You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1401 lines
48 KiB
1401 lines
48 KiB
//====== 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 "smartptr.h"
|
|
|
|
#ifdef CSTRIKE15
|
|
#include "cstrike15_gcmessages.pb.h"
|
|
#endif
|
|
|
|
#define ECON_ITEM_SET_NOT_YET_SCANNED 0xFFu
|
|
#define ECON_ITEM_SET_INVALID 0xFEu
|
|
|
|
using namespace GCSDK;
|
|
|
|
#ifdef CLIENT_DLL
|
|
#include "bannedwords.h"
|
|
#endif
|
|
|
|
// memdbgon must be the last include file in a .cpp file!!!
|
|
#include "tier0/memdbgon.h"
|
|
|
|
/*static*/ const schema_attribute_stat_bucket_t *CSchemaAttributeStats::m_pHead;
|
|
|
|
#ifndef GC_DLL
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Utility function to match two items based on their item views
|
|
//-----------------------------------------------------------------------------
|
|
bool ItemsMatch( CEconItemView *pCurItem, CEconItemView *pNewItem )
|
|
{
|
|
if ( !pNewItem || !pNewItem->IsValid() || !pCurItem || !pCurItem->IsValid() )
|
|
return false;
|
|
|
|
// If we already have an item in this slot but is not the same type, nuke it (changed classes)
|
|
// We don't need to do this for non-base items because they've already been verified above.
|
|
bool bHasNonBase = pNewItem ? pNewItem->GetItemQuality() != AE_NORMAL : false;
|
|
if ( bHasNonBase )
|
|
{
|
|
// If the item isn't the one we're supposed to have, nuke it
|
|
if ( pCurItem->GetItemID() != pNewItem->GetItemID() || pCurItem->GetItemID() == 0 || pNewItem->GetItemID() == 0 )
|
|
{
|
|
/*
|
|
Msg("Removing %s because its global index (%d) doesn't match the loadout's (%d)\n", p->GetDebugName(),
|
|
pCurItem->GetItemID(),
|
|
pNewItem->GetItemID() );
|
|
*/
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( pCurItem->GetItemQuality() != AE_NORMAL || (pCurItem->GetItemIndex() != pNewItem->GetItemIndex()) )
|
|
{
|
|
//Msg("Removing %s because it's not the right type for the class.\n", p->GetDebugName() );
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
#endif
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// 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 );
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Implementation of CS:GO optimized custom data object
|
|
//
|
|
|
|
|
|
static inline uint32 ComputeEconItemCustomDataOptimizedObjectAllocationSize( uint32 numAttributes )
|
|
{
|
|
uint32 numBytesNeeded = sizeof( CEconItem::CustomDataOptimizedObject_t ) + numAttributes*sizeof( CEconItem::attribute_t );
|
|
|
|
// Since we know that attributes are allocated in SBH we can allocate more memory then needed to allow
|
|
// for less memory copying when growing the container -
|
|
if ( sizeof( CEconItem::attribute_t ) < 8 )
|
|
numBytesNeeded = ( ( numBytesNeeded <= 128 ) ? ( ( numBytesNeeded + 7 ) / 8 ) * 8 : ( ( numBytesNeeded + 31 ) / 32 ) * 32 ); // round up to SBH boundaries
|
|
|
|
return numBytesNeeded;
|
|
}
|
|
|
|
void HelperDumpMemStatsEconItemAttributes() {}
|
|
|
|
bool Helper_IsSticker( const CEconItemDefinition * pEconItemDefinition )
|
|
{
|
|
static CSchemaItemDefHandle hItemDefCompare( "sticker" );
|
|
return pEconItemDefinition && hItemDefCompare && pEconItemDefinition->IsTool() && ( pEconItemDefinition->GetDefinitionIndex() == hItemDefCompare->GetDefinitionIndex() );
|
|
}
|
|
bool Helper_IsSpray( const CEconItemDefinition * pEconItemDefinition )
|
|
{
|
|
static CSchemaItemDefHandle hItemDefCompare( "spray" );
|
|
static CSchemaItemDefHandle hItemDefCompare2( "spraypaint" );
|
|
return pEconItemDefinition && pEconItemDefinition->IsTool() && (
|
|
( hItemDefCompare && ( pEconItemDefinition->GetDefinitionIndex() == hItemDefCompare->GetDefinitionIndex() ) )
|
|
|| ( hItemDefCompare2 && ( pEconItemDefinition->GetDefinitionIndex() == hItemDefCompare2->GetDefinitionIndex() ) )
|
|
)
|
|
;
|
|
}
|
|
bool Helper_IsGraphicTool( const CEconItemDefinition * pEconItemDefinition )
|
|
{
|
|
return Helper_IsSticker( pEconItemDefinition ) || Helper_IsSpray( pEconItemDefinition );
|
|
}
|
|
|
|
|
|
CEconItem::CustomDataOptimizedObject_t * CEconItem::CustomDataOptimizedObject_t::Alloc( uint32 numAttributes )
|
|
{
|
|
uint32 numBytesNeeded = ComputeEconItemCustomDataOptimizedObjectAllocationSize( numAttributes );
|
|
CEconItem::CustomDataOptimizedObject_t *ptr = reinterpret_cast< CEconItem::CustomDataOptimizedObject_t * >( malloc( numBytesNeeded ) );
|
|
ptr->m_equipInstanceSlot1 = INVALID_EQUIPPED_SLOT_BITPACKED;
|
|
ptr->m_numAttributes = numAttributes;
|
|
return ptr;
|
|
}
|
|
|
|
CEconItem::attribute_t * CEconItem::CustomDataOptimizedObject_t::AddAttribute( CustomDataOptimizedObject_t * &rptr )
|
|
{
|
|
uint32 numAttributesOld = rptr->m_numAttributes;
|
|
|
|
uint32 numBytesOldAllocated = ComputeEconItemCustomDataOptimizedObjectAllocationSize( numAttributesOld );
|
|
uint32 numBytesNeededNew = ComputeEconItemCustomDataOptimizedObjectAllocationSize( numAttributesOld + 1 );
|
|
|
|
if ( numBytesNeededNew > numBytesOldAllocated )
|
|
{
|
|
CEconItem::CustomDataOptimizedObject_t *ptr = CEconItem::CustomDataOptimizedObject_t::Alloc( numAttributesOld + 1 );
|
|
Q_memcpy( ptr, rptr, sizeof( CEconItem::CustomDataOptimizedObject_t ) + rptr->m_numAttributes*sizeof( CEconItem::attribute_t ) );
|
|
free( rptr );
|
|
rptr = ptr;
|
|
}
|
|
rptr->m_numAttributes = numAttributesOld + 1;
|
|
return rptr->GetAttribute( numAttributesOld );
|
|
}
|
|
|
|
void CEconItem::CustomDataOptimizedObject_t::RemoveAndFreeAttrMemory( uint32 idxAttributeInArray )
|
|
{
|
|
Assert( m_numAttributes );
|
|
Assert( idxAttributeInArray < m_numAttributes );
|
|
attribute_t *pAttr = GetAttribute( idxAttributeInArray );
|
|
CEconItem::FreeAttributeMemory( pAttr );
|
|
uint32 idxLastAttribute = m_numAttributes - 1;
|
|
if ( idxAttributeInArray != idxLastAttribute )
|
|
{
|
|
Q_memcpy( pAttr, GetAttribute( idxLastAttribute ), sizeof( CEconItem::attribute_t ) );
|
|
}
|
|
m_numAttributes = idxLastAttribute;
|
|
}
|
|
|
|
void CEconItem::CustomDataOptimizedObject_t::FreeObjectAndAttrMemory()
|
|
{
|
|
CEconItem::attribute_t *pAttr = reinterpret_cast< CEconItem::attribute_t * >( this + 1 );
|
|
CEconItem::attribute_t *pAttrEnd = pAttr + m_numAttributes;
|
|
for ( ; pAttr < pAttrEnd; ++ pAttr )
|
|
{
|
|
CEconItem::FreeAttributeMemory( pAttr );
|
|
}
|
|
free( this );
|
|
}
|
|
|
|
// --------------------------------------------------------------------------
|
|
// Purpose:
|
|
// --------------------------------------------------------------------------
|
|
CEconItem::CEconItem()
|
|
: BaseClass( ),
|
|
m_pCustomDataOptimizedObject( NULL )
|
|
{
|
|
m_ulID = 0;
|
|
m_ulOriginalID = 0;
|
|
m_iItemSet = ECON_ITEM_SET_NOT_YET_SCANNED;
|
|
#ifndef GC_DLL
|
|
m_bSOUpdateFrame = -1;
|
|
#endif
|
|
|
|
m_unAccountID = 0;
|
|
m_unInventory = 0;
|
|
m_unLevel = 0;
|
|
m_nQuality = 0;
|
|
m_unOrigin = 0;
|
|
m_nRarity = 0;
|
|
m_unFlags = 0;
|
|
m_dirtybitInUse = 0;
|
|
}
|
|
|
|
// --------------------------------------------------------------------------
|
|
// Purpose:
|
|
// --------------------------------------------------------------------------
|
|
CEconItem::~CEconItem()
|
|
{
|
|
if ( m_pCustomDataOptimizedObject )
|
|
m_pCustomDataOptimizedObject->FreeObjectAndAttrMemory();
|
|
}
|
|
|
|
// --------------------------------------------------------------------------
|
|
// 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++ )
|
|
{
|
|
attribute_t const &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 );
|
|
}
|
|
}
|
|
|
|
void CEconItem::CopyWithoutAttributesFrom( 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 )
|
|
{
|
|
Assert( false ); // this probably not what you want!! This will not wipe attributes on the item.
|
|
return;
|
|
}
|
|
|
|
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_nRarity = rhs.m_nRarity;
|
|
m_unInventory = rhs.m_unInventory;
|
|
SetQuantity( rhs.GetQuantity() );
|
|
m_unFlags = rhs.m_unFlags;
|
|
m_unOrigin = rhs.m_unOrigin;
|
|
|
|
m_dirtybitInUse = rhs.m_dirtybitInUse;
|
|
|
|
m_iItemSet = rhs.m_iItemSet;
|
|
if ( m_pCustomDataOptimizedObject )
|
|
{
|
|
m_pCustomDataOptimizedObject->FreeObjectAndAttrMemory();
|
|
m_pCustomDataOptimizedObject = NULL;
|
|
}
|
|
}
|
|
|
|
// --------------------------------------------------------------------------
|
|
// 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;
|
|
|
|
// Copy all plain data
|
|
CopyWithoutAttributesFrom( rhs );
|
|
|
|
//
|
|
// see -- CopyAttributesFrom( rhs );
|
|
//
|
|
// copied for more efficient memory management
|
|
//
|
|
if ( rhs.m_pCustomDataOptimizedObject && rhs.m_pCustomDataOptimizedObject->m_numAttributes )
|
|
{
|
|
uint32 numAttributes = rhs.m_pCustomDataOptimizedObject->m_numAttributes;
|
|
m_pCustomDataOptimizedObject = CustomDataOptimizedObject_t::Alloc( numAttributes );
|
|
for ( uint32 i = 0; i < numAttributes; ++ i )
|
|
{
|
|
attribute_t const &attr = * rhs.m_pCustomDataOptimizedObject->GetAttribute( 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 );
|
|
|
|
// Make an efficient copy of the attribute at the end of the array
|
|
attribute_t *pEconAttribCopy = m_pCustomDataOptimizedObject->GetAttribute( i );
|
|
pEconAttribCopy->m_unDefinitionIndex = pAttrDef->GetDefinitionIndex();
|
|
pAttrType->InitializeNewEconAttributeValue( &pEconAttribCopy->m_value );
|
|
pAttrType->LoadByteStreamToEconAttributeValue( this, pAttrDef, sBytes );
|
|
}
|
|
}
|
|
|
|
// Transfer equip state as well
|
|
if ( rhs.m_pCustomDataOptimizedObject )
|
|
{
|
|
// Attributes transfer can allocate optimized custom data, but if we had no attributes then allocate it here
|
|
if ( !m_pCustomDataOptimizedObject )
|
|
m_pCustomDataOptimizedObject = CustomDataOptimizedObject_t::Alloc( 0 );
|
|
|
|
// Transfer equip state values as well
|
|
m_pCustomDataOptimizedObject->m_equipInstanceSlot1 = rhs.m_pCustomDataOptimizedObject->m_equipInstanceSlot1;
|
|
m_pCustomDataOptimizedObject->m_equipInstanceClass1 = rhs.m_pCustomDataOptimizedObject->m_equipInstanceClass1;
|
|
m_pCustomDataOptimizedObject->m_equipInstanceClass2Bit = rhs.m_pCustomDataOptimizedObject->m_equipInstanceClass2Bit;
|
|
}
|
|
|
|
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 != 0 && ulOldID != ulID && m_ulOriginalID == 0 && ulID != INVALID_ITEM_ID && ulOldID != INVALID_ITEM_ID )
|
|
{
|
|
SetOriginalID( ulOldID );
|
|
}
|
|
|
|
}
|
|
|
|
// --------------------------------------------------------------------------
|
|
// Purpose:
|
|
// --------------------------------------------------------------------------
|
|
itemid_t CEconItem::GetOriginalID() const
|
|
{
|
|
return m_ulOriginalID ? m_ulOriginalID : m_ulID;
|
|
}
|
|
|
|
// --------------------------------------------------------------------------
|
|
// Purpose:
|
|
// --------------------------------------------------------------------------
|
|
void CEconItem::SetOriginalID( itemid_t ulOriginalID )
|
|
{
|
|
if ( ulOriginalID != m_ulID )
|
|
m_ulOriginalID = ulOriginalID;
|
|
}
|
|
|
|
int32 CEconItem::GetRarity() const
|
|
{
|
|
int nDefRarity = GetItemDefinition() ? GetItemDefinition()->GetRarity() : 1;
|
|
bool bIsTool = GetItemDefinition() ? GetItemDefinition()->IsTool() : false;
|
|
|
|
const CEconItemRarityDefinition *pImmortal = GetItemSchema()->GetRarityDefinitionByName( "immortal" );
|
|
if ( nDefRarity == pImmortal->GetDBValue() )
|
|
{
|
|
// Items that had their definition marked as immortal just use that!
|
|
return nDefRarity;
|
|
}
|
|
|
|
// Check if the item has its rarity set in the database
|
|
if ( m_nRarity != k_unItemRarity_Any )
|
|
{
|
|
// HACK: The default value for rarity used to be 0 and some coins and passes were created that way,
|
|
// but since their rarity should have been k_unItemRarity_Any they actually need to fall back to the definition rarity
|
|
if ( !( m_nRarity == 0 && ( nDefRarity >= 6 || bIsTool ) ) )
|
|
{
|
|
return m_nRarity;
|
|
}
|
|
}
|
|
|
|
return nDefRarity;
|
|
}
|
|
|
|
// --------------------------------------------------------------------------
|
|
// Purpose:
|
|
// --------------------------------------------------------------------------
|
|
uint16 CEconItem::GetQuantity() const
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
// --------------------------------------------------------------------------
|
|
// Purpose:
|
|
// --------------------------------------------------------------------------
|
|
void CEconItem::SetQuantity( uint16 unQuantity )
|
|
{
|
|
Assert( unQuantity <= 1 );
|
|
}
|
|
|
|
// --------------------------------------------------------------------------
|
|
// Purpose:
|
|
// --------------------------------------------------------------------------
|
|
void CEconItem::InitAttributesDroppedFromListEntry( item_list_entry_t const *pEntryInfo )
|
|
{
|
|
const CEconItemDefinition *pItemDef = GetItemDefinition();
|
|
|
|
// Set the paint kit
|
|
if ( pEntryInfo )
|
|
{
|
|
extern bool Helper_IsGraphicTool( const CEconItemDefinition * pEconItemDefinition );
|
|
if ( pEntryInfo->m_nStickerKit && pItemDef && Helper_IsGraphicTool( pItemDef ) )
|
|
{
|
|
const CStickerKit *pStickerKit = GetItemSchema()->GetStickerKitDefinition( pEntryInfo->m_nStickerKit );
|
|
if ( pStickerKit )
|
|
{
|
|
static CSchemaAttributeDefHandle pAttr_StickerKit( "sticker slot 0 id" );
|
|
if ( pAttr_StickerKit )
|
|
{
|
|
SetDynamicAttributeValue( pAttr_StickerKit, pEntryInfo->m_nStickerKit );
|
|
}
|
|
|
|
SetRarity( EconRarity_CombinedItemAndPaintRarity( pItemDef->GetRarity(), pStickerKit->nRarity ) );
|
|
}
|
|
}
|
|
else if ( pEntryInfo->m_nPaintKit )
|
|
{
|
|
const CPaintKit *pPaintKit = GetItemSchema()->GetPaintKitDefinition( pEntryInfo->m_nPaintKit );
|
|
if ( pPaintKit )
|
|
{
|
|
static CSchemaAttributeDefHandle pAttr_PaintKit( "set item texture prefab" );
|
|
if ( pAttr_PaintKit )
|
|
{
|
|
AddOrSetCustomAttribute( pAttr_PaintKit->GetDefinitionIndex(), pEntryInfo->m_nPaintKit );
|
|
}
|
|
|
|
static CSchemaAttributeDefHandle pAttr_PaintKitSeed( "set item texture seed" );
|
|
if ( pAttr_PaintKitSeed && ( pEntryInfo->m_nPaintKitSeed >= -1 ) )
|
|
{
|
|
int nSeed = pEntryInfo->m_nPaintKitSeed;
|
|
if ( nSeed < 0 )
|
|
{
|
|
nSeed = RandomInt( 0, 1000 );
|
|
}
|
|
|
|
|
|
AddOrSetCustomAttribute( pAttr_PaintKitSeed->GetDefinitionIndex(), nSeed );
|
|
}
|
|
|
|
static CSchemaAttributeDefHandle pAttr_PaintKitWear( "set item texture wear" );
|
|
if ( pAttr_PaintKitWear && ( pEntryInfo->m_flPaintKitWear >= -1.1 ) )
|
|
{
|
|
float flWear = pEntryInfo->m_flPaintKitWear;
|
|
if ( flWear < 0 )
|
|
{
|
|
flWear = RandomFloat();
|
|
}
|
|
AddOrSetCustomAttribute( pAttr_PaintKitWear->GetDefinitionIndex(),
|
|
RemapValClamped( flWear, 0.0f, 1.0f, pPaintKit->flWearRemapMin, pPaintKit->flWearRemapMax ) );
|
|
}
|
|
|
|
SetRarity( EconRarity_CombinedItemAndPaintRarity( GetItemDefinition()->GetRarity(), pPaintKit->nRarity ) );
|
|
}
|
|
}
|
|
else if ( pEntryInfo->m_nMusicKit )
|
|
{
|
|
const CEconMusicDefinition *pMusicDef = GetItemSchema()->GetMusicDefinition( pEntryInfo->m_nMusicKit );
|
|
if ( pMusicDef )
|
|
{
|
|
static CSchemaAttributeDefHandle pAttr_Music( "music id" );
|
|
if ( pAttr_Music )
|
|
{
|
|
SetDynamicAttributeValue( pAttr_Music, pEntryInfo->m_nMusicKit );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( pItemDef )
|
|
{
|
|
int nDefaultDropQuality = pItemDef->GetDefaultDropQuality();
|
|
if ( nDefaultDropQuality != k_unItemQuality_Any )
|
|
{
|
|
SetQuality( nDefaultDropQuality );
|
|
}
|
|
}
|
|
}
|
|
|
|
// --------------------------------------------------------------------------
|
|
// 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 ) )
|
|
{
|
|
#ifdef CLIENT_DLL
|
|
if ( pszStrContents && *pszStrContents )
|
|
g_BannedWords.CensorBannedWordsInplace( const_cast< char * >( pszStrContents ) );
|
|
#endif
|
|
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
|
|
{
|
|
return m_pCustomDataOptimizedObject && ( m_pCustomDataOptimizedObject->m_equipInstanceSlot1 != INVALID_EQUIPPED_SLOT_BITPACKED );
|
|
}
|
|
|
|
// --------------------------------------------------------------------------
|
|
// Purpose:
|
|
// --------------------------------------------------------------------------
|
|
bool CEconItem::IsEquippedForClass( equipped_class_t unClass ) const
|
|
{
|
|
return m_pCustomDataOptimizedObject && ( m_pCustomDataOptimizedObject->m_equipInstanceSlot1 != INVALID_EQUIPPED_SLOT_BITPACKED ) &&
|
|
( // CS:GO optimized test - equip class is 0 or 2/3
|
|
( unClass == m_pCustomDataOptimizedObject->m_equipInstanceClass1 ) ||
|
|
( ( unClass == 3 ) && ( m_pCustomDataOptimizedObject->m_equipInstanceClass1 == 2 ) && ( m_pCustomDataOptimizedObject->m_equipInstanceClass2Bit != 0 ) )
|
|
);
|
|
}
|
|
|
|
// --------------------------------------------------------------------------
|
|
// Purpose:
|
|
// --------------------------------------------------------------------------
|
|
equipped_slot_t CEconItem::GetEquippedPositionForClass( equipped_class_t unClass ) const
|
|
{
|
|
return IsEquippedForClass( unClass ) ? m_pCustomDataOptimizedObject->m_equipInstanceSlot1 : INVALID_EQUIPPED_SLOT;
|
|
}
|
|
|
|
// --------------------------------------------------------------------------
|
|
// Purpose:
|
|
// --------------------------------------------------------------------------
|
|
void CEconItem::UpdateEquippedState( EquippedInstance_t equipInstance )
|
|
{
|
|
// If it's invalid we need to remove it
|
|
if ( equipInstance.m_unEquippedSlot == INVALID_EQUIPPED_SLOT )
|
|
{
|
|
if ( m_pCustomDataOptimizedObject && ( m_pCustomDataOptimizedObject->m_equipInstanceSlot1 != INVALID_EQUIPPED_SLOT_BITPACKED ) )
|
|
{
|
|
if ( equipInstance.m_unEquippedClass == m_pCustomDataOptimizedObject->m_equipInstanceClass1 )
|
|
{
|
|
if ( m_pCustomDataOptimizedObject->m_equipInstanceClass2Bit )
|
|
{
|
|
// Move the 2nd class down
|
|
if ( equipInstance.m_unEquippedClass == 2 )
|
|
{ // leave it equipped for class 3
|
|
m_pCustomDataOptimizedObject->m_equipInstanceClass1 = 3;
|
|
m_pCustomDataOptimizedObject->m_equipInstanceClass2Bit = 0;
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
Assert( equipInstance.m_unEquippedClass == 2 ); // weird case, claims to be equipped for both classes, but first class is invalid!
|
|
}
|
|
}
|
|
|
|
// Fully unequip
|
|
m_pCustomDataOptimizedObject->m_equipInstanceSlot1 = INVALID_EQUIPPED_SLOT_BITPACKED;
|
|
if ( !m_pCustomDataOptimizedObject->m_numAttributes )
|
|
{
|
|
m_pCustomDataOptimizedObject->FreeObjectAndAttrMemory();
|
|
m_pCustomDataOptimizedObject = NULL;
|
|
}
|
|
return;
|
|
}
|
|
else if ( ( equipInstance.m_unEquippedClass == 3 ) && ( m_pCustomDataOptimizedObject->m_equipInstanceClass1 == 2 ) && ( m_pCustomDataOptimizedObject->m_equipInstanceClass2Bit != 0 ) )
|
|
{
|
|
// was equipped for both, unequipping 3rd class
|
|
m_pCustomDataOptimizedObject->m_equipInstanceClass2Bit = 0;
|
|
return;
|
|
}
|
|
}
|
|
// item was not equipped to begin with
|
|
return;
|
|
}
|
|
|
|
// If this item is already equipped
|
|
if ( m_pCustomDataOptimizedObject && ( m_pCustomDataOptimizedObject->m_equipInstanceSlot1 != INVALID_EQUIPPED_SLOT_BITPACKED ) )
|
|
{
|
|
m_pCustomDataOptimizedObject->m_equipInstanceSlot1 = equipInstance.m_unEquippedSlot;
|
|
switch ( equipInstance.m_unEquippedClass )
|
|
{
|
|
case 0: // non-team item
|
|
m_pCustomDataOptimizedObject->m_equipInstanceClass1 = 0;
|
|
m_pCustomDataOptimizedObject->m_equipInstanceClass2Bit = 0;
|
|
return;
|
|
case 2:
|
|
if ( m_pCustomDataOptimizedObject->m_equipInstanceClass1 == 3 )
|
|
{
|
|
m_pCustomDataOptimizedObject->m_equipInstanceClass1 = 2;
|
|
m_pCustomDataOptimizedObject->m_equipInstanceClass2Bit = 1;
|
|
}
|
|
else
|
|
{
|
|
m_pCustomDataOptimizedObject->m_equipInstanceClass1 = 2;
|
|
m_pCustomDataOptimizedObject->m_equipInstanceClass2Bit = 0;
|
|
}
|
|
return;
|
|
case 3:
|
|
if ( m_pCustomDataOptimizedObject->m_equipInstanceClass1 == 2 )
|
|
{
|
|
m_pCustomDataOptimizedObject->m_equipInstanceClass2Bit = 1;
|
|
}
|
|
else
|
|
{
|
|
m_pCustomDataOptimizedObject->m_equipInstanceClass1 = 3;
|
|
m_pCustomDataOptimizedObject->m_equipInstanceClass2Bit = 0;
|
|
}
|
|
return;
|
|
default:
|
|
Assert( false );
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Otherwise this item has not been equipped yet
|
|
if ( !m_pCustomDataOptimizedObject )
|
|
{
|
|
m_pCustomDataOptimizedObject = CustomDataOptimizedObject_t::Alloc( 0 );
|
|
}
|
|
|
|
m_pCustomDataOptimizedObject->m_equipInstanceSlot1 = equipInstance.m_unEquippedSlot;
|
|
m_pCustomDataOptimizedObject->m_equipInstanceClass1 = equipInstance.m_unEquippedClass;
|
|
m_pCustomDataOptimizedObject->m_equipInstanceClass2Bit = 0;
|
|
}
|
|
|
|
// --------------------------------------------------------------------------
|
|
// 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 );
|
|
}
|
|
|
|
int CEconItem::GetItemSetIndex() const
|
|
{
|
|
// If we already cached it, use that one
|
|
if ( m_iItemSet != ECON_ITEM_SET_NOT_YET_SCANNED )
|
|
return ( ( m_iItemSet == ECON_ITEM_SET_INVALID ) ? -1 : m_iItemSet );
|
|
|
|
// Mark it as cached and invalid
|
|
m_iItemSet = ECON_ITEM_SET_INVALID;
|
|
|
|
const CEconItemDefinition *pItemDef = GetItemSchema()->GetItemDefinition( GetDefinitionIndex() );
|
|
if ( !pItemDef )
|
|
return ( ( m_iItemSet == ECON_ITEM_SET_INVALID ) ? -1 : m_iItemSet );
|
|
|
|
// They might not have any possible sets
|
|
const CUtlVector< int > &itemSets = pItemDef->GetItemSets();
|
|
if ( itemSets.Count() == 0 )
|
|
return ( ( m_iItemSet == ECON_ITEM_SET_INVALID ) ? -1 : m_iItemSet );
|
|
|
|
// Paint kit specified, so we need to match it
|
|
int nPaintKit = GetCustomPaintKitIndex();
|
|
|
|
FOR_EACH_VEC( itemSets, nItemSet )
|
|
{
|
|
int nItemSetIndex = itemSets[ nItemSet ];
|
|
|
|
const CEconItemSetDefinition *pItemSetDef = GetItemSchema()->GetItemSetByIndex( nItemSetIndex );
|
|
if ( !pItemSetDef )
|
|
continue;
|
|
|
|
FOR_EACH_VEC( pItemSetDef->m_ItemEntries, i )
|
|
{
|
|
if ( pItemSetDef->m_ItemEntries[ i ].m_nItemDef != pItemDef->GetDefinitionIndex() ||
|
|
pItemSetDef->m_ItemEntries[ i ].m_nPaintKit != nPaintKit )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
m_iItemSet = nItemSetIndex;
|
|
return ( ( m_iItemSet == ECON_ITEM_SET_INVALID ) ? -1 : m_iItemSet );
|
|
}
|
|
}
|
|
|
|
return ( ( m_iItemSet == ECON_ITEM_SET_INVALID ) ? -1 : m_iItemSet );
|
|
}
|
|
|
|
// --------------------------------------------------------------------------
|
|
// Purpose:
|
|
// --------------------------------------------------------------------------
|
|
bool CEconItem::GetInUse() const
|
|
{
|
|
return m_dirtybitInUse != 0;
|
|
}
|
|
|
|
// --------------------------------------------------------------------------
|
|
// Purpose:
|
|
// --------------------------------------------------------------------------
|
|
void CEconItem::SetInUse( bool bInUse )
|
|
{
|
|
m_dirtybitInUse = bInUse ? 1 : 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_dirtybitInUse
|
|
&& IEconItemInterface::IsTradable();
|
|
}
|
|
|
|
// --------------------------------------------------------------------------
|
|
// Purpose:
|
|
// --------------------------------------------------------------------------
|
|
bool CEconItem::IsMarketable() const
|
|
{
|
|
return !m_dirtybitInUse
|
|
&& IEconItemInterface::IsMarketable();
|
|
}
|
|
|
|
void CEconItem::IterateAttributes( IEconItemAttributeIterator *pIterator ) const
|
|
{
|
|
Assert( pIterator );
|
|
|
|
// custom attributes?
|
|
for ( int i = 0; i < GetDynamicAttributeCountInternal(); i++ )
|
|
{
|
|
attribute_t const &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:
|
|
// --------------------------------------------------------------------------
|
|
bool CEconItem::IsUsableInCrafting() const
|
|
{
|
|
return !m_dirtybitInUse
|
|
&& IEconItemInterface::IsUsableInCrafting();
|
|
}
|
|
|
|
// --------------------------------------------------------------------------
|
|
// Purpose:
|
|
// --------------------------------------------------------------------------
|
|
int CEconItem::GetDynamicAttributeCountInternal() const
|
|
{
|
|
return m_pCustomDataOptimizedObject ? m_pCustomDataOptimizedObject->m_numAttributes : 0;
|
|
}
|
|
|
|
// --------------------------------------------------------------------------
|
|
// Purpose:
|
|
// --------------------------------------------------------------------------
|
|
const CEconItem::attribute_t & CEconItem::GetDynamicAttributeInternal( int iAttrIndexIntoArray ) const
|
|
{
|
|
Assert( iAttrIndexIntoArray >= 0 );
|
|
Assert( iAttrIndexIntoArray < GetDynamicAttributeCountInternal() );
|
|
|
|
return *m_pCustomDataOptimizedObject->GetAttribute( iAttrIndexIntoArray );
|
|
}
|
|
|
|
// --------------------------------------------------------------------------
|
|
// Purpose:
|
|
// --------------------------------------------------------------------------
|
|
CEconItem::attribute_t *CEconItem::FindDynamicAttributeInternal( const CEconItemAttributeDefinition *pAttrDef )
|
|
{
|
|
Assert( pAttrDef );
|
|
|
|
if ( m_pCustomDataOptimizedObject )
|
|
{
|
|
attribute_t *pAttr = m_pCustomDataOptimizedObject->GetAttribute( 0 );
|
|
attribute_t *pAttrEnd = pAttr + m_pCustomDataOptimizedObject->m_numAttributes;
|
|
for ( ; pAttr < pAttrEnd; ++pAttr )
|
|
{
|
|
if ( pAttr->m_unDefinitionIndex == pAttrDef->GetDefinitionIndex() )
|
|
return pAttr;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
// --------------------------------------------------------------------------
|
|
// Purpose:
|
|
// --------------------------------------------------------------------------
|
|
void CEconItem::AddCustomAttribute( uint16 usDefinitionIndex, float flValue )
|
|
{
|
|
attribute_t &attrib = AddDynamicAttributeInternal();
|
|
attrib.m_unDefinitionIndex = usDefinitionIndex;
|
|
attrib.m_value.asFloat = flValue;
|
|
}
|
|
|
|
// --------------------------------------------------------------------------
|
|
// Purpose:
|
|
// --------------------------------------------------------------------------
|
|
void CEconItem::AddOrSetCustomAttribute( uint16 usDefinitionIndex, float flValue )
|
|
{
|
|
attribute_t *pAttrib = FindDynamicAttributeInternal( GetItemSchema()->GetAttributeDefinition( usDefinitionIndex ) );
|
|
if ( NULL != pAttrib )
|
|
{
|
|
pAttrib->m_value.asFloat = flValue;
|
|
return;
|
|
}
|
|
|
|
AddCustomAttribute( usDefinitionIndex, flValue );
|
|
}
|
|
|
|
// --------------------------------------------------------------------------
|
|
// Purpose:
|
|
// --------------------------------------------------------------------------
|
|
CEconItem::attribute_t &CEconItem::AddDynamicAttributeInternal()
|
|
{
|
|
if ( !m_pCustomDataOptimizedObject )
|
|
return * ( m_pCustomDataOptimizedObject = CustomDataOptimizedObject_t::Alloc( 1 ) )->GetAttribute( 0 );
|
|
else
|
|
return * CustomDataOptimizedObject_t::AddAttribute( m_pCustomDataOptimizedObject );
|
|
}
|
|
|
|
// --------------------------------------------------------------------------
|
|
// Purpose:
|
|
// --------------------------------------------------------------------------
|
|
void CEconItem::RemoveDynamicAttribute( const CEconItemAttributeDefinition *pAttrDef )
|
|
{
|
|
Assert( pAttrDef );
|
|
Assert( pAttrDef->GetDefinitionIndex() != INVALID_ITEM_DEF_INDEX );
|
|
|
|
if ( m_pCustomDataOptimizedObject )
|
|
{
|
|
attribute_t *pAttr = m_pCustomDataOptimizedObject->GetAttribute( 0 );
|
|
attribute_t *pAttrStart = pAttr;
|
|
attribute_t *pAttrEnd = pAttr + m_pCustomDataOptimizedObject->m_numAttributes;
|
|
for ( ; pAttr < pAttrEnd; ++pAttr )
|
|
{
|
|
if ( pAttr->m_unDefinitionIndex == pAttrDef->GetDefinitionIndex() )
|
|
{
|
|
m_pCustomDataOptimizedObject->RemoveAndFreeAttrMemory( pAttr - pAttrStart );
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// --------------------------------------------------------------------------
|
|
// Purpose:
|
|
// --------------------------------------------------------------------------
|
|
/*static*/ void CEconItem::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: This item has been traded. Give it an opportunity to update any internal
|
|
// properties in response.
|
|
// --------------------------------------------------------------------------
|
|
void CEconItem::OnTransferredOwnership()
|
|
{
|
|
/** Removed for partner depot **/
|
|
}
|
|
|
|
// --------------------------------------------------------------------------
|
|
// 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;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
// 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.
|
|
//----------------------------------------------------------------------------
|
|
static CSOEconItem g_msgEconItem;
|
|
bool CEconItem::BAddToMessage( std::string *pBuffer ) const
|
|
{
|
|
VPROF_BUDGET( "CEconItem::BAddToMessage::std::string", VPROF_BUDGETGROUP_STEAM );
|
|
|
|
//this function is called A LOT, therefore we use a static item to avoid a lot of re-allocations. However, this means that
|
|
//this should never be re-entrant
|
|
g_msgEconItem.Clear();
|
|
SerializeToProtoBufItem( g_msgEconItem );
|
|
return g_msgEconItem.SerializeToString( pBuffer );
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
// 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 );
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
// 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: 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, const CEconItemSchema &pschema, CUtlVector<CUtlString> *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 CEconItemQualityDefinition *pQuality = NULL;
|
|
const CEconItemDefinition *pItemDef = NULL;
|
|
|
|
const char *pchDefName = pKVItem->GetString( "DefName" );
|
|
pItemDef = pschema.GetItemDefinitionByName( pchDefName );
|
|
if( !pItemDef )
|
|
{
|
|
if ( pVecErrors )
|
|
{
|
|
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() );
|
|
|
|
uint8 unValueGet = 0;
|
|
|
|
const char *pchQualityName = pKVItem->GetString( "QualityName" );
|
|
if( !pchQualityName || ! *pchQualityName )
|
|
{
|
|
// set the default quality for the definition
|
|
if( pItemDef->GetQuality() == k_unItemQuality_Any )
|
|
{
|
|
if ( NULL == pVecErrors )
|
|
return false;
|
|
pVecErrors->AddToTail( CUtlString( CFmtStr( "Quality was not specified and this item def doesn't define one either." ) ) );
|
|
}
|
|
else
|
|
{
|
|
SetQuality( pItemDef->GetQuality() );
|
|
}
|
|
}
|
|
else if ( !pschema.BGetItemQualityFromName( pchQualityName, &unValueGet ) || (( m_nQuality = unValueGet ),true) || k_unItemQuality_Any == GetQuality() )
|
|
{
|
|
if ( NULL == pVecErrors )
|
|
return false;
|
|
pVecErrors->AddToTail( CUtlString( CFmtStr( "Quality \"%s\" not found", pchQualityName ) ) );
|
|
}
|
|
else
|
|
{
|
|
pQuality = pschema.GetQualityDefinition( GetQuality() );
|
|
}
|
|
|
|
const char *pchRarityName = pKVItem->GetString( "RarityName" );
|
|
if( !pchRarityName || ! *pchRarityName )
|
|
{
|
|
// set the default quality for the definition
|
|
if( pItemDef->GetRarity() == k_unItemRarity_Any )
|
|
{
|
|
if ( NULL == pVecErrors )
|
|
return false;
|
|
pVecErrors->AddToTail( CUtlString( CFmtStr( "Rarity was not specified and this item def doesn't define one either." ) ) );
|
|
}
|
|
else
|
|
{
|
|
SetRarity( pItemDef->GetRarity() );
|
|
}
|
|
}
|
|
else if ( !pschema.BGetItemRarityFromName( pchRarityName, &unValueGet ) || (( m_nRarity = unValueGet ),true) || k_unItemRarity_Any == GetRarity() )
|
|
{
|
|
if ( NULL == pVecErrors )
|
|
return false;
|
|
pVecErrors->AddToTail( CUtlString( CFmtStr( "Rarity \"%s\" not found", pchRarityName ) ) );
|
|
}
|
|
|
|
// 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:
|
|
// --------------------------------------------------------------------------
|
|
void CEconItem::SerializeToProtoBufItem( CSOEconItem &msgItem ) const
|
|
{
|
|
msgItem.set_id( m_ulID );
|
|
msgItem.set_account_id( m_unAccountID );
|
|
msgItem.set_def_index( m_unDefIndex );
|
|
msgItem.set_level( m_unLevel );
|
|
msgItem.set_quality( m_nQuality );
|
|
msgItem.set_rarity( m_nRarity );
|
|
msgItem.set_inventory( m_unInventory );
|
|
msgItem.set_quantity( GetQuantity() );
|
|
msgItem.set_flags( m_unFlags );
|
|
msgItem.set_origin( m_unOrigin );
|
|
msgItem.set_in_use( m_dirtybitInUse );
|
|
|
|
|
|
if ( m_pCustomDataOptimizedObject )
|
|
{
|
|
//
|
|
// Write our custom attributes
|
|
//
|
|
attribute_t const *pAttr = m_pCustomDataOptimizedObject->GetAttribute( 0 );
|
|
attribute_t const *pAttrEnd = pAttr + m_pCustomDataOptimizedObject->m_numAttributes;
|
|
for ( ; pAttr < pAttrEnd; ++pAttr )
|
|
{
|
|
const attribute_t & attr = *pAttr;
|
|
|
|
// 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 );
|
|
|
|
pAttrType->ConvertEconAttributeValueToByteStream( attr.m_value, pMsgAttr->mutable_value_bytes() );
|
|
}
|
|
|
|
//
|
|
// Write equipped instances
|
|
//
|
|
if ( m_pCustomDataOptimizedObject->m_equipInstanceSlot1 != INVALID_EQUIPPED_SLOT_BITPACKED )
|
|
{
|
|
CSOEconItemEquipped *pMsgEquipped = msgItem.add_equipped_state();
|
|
pMsgEquipped->set_new_class( m_pCustomDataOptimizedObject->m_equipInstanceClass1 );
|
|
pMsgEquipped->set_new_slot( m_pCustomDataOptimizedObject->m_equipInstanceSlot1 );
|
|
|
|
if ( m_pCustomDataOptimizedObject->m_equipInstanceClass2Bit && ( m_pCustomDataOptimizedObject->m_equipInstanceClass1 == 2 ) )
|
|
{
|
|
pMsgEquipped = msgItem.add_equipped_state();
|
|
pMsgEquipped->set_new_class( 3 );
|
|
pMsgEquipped->set_new_slot( m_pCustomDataOptimizedObject->m_equipInstanceSlot1 );
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifndef GC_DLL // no need to send original IDs outside of GC
|
|
if ( m_ulID != GetOriginalID() )
|
|
msgItem.set_original_id( GetOriginalID() );
|
|
#endif
|
|
|
|
#if 0
|
|
// Names and descriptions are now attributes, no need to duplicate them here (perf)
|
|
const char *pszCustomName = GetCustomName();
|
|
if ( pszCustomName )
|
|
{
|
|
msgItem.set_custom_name( pszCustomName );
|
|
}
|
|
|
|
const char *pszCustomDesc = GetCustomDesc();
|
|
if ( pszCustomDesc )
|
|
{
|
|
msgItem.set_custom_desc( pszCustomDesc );
|
|
}
|
|
#endif
|
|
}
|
|
|
|
// --------------------------------------------------------------------------
|
|
// Purpose:
|
|
// --------------------------------------------------------------------------
|
|
void CEconItem::DeserializeFromProtoBufItem( const CSOEconItem &msgItem )
|
|
{
|
|
VPROF_BUDGET( "CEconItem::DeserializeFromProtoBufItem()", VPROF_BUDGETGROUP_STEAM );
|
|
|
|
// Start by resetting
|
|
if ( m_pCustomDataOptimizedObject )
|
|
{
|
|
m_pCustomDataOptimizedObject->FreeObjectAndAttrMemory();
|
|
m_pCustomDataOptimizedObject = NULL;
|
|
}
|
|
|
|
// 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_nQuality = msgItem.quality();
|
|
m_nRarity = msgItem.rarity();
|
|
m_unInventory = msgItem.inventory();
|
|
m_unFlags = msgItem.flags();
|
|
m_unOrigin = msgItem.origin();
|
|
|
|
//the default value of these fields will be switched from zero to 1 since almost all items have 1 for both of these. However, to
|
|
//ensure a smooth transition, we ensure that the values are always 1 (zero is invalid for these). This can be removed after a few
|
|
//versions and the GC/client/server are all in sync
|
|
m_unLevel = MAX( 1, msgItem.level() );
|
|
SetQuantity( MAX( 1, msgItem.quantity() ) );
|
|
|
|
m_dirtybitInUse = 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.
|
|
Assert( !msgItem.has_interior_item() );
|
|
|
|
// update equipped state
|
|
for ( int i = 0; i < msgItem.equipped_state_size(); i++ )
|
|
{
|
|
UpdateEquippedState( msgItem.equipped_state(i).new_class(), msgItem.equipped_state(i).new_slot() );
|
|
}
|
|
}
|
|
|
|
|
|
int CEconItem::GetEquippedInstanceArray( EquippedInstanceArray_t &equips ) const
|
|
{
|
|
//build up a list of the equip instances we need to copy over, since as we equip new items into that slot, it will unequip these other items
|
|
equips.RemoveAll();
|
|
|
|
if ( m_pCustomDataOptimizedObject && ( m_pCustomDataOptimizedObject->m_equipInstanceSlot1 != INVALID_EQUIPPED_SLOT_BITPACKED ) )
|
|
{
|
|
EquippedInstance_t instEquip( m_pCustomDataOptimizedObject->m_equipInstanceClass1, m_pCustomDataOptimizedObject->m_equipInstanceSlot1 );
|
|
equips.AddToTail( instEquip );
|
|
|
|
if ( m_pCustomDataOptimizedObject->m_equipInstanceClass2Bit && ( m_pCustomDataOptimizedObject->m_equipInstanceClass1 == 2 ) )
|
|
{
|
|
instEquip.m_unEquippedClass = 3;
|
|
equips.AddToTail( instEquip );
|
|
}
|
|
}
|
|
|
|
return equips.Count();
|
|
}
|