//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: Attributable entities contain one of these, which handles game specific handling: // - Save / Restore // - Networking // - Attribute providers // - Application of attribute effects // //============================================================================= #ifndef ATTRIBUTE_MANAGER_H #define ATTRIBUTE_MANAGER_H #ifdef _WIN32 #pragma once #endif #include "econ_item_view.h" #include "ihasattributes.h" #include "tf_gcmessages.h" // Provider types enum attributeprovidertypes_t { PROVIDER_GENERIC, PROVIDER_WEAPON, }; float CollateAttributeValues( const CEconItemAttributeDefinition *pAttrDef1, const float flAttribValue1, const CEconItemAttributeDefinition *pAttrDef2, const float flAttribValue2 ); // Retrieve the IHasAttributes pointer from a Base Entity. This function checks for NULL entities // and asserts the return value is == to dynamic_cast< IHasAttributes * >( pEntity ). inline IHasAttributes *GetAttribInterface( CBaseEntity *pEntity ) { IHasAttributes *pAttribInterface = pEntity ? pEntity->GetHasAttributesInterfacePtr() : NULL; // If this assert hits it most likely means that m_pAttribInterface has not been set // in the leaf class constructor for this object. See CTFPlayer::CTFPlayer() for an // example. Assert( pAttribInterface == dynamic_cast< IHasAttributes *>( pEntity ) ); return pAttribInterface; } //----------------------------------------------------------------------------- // Macros for hooking the application of attributes #define CALL_ATTRIB_HOOK( vartype, retval, hookName, who, itemlist ) \ retval = CAttributeManager::AttribHookValue( retval, #hookName, static_cast( who ), itemlist, true ); #define CALL_ATTRIB_HOOK_INT( retval, hookName ) CALL_ATTRIB_HOOK( int, retval, hookName, this, NULL ) #define CALL_ATTRIB_HOOK_FLOAT( retval, hookName ) CALL_ATTRIB_HOOK( float, retval, hookName, this, NULL ) #define CALL_ATTRIB_HOOK_STRING( retval, hookName ) CALL_ATTRIB_HOOK( CAttribute_String, retval, hookName, this, NULL ) #define CALL_ATTRIB_HOOK_INT_ON_OTHER( other, retval, hookName ) CALL_ATTRIB_HOOK( int, retval, hookName, other, NULL ) #define CALL_ATTRIB_HOOK_FLOAT_ON_OTHER( other, retval, hookName ) CALL_ATTRIB_HOOK( float, retval, hookName, other, NULL ) #define CALL_ATTRIB_HOOK_STRING_ON_OTHER( other, retval, hookName ) CALL_ATTRIB_HOOK( CAttribute_String, retval, hookName, other, NULL ) #define CALL_ATTRIB_HOOK_INT_ON_OTHER_WITH_ITEMS( other, retval, items_array, hookName ) CALL_ATTRIB_HOOK( int, retval, hookName, other, items_array ) #define CALL_ATTRIB_HOOK_FLOAT_ON_OTHER_WITH_ITEMS( other, retval, items_array, hookName ) CALL_ATTRIB_HOOK( float, retval, hookName, other, items_array ) #define CALL_ATTRIB_HOOK_STRING_ON_OTHER_WITH_ITEMS( other, retval, items_array, hookName ) CALL_ATTRIB_HOOK( CAttribute_String, retval, hookName, other, items_array ) template< class T > T AttributeConvertFromFloat( float flValue ); template<> float AttributeConvertFromFloat( float flValue ); template<> int AttributeConvertFromFloat( float flValue ); //----------------------------------------------------------------------------- // Purpose: Base Attribute manager. // This class knows how to apply attribute effects that have been // provided to its owner by other entities, but doesn't contain attributes itself. //----------------------------------------------------------------------------- class CAttributeManager { DECLARE_CLASS_NOBASE( CAttributeManager ); public: DECLARE_DATADESC(); DECLARE_EMBEDDED_NETWORKVAR(); CAttributeManager(); virtual ~CAttributeManager() {} // Call this inside your entity's Spawn() virtual void InitializeAttributes( CBaseEntity *pEntity ); CBaseEntity *GetOuter( void ) const { return m_hOuter.Get(); } //-------------------------------------------------------- // Attribute providers. // Other entities that are providing attributes to this entity (i.e. weapons being carried by a player) void ProvideTo( CBaseEntity *pProvider ); void StopProvidingTo( CBaseEntity *pProvider ); protected: // Not to be called directly. Use ProvideTo() or StopProvidingTo() above. void AddProvider( CBaseEntity *pProvider ); void RemoveProvider( CBaseEntity *pProvider ); public: // Return true if this entity is providing attributes to the specified entity bool IsProvidingTo( CBaseEntity *pEntity ) const; // Return true if this entity is being provided attributes by the specified entity bool IsBeingProvidedToBy( CBaseEntity *pEntity ) const; // Provider types are used to prevent specified providers supplying to certain initiators void SetProviderType( attributeprovidertypes_t tType ) { m_ProviderType = tType; } attributeprovidertypes_t GetProviderType( void ) const { return m_ProviderType; } //-------------------------------------------------------- // Attribute hook. Use the CALL_ATTRIB_HOOK macros above. template static T AttribHookValue( T TValue, const char *pszAttribHook, const CBaseEntity *pEntity, CUtlVector *pItemList = NULL, bool bIsGlobalConstString = false ) { VPROF_BUDGET( "CAttributeManager::AttribHookValue", VPROF_BUDGETGROUP_ATTRIBUTES ); // Do we have a hook? if ( pszAttribHook == NULL || pszAttribHook[0] == '\0' ) return TValue; // Verify that we have an entity, at least as "this" if ( pEntity == NULL ) return TValue; IHasAttributes *pAttribInterface = GetAttribInterface( (CBaseEntity*) pEntity ); AssertMsg( pAttribInterface, "If you hit this, you've probably got a hook incorrectly setup, because the entity it's hooking on doesn't know about attributes." ); if ( pAttribInterface == NULL ) return TValue; // Hook base attribute. T Scratch; AttribHookValueInternal( Scratch, TValue, pszAttribHook, pEntity, pAttribInterface, pItemList, bIsGlobalConstString ); return Scratch; } private: template static void TypedAttribHookValueInternal( T& out, T TValue, string_t iszAttribHook, const CBaseEntity *pEntity, IHasAttributes *pAttribInterface, CUtlVector *pItemList ) { float flValue = pAttribInterface->GetAttributeManager()->ApplyAttributeFloatWrapper( static_cast( TValue ), const_cast( pEntity ), iszAttribHook, pItemList ); out = AttributeConvertFromFloat( flValue ); } static void TypedAttribHookValueInternal( CAttribute_String& out, const CAttribute_String& TValue, string_t iszAttribHook, const CBaseEntity *pEntity, IHasAttributes *pAttribInterface, CUtlVector *pItemList ) { string_t iszIn = AllocPooledString( TValue.value().c_str() ); string_t iszOut = pAttribInterface->GetAttributeManager()->ApplyAttributeStringWrapper( iszIn, const_cast( pEntity ), iszAttribHook, pItemList ); const char* pszOut = STRING( iszOut ); // STRING() returns different value for server and client // server will return "" for NULL_STRING // client will return NULL for NULL_STRING if ( pszOut ) { out.set_value( pszOut ); } else { out.set_value( "" ); } } template static void AttribHookValueInternal( T& out, T TValue, const char *pszAttribHook, const CBaseEntity *pEntity, IHasAttributes *pAttribInterface, CUtlVector *pItemList, bool bIsGlobalConstString ) { Assert( pszAttribHook ); Assert( pszAttribHook[0] ); Assert( pEntity ); Assert( pAttribInterface ); Assert( GetAttribInterface( (CBaseEntity*) pEntity ) == pAttribInterface ); Assert( pAttribInterface->GetAttributeManager() ); string_t iszAttribHook = bIsGlobalConstString ? AllocPooledString_StaticConstantStringPointer( pszAttribHook ) : AllocPooledString( pszAttribHook ); return TypedAttribHookValueInternal( out, TValue, iszAttribHook, pEntity, pAttribInterface, pItemList ); } int m_nCurrentTick; int m_nCalls; public: virtual float ApplyAttributeFloat( float flValue, CBaseEntity *pInitiator, string_t iszAttribHook = NULL_STRING, CUtlVector *pItemList = NULL ); virtual string_t ApplyAttributeString( string_t iszValue, CBaseEntity *pInitiator, string_t iszAttribHook = NULL_STRING, CUtlVector *pItemList = NULL ); //-------------------------------------------------------- // Networking #ifdef CLIENT_DLL virtual void OnPreDataChanged( DataUpdateType_t updateType ); virtual void OnDataChanged( DataUpdateType_t updateType ); #endif //-------------------------------------------------------- // memory handling void *operator new( size_t stAllocateBlock ); void *operator new( size_t stAllocateBlock, int nBlockUse, const char *pFileName, int nLine ); protected: CUtlVector m_Providers; // entities that we receive attribute data *from* CUtlVector m_Receivers; // entities that we provide attribute data *to* CNetworkVarForDerived( int, m_iReapplyProvisionParity ); CNetworkVarForDerived( EHANDLE, m_hOuter ); bool m_bPreventLoopback; CNetworkVarForDerived( attributeprovidertypes_t, m_ProviderType ); int m_iCacheVersion; // maps to gamerules counter for global cache flushing public: virtual void OnAttributeValuesChanged() { ClearCache(); } private: void ClearCache(); int GetGlobalCacheVersion() const; virtual float ApplyAttributeFloatWrapper( float flValue, CBaseEntity *pInitiator, string_t iszAttribHook, CUtlVector *pItemList = NULL ); virtual string_t ApplyAttributeStringWrapper( string_t iszValue, CBaseEntity *pInitiator, string_t iszAttribHook, CUtlVector *pItemList = NULL ); // Cached attribute results // We cache off requests for data, and wipe the cache whenever our providers change. union cached_attribute_types { float fl; string_t isz; }; struct cached_attribute_t { string_t iAttribHook; cached_attribute_types in; cached_attribute_types out; }; CUtlVector m_CachedResults; #ifdef CLIENT_DLL public: // Data received from the server int m_iOldReapplyProvisionParity; #endif }; //----------------------------------------------------------------------------- // Purpose: This is an attribute manager that also knows how to contain attributes. //----------------------------------------------------------------------------- class CAttributeContainer : public CAttributeManager { public: DECLARE_DATADESC(); DECLARE_CLASS( CAttributeContainer, CAttributeManager ); DECLARE_EMBEDDED_NETWORKVAR(); virtual void InitializeAttributes( CBaseEntity *pEntity ); //-------------------------------------------------------- // Attribute hook. Use the CALL_ATTRIB_HOOK macros above. virtual float ApplyAttributeFloat( float flValue, CBaseEntity *pInitiator, string_t iszAttribHook = NULL_STRING, CUtlVector *pItemList = NULL ) OVERRIDE; virtual string_t ApplyAttributeString( string_t iszValue, CBaseEntity *pInitiator, string_t iszAttribHook = NULL_STRING, CUtlVector *pItemList = NULL ) OVERRIDE; CEconItemView *GetItem( void ) { return &m_Item; } const CEconItemView *GetItem( void ) const { return &m_Item; } void SetItem( const CEconItemView *pItem ) { m_Item.CopyFrom( *pItem ); } virtual void OnAttributeValuesChanged() { BaseClass::OnAttributeValuesChanged(); m_Item.OnAttributeValuesChanged(); } private: CNetworkVarEmbedded( CEconItemView, m_Item ); }; //----------------------------------------------------------------------------- // Purpose: An attribute manager that uses a player's shared attributes. //----------------------------------------------------------------------------- #ifndef DOTA_DLL class CAttributeContainerPlayer : public CAttributeManager { public: DECLARE_DATADESC(); DECLARE_CLASS( CAttributeContainerPlayer, CAttributeManager ); DECLARE_EMBEDDED_NETWORKVAR(); virtual float ApplyAttributeFloat( float flValue, CBaseEntity *pInitiator, string_t iszAttribHook = NULL_STRING, CUtlVector *pItemList = NULL ) OVERRIDE; virtual string_t ApplyAttributeString( string_t iszValue, CBaseEntity *pInitiator, string_t iszAttribHook = NULL_STRING, CUtlVector *pItemList = NULL ) OVERRIDE; CBasePlayer* GetPlayer( void ) { return m_hPlayer; } void SetPlayer( CBasePlayer *pPlayer ) { m_hPlayer = pPlayer; } virtual void OnAttributeValuesChanged() { BaseClass::OnAttributeValuesChanged(); m_hPlayer->NetworkStateChanged(); } private: CNetworkHandle( CBasePlayer, m_hPlayer ); }; #endif #ifdef CLIENT_DLL EXTERN_RECV_TABLE( DT_AttributeManager ); EXTERN_RECV_TABLE( DT_AttributeContainer ); #else EXTERN_SEND_TABLE( DT_AttributeManager ); EXTERN_SEND_TABLE( DT_AttributeContainer ); #endif #endif // ATTRIBUTE_MANAGER_H