//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: CEconItem, a shared object for econ items // //============================================================================= #ifndef ECONITEMINTERFACE_H #define ECONITEMINTERFACE_H #ifdef _WIN32 #pragma once #endif #include "game_item_schema.h" // needed for GameItemDefinition_t class CAttribute_String; class CAttribute_DynamicRecipeComponent; class CAttribute_ItemSlotCriteria; class CAttribute_WorldItemPlacement; class IMaterial; //----------------------------------------------------------------------------- // Purpose: Template helper classes for dealing with types. //----------------------------------------------------------------------------- // StripConstIfPresent will take an input type T and "return" via ResultType: // // - T: T // - const T: T // // This is used to prevent having to have different specializations for "T" versus // "const T" when checking for equivalent template type arguments, etc. template < typename T > struct StripConstIfPresent { typedef T ResultType; }; template < typename T > struct StripConstIfPresent { typedef T ResultType; }; // AreTypesIdentical takes two input types and "returns" via kValue whether the // types are exactly equal. This is intended for checking type equivalence at compile-time // in ways that template specializations for functions/classes may not be ideal for. // // We use it in the attribute code to guarantee that we're only doing The Old, Scary Path // when dealing with attributes of The Old, Scary Type. template < typename T, typename U > struct AreTypesIdentical { enum { kValue = false }; }; template < typename T > struct AreTypesIdentical { enum { kValue = true }; }; // IsPointerType takes one input and "returns" via kValue whether the type is a pointer // type in any way, const, volatile, whatever. template < typename T > struct IsPointerType { enum { kValue = false }; }; template < typename T > struct IsPointerType { enum { kValue = true }; }; // IsValidAttributeValueTypeImpl is a hand-made specialization for what types we want // to consider valid attribute data types. This is used as a sanity check to make sure we // don't pass in completely arbitrary types to things like FindAttribute(). (Doing so // would cause an assert at runtime, but it seems like getting compile-time asserts is // advantageous, and probably worth paying the small cost of adding to this list whenever // a new attribute type is added.) template < typename T> struct IsValidAttributeValueTypeImpl { enum { kValue = false }; }; template < > struct IsValidAttributeValueTypeImpl { enum { kValue = true }; }; template < > struct IsValidAttributeValueTypeImpl { enum { kValue = true }; }; template < > struct IsValidAttributeValueTypeImpl { enum { kValue = true }; }; template < > struct IsValidAttributeValueTypeImpl { enum { kValue = true }; }; template < > struct IsValidAttributeValueTypeImpl { enum { kValue = true }; }; template < > struct IsValidAttributeValueTypeImpl < CAttribute_ItemSlotCriteria > { enum { kValue = true }; }; template < > struct IsValidAttributeValueTypeImpl < CAttribute_WorldItemPlacement > { enum { kValue = true }; }; template < typename T > struct IsValidAttributeValueType : public IsValidAttributeValueTypeImpl< typename StripConstIfPresent::ResultType > { }; //----------------------------------------------------------------------------- // Purpose: Interface for callback functions per-attribute-data-type. When adding // a new attribute data type that can't be converted to any existing type, // you'll need to add a new virtual function here or the code will fail // to compile. //----------------------------------------------------------------------------- class IEconItemAttributeIterator { public: virtual ~IEconItemAttributeIterator ( ) { } // Returns whether to continue iteration after this element. virtual bool OnIterateAttributeValue( const CEconItemAttributeDefinition *pAttrDef, attrib_value_t value ) = 0; virtual bool OnIterateAttributeValue( const CEconItemAttributeDefinition *pAttrDef, float value ) = 0; virtual bool OnIterateAttributeValue( const CEconItemAttributeDefinition *pAttrDef, const uint64& value ) = 0; virtual bool OnIterateAttributeValue( const CEconItemAttributeDefinition *pAttrDef, const CAttribute_String& value ) = 0; virtual bool OnIterateAttributeValue( const CEconItemAttributeDefinition *pAttrDef, const CAttribute_DynamicRecipeComponent& value ) = 0; virtual bool OnIterateAttributeValue( const CEconItemAttributeDefinition *pAttrDef, const CAttribute_ItemSlotCriteria& value ) = 0; virtual bool OnIterateAttributeValue( const CEconItemAttributeDefinition *pAttrDef, const CAttribute_WorldItemPlacement& value ) = 0; }; //----------------------------------------------------------------------------- // Purpose: Iterator where each callback is default implemented, but the value // is ignored. Derive from this iterator when you only care about certain // attribute types. // //----------------------------------------------------------------------------- class CEconItemSpecificAttributeIterator : public IEconItemAttributeIterator { // By default, always return true virtual bool OnIterateAttributeValue( const CEconItemAttributeDefinition *pAttrDef, attrib_value_t value ) { return true; } virtual bool OnIterateAttributeValue( const CEconItemAttributeDefinition *pAttrDef, float value ) { return true; } virtual bool OnIterateAttributeValue( const CEconItemAttributeDefinition *pAttrDef, const uint64& value ) { return true; } virtual bool OnIterateAttributeValue( const CEconItemAttributeDefinition *pAttrDef, const CAttribute_String& value ) { return true; } virtual bool OnIterateAttributeValue( const CEconItemAttributeDefinition *pAttrDef, const CAttribute_DynamicRecipeComponent& value ) { return true; } virtual bool OnIterateAttributeValue( const CEconItemAttributeDefinition *pAttrDef, const CAttribute_ItemSlotCriteria& value ) { return true; } virtual bool OnIterateAttributeValue( const CEconItemAttributeDefinition *pAttrDef, const CAttribute_WorldItemPlacement& value ) { return true; } }; //----------------------------------------------------------------------------- // Purpose: Interface for a single callback function per-attribute, regardless of // what type of data it stores and what the value is. This can be used // to count attributes, display generic information about definitions, etc. // but can't be used to pull data. // // To implement a subclass, override the OnIterateAttributeValueUntyped() // method. //----------------------------------------------------------------------------- class IEconItemUntypedAttributeIterator : public IEconItemAttributeIterator { public: virtual bool OnIterateAttributeValue( const CEconItemAttributeDefinition *pAttrDef, attrib_value_t ) OVERRIDE { return OnIterateAttributeValueUntyped( pAttrDef ); } virtual bool OnIterateAttributeValue( const CEconItemAttributeDefinition *pAttrDef, float ) OVERRIDE { return OnIterateAttributeValueUntyped( pAttrDef ); } virtual bool OnIterateAttributeValue( const CEconItemAttributeDefinition *pAttrDef, const uint64& ) OVERRIDE { return OnIterateAttributeValueUntyped( pAttrDef ); } virtual bool OnIterateAttributeValue( const CEconItemAttributeDefinition *pAttrDef, const CAttribute_String& ) OVERRIDE { return OnIterateAttributeValueUntyped( pAttrDef ); } virtual bool OnIterateAttributeValue( const CEconItemAttributeDefinition *pAttrDef, const CAttribute_DynamicRecipeComponent& ) OVERRIDE { return OnIterateAttributeValueUntyped( pAttrDef ); } virtual bool OnIterateAttributeValue( const CEconItemAttributeDefinition *pAttrDef, const CAttribute_ItemSlotCriteria& ) OVERRIDE { return OnIterateAttributeValueUntyped( pAttrDef ); } virtual bool OnIterateAttributeValue( const CEconItemAttributeDefinition *pAttrDef, const CAttribute_WorldItemPlacement& ) OVERRIDE { return OnIterateAttributeValueUntyped( pAttrDef ); } private: virtual bool OnIterateAttributeValueUntyped( const CEconItemAttributeDefinition *pAttrDef ) = 0; }; //----------------------------------------------------------------------------- // Purpose: Simple class to answer the question "does this attribute exist" without // regards to what value it might have. Intended to be used by FindAttribute() // but made global because why not. //----------------------------------------------------------------------------- class CAttributeIterator_HasAttribute : public IEconItemUntypedAttributeIterator { public: CAttributeIterator_HasAttribute( const CEconItemAttributeDefinition *pAttrDef ) : m_pAttrDef( pAttrDef ) , m_bFound( false ) { Assert( m_pAttrDef ); } bool WasFound() const { return m_bFound; } private: bool OnIterateAttributeValueUntyped( const CEconItemAttributeDefinition *pAttrDef ) OVERRIDE { // We don't assert because we might be reusing the same iterator between calls. // Assert( !m_bFound ); if ( m_pAttrDef == pAttrDef ) { m_bFound = true; } return !m_bFound; } private: const CEconItemAttributeDefinition *m_pAttrDef; bool m_bFound; }; //----------------------------------------------------------------------------- // Purpose: Helper class to answer the question "does this attribute exist? and if // so what is its value?". There are some template shenanigans that happen // to make things as safe as possible, and to catch errors as early as // possible. // // TActualTypeInMemory: the in-memory type of the data we're going to try // to read out (ie., "attrib_value_t", "CAttribute_String", // etc. // // TTreatAsThisType: if TActualTypeInMemory is "attrib_value_t", then we're // dealing with the old attribute system and so maybe we // want to treat these bits as a float, or a bitmask, or // who knows! Specifying this type for non-attrib_value_t // in-memory types is invalid and will fail to compile. // // This class isn't intended to be used directly but instead called from // either FindAttribute() or FindAttribute_UnsafeBitwiseCast(). It's // global because C++ doesn't support template member functions on a // template class inside a standalone template function. Weird. //----------------------------------------------------------------------------- template < typename TActualTypeInMemory, typename TTreatAsThisType = TActualTypeInMemory > class CAttributeIterator_GetTypedAttributeValue : public IEconItemAttributeIterator { public: CAttributeIterator_GetTypedAttributeValue( const CEconItemAttributeDefinition *pAttrDef, TTreatAsThisType *outpValue ) : m_pAttrDef( pAttrDef ) , m_outpValue( outpValue ) , m_bFound( false ) { // If this fails, it means that the type TActualTypeInMemory isn't something the attribute // system is prepared to recognize as a valid attribute storage type. The list of valid types // are IsValidAttributeValueTypeImpl<> specializations. // // If you added a new type and didn't make a specialization for it, this will fail. If you // *didn't* add a new type, it probably means you're passing a pointer of an incorrect type // in to FindAttribute(). COMPILE_TIME_ASSERT( IsValidAttributeValueType::kValue ); // The only reason we allow callers to specify a different TTreatAsThisType (versus having // it always match TActualTypeInMemory) is to deal with the old attribute system, which sometimes // had attributes have int/float types and sometimes had attribute data values that were 32 // arbitrary bits. We test here to make sure that we're only using the "treat these bits as // a different type" behavior code when dealing with attributes using the old storage system // (attrib_value_t) or when we're trying to get the pointer to buffer contents for a string. COMPILE_TIME_ASSERT( ((AreTypesIdentical::kValue && AreTypesIdentical::kValue) || (AreTypesIdentical::kValue && AreTypesIdentical::kValue) || AreTypesIdentical::kValue) ); Assert( m_pAttrDef ); Assert( outpValue ); } virtual bool OnIterateAttributeValue( const CEconItemAttributeDefinition *pAttrDef, attrib_value_t value ) OVERRIDE { return OnIterateAttributeValueTyped( pAttrDef, value ); } virtual bool OnIterateAttributeValue( const CEconItemAttributeDefinition *pAttrDef, float value ) OVERRIDE { return OnIterateAttributeValueTyped( pAttrDef, value ); } virtual bool OnIterateAttributeValue( const CEconItemAttributeDefinition *pAttrDef, const uint64 & value ) OVERRIDE { return OnIterateAttributeValueTyped( pAttrDef, value ); } virtual bool OnIterateAttributeValue( const CEconItemAttributeDefinition *pAttrDef, const CAttribute_String & value ) OVERRIDE { return OnIterateAttributeValueTyped( pAttrDef, value ); } virtual bool OnIterateAttributeValue( const CEconItemAttributeDefinition *pAttrDef, const CAttribute_DynamicRecipeComponent & value ) OVERRIDE { return OnIterateAttributeValueTyped( pAttrDef, value ); } virtual bool OnIterateAttributeValue( const CEconItemAttributeDefinition *pAttrDef, const CAttribute_ItemSlotCriteria & value ) OVERRIDE { return OnIterateAttributeValueTyped( pAttrDef, value ); } virtual bool OnIterateAttributeValue( const CEconItemAttributeDefinition *pAttrDef, const CAttribute_WorldItemPlacement & value ) OVERRIDE { return OnIterateAttributeValueTyped( pAttrDef, value ); } bool WasFound() const { return m_bFound; } private: // Generic template function for handling any attribute value of any type besides the one that we're looking // for. For example, if we say "we're looking for attribute 'damage multiplier' and give me back a float", then // all other attribute value types (strings, structures, etc.) will go through this code, which does nothing // except look for caller errors. // // If you call FindAttribute() and specify the wrong type for an attribute (ie., using the above example, looking // for "damage multiplier" but feeding in a string), it will get found in this function, which will assert and // tell you you've got the wrong type. (FindAttribute() in that case will return false because it's impossible // for us to safely copy the value out.) template < typename TAnyOtherType > bool OnIterateAttributeValueTyped( const CEconItemAttributeDefinition *pAttrDef, const TAnyOtherType& value ) { COMPILE_TIME_ASSERT( IsValidAttributeValueType::kValue ); // We don't assert because we might be reusing the same iterator between calls. // Assert( !m_bFound ); AssertMsg( m_pAttrDef != pAttrDef, "Incorrect type found for attribute during iteration." ); return true; } // Overload for attributes of the data type we're looking for. ie., if we say "we're looking for attribute // 'damage multiplier' and give me back a float", this will be the specialization. We assume that // we're only going to find at most one attribute per definition and stop looking after we've found the first. // // Note that this is just a normal member function, but is *not* a template member function, which would compile // under VC but otherwise be illegal. bool OnIterateAttributeValueTyped( const CEconItemAttributeDefinition *pAttrDef, const TActualTypeInMemory& value ) { // We don't assert because we might be reusing the same iterator between calls. // Assert( !m_bFound ); if ( m_pAttrDef == pAttrDef ) { m_bFound = true; CopyAttributeValueToOutput( &value, reinterpret_cast( m_outpValue ) ); } return !m_bFound; } private: static void CopyAttributeValueToOutput( const TActualTypeInMemory *pValue, TTreatAsThisType *out_pValue ) { // Even if we are using the old attribute type system, we need to guarantee that the type // in memory (ie., uint32) and the type we're considering it as (ie., float) are the same size // because we're going to be doing bitwise casts. COMPILE_TIME_ASSERT( sizeof( TActualTypeInMemory ) == sizeof( TTreatAsThisType ) ); Assert( pValue ); Assert( out_pValue ); *out_pValue = *reinterpret_cast( pValue ); } private: const CEconItemAttributeDefinition *m_pAttrDef; TTreatAsThisType *m_outpValue; bool m_bFound; }; //----------------------------------------------------------------------------- // Purpose: Custom code path to support getting the char * result from an // attribute of type CAttribute_String. // // We can't specify the implementation here because we may or may not // have the definition of CAttribute_String in scope. We also can't // declare the template specialization here and define it later because // that would violate the standard, so instead we have the template // function call a declared-but-not-defined non-template function that // we can define later. //----------------------------------------------------------------------------- void CopyStringAttributeValueToCharPointerOutput( const CAttribute_String *pValue, const char **out_pValue ); template < > inline void CAttributeIterator_GetTypedAttributeValue::CopyAttributeValueToOutput( const CAttribute_String *pValue, const char **out_pValue ) { CopyStringAttributeValueToCharPointerOutput( pValue, out_pValue ); } //----------------------------------------------------------------------------- // Purpose: Look for the existence/nonexistence of an attribute with the // definition [pAttrDef]. Can be called on anything with an IterateAttributes() // member functions (IEconItemInterface, CEconItemDefinition). //----------------------------------------------------------------------------- template < typename TAttributeContainerType > bool FindAttribute( const TAttributeContainerType *pSomethingThatHasAnIterateAttributesFunction, const CEconItemAttributeDefinition *pAttrDef ) { #ifdef CLIENT_DLL VPROF_BUDGET( "IEconItemInterface::FindAttribute", VPROF_BUDGETGROUP_FINDATTRIBUTE ); #endif if ( !pAttrDef ) return false; CAttributeIterator_HasAttribute it( pAttrDef ); pSomethingThatHasAnIterateAttributesFunction->IterateAttributes( &it ); return it.WasFound(); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- template < typename TActualTypeInMemory, typename TTreatAsThisType, typename TAttributeContainerType > bool FindAttribute_UnsafeBitwiseCast( const TAttributeContainerType *pSomethingThatHasAnIterateAttributesFunction, const CEconItemAttributeDefinition *pAttrDef, TTreatAsThisType *out_pValue ) { #ifdef CLIENT_DLL VPROF_BUDGET( "IEconItemInterface::FindAttribute_UnsafeBitwiseCast", VPROF_BUDGETGROUP_FINDATTRIBUTEUNSAFE ); #endif if ( !pAttrDef ) return false; CAttributeIterator_GetTypedAttributeValue it( pAttrDef, out_pValue ); pSomethingThatHasAnIterateAttributesFunction->IterateAttributes( &it ); return it.WasFound(); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- template < typename TAttributeContainerType, typename T > bool FindAttribute( const TAttributeContainerType *pSomethingThatHasAnIterateAttributesFunction, const CEconItemAttributeDefinition *pAttrDef, T *out_pValue ) { return FindAttribute_UnsafeBitwiseCast( pSomethingThatHasAnIterateAttributesFunction, pAttrDef, out_pValue ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- class IEconItemInterface { public: virtual ~IEconItemInterface() { } // Is an attribute present? We neither know nor care anything about the attribute // value stored. bool FindAttribute( const CEconItemAttributeDefinition *pAttrDef ) const { return ::FindAttribute( this, pAttrDef ); } // If an attribute is present, it will copy the value into out_pValue and return true. // If the attribute is not present, it will return false and not touch the value in // out_pValue. If a T is passed in that is not a type the attribute system understands, // this function will fail to compile. template < typename T > bool FindAttribute( const CEconItemAttributeDefinition *pAttrDef, T *out_pValue ) const { return ::FindAttribute( this, pAttrDef, out_pValue ); } // Helpers to look for specific attribute values virtual CEconItemPaintKitDefinition *GetCustomPainkKitDefinition( void ) const { return GetItemDefinition() ? GetItemDefinition()->GetCustomPainkKitDefinition() : NULL; } virtual bool GetCustomPaintKitWear( float &flWear ) const; // IEconItemInterface common implementation. virtual bool IsTradable() const; virtual int GetUntradabilityFlags() const; virtual bool IsCommodity() const; virtual bool IsUsableInCrafting() const; virtual bool IsMarketable() const; // can this item be listed on the Marketplace? bool IsTemporaryItem() const; // returns whether this item is a temporary instance of an item that is not by nature temporary (ie., a preview item, an item with an attribute expiration timer) RTime32 GetExpirationDate() const; // will return RTime32( 0 ) if this item will not expire, otherwise the time that it will auto-delete itself; this looks at both static and dynamic ways of expiring timers // IEconItemInterface interface. virtual const GameItemDefinition_t *GetItemDefinition() const = 0; virtual itemid_t GetID() const = 0; // intentionally not called GetItemID to avoid stomping non-virtual GetItemID() on CEconItem virtual uint32 GetAccountID() const = 0; virtual int32 GetQuality() const = 0; virtual style_index_t GetStyle() const = 0; virtual uint8 GetFlags() const = 0; virtual eEconItemOrigin GetOrigin() const = 0; virtual int GetQuantity() const = 0; virtual uint32 GetItemLevel() const = 0; virtual bool GetInUse() const = 0; // is this item in use somewhere in the backend? (ie., cross-game trading) virtual const char *GetCustomName() const = 0; // get a user-generated name, if present, otherwise NULL; return value is UTF8 virtual const char *GetCustomDesc() const = 0; // get a user-generated flavor text, if present, otherwise NULL; return value is UTF8 // IEconItemInterface attribute iteration interface. This is not meant to be used for // attribute lookup! This is meant for anything that requires iterating over the full // attribute list. virtual void IterateAttributes( IEconItemAttributeIterator *pIterator ) const = 0; // Fetch values from the definition const char *GetDefinitionString( const char *pszKeyName, const char *pszDefaultValue = "" ) const; KeyValues *GetDefinitionKey( const char *pszKeyName ) const; RTime32 GetTradableAfterDateTime() const; virtual item_definition_index_t GetItemDefIndex() const { return GetItemDefinition() ? GetItemDefinition()->GetDefinitionIndex() : INVALID_ITEM_DEF_INDEX; } virtual IMaterial* GetMaterialOverride( int iTeam ) = 0; protected: bool IsPermanentlyUntradable() const; bool IsTemporarilyUntradable() const; }; //----------------------------------------------------------------------------- // Purpose: Classes that want default behavior for GetMaterialOverride, which // currently derive from IEconItemInterface can instead derive from // CMaterialOverrideContainer< IEconItemInterface > and have the details // of material overrides hidden from them. //----------------------------------------------------------------------------- template class CMaterialOverrideContainer : public TBaseClass { public: virtual IMaterial* GetMaterialOverride( int iTeam ) OVERRIDE { #ifdef CLIENT_DLL Assert( iTeam >= 0 && iTeam < ARRAYSIZE( m_materialOverrides ) ); if ( m_materialOverrides[ iTeam ].IsValid() ) return m_materialOverrides[ iTeam ]; if ( !this->GetItemDefinition() ) return NULL; const char* pName = this->GetItemDefinition()->GetMaterialOverride( iTeam ); if ( pName == NULL ) return NULL; m_materialOverrides[ iTeam ].Init( pName, TEXTURE_GROUP_CLIENT_EFFECTS ); return m_materialOverrides[ iTeam ]; #else return NULL; #endif } protected: void ResetMaterialOverrides() { #ifdef CLIENT_DLL for ( int i = 0; i < TF_TEAM_COUNT; ++i ) m_materialOverrides[ i ].Shutdown(); #endif } private: #ifdef CLIENT_DLL CMaterialReference m_materialOverrides[ TF_TEAM_COUNT ]; #endif }; #endif // ECONITEMINTERFACE_H