Team Fortress 2 Source Code as on 22/4/2020
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

881 lines
29 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================
  6. #include "cbase.h"
  7. #include "attribute_manager.h"
  8. #include "gamestringpool.h"
  9. #include "saverestore.h"
  10. #include "saverestore_utlvector.h"
  11. #include "fmtstr.h"
  12. #include "KeyValues.h"
  13. #include "econ_item_system.h"
  14. #if defined( TF_DLL ) || defined( TF_CLIENT_DLL )
  15. #include "tf_gamerules.h" // attribute cache flushing; can be generalized if/when Dota needs similar functionality
  16. #endif // defined( TF_DLL ) || defined( TF_CLIENT_DLL )
  17. #define PROVIDER_PARITY_BITS 6
  18. #define PROVIDER_PARITY_MASK ((1<<PROVIDER_PARITY_BITS)-1)
  19. //==================================================================================================================
  20. // ATTRIBUTE MANAGER SAVE/LOAD & NETWORKING
  21. //===================================================================================================================
  22. BEGIN_DATADESC_NO_BASE( CAttributeManager )
  23. DEFINE_UTLVECTOR( m_Providers, FIELD_EHANDLE ),
  24. DEFINE_UTLVECTOR( m_Receivers, FIELD_EHANDLE ),
  25. DEFINE_FIELD( m_iReapplyProvisionParity, FIELD_INTEGER ),
  26. DEFINE_FIELD( m_hOuter, FIELD_EHANDLE ),
  27. // DEFINE_FIELD( m_bPreventLoopback, FIELD_BOOLEAN ), // Don't need to save
  28. DEFINE_FIELD( m_ProviderType, FIELD_INTEGER ),
  29. END_DATADESC()
  30. BEGIN_DATADESC( CAttributeContainer )
  31. DEFINE_EMBEDDED( m_Item ),
  32. END_DATADESC()
  33. #ifndef DOTA_DLL
  34. BEGIN_DATADESC( CAttributeContainerPlayer )
  35. END_DATADESC()
  36. #endif
  37. #ifndef CLIENT_DLL
  38. EXTERN_SEND_TABLE( DT_ScriptCreatedItem );
  39. #else
  40. EXTERN_RECV_TABLE( DT_ScriptCreatedItem );
  41. #endif
  42. BEGIN_NETWORK_TABLE_NOBASE( CAttributeManager, DT_AttributeManager )
  43. #ifndef CLIENT_DLL
  44. SendPropEHandle( SENDINFO(m_hOuter) ),
  45. SendPropInt( SENDINFO(m_ProviderType), 4, SPROP_UNSIGNED ),
  46. SendPropInt( SENDINFO(m_iReapplyProvisionParity), PROVIDER_PARITY_BITS, SPROP_UNSIGNED ),
  47. #else
  48. RecvPropEHandle( RECVINFO(m_hOuter) ),
  49. RecvPropInt( RECVINFO(m_ProviderType) ),
  50. RecvPropInt( RECVINFO(m_iReapplyProvisionParity) ),
  51. #endif
  52. END_NETWORK_TABLE()
  53. BEGIN_NETWORK_TABLE_NOBASE( CAttributeContainer, DT_AttributeContainer )
  54. #ifndef CLIENT_DLL
  55. SendPropEHandle( SENDINFO(m_hOuter) ),
  56. SendPropInt( SENDINFO(m_ProviderType), 4, SPROP_UNSIGNED ),
  57. SendPropInt( SENDINFO(m_iReapplyProvisionParity), PROVIDER_PARITY_BITS, SPROP_UNSIGNED ),
  58. SendPropDataTable(SENDINFO_DT(m_Item), &REFERENCE_SEND_TABLE(DT_ScriptCreatedItem)),
  59. #else
  60. RecvPropEHandle( RECVINFO(m_hOuter) ),
  61. RecvPropInt( RECVINFO(m_ProviderType) ),
  62. RecvPropInt( RECVINFO(m_iReapplyProvisionParity) ),
  63. RecvPropDataTable(RECVINFO_DT(m_Item), 0, &REFERENCE_RECV_TABLE(DT_ScriptCreatedItem)),
  64. #endif
  65. END_NETWORK_TABLE()
  66. #ifndef DOTA_DLL
  67. BEGIN_NETWORK_TABLE_NOBASE( CAttributeContainerPlayer, DT_AttributeContainerPlayer )
  68. #ifndef CLIENT_DLL
  69. SendPropEHandle( SENDINFO(m_hOuter) ),
  70. SendPropInt( SENDINFO(m_ProviderType), 4, SPROP_UNSIGNED ),
  71. SendPropInt( SENDINFO(m_iReapplyProvisionParity), PROVIDER_PARITY_BITS, SPROP_UNSIGNED ),
  72. SendPropEHandle( SENDINFO(m_hPlayer) ),
  73. #else
  74. RecvPropEHandle( RECVINFO(m_hOuter) ),
  75. RecvPropInt( RECVINFO(m_ProviderType) ),
  76. RecvPropInt( RECVINFO(m_iReapplyProvisionParity) ),
  77. RecvPropEHandle( RECVINFO( m_hPlayer ) ),
  78. #endif
  79. END_NETWORK_TABLE()
  80. #endif
  81. template< class T > T AttributeConvertFromFloat( float flValue )
  82. {
  83. return static_cast<T>( flValue );
  84. }
  85. template<> float AttributeConvertFromFloat<float>( float flValue )
  86. {
  87. return flValue;
  88. }
  89. template<> int AttributeConvertFromFloat<int>( float flValue )
  90. {
  91. return RoundFloatToInt( flValue );
  92. }
  93. //-----------------------------------------------------------------------------
  94. // All fields in the object are all initialized to 0.
  95. //-----------------------------------------------------------------------------
  96. void *CAttributeManager::operator new( size_t stAllocateBlock )
  97. {
  98. // call into engine to get memory
  99. Assert( stAllocateBlock != 0 );
  100. void *pMem = malloc( stAllocateBlock );
  101. memset( pMem, 0, stAllocateBlock );
  102. return pMem;
  103. };
  104. void *CAttributeManager::operator new( size_t stAllocateBlock, int nBlockUse, const char *pFileName, int nLine )
  105. {
  106. // call into engine to get memory
  107. Assert( stAllocateBlock != 0 );
  108. void *pMem = malloc( stAllocateBlock );
  109. memset( pMem, 0, stAllocateBlock );
  110. return pMem;
  111. }
  112. CAttributeManager::CAttributeManager()
  113. {
  114. m_nCalls = 0;
  115. m_nCurrentTick = 0;
  116. }
  117. #ifdef CLIENT_DLL
  118. //-----------------------------------------------------------------------------
  119. // Purpose:
  120. //-----------------------------------------------------------------------------
  121. void CAttributeManager::OnPreDataChanged( DataUpdateType_t updateType )
  122. {
  123. m_iOldReapplyProvisionParity = m_iReapplyProvisionParity;
  124. }
  125. //-----------------------------------------------------------------------------
  126. // Purpose:
  127. //-----------------------------------------------------------------------------
  128. void CAttributeManager::OnDataChanged( DataUpdateType_t updateType )
  129. {
  130. if ( m_iReapplyProvisionParity != m_iOldReapplyProvisionParity )
  131. {
  132. // We've changed who we're providing to in some way. Reapply it.
  133. IHasAttributes *pAttribInterface = GetAttribInterface( GetOuter() );
  134. if ( pAttribInterface )
  135. {
  136. pAttribInterface->ReapplyProvision();
  137. }
  138. ClearCache();
  139. m_iOldReapplyProvisionParity = m_iReapplyProvisionParity.Get();
  140. }
  141. }
  142. #endif // CLIENT_DLL
  143. //-----------------------------------------------------------------------------
  144. // Purpose: Call this inside your entity's Spawn()
  145. //-----------------------------------------------------------------------------
  146. void CAttributeManager::InitializeAttributes( CBaseEntity *pEntity )
  147. {
  148. Assert( GetAttribInterface( pEntity ) );
  149. m_hOuter = pEntity;
  150. m_bPreventLoopback = false;
  151. }
  152. //=====================================================================================================
  153. // ATTRIBUTE PROVIDERS
  154. //=====================================================================================================
  155. //-----------------------------------------------------------------------------
  156. // Purpose:
  157. //-----------------------------------------------------------------------------
  158. void CAttributeManager::ProvideTo( CBaseEntity *pProvider )
  159. {
  160. IHasAttributes *pOwnerAttribInterface = GetAttribInterface( pProvider );
  161. if ( pOwnerAttribInterface )
  162. {
  163. pOwnerAttribInterface->GetAttributeManager()->AddProvider( m_hOuter.Get() );
  164. #ifndef CLIENT_DLL
  165. m_iReapplyProvisionParity = (m_iReapplyProvisionParity + 1) & PROVIDER_PARITY_MASK;
  166. NetworkStateChanged();
  167. #endif
  168. }
  169. }
  170. //-----------------------------------------------------------------------------
  171. // Purpose:
  172. //-----------------------------------------------------------------------------
  173. void CAttributeManager::StopProvidingTo( CBaseEntity *pProvider )
  174. {
  175. IHasAttributes *pOwnerAttribInterface = GetAttribInterface( pProvider );
  176. if ( pOwnerAttribInterface )
  177. {
  178. pOwnerAttribInterface->GetAttributeManager()->RemoveProvider( m_hOuter.Get() );
  179. #ifndef CLIENT_DLL
  180. m_iReapplyProvisionParity = (m_iReapplyProvisionParity + 1) & PROVIDER_PARITY_MASK;
  181. NetworkStateChanged();
  182. #endif
  183. }
  184. }
  185. //-----------------------------------------------------------------------------
  186. // Purpose:
  187. //-----------------------------------------------------------------------------
  188. void CAttributeManager::AddProvider( CBaseEntity *pProvider )
  189. {
  190. // Make sure he's not already in our list, and prevent circular provision
  191. Assert( !IsBeingProvidedToBy(pProvider) );
  192. Assert( !IsProvidingTo(pProvider) );
  193. // Ensure he's allowed to provide
  194. IHasAttributes *pProviderAttrInterface = GetAttribInterface( pProvider );
  195. Assert( pProviderAttrInterface );
  196. m_Providers.AddToTail( pProvider );
  197. pProviderAttrInterface->GetAttributeManager()->m_Receivers.AddToTail( GetOuter() );
  198. ClearCache();
  199. }
  200. //-----------------------------------------------------------------------------
  201. // Purpose:
  202. //-----------------------------------------------------------------------------
  203. void CAttributeManager::RemoveProvider( CBaseEntity *pProvider )
  204. {
  205. Assert( pProvider );
  206. IHasAttributes *pProviderAttrInterface = GetAttribInterface( pProvider );
  207. Assert( pProviderAttrInterface );
  208. if ( !IsBeingProvidedToBy( pProvider ) )
  209. return;
  210. Assert( pProviderAttrInterface->GetAttributeManager()->IsProvidingTo( GetOuter() ) );
  211. Assert( pProviderAttrInterface->GetAttributeManager()->m_Receivers.Find( GetOuter() ) != pProviderAttrInterface->GetAttributeManager()->m_Receivers.InvalidIndex() );
  212. m_Providers.FindAndFastRemove( pProvider );
  213. pProviderAttrInterface->GetAttributeManager()->m_Receivers.FindAndFastRemove( GetOuter() );
  214. ClearCache();
  215. }
  216. //-----------------------------------------------------------------------------
  217. // Purpose:
  218. //-----------------------------------------------------------------------------
  219. void CAttributeManager::ClearCache( void )
  220. {
  221. if ( m_bPreventLoopback )
  222. return;
  223. m_CachedResults.Purge();
  224. m_bPreventLoopback = true;
  225. // Tell all providers relying on me that they need to wipe their cache too
  226. FOR_EACH_VEC( m_Receivers, i )
  227. {
  228. IHasAttributes *pAttribInterface = GetAttribInterface( m_Receivers[i].Get() );
  229. if ( pAttribInterface )
  230. {
  231. pAttribInterface->GetAttributeManager()->ClearCache();
  232. }
  233. }
  234. // Tell our owner that he needs to clear his too, in case he has attributes affecting him
  235. IHasAttributes *pMyAttribInterface = GetAttribInterface( m_hOuter.Get().Get() );
  236. if ( pMyAttribInterface )
  237. {
  238. pMyAttribInterface->GetAttributeManager()->ClearCache();
  239. }
  240. m_bPreventLoopback = false;
  241. #ifndef CLIENT_DLL
  242. // Force out client to clear their cache as well
  243. m_iReapplyProvisionParity = (m_iReapplyProvisionParity + 1) & PROVIDER_PARITY_MASK;
  244. NetworkStateChanged();
  245. #endif
  246. }
  247. //-----------------------------------------------------------------------------
  248. // Purpose:
  249. //-----------------------------------------------------------------------------
  250. int CAttributeManager::GetGlobalCacheVersion() const
  251. {
  252. #if defined( TF_DLL ) || defined( TF_CLIENT_DLL )
  253. return TFGameRules() ? TFGameRules()->GetGlobalAttributeCacheVersion() : 0;
  254. #else
  255. return 0;
  256. #endif
  257. }
  258. //-----------------------------------------------------------------------------
  259. // Purpose: Return true if this entity is providing attributes to the specified entity
  260. //-----------------------------------------------------------------------------
  261. bool CAttributeManager::IsProvidingTo( CBaseEntity *pEntity ) const
  262. {
  263. IHasAttributes *pAttribInterface = GetAttribInterface( pEntity );
  264. if ( pAttribInterface )
  265. {
  266. if ( pAttribInterface->GetAttributeManager()->IsBeingProvidedToBy( GetOuter() ) )
  267. return true;
  268. }
  269. return false;
  270. }
  271. //-----------------------------------------------------------------------------
  272. // Purpose: Return true if this entity is being provided attributes by the specified entity
  273. //-----------------------------------------------------------------------------
  274. bool CAttributeManager::IsBeingProvidedToBy( CBaseEntity *pEntity ) const
  275. {
  276. return ( m_Providers.Find( pEntity ) != m_Providers.InvalidIndex() );
  277. }
  278. //=====================================================================================================
  279. // ATTRIBUTE HOOKS
  280. //=====================================================================================================
  281. //-----------------------------------------------------------------------------
  282. // Purpose: Wrapper that checks to see if we've already got the result in our cache
  283. //-----------------------------------------------------------------------------
  284. float CAttributeManager::ApplyAttributeFloatWrapper( float flValue, CBaseEntity *pInitiator, string_t iszAttribHook, CUtlVector<CBaseEntity*> *pItemList )
  285. {
  286. VPROF_BUDGET( "CAttributeManager::ApplyAttributeFloatWrapper", VPROF_BUDGETGROUP_ATTRIBUTES );
  287. #ifdef DEBUG
  288. AssertMsg1( m_nCalls != 5000, "%d calls for attributes in a single tick. This is slow and bad.", m_nCalls );
  289. if( m_nCurrentTick != gpGlobals->tickcount )
  290. {
  291. m_nCalls = 0;
  292. m_nCurrentTick = gpGlobals->tickcount;
  293. }
  294. ++m_nCalls;
  295. #endif
  296. // Have we requested a global attribute cache flush?
  297. const int iGlobalCacheVersion = GetGlobalCacheVersion();
  298. if ( m_iCacheVersion != iGlobalCacheVersion )
  299. {
  300. ClearCache();
  301. m_iCacheVersion = iGlobalCacheVersion;
  302. }
  303. // We can't cache off item references so if we asked for them we need to execute the whole slow path.
  304. if ( !pItemList )
  305. {
  306. int iCount = m_CachedResults.Count();
  307. for ( int i = iCount-1; i >= 0; i-- )
  308. {
  309. if ( m_CachedResults[i].iAttribHook == iszAttribHook )
  310. {
  311. if ( m_CachedResults[i].in.fl == flValue )
  312. return m_CachedResults[i].out.fl;
  313. // We've got a cached result for a different flIn value. Remove the cached result to
  314. // prevent stacking up entries for different requests (i.e. crit chance)
  315. m_CachedResults.Remove(i);
  316. break;
  317. }
  318. }
  319. }
  320. // Wasn't in cache, or we need item references. Do the work.
  321. float flResult = ApplyAttributeFloat( flValue, pInitiator, iszAttribHook, pItemList );
  322. // Add it to our cache if we didn't ask for item references. We could add the result value here
  323. // even if we did but we'd need to walk the cache to search for an old entry to overwrite first.
  324. if ( !pItemList )
  325. {
  326. int iIndex = m_CachedResults.AddToTail();
  327. m_CachedResults[iIndex].in.fl = flValue;
  328. m_CachedResults[iIndex].out.fl = flResult;
  329. m_CachedResults[iIndex].iAttribHook = iszAttribHook;
  330. }
  331. return flResult;
  332. }
  333. //-----------------------------------------------------------------------------
  334. // Purpose: Wrapper that checks to see if we've already got the result in our cache
  335. //-----------------------------------------------------------------------------
  336. string_t CAttributeManager::ApplyAttributeStringWrapper( string_t iszValue, CBaseEntity *pInitiator, string_t iszAttribHook, CUtlVector<CBaseEntity*> *pItemList /*= NULL*/ )
  337. {
  338. // Have we requested a global attribute cache flush?
  339. const int iGlobalCacheVersion = GetGlobalCacheVersion();
  340. if ( m_iCacheVersion != iGlobalCacheVersion )
  341. {
  342. ClearCache();
  343. m_iCacheVersion = iGlobalCacheVersion;
  344. }
  345. // We can't cache off item references so if we asked for them we need to execute the whole slow path.
  346. if ( !pItemList )
  347. {
  348. int iCount = m_CachedResults.Count();
  349. for ( int i = iCount-1; i >= 0; i-- )
  350. {
  351. if ( m_CachedResults[i].iAttribHook == iszAttribHook )
  352. {
  353. if ( m_CachedResults[i].in.isz == iszValue )
  354. {
  355. return m_CachedResults[i].out.isz;
  356. }
  357. // We've got a cached result for a different flIn value. Remove the cached result to
  358. // prevent stacking up entries for different requests (i.e. crit chance)
  359. m_CachedResults.Remove(i);
  360. break;
  361. }
  362. }
  363. }
  364. // Wasn't in cache, or we need item references. Do the work.
  365. string_t iszOut = ApplyAttributeString( iszValue, pInitiator, iszAttribHook, pItemList );
  366. // Add it to our cache if we didn't ask for item references. We could add the result value here
  367. // even if we did but we'd need to walk the cache to search for an old entry to overwrite first.
  368. if ( !pItemList )
  369. {
  370. int iIndex = m_CachedResults.AddToTail();
  371. m_CachedResults[iIndex].in.isz = iszValue;
  372. m_CachedResults[iIndex].out.isz = iszOut;
  373. m_CachedResults[iIndex].iAttribHook = iszAttribHook;
  374. }
  375. return iszOut;
  376. }
  377. //-----------------------------------------------------------------------------
  378. // Purpose:
  379. //-----------------------------------------------------------------------------
  380. float CAttributeManager::ApplyAttributeFloat( float flValue, CBaseEntity *pInitiator, string_t iszAttribHook, CUtlVector<CBaseEntity*> *pItemList )
  381. {
  382. VPROF_BUDGET( "CAttributeManager::ApplyAttributeFloat", VPROF_BUDGETGROUP_ATTRIBUTES );
  383. if ( m_bPreventLoopback || !GetOuter() )
  384. return flValue;
  385. // We need to prevent loopback between two items both providing to the same entity.
  386. m_bPreventLoopback = true;
  387. IHasAttributes *pInitiatorAttribInterface = GetAttribInterface( pInitiator );
  388. // See if we have any providers. If we do, tell them to apply.
  389. FOR_EACH_VEC( m_Providers, iHook )
  390. {
  391. CBaseEntity *pProvider = m_Providers[iHook].Get();
  392. if ( !pProvider )
  393. continue;
  394. if ( pProvider == pInitiator )
  395. continue;
  396. IHasAttributes *pAttribInterface = GetAttribInterface( pProvider );
  397. Assert( pAttribInterface );
  398. // Don't allow weapons to provide to other weapons being carried by the same person
  399. if ( pInitiatorAttribInterface &&
  400. pAttribInterface->GetAttributeManager()->GetProviderType() == PROVIDER_WEAPON &&
  401. pInitiatorAttribInterface->GetAttributeManager()->GetProviderType() == PROVIDER_WEAPON )
  402. {
  403. continue;
  404. }
  405. flValue = pAttribInterface->GetAttributeManager()->ApplyAttributeFloat( flValue, pInitiator, iszAttribHook, pItemList );
  406. }
  407. // Then see if our owner has any attributes he wants to apply as well.
  408. // i.e. An aura is providing attributes to this weapon's carrier.
  409. IHasAttributes *pMyAttribInterface = GetAttribInterface( m_hOuter.Get().Get() );
  410. Assert( pMyAttribInterface );
  411. if ( pMyAttribInterface && pMyAttribInterface->GetAttributeOwner() )
  412. {
  413. IHasAttributes *pOwnerAttribInterface = GetAttribInterface( pMyAttribInterface->GetAttributeOwner() );
  414. if ( pOwnerAttribInterface )
  415. {
  416. flValue = pOwnerAttribInterface->GetAttributeManager()->ApplyAttributeFloat( flValue, pInitiator, iszAttribHook, pItemList );
  417. }
  418. }
  419. m_bPreventLoopback = false;
  420. return flValue;
  421. }
  422. string_t CAttributeManager::ApplyAttributeString( string_t iszValue, CBaseEntity *pInitiator, string_t iszAttribHook /*= NULL_STRING*/, CUtlVector<CBaseEntity*> *pItemList /*= NULL*/ )
  423. {
  424. VPROF_BUDGET( "CAttributeManager::ApplyAttributeString", VPROF_BUDGETGROUP_ATTRIBUTES );
  425. if ( m_bPreventLoopback || !GetOuter() )
  426. return iszValue;
  427. // We need to prevent loopback between two items both providing to the same entity.
  428. m_bPreventLoopback = true;
  429. IHasAttributes *pInitiatorAttribInterface = GetAttribInterface( pInitiator );
  430. // See if we have any providers. If we do, tell them to apply.
  431. FOR_EACH_VEC( m_Providers, iHook )
  432. {
  433. CBaseEntity *pProvider = m_Providers[iHook].Get();
  434. if ( !pProvider )
  435. continue;
  436. if ( pProvider == pInitiator )
  437. continue;
  438. IHasAttributes *pAttribInterface = GetAttribInterface( pProvider );
  439. Assert( pAttribInterface );
  440. // Don't allow weapons to provide to other weapons being carried by the same person
  441. if ( pInitiatorAttribInterface &&
  442. pAttribInterface->GetAttributeManager()->GetProviderType() == PROVIDER_WEAPON &&
  443. pInitiatorAttribInterface->GetAttributeManager()->GetProviderType() == PROVIDER_WEAPON )
  444. {
  445. continue;
  446. }
  447. iszValue = pAttribInterface->GetAttributeManager()->ApplyAttributeString( iszValue, pInitiator, iszAttribHook, pItemList );
  448. }
  449. // Then see if our owner has any attributes he wants to apply as well.
  450. // i.e. An aura is providing attributes to this weapon's carrier.
  451. IHasAttributes *pMyAttribInterface = GetAttribInterface( m_hOuter.Get().Get() );
  452. Assert( pMyAttribInterface );
  453. if ( pMyAttribInterface->GetAttributeOwner() )
  454. {
  455. IHasAttributes *pOwnerAttribInterface = GetAttribInterface( pMyAttribInterface->GetAttributeOwner() );
  456. if ( pOwnerAttribInterface )
  457. {
  458. iszValue = pOwnerAttribInterface->GetAttributeManager()->ApplyAttributeString( iszValue, pInitiator, iszAttribHook, pItemList );
  459. }
  460. }
  461. m_bPreventLoopback = false;
  462. return iszValue;
  463. }
  464. //=====================================================================================================
  465. // ATTRIBUTE CONTAINER
  466. //=====================================================================================================
  467. //-----------------------------------------------------------------------------
  468. // Purpose: Call this inside your entity's Spawn()
  469. //-----------------------------------------------------------------------------
  470. void CAttributeContainer::InitializeAttributes( CBaseEntity *pEntity )
  471. {
  472. BaseClass::InitializeAttributes( pEntity );
  473. #ifndef CLIENT_DLL
  474. /*
  475. if ( !m_Item.IsValid() )
  476. {
  477. Warning("Item '%s' not setup correctly. Attempting to create attributes on an unitialized item.\n", m_hOuter.Get()->GetDebugName() );
  478. }
  479. */
  480. #endif
  481. m_Item.GetAttributeList()->SetManager( this );
  482. OnAttributeValuesChanged();
  483. }
  484. static void ApplyAttribute( const CEconItemAttributeDefinition *pAttributeDef, float& flValue, const float flValueModifier )
  485. {
  486. Assert( pAttributeDef );
  487. Assert( pAttributeDef->GetAttributeType() );
  488. AssertMsg1( pAttributeDef->GetAttributeType()->BSupportsGameplayModificationAndNetworking(), "Attempt to hook the value of attribute '%s' which doesn't support hooking! Pull the value of the attribute directly using FindAttribute()!", pAttributeDef->GetDefinitionName() );
  489. const int iAttrDescFormat = pAttributeDef->GetDescriptionFormat();
  490. switch ( iAttrDescFormat )
  491. {
  492. case ATTDESCFORM_VALUE_IS_PERCENTAGE:
  493. case ATTDESCFORM_VALUE_IS_INVERTED_PERCENTAGE:
  494. {
  495. flValue *= flValueModifier;
  496. }
  497. break;
  498. case ATTDESCFORM_VALUE_IS_ADDITIVE:
  499. case ATTDESCFORM_VALUE_IS_ADDITIVE_PERCENTAGE:
  500. case ATTDESCFORM_VALUE_IS_PARTICLE_INDEX:
  501. {
  502. flValue += flValueModifier;
  503. }
  504. break;
  505. case ATTDESCFORM_VALUE_IS_KILLSTREAK_IDLEEFFECT_INDEX:
  506. case ATTDESCFORM_VALUE_IS_KILLSTREAKEFFECT_INDEX:
  507. case ATTDESCFORM_VALUE_IS_FROM_LOOKUP_TABLE:
  508. {
  509. flValue = flValueModifier;
  510. }
  511. break;
  512. case ATTDESCFORM_VALUE_IS_OR:
  513. {
  514. int iTmp = flValue;
  515. iTmp |= (int)flValueModifier;
  516. flValue = iTmp;
  517. }
  518. break;
  519. case ATTDESCFORM_VALUE_IS_DATE:
  520. Assert( !"Attempt to apply date attribute in ApplyAttribute()." ); // No-one should be hooking date descriptions
  521. break;
  522. default:
  523. // Unknown value format.
  524. AssertMsg1( false, "Unknown attribute value type %i in ApplyAttribute().", iAttrDescFormat );
  525. break;
  526. }
  527. }
  528. //-----------------------------------------------------------------------------
  529. // Purpose: Given two attributes, return a collated value.
  530. //-----------------------------------------------------------------------------
  531. float CollateAttributeValues( const CEconItemAttributeDefinition *pAttrDef1, const float flAttribValue1, const CEconItemAttributeDefinition *pAttrDef2, const float flAttribValue2 )
  532. {
  533. Assert( pAttrDef1 );
  534. Assert( pAttrDef2 );
  535. AssertMsg2( !Q_stricmp( pAttrDef1->GetAttributeClass(), pAttrDef2->GetAttributeClass() ), "We can only collate attributes of matching definitions: mismatch between '%s' / '%s'!", pAttrDef1->GetAttributeClass(), pAttrDef2->GetAttributeClass() );
  536. AssertMsg2( pAttrDef1->GetDescriptionFormat() == pAttrDef2->GetDescriptionFormat(), "We can only collate attributes of matching description format: mismatch between '%u' / '%u'!", pAttrDef1->GetDescriptionFormat(), pAttrDef2->GetDescriptionFormat() );
  537. const int iAttrDescFormat = pAttrDef1->GetDescriptionFormat();
  538. float flValue = 0;
  539. switch ( iAttrDescFormat )
  540. {
  541. case ATTDESCFORM_VALUE_IS_PERCENTAGE:
  542. case ATTDESCFORM_VALUE_IS_INVERTED_PERCENTAGE:
  543. {
  544. flValue = 1.0;
  545. }
  546. break;
  547. case ATTDESCFORM_VALUE_IS_ADDITIVE:
  548. case ATTDESCFORM_VALUE_IS_ADDITIVE_PERCENTAGE:
  549. case ATTDESCFORM_VALUE_IS_FROM_LOOKUP_TABLE:
  550. case ATTDESCFORM_VALUE_IS_OR:
  551. {
  552. flValue = 0;
  553. }
  554. break;
  555. case ATTDESCFORM_VALUE_IS_DATE:
  556. Assert( !"Attempt to apply date attribute in CollateAttributeValues()." ); // No-one should be hooking date descriptions
  557. break;
  558. default:
  559. // Unknown value format.
  560. AssertMsg1( false, "Unknown attribute value type %i in ApplyAttribute().", iAttrDescFormat );
  561. break;
  562. }
  563. ApplyAttribute( pAttrDef1, flValue, flAttribValue1 );
  564. ApplyAttribute( pAttrDef2, flValue, flAttribValue2 );
  565. return flValue;
  566. }
  567. //-----------------------------------------------------------------------------
  568. // Purpose:
  569. //-----------------------------------------------------------------------------
  570. class CEconItemAttributeIterator_ApplyAttributeFloat : public CEconItemSpecificAttributeIterator
  571. {
  572. public:
  573. CEconItemAttributeIterator_ApplyAttributeFloat( CBaseEntity *pOuter, float flInitialValue, string_t iszAttribHook, CUtlVector<CBaseEntity *> *pItemList )
  574. : m_pOuter( pOuter )
  575. , m_flValue( flInitialValue )
  576. , m_iszAttribHook( iszAttribHook )
  577. , m_pItemList( pItemList )
  578. {
  579. Assert( pOuter );
  580. }
  581. virtual bool OnIterateAttributeValue( const CEconItemAttributeDefinition *pAttrDef, attrib_value_t value )
  582. {
  583. COMPILE_TIME_ASSERT( sizeof( value ) == sizeof( float ) );
  584. Assert( pAttrDef );
  585. if ( pAttrDef->GetCachedClass() != m_iszAttribHook )
  586. return true;
  587. if ( m_pItemList && !m_pItemList->HasElement( m_pOuter ) )
  588. {
  589. m_pItemList->AddToTail( m_pOuter );
  590. }
  591. ApplyAttribute( pAttrDef, m_flValue, *reinterpret_cast<float *>( &value ) );
  592. // We assume that each attribute can only be in the attribute list for a single item once, but we're
  593. // iterating over attribute *classes* here, not unique attribute types, so we carry on looking.
  594. return true;
  595. }
  596. float GetResultValue() const
  597. {
  598. return m_flValue;
  599. }
  600. private:
  601. CBaseEntity *m_pOuter;
  602. float m_flValue;
  603. string_t m_iszAttribHook;
  604. CUtlVector<CBaseEntity *> *m_pItemList;
  605. };
  606. //-----------------------------------------------------------------------------
  607. // Purpose:
  608. //-----------------------------------------------------------------------------
  609. float CAttributeContainer::ApplyAttributeFloat( float flValue, CBaseEntity *pInitiator, string_t iszAttribHook, CUtlVector<CBaseEntity*> *pItemList )
  610. {
  611. if ( m_bPreventLoopback || !GetOuter() )
  612. return flValue;
  613. // We need to prevent loopback between two items both providing to the same entity.
  614. m_bPreventLoopback = true;
  615. // ...
  616. CEconItemAttributeIterator_ApplyAttributeFloat it( GetOuter(), flValue, iszAttribHook, pItemList );
  617. m_Item.IterateAttributes( &it );
  618. m_bPreventLoopback = false;
  619. return BaseClass::ApplyAttributeFloat( it.GetResultValue(), pInitiator, iszAttribHook, pItemList );
  620. }
  621. #ifndef DOTA_DLL
  622. //-----------------------------------------------------------------------------
  623. // Purpose:
  624. //-----------------------------------------------------------------------------
  625. float CAttributeContainerPlayer::ApplyAttributeFloat( float flValue, CBaseEntity *pInitiator, string_t iszAttribHook, CUtlVector<CBaseEntity*> *pItemList )
  626. {
  627. if ( m_bPreventLoopback || !GetOuter() )
  628. return flValue;
  629. m_bPreventLoopback = true;
  630. CEconItemAttributeIterator_ApplyAttributeFloat it( GetOuter(), flValue, iszAttribHook, pItemList );
  631. CBasePlayer *pPlayer = GetPlayer();
  632. if ( pPlayer )
  633. {
  634. pPlayer->m_AttributeList.IterateAttributes( &it );
  635. }
  636. m_bPreventLoopback = false;
  637. return BaseClass::ApplyAttributeFloat( it.GetResultValue(), pInitiator, iszAttribHook, pItemList );
  638. }
  639. #endif
  640. //-----------------------------------------------------------------------------
  641. // Purpose:
  642. //-----------------------------------------------------------------------------
  643. class CEconItemAttributeIterator_ApplyAttributeString : public CEconItemSpecificAttributeIterator
  644. {
  645. public:
  646. CEconItemAttributeIterator_ApplyAttributeString( CBaseEntity *pOuter, string_t iszInitialValue, string_t iszAttribHook, CUtlVector<CBaseEntity *> *pItemList )
  647. : m_pOuter( pOuter )
  648. , m_iszValue( iszInitialValue )
  649. , m_iszAttribHook( iszAttribHook )
  650. , m_pItemList( pItemList )
  651. , m_bFoundString( false )
  652. {
  653. Assert( pOuter );
  654. }
  655. virtual bool OnIterateAttributeValue( const CEconItemAttributeDefinition *pAttrDef, attrib_value_t value )
  656. {
  657. COMPILE_TIME_ASSERT( sizeof( value ) == sizeof( float ) );
  658. // Do we want to process attribute of this type?
  659. Assert( pAttrDef );
  660. Assert( pAttrDef->GetCachedClass() != m_iszAttribHook );
  661. //AssertMsg( 0, "OnIterateAttributeValue of type CAttribute_String, we shouldn't get here." );
  662. return true;
  663. }
  664. virtual bool OnIterateAttributeValue( const CEconItemAttributeDefinition *pAttrDef, const CAttribute_String& value )
  665. {
  666. Assert( pAttrDef );
  667. if ( pAttrDef->GetCachedClass() != m_iszAttribHook )
  668. return true;
  669. if ( FoundString() )
  670. return true;
  671. m_iszValue = AllocPooledString( value.value().c_str() );
  672. m_bFoundString = true;
  673. return true;
  674. }
  675. string_t GetResultValue()
  676. {
  677. return m_iszValue;
  678. }
  679. private:
  680. bool FoundString()
  681. {
  682. // Implement something for the case where there's more than one of the same attribute
  683. AssertMsg( !m_bFoundString, "Already found a string attribute with %s class, return the first attribute found.", STRING( m_iszAttribHook ) );
  684. return m_bFoundString;
  685. }
  686. CBaseEntity *m_pOuter;
  687. string_t m_iszValue;
  688. string_t m_iszAttribHook;
  689. CUtlVector<CBaseEntity *> *m_pItemList;
  690. bool m_bFoundString;
  691. };
  692. //-----------------------------------------------------------------------------
  693. // Purpose:
  694. //-----------------------------------------------------------------------------
  695. string_t CAttributeContainer::ApplyAttributeString( string_t iszValue, CBaseEntity *pInitiator, string_t iszAttribHook /*= NULL_STRING*/, CUtlVector<CBaseEntity*> *pItemList /*= NULL*/ )
  696. {
  697. if ( m_bPreventLoopback || !GetOuter() )
  698. return iszValue;
  699. // We need to prevent loopback between two items both providing to the same entity.
  700. m_bPreventLoopback = true;
  701. // ...
  702. CEconItemAttributeIterator_ApplyAttributeString it( GetOuter(), iszValue, iszAttribHook, pItemList );
  703. m_Item.IterateAttributes( &it );
  704. m_bPreventLoopback = false;
  705. return BaseClass::ApplyAttributeString( it.GetResultValue(), pInitiator, iszAttribHook, pItemList );
  706. }
  707. string_t CAttributeContainerPlayer::ApplyAttributeString( string_t iszValue, CBaseEntity *pInitiator, string_t iszAttribHook /*= NULL_STRING*/, CUtlVector<CBaseEntity*> *pItemList /*= NULL*/ )
  708. {
  709. if ( m_bPreventLoopback || !GetOuter() )
  710. return iszValue;
  711. m_bPreventLoopback = true;
  712. CEconItemAttributeIterator_ApplyAttributeString it( GetOuter(), iszValue, iszAttribHook, pItemList );
  713. CBasePlayer *pPlayer = GetPlayer();
  714. if ( pPlayer )
  715. {
  716. pPlayer->m_AttributeList.IterateAttributes( &it );
  717. }
  718. m_bPreventLoopback = false;
  719. return BaseClass::ApplyAttributeString( it.GetResultValue(), pInitiator, iszAttribHook, pItemList );
  720. }