summaryrefslogtreecommitdiff
path: root/game/shared/econ/econ_item.cpp
diff options
context:
space:
mode:
authorFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
committerFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
commit3bf9df6b2785fa6d951086978a3e66f49427166a (patch)
tree2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /game/shared/econ/econ_item.cpp
downloadarchived-source-engine-2018-hl2-src-master.tar.xz
archived-source-engine-2018-hl2-src-master.zip
Diffstat (limited to 'game/shared/econ/econ_item.cpp')
-rw-r--r--game/shared/econ/econ_item.cpp2765
1 files changed, 2765 insertions, 0 deletions
diff --git a/game/shared/econ/econ_item.cpp b/game/shared/econ/econ_item.cpp
new file mode 100644
index 0000000..a5ec004
--- /dev/null
+++ b/game/shared/econ/econ_item.cpp
@@ -0,0 +1,2765 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: CEconItem, a shared object for econ items
+//
+//=============================================================================
+
+#include "cbase.h"
+#include "econ_item.h"
+#include "econ_item_schema.h"
+#include "rtime.h"
+#include "gcsdk/enumutils.h"
+#include "smartptr.h"
+
+#ifdef GC_DLL
+#include "gcsdk/sqlaccess/sqlaccess.h"
+#include "econ/localization_provider.h"
+#endif
+
+#if defined( TF_CLIENT_DLL ) || defined( TF_DLL )
+#include "tf_gcmessages.h"
+#endif
+
+using namespace GCSDK;
+
+#ifdef GC_DLL
+IMPLEMENT_CLASS_MEMPOOL( CEconItem, 100 * 1000, UTLMEMORYPOOL_GROW_SLOW );
+IMPLEMENT_CLASS_MEMPOOL( CEconItemCustomData, 50 * 1000, UTLMEMORYPOOL_GROW_SLOW );
+#endif
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+extern int EconWear_ToIntCategory( float flWear );
+/*static*/ const schema_attribute_stat_bucket_t *CSchemaAttributeStats::m_pHead;
+
+//-----------------------------------------------------------------------------
+// Purpose: Utility function to convert datafile strings to ints.
+//-----------------------------------------------------------------------------
+int StringFieldToInt( const char *szValue, const char **pValueStrings, int iNumStrings, bool bDontAssert )
+{
+ if ( !szValue || !szValue[0] )
+ return -1;
+
+ for ( int i = 0; i < iNumStrings; i++ )
+ {
+ if ( !Q_stricmp(szValue, pValueStrings[i]) )
+ return i;
+ }
+
+ if ( !bDontAssert )
+ {
+ Assert( !"Missing value in StringFieldToInt()!" );
+ }
+ return -1;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Utility function to convert datafile strings to ints.
+//-----------------------------------------------------------------------------
+int StringFieldToInt( const char *szValue, const CUtlVector<const char *>& vecValueStrings, bool bDontAssert )
+{
+ return StringFieldToInt( szValue, (const char **)&vecValueStrings[0], vecValueStrings.Count(), bDontAssert );
+}
+
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+CEconItem::CEconItem()
+ : BaseClass( )
+ , m_pCustomData( NULL )
+ , m_ulID( INVALID_ITEM_ID )
+ , m_unStyle( 0 )
+ , m_pszSmallIcon( NULL )
+ , m_pszLargeIcon( NULL )
+{
+ Init();
+}
+
+CEconItem::CEconItem( const CEconItem& rhs )
+ : BaseClass( )
+ , m_pCustomData( NULL )
+ , m_ulID( INVALID_ITEM_ID )
+ , m_unStyle( 0 )
+ , m_pszSmallIcon( NULL )
+ , m_pszLargeIcon( NULL )
+{
+ Init();
+ (*this) = rhs;
+}
+
+void CEconItem::Init()
+{
+ memset( &m_dirtyBits, 0, sizeof( m_dirtyBits ) );
+
+#ifdef GC
+ // to set defaults
+ CSchItem item;
+ item.m_ulID = INVALID_ITEM_ID;
+ DeserializeFromSchemaItem( item );
+
+ COMPILE_TIME_ASSERT( sizeof( m_ulID ) == sizeof( item.m_ulID ) );
+ COMPILE_TIME_ASSERT( sizeof( m_unAccountID ) == sizeof( item.m_unAccountID ) );
+ COMPILE_TIME_ASSERT( sizeof( m_unDefIndex ) == sizeof( item.m_unDefIndex ) );
+ COMPILE_TIME_ASSERT( sizeof( m_unLevel ) == sizeof( item.m_unLevel ) );
+ COMPILE_TIME_ASSERT( sizeof( m_nQuality ) == sizeof( item.m_nQuality ) );
+ COMPILE_TIME_ASSERT( sizeof( m_unInventory ) == sizeof( item.m_unInventory ) );
+ COMPILE_TIME_ASSERT( sizeof( m_unFlags ) == sizeof( item.m_unFlags ) );
+ COMPILE_TIME_ASSERT( sizeof( m_unOrigin ) == sizeof( item.m_unOrigin ) );
+ COMPILE_TIME_ASSERT( sizeof( m_unStyle ) == sizeof( item.m_unStyle ) );
+ // @note (Tom Bui): we need to know about any new fields
+ // Need to add new fields to:
+ // CEconItem::operator=
+ // CEconItem::SerializeToSchemaItem
+ // CEconItem::DeserializeFromSchemaItem
+ // CEconItem::SerializeToProtoBufItem
+ // CEconItem::DeserializeFromProtoBufItem
+ // CEconManager::BYieldingLoadSOCache
+ // CEconGetPlayerItemsJob::BYieldingHandleGetPlayerItemsV001
+ COMPILE_TIME_ASSERT( CSchItem::k_iFieldMax == 13 );
+
+ m_bEquippedThisGameServerSession = false;
+#endif
+}
+
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+CEconItem::~CEconItem()
+{
+ // Free up any memory we may have allocated for our singleton attribute. Any other attributes
+ // will be cleaned up as part of freeing the custom data object itself.
+ if ( m_dirtyBits.m_bHasAttribSingleton )
+ {
+ CEconItemCustomData::FreeAttributeMemory( &m_CustomAttribSingleton );
+ }
+
+ // Free up any custom data we may have allocated. This will catch any attributes not
+ // in our singleton.
+ if ( m_pCustomData )
+ {
+ delete m_pCustomData;
+ }
+}
+
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+CEconItemCustomData::~CEconItemCustomData()
+{
+ FOR_EACH_VEC( m_vecAttributes, i )
+ {
+ FreeAttributeMemory( &m_vecAttributes[i] );
+ }
+
+ if ( m_pInteriorItem )
+ {
+ delete m_pInteriorItem;
+ }
+}
+
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+void CEconItem::CopyAttributesFrom( const CEconItem& source )
+{
+ // Copy attributes -- each new instance needs to be allocated and then copied into by somewhere
+ // that knows what the actual type is. Rather than do anything type-specific here, we just have each
+ // attribute serialize it's value to a bytestream and then deserialize it. This is as safe as we can
+ // make it but sort of silly wasteful.
+ for ( int i = 0; i < source.GetDynamicAttributeCountInternal(); i++ )
+ {
+ const attribute_t& attr = source.GetDynamicAttributeInternal( i );
+
+ const CEconItemAttributeDefinition *pAttrDef = GetItemSchema()->GetAttributeDefinition( attr.m_unDefinitionIndex );
+ Assert( pAttrDef );
+
+ const ISchemaAttributeType *pAttrType = pAttrDef->GetAttributeType();
+ Assert( pAttrType );
+
+ std::string sBytes;
+ pAttrType->ConvertEconAttributeValueToByteStream( attr.m_value, &sBytes );
+ pAttrType->LoadByteStreamToEconAttributeValue( this, pAttrDef, sBytes );
+ }
+}
+
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+CEconItem &CEconItem::operator=( const CEconItem& rhs )
+{
+ // We do destructive operations on our local object, including freeing attribute memory, as part of
+ // the copy, so we force self-copies to be a no-op.
+ if ( &rhs == this )
+ return *this;
+
+ m_ulID = rhs.m_ulID;
+ SetOriginalID( rhs.GetOriginalID() );
+ m_unAccountID = rhs.m_unAccountID;
+ m_unDefIndex = rhs.m_unDefIndex;
+ m_unLevel = rhs.m_unLevel;
+ m_nQuality = rhs.m_nQuality;
+ m_unInventory = rhs.m_unInventory;
+ SetQuantity( rhs.GetQuantity() );
+ m_unFlags = rhs.m_unFlags;
+ m_unOrigin = rhs.m_unOrigin;
+ m_unStyle = rhs.m_unStyle;
+ m_EquipInstanceSingleton = rhs.m_EquipInstanceSingleton;
+
+ // If we have memory allocated for a single attribute we free it manually.
+ if ( m_dirtyBits.m_bHasAttribSingleton )
+ {
+ CEconItemCustomData::FreeAttributeMemory( &m_CustomAttribSingleton );
+ }
+
+ // Copy over our dirty bits but manually reset our attribute singleton state -- if we did have one,
+ // we just deleted it above (and might replace it below); if we didn't have one, this won't affect
+ // anything. Either way, because we have no attribute memory allocated at this point, we need this
+ // to be reflected in the dirty bits so that if we do copy attributes, we copy them into the correct
+ // place (either the singleton or the custom data, to be allocated later).
+ m_dirtyBits = rhs.m_dirtyBits;
+ m_dirtyBits.m_bHasAttribSingleton = false;
+
+ // Free any custom memory we've allocated. This will also remove any custom attributes.
+ if ( rhs.m_pCustomData == NULL )
+ {
+ delete m_pCustomData;
+ m_pCustomData = NULL;
+ }
+ else
+ {
+ // Check for and copy in the equip instances from CustomData
+ EnsureCustomDataExists();
+ m_pCustomData->m_vecEquipped = rhs.m_pCustomData->m_vecEquipped;
+ }
+
+ CopyAttributesFrom( rhs );
+
+ // Reset our material overrides, they'll be set again on demand as needed.
+ ResetMaterialOverrides();
+
+ return *this;
+}
+
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+void CEconItem::SetItemID( itemid_t ulID )
+{
+ uint64 ulOldID = m_ulID;
+ m_ulID = ulID;
+ // only overwrite if we don't have an original id currently and we are a new item cloned off an old item
+ 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 )
+ {
+ SetOriginalID( ulOldID );
+ }
+
+ ResetMaterialOverrides();
+}
+
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+itemid_t CEconItem::GetOriginalID() const
+{
+ if ( m_pCustomData != NULL && m_pCustomData->m_ulOriginalID != INVALID_ITEM_ID )
+ return m_pCustomData->m_ulOriginalID;
+ return m_ulID;
+}
+
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+void CEconItem::SetOriginalID( itemid_t ulOriginalID )
+{
+ if ( ulOriginalID != m_ulID )
+ {
+ EnsureCustomDataExists();
+ m_pCustomData->m_ulOriginalID = ulOriginalID;
+ }
+}
+
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+int CEconItem::GetQuantity() const
+{
+ if ( m_pCustomData != NULL )
+ return m_pCustomData->m_unQuantity;
+ return 1;
+}
+
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+void CEconItem::SetQuantity( uint16 unQuantity )
+{
+ if ( m_pCustomData )
+ {
+ m_pCustomData->m_unQuantity = unQuantity;
+ }
+ else if ( unQuantity > 1 )
+ {
+ EnsureCustomDataExists();
+ m_pCustomData->m_unQuantity = unQuantity;
+ }
+}
+
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+static const char *GetCustomNameOrAttributeDesc( const CEconItem *pItem, const CEconItemAttributeDefinition *pAttrDef )
+{
+ if ( !pAttrDef )
+ {
+ // If we didn't specify the attribute in the schema we can't possibly have an
+ // answer. This isn't really an error in that case.
+ return NULL;
+ }
+
+ const char *pszStrContents;
+ if ( FindAttribute_UnsafeBitwiseCast<CAttribute_String>( pItem, pAttrDef, &pszStrContents ) )
+ return pszStrContents;
+
+ return NULL;
+}
+
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+static void SetCustomNameOrDescAttribute( CEconItem *pItem, const CEconItemAttributeDefinition *pAttrDef, const char *pszNewValue )
+{
+ Assert( pItem );
+
+ if ( !pAttrDef )
+ {
+ // If we didn't specify the attribute in the schema, that's fine if we're setting
+ // the empty name/description string, but it isn't fine if we're trying to set
+ // actual content.
+ AssertMsg( !pszNewValue, "Attempt to set non-empty value for custom name/desc with no attribute present." );
+ return;
+ }
+
+ // Removing existing value?
+ if ( !pszNewValue || !pszNewValue[0] )
+ {
+ pItem->RemoveDynamicAttribute( pAttrDef );
+ return;
+ }
+
+ CAttribute_String attrStr;
+ attrStr.set_value( pszNewValue );
+
+ pItem->SetDynamicAttributeValue( pAttrDef, attrStr );
+}
+
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+const char *CEconItem::GetCustomName() const
+{
+ static CSchemaAttributeDefHandle pAttrDef_CustomName( "custom name attr" );
+
+ return GetCustomNameOrAttributeDesc( this, pAttrDef_CustomName );
+}
+
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+void CEconItem::SetCustomName( const char *pName )
+{
+ static CSchemaAttributeDefHandle pAttrDef_CustomName( "custom name attr" );
+
+ SetCustomNameOrDescAttribute( this, pAttrDef_CustomName, pName );
+}
+
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+bool CEconItem::IsEquipped() const
+{
+ for ( int i = 0; i < GetEquippedInstanceCount(); i++ )
+ {
+ const EquippedInstance_t &curEquipInstance = GetEquippedInstance( i );
+ Assert( curEquipInstance.m_unEquippedSlot != INVALID_EQUIPPED_SLOT );
+
+ if ( GetItemSchema()->IsValidClass( curEquipInstance.m_unEquippedClass ) )
+ return true;
+ }
+
+ return false;
+}
+
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+bool CEconItem::IsEquippedForClass( equipped_class_t unClass ) const
+{
+ return NULL != FindEquippedInstanceForClass( unClass );
+}
+
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+equipped_slot_t CEconItem::GetEquippedPositionForClass( equipped_class_t unClass ) const
+{
+ const EquippedInstance_t *pInstance = FindEquippedInstanceForClass( unClass );
+ if ( pInstance )
+ return pInstance->m_unEquippedSlot;
+
+ return INVALID_EQUIPPED_SLOT;
+}
+
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+const CEconItem::EquippedInstance_t *CEconItem::FindEquippedInstanceForClass( equipped_class_t nClass ) const
+{
+ for ( int i = 0; i < GetEquippedInstanceCount(); i++ )
+ {
+ const EquippedInstance_t &curEquipInstance = GetEquippedInstance( i );
+ if ( curEquipInstance.m_unEquippedClass == nClass )
+ return &curEquipInstance;
+ }
+
+ return NULL;
+}
+
+
+
+//----------------------------------------------------------------------------
+// Purpose:
+//----------------------------------------------------------------------------
+void CEconItem::InternalVerifyEquipInstanceIntegrity() const
+{
+ if ( m_dirtyBits.m_bHasEquipSingleton )
+ {
+ Assert( !m_pCustomData );
+ Assert( m_EquipInstanceSingleton.m_unEquippedSlot != INVALID_EQUIPPED_SLOT );
+ }
+ else if ( m_pCustomData )
+ {
+ FOR_EACH_VEC( m_pCustomData->m_vecEquipped, i )
+ {
+ Assert( m_pCustomData->m_vecEquipped[i].m_unEquippedSlot != INVALID_EQUIPPED_SLOT );
+
+ for ( int j = i + 1; j < m_pCustomData->m_vecEquipped.Count(); j++ )
+ {
+ Assert( m_pCustomData->m_vecEquipped[i].m_unEquippedClass != m_pCustomData->m_vecEquipped[j].m_unEquippedClass );
+ }
+ }
+ }
+ else
+ {
+ Assert( GetEquippedInstanceCount() == 0 );
+ }
+}
+
+//----------------------------------------------------------------------------
+// Purpose:
+//----------------------------------------------------------------------------
+void CEconItem::Equip( equipped_class_t unClass, equipped_slot_t unSlot )
+{
+ Assert( GetItemSchema()->IsValidClass( unClass ) );
+ Assert( GetItemSchema()->IsValidItemSlot( unSlot, unClass ) );
+
+ // First, make sure we don't have this item already equipped for this class.
+ UnequipFromClass( unClass );
+
+ // If we have no instances of this item equipped, we want to shove this into the
+ // first empty slot we can find. If we already have a custom data allocated, we
+ // use that. If not, we want to use the singleton if we can. Otherwise, we make
+ // a new custom data and fall back to using that.
+ if ( m_pCustomData )
+ {
+ m_pCustomData->m_vecEquipped.AddToTail( EquippedInstance_t( unClass, unSlot ) );
+ }
+ else if ( !m_dirtyBits.m_bHasEquipSingleton )
+ {
+ m_EquipInstanceSingleton = EquippedInstance_t( unClass, unSlot );
+ m_dirtyBits.m_bHasEquipSingleton = true;
+ }
+ else
+ {
+ EnsureCustomDataExists();
+ m_pCustomData->m_vecEquipped.AddToTail( EquippedInstance_t( unClass, unSlot ) );
+ }
+
+ InternalVerifyEquipInstanceIntegrity();
+
+#ifdef GC_DLL
+ m_bEquippedThisGameServerSession = true;
+#endif // GC_DLL
+
+}
+
+//----------------------------------------------------------------------------
+// Purpose:
+//----------------------------------------------------------------------------
+void CEconItem::Unequip()
+{
+ if ( m_dirtyBits.m_bHasEquipSingleton )
+ {
+ Assert( !m_pCustomData );
+ m_dirtyBits.m_bHasEquipSingleton = false;
+ }
+ else if ( m_pCustomData )
+ {
+ m_pCustomData->m_vecEquipped.Purge();
+ }
+
+ InternalVerifyEquipInstanceIntegrity();
+}
+
+//----------------------------------------------------------------------------
+// Purpose:
+//----------------------------------------------------------------------------
+void CEconItem::UnequipFromClass( equipped_class_t unClass )
+{
+ Assert( GetItemSchema()->IsValidClass( unClass ) );
+
+ // If we only have a single equipped class...
+ if ( m_dirtyBits.m_bHasEquipSingleton )
+ {
+ // ...and that's the class we're trying to remove from...
+ if ( m_EquipInstanceSingleton.m_unEquippedClass == unClass )
+ {
+ // ...we now have no equipped classes!
+ m_dirtyBits.m_bHasEquipSingleton = false;
+ }
+ }
+ else if ( m_pCustomData )
+ {
+ // ...otherwise, if we have multiple equipped classes...
+ FOR_EACH_VEC( m_pCustomData->m_vecEquipped, i )
+ {
+ // ...then look through our list to find out if we have this class...
+ if ( m_pCustomData->m_vecEquipped[i].m_unEquippedClass == unClass )
+ {
+ // ...and if we do, remove it.
+ m_pCustomData->m_vecEquipped.FastRemove( i );
+ break;
+ }
+ }
+ }
+
+ InternalVerifyEquipInstanceIntegrity();
+}
+
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+int CEconItem::GetEquippedInstanceCount() const
+{
+ if ( m_pCustomData )
+ return m_pCustomData->m_vecEquipped.Count();
+ else
+ return m_dirtyBits.m_bHasEquipSingleton ? 1 : 0;
+}
+
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+const CEconItem::EquippedInstance_t &CEconItem::GetEquippedInstance( int iIdx ) const
+{
+ Assert( iIdx >= 0 && iIdx < GetEquippedInstanceCount() );
+
+ if ( m_pCustomData )
+ return m_pCustomData->m_vecEquipped[iIdx];
+ else
+ return m_EquipInstanceSingleton;
+}
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+const char *CEconItem::GetCustomDesc() const
+{
+ static CSchemaAttributeDefHandle pAttrDef_CustomDesc( "custom desc attr" );
+
+ return GetCustomNameOrAttributeDesc( this, pAttrDef_CustomDesc );
+}
+
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+void CEconItem::SetCustomDesc( const char *pDesc )
+{
+ static CSchemaAttributeDefHandle pAttrDef_CustomDesc( "custom desc attr" );
+
+ SetCustomNameOrDescAttribute( this, pAttrDef_CustomDesc, pDesc );
+}
+
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+bool CEconItem::GetInUse() const
+{
+ return ( m_dirtyBits.m_bInUse ) != 0;
+}
+
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+void CEconItem::SetInUse( bool bInUse )
+{
+ if ( bInUse )
+ {
+ m_dirtyBits.m_bInUse = 1;
+ }
+ else
+ {
+ m_dirtyBits.m_bInUse = 0;
+ }
+}
+
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+const GameItemDefinition_t *CEconItem::GetItemDefinition() const
+{
+ const CEconItemDefinition *pRet = GetItemSchema()->GetItemDefinition( GetDefinitionIndex() );
+ const GameItemDefinition_t *pTypedRet = dynamic_cast<const GameItemDefinition_t *>( pRet );
+
+ AssertMsg( pRet == pTypedRet, "Item definition of inappropriate type." );
+
+ return pTypedRet;
+}
+
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+bool CEconItem::IsTradable() const
+{
+ return !m_dirtyBits.m_bInUse
+ && IEconItemInterface::IsTradable();
+}
+
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+void CEconItem::AdoptMoreRestrictedTradabilityFromItem( const CEconItem *pOther, uint32 nTradabilityFlagsToAccept /*= 0xFFFFFFFF*/ )
+{
+ if ( !pOther )
+ return;
+
+ int nOtherUntradability = pOther->GetUntradabilityFlags() & nTradabilityFlagsToAccept;
+ RTime32 otherUntradableTime = pOther->GetTradableAfterDateTime();
+ // Become untradable if the other item is untradable
+ AdoptMoreRestrictedTradability( nOtherUntradability, otherUntradableTime );
+}
+
+// --------------------------------------------------------------------------
+// Purpose: Given untradability flags and a untradable time, set this item's
+// untradability. This does not clear existing untradabilty.
+// --------------------------------------------------------------------------
+void CEconItem::AdoptMoreRestrictedTradability( uint32 nTradabilityFlags, RTime32 nUntradableTime )
+{
+ static CSchemaAttributeDefHandle pAttrib_CannotTrade( "cannot trade" );
+ static CSchemaAttributeDefHandle pAttrib_TradableAfter( "tradable after date" );
+
+ if ( !pAttrib_CannotTrade || !pAttrib_TradableAfter )
+ return;
+
+ // We're already permanently untradable. We can't get more untradable, so we're done.
+ if ( GetUntradabilityFlags() & k_Untradability_Permanent )
+ return;
+
+ if( nTradabilityFlags & k_Untradability_Permanent )
+ {
+ SetDynamicAttributeValue( pAttrib_CannotTrade, 0u );
+ }
+ else if ( nTradabilityFlags & k_Untradability_Temporary && nUntradableTime > GetTradableAfterDateTime() )
+ {
+ // Take the "tradable after date" if it's larger than ours
+ SetDynamicAttributeValue( pAttrib_TradableAfter, nUntradableTime );
+ }
+}
+
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+bool CEconItem::IsMarketable() const
+{
+ return !m_dirtyBits.m_bInUse
+ && IEconItemInterface::IsMarketable();
+}
+
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+bool CEconItem::IsCommodity() const
+{
+ return !m_dirtyBits.m_bInUse
+ && IEconItemInterface::IsCommodity();
+}
+
+void CEconItem::IterateAttributes( IEconItemAttributeIterator *pIterator ) const
+{
+ Assert( pIterator );
+
+ // custom attributes?
+ for ( int i = 0; i < GetDynamicAttributeCountInternal(); i++ )
+ {
+ const attribute_t &attrib = GetDynamicAttributeInternal( i );
+ const CEconItemAttributeDefinition *pAttrDef = GetItemSchema()->GetAttributeDefinition( attrib.m_unDefinitionIndex );
+ if ( !pAttrDef )
+ continue;
+
+ if ( !pAttrDef->GetAttributeType()->OnIterateAttributeValue( pIterator, pAttrDef, attrib.m_value ) )
+ return;
+ }
+
+ // in static attributes?
+ const CEconItemDefinition *pItemDef = GetItemDefinition();
+ if ( !pItemDef )
+ return;
+
+ pItemDef->IterateAttributes( pIterator );
+}
+
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+style_index_t CEconItem::GetStyle() const
+{
+ static CSchemaAttributeDefHandle pAttrDef_ItemStyleOverride( "item style override" );
+ float fStyleOverride = 0.f;
+ if ( FindAttribute_UnsafeBitwiseCast<attrib_value_t>( this, pAttrDef_ItemStyleOverride, &fStyleOverride ) )
+ {
+ return fStyleOverride;
+ }
+
+ static CSchemaAttributeDefHandle pAttrDef_ItemStyleStrange( "style changes on strange level" );
+ uint32 iMaxStyle = 0;
+ if ( pAttrDef_ItemStyleStrange && FindAttribute( pAttrDef_ItemStyleStrange, &iMaxStyle ) )
+ {
+ // Use the strange prefix if the weapon has one.
+ uint32 unScore = 0;
+ if ( !FindAttribute( GetKillEaterAttr_Score( 0 ), &unScore ) )
+ return 0;
+
+ // What type of event are we tracking and how does it describe itself?
+ uint32 unKillEaterEventType = 0;
+ // This will overwrite our default 0 value if we have a value set but leave it if not.
+ float fKillEaterEventType;
+ if ( FindAttribute_UnsafeBitwiseCast<attrib_value_t>( this, GetKillEaterAttr_Type( 0 ), &fKillEaterEventType ) )
+ {
+ unKillEaterEventType = fKillEaterEventType;
+ }
+
+ const char *pszLevelingDataName = GetItemSchema()->GetKillEaterScoreTypeLevelingDataName( unKillEaterEventType );
+ if ( !pszLevelingDataName )
+ {
+ pszLevelingDataName = KILL_EATER_RANK_LEVEL_BLOCK_NAME;
+ }
+
+ const CItemLevelingDefinition *pLevelDef = GetItemSchema()->GetItemLevelForScore( pszLevelingDataName, unScore );
+ if ( !pLevelDef )
+ return 0;
+
+ return Min( pLevelDef->GetLevel(), iMaxStyle );
+ }
+
+ return m_unStyle;
+}
+
+const char* CEconItem::FindIconURL( bool bLarge ) const
+{
+ const char* pszSize = bLarge ? "l" : "s";
+
+ static CSchemaAttributeDefHandle pAttrDef_IsFestivized( "is_festivized" );
+ bool bIsFestivized = pAttrDef_IsFestivized ? FindAttribute( pAttrDef_IsFestivized ) : false;
+
+ const CEconItemDefinition *pDef = GetItemDefinition();
+
+ // Go through and figure out all the different decorations on
+ // this item and construct the key to lookup the icon.
+ // NOTE: These are not currently composable, so they return out when
+ // a match is found. Once items are more composable, we'll want
+ // to keep adding all the components together to get the fully
+ // composed icon (ie. add the strange token, and the festive token, etc.)
+ const CEconItemPaintKitDefinition* pPaintKitDef = pDef->GetCustomPainkKitDefinition();
+ if ( pPaintKitDef )
+ {
+ float flWear = 0;
+ GetCustomPaintKitWear( flWear );
+ int iWearIndex = EconWear_ToIntCategory( flWear );
+ const char* pszFmtStr = bIsFestivized ? "%s%sw%df" : "%s%sw%d";
+
+ const char* pszValue = pDef->GetIconURL( CFmtStr( pszFmtStr, pszSize, pPaintKitDef->GetName(), iWearIndex ) );
+ if ( pszValue )
+ return pszValue;
+ }
+
+ const CEconStyleInfo *pStyle = pDef->GetStyleInfo( GetStyle() );
+ if ( pStyle )
+ {
+ const char* pszValue = pDef->GetIconURL( CFmtStr( "%ss%d", pszSize, GetStyle() ) );
+ if ( pszValue )
+ return pszValue;
+ }
+
+ if ( bIsFestivized )
+ {
+ const char* pszValue = pDef->GetIconURL( CFmtStr( "%sf", pszSize ) );
+ if ( pszValue )
+ return pszValue;
+ }
+
+ return pDef->GetIconURL( CFmtStr( "%s", pszSize ) );
+}
+
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+const char *CEconItem::GetIconURLSmall() const
+{
+ if ( m_pszSmallIcon == NULL )
+ {
+ m_pszSmallIcon = FindIconURL( false );
+ }
+
+ return m_pszSmallIcon;
+}
+
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+const char *CEconItem::GetIconURLLarge() const
+{
+ if ( m_pszLargeIcon == NULL )
+ {
+ m_pszLargeIcon = FindIconURL( true );
+ }
+
+ return m_pszLargeIcon;
+}
+
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+bool CEconItem::IsUsableInCrafting() const
+{
+ return !m_dirtyBits.m_bInUse
+ && IEconItemInterface::IsUsableInCrafting();
+}
+
+#ifdef GC_DLL
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+RTime32 CEconItem::GetAssetInfoExpirationCacheExpirationTime() const
+{
+ return GetTradableAfterDateTime();
+}
+#endif // GC_DLL
+
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+int CEconItem::GetDynamicAttributeCountInternal() const
+{
+ if ( m_pCustomData )
+ return m_pCustomData->m_vecAttributes.Count();
+ else
+ return m_dirtyBits.m_bHasAttribSingleton ? 1 : 0;
+}
+
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+CEconItem::attribute_t &CEconItem::GetMutableDynamicAttributeInternal( int iAttrIndexIntoArray )
+{
+ Assert( iAttrIndexIntoArray >= 0 );
+ Assert( iAttrIndexIntoArray < GetDynamicAttributeCountInternal() );
+
+ if ( m_pCustomData )
+ return m_pCustomData->m_vecAttributes[ iAttrIndexIntoArray ];
+ else
+ return m_CustomAttribSingleton;
+}
+
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+CEconItem::attribute_t *CEconItem::FindDynamicAttributeInternal( const CEconItemAttributeDefinition *pAttrDef )
+{
+ Assert( pAttrDef );
+
+ if ( m_pCustomData )
+ {
+ FOR_EACH_VEC( m_pCustomData->m_vecAttributes, i )
+ {
+ if ( m_pCustomData->m_vecAttributes[i].m_unDefinitionIndex == pAttrDef->GetDefinitionIndex() )
+ return &m_pCustomData->m_vecAttributes[i];
+ }
+ }
+ else if ( m_dirtyBits.m_bHasAttribSingleton )
+ {
+ if ( m_CustomAttribSingleton.m_unDefinitionIndex == pAttrDef->GetDefinitionIndex() )
+ return &m_CustomAttribSingleton;
+ }
+
+ return NULL;
+}
+
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+CEconItem::attribute_t &CEconItem::AddDynamicAttributeInternal()
+{
+ if ( 0 == GetDynamicAttributeCountInternal() && NULL == m_pCustomData )
+ {
+ m_dirtyBits.m_bHasAttribSingleton = true;
+ return m_CustomAttribSingleton;
+ }
+ else
+ {
+ EnsureCustomDataExists();
+ return m_pCustomData->m_vecAttributes[ m_pCustomData->m_vecAttributes.AddToTail() ];
+ }
+}
+
+// --------------------------------------------------------------------------
+void CEconItem::SetDynamicMaxTimeAttributeValue( const CEconItemAttributeDefinition *pAttrDef, RTime32 rtTime )
+{
+ RTime32 rtExistingTime = 0;
+ if ( FindAttribute( pAttrDef, &rtExistingTime ) )
+ {
+ //we have the attribute already, and see if the value exceeds what we are going to set
+ if ( rtExistingTime >= rtTime )
+ return;
+ }
+
+ //it doesn't so we need to update
+ SetDynamicAttributeValue( pAttrDef, rtTime );
+}
+
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+void CEconItem::SetTradableAfterDateTime( RTime32 rtTime )
+{
+ //don't bother if the time is in the past (this also covers the 0 case)
+ if( rtTime < CRTime::RTime32TimeCur() )
+ return;
+
+ //the attribute we are going to assign
+ static CSchemaAttributeDefHandle pAttrib_TradableAfter( "tradable after date" );
+ if( !pAttrib_TradableAfter )
+ return;
+
+ //see if we have a STATIC cannot trade attribute (ignore dynamic, because that could change and be used
+ // to short out the trade restriction).
+
+ //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
+ /*
+ const GameItemDefinition_t* pItemDef = GetItemDefinition();
+ if( pItemDef )
+ {
+ static CSchemaAttributeDefHandle pAttrib_CannotTrade( "cannot trade" );
+ uint32 unCannotTrade = 0;
+ if( ::FindAttribute( pItemDef, pAttrib_CannotTrade, &unCannotTrade ) )
+ {
+ return;
+ }
+ }
+ */
+
+ //now set it to the maximum time
+ SetDynamicMaxTimeAttributeValue( pAttrib_TradableAfter, rtTime );
+}
+
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+void CEconItem::RemoveDynamicAttribute( const CEconItemAttributeDefinition *pAttrDef )
+{
+ Assert( pAttrDef );
+ Assert( pAttrDef->GetDefinitionIndex() != INVALID_ATTRIB_DEF_INDEX );
+
+ if ( m_pCustomData )
+ {
+ for ( int i = 0; i < m_pCustomData->m_vecAttributes.Count(); i++ )
+ {
+ if ( m_pCustomData->m_vecAttributes[i].m_unDefinitionIndex == pAttrDef->GetDefinitionIndex() )
+ {
+ CEconItemCustomData::FreeAttributeMemory( &m_pCustomData->m_vecAttributes[i] );
+ m_pCustomData->m_vecAttributes.FastRemove( i );
+ return;
+ }
+ }
+ }
+ else if ( m_dirtyBits.m_bHasAttribSingleton )
+ {
+ if ( m_CustomAttribSingleton.m_unDefinitionIndex == pAttrDef->GetDefinitionIndex() )
+ {
+ CEconItemCustomData::FreeAttributeMemory( &m_CustomAttribSingleton );
+ m_dirtyBits.m_bHasAttribSingleton = false;
+ }
+ }
+}
+
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+/*static*/ void CEconItemCustomData::FreeAttributeMemory( CEconItem::attribute_t *pAttrib )
+{
+ Assert( pAttrib );
+
+ const CEconItemAttributeDefinition *pAttrDef = GetItemSchema()->GetAttributeDefinition( pAttrib->m_unDefinitionIndex );
+ Assert( pAttrDef );
+
+ const ISchemaAttributeType *pAttrType = pAttrDef->GetAttributeType();
+ Assert( pAttrType );
+
+ pAttrType->UnloadEconAttributeValue( &pAttrib->m_value );
+}
+
+// --------------------------------------------------------------------------
+// Purpose: Frees any unused memory in the internal structures
+// --------------------------------------------------------------------------
+void CEconItem::Compact()
+{
+ if ( m_pCustomData )
+ {
+ m_pCustomData->m_vecAttributes.Compact();
+ m_pCustomData->m_vecEquipped.Compact();
+ }
+}
+
+#ifdef GC_DLL
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+static bool BInsertEquippedInstanceSQL( const CEconItem *pItem, const CEconItem::EquippedInstance_t& equipInst, CSQLAccess& sqlAccess )
+{
+ Assert( pItem );
+
+ const char *pszUpdateOrInsert = "MERGE EquipInstance AS tblEquipInstance "
+ "USING (SELECT ? AS AccountID, ? AS ClassID, ? AS SlotID) AS tblEquipInstance_NewRow "
+ "ON (tblEquipInstance.AccountID = tblEquipInstance_NewRow.AccountID AND "
+ "tblEquipInstance.ClassID = tblEquipInstance_NewRow.ClassID AND "
+ "tblEquipInstance.SlotID = tblEquipInstance_NewRow.SlotID) "
+ "WHEN MATCHED THEN "
+ "UPDATE SET tblEquipInstance.ItemID = ? "
+ "WHEN NOT MATCHED BY TARGET THEN "
+ "INSERT (AccountID, ClassID, SlotID, ItemID) VALUES (?, ?, ?, ?);";
+
+ sqlAccess.AddBindParam( pItem->GetAccountID() ); // USING (SELECT ... AS AccountID)
+ sqlAccess.AddBindParam( equipInst.m_unEquippedClass ); // USING (SELECT ... AS ClassID)
+ sqlAccess.AddBindParam( equipInst.m_unEquippedSlot ); // USING (SELECT ... AS SlotID)
+ sqlAccess.AddBindParam( pItem->GetItemID() ); // UPDATE SET ...
+
+ sqlAccess.AddBindParam( pItem->GetAccountID() ); // INSERT (AccountID)
+ sqlAccess.AddBindParam( equipInst.m_unEquippedClass ); // INSERT (ClassID)
+ sqlAccess.AddBindParam( equipInst.m_unEquippedSlot ); // INSERT (SlotID)
+ sqlAccess.AddBindParam( pItem->GetItemID() ); // INSERT (ItemID)
+
+ return sqlAccess.BYieldingExecute( "BYieldingOnHandledPeriodicScoreTimePeriod", pszUpdateOrInsert );
+}
+
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+static bool BInsertAllEquippedInstancesSQL( const CEconItem *pItem, CSQLAccess& sqlAccess )
+{
+ Assert( pItem );
+
+ for ( int i = 0; i < pItem->GetEquippedInstanceCount(); i++ )
+ {
+ if ( !BInsertEquippedInstanceSQL( pItem, pItem->GetEquippedInstance( i ), sqlAccess ) )
+ return false;
+ }
+
+ return true;
+}
+
+// --------------------------------------------------------------------------
+// Purpose: Adds non-Item-table inserts to the SQL insert for this object
+// --------------------------------------------------------------------------
+bool CEconItem::BYieldingAddInsertToTransaction( CSQLAccess & sqlAccess )
+{
+ // @note Tom Bui: This could be simplified greatly, but let's see if it is an issue
+ CSchItem item;
+ SerializeToSchemaItem( item );
+ if( !CSchemaSharedObjectHelper::BYieldingAddInsertToTransaction( sqlAccess, &item ) )
+ return false;
+
+ // attributes get to written to any number of joined tables based on type
+ for ( int i = 0; i < GetDynamicAttributeCountInternal(); i++ )
+ {
+ const attribute_t &attrib = GetDynamicAttributeInternal( i );
+
+ const CEconItemAttributeDefinition *pAttrDef = GetItemSchema()->GetAttributeDefinition( attrib.m_unDefinitionIndex );
+ if ( !pAttrDef )
+ {
+ EmitError( SPEW_GC, "CEconItem::BYieldingAddInsertToTransaction(): attempt to insert unknown attribute ID %d.\n", attrib.m_unDefinitionIndex );
+ return false;
+ }
+
+ const ISchemaAttributeType *pAttrType = pAttrDef->GetAttributeType();
+ Assert( pAttrType );
+
+ CPlainAutoPtr<CRecordBase> pAttrRecord( pAttrType->CreateTypedSchRecord() );
+ pAttrType->ConvertEconAttributeValueToSch( GetItemID(), pAttrDef, attrib.m_value, pAttrRecord.Get() );
+ if( !sqlAccess.BYieldingInsertRecord( pAttrRecord.Get() ) )
+ return false;
+ }
+
+ // currently-equipped positions get written to a joined table
+ if ( !BInsertAllEquippedInstancesSQL( this, sqlAccess ) )
+ return false;
+
+ // add audit record for the create
+ CEconItemDefinition *pItemDef = GEconManager()->GetItemSchema()->GetItemDefinition( GetDefinitionIndex() );
+ if ( pItemDef )
+ {
+ const char* pDatabaseAuditTableName = pItemDef->GetDatabaseAuditTableName();
+ if ( pDatabaseAuditTableName )
+ {
+ CFmtStr1024 sStatement;
+ sStatement.sprintf( "INSERT INTO %s (ItemID) VALUES (%llu)", pDatabaseAuditTableName, m_ulID );
+ uint32 nRows;
+ bool bRet = sqlAccess.BYieldingExecute( sStatement, sStatement, &nRows );
+ if ( bRet == false )
+ {
+ CSteamID steamID( m_unAccountID, GGCHost()->GetUniverse(), k_EAccountTypeIndividual );
+ EmitError( SPEW_GC, "Failed to add item audit to table %s for %s and item %llu.\n", pDatabaseAuditTableName, steamID.Render(), m_ulID );
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+//----------------------------------------------------------------------------
+// Purpose:
+//----------------------------------------------------------------------------
+static bool BRemoveEquipInstancesFromSQL( CSQLAccess& sqlAccess, uint32 unAccountID, itemid_t unItemID )
+{
+ CSchEquipInstance schEquipInstanceWhere;
+ schEquipInstanceWhere.m_unAccountID = unAccountID;
+ schEquipInstanceWhere.m_ulItemID = unItemID;
+
+ return sqlAccess.BYieldingDeleteRecords( schEquipInstanceWhere, CSET_2_COL( CSchEquipInstance, k_iField_unAccountID, k_iField_ulItemID ) );
+}
+
+//----------------------------------------------------------------------------
+// Purpose: Writes the non-PK fields on the object to the database
+//----------------------------------------------------------------------------
+bool CEconItem::BYieldingAddWriteToTransaction( CSQLAccess & sqlAccess, const CUtlVector< int > &fields )
+{
+ Assert( sqlAccess.BInTransaction() );
+
+ if( IsForeign() )
+ {
+ CSchForeignItem schForeignItem;
+ schForeignItem.m_ulID = GetItemID();
+ schForeignItem.m_unInventory = GetInventoryToken();
+ return CSchemaSharedObjectHelper::BYieldingAddWriteToTransaction( sqlAccess, &schForeignItem, CSET_1_COL( CSchForeignItem, k_iField_unInventory ) );
+ }
+
+ // Non-foreign items.
+ CSchItem item;
+ SerializeToSchemaItem( item );
+ CColumnSet csDatabaseDirty( item.GetPRecordInfo() );
+ GetDirtyColumnSet( fields, csDatabaseDirty );
+
+ // Write out base item properties if any are dirty. It's possible to get here with only dirty attributes,
+ // in which case we want to neither write to our item properties nor abort here if the write fails.
+ if ( !csDatabaseDirty.IsEmpty() && !CSchemaSharedObjectHelper::BYieldingAddWriteToTransaction( sqlAccess, &item, csDatabaseDirty ) )
+ return false;
+
+ // Write out additional attribute properties?
+ FOR_EACH_VEC( fields, i )
+ {
+ uint32 iFieldID = fields[i] & 0x0000ffff, // low 16 bits are the ID
+ iFieldIDType = fields[i] & 0xffff0000; // high 16 bits are the ID type
+
+ if ( iFieldIDType == kUpdateFieldIDType_FieldID )
+ {
+ // Most item field IDs will be updated above in SerializeToSchemaItem(). Anything that needs custom
+ // writing behavior should handle it here.
+
+ // If we've changed our equip state, remove all of our previous equipped instances (if any) and write
+ // out our new rows (if any).
+ if ( iFieldID == CSOEconItem::kEquippedStateFieldNumber )
+ {
+ if ( !BRemoveEquipInstancesFromSQL( sqlAccess, GetAccountID(), GetID() ) )
+ return false;
+
+ if ( !BInsertAllEquippedInstancesSQL( this, sqlAccess ) )
+ return false;
+ }
+ }
+ else if ( iFieldIDType == kUpdateFieldIDType_AttributeID )
+ {
+ const CEconItemAttributeDefinition *pAttrDef = GetItemSchema()->GetAttributeDefinition( iFieldID );
+ Assert( pAttrDef );
+
+ // Did we dirty an attribute and then remove it entirely?
+ const attribute_t *pEconAttr = FindDynamicAttributeInternal( pAttrDef );
+ if ( !pEconAttr )
+ continue;
+
+ const ISchemaAttributeType *pAttrType = pAttrDef->GetAttributeType();
+ Assert( pAttrType );
+
+ CPlainAutoPtr<CRecordBase> pAttrRecord( pAttrType->CreateTypedSchRecord() );
+ pAttrType->ConvertEconAttributeValueToSch( GetItemID(), pAttrDef, pEconAttr->m_value, pAttrRecord.Get() );
+
+ CColumnSet csWhere( pAttrRecord->GetPRecordInfo() );
+ csWhere.BAddColumn( 0 );
+ csWhere.BAddColumn( 1 );
+
+ CColumnSet csUpdate( CColumnSet::Inverse( csWhere ) );
+ Assert( !csUpdate.IsEmpty() );
+
+ if ( !sqlAccess.BYieldingUpdateRecord( *pAttrRecord.Get(), csWhere, csUpdate ) )
+ return false;
+ }
+ // Our field ID is something we don't recognize.
+ else
+ {
+ AssertMsg( false, "Unknown field ID type in CEconItem::BYieldingAddWriteToTransaction()." );
+ }
+ }
+
+ return true;
+}
+
+
+// --------------------------------------------------------------------------
+// Purpose: "deletes" an object by setting its owner to 0.
+// --------------------------------------------------------------------------
+bool CEconItem::BYieldingAddRemoveToTransaction( CSQLAccess & sqlAccess )
+{
+ Assert( sqlAccess.BInTransaction() );
+
+ // we never remove econ items in the server
+ sqlAccess.AddBindParam( GetItemID() );
+ if( !sqlAccess.BYieldingExecute( NULL, "UPDATE Item SET AccountId = 0 WHERE ID = ?" ) )
+ return false;
+
+ if ( !BRemoveEquipInstancesFromSQL( sqlAccess, GetAccountID(), GetItemID() ) )
+ return false;
+
+ return true;
+}
+
+// --------------------------------------------------------------------------
+// Purpose: Load the interior item from the database.
+// --------------------------------------------------------------------------
+bool CEconItem::BYieldingLoadInteriorItem()
+{
+ static CSchemaAttributeDefHandle pAttribLow( "referenced item id low" );
+ static CSchemaAttributeDefHandle pAttribHigh( "referenced item id high" );
+
+ // see if it's already loaded
+ if ( m_pCustomData && m_pCustomData->m_pInteriorItem )
+ return true;
+
+ // we require the low 32 bits...
+ uint32 unAttribValueBitsLow;
+ if ( !FindAttribute( pAttribLow, &unAttribValueBitsLow ) )
+ return false;
+
+ // ...but default the high 32 bits to 0 if not present
+ uint32 unAttribValueBitsHigh = 0;
+ FindAttribute( pAttribHigh, &unAttribValueBitsHigh ); // return value ignored
+
+ COMPILE_TIME_ASSERT( sizeof( uint64 ) == sizeof( itemid_t ) );
+ uint64 wrappedItemId = ( uint64( unAttribValueBitsHigh ) << 32 ) | uint64( unAttribValueBitsLow );
+
+ // allocate scratch object to try and fill with DB contents
+ CEconItem *pNewItem = new CEconItem;
+ if ( !pNewItem->BYieldingSerializeFromDatabase( wrappedItemId ) )
+ {
+ delete pNewItem;
+ return false;
+ }
+
+ // DB load succeeded -- make sure if we're treating this item ID as an interior item, it isn't
+ // currently owned by someone. this is a non-fatal error (it could happen if support rolls back
+ // transactions in weird ways) but it probably means we have something weird going on
+ if ( pNewItem->GetAccountID() != 0 )
+ {
+ // ...
+ }
+
+ // everything loaded alright so hook our reference up
+ EnsureCustomDataExists();
+ m_pCustomData->m_pInteriorItem = pNewItem;
+ return true;
+}
+
+
+// --------------------------------------------------------------------------
+// Purpose: Directly set the interior item
+// --------------------------------------------------------------------------
+void CEconItem::SetInteriorItem( CEconItem* pInteriorItem )
+{
+ EnsureCustomDataExists();
+ m_pCustomData->m_pInteriorItem = pInteriorItem;
+}
+
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+CEconItem* CEconItem::YieldingGetInteriorItem()
+{
+ if ( !m_pCustomData || !m_pCustomData->m_pInteriorItem )
+ {
+ if ( !BYieldingLoadInteriorItem() )
+ {
+ Assert( !GetInteriorItem() );
+ }
+ }
+
+ return GetInteriorItem();
+}
+
+#endif // GC_DLL
+
+CEconItem* CEconItem::GetInteriorItem()
+{
+ return m_pCustomData ? m_pCustomData->m_pInteriorItem : NULL;
+}
+
+// --------------------------------------------------------------------------
+// Purpose: This item has been traded. Give it an opportunity to update any internal
+// properties in response.
+// --------------------------------------------------------------------------
+void CEconItem::OnTraded( uint32 unTradabilityDelaySeconds )
+{
+ // if Steam wants us to impose a tradability delay on the item
+ if ( unTradabilityDelaySeconds != 0 )
+ {
+ RTime32 rtTradableAfter = ( ( CRTime::RTime32TimeCur() / k_nSecondsPerDay ) * k_nSecondsPerDay ) + unTradabilityDelaySeconds;
+ SetTradableAfterDateTime( rtTradableAfter );
+ }
+ else
+ {
+ // If we have a "tradable after date" attribute and we were just traded, remove the date
+ // limit as we're obviously past it.
+ static CSchemaAttributeDefHandle pAttrib_TradableAfter( "tradable after date" );
+ RemoveDynamicAttribute( pAttrib_TradableAfter );
+ }
+
+ OnTransferredOwnership();
+}
+
+// --------------------------------------------------------------------------
+// Purpose: Ownership of this item has changed, so do whatever things are necessary
+// --------------------------------------------------------------------------
+void CEconItem::OnTransferredOwnership()
+{
+ // Reset all our strange scores.
+ for ( int i = 0; i < GetKillEaterAttrCount(); i++ )
+ {
+ const CEconItemAttributeDefinition *pAttrDef = GetKillEaterAttr_Score(i);
+
+ // Skip over any attributes our schema doesn't understand. We ideally wouldn't ever
+ // have this happen but if it does we don't want to ignore other valid attributes.
+ if ( !pAttrDef )
+ continue;
+
+ // Ignore any attributes we don't have on this item.
+ if ( !FindAttribute( pAttrDef ) )
+ continue;
+
+ // Zero out the value of this stat attribute.
+ SetDynamicAttributeValue( pAttrDef, 0u );
+ }
+
+ // Free accounts have the ability to trade any item out that they received in a trade.
+ SetFlag( kEconItemFlag_CanBeTradedByFreeAccounts );
+}
+
+// --------------------------------------------------------------------------
+// Purpose: This item has been traded. Give it an opportunity to update any internal
+// properties in response.
+// --------------------------------------------------------------------------
+void CEconItem::OnReceivedFromMarket( bool bFromRollback )
+{
+ OnTransferredOwnership();
+
+#ifdef GC_DLL
+ static CSchemaAttributeDefHandle pAttrib_TradableAfter( "tradable after date" );
+ if ( !bFromRollback && GEconManager()->GetTradableAfterDurationForPurchase() != 0 )
+ {
+ // Add "tradable after date" attribute for items received from the market, since the funds
+ // used to purchase the item may be untrusted
+ RTime32 rtTradableAfter = GEconManager()->GetTradableAfterDateForPurchase();
+ SetDynamicAttributeValue( pAttrib_TradableAfter, rtTradableAfter );
+ }
+ // Do we need to remove this attribute on rollback?
+ /*else
+ {
+ RemoveDynamicAttribute( pAttrib_TradableAfter );
+ }*/
+#endif // GC_DLL
+}
+
+// --------------------------------------------------------------------------
+// Purpose: Parses the bits required to create a econ item from the message.
+// Overloaded to include support for attributes.
+// --------------------------------------------------------------------------
+bool CEconItem::BParseFromMessage( const CUtlBuffer & buffer )
+{
+ CSOEconItem msgItem;
+ if( !msgItem.ParseFromArray( buffer.Base(), buffer.TellMaxPut() ) )
+ return false;
+
+ DeserializeFromProtoBufItem( msgItem );
+ return true;
+}
+
+// --------------------------------------------------------------------------
+// Purpose: Parses the bits required to create a econ item from the message.
+// Overloaded to include support for attributes.
+// --------------------------------------------------------------------------
+bool CEconItem::BParseFromMessage( const std::string &buffer )
+{
+ CSOEconItem msgItem;
+ if( !msgItem.ParseFromString( buffer ) )
+ return false;
+
+ DeserializeFromProtoBufItem( msgItem );
+ return true;
+}
+
+//----------------------------------------------------------------------------
+// Purpose: Overrides all the fields in msgLocal that are present in the
+// network message
+//----------------------------------------------------------------------------
+bool CEconItem::BUpdateFromNetwork( const CSharedObject & objUpdate )
+{
+ const CEconItem & econObjUpdate = (const CEconItem &)objUpdate;
+
+ *this = econObjUpdate;
+
+ return true;
+}
+
+#ifdef GC
+//----------------------------------------------------------------------------
+// Purpose: Adds the relevant bits to update this object to the message. This
+// must include any relevant information about which fields are being
+// updated. This is called once for all subscribers.
+//----------------------------------------------------------------------------
+bool CEconItem::BAddToMessage( CUtlBuffer & bufOutput ) const
+{
+ VPROF_BUDGET( "CEconItem::BAddToMessage::CUtlBuffer", VPROF_BUDGETGROUP_STEAM );
+ // StaticAssert( sizeof( int ) >= sizeof( uint32 ) );
+
+ CSOEconItem msg;
+ SerializeToProtoBufItem( msg );
+ return CProtoBufSharedObjectBase::SerializeToBuffer( msg, bufOutput );
+}
+
+//----------------------------------------------------------------------------
+// Purpose: Adds the relevant bits to update this object to the message. This
+// must include any relevant information about which fields are being
+// updated. This is called once for all subscribers.
+//----------------------------------------------------------------------------
+bool CEconItem::BAddToMessage( std::string *pBuffer ) const
+{
+ VPROF_BUDGET( "CEconItem::BAddToMessage::std::string", VPROF_BUDGETGROUP_STEAM );
+ // StaticAssert( sizeof( int ) >= sizeof( uint32 ) );
+
+ CSOEconItem msg;
+ SerializeToProtoBufItem( msg );
+ return msg.SerializeToString( pBuffer );
+}
+
+//----------------------------------------------------------------------------
+// Purpose: Adds just the item ID to the message so that the client can find
+// which item to destroy
+//----------------------------------------------------------------------------
+bool CEconItem::BAddDestroyToMessage( CUtlBuffer & bufDestroy ) const
+{
+ CSOEconItem msgItem;
+ msgItem.set_id( GetItemID() );
+ CProtoBufSharedObjectBase::SerializeToBuffer( msgItem, bufDestroy );
+ return true;
+}
+
+//----------------------------------------------------------------------------
+// Purpose: Adds just the item ID to the message so that the client can find
+// which item to destroy
+//----------------------------------------------------------------------------
+bool CEconItem::BAddDestroyToMessage( std::string *pBuffer ) const
+{
+ CSOEconItem msgItem;
+ msgItem.set_id( GetItemID() );
+ return msgItem.SerializeToString( pBuffer );
+}
+#endif //GC
+
+//----------------------------------------------------------------------------
+// Purpose: Returns true if this is less than than the object in soRHS. This
+// comparison is deterministic, but it may not be pleasing to a user
+// since it is just going to compare raw memory. If you need a sort
+// that is user-visible you will need to do it at a higher level that
+// actually knows what the data in these objects means.
+//----------------------------------------------------------------------------
+bool CEconItem::BIsKeyLess( const CSharedObject & soRHS ) const
+{
+ Assert( GetTypeID() == soRHS.GetTypeID() );
+ const CEconItem & soSchemaRHS = (const CEconItem &)soRHS;
+
+ return m_ulID < soSchemaRHS.m_ulID;
+}
+
+//----------------------------------------------------------------------------
+// Purpose: Copy the data from the specified schema shared object into this.
+// Both objects must be of the same type.
+//----------------------------------------------------------------------------
+void CEconItem::Copy( const CSharedObject & soRHS )
+{
+ *this = (const CEconItem &)soRHS;
+}
+
+//----------------------------------------------------------------------------
+// Purpose: Dumps diagnostic information about the shared object
+//----------------------------------------------------------------------------
+void CEconItem::Dump() const
+{
+ CSOEconItem msgItem;
+ SerializeToProtoBufItem( msgItem );
+ CProtoBufSharedObjectBase::Dump( msgItem );
+}
+
+
+//----------------------------------------------------------------------------
+// Purpose: Return short, identifying string about the object
+//----------------------------------------------------------------------------
+CUtlString CEconItem::GetDebugString() const
+{
+ CUtlString result;
+ result.Format( "[CEconItem: ID=%llu, DefIdx=%d]", GetItemID(), GetDefinitionIndex() );
+ return result;
+}
+
+
+#ifdef GC
+//-----------------------------------------------------------------------------
+// Purpose: Deserializes an item from a KV object
+// Input: pKVItem - Pointer to the KV structure that represents an item
+// schema - Econ item schema used for decoding human readable names
+// pVecErrors - Pointer to a vector where human readable errors will
+// be added
+// Output: True if the item deserialized successfully, false otherwise
+//-----------------------------------------------------------------------------
+bool CEconItem::BDeserializeFromKV( KeyValues *pKVItem, CUtlVector<CUtlString> *pVecErrors )
+{
+ Assert( pVecErrors );
+
+ Assert( NULL != pKVItem );
+ if ( NULL == pKVItem )
+ return false;
+
+ // The basic properties
+ SetItemID( pKVItem->GetUint64( "ID", INVALID_ITEM_ID ) );
+ SetInventoryToken( pKVItem->GetInt( "InventoryPos", GetUnacknowledgedPositionFor(UNACK_ITEM_DROPPED) ) ); // Start by assuming it's a drop
+ SetQuantity( pKVItem->GetInt( "Quantity", 1 ) );
+
+ // Look up the following properties based on names from the schema
+ const char *pchDefName = pKVItem->GetString( "DefName" );
+ const CEconItemDefinition *pItemDef = GetItemSchema()->GetItemDefinitionByName( pchDefName );
+ if( !pItemDef )
+ {
+ pVecErrors->AddToTail( CUtlString( CFmtStr( "Item definition \"%s\" not found", pchDefName ) ) );
+
+ // we can't do any reasonable validation with no item def, so just stop here
+ return false;
+ }
+
+ SetDefinitionIndex( pItemDef->GetDefinitionIndex() );
+
+ const char *pchQualityName = pKVItem->GetString( "QualityName" );
+ if( !pchQualityName || ! *pchQualityName )
+ {
+ // set the default quality for the definition
+ if( pItemDef->GetQuality() == k_unItemQuality_Any )
+ {
+ pVecErrors->AddToTail( CUtlString( CFmtStr( "Quality was not specified and this item def doesn't define one either." ) ) );
+ }
+ else
+ {
+ SetQuality( pItemDef->GetQuality() );
+ }
+ }
+ else if ( !GetItemSchema()->BGetItemQualityFromName( pchQualityName, &m_nQuality ) || k_unItemQuality_Any == GetQuality() )
+ {
+ pVecErrors->AddToTail( CUtlString( CFmtStr( "Quality \"%s\" not found", pchQualityName ) ) );
+ }
+
+ // make sure the level is sane
+ SetItemLevel( pKVItem->GetInt( "Level", pItemDef->GetMinLevel() ) );
+
+ // read the flags
+ uint8 unFlags = GetFlags();
+ if( pKVItem->GetInt( "flag_cannot_trade", 0 ) )
+ {
+ unFlags |= kEconItemFlag_CannotTrade;
+ }
+ else
+ {
+ unFlags = unFlags & ~kEconItemFlag_CannotTrade;
+ }
+ if( pKVItem->GetInt( "flag_cannot_craft", 0 ) )
+ {
+ unFlags |= kEconItemFlag_CannotBeUsedInCrafting;
+ }
+ else
+ {
+ unFlags = unFlags & ~kEconItemFlag_CannotBeUsedInCrafting;
+ }
+ if( pKVItem->GetInt( "flag_non_economy", 0 ) )
+ {
+ unFlags |= kEconItemFlag_NonEconomy;
+ }
+ else
+ {
+ unFlags = unFlags & ~kEconItemFlag_NonEconomy;
+ }
+ SetFlag( unFlags );
+
+ // Deserialize the attributes
+ KeyValues *pKVAttributes = pKVItem->FindKey( "Attributes" );
+ if ( NULL != pKVAttributes )
+ {
+ FOR_EACH_SUBKEY( pKVAttributes, pKVAttr )
+ {
+ // Try to load each line into an attribute in memory. It's possible that if we fail to successfully
+ // load some attribute contents here we'll leak small amounts of memory, but if that happens we're
+ // going to fail to start up anyway so we don't really care.
+ static_attrib_t staticAttrib;
+ if ( !staticAttrib.BInitFromKV_SingleLine( __FUNCTION__, pKVAttr, pVecErrors ) )
+ continue;
+
+ const CEconItemAttributeDefinition *pAttrDef = staticAttrib.GetAttributeDefinition();
+ Assert( pAttrDef );
+
+ const ISchemaAttributeType *pAttrType = pAttrDef->GetAttributeType();
+ Assert( pAttrType );
+
+ // Load the attribute contents into memory on the item.
+ pAttrType->LoadEconAttributeValue( this, pAttrDef, staticAttrib.m_value );
+
+ // Free up our temp loading memory.
+ pAttrType->UnloadEconAttributeValue( &staticAttrib.m_value );
+ }
+ }
+
+ return ( NULL == pVecErrors || 0 == pVecErrors->Count() );
+}
+
+
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+bool CEconItem::BYieldingSerializeFromDatabase( itemid_t ulItemID )
+{
+ static CColumnSet csItem( CSET_FULL( CSchItem ) );
+
+ enum
+ {
+ kQueryResultSet_SingleItem_Item = 0,
+ kQueryResultSet_SingleItem_Attributes_Base = 1,
+
+ kQueryResultSet_SingleItemCount
+ };
+
+ static uint32 sExpectedResults = 0;
+ static CFmtStrMax sLoadQuery;
+ if ( 0 == sLoadQuery.Length() )
+ {
+ sLoadQuery.Append(
+ "DECLARE @ItemID BIGINT "
+ "SET @ItemID = ? " );
+
+ // Load the item itself.
+ TSQLCmdStr sStatement;
+ BuildSelectStatementText( &sStatement, csItem );
+ sStatement.Append( " WHERE ID = @ItemID " );
+ sLoadQuery.Append( sStatement );
+ DbgVerify( kQueryResultSet_SingleItem_Item == sExpectedResults++ );
+
+ // Load any associated attributes.
+ DbgVerify( kQueryResultSet_SingleItem_Attributes_Base == sExpectedResults++ );
+ {
+ const CUtlVector<attr_type_t>& vecAttributeTypes = GetItemSchema()->GetAttributeTypes();
+
+ FOR_EACH_VEC( vecAttributeTypes, i )
+ {
+ // We could actually skip loading the "ItemID" column here because we're only loading one item
+ // of data, but we would have to do some work because we don't actually know the CSch types here
+ // so it doesn't seem worth it. We'll have to update the LoadSQLAttributesToEconItem() code if
+ // we ever do this.
+ BuildSelectStatementText( &sStatement, vecAttributeTypes[i].m_pAttrType->GetFullColumnSet() );
+ sStatement.Append( " WHERE ItemID = @ItemID " );
+ sLoadQuery.Append( sStatement );
+ }
+ }
+ }
+ Assert( kQueryResultSet_SingleItemCount == sExpectedResults );
+
+ // Load item and associated attributes from SQL.
+ CSQLAccess sqlAccess;
+ sqlAccess.AddBindParam( ulItemID );
+ if ( !sqlAccess.BYieldingExecute( "CEconItem::BYieldingSerializeFromDatabase", sLoadQuery.Get() ) )
+ {
+ EmitError( SPEW_GC, __FUNCTION__ ": failed to execute SQL load for item ID %llu\n", ulItemID );
+ return false;
+ }
+
+ // ...
+ if ( sqlAccess.GetResultSetCount() < sExpectedResults )
+ {
+ EmitError( SPEW_GC, __FUNCTION__ ": unable to interior item ID %llu. Got %d results back, but expected %d!\n", ulItemID, sqlAccess.GetResultSetCount(), sExpectedResults );
+ return false;
+ }
+
+ // Make sure we only got one item back.
+ const uint32 nRowCount = sqlAccess.GetResultSetRowCount( kQueryResultSet_SingleItem_Item );
+ if ( nRowCount != 1 )
+ {
+ EmitError( SPEW_GC, __FUNCTION__ ": invalid number of rows returned from SQL loading item ID %llu: %d\n", ulItemID, nRowCount );
+ return false;
+ }
+
+ // Make sure we understand what sort of item this is. We can't make any forward progress loading if we don't know
+ // what definition its attached to.
+ CSchItem schItem;
+ sqlAccess.GetResultRecord( kQueryResultSet_SingleItem_Item, 0 ).BWriteToRecord( &schItem, csItem );
+ const CEconItemDefinition *pItemDef = GetItemSchema()->GetItemDefinition( schItem.m_unDefIndex );
+ if ( !pItemDef )
+ {
+ EmitError( SPEW_GC, __FUNCTION__ ": unable to find item definition %d for item %llu!\n", schItem.m_unDefIndex, schItem.m_ulID );
+ return false;
+ }
+
+ // We have everything we need to complete the load here so default initialize -- free up attribute memory, current
+ // state, etc.
+ *this = CEconItem();
+
+ // Deserialize from DB schema.
+ DeserializeFromSchemaItem( schItem );
+
+ // Load attributes.
+ CEconManager::CEconItemAttributeLoader AttributeLoader;
+ AttributeLoader.LoadSQLAttributesToEconItem( this, sqlAccess, kQueryResultSet_SingleItem_Attributes_Base );
+
+ // Done.
+ Compact();
+
+ return true;
+}
+
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+void CEconItem::SerializeToSchemaItem( CSchItem &item ) const
+{
+ VPROF_BUDGET( "CEconItem::SerializeToSchemaItem()", VPROF_BUDGETGROUP_STEAM );
+
+ item.m_ulID = m_ulID;
+ item.m_ulOriginalID = GetOriginalID();
+ item.m_unAccountID = m_unAccountID;
+ item.m_unDefIndex = m_unDefIndex;
+ item.m_unLevel = m_unLevel;
+ item.m_nQuality = m_nQuality;
+ item.m_unInventory = m_unInventory;
+ item.m_unQuantity = GetQuantity();
+ item.m_unFlags = m_unFlags;
+ item.m_unOrigin = m_unOrigin;
+ item.m_unStyle = m_unStyle;
+}
+
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+void CEconItem::DeserializeFromSchemaItem( const CSchItem &item )
+{
+ VPROF_BUDGET( "CEconItem::DeserializeFromSchemaItem()", VPROF_BUDGETGROUP_STEAM );
+
+ m_ulID = item.m_ulID;
+ SetOriginalID( item.m_ulOriginalID ? item.m_ulOriginalID : item.m_ulID );
+ m_unAccountID = item.m_unAccountID;
+ m_unDefIndex = item.m_unDefIndex;
+ m_unLevel = item.m_unLevel;
+ m_nQuality = item.m_nQuality;
+ m_unInventory = item.m_unInventory;
+ SetQuantity( item.m_unQuantity );
+ m_unFlags = item.m_unFlags;
+ m_unOrigin = item.m_unOrigin;
+ m_unStyle = item.m_unStyle;
+
+ // set name if any, or remove if non-existent
+ SetCustomName( READ_VAR_CHAR_FIELD( item, m_VarCharCustomName ) );
+
+ // set desc if any, or remove if non-existent
+ SetCustomDesc( READ_VAR_CHAR_FIELD( item, m_VarCharCustomDesc ) );
+}
+#endif // GC
+
+
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+void CEconItem::SerializeToProtoBufItem( CSOEconItem &msgItem ) const
+{
+ VPROF_BUDGET( "CEconItem::SerializeToProtoBufItem()", VPROF_BUDGETGROUP_STEAM );
+
+ msgItem.set_id( m_ulID );
+ if( m_ulID != GetOriginalID() )
+ msgItem.set_original_id( GetOriginalID() );
+ msgItem.set_account_id( m_unAccountID );
+ msgItem.set_def_index( m_unDefIndex );
+ msgItem.set_level( m_unLevel );
+ msgItem.set_quality( m_nQuality );
+ msgItem.set_inventory( m_unInventory );
+ msgItem.set_quantity( GetQuantity() );
+ msgItem.set_flags( m_unFlags );
+ msgItem.set_origin( m_unOrigin );
+ msgItem.set_style( m_unStyle );
+ msgItem.set_in_use( m_dirtyBits.m_bInUse );
+
+ for( int nAttr = 0; nAttr < GetDynamicAttributeCountInternal(); nAttr++ )
+ {
+ const attribute_t & attr = GetDynamicAttributeInternal( nAttr );
+
+ // skip over attributes we don't understand
+ const CEconItemAttributeDefinition *pAttrDef = GetItemSchema()->GetAttributeDefinition( attr.m_unDefinitionIndex );
+ if ( !pAttrDef )
+ continue;
+
+ const ISchemaAttributeType *pAttrType = pAttrDef->GetAttributeType();
+ Assert( pAttrType );
+
+ CSOEconItemAttribute *pMsgAttr = msgItem.add_attribute();
+ pMsgAttr->set_def_index( attr.m_unDefinitionIndex );
+
+ std::string sBytes;
+ pAttrType->ConvertEconAttributeValueToByteStream( attr.m_value, &sBytes );
+ pMsgAttr->set_value_bytes( sBytes );
+ }
+
+ msgItem.set_contains_equipped_state_v2( true );
+ for ( int i = 0; i < GetEquippedInstanceCount(); i++ )
+ {
+ const EquippedInstance_t &instance = GetEquippedInstance( i );
+ CSOEconItemEquipped *pMsgEquipped = msgItem.add_equipped_state();
+ pMsgEquipped->set_new_class( instance.m_unEquippedClass );
+ pMsgEquipped->set_new_slot( instance.m_unEquippedSlot );
+ }
+
+ if ( m_pCustomData )
+ {
+ const char *pszCustomName = GetCustomName();
+ if ( pszCustomName )
+ {
+ msgItem.set_custom_name( pszCustomName );
+ }
+
+ const char *pszCustomDesc = GetCustomDesc();
+ if ( pszCustomDesc )
+ {
+ msgItem.set_custom_desc( pszCustomDesc );
+ }
+
+ const CEconItem *pInteriorItem = GetInteriorItem();
+ if ( pInteriorItem )
+ {
+ CSOEconItem *pMsgInteriorItem = msgItem.mutable_interior_item();
+ pInteriorItem->SerializeToProtoBufItem( *pMsgInteriorItem );
+ }
+ }
+}
+
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+void CEconItem::DeserializeFromProtoBufItem( const CSOEconItem &msgItem )
+{
+ VPROF_BUDGET( "CEconItem::DeserializeFromProtoBufItem()", VPROF_BUDGETGROUP_STEAM );
+
+ // Start by resetting
+ SAFE_DELETE( m_pCustomData );
+ m_dirtyBits.m_bHasAttribSingleton = false;
+ m_dirtyBits.m_bHasEquipSingleton = false;
+
+ // Now copy from the message
+ m_ulID = msgItem.id();
+ SetOriginalID( msgItem.has_original_id() ? msgItem.original_id() : m_ulID );
+ m_unAccountID = msgItem.account_id();
+ m_unDefIndex = msgItem.def_index();
+ m_unLevel = msgItem.level();
+ m_nQuality = msgItem.quality();
+ m_unInventory = msgItem.inventory();
+ SetQuantity( msgItem.quantity() );
+ m_unFlags = msgItem.flags();
+ m_unOrigin = msgItem.origin();
+ m_unStyle = msgItem.style();
+
+ m_dirtyBits.m_bInUse = msgItem.in_use() ? 1 : 0;
+
+ // set name if any
+ if( msgItem.has_custom_name() )
+ {
+ SetCustomName( msgItem.custom_name().c_str() );
+ }
+
+ // set desc if any
+ if( msgItem.has_custom_desc() )
+ {
+ SetCustomDesc( msgItem.custom_desc().c_str() );
+ }
+
+ // read the attributes
+ for( int nAttr = 0; nAttr < msgItem.attribute_size(); nAttr++ )
+ {
+ // skip over old-format messages
+ const CSOEconItemAttribute& msgAttr = msgItem.attribute( nAttr );
+ if ( msgAttr.has_value() || !msgAttr.has_value_bytes() )
+ continue;
+
+ // skip over attributes we don't understand
+ const CEconItemAttributeDefinition *pAttrDef = GetItemSchema()->GetAttributeDefinition( msgAttr.def_index() );
+ if ( !pAttrDef )
+ continue;
+
+ const ISchemaAttributeType *pAttrType = pAttrDef->GetAttributeType();
+ Assert( pAttrType );
+
+ pAttrType->LoadByteStreamToEconAttributeValue( this, pAttrDef, msgAttr.value_bytes() );
+ }
+
+ // Check to see if the item has an interior object.
+ if ( msgItem.has_interior_item() )
+ {
+ EnsureCustomDataExists();
+
+ m_pCustomData->m_pInteriorItem = new CEconItem();
+ m_pCustomData->m_pInteriorItem->DeserializeFromProtoBufItem( msgItem.interior_item() );
+ }
+
+ // update equipped state
+ if ( msgItem.has_contains_equipped_state_v2() && msgItem.contains_equipped_state_v2() )
+ {
+ // unequip from everything...
+ Unequip();
+
+ // ...and re-equip to whatever our current state is
+ for ( int i = 0; i < msgItem.equipped_state_size(); i++ )
+ {
+ Equip( msgItem.equipped_state(i).new_class(), msgItem.equipped_state(i).new_slot() );
+ }
+ }
+}
+
+#ifdef GC
+#include "econ/localization_provider.h"
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+void CEconItem::GetDirtyColumnSet( const CUtlVector< int > &fields, CColumnSet &cs ) const
+{
+ cs.MakeEmpty();
+ /* @note Tom Bui: We know only the inventory/position field and the quantity field can be dirty...
+ for ( int nField = 0; nField < CSchItem::k_iFieldMax; ++nField )
+ {
+ if ( bDatabase ? BIsFieldDatabaseDirty( nField ) : BIsFieldNetworkDirty( nField ) )
+ {
+ cs.BAddColumn( nField );
+ }
+ }
+ */
+ if( fields.HasElement( CSOEconItem::kInventoryFieldNumber ) )
+ cs.BAddColumn( CSchItem::k_iField_unInventory );
+ if( fields.HasElement( CSOEconItem::kQuantityFieldNumber ) )
+ cs.BAddColumn( CSchItem::k_iField_unQuantity );
+ if( fields.HasElement( CSOEconItem::kStyleFieldNumber ) )
+ cs.BAddColumn( CSchItem::k_iField_unStyle );
+}
+
+
+// --------------------------------------------------------------------------
+// Purpose: Exports the item for use in another app
+// --------------------------------------------------------------------------
+void CEconItem::ExportToAPI( CWebAPIValues *pValues ) const
+{
+ pValues->CreateChildObject( "id" )->SetUInt64Value( m_ulID );
+ pValues->CreateChildObject( "def_index" )->SetUInt32Value( m_unDefIndex );
+ pValues->CreateChildObject( "level" )->SetUInt32Value( m_unLevel );
+ pValues->CreateChildObject( "quality" )->SetInt32Value( m_nQuality );
+
+ if( GetCustomName() )
+ pValues->CreateChildObject( "custom_name" )->SetStringValue( GetCustomName() );
+ if( GetCustomDesc() )
+ pValues->CreateChildObject( "custom_desc" )->SetStringValue( GetCustomDesc() );
+}
+
+
+// --------------------------------------------------------------------------
+// Purpose: Imports a view of the item from another app
+// --------------------------------------------------------------------------
+bool CEconItem::BImportFromAPI( CWebAPIValues *pValues )
+{
+ m_unLevel = pValues->GetChildUInt32Value( "level", 1 );
+ m_nQuality = pValues->GetChildUInt32Value( "quality", GetItemSchema()->GetDefaultQuality() );
+
+ CUtlString sValue;
+ pValues->GetChildStringValue( sValue, "custom_name", "" );
+ CGCLocalizationProvider::BEnsureCleanUTF8Truncation( sValue.GetForModify() );
+ if( !sValue.IsEmpty() )
+ SetCustomName( sValue.Get() );
+ pValues->GetChildStringValue( sValue, "custom_desc", "" );
+ CGCLocalizationProvider::BEnsureCleanUTF8Truncation( sValue.GetForModify() );
+ if( !sValue.IsEmpty() )
+ SetCustomDesc( sValue.Get() );
+
+ return true;
+}
+
+#endif
+
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+void CEconItem::EnsureCustomDataExists()
+{
+ if ( m_pCustomData == NULL )
+ {
+ m_pCustomData = new CEconItemCustomData();
+
+ if ( m_dirtyBits.m_bHasEquipSingleton )
+ {
+ m_pCustomData->m_vecEquipped.AddToTail( m_EquipInstanceSingleton );
+ m_EquipInstanceSingleton = EquippedInstance_t();
+ m_dirtyBits.m_bHasEquipSingleton = false;
+ }
+ if ( m_dirtyBits.m_bHasAttribSingleton )
+ {
+ m_pCustomData->m_vecAttributes.AddToTail( m_CustomAttribSingleton );
+ m_dirtyBits.m_bHasAttribSingleton = false;
+ }
+ }
+}
+
+#ifdef GC_DLL
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+static bool BYieldingAddAuditRecordImpl( GCSDK::CSQLAccess *sqlAccess, uint64 ulItemID, uint32 unOwnerID, EItemAction eAction, uint32 unData )
+{
+ CEconUserSession *pUserSession = GGCGameBase()->FindEconUserSession( CSteamID( unOwnerID, GGCHost()->GetUniverse(), k_EAccountTypeIndividual ) );
+ CGCGSSession *pGSSession = pUserSession && pUserSession->GetSteamIDGS().IsValid() ? GGCEcon()->FindGSSession( pUserSession->GetSteamIDGS() ) : NULL;
+
+ // Prepare the audit record
+ CSchItemAudit schItemAudit;
+ schItemAudit.m_ulItemID = ulItemID;
+ schItemAudit.m_RTime32Stamp = CRTime::RTime32TimeCur();
+ schItemAudit.m_eAction = eAction;
+ schItemAudit.m_unOwnerID = unOwnerID;
+ schItemAudit.m_unData = unData;
+ schItemAudit.m_unServerIP = pGSSession ? pGSSession->GetAddr() : 0;
+ schItemAudit.m_usServerPort = pGSSession ? pGSSession->GetPort() : 0;
+
+ return sqlAccess->BYieldingInsertRecord( &schItemAudit );
+}
+
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+bool CEconItem::CAuditEntry::BAddAuditEntryToTransaction( CSQLAccess& sqlAccess, const CEconItem *pItem ) const
+{
+ return BYieldingAddAuditRecordImpl( &sqlAccess, pItem->GetID(), pItem->GetAccountID(), m_eAction, m_unData );
+}
+
+// --------------------------------------------------------------------------
+// Purpose: Adds an audit record to the DB for an item
+// --------------------------------------------------------------------------
+void YieldingAddAuditRecord( GCSDK::CSQLAccess *sqlAccess, CEconItem *pItem, uint32 unOwnerID, EItemAction eAction, uint32 unData )
+{
+ YieldingAddAuditRecord( sqlAccess, pItem->GetItemID(), unOwnerID, eAction, unData );
+}
+
+// --------------------------------------------------------------------------
+// Purpose: Adds an audit record to the DB for an item
+// --------------------------------------------------------------------------
+void YieldingAddAuditRecord( GCSDK::CSQLAccess *sqlAccess, uint64 ulItemID, uint32 unOwnerID, EItemAction eAction, uint32 unData )
+{
+ Verify( BYieldingAddAuditRecordImpl( sqlAccess, ulItemID, unOwnerID, eAction, unData ) );
+}
+
+
+// --------------------------------------------------------------------------
+// Purpose: Adds an item to the DB
+// --------------------------------------------------------------------------
+bool YieldingAddItemToDatabase( CEconItem *pItem, const CSteamID & steamID, EItemAction eAction, uint32 unData )
+{
+ CSQLAccess sqlAccess;
+ sqlAccess.BBeginTransaction( "YieldingAddItemToDatabase" );
+
+ pItem->SetAccountID( steamID.GetAccountID() );
+ if( !pItem->BYieldingAddInsertToTransaction( sqlAccess ) )
+ {
+ sqlAccess.RollbackTransaction();
+ return false;
+ }
+
+ YieldingAddAuditRecord( &sqlAccess, pItem, steamID.GetAccountID(), eAction, unData );
+
+ // commit the item and audit record
+ if( !sqlAccess.BCommitTransaction() )
+ {
+ EmitError( SPEW_GC, "Failed to add item for %s\n", steamID.Render() );
+ return false;
+ }
+
+ return true;
+}
+
+//----------------------------------------------------------------------------
+// Purpose:
+//----------------------------------------------------------------------------
+static CEconItem *FindEquippedItemForClassAndSlot( CEconSharedObjectCache *pSOCache, equipped_class_t unClass, equipped_slot_t unSlot )
+{
+ VPROF_BUDGET( "FindEquippedItemForClassAndSlot()", VPROF_BUDGETGROUP_STEAM );
+ Assert( pSOCache );
+
+ CGCSharedObjectTypeCache *pItemTypeCache = pSOCache->FindTypeCache( k_EEconTypeItem );
+
+ const uint32 unCount = pItemTypeCache ? pItemTypeCache->GetCount() : 0;
+ for ( uint32 i = 0; i < unCount; i++ )
+ {
+ CEconItem *pItem = static_cast<CEconItem *>( pItemTypeCache->GetObject( i ) );
+ Assert( pItem );
+
+ for ( int j = 0; j < pItem->GetEquippedInstanceCount(); j++ )
+ {
+ const CEconItem::EquippedInstance_t& equipInst = pItem->GetEquippedInstance( j );
+
+ if ( equipInst.m_unEquippedClass == unClass && equipInst.m_unEquippedSlot == unSlot )
+ return pItem;
+ }
+ }
+
+ return NULL;
+}
+
+//----------------------------------------------------------------------------
+// Purpose:
+//----------------------------------------------------------------------------
+/*static*/ void CEconItemEquipInstanceHelpers::AssignItemToSlot( CEconSharedObjectCache *pSOCache, CEconItem *pItem, equipped_class_t unClass, equipped_slot_t unSlot, CEconUserSession *pOptionalSession /* = NULL */ )
+{
+ VPROF_BUDGET( "CEconEquipInstance::AssignItemToSlot()", VPROF_BUDGETGROUP_STEAM );
+
+ Assert( pSOCache );
+ Assert( GetItemSchema()->IsValidClass( unClass ) );
+ Assert( GetItemSchema()->IsValidItemSlot( unSlot, unClass ) );
+
+ CEconItem *pPreviousEquippedItem = FindEquippedItemForClassAndSlot( pSOCache, unClass, unSlot );
+
+ // skip out on expensive work if we're going to equip the item that's already in that slot
+ if ( pItem == pPreviousEquippedItem )
+ return;
+
+ // do we have an item to unequip first
+ if ( pPreviousEquippedItem )
+ {
+ pPreviousEquippedItem->UnequipFromClass( unClass );
+ pSOCache->DirtyObjectField( pPreviousEquippedItem, CSOEconItem::kEquippedStateFieldNumber );
+ GGCBase()->AddCacheToWritebackQueue( pSOCache );
+
+ const bool bPreviousItemIsNowEquipped = pPreviousEquippedItem->IsEquipped();
+
+ if ( pOptionalSession && !bPreviousItemIsNowEquipped )
+ {
+ pOptionalSession->OnEquippedStateChange( pPreviousEquippedItem, false );
+ }
+ }
+
+ // are we equipping a new item to this slot, or were we only responsible for unequipping
+ // the existing item?
+ if ( pItem )
+ {
+ // notify the session, in case it wants to do anything; the default implementation
+ // will update the equipped state on the item but other types may have additional
+ // behavior
+ const bool bItemWasEquipped = pItem->IsEquipped();
+
+ pItem->Equip( unClass, unSlot );
+ pSOCache->DirtyObjectField( pItem, CSOEconItem::kEquippedStateFieldNumber );
+ GGCBase()->AddCacheToWritebackQueue( pSOCache );
+
+ const bool bItemIsEquipped = pItem->IsEquipped();
+ Assert( bItemIsEquipped );
+
+ // did our equipped state change? this can happen if we equip ourself for the
+ // first time for this account right now. Keep in mind that this can be either
+ // a "is newly-equipped" or a "was equipped but is now not" state transition.
+ if ( pOptionalSession && (bItemWasEquipped != bItemIsEquipped) )
+ {
+ pOptionalSession->OnEquippedStateChange( pItem, bItemIsEquipped );
+ }
+ }
+}
+
+#define ITEM_ACTION( x ) { x, #x }
+
+ENUMSTRINGS_START( EItemAction )
+ ITEM_ACTION( k_EItemActionGSCreate ),
+ ITEM_ACTION( k_EItemActionUnpurchase ),
+ ITEM_ACTION( k_EItemActionDelete ),
+ ITEM_ACTION( k_EItemActionAwardAchievement ),
+ ITEM_ACTION( k_EItemActionBanned ),
+ ITEM_ACTION( k_EItemActionQuantityChanged ),
+ ITEM_ACTION( k_EItemActionRestored ),
+ ITEM_ACTION( k_EItemActionAwardTime ),
+ ITEM_ACTION( k_EItemActionManualCreate ),
+ ITEM_ACTION( k_EItemActionDrop ),
+ ITEM_ACTION( k_EItemActionPickUp ),
+ ITEM_ACTION( k_EItemActionCraftDestroy ),
+ ITEM_ACTION( k_EItemActionCraftCreate ),
+ ITEM_ACTION( k_EItemActionLimitExceeded ),
+ ITEM_ACTION( k_EItemActionPurchase ),
+ ITEM_ACTION( k_EItemActionNameChanged_Add ),
+ ITEM_ACTION( k_EItemActionUnlockCrate_Add ),
+ ITEM_ACTION( k_EItemActionPaintItem_Add ),
+ ITEM_ACTION( k_EItemActionAutoGrantItem ),
+ ITEM_ACTION( k_EItemActionCrossGameAchievement ),
+ ITEM_ACTION( k_EItemActionAddItemToSocket_Add ),
+ ITEM_ACTION( k_EItemActionAddSocketToItem_Add ),
+ ITEM_ACTION( k_EItemActionRemoveSocketItem_Add ),
+ ITEM_ACTION( k_EItemActionCustomizeItemTexture_Add ),
+ ITEM_ACTION( k_EItemActionItemTraded_Add ),
+ ITEM_ACTION( k_EItemActionUseItem ),
+ ITEM_ACTION( k_EItemActionAwardGift_Receiver ),
+ ITEM_ACTION( k_EItemActionNameChanged_Remove ),
+ ITEM_ACTION( k_EItemActionUnlockCrate_Remove ),
+ ITEM_ACTION( k_EItemActionPaintItem_Remove ),
+ ITEM_ACTION( k_EItemActionAddItemToSocket_Remove ),
+ ITEM_ACTION( k_EItemActionAddSocketToItem_Remove ),
+ ITEM_ACTION( k_EItemActionRemoveSocketItem_Remove ),
+ ITEM_ACTION( k_EItemActionCustomizeItemTexture_Remove ),
+ ITEM_ACTION( k_EItemActionItemTraded_Remove ),
+ ITEM_ACTION( k_EItemActionUnpackItemBundle ),
+ ITEM_ACTION( k_EItemActionCreateItemFromBundle ),
+ ITEM_ACTION( k_EItemActionAwardStorePromotionItem ),
+ ITEM_ACTION( k_EItemActionConvertItem ),
+ ITEM_ACTION( k_EItemActionEarnedItem ),
+ ITEM_ACTION( k_EItemActionAwardGift_Giver ),
+ ITEM_ACTION( k_EItemActionRefundedItem ),
+ ITEM_ACTION( k_EItemActionAwardThirdPartyPromo ),
+ ITEM_ACTION( k_EItemActionRemoveItemName_Remove ),
+ ITEM_ACTION( k_EItemActionRemoveItemName_Add ),
+ ITEM_ACTION( k_EItemActionRemoveItemPaint_Remove ),
+ ITEM_ACTION( k_EItemActionRemoveItemPaint_Add ),
+ ITEM_ACTION( k_EItemActionHalloweenDrop ),
+ ITEM_ACTION( k_EItemActionSteamWorkshopContributor ),
+ ITEM_ACTION( k_EItemActionManualOwnershipChange ),
+ ITEM_ACTION( k_EItemActionSupportDelete ),
+ ITEM_ACTION( k_EItemActionSupportCreatedByUndo ),
+ ITEM_ACTION( k_EItemActionSupportDeletedByUndo ),
+ ITEM_ACTION( k_EItemActionSupportQuantityChangedByUndo ),
+ ITEM_ACTION( k_EItemActionSupportRename_Add ),
+ ITEM_ACTION( k_EItemActionSupportRename_Remove ),
+ ITEM_ACTION( k_EItemActionSupportDescribe_Add ),
+ ITEM_ACTION( k_EItemActionSupportDescribe_Remove ),
+
+ ITEM_ACTION( k_EItemActionStrangePartApply_Add ),
+ ITEM_ACTION( k_EItemActionStrangePartApply_Remove ),
+ ITEM_ACTION( k_EItemActionStrangeScoreReset_Add ),
+ ITEM_ACTION( k_EItemActionStrangeScoreReset_Remove ),
+ ITEM_ACTION( k_EItemActionStrangePartRemove_Add ),
+ ITEM_ACTION( k_EItemActionStrangePartRemove_Remove ),
+
+ ITEM_ACTION( k_EItemActionStrangeRestrictionApply_Add ),
+ ITEM_ACTION( k_EItemActionStrangeRestrictionApply_Remove ),
+ ITEM_ACTION( k_EItemActionTransmogrify_Add ),
+ ITEM_ACTION( k_EItemActionTransmogrify_Remove ),
+ ITEM_ACTION( k_EItemActionHalloweenSpellPageAdd_Add ),
+ ITEM_ACTION( k_EItemActionHalloweenSpellPageAdd_Remove ),
+
+ ITEM_ACTION( k_EItemActionSupportStrangify_Add ),
+ ITEM_ACTION( k_EItemActionSupportStrangify_Remove ),
+
+ ITEM_ACTION( k_EItemActionUpgradeCardApply_Add ),
+ ITEM_ACTION( k_EItemActionUpgradeCardApply_Remove ),
+ ITEM_ACTION( k_EItemActionUpgradeCardRemove_Add ),
+ ITEM_ACTION( k_EItemActionUpgradeCardRemove_Remove ),
+
+#ifdef STAGING_ONLY
+ ITEM_ACTION( k_EItemActionDev_ClientLootListRoll ),
+#endif
+
+ ITEM_ACTION( k_EItemActionPeriodicScoreReward_Add ),
+ ITEM_ACTION( k_EItemActionPeriodicScoreReward_Remove ),
+
+ ITEM_ACTION( k_EItemActionGiftWrap_Add ),
+ ITEM_ACTION( k_EItemActionGiftWrap_Remove ),
+ ITEM_ACTION( k_EItemActionGiftDelivery_Add ),
+ ITEM_ACTION( k_EItemActionGiftDelivery_Remove ),
+ ITEM_ACTION( k_EItemActionGiftUnwrap_Add ),
+ ITEM_ACTION( k_EItemActionGiftUnwrap_Remove ),
+ ITEM_ACTION( k_EItemActionPackageItem ),
+ ITEM_ACTION( k_EItemActionPackageItem_Revoked ),
+ ITEM_ACTION( k_EItemActionHandleMapToken ),
+ ITEM_ACTION( k_EItemActionCafeOrSchoolItem_Remove ),
+ ITEM_ACTION( k_EItemActionVACBanned_Remove ),
+ ITEM_ACTION( k_EItemActionUpgradeThirdPartyPromo ),
+ ITEM_ACTION( k_EItemActionExpired ),
+ ITEM_ACTION( k_EItemActionTradeRollback_Add ),
+ ITEM_ACTION( k_EItemActionTradeRollback_Remove ),
+ ITEM_ACTION( k_EItemActionCDKeyGrant ),
+ ITEM_ACTION( k_EItemActionCDKeyRevoke ),
+ ITEM_ACTION( k_EItemActionWeddingRing_Add ),
+ ITEM_ACTION( k_EItemActionWeddingRing_Remove ),
+ ITEM_ACTION( k_EItemActionWeddingRing_AddPartner ),
+
+ ITEM_ACTION( k_EItemActionEconSetUnowned ),
+ ITEM_ACTION( k_EItemActionEconSetOwned ),
+ ITEM_ACTION( k_EItemActionStrangifyItem_Add ),
+ ITEM_ACTION( k_EItemActionStrangifyItem_Remove ),
+
+ ITEM_ACTION( k_EItemActionRemoveItemCraftIndex_Remove ),
+ ITEM_ACTION( k_EItemActionRemoveItemCraftIndex_Add ),
+ ITEM_ACTION( k_EItemActionRemoveItemMakersMark_Remove ),
+ ITEM_ACTION( k_EItemActionRemoveItemMakersMark_Add ),
+ ITEM_ACTION( k_EItemActionRemoveItemKillStreak_Remove ),
+ ITEM_ACTION( k_EItemActionRemoveItemKillStreak_Add ),
+
+ ITEM_ACTION( k_EItemActionCollectItem_RemoveCollection ),
+ ITEM_ACTION( k_EItemActionCollectItem_UpdateCollection ),
+ ITEM_ACTION( k_EItemActionCollectItem_CollectedItem ),
+ ITEM_ACTION( k_EItemActionCollectItem_RedeemCollectionReward ),
+
+ ITEM_ACTION( k_EItemActionPreviewItem_BeginPreviewPeriod ),
+ ITEM_ACTION( k_EItemActionPreviewItem_EndPreviewPeriodExpired ),
+ ITEM_ACTION( k_EItemActionPreviewItem_EndPreviewPeriodItemBought ),
+
+ ITEM_ACTION( k_EItemActionMvM_ChallengeCompleted_RemoveTicket ),
+ ITEM_ACTION( k_EItemActionMvM_ChallengeCompleted_GrantBadge ),
+ ITEM_ACTION( k_EItemActionMvM_ChallengeCompleted_UpdateBadgeStamps_Remove ),
+ ITEM_ACTION( k_EItemActionMvM_ChallengeCompleted_UpdateBadgeStamps_Add ),
+ ITEM_ACTION( k_EItemActionMvM_ChallengeCompleted_GrantMissionCompletionLoot ),
+ ITEM_ACTION( k_EItemActionMvM_RemoveSquadSurplusVoucher ),
+ ITEM_ACTION( k_EItemActionMvM_AwardSquadSurplus_Receiver ),
+ ITEM_ACTION( k_EItemActionMvM_AwardSquadSurplus_Giver ),
+ ITEM_ACTION( k_EItemActionMvM_ChallengeCompleted_GrantTourCompletionLoot ),
+ ITEM_ACTION( k_EItemActionMvM_AwardHelpANoobBonus_Helper ),
+
+ ITEM_ACTION( k_EItemActionHalloween_UpdateMerasmusLootLevel_Add ),
+ ITEM_ACTION( k_EItemActionHalloween_UpdateMerasmusLootLevel_Remove ),
+
+ ITEM_ACTION( k_EItemActionConsumeItem_Consume_ToolRemove ),
+ ITEM_ACTION( k_EItemActionConsumeItem_Consume_ToolAdd ),
+ ITEM_ACTION( k_EItemActionConsumeItem_Consume_InputRemove ),
+ ITEM_ACTION( k_EItemActionConsumeItem_Complete_OutputAdd ),
+ ITEM_ACTION( k_EItemActionConsumeItem_Complete_ToolRemove ),
+
+ ITEM_ACTION( k_EItemActionItemEaterRecharge_Add ),
+ ITEM_ACTION( k_EItemActionItemEaterRecharge_Remove ),
+
+ ITEM_ACTION( k_EItemActionSupportAddOrModifyAttribute_Remove ),
+ ITEM_ACTION( k_EItemActionSupportAddOrModifyAttribute_Add ),
+
+ ITEM_ACTION( k_EItemAction_UpdateDuckBadgeLevel_Add ),
+ ITEM_ACTION( k_EItemAction_UpdateDuckBadgeLevel_Remove ),
+
+ ITEM_ACTION( k_EItemAction_OperationPass_Add ),
+
+ ITEM_ACTION( k_EItemActionSpyVsEngyWar_JoinedWar ),
+
+ ITEM_ACTION( k_EItemActionMarket_Add ),
+ ITEM_ACTION( k_EItemActionMarket_Remove ),
+
+ ITEM_ACTION( k_EItemAction_QuestComplete_Reward ),
+ ITEM_ACTION( k_EItemAction_QuestComplete_Remove ),
+
+ ITEM_ACTION( k_EItemActionStrangeCountTransfer_Add ),
+ ITEM_ACTION( k_EItemActionStrangeCountTransfer_Remove ),
+
+ ITEM_ACTION( k_EItemActionCraftCollectionUpgrade_Add ),
+ ITEM_ACTION( k_EItemActionCraftCollectionUpgrade_Remove ),
+
+ ITEM_ACTION( k_EItemActionCraftHalloweenOffering_Add ),
+ ITEM_ACTION( k_EItemActionCraftHalloweenOffering_Remove ),
+
+ ITEM_ACTION( k_EItemActionRemoveItemGiftedBy_Remove ),
+ ITEM_ACTION( k_EItemActionRemoveItemGiftedBy_Add ),
+
+ ITEM_ACTION( k_EItemActionAddParticleVerticalAttr_Remove ),
+ ITEM_ACTION( k_EItemActionAddParticleVerticalAttr_Add ),
+
+ ITEM_ACTION( k_EItemActionAddParticleUseHeadOriginAttr_Remove ),
+ ITEM_ACTION( k_EItemActionAddParticleUseHeadOriginAttr_Add ),
+
+ ITEM_ACTION( k_EItemActionRemoveItemDynamicAttr_Remove ),
+ ITEM_ACTION( k_EItemActionRemoveItemDynamicAttr_Add ),
+
+ ITEM_ACTION( k_EItemActionCraftStatClockTradeUp_Remove ),
+ ITEM_ACTION( k_EItemActionCraftStatClockTradeUp_Add ),
+
+ ITEM_ACTION( k_EItemActionViralCompetitiveBetaPass_Drop ),
+
+ ITEM_ACTION( k_EItemActionSupportDeleteAttribute_Remove ),
+ ITEM_ACTION( k_EItemActionSupportDeleteAttribute_Add ),
+
+ENUMSTRINGS_END( EItemAction )
+
+const char *PchFriendlyNameFromEItemAction( EItemAction eItemAction, EItemActionMissingBehavior eMissingBehavior )
+{
+ Assert( eMissingBehavior == kEItemAction_FriendlyNameLookup_ReturnDummyStringIfMissing || eMissingBehavior == kEItemAction_FriendlyNameLookup_ReturnNULLIfMissing );
+
+ switch( eItemAction )
+ {
+
+ case k_EItemActionGSCreate: return "Created by Gameserver";
+ case k_EItemActionUnpurchase: return "Unpurchase";
+ case k_EItemActionDelete: return "Deleted by Owner";
+ case k_EItemActionAwardAchievement: return "Achievement Award";
+ case k_EItemActionBanned: return "Banned";
+ case k_EItemActionQuantityChanged: return "Quantity Changed";
+ case k_EItemActionRestored: return "Restored";
+ case k_EItemActionAwardTime: return "Timed Drop";
+ case k_EItemActionManualCreate: return "Created by Support";
+ case k_EItemActionDrop: return "Dropped";
+ case k_EItemActionPickUp: return "Picked up";
+ case k_EItemActionCraftDestroy: return "Crafted";
+ case k_EItemActionCraftCreate: return "Crafted";
+ case k_EItemActionLimitExceeded: return "Destroyed by Backpack Limit";
+ case k_EItemActionPurchase: return "Purchase";
+ case k_EItemActionNameChanged_Add: return "Added by Name Change";
+ case k_EItemActionUnlockCrate_Add: return "Added by Crate Unlock";
+ case k_EItemActionPaintItem_Add: return "Added by Paint";
+ case k_EItemActionAutoGrantItem: return "Autogrant";
+ case k_EItemActionCrossGameAchievement: return "Cross-Game Achievement Award";
+ case k_EItemActionAddItemToSocket_Add: return "Added to Socket";
+ case k_EItemActionAddSocketToItem_Add: return "Added by Socket";
+ case k_EItemActionRemoveSocketItem_Add: return "Removed by Socket";
+ case k_EItemActionCustomizeItemTexture_Add: return "Added by Custom Texture";
+ case k_EItemActionItemTraded_Add: return "Trade";
+ case k_EItemActionUseItem: return "Used";
+ case k_EItemActionAwardGift_Receiver: return "Gift Received";
+ case k_EItemActionNameChanged_Remove: return "Removed by Name Change";
+ case k_EItemActionUnlockCrate_Remove: return "Removed by Crate Unlock";
+ case k_EItemActionPaintItem_Remove: return "Removed by Paint";
+ case k_EItemActionAddItemToSocket_Remove: return "Removed from Socket";
+ case k_EItemActionAddSocketToItem_Remove: return "Removed by Socket";
+ case k_EItemActionRemoveSocketItem_Remove: return "Removed Socket Item";
+ case k_EItemActionCustomizeItemTexture_Remove: return "Removed by Custom Texture";
+ case k_EItemActionItemTraded_Remove: return "Trade";
+ case k_EItemActionUnpackItemBundle: return "Unpacked Bundle";
+ case k_EItemActionCreateItemFromBundle: return "Added from Bundle";
+ case k_EItemActionAwardStorePromotionItem: return "Store Promo Item";
+ case k_EItemActionConvertItem: return "Converted";
+ case k_EItemActionEarnedItem: return "Earned";
+ case k_EItemActionAwardGift_Giver: return "Gift Sent";
+ case k_EItemActionRefundedItem: return "Item Refunded";
+ case k_EItemActionAwardThirdPartyPromo: return "Third-Party Promo";
+ case k_EItemActionRemoveItemName_Remove: return "Removed by Name Removal";
+ case k_EItemActionRemoveItemName_Add: return "Added by Name Removal";
+ case k_EItemActionRemoveItemPaint_Remove: return "Removed by Paint Removal";
+ case k_EItemActionRemoveItemPaint_Add: return "Added by Paint Removal";
+ case k_EItemActionHalloweenDrop: return "Halloween Drop";
+ case k_EItemActionSteamWorkshopContributor: return "Steam Workshop Contributor";
+ case k_EItemActionManualOwnershipChange: return "Support Manual Ownership Change";
+ case k_EItemActionSupportDelete: return "Deleted by Support";
+ case k_EItemActionSupportCreatedByUndo: return "Created by Undo";
+ case k_EItemActionSupportDeletedByUndo: return "Deleted by Undo";
+ case k_EItemActionSupportQuantityChangedByUndo: return "Quantity Changed by Undo";
+ case k_EItemActionSupportRename_Add: return "Added by Support Rename";
+ case k_EItemActionSupportRename_Remove: return "Removed by Support Rename";
+ case k_EItemActionSupportDescribe_Add: return "Added by Support Describe";
+ case k_EItemActionSupportDescribe_Remove: return "Removed by Support Describe";
+
+ case k_EItemActionStrangePartApply_Add: return "Added by Strange Part Apply";
+ case k_EItemActionStrangePartApply_Remove: return "Removed by Strange Part Apply";
+ case k_EItemActionStrangeScoreReset_Add: return "Added by Strange Score Reset";
+ case k_EItemActionStrangeScoreReset_Remove: return "Removed by Strange Score Reset";
+ case k_EItemActionStrangePartRemove_Add: return "Added by Strange Part Remove";
+ case k_EItemActionStrangePartRemove_Remove: return "Removed by Strange Part Remove";
+
+ case k_EItemActionStrangeRestrictionApply_Add: return "Added by Strange Restriction Apply";
+ case k_EItemActionStrangeRestrictionApply_Remove: return "Removed by Strange Restriction Apply";
+ case k_EItemActionTransmogrify_Add: return "Added by Transmogrify Apply";
+ case k_EItemActionTransmogrify_Remove: return "Removed by Transmogrify Apply";
+ case k_EItemActionHalloweenSpellPageAdd_Add: return "Added by Halloween Spell Page Apply";
+ case k_EItemActionHalloweenSpellPageAdd_Remove: return "Removed by Halloween Spell Page Apply";
+
+ case k_EItemActionSupportStrangify_Add: return "Added by Support Strangify";
+ case k_EItemActionSupportStrangify_Remove: return "Removed by Support Strangify";
+
+ case k_EItemActionUpgradeCardApply_Add: return "Added by Upgrade Card Apply";
+ case k_EItemActionUpgradeCardApply_Remove: return "Removed by Upgrade Card Apply";
+ case k_EItemActionUpgradeCardRemove_Add: return "Added by Upgrade Card Remove";
+ case k_EItemActionUpgradeCardRemove_Remove: return "Removed by Upgrade Card Remove";
+
+#ifdef STAGING_ONLY
+ case k_EItemActionDev_ClientLootListRoll: return "Dev: Client Loot List Roll";
+#endif
+
+ case k_EItemActionPeriodicScoreReward_Add: return "Periodic Score System Reward Grant Add";
+ case k_EItemActionPeriodicScoreReward_Remove: return "Periodic Score System Reward Remove";
+
+ case k_EItemActionGiftWrap_Add: return "Wrapped Gift - Added";
+ case k_EItemActionGiftWrap_Remove: return "Wrapped Gift - Remove";
+ case k_EItemActionGiftDelivery_Add: return "Gift Delivered - Add";
+ case k_EItemActionGiftDelivery_Remove: return "Gift Delivered - Remove";
+ case k_EItemActionGiftUnwrap_Add: return "Unwrapped Gift - Added";
+ case k_EItemActionGiftUnwrap_Remove: return "Unwrapped Gift - Remove";
+ case k_EItemActionPackageItem: return "Added by Steam Purchase";
+ case k_EItemActionPackageItem_Revoked: return "Revoked by Steam Purchase";
+ case k_EItemActionHandleMapToken: return "Map Token Applied";
+ case k_EItemActionCafeOrSchoolItem_Remove: return "Cafe or School Removal";
+ case k_EItemActionVACBanned_Remove: return "VAC Ban removal";
+ case k_EItemActionUpgradeThirdPartyPromo: return "Third-Party Promo Upgrade";
+ case k_EItemActionExpired: return "Expired";
+ case k_EItemActionTradeRollback_Add: return "Trade Rollback";
+ case k_EItemActionTradeRollback_Remove: return "Trade Rollback";
+ case k_EItemActionCDKeyGrant: return "CD Key Grant";
+ case k_EItemActionCDKeyRevoke: return "CD Key Rollback";
+ case k_EItemActionWeddingRing_Add: return "Wedding Ring Add";
+ case k_EItemActionWeddingRing_Remove: return "Wedding Ring Remove";
+ case k_EItemActionWeddingRing_AddPartner: return "Wedding Ring Add (Partner)";
+
+ case k_EItemActionEconSetUnowned: return "Econ SetUnowned";
+ case k_EItemActionEconSetOwned: return "Econ SetOwned";
+
+ case k_EItemActionStrangifyItem_Add: return "Added by Strangify Apply";
+ case k_EItemActionStrangifyItem_Remove: return "Removed by Strangify Apply";
+
+ case k_EItemActionRemoveItemCraftIndex_Remove: return "Removed by Craft Index Removal";
+ case k_EItemActionRemoveItemCraftIndex_Add: return "Added by Craft Index Removal";
+ case k_EItemActionRemoveItemMakersMark_Remove: return "Removed by Maker's Mark Removal";
+ case k_EItemActionRemoveItemMakersMark_Add: return "Added by Maker's Mark Removal";
+ case k_EItemActionRemoveItemKillStreak_Remove: return "Removed by KillStreak Removal";
+ case k_EItemActionRemoveItemKillStreak_Add: return "Added by KillStreak Removal";
+
+ case k_EItemActionCollectItem_CollectedItem: return "Item Collection: Added to a Collection";
+ case k_EItemActionCollectItem_RemoveCollection: return "Item Collection: Removed Old Collection Item";
+ case k_EItemActionCollectItem_UpdateCollection: return "Item Collection: Created New Collection Item";
+ case k_EItemActionCollectItem_RedeemCollectionReward: return "Item Collection: Redeemed Reward";
+
+ case k_EItemActionPreviewItem_BeginPreviewPeriod: return "Item Preview: Begin Preview Period";
+ case k_EItemActionPreviewItem_EndPreviewPeriodExpired: return "Item Preview: End Preview Period by Expiration";
+ case k_EItemActionPreviewItem_EndPreviewPeriodItemBought: return "Item Preview: End Preview Period by Item Purchase";
+
+ case k_EItemActionMvM_ChallengeCompleted_RemoveTicket: return "Removed MvM Ticket";
+ case k_EItemActionMvM_ChallengeCompleted_GrantBadge: return "Granted MvM Badge";
+ case k_EItemActionMvM_ChallengeCompleted_UpdateBadgeStamps_Add: return "Added by MvM Stamp Update";
+ case k_EItemActionMvM_ChallengeCompleted_UpdateBadgeStamps_Remove: return "Removed by MvM Stamp Update";
+ case k_EItemActionMvM_ChallengeCompleted_GrantMissionCompletionLoot: return "Added by MvM Mission Completion";
+ case k_EItemActionMvM_RemoveSquadSurplusVoucher: return "Removed MvM squad surplus voucher";
+ case k_EItemActionMvM_AwardSquadSurplus_Receiver: return "Received MvM squad surplus";
+ case k_EItemActionMvM_AwardSquadSurplus_Giver: return "Generated MvM squad surplus";
+ case k_EItemActionMvM_ChallengeCompleted_GrantTourCompletionLoot: return "Added by MvM Tour Completion";
+ case k_EItemActionMvM_AwardHelpANoobBonus_Helper: return "Added by MvM Help-A-Noob";
+
+ case k_EItemActionHalloween_UpdateMerasmusLootLevel_Add: return "Updating Halloween Merasmus Loot - Adding New item";
+ case k_EItemActionHalloween_UpdateMerasmusLootLevel_Remove: return "Updating Halloween Merasmus Loot Level - Removing Old Item";
+
+ case k_EItemActionConsumeItem_Consume_ToolRemove: return "Dynamic Recipe: Consuming Input Item - Removing Old Recipe-Item";
+ case k_EItemActionConsumeItem_Consume_ToolAdd: return "Dynamic Recipe: Consuming Input Item - Adding New Recipe-Item";
+ case k_EItemActionConsumeItem_Consume_InputRemove: return "Dynamic Recipe: Consuming Input Item - Removing Input item";
+ case k_EItemActionConsumeItem_Complete_OutputAdd: return "Dynamic Recipe: Recipe Complete - Adding Output Item";
+ case k_EItemActionConsumeItem_Complete_ToolRemove: return "Dynamic Recipe: Recipe Complete - Removing Recipe-Item";
+
+ case k_EItemActionItemEaterRecharge_Add: return "Added by Item Eater Recharger";
+ case k_EItemActionItemEaterRecharge_Remove: return "Removed by Item Eater Recharger";
+
+ case k_EItemActionSupportAddOrModifyAttribute_Remove: return "Removed by Support Add/Modify Attribute";
+ case k_EItemActionSupportAddOrModifyAttribute_Add: return "Added by Support Add/Modify Attribute";
+
+ case k_EItemAction_UpdateDuckBadgeLevel_Add: return "Updating Duck Badge Level - Adding New item";
+ case k_EItemAction_UpdateDuckBadgeLevel_Remove: return "Updating Duck Badge Level - Removing Old Item";
+
+ case k_EItemActionSpyVsEngyWar_JoinedWar: return "Added by joining the Spy vs. Engy War";
+
+ case k_EItemAction_OperationPass_Add: return "Adding Operation Pass";
+
+ case k_EItemAction_QuestDrop: return "Quest Drop";
+
+ case k_EItemActionMarket_Add: return "Market - Add";
+ case k_EItemActionMarket_Remove: return "Market - Remove";
+
+ case k_EItemAction_QuestComplete_Reward: return "Added from completing a quest";
+ case k_EItemAction_QuestComplete_Remove: return "Removed from completing a quest";
+
+ case k_EItemActionStrangeCountTransfer_Add: return "Added by using a Strange Count Transfer tool";
+ case k_EItemActionStrangeCountTransfer_Remove: return "Removed by using a Strange Count Transfer tool";
+
+ case k_EItemActionCraftCollectionUpgrade_Add: return "Added by using Craft Collection Upgrade";
+ case k_EItemActionCraftCollectionUpgrade_Remove: return "Removed by using Craft Collection Upgrade";
+
+ case k_EItemActionCraftHalloweenOffering_Add: return "Added by using Craft Halloween Offering";
+ case k_EItemActionCraftHalloweenOffering_Remove: return "Removed by using Craft Halloween Offering";
+
+ case k_EItemActionRemoveItemGiftedBy_Remove: return "Removed by Gifted By Removal";
+ case k_EItemActionRemoveItemGiftedBy_Add: return "Added by Gifted By Removal";
+
+ case k_EItemActionAddParticleVerticalAttr_Remove: return "Removed by Particle Vertical Attr Add";
+ case k_EItemActionAddParticleVerticalAttr_Add: return "Added by Particle Vertical Attr Add";
+
+ case k_EItemActionAddParticleUseHeadOriginAttr_Remove: return "Removed by Particle Use Head Origin Attr Add";
+ case k_EItemActionAddParticleUseHeadOriginAttr_Add: return "Added by Particle Use Head Origin Attr Add";
+
+ case k_EItemActionRemoveItemDynamicAttr_Remove: return "Removed by Dynamic Attribute Removal";
+ case k_EItemActionRemoveItemDynamicAttr_Add: return "Added by Dynamic Attribute Removal";
+
+ case k_EItemActionCraftStatClockTradeUp_Add: return "Added through Stat Clock Trade-Up";
+ case k_EItemActionCraftStatClockTradeUp_Remove: return "Consumed through Stat Clock Trade-Up";
+ case k_EItemActionViralCompetitiveBetaPass_Drop: return "Added via Competitive Beta Invite Drop";
+
+ case k_EItemActionSupportDeleteAttribute_Remove: return "Removed by Support Delete Attribute";
+ case k_EItemActionSupportDeleteAttribute_Add: return "Added by Support Delete Attribute";
+
+ default:
+ return eMissingBehavior == kEItemAction_FriendlyNameLookup_ReturnDummyStringIfMissing
+ ? PchNameFromEItemAction( eItemAction )
+ : PchNameFromEItemActionUnsafe( eItemAction );
+ }
+
+}
+
+const char *PchLocalizedNameFromEItemAction( EItemAction eAction, CLocalizationProvider &localizationProvider )
+{
+ const char *pchAction = PchNameFromEItemAction( eAction );
+ if ( !V_strncmp( pchAction, "k_EItemAction", 13 ) )
+ {
+ CFmtStr fmtLocKey( "ItemHistory_Action_%s", &pchAction[13] );
+ locchar_t *pchLocalizedAction = localizationProvider.Find( fmtLocKey.String() );
+ if ( pchLocalizedAction != NULL )
+ {
+ return pchLocalizedAction;
+ }
+ }
+
+ if ( BIsActionDestructive( eAction ) )
+ {
+ return localizationProvider.Find( "ItemHistory_Action_GenericRemove" );
+ }
+ else if ( BIsActionCreative( eAction ) )
+ {
+ return localizationProvider.Find( "ItemHistory_Action_GenericAdd" );
+ }
+ else
+ {
+ return "Unknown";
+ }
+}
+
+ENUMSTRINGS_START( eEconItemOrigin )
+{ kEconItemOrigin_Drop, "Timed Drop" },
+{ kEconItemOrigin_Achievement, "Achievement" },
+{ kEconItemOrigin_Purchased, "Purchased" },
+{ kEconItemOrigin_Traded, "Traded" },
+{ kEconItemOrigin_Crafted, "Crafted" },
+{ kEconItemOrigin_StorePromotion, "Store Promotion" },
+{ kEconItemOrigin_Gifted, "Gifted" },
+{ kEconItemOrigin_SupportGranted, "Support Granted" },
+{ kEconItemOrigin_FoundInCrate, "Found in Crate" },
+{ kEconItemOrigin_Earned, "Earned" },
+{ kEconItemOrigin_ThirdPartyPromotion, "Third-Party Promotion" },
+{ kEconItemOrigin_GiftWrapped, "Wrapped Gift" },
+{ kEconItemOrigin_HalloweenDrop, "Halloween Drop" },
+{ kEconItemOrigin_PackageItem, "Steam Purchase" },
+{ kEconItemOrigin_Foreign, "Foreign Item" },
+{ kEconItemOrigin_CDKey, "CD Key" },
+{ kEconItemOrigin_CollectionReward, "Collection Reward" },
+{ kEconItemOrigin_PreviewItem, "Preview Item" },
+{ kEconItemOrigin_SteamWorkshopContribution, "Steam Workshop Contribution" },
+{ kEconItemOrigin_PeriodicScoreReward, "Periodic score reward" },
+{ kEconItemOrigin_MvMMissionCompletionReward, "MvM Badge completion reward" },
+{ kEconItemOrigin_MvMSquadSurplusReward, "MvM Squad surplus reward" },
+{ kEconItemOrigin_RecipeOutput, "Recipe output" },
+{ kEconItemOrigin_QuestDrop, "Quest Drop" },
+{ kEconItemOrigin_QuestLoanerItem, "Quest Loaner Item" },
+{ kEconItemOrigin_TradeUp, "Trade-Up" },
+{ kEconItemOrigin_ViralCompetitiveBetaPassSpread, "Viral Competitive Beta Pass Spread" },
+ENUMSTRINGS_END( eEconItemOrigin )
+
+#endif
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CCrateLootListWrapper::BAttemptCrateSeriesInitialization( const IEconItemInterface *pEconItem )
+{
+ Assert( m_pLootList == NULL );
+
+ // Find out what series this crate belongs to.
+ static CSchemaAttributeDefHandle pAttr_CrateSeries( "set supply crate series" );
+ if ( !pAttr_CrateSeries )
+ return false;
+
+ int iCrateSeries;
+ {
+ float fCrateSeries; // crate series ID is stored as a float internally because we hate ourselves
+ if ( !FindAttribute_UnsafeBitwiseCast<attrib_value_t>( pEconItem, pAttr_CrateSeries, &fCrateSeries ) || fCrateSeries == 0.0f )
+ return false;
+
+ iCrateSeries = fCrateSeries;
+ }
+
+ // Our index is an index into the revolving-loot-lists list. From that list we'll be able to
+ // get a loot list name, which we'll use to look up the actual contents.
+ const CEconItemSchema::RevolvingLootListDefinitionMap_t& mapRevolvingLootLists = GetItemSchema()->GetRevolvingLootLists();
+ int idx = mapRevolvingLootLists.Find( iCrateSeries );
+ if ( !mapRevolvingLootLists.IsValidIndex( idx ) )
+ return false;
+
+ const char *pszLootList = mapRevolvingLootLists.Element( idx );
+
+ // Get the loot list.
+ m_pLootList = GetItemSchema()->GetLootListByName( pszLootList );
+ m_unAuditDetailData = iCrateSeries;
+
+ return m_pLootList != NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CCrateLootListWrapper::BAttemptLootListStringInitialization( const IEconItemInterface *pEconItem )
+{
+ Assert( m_pLootList == NULL );
+
+ // Find out what series this crate belongs to.
+ static CSchemaAttributeDefHandle pAttr_LootListName( "loot list name" );
+ if ( !pAttr_LootListName )
+ return false;
+
+ CAttribute_String str;
+ if ( !pEconItem->FindAttribute( pAttr_LootListName, &str ) )
+ return false;
+
+ m_pLootList = GetItemSchema()->GetLootListByName( str.value().c_str() );
+
+ return m_pLootList != NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CCrateLootListWrapper::BAttemptLineItemInitialization( const IEconItemInterface *pEconItem )
+{
+ Assert( m_pLootList == NULL );
+
+ // Do we have at least one line item specified?
+ if ( !pEconItem->FindAttribute( CAttributeLineItemLootList::s_pAttrDef_RandomDropLineItems[0] ) )
+ return false;
+
+ m_pLootList = new CAttributeLineItemLootList( pEconItem );
+ m_bIsDynamicallyAllocatedLootList = true;
+
+ return true;
+}