summaryrefslogtreecommitdiff
path: root/game/shared/econ
diff options
context:
space:
mode:
Diffstat (limited to 'game/shared/econ')
-rw-r--r--game/shared/econ/attribute_manager.cpp881
-rw-r--r--game/shared/econ/attribute_manager.h301
-rw-r--r--game/shared/econ/econ_claimcode.cpp126
-rw-r--r--game/shared/econ/econ_claimcode.h40
-rw-r--r--game/shared/econ/econ_contribution.cpp64
-rw-r--r--game/shared/econ/econ_contribution.h45
-rw-r--r--game/shared/econ/econ_dynamic_recipe.cpp250
-rw-r--r--game/shared/econ/econ_dynamic_recipe.h76
-rw-r--r--game/shared/econ/econ_entity.cpp2078
-rw-r--r--game/shared/econ/econ_entity.h229
-rw-r--r--game/shared/econ/econ_entity_creation.cpp180
-rw-r--r--game/shared/econ/econ_entity_creation.h50
-rw-r--r--game/shared/econ/econ_experiment.cpp19
-rw-r--r--game/shared/econ/econ_experiment.h27
-rw-r--r--game/shared/econ/econ_game_account.cpp17
-rw-r--r--game/shared/econ/econ_game_account.h57
-rw-r--r--game/shared/econ/econ_game_account_client.cpp18
-rw-r--r--game/shared/econ/econ_game_account_client.h29
-rw-r--r--game/shared/econ/econ_game_account_server.cpp40
-rw-r--r--game/shared/econ/econ_game_account_server.h110
-rw-r--r--game/shared/econ/econ_gcmessages.h345
-rw-r--r--game/shared/econ/econ_gcmessages.proto644
-rw-r--r--game/shared/econ/econ_holidays.cpp415
-rw-r--r--game/shared/econ/econ_holidays.h18
-rw-r--r--game/shared/econ/econ_item.cpp2765
-rw-r--r--game/shared/econ/econ_item.h864
-rw-r--r--game/shared/econ/econ_item_constants.cpp970
-rw-r--r--game/shared/econ/econ_item_constants.h965
-rw-r--r--game/shared/econ/econ_item_description.cpp4146
-rw-r--r--game/shared/econ/econ_item_description.h536
-rw-r--r--game/shared/econ/econ_item_factory.cpp331
-rw-r--r--game/shared/econ/econ_item_factory.h75
-rw-r--r--game/shared/econ/econ_item_interface.cpp412
-rw-r--r--game/shared/econ/econ_item_interface.h546
-rw-r--r--game/shared/econ/econ_item_inventory.cpp2340
-rw-r--r--game/shared/econ/econ_item_inventory.h472
-rw-r--r--game/shared/econ/econ_item_preset.cpp338
-rw-r--r--game/shared/econ/econ_item_preset.h102
-rw-r--r--game/shared/econ/econ_item_schema.cpp9705
-rw-r--r--game/shared/econ/econ_item_schema.h3374
-rw-r--r--game/shared/econ/econ_item_system.cpp694
-rw-r--r--game/shared/econ/econ_item_system.h86
-rw-r--r--game/shared/econ/econ_item_tools.cpp1777
-rw-r--r--game/shared/econ/econ_item_tools.h1043
-rw-r--r--game/shared/econ/econ_item_view.cpp1971
-rw-r--r--game/shared/econ/econ_item_view.h455
-rw-r--r--game/shared/econ/econ_quests.cpp71
-rw-r--r--game/shared/econ/econ_quests.h22
-rw-r--r--game/shared/econ/econ_store.cpp1672
-rw-r--r--game/shared/econ/econ_store.h621
-rw-r--r--game/shared/econ/econ_storecategory.cpp267
-rw-r--r--game/shared/econ/econ_storecategory.h110
-rw-r--r--game/shared/econ/econ_wearable.cpp860
-rw-r--r--game/shared/econ/econ_wearable.h138
-rw-r--r--game/shared/econ/game_item_schema.h49
-rw-r--r--game/shared/econ/ihasattributes.h46
-rw-r--r--game/shared/econ/ihasowner.h24
-rw-r--r--game/shared/econ/item_selection_criteria.cpp614
-rw-r--r--game/shared/econ/item_selection_criteria.h290
-rw-r--r--game/shared/econ/localization_provider.cpp199
-rw-r--r--game/shared/econ/localization_provider.h103
61 files changed, 45112 insertions, 0 deletions
diff --git a/game/shared/econ/attribute_manager.cpp b/game/shared/econ/attribute_manager.cpp
new file mode 100644
index 0000000..9c142d3
--- /dev/null
+++ b/game/shared/econ/attribute_manager.cpp
@@ -0,0 +1,881 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+
+#include "cbase.h"
+#include "attribute_manager.h"
+#include "gamestringpool.h"
+#include "saverestore.h"
+#include "saverestore_utlvector.h"
+#include "fmtstr.h"
+#include "KeyValues.h"
+#include "econ_item_system.h"
+
+#if defined( TF_DLL ) || defined( TF_CLIENT_DLL )
+ #include "tf_gamerules.h" // attribute cache flushing; can be generalized if/when Dota needs similar functionality
+#endif // defined( TF_DLL ) || defined( TF_CLIENT_DLL )
+
+#define PROVIDER_PARITY_BITS 6
+#define PROVIDER_PARITY_MASK ((1<<PROVIDER_PARITY_BITS)-1)
+
+//==================================================================================================================
+// ATTRIBUTE MANAGER SAVE/LOAD & NETWORKING
+//===================================================================================================================
+BEGIN_DATADESC_NO_BASE( CAttributeManager )
+ DEFINE_UTLVECTOR( m_Providers, FIELD_EHANDLE ),
+ DEFINE_UTLVECTOR( m_Receivers, FIELD_EHANDLE ),
+ DEFINE_FIELD( m_iReapplyProvisionParity, FIELD_INTEGER ),
+ DEFINE_FIELD( m_hOuter, FIELD_EHANDLE ),
+ // DEFINE_FIELD( m_bPreventLoopback, FIELD_BOOLEAN ), // Don't need to save
+ DEFINE_FIELD( m_ProviderType, FIELD_INTEGER ),
+END_DATADESC()
+
+BEGIN_DATADESC( CAttributeContainer )
+ DEFINE_EMBEDDED( m_Item ),
+END_DATADESC()
+
+#ifndef DOTA_DLL
+BEGIN_DATADESC( CAttributeContainerPlayer )
+END_DATADESC()
+#endif
+
+#ifndef CLIENT_DLL
+EXTERN_SEND_TABLE( DT_ScriptCreatedItem );
+#else
+EXTERN_RECV_TABLE( DT_ScriptCreatedItem );
+#endif
+
+BEGIN_NETWORK_TABLE_NOBASE( CAttributeManager, DT_AttributeManager )
+#ifndef CLIENT_DLL
+ SendPropEHandle( SENDINFO(m_hOuter) ),
+ SendPropInt( SENDINFO(m_ProviderType), 4, SPROP_UNSIGNED ),
+ SendPropInt( SENDINFO(m_iReapplyProvisionParity), PROVIDER_PARITY_BITS, SPROP_UNSIGNED ),
+#else
+ RecvPropEHandle( RECVINFO(m_hOuter) ),
+ RecvPropInt( RECVINFO(m_ProviderType) ),
+ RecvPropInt( RECVINFO(m_iReapplyProvisionParity) ),
+#endif
+END_NETWORK_TABLE()
+
+BEGIN_NETWORK_TABLE_NOBASE( CAttributeContainer, DT_AttributeContainer )
+#ifndef CLIENT_DLL
+ SendPropEHandle( SENDINFO(m_hOuter) ),
+ SendPropInt( SENDINFO(m_ProviderType), 4, SPROP_UNSIGNED ),
+ SendPropInt( SENDINFO(m_iReapplyProvisionParity), PROVIDER_PARITY_BITS, SPROP_UNSIGNED ),
+ SendPropDataTable(SENDINFO_DT(m_Item), &REFERENCE_SEND_TABLE(DT_ScriptCreatedItem)),
+#else
+ RecvPropEHandle( RECVINFO(m_hOuter) ),
+ RecvPropInt( RECVINFO(m_ProviderType) ),
+ RecvPropInt( RECVINFO(m_iReapplyProvisionParity) ),
+ RecvPropDataTable(RECVINFO_DT(m_Item), 0, &REFERENCE_RECV_TABLE(DT_ScriptCreatedItem)),
+#endif
+END_NETWORK_TABLE()
+
+#ifndef DOTA_DLL
+BEGIN_NETWORK_TABLE_NOBASE( CAttributeContainerPlayer, DT_AttributeContainerPlayer )
+#ifndef CLIENT_DLL
+ SendPropEHandle( SENDINFO(m_hOuter) ),
+ SendPropInt( SENDINFO(m_ProviderType), 4, SPROP_UNSIGNED ),
+ SendPropInt( SENDINFO(m_iReapplyProvisionParity), PROVIDER_PARITY_BITS, SPROP_UNSIGNED ),
+ SendPropEHandle( SENDINFO(m_hPlayer) ),
+#else
+ RecvPropEHandle( RECVINFO(m_hOuter) ),
+ RecvPropInt( RECVINFO(m_ProviderType) ),
+ RecvPropInt( RECVINFO(m_iReapplyProvisionParity) ),
+ RecvPropEHandle( RECVINFO( m_hPlayer ) ),
+#endif
+END_NETWORK_TABLE()
+#endif
+
+template< class T > T AttributeConvertFromFloat( float flValue )
+{
+ return static_cast<T>( flValue );
+}
+
+template<> float AttributeConvertFromFloat<float>( float flValue )
+{
+ return flValue;
+}
+
+template<> int AttributeConvertFromFloat<int>( float flValue )
+{
+ return RoundFloatToInt( flValue );
+}
+
+//-----------------------------------------------------------------------------
+// All fields in the object are all initialized to 0.
+//-----------------------------------------------------------------------------
+void *CAttributeManager::operator new( size_t stAllocateBlock )
+{
+ // call into engine to get memory
+ Assert( stAllocateBlock != 0 );
+ void *pMem = malloc( stAllocateBlock );
+ memset( pMem, 0, stAllocateBlock );
+ return pMem;
+};
+
+void *CAttributeManager::operator new( size_t stAllocateBlock, int nBlockUse, const char *pFileName, int nLine )
+{
+ // call into engine to get memory
+ Assert( stAllocateBlock != 0 );
+ void *pMem = malloc( stAllocateBlock );
+ memset( pMem, 0, stAllocateBlock );
+ return pMem;
+}
+
+CAttributeManager::CAttributeManager()
+{
+ m_nCalls = 0;
+ m_nCurrentTick = 0;
+}
+
+#ifdef CLIENT_DLL
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CAttributeManager::OnPreDataChanged( DataUpdateType_t updateType )
+{
+ m_iOldReapplyProvisionParity = m_iReapplyProvisionParity;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CAttributeManager::OnDataChanged( DataUpdateType_t updateType )
+{
+ if ( m_iReapplyProvisionParity != m_iOldReapplyProvisionParity )
+ {
+ // We've changed who we're providing to in some way. Reapply it.
+ IHasAttributes *pAttribInterface = GetAttribInterface( GetOuter() );
+ if ( pAttribInterface )
+ {
+ pAttribInterface->ReapplyProvision();
+ }
+
+ ClearCache();
+
+ m_iOldReapplyProvisionParity = m_iReapplyProvisionParity.Get();
+ }
+}
+#endif // CLIENT_DLL
+
+//-----------------------------------------------------------------------------
+// Purpose: Call this inside your entity's Spawn()
+//-----------------------------------------------------------------------------
+void CAttributeManager::InitializeAttributes( CBaseEntity *pEntity )
+{
+ Assert( GetAttribInterface( pEntity ) );
+ m_hOuter = pEntity;
+ m_bPreventLoopback = false;
+}
+
+//=====================================================================================================
+// ATTRIBUTE PROVIDERS
+//=====================================================================================================
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CAttributeManager::ProvideTo( CBaseEntity *pProvider )
+{
+ IHasAttributes *pOwnerAttribInterface = GetAttribInterface( pProvider );
+ if ( pOwnerAttribInterface )
+ {
+ pOwnerAttribInterface->GetAttributeManager()->AddProvider( m_hOuter.Get() );
+
+#ifndef CLIENT_DLL
+ m_iReapplyProvisionParity = (m_iReapplyProvisionParity + 1) & PROVIDER_PARITY_MASK;
+ NetworkStateChanged();
+#endif
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CAttributeManager::StopProvidingTo( CBaseEntity *pProvider )
+{
+ IHasAttributes *pOwnerAttribInterface = GetAttribInterface( pProvider );
+ if ( pOwnerAttribInterface )
+ {
+ pOwnerAttribInterface->GetAttributeManager()->RemoveProvider( m_hOuter.Get() );
+#ifndef CLIENT_DLL
+ m_iReapplyProvisionParity = (m_iReapplyProvisionParity + 1) & PROVIDER_PARITY_MASK;
+ NetworkStateChanged();
+#endif
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CAttributeManager::AddProvider( CBaseEntity *pProvider )
+{
+ // Make sure he's not already in our list, and prevent circular provision
+ Assert( !IsBeingProvidedToBy(pProvider) );
+ Assert( !IsProvidingTo(pProvider) );
+
+ // Ensure he's allowed to provide
+ IHasAttributes *pProviderAttrInterface = GetAttribInterface( pProvider );
+ Assert( pProviderAttrInterface );
+
+ m_Providers.AddToTail( pProvider );
+ pProviderAttrInterface->GetAttributeManager()->m_Receivers.AddToTail( GetOuter() );
+
+ ClearCache();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CAttributeManager::RemoveProvider( CBaseEntity *pProvider )
+{
+ Assert( pProvider );
+
+ IHasAttributes *pProviderAttrInterface = GetAttribInterface( pProvider );
+ Assert( pProviderAttrInterface );
+
+ if ( !IsBeingProvidedToBy( pProvider ) )
+ return;
+
+ Assert( pProviderAttrInterface->GetAttributeManager()->IsProvidingTo( GetOuter() ) );
+ Assert( pProviderAttrInterface->GetAttributeManager()->m_Receivers.Find( GetOuter() ) != pProviderAttrInterface->GetAttributeManager()->m_Receivers.InvalidIndex() );
+
+ m_Providers.FindAndFastRemove( pProvider );
+ pProviderAttrInterface->GetAttributeManager()->m_Receivers.FindAndFastRemove( GetOuter() );
+
+ ClearCache();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CAttributeManager::ClearCache( void )
+{
+ if ( m_bPreventLoopback )
+ return;
+
+ m_CachedResults.Purge();
+
+ m_bPreventLoopback = true;
+
+ // Tell all providers relying on me that they need to wipe their cache too
+ FOR_EACH_VEC( m_Receivers, i )
+ {
+ IHasAttributes *pAttribInterface = GetAttribInterface( m_Receivers[i].Get() );
+ if ( pAttribInterface )
+ {
+ pAttribInterface->GetAttributeManager()->ClearCache();
+ }
+ }
+
+ // Tell our owner that he needs to clear his too, in case he has attributes affecting him
+ IHasAttributes *pMyAttribInterface = GetAttribInterface( m_hOuter.Get().Get() );
+ if ( pMyAttribInterface )
+ {
+ pMyAttribInterface->GetAttributeManager()->ClearCache();
+ }
+
+ m_bPreventLoopback = false;
+
+#ifndef CLIENT_DLL
+ // Force out client to clear their cache as well
+ m_iReapplyProvisionParity = (m_iReapplyProvisionParity + 1) & PROVIDER_PARITY_MASK;
+ NetworkStateChanged();
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+int CAttributeManager::GetGlobalCacheVersion() const
+{
+#if defined( TF_DLL ) || defined( TF_CLIENT_DLL )
+ return TFGameRules() ? TFGameRules()->GetGlobalAttributeCacheVersion() : 0;
+#else
+ return 0;
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Return true if this entity is providing attributes to the specified entity
+//-----------------------------------------------------------------------------
+bool CAttributeManager::IsProvidingTo( CBaseEntity *pEntity ) const
+{
+ IHasAttributes *pAttribInterface = GetAttribInterface( pEntity );
+ if ( pAttribInterface )
+ {
+ if ( pAttribInterface->GetAttributeManager()->IsBeingProvidedToBy( GetOuter() ) )
+ return true;
+ }
+
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Return true if this entity is being provided attributes by the specified entity
+//-----------------------------------------------------------------------------
+bool CAttributeManager::IsBeingProvidedToBy( CBaseEntity *pEntity ) const
+{
+ return ( m_Providers.Find( pEntity ) != m_Providers.InvalidIndex() );
+}
+
+//=====================================================================================================
+// ATTRIBUTE HOOKS
+//=====================================================================================================
+
+//-----------------------------------------------------------------------------
+// Purpose: Wrapper that checks to see if we've already got the result in our cache
+//-----------------------------------------------------------------------------
+float CAttributeManager::ApplyAttributeFloatWrapper( float flValue, CBaseEntity *pInitiator, string_t iszAttribHook, CUtlVector<CBaseEntity*> *pItemList )
+{
+ VPROF_BUDGET( "CAttributeManager::ApplyAttributeFloatWrapper", VPROF_BUDGETGROUP_ATTRIBUTES );
+
+#ifdef DEBUG
+ AssertMsg1( m_nCalls != 5000, "%d calls for attributes in a single tick. This is slow and bad.", m_nCalls );
+
+ if( m_nCurrentTick != gpGlobals->tickcount )
+ {
+ m_nCalls = 0;
+ m_nCurrentTick = gpGlobals->tickcount;
+ }
+
+ ++m_nCalls;
+#endif
+
+ // Have we requested a global attribute cache flush?
+ const int iGlobalCacheVersion = GetGlobalCacheVersion();
+ if ( m_iCacheVersion != iGlobalCacheVersion )
+ {
+ ClearCache();
+ m_iCacheVersion = iGlobalCacheVersion;
+ }
+
+ // We can't cache off item references so if we asked for them we need to execute the whole slow path.
+ if ( !pItemList )
+ {
+ int iCount = m_CachedResults.Count();
+ for ( int i = iCount-1; i >= 0; i-- )
+ {
+ if ( m_CachedResults[i].iAttribHook == iszAttribHook )
+ {
+ if ( m_CachedResults[i].in.fl == flValue )
+ return m_CachedResults[i].out.fl;
+
+ // We've got a cached result for a different flIn value. Remove the cached result to
+ // prevent stacking up entries for different requests (i.e. crit chance)
+ m_CachedResults.Remove(i);
+ break;
+ }
+ }
+ }
+
+ // Wasn't in cache, or we need item references. Do the work.
+ float flResult = ApplyAttributeFloat( flValue, pInitiator, iszAttribHook, pItemList );
+
+ // Add it to our cache if we didn't ask for item references. We could add the result value here
+ // even if we did but we'd need to walk the cache to search for an old entry to overwrite first.
+ if ( !pItemList )
+ {
+ int iIndex = m_CachedResults.AddToTail();
+ m_CachedResults[iIndex].in.fl = flValue;
+ m_CachedResults[iIndex].out.fl = flResult;
+ m_CachedResults[iIndex].iAttribHook = iszAttribHook;
+ }
+
+ return flResult;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Wrapper that checks to see if we've already got the result in our cache
+//-----------------------------------------------------------------------------
+string_t CAttributeManager::ApplyAttributeStringWrapper( string_t iszValue, CBaseEntity *pInitiator, string_t iszAttribHook, CUtlVector<CBaseEntity*> *pItemList /*= NULL*/ )
+{
+ // Have we requested a global attribute cache flush?
+ const int iGlobalCacheVersion = GetGlobalCacheVersion();
+ if ( m_iCacheVersion != iGlobalCacheVersion )
+ {
+ ClearCache();
+ m_iCacheVersion = iGlobalCacheVersion;
+ }
+
+ // We can't cache off item references so if we asked for them we need to execute the whole slow path.
+ if ( !pItemList )
+ {
+ int iCount = m_CachedResults.Count();
+ for ( int i = iCount-1; i >= 0; i-- )
+ {
+ if ( m_CachedResults[i].iAttribHook == iszAttribHook )
+ {
+ if ( m_CachedResults[i].in.isz == iszValue )
+ {
+ return m_CachedResults[i].out.isz;
+ }
+
+ // We've got a cached result for a different flIn value. Remove the cached result to
+ // prevent stacking up entries for different requests (i.e. crit chance)
+ m_CachedResults.Remove(i);
+ break;
+ }
+ }
+ }
+
+ // Wasn't in cache, or we need item references. Do the work.
+ string_t iszOut = ApplyAttributeString( iszValue, pInitiator, iszAttribHook, pItemList );
+
+ // Add it to our cache if we didn't ask for item references. We could add the result value here
+ // even if we did but we'd need to walk the cache to search for an old entry to overwrite first.
+ if ( !pItemList )
+ {
+ int iIndex = m_CachedResults.AddToTail();
+ m_CachedResults[iIndex].in.isz = iszValue;
+ m_CachedResults[iIndex].out.isz = iszOut;
+ m_CachedResults[iIndex].iAttribHook = iszAttribHook;
+ }
+
+ return iszOut;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+float CAttributeManager::ApplyAttributeFloat( float flValue, CBaseEntity *pInitiator, string_t iszAttribHook, CUtlVector<CBaseEntity*> *pItemList )
+{
+ VPROF_BUDGET( "CAttributeManager::ApplyAttributeFloat", VPROF_BUDGETGROUP_ATTRIBUTES );
+
+ if ( m_bPreventLoopback || !GetOuter() )
+ return flValue;
+
+ // We need to prevent loopback between two items both providing to the same entity.
+ m_bPreventLoopback = true;
+
+ IHasAttributes *pInitiatorAttribInterface = GetAttribInterface( pInitiator );
+
+ // See if we have any providers. If we do, tell them to apply.
+ FOR_EACH_VEC( m_Providers, iHook )
+ {
+ CBaseEntity *pProvider = m_Providers[iHook].Get();
+
+ if ( !pProvider )
+ continue;
+
+ if ( pProvider == pInitiator )
+ continue;
+
+ IHasAttributes *pAttribInterface = GetAttribInterface( pProvider );
+ Assert( pAttribInterface );
+
+ // Don't allow weapons to provide to other weapons being carried by the same person
+ if ( pInitiatorAttribInterface &&
+ pAttribInterface->GetAttributeManager()->GetProviderType() == PROVIDER_WEAPON &&
+ pInitiatorAttribInterface->GetAttributeManager()->GetProviderType() == PROVIDER_WEAPON )
+ {
+ continue;
+ }
+
+ flValue = pAttribInterface->GetAttributeManager()->ApplyAttributeFloat( flValue, pInitiator, iszAttribHook, pItemList );
+ }
+
+ // Then see if our owner has any attributes he wants to apply as well.
+ // i.e. An aura is providing attributes to this weapon's carrier.
+ IHasAttributes *pMyAttribInterface = GetAttribInterface( m_hOuter.Get().Get() );
+ Assert( pMyAttribInterface );
+
+ if ( pMyAttribInterface && pMyAttribInterface->GetAttributeOwner() )
+ {
+ IHasAttributes *pOwnerAttribInterface = GetAttribInterface( pMyAttribInterface->GetAttributeOwner() );
+ if ( pOwnerAttribInterface )
+ {
+ flValue = pOwnerAttribInterface->GetAttributeManager()->ApplyAttributeFloat( flValue, pInitiator, iszAttribHook, pItemList );
+ }
+ }
+
+ m_bPreventLoopback = false;
+
+ return flValue;
+}
+
+string_t CAttributeManager::ApplyAttributeString( string_t iszValue, CBaseEntity *pInitiator, string_t iszAttribHook /*= NULL_STRING*/, CUtlVector<CBaseEntity*> *pItemList /*= NULL*/ )
+{
+ VPROF_BUDGET( "CAttributeManager::ApplyAttributeString", VPROF_BUDGETGROUP_ATTRIBUTES );
+
+ if ( m_bPreventLoopback || !GetOuter() )
+ return iszValue;
+
+ // We need to prevent loopback between two items both providing to the same entity.
+ m_bPreventLoopback = true;
+
+ IHasAttributes *pInitiatorAttribInterface = GetAttribInterface( pInitiator );
+
+ // See if we have any providers. If we do, tell them to apply.
+ FOR_EACH_VEC( m_Providers, iHook )
+ {
+ CBaseEntity *pProvider = m_Providers[iHook].Get();
+
+ if ( !pProvider )
+ continue;
+
+ if ( pProvider == pInitiator )
+ continue;
+
+ IHasAttributes *pAttribInterface = GetAttribInterface( pProvider );
+ Assert( pAttribInterface );
+
+ // Don't allow weapons to provide to other weapons being carried by the same person
+ if ( pInitiatorAttribInterface &&
+ pAttribInterface->GetAttributeManager()->GetProviderType() == PROVIDER_WEAPON &&
+ pInitiatorAttribInterface->GetAttributeManager()->GetProviderType() == PROVIDER_WEAPON )
+ {
+ continue;
+ }
+
+ iszValue = pAttribInterface->GetAttributeManager()->ApplyAttributeString( iszValue, pInitiator, iszAttribHook, pItemList );
+ }
+
+ // Then see if our owner has any attributes he wants to apply as well.
+ // i.e. An aura is providing attributes to this weapon's carrier.
+ IHasAttributes *pMyAttribInterface = GetAttribInterface( m_hOuter.Get().Get() );
+ Assert( pMyAttribInterface );
+
+ if ( pMyAttribInterface->GetAttributeOwner() )
+ {
+ IHasAttributes *pOwnerAttribInterface = GetAttribInterface( pMyAttribInterface->GetAttributeOwner() );
+ if ( pOwnerAttribInterface )
+ {
+ iszValue = pOwnerAttribInterface->GetAttributeManager()->ApplyAttributeString( iszValue, pInitiator, iszAttribHook, pItemList );
+ }
+ }
+
+ m_bPreventLoopback = false;
+
+ return iszValue;
+}
+
+//=====================================================================================================
+// ATTRIBUTE CONTAINER
+//=====================================================================================================
+
+//-----------------------------------------------------------------------------
+// Purpose: Call this inside your entity's Spawn()
+//-----------------------------------------------------------------------------
+void CAttributeContainer::InitializeAttributes( CBaseEntity *pEntity )
+{
+ BaseClass::InitializeAttributes( pEntity );
+
+#ifndef CLIENT_DLL
+ /*
+ if ( !m_Item.IsValid() )
+ {
+ Warning("Item '%s' not setup correctly. Attempting to create attributes on an unitialized item.\n", m_hOuter.Get()->GetDebugName() );
+ }
+ */
+#endif
+
+ m_Item.GetAttributeList()->SetManager( this );
+
+ OnAttributeValuesChanged();
+}
+
+static void ApplyAttribute( const CEconItemAttributeDefinition *pAttributeDef, float& flValue, const float flValueModifier )
+{
+ Assert( pAttributeDef );
+ Assert( pAttributeDef->GetAttributeType() );
+ 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() );
+
+ const int iAttrDescFormat = pAttributeDef->GetDescriptionFormat();
+
+ switch ( iAttrDescFormat )
+ {
+ case ATTDESCFORM_VALUE_IS_PERCENTAGE:
+ case ATTDESCFORM_VALUE_IS_INVERTED_PERCENTAGE:
+ {
+ flValue *= flValueModifier;
+ }
+ break;
+
+ case ATTDESCFORM_VALUE_IS_ADDITIVE:
+ case ATTDESCFORM_VALUE_IS_ADDITIVE_PERCENTAGE:
+ case ATTDESCFORM_VALUE_IS_PARTICLE_INDEX:
+ {
+ flValue += flValueModifier;
+ }
+ break;
+
+ case ATTDESCFORM_VALUE_IS_KILLSTREAK_IDLEEFFECT_INDEX:
+ case ATTDESCFORM_VALUE_IS_KILLSTREAKEFFECT_INDEX:
+ case ATTDESCFORM_VALUE_IS_FROM_LOOKUP_TABLE:
+ {
+ flValue = flValueModifier;
+ }
+ break;
+
+ case ATTDESCFORM_VALUE_IS_OR:
+ {
+ int iTmp = flValue;
+ iTmp |= (int)flValueModifier;
+ flValue = iTmp;
+ }
+ break;
+
+ case ATTDESCFORM_VALUE_IS_DATE:
+ Assert( !"Attempt to apply date attribute in ApplyAttribute()." ); // No-one should be hooking date descriptions
+ break;
+
+ default:
+ // Unknown value format.
+ AssertMsg1( false, "Unknown attribute value type %i in ApplyAttribute().", iAttrDescFormat );
+ break;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Given two attributes, return a collated value.
+//-----------------------------------------------------------------------------
+float CollateAttributeValues( const CEconItemAttributeDefinition *pAttrDef1, const float flAttribValue1, const CEconItemAttributeDefinition *pAttrDef2, const float flAttribValue2 )
+{
+ Assert( pAttrDef1 );
+ Assert( pAttrDef2 );
+ AssertMsg2( !Q_stricmp( pAttrDef1->GetAttributeClass(), pAttrDef2->GetAttributeClass() ), "We can only collate attributes of matching definitions: mismatch between '%s' / '%s'!", pAttrDef1->GetAttributeClass(), pAttrDef2->GetAttributeClass() );
+ AssertMsg2( pAttrDef1->GetDescriptionFormat() == pAttrDef2->GetDescriptionFormat(), "We can only collate attributes of matching description format: mismatch between '%u' / '%u'!", pAttrDef1->GetDescriptionFormat(), pAttrDef2->GetDescriptionFormat() );
+
+ const int iAttrDescFormat = pAttrDef1->GetDescriptionFormat();
+
+ float flValue = 0;
+ switch ( iAttrDescFormat )
+ {
+ case ATTDESCFORM_VALUE_IS_PERCENTAGE:
+ case ATTDESCFORM_VALUE_IS_INVERTED_PERCENTAGE:
+ {
+ flValue = 1.0;
+ }
+ break;
+
+ case ATTDESCFORM_VALUE_IS_ADDITIVE:
+ case ATTDESCFORM_VALUE_IS_ADDITIVE_PERCENTAGE:
+ case ATTDESCFORM_VALUE_IS_FROM_LOOKUP_TABLE:
+ case ATTDESCFORM_VALUE_IS_OR:
+ {
+ flValue = 0;
+ }
+ break;
+
+ case ATTDESCFORM_VALUE_IS_DATE:
+ Assert( !"Attempt to apply date attribute in CollateAttributeValues()." ); // No-one should be hooking date descriptions
+ break;
+
+ default:
+ // Unknown value format.
+ AssertMsg1( false, "Unknown attribute value type %i in ApplyAttribute().", iAttrDescFormat );
+ break;
+ }
+
+ ApplyAttribute( pAttrDef1, flValue, flAttribValue1 );
+ ApplyAttribute( pAttrDef2, flValue, flAttribValue2 );
+
+ return flValue;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+class CEconItemAttributeIterator_ApplyAttributeFloat : public CEconItemSpecificAttributeIterator
+{
+public:
+ CEconItemAttributeIterator_ApplyAttributeFloat( CBaseEntity *pOuter, float flInitialValue, string_t iszAttribHook, CUtlVector<CBaseEntity *> *pItemList )
+ : m_pOuter( pOuter )
+ , m_flValue( flInitialValue )
+ , m_iszAttribHook( iszAttribHook )
+ , m_pItemList( pItemList )
+ {
+ Assert( pOuter );
+ }
+
+ virtual bool OnIterateAttributeValue( const CEconItemAttributeDefinition *pAttrDef, attrib_value_t value )
+ {
+ COMPILE_TIME_ASSERT( sizeof( value ) == sizeof( float ) );
+
+ Assert( pAttrDef );
+
+ if ( pAttrDef->GetCachedClass() != m_iszAttribHook )
+ return true;
+
+ if ( m_pItemList && !m_pItemList->HasElement( m_pOuter ) )
+ {
+ m_pItemList->AddToTail( m_pOuter );
+ }
+
+ ApplyAttribute( pAttrDef, m_flValue, *reinterpret_cast<float *>( &value ) );
+
+ // We assume that each attribute can only be in the attribute list for a single item once, but we're
+ // iterating over attribute *classes* here, not unique attribute types, so we carry on looking.
+ return true;
+ }
+
+ float GetResultValue() const
+ {
+ return m_flValue;
+ }
+
+private:
+ CBaseEntity *m_pOuter;
+ float m_flValue;
+ string_t m_iszAttribHook;
+ CUtlVector<CBaseEntity *> *m_pItemList;
+};
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+float CAttributeContainer::ApplyAttributeFloat( float flValue, CBaseEntity *pInitiator, string_t iszAttribHook, CUtlVector<CBaseEntity*> *pItemList )
+{
+ if ( m_bPreventLoopback || !GetOuter() )
+ return flValue;
+
+ // We need to prevent loopback between two items both providing to the same entity.
+ m_bPreventLoopback = true;
+
+ // ...
+ CEconItemAttributeIterator_ApplyAttributeFloat it( GetOuter(), flValue, iszAttribHook, pItemList );
+ m_Item.IterateAttributes( &it );
+
+ m_bPreventLoopback = false;
+
+ return BaseClass::ApplyAttributeFloat( it.GetResultValue(), pInitiator, iszAttribHook, pItemList );
+}
+
+#ifndef DOTA_DLL
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+float CAttributeContainerPlayer::ApplyAttributeFloat( float flValue, CBaseEntity *pInitiator, string_t iszAttribHook, CUtlVector<CBaseEntity*> *pItemList )
+{
+ if ( m_bPreventLoopback || !GetOuter() )
+ return flValue;
+
+ m_bPreventLoopback = true;
+
+ CEconItemAttributeIterator_ApplyAttributeFloat it( GetOuter(), flValue, iszAttribHook, pItemList );
+
+ CBasePlayer *pPlayer = GetPlayer();
+ if ( pPlayer )
+ {
+ pPlayer->m_AttributeList.IterateAttributes( &it );
+ }
+
+ m_bPreventLoopback = false;
+
+ return BaseClass::ApplyAttributeFloat( it.GetResultValue(), pInitiator, iszAttribHook, pItemList );
+}
+
+
+#endif
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+class CEconItemAttributeIterator_ApplyAttributeString : public CEconItemSpecificAttributeIterator
+{
+public:
+ CEconItemAttributeIterator_ApplyAttributeString( CBaseEntity *pOuter, string_t iszInitialValue, string_t iszAttribHook, CUtlVector<CBaseEntity *> *pItemList )
+ : m_pOuter( pOuter )
+ , m_iszValue( iszInitialValue )
+ , m_iszAttribHook( iszAttribHook )
+ , m_pItemList( pItemList )
+ , m_bFoundString( false )
+ {
+ Assert( pOuter );
+ }
+
+ virtual bool OnIterateAttributeValue( const CEconItemAttributeDefinition *pAttrDef, attrib_value_t value )
+ {
+ COMPILE_TIME_ASSERT( sizeof( value ) == sizeof( float ) );
+
+ // Do we want to process attribute of this type?
+ Assert( pAttrDef );
+ Assert( pAttrDef->GetCachedClass() != m_iszAttribHook );
+ //AssertMsg( 0, "OnIterateAttributeValue of type CAttribute_String, we shouldn't get here." );
+
+ return true;
+ }
+
+ virtual bool OnIterateAttributeValue( const CEconItemAttributeDefinition *pAttrDef, const CAttribute_String& value )
+ {
+ Assert( pAttrDef );
+
+ if ( pAttrDef->GetCachedClass() != m_iszAttribHook )
+ return true;
+
+ if ( FoundString() )
+ return true;
+
+ m_iszValue = AllocPooledString( value.value().c_str() );
+
+ m_bFoundString = true;
+
+ return true;
+ }
+
+ string_t GetResultValue()
+ {
+ return m_iszValue;
+ }
+
+private:
+ bool FoundString()
+ {
+ // Implement something for the case where there's more than one of the same attribute
+ AssertMsg( !m_bFoundString, "Already found a string attribute with %s class, return the first attribute found.", STRING( m_iszAttribHook ) );
+
+ return m_bFoundString;
+ }
+
+ CBaseEntity *m_pOuter;
+ string_t m_iszValue;
+ string_t m_iszAttribHook;
+ CUtlVector<CBaseEntity *> *m_pItemList;
+ bool m_bFoundString;
+};
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+string_t CAttributeContainer::ApplyAttributeString( string_t iszValue, CBaseEntity *pInitiator, string_t iszAttribHook /*= NULL_STRING*/, CUtlVector<CBaseEntity*> *pItemList /*= NULL*/ )
+{
+ if ( m_bPreventLoopback || !GetOuter() )
+ return iszValue;
+
+ // We need to prevent loopback between two items both providing to the same entity.
+ m_bPreventLoopback = true;
+
+ // ...
+ CEconItemAttributeIterator_ApplyAttributeString it( GetOuter(), iszValue, iszAttribHook, pItemList );
+ m_Item.IterateAttributes( &it );
+
+ m_bPreventLoopback = false;
+
+ return BaseClass::ApplyAttributeString( it.GetResultValue(), pInitiator, iszAttribHook, pItemList );
+}
+
+
+string_t CAttributeContainerPlayer::ApplyAttributeString( string_t iszValue, CBaseEntity *pInitiator, string_t iszAttribHook /*= NULL_STRING*/, CUtlVector<CBaseEntity*> *pItemList /*= NULL*/ )
+{
+ if ( m_bPreventLoopback || !GetOuter() )
+ return iszValue;
+
+ m_bPreventLoopback = true;
+
+ CEconItemAttributeIterator_ApplyAttributeString it( GetOuter(), iszValue, iszAttribHook, pItemList );
+
+ CBasePlayer *pPlayer = GetPlayer();
+ if ( pPlayer )
+ {
+ pPlayer->m_AttributeList.IterateAttributes( &it );
+ }
+
+ m_bPreventLoopback = false;
+
+ return BaseClass::ApplyAttributeString( it.GetResultValue(), pInitiator, iszAttribHook, pItemList );
+}
diff --git a/game/shared/econ/attribute_manager.h b/game/shared/econ/attribute_manager.h
new file mode 100644
index 0000000..eb83675
--- /dev/null
+++ b/game/shared/econ/attribute_manager.h
@@ -0,0 +1,301 @@
+//========= 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<vartype>( retval, #hookName, static_cast<const CBaseEntity*>( 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>( float flValue );
+template<> int AttributeConvertFromFloat<int>( 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 <class T> static T AttribHookValue( T TValue, const char *pszAttribHook, const CBaseEntity *pEntity, CUtlVector<CBaseEntity*> *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 <class T> static void TypedAttribHookValueInternal( T& out, T TValue, string_t iszAttribHook, const CBaseEntity *pEntity, IHasAttributes *pAttribInterface, CUtlVector<CBaseEntity*> *pItemList )
+ {
+ float flValue = pAttribInterface->GetAttributeManager()->ApplyAttributeFloatWrapper( static_cast<float>( TValue ), const_cast<CBaseEntity *>( pEntity ), iszAttribHook, pItemList );
+
+ out = AttributeConvertFromFloat<T>( flValue );
+ }
+
+ static void TypedAttribHookValueInternal( CAttribute_String& out, const CAttribute_String& TValue, string_t iszAttribHook, const CBaseEntity *pEntity, IHasAttributes *pAttribInterface, CUtlVector<CBaseEntity*> *pItemList )
+ {
+ string_t iszIn = AllocPooledString( TValue.value().c_str() );
+ string_t iszOut = pAttribInterface->GetAttributeManager()->ApplyAttributeStringWrapper( iszIn, const_cast<CBaseEntity *>( 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 <class T> static void AttribHookValueInternal( T& out, T TValue, const char *pszAttribHook, const CBaseEntity *pEntity, IHasAttributes *pAttribInterface, CUtlVector<CBaseEntity*> *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<CBaseEntity*> *pItemList = NULL );
+ virtual string_t ApplyAttributeString( string_t iszValue, CBaseEntity *pInitiator, string_t iszAttribHook = NULL_STRING, CUtlVector<CBaseEntity*> *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<EHANDLE> m_Providers; // entities that we receive attribute data *from*
+ CUtlVector<EHANDLE> 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<CBaseEntity*> *pItemList = NULL );
+ virtual string_t ApplyAttributeStringWrapper( string_t iszValue, CBaseEntity *pInitiator, string_t iszAttribHook, CUtlVector<CBaseEntity*> *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<cached_attribute_t> 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<CBaseEntity*> *pItemList = NULL ) OVERRIDE;
+ virtual string_t ApplyAttributeString( string_t iszValue, CBaseEntity *pInitiator, string_t iszAttribHook = NULL_STRING, CUtlVector<CBaseEntity*> *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<CBaseEntity*> *pItemList = NULL ) OVERRIDE;
+ virtual string_t ApplyAttributeString( string_t iszValue, CBaseEntity *pInitiator, string_t iszAttribHook = NULL_STRING, CUtlVector<CBaseEntity*> *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
diff --git a/game/shared/econ/econ_claimcode.cpp b/game/shared/econ/econ_claimcode.cpp
new file mode 100644
index 0000000..94f15a9
--- /dev/null
+++ b/game/shared/econ/econ_claimcode.cpp
@@ -0,0 +1,126 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Code for the CEconClaimCode object
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include "cbase.h"
+
+#include "econ_item_tools.h"
+#include "econ_claimcode.h"
+
+using namespace GCSDK;
+
+#ifdef GC
+IMPLEMENT_CLASS_MEMPOOL( CEconClaimCode, 10 * 1000, UTLMEMORYPOOL_GROW_SLOW );
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+bool CEconClaimCode::BYieldingAddInsertToTransaction( GCSDK::CSQLAccess & sqlAccess )
+{
+ CSchAssignedClaimCode schCode;
+ WriteToRecord( &schCode );
+ return CSchemaSharedObjectHelper::BYieldingAddInsertToTransaction( sqlAccess, &schCode );
+}
+
+void CEconClaimCode::WriteToRecord( CSchAssignedClaimCode *pClaimCode )
+{
+ pClaimCode->m_unAccountID = Obj().account_id();
+ pClaimCode->m_unCodeType = Obj().code_type();
+ pClaimCode->m_rtime32TimeAcquired = Obj().time_acquired();
+ WRITE_VAR_CHAR_FIELD( *pClaimCode, VarCharCode, Obj().code().c_str() );
+}
+
+void CEconClaimCode::ReadFromRecord( const CSchAssignedClaimCode & code )
+{
+ const char *pchCode = READ_VAR_CHAR_FIELD( code, m_VarCharCode );
+ Obj().set_code_type( code.m_unCodeType );
+ Obj().set_time_acquired( code.m_rtime32TimeAcquired );
+ Obj().set_code( pchCode );
+}
+
+
+bool BBuildRedemptionURL( CEconClaimCode *pClaimCode, CUtlString &redemptionURL )
+{
+ const CEconItemDefinition *pItemDef = GEconManager()->GetItemSchema()->GetItemDefinition( pClaimCode->Obj().code_type() );
+ if ( pItemDef )
+ {
+ const char *pOriginalURL = pItemDef->GetDefinitionString( "redeem_url" );
+ const char *code = pClaimCode->Obj().code().c_str();
+ char url[1024];
+ if ( Q_StrSubst( pOriginalURL, "CLAIMCODE", code, url, sizeof( url ) ) )
+ {
+ redemptionURL = url;
+ return true;
+ }
+ }
+ return false;
+}
+
+// -----------------------------------------------------------------------------
+// Purpose: Gets a summary of what's going on with the GC
+// -----------------------------------------------------------------------------
+class CJobWG_GetPromoCodes : public CGCGameBaseWGJob
+{
+public:
+ CJobWG_GetPromoCodes( CGCGameBase *pGC ) : CGCGameBaseWGJob( pGC ) {}
+
+ virtual bool BYieldingRunJobFromRequest( KeyValues *pkvRequest, KeyValues *pkvResponse );
+
+private:
+};
+
+
+bool CJobWG_GetPromoCodes::BYieldingRunJobFromRequest( KeyValues *pkvRequest, KeyValues *pkvResponse )
+{
+ CSteamID actorID( pkvRequest->GetUint64( "token/steamid" ) );
+
+ KeyValues *pkvPromoCodes = pkvResponse->FindKey( "promo_codes", true );
+
+ CSharedObjectCache *pSOCache = m_pGCGameBase->YieldingFindOrLoadSOCache( actorID );
+ if ( pSOCache == NULL )
+ return true;
+
+ CSharedObjectTypeCache *pTypeCache = pSOCache->FindBaseTypeCache( k_EEconTypeClaimCode );
+ if ( pTypeCache == NULL )
+ return true;
+
+ for ( uint32 i = 0; i < pTypeCache->GetCount(); ++i )
+ {
+ CEconClaimCode *pClaimCode = (CEconClaimCode*)pTypeCache->GetObject( i );
+ const CEconItemDefinition *pItemDef = GetItemSchema()->GetItemDefinition( pClaimCode->Obj().code_type() );
+ if ( pItemDef == NULL )
+ continue;
+
+ const CEconTool_ClaimCode *pEconClaimCodeTool = pItemDef->GetTypedEconTool<CEconTool_ClaimCode>();
+ if ( pEconClaimCodeTool == NULL )
+ continue;
+
+ const char *pClaimCodeName = pEconClaimCodeTool->GetClaimType();
+ if ( pClaimCodeName == NULL )
+ continue;
+
+ CUtlString claimURL;
+ if ( BBuildRedemptionURL( pClaimCode, claimURL ) == false )
+ {
+ SetErrorMessage( pkvResponse, CFmtStr( "Unable to construct redemption url for: %s", pClaimCodeName ), k_EResultFail );
+ continue;
+ }
+ const char *code = pClaimCode->Obj().code().c_str();
+ // finally populate the key values
+ KeyValues *pkvPromoCode = pkvPromoCodes->CreateNewKey();
+ pkvPromoCode->SetString( "code_name", pClaimCodeName );
+ pkvPromoCode->SetInt( "timestamp", pClaimCode->Obj().time_acquired() );
+ pkvPromoCode->SetString( "code", code );
+ pkvPromoCode->SetString( "redeem_url", claimURL.Get() );
+ }
+
+ return true;
+}
+
+DECLARE_GCWG_JOB( CGCEcon, CJobWG_GetPromoCodes, "GetPromoCodes", k_EGCWebApiPriv_Session )
+END_DECLARE_GCWG_JOB( CJobWG_GetPromoCodes);
+
+#endif
diff --git a/game/shared/econ/econ_claimcode.h b/game/shared/econ/econ_claimcode.h
new file mode 100644
index 0000000..06b85f6
--- /dev/null
+++ b/game/shared/econ/econ_claimcode.h
@@ -0,0 +1,40 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Holds the CEconClaimCode object
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef ECON_CLAIMCODE_H
+#define ECON_CLAIMCODE_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "gcsdk/protobufsharedobject.h"
+
+//---------------------------------------------------------------------------------
+// Purpose: All the account-level information that the GC tracks for TF
+//---------------------------------------------------------------------------------
+class CEconClaimCode : public GCSDK::CProtoBufSharedObject< CSOEconClaimCode, k_EEconTypeClaimCode >
+{
+#ifdef GC
+ DECLARE_CLASS_MEMPOOL( CEconClaimCode );
+#endif
+
+public:
+
+#ifdef GC
+ virtual bool BYieldingAddInsertToTransaction( GCSDK::CSQLAccess & sqlAccess );
+
+ void WriteToRecord( CSchAssignedClaimCode *pClaimCode );
+ void ReadFromRecord( const CSchAssignedClaimCode & mapContribution );
+#endif
+};
+
+#ifdef GC
+bool BBuildRedemptionURL( CEconClaimCode *pClaimCode, CUtlString &redemptionURL );
+#endif
+
+#endif // ECON_CLAIMCODE_H
+
diff --git a/game/shared/econ/econ_contribution.cpp b/game/shared/econ/econ_contribution.cpp
new file mode 100644
index 0000000..25dc060
--- /dev/null
+++ b/game/shared/econ/econ_contribution.cpp
@@ -0,0 +1,64 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Code for the CTFMapContribution object
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include "cbase.h"
+
+#include "econ_contribution.h"
+
+using namespace GCSDK;
+
+#ifdef GC
+IMPLEMENT_CLASS_MEMPOOL( CTFMapContribution, 10 * 1000, UTLMEMORYPOOL_GROW_SLOW );
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+bool CTFMapContribution::BYieldingAddInsertToTransaction( GCSDK::CSQLAccess & sqlAccess )
+{
+ CSchMapContribution schMapContribution;
+ WriteToRecord( &schMapContribution );
+ return CSchemaSharedObjectHelper::BYieldingAddInsertToTransaction( sqlAccess, &schMapContribution );
+}
+
+bool CTFMapContribution::BYieldingAddWriteToTransaction( GCSDK::CSQLAccess & sqlAccess, const CUtlVector< int > &fields )
+{
+ CSchMapContribution schMapContribution;
+ WriteToRecord( &schMapContribution );
+ CColumnSet csDatabaseDirty( schMapContribution.GetPSchema()->GetRecordInfo() );
+ csDatabaseDirty.MakeEmpty();
+ if ( fields.HasElement( CSOTFMapContribution::kContributionLevelFieldNumber ) )
+ {
+ csDatabaseDirty.BAddColumn( CSchMapContribution::k_iField_unContributionLevel );
+ }
+ return CSchemaSharedObjectHelper::BYieldingAddWriteToTransaction( sqlAccess, &schMapContribution, csDatabaseDirty );
+}
+
+bool CTFMapContribution::BYieldingAddRemoveToTransaction( GCSDK::CSQLAccess & sqlAccess )
+{
+ CSchMapContribution schMapContribution;
+ WriteToRecord( &schMapContribution );
+ return CSchemaSharedObjectHelper::BYieldingAddRemoveToTransaction( sqlAccess, &schMapContribution );
+}
+
+void CTFMapContribution::WriteToRecord( CSchMapContribution *pMapContribution ) const
+{
+ pMapContribution->m_unAccountID = Obj().account_id();
+ pMapContribution->m_unDefIndex = Obj().def_index();
+ pMapContribution->m_unContributionLevel = Obj().contribution_level();
+}
+
+
+void CTFMapContribution::ReadFromRecord( const CSchMapContribution & mapContribution )
+{
+ Obj().set_account_id( mapContribution.m_unAccountID );
+ Obj().set_def_index( mapContribution.m_unDefIndex );
+ Obj().set_contribution_level( mapContribution.m_unContributionLevel );
+}
+
+
+
+#endif
diff --git a/game/shared/econ/econ_contribution.h b/game/shared/econ/econ_contribution.h
new file mode 100644
index 0000000..b46f87f
--- /dev/null
+++ b/game/shared/econ/econ_contribution.h
@@ -0,0 +1,45 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Holds the CTFMapContribution object
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef TFMAPCONTRIBUTION_H
+#define TFMAPCONTRIBUTION_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "gcsdk/protobufsharedobject.h"
+#include "tf_gcmessages.h"
+
+namespace GCSDK
+{
+ class CSQLAccess;
+};
+
+//---------------------------------------------------------------------------------
+// Purpose: All the account-level information that the GC tracks for TF
+//---------------------------------------------------------------------------------
+class CTFMapContribution : public GCSDK::CProtoBufSharedObject< CSOTFMapContribution, k_EEconTypeMapContribution >
+{
+#ifdef GC
+ DECLARE_CLASS_MEMPOOL( CTFMapContribution );
+#endif
+
+public:
+ CTFMapContribution() {}
+
+#ifdef GC
+ virtual bool BYieldingAddInsertToTransaction( GCSDK::CSQLAccess & sqlAccess );
+ virtual bool BYieldingAddWriteToTransaction( GCSDK::CSQLAccess & sqlAccess, const CUtlVector< int > &fields );
+ virtual bool BYieldingAddRemoveToTransaction( GCSDK::CSQLAccess & sqlAccess );
+
+ void WriteToRecord( CSchMapContribution *pMapContribution ) const;
+ void ReadFromRecord( const CSchMapContribution & mapContribution );
+#endif
+};
+
+#endif // TFMAPCONTRIBUTION_H
+
diff --git a/game/shared/econ/econ_dynamic_recipe.cpp b/game/shared/econ/econ_dynamic_recipe.cpp
new file mode 100644
index 0000000..ee6fb17
--- /dev/null
+++ b/game/shared/econ/econ_dynamic_recipe.cpp
@@ -0,0 +1,250 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Functions related to dynamic recipes
+//
+//=============================================================================
+
+
+#include "cbase.h"
+#include "econ_dynamic_recipe.h"
+#ifndef GC_DLL
+ #include "quest_objective_manager.h"
+#endif
+
+// This pattern was chosen to not be:
+// - a valid string acceptable for user-input (ie., custom name)
+// - a sensical float bit pattern
+// - a common int bit pattern
+// - meaningful Unicode data
+const char *g_pszAttrEncodeSeparator = "|\x01\x02\x01\x03|\x01\x02\x01\x03|";
+
+CRecipeComponentMatchingIterator::CRecipeComponentMatchingIterator( const IEconItemInterface *pSourceItem,
+ const IEconItemInterface *pTargetItem )
+ : m_pSourceItem( pSourceItem )
+ , m_pTargetItem( pTargetItem )
+ , m_bIgnoreCompleted( true )
+ , m_nInputsTotal( 0 )
+ , m_nInputsFulfilled( 0 )
+ , m_nOutputsTotal( 0 )
+{}
+
+bool CRecipeComponentMatchingIterator::OnIterateAttributeValue( const CEconItemAttributeDefinition *pAttrDef, const CAttribute_DynamicRecipeComponent& value )
+{
+ // Don't count ourselves as a match!
+ if ( m_pSourceItem && m_pTargetItem && m_pSourceItem->GetID() == m_pTargetItem->GetID() )
+ return true;
+
+ // If this isn't a match and the item isn't NULL, we skip. We consider NULL to mean
+ // that we want to tally ALL attributes of this type
+ if ( !DefinedItemAttribMatch( value, m_pTargetItem ) && m_pTargetItem != NULL )
+ return true;
+
+ // Dont let non-craftable items through
+ if ( m_pTargetItem && !m_pTargetItem->IsUsableInCrafting() )
+ return true;
+
+ // Is this an output?
+ if ( value.component_flags() & DYNAMIC_RECIPE_FLAG_IS_OUTPUT )
+ {
+ m_vecMatchingOutputs.AddToTail( pAttrDef );
+ m_nOutputsTotal += value.num_required();
+ }
+ else
+ {
+ m_vecMatchingInputs.AddToTail( pAttrDef );
+ m_nInputsTotal += value.num_required();
+ m_nInputsFulfilled += value.num_fulfilled();
+ }
+
+ return true;
+}
+
+
+bool DefinedItemAttribMatch( const CAttribute_DynamicRecipeComponent& attribValue, const IEconItemInterface* pItem )
+{
+ if ( !pItem )
+ return false;
+
+ // If our fulfilled count is what our item count is, then we're done. We dont want any more matches.
+ if ( attribValue.num_fulfilled() == attribValue.num_required() )
+ return false;
+
+ // If the item_def flag is set, and the item's item_def doesnt match then not a match
+ if ( ( attribValue.component_flags() & DYNAMIC_RECIPE_FLAG_PARAM_ITEM_DEF_SET ) &&
+ ( attribValue.def_index() != (uint32)pItem->GetItemDefIndex() ) )
+ return false;
+
+ // If the quality flag is set, and the item's quality doesn't match, then not a match
+ if ( ( attribValue.component_flags() & DYNAMIC_RECIPE_FLAG_PARAM_QUALITY_SET ) &&
+ ( attribValue.item_quality() != (uint32)pItem->GetQuality() ) )
+ return false;
+
+ // check if we have ALL required attributes
+ if ( attribValue.component_flags() & DYNAMIC_RECIPE_FLAG_PARAM_ATTRIBUTE_SET_ALL )
+ {
+ CUtlVector<CEconItem::attribute_t> vecAttribs;
+ if( !DecodeAttributeStringIntoAttributes( attribValue, vecAttribs ) )
+ {
+ AssertMsg2( 0, "%s: Unable to decode dynamic recipe attributes on item %llu", __FUNCTION__, pItem->GetID() );
+ return false;
+ }
+
+ FOR_EACH_VEC( vecAttribs, i )
+ {
+ const CEconItemAttributeDefinition *pAttr = GetItemSchema()->GetAttributeDefinition( vecAttribs[i].m_unDefinitionIndex );
+ Assert( pAttr );
+ uint32 itemAttributeValue;
+ if ( !pAttr || !pItem->FindAttribute( pAttr, &itemAttributeValue ) || itemAttributeValue != vecAttribs[i].m_value.asUint32 )
+ {
+ return false;
+ }
+ }
+ }
+ // check if we have ANY required attributes
+ else if ( attribValue.component_flags() & DYNAMIC_RECIPE_FLAG_PARAM_ATTRIBUTE_SET_ANY )
+ {
+ CUtlVector<CEconItem::attribute_t> vecAttribs;
+ if( !DecodeAttributeStringIntoAttributes( attribValue, vecAttribs ) )
+ {
+ AssertMsg2( 0, "%s: Unable to decode dynamic recipe attributes on item %llu", __FUNCTION__, pItem->GetID() );
+ return false;
+ }
+
+ bool bHasAnyMatchingAttributes = false;
+ FOR_EACH_VEC( vecAttribs, i )
+ {
+ const CEconItemAttributeDefinition *pAttr = GetItemSchema()->GetAttributeDefinition( vecAttribs[i].m_unDefinitionIndex );
+ Assert( pAttr );
+ uint32 itemAttributeValue;
+ if ( pAttr && pItem->FindAttribute( pAttr, &itemAttributeValue ) && itemAttributeValue == vecAttribs[i].m_value.asUint32 )
+ {
+ bHasAnyMatchingAttributes = true;
+ break;
+ }
+ }
+
+ if ( !bHasAnyMatchingAttributes )
+ {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool DecodeAttributeStringIntoAttributes( const CAttribute_DynamicRecipeComponent& attribValue, CUtlVector<CEconItem::attribute_t>& vecAttribs )
+{
+ CUtlStringList vecAttributeStrings; // Automatically free'd
+ V_SplitString( attribValue.attributes_string().c_str(), g_pszAttrEncodeSeparator, vecAttributeStrings );
+
+ if( vecAttributeStrings.Count() % 2 != 0 )
+ {
+ AssertMsg1( 0, "%s: Uneven count of encoded attribute strings!", __FUNCTION__ );
+ return false;
+ }
+
+ for( int j = 0; j< vecAttributeStrings.Count(); j+=2 )
+ {
+ // Get the attribute definition that's stored in the string, and its type
+ attrib_definition_index_t index = Q_atoi( vecAttributeStrings[j] );
+ const CEconItemAttributeDefinition *pAttrDef = GEconItemSchema().GetAttributeDefinition( index );
+ if ( !pAttrDef )
+ {
+#ifdef GC
+ EmitError( SPEW_GC, __FUNCTION__ ": Unable to find attribute definition '%s' (index %d)!\n", vecAttributeStrings[j], j );
+#endif
+ return false;
+ }
+
+ CEconItem::attribute_t& attrib = vecAttribs[vecAttribs.AddToTail()];
+ attrib.m_unDefinitionIndex = pAttrDef->GetDefinitionIndex();
+
+ // Now have the attribute read in the value stored in the string
+ const ISchemaAttributeType* pAttrType = pAttrDef->GetAttributeType();
+ pAttrType->InitializeNewEconAttributeValue( &attrib.m_value );
+
+ // Don't fail us now!
+ const char* pszAttribValue = vecAttributeStrings[j+1];
+ if ( !pAttrType->BConvertStringToEconAttributeValue( pAttrDef, pszAttribValue, &attrib.m_value ) )
+ {
+#ifdef GC
+ EmitError( SPEW_GC, __FUNCTION__ ": Unable to parse attribute value '%s' for attribute '%s'!\n", pszAttribValue, pAttrDef->GetDefinitionName() );
+#endif
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool DecodeItemFromEncodedAttributeString( const CAttribute_DynamicRecipeComponent& attribValue, CEconItem* pItem )
+{
+ // If the item_def flag is set, set that item def
+ if ( attribValue.component_flags() & DYNAMIC_RECIPE_FLAG_PARAM_ITEM_DEF_SET )
+ {
+ pItem->SetDefinitionIndex( attribValue.def_index() );
+ }
+ else
+ {
+ // If the flag is not set, then we want the item name to be generic. In english, we want to just call it "item".
+ CAttribute_String attrStr;
+ attrStr.set_value( "#TF_ItemName_Item" );
+
+ static CSchemaAttributeDefHandle pAttrDef_ItemNameTextOverride( "item name text override" );
+ pItem->SetDynamicAttributeValue( pAttrDef_ItemNameTextOverride, attrStr );
+ }
+
+ // If the quality flag is set, take the quality
+ if ( attribValue.component_flags() & DYNAMIC_RECIPE_FLAG_PARAM_QUALITY_SET )
+ {
+ pItem->SetQuality( attribValue.item_quality() );
+
+ // If there's no item def set and the quality specified is "unique", we want to be explicit
+ // and have the item description actually say "unique item" so there's no confusion as to what
+ // item quality we want as an input.
+ if ( !( attribValue.component_flags() & DYNAMIC_RECIPE_FLAG_PARAM_ITEM_DEF_SET )
+ && pItem->GetQuality() == AE_UNIQUE )
+ {
+ CAttribute_String attrStr;
+ attrStr.set_value( "#unique" );
+
+ static CSchemaAttributeDefHandle pAttrDef_QualityTextOverride( "quality text override" );
+ pItem->SetDynamicAttributeValue( pAttrDef_QualityTextOverride, attrStr );
+ }
+ }
+ else
+ {
+ // If no quality was specified, we want to explicity say that we'll accept ANY quality.
+ pItem->SetQuality( AE_UNIQUE );
+ CAttribute_String attrStr;
+ attrStr.set_value( "#TF_QualityText_Any" );
+
+ static CSchemaAttributeDefHandle pAttrDef_QualityTextOverride( "quality text override" );
+ pItem->SetDynamicAttributeValue( pAttrDef_QualityTextOverride, attrStr );
+ }
+ pItem->SetFlags( 0 );
+
+ // Get all the attributes encoded into the attribute
+ CUtlVector<CEconItem::attribute_t> vecAttribs;
+ if( !DecodeAttributeStringIntoAttributes( attribValue, vecAttribs ) )
+ {
+ AssertMsg1( 0, " %s : Unable to decode dynamic recipe attributes", __FUNCTION__ );
+ return false;
+ }
+
+ // Apply the attributes to the item
+ FOR_EACH_VEC( vecAttribs, j )
+ {
+ // We don't expect to get here with any missing attributes.
+ const CEconItemAttributeDefinition *pAttrDef = GetItemSchema()->GetAttributeDefinition( vecAttribs[j].m_unDefinitionIndex );
+ Assert( pAttrDef );
+
+ const ISchemaAttributeType *pAttrType = pAttrDef->GetAttributeType();
+ pAttrType->LoadEconAttributeValue( pItem, pAttrDef, vecAttribs[j].m_value );
+
+ // Free up our attribute memory now that we're done with it.
+ pAttrType->UnloadEconAttributeValue( &vecAttribs[j].m_value );
+ }
+
+ return true;
+}
diff --git a/game/shared/econ/econ_dynamic_recipe.h b/game/shared/econ/econ_dynamic_recipe.h
new file mode 100644
index 0000000..9927ab9
--- /dev/null
+++ b/game/shared/econ/econ_dynamic_recipe.h
@@ -0,0 +1,76 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Functions related to dynamic recipes
+//
+//=============================================================================
+
+#ifndef ECON_DYNAMIC_RECIPE
+#define ECON_DYNAMIC_RECIPE
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "tf_gcmessages.h"
+#include "game_item_schema.h"
+#include "econ_item.h"
+
+extern const char *g_pszAttrEncodeSeparator;
+
+//-----------------------------------------------------------------------------
+// Purpose: Stores off all CAttribute_DynamicRecipeComponent attributes
+// that consider m_pTargetItem to be a match. If NULL is passed in for pTargetItem
+// then we consider all attributes to be a match
+//-----------------------------------------------------------------------------
+class CRecipeComponentMatchingIterator : public CEconItemSpecificAttributeIterator
+{
+public:
+ CRecipeComponentMatchingIterator( const IEconItemInterface *pSourceItem,
+ const IEconItemInterface *pTargetItem );
+
+ void SetSourceItem( const IEconItemInterface *m_pSourceItem );
+ void SetTargetItem( const IEconItemInterface *pTargetItem );
+ void SetIgnoreCompleted( bool bIgnoreCompleted ) { m_bIgnoreCompleted = bIgnoreCompleted; }
+
+ virtual bool OnIterateAttributeValue( const CEconItemAttributeDefinition *pAttrDef,
+ const CAttribute_DynamicRecipeComponent& value ) OVERRIDE;
+
+ const CUtlVector< const CEconItemAttributeDefinition* >& GetMatchingComponentInputs() const { return m_vecMatchingInputs; }
+ const CUtlVector< const CEconItemAttributeDefinition* >& GetMatchingComponentOutputs() const { return m_vecMatchingOutputs; }
+
+ int GetTotalInputs() const { return m_nInputsTotal; }
+ int GetInputsFulfilled() const { return m_nInputsFulfilled; }
+ int GetTotalOutputs() const { return m_nOutputsTotal; }
+private:
+
+ const IEconItemInterface *m_pSourceItem;
+ const IEconItemInterface *m_pTargetItem;
+ bool m_bIgnoreCompleted;
+
+ CUtlVector< const CEconItemAttributeDefinition* > m_vecMatchingInputs;
+ CUtlVector< const CEconItemAttributeDefinition* > m_vecMatchingOutputs;
+
+ int m_nInputsTotal;
+ int m_nInputsFulfilled;
+ int m_nOutputsTotal;
+};
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Given a CAttribute_DynamicRecipeComponent and a IEconItemInterface,
+// returns whether the item pass the criteria of the attribute
+//-----------------------------------------------------------------------------
+bool DefinedItemAttribMatch( const CAttribute_DynamicRecipeComponent& attribValue, const IEconItemInterface* pItem );
+
+//-----------------------------------------------------------------------------
+// Purpose: Decodes the encoded attributes in attribValue and applies those attributes to pItem
+// Returns true on success, false if anything fails
+//-----------------------------------------------------------------------------
+bool DecodeAttributeStringIntoAttributes( const CAttribute_DynamicRecipeComponent& attribValue, CUtlVector<CEconItem::attribute_t>& vecAttribs);
+
+//-----------------------------------------------------------------------------
+// Purpose: Decodes the encoded attributes in attribValue forms pItem into the
+// item that it describes
+//-----------------------------------------------------------------------------
+bool DecodeItemFromEncodedAttributeString( const CAttribute_DynamicRecipeComponent& attribValue, CEconItem* pItem );
+
+#endif //ECON_DYNAMIC_RECIPE
diff --git a/game/shared/econ/econ_entity.cpp b/game/shared/econ/econ_entity.cpp
new file mode 100644
index 0000000..041ad99
--- /dev/null
+++ b/game/shared/econ/econ_entity.cpp
@@ -0,0 +1,2078 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+
+#include "cbase.h"
+#include "econ_entity_creation.h"
+#include "vgui/ILocalize.h"
+#include "tier3/tier3.h"
+
+#if defined( CLIENT_DLL )
+#define UTIL_VarArgs VarArgs
+
+#include "econ_item_inventory.h"
+#include "model_types.h"
+#include "eventlist.h"
+#include "networkstringtable_clientdll.h"
+#include "cdll_util.h"
+
+#if defined(TF_CLIENT_DLL)
+#include "c_tf_player.h"
+#include "tf_gamerules.h"
+#include "c_playerresource.h"
+#include "tf_shareddefs.h"
+#endif
+
+#else // defined( CLIENT_DLL )
+
+#include "activitylist.h"
+
+#if defined(TF_DLL)
+#include "tf_player.h"
+#endif
+#endif // defined( CLIENT_DLL )
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+IMPLEMENT_NETWORKCLASS_ALIASED( EconEntity, DT_EconEntity )
+IMPLEMENT_NETWORKCLASS_ALIASED( BaseAttributableItem, DT_BaseAttributableItem )
+
+#if defined( CLIENT_DLL )
+bool ParseItemKeyvalue( void *pObject, typedescription_t *pFields, int iNumFields, const char *szKeyName, const char *szValue );
+#endif
+
+#if defined(_DEBUG)
+extern ConVar item_debug;
+extern ConVar item_debug_validation;
+#endif
+
+#if !defined( CLIENT_DLL )
+ #define DEFINE_ECON_ENTITY_NETWORK_TABLE() \
+ SendPropDataTable( SENDINFO_DT( m_AttributeManager ), &REFERENCE_SEND_TABLE(DT_AttributeContainer) ),
+#else
+ #define DEFINE_ECON_ENTITY_NETWORK_TABLE() \
+ RecvPropDataTable( RECVINFO_DT( m_AttributeManager ), 0, &REFERENCE_RECV_TABLE(DT_AttributeContainer) ),
+#endif // CLIENT_DLL
+
+BEGIN_NETWORK_TABLE( CEconEntity , DT_EconEntity )
+ DEFINE_ECON_ENTITY_NETWORK_TABLE()
+
+#if defined(TF_DLL)
+ SendPropBool( SENDINFO( m_bValidatedAttachedEntity ) ),
+#elif defined(TF_CLIENT_DLL)
+ RecvPropBool( RECVINFO( m_bValidatedAttachedEntity ) ),
+#endif // TF_DLL || TF_CLIENT_DLL
+
+END_NETWORK_TABLE()
+
+BEGIN_DATADESC( CEconEntity )
+END_DATADESC()
+
+//
+// Duplicating CEconEntity's network table and data description for backwards compat with demos.
+// NOTE: NOTE_RENAMED_RECVTABLE() will not work with this class.
+//
+BEGIN_NETWORK_TABLE( CBaseAttributableItem, DT_BaseAttributableItem )
+ DEFINE_ECON_ENTITY_NETWORK_TABLE()
+END_NETWORK_TABLE()
+
+BEGIN_DATADESC( CBaseAttributableItem )
+END_DATADESC()
+
+#ifdef TF_CLIENT_DLL
+extern ConVar cl_flipviewmodels;
+#ifdef STAGING_ONLY
+ConVar unusual_force_weapon_effect( "unusual_force_weapon_effect", "-1", FCVAR_CHEAT, "Set to force an Unusual effect on your weapon (Primary, Secondary, Melee)" );
+ConVar unusual_force_cosmetic_effect( "unusual_force_cosmetic_effect", "-1", FCVAR_CHEAT, "Set to force an Unusual effect on your equipped cosmetics" );
+#endif
+#endif
+
+
+#ifdef CLIENT_DLL
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void DrawEconEntityAttachedModels( CBaseAnimating *pEnt, CEconEntity *pAttachedModelSource, const ClientModelRenderInfo_t *pInfo, int iMatchDisplayFlags )
+{
+#ifndef DOTA_DLL
+ if ( !pEnt || !pAttachedModelSource || !pInfo )
+ return;
+
+ // This flag says we should turn off the material overrides for attachments.
+ IMaterial* pMaterialOverride = NULL;
+ OverrideType_t nMaterialOverrideType = OVERRIDE_NORMAL;
+
+ if ( ( pInfo->flags & STUDIO_NO_OVERRIDE_FOR_ATTACH ) != 0 )
+ {
+ modelrender->GetMaterialOverride( &pMaterialOverride, &nMaterialOverrideType );
+ modelrender->ForcedMaterialOverride( NULL, nMaterialOverrideType );
+ }
+
+ // Draw our attached models as well
+ for ( int i = 0; i < pAttachedModelSource->m_vecAttachedModels.Size(); i++ )
+ {
+ const AttachedModelData_t& attachedModel = pAttachedModelSource->m_vecAttachedModels[i];
+
+ if ( attachedModel.m_pModel && (attachedModel.m_iModelDisplayFlags & iMatchDisplayFlags) )
+ {
+ ClientModelRenderInfo_t infoAttached = *pInfo;
+
+ infoAttached.pRenderable = pEnt;
+ infoAttached.instance = MODEL_INSTANCE_INVALID;
+ infoAttached.entity_index = pEnt->index;
+ infoAttached.pModel = attachedModel.m_pModel;
+
+ infoAttached.pModelToWorld = &infoAttached.modelToWorld;
+
+ // Turns the origin + angles into a matrix
+ AngleMatrix( infoAttached.angles, infoAttached.origin, infoAttached.modelToWorld );
+
+ DrawModelState_t state;
+ matrix3x4_t *pBoneToWorld;
+ bool bMarkAsDrawn = modelrender->DrawModelSetup( infoAttached, &state, NULL, &pBoneToWorld );
+ pEnt->DoInternalDrawModel( &infoAttached, ( bMarkAsDrawn && ( infoAttached.flags & STUDIO_RENDER ) ) ? &state : NULL, pBoneToWorld );
+ }
+ }
+
+ if ( pMaterialOverride != NULL )
+ modelrender->ForcedMaterialOverride( pMaterialOverride, nMaterialOverrideType );
+#endif
+}
+#endif // CLIENT_DLL
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CEconEntity::CEconEntity()
+{
+ m_pAttributes = this;
+
+ // Inform base entity system that we can deal with dynamic models
+ EnableDynamicModels();
+#ifdef GAME_DLL
+ m_iOldOwnerClass = 0;
+#endif
+
+#if defined(TF_DLL) || defined(TF_CLIENT_DLL)
+ m_bValidatedAttachedEntity = false;
+#endif // TF_DLL || TF_CLIENT_DLL
+
+#ifdef CLIENT_DLL
+ m_flFlexDelayTime = 0.0f;
+ m_flFlexDelayedWeight = NULL;
+ m_cFlexDelayedWeight = 0;
+ m_iNumOwnerValidationRetries = 0;
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CEconEntity::~CEconEntity()
+{
+#ifdef CLIENT_DLL
+ SetParticleSystemsVisible( PARTICLE_SYSTEM_STATE_NOT_VISIBLE );
+ delete [] m_flFlexDelayedWeight;
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CStudioHdr * CEconEntity::OnNewModel()
+{
+ CStudioHdr* hdr = BaseClass::OnNewModel();
+
+#ifdef GAME_DLL
+ // Adjust class-specific bodygroup after model load if we have a model and a class
+ if ( hdr && m_iOldOwnerClass > 0 )
+ {
+ CEconItemView *pItem = GetAttributeContainer()->GetItem();
+ if ( pItem && pItem->IsValid() && pItem->GetStaticData()->UsesPerClassBodygroups( GetTeamNumber() ) )
+ {
+ // Classes start at 1, bodygroups at 0
+ SetBodygroup( 1, m_iOldOwnerClass - 1 );
+ }
+ }
+#endif
+
+#ifdef TF_CLIENT_DLL
+ m_bValidatedOwner = false; // require item validation to re-run
+
+ // If we're carried by a player, let him know he should recalc his bodygroups.
+ C_TFPlayer *pPlayer = ToTFPlayer( GetOwnerEntity() );
+ if ( pPlayer )
+ {
+ pPlayer->SetBodygroupsDirty();
+ }
+
+ // allocate room for delayed flex weights
+ delete [] m_flFlexDelayedWeight;
+ m_flFlexDelayedWeight = NULL;
+ m_cFlexDelayedWeight = 0;
+ if ( hdr && hdr->numflexcontrollers() )
+ {
+ m_cFlexDelayedWeight = hdr->numflexcontrollers();
+ m_flFlexDelayedWeight = new float[ m_cFlexDelayedWeight ];
+ memset( m_flFlexDelayedWeight, 0, sizeof( float ) * m_cFlexDelayedWeight );
+
+ C_BaseFlex::LinkToGlobalFlexControllers( hdr );
+ }
+#endif // TF_CLIENT_DLL
+
+ return hdr;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CEconEntity::InitializeAttributes( void )
+{
+ m_AttributeManager.InitializeAttributes( this );
+ m_AttributeManager.SetProviderType( PROVIDER_WEAPON );
+
+#ifdef CLIENT_DLL
+ // Check particle systems
+ CUtlVector<const attachedparticlesystem_t *> vecParticles;
+ GetEconParticleSystems( &vecParticles );
+ m_bHasParticleSystems = vecParticles.Count() > 0;
+
+ if ( !m_bClientside )
+ return;
+#else
+ m_AttributeManager.GetItem()->InitNetworkedDynamicAttributesForDemos();
+#endif
+}
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CEconEntity::DebugDescribe( void )
+{
+ CEconItemView *pScriptItem = GetAttributeContainer()->GetItem();
+
+ Msg("============================================\n");
+ char tempstr[1024];
+// FIXME: ILocalize::ConvertUnicodeToANSI( pScriptItem->GetItemName(), tempstr, sizeof(tempstr) );
+ const char *pszQualityString = EconQuality_GetQualityString( (EEconItemQuality)pScriptItem->GetItemQuality() );
+ Msg("%s \"%s\" (level %d)\n", pszQualityString ? pszQualityString : "[unknown]", tempstr, pScriptItem->GetItemLevel() );
+ // FIXME: ILocalize::ConvertUnicodeToANSI( pScriptItem->GetAttributeDescription(), tempstr, sizeof(tempstr) );
+ Msg("%s", tempstr );
+ Msg("\n============================================\n");
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CEconEntity::UpdateOnRemove( void )
+{
+ SetOwnerEntity( NULL );
+ ReapplyProvision();
+
+ BaseClass::UpdateOnRemove();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CEconEntity::ReapplyProvision( void )
+{
+#ifdef GAME_DLL
+ UpdateModelToClass();
+#endif
+
+ CBaseEntity *pNewOwner = GetOwnerEntity();
+ if ( pNewOwner == m_hOldProvidee.Get() )
+ return;
+
+ // Remove ourselves from the old providee's list
+ if ( m_hOldProvidee.Get() )
+ {
+ GetAttributeManager()->StopProvidingTo( m_hOldProvidee.Get() );
+ }
+
+ // Add ourselves to our new owner's provider list
+ if ( pNewOwner )
+ {
+ GetAttributeManager()->ProvideTo( pNewOwner );
+ }
+
+ m_hOldProvidee = pNewOwner;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+Activity CEconEntity::TranslateViewmodelHandActivity( Activity actBase )
+{
+ CEconItemView *pItem = GetAttributeContainer()->GetItem();
+ if ( pItem && pItem->IsValid() )
+ {
+ GameItemDefinition_t *pStaticData = pItem->GetStaticData();
+ if ( pStaticData && pStaticData->ShouldAttachToHands() )
+ {
+ return TranslateViewmodelHandActivityInternal(actBase);
+ }
+ }
+
+ return actBase;
+}
+
+#if !defined( CLIENT_DLL )
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CEconEntity::OnOwnerClassChange( void )
+{
+#ifdef TF_DLL
+ CTFPlayer *pPlayer = ToTFPlayer( GetOwnerEntity() );
+ if ( pPlayer && pPlayer->GetPlayerClass()->GetClassIndex() != m_iOldOwnerClass )
+ {
+ UpdateModelToClass();
+ }
+#endif
+}
+
+#ifdef GAME_DLL
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+int CEconEntity::CalculateVisibleClassFor( CBaseCombatCharacter *pPlayer )
+{
+#ifdef TF_DLL
+ CTFPlayer *pTFPlayer = ToTFPlayer( pPlayer );
+ return (pTFPlayer ? pTFPlayer->GetPlayerClass()->GetClassIndex() : 0);
+#else
+ return 0;
+#endif
+}
+#endif
+
+//-----------------------------------------------------------------------------
+// Purpose: double duty function - sets up model for current player class, and
+// also sets bodygroups if the correct model is fully loaded.
+//-----------------------------------------------------------------------------
+void CEconEntity::UpdateModelToClass( void )
+{
+#ifdef TF_DLL
+ MDLCACHE_CRITICAL_SECTION();
+
+ CTFPlayer *pPlayer = ToTFPlayer( GetOwnerEntity() );
+ m_iOldOwnerClass = CalculateVisibleClassFor( pPlayer );
+ if ( !pPlayer )
+ return;
+
+ CEconItemView *pItem = GetAttributeContainer()->GetItem();
+ if ( !pItem->IsValid() )
+ return;
+
+ const char *pszModel = NULL;
+
+ // If we attach to hands, we need to use the hand models
+ if ( pItem->GetStaticData()->ShouldAttachToHands() )
+ {
+ pszModel = pPlayer->GetPlayerClass()->GetHandModelName( 0 );
+ }
+ else
+ {
+ int nTeam = pPlayer->GetTeamNumber();
+ CTFWearable *pWearable = dynamic_cast< CTFWearable*>( this );
+ if ( pWearable && pWearable->IsDisguiseWearable() )
+ {
+ nTeam = pPlayer->m_Shared.GetDisguiseTeam();
+ }
+
+ pszModel = pItem->GetPlayerDisplayModel( m_iOldOwnerClass, nTeam );
+ }
+ if ( pszModel && pszModel[0] )
+ {
+ if ( V_stricmp( STRING( GetModelName() ), pszModel ) != 0 )
+ {
+ if ( pItem->GetStaticData()->IsContentStreamable() )
+ {
+ modelinfo->RegisterDynamicModel( pszModel, IsClient() );
+
+ const char *pszModelAlt = pItem->GetStaticData()->GetPlayerDisplayModelAlt( m_iOldOwnerClass );
+ if ( pszModelAlt && pszModelAlt[0] )
+ {
+ modelinfo->RegisterDynamicModel( pszModelAlt, IsClient() );
+ }
+
+ if ( pItem->GetVisionFilteredDisplayModel() && pItem->GetVisionFilteredDisplayModel()[ 0 ] != '\0' )
+ {
+ modelinfo->RegisterDynamicModel( pItem->GetVisionFilteredDisplayModel(), IsClient() );
+ }
+ }
+
+ SetModel( pszModel );
+ }
+ }
+
+ if ( GetModelPtr() && pItem->GetStaticData()->UsesPerClassBodygroups( GetTeamNumber() ) )
+ {
+ // Classes start at 1, bodygroups at 0, so we shift them all back 1.
+ SetBodygroup( 1, (m_iOldOwnerClass-1) );
+ }
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CEconEntity::PlayAnimForPlaybackEvent( wearableanimplayback_t iPlayback )
+{
+ CEconItemView *pItem = GetAttributeContainer()->GetItem();
+ if ( !pItem->IsValid() || !GetOwnerEntity() )
+ return;
+
+ int iTeamNum = GetOwnerEntity()->GetTeamNumber();
+ int iActivities = pItem->GetStaticData()->GetNumPlaybackActivities( iTeamNum );
+ for ( int i = 0; i < iActivities; i++ )
+ {
+ activity_on_wearable_t *pData = pItem->GetStaticData()->GetPlaybackActivityData( iTeamNum, i );
+ if ( pData && pData->iPlayback == iPlayback && pData->pszActivity )
+ {
+ // If this is the first time we've tried to use it, find the activity
+ if ( pData->iActivity == kActivityLookup_Unknown )
+ {
+ pData->iActivity = ActivityList_IndexForName( pData->pszActivity );
+ }
+
+ int sequence = SelectWeightedSequence( (Activity)pData->iActivity );
+ if ( sequence != ACTIVITY_NOT_AVAILABLE )
+ {
+ ResetSequence( sequence );
+ SetCycle( 0 );
+
+#if !defined( CLIENT_DLL )
+ if ( IsUsingClientSideAnimation() )
+ {
+ ResetClientsideFrame();
+ }
+#endif
+ }
+ return;
+ }
+ }
+}
+
+#endif // !CLIENT_DLL
+
+#if defined( TF_CLIENT_DLL )
+// It's okay to draw attached entities with these models.
+const char* g_modelWhiteList[] =
+{
+ "models/weapons/w_models/w_toolbox.mdl",
+ "models/weapons/w_models/w_sapper.mdl",
+
+ // Canteens can change model based on the powerup type... all of these alternates are ok!
+ "models/player/items/mvm_loot/all_class/mvm_flask_krit.mdl",
+ "models/player/items/mvm_loot/all_class/mvm_flask_uber.mdl",
+ "models/player/items/mvm_loot/all_class/mvm_flask_tele.mdl",
+ "models/player/items/mvm_loot/all_class/mvm_flask_ammo.mdl",
+ "models/player/items/mvm_loot/all_class/mvm_flask_build.mdl",
+
+ TF_WEAPON_TAUNT_FRONTIER_JUSTICE_GUITAR_MODEL,
+
+ "models/workshop/weapons/c_models/c_paratooper_pack/c_paratrooper_pack.mdl",
+ "models/workshop/weapons/c_models/c_paratooper_pack/c_paratrooper_pack_open.mdl",
+
+};
+
+#define HALLOWEEN_KART_MODEL "models/player/items/taunts/bumpercar/parts/bumpercar.mdl"
+#define HALLOWEEN_KART_CAGE_MODEL "models/props_halloween/bumpercar_cage.mdl"
+#endif
+
+#if defined( CLIENT_DLL )
+//-----------------------------------------------------------------------------
+// Purpose: TF prevents drawing of any entity attached to players that aren't items in the inventory of the player.
+// This is to prevent servers creating fake cosmetic items and attaching them to players.
+//-----------------------------------------------------------------------------
+bool CEconEntity::ValidateEntityAttachedToPlayer( bool &bShouldRetry )
+{
+ bShouldRetry = false;
+
+ // We only use this variable in debug or on the client.
+#if defined( _DEBUG ) || defined( TF_CLIENT_DLL )
+ bool bItemDebugValidation = false;
+#endif // defined( _DEBUG ) || defined( TF_CLIENT_DLL )
+
+#ifdef _DEBUG
+ bItemDebugValidation = item_debug_validation.GetBool();
+
+ // Always valid in debug if item_debug_validation is disabled
+ if ( !bItemDebugValidation )
+ return true;
+#endif // _DEBUG
+
+#if defined( TF_CLIENT_DLL )
+ // Always valid in item testing mode
+ if ( TFGameRules()->IsInItemTestingMode() )
+ return true;
+
+ C_TFPlayer *pOwner = ToTFPlayer( GetOwnerEntity() );
+
+ // If we're not carried by a player, we're not valid. This prevents them
+ // parenting hats to ents that they then parent to the player.
+ if ( !pOwner )
+ {
+ //Msg( "NO OWNER SET! %i\n", m_iNumOwnerValidationRetries );
+ bShouldRetry = ( m_iNumOwnerValidationRetries < 500 );
+ m_iNumOwnerValidationRetries++;
+ return false;
+ }
+
+ C_BaseEntity *pVM = pOwner->GetViewModel();
+
+ // The owner entity must also be a move parent of this entity.
+ bool bPlayerIsParented = false;
+ C_BaseEntity *pEntity = this;
+ while ( (pEntity = pEntity->GetMoveParent()) != NULL )
+ {
+ if ( pOwner == pEntity || pVM == pEntity )
+ {
+ bPlayerIsParented = true;
+ break;
+ }
+ }
+
+ if ( !bPlayerIsParented )
+ {
+ //Msg( "NOT PARENTED! %i\n", m_iNumOwnerValidationRetries );
+ bShouldRetry = ( m_iNumOwnerValidationRetries < 500 );
+ m_iNumOwnerValidationRetries++;
+ return false;
+ }
+
+ m_iNumOwnerValidationRetries = 0;
+
+ // We only need this in debug (for item_debug_validation) or PvE mode
+ bool bOwnerIsBot = pOwner->IsABot(); // THIS IS INSECURE -- DO NOT USE THIS OUTSIDE OF DEBUG OR PVE MODE
+
+ // Allow bots to use anything in PvE mode
+ if ( bOwnerIsBot && TFGameRules()->IsPVEModeActive() )
+ return true;
+
+ int iClass = pOwner->GetPlayerClass()->GetClassIndex();
+ int iTeam = pOwner->GetTeamNumber();
+
+ // Allow all weapons parented to the local player
+ if ( pOwner == C_BasePlayer::GetLocalPlayer() )
+ {
+ // They can change the owner entity, so we have to keep checking.
+ bShouldRetry = true;
+ return true;
+ }
+
+ // HACK: For now, if our owner is a disguised spy, we assume everything is valid.
+ if ( (pOwner->m_Shared.InCond( TF_COND_DISGUISED ) || pOwner->m_Shared.InCond( TF_COND_DISGUISING )) && iClass == TF_CLASS_SPY )
+ {
+ bShouldRetry = true; // Keep checking in case the player switches class or becomes no longer disguised
+ return true;
+ }
+
+ // If our owner is a disguised spy, we validate everything based
+ // on the items carried by the person we're disguised as.
+ /*if ( pOwner->m_Shared.InCond( TF_COND_DISGUISED ) )
+ {
+ // DAMN: This won't work. If our disguise target is a player we've never seen before,
+ // we won't have a client entity, and hence we don't have their inventory.
+ C_TFPlayer *pDisguiseTarget = ToTFPlayer( pOwner->m_Shared.GetDisguiseTarget() );
+ if ( pDisguiseTarget && pDisguiseTarget != pOwner )
+ {
+ pOwner = pDisguiseTarget;
+ iClass = pOwner->GetPlayerClass()->GetClassIndex();
+ }
+ else
+ {
+ // We're not disguised as a specific player. Make sure we lookup base weapons with the disguise class.
+ iClass = pOwner->m_Shared.GetDisguiseClass();
+ }
+ }
+ */
+
+#if defined(TF_DLL) || defined(TF_CLIENT_DLL)
+ if ( m_bValidatedAttachedEntity )
+ return true;
+#endif // TF_DLL || TF_CLIENT_DLL
+
+ const char *pszClientModel = modelinfo->GetModelName( GetModel() );
+ if ( pszClientModel && g_modelWhiteList[0] )
+ {
+ // Certain builder models are okay to have.
+ for ( int i=0; i<ARRAYSIZE( g_modelWhiteList ); ++i )
+ {
+ if ( FStrEq( pszClientModel, g_modelWhiteList[i] ) )
+ return true;
+ }
+ }
+
+ // Halloween Karts
+ if ( pOwner->m_Shared.InCond( TF_COND_HALLOWEEN_KART ) )
+ {
+ if ( FStrEq( pszClientModel, HALLOWEEN_KART_MODEL ) )
+ return true;
+
+ if ( FStrEq( pszClientModel, HALLOWEEN_KART_CAGE_MODEL ) )
+ return true;
+ }
+
+ // If our player doesn't have an inventory, we're not valid.
+ CTFPlayerInventory *pInv = pOwner->Inventory();
+ if ( !pInv )
+ return false;
+
+ // check if this is a custom taunt prop
+ if ( pOwner->m_Shared.InCond( TF_COND_TAUNTING ) )
+ {
+ const char* pszCustomTauntProp = NULL;
+ int iClassTaunt = pOwner->GetPlayerClass()->GetClassIndex();
+ CEconItemView *pMiscItemView = pInv->GetItemInLoadout( iClassTaunt, pOwner->GetActiveTauntSlot() );
+ if ( pMiscItemView && pMiscItemView->IsValid() )
+ {
+ if ( pMiscItemView->GetStaticData()->GetTauntData() )
+ {
+ pszCustomTauntProp = pMiscItemView->GetStaticData()->GetTauntData()->GetProp( iClassTaunt );
+ if ( pszCustomTauntProp )
+ {
+ return true;
+ }
+ }
+ }
+ }
+
+ // If we've lost connection to the GC, let's just trust the server to avoid breaking the appearance for everyone.
+ bool bSkipInventoryCheck = bItemDebugValidation && bOwnerIsBot; // will always be false in release builds
+ if ( ( !pInv->GetSOC() || !pInv->GetSOC()->BIsInitialized() ) && !bSkipInventoryCheck )
+ {
+ bShouldRetry = true;
+ return true;
+ }
+
+ CEconItemView *pScriptItem = GetAttributeContainer()->GetItem();
+
+ // If the item isn't valid, we're probably an extra wearable for another item. See if our model is
+ // a model specified as the extra wearable for any of the items we have equipped.
+ if ( !pScriptItem->IsValid() )
+ {
+ // Uninitialized client models return their model as '?'
+ if ( pszClientModel && pszClientModel[0] != '?' )
+ {
+ CSteamID steamIDForPlayer;
+ pOwner->GetSteamID( &steamIDForPlayer );
+
+ for ( int i = 0; i < CLASS_LOADOUT_POSITION_COUNT; i++ )
+ {
+ CEconItemView *pItem = TFInventoryManager()->GetItemInLoadoutForClass( iClass, i, &steamIDForPlayer );
+ if ( pItem && pItem->IsValid() )
+ {
+ const char *pszAttached = pItem->GetExtraWearableModel();
+ if ( pszAttached && pszAttached[0] )
+ {
+ if ( FStrEq( pszClientModel, pszAttached ) )
+ return true;
+ }
+
+ pszAttached = pItem->GetExtraWearableViewModel();
+ if ( pszAttached && pszAttached[0] )
+ {
+ if ( FStrEq( pszClientModel, pszAttached ) )
+ return true;
+ }
+ }
+ }
+ }
+ else if ( pszClientModel && pszClientModel[0] == '?' )
+ {
+ bShouldRetry = true;
+ }
+
+ return false;
+ }
+
+ // Skip this check for bots if item_debug_validation is enabled.
+ if ( !pInv->GetInventoryItemByItemID( pScriptItem->GetItemID() ) && !bSkipInventoryCheck )
+ {
+ // If it's a base item, we allow it.
+ CEconItemView *pBaseItem = TFInventoryManager()->GetBaseItemForClass( iClass, pScriptItem->GetStaticData()->GetLoadoutSlot(iClass) );
+ if ( *pScriptItem != *pBaseItem )
+ {
+ const wchar_t *pwzItemName = pScriptItem->GetItemName();
+
+ char szItemName[ MAX_ITEM_NAME_LENGTH ];
+ ILocalize::ConvertUnicodeToANSI( pwzItemName, szItemName, sizeof( szItemName ) );
+
+#ifdef _DEBUG
+ Warning("Item '%s' attached to %s, but it's not in his inventory.\n", szItemName, pOwner->GetPlayerName() );
+#endif
+ return false;
+ }
+ }
+
+ // Our model has to match the model in our script
+ const char *pszScriptModel = pScriptItem->GetWorldDisplayModel();
+ if ( !pszScriptModel )
+ {
+ pszScriptModel = pScriptItem->GetPlayerDisplayModel( iClass, iTeam );
+ }
+
+ if ( pszClientModel && pszClientModel[0] && pszClientModel[0] != '?' )
+ {
+ // A model was set on the entity, let's make sure it matches the model in the script.
+ if ( !pszScriptModel || !pszScriptModel[0] )
+ return false;
+
+ if ( FStrEq( pszClientModel, pszScriptModel ) == false )
+ {
+#if defined( STAGING_ONLY ) && defined _DEBUG
+ CUtlString strScriptModel( pszScriptModel );
+ strScriptModel.FixSlashes();
+ CUtlString strClientModel( pszClientModel );
+ strClientModel.FixSlashes();
+ AssertMsg( strScriptModel != strClientModel, "Model path separator differs between script and client!" );
+#endif // STAGING_ONLY
+ // The regular model didn't work...let's try the Alt version if it exists
+ const char *pszScriptModelAlt = pScriptItem->GetStaticData()->GetPlayerDisplayModelAlt( iClass );
+ if ( !pszScriptModelAlt || !pszScriptModelAlt[0] || ( FStrEq( pszClientModel, pszScriptModelAlt ) == false ) )
+ {
+ // The Alt model didn't work... let's try the vision filtered display model if it happens to exist
+ pszScriptModel = pScriptItem->GetVisionFilteredDisplayModel();
+ if ( !pszScriptModel || !pszScriptModel[0] )
+ return false;
+
+ if ( FStrEq( pszClientModel, pszScriptModel ) == false )
+ {
+ return false;
+ }
+ }
+ }
+ }
+ else
+ {
+ // The client model was not set, so check that there isn't a model set in the script either.
+ if ( pszScriptModel && pszScriptModel[0] )
+ {
+ if ( pszClientModel[0] == '?' )
+ bShouldRetry = true;
+
+ return false;
+ }
+ }
+
+ return true;
+#else
+ return false;
+#endif
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Set a material override for this entity via code
+//-----------------------------------------------------------------------------
+void CEconEntity::SetMaterialOverride( int team, const char *pszMaterial )
+{
+ if ( team >= 0 && team < TEAM_VISUAL_SECTIONS )
+ {
+ m_MaterialOverrides[ team ].Init( pszMaterial, TEXTURE_GROUP_CLIENT_EFFECTS );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+void CEconEntity::SetMaterialOverride( int team, CMaterialReference &ref )
+{
+ if ( team >= 0 && team < TEAM_VISUAL_SECTIONS )
+ {
+ m_MaterialOverrides[ team ].Init( ref );
+ }
+}
+
+
+// Deal with recording
+void CEconEntity::GetToolRecordingState( KeyValues *msg )
+{
+#ifndef _XBOX
+ BaseClass::GetToolRecordingState( msg );
+
+ bool bUseOverride = ( GetTeamNumber() >= 0 && GetTeamNumber() < TEAM_VISUAL_SECTIONS ) && m_MaterialOverrides[ GetTeamNumber() ].IsValid();
+ if ( bUseOverride )
+ {
+ msg->SetString( "materialOverride", m_MaterialOverrides[ GetTeamNumber() ]->GetName() );
+ }
+#endif
+}
+
+
+#ifndef DOTA_DLL
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void C_ViewmodelAttachmentModel::SetOuter( CEconEntity *pOuter )
+{
+ m_hOuter = pOuter;
+ SetOwnerEntity( pOuter );
+
+ CEconItemView *pItem = pOuter->GetAttributeContainer()->GetItem();
+ if ( pItem->IsValid() )
+ {
+ m_bAlwaysFlip = pItem->GetStaticData()->ShouldFlipViewmodels();
+ }
+}
+
+bool C_ViewmodelAttachmentModel::InitializeAsClientEntity( const char *pszModelName, RenderGroup_t renderGroup )
+{
+ if ( !BaseClass::InitializeAsClientEntity( pszModelName, renderGroup ) )
+ return false;
+
+ AddEffects( EF_BONEMERGE );
+ AddEffects( EF_BONEMERGE_FASTCULL );
+
+ // Invisible by default, and made visible->drawn->made invisible when the viewmodel is drawn
+ AddEffects( EF_NODRAW );
+ return true;
+}
+
+int C_ViewmodelAttachmentModel::InternalDrawModel( int flags )
+{
+#ifdef TF_CLIENT_DLL
+ CMatRenderContextPtr pRenderContext( materials );
+ if ( cl_flipviewmodels.GetBool() != m_bAlwaysFlip )
+ {
+ pRenderContext->CullMode( MATERIAL_CULLMODE_CW );
+ }
+#endif
+ int r = BaseClass::InternalDrawModel( flags );
+
+#ifdef TF_CLIENT_DLL
+ pRenderContext->CullMode( MATERIAL_CULLMODE_CCW );
+#endif
+
+ return r;
+}
+
+bool C_ViewmodelAttachmentModel::OnPostInternalDrawModel( ClientModelRenderInfo_t *pInfo )
+{
+ if ( !BaseClass::OnPostInternalDrawModel( pInfo ) )
+ return false;
+
+ if ( !m_hOuter )
+ return true;
+ if ( !m_hOuter->GetAttributeContainer() )
+ return true;
+ if ( !m_hOuter->GetAttributeContainer()->GetItem() )
+ return true;
+
+ DrawEconEntityAttachedModels( this, GetOuter(), pInfo, kAttachedModelDisplayFlag_ViewModel );
+ return true;
+}
+
+void C_ViewmodelAttachmentModel::StandardBlendingRules( CStudioHdr *hdr, Vector pos[], Quaternion q[], float currentTime, int boneMask )
+{
+ BaseClass::StandardBlendingRules( hdr, pos, q, currentTime, boneMask );
+
+ // Will it blend?
+
+ if ( !m_hOuter )
+ return;
+
+ m_hOuter->ViewModelAttachmentBlending( hdr, pos, q, currentTime, boneMask );
+}
+
+void FormatViewModelAttachment( Vector &vOrigin, bool bInverse );
+void C_ViewmodelAttachmentModel::FormatViewModelAttachment( int nAttachment, matrix3x4_t &attachmentToWorld )
+{
+ Vector vecOrigin;
+ MatrixPosition( attachmentToWorld, vecOrigin );
+ ::FormatViewModelAttachment( vecOrigin, false );
+ PositionMatrix( vecOrigin, attachmentToWorld );
+}
+int C_ViewmodelAttachmentModel::GetSkin( void )
+{
+ if ( m_hOuter != NULL )
+ {
+ CBaseCombatWeapon *pWeapon = m_hOuter->MyCombatWeaponPointer();
+
+ if ( pWeapon )
+ {
+ int nSkin = pWeapon->GetSkinOverride();
+ if ( nSkin != -1 )
+ {
+ return nSkin;
+ }
+ }
+ else
+ {
+ // some models like the Festive Targe don't have combat pointers but they still need to get the correct skin
+ if ( m_hOuter->GetAttributeContainer() )
+ {
+ CEconItemView *pItem = m_hOuter->GetAttributeContainer()->GetItem();
+ if ( pItem && pItem->IsValid() && GetOwnerViaInterface() )
+ {
+ return pItem->GetSkin( GetOwnerViaInterface()->GetTeamNumber(), true );
+ }
+ }
+ }
+ }
+
+ return BaseClass::GetSkin();
+}
+
+#endif // !defined( DOTA_DLL )
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CEconEntity::Release( void )
+{
+#ifdef CLIENT_DLL
+ SetParticleSystemsVisible( PARTICLE_SYSTEM_STATE_NOT_VISIBLE );
+
+ // Remove all effects associated with this econ entity, not just turn them off
+ C_BaseEntity *pEffectOwnerWM = this;
+ C_BaseEntity *pEffectOwnerVM = NULL;
+
+ bool bExtraWearable = false;
+ bool bExtraWearableVM = false;
+
+ CTFWeaponBase *pWeapon = dynamic_cast<CTFWeaponBase*>( this );
+ if ( pWeapon )
+ {
+ pEffectOwnerVM = pWeapon->GetPlayerOwner() ? pWeapon->GetPlayerOwner()->GetViewModel() : NULL;
+ if ( pWeapon->m_hExtraWearable.Get() )
+ {
+ pEffectOwnerWM = pWeapon->m_hExtraWearable.Get();
+ bExtraWearable = true;
+ }
+
+ if ( pWeapon->m_hExtraWearableViewModel.Get() )
+ {
+ pEffectOwnerVM = pWeapon->m_hExtraWearableViewModel.Get();
+ bExtraWearableVM = true;
+ }
+ // always kill all effects for VM
+ if ( pEffectOwnerVM )
+ {
+ pEffectOwnerVM->ParticleProp()->StopEmission( NULL, false, true );
+ }
+ }
+
+ pEffectOwnerWM->ParticleProp()->StopEmission( NULL, false, true );
+
+#endif
+ if ( m_hViewmodelAttachment )
+ {
+ m_hViewmodelAttachment->Release();
+ }
+
+ BaseClass::Release();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CEconEntity::SetDormant( bool bDormant )
+{
+ // If I'm burning, stop the burning sounds
+ if ( !IsDormant() && bDormant && m_nParticleSystemsCreated != PARTICLE_SYSTEM_STATE_NOT_VISIBLE )
+ {
+ SetParticleSystemsVisible( PARTICLE_SYSTEM_STATE_NOT_VISIBLE );
+ }
+
+ BaseClass::SetDormant( bDormant );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CEconEntity::OnPreDataChanged( DataUpdateType_t type )
+{
+ BaseClass::OnPreDataChanged( type );
+
+ m_iOldTeam = m_iTeamNum;
+}
+
+IMaterial *CreateTempMaterialForPlayerLogo( int iPlayerIndex, player_info_t *info, char *texname, int nchars );
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CEconEntity::OnDataChanged( DataUpdateType_t updateType )
+{
+ // If we were just created, setup from the script files
+ if ( updateType == DATA_UPDATE_CREATED )
+ {
+ InitializeAttributes();
+ m_nParticleSystemsCreated = PARTICLE_SYSTEM_STATE_NOT_VISIBLE;
+ m_bAttachmentDirty = true;
+ }
+
+ BaseClass::OnDataChanged( updateType );
+
+ GetAttributeContainer()->OnDataChanged( updateType );
+
+ if ( updateType == DATA_UPDATE_CREATED )
+ {
+ CEconItemView *pItem = m_AttributeManager.GetItem();
+
+#if defined(_DEBUG)
+ if ( item_debug.GetBool() )
+ {
+ DebugDescribe();
+ }
+#endif
+
+ // Find & cache for easy leaf code usage
+ for ( int team = 0; team < TEAM_VISUAL_SECTIONS; team++ )
+ {
+ const char *pszMaterial = pItem->GetStaticData()->GetMaterialOverride( team );
+ if ( pszMaterial )
+ {
+ m_MaterialOverrides[team].Init( pszMaterial, TEXTURE_GROUP_CLIENT_EFFECTS );
+ }
+ }
+
+#ifdef TF_CLIENT_DLL
+ // If we're carried by a player, let him know he should recalc his bodygroups.
+ C_TFPlayer *pPlayer = ToTFPlayer( GetOwnerEntity() );
+ if ( pPlayer )
+ {
+ pPlayer->SetBodygroupsDirty();
+ }
+
+ //Warning("Forcing recalc of visiblity for %d\n", entindex());
+ m_bValidatedOwner = false;
+ m_iNumOwnerValidationRetries = 0;
+ UpdateVisibility();
+#endif // TF_CLIENT_DLL
+ }
+
+ UpdateAttachmentModels();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CEconEntity::UpdateAttachmentModels( void )
+{
+#ifndef DOTA_DLL
+ CEconItemView *pItem = GetAttributeContainer()->GetItem();
+ GameItemDefinition_t *pItemDef = pItem && pItem->IsValid() ? pItem->GetStaticData() : NULL;
+
+ // Update the state of additional model attachments
+ m_vecAttachedModels.Purge();
+ if ( pItemDef && AttachmentModelsShouldBeVisible() )
+ {
+ int iTeamNumber = GetTeamNumber();
+ {
+ int iAttachedModels = pItemDef->GetNumAttachedModels( iTeamNumber );
+ for ( int i = 0; i < iAttachedModels; i++ )
+ {
+ attachedmodel_t *pModel = pItemDef->GetAttachedModelData( iTeamNumber, i );
+
+ int iModelIndex = modelinfo->GetModelIndex( pModel->m_pszModelName );
+ if ( iModelIndex >= 0 )
+ {
+ AttachedModelData_t attachedModelData;
+ attachedModelData.m_pModel = modelinfo->GetModel( iModelIndex );
+ attachedModelData.m_iModelDisplayFlags = pModel->m_iModelDisplayFlags;
+ m_vecAttachedModels.AddToTail( attachedModelData );
+ }
+ }
+ }
+
+ // Check for Festive attachedmodels for festivized weapons
+ {
+ int iAttachedModels = pItemDef->GetNumAttachedModelsFestivized( iTeamNumber );
+ if ( iAttachedModels )
+ {
+ int iFestivized = 0;
+ CALL_ATTRIB_HOOK_INT( iFestivized, is_festivized );
+ if ( iFestivized )
+ {
+ for ( int i = 0; i < iAttachedModels; i++ )
+ {
+ attachedmodel_t *pModel = pItemDef->GetAttachedModelDataFestivized( iTeamNumber, i );
+
+ int iModelIndex = modelinfo->GetModelIndex( pModel->m_pszModelName );
+ if ( iModelIndex >= 0 )
+ {
+ AttachedModelData_t attachedModelData;
+ attachedModelData.m_pModel = modelinfo->GetModel( iModelIndex );
+ attachedModelData.m_iModelDisplayFlags = pModel->m_iModelDisplayFlags;
+ m_vecAttachedModels.AddToTail( attachedModelData );
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // Update the state of attachment models for this item
+ bool bItemNeedsAttachment = pItemDef && (pItemDef->ShouldAttachToHands() || pItemDef->ShouldAttachToHandsVMOnly());
+ if ( bItemNeedsAttachment )
+ {
+ bool bShouldShowAttachment = false;
+ CBasePlayer *pOwner = ToBasePlayer( GetOwnerEntity() );
+ if ( pOwner && !pOwner->ShouldDrawThisPlayer() )
+ {
+ // Drawing the viewmodel
+ bShouldShowAttachment = true;
+ }
+
+ if ( bShouldShowAttachment && AttachmentModelsShouldBeVisible() )
+ {
+ if ( !m_hViewmodelAttachment )
+ {
+ C_BaseViewModel *vm = pOwner->GetViewModel( 0 );
+ if ( vm )
+ {
+ C_ViewmodelAttachmentModel *pEnt = new class C_ViewmodelAttachmentModel;
+ if ( !pEnt )
+ return;
+
+ pEnt->SetOuter( this );
+
+ int iClass = 0;
+#if defined( TF_DLL ) || defined( TF_CLIENT_DLL )
+ CTFPlayer *pTFPlayer = ToTFPlayer( pOwner );
+ if ( pTFPlayer )
+ {
+ iClass = pTFPlayer->GetPlayerClass()->GetClassIndex();
+ }
+#endif // defined( TF_DLL ) || defined( TF_CLIENT_DLL )
+ if ( pEnt->InitializeAsClientEntity( pItem->GetPlayerDisplayModel( iClass, pOwner->GetTeamNumber() ), RENDER_GROUP_VIEW_MODEL_OPAQUE ) == false )
+ return;
+
+ m_hViewmodelAttachment = pEnt;
+ m_hViewmodelAttachment->SetParent( vm );
+ m_hViewmodelAttachment->SetLocalOrigin( vec3_origin );
+ m_hViewmodelAttachment->UpdatePartitionListEntry();
+ m_hViewmodelAttachment->CollisionProp()->UpdatePartition();
+ m_hViewmodelAttachment->UpdateVisibility();
+
+ m_bAttachmentDirty = true;
+ }
+ }
+ else if ( m_hViewmodelAttachment )
+ {
+ // If a player changes team, we may need to update the skin on the attachment weapon model
+ if ( m_iOldTeam != m_iTeamNum )
+ {
+ m_bAttachmentDirty = true;
+ }
+ }
+
+ // We can't pull data from the viewmodel until we're actually the active weapon.
+ if ( m_bAttachmentDirty && m_hViewmodelAttachment )
+ {
+ pOwner = ToBasePlayer( GetOwnerEntity() );
+ C_BaseViewModel *vm = pOwner->GetViewModel( 0 );
+ if ( vm && vm->GetWeapon() == this )
+ {
+ m_hViewmodelAttachment->m_nSkin = vm->GetSkin();
+ m_bAttachmentDirty = false;
+ }
+ }
+ return;
+ }
+ }
+
+ // If we get here we shouldn't have an attachment.
+ if ( m_hViewmodelAttachment )
+ {
+ m_hViewmodelAttachment->Release();
+ }
+
+#endif // !defined( DOTA_DLL )
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CEconEntity::HasCustomParticleSystems( void ) const
+{
+ return m_bHasParticleSystems;
+}
+
+//-----------------------------------------------------------------z------------
+// Purpose: Create / Destroy particle systems on this item as appropriate
+//-----------------------------------------------------------------------------
+void CEconEntity::UpdateParticleSystems( void )
+{
+ if ( !HasCustomParticleSystems() )
+ return;
+
+ ParticleSystemState_t nVisible = PARTICLE_SYSTEM_STATE_NOT_VISIBLE;
+ if ( IsEffectActive( EF_NODRAW ) || !ShouldDraw() )
+ {
+ nVisible = PARTICLE_SYSTEM_STATE_NOT_VISIBLE;
+ }
+ else if ( !GetOwnerEntity() && !IsDormant() )
+ {
+ nVisible = PARTICLE_SYSTEM_STATE_VISIBLE;
+ }
+ else if ( GetOwnerEntity() && !GetOwnerEntity()->IsDormant() && GetOwnerEntity()->IsPlayer() && GetOwnerEntity()->IsAlive() )
+ {
+ nVisible = PARTICLE_SYSTEM_STATE_VISIBLE;
+ }
+
+ if ( nVisible == PARTICLE_SYSTEM_STATE_NOT_VISIBLE )
+ {
+#if defined(TF_CLIENT_DLL) || defined(TF_DLL)
+ // Make sure the entity we're attaching to is being drawn
+ CTFWeaponBase *pWeapon = dynamic_cast< CTFWeaponBase* >( this );
+ C_BasePlayer *pLocalPlayer = C_BasePlayer::GetLocalPlayer();
+ if ( pLocalPlayer && pLocalPlayer == GetOwnerEntity() && pLocalPlayer->GetViewModel() && pLocalPlayer->GetViewModel()->GetWeapon() == pWeapon && !C_BasePlayer::ShouldDrawLocalPlayer() )
+ {
+ nVisible = PARTICLE_SYSTEM_STATE_VISIBLE_VM;
+ }
+#endif
+ }
+
+ if ( nVisible != PARTICLE_SYSTEM_STATE_NOT_VISIBLE && !ShouldDrawParticleSystems() )
+ {
+ nVisible = PARTICLE_SYSTEM_STATE_NOT_VISIBLE;
+ }
+
+ SetParticleSystemsVisible( nVisible );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CEconEntity::ShouldDrawParticleSystems( void )
+{
+#if defined(TF_CLIENT_DLL) || defined(TF_DLL)
+ C_TFPlayer *pPlayer = ToTFPlayer( GetOwnerEntity() );
+ if ( pPlayer )
+ {
+ bool bStealthed = pPlayer->m_Shared.IsStealthed();
+ if ( bStealthed )
+ return false;
+ bool bDisguised = pPlayer->m_Shared.InCond( TF_COND_DISGUISED );
+ if ( bDisguised )
+ {
+ CTFWeaponBase *pWeapon = dynamic_cast<CTFWeaponBase*>( this );
+ bool bDisguiseWeapon = pWeapon && pWeapon->m_bDisguiseWeapon;
+ if ( !bDisguiseWeapon )
+ {
+ return false;
+ }
+ }
+ }
+#endif
+
+ // Make sure the entity we're attaching to is being drawn
+ C_BasePlayer *pLocalPlayer = C_BasePlayer::GetLocalPlayer();
+ if ( pLocalPlayer )
+ {
+ C_BaseEntity *pEffectOwner = this;
+ if ( pLocalPlayer == GetOwnerEntity() && pLocalPlayer->GetViewModel() && !C_BasePlayer::ShouldDrawLocalPlayer() )
+ {
+ pEffectOwner = pLocalPlayer->GetViewModel();
+ }
+
+ if ( !pEffectOwner->ShouldDraw() )
+ {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CEconEntity::FireEvent( const Vector& origin, const QAngle& angles, int event, const char *options )
+{
+ if ( !InternalFireEvent( origin, angles, event, options ) )
+ {
+ BaseClass::FireEvent( origin, angles, event, options );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CEconEntity::OnFireEvent( C_BaseViewModel *pViewModel, const Vector& origin, const QAngle& angles, int event, const char *options )
+{
+ return InternalFireEvent( origin, angles, event, options );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CEconEntity::InternalFireEvent( const Vector& origin, const QAngle& angles, int event, const char *options )
+{
+ switch( event )
+ {
+ case AE_CL_BODYGROUP_SET_VALUE_CMODEL_WPN:
+ if ( m_hViewmodelAttachment )
+ {
+ // Translate it into a set bodygroup event on our attached weapon
+ m_hViewmodelAttachment->FireEvent( origin, angles, AE_CL_BODYGROUP_SET_VALUE, options );
+ }
+ return true;
+ break;
+ }
+
+ return false;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Does this model use delayed flex weights?
+//-----------------------------------------------------------------------------
+bool CEconEntity::UsesFlexDelayedWeights()
+{
+ return m_flFlexDelayedWeight != NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Rendering callback to allow the client to set up all the model specific flex weights
+//-----------------------------------------------------------------------------
+void CEconEntity::SetupWeights( const matrix3x4_t *pBoneToWorld, int nFlexWeightCount, float *pFlexWeights, float *pFlexDelayedWeights )
+{
+ if ( GetModelPtr() && GetModelPtr()->numflexcontrollers() )
+ {
+ if ( IsEffectActive( EF_BONEMERGE ) && GetMoveParent() )
+ {
+ C_BaseFlex *pParentFlex = dynamic_cast<C_BaseFlex*>( GetMoveParent() );
+ if ( pParentFlex )
+ {
+ // BUGBUG: We have a bug with SetCustomModel that causes a disagreement between the studio header here and the one used in l_studio.cpp CModelRender::DrawModelExecute
+ // So when we hit that case, let's not do any work because otherwise we'd crash since the array sizes (m_flFlexDelayedWeight vs pFlexWeights) don't match.
+ // Note that this check is duplicated in C_BaseFlex::SetupLocalWeights.
+ AssertMsg( nFlexWeightCount == m_cFlexDelayedWeight, "Disagreement between the number of flex weights. Do the studio headers match?" );
+ if ( nFlexWeightCount != m_cFlexDelayedWeight )
+ {
+ return;
+ }
+
+ if ( pParentFlex->SetupGlobalWeights( pBoneToWorld, nFlexWeightCount, pFlexWeights, pFlexDelayedWeights ) )
+ {
+ // convert the flex controllers into actual flex values
+ C_BaseFlex::RunFlexRules( GetModelPtr(), pFlexWeights );
+
+ // aim the eyes
+ // SetViewTarget( hdr ); // FIXME: Not enough info yet
+
+ // process local versions of the delay weights
+ if ( pFlexDelayedWeights )
+ {
+ C_BaseFlex::RunFlexDelay( nFlexWeightCount, pFlexWeights, m_flFlexDelayedWeight, m_flFlexDelayTime );
+ memcpy( pFlexDelayedWeights, m_flFlexDelayedWeight, sizeof( float ) * nFlexWeightCount );
+ }
+ return;
+ }
+ }
+ }
+ }
+
+ BaseClass::SetupWeights( pBoneToWorld, nFlexWeightCount, pFlexWeights, pFlexDelayedWeights );
+ return;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+static void cc_dump_particlemanifest()
+{
+ Msg("Dumping particle list:\n");
+ for ( int i = 0; i < g_pParticleSystemMgr->GetParticleSystemCount(); i++ )
+ {
+ const char *pParticleSystemName = g_pParticleSystemMgr->GetParticleSystemNameFromIndex(i);
+ Msg(" %d: %s\n", i, pParticleSystemName );
+ }
+}
+static ConCommand dump_particlemanifest( "dump_particlemanifest", cc_dump_particlemanifest, "Dump the list of particles loaded.", FCVAR_CHEAT );
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CEconEntity::GetEconParticleSystems( CUtlVector<const attachedparticlesystem_t *> *out_pvecParticleSystems ) const
+{
+ Assert( out_pvecParticleSystems );
+
+ const CEconItemView *pEconItemView = m_AttributeManager.GetItem();
+ if ( pEconItemView )
+ {
+ const GameItemDefinition_t *pItemDef = pEconItemView->GetStaticData();
+
+ // Count static particles included in the item definition -- these are things like
+ // the kritzkrieg particles or the milk splash particles.
+ const int iStaticParticleCount = pItemDef->GetNumAttachedParticles( GetTeamNumber() );
+ for ( int i = 0; i < iStaticParticleCount; i++ )
+ {
+ out_pvecParticleSystems->AddToTail( pItemDef->GetAttachedParticleData( GetTeamNumber(), i ) );
+ }
+
+ // Do we have a particle effect that goes along with our specific quality? Self-made
+ // and community items have a sparkle, for example.
+ const int iQualityParticleType = pEconItemView->GetQualityParticleType();
+ if ( iQualityParticleType > 0 )
+ {
+ out_pvecParticleSystems->AddToTail( GetItemSchema()->GetAttributeControlledParticleSystem( iQualityParticleType ) );
+ }
+ }
+
+ // Do we have particle systems added on via static attributes (ie., pipe smoke)?
+ // Note that this is functionally identical to the dynamic unusual particles. We don't support
+ // having multiple attributes of the same type with independent values so we split these out
+ // at a higher level, limiting ourself to one of each.
+ int iStaticParticleEffect = 0;
+ CALL_ATTRIB_HOOK_INT( iStaticParticleEffect, set_attached_particle_static );
+ if ( iStaticParticleEffect > 0 )
+ {
+ out_pvecParticleSystems->AddToTail( GetItemSchema()->GetAttributeControlledParticleSystem( iStaticParticleEffect ) );
+ }
+
+ // Do we have particle systems added on dynamically (ie., unusuals?)?
+ int iDynamicParticleEffect = 0;
+ int iIsThrowableTrail = 0;
+ CALL_ATTRIB_HOOK_INT( iDynamicParticleEffect, set_attached_particle );
+ CALL_ATTRIB_HOOK_INT( iIsThrowableTrail, throwable_particle_trail_only );
+
+#if defined(TF_CLIENT_DLL)
+#ifdef STAGING_ONLY
+ if ( pEconItemView )
+ {
+ const GameItemDefinition_t *pItemDef = pEconItemView->GetStaticData();
+
+ int iSlot = pItemDef->GetLoadoutSlot( 0 );
+ if ( unusual_force_weapon_effect.GetInt() > 0 )
+ {
+ if ( iSlot == LOADOUT_POSITION_PRIMARY || iSlot == LOADOUT_POSITION_SECONDARY || iSlot == LOADOUT_POSITION_MELEE )
+ {
+ iDynamicParticleEffect = unusual_force_weapon_effect.GetInt();
+ }
+ }
+ if ( unusual_force_cosmetic_effect.GetInt() > 0 )
+ {
+ if ( iSlot == LOADOUT_POSITION_MISC )
+ {
+ iDynamicParticleEffect = unusual_force_cosmetic_effect.GetInt();
+ }
+ }
+ }
+#endif
+#endif
+
+ if ( iDynamicParticleEffect > 0 && !iIsThrowableTrail )
+ {
+ attachedparticlesystem_t *pSystem = GetItemSchema()->GetAttributeControlledParticleSystem( iDynamicParticleEffect );
+
+ if ( pSystem )
+ {
+#if defined(TF_CLIENT_DLL) || defined(TF_DLL)
+ // TF Team Color Particles
+ static char pszFullname[256];
+ if ( GetTeamNumber() == TF_TEAM_BLUE && V_stristr( pSystem->pszSystemName, "_teamcolor_red" ))
+ {
+ V_StrSubst( pSystem->pszSystemName, "_teamcolor_red", "_teamcolor_blue", pszFullname, 256 );
+ pSystem = GetItemSchema()->FindAttributeControlledParticleSystem( pszFullname );
+
+ }
+ else if ( GetTeamNumber() == TF_TEAM_RED && V_stristr( pSystem->pszSystemName, "_teamcolor_blue" ))
+ {
+ // Guard against accidentally giving out the blue team color (support tool)
+ V_StrSubst( pSystem->pszSystemName, "_teamcolor_blue", "_teamcolor_red", pszFullname, 256 );
+ pSystem = GetItemSchema()->FindAttributeControlledParticleSystem( pszFullname );
+ }
+#endif
+ if ( pSystem )
+ {
+ out_pvecParticleSystems->AddToTail( pSystem );
+ }
+ }
+ }
+
+ // Scan the particle system
+ // - Clean up our list in case we fed in bad data from the schema or wherever.
+ for ( int i = out_pvecParticleSystems->Count() - 1; i >= 0; i-- )
+ {
+ if ( !(*out_pvecParticleSystems)[i] ||
+ !(*out_pvecParticleSystems)[i]->pszSystemName ||
+ !(*out_pvecParticleSystems)[i]->pszSystemName[0] )
+ {
+ out_pvecParticleSystems->FastRemove( i );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CEconEntity::SetParticleSystemsVisible( ParticleSystemState_t nState )
+{
+ if ( nState == m_nParticleSystemsCreated )
+ {
+ bool bDirty = false;
+
+#if defined(TF_CLIENT_DLL) || defined(TF_DLL)
+ CTFWeaponBase *pWeapon = dynamic_cast< CTFWeaponBase* >( this );
+ if ( pWeapon )
+ {
+ if ( pWeapon->m_hExtraWearable.Get() )
+ {
+ bDirty = !( pWeapon->m_hExtraWearable->m_nParticleSystemsCreated == nState );
+ pWeapon->m_hExtraWearable->m_nParticleSystemsCreated = nState;
+ }
+
+ if ( pWeapon->m_hExtraWearableViewModel.Get() )
+ {
+ bDirty = !( pWeapon->m_hExtraWearableViewModel->m_nParticleSystemsCreated == nState );
+ pWeapon->m_hExtraWearableViewModel->m_nParticleSystemsCreated = nState;
+ }
+ }
+#endif
+
+ if ( !bDirty )
+ {
+ return;
+ }
+ }
+
+ CUtlVector<const attachedparticlesystem_t *> vecParticleSystems;
+ GetEconParticleSystems( &vecParticleSystems );
+
+ FOR_EACH_VEC( vecParticleSystems, i )
+ {
+ const attachedparticlesystem_t *pSystem = vecParticleSystems[i];
+ Assert( pSystem );
+ Assert( pSystem->pszSystemName );
+ Assert( pSystem->pszSystemName[0] );
+
+ // Ignore custom particles. Weapons handle them in custom fashions.
+ if ( pSystem->iCustomType )
+ continue;
+
+ UpdateSingleParticleSystem( nState != PARTICLE_SYSTEM_STATE_NOT_VISIBLE, pSystem );
+ }
+
+ m_nParticleSystemsCreated = nState;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CEconEntity::UpdateSingleParticleSystem( bool bVisible, const attachedparticlesystem_t *pSystem )
+{
+ Assert( pSystem );
+
+ C_BasePlayer *pLocalPlayer = C_BasePlayer::GetLocalPlayer();
+ if ( !pLocalPlayer )
+ return;
+
+ C_BaseEntity *pEffectOwnerWM = this;
+ C_BaseEntity *pEffectOwnerVM = NULL;
+
+ bool bExtraWearable = false;
+ bool bExtraWearableVM = false;
+
+ CTFWeaponBase *pWeapon = dynamic_cast< CTFWeaponBase* >( this );
+ if ( pWeapon )
+ {
+ pEffectOwnerVM = pWeapon->GetPlayerOwner() ? pWeapon->GetPlayerOwner()->GetViewModel() : NULL;
+ if ( pWeapon->m_hExtraWearable.Get() )
+ {
+ pEffectOwnerWM = pWeapon->m_hExtraWearable.Get();
+ bExtraWearable = true;
+ }
+
+ if ( pWeapon->m_hExtraWearableViewModel.Get() )
+ {
+ pEffectOwnerVM = pWeapon->m_hExtraWearableViewModel.Get();
+ bExtraWearableVM = true;
+ }
+ }
+
+ C_BaseEntity *pEffectOwner = pEffectOwnerWM;
+ bool bIsVM = false;
+ C_BasePlayer *pOwner = ToBasePlayer(GetOwnerEntity());
+ bool bDrawThisEffect = true;
+ if ( !pOwner->ShouldDrawThisPlayer() )
+ {
+ // only draw effects designated for this
+ if ( !pSystem->bDrawInViewModel )
+ {
+ bDrawThisEffect = false;
+ }
+
+ C_BaseViewModel *pLocalPlayerVM = pLocalPlayer->GetViewModel();
+ if ( pLocalPlayerVM && pLocalPlayerVM->GetOwningWeapon() == this )
+ {
+ bIsVM = true;
+ pEffectOwner = pEffectOwnerVM;
+ }
+ }
+
+ const char *pszAttachmentName = pSystem->pszControlPoints[0];
+ if ( bIsVM && bExtraWearableVM )
+ pszAttachmentName = "attach_fob_v";
+ if ( !bIsVM && bExtraWearable )
+ pszAttachmentName = "attach_fob";
+
+ int iAttachment = INVALID_PARTICLE_ATTACHMENT;
+ if ( pszAttachmentName && pszAttachmentName[0] && pEffectOwner->GetBaseAnimating() )
+ {
+ iAttachment = pEffectOwner->GetBaseAnimating()->LookupAttachment( pszAttachmentName );
+ }
+
+ // Stop it on both the viewmodel & the world model, because it may be removed due to first/thirdperson switch
+ // Get Full name
+ const CEconItemView *pEconItemView = m_AttributeManager.GetItem();
+ static char pszTempName[256];
+ static char pszTempNameVM[256];
+ const char* pszSystemName = pSystem->pszSystemName;
+
+
+ // Weapon Remap for a Base Effect to be used on a specific weapon
+ if ( pSystem->bUseSuffixName && pEconItemView && pEconItemView->GetItemDefinition()->GetParticleSuffix() )
+ {
+ V_strcpy_safe( pszTempName, pszSystemName );
+ V_strcat_safe( pszTempName, "_" );
+ V_strcat_safe( pszTempName, pEconItemView->GetItemDefinition()->GetParticleSuffix() );
+ pszSystemName = pszTempName;
+ }
+
+ if ( pSystem->bHasViewModelSpecificEffect )
+ {
+ V_strcpy_safe( pszTempNameVM, pszSystemName );
+ V_strcat_safe( pszTempNameVM, "_vm" );
+
+ // VM doesnt exist so fall back to regular
+ if ( g_pParticleSystemMgr->FindParticleSystem( pszTempNameVM ) == NULL )
+ {
+ V_strcpy_safe( pszTempNameVM, pszSystemName );
+ }
+
+ if ( bIsVM )
+ {
+ pszSystemName = pszTempNameVM;
+ }
+ }
+
+ // Check that the effect is valid
+ if ( g_pParticleSystemMgr->FindParticleSystem( pszSystemName ) == NULL )
+ return;
+
+ if ( iAttachment != INVALID_PARTICLE_ATTACHMENT )
+ {
+ pEffectOwnerWM->ParticleProp()->StopParticlesWithNameAndAttachment( pszSystemName, iAttachment, true );
+
+ if ( pEffectOwnerVM )
+ {
+ if ( pSystem->bHasViewModelSpecificEffect )
+ {
+ pEffectOwnerVM->ParticleProp()->StopParticlesWithNameAndAttachment( pszTempNameVM, iAttachment, true );
+ }
+ pEffectOwnerVM->ParticleProp()->StopParticlesWithNameAndAttachment( pszSystemName, iAttachment, true );
+ }
+ }
+ else
+ {
+ pEffectOwnerWM->ParticleProp()->StopParticlesNamed( pszSystemName, true );
+
+ if ( pEffectOwnerVM )
+ {
+ if ( pSystem->bHasViewModelSpecificEffect )
+ {
+ pEffectOwnerVM->ParticleProp()->StopParticlesNamed( pszTempNameVM, true );
+ }
+ pEffectOwnerVM->ParticleProp()->StopParticlesNamed( pszSystemName, true );
+ }
+ }
+
+ if ( !bDrawThisEffect )
+ return;
+
+ // do not generate a viewmodel effect if there is no weapon else it is in your face
+ if ( !pWeapon && bIsVM )
+ {
+ Assert( 0 );
+ Warning( "Cannot create a Viewmodel Particle Effect [%s] when there is no Viewmodel Weapon", pszSystemName );
+ return;
+ }
+
+ if ( bVisible && pEffectOwner )
+ {
+ HPARTICLEFFECT pEffect = NULL;
+ // We can't have fastcull on if we want particles attached to us
+ //if ( !bIsVM )
+ {
+ RemoveEffects( EF_BONEMERGE_FASTCULL );
+ }
+
+ if ( iAttachment != INVALID_PARTICLE_ATTACHMENT )
+ {
+ pEffect = pEffectOwner->ParticleProp()->Create( pszSystemName, PATTACH_POINT_FOLLOW, pszAttachmentName );
+ }
+ else
+ {
+ // Attachments can fall back to following root bones if the attachment point wasn't found
+ if ( pSystem->bFollowRootBone )
+ {
+ pEffect = pEffectOwner->ParticleProp()->Create( pszSystemName, PATTACH_ROOTBONE_FOLLOW );
+ }
+ else
+ {
+ pEffect = pEffectOwner->ParticleProp()->Create( pszSystemName, PATTACH_ABSORIGIN_FOLLOW );
+ }
+ }
+
+ if ( pEffect )
+ {
+ // update the control points if necessary
+ for ( int i=1; i<ARRAYSIZE( pSystem->pszControlPoints ); ++i )
+ {
+ const char *pszControlPointName = pSystem->pszControlPoints[i];
+ if ( pszControlPointName && pszControlPointName[0] != '\0' )
+ {
+ pEffectOwner->ParticleProp()->AddControlPoint( pEffect, i, this, PATTACH_POINT_FOLLOW, pszControlPointName );
+ }
+ }
+
+ if ( bIsVM )
+ {
+ pEffect->SetIsViewModelEffect( true );
+ ClientLeafSystem()->SetRenderGroup( pEffect->RenderHandle(), RENDER_GROUP_VIEW_MODEL_TRANSLUCENT );
+ }
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CEconEntity::InitializeAsClientEntity( const char *pszModelName, RenderGroup_t renderGroup )
+{
+ m_bClientside = true;
+ return BaseClass::InitializeAsClientEntity( pszModelName, renderGroup );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Get an econ material override for the given team.
+// Returns: NULL if there is no override.
+//-----------------------------------------------------------------------------
+IMaterial* CEconEntity::GetEconWeaponMaterialOverride( int iTeam )
+{
+ if ( iTeam >= 0 && iTeam < TEAM_VISUAL_SECTIONS && m_MaterialOverrides[ iTeam ].IsValid() )
+ return m_MaterialOverrides[ iTeam ];
+
+ return NULL;
+}
+
+
+bool CEconEntity::ShouldDraw()
+{
+ if ( ShouldHideForVisionFilterFlags() )
+ {
+ return false;
+ }
+
+ return BaseClass::ShouldDraw();
+}
+
+bool CEconEntity::ShouldHideForVisionFilterFlags( void )
+{
+ CEconItemView *pItem = GetAttributeContainer()->GetItem();
+ if ( pItem && pItem->IsValid() )
+ {
+ CEconItemDefinition *pData = pItem->GetStaticData();
+ if ( pData )
+ {
+ int nVisionFilterFlags = pData->GetVisionFilterFlags();
+ if ( nVisionFilterFlags != 0 )
+ {
+ // Only visible if the local player has an item that allows them to see it (Pyro Goggles)
+ if ( !IsLocalPlayerUsingVisionFilterFlags( nVisionFilterFlags, true ) )
+ {
+ // They didn't have the correct vision flags
+ return true;
+ }
+ }
+ }
+ }
+
+ return false;
+}
+
+bool CEconEntity::IsTransparent( void )
+{
+#ifdef TF_CLIENT_DLL
+ C_TFPlayer *pPlayer = ToTFPlayer( GetOwnerEntity() );
+ if ( pPlayer )
+ {
+ return pPlayer->IsTransparent();
+ }
+#endif // TF_CLIENT_DLL
+
+ return BaseClass::IsTransparent();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CEconEntity::ViewModel_IsTransparent( void )
+{
+ if ( m_hViewmodelAttachment != NULL && m_hViewmodelAttachment->IsTransparent() )
+ {
+ return true;
+ }
+ return IsTransparent();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CEconEntity::ViewModel_IsUsingFBTexture( void )
+{
+ if ( m_hViewmodelAttachment != NULL && m_hViewmodelAttachment->UsesPowerOfTwoFrameBufferTexture() )
+ {
+ return true;
+ }
+ return UsesPowerOfTwoFrameBufferTexture();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CEconEntity::IsOverridingViewmodel( void )
+{
+ bool bUseOverride = (GetTeamNumber() >= 0 && GetTeamNumber() < TEAM_VISUAL_SECTIONS) && m_MaterialOverrides[GetTeamNumber()].IsValid();
+ bUseOverride = bUseOverride || (m_hViewmodelAttachment != NULL) || ( m_AttributeManager.GetItem()->GetStaticData()->GetNumAttachedModels( GetTeamNumber() ) > 0 );
+ return bUseOverride;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+int CEconEntity::DrawOverriddenViewmodel( C_BaseViewModel *pViewmodel, int flags )
+{
+ int ret = 0;
+#ifndef DOTA_DLL
+ bool bIsAttachmentTranslucent = m_hViewmodelAttachment.Get() ? m_hViewmodelAttachment->IsTransparent() : false;
+ bool bUseOverride = false;
+
+ CEconItemView *pItem = GetAttributeContainer()->GetItem();
+ bool bAttachesToHands = ( pItem->IsValid() && (pItem->GetStaticData()->ShouldAttachToHands() || pItem->GetStaticData()->ShouldAttachToHandsVMOnly()));
+
+ // If the attachment is translucent, we need to render the viewmodel first
+ if ( bIsAttachmentTranslucent )
+ {
+ ret = pViewmodel->DrawOverriddenViewmodel( flags );
+ }
+
+ if ( flags & STUDIO_RENDER )
+ {
+ // If there is some other material override, it's probably the client asking for us to render invuln or the
+ // spy cloaking. Those are way more important than ours, so do them instead.
+ IMaterial* pOverrideMaterial = NULL;
+ OverrideType_t nDontcare = OVERRIDE_NORMAL;
+ modelrender->GetMaterialOverride( &pOverrideMaterial, &nDontcare );
+ bool bIgnoreOverride = pOverrideMaterial != NULL;
+
+ bUseOverride = !bIgnoreOverride && (GetTeamNumber() >= 0 && GetTeamNumber() < TEAM_VISUAL_SECTIONS) && m_MaterialOverrides[GetTeamNumber()].IsValid();
+ if ( bUseOverride )
+ {
+ modelrender->ForcedMaterialOverride( m_MaterialOverrides[GetTeamNumber()] );
+ flags |= STUDIO_NO_OVERRIDE_FOR_ATTACH;
+ }
+
+ if ( m_hViewmodelAttachment )
+ {
+ m_hViewmodelAttachment->RemoveEffects( EF_NODRAW );
+ m_hViewmodelAttachment->DrawModel( flags );
+ m_hViewmodelAttachment->AddEffects( EF_NODRAW );
+ }
+
+ // if we are attached to the hands, then we DO NOT want have an override material when we draw our view model
+ if ( bAttachesToHands && bUseOverride )
+ {
+ modelrender->ForcedMaterialOverride( NULL );
+ bUseOverride = false;
+ }
+ }
+
+ if ( !bIsAttachmentTranslucent )
+ {
+ ret = pViewmodel->DrawOverriddenViewmodel( flags );
+ }
+
+ if ( bUseOverride )
+ {
+ modelrender->ForcedMaterialOverride( NULL );
+ }
+#endif // !defined( DOTA_DLL )
+ return ret;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CEconEntity::OnInternalDrawModel( ClientModelRenderInfo_t *pInfo )
+{
+ if ( !BaseClass::OnInternalDrawModel( pInfo ) )
+ return false;
+
+ DrawEconEntityAttachedModels( this, this, pInfo, kAttachedModelDisplayFlag_WorldModel );
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+int CEconEntity::LookupAttachment( const char *pAttachmentName )
+{
+ if ( m_hViewmodelAttachment )
+ return m_hViewmodelAttachment->LookupAttachment( pAttachmentName );
+
+ return BaseClass::LookupAttachment( pAttachmentName );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CEconEntity::GetAttachment( int number, matrix3x4_t &matrix )
+{
+ if ( m_hViewmodelAttachment )
+ return m_hViewmodelAttachment->GetAttachment( number, matrix );
+
+ return BaseClass::GetAttachment( number, matrix );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CEconEntity::GetAttachment( int number, Vector &origin )
+{
+ if ( m_hViewmodelAttachment )
+ return m_hViewmodelAttachment->GetAttachment( number, origin );
+
+ return BaseClass::GetAttachment( number, origin );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CEconEntity::GetAttachment( int number, Vector &origin, QAngle &angles )
+{
+ if ( m_hViewmodelAttachment )
+ return m_hViewmodelAttachment->GetAttachment( number, origin, angles );
+
+ return BaseClass::GetAttachment( number, origin, angles );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CEconEntity::GetAttachmentVelocity( int number, Vector &originVel, Quaternion &angleVel )
+{
+ if ( m_hViewmodelAttachment )
+ return m_hViewmodelAttachment->GetAttachmentVelocity( number, originVel, angleVel );
+
+ return BaseClass::GetAttachmentVelocity( number, originVel, angleVel );
+}
+
+#endif
+
+//-----------------------------------------------------------------------------
+// Purpose: Hides or shows masked bodygroups associated with this item.
+//-----------------------------------------------------------------------------
+bool CEconEntity::UpdateBodygroups( CBaseCombatCharacter* pOwner, int iState )
+{
+ if ( !pOwner )
+ return false;
+
+ CAttributeContainer *pCont = GetAttributeContainer();
+ if ( !pCont )
+ return false;
+
+ CEconItemView *pItem = pCont->GetItem();
+ if ( !pItem )
+ return false;
+
+ const CEconItemDefinition *pItemDef = pItem->GetItemDefinition();
+ if ( !pItemDef )
+ return false;
+
+ int iNumBodyGroups = pItemDef->GetNumModifiedBodyGroups( 0 );
+ for ( int i=0; i<iNumBodyGroups; ++i )
+ {
+ int iBody = 0;
+ const char *pszBodyGroup = pItemDef->GetModifiedBodyGroup( 0, i, iBody );
+ if ( iBody != iState )
+ continue;
+
+ int iBodyGroup = pOwner->FindBodygroupByName( pszBodyGroup );
+
+ if ( iBodyGroup == -1 )
+ continue;
+
+ pOwner->SetBodygroup( iBodyGroup, iState );
+ }
+
+ // Handle per-style bodygroup hiding
+ const CEconStyleInfo *pStyle = pItemDef->GetStyleInfo( pItem->GetStyle() );
+ if ( pStyle )
+ {
+ FOR_EACH_VEC( pStyle->GetAdditionalHideBodygroups(), i )
+ {
+ int iBodyGroup = pOwner->FindBodygroupByName( pStyle->GetAdditionalHideBodygroups()[i] );
+
+ if ( iBodyGroup == -1 )
+ continue;
+
+ pOwner->SetBodygroup( iBodyGroup, iState );
+ }
+
+ // should we override this model bodygroup
+ if ( pStyle->GetBodygroupName() != NULL )
+ {
+ int iBodyGroup = pOwner->FindBodygroupByName( pStyle->GetBodygroupName() );
+ if ( iBodyGroup != -1 )
+ {
+ SetBodygroup( iBodyGroup, pStyle->GetBodygroupSubmodelIndex() );
+ }
+ }
+ }
+
+ // Handle world model bodygroup overrides
+ int iBodyOverride = pItemDef->GetWorldmodelBodygroupOverride( pOwner->GetTeamNumber() );
+ int iBodyStateOverride = pItemDef->GetWorldmodelBodygroupStateOverride( pOwner->GetTeamNumber() );
+ if ( iBodyOverride > -1 && iBodyStateOverride > -1 )
+ {
+ pOwner->SetBodygroup( iBodyOverride, iBodyStateOverride );
+ }
+
+ // Handle view model bodygroup overrides
+ iBodyOverride = pItemDef->GetViewmodelBodygroupOverride( pOwner->GetTeamNumber() );
+ iBodyStateOverride = pItemDef->GetViewmodelBodygroupStateOverride( pOwner->GetTeamNumber() );
+ if ( iBodyOverride > -1 && iBodyStateOverride > -1 )
+ {
+ CBasePlayer *pPlayer = ToBasePlayer( pOwner );
+ if ( pPlayer )
+ {
+ CBaseViewModel *pVM = pPlayer->GetViewModel();
+ if ( pVM && pVM->GetModelPtr() )
+ {
+ pVM->SetBodygroup( iBodyOverride, iBodyStateOverride );
+ }
+ }
+ }
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CBaseAttributableItem::CBaseAttributableItem()
+{
+}
diff --git a/game/shared/econ/econ_entity.h b/game/shared/econ/econ_entity.h
new file mode 100644
index 0000000..e0c2453
--- /dev/null
+++ b/game/shared/econ/econ_entity.h
@@ -0,0 +1,229 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+
+#ifndef ECON_ENTITY_H
+#define ECON_ENTITY_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "ihasattributes.h"
+#include "ihasowner.h"
+#include "attribute_manager.h"
+#include "econ_item_view.h"
+
+#if defined( CLIENT_DLL )
+#define CEconEntity C_EconEntity
+#define CBaseAttributableItem C_BaseAttributableItem
+
+// Additional attachments.
+struct AttachedModelData_t
+{
+ const model_t *m_pModel;
+ int m_iModelDisplayFlags;
+};
+
+#endif
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+class CEconEntity : public CBaseAnimating, public IHasAttributes
+{
+ DECLARE_CLASS( CEconEntity, CBaseAnimating );
+public:
+ DECLARE_NETWORKCLASS();
+ DECLARE_DATADESC();
+ CEconEntity();
+ ~CEconEntity();
+
+ void InitializeAttributes( void );
+ void DebugDescribe( void );
+ Activity TranslateViewmodelHandActivity( Activity actBase );
+ virtual void UpdateOnRemove( void );
+
+ virtual CStudioHdr * OnNewModel();
+
+#if !defined( CLIENT_DLL )
+ virtual void GiveTo( CBaseEntity *pOther ) {}
+ void OnOwnerClassChange( void );
+ void UpdateModelToClass( void );
+ void PlayAnimForPlaybackEvent( wearableanimplayback_t iPlayback );
+ virtual int CalculateVisibleClassFor( CBaseCombatCharacter *pPlayer );
+
+#if defined(TF_DLL) || defined(TF_CLIENT_DLL)
+ void MarkAttachedEntityAsValidated() { m_bValidatedAttachedEntity = true; }
+#endif // TF_DLL || TF_CLIENT_DLL
+
+#else
+ enum ParticleSystemState_t
+ {
+ PARTICLE_SYSTEM_STATE_NOT_VISIBLE,
+ PARTICLE_SYSTEM_STATE_VISIBLE,
+ PARTICLE_SYSTEM_STATE_VISIBLE_VM
+ };
+
+ virtual void Release();
+ virtual void SetDormant( bool bDormant );
+ virtual void OnPreDataChanged( DataUpdateType_t type );
+ virtual void OnDataChanged( DataUpdateType_t updateType );
+ virtual bool ShouldShowToolTip( void ) { return true; }
+ virtual bool InitializeAsClientEntity( const char *pszModelName, RenderGroup_t renderGroup );
+ virtual bool OnInternalDrawModel( ClientModelRenderInfo_t *pInfo );
+ virtual IMaterial *GetEconWeaponMaterialOverride( int iTeam ) OVERRIDE;
+ virtual void FireEvent( const Vector& origin, const QAngle& angles, int event, const char *options );
+ virtual bool OnFireEvent( C_BaseViewModel *pViewModel, const Vector& origin, const QAngle& angles, int event, const char *options );
+ bool InternalFireEvent( const Vector& origin, const QAngle& angles, int event, const char *options );
+
+ // Custom flex controllers
+ virtual bool UsesFlexDelayedWeights( void );
+ virtual void SetupWeights( const matrix3x4_t *pBoneToWorld, int nFlexWeightCount, float *pFlexWeights, float *pFlexDelayedWeights );
+ float m_flFlexDelayTime;
+ float * m_flFlexDelayedWeight;
+ int m_cFlexDelayedWeight;
+
+ // Custom particle attachments
+ bool HasCustomParticleSystems( void ) const;
+ void UpdateParticleSystems( void );
+ virtual bool ShouldDrawParticleSystems( void );
+ void SetParticleSystemsVisible( ParticleSystemState_t bVisible );
+ void UpdateSingleParticleSystem( bool bVisible, const attachedparticlesystem_t *pSystem );
+ virtual void UpdateAttachmentModels( void );
+ virtual bool AttachmentModelsShouldBeVisible( void ) { return true; }
+ void GetEconParticleSystems( CUtlVector<const attachedparticlesystem_t *> *out_pvecParticleSystems ) const;
+
+ // Model swaping
+ bool ShouldDraw( void );
+ bool ShouldHideForVisionFilterFlags( void );
+
+ virtual bool IsTransparent( void ) OVERRIDE;
+
+ // Viewmodel overriding
+ virtual bool ViewModel_IsTransparent( void );
+ virtual bool ViewModel_IsUsingFBTexture( void );
+ virtual bool IsOverridingViewmodel( void );
+ virtual int DrawOverriddenViewmodel( C_BaseViewModel *pViewmodel, int flags );
+
+ // Attachments
+ bool WantsToOverrideViewmodelAttachments( void ) { return (m_hViewmodelAttachment != NULL); }
+ virtual int LookupAttachment( const char *pAttachmentName );
+ virtual bool GetAttachment( const char *szName, Vector &absOrigin ) { return BaseClass::GetAttachment(szName,absOrigin); }
+ virtual bool GetAttachment( const char *szName, Vector &absOrigin, QAngle &absAngles ) { return BaseClass::GetAttachment(szName,absOrigin,absAngles); }
+ virtual bool GetAttachment( int number, matrix3x4_t &matrix );
+ virtual bool GetAttachment( int number, Vector &origin );
+ virtual bool GetAttachment( int number, Vector &origin, QAngle &angles );
+ virtual bool GetAttachmentVelocity( int number, Vector &originVel, Quaternion &angleVel );
+
+ C_BaseAnimating *GetViewmodelAttachment( void ) { return m_hViewmodelAttachment.Get(); }
+ virtual void ViewModelAttachmentBlending( CStudioHdr *hdr, Vector pos[], Quaternion q[], float currentTime, int boneMask ) {}
+
+ void SetWaitingToLoad( bool bWaiting );
+
+ virtual bool ValidateEntityAttachedToPlayer( bool &bShouldRetry );
+
+ virtual void SetMaterialOverride( int team, const char *pszMaterial );
+ virtual void SetMaterialOverride( int team, CMaterialReference &ref );
+
+ // Deal with recording
+ virtual void GetToolRecordingState( KeyValues *msg );
+
+#endif
+
+public:
+ // IHasAttributes
+ CAttributeManager *GetAttributeManager( void ) { return &m_AttributeManager; }
+ CAttributeContainer *GetAttributeContainer( void ) { return &m_AttributeManager; }
+ const CAttributeContainer *GetAttributeContainer( void ) const { return &m_AttributeManager; }
+ CBaseEntity *GetAttributeOwner( void ) { return GetOwnerEntity(); }
+ CAttributeList *GetAttributeList( void ) { return m_AttributeManager.GetItem()->GetAttributeList(); }
+ virtual void ReapplyProvision( void );
+
+ virtual bool UpdateBodygroups( CBaseCombatCharacter* pOwner, int iState );
+
+protected:
+ virtual Activity TranslateViewmodelHandActivityInternal( Activity actBase ) { return actBase; }
+
+protected:
+ CNetworkVarEmbedded( CAttributeContainer, m_AttributeManager );
+
+#if defined(TF_DLL) || defined(TF_CLIENT_DLL)
+ CNetworkVar( bool, m_bValidatedAttachedEntity );
+#endif // TF_DLL || TF_CLIENT_DLL
+
+#ifdef CLIENT_DLL
+ bool m_bClientside;
+ ParticleSystemState_t m_nParticleSystemsCreated;
+ CMaterialReference m_MaterialOverrides[TEAM_VISUAL_SECTIONS];
+ CHandle<C_BaseAnimating> m_hViewmodelAttachment;
+ int m_iOldTeam;
+ bool m_bAttachmentDirty;
+ int m_nUnloadedModelIndex;
+ int m_iNumOwnerValidationRetries;
+#endif
+
+ bool m_bHasParticleSystems;
+ EHANDLE m_hOldProvidee;
+
+#ifdef GAME_DLL
+ int m_iOldOwnerClass; // Used to detect class changes on items that have per-class models
+#endif
+
+protected:
+#ifdef CLIENT_DLL
+
+public:
+
+ CUtlVector<AttachedModelData_t> m_vecAttachedModels;
+
+#endif // CLIENT_DLL
+};
+
+#define ITEM_PICKUP_BOX_BLOAT 24
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+class CBaseAttributableItem : public CEconEntity
+{
+ DECLARE_CLASS( CBaseAttributableItem, CEconEntity );
+public:
+ DECLARE_NETWORKCLASS();
+ DECLARE_DATADESC();
+
+ CBaseAttributableItem();
+};
+
+#if defined( CLIENT_DLL )
+#ifndef DOTA_DLL
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+class C_ViewmodelAttachmentModel : public C_BaseAnimating, public IHasOwner
+{
+ DECLARE_CLASS( C_ViewmodelAttachmentModel, C_BaseAnimating );
+public:
+ void SetOuter( CEconEntity *pOuter );
+ CHandle<CEconEntity> GetOuter( void ) { return m_hOuter; }
+ bool InitializeAsClientEntity( const char *pszModelName, RenderGroup_t renderGroup );
+ int InternalDrawModel( int flags );
+ bool OnPostInternalDrawModel( ClientModelRenderInfo_t *pInfo );
+ virtual void StandardBlendingRules( CStudioHdr *hdr, Vector pos[], Quaternion q[], float currentTime, int boneMask );
+
+ virtual CBaseEntity *GetOwnerViaInterface( void ) { return GetOuter()->GetAttributeOwner(); }
+
+ virtual void FormatViewModelAttachment( int nAttachment, matrix3x4_t &attachmentToWorld );
+
+ virtual int GetSkin( void );
+
+private:
+ CHandle<CEconEntity> m_hOuter;
+ bool m_bAlwaysFlip;
+};
+#endif // !defined( DOTA_DLL )
+#endif // defined( CLIENT_DLL )
+
+#endif // ECON_ENTITY_H
diff --git a/game/shared/econ/econ_entity_creation.cpp b/game/shared/econ/econ_entity_creation.cpp
new file mode 100644
index 0000000..2461005
--- /dev/null
+++ b/game/shared/econ/econ_entity_creation.cpp
@@ -0,0 +1,180 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+
+#include "cbase.h"
+#include "econ_entity_creation.h"
+#include "utldict.h"
+#include "filesystem.h"
+#include "gamestringpool.h"
+#include "KeyValues.h"
+#include "attribute_manager.h"
+#include "vgui/ILocalize.h"
+#include "tier3/tier3.h"
+#include "util_shared.h"
+
+#ifdef TF_CLIENT_DLL
+#include "c_tf_player.h"
+#endif // TF_CLIENT_DLL
+
+//==================================================================================
+// GENERATION SYSTEM
+//==================================================================================
+CItemGeneration g_ItemGenerationSystem;
+CItemGeneration *ItemGeneration( void )
+{
+ return &g_ItemGenerationSystem;
+}
+
+//-----------------------------------------------------------------------------
+// Constructor, destructor
+//-----------------------------------------------------------------------------
+CItemGeneration::CItemGeneration( void )
+{
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Generate a random item matching the specified criteria
+//-----------------------------------------------------------------------------
+CBaseEntity *CItemGeneration::GenerateRandomItem( CItemSelectionCriteria *pCriteria, const Vector &vecOrigin, const QAngle &vecAngles )
+{
+ entityquality_t iQuality;
+ int iChosenItem = ItemSystem()->GenerateRandomItem( pCriteria, &iQuality );
+ if ( iChosenItem == INVALID_ITEM_DEF_INDEX )
+ return NULL;
+
+ return SpawnItem( iChosenItem, vecOrigin, vecAngles, pCriteria->GetItemLevel(), iQuality, NULL );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Generate a random item matching the specified definition index
+//-----------------------------------------------------------------------------
+CBaseEntity *CItemGeneration::GenerateItemFromDefIndex( int iDefIndex, const Vector &vecOrigin, const QAngle &vecAngles )
+{
+ return SpawnItem( iDefIndex, vecOrigin, vecAngles, 1, AE_UNIQUE, NULL );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Generate an item from the specified item data
+//-----------------------------------------------------------------------------
+CBaseEntity *CItemGeneration::GenerateItemFromScriptData( const CEconItemView *pData, const Vector &vecOrigin, const QAngle &vecAngles, const char *pszOverrideClassName )
+{
+ return SpawnItem( pData, vecOrigin, vecAngles, pszOverrideClassName );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Generate the base item for a class's loadout slot
+//-----------------------------------------------------------------------------
+CBaseEntity *CItemGeneration::GenerateBaseItem( struct baseitemcriteria_t *pCriteria )
+{
+ int iChosenItem = ItemSystem()->GenerateBaseItem( pCriteria );
+ if ( iChosenItem == INVALID_ITEM_DEF_INDEX )
+ return NULL;
+
+ return SpawnItem( iChosenItem, vec3_origin, vec3_angle, 1, AE_NORMAL, NULL );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Create a new instance of the chosen item
+//-----------------------------------------------------------------------------
+CBaseEntity *CItemGeneration::SpawnItem( int iChosenItem, const Vector &vecAbsOrigin, const QAngle &vecAbsAngles, int iItemLevel, entityquality_t entityQuality, const char *pszOverrideClassName )
+{
+ CEconItemDefinition *pData = ItemSystem()->GetStaticDataForItemByDefIndex( iChosenItem );
+ if ( !pData )
+ return NULL;
+
+ if ( !pszOverrideClassName )
+ {
+ pszOverrideClassName = pData->GetItemClass();
+ }
+
+ if ( !pszOverrideClassName )
+ return NULL;
+
+ CBaseEntity *pItem = CreateEntityByName( pszOverrideClassName );
+ if ( !pItem )
+ return NULL;
+
+ // Set the item level & quality
+ IHasAttributes *pItemInterface = GetAttribInterface( pItem );
+ Assert( pItemInterface );
+ if ( pItemInterface )
+ {
+ // Setup the script item. Don't generate attributes here, because it'll be done during entity spawn.
+ CEconItemView *pScriptItem = pItemInterface->GetAttributeContainer()->GetItem();
+ pScriptItem->Init( iChosenItem, entityQuality, iItemLevel, false );
+ }
+
+ return PostSpawnItem( pItem, pItemInterface, vecAbsOrigin, vecAbsAngles );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Create a base entity for the specified item data
+//-----------------------------------------------------------------------------
+CBaseEntity *CItemGeneration::SpawnItem( const CEconItemView *pData, const Vector &vecAbsOrigin, const QAngle &vecAbsAngles, const char *pszOverrideClassName )
+{
+ if ( !pData->GetStaticData() )
+ return NULL;
+
+ if ( !pszOverrideClassName )
+ {
+ pszOverrideClassName = pData->GetStaticData()->GetItemClass();
+ }
+
+ if ( !pszOverrideClassName )
+ return NULL;
+
+ CBaseEntity *pItem = CreateEntityByName( pszOverrideClassName );
+ if ( !pItem )
+ return NULL;
+
+ // Set the item level & quality
+ IHasAttributes *pItemInterface = GetAttribInterface( pItem );
+ Assert( pItemInterface );
+ if ( pItemInterface )
+ {
+ pItemInterface->GetAttributeContainer()->SetItem( pData );
+ }
+
+ return PostSpawnItem( pItem, pItemInterface, vecAbsOrigin, vecAbsAngles );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CBaseEntity *CItemGeneration::PostSpawnItem( CBaseEntity *pItem, IHasAttributes *pItemInterface, const Vector &vecAbsOrigin, const QAngle &vecAbsAngles )
+{
+#ifdef CLIENT_DLL
+ const char *pszPlayerModel = NULL;
+ if ( pItemInterface )
+ {
+ CEconItemView *pScriptItem = pItemInterface->GetAttributeContainer()->GetItem();
+
+ int iClass = 0;
+ int iTeam = 0;
+#ifdef TF_CLIENT_DLL
+ C_TFPlayer *pTFPlayer = ToTFPlayer( GetPlayerByAccountID( pScriptItem->GetAccountID() ) );
+ if ( pTFPlayer )
+ {
+ iClass = pTFPlayer->GetPlayerClass()->GetClassIndex();
+ iTeam = pTFPlayer->GetTeamNumber();
+ }
+#endif // TF_CLIENT_DLL
+ pszPlayerModel = pScriptItem->GetPlayerDisplayModel( iClass, iTeam );
+ }
+
+ // If we create a clientside item, we need to force it to initialize attributes
+ if ( pItem->InitializeAsClientEntity( pszPlayerModel, RENDER_GROUP_OPAQUE_ENTITY ) == false )
+ return NULL;
+#endif // CLIENT_DLL
+
+ pItem->SetAbsOrigin( vecAbsOrigin );
+ pItem->SetAbsAngles( vecAbsAngles );
+
+ pItem->Spawn();
+ pItem->Activate();
+ return pItem;
+}
+
diff --git a/game/shared/econ/econ_entity_creation.h b/game/shared/econ/econ_entity_creation.h
new file mode 100644
index 0000000..2abf8c6
--- /dev/null
+++ b/game/shared/econ/econ_entity_creation.h
@@ -0,0 +1,50 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+
+#ifndef ITEM_CREATION_H
+#define ITEM_CREATION_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "igamesystem.h"
+#include "econ_item_system.h"
+#include "econ_entity.h"
+
+#if defined(TF_CLIENT_DLL) || defined(TF_DLL)
+#include "tf_shareddefs.h"
+#endif
+
+//-----------------------------------------------------------------------------
+// Purpose: Game system that handles initializing the item system, and generating items as full game entities
+//-----------------------------------------------------------------------------
+class CItemGeneration : public CAutoGameSystem
+{
+public:
+ CItemGeneration( void );
+
+ // Generate a random item matching the specified criteria
+ CBaseEntity *GenerateRandomItem( CItemSelectionCriteria *pCriteria, const Vector &vecOrigin, const QAngle &vecAngles );
+
+ // Generate a random item matching the specified definition index
+ CBaseEntity *GenerateItemFromDefIndex( int iDefIndex, const Vector &vecOrigin, const QAngle &vecAngles );
+
+ // Generate an item from the specified item data
+ CBaseEntity *GenerateItemFromScriptData( const CEconItemView *pData, const Vector &vecOrigin, const QAngle &vecAngles, const char *pszOverrideClassName );
+
+ // Generate the base item for a class's loadout slot
+ CBaseEntity *GenerateBaseItem( struct baseitemcriteria_t *pCriteria );
+
+private:
+ // Create a new instance of the chosen item
+ CBaseEntity *SpawnItem( int iChosenItem, const Vector &vecAbsOrigin, const QAngle &vecAbsAngles, int iItemLevel, entityquality_t entityQuality, const char *pszOverrideClassName );
+ CBaseEntity *SpawnItem( const CEconItemView *pData, const Vector &vecAbsOrigin, const QAngle &vecAbsAngles, const char *pszOverrideClassName );
+ CBaseEntity *PostSpawnItem( CBaseEntity *pItem, IHasAttributes *pItemInterface, const Vector &vecAbsOrigin, const QAngle &vecAbsAngles );
+};
+
+extern CItemGeneration *ItemGeneration( void );
+
+#endif // ITEM_CREATION_H \ No newline at end of file
diff --git a/game/shared/econ/econ_experiment.cpp b/game/shared/econ/econ_experiment.cpp
new file mode 100644
index 0000000..b6118b8
--- /dev/null
+++ b/game/shared/econ/econ_experiment.cpp
@@ -0,0 +1,19 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include "cbase.h"
+
+#include "econ_experiment.h"
+
+using namespace GCSDK;
+
+#ifdef GC_DLL
+IMPLEMENT_CLASS_MEMPOOL( CEconExperiment, 10 * 10000, UTLMEMORYPOOL_GROW_SLOW );
+#endif
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
diff --git a/game/shared/econ/econ_experiment.h b/game/shared/econ/econ_experiment.h
new file mode 100644
index 0000000..20a09a2
--- /dev/null
+++ b/game/shared/econ/econ_experiment.h
@@ -0,0 +1,27 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Holds the CEconExperiment
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef TFEXPERIMENT_H
+#define TFEXPERIMENT_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "gcsdk/schemasharedobject.h"
+
+//---------------------------------------------------------------------------------
+// Purpose:
+//---------------------------------------------------------------------------------
+class CEconExperiment : public GCSDK::CSchemaSharedObject< CSchExperiment, k_EEconTypeExperiment >
+{
+#ifdef GC_DLL
+ DECLARE_CLASS_MEMPOOL( CEconExperiment );
+#endif
+
+};
+
+#endif // TFEXPERIMENT_H
diff --git a/game/shared/econ/econ_game_account.cpp b/game/shared/econ/econ_game_account.cpp
new file mode 100644
index 0000000..2c8a923
--- /dev/null
+++ b/game/shared/econ/econ_game_account.cpp
@@ -0,0 +1,17 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Code for the CEconGameAccount object
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include "cbase.h"
+
+using namespace GCSDK;
+
+#ifdef GC_DLL
+IMPLEMENT_CLASS_MEMPOOL( CEconGameAccount, 10 * 1000, UTLMEMORYPOOL_GROW_SLOW );
+#endif
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
diff --git a/game/shared/econ/econ_game_account.h b/game/shared/econ/econ_game_account.h
new file mode 100644
index 0000000..a6b2a84
--- /dev/null
+++ b/game/shared/econ/econ_game_account.h
@@ -0,0 +1,57 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Holds the CEconGameAccount object
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef ECON_GAME_ACCOUNT_H
+#define ECON_GAME_ACCOUNT_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "gcsdk/schemasharedobject.h"
+#include "rtime.h"
+
+enum
+{
+ kGameAccountFlags_ConvertedUniques = 1 << 0,
+ kGameAccountFlags_MadeFirstPurchase = 1 << 1,
+ kGameAccountFlags_ConvertItemFlagsToOrigin = 1 << 2,
+ kGameAccountFlags_ConvertPackageItemGrants = 1 << 3,
+ kGameAccountFlags_RemoveCafeOrSchoolItems = 1 << 4,
+ kGameAccountFlags_CleanupItemNames = 1 << 5,
+ kGameAccountFlags_NeedToChooseMostHelpfulFriend = 1 << 6,
+ kGameAccountFlags_DONT_USE_THIS_BUI_LIES = 1 << 7, // some accounts might have this set! it used to be the "needs to thank a friend" bit
+ kGameAccountFlags_OwnedGameServersDisabled = 1 << 8,
+ kGameAccountFlags_UpdatedEquippedSlots = 1 << 9,
+ kGameAccountFlags_MadeFirstWebPurchase = 1 << 10,
+ kGameAccountFlags_UpdatedPresetOriginalItemIDs = 1 << 11,
+ kGameAccountFlags_GC_UpgradedToPremium = 1 << 12, // we did something (used an item, whatever) on the GC that means the GC has decided we're premium regardless of what Steam says
+ // Deprecated
+ // kGameAccountFlags_InitializedSkillRating = 1 << 13,
+ // kGameAccountFlags_InitializedSkillRating6v6 = 1 << 14,
+ // kGameAccountFlags_InitializedSkillRating9v9 = 1 << 15,
+ kGameAccountFlags_InitializedKickBucket = 1 << 16,
+};
+
+//---------------------------------------------------------------------------------
+// Purpose: All the account-level information that the GC tracks
+//---------------------------------------------------------------------------------
+class CEconGameAccount : public GCSDK::CSchemaSharedObject< CSchGameAccount, k_EEconTypeGameAccount >
+{
+#ifdef GC_DLL
+ DECLARE_CLASS_MEMPOOL( CEconGameAccount );
+#endif
+
+public:
+ CEconGameAccount() {}
+ CEconGameAccount( uint32 unAccountID )
+ {
+ Obj().m_unAccountID = unAccountID;
+ Obj().m_rtime32FirstPlayed = CRTime::RTime32TimeCur();
+ }
+};
+
+#endif //ECON_GAME_ACCOUNT_H
diff --git a/game/shared/econ/econ_game_account_client.cpp b/game/shared/econ/econ_game_account_client.cpp
new file mode 100644
index 0000000..d92e6cf
--- /dev/null
+++ b/game/shared/econ/econ_game_account_client.cpp
@@ -0,0 +1,18 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Code for the CEconGameAccountClient object
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include "cbase.h"
+
+using namespace GCSDK;
+
+#ifdef GC
+IMPLEMENT_CLASS_MEMPOOL( CEconGameAccountClient, 10 * 1000, UTLMEMORYPOOL_GROW_SLOW );
+#endif
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
diff --git a/game/shared/econ/econ_game_account_client.h b/game/shared/econ/econ_game_account_client.h
new file mode 100644
index 0000000..3720868
--- /dev/null
+++ b/game/shared/econ/econ_game_account_client.h
@@ -0,0 +1,29 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Holds the CEconGameAccountClient object
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef ECON_GAME_ACCOUNT_CLIENT_H
+#define ECON_GAME_ACCOUNT_CLIENT_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "gcsdk/protobufsharedobject.h"
+#include "base_gcmessages.pb.h"
+
+//---------------------------------------------------------------------------------
+// Purpose: All the account-level information that the GC tracks
+//---------------------------------------------------------------------------------
+class CEconGameAccountClient : public GCSDK::CProtoBufSharedObject< CSOEconGameAccountClient, k_EEconTypeGameAccountClient >
+{
+#ifdef GC
+ DECLARE_CLASS_MEMPOOL( CEconGameAccountClient );
+public:
+ virtual bool BIsDatabaseBacked() const { return false; }
+#endif
+};
+
+#endif //ECON_GAME_ACCOUNT_CLIENT_H
diff --git a/game/shared/econ/econ_game_account_server.cpp b/game/shared/econ/econ_game_account_server.cpp
new file mode 100644
index 0000000..4ce65ba
--- /dev/null
+++ b/game/shared/econ/econ_game_account_server.cpp
@@ -0,0 +1,40 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Code for the CEconGameAccount object
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include "cbase.h"
+
+#include "econ_game_account_server.h"
+
+using namespace GCSDK;
+
+#ifdef GC_DLL
+//---------------------------------------------------------------------------------
+// Purpose:
+//---------------------------------------------------------------------------------
+IMPLEMENT_CLASS_MEMPOOL( CEconGameServerAccount, 100, UTLMEMORYPOOL_GROW_SLOW );
+
+void GameServerAccount_GenerateIdentityToken( char* pIdentityToken, uint32 unMaxChars )
+{
+ static const char s_ValidChars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890./+!$%^-_+?<>()&~:";
+ const int nLastValidIndex = ARRAYSIZE(s_ValidChars) - 2; // last = size - 1, minus another one for null terminator
+
+ // create a randomized token
+ for ( uint32 i = 0; i < unMaxChars - 1; ++i )
+ {
+ pIdentityToken[i] = s_ValidChars[ RandomInt( 0, nLastValidIndex ) ];
+ }
+ pIdentityToken[unMaxChars - 1] = 0;
+}
+
+//---------------------------------------------------------------------------------
+// Purpose: Selective account-level data for game servers
+//---------------------------------------------------------------------------------
+IMPLEMENT_CLASS_MEMPOOL( CEconGameAccountForGameServers, 10 * 1000, UTLMEMORYPOOL_GROW_SLOW );
+#endif
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
diff --git a/game/shared/econ/econ_game_account_server.h b/game/shared/econ/econ_game_account_server.h
new file mode 100644
index 0000000..01c1cc4
--- /dev/null
+++ b/game/shared/econ/econ_game_account_server.h
@@ -0,0 +1,110 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Holds the CEconGameServerAccount object
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef ECON_GAME_SERVER_ACCOUNT_H
+#define ECON_GAME_SERVER_ACCOUNT_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+enum eGameServerOrigin
+{
+ kGSAOrigin_Player = 0,
+ kGSAOrigin_Support = 1,
+ kGSAOrigin_AutoRegister = 2, // for valve-owned servers
+};
+
+enum eGameServerScoreStanding
+{
+ kGSStanding_Good,
+ kGSStanding_Bad,
+};
+
+enum eGameServerScoreStandingTrend
+{
+ kGSStandingTrend_Up,
+ kGSStandingTrend_SteadyUp,
+ kGSStandingTrend_Steady,
+ kGSStandingTrend_SteadyDown,
+ kGSStandingTrend_Down,
+};
+
+#ifdef GC
+#include "gcsdk/schemasharedobject.h"
+
+//---------------------------------------------------------------------------------
+// Purpose:
+//---------------------------------------------------------------------------------
+class CEconGameServerAccount : public GCSDK::CSchemaSharedObject< CSchGameServerAccount, k_EEconTypeGameServerAccount >
+{
+#ifdef GC_DLL
+ DECLARE_CLASS_MEMPOOL( CEconGameServerAccount );
+#endif
+
+public:
+ CEconGameServerAccount() {}
+ CEconGameServerAccount( uint32 unAccountID )
+ {
+ Obj().m_unAccountID = unAccountID;
+ }
+};
+
+void GameServerAccount_GenerateIdentityToken( char* pIdentityToken, uint32 unMaxChars );
+#endif // GC
+
+inline const char *GameServerAccount_GetStandingString( eGameServerScoreStanding standing )
+{
+ const char *pStanding = "Good";
+ switch ( standing )
+ {
+ case kGSStanding_Good:
+ pStanding = "Good";
+ break;
+ case kGSStanding_Bad:
+ pStanding = "Bad";
+ break;
+ } // switch
+ return pStanding;
+}
+
+inline const char *GameServerAccount_GetStandingTrendString( eGameServerScoreStandingTrend trend )
+{
+ const char *pStandingTrend = "Steady";
+ switch ( trend )
+ {
+ case kGSStandingTrend_Up:
+ pStandingTrend = "Upward Fast";
+ break;
+ case kGSStandingTrend_SteadyUp:
+ pStandingTrend = "Slightly Upward";
+ break;
+ case kGSStandingTrend_Steady:
+ pStandingTrend = "Steady";
+ break;
+ case kGSStandingTrend_SteadyDown:
+ pStandingTrend = "Slightly Downward";
+ break;
+ case kGSStandingTrend_Down:
+ pStandingTrend = "Downward Fast";
+ break;
+ } // switch
+ return pStandingTrend;
+}
+
+//---------------------------------------------------------------------------------
+// Purpose: Selective account-level data for game servers
+//---------------------------------------------------------------------------------
+class CEconGameAccountForGameServers : public GCSDK::CProtoBufSharedObject < CSOEconGameAccountForGameServers, k_EEconTypeGameAccountForGameServers >
+{
+#ifdef GC
+ DECLARE_CLASS_MEMPOOL( CEconGameAccountForGameServers );
+public:
+ virtual bool BIsDatabaseBacked() const { return false; }
+#endif
+};
+
+#endif //ECON_GAME_SERVER_ACCOUNT_H
diff --git a/game/shared/econ/econ_gcmessages.h b/game/shared/econ/econ_gcmessages.h
new file mode 100644
index 0000000..c5e5d7a
--- /dev/null
+++ b/game/shared/econ/econ_gcmessages.h
@@ -0,0 +1,345 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: This file defines all of our over-the-wire net protocols for the
+// Game Coordinator for the item system. Note that we never use types
+// with undefined length (like int). Always use an explicit type
+// (like int32).
+//
+//=============================================================================
+
+#ifndef ITEM_GCMESSAGES_H
+#define ITEM_GCMESSAGES_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "econ_gcmessages.pb.h"
+
+#pragma pack( push, 1 )
+
+
+// generic zero-length message struct
+struct MsgGCEmpty_t
+{
+
+};
+
+// k_EMsgGCSetItemPosition
+struct MsgGCSetItemPosition_t
+{
+ uint64 m_unItemID;
+ uint32 m_unNewPosition;
+};
+
+// k_EMsgGCCraft
+struct MsgGCCraft_t
+{
+ int16 m_nRecipeDefIndex;
+ uint16 m_nItemCount;
+ // list of m_nItemCount uint64 item IDs
+};
+
+// k_EMsgGCDelete
+struct MsgGCDelete_t
+{
+ uint64 m_unItemID;
+};
+
+// k_EMsgGCCraftResponse
+struct MsgGCStandardResponse_t
+{
+ int16 m_nResponseIndex;
+ uint32 m_eResponse;
+};
+
+// k_EMsgGCVerifyCacheSubscription
+struct MsgGCVerifyCacheSubscription_t
+{
+ uint64 m_ulSteamID;
+};
+
+// k_EMsgGCNameItem
+struct MsgGCNameItem_t
+{
+ uint64 m_unToolItemID; // the Nametag item
+ uint64 m_unSubjectItemID; // the item to be renamed
+ bool m_bDescription;
+ // Varchar: Item name
+};
+
+// k_EMsgGCNameBaseItem
+struct MsgGCNameBaseItem_t
+{
+ uint64 m_unToolItemID; // the Nametag item
+ uint32 m_unBaseItemDefinitionID; // the base item definition to be renamed
+ bool m_bDescription;
+ // Varchar: Item name
+};
+
+// k_EMsgGCUnlockCrate
+struct MsgGCUnlockCrate_t
+{
+ uint64 m_unToolItemID; // the crate key
+ uint64 m_unSubjectItemID; // the crate to be decoded
+};
+
+// k_EMsgGCPaintItem
+struct MsgGCPaintItem_t
+{
+ uint64 m_unToolItemID; // the Paint Can item
+ uint64 m_unSubjectItemID; // the item to be painted
+};
+
+// k_EMsgGCGiftWrapItem
+struct MsgGCGiftWrapItem_t
+{
+ uint64 m_unToolItemID; // the Gift Wrap item
+ uint64 m_unSubjectItemID; // the item to be wrapped
+};
+
+// k_EMsgGCDeliverGift
+struct MsgGCDeliverGift_t
+{
+ uint64 m_unGiftID;
+ uint64 m_ulGiverSteamID;
+ uint64 m_ulTargetSteamID;
+};
+
+// k_EMsgGCUnwrapGiftRequest
+struct MsgGCUnwrapGiftRequest_t
+{
+ uint64 m_unItemID;
+};
+
+// k_EMsgGCMOTDRequest
+struct MsgGCMOTDRequest_t
+{
+ RTime32 m_nLastMOTDRequest; // Time at which the client last asked for MOTDs. GC will send back all MOTDs posted since.
+ int16 m_eLanguage;
+};
+
+// k_EMsgGCMOTDRequestResponse
+struct MsgGCMOTDRequestResponse_t
+{
+ int16 m_nEntries;
+};
+
+// k_EMsgGCCustomizeItemTexture
+struct MsgGCCustomizeItemTexture_t
+{
+ uint64 m_unToolItemID; // the tool
+ uint64 m_unSubjectItemID; // the item wants the texture
+ uint64 m_unImageUGCHandle; // cloud ID of image file (UGCHandle_t)
+};
+
+// k_EMsgGCSetItemStyle
+struct MsgGCSetItemStyle_t
+{
+ uint64 m_unItemID;
+ uint8 m_iStyle;
+};
+
+// k_EMsgGCItemPreviewCheckStatus
+struct MsgGCCheckItemPreviewStatus_t
+{
+ uint32 m_unItemDefIndex;
+};
+
+// k_EMsgGCItemPreviewCheckStatusResponse
+struct MsgGCItemPreviewCheckStatusResponse_t
+{
+ uint32 m_unItemDefIndex;
+ uint32 m_eResponse;
+ RTime32 m_timePreviewTime;
+};
+
+// k_EMsgGCItemPreviewRequest
+struct MsgGCItemPreviewRequest_t
+{
+ uint32 m_unItemDefIndex;
+};
+
+// k_EMsgGCItemPreviewRequestResponse
+struct MsgGCItemPreviewRequestResponse_t
+{
+ uint32 m_unItemDefIndex;
+ uint32 m_eResponse;
+};
+
+// k_EMsgGCItemPreviewExpire
+struct MsgGCItemPreviewExpire_t
+{
+
+};
+
+// k_EMsgGCItemPreviewExpireNotification
+struct MsgGCItemPreviewExpireNotification_t
+{
+ uint32 m_unItemDefIndex;
+};
+
+//-----------------------------------------------------------------------------
+
+// k_EMsgGCUseItemResponse
+enum EGCMsgUseItemResponse
+{
+ k_EGCMsgUseItemResponse_ItemUsed = 0,
+ k_EGCMsgUseItemResponse_GiftNoOtherPlayers = 1,
+ k_EGCMsgUseItemResponse_ServerError = 2,
+ k_EGCMsgUseItemResponse_MiniGameAlreadyStarted = 3,
+ k_EGCMsgUseItemResponse_ItemUsed_ItemsGranted = 4,
+ k_EGCMsgUseItemResponse_CannotBeUsedByAccount = 5,
+ k_EGCMsgUseItemResponse_ForceSizeInt = 0x7FFFFFFF
+};
+
+// k_EMsgGCUseItemResponse
+struct MsgGCUseItemResponse_t
+{
+ uint32 m_eResponse;
+};
+
+// k_EMsgGCSpawnItem
+struct MsgGCSpawnItem_t
+{
+ uint64 m_ulInitiatorSteamID;
+ uint32 m_unItemDefinitionID;
+ // other data dynamically added:
+ // string of initiator name
+};
+
+// k_EMsgGCRespawnPostLoadoutChange
+struct MsgGCRespawnPostLoadoutChange_t
+{
+ uint64 m_ulInitiatorSteamID;
+};
+
+// k_EMsgGCRemoveItemName
+struct MsgGCRemoveItemName_t
+{
+ uint64 m_unItemID;
+ bool m_bDescription;
+};
+
+//-----------------------------------------------------------------------------
+// Trading
+
+// k_EMsgGCTrading_InitiateTradeRequest
+struct MsgGCTrading_InitiateTradeRequest_t
+{
+ uint32 m_unTradeRequestID;
+ uint64 m_ulOtherSteamID;
+ // @note player A's name as string when sent to party B
+};
+
+enum EGCMsgInitiateTradeResponse
+{
+ k_EGCMsgInitiateTradeResponse_Accepted = 0,
+ k_EGCMsgInitiateTradeResponse_Declined = 1,
+ k_EGCMsgInitiateTradeResponse_VAC_Banned_Initiator = 2,
+ k_EGCMsgInitiateTradeResponse_VAC_Banned_Target = 3,
+ k_EGCMsgInitiateTradeResponse_Target_Already_Trading = 4,
+ k_EGCMsgInitiateTradeResponse_Disabled = 5,
+ k_EGCMsgInitiateTradeResponse_NotLoggedIn = 6,
+ k_EGCMsgInitiateTradeResponse_Cancel = 7,
+ k_EGCMsgInitiateTradeResponse_TooSoon = 8,
+ k_EGCMsgInitiateTradeResponse_TooSoonPenalty = 9,
+ k_EGCMsgInitiateTradeResponse_Trade_Banned_Initiator = 10,
+ k_EGCMsgInitiateTradeResponse_Trade_Banned_Target = 11,
+ k_EGCMsgInitiateTradeResponse_Free_Account_Initiator_DEPRECATED = 12, // free accounts can initiate trades now
+ k_EGCMsgInitiateTradeResponse_Shared_Account_Initiator= 13,
+ k_EGCMsgInitiateTradeResponse_Service_Unavailable = 14,
+ k_EGCMsgInitiateTradeResponse_Target_Blocked = 15,
+ k_EGCMsgInitiateTradeResponse_NeedVerifiedEmail = 16,
+ k_EGCMsgInitiateTradeResponse_NeedSteamGuard = 17,
+ k_EGCMsgInitiateTradeResponse_SteamGuardDuration = 18,
+ k_EGCMsgInitiateTradeResponse_TheyCannotTrade = 19,
+ k_EGCMsgInitiateTradeResponse_Recent_Password_Reset = 20,
+ k_EGCMsgInitiateTradeResponse_Using_New_Device = 21,
+ k_EGCMsgInitiateTradeResponse_Sent_Invalid_Cookie = 22,
+
+ k_EGCMsgInitiateTradeResponse_Count,
+ k_EGCMsgInitiateTradeResponse_ForceSizeInt = 0x7FFFFFFF
+};
+
+// k_EMsgGCTrading_InitiateTradeResponse
+struct MsgGCTrading_InitiateTradeResponse_t
+{
+ uint32 m_eResponse;
+ uint32 m_unTradeRequestID;
+};
+
+// k_EMsgGCTrading_StartSession
+struct MsgGCTrading_StartSession_t
+{
+ uint32 m_unSessionVersion;
+ uint64 m_ulSteamIDPartyA;
+ uint64 m_ulSteamIDPartyB;
+ // @note strings from player names will be added to the message
+};
+
+// k_EMsgGCTrading_CancelSession
+struct MsgGCTrading_CancelSession_t
+{
+};
+
+// k_EMsgGCUsedClaimCodeItem
+struct MsgGCUsedClaimCodeItem_t
+{
+ // string of URL
+};
+
+//-----------------------------------------------------------------------------
+// ServerBrowser messages
+
+enum EGCMsgServerBrowser
+{
+ k_EGCMsgServerBrowser_FromServerBrowser = 0,
+ k_EGCMsgServerBrowser_FromAutoAskDialog = 1,
+};
+
+// k_EMsgGCServerBrowser_FavoriteServer
+// k_EMsgGCServerBrowser_BlacklistServer
+struct MsgGCServerBrowser_Server_t
+{
+ uint32 m_unIP;
+ int m_usPort;
+ uint8 m_ubSource; // 0=serverbrowser, 1=auto-ask dialog
+};
+
+//-----------------------------------------------------------------------------
+// Public facing loot lists.
+
+// k_EMsgGC_RevolvingLootList
+struct MsgGC_RevolvingLootList_t
+{
+ uint8 m_usListID; // Id of this list.
+ // Var Data:
+ // Serialized Lootlist KV
+};
+
+
+// k_EMsgGCLookupAccount
+struct MsgGCLookupAccount_t
+{
+ uint16 m_uiFindType;
+
+ // Var Data
+ // string containing Persona / URL / etc
+};
+
+// k_EMsgGCLookupAccountName
+struct MsgGCLookupAccountName_t
+{
+ uint32 m_unAccountID;
+};
+
+// k_EMsgGCLookupAccountNameResponse
+struct MsgGCLookupAccountNameResponse_t
+{
+ uint32 m_unAccountID;
+ // string containing persona name
+};
+
+#pragma pack( pop )
+
+#endif
diff --git a/game/shared/econ/econ_gcmessages.proto b/game/shared/econ/econ_gcmessages.proto
new file mode 100644
index 0000000..19cb487
--- /dev/null
+++ b/game/shared/econ/econ_gcmessages.proto
@@ -0,0 +1,644 @@
+//====== Copyright 1996-2010, Valve Corporation, All rights reserved. =======
+//
+// Purpose: The file defines our Google Protocol Buffers which are used in over
+// the wire messages between servers as well as between the TF GC and TF gameservers
+// and clients.
+//
+//=============================================================================
+
+// We care more about speed than code size
+option optimize_for = SPEED;
+
+// We don't use the service generation functionality
+option cc_generic_services = false;
+
+
+//
+// STYLE NOTES:
+//
+// Use CamelCase CMsgMyMessageName style names for messages.
+//
+// Use lowercase _ delimited names like my_steam_id for field names, this is non-standard for Steam,
+// but plays nice with the Google formatted code generation.
+//
+// Try not to use required fields ever. Only do so if you are really really sure you'll never want them removed.
+// Optional should be preffered as it will make versioning easier and cleaner in the future if someone refactors
+// your message and wants to remove or rename fields.
+//
+// Use fixed64 for JobId_t, GID_t, or SteamID. This is appropriate for any field that is normally
+// going to be larger than 2^56. Otherwise use int64 for 64 bit values that are frequently smaller
+// than 2^56 as it will safe space on the wire in those cases.
+//
+// Similar to fixed64, use fixed32 for RTime32 or other 32 bit values that are frequently larger than
+// 2^28. It will save space in those cases, otherwise use int32 which will safe space for smaller values.
+// An exception to this rule for RTime32 is if the value will frequently be zero rather than set to an actual
+// time.
+//
+
+import "steammessages.proto";
+
+enum EGCItemMsg
+{
+ k_EMsgGCBase = 1000;
+ k_EMsgGCSetSingleItemPosition = 1001; // uses old-school struct for a single item. Prefer k_EMsgGCSetItemPositions
+ k_EMsgGCCraft = 1002;
+ k_EMsgGCCraftResponse = 1003;
+ k_EMsgGCDelete = 1004;
+ k_EMsgGCVerifyCacheSubscription = 1005; // sent by gameservers who don't have a cache they expect
+ k_EMsgGCNameItem = 1006;
+ k_EMsgGCUnlockCrate = 1007; // used by decoder rings to unlock supply crates
+ k_EMsgGCUnlockCrateResponse = 1008;
+ k_EMsgGCPaintItem = 1009; // used by paint cans to paint items
+ k_EMsgGCPaintItemResponse = 1010;
+ k_EMsgGCGoldenWrenchBroadcast = 1011; // sent to all users when a Golden Wrench is crafted or deleted
+ k_EMsgGCMOTDRequest = 1012; // client is asking for a set of MOTDs
+ k_EMsgGCMOTDRequestResponse = 1013;
+
+// k_EMsgGCAddItemToSocket_DEPRECATED = 1014;
+// k_EMsgGCAddItemToSocketResponse_DEPRECATED = 1015;
+// k_EMsgGCAddSocketToBaseItem_DEPRECATED = 1016;
+// k_EMsgGCAddSocketToItem_DEPRECATED = 1017;
+// k_EMsgGCAddSocketToItemResponse_DEPRECATED = 1018;
+
+ k_EMsgGCNameBaseItem = 1019;
+ k_EMsgGCNameBaseItemResponse = 1020;
+
+ k_EMsgGCRemoveSocketItem_DEPRECATED = 1021;
+ k_EMsgGCRemoveSocketItemResponse_DEPRECATED = 1022;
+
+ k_EMsgGCCustomizeItemTexture = 1023;
+ k_EMsgGCCustomizeItemTextureResponse = 1024;
+ k_EMsgGCUseItemRequest = 1025; // client/game server => GC
+ k_EMsgGCUseItemResponse = 1026; // GC => client/game server
+
+// k_EMsgGCSpawnItem_DEPRECATED = 1028; // GC => game server
+ k_EMsgGCRespawnPostLoadoutChange = 1029; // client => GC => game server
+ k_EMsgGCRemoveItemName = 1030; // client => GC
+ k_EMsgGCRemoveItemPaint = 1031; // client => GC
+ k_EMsgGCGiftWrapItem = 1032; // client => GC (the player requests an item to be gift wrapped)
+ k_EMsgGCGiftWrapItemResponse = 1033; // GC => client (confirmation that an item was gift wrapped)
+ k_EMsgGCDeliverGift = 1034;
+ k_EMsgGCDeliverGiftResponseReceiver = 1036;
+ k_EMsgGCUnwrapGiftRequest = 1037;
+ k_EMsgGCUnwrapGiftResponse = 1038;
+ k_EMsgGCSetItemStyle = 1039;
+
+ k_EMsgGCUsedClaimCodeItem = 1040;
+ k_EMsgGCSortItems = 1041;
+
+ k_EMsgGC_RevolvingLootList_DEPRECATED= 1042; // GC => client; revolving loot list
+
+ k_EMsgGCLookupAccount = 1043; // client is requesting a lookup of an account
+ k_EMsgGCLookupAccountResponse = 1044;
+ k_EMsgGCLookupAccountName = 1045; // old-school struct for single account. client is requesting a lookup of an account name
+ k_EMsgGCLookupAccountNameResponse = 1046;
+
+ //k_EMsgGCStartupCheck = 1047; // GC => client
+ //k_EMsgGCStartupCheckResponse = 1048; // client => GC
+ k_EMsgGCUpdateItemSchema = 1049; // GC => client
+ k_EMsgGCRequestInventoryRefresh = 1050; // client => GC
+
+ k_EMsgGCRemoveCustomTexture = 1051; // client => GC
+ k_EMsgGCRemoveCustomTextureResponse = 1052; // GC => client
+ k_EMsgGCRemoveMakersMark = 1053; // client => GC
+ k_EMsgGCRemoveMakersMarkResponse = 1054; // GC => client
+ k_EMsgGCRemoveUniqueCraftIndex = 1055; // client => GC
+ k_EMsgGCRemoveUniqueCraftIndexResponse = 1056; // GC => client
+
+ k_EMsgGCSaxxyBroadcast = 1057; // sent to all users when a Saxxy is deleted
+
+ k_EMsgGCBackpackSortFinished = 1058; // GC => client
+ k_EMsgGCAdjustItemEquippedState = 1059; // GC => client
+// k_EMsgGCRequestItemSchemaData_DEPRECATED = 1060; // client => GC Should only be used in dev universe
+
+ k_EMsgGCCollectItem = 1061;
+
+ k_EMsgGCItemAcknowledged = 1062; // sent to a dedicated server when a client acknowledges an item
+
+ // item presets
+ k_EMsgGCPresets_SelectPresetForClass = 1063; // client => GC
+ k_EMsgGCPresets_SetItemPosition = 1064; // client => GC
+
+ // Abuse reporting
+ k_EMsgGC_ReportAbuse = 1065; // client => GC
+ k_EMsgGC_ReportAbuseResponse = 1066; // GC => client
+
+ // more item presets
+ k_EMsgGCPresets_SelectPresetForClassReply = 1067; // GC => client
+
+ // item naming broadcast
+ k_EMsgGCNameItemNotification = 1068; // GC => client
+
+// !FIXME! DOTAMERGE
+// these messages are particular to DOTA, or
+// conflict with corresponding TF messages
+//
+// k_EMsgGCGiftedItems = 1027; // GC => game server
+// k_EMsgGCDeliverGiftResponseGiver = 1035;
+//
+// k_EMsgGCApplyConsumableEffects = 1069;
+//
+// k_EMsgGCConsumableExhausted = 1070;
+// k_EMsgGCApplyStrangePart = 1073; // GC => client
+// k_EMsgGCShowItemsPickedUp = 1071;
+//
+// // generic broadcast
+// k_EMsgGCClientDisplayNotification = 1072; // GC => client
+//
+//// OBSOLETE k_EMsgGC_IncrementKillCountAttribute = 1074; // client => GC
+// k_EMsgGC_IncrementKillCountResponse = 1075; // GC => client
+// k_EMsgGCApplyPennantUpgrade = 1076; // GC => client
+//
+// k_EMsgGCSetItemPositions = 1077; // client => GC; protobuf batched item position update
+//
+// k_EMsgGCUnlockItemStyle = 1080;
+// k_EMsgGCUnlockItemStyleResponse = 1081;
+//
+// k_EMsgGCFulfillDynamicRecipeComponent = 1082;
+// k_EMsgGCFulfillDynamicRecipeComponentResponse = 1083;
+// k_EMsgGCApplyEggEssence = 1078;
+// k_EMsgGCNameEggEssenceResponse = 1079;
+//
+// k_EMsgGCClientRequestMarketData = 1084;
+// k_EMsgGCClientRequestMarketDataResponse = 1085;
+// k_EMsgGCExtractGems = 1086;
+// k_EMsgGCAddSocket = 1087; // client -> GC
+// k_EMsgGCAddItemToSocket = 1088; // client -> GC
+// k_EMsgGCAddItemToSocketResponse = 1089; // GC -> client
+// k_EMsgGCAddSocketResponse = 1090; // GC -> client
+//
+// k_EMsgGCResetStrangeGemCount = 1091; // client -> GC
+
+// << DOTA
+
+// TF >>
+
+ // generic broadcast
+ k_EMsgGCClientDisplayNotification = 1069; // GC => client
+
+ k_EMsgGCApplyStrangePart = 1070; // GC => client
+ k_EMsgGC_IncrementKillCountAttribute = 1071; // client => GC
+ k_EMsgGC_IncrementKillCountResponse = 1072; // GC => client
+ k_EMsgGCRemoveStrangePart = 1073; // GC => client
+ k_EMsgGCResetStrangeScores = 1074; // client => GC
+
+ k_EMsgGCGiftedItems = 1075; // GC => game server
+
+ k_EMsgGCApplyUpgradeCard = 1077; // client => GC
+ k_EMsgGCRemoveUpgradeCard = 1078; // client => GC
+
+ k_EMsgGCApplyStrangeRestriction = 1079; // client => GC
+
+ k_EMsgGCClientRequestMarketData = 1080; // client => GC
+ k_EMsgGCClientRequestMarketDataResponse = 1081; // GC => client
+
+ k_EMsgGCApplyXifier = 1082; // client => GC
+ k_EMsgGCApplyXifierResponse = 1083; // GC => Client
+
+ k_EMsgGC_TrackUniquePlayerPairEvent = 1084; // client => GC
+ k_EMsgGCFulfillDynamicRecipeComponent = 1085; // client => GC
+ k_EMsgGCFulfillDynamicRecipeComponentResponse = 1086; // GC => client
+
+ k_EMsgGCSetItemEffectVerticalOffset = 1087; // client => GC
+ k_EMsgGCSetHatEffectUseHeadOrigin = 1088; // client => GC
+
+ k_EMsgGCItemEaterRecharger = 1089; // client => GC
+ k_EMsgGCItemEaterRechargerResponse = 1090; // GC => Client
+
+ k_EMsgGCApplyBaseItemXifier = 1091; // client => GC
+
+ k_EMsgGCApplyClassTransmogrifier = 1092; // client => GC
+ k_EMsgGCApplyHalloweenSpellbookPage = 1093; // client => GC
+
+ k_EMsgGCRemoveKillStreak = 1094; // client => GC
+ k_EMsgGCRemoveKillStreakResponse = 1095; // GC => client
+
+ k_EMsgGCTFSpecificItemBroadcast = 1096; // GC => client (broadcast)
+ k_EMsgGC_IncrementKillCountAttribute_Multiple = 1097; // client (game server) => GC
+ k_EMsgGCDeliverGiftResponseGiver = 1098;
+
+ k_EMsgGCSetItemPositions = 1100; // client => GC; protobuf batched item position update
+
+// << TF
+
+ k_EMsgGCLookupMultipleAccountNames = 1101;
+ k_EMsgGCLookupMultipleAccountNamesResponse = 1102;
+
+ // trading!
+ k_EMsgGCTradingBase = 1500;
+ k_EMsgGCTrading_InitiateTradeRequest = 1501; // client A -> GC and then GC -> client B
+ k_EMsgGCTrading_InitiateTradeResponse = 1502; // client B -> GC or GC -> client A
+ k_EMsgGCTrading_StartSession = 1503; // GC -> client A & B
+// k_EMsgGCTrading_SetItem = 1504; // client -> GC
+// k_EMsgGCTrading_RemoveItem = 1505; // client -> GC
+// k_EMsgGCTrading_UpdateTradeInfo = 1506; // GC -> client A & B in response to SetItem or RemoveItem message
+// k_EMsgGCTrading_SetReadiness = 1507; // client -> GC
+// k_EMsgGCTrading_ReadinessResponse = 1508; // GC -> client A & B
+ k_EMsgGCTrading_SessionClosed = 1509; // GC -> client A & B
+ k_EMsgGCTrading_CancelSession = 1510; // client -> GC
+// k_EMsgGCTrading_TradeChatMsg = 1511; // client -> GC and then GC -> other client
+// k_EMsgGCTrading_ConfirmOffer = 1512; // client -> GC
+// k_EMsgGCTrading_TradeTypingChatMsg = 1513; // client -> GC and then GC -> other client
+ k_EMsgGCTrading_InitiateTradeRequestResponse = 1514; // GC -> client
+
+ // serverbrowser messages
+ k_EMsgGCServerBrowser_FavoriteServer = 1601;
+ k_EMsgGCServerBrowser_BlacklistServer = 1602;
+
+ // rentals & previews
+ k_EMsgGCServerRentalsBase = 1700;
+ k_EMsgGCItemPreviewCheckStatus = 1701;
+ k_EMsgGCItemPreviewStatusResponse = 1702;
+ k_EMsgGCItemPreviewRequest = 1703;
+ k_EMsgGCItemPreviewRequestResponse = 1704;
+ k_EMsgGCItemPreviewExpire = 1705;
+ k_EMsgGCItemPreviewExpireNotification = 1706;
+
+// !FIXME! DOTAMERGE
+// This message is 1707 in DOTA, but it came from TF, where it was 1707 at one time, then switched to 1708
+// when the message format changed.
+// k_EMsgGCItemPreviewItemBoughtNotification = 1707;
+ k_EMsgGCItemPreviewItemBoughtNotification = 1708;
+
+ // Development only messages
+ k_EMsgGCDev_NewItemRequest = 2001;
+ k_EMsgGCDev_NewItemRequestResponse = 2002;
+ k_EMsgGCDev_DebugRollLootRequest = 2003;
+
+ // Microtransaction messages
+ k_EMsgGCStoreGetUserData = 2500; // Gets the current price sheet from the GC
+ k_EMsgGCStoreGetUserDataResponse = 2501; // Response
+ k_EMsgGCStorePurchaseInit_DEPRECATED = 2502; // Initiate a purchase (old pre-protobuff format -- deprecated!)
+ k_EMsgGCStorePurchaseInitResponse_DEPRECATED = 2503; // Response
+
+// !FIXME! DOTAMERGE
+// These messages have different values in TF and DOTA.
+// k_EMsgGCStorePurchaseFinalize = 2504; // Finalize a purchase
+// k_EMsgGCStorePurchaseFinalizeResponse = 2505; // Response
+// k_EMsgGCStorePurchaseCancel = 2506; // Cancel a purchase
+// k_EMsgGCStorePurchaseCancelResponse = 2507; // Response
+ k_EMsgGCStorePurchaseFinalize = 2512; // Finalize a purchase
+ k_EMsgGCStorePurchaseFinalizeResponse = 2513; // Response
+ k_EMsgGCStorePurchaseCancel = 2514; // Cancel a purchase
+ k_EMsgGCStorePurchaseCancelResponse = 2515; // Response
+
+ k_EMsgGCStorePurchaseQueryTxn = 2508; // Query the status of a transaction
+ k_EMsgGCStorePurchaseQueryTxnResponse = 2509; // Response
+ k_EMsgGCStorePurchaseInit = 2510; // Initiate a purchase
+ k_EMsgGCStorePurchaseInitResponse = 2511; // Response
+
+// !FIXME! DOTAMERGE
+// Conflict with TF messages
+// k_EMsgGCBannedWordListRequest = 2512; // Request a list of new banned words
+// k_EMsgGCBannedWordListResponse = 2513; // response to a request, or a push of a new banned word update to clients
+// k_EMsgGCToGCBannedWordListBroadcast = 2514; // sent from GC to GC so the main GC can broadcast a banned word change to clients
+// k_EMsgGCToGCBannedWordListUpdated = 2515; // sent from GC to other GCs so that they can be kept in sync with banned word list updates
+
+ k_EMsgGCToGCDirtySDOCache = 2516; // when an SDO cache needs to be dirtied on another GC
+ k_EMsgGCToGCDirtyMultipleSDOCache = 2517; // when a list of SDO caches needs to be dirtied on another GC
+
+ k_EMsgGCToGCUpdateSQLKeyValue = 2518; // when a key value changes and needs to be updated on other GCs
+// k_EMsgGCToGCIsTrustedServer = 2519; // Is the specified server trusted?
+// k_EMsgGCToGCIsTrustedServerResponse = 2520; // response to whether or not this is a trusted server
+ k_EMsgGCToGCBroadcastConsoleCommand = 2521; // run a console command remotely on another GC from a GC
+
+ k_EMsgGCServerVersionUpdated = 2522; // Sent when the active version of a server changes so servers can restart
+
+ k_EMsgGCApplyAutograph = 2523; //
+ k_EMsgGCToGCWebAPIAccountChanged = 2524;
+ k_EMsgGCRequestAnnouncements = 2525; //
+ k_EMsgGCRequestAnnouncementsResponse = 2526; //
+ k_EMsgGCRequestPassportItemGrant = 2527;
+
+ k_EMsgGCClientVersionUpdated = 2528; // Sent when the client doesn't match the appropriate version
+
+ k_EMsgGCItemPurgatory_FinalizePurchase = 2531; // Sent for Korean government requirement - move a purchased item from the "maybe box" (referred to as item purgatory in code) to the backpack
+ k_EMsgGCItemPurgatory_FinalizePurchaseResponse = 2532;
+ k_EMsgGCItemPurgatory_RefundPurchase = 2533;
+ k_EMsgGCItemPurgatory_RefundPurchaseResponse = 2534;
+
+ k_EMsgGCToGCPlayerStrangeCountAdjustments = 2535;
+
+ k_EMsgGCRequestStoreSalesData = 2536; // get which items are currently on sale
+ k_EMsgGCRequestStoreSalesDataResponse = 2537;
+ k_EMsgGCRequestStoreSalesDataUpToDateResponse = 2538;
+
+ k_EMsgGCToGCPingRequest = 2539;
+ k_EMsgGCToGCPingResponse = 2540;
+
+ k_EMsgGCToGCGetUserSessionServer = 2541; // GC->GC, see what the steam ID is of the server that this user is on
+ k_EMsgGCToGCGetUserSessionServerResponse = 2542; // --response
+ k_EMsgGCToGCGetUserServerMembers = 2543; // GC->GC, what members are on the server and spectating
+ k_EMsgGCToGCGetUserServerMembersResponse = 2544; // --response
+
+ k_EMsgGCToGCGrantSelfMadeItemToAccount = 2555; // GC->GC, via SQL message queue, grant one specific self-made item to this contributor account ID
+ k_EMsgGCToGCThankedByNewUser = 2556; // GC->GC, via SQL message queue, this account was thanked by a new user account, so grant a thanked item or level up the current item
+
+ k_EMsgGCShuffleCrateContents = 2557; // game client->GC, shuffle the contents of the line item loot list for this crate
+
+ k_EMsgGCQuestObjective_Progress = 2558; // client/game -> GC, report progress in a quest objective
+ k_EMsgGCQuestCompleted = 2559; // GC -> client, report completion of a quest
+
+ k_EMsgGCApplyDuckToken = 2560; // client => GC
+
+ k_EMsgGCQuestComplete_Request = 2561; // client -> GC
+ k_EMsgGCQuestObjective_PointsChange = 2562; // server -> GC
+ k_EMsgGCQuestObjective_RequestLoanerItems = 2564; // client -> GC
+ k_EMsgGCQuestObjective_RequestLoanerResponse = 2565; // GC -> client
+
+ k_EMsgGCApplyStrangeCountTransfer = 2566; // client => GC
+ k_EMsgGCCraftCollectionUpgrade = 2567; // client => GC
+ k_EMsgGCCraftHalloweenOffering = 2568; // client => GC
+
+ k_EMsgGCQuestDiscard_Request = 2569; // client => GC
+
+ k_EMsgGCRemoveGiftedBy = 2570; // client => GC
+ k_EMsgGCRemoveGiftedByResponse = 2571; // GC => client
+
+ k_EMsgGCRemoveFestivizer = 2572; // client => GC
+ k_EMsgGCRemoveFestivizerResponse = 2573; // GC => client
+
+ k_EMsgGCCraftCommonStatClock = 2574; // client => GC
+
+ // Game specific messages start at 5000 and GCGameBase messages start at 3000
+ // So all these must be < 3000
+};
+
+enum EGCMsgResponse
+{
+ k_EGCMsgResponseOK = 0; // Request succeeded
+ k_EGCMsgResponseDenied = 1; // Request denied
+ k_EGCMsgResponseServerError = 2; // Request failed due to a temporary server error
+ k_EGCMsgResponseTimeout = 3; // Request timed out
+ k_EGCMsgResponseInvalid = 4; // Request was corrupt
+ k_EGCMsgResponseNoMatch = 5; // No item definition matched the request
+ k_EGCMsgResponseUnknownError = 6; // Request failed with an unknown error
+ k_EGCMsgResponseNotLoggedOn = 7; // Client not logged on to steam
+ k_EGCMsgFailedToCreate = 8; // Failed to create whatever object the GC was asked to create
+
+// k_EGCMsgResponseForceSizeInt = 0x7FFFFFFF
+};
+
+enum EUnlockStyle
+{
+ k_UnlockStyle_Succeeded = 0;
+ k_UnlockStyle_Failed_PreReq = 1;
+ k_UnlockStyle_Failed_CantAfford = 2;
+ k_UnlockStyle_Failed_CantCommit = 3;
+ k_UnlockStyle_Failed_CantLockCache = 4;
+ k_UnlockStyle_Failed_CantAffordAttrib = 5;
+ k_UnlockStyle_Failed_CantAffordGem = 6;
+};
+
+enum EItemPurgatoryResponse_Finalize
+{
+ k_ItemPurgatoryResponse_Finalize_Succeeded = 0;
+ k_ItemPurgatoryResponse_Finalize_Failed_Incomplete = 1; // Some but not all finalized
+ k_ItemPurgatoryResponse_Finalize_Failed_ItemsNotInPurgatory = 2; // Item ID's sent up were not in purgatory
+ k_ItemPurgatoryResponse_Finalize_Failed_CouldNotFindItems = 3; // One or more items do not belong to the given steam ID or were deleted, etc.
+ k_ItemPurgatoryResponse_Finalize_Failed_NoSOCache = 4; // Couldn't load the user's SO cache
+ k_ItemPurgatoryResponse_Finalize_BackpackFull = 5; // Backpack was full. We may have finalized some items.
+};
+
+enum EItemPurgatoryResponse_Refund
+{
+ k_ItemPurgatoryResponse_Refund_Succeeded = 0;
+ k_ItemPurgatoryResponse_Refund_Failed_ItemNotInPurgatory = 1; // Item ID's sent up were not in purgatory
+ k_ItemPurgatoryResponse_Refund_Failed_CouldNotFindItem = 2; // One or more items do not belong to the given steam ID or were deleted, etc.
+ k_ItemPurgatoryResponse_Refund_Failed_NoSOCache = 3; // Couldn't load the user's SO cache
+ k_ItemPurgatoryResponse_Refund_Failed_NoDetail = 4; // Generic error to avoid giving the client too much detail
+ k_ItemPurgatoryResponse_Refund_Failed_NexonWebAPI = 5; // The Nexon WebAPI failed
+};
+
+//
+// k_EMsgGCApplyAutograph
+//
+message CMsgApplyAutograph
+{
+ optional uint64 autograph_item_id = 1; // which autograph?
+ optional uint64 item_item_id = 2; // which item is getting this autograph
+};
+
+
+// k_EMsgGCToGCPlayerStrangeCountAdjustments
+// Used in the GC SQL Msg queue and in match signout
+message CMsgEconPlayerStrangeCountAdjustment
+{
+ message CStrangeCountAdjustment
+ {
+ optional uint32 event_type = 1;
+ optional uint64 item_id = 2;
+ optional uint32 adjustment = 3;
+ };
+
+ optional uint32 account_id = 1;
+ repeated CStrangeCountAdjustment strange_count_adjustments = 2;
+};
+
+
+//
+// k_EMsgGCItemPurgatory_FinalizePurchase
+//
+message CMsgRequestItemPurgatory_FinalizePurchase
+{
+ repeated uint64 item_ids = 1;
+};
+
+//
+// k_EMsgGCItemPurgatory_FinalizePurchaseResponse
+//
+message CMsgRequestItemPurgatory_FinalizePurchaseResponse
+{
+ optional uint32 result = 1;
+};
+
+//
+// k_EMsgGCItemPurgatory_RefundPurchase
+//
+message CMsgRequestItemPurgatory_RefundPurchase
+{
+ optional uint64 item_id = 1;
+};
+
+//
+// k_EMsgGCItemPurgatory_RefundPurchaseResponse
+//
+message CMsgRequestItemPurgatory_RefundPurchaseResponse
+{
+ optional uint32 result = 1;
+};
+
+message CMsgCraftingResponse
+{
+ repeated uint64 item_ids = 1;
+};
+
+// k_EMsgGCRequestStoreSalesData
+message CMsgGCRequestStoreSalesData
+{
+ optional uint32 version = 1; // the last received version (used to identify when sales data may have changed)
+ optional uint32 currency = 2; // which currency we want the sales values for
+};
+
+// k_EMsgGCRequestStoreSalesDataResponse
+message CMsgGCRequestStoreSalesDataResponse
+{
+ message Price
+ {
+ optional uint32 item_def = 1;
+ optional uint32 price = 2;
+ };
+ repeated Price sale_price = 1; // the list of items on sale and their sale price for the requested currency
+ optional uint32 version = 2; // the version of this data, future sale requests should provide this value so redundant requests can be ignored
+ optional uint32 expiration_time = 3; // the time after which this sale is likely to expire. It could expire sooner than this if the GC restarts
+};
+
+// k_EMsgGCRequestStoreSalesDataUpToDateResponse
+message CMsgGCRequestStoreSalesDataUpToDateResponse
+{
+ optional uint32 version = 1; // the last received version (used to identify when sales data may have changed)
+ optional uint32 expiration_time = 2; // the time after which this sale is likely to expire. It could expire sooner than this if the GC restarts
+};
+
+// k_EMsgGCToGCPingRequest
+message CMsgGCToGCPingRequest
+{
+};
+
+// k_EMsgGCToGCPingResponse
+message CMsgGCToGCPingResponse
+{
+};
+
+// k_EMsgGCToGCGetUserSessionServer
+message CMsgGCToGCGetUserSessionServer
+{
+ optional uint32 account_id = 1; // the user to lookup the server information for
+};
+
+// k_EMsgGCToGCGetUserSessionServerResponse
+message CMsgGCToGCGetUserSessionServerResponse
+{
+ optional fixed64 server_steam_id = 1; // zero if this user is not online or not on a server
+};
+
+// k_EMsgGCToGCGetUserServerMembers
+message CMsgGCToGCGetUserServerMembers
+{
+ optional uint32 account_id = 1; // the account ID to look up the server and spectators from
+ optional uint32 max_spectators = 2; // do you want spectators? If so what is the limit of how many will be returned (otherwise specify zero)
+};
+
+// k_EMsgGCToGCGetUserServerMembersResponse
+message CMsgGCToGCGetUserServerMembersResponse
+{
+ repeated uint32 member_account_id = 1; // the list of other server members or spectators
+};
+
+// k_EMsgGCLookupMultipleAccountNames
+message CMsgLookupMultipleAccountNames
+{
+ repeated uint32 accountids = 1 [ packed=true ];
+};
+
+// k_EMsgGCLookupMultipleAccountNamesResponse
+message CMsgLookupMultipleAccountNamesResponse
+{
+ message Account
+ {
+ optional uint32 accountid = 1;
+ optional string persona = 2;
+ }
+ repeated Account accounts = 1;
+};
+
+// k_EMsgGCToGCGrantSelfMadeItemToAccount
+message CMsgGCToGCGrantSelfMadeItemToAccount
+{
+ optional uint32 item_def_index = 1;
+ optional uint32 accountid = 2;
+}
+
+// k_EMsgGCToGCThankedByNewUser
+message CMsgGCToGCThankedByNewUser
+{
+ optional uint32 new_user_accountid = 1; // guy who did the thanking
+ optional uint32 thanked_user_accountid = 2; // guy who was thanked
+}
+
+// k_EMsgGCShuffleCrateContents
+message CMsgGCShuffleCrateContents
+{
+ optional uint64 crate_item_id = 1;
+ optional string user_code_string = 2;
+}
+
+// k_EMsgGCQuestObjective_Progress
+message CMsgGCQuestObjective_Progress
+{
+ optional uint64 quest_item_id = 1;
+ optional uint32 quest_attrib_index = 2;
+ optional uint32 delta = 3;
+ optional fixed64 owner_steamid = 4;
+}
+
+// CMsgGCQuestObjective_PointsChange
+message CMsgGCQuestObjective_PointsChange
+{
+ optional uint64 quest_item_id = 1;
+ optional uint32 standard_points = 2;
+ optional uint32 bonus_points = 3;
+ optional fixed64 owner_steamid = 4;
+ optional bool update_base_points = 5 [default = false];
+}
+
+// k_EMsgGCQuestComplete_Request
+message CMsgGCQuestComplete_Request
+{
+ optional uint64 quest_item_id = 1;
+}
+
+// k_EMsgGCQuestCompleted
+message CMsgGCQuestCompleted
+{
+}
+
+// k_EMsgGCQuestObjective_RequestLoanerItems
+message CMsgGCQuestObjective_RequestLoanerItems
+{
+ optional uint64 quest_item_id = 1;
+}
+
+// k_EMsgGCQuestObjective_RequestLoanerResponse
+message CMsgGCQuestObjective_RequestLoanerResponse
+{
+}
+
+// k_EMsgGCCraftCollectionUpgrader
+message CMsgCraftCollectionUpgrade
+{
+ repeated uint64 item_id = 1; // list of item ids
+};
+
+// k_EMsgGCCraftHalloweenOffering
+message CMsgCraftHalloweenOffering
+{
+ optional uint64 tool_id = 1; // tool that is invoking this call
+ repeated uint64 item_id = 2; // list of item ids
+};
+
+// k_EMsgGCCraftCommonStatClock
+message CMsgCraftCommonStatClock
+{
+ optional uint64 tool_id = 1; // tool that is invoking this call
+ repeated uint64 item_id = 2; // list of item ids
+};
+
+// k_EMsgGCQuestDiscard_Request
+message CMsgGCQuestDiscard_Request
+{
+ optional uint64 quest_item_id = 1;
+}
+
+// Do not remove this comment due to a bug on the Mac OS X protobuf compiler - lol
+
diff --git a/game/shared/econ/econ_holidays.cpp b/game/shared/econ/econ_holidays.cpp
new file mode 100644
index 0000000..afe1278
--- /dev/null
+++ b/game/shared/econ/econ_holidays.cpp
@@ -0,0 +1,415 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+
+#include "cbase.h"
+
+#include "rtime.h"
+#include "econ_holidays.h"
+
+//-----------------------------------------------------------------------------
+// Purpose: Interface that answers the simple question "on the passed-in time,
+// would this holiday be active?". Any caching of calculations is left
+// up to subclasses.
+//-----------------------------------------------------------------------------
+class IIsHolidayActive
+{
+public:
+ IIsHolidayActive( const char *pszHolidayName ) : m_pszHolidayName( pszHolidayName ) { }
+ virtual ~IIsHolidayActive ( ) { }
+ virtual bool IsActive( const CRTime& timeCurrent ) = 0;
+
+ const char *GetHolidayName() const { return m_pszHolidayName; }
+
+private:
+ const char *m_pszHolidayName;
+};
+
+//-----------------------------------------------------------------------------
+// Purpose: Always-disabled. Dummy event needed to map to slot zero for "disabled
+// holiday".
+//-----------------------------------------------------------------------------
+class CNoHoliday : public IIsHolidayActive
+{
+public:
+ CNoHoliday() : IIsHolidayActive( "none" ) { }
+
+ virtual bool IsActive( const CRTime& timeCurrent )
+ {
+ return false;
+ }
+};
+
+//-----------------------------------------------------------------------------
+// Purpose: A holiday that lasts exactly one and only one day.
+//-----------------------------------------------------------------------------
+class CSingleDayHoliday : public IIsHolidayActive
+{
+public:
+ CSingleDayHoliday( const char *pszName, int iMonth, int iDay )
+ : IIsHolidayActive( pszName )
+ , m_iMonth( iMonth )
+ , m_iDay( iDay )
+ {
+ //
+ }
+
+ virtual bool IsActive( const CRTime& timeCurrent )
+ {
+ return m_iMonth == timeCurrent.GetMonth()
+ && m_iDay == timeCurrent.GetDayOfMonth();
+ }
+
+private:
+ int m_iMonth;
+ int m_iDay;
+};
+
+//-----------------------------------------------------------------------------
+// Purpose: We want "week long" holidays to encompass at least two weekends,
+// so that players get plenty of time interacting with the holiday
+// features.
+//-----------------------------------------------------------------------------
+class CWeeksBasedHoliday : public IIsHolidayActive
+{
+public:
+ CWeeksBasedHoliday( const char *pszName, int iMonth, int iDay, int iExtraWeeks )
+ : IIsHolidayActive( pszName )
+ , m_iMonth( iMonth )
+ , m_iDay( iDay )
+ , m_iExtraWeeks( iExtraWeeks )
+ , m_iCachedCalculatedYear( 0 )
+ {
+ // We'll calculate the interval the first time we call IsActive().
+ }
+
+ void RecalculateTimeActiveInterval( int iYear )
+ {
+ // Get the date of the holiday.
+ tm holiday_tm = { };
+ holiday_tm.tm_mday = m_iDay;
+ holiday_tm.tm_mon = m_iMonth - 1;
+ holiday_tm.tm_year = iYear - 1900; // convert to years since 1900
+ mktime( &holiday_tm );
+
+ // The event starts on the first Friday at least four days prior to the holiday.
+ tm start_time_tm( holiday_tm );
+ start_time_tm.tm_mday -= 4; // Move back four days.
+ mktime( &start_time_tm );
+ int days_offset = start_time_tm.tm_wday - kFriday; // Find the nearest prior Friday.
+ if ( days_offset < 0 )
+ days_offset += 7;
+ start_time_tm.tm_mday -= days_offset;
+ time_t start_time = mktime( &start_time_tm );
+
+ // The event ends on the first Monday after the holiday, maybe plus some additional fudge
+ // time.
+ tm end_time_tm( holiday_tm );
+ days_offset = 7 - (end_time_tm.tm_wday - kMonday);
+ if ( days_offset >= 7 )
+ days_offset -= 7;
+ end_time_tm.tm_mday += days_offset + 7 * m_iExtraWeeks;
+ time_t end_time = mktime( &end_time_tm );
+
+#ifdef GC_DLL
+ char rgchDateStartBuf[ 128 ];
+ BGetLocalFormattedDate( start_time, rgchDateStartBuf, sizeof( rgchDateStartBuf) );
+
+ char rgchDateEndBuf[ 128 ];
+ BGetLocalFormattedDate( end_time, rgchDateEndBuf, sizeof( rgchDateEndBuf ) );
+
+ EmitInfo( GCSDK::SPEW_GC, 4, LOG_ALWAYS, "Holiday - '%s' event starts on '%s' and ends on '%s'.\n", GetHolidayName(), rgchDateStartBuf, rgchDateEndBuf );
+#endif // GC_DLL
+
+ m_timeStart = start_time;
+ m_timeEnd = end_time;
+
+ // We're done and our interval data is cached.
+ m_iCachedCalculatedYear = iYear;
+ }
+
+ virtual bool IsActive( const CRTime& timeCurrent )
+ {
+ const int iCurrentYear = timeCurrent.GetYear();
+ if ( m_iCachedCalculatedYear != iCurrentYear )
+ RecalculateTimeActiveInterval( iCurrentYear );
+
+ return timeCurrent.GetRTime32() > m_timeStart
+ && timeCurrent.GetRTime32() < m_timeEnd;
+ }
+
+private:
+ static const int kMonday = 1;
+ static const int kFriday = 5;
+
+ int m_iMonth;
+ int m_iDay;
+ int m_iExtraWeeks;
+
+ // Filled out from RecalculateTimeActiveInterval().
+ int m_iCachedCalculatedYear;
+
+ RTime32 m_timeStart;
+ RTime32 m_timeEnd;
+};
+
+//-----------------------------------------------------------------------------
+// Purpose: A holiday that repeats on a certain time interval, like "every N days"
+// or "once every two months" or, uh, "any time there's a full moon".
+//-----------------------------------------------------------------------------
+class CCyclicalHoliday : public IIsHolidayActive
+{
+public:
+ CCyclicalHoliday( const char *pszName, int iMonth, int iDay, int iYear, float fCycleLengthInDays, float fBonusTimeInDays )
+ : IIsHolidayActive( pszName )
+ , m_fCycleLengthInDays( fCycleLengthInDays )
+ , m_fBonusTimeInDays( fBonusTimeInDays )
+ {
+ // When is our initial interval?
+ tm holiday_tm = { };
+ holiday_tm.tm_mday = iDay;
+ holiday_tm.tm_mon = iMonth - 1;
+ holiday_tm.tm_year = iYear - 1900; // convert to years since 1900
+ m_timeInitial = mktime( &holiday_tm );
+ }
+
+ virtual bool IsActive( const CRTime& timeCurrent )
+ {
+ // Days-to-seconds conversion.
+ const int iSecondsPerDay = 24 * 60 * 60;
+
+ // Convert our cycle/buffer times to seconds.
+ const int iCycleLengthInSeconds = (int)(m_fCycleLengthInDays * iSecondsPerDay);
+ const int iBufferTimeInSeconds = (int)(m_fBonusTimeInDays * iSecondsPerDay);
+
+ // How long has it been since we started this cycle?
+ int iSecondsIntoCycle = (timeCurrent.GetRTime32() - m_timeInitial) % iCycleLengthInSeconds;
+
+ // If we're within the buffer period right after the start of a cycle, we're active.
+ if ( iSecondsIntoCycle < iBufferTimeInSeconds )
+ return true;
+
+ // If we're within the buffer period towards the end of a cycle, we're active.
+ if ( iSecondsIntoCycle > iCycleLengthInSeconds - iBufferTimeInSeconds )
+ return true;
+
+ // Alas, normal mode for us.
+ return false;
+ }
+
+private:
+ time_t m_timeInitial ;
+
+ float m_fCycleLengthInDays;
+ float m_fBonusTimeInDays;
+};
+
+//-----------------------------------------------------------------------------
+// Purpose: A pseudo-holiday that is active when either of its child holidays
+// is active. Works through pointers but does not manage memory.
+//-----------------------------------------------------------------------------
+class COrHoliday : public IIsHolidayActive
+{
+public:
+ COrHoliday( const char *pszName, IIsHolidayActive *pA, IIsHolidayActive *pB )
+ : IIsHolidayActive( pszName )
+ , m_pA( pA )
+ , m_pB( pB )
+ {
+ Assert( pA );
+ Assert( pB );
+ Assert( pA != pB );
+ }
+
+ virtual bool IsActive( const CRTime& timeCurrent )
+ {
+ return m_pA->IsActive( timeCurrent )
+ || m_pB->IsActive( timeCurrent );
+ }
+
+private:
+ IIsHolidayActive *m_pA;
+ IIsHolidayActive *m_pB;
+};
+
+//-----------------------------------------------------------------------------
+// Purpose: Holiday that is defined by a start and end date
+//-----------------------------------------------------------------------------
+class CDateBasedHoliday : public IIsHolidayActive
+{
+public:
+ CDateBasedHoliday( const char *pszName, const char *pszStartTime, const char *pszEndTime )
+ : IIsHolidayActive( pszName )
+ {
+ m_rtStartTime = CRTime::RTime32FromString( pszStartTime );
+ m_rtEndTime = CRTime::RTime32FromString( pszEndTime );
+ }
+
+ virtual bool IsActive( const CRTime& timeCurrent )
+ {
+ return ( ( timeCurrent >= m_rtStartTime ) && ( timeCurrent <= m_rtEndTime ) );
+ }
+
+ RTime32 GetEndRTime() const
+ {
+ return m_rtEndTime.GetRTime32();
+ }
+
+
+private:
+ CRTime m_rtStartTime;
+ CRTime m_rtEndTime;
+};
+
+//-----------------------------------------------------------------------------
+// Purpose: Holiday that is defined by a start and end date with no year specified
+//-----------------------------------------------------------------------------
+class CDateBasedHolidayNoSpecificYear : public IIsHolidayActive
+{
+public:
+ CDateBasedHolidayNoSpecificYear( const char *pszName, const char *pszStartTime, const char *pszEndTime )
+ : IIsHolidayActive( pszName )
+ , m_pszStartTime( pszStartTime )
+ , m_pszEndTime( pszEndTime )
+ , m_iCachedYear( -1 )
+ {
+ }
+
+ virtual bool IsActive( const CRTime& timeCurrent )
+ {
+ const int iYear = timeCurrent.GetYear();
+
+ if ( iYear != m_iCachedYear )
+ {
+ char m_szStartTime[k_RTimeRenderBufferSize];
+ char m_szEndTime[k_RTimeRenderBufferSize];
+
+ V_sprintf_safe( m_szStartTime, "%d-%s", iYear, m_pszStartTime );
+ V_sprintf_safe( m_szEndTime, "%d-%s", iYear, m_pszEndTime );
+
+ m_iCachedYear = iYear;
+ m_rtCachedStartTime = CRTime::RTime32FromString( m_szStartTime );
+ m_rtCachedEndTime = CRTime::RTime32FromString( m_szEndTime );
+ }
+
+ return ( ( timeCurrent >= m_rtCachedStartTime ) && ( timeCurrent <= m_rtCachedEndTime ) );
+ }
+
+private:
+ const char *m_pszStartTime;
+ const char *m_pszEndTime;
+
+ int m_iCachedYear;
+ CRTime m_rtCachedStartTime;
+ CRTime m_rtCachedEndTime;
+};
+
+//-----------------------------------------------------------------------------
+// Purpose: Actual holiday implementation objects.
+//-----------------------------------------------------------------------------
+
+static CNoHoliday g_Holiday_NoHoliday;
+
+static CDateBasedHolidayNoSpecificYear g_Holiday_TF2Birthday ( "birthday", "08-23", "08-25" );
+
+static CDateBasedHoliday g_Holiday_Halloween ( "halloween", "2016-10-19", "2016-11-18" );
+
+static CDateBasedHoliday g_Holiday_Christmas ( "christmas", "2016-11-28", "2017-01-12" );
+
+static CDateBasedHolidayNoSpecificYear g_Holiday_ValentinesDay ( "valentines", "02-13", "02-15" );
+
+static CDateBasedHoliday g_Holiday_MeetThePyro ( "meet_the_pyro", "2012-06-26", "2012-07-05" );
+ /* starting date cycle length in days bonus time in days on both sides */
+static CCyclicalHoliday g_Holiday_FullMoon ( "fullmoon", 5, 21, 2016, 29.53f, 1.0f );
+ // note: the cycle length is 29.5 instead of 29.53 so that the time calculations always start at noon based on the way CCyclicalHoliday works
+static COrHoliday g_Holiday_HalloweenOrFullMoon ( "halloween_or_fullmoon", &g_Holiday_Halloween, &g_Holiday_FullMoon );
+
+static COrHoliday g_Holiday_HalloweenOrFullMoonOrValentines ( "halloween_or_fullmoon_or_valentines", &g_Holiday_HalloweenOrFullMoon, &g_Holiday_ValentinesDay );
+
+static CDateBasedHolidayNoSpecificYear g_Holiday_AprilFools ( "april_fools", "03-31", "04-02" );
+
+static CDateBasedHoliday g_Holiday_EndOfTheLine ( "eotl_launch", "2014-12-03", "2015-01-05" );
+
+static CDateBasedHoliday g_Holiday_CommunityUpdate ( "community_update", "2015-09-01", "2015-11-05" );
+
+// ORDER NEEDS TO MATCH enum EHoliday
+static IIsHolidayActive *s_HolidayChecks[] =
+{
+ &g_Holiday_NoHoliday, // kHoliday_None
+ &g_Holiday_TF2Birthday, // kHoliday_TFBirthday
+ &g_Holiday_Halloween, // kHoliday_Halloween
+ &g_Holiday_Christmas, // kHoliday_Christmas
+ &g_Holiday_CommunityUpdate, // kHoliday_CommunityUpdate
+ &g_Holiday_EndOfTheLine, // kHoliday_EOTL
+ &g_Holiday_ValentinesDay, // kHoliday_Valentines
+ &g_Holiday_MeetThePyro, // kHoliday_MeetThePyro
+ &g_Holiday_FullMoon, // kHoliday_FullMoon
+ &g_Holiday_HalloweenOrFullMoon, // kHoliday_HalloweenOrFullMoon
+ &g_Holiday_HalloweenOrFullMoonOrValentines, // kHoliday_HalloweenOrFullMoonOrValentines
+ &g_Holiday_AprilFools, // kHoliday_AprilFools
+};
+
+COMPILE_TIME_ASSERT( ARRAYSIZE( s_HolidayChecks ) == kHolidayCount );
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool EconHolidays_IsHolidayActive( int iHolidayIndex, const CRTime& timeCurrent )
+{
+ if ( iHolidayIndex < 0 || iHolidayIndex >= kHolidayCount )
+ return false;
+
+ Assert( s_HolidayChecks[iHolidayIndex] );
+ if ( !s_HolidayChecks[iHolidayIndex] )
+ return false;
+
+ return s_HolidayChecks[iHolidayIndex]->IsActive( timeCurrent );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+int EconHolidays_GetHolidayForString( const char* pszHolidayName )
+{
+ for ( int iHoliday = 0; iHoliday < kHolidayCount; ++iHoliday )
+ {
+ Assert( s_HolidayChecks[iHoliday] );
+ if ( s_HolidayChecks[iHoliday] &&
+ 0 == Q_stricmp( pszHolidayName, s_HolidayChecks[iHoliday]->GetHolidayName() ) )
+ {
+ return iHoliday;
+ }
+ }
+
+ return kHoliday_None;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+const char *EconHolidays_GetActiveHolidayString()
+{
+ CRTime timeNow;
+ timeNow.SetToCurrentTime();
+ timeNow.SetToGMT( true );
+
+ for ( int iHoliday = 0; iHoliday < kHolidayCount; iHoliday++ )
+ {
+ if ( EconHolidays_IsHolidayActive( iHoliday, timeNow ) )
+ {
+ Assert( s_HolidayChecks[iHoliday] );
+ return s_HolidayChecks[iHoliday]->GetHolidayName();
+ }
+ }
+
+ // No holidays currently active.
+ return NULL;
+}
+
+#if defined(TF_CLIENT_DLL) || defined(TF_DLL) || defined(TF_GC_DLL)
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+RTime32 EconHolidays_TerribleHack_GetHalloweenEndData()
+{
+ return g_Holiday_Halloween.GetEndRTime();
+}
+#endif // defined(TF_CLIENT_DLL) || defined(TF_DLL) || defined(TF_GC_DLL)
diff --git a/game/shared/econ/econ_holidays.h b/game/shared/econ/econ_holidays.h
new file mode 100644
index 0000000..bba409f
--- /dev/null
+++ b/game/shared/econ/econ_holidays.h
@@ -0,0 +1,18 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+
+#ifndef ECON_HOLIDAYS_H
+#define ECON_HOLIDAYS_H
+
+#ifdef _WIN32
+#pragma once
+#endif
+
+bool EconHolidays_IsHolidayActive( int iHolidayIndex, const class CRTime& timeCurrent );
+int EconHolidays_GetHolidayForString( const char* pszHolidayName );
+const char *EconHolidays_GetActiveHolidayString();
+
+#if defined(TF_CLIENT_DLL) || defined(TF_DLL) || defined(TF_GC_DLL)
+RTime32 EconHolidays_TerribleHack_GetHalloweenEndData();
+#endif // defined(TF_CLIENT_DLL) || defined(TF_DLL) || defined(TF_GC_DLL)
+
+#endif // ECON_HOLIDAYS_H \ No newline at end of file
diff --git a/game/shared/econ/econ_item.cpp b/game/shared/econ/econ_item.cpp
new file mode 100644
index 0000000..a5ec004
--- /dev/null
+++ b/game/shared/econ/econ_item.cpp
@@ -0,0 +1,2765 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: CEconItem, a shared object for econ items
+//
+//=============================================================================
+
+#include "cbase.h"
+#include "econ_item.h"
+#include "econ_item_schema.h"
+#include "rtime.h"
+#include "gcsdk/enumutils.h"
+#include "smartptr.h"
+
+#ifdef GC_DLL
+#include "gcsdk/sqlaccess/sqlaccess.h"
+#include "econ/localization_provider.h"
+#endif
+
+#if defined( TF_CLIENT_DLL ) || defined( TF_DLL )
+#include "tf_gcmessages.h"
+#endif
+
+using namespace GCSDK;
+
+#ifdef GC_DLL
+IMPLEMENT_CLASS_MEMPOOL( CEconItem, 100 * 1000, UTLMEMORYPOOL_GROW_SLOW );
+IMPLEMENT_CLASS_MEMPOOL( CEconItemCustomData, 50 * 1000, UTLMEMORYPOOL_GROW_SLOW );
+#endif
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+extern int EconWear_ToIntCategory( float flWear );
+/*static*/ const schema_attribute_stat_bucket_t *CSchemaAttributeStats::m_pHead;
+
+//-----------------------------------------------------------------------------
+// Purpose: Utility function to convert datafile strings to ints.
+//-----------------------------------------------------------------------------
+int StringFieldToInt( const char *szValue, const char **pValueStrings, int iNumStrings, bool bDontAssert )
+{
+ if ( !szValue || !szValue[0] )
+ return -1;
+
+ for ( int i = 0; i < iNumStrings; i++ )
+ {
+ if ( !Q_stricmp(szValue, pValueStrings[i]) )
+ return i;
+ }
+
+ if ( !bDontAssert )
+ {
+ Assert( !"Missing value in StringFieldToInt()!" );
+ }
+ return -1;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Utility function to convert datafile strings to ints.
+//-----------------------------------------------------------------------------
+int StringFieldToInt( const char *szValue, const CUtlVector<const char *>& vecValueStrings, bool bDontAssert )
+{
+ return StringFieldToInt( szValue, (const char **)&vecValueStrings[0], vecValueStrings.Count(), bDontAssert );
+}
+
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+CEconItem::CEconItem()
+ : BaseClass( )
+ , m_pCustomData( NULL )
+ , m_ulID( INVALID_ITEM_ID )
+ , m_unStyle( 0 )
+ , m_pszSmallIcon( NULL )
+ , m_pszLargeIcon( NULL )
+{
+ Init();
+}
+
+CEconItem::CEconItem( const CEconItem& rhs )
+ : BaseClass( )
+ , m_pCustomData( NULL )
+ , m_ulID( INVALID_ITEM_ID )
+ , m_unStyle( 0 )
+ , m_pszSmallIcon( NULL )
+ , m_pszLargeIcon( NULL )
+{
+ Init();
+ (*this) = rhs;
+}
+
+void CEconItem::Init()
+{
+ memset( &m_dirtyBits, 0, sizeof( m_dirtyBits ) );
+
+#ifdef GC
+ // to set defaults
+ CSchItem item;
+ item.m_ulID = INVALID_ITEM_ID;
+ DeserializeFromSchemaItem( item );
+
+ COMPILE_TIME_ASSERT( sizeof( m_ulID ) == sizeof( item.m_ulID ) );
+ COMPILE_TIME_ASSERT( sizeof( m_unAccountID ) == sizeof( item.m_unAccountID ) );
+ COMPILE_TIME_ASSERT( sizeof( m_unDefIndex ) == sizeof( item.m_unDefIndex ) );
+ COMPILE_TIME_ASSERT( sizeof( m_unLevel ) == sizeof( item.m_unLevel ) );
+ COMPILE_TIME_ASSERT( sizeof( m_nQuality ) == sizeof( item.m_nQuality ) );
+ COMPILE_TIME_ASSERT( sizeof( m_unInventory ) == sizeof( item.m_unInventory ) );
+ COMPILE_TIME_ASSERT( sizeof( m_unFlags ) == sizeof( item.m_unFlags ) );
+ COMPILE_TIME_ASSERT( sizeof( m_unOrigin ) == sizeof( item.m_unOrigin ) );
+ COMPILE_TIME_ASSERT( sizeof( m_unStyle ) == sizeof( item.m_unStyle ) );
+ // @note (Tom Bui): we need to know about any new fields
+ // Need to add new fields to:
+ // CEconItem::operator=
+ // CEconItem::SerializeToSchemaItem
+ // CEconItem::DeserializeFromSchemaItem
+ // CEconItem::SerializeToProtoBufItem
+ // CEconItem::DeserializeFromProtoBufItem
+ // CEconManager::BYieldingLoadSOCache
+ // CEconGetPlayerItemsJob::BYieldingHandleGetPlayerItemsV001
+ COMPILE_TIME_ASSERT( CSchItem::k_iFieldMax == 13 );
+
+ m_bEquippedThisGameServerSession = false;
+#endif
+}
+
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+CEconItem::~CEconItem()
+{
+ // Free up any memory we may have allocated for our singleton attribute. Any other attributes
+ // will be cleaned up as part of freeing the custom data object itself.
+ if ( m_dirtyBits.m_bHasAttribSingleton )
+ {
+ CEconItemCustomData::FreeAttributeMemory( &m_CustomAttribSingleton );
+ }
+
+ // Free up any custom data we may have allocated. This will catch any attributes not
+ // in our singleton.
+ if ( m_pCustomData )
+ {
+ delete m_pCustomData;
+ }
+}
+
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+CEconItemCustomData::~CEconItemCustomData()
+{
+ FOR_EACH_VEC( m_vecAttributes, i )
+ {
+ FreeAttributeMemory( &m_vecAttributes[i] );
+ }
+
+ if ( m_pInteriorItem )
+ {
+ delete m_pInteriorItem;
+ }
+}
+
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+void CEconItem::CopyAttributesFrom( const CEconItem& source )
+{
+ // Copy attributes -- each new instance needs to be allocated and then copied into by somewhere
+ // that knows what the actual type is. Rather than do anything type-specific here, we just have each
+ // attribute serialize it's value to a bytestream and then deserialize it. This is as safe as we can
+ // make it but sort of silly wasteful.
+ for ( int i = 0; i < source.GetDynamicAttributeCountInternal(); i++ )
+ {
+ const attribute_t& attr = source.GetDynamicAttributeInternal( i );
+
+ const CEconItemAttributeDefinition *pAttrDef = GetItemSchema()->GetAttributeDefinition( attr.m_unDefinitionIndex );
+ Assert( pAttrDef );
+
+ const ISchemaAttributeType *pAttrType = pAttrDef->GetAttributeType();
+ Assert( pAttrType );
+
+ std::string sBytes;
+ pAttrType->ConvertEconAttributeValueToByteStream( attr.m_value, &sBytes );
+ pAttrType->LoadByteStreamToEconAttributeValue( this, pAttrDef, sBytes );
+ }
+}
+
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+CEconItem &CEconItem::operator=( const CEconItem& rhs )
+{
+ // We do destructive operations on our local object, including freeing attribute memory, as part of
+ // the copy, so we force self-copies to be a no-op.
+ if ( &rhs == this )
+ return *this;
+
+ m_ulID = rhs.m_ulID;
+ SetOriginalID( rhs.GetOriginalID() );
+ m_unAccountID = rhs.m_unAccountID;
+ m_unDefIndex = rhs.m_unDefIndex;
+ m_unLevel = rhs.m_unLevel;
+ m_nQuality = rhs.m_nQuality;
+ m_unInventory = rhs.m_unInventory;
+ SetQuantity( rhs.GetQuantity() );
+ m_unFlags = rhs.m_unFlags;
+ m_unOrigin = rhs.m_unOrigin;
+ m_unStyle = rhs.m_unStyle;
+ m_EquipInstanceSingleton = rhs.m_EquipInstanceSingleton;
+
+ // If we have memory allocated for a single attribute we free it manually.
+ if ( m_dirtyBits.m_bHasAttribSingleton )
+ {
+ CEconItemCustomData::FreeAttributeMemory( &m_CustomAttribSingleton );
+ }
+
+ // Copy over our dirty bits but manually reset our attribute singleton state -- if we did have one,
+ // we just deleted it above (and might replace it below); if we didn't have one, this won't affect
+ // anything. Either way, because we have no attribute memory allocated at this point, we need this
+ // to be reflected in the dirty bits so that if we do copy attributes, we copy them into the correct
+ // place (either the singleton or the custom data, to be allocated later).
+ m_dirtyBits = rhs.m_dirtyBits;
+ m_dirtyBits.m_bHasAttribSingleton = false;
+
+ // Free any custom memory we've allocated. This will also remove any custom attributes.
+ if ( rhs.m_pCustomData == NULL )
+ {
+ delete m_pCustomData;
+ m_pCustomData = NULL;
+ }
+ else
+ {
+ // Check for and copy in the equip instances from CustomData
+ EnsureCustomDataExists();
+ m_pCustomData->m_vecEquipped = rhs.m_pCustomData->m_vecEquipped;
+ }
+
+ CopyAttributesFrom( rhs );
+
+ // Reset our material overrides, they'll be set again on demand as needed.
+ ResetMaterialOverrides();
+
+ return *this;
+}
+
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+void CEconItem::SetItemID( itemid_t ulID )
+{
+ uint64 ulOldID = m_ulID;
+ m_ulID = ulID;
+ // only overwrite if we don't have an original id currently and we are a new item cloned off an old item
+ if ( ulOldID != INVALID_ITEM_ID && ulOldID != ulID && ( m_pCustomData == NULL || m_pCustomData->m_ulOriginalID == INVALID_ITEM_ID ) && ulID != INVALID_ITEM_ID && ulOldID != INVALID_ITEM_ID )
+ {
+ SetOriginalID( ulOldID );
+ }
+
+ ResetMaterialOverrides();
+}
+
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+itemid_t CEconItem::GetOriginalID() const
+{
+ if ( m_pCustomData != NULL && m_pCustomData->m_ulOriginalID != INVALID_ITEM_ID )
+ return m_pCustomData->m_ulOriginalID;
+ return m_ulID;
+}
+
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+void CEconItem::SetOriginalID( itemid_t ulOriginalID )
+{
+ if ( ulOriginalID != m_ulID )
+ {
+ EnsureCustomDataExists();
+ m_pCustomData->m_ulOriginalID = ulOriginalID;
+ }
+}
+
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+int CEconItem::GetQuantity() const
+{
+ if ( m_pCustomData != NULL )
+ return m_pCustomData->m_unQuantity;
+ return 1;
+}
+
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+void CEconItem::SetQuantity( uint16 unQuantity )
+{
+ if ( m_pCustomData )
+ {
+ m_pCustomData->m_unQuantity = unQuantity;
+ }
+ else if ( unQuantity > 1 )
+ {
+ EnsureCustomDataExists();
+ m_pCustomData->m_unQuantity = unQuantity;
+ }
+}
+
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+static const char *GetCustomNameOrAttributeDesc( const CEconItem *pItem, const CEconItemAttributeDefinition *pAttrDef )
+{
+ if ( !pAttrDef )
+ {
+ // If we didn't specify the attribute in the schema we can't possibly have an
+ // answer. This isn't really an error in that case.
+ return NULL;
+ }
+
+ const char *pszStrContents;
+ if ( FindAttribute_UnsafeBitwiseCast<CAttribute_String>( pItem, pAttrDef, &pszStrContents ) )
+ return pszStrContents;
+
+ return NULL;
+}
+
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+static void SetCustomNameOrDescAttribute( CEconItem *pItem, const CEconItemAttributeDefinition *pAttrDef, const char *pszNewValue )
+{
+ Assert( pItem );
+
+ if ( !pAttrDef )
+ {
+ // If we didn't specify the attribute in the schema, that's fine if we're setting
+ // the empty name/description string, but it isn't fine if we're trying to set
+ // actual content.
+ AssertMsg( !pszNewValue, "Attempt to set non-empty value for custom name/desc with no attribute present." );
+ return;
+ }
+
+ // Removing existing value?
+ if ( !pszNewValue || !pszNewValue[0] )
+ {
+ pItem->RemoveDynamicAttribute( pAttrDef );
+ return;
+ }
+
+ CAttribute_String attrStr;
+ attrStr.set_value( pszNewValue );
+
+ pItem->SetDynamicAttributeValue( pAttrDef, attrStr );
+}
+
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+const char *CEconItem::GetCustomName() const
+{
+ static CSchemaAttributeDefHandle pAttrDef_CustomName( "custom name attr" );
+
+ return GetCustomNameOrAttributeDesc( this, pAttrDef_CustomName );
+}
+
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+void CEconItem::SetCustomName( const char *pName )
+{
+ static CSchemaAttributeDefHandle pAttrDef_CustomName( "custom name attr" );
+
+ SetCustomNameOrDescAttribute( this, pAttrDef_CustomName, pName );
+}
+
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+bool CEconItem::IsEquipped() const
+{
+ for ( int i = 0; i < GetEquippedInstanceCount(); i++ )
+ {
+ const EquippedInstance_t &curEquipInstance = GetEquippedInstance( i );
+ Assert( curEquipInstance.m_unEquippedSlot != INVALID_EQUIPPED_SLOT );
+
+ if ( GetItemSchema()->IsValidClass( curEquipInstance.m_unEquippedClass ) )
+ return true;
+ }
+
+ return false;
+}
+
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+bool CEconItem::IsEquippedForClass( equipped_class_t unClass ) const
+{
+ return NULL != FindEquippedInstanceForClass( unClass );
+}
+
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+equipped_slot_t CEconItem::GetEquippedPositionForClass( equipped_class_t unClass ) const
+{
+ const EquippedInstance_t *pInstance = FindEquippedInstanceForClass( unClass );
+ if ( pInstance )
+ return pInstance->m_unEquippedSlot;
+
+ return INVALID_EQUIPPED_SLOT;
+}
+
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+const CEconItem::EquippedInstance_t *CEconItem::FindEquippedInstanceForClass( equipped_class_t nClass ) const
+{
+ for ( int i = 0; i < GetEquippedInstanceCount(); i++ )
+ {
+ const EquippedInstance_t &curEquipInstance = GetEquippedInstance( i );
+ if ( curEquipInstance.m_unEquippedClass == nClass )
+ return &curEquipInstance;
+ }
+
+ return NULL;
+}
+
+
+
+//----------------------------------------------------------------------------
+// Purpose:
+//----------------------------------------------------------------------------
+void CEconItem::InternalVerifyEquipInstanceIntegrity() const
+{
+ if ( m_dirtyBits.m_bHasEquipSingleton )
+ {
+ Assert( !m_pCustomData );
+ Assert( m_EquipInstanceSingleton.m_unEquippedSlot != INVALID_EQUIPPED_SLOT );
+ }
+ else if ( m_pCustomData )
+ {
+ FOR_EACH_VEC( m_pCustomData->m_vecEquipped, i )
+ {
+ Assert( m_pCustomData->m_vecEquipped[i].m_unEquippedSlot != INVALID_EQUIPPED_SLOT );
+
+ for ( int j = i + 1; j < m_pCustomData->m_vecEquipped.Count(); j++ )
+ {
+ Assert( m_pCustomData->m_vecEquipped[i].m_unEquippedClass != m_pCustomData->m_vecEquipped[j].m_unEquippedClass );
+ }
+ }
+ }
+ else
+ {
+ Assert( GetEquippedInstanceCount() == 0 );
+ }
+}
+
+//----------------------------------------------------------------------------
+// Purpose:
+//----------------------------------------------------------------------------
+void CEconItem::Equip( equipped_class_t unClass, equipped_slot_t unSlot )
+{
+ Assert( GetItemSchema()->IsValidClass( unClass ) );
+ Assert( GetItemSchema()->IsValidItemSlot( unSlot, unClass ) );
+
+ // First, make sure we don't have this item already equipped for this class.
+ UnequipFromClass( unClass );
+
+ // If we have no instances of this item equipped, we want to shove this into the
+ // first empty slot we can find. If we already have a custom data allocated, we
+ // use that. If not, we want to use the singleton if we can. Otherwise, we make
+ // a new custom data and fall back to using that.
+ if ( m_pCustomData )
+ {
+ m_pCustomData->m_vecEquipped.AddToTail( EquippedInstance_t( unClass, unSlot ) );
+ }
+ else if ( !m_dirtyBits.m_bHasEquipSingleton )
+ {
+ m_EquipInstanceSingleton = EquippedInstance_t( unClass, unSlot );
+ m_dirtyBits.m_bHasEquipSingleton = true;
+ }
+ else
+ {
+ EnsureCustomDataExists();
+ m_pCustomData->m_vecEquipped.AddToTail( EquippedInstance_t( unClass, unSlot ) );
+ }
+
+ InternalVerifyEquipInstanceIntegrity();
+
+#ifdef GC_DLL
+ m_bEquippedThisGameServerSession = true;
+#endif // GC_DLL
+
+}
+
+//----------------------------------------------------------------------------
+// Purpose:
+//----------------------------------------------------------------------------
+void CEconItem::Unequip()
+{
+ if ( m_dirtyBits.m_bHasEquipSingleton )
+ {
+ Assert( !m_pCustomData );
+ m_dirtyBits.m_bHasEquipSingleton = false;
+ }
+ else if ( m_pCustomData )
+ {
+ m_pCustomData->m_vecEquipped.Purge();
+ }
+
+ InternalVerifyEquipInstanceIntegrity();
+}
+
+//----------------------------------------------------------------------------
+// Purpose:
+//----------------------------------------------------------------------------
+void CEconItem::UnequipFromClass( equipped_class_t unClass )
+{
+ Assert( GetItemSchema()->IsValidClass( unClass ) );
+
+ // If we only have a single equipped class...
+ if ( m_dirtyBits.m_bHasEquipSingleton )
+ {
+ // ...and that's the class we're trying to remove from...
+ if ( m_EquipInstanceSingleton.m_unEquippedClass == unClass )
+ {
+ // ...we now have no equipped classes!
+ m_dirtyBits.m_bHasEquipSingleton = false;
+ }
+ }
+ else if ( m_pCustomData )
+ {
+ // ...otherwise, if we have multiple equipped classes...
+ FOR_EACH_VEC( m_pCustomData->m_vecEquipped, i )
+ {
+ // ...then look through our list to find out if we have this class...
+ if ( m_pCustomData->m_vecEquipped[i].m_unEquippedClass == unClass )
+ {
+ // ...and if we do, remove it.
+ m_pCustomData->m_vecEquipped.FastRemove( i );
+ break;
+ }
+ }
+ }
+
+ InternalVerifyEquipInstanceIntegrity();
+}
+
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+int CEconItem::GetEquippedInstanceCount() const
+{
+ if ( m_pCustomData )
+ return m_pCustomData->m_vecEquipped.Count();
+ else
+ return m_dirtyBits.m_bHasEquipSingleton ? 1 : 0;
+}
+
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+const CEconItem::EquippedInstance_t &CEconItem::GetEquippedInstance( int iIdx ) const
+{
+ Assert( iIdx >= 0 && iIdx < GetEquippedInstanceCount() );
+
+ if ( m_pCustomData )
+ return m_pCustomData->m_vecEquipped[iIdx];
+ else
+ return m_EquipInstanceSingleton;
+}
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+const char *CEconItem::GetCustomDesc() const
+{
+ static CSchemaAttributeDefHandle pAttrDef_CustomDesc( "custom desc attr" );
+
+ return GetCustomNameOrAttributeDesc( this, pAttrDef_CustomDesc );
+}
+
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+void CEconItem::SetCustomDesc( const char *pDesc )
+{
+ static CSchemaAttributeDefHandle pAttrDef_CustomDesc( "custom desc attr" );
+
+ SetCustomNameOrDescAttribute( this, pAttrDef_CustomDesc, pDesc );
+}
+
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+bool CEconItem::GetInUse() const
+{
+ return ( m_dirtyBits.m_bInUse ) != 0;
+}
+
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+void CEconItem::SetInUse( bool bInUse )
+{
+ if ( bInUse )
+ {
+ m_dirtyBits.m_bInUse = 1;
+ }
+ else
+ {
+ m_dirtyBits.m_bInUse = 0;
+ }
+}
+
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+const GameItemDefinition_t *CEconItem::GetItemDefinition() const
+{
+ const CEconItemDefinition *pRet = GetItemSchema()->GetItemDefinition( GetDefinitionIndex() );
+ const GameItemDefinition_t *pTypedRet = dynamic_cast<const GameItemDefinition_t *>( pRet );
+
+ AssertMsg( pRet == pTypedRet, "Item definition of inappropriate type." );
+
+ return pTypedRet;
+}
+
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+bool CEconItem::IsTradable() const
+{
+ return !m_dirtyBits.m_bInUse
+ && IEconItemInterface::IsTradable();
+}
+
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+void CEconItem::AdoptMoreRestrictedTradabilityFromItem( const CEconItem *pOther, uint32 nTradabilityFlagsToAccept /*= 0xFFFFFFFF*/ )
+{
+ if ( !pOther )
+ return;
+
+ int nOtherUntradability = pOther->GetUntradabilityFlags() & nTradabilityFlagsToAccept;
+ RTime32 otherUntradableTime = pOther->GetTradableAfterDateTime();
+ // Become untradable if the other item is untradable
+ AdoptMoreRestrictedTradability( nOtherUntradability, otherUntradableTime );
+}
+
+// --------------------------------------------------------------------------
+// Purpose: Given untradability flags and a untradable time, set this item's
+// untradability. This does not clear existing untradabilty.
+// --------------------------------------------------------------------------
+void CEconItem::AdoptMoreRestrictedTradability( uint32 nTradabilityFlags, RTime32 nUntradableTime )
+{
+ static CSchemaAttributeDefHandle pAttrib_CannotTrade( "cannot trade" );
+ static CSchemaAttributeDefHandle pAttrib_TradableAfter( "tradable after date" );
+
+ if ( !pAttrib_CannotTrade || !pAttrib_TradableAfter )
+ return;
+
+ // We're already permanently untradable. We can't get more untradable, so we're done.
+ if ( GetUntradabilityFlags() & k_Untradability_Permanent )
+ return;
+
+ if( nTradabilityFlags & k_Untradability_Permanent )
+ {
+ SetDynamicAttributeValue( pAttrib_CannotTrade, 0u );
+ }
+ else if ( nTradabilityFlags & k_Untradability_Temporary && nUntradableTime > GetTradableAfterDateTime() )
+ {
+ // Take the "tradable after date" if it's larger than ours
+ SetDynamicAttributeValue( pAttrib_TradableAfter, nUntradableTime );
+ }
+}
+
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+bool CEconItem::IsMarketable() const
+{
+ return !m_dirtyBits.m_bInUse
+ && IEconItemInterface::IsMarketable();
+}
+
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+bool CEconItem::IsCommodity() const
+{
+ return !m_dirtyBits.m_bInUse
+ && IEconItemInterface::IsCommodity();
+}
+
+void CEconItem::IterateAttributes( IEconItemAttributeIterator *pIterator ) const
+{
+ Assert( pIterator );
+
+ // custom attributes?
+ for ( int i = 0; i < GetDynamicAttributeCountInternal(); i++ )
+ {
+ const attribute_t &attrib = GetDynamicAttributeInternal( i );
+ const CEconItemAttributeDefinition *pAttrDef = GetItemSchema()->GetAttributeDefinition( attrib.m_unDefinitionIndex );
+ if ( !pAttrDef )
+ continue;
+
+ if ( !pAttrDef->GetAttributeType()->OnIterateAttributeValue( pIterator, pAttrDef, attrib.m_value ) )
+ return;
+ }
+
+ // in static attributes?
+ const CEconItemDefinition *pItemDef = GetItemDefinition();
+ if ( !pItemDef )
+ return;
+
+ pItemDef->IterateAttributes( pIterator );
+}
+
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+style_index_t CEconItem::GetStyle() const
+{
+ static CSchemaAttributeDefHandle pAttrDef_ItemStyleOverride( "item style override" );
+ float fStyleOverride = 0.f;
+ if ( FindAttribute_UnsafeBitwiseCast<attrib_value_t>( this, pAttrDef_ItemStyleOverride, &fStyleOverride ) )
+ {
+ return fStyleOverride;
+ }
+
+ static CSchemaAttributeDefHandle pAttrDef_ItemStyleStrange( "style changes on strange level" );
+ uint32 iMaxStyle = 0;
+ if ( pAttrDef_ItemStyleStrange && FindAttribute( pAttrDef_ItemStyleStrange, &iMaxStyle ) )
+ {
+ // Use the strange prefix if the weapon has one.
+ uint32 unScore = 0;
+ if ( !FindAttribute( GetKillEaterAttr_Score( 0 ), &unScore ) )
+ return 0;
+
+ // What type of event are we tracking and how does it describe itself?
+ uint32 unKillEaterEventType = 0;
+ // This will overwrite our default 0 value if we have a value set but leave it if not.
+ float fKillEaterEventType;
+ if ( FindAttribute_UnsafeBitwiseCast<attrib_value_t>( this, GetKillEaterAttr_Type( 0 ), &fKillEaterEventType ) )
+ {
+ unKillEaterEventType = fKillEaterEventType;
+ }
+
+ const char *pszLevelingDataName = GetItemSchema()->GetKillEaterScoreTypeLevelingDataName( unKillEaterEventType );
+ if ( !pszLevelingDataName )
+ {
+ pszLevelingDataName = KILL_EATER_RANK_LEVEL_BLOCK_NAME;
+ }
+
+ const CItemLevelingDefinition *pLevelDef = GetItemSchema()->GetItemLevelForScore( pszLevelingDataName, unScore );
+ if ( !pLevelDef )
+ return 0;
+
+ return Min( pLevelDef->GetLevel(), iMaxStyle );
+ }
+
+ return m_unStyle;
+}
+
+const char* CEconItem::FindIconURL( bool bLarge ) const
+{
+ const char* pszSize = bLarge ? "l" : "s";
+
+ static CSchemaAttributeDefHandle pAttrDef_IsFestivized( "is_festivized" );
+ bool bIsFestivized = pAttrDef_IsFestivized ? FindAttribute( pAttrDef_IsFestivized ) : false;
+
+ const CEconItemDefinition *pDef = GetItemDefinition();
+
+ // Go through and figure out all the different decorations on
+ // this item and construct the key to lookup the icon.
+ // NOTE: These are not currently composable, so they return out when
+ // a match is found. Once items are more composable, we'll want
+ // to keep adding all the components together to get the fully
+ // composed icon (ie. add the strange token, and the festive token, etc.)
+ const CEconItemPaintKitDefinition* pPaintKitDef = pDef->GetCustomPainkKitDefinition();
+ if ( pPaintKitDef )
+ {
+ float flWear = 0;
+ GetCustomPaintKitWear( flWear );
+ int iWearIndex = EconWear_ToIntCategory( flWear );
+ const char* pszFmtStr = bIsFestivized ? "%s%sw%df" : "%s%sw%d";
+
+ const char* pszValue = pDef->GetIconURL( CFmtStr( pszFmtStr, pszSize, pPaintKitDef->GetName(), iWearIndex ) );
+ if ( pszValue )
+ return pszValue;
+ }
+
+ const CEconStyleInfo *pStyle = pDef->GetStyleInfo( GetStyle() );
+ if ( pStyle )
+ {
+ const char* pszValue = pDef->GetIconURL( CFmtStr( "%ss%d", pszSize, GetStyle() ) );
+ if ( pszValue )
+ return pszValue;
+ }
+
+ if ( bIsFestivized )
+ {
+ const char* pszValue = pDef->GetIconURL( CFmtStr( "%sf", pszSize ) );
+ if ( pszValue )
+ return pszValue;
+ }
+
+ return pDef->GetIconURL( CFmtStr( "%s", pszSize ) );
+}
+
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+const char *CEconItem::GetIconURLSmall() const
+{
+ if ( m_pszSmallIcon == NULL )
+ {
+ m_pszSmallIcon = FindIconURL( false );
+ }
+
+ return m_pszSmallIcon;
+}
+
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+const char *CEconItem::GetIconURLLarge() const
+{
+ if ( m_pszLargeIcon == NULL )
+ {
+ m_pszLargeIcon = FindIconURL( true );
+ }
+
+ return m_pszLargeIcon;
+}
+
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+bool CEconItem::IsUsableInCrafting() const
+{
+ return !m_dirtyBits.m_bInUse
+ && IEconItemInterface::IsUsableInCrafting();
+}
+
+#ifdef GC_DLL
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+RTime32 CEconItem::GetAssetInfoExpirationCacheExpirationTime() const
+{
+ return GetTradableAfterDateTime();
+}
+#endif // GC_DLL
+
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+int CEconItem::GetDynamicAttributeCountInternal() const
+{
+ if ( m_pCustomData )
+ return m_pCustomData->m_vecAttributes.Count();
+ else
+ return m_dirtyBits.m_bHasAttribSingleton ? 1 : 0;
+}
+
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+CEconItem::attribute_t &CEconItem::GetMutableDynamicAttributeInternal( int iAttrIndexIntoArray )
+{
+ Assert( iAttrIndexIntoArray >= 0 );
+ Assert( iAttrIndexIntoArray < GetDynamicAttributeCountInternal() );
+
+ if ( m_pCustomData )
+ return m_pCustomData->m_vecAttributes[ iAttrIndexIntoArray ];
+ else
+ return m_CustomAttribSingleton;
+}
+
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+CEconItem::attribute_t *CEconItem::FindDynamicAttributeInternal( const CEconItemAttributeDefinition *pAttrDef )
+{
+ Assert( pAttrDef );
+
+ if ( m_pCustomData )
+ {
+ FOR_EACH_VEC( m_pCustomData->m_vecAttributes, i )
+ {
+ if ( m_pCustomData->m_vecAttributes[i].m_unDefinitionIndex == pAttrDef->GetDefinitionIndex() )
+ return &m_pCustomData->m_vecAttributes[i];
+ }
+ }
+ else if ( m_dirtyBits.m_bHasAttribSingleton )
+ {
+ if ( m_CustomAttribSingleton.m_unDefinitionIndex == pAttrDef->GetDefinitionIndex() )
+ return &m_CustomAttribSingleton;
+ }
+
+ return NULL;
+}
+
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+CEconItem::attribute_t &CEconItem::AddDynamicAttributeInternal()
+{
+ if ( 0 == GetDynamicAttributeCountInternal() && NULL == m_pCustomData )
+ {
+ m_dirtyBits.m_bHasAttribSingleton = true;
+ return m_CustomAttribSingleton;
+ }
+ else
+ {
+ EnsureCustomDataExists();
+ return m_pCustomData->m_vecAttributes[ m_pCustomData->m_vecAttributes.AddToTail() ];
+ }
+}
+
+// --------------------------------------------------------------------------
+void CEconItem::SetDynamicMaxTimeAttributeValue( const CEconItemAttributeDefinition *pAttrDef, RTime32 rtTime )
+{
+ RTime32 rtExistingTime = 0;
+ if ( FindAttribute( pAttrDef, &rtExistingTime ) )
+ {
+ //we have the attribute already, and see if the value exceeds what we are going to set
+ if ( rtExistingTime >= rtTime )
+ return;
+ }
+
+ //it doesn't so we need to update
+ SetDynamicAttributeValue( pAttrDef, rtTime );
+}
+
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+void CEconItem::SetTradableAfterDateTime( RTime32 rtTime )
+{
+ //don't bother if the time is in the past (this also covers the 0 case)
+ if( rtTime < CRTime::RTime32TimeCur() )
+ return;
+
+ //the attribute we are going to assign
+ static CSchemaAttributeDefHandle pAttrib_TradableAfter( "tradable after date" );
+ if( !pAttrib_TradableAfter )
+ return;
+
+ //see if we have a STATIC cannot trade attribute (ignore dynamic, because that could change and be used
+ // to short out the trade restriction).
+
+ //This is currently disabled so we can measure whether or not this is beneficial and if the savings justifies the corner case risk this exposes - JohnO 1/12/15
+ /*
+ const GameItemDefinition_t* pItemDef = GetItemDefinition();
+ if( pItemDef )
+ {
+ static CSchemaAttributeDefHandle pAttrib_CannotTrade( "cannot trade" );
+ uint32 unCannotTrade = 0;
+ if( ::FindAttribute( pItemDef, pAttrib_CannotTrade, &unCannotTrade ) )
+ {
+ return;
+ }
+ }
+ */
+
+ //now set it to the maximum time
+ SetDynamicMaxTimeAttributeValue( pAttrib_TradableAfter, rtTime );
+}
+
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+void CEconItem::RemoveDynamicAttribute( const CEconItemAttributeDefinition *pAttrDef )
+{
+ Assert( pAttrDef );
+ Assert( pAttrDef->GetDefinitionIndex() != INVALID_ATTRIB_DEF_INDEX );
+
+ if ( m_pCustomData )
+ {
+ for ( int i = 0; i < m_pCustomData->m_vecAttributes.Count(); i++ )
+ {
+ if ( m_pCustomData->m_vecAttributes[i].m_unDefinitionIndex == pAttrDef->GetDefinitionIndex() )
+ {
+ CEconItemCustomData::FreeAttributeMemory( &m_pCustomData->m_vecAttributes[i] );
+ m_pCustomData->m_vecAttributes.FastRemove( i );
+ return;
+ }
+ }
+ }
+ else if ( m_dirtyBits.m_bHasAttribSingleton )
+ {
+ if ( m_CustomAttribSingleton.m_unDefinitionIndex == pAttrDef->GetDefinitionIndex() )
+ {
+ CEconItemCustomData::FreeAttributeMemory( &m_CustomAttribSingleton );
+ m_dirtyBits.m_bHasAttribSingleton = false;
+ }
+ }
+}
+
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+/*static*/ void CEconItemCustomData::FreeAttributeMemory( CEconItem::attribute_t *pAttrib )
+{
+ Assert( pAttrib );
+
+ const CEconItemAttributeDefinition *pAttrDef = GetItemSchema()->GetAttributeDefinition( pAttrib->m_unDefinitionIndex );
+ Assert( pAttrDef );
+
+ const ISchemaAttributeType *pAttrType = pAttrDef->GetAttributeType();
+ Assert( pAttrType );
+
+ pAttrType->UnloadEconAttributeValue( &pAttrib->m_value );
+}
+
+// --------------------------------------------------------------------------
+// Purpose: Frees any unused memory in the internal structures
+// --------------------------------------------------------------------------
+void CEconItem::Compact()
+{
+ if ( m_pCustomData )
+ {
+ m_pCustomData->m_vecAttributes.Compact();
+ m_pCustomData->m_vecEquipped.Compact();
+ }
+}
+
+#ifdef GC_DLL
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+static bool BInsertEquippedInstanceSQL( const CEconItem *pItem, const CEconItem::EquippedInstance_t& equipInst, CSQLAccess& sqlAccess )
+{
+ Assert( pItem );
+
+ const char *pszUpdateOrInsert = "MERGE EquipInstance AS tblEquipInstance "
+ "USING (SELECT ? AS AccountID, ? AS ClassID, ? AS SlotID) AS tblEquipInstance_NewRow "
+ "ON (tblEquipInstance.AccountID = tblEquipInstance_NewRow.AccountID AND "
+ "tblEquipInstance.ClassID = tblEquipInstance_NewRow.ClassID AND "
+ "tblEquipInstance.SlotID = tblEquipInstance_NewRow.SlotID) "
+ "WHEN MATCHED THEN "
+ "UPDATE SET tblEquipInstance.ItemID = ? "
+ "WHEN NOT MATCHED BY TARGET THEN "
+ "INSERT (AccountID, ClassID, SlotID, ItemID) VALUES (?, ?, ?, ?);";
+
+ sqlAccess.AddBindParam( pItem->GetAccountID() ); // USING (SELECT ... AS AccountID)
+ sqlAccess.AddBindParam( equipInst.m_unEquippedClass ); // USING (SELECT ... AS ClassID)
+ sqlAccess.AddBindParam( equipInst.m_unEquippedSlot ); // USING (SELECT ... AS SlotID)
+ sqlAccess.AddBindParam( pItem->GetItemID() ); // UPDATE SET ...
+
+ sqlAccess.AddBindParam( pItem->GetAccountID() ); // INSERT (AccountID)
+ sqlAccess.AddBindParam( equipInst.m_unEquippedClass ); // INSERT (ClassID)
+ sqlAccess.AddBindParam( equipInst.m_unEquippedSlot ); // INSERT (SlotID)
+ sqlAccess.AddBindParam( pItem->GetItemID() ); // INSERT (ItemID)
+
+ return sqlAccess.BYieldingExecute( "BYieldingOnHandledPeriodicScoreTimePeriod", pszUpdateOrInsert );
+}
+
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+static bool BInsertAllEquippedInstancesSQL( const CEconItem *pItem, CSQLAccess& sqlAccess )
+{
+ Assert( pItem );
+
+ for ( int i = 0; i < pItem->GetEquippedInstanceCount(); i++ )
+ {
+ if ( !BInsertEquippedInstanceSQL( pItem, pItem->GetEquippedInstance( i ), sqlAccess ) )
+ return false;
+ }
+
+ return true;
+}
+
+// --------------------------------------------------------------------------
+// Purpose: Adds non-Item-table inserts to the SQL insert for this object
+// --------------------------------------------------------------------------
+bool CEconItem::BYieldingAddInsertToTransaction( CSQLAccess & sqlAccess )
+{
+ // @note Tom Bui: This could be simplified greatly, but let's see if it is an issue
+ CSchItem item;
+ SerializeToSchemaItem( item );
+ if( !CSchemaSharedObjectHelper::BYieldingAddInsertToTransaction( sqlAccess, &item ) )
+ return false;
+
+ // attributes get to written to any number of joined tables based on type
+ for ( int i = 0; i < GetDynamicAttributeCountInternal(); i++ )
+ {
+ const attribute_t &attrib = GetDynamicAttributeInternal( i );
+
+ const CEconItemAttributeDefinition *pAttrDef = GetItemSchema()->GetAttributeDefinition( attrib.m_unDefinitionIndex );
+ if ( !pAttrDef )
+ {
+ EmitError( SPEW_GC, "CEconItem::BYieldingAddInsertToTransaction(): attempt to insert unknown attribute ID %d.\n", attrib.m_unDefinitionIndex );
+ return false;
+ }
+
+ const ISchemaAttributeType *pAttrType = pAttrDef->GetAttributeType();
+ Assert( pAttrType );
+
+ CPlainAutoPtr<CRecordBase> pAttrRecord( pAttrType->CreateTypedSchRecord() );
+ pAttrType->ConvertEconAttributeValueToSch( GetItemID(), pAttrDef, attrib.m_value, pAttrRecord.Get() );
+ if( !sqlAccess.BYieldingInsertRecord( pAttrRecord.Get() ) )
+ return false;
+ }
+
+ // currently-equipped positions get written to a joined table
+ if ( !BInsertAllEquippedInstancesSQL( this, sqlAccess ) )
+ return false;
+
+ // add audit record for the create
+ CEconItemDefinition *pItemDef = GEconManager()->GetItemSchema()->GetItemDefinition( GetDefinitionIndex() );
+ if ( pItemDef )
+ {
+ const char* pDatabaseAuditTableName = pItemDef->GetDatabaseAuditTableName();
+ if ( pDatabaseAuditTableName )
+ {
+ CFmtStr1024 sStatement;
+ sStatement.sprintf( "INSERT INTO %s (ItemID) VALUES (%llu)", pDatabaseAuditTableName, m_ulID );
+ uint32 nRows;
+ bool bRet = sqlAccess.BYieldingExecute( sStatement, sStatement, &nRows );
+ if ( bRet == false )
+ {
+ CSteamID steamID( m_unAccountID, GGCHost()->GetUniverse(), k_EAccountTypeIndividual );
+ EmitError( SPEW_GC, "Failed to add item audit to table %s for %s and item %llu.\n", pDatabaseAuditTableName, steamID.Render(), m_ulID );
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+//----------------------------------------------------------------------------
+// Purpose:
+//----------------------------------------------------------------------------
+static bool BRemoveEquipInstancesFromSQL( CSQLAccess& sqlAccess, uint32 unAccountID, itemid_t unItemID )
+{
+ CSchEquipInstance schEquipInstanceWhere;
+ schEquipInstanceWhere.m_unAccountID = unAccountID;
+ schEquipInstanceWhere.m_ulItemID = unItemID;
+
+ return sqlAccess.BYieldingDeleteRecords( schEquipInstanceWhere, CSET_2_COL( CSchEquipInstance, k_iField_unAccountID, k_iField_ulItemID ) );
+}
+
+//----------------------------------------------------------------------------
+// Purpose: Writes the non-PK fields on the object to the database
+//----------------------------------------------------------------------------
+bool CEconItem::BYieldingAddWriteToTransaction( CSQLAccess & sqlAccess, const CUtlVector< int > &fields )
+{
+ Assert( sqlAccess.BInTransaction() );
+
+ if( IsForeign() )
+ {
+ CSchForeignItem schForeignItem;
+ schForeignItem.m_ulID = GetItemID();
+ schForeignItem.m_unInventory = GetInventoryToken();
+ return CSchemaSharedObjectHelper::BYieldingAddWriteToTransaction( sqlAccess, &schForeignItem, CSET_1_COL( CSchForeignItem, k_iField_unInventory ) );
+ }
+
+ // Non-foreign items.
+ CSchItem item;
+ SerializeToSchemaItem( item );
+ CColumnSet csDatabaseDirty( item.GetPRecordInfo() );
+ GetDirtyColumnSet( fields, csDatabaseDirty );
+
+ // Write out base item properties if any are dirty. It's possible to get here with only dirty attributes,
+ // in which case we want to neither write to our item properties nor abort here if the write fails.
+ if ( !csDatabaseDirty.IsEmpty() && !CSchemaSharedObjectHelper::BYieldingAddWriteToTransaction( sqlAccess, &item, csDatabaseDirty ) )
+ return false;
+
+ // Write out additional attribute properties?
+ FOR_EACH_VEC( fields, i )
+ {
+ uint32 iFieldID = fields[i] & 0x0000ffff, // low 16 bits are the ID
+ iFieldIDType = fields[i] & 0xffff0000; // high 16 bits are the ID type
+
+ if ( iFieldIDType == kUpdateFieldIDType_FieldID )
+ {
+ // Most item field IDs will be updated above in SerializeToSchemaItem(). Anything that needs custom
+ // writing behavior should handle it here.
+
+ // If we've changed our equip state, remove all of our previous equipped instances (if any) and write
+ // out our new rows (if any).
+ if ( iFieldID == CSOEconItem::kEquippedStateFieldNumber )
+ {
+ if ( !BRemoveEquipInstancesFromSQL( sqlAccess, GetAccountID(), GetID() ) )
+ return false;
+
+ if ( !BInsertAllEquippedInstancesSQL( this, sqlAccess ) )
+ return false;
+ }
+ }
+ else if ( iFieldIDType == kUpdateFieldIDType_AttributeID )
+ {
+ const CEconItemAttributeDefinition *pAttrDef = GetItemSchema()->GetAttributeDefinition( iFieldID );
+ Assert( pAttrDef );
+
+ // Did we dirty an attribute and then remove it entirely?
+ const attribute_t *pEconAttr = FindDynamicAttributeInternal( pAttrDef );
+ if ( !pEconAttr )
+ continue;
+
+ const ISchemaAttributeType *pAttrType = pAttrDef->GetAttributeType();
+ Assert( pAttrType );
+
+ CPlainAutoPtr<CRecordBase> pAttrRecord( pAttrType->CreateTypedSchRecord() );
+ pAttrType->ConvertEconAttributeValueToSch( GetItemID(), pAttrDef, pEconAttr->m_value, pAttrRecord.Get() );
+
+ CColumnSet csWhere( pAttrRecord->GetPRecordInfo() );
+ csWhere.BAddColumn( 0 );
+ csWhere.BAddColumn( 1 );
+
+ CColumnSet csUpdate( CColumnSet::Inverse( csWhere ) );
+ Assert( !csUpdate.IsEmpty() );
+
+ if ( !sqlAccess.BYieldingUpdateRecord( *pAttrRecord.Get(), csWhere, csUpdate ) )
+ return false;
+ }
+ // Our field ID is something we don't recognize.
+ else
+ {
+ AssertMsg( false, "Unknown field ID type in CEconItem::BYieldingAddWriteToTransaction()." );
+ }
+ }
+
+ return true;
+}
+
+
+// --------------------------------------------------------------------------
+// Purpose: "deletes" an object by setting its owner to 0.
+// --------------------------------------------------------------------------
+bool CEconItem::BYieldingAddRemoveToTransaction( CSQLAccess & sqlAccess )
+{
+ Assert( sqlAccess.BInTransaction() );
+
+ // we never remove econ items in the server
+ sqlAccess.AddBindParam( GetItemID() );
+ if( !sqlAccess.BYieldingExecute( NULL, "UPDATE Item SET AccountId = 0 WHERE ID = ?" ) )
+ return false;
+
+ if ( !BRemoveEquipInstancesFromSQL( sqlAccess, GetAccountID(), GetItemID() ) )
+ return false;
+
+ return true;
+}
+
+// --------------------------------------------------------------------------
+// Purpose: Load the interior item from the database.
+// --------------------------------------------------------------------------
+bool CEconItem::BYieldingLoadInteriorItem()
+{
+ static CSchemaAttributeDefHandle pAttribLow( "referenced item id low" );
+ static CSchemaAttributeDefHandle pAttribHigh( "referenced item id high" );
+
+ // see if it's already loaded
+ if ( m_pCustomData && m_pCustomData->m_pInteriorItem )
+ return true;
+
+ // we require the low 32 bits...
+ uint32 unAttribValueBitsLow;
+ if ( !FindAttribute( pAttribLow, &unAttribValueBitsLow ) )
+ return false;
+
+ // ...but default the high 32 bits to 0 if not present
+ uint32 unAttribValueBitsHigh = 0;
+ FindAttribute( pAttribHigh, &unAttribValueBitsHigh ); // return value ignored
+
+ COMPILE_TIME_ASSERT( sizeof( uint64 ) == sizeof( itemid_t ) );
+ uint64 wrappedItemId = ( uint64( unAttribValueBitsHigh ) << 32 ) | uint64( unAttribValueBitsLow );
+
+ // allocate scratch object to try and fill with DB contents
+ CEconItem *pNewItem = new CEconItem;
+ if ( !pNewItem->BYieldingSerializeFromDatabase( wrappedItemId ) )
+ {
+ delete pNewItem;
+ return false;
+ }
+
+ // DB load succeeded -- make sure if we're treating this item ID as an interior item, it isn't
+ // currently owned by someone. this is a non-fatal error (it could happen if support rolls back
+ // transactions in weird ways) but it probably means we have something weird going on
+ if ( pNewItem->GetAccountID() != 0 )
+ {
+ // ...
+ }
+
+ // everything loaded alright so hook our reference up
+ EnsureCustomDataExists();
+ m_pCustomData->m_pInteriorItem = pNewItem;
+ return true;
+}
+
+
+// --------------------------------------------------------------------------
+// Purpose: Directly set the interior item
+// --------------------------------------------------------------------------
+void CEconItem::SetInteriorItem( CEconItem* pInteriorItem )
+{
+ EnsureCustomDataExists();
+ m_pCustomData->m_pInteriorItem = pInteriorItem;
+}
+
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+CEconItem* CEconItem::YieldingGetInteriorItem()
+{
+ if ( !m_pCustomData || !m_pCustomData->m_pInteriorItem )
+ {
+ if ( !BYieldingLoadInteriorItem() )
+ {
+ Assert( !GetInteriorItem() );
+ }
+ }
+
+ return GetInteriorItem();
+}
+
+#endif // GC_DLL
+
+CEconItem* CEconItem::GetInteriorItem()
+{
+ return m_pCustomData ? m_pCustomData->m_pInteriorItem : NULL;
+}
+
+// --------------------------------------------------------------------------
+// Purpose: This item has been traded. Give it an opportunity to update any internal
+// properties in response.
+// --------------------------------------------------------------------------
+void CEconItem::OnTraded( uint32 unTradabilityDelaySeconds )
+{
+ // if Steam wants us to impose a tradability delay on the item
+ if ( unTradabilityDelaySeconds != 0 )
+ {
+ RTime32 rtTradableAfter = ( ( CRTime::RTime32TimeCur() / k_nSecondsPerDay ) * k_nSecondsPerDay ) + unTradabilityDelaySeconds;
+ SetTradableAfterDateTime( rtTradableAfter );
+ }
+ else
+ {
+ // If we have a "tradable after date" attribute and we were just traded, remove the date
+ // limit as we're obviously past it.
+ static CSchemaAttributeDefHandle pAttrib_TradableAfter( "tradable after date" );
+ RemoveDynamicAttribute( pAttrib_TradableAfter );
+ }
+
+ OnTransferredOwnership();
+}
+
+// --------------------------------------------------------------------------
+// Purpose: Ownership of this item has changed, so do whatever things are necessary
+// --------------------------------------------------------------------------
+void CEconItem::OnTransferredOwnership()
+{
+ // Reset all our strange scores.
+ for ( int i = 0; i < GetKillEaterAttrCount(); i++ )
+ {
+ const CEconItemAttributeDefinition *pAttrDef = GetKillEaterAttr_Score(i);
+
+ // Skip over any attributes our schema doesn't understand. We ideally wouldn't ever
+ // have this happen but if it does we don't want to ignore other valid attributes.
+ if ( !pAttrDef )
+ continue;
+
+ // Ignore any attributes we don't have on this item.
+ if ( !FindAttribute( pAttrDef ) )
+ continue;
+
+ // Zero out the value of this stat attribute.
+ SetDynamicAttributeValue( pAttrDef, 0u );
+ }
+
+ // Free accounts have the ability to trade any item out that they received in a trade.
+ SetFlag( kEconItemFlag_CanBeTradedByFreeAccounts );
+}
+
+// --------------------------------------------------------------------------
+// Purpose: This item has been traded. Give it an opportunity to update any internal
+// properties in response.
+// --------------------------------------------------------------------------
+void CEconItem::OnReceivedFromMarket( bool bFromRollback )
+{
+ OnTransferredOwnership();
+
+#ifdef GC_DLL
+ static CSchemaAttributeDefHandle pAttrib_TradableAfter( "tradable after date" );
+ if ( !bFromRollback && GEconManager()->GetTradableAfterDurationForPurchase() != 0 )
+ {
+ // Add "tradable after date" attribute for items received from the market, since the funds
+ // used to purchase the item may be untrusted
+ RTime32 rtTradableAfter = GEconManager()->GetTradableAfterDateForPurchase();
+ SetDynamicAttributeValue( pAttrib_TradableAfter, rtTradableAfter );
+ }
+ // Do we need to remove this attribute on rollback?
+ /*else
+ {
+ RemoveDynamicAttribute( pAttrib_TradableAfter );
+ }*/
+#endif // GC_DLL
+}
+
+// --------------------------------------------------------------------------
+// Purpose: Parses the bits required to create a econ item from the message.
+// Overloaded to include support for attributes.
+// --------------------------------------------------------------------------
+bool CEconItem::BParseFromMessage( const CUtlBuffer & buffer )
+{
+ CSOEconItem msgItem;
+ if( !msgItem.ParseFromArray( buffer.Base(), buffer.TellMaxPut() ) )
+ return false;
+
+ DeserializeFromProtoBufItem( msgItem );
+ return true;
+}
+
+// --------------------------------------------------------------------------
+// Purpose: Parses the bits required to create a econ item from the message.
+// Overloaded to include support for attributes.
+// --------------------------------------------------------------------------
+bool CEconItem::BParseFromMessage( const std::string &buffer )
+{
+ CSOEconItem msgItem;
+ if( !msgItem.ParseFromString( buffer ) )
+ return false;
+
+ DeserializeFromProtoBufItem( msgItem );
+ return true;
+}
+
+//----------------------------------------------------------------------------
+// Purpose: Overrides all the fields in msgLocal that are present in the
+// network message
+//----------------------------------------------------------------------------
+bool CEconItem::BUpdateFromNetwork( const CSharedObject & objUpdate )
+{
+ const CEconItem & econObjUpdate = (const CEconItem &)objUpdate;
+
+ *this = econObjUpdate;
+
+ return true;
+}
+
+#ifdef GC
+//----------------------------------------------------------------------------
+// Purpose: Adds the relevant bits to update this object to the message. This
+// must include any relevant information about which fields are being
+// updated. This is called once for all subscribers.
+//----------------------------------------------------------------------------
+bool CEconItem::BAddToMessage( CUtlBuffer & bufOutput ) const
+{
+ VPROF_BUDGET( "CEconItem::BAddToMessage::CUtlBuffer", VPROF_BUDGETGROUP_STEAM );
+ // StaticAssert( sizeof( int ) >= sizeof( uint32 ) );
+
+ CSOEconItem msg;
+ SerializeToProtoBufItem( msg );
+ return CProtoBufSharedObjectBase::SerializeToBuffer( msg, bufOutput );
+}
+
+//----------------------------------------------------------------------------
+// Purpose: Adds the relevant bits to update this object to the message. This
+// must include any relevant information about which fields are being
+// updated. This is called once for all subscribers.
+//----------------------------------------------------------------------------
+bool CEconItem::BAddToMessage( std::string *pBuffer ) const
+{
+ VPROF_BUDGET( "CEconItem::BAddToMessage::std::string", VPROF_BUDGETGROUP_STEAM );
+ // StaticAssert( sizeof( int ) >= sizeof( uint32 ) );
+
+ CSOEconItem msg;
+ SerializeToProtoBufItem( msg );
+ return msg.SerializeToString( pBuffer );
+}
+
+//----------------------------------------------------------------------------
+// Purpose: Adds just the item ID to the message so that the client can find
+// which item to destroy
+//----------------------------------------------------------------------------
+bool CEconItem::BAddDestroyToMessage( CUtlBuffer & bufDestroy ) const
+{
+ CSOEconItem msgItem;
+ msgItem.set_id( GetItemID() );
+ CProtoBufSharedObjectBase::SerializeToBuffer( msgItem, bufDestroy );
+ return true;
+}
+
+//----------------------------------------------------------------------------
+// Purpose: Adds just the item ID to the message so that the client can find
+// which item to destroy
+//----------------------------------------------------------------------------
+bool CEconItem::BAddDestroyToMessage( std::string *pBuffer ) const
+{
+ CSOEconItem msgItem;
+ msgItem.set_id( GetItemID() );
+ return msgItem.SerializeToString( pBuffer );
+}
+#endif //GC
+
+//----------------------------------------------------------------------------
+// Purpose: Returns true if this is less than than the object in soRHS. This
+// comparison is deterministic, but it may not be pleasing to a user
+// since it is just going to compare raw memory. If you need a sort
+// that is user-visible you will need to do it at a higher level that
+// actually knows what the data in these objects means.
+//----------------------------------------------------------------------------
+bool CEconItem::BIsKeyLess( const CSharedObject & soRHS ) const
+{
+ Assert( GetTypeID() == soRHS.GetTypeID() );
+ const CEconItem & soSchemaRHS = (const CEconItem &)soRHS;
+
+ return m_ulID < soSchemaRHS.m_ulID;
+}
+
+//----------------------------------------------------------------------------
+// Purpose: Copy the data from the specified schema shared object into this.
+// Both objects must be of the same type.
+//----------------------------------------------------------------------------
+void CEconItem::Copy( const CSharedObject & soRHS )
+{
+ *this = (const CEconItem &)soRHS;
+}
+
+//----------------------------------------------------------------------------
+// Purpose: Dumps diagnostic information about the shared object
+//----------------------------------------------------------------------------
+void CEconItem::Dump() const
+{
+ CSOEconItem msgItem;
+ SerializeToProtoBufItem( msgItem );
+ CProtoBufSharedObjectBase::Dump( msgItem );
+}
+
+
+//----------------------------------------------------------------------------
+// Purpose: Return short, identifying string about the object
+//----------------------------------------------------------------------------
+CUtlString CEconItem::GetDebugString() const
+{
+ CUtlString result;
+ result.Format( "[CEconItem: ID=%llu, DefIdx=%d]", GetItemID(), GetDefinitionIndex() );
+ return result;
+}
+
+
+#ifdef GC
+//-----------------------------------------------------------------------------
+// Purpose: Deserializes an item from a KV object
+// Input: pKVItem - Pointer to the KV structure that represents an item
+// schema - Econ item schema used for decoding human readable names
+// pVecErrors - Pointer to a vector where human readable errors will
+// be added
+// Output: True if the item deserialized successfully, false otherwise
+//-----------------------------------------------------------------------------
+bool CEconItem::BDeserializeFromKV( KeyValues *pKVItem, CUtlVector<CUtlString> *pVecErrors )
+{
+ Assert( pVecErrors );
+
+ Assert( NULL != pKVItem );
+ if ( NULL == pKVItem )
+ return false;
+
+ // The basic properties
+ SetItemID( pKVItem->GetUint64( "ID", INVALID_ITEM_ID ) );
+ SetInventoryToken( pKVItem->GetInt( "InventoryPos", GetUnacknowledgedPositionFor(UNACK_ITEM_DROPPED) ) ); // Start by assuming it's a drop
+ SetQuantity( pKVItem->GetInt( "Quantity", 1 ) );
+
+ // Look up the following properties based on names from the schema
+ const char *pchDefName = pKVItem->GetString( "DefName" );
+ const CEconItemDefinition *pItemDef = GetItemSchema()->GetItemDefinitionByName( pchDefName );
+ if( !pItemDef )
+ {
+ pVecErrors->AddToTail( CUtlString( CFmtStr( "Item definition \"%s\" not found", pchDefName ) ) );
+
+ // we can't do any reasonable validation with no item def, so just stop here
+ return false;
+ }
+
+ SetDefinitionIndex( pItemDef->GetDefinitionIndex() );
+
+ const char *pchQualityName = pKVItem->GetString( "QualityName" );
+ if( !pchQualityName || ! *pchQualityName )
+ {
+ // set the default quality for the definition
+ if( pItemDef->GetQuality() == k_unItemQuality_Any )
+ {
+ pVecErrors->AddToTail( CUtlString( CFmtStr( "Quality was not specified and this item def doesn't define one either." ) ) );
+ }
+ else
+ {
+ SetQuality( pItemDef->GetQuality() );
+ }
+ }
+ else if ( !GetItemSchema()->BGetItemQualityFromName( pchQualityName, &m_nQuality ) || k_unItemQuality_Any == GetQuality() )
+ {
+ pVecErrors->AddToTail( CUtlString( CFmtStr( "Quality \"%s\" not found", pchQualityName ) ) );
+ }
+
+ // make sure the level is sane
+ SetItemLevel( pKVItem->GetInt( "Level", pItemDef->GetMinLevel() ) );
+
+ // read the flags
+ uint8 unFlags = GetFlags();
+ if( pKVItem->GetInt( "flag_cannot_trade", 0 ) )
+ {
+ unFlags |= kEconItemFlag_CannotTrade;
+ }
+ else
+ {
+ unFlags = unFlags & ~kEconItemFlag_CannotTrade;
+ }
+ if( pKVItem->GetInt( "flag_cannot_craft", 0 ) )
+ {
+ unFlags |= kEconItemFlag_CannotBeUsedInCrafting;
+ }
+ else
+ {
+ unFlags = unFlags & ~kEconItemFlag_CannotBeUsedInCrafting;
+ }
+ if( pKVItem->GetInt( "flag_non_economy", 0 ) )
+ {
+ unFlags |= kEconItemFlag_NonEconomy;
+ }
+ else
+ {
+ unFlags = unFlags & ~kEconItemFlag_NonEconomy;
+ }
+ SetFlag( unFlags );
+
+ // Deserialize the attributes
+ KeyValues *pKVAttributes = pKVItem->FindKey( "Attributes" );
+ if ( NULL != pKVAttributes )
+ {
+ FOR_EACH_SUBKEY( pKVAttributes, pKVAttr )
+ {
+ // Try to load each line into an attribute in memory. It's possible that if we fail to successfully
+ // load some attribute contents here we'll leak small amounts of memory, but if that happens we're
+ // going to fail to start up anyway so we don't really care.
+ static_attrib_t staticAttrib;
+ if ( !staticAttrib.BInitFromKV_SingleLine( __FUNCTION__, pKVAttr, pVecErrors ) )
+ continue;
+
+ const CEconItemAttributeDefinition *pAttrDef = staticAttrib.GetAttributeDefinition();
+ Assert( pAttrDef );
+
+ const ISchemaAttributeType *pAttrType = pAttrDef->GetAttributeType();
+ Assert( pAttrType );
+
+ // Load the attribute contents into memory on the item.
+ pAttrType->LoadEconAttributeValue( this, pAttrDef, staticAttrib.m_value );
+
+ // Free up our temp loading memory.
+ pAttrType->UnloadEconAttributeValue( &staticAttrib.m_value );
+ }
+ }
+
+ return ( NULL == pVecErrors || 0 == pVecErrors->Count() );
+}
+
+
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+bool CEconItem::BYieldingSerializeFromDatabase( itemid_t ulItemID )
+{
+ static CColumnSet csItem( CSET_FULL( CSchItem ) );
+
+ enum
+ {
+ kQueryResultSet_SingleItem_Item = 0,
+ kQueryResultSet_SingleItem_Attributes_Base = 1,
+
+ kQueryResultSet_SingleItemCount
+ };
+
+ static uint32 sExpectedResults = 0;
+ static CFmtStrMax sLoadQuery;
+ if ( 0 == sLoadQuery.Length() )
+ {
+ sLoadQuery.Append(
+ "DECLARE @ItemID BIGINT "
+ "SET @ItemID = ? " );
+
+ // Load the item itself.
+ TSQLCmdStr sStatement;
+ BuildSelectStatementText( &sStatement, csItem );
+ sStatement.Append( " WHERE ID = @ItemID " );
+ sLoadQuery.Append( sStatement );
+ DbgVerify( kQueryResultSet_SingleItem_Item == sExpectedResults++ );
+
+ // Load any associated attributes.
+ DbgVerify( kQueryResultSet_SingleItem_Attributes_Base == sExpectedResults++ );
+ {
+ const CUtlVector<attr_type_t>& vecAttributeTypes = GetItemSchema()->GetAttributeTypes();
+
+ FOR_EACH_VEC( vecAttributeTypes, i )
+ {
+ // We could actually skip loading the "ItemID" column here because we're only loading one item
+ // of data, but we would have to do some work because we don't actually know the CSch types here
+ // so it doesn't seem worth it. We'll have to update the LoadSQLAttributesToEconItem() code if
+ // we ever do this.
+ BuildSelectStatementText( &sStatement, vecAttributeTypes[i].m_pAttrType->GetFullColumnSet() );
+ sStatement.Append( " WHERE ItemID = @ItemID " );
+ sLoadQuery.Append( sStatement );
+ }
+ }
+ }
+ Assert( kQueryResultSet_SingleItemCount == sExpectedResults );
+
+ // Load item and associated attributes from SQL.
+ CSQLAccess sqlAccess;
+ sqlAccess.AddBindParam( ulItemID );
+ if ( !sqlAccess.BYieldingExecute( "CEconItem::BYieldingSerializeFromDatabase", sLoadQuery.Get() ) )
+ {
+ EmitError( SPEW_GC, __FUNCTION__ ": failed to execute SQL load for item ID %llu\n", ulItemID );
+ return false;
+ }
+
+ // ...
+ if ( sqlAccess.GetResultSetCount() < sExpectedResults )
+ {
+ EmitError( SPEW_GC, __FUNCTION__ ": unable to interior item ID %llu. Got %d results back, but expected %d!\n", ulItemID, sqlAccess.GetResultSetCount(), sExpectedResults );
+ return false;
+ }
+
+ // Make sure we only got one item back.
+ const uint32 nRowCount = sqlAccess.GetResultSetRowCount( kQueryResultSet_SingleItem_Item );
+ if ( nRowCount != 1 )
+ {
+ EmitError( SPEW_GC, __FUNCTION__ ": invalid number of rows returned from SQL loading item ID %llu: %d\n", ulItemID, nRowCount );
+ return false;
+ }
+
+ // Make sure we understand what sort of item this is. We can't make any forward progress loading if we don't know
+ // what definition its attached to.
+ CSchItem schItem;
+ sqlAccess.GetResultRecord( kQueryResultSet_SingleItem_Item, 0 ).BWriteToRecord( &schItem, csItem );
+ const CEconItemDefinition *pItemDef = GetItemSchema()->GetItemDefinition( schItem.m_unDefIndex );
+ if ( !pItemDef )
+ {
+ EmitError( SPEW_GC, __FUNCTION__ ": unable to find item definition %d for item %llu!\n", schItem.m_unDefIndex, schItem.m_ulID );
+ return false;
+ }
+
+ // We have everything we need to complete the load here so default initialize -- free up attribute memory, current
+ // state, etc.
+ *this = CEconItem();
+
+ // Deserialize from DB schema.
+ DeserializeFromSchemaItem( schItem );
+
+ // Load attributes.
+ CEconManager::CEconItemAttributeLoader AttributeLoader;
+ AttributeLoader.LoadSQLAttributesToEconItem( this, sqlAccess, kQueryResultSet_SingleItem_Attributes_Base );
+
+ // Done.
+ Compact();
+
+ return true;
+}
+
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+void CEconItem::SerializeToSchemaItem( CSchItem &item ) const
+{
+ VPROF_BUDGET( "CEconItem::SerializeToSchemaItem()", VPROF_BUDGETGROUP_STEAM );
+
+ item.m_ulID = m_ulID;
+ item.m_ulOriginalID = GetOriginalID();
+ item.m_unAccountID = m_unAccountID;
+ item.m_unDefIndex = m_unDefIndex;
+ item.m_unLevel = m_unLevel;
+ item.m_nQuality = m_nQuality;
+ item.m_unInventory = m_unInventory;
+ item.m_unQuantity = GetQuantity();
+ item.m_unFlags = m_unFlags;
+ item.m_unOrigin = m_unOrigin;
+ item.m_unStyle = m_unStyle;
+}
+
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+void CEconItem::DeserializeFromSchemaItem( const CSchItem &item )
+{
+ VPROF_BUDGET( "CEconItem::DeserializeFromSchemaItem()", VPROF_BUDGETGROUP_STEAM );
+
+ m_ulID = item.m_ulID;
+ SetOriginalID( item.m_ulOriginalID ? item.m_ulOriginalID : item.m_ulID );
+ m_unAccountID = item.m_unAccountID;
+ m_unDefIndex = item.m_unDefIndex;
+ m_unLevel = item.m_unLevel;
+ m_nQuality = item.m_nQuality;
+ m_unInventory = item.m_unInventory;
+ SetQuantity( item.m_unQuantity );
+ m_unFlags = item.m_unFlags;
+ m_unOrigin = item.m_unOrigin;
+ m_unStyle = item.m_unStyle;
+
+ // set name if any, or remove if non-existent
+ SetCustomName( READ_VAR_CHAR_FIELD( item, m_VarCharCustomName ) );
+
+ // set desc if any, or remove if non-existent
+ SetCustomDesc( READ_VAR_CHAR_FIELD( item, m_VarCharCustomDesc ) );
+}
+#endif // GC
+
+
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+void CEconItem::SerializeToProtoBufItem( CSOEconItem &msgItem ) const
+{
+ VPROF_BUDGET( "CEconItem::SerializeToProtoBufItem()", VPROF_BUDGETGROUP_STEAM );
+
+ msgItem.set_id( m_ulID );
+ if( m_ulID != GetOriginalID() )
+ msgItem.set_original_id( GetOriginalID() );
+ msgItem.set_account_id( m_unAccountID );
+ msgItem.set_def_index( m_unDefIndex );
+ msgItem.set_level( m_unLevel );
+ msgItem.set_quality( m_nQuality );
+ msgItem.set_inventory( m_unInventory );
+ msgItem.set_quantity( GetQuantity() );
+ msgItem.set_flags( m_unFlags );
+ msgItem.set_origin( m_unOrigin );
+ msgItem.set_style( m_unStyle );
+ msgItem.set_in_use( m_dirtyBits.m_bInUse );
+
+ for( int nAttr = 0; nAttr < GetDynamicAttributeCountInternal(); nAttr++ )
+ {
+ const attribute_t & attr = GetDynamicAttributeInternal( nAttr );
+
+ // skip over attributes we don't understand
+ const CEconItemAttributeDefinition *pAttrDef = GetItemSchema()->GetAttributeDefinition( attr.m_unDefinitionIndex );
+ if ( !pAttrDef )
+ continue;
+
+ const ISchemaAttributeType *pAttrType = pAttrDef->GetAttributeType();
+ Assert( pAttrType );
+
+ CSOEconItemAttribute *pMsgAttr = msgItem.add_attribute();
+ pMsgAttr->set_def_index( attr.m_unDefinitionIndex );
+
+ std::string sBytes;
+ pAttrType->ConvertEconAttributeValueToByteStream( attr.m_value, &sBytes );
+ pMsgAttr->set_value_bytes( sBytes );
+ }
+
+ msgItem.set_contains_equipped_state_v2( true );
+ for ( int i = 0; i < GetEquippedInstanceCount(); i++ )
+ {
+ const EquippedInstance_t &instance = GetEquippedInstance( i );
+ CSOEconItemEquipped *pMsgEquipped = msgItem.add_equipped_state();
+ pMsgEquipped->set_new_class( instance.m_unEquippedClass );
+ pMsgEquipped->set_new_slot( instance.m_unEquippedSlot );
+ }
+
+ if ( m_pCustomData )
+ {
+ const char *pszCustomName = GetCustomName();
+ if ( pszCustomName )
+ {
+ msgItem.set_custom_name( pszCustomName );
+ }
+
+ const char *pszCustomDesc = GetCustomDesc();
+ if ( pszCustomDesc )
+ {
+ msgItem.set_custom_desc( pszCustomDesc );
+ }
+
+ const CEconItem *pInteriorItem = GetInteriorItem();
+ if ( pInteriorItem )
+ {
+ CSOEconItem *pMsgInteriorItem = msgItem.mutable_interior_item();
+ pInteriorItem->SerializeToProtoBufItem( *pMsgInteriorItem );
+ }
+ }
+}
+
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+void CEconItem::DeserializeFromProtoBufItem( const CSOEconItem &msgItem )
+{
+ VPROF_BUDGET( "CEconItem::DeserializeFromProtoBufItem()", VPROF_BUDGETGROUP_STEAM );
+
+ // Start by resetting
+ SAFE_DELETE( m_pCustomData );
+ m_dirtyBits.m_bHasAttribSingleton = false;
+ m_dirtyBits.m_bHasEquipSingleton = false;
+
+ // Now copy from the message
+ m_ulID = msgItem.id();
+ SetOriginalID( msgItem.has_original_id() ? msgItem.original_id() : m_ulID );
+ m_unAccountID = msgItem.account_id();
+ m_unDefIndex = msgItem.def_index();
+ m_unLevel = msgItem.level();
+ m_nQuality = msgItem.quality();
+ m_unInventory = msgItem.inventory();
+ SetQuantity( msgItem.quantity() );
+ m_unFlags = msgItem.flags();
+ m_unOrigin = msgItem.origin();
+ m_unStyle = msgItem.style();
+
+ m_dirtyBits.m_bInUse = msgItem.in_use() ? 1 : 0;
+
+ // set name if any
+ if( msgItem.has_custom_name() )
+ {
+ SetCustomName( msgItem.custom_name().c_str() );
+ }
+
+ // set desc if any
+ if( msgItem.has_custom_desc() )
+ {
+ SetCustomDesc( msgItem.custom_desc().c_str() );
+ }
+
+ // read the attributes
+ for( int nAttr = 0; nAttr < msgItem.attribute_size(); nAttr++ )
+ {
+ // skip over old-format messages
+ const CSOEconItemAttribute& msgAttr = msgItem.attribute( nAttr );
+ if ( msgAttr.has_value() || !msgAttr.has_value_bytes() )
+ continue;
+
+ // skip over attributes we don't understand
+ const CEconItemAttributeDefinition *pAttrDef = GetItemSchema()->GetAttributeDefinition( msgAttr.def_index() );
+ if ( !pAttrDef )
+ continue;
+
+ const ISchemaAttributeType *pAttrType = pAttrDef->GetAttributeType();
+ Assert( pAttrType );
+
+ pAttrType->LoadByteStreamToEconAttributeValue( this, pAttrDef, msgAttr.value_bytes() );
+ }
+
+ // Check to see if the item has an interior object.
+ if ( msgItem.has_interior_item() )
+ {
+ EnsureCustomDataExists();
+
+ m_pCustomData->m_pInteriorItem = new CEconItem();
+ m_pCustomData->m_pInteriorItem->DeserializeFromProtoBufItem( msgItem.interior_item() );
+ }
+
+ // update equipped state
+ if ( msgItem.has_contains_equipped_state_v2() && msgItem.contains_equipped_state_v2() )
+ {
+ // unequip from everything...
+ Unequip();
+
+ // ...and re-equip to whatever our current state is
+ for ( int i = 0; i < msgItem.equipped_state_size(); i++ )
+ {
+ Equip( msgItem.equipped_state(i).new_class(), msgItem.equipped_state(i).new_slot() );
+ }
+ }
+}
+
+#ifdef GC
+#include "econ/localization_provider.h"
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+void CEconItem::GetDirtyColumnSet( const CUtlVector< int > &fields, CColumnSet &cs ) const
+{
+ cs.MakeEmpty();
+ /* @note Tom Bui: We know only the inventory/position field and the quantity field can be dirty...
+ for ( int nField = 0; nField < CSchItem::k_iFieldMax; ++nField )
+ {
+ if ( bDatabase ? BIsFieldDatabaseDirty( nField ) : BIsFieldNetworkDirty( nField ) )
+ {
+ cs.BAddColumn( nField );
+ }
+ }
+ */
+ if( fields.HasElement( CSOEconItem::kInventoryFieldNumber ) )
+ cs.BAddColumn( CSchItem::k_iField_unInventory );
+ if( fields.HasElement( CSOEconItem::kQuantityFieldNumber ) )
+ cs.BAddColumn( CSchItem::k_iField_unQuantity );
+ if( fields.HasElement( CSOEconItem::kStyleFieldNumber ) )
+ cs.BAddColumn( CSchItem::k_iField_unStyle );
+}
+
+
+// --------------------------------------------------------------------------
+// Purpose: Exports the item for use in another app
+// --------------------------------------------------------------------------
+void CEconItem::ExportToAPI( CWebAPIValues *pValues ) const
+{
+ pValues->CreateChildObject( "id" )->SetUInt64Value( m_ulID );
+ pValues->CreateChildObject( "def_index" )->SetUInt32Value( m_unDefIndex );
+ pValues->CreateChildObject( "level" )->SetUInt32Value( m_unLevel );
+ pValues->CreateChildObject( "quality" )->SetInt32Value( m_nQuality );
+
+ if( GetCustomName() )
+ pValues->CreateChildObject( "custom_name" )->SetStringValue( GetCustomName() );
+ if( GetCustomDesc() )
+ pValues->CreateChildObject( "custom_desc" )->SetStringValue( GetCustomDesc() );
+}
+
+
+// --------------------------------------------------------------------------
+// Purpose: Imports a view of the item from another app
+// --------------------------------------------------------------------------
+bool CEconItem::BImportFromAPI( CWebAPIValues *pValues )
+{
+ m_unLevel = pValues->GetChildUInt32Value( "level", 1 );
+ m_nQuality = pValues->GetChildUInt32Value( "quality", GetItemSchema()->GetDefaultQuality() );
+
+ CUtlString sValue;
+ pValues->GetChildStringValue( sValue, "custom_name", "" );
+ CGCLocalizationProvider::BEnsureCleanUTF8Truncation( sValue.GetForModify() );
+ if( !sValue.IsEmpty() )
+ SetCustomName( sValue.Get() );
+ pValues->GetChildStringValue( sValue, "custom_desc", "" );
+ CGCLocalizationProvider::BEnsureCleanUTF8Truncation( sValue.GetForModify() );
+ if( !sValue.IsEmpty() )
+ SetCustomDesc( sValue.Get() );
+
+ return true;
+}
+
+#endif
+
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+void CEconItem::EnsureCustomDataExists()
+{
+ if ( m_pCustomData == NULL )
+ {
+ m_pCustomData = new CEconItemCustomData();
+
+ if ( m_dirtyBits.m_bHasEquipSingleton )
+ {
+ m_pCustomData->m_vecEquipped.AddToTail( m_EquipInstanceSingleton );
+ m_EquipInstanceSingleton = EquippedInstance_t();
+ m_dirtyBits.m_bHasEquipSingleton = false;
+ }
+ if ( m_dirtyBits.m_bHasAttribSingleton )
+ {
+ m_pCustomData->m_vecAttributes.AddToTail( m_CustomAttribSingleton );
+ m_dirtyBits.m_bHasAttribSingleton = false;
+ }
+ }
+}
+
+#ifdef GC_DLL
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+static bool BYieldingAddAuditRecordImpl( GCSDK::CSQLAccess *sqlAccess, uint64 ulItemID, uint32 unOwnerID, EItemAction eAction, uint32 unData )
+{
+ CEconUserSession *pUserSession = GGCGameBase()->FindEconUserSession( CSteamID( unOwnerID, GGCHost()->GetUniverse(), k_EAccountTypeIndividual ) );
+ CGCGSSession *pGSSession = pUserSession && pUserSession->GetSteamIDGS().IsValid() ? GGCEcon()->FindGSSession( pUserSession->GetSteamIDGS() ) : NULL;
+
+ // Prepare the audit record
+ CSchItemAudit schItemAudit;
+ schItemAudit.m_ulItemID = ulItemID;
+ schItemAudit.m_RTime32Stamp = CRTime::RTime32TimeCur();
+ schItemAudit.m_eAction = eAction;
+ schItemAudit.m_unOwnerID = unOwnerID;
+ schItemAudit.m_unData = unData;
+ schItemAudit.m_unServerIP = pGSSession ? pGSSession->GetAddr() : 0;
+ schItemAudit.m_usServerPort = pGSSession ? pGSSession->GetPort() : 0;
+
+ return sqlAccess->BYieldingInsertRecord( &schItemAudit );
+}
+
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+bool CEconItem::CAuditEntry::BAddAuditEntryToTransaction( CSQLAccess& sqlAccess, const CEconItem *pItem ) const
+{
+ return BYieldingAddAuditRecordImpl( &sqlAccess, pItem->GetID(), pItem->GetAccountID(), m_eAction, m_unData );
+}
+
+// --------------------------------------------------------------------------
+// Purpose: Adds an audit record to the DB for an item
+// --------------------------------------------------------------------------
+void YieldingAddAuditRecord( GCSDK::CSQLAccess *sqlAccess, CEconItem *pItem, uint32 unOwnerID, EItemAction eAction, uint32 unData )
+{
+ YieldingAddAuditRecord( sqlAccess, pItem->GetItemID(), unOwnerID, eAction, unData );
+}
+
+// --------------------------------------------------------------------------
+// Purpose: Adds an audit record to the DB for an item
+// --------------------------------------------------------------------------
+void YieldingAddAuditRecord( GCSDK::CSQLAccess *sqlAccess, uint64 ulItemID, uint32 unOwnerID, EItemAction eAction, uint32 unData )
+{
+ Verify( BYieldingAddAuditRecordImpl( sqlAccess, ulItemID, unOwnerID, eAction, unData ) );
+}
+
+
+// --------------------------------------------------------------------------
+// Purpose: Adds an item to the DB
+// --------------------------------------------------------------------------
+bool YieldingAddItemToDatabase( CEconItem *pItem, const CSteamID & steamID, EItemAction eAction, uint32 unData )
+{
+ CSQLAccess sqlAccess;
+ sqlAccess.BBeginTransaction( "YieldingAddItemToDatabase" );
+
+ pItem->SetAccountID( steamID.GetAccountID() );
+ if( !pItem->BYieldingAddInsertToTransaction( sqlAccess ) )
+ {
+ sqlAccess.RollbackTransaction();
+ return false;
+ }
+
+ YieldingAddAuditRecord( &sqlAccess, pItem, steamID.GetAccountID(), eAction, unData );
+
+ // commit the item and audit record
+ if( !sqlAccess.BCommitTransaction() )
+ {
+ EmitError( SPEW_GC, "Failed to add item for %s\n", steamID.Render() );
+ return false;
+ }
+
+ return true;
+}
+
+//----------------------------------------------------------------------------
+// Purpose:
+//----------------------------------------------------------------------------
+static CEconItem *FindEquippedItemForClassAndSlot( CEconSharedObjectCache *pSOCache, equipped_class_t unClass, equipped_slot_t unSlot )
+{
+ VPROF_BUDGET( "FindEquippedItemForClassAndSlot()", VPROF_BUDGETGROUP_STEAM );
+ Assert( pSOCache );
+
+ CGCSharedObjectTypeCache *pItemTypeCache = pSOCache->FindTypeCache( k_EEconTypeItem );
+
+ const uint32 unCount = pItemTypeCache ? pItemTypeCache->GetCount() : 0;
+ for ( uint32 i = 0; i < unCount; i++ )
+ {
+ CEconItem *pItem = static_cast<CEconItem *>( pItemTypeCache->GetObject( i ) );
+ Assert( pItem );
+
+ for ( int j = 0; j < pItem->GetEquippedInstanceCount(); j++ )
+ {
+ const CEconItem::EquippedInstance_t& equipInst = pItem->GetEquippedInstance( j );
+
+ if ( equipInst.m_unEquippedClass == unClass && equipInst.m_unEquippedSlot == unSlot )
+ return pItem;
+ }
+ }
+
+ return NULL;
+}
+
+//----------------------------------------------------------------------------
+// Purpose:
+//----------------------------------------------------------------------------
+/*static*/ void CEconItemEquipInstanceHelpers::AssignItemToSlot( CEconSharedObjectCache *pSOCache, CEconItem *pItem, equipped_class_t unClass, equipped_slot_t unSlot, CEconUserSession *pOptionalSession /* = NULL */ )
+{
+ VPROF_BUDGET( "CEconEquipInstance::AssignItemToSlot()", VPROF_BUDGETGROUP_STEAM );
+
+ Assert( pSOCache );
+ Assert( GetItemSchema()->IsValidClass( unClass ) );
+ Assert( GetItemSchema()->IsValidItemSlot( unSlot, unClass ) );
+
+ CEconItem *pPreviousEquippedItem = FindEquippedItemForClassAndSlot( pSOCache, unClass, unSlot );
+
+ // skip out on expensive work if we're going to equip the item that's already in that slot
+ if ( pItem == pPreviousEquippedItem )
+ return;
+
+ // do we have an item to unequip first
+ if ( pPreviousEquippedItem )
+ {
+ pPreviousEquippedItem->UnequipFromClass( unClass );
+ pSOCache->DirtyObjectField( pPreviousEquippedItem, CSOEconItem::kEquippedStateFieldNumber );
+ GGCBase()->AddCacheToWritebackQueue( pSOCache );
+
+ const bool bPreviousItemIsNowEquipped = pPreviousEquippedItem->IsEquipped();
+
+ if ( pOptionalSession && !bPreviousItemIsNowEquipped )
+ {
+ pOptionalSession->OnEquippedStateChange( pPreviousEquippedItem, false );
+ }
+ }
+
+ // are we equipping a new item to this slot, or were we only responsible for unequipping
+ // the existing item?
+ if ( pItem )
+ {
+ // notify the session, in case it wants to do anything; the default implementation
+ // will update the equipped state on the item but other types may have additional
+ // behavior
+ const bool bItemWasEquipped = pItem->IsEquipped();
+
+ pItem->Equip( unClass, unSlot );
+ pSOCache->DirtyObjectField( pItem, CSOEconItem::kEquippedStateFieldNumber );
+ GGCBase()->AddCacheToWritebackQueue( pSOCache );
+
+ const bool bItemIsEquipped = pItem->IsEquipped();
+ Assert( bItemIsEquipped );
+
+ // did our equipped state change? this can happen if we equip ourself for the
+ // first time for this account right now. Keep in mind that this can be either
+ // a "is newly-equipped" or a "was equipped but is now not" state transition.
+ if ( pOptionalSession && (bItemWasEquipped != bItemIsEquipped) )
+ {
+ pOptionalSession->OnEquippedStateChange( pItem, bItemIsEquipped );
+ }
+ }
+}
+
+#define ITEM_ACTION( x ) { x, #x }
+
+ENUMSTRINGS_START( EItemAction )
+ ITEM_ACTION( k_EItemActionGSCreate ),
+ ITEM_ACTION( k_EItemActionUnpurchase ),
+ ITEM_ACTION( k_EItemActionDelete ),
+ ITEM_ACTION( k_EItemActionAwardAchievement ),
+ ITEM_ACTION( k_EItemActionBanned ),
+ ITEM_ACTION( k_EItemActionQuantityChanged ),
+ ITEM_ACTION( k_EItemActionRestored ),
+ ITEM_ACTION( k_EItemActionAwardTime ),
+ ITEM_ACTION( k_EItemActionManualCreate ),
+ ITEM_ACTION( k_EItemActionDrop ),
+ ITEM_ACTION( k_EItemActionPickUp ),
+ ITEM_ACTION( k_EItemActionCraftDestroy ),
+ ITEM_ACTION( k_EItemActionCraftCreate ),
+ ITEM_ACTION( k_EItemActionLimitExceeded ),
+ ITEM_ACTION( k_EItemActionPurchase ),
+ ITEM_ACTION( k_EItemActionNameChanged_Add ),
+ ITEM_ACTION( k_EItemActionUnlockCrate_Add ),
+ ITEM_ACTION( k_EItemActionPaintItem_Add ),
+ ITEM_ACTION( k_EItemActionAutoGrantItem ),
+ ITEM_ACTION( k_EItemActionCrossGameAchievement ),
+ ITEM_ACTION( k_EItemActionAddItemToSocket_Add ),
+ ITEM_ACTION( k_EItemActionAddSocketToItem_Add ),
+ ITEM_ACTION( k_EItemActionRemoveSocketItem_Add ),
+ ITEM_ACTION( k_EItemActionCustomizeItemTexture_Add ),
+ ITEM_ACTION( k_EItemActionItemTraded_Add ),
+ ITEM_ACTION( k_EItemActionUseItem ),
+ ITEM_ACTION( k_EItemActionAwardGift_Receiver ),
+ ITEM_ACTION( k_EItemActionNameChanged_Remove ),
+ ITEM_ACTION( k_EItemActionUnlockCrate_Remove ),
+ ITEM_ACTION( k_EItemActionPaintItem_Remove ),
+ ITEM_ACTION( k_EItemActionAddItemToSocket_Remove ),
+ ITEM_ACTION( k_EItemActionAddSocketToItem_Remove ),
+ ITEM_ACTION( k_EItemActionRemoveSocketItem_Remove ),
+ ITEM_ACTION( k_EItemActionCustomizeItemTexture_Remove ),
+ ITEM_ACTION( k_EItemActionItemTraded_Remove ),
+ ITEM_ACTION( k_EItemActionUnpackItemBundle ),
+ ITEM_ACTION( k_EItemActionCreateItemFromBundle ),
+ ITEM_ACTION( k_EItemActionAwardStorePromotionItem ),
+ ITEM_ACTION( k_EItemActionConvertItem ),
+ ITEM_ACTION( k_EItemActionEarnedItem ),
+ ITEM_ACTION( k_EItemActionAwardGift_Giver ),
+ ITEM_ACTION( k_EItemActionRefundedItem ),
+ ITEM_ACTION( k_EItemActionAwardThirdPartyPromo ),
+ ITEM_ACTION( k_EItemActionRemoveItemName_Remove ),
+ ITEM_ACTION( k_EItemActionRemoveItemName_Add ),
+ ITEM_ACTION( k_EItemActionRemoveItemPaint_Remove ),
+ ITEM_ACTION( k_EItemActionRemoveItemPaint_Add ),
+ ITEM_ACTION( k_EItemActionHalloweenDrop ),
+ ITEM_ACTION( k_EItemActionSteamWorkshopContributor ),
+ ITEM_ACTION( k_EItemActionManualOwnershipChange ),
+ ITEM_ACTION( k_EItemActionSupportDelete ),
+ ITEM_ACTION( k_EItemActionSupportCreatedByUndo ),
+ ITEM_ACTION( k_EItemActionSupportDeletedByUndo ),
+ ITEM_ACTION( k_EItemActionSupportQuantityChangedByUndo ),
+ ITEM_ACTION( k_EItemActionSupportRename_Add ),
+ ITEM_ACTION( k_EItemActionSupportRename_Remove ),
+ ITEM_ACTION( k_EItemActionSupportDescribe_Add ),
+ ITEM_ACTION( k_EItemActionSupportDescribe_Remove ),
+
+ ITEM_ACTION( k_EItemActionStrangePartApply_Add ),
+ ITEM_ACTION( k_EItemActionStrangePartApply_Remove ),
+ ITEM_ACTION( k_EItemActionStrangeScoreReset_Add ),
+ ITEM_ACTION( k_EItemActionStrangeScoreReset_Remove ),
+ ITEM_ACTION( k_EItemActionStrangePartRemove_Add ),
+ ITEM_ACTION( k_EItemActionStrangePartRemove_Remove ),
+
+ ITEM_ACTION( k_EItemActionStrangeRestrictionApply_Add ),
+ ITEM_ACTION( k_EItemActionStrangeRestrictionApply_Remove ),
+ ITEM_ACTION( k_EItemActionTransmogrify_Add ),
+ ITEM_ACTION( k_EItemActionTransmogrify_Remove ),
+ ITEM_ACTION( k_EItemActionHalloweenSpellPageAdd_Add ),
+ ITEM_ACTION( k_EItemActionHalloweenSpellPageAdd_Remove ),
+
+ ITEM_ACTION( k_EItemActionSupportStrangify_Add ),
+ ITEM_ACTION( k_EItemActionSupportStrangify_Remove ),
+
+ ITEM_ACTION( k_EItemActionUpgradeCardApply_Add ),
+ ITEM_ACTION( k_EItemActionUpgradeCardApply_Remove ),
+ ITEM_ACTION( k_EItemActionUpgradeCardRemove_Add ),
+ ITEM_ACTION( k_EItemActionUpgradeCardRemove_Remove ),
+
+#ifdef STAGING_ONLY
+ ITEM_ACTION( k_EItemActionDev_ClientLootListRoll ),
+#endif
+
+ ITEM_ACTION( k_EItemActionPeriodicScoreReward_Add ),
+ ITEM_ACTION( k_EItemActionPeriodicScoreReward_Remove ),
+
+ ITEM_ACTION( k_EItemActionGiftWrap_Add ),
+ ITEM_ACTION( k_EItemActionGiftWrap_Remove ),
+ ITEM_ACTION( k_EItemActionGiftDelivery_Add ),
+ ITEM_ACTION( k_EItemActionGiftDelivery_Remove ),
+ ITEM_ACTION( k_EItemActionGiftUnwrap_Add ),
+ ITEM_ACTION( k_EItemActionGiftUnwrap_Remove ),
+ ITEM_ACTION( k_EItemActionPackageItem ),
+ ITEM_ACTION( k_EItemActionPackageItem_Revoked ),
+ ITEM_ACTION( k_EItemActionHandleMapToken ),
+ ITEM_ACTION( k_EItemActionCafeOrSchoolItem_Remove ),
+ ITEM_ACTION( k_EItemActionVACBanned_Remove ),
+ ITEM_ACTION( k_EItemActionUpgradeThirdPartyPromo ),
+ ITEM_ACTION( k_EItemActionExpired ),
+ ITEM_ACTION( k_EItemActionTradeRollback_Add ),
+ ITEM_ACTION( k_EItemActionTradeRollback_Remove ),
+ ITEM_ACTION( k_EItemActionCDKeyGrant ),
+ ITEM_ACTION( k_EItemActionCDKeyRevoke ),
+ ITEM_ACTION( k_EItemActionWeddingRing_Add ),
+ ITEM_ACTION( k_EItemActionWeddingRing_Remove ),
+ ITEM_ACTION( k_EItemActionWeddingRing_AddPartner ),
+
+ ITEM_ACTION( k_EItemActionEconSetUnowned ),
+ ITEM_ACTION( k_EItemActionEconSetOwned ),
+ ITEM_ACTION( k_EItemActionStrangifyItem_Add ),
+ ITEM_ACTION( k_EItemActionStrangifyItem_Remove ),
+
+ ITEM_ACTION( k_EItemActionRemoveItemCraftIndex_Remove ),
+ ITEM_ACTION( k_EItemActionRemoveItemCraftIndex_Add ),
+ ITEM_ACTION( k_EItemActionRemoveItemMakersMark_Remove ),
+ ITEM_ACTION( k_EItemActionRemoveItemMakersMark_Add ),
+ ITEM_ACTION( k_EItemActionRemoveItemKillStreak_Remove ),
+ ITEM_ACTION( k_EItemActionRemoveItemKillStreak_Add ),
+
+ ITEM_ACTION( k_EItemActionCollectItem_RemoveCollection ),
+ ITEM_ACTION( k_EItemActionCollectItem_UpdateCollection ),
+ ITEM_ACTION( k_EItemActionCollectItem_CollectedItem ),
+ ITEM_ACTION( k_EItemActionCollectItem_RedeemCollectionReward ),
+
+ ITEM_ACTION( k_EItemActionPreviewItem_BeginPreviewPeriod ),
+ ITEM_ACTION( k_EItemActionPreviewItem_EndPreviewPeriodExpired ),
+ ITEM_ACTION( k_EItemActionPreviewItem_EndPreviewPeriodItemBought ),
+
+ ITEM_ACTION( k_EItemActionMvM_ChallengeCompleted_RemoveTicket ),
+ ITEM_ACTION( k_EItemActionMvM_ChallengeCompleted_GrantBadge ),
+ ITEM_ACTION( k_EItemActionMvM_ChallengeCompleted_UpdateBadgeStamps_Remove ),
+ ITEM_ACTION( k_EItemActionMvM_ChallengeCompleted_UpdateBadgeStamps_Add ),
+ ITEM_ACTION( k_EItemActionMvM_ChallengeCompleted_GrantMissionCompletionLoot ),
+ ITEM_ACTION( k_EItemActionMvM_RemoveSquadSurplusVoucher ),
+ ITEM_ACTION( k_EItemActionMvM_AwardSquadSurplus_Receiver ),
+ ITEM_ACTION( k_EItemActionMvM_AwardSquadSurplus_Giver ),
+ ITEM_ACTION( k_EItemActionMvM_ChallengeCompleted_GrantTourCompletionLoot ),
+ ITEM_ACTION( k_EItemActionMvM_AwardHelpANoobBonus_Helper ),
+
+ ITEM_ACTION( k_EItemActionHalloween_UpdateMerasmusLootLevel_Add ),
+ ITEM_ACTION( k_EItemActionHalloween_UpdateMerasmusLootLevel_Remove ),
+
+ ITEM_ACTION( k_EItemActionConsumeItem_Consume_ToolRemove ),
+ ITEM_ACTION( k_EItemActionConsumeItem_Consume_ToolAdd ),
+ ITEM_ACTION( k_EItemActionConsumeItem_Consume_InputRemove ),
+ ITEM_ACTION( k_EItemActionConsumeItem_Complete_OutputAdd ),
+ ITEM_ACTION( k_EItemActionConsumeItem_Complete_ToolRemove ),
+
+ ITEM_ACTION( k_EItemActionItemEaterRecharge_Add ),
+ ITEM_ACTION( k_EItemActionItemEaterRecharge_Remove ),
+
+ ITEM_ACTION( k_EItemActionSupportAddOrModifyAttribute_Remove ),
+ ITEM_ACTION( k_EItemActionSupportAddOrModifyAttribute_Add ),
+
+ ITEM_ACTION( k_EItemAction_UpdateDuckBadgeLevel_Add ),
+ ITEM_ACTION( k_EItemAction_UpdateDuckBadgeLevel_Remove ),
+
+ ITEM_ACTION( k_EItemAction_OperationPass_Add ),
+
+ ITEM_ACTION( k_EItemActionSpyVsEngyWar_JoinedWar ),
+
+ ITEM_ACTION( k_EItemActionMarket_Add ),
+ ITEM_ACTION( k_EItemActionMarket_Remove ),
+
+ ITEM_ACTION( k_EItemAction_QuestComplete_Reward ),
+ ITEM_ACTION( k_EItemAction_QuestComplete_Remove ),
+
+ ITEM_ACTION( k_EItemActionStrangeCountTransfer_Add ),
+ ITEM_ACTION( k_EItemActionStrangeCountTransfer_Remove ),
+
+ ITEM_ACTION( k_EItemActionCraftCollectionUpgrade_Add ),
+ ITEM_ACTION( k_EItemActionCraftCollectionUpgrade_Remove ),
+
+ ITEM_ACTION( k_EItemActionCraftHalloweenOffering_Add ),
+ ITEM_ACTION( k_EItemActionCraftHalloweenOffering_Remove ),
+
+ ITEM_ACTION( k_EItemActionRemoveItemGiftedBy_Remove ),
+ ITEM_ACTION( k_EItemActionRemoveItemGiftedBy_Add ),
+
+ ITEM_ACTION( k_EItemActionAddParticleVerticalAttr_Remove ),
+ ITEM_ACTION( k_EItemActionAddParticleVerticalAttr_Add ),
+
+ ITEM_ACTION( k_EItemActionAddParticleUseHeadOriginAttr_Remove ),
+ ITEM_ACTION( k_EItemActionAddParticleUseHeadOriginAttr_Add ),
+
+ ITEM_ACTION( k_EItemActionRemoveItemDynamicAttr_Remove ),
+ ITEM_ACTION( k_EItemActionRemoveItemDynamicAttr_Add ),
+
+ ITEM_ACTION( k_EItemActionCraftStatClockTradeUp_Remove ),
+ ITEM_ACTION( k_EItemActionCraftStatClockTradeUp_Add ),
+
+ ITEM_ACTION( k_EItemActionViralCompetitiveBetaPass_Drop ),
+
+ ITEM_ACTION( k_EItemActionSupportDeleteAttribute_Remove ),
+ ITEM_ACTION( k_EItemActionSupportDeleteAttribute_Add ),
+
+ENUMSTRINGS_END( EItemAction )
+
+const char *PchFriendlyNameFromEItemAction( EItemAction eItemAction, EItemActionMissingBehavior eMissingBehavior )
+{
+ Assert( eMissingBehavior == kEItemAction_FriendlyNameLookup_ReturnDummyStringIfMissing || eMissingBehavior == kEItemAction_FriendlyNameLookup_ReturnNULLIfMissing );
+
+ switch( eItemAction )
+ {
+
+ case k_EItemActionGSCreate: return "Created by Gameserver";
+ case k_EItemActionUnpurchase: return "Unpurchase";
+ case k_EItemActionDelete: return "Deleted by Owner";
+ case k_EItemActionAwardAchievement: return "Achievement Award";
+ case k_EItemActionBanned: return "Banned";
+ case k_EItemActionQuantityChanged: return "Quantity Changed";
+ case k_EItemActionRestored: return "Restored";
+ case k_EItemActionAwardTime: return "Timed Drop";
+ case k_EItemActionManualCreate: return "Created by Support";
+ case k_EItemActionDrop: return "Dropped";
+ case k_EItemActionPickUp: return "Picked up";
+ case k_EItemActionCraftDestroy: return "Crafted";
+ case k_EItemActionCraftCreate: return "Crafted";
+ case k_EItemActionLimitExceeded: return "Destroyed by Backpack Limit";
+ case k_EItemActionPurchase: return "Purchase";
+ case k_EItemActionNameChanged_Add: return "Added by Name Change";
+ case k_EItemActionUnlockCrate_Add: return "Added by Crate Unlock";
+ case k_EItemActionPaintItem_Add: return "Added by Paint";
+ case k_EItemActionAutoGrantItem: return "Autogrant";
+ case k_EItemActionCrossGameAchievement: return "Cross-Game Achievement Award";
+ case k_EItemActionAddItemToSocket_Add: return "Added to Socket";
+ case k_EItemActionAddSocketToItem_Add: return "Added by Socket";
+ case k_EItemActionRemoveSocketItem_Add: return "Removed by Socket";
+ case k_EItemActionCustomizeItemTexture_Add: return "Added by Custom Texture";
+ case k_EItemActionItemTraded_Add: return "Trade";
+ case k_EItemActionUseItem: return "Used";
+ case k_EItemActionAwardGift_Receiver: return "Gift Received";
+ case k_EItemActionNameChanged_Remove: return "Removed by Name Change";
+ case k_EItemActionUnlockCrate_Remove: return "Removed by Crate Unlock";
+ case k_EItemActionPaintItem_Remove: return "Removed by Paint";
+ case k_EItemActionAddItemToSocket_Remove: return "Removed from Socket";
+ case k_EItemActionAddSocketToItem_Remove: return "Removed by Socket";
+ case k_EItemActionRemoveSocketItem_Remove: return "Removed Socket Item";
+ case k_EItemActionCustomizeItemTexture_Remove: return "Removed by Custom Texture";
+ case k_EItemActionItemTraded_Remove: return "Trade";
+ case k_EItemActionUnpackItemBundle: return "Unpacked Bundle";
+ case k_EItemActionCreateItemFromBundle: return "Added from Bundle";
+ case k_EItemActionAwardStorePromotionItem: return "Store Promo Item";
+ case k_EItemActionConvertItem: return "Converted";
+ case k_EItemActionEarnedItem: return "Earned";
+ case k_EItemActionAwardGift_Giver: return "Gift Sent";
+ case k_EItemActionRefundedItem: return "Item Refunded";
+ case k_EItemActionAwardThirdPartyPromo: return "Third-Party Promo";
+ case k_EItemActionRemoveItemName_Remove: return "Removed by Name Removal";
+ case k_EItemActionRemoveItemName_Add: return "Added by Name Removal";
+ case k_EItemActionRemoveItemPaint_Remove: return "Removed by Paint Removal";
+ case k_EItemActionRemoveItemPaint_Add: return "Added by Paint Removal";
+ case k_EItemActionHalloweenDrop: return "Halloween Drop";
+ case k_EItemActionSteamWorkshopContributor: return "Steam Workshop Contributor";
+ case k_EItemActionManualOwnershipChange: return "Support Manual Ownership Change";
+ case k_EItemActionSupportDelete: return "Deleted by Support";
+ case k_EItemActionSupportCreatedByUndo: return "Created by Undo";
+ case k_EItemActionSupportDeletedByUndo: return "Deleted by Undo";
+ case k_EItemActionSupportQuantityChangedByUndo: return "Quantity Changed by Undo";
+ case k_EItemActionSupportRename_Add: return "Added by Support Rename";
+ case k_EItemActionSupportRename_Remove: return "Removed by Support Rename";
+ case k_EItemActionSupportDescribe_Add: return "Added by Support Describe";
+ case k_EItemActionSupportDescribe_Remove: return "Removed by Support Describe";
+
+ case k_EItemActionStrangePartApply_Add: return "Added by Strange Part Apply";
+ case k_EItemActionStrangePartApply_Remove: return "Removed by Strange Part Apply";
+ case k_EItemActionStrangeScoreReset_Add: return "Added by Strange Score Reset";
+ case k_EItemActionStrangeScoreReset_Remove: return "Removed by Strange Score Reset";
+ case k_EItemActionStrangePartRemove_Add: return "Added by Strange Part Remove";
+ case k_EItemActionStrangePartRemove_Remove: return "Removed by Strange Part Remove";
+
+ case k_EItemActionStrangeRestrictionApply_Add: return "Added by Strange Restriction Apply";
+ case k_EItemActionStrangeRestrictionApply_Remove: return "Removed by Strange Restriction Apply";
+ case k_EItemActionTransmogrify_Add: return "Added by Transmogrify Apply";
+ case k_EItemActionTransmogrify_Remove: return "Removed by Transmogrify Apply";
+ case k_EItemActionHalloweenSpellPageAdd_Add: return "Added by Halloween Spell Page Apply";
+ case k_EItemActionHalloweenSpellPageAdd_Remove: return "Removed by Halloween Spell Page Apply";
+
+ case k_EItemActionSupportStrangify_Add: return "Added by Support Strangify";
+ case k_EItemActionSupportStrangify_Remove: return "Removed by Support Strangify";
+
+ case k_EItemActionUpgradeCardApply_Add: return "Added by Upgrade Card Apply";
+ case k_EItemActionUpgradeCardApply_Remove: return "Removed by Upgrade Card Apply";
+ case k_EItemActionUpgradeCardRemove_Add: return "Added by Upgrade Card Remove";
+ case k_EItemActionUpgradeCardRemove_Remove: return "Removed by Upgrade Card Remove";
+
+#ifdef STAGING_ONLY
+ case k_EItemActionDev_ClientLootListRoll: return "Dev: Client Loot List Roll";
+#endif
+
+ case k_EItemActionPeriodicScoreReward_Add: return "Periodic Score System Reward Grant Add";
+ case k_EItemActionPeriodicScoreReward_Remove: return "Periodic Score System Reward Remove";
+
+ case k_EItemActionGiftWrap_Add: return "Wrapped Gift - Added";
+ case k_EItemActionGiftWrap_Remove: return "Wrapped Gift - Remove";
+ case k_EItemActionGiftDelivery_Add: return "Gift Delivered - Add";
+ case k_EItemActionGiftDelivery_Remove: return "Gift Delivered - Remove";
+ case k_EItemActionGiftUnwrap_Add: return "Unwrapped Gift - Added";
+ case k_EItemActionGiftUnwrap_Remove: return "Unwrapped Gift - Remove";
+ case k_EItemActionPackageItem: return "Added by Steam Purchase";
+ case k_EItemActionPackageItem_Revoked: return "Revoked by Steam Purchase";
+ case k_EItemActionHandleMapToken: return "Map Token Applied";
+ case k_EItemActionCafeOrSchoolItem_Remove: return "Cafe or School Removal";
+ case k_EItemActionVACBanned_Remove: return "VAC Ban removal";
+ case k_EItemActionUpgradeThirdPartyPromo: return "Third-Party Promo Upgrade";
+ case k_EItemActionExpired: return "Expired";
+ case k_EItemActionTradeRollback_Add: return "Trade Rollback";
+ case k_EItemActionTradeRollback_Remove: return "Trade Rollback";
+ case k_EItemActionCDKeyGrant: return "CD Key Grant";
+ case k_EItemActionCDKeyRevoke: return "CD Key Rollback";
+ case k_EItemActionWeddingRing_Add: return "Wedding Ring Add";
+ case k_EItemActionWeddingRing_Remove: return "Wedding Ring Remove";
+ case k_EItemActionWeddingRing_AddPartner: return "Wedding Ring Add (Partner)";
+
+ case k_EItemActionEconSetUnowned: return "Econ SetUnowned";
+ case k_EItemActionEconSetOwned: return "Econ SetOwned";
+
+ case k_EItemActionStrangifyItem_Add: return "Added by Strangify Apply";
+ case k_EItemActionStrangifyItem_Remove: return "Removed by Strangify Apply";
+
+ case k_EItemActionRemoveItemCraftIndex_Remove: return "Removed by Craft Index Removal";
+ case k_EItemActionRemoveItemCraftIndex_Add: return "Added by Craft Index Removal";
+ case k_EItemActionRemoveItemMakersMark_Remove: return "Removed by Maker's Mark Removal";
+ case k_EItemActionRemoveItemMakersMark_Add: return "Added by Maker's Mark Removal";
+ case k_EItemActionRemoveItemKillStreak_Remove: return "Removed by KillStreak Removal";
+ case k_EItemActionRemoveItemKillStreak_Add: return "Added by KillStreak Removal";
+
+ case k_EItemActionCollectItem_CollectedItem: return "Item Collection: Added to a Collection";
+ case k_EItemActionCollectItem_RemoveCollection: return "Item Collection: Removed Old Collection Item";
+ case k_EItemActionCollectItem_UpdateCollection: return "Item Collection: Created New Collection Item";
+ case k_EItemActionCollectItem_RedeemCollectionReward: return "Item Collection: Redeemed Reward";
+
+ case k_EItemActionPreviewItem_BeginPreviewPeriod: return "Item Preview: Begin Preview Period";
+ case k_EItemActionPreviewItem_EndPreviewPeriodExpired: return "Item Preview: End Preview Period by Expiration";
+ case k_EItemActionPreviewItem_EndPreviewPeriodItemBought: return "Item Preview: End Preview Period by Item Purchase";
+
+ case k_EItemActionMvM_ChallengeCompleted_RemoveTicket: return "Removed MvM Ticket";
+ case k_EItemActionMvM_ChallengeCompleted_GrantBadge: return "Granted MvM Badge";
+ case k_EItemActionMvM_ChallengeCompleted_UpdateBadgeStamps_Add: return "Added by MvM Stamp Update";
+ case k_EItemActionMvM_ChallengeCompleted_UpdateBadgeStamps_Remove: return "Removed by MvM Stamp Update";
+ case k_EItemActionMvM_ChallengeCompleted_GrantMissionCompletionLoot: return "Added by MvM Mission Completion";
+ case k_EItemActionMvM_RemoveSquadSurplusVoucher: return "Removed MvM squad surplus voucher";
+ case k_EItemActionMvM_AwardSquadSurplus_Receiver: return "Received MvM squad surplus";
+ case k_EItemActionMvM_AwardSquadSurplus_Giver: return "Generated MvM squad surplus";
+ case k_EItemActionMvM_ChallengeCompleted_GrantTourCompletionLoot: return "Added by MvM Tour Completion";
+ case k_EItemActionMvM_AwardHelpANoobBonus_Helper: return "Added by MvM Help-A-Noob";
+
+ case k_EItemActionHalloween_UpdateMerasmusLootLevel_Add: return "Updating Halloween Merasmus Loot - Adding New item";
+ case k_EItemActionHalloween_UpdateMerasmusLootLevel_Remove: return "Updating Halloween Merasmus Loot Level - Removing Old Item";
+
+ case k_EItemActionConsumeItem_Consume_ToolRemove: return "Dynamic Recipe: Consuming Input Item - Removing Old Recipe-Item";
+ case k_EItemActionConsumeItem_Consume_ToolAdd: return "Dynamic Recipe: Consuming Input Item - Adding New Recipe-Item";
+ case k_EItemActionConsumeItem_Consume_InputRemove: return "Dynamic Recipe: Consuming Input Item - Removing Input item";
+ case k_EItemActionConsumeItem_Complete_OutputAdd: return "Dynamic Recipe: Recipe Complete - Adding Output Item";
+ case k_EItemActionConsumeItem_Complete_ToolRemove: return "Dynamic Recipe: Recipe Complete - Removing Recipe-Item";
+
+ case k_EItemActionItemEaterRecharge_Add: return "Added by Item Eater Recharger";
+ case k_EItemActionItemEaterRecharge_Remove: return "Removed by Item Eater Recharger";
+
+ case k_EItemActionSupportAddOrModifyAttribute_Remove: return "Removed by Support Add/Modify Attribute";
+ case k_EItemActionSupportAddOrModifyAttribute_Add: return "Added by Support Add/Modify Attribute";
+
+ case k_EItemAction_UpdateDuckBadgeLevel_Add: return "Updating Duck Badge Level - Adding New item";
+ case k_EItemAction_UpdateDuckBadgeLevel_Remove: return "Updating Duck Badge Level - Removing Old Item";
+
+ case k_EItemActionSpyVsEngyWar_JoinedWar: return "Added by joining the Spy vs. Engy War";
+
+ case k_EItemAction_OperationPass_Add: return "Adding Operation Pass";
+
+ case k_EItemAction_QuestDrop: return "Quest Drop";
+
+ case k_EItemActionMarket_Add: return "Market - Add";
+ case k_EItemActionMarket_Remove: return "Market - Remove";
+
+ case k_EItemAction_QuestComplete_Reward: return "Added from completing a quest";
+ case k_EItemAction_QuestComplete_Remove: return "Removed from completing a quest";
+
+ case k_EItemActionStrangeCountTransfer_Add: return "Added by using a Strange Count Transfer tool";
+ case k_EItemActionStrangeCountTransfer_Remove: return "Removed by using a Strange Count Transfer tool";
+
+ case k_EItemActionCraftCollectionUpgrade_Add: return "Added by using Craft Collection Upgrade";
+ case k_EItemActionCraftCollectionUpgrade_Remove: return "Removed by using Craft Collection Upgrade";
+
+ case k_EItemActionCraftHalloweenOffering_Add: return "Added by using Craft Halloween Offering";
+ case k_EItemActionCraftHalloweenOffering_Remove: return "Removed by using Craft Halloween Offering";
+
+ case k_EItemActionRemoveItemGiftedBy_Remove: return "Removed by Gifted By Removal";
+ case k_EItemActionRemoveItemGiftedBy_Add: return "Added by Gifted By Removal";
+
+ case k_EItemActionAddParticleVerticalAttr_Remove: return "Removed by Particle Vertical Attr Add";
+ case k_EItemActionAddParticleVerticalAttr_Add: return "Added by Particle Vertical Attr Add";
+
+ case k_EItemActionAddParticleUseHeadOriginAttr_Remove: return "Removed by Particle Use Head Origin Attr Add";
+ case k_EItemActionAddParticleUseHeadOriginAttr_Add: return "Added by Particle Use Head Origin Attr Add";
+
+ case k_EItemActionRemoveItemDynamicAttr_Remove: return "Removed by Dynamic Attribute Removal";
+ case k_EItemActionRemoveItemDynamicAttr_Add: return "Added by Dynamic Attribute Removal";
+
+ case k_EItemActionCraftStatClockTradeUp_Add: return "Added through Stat Clock Trade-Up";
+ case k_EItemActionCraftStatClockTradeUp_Remove: return "Consumed through Stat Clock Trade-Up";
+ case k_EItemActionViralCompetitiveBetaPass_Drop: return "Added via Competitive Beta Invite Drop";
+
+ case k_EItemActionSupportDeleteAttribute_Remove: return "Removed by Support Delete Attribute";
+ case k_EItemActionSupportDeleteAttribute_Add: return "Added by Support Delete Attribute";
+
+ default:
+ return eMissingBehavior == kEItemAction_FriendlyNameLookup_ReturnDummyStringIfMissing
+ ? PchNameFromEItemAction( eItemAction )
+ : PchNameFromEItemActionUnsafe( eItemAction );
+ }
+
+}
+
+const char *PchLocalizedNameFromEItemAction( EItemAction eAction, CLocalizationProvider &localizationProvider )
+{
+ const char *pchAction = PchNameFromEItemAction( eAction );
+ if ( !V_strncmp( pchAction, "k_EItemAction", 13 ) )
+ {
+ CFmtStr fmtLocKey( "ItemHistory_Action_%s", &pchAction[13] );
+ locchar_t *pchLocalizedAction = localizationProvider.Find( fmtLocKey.String() );
+ if ( pchLocalizedAction != NULL )
+ {
+ return pchLocalizedAction;
+ }
+ }
+
+ if ( BIsActionDestructive( eAction ) )
+ {
+ return localizationProvider.Find( "ItemHistory_Action_GenericRemove" );
+ }
+ else if ( BIsActionCreative( eAction ) )
+ {
+ return localizationProvider.Find( "ItemHistory_Action_GenericAdd" );
+ }
+ else
+ {
+ return "Unknown";
+ }
+}
+
+ENUMSTRINGS_START( eEconItemOrigin )
+{ kEconItemOrigin_Drop, "Timed Drop" },
+{ kEconItemOrigin_Achievement, "Achievement" },
+{ kEconItemOrigin_Purchased, "Purchased" },
+{ kEconItemOrigin_Traded, "Traded" },
+{ kEconItemOrigin_Crafted, "Crafted" },
+{ kEconItemOrigin_StorePromotion, "Store Promotion" },
+{ kEconItemOrigin_Gifted, "Gifted" },
+{ kEconItemOrigin_SupportGranted, "Support Granted" },
+{ kEconItemOrigin_FoundInCrate, "Found in Crate" },
+{ kEconItemOrigin_Earned, "Earned" },
+{ kEconItemOrigin_ThirdPartyPromotion, "Third-Party Promotion" },
+{ kEconItemOrigin_GiftWrapped, "Wrapped Gift" },
+{ kEconItemOrigin_HalloweenDrop, "Halloween Drop" },
+{ kEconItemOrigin_PackageItem, "Steam Purchase" },
+{ kEconItemOrigin_Foreign, "Foreign Item" },
+{ kEconItemOrigin_CDKey, "CD Key" },
+{ kEconItemOrigin_CollectionReward, "Collection Reward" },
+{ kEconItemOrigin_PreviewItem, "Preview Item" },
+{ kEconItemOrigin_SteamWorkshopContribution, "Steam Workshop Contribution" },
+{ kEconItemOrigin_PeriodicScoreReward, "Periodic score reward" },
+{ kEconItemOrigin_MvMMissionCompletionReward, "MvM Badge completion reward" },
+{ kEconItemOrigin_MvMSquadSurplusReward, "MvM Squad surplus reward" },
+{ kEconItemOrigin_RecipeOutput, "Recipe output" },
+{ kEconItemOrigin_QuestDrop, "Quest Drop" },
+{ kEconItemOrigin_QuestLoanerItem, "Quest Loaner Item" },
+{ kEconItemOrigin_TradeUp, "Trade-Up" },
+{ kEconItemOrigin_ViralCompetitiveBetaPassSpread, "Viral Competitive Beta Pass Spread" },
+ENUMSTRINGS_END( eEconItemOrigin )
+
+#endif
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CCrateLootListWrapper::BAttemptCrateSeriesInitialization( const IEconItemInterface *pEconItem )
+{
+ Assert( m_pLootList == NULL );
+
+ // Find out what series this crate belongs to.
+ static CSchemaAttributeDefHandle pAttr_CrateSeries( "set supply crate series" );
+ if ( !pAttr_CrateSeries )
+ return false;
+
+ int iCrateSeries;
+ {
+ float fCrateSeries; // crate series ID is stored as a float internally because we hate ourselves
+ if ( !FindAttribute_UnsafeBitwiseCast<attrib_value_t>( pEconItem, pAttr_CrateSeries, &fCrateSeries ) || fCrateSeries == 0.0f )
+ return false;
+
+ iCrateSeries = fCrateSeries;
+ }
+
+ // Our index is an index into the revolving-loot-lists list. From that list we'll be able to
+ // get a loot list name, which we'll use to look up the actual contents.
+ const CEconItemSchema::RevolvingLootListDefinitionMap_t& mapRevolvingLootLists = GetItemSchema()->GetRevolvingLootLists();
+ int idx = mapRevolvingLootLists.Find( iCrateSeries );
+ if ( !mapRevolvingLootLists.IsValidIndex( idx ) )
+ return false;
+
+ const char *pszLootList = mapRevolvingLootLists.Element( idx );
+
+ // Get the loot list.
+ m_pLootList = GetItemSchema()->GetLootListByName( pszLootList );
+ m_unAuditDetailData = iCrateSeries;
+
+ return m_pLootList != NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CCrateLootListWrapper::BAttemptLootListStringInitialization( const IEconItemInterface *pEconItem )
+{
+ Assert( m_pLootList == NULL );
+
+ // Find out what series this crate belongs to.
+ static CSchemaAttributeDefHandle pAttr_LootListName( "loot list name" );
+ if ( !pAttr_LootListName )
+ return false;
+
+ CAttribute_String str;
+ if ( !pEconItem->FindAttribute( pAttr_LootListName, &str ) )
+ return false;
+
+ m_pLootList = GetItemSchema()->GetLootListByName( str.value().c_str() );
+
+ return m_pLootList != NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CCrateLootListWrapper::BAttemptLineItemInitialization( const IEconItemInterface *pEconItem )
+{
+ Assert( m_pLootList == NULL );
+
+ // Do we have at least one line item specified?
+ if ( !pEconItem->FindAttribute( CAttributeLineItemLootList::s_pAttrDef_RandomDropLineItems[0] ) )
+ return false;
+
+ m_pLootList = new CAttributeLineItemLootList( pEconItem );
+ m_bIsDynamicallyAllocatedLootList = true;
+
+ return true;
+}
diff --git a/game/shared/econ/econ_item.h b/game/shared/econ/econ_item.h
new file mode 100644
index 0000000..ae3270e
--- /dev/null
+++ b/game/shared/econ/econ_item.h
@@ -0,0 +1,864 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: CEconItem, a shared object for econ items
+//
+//=============================================================================
+
+#ifndef ECONITEM_H
+#define ECONITEM_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "gcsdk/gcclientsdk.h"
+#include "base_gcmessages.pb.h"
+
+#include "econ_item_constants.h"
+#include "econ_item_interface.h"
+#include "econ_item_schema.h"
+
+#include <typeinfo> // needed for typeid()
+
+#define ENABLE_TYPED_ATTRIBUTE_PARANOIA 1
+
+#ifdef GC_DLL
+class CSchItem;
+class CEconSharedObjectCache;
+#endif
+
+namespace GCSDK
+{
+ class CColumnSet;
+#ifdef GC_DLL
+ class CWebAPIValues;
+#endif
+};
+
+class CEconItem;
+class CSOEconItem;
+class CEconItemCustomData;
+class CEconSessionItemAudit;
+
+//-----------------------------------------------------------------------------
+// Stats tracking for the attributes attached to CEconItem instances.
+//-----------------------------------------------------------------------------
+struct schema_attribute_stat_bucket_t
+{
+ const schema_attribute_stat_bucket_t *m_pNext;
+
+ const char *m_pszDesc;
+ uint64 m_unLiveInlineCount;
+ uint64 m_unLifetimeInlineCount;
+ uint64 m_unLiveHeapCount;
+ uint64 m_unLifetimeHeapCount;
+
+ void OnAllocateInlineInstance() { m_unLiveInlineCount++; m_unLifetimeInlineCount++; }
+ void OnFreeInlineInstance() { Assert( m_unLiveInlineCount > 0 ); m_unLiveInlineCount--; }
+ void OnAllocateHeapInstance() { m_unLiveHeapCount++; m_unLifetimeHeapCount++; }
+ void OnFreeHeapInstance() { Assert( m_unLiveHeapCount ); m_unLiveHeapCount--; }
+};
+
+class CSchemaAttributeStats
+{
+public:
+ template < typename TAttribStatsStorageClass, typename TAttribInMemoryType >
+ static void RegisterAttributeType()
+ {
+ TAttribStatsStorageClass::s_InstanceStats.m_pszDesc = typeid( TAttribInMemoryType ).name();
+ TAttribStatsStorageClass::s_InstanceStats.m_pNext = m_pHead;
+
+ m_pHead = &TAttribStatsStorageClass::s_InstanceStats;
+ }
+
+ static const schema_attribute_stat_bucket_t *GetFirstStatBucket()
+ {
+ return m_pHead;
+ }
+
+private:
+ static const schema_attribute_stat_bucket_t *m_pHead;
+};
+
+//-----------------------------------------------------------------------------
+// Base class interface for attributes of a certain in-memory type.
+//-----------------------------------------------------------------------------
+unsigned int Internal_GetAttributeTypeUniqueIdentifierNextValue();
+
+template < typename T >
+unsigned int GetAttributeTypeUniqueIdentifier()
+{
+ static unsigned int s_unUniqueCounter = Internal_GetAttributeTypeUniqueIdentifierNextValue();
+ return s_unUniqueCounter;
+}
+
+//-----------------------------------------------------------------------------
+// Base class interface for attributes of a certain in-memory type.
+//-----------------------------------------------------------------------------
+template < typename TAttribInMemoryType >
+class ISchemaAttributeTypeBase : public ISchemaAttributeType
+{
+ friend class CSchemaAttributeStats;
+
+public:
+ ISchemaAttributeTypeBase()
+ {
+ CSchemaAttributeStats::RegisterAttributeType< ISchemaAttributeTypeBase<TAttribInMemoryType>, TAttribInMemoryType >();
+
+ // The implementation of the attributes-in-memory system is such that it may or may not behave according to
+ // expectations. Rather than have to stare at all the details to answer questions about where memory is allocated
+ // or managed, or when it will be freed, for all our current use cases it makes more sense to just disable raw
+ // pointer types from being an attribute-in-memory type and instead steer people towards this message explaining
+ // why.
+ COMPILE_TIME_ASSERT( !IsPointerType<TAttribInMemoryType>::kValue );
+ }
+
+#ifdef GC_DLL
+ // By default, without a specific type we don't support any sort of custom value generation, so all we can do
+ // to load an attribute is to copy the value out from the generic format (union) and turn it into whatever our
+ // type is, and then add that type to the item as an attribute.
+ //
+ // Unlike most of the functions in this class, this is not meant to be a catch-all default implementation but
+ // is instead a base implementation. Subclasses are intended to override to add or change functionality.
+ virtual void LoadOrGenerateEconAttributeValue( CEconItem *pTargetItem, const CEconItemAttributeDefinition *pAttrDef, const static_attrib_t& staticAttrib, const CEconGameAccount *pGameAccount ) const OVERRIDE
+ {
+ Assert( pTargetItem );
+ Assert( pAttrDef );
+ AssertMsg( !staticAttrib.m_pKVCustomData, "Default implementation of LoadOrGenerateEconAttributeValue() doesn't support custom value generation!" );
+ AssertMsg( pGameAccount || !staticAttrib.m_pKVCustomData, "Cannot run custom logic with no game account object! Passing in NULL for pGameAccount is only supported when we know we won't be running custom value generation code!" );
+
+ LoadEconAttributeValue( pTargetItem, pAttrDef, staticAttrib.m_value );
+ }
+
+ // By default, we dont generate any custom value
+ virtual void GenerateEconAttributeValue( const CEconItemAttributeDefinition *pAttrDef, const static_attrib_t& staticAttrib, const CEconGameAccount *pGameAccount, attribute_data_union_t* out_pValue ) const OVERRIDE
+ {
+ Assert( pAttrDef );
+ Assert( pGameAccount );
+ Assert( out_pValue );
+ }
+#endif // GC_DLL
+
+ virtual void LoadEconAttributeValue( CEconItem *pTargetItem, const CEconItemAttributeDefinition *pAttrDef, const union attribute_data_union_t& value ) const OVERRIDE;
+
+ // Returns a unique identifier per run based on the type of <TAttribInMemoryType>.
+ virtual unsigned int GetTypeUniqueIdentifier() const OVERRIDE
+ {
+ return GetAttributeTypeUniqueIdentifier<TAttribInMemoryType>();
+ }
+
+ // Takes the value specified in [typedValue] and stores it in the most appropriate way
+ // somewhere attached to [out_pValue]. This may hit the heap. The storage itself is
+ // intended to be opaque but can be reversed by calling GetTypedValueContentsFromEconAttributeValue().
+ void ConvertTypedValueToEconAttributeValue( const TAttribInMemoryType& typedValue, attribute_data_union_t *out_pValue ) const
+ {
+ // If our type is smaller than an int, we don't know how to copy the memory into our flat structure. We could write
+ // this code but we have no use case for it now so this is set up to fail so if someone does come up with a use case
+ // they know where to fix.
+ COMPILE_TIME_ASSERT( sizeof( TAttribInMemoryType ) >= sizeof( uint32 ) );
+
+ // Do we fit in the bottom 32-bits?
+ if ( sizeof( TAttribInMemoryType ) <= sizeof( uint32 ) )
+ {
+ *reinterpret_cast<TAttribInMemoryType *>( &out_pValue->asUint32 ) = typedValue;
+ }
+ // What about in the full 64-bits (if we're running a 64-bit build)?
+ else if ( sizeof( TAttribInMemoryType ) <= sizeof( void * ) )
+ {
+ *reinterpret_cast<TAttribInMemoryType *>( &out_pValue->asBlobPointer ) = typedValue;
+ }
+ // We're too big for our flat structure. We need to allocate space somewhere outside our attribute instance and point
+ // to that.
+ else
+ {
+ Assert( out_pValue->asBlobPointer );
+ *reinterpret_cast<TAttribInMemoryType *>( out_pValue->asBlobPointer ) = typedValue;
+ }
+ }
+
+ // Guaranteed to return a valid reference (or assert/crash if calling code is behaving inappropriately and calling
+ // this before an attribute value is allocated/set).
+ const TAttribInMemoryType& GetTypedValueContentsFromEconAttributeValue( const attribute_data_union_t& value ) const
+ {
+ COMPILE_TIME_ASSERT( sizeof( TAttribInMemoryType ) >= sizeof( uint32 ) );
+
+ // Do we fit in the bottom 32-bits?
+ if ( sizeof( TAttribInMemoryType ) <= sizeof( uint32 ) )
+ return *reinterpret_cast<const TAttribInMemoryType *>( &value.asUint32 );
+
+ // What about in the full 64-bits (if we're running a 64-bit build)?
+ if ( sizeof( TAttribInMemoryType ) <= sizeof( void * ) )
+ return *reinterpret_cast<const TAttribInMemoryType *>( &value.asBlobPointer );
+
+ // We don't expect to get to a "read value" call without having written a value, which would
+ // have allocated this memory.
+ Assert( value.asBlobPointer );
+
+ return *reinterpret_cast<const TAttribInMemoryType *>( value.asBlobPointer );
+ }
+
+ void ConvertEconAttributeValueToTypedValue( const attribute_data_union_t& value, TAttribInMemoryType *out_pTypedValue ) const
+ {
+ Assert( out_pTypedValue );
+
+ *out_pTypedValue = GetTypedValueContentsFromEconAttributeValue( value );
+ }
+
+ void InitializeNewEconAttributeValue( attribute_data_union_t *out_pValue ) const OVERRIDE
+ {
+ if ( sizeof( TAttribInMemoryType ) <= sizeof( uint32 ) )
+ {
+ new( &out_pValue->asUint32 ) TAttribInMemoryType;
+ s_InstanceStats.OnAllocateInlineInstance();
+ }
+ else if ( sizeof( TAttribInMemoryType ) <= sizeof( void * ) )
+ {
+ new( &out_pValue->asBlobPointer ) TAttribInMemoryType;
+ s_InstanceStats.OnAllocateInlineInstance();
+ }
+ else
+ {
+ out_pValue->asBlobPointer = reinterpret_cast<byte *>( new TAttribInMemoryType );
+ s_InstanceStats.OnAllocateHeapInstance();
+ }
+ }
+
+ virtual void UnloadEconAttributeValue( attribute_data_union_t *out_pValue ) const OVERRIDE
+ {
+ COMPILE_TIME_ASSERT( sizeof( TAttribInMemoryType ) >= sizeof( uint32 ) );
+
+ // For smaller types, anything that fits inside the bits of a void pointer, we store the contents
+ // inline and only have to worry about calling the correct destructor. We check against the small-/
+ // size/medium-size values separately to not worry about which bits we're storing the uint32 in.
+ if ( sizeof( TAttribInMemoryType ) <= sizeof( uint32 ) )
+ {
+ (reinterpret_cast<TAttribInMemoryType *>( &out_pValue->asUint32 ))->~TAttribInMemoryType();
+ s_InstanceStats.OnFreeInlineInstance();
+ }
+ else if ( sizeof( TAttribInMemoryType ) <= sizeof( void * ) )
+ {
+ (reinterpret_cast<TAttribInMemoryType *>( &out_pValue->asBlobPointer ))->~TAttribInMemoryType();
+ s_InstanceStats.OnFreeInlineInstance();
+ }
+ // For larger types, we have the memory stored on the heap somewhere. We don't have to manually
+ // destruct, but we do have to manually free.
+ else
+ {
+ Assert( out_pValue->asBlobPointer );
+
+ delete reinterpret_cast<TAttribInMemoryType *>( out_pValue->asBlobPointer );
+ s_InstanceStats.OnFreeHeapInstance();
+ }
+ }
+
+ virtual bool OnIterateAttributeValue( IEconItemAttributeIterator *pIterator, const CEconItemAttributeDefinition *pAttrDef, const attribute_data_union_t& value ) const OVERRIDE
+ {
+ Assert( pIterator );
+ Assert( pAttrDef );
+
+ // Call the appropriate virtual function on our iterator based on whatever type we represent.
+ return pIterator->OnIterateAttributeValue( pAttrDef, GetTypedValueContentsFromEconAttributeValue( value ) );
+ }
+
+ virtual void LoadByteStreamToEconAttributeValue( CEconItem *pTargetItem, const CEconItemAttributeDefinition *pAttrDef, const std::string& sBytes ) const OVERRIDE;
+ virtual void ConvertEconAttributeValueToByteStream( const attribute_data_union_t& value, ::std::string *out_psBytes ) const;
+
+ virtual void ConvertTypedValueToByteStream( const TAttribInMemoryType& typedValue, ::std::string *out_psBytes ) const = 0;
+ virtual void ConvertByteStreamToTypedValue( const ::std::string& sBytes, TAttribInMemoryType *out_pTypedValue ) const = 0;
+
+private:
+ static schema_attribute_stat_bucket_t s_InstanceStats;
+};
+
+// This function exists only to back-convert code that relies on the old untyped
+// attribute system, doing things like shoving floating-point bits into a uint32
+// value in the database.
+//
+// There is no reason to use this function moving forward! If you're writing new
+// code and calling this function seems like the only way to get the effect you
+// want, it probably just means that there is no attribute type for what you're
+// trying to do yet.
+template < typename T > uint32 WrapDeprecatedUntypedEconItemAttribute( T tValue ) { COMPILE_TIME_ASSERT( sizeof( T ) == sizeof( uint32 ) ); return *reinterpret_cast<uint32 *>( &tValue ); }
+
+template < typename TAttribInMemoryType >
+schema_attribute_stat_bucket_t ISchemaAttributeTypeBase<TAttribInMemoryType>::s_InstanceStats;
+
+class CEconItem : public GCSDK::CSharedObject, public CMaterialOverrideContainer< IEconItemInterface >
+{
+#ifdef GC_DLL
+ DECLARE_CLASS_MEMPOOL( CEconItem );
+#endif
+
+public:
+ typedef GCSDK::CSharedObject BaseClass;
+
+ struct attribute_t
+ {
+ attrib_definition_index_t m_unDefinitionIndex; // stored as ints here for memory efficiency on the GC
+ attribute_data_union_t m_value;
+
+ private:
+ void operator=( const attribute_t& rhs );
+ };
+
+ struct EquippedInstance_t
+ {
+ EquippedInstance_t() : m_unEquippedClass( 0 ), m_unEquippedSlot( INVALID_EQUIPPED_SLOT ) {}
+ EquippedInstance_t( equipped_class_t unClass, equipped_slot_t unSlot ) : m_unEquippedClass( unClass ), m_unEquippedSlot( unSlot ) {}
+ equipped_class_t m_unEquippedClass;
+ equipped_slot_t m_unEquippedSlot;
+ };
+
+#ifdef GC_DLL
+ class CAuditEntry
+ {
+ public:
+ CAuditEntry( EItemAction eAction, uint32 unData ) : m_eAction( eAction ), m_unData( unData ) { }
+
+ bool BAddAuditEntryToTransaction( CSQLAccess& sqlAccess, const CEconItem *pItem ) const;
+
+ private:
+ EItemAction m_eAction;
+ uint32 m_unData;
+ };
+
+ // Set only the top 16 bits for field ID types! These will be or'd into the index of
+ // the field itself and then pulled apart later.
+ enum
+ {
+ kUpdateFieldIDType_FieldID = 0x00000000, // this must stay as 0 for legacy code
+ kUpdateFieldIDType_AttributeID = 0x00010000,
+ };
+#endif // GC_DLL
+
+ const static int k_nTypeID = k_EEconTypeItem;
+ virtual int GetTypeID() const { return k_nTypeID; }
+
+ CEconItem();
+ CEconItem( const CEconItem& rhs );
+ virtual ~CEconItem();
+
+ CEconItem &operator=( const CEconItem& rhs );
+
+ //called to determine if this item is tradable or not. This will return the time after which it can be traded. If 0 it can be traded. This is
+ //needed since the base implementation of this is protected
+ RTime32 GetTradableAfterDateTime() const { return IEconItemInterface::GetTradableAfterDateTime(); }
+
+ //called to set a tradable after date/time value onto this item (this avoids a lot of potential inefficiencies around this process)
+ void SetTradableAfterDateTime( RTime32 rtTime );
+
+ // IEconItemInterface interface.
+ const GameItemDefinition_t *GetItemDefinition() const;
+public:
+
+ virtual void IterateAttributes( class IEconItemAttributeIterator *pIterator ) const OVERRIDE;
+ virtual itemid_t GetID() const { return GetItemID(); }
+
+ // Accessors/Settors
+ itemid_t GetItemID() const { return m_ulID; }
+ void SetItemID( uint64 ulID );
+
+ itemid_t GetOriginalID() const;
+ void SetOriginalID( uint64 ulOriginalID );
+
+ uint32 GetAccountID() const { return m_unAccountID; }
+ void SetAccountID( uint32 unAccountID ) { m_unAccountID = unAccountID; }
+
+ uint32 GetDefinitionIndex() const { return m_unDefIndex; }
+ void SetDefinitionIndex( uint32 unDefinitionIndex ) { m_unDefIndex = unDefinitionIndex; }
+
+ uint32 GetItemLevel() const { return m_unLevel; }
+ void SetItemLevel( uint32 unItemLevel ) { m_unLevel = unItemLevel; }
+
+ int32 GetQuality() const { return m_nQuality; }
+ void SetQuality( int32 nQuality ) { m_nQuality = nQuality; }
+
+ uint32 GetInventoryToken() const { return m_unInventory; }
+ void SetInventoryToken( uint32 unToken ) { m_unInventory = unToken; }
+
+ int GetQuantity() const;
+ void SetQuantity( uint16 unQuantity );
+
+ uint8 GetFlags() const { return m_unFlags; }
+ void SetFlags( uint8 unFlags ) { m_unFlags = unFlags; }
+
+ void SetFlag( uint8 unFlag ) { m_unFlags |= unFlag; }
+ void ClearFlag( uint8 unFlag ) { m_unFlags &= ~unFlag; }
+ bool CheckFlags( uint8 unFlags ) const { return ( m_unFlags & unFlags ) != 0; }
+
+ eEconItemOrigin GetOrigin() const { return (eEconItemOrigin)m_unOrigin; }
+ void SetOrigin( eEconItemOrigin unOrigin ) { m_unOrigin = unOrigin; Assert( m_unOrigin == unOrigin ); }
+ bool IsForeign() const { return m_unOrigin == kEconItemOrigin_Foreign; }
+
+ style_index_t GetStyle() const;
+ void SetStyle( uint8 unStyle ) { m_unStyle = unStyle; DirtyIconURL(); }
+
+ const char *GetIconURLSmall() const;
+ const char *GetIconURLLarge() const;
+
+ const char *GetCustomName() const;
+ void SetCustomName( const char *pName );
+
+ const char *GetCustomDesc() const;
+ void SetCustomDesc( const char *pDesc );
+
+ bool IsEquipped() const;
+ bool IsEquippedForClass( equipped_class_t unClass ) const;
+ equipped_slot_t GetEquippedPositionForClass( equipped_class_t unClass ) const;
+
+ void Equip( equipped_class_t unClass, equipped_slot_t unSlot );
+ void Unequip();
+ void UnequipFromClass( equipped_class_t unClass );
+
+ // This should really only used for the WebAPIs, debugging, etc. Data manipulation during gameplay should use
+ // the above functions.
+ int GetEquippedInstanceCount() const;
+ const EquippedInstance_t &GetEquippedInstance( int iIdx ) const;
+
+ virtual bool GetInUse() const;
+ void SetInUse( bool bInUse );
+
+ bool IsTradable() const;
+ bool IsMarketable() const;
+ bool IsCommodity() const;
+
+ void AdoptMoreRestrictedTradabilityFromItem( const CEconItem *pOther, uint32 nTradabilityFlagsToAccept = 0xFFFFFFFF );
+ void AdoptMoreRestrictedTradability( uint32 nTradabilityFlags, RTime32 nUntradableTime );
+ bool IsUsableInCrafting() const;
+
+#ifdef GC_DLL
+ RTime32 GetAssetInfoExpirationCacheExpirationTime() const;
+#endif // GC_DLL
+
+ // --------------------------------------------------------------------------------------------
+ // Typed attributes. These are methods for accessing and setting values of attributes with
+ // some semblance of type information and type safety.
+ // --------------------------------------------------------------------------------------------
+
+ // Assign the value of the attribute [pAttrDef] to [value]. Passing in a type for [value] that
+ // doesn't match the storage type specified by the attribute definition will fail asserts a bunch
+ // of asserts all the way down the stack and may or may not crash -- it would be nice to make this
+ // fail asserts at compile time.
+ //
+ // This function has undefined results (besides asserting) if called to add a dynamic version of
+ // an attrib that's already specified statically.
+ template < typename T >
+ void SetDynamicAttributeValue( const CEconItemAttributeDefinition *pAttrDef, const T& value )
+ {
+ Assert( pAttrDef );
+
+ const ISchemaAttributeTypeBase<T> *pAttrType = GetTypedAttributeType<T>( pAttrDef );
+#ifdef GC_DLL
+ // The GC is expected to always have internally-consistent information and so be able to access the
+ // type information of any attribute if we started up successfully.
+ Assert( pAttrType );
+#else
+ // Game clients and servers may be running code that doesn't have all of the types for the new attributes
+ // for a GC that just propped. Because we're not authoritative over items here, about the best we can do
+ // here is abort entirely. This means that the client may not display certain attributes at all, or even
+ // have them in the attribute list in memory, but we don't understand those attributes anyway.
+ if ( !pAttrType )
+ return;
+#endif
+
+ // Fail right off the bat if we're trying to write a dynamic attribute value for an item that already
+ // has this as a static value.
+ AssertMsg4( !::FindAttribute( GetItemDefinition(), pAttrDef ),
+ "Item id %llu (%s) attempting to set dynamic attribute value for '%s' (%d) when static attribute exists!",
+ GetItemID(), GetItemDefinition()->GetDefinitionName(), pAttrDef->GetDefinitionName(), pAttrDef->GetDefinitionIndex() );
+
+ // Alright, we have a data type match so we can safely store data. Some types may need to initialize
+ // their data to a current state if it's the first time we're writing to this value (as opposed to
+ // updating an existing value).
+ attribute_t *pEconAttrib = FindDynamicAttributeInternal( pAttrDef );
+
+ if ( !pEconAttrib )
+ {
+ pEconAttrib = &(AddDynamicAttributeInternal());
+ pEconAttrib->m_unDefinitionIndex = pAttrDef->GetDefinitionIndex();
+ pAttrType->InitializeNewEconAttributeValue( &pEconAttrib->m_value );
+ }
+
+ pAttrType->ConvertTypedValueToEconAttributeValue( value, &pEconAttrib->m_value );
+
+#if ENABLE_TYPED_ATTRIBUTE_PARANOIA
+ // Paranoia!: make sure that our read/write functions are mirrored correctly, and that if we attempt
+ // to read back a value we get something identical to what we just wrote. We do this via converting
+ // to strings and then comparing those because there may or not be equality comparisons for our type
+ // T that make sense (ie., protobufs).
+ {
+ T readValue;
+ DbgVerify( FindAttribute( pAttrDef, &readValue ) );
+
+ std::string sBytes, sReadBytes;
+ pAttrType->ConvertTypedValueToByteStream( value, &sBytes );
+ pAttrType->ConvertTypedValueToByteStream( readValue, &sReadBytes );
+ AssertMsg1( sBytes == sReadBytes, "SetDynamicAttributeValue(): read/write mismatch for attribute '%s'.", pAttrDef->GetDefinitionName() );
+ }
+#endif // ENABLE_TYPED_ATTRIBUTE_PARANOIA
+ }
+
+ // Called to set a time stamp dynamic attribute on this item. But it will first check the current value assigned to this item, and will
+ // only set it if this new time extends beyond the current one
+ void SetDynamicMaxTimeAttributeValue( const CEconItemAttributeDefinition *pAttrDef, RTime32 rtTime );
+
+ // Remove an instance of an attribute from this item. This will also free any dynamic memory associated
+ // with that instance if any was allocated.
+ void RemoveDynamicAttribute( const CEconItemAttributeDefinition *pAttrDef );
+
+ // Copy all attributes and values in a type-safe way from [source] to ourself. Attributes that we have
+ // that don't exist on [source] will maintain their current values. All other attributes will get their
+ // values set to whatever [source] specifies.
+ void CopyAttributesFrom( const CEconItem& source );
+
+ bool BHasDynamicAttributes() const { return GetDynamicAttributeCountInternal() > 0; }
+
+private:
+ const char* FindIconURL( bool bLarge ) const;
+
+ void Init();
+
+ template < typename T >
+ static const ISchemaAttributeTypeBase<T> *GetTypedAttributeType( const CEconItemAttributeDefinition *pAttrDef )
+ {
+ // Make sure the type of data we're passing in matches the type of data we're claiming that we can
+ // store in the attribute definition.
+ const ISchemaAttributeType *pIAttr = pAttrDef->GetAttributeType();
+ Assert( pIAttr );
+ Assert( pIAttr->GetTypeUniqueIdentifier() == GetAttributeTypeUniqueIdentifier<T>() );
+
+#if ENABLE_TYPED_ATTRIBUTE_PARANOIA
+ return dynamic_cast<const ISchemaAttributeTypeBase<T> *>( pIAttr );
+#else
+ return static_cast<const ISchemaAttributeTypeBase<T> *>( pIAttr );
+#endif
+ }
+
+public:
+ void Compact();
+
+#ifdef GC
+ bool BDeserializeFromKV( KeyValues *pKVItem, CUtlVector<CUtlString> *pVecErrors );
+#endif // GC
+
+#ifdef GC_DLL
+ void ExportToAPI( GCSDK::CWebAPIValues *pValues ) const;
+ bool BImportFromAPI( GCSDK::CWebAPIValues *pValues );
+#endif // GC_DLL
+
+ // these are overridden to handle attributes
+#ifdef GC_DLL
+ virtual bool BYieldingAddInsertToTransaction( GCSDK::CSQLAccess & sqlAccess );
+ virtual bool BYieldingAddWriteToTransaction( GCSDK::CSQLAccess & sqlAccess, const CUtlVector< int > &fields );
+ virtual bool BYieldingAddRemoveToTransaction( GCSDK::CSQLAccess & sqlAccess );
+
+ void SerializeToSchemaItem( CSchItem &item ) const;
+ void DeserializeFromSchemaItem( const CSchItem &item );
+
+ void SetInteriorItem( CEconItem* pInteriorItem );
+#endif // GC_DLL
+ virtual bool BParseFromMessage( const CUtlBuffer &buffer ) OVERRIDE;
+ virtual bool BParseFromMessage( const std::string &buffer ) OVERRIDE;
+ virtual bool BUpdateFromNetwork( const CSharedObject & objUpdate ) OVERRIDE;
+
+#ifdef GC
+ virtual bool BAddToMessage( CUtlBuffer & bufOutput ) const OVERRIDE;
+ virtual bool BAddToMessage( std::string *pBuffer ) const OVERRIDE; // short cut to remove an extra copy
+ virtual bool BAddDestroyToMessage( CUtlBuffer & bufDestroy ) const OVERRIDE;
+ virtual bool BAddDestroyToMessage( std::string *pBuffer ) const OVERRIDE;
+
+ bool BYieldingSerializeFromDatabase( itemid_t ulItemID );
+#endif
+
+ virtual bool BIsKeyLess( const CSharedObject & soRHS ) const ;
+ virtual void Copy( const CSharedObject & soRHS );
+ virtual void Dump() const;
+ virtual CUtlString GetDebugString() const OVERRIDE;
+
+ void SerializeToProtoBufItem( CSOEconItem &msgItem ) const;
+ void DeserializeFromProtoBufItem( const CSOEconItem &msgItem );
+
+#ifdef GC_DLL
+ CEconItem* YieldingGetInteriorItem();
+ const CEconItem* YieldingGetInteriorItem() const { return const_cast<CEconItem *>(this)->YieldingGetInteriorItem(); }
+
+ void SetEquippedThisGameServerSession( bool bEquipped ) { m_bEquippedThisGameServerSession = bEquipped; }
+ bool EquippedThisGameServerSession() const { return m_bEquippedThisGameServerSession; }
+#endif
+
+ // Non-yielding -- will return current interior item if it exists and is already loaded
+ // but will make no attempt to load.
+ CEconItem* GetInteriorItem();
+ const CEconItem* GetInteriorItem() const { return const_cast<CEconItem *>(this)->GetInteriorItem(); }
+
+ const CEconItemCustomData* GetCustomData() const { return m_pCustomData; }
+
+ void OnTraded( uint32 unTradabilityDelaySeconds );
+ void OnReceivedFromMarket( bool bFromRollback );
+
+protected:
+
+ // Call this when the appearance of this item changes (ex. paintkit, style, festive). This will
+ // cause the icon to be lazily re-evaluated (ie. so that changing the style will change the icon)
+ void DirtyIconURL() { m_pszLargeIcon = NULL; m_pszSmallIcon = NULL; }
+ // CSharedObject
+ // adapted from CSchemaSharedObject
+ void GetDirtyColumnSet( const CUtlVector< int > &fields, GCSDK::CColumnSet &cs ) const;
+
+ void EnsureCustomDataExists();
+
+ bool BYieldingLoadInteriorItem();
+
+ void OnTransferredOwnership();
+
+ // Internal attribute interface.
+ friend class CWebAPIStringExporterAttributeIterator;
+ friend class CAttributeToStringIterator;
+
+ attribute_t& AddDynamicAttributeInternal(); // add another chunk of data to our internal storage to store a new attribute -- initialization is the responsibility of the caller
+ attribute_t *FindDynamicAttributeInternal( const CEconItemAttributeDefinition *pAttrDef ); // search for an instance of a dynamic attribute with this definition -- ignores static properties, etc. and will return NULL if not found
+ int GetDynamicAttributeCountInternal() const; // how many attributes are there attached to this instance?
+ attribute_t& GetMutableDynamicAttributeInternal( int iAttrIndexIntoArray ); // get a writable version of our attribute memory base chunk (added by AddDynamicAttributeInternal) for this index (same "array" as GetDynamicAttributeCountInternal)
+ const attribute_t& GetDynamicAttributeInternal( int iAttrIndexIntoArray ) const // read-only version of our attribute memory base chunk for this index (same "array" as GetDynamicAttributeCountInternal)
+ {
+ return const_cast<CEconItem *>( this )->GetMutableDynamicAttributeInternal( iAttrIndexIntoArray );
+ }
+
+ const EquippedInstance_t *FindEquippedInstanceForClass( equipped_class_t nClass ) const;
+ void InternalVerifyEquipInstanceIntegrity() const;
+
+ struct dirty_bits_t
+ {
+ // other
+ uint8 m_bInUse : 1;
+ uint8 m_bHasEquipSingleton : 1;
+ uint8 m_bHasAttribSingleton: 1;
+ };
+
+ mutable const char* m_pszSmallIcon;
+ mutable const char* m_pszLargeIcon;
+public:
+ // data that is most commonly changed
+ uint64 m_ulID; // Item ID
+ uint32 m_unAccountID; // Item Owner
+ uint32 m_unInventory; // App managed int representing inventory placement
+ item_definition_index_t m_unDefIndex; // Item definition index
+ uint8 m_unLevel; // Item Level
+ uint8 m_nQuality; // Item quality (rarity)
+ uint8 m_unFlags; // Flags
+ uint8 m_unOrigin; // Origin (eEconItemOrigin)
+ style_index_t m_unStyle; // Style
+
+ dirty_bits_t m_dirtyBits; // dirty bits
+
+ // Fields that we often have zero or one of, but not often more
+ EquippedInstance_t m_EquipInstanceSingleton; // Where the item is equipped. Valid only if m_bHasEquipSingleton and there is no custom data
+ attribute_t m_CustomAttribSingleton; // Custom attribute. Valid only if m_bHasAttribSingleton and there is no custom data
+
+ // optional data (custom name, additional attributes, etc.)
+ CEconItemCustomData *m_pCustomData;
+
+#ifdef GC_DLL
+private:
+ bool m_bEquippedThisGameServerSession;
+#endif // GC_DLL
+};
+
+//-----------------------------------------------------------------------------
+// Purpose: Storage for data that is not commonly changed in CEconItem, primarily
+// as a memory savings mechanism.
+//-----------------------------------------------------------------------------
+class CEconItemCustomData
+{
+public:
+ CEconItemCustomData()
+ : m_pInteriorItem( NULL )
+ , m_ulOriginalID( INVALID_ITEM_ID )
+ , m_unQuantity( 1 )
+ , m_vecAttributes( /* grow size: */ 1, /* init size: */ 0 )
+ , m_vecEquipped( /* grow size: */ 1, /* init size: */ 0 )
+ {}
+
+ ~CEconItemCustomData();
+
+ CUtlVector< CEconItem::attribute_t > m_vecAttributes;
+ CEconItem* m_pInteriorItem;
+ uint64 m_ulOriginalID; // Original Item ID
+ uint16 m_unQuantity; // Consumable stack count (ammo, money, etc)
+
+ CUtlVector<CEconItem::EquippedInstance_t> m_vecEquipped;
+
+ static void FreeAttributeMemory( CEconItem::attribute_t *pAttrib );
+
+#ifdef GC_DLL
+ DECLARE_CLASS_MEMPOOL( CEconItemCustomData );
+#endif
+};
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+template < typename TAttribInMemoryType >
+/*virtual*/ void ISchemaAttributeTypeBase<TAttribInMemoryType>::LoadByteStreamToEconAttributeValue( CEconItem *pTargetItem, const CEconItemAttributeDefinition *pAttrDef, const std::string& sBytes ) const
+{
+ Assert( pTargetItem );
+ Assert( pAttrDef );
+
+ TAttribInMemoryType typedValue;
+ ConvertByteStreamToTypedValue( sBytes, &typedValue );
+
+ pTargetItem->SetDynamicAttributeValue( pAttrDef, typedValue );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+template < typename TAttribInMemoryType >
+/*virtual*/ void ISchemaAttributeTypeBase<TAttribInMemoryType>::ConvertEconAttributeValueToByteStream( const attribute_data_union_t& value, ::std::string *out_psBytes ) const
+{
+ ConvertTypedValueToByteStream( GetTypedValueContentsFromEconAttributeValue( value ), out_psBytes );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+template < typename TAttribInMemoryType >
+/*virtual*/ void ISchemaAttributeTypeBase<TAttribInMemoryType>::LoadEconAttributeValue( CEconItem *pTargetItem, const CEconItemAttributeDefinition *pAttrDef, const union attribute_data_union_t& value ) const
+{
+ pTargetItem->SetDynamicAttributeValue( pAttrDef, GetTypedValueContentsFromEconAttributeValue( value ) );
+}
+
+#ifdef GC_DLL
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+struct CEconItemEquipInstanceHelpers
+{
+ static void AssignItemToSlot( CEconSharedObjectCache *pSOCache, CEconItem *pItem, equipped_class_t unClass, equipped_slot_t unSlot, CEconUserSession *pOptionalSession = NULL );
+};
+#endif // GC_DLL
+
+void YieldingAddAuditRecord( GCSDK::CSQLAccess *sqlAccess, CEconItem *pItem, uint32 unOwnerID, EItemAction eAction, uint32 unData );
+void YieldingAddAuditRecord( GCSDK::CSQLAccess *sqlAccess, uint64 ulItemID, uint32 unOwnerID, EItemAction eAction, uint32 unData );
+bool YieldingAddItemToDatabase( CEconItem *pItem, const CSteamID & steamID, EItemAction eAction, uint32 unData );
+
+//-----------------------------------------------------------------------------
+// Purpose: wrap the idea of "get a loot list from this item"; some loot lists
+// are static definitions and some are temporary heap-allocated objects
+// and this means you don't care which you're dealing with until we
+// come up with a better interface
+//-----------------------------------------------------------------------------
+class CCrateLootListWrapper
+{
+public:
+ CCrateLootListWrapper( const IEconItemInterface *pEconItem )
+ : m_pLootList( NULL )
+ , m_unAuditDetailData( 0 )
+ , m_bIsDynamicallyAllocatedLootList( false )
+ {
+ Assert( pEconItem );
+
+ if ( !BAttemptCrateSeriesInitialization( pEconItem )
+ && !BAttemptLootListStringInitialization( pEconItem )
+ && !BAttemptLineItemInitialization( pEconItem ) )
+ {
+ // We don't actually have anything to do here. We'll return NULL when someone asks for our
+ // loot list and we're done.
+ }
+ }
+
+ ~CCrateLootListWrapper()
+ {
+ if ( m_bIsDynamicallyAllocatedLootList )
+ {
+ delete m_pLootList;
+ }
+ }
+
+ const IEconLootList *GetEconLootList() const
+ {
+ return m_pLootList;
+ }
+
+ uint32 GetAuditDetailData() const
+ {
+ return m_unAuditDetailData;
+ }
+
+private:
+ CCrateLootListWrapper( const CCrateLootListWrapper& ); // intentionally unimplemented
+ void operator=( const CCrateLootListWrapper& ); // intentionally unimplemented
+
+private:
+ // Look for an attribute that specifies a crate series.
+ MUST_CHECK_RETURN bool BAttemptCrateSeriesInitialization( const IEconItemInterface *pEconItem );
+
+ // Look for an attribute that specifies a loot list by string name.
+ MUST_CHECK_RETURN bool BAttemptLootListStringInitialization( const IEconItemInterface *pEconItem );
+
+ // Look for a line-item-per-attribute list.
+ MUST_CHECK_RETURN bool BAttemptLineItemInitialization( const IEconItemInterface *pEconItem );
+
+private:
+ const IEconLootList *m_pLootList;
+ uint32 m_unAuditDetailData;
+ bool m_bIsDynamicallyAllocatedLootList;
+};
+
+//-----------------------------------------------------------------------------
+// Purpose: Maintains a handle to an CEconItem. If the item gets deleted, this
+// handle will return NULL when dereferenced
+//-----------------------------------------------------------------------------
+class CEconItemHandle : GCSDK::ISharedObjectListener
+{
+public:
+ CEconItemHandle()
+ : m_pItem( NULL )
+ , m_iItemID( INVALID_ITEM_ID )
+ {}
+
+ CEconItemHandle( CEconItem* pItem )
+ : m_pItem( pItem )
+ {
+ SetItem( pItem );
+ }
+
+ virtual ~CEconItemHandle();
+
+ void SetItem( CEconItem* pItem );
+
+ operator CEconItem *( void ) const
+ {
+ return m_pItem;
+ }
+
+ CEconItem* operator->( void ) const
+ {
+ return m_pItem;
+ }
+
+ CEconItem* operator=( CEconItem* pRhs )
+ {
+ SetItem( pRhs );
+ return m_pItem;
+ }
+
+ virtual void SODestroyed( const CSteamID & steamIDOwner, const GCSDK::CSharedObject *pObject, GCSDK::ESOCacheEvent eEvent ) OVERRIDE;
+
+ virtual void SOCacheUnsubscribed( const CSteamID & steamIDOwner, GCSDK::ESOCacheEvent eEvent ) OVERRIDE;
+ virtual void SOCreated( const CSteamID & steamIDOwner, const GCSDK::CSharedObject *pObject, GCSDK::ESOCacheEvent eEvent ) OVERRIDE;
+ virtual void SOUpdated( const CSteamID & steamIDOwner, const GCSDK::CSharedObject *pObject, GCSDK::ESOCacheEvent eEvent ) OVERRIDE;
+
+ virtual void PreSOUpdate( const CSteamID & steamIDOwner, GCSDK::ESOCacheEvent eEvent ) OVERRIDE{}
+ virtual void PostSOUpdate( const CSteamID & steamIDOwner, GCSDK::ESOCacheEvent eEvent ) OVERRIDE{}
+ virtual void SOCacheSubscribed( const CSteamID & steamIDOwner, GCSDK::ESOCacheEvent eEvent ) OVERRIDE{}
+
+private:
+
+ void UnsubscribeFromSOEvents();
+
+ CEconItem* m_pItem; // The item
+ itemid_t m_iItemID; // The stored itemID
+ CSteamID m_OwnerSteamID; // Steam ID of the item owner. Used for registering/unregistering from SOCache
+};
+
+#endif // ECONITEM_H
diff --git a/game/shared/econ/econ_item_constants.cpp b/game/shared/econ/econ_item_constants.cpp
new file mode 100644
index 0000000..ffb7784
--- /dev/null
+++ b/game/shared/econ/econ_item_constants.cpp
@@ -0,0 +1,970 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Holds constants for the econ item system
+//
+//=============================================================================
+
+#include "cbase.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+const char *g_szQualityStrings[] =
+{
+ "Normal",
+ "rarity1", // Genuine
+ "rarity2", // Customized
+ "vintage", // Vintage has to stay at 3 for backwards compatibility
+ "rarity3", // Well-Designed
+ "rarity4", // Unusual
+ "Unique",
+ "community",
+ "developer",
+ "selfmade",
+ "customized",
+ "strange",
+ "completed",
+ "haunted",
+ "collectors",
+ "paintkitWeapon",
+
+ "default", // AE_RARITY_DEFAULT,
+ "common", // AE_RARITY_COMMON,
+ "uncommon", // AE_RARITY_UNCOMMON,
+ "rare", // AE_RARITY_RARE,
+ "mythical", // AE_RARITY_MYTHICAL,
+ "legendary", // AE_RARITY_LEGENDARY,
+ "ancient", // AE_RARITY_ANCIENT,
+};
+
+COMPILE_TIME_ASSERT( ARRAYSIZE( g_szQualityStrings ) == AE_MAX_TYPES );
+
+const char *EconQuality_GetQualityString( EEconItemQuality eQuality )
+{
+ // This is a runtime check and not an assert because we could theoretically bounce the GC with new
+ // qualities while the client is running.
+ if ( eQuality >= 0 && eQuality < AE_MAX_TYPES )
+ return g_szQualityStrings[ eQuality ];
+
+ return NULL;
+}
+
+EEconItemQuality EconQuality_GetQualityFromString( const char* pszQuality )
+{
+ // Convert to lowercase
+ CUtlString strLoweredInput( pszQuality );
+ strLoweredInput.ToLower();
+
+ // Guaranteed with the compile time assert above that AE_MAX_TYPES is
+ // the size of the string qualities
+ for( int i = 0; i < AE_MAX_TYPES; ++i )
+ {
+ // Convert to lowercase
+ CUtlString strLoweredQuality( g_szQualityStrings[i] );
+ strLoweredQuality.ToLower();
+
+ if( !Q_stricmp( strLoweredInput.Get(), strLoweredQuality.Get() ) )
+ return EEconItemQuality(i);
+ }
+
+ return AE_UNDEFINED;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+const char *g_szQualityColorStrings[] =
+{
+ "QualityColorNormal",
+ "QualityColorrarity1",
+ "QualityColorrarity2",
+ "QualityColorVintage",
+ "QualityColorrarity3",
+ "QualityColorrarity4", // AE_UNUSUAL
+ "QualityColorUnique",
+ "QualityColorCommunity",
+ "QualityColorDeveloper",
+ "QualityColorSelfMade",
+ "QualityColorSelfMadeCustomized",
+ "QualityColorStrange",
+ "QualityColorCompleted",
+ "QualityColorHaunted", // AE_HAUNTED
+ "QualityColorCollectors", // AE_COLLECTORS
+ "QualityColorPaintkitWeapon", // AE_PAINTKITWEAPON
+
+ "ItemRarityDefault" , // AE_RARITY_DEFAULT,
+ "ItemRarityCommon" , // AE_RARITY_COMMON,
+ "ItemRarityUncommon" , // AE_RARITY_UNCOMMON,
+ "ItemRarityRare" , // AE_RARITY_RARE,
+ "ItemRarityMythical" , // AE_RARITY_MYTHICAL,
+ "ItemRarityLegendary" , // AE_RARITY_LEGENDARY,
+ "ItemRarityAncient" , // AE_RARITY_ANCIENT,
+};
+
+COMPILE_TIME_ASSERT( ARRAYSIZE( g_szQualityColorStrings ) == AE_MAX_TYPES );
+
+const char *EconQuality_GetColorString( EEconItemQuality eQuality )
+{
+ if ( eQuality >= 0 && eQuality < AE_MAX_TYPES )
+ return g_szQualityColorStrings[ eQuality ];
+
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+const char *g_szQualityLocalizationStrings[] =
+{
+ "#Normal",
+ "#rarity1", // Genuine
+ "#rarity2",
+ "#vintage",
+ "#rarity3", // Artisan
+ "#rarity4", // Unusual
+ "#unique",
+ "#community",
+ "#developer",
+ "#selfmade",
+ "#customized",
+ "#strange",
+ "#completed",
+ "#haunted",
+ "#collectors",
+ "#paintkitWeapon",
+
+ "#Rarity_Default",
+ "#Rarity_Common",
+ "#Rarity_Uncommon",
+ "#Rarity_Rare",
+ "#Rarity_Mythical",
+ "#Rarity_Legendary",
+ "#Rarity_Ancient"
+};
+
+COMPILE_TIME_ASSERT( ARRAYSIZE( g_szQualityLocalizationStrings ) == AE_MAX_TYPES );
+
+const char *EconQuality_GetLocalizationString( EEconItemQuality eQuality )
+{
+ if ( eQuality >= 0 && eQuality < AE_MAX_TYPES )
+ return g_szQualityLocalizationStrings[ eQuality ];
+
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Sort order for rarities
+// Small Numbers sort to front
+//-----------------------------------------------------------------------------
+int g_nRarityScores[] =
+{
+ 15, // AE_NORMAL,
+ 10, // AE_RARITY1, // Geniune
+ 102, // AE_RARITY2, // Customized (unused)
+ 11, // AE_VINTAGE,
+ 101, // AE_RARITY3, // Artisan (unused)
+ 0, // AE_UNUSUAL,
+ 14, // AE_UNIQUE,
+ -1, // AE_COMMUNITY,
+ -3, // AE_DEVELOPER,
+ -2, // AE_SELFMADE,
+ 100, // AE_CUSTOMIZED, // Unused
+ 9, // AE_STRANGE,
+ 103, // AE_COMPLETED, // Unused
+ 13, // AE_HAUNTED
+ 12, // AE_COLLECTORS
+ 8, // AE_PAINTKITWEAPON
+ 7, // AE_RARITY_DEFAULT,
+ 6, // AE_RARITY_COMMON,
+ 5, // AE_RARITY_UNCOMMON,
+ 4, // AE_RARITY_RARE,
+ 3, // AE_RARITY_MYTHICAL,
+ 2, // AE_RARITY_LEGENDARY,
+ 1, // AE_RARITY_ANCIENT,
+};
+COMPILE_TIME_ASSERT( ARRAYSIZE( g_nRarityScores ) == AE_MAX_TYPES );
+
+//-----------------------------------------------------------------------------
+int EconQuality_GetRarityScore( EEconItemQuality eQuality )
+{
+ if ( eQuality >= 0 && eQuality < AE_MAX_TYPES )
+ return g_nRarityScores[ eQuality ];
+
+ return 0;
+}
+
+//-----------------------------------------------------------------------------
+const char *g_pchWearAmountStrings[] =
+{
+ "#TFUI_InvTooltip_None",
+ "#TFUI_InvTooltip_FactoryNew",
+ "#TFUI_InvTooltip_MinimalWear",
+ "#TFUI_InvTooltip_FieldTested",
+ "#TFUI_InvTooltip_WellWorn",
+ "#TFUI_InvTooltip_BattleScared"
+};
+
+//-----------------------------------------------------------------------------
+int EconWear_ToIntCategory( float flWear )
+{
+ if ( flWear <= 0.2f )
+ {
+ return 1;
+ }
+ else if ( flWear <= 0.4f )
+ {
+ return 2;
+ }
+ else if ( flWear <= 0.6f )
+ {
+ return 3;
+ }
+ else if ( flWear <= 0.8f )
+ {
+ return 4;
+ }
+ else if ( flWear <= 1.0f )
+ {
+ return 5;
+ }
+
+ return 3; // default wear
+}
+
+// -------------------------------------------------------------------------
+// Shim to return a value for buckets. For strange we bucket all of them in to 1 non-instance data group
+int EconStrange_ToStrangeBucket( float value )
+{
+ return 0;
+}
+float EconStrange_FromStrangeBucket( int value )
+{
+ return 0;
+}
+
+//-----------------------------------------------------------------------------
+const char *GetWearLocalizationString( float flWear )
+{
+ int nIndex = EconWear_ToIntCategory( flWear );
+ return g_pchWearAmountStrings[ nIndex ];
+}
+//-----------------------------------------------------------------------------
+bool EconWear_IsValidValue( int nWear )
+{
+ return nWear > 0 && nWear <= 5;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CSchemaColorDefHandle g_AttribColorDefs[] =
+{
+ CSchemaColorDefHandle( "desc_level" ), // ATTRIB_COL_LEVEL
+ CSchemaColorDefHandle( "desc_attrib_neutral" ), // ATTRIB_COL_NEUTRAL
+ CSchemaColorDefHandle( "desc_attrib_positive" ), // ATTRIB_COL_POSITIVE
+ CSchemaColorDefHandle( "desc_attrib_negative" ), // ATTRIB_COL_NEGATIVE
+ CSchemaColorDefHandle( "desc_itemset_name" ), // ATTRIB_COL_ITEMSET_NAME
+ CSchemaColorDefHandle( "desc_itemset_equipped" ), // ATTRIB_COL_ITEMSET_EQUIPPED
+ CSchemaColorDefHandle( "desc_itemset_missing" ), // ATTRIB_COL_ITEMSET_MISSING
+ CSchemaColorDefHandle( "desc_bundle" ), // ATTRIB_COL_BUNDLE_ITEM
+ CSchemaColorDefHandle( "desc_limited_use" ), // ATTRIB_COL_LIMITED_USE
+ CSchemaColorDefHandle( "desc_flags" ), // ATTRIB_COL_component_flags
+ CSchemaColorDefHandle( "desc_limited_quantity" ), // ATTRIB_COL_LIMITED_QUANTITY
+
+ CSchemaColorDefHandle( "desc_default" ), // ATTRIB_COL_RARITY_DEFAULT
+ CSchemaColorDefHandle( "desc_common" ), // ATTRIB_COL_RARITY_COMMON
+ CSchemaColorDefHandle( "desc_uncommon" ), // ATTRIB_COL_RARITY_UNCOMMON
+ CSchemaColorDefHandle( "desc_rare" ), // ATTRIB_COL_RARITY_RARE
+ CSchemaColorDefHandle( "desc_mythical" ), // ATTRIB_COL_RARITY_MYTHICAL
+ CSchemaColorDefHandle( "desc_legendary" ), // ATTRIB_COL_RARITY_LEGENDARY
+ CSchemaColorDefHandle( "desc_ancient" ), // ATTRIB_COL_RARITY_ANCIENT
+ CSchemaColorDefHandle( "desc_immortal" ), // ATTRIB_COL_RARITY_IMMORTAL
+ CSchemaColorDefHandle( "desc_arcana" ), // ATTRIB_COL_RARITY_ARCANA
+
+ CSchemaColorDefHandle( "desc_strange" ), // ATTRIB_COL_STRANGE
+ CSchemaColorDefHandle( "desc_unusual" ), // ATTRIB_COL_UNUSUAL
+};
+
+COMPILE_TIME_ASSERT( ARRAYSIZE( g_AttribColorDefs ) == NUM_ATTRIB_COLORS );
+
+attrib_colors_t GetAttribColorIndexForName( const char* pszName )
+{
+ for ( int i = 0; i < NUM_ATTRIB_COLORS; ++i )
+ {
+ if ( !Q_strcmp( g_AttribColorDefs[i].GetName(), pszName ) )
+ return (attrib_colors_t)i;
+ }
+
+ return (attrib_colors_t)0;
+}
+
+const char *GetColorNameForAttribColor( attrib_colors_t unAttribColor )
+{
+ Assert( unAttribColor >= 0 );
+ Assert( unAttribColor < NUM_ATTRIB_COLORS );
+
+ return g_AttribColorDefs[unAttribColor]
+ ? g_AttribColorDefs[unAttribColor]->GetColorName()
+ : "ItemAttribNeutral";
+}
+
+const char *GetHexColorForAttribColor( attrib_colors_t unAttribColor )
+{
+ Assert( unAttribColor >= 0 );
+ Assert( unAttribColor < NUM_ATTRIB_COLORS );
+
+ return g_AttribColorDefs[unAttribColor]
+ ? g_AttribColorDefs[unAttribColor]->GetHexColor()
+ : "#ebe2ca";
+}
+
+entityquality_t GetItemQualityFromString( const char *sQuality )
+{
+ for ( int i = 0; i < AE_MAX_TYPES; i++ )
+ {
+ if ( !Q_strnicmp( sQuality, g_szQualityStrings[i], 16 ) )
+ return (entityquality_t)i;
+ }
+
+ return AE_NORMAL;
+}
+
+const char *g_szRecipeCategoryStrings[] =
+{
+ "crafting", // RECIPE_CATEGORY_CRAFTINGITEMS = 0,
+ "commonitem", // RECIPE_CATEGORY_COMMONITEMS,
+ "rareitem", // RECIPE_CATEGORY_RAREITEMS,
+ "special", // RECIPE_CATEGORY_SPECIAL,
+};
+
+COMPILE_TIME_ASSERT( ARRAYSIZE( g_szRecipeCategoryStrings ) == NUM_RECIPE_CATEGORIES );
+
+//-----------------------------------------------------------------------------
+// Item acquisition.
+//-----------------------------------------------------------------------------
+// Strings shown to the local player in the pickup dialog
+const char *g_pszItemPickupMethodStrings[] =
+{
+ "#NewItemMethod_Dropped", // UNACK_ITEM_DROPPED = 1,
+ "#NewItemMethod_Crafted", // UNACK_ITEM_CRAFTED,
+ "#NewItemMethod_Traded", // UNACK_ITEM_TRADED,
+ "#NewItemMethod_Purchased", // UNACK_ITEM_PURCHASED,
+ "#NewItemMethod_FoundInCrate", // UNACK_ITEM_FOUND_IN_CRATE,
+ "#NewItemMethod_Gifted", // UNACK_ITEM_GIFTED,
+ "#NewItemMethod_Support", // UNACK_ITEM_SUPPORT,
+ "#NewItemMethod_Promotion", // UNACK_ITEM_PROMOTION,
+ "#NewItemMethod_Earned", // UNACK_ITEM_EARNED,
+ "#NewItemMethod_Refunded", // UNACK_ITEM_REFUNDED,
+ "#NewItemMethod_GiftWrapped", // UNACK_ITEM_GIFT_WRAPPED,
+ "#NewItemMethod_Foreign", // UNACK_ITEM_FOREIGN,
+ "#NewItemMethod_CollectionReward", // UNACK_ITEM_COLLECTION_REWARD
+ "#NewItemMethod_PreviewItem", // UNACK_ITEM_PREVIEW_ITEM
+ "#NewItemMethod_PreviewItemPurchased", // UNACK_ITEM_PREVIEW_ITEM_PURCHASED
+ "#NewItemMethod_PeriodicScoreReward",// UNACK_ITEM_PERIODIC_SCORE_REWARD
+ "#NewItemMethod_MvMBadgeCompletionReward",// UNACK_ITEM_MVM_MISSION_COMPLETION_REWARD
+ "#NewItemMethod_MvMSquadSurplusReward",// UNACK_ITEM_MVM_SQUAD_SURPLUS_REWARD
+ "#NewItemMethod_HolidayGift", // UNACK_ITEM_FOUND_HOLIDAY_GIFT
+ "#NewItemMethod_CommunityMarketPurchase", // UNACK_ITEM_COMMUNITY_MARKET_PURCHASE
+ "#NewItemMethod_RecipeOutput", // UNACK_ITEM_RECIPE_OUTPUT
+ NULL, // UNACK_ITEM_HIDDEN_QUEST_ITEM
+ "#NewItemMethod_QuestOutput", // UNACK_ITEM_QUEST_OUTPUT
+ "#NewItemMethod_QuestLoaner", // UNACK_ITEM_QUEST_LOANER
+ "#NewItemMethod_TradeUp", // UNACK_ITEM_TRADE_UP
+ "#NewItemMethod_QuestMerasmissionOutput", //UNACK_ITEM_QUEST_MERASMISSION_OUTPUT
+ "#NewItemMethod_ViralCompetitiveBetaPassSpread", //UNACK_ITEM_VIRAL_COMPETITIVE_BETA_PASS_SPREAD
+#ifdef ENABLE_STORE_RENTAL_BACKEND
+ "#NewItemMethod_RentalPurchase", // UNACK_ITEM_RENTAL_PURCHASE
+#endif
+};
+
+COMPILE_TIME_ASSERT( ARRAYSIZE( g_pszItemPickupMethodStrings ) == (UNACK_NUM_METHODS - 1) ); // -1 because UNACK_ITEM_DROPPED is index 1, not 0
+
+const char *g_pszItemPickupMethodStringsUnloc[] =
+{
+ "dropped", // UNACK_ITEM_DROPPED = 1,
+ "crafted", // UNACK_ITEM_CRAFTED,
+ "traded", // UNACK_ITEM_TRADED,
+ "purchased", // UNACK_ITEM_PURCHASED,
+ "found_in_crate", // UNACK_ITEM_FOUND_IN_CRATE,
+ "gifted", // UNACK_ITEM_GIFTED,
+ "support", // UNACK_ITEM_SUPPORT,
+ "promotion", // UNACK_ITEM_PROMOTION,
+ "earned", // UNACK_ITEM_EARNED,
+ "refunded", // UNACK_ITEM_REFUNDED,
+ "gift_wrapped", // UNACK_ITEM_GIFT_WRAPPED
+ "foreign", // UNACK_ITEM_FOREIGN
+ "collection_reward",// UNACK_ITEM_COLLECTION_REWARD
+ "preview_item", // UNACK_ITEM_PREVIEW_ITEM
+ "preview_item_purchased", // UNACK_ITEM_PREVIEW_ITEM_PURCHASED
+ "periodic_score_reward", // UNACK_ITEM_PERIODIC_SCORE_REWARD
+ "mvm_badge_completion_reward", // UNACK_ITEM_MVM_MISSION_COMPLETION_REWARD
+ "mvm_squad_surplus_reward", // UNACK_ITEM_MVM_SQUAD_SURPLUS_REWARD
+ "holiday_gift", // UNACK_ITEM_FOUND_HOLIDAY_GIFT
+ "market_purchase", // UNACK_ITEM_COMMUNITY_MARKET_PURCHASE
+ "recipe_output", // UNACK_ITEM_RECIPE_OUTPUT
+ "hidden_quest", // UNACK_ITEM_HIDDEN_QUEST_ITEM
+ "quest_output", // UNACK_ITEM_QUEST_OUTPUT
+ "trade_up", // UNACK_ITEM_TRADE_UP
+ "quest_output", // UNACK_ITEM_QUEST_MERASMISSION_OUTPUT
+ "viral_competitive_beta_pass", //UNACK_ITEM_VIRAL_COMPETITIVE_BETA_PASS_SPREAD
+#ifdef ENABLE_STORE_RENTAL_BACKEND
+ "rental_purchase", // UNACK_ITEM_RENTAL_PURCHASE
+#endif
+};
+
+COMPILE_TIME_ASSERT( ARRAYSIZE( g_pszItemPickupMethodStringsUnloc ) == (UNACK_NUM_METHODS - 1) );
+
+// Strings shown to other players in the chat dialog
+const char *g_pszItemFoundMethodStrings[] =
+{
+ "#Item_Found", // UNACK_ITEM_DROPPED = 1,
+ "#Item_Crafted", // UNACK_ITEM_CRAFTED,
+ "#Item_Traded", // UNACK_ITEM_TRADED,
+ NULL, // UNACK_ITEM_PURCHASED,
+ "#Item_FoundInCrate", // UNACK_ITEM_FOUND_IN_CRATE,
+ "#Item_Gifted", // UNACK_ITEM_GIFTED,
+ NULL, // UNACK_ITEM_SUPPORT,
+ NULL, // UNACK_ITEM_PROMOTION
+ "#Item_Earned", // UNACK_ITEM_EARNED
+ "#Item_Refunded", // UNACK_ITEM_REFUNDED
+ "#Item_GiftWrapped", // UNACK_ITEM_GIFT_WRAPPED
+ "#Item_Foreign", // UNACK_ITEM_FOREIGN
+ "#Item_CollectionReward", // UNACK_ITEM_COLLECTION_REWARD
+ "#Item_PreviewItem", // UNACK_ITEM_PREVIEW_ITEM
+ "#Item_PreviewItemPurchased",// UNACK_ITEM_PREVIEW_ITEM_PURCHASED
+ "#Item_PeriodicScoreReward",// UNACK_ITEM_PERIODIC_SCORE_REWARD
+ "#Item_MvMBadgeCompletionReward",// UNACK_ITEM_MVM_MISSION_COMPLETION_REWARD
+ "#Item_MvMSquadSurplusReward",// UNACK_ITEM_MVM_SQUAD_SURPLUS_REWARD
+ "#Item_HolidayGift", // UNACK_ITEM_FOUND_HOLIDAY_GIFT
+ NULL, // UNACK_ITEM_COMMUNITY_MARKET_PURCHASE
+ "#Item_RecipeOutput", // UNACK_ITEM_RECIPE_OUTPUT
+ NULL, // UNACK_ITEM_HIDDEN_QUEST_ITEM
+ "#Item_QuestOutput", // UNACK_ITEM_QUEST_OUTPUT
+ NULL, // UNACK_ITEM_QUEST_LOANER
+ "#Item_TradeUp", // UNACK_ITEM_TRADE_UP
+ "#Item_QuestMerasmissionOutput", // UNACK_ITEM_QUEST_MERASMISSION_OUTPUT
+ "#Item_ViralCompetitiveBetaPassSpread", //UNACK_ITEM_VIRAL_COMPETITIVE_BETA_PASS_SPREAD
+#ifdef ENABLE_STORE_RENTAL_BACKEND
+ NULL, // UNACK_ITEM_RENTAL_PURCHASE
+#endif
+};
+
+COMPILE_TIME_ASSERT( ARRAYSIZE( g_pszItemFoundMethodStrings ) == (UNACK_NUM_METHODS - 1) );
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+struct strange_attr_set_t
+{
+ strange_attr_set_t( const char *pScoreAttrName, const char *pTypeAttrName, const char *pRestrictionAttrName, const char *pRestrictionValueAttrName, bool bIsUserCustomizable )
+ : m_attrScore( pScoreAttrName )
+ , m_attrType( pTypeAttrName )
+ , m_attrRestriction( pRestrictionAttrName )
+ , m_attrRestrictionValue( pRestrictionValueAttrName )
+ , m_bIsUserCustomizable( bIsUserCustomizable )
+ {
+ //
+ }
+
+ CSchemaAttributeDefHandle m_attrScore;
+ CSchemaAttributeDefHandle m_attrType;
+ CSchemaAttributeDefHandle m_attrRestriction;
+ CSchemaAttributeDefHandle m_attrRestrictionValue;
+ bool m_bIsUserCustomizable;
+};
+
+strange_attr_set_t g_KillEaterAttr[] =
+{
+ strange_attr_set_t( "kill eater", "kill eater score type", "strange restriction type 1", "strange restriction value 1", false ),
+ strange_attr_set_t( "kill eater 2", "kill eater score type 2", "strange restriction type 2", "strange restriction value 2", false ),
+ strange_attr_set_t( "kill eater 3", "kill eater score type 3", "strange restriction type 3", "strange restriction value 3", false ),
+
+ // assumption: all of the user-customizable attributes will follow all of the schema-specified attributes
+ strange_attr_set_t( "kill eater user 1", "kill eater user score type 1", "strange restriction user type 1", "strange restriction user value 1", true ),
+ strange_attr_set_t( "kill eater user 2", "kill eater user score type 2", "strange restriction user type 2", "strange restriction user value 2", true ),
+ strange_attr_set_t( "kill eater user 3", "kill eater user score type 3", "strange restriction user type 3", "strange restriction user value 3", true ),
+};
+
+int GetKillEaterAttrCount()
+{
+#ifdef DBGFLAG_ASSERT
+ // Verify our commented assumption that all of the non-user-customizable attributes will be followed by
+ // all of the user-customizable attributes.
+ bool bInUserCustomizableBlock = false;
+
+ for ( int i = 0; i < ARRAYSIZE( g_KillEaterAttr ); i++ )
+ {
+ if ( bInUserCustomizableBlock )
+ {
+ AssertMsg( g_KillEaterAttr[i].m_bIsUserCustomizable, "Ordering assumption for g_KillEaterAttr violated! User-customizable attributes should all be at the end of the list!" );
+ }
+
+ bInUserCustomizableBlock |= g_KillEaterAttr[i].m_bIsUserCustomizable;
+ }
+#endif
+
+ return ARRAYSIZE( g_KillEaterAttr );
+}
+
+int GetKillEaterAttrCount_UserCustomizable()
+{
+ int iCount = 0;
+ for ( int i = 0; i < GetKillEaterAttrCount(); i++ )
+ {
+ if ( GetKillEaterAttr_IsUserCustomizable( i ) )
+ {
+ iCount++;
+ }
+ }
+
+ return iCount;
+}
+
+const CEconItemAttributeDefinition *GetKillEaterAttr_Score( int i )
+{
+ Assert( i >= 0 );
+ Assert( i < GetKillEaterAttrCount() );
+
+ const CEconItemAttributeDefinition *pAttrRes = g_KillEaterAttr[i].m_attrScore;
+ AssertMsg1( pAttrRes, "Missing Killeater attr score %s", g_KillEaterAttr[ i ].m_attrScore.GetName() );
+
+ return pAttrRes;
+}
+
+const CEconItemAttributeDefinition *GetKillEaterAttr_Type( int i )
+{
+ Assert( i >= 0 );
+ Assert( i < GetKillEaterAttrCount() );
+
+ const CEconItemAttributeDefinition *pAttrRes = g_KillEaterAttr[i].m_attrType;
+ AssertMsg1( pAttrRes, "Missing Killeater attr type %s", g_KillEaterAttr[ i ].m_attrType.GetName() );
+
+ return pAttrRes;
+}
+
+const CEconItemAttributeDefinition *GetKillEaterAttr_Restriction( int i )
+{
+ Assert( i >= 0 );
+ Assert( i < GetKillEaterAttrCount() );
+
+ const CEconItemAttributeDefinition *pAttrRes = g_KillEaterAttr[i].m_attrRestriction;
+ AssertMsg1( pAttrRes, "Missing Killeater attr restriction %s", g_KillEaterAttr[ i ].m_attrRestriction.GetName() );
+
+ return pAttrRes;
+}
+
+const CEconItemAttributeDefinition *GetKillEaterAttr_RestrictionValue( int i )
+{
+ Assert( i >= 0 );
+ Assert( i < GetKillEaterAttrCount() );
+
+ const CEconItemAttributeDefinition *pAttrRes = g_KillEaterAttr[i].m_attrRestrictionValue;
+ AssertMsg1( pAttrRes, "Missing Killeater attr restriction value %s", g_KillEaterAttr[ i ].m_attrRestrictionValue.GetName() );
+
+ return pAttrRes;
+}
+
+bool GetKillEaterAttr_IsUserCustomizable( int i )
+{
+ Assert( i >= 0 );
+ Assert( i < GetKillEaterAttrCount() );
+
+ return g_KillEaterAttr[i].m_bIsUserCustomizable;
+}
+
+
+bool GetKilleaterValueByEvent( const IEconItemInterface* pItem, const kill_eater_event_t& EEventType, uint32& value )
+{
+ for ( int i = 0; i < GetKillEaterAttrCount(); i++ )
+ {
+ const CEconItemAttributeDefinition *pAttribKillEater = GetKillEaterAttr_Score( i );
+ const CEconItemAttributeDefinition *pAttribKillEaterScoreType = GetKillEaterAttr_Type( i );
+
+ Assert( pAttribKillEater && pAttribKillEaterScoreType );
+ if ( !pAttribKillEater || !pAttribKillEaterScoreType )
+ return false;
+
+ // make sure this item even has a kill count attribute we're looking for
+ uint32 unKillEaterAttrValue;
+ if ( !pItem->FindAttribute( pAttribKillEater, &unKillEaterAttrValue ) )
+ continue;
+
+ uint32 unKillEaterScoreTypeAttrValue = kKillEaterEvent_PlayerKill;
+
+ float fKillEaterScoreTypeAttrValue;
+ if ( FindAttribute_UnsafeBitwiseCast<attrib_value_t>( pItem, pAttribKillEaterScoreType, &fKillEaterScoreTypeAttrValue ) )
+ {
+ unKillEaterScoreTypeAttrValue = (uint32)fKillEaterScoreTypeAttrValue;
+ }
+
+ // this isn't the attribute we're trying to find
+ if ( EEventType != (kill_eater_event_t)unKillEaterScoreTypeAttrValue )
+ continue;
+
+ value = unKillEaterAttrValue;
+ return true;
+ }
+
+ return false;
+}
+
+// Does this thing have kill eater
+bool BIsItemStrange( const IEconItemInterface *pItem )
+{
+ // Go over the attributes of the item, if it has any strange attributes the item is strange and don't apply
+ uint32 unKillEaterAttr;
+ for ( int i = 0; i < GetKillEaterAttrCount(); i++ )
+ {
+ if ( pItem->FindAttribute( GetKillEaterAttr_Score( i ), &unKillEaterAttr ) )
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Get a localization token that describes why an item is not usable
+// in the trade-up crafting. Returns NULL if no reason. Can pass in
+// another item to compare against, which causes extra consistency checks
+//-----------------------------------------------------------------------------
+const char* GetCollectionCraftingInvalidReason( const IEconItemInterface *pTestItem, const IEconItemInterface *pSourceItem )
+{
+ if ( !pTestItem )
+ {
+ return "#TF_CollectionCrafting_NoItem";
+ }
+
+ // Needs to have a collection
+ const CEconItemCollectionDefinition* pTestCollection = pTestItem->GetItemDefinition()->GetItemCollectionDefinition();
+ if ( !pTestCollection )
+ {
+ return "#TF_CollectionCrafting_NoCollection";
+ }
+
+ // Make sure this item is a part of the collection it claims to be in
+ {
+ item_definition_index_t nThisDefIndex = pTestItem->GetItemDefIndex();
+ bool bFound = false;
+ for( int i=0; i < pTestCollection->m_iItemDefs.Count() && !bFound; ++i )
+ {
+ bFound |= pTestCollection->m_iItemDefs[i] == nThisDefIndex;
+ }
+
+ if ( !bFound )
+ {
+ return "#TF_CollectionCrafting_NoCollection";
+ }
+ }
+
+ // Needs rarity
+ uint8 nRarity = pTestItem->GetItemDefinition()->GetRarity();
+ if( nRarity == k_unItemRarity_Any )
+ {
+ return "#TF_CollectionCrafting_NoRarity";
+ }
+
+ // Can't use items with rarity at the "top" of a collection (what would they craft into?)
+ if ( nRarity == pTestCollection->GetMaxRarity() )
+ {
+ return "#TF_CollectionCrafting_MaxRarity";
+ }
+
+ // No self mades or community items
+ uint32 eQuality = pTestItem->GetQuality();
+ if ( eQuality == AE_SELFMADE || eQuality == AE_COMMUNITY )
+ {
+ return "#TF_CollectionCrafting_NoUnusual";
+ }
+
+ // This is how we test for unusuals. Don't let unusuals be crafted
+ static CSchemaAttributeDefHandle pAttrDef_ParticleEffect( "attach particle effect" );
+ if ( pTestItem->FindAttribute( pAttrDef_ParticleEffect ) )
+ {
+ return "#TF_CollectionCrafting_NoUnusual";
+ }
+
+ static CSchemaAttributeDefHandle pAttrDef_TauntUnusualAttr( "on taunt attach particle index" );
+ if ( pTestItem->FindAttribute( pAttrDef_TauntUnusualAttr ) )
+ {
+ return "#TF_CollectionCrafting_NoUnusual";
+ }
+
+ // Not allowed to be crafted?
+ if ( !pTestItem->IsUsableInCrafting() )
+ {
+ return "#TF_CollectionCrafting_NotCraftable";
+ }
+
+ // If another item was passed in, we have a few consistency checks to make
+ if ( pSourceItem )
+ {
+ // Need to have the same rarity
+ if ( nRarity != pSourceItem->GetItemDefinition()->GetRarity() )
+ {
+ return "#TF_CollectionCrafting_MismatchRarity";
+ }
+
+ // Need to have the same strangeness
+ if ( BIsItemStrange( pSourceItem ) != BIsItemStrange( pTestItem ) )
+ {
+ return "#TF_CollectionCrafting_MismatchStrange";
+ }
+ }
+
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Get a localization token that describes why an item is not usable
+// in the Halloween Offering. Returns NULL if no reason. Can pass in
+// another item to compare against, which causes extra consistency checks
+//-----------------------------------------------------------------------------
+const char* GetHalloweenOfferingInvalidReason( const IEconItemInterface *pTestItem, const IEconItemInterface *pSourceItem )
+{
+ // Must either be a Cosmetic
+ // Taunt
+ // Allowable Tool (Strange part, Paint, name tag, killstreak). Not crates, keys
+ // Marketable Weapon ie Strange, Genuine, Vintage, paintkit
+
+ // Cannot be Unusual
+
+ if ( !pTestItem )
+ {
+ return "#TF_CollectionCrafting_NoItem";
+ }
+
+ // No self mades or community items
+ uint32 eQuality = pTestItem->GetQuality();
+ if ( eQuality == AE_SELFMADE || eQuality == AE_COMMUNITY )
+ {
+ return "#TF_CollectionCrafting_NoUnusual";
+ }
+
+ // This is how we test for unusuals. Don't let unusuals be crafted
+ static CSchemaAttributeDefHandle pAttrDef_ParticleEffect( "attach particle effect" );
+ if ( pTestItem->FindAttribute( pAttrDef_ParticleEffect ) )
+ {
+ return "#TF_CollectionCrafting_NoUnusual";
+ }
+
+ static CSchemaAttributeDefHandle pAttrDef_TauntUnusualAttr( "on taunt attach particle index" );
+ if ( pTestItem->FindAttribute( pAttrDef_TauntUnusualAttr ) )
+ {
+ return "#TF_CollectionCrafting_NoUnusual";
+ }
+
+ // Invalid Items
+ static CSchemaAttributeDefHandle pAttrDef_CannotTransmute( "cannot_transmute" );
+ if ( pTestItem->FindAttribute( pAttrDef_CannotTransmute ) )
+ {
+ return "#TF_HalloweenOffering_Invalid";
+ }
+
+ static CSchemaAttributeDefHandle pAttrDef_CannotDelete( "cannot delete" );
+ if ( pTestItem->FindAttribute( pAttrDef_CannotDelete ) )
+ {
+ return "#TF_HalloweenOffering_Invalid";
+ }
+
+ const CEconItemDefinition *pItemDef = pTestItem->GetItemDefinition();
+ if ( pItemDef == NULL )
+ {
+ return "#TF_CollectionCrafting_NoItem";
+ }
+
+ if ( pTestItem->IsTemporaryItem() )
+ {
+ return "#TF_CollectionCrafting_NoItem";
+ }
+
+ // If you are a taunt or a cosmetic you are allowed
+ if ( pTestItem->GetItemDefinition()->GetLoadoutSlot( 0 ) == LOADOUT_POSITION_MISC || pTestItem->GetItemDefinition()->GetLoadoutSlot( 0 ) == LOADOUT_POSITION_TAUNT )
+ {
+ // do not 'medal' equip region items
+ if ( pTestItem->GetItemDefinition()->GetEquipRegionMask() & GetItemSchema()->GetEquipRegionBitMaskByName( "medal" ) )
+ {
+ return "#TF_HalloweenOffering_Invalid";
+ }
+
+ return NULL;
+ }
+
+ // Do not allow Crates
+ if ( ( pItemDef->GetCapabilities() & ITEM_CAP_DECODABLE ) != 0 )
+ {
+ return "#TF_HalloweenOffering_Invalid";
+ }
+
+ // Cause of weird legacy items lets be explicit about what we allow
+ if ( pItemDef->IsTool() )
+ {
+ // ignore everything that is not a paint can tool
+ const IEconTool *pEconTool = pItemDef->GetEconTool();
+ if ( !pEconTool )
+ return "#TF_HalloweenOffering_Invalid";
+
+ const char *pToolType = pEconTool->GetTypeName();
+
+ if ( !V_strcmp( pToolType, "paint_can" ) )
+ return NULL;
+ else if ( !V_strcmp( pToolType, "strange_part" ) )
+ return NULL;
+ else if ( !V_strcmp( pToolType, "name" ) )
+ return NULL;
+ else if ( !V_strcmp( pToolType, "desc" ) )
+ return NULL;
+ else if ( !V_strcmp( pToolType, "killstreakifier" ) )
+ return NULL;
+ else if ( !V_strcmp( pToolType, "strangifier" ) )
+ return NULL;
+
+ // Not a tool we are allowing
+ return "#TF_HalloweenOffering_Invalid";
+ }
+
+ // Otherwise you must be a weapon or we won't allow
+ if ( pTestItem->GetItemDefinition()->GetLoadoutSlot( 0 ) == LOADOUT_POSITION_PRIMARY
+ || pTestItem->GetItemDefinition()->GetLoadoutSlot( 0 ) == LOADOUT_POSITION_SECONDARY
+ || pTestItem->GetItemDefinition()->GetLoadoutSlot( 0 ) == LOADOUT_POSITION_MELEE
+ || pTestItem->GetItemDefinition()->GetLoadoutSlot( 0 ) == LOADOUT_POSITION_BUILDING
+ || pTestItem->GetItemDefinition()->GetLoadoutSlot( 0 ) == LOADOUT_POSITION_PDA
+ || pTestItem->GetItemDefinition()->GetLoadoutSlot( 0 ) == LOADOUT_POSITION_PDA2
+ ) {
+ // Must be strange, genuine, vintage, haunted or paintkit (ie a marketable weapon)
+ eQuality = pTestItem->GetQuality();
+ if ( eQuality == AE_RARITY1
+ || eQuality == AE_VINTAGE
+ || eQuality == AE_HAUNTED
+ || eQuality == AE_COLLECTORS
+ || eQuality == AE_PAINTKITWEAPON
+ ) {
+ return NULL;
+ }
+
+ // Weapons with rarity are allowed
+ uint8 nRarity = pTestItem->GetItemDefinition()->GetRarity();
+ if ( nRarity != k_unItemRarity_Any )
+ {
+ return NULL;
+ }
+
+ // Strange items. Dont just check for strange quality, actually check for a strange attribute.
+ // See if we've got any strange attributes.
+ for ( int i = 0; i < GetKillEaterAttrCount(); i++ )
+ {
+ if ( pTestItem->FindAttribute( GetKillEaterAttr_Score( i ) ) )
+ {
+ return NULL;
+ }
+ }
+ }
+
+ return "#TF_HalloweenOffering_Invalid";
+}
+
+const char* GetCraftCommonStatClockInvalidReason( const class IEconItemInterface *pTestItem, const class IEconItemInterface *pSourceItem )
+{
+ if ( !pTestItem )
+ {
+ return "#TF_CollectionCrafting_NoItem";
+ }
+
+ // Not allowed to be crafted?
+ if ( !pTestItem->IsUsableInCrafting() )
+ {
+ return "#TF_CollectionCrafting_NotCraftable";
+ }
+
+ // No self mades or community items
+ uint32 eQuality = pTestItem->GetQuality();
+ if ( eQuality == AE_SELFMADE || eQuality == AE_COMMUNITY )
+ return "#TF_CollectionCrafting_NoUnusual";
+
+ // This is how we test for unusuals. Don't let unusuals be crafted
+ static CSchemaAttributeDefHandle pAttrDef_ParticleEffect( "attach particle effect" );
+ if ( pTestItem->FindAttribute( pAttrDef_ParticleEffect ) )
+ return "#TF_CollectionCrafting_NoUnusual";
+
+ static CSchemaAttributeDefHandle pAttrDef_TauntUnusualAttr( "on taunt attach particle index" );
+ if ( pTestItem->FindAttribute( pAttrDef_TauntUnusualAttr ) )
+ return "#TF_CollectionCrafting_NoUnusual";
+
+ const CEconItemDefinition *pItemDef = pTestItem->GetItemDefinition();
+ if ( pItemDef == NULL )
+ return "#TF_CollectionCrafting_NoItem";
+
+ if ( pTestItem->IsTemporaryItem() )
+ return "#TF_CollectionCrafting_NoItem";
+
+ // Strange items. Dont just check for strange quality, actually check for a strange attribute.
+ // See if we've got any strange attributes.
+ for ( int i = 0; i < GetKillEaterAttrCount(); i++ )
+ {
+ if ( pTestItem->FindAttribute( GetKillEaterAttr_Score( i ) ) )
+ {
+ return NULL;
+ }
+ }
+
+ // Needs Rarity
+ uint8 nRarity = pTestItem->GetItemDefinition()->GetRarity();
+ if ( nRarity != k_unItemRarity_Any && nRarity > 1 ) // do not allow default nor common rarity
+ {
+ return NULL;
+ }
+
+ return "#TF_MannCoTrade_ItemInvalid";
+}
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+enum { kMaxCardUpgradesPerItem = 2 };
+
+int GetMaxCardUpgradesPerItem()
+{
+ return kMaxCardUpgradesPerItem;
+}
+
+const CEconItemAttributeDefinition *GetCardUpgradeForIndex( const IEconItemInterface *pItem, int i )
+{
+ Assert( pItem );
+ Assert( i >= 0 );
+ Assert( i < kMaxCardUpgradesPerItem );
+
+ class CGetNthUserGeneratedAttributeIterator : public IEconItemUntypedAttributeIterator
+ {
+ public:
+ CGetNthUserGeneratedAttributeIterator( int iTargetIndex )
+ : m_iCount( iTargetIndex )
+ , m_pAttrDef( NULL )
+ {
+ }
+
+ virtual bool OnIterateAttributeValueUntyped( const CEconItemAttributeDefinition *pAttrDef ) OVERRIDE
+ {
+ if ( pAttrDef->GetUserGenerationType() != 0 && m_iCount-- == 0 )
+ {
+ m_pAttrDef = pAttrDef;
+ return false;
+ }
+
+ return true;
+ }
+
+ const CEconItemAttributeDefinition *GetAttrDef() const { return m_pAttrDef; }
+
+ private:
+ int m_iCount;
+ const CEconItemAttributeDefinition *m_pAttrDef;
+ };
+
+ CGetNthUserGeneratedAttributeIterator findNthAttrIterator( i );
+ pItem->IterateAttributes( &findNthAttrIterator );
+
+ return findNthAttrIterator.GetAttrDef();
+}
diff --git a/game/shared/econ/econ_item_constants.h b/game/shared/econ/econ_item_constants.h
new file mode 100644
index 0000000..80518c7
--- /dev/null
+++ b/game/shared/econ/econ_item_constants.h
@@ -0,0 +1,965 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+
+#ifndef ACTUAL_ECON_ITEM_CONSTANTS_H // ECON_ITEM_CONSTANTS_H is used by src/common/econ_item_view.h
+#define ACTUAL_ECON_ITEM_CONSTANTS_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+//=============================================================================
+// To avoid #include dependency chains, this file should
+// contain only constants that do not depend on other
+// header files.
+// This file is #included in cbase.h to allow schema compiles
+// to use these constants to ensure correlation between
+// code data structures and database entries
+//=============================================================================
+
+typedef uint32 item_price_t; // this is the type that is used to hold currency values for transactions! don't change this without changing the relevant code/databases/etc.
+typedef uint8 item_transaction_quantity_t;
+
+class CLocalizationProvider;
+
+enum { kLocalizedPriceSizeInChararacters = 64 };
+
+//-----------------------------------------------------------------------------
+// Econ Item testing
+//-----------------------------------------------------------------------------
+enum testitem_itemtypes_t
+{
+ TI_TYPE_UNKNOWN = -1,
+
+ TI_TYPE_WEAPON = 0,
+ TI_TYPE_HEADGEAR,
+ TI_TYPE_MISC1,
+ TI_TYPE_MISC2,
+
+ TI_TYPE_COUNT,
+};
+#define TESTITEM_DEFINITIONS_BEGIN_AT 40000
+
+//-----------------------------------------------------------------------------
+// Type IDs for economy classes. These are part of the client-GC protocol and
+// should not change if it can be helped
+//-----------------------------------------------------------------------------
+enum EEconTypeID
+{
+ k_EEconTypeItem =1,
+ k_EEconTypePlayerInfo =2,
+ k_EEconTypeClaimCode =3,
+ k_EEconTypeRecipe =5,
+ k_EEconTypeGameAccountClient =7,
+ k_EEconTypeGameAccount =8,
+ k_EEconTypeDuelSummary =19,
+ k_EEconTypeExperiment =20,
+ k_EEconTypeMapContribution =28,
+ k_EEconTypeGameServerAccount =29,
+ k_EEconTypeCoachRating =30,
+// k_EEconTypeEquipInstance =31, // DEPRECATED
+ k_EEconTypeSelectedItemPreset =35,
+ k_EEconTypeItemPresetInstance =36,
+ k_EEconTypeGameAccountForGameServers =37,
+ k_EEConTypeWarData =38,
+ k_EEConTypeLadderData =39,
+ k_EEConTypeMatchResultPlayerInfo =40,
+ k_EEconTypeXPSource =41,
+ k_EEconTypeNotification =42,
+};
+
+//-----------------------------------------------------------------------------
+// Actions for the ItemAudit table
+//-----------------------------------------------------------------------------
+// WARNING!!! Values stored in DB. Do not renumber!
+enum EItemAction
+{
+ k_EItemActionInvalid = -1,
+ k_EItemActionGSCreate = 0,
+ k_EItemActionUnpurchase = 1,
+ k_EItemActionDelete = 2,
+ k_EItemActionAwardAchievement = 3,
+ k_EItemActionBanned = 4,
+ k_EItemActionQuantityChanged = 5,
+ k_EItemActionRestored = 6,
+ k_EItemActionAwardTime = 7,
+ k_EItemActionManualCreate = 8,
+ k_EItemActionDrop = 9,
+ k_EItemActionPickUp = 10,
+ k_EItemActionCraftDestroy = 11,
+ k_EItemActionCraftCreate = 12,
+ k_EItemActionLimitExceeded = 13,
+ k_EItemActionPurchase = 14,
+ k_EItemActionNameChanged_Add = 15,
+ k_EItemActionUnlockCrate_Add = 16,
+ k_EItemActionPaintItem_Add = 17,
+ k_EItemActionAutoGrantItem = 18,
+ k_EItemActionCrossGameAchievement = 19,
+ k_EItemActionAddItemToSocket_Add = 20,
+ k_EItemActionAddSocketToItem_Add = 21,
+ k_EItemActionRemoveSocketItem_Add = 22,
+ k_EItemActionCustomizeItemTexture_Add = 23,
+ k_EItemActionItemTraded_Add = 24,
+ k_EItemActionUseItem = 25,
+ k_EItemActionAwardGift_Receiver = 26,
+ k_EItemActionNameChanged_Remove = 27,
+ k_EItemActionUnlockCrate_Remove = 28,
+ k_EItemActionPaintItem_Remove = 29,
+ k_EItemActionAddItemToSocket_Remove = 30,
+ k_EItemActionAddSocketToItem_Remove = 31,
+ k_EItemActionRemoveSocketItem_Remove = 32,
+ k_EItemActionCustomizeItemTexture_Remove = 33,
+ k_EItemActionItemTraded_Remove = 34,
+ k_EItemActionUnpackItemBundle = 35,
+ k_EItemActionCreateItemFromBundle = 36,
+ k_EItemActionAwardStorePromotionItem = 37,
+ k_EItemActionConvertItem = 38,
+ k_EItemActionEarnedItem = 39,
+ k_EItemActionAwardGift_Giver = 40,
+ k_EItemActionRefundedItem = 41,
+ k_EItemActionAwardThirdPartyPromo = 42,
+ k_EItemActionRemoveItemName_Remove = 43,
+ k_EItemActionRemoveItemName_Add = 44,
+ k_EItemActionRemoveItemPaint_Remove = 45,
+ k_EItemActionRemoveItemPaint_Add = 46,
+ k_EItemActionHalloweenDrop = 47,
+ k_EItemActionSteamWorkshopContributor = 48,
+ k_EItemActionManualOwnershipChange = 49, // when we have bad bugs that corrupt item data and have to fix up rows in the DB by hand
+ k_EItemActionSupportDelete = 50,
+ k_EItemActionSupportCreatedByUndo = 51,
+ k_EItemActionSupportDeletedByUndo = 52,
+ k_EItemActionSupportQuantityChangedByUndo = 53,
+ k_EItemActionSupportRename_Add = 54,
+ k_EItemActionSupportRename_Remove = 55,
+ k_EItemActionSupportDescribe_Add = 56,
+ k_EItemActionSupportDescribe_Remove = 57,
+
+ k_EItemActionStrangePartApply_Add = 58,
+ k_EItemActionStrangePartApply_Remove = 59,
+ k_EItemActionStrangeScoreReset_Add = 60,
+ k_EItemActionStrangeScoreReset_Remove = 61,
+ k_EItemActionStrangePartRemove_Add = 62,
+ k_EItemActionStrangePartRemove_Remove = 63,
+
+ k_EItemActionSupportStrangify_Add = 64,
+ k_EItemActionSupportStrangify_Remove = 65,
+
+ k_EItemActionUpgradeCardApply_Add = 66,
+ k_EItemActionUpgradeCardApply_Remove = 67,
+ k_EItemActionUpgradeCardRemove_Add = 68,
+ k_EItemActionUpgradeCardRemove_Remove = 69,
+
+ k_EItemActionStrangeRestrictionApply_Add = 70,
+ k_EItemActionStrangeRestrictionApply_Remove = 71,
+ k_EItemActionTransmogrify_Add = 72,
+ k_EItemActionTransmogrify_Remove = 73,
+ k_EItemActionHalloweenSpellPageAdd_Add = 74,
+ k_EItemActionHalloweenSpellPageAdd_Remove = 75,
+
+ k_EItemActionDev_ClientLootListRoll = 90,
+
+ k_EItemActionGiftWrap_Add = 100,
+ k_EItemActionGiftWrap_Remove = 101,
+ k_EItemActionGiftDelivery_Add = 102,
+ k_EItemActionGiftDelivery_Remove = 103,
+ k_EItemActionGiftUnwrap_Add = 104,
+ k_EItemActionGiftUnwrap_Remove = 105,
+ k_EItemActionPackageItem = 106,
+ k_EItemActionPackageItem_Revoked = 107,
+ k_EItemActionHandleMapToken = 108,
+ k_EItemActionCafeOrSchoolItem_Remove = 109,
+ k_EItemActionVACBanned_Remove = 110,
+ k_EItemActionUpgradeThirdPartyPromo = 111,
+ k_EItemActionExpired = 112,
+ k_EItemActionTradeRollback_Add = 113,
+ k_EItemActionTradeRollback_Remove = 114,
+ k_EItemActionCDKeyGrant = 115,
+ k_EItemActionCDKeyRevoke = 116,
+ k_EItemActionWeddingRing_Add = 117,
+ k_EItemActionWeddingRing_Remove = 118,
+ k_EItemActionWeddingRing_AddPartner = 119,
+ k_EItemActionEconSetUnowned = 120,
+ k_EItemActionEconSetOwned = 121,
+ k_EItemActionStrangifyItem_Add = 122,
+ k_EItemActionStrangifyItem_Remove = 123,
+ k_EItemActionConsumeItem_Consume_ToolRemove = 124,
+ k_EItemActionConsumeItem_Consume_ToolAdd = 125,
+ k_EItemActionConsumeItem_Consume_InputRemove = 126,
+ k_EItemActionConsumeItem_Complete_OutputAdd = 127,
+ k_EItemActionConsumeItem_Complete_ToolRemove = 128,
+ k_EItemActionItemEaterRecharge_Add = 129,
+ k_EItemActionItemEaterRecharge_Remove = 130,
+
+ k_EItemActionRemoveItemCraftIndex_Remove = 150,
+ k_EItemActionRemoveItemCraftIndex_Add = 151,
+ k_EItemActionRemoveItemMakersMark_Remove = 152, // early versions of this will be in the database as 150
+ k_EItemActionRemoveItemMakersMark_Add = 153, // early versions of this will be in the database as 151 because I am a terrible person
+
+ k_EItemActionCollectItem_CollectedItem = 154,
+ k_EItemActionCollectItem_UpdateCollection = 155,
+ k_EItemActionCollectItem_RemoveCollection = 156,
+ k_EItemActionCollectItem_RedeemCollectionReward = 157,
+
+ k_EItemActionPreviewItem_BeginPreviewPeriod = 158,
+ k_EItemActionPreviewItem_EndPreviewPeriodExpired = 159,
+ k_EItemActionPreviewItem_EndPreviewPeriodItemBought = 160,
+
+ k_EItemActionPeriodicScoreReward_Add = 170,
+ k_EItemActionPeriodicScoreReward_Remove = 171,
+
+ k_EItemActionMvM_ChallengeCompleted_RemoveTicket = 180, // we completed a challenge and consumed this ticket as the cost
+ k_EItemActionMvM_ChallengeCompleted_GrantBadge = 181, // we completed a challenge and granted the player a badge because they didn't have one
+ k_EItemActionMvM_ChallengeCompleted_UpdateBadgeStamps_Remove = 182, // we completed a challenge and we're crossing an entry off our badge checklist (this may also reset the badge back down to empty if this was the last line item)
+ k_EItemActionMvM_ChallengeCompleted_UpdateBadgeStamps_Add = 183, // (other half of the above)
+ k_EItemActionMvM_ChallengeCompleted_GrantMissionCompletionLoot = 184, // we completed a mission in MvM
+ k_EItemActionMvM_RemoveSquadSurplusVoucher = 185,
+ k_EItemActionMvM_AwardSquadSurplus_Receiver = 186,
+ k_EItemActionMvM_AwardSquadSurplus_Giver = 187,
+ k_EItemActionMvM_ChallengeCompleted_GrantTourCompletionLoot = 188, // we completed a full tour in MvM
+ k_EItemActionMvM_AwardHelpANoobBonus_Helper = 189,
+
+ k_EItemActionHalloween_UpdateMerasmusLootLevel_Add = 200, // set the level of the merasmus loot
+ k_EItemActionHalloween_UpdateMerasmusLootLevel_Remove = 201,
+
+ k_EItemActionRemoveItemKillStreak_Remove = 202,
+ k_EItemActionRemoveItemKillStreak_Add = 203,
+
+ k_EItemActionSupportAddOrModifyAttribute_Remove = 204,
+ k_EItemActionSupportAddOrModifyAttribute_Add = 205,
+
+ k_EItemActionSpyVsEngyWar_JoinedWar = 206,
+
+ k_EItemAction_UpdateDuckBadgeLevel_Add = 207,
+ k_EItemAction_UpdateDuckBadgeLevel_Remove = 208,
+
+ k_EItemAction_QuestDrop = 209,
+
+ k_EItemAction_OperationPass_Add = 210,
+
+ k_EItemActionMarket_Add = 211,
+ k_EItemActionMarket_Remove = 212,
+
+ k_EItemAction_QuestComplete_Reward = 213,
+ k_EItemAction_QuestComplete_Remove = 214,
+
+ k_EItemAction_QuestLoaner_Add = 215,
+ k_EItemActionStrangeCountTransfer_Add = 216,
+ k_EItemActionStrangeCountTransfer_Remove = 217,
+
+ k_EItemActionCraftCollectionUpgrade_Add = 218,
+ k_EItemActionCraftCollectionUpgrade_Remove = 219,
+
+ k_EItemActionCraftHalloweenOffering_Add = 220,
+ k_EItemActionCraftHalloweenOffering_Remove = 221,
+
+ k_EItemActionRemoveItemGiftedBy_Remove = 222,
+ k_EItemActionRemoveItemGiftedBy_Add = 223,
+
+ k_EItemActionAddParticleVerticalAttr_Remove = 224,
+ k_EItemActionAddParticleVerticalAttr_Add = 225,
+
+ k_EItemActionAddParticleUseHeadOriginAttr_Remove = 226,
+ k_EItemActionAddParticleUseHeadOriginAttr_Add = 227,
+
+ k_EItemActionRemoveItemDynamicAttr_Add = 228,
+ k_EItemActionRemoveItemDynamicAttr_Remove = 229,
+
+ k_EItemActionCraftStatClockTradeUp_Add = 230,
+ k_EItemActionCraftStatClockTradeUp_Remove = 231,
+
+ k_EItemActionViralCompetitiveBetaPass_Drop = 232,
+
+ k_EItemActionSupportDeleteAttribute_Remove = 233,
+ k_EItemActionSupportDeleteAttribute_Add = 234,
+
+ // Let's be consistent with the underscores please.
+ // k_EItemActionYourNewAction, not k_EItemAction_YourNewAction
+ // Yes, it matters. See PchLocalizedNameFromEItemAction for why.
+};
+extern const char *PchNameFromEItemAction( EItemAction eAction );
+extern const char *PchNameFromEItemActionUnsafe( EItemAction eAction );
+
+extern bool BIsActionCreative( EItemAction );
+extern bool BIsActionDestructive( EItemAction );
+
+enum EItemActionMissingBehavior { kEItemAction_FriendlyNameLookup_ReturnNULLIfMissing, kEItemAction_FriendlyNameLookup_ReturnDummyStringIfMissing };
+extern const char *PchFriendlyNameFromEItemAction( EItemAction eAction, EItemActionMissingBehavior eMissingBehavior );
+extern const char *PchLocalizedNameFromEItemAction( EItemAction eAction, CLocalizationProvider &localizationProvider );
+
+//-----------------------------------------------------------------------------
+// Purpose: Used to pass audit actions to asset servers for SetUnowned and
+// SetOwned methods.
+//-----------------------------------------------------------------------------
+enum EEconOwnershipAction
+{
+ k_EEconOwnershipAction_Invalid = 0,
+
+ k_EEconOwnershipAction_TradeBase = 100,
+ k_EEconOwnershipAction_TradeCommit = 101, // precommit and docommit step of a trade. Reference is trade ID
+ k_EEconOwnershipAction_TradeRollback = 102, // cancelcommit and rollbackcommit step of a trade. Reference is trade ID
+};
+
+// old
+enum eEconItemFlags_Deprecated
+{
+ kDeprecated_EconItemFlag_AchievementGrantedItem = 1 << 0,
+ kDeprecated_EconItemFlag_CannotTrade = 1 << 1,
+ kDeprecated_EconItemFlag_Purchased = 1 << 2,
+ kDeprecated_EconItemFlag_CannotBeUsedInCrafting = 1 << 3,
+ kDeprecated_EconItemFlag_Promotion = 1 << 4,
+};
+
+//-----------------------------------------------------------------------------
+// Periodic score events
+//-----------------------------------------------------------------------------
+enum eEconPeriodicScoreEvents
+{
+ kPeriodicScoreEvent_GiftsDistributed = 0,
+ kPeriodicScoreEvent_DuelsWon = 1,
+ kPeriodicScoreEvent_MapStampsPurchased = 2,
+};
+
+//-----------------------------------------------------------------------------
+// Flags for CEconItem
+//-----------------------------------------------------------------------------
+// WARNING!!! Values stored in DB. DO NOT CHANGE EXISTING VALUES. Add values to the end.
+enum eEconItemFlags
+{
+ kEconItemFlag_CannotTrade = 1 << 0,
+ kEconItemFlag_CannotBeUsedInCrafting = 1 << 1,
+ kEconItemFlag_CanBeTradedByFreeAccounts = 1 << 2,
+ kEconItemFlag_NonEconomy = 1 << 3, // used for items that are meant to not interact in the economy -- these can't be traded, gift-wrapped, crafted, etc.
+ kEconItemFlag_PurchasedAfterStoreCraftabilityChanges2012 = 1 << 4, // cosmetic items coming from the store are now usable in crafting; this flag is set on all items purchased from the store after this change was made
+
+#ifdef CLIENT_DLL
+#ifdef TF_CLIENT_DLL
+ kEconItemFlagClient_ForceBlueTeam = 1 << 5,
+#endif // TF_CLIENT_DLL
+ kEconItemFlagClient_StoreItem = 1 << 6,
+ kEconItemFlagClient_Preview = 1 << 7, // only set on the client; means "this item is being previewed"
+#endif // CLIENT_DLL
+
+ // combination of the above flags used in code
+ kEconItemFlags_CheckFlags_AllGCFlags = kEconItemFlag_CannotTrade | kEconItemFlag_CannotBeUsedInCrafting | kEconItemFlag_CanBeTradedByFreeAccounts | kEconItemFlag_NonEconomy | kEconItemFlag_PurchasedAfterStoreCraftabilityChanges2012,
+};
+
+//-----------------------------------------------------------------------------
+// Origin for an item for CEconItem
+//-----------------------------------------------------------------------------
+// WARNING!!! Values stored in DB. DO NOT CHANGE EXISTING VALUES. Add values to the end.
+enum eEconItemOrigin
+{
+ kEconItemOrigin_Invalid = -1, // should never be stored in the DB! used to indicate "invalid" for in-memory objects only
+
+ kEconItemOrigin_Drop = 0,
+ kEconItemOrigin_Achievement,
+ kEconItemOrigin_Purchased,
+ kEconItemOrigin_Traded,
+ kEconItemOrigin_Crafted,
+ kEconItemOrigin_StorePromotion,
+ kEconItemOrigin_Gifted,
+ kEconItemOrigin_SupportGranted,
+ kEconItemOrigin_FoundInCrate,
+ kEconItemOrigin_Earned,
+ kEconItemOrigin_ThirdPartyPromotion,
+ kEconItemOrigin_GiftWrapped,
+ kEconItemOrigin_HalloweenDrop,
+ kEconItemOrigin_PackageItem,
+ kEconItemOrigin_Foreign,
+ kEconItemOrigin_CDKey,
+ kEconItemOrigin_CollectionReward,
+ kEconItemOrigin_PreviewItem,
+ kEconItemOrigin_SteamWorkshopContribution,
+ kEconItemOrigin_PeriodicScoreReward,
+ kEconItemOrigin_MvMMissionCompletionReward, // includes loot from both "mission completed" and "tour completed" events
+ kEconItemOrigin_MvMSquadSurplusReward,
+ kEconItemOrigin_RecipeOutput,
+ kEconItemOrigin_QuestDrop,
+ kEconItemOrigin_QuestLoanerItem,
+ kEconItemOrigin_TradeUp,
+ kEconItemOrigin_ViralCompetitiveBetaPassSpread,
+
+ kEconItemOrigin_Max,
+};
+extern const char *PchNameFromeEconItemOrigin( eEconItemOrigin eOrigin );
+
+// The Steam backend representation of a unique item index
+typedef uint64 itemid_t;
+typedef uint16 item_definition_index_t;
+typedef uint16 attrib_definition_index_t;
+typedef uint32 attrib_value_t;
+typedef uint32 operation_definition_index_t;
+typedef uint8 war_definition_index_t;
+typedef uint8 war_side_t;
+
+// Misc typedefs for clarity.
+typedef uint32 equip_region_mask_t;
+typedef uint8 style_index_t;
+
+const uint64 INVALID_ITEM_ID = (itemid_t)-1;
+const item_definition_index_t INVALID_ITEM_DEF_INDEX = ((item_definition_index_t)-1);
+const attrib_definition_index_t INVALID_ATTRIB_DEF_INDEX= ((attrib_definition_index_t)-1);
+const war_definition_index_t INVALID_WAR_DEF_INDEX = ((war_definition_index_t)-1);
+const war_side_t INVALID_WAR_SIDE = ((war_side_t)-1);
+// Hard code the pyro/heavy stuff. Must be in sync with the schema.
+const war_definition_index_t PYRO_VS_HEAVY_WAR_DEF_INDEX= ((war_definition_index_t)0);
+const war_side_t PYRO_VS_HEAVY_WAR_SIDE_HEAVY = ((war_side_t)0);
+const war_side_t PYRO_VS_HEAVY_WAR_SIDE_PYRO = ((war_side_t)1);
+
+//-----------------------------------------------------------------------------
+
+// Standard/default backpack size
+#define DEFAULT_NUM_BACKPACK_SLOTS 300
+#define DEFAULT_NUM_BACKPACK_SLOTS_FREE_TRIAL_ACCOUNT 50
+#define MAX_NUM_BACKPACK_SLOTS 2000
+
+// Current item level range
+#define MIN_ITEM_LEVEL 0
+#define MAX_ITEM_LEVEL 100
+
+// Maximum number of attributes allowed on a single item
+#define MAX_ATTRIBUTES_PER_ITEM 15
+// The maximum length of a single attribute's description
+// divide by locchar_t, so we can ensure 192 bytes, whether that's 128 wchars on client or 256 utf-8 bytes on gc
+#define MAX_ATTRIBUTE_DESCRIPTION_LENGTH ( 256 / sizeof( locchar_t ) )
+
+// The maximum length of an item's name
+#define MAX_ITEM_NAME_LENGTH 128
+#define MAX_ITEM_DESC_LENGTH 256
+// The maximum length of an item description. (Extra +1 line is for the base item type line)
+#define MAX_ITEM_DESCRIPTION_LENGTH ((MAX_ATTRIBUTES_PER_ITEM+1) * MAX_ATTRIBUTE_DESCRIPTION_LENGTH)
+
+// For custom user-naming of econ items.
+#define MAX_ITEM_CUSTOM_NAME_LENGTH 40
+#define MAX_ITEM_CUSTOM_NAME_DATABASE_SIZE ((4 * MAX_ITEM_CUSTOM_NAME_LENGTH) + 1) // Ensures we can store MAX_ITEM_CUSTOM_NAME_LENGTH
+ // characters worth of obscure unicode characters in UTF8
+#define MAX_ITEM_CUSTOM_DESC_LENGTH 80
+#define MAX_ITEM_CUSTOM_DESC_DATABASE_SIZE ((4 * MAX_ITEM_CUSTOM_DESC_LENGTH) + 1)
+
+#define MAX_KILLCAM_MESSAGE_LENGTH 40
+#define MAX_KILLCAM_MESSAGE_DATABASE_SIZE ((4 * MAX_KILLCAM_MESSAGE_LENGTH) + 1)
+
+// max length in the DB for claim codes
+#define MAX_CLAIM_CODE_LENGTH 128
+
+// The item definition index reserved for the preview item
+#define PREVIEW_ITEM_DEFINITION_INDEX (item_definition_index_t)-1
+
+// The number of items to work on in a job before checking if a yield is necessary
+#define MAX_ITEMS_BEFORE_YIELD 50
+
+// TF team-color paints (moved from econ_item_view.h)
+#define RGB_INT_RED 12073019
+#define RGB_INT_BLUE 5801378
+
+// Custom textures
+const int k_nCustomImageSize = 128;
+const int k_nMaxCustomImageFileSize = k_nCustomImageSize*k_nCustomImageSize*4 + 4*1024; // Is this about right?
+
+//-----------------------------------------------------------------------------
+// Purpose: Quality types of items
+//-----------------------------------------------------------------------------
+typedef int32 entityquality_t;
+enum EEconItemQuality
+{
+ AE_UNDEFINED = -1,
+
+ AE_NORMAL = 0,
+ AE_RARITY1 = 1, // Genuine
+ AE_RARITY2 = 2, // Customized (unused)
+ AE_VINTAGE = 3, // Vintage has to stay at 3 for backwards compatibility
+ AE_RARITY3, // Artisan
+ AE_UNUSUAL, // Unusual
+ AE_UNIQUE,
+ AE_COMMUNITY,
+ AE_DEVELOPER,
+ AE_SELFMADE,
+ AE_CUSTOMIZED, // (unused)
+ AE_STRANGE,
+ AE_COMPLETED,
+ AE_HAUNTED,
+ AE_COLLECTORS,
+ AE_PAINTKITWEAPON,
+
+ AE_RARITY_DEFAULT,
+ AE_RARITY_COMMON,
+ AE_RARITY_UNCOMMON,
+ AE_RARITY_RARE,
+ AE_RARITY_MYTHICAL,
+ AE_RARITY_LEGENDARY,
+ AE_RARITY_ANCIENT,
+
+ AE_MAX_TYPES,
+ AE_DEPRECATED_UNIQUE = 3,
+};
+
+
+//-----------------------------------------------------------------------------
+// Purpose: colors used in the display of attributes
+//-----------------------------------------------------------------------------
+enum attrib_colors_t
+{
+ ATTRIB_COL_LEVEL = 0,
+ ATTRIB_COL_NEUTRAL,
+ ATTRIB_COL_POSITIVE,
+ ATTRIB_COL_NEGATIVE,
+ ATTRIB_COL_ITEMSET_NAME,
+ ATTRIB_COL_ITEMSET_EQUIPPED,
+ ATTRIB_COL_ITEMSET_MISSING,
+ ATTRIB_COL_BUNDLE_ITEM,
+ ATTRIB_COL_LIMITED_USE,
+ ATTRIB_COL_component_flags,
+ ATTRIB_COL_LIMITED_QUANTITY,
+
+ ATTRIB_COL_RARITY_DEFAULT,
+ ATTRIB_COL_RARITY_COMMON,
+ ATTRIB_COL_RARITY_UNCOMMON,
+ ATTRIB_COL_RARITY_RARE,
+ ATTRIB_COL_RARITY_MYTHICAL,
+ ATTRIB_COL_RARITY_LEGENDARY,
+ ATTRIB_COL_RARITY_ANCIENT,
+ ATTRIB_COL_RARITY_IMMORTAL,
+ ATTRIB_COL_RARITY_ARCANA,
+
+ ATTRIB_COL_STRANGE,
+ ATTRIB_COL_UNUSUAL,
+
+ NUM_ATTRIB_COLORS,
+};
+
+
+#define AE_USE_SCRIPT_VALUE 9999 // Can't be -1, due to unsigned ints used on the backend
+
+const char *EconQuality_GetQualityString( EEconItemQuality eQuality );
+const char *EconQuality_GetColorString( EEconItemQuality eQuality );
+const char *EconQuality_GetLocalizationString( EEconItemQuality eQuality );
+EEconItemQuality EconQuality_GetQualityFromString( const char* pszQuality );
+
+// Sort order for rarities
+int EconQuality_GetRarityScore( EEconItemQuality eQuality );
+
+extern attrib_colors_t GetAttribColorIndexForName( const char* pszName );
+extern const char *GetColorNameForAttribColor( attrib_colors_t unAttribColor );
+extern const char *GetHexColorForAttribColor( attrib_colors_t unAttribColor );
+
+// Utility function that'll get you an item quality from a string
+entityquality_t GetItemQualityFromString( const char *sQuality );
+
+enum recipecategories_t
+{
+ RECIPE_CATEGORY_CRAFTINGITEMS = 0,
+ RECIPE_CATEGORY_COMMONITEMS,
+ RECIPE_CATEGORY_RAREITEMS,
+ RECIPE_CATEGORY_SPECIAL,
+
+ NUM_RECIPE_CATEGORIES
+};
+extern const char *g_szRecipeCategoryStrings[NUM_RECIPE_CATEGORIES];
+
+//-----------------------------------------------------------------------------
+// Kill eater support.
+// Strange counters and strange parts
+//-----------------------------------------------------------------------------
+#if defined( TF_DLL ) || defined( TF_GC_DLL ) || defined( TF_CLIENT_DLL )
+enum kill_eater_event_t
+{
+ kKillEaterEvent_PlayerKill = 0, // default; items with no event type specified use this
+ kKillEaterEvent_UberActivated,
+ kKillEaterEvent_PlayerKillAssist,
+ kKillEaterEvent_PlayerKillsBySentry, // your sentry you built with this item killed someone
+ kKillEaterEvent_PeeVictims, // this game is great
+ kKillEaterEvent_BackstabAbsorbed, // you're a sniper and you got a spy to stab your Razorback
+ kKillEaterEvent_HeadsTaken, // this also tracks kills but with different flavor text
+ kKillEaterEvent_Humiliations, // fish kills!
+ kKillEaterEvent_GiftsGiven, // number of gifts given
+ kKillEaterEvent_DeathsFeigned, // number of deaths successfully feigned with the Dead Ringer
+ kKillEaterEvent_ScoutKill, // (part)
+ kKillEaterEvent_SniperKill, // (part)
+ kKillEaterEvent_SoldierKill, // (part)
+ kKillEaterEvent_DemomanKill, // (part)
+ kKillEaterEvent_HeavyKill, // (part)
+ kKillEaterEvent_PyroKill, // (part)
+ kKillEaterEvent_SpyKill, // (part)
+ kKillEaterEvent_EngineerKill, // (part)
+ kKillEaterEvent_MedicKill, // (part)
+ kKillEaterEvent_BuildingDestroyed, // (part)
+ kKillEaterEvent_ProjectileReflect, // (part)
+ kKillEaterEvent_HeadshotKill, // (part)
+ kKillEaterEvent_AirborneEnemyKill, // (part) (enemy is in the air when they die)
+ kKillEaterEvent_GibKill, // (part)
+ kKillEaterEvent_BuildingSapped, // a sapper was doing damage to this building while it was destroyed
+ kKillEaterEvent_PlayerTickle, // we used our comedy holiday gloves to force someone else to laugh
+ kKillEaterEvent_PlayerKillByBootStomp, // we killed a player by transferring our falling damage onto them
+ kKillEaterEvent_PlayerKillDuringFullMoon, // (part) we killed a player during the full moon holiday event (GC-updated)
+ kKillEaterEvent_PlayerKillStartDomination, // (part) we killed a player and this kill was enough to start our domination of them
+ kKillEaterEvent_PlayerKillAlreadyDominated, // (part) we killed a player with this weapon that we were already dominating
+ kKillEaterEvent_PlayerKillRevenge, // (part) we killed a player with this weapon when that player was dominating us
+ kKillEaterEvent_PlayerKillPosthumous, // (part) we killed a player after we were already dead (afterburn, stray rocket, etc.)
+ kKillEaterEvent_BurningAllyExtinguished, // (part) we used urine/milk/flamethrower/whatever to put out the fire on an ally that was burning
+ kKillEaterEvent_PlayerKillCritical, // (part) we killed a player with a shot that was a critical
+ kKillEaterEvent_PlayerKillWhileExplosiveJumping, // (part) we killed a player while we were rocket/sticky-jumping
+ kKillEaterEvent_PlayerKillFriend, // (part) we killed a player who is a Steam friend (GC-updated)
+ kKillEaterEvent_SapperDestroyed, // (part) we destroyed a sapper that was on a friendly building
+ kKillEaterEvent_InvisibleSpiesKilled, // (part) we killed an invisible spy
+ kKillEaterEvent_MedicsWithFullUberKilled, // (part) we killed a fully ubered medic
+ kKillEaterEvent_RobotsDestroyed, // (part) we killed a robot in MvM
+ kKillEaterEvent_MinibossRobotsDestroyed, // (part) we killed a miniboss robot in MvM
+ kKillEaterEvent_RobotsDestroyedAfterPenetration, // (part) we killed a robot with a shot that had already penetrated another robot
+ kKillEaterEvent_RobotHeadshotKills, // (part) like kKillEaterEvent_HeadshotKill, but only for robots
+ kKillEaterEvent_RobotsSlowed, // (part) we hit some robots with Jarate and now they're slow
+ kKillEaterEvent_KillWhileLowHealth, // (part) we killed someone while we had <10% max health
+ kKillEaterEvent_HalloweenKill, // (part) we killed someone during the Halloween holiday
+ kKillEaterEvent_HalloweenKillRobot, // (part) we killed a robot in MvM during the Halloween holiday
+ kKillEaterEvent_DefenderKill, // (part) we killed someone carrying the intel, pushing the cart, or capping a point
+ kKillEaterEvent_UnderwaterKill, // (part) we killed someone who was completely submerged
+ kKillEaterEvent_KillWhileUbercharged, // (part) we killed someone while we were invulnerable
+ kKillEaterEvent_FoodEaten, // We ate our food
+ kKillEaterEvent_BannersDeployed, // We deployed a banner buff
+ kKillEaterEvent_NEGATIVE_SniperShotsMissed, // (part) we shot our sniper rifle and didnt hit anything
+ kKillEaterEvent_NEGATIVE_UbersDropped, // (part) we died with a full ubercharge
+ kKillEaterEvent_NEGATIVE_DeathsWhileCarryingBuilding, // (part) we died while carrying a building
+ kKillEaterEvent_NEGATIVE_DeathsFromCratering, // (part) we died from cratering
+ kKillEaterEvent_NEGATIVE_DeathsFromEnvironment, // (part) we died from environmental damage
+ kKillEaterEvent_NEGATIVE_Deaths, // (part) we died :(
+ kKillEaterEvent_TimeCloaked, // Time we are cloaked
+ kKillEaterEvent_HealingProvided, // Health Provided to Allies
+ kKillEaterEvent_TeleportsProvided, // Teleports Provided to Allies
+ kKillEaterEvent_TanksDestroyed, // (part) we dealt the killing blow to a tank in MvM
+ kKillEaterEvent_LongDistanceKill, // (part) we dealt the killing blow (while alive) from far away
+ kKillEaterEvent_UniqueEvent__KilledAccountWithItem, // (part) (unique event) how many individual accounts have we killed?
+// kKillEaterEvent_UniqueEvent__PlayedWithAccountIDWhileWearingItem, // (part) (unique event) how many individual accounts have we played a round with?
+ kKillEaterEvent_PointsScored, // How many score points we've accumulated
+ kKillEaterEvent_DoubleDonks, // Double-Donks scored with the loose cannon
+ kKillEaterEvent_TeammatesWhipped, // Whipped Teammates with the Disciplinary Action
+ kKillEaterEvent_VictoryTimeKill, // Kills while in Victory / Bonus Time
+ kKillEaterEvent_RobotScoutKill, // (part)
+ kKillEaterEvent_RobotSniperKill, // (part) Not yet shipped
+ kKillEaterEvent_RobotSoldierKill, // (part) Not yet shipped
+ kKillEaterEvent_RobotDemomanKill, // (part) Not yet shipped
+ kKillEaterEvent_RobotHeavyKill, // (part) Not yet shipped
+ kKillEaterEvent_RobotPyroKill, // (part) Not yet shipped
+ kKillEaterEvent_RobotSpyKill, // (part)
+ kKillEaterEvent_RobotEngineerKill, // (part) Not yet shipped
+ kKillEaterEvent_RobotMedicKill, // (part) Not yet shipped
+ kKillEaterEvent_TauntKill, // Taunt Kills
+ kKillEaterEvent_PlayersWearingUnusualKill, // (part) we killed someone wearing an unusual hat (!)
+ kKillEaterEvent_BurningEnemyKill, // (part) we killed someone who was on fire up until they died
+ kKillEaterEvent_KillstreaksEnded, // (part) we killed someone who was on a killstreak
+ kKillEaterEvent_KillcamTaunts, // (cosmetic part) we appeared wearing this item in the killcam taunting
+ kKillEaterEvent_DamageDealt, // (part) we have dealt this much damage to people
+ kKillEaterEvent_FiresSurvived, // (cosmetic part) we were lit on fire wearing this item and then the fire went out and we were still alive
+ kKillEaterEvent_AllyHealingDone, // (part) we have healed this much (directly, so doesn't count Mad Milk, etc. because we lose the item pointer at some point); also ignores self heal (ie., Concheror buff, MvM upgrades)
+ kKillEaterEvent_PointBlankKills, // (part) we killed someone while standing right next to them
+ kKillEaterEvent_PlayerKillsByManualControlOfSentry, // Kills from wrangled a sentry
+ kKillEaterEvent_CosmeticKills, // (cosmetic part) kills
+ kKillEaterEvent_FullHealthKills, // (part) Kills while at fullhealth
+ kKillEaterEvent_TauntingPlayerKills, // (part) Taunting Player Kills
+ kKillEaterEvent_Halloween_OverworldKills,
+ kKillEaterEvent_Halloween_UnderworldKills,
+ kKillEaterEvent_Halloween_MinigamesWon,
+ kKillEaterEvent_NonCritKills, // part kills that are not crit or mini crit
+ kKillEaterEvent_PlayersHit, // part
+ kKillEaterEvent_CosmeticAssists, // Cosmetic part
+ kKillEaterEvent_CosmeticOperationContractsCompleted, // Operation Stat Tracker
+ kKillEaterEvent_CosmeticOperationKills, // Operation Stat Tracker
+ kKillEaterEvent_CosmeticOperationContractsPoints,
+ kKillEaterEvent_CosmeticOperationBonusPoints,
+ kKillEaterEvent_TauntsPerformed, // Strange Taunts
+ kKillEaterEvent_InvasionKills, // Kills During Invasion Event. Locked after Operation
+ kKillEaterEvent_InvasionKillsOnMap01,
+ kKillEaterEvent_InvasionKillsOnMap02,
+ kKillEaterEvent_InvasionKillsOnMap03,
+ kKillEaterEvent_InvasionKillsOnMap04,
+ kKillEaterEvent_HalloweenSouls, // Halloween
+ kKillEaterEvent_HalloweenContractsCompleted,
+ kKillEaterEvent_HalloweenOfferings,
+ kKillEaterEvent_PowerupBottlesUsed,
+
+ // NEW ENTRIES MUST BE ADDED AT THE BOTTOM
+};
+#else
+ // projects that actually want to implement kill-eater functionality will want to put their list somewhere around here,
+ // but unfortunately the base code relies on this specific definition being entry 0
+ static const uint32 kKillEaterEvent_PlayerKill = 0;
+#endif // defined( TF_DLL ) || defined( TF_GC_DLL ) || defined( TF_CLIENT_DLL )
+
+enum strange_event_restriction_t
+{
+ kStrangeEventRestriction_None = 0, // default -- unassigned, all events pass
+ kStrangeEventRestriction_VictimSteamAccount, // the victim must have a specific Steam ID
+#if defined( TF_DLL ) || defined( TF_GC_DLL ) || defined( TF_CLIENT_DLL )
+ kStrangeEventRestriction_Map, // must be playing on a certain map when the event takes place
+ kStrangeEventRestriction_Competitive, // must be playing in a competitive game
+#endif // defined( TF_DLL ) || defined( TF_GC_DLL ) || defined( TF_CLIENT_DLL )
+ kStrangeEventRestrictionCount
+};
+
+// Ugh -- these are shared between the GC and the client. Maybe #define is slightly better than
+// magic string literals?
+#define KILL_EATER_RANK_LEVEL_BLOCK_NAME "KillEaterRank"
+
+#ifdef TF_DLL
+ class CTFWeaponBase *GetKilleaterWeaponFromDamageInfo( const class CTakeDamageInfo *pInfo );
+ // A specific CEconEntity caused a kill eater event to happen. For example, a weapon might cause a
+ // player kill event so we want to update the stats for that specific weapon.
+ void EconEntity_OnOwnerKillEaterEvent( class CEconEntity *pEconEntity, class CTFPlayer *pOwner, class CTFPlayer *pVictim, kill_eater_event_t eEventType, int nIncrementValue = 1 );
+ void EconItemInterface_OnOwnerKillEaterEvent( class IEconItemInterface *pEconEntity, class CTFPlayer *pOwner, class CTFPlayer *pVictim, kill_eater_event_t eEventType, int nIncrementValue = 1 );
+ void EconEntity_OnOwnerKillEaterEventNoPartner( class CEconEntity *pEconEntity, class CTFPlayer *pOwner, kill_eater_event_t eEventType, int nIncrementValue = 1 );
+ void EconItemInterface_OnOwnerKillEaterEventNoPartner( class IEconItemInterface *pEconEntity, class CTFPlayer *pOwner, kill_eater_event_t eEventType, int nIncrementValue = 1 );
+
+ void HatAndMiscEconEntities_OnOwnerKillEaterEvent( class CTFPlayer *pOwner, class CTFPlayer *pVictim, kill_eater_event_t eEventType, int nIncrementValue = 1 );
+ void HatAndMiscEconEntities_OnOwnerKillEaterEventNoParter( class CTFPlayer *pOwner, kill_eater_event_t eEventType, int nIncrementValue = 1 );
+
+ void EconEntity_NonEquippedItemKillTracking_NoPartner( class CTFPlayer *pOwner, item_definition_index_t iDefIndex, kill_eater_event_t eEventType, int nIncrementValue = 1 );
+ void EconEntity_NonEquippedItemKillTracking_NoPartnerBatched( class CTFPlayer *pOwner, item_definition_index_t iDefIndex, kill_eater_event_t eEventType, int nIncrementValue = 1 );
+ // Batching system for frequent events (ie., damage dealing). The game server will flush all batches
+ // at specific time intervals and send up one composite message to avoid flooding the GC. Batched
+ // messages will only work correctly for types that support increment values. Because the game client
+ // and game server don't know which event types support increment values we can't do any checking
+ // before we send the message.
+ void EconEntity_OnOwnerKillEaterEvent_Batched( class CEconEntity *pEconEntity, class CTFPlayer *pOwner, class CTFPlayer *pVictim, kill_eater_event_t eEventType, int nIncrementValue = 1 );
+ void EconItemInterface_OnOwnerKillEaterEvent_Batched( class IEconItemInterface *pEconEntity, class CTFPlayer *pOwner, class CTFPlayer *pVictim, kill_eater_event_t eEventType, int nIncrementValue = 1 );
+ void KillEaterEvents_FlushBatches();
+#endif // TF_DLL
+
+int GetKillEaterAttrCount();
+int GetKillEaterAttrCount_UserCustomizable();
+const class CEconItemAttributeDefinition *GetKillEaterAttr_Score( int i );
+const class CEconItemAttributeDefinition *GetKillEaterAttr_Type( int i );
+const class CEconItemAttributeDefinition *GetKillEaterAttr_Restriction( int i );
+const class CEconItemAttributeDefinition *GetKillEaterAttr_RestrictionValue( int i );
+bool GetKillEaterAttr_IsUserCustomizable( int i );
+bool GetKilleaterValueByEvent( const class IEconItemInterface* pItem, const kill_eater_event_t& EEventType, uint32& value );
+bool BIsItemStrange( const class IEconItemInterface *pItem );
+
+const int COLLECTION_CRAFTING_ITEM_COUNT = 10;
+const char* GetCollectionCraftingInvalidReason( const class IEconItemInterface *pTestItem, const class IEconItemInterface *pSourceItem );
+
+const int HALLOWEEN_OFFERING_ITEM_COUNT = 3;
+const char* GetHalloweenOfferingInvalidReason( const class IEconItemInterface *pTestItem, const class IEconItemInterface *pSourceItem );
+
+const int CRAFT_COMMON_STATCLOCK_ITEM_COUNT = 5;
+const char* GetCraftCommonStatClockInvalidReason( const class IEconItemInterface *pTestItem, const class IEconItemInterface *pSourceItem );
+
+int GetMaxCardUpgradesPerItem();
+const class CEconItemAttributeDefinition *GetCardUpgradeForIndex( const class IEconItemInterface *pItem, int i );
+
+#define GUARANTEED_OUTPUT (1<<0)
+#define GUARANTEED_INPUT (1<<1)
+
+#define DYNAMIC_RECIPE_FLAG_IS_OUTPUT (1<<0)
+#define DYNAMIC_RECIPE_FLAG_IS_UNTRADABLE (1<<1)
+#define DYNAMIC_RECIPE_FLAG_PARAM_ITEM_DEF_SET (1<<2)
+#define DYNAMIC_RECIPE_FLAG_PARAM_QUALITY_SET (1<<3)
+#define DYNAMIC_RECIPE_FLAG_PARAM_ATTRIBUTE_SET_ALL (1<<4)
+#define DYNAMIC_RECIPE_FLAG_PARAM_ATTRIBUTE_SET_ANY (1<<5)
+
+#define k_ObjectiveTrackerFlag_OwnerClient (1<<0)
+#define k_ObjectiveTrackerFlag_Servers (1<<1)
+#define k_ObjectiveTrackerFlag_AllClients (1<<2)
+
+#define k_ObjectiveTrackerFlag_ClientAndServer ( k_ObjectiveTrackerFlag_OwnerClient | k_ObjectiveTrackerFlag_Servers )
+
+const float k_MaxElapsedQuestReportTime = 10.f;
+
+//===============================================================================================================
+// POSITION HANDLING
+//===============================================================================================================
+// TF Inventory Position cracking
+
+// REALLY OLD FORMAT (??):
+// We store a bag index in the highbits of the inventory position.
+// The lowbit stores the position of the item within the bag.
+//
+// LESS OLD FORMAT (up through July, 2011):
+// If Bit 31 is 0:
+// Bits 1-16 are the backpack position.
+// Bits 17-26 are a bool for whether the item is equipped in the matching class.
+// Otherwise, if Bit 31 is 1:
+// Item hasn't been acknowledged by the player yet.
+// Bits 1-16 are the method by the player found the item (see unacknowledged_item_inventory_positions_t)
+// Bit 32 is 1, to note the new format.
+//
+// CURRENT FORMAT:
+// If Bit 31 is 0:
+// Bits 1-16 are the backpack position.
+// Otherwise, if Bit 31 is 1:
+// Item hasn't been acknowledged by the player yet.
+// Bits 1-16 are the method by the player found the item (see unacknowledged_item_inventory_positions_t)
+// Equipped state is stored elsewhere.
+// This is the only format that should exist on clients.
+// Note (1/15/2013) For backwards compatibility, if the value is 0 item is considered unacknowledged too
+
+
+enum unacknowledged_item_inventory_positions_t
+{
+ UNACK_ITEM_UNKNOWN = 0,
+ UNACK_ITEM_DROPPED = 1,
+ UNACK_ITEM_CRAFTED,
+ UNACK_ITEM_TRADED,
+ UNACK_ITEM_PURCHASED,
+ UNACK_ITEM_FOUND_IN_CRATE,
+ UNACK_ITEM_GIFTED,
+ UNACK_ITEM_SUPPORT,
+ UNACK_ITEM_PROMOTION,
+ UNACK_ITEM_EARNED,
+ UNACK_ITEM_REFUNDED,
+ UNACK_ITEM_GIFT_WRAPPED,
+ UNACK_ITEM_FOREIGN,
+ UNACK_ITEM_COLLECTION_REWARD,
+ UNACK_ITEM_PREVIEW_ITEM,
+ UNACK_ITEM_PREVIEW_ITEM_PURCHASED,
+ UNACK_ITEM_PERIODIC_SCORE_REWARD,
+ UNACK_ITEM_MVM_MISSION_COMPLETION_REWARD,
+ UNACK_ITEM_MVM_SQUAD_SURPLUS_REWARD,
+ UNACK_ITEM_FOUND_HOLIDAY_GIFT,
+ UNACK_ITEM_COMMUNITY_MARKET_PURCHASE,
+ UNACK_ITEM_RECIPE_OUTPUT,
+ UNACK_ITEM_HIDDEN_QUEST_ITEM,
+ UNACK_ITEM_QUEST_OUTPUT,
+ UNACK_ITEM_QUEST_LOANER,
+ UNACK_ITEM_TRADE_UP,
+ UNACK_ITEM_QUEST_MERASMISSION_OUTPUT,
+ UNACK_ITEM_VIRAL_COMPETITIVE_BETA_PASS_SPREAD,
+#ifdef ENABLE_STORE_RENTAL_BACKEND
+ UNACK_ITEM_RENTAL_PURCHASE,
+#endif
+
+ UNACK_NUM_METHODS,
+};
+
+extern const char *g_pszItemPickupMethodStrings[UNACK_NUM_METHODS - 1]; // -1 because UNACK_ITEM_DROPPED is index 1, not 0
+extern const char *g_pszItemPickupMethodStringsUnloc[UNACK_NUM_METHODS - 1];
+extern const char *g_pszItemFoundMethodStrings[UNACK_NUM_METHODS - 1];
+
+enum
+{
+ kGCItemSort_NoSort = 0, // this won't do anything, but can be used as a safe "header" value
+
+ kGCItemSort_SortByName = 1,
+ kGCItemSort_SortByDefIndex = 2,
+ kGCItemSort_SortByRarity = 3,
+ kGCItemSort_SortByType = 4,
+ kGCItemSort_SortByDate = 5,
+
+ kGCItemSort_GameSpecificBase = 100,
+};
+
+// FIXME: these should be moved... somewhere; where?
+enum
+{
+ kTFGCItemSort_SortByClass = kGCItemSort_GameSpecificBase + 1,
+ kTFGCItemSort_SortBySlot = kGCItemSort_GameSpecificBase + 2,
+};
+
+enum
+{
+ kBackendPosition_Unacked = 1 << 30,
+ kBackendPosition_NewFormat = 1 << 31,
+
+ kBackendPositionMask_Position = 0x0000ffff,
+ kBackendPositionMask_FormatFlags = (kBackendPosition_Unacked | kBackendPosition_NewFormat),
+};
+
+inline void SetBackpackPosition( uint32 *pPosition, uint32 iPackPosition )
+{
+ (*pPosition) = iPackPosition;
+
+ // Remove the unack'd flag
+ (*pPosition) &= ~kBackendPosition_Unacked;
+}
+
+inline bool IsNewPositionFormat( uint32 iBackendPosition )
+{
+ return ( iBackendPosition & kBackendPosition_NewFormat ) != 0;
+}
+
+inline bool IsUnacknowledged( uint32 iBackendPosition )
+{
+ // For backwards compatibility, we consider position 0 as unacknowledged too
+ return (iBackendPosition == 0 || (iBackendPosition & kBackendPosition_Unacked) != 0);
+}
+
+inline int ExtractBackpackPositionFromBackend( uint32 iBackendPosition )
+{
+ if ( IsUnacknowledged( iBackendPosition) )
+ return 0;
+
+ return iBackendPosition & kBackendPositionMask_Position;
+}
+
+inline unacknowledged_item_inventory_positions_t GetUnacknowledgedReason( uint32 iBackendPosition )
+{
+ return (unacknowledged_item_inventory_positions_t)( iBackendPosition &= ~kBackendPositionMask_FormatFlags );
+}
+
+inline uint32 GetUnacknowledgedPositionFor( unacknowledged_item_inventory_positions_t iMethod )
+{
+ return (iMethod | kBackendPosition_Unacked | kBackendPosition_NewFormat);
+}
+
+//-----------------------------------------------------------------------------
+// Item Preview event IDs for logging.
+//-----------------------------------------------------------------------------
+enum EEconItemPreviewEventIDs
+{
+ k_EEconItemPreview_Start =1,
+ k_EEconItemPreview_Expired =2,
+ k_EEconItemPreview_ItemPurchased =3,
+};
+
+//-----------------------------------------------------------------------------
+// List of holidays. These are sorted by priority. Needs to match static IIsHolidayActive *s_HolidayChecks
+//-----------------------------------------------------------------------------
+enum EHoliday
+{
+ kHoliday_None = 0, // must stay at zero for backwards compatibility
+ kHoliday_TFBirthday,
+ kHoliday_Halloween,
+ kHoliday_Christmas,
+ kHoliday_CommunityUpdate,
+ kHoliday_EOTL,
+ kHoliday_Valentines,
+ kHoliday_MeetThePyro,
+ kHoliday_FullMoon,
+ kHoliday_HalloweenOrFullMoon,
+ kHoliday_HalloweenOrFullMoonOrValentines,
+ kHoliday_AprilFools,
+ kHolidayCount,
+};
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+enum ECartItemType
+{
+ kCartItem_Purchase, // a normal lifetime purchase (needs to stay as entry 0!)
+ kCartItem_TryOutUpgrade, // an upgrade from "try-it-out"
+ kCartItem_Rental_1Day,
+ kCartItem_Rental_3Day,
+ kCartItem_Rental_7Day,
+};
+
+inline bool IsRentalCartItemType( ECartItemType eCartType )
+{
+ return eCartType == kCartItem_Rental_1Day
+ || eCartType == kCartItem_Rental_3Day
+ || eCartType == kCartItem_Rental_7Day;
+}
+
+const uint8 k_unItemRarity_Any = 0xFF;
+const uint8 k_unItemQuality_Any = 0xFF;
+
+typedef int econ_tag_handle_t;
+
+enum EItemUntradability
+{
+ k_Untradability_Temporary = 1<<1,
+ k_Untradability_Permanent = 1<<2,
+};
+
+#define INVALID_ECON_TAG_HANDLE ((econ_tag_handle_t)-1)
+
+#endif // ACTUAL_ECON_ITEM_CONSTANTS_H
diff --git a/game/shared/econ/econ_item_description.cpp b/game/shared/econ/econ_item_description.cpp
new file mode 100644
index 0000000..b74a86f
--- /dev/null
+++ b/game/shared/econ/econ_item_description.cpp
@@ -0,0 +1,4146 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+
+#include "cbase.h"
+#include "econ_item_description.h"
+#include "econ_item_interface.h"
+#include "econ_item_tools.h"
+#include "econ_holidays.h"
+#include "econ_store.h"
+#include "tier1/ilocalize.h"
+#include "localization_provider.h"
+#include "rtime.h"
+#include "econ_dynamic_recipe.h"
+
+#ifdef GC
+ // the GC needs accountdetails to get persona names
+ #include "gcsdk/accountdetails.h"
+#else // !GC
+
+ #ifndef EXTERNALTESTS_DLL
+ #include "econ_item_inventory.h"
+ #endif
+
+ #ifdef CLIENT_DLL
+ #include "gc_clientsystem.h"
+ #include "client_community_market.h" // for Market data in tooltips
+ #include "econ_ui.h" // for money-value-to-display-string formatting
+ #include "store/store_panel.h" // for money-value-to-display-string formatting
+ #endif // CLIENT_DLL
+#endif // GC
+
+
+#ifdef PROJECT_TF
+ #include "tf_duel_summary.h"
+ #include "econ_contribution.h"
+ #include "tf_player_info.h"
+ #include "tf_wardata.h"
+
+ #ifdef TF_CLIENT_DLL
+ #include "tf_gamerules.h"
+ #include "tf_mapinfo.h"
+ #endif
+#endif
+
+#ifdef VPROF_ENABLED
+ static const char *g_pszEconDescriptionVprofGroup = _T("Econ Description");
+#endif
+
+extern const char *GetWearLocalizationString( float flWear );
+
+// --------------------------------------------------------------------------
+// Local Helper
+// --------------------------------------------------------------------------
+const size_t k_VerboseStringBufferSize = 128;
+static char *BuildVerboseStrings( char buf[k_VerboseStringBufferSize], bool bIsVerbose, const char *format, ... )
+{
+ if ( !bIsVerbose )
+ return NULL;
+
+ va_list argptr;
+ va_start( argptr, format );
+ Q_vsnprintf( buf, k_VerboseStringBufferSize, format, argptr );
+ va_end(argptr);
+
+ return buf;
+}
+
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+static bool IsStorePreviewItem( const IEconItemInterface *pEconItem )
+{
+ Assert( pEconItem );
+
+#ifdef CLIENT_DLL
+ return pEconItem->GetFlags() & kEconItemFlagClient_StoreItem;
+#else
+ return false;
+#endif
+}
+
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+void IEconItemDescription::YieldingFillOutEconItemDescription( IEconItemDescription *out_pDescription, CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem )
+{
+ VPROF_BUDGET( "IEconItemDescription::YieldingFillOutEconItemDescription()", g_pszEconDescriptionVprofGroup );
+
+ Assert( out_pDescription );
+ Assert( pLocalizationProvider );
+ Assert( pEconItem );
+
+ out_pDescription->YieldingCacheDescriptionData( pLocalizationProvider, pEconItem );
+ out_pDescription->GenerateDescriptionLines( pLocalizationProvider, pEconItem );
+}
+
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+const econ_item_description_line_t *IEconItemDescription::GetFirstLineWithMetaType( uint32 unMetaTypeSearchFlags ) const
+{
+ for ( unsigned int i = 0; i < GetLineCount(); i++ )
+ {
+ const econ_item_description_line_t& pLine = GetLine(i);
+ if ( (pLine.unMetaType & unMetaTypeSearchFlags) == unMetaTypeSearchFlags )
+ return &pLine;
+ }
+
+ return NULL;
+}
+
+#ifdef BUILD_ITEM_NAME_AND_DESC
+
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+CLocalizedStringArg<CLocalizedRTime32>::CLocalizedStringArg( const CLocalizedRTime32& cTimeIn )
+{
+#if TF_ANTI_IDLEBOT_VERIFICATION
+ // We expect the client and the GC to display dates and times differently in certain situations (ie.,
+ // they may disagree on whether to display in GMT). Rather than trying to find the specific cases where
+ // they might agree and let them through, we just early out and feed back the empty string for all
+ // date/time formatting when doing client verification.
+ if ( cTimeIn.m_pHashContext )
+ return;
+#endif
+
+ CRTime cTime( cTimeIn.m_unTime );
+
+ // The GC will always display time in GMT. "Local time" isn't a useful thing from a client's perspective
+ // when viewing an item in the Steam Community, etc.
+#ifdef GC_DLL
+ cTime.SetToGMT( true );
+#else
+ cTime.SetToGMT( cTimeIn.m_bForceGMTOnClient );
+#endif
+
+ const locchar_t *loc_LocalizationFormat = cTimeIn.m_pLocalizationProvider->Find( cTime.BIsGMT() ? "Econ_DateFormat_GMT" : "Econ_DateFormat" );
+
+ time_t tTime = cTime.GetRTime32();
+ struct tm tmStruct;
+ struct tm *ptm = cTime.BIsGMT() ? Plat_gmtime( &tTime, &tmStruct ) : Plat_localtime( &tTime, &tmStruct );
+
+ time_t tFinalTime = mktime( ptm );
+
+ char rgchDateBuf[ 128 ];
+ BGetLocalFormattedDate( tFinalTime, rgchDateBuf, sizeof( rgchDateBuf ) );
+
+ KeyValues *pKeyValues = new KeyValues( "DateTokens" );
+
+ pKeyValues->SetString( "day", &rgchDateBuf[0] );
+ pKeyValues->SetInt( "hour", cTime.GetHour() );
+ pKeyValues->SetString( "min", CFmtStr( "%02u", cTime.GetMinute() ).Access() );
+ pKeyValues->SetString( "sec", CFmtStr( "%02u", cTime.GetSecond() ).Access() );
+
+ m_Str = CConstructLocalizedString( loc_LocalizationFormat, pKeyValues );
+
+ pKeyValues->deleteThis();
+}
+
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+void CEconItemDescription::YieldingFillOutAccountPersonaName( const CLocalizationProvider *pLocalizationProvider, uint32 unAccountID )
+{
+ Assert( pLocalizationProvider );
+
+ // Never cache invalid accounts.
+ if ( unAccountID == 0 )
+ return;
+
+ // Make sure we have a cache entry for this account ID. If we're hashing, we won't fill
+ // this with real data to avoid discrepancies between the GC view of a persona name and the
+ // client view, both of which are cached differently. If we're not hashing, we'll do our best
+ // to find the current name. Either way, by the time this function ends we expect to have a
+ // value stored for this account ID.
+ CEconItemDescription::steam_account_persona_name_t& AccountPersona = vecPersonaNames[ vecPersonaNames.AddToTail() ];
+ AccountPersona.unAccountID = unAccountID;
+
+#if TF_ANTI_IDLEBOT_VERIFICATION
+ // Force persona names to match "verify" between the client and the GC for verification.
+ if ( m_pHashContext )
+ {
+ AccountPersona.loc_sPersonaName = LOCCHAR("verify");
+ }
+ else
+#endif // TF_ANTI_IDLEBOT_VERIFICATION
+ {
+ const char *utf8_PersonaName = NULL;
+
+#ifdef GC
+ CSteamID steamID( unAccountID, GCSDK::GGCHost()->GetUniverse(), k_EAccountTypeIndividual );
+ utf8_PersonaName = GGCGameBase()->YieldingGetPersonaName( steamID );
+#else // if defined( CLIENT_DLL )
+ utf8_PersonaName = InventoryManager()->PersonaName_Get( unAccountID );
+#endif
+
+#if defined( CLIENT_DLL )
+ m_bUnknownPlayer = Q_strncmp( utf8_PersonaName, "[unknown]", ARRAYSIZE( "[unknown]" ) ) == 0;
+#endif
+
+ // We should have filled this in with something by now, even if that something is "we couldn't
+ // find useful information".
+ Assert( utf8_PersonaName );
+
+ // Convert our UTF8 to whatever we're using for localized display, and done.
+ pLocalizationProvider->ConvertUTF8ToLocchar( utf8_PersonaName, &AccountPersona.loc_sPersonaName );
+ }
+
+ Assert( !AccountPersona.loc_sPersonaName.IsEmpty() );
+}
+
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+const locchar_t *CEconItemDescription::FindAccountPersonaName( uint32 unAccountID ) const
+{
+ FOR_EACH_VEC( vecPersonaNames, i )
+ {
+ if ( vecPersonaNames[i].unAccountID == unAccountID )
+ return vecPersonaNames[i].loc_sPersonaName.Get();
+ }
+
+ // FIXME: add localization token
+ return LOCCHAR("Unknown User");
+}
+
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+void CEconItemDescription::YieldingFillOutAccountTypeCache( uint32 unAccountID, int nClassID )
+{
+ if( !unAccountID )
+ return;
+
+#ifdef GC_DLL
+ CEconSharedObjectCache *pSOCache = GGCEcon()->YieldingFindOrLoadEconSOCache( CSteamID( unAccountID, GCSDK::GGCHost()->GetUniverse(), k_EAccountTypeIndividual ) );
+#else // if defined( CLIENT_DLL )
+ EUniverse eUniverse = GetUniverse();
+
+ if ( eUniverse == k_EUniverseInvalid )
+ return;
+
+ GCSDK::CGCClientSharedObjectCache *pSOCache = GCClientSystem()->GetSOCache( CSteamID( unAccountID, eUniverse, k_EAccountTypeIndividual ) );
+#endif
+ if ( !pSOCache )
+ return;
+
+ GCSDK::CSharedObjectTypeCache *pTypeCache = pSOCache->FindTypeCache( nClassID );
+ if ( !pTypeCache )
+ return;
+
+ CEconItemDescription::steam_account_type_cache_t& AccountTypeCache = vecTypeCaches[ vecTypeCaches.AddToTail() ];
+
+ AccountTypeCache.unAccountID = unAccountID;
+ AccountTypeCache.nClassID = nClassID;
+ AccountTypeCache.pTypeCache = pTypeCache;
+}
+
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+GCSDK::CSharedObjectTypeCache *CEconItemDescription::FindAccountTypeCache( uint32 unAccountID, int nClassID ) const
+{
+ FOR_EACH_VEC( vecTypeCaches, i )
+ {
+ if ( vecTypeCaches[i].unAccountID == unAccountID &&
+ vecTypeCaches[i].nClassID == nClassID )
+ {
+ return vecTypeCaches[i].pTypeCache;
+ }
+ }
+
+ return NULL;
+}
+
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+template < typename T >
+const T *CEconItemDescription::FindAccountTypeCacheSingleton( uint32 unAccountID, int nClassID ) const
+{
+ GCSDK::CSharedObjectTypeCache *pSOTypeCache = FindAccountTypeCache( unAccountID, nClassID );
+ if ( !pSOTypeCache )
+ return NULL;
+
+ if ( pSOTypeCache->GetCount() != 1 )
+ return NULL;
+
+ return dynamic_cast<T *>( pSOTypeCache->GetObject( 0 ) );
+}
+
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+void CEconItemDescription::YieldingCacheDescriptionData( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem )
+{
+ VPROF_BUDGET( "CEconItemDescription::YieldingCacheDescriptionData()", g_pszEconDescriptionVprofGroup );
+
+ vecPersonaNames.Purge();
+ vecTypeCaches.Purge();
+
+ // For each attribute that is set to display as an account ID, load the persona name for that account
+ // ID in advance so that we don't yield somewhere crazy down below.
+
+ // Walk our attribute list and accumulate IDs.
+ CSteamAccountIDAttributeCollector AccountIDCollector;
+ pEconItem->IterateAttributes( &AccountIDCollector );
+ const CUtlVector<uint32>& vecSteamAccountIDs = AccountIDCollector.GetAccountIDs();
+
+ // Look up the persona names for each account referenced by an attribute directly.
+ FOR_EACH_VEC( vecSteamAccountIDs, i )
+ {
+ YieldingFillOutAccountPersonaName( pLocalizationProvider, vecSteamAccountIDs[i] );
+ }
+
+ // Look up the persona names for each account referencing an attribute indirectly (ie., just stuffed
+ // into 32 bits).
+ for ( int i = 0; i < GetKillEaterAttrCount(); i++ )
+ {
+ uint32 unRestrictionType;
+ if ( pEconItem->FindAttribute( GetKillEaterAttr_Restriction(i), &unRestrictionType ) &&
+ unRestrictionType == kStrangeEventRestriction_VictimSteamAccount )
+ {
+ uint32 unAccountID;
+ DbgVerify( pEconItem->FindAttribute( GetKillEaterAttr_RestrictionValue(i), &unAccountID ) );
+ YieldingFillOutAccountPersonaName( pLocalizationProvider, unAccountID );
+ }
+ }
+
+#ifdef PROJECT_TF
+ uint32 unAccountID = pEconItem->GetAccountID();
+
+ // Duel summary.
+ {
+ // We'll need to access other information about our duel stats later, but we also need to precache
+ // the account name of whoever our last kill was beforehand.
+ YieldingFillOutAccountTypeCache( unAccountID, CTFDuelSummary::k_nTypeID );
+
+ // In TF, we also store information about our previous duel target, stored way way down inside some
+ // other structures.
+ const CTFDuelSummary *pDuelSummary = FindAccountTypeCacheSingleton<CTFDuelSummary>( unAccountID, CTFDuelSummary::k_nTypeID );
+
+ if ( pDuelSummary )
+ {
+ YieldingFillOutAccountPersonaName( pLocalizationProvider, pDuelSummary->Obj().last_duel_account_id() );
+ }
+ }
+
+ // Map contributions.
+ YieldingFillOutAccountTypeCache( unAccountID, CTFMapContribution::k_nTypeID );
+
+ // New users helped.
+ YieldingFillOutAccountTypeCache( unAccountID, CTFPlayerInfo::k_nTypeID );
+
+ // War data
+ YieldingFillOutAccountTypeCache( unAccountID, CWarData::k_nTypeID );
+
+#ifdef CLIENT_DLL
+ // Duck LeaderBoards
+ {
+ static CSchemaAttributeDefHandle pAttrDef_DisplayDuckLeaderboard( "display duck leaderboard" );
+ if ( pEconItem->FindAttribute( pAttrDef_DisplayDuckLeaderboard ) )
+ {
+ CUtlVector< AccountID_t > accountIds;
+ Leaderboards_GetDuckLeaderboardSteamIDs( accountIds );
+
+ FOR_EACH_VEC( accountIds, i )
+ {
+ // Look up the persona names for each account referenced in the leaderboard
+ YieldingFillOutAccountPersonaName( pLocalizationProvider, accountIds[i] );
+ }
+ }
+ }
+#endif // CLIENT_DLL
+
+#if TF_ANTI_IDLEBOT_VERIFICATION
+ if ( m_pHashContext )
+ {
+ // Feed in the account IDs of each person we care about. We don't feed in the actual persona name
+ // string because this could theoretically differ between the GC and the client if one is out of sync.
+ FOR_EACH_VEC( vecPersonaNames, i )
+ {
+ char verboseStringBuf[ k_VerboseStringBufferSize ];
+ TFDescription_HashDataMunge( m_pHashContext, vecPersonaNames[i].unAccountID, m_bIsVerbose, BuildVerboseStrings( verboseStringBuf, m_bIsVerbose, "%d", vecPersonaNames[i].unAccountID ) );
+ }
+
+ // Are we in text mode or not? We'll use this to generate two different hashes to compare against.
+ unsigned int unRunningTextMode =
+#ifdef GC_DLL
+ m_bTextModeEnabled
+#else
+ *((bool *)g_pClientPurchaseInterface - 156)
+#endif
+ ? 0x73aaff8e
+ : 0x12800c0a;
+
+ char verboseStringBuf[ k_VerboseStringBufferSize ];
+ TFDescription_HashDataMunge( m_pHashContext, unRunningTextMode, m_bIsVerbose, BuildVerboseStrings( verboseStringBuf, m_bIsVerbose, "%d", unRunningTextMode ) );
+ }
+#endif // TF_ANTI_IBLEBOT_VERIFICATION
+
+#endif // PROJECT_TF
+}
+
+void CEconItemDescription::GenerateDescriptionLines( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem )
+{
+ VPROF_BUDGET( "CEconItemDescription::GenerateDescriptionLines()", g_pszEconDescriptionVprofGroup );
+
+ Assert( pLocalizationProvider );
+ Assert( pEconItem );
+
+ m_vecDescLines.Purge();
+
+ Generate_ItemName( pLocalizationProvider, pEconItem );
+ Generate_ItemRarityDesc( pLocalizationProvider, pEconItem );
+ Generate_ItemLevelDesc( pLocalizationProvider, pEconItem );
+ //Generate_WearAmountDesc( pLocalizationProvider, pEconItem );
+#if defined( STAGING_ONLY ) && defined( CLIENT_DLL )
+ Generate_DebugInformation( pLocalizationProvider, pEconItem );
+#endif
+
+ // If we decide that for performance reasons some descriptions only want the name/description
+ // information and not all the details, this is the block to skip over.
+ {
+ Generate_CraftTag( pLocalizationProvider, pEconItem );
+ Generate_StyleDesc( pLocalizationProvider, pEconItem );
+ Generate_Painted( pLocalizationProvider, pEconItem );
+
+ Generate_HolidayRestriction( pLocalizationProvider, pEconItem );
+#ifdef PROJECT_TF
+ Generate_SaxxyAwardDesc( pLocalizationProvider, pEconItem );
+#endif // PROJECT_TF
+ Generate_VisibleAttributes( pLocalizationProvider, pEconItem );
+
+ Generate_QualityDesc( pLocalizationProvider, pEconItem );
+ Generate_ItemDesc( pLocalizationProvider, pEconItem );
+ Generate_Bundle( pLocalizationProvider, pEconItem );
+ Generate_GiftedBy( pLocalizationProvider, pEconItem );
+#ifdef PROJECT_TF
+ Generate_DuelingMedal( pLocalizationProvider, pEconItem );
+ Generate_MapContributor( pLocalizationProvider, pEconItem );
+ Generate_FriendlyHat( pLocalizationProvider, pEconItem );
+ Generate_SquadSurplusClaimedBy( pLocalizationProvider, pEconItem );
+ Generate_MvmChallenges( pLocalizationProvider, pEconItem );
+ Generate_DynamicRecipe( pLocalizationProvider, pEconItem );
+ Generate_Leaderboard( pLocalizationProvider, pEconItem );
+#endif // PROJECT_TF
+ Generate_XifierToolTargetItem( pLocalizationProvider, pEconItem );
+ Generate_LootListDesc( pLocalizationProvider, pEconItem );
+ Generate_EventDetail( pLocalizationProvider, pEconItem );
+ Generate_ItemSetDesc( pLocalizationProvider, pEconItem );
+ Generate_CollectionDesc( pLocalizationProvider, pEconItem );
+ Generate_ExpirationDesc( pLocalizationProvider, pEconItem );
+ Generate_DropPeriodDesc( pLocalizationProvider, pEconItem );
+
+ Generate_MarketInformation( pLocalizationProvider, pEconItem );
+ Generate_DirectX8Warning( pLocalizationProvider, pEconItem );
+ }
+
+ // Certain information (tradeability, etc.) used to only get displayed if we were the owning player, or
+ // if we were looking at an unowned item (ie., a store preview) and want to show what it will look like
+ // when it *is* owned. Unfortunately this led to problems where you wouldn't know if the item you were
+ // about to be traded (currently not owned by you) would be craftable, etc.
+ Generate_FlagsAttributes( pLocalizationProvider, pEconItem );
+}
+
+// --------------------------------------------------------------------------
+// Purpose: Code to build up the item display name, including any relevant quality
+// strings, custom renaming, craft numbers, and anything else we decide
+// to throw at it.
+// --------------------------------------------------------------------------
+
+/*static*/ uint32 GetScoreTypeForKillEaterAttr( const IEconItemInterface *pEconItem, const CEconItemAttributeDefinition *pAttribDef )
+{
+ // What sort of event are we tracking? If we don't have an attribute at all we're one of the
+ // old kill-eater weapons that didn't specify what it was tracking.
+ uint32 unKillEaterEventType = 0;
+
+ // This will overwrite our default 0 value if we have a value set but leave it if not.
+ {
+ float fKillEaterEventType;
+ if ( FindAttribute_UnsafeBitwiseCast<attrib_value_t>( pEconItem, pAttribDef, &fKillEaterEventType ) )
+ {
+ unKillEaterEventType = fKillEaterEventType;
+ }
+ }
+
+ return unKillEaterEventType;
+}
+
+// The item backend may add craft numbers well past what we want to display in the game. This
+// function determines whether a given number should be visible rather than always showing
+// whatever the GC shows.
+bool ShouldDisplayCraftCounterValue( int iValue )
+{
+ return iValue > 0 && iValue <= 100;
+}
+
+// This function will return the localized string (ie., "Face-Melting") for a specific item based
+// on the score it has accumulated.
+#if defined( CLIENT_DLL ) && defined( STAGING_ONLY )
+ ConVar staging_force_strange_score_selector_value( "staging_force_strange_score_selector_value", "-1" );
+#endif // defined( CLIENT_DLL ) && defined( STAGING_ONLY )
+
+class CStrangeRankLocalizationGenerator
+{
+public:
+ CStrangeRankLocalizationGenerator( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem, bool bHashContextOff );
+
+ bool IsValid() const { return m_bValid; }
+
+ const locchar_t *GetRankLocalized() const { Assert( m_bValid ); return m_loc_Rank; }
+ const locchar_t *GetRankSecondaryLocalized() const { Assert( m_bValid ); return m_loc_SecondaryRank; }
+
+ uint32 GetStrangeType() const { Assert( m_bValid ); return m_unType; }
+ uint32 GetStrangeScore() const { Assert( m_bValid ); return m_unScore; }
+ uint32 GetUsedStrangeSlot() const { Assert( m_bValid ); return m_unUsedStrangeSlot; }
+
+private:
+ bool m_bValid;
+
+ const locchar_t *m_loc_Rank;
+ const locchar_t *m_loc_SecondaryRank;
+
+ uint32 m_unType;
+ uint32 m_unScore;
+ uint32 m_unUsedStrangeSlot;
+};
+
+CStrangeRankLocalizationGenerator::CStrangeRankLocalizationGenerator( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem, bool bHashContextOff )
+ : m_bValid( false )
+ , m_loc_Rank( NULL )
+ , m_loc_SecondaryRank( NULL )
+ , m_unType( kKillEaterEvent_PlayerKill )
+ , m_unScore( 0 )
+ , m_unUsedStrangeSlot( 0 )
+{
+ Assert( pLocalizationProvider );
+ Assert( pEconItem );
+
+ static CSchemaAttributeDefHandle pAttrDef_StrangeScoreSelector( "strange score selector" );
+
+ // Do we have a strange score selector attribute? If so, the value of this attribute will tell us which strange
+ // attribute we're actually going to use to generate a name. Leaving this value as 0 will fall back to the
+ // default behavior of looking at the base "kill eater" attribute.
+ if ( pEconItem->FindAttribute( pAttrDef_StrangeScoreSelector, &m_unUsedStrangeSlot ) )
+ {
+ // Make sure the value we pulled from the database is within range.
+ m_unUsedStrangeSlot = MIN( m_unUsedStrangeSlot, static_cast<uint32>( GetKillEaterAttrCount() ) );
+ }
+
+#if defined( CLIENT_DLL ) && defined( STAGING_ONLY )
+ if ( staging_force_strange_score_selector_value.GetInt() > 0 )
+ {
+ m_unUsedStrangeSlot = staging_force_strange_score_selector_value.GetInt();
+ }
+#endif // defined( CLIENT_DLL ) && defined( STAGING_ONLY )
+
+ // Use the strange prefix if the weapon has one.
+ if ( !pEconItem->FindAttribute( GetKillEaterAttr_Score( m_unUsedStrangeSlot ), &m_unScore ) )
+ return;
+
+ // What type of event are we tracking and how does it describe itself?
+ m_unType = GetScoreTypeForKillEaterAttr( pEconItem, GetKillEaterAttr_Type( m_unUsedStrangeSlot ) );
+
+ const char *pszLevelingDataName = GetItemSchema()->GetKillEaterScoreTypeLevelingDataName( m_unType );
+ if ( !pszLevelingDataName )
+ {
+ pszLevelingDataName = KILL_EATER_RANK_LEVEL_BLOCK_NAME;
+ }
+
+ uint32 uUsedScore = m_unScore;
+#ifdef TF_ANTI_IDLEBOT_VERIFICATION
+ // TF2 Anti-Idle hack. It totally needs to be fixed
+ if ( !bHashContextOff )
+ {
+ uUsedScore = 0;
+ }
+#endif //TF_ANTI_IDLEBOT_VERIFICATION
+
+ // For TF - Strange Scores reset on Trade, sharing that information is actually misleading so we'll always display base strange name
+#ifdef TF_GC_DLL
+ uUsedScore = 0;
+#endif // TF_GC_DLL
+
+ const CItemLevelingDefinition *pLevelDef = GetItemSchema()->GetItemLevelForScore( pszLevelingDataName, uUsedScore );
+ if ( !pLevelDef )
+ return;
+
+ // Primary rank established!
+ m_loc_Rank = pLocalizationProvider->Find( pLevelDef->GetNameLocalizationKey() );
+ m_bValid = true;
+
+ // Does this score slot have a restriction that adds additional text somewhere in the localization token?
+ uint32 unFilterType;
+ uint32 unFilterValue;
+ if ( pEconItem->FindAttribute( GetKillEaterAttr_Restriction( m_unUsedStrangeSlot ), &unFilterType ) &&
+ pEconItem->FindAttribute( GetKillEaterAttr_RestrictionValue( m_unUsedStrangeSlot ), &unFilterValue ) )
+ {
+ // Game-specific code doesn't belong here. "We're shipping soon" hack fun!
+#ifdef PROJECT_TF
+ if ( unFilterType == kStrangeEventRestriction_Map )
+ {
+ const MapDef_t *pMap = GetItemSchema()->GetMasterMapDefByIndex( unFilterValue );
+ if ( pMap && pMap->pszStrangePrefixLocKey )
+ {
+ m_loc_SecondaryRank = pLocalizationProvider->Find( pMap->pszStrangePrefixLocKey );
+ }
+ }
+ else if (unFilterType == kStrangeEventRestriction_Competitive)
+ {
+ m_loc_SecondaryRank = pLocalizationProvider->Find( "TF_StrangeFilter_Prefix_Competitive" );
+ }
+#endif // PROJECT_TF
+ }
+}
+
+// ---------------------------------------------------------------------------------------------------------------------------
+void Econ_SetNameAsPaintkit( locchar_t( &out_pItemName )[MAX_ITEM_NAME_LENGTH], const CLocalizationProvider *pLocalizationProvider, CEconItemPaintKitDefinition *pPaintKit )
+{
+ if ( !pPaintKit )
+ return;
+
+ // Generate Paint kitted name
+ // IE Purple Rain Sniper Rifle
+ locchar_t tempName[MAX_ITEM_NAME_LENGTH];
+ loc_scpy_safe( tempName, out_pItemName );
+#ifndef GC
+ //bool bAppendWeapon = wcsstr( pPaintKitStr, out_pItemName ) == NULL;
+ g_pVGuiLocalize->ConstructString_safe( out_pItemName,
+ LOCCHAR( "%s1" ),
+ 1,
+ pLocalizationProvider->Find( pPaintKit->GetLocalizeName() ) );
+#else
+ // GC doesn't have g_pVGuiLocalize so we construct the painted gun string like this
+ locchar_t *pPaintKitStr = pLocalizationProvider->Find( pPaintKit->GetLocalizeName() );
+ loc_scpy_safe( out_pItemName, pPaintKitStr ? pPaintKitStr : LOCCHAR( "" ) );
+#endif
+}
+
+// ---------------------------------------------------------------------------------------------------------------------------
+void Econ_ConcatPaintKitName( locchar_t( &out_pItemName )[MAX_ITEM_NAME_LENGTH], const CLocalizationProvider *pLocalizationProvider, CEconItemPaintKitDefinition *pPaintKit )
+{
+ if ( !pPaintKit )
+ return;
+
+ // Check to see if the paintkit localized name already has the weapon name, if so do not add (Weapon)
+ locchar_t *pPaintKitStr = pLocalizationProvider->FindSafe( pPaintKit->GetLocalizeName() );
+
+ locchar_t tempName[MAX_ITEM_NAME_LENGTH];
+ loc_scpy_safe( tempName, out_pItemName );
+
+#ifndef GC
+ bool bAppendWeapon = wcsstr( pPaintKitStr, out_pItemName ) == NULL;
+ // Generate Paint kitted name
+ // IE Purple Rain Sniper Rifle
+
+ if ( bAppendWeapon )
+ {
+ g_pVGuiLocalize->ConstructString_safe( out_pItemName,
+ LOCCHAR("%s1 (%s2)"),
+ 2,
+ pPaintKitStr,
+ tempName );
+ }
+ else
+ {
+ g_pVGuiLocalize->ConstructString_safe( out_pItemName,
+ LOCCHAR("%s1"),
+ 1,
+ pPaintKitStr
+ );
+ }
+#else
+ // GC doesn't have g_pVGuiLocalize so we construct the painted gun string like this
+ bool bAppendWeapon = V_strstr( pPaintKitStr, out_pItemName ) == NULL;
+ // Generate Paint kitted name
+ // IE Purple Rain Sniper Rifle
+ loc_scpy_safe( out_pItemName, pPaintKitStr ? pPaintKitStr : LOCCHAR( "" ) );
+ if ( bAppendWeapon )
+ {
+ loc_scat_safe( out_pItemName, LOCCHAR( " " ) );
+ loc_scat_safe( out_pItemName, tempName );
+ }
+#endif
+}
+
+// ---------------------------------------------------------------------------------------------------------------------------
+void Econ_ConcatPaintKitWear( locchar_t( &out_pItemName )[MAX_ITEM_NAME_LENGTH], const CLocalizationProvider *pLocalizationProvider, float flWear )
+{
+ if ( flWear <= 0.0 )
+ return;
+
+#ifndef GC
+ locchar_t tempName[MAX_ITEM_NAME_LENGTH];
+ loc_scpy_safe( tempName, out_pItemName );
+
+ g_pVGuiLocalize->ConstructString_safe( out_pItemName,
+ LOCCHAR( "%s1 (%s2)" ),
+ 2,
+ tempName,
+ pLocalizationProvider->Find( GetWearLocalizationString( flWear ) )
+ );
+#else
+ // GC doesn't have g_pVGuiLocalize so we construct the painted gun string like this
+ locchar_t *pWearStr = pLocalizationProvider->Find( GetWearLocalizationString( flWear ) );
+ loc_scat_safe( out_pItemName, LOCCHAR( " (" ) );
+ loc_scat_safe( out_pItemName, pWearStr ? pWearStr : LOCCHAR( "" ) );
+ loc_scat_safe( out_pItemName, LOCCHAR( ")" ) );
+#endif
+}
+// ---------------------------------------------------------------------------------------------------------------------------
+static bool GetLocalizedBaseItemName( locchar_t (&szItemName)[MAX_ITEM_NAME_LENGTH], const CLocalizationProvider *pLocalizationProvider, const CEconItemDefinition *pEconItemDefinition )
+{
+ if ( pEconItemDefinition->GetItemBaseName() )
+ {
+ const locchar_t *pLocalizedItemName = pLocalizationProvider->Find( pEconItemDefinition->GetItemBaseName() );
+ if ( !pLocalizedItemName || !pLocalizedItemName[0] )
+ {
+ // Couldn't localize it, just use it raw
+ pLocalizationProvider->ConvertUTF8ToLocchar( pEconItemDefinition->GetItemBaseName(), szItemName, ARRAYSIZE( szItemName ) );
+ }
+ else
+ {
+ loc_scpy_safe( szItemName, pLocalizedItemName );
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
+// Given the item in pEconItem and the localization provider passed in, stuff the correct *localized*
+// string into out_pItemName.
+static void GenerateLocalizedFullItemName
+(
+ locchar_t (&out_pItemName)[MAX_ITEM_NAME_LENGTH],
+ const CLocalizationProvider *pLocalizationProvider,
+ const IEconItemInterface *pEconItem,
+ EGenerateLocalizedFullItemNameFlag_t eFlagsMask,
+ bool bHashContextOff
+)
+{
+ bool bUseProperName = bHashContextOff;
+ Assert( pLocalizationProvider );
+ Assert( pEconItem );
+
+ static const locchar_t *s_pUnknownItemName = LOCCHAR("Unknown Item");
+
+ const CEconItemDefinition *pEconItemDefinition = pEconItem->GetItemDefinition();
+ if ( !pEconItemDefinition )
+ {
+ out_pItemName[0] = (locchar_t)0;
+ return;
+ }
+
+ bool bIgnoreQualityAndWear = false;
+ bool bIgnoreNameWithPaintkit = false;
+ bool bHasCustomName = false;
+ if ( eFlagsMask == k_EGenerateLocalizedFullItemName_Default )
+ {
+ bIgnoreQualityAndWear = pEconItem->GetCustomPainkKitDefinition() ? true : false;
+ }
+
+ if ( eFlagsMask == k_EGenerateLocalizedFullItemName_WithPaintkitNoItem )
+ {
+ bIgnoreNameWithPaintkit = pEconItem->GetCustomPainkKitDefinition() ? true : false;
+ bIgnoreQualityAndWear = pEconItem->GetCustomPainkKitDefinition() ? true : false;
+ }
+
+ // Figure out which localization pattern we're using. By default we assume we're using the common "[Quality] [Item Name]"
+ // format, but if we're a unique item with an article we'll change this later on.
+ const char *pszLocalizationPattern = "ItemNameFormat";
+
+ // Start with the base name.
+ locchar_t szItemName[ MAX_ITEM_NAME_LENGTH ];
+
+ static CSchemaAttributeDefHandle pAttrDef_ItemNameTextOverride( "item name text override" );
+ CAttribute_String attrItemNameTextOverride;
+ // Check if we ahve a item name override
+ if ( pEconItem->FindAttribute( pAttrDef_ItemNameTextOverride, &attrItemNameTextOverride ) )
+ {
+ const locchar_t *pNameOverrideString = pLocalizationProvider->Find( attrItemNameTextOverride.value().c_str() );
+ if ( pNameOverrideString )
+ {
+ loc_scpy_safe( szItemName, pNameOverrideString );
+ bHasCustomName = true;
+ }
+ }
+ else if( !GetLocalizedBaseItemName( szItemName, pLocalizationProvider, pEconItemDefinition ) )
+ {
+ loc_scpy_safe( szItemName, s_pUnknownItemName );
+ }
+
+ // Check for killstreak attribute
+ enum { kKillStreakLength = 64, };
+ locchar_t szKillStreak[ kKillStreakLength ] = LOCCHAR("");
+ static CSchemaAttributeDefHandle pAttrDef_KillStreak( "killstreak tier" );
+ uint32 nKillStreakValue;
+
+ if ( pEconItem->FindAttribute( pAttrDef_KillStreak, &nKillStreakValue ) && !bIgnoreQualityAndWear )
+ {
+ nKillStreakValue = (float&)(nKillStreakValue);
+
+ // if you have the eyeballs you are automatically higher tier
+ static CSchemaAttributeDefHandle pAttrDef_KillStreakEyes( "killstreak effect" );
+ static CSchemaAttributeDefHandle pAttrDef_KillStreakSheen( "killstreak idleeffect" );
+ if ( pEconItem->FindAttribute( pAttrDef_KillStreakEyes ) )
+ {
+ nKillStreakValue = 3; // professional
+ }
+ else if ( pEconItem->FindAttribute( pAttrDef_KillStreakSheen ) )
+ {
+ nKillStreakValue = 2; // specialized
+ }
+
+ const locchar_t *pKillStreakLocalizedString = NULL;
+
+ // All tier-1 killstreaks have idle effect 1
+ if ( nKillStreakValue == 1 )
+ {
+ pKillStreakLocalizedString = pLocalizationProvider->Find( "ItemNameKillStreakv0" );
+ }
+ else if ( nKillStreakValue == 2 )
+ {
+ pKillStreakLocalizedString = pLocalizationProvider->Find( "ItemNameKillStreakv1" );
+ }
+ else // Tier-2's are things above 1
+ {
+ pKillStreakLocalizedString = pLocalizationProvider->Find( "ItemNameKillStreakv2" );
+ }
+
+ if ( pKillStreakLocalizedString )
+ {
+ loc_scpy_safe( szKillStreak, pKillStreakLocalizedString );
+ // If we're appending some sort of killstreak identifier, dont use the proper name
+ bUseProperName = false;
+ }
+ }
+
+ // Check to see if we have a quality text override attribute. We can get this when a temporary item
+ // comes in from a crafting recipe that needs to get its name generated, and wants to specify that it
+ // takes in any quality
+ static CSchemaAttributeDefHandle pAttrDef_QualityTextOverride( "quality text override" );
+ CAttribute_String attrQualityTextOverride;
+ pEconItem->FindAttribute( pAttrDef_QualityTextOverride, &attrQualityTextOverride );
+
+ // Generate our quality string.
+ enum { kQualityLength = 128, };
+ locchar_t szQuality[ kQualityLength ] = LOCCHAR("");
+
+ // Unique names may have a prefix or not, and so use a different format. (This is less to deal
+ // with the space after "The" and more to deal with foreign languages that want to display unique
+ // and non-unique items differently.
+ const uint8 unQuality = pEconItem->GetQuality();
+ if ( unQuality == AE_SELFMADE || ( !bIgnoreQualityAndWear ) )
+ {
+ // It's possible to get in here with a quality of -1 if we're dealing with an item view that has no
+ // associated item. In that case we're probably doing something like browsing the armory, and in any
+ // event don't have an item and so don't have a quality and so we just don't show a quality string.
+ // If we have a quality text override, use that.
+ const char *pszQualityLocalizationString = attrQualityTextOverride.has_value()
+ ? attrQualityTextOverride.value().c_str()
+ : EconQuality_GetLocalizationString( (EEconItemQuality)unQuality );
+
+ if ( unQuality > 0 && pszQualityLocalizationString && unQuality != AE_PAINTKITWEAPON )
+ {
+ // Unique items use proper names, but not if we have a quality text override
+ if ( unQuality == AE_UNIQUE && !attrQualityTextOverride.has_value() )
+ {
+ const locchar_t *pszArticleContent = NULL;
+ if ( bUseProperName && pEconItemDefinition->HasProperName() )
+ {
+ pszArticleContent = pLocalizationProvider->Find( "TF_Unique_Prepend_Proper" );
+ }
+
+ // If the language isn't supposed to have articles or we just haven't provided one yet, fall
+ // back to the empty string.
+ if ( !pszArticleContent )
+ {
+ pszArticleContent = LOCCHAR("");
+ }
+
+ loc_scpy_safe( szQuality, pszArticleContent );
+ }
+ // Any quality besides unique ignores "proper name" articles.
+ else
+ {
+ const locchar_t *pQualityLocalizedString = pLocalizationProvider->Find( pszQualityLocalizationString );
+ if ( pQualityLocalizedString )
+ {
+ loc_scpy_safe( szQuality, pQualityLocalizedString );
+ loc_scat_safe( szQuality, LOCCHAR(" ") );
+ }
+ }
+ }
+
+ {
+ static CSchemaAttributeDefHandle pAttrDef_HideStrangePrefix( "hide_strange_prefix" );
+ if ( !pAttrDef_HideStrangePrefix || !pEconItem->FindAttribute( pAttrDef_HideStrangePrefix ) )
+ {
+ //
+ CStrangeRankLocalizationGenerator RankGenerator( pLocalizationProvider, pEconItem, bHashContextOff );
+ if ( RankGenerator.IsValid() )
+ {
+ // If the quality of this item is special (not just strange) persist and append that value
+ // Otherwise the ranker will replace the 'strange' quality tag with a strange rank
+ if ( unQuality == AE_STRANGE )
+ {
+ loc_scpy_safe( szQuality,
+ CConstructLocalizedString( LOCCHAR("%s1%s2 "),
+ RankGenerator.GetRankLocalized(),
+ RankGenerator.GetRankSecondaryLocalized() ? RankGenerator.GetRankSecondaryLocalized() : LOCCHAR("") ) );
+ }
+ else // Strange Unusual Something
+ {
+ loc_scpy_safe( szQuality,
+ CConstructLocalizedString( LOCCHAR("%s1%s2 %s3"),
+ RankGenerator.GetRankLocalized(),
+ RankGenerator.GetRankSecondaryLocalized() ? RankGenerator.GetRankSecondaryLocalized() : LOCCHAR(""),
+ szQuality) );
+ }
+ }
+ }
+ }
+ }
+
+ static CSchemaAttributeDefHandle pAttrDef_IsAustralium( "is australium item" );
+ enum { kAustraliumLength = 64, };
+ locchar_t szAustraliumSkin[ kAustraliumLength ] = LOCCHAR("");
+ if ( pAttrDef_IsAustralium && pEconItem->FindAttribute( pAttrDef_IsAustralium ) )
+ {
+ const locchar_t *pAustraliumLocalizedString = pLocalizationProvider->Find( "ItemNameAustralium" );
+ if ( pAustraliumLocalizedString )
+ {
+ loc_scpy_safe( szAustraliumSkin, pAustraliumLocalizedString );
+ }
+ }
+
+ static CSchemaAttributeDefHandle pAttrDef_IsFestivized( "is_festivized" );
+ enum { kFestiveLength = 64, };
+ locchar_t szIsFestivized[kFestiveLength] = LOCCHAR( "" );
+ if ( pAttrDef_IsFestivized && pEconItem->FindAttribute( pAttrDef_IsFestivized ) )
+ {
+ // TODO : update ItemNameFestive in tf_english to Festivized later to differentiate Festive vs Festivized
+ const locchar_t *pFestivizedLocalizedString = pLocalizationProvider->Find( "ItemNameFestive" );
+ if ( pFestivizedLocalizedString )
+ {
+ loc_scpy_safe( szIsFestivized, pFestivizedLocalizedString );
+ }
+ }
+
+ const char* pszQualityFormat = ( !attrQualityTextOverride.has_value() && ( unQuality == AE_NORMAL || unQuality == AE_UNIQUE || unQuality == AE_PAINTKITWEAPON || bIgnoreQualityAndWear ) && unQuality != AE_SELFMADE )
+ ? "ItemNameNormalOrUniqueQualityFormat"
+ : "ItemNameQualityFormat";
+
+ // TODO : Make Generic
+ // Journal Leveling
+ uint32 unDuckBadgeLevel;
+ static CSchemaAttributeDefHandle pAttrDef_DuckBadgeLevel( "duck rating" );
+ enum { kDuckBadgeLength = 64, };
+ locchar_t szDuckBadge[kDuckBadgeLength] = LOCCHAR("");
+ { //if ( pItem && FindAttribute_UnsafeBitwiseCast<attrib_value_t>( pItem, pAttr_DuckLevelBadge, &iDuckBadgeLevel ) )
+ if ( pAttrDef_DuckBadgeLevel && FindAttribute_UnsafeBitwiseCast<attrib_value_t>( pEconItem, pAttrDef_DuckBadgeLevel, &unDuckBadgeLevel ) && unDuckBadgeLevel != 0 )
+ {
+ const CItemLevelingDefinition *pLevelDef = GetItemSchema()->GetItemLevelForScore( "Journal_DuckBadge", unDuckBadgeLevel );
+ if ( pLevelDef )
+ {
+ loc_scpy_safe( szDuckBadge, pLocalizationProvider->Find( pLevelDef->GetNameLocalizationKey() ) );
+ loc_scat_safe( szDuckBadge, LOCCHAR(" ") );
+ }
+ }
+ }
+
+ // Strange Unusual Festive Killstreak Australium ducks
+ loc_scpy_safe( szQuality, CConstructLocalizedString( pLocalizationProvider->Find( pszQualityFormat ), szQuality, szIsFestivized, szKillStreak, szAustraliumSkin, szDuckBadge ) );
+
+ enum { kLocalizedCrateSeriesLength = 128, };
+ locchar_t szLocalizedCrateSeries[ kLocalizedCrateSeriesLength ] = LOCCHAR("");
+
+#ifdef PROJECT_TF
+ static CSchemaAttributeDefHandle pAttrDef_SupplyCrateSeries( "set supply crate series" );
+ // do not display series number for crates that have a collection reference
+ if ( pAttrDef_SupplyCrateSeries && pEconItemDefinition->GetItemClass() && !Q_stricmp( pEconItemDefinition->GetItemClass(), "supply_crate" ) && !pEconItemDefinition->GetCollectionReference() )
+ {
+ // It's a crate, find a series #
+ uint32 unSupplyCrateSeries;
+ float fSupplyCrateSeries;
+ if ( FindAttribute_UnsafeBitwiseCast<attrib_value_t>( pEconItem, pAttrDef_SupplyCrateSeries, &fSupplyCrateSeries ) && fSupplyCrateSeries != 0.0f )
+ {
+ unSupplyCrateSeries = fSupplyCrateSeries;
+
+ loc_scpy_safe( szLocalizedCrateSeries,
+ CConstructLocalizedString( pLocalizationProvider->Find( "ItemNameCraftSeries" ),
+ unSupplyCrateSeries ) );
+ }
+ }
+
+ // This is not "crate series number"; this is "release series number", ie., "a series 3 chemistry kit".
+ static CSchemaAttributeDefHandle pAttrDef_SeriesNumber( "series number" );
+ if ( pAttrDef_SeriesNumber )
+ {
+ float flSeriesNumber = 0.0f;
+ if ( FindAttribute_UnsafeBitwiseCast<attrib_value_t>( pEconItem, pAttrDef_SeriesNumber, &flSeriesNumber ) && flSeriesNumber != 0.0f )
+ {
+ uint32 unSeriesNumber = flSeriesNumber;
+
+ loc_scpy_safe( szLocalizedCrateSeries,
+ CConstructLocalizedString( pLocalizationProvider->Find( "ItemNameCraftSeries" ),
+ unSeriesNumber ) );
+ }
+ }
+#endif
+
+ // Were we one of the first couple that were crafted? If so, output our craft number as well.
+ locchar_t *pCraftNumberLocFormat = pLocalizationProvider->Find( "ItemNameCraftNumberFormat" );
+
+ enum { kLocalizedCraftIndexLength = 128, };
+ locchar_t szLocalizedCraftIndex[ kLocalizedCraftIndexLength ] = LOCCHAR("");
+
+ if ( pCraftNumberLocFormat )
+ {
+ static CSchemaAttributeDefHandle pAttrDef_UniqueCraftIndex( "unique craft index" );
+
+ uint32 unCraftIndex;
+ if ( pEconItem->FindAttribute( pAttrDef_UniqueCraftIndex, &unCraftIndex ) &&
+ ShouldDisplayCraftCounterValue( unCraftIndex ) )
+ {
+ locchar_t szCraftNumber[ kLocalizedCraftIndexLength ];
+ loc_sprintf_safe( szCraftNumber, LOCCHAR( "%i" ), unCraftIndex );
+
+ ILocalize::ConstructString_safe( szLocalizedCraftIndex,
+ pCraftNumberLocFormat,
+ 1,
+ szCraftNumber );
+ }
+ }
+
+ // Generate tool application string
+ enum { kToolApplicationNameLength = 128, };
+ locchar_t szToolTargetNameName[ kToolApplicationNameLength ] = LOCCHAR("");
+ locchar_t szDynamicRecipeOutputName[ kToolApplicationNameLength ] = LOCCHAR("");
+
+ static CSchemaAttributeDefHandle pAttribDef_ToolTarget( "tool target item" );
+ if( pAttribDef_ToolTarget && pEconItem->GetItemDefinition()->GetItemClass() && !Q_stricmp( pEconItem->GetItemDefinition()->GetItemClass(), "tool" ) )
+ {
+ // It's a tool, see if it has a tool target item attribute
+ float flItemDef;
+ if ( FindAttribute_UnsafeBitwiseCast<attrib_value_t>( pEconItem, pAttribDef_ToolTarget, &flItemDef ) )
+ {
+ const item_definition_index_t unItemDef = flItemDef;
+
+ locchar_t szTargetItemName[ MAX_ITEM_NAME_LENGTH ] = LOCCHAR("Unknown Item");
+
+ // Get base name of target item
+ const CEconItemDefinition *pEconTargetDef = GetItemSchema()->GetItemDefinition( unItemDef );
+ if ( pEconTargetDef )
+ {
+ GetLocalizedBaseItemName( szTargetItemName, pLocalizationProvider, pEconTargetDef );
+ }
+
+ loc_scpy_safe( szToolTargetNameName,
+ CConstructLocalizedString( pLocalizationProvider->Find( "ItemNameToolTargetNameFormat" ),
+ szTargetItemName ) );
+ }
+ else
+ {
+ CRecipeComponentMatchingIterator componentIterator( pEconItem, NULL );
+ pEconItem->IterateAttributes( &componentIterator );
+
+ // It only makes sense to mention the output if there's only 1 output
+ if( componentIterator.GetMatchingComponentOutputs().Count() == 1 )
+ {
+ CAttribute_DynamicRecipeComponent attribValue;
+ pEconItem->FindAttribute( componentIterator.GetMatchingComponentOutputs().Head(), &attribValue );
+
+ CEconItem tempItem;
+ if ( !DecodeItemFromEncodedAttributeString( attribValue, &tempItem ) )
+ {
+ AssertMsg2( 0, "%s: Unable to decode dynamic recipe output attribute on item %llu.", __FUNCTION__, pEconItem->GetID() );
+ }
+ else
+ {
+ locchar_t loc_ItemName[MAX_ITEM_NAME_LENGTH];
+ GenerateLocalizedFullItemName( loc_ItemName, pLocalizationProvider, &tempItem, k_EGenerateLocalizedFullItemName_Default, false );
+
+ loc_scpy_safe( szDynamicRecipeOutputName,
+ CConstructLocalizedString( pLocalizationProvider->Find( "ItemNameDynamicRecipeTargetNameFormat" ),
+ loc_ItemName ) );
+ }
+ }
+ }
+ }
+
+
+ // PaintKit and Wear
+ if ( !bHasCustomName )
+ {
+ CEconItemPaintKitDefinition *pPaintKit = pEconItem->GetCustomPainkKitDefinition();
+ if ( pPaintKit )
+ {
+ if ( bIgnoreNameWithPaintkit )
+ {
+ Econ_SetNameAsPaintkit( szItemName, pLocalizationProvider, pPaintKit );
+ }
+ else
+ {
+ Econ_ConcatPaintKitName( szItemName, pLocalizationProvider, pPaintKit );
+ if ( !bIgnoreQualityAndWear )
+ {
+ float flWear = 0;
+ if ( pEconItem->GetCustomPaintKitWear( flWear ) )
+ {
+ Econ_ConcatPaintKitWear( szItemName, pLocalizationProvider, flWear );
+ }
+ }
+ }
+ }
+ }
+
+ locchar_t *pNameLocalizationFormat = pLocalizationProvider->Find( pszLocalizationPattern );
+
+ if ( pNameLocalizationFormat )
+ {
+ ILocalize::ConstructString_safe( out_pItemName,
+ pNameLocalizationFormat,
+ 6,
+ szQuality,
+ szItemName,
+ szLocalizedCraftIndex,
+ szLocalizedCrateSeries,
+ szToolTargetNameName,
+ szDynamicRecipeOutputName);
+ }
+ else
+ {
+ loc_scpy_safe( out_pItemName, s_pUnknownItemName );
+ }
+}
+
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+void CEconItemDescription::Generate_ItemName( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem )
+{
+ Assert( pLocalizationProvider );
+ Assert( pEconItem );
+
+ // If this item has a custom name, use it instead of doing our crazy name compositing based on quality,
+ // type, etc.
+ const char *utf8_CustomName = pEconItem->GetCustomName();
+
+ if ( utf8_CustomName && utf8_CustomName[0] )
+ {
+ locchar_t loc_CustomName[ MAX_ITEM_NAME_LENGTH ];
+ pLocalizationProvider->ConvertUTF8ToLocchar( utf8_CustomName, loc_CustomName, sizeof( loc_CustomName ) );
+
+ // Store it in the item name, wrapped in quotes to prevent item name spoofing
+ // We use two single quotes, because the double quote isn't very visible in the TF2 font
+ locchar_t loc_CustomNameWithQuotes[ MAX_ITEM_NAME_LENGTH ];
+ loc_scpy_safe( loc_CustomNameWithQuotes, LOCCHAR("''") );
+ loc_scat_safe( loc_CustomNameWithQuotes, loc_CustomName );
+ loc_scat_safe( loc_CustomNameWithQuotes, LOCCHAR("''") );
+
+ AddDescLine( loc_CustomNameWithQuotes, /* this will be ignored: */ ATTRIB_COL_LEVEL, kDescLineFlag_Name );
+ }
+ else
+ {
+ locchar_t loc_ItemName[MAX_ITEM_NAME_LENGTH];
+ GenerateLocalizedFullItemName( loc_ItemName, pLocalizationProvider, pEconItem, k_EGenerateLocalizedFullItemName_WithPaintkitNoItem, m_pHashContext == NULL );
+
+ AddDescLine( loc_ItemName, /* this will be ignored: */ ATTRIB_COL_LEVEL, kDescLineFlag_Name );
+ }
+}
+
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+const locchar_t *GetLocalizedStringForKillEaterTypeAttr( const CLocalizationProvider *pLocalizationProvider, uint32 unKillEaterEventType )
+{
+ Assert( pLocalizationProvider );
+
+ // Generate localized string.
+ const char *pszLocString = GetItemSchema()->GetKillEaterScoreTypeLocString( unKillEaterEventType );
+
+ return pszLocString != NULL
+ ? pLocalizationProvider->Find( pszLocString )
+ : LOCCHAR("");
+}
+
+class CStrangeRestrictionAttrWrapper
+{
+public:
+ CStrangeRestrictionAttrWrapper( const CLocalizationProvider *pLocalizationProvider, const locchar_t *loc_In )
+ : m_str( loc_In ? pLocalizationProvider->Find( "ItemTypeDescStrangeFilterSubStr" ) : LOCCHAR(""), loc_In ? loc_In : LOCCHAR("") )
+ {
+ //
+ }
+
+ const locchar_t *operator *() const
+ {
+ return static_cast<const locchar_t *>( m_str );
+ }
+
+private:
+ CConstructLocalizedString m_str;
+};
+
+const locchar_t *CEconItemDescription::GetLocalizedStringForStrangeRestrictionAttr( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem, int iAttrIndex ) const
+{
+ uint32 unRestrictionType;
+ uint32 unRestrictionValue;
+ if ( !pEconItem->FindAttribute( GetKillEaterAttr_Restriction( iAttrIndex ), &unRestrictionType ) ||
+ !pEconItem->FindAttribute( GetKillEaterAttr_RestrictionValue( iAttrIndex ), &unRestrictionValue ) ||
+ unRestrictionType == kStrangeEventRestriction_None )
+ {
+ return NULL;
+ }
+
+ switch ( unRestrictionType )
+ {
+#ifdef PROJECT_TF
+ case kStrangeEventRestriction_Map:
+ {
+ const MapDef_t *pMap = GetItemSchema()->GetMasterMapDefByIndex( unRestrictionValue );
+ if ( pMap )
+ return pLocalizationProvider->Find( pMap->pszMapNameLocKey );
+ }
+ case kStrangeEventRestriction_Competitive:
+ {
+ return pLocalizationProvider->Find( "ItemTypeDescStrangeFilterCompetitive" );
+ }
+#endif // PROJECT_TF
+
+ case kStrangeEventRestriction_VictimSteamAccount:
+ return FindAccountPersonaName( unRestrictionValue );
+ }
+
+ return NULL;
+}
+
+bool CEconItemDescription::BGenerate_ItemLevelDesc_StrangeNameAndStats( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem, const locchar_t *locTypename )
+{
+ CStrangeRankLocalizationGenerator RankGenerator( pLocalizationProvider, pEconItem, m_pHashContext == NULL );
+ if ( !RankGenerator.IsValid() )
+ return false;
+
+ // For Collection Items
+ if ( pEconItem->GetCustomPainkKitDefinition() )
+ {
+ AddDescLine( CConstructLocalizedString( pLocalizationProvider->Find( "Attrib_stattrakmodule" ), RankGenerator.GetRankLocalized() ),
+ ATTRIB_COL_STRANGE,
+ kDescLineFlag_Misc
+ );
+
+ // Are we tracking alternate stats as well?
+ for ( int i = 0; i < GetKillEaterAttrCount(); i++ )
+ {
+ const CEconItemAttributeDefinition *pKillEaterAltAttrDef = GetKillEaterAttr_Score( i ),
+ *pKillEaterAltScoreTypeAttrDef = GetKillEaterAttr_Type( i );
+ if ( !pKillEaterAltAttrDef || !pKillEaterAltScoreTypeAttrDef )
+ continue;
+
+ uint32 unKillEaterAltScore;
+ if ( !pEconItem->FindAttribute( pKillEaterAltAttrDef, &unKillEaterAltScore ) )
+ continue;
+
+ // Older items can optionally not specify a type attribute at all and have an implicit "I'm tracking
+ // kills" zeroth attribute. We require a score type for any slot besides that.
+ if ( i != 0 && !pEconItem->FindAttribute( pKillEaterAltScoreTypeAttrDef ) )
+ continue;
+
+ const uint32 unKillEaterAltType = GetScoreTypeForKillEaterAttr( pEconItem, pKillEaterAltScoreTypeAttrDef );
+
+ AddDescLine( CConstructLocalizedString( pLocalizationProvider->Find( "ItemTypeDescKillEaterAltv2" ),
+ unKillEaterAltScore,
+ GetLocalizedStringForKillEaterTypeAttr( pLocalizationProvider, unKillEaterAltType ),
+ *CStrangeRestrictionAttrWrapper( pLocalizationProvider, GetLocalizedStringForStrangeRestrictionAttr( pLocalizationProvider, pEconItem, i ) ) ),
+ ATTRIB_COL_LEVEL,
+ kDescLineFlag_Misc ); // strange item scores past the first are not considered part of the type
+ }
+
+ return true;
+ } // End Collection Items
+
+ // Normal old way
+
+ // Look for Limited Item Attr
+ bool bLimitedQuantity = false;
+ static CSchemaAttributeDefHandle pAttrDef_LimitedQuantityItem( "limited quantity item" );
+ bLimitedQuantity = pEconItem->FindAttribute( pAttrDef_LimitedQuantityItem );
+
+ AddDescLine( CConstructLocalizedString( pLocalizationProvider->Find( "ItemTypeDescKillEater" ),
+ RankGenerator.GetRankLocalized(),
+ locTypename ? locTypename : LOCCHAR(""),
+ RankGenerator.GetStrangeScore(),
+ GetLocalizedStringForKillEaterTypeAttr( pLocalizationProvider, RankGenerator.GetStrangeType() ),
+ *CStrangeRestrictionAttrWrapper( pLocalizationProvider, GetLocalizedStringForStrangeRestrictionAttr( pLocalizationProvider, pEconItem, RankGenerator.GetUsedStrangeSlot() ) ),
+ RankGenerator.GetRankSecondaryLocalized() ? RankGenerator.GetRankSecondaryLocalized() : LOCCHAR(""),
+ bLimitedQuantity ? pLocalizationProvider->Find( "LimitedQualityDesc" ) : LOCCHAR("")
+ ),
+ bLimitedQuantity ? ATTRIB_COL_LIMITED_QUANTITY : ATTRIB_COL_LEVEL,
+ kDescLineFlag_Type );
+
+ // Are we tracking alternate stats as well?
+ for ( int i = 0; i < GetKillEaterAttrCount(); i++ )
+ {
+ const CEconItemAttributeDefinition *pKillEaterAltAttrDef = GetKillEaterAttr_Score(i),
+ *pKillEaterAltScoreTypeAttrDef = GetKillEaterAttr_Type(i);
+ if ( !pKillEaterAltAttrDef || !pKillEaterAltScoreTypeAttrDef )
+ continue;
+
+ uint32 unKillEaterAltScore;
+ if ( !pEconItem->FindAttribute( pKillEaterAltAttrDef, &unKillEaterAltScore ) )
+ continue;
+
+ // Older items can optionally not specify a type attribute at all and have an implicit "I'm tracking
+ // kills" zeroth attribute. We require a score type for any slot besides that.
+ if ( i != 0 && !pEconItem->FindAttribute( pKillEaterAltScoreTypeAttrDef ) )
+ continue;
+
+ const uint32 unKillEaterAltType = GetScoreTypeForKillEaterAttr( pEconItem, pKillEaterAltScoreTypeAttrDef );
+
+ // Skip if this is our primary stat and we already output it above.
+ if ( unKillEaterAltType == RankGenerator.GetStrangeType() )
+ continue;
+
+ AddDescLine( CConstructLocalizedString( pLocalizationProvider->Find( "ItemTypeDescKillEaterAlt" ),
+ unKillEaterAltScore,
+ GetLocalizedStringForKillEaterTypeAttr( pLocalizationProvider, unKillEaterAltType ),
+ *CStrangeRestrictionAttrWrapper( pLocalizationProvider, GetLocalizedStringForStrangeRestrictionAttr( pLocalizationProvider, pEconItem, i ) ) ),
+ ATTRIB_COL_LEVEL,
+ kDescLineFlag_Misc ); // strange item scores past the first are not considered part of the type
+ }
+
+ return true;
+}
+
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+uint32 GetItemDescriptionDisplayLevel( const IEconItemInterface *pEconItem )
+{
+ Assert( pEconItem );
+
+ static CSchemaAttributeDefHandle pAttrDef_WideItemLevel( "wide item level" );
+
+ uint32 unWideLevelValue;
+ if ( pEconItem->FindAttribute( pAttrDef_WideItemLevel, &unWideLevelValue ) )
+ return unWideLevelValue;
+
+ return pEconItem->GetItemLevel();
+}
+
+void CEconItemDescription::Generate_ItemLevelDesc_Default( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem, const locchar_t *locTypename )
+{
+ // By default, items will only show the level if there is an item type to go along with it.
+ // Combined, these will build a string like "Level 10 Shotgun". We allow a custom attribute
+ // to force the level to be displayed by itself even if there is no item class ("Level 10").
+ static CSchemaAttributeDefHandle pAttrDef_ForceLevelDisplay( "force_level_display" );
+
+ item_definition_index_t usDefIndex = pEconItem->GetItemDefIndex();
+
+
+#ifdef CLIENT_DLL
+ const bool bIsStoreItem = IsStorePreviewItem( pEconItem );
+ const bool bIsPreviewItem = pEconItem->GetFlags() & kEconItemFlagClient_Preview;
+
+ // If the item doesn't have a valid itemID, we'll just use the locTypename for the item level description.
+ // We don't want to display "Level 0 Hat" in places like the Mann Co. Store and Armory. We'll just display "Hat".
+ if ( bIsStoreItem || bIsPreviewItem || pEconItem->GetItemDefinition()->GetRarity() != k_unItemRarity_Any )
+ {
+ if ( locTypename && *locTypename )
+ {
+ AddDescLine( locTypename, ATTRIB_COL_LEVEL, kDescLineFlag_Type, NULL, usDefIndex );
+ }
+ return;
+ }
+#endif
+
+ float fForceLevelDisplayValue;
+ bool bForceLevelDisplay = FindAttribute_UnsafeBitwiseCast<attrib_value_t>( pEconItem, pAttrDef_ForceLevelDisplay, &fForceLevelDisplayValue )
+ && fForceLevelDisplayValue > 0.0f;
+
+ if ( ( locTypename && *locTypename ) || bForceLevelDisplay )
+ {
+ if ( locTypename )
+ {
+ // How are we going to format our level number and base type string?
+ const locchar_t *pszFormatString = NULL;
+
+#ifdef PROJECT_TF
+ static CSchemaAttributeDefHandle pAttrDef_OverrideItemLevelDescString( CTFItemSchema::k_rchOverrideItemLevelDescStringAttribName );
+ static const char *s_pszCustomItemLevelDescLocalizationTokens[] =
+ {
+ "ItemTypeDescCustomLevelString_MvMTour",
+ };
+
+ // ...are we going to use a custom format string specified in an attribute?
+ uint32 unOverrideItemLevelDescString = 0;
+ if ( pEconItem->FindAttribute( pAttrDef_OverrideItemLevelDescString, &unOverrideItemLevelDescString )
+ && unOverrideItemLevelDescString != 0
+ && unOverrideItemLevelDescString <= ARRAYSIZE( s_pszCustomItemLevelDescLocalizationTokens ) )
+ {
+ const char *pszLevelLocalizationToken = s_pszCustomItemLevelDescLocalizationTokens[ unOverrideItemLevelDescString - 1 ];
+ Assert( pszLevelLocalizationToken );
+
+ pszFormatString = pLocalizationProvider->Find( pszLevelLocalizationToken );
+ }
+#endif // PROJECT_TF
+
+ // Either we didn't have a custom override attribute, or we did and it had an invalid value, or it had a valid
+ // value but the localization system failed to find something for that key. In any event, we fall back to our default
+ // format string here.
+ if ( pszFormatString == NULL )
+ {
+ bool bLimitedQuantity = false;
+ static CSchemaAttributeDefHandle pAttrDef_LimitedQuantityItem( "limited quantity item" );
+ bLimitedQuantity = pEconItem->FindAttribute( pAttrDef_LimitedQuantityItem );
+
+#if defined( TF_CLIENT_DLL )
+ if ( pEconItem->GetItemDefinition()->GetItemClass() && V_strcmp( pEconItem->GetItemDefinition()->GetItemClass(), "map_token" ) == 0 )
+ {
+ // For map stamps on the client we can show how many hours they've played each map
+ // And how many times they've donated to it instead of the generic "level"
+ for ( int i = 0; i < GetItemSchema()->GetMapCount(); i++ )
+ {
+ const MapDef_t* pMap = GetItemSchema()->GetMasterMapDefByIndex( i );
+
+ if ( pMap->mapStampDef != pEconItem->GetItemDefinition() )
+ continue;
+
+ int nItemLevel = MapInfo_GetDonationAmount( steamapicontext->SteamUser()->GetSteamID().GetAccountID(), pMap->pszMapName );
+
+ MapStats_t &mapStats = GetMapStats( pMap->GetStatsIdentifier() );
+ int nNumHours = ( mapStats.accumulated.m_iStat[TFMAPSTAT_PLAYTIME] ) / ( 60 /*sec*/ * 60 /*min*/ );
+
+ AddDescLine( CConstructLocalizedString( pLocalizationProvider->Find( "ItemTypeDescCustomLevelString_MapStamp" ), (uint32)nItemLevel, (uint32)nNumHours ), ATTRIB_COL_LEVEL, kDescLineFlag_Type );
+ return;
+ }
+ }
+ else
+#endif
+ {
+ if ( bLimitedQuantity )
+ {
+ // Limited Item Description
+ pszFormatString = pLocalizationProvider->Find( "ItemTypeDescLimited" );
+ AddDescLine( CConstructLocalizedString(
+ pszFormatString,
+ GetItemDescriptionDisplayLevel( pEconItem ),
+ locTypename,
+ pLocalizationProvider->Find( "LimitedQualityDesc" ) ),
+ ATTRIB_COL_LIMITED_QUANTITY,
+ kDescLineFlag_Type,
+ NULL,
+ usDefIndex
+ );
+ return;
+ }
+ pszFormatString = pLocalizationProvider->Find( "ItemTypeDesc" );
+ }
+ }
+
+ // If we still don't have a format string here, it means our default also failed, but CConstructLocalizedString will
+ // handle that safely.
+ AddDescLine( CConstructLocalizedString( pszFormatString, GetItemDescriptionDisplayLevel( pEconItem ), locTypename ), ATTRIB_COL_LEVEL, kDescLineFlag_Type, NULL, usDefIndex );
+ }
+ else
+ {
+ Assert( bForceLevelDisplay );
+
+ AddDescLine( CConstructLocalizedString( pLocalizationProvider->Find( "ItemTypeDescNoLevel" ), GetItemDescriptionDisplayLevel( pEconItem ) ), ATTRIB_COL_LEVEL, kDescLineFlag_Type, NULL, usDefIndex );
+ }
+ }
+}
+
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+void CEconItemDescription::Generate_ItemLevelDesc( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem )
+{
+ Assert( pLocalizationProvider );
+ Assert( pEconItem );
+
+ const GameItemDefinition_t *pItemDef = pEconItem->GetItemDefinition();
+ if ( !pItemDef )
+ return;
+
+ const locchar_t *locTypename = pLocalizationProvider->Find( pItemDef->GetItemTypeName() );
+
+ // Kill-eating weapons replace the standard weapon name/level line with a label
+ // describing the current class of the item instead of the level. This overrides
+ // even "force_level_display".
+ if ( BGenerate_ItemLevelDesc_StrangeNameAndStats( pLocalizationProvider, pEconItem, locTypename ) )
+ return;
+
+ // Not strange, but if you are paint kitted or have a collection reference dont create this
+ if ( pEconItem->GetCustomPainkKitDefinition() || pItemDef->GetCollectionReference() )
+ return;
+
+ // If we didn't generate a fancy strange name, we fall back to our default behavior.
+ Generate_ItemLevelDesc_Default( pLocalizationProvider, pEconItem, locTypename );
+}
+
+#if defined( STAGING_ONLY ) && defined( CLIENT_DLL )
+ConVar econ_include_debug_item_description( "econ_include_debug_item_description","0", FCVAR_CLIENTDLL | FCVAR_ARCHIVE, "Controls display of the additional debug fields in CEconItemDescription (definition index, etc.)." );
+
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+void CEconItemDescription::Generate_DebugInformation( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem )
+{
+ Assert( pLocalizationProvider );
+ Assert( pEconItem );
+
+ if ( !econ_include_debug_item_description.GetBool() )
+ return;
+
+#if TF_ANTI_IDLEBOT_VERIFICATION
+ // Adding these extra description lines would mess with our GC/client sync.
+ if ( m_pHashContext )
+ return;
+#endif // TF_ANTI_IDLEBOT_VERIFICATION
+
+ AddDescLine( CConstructLocalizedString( LOCCHAR("([ Item ID: %s1 ])"), pEconItem->GetID() ), ATTRIB_COL_POSITIVE, kDescLineFlag_Misc );
+ AddDescLine( CConstructLocalizedString( LOCCHAR("([ Item Definition Index: %s1 ])"), (uint32)pEconItem->GetItemDefIndex() ), ATTRIB_COL_POSITIVE, kDescLineFlag_Misc );
+ AddDescLine( CConstructLocalizedString( LOCCHAR("([ In Use?: %s1 ])"), pEconItem->GetInUse() ? LOCCHAR("true") : LOCCHAR("false") ), ATTRIB_COL_POSITIVE, kDescLineFlag_Misc );
+ AddEmptyDescLine();
+
+ class CDebugAttributeDisplayer : public IEconItemAttributeIterator
+ {
+ public:
+ CDebugAttributeDisplayer( CEconItemDescription *pOut_Desc ) : m_pDesc( pOut_Desc ) { Assert( m_pDesc ); }
+
+ virtual bool OnIterateAttributeValue( const CEconItemAttributeDefinition *pAttrDef, attrib_value_t value ) OVERRIDE
+ {
+ // Ugh.
+ wchar_t wszAttrDef[ 256 ];
+ ILocalize::ConvertANSIToUnicode( pAttrDef->GetDefinitionName(), &wszAttrDef[0], sizeof( wszAttrDef ) );
+
+ m_pDesc->AddDescLine( CConstructLocalizedString( LOCCHAR("([ Attribute [%s1]: '%s2' ( %s3 | %s4 ) ])"),
+ (uint32)pAttrDef->GetDefinitionIndex(),
+ wszAttrDef,
+ *(uint32 *)&value,
+ *(float *)&value ), ATTRIB_COL_POSITIVE, kDescLineFlag_Misc );
+
+ return true;
+ }
+
+ virtual bool OnIterateAttributeValue( const CEconItemAttributeDefinition *pAttrDef, float value ) OVERRIDE
+ {
+ // Ugh.
+ wchar_t wszAttrDef[ 256 ];
+ ILocalize::ConvertANSIToUnicode( pAttrDef->GetDefinitionName(), &wszAttrDef[0], sizeof( wszAttrDef ) );
+
+ m_pDesc->AddDescLine( CConstructLocalizedString( LOCCHAR("([ Attribute [%s1]: '%s2' ( %f ) ])"),
+ (uint32)pAttrDef->GetDefinitionIndex(),
+ wszAttrDef,
+ value ), ATTRIB_COL_POSITIVE, kDescLineFlag_Misc );
+
+ return true;
+ }
+
+ virtual bool OnIterateAttributeValue( const CEconItemAttributeDefinition *pAttrDef, const uint64& value ) OVERRIDE
+ {
+ // Ugh.
+ wchar_t wszAttrDef[ 256 ];
+ ILocalize::ConvertANSIToUnicode( pAttrDef->GetDefinitionName(), &wszAttrDef[0], sizeof( wszAttrDef ) );
+
+ m_pDesc->AddDescLine( CConstructLocalizedString( LOCCHAR("([ Attribute [%s1]: '%s2' ( %llu ) ])"),
+ (uint32)pAttrDef->GetDefinitionIndex(),
+ wszAttrDef,
+ value ), ATTRIB_COL_POSITIVE, kDescLineFlag_Misc );
+
+ return true;
+ }
+
+ virtual bool OnIterateAttributeValue( const CEconItemAttributeDefinition *pAttrDef, const CAttribute_String& value ) OVERRIDE
+ {
+ // Ugh.
+ wchar_t wszAttrDef[ 256 ];
+ ILocalize::ConvertANSIToUnicode( pAttrDef->GetDefinitionName(), &wszAttrDef[0], sizeof( wszAttrDef ) );
+
+ wchar_t wszAttrContents[ 1024 ];
+ ILocalize::ConvertANSIToUnicode( value.value().c_str(), &wszAttrContents[0], sizeof( wszAttrContents ) );
+
+ m_pDesc->AddDescLine( CConstructLocalizedString( LOCCHAR("([ Attribute [%s1]: '%s2' ( \"%s3\" ) ])"),
+ (uint32)pAttrDef->GetDefinitionIndex(),
+ wszAttrDef,
+ wszAttrContents ), ATTRIB_COL_POSITIVE, kDescLineFlag_Misc );
+ return true;
+ }
+
+ virtual bool OnIterateAttributeValue( const CEconItemAttributeDefinition *pAttrDef, const CAttribute_DynamicRecipeComponent& value ) OVERRIDE
+ {
+ const char* pszItemName = GetItemSchema()->GetItemDefinition( value.def_index() )->GetItemBaseName();
+
+ wchar_t wszItemQuality[ 256 ];
+ ILocalize::ConvertANSIToUnicode( EconQuality_GetQualityString(EEconItemQuality(value.item_quality())), &wszItemQuality[0], sizeof( wszItemQuality ) );
+
+ wchar_t wszAttrString[ 256 ];
+ ILocalize::ConvertANSIToUnicode( value.attributes_string().c_str(), &wszAttrString[0], sizeof( wszAttrString ) );
+
+ wchar_t wszCount[ 64 ];
+ ILocalize::ConvertANSIToUnicode( CFmtStr( "%d/%d", value.num_fulfilled(), value.num_required() ), &wszCount[0], sizeof( wszCount ) );
+
+ locchar_t wszLocalizedItemName[ 128 ];
+ const locchar_t *pLocalizedItemName = GLocalizationProvider()->Find( pszItemName );
+ if ( pLocalizedItemName )
+ {
+ V_wcscpy_safe( wszLocalizedItemName, pLocalizedItemName );
+ }
+ else
+ {
+ // name wasn't found by Find(), so just convert the pszItemName
+ ILocalize::ConvertANSIToUnicode( pszItemName, &wszLocalizedItemName[0], sizeof( wszLocalizedItemName ) );
+ }
+
+ m_pDesc->AddDescLine( CConstructLocalizedString( LOCCHAR( "\nItem: \"%s1\"\nQuality: %s2\nAttribs:%s3\nCount: %s4" ),
+ wszLocalizedItemName,
+ wszItemQuality,
+ wszAttrString,
+ wszCount ),
+ ATTRIB_COL_POSITIVE, kDescLineFlag_Misc );
+ return true;
+ }
+
+ virtual bool OnIterateAttributeValue( const CEconItemAttributeDefinition *pAttrDef, const CAttribute_ItemSlotCriteria& value ) OVERRIDE
+ {
+ // Ugh.
+ wchar_t wszAttrDef[ 256 ];
+ ILocalize::ConvertANSIToUnicode( pAttrDef->GetDefinitionName(), &wszAttrDef[0], sizeof( wszAttrDef ) );
+
+ wchar_t wszAttrTags[ 256 ];
+ ILocalize::ConvertANSIToUnicode( value.tags().c_str(), &wszAttrTags[0], sizeof( wszAttrTags ) );
+
+ m_pDesc->AddDescLine( CConstructLocalizedString( LOCCHAR("([ Attribute [%s1]: '%s2'])\nTags: \"%s3\""),
+ (uint32)pAttrDef->GetDefinitionIndex(),
+ wszAttrDef,
+ wszAttrTags ),
+ ATTRIB_COL_POSITIVE, kDescLineFlag_Misc );
+ return true;
+ }
+
+ virtual bool OnIterateAttributeValue( const CEconItemAttributeDefinition *pAttrDef, const CAttribute_WorldItemPlacement& value ) OVERRIDE
+ {
+ wchar_t wszAttrDef[ 256 ];
+ ILocalize::ConvertANSIToUnicode( pAttrDef->GetDefinitionName(), &wszAttrDef[0], sizeof( wszAttrDef ) );
+
+ m_pDesc->AddDescLine( CConstructLocalizedString( LOCCHAR( "([ Attribute [%s1]: '%s2'])\n" ),
+ (uint32)pAttrDef->GetDefinitionIndex(),
+ wszAttrDef ),
+ ATTRIB_COL_POSITIVE, kDescLineFlag_Misc );
+ return true;
+ }
+
+ private:
+ CEconItemDescription *m_pDesc;
+ };
+
+ CDebugAttributeDisplayer DebugAttributeDisplayer( this );
+ pEconItem->IterateAttributes( &DebugAttributeDisplayer );
+}
+#endif // defined( STAGING_ONLY ) && defined( CLIENT_DLL )
+
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+void CEconItemDescription::Generate_CraftTag( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem )
+{
+ Assert( pLocalizationProvider );
+ Assert( pEconItem );
+
+ static CSchemaAttributeDefHandle pAttribDef_MakersMarkId( "makers mark id" );
+
+ attrib_value_t value;
+ if ( !pEconItem->FindAttribute( pAttribDef_MakersMarkId, &value ) )
+ return;
+
+ AddAttributeDescription( pLocalizationProvider, pAttribDef_MakersMarkId, value );
+}
+
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+void CEconItemDescription::Generate_StyleDesc( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem )
+{
+ Assert( pLocalizationProvider );
+ Assert( pEconItem );
+
+ const GameItemDefinition_t *pItemDef = pEconItem->GetItemDefinition();
+ if ( !pItemDef )
+ return;
+
+ const CEconStyleInfo *pStyle = pItemDef->GetStyleInfo( pEconItem->GetStyle() );
+ if ( !pStyle )
+ return;
+
+ const locchar_t *loc_StyleName = pLocalizationProvider->Find( pStyle->GetName() );
+ if ( !loc_StyleName )
+ return;
+
+ AddDescLine( CConstructLocalizedString( pLocalizationProvider->Find( "#Econ_Style_Desc" ), loc_StyleName ), ATTRIB_COL_LEVEL, kDescLineFlag_Misc );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CEconItemDescription::Generate_HolidayRestriction( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem )
+{
+ Assert( pLocalizationProvider );
+ Assert( pEconItem );
+
+ const GameItemDefinition_t *pItemDef = pEconItem->GetItemDefinition();
+ if ( !pItemDef )
+ return;
+
+ const char *pszHolidayRestriction = pItemDef->GetHolidayRestriction();
+ if ( !pszHolidayRestriction )
+ return;
+
+ // Report any special restrictions. We'll output in a different color depending on whether or not
+ // the restriction currently prevents the item from showing up.
+ LocalizedAddDescLine( pLocalizationProvider,
+ CFmtStr( "Econ_holiday_restriction_%s", pszHolidayRestriction ).Access(),
+ EconHolidays_IsHolidayActive( EconHolidays_GetHolidayForString( pszHolidayRestriction ), CRTime::RTime32TimeCur() ) ? ATTRIB_COL_LEVEL : ATTRIB_COL_NEGATIVE,
+ kDescLineFlag_Misc );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CEconItemDescription::Generate_QualityDesc( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem )
+{
+ Assert( pLocalizationProvider );
+ Assert( pEconItem );
+
+ // Does this item quality have additional description information that goes along with
+ // besides the usual name/coloration changes?
+ const char *pszQualityDescLocalizationKey = NULL;
+
+ switch( pEconItem->GetQuality() )
+ {
+ case AE_SELFMADE:
+ pszQualityDescLocalizationKey = "Attrib_Selfmade_Description";
+ break;
+ case AE_COMMUNITY:
+ pszQualityDescLocalizationKey = "Attrib_Community_Description";
+ break;
+ }
+
+ // We don't need to do anything special.
+ if ( !pszQualityDescLocalizationKey )
+ return;
+
+ // If this item has a particle system attached but doesn't have the attribute that we usually use
+ // to attach particles, we hack it and dump out an extra line to show the particle system description
+ // as well.
+ static CSchemaAttributeDefHandle pAttrDef_ParticleEffect( "attach particle effect" );
+ static attachedparticlesystem_t *pSparkleSystem = GetItemSchema()->FindAttributeControlledParticleSystem( "community_sparkle" );
+
+ // If the schema understands these properties...
+ if ( pAttrDef_ParticleEffect && pSparkleSystem )
+ {
+ // ...and we don't have a real particle effect attribute attribute...
+ if ( !pEconItem->FindAttribute( pAttrDef_ParticleEffect ) )
+ {
+ // check for Unusual Cap def index (1173)
+ // We manually assign unusual effect to content author. No community sparkle
+ if ( pEconItem->GetItemDefIndex() != 1173 )
+ {
+ // ...then manually add the description as if we did.
+ float flSystemID = pSparkleSystem->nSystemID;
+ AddAttributeDescription( pLocalizationProvider, pAttrDef_ParticleEffect, *(uint32*)&flSystemID );
+ }
+ }
+ }
+
+ LocalizedAddDescLine( pLocalizationProvider, pszQualityDescLocalizationKey, ATTRIB_COL_NEUTRAL, kDescLineFlag_Misc );
+}
+
+//-----------------------------------------------------------------------------
+void CEconItemDescription::Generate_ItemRarityDesc( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem )
+{
+ const CEconItemRarityDefinition* pItemRarity = GetItemSchema()->GetRarityDefinition( pEconItem->GetItemDefinition()->GetRarity() );
+ if ( !pItemRarity )
+ return;
+
+ const GameItemDefinition_t *pItemDef = pEconItem->GetItemDefinition();
+ if ( !pItemDef )
+ return;
+
+ const char *pszTooltip = "TFUI_InvTooltip_Rarity";
+
+ attrib_colors_t colorRarity = pItemRarity->GetAttribColor();
+
+ const locchar_t *loc_RarityText = pLocalizationProvider->Find( pItemRarity->GetLocKey() );
+ const locchar_t *locTypename = pLocalizationProvider->Find( pItemDef->GetItemTypeName() );
+ const locchar_t *loc_WearText = LOCCHAR("");
+
+ float flWear = 0;
+ if ( pEconItem->GetCustomPaintKitWear( flWear ) )
+ {
+ loc_WearText = pLocalizationProvider->Find( GetWearLocalizationString( flWear ) );
+ }
+ else
+ {
+ pszTooltip = "TFUI_InvTooltip_RarityNoWear";
+ }
+
+ AddDescLine( CConstructLocalizedString( pLocalizationProvider->Find( pszTooltip ), loc_RarityText, locTypename, loc_WearText ), colorRarity, kDescLineFlag_Misc );
+}
+
+
+//-----------------------------------------------------------------------------
+void CEconItemDescription::Generate_WearAmountDesc( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem )
+{
+ if ( pEconItem->GetCustomPainkKitDefinition() == 0 )
+ return;
+
+ Assert( pLocalizationProvider );
+ Assert( pEconItem );
+
+ float flWear = 0;
+ if ( pEconItem->GetCustomPaintKitWear( flWear ) )
+ {
+ locchar_t loc_WearText[MAX_ATTRIBUTE_DESCRIPTION_LENGTH];
+
+ loc_scpy_safe( loc_WearText, pLocalizationProvider->Find( "#TFUI_InvTooltip_Wear" ) );
+ loc_scat_safe( loc_WearText, LOCCHAR( " " ) );
+ loc_scat_safe( loc_WearText, pLocalizationProvider->Find( GetWearLocalizationString( flWear ) ) );
+
+ AddDescLine( loc_WearText, ATTRIB_COL_NEUTRAL, kDescLineFlag_Misc );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CEconItemDescription::Generate_ItemDesc( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem )
+{
+ Assert( pLocalizationProvider );
+ Assert( pEconItem );
+
+ // Show the custom description if it has one.
+ const char *utf8_CustomDesc = pEconItem->GetCustomDesc();
+ if ( utf8_CustomDesc && utf8_CustomDesc[0] )
+ {
+ locchar_t loc_CustomDesc[ MAX_ITEM_DESC_LENGTH ];
+ pLocalizationProvider->ConvertUTF8ToLocchar( utf8_CustomDesc, loc_CustomDesc, sizeof( loc_CustomDesc ) );
+
+ locchar_t loc_CustomDescWithQuotes[ MAX_ITEM_DESC_LENGTH ];
+ loc_scpy_safe( loc_CustomDescWithQuotes, LOCCHAR("''") );
+ loc_scat_safe( loc_CustomDescWithQuotes, loc_CustomDesc );
+ loc_scat_safe( loc_CustomDescWithQuotes, LOCCHAR("''") );
+
+ AddDescLine( loc_CustomDescWithQuotes, ATTRIB_COL_NEUTRAL, kDescLineFlag_Desc );
+ return;
+ }
+
+ // No custom description -- see if the item has a default description as part of the definition.
+ const GameItemDefinition_t *pItemDef = pEconItem->GetItemDefinition();
+ if ( !pItemDef )
+ return;
+
+ // Add any additional item description
+ if ( pItemDef->GetItemDesc() )
+ {
+ LocalizedAddDescLine( pLocalizationProvider, pItemDef->GetItemDesc(), ATTRIB_COL_NEUTRAL, kDescLineFlag_Desc );
+ }
+
+ // If we're a store preview item, show the available styles in the tooltip so potential buyers
+ // have more information.
+ if ( IsStorePreviewItem( pEconItem ) )
+ {
+ if ( pItemDef && pItemDef->GetNumStyles() > 0 )
+ {
+ AddEmptyDescLine();
+ LocalizedAddDescLine( pLocalizationProvider, "#Store_AvailableStyles_Header", ATTRIB_COL_LEVEL, kDescLineFlag_Misc );
+
+ for ( int i = 0; i < pItemDef->GetNumStyles(); i++ )
+ {
+ LocalizedAddDescLine( pLocalizationProvider, pItemDef->GetStyleInfo( i )->GetName(), ATTRIB_COL_LEVEL, kDescLineFlag_Misc );
+ }
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+
+// If we have at least this many items in our bundle then display multiple entries
+// per line. Otherwise display one item per line for clarity.
+enum { kDescription_CompositeBundleEntriesCount = 15 };
+
+void CEconItemDescription::Generate_Bundle( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem )
+{
+ Assert( pLocalizationProvider );
+ Assert( pEconItem );
+
+ const GameItemDefinition_t *pItemDef = pEconItem->GetItemDefinition();
+ if ( !pItemDef )
+ return;
+
+ const bundleinfo_t *pBundleInfo = pItemDef->GetBundleInfo();
+ if ( !pBundleInfo )
+ return;
+
+ enum EBundleEntryDisplayStyle
+ {
+ kBundleDisplay_SingleEntry, // one entry per line
+ kBundleDisplay_PairEntry, // "Some Item, Some Other Item," (with ending comma)
+ kBundleDisplay_PairEntryFinal, // "Some Item, Some Other Item" (with no ending comma)
+ };
+
+#ifdef GC_DLL
+ AddEmptyDescLine();
+#endif // GC_DLL
+
+ CUtlVector< item_definition_index_t > vecPackBundlesAdded;
+
+ FOR_EACH_VEC( pBundleInfo->vecItemDefs, i )
+ {
+ // Sanity check.
+ const CEconItemDefinition *pBundleItemDef = pBundleInfo->vecItemDefs[i];
+ if ( !pBundleItemDef )
+ continue;
+
+ // If the current item is part of a pack bundle, add the pack bundle to the description, rather than the individual item
+#ifdef DOTA
+ if ( pBundleItemDef->IsPackItem() )
+ {
+ const CUtlVector< CEconItemDefinition * > &vecPackBundleItemDefs = pBundleItemDef->GetOwningPackBundles();
+
+ item_definition_index_t usPackBundleItemDefIndex = vecPackBundleItemDefs[i]->GetDefinitionIndex();
+ if ( vecPackBundlesAdded.HasElement( usPackBundleItemDefIndex ) )
+ continue;
+
+ // Remember the def index so we don't add the reference to the pack bundle more than once
+ vecPackBundlesAdded.AddToTail( usPackBundleItemDefIndex );
+
+ // Now, point pBundleItemDef at the pack bundle itself and carry on
+ pBundleItemDef = pPackBundleItemDef;
+ }
+#endif
+
+ // Figure out which display style to use for this item. By default we put one item one each line...
+ EBundleEntryDisplayStyle eDisplayStyle = kBundleDisplay_SingleEntry;
+
+ // ...but if we have a whole bunch of items in a single bundle, we lump them together two per line to
+ // save space. Only do this on the client. On the GC, use single lines so that link meta data can be passed
+ // along per-line to the store bundle pages.
+#if defined( CLIENT_DLL ) && !defined( TF_CLIENT_DLL )
+ if ( pBundleInfo->vecItemDefs.Count() >= kDescription_CompositeBundleEntriesCount )
+ {
+ const int iRemainingItems = pBundleInfo->vecItemDefs.Count() - i;
+
+ // We distinguish between "there are at least three entries left", which means we'll end the line
+ // with a comma, etc.
+ if ( iRemainingItems > 2 )
+ {
+ eDisplayStyle = kBundleDisplay_PairEntry;
+ }
+ // ...or if these are our very last two items, we list our last two items and that's it.
+ else if ( iRemainingItems == 2 )
+ {
+ eDisplayStyle = kBundleDisplay_PairEntryFinal;
+ }
+ }
+#endif
+
+ if ( eDisplayStyle == kBundleDisplay_SingleEntry )
+ {
+ // pBundleItemDef will point at the pack bundle if pBundleItemDef is a pack item. In DotA, pack bundles *only* include pack items, whereas in TF, there are bundles which include some items where are individually for sale and others that are not. For example, the Scout Starter Bundle, etc.
+#ifdef DOTA
+ LocalizedAddDescLine( pLocalizationProvider, pBundleItemDef->GetItemBaseName(), ATTRIB_COL_BUNDLE_ITEM, kDescLineFlag_Misc, NULL, pBundleItemDef->GetDefinitionIndex() );
+#else
+ LocalizedAddDescLine( pLocalizationProvider, pBundleItemDef->GetItemBaseName(), pBundleItemDef->IsPackItem() ? ATTRIB_COL_NEUTRAL : ATTRIB_COL_BUNDLE_ITEM, kDescLineFlag_Misc, NULL, pBundleItemDef->IsPackItem() ? INVALID_ITEM_DEF_INDEX : pBundleItemDef->GetDefinitionIndex(), !pBundleItemDef->IsPackItem() );
+#endif
+ }
+ else
+ {
+ Assert( eDisplayStyle == kBundleDisplay_PairEntry || eDisplayStyle == kBundleDisplay_PairEntryFinal );
+
+ const CEconItemDefinition *pOtherBundleItem = pBundleInfo->vecItemDefs[i + 1];
+ const char *pOtherBundleItemBaseName = pOtherBundleItem ? pOtherBundleItem->GetItemBaseName() : "";
+
+ AddDescLine( CConstructLocalizedString( pLocalizationProvider->Find( eDisplayStyle == kBundleDisplay_PairEntryFinal ? "Econ_Bundle_Double" : "Econ_Bundle_DoubleContinued" ),
+ pLocalizationProvider->Find( pBundleItemDef->GetItemBaseName() ),
+ pLocalizationProvider->Find( pOtherBundleItemBaseName ) ),
+ ATTRIB_COL_BUNDLE_ITEM,
+ kDescLineFlag_Misc,
+ NULL,
+ pBundleItemDef->GetDefinitionIndex() );
+
+ // We consumed a second element as well.
+ i++;
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CEconItemDescription::Generate_GiftedBy( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem )
+{
+ Assert( pLocalizationProvider );
+ Assert( pEconItem );
+
+ static CSchemaAttributeDefHandle pAttrDef_GiftedBy( "gifter account id" );
+ static CSchemaAttributeDefHandle pAttrDef_EventDate( "event date" );
+
+ attrib_value_t val_GifterId;
+ if ( pAttrDef_GiftedBy && pEconItem->FindAttribute( pAttrDef_GiftedBy, &val_GifterId ) )
+ {
+ // Who gifted us this present?
+ AddAttributeDescription( pLocalizationProvider, pAttrDef_GiftedBy, val_GifterId );
+
+ // Do we also have (optional) information about when it happened?
+ attrib_value_t val_EventData;
+ if ( pAttrDef_EventDate && pEconItem->FindAttribute( pAttrDef_EventDate, &val_EventData ) )
+ {
+ AddAttributeDescription( pLocalizationProvider, pAttrDef_EventDate, val_EventData );
+ }
+ }
+}
+
+#ifdef PROJECT_TF
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+static bool IsDuelingMedal( const GameItemDefinition_t *pItemDef )
+{
+ static CSchemaItemDefHandle pAttrDef_DuelingMedals[] =
+ {
+ CSchemaItemDefHandle( "Duel Medal Bronze" ),
+ CSchemaItemDefHandle( "Duel Medal Silver" ),
+ CSchemaItemDefHandle( "Duel Medal Gold" ),
+ CSchemaItemDefHandle( "Duel Medal Plat" ),
+ };
+
+ Assert( pItemDef );
+
+ for ( int i = 0; i < ARRAYSIZE( pAttrDef_DuelingMedals ); i++ )
+ if ( pItemDef == pAttrDef_DuelingMedals[i] )
+ return true;
+
+ return false;
+}
+
+void CEconItemDescription::Generate_DuelingMedal( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem )
+{
+ Assert( pLocalizationProvider );
+ Assert( pEconItem );
+
+ static CSchemaAttributeDefHandle pAttrDef_EventDate( "event date" );
+
+ const GameItemDefinition_t *pItemDef = pEconItem->GetItemDefinition();
+ if ( !pItemDef )
+ return;
+
+ if ( !IsDuelingMedal( pItemDef ) )
+ return;
+
+ const CTFDuelSummary *pDuelSummary = FindAccountTypeCacheSingleton<CTFDuelSummary>( pEconItem->GetAccountID(), CTFDuelSummary::k_nTypeID );
+ if ( !pDuelSummary )
+ return;
+
+ // Add the date received first.
+ attrib_value_t value;
+ if ( !pEconItem->FindAttribute( pAttrDef_EventDate, &value ) )
+ return;
+
+ // We feed our format-string parameters in via KeyValues.
+ KeyValues *pKeyValues = new KeyValues( "DuelStrings" );
+
+ CLocalizedRTime32 time = { pDuelSummary->Obj().last_duel_timestamp(), false, pLocalizationProvider TF_ANTI_IDLEBOT_VERIFICATION_ONLY_COMMA TF_ANTI_IDLEBOT_VERIFICATION_ONLY_ARG( m_pHashContext ) };
+
+ TypedKeyValuesStringHelper<locchar_t>::Write( pKeyValues, "last_date", CLocalizedStringArg<CLocalizedRTime32>( time ).GetLocArg() );
+ TypedKeyValuesStringHelper<locchar_t>::Write( pKeyValues, "wins", CLocalizedStringArg<uint32>( pDuelSummary->Obj().duel_wins() ).GetLocArg() );
+ TypedKeyValuesStringHelper<locchar_t>::Write( pKeyValues, "last_target", FindAccountPersonaName( pDuelSummary->Obj().last_duel_account_id() ) );
+
+ // What happened in our last duel? This will be used as a format string to wrap the above data.
+ const char *pszTextFormat;
+ switch ( pDuelSummary->Obj().last_duel_status() )
+ {
+ case kDuelStatus_Loss:
+ pszTextFormat = "#TF_Duel_Desc_Lost";
+ break;
+ case kDuelStatus_Tie:
+ pszTextFormat = "#TF_Duel_Desc_Tied";
+ break;
+ case kDuelStatus_Win:
+ default:
+ pszTextFormat = "#TF_Duel_Desc_Won";
+ break;
+ }
+
+ // Output our whole description.
+ AddEmptyDescLine();
+ AddAttributeDescription( pLocalizationProvider, pAttrDef_EventDate, value );
+ AddEmptyDescLine();
+ AddDescLine( CConstructLocalizedString( pLocalizationProvider->Find( pszTextFormat ), pKeyValues ), ATTRIB_COL_NEUTRAL, kDescLineFlag_Misc );
+
+ pKeyValues->deleteThis();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CEconItemDescription::Generate_MapContributor( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem )
+{
+ Assert( pLocalizationProvider );
+ Assert( pEconItem );
+
+ static CSchemaItemDefHandle pItemDef_WorldTraveler( "World Traveler" );
+ if ( !pItemDef_WorldTraveler || pEconItem->GetItemDefinition() != pItemDef_WorldTraveler )
+ return;
+
+ GCSDK::CSharedObjectTypeCache *pTypeCache = FindAccountTypeCache( pEconItem->GetAccountID(), CTFMapContribution::k_nTypeID );
+ if ( !pTypeCache )
+ return;
+
+ static const char *kDonationLevels[] =
+ {
+ "#TF_MapDonationLevel_Bronze",
+ "#TF_MapDonationLevel_Silver",
+ "#TF_MapDonationLevel_Gold",
+ "#TF_MapDonationLevel_Platinum",
+ "#TF_MapDonationLevel_Diamond",
+ "#TF_MapDonationLevel_Australium1",
+ "#TF_MapDonationLevel_Australium2",
+ "#TF_MapDonationLevel_Australium3",
+ "#TF_MapDonationLevel_Unobtainium"
+ };
+ const int kNumDonationLevels = ARRAYSIZE( kDonationLevels );
+ const int kNumDonationsPerLevel = 25;
+
+ CUtlVector<const CTFMapContribution *> vecContributionsPerLevel[ kNumDonationLevels ];
+
+ for ( uint32 i = 0; i < pTypeCache->GetCount(); ++i )
+ {
+ CTFMapContribution *pMapContribution = (CTFMapContribution*)( pTypeCache->GetObject( i ) );
+ const CEconItemDefinition *pMapItemDef = GetItemSchema()->GetItemDefinition( pMapContribution->Obj().def_index() );
+ if ( pMapItemDef )
+ {
+ int iLevel = MIN( pMapContribution->Obj().contribution_level() / kNumDonationsPerLevel, kNumDonationLevels - 1 );
+ vecContributionsPerLevel[iLevel].AddToTail( pMapContribution );
+ }
+ }
+ for ( int i = 0; i < kNumDonationLevels; ++i )
+ {
+ const CUtlVector<const CTFMapContribution *>& vecContributions = vecContributionsPerLevel[i];
+ if ( vecContributions.Count() > 0 )
+ {
+ // Add header like "Silver:" to show the level of contribution for each of the maps following.
+ LocalizedAddDescLine( pLocalizationProvider, kDonationLevels[i], ATTRIB_COL_ITEMSET_NAME, kDescLineFlag_Misc );
+
+ // Add a label showing the map names and number of contributions for each map.
+ locchar_t tempDescription[MAX_ITEM_DESCRIPTION_LENGTH] = { 0 };
+ FOR_EACH_VEC( vecContributions, j )
+ {
+ const CTFMapContribution *pMapContribution = vecContributions[j];
+ const CEconItemDefinition *pMapItemDef = GetItemSchema()->GetItemDefinition( pMapContribution->Obj().def_index() );
+ Assert( pMapItemDef );
+
+ const char *pszMapNameLocalizationToken = pMapItemDef->GetDefinitionString( "map_name", NULL );
+ if ( pszMapNameLocalizationToken )
+ {
+ loc_sncat( tempDescription,
+ CConstructLocalizedString( pLocalizationProvider->Find( "#Attrib_MapDonation" ),
+ pLocalizationProvider->Find( pszMapNameLocalizationToken ),
+ (uint32)pMapContribution->Obj().contribution_level() ),
+ MAX_ITEM_DESCRIPTION_LENGTH );
+
+ if ( j < ( vecContributions.Count() - 1 ) )
+ {
+ loc_sncat( tempDescription, LOCCHAR( ", " ), MAX_ITEM_DESCRIPTION_LENGTH );
+ }
+ }
+ }
+
+ if ( tempDescription[0] )
+ {
+ AddDescLine( tempDescription, ATTRIB_COL_POSITIVE, kDescLineFlag_Misc );
+ }
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CEconItemDescription::Generate_FriendlyHat( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem )
+{
+ Assert( pLocalizationProvider );
+ Assert( pEconItem );
+
+ static CSchemaItemDefHandle pItemDef_FriendlyHat( "Friendly Item" );
+ if ( !pItemDef_FriendlyHat || pEconItem->GetItemDefinition() != pItemDef_FriendlyHat )
+ return;
+
+ const CTFPlayerInfo *pPlayerInfo = FindAccountTypeCacheSingleton<CTFPlayerInfo>( pEconItem->GetAccountID(), CTFPlayerInfo::k_nTypeID );
+ if ( !pPlayerInfo )
+ return;
+
+ AddDescLine( CConstructLocalizedString( pLocalizationProvider->Find( "#Attrib_NewUsersHelped" ), (uint32)pPlayerInfo->Obj().num_new_users_helped() ), ATTRIB_COL_POSITIVE, kDescLineFlag_Misc );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CEconItemDescription::Generate_SaxxyAwardDesc( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem )
+{
+ Assert( pLocalizationProvider );
+ Assert( pEconItem );
+
+ // Don't display anything for items besides the Saxxy itself.
+ static CSchemaItemDefHandle pItemDef_Saxxy( "Saxxy" );
+ static CSchemaItemDefHandle pItemDef_MemoryMaker( "The Memory Maker" );
+ if ( ( !pItemDef_Saxxy || pEconItem->GetItemDefinition() != pItemDef_Saxxy ) &&
+ ( !pItemDef_MemoryMaker || pEconItem->GetItemDefinition() != pItemDef_MemoryMaker ) )
+ {
+ return;
+ }
+
+ // Output our award category if present, or abort if absent.
+ static CSchemaAttributeDefHandle pAttrDef_SaxxyAwardCategory( "saxxy award category" );
+ static CSchemaAttributeDefHandle pAttrDef_EventDate( "event date" );
+
+ uint32 unAwardCategory,
+ unEventDate;
+ if ( !pEconItem->FindAttribute( pAttrDef_SaxxyAwardCategory, &unAwardCategory ) ||
+ !pEconItem->FindAttribute( pAttrDef_EventDate, &unEventDate ) )
+ {
+ return;
+ }
+
+ CRTime cTime( unEventDate );
+ cTime.SetToGMT( false );
+
+ const char *pszFormatString = "#Attrib_SaxxyAward";
+ if ( pEconItem->GetItemDefinition() == pItemDef_MemoryMaker )
+ {
+ pszFormatString = "#Attrib_MemoryMakerAward";
+ }
+
+ AddDescLine( CConstructLocalizedString( pLocalizationProvider->Find( pszFormatString ),
+ pLocalizationProvider->Find( CFmtStr( "Replay_Contest_Category%d", unAwardCategory ).Access() ),
+ (uint32)cTime.GetYear() ),
+ ATTRIB_COL_POSITIVE,
+ kDescLineFlag_Misc );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CEconItemDescription::Generate_MvmChallenges( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem )
+{
+ // Look for our "challenges completed" attribute. If we have this, we assume we're a badge
+ // of some kind. If we don't, we don't display MvM information. This would be a little weird
+ // for level 0 badges that have no completed challenges, but those are something that currently
+ // exist.
+ static CSchemaAttributeDefHandle pAttrDef_ChallengesCompleted( CTFItemSchema::k_rchMvMChallengeCompletedMaskAttribName );
+
+ uint32 unMask = 0;
+ if ( !pEconItem->FindAttribute( pAttrDef_ChallengesCompleted, &unMask ) )
+ return;
+
+ // Look through our list of MvM tours to figure out which badge this came from. The badge itself
+ // doesn't know and we need this information to figure out which completion bits map to which
+ // missions.
+ const MvMTour_t *pTour = NULL;
+
+ FOR_EACH_VEC( GetItemSchema()->GetMvmTours(), i )
+ {
+ const MvMTour_t& tour = GetItemSchema()->GetMvmTours()[i];
+
+ if ( tour.m_pBadgeItemDef == pEconItem->GetItemDefinition() )
+ {
+ pTour = &tour;
+ break;
+ }
+ }
+
+ // Couldn't find a tour matching this badge? (This can happen if a client has a busted schema or if
+ // we remove a tour for some reason.)
+ if ( !pTour )
+ return;
+
+ const CUtlVector<MvMMission_t>& vecAllMissions = GetItemSchema()->GetMvmMissions();
+ CUtlVector<int> vecCompletedMissions;
+
+ FOR_EACH_VEC( pTour->m_vecMissions, i )
+ {
+ // Make sure our mission index is valid based on our current schema. If we're a client playing a
+ // game during a GC roll, we could wind up looking at someone else's badge where they have a
+ // mission that we don't understand.
+ const int iMissionIndex = pTour->m_vecMissions[i].m_iMissionIndex;
+ if ( !vecAllMissions.IsValidIndex( iMissionIndex ) )
+ continue;
+
+ const int iBadgeSlot = pTour->m_vecMissions[i].m_iBadgeSlot;
+ if ( iBadgeSlot >= 0 && ((unMask & (1U << iBadgeSlot)) != 0) )
+ {
+ vecCompletedMissions.AddToTail( iMissionIndex );
+ }
+ }
+
+ // Add a summary line for the number they have completed
+ AddDescLine(
+ CConstructLocalizedString(
+ pLocalizationProvider->Find( "#Attrib_MvMChallengesCompletedSummary" ),
+ uint32( vecCompletedMissions.Count() )
+ ),
+ ATTRIB_COL_POSITIVE,
+ kDescLineFlag_Misc
+ );
+
+ // Detail lines for each completed challenge
+ FOR_EACH_VEC( vecCompletedMissions, i )
+ {
+ const MvMMission_t& mission = vecAllMissions[ vecCompletedMissions[i] ];
+ const MvMMap_t& map = GetItemSchema()->GetMvmMaps()[ mission.m_iDisplayMapIndex ];
+ const locchar_t *pszLocFmt = pLocalizationProvider->Find( "#Attrib_MvMChallengeCompletedDetail" );
+ const locchar_t *pszLocMap = pLocalizationProvider->Find( map.m_sDisplayName.Get() );
+ const locchar_t *pszLocChal = pLocalizationProvider->Find( mission.m_sDisplayName.Get() );
+ if ( pszLocFmt && pszLocMap && pszLocChal )
+ {
+ CConstructLocalizedString locLine(
+ pszLocFmt,
+ pszLocMap,
+ pszLocChal
+ );
+ AddDescLine(
+ locLine,
+ ATTRIB_COL_POSITIVE,
+ kDescLineFlag_Misc
+ );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CEconItemDescription::Generate_SquadSurplusClaimedBy( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem )
+{
+ Assert( pLocalizationProvider );
+ Assert( pEconItem );
+
+ static CSchemaAttributeDefHandle pAttrDef_SquadSurplusClaimer( "squad surplus claimer id" );
+ static CSchemaAttributeDefHandle pAttrDef_EventDate( "event date" );
+
+ attrib_value_t val_GifterId;
+ if ( pAttrDef_SquadSurplusClaimer&& pEconItem->FindAttribute( pAttrDef_SquadSurplusClaimer, &val_GifterId ) )
+ {
+ // Who gifted us this present?
+ AddAttributeDescription( pLocalizationProvider, pAttrDef_SquadSurplusClaimer, val_GifterId );
+
+ // Do we also have (optional) information about when it happened?
+ attrib_value_t val_EventData;
+ if ( pAttrDef_EventDate && pEconItem->FindAttribute( pAttrDef_EventDate, &val_EventData ) )
+ {
+ AddAttributeDescription( pLocalizationProvider, pAttrDef_EventDate, val_EventData );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CEconItemDescription::Generate_DynamicRecipe( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem )
+{
+ // Gather our attributes we care about
+ CRecipeComponentMatchingIterator componentIterator( pEconItem, NULL );
+ pEconItem->IterateAttributes( &componentIterator );
+
+ // Nothing to say, bail!
+ if( !componentIterator.GetMatchingComponentInputs().Count() &&
+ !componentIterator.GetMatchingComponentOutputs().Count() )
+ {
+ return;
+ }
+
+ // Add the no partial complete tag if the attribute exists
+ static CSchemaAttributeDefHandle pAttrib_NoPartialComplete( "recipe no partial complete" );
+ if ( pEconItem->FindAttribute( pAttrib_NoPartialComplete ) )
+ {
+ LocalizedAddDescLine( pLocalizationProvider, "TF_ItemDynamic_Recipe_No_Partial_Completion", ATTRIB_COL_POSITIVE, kDescLineFlag_Misc );
+ }
+
+ AddEmptyDescLine();
+
+ if ( componentIterator.GetMatchingComponentInputs().Count() )
+ {
+ // Print out item input header
+ LocalizedAddDescLine( pLocalizationProvider, "TF_ItemDynamic_Recipe_Inputs", ATTRIB_COL_NEUTRAL, kDescLineFlag_Misc );
+ // Print out inputs
+ FOR_EACH_VEC( componentIterator.GetMatchingComponentInputs(), i )
+ {
+ CAttribute_DynamicRecipeComponent attribValue;
+ pEconItem->FindAttribute( componentIterator.GetMatchingComponentInputs()[i], &attribValue );
+
+ const GameItemDefinition_t *pItemDef = dynamic_cast<GameItemDefinition_t *>( GetItemSchema()->GetItemDefinition( attribValue.def_index() ) );
+ if ( !pItemDef )
+ continue;
+
+ int nCount = attribValue.num_required() - attribValue.num_fulfilled();
+
+ // This is a completed component. We don't want to show it (for now)
+ if( nCount == 0 )
+ continue;
+
+ CEconItem tempItem;
+ if ( !DecodeItemFromEncodedAttributeString( attribValue, &tempItem ) )
+ {
+ AssertMsg2( 0, "%s: Unable to decode dynamic recipe input attribute on item %llu.", __FUNCTION__, pEconItem->GetID() );
+ continue;
+ }
+
+ locchar_t lineItem[256];
+ locchar_t loc_ItemName[MAX_ITEM_NAME_LENGTH];
+ GenerateLocalizedFullItemName( loc_ItemName, pLocalizationProvider, &tempItem, k_EGenerateLocalizedFullItemName_Default, m_pHashContext == NULL );
+
+ loc_sprintf_safe( lineItem,
+ LOCCHAR("%s x %d"),
+ loc_ItemName,
+ nCount
+ );
+
+ AddDescLine( lineItem, ATTRIB_COL_ITEMSET_MISSING, kDescLineFlag_Misc );
+ }
+
+ AddEmptyDescLine();
+ }
+
+ // Print out outputs
+ LocalizedAddDescLine( pLocalizationProvider, "TF_ItemDynamic_Recipe_Outputs", ATTRIB_COL_NEUTRAL, kDescLineFlag_Misc );
+ FOR_EACH_VEC( componentIterator.GetMatchingComponentOutputs(), i )
+ {
+ CAttribute_DynamicRecipeComponent attribValue;
+ pEconItem->FindAttribute( componentIterator.GetMatchingComponentOutputs()[i], &attribValue );
+
+ CEconItem tempItem;
+ if ( !DecodeItemFromEncodedAttributeString( attribValue, &tempItem ) )
+ {
+ AssertMsg2( 0, "%s: Unable to decode dynamic recipe output attribute on item %llu.", __FUNCTION__, pEconItem->GetID() );
+ continue;
+ }
+
+ locchar_t loc_ItemName[MAX_ITEM_NAME_LENGTH];
+ GenerateLocalizedFullItemName( loc_ItemName, pLocalizationProvider, &tempItem, k_EGenerateLocalizedFullItemName_Default, m_pHashContext == NULL );
+
+ AddDescLine( loc_ItemName, /* this will be ignored: */ ATTRIB_COL_ITEMSET_MISSING, kDescLineFlag_Misc );
+
+ // Iterate through the attributes on this temp item and have it store the attributes that should affect
+ // this component's name. Once we have that, have it fill out a temporary CEconItemDescription.
+ CRecipeNameAttributeDisplayer recipeAttributeIterator;
+ tempItem.IterateAttributes( &recipeAttributeIterator );
+ recipeAttributeIterator.SortAttributes();
+ CEconItemDescription tempDescription;
+ recipeAttributeIterator.Finalize( &tempItem, &tempDescription, pLocalizationProvider );
+
+ // Check if that temp CEconItemDescription has any attributes we want. If so, steal them.
+ if ( tempDescription.m_vecDescLines.Count() > 0 )
+ {
+ locchar_t loc_Attribs[MAX_ITEM_NAME_LENGTH] = LOCCHAR("");
+
+ // Put the attributes on the next line in parenthesis
+ loc_scat_safe( loc_Attribs, LOCCHAR("(") );
+
+ // Put in each attribute
+ FOR_EACH_VEC( tempDescription.m_vecDescLines, j )
+ {
+ // Comma separated
+ if ( j > 0 )
+ {
+ loc_scat_safe( loc_Attribs, LOCCHAR(", ") );
+ }
+
+ loc_sprintf_safe( loc_Attribs,
+ LOCCHAR("%s%s"),
+ loc_Attribs,
+ tempDescription.m_vecDescLines[j].sText.Get() );
+ }
+
+ loc_scat_safe( loc_Attribs, LOCCHAR(")") );
+
+ // Print out in the same color as the item name above
+ AddDescLine( loc_Attribs, /* this will be ignored: */ ATTRIB_COL_ITEMSET_MISSING, kDescLineFlag_Misc );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+void CEconItemDescription::Generate_Leaderboard( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem )
+{
+#ifdef GC_DLL
+ return;
+#endif
+
+#ifdef TF_CLIENT_DLL
+ Assert( pLocalizationProvider );
+ Assert( pEconItem );
+
+ static CSchemaAttributeDefHandle pAttrDef_DisplayDuckLeaderboard( "display duck leaderboard" );
+
+ if ( pEconItem->FindAttribute( pAttrDef_DisplayDuckLeaderboard ) )
+ {
+ // Friend Board
+ //locchar_t lineItem[256];
+ //
+ //AddDescLine( pLocalizationProvider->Find( "#TF_DuckLeaderboard_Friends" ), ATTRIB_COL_POSITIVE, kDescLineFlag_Misc );
+ //
+ //CUtlVector< LeaderboardEntry_t > scores;
+ //Leaderboards_GetDuckLeaderboard( scores, g_szDuckLeaderboardNames[0] );
+
+ //// Show max of top 10
+ //for ( int i = 0; i < scores.Count() && i < 10; i++ )
+ //{
+ // const locchar_t *pName = FindAccountPersonaName( scores[i].m_steamIDUser.GetAccountID() );
+ // uint32 iRank = scores[i].m_nGlobalRank;
+ // uint32 iScore = scores[i].m_nScore;
+ //
+ // AddDescLine(
+ // CConstructLocalizedString(
+ // pLocalizationProvider->Find( "#TF_DuckLeaderboard_Entry" ),
+ // iRank, pName, iScore
+ // ),
+ // ATTRIB_COL_POSITIVE,
+ // kDescLineFlag_Misc
+ // );
+ //}
+ }
+#endif // TF_CLIENT_DLL
+}
+
+#endif // PROJECT_TF
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+const CEconItemDefinition *GetPaintItemDefinitionForPaintedItem( const IEconItemInterface *pEconItem )
+{
+ static CSchemaAttributeDefHandle pAttribDef_Paint( "set item tint RGB" );
+
+ attrib_value_t unPaintRGBAttrBits;
+ if ( !pAttribDef_Paint || !pEconItem->FindAttribute( pAttribDef_Paint, &unPaintRGBAttrBits ) )
+ return NULL;
+
+ const CEconItemSchema::ToolsItemDefinitionMap_t &toolDefs = GetItemSchema()->GetToolsItemDefinitionMap();
+
+ FOR_EACH_MAP_FAST( toolDefs, i )
+ {
+ const CEconItemDefinition *pItemDef = toolDefs[i];
+
+ // ignore everything that is not a paint can tool
+ const IEconTool *pEconTool = pItemDef->GetEconTool();
+ if ( pEconTool && !V_strcmp( pEconTool->GetTypeName(), "paint_can" ) )
+ {
+ attrib_value_t unPaintRGBAttrCompareBits;
+ if ( FindAttribute( pItemDef, pAttribDef_Paint, &unPaintRGBAttrCompareBits ) && unPaintRGBAttrCompareBits == unPaintRGBAttrBits )
+ return pItemDef;
+ }
+ }
+
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Specify target (strangifiers, etc that can only be applied to specific items)
+//-----------------------------------------------------------------------------
+void CEconItemDescription::Generate_XifierToolTargetItem( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem )
+{
+ Assert( pLocalizationProvider );
+ Assert( pEconItem );
+
+ // Make sure it's a tool of the appropriate type
+ const CEconTool_Xifier *pTool = pEconItem->GetItemDefinition()->GetTypedEconTool<CEconTool_Xifier>();
+ if ( pTool == NULL )
+ return;
+
+ // Make sure there is a specific target item
+ static CSchemaAttributeDefHandle pAttribDef_ToolTargetItem( "tool target item" );
+ float flItemDef;
+ if( pAttribDef_ToolTargetItem && FindAttribute_UnsafeBitwiseCast<attrib_value_t>( pEconItem, pAttribDef_ToolTargetItem, &flItemDef ) )
+ {
+ locchar_t szTargetItemName[ MAX_ITEM_NAME_LENGTH ] = LOCCHAR("Unknown Item");
+
+ // It's a tool, see if it has a tool target item attribute
+ const item_definition_index_t unItemDef = flItemDef;
+ const CEconItemDefinition *pEconTargetDef = GetItemSchema()->GetItemDefinition( unItemDef );
+
+ // Start with the base name.
+ if ( pEconTargetDef )
+ {
+ GetLocalizedBaseItemName( szTargetItemName, pLocalizationProvider, pEconTargetDef );
+ }
+
+ const char *pszDesc = pTool->GetItemDescToolTargetLocToken();
+ AssertMsg( pszDesc && *pszDesc, "%s: missing 'item_desc_tool_target' key", pTool->GetTypeName() );
+ AddDescLine( CConstructLocalizedString( pLocalizationProvider->Find( pszDesc ),
+ szTargetItemName ),
+ ATTRIB_COL_NEUTRAL,
+ kDescLineFlag_Desc );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CEconItemDescription::Generate_Painted( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem )
+{
+ Assert( pLocalizationProvider );
+ Assert( pEconItem );
+
+ static CSchemaAttributeDefHandle pAttrDef_PaintEffect( "Paint Effect" );
+
+ float fPaintEffectType;
+ if ( pAttrDef_PaintEffect && FindAttribute_UnsafeBitwiseCast<attrib_value_t>( pEconItem, pAttrDef_PaintEffect, &fPaintEffectType ) )
+ {
+ if ( fPaintEffectType == 1 )
+ {
+ LocalizedAddDescLine( pLocalizationProvider, "Econ_Paint_Effect_Oscillating", ATTRIB_COL_NEUTRAL, kDescLineFlag_Misc );
+ }
+ else if ( fPaintEffectType == 2 )
+ {
+ LocalizedAddDescLine( pLocalizationProvider, "Econ_Paint_Effect_Position", ATTRIB_COL_NEUTRAL, kDescLineFlag_Misc );
+ }
+ else if ( fPaintEffectType == 3 )
+ {
+ LocalizedAddDescLine( pLocalizationProvider, "Econ_Paint_Effect_LowHealthWarning", ATTRIB_COL_NEUTRAL, kDescLineFlag_Misc );
+ }
+ }
+
+ // Find the name of the paint we have applied in the least efficient way imaginable!
+ const CEconItemDefinition *pItemDef = pEconItem->GetItemDefinition();
+ static CSchemaAttributeDefHandle pAttrDef_ShowPaint( "show paint description" );
+ if ( pItemDef && ( !pItemDef->IsTool() || FindAttribute( pEconItem, pAttrDef_ShowPaint ) ) )
+ {
+ const CEconItemDefinition *pTempDef = GetPaintItemDefinitionForPaintedItem( pEconItem );
+ if ( pTempDef )
+ {
+ const locchar_t *locLocalizedPaintName = pLocalizationProvider->Find( pTempDef->GetItemBaseName() );
+
+ if ( locLocalizedPaintName )
+ {
+ AddDescLine( CConstructLocalizedString( pLocalizationProvider->Find( "Econ_Paint_Name" ),
+ locLocalizedPaintName ),
+ ATTRIB_COL_LEVEL,
+ kDescLineFlag_Misc );
+ }
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CEconItemDescription::Generate_Uses( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem )
+{
+ Assert( pLocalizationProvider );
+ Assert( pEconItem );
+
+ // don't display a quantity if we have the unlimited quantity attribute
+ static CSchemaAttributeDefHandle unlimitedQuantityAttribute( "unlimited quantity" );
+ if ( pEconItem->FindAttribute( unlimitedQuantityAttribute ) )
+ return;
+
+ // Collection tools don't display this.
+ const GameItemDefinition_t *pItemDef = pEconItem->GetItemDefinition();
+ if ( !pItemDef )
+ return;
+
+ const IEconTool *pEconTool = pItemDef->GetEconTool();
+ if ( !pEconTool->ShouldDisplayQuantity( pEconItem ) )
+ return;
+
+ int iQuantity = pEconItem->GetQuantity();
+ bool bIsTool = pItemDef->GetItemClass() && !Q_strcmp( pItemDef->GetItemClass(), "tool" );
+ bool bIsConsumable = ( pItemDef->GetCapabilities() & ITEM_CAP_USABLE_GC ) != 0 && iQuantity != 0;
+
+ if ( bIsTool || bIsConsumable )
+ {
+ locchar_t wszQuantity[10];
+ loc_sprintf_safe( wszQuantity, LOCCHAR( "%d" ), iQuantity );
+
+ // Add an empty line before the usage display.
+ AddEmptyDescLine();
+
+ // Display our usage count.
+ AddDescLine( CConstructLocalizedString( pLocalizationProvider->Find( "#Attrib_LimitedUse" ), &wszQuantity[0] ), ATTRIB_COL_LIMITED_USE, kDescLineFlag_Misc );
+ }
+}
+
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+void CEconItemDescription::Generate_LootListDesc( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem )
+{
+ Assert( pLocalizationProvider );
+ Assert( pEconItem );
+
+ const CEconItemDefinition *pItemDef = pEconItem->GetItemDefinition();
+ if ( !pItemDef )
+ return;
+
+ // Don't add this description if the item is a special crate type.
+ const IEconTool *pEconTool = pItemDef->GetEconTool();
+ const bool bIsRestrictedCrate = pEconTool && pEconTool->GetUsageRestriction()
+ ? !V_stricmp( pEconTool->GetUsageRestriction(), "winter" ) || !V_stricmp( pEconTool->GetUsageRestriction(), "summer" )
+ : false;
+
+ if ( bIsRestrictedCrate )
+ return;
+
+ // Do we have a generation code we want to make public? We do this regardless of whether we're describing our
+ // loot list contents in detail.
+ {
+ static CSchemaAttributeDefHandle pAttrDef_CrateGenerationCode( "crate generation code" );
+ CAttribute_String sCrateGenerationCode;
+
+ const locchar_t *pszCrateGenerationCodeLoc = pLocalizationProvider->Find( "Attrib_CrateGenerationCode" );
+
+ if ( pEconItem->FindAttribute( pAttrDef_CrateGenerationCode, &sCrateGenerationCode ) && sCrateGenerationCode.value().length() > 0 )
+ {
+ CUtlConstStringBase<locchar_t> loc_sAttrValue;
+ pLocalizationProvider->ConvertUTF8ToLocchar( sCrateGenerationCode.value().c_str(), &loc_sAttrValue );
+
+ AddDescLine( CConstructLocalizedString( pszCrateGenerationCodeLoc, loc_sAttrValue.Get() ),
+ ATTRIB_COL_POSITIVE,
+ kDescLineFlag_Misc );
+ }
+ }
+
+ // Grab the actual contents of our loot list.
+ CCrateLootListWrapper LootListWrapper( pEconItem );
+ const IEconLootList *pLootList = LootListWrapper.GetEconLootList();
+
+ if ( pLootList == nullptr )
+ return;
+
+ // If our base loot list is set not to list contents, skip the header/footer as well and don't display anything.
+ if ( !pLootList->BPublicListContents() )
+ return;
+
+ AddEmptyDescLine();
+
+ if ( !pLootList->GetLootListCollectionReference() )
+ {
+ LocalizedAddDescLine( pLocalizationProvider, pLootList->GetLootListHeaderLocalizationKey(), ATTRIB_COL_NEUTRAL, kDescLineFlag_Misc );
+ }
+ else
+ {
+
+ int iCollectionIndex = GetItemSchema()->GetItemCollections().Find( pLootList->GetLootListCollectionReference() );
+ if ( GetItemSchema()->GetItemCollections().IsValidIndex( iCollectionIndex ) )
+ {
+ LocalizedAddDescLine( pLocalizationProvider, (GetItemSchema()->GetItemCollections()[iCollectionIndex])->m_pszLocalizedDesc, ATTRIB_COL_NEUTRAL, kDescLineFlag_Misc );
+ }
+ }
+
+ class CDescriptionLootListIterator : public IEconLootList::IEconLootListIterator
+ {
+ public:
+ CDescriptionLootListIterator( CEconItemDescription *pThis, const CLocalizationProvider *pLocalizationProvider, bool bUseProperName )
+ : m_pThis( pThis )
+ , m_pLocalizationProvider( pLocalizationProvider )
+ , m_bUseProperName( bUseProperName )
+ {
+ }
+
+ virtual void OnIterate( item_definition_index_t unItemDefIndex ) OVERRIDE
+ {
+ const CEconItemDefinition *pItemDef = GetItemSchema()->GetItemDefinition( unItemDefIndex );
+ if ( pItemDef )
+ {
+ // Check if this item is already owned
+ bool bOwned = false;
+ bool bUnusual = false;
+#ifdef CLIENT_DLL
+ CPlayerInventory *pLocalInv = TFInventoryManager()->GetLocalInventory();
+ if ( pLocalInv )
+ {
+ for ( int i = 0; i < pLocalInv->GetItemCount(); ++i )
+ {
+ CEconItemView *pItem = pLocalInv->GetItem( i );
+ if ( pItem->GetItemDefinition() == pItemDef )
+ {
+ bOwned = true;
+ // Check Quality
+ if ( pItem->GetQuality() == AE_UNUSUAL )
+ {
+ bUnusual = true;
+ break;
+ }
+ }
+ }
+ }
+#endif
+ const locchar_t * pCheckmark = bOwned ? m_pLocalizationProvider->Find( "TF_Checkmark" ) : m_pLocalizationProvider->Find( "TF_LackOfCheckmark" );
+ if ( bOwned && bUnusual )
+ {
+ pCheckmark = m_pLocalizationProvider->Find( "TF_Checkmark_Unusual" );
+ }
+
+ attrib_colors_t colorRarity = ATTRIB_COL_RARITY_DEFAULT;
+ const CEconItemRarityDefinition* pItemRarity = GetItemSchema()->GetRarityDefinition( pItemDef->GetRarity() );
+ if ( pItemRarity )
+ {
+ colorRarity = pItemRarity->GetAttribColor();
+ }
+
+ m_pThis->AddDescLine(
+ CConstructLocalizedString( LOCCHAR( "%s1%s2" ), pCheckmark,
+ CEconItemLocalizedFullNameGenerator(
+ m_pLocalizationProvider,
+ pItemDef,
+ m_bUseProperName
+ ).GetFullName() ),
+ colorRarity,
+ kDescLineFlag_Misc,
+ NULL,
+ pItemDef->GetDefinitionIndex()
+ );
+ }
+ }
+
+ private:
+ CEconItemDescription *m_pThis; // look at me I'm a lambda!
+ const CLocalizationProvider *m_pLocalizationProvider;
+ bool m_bUseProperName;
+ };
+
+ CDescriptionLootListIterator it( this, pLocalizationProvider, m_pHashContext == NULL );
+ pLootList->EnumerateUserFacingPotentialDrops( &it );
+
+ if ( pLootList->GetLootListFooterLocalizationKey() )
+ {
+ LocalizedAddDescLine( pLocalizationProvider, pLootList->GetLootListFooterLocalizationKey(), ATTRIB_COL_POSITIVE, kDescLineFlag_Misc );
+ }
+ else
+ {
+ const char *pszRareLootListFooterLocalizationKey = pItemDef->GetDefinitionString( "loot_list_rare_item_footer", "#Econ_Revolving_Loot_List_Rare_Item" );
+ LocalizedAddDescLine( pLocalizationProvider, pszRareLootListFooterLocalizationKey, ATTRIB_COL_POSITIVE, kDescLineFlag_Misc );
+ }
+}
+
+// --------------------------------------------------------------------------
+void CEconItemDescription::Generate_EventDetail( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem )
+{
+ Assert( pLocalizationProvider );
+ Assert( pEconItem );
+
+ const CEconItemDefinition *pItemDef = pEconItem->GetItemDefinition();
+ if ( !pItemDef )
+ return;
+
+ // Check to see if we should append any extra description information
+ const char *pszEventLocalizationKey = pItemDef->GetDefinitionString( "event_desc_footer", NULL );
+ if ( pszEventLocalizationKey )
+ {
+ AddEmptyDescLine();
+ LocalizedAddDescLine( pLocalizationProvider, pszEventLocalizationKey, ATTRIB_COL_POSITIVE, kDescLineFlag_Misc );
+ }
+}
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+#ifdef CLIENT_DLL
+
+static bool IsItemEquipped( uint32 unAccountID, const CEconItemDefinition *pSearchItemDef, const GameItemDefinition_t **ppFoundSetItemDef )
+{
+ Assert( pSearchItemDef );
+ Assert( ppFoundSetItemDef );
+
+ CPlayerInventory *pInv = InventoryManager()->GetInventoryForAccount( unAccountID );
+ if ( !pInv )
+ return false;
+
+ for ( int i = 0; i < pInv->GetItemCount(); i++ )
+ {
+ const CEconItemView *pInvItem = pInv->GetItem( i );
+ if ( !pInvItem )
+ continue;
+
+ // This code is client-only so we expect to always get back an item definition pointer.
+ const GameItemDefinition_t *pInvItemDef = pInvItem->GetItemDefinition();
+ Assert( pInvItemDef );
+
+ if ( pInvItemDef->GetSetItemRemap() != pSearchItemDef->GetDefinitionIndex() )
+ continue;
+
+ if ( !pInvItem->IsEquipped() )
+ continue;
+
+ *ppFoundSetItemDef = pInvItemDef;
+ return true;
+ }
+
+ return false;
+}
+
+#endif // CLIENT_DLL
+
+void CEconItemDescription::Generate_ItemSetDesc( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem )
+{
+ Assert( pLocalizationProvider );
+ Assert( pEconItem );
+
+ const GameItemDefinition_t *pItemDef = pEconItem->GetItemDefinition();
+ if ( !pItemDef )
+ return;
+
+ const CEconItemSetDefinition *pItemSetDef = pItemDef->GetItemSetDefinition();
+ if ( !pItemSetDef )
+ return;
+
+ bool bAllItemsEquipped = true; // filled in below when iterating over items
+
+ // Some item sets are tagged to only appear on items at all if the entire set is visible. Rather than
+ // walk the whole set multiple times checking for item equipped state, we build up a set of description lines
+ // that we *will* display if we display anything at all. Later, we either submit all those lines for real or
+ // return before adding any of them.
+ {
+ CUtlVector<econ_item_description_line_t> vecPotentialDescLines;
+
+ AddEmptyDescLine( &vecPotentialDescLines );
+ LocalizedAddDescLine( pLocalizationProvider, pItemSetDef->m_pszLocalizedName, ATTRIB_COL_ITEMSET_NAME, kDescLineFlag_Set | kDescLineFlag_SetName, &vecPotentialDescLines, pItemSetDef->m_iBundleItemDef );
+
+ // Kyle says: Jon wants different formatting on the GC for sets.
+#if defined( GC_DLL )
+#if TF_ANTI_IDLEBOT_VERIFICATION
+ if ( !m_pHashContext )
+#endif // TF_ANTI_IDLEBOT_VERIFICATION
+ {
+ AddEmptyDescLine( &vecPotentialDescLines );
+ }
+#endif // defined( GC_DLL ) &&
+
+ // Iterate over the items in the set. We'll output a line in different colors to show
+ // the current state of this item. For normal item sets, the color is based on whether
+ // the owner has the item in question equipped (except on the GC, where we always say
+ // "it's not equipped" to avoid confusion). For collections, the color is based on whether
+ // the item has been collected.
+ FOR_EACH_VEC( pItemSetDef->m_iItemDefs, i )
+ {
+ const GameItemDefinition_t *pOtherSetItem = dynamic_cast<const GameItemDefinition_t *>( GetItemSchema()->GetItemDefinition( pItemSetDef->m_iItemDefs[i] ) );
+ if ( !pOtherSetItem )
+ continue;
+
+ item_definition_index_t usLinkItemDefIndex = pOtherSetItem->GetDefinitionIndex();
+
+#ifdef DOTA
+ // If the current item is part of a pack bundle, add the pack bundle to the description, rather than the individual item
+ if ( pOtherSetItem->IsPackItem() )
+ {
+ // Link to the pack bundle, not the individual pack item
+ usLinkItemDefIndex = pOtherSetItem->GetOwningPackBundle()->GetDefinitionIndex();
+ }
+#endif
+
+ // Only used on non-GC in case we have an item misrepresenting itself intentionally for set
+ // grouping purposes. NULL elsewhere.
+ const GameItemDefinition_t *pFoundSetItemDef = NULL;
+
+ const bool bItemPresent =
+#ifdef GC_DLL
+ false; // the GC always treats set items as unequipped for clarity in trading
+#else
+#if TF_ANTI_IDLEBOT_VERIFICATION
+ m_pHashContext
+ ? false // when generating descriptions for GC verification we treat item sets as unequipped to match the GC
+#endif
+ : IsItemEquipped( pEconItem->GetAccountID(), pOtherSetItem, &pFoundSetItemDef ); // non-GC display will find out whether the player in question has this item actively equipped
+#endif
+
+ AddDescLine( CEconItemLocalizedFullNameGenerator(
+ pLocalizationProvider,
+ pFoundSetItemDef ? pFoundSetItemDef : pOtherSetItem,
+ m_pHashContext == NULL
+ ).GetFullName(), bItemPresent ? ATTRIB_COL_ITEMSET_EQUIPPED : ATTRIB_COL_ITEMSET_MISSING, kDescLineFlag_Set, &vecPotentialDescLines, usLinkItemDefIndex );
+
+ bAllItemsEquipped &= bItemPresent;
+ }
+
+ // If the item set is set to be only displayed when the full set is equipped, give up here and
+ // toss out our potential lines. Otherwise submit them for real.
+ if ( pItemSetDef->m_bIsHiddenSet && !bAllItemsEquipped )
+ return;
+
+ FOR_EACH_VEC( vecPotentialDescLines, i )
+ {
+ AddDescLine( vecPotentialDescLines[i].sText.Get(), vecPotentialDescLines[i].eColor, vecPotentialDescLines[i].unMetaType, NULL, vecPotentialDescLines[i].unDefIndex );
+ }
+ }
+
+ // Show the set only attributes if we have the entire set and we have bonus attributes to display.
+ bool bHasVisible = false;
+ FOR_EACH_VEC( pItemSetDef->m_iAttributes, i )
+ {
+ const CEconItemSetDefinition::itemset_attrib_t& attrib = pItemSetDef->m_iAttributes[i];
+ const CEconItemAttributeDefinition *pAttrDef = GetItemSchema()->GetAttributeDefinition( attrib.m_iAttribDefIndex );
+
+ if ( !pAttrDef->IsHidden() )
+ {
+ bHasVisible = true;
+ break;
+ }
+ }
+
+ if ( !bHasVisible )
+ return;
+
+ AddEmptyDescLine();
+ LocalizedAddDescLine( pLocalizationProvider, "#Econ_Set_Bonus", ATTRIB_COL_ITEMSET_NAME, kDescLineFlag_Set );
+
+ FOR_EACH_VEC( pItemSetDef->m_iAttributes, i )
+ {
+ const CEconItemSetDefinition::itemset_attrib_t& attrib = pItemSetDef->m_iAttributes[i];
+ const CEconItemAttributeDefinition *pAttrDef = GetItemSchema()->GetAttributeDefinition( attrib.m_iAttribDefIndex );
+
+ if ( pAttrDef )
+ {
+ // Add the attribute description. Override the color to be grayed out if we don't have the
+ // full set equipped.
+ AddAttributeDescription( pLocalizationProvider,
+ pAttrDef,
+ *(attrib_value_t *)&attrib.m_flValue,
+ bAllItemsEquipped ? /* "do not override": */ NUM_ATTRIB_COLORS : ATTRIB_COL_ITEMSET_MISSING );
+ }
+ }
+}
+
+// --------------------------------------------------------------------------
+void CEconItemDescription::Generate_CollectionDesc( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem )
+{
+ Assert( pLocalizationProvider );
+ Assert( pEconItem );
+
+ const CEconItemDefinition *pItemDef = pEconItem->GetItemDefinition();
+ if ( !pItemDef )
+ return;
+
+ const CEconItemCollectionDefinition *pCollection = pItemDef->GetItemCollectionDefinition();
+ if ( !pCollection )
+ return;
+
+ // Collection Header ..
+ const locchar_t *loc_name = pLocalizationProvider->Find( pCollection->m_pszLocalizedName );
+
+ // Add a bit of spacing, this is only for the market
+ // Add empty line
+ AddDescLine( LOCCHAR( " " ), ATTRIB_COL_NEUTRAL, kDescLineFlag_CollectionName | kDescLineFlag_Empty );
+ AddDescLine( LOCCHAR( " " ), ATTRIB_COL_NEUTRAL, kDescLineFlag_CollectionName | kDescLineFlag_Empty );
+
+ AddDescLine(
+ CConstructLocalizedString( LOCCHAR( "%s1" ), loc_name ),
+ ATTRIB_COL_NEUTRAL,
+ kDescLineFlag_CollectionName
+ );
+
+ FOR_EACH_VEC( pCollection->m_iItemDefs, index )
+ {
+ int eFlag = kDescLineFlag_Collection;
+ const CEconItemDefinition *pTempItemDef = GetItemSchema()->GetItemDefinition( pCollection->m_iItemDefs[index] );
+ if ( pTempItemDef )
+ {
+ // Check if this item is already owned
+ bool bOwned = false;
+ bool bUnusual = false;
+ if ( pTempItemDef == pItemDef )
+ {
+ bOwned = true;
+ eFlag |= kDescLineFlag_CollectionCurrentItem;
+ if ( pEconItem->GetQuality() == AE_UNUSUAL )
+ {
+ bUnusual = true;
+ }
+ }
+#ifdef CLIENT_DLL
+ else
+ {
+ CPlayerInventory *pLocalInv = TFInventoryManager()->GetLocalInventory();
+ if ( pLocalInv )
+ {
+ const CEconItemCollectionDefinition *pRefCollection = GetItemSchema()->GetCollectionByName( pTempItemDef->GetCollectionReference() );
+ // if item has a collection reference, we are looking for all those items and this item
+ if ( pRefCollection )
+ {
+ bOwned = true;
+ FOR_EACH_VEC( pRefCollection->m_iItemDefs, iRefCollectionItem )
+ {
+ const CEconItemView *pRefItem = pLocalInv->FindFirstItembyItemDef( pRefCollection->m_iItemDefs[ iRefCollectionItem ] );
+ if ( !pRefItem )
+ {
+ bOwned = false;
+ break;
+ }
+ }
+ }
+ else
+ {
+ // Normal Backpack scan
+ for ( int i = 0; i < pLocalInv->GetItemCount(); ++i )
+ {
+ CEconItemView *pItem = pLocalInv->GetItem( i );
+ if ( pItem->GetItemDefinition() == pTempItemDef )
+ {
+ bOwned = true;
+ // Check Quality
+ if ( pItem->GetQuality() == AE_UNUSUAL )
+ {
+ bUnusual = true;
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+#endif
+
+ const locchar_t * pCheckmark = bOwned ? pLocalizationProvider->Find( "TF_Checkmark" ) : pLocalizationProvider->Find( "TF_LackOfCheckmark" );
+ if ( bOwned && bUnusual )
+ {
+ pCheckmark = pLocalizationProvider->Find( "TF_Checkmark_Unusual" );
+ }
+
+ attrib_colors_t colorRarity = ATTRIB_COL_RARITY_DEFAULT;
+ const CEconItemRarityDefinition* pItemRarity = GetItemSchema()->GetRarityDefinition( pTempItemDef->GetRarity() );
+ if ( pItemRarity )
+ {
+ colorRarity = pItemRarity->GetAttribColor();
+ }
+
+ AddDescLine(
+ CConstructLocalizedString( LOCCHAR("%s1%s2"), pCheckmark,
+ CEconItemLocalizedFullNameGenerator(
+ pLocalizationProvider,
+ pTempItemDef,
+ m_pHashContext == NULL
+ ).GetFullName() ),
+ colorRarity,
+ eFlag,
+ NULL,
+ pTempItemDef->GetDefinitionIndex()
+ );
+ }
+ }
+}
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+void CEconItemDescription::Generate_ExpirationDesc( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem )
+{
+ Assert( pLocalizationProvider );
+ Assert( pEconItem );
+
+ const CEconItemDefinition *pItemDef = pEconItem->GetItemDefinition();
+ if ( !pItemDef )
+ return;
+
+ // Look for schema-specified static expiration date.
+ RTime32 timeSchemaExpiration = pItemDef->GetExpirationDate();
+
+ // Look also for a dynamic attribute -- this could come from item tryouts.
+ static CSchemaAttributeDefHandle pAttrDef_ExpirationDate( "expiration date" );
+
+ RTime32 timeAttrExpiration = 0;
+ pEconItem->FindAttribute( pAttrDef_ExpirationDate, &timeAttrExpiration ); // if we don't have the attribute we'll use our starting value of 0
+
+ // Which will have us expire first?
+ RTime32 timeExpiration = MAX( timeSchemaExpiration, timeAttrExpiration );
+
+ // If we still don't have an expiration date we don't display anything.
+ if ( !timeExpiration )
+ return;
+
+ AddEmptyDescLine();
+
+#ifdef TF_CLIENT_DLL
+ // is this a loaner item?
+ if ( GetAssociatedQuestItemID( pEconItem ) != INVALID_ITEM_ID )
+ {
+ AddDescLine( pLocalizationProvider->Find( "#Attrib_LoanerItemExpirationDate" ),
+ ATTRIB_COL_NEGATIVE,
+ kDescLineFlag_Misc );
+ }
+ else
+#endif // TF_CLIENT_DLL
+ {
+ CLocalizedRTime32 time = { timeExpiration, false, pLocalizationProvider TF_ANTI_IDLEBOT_VERIFICATION_ONLY_COMMA TF_ANTI_IDLEBOT_VERIFICATION_ONLY_ARG( m_pHashContext ) };
+ AddDescLine( CConstructLocalizedString( pLocalizationProvider->Find( "#Attrib_ExpirationDate" ),
+ time ),
+ ATTRIB_COL_NEGATIVE,
+ kDescLineFlag_Misc );
+ }
+}
+
+
+void CEconItemDescription::Generate_DropPeriodDesc( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem )
+{
+ Assert( pLocalizationProvider );
+ Assert( pEconItem );
+
+ static CSchemaAttributeDefHandle pAttr_EndDropDate( "end drop date" );
+
+ // See if we have the drop date period end attribute
+ CAttribute_String value;
+ if ( !FindAttribute( pEconItem, pAttr_EndDropDate, &value ) )
+ return;
+
+ AddEmptyDescLine();
+
+ // Convert the string value to an RTime32
+ RTime32 endDate = CRTime::RTime32FromString( value.value().c_str() );
+ // Is the time before or after now? Use different strings for each
+ const char* pszDropString = endDate > CRTime::RTime32TimeCur()
+ ? "#Attrib_DropPeriodComing"
+ : "#Attrib_DropPeriodPast";
+
+ CLocalizedRTime32 time = { endDate, false, pLocalizationProvider TF_ANTI_IDLEBOT_VERIFICATION_ONLY_COMMA TF_ANTI_IDLEBOT_VERIFICATION_ONLY_ARG( m_pHashContext ) };
+ AddDescLine( CConstructLocalizedString( pLocalizationProvider->Find( pszDropString ),
+ time ),
+ ATTRIB_COL_LEVEL,
+ kDescLineFlag_Misc );
+}
+
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+#ifdef TF_CLIENT_DLL
+ extern ConVar cl_showbackpackrarities;
+ extern ConVar cl_show_market_data_on_items;
+#endif
+
+void CEconItemDescription::Generate_MarketInformation( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem )
+{
+ // Deprecated;
+ // We now have right click go to market
+ return;
+//
+// Assert( pLocalizationProvider );
+// Assert( pEconItem );
+//
+// // Early-out to avoid doing more expensive name lookups for items that can't possibly have
+// // entries.
+// if ( !pEconItem->IsMarketable() )
+// return;
+//
+// // For now, Market information is only shown on clients, and even then only sometimes.
+//#ifdef CLIENT_DLL
+//#if TF_ANTI_IDLEBOT_VERIFICATION
+// // Don't generate this client-only information when we're trying to match GC output.
+// if ( m_pHashContext )
+// return;
+//#endif // TF_ANTI_IDLEBOT_VERIFICATION
+//
+//#ifdef TF_CLIENT_DLL
+// if ( cl_show_market_data_on_items.GetInt() == 0 )
+// return;
+//
+// if ( cl_show_market_data_on_items.GetInt() == 1 && cl_showbackpackrarities.GetInt() != 2 )
+// return;
+//#endif // TF_CLIENT_DLL
+//
+// steam_market_gc_identifier_t ident;
+// ident.m_unDefIndex = pEconItem->GetItemDefIndex();
+// ident.m_unQuality = pEconItem->GetQuality();
+//
+// const client_market_data_t *pClientMarketData = GetClientMarketData( ident );
+// if ( !pClientMarketData )
+// return;
+//
+// locchar_t loc_Price[ kLocalizedPriceSizeInChararacters ];
+// MakeMoneyString( loc_Price, ARRAYSIZE( loc_Price ), pClientMarketData->m_unLowestPrice, EconUI()->GetStorePanel()->GetCurrency() );
+//
+// AddEmptyDescLine();
+// AddDescLine( CConstructLocalizedString( pLocalizationProvider->Find( "#Econ_MarketTooltipFormat" ),
+// pClientMarketData->m_unQuantityAvailable,
+// loc_Price ),
+// ATTRIB_COL_POSITIVE,
+// kDescLineFlag_Misc );
+//#endif // CLIENT_DLL
+}
+
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+struct localized_localplayer_line_t
+{
+ localized_localplayer_line_t( const char *pLocalizationKey, attrib_colors_t eAttribColor, const char *pLocalizationSubKey = NULL )
+ : m_pLocalizationKey( pLocalizationKey )
+ , m_pLocalizationSubKey( pLocalizationSubKey )
+ , m_eAttribColor( eAttribColor )
+ {
+ //
+ }
+
+ const char *m_pLocalizationKey;
+ const char *m_pLocalizationSubKey;
+ attrib_colors_t m_eAttribColor;
+};
+
+void CEconItemDescription::Generate_FlagsAttributes( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem )
+{
+ Assert( pLocalizationProvider );
+ Assert( pEconItem );
+
+ const CEconItemDefinition *pItemDef = pEconItem->GetItemDefinition();
+ if ( pItemDef && pItemDef->GetEconTool() && pItemDef->GetEconTool()->ShouldDisplayQuantity( pEconItem ) )
+ {
+ AddEmptyDescLine();
+ AddDescLine( CConstructLocalizedString( pLocalizationProvider->Find( "#Attrib_LimitedUse" ), (uint32)pEconItem->GetQuantity() ),
+ ATTRIB_COL_LIMITED_USE,
+ kDescLineFlag_LimitedUse );
+ }
+
+ CUtlVector<localized_localplayer_line_t> vecLines;
+
+ // Is this item in use? (ie., being used as part of a cross-game trade)
+ if ( pEconItem->GetInUse() )
+ {
+ vecLines.AddToTail( localized_localplayer_line_t( "#Attrib_InUse", ATTRIB_COL_NEUTRAL ) );
+ }
+
+ static CSchemaAttributeDefHandle pAttrDef_TradableAfter( "tradable after date" );
+ static CSchemaAttributeDefHandle pAttrDef_ToolEscrowUntil( "tool escrow until date" );
+ static CSchemaAttributeDefHandle pAttrDef_AlwaysTradableAndUsableInCrafting( "always tradable" );
+
+ uint32 unTradeTime = 0,
+ unEscrowTime = 0;
+ const bool bHasTradableAfterDate = pEconItem->FindAttribute( pAttrDef_TradableAfter, &unTradeTime );
+ const bool bHasToolEscrowUntilDate = pEconItem->FindAttribute( pAttrDef_ToolEscrowUntil, &unEscrowTime );
+ const bool bHasExpiringTimer = bHasTradableAfterDate || bHasToolEscrowUntilDate;
+
+#ifdef CLIENT_DLL
+ const bool bIsStoreItem = IsStorePreviewItem( pEconItem );
+ const bool bIsPreviewItem = pEconItem->GetFlags() & kEconItemFlagClient_Preview;
+
+ if ( bIsStoreItem || bIsPreviewItem )
+ {
+#if TF_ANTI_IDLEBOT_VERIFICATION
+ if ( !m_pHashContext )
+#endif // TF_ANTI_IDLEBOT_VERIFICATION
+ {
+ // Does this item come with other packages on Steam?
+ const econ_store_entry_t *pStoreEntry = GetEconPriceSheet() ? GetEconPriceSheet()->GetEntry( pItemDef->GetDefinitionIndex() ) : NULL;
+ if ( pStoreEntry && pStoreEntry->GetGiftSteamPackageID() != 0 )
+ {
+ const char *pszSteamPackageLocalizationToken = GetItemSchema()->GetSteamPackageLocalizationToken( pStoreEntry->GetGiftSteamPackageID() );
+ if ( pszSteamPackageLocalizationToken )
+ {
+ vecLines.AddToTail( localized_localplayer_line_t( "#Attrib_Store_IncludesSteamGiftPackage", ATTRIB_COL_POSITIVE, pszSteamPackageLocalizationToken ) );
+ vecLines.AddToTail( localized_localplayer_line_t( NULL, ATTRIB_COL_POSITIVE ) );
+ }
+ }
+
+ // While the above apply to store *and* preview items, the below only apply to store items.
+ if ( bIsStoreItem )
+ {
+ // Don't display this line for map stamps because they can't be traded.
+ if ( pItemDef && pItemDef->GetItemClass() && !FStrEq( pItemDef->GetItemClass(), "map_token" ) )
+ {
+ static CSchemaAttributeDefHandle pAttrib_CannotTrade( "cannot trade" );
+ Assert( pAttrib_CannotTrade );
+
+ // Some items cannot ever be traded, so don't indicate to the users that they'll be tradeable after a few days.
+ if ( !FindAttribute( pItemDef, pAttrib_CannotTrade ) )
+ {
+ vecLines.AddToTail( localized_localplayer_line_t( "#Attrib_Store_TradableAfterDate", ATTRIB_COL_NEGATIVE ) );
+ }
+
+ if ( pItemDef->GetEconTool() && pItemDef->GetEconTool()->RequiresToolEscrowPeriod() )
+ {
+ vecLines.AddToTail( localized_localplayer_line_t( "#Attrib_Store_ToolEscrowUntilDate", ATTRIB_COL_NEGATIVE ) );
+ }
+ }
+
+ if ( !pItemDef || (pItemDef->GetCapabilities() & ITEM_CAP_CAN_BE_CRAFTED_IF_PURCHASED) == 0 )
+ {
+ if ( pItemDef->IsBundle() )
+ {
+ vecLines.AddToTail( localized_localplayer_line_t( "#Attrib_CannotCraftWeapons", ATTRIB_COL_NEGATIVE ) );
+ }
+ else
+ {
+ vecLines.AddToTail( localized_localplayer_line_t( "#Attrib_CannotCraft", ATTRIB_COL_NEGATIVE ) );
+ }
+ }
+ }
+ }
+ }
+ else
+#endif // CLIENT_DLL
+ if ( bHasExpiringTimer )
+ {
+ if ( unTradeTime > CRTime::RTime32TimeCur() )
+ {
+ AddAttributeDescription( pLocalizationProvider, pAttrDef_TradableAfter, unTradeTime );
+ }
+
+ if ( unEscrowTime > CRTime::RTime32TimeCur() )
+ {
+ AddAttributeDescription( pLocalizationProvider, pAttrDef_ToolEscrowUntil, unEscrowTime );
+ }
+
+ if ( !pEconItem->IsUsableInCrafting() )
+ {
+ vecLines.AddToTail( localized_localplayer_line_t( "#Attrib_CannotCraft", ATTRIB_COL_NEUTRAL ) );
+ }
+ }
+ else if ( pEconItem->FindAttribute( pAttrDef_AlwaysTradableAndUsableInCrafting ) && pEconItem->IsTradable() )
+ {
+ // do nothing if we are always tradable or usable in crafting
+ //
+ // some items are marked as "always_tradable" in their itemDef but the specific item may have the
+ // "non_economy" flag, so we need to also check that this specific item is tradable before doing nothing
+ }
+ else
+ {
+ const int32 iQuality = pEconItem->GetQuality();
+ const eEconItemOrigin eOrigin = pEconItem->GetOrigin();
+
+ if ( iQuality >= AE_COMMUNITY && iQuality <= AE_SELFMADE )
+ {
+ vecLines.AddToTail( localized_localplayer_line_t( "#Attrib_SpecialItem", ATTRIB_COL_NEUTRAL ) );
+ }
+ else if ( eOrigin == kEconItemOrigin_Achievement )
+ {
+ vecLines.AddToTail( localized_localplayer_line_t( "#Attrib_AchievementItem", ATTRIB_COL_NEUTRAL ) );
+ }
+ else if ( eOrigin == kEconItemOrigin_CollectionReward )
+ {
+ vecLines.AddToTail( localized_localplayer_line_t( "#Attrib_CollectionReward", ATTRIB_COL_NEUTRAL ) );
+ }
+ else if ( eOrigin == kEconItemOrigin_PreviewItem )
+ {
+ vecLines.AddToTail( localized_localplayer_line_t( "#Attrib_PreviewItem", ATTRIB_COL_NEUTRAL ) );
+ }
+ else if ( eOrigin == kEconItemOrigin_QuestLoanerItem )
+ {
+ vecLines.AddToTail( localized_localplayer_line_t( "#Attrib_LoanerItem", ATTRIB_COL_NEUTRAL ) );
+ }
+ else if ( eOrigin == kEconItemOrigin_Invalid )
+ {
+ // do nothing, but skip the below "cannot trade/cannot craft" block below"
+ }
+ else if ( (pEconItem->GetFlags() & kEconItemFlag_NonEconomy) != 0 )
+ {
+ vecLines.AddToTail( localized_localplayer_line_t( "#Attrib_NonEconomyItem", ATTRIB_COL_NEUTRAL ) );
+ }
+ else
+ {
+ const bool bIsTradable = pEconItem->IsTradable(),
+ bIsCraftable = pEconItem->IsUsableInCrafting();
+
+ if ( !bIsTradable && !bIsCraftable )
+ {
+ vecLines.AddToTail( localized_localplayer_line_t( "#Attrib_CannotTradeOrCraft", ATTRIB_COL_NEUTRAL ) );
+ }
+ else
+ {
+ if ( !bIsTradable )
+ {
+ vecLines.AddToTail( localized_localplayer_line_t( "#Attrib_CannotTrade", ATTRIB_COL_NEUTRAL ) );
+ }
+
+ if ( !bIsCraftable )
+ {
+ vecLines.AddToTail( localized_localplayer_line_t( "#Attrib_CannotCraft", ATTRIB_COL_NEUTRAL ) );
+ }
+ }
+ }
+ }
+
+ if ( vecLines.Count() > 0 )
+ {
+ const locchar_t *loc_AttribFormat_AdditionalNode = pLocalizationProvider->Find( "#AttribFormat_AdditionalNote" );
+ if ( loc_AttribFormat_AdditionalNode )
+ {
+ AddEmptyDescLine();
+
+ FOR_EACH_VEC( vecLines, i )
+ {
+ const char *pszLocalizationKey = vecLines[i].m_pLocalizationKey;
+ const char *pszLocalizationSubKey = vecLines[i].m_pLocalizationSubKey;
+
+ if ( pszLocalizationKey )
+ {
+ AddDescLine( pszLocalizationSubKey ?
+ CConstructLocalizedString( pLocalizationProvider->Find( pszLocalizationKey ), pLocalizationProvider->Find( pszLocalizationSubKey ) ): // has subtoken, doesn't use additional note format
+ CConstructLocalizedString( loc_AttribFormat_AdditionalNode, pLocalizationProvider->Find( pszLocalizationKey ) ), // no subtoken, uses base additional note format
+ vecLines[i].m_eAttribColor,
+ kDescLineFlag_Misc );
+ }
+ else
+ {
+ AddEmptyDescLine();
+ }
+ }
+ }
+ }
+}
+
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+
+bool CEconItemDescription::CVisibleAttributeDisplayer::OnIterateAttributeValue( const CEconItemAttributeDefinition *pAttrDef, attrib_value_t value )
+{
+ if ( !pAttrDef->IsHidden() )
+ {
+ attrib_iterator_value_t attrVal = { pAttrDef, value };
+ m_vecAttributes.AddToTail( attrVal );
+ }
+
+ return true;
+}
+
+void CEconItemDescription::CVisibleAttributeDisplayer::SortAttributes()
+{
+ // We need to make sure we process attributes in the same order when iterating on the GC and the client
+ // when looking for agreement. We take advantage of this to also sort our attributes into a coherent
+ // order for display -- first come neutral attributes, then positive, then negative. In the event of a
+ // tie, we sort by attribute index, which is arbitrary but consistent across the client/GC.
+ struct AttributeValueSorter
+ {
+ static int sSort( const attrib_iterator_value_t *pA, const attrib_iterator_value_t *pB )
+ {
+ const int iEffectTypeDelta = pA->m_pAttrDef->GetEffectType() - pB->m_pAttrDef->GetEffectType();
+ if ( iEffectTypeDelta != 0 )
+ return iEffectTypeDelta;
+
+ return pA->m_pAttrDef->GetDefinitionIndex() - pB->m_pAttrDef->GetDefinitionIndex();
+ }
+ };
+
+ m_vecAttributes.Sort( &AttributeValueSorter::sSort );
+}
+
+void CEconItemDescription::CVisibleAttributeDisplayer::Finalize( const IEconItemInterface *pEconItem, CEconItemDescription *pEconItemDescription, const CLocalizationProvider *pLocalizationProvider )
+{
+ // HACK so we dont show series number on select crates since they are self describing (Event Crates, Collection Crates)
+ static CSchemaAttributeDefHandle pAttrDef_SupplyCrateSeries( "set supply crate series" );
+ static CSchemaAttributeDefHandle pAttrDef_HideSeries( "hide crate series number" );
+
+ FOR_EACH_VEC( m_vecAttributes, i )
+ {
+ if ( pEconItem && m_vecAttributes[i].m_pAttrDef == pAttrDef_SupplyCrateSeries && pEconItem->FindAttribute( pAttrDef_HideSeries ) )
+ continue;
+
+ pEconItemDescription->AddAttributeDescription( pLocalizationProvider, m_vecAttributes[i].m_pAttrDef, m_vecAttributes[i].m_value );
+ }
+}
+
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+void CEconItemDescription::Generate_VisibleAttributes( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem )
+{
+ Assert( pLocalizationProvider );
+ Assert( pEconItem );
+
+ CVisibleAttributeDisplayer AttributeDisplayer;
+ pEconItem->IterateAttributes( &AttributeDisplayer );
+ AttributeDisplayer.SortAttributes();
+ AttributeDisplayer.Finalize( pEconItem, this, pLocalizationProvider );
+}
+
+// --------------------------------------------------------------------------
+void CEconItemDescription::Generate_DirectX8Warning( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem )
+{
+#ifdef CLIENT_DLL
+ static ConVarRef mat_dxlevel( "mat_dxlevel" );
+ const CEconItemDefinition *pEconItemDefinition = pEconItem->GetItemDefinition();
+ // If less than 90, we�re in DX8 mode.
+ // Display warning if you are looking at a painthit item or case
+ if ( mat_dxlevel.GetInt() < 90 && pEconItemDefinition && ( pEconItemDefinition->GetItemCollectionDefinition() || pEconItemDefinition->GetCollectionReference() ) )
+ {
+ AddEmptyDescLine();
+ AddDescLine( pLocalizationProvider->Find( "#Attrib_DirectX8Warning" ),
+ ATTRIB_COL_NEGATIVE,
+ kDescLineFlag_Misc );
+ }
+
+#endif
+}
+
+
+bool CEconItemDescription::CRecipeNameAttributeDisplayer::OnIterateAttributeValue( const CEconItemAttributeDefinition *pAttrDef, attrib_value_t value )
+{
+ if ( pAttrDef->CanAffectRecipeComponentName() )
+ {
+ return CVisibleAttributeDisplayer::OnIterateAttributeValue( pAttrDef, value );
+ }
+
+ return true;
+}
+
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+static attrib_colors_t GetAttributeDefaultColor( const CEconItemAttributeDefinition *pAttribDef )
+{
+ // positive attribute?
+ switch ( pAttribDef->GetEffectType() )
+ {
+ case ATTRIB_EFFECT_NEUTRAL: return ATTRIB_COL_NEUTRAL;
+ case ATTRIB_EFFECT_POSITIVE: return ATTRIB_COL_POSITIVE;
+ case ATTRIB_EFFECT_NEGATIVE: return ATTRIB_COL_NEGATIVE;
+ case ATTRIB_EFFECT_STRANGE: return ATTRIB_COL_STRANGE;
+ case ATTRIB_EFFECT_UNUSUAL: return ATTRIB_COL_UNUSUAL;
+ }
+
+ // hell if we know
+ return ATTRIB_COL_NEUTRAL;
+}
+
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+void CEconAttributeDescription::InternalConstruct
+(
+ const CLocalizationProvider *pLocalizationProvider,
+ const CEconItemAttributeDefinition *pAttribDef,
+ attrib_value_t value,
+ TF_ANTI_IDLEBOT_VERIFICATION_ONLY_ARG( MD5Context_t *pHashContext ) TF_ANTI_IDLEBOT_VERIFICATION_ONLY_COMMA
+ IAccountPersonaLocalizer *pOptionalAccountPersonaLocalizer
+)
+{
+ Assert( pAttribDef != NULL );
+
+ const float& value_as_float = (float&)value;
+ const uint32& value_as_uint32 = (uint32&)value;
+
+ // Calculate our color first -- if we don't know what to do, we'll wind up as neutral.
+ m_eDefaultColor = GetAttributeDefaultColor( pAttribDef );
+
+ // Early out abort if we don't have a localization string for this attribute.
+ locchar_t *loc_String = pAttribDef->GetDescriptionString() && pLocalizationProvider
+ ? pLocalizationProvider->Find( pAttribDef->GetDescriptionString() )
+ : NULL;
+
+ if ( !loc_String )
+ return;
+
+ char szAttrShortDescToken[MAX_PATH];
+ V_sprintf_safe( szAttrShortDescToken, "%s%s", pAttribDef->GetDescriptionString(), "_shortdesc" );
+
+ locchar_t *loc_ShortString = pLocalizationProvider
+ ? pLocalizationProvider->Find( szAttrShortDescToken )
+ : NULL;
+
+ // How do we format an attribute value of this type?
+ switch ( pAttribDef->GetDescriptionFormat() )
+ {
+ case ATTDESCFORM_VALUE_IS_ADDITIVE_PERCENTAGE:
+ m_loc_sValue = CLocalizedStringArg<float>( value_as_float * 100.0f ).GetLocArg();
+ break;
+
+ case ATTDESCFORM_VALUE_IS_ACCOUNT_ID:
+#ifdef CLIENT_DLL
+ // If this assert fires, it means that the client fed in an attribute that should be localized
+ // as a Steam persona name but didn't feed it any way to get that information. The GC won't
+ // assert, but also won't generate anything for the attribute text.
+ //
+ // It's still totally fine to pass in NULL for the persona localizer as long as you don't
+ // expect to have any attributes that have account IDs.
+ Assert( pOptionalAccountPersonaLocalizer );
+#endif
+ if ( pOptionalAccountPersonaLocalizer )
+ {
+ m_loc_sValue = pOptionalAccountPersonaLocalizer->FindAccountPersonaName( value_as_uint32 );
+ }
+ break;
+
+ case ATTDESCFORM_VALUE_IS_ADDITIVE:
+ m_loc_sValue = pAttribDef->IsStoredAsFloat()
+ ? CLocalizedStringArg<float>( value_as_float ).GetLocArg()
+ : CLocalizedStringArg<uint32>( value_as_uint32 ).GetLocArg();
+ break;
+
+ case ATTDESCFORM_VALUE_IS_INVERTED_PERCENTAGE:
+ if ( value_as_float < 1.0 )
+ {
+ m_loc_sValue = CLocalizedStringArg<float>( (1.0 - value_as_float) * 100.0f ).GetLocArg();
+ break;
+ }
+
+ // We intentionally fall through when value_as_float >= 1.0f to treat it the same as "value as
+ // percentage".
+ case ATTDESCFORM_VALUE_IS_PERCENTAGE:
+ m_loc_sValue = CLocalizedStringArg<float>( (value_as_float * 100.0f) - 100.0f ).GetLocArg();
+ break;
+
+ case ATTDESCFORM_VALUE_IS_DATE:
+ {
+ bool bUseGMT = false;
+
+#ifdef PROJECT_TF
+ static CSchemaAttributeDefHandle pAttribDef_SetEmployeeNumber( "custom employee number" );
+
+ // only use GMT for custom employee number -- not doing this generated a bunch of support
+ // tickets because items were granted based on GC time but would display local time, causing
+ // people on the border to think they deserved a better badge, etc.
+ bUseGMT = (pAttribDef == pAttribDef_SetEmployeeNumber);
+#endif // PROJECT_TF
+
+ CLocalizedRTime32 time = { value_as_uint32, bUseGMT, pLocalizationProvider TF_ANTI_IDLEBOT_VERIFICATION_ONLY_COMMA TF_ANTI_IDLEBOT_VERIFICATION_ONLY_ARG( pHashContext ) };
+ m_loc_sValue = CLocalizedStringArg<CLocalizedRTime32>( time ).GetLocArg();
+ break;
+ }
+
+ case ATTDESCFORM_VALUE_IS_PARTICLE_INDEX:
+ {
+ // This is a horrible, horrible line of code. It exists because old particle references are
+ // ints stored as floats as float bit patterns and new particle references are ints stored
+ // as ints all the way through.
+ CUtlConstString utf8_ParticleKeyName( CFmtStr( "#Attrib_Particle%i", pAttribDef->IsStoredAsInteger() ? value_as_uint32 : (int)value_as_float ).Access() ); // this value is stored as a float but interpreted as an int (1.0 -> 1)
+ if ( utf8_ParticleKeyName.IsEmpty() )
+ return;
+
+ m_loc_sValue = pLocalizationProvider->Find( utf8_ParticleKeyName.Get() );
+ break;
+ }
+
+ case ATTDESCFORM_VALUE_IS_KILLSTREAKEFFECT_INDEX:
+ {
+ CUtlConstString utf8_KeyName( CFmtStr( "#Attrib_KillStreakEffect%i", (int)value_as_float ).Access() ); // this value is stored as a float but interpreted as an int (1.0 -> 1)
+ if ( utf8_KeyName.IsEmpty() )
+ return;
+
+ m_loc_sValue = pLocalizationProvider->Find( utf8_KeyName.Get() );
+ break;
+ }
+
+ case ATTDESCFORM_VALUE_IS_KILLSTREAK_IDLEEFFECT_INDEX:
+ {
+ CUtlConstString utf8_KeyName( CFmtStr( "#Attrib_KillStreakIdleEffect%i", (int)value_as_float ).Access() ); // this value is stored as a float but interpreted as an int (1.0 -> 1)
+ if ( utf8_KeyName.IsEmpty() )
+ return;
+
+ m_loc_sValue = pLocalizationProvider->Find( utf8_KeyName.Get() );
+ break;
+ }
+ // Don't output any value for bitmasks, but let the attribute text display.
+ case ATTDESCFORM_VALUE_IS_OR:
+ break;
+
+ default:
+#ifdef CLIENT_DLL
+ // Only assert on the client -- the GC will just silently fail rather than crash if we ever run into
+ // this case, but if we are adding a new display type this will help us catch a reason why it isn't
+ // showing up.
+ Assert( !"Unhandled attribute value display type in CEconAttributeDescription." );
+
+ // Anywhere besides the client, we intentionally fall through to return immediately.
+#endif
+ case ATTDESCFORM_VALUE_IS_ITEM_DEF: // referencing definitions is handled per-attribute
+ return;
+
+ case ATTDESCFORM_VALUE_IS_FROM_LOOKUP_TABLE:
+ {
+ const char *pszLocalizationToken = GetItemSchema()->FindStringTableEntry( pAttribDef->GetDefinitionName(), (int)value_as_float );
+ if ( !pszLocalizationToken )
+ return;
+
+ const locchar_t *loc_Entry = pLocalizationProvider->Find( pszLocalizationToken );
+ if ( !loc_Entry )
+ return;
+
+ m_loc_sValue = loc_Entry;
+ break;
+ }
+ }
+
+ // Some attributes have a short description for the upgrade
+ if ( loc_ShortString )
+ {
+ m_loc_sShortValue = CConstructLocalizedString( loc_ShortString, m_loc_sValue.Get() );
+ }
+
+ // Combine the value string we just generated with the localized display for that value. (ie., the value
+ // might be "10" and the display would be "health is increased by 10%".)
+ m_loc_sValue = CConstructLocalizedString( loc_String, m_loc_sValue.Get() );
+
+ // Is this an attribute that needs a custom wrapper around the default attribute text? (ie.,
+ // if our string was "Damage +10%" we want that to be "(only on Hightower: Damage +10%)")
+ if ( pAttribDef->GetUserGenerationType() )
+ {
+ const locchar_t *locUGTLocalizationKey = pLocalizationProvider->Find( CFmtStr( "#Econ_Attrib_UserGeneratedWrapper_%i", pAttribDef->GetUserGenerationType() ).Get() );
+
+ if ( locUGTLocalizationKey )
+ {
+ m_loc_sValue = CConstructLocalizedString( locUGTLocalizationKey, m_loc_sValue.Get() );
+ }
+ }
+
+ // If there's no short description, just copy the normal one
+ if ( !loc_ShortString )
+ {
+ m_loc_sShortValue = m_loc_sValue;
+ }
+}
+
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+void CEconItemDescription::AddAttributeDescription( const CLocalizationProvider *pLocalizationProvider, const CEconItemAttributeDefinition *pAttribDef, attrib_value_t value, attrib_colors_t eOverrideDisplayColor /* = NUM_ATTRIB_COLORS */ )
+{
+ Assert( pLocalizationProvider );
+ Assert( pAttribDef );
+
+ CEconAttributeDescription AttrDesc( pLocalizationProvider,
+ pAttribDef,
+ value,
+ TF_ANTI_IDLEBOT_VERIFICATION_ONLY_ARG( m_pHashContext ) TF_ANTI_IDLEBOT_VERIFICATION_ONLY_COMMA
+ this );
+
+ if ( AttrDesc.GetDescription().IsEmpty() )
+ return;
+
+ // Is this an attribute that needs a custom wrapper around the default attribute text? (ie.,
+ // if our string was "Damage +10%" we want that to be "(only on Hightower: Damage +10%)")
+ attrib_colors_t eDefaultAttribColor = GetAttributeDefaultColor( pAttribDef );
+
+#ifdef TF_CLIENT_DLL
+ enum
+ {
+ kUserGeneratedAttributeType_None = 0,
+ kUserGeneratedAttributeType_MVMEngineering = 1,
+ kUserGeneratedAttributeType_HalloweenSpell = 2
+ };
+
+ // On TF, these user-generated attributes can be from upgrade cards which only apply in MvM.
+ // We then colorize them based on whether they'll be active, with the caveat that out-of-game
+ // views always say yes (GC, loadout when not on a server, etc.).
+ if ( pAttribDef->GetUserGenerationType() == kUserGeneratedAttributeType_MVMEngineering && TFGameRules() && !TFGameRules()->IsMannVsMachineMode() )
+ {
+ eDefaultAttribColor = ATTRIB_COL_ITEMSET_MISSING;
+ }
+ // They can also be from Halloween spells. These are intended to expire after Halloween in any
+ // event, but for display purposes they'll appear in grey unless the holiday is active.
+ else if ( pAttribDef->GetUserGenerationType() == kUserGeneratedAttributeType_HalloweenSpell && !EconHolidays_IsHolidayActive( kHoliday_Halloween, CRTime::RTime32TimeCur() ) )
+ {
+ eDefaultAttribColor = ATTRIB_COL_ITEMSET_MISSING;
+ }
+#endif // TF_CLIENT_DLL
+
+ AddDescLine( AttrDesc.GetDescription().Get(),
+ eOverrideDisplayColor != NUM_ATTRIB_COLORS ? // are we overriding the output color?
+ eOverrideDisplayColor : // we are
+ eDefaultAttribColor, // fall back to normal attribute color
+ kDescLineFlag_Attribute );
+}
+
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+void CEconItemDescription::AddDescLine( const locchar_t *pString, attrib_colors_t eColor, uint32 unMetaType, CUtlVector<econ_item_description_line_t> *out_pOptionalDescLineDest /* = NULL */, item_definition_index_t unDefIndex /* = INVALID_ITEM_DEF_INDEX */, bool bIsItemForSale /*= true*/ )
+{
+ CUtlVector<econ_item_description_line_t>& vecTargetDescLines = out_pOptionalDescLineDest ? *out_pOptionalDescLineDest : m_vecDescLines;
+
+ econ_item_description_line_t& line = vecTargetDescLines[ vecTargetDescLines.AddToTail() ];
+
+ line.eColor = eColor;
+ line.unMetaType = unMetaType;
+ line.sText = pString;
+ line.unDefIndex = unDefIndex;
+ line.bIsItemForSale = bIsItemForSale;
+
+#if TF_ANTI_IDLEBOT_VERIFICATION
+ if ( m_pHashContext )
+ {
+ const int iLineCount = vecTargetDescLines.Count() + 1;
+
+ char verboseStringBuf[ k_VerboseStringBufferSize ];
+ TFDescription_HashDataMunge( m_pHashContext, iLineCount, m_bIsVerbose, BuildVerboseStrings( verboseStringBuf, m_bIsVerbose, "%d", iLineCount ) ); // which line did we just add?
+ TFDescription_HashDataMunge( m_pHashContext, line.eColor, m_bIsVerbose, BuildVerboseStrings( verboseStringBuf, m_bIsVerbose,"%d", line.eColor ) );
+ TFDescription_HashDataMunge( m_pHashContext, line.unMetaType, m_bIsVerbose, BuildVerboseStrings( verboseStringBuf, m_bIsVerbose, "%d", line.unMetaType ) );
+
+#ifdef GC_DLL
+ COMPILE_TIME_ASSERT( sizeof( locchar_t ) == sizeof( char ) );
+ TFDescription_HashDataMungeContents( m_pHashContext, pString, StringFuncs<locchar_t>::Length( pString ) * sizeof( locchar_t ), m_bIsVerbose, pString );
+#else
+ COMPILE_TIME_ASSERT( sizeof( locchar_t ) == sizeof( wchar_t ) );
+
+ CUtlConstString ansiString;
+ GLocalizationProvider()->ConvertLoccharToANSI( pString, &ansiString );
+ TFDescription_HashDataMungeContents( m_pHashContext, ansiString.Get(), StringFuncs<char>::Length( ansiString.Get() ) * sizeof( char ), m_bIsVerbose, ansiString.Get() );
+#endif
+ }
+#endif // TF_ANTI_IDLEBOT_VERIFICATION
+}
+
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+void CEconItemDescription::AddEmptyDescLine( CUtlVector<econ_item_description_line_t> *out_pOptionalDescLineDest )
+{
+ AddDescLine( LOCCHAR(" "), ATTRIB_COL_NEUTRAL, kDescLineFlag_Empty, out_pOptionalDescLineDest );
+}
+
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+void CEconItemDescription::LocalizedAddDescLine( const CLocalizationProvider *pLocalizationProvider, const char *pLocalizationToken, attrib_colors_t eColor, uint32 unMetaType, CUtlVector<econ_item_description_line_t> *out_pOptionalDescLineDest /* = NULL */, item_definition_index_t unDefIndex /* = INVALID_ITEM_DEF_INDEX */, bool bIsItemForSale /* = true */ )
+{
+ Assert( pLocalizationToken );
+
+ const locchar_t *pTextToAdd = pLocalizationProvider->Find( pLocalizationToken );
+
+ if ( pTextToAdd )
+ {
+ AddDescLine( pTextToAdd, eColor, unMetaType, out_pOptionalDescLineDest, unDefIndex, bIsItemForSale );
+ }
+ else if ( pLocalizationToken && (pLocalizationToken[0] != '#') )
+ {
+ // If we couldn't localize correctly, we might be a string literal like "My temp item desc.". In
+ // this case, we use that string as-is.
+ CUtlConstStringBase<locchar_t> loc_sText;
+ pLocalizationProvider->ConvertUTF8ToLocchar( pLocalizationToken, &loc_sText );
+ AddDescLine( loc_sText.Get(), eColor, unMetaType, out_pOptionalDescLineDest, unDefIndex, bIsItemForSale );
+ }
+ else
+ {
+ // We couldn't localize this token, but also don't think it was a string meant to be user-facing so
+ // just silently fail.
+ }
+
+#if TF_ANTI_IDLEBOT_VERIFICATION
+ if ( m_pHashContext )
+ {
+ TFDescription_HashDataMungeContents( m_pHashContext, pLocalizationToken, V_strlen( pLocalizationToken ), m_bIsVerbose, pLocalizationToken );
+ }
+#endif // TF_ANTI_IDLEBOT_VERIFICATION
+}
+
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+class CGameItemDefinition_EconItemInterfaceWrapper : public CMaterialOverrideContainer< IEconItemInterface >
+{
+public:
+ CGameItemDefinition_EconItemInterfaceWrapper( const CEconItemDefinition *pEconItemDefinition, entityquality_t eQuality )
+ : m_pEconItemDefinition( pEconItemDefinition )
+ , m_eQuality( eQuality )
+ {
+ Assert( m_pEconItemDefinition );
+ }
+
+ virtual const GameItemDefinition_t *GetItemDefinition() const { return assert_cast<const GameItemDefinition_t *>( m_pEconItemDefinition ); }
+
+ virtual itemid_t GetID() const { return INVALID_ITEM_ID; }
+ virtual uint32 GetAccountID() const { return 0; }
+ virtual int32 GetQuality() const { return m_eQuality; }
+ virtual style_index_t GetStyle() const { return INVALID_STYLE_INDEX; }
+ virtual uint8 GetFlags() const { return 0; }
+ virtual eEconItemOrigin GetOrigin() const { return kEconItemOrigin_Invalid; }
+ virtual int GetQuantity() const { return 1; }
+ virtual uint32 GetItemLevel() const { return 0; }
+ virtual bool GetInUse() const { return false; }
+
+ virtual const char *GetCustomName() const { return NULL; }
+ virtual const char *GetCustomDesc() const { return NULL; }
+
+ virtual CEconItemPaintKitDefinition *GetCustomPainkKitDefinition( void ) const { return GetItemDefinition()->GetCustomPainkKitDefinition(); }
+
+ // 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( class IEconItemAttributeIterator *pIterator ) const OVERRIDE
+ {
+ Assert( pIterator );
+
+ m_pEconItemDefinition->IterateAttributes( pIterator );
+ }
+
+private:
+ const CEconItemDefinition *m_pEconItemDefinition;
+ entityquality_t m_eQuality;
+};
+
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+CEconItemLocalizedFullNameGenerator::CEconItemLocalizedFullNameGenerator( const CLocalizationProvider *pLocalizationProvider, const CEconItemDefinition *pItemDef, bool bUseProperName, entityquality_t eQuality )
+{
+ Assert( pItemDef );
+
+ CGameItemDefinition_EconItemInterfaceWrapper EconItemDefinitionWrapper( pItemDef, eQuality );
+ GenerateLocalizedFullItemName( m_loc_LocalizedItemName, pLocalizationProvider, &EconItemDefinitionWrapper, k_EGenerateLocalizedFullItemName_Default, bUseProperName );
+}
+
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+class CMarketNameGenerator_EconItemInterfaceWrapper : public IEconItemInterface
+{
+public:
+ CMarketNameGenerator_EconItemInterfaceWrapper( CEconItem *pItem )
+ : m_pItem( pItem )
+ {
+ Assert( m_pItem );
+ }
+
+ virtual const GameItemDefinition_t *GetItemDefinition() const { return m_pItem->GetItemDefinition(); }
+
+ virtual itemid_t GetID() const { return m_pItem->GetID(); }
+ virtual uint32 GetAccountID() const { return 0; }
+ virtual int32 GetQuality() const { return m_pItem->GetQuality(); }
+ virtual style_index_t GetStyle() const { return INVALID_STYLE_INDEX; }
+ virtual uint8 GetFlags() const { return 0; }
+ virtual eEconItemOrigin GetOrigin() const { return kEconItemOrigin_Invalid; }
+ virtual int GetQuantity() const { return 1; }
+ virtual uint32 GetItemLevel() const { return 0; }
+ virtual bool GetInUse() const { return false; }
+
+ virtual const char *GetCustomName() const { return NULL; }
+ virtual const char *GetCustomDesc() const { return NULL; }
+
+ virtual CEconItemPaintKitDefinition *GetCustomPainkKitDefinition( void ) const { return m_pItem->GetCustomPainkKitDefinition(); }
+ virtual bool GetCustomPaintKitWear( float &flWear ) const { return m_pItem->GetCustomPaintKitWear( flWear ); }
+
+ virtual IMaterial *GetMaterialOverride( int iTeam ) OVERRIDE { return m_pItem->GetMaterialOverride( iTeam ); }
+
+ // 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( class IEconItemAttributeIterator *pIterator ) const OVERRIDE
+ {
+ Assert( pIterator );
+
+ // Wrap their iterator in our iterator that will selectively let specific attributes
+ // get iterated on by the wrapped iterator.
+ CMarketNameGenerator_SelectiveAttributeIterator iteratorWrapper( pIterator );
+ m_pItem->IterateAttributes( &iteratorWrapper );
+ }
+
+private:
+
+ CEconItem *m_pItem;
+
+ // Iterator class that wraps another iterator and selectively allows specific attributes to be
+ // iterated by the passed in iterator
+ class CMarketNameGenerator_SelectiveAttributeIterator : public IEconItemAttributeIterator
+ {
+ public:
+ CMarketNameGenerator_SelectiveAttributeIterator( IEconItemAttributeIterator* pIterator )
+ : m_pIterator( pIterator )
+ {}
+
+ virtual bool OnIterateAttributeValue( const CEconItemAttributeDefinition *pAttrDef, attrib_value_t value ) OVERRIDE
+ {
+ if( pAttrDef->CanAffectMarketName() )
+ {
+ m_pIterator->OnIterateAttributeValue( pAttrDef, value );
+ }
+
+ return true;
+ }
+
+ virtual bool OnIterateAttributeValue( const CEconItemAttributeDefinition *pAttrDef, float value ) OVERRIDE
+ {
+ if( pAttrDef->CanAffectMarketName() )
+ {
+ m_pIterator->OnIterateAttributeValue( pAttrDef, value );
+ }
+
+ return true;
+ }
+
+ virtual bool OnIterateAttributeValue( const CEconItemAttributeDefinition *pAttrDef, const uint64& value ) OVERRIDE
+ {
+ if( pAttrDef->CanAffectMarketName() )
+ {
+ m_pIterator->OnIterateAttributeValue( pAttrDef, value );
+ }
+
+ return true;
+ }
+
+ virtual bool OnIterateAttributeValue( const CEconItemAttributeDefinition *pAttrDef, const CAttribute_String& value ) OVERRIDE
+ {
+ if( pAttrDef->CanAffectMarketName() )
+ {
+ m_pIterator->OnIterateAttributeValue( pAttrDef, value );
+ }
+
+ return true;
+ }
+
+ virtual bool OnIterateAttributeValue( const CEconItemAttributeDefinition *pAttrDef, const CAttribute_DynamicRecipeComponent& value ) OVERRIDE
+ {
+ if( pAttrDef->CanAffectMarketName() )
+ {
+ m_pIterator->OnIterateAttributeValue( pAttrDef, value );
+ }
+
+ return true;
+ }
+
+ virtual bool OnIterateAttributeValue( const CEconItemAttributeDefinition *pAttrDef, const CAttribute_ItemSlotCriteria& value ) OVERRIDE
+ {
+ if( pAttrDef->CanAffectMarketName() )
+ {
+ m_pIterator->OnIterateAttributeValue( pAttrDef, value );
+ }
+
+ return true;
+ }
+
+ virtual bool OnIterateAttributeValue( const CEconItemAttributeDefinition *pAttrDef, const CAttribute_WorldItemPlacement& value ) OVERRIDE
+ {
+ if ( pAttrDef->CanAffectMarketName() )
+ {
+ m_pIterator->OnIterateAttributeValue( pAttrDef, value );
+ }
+
+ return true;
+ }
+
+ private:
+
+ IEconItemAttributeIterator *m_pIterator;
+ };
+};
+
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+CEconItemLocalizedMarketNameGenerator::CEconItemLocalizedMarketNameGenerator( const CLocalizationProvider *pLocalizationProvider, CEconItem *pItem, bool bUseProperName )
+{
+ Assert( pItem );
+
+ CMarketNameGenerator_EconItemInterfaceWrapper EconItemWrapper( pItem );
+ GenerateLocalizedFullItemName( m_loc_LocalizedItemName, pLocalizationProvider, &EconItemWrapper, k_EGenerateLocalizedFullItemName_WithPaintWear, bUseProperName );
+}
+
+#endif // BUILD_ITEM_NAME_AND_DESC
diff --git a/game/shared/econ/econ_item_description.h b/game/shared/econ/econ_item_description.h
new file mode 100644
index 0000000..f900974
--- /dev/null
+++ b/game/shared/econ/econ_item_description.h
@@ -0,0 +1,536 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+
+#ifndef ECONITEMDESCRIPTION_H
+#define ECONITEMDESCRIPTION_H
+
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "localization_provider.h" // needed for locchar_t type
+
+#if defined( TF_DLL ) || defined( TF_CLIENT_DLL ) || defined( TF_GC_DLL )
+ #define PROJECT_TF
+#endif
+
+#define TF_ANTI_IDLEBOT_VERIFICATION defined( PROJECT_TF )
+
+#if TF_ANTI_IDLEBOT_VERIFICATION
+ #define TF_ANTI_IDLEBOT_VERIFICATION_ONLY_COMMA ,
+ #define TF_ANTI_IDLEBOT_VERIFICATION_ONLY_ARG( arg ) arg
+#else
+ #define TF_ANTI_IDLEBOT_VERIFICATION_ONLY_COMMA
+ #define TF_ANTI_IDLEBOT_VERIFICATION_ONLY_ARG( arg )
+#endif
+
+#if TF_ANTI_IDLEBOT_VERIFICATION
+ #include "checksum_md5.h"
+ #include "tf_gcmessages.pb.h"
+ #include "tf_gcmessages.h"
+#ifdef CLIENT_DLL
+ #include "gc_clientsystem.h"
+#endif // CLIENT_DLL
+
+#endif // TF_ANTI_IDLEBOT_VERIFICATION
+
+#ifdef GC_DLL
+#include "gcsdk/gclogger.h"
+using namespace GCSDK;
+#endif
+
+class IEconItemInterface;
+namespace GCSDK
+{
+ class CSharedObjectTypeCache;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Generate a description block for an IEconItemInterface. What the
+// client does with the description is anyone's guess, but this will
+// generate a block of UTF16 lines of text with meta/color data that
+// can be used for whatever.
+//-----------------------------------------------------------------------------
+
+enum EDescriptionLineMetaFlags
+{
+ kDescLineFlag_Name = 0x001, // the item name (can be renamed by user)
+ kDescLineFlag_Type = 0x002, // the item type (ie., "Level 5 Rocket Launcher")
+ kDescLineFlag_Desc = 0x004, // base item description (description from the item definition, level, etc.)
+ kDescLineFlag_Attribute = 0x008, // some sort of gameplay-affecting attribute
+ kDescLineFlag_Misc = 0x010, // not an attribute, not name/level
+ kDescLineFlag_Empty = 0x020, // line with no content that needs to be displayed; meant for spacing
+ kDescLineFlag_Set = 0x040, // this line is associated with item sets somehow
+ kDescLineFlag_LimitedUse= 0x080, // this is a limited use item
+ kDescLineFlag_SetName = 0x100, // this line is the title for an item set
+ kDescLineFlag_Collection = 0x200, // this line is associated with item collections
+ kDescLineFlag_CollectionCurrentItem = 0x400, // this line is the current item being describe
+ kDescLineFlag_CollectionName = 0x800, // this line is the collection name
+
+ kDescLineFlagSet_DisplayInAttributeBlock = ~(kDescLineFlag_Name | kDescLineFlag_Type),
+};
+
+struct econ_item_description_line_t
+{
+ attrib_colors_t eColor; // desired color type for this line -- will likely be looked up either in VGUI or in the schema
+ uint32 unMetaType; // type information for this line -- "item name"? "item level"?; etc.; can be game-specific
+ CUtlConstStringBase<locchar_t> sText; // actual text for this, post-localization
+ item_definition_index_t unDefIndex; // item def index for description lines which represent names of other items (used by bundles)
+ bool bIsItemForSale; // if this line is an item (eg in the case where a bundle description lists its contained items) - is the given item for sale?
+};
+
+class IEconItemDescription
+{
+public:
+ // This may yield on the GC and should never yield on the client.
+ static void YieldingFillOutEconItemDescription( IEconItemDescription *out_pDescription, CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem );
+
+public:
+ IEconItemDescription() { }
+ virtual ~IEconItemDescription() { }
+
+ uint32 GetLineCount() const { return m_vecDescLines.Count(); }
+ const econ_item_description_line_t& GetLine( int i ) const { return m_vecDescLines[i]; }
+
+ // Finds and returns the first line with *all* of the passed in search flags. Will return NULL if a line
+ // will all of the flags cannot be found.
+ const econ_item_description_line_t *GetFirstLineWithMetaType( uint32 unMetaTypeSearchFlags ) const;
+
+private:
+ // When generating an item description, this is guaranteed to be called once and only once before GenerateDescription()
+ // is called. Any data that may yield but will be needed somewhere deep inside GenerateDescription() should be determined
+ // and cached off here.
+ virtual void YieldingCacheDescriptionData( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem ) { }
+
+ // Take the properties off our pEconItem, and anything that we calculated in YieldingCacheDescriptionData() above and
+ // fill out all of our description lines.
+ virtual void GenerateDescriptionLines( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem ) = 0;
+
+protected:
+ CUtlVector<econ_item_description_line_t> m_vecDescLines;
+};
+
+// This will be defined as either 1 or 0 depending on which project we're in. We test its value explicitly
+// rather than just checking defined() because otherwise failing to include this header file will silently
+// result in it appearing to be undefined.
+#define BUILD_ITEM_NAME_AND_DESC (defined( CLIENT_DLL ) || defined( GC_DLL ))
+
+#if BUILD_ITEM_NAME_AND_DESC
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+class IAccountPersonaLocalizer
+{
+public:
+ virtual const locchar_t *FindAccountPersonaName( uint32 unAccountID ) const = 0;
+};
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+class CEconItemDescription : public IEconItemDescription, public IAccountPersonaLocalizer
+{
+public:
+ // Instances should be filled out via YieldingFillOutEconItemDescription().
+ CEconItemDescription()
+#if TF_ANTI_IDLEBOT_VERIFICATION
+ : m_pHashContext( NULL )
+ , m_bIsVerbose( false )
+#ifdef GC_DLL
+ , m_bTextModeEnabled( false )
+#else // if defined( CLIENT_DLL )
+ , m_bUnknownPlayer( false )
+#endif // GC_DLL
+#endif // TF_ANTI_IDLEBOT_VERIFICATION
+ {
+ //
+ }
+
+ // External helper interface, also used internally. This should only be used to add lines
+ // that are not a part of the properties of the item, but are instead a part of the environment
+ // around the item (ie., "this item cannot be equipped in this slot because another item is
+ // equipped that has conflicting regions").
+ //
+ // The final argument is an optional target array to use for the description lines instead of
+ // our internal storage. We can use this to queue up and then batch-submit/-discard lines. Passing
+ // in NULL means "use the internal array".
+ virtual void AddDescLine( const locchar_t *pString, attrib_colors_t eColor, uint32 unMetaType, CUtlVector<econ_item_description_line_t> *out_pOptionalDescLineDest = NULL, item_definition_index_t unDefIndex = INVALID_ITEM_DEF_INDEX, bool bIsItemForSale = true );
+ virtual void AddEmptyDescLine( CUtlVector<econ_item_description_line_t> *out_pOptionalDescLineDest = NULL );
+ virtual void LocalizedAddDescLine( const CLocalizationProvider *pLocalizationProvider, const char *pLocalizationToken, attrib_colors_t eColor, uint32 unMetaType, CUtlVector<econ_item_description_line_t> *out_pOptionalDescLineDest = NULL, item_definition_index_t unDefIndex = INVALID_ITEM_DEF_INDEX, bool bIsItemForSale = true );
+
+ // A helper class to iterate all attributes that we expect to appear on an item description. This
+ // is useable from outside CEconItemDescription. Attributes can be accessed in iteration order or
+ // manually sorted to be grouped by positive/negative status, etc.
+ class CVisibleAttributeDisplayer : public IEconItemAttributeIterator
+ {
+ public:
+ virtual bool OnIterateAttributeValue( const CEconItemAttributeDefinition *pAttrDef, attrib_value_t value ) OVERRIDE;
+
+ virtual bool OnIterateAttributeValue( const CEconItemAttributeDefinition *pAttrDef, float value ) OVERRIDE
+ {
+ return true;
+ }
+
+ virtual bool OnIterateAttributeValue( const CEconItemAttributeDefinition *pAttrDef, const uint64& value ) OVERRIDE
+ {
+ return true;
+ }
+
+ virtual bool OnIterateAttributeValue( const CEconItemAttributeDefinition *pAttrDef, const CAttribute_String& value ) OVERRIDE
+ {
+ return true;
+ }
+
+ virtual bool OnIterateAttributeValue( const CEconItemAttributeDefinition *pAttrDef, const CAttribute_DynamicRecipeComponent& value ) OVERRIDE
+ {
+ // Don't show these
+ return true;
+ }
+
+ virtual bool OnIterateAttributeValue( const CEconItemAttributeDefinition *pAttrDef, const CAttribute_ItemSlotCriteria& value ) OVERRIDE
+ {
+ // Don't show these
+ return true;
+ }
+
+ virtual bool OnIterateAttributeValue( const CEconItemAttributeDefinition *pAttrDef, const CAttribute_WorldItemPlacement& value ) OVERRIDE
+ {
+ // Don't show these
+ return true;
+ }
+
+ void SortAttributes();
+ void Finalize( const IEconItemInterface *pEconItem, CEconItemDescription *pEconItemDescription, const CLocalizationProvider *pLocalizationProvider );
+
+ private:
+ struct attrib_iterator_value_t
+ {
+ const CEconItemAttributeDefinition *m_pAttrDef;
+ attrib_value_t m_value;
+ };
+
+ CUtlVector<attrib_iterator_value_t> m_vecAttributes;
+ };
+
+ class CRecipeNameAttributeDisplayer : public CVisibleAttributeDisplayer
+ {
+ public:
+ virtual bool OnIterateAttributeValue( const CEconItemAttributeDefinition *pAttrDef, attrib_value_t value ) OVERRIDE;
+ };
+
+#if TF_ANTI_IDLEBOT_VERIFICATION
+ void SetHashContext( MD5Context_t *pHashContext )
+ {
+ AssertMsg( pHashContext == NULL || m_pHashContext == NULL, "Only one hash context allowed per item description!" );
+
+ m_pHashContext = pHashContext;
+ }
+
+ void SetVerbose( bool bIsVerbose )
+ {
+ m_bIsVerbose = bIsVerbose;
+ }
+
+#ifdef GC_DLL
+ void SetHashGCTextModeEnabled( bool bTextModeEnabled )
+ {
+ m_bTextModeEnabled = bTextModeEnabled;
+ }
+#endif // GC_DLL
+#endif // TF_ANTI_IDLEBOT_VERIFICATION
+
+#ifdef GC_DLL
+ bool HasUnknownPlayer( ) const
+ {
+ return false;
+ }
+#else // if defined( CLIENT_DLL )
+ bool HasUnknownPlayer( ) const
+ {
+ return m_bUnknownPlayer;
+ }
+#endif
+
+private:
+ // IEconItemDescription interface.
+ virtual void YieldingCacheDescriptionData( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem );
+ virtual void GenerateDescriptionLines( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem );
+
+private:
+ // Internal.
+ virtual void AddAttributeDescription( const CLocalizationProvider *pLocalizationProvider, const CEconItemAttributeDefinition *pAttribDef, attrib_value_t value, attrib_colors_t eOverrideDisplayColor = NUM_ATTRIB_COLORS );
+
+ virtual void Generate_ItemName( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem );
+ virtual void Generate_ItemLevelDesc( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem );
+#if defined( STAGING_ONLY ) && defined( CLIENT_DLL )
+ virtual void Generate_DebugInformation( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem );
+#endif // defined( DEBUG ) && defined( CLIENT_DLL )
+ virtual void Generate_CraftTag( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem );
+ virtual void Generate_StyleDesc( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem );
+ virtual void Generate_HolidayRestriction( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem );
+ virtual void Generate_QualityDesc( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem );
+ virtual void Generate_ItemRarityDesc( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem );
+ virtual void Generate_WearAmountDesc( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem );
+ virtual void Generate_ItemDesc( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem );
+ virtual void Generate_Bundle( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem );
+ virtual void Generate_GiftedBy( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem );
+#ifdef PROJECT_TF
+ virtual void Generate_DuelingMedal( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem );
+ virtual void Generate_MapContributor( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem );
+ virtual void Generate_FriendlyHat( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem );
+ virtual void Generate_SaxxyAwardDesc( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem );
+ virtual void Generate_MvmChallenges( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem );
+ virtual void Generate_SquadSurplusClaimedBy( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem );
+ virtual void Generate_DynamicRecipe( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem );
+ virtual void Generate_Leaderboard( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem );
+#endif // PROJECT_TF
+ virtual void Generate_XifierToolTargetItem( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem );
+ virtual void Generate_Painted( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem );
+ virtual void Generate_Uses( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem );
+ virtual void Generate_LootListDesc( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem );
+ virtual void Generate_EventDetail( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem );
+ virtual void Generate_ItemSetDesc( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem );
+ virtual void Generate_CollectionDesc( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem );
+ virtual void Generate_ExpirationDesc( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem );
+ virtual void Generate_MarketInformation( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem );
+ virtual void Generate_FlagsAttributes( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem );
+ virtual void Generate_DropPeriodDesc( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem );
+
+ virtual void Generate_VisibleAttributes( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem );
+ virtual void Generate_DirectX8Warning( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem );
+
+ // Helpers for the above.
+ virtual void Generate_ItemLevelDesc_Default( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem, const locchar_t *locTypename );
+ virtual bool BGenerate_ItemLevelDesc_StrangeNameAndStats( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem, const locchar_t *locTypename ); // returns true if generated a level/desc based on strange stats or false if nothing was generated
+ const locchar_t *GetLocalizedStringForStrangeRestrictionAttr( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem, int iAttrIndex ) const;
+
+ // Internal data.
+ void YieldingFillOutAccountPersonaName( const CLocalizationProvider *pLocalizationProvider, uint32 unAccountID );
+ const locchar_t *FindAccountPersonaName( uint32 unAccountID ) const;
+
+ void YieldingFillOutAccountTypeCache( uint32 unAccountID, int nClassID );
+ GCSDK::CSharedObjectTypeCache *FindAccountTypeCache( uint32 unAccountID, int nClassID ) const;
+
+ // Defined in source file -- not meant for external access.
+ template < typename T >
+ const T *FindAccountTypeCacheSingleton( uint32 unAccountID, int nClassID ) const;
+
+ // Precache data.
+ struct steam_account_persona_name_t
+ {
+ uint32 unAccountID;
+ CUtlConstStringBase<locchar_t> loc_sPersonaName;
+ };
+
+ CUtlVector<steam_account_persona_name_t> vecPersonaNames;
+
+ struct steam_account_type_cache_t
+ {
+ uint32 unAccountID;
+ int nClassID;
+ GCSDK::CSharedObjectTypeCache *pTypeCache;
+ };
+
+ CUtlVector<steam_account_type_cache_t> vecTypeCaches;
+
+#if TF_ANTI_IDLEBOT_VERIFICATION
+ MD5Context_t *m_pHashContext;
+ bool m_bIsVerbose;
+#ifdef GC_DLL
+ bool m_bTextModeEnabled;
+#else // if defined( CLIENT_DLL )
+ bool m_bUnknownPlayer;
+#endif // GC_DLL
+#endif // TF_ANTI_IDLEBOT_VERIFICATION
+};
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+class CEconAttributeDescription
+{
+private:
+ // Internal constructor.
+ void InternalConstruct
+ (
+ const CLocalizationProvider *pLocalizationProvider,
+ const CEconItemAttributeDefinition *pAttribDef,
+ attrib_value_t value,
+ TF_ANTI_IDLEBOT_VERIFICATION_ONLY_ARG( MD5Context_t *pHashContext ) TF_ANTI_IDLEBOT_VERIFICATION_ONLY_COMMA
+ IAccountPersonaLocalizer *pOptionalAccountPersonaLocalizer
+ );
+
+public:
+ // Outward-facing constructor. Pass in whatever you want for "value" and we'll
+ // use the raw bits for their value interpreted however the attribute says.
+ template < typename T >
+ CEconAttributeDescription
+ (
+ const CLocalizationProvider *pLocalizationProvider,
+ const CEconItemAttributeDefinition *pAttribDef,
+ T value,
+ TF_ANTI_IDLEBOT_VERIFICATION_ONLY_ARG( MD5Context_t *pHashContext = NULL ) TF_ANTI_IDLEBOT_VERIFICATION_ONLY_COMMA
+ IAccountPersonaLocalizer *pOptionalAccountPersonaLocalizer = NULL
+ )
+ {
+ COMPILE_TIME_ASSERT( sizeof( T ) == sizeof( attrib_value_t ) );
+
+ InternalConstruct( pLocalizationProvider, pAttribDef, *(attrib_value_t *)&value, TF_ANTI_IDLEBOT_VERIFICATION_ONLY_ARG( pHashContext ) TF_ANTI_IDLEBOT_VERIFICATION_ONLY_COMMA pOptionalAccountPersonaLocalizer );
+ }
+
+ const CUtlConstStringBase<locchar_t>& GetDescription() const { return m_loc_sValue; }
+ const CUtlConstStringBase<locchar_t>& GetShortDescription() const { return m_loc_sShortValue; }
+ attrib_colors_t GetDefaultColor() const { return m_eDefaultColor; }
+
+private:
+ CUtlConstStringBase<locchar_t> m_loc_sValue;
+ CUtlConstStringBase<locchar_t> m_loc_sShortValue;
+ attrib_colors_t m_eDefaultColor;
+};
+
+//-----------------------------------------------------------------------------
+// Purpose: control how item name is generated
+//-----------------------------------------------------------------------------
+enum EGenerateLocalizedFullItemNameFlag_t
+{
+ k_EGenerateLocalizedFullItemName_Default = 0,
+ k_EGenerateLocalizedFullItemName_WithPaintWear = ( 1 << 0 ),
+ k_EGenerateLocalizedFullItemName_WithoutCustomName = ( 1 << 1 ),
+ k_EGenerateLocalizedFullItemName_WithoutQuality = ( 1 << 2 ),
+ k_EGenerateLocalizedFullItemName_WithPaintkitNoItem = ( 1 << 3 ),
+};
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+class CEconItemLocalizedFullNameGenerator
+{
+public:
+ CEconItemLocalizedFullNameGenerator( const CLocalizationProvider *pLocalizationProvider, const CEconItemDefinition *pItemDef, bool bUseingHashContext = true, entityquality_t eQuality = AE_UNIQUE );
+
+ const locchar_t *GetFullName() const { return m_loc_LocalizedItemName; }
+
+private:
+ locchar_t m_loc_LocalizedItemName[ MAX_ITEM_NAME_LENGTH ];
+};
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+class CEconItemLocalizedMarketNameGenerator
+{
+public:
+ CEconItemLocalizedMarketNameGenerator( const CLocalizationProvider *pLocalizationProvider, CEconItem *pItem, bool bUseingHashContext = true );
+
+ const locchar_t *GetFullName() const { return m_loc_LocalizedItemName; }
+
+private:
+ locchar_t m_loc_LocalizedItemName[ MAX_ITEM_NAME_LENGTH ];
+};
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+class CSteamAccountIDAttributeCollector : public CEconItemSpecificAttributeIterator
+{
+public:
+ virtual bool OnIterateAttributeValue( const CEconItemAttributeDefinition *pAttrDef, attrib_value_t value ) OVERRIDE
+ {
+ if ( pAttrDef->GetDescriptionFormat() == ATTDESCFORM_VALUE_IS_ACCOUNT_ID )
+ {
+ m_vecSteamAccountIDs.AddToTail( value );
+ }
+
+ return true;
+ }
+
+ // Data access.
+ const CUtlVector<uint32>& GetAccountIDs()
+ {
+ return m_vecSteamAccountIDs;
+ }
+
+private:
+ CUtlVector<uint32> m_vecSteamAccountIDs;
+};
+
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+#if TF_ANTI_IDLEBOT_VERIFICATION
+
+#include "checksum_md5.h"
+
+enum
+{
+ kTFDescriptionHash_TextmodeArbitraryKey = 0x19a04480,
+ kTFDescriptionHash_ValidArbitraryKey = 0xa0939180,
+ kTFDescriptionHash_MultiRunArbitraryKey = 0x5790a31d,
+ kTFDescriptionHash_ChallengeXorShenanigans = 0x1870f0d2,
+};
+
+// Global function/variable names show up in Mac binaries so we give them names that will stand out less
+// here and then #define them back so the code is readable.
+#define TF_Description_HashDataMungeContents CompressFragments
+inline void TFDescription_HashDataMungeContents( MD5Context_t *out_pContext, const void *pContents, size_t unContentLength, bool bIsVerbose, const char* pszInfo )
+{
+ Assert( out_pContext );
+ Assert( pContents );
+
+ MD5Update( out_pContext, static_cast<const uint8 *>( pContents ), unContentLength );
+
+ // if Verbose, report the contents to the GC
+ if ( bIsVerbose )
+ {
+ MD5Context_t md5ContextEx = *out_pContext;
+ MD5Value_t md5ResultEx;
+ MD5Final( &md5ResultEx.bits[0], &md5ContextEx );
+
+#ifdef GC_DLL
+ EmitInfo( SPEW_GC, SPEW_ALWAYS, LOG_ALWAYS, "Verbose Verification GC : [ %s ] - [ %s ] \n", MD5_Print( md5ResultEx.bits, MD5_DIGEST_LENGTH ), pszInfo );
+#else
+ // Client reports this to the GC
+ GCSDK::CProtoBufMsg<CGCMsgTFSyncEx> msgResponse( k_EMsgGC_ClientVerificationVerboseResponse );
+ msgResponse.Body().set_version_checksum( pszInfo ); // before
+ msgResponse.Body().set_version_checksum_ex( &md5ResultEx.bits[0], MD5_DIGEST_LENGTH ); // after
+ GCClientSystem()->BSendMessage( msgResponse );
+#endif
+ //delete [] pArr;
+ }
+}
+
+// Okay, this one is actually just a helper macro.
+#define TFDescription_HashDataMunge( context, field, bIsVerbose, pszInfo ) \
+ { \
+ TFDescription_HashDataMungeContents( context, (void *)&field, sizeof( field ), bIsVerbose, pszInfo ); \
+ }
+
+#endif // TF_ANTI_IDLEBOT_VERIFICATION
+
+#endif // BUILD_ITEM_NAME_AND_DESC
+
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+struct CLocalizedRTime32
+{
+ RTime32 m_unTime;
+ bool m_bForceGMTOnClient; // display this time in GMT on the client? by default, clients show local time; the GC will ignore this flag and always display GMT
+ const CLocalizationProvider *m_pLocalizationProvider;
+ TF_ANTI_IDLEBOT_VERIFICATION_ONLY_ARG( MD5Context_t *m_pHashContext; )
+};
+
+template < >
+class CLocalizedStringArg<CLocalizedRTime32>
+{
+public:
+ enum { kIsValid = true };
+
+ CLocalizedStringArg( const CLocalizedRTime32& cTimeIn );
+
+ const locchar_t *GetLocArg() const { return m_Str.Get(); }
+
+private:
+ CUtlConstStringBase<locchar_t> m_Str;
+};
+
+#endif // ECONITEMDESCRIPTION_H
diff --git a/game/shared/econ/econ_item_factory.cpp b/game/shared/econ/econ_item_factory.cpp
new file mode 100644
index 0000000..426ce85
--- /dev/null
+++ b/game/shared/econ/econ_item_factory.cpp
@@ -0,0 +1,331 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: EconItemFactory: Manages rolling for items requested by the game server
+//
+//=============================================================================
+
+#include "cbase.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+#include "econ/econ_assetapi_context.h"
+
+
+using namespace GCSDK;
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor
+//-----------------------------------------------------------------------------
+CEconItemFactory::CEconItemFactory( )
+ : m_ulNextObjID( 0 )
+ , m_bIsInitialized( false )
+{
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Initializes the item factory and schema. Return false if init failed
+//-----------------------------------------------------------------------------
+bool CEconItemFactory::BYieldingInit()
+{
+ CUtlVector< CUtlString > vecErrors;
+ bool bRet = m_schema.BInit( "scripts/items/unencrypted/items_master.txt", "GAME", &vecErrors );
+
+ FOR_EACH_VEC( vecErrors, nError )
+ {
+ EmitError( SPEW_GC, "%s\n", vecErrors[nError].Get() );
+ }
+
+ static const char *pchMaxIDQuery = "SELECT MAX( ID ) FROM "
+ "( select max(ID) AS ID FROM Item UNION SELECT MAX(ID) AS ID FROM ForeignItem ) as tbl";
+
+ CSQLAccess sqlAccess;
+ if( !sqlAccess.BYieldingExecuteSingleResult<uint64, uint64>( NULL, pchMaxIDQuery, k_EGCSQLType_int64, &m_ulNextObjID, NULL ) )
+ {
+ EmitError( SPEW_GC, "Failed to read max item ID" );
+ return false;
+ }
+ m_ulNextObjID++; // our next ID is one past the current max ID
+
+ m_bIsInitialized = bRet;
+ return bRet;
+}
+
+static const CEconItemQualityDefinition *GetQualityDefinitionForItemCreation( const CItemSelectionCriteria *pOptionalCriteria, const CEconItemDefinition *pItemDef )
+{
+ Assert( pItemDef );
+
+ // Do we have a quality specified? If so, is it a valid quality? If not, we fall back to the
+ // quality specified by the item definition, the schema, etc.
+ uint8 unQuality = k_unItemQuality_Any;
+
+ // Quality specified in generation request via criteria?
+ if ( pOptionalCriteria && pOptionalCriteria->BQualitySet() )
+ {
+ unQuality = pOptionalCriteria->GetQuality();
+ }
+
+ // If not: quality specified in item definition?
+ if ( unQuality == k_unItemQuality_Any )
+ {
+ unQuality = pItemDef->GetQuality();
+ }
+
+ // Final fallback: default quality in schema.
+ if ( unQuality == k_unItemQuality_Any )
+ {
+ unQuality = GetItemSchema()->GetDefaultQuality();
+ }
+
+ AssertMsg( unQuality != k_unItemQuality_Any, "Unable to locate valid quality!" );
+
+ return GetItemSchema()->GetQualityDefinition( unQuality );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Creates an item matching the incoming item selection criteria
+// Input: pItem - Pointer to the item to fill in
+// criteria - The criteria that the generated item must match
+// Output: True if a matching item could be generated, false otherwise
+//-----------------------------------------------------------------------------
+CEconItem *CEconItemFactory::CreateRandomItem( const CEconGameAccount *pGameAccount, const CItemSelectionCriteria &criteria )
+{
+ // Find a matching item definition.
+ const CEconItemDefinition *pItemDef = RollItemDefinition( criteria );
+ if ( NULL == pItemDef )
+ {
+ EmitWarning( SPEW_GC, 2, "CEconItemFactory::CreateRandomItem(): Item creation request with no matching definition\n" );
+ return NULL;
+ }
+
+ const CEconItemQualityDefinition *pQualityDef = GetQualityDefinitionForItemCreation( &criteria, pItemDef );
+ if ( NULL == pQualityDef )
+ {
+ EmitWarning( SPEW_GC, 2, "CEconItemFactory::CreateRandomItem(): Item creation request with unknown quality\n" );
+ return NULL;
+ }
+
+ // At this point we have everything that can fail will already have failed, so we can safely
+ // create an item and just move properties over to it.
+ CEconItem *pItem = new CEconItem();
+ pItem->SetItemID( GetNextID() );
+ pItem->SetDefinitionIndex( pItemDef->GetDefinitionIndex() );
+ pItem->SetItemLevel( criteria.BItemLevelSet() ? criteria.GetItemLevel() : pItemDef->RollItemLevel() );
+ pItem->SetQuality( pQualityDef->GetDBValue() );
+ pItem->SetInventoryToken( criteria.GetInitialInventory() );
+ pItem->SetQuantity( criteria.BInitialQuantitySet() ? criteria.GetInitialQuantity() : pItemDef->GetDefaultDropQuantity() );
+ // don't set account ID
+
+ // Add any custom attributes we need
+ if( !BAddGCGeneratedAttributesToItem( pGameAccount, pItem ) )
+ {
+ delete pItem;
+ EmitWarning( SPEW_GC, 2, "CEconItemFactory::CreateSpecificItem(): Failed to generate attributes\n" );
+ return NULL;
+ }
+
+ return pItem;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Creates an item based on a specific item definition index
+// Input: pItem - Pointer to the item to fill in
+// unDefinitionIndex - The definition index of the item to create
+// Output: True if a matching item could be generated, false otherwise
+//-----------------------------------------------------------------------------
+CEconItem *CEconItemFactory::CreateSpecificItem( const CEconGameAccount *pGameAccount, item_definition_index_t unDefinitionIndex )
+{
+ // Find the matching index
+ const CEconItemDefinition *pItemDef = m_schema.GetItemDefinition( unDefinitionIndex );
+ if ( NULL == pItemDef )
+ {
+ EmitWarning( SPEW_GC, 2, "CEconItemFactory::CreateSpecificItem(): Item creation request with no matching definition (def index %u)\n", unDefinitionIndex );
+ return NULL;
+ }
+
+ const CEconItemQualityDefinition *pQualityDef = GetQualityDefinitionForItemCreation( NULL, pItemDef );
+ if ( NULL == pQualityDef )
+ {
+ EmitWarning( SPEW_GC, 2, "CEconItemFactory::CreateSpecificItem(): Item creation request with unknown quality\n" );
+ return NULL;
+ }
+
+ CEconItem *pItem = new CEconItem();
+ if ( pGameAccount != NULL )
+ pItem->SetItemID( GetNextID() );
+ pItem->SetDefinitionIndex( unDefinitionIndex );
+ pItem->SetItemLevel( pItemDef->RollItemLevel() );
+ pItem->SetQuality( pQualityDef->GetDBValue() );
+ // don't set inventory token
+ pItem->SetQuantity( MAX( 1, pItemDef->GetDefaultDropQuantity() ) );
+
+ // Startup test code calls this with a null pGameAccount.
+ if ( pGameAccount != NULL )
+ {
+ pItem->SetAccountID( pGameAccount->Obj().m_unAccountID );
+
+ // Add any custom attributes we need
+ if( !BAddGCGeneratedAttributesToItem( pGameAccount, pItem ) )
+ {
+ delete pItem;
+ EmitWarning( SPEW_GC, 2, "CEconItemFactory::CreateSpecificItem(): Failed to generate attributes\n" );
+ return NULL;
+ }
+ }
+
+ return pItem;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Randomly chooses an item definition that matches the criteria
+// Input: sCriteria - The criteria that the generated item must match
+// Output: The chosen item definition, or NULL if no item could be selected
+//-----------------------------------------------------------------------------
+const CEconItemDefinition *CEconItemFactory::RollItemDefinition( const CItemSelectionCriteria &criteria ) const
+{
+ // Determine which item templates match the criteria
+ CUtlVector<item_definition_index_t> vecMatches;
+ const CEconItemSchema::ItemDefinitionMap_t &mapDefs = m_schema.GetItemDefinitionMap();
+
+ FOR_EACH_MAP_FAST( mapDefs, i )
+ {
+ if ( criteria.BEvaluate( mapDefs[i] ) )
+ {
+ vecMatches.AddToTail( mapDefs.Key( i ) );
+ }
+ }
+
+ if ( 0 == vecMatches.Count() )
+ return NULL;
+
+ // Choose a random match
+ int iIndex = RandomInt( 0, vecMatches.Count() - 1 );
+ return m_schema.GetItemDefinition( vecMatches[iIndex] );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Generates attributes that the item definition insists it always has, but must be generated by the GC
+// Input:
+//-----------------------------------------------------------------------------
+bool CEconItemFactory::BAddGCGeneratedAttributesToItem( const CEconGameAccount *pGameAccount, CEconItem *pItem ) const
+{
+ const CEconItemDefinition *pDef = m_schema.GetItemDefinition( pItem->GetDefinitionIndex() );
+ if ( !pDef )
+ return false;
+
+ const CUtlVector<static_attrib_t> &vecStaticAttribs = pDef->GetStaticAttributes();
+
+ // Only generate attributes that force the GC to generate them (so they vary per item created)
+ FOR_EACH_VEC( vecStaticAttribs, i )
+ {
+ if ( vecStaticAttribs[i].bForceGCToGenerate )
+ {
+ ApplyStaticAttributeToItem( pItem, vecStaticAttribs[i], pGameAccount );
+ }
+ }
+
+ const IEconTool* pTool = pDef->GetEconTool();
+ if( pTool )
+ {
+ if( !pTool->BGenerateDynamicAttributes( pItem, pGameAccount ) )
+ return false;
+ }
+
+ if ( !pDef->BApplyPropertyGenerators( pItem ) )
+ return false;
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CEconItemFactory::ApplyStaticAttributeToItem( CEconItem *pItem, const static_attrib_t& staticAttrib, const CEconGameAccount *pGameAccount ) const
+{
+ static CSchemaAttributeDefHandle pAttr_ElevateQuality( "elevate quality" );
+ static CSchemaAttributeDefHandle pAttr_ElevateToUnusual( "elevate to unusual if applicable" );
+
+ static CSchemaAttributeDefHandle pAttr_Particle( "attach particle effect" );
+ static CSchemaAttributeDefHandle pAttr_HatUnusual( "hat only unusual effect" );
+
+ static CSchemaAttributeDefHandle pAttrDef_TauntUnusual( "taunt only unusual effect" );
+ static CSchemaAttributeDefHandle pAttrDef_TauntUnusualAttr( "on taunt attach particle index" );
+
+ const CEconItemAttributeDefinition *pAttrDef = GetItemSchema()->GetAttributeDefinition( staticAttrib.iDefIndex );
+ Assert( pAttrDef );
+
+ // Special-case the elevate-quality attribute.
+ if ( pAttrDef == pAttr_ElevateQuality )
+ {
+ //AssertMsg( CEconItem::GetTypedAttributeType<CSchemaAttributeType_Default>( pAttrDef ), "Elevate quality attribute doesn't have the right type!" );
+ int iQuality = (int)staticAttrib.m_value.asFloat;
+
+ // Do not change the quality of an item to Strange if it is not basic
+ if ( iQuality == AE_STRANGE )
+ {
+ if ( pItem->GetQuality() == AE_UNIQUE || pItem->GetQuality() == AE_PAINTKITWEAPON || pItem->GetQuality() == AE_NORMAL )
+ {
+ pItem->SetQuality( iQuality );
+ }
+ // If the quality is strange, strangify this item
+ StrangifyItemInPlace( pItem );
+ }
+ else
+ {
+ pItem->SetQuality( iQuality );
+ }
+
+ return;
+ }
+ // Special-case to elevate-quality only if item has particles. This 'attr' needs to be added LAST in a lootlist
+ // Or rather after particles may have been granted
+ else if ( pAttrDef == pAttr_ElevateToUnusual )
+ {
+ // Scan all attributes.
+ if ( pItem->FindAttribute( pAttr_Particle ) || pItem->FindAttribute( pAttrDef_TauntUnusualAttr ) )
+ {
+ pItem->SetQuality( AE_UNUSUAL );
+ }
+ return;
+ }
+ else if ( pAttrDef == pAttr_HatUnusual )
+ {
+ // Ensure the target item is a hat, if it is not bail, if it is setup a particle effect attr (Whole head items are considered 'hats' for purposes of unusuals )
+
+ if ( !(pItem->GetItemDefinition()->GetEquipRegionMask() & GetItemSchema()->GetEquipRegionBitMaskByName( "hat" ) )
+ && !(pItem->GetItemDefinition()->GetEquipRegionMask() & GetItemSchema()->GetEquipRegionBitMaskByName( "whole_head" ) )
+ ) {
+ // does not match, bail
+ return;
+ }
+
+ // create a new static attrib
+ static_attrib_t unusualAttr( staticAttrib );
+
+ // load the normal attach effect instead
+ pAttr_Particle->GetAttributeType()->LoadOrGenerateEconAttributeValue( pItem, pAttr_Particle, unusualAttr, pGameAccount );
+ return;
+ }
+ else if ( pAttrDef == pAttrDef_TauntUnusual )
+ {
+ // Ensure the target item is a taunt, if it is not bail
+ if ( pItem->GetItemDefinition()->GetLoadoutSlot( 0 ) != LOADOUT_POSITION_TAUNT )
+ {
+ // does not match, bail
+ CFmtStr fmtStr( "Attempted to put an unusual taunt effect onto item %s, but it's not a taunt! Check which lootlists it appears in and remove it from any that are trying to unusualize it!", pItem->GetItemDefinition()->GetItemDefinitionName() );
+ EmitError( SPEW_GC, "%s\n", fmtStr.Get() );
+ return;
+ }
+
+ // create a new static attrib
+ static_attrib_t unusualAttr( staticAttrib );
+
+ // load the normal attach effect instead
+ pAttrDef_TauntUnusualAttr->GetAttributeType()->LoadOrGenerateEconAttributeValue( pItem, pAttrDef_TauntUnusualAttr, unusualAttr, pGameAccount );
+ return;
+ }
+
+ // Custom attribute initialization code?
+ pAttrDef->GetAttributeType()->LoadOrGenerateEconAttributeValue( pItem, pAttrDef, staticAttrib, pGameAccount );
+}
diff --git a/game/shared/econ/econ_item_factory.h b/game/shared/econ/econ_item_factory.h
new file mode 100644
index 0000000..5673f28
--- /dev/null
+++ b/game/shared/econ/econ_item_factory.h
@@ -0,0 +1,75 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: EconItemFactory: Manages rolling for items requested by the game server
+//
+//=============================================================================
+
+#ifndef ECONITEMFACTORY_H
+#define ECONITEMFACTORY_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+class CEconItem;
+class CEconGameAccount;
+
+namespace GCSDK
+{
+ class CGCSharedObjectCache;
+}
+
+#include "game_item_schema.h"
+
+//-----------------------------------------------------------------------------
+// CEconItemFactory
+// Factory responsible for rolling random items
+//-----------------------------------------------------------------------------
+class CEconItemFactory
+{
+public:
+ CEconItemFactory( );
+
+ // Gets a pointer to the underlying item schema the factory is using
+ GameItemSchema_t &GetSchema() { return m_schema; }
+
+ // Create a random item based on the incoming item selection criteria
+ CEconItem *CreateRandomItem( const CEconGameAccount *pGameAccount, const CItemSelectionCriteria &criteria );
+
+ // Create an item from a specific definition index
+ CEconItem *CreateSpecificItem( const CEconGameAccount *pGameAccount, item_definition_index_t unDefinitionIndex );
+
+ CEconItem *CreateSpecificItem( GCSDK::CGCSharedObjectCache *pUserSOCache, item_definition_index_t unDefinitionIndex )
+ {
+ return CreateSpecificItem( pUserSOCache->GetSingleton<CEconGameAccount>(), unDefinitionIndex );
+ }
+
+ uint64 GetNextID() { Assert( m_bIsInitialized ); return m_ulNextObjID++; }
+
+ bool BYieldingInit();
+ bool BIsInitialized() { return m_bIsInitialized; }
+
+#ifdef DBGFLAG_VALIDATE
+ virtual void Validate( CValidator &validator, const char *pchName )
+ {
+ VALIDATE_SCOPE();
+ ValidateObj( m_schema );
+ }
+#endif // DBGFLAG_VALIDATE
+
+ void ApplyStaticAttributeToItem( CEconItem *pItem, const static_attrib_t& staticAttrib, const CEconGameAccount *pGameAccount ) const;
+ const CEconItemDefinition *RollItemDefinition( const CItemSelectionCriteria &criteria ) const;
+
+private:
+ bool BAddGCGeneratedAttributesToItem( const CEconGameAccount *pGameAccount, CEconItem *pItem ) const;
+
+private:
+
+ // The schema this factory uses to create items
+ GameItemSchema_t m_schema;
+
+ // the next item ID to give out
+ itemid_t m_ulNextObjID;
+ bool m_bIsInitialized;
+};
+
+#endif //ECONITEMFACTORY_H
diff --git a/game/shared/econ/econ_item_interface.cpp b/game/shared/econ/econ_item_interface.cpp
new file mode 100644
index 0000000..f915846
--- /dev/null
+++ b/game/shared/econ/econ_item_interface.cpp
@@ -0,0 +1,412 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+
+#include "cbase.h"
+#include "econ_item_interface.h"
+#include "econ_item_tools.h" // needed for CEconTool_WrappedGift definition for IsMarketable()
+#include "rtime.h"
+
+#ifdef STAGING_ONLY
+ConVar tf_paint_kit_force_wear( "tf_paint_kit_force_wear", "0", FCVAR_REPLICATED, "Set to force the wear level of paink kit weapons and ignore the GC dynamic attribute value." );
+#endif
+
+// --------------------------------------------------------------------------
+bool IEconItemInterface::GetCustomPaintKitWear( float &flWear ) const
+{
+
+#ifdef STAGING_ONLY
+ // don't assert in staging if this ConVar is set
+ if ( tf_paint_kit_force_wear.GetInt() > 0 )
+ {
+ flWear = tf_paint_kit_force_wear.GetFloat();
+ return true;
+ }
+#endif // STAGING_ONLY
+
+ static CSchemaAttributeDefHandle pAttrDef_PaintKitWear( "set_item_texture_wear" );
+ float flPaintKitWear = 0;
+ if ( pAttrDef_PaintKitWear && FindAttribute_UnsafeBitwiseCast<attrib_value_t>( this, pAttrDef_PaintKitWear, &flPaintKitWear ) )
+ {
+ flWear = flPaintKitWear;
+ return true;
+ }
+
+ static CSchemaAttributeDefHandle pAttrDef_DefaultWear( "texture_wear_default" );
+ if ( pAttrDef_DefaultWear && FindAttribute_UnsafeBitwiseCast<attrib_value_t>( this, pAttrDef_DefaultWear, &flPaintKitWear ) )
+ {
+ flWear = flPaintKitWear;
+ return true;
+ }
+ // If you have no wear, you also should not have a paint kit
+ AssertMsg( !GetCustomPainkKitDefinition(), "No Wear Found on Item [%llu - %s] that has a Paintkit!", GetID(), GetItemDefinition()->GetDefinitionName() );
+
+ return false;
+}
+
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+bool IEconItemInterface::IsTemporaryItem() const
+{
+ // store preview items are also temporary
+ if ( GetOrigin() == kEconItemOrigin_PreviewItem )
+ return true;
+
+ RTime32 rtTime = GetExpirationDate();
+ if ( rtTime > 0 )
+ return true;
+
+ return false;
+}
+
+// --------------------------------------------------------------------------
+RTime32 IEconItemInterface::GetExpirationDate() const
+{
+ COMPILE_TIME_ASSERT( sizeof( float ) == sizeof( RTime32 ) );
+
+ // dynamic attributes, if present, will override any static expiration timer
+ static CSchemaAttributeDefHandle pAttrib_ExpirationDate( "expiration date" );
+
+ attrib_value_t unAttribExpirationTimeBits;
+ COMPILE_TIME_ASSERT( sizeof( unAttribExpirationTimeBits ) == sizeof( RTime32 ) );
+
+ if ( pAttrib_ExpirationDate && FindAttribute( pAttrib_ExpirationDate, &unAttribExpirationTimeBits ) )
+ return *(RTime32 *)&unAttribExpirationTimeBits;
+
+ // do we have a static timer set in the schema for all instances to expire?
+ return GetItemDefinition()
+ ? GetItemDefinition()->GetExpirationDate()
+ : RTime32( 0 );
+}
+
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+RTime32 IEconItemInterface::GetTradableAfterDateTime() const
+{
+ static CSchemaAttributeDefHandle pAttrib_TradableAfter( "tradable after date" );
+ Assert( pAttrib_TradableAfter );
+
+ if ( !pAttrib_TradableAfter )
+ return 0;
+
+ RTime32 rtTimestamp;
+ if ( !FindAttribute( pAttrib_TradableAfter, &rtTimestamp ) )
+ return 0;
+
+ return rtTimestamp;
+}
+
+// --------------------------------------------------------------------------
+// Purpose: Return true if this item can never be traded
+// --------------------------------------------------------------------------
+bool IEconItemInterface::IsPermanentlyUntradable() const
+{
+ if ( GetItemDefinition() == NULL )
+ return true;
+
+ // tagged to not be a part of the economy?
+ if ( ( kEconItemFlag_NonEconomy & GetFlags() ) != 0 )
+ return true;
+
+ // check attributes
+
+ static CSchemaAttributeDefHandle pAttrib_AlwaysTradable( "always tradable" );
+ static CSchemaAttributeDefHandle pAttrib_CannotTrade( "cannot trade" );
+ static CSchemaAttributeDefHandle pAttrib_NonEconomy( "non economy" );
+
+ Assert( pAttrib_AlwaysTradable != NULL );
+ Assert( pAttrib_CannotTrade != NULL );
+
+ if ( pAttrib_AlwaysTradable == NULL || pAttrib_CannotTrade == NULL || pAttrib_NonEconomy == NULL )
+ return true;
+
+ // Order matters, check for nonecon first. Always tradable overrides cannot trade.
+ if ( FindAttribute( pAttrib_NonEconomy ) )
+ return true;
+
+ if ( FindAttribute( pAttrib_AlwaysTradable ) ) // *sigh*
+ return false;
+
+ if ( FindAttribute( pAttrib_CannotTrade ) )
+ return true;
+
+ // items gained in this way are not tradable
+ switch ( GetOrigin() )
+ {
+ case kEconItemOrigin_Invalid:
+ case kEconItemOrigin_Achievement:
+ case kEconItemOrigin_Foreign:
+ case kEconItemOrigin_PreviewItem:
+ case kEconItemOrigin_SteamWorkshopContribution:
+ return true;
+ }
+
+ // temporary items (items that will expire for any reason) cannot be traded
+ if ( IsTemporaryItem() )
+ return true;
+
+ // certain quality levels are not tradable
+ if ( GetQuality() >= AE_COMMUNITY && GetQuality() <= AE_SELFMADE )
+ return true;
+
+ // explicitly marked cannot trade?
+ if ( ( kEconItemFlag_CannotTrade & GetFlags() ) != 0 )
+ return true;
+
+ return false;
+}
+
+// --------------------------------------------------------------------------
+// Purpose: Return true if this item is a commodity on the Market (can place buy orders)
+// --------------------------------------------------------------------------
+bool IEconItemInterface::IsCommodity() const
+{
+ if ( GetItemDefinition() == NULL )
+ return false;
+
+ static CSchemaAttributeDefHandle pAttrib_IsCommodity( "is commodity" );
+ if ( FindAttribute( pAttrib_IsCommodity ) )
+ return true;
+
+ return false;
+}
+
+// --------------------------------------------------------------------------
+// Purpose: Return true if temporarily untradable
+// --------------------------------------------------------------------------
+bool IEconItemInterface::IsTemporarilyUntradable() const
+{
+ // Temporary untradability does NOT take "always tradable" into account
+ if ( GetTradableAfterDateTime() >= CRTime::RTime32TimeCur() )
+ return true;
+
+ return false;
+}
+
+// --------------------------------------------------------------------------
+// Purpose: Return true if this item is untradable
+// --------------------------------------------------------------------------
+bool IEconItemInterface::IsTradable() const
+{
+ // Items that are expired are never listable, regardless of other rules.
+ //RTime32 timeExpirationDate = GetExpirationDate();
+ //if ( timeExpirationDate > 0 && timeExpirationDate < CRTime::RTime32TimeCur() )
+ // return false;
+
+ return GetUntradabilityFlags() == 0;
+}
+
+// --------------------------------------------------------------------------
+// Purpose: Return untradability flags
+// --------------------------------------------------------------------------
+int IEconItemInterface::GetUntradabilityFlags() const
+{
+ int nFlags = 0;
+ if ( IsTemporarilyUntradable() )
+ {
+ nFlags |= k_Untradability_Temporary;
+ }
+
+ if ( IsPermanentlyUntradable() )
+ {
+ nFlags |= k_Untradability_Permanent;
+ }
+
+ return nFlags;
+}
+
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+bool IEconItemInterface::IsUsableInCrafting() const
+{
+ if ( GetItemDefinition() == NULL )
+ return false;
+
+ // tagged to not be a part of the economy?
+ if ( ( kEconItemFlag_NonEconomy & GetFlags() ) != 0 )
+ return false;
+
+ // always craftable?
+ static CSchemaAttributeDefHandle pAttrib_AlwaysUsableInCraft( "always tradable" );
+ Assert( pAttrib_AlwaysUsableInCraft );
+
+ if ( FindAttribute( pAttrib_AlwaysUsableInCraft ) )
+ return true;
+
+ // never craftable?
+ static CSchemaAttributeDefHandle pAttrib_NeverCraftable( "never craftable" );
+ Assert( pAttrib_NeverCraftable );
+
+ if ( FindAttribute( pAttrib_NeverCraftable ) )
+ return false;
+
+ // temporary items (items that will expire for any reason) cannot be turned into
+ // permanent items
+ if ( IsTemporaryItem() )
+ return false;
+
+ // explicitly marked not usable in crafting?
+ if ( ( kEconItemFlag_CannotBeUsedInCrafting & GetFlags() ) != 0 )
+ return false;
+
+ // items gained in this way are not craftable
+ switch ( GetOrigin() )
+ {
+ case kEconItemOrigin_Invalid:
+ case kEconItemOrigin_Foreign:
+ case kEconItemOrigin_StorePromotion:
+ case kEconItemOrigin_SteamWorkshopContribution:
+ return false;
+
+ // purchased items can be used in crafting if explicitly tagged, but not by default
+ case kEconItemOrigin_Purchased:
+ // deny items the GC didn't flag at purchase time
+ if ( (GetFlags() & kEconItemFlag_PurchasedAfterStoreCraftabilityChanges2012) == 0 )
+ return false;
+
+ // deny items that can never be used
+ if ( (GetItemDefinition()->GetCapabilities() & ITEM_CAP_CAN_BE_CRAFTED_IF_PURCHASED) == 0 )
+ return false;
+
+ break;
+ }
+
+ // certain quality levels are not craftable
+ if ( GetQuality() >= AE_COMMUNITY && GetQuality() <= AE_SELFMADE )
+ return false;
+
+ return true;
+}
+
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+bool IEconItemInterface::IsMarketable() const
+{
+ const CEconItemDefinition *pItemDef = GetItemDefinition();
+ if ( pItemDef == NULL )
+ return false;
+
+ // Untradeable items can never be marketed, regardless of other rules.
+ // Temporarily untradable items can be marketed, only permanent untradable items cannot be marketed
+ if ( IsPermanentlyUntradable() )
+ return false;
+
+ // Items that are expired are never listable, regardless of other rules.
+ RTime32 timeExpirationDate = GetExpirationDate();
+ if ( timeExpirationDate > 0 && timeExpirationDate < CRTime::RTime32TimeCur() )
+ return false;
+
+ // Initially, only TF2 supports listing items in the Marketplace.
+#if defined( TF_DLL ) || defined( TF_CLIENT_DLL ) || defined( TF_GC_DLL )
+ {
+ // User-created wrapped gifts are untradeable for the moment. This would provide a backdoor
+ // for users to sell anything they wanted, which is interesting but not what we want in
+ // the initial launch.
+ if ( pItemDef->GetTypedEconTool<CEconTool_WrappedGift>() )
+ return false;
+
+ // All other tools are listable. This includes keys, paints, backpack expanders, strange
+ // parts, Halloween spells, wedding rings, etc. It does not includes gifts (see above),
+ // noisemakers, or crates (see below).
+ if ( pItemDef->IsTool() )
+ return true;
+
+ // All crates are listable. Anything with the "decodable" flag is considered a crate.
+ if ( (pItemDef->GetCapabilities() & ITEM_CAP_DECODABLE) != 0 )
+ return true;
+
+ // Genuine-quality items come from time-limited purchase promos and are listable. Vintage
+ // items are from one-time transitions and are all finite quality. Haunted quality items are
+ // TF-Halloween-event specific. Some of the older haunted items didn't generate revenue, but
+ // the content is all old and there seems to be little harm in letting it be listed. The
+ // haunted items from 2013 all come from crates, which means they all generated revenue.
+ // Collectors items are created from a finite set of recipes.
+ // Paintkit Weapons are from cases or operations
+ if ( GetQuality() == AE_RARITY1 || GetQuality() == AE_VINTAGE || GetQuality() == AE_HAUNTED
+ || GetQuality() == AE_COLLECTORS || GetQuality() == AE_PAINTKITWEAPON )
+ return true;
+
+ // All festive items are from time-limited holiday crates and are listable. This code seems
+ // safe. (...) (This code is in fact so safe that if we just do a substring match we'll also
+ // allow "A Rather Festive Tree".)
+ if ( !V_strncmp( pItemDef->GetDefinitionName(), "Festive", 7 ) )
+ return true;
+
+ // All botkiller items come from MvM rewards and are listable. This does a substring search
+ // to find all varieties (gold, silver, rust, etc.), etc.
+ if ( V_strstr( pItemDef->GetDefinitionName(), " Botkiller " ) )
+ return true;
+
+ // Mvm V2 Robit Parts
+ if ( V_strstr( pItemDef->GetDefinitionName(), "Robits " ) )
+ return true;
+
+ // MvM Killstreak Weapons
+ static CSchemaAttributeDefHandle pAttr_killstreak( "killstreak tier" );
+ if ( FindAttribute( pAttr_killstreak ) )
+ return true;
+
+ // Australium Items
+ static CSchemaAttributeDefHandle pAttrDef_IsAustralium( "is australium item" );
+ if ( FindAttribute( pAttrDef_IsAustralium ) )
+ return true;
+
+ // Glitch GateHat Replacement Item
+ static CSchemaItemDefHandle pItemDef_GlitchedCircuit( "Glitched Circuit Board" );
+ if ( pItemDef == pItemDef_GlitchedCircuit )
+ return true;
+
+ // Anything that says it wants to be marketable.
+ static CSchemaAttributeDefHandle pAttrDef_IsMarketable( "is marketable" );
+ if ( FindAttribute( pAttrDef_IsMarketable ) )
+ return true;
+
+ // Anything that is of limited quantity (ie limited promos)
+ static CSchemaAttributeDefHandle pAttrDef_IsLimited( "limited quantity item" );
+ if ( FindAttribute( pAttrDef_IsLimited ) )
+ return true;
+
+ // Allow the Giving items (not a wrapped_gift but a gift, ie Secret Saxton, Pile O Gifts, Pallet of Keys)
+ const CEconTool_Gift *pEconToolGift = pItemDef->GetTypedEconTool<CEconTool_Gift>();
+ if ( pEconToolGift )
+ return true;
+
+ // Unusual Cosmetics and Taunts
+ if ( GetQuality() == AE_UNUSUAL && ( GetItemDefinition()->GetLoadoutSlot( 0 ) == LOADOUT_POSITION_MISC || GetItemDefinition()->GetLoadoutSlot( 0 ) == LOADOUT_POSITION_TAUNT ) )
+ return true;
+
+ // Strange items. Dont just check for strange quality, actually check for a strange attribute.
+ // See if we've got any strange attributes.
+ for ( int i = 0; i < GetKillEaterAttrCount(); i++ )
+ {
+ if ( FindAttribute( GetKillEaterAttr_Score( i ) ) )
+ {
+ return true;
+ }
+ }
+ }
+#endif // defined( TF_DLL ) || defined( TF_CLIENT_DLL ) || defined( TF_GC_DLL )
+
+ // By default, items aren't listable.
+ return false;
+}
+
+// --------------------------------------------------------------------------
+const char *IEconItemInterface::GetDefinitionString( const char *pszKeyName, const char *pszDefaultValue ) const
+{
+ const GameItemDefinition_t *pDef = GetItemDefinition();
+ if ( pDef )
+ return pDef->GetDefinitionString( pszKeyName, pszDefaultValue );
+ return pszDefaultValue;
+}
+
+// --------------------------------------------------------------------------
+KeyValues *IEconItemInterface::GetDefinitionKey( const char *pszKeyName ) const
+{
+ const GameItemDefinition_t *pDef = GetItemDefinition();
+ if ( pDef )
+ return pDef->GetDefinitionKey( pszKeyName );
+ return NULL;
+}
diff --git a/game/shared/econ/econ_item_interface.h b/game/shared/econ/econ_item_interface.h
new file mode 100644
index 0000000..287ab0a
--- /dev/null
+++ b/game/shared/econ/econ_item_interface.h
@@ -0,0 +1,546 @@
+//========= 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<T> 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<const T> { typedef T ResultType; };
+
+// AreTypesIdentical<T, U> 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<T, T> { enum { kValue = true }; };
+
+// IsPointerType<T> 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<T *> { enum { kValue = true }; };
+
+// IsValidAttributeValueTypeImpl<T> 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<attrib_value_t> { enum { kValue = true }; };
+template < > struct IsValidAttributeValueTypeImpl<float> { enum { kValue = true }; };
+template < > struct IsValidAttributeValueTypeImpl<uint64> { enum { kValue = true }; };
+template < > struct IsValidAttributeValueTypeImpl<CAttribute_String> { enum { kValue = true }; };
+template < > struct IsValidAttributeValueTypeImpl<CAttribute_DynamicRecipeComponent> { 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<T>::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<TActualTypeInMemory>::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<TActualTypeInMemory, attrib_value_t>::kValue && AreTypesIdentical<TTreatAsThisType, float>::kValue) ||
+ (AreTypesIdentical<TActualTypeInMemory, CAttribute_String>::kValue && AreTypesIdentical<TTreatAsThisType, const char *>::kValue) ||
+ AreTypesIdentical<TActualTypeInMemory, TTreatAsThisType>::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<TAnyOtherType>::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 <float> 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<TTreatAsThisType *>( 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<const TTreatAsThisType *>( 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<CAttribute_String, const char *>::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<TActualTypeInMemory, TTreatAsThisType> 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<T, T, TAttributeContainerType>( 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 <typename TBaseClass>
+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
diff --git a/game/shared/econ/econ_item_inventory.cpp b/game/shared/econ/econ_item_inventory.cpp
new file mode 100644
index 0000000..77082ec
--- /dev/null
+++ b/game/shared/econ/econ_item_inventory.cpp
@@ -0,0 +1,2340 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+
+#include "cbase.h"
+#include "econ_item_inventory.h"
+#include "vgui/ILocalize.h"
+#include "tier3/tier3.h"
+#include "econ_item_system.h"
+#include "econ_item.h"
+#include "econ_gcmessages.h"
+#include "shareddefs.h"
+#include "filesystem.h"
+#include "econ_item_description.h" // only for CSteamAccountIDAttributeCollector
+
+#ifdef CLIENT_DLL
+#include <igameevents.h>
+#include "econ_game_account_client.h"
+#include "ienginevgui.h"
+#include "econ_ui.h"
+#include "item_pickup_panel.h"
+#include "econ/econ_item_preset.h"
+#include "econ/confirm_dialog.h"
+#include "tf_xp_source.h"
+#include "tf_notification.h"
+#else
+#include "props_shared.h"
+#include "basemultiplayerplayer.h"
+#endif
+
+#if defined(TF_CLIENT_DLL) || defined(TF_DLL)
+#include "tf_gcmessages.h"
+#include "tf_duel_summary.h"
+#include "econ_contribution.h"
+#include "tf_player_info.h"
+#include "econ/econ_claimcode.h"
+#include "tf_wardata.h"
+#include "tf_ladder_data.h"
+#include "tf_rating_data.h"
+#endif
+
+#if defined(TF_DLL) && defined(GAME_DLL)
+#include "tf_gc_api.h"
+#include "econ/econ_game_account_server.h"
+#endif
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+using namespace GCSDK;
+
+#ifdef _DEBUG
+ConVar item_inventory_debug( "item_inventory_debug", "0", FCVAR_REPLICATED | FCVAR_CHEAT );
+#endif
+
+#ifdef USE_DYNAMIC_ASSET_LOADING
+//extern ConVar item_dynamicload;
+#endif
+
+#define ITEM_CLIENTACK_FILE "item_clientacks.txt"
+
+#ifdef _DEBUG
+#ifdef CLIENT_DLL
+ConVar item_debug_clientacks( "item_debug_clientacks", "0", FCVAR_CLIENTDLL | FCVAR_ARCHIVE );
+#endif
+#endif // _DEBUG
+
+// Result codes strings for GC results.
+const char* GCResultString[8] =
+{
+ "k_EGCMsgResponseOK", // Request succeeded
+ "k_EGCMsgResponseDenied", // Request denied
+ "k_EGCMsgResponseServerError", // Request failed due to a temporary server error
+ "k_EGCMsgResponseTimeout", // Request timed out
+ "k_EGCMsgResponseInvalid", // Request was corrupt
+ "k_EGCMsgResponseNoMatch", // No item definition matched the request
+ "k_EGCMsgResponseUnknownError", // Request failed with an unknown error
+ "k_EGCMsgResponseNotLoggedOn", // Client not logged on to steam
+};
+
+CBasePlayer *GetPlayerBySteamID( const CSteamID &steamID )
+{
+ CSteamID steamIDPlayer;
+ for ( int i = 1; i <= gpGlobals->maxClients; i++ )
+ {
+ CBasePlayer *pPlayer = UTIL_PlayerByIndex( i );
+ if ( pPlayer == NULL )
+ continue;
+
+ if ( pPlayer->GetSteamID( &steamIDPlayer ) == false )
+ continue;
+
+ if ( steamIDPlayer == steamID )
+ return pPlayer;
+ }
+ return NULL;
+}
+
+// Inventory Less function.
+// Used to sort the inventory items into their positions.
+bool CInventoryListLess::Less( const CEconItemView &src1, const CEconItemView &src2, void *pCtx )
+{
+ int iPos1 = src1.GetInventoryPosition();
+ int iPos2 = src2.GetInventoryPosition();
+
+ // Context can be specified to point to a func that extracts the position from the backend position.
+ // Necessary if your inventory packs a bunch of info into the position instead of using it just as a position.
+ if ( pCtx )
+ {
+ CPlayerInventory *pInv = (CPlayerInventory*)pCtx;
+ iPos1 = pInv->ExtractInventorySortPosition( iPos1 );
+ iPos2 = pInv->ExtractInventorySortPosition( iPos2 );
+ }
+
+ if ( iPos1 < iPos2 )
+ return true;
+
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CInventoryManager::CInventoryManager( void )
+#ifdef CLIENT_DLL
+ : m_mapPersonaNamesCache( DefLessFunc( uint32 ) )
+ , m_sPersonaStateChangedCallback( this, &CInventoryManager::OnPersonaStateChanged )
+ , m_personaNameRequests( DefLessFunc( uint64 ) )
+#endif
+{
+#ifdef CLIENT_DLL
+ m_pkvItemClientAckFile = NULL;
+ m_bClientAckDirty = false;
+ m_iPredictedDiscards = 0;
+ m_flNextLoadPresetChange = 0.0f;
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CInventoryManager::SteamRequestInventory( CPlayerInventory *pInventory, CSteamID pSteamID, IInventoryUpdateListener *pListener )
+{
+ // SteamID must be valid
+ if ( !pSteamID.IsValid() || !pSteamID.BIndividualAccount() )
+ {
+ if ( !HushAsserts() )
+ {
+ Assert( pSteamID.IsValid() );
+ Assert( pSteamID.BIndividualAccount() );
+ }
+ return;
+ }
+
+ // If we haven't seen this inventory before, register it
+ bool bFound = false;
+ for ( int i = 0; i < m_pInventories.Count(); i++ )
+ {
+ if ( m_pInventories[i].pInventory == pInventory )
+ {
+ bFound = true;
+ break;
+ }
+ }
+ if ( !bFound )
+ {
+ int iIdx = m_pInventories.AddToTail();
+ m_pInventories[iIdx].pInventory = pInventory;
+ m_pInventories[iIdx].pListener = pListener;
+ }
+
+ // Add the request to our list of pending requests
+ int iIdx = m_hPendingInventoryRequests.AddToTail();
+ m_hPendingInventoryRequests[iIdx].pID = pSteamID;
+ m_hPendingInventoryRequests[iIdx].pInventory = pInventory;
+
+ pInventory->RequestInventory( pSteamID );
+
+ if( pListener )
+ {
+ pInventory->AddListener( pListener );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Called when a gameserver connects to steam.
+//-----------------------------------------------------------------------------
+void CInventoryManager::GameServerSteamAPIActivated()
+{
+#if defined(TF_DLL) && defined(GAME_DLL)
+ GameCoordinator_NotifyGameState();
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CPlayerInventory *CInventoryManager::GetInventoryForAccount( uint32 iAccountID )
+{
+ FOR_EACH_VEC( m_pInventories, i )
+ {
+ if ( m_pInventories[i].pInventory->GetOwner().GetAccountID() == iAccountID )
+ return m_pInventories[i].pInventory;
+ }
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CInventoryManager::DeregisterInventory( CPlayerInventory *pInventory )
+{
+ int iCount = m_pInventories.Count();
+ for ( int i = iCount-1; i >= 0; i-- )
+ {
+ if ( m_pInventories[i].pInventory == pInventory )
+ {
+ m_pInventories.Remove(i);
+ }
+ }
+}
+
+
+#ifdef CLIENT_DLL
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CInventoryManager::IsPresetIndexValid( equipped_preset_t unPreset )
+{
+ const bool bResult = GetItemSchema()->IsValidPreset( unPreset );
+ AssertMsg( bResult, "Invalid preset index!" );
+ return bResult;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CInventoryManager::LoadPreset( equipped_class_t unClass, equipped_preset_t unPreset )
+{
+ if ( !IsValidPlayerClass( unClass ) )
+ return false;
+
+ if ( !IsPresetIndexValid( unPreset ) )
+ return false;
+
+ if ( !GetLocalInventory()->GetSOC() )
+ return false;
+
+ if ( m_flNextLoadPresetChange > gpGlobals->realtime )
+ {
+ Msg( "Loadout change denied. Changing presets too quickly.\n" );
+ return false;
+ }
+
+ m_flNextLoadPresetChange = gpGlobals->realtime + 0.5f;
+
+ GCSDK::CProtoBufMsg<CMsgSelectPresetForClass> msg( k_EMsgGCPresets_SelectPresetForClass );
+ msg.Body().set_class_id( unClass );
+ msg.Body().set_preset_id( unPreset );
+ GCClientSystem()->BSendMessage( msg );
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CInventoryManager::UpdateLocalInventory( void )
+{
+ if ( steamapicontext->SteamUser() && GetLocalInventory() )
+ {
+ CSteamID steamID = steamapicontext->SteamUser()->GetSteamID();
+ if ( steamID.IsValid() ) // make sure we're logged in and we know who we are
+ {
+ SteamRequestInventory( GetLocalInventory(), steamID );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CInventoryManager::OnPersonaStateChanged( PersonaStateChange_t *info )
+{
+ if ( ( info->m_nChangeFlags & k_EPersonaChangeName ) != 0 )
+ m_personaNameRequests.InsertOrReplace( info->m_ulSteamID, true );
+}
+
+#endif
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CInventoryManager::Init( void )
+{
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CInventoryManager::PostInit( void )
+{
+ // Initialize the item system.
+ ItemSystem()->Init();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CInventoryManager::PreInitGC()
+{
+ REG_SHARED_OBJECT_SUBCLASS( CEconItem );
+
+#if defined (CLIENT_DLL)
+ REG_SHARED_OBJECT_SUBCLASS( CEconGameAccountClient );
+ REG_SHARED_OBJECT_SUBCLASS( CEconItemPerClassPresetData );
+ REG_SHARED_OBJECT_SUBCLASS( CSOTFMatchResultPlayerInfo );
+ REG_SHARED_OBJECT_SUBCLASS( CXPSource );
+ REG_SHARED_OBJECT_SUBCLASS( CTFNotification );
+#endif
+
+#if defined(TF_CLIENT_DLL) || defined(TF_DLL)
+
+ REG_SHARED_OBJECT_SUBCLASS( CWarData );
+ REG_SHARED_OBJECT_SUBCLASS( CTFDuelSummary );
+ REG_SHARED_OBJECT_SUBCLASS( CTFMapContribution );
+ REG_SHARED_OBJECT_SUBCLASS( CTFPlayerInfo );
+ REG_SHARED_OBJECT_SUBCLASS( CEconClaimCode );
+ REG_SHARED_OBJECT_SUBCLASS( CSOTFLadderData );
+#endif
+
+#ifdef TF_DLL
+ REG_SHARED_OBJECT_SUBCLASS( CEconGameAccountForGameServers );
+#endif // TF_DLL
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CInventoryManager::PostInitGC()
+{
+#ifdef CLIENT_DLL
+ // The client immediately loads the local player's inventory
+ UpdateLocalInventory();
+#endif
+}
+
+
+//-----------------------------------------------------------------------------
+void CInventoryManager::Shutdown()
+{
+ int nInventoryCount = m_pInventories.Count();
+ for ( int iInventory = 0; iInventory < nInventoryCount; ++iInventory )
+ {
+ CPlayerInventory *pInventory = m_pInventories[iInventory].pInventory;
+ if ( pInventory )
+ {
+ pInventory->Clear();
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CInventoryManager::LevelInitPreEntity( void )
+{
+ // Throw out any testitem definitions
+ for ( int i = 0; i < TI_TYPE_COUNT; i++ )
+ {
+ int iNewDef = TESTITEM_DEFINITIONS_BEGIN_AT + i;
+ ItemSystem()->GetItemSchema()->ItemTesting_DiscardTestDefinition( iNewDef );
+ }
+
+ // Precache all item models we've got
+#ifdef GAME_DLL
+ CUtlVector<const char *> vecPrecacheModelStrings;
+#endif // GAME_DLL
+ const CEconItemSchema::ItemDefinitionMap_t& mapItemDefs = ItemSystem()->GetItemSchema()->GetItemDefinitionMap();
+ FOR_EACH_MAP_FAST( mapItemDefs, i )
+ {
+ CEconItemDefinition *pData = mapItemDefs[i];
+
+ pData->SetHasBeenLoaded( true );
+
+#ifdef GAME_DLL
+ bool bDynamicLoad = false;
+#ifdef USE_DYNAMIC_ASSET_LOADING
+ bDynamicLoad = true;//item_dynamicload.GetBool();
+#endif // USE_DYNAMIC_ASSET_LOADING
+ pData->GeneratePrecacheModelStrings( bDynamicLoad, &vecPrecacheModelStrings );
+
+ // Precache the models and the gibs for everything the definition requested.
+ FOR_EACH_VEC( vecPrecacheModelStrings, i )
+ {
+ // Ignore any objects which requested an empty precache string for whatever reason.
+ if ( vecPrecacheModelStrings[i] && vecPrecacheModelStrings[i][0] )
+ {
+ int iModelIndex = CBaseEntity::PrecacheModel( vecPrecacheModelStrings[i] );
+ PrecacheGibsForModel( iModelIndex );
+ }
+ }
+
+ vecPrecacheModelStrings.RemoveAll();
+
+ pData->GeneratePrecacheSoundStrings( bDynamicLoad, &vecPrecacheModelStrings );
+
+ // Precache the sounds for everything
+ FOR_EACH_VEC( vecPrecacheModelStrings, i )
+ {
+ // Ignore any objects which requested an empty precache string for whatever reason.
+ if ( vecPrecacheModelStrings[i] && vecPrecacheModelStrings[i][0] )
+ {
+ CBaseEntity::PrecacheScriptSound( vecPrecacheModelStrings[i] );
+ }
+ }
+
+ vecPrecacheModelStrings.RemoveAll();
+#endif
+ }
+
+ // We reset the cached attribute class strings, since it's invalidated by level changes
+ ItemSystem()->ResetAttribStringCache();
+
+#ifdef GAME_DLL
+ ItemSystem()->ReloadWhitelist();
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CInventoryManager::LevelShutdownPostEntity( void )
+{
+ // We reset the cached attribute class strings, since it's invalidated by level changes
+ ItemSystem()->ResetAttribStringCache();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Lets the client know that we're now connected to the GC
+//-----------------------------------------------------------------------------
+#ifdef CLIENT_DLL
+void CInventoryManager::SendGCConnectedEvent( void )
+{
+ IGameEvent *event = gameeventmanager->CreateEvent( "gc_connected" );
+ if ( event )
+ {
+ gameeventmanager->FireEventClientSide( event );
+ }
+}
+#endif
+
+
+#if !defined(NO_STEAM)
+//-----------------------------------------------------------------------------
+// Purpose: GC Msg handler to receive the dev "new item" response
+//-----------------------------------------------------------------------------
+class CGCDev_NewItemRequestResponse : public GCSDK::CGCClientJob
+{
+public:
+ CGCDev_NewItemRequestResponse( GCSDK::CGCClient *pClient ) : GCSDK::CGCClientJob( pClient ) {}
+
+ virtual bool BYieldingRunGCJob( GCSDK::IMsgNetPacket *pNetPacket )
+ {
+ GCSDK::CGCMsg<MsgGCStandardResponse_t> msg( pNetPacket );
+
+ if ( msg.Body().m_eResponse == k_EGCMsgResponseOK )
+ {
+ Msg("Received new item acknowledgement: %s\n", GCResultString[msg.Body().m_eResponse] );
+ }
+ else
+ {
+ Warning("Failed to generate new item: %s\n", GCResultString[msg.Body().m_eResponse] );
+ }
+ return true;
+ }
+
+};
+GC_REG_JOB( GCSDK::CGCClient, CGCDev_NewItemRequestResponse, "CGCDev_NewItemRequestResponse", k_EMsgGCDev_NewItemRequestResponse, GCSDK::k_EServerTypeGCClient );
+
+#endif // NO_STEAM
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CInventoryManager::RemovePendingRequest( CSteamID *pSteamID )
+{
+#ifdef CLIENT_DLL
+ // Only the client, all requests are for the local player. Clear them all.
+ m_hPendingInventoryRequests.Purge();
+ return;
+#endif
+
+ // On the server, remove all requests for the specified steam id
+ int iCount = m_hPendingInventoryRequests.Count();
+ for ( int i = iCount-1; i >= 0; i-- )
+ {
+ if ( m_hPendingInventoryRequests[i].pID == *pSteamID )
+ {
+ m_hPendingInventoryRequests.Remove(i);
+ }
+ }
+}
+
+#ifdef CLIENT_DLL
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CInventoryManager::DropItem( itemid_t iItemID )
+{
+ static CSchemaAttributeDefHandle pAttrDef_NoDelete( "cannot delete" );
+
+ // Double check that this item can be delete
+ CEconItemView *pItem = GetLocalInventory()->GetInventoryItemByItemID( iItemID );
+ if ( !pItem || !pAttrDef_NoDelete || pItem->FindAttribute( pAttrDef_NoDelete ) )
+ {
+ return;
+ }
+
+ GCSDK::CGCMsg<MsgGCDelete_t> msg( k_EMsgGCDelete );
+ msg.Body().m_unItemID = iItemID;
+ GCClientSystem()->BSendMessage( msg );
+
+ // Keep track of how many items we've discarded, but haven't received responses for.
+ m_iPredictedDiscards++;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Delete any items we can't find static data for. This can happen when we're testing
+// internally, and then remove an item. Shouldn't ever happen in the wild.
+//-----------------------------------------------------------------------------
+int CInventoryManager::DeleteUnknowns( CPlayerInventory *pInventory )
+{
+ // We need to manually walk the main inventory's SOC, because unknown items won't be in the inventory
+ GCSDK::CGCClientSharedObjectCache *pSOC = pInventory->GetSOC();
+ if ( pSOC )
+ {
+ int iBadItems = 0;
+ CGCClientSharedObjectTypeCache *pTypeCache = pSOC->FindTypeCache( CEconItem::k_nTypeID );
+ if( pTypeCache )
+ {
+ for( uint32 unItem = 0; unItem < pTypeCache->GetCount(); unItem++ )
+ {
+ CEconItem *pItem = (CEconItem *)pTypeCache->GetObject( unItem );
+ if ( pItem )
+ {
+ CEconItemDefinition *pData = ItemSystem()->GetStaticDataForItemByDefIndex( pItem->GetDefinitionIndex() );
+ if ( !pData )
+ {
+ DropItem( pItem->GetItemID() );
+ iBadItems++;
+ }
+ }
+ }
+ }
+ return iBadItems;
+ }
+
+ return 0;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Tries to move the specified item into the player's backpack.
+// FAILS if the backpack is full. Returns false in that case.
+//-----------------------------------------------------------------------------
+bool CInventoryManager::SetItemBackpackPosition( CEconItemView *pItem, uint32 iPosition, bool bForceUnequip, bool bAllowOverflow )
+{
+ CPlayerInventory *pInventory = GetLocalInventory();
+ if ( !pInventory )
+ return false;
+
+ const int iMaxItems = pInventory->GetMaxItemCount();
+ if ( !iPosition )
+ {
+ // Build a list of empty slots. We track extra slots beyond the backpack for overflow.
+ CUtlVector< bool > bFilledSlots;
+ bFilledSlots.SetSize( iMaxItems * 2 );
+ for ( int i = 0; i < bFilledSlots.Count(); ++i )
+ {
+ bFilledSlots[i] = false;
+ }
+ for ( int i = 0; i < pInventory->GetItemCount(); i++ )
+ {
+ CEconItemView *pTmpItem = pInventory->GetItem(i);
+ // Ignore the item we're moving.
+ if ( pTmpItem == pItem )
+ continue;
+
+ int iBackpackPos = GetBackpackPositionFromBackend( pTmpItem->GetInventoryPosition() );
+ if ( iBackpackPos >= 0 && iBackpackPos < bFilledSlots.Count() )
+ {
+ bFilledSlots[iBackpackPos] = true;
+ }
+ }
+
+ // Add predicted filled slots
+ for ( int i = 0; i < m_PredictedFilledSlots.Count(); i++ )
+ {
+ int iBackpackPos = m_PredictedFilledSlots[i];
+ if ( iBackpackPos >= 0 && iBackpackPos < bFilledSlots.Count() )
+ {
+ bFilledSlots[iBackpackPos] = true;
+ }
+ }
+
+ // Now find an empty slot
+ for ( int i = 1; i < bFilledSlots.Count(); i++ )
+ {
+ if ( !bFilledSlots[i] )
+ {
+ iPosition = i;
+ break;
+ }
+ }
+
+ if ( !iPosition )
+ return false;
+ }
+
+ if ( !bAllowOverflow && iPosition > (uint32)iMaxItems )
+ return false;
+
+ //Warning("Moved item %llu to backpack slot: %d\n", pItem->GetItemID(), iPosition );
+
+ uint32 iBackendPosition = bForceUnequip ? 0 : pItem->GetInventoryPosition();
+ SetBackpackPosition( &iBackendPosition, iPosition );
+ UpdateInventoryPosition( pInventory, pItem->GetItemID(), iBackendPosition );
+
+ m_PredictedFilledSlots.AddToTail( iPosition );
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CInventoryManager::MoveItemToBackpackPosition( CEconItemView *pItem, int iBackpackPosition )
+{
+ CEconItemView *pOldItem = GetItemByBackpackPosition( iBackpackPosition );
+ if ( pOldItem )
+ {
+ // Move the item in the new spot to our current spot
+ SetItemBackpackPosition( pOldItem, GetBackpackPositionFromBackend(pItem->GetInventoryPosition()) );
+
+ //Warning("Moved OLD item %llu to backpack slot: %d\n", pOldItem->GetItemID(), GetBackpackPositionFromBackend(iBackendPosition) );
+ }
+
+ // Move the item to the new spot
+ SetItemBackpackPosition( pItem, iBackpackPosition );
+
+ //Warning("Moved item %llu to backpack slot: %d\n", pItem->GetItemID(), iBackpackPosition );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+class CWaitForBackpackSortFinishDialog : public CGenericWaitingDialog
+{
+public:
+ CWaitForBackpackSortFinishDialog( vgui::Panel *pParent ) : CGenericWaitingDialog( pParent )
+ {
+ }
+
+protected:
+ virtual void OnTimeout()
+ {
+ InventoryManager()->SortBackpackFinished();
+ }
+};
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CInventoryManager::SortBackpackBy( uint32 iSortType )
+{
+ GCSDK::CProtoBufMsg<CMsgSortItems> msg( k_EMsgGCSortItems );
+ msg.Body().set_sort_type( iSortType );
+ GCClientSystem()->BSendMessage( msg );
+
+ ShowWaitingDialog( new CWaitForBackpackSortFinishDialog( NULL ), "#BackpackSortExplanation_Title", true, false, 3.0f );
+ m_bInBackpackSort = true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CInventoryManager::SortBackpackFinished( void )
+{
+ m_bInBackpackSort = false;
+
+ GetLocalInventory()->SendInventoryUpdateEvent();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: GC Msg handler to receive the sort finished message
+//-----------------------------------------------------------------------------
+class CGBackpackSortFinished : public GCSDK::CGCClientJob
+{
+public:
+ CGBackpackSortFinished( GCSDK::CGCClient *pClient ) : GCSDK::CGCClientJob( pClient ) {}
+
+ virtual bool BYieldingRunGCJob( GCSDK::IMsgNetPacket *pNetPacket )
+ {
+ CloseWaitingDialog();
+ InventoryManager()->SortBackpackFinished();
+ return true;
+ }
+
+};
+GC_REG_JOB( GCSDK::CGCClient, CGBackpackSortFinished, "CGBackpackSortFinished", k_EMsgGCBackpackSortFinished, GCSDK::k_EServerTypeGCClient );
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CInventoryManager::UpdateInventoryPosition( CPlayerInventory *pInventory, uint64 ulItemID, uint32 unNewInventoryPos )
+{
+ if ( !pInventory->GetInventoryItemByItemID( ulItemID ) )
+ {
+ Warning("Attempt to update inventory position failure: %s.\n", "could not find matching item ID");
+ return;
+ }
+ if ( !pInventory->GetSOCDataForItem( ulItemID ) )
+ {
+ Warning("Attempt to update inventory position failure: %s\n", "could not find SOC data for item");
+ return;
+ }
+
+ // In the incredibly rare case where the GC crashed while sorting our backpack, we won't have gotten
+ // a k_EMsgGCBackpackSortFinished message. Assume that if we're requesting a manual move of an item, we're not sorting anymore.
+ m_bInBackpackSort = false;
+
+ // TF has multiple ways of using the inventory position bits. For all inventory positions moving forward, assume
+ // they're in the new format.
+#if defined(TF_CLIENT_DLL) || defined(TF_DLL)
+ if ( unNewInventoryPos != 0 )
+ {
+ unNewInventoryPos |= kBackendPosition_NewFormat;
+ }
+#endif // defined(TF_CLIENT_DLL) || defined(TF_DLL)
+
+ // Queue a message to be sent to the GC
+ CMsgSetItemPositions_ItemPosition *pMsg = m_msgPendingSetItemPositions.add_item_positions();
+ pMsg->set_item_id( ulItemID );
+ pMsg->set_position( unNewInventoryPos );
+}
+
+void CInventoryManager::Update( float frametime )
+{
+
+ // Check if we have any pending item position changes that we need to flush out
+ if ( m_msgPendingSetItemPositions.item_positions_size() > 0 )
+ {
+ // !KLUDGE! It would be nice if we could just send this in one line instead of making a copy
+ CProtoBufMsg<CMsgSetItemPositions> msg( k_EMsgGCSetItemPositions );
+ msg.Body() = m_msgPendingSetItemPositions;
+ GCClientSystem()->BSendMessage( msg );
+
+ m_msgPendingSetItemPositions.Clear();
+ }
+
+ // Check if we have any pending account lookups to batch up
+ if ( m_msgPendingLookupAccountNames.accountids_size() > 0 )
+ {
+ // !KLUDGE! It would be nice if we could just send this in one line instead of making a copy
+ CProtoBufMsg< CMsgLookupMultipleAccountNames > msg( k_EMsgGCLookupMultipleAccountNames );
+ msg.Body() = m_msgPendingLookupAccountNames;
+ GCClientSystem()->BSendMessage( msg );
+
+ m_msgPendingLookupAccountNames.Clear();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CInventoryManager::UpdateInventoryEquippedState( CPlayerInventory *pInventory, uint64 ulItemID, equipped_class_t unClass, equipped_slot_t unSlot )
+{
+ // passing in INVALID_ITEM_ID means "unequip from this slot"
+ if ( ulItemID != INVALID_ITEM_ID )
+ {
+ if ( !pInventory->GetInventoryItemByItemID( ulItemID ) )
+ {
+ //Warning("Attempt to update equipped state failure: %s.\n", "could not find matching item ID");
+ return;
+ }
+ if ( !pInventory->GetSOCDataForItem( ulItemID ) )
+ {
+ //Warning("Attempt to update equipped state failure: %s\n", "could not find SOC data for item");
+ return;
+ }
+ }
+
+ CProtoBufMsg<CMsgAdjustItemEquippedState> msg( k_EMsgGCAdjustItemEquippedState );
+ msg.Body().set_item_id( ulItemID );
+ msg.Body().set_new_class( unClass );
+ msg.Body().set_new_slot( unSlot );
+ GCClientSystem()->BSendMessage( msg );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CInventoryManager::ShowItemsPickedUp( bool bForce, bool bReturnToGame, bool bNoPanel )
+{
+ CPlayerInventory *pLocalInv = GetLocalInventory();
+ if ( !pLocalInv )
+ return false;
+
+ // Don't bring it up if we're already browsing something in the gameUI
+ vgui::VPANEL gameuiPanel = enginevgui->GetPanel( PANEL_GAMEUIDLL );
+ if ( !bForce && vgui::ipanel()->IsVisible( gameuiPanel ) )
+ return false;
+
+ CUtlVector<CEconItemView*> aItemsFound;
+
+ // Go through the root inventory and find any items that are in the "found" position
+ int iCount = pLocalInv->GetItemCount();
+ for ( int i = 0; i < iCount; i++ )
+ {
+ CEconItemView *pTmp = pLocalInv->GetItem(i);
+ if ( !pTmp )
+ continue;
+
+ if ( pTmp->GetStaticData()->IsHidden() )
+ continue;
+
+ uint32 iPosition = pTmp->GetInventoryPosition();
+ if ( IsUnacknowledged(iPosition) == false )
+ continue;
+ if ( GetBackpackPositionFromBackend(iPosition) != 0 )
+ continue;
+
+ // Now make sure we haven't got a clientside saved ack for this item.
+ // This makes sure we don't show multiple pickups for items that we've found,
+ // but haven't been able to move out of unack'd position due to the GC being unavailable.
+ if ( HasBeenAckedByClient( pTmp ) )
+ continue;
+
+ aItemsFound.AddToTail( pTmp );
+ }
+
+ if ( !aItemsFound.Count() )
+ return CheckForRoomAndForceDiscard();
+
+ // We're not forcing the player to make room yet. Just show the pickup panel.
+ CItemPickupPanel *pItemPanel = bNoPanel ? NULL : EconUI()->OpenItemPickupPanel();
+
+ if ( pItemPanel )
+ {
+ pItemPanel->SetReturnToGame( bReturnToGame );
+ }
+
+ for ( int i = 0; i < aItemsFound.Count(); i++ )
+ {
+ if ( pItemPanel )
+ {
+ pItemPanel->AddItem( aItemsFound[i] );
+ }
+ else
+ {
+ AcknowledgeItem( aItemsFound[i] );
+ }
+ }
+
+ if ( pItemPanel )
+ {
+ pItemPanel->MoveToFront();
+ }
+ else
+ {
+ SaveAckFile();
+ }
+
+ aItemsFound.Purge();
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CInventoryManager::CheckForRoomAndForceDiscard( void )
+{
+ CPlayerInventory *pLocalInv = GetLocalInventory();
+ if ( !pLocalInv )
+ return false;
+
+ // Go through the inventory and attempt to move any items outside the backpack into valid positions.
+ // Remember the first item that we failed to move, so we can force a discard later.
+ CEconItemView *pItem = NULL;
+ const int iMaxItems = pLocalInv->GetMaxItemCount();
+ int iCount = pLocalInv->GetItemCount();
+ for ( int i = 0; i < iCount; i++ )
+ {
+ CEconItemView *pTmp = pLocalInv->GetItem(i);
+ if ( !pTmp )
+ continue;
+
+ if ( pTmp->GetStaticData()->IsHidden() )
+ continue;
+
+ uint32 iPosition = pTmp->GetInventoryPosition();
+ if ( IsUnacknowledged(iPosition) || GetBackpackPositionFromBackend(iPosition) > iMaxItems )
+ {
+ if ( !SetItemBackpackPosition( pTmp, 0, false, false ) )
+ {
+ pItem = pTmp;
+ break;
+ }
+ }
+ }
+
+ // If we're not over the limit, we're done.
+ if ( ( iCount - m_iPredictedDiscards ) <= iMaxItems )
+ return false;
+
+ if ( !pItem )
+ return false;
+
+ // We're forcing the player to make room for items he's found. Bring up that panel with the first item over the limit.
+ CItemDiscardPanel *pDiscardPanel = EconUI()->OpenItemDiscardPanel();
+ pDiscardPanel->SetItem( pItem );
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Client Acknowledges an item and moves it in to the backpack
+//-----------------------------------------------------------------------------
+void CInventoryManager::AcknowledgeItem ( CEconItemView *pItem, bool bMoveToBackpack /* = true */ )
+{
+ SetAckedByClient( pItem );
+
+ int iMethod = GetUnacknowledgedReason( pItem->GetInventoryPosition() ) - 1;
+ if ( iMethod >= ARRAYSIZE( g_pszItemPickupMethodStringsUnloc ) || iMethod < 0 )
+ iMethod = 0;
+ EconUI()->Gamestats_ItemTransaction( IE_ITEM_RECEIVED, pItem, g_pszItemPickupMethodStringsUnloc[iMethod] );
+
+ // Then move it to the first empty backpack position
+ if ( bMoveToBackpack )
+ {
+ SetItemBackpackPosition( pItem, 0, false, true );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CEconItemView *CInventoryManager::GetItemByBackpackPosition( int iBackpackPosition )
+{
+ CPlayerInventory *pInventory = GetLocalInventory();
+ if ( !pInventory )
+ return NULL;
+
+ // Backpack positions start from 1
+ Assert( iBackpackPosition > 0 && iBackpackPosition <= pInventory->GetMaxItemCount() );
+ for ( int i = 0; i < pInventory->GetItemCount(); i++ )
+ {
+ CEconItemView *pItem = pInventory->GetItem(i);
+ if ( GetBackpackPositionFromBackend( pItem->GetInventoryPosition() ) == iBackpackPosition )
+ return pItem;
+ }
+
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CInventoryManager::HasBeenAckedByClient( CEconItemView *pItem )
+{
+ return ( GetAckKeyForItem( pItem ) != NULL );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CInventoryManager::SetAckedByClient( CEconItemView *pItem )
+{
+ VerifyAckFileLoaded();
+
+ static char szTmp[128];
+ Q_snprintf( szTmp, sizeof(szTmp), "%llu", pItem->GetItemID() );
+ m_pkvItemClientAckFile->SetInt( szTmp, 1 );
+
+ m_bClientAckDirty = true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CInventoryManager::SetAckedByGC( CEconItemView *pItem, bool bSave )
+{
+ KeyValues *pkvItem = GetAckKeyForItem( pItem );
+ if ( pkvItem )
+ {
+ m_pkvItemClientAckFile->RemoveSubKey( pkvItem );
+ pkvItem->deleteThis();
+
+ m_bClientAckDirty = true;
+
+ if ( bSave )
+ {
+ SaveAckFile();
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+KeyValues *CInventoryManager::GetAckKeyForItem( CEconItemView *pItem )
+{
+ VerifyAckFileLoaded();
+
+ static char szTmp[128];
+ Q_snprintf( szTmp, sizeof(szTmp), "%llu", pItem->GetItemID() );
+ return m_pkvItemClientAckFile->FindKey( szTmp );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CInventoryManager::VerifyAckFileLoaded( void )
+{
+ if ( m_pkvItemClientAckFile )
+ return;
+
+ m_pkvItemClientAckFile = new KeyValues( ITEM_CLIENTACK_FILE );
+
+ ISteamRemoteStorage *pRemoteStorage = SteamClient()?(ISteamRemoteStorage *)SteamClient()->GetISteamGenericInterface(
+ SteamAPI_GetHSteamUser(), SteamAPI_GetHSteamPipe(), STEAMREMOTESTORAGE_INTERFACE_VERSION ):NULL;
+
+ if ( pRemoteStorage )
+ {
+ if ( pRemoteStorage->FileExists(ITEM_CLIENTACK_FILE) )
+ {
+ int32 nFileSize = pRemoteStorage->GetFileSize( ITEM_CLIENTACK_FILE );
+
+ if ( nFileSize > 0 )
+ {
+ CUtlBuffer buf( 0, nFileSize );
+ if ( pRemoteStorage->FileRead( ITEM_CLIENTACK_FILE, buf.Base(), nFileSize ) == nFileSize )
+ {
+ buf.SeekPut( CUtlBuffer::SEEK_HEAD, nFileSize );
+ m_pkvItemClientAckFile->ReadAsBinary( buf );
+
+#ifdef _DEBUG
+ if ( item_debug_clientacks.GetBool() )
+ {
+ m_pkvItemClientAckFile->SaveToFile( g_pFullFileSystem, "cfg/tmp_readack.txt", "MOD" );
+ }
+#endif
+ }
+ }
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Clean up any item references that we no longer have items for.
+// This ensures that if we delete an item on the backend, we remove it from the ack file.
+//-----------------------------------------------------------------------------
+void CInventoryManager::CleanAckFile( void )
+{
+ CPlayerInventory *pInventory = InventoryManager()->GetLocalInventory();
+ if ( !pInventory )
+ return;
+
+ if ( !pInventory->RetrievedInventoryFromSteam() )
+ return;
+
+ if ( m_pkvItemClientAckFile )
+ {
+ KeyValues *pKVItem = m_pkvItemClientAckFile->GetFirstSubKey();
+ while ( pKVItem != NULL )
+ {
+ itemid_t ulID = (itemid_t)Q_atoi64( pKVItem->GetName() );
+ if ( pInventory->GetInventoryItemByItemID(ulID) == NULL )
+ {
+ KeyValues *pTmp = pKVItem->GetNextKey();
+ m_pkvItemClientAckFile->RemoveSubKey( pKVItem );
+ pKVItem->deleteThis();
+
+ m_bClientAckDirty = true;
+
+ pKVItem = pTmp;
+ }
+ else
+ {
+ pKVItem = pKVItem->GetNextKey();
+ }
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CInventoryManager::SaveAckFile( void )
+{
+ if ( !m_bClientAckDirty )
+ return;
+ m_bClientAckDirty = false;
+
+ ISteamRemoteStorage *pRemoteStorage = SteamClient()?(ISteamRemoteStorage *)SteamClient()->GetISteamGenericInterface(
+ SteamAPI_GetHSteamUser(), SteamAPI_GetHSteamPipe(), STEAMREMOTESTORAGE_INTERFACE_VERSION ):NULL;
+
+ if ( pRemoteStorage )
+ {
+ CUtlBuffer buf;
+ m_pkvItemClientAckFile->WriteAsBinary( buf );
+ pRemoteStorage->FileWrite( ITEM_CLIENTACK_FILE, buf.Base(), buf.TellPut() );
+
+#ifdef _DEBUG
+ if ( item_debug_clientacks.GetBool() )
+ {
+ m_pkvItemClientAckFile->SaveToFile( g_pFullFileSystem, "cfg/tmp_saveack.txt", "MOD" );
+ }
+#endif
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: GC sent name of account down
+//-----------------------------------------------------------------------------
+class CGCLookupAccountNameResponse : public GCSDK::CGCClientJob
+{
+public:
+ CGCLookupAccountNameResponse( GCSDK::CGCClient *pClient ) : GCSDK::CGCClientJob( pClient ) {}
+ virtual bool BYieldingRunGCJob( GCSDK::IMsgNetPacket *pNetPacket )
+ {
+ GCSDK::CGCMsg<MsgGCLookupAccountNameResponse_t> msg( pNetPacket );
+
+ CUtlString playerName;
+ if ( msg.BReadStr( &playerName ) )
+ {
+ InventoryManager()->PersonaName_Store( msg.Body().m_unAccountID, playerName.Get() );
+ }
+ return true;
+ }
+};
+GC_REG_JOB( GCSDK::CGCClient, CGCLookupAccountNameResponse, "CGCLookupAccountNameResponse", k_EMsgGCLookupAccountNameResponse, GCSDK::k_EServerTypeGCClient );
+
+class CGCLookupMultipleAccountsNameResponse : public GCSDK::CGCClientJob
+{
+public:
+ CGCLookupMultipleAccountsNameResponse( GCSDK::CGCClient *pClient ) : GCSDK::CGCClientJob( pClient ) {}
+ virtual bool BYieldingRunGCJob( GCSDK::IMsgNetPacket *pNetPacket )
+ {
+ CProtoBufMsg<CMsgLookupMultipleAccountNamesResponse> msg( pNetPacket );
+ for ( int i = 0 ; i < msg.Body().accounts_size() ; ++i )
+ {
+ const CMsgLookupMultipleAccountNamesResponse_Account &account = msg.Body().accounts( i );
+ InventoryManager()->PersonaName_Store( account.accountid(), account.persona().c_str() );
+ }
+
+ return true;
+ }
+};
+GC_REG_JOB( GCSDK::CGCClient, CGCLookupMultipleAccountsNameResponse, "CGCLookupMultipleAccountsNameResponse", k_EMsgGCLookupMultipleAccountNamesResponse, GCSDK::k_EServerTypeGCClient );
+
+void CInventoryManager::PersonaName_Precache( uint32 unAccountID )
+{
+ const char *pszName = PersonaName_Get( unAccountID );
+ if ( pszName == NULL )
+ {
+ // Queue request name from GC
+ m_msgPendingLookupAccountNames.add_accountids( unAccountID );
+
+ // insert empty string so we don't ask again
+ m_mapPersonaNamesCache.Insert( unAccountID, "" );
+ }
+}
+
+const char *CInventoryManager::PersonaName_Get( uint32 unAccountID )
+{
+ tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s", __FUNCTION__ );
+
+ // First ask Steam if this is one of friends -- if so we can get an up-to-date persona name.
+ {
+ const char *pszName = NULL;
+
+ if ( steamapicontext && steamapicontext->SteamUser() && steamapicontext->SteamFriends() )
+ {
+ CSteamID steamID = steamapicontext->SteamUser()->GetSteamID();
+ steamID.SetAccountID( unAccountID );
+ uint64 u64AccountId = steamID.ConvertToUint64();
+
+ // We're covering three states here:
+ // 1. We've never asked before. We need to queue up a RequestUserInformation.
+ // 2. We've asked before, and we haven't heard back yet
+ // 3. We've asked before, we heard back. Don't re-request user information.
+ auto index = m_personaNameRequests.Find( u64AccountId );
+ if ( !m_personaNameRequests.IsValidIndex( index ) )
+ {
+ // This is case 1--we've never asked before.
+
+ // If RequestUserInformation returns false, the information is already available.
+ // Otherwise, it will arrive later and we need to rebuild the description at that time.
+ if ( !steamapicontext->SteamFriends()->RequestUserInformation( steamID, true ) )
+ {
+ pszName = steamapicontext->SteamFriends()->GetFriendPersonaName( steamID );
+ Assert( pszName ); // Guaranteed by the steam api
+
+ if ( Q_strncmp( pszName, "[unknown]", ARRAYSIZE( "[unknown]" ) ) != 0 )
+ {
+ m_mapPersonaNamesCache.InsertOrReplace( unAccountID, pszName );
+ return pszName;
+ }
+ }
+ else
+ {
+ // This is case 2, we've asked above.
+ m_personaNameRequests.Insert( u64AccountId, false );
+ }
+ }
+ else
+ {
+ if ( m_personaNameRequests[ index ] )
+ {
+ // This is case 3.
+ pszName = steamapicontext->SteamFriends()->GetFriendPersonaName( steamID );
+ Assert( pszName ); // Guaranteed by the steam api
+
+ if ( Q_strncmp( pszName, "[unknown]", ARRAYSIZE( "[unknown]" ) ) != 0 )
+ {
+ m_mapPersonaNamesCache.InsertOrReplace( unAccountID, pszName );
+ return pszName;
+ }
+ }
+ }
+ }
+ }
+
+ // If that didn't work, ask the server we're playing on if they know this account ID.
+ CBasePlayer *pPlayer = GetPlayerByAccountID( unAccountID );
+ if ( pPlayer )
+ {
+ const char *pszPlayerName = pPlayer->GetPlayerName();
+ if ( pszPlayerName )
+ {
+ m_mapPersonaNamesCache.InsertOrReplace( unAccountID, pszPlayerName );
+ return pszPlayerName;
+ }
+ }
+
+ // If *that* didn't work, look in our cache populated by the GC (or the above paths). This
+ // might be out of date but it's better than nothing.
+ int idx = m_mapPersonaNamesCache.Find( unAccountID );
+ if ( m_mapPersonaNamesCache.IsValidIndex( idx ) )
+ {
+ return m_mapPersonaNamesCache[idx].Get();
+ }
+
+ return "[unknown]";
+}
+
+void CInventoryManager::PersonaName_Store( uint32 unAccountID, const char *pPersonaName )
+{
+ m_mapPersonaNamesCache.InsertOrReplace( unAccountID, pPersonaName );
+}
+
+#endif // CLIENT_DLL
+
+
+//=======================================================================================================================
+// PLAYER INVENTORY
+//=======================================================================================================================
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CPlayerInventory::CPlayerInventory( void )
+{
+ m_bGotItemsFromSteam = false;
+ m_iPendingRequests = 0;
+ m_aInventoryItems.Purge();
+ m_pSOCache = NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CPlayerInventory::~CPlayerInventory()
+{
+ FOR_EACH_VEC( m_vecItemHandles, i )
+ {
+ m_vecItemHandles[ i ]->InventoryIsBeingDeleted();
+ }
+ m_vecItemHandles.Purge();
+
+ if ( m_iPendingRequests )
+ {
+ InventoryManager()->RemovePendingRequest( &m_OwnerID );
+ m_iPendingRequests = 0;
+ }
+
+ SOClear();
+
+ InventoryManager()->DeregisterInventory( this );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CPlayerInventory::SOClear()
+{
+ if ( m_OwnerID.IsValid() )
+ {
+ CGCClientSystem *pClientSystem = GCClientSystem();
+ Assert ( pClientSystem != NULL );
+ if ( pClientSystem != NULL )
+ {
+ CGCClient *pClient = pClientSystem->GetGCClient();
+ Assert ( pClient != NULL );
+ pClient->RemoveSOCacheListener( m_OwnerID, this );
+ }
+ }
+
+ // Somebody registered as a listener through us, but now our Steam ID
+ // is changing? This is bad news.
+ Assert( m_vecListeners.Count() == 0 );
+ while ( m_vecListeners.Count() > 0 )
+ {
+ RemoveListener( m_vecListeners[0] );
+ }
+
+ // If we were subscribed, we should have gotten our unsubscribe message,
+ // and that should have cleared the pointer
+ Assert( m_pSOCache == NULL);
+ m_pSOCache = NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CPlayerInventory::AddItemHandle( CEconItemViewHandle* pHandle )
+{
+ FOR_EACH_VEC( m_vecItemHandles, i )
+ {
+ if ( m_vecItemHandles[ i ] == pHandle )
+ {
+ Assert( !"Item handle already in list to track!" );
+ return;
+ }
+ }
+
+ m_vecItemHandles.AddToTail( pHandle );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CPlayerInventory::RemoveItemHandle( CEconItemViewHandle* pHandle )
+{
+ FOR_EACH_VEC( m_vecItemHandles, i )
+ {
+ if ( m_vecItemHandles[ i ] == pHandle )
+ {
+ m_vecItemHandles.Remove( i );
+ return;
+ }
+ }
+
+ Assert( !"Could not find item handle to remove!" );
+}
+
+
+void CPlayerInventory::Clear()
+{
+ SOClear();
+ m_OwnerID = CSteamID();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CPlayerInventory::RequestInventory( CSteamID pSteamID )
+{
+ // Make sure we don't already have somebody else's stuff
+ // on hand
+ if ( m_OwnerID != pSteamID )
+ SOClear();
+
+ // Remember whose inventory we're looking at
+ m_OwnerID = pSteamID;
+
+ // SteamID must be valid
+ if ( !m_OwnerID.IsValid() || !m_OwnerID.BIndividualAccount() )
+ {
+ Assert( m_OwnerID.IsValid() );
+ Assert( m_OwnerID.BIndividualAccount() );
+ return;
+ }
+
+ // If we don't already have an SO cache, then ask the GC for one,
+ // and start listening to it. We will receive our "subscribed" message
+ // when the data is valid
+ GCClientSystem()->GetGCClient()->AddSOCacheListener( m_OwnerID, this );
+}
+
+void CPlayerInventory::AddListener( GCSDK::ISharedObjectListener *pListener )
+{
+ Assert( m_OwnerID.IsValid() );
+ if ( m_vecListeners.Find( pListener ) < 0 )
+ {
+ m_vecListeners.AddToTail( pListener );
+ GCClientSystem()->GetGCClient()->AddSOCacheListener( m_OwnerID, pListener );
+ }
+}
+
+
+void CPlayerInventory::RemoveListener( GCSDK::ISharedObjectListener *pListener )
+{
+ if ( m_OwnerID.IsValid() )
+ {
+ m_vecListeners.FindAndFastRemove( pListener );
+ GCClientSystem()->GetGCClient()->RemoveSOCacheListener( m_OwnerID, pListener );
+ }
+ else
+ {
+ Assert( m_vecListeners.Count() == 0 );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Helper function to add a new item for a econ item
+//-----------------------------------------------------------------------------
+bool CPlayerInventory::AddEconItem( CEconItem * pItem, bool bUpdateAckFile, bool bWriteAckFile, bool bCheckForNewItems )
+{
+ CEconItemView newItem;
+ if( !FilloutItemFromEconItem( &newItem, pItem ) )
+ {
+ return false;
+ }
+
+ int iIdx = m_aInventoryItems.Insert( newItem );
+
+ DirtyItemHandles();
+
+ ItemHasBeenUpdated( &m_aInventoryItems[iIdx], bUpdateAckFile, bWriteAckFile );
+
+#ifdef CLIENT_DLL
+ if ( bCheckForNewItems && InventoryManager()->GetLocalInventory() == this )
+ {
+ bool bNotify = IsUnacknowledged( pItem->GetInventoryToken() );
+ // ignore Halloween drops
+ bNotify &= pItem->GetOrigin() != kEconItemOrigin_HalloweenDrop;
+ // only notify for specific reasons
+ unacknowledged_item_inventory_positions_t reason = GetUnacknowledgedReason( pItem->GetInventoryToken() );
+ switch ( reason )
+ {
+ case UNACK_ITEM_UNKNOWN:
+ case UNACK_ITEM_DROPPED:
+ case UNACK_ITEM_SUPPORT:
+ case UNACK_ITEM_EARNED:
+ case UNACK_ITEM_REFUNDED:
+ case UNACK_ITEM_COLLECTION_REWARD:
+ case UNACK_ITEM_TRADED:
+ case UNACK_ITEM_GIFTED:
+ case UNACK_ITEM_QUEST_LOANER:
+ case UNACK_ITEM_VIRAL_COMPETITIVE_BETA_PASS_SPREAD:
+ break;
+ default:
+ bNotify = false;
+ break;
+ }
+
+ if ( bNotify && !pItem->GetItemDefinition()->IsHidden() )
+ {
+ OnHasNewItems();
+ }
+ }
+#endif
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Creates a script item and associates it with this econ item
+//-----------------------------------------------------------------------------
+void CPlayerInventory::SOCreated( const CSteamID & steamIDOwner, const GCSDK::CSharedObject *pObject, GCSDK::ESOCacheEvent eEvent )
+{
+ tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s", __FUNCTION__ );
+
+ if( pObject->GetTypeID() != CEconItem::k_nTypeID )
+ return;
+ Assert( steamIDOwner == m_OwnerID );
+ if ( steamIDOwner != m_OwnerID )
+ return;
+
+ // We shouldn't get these notifications unless we're subscribed, right?
+ if ( m_pSOCache == NULL)
+ {
+ Assert( m_pSOCache );
+ return;
+ }
+
+ // Don't bother unless it's an incremental notification.
+ // For mass updates, we'll do everything more efficiently in one place
+ if ( eEvent != GCSDK::eSOCacheEvent_Incremental )
+ {
+ Assert( eEvent == GCSDK::eSOCacheEvent_Subscribed || eEvent == GCSDK::eSOCacheEvent_Resubscribed || eEvent == GCSDK::eSOCacheEvent_ListenerAdded );
+ return;
+ }
+
+ CEconItem *pItem = (CEconItem *)pObject;
+ AddEconItem( pItem, true, true, true );
+ SendInventoryUpdateEvent();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Updates the script item associated with this econ item
+//-----------------------------------------------------------------------------
+void CPlayerInventory::SOUpdated( const CSteamID & steamIDOwner, const GCSDK::CSharedObject *pObject, GCSDK::ESOCacheEvent eEvent )
+{
+ if( pObject->GetTypeID() != CEconItem::k_nTypeID )
+ return;
+ Assert( steamIDOwner == m_OwnerID );
+ if ( steamIDOwner != m_OwnerID )
+ return;
+
+ // We shouldn't get these notifications unless we're subscribed, right?
+ if ( m_pSOCache == NULL)
+ {
+ Assert( m_pSOCache );
+ return;
+ }
+
+ // Don't bother unless it's an incremental notification.
+ // For mass updates, we'll do everything more efficiently in one place
+ if ( eEvent != GCSDK::eSOCacheEvent_Incremental )
+ {
+ Assert( eEvent == GCSDK::eSOCacheEvent_Subscribed || eEvent == GCSDK::eSOCacheEvent_Resubscribed );
+ return;
+ }
+
+ CEconItem *pEconItem = (CEconItem *)pObject;
+
+ bool bChanged = false;
+ CEconItemView *pScriptItem = GetInventoryItemByItemID( pEconItem->GetItemID() );
+ if ( pScriptItem )
+ {
+ if ( FilloutItemFromEconItem( pScriptItem, pEconItem ) )
+ {
+ ItemHasBeenUpdated( pScriptItem, false, false );
+ }
+
+ bChanged = true;
+ }
+ else
+ {
+ // The item isn't in this inventory right now. But it may need to be
+ // after the update, so try adding it and see if the inventory wants it.
+ bChanged = AddEconItem( pEconItem, false, false, false );
+ }
+
+ if ( bChanged )
+ {
+ ResortInventory();
+
+ DirtyItemHandles();
+
+#ifdef CLIENT_DLL
+ // Client doesn't update inventory while items are moving in a backpack sort. Does it once at the sort end instead.
+ if ( !InventoryManager()->IsInBackpackSort() )
+#endif
+ {
+ SendInventoryUpdateEvent();
+ }
+#ifdef _DEBUG
+ if ( item_inventory_debug.GetBool() )
+ {
+ DumpInventoryToConsole( true );
+ }
+#endif
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Removes the script item associated with this econ item
+//-----------------------------------------------------------------------------
+void CPlayerInventory::SODestroyed( const CSteamID & steamIDOwner, const GCSDK::CSharedObject *pObject, GCSDK::ESOCacheEvent eEvent )
+{
+ if( pObject->GetTypeID() != CEconItem::k_nTypeID )
+ return;
+ Assert( steamIDOwner == m_OwnerID );
+ if ( steamIDOwner != m_OwnerID )
+ return;
+
+ // We shouldn't get these notifications unless we're subscribed, right?
+ if ( m_pSOCache == NULL)
+ {
+ Assert( m_pSOCache );
+ return;
+ }
+
+ // Don't bother unless it's an incremental notification.
+ // For mass updates, we'll do everything more efficiently in one place
+ if ( eEvent != GCSDK::eSOCacheEvent_Incremental )
+ {
+ Assert( eEvent == GCSDK::eSOCacheEvent_Subscribed || eEvent == GCSDK::eSOCacheEvent_Resubscribed );
+ return;
+ }
+
+ CEconItem *pEconItem = (CEconItem *)pObject;
+ RemoveItem( pEconItem->GetItemID() );
+
+#ifdef CLIENT_DLL
+ InventoryManager()->OnItemDeleted( this );
+#endif
+
+ SendInventoryUpdateEvent();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: This is our initial notification that this cache has been received
+// from the server.
+//-----------------------------------------------------------------------------
+void CPlayerInventory::SOCacheSubscribed( const CSteamID & steamIDOwner, GCSDK::ESOCacheEvent eEvent )
+{
+ // Make sure we expect notifications about this guy
+ Assert( steamIDOwner == m_OwnerID );
+ if ( steamIDOwner != m_OwnerID )
+ return;
+
+ #ifdef _DEBUG
+ Msg("CPlayerInventory::SOCacheSubscribed\n");
+ #endif
+
+ // Clear our old inventory
+ m_aInventoryItems.Purge();
+
+ DirtyItemHandles();
+
+ // Locate the cache that was just subscribed to
+ m_pSOCache = GCClientSystem()->GetSOCache( m_OwnerID );
+ if ( m_pSOCache == NULL )
+ {
+ Assert( m_pSOCache != NULL );
+ return;
+ }
+
+ // add all the items already in the inventory
+ CSharedObjectTypeCache *pTypeCache = m_pSOCache->FindTypeCache( CEconItem::k_nTypeID );
+ if( pTypeCache )
+ {
+ for( uint32 unItem = 0; unItem < pTypeCache->GetCount(); unItem++ )
+ {
+ CEconItem *pItem = (CEconItem *)pTypeCache->GetObject( unItem );
+ AddEconItem(pItem, true, false, true );
+ }
+ }
+
+ m_bGotItemsFromSteam = true;
+
+#ifdef CLIENT_DLL
+ if ( InventoryManager()->GetLocalInventory() == this )
+ {
+ // Only validate the local player inventory
+ ValidateInventoryPositions();
+
+ // tell the entire client that we're 'connected' to the GC now
+ CInventoryManager::SendGCConnectedEvent();
+ }
+#endif
+
+ ResortInventory();
+
+#ifdef CLIENT_DLL
+ // Now that we've read all the items in, write out the ack file (only if we're the local inventory)
+ if ( InventoryManager()->GetLocalInventory() == this )
+ {
+ InventoryManager()->CleanAckFile();
+ InventoryManager()->SaveAckFile();
+ }
+#endif
+}
+
+bool CInventoryManager::IsValidPlayerClass( equipped_class_t unClass )
+{
+ const bool bResult = ItemSystem()->GetItemSchema()->IsValidClass( unClass );
+ AssertMsg( bResult, "Invalid player class!" );
+ return bResult;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Removes the script item associated with this econ item
+//-----------------------------------------------------------------------------
+void CPlayerInventory::ValidateInventoryPositions( void )
+{
+#ifdef TF2
+ if ( engine->GetAppID() == 520 )
+ {
+ TFInventoryManager()->DeleteUnknowns( this );
+ }
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CPlayerInventory::ItemHasBeenUpdated( CEconItemView *pItem, bool bUpdateAckFile, bool bWriteAckFile )
+{
+#ifdef CLIENT_DLL
+ // Handle the clientside ack file
+ if ( bUpdateAckFile && !IsUnacknowledged(pItem->GetInventoryPosition()) )
+ {
+ if ( InventoryManager()->GetLocalInventory() == this )
+ {
+ InventoryManager()->SetAckedByGC( pItem, bWriteAckFile );
+ }
+ }
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CPlayerInventory::SOCacheUnsubscribed( const CSteamID & steamIDOwner, GCSDK::ESOCacheEvent eEvent )
+{
+ m_pSOCache = NULL;
+ m_bGotItemsFromSteam = false;
+ m_aInventoryItems.Purge();
+
+ DirtyItemHandles();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: On the client this sends the "inventory_updated" event. On the server
+// it does nothing.
+//-----------------------------------------------------------------------------
+void CPlayerInventory::SendInventoryUpdateEvent()
+{
+#ifdef CLIENT_DLL
+ if( InventoryManager()->GetLocalInventory() == this )
+ {
+ IGameEvent *event = gameeventmanager->CreateEvent( "inventory_updated" );
+ if ( event )
+ {
+ gameeventmanager->FireEventClientSide( event );
+ }
+ }
+#endif
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Fills out all the fields in the script item based on what's in the
+// econ item
+//-----------------------------------------------------------------------------
+bool CPlayerInventory::FilloutItemFromEconItem( CEconItemView *pScriptItem, CEconItem *pEconItem )
+{
+ // We need to detect the case where items have been updated & moved bags / positions.
+ uint32 iOldPos = pScriptItem->GetInventoryPosition();
+ bool bWasInThisBag = ItemShouldBeIncluded( iOldPos );
+
+ // Ignore items that this inventory doesn't care about
+ if ( !ItemShouldBeIncluded( pEconItem->GetInventoryToken() ) )
+ {
+ // The item has been moved out of this bag. Ensure our derived inventory classes know.
+ if ( bWasInThisBag )
+ {
+ // We need to update it before it's removed.
+ ItemHasBeenUpdated( pScriptItem, false, false );
+
+ RemoveItem( pEconItem->GetItemID() );
+ }
+
+ return false;
+ }
+
+ pScriptItem->Init( pEconItem->GetDefinitionIndex(), pEconItem->GetQuality(), pEconItem->GetItemLevel(), pEconItem->GetAccountID() );
+ if ( !pScriptItem->IsValid() )
+ return false;
+
+ pScriptItem->SetItemID( pEconItem->GetItemID() );
+
+ pScriptItem->SetInventoryPosition( pEconItem->GetInventoryToken() );
+ OnItemChangedPosition( pScriptItem, iOldPos );
+
+#if BUILD_ITEM_NAME_AND_DESC
+ // Precache account names if we have any. We do this way in advance of any code that might
+ // use it (ie., description text building) so that by the time we try that we already have
+ // the data setup.
+ //
+ // We don't worry about yielding here because this inventory code only runs on game
+ // clients/servers, not the GC.
+ CSteamAccountIDAttributeCollector AccountIDCollector;
+ pEconItem->IterateAttributes( &AccountIDCollector );
+
+ FOR_EACH_VEC( AccountIDCollector.GetAccountIDs(), i )
+ {
+ InventoryManager()->PersonaName_Precache( (AccountIDCollector.GetAccountIDs())[i] );
+ }
+#endif
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CPlayerInventory::DumpInventoryToConsole( bool bRoot )
+{
+ if ( bRoot )
+ {
+#ifdef CLIENT_DLL
+ Msg("(CLIENT) Inventory:\n");
+#else
+ Msg("(SERVER) Inventory for account (%d):\n", m_OwnerID.GetAccountID() );
+#endif
+ Msg(" Version: %llu:\n", m_pSOCache ? m_pSOCache->GetVersion() : -1 );
+ }
+
+ int iCount = m_aInventoryItems.Count();
+ Msg(" Num items: %d\n", iCount );
+ for ( int i = 0; i < iCount; i++ )
+ {
+ Msg(" %s (ID %llu)\n", m_aInventoryItems[i].GetStaticData()->GetDefinitionName(), m_aInventoryItems[i].GetItemID() );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CPlayerInventory::RemoveItem( itemid_t iItemID )
+{
+ int iIndex;
+ CEconItemView *pItem = GetInventoryItemByItemID( iItemID, &iIndex );
+ if ( pItem )
+ {
+ ItemIsBeingRemoved( pItem );
+
+ FOR_EACH_VEC( m_vecItemHandles, i )
+ {
+ m_vecItemHandles[ i ]->MarkDirty();
+ m_vecItemHandles[ i ]->ItemIsBeingDeleted( pItem );
+ }
+
+ m_aInventoryItems.Remove(iIndex);
+
+#ifdef _DEBUG
+ if ( item_inventory_debug.GetBool() )
+ {
+ DumpInventoryToConsole( true );
+ }
+#endif
+ }
+
+ // Don't need to resort because items will still be in order
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Finds the item in our inventory that matches the specified global index
+//-----------------------------------------------------------------------------
+CEconItemView *CPlayerInventory::GetInventoryItemByItemID( itemid_t iIndex, int *pIndex )
+{
+ int iCount = m_aInventoryItems.Count();
+ for ( int i = 0; i < iCount; i++ )
+ {
+ if ( m_aInventoryItems[i].GetItemID() == iIndex )
+ {
+ if ( pIndex )
+ {
+ *pIndex = i;
+ }
+
+ return &m_aInventoryItems[i];
+ }
+ }
+
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Finds the item in our inventory that matches the specified global original id
+//-----------------------------------------------------------------------------
+CEconItemView *CPlayerInventory::GetInventoryItemByOriginalID( itemid_t iOriginalID, int *pIndex /*= NULL*/ )
+{
+ int iCount = m_aInventoryItems.Count();
+ for ( int i = 0; i < iCount; i++ )
+ {
+ CEconItem *pItem = m_aInventoryItems[i].GetSOCData();
+ if ( pItem && pItem->GetOriginalID() == iOriginalID )
+ {
+ if ( pIndex )
+ {
+ *pIndex = i;
+ }
+
+ return &m_aInventoryItems[i];
+ }
+ }
+
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Finds the item in our inventory in the specified position
+//-----------------------------------------------------------------------------
+CEconItemView *CPlayerInventory::GetItemByPosition( int iPosition, int *pIndex )
+{
+ int iCount = m_aInventoryItems.Count();
+ for ( int i = 0; i < iCount; i++ )
+ {
+ if ( m_aInventoryItems[i].GetInventoryPosition() == (unsigned int)iPosition )
+ {
+ if ( pIndex )
+ {
+ *pIndex = i;
+ }
+
+ return &m_aInventoryItems[i];
+ }
+ }
+
+ return NULL;
+}
+
+// Finds the first item in our backpack with match itemdef
+//-----------------------------------------------------------------------------
+CEconItemView *CPlayerInventory::FindFirstItembyItemDef( item_definition_index_t iItemDef )
+{
+ int iCount = m_aInventoryItems.Count();
+ for ( int i = 0; i < iCount; i++ )
+ {
+ //GetItemDefIndex()
+ if ( m_aInventoryItems[i].GetItemDefIndex() == iItemDef )
+ {
+ return &m_aInventoryItems[i];
+ }
+ }
+
+ return NULL;
+
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Get the index for the item in our inventory utlvector
+//-----------------------------------------------------------------------------
+int CPlayerInventory::GetIndexForItem( CEconItemView *pItem )
+{
+ int iCount = m_aInventoryItems.Count();
+ for ( int i = 0; i < iCount; i++ )
+ {
+ if ( m_aInventoryItems[i].GetItemID() == pItem->GetItemID() )
+ return i;
+ }
+
+ return -1;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Dirty all the item handles that are registered with us
+//-----------------------------------------------------------------------------
+void CPlayerInventory::DirtyItemHandles()
+{
+ FOR_EACH_VEC( m_vecItemHandles, i )
+ {
+ m_vecItemHandles[ i ]->MarkDirty();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Get the item object cache data for the specified item
+//-----------------------------------------------------------------------------
+CEconItem *CPlayerInventory::GetSOCDataForItem( itemid_t iItemID )
+{
+ if ( !m_pSOCache )
+ return NULL;
+
+ CEconItem soIndex;
+ soIndex.SetItemID( iItemID );
+ return (CEconItem *)m_pSOCache->FindSharedObject( soIndex );
+}
+
+#if defined (_DEBUG) && defined(CLIENT_DLL)
+CON_COMMAND_F( item_deleteall, "WARNING: Removes all of the items in your inventory.", FCVAR_CHEAT )
+{
+ CPlayerInventory *pInventory = InventoryManager()->GetLocalInventory();
+ if ( !pInventory )
+ return;
+
+ int iCount = pInventory->GetItemCount();
+ for ( int i = 0; i < iCount; i++ )
+ {
+ CEconItemView *pItem = pInventory->GetItem(i);
+ if ( pItem )
+ {
+ InventoryManager()->DropItem( pItem->GetItemID() );
+ }
+ }
+
+ InventoryManager()->UpdateLocalInventory();
+}
+#endif
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+int CPlayerInventory::GetRecipeCount() const
+{
+ const CUtlMap<int, CEconCraftingRecipeDefinition *, int>& mapRecipes = ItemSystem()->GetItemSchema()->GetRecipeDefinitionMap();
+
+ return mapRecipes.Count();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+const CEconCraftingRecipeDefinition *CPlayerInventory::GetRecipeDef( int iIndex )
+{
+ if ( !m_pSOCache )
+ return NULL;
+
+ if ( iIndex < 0 || iIndex >= GetRecipeCount() )
+ return NULL;
+
+ const CEconItemSchema::RecipeDefinitionMap_t& mapRecipes = GetItemSchema()->GetRecipeDefinitionMap();
+
+ // Store off separate index for "number of items iterated over" in case something
+ // deletes from the recipes map out from under us.
+ int j = 0;
+ FOR_EACH_MAP_FAST( mapRecipes, i )
+ {
+ if ( j == iIndex )
+ return mapRecipes[i];
+
+ j++;
+ }
+
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+const CEconCraftingRecipeDefinition *CPlayerInventory::GetRecipeDefByDefIndex( uint16 iDefIndex )
+{
+ if ( !m_pSOCache )
+ return NULL;
+
+ // check always-known recipes
+ const CUtlMap<int, CEconCraftingRecipeDefinition *, int>& mapRecipes = ItemSystem()->GetItemSchema()->GetRecipeDefinitionMap();
+ int i = mapRecipes.Find( iDefIndex );
+ if ( i != mapRecipes.InvalidIndex() )
+ return mapRecipes[i];
+
+ // there are no more SO recipes
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CEconItemViewHandle::SetItem( CEconItemView* pItem )
+{
+ m_pItem = pItem;
+
+ if ( pItem )
+ {
+ // Cache the item_id for lookup when our pointer gets dirtied
+ m_nItemID = pItem->GetItemID();
+ auto* pInv = InventoryManager()->GetInventoryForAccount( pItem->GetAccountID() );
+ Assert( pInv );
+ if ( m_pInv != pInv )
+ {
+ // If this is a different inventory, unsubscribe. This can happen if the
+ // handle gets reused
+ if ( m_pInv )
+ {
+ m_pInv->RemoveItemHandle( this );
+ }
+
+ m_pInv = pInv;
+
+ // Subscribe to the new inventory
+ m_pInv->AddItemHandle( this );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Return a pointer to a CEconItemView
+//-----------------------------------------------------------------------------
+CEconItemView* CEconItemViewHandle::Get() const
+{
+ // If our pointer is dirty, we need to go get a new pointer
+ if ( m_bPointerDirty )
+ {
+ if ( m_pInv )
+ {
+ m_pItem = m_pInv->GetInventoryItemByItemID( m_nItemID );
+ m_bPointerDirty = false;
+ }
+ }
+
+ return m_pItem;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Unsubscribe us from future updates
+//-----------------------------------------------------------------------------
+CEconItemHandle::~CEconItemHandle()
+{
+ UnsubscribeFromSOEvents();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Save a pointer to the item and register us for SOCache events
+//-----------------------------------------------------------------------------
+void CEconItemHandle::SetItem( CEconItem* pItem )
+{
+ UnsubscribeFromSOEvents();
+
+ m_pItem = NULL;
+ m_iItemID = INVALID_ITEM_ID;
+
+ if ( pItem )
+ {
+ auto* pInv = InventoryManager()->GetInventoryForAccount( pItem->GetAccountID() );
+ if ( pInv )
+ {
+ m_OwnerSteamID.SetFromUint64( pInv->GetOwner().ConvertToUint64() );
+ GCClientSystem()->GetGCClient()->AddSOCacheListener( m_OwnerSteamID, this );
+ }
+
+ m_pItem = pItem;
+ m_iItemID = pItem->GetID();
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Check if out item got deleted. If it did, mark our pointer as NULL
+// so future dereferences will get NULL instead of a stale pointer.
+//-----------------------------------------------------------------------------
+void CEconItemHandle::SODestroyed( const CSteamID & steamIDOwner, const GCSDK::CSharedObject *pObject, GCSDK::ESOCacheEvent eEvent )
+{
+ if( pObject->GetTypeID() != CEconItem::k_nTypeID || m_pItem == NULL )
+ return;
+
+ const CEconItem *pItem = (CEconItem *)pObject;
+
+ if ( m_iItemID == pItem->GetID() )
+ {
+ UnsubscribeFromSOEvents();
+ m_pItem = NULL;
+ m_iItemID = INVALID_ITEM_ID;
+ }
+}
+
+void CEconItemHandle::SOCreated( const CSteamID & steamIDOwner, const GCSDK::CSharedObject *pObject, GCSDK::ESOCacheEvent eEvent )
+{
+ if( pObject->GetTypeID() != CEconItem::k_nTypeID )
+ return;
+
+ CEconItem *pItem = (CEconItem *)pObject;
+
+ if ( m_iItemID == pItem->GetID() )
+ {
+ SetItem( pItem );
+ }
+}
+
+void CEconItemHandle::SOUpdated( const CSteamID & steamIDOwner, const GCSDK::CSharedObject *pObject, GCSDK::ESOCacheEvent eEvent )
+{
+ if ( pObject->GetTypeID() != CEconItem::k_nTypeID )
+ return;
+
+ CEconItem *pItem = (CEconItem *)pObject;
+
+ if ( m_iItemID == pItem->GetID() )
+ {
+ SetItem( pItem );
+ }
+}
+
+void CEconItemHandle::SOCacheUnsubscribed( const CSteamID & steamIDOwner, GCSDK::ESOCacheEvent eEvent )
+{
+ UnsubscribeFromSOEvents();
+}
+
+void CEconItemHandle::UnsubscribeFromSOEvents()
+{
+ if ( m_OwnerSteamID.GetAccountID() != 0 )
+ {
+ GCClientSystem()->GetGCClient()->RemoveSOCacheListener( m_OwnerSteamID, this );
+ }
+}
+
+
+#if defined( STAGING_ONLY ) || defined( _DEBUG )
+#if defined(CLIENT_DLL)
+CON_COMMAND_F( item_dumpinv, "Dumps the contents of a specified client inventory.", FCVAR_CHEAT )
+#else
+CON_COMMAND_F( item_dumpinv_sv, "Dumps the contents of a specified server inventory.", FCVAR_CHEAT )
+#endif
+{
+#if defined(CLIENT_DLL)
+ CPlayerInventory *pInventory = InventoryManager()->GetLocalInventory();
+#else
+ CSteamID steamID;
+ CBaseMultiplayerPlayer *pPlayer = ToBaseMultiplayerPlayer( UTIL_GetCommandClient() );
+ pPlayer->GetSteamID( &steamID );
+ CPlayerInventory *pInventory = InventoryManager()->GetInventoryForAccount( steamID.GetAccountID() );
+#endif
+ if ( !pInventory )
+ {
+ Msg("No inventory found.\n");
+ return;
+ }
+
+ pInventory->DumpInventoryToConsole( true );
+}
+
+#if defined (CLIENT_DLL)
+
+CON_COMMAND_F( item_dumpschema, "Dump the expanded schema for items to a file in sorted order suitable for diffs. Format: item_dumpschema <filename>", FCVAR_CHEAT )
+{
+ if ( args.ArgC() != 2 )
+ {
+ Msg("Usage: item_dumpschema <filename>\n");
+ return;
+ }
+
+ if ( GetItemSchema()->DumpItems(args[1]) )
+ Msg("Dump complete, saved in game/tf/%s\n", args[1]);
+ else
+ Msg("Dump failed (?)\n");
+}
+
+CON_COMMAND_F( item_giveitem, "Give an item to the local player. Format: item_giveitem <item definition name> or <item def index>", FCVAR_NONE )
+{
+ if ( !steamapicontext || !steamapicontext->SteamUser() )
+ {
+ Msg("Not connected to Steam.\n");
+ return;
+ }
+ CSteamID steamIDForPlayer = steamapicontext->SteamUser()->GetSteamID();
+ if ( !steamIDForPlayer.IsValid() )
+ {
+ Msg("Failed to find a valid steamID for the local player.\n");
+ return;
+ }
+
+ int iItemCount = args.ArgC();
+ for ( int i = 1; i < iItemCount; ++i )
+ {
+ // Check to see if args[1] is a number (itemdefid) and if so, translate it to actual itemname
+ const char *pszItemname = NULL;
+ if ( V_isdigit( args[i][0] ) )
+ {
+ int iDef = V_atoi( args[i] );
+ CEconItemDefinition *pItemDef = GetItemSchema()->GetItemDefinition( iDef );
+ if ( pItemDef )
+ {
+ pszItemname = pItemDef->GetItemDefinitionName();
+ }
+ }
+ else
+ {
+ pszItemname = args[i];
+ }
+
+ Msg("Sending request to generate '%s' for Local Player (%llu)\n", pszItemname, steamIDForPlayer.ConvertToUint64() );
+
+ CItemSelectionCriteria criteria;
+
+ GCSDK::CProtoBufMsg<CMsgDevNewItemRequest> msg( k_EMsgGCDev_NewItemRequest );
+ msg.Body().set_receiver( steamIDForPlayer.ConvertToUint64() );
+
+ criteria.SetIgnoreEnabledFlag( true );
+ if ( !criteria.BAddCondition( "name", k_EOperator_String_EQ, pszItemname, true ) ||
+ !criteria.BSerializeToMsg( *msg.Body().mutable_criteria() ) )
+ {
+ Msg("Failed to add condition and/or serialize item grant request. This is probably caused by having a string that's too long.\n" );
+ return;
+ }
+ GCClientSystem()->BSendMessage( msg );
+ }
+}
+
+CON_COMMAND_F( item_rolllootlist, "Force a loot list rool for the local player. Format: item_rolllootlist <loot list definition name>", FCVAR_NONE )
+{
+ if ( !steamapicontext || !steamapicontext->SteamUser() )
+ {
+ Msg("Not connected to Steam.\n");
+ return;
+ }
+ CSteamID steamIDForPlayer = steamapicontext->SteamUser()->GetSteamID();
+ if ( !steamIDForPlayer.IsValid() )
+ {
+ Msg("Failed to find a valid steamID for the local player.\n");
+ return;
+ }
+
+ Msg("Sending request to roll '%s' for Local Player (%llu)\n", args[1], steamIDForPlayer.ConvertToUint64() );
+
+ GCSDK::CProtoBufMsg<CMsgDevDebugRollLootRequest> msg( k_EMsgGCDev_DebugRollLootRequest );
+ msg.Body().set_receiver( steamIDForPlayer.ConvertToUint64() );
+ msg.Body().set_loot_list_name( args[1] );
+ GCClientSystem()->BSendMessage( msg );
+}
+
+#include "econ_item_description.h"
+#include "localization_provider.h"
+
+CON_COMMAND_F( item_generate_all_descriptions, "Generate full item descriptions for every item in your backpack. Meant as a code test.", FCVAR_CHEAT )
+{
+ CPlayerInventory *pInventory = InventoryManager()->GetLocalInventory();
+
+ for ( int i = 0; i < pInventory->GetItemCount(); i++ )
+ {
+ CEconItemDescription desc;
+ IEconItemDescription::YieldingFillOutEconItemDescription( &desc, GLocalizationProvider(), pInventory->GetItem( i ) );
+ }
+
+ Msg("Done.\n");
+}
+#endif // CLIENT_DLL
+
+#endif // STAGING_ONLY || _DEBUG
+
+
diff --git a/game/shared/econ/econ_item_inventory.h b/game/shared/econ/econ_item_inventory.h
new file mode 100644
index 0000000..fd6e8b8
--- /dev/null
+++ b/game/shared/econ/econ_item_inventory.h
@@ -0,0 +1,472 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Container that allows client & server access to data in player inventories & loadouts
+//
+//=============================================================================
+
+#ifndef ITEM_INVENTORY_H
+#define ITEM_INVENTORY_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "igamesystem.h"
+#include "econ_entity.h"
+#include "gamestringpool.h"
+#include "econ_item_view.h"
+#include "UtlSortVector.h"
+#include "econ_gcmessages.h"
+#include "gc_clientsystem.h"
+
+#if !defined(NO_STEAM)
+#include "steam/steam_api.h"
+#include "gcsdk/gcclientsdk.h"
+#endif // NO_STEAM
+
+
+class CPlayerInventory;
+class CEconItem;
+struct baseitemcriteria_t;
+class CEconItemViewHandle;
+#ifdef CLIENT_DLL
+class ITexture;
+#endif
+
+// Inventory Less function.
+// Used to sort the inventory items into their positions.
+class CInventoryListLess
+{
+public:
+ bool Less( const CEconItemView &src1, const CEconItemView &src2, void *pCtx );
+};
+
+// A class that wants notifications when an inventory is updated
+class IInventoryUpdateListener : public GCSDK::ISharedObjectListener
+{
+public:
+ virtual void InventoryUpdated( CPlayerInventory *pInventory ) = 0;
+
+ virtual void SOCreated( const CSteamID & steamIDOwner, const GCSDK::CSharedObject *pObject, GCSDK::ESOCacheEvent eEvent ) OVERRIDE { InventoryUpdated( NULL ); }
+ virtual void PreSOUpdate( const CSteamID & steamIDOwner, GCSDK::ESOCacheEvent eEvent ) OVERRIDE { /* do nothing */ }
+ virtual void SOUpdated( const CSteamID & steamIDOwner, const GCSDK::CSharedObject *pObject, GCSDK::ESOCacheEvent eEvent ) OVERRIDE { /* do nothing */ }
+ virtual void PostSOUpdate( const CSteamID & steamIDOwner, GCSDK::ESOCacheEvent eEvent ) OVERRIDE { InventoryUpdated( NULL ); }
+ virtual void SODestroyed( const CSteamID & steamIDOwner, const GCSDK::CSharedObject *pObject, GCSDK::ESOCacheEvent eEvent ) OVERRIDE { InventoryUpdated( NULL ); }
+ virtual void SOCacheSubscribed( const CSteamID & steamIDOwner, GCSDK::ESOCacheEvent eEvent ) OVERRIDE { InventoryUpdated( NULL ); }
+ virtual void SOCacheUnsubscribed( const CSteamID & steamIDOwner, GCSDK::ESOCacheEvent eEvent ) OVERRIDE { InventoryUpdated( NULL ); }
+};
+
+//-----------------------------------------------------------------------------
+// Purpose: A single player's inventory.
+// On the client, the inventory manager contains an instance of this for the local player.
+// On the server, each player contains an instance of this.
+//-----------------------------------------------------------------------------
+class CPlayerInventory : public GCSDK::ISharedObjectListener
+{
+ DECLARE_CLASS_NOBASE( CPlayerInventory );
+public:
+ CPlayerInventory();
+ virtual ~CPlayerInventory();
+
+ void Clear();
+
+ // Returns true if this inventory has been filled out by Steam.
+ bool RetrievedInventoryFromSteam( void ) { return m_bGotItemsFromSteam; }
+ bool IsWaitingForSteam( void ) { return (m_iPendingRequests > 0); }
+
+ // Inventory access
+ CSteamID &GetOwner( void ) { return m_OwnerID; }
+ int GetItemCount( void ) const { return m_aInventoryItems.Count(); }
+ virtual bool CanPurchaseItems( int iItemCount ) const { return GetMaxItemCount() - GetItemCount() >= iItemCount; }
+ virtual int GetMaxItemCount( void ) const { return DEFAULT_NUM_BACKPACK_SLOTS; }
+ CEconItemView *GetItem( int i ) { return &m_aInventoryItems[i]; }
+
+ virtual CEconItemView *GetItemInLoadout( int iClass, int iSlot ) { AssertMsg( 0, "Implement me!" ); return NULL; }
+
+ // Get the item object cache data for the specified item
+ CEconItem *GetSOCDataForItem( itemid_t iItemID );
+ GCSDK::CGCClientSharedObjectCache *GetSOC( void ) { return m_pSOCache; }
+
+ // tells the GC systems to forget about this listener
+ void RemoveListener( GCSDK::ISharedObjectListener *pListener );
+
+ // Finds the item in our inventory that matches the specified global index
+ CEconItemView *GetInventoryItemByItemID( itemid_t iIndex, int *pIndex = NULL );
+
+ // Finds the item in our inventory that matches the specified global original id
+ CEconItemView *GetInventoryItemByOriginalID( itemid_t iOriginalID, int *pIndex = NULL );
+
+ // Finds the item in our inventory in the specified position
+ CEconItemView *GetItemByPosition( int iPosition, int *pIndex = NULL );
+
+ // Finds the first item in our backpack with match itemdef
+ CEconItemView *FindFirstItembyItemDef( item_definition_index_t iItemDef );
+
+ // Used to reject items on the backend for inclusion into this inventory.
+ // Mostly used for division of bags into different in-game inventories.
+ virtual bool ItemShouldBeIncluded( int iItemPosition ) { return true; }
+
+ // Debugging
+ virtual void DumpInventoryToConsole( bool bRoot );
+
+ // Extracts the position that should be used to sort items in the inventory from the backend position.
+ // Necessary if your inventory packs a bunch of info into the position instead of using it just as a position.
+ virtual int ExtractInventorySortPosition( uint32 iBackendPosition ) { return iBackendPosition; }
+
+ // Recipe access
+ int GetRecipeCount( void ) const;
+ const CEconCraftingRecipeDefinition *GetRecipeDef( int iIndex );
+ const CEconCraftingRecipeDefinition *GetRecipeDefByDefIndex( uint16 iDefIndex );
+
+ // Item previews
+ virtual int GetPreviewItemDef( void ) const { return 0; };
+
+ // Access helpers
+ virtual void SOClear();
+
+ virtual void NotifyHasNewItems() {}
+
+ void AddItemHandle( CEconItemViewHandle* pHandle );
+ void RemoveItemHandle( CEconItemViewHandle* pHandle );
+
+#ifdef CLIENT_DLL
+ virtual ITexture *GetWeaponSkinBaseLowRes( itemid_t nItemId, int iTeam ) const { return NULL; }
+#endif
+
+
+protected:
+ // Inventory updating, called by the Inventory Manager only. If you want an inventory updated,
+ // use the SteamRequestX functions in CInventoryManager.
+ void RequestInventory( CSteamID pSteamID );
+ void AddListener( GCSDK::ISharedObjectListener *pListener );
+ virtual bool AddEconItem( CEconItem * pItem, bool bUpdateAckFile, bool bWriteAckFile, bool bCheckForNewItems );
+ virtual void RemoveItem( itemid_t iItemID );
+ bool FilloutItemFromEconItem( CEconItemView *pScriptItem, CEconItem *pEconItem );
+ void SendInventoryUpdateEvent();
+ virtual void OnHasNewItems() {}
+ virtual void OnItemChangedPosition( CEconItemView *pItem, uint32 iOldPos ) { return; }
+
+ virtual void SOCreated( const CSteamID & steamIDOwner, const GCSDK::CSharedObject *pObject, GCSDK::ESOCacheEvent eEvent ) OVERRIDE;
+ virtual void PreSOUpdate( const CSteamID & steamIDOwner, GCSDK::ESOCacheEvent eEvent ) OVERRIDE { /* do nothing */ }
+ virtual void SOUpdated( const CSteamID & steamIDOwner, const GCSDK::CSharedObject *pObject, GCSDK::ESOCacheEvent eEvent ) OVERRIDE;
+ virtual void PostSOUpdate( const CSteamID & steamIDOwner, GCSDK::ESOCacheEvent eEvent ) OVERRIDE { /* do nothing */ }
+ virtual void SODestroyed( const CSteamID & steamIDOwner, const GCSDK::CSharedObject *pObject, GCSDK::ESOCacheEvent eEvent ) OVERRIDE;
+ virtual void SOCacheSubscribed( const CSteamID & steamIDOwner, GCSDK::ESOCacheEvent eEvent ) OVERRIDE;
+ virtual void SOCacheUnsubscribed( const CSteamID & steamIDOwner, GCSDK::ESOCacheEvent eEvent ) OVERRIDE;
+
+ void ResortInventory( void ) { m_aInventoryItems.RedoSort( true ); }
+ virtual void ValidateInventoryPositions( void );
+
+ // Derived inventory hooks
+ virtual void ItemHasBeenUpdated( CEconItemView *pItem, bool bUpdateAckFile, bool bWriteAckFile );
+ virtual void ItemIsBeingRemoved( CEconItemView *pItem ) { return; }
+
+ // Get the index for the item in our inventory utlvector
+ int GetIndexForItem( CEconItemView *pItem );
+
+ void DirtyItemHandles();
+
+protected:
+ // The Steam Id of the player who owns this inventory
+ CSteamID m_OwnerID;
+
+ // The items the player has in his inventory, received from steam.
+ CUtlSortVector<CEconItemView,CInventoryListLess> m_aInventoryItems;
+
+ int m_iPendingRequests;
+ bool m_bGotItemsFromSteam;
+
+ GCSDK::CGCClientSharedObjectCache *m_pSOCache;
+
+ CUtlVector<GCSDK::ISharedObjectListener *> m_vecListeners;
+
+ CUtlVector< CEconItemViewHandle* > m_vecItemHandles;
+
+ friend class CInventoryManager;
+};
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+class CInventoryManager : public CAutoGameSystemPerFrame
+{
+ DECLARE_CLASS_GAMEROOT( CInventoryManager, CAutoGameSystem );
+public:
+ CInventoryManager( void );
+
+ // Adds the inventory to the list of inventories that should be maintained.
+ // This causes the game to load the items for the SteamID into this inventory.
+ // NOTE: This fires off a request to Steam. The data will not be filled out immediately.
+ void SteamRequestInventory( CPlayerInventory *pInventory, CSteamID pSteamID, IInventoryUpdateListener *pListener = NULL );
+
+ void PreInitGC();
+ void PostInitGC();
+
+#ifdef CLIENT_DLL
+ void DropItem( itemid_t iItemID );
+ int DeleteUnknowns( CPlayerInventory *pInventory );
+#endif
+
+public:
+ //-----------------------------------------------------------------------
+ // IAutoServerSystem
+ //-----------------------------------------------------------------------
+ virtual bool Init( void ) OVERRIDE;
+ virtual void PostInit( void ) OVERRIDE;
+ virtual void Shutdown() OVERRIDE;
+ virtual void LevelInitPreEntity( void ) OVERRIDE;
+ virtual void LevelShutdownPostEntity( void ) OVERRIDE;
+
+#ifdef CLIENT_DLL
+ // Gets called each frame
+ virtual void Update( float frametime ) OVERRIDE;
+#endif
+
+ void GameServerSteamAPIActivated();
+
+ virtual CPlayerInventory *GetInventoryForAccount( uint32 iAccountID );
+
+ // We're generating a base item. We need to add the game-specific keys to the criteria so that it'll find the right base item.
+ virtual void AddBaseItemCriteria( baseitemcriteria_t *pCriteria, CItemSelectionCriteria *pSelectionCriteria ) { return; }
+
+#ifdef CLIENT_DLL
+ // Must be implemented by derived class
+ virtual bool EquipItemInLoadout( int iClass, int iSlot, itemid_t iItemID ) = 0;
+
+ virtual CPlayerInventory *GeneratePlayerInventoryObject() const { return new CPlayerInventory; }
+
+ //-----------------------------------------------------------------------
+ // ITEM PRESETS
+ //-----------------------------------------------------------------------
+
+ // Is the given preset index valid?
+ bool IsPresetIndexValid( equipped_preset_t unPreset );
+
+ // Equip all items for the given class and preset (all the work is done on the GC -- this just
+ // sends the message up)
+ bool LoadPreset( equipped_class_t unClass, equipped_preset_t unPreset );
+
+ //-----------------------------------------------------------------------
+ // LOCAL INVENTORY
+ //
+ // On the client, we have a single inventory for the local player. Stored here, instead of in the
+ // local player entity, because players need to access it while not being connected to a server.
+ // Override GetLocalInventory() in your inventory manager and return your custom local inventory.
+ //-----------------------------------------------------------------------
+ virtual void UpdateLocalInventory( void );
+ virtual CPlayerInventory *GetLocalInventory( void ) { return NULL; }
+
+ // The local inventory is used to track discards & responses to. We need to
+ // make a decision about inventory space right after sending a delete request,
+ // so we predict the request will work.
+ void OnItemDeleted( CPlayerInventory *pInventory ) { if ( pInventory == GetLocalInventory() ) m_iPredictedDiscards--; }
+
+ virtual void PersonaName_Precache( uint32 unAccountID );
+ virtual const char *PersonaName_Get( uint32 unAccountID );
+ virtual void PersonaName_Store( uint32 unAccountID, const char *pPersonaName );
+
+ static void SendGCConnectedEvent( void );
+
+ // Returns the item at the specified backpack position
+ virtual CEconItemView *GetItemByBackpackPosition( int iBackpackPosition );
+
+ // Moves the item to the specified backpack position. If there's another item as that spot, it swaps positions with it.
+ virtual void MoveItemToBackpackPosition( CEconItemView *pItem, int iBackpackPosition );
+
+ // Tries to set the item to the specified backpack position. Passing in 0 will find the first empty position.
+ // FAILS if the backpack is full, or if that spot isn't clear. Returns false in that case.
+ virtual bool SetItemBackpackPosition( CEconItemView *pItem, uint32 iPosition = 0, bool bForceUnequip = false, bool bAllowOverflow = false );
+
+ // Sort the backpack items by the specified type
+ virtual void SortBackpackBy( uint32 iSortType );
+ void SortBackpackFinished( void );
+ bool IsInBackpackSort( void ) { return m_bInBackpackSort; }
+
+ void PredictedBackpackPosFilled( int iBackpackPos ) { m_PredictedFilledSlots.FindAndRemove( iBackpackPos ); }
+
+ // Tell the backend to move an item to a specified backend position
+ virtual void UpdateInventoryPosition( CPlayerInventory *pInventory, uint64 ulItemID, uint32 unNewInventoryPos );
+
+ virtual void UpdateInventoryEquippedState( CPlayerInventory *pInventory, uint64 ulItemID, equipped_class_t unClass, equipped_slot_t unSlot );
+
+
+ //-----------------------------------------------------------------------
+ // CLIENT PICKUP UI HANDLING
+ //-----------------------------------------------------------------------
+
+ // Get the number of items picked up
+ virtual int GetNumItemPickedUpItems( void ) { return 0; }
+
+ // Show the player a pickup screen with any items they've collected recently, if any
+ virtual bool ShowItemsPickedUp( bool bForce = false, bool bReturnToGame = true, bool bNoPanel = false );
+
+ // Show the player a pickup screen with the items they've crafted
+ virtual void ShowItemsCrafted( CUtlVector<itemid_t> *vecCraftedIndices ) { return; }
+
+ // Force the player to discard an item to make room for a new item, if they have one.
+ // Returns true if the discard panel has been brought up, and the player will be forced to discard an item.
+ virtual bool CheckForRoomAndForceDiscard( void );
+
+ //-----------------------------------------------------------------------
+ // CLIENT ITEM PICKUP ACKNOWLEDGEMENT FILES
+ //
+ // This system avoids showing multiple pickups for items that we've found, but haven't been
+ // able to move out of unack'd position due to the GC being unavailable. We keep a list of
+ // items we've ack'd in a client file, and don't re-show pickups for them. When a GC item
+ // update tells us the item has moved out of the unack'd position, we remove it from our file.
+ //-----------------------------------------------------------------------
+
+ virtual void AcknowledgeItem ( CEconItemView *pItem, bool bMoveToBackpack = true ); // Client Acknowledges an item and moves it in to the backpack
+ bool HasBeenAckedByClient( CEconItemView *pItem ); // Returns true if it's in our client file
+ void SetAckedByClient( CEconItemView *pItem ); // Adds it to our client file
+ void SetAckedByGC( CEconItemView *pItem, bool bSave ); // Removes it from our client file
+ KeyValues *GetAckKeyForItem( CEconItemView *pItem );
+ void CleanAckFile( void );
+ void SaveAckFile( void );
+
+private:
+ void VerifyAckFileLoaded( void );
+ KeyValues *m_pkvItemClientAckFile;
+ bool m_bClientAckDirty;
+
+private:
+ // As we move items around in batches (on pickups usually) we need to know what slots will be filled
+ // by items we've moved, and haven't received a response from Steam.
+ CUtlVector<int> m_PredictedFilledSlots;
+#endif
+
+public:
+ virtual int GetBackpackPositionFromBackend( uint32 iBackendPosition ) { return ExtractBackpackPositionFromBackend(iBackendPosition); }
+
+private:
+ //-----------------------------------------------------------------------
+ // Pending inventory requests
+ struct pendingreq_t
+ {
+ CPlayerInventory *pInventory;
+ CSteamID pID;
+ };
+ CUtlVector<pendingreq_t> m_hPendingInventoryRequests;
+ void RemovePendingRequest( CSteamID *pSteamID );
+
+protected:
+ //-----------------------------------------------------------------------
+ // Inventory registry
+ void DeregisterInventory( CPlayerInventory *pInventory );
+ struct inventories_t
+ {
+ CPlayerInventory *pInventory;
+ IInventoryUpdateListener *pListener;
+ };
+ CUtlVector<inventories_t> m_pInventories;
+
+ friend class CPlayerInventory;
+
+ inline bool IsValidPlayerClass( equipped_class_t unClass );
+
+#ifdef CLIENT_DLL
+ // Keep track of the number of items we've tried to discard, but haven't recieved responses on
+ int m_iPredictedDiscards;
+
+ typedef CUtlMap< uint32, CUtlString, int > tPersonaNamesByAccountID;
+ tPersonaNamesByAccountID m_mapPersonaNamesCache;
+
+ bool m_bInBackpackSort;
+
+ float m_flNextLoadPresetChange;
+
+ CMsgSetItemPositions m_msgPendingSetItemPositions;
+ CMsgLookupMultipleAccountNames m_msgPendingLookupAccountNames;
+
+ void OnPersonaStateChanged( PersonaStateChange_t *info );
+ CCallback< CInventoryManager, PersonaStateChange_t, false > m_sPersonaStateChangedCallback;
+ CUtlMap< uint64, bool > m_personaNameRequests;
+
+#endif
+};
+
+//=================================================================================
+// Implement these functions in your game code to create custom derived versions
+CInventoryManager *InventoryManager( void );
+
+CBasePlayer *GetPlayerBySteamID( const CSteamID &steamID );
+
+//-----------------------------------------------------------------------------
+// Purpose: Maintains a handle to an CEconItemView within an inventory. When
+// the inventory gets updated and shuffles CEconItemViews around, this
+// handle automatically updates its pointer to point to the new
+// CEconItemView that has the same item_id
+//-----------------------------------------------------------------------------
+class CEconItemViewHandle
+{
+public:
+ CEconItemViewHandle()
+ : m_pItem( NULL )
+ , m_pInv( NULL )
+ , m_bPointerDirty( false )
+ {}
+
+ CEconItemViewHandle( CEconItemView* pItem )
+ : m_pItem( pItem )
+ , m_pInv( NULL )
+ , m_bPointerDirty( false )
+ {
+ SetItem( pItem );
+ }
+
+ virtual ~CEconItemViewHandle()
+ {
+ // Unregister us
+ if ( m_pInv )
+ {
+ m_pInv->RemoveItemHandle( this );
+ }
+ }
+
+ void SetItem( CEconItemView* pItem );
+
+ operator CEconItemView *( void ) const
+ {
+ return Get();
+ }
+
+ CEconItemView* operator->( void ) const
+ {
+ return Get();
+ }
+
+ void ItemIsBeingDeleted( const CEconItemView* pItem )
+ {
+ m_bPointerDirty = true;
+
+ // Inventory told us the item is going away
+ if ( m_pItem == pItem )
+ {
+ m_pItem = NULL;
+ }
+ }
+
+ void InventoryIsBeingDeleted()
+ {
+ m_pInv = NULL;
+ m_pItem = NULL;
+ m_bPointerDirty = false; // So we dont keep trying to look up the item
+ }
+
+ void MarkDirty()
+ {
+ m_bPointerDirty = true;
+ }
+
+private:
+
+ CEconItemView* Get() const;
+
+ mutable bool m_bPointerDirty; // Used to mark when m_pItem is no longer valid
+ CPlayerInventory *m_pInv; // Inventory the item belongs to. Used to look up new CEconItemView
+ mutable CEconItemView* m_pItem; // The item.
+ uint64 m_nItemID; // ID of the item
+ CSteamID m_OwnerSteamID; // Steam ID of the item owner
+};
+
+
+#endif // ITEM_INVENTORY_H
diff --git a/game/shared/econ/econ_item_preset.cpp b/game/shared/econ/econ_item_preset.cpp
new file mode 100644
index 0000000..e778167
--- /dev/null
+++ b/game/shared/econ/econ_item_preset.cpp
@@ -0,0 +1,338 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+//===================================================================
+
+#include "cbase.h"
+#include "econ_item_preset.h"
+#include "tier1/generichash.h"
+
+#ifdef GC_DLL
+#include "gcsdk/sqlaccess/sqlaccess.h"
+#endif
+
+using namespace GCSDK;
+
+#ifdef GC_DLL
+IMPLEMENT_CLASS_MEMPOOL( CEconItemPerClassPresetData, 10 * 1000, UTLMEMORYPOOL_GROW_SLOW );
+#endif
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+CEconItemPerClassPresetData::CEconItemPerClassPresetData()
+ : m_unAccountID( 0 )
+ , m_unClassID( (equipped_class_t)-1 )
+ , m_unActivePreset( INVALID_PRESET_INDEX )
+{
+}
+
+CEconItemPerClassPresetData::CEconItemPerClassPresetData( uint32 unAccountID, equipped_class_t unClassID )
+ : m_unAccountID( unAccountID )
+ , m_unClassID( unClassID )
+ , m_unActivePreset( 0 )
+{
+}
+
+void CEconItemPerClassPresetData::SerializeToProtoBufItem( CSOClassPresetClientData& msgPresetData ) const
+{
+ msgPresetData.set_account_id( m_unAccountID );
+ msgPresetData.set_class_id( m_unClassID );
+ msgPresetData.set_active_preset_id( m_unActivePreset );
+}
+
+void CEconItemPerClassPresetData::DeserializeFromProtoBufItem( const CSOClassPresetClientData &msgPresetData )
+{
+ m_unAccountID = msgPresetData.account_id();
+ m_unClassID = msgPresetData.class_id();
+ m_unActivePreset = msgPresetData.active_preset_id();
+}
+
+bool CEconItemPerClassPresetData::BIsKeyLess( const CSharedObject& soRHS ) const
+{
+ const CEconItemPerClassPresetData *soPresetData = assert_cast< const CEconItemPerClassPresetData * >( &soRHS );
+
+ Assert( m_unAccountID == soPresetData->m_unAccountID );
+
+ return m_unClassID < soPresetData->m_unClassID;
+}
+
+#ifdef GC
+static bool BYieldingAddPresetItemRowsForSpecificPreset( GCSDK::CSQLAccess &sqlAccess, CSchItemPresetInstance& schItemPresetInstance, const CUtlVector<PresetSlotItem_t>& vecPresetData )
+{
+ FOR_EACH_VEC( vecPresetData, j )
+ {
+ schItemPresetInstance.m_unSlotID = vecPresetData[j].m_unSlotID;
+ schItemPresetInstance.m_ulItemID = vecPresetData[j].m_ulItemOriginalID;
+ if ( !sqlAccess.BYieldingInsertRecord( &schItemPresetInstance ) )
+ return false;
+ }
+
+ return true;
+}
+
+bool CEconItemPerClassPresetData::BYieldingAddInsertToTransaction( GCSDK::CSQLAccess &sqlAccess )
+{
+ // Write out the preset data for our selected items.
+ CSchItemPresetInstance schItemPresetInstance;
+ schItemPresetInstance.m_unAccountID = m_unAccountID;
+ schItemPresetInstance.m_unClassID = m_unClassID;
+
+ for ( int i = 0; i < ARRAYSIZE( m_PresetData ); i++ )
+ {
+ schItemPresetInstance.m_unPresetID = i;
+
+ if ( !BYieldingAddPresetItemRowsForSpecificPreset( sqlAccess, schItemPresetInstance, m_PresetData[i] ) )
+ return false;
+ }
+
+ // Write out the data for which preset is active for this class.
+ CSchSelectedItemPreset schSelectedItemPreset;
+ schSelectedItemPreset.m_unAccountID = m_unAccountID;
+ schSelectedItemPreset.m_unClassID = m_unClassID;
+ schSelectedItemPreset.m_unPresetID = m_unActivePreset;
+
+ if ( !sqlAccess.BYieldingInsertRecord( &schSelectedItemPreset ) )
+ return false;
+
+ return true;
+}
+
+bool CEconItemPerClassPresetData::BYieldingAddWriteToTransaction( GCSDK::CSQLAccess &sqlAccess, const CUtlVector< int > &fields )
+{
+ Assert( sqlAccess.BInTransaction() );
+
+ FOR_EACH_VEC( fields, i )
+ {
+ const int iField = fields[i];
+
+ if ( iField == kPerClassPresetDataDirtyField_ActivePreset )
+ {
+ CSchSelectedItemPreset schSelectedItemPreset;
+ schSelectedItemPreset.m_unAccountID = m_unAccountID;
+ schSelectedItemPreset.m_unClassID = m_unClassID;
+ schSelectedItemPreset.m_unPresetID = m_unActivePreset;
+
+ if ( !sqlAccess.BYieldingUpdateRecord( schSelectedItemPreset, CSET_2_COL( CSchSelectedItemPreset, k_iField_unAccountID, k_iField_unClassID ), CSET_1_COL( CSchSelectedItemPreset, k_iField_unPresetID ) ) )
+ return false;
+ }
+ else if ( iField >= kPerClassPresetDataDirtyField_PresetData_Base )
+ {
+ int iDirtyPreset = iField - kPerClassPresetDataDirtyField_PresetData_Base;
+ Assert( iDirtyPreset >= 0 );
+ Assert( iDirtyPreset < ARRAYSIZE( m_PresetData ) );
+
+ // First, remove any existing rows for this preset.
+ CSchItemPresetInstance schItemPresetInstance;
+ schItemPresetInstance.m_unAccountID = m_unAccountID;
+ schItemPresetInstance.m_unClassID = m_unClassID;
+ schItemPresetInstance.m_unPresetID = iDirtyPreset;
+
+ if ( !sqlAccess.BYieldingDeleteRecords( schItemPresetInstance, CSET_3_COL( CSchItemPresetInstance, k_iField_unAccountID, k_iField_unPresetID, k_iField_unClassID ) ) )
+ return false;
+
+ // Don't write out data for our currently-equipped items. We'll handle these by
+ // writing them out as actually equipped.
+ if ( iDirtyPreset != GetActivePreset() )
+ {
+ // Add our new rows.
+ if ( !BYieldingAddPresetItemRowsForSpecificPreset( sqlAccess, schItemPresetInstance, m_PresetData[iDirtyPreset] ) )
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+bool CEconItemPerClassPresetData::BYieldingAddRemoveToTransaction( GCSDK::CSQLAccess &sqlAccess )
+{
+ CSchItemPresetInstance schItemPresetInstance;
+ schItemPresetInstance.m_unAccountID = m_unAccountID;
+ schItemPresetInstance.m_unClassID = m_unClassID;
+
+ if ( !sqlAccess.BYieldingDeleteRecords( schItemPresetInstance, CSET_2_COL( CSchItemPresetInstance, k_iField_unAccountID, k_iField_unClassID ) ) )
+ return false;
+
+ CSchSelectedItemPreset schSelectedItemPreset;
+ schSelectedItemPreset.m_unAccountID = m_unAccountID;
+ schSelectedItemPreset.m_unClassID = m_unClassID;
+
+ if ( !sqlAccess.BYieldingDeleteRecords( schSelectedItemPreset, CSET_2_COL( CSchSelectedItemPreset, k_iField_unAccountID, k_iField_unClassID ) ) )
+ return false;
+
+ return true;
+}
+
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+bool CEconItemPerClassPresetData::BAddToMessage( CUtlBuffer & bufOutput ) const
+{
+ CSOClassPresetClientData msgClientPresetData;
+ SerializeToProtoBufItem( msgClientPresetData );
+ return CProtoBufSharedObjectBase::SerializeToBuffer( msgClientPresetData, bufOutput );
+}
+
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+bool CEconItemPerClassPresetData::BAddToMessage( std::string *pBuffer ) const
+{
+ CSOClassPresetClientData msgClientPresetData;
+ SerializeToProtoBufItem( msgClientPresetData );
+ return msgClientPresetData.SerializeToString( pBuffer );
+}
+
+//----------------------------------------------------------------------------
+// Purpose: Adds just the item ID to the message so that the client can find
+// which item to destroy
+//----------------------------------------------------------------------------
+bool CEconItemPerClassPresetData::BAddDestroyToMessage( CUtlBuffer & bufDestroy ) const
+{
+ CSOClassPresetClientData msgClientPresetData;
+ msgClientPresetData.set_class_id( m_unClassID );
+ return CProtoBufSharedObjectBase::SerializeToBuffer( msgClientPresetData, bufDestroy );
+}
+
+//----------------------------------------------------------------------------
+// Purpose: Adds just the item ID to the message so that the client can find
+// which item to destroy
+//----------------------------------------------------------------------------
+bool CEconItemPerClassPresetData::BAddDestroyToMessage( std::string *pBuffer ) const
+{
+ CSOClassPresetClientData msgClientPresetData;
+ msgClientPresetData.set_class_id( m_unClassID );
+ return msgClientPresetData.SerializeToString( pBuffer );
+}
+#endif
+
+bool CEconItemPerClassPresetData::BParseFromMessage( const CUtlBuffer & buffer )
+{
+ CSOClassPresetClientData msgClientPresetData;
+ if( !msgClientPresetData.ParseFromArray( buffer.Base(), buffer.TellMaxPut() ) )
+ return false;
+
+ DeserializeFromProtoBufItem( msgClientPresetData );
+
+ return true;
+}
+
+bool CEconItemPerClassPresetData::BParseFromMessage( const std::string &buffer )
+{
+ CSOClassPresetClientData msgClientPresetData;
+ if( !msgClientPresetData.ParseFromString( buffer ) )
+ return false;
+
+ DeserializeFromProtoBufItem( msgClientPresetData );
+
+ return true;
+}
+
+//----------------------------------------------------------------------------
+// Purpose:
+//----------------------------------------------------------------------------
+bool CEconItemPerClassPresetData::BUpdateFromNetwork( const CSharedObject & objUpdate )
+{
+ Copy( objUpdate );
+ return true;
+}
+
+void CEconItemPerClassPresetData::Copy( const CSharedObject & soRHS )
+{
+ const CEconItemPerClassPresetData& rhs = static_cast<const CEconItemPerClassPresetData&>( soRHS );
+
+ m_unAccountID = rhs.m_unAccountID;
+ m_unClassID = rhs.m_unClassID;
+ m_unActivePreset = rhs.m_unActivePreset;
+
+ for ( int i = 0; i < ARRAYSIZE( m_PresetData ); i++ )
+ {
+ m_PresetData[i].CopyArray( rhs.m_PresetData[i].Base(), rhs.m_PresetData[i].Count() );
+ }
+}
+
+void CEconItemPerClassPresetData::Dump() const
+{
+#if 0
+ EmitInfo( SPEW_GC, SPEW_ALWAYS, LOG_ALWAYS, "preset id=%d class id=%d slot id=%d item id=%llu\n",
+ m_unPresetID, m_unClassID, m_unSlotID, m_ulItemID );
+#endif
+}
+
+#ifdef GC_DLL
+//----------------------------------------------------------------------------
+// Purpose:
+//----------------------------------------------------------------------------
+const CUtlVector<PresetSlotItem_t> *CEconItemPerClassPresetData::FindItemsForPresetIndex( equipped_preset_t unPreset ) const
+{
+ if ( unPreset >= ARRAYSIZE( m_PresetData ) )
+ return NULL;
+
+ return &m_PresetData[ unPreset ];
+}
+
+//----------------------------------------------------------------------------
+// Purpose:
+//----------------------------------------------------------------------------
+void CEconItemPerClassPresetData::SetActivePreset( equipped_preset_t unPreset )
+{
+ if ( unPreset >= ARRAYSIZE( m_PresetData ) )
+ return;
+
+ m_unActivePreset = unPreset;
+}
+
+//----------------------------------------------------------------------------
+// Purpose:
+//----------------------------------------------------------------------------
+void CEconItemPerClassPresetData::EquipItemIntoActivePresetSlot( equipped_slot_t unSlot, itemid_t unOriginalItemID )
+{
+ Assert( GetItemSchema()->IsValidItemSlot( unSlot, EQUIP_TYPE_CLASS ) );
+ Assert( m_unActivePreset < ARRAYSIZE( m_PresetData ) );
+
+ auto& PresetData = m_PresetData[m_unActivePreset];
+
+ FOR_EACH_VEC( PresetData, i )
+ {
+ if ( PresetData[i].m_unSlotID == unSlot )
+ {
+ // If we're unequipping, stop tracking this slot.
+ if ( unOriginalItemID == INVALID_ITEM_ID )
+ {
+ PresetData.FastRemove( i );
+ }
+ // Otherwise store the current reference.
+ else
+ {
+ PresetData[i].m_ulItemOriginalID = unOriginalItemID;
+ }
+ return;
+ }
+ }
+
+ // We don't expect to get here without having an item equipped already, but it's possible if
+ // items get swapped around on the back end, or if we process messages out of order and/or drop
+ // some.
+ if ( unOriginalItemID != INVALID_ITEM_ID )
+ {
+ PresetSlotItem_t PresetSlotItem;
+ PresetSlotItem.m_unSlotID = unSlot;
+ PresetSlotItem.m_ulItemOriginalID = unOriginalItemID;
+ PresetData.AddToTail( PresetSlotItem );
+ }
+}
+
+//----------------------------------------------------------------------------
+// Purpose:
+//----------------------------------------------------------------------------
+void CEconItemPerClassPresetData::RemoveAllItemsFromPresetIndex( equipped_preset_t unPreset )
+{
+ if ( unPreset >= ARRAYSIZE( m_PresetData ) )
+ return;
+
+ m_PresetData[unPreset].Purge();
+}
+#endif // GC_DLL \ No newline at end of file
diff --git a/game/shared/econ/econ_item_preset.h b/game/shared/econ/econ_item_preset.h
new file mode 100644
index 0000000..0aa394a
--- /dev/null
+++ b/game/shared/econ/econ_item_preset.h
@@ -0,0 +1,102 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+//===================================================================
+
+#ifndef ECONITEMPRESET_H
+#define ECONITEMPRESET_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "gcsdk/protobufsharedobject.h"
+#include "gcsdk/gcclientsdk.h"
+#include "base_gcmessages.pb.h"
+
+#include "econ/econ_item_constants.h"
+
+namespace GCSDK
+{
+ class CSQLAccess;
+};
+
+class CSOClassPresetClientData;
+
+typedef uint8 equipped_preset_t;
+
+struct PresetSlotItem_t
+{
+#ifdef GC_DLL
+ DECLARE_CLASS_MEMPOOL( PresetSlotItem_t );
+#endif
+
+ equipped_slot_t m_unSlotID;
+ itemid_t m_ulItemOriginalID; // Original ID of the item in this slot. We store this instead of the current ID to avoid breaking presets when items get renamed, etc.
+};
+
+// --------------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------------
+class CEconItemPerClassPresetData : public GCSDK::CSharedObject
+{
+#ifdef GC_DLL
+ DECLARE_CLASS_MEMPOOL( CEconItemPerClassPresetData );
+#endif
+
+public:
+ typedef GCSDK::CSharedObject BaseClass;
+
+ const static int k_nTypeID = k_EEconTypeItemPresetInstance;
+ virtual int GetTypeID() const OVERRIDE { return k_nTypeID; }
+
+ CEconItemPerClassPresetData();
+ CEconItemPerClassPresetData( uint32 unAccountID, equipped_class_t unClassID );
+
+ virtual bool BIsKeyLess( const CSharedObject& soRHS ) const;
+
+#ifdef GC
+ virtual bool BYieldingAddInsertToTransaction( GCSDK::CSQLAccess &sqlAccess ) OVERRIDE;
+ virtual bool BYieldingAddWriteToTransaction( GCSDK::CSQLAccess &sqlAccess, const CUtlVector< int > &fields ) OVERRIDE;
+ virtual bool BYieldingAddRemoveToTransaction( GCSDK::CSQLAccess &sqlAccess ) OVERRIDE;
+ virtual bool BAddToMessage( CUtlBuffer & bufOutput ) const OVERRIDE;
+ virtual bool BAddToMessage( std::string *pBuffer ) const OVERRIDE;
+ virtual bool BAddDestroyToMessage( CUtlBuffer & bufDestroy ) const OVERRIDE;
+ virtual bool BAddDestroyToMessage( std::string *pBuffer ) const OVERRIDE;
+#endif
+
+ virtual bool BParseFromMessage( const CUtlBuffer & buffer ) OVERRIDE;
+ virtual bool BParseFromMessage( const std::string &buffer ) OVERRIDE;
+ virtual bool BUpdateFromNetwork( const CSharedObject & objUpdate ) OVERRIDE;
+ virtual void Copy( const CSharedObject & soRHS );
+ virtual void Dump() const;
+
+ void SerializeToProtoBufItem( CSOClassPresetClientData &msgPresetInstance ) const;
+ void DeserializeFromProtoBufItem( const CSOClassPresetClientData &msgPresetIntance );
+
+ enum
+ {
+ kPerClassPresetDataDirtyField_ActivePreset,
+ kPerClassPresetDataDirtyField_PresetData_Base,
+ };
+
+#ifdef GC_DLL
+ const CUtlVector<PresetSlotItem_t> *FindItemsForPresetIndex( equipped_preset_t unPreset ) const;
+ void EquipItemIntoActivePresetSlot( equipped_slot_t unSlot, itemid_t unOriginalItemID );
+ void RemoveAllItemsFromPresetIndex( equipped_preset_t unPreset );
+
+ void SetActivePreset( equipped_preset_t unPreset );
+ equipped_class_t GetClass() const { return m_unClassID; }
+#endif // GC_DLL
+ equipped_preset_t GetActivePreset() const { return m_unActivePreset; }
+
+private:
+ CEconItemPerClassPresetData( const CEconItemPerClassPresetData& ) = delete;
+ void operator=( const CEconItemPerClassPresetData& ) = delete;
+
+private:
+ uint32 m_unAccountID;
+ equipped_class_t m_unClassID;
+ equipped_preset_t m_unActivePreset;
+ CUtlVector<PresetSlotItem_t> m_PresetData[ CEconItemSchema::kMaxItemPresetCount ];
+};
+
+#endif // ECONITEMPRESET_H
diff --git a/game/shared/econ/econ_item_schema.cpp b/game/shared/econ/econ_item_schema.cpp
new file mode 100644
index 0000000..1093c5c
--- /dev/null
+++ b/game/shared/econ/econ_item_schema.cpp
@@ -0,0 +1,9705 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: EconItemSchema: Defines a schema for econ items
+//
+//=============================================================================
+
+#include "cbase.h"
+#include "econ_item_schema.h"
+#include "tier1/fmtstr.h"
+#include "tier1/UtlSortVector.h"
+#include "tier2/tier2.h"
+#include "filesystem.h"
+#include "schemainitutils.h"
+#include "gcsdk/gcsdk_auto.h"
+#include "rtime.h"
+#include "item_selection_criteria.h"
+#include "crypto.h"
+#include "checksum_sha1.h"
+
+#include <google/protobuf/text_format.h>
+#include <string.h>
+
+#include "materialsystem/imaterialsystem.h"
+#include "materialsystem/itexture.h"
+#include "materialsystem/itexturecompositor.h"
+
+#if ( defined( _MSC_VER ) && _MSC_VER >= 1900 )
+#define timezone _timezone
+#define daylight _daylight
+#endif
+
+// For holiday-limited loot lists.
+#include "econ_holidays.h"
+
+// Only used for startup testing.
+#include "econ_item_tools.h"
+
+#if defined(CLIENT_DLL) || defined(GAME_DLL)
+ #include "econ_item_system.h"
+ #include "econ_item.h"
+ #include "activitylist.h"
+
+ #if defined(TF_CLIENT_DLL) || defined(TF_DLL)
+ #include "tf_gcmessages.h"
+ #endif
+#endif
+
+#ifdef GC_DLL
+#include "gcgamebase.h"
+#include <memory> // unique_ptr
+#endif
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+
+using namespace GCSDK;
+
+
+CEconItemSchema & GEconItemSchema()
+{
+#if defined( EXTERNALTESTS_DLL )
+ static CEconItemSchema g_econItemSchema;
+ return g_econItemSchema;
+#elif defined( GC_DLL )
+ return *GEconManager()->GetItemSchema();
+#else
+ return *ItemSystem()->GetItemSchema();
+#endif
+}
+
+const char *g_szDropTypeStrings[] =
+{
+ "", // Blank and none mean the same thing: stay attached to the body.
+ "none",
+ "drop", // The item drops off the body.
+ "break", // Not implemented, but an example of a type that could be added.
+};
+
+const char *g_TeamVisualSections[TEAM_VISUAL_SECTIONS] =
+{
+ "visuals", // TF_TEAM_UNASSIGNED. Visual changes applied to both teams.
+ NULL, // TF_TEAM_SPECTATOR. Unused.
+ "visuals_red", // TF_TEAM_RED
+ "visuals_blu", // TF_TEAM_BLUE
+ "visuals_mvm_boss", // Hack to override things in MvM at a general level
+};
+
+int GetTeamVisualsFromString( const char *pszString )
+{
+ for ( int i = 0; i < TEAM_VISUAL_SECTIONS; i++ )
+ {
+ // There's a NULL hidden in g_TeamVisualSections
+ if ( g_TeamVisualSections[i] && !Q_stricmp( pszString, g_TeamVisualSections[i] ) )
+ return i;
+ }
+ return -1;
+}
+
+#if defined(CLIENT_DLL) || defined(GAME_DLL)
+// Used to convert strings to ints for wearable animation types
+const char *g_WearableAnimTypeStrings[ NUM_WAP_TYPES ] =
+{
+ "on_spawn", // WAP_ON_SPAWN,
+ "start_building", // WAP_START_BUILDING,
+ "stop_building", // WAP_STOP_BUILDING,
+ "start_taunting", // WAP_START_TAUNTING,
+ "stop_taunting", // WAP_STOP_TAUNTING,
+};
+#endif
+
+const char *g_AttributeDescriptionFormats[] =
+{
+ "value_is_percentage", // ATTDESCFORM_VALUE_IS_PERCENTAGE,
+ "value_is_inverted_percentage", // ATTDESCFORM_VALUE_IS_INVERTED_PERCENTAGE
+ "value_is_additive", // ATTDESCFORM_VALUE_IS_ADDITIVE
+ "value_is_additive_percentage", // ATTDESCFORM_VALUE_IS_ADDITIVE_PERCENTAGE
+ "value_is_or", // ATTDESCFORM_VALUE_IS_OR
+ "value_is_date", // ATTDESCFORM_VALUE_IS_DATE
+ "value_is_account_id", // ATTDESCFORM_VALUE_IS_ACCOUNT_ID
+ "value_is_particle_index", // ATTDESCFORM_VALUE_IS_PARTICLE_INDEX -> Could change to "string index"
+ "value_is_killstreakeffect_index", // ATTDESCFORM_VALUE_IS_KILLSTREAKEFFECT_INDEX -> Could change to "string index"
+ "value_is_killstreak_idleeffect_index", // ATTDESCFORM_VALUE_IS_KILLSTREAK_IDLEEFFECT_INDEX
+ "value_is_item_def", // ATTDESCFORM_VALUE_IS_ITEM_DEF
+ "value_is_from_lookup_table", // ATTDESCFORM_VALUE_IS_FROM_LOOKUP_TABLE
+};
+
+const char *g_EffectTypes[NUM_EFFECT_TYPES] =
+{
+ "unusual", // ATTRIB_EFFECT_UNUSUAL,
+ "strange", // ATTRIB_EFFECT_STRANGE,
+ "neutral", // ATTRIB_EFFECT_NEUTRAL = 0,
+ "positive", // ATTRIB_EFFECT_POSITIVE,
+ "negative", // ATTRIB_EFFECT_NEGATIVE,
+};
+
+//-----------------------------------------------------------------------------
+// Purpose: Set the capabilities bitfield based on whether the entry is true/false.
+//-----------------------------------------------------------------------------
+const char *g_Capabilities[] =
+{
+ "paintable", // ITEM_CAP_PAINTABLE
+ "nameable", // ITEM_CAP_NAMEABLE
+ "decodable", // ITEM_CAP_DECODABLE
+ "can_craft_if_purchased", // ITEM_CAP_CAN_BE_CRAFTED_IF_PURCHASED
+ "can_customize_texture", // ITEM_CAP_CAN_CUSTOMIZE_TEXTURE
+ "usable", // ITEM_CAP_USABLE
+ "usable_gc", // ITEM_CAP_USABLE_GC
+ "can_gift_wrap", // ITEM_CAP_CAN_GIFT_WRAP
+ "usable_out_of_game", // ITEM_CAP_USABLE_OUT_OF_GAME
+ "can_collect", // ITEM_CAP_CAN_COLLECT
+ "can_craft_count", // ITEM_CAP_CAN_CRAFT_COUNT
+ "can_craft_mark", // ITEM_CAP_CAN_CRAFT_MARK
+ "paintable_team_colors", // ITEM_CAP_PAINTABLE_TEAM_COLORS
+ "can_be_restored", // ITEM_CAP_CAN_BE_RESTORED
+ "strange_parts", // ITEM_CAP_CAN_USE_STRANGE_PARTS
+ "can_card_upgrade", // ITEM_CAP_CAN_CARD_UPGRADE
+ "can_strangify", // ITEM_CAP_CAN_STRANGIFY
+ "can_killstreakify", // ITEM_CAP_CAN_KILLSTREAKIFY
+ "can_consume", // ITEM_CAP_CAN_CONSUME_ITEMS
+ "can_spell_page", // ITEM_CAP_CAN_SPELLBOOK_PAGE
+ "has_slots", // ITEM_CAP_HAS_SLOTS
+ "duck_upgradable", // ITEM_CAP_DUCK_UPGRADABLE
+ "can_unusualify", // ITEM_CAP_CAN_UNUSUALIFY
+};
+COMPILE_TIME_ASSERT( ARRAYSIZE(g_Capabilities) == NUM_ITEM_CAPS );
+
+#define RETURN_ATTRIBUTE_STRING( attrib_name, default_string ) \
+ static CSchemaAttributeDefHandle pAttribString( attrib_name ); \
+ const char *pchResultAttribString = default_string; \
+ FindAttribute_UnsafeBitwiseCast< CAttribute_String >( this, pAttribString, &pchResultAttribString ); \
+ return pchResultAttribString;
+
+#define RETURN_ATTRIBUTE_STRING_F( func_name, attrib_name, default_string ) \
+ const char *func_name( void ) const { RETURN_ATTRIBUTE_STRING( attrib_name, default_string ) }
+
+static void ParseCapability( item_capabilities_t &capsBitfield, KeyValues* pEntry )
+{
+ int idx = StringFieldToInt( pEntry->GetName(), g_Capabilities, ARRAYSIZE(g_Capabilities) );
+ if ( idx < 0 )
+ {
+ return;
+ }
+ int bit = 1 << idx;
+ if ( pEntry->GetBool() )
+ {
+ (int&)capsBitfield |= bit;
+ }
+ else
+ {
+ (int&)capsBitfield &= ~bit;
+ }
+}
+
+#ifdef GC_DLL
+static bool BGetPaymentRule( KeyValues *pKVRule, EPaymentRuleType *out_pePaymentRuleType, double *out_pRevenueShare )
+{
+ Assert( out_pePaymentRuleType );
+ Assert( out_pRevenueShare );
+
+ struct payment_rule_lookup_t
+ {
+ const char *m_pszStr;
+ EPaymentRuleType m_eRuleType;
+ };
+
+ static payment_rule_lookup_t s_Lookup[] =
+ {
+ { "workshop_revenue_share", kPaymentRule_SteamWorkshopFileID },
+ { "partner_revenue_share", kPaymentRule_PartnerSteamID },
+ { "bundle_revenue_share", kPaymentRule_Bundle },
+ };
+
+ for ( int i = 0; i < ARRAYSIZE( s_Lookup ); i++ )
+ {
+ KeyValues *pKVKey = pKVRule->FindKey( s_Lookup[i].m_pszStr );
+ if ( !pKVKey )
+ continue;
+
+ *out_pePaymentRuleType = s_Lookup[i].m_eRuleType;
+ *out_pRevenueShare = atof( pKVKey->GetString() ) * 100.0; // KeyValues doesn't support parsing a string as a double-precision value, so we do it by hand
+ return true;
+ }
+
+ return false;
+}
+#endif // GC_DLL
+
+//-----------------------------------------------------------------------------
+// Purpose: CEconItemSeriesDefinition
+//-----------------------------------------------------------------------------
+CEconItemSeriesDefinition::CEconItemSeriesDefinition( void )
+ : m_nValue( INT_MAX )
+{
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Copy constructor
+//-----------------------------------------------------------------------------
+CEconItemSeriesDefinition::CEconItemSeriesDefinition( const CEconItemSeriesDefinition &that )
+{
+ ( *this ) = that;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Operator=
+//-----------------------------------------------------------------------------
+CEconItemSeriesDefinition &CEconItemSeriesDefinition::operator=( const CEconItemSeriesDefinition &rhs )
+{
+ m_nValue = rhs.m_nValue;
+ m_strName = rhs.m_strName;
+
+ return *this;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Initialize the quality definition
+// Input: pKVQuality - The KeyValues representation of the quality
+// schema - The overall item schema for this attribute
+// pVecErrors - An optional vector that will contain error messages if
+// the init fails.
+// Output: True if initialization succeeded, false otherwise
+//-----------------------------------------------------------------------------
+bool CEconItemSeriesDefinition::BInitFromKV( KeyValues *pKVSeries, CUtlVector<CUtlString> *pVecErrors /* = NULL */ )
+{
+
+ m_nValue = pKVSeries->GetInt( "value", -1 );
+ m_strName = pKVSeries->GetName();
+
+ m_strLockKey = pKVSeries->GetString( "loc_key" );
+ m_strUiFile = pKVSeries->GetString( "ui" );
+
+ // Check for required fields
+ SCHEMA_INIT_CHECK(
+ NULL != pKVSeries->FindKey( "value" ),
+ "Quality definition %s: Missing required field \"value\"", pKVSeries->GetName() );
+
+ return SCHEMA_INIT_SUCCESS();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor
+//-----------------------------------------------------------------------------
+CEconItemQualityDefinition::CEconItemQualityDefinition( void )
+: m_nValue( INT_MAX )
+, m_bCanSupportSet( false )
+{
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Copy constructor
+//-----------------------------------------------------------------------------
+CEconItemQualityDefinition::CEconItemQualityDefinition( const CEconItemQualityDefinition &that )
+{
+ (*this) = that;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Operator=
+//-----------------------------------------------------------------------------
+CEconItemQualityDefinition &CEconItemQualityDefinition::operator=( const CEconItemQualityDefinition &rhs )
+{
+ m_nValue = rhs.m_nValue;
+ m_strName = rhs.m_strName;
+ m_bCanSupportSet = rhs.m_bCanSupportSet;
+
+ return *this;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Initialize the quality definition
+// Input: pKVQuality - The KeyValues representation of the quality
+// schema - The overall item schema for this attribute
+// pVecErrors - An optional vector that will contain error messages if
+// the init fails.
+// Output: True if initialization succeeded, false otherwise
+//-----------------------------------------------------------------------------
+bool CEconItemQualityDefinition::BInitFromKV( KeyValues *pKVQuality, CUtlVector<CUtlString> *pVecErrors /* = NULL */ )
+{
+
+ m_nValue = pKVQuality->GetInt( "value", -1 );
+ m_strName = pKVQuality->GetName();
+ m_bCanSupportSet = pKVQuality->GetBool( "canSupportSet" );
+#ifdef GC_DLL
+ m_strHexColor = pKVQuality->GetString( "hexColor" );
+#endif // GC_DLL
+
+ // Check for required fields
+ SCHEMA_INIT_CHECK(
+ NULL != pKVQuality->FindKey( "value" ),
+ "Quality definition %s: Missing required field \"value\"", pKVQuality->GetName() );
+
+#if defined(CLIENT_DLL) || defined(GAME_DLL)
+ return SCHEMA_INIT_SUCCESS();
+#endif // GC_DLL
+
+ // Check for data consistency
+ SCHEMA_INIT_CHECK(
+ 0 != Q_stricmp( GetName(), "any" ),
+ "Quality definition any: The quality name \"any\" is a reserved keyword and cannot be used." );
+
+ SCHEMA_INIT_CHECK(
+ m_nValue != k_unItemQuality_Any,
+ "Quality definition %s: Invalid value (%d). It is reserved for Any", GetName(), k_unItemQuality_Any );
+
+ return SCHEMA_INIT_SUCCESS();
+}
+
+//-----------------------------------------------------------------------------
+// CEconItemRarityDefinition
+//-----------------------------------------------------------------------------
+CEconItemRarityDefinition::CEconItemRarityDefinition( void )
+ : m_nValue( INT_MAX )
+ , m_nLootlistWeight( 0 )
+{
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Initialize the rarity definition
+//-----------------------------------------------------------------------------
+bool CEconItemRarityDefinition::BInitFromKV( KeyValues *pKVRarity, KeyValues *pKVRarityWeights, CEconItemSchema &pschema, CUtlVector<CUtlString> *pVecErrors /* = NULL */ )
+{
+ m_nValue = pKVRarity->GetInt( "value", -1 );
+ m_strName = pKVRarity->GetName();
+ m_strLocKey = pKVRarity->GetString( "loc_key" );
+ m_strWepLocKey = pKVRarity->GetString( "loc_key_weapon" );
+
+ m_iAttribColor = GetAttribColorIndexForName( pKVRarity->GetString( "color" ) );
+ m_strDropSound = pKVRarity->GetString( "drop_sound" );
+ m_strNextRarity = pKVRarity->GetString( "next_rarity" ); // Not required.
+
+#ifdef GC_DLL
+ if ( pKVRarityWeights )
+ {
+ m_nLootlistWeight = pKVRarityWeights->GetInt( m_strName, 0 );
+ }
+#endif
+ //
+
+ // Check for required fields
+ SCHEMA_INIT_CHECK(
+ NULL != pKVRarity->FindKey( "value" ),
+ "Rarity definition %s: Missing required field \"value\"", pKVRarity->GetName() );
+
+ SCHEMA_INIT_CHECK(
+ NULL != pKVRarity->FindKey( "loc_key" ),
+ "Rarity definition %s: Missing required field \"loc_key\"", pKVRarity->GetName() );
+
+ return SCHEMA_INIT_SUCCESS();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CEconColorDefinition::BInitFromKV( KeyValues *pKVColor, CUtlVector<CUtlString> *pVecErrors /* = NULL */ )
+{
+ m_strName = pKVColor->GetName();
+ m_strColorName = pKVColor->GetString( "color_name" );
+#ifdef GC_DLL
+ m_strHexColor = pKVColor->GetString( "hex_color" );
+#endif // GC_DLL
+
+ SCHEMA_INIT_CHECK(
+ !m_strColorName.IsEmpty(),
+ "Quality definition %s: missing \"color_name\"", GetName() );
+
+#ifdef GC_DLL
+ SCHEMA_INIT_CHECK(
+ !m_strHexColor.IsEmpty(),
+ "Quality definition %s: missing \"hex_color\"", GetName() );
+#endif // GC_DLL
+
+ return SCHEMA_INIT_SUCCESS();
+}
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+CEconItemSetDefinition::CEconItemSetDefinition( void )
+ : m_pszName( NULL )
+ , m_pszLocalizedName( NULL )
+ , m_iBundleItemDef( INVALID_ITEM_DEF_INDEX )
+ , m_bIsHiddenSet( false )
+{
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Copy constructor
+//-----------------------------------------------------------------------------
+CEconItemSetDefinition::CEconItemSetDefinition( const CEconItemSetDefinition &that )
+{
+ (*this) = that;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Operator=
+//-----------------------------------------------------------------------------
+CEconItemSetDefinition &CEconItemSetDefinition::operator=( const CEconItemSetDefinition &other )
+{
+ m_pszName = other.m_pszName;
+ m_pszLocalizedName = other.m_pszLocalizedName;
+ m_iItemDefs = other.m_iItemDefs;
+ m_iAttributes = other.m_iAttributes;
+ m_iBundleItemDef = other.m_iBundleItemDef;
+ m_bIsHiddenSet = other.m_bIsHiddenSet;
+
+ return *this;
+}
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+bool CEconItemSetDefinition::BInitFromKV( KeyValues *pKVItemSet, CUtlVector<CUtlString> *pVecErrors )
+{
+ m_pszName = pKVItemSet->GetName();
+
+ m_iBundleItemDef = INVALID_ITEM_DEF_INDEX;
+ const char *pszBundleName = pKVItemSet->GetString( "store_bundle" );
+ if ( pszBundleName && pszBundleName[0] )
+ {
+ CEconItemDefinition *pDef = GetItemSchema()->GetItemDefinitionByName( pszBundleName );
+ if ( pDef )
+ {
+ m_iBundleItemDef = pDef->GetDefinitionIndex();
+ }
+
+ SCHEMA_INIT_CHECK(
+ pDef != NULL,
+ "Item set %s: Bundle definition \"%s\" was not found", m_pszName, pszBundleName );
+ }
+
+ m_pszLocalizedName = pKVItemSet->GetString( "name", NULL );
+ m_bIsHiddenSet = pKVItemSet->GetBool( "is_hidden_set", false );
+
+ KeyValues *pKVItems = pKVItemSet->FindKey( "items" );
+ if ( pKVItems )
+ {
+ FOR_EACH_SUBKEY( pKVItems, pKVItem )
+ {
+ const char *pszName = pKVItem->GetName();
+
+ CEconItemDefinition *pDef = GetItemSchema()->GetItemDefinitionByName( pszName );
+
+ SCHEMA_INIT_CHECK(
+ pDef != NULL,
+ "Item set %s: Item definition \"%s\" was not found", m_pszName, pszName );
+
+ const item_definition_index_t unDefIndex = pDef->GetDefinitionIndex();
+
+ SCHEMA_INIT_CHECK(
+ !m_iItemDefs.IsValidIndex( m_iItemDefs.Find( unDefIndex ) ),
+ "Item set %s: item definition \"%s\" appears multiple times", m_pszName, pszName );
+ SCHEMA_INIT_CHECK(
+ !pDef->GetItemSetDefinition(),
+ "Item set %s: item definition \"%s\" specified in multiple item sets", m_pszName, pszName );
+
+ m_iItemDefs.AddToTail( unDefIndex );
+ pDef->SetItemSetDefinition( this );
+
+ // FIXME: hack to work around crafting item criteria
+ pDef->GetRawDefinition()->SetString( "item_set", m_pszName );
+ }
+ }
+
+ KeyValues *pKVAttributes = pKVItemSet->FindKey( "attributes" );
+ if ( pKVAttributes )
+ {
+ FOR_EACH_SUBKEY( pKVAttributes, pKVAttribute )
+ {
+ const char *pszName = pKVAttribute->GetName();
+
+ const CEconItemAttributeDefinition *pAttrDef = GetItemSchema()->GetAttributeDefinitionByName( pszName );
+ SCHEMA_INIT_CHECK(
+ pAttrDef != NULL,
+ "Item set %s: Attribute definition \"%s\" was not found", m_pszName, pszName );
+ SCHEMA_INIT_CHECK(
+ pAttrDef->BIsSetBonusAttribute(),
+ "Item set %s: Attribute definition \"%s\" is not a set bonus attribute", m_pszName, pszName );
+
+ int iIndex = m_iAttributes.AddToTail();
+ m_iAttributes[iIndex].m_iAttribDefIndex = pAttrDef->GetDefinitionIndex();
+ m_iAttributes[iIndex].m_flValue = pKVAttribute->GetFloat( "value" );
+ }
+ }
+
+ // Sanity check.
+ SCHEMA_INIT_CHECK( m_pszLocalizedName != NULL,
+ "Item set %s: Set contains no localized name", m_pszName );
+ SCHEMA_INIT_CHECK( m_iItemDefs.Count() > 0,
+ "Item set %s: Set contains no items", m_pszName );
+
+ return SCHEMA_INIT_SUCCESS();
+}
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+void CEconItemSetDefinition::IterateAttributes( class IEconItemAttributeIterator *pIterator ) const
+{
+ FOR_EACH_VEC( m_iAttributes, i )
+ {
+ const itemset_attrib_t& itemsetAttrib = m_iAttributes[i];
+
+ const CEconItemAttributeDefinition *pAttrDef = GetItemSchema()->GetAttributeDefinition( itemsetAttrib.m_iAttribDefIndex );
+ if ( !pAttrDef )
+ continue;
+
+ const ISchemaAttributeType *pAttrType = pAttrDef->GetAttributeType();
+ Assert( pAttrType );
+
+ // We know (and assert) that we only need 32 bits of data to store this attribute
+ // data. We don't know anything about the type but we'll let the type handle it
+ // below.
+ attribute_data_union_t value;
+ value.asFloat = itemsetAttrib.m_flValue;
+
+ if ( !pAttrType->OnIterateAttributeValue( pIterator, pAttrDef, value ) )
+ return;
+ }
+}
+
+//-----------------------------------------------------------------------------
+CEconItemCollectionDefinition::CEconItemCollectionDefinition( void )
+ : m_pszName( NULL )
+ , m_pszLocalizedName( NULL )
+ , m_pszLocalizedDesc( NULL )
+ , m_iRarityMin( k_unItemRarity_Any )
+ , m_iRarityMax( k_unItemRarity_Any )
+{
+}
+
+//-----------------------------------------------------------------------------
+//
+
+//-----------------------------------------------------------------------------
+static int SortCollectionByRarity( item_definition_index_t const *a, item_definition_index_t const *b )
+{
+ Assert( a );
+ Assert( *a );
+ Assert( b );
+ Assert( *b );
+
+ CEconItemDefinition *pItemA = GetItemSchema()->GetItemDefinition( *a );
+ CEconItemDefinition *pItemB = GetItemSchema()->GetItemDefinition( *b );
+
+ if ( !pItemA || !pItemB )
+ {
+ AssertMsg( 0, "ItemDef Doesn't exist for sorting" );
+ return 1;
+ }
+
+ // If same Rarity, leave in current position?
+ if ( pItemA->GetRarity() == pItemB->GetRarity() && pItemA->GetCustomPainkKitDefinition() && pItemB->GetCustomPainkKitDefinition() )
+ {
+#ifdef CLIENT_DLL
+ // Sort by localized name
+ // paintkits sort by paintkit name
+ auto paintkitA = pItemA->GetCustomPainkKitDefinition();
+ auto paintkitB = pItemB->GetCustomPainkKitDefinition();
+ auto paintkitALocName = paintkitA->GetLocalizeName();
+ auto paintkitBLocName = paintkitB->GetLocalizeName();
+ auto pkALocalized = g_pVGuiLocalize->Find( paintkitALocName );
+ auto pkBLocalized = g_pVGuiLocalize->Find( paintkitBLocName );
+ if ( pkALocalized )
+ {
+ if ( pkBLocalized )
+ {
+ return V_wcscmp( pkALocalized, pkBLocalized );
+ }
+ else
+ {
+ return -1;
+ }
+ }
+ else
+ {
+ return pkBLocalized ? 1 : -1;
+ }
+#else
+ return 0;
+#endif
+ }
+
+ return ( pItemA->GetRarity() > pItemB->GetRarity() ) ? -1 : 1;
+}
+
+
+//-----------------------------------------------------------------------------
+bool CEconItemCollectionDefinition::BInitFromKV( KeyValues *pKVPItemCollection, CUtlVector<CUtlString> *pVecErrors )
+{
+ m_pszName = pKVPItemCollection->GetName();
+
+ m_pszLocalizedName = pKVPItemCollection->GetString( "name", NULL );
+ m_pszLocalizedDesc = pKVPItemCollection->GetString( "description", NULL );
+
+ m_bIsReferenceCollection = pKVPItemCollection->GetBool( "is_reference_collection", false );
+
+ KeyValues *pKVItems = pKVPItemCollection->FindKey( "items" );
+
+ // Create a 'lootlist' from this collection
+ KeyValues *pCollectionLootList = NULL;
+ bool bIsLootList = false;
+ if ( !m_bIsReferenceCollection )
+ {
+ pCollectionLootList = new KeyValues( m_pszName );
+ }
+
+ if ( pKVItems )
+ {
+ // Traverse rarity items and set rarity
+ // Create a lootlist if applicable
+ FOR_EACH_TRUE_SUBKEY( pKVItems, pKVRarity )
+ {
+ bIsLootList = true;
+ // Get the Rarity Value
+ const CEconItemRarityDefinition *pRarity = GetItemSchema()->GetRarityDefinitionByName( pKVRarity->GetName() );
+ SCHEMA_INIT_CHECK( pRarity != NULL, "Item collection %s: Rarity type \"%s\" was not found", m_pszName, pKVRarity->GetName() );
+
+ // Create a lootlist
+ if ( !m_bIsReferenceCollection )
+ {
+ CFmtStr lootlistname( "%s_%s", m_pszName, pRarity->GetName() );
+ const char *pszName = V_strdup( lootlistname.Get() );
+ pKVRarity->SetInt( "rarity", pRarity->GetDBValue() );
+ SCHEMA_INIT_CHECK( GetItemSchema()->BInsertLootlist( pszName, pKVRarity, pVecErrors ), "Invalid collection lootlist %s", pszName );
+ KeyValues *pTempRarityKey = pKVRarity->FindKey( "rarity" );
+ Assert( pTempRarityKey );
+ pKVRarity->RemoveSubKey( pTempRarityKey );
+ pTempRarityKey->deleteThis();
+ pCollectionLootList->SetInt( pszName, pRarity->GetLootlistWeight() );
+ }
+
+ // Items in the Rarity
+ FOR_EACH_VALUE( pKVRarity, pKVItem )
+ {
+ const char *pszName = pKVItem->GetName();
+
+ CEconItemDefinition *pDef = GetItemSchema()->GetItemDefinitionByName( pszName );
+
+ SCHEMA_INIT_CHECK(
+ pDef != NULL,
+ "Item set %s: Item definition \"%s\" was not found", m_pszName, pszName );
+
+ const item_definition_index_t unDefIndex = pDef->GetDefinitionIndex();
+
+ SCHEMA_INIT_CHECK(
+ !m_iItemDefs.IsValidIndex( m_iItemDefs.Find( unDefIndex ) ),
+ "Item Collection %s: item definition \"%s\" appears multiple times", m_pszName, pszName );
+
+ m_iItemDefs.AddToTail( unDefIndex );
+
+ // Collection Reference
+ if ( !m_bIsReferenceCollection )
+ {
+ SCHEMA_INIT_CHECK(
+ !pDef->GetItemCollectionDefinition(),
+ "Item Collection %s: item definition \"%s\" specified in multiple item sets", m_pszName, pszName );
+ pDef->SetItemCollectionDefinition( this );
+ }
+
+ // Item Rarity
+ pDef->SetRarity( pRarity->GetDBValue() );
+ }
+ }
+
+ // Loose Items
+ FOR_EACH_VALUE( pKVItems, pKVItem )
+ {
+ const char *pszName = pKVItem->GetName();
+
+ CEconItemDefinition *pDef = GetItemSchema()->GetItemDefinitionByName( pszName );
+
+ SCHEMA_INIT_CHECK(
+ pDef != NULL,
+ "Item set %s: Item definition \"%s\" was not found", m_pszName, pszName );
+
+ const item_definition_index_t unDefIndex = pDef->GetDefinitionIndex();
+
+ SCHEMA_INIT_CHECK(
+ !m_iItemDefs.IsValidIndex( m_iItemDefs.Find( unDefIndex ) ),
+ "Item Collection %s: item definition \"%s\" appears multiple times", m_pszName, pszName );
+
+ m_iItemDefs.AddToTail( unDefIndex );
+
+ if ( !m_bIsReferenceCollection )
+ {
+ SCHEMA_INIT_CHECK(
+ !pDef->GetItemCollectionDefinition(),
+ "Item Collection %s: item definition \"%s\" specified in multiple item sets", m_pszName, pszName );
+ pDef->SetItemCollectionDefinition( this );
+ }
+ }
+
+ // Sort by Rarity
+ m_iItemDefs.Sort( &SortCollectionByRarity );
+ }
+
+ if ( !m_bIsReferenceCollection && bIsLootList )
+ {
+ // Insert collection lootlist
+ GetItemSchema()->BInsertLootlist( m_pszName, pCollectionLootList, pVecErrors );
+ }
+
+ if ( pCollectionLootList )
+ {
+ pCollectionLootList->deleteThis();
+ }
+
+ // Sorted high to low
+ m_iRarityMax = GetItemSchema()->GetItemDefinition( m_iItemDefs[ 0 ] )->GetRarity();
+ m_iRarityMin = GetItemSchema()->GetItemDefinition( m_iItemDefs[ m_iItemDefs.Count() - 1] )->GetRarity();
+ // Verify that there is no gaps in the Rarity (would cause crafting problems and makes no sense)
+
+ if ( !m_bIsReferenceCollection )
+ {
+ int iRarityVerify = m_iRarityMax;
+ FOR_EACH_VEC( m_iItemDefs, i )
+ {
+ int iNextRarity = GetItemSchema()->GetItemDefinition( m_iItemDefs[i] )->GetRarity();
+ SCHEMA_INIT_CHECK( iRarityVerify - iNextRarity <= 1, "Items in Collection %s: Have a gap in rarity tiers", m_pszName );
+ iRarityVerify = iNextRarity;
+ }
+ }
+
+ // Sanity check.
+ SCHEMA_INIT_CHECK( m_pszLocalizedName != NULL,
+ "Item Collection %s: Collection contains no localized name", m_pszName );
+ SCHEMA_INIT_CHECK( m_pszLocalizedDesc != NULL,
+ "Item Collection %s: Collection contains no localized description", m_pszName );
+ SCHEMA_INIT_CHECK( m_iItemDefs.Count() > 0,
+ "Item Collection %s: Collection contains no items", m_pszName );
+
+ return SCHEMA_INIT_SUCCESS();
+}
+
+//-----------------------------------------------------------------------------
+// CEconItemPaintKitDefinition
+//-----------------------------------------------------------------------------
+CEconItemPaintKitDefinition::CEconItemPaintKitDefinition( void )
+ : m_pszName( NULL )
+{
+}
+
+//-----------------------------------------------------------------------------
+CEconItemPaintKitDefinition::~CEconItemPaintKitDefinition( void )
+{
+ FOR_EACH_VEC( m_vecPaintKitWearKVP, i )
+ {
+ if ( m_vecPaintKitWearKVP[i] )
+ {
+ m_vecPaintKitWearKVP[i]->deleteThis();
+ }
+ }
+
+ m_vecPaintKitWearKVP.Purge();
+}
+
+bool VerifyPaintKitComposite( KeyValues *pKVWearInputItems, const char* pName, int iWearLevel, CUtlVector<CUtlString> *pVecErrors )
+{
+ SCHEMA_INIT_CHECK( pKVWearInputItems != NULL, "Paint Kit %s: Does not contain Wear Level %d", pName, iWearLevel );
+#ifdef CLIENT_DLL
+ int w = 1;
+ int h = 1;
+ int seed = 0;
+ ITextureCompositor* pWeaponSkinBaseCompositor = NULL;
+
+ SafeAssign( &pWeaponSkinBaseCompositor, materials->NewTextureCompositor( w, h, pName, TF_TEAM_RED, seed, pKVWearInputItems, TEX_COMPOSITE_CREATE_FLAGS_VERIFY_SCHEMA_ONLY ) );
+ SCHEMA_INIT_CHECK( pWeaponSkinBaseCompositor != NULL, "Could Not Create Weapon Skin Compositor for [%s][Wear %d][Team Red]", pName, iWearLevel);
+ SafeRelease( &pWeaponSkinBaseCompositor );
+
+ SafeAssign( &pWeaponSkinBaseCompositor, materials->NewTextureCompositor( w, h, pName, TF_TEAM_BLUE, seed, pKVWearInputItems, TEX_COMPOSITE_CREATE_FLAGS_VERIFY_SCHEMA_ONLY ) );
+ SCHEMA_INIT_CHECK( pWeaponSkinBaseCompositor != NULL, "Could Not Create Weapon Skin Compositor for [%s][Wear %d][Team BLUE]", pName, iWearLevel );
+ SafeRelease( &pWeaponSkinBaseCompositor );
+#endif // CLIENT_DLL
+ return SCHEMA_INIT_SUCCESS();
+}
+
+//-----------------------------------------------------------------------------
+bool CEconItemPaintKitDefinition::BInitFromKV( KeyValues *pKVPItemPaintKit, CUtlVector<CUtlString> *pVecErrors )
+{
+ m_pszName = pKVPItemPaintKit->GetName();
+ m_pszLocalizedName = m_pszName; // localization key is same as paintkit name for ease of generation
+
+ SCHEMA_INIT_CHECK( m_pszLocalizedName != NULL, "Paint Kit %s: PaintKit contains no localized name", m_pszName );
+
+ KeyValues *pKVWearInputItems = NULL;
+
+ pKVWearInputItems = pKVPItemPaintKit->FindKey( "wear_level_1", false );
+ SCHEMA_INIT_CHECK( VerifyPaintKitComposite( pKVWearInputItems, m_pszName, 1, pVecErrors ), "Could Not Create Weapon Skin Compositor for [%s][Wear %d]", m_pszName, 1 );
+ m_vecPaintKitWearKVP.AddToTail( pKVWearInputItems->MakeCopy() );
+
+ pKVWearInputItems = pKVPItemPaintKit->FindKey( "wear_level_2", false );
+ SCHEMA_INIT_CHECK( VerifyPaintKitComposite( pKVWearInputItems, m_pszName, 2, pVecErrors ), "Could Not Create Weapon Skin Compositor for [%s][Wear %d]", m_pszName, 2 );
+ m_vecPaintKitWearKVP.AddToTail( pKVWearInputItems->MakeCopy() );
+
+ pKVWearInputItems = pKVPItemPaintKit->FindKey( "wear_level_3", false );
+ SCHEMA_INIT_CHECK( VerifyPaintKitComposite( pKVWearInputItems, m_pszName, 3, pVecErrors ), "Could Not Create Weapon Skin Compositor for [%s][Wear %d]", m_pszName, 3 );
+ m_vecPaintKitWearKVP.AddToTail( pKVWearInputItems->MakeCopy() );
+
+ pKVWearInputItems = pKVPItemPaintKit->FindKey( "wear_level_4", false );
+ SCHEMA_INIT_CHECK( VerifyPaintKitComposite( pKVWearInputItems, m_pszName, 4, pVecErrors ), "Could Not Create Weapon Skin Compositor for [%s][Wear %d]", m_pszName, 4 );
+ m_vecPaintKitWearKVP.AddToTail( pKVWearInputItems->MakeCopy() );
+
+ pKVWearInputItems = pKVPItemPaintKit->FindKey( "wear_level_5", false );
+ SCHEMA_INIT_CHECK( VerifyPaintKitComposite( pKVWearInputItems, m_pszName, 5, pVecErrors ), "Could Not Create Weapon Skin Compositor for [%s][Wear %d]", m_pszName, 5 );
+ m_vecPaintKitWearKVP.AddToTail( pKVWearInputItems->MakeCopy() );
+
+ return SCHEMA_INIT_SUCCESS();
+}
+
+KeyValues *CEconItemPaintKitDefinition::GetPaintKitWearKV( int nWear )
+{
+ // Wear is 1-5 but this vec is 0-4
+ int iIndex = nWear - 1;
+
+ if ( !m_vecPaintKitWearKVP.IsValidIndex( iIndex ) )
+ {
+ iIndex = 0;
+ Assert( m_vecPaintKitWearKVP.IsValidIndex( iIndex ) );
+ DevMsg( "Invalid Paint Kit or Paint Kit Wear Entry (%s) Wear (%d).", m_pszName, nWear );
+ return NULL;
+ }
+
+ return m_vecPaintKitWearKVP[iIndex];
+}
+
+//-----------------------------------------------------------------------------
+// CEconOperationDefinition
+//-----------------------------------------------------------------------------
+CEconOperationDefinition::CEconOperationDefinition( void )
+ : m_pszName( NULL )
+ , m_unRequiredItemDefIndex( INVALID_ITEM_DEF_INDEX )
+ , m_unGatewayItemDefIndex( INVALID_ITEM_DEF_INDEX )
+{
+}
+
+//-----------------------------------------------------------------------------
+CEconOperationDefinition::~CEconOperationDefinition( void )
+{
+ if ( m_pKVItem )
+ m_pKVItem->deleteThis();
+ m_pKVItem = NULL;
+}
+
+//-----------------------------------------------------------------------------
+bool CEconOperationDefinition::BInitFromKV( KeyValues *pKVPOperation, CUtlVector<CUtlString> *pVecErrors )
+{
+ m_unOperationID = V_atoi( pKVPOperation->GetName() );
+
+ m_pszName = pKVPOperation->GetString( "name", NULL );
+ SCHEMA_INIT_CHECK( m_pszName != NULL, "OperationDefinition %s does not have 'name'", m_pszName );
+
+ // initialize required item def index if we specified one
+ const char *pszRequiredName = pKVPOperation->GetString( "required_item_name", NULL );
+ if ( pszRequiredName )
+ {
+ CEconItemDefinition *pDef = GetItemSchema()->GetItemDefinitionByName( pszRequiredName );
+ SCHEMA_INIT_CHECK( pDef != NULL, "OperationDefinition couldn't find item def from required name '%s'", pszRequiredName );
+
+ m_unRequiredItemDefIndex = pDef->GetDefinitionIndex();
+ }
+
+ const char *pszGatewayItemName = pKVPOperation->GetString( "gateway_item_name", NULL );
+ if ( pszGatewayItemName )
+ {
+ CEconItemDefinition *pDef = GetItemSchema()->GetItemDefinitionByName( pszGatewayItemName );
+ SCHEMA_INIT_CHECK( pDef != NULL, "OperationDefinition couldn't find item def from gateway name '%s'", pszGatewayItemName );
+
+ m_unGatewayItemDefIndex = pDef->GetDefinitionIndex();
+ }
+
+ if ( m_unGatewayItemDefIndex != INVALID_ITEM_DEF_INDEX )
+ {
+ SCHEMA_INIT_CHECK( m_unRequiredItemDefIndex != INVALID_ITEM_DEF_INDEX, "If a gateway item is set, a required item must be set! Mismatch in %d", m_unOperationID );
+ }
+
+ const char *pszOperationStartDate = pKVPOperation->GetString( "operation_start_date", NULL );
+ SCHEMA_INIT_CHECK( pszOperationStartDate != NULL, "OperationDefinition %s does not have 'operation_start_date'", m_pszName );
+ m_OperationStartDate = ( pszOperationStartDate && pszOperationStartDate[0] )
+ ? CRTime::RTime32FromFmtString( "YYYY-MM-DD hh:mm:ss" , pszOperationStartDate )
+ : RTime32(0);
+
+ const char *pszDropEndDate = pKVPOperation->GetString( "stop_giving_to_player_date", NULL );
+ SCHEMA_INIT_CHECK( pszDropEndDate != NULL, "OperationDefinition %s does not have 'stop_giving_to_player_date'", m_pszName );
+ m_StopGivingToPlayerDate = ( pszDropEndDate && pszDropEndDate[0] )
+ ? CRTime::RTime32FromFmtString( "YYYY-MM-DD hh:mm:ss" , pszDropEndDate )
+ : RTime32(0);
+
+ const char *pszOperationEndDate = pKVPOperation->GetString( "stop_adding_to_queue_date", NULL );
+ SCHEMA_INIT_CHECK( pszOperationEndDate != NULL, "OperationDefinition %s does not have 'stop_adding_to_queue_date'", m_pszName );
+ m_StopAddingToQueueDate = ( pszOperationEndDate && pszOperationEndDate[0] )
+ ? CRTime::RTime32FromFmtString( "YYYY-MM-DD hh:mm:ss" , pszOperationEndDate )
+ : RTime32(0);
+
+ m_pszQuestLogResFile = pKVPOperation->GetString( "quest_log_res_file", NULL );
+ m_pszQuestListResFile = pKVPOperation->GetString( "quest_list_res_file", NULL );
+
+ m_pszOperationLootList = pKVPOperation->GetString( "operation_lootlist" );
+ SCHEMA_INIT_CHECK( m_pszOperationLootList != NULL, "OperationDefinition %s does not have 'operation_lootlist'", m_pszName );
+
+ m_bIsCampaign = pKVPOperation->GetBool( "is_campaign" );
+ m_unMaxDropCount = pKVPOperation->GetInt( "max_drop_count" );
+
+#ifdef GC_DLL
+
+ m_rtQueueFreqMin = pKVPOperation->GetFloat( "queue_freq_min" ) * k_nSecondsPerHour; // converts specified hours to seconds
+ SCHEMA_INIT_CHECK( m_rtQueueFreqMin != 0.f, "OperationDefinition %s does not have 'queue_freq_min'", m_pszName );
+
+ m_rtQueueFreqMax = pKVPOperation->GetFloat( "queue_freq_max" ) * k_nSecondsPerHour; // converts specified hours to seconds
+ SCHEMA_INIT_CHECK( m_rtQueueFreqMax != 0.f, "OperationDefinition %s does not have 'queue_freq_max'", m_pszName );
+
+ SCHEMA_INIT_CHECK( m_rtQueueFreqMin <= m_rtQueueFreqMax, "OperationDefinition %s 'queue_freq_min' must be less than 'queue_freq_max'", m_pszName );
+
+ m_rtDropFreqMin = pKVPOperation->GetFloat( "drop_freq_min" ) * k_nSecondsPerHour; // converts specified hours to seconds
+ SCHEMA_INIT_CHECK( m_rtDropFreqMin != 0.f, "OperationDefinition %s does not have 'drop_freq_min'", m_pszName );
+
+ m_rtDropFreqMax = pKVPOperation->GetFloat( "drop_freq_max" ) * k_nSecondsPerHour; // converts specified hours to seconds
+ SCHEMA_INIT_CHECK( m_rtDropFreqMax != 0.f, "OperationDefinition %s does not have 'drop_freq_max'", m_pszName );
+
+ SCHEMA_INIT_CHECK( m_rtDropFreqMin <= m_rtDropFreqMax, "OperationDefinition %s 'drop_freq_min' must be less than 'drop_freq_max'", m_pszName );
+
+ m_unSeed = pKVPOperation->GetInt( "seed_drops" );
+ m_unMaxHeldDrops = pKVPOperation->GetInt( "max_held_drops" );
+ m_nMaxQueueCount = pKVPOperation->GetInt( "max_queue_count" );
+ m_unMaxDropPerThink = pKVPOperation->GetInt( "max_drop_per_think", 1 );
+
+ m_pszContractRewardLootlist[ REWARD_CASE ] = pKVPOperation->GetString( "contract_reward_case_lootlist" );
+ m_pszContractRewardLootlist[ REWARD_WEAPON ] = pKVPOperation->GetString( "contract_reward_weapon_lootlist" );
+
+#endif // GC_DLL
+
+ m_pKVItem = pKVPOperation->MakeCopy();
+
+ return SCHEMA_INIT_SUCCESS();
+}
+
+#ifdef GC_DLL
+#ifdef STAGING_ONLY
+ GCConVar gc_quick_operation_drop_name( "gc_quick_operation_drop_name", "" );
+ GCConVar gc_quick_operation_drop_rate( "gc_quick_operation_drop_rate", "10" );
+#endif // STAGING_ONLY
+
+RTime32 CEconOperationDefinition::GetMinQueueFreq() const
+{
+#ifdef STAGING_ONLY
+ if ( Q_stricmp( gc_quick_operation_drop_name.GetString(), m_pszName ) == 0 )
+ {
+ return gc_quick_operation_drop_rate.GetInt();
+ }
+#endif
+
+ return m_rtQueueFreqMin;
+}
+
+RTime32 CEconOperationDefinition::GetMaxQueueFreq() const
+{
+#ifdef STAGING_ONLY
+ if ( Q_stricmp( gc_quick_operation_drop_name.GetString(), m_pszName ) == 0 )
+ {
+ return gc_quick_operation_drop_rate.GetInt() + 2;
+ }
+#endif
+
+ return m_rtQueueFreqMax;
+}
+
+RTime32 CEconOperationDefinition::GetMinDropFreq() const
+{
+#ifdef STAGING_ONLY
+ if ( Q_stricmp( gc_quick_operation_drop_name.GetString(), m_pszName ) == 0 )
+ {
+ return gc_quick_operation_drop_rate.GetInt();
+ }
+#endif
+
+ return m_rtDropFreqMin;
+}
+
+RTime32 CEconOperationDefinition::GetMaxDropFreq() const
+{
+#ifdef STAGING_ONLY
+ if ( Q_stricmp( gc_quick_operation_drop_name.GetString(), m_pszName ) == 0 )
+ {
+ return gc_quick_operation_drop_rate.GetInt() + 2;
+ }
+#endif
+
+ return m_rtDropFreqMax;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool BCommonInitPropertyGeneratorsFromKV( const char *pszContext, CUtlVector<const IEconItemPropertyGenerator *> *out_pvecGenerators, KeyValues *pKV, CUtlVector<CUtlString> *pVecErrors )
+{
+ // Forward declaration so factory functions can be wherever.
+ IEconItemPropertyGenerator *CreateChangeQualityGenerator( KeyValues *, CUtlVector<CUtlString> * );
+ IEconItemPropertyGenerator *CreateRandomEvenChanceAttrGenerator( KeyValues *, CUtlVector<CUtlString> * );
+ IEconItemPropertyGenerator *CreateUniformLineItemLootListGenerator( KeyValues *, CUtlVector<CUtlString> * );
+ IEconItemPropertyGenerator *CreateDynamicAttrsGenerator( KeyValues *, CUtlVector<CUtlString> * );
+
+ // "Factory".
+ struct econ_item_property_generator_factory_entry_t
+ {
+ const char *m_pszGeneratorName;
+ IEconItemPropertyGenerator * (* m_funcCreateGeneratorInstance)( KeyValues *, CUtlVector<CUtlString> * );
+ };
+
+ static econ_item_property_generator_factory_entry_t s_Generators[] =
+ {
+ { "change_quality", &CreateChangeQualityGenerator },
+ { "random_even_chance_attr", &CreateRandomEvenChanceAttrGenerator },
+ { "uniform_line_item_loot_list", &CreateUniformLineItemLootListGenerator },
+ { "dynamic_attrs", &CreateDynamicAttrsGenerator },
+ };
+
+ Assert( out_pvecGenerators );
+ Assert( pVecErrors );
+
+ // No input data means "we succeeded here".
+ if ( !pKV )
+ return true;
+
+ // Handle each generator one at a time. We'll try to initialize the whole set to get as many
+ // errors as possible and then return accumulated errors.
+ FOR_EACH_SUBKEY( pKV, pKVGenerator )
+ {
+ const char *pszGeneratorName = pKVGenerator->GetName();
+
+ IEconItemPropertyGenerator *pGenerator = NULL;
+ for ( const auto& gen : s_Generators )
+ {
+ if ( Q_stricmp( gen.m_pszGeneratorName, pszGeneratorName ) != 0 )
+ continue;
+
+ pGenerator = (*gen.m_funcCreateGeneratorInstance)( pKVGenerator, pVecErrors );
+ SCHEMA_INIT_CHECK( pGenerator != nullptr, "%s: property generator \"%s\" failed to initialize.\n", pszContext, pszGeneratorName );
+
+ out_pvecGenerators->AddToTail( pGenerator );
+ break;
+ }
+
+ // Make sure we found a way to create this instance.
+ SCHEMA_INIT_CHECK( pGenerator != nullptr, "%s: unknown type for property generator \"%s\".\n", pszContext, pszGeneratorName );
+ }
+
+ return SCHEMA_INIT_SUCCESS();
+}
+#endif // GC_DLL
+
+//-----------------------------------------------------------------------------
+// Dtor
+//-----------------------------------------------------------------------------
+CEconLootListDefinition::~CEconLootListDefinition( void )
+{
+#ifdef GC_DLL
+ m_RandomAttribs.PurgeAndDeleteElements();
+ m_PropertyGenerators.PurgeAndDeleteElements();
+#endif
+}
+
+#ifdef GC_DLL
+bool CEconLootListDefinition::AddRandomAtrributes( KeyValues *pRandomAttributesKV, CEconItemSchema &pschema, CUtlVector<CUtlString> *pVecErrors /*= NULL*/ )
+{
+ const char *pszAttrName = pRandomAttributesKV->GetName();
+
+ // We've found the random attribute block. Parse it.
+ random_attrib_t *pRandomAttr = pschema.CreateRandomAttribute( m_pszName, pRandomAttributesKV, pVecErrors );
+
+ SCHEMA_INIT_CHECK(
+ NULL != pRandomAttr,
+ CFmtStr( "Loot List %s: Failed to create random_attrib_t '%s'", m_pszName, pszAttrName ) );
+
+ m_RandomAttribs.AddToTail( pRandomAttr );
+
+ return true;
+}
+
+bool CEconLootListDefinition::AddRandomAttributesFromTemplates( KeyValues *pRandomAttributesKV, CEconItemSchema &pschema, CUtlVector<CUtlString> *pVecErrors /*= NULL*/ )
+{
+ const char *pszAttrName = pRandomAttributesKV->GetName();
+
+ // try to find attr by template name
+ random_attrib_t *pRandomAttrTemplate = pschema.GetRandomAttributeTemplateByName( pszAttrName );
+
+ SCHEMA_INIT_CHECK(
+ NULL != pRandomAttrTemplate,
+ CFmtStr( "Loot List %s: Couldn't find random_attrib_t '%s' from attribute_templates", m_pszName, pszAttrName ) );
+
+ // craete a copy of the template and add to the list
+ random_attrib_t *pRandomAttr = new random_attrib_t;
+ *pRandomAttr = *pRandomAttrTemplate;
+ m_RandomAttribs.AddToTail( pRandomAttr );
+
+ return true;
+}
+#endif // GC_DLL
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+static const char *g_pszDefaultRevolvingLootListHeader = "#Econ_Revolving_Loot_List";
+
+bool CEconLootListDefinition::BInitFromKV( KeyValues *pKVLootList, CEconItemSchema &pschema, CUtlVector<CUtlString> *pVecErrors )
+{
+ m_pszName = pKVLootList->GetName();
+ m_pszLootListHeader = g_pszDefaultRevolvingLootListHeader;
+ m_pszLootListFooter = NULL;
+ m_pszCollectionReference = NULL;
+ m_bPublicListContents = true;
+#ifdef GC_DLL
+ m_iNoDupesIterations = -1; // disable no-dupes functionality by default
+ m_unRarity = pKVLootList->GetInt( "rarity", k_unItemRarity_Any );
+#endif // GC_DLL
+ bool bCollectionLootList = false;
+
+ FOR_EACH_SUBKEY( pKVLootList, pKVListItem )
+ {
+ const char *pszName = pKVListItem->GetName();
+
+ if ( !Q_strcmp( pszName, "loot_list_header_desc" ) )
+ {
+ // Make sure we didn't specify multiple entries.
+ SCHEMA_INIT_CHECK(
+ g_pszDefaultRevolvingLootListHeader == m_pszLootListHeader,
+ "Loot list %s: Multiple header descriptions specified", m_pszName );
+
+ m_pszLootListHeader = pKVListItem->GetString();
+
+ SCHEMA_INIT_CHECK(
+ NULL != m_pszLootListHeader,
+ "Loot list %s: Invalid header description specified", m_pszName );
+
+ continue;
+ }
+ else if ( !Q_strcmp( pszName, "loot_list_footer_desc" ) )
+ {
+ // Make sure we didn't specify multiple entries.
+ SCHEMA_INIT_CHECK(
+ NULL == m_pszLootListFooter,
+ "Loot list %s: Multiple footer descriptions specified", m_pszName );
+
+ m_pszLootListFooter = pKVListItem->GetString();
+
+ SCHEMA_INIT_CHECK(
+ NULL != m_pszLootListFooter,
+ "Loot list %s: Invalid header description specified", m_pszName );
+
+ continue;
+ }
+ else if ( !Q_strcmp( pszName, "loot_list_collection" ) )
+ {
+ // Set name as the collection lootlist name
+ pszName = pKVListItem->GetString();
+ m_pszCollectionReference = pszName;
+ bCollectionLootList = true;
+ }
+ else if ( !Q_strcmp( pszName, "hide_lootlist" ) )
+ {
+ m_bPublicListContents = !pKVListItem->GetBool( nullptr, true );
+ continue;
+ }
+ else if ( !Q_strcmp( pszName, "rarity" ) )
+ {
+ // already parsed up top
+ continue;
+ }
+#ifdef GC_DLL
+ else if ( !Q_strcmp( pszName, "random_attributes" ) )
+ {
+ AddRandomAtrributes( pKVListItem, pschema, pVecErrors );
+ continue;
+ }
+ else if ( !Q_strcmp( pszName, "attribute_templates" ) )
+ {
+ FOR_EACH_SUBKEY( pKVListItem, pKVAttributeTemplate )
+ {
+ if ( pKVAttributeTemplate->GetInt() == 0 )
+ continue;
+
+ bool bAdded = AddRandomAttributesFromTemplates( pKVAttributeTemplate, pschema, pVecErrors );
+ SCHEMA_INIT_CHECK( bAdded, "Loot list %s: Failed to attribute_templates '%s'", m_pszName, pKVAttributeTemplate->GetName() );
+ }
+
+ continue;
+ }
+ else if ( !Q_strcmp( pszName, "public_list_contents" ) )
+ {
+ m_bPublicListContents = pKVListItem->GetBool( nullptr, true );
+ continue;
+ }
+ else if ( !Q_stricmp( pszName, "__no_dupes_iter_count" ) )
+ {
+ m_iNoDupesIterations = pKVListItem->GetInt( nullptr, -1 );
+ continue;
+ }
+ else if ( !Q_strcmp( pszName, "additional_drop" ) )
+ {
+ float fChance = pKVListItem->GetFloat( "chance", 0.0f );
+ bool bPremiumOnly = pKVListItem->GetBool( "premium_only", false );
+ const char *pszLootList = pKVListItem->GetString( "loot_list", "" );
+ const char *pszRequiredHoliday = pKVListItem->GetString( "required_holiday", NULL );
+ const char *pszDropPerdiodStartDate = pKVListItem->GetString( "start_date", NULL );
+ const char *pszDropPerdiodEndDate = pKVListItem->GetString( "end_date", NULL );
+
+ int iRequiredHolidayIndex = pszRequiredHoliday
+ ? EconHolidays_GetHolidayForString( pszRequiredHoliday )
+ : kHoliday_None;
+
+ RTime32 dropStartDate = ( pszDropPerdiodStartDate && pszDropPerdiodStartDate[0] )
+ ? CRTime::RTime32FromFmtString( "YYYY-MM-DD hh:mm:ss" , pszDropPerdiodStartDate )
+ : RTime32(0); // Default to the start of time
+
+ // Check that if we convert back to a string, we get the same value
+ char rtimeBuf[k_RTimeRenderBufferSize];
+ SCHEMA_INIT_CHECK(
+ pszDropPerdiodStartDate == NULL || Q_strcmp( CRTime::RTime32ToString( dropStartDate, rtimeBuf ), pszDropPerdiodStartDate ) == 0,
+ "Malformed start drop date \"%s\" for additional_drop in lootlist %s. Must be of the form \"YYYY-MM-DD hh:mm:ss\"", pszDropPerdiodStartDate, m_pszName );
+
+
+ RTime32 dropEndDate = ( pszDropPerdiodEndDate && pszDropPerdiodEndDate[0] )
+ ? CRTime::RTime32FromFmtString( "YYYY-MM-DD hh:mm:ss" , pszDropPerdiodEndDate )
+ : ~RTime32(0); // Default to the end of time
+
+ // Check that if we convert back to a string, we get the same value
+ SCHEMA_INIT_CHECK(
+ pszDropPerdiodEndDate == NULL || Q_strcmp( CRTime::RTime32ToString( dropEndDate, rtimeBuf ), pszDropPerdiodEndDate ) == 0,
+ "Malformed end drop date \"%s\" for additional_drop in lootlist %s. Must be of the form \"YYYY-MM-DD hh:mm:ss\"", pszDropPerdiodEndDate, m_pszName );
+
+ SCHEMA_INIT_CHECK(
+ fChance > 0.0f && fChance <= 1.0f,
+ "Loot list %s: Invalid \"additional_drop\" chance %.2f", m_pszName, fChance );
+
+ SCHEMA_INIT_CHECK(
+ pszLootList && pszLootList[0],
+ "Loot list %s: Missing \"additional_drop\" loot list name", m_pszName );
+
+ SCHEMA_INIT_CHECK(
+ (pszRequiredHoliday == NULL) == (iRequiredHolidayIndex == kHoliday_None),
+ "Loot list %s: Unknown or missing holiday \"%s\"", m_pszName, pszRequiredHoliday ? pszRequiredHoliday : "(null)" );
+
+ if ( pszLootList )
+ {
+ const CEconLootListDefinition *pLootListDef = GetItemSchema()->GetLootListByName( pszLootList );
+ SCHEMA_INIT_CHECK(
+ pLootListDef,
+ "Loot list %s: Invalid \"additional_drop\" loot list \"%s\"", m_pszName, pszLootList );
+
+ if ( pLootListDef )
+ {
+ drop_period_t dropPeriod = { dropStartDate, dropEndDate };
+ loot_list_additional_drop_t additionalDrop = { fChance, bPremiumOnly, pszLootList, iRequiredHolidayIndex, dropPeriod };
+ m_AdditionalDrops.AddToTail( additionalDrop );
+ }
+ }
+ continue;
+ }
+ else if ( !Q_strcmp( pszName, "property_generators" ) )
+ {
+ SCHEMA_INIT_SUBSTEP( BCommonInitPropertyGeneratorsFromKV( m_pszName, &m_PropertyGenerators, pKVListItem, pVecErrors ) );
+ continue;
+ }
+#endif // GC_DLL
+
+ int iDef = 0;
+ // First, see if we've got a loot list name, for embedded loot lists
+ int iIdx = 0;
+ if ( GetItemSchema()->GetLootListByName( pszName, &iIdx ) )
+ {
+ // HACKY: Store loot list indices as negatives, starting from -1, because 0 is a valid item index
+ iDef = 0 - 1 - iIdx;
+ }
+ else
+ {
+ // Not a loot list. See if it's an item index. Check the first character.
+ if ( pszName[0] >= '0' && pszName[0] <= '9' )
+ {
+ iDef = atoi( pszName );
+ }
+ else
+ {
+ // Not a number. See if we can find an item def with a matching name.
+ const CEconItemDefinition *pDef = GetItemSchema()->GetItemDefinitionByName( pszName );
+ if ( pDef )
+ {
+ iDef = pDef->GetDefinitionIndex();
+ }
+
+ SCHEMA_INIT_CHECK(
+ pDef != NULL,
+ "Loot list %s: Item definition \"%s\" was not found", m_pszName, pszName );
+ }
+ }
+
+ // Default to the start dropping at the start of time and end dropping at the end of time
+ drop_period_t dropPeriod = { RTime32(0), ~RTime32(0) };
+
+ // Make sure we never put non-enabled items into loot lists
+ if ( iDef > 0 )
+ {
+ const CEconItemDefinition *pItemDef = GetItemSchema()->GetItemDefinition( iDef );
+ SCHEMA_INIT_CHECK(
+ pItemDef != NULL,
+ "Loot list %s: Item definition index \"%s\" (%d) was not found", m_pszName, pszName, iDef );
+
+ static CSchemaAttributeDefHandle pAttribDef_StartDropDate( "start drop date" );
+ static CSchemaAttributeDefHandle pAttribDef_EndDropDate( "end drop date" );
+
+ CAttribute_String value;
+ // Check for start drop date attribute on this item
+ if ( FindAttribute( pItemDef, pAttribDef_StartDropDate, &value ) )
+ {
+ const char* pszStartDate = value.value().c_str();
+ dropPeriod.m_DropStartDate = CRTime::RTime32FromFmtString( "YYYY-MM-DD hh:mm:ss" , pszStartDate );
+
+ // Check that if we convert back to a string, we get the same value
+ char rtimeBuf[k_RTimeRenderBufferSize];
+ SCHEMA_INIT_CHECK(
+ Q_strcmp( CRTime::RTime32ToString( dropPeriod.m_DropStartDate, rtimeBuf ), pszStartDate ) == 0,
+ "Malformed start drop date \"%s\" for item %s. Must be of the form \"YYYY-MM-DD hh:mm:ss\"",
+ pszStartDate, pItemDef->GetDefinitionName() );
+ }
+
+ // Check for end drop date attribute on this item
+ if ( FindAttribute( pItemDef, pAttribDef_EndDropDate, &value ) )
+ {
+ const char* pszEndDate = value.value().c_str();
+ dropPeriod.m_DropEndDate = CRTime::RTime32FromFmtString( "YYYY-MM-DD hh:mm:ss" , pszEndDate );
+
+ // Check that if we convert back to a string, we get the same value
+ char rtimeBuf[k_RTimeRenderBufferSize];
+ SCHEMA_INIT_CHECK(
+ Q_strcmp( CRTime::RTime32ToString( dropPeriod.m_DropEndDate, rtimeBuf ), pszEndDate ) == 0,
+ "Malformed end drop date \"%s\" for item %s. Must be of the form \"YYYY-MM-DD hh:mm:ss\"", pszEndDate, pItemDef->GetDefinitionName() );
+ }
+
+#ifdef GC_DLL
+ if ( pItemDef )
+ {
+ SCHEMA_INIT_CHECK(
+ true == pItemDef->BEnabled(),
+ "Loot list %s: Item definition \"%s\" (%d) isn't enabled, not allowed in loot lists", m_pszName, pItemDef->GetDefinitionName(), iDef );
+ }
+#endif // GC_DLL
+ }
+
+ float fItemWeight = 0.f;
+#ifdef GC_DLL
+ fItemWeight = bCollectionLootList ? 1.0f : pKVListItem->GetFloat();
+ SCHEMA_INIT_CHECK(
+ fItemWeight > 0.0f,
+ "Loot list %s: Item definition index \"%s\" (%d) has invalid weight %.2f", m_pszName, pszName, iDef, fItemWeight );
+#endif // GC_DLL
+
+ // Add this item
+ drop_item_t dropItem = { iDef, fItemWeight, dropPeriod };
+ m_DropList.AddToTail( dropItem );
+ }
+
+#ifdef GC_DLL
+
+ int nNumTimeLimitedItems = 0;
+ FOR_EACH_VEC( m_DropList, i )
+ {
+ // If either the start date or the end date is set, tally it up as a time limited item
+ if( m_DropList[i].m_dropPeriod.m_DropStartDate != RTime32(0) || m_DropList[i].m_dropPeriod.m_DropEndDate != ~RTime32(0) )
+ {
+ ++nNumTimeLimitedItems;
+ }
+ }
+
+ // Verify that at least one item in a lootlist does not have a drop period that limits when it can drop.
+ // This guarantees that we will always drop *something*
+ SCHEMA_INIT_CHECK( m_DropList.Count() > nNumTimeLimitedItems, "Lootlist \"%s\" is made up entirely of limited-time items! At least one must not be time-limited.", m_pszName );
+#endif
+
+ return SCHEMA_INIT_SUCCESS();
+}
+
+#ifdef GC_DLL
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+static bool BContainsDuplicateItemDefs( const CUtlVector<CEconLootListDefinition::rolled_item_defs_t>& vecItemDefsA, const CUtlVector<CEconLootListDefinition::rolled_item_defs_t>& vecItemDefsB )
+{
+ CUtlHashtable<const CEconItemDefinition *> hashItemDefs;
+
+ auto BPopulateAndLookForDupes = [&] ( const CUtlVector<CEconLootListDefinition::rolled_item_defs_t>& vecItemDefs )
+ {
+ for ( const auto& rolledItemDef : vecItemDefs )
+ {
+ if ( hashItemDefs.HasElement( rolledItemDef.m_pItemDef ) )
+ return true;
+
+ hashItemDefs.Insert( rolledItemDef.m_pItemDef );
+ }
+
+ return false;
+ };
+
+ return BPopulateAndLookForDupes( vecItemDefsA )
+ || BPopulateAndLookForDupes( vecItemDefsB );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CEconLootListDefinition::BGenerateSingleRollRandomItems( const CEconGameAccount *pGameAccount, bool bFreeAccount, CUtlVector<CEconItem *> *out_pvecItems, const CUtlVector< item_definition_index_t > *pVecAvoidItemDefs /*= NULL*/ ) const
+{
+ Assert( out_pvecItems );
+
+ // Where is our source of random numbers coming from? If we're a no-dupe list,
+ // we want to have reproducible state so we use our account ID as a unique-ish
+ // seed.
+ //
+ // Wrap the whole thing in a smart pointer so we clean up whenever/however we
+ // leave.
+ std::unique_ptr<IUniformRandomStream> pRandomStream( [=]() -> IUniformRandomStream *
+ {
+ if ( !BIsInternalNoDupesLootList() || !pGameAccount )
+ return new CDefaultUniformRandomStream;
+
+
+ CUniformRandomStream *pAccountUniformRandomStream = new CUniformRandomStream;
+ pAccountUniformRandomStream->SetSeed( pGameAccount->Obj().m_unAccountID );
+
+ return pAccountUniformRandomStream;
+ }() );
+
+ // Make however many passes through our loot list code until we've generated the
+ // right number of passing sets. (Most loot lists let anything pass. Some specify
+ // no duplicate definitions allowed.)
+ CUtlVector<rolled_item_defs_t> vecCumulativeItemDefs; // total list of everything we've generated so far in any number of result sets
+ CUtlVector<rolled_item_defs_t> vecItemDefs; // current list under evaluation
+ int iNoDupesIterations = 0;
+
+ // This actually isn't guaranteed to converge, and is guaranteed not to converge if
+ // we set up a broken lootlist. We set a really high bar here to catch broken/pathologically
+ // bad cases here without grinding the whole GC to a halt.
+ enum { kUpperBoundIterationSanityCheck = 2000 };
+ int iTotalIterations = 0;
+
+ while ( true )
+ {
+ // Don't runaway.
+ iTotalIterations++;
+ if ( iTotalIterations >= kUpperBoundIterationSanityCheck )
+ return false;
+
+ // Generate all of our item defs and their lootlists
+ vecItemDefs.Purge();
+ if ( !RollRandomItemsAndAdditionalItems( pRandomStream.get(), bFreeAccount, &vecItemDefs, pVecAvoidItemDefs ) )
+ return false;
+
+ // If we don't care about dupes and we got any results at all we're done.
+ if ( !BIsInternalNoDupesLootList() )
+ break;
+
+ // If we do care about dupes and we have some, ignore this set of items.
+ if ( BContainsDuplicateItemDefs( vecItemDefs, vecCumulativeItemDefs ) )
+ continue;
+
+ // Did we get to the right result set?
+ iNoDupesIterations++;
+ if ( iNoDupesIterations > m_iNoDupesIterations )
+ break;
+
+ // Store off the list of definition indices we've already used them so they don't get reused
+ // in a later set.
+ vecCumulativeItemDefs.AddVectorToTail( vecItemDefs );
+ }
+
+ // If we get down to here, we expect that we've rolled at least one item def
+ Assert( vecItemDefs.Count() > 0 );
+ FOR_EACH_VEC( vecItemDefs, i )
+ {
+ const rolled_item_defs_t& rolledDef = vecItemDefs[i];
+ Assert( rolledDef.m_pItemDef );
+ Assert( rolledDef.m_vecAffectingLootLists.Count() > 0 );
+
+ // Create the items
+ CEconItem *pItem = GEconManager()->GetItemFactory().CreateSpecificItem( pGameAccount, rolledDef.m_pItemDef->GetDefinitionIndex() );
+ out_pvecItems->AddToTail( pItem );
+
+ // Go through and let all the affecting lootlists attach their attributes to the item
+ FOR_EACH_VEC( rolledDef.m_vecAffectingLootLists, j )
+ {
+ const CEconLootListDefinition *pLootList = rolledDef.m_vecAffectingLootLists[j];
+ Assert( pLootList );
+ if ( !pLootList->BAttachLootListAttributes( pGameAccount, pItem ) )
+ return false;
+ }
+ }
+
+ return ( out_pvecItems->Count() > 0 );
+}
+
+class CRollSimulator
+{
+public:
+
+ CRollSimulator()
+ : m_mapRarityCounts( StringLessThan )
+ , m_mapCounts( DefLessFunc( item_definition_index_t ) )
+ , m_mapUnusualHatEffectsCount( DefLessFunc( uint32 ) )
+ , m_mapUnusualTauntEffectsCount( DefLessFunc( uint32 ) )
+ , m_nNumIters( 0 )
+ {}
+
+ void RollLootlist( const char* pszLootListName, int nRolls, bool bWipePreviousResults = false )
+ {
+ const CEconLootListDefinition* pLootlist = GetItemSchema()->GetLootListByName( pszLootListName );
+
+ if ( !pLootlist )
+ {
+ EmitInfo( SPEW_CONSOLE, SPEW_ALWAYS, LOG_ALWAYS, "Invalid lootlist \"%s\".\n", pszLootListName );
+ return;
+ }
+
+ // Clear out results first?
+ if ( bWipePreviousResults )
+ {
+ m_mapCounts.Purge();
+ m_mapRarityCounts.Purge();
+ m_DropsMaxes.Clear();
+ m_DropsTotals.Clear();
+ }
+
+ while( nRolls-- )
+ {
+ AutoYield();
+ CUtlVector<CEconItem *> vecItems;
+ pLootlist->BGenerateSingleRollRandomItems( NULL, false, &vecItems );
+
+ // Tally up what we got.
+ for( auto pItem : vecItems )
+ {
+ AutoYield();
+ // Insert item def if we need
+ item_definition_index_t nDefIndex = pItem->GetDefinitionIndex();
+ auto idx = m_mapCounts.Find( nDefIndex );
+ if ( m_mapCounts.InvalidIndex() == idx )
+ {
+ idx = m_mapCounts.Insert( nDefIndex );
+ }
+
+ // Insert rarity if needed
+ const CEconItemRarityDefinition* pItemRarity = GetItemSchema()->GetRarityDefinition( pItem->GetItemDefinition()->GetRarity() );
+ if ( pItemRarity )
+ {
+ const char* pszRarity = GGCGameBase()->LocalizeToken( pItemRarity->GetLocKey() , k_Lang_English );
+ auto rarityIdx = m_mapRarityCounts.Find( pszRarity );
+ if ( m_mapRarityCounts.InvalidIndex() == rarityIdx )
+ {
+ rarityIdx = m_mapRarityCounts.Insert( pszRarity, 0 );
+ }
+
+ m_mapRarityCounts[ rarityIdx ] += 1;
+ }
+
+ // Count
+ DropResult_t& result = m_mapCounts[ idx ];
+ ++result.m_nRollCount;
+ ++m_DropsTotals.m_nRollCount;
+ m_DropsMaxes.m_nRollCount = Max( m_DropsMaxes.m_nRollCount, result.m_nRollCount );
+
+ // Strange count
+ if ( BIsItemStrange( pItem ) )
+ {
+ ++result.m_nStrangeCount;
+ ++m_DropsTotals.m_nStrangeCount;
+ m_DropsMaxes.m_nStrangeCount = Max( m_DropsMaxes.m_nStrangeCount, result.m_nStrangeCount );
+ }
+
+ // Unusual count
+ static CSchemaAttributeDefHandle pAttrDef_ParticleEffect( "attach particle effect" );
+ static CSchemaAttributeDefHandle pAttrDef_TauntUnusualAttr( "on taunt attach particle index" );
+ if ( pAttrDef_ParticleEffect && pAttrDef_TauntUnusualAttr )
+ {
+ uint32 nUnusualHatValue = 0;
+ uint32 nUnusualTauntValue = 0;
+ pItem->FindAttribute( pAttrDef_ParticleEffect, &nUnusualHatValue );
+ pItem->FindAttribute( pAttrDef_TauntUnusualAttr, &nUnusualTauntValue );
+ // Cant use quality cause of old legacy items. Quality is just a quick test
+ if ( nUnusualHatValue != 0 || nUnusualTauntValue != 0 )
+ {
+ ++result.m_nUnusualCount;
+ ++m_DropsTotals.m_nUnusualCount;
+ m_DropsMaxes.m_nUnusualCount = Max( m_DropsMaxes.m_nUnusualCount, result.m_nUnusualCount );
+ }
+
+ if ( nUnusualHatValue )
+ {
+ nUnusualHatValue = (uint32)((float&)nUnusualHatValue);
+ auto idx = m_mapUnusualHatEffectsCount.Find( nUnusualHatValue );
+ if ( idx == m_mapUnusualHatEffectsCount.InvalidIndex() )
+ {
+ idx = m_mapUnusualHatEffectsCount.Insert( nUnusualHatValue, 0 );
+ }
+
+ ++m_mapUnusualHatEffectsCount[ idx ];
+ }
+
+ if ( nUnusualTauntValue )
+ {
+ nUnusualTauntValue = (uint32)((float&)nUnusualTauntValue);
+ auto idx = m_mapUnusualTauntEffectsCount.Find( nUnusualTauntValue );
+ if ( idx == m_mapUnusualTauntEffectsCount.InvalidIndex() )
+ {
+ idx = m_mapUnusualTauntEffectsCount.Insert( nUnusualTauntValue, 0 );
+ }
+
+ ++m_mapUnusualTauntEffectsCount[ idx ];
+ }
+ }
+ }
+
+ // Delete what we got
+ vecItems.PurgeAndDeleteElements();
+ }
+ }
+
+ void PrintRarityBreakdwn()
+ {
+ EmitInfo( SPEW_CONSOLE, SPEW_ALWAYS, LOG_ALWAYS, "-- Rarity breakdown:\n" );
+ int nMaxDigits = 0;
+ while( m_mapRarityCounts.Count() )
+ {
+ // Go through and print the most rolled to least rolled rarities
+ auto maxIdx = m_mapRarityCounts.InvalidIndex();
+
+ // Find the most hit
+ FOR_EACH_MAP_FAST( m_mapRarityCounts, i )
+ {
+ AutoYield();
+ if ( maxIdx == m_mapRarityCounts.InvalidIndex() || m_mapRarityCounts[ i ] > m_mapRarityCounts[ maxIdx ] )
+ {
+ maxIdx = i;
+ }
+
+ nMaxDigits = Max( nMaxDigits, NumDigits( m_mapRarityCounts[ maxIdx ] ) );
+ }
+
+ EmitInfo( SPEW_CONSOLE, SPEW_ALWAYS, LOG_ALWAYS, "%9s %*d %.3f%%\n",
+ m_mapRarityCounts.Key( maxIdx ), nMaxDigits, m_mapRarityCounts[ maxIdx ],
+ 100.f * (float)m_mapRarityCounts[ maxIdx ] / m_DropsTotals.m_nRollCount );
+ m_mapRarityCounts.RemoveAt( maxIdx );
+ }
+
+ EmitInfo( SPEW_CONSOLE, SPEW_ALWAYS, LOG_ALWAYS, "%-*s %*s %*s\n", 9 + 1 + NumDigits( m_DropsMaxes.m_nRollCount ) + 6,
+ "-- Item breakdown", NumDigits( m_DropsMaxes.m_nStrangeCount ) + 1, "S", NumDigits( m_DropsMaxes.m_nUnusualCount ), "U" );
+
+ }
+
+ void PrintUnusualCounts()
+ {
+ EmitInfo( SPEW_CONSOLE, SPEW_ALWAYS, LOG_ALWAYS, "-- Unusual Hats breakdown:\n" );
+ int nTotal = PrintUnusualsForType( m_mapUnusualHatEffectsCount );
+ EmitInfo( SPEW_CONSOLE, SPEW_ALWAYS, LOG_ALWAYS, "-- %d total unusual hats\n", nTotal );
+
+ EmitInfo( SPEW_CONSOLE, SPEW_ALWAYS, LOG_ALWAYS, "-- Unusual Taunts breakdown:\n" );
+ nTotal = PrintUnusualsForType( m_mapUnusualTauntEffectsCount );
+ EmitInfo( SPEW_CONSOLE, SPEW_ALWAYS, LOG_ALWAYS, "-- %d total unusual taunts\n", nTotal );
+ }
+
+ void PrintTotals()
+ {
+ while( m_mapCounts.Count() )
+ {
+ // Go through and print the most rolled to least rolled
+ auto maxIdx = m_mapCounts.InvalidIndex();
+
+ // Find the most hit
+ FOR_EACH_MAP_FAST( m_mapCounts, i )
+ {
+ AutoYield();
+ if ( maxIdx == m_mapCounts.InvalidIndex() || m_mapCounts[ i ].m_nRollCount > m_mapCounts[ maxIdx ].m_nRollCount )
+ {
+ maxIdx = i;
+ }
+ }
+
+ DropResult_t& result = m_mapCounts[ maxIdx ];
+ CEconItemDefinition *pItemDef = GetItemSchema()->GetItemDefinition( m_mapCounts.Key( maxIdx ) );
+
+ const CEconItemRarityDefinition* pItemRarity = GetItemSchema()->GetRarityDefinition( pItemDef->GetRarity() );
+ const char* pszRarity = pItemRarity ? GGCGameBase()->LocalizeToken( pItemRarity->GetLocKey() , k_Lang_English ) : "";
+ CFmtStr rollString( "%*d %2.3f%%", NumDigits( m_DropsMaxes.m_nRollCount ), result.m_nRollCount,
+ 100.f * (float)result.m_nRollCount / m_DropsTotals.m_nRollCount );
+ CFmtStr strangeString( "%*d", NumDigits( m_DropsMaxes.m_nStrangeCount ), result.m_nStrangeCount );
+ CFmtStr unusualString( "%*d", NumDigits( m_DropsMaxes.m_nUnusualCount ), result.m_nUnusualCount );
+ const char* pszItemname = GGCGameBase()->LocalizeToken( pItemDef->GetCustomPainkKitDefinition() ? pItemDef->GetCustomPainkKitDefinition()->GetName() : pItemDef->GetItemBaseName(), k_Lang_English );
+
+ EmitInfo( SPEW_CONSOLE, SPEW_ALWAYS, LOG_ALWAYS, "%9s %s %s %s %s\n", pszRarity, rollString.Get(), strangeString.Get(), unusualString.Get(), pszItemname );
+
+ m_mapCounts.RemoveAt( maxIdx );
+ }
+
+ EmitInfo( SPEW_CONSOLE, SPEW_ALWAYS, LOG_ALWAYS, "-- Total Items: %d Strange: %d (%.3f%%) Unusual: %d (%.3f%%)\n"
+ , m_DropsTotals.m_nRollCount
+ , m_DropsTotals.m_nStrangeCount
+ , ( 100.f * (float)m_DropsTotals.m_nStrangeCount / m_DropsTotals.m_nRollCount )
+ , m_DropsTotals.m_nUnusualCount
+ , ( 100.f * (float)m_DropsTotals.m_nUnusualCount / m_DropsTotals.m_nRollCount ) );
+ }
+
+ struct DropResult_t
+ {
+ DropResult_t() { Clear(); }
+
+ void Clear()
+ {
+ m_nStrangeCount = 0;
+ m_nUnusualCount = 0;
+ m_nRollCount = 0;
+ }
+
+ int m_nStrangeCount;
+ int m_nUnusualCount;
+ int m_nRollCount;
+ };
+
+ const DropResult_t& GetTotalDrops() const { return m_DropsTotals; }
+ const DropResult_t& GetMaxesDrops() const { return m_DropsMaxes; }
+
+private:
+
+ int PrintUnusualsForType( CUtlMap< uint32, int >& mapUnusuals )
+ {
+ int nMaxDigits = 0;
+ int nTotal = 0;
+ FOR_EACH_MAP_FAST( mapUnusuals, i )
+ {
+ nTotal += mapUnusuals[ i ];
+ }
+
+ while( mapUnusuals.Count() )
+ {
+ // Go through and print the most rolled to least rolled unusual effects
+ auto maxIdx = mapUnusuals.InvalidIndex();
+
+ // Find the most hit
+ FOR_EACH_MAP_FAST( mapUnusuals, i )
+ {
+ AutoYield();
+ if ( maxIdx == mapUnusuals.InvalidIndex() || mapUnusuals[ i ] > mapUnusuals[ maxIdx ] )
+ {
+ maxIdx = i;
+ }
+
+ nMaxDigits = Max( nMaxDigits, NumDigits( mapUnusuals[ maxIdx ] ) );
+ }
+
+ char particleNameEntry[128];
+ Q_snprintf( particleNameEntry, ARRAYSIZE( particleNameEntry ), "#Attrib_Particle%d", mapUnusuals.Key( maxIdx ) );
+ const char* pszParticleName = GGCGameBase()->LocalizeToken( particleNameEntry, k_Lang_English );
+
+ EmitInfo( SPEW_CONSOLE, SPEW_ALWAYS, LOG_ALWAYS, "%*d %.3f%% %s (%d)\n",
+ nMaxDigits,
+ mapUnusuals[ maxIdx ],
+ 100.f * (float)mapUnusuals[ maxIdx ] / nTotal,
+ pszParticleName,
+ mapUnusuals.Key( maxIdx ) );
+
+ mapUnusuals.RemoveAt( maxIdx );
+ }
+
+ return nTotal;
+ }
+
+ void AutoYield()
+ {
+ if ( ++m_nNumIters % 100 == 0 )
+ {
+ if ( GJobCur().BYieldIfNeeded() )
+ {
+ // If we re-entered logon surge we should go away for a while
+ while ( GGCGameBase()->BIsInLogonSurge() )
+ {
+ GJobCur().BYieldingWaitOneFrame();
+ }
+ }
+ }
+ };
+
+ int NumDigits( int nNumber )
+ {
+ int digits = 0;
+
+ if ( nNumber <= 0)
+ {
+ digits = 1;
+ }
+
+ while ( nNumber )
+ {
+ nNumber /= 10;
+ ++digits;
+ }
+
+ return digits;
+ }
+
+
+
+ CUtlMap< item_definition_index_t, DropResult_t > m_mapCounts;
+ CUtlMap< uint32, int > m_mapUnusualHatEffectsCount;
+ CUtlMap< uint32, int > m_mapUnusualTauntEffectsCount;
+ // Rarity name is the key
+ CUtlMap< const char*, int > m_mapRarityCounts;
+ DropResult_t m_DropsTotals;
+ DropResult_t m_DropsMaxes;
+ size_t m_nNumIters;
+};
+
+GC_CON_COMMAND( simulate_lootlist_contents, "<lootlist> <iterations> Check item distribution from a given lootlist n times" )
+{
+ if ( !BCheckArgs( 2, args, simulate_lootlist_contents_command ) )
+ return;
+
+ const char* pszLootListName = args[1];
+ int nRolls = args.ArgC() == 3 ? atoi( args[2] ) : 1000;
+
+ CRollSimulator simulator;
+ simulator.RollLootlist( pszLootListName, nRolls );
+ simulator.PrintRarityBreakdwn();
+ simulator.PrintUnusualCounts();
+ simulator.PrintTotals();
+}
+
+
+#ifdef GC_DLL
+class CItemSourceFinder
+{
+public:
+ CItemSourceFinder( const char* pszItemName )
+ : m_pItemDef( GetItemSchema()->GetItemDefinitionByName( pszItemName ) )
+ {
+ Assert( m_pItemDef );
+ if ( !m_pItemDef )
+ {
+ EG_ERROR( SPEW_CONSOLE, "%s is not a valid item", pszItemName );
+ return;
+ }
+
+ // Find out what series this crate belongs to.
+ static CSchemaAttributeDefHandle pAttr_CrateSeries( "set supply crate series" );
+ if ( !pAttr_CrateSeries )
+ return;
+
+ auto& mapItemDefs = GetItemSchema()->GetItemDefinitionMap();
+ auto& mapRevolvingLootlists = GetItemSchema()->GetRevolvingLootLists();
+
+ CUtlDict< int > dictSeenLootlists;
+
+ // Look through all the item defs and see if any of them statically specify a lootlist that they want to open
+ FOR_EACH_MAP_FAST( mapItemDefs, i )
+ {
+ const CEconItemDefinition* pSourceItemDef = mapItemDefs[ i ];
+ const CEconLootListDefinition* pLootlist = NULL;
+
+ const CEconTool_Gift* pGift = pSourceItemDef->GetTypedEconTool< CEconTool_Gift >();
+ // Self-opening crate?
+ if ( pGift )
+ {
+ pLootlist = GetItemSchema()->GetLootListByName( pGift->GetLootListName() );
+ }
+ else // Crate with an item series?
+ {
+ int iCrateSeries;
+ {
+ float fCrateSeries; // crate series ID is stored as a float internally because we hate ourselves
+ if ( !FindAttribute_UnsafeBitwiseCast<attrib_value_t>( pSourceItemDef, pAttr_CrateSeries, &fCrateSeries ) || fCrateSeries == 0.0f )
+ continue;
+
+ iCrateSeries = fCrateSeries;
+ }
+
+ auto idx = mapRevolvingLootlists.Find( iCrateSeries );
+ if ( idx == mapRevolvingLootlists.InvalidIndex() )
+ continue;
+
+ pLootlist = GetItemSchema()->GetLootListByName( mapRevolvingLootlists[ idx ] );
+ }
+
+ if ( !pLootlist )
+ continue;
+
+ // Mark that we've seen this lootlist already
+ dictSeenLootlists.Insert( pLootlist->GetName() );
+
+ DropSource_t& source = m_vecSources[ m_vecSources.AddToTail() ];
+ source.m_pDroppingItem = pSourceItemDef;
+ ChanceForItemFromLootlist( m_pItemDef, pLootlist, source.lootlistSource );
+ }
+
+ CEconItemDefinition* pCrateItemDef = GetItemSchema()->GetItemDefinitionByName( "Supply Crate" );
+ Assert( pCrateItemDef );
+
+ // Go through all the revolving lootlists and see if they have the item. Assume that
+ // they're from a "Supply Crate".
+ FOR_EACH_MAP_FAST( mapRevolvingLootlists, i )
+ {
+ if ( !pCrateItemDef )
+ continue;
+
+ if ( mapRevolvingLootlists.Key( i ) <= 0 )
+ continue;
+
+ auto pLootlist = GetItemSchema()->GetLootListByName( mapRevolvingLootlists[ i ] );
+ if ( !pLootlist )
+ continue;
+
+ // This lootlist was on a different crate already
+ if ( dictSeenLootlists.Find( pLootlist->GetName() ) != dictSeenLootlists.InvalidIndex() )
+ continue;
+
+ DropSource_t& source = m_vecSources[ m_vecSources.AddToTail() ];
+ source.m_pDroppingItem = pCrateItemDef;
+ ChanceForItemFromLootlist( m_pItemDef, pLootlist, source.lootlistSource );
+ }
+
+ // Not available at all!
+ if ( !m_vecSources.Count() )
+ {
+ EmitInfo( SPEW_CONSOLE, SPEW_ALWAYS, LOG_ALWAYS, "Not available from any sources!\n" );
+ return;
+ }
+
+ // Sort greatest % chance
+ auto lambdaSort = [] ( DropSource_t const *pLHS, DropSource_t const *pRHS ) -> int
+ {
+ return pLHS->lootlistSource.m_flChance < pRHS->lootlistSource.m_flChance;
+ };
+ m_vecSources.Sort( lambdaSort );
+ }
+
+ void PrintSources()
+ {
+ FOR_EACH_VEC( m_vecSources, i )
+ {
+ m_vecSources[ i ].PrintSources();
+ }
+ }
+
+ bool BDropsFromLootlist( const CEconLootListDefinition* pLootlist )
+ {
+ FOR_EACH_VEC( m_vecSources, i )
+ {
+ if ( m_vecSources[ i ].lootlistSource.BDropsFromLootlist( pLootlist ) )
+ return true;
+ }
+
+ return false;
+ }
+
+private:
+
+ struct DropSource_t
+ {
+ void PrintSources()
+ {
+ if ( lootlistSource.m_flChance == 0.f )
+ return;
+
+ bool bSelfOpening = m_pDroppingItem->GetTypedEconTool< CEconTool_Gift >() != NULL;
+
+ // Print the name of the item, and whether it's a self-opening item, or a crate
+ EmitInfo( SPEW_CONSOLE, SPEW_ALWAYS, LOG_ALWAYS, "%3.5f%% %s (%d - %s)\n",
+ lootlistSource.m_flChance * 100.f,
+ m_pDroppingItem->GetDefinitionName(),
+ m_pDroppingItem->GetDefinitionIndex(),
+ bSelfOpening ? "Self-Opening" : "Crate/Case" );
+
+ // Print all the sources
+ lootlistSource.PrintSources( 1 );
+
+ EmitInfo( SPEW_CONSOLE, SPEW_ALWAYS, LOG_ALWAYS, "\n" );
+ }
+
+ struct LootListSource_t
+ {
+ LootListSource_t()
+ : m_flChance( 0.f )
+ {}
+
+ bool BDropsFromLootlist( const CEconLootListDefinition* pLootlist )
+ {
+ // Skip no-chance entries
+ if ( m_flChance == 0.f )
+ return false;
+
+
+ if ( m_pDroppingLootlist == pLootlist )
+ return true;
+
+ FOR_EACH_VEC( m_vecLootlistSources, i )
+ {
+ if ( m_vecLootlistSources[ i ].BDropsFromLootlist( pLootlist ) )
+ return true;
+ }
+
+ return false;
+ }
+
+ void PrintSources( int nInset )
+ {
+ // Skip no-chance entries
+ if ( m_flChance == 0.f )
+ return;
+
+ auto& mapRevolvingLootlists = GetItemSchema()->GetRevolvingLootLists();
+ int nRevolvingIdx = mapRevolvingLootlists.InvalidIndex();
+
+ // Check if our lootlist is one of the revolving lootlists. If so, we want
+ // to print out its index within revolving_lootlists, so we can map that to
+ // the attribute value of supply_crate_series (187)
+ FOR_EACH_MAP_FAST( mapRevolvingLootlists, i )
+ {
+ if ( V_stricmp( mapRevolvingLootlists[ i ], m_pDroppingLootlist->GetName() ) == 0 )
+ {
+ nRevolvingIdx = mapRevolvingLootlists.Key( i );
+ break;
+ }
+ }
+
+ if ( nRevolvingIdx != mapRevolvingLootlists.InvalidIndex() && nRevolvingIdx > 0 )
+ {
+ // It's in revolving_lootlists. Print its index in there.
+ EmitInfo( SPEW_CONSOLE, SPEW_ALWAYS, LOG_ALWAYS, "%*.5f%% (%d) %s\n",
+ 4 * nInset + 5,
+ m_flChance * 100.f,
+ nRevolvingIdx,
+ m_pDroppingLootlist->GetName() );
+ }
+ else
+ {
+ // Not in the revolving lootlist
+ EmitInfo( SPEW_CONSOLE, SPEW_ALWAYS, LOG_ALWAYS, "%*.5f%% %s\n",
+ 4 * nInset + 5,
+ m_flChance * 100.f,
+ m_pDroppingLootlist->GetName() );
+ }
+
+ // Sort greatest % chance
+ auto lambdaSort = [] ( LootListSource_t const *pLHS, LootListSource_t const *pRHS ) -> int
+ {
+ return pLHS->m_flChance < pRHS->m_flChance;
+ };
+
+ m_vecLootlistSources.Sort( lambdaSort );
+
+ // Print out children indented a lil bit
+ FOR_EACH_VEC( m_vecLootlistSources, i )
+ {
+ m_vecLootlistSources[i].PrintSources( nInset + 1 );
+ }
+ }
+
+ float m_flChance;
+ const CEconLootListDefinition* m_pDroppingLootlist;
+
+ CCopyableUtlVector< LootListSource_t > m_vecLootlistSources;
+ };
+
+ const CEconItemDefinition* m_pDroppingItem;
+ LootListSource_t lootlistSource;
+ };
+
+ float ChanceForItemFromLootlist( const CEconItemDefinition* pItemDef, const CEconLootListDefinition* pLootlist, DropSource_t::LootListSource_t& lootlistSource )
+ {
+ auto& vecContents = pLootlist->GetLootListContents();
+ // Accumulate our chance of dropping the specified item
+ lootlistSource.m_flChance = 0.f;
+ lootlistSource.m_pDroppingLootlist = pLootlist;
+
+ // Gather the items in this lootlist that we're able to roll for at this time
+ float flTotalWeight = 0.f;
+ CUtlVector<CEconLootListDefinition::drop_item_t> vecValidContents;
+ FOR_EACH_VEC( vecContents, i )
+ {
+ if( !vecContents[ i ].m_dropPeriod.IsValidForTime( CRTime::RTime32TimeCur() ) )
+ continue;
+
+ flTotalWeight += vecContents[ i ].m_flWeight;
+ vecValidContents.AddToTail( vecContents[ i ] );
+ }
+
+ // Go through valid contents, and see if the specified item is in there
+ FOR_EACH_VEC( vecValidContents, i )
+ {
+ const int iItemDef = vecValidContents[ i ].m_iItemOrLootlistDef;
+ const float flChance = vecValidContents[ i ].m_flWeight / flTotalWeight;
+
+ if ( iItemDef < 0 ) // Lootlist
+ {
+ int iLLIndex = (iItemDef * -1) - 1;
+ auto pSubLootlist = GetItemSchema()->GetLootListByIndex( iLLIndex );
+
+ // One of our sub-lootlists might drop it. Add in it's chance within
+ // the sub-lootlist scaled by the chance to roll that sub-lootlist.
+ auto& subSource = lootlistSource.m_vecLootlistSources[ lootlistSource.m_vecLootlistSources.AddToTail() ];
+ lootlistSource.m_flChance += ChanceForItemFromLootlist( pItemDef, pSubLootlist, subSource ) * flChance;
+ }
+ else if ( pItemDef->GetDefinitionIndex() == iItemDef )
+ {
+ // We drop it! Add the chance
+ lootlistSource.m_flChance += flChance;
+ }
+ }
+
+ // Treat additional drops just the same as nested lootlists.
+ auto& vecAdditionalDrops = pLootlist->GetAdditionalDrops();
+ FOR_EACH_VEC( vecAdditionalDrops, i )
+ {
+ auto& additionalDrop = vecAdditionalDrops[ i ];
+ if ( !additionalDrop.m_dropPeriod.IsValidForTime( CRTime::RTime32TimeCur() ) )
+ continue;
+
+ auto pSubLootlist = GetItemSchema()->GetLootListByName( additionalDrop.m_pszLootListDefName );
+ auto& subSource = lootlistSource.m_vecLootlistSources[ lootlistSource.m_vecLootlistSources.AddToTail() ];
+ lootlistSource.m_flChance += ChanceForItemFromLootlist( pItemDef, pSubLootlist, subSource ) * additionalDrop.m_fChance;
+ }
+
+ // Return the total chance
+ return lootlistSource.m_flChance;
+ }
+
+ CUtlVector< DropSource_t > m_vecSources;
+ const CEconItemDefinition* m_pItemDef;
+};
+
+GC_CON_COMMAND( item_sources, "Lists the sources for obtaining a list of specific items" )
+{
+ if ( !BCheckArgs( 1, args, item_sources_command ) )
+ return;
+
+ for ( int i=1; i < args.ArgC(); ++i )
+ {
+ const char* pszItemName = args[i];
+ const CEconItemDefinition* pItemDef = GetItemSchema()->GetItemDefinitionByName( pszItemName );
+ if ( pItemDef )
+ {
+ EmitInfo( SPEW_CONSOLE, SPEW_ALWAYS, LOG_ALWAYS, "\nChecking items for lootlists containing (%d) %s...\n", pItemDef->GetDefinitionIndex(), pItemDef->GetDefinitionName() );
+ CItemSourceFinder source( pszItemName );
+ source.PrintSources();
+ }
+ else
+ {
+ EmitInfo( SPEW_CONSOLE, SPEW_ALWAYS, LOG_ALWAYS, "\n\"%s\" is not a valid item name\n", pszItemName );
+ }
+ }
+}
+
+GC_CON_COMMAND( list_keys, "Lists all the keys in the schema" )
+{
+ EmitInfo( SPEW_CONSOLE, SPEW_ALWAYS, LOG_ALWAYS, "Spewing keys...\n" );
+
+ auto& mapItemDefs = GetItemSchema()->GetItemDefinitionMap();
+ FOR_EACH_MAP_FAST( mapItemDefs, i )
+ {
+ const CEconItemDefinition* pItemDef = mapItemDefs[ i ];
+ if ( !pItemDef || !pItemDef->GetEconTool() || ( Q_strcmp( pItemDef->GetEconTool()->GetTypeName(), "decoder_ring" ) != 0 ) )
+ {
+ continue;
+ }
+
+ EmitInfo( SPEW_CONSOLE, SPEW_ALWAYS, LOG_ALWAYS, "%llu - %s\n", pItemDef->GetDefinitionIndex(), pItemDef->GetItemDefinitionName() );
+ }
+}
+
+ConVar case_behavior_rolls_per_lootlist( "case_behavior_rolls_per_lootlist", "1000000", FCVAR_REPLICATED, "How many times to roll a lootlist" );
+class CJobCaseBehaviorCheck : public CGCGameBaseJob
+{
+public:
+ CJobCaseBehaviorCheck()
+ : CGCGameBaseJob( GGCGameBase() )
+ {}
+
+ virtual bool BYieldingRunGCJob()
+ {
+ // Wait until logon surge ends to get going
+ while ( GGCGameBase()->BIsInLogonSurge() )
+ {
+ GJobCur().BYieldingWaitOneFrame();
+ }
+
+ CRollSimulator simulator;
+
+ // Convert the hash to something readable
+ char pchSHAHex[41];
+ memset( pchSHAHex, 0, sizeof( pchSHAHex ) );
+ V_binarytohex( GetItemSchema()->GetSchemaSHA().m_shaDigest, 20, pchSHAHex, 41 );
+
+ RTime32 now = CRTime::RTime32TimeCur();
+ int nNewRecords = 0;
+
+ EmitInfo( SPEW_CONSOLE, SPEW_ALWAYS, LOG_ALWAYS, "Beginning CJobCaseBehaviorCheck\n" );
+
+ auto& lootlists = GetItemSchema()->GetRevolvingLootLists();
+
+ // Go through all of the lootlists
+ FOR_EACH_MAP( lootlists, i )
+ {
+ const CEconLootListDefinition* pEconLootlist = GetItemSchema()->GetLootListByName( lootlists[ i ] );
+ if ( pEconLootlist )
+ {
+ // Check if we have data for this lootlist on this hash already
+ {
+ CSQLAccess sqlReadAccess;
+ CUtlVector< CSchCaseBehavior > vecExistingResult;
+ sqlReadAccess.AddBindParam( pchSHAHex );
+ sqlReadAccess.AddBindParam( i );
+ if ( sqlReadAccess.BYieldingReadRecordsWithWhereClause( &vecExistingResult, "SchemaSHA = ? and Series = ?", CSET_FULL( CSchCaseBehavior ) ) )
+ {
+ // Already got it? Skip the work
+ if ( vecExistingResult.Count() )
+ {
+ continue;
+ }
+ }
+ }
+
+ // Roll 'em up
+ simulator.RollLootlist( pEconLootlist->GetName(), case_behavior_rolls_per_lootlist.GetInt(), true );
+
+ // Insert record
+ CSchCaseBehavior behavior;
+ behavior.SetVarCharField( behavior.m_VarCharSchemaSHA, pchSHAHex, true, CSchCaseBehavior::k_iField_VarCharSchemaSHA );
+ behavior.m_RTime32Date = now;
+ behavior.m_unSeries = i;
+ behavior.SetVarCharField( behavior.m_VarCharLootListName, pEconLootlist->GetName(), true, CSchCaseBehavior::k_iField_VarCharLootListName );
+ behavior.m_fStrangeChance = 100.f * (float)simulator.GetTotalDrops().m_nStrangeCount / simulator.GetTotalDrops().m_nRollCount;
+ behavior.m_fUnusualChance = 100.f * (float)simulator.GetTotalDrops().m_nUnusualCount / simulator.GetTotalDrops().m_nRollCount;
+
+ CSQLAccess sqlAccess;
+ sqlAccess.BBeginTransaction( "CJobCaseBehaviorCheck" );
+ sqlAccess.BYieldingInsertOrUpdateOnPK( &behavior );
+ sqlAccess.BCommitTransaction( true );
+ ++nNewRecords;
+ }
+
+ BYieldIfNeeded();
+ }
+
+ EmitInfo( SPEW_CONSOLE, SPEW_ALWAYS, LOG_ALWAYS, "Completed CJobUnusualChecker. %d updated records.\n", nNewRecords );
+
+ return true;
+ }
+};
+
+void CEconItemSchema::PerformCaseBehaviorCheck()
+{
+ CJob* pJob = new CJobCaseBehaviorCheck();
+ pJob->StartJobDelayed( NULL );
+}
+#endif
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CEconLootListDefinition::RollRandomItemsAndAdditionalItems( IUniformRandomStream *pRandomStream, bool bFreeAccount, CUtlVector<rolled_item_defs_t> *out_pVecRolledItems, const CUtlVector< item_definition_index_t > *pVecAvoidItemDefs ) const
+{
+ Assert( out_pVecRolledItems );
+
+ // Roll to see what items we get from this loot list.
+ bool bCreatedItems = RollRandomItemDef( pRandomStream, bFreeAccount, out_pVecRolledItems, pVecAvoidItemDefs );
+
+ // Do we have additional drops?
+ FOR_EACH_VEC( m_AdditionalDrops, i )
+ {
+ if ( !bCreatedItems )
+ break;
+
+ // Is this within the period this is allowed to drop?
+ if ( !m_AdditionalDrops[i].m_dropPeriod.IsValidForTime( CRTime::RTime32TimeCur() ) )
+ continue;
+
+ // Does this only apply to premium accounts?
+ if ( m_AdditionalDrops[i].m_bPremiumOnly && bFreeAccount )
+ continue;
+
+ // Does this only apply on certain holidays?
+ if ( m_AdditionalDrops[i].m_iRequiredHolidayIndex != kHoliday_None && !EconHolidays_IsHolidayActive( m_AdditionalDrops[i].m_iRequiredHolidayIndex, CRTime::RTime32TimeCur() ) )
+ continue;
+
+ // Random chance is in the range 0-1 so generate a value in that range to "roll".
+ if ( pRandomStream->RandomFloat( 0.0f, 1.0f ) > m_AdditionalDrops[i].m_fChance )
+ continue;
+
+ // Roll!
+ const char *pszAdditionalDropLootList = m_AdditionalDrops[i].m_pszLootListDefName;
+ const CEconLootListDefinition *pLootListDef = GetItemSchema()->GetLootListByName( pszAdditionalDropLootList );
+ if ( pLootListDef == NULL )
+ {
+ AssertMsg2( false, "Loot list '%s' specifies unknown additional drop '%s'", GetName(), pszAdditionalDropLootList );
+ return false;
+ }
+
+ bCreatedItems &= pLootListDef->RollRandomItemsAndAdditionalItems( pRandomStream, bFreeAccount, out_pVecRolledItems );
+ }
+
+ // If we failed to create some items, we might still have chosen some item defs before those failures. In that case, clear
+ // out any choices we've made so far
+ if ( !bCreatedItems )
+ {
+ out_pVecRolledItems->Purge();
+ }
+
+ Assert( bCreatedItems == (out_pVecRolledItems->Count() > 0) );
+
+ return bCreatedItems;
+}
+
+
+bool CEconLootListDefinition::RollRandomItemDef( IUniformRandomStream *pRandomStream, bool bFreeAccount, CUtlVector<rolled_item_defs_t> *out_pVecRolledItems, const CUtlVector< item_definition_index_t > *pVecAvoidItemDefs ) const
+{
+ Assert( out_pVecRolledItems );
+
+ CUtlVector< rolled_item_defs_t > vecScratchDefs;
+ CUtlVector<const drop_item_t*> vecValidDrops;
+
+ // Gather the items in this lootlist that we're able to roll for at this time
+ float flTotalWeight = 0.f;
+ FOR_EACH_VEC( m_DropList, i )
+ {
+ if( !m_DropList[i].m_dropPeriod.IsValidForTime( CRTime::RTime32TimeCur() ) )
+ continue;
+
+ // Skip any item defs that are in our avoid list (if we have one)
+ if ( pVecAvoidItemDefs )
+ {
+ item_definition_index_t defIndex = m_DropList[i].m_iItemOrLootlistDef;
+ if ( pVecAvoidItemDefs->Find( defIndex ) != pVecAvoidItemDefs->InvalidIndex() )
+ continue;
+ }
+
+ // If this is valid, add it to the list and add its weight to the total
+ vecValidDrops.AddToTail( &m_DropList[i] );
+ flTotalWeight += m_DropList[i].m_flWeight;
+ }
+
+ // Roll to see what item drops.
+ float flRand = pRandomStream->RandomFloat(0.0f, 1.0f) * flTotalWeight;
+
+ float flAccum = 0.0f;
+ FOR_EACH_VEC( vecValidDrops, i )
+ {
+ flAccum += vecValidDrops[i]->m_flWeight;
+ if ( flRand <= flAccum )
+ {
+ const int iItemDef = vecValidDrops[i]->m_iItemOrLootlistDef; // not item_definition_index because it might also be a negative value to indicate a sub lootlist
+
+ if ( iItemDef >= 0 )
+ {
+ const CEconItemDefinition* pItemDef = GetItemSchema()->GetItemDefinition( iItemDef );
+ if( !pItemDef )
+ return false;
+
+ // Add the item def and the lootlist
+ rolled_item_defs_t& rolledDef = vecScratchDefs[ vecScratchDefs.AddToTail() ];
+ rolledDef.m_pItemDef = pItemDef;
+ }
+ else
+ {
+ // In the case where iItemDef is negative, it's a nested loot list. Ask that list to choose an item.
+ // HACKY: Store loot list indices as negatives, starting from -1, because 0 is a valid item index
+ int iLLIndex = (iItemDef * -1) - 1;
+ const CEconLootListDefinition *pNestedLootList = GetItemSchema()->GetLootListByIndex( iLLIndex );
+ if ( !pNestedLootList )
+ return false;
+
+ if( !pNestedLootList->RollRandomItemsAndAdditionalItems( pRandomStream, bFreeAccount, &vecScratchDefs, pVecAvoidItemDefs ) )
+ return false;
+ }
+
+ // Add ourselves to the list of affecting lootlists, so that we and all nested loot lists will affect this item
+ // We intentionally don't do this in our calling function because we don't want to include additional drops.
+ FOR_EACH_VEC( vecScratchDefs, j )
+ {
+ vecScratchDefs[j].m_vecAffectingLootLists.AddToTail( this );
+ }
+
+ // We want to exit the loop here regardless of whether items were successfully so that we only perform a single
+ // item-generating roll on this list.
+ break;
+ }
+ }
+
+ // Feed item defs, if they exist, back to our caller
+ out_pVecRolledItems->AddVectorToTail( vecScratchDefs );
+
+ // Did we successfully create any items?
+ return ( vecScratchDefs.Count() > 0 );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: find a list of lootlists with rarity from this lootlist
+//-----------------------------------------------------------------------------
+void CEconLootListDefinition::GetRarityLootLists( CUtlVector< const CEconLootListDefinition* > *out_pVecRarityLootList ) const
+{
+ Assert( out_pVecRarityLootList );
+ if ( m_unRarity != k_unItemRarity_Any )
+ {
+ out_pVecRarityLootList->AddToTail( this );
+ }
+
+ FOR_EACH_VEC( m_DropList, i )
+ {
+ const int iItemDef = m_DropList[i].m_iItemOrLootlistDef; // not item_definition_index because it might also be a negative value to indicate a sub lootlist
+ if ( iItemDef < 0 )
+ {
+ // In the case where iItemDef is negative, it's a nested loot list. Ask that list to choose an item.
+ // HACKY: Store loot list indices as negatives, starting from -1, because 0 is a valid item index
+ int iLLIndex = (iItemDef * -1) - 1;
+ const CEconLootListDefinition *pNestedLootList = GetItemSchema()->GetLootListByIndex( iLLIndex );
+ if ( !pNestedLootList )
+ return;
+
+ pNestedLootList->GetRarityLootLists( out_pVecRarityLootList );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: get all item defs from this lootlist ( not lootlist item def )
+//-----------------------------------------------------------------------------
+void CEconLootListDefinition::GetItemDefs( CUtlVector< item_definition_index_t > *out_pVecItemDefs ) const
+{
+ Assert( out_pVecItemDefs );
+
+ FOR_EACH_VEC( m_DropList, i )
+ {
+ const int iItemDef = m_DropList[i].m_iItemOrLootlistDef; // not item_definition_index because it might also be a negative value to indicate a sub lootlist
+ if ( iItemDef >= 0 )
+ {
+ out_pVecItemDefs->AddToTail( (item_definition_index_t)iItemDef );
+ }
+ else
+ {
+ // In the case where iItemDef is negative, it's a nested loot list. Ask that list to choose an item.
+ // HACKY: Store loot list indices as negatives, starting from -1, because 0 is a valid item index
+ int iLLIndex = (iItemDef * -1) - 1;
+ const CEconLootListDefinition *pNestedLootList = GetItemSchema()->GetLootListByIndex( iLLIndex );
+ if ( !pNestedLootList )
+ return;
+
+ pNestedLootList->GetItemDefs( out_pVecItemDefs );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Given a vector of possible attributes, roll to see which ones are
+// chosen. We allocate memory for these new attributes, so it's the
+// responsibility of the caller to free these attributes when they're
+// done with them!
+//-----------------------------------------------------------------------------
+void CEconLootListDefinition::RollRandomAttributes( CUtlVector< static_attrib_t >& vecAttributes, const CEconGameAccount *pGameAccount ) const
+{
+ for ( int i=0; i<m_RandomAttribs.Count(); ++i )
+ {
+ const random_attrib_t* rattr = m_RandomAttribs[i];
+ rattr->RollRandomAttributes( vecAttributes, pGameAccount );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CEconLootListDefinition::drop_period_t::IsValidForTime( const RTime32& time ) const
+{
+ if( time >= m_DropStartDate && time < m_DropEndDate )
+ return true;
+
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CEconLootListDefinition::BAttachLootListAttributes( const CEconGameAccount *pGameAccount, CEconItem *pItem ) const
+{
+ //static CSchemaAttributeDefHandle pAttr_ElevateQuality( "elevate quality" );
+
+ // Gather and apply old-style random attributes.
+ CUtlVector< static_attrib_t > vecAttributes;
+ RollRandomAttributes( vecAttributes, pGameAccount );
+
+ FOR_EACH_VEC( vecAttributes, i )
+ {
+ GEconManager()->GetItemFactory().ApplyStaticAttributeToItem( pItem, vecAttributes[i], pGameAccount );
+ vecAttributes[i].GetAttributeDefinition()->GetAttributeType()->UnloadEconAttributeValue( &vecAttributes[i].m_value );
+ }
+
+ // Apply all relevant property generators.
+ for ( auto pGenerator : m_PropertyGenerators )
+ {
+ if ( !pGenerator->BGenerateProperties( pItem ) )
+ return false;
+ }
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool lootlist_attrib_t::BInitFromKV( const char *pszContext, KeyValues *pKVKey, CEconItemSchema &pschema, CUtlVector<CUtlString> *pVecErrors )
+{
+ SCHEMA_INIT_SUBSTEP( m_staticAttrib.BInitFromKV_MultiLine( pszContext, pKVKey, pVecErrors ) );
+
+ SCHEMA_INIT_CHECK(
+ pKVKey->FindKey( "weight" ),
+ "Context '%s': Attribute \"%s\" missing required 'weight' field", pszContext, pKVKey->GetName() );
+
+ m_flWeight = pKVKey->GetFloat( "weight" );
+
+ return SCHEMA_INIT_SUCCESS();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: returns true if we should stop rolling from this random_attrib_t
+//-----------------------------------------------------------------------------
+bool random_attrib_t::RollRandomAttributes( CUtlVector< static_attrib_t >& vecAttributes, const CEconGameAccount *pGameAccount ) const
+{
+ if ( m_flChanceOfRandomAttribute && RandomFloat() <= m_flChanceOfRandomAttribute )
+ {
+ // We're attaching a random attribute. Determine which attribute.
+ float flRand = 0.0f;
+ if ( !m_bPickAllAttributes )
+ {
+ // Pick one attribute to add
+ // Otherwise we'll pick them all
+ flRand = RandomFloat( 0.f, 1.f ) * m_flTotalAttributeWeight;
+ }
+
+ float flAccum = 0.f;
+ for ( int iAttrib = 0; iAttrib < m_RandomAttributes.Count(); ++iAttrib )
+ {
+ const lootlist_attrib_t& randomAttrib = m_RandomAttributes[iAttrib];
+
+ flAccum += randomAttrib.m_flWeight;
+ if ( flRand <= flAccum )
+ {
+ // Add the attribute
+ static_attrib_t &staticAttrib = vecAttributes[ vecAttributes.AddToTail( randomAttrib.m_staticAttrib ) ];
+ const CEconItemAttributeDefinition *pAttrDef = staticAttrib.GetAttributeDefinition();
+ const ISchemaAttributeType *pAttrType = pAttrDef->GetAttributeType();
+ // Generate a special value?
+ pAttrType->InitializeNewEconAttributeValue( &staticAttrib.m_value );
+ pAttrDef->GetAttributeType()->GenerateEconAttributeValue( pAttrDef, staticAttrib, pGameAccount, &staticAttrib.m_value );
+
+ if ( !m_bPickAllAttributes )
+ {
+ // We're only picking one attribute from the list
+ return true;
+ }
+ }
+ }
+ }
+
+ return false;
+}
+
+#endif // GC_DLL
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CEconLootListDefinition::EnumerateUserFacingPotentialDrops( IEconLootListIterator *pIt ) const
+{
+ Assert( pIt );
+
+ // Loot lists have the option of specifying that their contents should not be publicly
+ // listed. This is used on the GC for things like the "rare item drop list" to prevent
+ // every single potentially-unusual hat from showing up.
+ if ( !BPublicListContents() )
+ return;
+
+ FOR_EACH_VEC( GetLootListContents(), i )
+ {
+ const int iID = GetLootListContents()[i].m_iItemOrLootlistDef;
+
+ // Nested loot lists are stored as negative indices.
+ if ( iID < 0 )
+ {
+ const CEconLootListDefinition *pLootListDef = GetItemSchema()->GetLootListByIndex( (-iID) - 1 );
+ if ( !pLootListDef )
+ continue;
+
+ pLootListDef->EnumerateUserFacingPotentialDrops( pIt );
+ }
+ else
+ {
+ pIt->OnIterate( iID );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+/*static*/ CSchemaAttributeDefHandle CAttributeLineItemLootList::s_pAttrDef_RandomDropLineItems[] =
+{
+ CSchemaAttributeDefHandle( "random drop line item 0" ),
+ CSchemaAttributeDefHandle( "random drop line item 1" ),
+ CSchemaAttributeDefHandle( "random drop line item 2" ),
+ CSchemaAttributeDefHandle( "random drop line item 3" ),
+};
+
+#ifdef GC_DLL
+/*static*/ CSchemaAttributeDefHandle CAttributeLineItemLootList::s_pAttrDef_RandomDropLineItemUnusualChance( "random drop line item unusual chance" ); // "one out of this many"
+/*static*/ CSchemaAttributeDefHandle CAttributeLineItemLootList::s_pAttrDef_RandomDropLineItemUnusualList( "random drop line item unusual list" );
+#endif // GC_DLL
+CSchemaAttributeDefHandle CAttributeLineItemLootList::s_pAttrDef_RandomDropLineItemFooterDesc( "random drop line item footer desc" );
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CAttributeLineItemLootList::EnumerateUserFacingPotentialDrops( IEconLootListIterator *pIt ) const
+{
+ Assert( pIt );
+
+ for ( int i = 0; i < ARRAYSIZE( s_pAttrDef_RandomDropLineItems ); i++ )
+ {
+ uint32 unItemDef;
+ COMPILE_TIME_ASSERT( sizeof( unItemDef ) >= sizeof( item_definition_index_t ) );
+
+ // If we run out of attributes we have set we're done.
+ if ( !m_pEconItem->FindAttribute( s_pAttrDef_RandomDropLineItems[i], &unItemDef ) )
+ break;
+
+ pIt->OnIterate( unItemDef );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+const char *CAttributeLineItemLootList::GetLootListHeaderLocalizationKey() const
+{
+ return g_pszDefaultRevolvingLootListHeader;
+}
+
+//-----------------------------------------------------------------------------
+const char *CAttributeLineItemLootList::GetLootListFooterLocalizationKey() const
+{
+ CAttribute_String sFooter;
+ const char* pszFooter = NULL;
+ if ( FindAttribute_UnsafeBitwiseCast<CAttribute_String>( m_pEconItem, s_pAttrDef_RandomDropLineItemFooterDesc, &pszFooter ) )
+ {
+ return pszFooter;
+ }
+ return NULL;
+}
+//-----------------------------------------------------------------------------
+const char *CAttributeLineItemLootList::GetLootListCollectionReference() const
+{
+ // TODO : Implement me!
+ return NULL;
+}
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+const CEconLootListDefinition* CEconItemSchema::GetLootListByName( const char* pListName, int *out_piIndex ) const
+{
+ auto idx = m_mapLootLists.Find( pListName );
+ if ( !m_mapLootLists.IsValidIndex( idx ) )
+ return NULL;
+
+ if ( out_piIndex )
+ {
+ *out_piIndex = idx;
+ }
+
+ return m_mapLootLists[idx];
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+const CQuestObjectiveDefinition* CEconItemSchema::GetQuestObjectiveByDefIndex( int iIdx ) const
+{
+ auto nMapIndex = m_mapQuestObjectives.Find( iIdx );
+ if ( nMapIndex != m_mapQuestObjectives.InvalidIndex() )
+ {
+ return m_mapQuestObjectives[ nMapIndex ];
+ }
+
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor
+//-----------------------------------------------------------------------------
+CEconCraftingRecipeDefinition::CEconCraftingRecipeDefinition( void )
+ : m_nDefIndex( 0 )
+#ifdef GC_DLL
+ , m_bIsCraftableByUnverifiedClient( false )
+#endif // GC_DLL
+{
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Initialize the attribute definition
+// Input: pKVAttribute - The KeyValues representation of the attribute
+// schema - The overall item schema for this attribute
+// pVecErrors - An optional vector that will contain error messages if
+// the init fails.
+// Output: True if initialization succeeded, false otherwise
+//-----------------------------------------------------------------------------
+bool CEconCraftingRecipeDefinition::BInitFromKV( KeyValues *pKVRecipe, CUtlVector<CUtlString> *pVecErrors /* = NULL */ )
+{
+ m_nDefIndex = Q_atoi( pKVRecipe->GetName() );
+
+ // Check for required fields
+ SCHEMA_INIT_CHECK(
+ NULL != pKVRecipe->FindKey( "input_items" ),
+ "Recipe definition %d: Missing required field \"input_items\"", m_nDefIndex );
+
+ SCHEMA_INIT_CHECK(
+ NULL != pKVRecipe->FindKey( "output_items" ),
+ "Recipe definition %d: Missing required field \"output_items\"", m_nDefIndex );
+
+ m_bDisabled = pKVRecipe->GetBool( "disabled" );
+ m_strName = pKVRecipe->GetString( "name" );
+ m_strN_A = pKVRecipe->GetString( "n_A" );
+ m_strDescInputs = pKVRecipe->GetString( "desc_inputs" );
+ m_strDescOutputs = pKVRecipe->GetString( "desc_outputs" );
+ m_strDI_A = pKVRecipe->GetString( "di_A" );
+ m_strDI_B = pKVRecipe->GetString( "di_B" );
+ m_strDI_C = pKVRecipe->GetString( "di_C" );
+ m_strDO_A = pKVRecipe->GetString( "do_A" );
+ m_strDO_B = pKVRecipe->GetString( "do_B" );
+ m_strDO_C = pKVRecipe->GetString( "do_C" );
+
+#ifdef GC_DLL
+ m_bIsCraftableByUnverifiedClient = pKVRecipe->GetBool( "is_craftable_by_unverified_clients", false );
+#endif // GC_DLL
+ m_bRequiresAllSameClass = pKVRecipe->GetBool( "all_same_class" );
+ m_bRequiresAllSameSlot = pKVRecipe->GetBool( "all_same_slot" );
+ m_iCacheClassUsageForOutputFromItem = pKVRecipe->GetInt( "add_class_usage_to_output", -1 );
+ m_iCacheSlotUsageForOutputFromItem = pKVRecipe->GetInt( "add_slot_usage_to_output", -1 );
+ m_iCacheSetForOutputFromItem = pKVRecipe->GetInt( "add_set_to_output", -1 );
+ m_bPremiumAccountOnly = pKVRecipe->GetBool( "premium_only", false );
+ m_iCategory = (recipecategories_t)StringFieldToInt( pKVRecipe->GetString("category"), g_szRecipeCategoryStrings, ARRAYSIZE(g_szRecipeCategoryStrings) );
+
+ // Read in all the input items
+ KeyValues *pKVInputItems = pKVRecipe->FindKey( "input_items" );
+ if ( NULL != pKVInputItems )
+ {
+ FOR_EACH_TRUE_SUBKEY( pKVInputItems, pKVInputItem )
+ {
+ int index = m_InputItemsCriteria.AddToTail();
+ SCHEMA_INIT_SUBSTEP( m_InputItemsCriteria[index].BInitFromKV( pKVInputItem ) );
+
+ // Recipes ignore the enabled flag when generating items
+ m_InputItemsCriteria[index].SetIgnoreEnabledFlag( true );
+
+ index = m_InputItemDupeCounts.AddToTail();
+ m_InputItemDupeCounts[index] = atoi( pKVInputItem->GetName() );
+ }
+ }
+
+ // Read in all the output items
+ KeyValues *pKVOutputItems = pKVRecipe->FindKey( "output_items" );
+ if ( NULL != pKVOutputItems )
+ {
+ FOR_EACH_TRUE_SUBKEY( pKVOutputItems, pKVOutputItem )
+ {
+ int index = m_OutputItemsCriteria.AddToTail();
+ SCHEMA_INIT_SUBSTEP( m_OutputItemsCriteria[index].BInitFromKV( pKVOutputItem ) );
+
+ // Recipes ignore the enabled flag when generating items
+ m_OutputItemsCriteria[index].SetIgnoreEnabledFlag( true );
+ }
+ }
+
+ return SCHEMA_INIT_SUCCESS();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Serializes the criteria to and from messages
+//-----------------------------------------------------------------------------
+bool CEconCraftingRecipeDefinition::BSerializeToMsg( CSOItemRecipe & msg ) const
+{
+ msg.set_def_index( m_nDefIndex );
+ msg.set_name( m_strName );
+ msg.set_n_a( m_strN_A );
+ msg.set_desc_inputs( m_strDescInputs );
+ msg.set_desc_outputs( m_strDescOutputs );
+ msg.set_di_a( m_strDI_A );
+ msg.set_di_b( m_strDI_B );
+ msg.set_di_c( m_strDI_C );
+ msg.set_do_a( m_strDO_A );
+ msg.set_do_b( m_strDO_B );
+ msg.set_do_c( m_strDO_C );
+ msg.set_requires_all_same_class( m_bRequiresAllSameClass );
+ msg.set_requires_all_same_slot( m_bRequiresAllSameSlot );
+ msg.set_class_usage_for_output( m_iCacheClassUsageForOutputFromItem );
+ msg.set_slot_usage_for_output( m_iCacheSlotUsageForOutputFromItem );
+ msg.set_set_for_output( m_iCacheSetForOutputFromItem );
+
+ FOR_EACH_VEC( m_InputItemsCriteria, i )
+ {
+ CSOItemCriteria *pCrit = msg.add_input_items_criteria();
+ if ( !m_InputItemsCriteria[i].BSerializeToMsg( *pCrit ) )
+ return false;
+ }
+
+ FOR_EACH_VEC( m_InputItemDupeCounts, i )
+ {
+ msg.add_input_item_dupe_counts( m_InputItemDupeCounts[i] );
+ }
+
+ FOR_EACH_VEC( m_OutputItemsCriteria, i )
+ {
+ CSOItemCriteria *pCrit = msg.add_output_items_criteria();
+ if ( !m_OutputItemsCriteria[i].BSerializeToMsg( *pCrit ) )
+ return false;
+ }
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Serializes the criteria to and from messages
+//-----------------------------------------------------------------------------
+bool CEconCraftingRecipeDefinition::BDeserializeFromMsg( const CSOItemRecipe & msg )
+{
+ m_nDefIndex = msg.def_index();
+ m_strName = msg.name().c_str();
+ m_strN_A = msg.n_a().c_str();
+ m_strDescInputs = msg.desc_inputs().c_str();
+ m_strDescOutputs = msg.desc_outputs().c_str();
+ m_strDI_A = msg.di_a().c_str();
+ m_strDI_B = msg.di_b().c_str();
+ m_strDI_C = msg.di_c().c_str();
+ m_strDO_A = msg.do_a().c_str();
+ m_strDO_B = msg.do_b().c_str();
+ m_strDO_C = msg.do_c().c_str();
+
+ m_bRequiresAllSameClass = msg.requires_all_same_class();
+ m_bRequiresAllSameSlot = msg.requires_all_same_slot();
+ m_iCacheClassUsageForOutputFromItem = msg.class_usage_for_output();
+ m_iCacheSlotUsageForOutputFromItem = msg.slot_usage_for_output();
+ m_iCacheSetForOutputFromItem = msg.set_for_output();
+
+ // Read how many input items there are
+ uint32 unCount = msg.input_items_criteria_size();
+ m_InputItemsCriteria.SetSize( unCount );
+ for ( uint32 i = 0; i < unCount; i++ )
+ {
+ if ( !m_InputItemsCriteria[i].BDeserializeFromMsg( msg.input_items_criteria( i ) ) )
+ return false;
+ }
+
+ // Read how many input item dupe counts there are
+ unCount = msg.input_item_dupe_counts_size();
+ m_InputItemDupeCounts.SetSize( unCount );
+ for ( uint32 i = 0; i < unCount; i++ )
+ {
+ m_InputItemDupeCounts[i] = msg.input_item_dupe_counts( i );
+ }
+
+ // Read how many output items there are
+ unCount = msg.output_items_criteria_size();
+ m_OutputItemsCriteria.SetSize( unCount );
+ for ( uint32 i = 0; i < unCount; i++ )
+ {
+ if ( !m_OutputItemsCriteria[i].BDeserializeFromMsg( msg.output_items_criteria( i ) ) )
+ return false;
+ }
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Returns true if the vector contains a set of items that matches the inputs for this recipe
+// Note it will fail if the vector contains extra items that aren't needed.
+//
+//-----------------------------------------------------------------------------
+bool CEconCraftingRecipeDefinition::ItemListMatchesInputs( CUtlVector<CEconItem*> *vecCraftingItems, KeyValues *out_pkvCraftParams, bool bIgnoreSlop, CUtlVector<uint64> *vecChosenItems ) const
+{
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor
+//-----------------------------------------------------------------------------
+int CEconCraftingRecipeDefinition::GetTotalInputItemsRequired( void ) const
+{
+ int iCount = 0;
+ FOR_EACH_VEC( m_InputItemsCriteria, i )
+ {
+ if ( m_InputItemDupeCounts[i] )
+ {
+ iCount += m_InputItemDupeCounts[i];
+ }
+ else
+ {
+ iCount++;
+ }
+ }
+ return iCount;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+#ifdef GC_DLL
+ #define GC_SCH_REFERENCE( TAttribSchType ) \
+ TAttribSchType,
+#else
+ #define GC_SCH_REFERENCE( TAttribSchType )
+#endif
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+unsigned int Internal_GetAttributeTypeUniqueIdentifierNextValue()
+{
+ static unsigned int s_unUniqueCounter = 0;
+
+ unsigned int unCounter = s_unUniqueCounter;
+ s_unUniqueCounter++;
+ return unCounter;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+#ifdef GC_DLL
+template < typename TAttribSchType, typename TRecordBaseType >
+static TAttribSchType *GetTypedSch( TRecordBaseType *pRecordBase )
+{
+ Assert( pRecordBase->GetITable() == TAttribSchType::k_iTable );
+
+#if ENABLE_TYPED_ATTRIBUTE_PARANOIA
+ TAttribSchType *pTypedSch = dynamic_cast<TAttribSchType *>( pRecordBase );
+ Assert( pTypedSch );
+ return pTypedSch;
+#else
+ return static_cast<TAttribSchType *>( pRecordBase );
+#endif
+}
+#endif // GC_DLL
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+template < GC_SCH_REFERENCE( typename TAttribSchType ) typename TAttribInMemoryType >
+class CSchemaAttributeTypeBase : public ISchemaAttributeTypeBase<TAttribInMemoryType>
+{
+public:
+#ifdef GC_DLL
+ virtual CColumnSet& GetFullColumnSet() const OVERRIDE
+ {
+ static CColumnSet sFullColumnSet( CColumnSet::Full<TAttribSchType>() );
+
+ return sFullColumnSet;
+ }
+
+ virtual CRecordBase *CreateTypedSchRecord() const OVERRIDE
+ {
+ return new TAttribSchType;
+ }
+#endif // GC_DLL
+};
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+template < GC_SCH_REFERENCE( typename TAttribSchType ) typename TProtobufValueType >
+class CSchemaAttributeTypeProtobufBase : public CSchemaAttributeTypeBase< GC_SCH_REFERENCE( TAttribSchType ) TProtobufValueType >
+{
+public:
+ virtual void ConvertTypedValueToByteStream( const TProtobufValueType& typedValue, ::std::string *out_psBytes ) const OVERRIDE
+ {
+ DbgVerify( typedValue.SerializeToString( out_psBytes ) );
+ }
+
+ virtual void ConvertByteStreamToTypedValue( const ::std::string& sBytes, TProtobufValueType *out_pTypedValue ) const OVERRIDE
+ {
+ DbgVerify( out_pTypedValue->ParseFromString( sBytes ) );
+ }
+
+ virtual bool BConvertStringToEconAttributeValue( const CEconItemAttributeDefinition *pAttrDef, const char *pszValue, union attribute_data_union_t *out_pValue, bool bEnableTerribleBackwardsCompatibilitySchemaParsingCode ) const OVERRIDE
+ {
+ Assert( pAttrDef );
+ Assert( out_pValue );
+
+ std::string sValue( pszValue );
+ TProtobufValueType typedValue;
+ if ( !google::protobuf::TextFormat::ParseFromString( sValue, &typedValue ) )
+ return false;
+
+ this->ConvertTypedValueToEconAttributeValue( typedValue, out_pValue );
+ return true;
+ }
+
+ virtual void ConvertEconAttributeValueToString( const CEconItemAttributeDefinition *pAttrDef, const attribute_data_union_t& value, std::string *out_ps ) const OVERRIDE
+ {
+ Assert( pAttrDef );
+ Assert( out_ps );
+
+ google::protobuf::TextFormat::PrintToString( this->GetTypedValueContentsFromEconAttributeValue( value ), out_ps );
+ }
+};
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+class CSchemaAttributeType_String : public CSchemaAttributeTypeProtobufBase< GC_SCH_REFERENCE( CSchItemAttributeString ) CAttribute_String >
+{
+public:
+#ifdef GC_DLL
+ virtual void ConvertEconAttributeValueToSch( itemid_t unItemId, const CEconItemAttributeDefinition *pAttrDef, const attribute_data_union_t& value, GCSDK::CRecordBase *out_pSchRecord ) const OVERRIDE
+ {
+ Assert( out_pSchRecord );
+ Assert( pAttrDef );
+ Assert( pAttrDef->GetAttributeType() == this );
+
+ CSchItemAttributeString *out_psch = GetTypedSch<CSchItemAttributeString>( out_pSchRecord );
+
+ CAttribute_String typedValue;
+ this->ConvertEconAttributeValueToTypedValue( value, &typedValue );
+
+ // const CAttribute_String& typedValue = GetTypedValueContentsFromEconAttributeValue( value );
+
+ out_psch->m_ulItemID = unItemId;
+ out_psch->m_unAttrDefIndex = pAttrDef->GetDefinitionIndex();
+ WRITE_VAR_CHAR_FIELD( (*out_psch), VarCharAttrStrValue, typedValue.value().c_str() );
+ }
+
+ virtual void LoadSchToEconAttributeValue( CEconItem *pTargetItem, const CEconItemAttributeDefinition *pAttrDef, const GCSDK::CRecordBase *pSchRecord ) const OVERRIDE
+ {
+ Assert( pTargetItem );
+ Assert( pAttrDef );
+ Assert( pSchRecord );
+ Assert( pAttrDef->GetAttributeType() == this );
+
+ const CSchItemAttributeString *psch = GetTypedSch<const CSchItemAttributeString>( pSchRecord );
+
+ CAttribute_String typedValue;
+ typedValue.set_value( READ_VAR_CHAR_FIELD( (*psch), m_VarCharAttrStrValue ) );
+
+ pTargetItem->SetDynamicAttributeValue( pAttrDef, typedValue );
+ }
+#endif // GC_DLL
+
+ // We intentionally override the convert-to-/convert-from-string functions for strings so that string literals can be
+ // specified in the schema, etc. without worrying about the protobuf text format.
+ virtual bool BConvertStringToEconAttributeValue( const CEconItemAttributeDefinition *pAttrDef, const char *pszValue, union attribute_data_union_t *out_pValue, bool bEnableTerribleBackwardsCompatibilitySchemaParsingCode ) const OVERRIDE
+ {
+ Assert( pAttrDef );
+ Assert( out_pValue );
+
+ CAttribute_String typedValue;
+ typedValue.set_value( pszValue );
+
+ this->ConvertTypedValueToEconAttributeValue( typedValue, out_pValue );
+
+ return true;
+ }
+
+ virtual void ConvertEconAttributeValueToString( const CEconItemAttributeDefinition *pAttrDef, const attribute_data_union_t& value, std::string *out_ps ) const OVERRIDE
+ {
+ Assert( pAttrDef );
+ Assert( out_ps );
+
+ *out_ps = this->GetTypedValueContentsFromEconAttributeValue( value ).value().c_str();
+ }
+};
+
+void CopyStringAttributeValueToCharPointerOutput( const CAttribute_String *pValue, const char **out_pValue )
+{
+ Assert( pValue );
+ Assert( out_pValue );
+
+ *out_pValue = pValue->value().c_str();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+class CSchemaAttributeType_DynamicRecipeComponentDefinedItem : public CSchemaAttributeTypeProtobufBase< GC_SCH_REFERENCE( CSchItemAttributeDynamicRecipeComponentDefinedItem ) CAttribute_DynamicRecipeComponent >
+{
+public:
+#ifdef GC_DLL
+ virtual void ConvertEconAttributeValueToSch( itemid_t unItemId, const CEconItemAttributeDefinition *pAttrDef, const union attribute_data_union_t& value, GCSDK::CRecordBase *out_pSchRecord ) const OVERRIDE
+ {
+ Assert( out_pSchRecord );
+ Assert( pAttrDef );
+ Assert( pAttrDef->GetAttributeType() == this );
+
+ CSchItemAttributeDynamicRecipeComponentDefinedItem *out_psch = GetTypedSch<CSchItemAttributeDynamicRecipeComponentDefinedItem>( out_pSchRecord );
+
+ CAttribute_DynamicRecipeComponent typedValue;
+ ConvertEconAttributeValueToTypedValue( value, &typedValue );
+
+ out_psch->m_ulItemID = unItemId;
+ out_psch->m_unAttrDefIndex = pAttrDef->GetDefinitionIndex();
+ out_psch->m_unItemDef = typedValue.def_index();
+ out_psch->m_unItemQuality = typedValue.item_quality();
+ out_psch->m_unFlags = typedValue.component_flags();
+ out_psch->m_unItemCount = typedValue.num_required();
+ out_psch->m_unItemsFulfilled = typedValue.num_fulfilled();
+ WRITE_VAR_CHAR_FIELD( (*out_psch), VarCharAttrStr, typedValue.attributes_string().c_str() );
+ }
+
+ virtual void LoadSchToEconAttributeValue( CEconItem *pTargetItem, const CEconItemAttributeDefinition *pAttrDef, const GCSDK::CRecordBase *pSchRecord ) const OVERRIDE
+ {
+ Assert( pTargetItem );
+ Assert( pAttrDef );
+ Assert( pSchRecord );
+ Assert( pAttrDef->GetAttributeType() == this );
+
+ const CSchItemAttributeDynamicRecipeComponentDefinedItem *psch = GetTypedSch<const CSchItemAttributeDynamicRecipeComponentDefinedItem>( pSchRecord );
+
+ CAttribute_DynamicRecipeComponent typedValue;
+ typedValue.set_def_index( psch->m_unItemDef );
+ typedValue.set_item_quality( psch->m_unItemQuality );
+ typedValue.set_component_flags( psch->m_unFlags );
+ typedValue.set_attributes_string( READ_VAR_CHAR_FIELD( (*psch), m_VarCharAttrStr ) );
+ typedValue.set_num_required( psch->m_unItemCount );
+ typedValue.set_num_fulfilled( psch->m_unItemsFulfilled );
+
+ pTargetItem->SetDynamicAttributeValue( pAttrDef, typedValue );
+ }
+
+ virtual bool BConvertStringToEconAttributeValue( const CEconItemAttributeDefinition *pAttrDef, const char *pszValue, union attribute_data_union_t *out_pValue, bool bEnableTerribleBackwardsCompatibilitySchemaParsingCode ) const OVERRIDE
+ {
+ Assert( pAttrDef );
+ Assert( out_pValue );
+
+ std::string sValue( pszValue );
+ // What's happened here is we've renamed some fields within CAttribute_DynamicRecipeComponent,
+ // but steam contains the strings of the old format serialized, and keeps sending them to us.
+ // Rather than updating steam, we're going to made a protobuff class that can accept the new
+ // and old formats, and put the corret values into the correct members of the new format.
+
+
+ CAttribute_DynamicRecipeComponent_COMPAT_NEVER_SERIALIZE_THIS_OUT typedCompatValue;
+ CAttribute_DynamicRecipeComponent typedActualValue;
+
+#ifdef STAGING_ONLY
+ auto *pActualFields = typedActualValue.descriptor();
+ auto *pCompatFields = typedCompatValue.descriptor();
+ for ( int i=0; i < pActualFields->field_count(); ++i )
+ {
+ const bool bFoundField = pCompatFields->FindFieldByName( pActualFields->field( i )->name() ) != NULL;
+ Assert( bFoundField );
+ if ( !bFoundField )
+ {
+ EmitError( SPEW_GC, "Missing field '%s' in CAttribute_DynamicRecipeComponent_COMPAT_NEVER_SERIALIZE_THIS_OUT\n", pActualFields->field( i )->name() );
+ return false;
+ }
+ }
+#endif // STAGING_ONLY
+
+ if ( !google::protobuf::TextFormat::ParseFromString( sValue, &typedCompatValue ) )
+ {
+ EmitError( SPEW_GC, "Failed to parse recipe component into compatible protobuf\n" );
+ return false;
+ }
+
+ if ( typedCompatValue.has_component_flags() )
+ typedActualValue.set_component_flags( typedCompatValue.component_flags() );
+ else if ( typedCompatValue.has_item_flags() )
+ typedActualValue.set_component_flags( typedCompatValue.item_flags() );
+ else
+ {
+ EmitError( SPEW_GC, "Failed to parse component_flags. component_flags: %d, item_flags: %d\n", typedCompatValue.component_flags(), typedCompatValue.item_flags() );
+ return false;
+ }
+
+ if ( typedCompatValue.has_def_index() )
+ typedActualValue.set_def_index( typedCompatValue.def_index() );
+ else if ( typedCompatValue.has_item_def() )
+ typedActualValue.set_def_index( typedCompatValue.item_def() );
+ else if ( typedActualValue.component_flags() & DYNAMIC_RECIPE_FLAG_PARAM_ITEM_DEF_SET )
+ {
+ EmitError( SPEW_GC, "Failed to parse item_def. def_index: %d, item_def: %d\n", typedCompatValue.def_index(), typedCompatValue.item_def() );
+ return false;
+ }
+
+ typedActualValue.set_item_quality( typedCompatValue.item_quality() );
+
+
+
+ typedActualValue.set_attributes_string( typedCompatValue.attributes_string() );
+
+ if( typedCompatValue.has_num_required() )
+ typedActualValue.set_num_required( typedCompatValue.num_required() );
+ else if ( typedCompatValue.has_item_count() )
+ typedActualValue.set_num_required( typedCompatValue.item_count() );
+ else
+ {
+ EmitError( SPEW_GC, "Failed to parse component_flags. num_required: %d, item_count: %d\n", typedCompatValue.num_required(), typedCompatValue.item_count() );
+ return false;
+ }
+
+ if ( typedCompatValue.has_items_fulfilled() )
+ typedActualValue.set_num_fulfilled( typedCompatValue.items_fulfilled() );
+ else if ( typedCompatValue.has_num_fulfilled() )
+ typedActualValue.set_num_fulfilled( typedCompatValue.num_fulfilled() );
+ else
+ {
+ EmitError( SPEW_GC, "Failed to parse num_fulfilled. items_fulfilled: %d, num_fulfilled: %d\n", typedCompatValue.items_fulfilled(), typedCompatValue.num_fulfilled() );
+ return false;
+ }
+
+ this->ConvertTypedValueToEconAttributeValue( typedActualValue, out_pValue );
+ return true;
+ }
+
+#endif // GC_DLL
+};
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+class CSchemaAttributeType_ItemSlotCriteria : public CSchemaAttributeTypeProtobufBase< GC_SCH_REFERENCE( CSchItemAttributeItemSlotCriteria ) CAttribute_ItemSlotCriteria >
+{
+public:
+#ifdef GC_DLL
+ virtual void ConvertEconAttributeValueToSch( itemid_t unItemId, const CEconItemAttributeDefinition *pAttrDef, const union attribute_data_union_t& value, GCSDK::CRecordBase *out_pSchRecord ) const OVERRIDE
+ {
+ Assert( out_pSchRecord );
+ Assert( pAttrDef );
+ Assert( pAttrDef->GetAttributeType() == this );
+
+ AssertMsg( 0, "Implement this when we want this attribute to be dynamic" );
+ }
+
+ virtual void LoadSchToEconAttributeValue( CEconItem *pTargetItem, const CEconItemAttributeDefinition *pAttrDef, const GCSDK::CRecordBase *pSchRecord ) const OVERRIDE
+ {
+ Assert( pTargetItem );
+ Assert( pAttrDef );
+ Assert( pSchRecord );
+ Assert( pAttrDef->GetAttributeType() == this );
+
+ AssertMsg( 0, "Implement this when we want this attribute to be dynamic" );
+ }
+#endif // GC_DLL
+
+ virtual bool BConvertStringToEconAttributeValue( const CEconItemAttributeDefinition *pAttrDef, const char *pszValue, union attribute_data_union_t *out_pValue, bool bEnableTerribleBackwardsCompatibilitySchemaParsingCode ) const OVERRIDE
+ {
+ Assert( pAttrDef );
+ Assert( out_pValue );
+
+ std::string sValue( pszValue );
+ CAttribute_ItemSlotCriteria typedValue;
+ if ( !google::protobuf::TextFormat::ParseFromString( sValue, &typedValue ) )
+ return false;
+
+ this->ConvertTypedValueToEconAttributeValue( typedValue, out_pValue );
+
+ return true;
+ }
+
+ virtual void ConvertEconAttributeValueToString( const CEconItemAttributeDefinition *pAttrDef, const attribute_data_union_t& value, std::string *out_ps ) const OVERRIDE
+ {
+ Assert( pAttrDef );
+ Assert( out_ps );
+
+ this->ConvertEconAttributeValueToString( pAttrDef, value, out_ps );
+ }
+};
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+class CSchemaAttributeType_WorldItemPlacement : public CSchemaAttributeTypeProtobufBase < GC_SCH_REFERENCE( CSchItemAttributeWorldItemPlacement ) CAttribute_WorldItemPlacement >
+{
+public:
+#ifdef GC_DLL
+ virtual void ConvertEconAttributeValueToSch( itemid_t unItemId, const CEconItemAttributeDefinition *pAttrDef, const union attribute_data_union_t& value, GCSDK::CRecordBase *out_pSchRecord ) const OVERRIDE
+ {
+ Assert( out_pSchRecord );
+ Assert( pAttrDef );
+ Assert( pAttrDef->GetAttributeType() == this );
+
+ CSchItemAttributeWorldItemPlacement *out_psch = GetTypedSch< CSchItemAttributeWorldItemPlacement >( out_pSchRecord );
+
+ CAttribute_WorldItemPlacement typedValue;
+ ConvertEconAttributeValueToTypedValue( value, &typedValue );
+
+ out_psch->m_ulItemID = unItemId;
+ out_psch->m_unAttrDefIndex = pAttrDef->GetDefinitionIndex();
+ out_psch->m_ulOriginalItemID = typedValue.original_item_id();
+ out_psch->m_fPosX = typedValue.pos_x();
+ out_psch->m_fPosY = typedValue.pos_y();
+ out_psch->m_fPosZ = typedValue.pos_z();
+ out_psch->m_fAngX = typedValue.ang_x();
+ out_psch->m_fAngY = typedValue.ang_y();
+ out_psch->m_fAngZ = typedValue.ang_z();
+ }
+
+ virtual void LoadSchToEconAttributeValue( CEconItem *pTargetItem, const CEconItemAttributeDefinition *pAttrDef, const GCSDK::CRecordBase *pSchRecord ) const OVERRIDE
+ {
+ Assert( pTargetItem );
+ Assert( pAttrDef );
+ Assert( pSchRecord );
+ Assert( pAttrDef->GetAttributeType() == this );
+
+ const CSchItemAttributeWorldItemPlacement *psch = GetTypedSch< const CSchItemAttributeWorldItemPlacement >( pSchRecord );
+
+ CAttribute_WorldItemPlacement typedValue;
+ typedValue.set_original_item_id( psch->m_ulOriginalItemID );
+ typedValue.set_pos_x( psch->m_fPosX );
+ typedValue.set_pos_y( psch->m_fPosY );
+ typedValue.set_pos_x( psch->m_fPosZ );
+ typedValue.set_ang_x( psch->m_fAngX );
+ typedValue.set_ang_y( psch->m_fAngY );
+ typedValue.set_ang_z( psch->m_fAngZ );
+
+ pTargetItem->SetDynamicAttributeValue( pAttrDef, typedValue );
+ }
+#endif // GC_DLL
+
+ virtual bool BConvertStringToEconAttributeValue( const CEconItemAttributeDefinition *pAttrDef, const char *pszValue, union attribute_data_union_t *out_pValue, bool bEnableTerribleBackwardsCompatibilitySchemaParsingCode ) const OVERRIDE
+ {
+ Assert( pAttrDef );
+ Assert( out_pValue );
+
+ CAttribute_WorldItemPlacement typedValue;
+
+ uint32 unValue = ( pszValue ) ? atoi( pszValue ) : 0;
+
+ // Item forcing us to create the attribute (via force_gc_to_generate)
+ if ( unValue == 0 )
+ {
+ typedValue.set_original_item_id( INVALID_ITEM_ID );
+ typedValue.set_pos_x( 0.f );
+ typedValue.set_pos_y( 0.f );
+ typedValue.set_pos_z( 0.f );
+ typedValue.set_ang_x( 0.f );
+ typedValue.set_ang_y( 0.f );
+ typedValue.set_ang_z( 0.f );
+ }
+ else
+ {
+ std::string sValue( pszValue );
+ if ( !google::protobuf::TextFormat::ParseFromString( sValue, &typedValue ) )
+ return false;
+ }
+
+ this->ConvertTypedValueToEconAttributeValue( typedValue, out_pValue );
+ return true;
+ }
+
+ virtual void ConvertEconAttributeValueToString( const CEconItemAttributeDefinition *pAttrDef, const attribute_data_union_t& value, std::string *out_ps ) const OVERRIDE
+ {
+ Assert( pAttrDef );
+ Assert( out_ps );
+
+ this->ConvertEconAttributeValueToString( pAttrDef, value, out_ps );
+ }
+};
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+class CSchemaAttributeType_Float : public CSchemaAttributeTypeBase< GC_SCH_REFERENCE( CSchItemAttributeFloat ) float >
+{
+public:
+#ifdef GC_DLL
+ virtual void ConvertEconAttributeValueToSch( itemid_t unItemId, const CEconItemAttributeDefinition *pAttrDef, const union attribute_data_union_t& value, GCSDK::CRecordBase *out_pSchRecord ) const OVERRIDE
+ {
+ Assert( out_pSchRecord );
+ Assert( pAttrDef );
+ Assert( pAttrDef->GetAttributeType() == this );
+
+ CSchItemAttributeFloat *out_pschItemAttribute = GetTypedSch<CSchItemAttributeFloat>( out_pSchRecord );
+
+ // @note Tom Bui: we store the value as an unsigned integer in the DB, so just treat the field as a bunch of bits
+ out_pschItemAttribute->m_ulItemID = unItemId;
+ out_pschItemAttribute->m_unAttrDefIndex = pAttrDef->GetDefinitionIndex();
+ out_pschItemAttribute->m_fValue = value.asFloat;
+ }
+
+ virtual void LoadSchToEconAttributeValue( CEconItem *pTargetItem, const CEconItemAttributeDefinition *pAttrDef, const GCSDK::CRecordBase *pSchRecord ) const OVERRIDE
+ {
+ Assert( pTargetItem );
+ Assert( pAttrDef );
+ Assert( pSchRecord );
+ Assert( pAttrDef->GetAttributeType() == this );
+
+ const CSchItemAttributeFloat *pschItemAttribute = GetTypedSch<const CSchItemAttributeFloat>( pSchRecord );
+
+ pTargetItem->SetDynamicAttributeValue( pAttrDef, pschItemAttribute->m_fValue );
+ }
+#endif // GC_DLL
+
+ virtual bool BConvertStringToEconAttributeValue( const CEconItemAttributeDefinition *pAttrDef, const char *pszValue, union attribute_data_union_t *out_pValue, bool bEnableTerribleBackwardsCompatibilitySchemaParsingCode ) const OVERRIDE
+ {
+ Assert( pAttrDef );
+ Assert( out_pValue );
+
+ out_pValue->asFloat = Q_atof( pszValue );
+ return true;
+ }
+
+ virtual void ConvertEconAttributeValueToString( const CEconItemAttributeDefinition *pAttrDef, const attribute_data_union_t& value, std::string *out_ps ) const OVERRIDE
+ {
+ Assert( pAttrDef );
+ Assert( out_ps );
+
+ *out_ps = CFmtStr( "%f", value.asFloat ).Get();
+ }
+
+ virtual void ConvertTypedValueToByteStream( const float& typedValue, ::std::string *out_psBytes ) const OVERRIDE
+ {
+ Assert( out_psBytes );
+ Assert( out_psBytes->size() == 0 );
+
+ out_psBytes->resize( sizeof( float ) );
+ *reinterpret_cast<float *>( &((*out_psBytes)[0]) ) = typedValue; // overwrite string contents (sizeof( float ) bytes)
+ }
+
+ virtual void ConvertByteStreamToTypedValue( const ::std::string& sBytes, float *out_pTypedValue ) const OVERRIDE
+ {
+ Assert( out_pTypedValue );
+ Assert( sBytes.size() == sizeof( float ) );
+
+ *out_pTypedValue = *reinterpret_cast<const float *>( &sBytes[0] );
+ }
+
+ virtual bool BSupportsGameplayModificationAndNetworking() const OVERRIDE
+ {
+ return true;
+ }
+};
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+class CSchemaAttributeType_UInt64 : public CSchemaAttributeTypeBase< GC_SCH_REFERENCE( CSchItemAttributeUInt64 ) uint64 >
+{
+public:
+#ifdef GC_DLL
+ virtual void ConvertEconAttributeValueToSch( itemid_t unItemId, const CEconItemAttributeDefinition *pAttrDef, const union attribute_data_union_t& value, GCSDK::CRecordBase *out_pSchRecord ) const OVERRIDE
+ {
+ Assert( out_pSchRecord );
+ Assert( pAttrDef );
+ Assert( pAttrDef->GetAttributeType() == this );
+
+ uint64 ulValue;
+ ConvertEconAttributeValueToTypedValue( value, &ulValue );
+
+ CSchItemAttributeUInt64 *out_pschItemAttribute = GetTypedSch<CSchItemAttributeUInt64>( out_pSchRecord );
+
+ // @note Tom Bui: we store the value as an unsigned integer in the DB, so just treat the field as a bunch of bits
+ out_pschItemAttribute->m_ulItemID = unItemId;
+ out_pschItemAttribute->m_unAttrDefIndex = pAttrDef->GetDefinitionIndex();
+ out_pschItemAttribute->m_ulValue = ulValue;
+ }
+
+ virtual void LoadSchToEconAttributeValue( CEconItem *pTargetItem, const CEconItemAttributeDefinition *pAttrDef, const GCSDK::CRecordBase *pSchRecord ) const OVERRIDE
+ {
+ Assert( pTargetItem );
+ Assert( pAttrDef );
+ Assert( pSchRecord );
+ Assert( pAttrDef->GetAttributeType() == this );
+
+ const CSchItemAttributeUInt64 *pschItemAttribute = GetTypedSch<const CSchItemAttributeUInt64>( pSchRecord );
+
+ pTargetItem->SetDynamicAttributeValue( pAttrDef, pschItemAttribute->m_ulValue );
+ }
+#endif // GC_DLL
+
+ virtual bool BConvertStringToEconAttributeValue( const CEconItemAttributeDefinition *pAttrDef, const char *pszValue, union attribute_data_union_t *out_pValue, bool bEnableTerribleBackwardsCompatibilitySchemaParsingCode ) const OVERRIDE
+ {
+ Assert( pAttrDef );
+ Assert( out_pValue );
+
+ out_pValue->asUint32 = V_atoui64( pszValue );
+ return true;
+ }
+
+ virtual void ConvertEconAttributeValueToString( const CEconItemAttributeDefinition *pAttrDef, const attribute_data_union_t& value, std::string *out_ps ) const OVERRIDE
+ {
+ Assert( pAttrDef );
+ Assert( out_ps );
+
+ uint64 ulValue;
+ ConvertEconAttributeValueToTypedValue( value, &ulValue );
+
+ *out_ps = CFmtStr( "%llu", ulValue ).Get();
+ }
+
+ virtual void ConvertTypedValueToByteStream( const uint64& typedValue, ::std::string *out_psBytes ) const OVERRIDE
+ {
+ Assert( out_psBytes );
+ Assert( out_psBytes->size() == 0 );
+
+ out_psBytes->resize( sizeof( uint64 ) );
+ *reinterpret_cast<uint64 *>( &((*out_psBytes)[0]) ) = typedValue; // overwrite string contents (sizeof( uint64 ) bytes)
+ }
+
+ virtual void ConvertByteStreamToTypedValue( const ::std::string& sBytes, uint64 *out_pTypedValue ) const OVERRIDE
+ {
+ Assert( out_pTypedValue );
+ Assert( sBytes.size() == sizeof( uint64 ) );
+
+ *out_pTypedValue = *reinterpret_cast<const uint64 *>( &sBytes[0] );
+ }
+};
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+class CSchemaAttributeType_Default : public CSchemaAttributeTypeBase< GC_SCH_REFERENCE( CSchItemAttribute ) attrib_value_t >
+{
+public:
+#ifdef GC_DLL
+ virtual bool BAssetClassExportedAttributeValue( const CEconItemAttributeDefinition *pAttrDef, const attribute_data_union_t& value ) const OVERRIDE
+ {
+ Assert( pAttrDef );
+
+ static CSchemaAttributeDefHandle pAttrib_TradableAfter( "tradable after date" );
+
+ // Don't include "tradable after date" if it's in the past
+ // See IEconItemInterface::IsTradable for the specific logic on how this affects tradability
+ if ( pAttrDef == pAttrib_TradableAfter && CRTime::RTime32TimeCur() > value.asUint32 )
+ return false;
+
+ return CSchemaAttributeTypeBase< GC_SCH_REFERENCE( CSchItemAttribute ) attrib_value_t >::BAssetClassExportedAttributeValue( pAttrDef, value );
+ }
+
+ virtual void ConvertEconAttributeValueToSch( itemid_t unItemId, const CEconItemAttributeDefinition *pAttrDef, const union attribute_data_union_t& value, GCSDK::CRecordBase *out_pSchRecord ) const OVERRIDE
+ {
+ Assert( out_pSchRecord );
+ Assert( pAttrDef );
+ Assert( pAttrDef->GetAttributeType() == this );
+
+ CSchItemAttribute *out_pschItemAttribute = GetTypedSch<CSchItemAttribute>( out_pSchRecord );
+
+ // @note Tom Bui: we store the value as an unsigned integer in the DB, so just treat the field as a bunch of bits
+ out_pschItemAttribute->m_ulItemID = unItemId;
+ out_pschItemAttribute->m_unAttrDefIndex = pAttrDef->GetDefinitionIndex();
+ out_pschItemAttribute->m_unValue = value.asUint32;
+ }
+
+ virtual void LoadSchToEconAttributeValue( CEconItem *pTargetItem, const CEconItemAttributeDefinition *pAttrDef, const GCSDK::CRecordBase *pSchRecord ) const OVERRIDE
+ {
+ Assert( pTargetItem );
+ Assert( pAttrDef );
+ Assert( pSchRecord );
+ Assert( pAttrDef->GetAttributeType() == this );
+
+ const CSchItemAttribute *pschItemAttribute = GetTypedSch<const CSchItemAttribute>( pSchRecord );
+
+ pTargetItem->SetDynamicAttributeValue( pAttrDef, pschItemAttribute->m_unValue );
+ }
+
+ virtual void LoadOrGenerateEconAttributeValue( CEconItem *pTargetItem, const CEconItemAttributeDefinition *pAttrDef, const static_attrib_t& staticAttrib, const CEconGameAccount *pGameAccount ) const OVERRIDE
+ {
+ Assert( pTargetItem );
+ Assert( pTargetItem->GetItemDefinition() );
+ Assert( pAttrDef );
+
+ // Wear is reassigned by attributes but has a default value from itemdef prefab
+ static CSchemaAttributeDefHandle pAttrDef_PaintkitWear( "set_item_texture_wear" );
+
+ // do not apply an attribute if it already exists. If the new and the old attribute value is different then we assert (and use the latest value)
+ attrib_value_t unValue = 0;
+ if ( pTargetItem->FindAttribute( pAttrDef, &unValue ) && pAttrDef != pAttrDef_PaintkitWear )
+ {
+ AssertMsg4( unValue == staticAttrib.m_value.asUint32,
+ "Item id %llu (%s) attempting to generate dynamic attribute value for '%s' (%d) when attribute already exists with a different Value! This probably indicates some sort of code flow error calling LoadOrGenerateEconAttributeValue() late.",
+ pTargetItem->GetItemID(), pTargetItem->GetItemDefinition()->GetDefinitionName(), pAttrDef->GetDefinitionName(), pAttrDef->GetDefinitionIndex() );
+
+ if ( unValue == staticAttrib.m_value.asUint32 )
+ return;
+ }
+
+ // Could be raw integer bits or raw floating-point bits depending on where in the union we stored the value. We copy the
+ // bit pattern indiscriminately.
+ attribute_data_union_t ResultValue;
+ ResultValue = staticAttrib.m_value;
+ GenerateEconAttributeValue( pAttrDef, staticAttrib, pGameAccount, &ResultValue );
+ LoadEconAttributeValue( pTargetItem, pAttrDef, ResultValue );
+ }
+
+ virtual void GenerateEconAttributeValue( const CEconItemAttributeDefinition *pAttrDef, const static_attrib_t& staticAttrib, const CEconGameAccount *pGameAccount, attribute_data_union_t* out_pValue ) const
+ {
+ Assert( pAttrDef );
+ AssertMsg( pGameAccount || !staticAttrib.m_pKVCustomData, "Cannot run custom logic with no game account object! Passing in NULL for pGameAccount is only supported when we know we won't be running custom value generation code!" );
+ Assert( out_pValue );
+
+ if( staticAttrib.m_pKVCustomData )
+ {
+ Internal_RunCustomAttributeValueLogic( staticAttrib, pGameAccount, out_pValue );
+ }
+ }
+#endif // GC_DLL
+
+ virtual bool BConvertStringToEconAttributeValue( const CEconItemAttributeDefinition *pAttrDef, const char *pszValue, union attribute_data_union_t *out_pValue, bool bEnableTerribleBackwardsCompatibilitySchemaParsingCode ) const OVERRIDE
+ {
+ Assert( pAttrDef );
+ Assert( out_pValue );
+
+ if ( bEnableTerribleBackwardsCompatibilitySchemaParsingCode )
+ {
+ // Not having any value specified is valid -- we interpret this as "default", or 0 as both an in int and a float.
+ out_pValue->asFloat = pszValue
+ ? atof( pszValue )
+ : 0.0f;
+ }
+ // This is terrible backwards-compatibility code to support the pulling of values from econ asset classes.
+ else
+ {
+ if ( pAttrDef->IsStoredAsInteger() )
+ {
+ out_pValue->asUint32 = (uint32)Q_atoui64( pszValue );
+ }
+ else if ( pAttrDef->IsStoredAsFloat() )
+ {
+ out_pValue->asFloat = Q_atof( pszValue );
+ }
+ else
+ {
+ Assert( !"Unknown storage type for CSchemaAttributeType_Default::BConvertStringToEconAttributeValue()!" );
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ virtual void ConvertEconAttributeValueToString( const CEconItemAttributeDefinition *pAttrDef, const attribute_data_union_t& value, std::string *out_ps ) const OVERRIDE
+ {
+ Assert( pAttrDef );
+ Assert( out_ps );
+
+ if( pAttrDef->IsStoredAsFloat() )
+ {
+ *out_ps = CFmtStr( "%f", value.asFloat ).Get();
+ }
+ else if( pAttrDef->IsStoredAsInteger() )
+ {
+ *out_ps = CFmtStr( "%u", value.asUint32 ).Get();
+ }
+ else
+ {
+ Assert( !"Unknown storage type for CSchemaAttributeType_Default::ConvertEconAttributeValueToString()!" );
+ }
+ }
+
+ virtual void ConvertTypedValueToByteStream( const attrib_value_t& typedValue, ::std::string *out_psBytes ) const OVERRIDE
+ {
+ Assert( out_psBytes );
+ Assert( out_psBytes->size() == 0 );
+
+ out_psBytes->resize( sizeof( attrib_value_t ) );
+ *reinterpret_cast<attrib_value_t *>( &((*out_psBytes)[0]) ) = typedValue; // overwrite string contents (sizeof( attrib_value_t ) bytes)
+ }
+
+ virtual void ConvertByteStreamToTypedValue( const ::std::string& sBytes, attrib_value_t *out_pTypedValue ) const OVERRIDE
+ {
+ Assert( out_pTypedValue );
+#ifdef GC_DLL
+ // The GC is expected to always have internally-consistent information.
+ Assert( sBytes.size() == sizeof( attrib_value_t ) );
+#else
+ // Game clients and servers may have partially out-of-date information, or may have downloaded a new schema
+ // but not know how to parse an attribute of a certain type, etc. In these cases, because we know we
+ // aren't on the GC, temporarily failing to load these values until the client shuts down and updates
+ // is about the best we can hope for.
+ if ( sBytes.size() < sizeof( attrib_value_t ) )
+ {
+ *out_pTypedValue = attrib_value_t();
+ return;
+ }
+#endif
+
+ *out_pTypedValue = *reinterpret_cast<const attrib_value_t *>( &sBytes[0] );
+ }
+
+ virtual bool BSupportsGameplayModificationAndNetworking() const OVERRIDE
+ {
+ return true;
+ }
+
+private:
+#ifdef GC_DLL
+ void Internal_RunCustomAttributeValueLogic( const static_attrib_t& staticAttrib, const CEconGameAccount *pGameAccount, attribute_data_union_t* out_pValue ) const
+ {
+ AssertMsg( pGameAccount, "No game account when running custom attribute value logic!" );
+
+ float flValue = 0;
+
+ const char *pszMethod = staticAttrib.m_pKVCustomData->GetString( "method", NULL );
+
+ if ( Q_stricmp( pszMethod, "employee_number" ) == 0 )
+ {
+ flValue = pGameAccount->Obj().m_rtime32FirstPlayed;
+ }
+ else if ( Q_stricmp( pszMethod, "date" ) == 0 ) // Not used?
+ {
+ flValue = CRTime::RTime32TimeCur();
+ }
+ else if ( Q_stricmp( pszMethod, "year" ) == 0 )
+ {
+ flValue = CRTime( CRTime::RTime32TimeCur() ).GetYear();
+ }
+ else if ( Q_stricmp( pszMethod, "gifts_given_out" ) == 0 )
+ {
+ flValue = pGameAccount->Obj().m_unNumGiftsGiven;
+ }
+ else if ( Q_stricmp( pszMethod, "expiration_period_hours_from_now" ) == 0 )
+ {
+ flValue = CRTime::RTime32DateAdd( CRTime::RTime32TimeCur(), staticAttrib.m_value.asFloat, k_ETimeUnitHour );
+ }
+ else if ( Q_stricmp( pszMethod, "def index from lootlist" ) == 0 )
+ {
+ const char* pszLootlistName = staticAttrib.m_pKVCustomData->GetString( "lootlist" );
+ // Custom data stores the lootlist
+ const CEconLootListDefinition* pLootList = GEconItemSchema().GetLootListByName( pszLootlistName );
+
+ if( !pLootList )
+ {
+ AssertMsg1( 0, "Lootlist '%s' not found when performing custom attribute logic", pszLootlistName );
+ return;
+ }
+
+ CUtlVector<CEconLootListDefinition::rolled_item_defs_t> vecRolledItems;
+ // Roll our item def
+ CDefaultUniformRandomStream RandomStream;
+ if( !pLootList->RollRandomItemsAndAdditionalItems( &RandomStream, false, &vecRolledItems ) )
+ {
+ AssertMsg1( 0, "Error generating item defs from lootlist '%s'", pszLootlistName );
+ return;
+ }
+
+ // Just take the first one's def index
+ flValue = vecRolledItems.Head().m_pItemDef->GetDefinitionIndex();
+ }
+ else
+ {
+ AssertMsg1( false, "Unknown value for 'method': '%s'", pszMethod );
+ }
+
+ // Put the value into the right part of the union
+ if ( staticAttrib.GetAttributeDefinition()->IsStoredAsFloat() )
+ {
+ (*out_pValue).asFloat = flValue;
+ }
+ else if ( staticAttrib.GetAttributeDefinition()->IsStoredAsInteger() )
+ {
+ (*out_pValue).asUint32 = (uint32)flValue;
+ }
+ else
+ {
+ AssertMsg1( 0, "Unknown storage type for CSchemaAttributeType_Default::Internal_RunCustomAttributeValueLogic() for attribute %s", staticAttrib.GetAttributeDefinition()->GetDefinitionName() );
+ }
+ }
+#endif
+};
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor
+//-----------------------------------------------------------------------------
+CEconItemAttributeDefinition::CEconItemAttributeDefinition( void )
+: m_pKVAttribute( NULL ),
+ m_pAttrType( NULL ),
+ m_bHidden( false ),
+ m_bWebSchemaOutputForced( false ),
+ m_bStoredAsInteger( false ),
+ m_bInstanceData( false ),
+ m_bIsSetBonus( false ),
+ m_iUserGenerationType( 0 ),
+ m_iEffectType( ATTRIB_EFFECT_NEUTRAL ),
+ m_iDescriptionFormat( 0 ),
+ m_pszDescriptionString( NULL ),
+ m_pszArmoryDesc( NULL ),
+ m_pszDefinitionName( NULL ),
+ m_pszAttributeClass( NULL ),
+ m_ItemDefinitionTag( INVALID_ECON_TAG_HANDLE ),
+ m_bCanAffectMarketName( false ),
+ m_bCanAffectRecipeComponentName( false )
+#ifndef GC_DLL
+ , m_iszAttributeClass( NULL_STRING )
+#endif
+{
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Copy constructor
+//-----------------------------------------------------------------------------
+CEconItemAttributeDefinition::CEconItemAttributeDefinition( const CEconItemAttributeDefinition &that )
+{
+ (*this) = that;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Operator=
+//-----------------------------------------------------------------------------
+CEconItemAttributeDefinition &CEconItemAttributeDefinition::operator=( const CEconItemAttributeDefinition &rhs )
+{
+ m_nDefIndex = rhs.m_nDefIndex;
+ m_pAttrType = rhs.m_pAttrType;
+ m_bHidden = rhs.m_bHidden;
+ m_bWebSchemaOutputForced = rhs.m_bWebSchemaOutputForced;
+ m_bStoredAsInteger = rhs.m_bStoredAsInteger;
+ m_iUserGenerationType = rhs.m_iUserGenerationType;
+ m_bInstanceData = rhs.m_bInstanceData;
+ m_bIsSetBonus = rhs.m_bIsSetBonus;
+ m_iEffectType = rhs.m_iEffectType;
+ m_iDescriptionFormat = rhs.m_iDescriptionFormat;
+ m_pszDescriptionString = rhs.m_pszDescriptionString;
+ m_pszArmoryDesc = rhs.m_pszArmoryDesc;
+ m_pszDefinitionName = rhs.m_pszDefinitionName;
+ m_pszAttributeClass = rhs.m_pszAttributeClass;
+ m_ItemDefinitionTag = rhs.m_ItemDefinitionTag;
+ m_bCanAffectMarketName = rhs.m_bCanAffectMarketName;
+ m_bCanAffectRecipeComponentName = rhs.m_bCanAffectRecipeComponentName;
+#ifndef GC_DLL
+ m_iszAttributeClass = rhs.m_iszAttributeClass;
+#endif
+
+ m_pKVAttribute = NULL;
+ if ( NULL != rhs.m_pKVAttribute )
+ {
+ m_pKVAttribute = rhs.m_pKVAttribute->MakeCopy();
+
+ // Re-assign string pointers
+ m_pszDefinitionName = m_pKVAttribute->GetString("name");
+ m_pszDescriptionString = m_pKVAttribute->GetString( "description_string", NULL );
+ m_pszArmoryDesc = m_pKVAttribute->GetString( "armory_desc", NULL );
+ m_pszAttributeClass = m_pKVAttribute->GetString( "attribute_class", NULL );
+
+ Assert( V_strcmp( m_pszDefinitionName, rhs.m_pszDefinitionName ) == 0 );
+ Assert( V_strcmp( m_pszDescriptionString, rhs.m_pszDescriptionString ) == 0 );
+ Assert( V_strcmp( m_pszArmoryDesc, rhs.m_pszArmoryDesc ) == 0 );
+ Assert( V_strcmp( m_pszAttributeClass, rhs.m_pszAttributeClass ) == 0 );
+ }
+ else
+ {
+ Assert( m_pszDefinitionName == NULL );
+ Assert( m_pszDescriptionString == NULL );
+ Assert( m_pszArmoryDesc == NULL );
+ Assert( m_pszAttributeClass == NULL );
+ }
+ return *this;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Destructor
+//-----------------------------------------------------------------------------
+CEconItemAttributeDefinition::~CEconItemAttributeDefinition( void )
+{
+ if ( m_pKVAttribute )
+ m_pKVAttribute->deleteThis();
+ m_pKVAttribute = NULL;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Initialize the attribute definition
+// Input: pKVAttribute - The KeyValues representation of the attribute
+// schema - The overall item schema for this attribute
+// pVecErrors - An optional vector that will contain error messages if
+// the init fails.
+// Output: True if initialization succeeded, false otherwise
+//-----------------------------------------------------------------------------
+bool CEconItemAttributeDefinition::BInitFromKV( KeyValues *pKVAttribute, CUtlVector<CUtlString> *pVecErrors /* = NULL */ )
+{
+ m_pKVAttribute = pKVAttribute->MakeCopy();
+ m_nDefIndex = Q_atoi( m_pKVAttribute->GetName() );
+
+ m_pszDefinitionName = m_pKVAttribute->GetString("name", "(unnamed)");
+ m_bHidden = m_pKVAttribute->GetInt( "hidden", 0 ) != 0;
+ m_bWebSchemaOutputForced = m_pKVAttribute->GetInt( "force_output_description", 0 ) != 0;
+ m_bStoredAsInteger = m_pKVAttribute->GetInt( "stored_as_integer", 0 ) != 0;
+ m_bIsSetBonus = m_pKVAttribute->GetBool( "is_set_bonus", false );
+ m_bCanAffectMarketName = m_pKVAttribute->GetBool( "can_affect_market_name", false );
+ m_bCanAffectRecipeComponentName = m_pKVAttribute->GetBool( "can_affect_recipe_component_name", false );
+ m_iUserGenerationType = m_pKVAttribute->GetInt( "is_user_generated", 0 );
+ m_iEffectType = (attrib_effect_types_t)StringFieldToInt( m_pKVAttribute->GetString("effect_type"), g_EffectTypes, ARRAYSIZE(g_EffectTypes) );
+ m_iDescriptionFormat = StringFieldToInt( m_pKVAttribute->GetString("description_format"), g_AttributeDescriptionFormats, ARRAYSIZE(g_AttributeDescriptionFormats) );
+ m_pszDescriptionString = m_pKVAttribute->GetString( "description_string", NULL );
+ m_pszArmoryDesc = m_pKVAttribute->GetString( "armory_desc", NULL );
+ m_pszAttributeClass = m_pKVAttribute->GetString( "attribute_class", NULL );
+ m_bInstanceData = pKVAttribute->GetBool( "instance_data", false );
+
+ const char *pszTag = m_pKVAttribute->GetString( "apply_tag_to_item_definition", NULL );
+ m_ItemDefinitionTag = pszTag ? GetItemSchema()->GetHandleForTag( pszTag ) : INVALID_ECON_TAG_HANDLE;
+
+#if defined(CLIENT_DLL) || defined(GAME_DLL)
+ m_iszAttributeClass = NULL_STRING;
+#endif
+ const char *pszAttrType = m_pKVAttribute->GetString( "attribute_type", NULL ); // NULL implies "default type" for backwards compatibility
+ m_pAttrType = GetItemSchema()->GetAttributeType( pszAttrType );
+
+ SCHEMA_INIT_CHECK(
+ NULL != m_pKVAttribute->FindKey( "name" ),
+ "Attribute definition %s: Missing required field \"name\"", m_pKVAttribute->GetName() );
+
+ SCHEMA_INIT_CHECK(
+ NULL != m_pAttrType,
+ "Attribute definition %s: Unable to find attribute data type '%s'", m_pszDefinitionName, pszAttrType ? pszAttrType : "(default)" );
+
+ if ( m_bIsSetBonus )
+ {
+ SCHEMA_INIT_CHECK(
+ m_pAttrType->BSupportsGameplayModificationAndNetworking(),
+ "Attribute definition %s: set as set bonus attribute but does not support gameplay modification/networking!", m_pszDefinitionName );
+ }
+
+ m_unAssetClassBucket = pKVAttribute->GetInt( "asset_class_bucket", 0 );
+ m_eAssetClassAttrExportRule = k_EAssetClassAttrExportRule_Default;
+ if ( char const *szRule = pKVAttribute->GetString( "asset_class_export", NULL ) )
+ {
+ if ( !V_stricmp( szRule, "skip" ) )
+ {
+ m_eAssetClassAttrExportRule = k_EAssetClassAttrExportRule_Skip;
+ }
+ else if ( !V_stricmp( szRule, "gconly" ) )
+ {
+ m_eAssetClassAttrExportRule = EAssetClassAttrExportRule_t( k_EAssetClassAttrExportRule_GCOnly | k_EAssetClassAttrExportRule_Skip );
+ }
+ else if ( !V_stricmp( szRule, "bucketed" ) )
+ {
+ SCHEMA_INIT_CHECK( m_unAssetClassBucket, "Attribute definition %s: Asset class export rule '%s' is incompatible", m_pszDefinitionName, szRule );
+ m_eAssetClassAttrExportRule = k_EAssetClassAttrExportRule_Bucketed;
+ }
+ else if ( !V_stricmp( szRule, "default" ) )
+ {
+ m_eAssetClassAttrExportRule = k_EAssetClassAttrExportRule_Default;
+ }
+ else
+ {
+ SCHEMA_INIT_CHECK( false, "Attribute definition %s: Invalid asset class export rule '%s'", m_pszDefinitionName, szRule );
+ }
+ }
+
+ // Check for misuse of asset class bucket
+ SCHEMA_INIT_CHECK( ( !m_unAssetClassBucket || m_bInstanceData ), "Attribute definition %s: Cannot use \"asset_class_bucket\" on class-level attributes", m_pKVAttribute->GetName() );
+
+
+ return SCHEMA_INIT_SUCCESS();
+}
+
+CQuestObjectiveDefinition::CQuestObjectiveDefinition( void )
+ : m_pszDescriptionToken( NULL )
+ , m_nDefIndex( 0 )
+ , m_nPoints( 0 )
+{}
+
+CQuestObjectiveDefinition::~CQuestObjectiveDefinition()
+{}
+
+bool CQuestObjectiveDefinition::BInitFromKV( KeyValues *pKVItem, CUtlVector<CUtlString> *pVecErrors /* = NULL */ )
+{
+ m_nDefIndex = pKVItem->GetInt( "defindex", -1 );
+ m_pszDescriptionToken = pKVItem->GetString( "description_string" );
+ m_bOptional = pKVItem->GetBool( "optional", false );
+ m_bAdvanced = pKVItem->GetBool( "advanced", false );
+ m_nPoints = pKVItem->GetInt( "points", 0 );
+
+ SCHEMA_INIT_CHECK( m_nDefIndex != -1, "Quest objective missing def index" );
+ SCHEMA_INIT_CHECK( m_pszDescriptionToken != NULL, "Quest objective is missing a description" );
+
+ return SCHEMA_INIT_SUCCESS();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor
+//-----------------------------------------------------------------------------
+CEconItemDefinition::CEconItemDefinition( void )
+: m_pKVItem( NULL ),
+m_bEnabled( false ),
+m_unMinItemLevel( 1 ),
+m_unMaxItemLevel( 1 ),
+m_iArmoryRemap( 0 ),
+m_iStoreRemap( 0 ),
+m_nItemQuality( k_unItemQuality_Any ),
+m_nForcedItemQuality( k_unItemQuality_Any ),
+m_nDefaultDropQuantity( 1 ),
+m_bLoadOnDemand( false ),
+m_pTool( NULL ),
+m_rtExpiration( 0 ),
+m_BundleInfo( NULL ),
+#ifdef TF_CLIENT_DLL
+m_unNumConcreteItems( 0 ),
+#endif // TF_CLIENT_DLL
+m_nPopularitySeed( 0 ),
+m_pszDefinitionName( NULL ),
+m_pszItemClassname( NULL ),
+m_pszClassToken( NULL ),
+m_pszSlotToken( NULL ),
+m_pszItemBaseName( NULL ),
+m_pszItemTypeName( NULL ),
+m_pszItemDesc( NULL ),
+m_pszArmoryDesc( NULL ),
+m_pszInventoryModel( NULL ),
+m_pszInventoryImage( NULL ),
+m_pszHolidayRestriction( NULL ),
+m_iSubType( 0 ),
+m_pszBaseDisplayModel( NULL ),
+m_iDefaultSkin( -1 ),
+m_pszWorldDisplayModel( NULL ),
+m_pszWorldExtraWearableModel( NULL ),
+m_pszWorldExtraWearableViewModel( NULL ),
+m_pszVisionFilteredDisplayModel( NULL ),
+m_pszBrassModelOverride( NULL ),
+m_bHideBodyGroupsDeployedOnly( false ),
+m_bAttachToHands( false ),
+m_bAttachToHandsVMOnly( false ),
+m_bProperName( false ),
+m_bFlipViewModel( false ),
+m_bActAsWearable( false ),
+m_bActAsWeapon( false ),
+m_iDropType( 1 ),
+m_bHidden( false ),
+m_bShouldShowInArmory( false ),
+m_bIsPackBundle( false ),
+m_pOwningPackBundle( NULL ),
+m_bIsPackItem( false ),
+m_bBaseItem( false ),
+m_pszItemLogClassname( NULL ),
+m_pszItemIconClassname( NULL ),
+m_pszDatabaseAuditTable( NULL ),
+m_bImported( false ),
+m_pItemSetDef( NULL ),
+m_pItemCollectionDef( NULL ),
+m_pItemPaintKitDef( NULL ),
+m_pszArmoryRemap( NULL ),
+m_pszStoreRemap( NULL ),
+m_unSetItemRemapDefIndex( INVALID_ITEM_DEF_INDEX ),
+m_pszXifierRemapClass( NULL ),
+m_pszBaseFunctionalItemName( NULL ),
+m_pszParticleSuffix( NULL ),
+m_pszCollectionReference( NULL ),
+m_nItemRarity( k_unItemRarity_Any ),
+m_unItemSeries( 0 ),
+m_bValidForShuffle( false ),
+m_bValidForSelfMade( true )
+{
+ for ( int team = 0; team < TEAM_VISUAL_SECTIONS; team++ )
+ {
+ m_PerTeamVisuals[team] = NULL;
+ }
+
+ m_pDictIcons = new CUtlDict< CUtlString >;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Destructor
+//-----------------------------------------------------------------------------
+CEconItemDefinition::~CEconItemDefinition( void )
+{
+ for ( int i = 0; i < ARRAYSIZE( m_PerTeamVisuals ); i++ )
+ delete m_PerTeamVisuals[i];
+
+#ifdef GC_DLL
+ m_vecPropertyGenerators.PurgeAndDeleteElements();
+#endif // GC_DLL
+
+ if ( m_pKVItem )
+ m_pKVItem->deleteThis();
+ m_pKVItem = NULL;
+ delete m_pTool;
+ delete m_BundleInfo;
+ delete m_pDictIcons;
+}
+
+#if defined(CLIENT_DLL) || defined(GAME_DLL)
+//-----------------------------------------------------------------------------
+// Purpose: Stomp our base data with extra testing data specified by the player
+//-----------------------------------------------------------------------------
+bool CEconItemDefinition::BInitFromTestItemKVs( int iNewDefIndex, KeyValues *pKVItem, CUtlVector<CUtlString>* pVecErrors )
+{
+ // The KeyValues are stored in the player entity, so we can cache our name there
+
+ m_nDefIndex = iNewDefIndex;
+ m_unSetItemRemapDefIndex = m_nDefIndex;
+
+ bool bTestingExistingItem = pKVItem->GetBool( "test_existing_item", false );
+ if ( !bTestingExistingItem )
+ {
+ m_pszDefinitionName = pKVItem->GetString( "name", NULL );
+ m_pszItemBaseName = pKVItem->GetString( "name", NULL );
+
+#ifdef CLIENT_DLL
+ pKVItem->SetString( "name", VarArgs("Test Item %d", iNewDefIndex) );
+#else
+ pKVItem->SetString( "name", UTIL_VarArgs("Test Item %d", iNewDefIndex) );
+#endif
+
+ m_pszBaseDisplayModel = pKVItem->GetString( "model_player", NULL );
+ m_pszVisionFilteredDisplayModel = pKVItem->GetString( "model_vision_filtered", NULL );
+ m_bAttachToHands = pKVItem->GetInt( "attach_to_hands", 0 ) != 0;
+
+ BInitVisualBlockFromKV( pKVItem );
+ }
+
+ // Handle attributes
+ m_vecStaticAttributes.Purge();
+ int iPaintCanIndex = pKVItem->GetInt("paintcan_index", 0);
+ if ( iPaintCanIndex )
+ {
+ static CSchemaAttributeDefHandle pAttrDef_PaintRGB( "set item tint RGB" );
+
+ const CEconItemDefinition *pCanDef = GetItemSchema()->GetItemDefinition(iPaintCanIndex);
+
+ float flRGBVal;
+ if ( pCanDef && pAttrDef_PaintRGB && FindAttribute_UnsafeBitwiseCast<attrib_value_t>( pCanDef, pAttrDef_PaintRGB, &flRGBVal ) )
+ {
+ static_attrib_t& StaticAttrib = m_vecStaticAttributes[ m_vecStaticAttributes.AddToTail() ];
+
+ StaticAttrib.iDefIndex = pAttrDef_PaintRGB->GetDefinitionIndex();
+ StaticAttrib.m_value.asFloat = flRGBVal; // this is bad! but we're in crazy hack code for UI customization of item definitions that don't exist so
+ }
+ }
+
+ int iUnusualEffectIndex = pKVItem->GetInt( "unusual_index", 0 );
+ if ( iUnusualEffectIndex )
+ {
+ static CSchemaAttributeDefHandle pAttrDef_AttachParticleStatic( "attach particle effect static" );
+
+ const attachedparticlesystem_t *pSystem = GetItemSchema()->GetAttributeControlledParticleSystem( iUnusualEffectIndex );
+
+ if ( pAttrDef_AttachParticleStatic && pSystem )
+ {
+ static_attrib_t& StaticAttrib = m_vecStaticAttributes[ m_vecStaticAttributes.AddToTail() ];
+
+ StaticAttrib.iDefIndex = pAttrDef_AttachParticleStatic->GetDefinitionIndex();
+ StaticAttrib.m_value.asFloat = iUnusualEffectIndex; // this is bad! but we're in crazy hack code for UI customization of item definitions that don't exist so
+ }
+ }
+
+ return true;
+}
+
+animation_on_wearable_t *GetOrCreateAnimationActivity( perteamvisuals_t *pVisData, const char *pszActivityName )
+{
+ FOR_EACH_VEC( pVisData->m_Animations, i )
+ {
+ if ( Q_stricmp(pVisData->m_Animations[i].pszActivity, pszActivityName) == 0 )
+ return &pVisData->m_Animations[i];
+ }
+
+ animation_on_wearable_t *pEntry = &pVisData->m_Animations[pVisData->m_Animations.AddToTail()];
+
+ pEntry->iActivity = kActivityLookup_Unknown; // We can't look it up yet, the activity list hasn't been populated.
+ pEntry->pszActivity = pszActivityName;
+ pEntry->iReplacement = kActivityLookup_Unknown;
+ pEntry->pszReplacement = NULL;
+ pEntry->pszSequence = NULL;
+ pEntry->pszScene = NULL;
+ pEntry->pszRequiredItem = NULL;
+
+ return pEntry;
+}
+
+activity_on_wearable_t *GetOrCreatePlaybackActivity( perteamvisuals_t *pVisData, wearableanimplayback_t iPlayback )
+{
+ FOR_EACH_VEC( pVisData->m_Animations, i )
+ {
+ if ( pVisData->m_Activities[i].iPlayback == iPlayback )
+ return &pVisData->m_Activities[i];
+ }
+
+ activity_on_wearable_t *pEntry = &pVisData->m_Activities[pVisData->m_Activities.AddToTail()];
+
+ pEntry->iPlayback = iPlayback;
+ pEntry->iActivity = kActivityLookup_Unknown; // We can't look it up yet, the activity list hasn't been populated.
+ pEntry->pszActivity = NULL;
+
+ return pEntry;
+}
+
+#endif // defined(CLIENT_DLL) || defined(GAME_DLL)
+
+//-----------------------------------------------------------------------------
+// Purpose: Handle parsing the per-team visual block from the keyvalues
+//-----------------------------------------------------------------------------
+void CEconItemDefinition::BInitVisualBlockFromKV( KeyValues *pKVItem, CUtlVector<CUtlString> *pVecErrors )
+{
+ // Visuals
+ for ( int team = 0; team < TEAM_VISUAL_SECTIONS; team++ )
+ {
+ m_PerTeamVisuals[team] = NULL;
+
+ if ( !g_TeamVisualSections[team] )
+ continue;
+
+ KeyValues *pVisualsKV = pKVItem->FindKey( g_TeamVisualSections[team] );
+ if ( pVisualsKV )
+ {
+ perteamvisuals_t *pVisData = new perteamvisuals_t();
+#if defined(CLIENT_DLL) || defined(GAME_DLL)
+ KeyValues *pKVEntry = pVisualsKV->GetFirstSubKey();
+ while ( pKVEntry )
+ {
+ const char *pszEntry = pKVEntry->GetName();
+
+ if ( !Q_stricmp( pszEntry, "use_visualsblock_as_base" ) )
+ {
+ // Start with a copy of an existing PerTeamVisuals
+ const char *pszString = pKVEntry->GetString();
+ int nOverrideTeam = GetTeamVisualsFromString( pszString );
+ if ( nOverrideTeam != -1 )
+ {
+ *pVisData = *m_PerTeamVisuals[nOverrideTeam];
+ }
+ else
+ {
+ pVecErrors->AddToTail( CFmtStr( "Unknown visuals block: %s", pszString ).Access() );
+ }
+ }
+ else if ( !Q_stricmp( pszEntry, "attached_models" ) )
+ {
+ FOR_EACH_SUBKEY( pKVEntry, pKVAttachedModelData )
+ {
+ int iAtt = pVisData->m_AttachedModels.AddToTail();
+ pVisData->m_AttachedModels[iAtt].m_iModelDisplayFlags = pKVAttachedModelData->GetInt( "model_display_flags", kAttachedModelDisplayFlag_MaskAll );
+ pVisData->m_AttachedModels[iAtt].m_pszModelName = pKVAttachedModelData->GetString( "model", NULL );
+ }
+ }
+ else if ( !Q_stricmp( pszEntry, "attached_models_festive" ) )
+ {
+ FOR_EACH_SUBKEY( pKVEntry, pKVAttachedModelData )
+ {
+ int iAtt = pVisData->m_AttachedModelsFestive.AddToTail();
+ pVisData->m_AttachedModelsFestive[iAtt].m_iModelDisplayFlags = pKVAttachedModelData->GetInt( "model_display_flags", kAttachedModelDisplayFlag_MaskAll );
+ pVisData->m_AttachedModelsFestive[iAtt].m_pszModelName = pKVAttachedModelData->GetString( "model", NULL );
+ }
+ }
+ else if ( !Q_stricmp( pszEntry, "attached_particlesystems" ) )
+ {
+ FOR_EACH_SUBKEY( pKVEntry, pKVAttachedParticleSystemData )
+ {
+ int iAtt = pVisData->m_AttachedParticles.AddToTail();
+ pVisData->m_AttachedParticles[iAtt].pszSystemName = pKVAttachedParticleSystemData->GetString( "system", NULL );
+ pVisData->m_AttachedParticles[iAtt].pszControlPoints[0] = pKVAttachedParticleSystemData->GetString( "attachment", NULL );
+ pVisData->m_AttachedParticles[iAtt].bFollowRootBone = pKVAttachedParticleSystemData->GetBool( "attach_to_rootbone" );
+ pVisData->m_AttachedParticles[iAtt].iCustomType = 0;
+ }
+ }
+ else if ( !Q_stricmp( pszEntry, "custom_particlesystem2" ) )
+ {
+ int iAtt = pVisData->m_AttachedParticles.AddToTail();
+ pVisData->m_AttachedParticles[iAtt].pszSystemName = pKVEntry->GetString( "system", NULL );
+ pVisData->m_AttachedParticles[iAtt].iCustomType = 2;
+ }
+ else if ( !Q_stricmp( pszEntry, "custom_particlesystem" ) )
+ {
+ int iAtt = pVisData->m_AttachedParticles.AddToTail();
+ pVisData->m_AttachedParticles[iAtt].pszSystemName = pKVEntry->GetString( "system", NULL );
+ pVisData->m_AttachedParticles[iAtt].iCustomType = 1;
+ }
+ else if ( !Q_stricmp( pszEntry, "playback_activity" ) )
+ {
+ FOR_EACH_SUBKEY( pKVEntry, pKVSubKey )
+ {
+ int iPlaybackInt = StringFieldToInt( pKVSubKey->GetName(), g_WearableAnimTypeStrings, ARRAYSIZE(g_WearableAnimTypeStrings) );
+ if ( iPlaybackInt >= 0 )
+ {
+ activity_on_wearable_t *pEntry = GetOrCreatePlaybackActivity( pVisData, (wearableanimplayback_t)iPlaybackInt );
+ pEntry->pszActivity = pKVSubKey->GetString();
+ }
+ }
+ }
+ else if ( !Q_stricmp( pszEntry, "animation_replacement" ) )
+ {
+ FOR_EACH_SUBKEY( pKVEntry, pKVSubKey )
+ {
+ animation_on_wearable_t *pEntry = GetOrCreateAnimationActivity( pVisData, pKVSubKey->GetName() );
+ pEntry->pszReplacement = pKVSubKey->GetString();
+ }
+ }
+ else if ( !Q_stricmp( pszEntry, "animation_sequence" ) )
+ {
+ FOR_EACH_SUBKEY( pKVEntry, pKVSubKey )
+ {
+ animation_on_wearable_t *pEntry = GetOrCreateAnimationActivity( pVisData, pKVSubKey->GetName() );
+ pEntry->pszSequence = pKVSubKey->GetString();
+ }
+ }
+ else if ( !Q_stricmp( pszEntry, "animation_scene" ) )
+ {
+ FOR_EACH_SUBKEY( pKVEntry, pKVSubKey )
+ {
+ animation_on_wearable_t *pEntry = GetOrCreateAnimationActivity( pVisData, pKVSubKey->GetName() );
+ pEntry->pszScene = pKVSubKey->GetString();
+ }
+ }
+ else if ( !Q_stricmp( pszEntry, "animation_required_item" ) )
+ {
+ FOR_EACH_SUBKEY( pKVEntry, pKVSubKey )
+ {
+ animation_on_wearable_t *pEntry = GetOrCreateAnimationActivity( pVisData, pKVSubKey->GetName() );
+ pEntry->pszRequiredItem = pKVSubKey->GetString();
+ }
+ }
+ else if ( !Q_stricmp( pszEntry, "player_bodygroups" ) )
+ {
+ FOR_EACH_SUBKEY( pKVEntry, pKVBodygroupKey )
+ {
+ const char *pszBodygroupName = pKVBodygroupKey->GetName();
+ int iValue = pKVBodygroupKey->GetInt();
+
+ // Track bodygroup information for this item in particular.
+ pVisData->m_Maps.m_ModifiedBodyGroupNames.Insert( pszBodygroupName, iValue );
+
+ // Track global schema state.
+ GetItemSchema()->AssignDefaultBodygroupState( pszBodygroupName, iValue );
+ }
+ }
+ else if ( !Q_stricmp( pszEntry, "skin" ) )
+ {
+ pVisData->iSkin = pKVEntry->GetInt();
+ }
+ else if ( !Q_stricmp( pszEntry, "use_per_class_bodygroups" ) )
+ {
+ pVisData->bUsePerClassBodygroups = pKVEntry->GetBool();
+ }
+ else if ( !Q_stricmp( pszEntry, "muzzle_flash" ) )
+ {
+ pVisData->pszMuzzleFlash = pKVEntry->GetString();
+ }
+ else if ( !Q_stricmp( pszEntry, "tracer_effect" ) )
+ {
+ pVisData->pszTracerEffect = pKVEntry->GetString();
+ }
+ else if ( !Q_stricmp( pszEntry, "particle_effect" ) )
+ {
+ pVisData->pszParticleEffect = pKVEntry->GetString();
+ }
+ else if ( !Q_strnicmp( pszEntry, "custom_sound", 12 ) ) // intentionally comparing prefixes
+ {
+ int iIndex = 0;
+ if ( pszEntry[12] )
+ {
+ iIndex = clamp( atoi( &pszEntry[12] ), 0, MAX_VISUALS_CUSTOM_SOUNDS-1 );
+ }
+ pVisData->pszCustomSounds[iIndex] = pKVEntry->GetString();
+ }
+ else if ( !Q_stricmp( pszEntry, "material_override" ) )
+ {
+ pVisData->pszMaterialOverride = pKVEntry->GetString();
+ }
+ else if ( !Q_strnicmp( pszEntry, "sound_", 6 ) ) // intentionally comparing prefixes
+ {
+ int iIndex = GetWeaponSoundFromString( &pszEntry[6] );
+ if ( iIndex != -1 )
+ {
+ pVisData->pszWeaponSoundReplacements[iIndex] = pKVEntry->GetString();
+ }
+ }
+ else if ( !Q_stricmp( pszEntry, "code_controlled_bodygroup" ) )
+ {
+ const char *pBodyGroupName = pKVEntry->GetString( "bodygroup", NULL );
+ const char *pFuncName = pKVEntry->GetString( "function", NULL );
+ if ( pBodyGroupName && pFuncName )
+ {
+ codecontrolledbodygroupdata_t ccbgd = { pFuncName, NULL };
+ pVisData->m_Maps.m_CodeControlledBodyGroupNames.Insert( pBodyGroupName, ccbgd );
+ }
+ }
+ else if ( !Q_stricmp( pszEntry, "vm_bodygroup_override" ) )
+ {
+ pVisData->m_iViewModelBodyGroupOverride = pKVEntry->GetInt();
+ }
+ else if ( !Q_stricmp( pszEntry, "vm_bodygroup_state_override" ) )
+ {
+ pVisData->m_iViewModelBodyGroupStateOverride = pKVEntry->GetInt();
+ }
+ else if ( !Q_stricmp( pszEntry, "wm_bodygroup_override" ) )
+ {
+ pVisData->m_iWorldModelBodyGroupOverride = pKVEntry->GetInt();
+ }
+ else if ( !Q_stricmp( pszEntry, "wm_bodygroup_state_override" ) )
+ {
+ pVisData->m_iWorldModelBodyGroupStateOverride = pKVEntry->GetInt();
+ }
+
+ pKVEntry = pKVEntry->GetNextKey();
+ }
+#endif // defined(CLIENT_DLL) || defined(GAME_DLL)
+ KeyValues *pStylesDataKV = pVisualsKV->FindKey( "styles" );
+ if ( pStylesDataKV )
+ {
+ // Styles are only valid in the base "visuals" section.
+ if ( team == 0 )
+ {
+ BInitStylesBlockFromKV( pStylesDataKV, pVisData, pVecErrors );
+ }
+ // ...but they used to be valid everywhere, so spit out a warning if people are trying to use
+ // the old style of per-team styles.
+ else
+ {
+ pVecErrors->AddToTail( "Per-team styles blocks are no longer valid. Use \"skin_red\" and \"skin_blu\" in a style entry instead." );
+ }
+ }
+
+ m_PerTeamVisuals[team] = pVisData;
+ }
+ }
+}
+
+#ifdef GC_DLL
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+template < typename T >
+static void NthPermutation ( T *pData, unsigned int unDataCount, unsigned int unIdx )
+{
+ for ( unsigned int i = 1; i < unDataCount; i++ )
+ {
+ std::swap( pData[ unIdx % (i + 1) ], pData[ i ] );
+ unIdx = unIdx / (i + 1);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CEconItemDefinition::BApplyPropertyGenerators( CEconItem *pItem ) const
+{
+ Assert( pItem );
+
+ for ( const IEconItemPropertyGenerator *pPropertyGenerator : m_vecPropertyGenerators )
+ {
+ if ( !pPropertyGenerator->BGenerateProperties( pItem ) )
+ return false;
+ }
+
+ return true;
+}
+
+#endif // GC_DLL
+
+#if defined(CLIENT_DLL) || defined(GAME_DLL)
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CEconItemDefinition::GeneratePrecacheModelStrings( bool bDynamicLoad, CUtlVector<const char *> *out_pVecModelStrings ) const
+{
+ Assert( out_pVecModelStrings );
+
+ // Add base model.
+ out_pVecModelStrings->AddToTail( GetBasePlayerDisplayModel() );
+
+ // Add styles.
+ if ( GetNumStyles() )
+ {
+ for ( style_index_t i=0; i<GetNumStyles(); ++i )
+ {
+ const CEconStyleInfo *pStyle = GetStyleInfo( i );
+ Assert( pStyle );
+
+ pStyle->GeneratePrecacheModelStringsForStyle( out_pVecModelStrings );
+ }
+ }
+
+ // Precache all the attached models
+ for ( int team = 0; team < TEAM_VISUAL_SECTIONS; team++ )
+ {
+ perteamvisuals_t *pPerTeamVisuals = GetPerTeamVisual( team );
+ if ( !pPerTeamVisuals )
+ continue;
+
+ for ( int model = 0; model < pPerTeamVisuals->m_AttachedModels.Count(); model++ )
+ {
+ out_pVecModelStrings->AddToTail( pPerTeamVisuals->m_AttachedModels[model].m_pszModelName );
+ }
+
+ // Festive
+ for ( int model = 0; model < pPerTeamVisuals->m_AttachedModelsFestive.Count(); model++ )
+ {
+ out_pVecModelStrings->AddToTail( pPerTeamVisuals->m_AttachedModelsFestive[model].m_pszModelName );
+ }
+ }
+
+ if ( GetExtraWearableModel() )
+ {
+ out_pVecModelStrings->AddToTail( GetExtraWearableModel() );
+ }
+
+ if ( GetExtraWearableViewModel() )
+ {
+ out_pVecModelStrings->AddToTail( GetExtraWearableViewModel() );
+ }
+
+ if ( GetVisionFilteredDisplayModel() )
+ {
+ out_pVecModelStrings->AddToTail( GetVisionFilteredDisplayModel() );
+ }
+
+ // We don't need to cache the inventory model, because it's never loaded by the game
+}
+
+void CEconItemDefinition::GeneratePrecacheSoundStrings( bool bDynamicLoad, CUtlVector<const char *> *out_pVecSoundStrings ) const
+{
+ Assert( out_pVecSoundStrings );
+
+ for ( int iTeam = 0; iTeam < TEAM_VISUAL_SECTIONS; ++iTeam )
+ {
+ for ( int iSound = 0; iSound < MAX_VISUALS_CUSTOM_SOUNDS; ++iSound )
+ {
+ const char *pSoundName = GetCustomSound( iTeam, iSound );
+ if ( pSoundName && pSoundName[ 0 ] != '\0' )
+ {
+ out_pVecSoundStrings->AddToTail( pSoundName );
+ }
+ }
+ }
+}
+#endif // #if defined(CLIENT_DLL) || defined(GAME_DLL)
+
+//-----------------------------------------------------------------------------
+const char *CEconItemDefinition::GetDefinitionString( const char *pszKeyName, const char *pszDefaultValue ) const
+{
+ // !FIXME! Here we could do a dynamic lookup to apply the prefab overlay logic.
+ // This could save a lot of duplicated data
+ if ( m_pKVItem )
+ return m_pKVItem->GetString( pszKeyName, pszDefaultValue );
+ return pszDefaultValue;
+}
+
+//-----------------------------------------------------------------------------
+KeyValues *CEconItemDefinition::GetDefinitionKey( const char *pszKeyName ) const
+{
+ // !FIXME! Here we could do a dynamic lookup to apply the prefab overlay logic.
+ // This could save a lot of duplicated data
+ if ( m_pKVItem )
+ return m_pKVItem->FindKey( pszKeyName );
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Parse the styles sub-section of the visuals block.
+//-----------------------------------------------------------------------------
+void CEconItemDefinition::BInitStylesBlockFromKV( KeyValues *pKVStyles, perteamvisuals_t *pVisData, CUtlVector<CUtlString> *pVecErrors )
+{
+ FOR_EACH_SUBKEY( pKVStyles, pKVStyle )
+ {
+ CEconStyleInfo *pStyleInfo = GetItemSchema()->CreateEconStyleInfo();
+ Assert( pStyleInfo );
+
+ pStyleInfo->BInitFromKV( pKVStyle, pVecErrors );
+
+ pVisData->m_Styles.AddToTail( pStyleInfo );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Parse one style from the styles block.
+//-----------------------------------------------------------------------------
+void CEconStyleInfo::BInitFromKV( KeyValues *pKVStyle, CUtlVector<CUtlString> *pVecErrors )
+{
+ enum { kInvalidSkinKey = -1, };
+
+ Assert( pKVStyle );
+
+ // A "skin" entry means "use this index for all of our teams, no matter how many we have".
+ int iCommonSkin = pKVStyle->GetInt( "skin", kInvalidSkinKey );
+ if ( iCommonSkin != kInvalidSkinKey )
+ {
+ for ( int i = 0; i < TEAM_VISUAL_SECTIONS; i++ )
+ {
+ m_iSkins[i] = iCommonSkin;
+ }
+ }
+
+ int iCommonViewmodelSkin = pKVStyle->GetInt( "v_skin", kInvalidSkinKey );
+ if ( iCommonViewmodelSkin != kInvalidSkinKey )
+ {
+ for ( int i=0; i<TEAM_VISUAL_SECTIONS; i++ )
+ {
+ m_iViewmodelSkins[i] = iCommonViewmodelSkin;
+ }
+ }
+
+ // If we don't have a base entry, we look for a unique entry for each team. This will be
+ // handled in a subclass if necessary.
+
+ // Are we hiding additional bodygroups when this style is active?
+ KeyValues *pKVHideBodygroups = pKVStyle->FindKey( "additional_hidden_bodygroups" );
+ if ( pKVHideBodygroups )
+ {
+ FOR_EACH_SUBKEY( pKVHideBodygroups, pKVBodygroup )
+ {
+ m_vecAdditionalHideBodygroups.AddToTail( pKVBodygroup->GetName() );
+ }
+ }
+
+ // Remaining common properties.
+ m_pszName = pKVStyle->GetString( "name", "#TF_UnknownStyle" );
+ m_pszBasePlayerModel = pKVStyle->GetString( "model_player", NULL );
+ m_bIsSelectable = pKVStyle->GetBool( "selectable", true );
+ m_pszInventoryImage = pKVStyle->GetString( "image_inventory", NULL );
+
+ KeyValues *pKVBodygroup = pKVStyle->FindKey( "bodygroup" );
+ if ( pKVBodygroup )
+ {
+ m_pszBodygroupName = pKVBodygroup->GetString( "name", NULL );
+ Assert( m_pszBodygroupName );
+ m_iBodygroupSubmodelIndex = pKVBodygroup->GetInt( "submodel_index", -1 );
+ Assert( m_iBodygroupSubmodelIndex != -1 );
+ }
+}
+
+#if defined(CLIENT_DLL) || defined(GAME_DLL)
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CEconStyleInfo::GeneratePrecacheModelStringsForStyle( CUtlVector<const char *> *out_pVecModelStrings ) const
+{
+ Assert( out_pVecModelStrings );
+
+ if ( GetBasePlayerDisplayModel() != NULL )
+ {
+ out_pVecModelStrings->AddToTail( GetBasePlayerDisplayModel() );
+ }
+}
+#endif
+
+//-----------------------------------------------------------------------------
+// Purpose: Item definition initialization helpers.
+//-----------------------------------------------------------------------------
+static void RecursiveInheritKeyValues( KeyValues *out_pValues, KeyValues *pInstance )
+{
+ KeyValues *pPrevSubKey = NULL;
+ for ( KeyValues * pSubKey = pInstance->GetFirstSubKey(); pSubKey != NULL; pPrevSubKey = pSubKey, pSubKey = pSubKey->GetNextKey() )
+ {
+ // If this assert triggers, you have an item that uses a prefab but has multiple keys with the same name
+ AssertMsg2 ( !pPrevSubKey || pPrevSubKey->GetNameSymbol() != pSubKey->GetNameSymbol(),
+ "Item definition \"%s\" has multiple attributes of the same name (%s) can't use prefabs", pInstance->GetName(), pSubKey->GetName() );
+
+ KeyValues::types_t eType = pSubKey->GetDataType();
+ switch ( eType )
+ {
+ case KeyValues::TYPE_STRING: out_pValues->SetString( pSubKey->GetName(), pSubKey->GetString() ); break;
+ case KeyValues::TYPE_INT: out_pValues->SetInt( pSubKey->GetName(), pSubKey->GetInt() ); break;
+ case KeyValues::TYPE_FLOAT: out_pValues->SetFloat( pSubKey->GetName(), pSubKey->GetFloat() ); break;
+ case KeyValues::TYPE_WSTRING: out_pValues->SetWString( pSubKey->GetName(), pSubKey->GetWString() ); break;
+ case KeyValues::TYPE_COLOR: out_pValues->SetColor( pSubKey->GetName(), pSubKey->GetColor() ) ; break;
+ case KeyValues::TYPE_UINT64: out_pValues->SetUint64( pSubKey->GetName(), pSubKey->GetUint64() ) ; break;
+
+ // "NONE" means "KeyValues"
+ case KeyValues::TYPE_NONE:
+ {
+ // We may already have this part of the tree to stuff data into/overwrite, or we
+ // may have to make a new block.
+ KeyValues *pNewChild = out_pValues->FindKey( pSubKey->GetName() );
+ if ( !pNewChild )
+ {
+ pNewChild = out_pValues->CreateNewKey();
+ pNewChild->SetName( pSubKey->GetName() );
+ }
+
+ RecursiveInheritKeyValues( pNewChild, pSubKey );
+ break;
+ }
+
+ case KeyValues::TYPE_PTR:
+ default:
+ Assert( !"Unhandled data type for KeyValues inheritance!" );
+ break;
+ }
+ }
+}
+
+void MergeDefinitionPrefab( KeyValues *pKVWriteItem, KeyValues *pKVSourceItem )
+{
+ Assert( pKVWriteItem );
+ Assert( pKVSourceItem );
+
+ const char *svPrefabName = pKVSourceItem->GetString( "prefab", NULL );
+
+ if ( svPrefabName )
+ {
+ CUtlStringList vecPrefabs;
+
+ Q_SplitString( svPrefabName, " ", vecPrefabs );
+
+ // Iterate backwards so adjectives get applied over the noun prefab
+ // e.g. wet scared cat would apply cat first, then scared and wet.
+ FOR_EACH_VEC_BACK( vecPrefabs, i )
+ {
+ KeyValues *pKVPrefab = GetItemSchema()->FindDefinitionPrefabByName( vecPrefabs[i] );
+ AssertMsg1( pKVPrefab, "Unable to find prefab \"%s\".", vecPrefabs[i] );
+
+ if ( pKVPrefab )
+ {
+ MergeDefinitionPrefab( pKVWriteItem, pKVPrefab );
+ }
+ }
+ }
+
+ RecursiveInheritKeyValues( pKVWriteItem, pKVSourceItem );
+}
+
+KeyValues *CEconItemSchema::FindDefinitionPrefabByName( const char *pszPrefabName ) const
+{
+ int iIndex = m_mapDefinitionPrefabs.Find( pszPrefabName );
+ if ( m_mapDefinitionPrefabs.IsValidIndex( iIndex ) )
+ return m_mapDefinitionPrefabs[iIndex];
+
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+const char *CEconItemSchema::FindStringTableEntry( const char *pszTableName, int iIndex ) const
+{
+ SchemaStringTableDict_t::IndexType_t i = m_dictStringTable.Find( pszTableName );
+ if ( !m_dictStringTable.IsValidIndex( i ) )
+ return NULL;
+
+ const CUtlVector< schema_string_table_entry_t >& vec = *m_dictStringTable[i];
+ FOR_EACH_VEC( vec, j )
+ {
+ if ( vec[j].m_iIndex == iIndex )
+ return vec[j].m_pszStr;
+ }
+
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Initialize the item definition
+// Input: pKVItem - The KeyValues representation of the item
+// schema - The overall item schema for this item
+// pVecErrors - An optional vector that will contain error messages if
+// the init fails.
+// Output: True if initialization succeeded, false otherwise
+//-----------------------------------------------------------------------------
+#ifdef GC_DLL
+ GCConVar gc_steam_payment_rules_kv_key( "gc_steam_payment_rules_kv_key", "payment_rules" );
+#endif // GC_DLL
+
+
+#if defined( WITH_STREAMABLE_WEAPONS )
+#if defined( CLIENT_DLL )
+ ConVar tf_loadondemand_default("cl_loadondemand_default", "1", FCVAR_ARCHIVE | FCVAR_CLIENTDLL, "The default value for whether items should be delay loaded (1) or loaded now (0).");
+#elif defined( GAME_DLL )
+ // The server doesn't load on demand by default because it can crash sometimes when this is set. We need to run that down, but in the meantime
+ // we just have it load on demand.
+ ConVar tf_loadondemand_default("sv_loadondemand_default", "0", FCVAR_ARCHIVE | FCVAR_GAMEDLL, "The default value for whether items should be delay loaded (1) or loaded now (0).");
+#elif defined( GC_DLL )
+ GCConVar tf_loadondemand_default("gc_loadondemand_default", "1", FCVAR_ARCHIVE, "The default value for whether items should be delay loaded (1) or loaded now (0).");
+#else
+#error "Need to add support for streamable weapons to this configuration, or disable streamable weapons here."
+#endif
+#endif // WITH_STREAMABLE_WEAPONS
+
+bool CEconItemDefinition::BInitFromKV( KeyValues *pKVItem, CUtlVector<CUtlString> *pVecErrors /* = NULL */ )
+{
+ // Set standard members
+ m_pKVItem = new KeyValues( pKVItem->GetName() );
+ MergeDefinitionPrefab( m_pKVItem, pKVItem );
+ m_bEnabled = m_pKVItem->GetBool( "enabled" );
+
+ // initializing this one first so that it will be available for all the errors below
+ m_pszDefinitionName = m_pKVItem->GetString( "name", NULL );
+
+#if defined( WITH_STREAMABLE_WEAPONS )
+ bool bGotDefault = false;
+ m_bLoadOnDemand = m_pKVItem->GetBool( "loadondemand", tf_loadondemand_default.GetBool(), &bGotDefault );
+
+ // This logging is useful for tracking down bugs that crop up because we've (possibly) swapped the default value for loadondemand.
+ // But it can be removed once we're satisfied there aren't any bugs as a result of the change (when we cleanup WITH_STREAMABLE_WEAPONS).
+ if (bGotDefault)
+ {
+ DevMsg(10, "Item %s received default value for loadondemand\n", m_pszDefinitionName);
+ }
+#else
+ // Keep the old behavior, which is that loadondemand is defaulted to false.
+ m_bLoadOnDemand = m_pKVItem->GetBool("loadondemand");
+#endif
+
+ m_nDefIndex = Q_atoi( m_pKVItem->GetName() );
+ m_unMinItemLevel = (uint32)m_pKVItem->GetInt( "min_ilevel", GetItemSchema()->GetMinLevel() );
+ m_unMaxItemLevel = (uint32)m_pKVItem->GetInt( "max_ilevel", GetItemSchema()->GetMaxLevel() );
+ m_nDefaultDropQuantity = m_pKVItem->GetInt( "default_drop_quantity", 1 );
+
+ m_nPopularitySeed = m_pKVItem->GetInt( "popularity_seed", 0 );
+
+
+#if defined(CLIENT_DLL) || defined(GAME_DLL)
+ // We read this manually here in the game dlls. The GC reads it below while checking the global schema.
+ GetItemSchema()->BGetItemQualityFromName( m_pKVItem->GetString( "item_quality" ), &m_nItemQuality );
+ GetItemSchema()->BGetItemQualityFromName( m_pKVItem->GetString( "forced_item_quality" ), &m_nForcedItemQuality );
+#endif
+
+ // Check for required fields
+ SCHEMA_INIT_CHECK(
+ NULL != m_pKVItem->FindKey( "name" ),
+ "Item definition %s: Missing required field \"name\"", m_pKVItem->GetName() );
+
+ SCHEMA_INIT_CHECK(
+ NULL != m_pKVItem->FindKey( "item_class" ),
+ "Item definition %s: Missing required field \"item_class\"", m_pKVItem->GetName() );
+
+ // Check value ranges
+ SCHEMA_INIT_CHECK(
+ m_pKVItem->GetInt( "min_ilevel" ) >= 0,
+ "Item definition %s: \"min_ilevel\" must be greater than or equal to 0", GetDefinitionName() );
+
+ SCHEMA_INIT_CHECK(
+ m_pKVItem->GetInt( "max_ilevel" ) >= 0,
+ "Item definition %s: \"max_ilevel\" must be greater than or equal to 0", GetDefinitionName() );
+
+ // Check for consistency
+#ifdef GC_DLL
+ // We don't do these consistency checks in the game, because it doesn't have the data to do them
+ SCHEMA_INIT_CHECK(
+ m_unMinItemLevel >= GetItemSchema()->GetMinLevel(),
+ "Item definition %s: min_ilevel (%d) must be greater or equal to Minimum Item Level (%d)", GetDefinitionName(), m_unMinItemLevel, GetItemSchema()->GetMinLevel() );
+
+ SCHEMA_INIT_CHECK(
+ m_unMinItemLevel <= m_unMaxItemLevel,
+ "Item definition %s: min_ilevel (%d) must be greater or equal to min_ilevel (%d)", GetDefinitionName(), m_unMaxItemLevel, m_unMinItemLevel );
+
+ SCHEMA_INIT_CHECK(
+ m_unMaxItemLevel <= GetItemSchema()->GetMaxLevel(),
+ "Item definition %s: max_ilevel (%d) must be less than or equal to Maximum Item Level (%d)", GetDefinitionName(), m_unMaxItemLevel, GetItemSchema()->GetMaxLevel() );
+
+ // Read the item quality
+ if ( m_pKVItem->FindKey( "item_quality" ) )
+ {
+ SCHEMA_INIT_CHECK(
+ GetItemSchema()->BGetItemQualityFromName( m_pKVItem->GetString( "item_quality" ), &m_nItemQuality ),
+ "Item definition %s: Undefined item_quality \"%s\"", GetDefinitionName(), m_pKVItem->GetString( "item_quality" ) );
+ }
+ if ( m_pKVItem->FindKey( "forced_item_quality" ) )
+ {
+ SCHEMA_INIT_CHECK(
+ GetItemSchema()->BGetItemQualityFromName( m_pKVItem->GetString( "forced_item_quality" ), &m_nForcedItemQuality ),
+ "Item definition %s: Undefined item_quality \"%s\"", GetDefinitionName(), m_pKVItem->GetString( "forced_item_quality" ) );
+ }
+#endif
+
+ // Rarity
+ // Get Index from this string and save the index
+ if ( m_pKVItem->FindKey( "item_rarity" ) )
+ {
+ SCHEMA_INIT_CHECK(
+ GetItemSchema()->BGetItemRarityFromName( m_pKVItem->GetString( "item_rarity" ), &m_nItemRarity ),
+ "Item definition %s: Undefined item_rarity \"%s\"", GetDefinitionName(), m_pKVItem->GetString( "item_rarity" ) );
+ }
+
+ if ( m_pKVItem->FindKey( "item_series" ) )
+ {
+ // Make sure this is a valid series
+ SCHEMA_INIT_CHECK(
+ GetItemSchema()->BGetItemSeries( m_pKVItem->GetString( "item_series" ), &m_unItemSeries ),
+ "Item definition %s: Undefined item_series \"%s\"", GetDefinitionName(), m_pKVItem->GetString( "item_series" ) );
+ }
+
+ // Get the item class
+ m_pszItemClassname = m_pKVItem->GetString( "item_class", NULL );
+
+ m_pszClassToken = m_pKVItem->GetString( "class_token_id", NULL );
+ m_pszSlotToken = m_pKVItem->GetString( "slot_token_id", NULL );
+
+ // expiration data
+ const char *pchExpiration = m_pKVItem->GetString( "expiration_date", NULL );
+ if( pchExpiration && pchExpiration[0] )
+ {
+ if ( pchExpiration[0] == '!' )
+ {
+ m_rtExpiration = GetItemSchema()->GetCustomExpirationDate( &pchExpiration[1] );
+ SCHEMA_INIT_CHECK(
+ m_rtExpiration != k_RTime32Nil,
+ "Unknown/malformed expiration_date string \"%s\" in item %s.", pchExpiration, m_pszDefinitionName );
+ }
+ else
+ {
+ m_rtExpiration = CRTime::RTime32FromFmtString( "YYYY-MM-DD hh:mm:ss" , pchExpiration );
+
+#ifdef GC_DLL
+ // Check that if we convert back to a string, we get the same value
+ char rtimeBuf[k_RTimeRenderBufferSize];
+ SCHEMA_INIT_CHECK(
+ Q_strcmp( CRTime::RTime32ToString( m_rtExpiration, rtimeBuf ), pchExpiration ) == 0,
+ "Malformed expiration_date \"%s\" for expiration_date in item %s. Must be of the form \"YYYY-MM-DD hh:mm:ss\". Input: %s Output: %s InputTime: %u LocalTime: %u Timezone: %lu", pchExpiration, m_pszDefinitionName, pchExpiration, rtimeBuf, m_rtExpiration, CRTime::RTime32TimeCur(), timezone );
+#else
+ // Check that if we convert back to a string, we get the same value. Emit an error, but don't fail in the game code
+ char rtimeBuf[k_RTimeRenderBufferSize];
+ if ( Q_strcmp( CRTime::RTime32ToString( m_rtExpiration, rtimeBuf ), pchExpiration ) != 0 )
+ {
+#if ( defined( _MSC_VER ) && _MSC_VER >= 1900 )
+#define timezone _timezone
+#define daylight _daylight
+#endif
+ Assert( false );
+ Warning( "Malformed expiration_date \"%s\" for expiration_date in item %s. Must be of the form \"YYYY-MM-DD hh:mm:ss\". Input: %s Output: %s InputTime: %u LocalTime: %u Timezone: %lu\n", pchExpiration, m_pszDefinitionName, pchExpiration, rtimeBuf, m_rtExpiration, CRTime::RTime32TimeCur(), timezone );
+ }
+#endif
+ }
+ }
+
+ // Display data
+ m_pszItemBaseName = m_pKVItem->GetString( "item_name", "" ); // non-NULL to ensure we can sort
+ m_pszItemTypeName = m_pKVItem->GetString( "item_type_name", "" ); // non-NULL to ensure we can sort
+ m_pszItemDesc = m_pKVItem->GetString( "item_description", NULL );
+ m_pszArmoryDesc = m_pKVItem->GetString( "armory_desc", NULL );
+ m_pszInventoryModel = m_pKVItem->GetString( "model_inventory", NULL );
+ m_pszInventoryImage = m_pKVItem->GetString( "image_inventory", NULL );
+
+ const char* pOverlay = m_pKVItem->GetString( "image_inventory_overlay", NULL );
+ if ( pOverlay )
+ {
+ m_pszInventoryOverlayImages.AddToTail( pOverlay );
+ }
+ pOverlay = m_pKVItem->GetString( "image_inventory_overlay2", NULL );
+ if ( pOverlay )
+ {
+ m_pszInventoryOverlayImages.AddToTail( pOverlay );
+ }
+
+ m_iInventoryImagePosition[0] = atoi( m_pKVItem->GetString( "image_inventory_pos_x", "0" ) );
+ m_iInventoryImagePosition[1] = atoi( m_pKVItem->GetString( "image_inventory_pos_y", "0" ) );
+ m_iInventoryImageSize[0] = atoi( m_pKVItem->GetString( "image_inventory_size_w", "128" ) );
+ m_iInventoryImageSize[1] = atoi( m_pKVItem->GetString( "image_inventory_size_h", "82" ) );
+ m_iInspectPanelDistance = m_pKVItem->GetInt( "inspect_panel_dist", 70 );
+ m_pszHolidayRestriction = m_pKVItem->GetString( "holiday_restriction", NULL );
+ m_nVisionFilterFlags = m_pKVItem->GetInt( "vision_filter_flags", 0 );
+ m_iSubType = atoi( m_pKVItem->GetString( "subtype", "0" ) );
+ m_pszBaseDisplayModel = m_pKVItem->GetString( "model_player", NULL );
+ m_iDefaultSkin = m_pKVItem->GetInt( "default_skin", -1 );
+ m_pszWorldDisplayModel = m_pKVItem->GetString( "model_world", NULL ); // Not the ideal method. c_models are better, but this is to solve a retrofit problem with the sticky launcher.
+ m_pszWorldExtraWearableModel = m_pKVItem->GetString( "extra_wearable", NULL );
+ m_pszWorldExtraWearableViewModel = m_pKVItem->GetString( "extra_wearable_vm", NULL );
+ m_pszVisionFilteredDisplayModel = pKVItem->GetString( "model_vision_filtered", NULL );
+ m_pszBrassModelOverride = m_pKVItem->GetString( "brass_eject_model", NULL );
+ m_bHideBodyGroupsDeployedOnly = m_pKVItem->GetBool( "hide_bodygroups_deployed_only" );
+ m_bAttachToHands = m_pKVItem->GetInt( "attach_to_hands", 0 ) != 0;
+ m_bAttachToHandsVMOnly = m_pKVItem->GetInt( "attach_to_hands_vm_only", 0 ) != 0;
+ m_bProperName = m_pKVItem->GetInt( "propername", 0 ) != 0;
+ m_bFlipViewModel = m_pKVItem->GetInt( "flip_viewmodel", 0 ) != 0;
+ m_bActAsWearable = m_pKVItem->GetInt( "act_as_wearable", 0 ) != 0;
+ m_bActAsWeapon = m_pKVItem->GetInt( "act_as_weapon", 0 ) != 0;
+ m_bIsTool = m_pKVItem->GetBool( "is_tool", 0 ) || ( GetItemClass() && !V_stricmp( GetItemClass(), "tool" ) );
+ m_iDropType = StringFieldToInt( m_pKVItem->GetString("drop_type"), g_szDropTypeStrings, ARRAYSIZE(g_szDropTypeStrings) );
+ m_pszCollectionReference = m_pKVItem->GetString( "collection_reference", NULL );
+
+ // Creation data
+ m_bHidden = m_pKVItem->GetInt( "hidden", 0 ) != 0;
+ m_bShouldShowInArmory = m_pKVItem->GetInt( "show_in_armory", 0 ) != 0;
+ m_bBaseItem = m_pKVItem->GetInt( "baseitem", 0 ) != 0;
+ m_pszItemLogClassname = m_pKVItem->GetString( "item_logname", NULL );
+ m_pszItemIconClassname = m_pKVItem->GetString( "item_iconname", NULL );
+ m_pszDatabaseAuditTable = m_pKVItem->GetString( "database_audit_table", NULL );
+ m_bImported = m_pKVItem->FindKey( "import_from" ) != NULL;
+
+ // Tool data
+ m_pTool = NULL;
+ KeyValues *pToolDataKV = m_pKVItem->FindKey( "tool" );
+ if ( pToolDataKV )
+ {
+ const char *pszType = pToolDataKV->GetString( "type", NULL );
+ SCHEMA_INIT_CHECK( pszType != NULL, "Tool '%s' missing required type.", m_pKVItem->GetName() );
+
+ // Common-to-all-tools settings.
+ const char *pszUseString = pToolDataKV->GetString( "use_string", NULL );
+ const char *pszUsageRestriction = pToolDataKV->GetString( "restriction", NULL );
+ KeyValues *pToolUsageKV = pToolDataKV->FindKey( "usage" );
+
+ // Common-to-all-tools usage capability flags.
+ item_capabilities_t usageCapabilities = (item_capabilities_t)ITEM_CAP_TOOL_DEFAULT;
+ KeyValues *pToolUsageCapsKV = pToolDataKV->FindKey( "usage_capabilities" );
+ if ( pToolUsageCapsKV )
+ {
+ KeyValues *pEntry = pToolUsageCapsKV->GetFirstSubKey();
+ while ( pEntry )
+ {
+ ParseCapability( usageCapabilities, pEntry );
+ pEntry = pEntry->GetNextKey();
+ }
+ }
+
+ m_pTool = GetItemSchema()->CreateEconToolImpl( pszType, pszUseString, pszUsageRestriction, usageCapabilities, pToolUsageKV );
+ SCHEMA_INIT_CHECK( m_pTool != NULL, "Unable to create tool implementation for '%s', of type '%s'.", m_pKVItem->GetName(), pszType );
+ }
+
+ // Bundle
+ KeyValues *pBundleDataKV = m_pKVItem->FindKey( "bundle" );
+ if ( pBundleDataKV )
+ {
+ m_BundleInfo = new bundleinfo_t();
+ FOR_EACH_SUBKEY( pBundleDataKV, pKVCurItem )
+ {
+ CEconItemDefinition *pItemDef = GetItemSchema()->GetItemDefinitionByName( pKVCurItem->GetName() );
+ SCHEMA_INIT_CHECK( pItemDef != NULL, "Unable to find item definition '%s' for bundle '%s'.", pKVCurItem->GetName(), m_pszDefinitionName );
+
+ m_BundleInfo->vecItemDefs.AddToTail( pItemDef );
+ }
+
+ // Only check for pack bundle if the item is actually a bundle - note that we could do this programatically by checking that all items in the bundle are flagged as a "pack item" - but for now the bundle needs to be explicitly flagged as a pack bundle.
+ m_bIsPackBundle = m_pKVItem->GetInt( "is_pack_bundle", 0 ) != 0;
+ }
+
+ // capabilities
+ m_iCapabilities = (item_capabilities_t)ITEM_CAP_DEFAULT;
+ KeyValues *pCapsKV = m_pKVItem->FindKey( "capabilities" );
+ if ( pCapsKV )
+ {
+ KeyValues *pEntry = pCapsKV->GetFirstSubKey();
+ while ( pEntry )
+ {
+ ParseCapability( m_iCapabilities, pEntry );
+ pEntry = pEntry->GetNextKey();
+ }
+ }
+
+ // item_set
+ SCHEMA_INIT_CHECK( (!m_pKVItem->GetString( "item_set", NULL )), "Item definition '%s' specifies deprecated \"item_set\" field. Items sets are now specified only in the set itself, not on the definition.", GetDefinitionName() );
+
+ const char *pszSetItemRemapDefIndexName = m_pKVItem->GetString( "set_item_remap", NULL );
+ if ( pszSetItemRemapDefIndexName )
+ {
+ const CEconItemDefinition *pRemapItemDef = GetItemSchema()->GetItemDefinitionByName( pszSetItemRemapDefIndexName );
+ m_unSetItemRemapDefIndex = pRemapItemDef ? pRemapItemDef->GetDefinitionIndex() : INVALID_ITEM_DEF_INDEX;
+
+ SCHEMA_INIT_CHECK( m_unSetItemRemapDefIndex != INVALID_ITEM_DEF_INDEX, "Unable to find set item remap definition '%s' for '%s'.", pszSetItemRemapDefIndexName, GetDefinitionName() );
+ SCHEMA_INIT_CHECK( m_unSetItemRemapDefIndex != GetDefinitionIndex(), "Unable to set set item remap for definition '%s' to itself.", GetDefinitionName() );
+ }
+ else
+ {
+ m_unSetItemRemapDefIndex = GetDefinitionIndex();
+ }
+
+ // cache item map names
+ m_pszArmoryRemap = m_pKVItem->GetString( "armory_remap", NULL );
+ m_pszStoreRemap = m_pKVItem->GetString( "store_remap", NULL );
+
+ m_pszXifierRemapClass = m_pKVItem->GetString( "xifier_class_remap", NULL );
+ m_pszBaseFunctionalItemName = m_pKVItem->GetString( "base_item_name", "" );
+ m_pszParticleSuffix = m_pKVItem->GetString( "particle_suffix", NULL );
+
+ m_bValidForShuffle = m_pKVItem->GetBool( "valid_for_shuffle", false );
+ m_bValidForSelfMade = m_pKVItem->GetBool( "valid_for_self_made", true );
+
+ // Init our visuals blocks.
+ BInitVisualBlockFromKV( m_pKVItem, pVecErrors );
+
+ // Calculate our equip region mask.
+ {
+ m_unEquipRegionMask = 0;
+ m_unEquipRegionConflictMask = 0;
+
+ // Our equip region will come from one of two places -- either we have an "equip_regions" (plural) section,
+ // in which case we have any number of regions specified; or we have an "equip_region" (singular) section
+ // which will have one and exactly one region. If we have "equip_regions" (plural), we ignore whatever is
+ // in "equip_region" (singular).
+ //
+ // Yes, this is sort of dumb.
+ CUtlVector<const char *> vecEquipRegionNames;
+
+ KeyValues *pKVMultiEquipRegions = m_pKVItem->FindKey( "equip_regions" ),
+ *pKVSingleEquipRegion = m_pKVItem->FindKey( "equip_region" );
+
+ // Maybe we have multiple entries?
+ if ( pKVMultiEquipRegions )
+ {
+ for ( KeyValues *pKVRegion = pKVMultiEquipRegions->GetFirstSubKey(); pKVRegion; pKVRegion = pKVRegion->GetNextKey() )
+ {
+ vecEquipRegionNames.AddToTail( pKVRegion->GetName() );
+ }
+ }
+ // This is our one-and-only-one equip region.
+ else if ( pKVSingleEquipRegion )
+ {
+ const char *pEquipRegionName = pKVSingleEquipRegion->GetString( (const char *)NULL, NULL );
+ if ( pEquipRegionName )
+ {
+ vecEquipRegionNames.AddToTail( pEquipRegionName );
+ }
+ }
+
+ // For each of our regions, add to our conflict mask both ourself and all the regions
+ // that we conflict with.
+ FOR_EACH_VEC( vecEquipRegionNames, i )
+ {
+ const char *pszEquipRegionName = vecEquipRegionNames[i];
+ equip_region_mask_t unThisRegionMask = GetItemSchema()->GetEquipRegionMaskByName( pszEquipRegionName );
+
+ SCHEMA_INIT_CHECK(
+ unThisRegionMask != 0,
+ "Item definition %s: Unable to find equip region mask for region named \"%s\"", GetDefinitionName(), vecEquipRegionNames[i] );
+
+ m_unEquipRegionMask |= GetItemSchema()->GetEquipRegionBitMaskByName( pszEquipRegionName );
+ m_unEquipRegionConflictMask |= unThisRegionMask;
+ }
+ }
+
+ // Single-line static attribute parsing.
+ {
+ KeyValues *pKVStaticAttrsKey = m_pKVItem->FindKey( "static_attrs" );
+ if ( pKVStaticAttrsKey )
+ {
+ FOR_EACH_SUBKEY( pKVStaticAttrsKey, pKVKey )
+ {
+ static_attrib_t staticAttrib;
+
+ SCHEMA_INIT_SUBSTEP( staticAttrib.BInitFromKV_SingleLine( GetDefinitionName(), pKVKey, pVecErrors, false ) );
+ m_vecStaticAttributes.AddToTail( staticAttrib );
+
+ // Does this attribute specify a tag to apply to this item definition?
+ Assert( staticAttrib.GetAttributeDefinition() );
+ }
+ }
+ }
+
+ // Old style attribute parsing. Really only useful now for GC-generated attributes.
+ KeyValues *pKVAttribKey = m_pKVItem->FindKey( "attributes" );
+ if ( pKVAttribKey )
+ {
+ FOR_EACH_SUBKEY( pKVAttribKey, pKVKey )
+ {
+ static_attrib_t staticAttrib;
+
+ SCHEMA_INIT_SUBSTEP( staticAttrib.BInitFromKV_MultiLine( GetDefinitionName(), pKVKey, pVecErrors ) );
+ m_vecStaticAttributes.AddToTail( staticAttrib );
+
+ // Does this attribute specify a tag to apply to this item definition?
+ Assert( staticAttrib.GetAttributeDefinition() );
+ }
+ }
+
+ // Initialize tags based on all static attributes for this item.
+ for ( const static_attrib_t& attr : m_vecStaticAttributes )
+ {
+ const econ_tag_handle_t tag = attr.GetAttributeDefinition()->GetItemDefinitionTag();
+ if ( tag != INVALID_ECON_TAG_HANDLE )
+ {
+ m_vecTags.AddToTail( tag );
+ }
+ }
+
+ // Auto-generate tags based on capabilities.
+ for ( int i = 0; i < NUM_ITEM_CAPS; i++ )
+ {
+ if ( m_iCapabilities & (1 << i) )
+ {
+ m_vecTags.AddToTail( GetItemSchema()->GetHandleForTag( CFmtStr( "auto__cap_%s", g_Capabilities[i] ).Get() ) );
+ }
+ }
+
+ // Initialize used-specified tags for this item if present.
+ KeyValues *pKVTags = m_pKVItem->FindKey( "tags" );
+ if ( pKVTags )
+ {
+ FOR_EACH_SUBKEY( pKVTags, pKVTag )
+ {
+ m_vecTags.AddToTail( GetItemSchema()->GetHandleForTag( pKVTag->GetName() ) );
+ }
+ }
+
+#ifdef GC_DLL
+ SCHEMA_INIT_SUBSTEP( BCommonInitPropertyGeneratorsFromKV( GetDefinitionName(), &m_vecPropertyGenerators, m_pKVItem->FindKey( "property_generators" ), pVecErrors ) );
+
+ // Parse payment rules on the GC if any exist.
+ KeyValues *pKVPaymentRules = m_pKVItem->FindKey( gc_steam_payment_rules_kv_key.GetString() );
+ if ( pKVPaymentRules )
+ {
+ FOR_EACH_TRUE_SUBKEY( pKVPaymentRules, pKVRule )
+ {
+ econ_item_payment_rule_t rule;
+
+ const bool bFoundPaymentRule = BGetPaymentRule( pKVRule, &rule.m_eRuleType, &rule.m_RevenueShare );
+ SCHEMA_INIT_CHECK( bFoundPaymentRule, "Item definition '%s': payment rule %s didn't specify a known payment rule type", GetDefinitionName(), pKVRule->GetName() );
+
+ // Allow us to override some of our checks if we want to claim we really know what we're doing.
+ if ( !pKVRule->FindKey( "sanity_check_override" ) )
+ {
+ SCHEMA_INIT_CHECK( rule.m_RevenueShare > 0.0, "Item definition '%s': payment rule %s has invalid revenue share %0.2f", GetDefinitionName(), pKVRule->GetName(), rule.m_RevenueShare );
+
+ // Ordinarily, bundles can only specify the "bundle_revenue_share" payment rule type. However, for backwards compatability
+ // with previous payment rules that pre-date the Workshop and had manual percentages set offline, we allow people who know
+ // what they're doing to specify the "sanity_check_override" key and then use custom rules.
+ SCHEMA_INIT_CHECK( (rule.m_eRuleType == kPaymentRule_Bundle) == IsBundle(), "Item definition '%s': payment rule %s has invalid bundle rules", GetDefinitionName(), pKVRule->GetName() );
+ }
+
+ KeyValues *pPaymentRuleForItemdef = pKVRule->FindKey( "payment_rule_for_itemdef" );
+ SCHEMA_INIT_CHECK( pPaymentRuleForItemdef && ( pPaymentRuleForItemdef->GetInt() == m_nDefIndex ), "Item definition '%s': payment rule %s has invalid payment_rule_for_itemdef", GetDefinitionName(), pKVRule->GetName() );
+
+ KeyValues *pKVMultiTargets = pKVRule->FindKey( "targets" );
+ KeyValues *pKVSingleTarget = pKVRule->FindKey( "target" );
+ SCHEMA_INIT_CHECK( pKVMultiTargets == NULL || pKVSingleTarget == NULL, "Item definition '%s': payment rule %s specifies both single- and multi-targets", GetDefinitionName(), pKVRule->GetName() );
+
+ if ( pKVMultiTargets )
+ {
+ FOR_EACH_SUBKEY( pKVMultiTargets, pKVTarget )
+ {
+ rule.m_vecValues.AddToTail( (uint64)Q_atoi64( pKVTarget->GetName() ) );
+ }
+ }
+
+ if ( pKVSingleTarget )
+ {
+ rule.m_vecValues.AddToTail( pKVSingleTarget->GetUint64() );
+ }
+
+ // We expect bundles to have no associated account data at all -- their payment processing
+ // is done by splitting the total value of the bundle between each of the items in it. All
+ // non-bundle payment rules require at least one data entry.
+ SCHEMA_INIT_CHECK( (rule.m_eRuleType == kPaymentRule_Bundle) == (rule.m_vecValues.Count() == 0), "Item definition '%s': payment rule %s has invalid number of target entries %i", GetDefinitionName(), pKVRule->GetName(), rule.m_vecValues.Count() );
+
+ // It doesn't make any sense to have multiple entries for a bundle. We expect their to be one
+ // rule, and that's "process this like a bundle".
+ if ( rule.m_eRuleType == kPaymentRule_Bundle )
+ {
+ SCHEMA_INIT_CHECK( m_vecPaymentRules.Count() == 0, "Item definition '%s': only the first payment rule can be specified as 'bundle'", GetDefinitionName() );
+
+ // We only allow bundle payment rules to be applied to actual bundles with contents
+ // specified. Without doing this, we would have nowhere to pull the metadata about
+ // which items are contained.
+ SCHEMA_INIT_CHECK( GetBundleInfo() && GetBundleInfo()->vecItemDefs.Count() > 0, "Item definition '%s': payment rule %s is specified as a bundle but outer item definition has no bundle contents.\n", GetDefinitionName(), pKVRule->GetName() );
+
+ // Bundles rely on sub-items for figuring out payment so the revenue share for the
+ // bundle itself is expected to be 100%.
+ SCHEMA_INIT_CHECK( rule.m_RevenueShare == 100.0, "Item definition '%s': payment rule %s has invalid bundle revenue share %0.2f", GetDefinitionName(), pKVRule->GetName(), rule.m_RevenueShare );
+ }
+
+ const bool bWasNumberedCorrectly = (AddPaymentRule( rule ) == atoi( pKVRule->GetName() ));
+ SCHEMA_INIT_CHECK( bWasNumberedCorrectly, "Item definition '%s': misnumbered payment rule %s", GetDefinitionName(), pKVRule->GetName() );
+ }
+ }
+#endif // GC_DLL
+
+ return SCHEMA_INIT_SUCCESS();
+}
+
+#ifdef GC_DLL
+int CEconItemDefinition::AddPaymentRule( const econ_item_payment_rule_t& newRule )
+{
+ return m_vecPaymentRules.AddToTail( newRule );
+}
+#endif // GC_DLL
+
+bool static_attrib_t::BInitFromKV_MultiLine( const char *pszContext, KeyValues *pKVAttribute, CUtlVector<CUtlString> *pVecErrors )
+{
+ const CEconItemAttributeDefinition *pAttrDef = GetItemSchema()->GetAttributeDefinitionByName( pKVAttribute->GetName() );
+
+ SCHEMA_INIT_CHECK(
+ NULL != pAttrDef,
+ "Context '%s': Attribute \"%s\" in \"attributes\" did not match any attribute definitions", pszContext, pKVAttribute->GetName() );
+
+ if ( pAttrDef )
+ {
+ iDefIndex = pAttrDef->GetDefinitionIndex();
+
+ const ISchemaAttributeType *pAttrType = pAttrDef->GetAttributeType();
+ Assert( pAttrType );
+
+ pAttrType->InitializeNewEconAttributeValue( &m_value );
+
+ const char *pszValue = pKVAttribute->GetString( "value", NULL );
+ const bool bSuccessfullyLoadedValue = pAttrType->BConvertStringToEconAttributeValue( pAttrDef, pszValue, &m_value, true );
+
+ SCHEMA_INIT_CHECK(
+ bSuccessfullyLoadedValue,
+ "Context '%s': Attribute \"%s\" could not parse value \"%s\"!", pszContext, pKVAttribute->GetName(), pszValue ? pszValue : "(null)" );
+
+ SCHEMA_INIT_CHECK(
+ !pAttrDef->BIsSetBonusAttribute(),
+ "Context '%s': Attribute \"%s\" is a set bonus attribute and not supported here", pszContext, pKVAttribute->GetName() );
+#ifdef GC_DLL
+ bForceGCToGenerate = pKVAttribute->GetBool( "force_gc_to_generate" );
+
+ KeyValues *pKVLogicData = pKVAttribute->FindKey( "custom_value_logic" );
+ if ( pKVLogicData )
+ {
+ m_pKVCustomData = pKVLogicData->MakeCopy();
+ }
+
+ SCHEMA_INIT_CHECK(
+ m_pKVCustomData == NULL || bForceGCToGenerate,
+ "Context '%s': Attribute \"%s\" is set to have custom logic but is not GC-generated so that logic will never get used!", pszContext, pKVAttribute->GetName() );
+
+ SCHEMA_INIT_CHECK(
+ m_pKVCustomData != NULL || pKVAttribute->FindKey( "value" ),
+ "Context '%s': Attribute \"%s\" has no value set", pszContext, pKVAttribute->GetName() );
+
+ SCHEMA_INIT_CHECK(
+ m_pKVCustomData == NULL || m_pKVCustomData->FindKey( "method" ) != NULL,
+ "Context '%s': Attribute \"%s\" custom logic data is set, but custom logic method is not set!", pszContext, pKVAttribute->GetName() );
+#endif // GC_DLL
+ }
+
+ return SCHEMA_INIT_SUCCESS();
+}
+
+bool static_attrib_t::BInitFromKV_SingleLine( const char *pszContext, KeyValues *pKVAttribute, CUtlVector<CUtlString> *pVecErrors, bool bEnableTerribleBackwardsCompatibilitySchemaParsingCode /* = true */ )
+{
+ const CEconItemAttributeDefinition *pAttrDef = GetItemSchema()->GetAttributeDefinitionByName( pKVAttribute->GetName() );
+
+ SCHEMA_INIT_CHECK(
+ NULL != pAttrDef,
+ "Context '%s': Attribute \"%s\" in \"attributes\" did not match any attribute definitions", pszContext, pKVAttribute->GetName() );
+
+ if ( pAttrDef )
+ {
+ iDefIndex = pAttrDef->GetDefinitionIndex();
+
+ const ISchemaAttributeType *pAttrType = pAttrDef->GetAttributeType();
+ Assert( pAttrType );
+
+ pAttrType->InitializeNewEconAttributeValue( &m_value );
+
+ const char *pszValue = pKVAttribute->GetString();
+ const bool bSuccessfullyLoadedValue = pAttrType->BConvertStringToEconAttributeValue( pAttrDef, pszValue, &m_value, bEnableTerribleBackwardsCompatibilitySchemaParsingCode );
+
+ SCHEMA_INIT_CHECK(
+ bSuccessfullyLoadedValue,
+ "Context '%s': Attribute \"%s\" could not parse value \"%s\"!", pszContext, pKVAttribute->GetName(), pszValue ? pszValue : "(null)" );
+
+ SCHEMA_INIT_CHECK(
+ !pAttrDef->BIsSetBonusAttribute(),
+ "Context '%s': Attribute \"%s\" is a set bonus attribute and not supported here", pszContext, pKVAttribute->GetName() );
+
+#ifdef GC_DLL
+ bForceGCToGenerate = false;
+ m_pKVCustomData = NULL;
+#endif // GC_DLL
+ }
+
+ return SCHEMA_INIT_SUCCESS();
+}
+
+
+
+bool CEconItemDefinition::BInitItemMappings( CUtlVector<CUtlString> *pVecErrors )
+{
+ // Armory remapping
+ if ( m_pszArmoryRemap && m_pszArmoryRemap[0] )
+ {
+ CEconItemDefinition *pDef = GetItemSchema()->GetItemDefinitionByName( m_pszArmoryRemap );
+ if ( pDef )
+ {
+ m_iArmoryRemap = pDef->GetDefinitionIndex();
+ }
+
+ SCHEMA_INIT_CHECK(
+ pDef != NULL,
+ "Item %s: Armory remap definition \"%s\" was not found", m_pszItemBaseName, m_pszArmoryRemap );
+ }
+
+ // Store remapping
+ if ( m_pszStoreRemap && m_pszStoreRemap[0] )
+ {
+ CEconItemDefinition *pDef = GetItemSchema()->GetItemDefinitionByName( m_pszStoreRemap );
+ if ( pDef )
+ {
+ m_iStoreRemap = pDef->GetDefinitionIndex();
+ }
+
+ SCHEMA_INIT_CHECK(
+ pDef != NULL,
+ "Item %s: Store remap definition \"%s\" was not found", m_pszItemBaseName, m_pszStoreRemap );
+ }
+
+ return SCHEMA_INIT_SUCCESS();
+}
+
+const char* CEconItemDefinition::GetIconURL( const char* pszKey ) const
+{
+ auto idx = m_pDictIcons->Find( pszKey );
+ if ( idx == m_pDictIcons->InvalidIndex() )
+ {
+ return NULL;
+ }
+
+ return (*m_pDictIcons)[ idx ];
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Generate and return a random level according to whatever leveling
+// curve this definition uses.
+//-----------------------------------------------------------------------------
+uint32 CEconItemDefinition::RollItemLevel( void ) const
+{
+ return RandomInt( GetMinLevel(), GetMaxLevel() );
+}
+
+const char *CEconItemDefinition::GetFirstSaleDate() const
+{
+ return GetDefinitionString( "first_sale_date", "1960/00/00" );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CEconItemDefinition::IterateAttributes( IEconItemAttributeIterator *pIterator ) const
+{
+ FOR_EACH_VEC( GetStaticAttributes(), i )
+ {
+ const static_attrib_t& staticAttrib = GetStaticAttributes()[i];
+
+#ifdef GC_DLL
+ // we skip over static attributes that the GC will turn into dynamic attributes because otherwise we'll have
+ // the appearance of iterating over them twice; for clients these attributes won't even make it into the
+ // list
+ if ( staticAttrib.bForceGCToGenerate )
+ continue;
+#endif // GC_DLL
+
+ const CEconItemAttributeDefinition *pAttrDef = GetItemSchema()->GetAttributeDefinition( staticAttrib.iDefIndex );
+ if ( !pAttrDef )
+ continue;
+
+ const ISchemaAttributeType *pAttrType = pAttrDef->GetAttributeType();
+ Assert( pAttrType );
+
+ if ( !pAttrType->OnIterateAttributeValue( pIterator, pAttrDef, staticAttrib.m_value ) )
+ return;
+ }
+}
+
+#if defined(CLIENT_DLL) || defined(GAME_DLL)
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+Activity CEconItemDefinition::GetActivityOverride( int iTeam, Activity baseAct ) const
+{
+ int iAnims = GetNumAnimations( iTeam );
+ for ( int i = 0; i < iAnims; i++ )
+ {
+ animation_on_wearable_t *pData = GetAnimationData( iTeam, i );
+ if ( !pData )
+ continue;
+ if ( pData->iActivity == kActivityLookup_Unknown )
+ {
+ pData->iActivity = ActivityList_IndexForName( pData->pszActivity );
+ }
+
+ if ( pData->iActivity == baseAct )
+ {
+ if ( pData->iReplacement == kActivityLookup_Unknown )
+ {
+ pData->iReplacement = ActivityList_IndexForName( pData->pszReplacement );
+ }
+
+ if ( pData->iReplacement > 0 )
+ {
+ return (Activity) pData->iReplacement;
+ }
+ }
+ }
+
+ return baseAct;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+const char *CEconItemDefinition::GetActivityOverride( int iTeam, const char *pszActivity ) const
+{
+ int iAnims = GetNumAnimations( iTeam );
+ for ( int i = 0; i < iAnims; i++ )
+ {
+ animation_on_wearable_t *pData = GetAnimationData( iTeam, i );
+ if ( Q_stricmp( pszActivity, pData->pszActivity ) == 0 )
+ return pData->pszReplacement;
+ }
+
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+const char *CEconItemDefinition::GetReplacementForActivityOverride( int iTeam, Activity baseAct ) const
+{
+ int iAnims = GetNumAnimations( iTeam );
+ for ( int i = 0; i < iAnims; i++ )
+ {
+ animation_on_wearable_t *pData = GetAnimationData( iTeam, i );
+ if ( pData->iActivity == kActivityLookup_Unknown )
+ {
+ pData->iActivity = ActivityList_IndexForName( pData->pszActivity );
+ }
+ if ( pData && pData->iActivity == baseAct )
+ return pData->pszReplacement;
+ }
+
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Returns true if the content for this item view should be streamed. If false,
+// it should be preloaded.
+//-----------------------------------------------------------------------------
+
+// DO NOT MERGE THIS CONSOLE VARIABLE TO REL WE SHOULD NOT SHIP THIS OH GOD
+#ifdef STAGING_ONLY
+ConVar item_enable_dynamic_loading( "item_enable_dynamic_loading", "1", FCVAR_REPLICATED, "Enable/disable dynamic streaming of econ content." );
+#endif // STAGING_ONLY
+
+bool CEconItemDefinition::IsContentStreamable() const
+{
+ if ( !BLoadOnDemand() )
+ return false;
+
+#ifdef STAGING_ONLY
+ return item_enable_dynamic_loading.GetBool();
+#else
+ return true;
+#endif
+}
+#endif // defined(CLIENT_DLL) || defined(GAME_DLL)
+
+RETURN_ATTRIBUTE_STRING_F( CEconItemDefinition::GetIconDisplayModel, "icon display model", m_pszWorldDisplayModel );
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor
+//-----------------------------------------------------------------------------
+CTimedItemRewardDefinition::CTimedItemRewardDefinition( void )
+: m_unMinFreq( 0 ),
+ m_unMaxFreq( UINT_MAX ),
+ m_flChance( 0.0f ),
+ m_pLootList( NULL ),
+ m_iRequiredItemDef(INVALID_ITEM_DEF_INDEX)
+{
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Copy constructor
+//-----------------------------------------------------------------------------
+CTimedItemRewardDefinition::CTimedItemRewardDefinition( const CTimedItemRewardDefinition &that )
+{
+ (*this) = that;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Operator=
+//-----------------------------------------------------------------------------
+CTimedItemRewardDefinition &CTimedItemRewardDefinition::operator=( const CTimedItemRewardDefinition &rhs )
+{
+ m_unMinFreq = rhs.m_unMinFreq;
+ m_unMaxFreq = rhs.m_unMaxFreq;
+ m_flChance = rhs.m_flChance;
+ m_criteria = rhs.m_criteria;
+ m_pLootList = rhs.m_pLootList;
+ m_iRequiredItemDef = rhs.m_iRequiredItemDef;
+
+ return *this;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Initialize the attribute definition
+// Input: pKVTimedReward - The KeyValues representation of the timed reward
+// schema - The overall item schema
+// pVecErrors - An optional vector that will contain error messages if
+// the init fails.
+// Output: True if initialization succeeded, false otherwise
+//-----------------------------------------------------------------------------
+bool CTimedItemRewardDefinition::BInitFromKV( KeyValues *pKVTimedReward, CUtlVector<CUtlString> *pVecErrors /* = NULL */ )
+{
+ // Parse the basic values
+ m_flChance = pKVTimedReward->GetFloat( "pctChance" );
+ m_unMinFreq = pKVTimedReward->GetInt( "value_min", 0 );
+ m_unMaxFreq = pKVTimedReward->GetInt( "value_max", UINT_MAX );
+ m_iRequiredItemDef = INVALID_ITEM_DEF_INDEX;
+
+ const char *pszRequiredItem = pKVTimedReward->GetString( "required_item", NULL );
+ if ( pszRequiredItem )
+ {
+ // Find the ItemDef
+ CEconItemDefinition *pDef = GetItemSchema()->GetItemDefinitionByName( pszRequiredItem );
+ SCHEMA_INIT_CHECK( pDef != NULL, "Invalid Item Def Required for a for TimedReward Definition");
+ m_iRequiredItemDef = pDef->GetDefinitionIndex();
+ }
+
+ // Check required fields
+ SCHEMA_INIT_CHECK(
+ NULL != pKVTimedReward->FindKey( "value_min" ),
+ "Time reward %s: Missing required field \"value_min\"", pKVTimedReward->GetName() );
+ SCHEMA_INIT_CHECK(
+ NULL != pKVTimedReward->FindKey( "value_max" ),
+ "Time reward %s: Missing required field \"value_max\"", pKVTimedReward->GetName() );
+
+ SCHEMA_INIT_CHECK(
+ NULL != pKVTimedReward->FindKey( "pctChance" ),
+ "Time reward %s: Missing required field \"pctChance\"", pKVTimedReward->GetName() );
+
+ SCHEMA_INIT_CHECK(
+ NULL == pKVTimedReward->FindKey( "criteria" ),
+ "Time reward %s: \"criteria\" is no longer supported. Restructure as \"loot_list\"?", pKVTimedReward->GetName() );
+
+ SCHEMA_INIT_CHECK(
+ NULL != pKVTimedReward->FindKey( "loot_list" ),
+ "Time reward %s: Missing required field \"loot_list\" ", pKVTimedReward->GetName() );
+
+ // Parse the loot list
+ const char *pszLootList = pKVTimedReward->GetString("loot_list", NULL);
+ if ( pszLootList && pszLootList[0] )
+ {
+ m_pLootList = GetItemSchema()->GetLootListByName( pszLootList );
+
+ // Make sure the item index is correct because we use this index as a reference
+ SCHEMA_INIT_CHECK(
+ NULL != m_pLootList,
+ "Time Reward %s: loot_list (%s) does not exist", pKVTimedReward->GetName(), pszLootList );
+ }
+
+ // Other integrity checks
+ SCHEMA_INIT_CHECK(
+ m_flChance >= 0.0f,
+ "Time Reward %s: pctChance (%f) must be greater or equal to 0.0", pKVTimedReward->GetName(), m_flChance );
+
+ SCHEMA_INIT_CHECK(
+ m_flChance <= 1.0f,
+ "Time Reward %s: pctChance (%f) must be less than or equal to 1.0", pKVTimedReward->GetName(), m_flChance );
+
+ SCHEMA_INIT_CHECK(
+ pKVTimedReward->GetInt( "value_min" ) > 0,
+ "Time Reward %s: value_min (%d) must be greater than 0", pKVTimedReward->GetName(), m_unMinFreq );
+ SCHEMA_INIT_CHECK(
+ pKVTimedReward->GetInt( "value_max" ) > 0,
+ "Time Reward %s: value_max (%d) must be greater than 0", pKVTimedReward->GetName(), m_unMaxFreq );
+ SCHEMA_INIT_CHECK(
+ (m_unMaxFreq >= m_unMinFreq),
+ "Time Reward %s: value_max (%d) must be greater than or equal to value_min (%d)", pKVTimedReward->GetName(), m_unMaxFreq, m_unMinFreq );
+
+ return SCHEMA_INIT_SUCCESS();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Adds a foreign item definition to local definition mapping for a
+// foreign app
+//-----------------------------------------------------------------------------
+void CForeignAppImports::AddMapping( uint16 unForeignDefIndex, const CEconItemDefinition *pDefn )
+{
+ m_mapDefinitions.InsertOrReplace( unForeignDefIndex, pDefn );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Adds a foreign item definition to local definition mapping for a
+// foreign app
+//-----------------------------------------------------------------------------
+const CEconItemDefinition *CForeignAppImports::FindMapping( uint16 unForeignDefIndex ) const
+{
+ int i = m_mapDefinitions.Find( unForeignDefIndex );
+ if( m_mapDefinitions.IsValidIndex( i ) )
+ return m_mapDefinitions[i];
+ else
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor
+//-----------------------------------------------------------------------------
+CEconItemSchema::CEconItemSchema( )
+: m_unResetCount( 0 )
+, m_pKVRawDefinition( NULL )
+, m_mapItemSeries( DefLessFunc(int) )
+, m_mapRarities( DefLessFunc(int) )
+, m_mapQualities( DefLessFunc(int) )
+, m_mapAttributes( DefLessFunc(int) )
+, m_mapRecipes( DefLessFunc(int) )
+, m_mapQuestObjectives( DefLessFunc(int) )
+, m_mapItemsSorted( DefLessFunc(int) )
+, m_mapToolsItems( DefLessFunc(int) )
+, m_mapBaseItems( DefLessFunc(int) )
+, m_unVersion( 0 )
+#if defined(CLIENT_DLL) || defined(GAME_DLL)
+, m_pDefaultItemDefinition( NULL )
+#endif
+, m_mapItemSets( CaselessStringLessThan )
+, m_mapItemCollections( CaselessStringLessThan )
+, m_mapItemPaintKits( CaselessStringLessThan )
+, m_mapOperationDefinitions( CaselessStringLessThan )
+, m_mapLootLists( CaselessStringLessThan )
+, m_mapRevolvingLootLists( DefLessFunc(int) )
+, m_mapDefinitionPrefabs( CaselessStringLessThan )
+, m_mapAchievementRewardsByData( DefLessFunc( uint32 ) )
+, m_mapAttributeControlledParticleSystems( DefLessFunc(int) )
+, m_mapDefaultBodygroupState( CaselessStringLessThan )
+#ifdef GC_DLL
+, m_mapForeignImports( DefLessFunc(AppId_t) )
+#elif defined(CLIENT_DLL) || defined(GAME_DLL)
+, m_pDelayedSchemaData( NULL )
+#endif
+, m_mapKillEaterScoreTypes( DefLessFunc( unsigned int ) )
+, m_mapCommunityMarketDefinitionIndexRemap( DefLessFunc( item_definition_index_t ) )
+#ifdef CLIENT_DLL
+, m_mapSteamPackageLocalizationTokens( DefLessFunc( uint32 ) )
+#endif
+{
+ Reset();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+IEconTool *CEconItemSchema::CreateEconToolImpl( const char *pszToolType, const char *pszUseString, const char *pszUsageRestriction, item_capabilities_t unCapabilities, KeyValues *pUsageKV )
+{
+ if ( pszToolType )
+ {
+ if ( !V_stricmp( pszToolType, "duel_minigame" ) )
+ {
+ // Error checking -- make sure we aren't setting properties in the schema that we don't support.
+ if ( pszUsageRestriction ) return NULL;
+ if ( unCapabilities != ITEM_CAP_NONE ) return NULL;
+ if ( pUsageKV ) return NULL;
+
+ return new CEconTool_DuelingMinigame( pszToolType, pszUseString );
+ }
+
+ if ( !V_stricmp( pszToolType, "noise_maker" ) )
+ {
+ // Error checking -- make sure we aren't setting properties in the schema that we don't support.
+ if ( pszUsageRestriction ) return NULL;
+ if ( unCapabilities != ITEM_CAP_NONE ) return NULL;
+ if ( pUsageKV ) return NULL;
+
+ return new CEconTool_Noisemaker( pszToolType, pszUseString );
+ }
+
+ if ( !V_stricmp( pszToolType, "wrapped_gift" ) )
+ {
+ // Error checking -- make sure we aren't setting properties in the schema that we don't support.
+ if ( pszUsageRestriction ) return NULL;
+
+ return new CEconTool_WrappedGift( pszToolType, pszUseString, unCapabilities, pUsageKV );
+ }
+
+ if ( !V_stricmp( pszToolType, "backpack_expander" ) )
+ {
+ // Error checking -- make sure we aren't setting properties in the schema that we don't support.
+ if ( pszUsageRestriction ) return NULL;
+ if ( unCapabilities != ITEM_CAP_NONE ) return NULL;
+
+ return new CEconTool_BackpackExpander( pszToolType, pszUseString, pUsageKV );
+ }
+
+ if ( !V_stricmp( pszToolType, "account_upgrade_to_premium" ) )
+ {
+ // Error checking -- make sure we aren't setting properties in the schema that we don't support.
+ if ( pszUsageRestriction ) return NULL;
+ if ( unCapabilities != ITEM_CAP_NONE ) return NULL;
+ if ( pUsageKV ) return NULL;
+
+ return new CEconTool_AccountUpgradeToPremium( pszToolType, pszUseString );
+ }
+
+ if ( !V_stricmp( pszToolType, "claimcode" ) )
+ {
+ // Error checking -- make sure we aren't setting properties in the schema that we don't support.
+ if ( pszUsageRestriction ) return NULL;
+ if ( unCapabilities != ITEM_CAP_NONE ) return NULL;
+
+ return new CEconTool_ClaimCode( pszToolType, pszUseString, pUsageKV );
+ }
+
+ if ( !V_stricmp( pszToolType, "gift" ) )
+ {
+ // Error checking -- make sure we aren't setting properties in the schema that we don't support.
+ if ( pszUsageRestriction ) return NULL;
+ if ( unCapabilities != ITEM_CAP_NONE ) return NULL;
+
+ return new CEconTool_Gift( pszToolType, pszUseString, pUsageKV );
+ }
+
+ if ( !V_stricmp( pszToolType, "paint_can" ) )
+ {
+ // Error checking -- make sure we aren't setting properties in the schema that we don't support.
+ if ( pszUsageRestriction ) return NULL;
+ if ( pUsageKV ) return NULL;
+
+ return new CEconTool_PaintCan( pszToolType, unCapabilities );
+ }
+
+ if ( !V_stricmp( pszToolType, "name" ) )
+ {
+ // Error checking -- make sure we aren't setting properties in the schema that we don't support.
+ if ( pszUsageRestriction ) return NULL;
+ if ( pUsageKV ) return NULL;
+
+ return new CEconTool_NameTag( pszToolType, unCapabilities );
+ }
+
+ if ( !V_stricmp( pszToolType, "desc" ) )
+ {
+ // Error checking -- make sure we aren't setting properties in the schema that we don't support.
+ if ( pszUsageRestriction ) return NULL;
+ if ( pUsageKV ) return NULL;
+
+ return new CEconTool_DescTag( pszToolType, unCapabilities );
+ }
+
+ if ( !V_stricmp( pszToolType, "decoder_ring" ) )
+ {
+ // Error checking -- make sure we aren't setting properties in the schema that we don't support.
+ if ( pUsageKV ) return NULL;
+
+ return new CEconTool_CrateKey( pszToolType, pszUsageRestriction, unCapabilities );
+ }
+
+ if ( !V_stricmp( pszToolType, "customize_texture_item" ) )
+ {
+ // Error checking -- make sure we aren't setting properties in the schema that we don't support.
+ if ( pszUsageRestriction ) return NULL;
+ if ( pUsageKV ) return NULL;
+
+ return new CEconTool_CustomizeTexture( pszToolType, unCapabilities );
+ }
+
+ if ( !V_stricmp( pszToolType, "gift_wrap" ) )
+ {
+ // Error checking -- make sure we aren't setting properties in the schema that we don't support.
+ if ( pszUsageRestriction ) return NULL;
+
+ return new CEconTool_GiftWrap( pszToolType, pszUseString, unCapabilities, pUsageKV );
+ }
+
+ if ( !V_stricmp( pszToolType, "wedding_ring" ) )
+ {
+ // Error checking -- make sure we aren't setting properties in the schema that we don't support.
+ if ( pszUsageRestriction ) return NULL;
+ if ( pUsageKV ) return NULL;
+
+ return new CEconTool_WeddingRing( pszToolType, pszUseString, unCapabilities );
+ }
+
+ if ( !V_stricmp( pszToolType, "strange_part" ) )
+ {
+ // Error checking -- make sure we aren't setting properties in the schema that we don't support.
+ if ( pszUsageRestriction ) return NULL;
+
+ return new CEconTool_StrangePart( pszToolType, pszUseString, unCapabilities, pUsageKV );
+ }
+
+ if ( !V_stricmp( pszToolType, "strange_part_restriction" ) )
+ {
+ // Error checking -- make sure we aren't setting properties in the schema that we don't support.
+ if ( pszUsageRestriction ) return NULL;
+ if ( !pUsageKV ) return NULL; // required
+
+ return new CEconTool_StrangePartRestriction( pszToolType, pszUseString, unCapabilities, pUsageKV );
+ }
+
+ if ( !V_stricmp( pszToolType, "apply_custom_attrib" ) )
+ {
+ // Error checking -- make sure we aren't setting properties in the schema that we don't support.
+ if ( pszUsageRestriction ) return NULL;
+
+ return new CEconTool_UpgradeCard( pszToolType, pszUseString, unCapabilities, pUsageKV );
+ }
+
+ if ( !V_stricmp( pszToolType, "strangifier" ) )
+ {
+ // Error checking -- make sure we aren't setting properties in the schema that we don't support.
+ if ( pszUsageRestriction ) return NULL;
+
+ return new CEconTool_Strangifier( pszToolType, pszUseString, unCapabilities, pUsageKV );
+ }
+
+ if ( !V_stricmp( pszToolType, "killstreakifier" ) )
+ {
+ // Error checking -- make sure we aren't setting properties in the schema that we don't support.
+ if ( pszUsageRestriction ) return NULL;
+
+ return new CEconTool_KillStreakifier( pszToolType, pszUseString, unCapabilities, pUsageKV );
+ }
+
+ if( !V_stricmp( pszToolType, "dynamic_recipe" ) )
+ {
+ // Error checking -- make sure we aren't setting properties in the schema that we don't support.
+ if ( pszUsageRestriction ) return NULL;
+
+ return new CEconTool_ItemDynamicRecipe( pszToolType, pszUseString, unCapabilities, pUsageKV );
+ }
+
+ if ( !V_stricmp( pszToolType, "item_eater_recharger" ) )
+ {
+ // Error checking -- make sure we aren't setting properties in the schema that we don't support.
+ if ( pszUsageRestriction ) return NULL;
+
+ return new CEconTool_ItemEaterRecharger( pszToolType, pszUseString, unCapabilities, pUsageKV );
+ }
+
+ if ( !V_stricmp( pszToolType, "class_transmogrifier" ) )
+ {
+ // Error checking -- make sure we aren't setting properties in the schema that we don't support.
+ if ( pszUsageRestriction ) return NULL;
+
+ return new CEconTool_ClassTransmogrifier( pszToolType, pszUseString, unCapabilities, pUsageKV );
+ }
+
+ if ( !V_stricmp( pszToolType, "duck_token" ) )
+ {
+ // Error checking -- make sure we aren't setting properties in the schema that we don't support.
+ if ( pszUsageRestriction ) return NULL;
+ if ( pUsageKV ) return NULL;
+
+ return new CEconTool_DuckToken( pszToolType, unCapabilities );
+ }
+
+ if ( !V_stricmp( pszToolType, "grant_operation_pass" ) )
+ {
+ // Error checking -- make sure we aren't setting properties in the schema that we don't support.
+ if ( pszUsageRestriction ) return NULL;
+
+ return new CEconTool_GrantOperationPass( pszToolType, pszUseString, unCapabilities, pUsageKV );
+ }
+
+ if ( !V_stricmp( pszToolType, "strange_count_transfer" ) )
+ {
+ // Error checking -- make sure we aren't setting properties in the schema that we don't support.
+ if ( pszUsageRestriction ) return NULL;
+ if ( pUsageKV ) return NULL;
+
+ return new CEconTool_StrangeCountTransfer( pszToolType, unCapabilities );
+ }
+
+ if ( !V_stricmp( pszToolType, "paintkit_weapon_festivizer" ) )
+ {
+ return new CEconTool_Festivizer( pszToolType, pszUseString, unCapabilities, pUsageKV );
+ }
+
+ if ( !V_stricmp( pszToolType, "unusualifier" ) )
+ {
+ return new CEconTool_Unusualifier( pszToolType, pszUseString, unCapabilities, pUsageKV );
+ }
+ }
+
+ // Default behavior.
+ return new CEconTool_Default( pszToolType, pszUseString, pszUsageRestriction, unCapabilities );
+}
+
+#ifdef GC_DLL
+random_attrib_t *CEconItemSchema::CreateRandomAttribute( const char *pszContext, KeyValues *pRandomAttributesKV, CUtlVector<CUtlString> *pVecErrors /*= NULL*/ )
+{
+ // We've found the random attribute block. Parse it.
+ if ( pRandomAttributesKV->FindKey( "chance" ) == NULL )
+ {
+ CUtlString msg; \
+ msg.Format( CFmtStr( "Missing required field \"chance\" in the \"random_attributes\" block." ) );
+ if ( pVecErrors )
+ {
+ pVecErrors->AddToTail( msg );
+ }
+ else
+ {
+ AssertMsg( pRandomAttributesKV->FindKey( "chance" ) != NULL, msg.String() );
+ }
+
+ return NULL;
+ }
+
+ random_attrib_t randomAttrib;
+
+ randomAttrib.m_flChanceOfRandomAttribute = pRandomAttributesKV->GetFloat( "chance" );
+ randomAttrib.m_bPickAllAttributes = ( pRandomAttributesKV->GetFloat( "pick_all_attributes" ) != 0 );
+ randomAttrib.m_flTotalAttributeWeight = 0;
+
+ FOR_EACH_TRUE_SUBKEY( pRandomAttributesKV, pKVAttribute )
+ {
+ const char *pszName = pKVAttribute->GetName();
+
+ if ( !Q_strcmp( pszName, "chance" ) )
+ continue;
+
+ // Quick block list of attrs that have equal weight
+ if ( !Q_strcmp( pszName, "is_even_chance_attr" ) )
+ {
+ FOR_EACH_VALUE( pKVAttribute, pKVListItem )
+ {
+ const CEconItemAttributeDefinition *pDef = GetAttributeDefinitionByName( pKVListItem->GetName() );
+ if ( pDef == NULL )
+ {
+ CUtlString msg; \
+ msg.Format( CFmtStr( "Attribute definition \"%s\" was not found", pszName ) );
+ if ( pVecErrors )
+ {
+ pVecErrors->AddToTail( msg );
+ }
+ else
+ {
+ AssertMsg( pDef != NULL, msg.String() );
+ }
+
+ return NULL;
+ }
+
+ lootlist_attrib_t lootListAttrib;
+ if ( !lootListAttrib.m_staticAttrib.BInitFromKV_SingleLine( __FUNCTION__, pKVListItem, pVecErrors, false ) )
+ {
+ if ( pVecErrors )
+ {
+ pVecErrors->AddToTail( __FUNCTION__ ": error initializing line-item attribute from lootlist definition (possible attr template).\n" );
+ }
+ return NULL;
+ }
+ // Weight is set to 1 for even chance attr
+ lootListAttrib.m_flWeight = 1.0f;
+ randomAttrib.m_flTotalAttributeWeight += 1.0f;
+ randomAttrib.m_RandomAttributes.AddToTail( lootListAttrib );
+ }
+ }
+ else
+ {
+ const CEconItemAttributeDefinition *pDef = GetAttributeDefinitionByName( pszName );
+ if ( pDef == NULL )
+ {
+ CUtlString msg; \
+ msg.Format( CFmtStr( "Attribute definition \"%s\" was not found", pszName ) );
+ if ( pVecErrors )
+ {
+ pVecErrors->AddToTail( msg );
+ }
+ else
+ {
+ AssertMsg( pDef != NULL, msg.String() );
+ }
+
+ return NULL;
+ }
+
+ lootlist_attrib_t lootListAttrib;
+ if ( !lootListAttrib.BInitFromKV( pszContext, pKVAttribute, *this, pVecErrors ) )
+ {
+ return NULL;
+ }
+
+ randomAttrib.m_flTotalAttributeWeight += lootListAttrib.m_flWeight;
+ randomAttrib.m_RandomAttributes.AddToTail( lootListAttrib );
+ }
+ }
+
+ random_attrib_t *pRandomAttr = new random_attrib_t;
+ *pRandomAttr = randomAttrib;
+ return pRandomAttr;
+}
+#endif // GC_DLL
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Resets the schema to before BInit was called
+//-----------------------------------------------------------------------------
+void CEconItemSchema::Reset( void )
+{
+ ++m_unResetCount;
+
+ m_unFirstValidClass = 0;
+ m_unLastValidClass = 0;
+ m_unAccoutClassIndex = 0;
+ m_unFirstValidClassItemSlot = 0;
+ m_unLastValidClassItemSlot = 0;
+ m_unFirstValidAccountItemSlot = 0;
+ m_unLastValidAccountItemSlot = 0;
+ m_unNumItemPresets = 0;
+ m_unMinLevel = 0;
+ m_unMaxLevel = 0;
+ m_unVersion = 0;
+ m_unSumQualityWeights = 0;
+ FOR_EACH_VEC( m_vecAttributeTypes, i )
+ {
+ delete m_vecAttributeTypes[i].m_pAttrType;
+ }
+ m_vecAttributeTypes.Purge();
+ m_mapItems.PurgeAndDeleteElements();
+ m_mapItems.Purge();
+ m_mapRarities.Purge();
+ m_mapQualities.Purge();
+ m_mapItemsSorted.Purge();
+ m_mapToolsItems.Purge();
+ m_mapBaseItems.Purge();
+ m_mapRecipes.PurgeAndDeleteElements();
+ m_vecTimedRewards.Purge();
+ m_mapItemSets.PurgeAndDeleteElements();
+ m_mapLootLists.PurgeAndDeleteElements();
+#ifdef GC_DLL
+ m_dictRandomAttributeTemplates.PurgeAndDeleteElements();
+#endif // GC_DLL
+ m_mapAttributeControlledParticleSystems.Purge();
+ m_vecAttributeControlledParticleSystemsCosmetics.Purge();
+ m_vecAttributeControlledParticleSystemsWeapons.Purge();
+ m_vecAttributeControlledParticleSystemsTaunts.Purge();
+
+ m_mapAttributes.Purge();
+ if ( m_pKVRawDefinition )
+ {
+ m_pKVRawDefinition->deleteThis();
+ m_pKVRawDefinition = NULL;
+ }
+
+#if defined(CLIENT_DLL) || defined(GAME_DLL)
+ delete m_pDefaultItemDefinition;
+ m_pDefaultItemDefinition = NULL;
+#endif
+
+ FOR_EACH_MAP_FAST( m_mapRecipes, i )
+ {
+ delete m_mapRecipes[i];
+ }
+
+ FOR_EACH_MAP_FAST( m_mapDefinitionPrefabs, i )
+ {
+ m_mapDefinitionPrefabs[i]->deleteThis();
+ }
+ m_mapDefinitionPrefabs.Purge();
+
+ m_vecEquipRegionsList.Purge();
+ m_vecItemLevelingData.PurgeAndDeleteElements();
+
+ m_dictStringTable.PurgeAndDeleteElements();
+ m_mapCommunityMarketDefinitionIndexRemap.Purge();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Operator=
+//-----------------------------------------------------------------------------
+CEconItemSchema &CEconItemSchema::operator=( CEconItemSchema &rhs )
+{
+ Reset();
+ BInitSchema( rhs.m_pKVRawDefinition );
+ return *this;
+}
+
+bool g_bLastSignatureCheck;
+
+bool CheckValveSignature( const void *data, uint32 nDataSize, const void *signature, uint32 nSignatureSize )
+{
+ // Must match the PUBLIC KEY in src\devtools\valve_source_officialcontent.privatekey.vdf
+ static const unsigned char valvePublicKey[] =
+ "\x30\x81\x9D\x30\x0D\x06\x09\x2A\x86\x48\x86\xF7\x0D\x01\x01\x01"
+ "\x05\x00\x03\x81\x8B\x00\x30\x81\x87\x02\x81\x81\x00\xB1\xC0\xF1"
+ "\x1C\xB2\x98\x2F\x29\x25\x95\x07\xA7\x74\xD4\x83\x43\x77\xC5\xB7"
+ "\xA3\x8D\x9A\x4B\x38\x92\xB5\x98\x00\x9F\x16\xAA\x10\x95\x65\xCB"
+ "\x09\xAD\x25\xDE\x0D\x3D\x1A\x08\x9C\x3C\xB6\x8E\x49\x19\x21\xCC"
+ "\x14\x2F\x38\x33\x83\x20\x1D\xE9\x82\x62\xA7\x6E\xD8\xA6\xCC\x78"
+ "\xBC\x51\x68\x5A\x0A\x64\xA6\x17\x2C\x67\x12\x7A\xF2\x3E\x78\x73"
+ "\x1F\x4A\x82\xC2\x01\xD6\x4C\x9A\xB8\x09\x37\x32\x21\x84\xB6\x42"
+ "\x72\x7F\xE1\x42\xD1\x5C\xC0\x45\xF3\x58\x3E\x19\xE3\xE3\xE1\xA9"
+ "\xC5\x0C\x0F\xC8\x41\x13\x57\x3A\x52\x0A\x8F\x73\x23\x02\x01\x11";
+
+ // Put into a global var. Could help with VAC detection, if this
+ // code gets detoured
+ g_bLastSignatureCheck = CCrypto::RSAVerifySignatureSHA256(
+ (const uint8 *)data, nDataSize,
+ (const uint8 *)signature, nSignatureSize,
+ valvePublicKey, sizeof(valvePublicKey)
+ );
+
+ return g_bLastSignatureCheck;
+}
+
+//-----------------------------------------------------------------------------
+// Initializes the schema, given KV filename
+//-----------------------------------------------------------------------------
+bool CEconItemSchema::BInit( const char *fileName, const char *pathID, CUtlVector<CUtlString> *pVecErrors /* = NULL */)
+{
+ Reset();
+
+ // Read the raw data
+ CUtlBuffer bufRawData;
+ bool bReadFileOK = g_pFullFileSystem->ReadFile( fileName, pathID, bufRawData );
+ SCHEMA_INIT_CHECK( bReadFileOK, "Cannot load file '%s'", fileName );
+
+ // Do we need to check the signature?
+ #if defined(TF_DLL) || defined(TF_CLIENT_DLL)
+ {
+
+ // Load up the signature
+ CUtlString sSignatureFilename( fileName ); sSignatureFilename.Append( ".sig" );
+ CUtlBuffer bufSignatureBinary;
+ bool bReadSignatureOK = g_pFullFileSystem->ReadFile( sSignatureFilename.String(), pathID, bufSignatureBinary );
+ SCHEMA_INIT_CHECK( bReadSignatureOK, "Cannot load file '%s'", sSignatureFilename.String() );
+
+ // Check it with the Valve public key
+ bool bSignatureValid = CheckValveSignature(
+ bufRawData.Base(), bufRawData.TellPut(),
+ bufSignatureBinary.Base(), bufSignatureBinary.TellPut()
+ );
+
+ // If they have a signature for a zero-byte file, that's OK, too.
+ // That's the secret code that is checked into P4 internally that
+ // let's us run with any items_game file
+ if ( !bSignatureValid )
+ {
+ bSignatureValid = CheckValveSignature(
+ "", 0,
+ bufSignatureBinary.Base(), bufSignatureBinary.TellPut()
+ );
+ }
+
+ SCHEMA_INIT_CHECK( bSignatureValid, "'%s' is corrupt. Please verify your local game files. (https://support.steampowered.com/kb_article.php?ref=2037-QEUH-3335)", fileName );
+ }
+ #endif
+
+ // Compute version hash
+ CSHA1 sha1;
+ sha1.Update( (unsigned char *)bufRawData.Base(), bufRawData.Size() );
+ sha1.Final();
+ sha1.GetHash( m_schemaSHA.m_shaDigest );
+
+ // Wrap it with a text buffer reader
+ CUtlBuffer bufText( bufRawData.Base(), bufRawData.TellPut(), CUtlBuffer::READ_ONLY | CUtlBuffer::TEXT_BUFFER );
+
+ // Use the standard init path
+ return BInitTextBuffer( bufText, pVecErrors );
+}
+
+//-----------------------------------------------------------------------------
+// Initializes the schema, given KV in binary form
+//-----------------------------------------------------------------------------
+bool CEconItemSchema::BInitBinaryBuffer( CUtlBuffer &buffer, CUtlVector<CUtlString> *pVecErrors /* = NULL */ )
+{
+ Reset();
+ m_pKVRawDefinition = new KeyValues( "CEconItemSchema" );
+ if ( m_pKVRawDefinition->ReadAsBinary( buffer ) )
+ {
+ return BInitSchema( m_pKVRawDefinition, pVecErrors )
+ && BPostSchemaInit( pVecErrors );
+ }
+ if ( pVecErrors )
+ {
+ pVecErrors->AddToTail( "Error parsing keyvalues" );
+ }
+ return false;
+}
+
+unsigned char g_sha1ItemSchemaText[ k_cubHash ];
+
+//-----------------------------------------------------------------------------
+// Initializes the schema, given KV in text form
+//-----------------------------------------------------------------------------
+bool CEconItemSchema::BInitTextBuffer( CUtlBuffer &buffer, CUtlVector<CUtlString> *pVecErrors /* = NULL */ )
+{
+ // Save off the hash into a global variable, so VAC can check it
+ // later
+ GenerateHash( g_sha1ItemSchemaText, buffer.Base(), buffer.TellPut() );
+
+ Reset();
+ m_pKVRawDefinition = new KeyValues( "CEconItemSchema" );
+ if ( m_pKVRawDefinition->LoadFromBuffer( NULL, buffer ) )
+ {
+ return BInitSchema( m_pKVRawDefinition, pVecErrors )
+ && BPostSchemaInit( pVecErrors );
+ }
+ if ( pVecErrors )
+ {
+ pVecErrors->AddToTail( "Error parsing keyvalues" );
+ }
+ return false;
+}
+
+bool CEconItemSchema::DumpItems ( const char *fileName, const char *pathID )
+{
+ // create a write file
+ FileHandle_t f = g_pFullFileSystem->Open(fileName, "wb", pathID);
+
+ if ( f == FILESYSTEM_INVALID_HANDLE )
+ {
+ DevMsg(1, "CEconItemSchema::DumpItems: couldn't open file \"%s\" in path \"%s\".\n",
+ fileName?fileName:"NULL", pathID?pathID:"NULL" );
+ return false;
+ }
+
+ CUtlSortVector< KeyValues*, CUtlSortVectorKeyValuesByName > vecSortedItems;
+
+ FOR_EACH_MAP_FAST( m_mapItems, i )
+ {
+ vecSortedItems.InsertNoSort( m_mapItems[ i ]->GetRawDefinition() );
+ }
+ vecSortedItems.RedoSort();
+
+ CUtlBuffer buf;
+ FOR_EACH_VEC( vecSortedItems, i )
+ {
+ vecSortedItems[i]->RecursiveSaveToFile( buf, 0, true );
+ }
+
+ int iBufSize = buf.GetBytesRemaining();
+ bool bSuccess = false;
+ if ( g_pFullFileSystem->Write(buf.PeekGet(), iBufSize, f) == iBufSize )
+ bSuccess = true;
+
+ g_pFullFileSystem->Close(f);
+
+ return bSuccess;
+}
+
+//-----------------------------------------------------------------------------
+// Called once the price sheet's been loaded
+//-----------------------------------------------------------------------------
+#ifdef GC_DLL
+GCConVar econ_orphaned_sold_items_owned_by_account_id( "econ_orphaned_sold_items_owned_by_account_id", "121416792" );
+
+bool CEconItemSchema::DoPostPriceSheetLoadInit( CEconStorePriceSheet *pPriceSheet )
+{
+ FOR_EACH_MAP_FAST( m_mapItems, iItem )
+ {
+ CEconItemDefinition *pItemDef = m_mapItems[ iItem ];
+
+ // Is this item being sold?
+ const econ_store_entry_t *pStoreEntry = pPriceSheet->GetEntry( pItemDef->GetDefinitionIndex() );
+ if ( pStoreEntry )
+ {
+ // Cache off whether this item is a pack item
+ pItemDef->SetIsPackItem( pStoreEntry->m_bIsPackItem );
+
+ // If an item is being sold and it has no payment rules set up, we can optionally force-create
+ // a dummy payment rule that will redirect that item revenue to a "hey, these are orphan items!"
+ // account.
+ if ( pItemDef->GetPaymentRules().Count() == 0 && econ_orphaned_sold_items_owned_by_account_id.GetInt() )
+ {
+ econ_item_payment_rule_t rule;
+ rule.m_RevenueShare = 1.0;
+ rule.m_eRuleType = kPaymentRule_PartnerSteamID;
+ rule.m_vecValues.AddToTail( econ_orphaned_sold_items_owned_by_account_id.GetInt() );
+
+ DbgVerify( pItemDef->AddPaymentRule( rule ) == 0 );
+ }
+ }
+
+ // Go through the cache of all bundles...
+ FOR_EACH_VEC( m_vecBundles, iBundle )
+ {
+ const CEconItemDefinition *pBundleItemDef = m_vecBundles[ iBundle ];
+ const bundleinfo_t *pBundle = pBundleItemDef->GetBundleInfo();
+ const bool bBundleItemIsForSale = pPriceSheet->BItemExistsInPriceSheet( pBundleItemDef->GetDefinitionIndex() ) != NULL; // Only add bundles that are actually for sale
+
+ bool bAddToContainingBundleItemDefs = false;
+ if ( pItemDef->IsPackBundle() )
+ {
+ // If the current item is a pack bundle, look for the first pack item in the current bundle (pBundle). We can safely assume that all pack items will be
+ // in pBundle if the first pack item is, since the GC won't startup otherwise. Don't add self as a containing bundle.
+ bAddToContainingBundleItemDefs = pItemDef->GetDefinitionIndex() != pBundleItemDef->GetDefinitionIndex()
+ && pBundle->vecItemDefs.HasElement( pItemDef->GetBundleInfo()->vecItemDefs[0] );
+ }
+ else
+ {
+ bAddToContainingBundleItemDefs = pBundle->vecItemDefs.HasElement( pItemDef );
+ }
+
+ // Does the current bundle contain the given item?
+ if ( bBundleItemIsForSale && bAddToContainingBundleItemDefs )
+ {
+ pItemDef->m_vecContainingBundleItemDefs.AddToTail( pBundleItemDef );
+ }
+ }
+ }
+
+ return true;
+}
+#endif
+
+
+#if defined(CLIENT_DLL) || defined(GAME_DLL)
+//-----------------------------------------------------------------------------
+// Set up the buffer to use to reinitialize our schema next time we can do so safely.
+//-----------------------------------------------------------------------------
+bool CEconItemSchema::MaybeInitFromBuffer( IDelayedSchemaData *pDelayedSchemaData )
+{
+ bool bDidInit = false;
+
+ // Use whatever our most current data block is.
+ if ( m_pDelayedSchemaData )
+ {
+ delete m_pDelayedSchemaData;
+ }
+
+ m_pDelayedSchemaData = pDelayedSchemaData;
+
+#ifdef CLIENT_DLL
+ // If we aren't in a game we can parse immediately now.
+ if ( !engine->IsInGame() )
+ {
+ BInitFromDelayedBuffer();
+ bDidInit = true;
+ }
+#endif // CLIENT_DLL
+
+ return bDidInit;
+}
+
+//-----------------------------------------------------------------------------
+// We're in a safe place to change the contents of the schema, so do so and clean
+// up whatever memory we were using.
+//-----------------------------------------------------------------------------
+bool CEconItemSchema::BInitFromDelayedBuffer()
+{
+ if ( !m_pDelayedSchemaData )
+ return true;
+
+ bool bSuccess = m_pDelayedSchemaData->InitializeSchema( this );
+ delete m_pDelayedSchemaData;
+ m_pDelayedSchemaData = NULL;
+
+ return bSuccess;
+}
+#endif // !GC_DLL
+
+static void CalculateKeyValuesCRCRecursive( KeyValues *pKV, CRC32_t *crc, bool bIgnoreName = false )
+{
+ // Hash in the key name in LOWERCASE. Keyvalues files are not deterministic due
+ // to the case insensitivity of the keys and the dependence on the existing
+ // state of the name table upon entry.
+ if ( !bIgnoreName )
+ {
+ const char *s = pKV->GetName();
+ for (;;)
+ {
+ unsigned char x = tolower(*s);
+ CRC32_ProcessBuffer( crc, &x, 1 ); // !SPEED! This is slow, but it works.
+ if (*s == '\0') break;
+ ++s;
+ }
+ }
+
+ // Now hash in value, depending on type
+ // !FIXME! This is not byte-order independent!
+ switch ( pKV->GetDataType() )
+ {
+ case KeyValues::TYPE_NONE:
+ {
+ FOR_EACH_SUBKEY( pKV, pChild )
+ {
+ CalculateKeyValuesCRCRecursive( pChild, crc );
+ }
+ break;
+ }
+ case KeyValues::TYPE_STRING:
+ {
+ const char *val = pKV->GetString();
+ CRC32_ProcessBuffer( crc, val, strlen(val)+1 );
+ break;
+ }
+
+ case KeyValues::TYPE_INT:
+ {
+ int val = pKV->GetInt();
+ CRC32_ProcessBuffer( crc, &val, sizeof(val) );
+ break;
+ }
+
+ case KeyValues::TYPE_UINT64:
+ {
+ uint64 val = pKV->GetUint64();
+ CRC32_ProcessBuffer( crc, &val, sizeof(val) );
+ break;
+ }
+
+ case KeyValues::TYPE_FLOAT:
+ {
+ float val = pKV->GetFloat();
+ CRC32_ProcessBuffer( crc, &val, sizeof(val) );
+ break;
+ }
+ case KeyValues::TYPE_COLOR:
+ {
+ int val = pKV->GetColor().GetRawColor();
+ CRC32_ProcessBuffer( crc, &val, sizeof(val) );
+ break;
+ }
+
+ default:
+ case KeyValues::TYPE_PTR:
+ case KeyValues::TYPE_WSTRING:
+ {
+ Assert( !"Unsupport data type!" );
+ break;
+ }
+ }
+}
+
+uint32 CEconItemSchema::CalculateKeyValuesVersion( KeyValues *pKV )
+{
+ CRC32_t crc;
+ CRC32_Init( &crc );
+
+ // Calc CRC recursively. Ignore the very top-most
+ // key name, which isn't set consistently
+ CalculateKeyValuesCRCRecursive( pKV, &crc, true );
+ CRC32_Final( &crc );
+ return crc;
+}
+
+EEquipType_t CEconItemSchema::GetEquipTypeFromClassIndex( int iClass ) const
+{
+ if ( iClass == GetAccountIndex() )
+ return EEquipType_t::EQUIP_TYPE_ACCOUNT;
+
+ if ( iClass >= GetFirstValidClass() && iClass <= GetLastValidClass() )
+ return EEquipType_t::EQUIP_TYPE_CLASS;
+
+ return EEquipType_t::EQUIP_TYPE_INVALID;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Initializes the schema
+// Input: pKVRawDefinition - The raw KeyValues representation of the schema
+// pVecErrors - An optional vector that will contain error messages if
+// the init fails.
+// Output: True if initialization succeeded, false otherwise
+//-----------------------------------------------------------------------------
+bool CEconItemSchema::BInitSchema( KeyValues *pKVRawDefinition, CUtlVector<CUtlString> *pVecErrors /* = NULL */ )
+{
+ m_unMinLevel = pKVRawDefinition->GetInt( "item_level_min", 0 );
+ m_unMaxLevel = pKVRawDefinition->GetInt( "item_level_max", 0 );
+
+#if !defined( GC_DLL )
+ m_unVersion = CalculateKeyValuesVersion( pKVRawDefinition );
+#endif
+
+
+
+#ifdef GC_DLL
+ // Validate the integrity of the base data.
+ SCHEMA_INIT_CHECK( 0 <= m_unMinLevel, "Minimum Item Level must be at least 0" );
+ SCHEMA_INIT_CHECK( m_unMinLevel <= m_unMaxLevel, "Minimum Item Level must be less than or equal to Maximum Item Level" );
+#endif // GC_DLL
+
+ // Parse the prefabs block first so the prefabs will be populated in case anything else wants
+ // to use them later.
+ KeyValues *pKVPrefabs = pKVRawDefinition->FindKey( "prefabs" );
+ if ( NULL != pKVPrefabs )
+ {
+ SCHEMA_INIT_SUBSTEP( BInitDefinitionPrefabs( pKVPrefabs, pVecErrors ) );
+ }
+
+ // Initialize the game info block
+ KeyValues *pKVGameInfo = pKVRawDefinition->FindKey( "game_info" );
+ SCHEMA_INIT_CHECK( NULL != pKVGameInfo, "Required key \"game_info\" missing.\n" );
+
+ if ( NULL != pKVGameInfo )
+ {
+ SCHEMA_INIT_SUBSTEP( BInitGameInfo( pKVGameInfo, pVecErrors ) );
+ }
+
+ // Initialize our attribute types. We don't actually pull this data from the schema right now but it
+ // still makes sense to initialize it at this point.
+ SCHEMA_INIT_SUBSTEP( BInitAttributeTypes( pVecErrors ) );
+
+ // Initialize the item series block
+ KeyValues *pKVItemSeries = pKVRawDefinition->FindKey( "item_series_types" );
+ SCHEMA_INIT_CHECK( NULL != pKVItemSeries, "Required key \"item_series_types\" missing.\n" );
+ if ( NULL != pKVItemSeries )
+ {
+ SCHEMA_INIT_SUBSTEP( BInitItemSeries( pKVItemSeries, pVecErrors ) );
+ }
+
+ // Initialize the rarity block
+ KeyValues *pKVRarities = pKVRawDefinition->FindKey( "rarities" );
+ KeyValues *pKVRarityWeights = pKVRawDefinition->FindKey( "rarities_lootlist_weights" );
+ SCHEMA_INIT_CHECK( NULL != pKVRarities, "Required key \"rarities\" missing.\n" );
+ if ( NULL != pKVRarities )
+ {
+ SCHEMA_INIT_SUBSTEP( BInitRarities( pKVRarities, pKVRarityWeights, pVecErrors ) );
+ }
+
+ // Initialize the qualities block
+ KeyValues *pKVQualities = pKVRawDefinition->FindKey( "qualities" );
+ SCHEMA_INIT_CHECK( NULL != pKVQualities, "Required key \"qualities\" missing.\n" );
+
+ if ( NULL != pKVQualities )
+ {
+ SCHEMA_INIT_SUBSTEP( BInitQualities( pKVQualities, pVecErrors ) );
+ }
+
+ // Initialize the colors block
+ KeyValues *pKVColors = pKVRawDefinition->FindKey( "colors" );
+ SCHEMA_INIT_CHECK( NULL != pKVColors, "Required key \"colors\" missing.\n" );
+
+ if ( NULL != pKVColors )
+ {
+ SCHEMA_INIT_SUBSTEP( BInitColors( pKVColors, pVecErrors ) );
+ }
+
+ // Initialize the attributes block
+ KeyValues *pKVAttributes = pKVRawDefinition->FindKey( "attributes" );
+ SCHEMA_INIT_CHECK( NULL != pKVAttributes, "Required key \"attributes\" missing.\n" );
+
+ if ( NULL != pKVAttributes )
+ {
+ SCHEMA_INIT_SUBSTEP( BInitAttributes( pKVAttributes, pVecErrors ) );
+ }
+
+#ifdef GC
+ // Initialize the motd block
+ KeyValues *pKVMOTD = pKVRawDefinition->FindKey( "motd_entries" );
+ SCHEMA_INIT_CHECK( NULL != pKVMOTD, "Required key \"motd_entries\" missing.\n" );
+
+ if ( NULL != pKVMOTD )
+ {
+ SCHEMA_INIT_SUBSTEP( GGCGameBase()->GetMOTDManager().BInitMOTDEntries( pKVMOTD, pVecErrors ) );
+ }
+#endif
+
+ // Initialize the "equip_regions_list" block -- this is an optional block
+ KeyValues *pKVEquipRegions = pKVRawDefinition->FindKey( "equip_regions_list" );
+ if ( NULL != pKVEquipRegions )
+ {
+ SCHEMA_INIT_SUBSTEP( BInitEquipRegions( pKVEquipRegions, pVecErrors ) );
+ }
+
+ // Initialize the "equip_conflicts" block -- this is an optional block, though it doesn't
+ // make any sense and will probably fail internally if there is no corresponding "equip_regions"
+ // block as well
+ KeyValues *pKVEquipRegionConflicts = pKVRawDefinition->FindKey( "equip_conflicts" );
+ if ( NULL != pKVEquipRegionConflicts )
+ {
+ SCHEMA_INIT_SUBSTEP( BInitEquipRegionConflicts( pKVEquipRegionConflicts, pVecErrors ) );
+ }
+
+ // TF2 Paint Kits
+ // No included in schema file (Too Big). Loaded Seperately
+ // Load the KV and add it to the pKVRawDefinition
+ KeyValues *pPaintkitKV = new KeyValues( "item_paintkit_definitions" );
+ SCHEMA_INIT_CHECK( pPaintkitKV->LoadFromFile( g_pFullFileSystem, "scripts/items/paintkits_master.txt", "GAME" ), "Unable to Load paintkits_master.txt KV File!" );
+ pKVRawDefinition->AddSubKey( pPaintkitKV );
+
+ // Init Item Paint Kits
+ // Must be BEFORE Item defs
+ KeyValues *pKVItemPaintKits = pKVRawDefinition->FindKey( "item_paintkit_definitions" );
+ if ( NULL != pKVItemPaintKits )
+ {
+ SCHEMA_INIT_SUBSTEP( BInitItemPaintKitDefinitions( pKVItemPaintKits, pVecErrors ) );
+ }
+
+#ifdef GC_DLL
+ // Parse the loot lists block (on the GC)
+ // Must be BEFORE Item defs
+ KeyValues *pKVRandomAttributeTemplates = pKVRawDefinition->FindKey( "random_attribute_templates" );
+ SCHEMA_INIT_SUBSTEP( BInitRandomAttributeTemplates( pKVRandomAttributeTemplates, pVecErrors ) );
+#endif // GC_DLL
+
+ // Initialize the items block
+ KeyValues *pKVItems = pKVRawDefinition->FindKey( "items" );
+ SCHEMA_INIT_CHECK( NULL != pKVItems, "Required key \"items\" missing.\n" );
+
+ if ( NULL != pKVItems )
+ {
+ SCHEMA_INIT_SUBSTEP( BInitItems( pKVItems, pVecErrors ) );
+ }
+
+
+ // Verify base item names are proper in item schema
+ SCHEMA_INIT_SUBSTEP( BVerifyBaseItemNames( pVecErrors ) );
+
+ // Parse the item_sets block.
+ KeyValues *pKVItemSets = pKVRawDefinition->FindKey( "item_sets" );
+ SCHEMA_INIT_SUBSTEP( BInitItemSets( pKVItemSets, pVecErrors ) );
+
+ // Particles
+ KeyValues *pKVParticleSystems = pKVRawDefinition->FindKey( "attribute_controlled_attached_particles" );
+ SCHEMA_INIT_SUBSTEP( BInitAttributeControlledParticleSystems( pKVParticleSystems, pVecErrors ) );
+
+ // Parse any recipes block
+ KeyValues *pKVRecipes = pKVRawDefinition->FindKey( "recipes" );
+ SCHEMA_INIT_SUBSTEP( BInitRecipes( pKVRecipes, pVecErrors ) );
+
+ // Reset our loot lists.
+ m_mapLootLists.RemoveAll();
+
+ // Init Item Collections - Must be before lootlists since collections are lootlists themselves and are referenced by lootlists
+ KeyValues *pKVItemCollections = pKVRawDefinition->FindKey( "item_collections" );
+ if ( NULL != pKVItemCollections )
+ {
+ SCHEMA_INIT_SUBSTEP( BInitItemCollections( pKVItemCollections, pVecErrors ) );
+ }
+
+#ifdef GC_DLL
+ // Parse the loot lists block (on the GC)
+ KeyValues *pKVLootLists = pKVRawDefinition->FindKey( "loot_lists" );
+ SCHEMA_INIT_SUBSTEP( BInitLootLists( pKVLootLists, pVecErrors ) );
+
+ // Initialize the periodic score accumulation block (this needs to take place after items)
+ KeyValues *pKVPeriodicScoring = pKVRawDefinition->FindKey( "periodic_score_accumulation" );
+ if ( NULL != pKVPeriodicScoring )
+ {
+ SCHEMA_INIT_SUBSTEP( BInitPeriodicScoring( pKVPeriodicScoring, pVecErrors ) );
+ }
+#endif // GC_DLL
+
+ // Parse the client loot lists block (everywhere)
+ KeyValues *pKVClientLootLists = pKVRawDefinition->FindKey( "client_loot_lists" );
+ SCHEMA_INIT_SUBSTEP( BInitLootLists( pKVClientLootLists, pVecErrors ) );
+
+ // Parse the revolving loot lists block
+ KeyValues *pKVRevolvingLootLists = pKVRawDefinition->FindKey( "revolving_loot_lists" );
+ SCHEMA_INIT_SUBSTEP( BInitRevolvingLootLists( pKVRevolvingLootLists, pVecErrors ) );
+
+ // Init Items that may reference Collections
+ SCHEMA_INIT_SUBSTEP( BInitCollectionReferences( pVecErrors ) );
+
+ // Validate Operation Pass
+ KeyValues *pKVOperationDefinitions = pKVRawDefinition->FindKey( "operations" );
+ if ( NULL != pKVOperationDefinitions )
+ {
+ SCHEMA_INIT_SUBSTEP( BInitOperationDefinitions( pKVGameInfo, pKVOperationDefinitions, pVecErrors ) );
+ }
+
+#if defined( GC_DLL )
+ // Parse any time-based rewards
+ KeyValues *pKVTimeRewards = pKVRawDefinition->FindKey( "time_rewards" );
+ SCHEMA_INIT_SUBSTEP( BInitTimedRewards( pKVTimeRewards, pVecErrors ) );
+
+ KeyValues *pKVExperiments = pKVRawDefinition->FindKey( "experiments" );
+ SCHEMA_INIT_SUBSTEP( BInitExperiements( pKVExperiments, pVecErrors ) );
+
+ SCHEMA_INIT_SUBSTEP( BInitForeignImports( pVecErrors ) );
+#elif defined( CLIENT_DLL ) || defined( GAME_DLL )
+ KeyValues *pKVArmoryData = pKVRawDefinition->FindKey( "armory_data" );
+ SCHEMA_INIT_SUBSTEP( BInitArmoryData( pKVArmoryData, pVecErrors ) );
+#endif // GC_DLL
+
+ // Parse any achievement rewards
+ KeyValues *pKVAchievementRewards = pKVRawDefinition->FindKey( "achievement_rewards" );
+ SCHEMA_INIT_SUBSTEP( BInitAchievementRewards( pKVAchievementRewards, pVecErrors ) );
+
+#ifdef TF_CLIENT_DLL
+ // Compute the number of concrete items, for each item, and cache for quick access
+ SCHEMA_INIT_SUBSTEP( BInitConcreteItemCounts( pVecErrors ) );
+
+ // We don't have access to Steam's full library of app data on the client so initialize whichever packages
+ // we want to reference.
+ KeyValues *pKVSteamPackages = pKVRawDefinition->FindKey( "steam_packages" );
+ SCHEMA_INIT_SUBSTEP( BInitSteamPackageLocalizationToken( pKVSteamPackages, pVecErrors ) );
+#endif // TF_CLIENT_DLL
+
+ // Parse the item levels block
+ KeyValues *pKVItemLevels = pKVRawDefinition->FindKey( "item_levels" );
+ SCHEMA_INIT_SUBSTEP( BInitItemLevels( pKVItemLevels, pVecErrors ) );
+
+ // Parse the kill eater score types
+ KeyValues *pKVKillEaterScoreTypes = pKVRawDefinition->FindKey( "kill_eater_score_types" );
+ SCHEMA_INIT_SUBSTEP( BInitKillEaterScoreTypes( pKVKillEaterScoreTypes, pVecErrors ) );
+
+ // Initialize the string tables, if present
+ KeyValues *pKVStringTables = pKVRawDefinition->FindKey( "string_lookups" );
+ SCHEMA_INIT_SUBSTEP( BInitStringTables( pKVStringTables, pVecErrors ) );
+
+ // Initialize the community Market remaps, if present
+ KeyValues *pKVCommunityMarketRemaps = pKVRawDefinition->FindKey( "community_market_item_remaps" );
+ SCHEMA_INIT_SUBSTEP( BInitCommunityMarketRemaps( pKVCommunityMarketRemaps, pVecErrors ) );
+
+ return SCHEMA_INIT_SUCCESS();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Initializes the "game_info" section of the schema
+//-----------------------------------------------------------------------------
+bool CEconItemSchema::BInitGameInfo( KeyValues *pKVGameInfo, CUtlVector<CUtlString> *pVecErrors )
+{
+ m_unFirstValidClass = pKVGameInfo->GetInt( "first_valid_class", 0 );
+ m_unLastValidClass = pKVGameInfo->GetInt( "last_valid_class", 0 );
+ SCHEMA_INIT_CHECK( 0 < m_unFirstValidClass, "First valid class must be greater than 0." );
+ SCHEMA_INIT_CHECK( m_unFirstValidClass <= m_unLastValidClass, "First valid class must be less than or equal to last valid class." );
+ m_unAccoutClassIndex = pKVGameInfo->GetInt( "account_class_index", 0 );
+ SCHEMA_INIT_CHECK( m_unAccoutClassIndex > m_unLastValidClass, "Account class index must be greater than 'last_valid_class'" );
+
+ m_unFirstValidClassItemSlot = pKVGameInfo->GetInt( "first_valid_item_slot", INVALID_EQUIPPED_SLOT );
+ m_unLastValidClassItemSlot = pKVGameInfo->GetInt( "last_valid_item_slot", INVALID_EQUIPPED_SLOT );
+ SCHEMA_INIT_CHECK( INVALID_EQUIPPED_SLOT != m_unFirstValidClassItemSlot, "first_valid_item_slot not set!" );
+ SCHEMA_INIT_CHECK( INVALID_EQUIPPED_SLOT != m_unFirstValidClassItemSlot, "last_valid_item_slot not set!" );
+ SCHEMA_INIT_CHECK( m_unFirstValidClassItemSlot <= m_unLastValidClassItemSlot, "First valid item slot must be less than or equal to last valid item slot." );
+
+ m_unFirstValidAccountItemSlot = pKVGameInfo->GetInt( "account_first_valid_item_slot", INVALID_EQUIPPED_SLOT );
+ m_unLastValidAccountItemSlot = pKVGameInfo->GetInt( "account_last_valid_item_slot", INVALID_EQUIPPED_SLOT );
+ SCHEMA_INIT_CHECK( INVALID_EQUIPPED_SLOT != m_unFirstValidAccountItemSlot, "account_first_valid_item_slot not set!" );
+ SCHEMA_INIT_CHECK( INVALID_EQUIPPED_SLOT != m_unLastValidAccountItemSlot, "account_last_valid_item_slot not set!" );
+ SCHEMA_INIT_CHECK( m_unFirstValidAccountItemSlot <= m_unLastValidAccountItemSlot, "First vlid account item slot must be less than or equal to the last valid account item slot." );
+
+ m_unNumItemPresets = pKVGameInfo->GetInt( "num_item_presets", -1 );
+ SCHEMA_INIT_CHECK( (uint32)-1 != m_unNumItemPresets, "num_item_presets not set!" );
+
+ return SCHEMA_INIT_SUCCESS();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CEconItemSchema::BInitAttributeTypes( CUtlVector<CUtlString> *pVecErrors )
+{
+ FOR_EACH_VEC( m_vecAttributeTypes, i )
+ {
+ delete m_vecAttributeTypes[i].m_pAttrType;
+ }
+ m_vecAttributeTypes.Purge();
+
+ m_vecAttributeTypes.AddToTail( attr_type_t( NULL, new CSchemaAttributeType_Default ) );
+ m_vecAttributeTypes.AddToTail( attr_type_t( "float", new CSchemaAttributeType_Float ) );
+ m_vecAttributeTypes.AddToTail( attr_type_t( "uint64", new CSchemaAttributeType_UInt64 ) );
+ m_vecAttributeTypes.AddToTail( attr_type_t( "string", new CSchemaAttributeType_String ) );
+ m_vecAttributeTypes.AddToTail( attr_type_t( "dynamic_recipe_component_defined_item", new CSchemaAttributeType_DynamicRecipeComponentDefinedItem ) );
+ m_vecAttributeTypes.AddToTail( attr_type_t( "item_slot_criteria", new CSchemaAttributeType_ItemSlotCriteria ) );
+ m_vecAttributeTypes.AddToTail( attr_type_t( "item_placement", new CSchemaAttributeType_WorldItemPlacement ) );
+
+ // Make sure that all attribute types specified have the item ID in the 0th column. We use this
+ // when loading items to map between item IDs and the attributes they own.
+ FOR_EACH_VEC( m_vecAttributeTypes, i )
+ {
+#ifdef GC_DLL
+ const CColumnSet& cs = m_vecAttributeTypes[i].m_pAttrType->GetFullColumnSet();
+
+ SCHEMA_INIT_CHECK( cs.GetColumnCount() >= 2, "BInitAttributeTypes(): '%s' has invalid column count.\n", cs.GetRecordInfo()->GetName() );
+
+ const CColumnInfo& Column0 = cs.GetColumnInfo( 0 );
+ SCHEMA_INIT_CHECK( Column0.GetType() == k_EGCSQLType_int64, "BInitAttributeTypes(): '%s' column 0 has invalid data type %u.\n", cs.GetRecordInfo()->GetName(), Column0.GetType() );
+ SCHEMA_INIT_CHECK( Column0.GetName() && !V_stricmp( Column0.GetName(), "ItemID" ), "BInitAttributeTypes(): '%s' has invalid name '%s'.\n", cs.GetRecordInfo()->GetName(), Column0.GetName() ? Column0.GetName() : "[null]" );
+ SCHEMA_INIT_CHECK( Column0.BIsPrimaryKey(), "BInitAttributeTypes(): '%s' has an item ID column that isn't in the PK.\n", cs.GetRecordInfo()->GetName() );
+
+ const CColumnInfo& Column1 = cs.GetColumnInfo( 1 );
+ SCHEMA_INIT_CHECK( Column1.GetType() == k_EGCSQLType_int16, "BInitAttributeTypes(): '%s' column 1 has invalid data type %u.\n", cs.GetRecordInfo()->GetName(), Column0.GetType() );
+ SCHEMA_INIT_CHECK( Column1.GetName() && !V_stricmp( Column1.GetName(), "AttrDefIndex" ), "BInitAttributeTypes(): '%s' has invalid name '%s'.\n", cs.GetRecordInfo()->GetName(), Column1.GetName() ? Column1.GetName() : "[null]" );
+
+ // Make sure two different attribute types don't point to the same DB table. There's nothing
+ // technically that would prevent this from working, but right now the way we load from the
+ // DB would make this super-inefficient so we'd want to fix that code if we are rolling content
+ // that would hit this error.
+ for ( int j = i + 1; j < m_vecAttributeTypes.Count(); j++ )
+ {
+ SCHEMA_INIT_CHECK( cs.GetRecordInfo() != m_vecAttributeTypes[j].m_pAttrType->GetFullColumnSet().GetRecordInfo(),
+ "BInitAttributeTypes(): multiple attribute types reference the same table '%s'.\n", cs.GetRecordInfo()->GetName() );
+ }
+#endif // GC_DLL
+ }
+
+ return SCHEMA_INIT_SUCCESS();
+}
+
+#ifdef GC_DLL
+//-----------------------------------------------------------------------------
+// Purpose: Initializes the "periodic_score_accumulation" section of the schema
+//-----------------------------------------------------------------------------
+struct periodic_score_event_lookup_entry_t { const char *m_pszName; eEconPeriodicScoreEvents m_eValue; bool m_bGCUpdateOnly; };
+static const periodic_score_event_lookup_entry_t sPeriodicScoreEvents[] =
+{
+ { "gifts_distributed", kPeriodicScoreEvent_GiftsDistributed, true },
+ { "duels_won", kPeriodicScoreEvent_DuelsWon, true },
+ { "map_stamps_purchased", kPeriodicScoreEvent_MapStampsPurchased, true },
+};
+
+struct periodic_score_duration_lookup_entry_t { const char *m_pszName; uint32 m_unValue; };
+static const periodic_score_duration_lookup_entry_t sPeriodicScoreDurations[] =
+{
+ { "disabled", 0 },
+ { "hourly", 60 * 60 },
+ { "daily", 60 * 60 * 24 },
+ { "weekly", 60 * 60 * 24 * 7 },
+ { "monthly", 60 * 60 * 24 * 7 * 4 }, // four weeks, not necessarily a month boundary
+};
+
+template < typename search_entry_type, int search_entry_array_size >
+static bool LookupValueFromString( const search_entry_type(&searchArray)[search_entry_array_size], const char *pszSearch, search_entry_type *out_pResult )
+{
+ Assert( out_pResult );
+
+ for ( int i = 0; i < search_entry_array_size; i++ )
+ {
+ if ( !V_stricmp( pszSearch, searchArray[i].m_pszName ) )
+ {
+ *out_pResult = searchArray[i];
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool CEconItemSchema::BInitPeriodicScoring( KeyValues *pKVPeriodicScoring, CUtlVector<CUtlString> *pVecErrors )
+{
+ FOR_EACH_TRUE_SUBKEY( pKVPeriodicScoring, pKVScoreType )
+ {
+ int index = Q_atoi( pKVScoreType->GetName() );
+ SCHEMA_INIT_CHECK( index == m_vecPeriodicScoreTypes.Count(), "Invalid or out-of-order periodic score type '%s'", pKVScoreType->GetName() );
+
+ periodic_score_t PeriodicScore;
+
+ // Reward item definition.
+ const char *pszRewardItemDefName = pKVScoreType->GetString( "reward_item_def_name", NULL );
+ PeriodicScore.m_pRewardItemDefinition = pszRewardItemDefName ? GetItemDefinitionByName( pszRewardItemDefName ) : NULL;
+ SCHEMA_INIT_CHECK( PeriodicScore.m_pRewardItemDefinition, "Periodic score type '%s' missing reward item definition name", pKVScoreType->GetName() );
+
+ // Event type via string lookup.
+ const char *pszEventName = pKVScoreType->GetString( "event", "" );
+ {
+ periodic_score_event_lookup_entry_t EventEntry;
+ SCHEMA_INIT_CHECK( LookupValueFromString( sPeriodicScoreEvents, pszEventName, &EventEntry ),
+ "Periodic score type '%s' could not find event name '%s'", pKVScoreType->GetName(), pszEventName );
+ PeriodicScore.m_eEventType = EventEntry.m_eValue;
+
+ // Note: other parts of the code assume that the event type is associated with the GC-only updatability flag.)
+ PeriodicScore.m_bGCUpdateOnly = EventEntry.m_bGCUpdateOnly;
+ }
+
+ // Time period via string lookup.
+ {
+ const char *pszTimePeriodName = pKVScoreType->GetString( "time_period", "" );
+ periodic_score_duration_lookup_entry_t DurationEntry;
+ SCHEMA_INIT_CHECK( LookupValueFromString( sPeriodicScoreDurations, pszTimePeriodName, &DurationEntry ),
+ "Periodic score type '%s' could not find time period name '%s'", pKVScoreType->GetName(), pszEventName );
+ PeriodicScore.m_unTimePeriodLengthInSeconds = DurationEntry.m_unValue;
+ }
+
+ // Alternate time period specified for use in internal Steam?
+ if ( GGCHost()->GetUniverse() != k_EUniversePublic )
+ {
+ const char *pszInternalTimePeriodName = pKVScoreType->GetString( "time_period_internal", NULL );
+ if ( pszInternalTimePeriodName )
+ {
+ periodic_score_duration_lookup_entry_t InternalDurationEntry;
+ SCHEMA_INIT_CHECK( LookupValueFromString( sPeriodicScoreDurations, pszInternalTimePeriodName, &InternalDurationEntry ),
+ "Periodic score type '%s' could not find internal time period name '%s'", pKVScoreType->GetName(), pszEventName );
+ PeriodicScore.m_unTimePeriodLengthInSeconds = InternalDurationEntry.m_unValue;
+ }
+ }
+
+ m_vecPeriodicScoreTypes.AddToTail( PeriodicScore );
+ }
+
+ return SCHEMA_INIT_SUCCESS();
+}
+#endif // GC_DLL
+
+//-----------------------------------------------------------------------------
+// Purpose: Initializes the "prefabs" section of the schema
+//-----------------------------------------------------------------------------
+bool CEconItemSchema::BInitDefinitionPrefabs( KeyValues *pKVPrefabs, CUtlVector<CUtlString> *pVecErrors )
+{
+ FOR_EACH_TRUE_SUBKEY( pKVPrefabs, pKVPrefab )
+ {
+ const char *pszPrefabName = pKVPrefab->GetName();
+
+ int nMapIndex = m_mapDefinitionPrefabs.Find( pszPrefabName );
+
+ // Make sure the item index is correct because we use this index as a reference
+ SCHEMA_INIT_CHECK(
+ !m_mapDefinitionPrefabs.IsValidIndex( nMapIndex ),
+ "Duplicate prefab name (%s)", pszPrefabName );
+
+ m_mapDefinitionPrefabs.Insert( pszPrefabName, pKVPrefab->MakeCopy() );
+ }
+
+ return SCHEMA_INIT_SUCCESS();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Initializes the Item Series section of the schema
+//-----------------------------------------------------------------------------
+bool CEconItemSchema::BInitItemSeries( KeyValues *pKVSeries, CUtlVector<CUtlString> *pVecErrors )
+{
+ // initialize the item definitions
+ if ( NULL != pKVSeries)
+ {
+ FOR_EACH_TRUE_SUBKEY( pKVSeries, pKVItem )
+ {
+ int nSeriesIndex = pKVItem->GetInt( "value" );
+ int nMapIndex = m_mapItemSeries.Find( nSeriesIndex );
+
+ // Make sure the item index is correct because we use this index as a reference
+ SCHEMA_INIT_CHECK(
+ !m_mapItemSeries.IsValidIndex( nMapIndex ),
+ "Duplicate item series value (%d)", nSeriesIndex );
+
+ nMapIndex = m_mapItemSeries.Insert( nMapIndex );
+ SCHEMA_INIT_SUBSTEP( m_mapItemSeries[nMapIndex].BInitFromKV( pKVItem, pVecErrors ) );
+ }
+ }
+
+ return SCHEMA_INIT_SUCCESS();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Initializes the rarity section of the schema
+//-----------------------------------------------------------------------------
+bool CEconItemSchema::BInitRarities( KeyValues *pKVRarities, KeyValues *pKVRarityWeights, CUtlVector<CUtlString> *pVecErrors )
+{
+ // initialize the item definitions
+ if ( NULL != pKVRarities )
+ {
+ FOR_EACH_TRUE_SUBKEY( pKVRarities, pKVRarity )
+ {
+ int nRarityIndex = pKVRarity->GetInt( "value" );
+ int nMapIndex = m_mapRarities.Find( nRarityIndex );
+
+ // Make sure the item index is correct because we use this index as a reference
+ SCHEMA_INIT_CHECK(
+ !m_mapRarities.IsValidIndex( nMapIndex ),
+ "Duplicate rarity value (%d)", nRarityIndex );
+
+ nMapIndex = m_mapRarities.Insert( nRarityIndex );
+ SCHEMA_INIT_SUBSTEP( m_mapRarities[nMapIndex].BInitFromKV( pKVRarity, pKVRarityWeights, *this, pVecErrors ) );
+ }
+ }
+
+ return SCHEMA_INIT_SUCCESS();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Initializes the qualities section of the schema
+// Input: pKVQualities - The qualities section of the KeyValues
+// representation of the schema
+// pVecErrors - An optional vector that will contain error messages if
+// the init fails.
+// Output: True if initialization succeeded, false otherwise
+//-----------------------------------------------------------------------------
+bool CEconItemSchema::BInitQualities( KeyValues *pKVQualities, CUtlVector<CUtlString> *pVecErrors )
+{
+ // initialize the item definitions
+ if ( NULL != pKVQualities )
+ {
+ FOR_EACH_TRUE_SUBKEY( pKVQualities, pKVQuality )
+ {
+ int nQualityIndex = pKVQuality->GetInt( "value" );
+ int nMapIndex = m_mapQualities.Find( nQualityIndex );
+
+ // Make sure the item index is correct because we use this index as a reference
+ SCHEMA_INIT_CHECK(
+ !m_mapQualities.IsValidIndex( nMapIndex ),
+ "Duplicate quality value (%d)", nQualityIndex );
+
+ nMapIndex = m_mapQualities.Insert( nQualityIndex );
+ SCHEMA_INIT_SUBSTEP( m_mapQualities[nMapIndex].BInitFromKV( pKVQuality, pVecErrors ) );
+ }
+ }
+
+ // Check the integrity of the quality definitions
+
+ // Check for duplicate quality names
+ CUtlRBTree<const char *> rbQualityNames( CaselessStringLessThan );
+ rbQualityNames.EnsureCapacity( m_mapQualities.Count() );
+ FOR_EACH_MAP_FAST( m_mapQualities, i )
+ {
+ int iIndex = rbQualityNames.Find( m_mapQualities[i].GetName() );
+ SCHEMA_INIT_CHECK(
+ !rbQualityNames.IsValidIndex( iIndex ),
+ "Quality definition %d: Duplicate quality name %s", m_mapQualities[i].GetDBValue(), m_mapQualities[i].GetName() );
+
+ if( !rbQualityNames.IsValidIndex( iIndex ) )
+ rbQualityNames.Insert( m_mapQualities[i].GetName() );
+ }
+
+ return SCHEMA_INIT_SUCCESS();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CEconItemSchema::BInitColors( KeyValues *pKVColors, CUtlVector<CUtlString> *pVecErrors )
+{
+ // initialize the color definitions
+ if ( NULL != pKVColors )
+ {
+ FOR_EACH_TRUE_SUBKEY( pKVColors, pKVColor )
+ {
+ CEconColorDefinition *pNewColorDef = new CEconColorDefinition;
+
+ SCHEMA_INIT_SUBSTEP( pNewColorDef->BInitFromKV( pKVColor, pVecErrors ) );
+ m_vecColorDefs.AddToTail( pNewColorDef );
+ }
+ }
+
+ return SCHEMA_INIT_SUCCESS();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+int CEconItemSchema::GetEquipRegionIndexByName( const char *pRegionName ) const
+{
+ FOR_EACH_VEC( m_vecEquipRegionsList, i )
+ {
+ const char *szEntryRegionName = m_vecEquipRegionsList[i].m_sName.Get();
+ if ( !V_stricmp( szEntryRegionName, pRegionName ) )
+ return i;
+ }
+
+ return -1;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+equip_region_mask_t CEconItemSchema::GetEquipRegionBitMaskByName( const char *pRegionName ) const
+{
+ int iRegionIndex = GetEquipRegionIndexByName( pRegionName );
+ if ( !m_vecEquipRegionsList.IsValidIndex( iRegionIndex ) )
+ return 0;
+
+ equip_region_mask_t unRegionMask = 1 << m_vecEquipRegionsList[iRegionIndex].m_unBitIndex;
+ Assert( unRegionMask > 0 );
+
+ return unRegionMask;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CEconItemSchema::SetEquipRegionConflict( int iRegion, unsigned int unBit )
+{
+ Assert( m_vecEquipRegionsList.IsValidIndex( iRegion ) );
+
+ equip_region_mask_t unRegionMask = 1 << unBit;
+ Assert( unRegionMask > 0 );
+
+ m_vecEquipRegionsList[iRegion].m_unMask |= unRegionMask;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+equip_region_mask_t CEconItemSchema::GetEquipRegionMaskByName( const char *pRegionName ) const
+{
+ int iRegionIdx = GetEquipRegionIndexByName( pRegionName );
+ if ( iRegionIdx < 0 )
+ return 0;
+
+ return m_vecEquipRegionsList[iRegionIdx].m_unMask;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CEconItemSchema::AssignDefaultBodygroupState( const char *pszBodygroupName, int iValue )
+{
+ // Flip the value passed in -- if we specify in the schema that a region should be off, we assume that it's
+ // on by default.
+ // actually the schemas are all authored assuming that the default is 0, so let's use that
+ int iDefaultValue = 0; //iValue == 0 ? 1 : 0;
+
+ // Make sure that we're constantly reinitializing our default value to the same default value. This is sort
+ // of dumb but it works for everything we've got now. In the event that conflicts start cropping up it would
+ // be easy enough to make a new schema section.
+ int iIndex = m_mapDefaultBodygroupState.Find( pszBodygroupName );
+ if ( (m_mapDefaultBodygroupState.IsValidIndex( iIndex ) && m_mapDefaultBodygroupState[iIndex] != iDefaultValue) ||
+ (iValue < 0 || iValue > 1) )
+ {
+ EmitWarning( SPEW_GC, 4, "Unable to get accurate read on whether bodygroup '%s' is enabled or disabled by default. (The schema is fine, but the code is confused and could stand to be made smarter.)\n", pszBodygroupName );
+ }
+
+ if ( !m_mapDefaultBodygroupState.IsValidIndex( iIndex ) )
+ {
+ m_mapDefaultBodygroupState.Insert( pszBodygroupName, iDefaultValue );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CEconItemSchema::BInitEquipRegions( KeyValues *pKVEquipRegions, CUtlVector<CUtlString> *pVecErrors )
+{
+ CUtlVector<const char *> vecNames;
+
+ FOR_EACH_SUBKEY( pKVEquipRegions, pKVRegion )
+ {
+ const char *pRegionKeyName = pKVRegion->GetName();
+
+ vecNames.Purge();
+
+ // The "shared" name is special for equip regions -- it means that all of the sub-regions specified
+ // will use the same bit to store equipped-or-not data, but that one bit can be accessed by a whole
+ // bunch of different names. This is useful in TF where different classes have different regions, but
+ // those regions cannot possibly conflict with each other. For example, "scout_backpack" cannot possibly
+ // overlap with "pyro_shoulder" because they can't even be equipped on the same character.
+ if ( pRegionKeyName && !Q_stricmp( pRegionKeyName, "shared" ) )
+ {
+ FOR_EACH_SUBKEY( pKVRegion, pKVSharedRegionName )
+ {
+ vecNames.AddToTail( pKVSharedRegionName->GetName() );
+ }
+ }
+ // We have a standard name -- this one entry is its own equip region.
+ else
+ {
+ vecNames.AddToTail( pRegionKeyName );
+ }
+
+ // What bit will this equip region use to mask against conflicts? If we don't have any equip regions
+ // at all, we'll use the base bit, otherwise we just grab one higher than whatever we used last.
+ unsigned int unNewBitIndex = m_vecEquipRegionsList.Count() <= 0 ? 0 : m_vecEquipRegionsList.Tail().m_unBitIndex + 1;
+
+ FOR_EACH_VEC( vecNames, i )
+ {
+ const char *pRegionName = vecNames[i];
+
+ // Make sure this name is unique.
+ if ( GetEquipRegionIndexByName( pRegionName ) >= 0 )
+ {
+ pVecErrors->AddToTail( CFmtStr( "Duplicate equip region named \"%s\".", pRegionName ).Access() );
+ continue;
+ }
+
+ // Make a new region.
+ EquipRegion newEquipRegion;
+ newEquipRegion.m_sName = pRegionName;
+ newEquipRegion.m_unMask = 0; // we'll update this mask later
+ newEquipRegion.m_unBitIndex = unNewBitIndex;
+
+ int iIdx = m_vecEquipRegionsList.AddToTail( newEquipRegion );
+
+ // Tag this region to conflict with itself so that if nothing else two items in the same
+ // region can't equip over each other.
+ SetEquipRegionConflict( iIdx, unNewBitIndex );
+ }
+ }
+
+ return SCHEMA_INIT_SUCCESS();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CEconItemSchema::BInitEquipRegionConflicts( KeyValues *pKVEquipRegionConflicts, CUtlVector<CUtlString> *pVecErrors )
+{
+ FOR_EACH_TRUE_SUBKEY( pKVEquipRegionConflicts, pKVConflict )
+ {
+ // What region is the base of this conflict?
+ const char *pRegionName = pKVConflict->GetName();
+ int iRegionIdx = GetEquipRegionIndexByName( pRegionName );
+ if ( iRegionIdx < 0 )
+ {
+ pVecErrors->AddToTail( CFmtStr( "Unable to find base equip region named \"%s\" for conflicts.", pRegionName ).Access() );
+ continue;
+ }
+
+ FOR_EACH_SUBKEY( pKVConflict, pKVConflictOther )
+ {
+ const char *pOtherRegionName = pKVConflictOther->GetName();
+ int iOtherRegionIdx = GetEquipRegionIndexByName( pOtherRegionName );
+ if ( iOtherRegionIdx < 0 )
+ {
+ pVecErrors->AddToTail( CFmtStr( "Unable to find other equip region named \"%s\" for conflicts.", pOtherRegionName ).Access() );
+ continue;
+ }
+
+ SetEquipRegionConflict( iRegionIdx, m_vecEquipRegionsList[iOtherRegionIdx].m_unBitIndex );
+ SetEquipRegionConflict( iOtherRegionIdx, m_vecEquipRegionsList[iRegionIdx].m_unBitIndex );
+ }
+ }
+
+ return SCHEMA_INIT_SUCCESS();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Initializes the attributes section of the schema
+// Input: pKVAttributes - The attributes section of the KeyValues
+// representation of the schema
+// pVecErrors - An optional vector that will contain error messages if
+// the init fails.
+// Output: True if initialization succeeded, false otherwise
+//-----------------------------------------------------------------------------
+bool CEconItemSchema::BInitAttributes( KeyValues *pKVAttributes, CUtlVector<CUtlString> *pVecErrors )
+{
+ // Initialize the attribute definitions
+ FOR_EACH_TRUE_SUBKEY( pKVAttributes, pKVAttribute )
+ {
+ int nAttrIndex = Q_atoi( pKVAttribute->GetName() );
+ int nMapIndex = m_mapAttributes.Find( nAttrIndex );
+
+ // Make sure the index is positive
+ SCHEMA_INIT_CHECK(
+ nAttrIndex >= 0,
+ "Attribute definition index %d must be greater than or equal to zero", nAttrIndex );
+
+ // Make sure the attribute index is not repeated
+ SCHEMA_INIT_CHECK(
+ !m_mapAttributes.IsValidIndex( nMapIndex ),
+ "Duplicate attribute definition index (%d)", nAttrIndex );
+
+ nMapIndex = m_mapAttributes.Insert( nAttrIndex );
+
+ SCHEMA_INIT_SUBSTEP( m_mapAttributes[nMapIndex].BInitFromKV( pKVAttribute, pVecErrors ) );
+ }
+
+ // Check the integrity of the attribute definitions
+
+ // Check for duplicate attribute definition names
+ CUtlRBTree<const char *> rbAttributeNames( CaselessStringLessThan );
+ rbAttributeNames.EnsureCapacity( m_mapAttributes.Count() );
+ FOR_EACH_MAP_FAST( m_mapAttributes, i )
+ {
+ int iIndex = rbAttributeNames.Find( m_mapAttributes[i].GetDefinitionName() );
+ SCHEMA_INIT_CHECK(
+ !rbAttributeNames.IsValidIndex( iIndex ),
+ "Attribute definition %d: Duplicate name \"%s\"", m_mapAttributes.Key( i ), m_mapAttributes[i].GetDefinitionName() );
+ if( !rbAttributeNames.IsValidIndex( iIndex ) )
+ rbAttributeNames.Insert( m_mapAttributes[i].GetDefinitionName() );
+ }
+
+ return SCHEMA_INIT_SUCCESS();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Initializes the items section of the schema
+// Input: pKVItems - The items section of the KeyValues
+// representation of the schema
+// pVecErrors - An optional vector that will contain error messages if
+// the init fails.
+// Output: True if initialization succeeded, false otherwise
+//-----------------------------------------------------------------------------
+bool CEconItemSchema::BInitItems( KeyValues *pKVItems, CUtlVector<CUtlString> *pVecErrors )
+{
+ m_mapItems.PurgeAndDeleteElements();
+ m_mapItemsSorted.Purge();
+ m_mapToolsItems.Purge();
+ m_mapBaseItems.Purge();
+ m_vecBundles.Purge();
+ m_mapQuestObjectives.PurgeAndDeleteElements();
+ m_vecItemCollectionCrates.Purge();
+
+#if defined(CLIENT_DLL) || defined(GAME_DLL)
+ if ( m_pDefaultItemDefinition )
+ {
+ delete m_pDefaultItemDefinition;
+ m_pDefaultItemDefinition = NULL;
+ }
+#endif
+
+ // initialize the item definitions
+ if ( NULL != pKVItems )
+ {
+ FOR_EACH_TRUE_SUBKEY( pKVItems, pKVItem )
+ {
+ if ( Q_stricmp( pKVItem->GetName(), "default" ) == 0 )
+ {
+#if defined(CLIENT_DLL) || defined(GAME_DLL)
+ SCHEMA_INIT_CHECK(
+ m_pDefaultItemDefinition == NULL,
+ "Duplicate 'default' item definition." );
+
+ m_pDefaultItemDefinition = CreateEconItemDefinition();
+ SCHEMA_INIT_SUBSTEP( m_pDefaultItemDefinition->BInitFromKV( pKVItem, pVecErrors ) );
+#endif
+ }
+ else
+ {
+ int nItemIndex = Q_atoi( pKVItem->GetName() );
+ int nMapIndex = m_mapItems.Find( nItemIndex );
+
+ // Make sure the item index is correct because we use this index as a reference
+ SCHEMA_INIT_CHECK(
+ !m_mapItems.IsValidIndex( nMapIndex ),
+ "Duplicate item definition (%d)", nItemIndex );
+
+ // Check to make sure the index is positive
+ SCHEMA_INIT_CHECK(
+ nItemIndex >= 0,
+ "Item definition index %d must be greater than or equal to zero", nItemIndex );
+
+ CEconItemDefinition *pItemDef = CreateEconItemDefinition();
+ nMapIndex = m_mapItems.Insert( nItemIndex, pItemDef );
+ m_mapItemsSorted.Insert( nItemIndex, pItemDef );
+ SCHEMA_INIT_SUBSTEP( m_mapItems[nMapIndex]->BInitFromKV( pKVItem, pVecErrors ) );
+
+ // Cache off Tools references
+ if ( pItemDef->IsTool() )
+ {
+ m_mapToolsItems.Insert( nItemIndex, pItemDef );
+ }
+
+ if ( pItemDef->IsBaseItem() )
+ {
+ m_mapBaseItems.Insert( nItemIndex, pItemDef );
+ }
+
+ // Cache off bundles for the link phase below.
+ if ( pItemDef->IsBundle() )
+ {
+ // Cache off the item def for the bundle, since we'll need both the bundle info and the item def index later.
+ m_vecBundles.AddToTail( pItemDef );
+
+ // If the bundle is a pack bundle, mark all the contained items as pack items / link to the owning pack bundle
+ if ( pItemDef->IsPackBundle() )
+ {
+ const bundleinfo_t *pBundleInfo = pItemDef->GetBundleInfo();
+ FOR_EACH_VEC( pBundleInfo->vecItemDefs, iCurItem )
+ {
+ CEconItemDefinition *pCurItemDef = pBundleInfo->vecItemDefs[ iCurItem ];
+ SCHEMA_INIT_CHECK( NULL == pCurItemDef->m_pOwningPackBundle, "Pack item \"%s\" included in more than one pack bundle - not allowed!", pCurItemDef->GetDefinitionName() );
+ pCurItemDef->m_pOwningPackBundle = pItemDef;
+ }
+ }
+ }
+
+ static CSchemaAttributeDefHandle pAttrDef_ContainsCollection( "contains collection" );
+ if ( pAttrDef_ContainsCollection )
+ {
+ FOR_EACH_VEC( pItemDef->GetStaticAttributes(), i )
+ {
+ const static_attrib_t& staticAttrib = pItemDef->GetStaticAttributes()[i];
+ if ( staticAttrib.iDefIndex == pAttrDef_ContainsCollection->GetDefinitionIndex() )
+ {
+ // Add to collection crate list
+ m_vecItemCollectionCrates.AddToTail( pItemDef->GetDefinitionIndex() );
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // Check the integrity of the item definitions
+ CUtlRBTree<const char *> rbItemNames( CaselessStringLessThan );
+ rbItemNames.EnsureCapacity( m_mapItems.Count() );
+ FOR_EACH_MAP_FAST( m_mapItems, i )
+ {
+ CEconItemDefinition *pItemDef = m_mapItems[ i ];
+
+ // Check for duplicate item definition names
+ int iIndex = rbItemNames.Find( pItemDef->GetDefinitionName() );
+ SCHEMA_INIT_CHECK(
+ !rbItemNames.IsValidIndex( iIndex ),
+ "Item definition %s: Duplicate name on index %d", pItemDef->GetDefinitionName(), m_mapItems.Key( i ) );
+ if( !rbItemNames.IsValidIndex( iIndex ) )
+ rbItemNames.Insert( m_mapItems[i]->GetDefinitionName() );
+
+ // Link up armory and store mappings for the item
+ SCHEMA_INIT_SUBSTEP( pItemDef->BInitItemMappings( pVecErrors ) );
+ }
+
+#ifdef DOTA
+ // Go through all regular (ie non-pack) bundles and ensure that if any pack items are included, *all* pack items in the owning pack bundle are included
+ FOR_EACH_VEC( m_vecBundles, iBundle )
+ {
+ const CEconItemDefinition *pBundleItemDef = m_vecBundles[ iBundle ];
+ if ( pBundleItemDef->IsPackBundle() )
+ continue;
+
+ // Go through all items in the bundle and look for pack items
+ const bundleinfo_t *pBundle = pBundleItemDef->GetBundleInfo();
+ if ( pBundle )
+ {
+ FOR_EACH_VEC( pBundle->vecItemDefs, iContainedBundleItem )
+ {
+ // Get the associated pack bundle
+ const CEconItemDefinition *pContainedBundleItemDef = pBundle->vecItemDefs[ iContainedBundleItem ];
+
+ // Ignore non-pack items
+ if ( !pContainedBundleItemDef || !pContainedBundleItemDef->IsPackItem() )
+ continue;
+
+ // Get the pack bundle that contains this particular pack item
+ const CEconItemDefinition *pOwningPackBundleItemDef = pContainedBundleItemDef->GetOwningPackBundle();
+
+ // Make sure all items in the owning pack bundle are in pBundleItemDef's bundle info (pBundle)
+ const bundleinfo_t *pOwningPackBundle = pOwningPackBundleItemDef->GetBundleInfo();
+ FOR_EACH_VEC( pOwningPackBundle->vecItemDefs, iCurPackBundleItem )
+ {
+ CEconItemDefinition *pCurPackBundleItem = pOwningPackBundle->vecItemDefs[ iCurPackBundleItem ];
+ if ( !pBundle->vecItemDefs.HasElement( pCurPackBundleItem ) )
+ {
+ SCHEMA_INIT_CHECK(
+ false,
+ "The bundle \"%s\" contains some, but not all pack items required specified by pack bundle \"%s.\"",
+ pBundleItemDef->GetDefinitionName(),
+ pOwningPackBundleItemDef->GetDefinitionName()
+ );
+ }
+ }
+ }
+ }
+ }
+#endif
+
+ return SCHEMA_INIT_SUCCESS();
+}
+
+#if 0 // Compiled out until some DotA changes from the item editor are brought over
+//-----------------------------------------------------------------------------
+// Purpose: Delete an item definition. Moderately dangerous as cached references will become bad.
+// Intended for use by the item editor.
+//-----------------------------------------------------------------------------
+bool CEconItemSchema::DeleteItemDefinition( int iDefIndex )
+{
+ m_mapItemsSorted.Remove( iDefIndex );
+
+ int nMapIndex = m_mapItems.Find( iDefIndex );
+ if ( m_mapItems.IsValidIndex( nMapIndex ) )
+ {
+ CEconItemDefinition* pItemDef = m_mapItems[nMapIndex];
+ if ( pItemDef )
+ {
+ m_mapItems.RemoveAt( nMapIndex );
+ delete pItemDef;
+ return true;
+ }
+ }
+ return false;
+}
+#endif
+
+//-----------------------------------------------------------------------------
+// Purpose: Parses the Item Sets section.
+//-----------------------------------------------------------------------------
+bool CEconItemSchema::BInitItemSets( KeyValues *pKVItemSets, CUtlVector<CUtlString> *pVecErrors )
+{
+ m_mapItemSets.RemoveAll();
+
+ if ( NULL != pKVItemSets )
+ {
+ FOR_EACH_TRUE_SUBKEY( pKVItemSets, pKVItemSet )
+ {
+ const char* setName = pKVItemSet->GetName();
+
+ SCHEMA_INIT_CHECK( setName != NULL, "All itemsets must have names." );
+ SCHEMA_INIT_CHECK( m_mapItemSets.Find( setName ) == m_mapItemSets.InvalidIndex(), "Duplicate itemset name (%s) found!", setName );
+
+ int idx = m_mapItemSets.Insert( setName, new CEconItemSetDefinition );
+ SCHEMA_INIT_SUBSTEP( m_mapItemSets[idx]->BInitFromKV( pKVItemSet, pVecErrors ) );
+ }
+
+ // Once we've initialized all of our item sets, loop through all of our item definitions looking
+ // for pseudo set items. For example, the Festive Holy Mackerel is a different item definition from
+ // the regular Holy Mackerel, but for set completion and set listing purposes, we want it to show
+ // as part of the base set.
+ FOR_EACH_MAP_FAST( m_mapItems, i )
+ {
+ CEconItemDefinition *pItemDef = m_mapItems[i];
+ Assert( pItemDef );
+
+ // Items that point to themselves are the base set items and got initialized as part of the
+ // set initialization above.
+ if ( pItemDef->GetSetItemRemap() == pItemDef->GetDefinitionIndex() )
+ continue;
+
+ // Which item are we stealing set information from?
+ const CEconItemDefinition *pRemappedSetItemDef = GetItemDefinition( pItemDef->GetSetItemRemap() );
+ AssertMsg( pRemappedSetItemDef, "Somehow got through item and set initialization but have a broken set remap item!" );
+
+ pItemDef->SetItemSetDefinition( pRemappedSetItemDef->GetItemSetDefinition() );
+ }
+ }
+
+ return SCHEMA_INIT_SUCCESS();
+}
+
+//-----------------------------------------------------------------------------
+bool CEconItemSchema::BVerifyBaseItemNames( CUtlVector<CUtlString> *pVecErrors )
+{
+ FOR_EACH_MAP_FAST( m_mapItems, i )
+ {
+ CEconItemDefinition *pItemDef = m_mapItems[i];
+
+ // get base item name
+ const char* pBaseName = pItemDef->GetBaseFunctionalItemName();
+
+ if ( !pBaseName || pBaseName[0] == '\0' )
+ {
+ continue;
+ }
+
+ // look up base item name
+ SCHEMA_INIT_CHECK( GetItemDefinitionByName( pBaseName ) != NULL, "Base item name not found %s.", pBaseName );
+ }
+
+ return SCHEMA_INIT_SUCCESS();
+}
+
+//-----------------------------------------------------------------------------
+bool CEconItemSchema::BInitItemCollections( KeyValues *pKVItemCollections, CUtlVector<CUtlString> *pVecErrors )
+{
+ m_mapItemCollections.Purge();
+
+ if ( NULL != pKVItemCollections )
+ {
+ FOR_EACH_TRUE_SUBKEY( pKVItemCollections, pKVItemCollection )
+ {
+ const char* setName = pKVItemCollection->GetName();
+
+ SCHEMA_INIT_CHECK( setName != NULL, "All item collections must have names." );
+ SCHEMA_INIT_CHECK( m_mapItemCollections.Find( setName ) == m_mapItemCollections.InvalidIndex(), "Duplicate item collection name (%s) found!", setName );
+
+ int idx = m_mapItemCollections.Insert( setName, new CEconItemCollectionDefinition );
+ SCHEMA_INIT_SUBSTEP( m_mapItemCollections[idx]->BInitFromKV( pKVItemCollection, pVecErrors ) );
+ }
+ }
+
+ return SCHEMA_INIT_SUCCESS();
+}
+
+//-----------------------------------------------------------------------------
+bool CEconItemSchema::BInitCollectionReferences( CUtlVector<CUtlString> *pVecErrors )
+{
+ FOR_EACH_MAP_FAST( m_mapItems, i )
+ {
+ CEconItemDefinition *pItemDef = m_mapItems[i];
+ const char *pszCollectionName = pItemDef->GetCollectionReference();
+ if ( pszCollectionName )
+ {
+ // Find the collection
+ bool bFound = false;
+ FOR_EACH_MAP_FAST( m_mapItemCollections, iCollectionIndex )
+ {
+ const char * pszTemp = m_mapItemCollections[iCollectionIndex]->m_pszName;
+
+ if ( !V_strcmp( pszTemp, pszCollectionName) )
+ {
+ bFound = true;
+ pItemDef->SetItemCollectionDefinition( m_mapItemCollections[iCollectionIndex] );
+ break;
+ }
+ }
+ SCHEMA_INIT_CHECK( bFound == true, "Collection %s referenced by item %s not found", pszCollectionName, pItemDef->GetDefinitionName() );
+ }
+ }
+
+ return SCHEMA_INIT_SUCCESS();
+}
+//-----------------------------------------------------------------------------
+const CEconItemCollectionDefinition *CEconItemSchema::GetCollectionByName( const char* pCollectionName )
+{
+ if ( !pCollectionName )
+ return NULL;
+
+ FOR_EACH_MAP_FAST( m_mapItemCollections, iCollectionIndex )
+ {
+ const char * pszTemp = m_mapItemCollections[iCollectionIndex]->m_pszName;
+ if ( !V_strcmp( pszTemp, pCollectionName ) )
+ {
+ return m_mapItemCollections[iCollectionIndex];
+ }
+ }
+ return NULL;
+}
+
+
+
+//-----------------------------------------------------------------------------
+bool CEconItemSchema::BInitItemPaintKitDefinitions( KeyValues *pKVItemPaintKits, CUtlVector<CUtlString> *pVecErrors )
+{
+ m_mapItemPaintKits.Purge();
+
+ const char* cWhitespace = " \r\n\t"; // space, end of line, tab.
+ cWhitespace; // Compiler happiness for GC build
+
+ if ( NULL != pKVItemPaintKits )
+ {
+#ifdef CLIENT_DLL
+ FOR_EACH_TRUE_SUBKEY( pKVItemPaintKits, pKVPaintKit )
+ {
+ const char* keyField = pKVPaintKit->GetName();
+ SCHEMA_INIT_CHECK( keyField != NULL, "All item collections must have names." );
+
+ if ( V_stristr( keyField, "paintkit_template" ) != NULL )
+ {
+ static const int cSkipLen = strlen( "paintkit_template" );
+ keyField += cSkipLen;
+ keyField += strspn( keyField, cWhitespace );
+
+ bool createTmplResult = materials->AddTextureCompositorTemplate( keyField, pKVPaintKit );
+ SCHEMA_INIT_CHECK( createTmplResult, "Could Not Create paintkit_template '%s'", keyField );
+ }
+ }
+
+ // Do post-load validation before moving on to paintkits.
+ SCHEMA_INIT_CHECK( materials->VerifyTextureCompositorTemplates(), "Paintkit template post-init validation failed." );
+#endif
+
+ // Now do all the paintkits
+ FOR_EACH_TRUE_SUBKEY( pKVItemPaintKits, pKVPaintKit )
+ {
+ // We know the keyField is valid, it was checked above.
+ const char* keyField = pKVPaintKit->GetName();
+
+ if ( V_stristr( keyField, "paintkit_template" ) == NULL )
+ {
+ SCHEMA_INIT_CHECK( m_mapItemPaintKits.Find( keyField ) == m_mapItemPaintKits.InvalidIndex(), "Duplicate paint kit definition name (%s) found!", keyField );
+
+ int idx = m_mapItemPaintKits.Insert( keyField, new CEconItemPaintKitDefinition );
+ SCHEMA_INIT_SUBSTEP( m_mapItemPaintKits[ idx ]->BInitFromKV( pKVPaintKit, pVecErrors ) );
+ }
+ }
+ }
+
+
+ return SCHEMA_INIT_SUCCESS();
+}
+
+//-----------------------------------------------------------------------------
+bool CEconItemSchema::BInitOperationDefinitions( KeyValues *pKVGameInfo, KeyValues *pKVOperationDefinitions, CUtlVector<CUtlString> *pVecErrors )
+{
+ m_mapOperationDefinitions.Purge();
+
+ if ( NULL != pKVOperationDefinitions )
+ {
+ FOR_EACH_TRUE_SUBKEY( pKVOperationDefinitions, pKVOperation )
+ {
+ const char* setName = pKVOperation->GetName();
+ SCHEMA_INIT_CHECK( setName != NULL, "All operations must have names." );
+ SCHEMA_INIT_CHECK( m_mapOperationDefinitions.Find( setName ) == m_mapOperationDefinitions.InvalidIndex(), "Duplicate operation definition name (%s) found!", setName );
+
+ CEconOperationDefinition *pNewOperation = new CEconOperationDefinition();
+ SCHEMA_INIT_SUBSTEP( pNewOperation->BInitFromKV( pKVOperation, pVecErrors ) );
+
+ // don't add expired operation to list
+ if ( pNewOperation->IsExpired() )
+ {
+ delete pNewOperation;
+ continue;
+ }
+
+ m_mapOperationDefinitions.Insert( setName, pNewOperation );
+ }
+ }
+
+ return SCHEMA_INIT_SUCCESS();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Initializes the timed rewards section of the schema
+// Input: pKVTimedRewards - The timed_rewards section of the KeyValues
+// representation of the schema
+// pVecErrors - An optional vector that will contain error messages if
+// the init fails.
+// Output: True if initialization succeeded, false otherwise
+//-----------------------------------------------------------------------------
+bool CEconItemSchema::BInitTimedRewards( KeyValues *pKVTimedRewards, CUtlVector<CUtlString> *pVecErrors )
+{
+ m_vecTimedRewards.RemoveAll();
+
+ // initialize the rewards sections
+ if ( NULL != pKVTimedRewards )
+ {
+ FOR_EACH_TRUE_SUBKEY( pKVTimedRewards, pKVTimedReward )
+ {
+ int index = m_vecTimedRewards.AddToTail();
+ SCHEMA_INIT_SUBSTEP( m_vecTimedRewards[index].BInitFromKV( pKVTimedReward, pVecErrors ) );
+ }
+ }
+
+ return SCHEMA_INIT_SUCCESS();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+const CTimedItemRewardDefinition* CEconItemSchema::GetTimedReward( eTimedRewardType type ) const
+{
+ if ( (int)type < m_vecTimedRewards.Count() )
+ {
+ return &m_vecTimedRewards[type];
+ }
+ return NULL;
+}
+
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Initializes the loot lists section of the schema
+//-----------------------------------------------------------------------------
+bool CEconItemSchema::BInitLootLists( KeyValues *pKVLootLists, CUtlVector<CUtlString> *pVecErrors )
+{
+ if ( NULL != pKVLootLists )
+ {
+ FOR_EACH_TRUE_SUBKEY( pKVLootLists, pKVLootList )
+ {
+ const char* pListName = pKVLootList->GetName();
+ SCHEMA_INIT_SUBSTEP( BInsertLootlist( pListName, pKVLootList, pVecErrors ) );
+ }
+ }
+
+ FOR_EACH_MAP_FAST( m_mapLootLists, i )
+ {
+ const CEconLootListDefinition *pLootList = m_mapLootLists[i];
+ BVerifyLootListItemDropDates( pLootList, pVecErrors );
+ }
+
+ return SCHEMA_INIT_SUCCESS();
+}
+//-----------------------------------------------------------------------------
+bool CEconItemSchema::BInsertLootlist( const char *pListName, KeyValues *pKVLootList, CUtlVector<CUtlString> *pVecErrors )
+{
+ SCHEMA_INIT_CHECK( pListName != NULL, "All lootlists must have names." );
+
+ if ( m_mapLootLists.Count() > 0 )
+ {
+ SCHEMA_INIT_CHECK( GetLootListByName( pListName ) == NULL, "Duplicate lootlist name (%s) found!", pListName );
+ }
+
+ CEconLootListDefinition *pLootList = new CEconLootListDefinition;
+ SCHEMA_INIT_SUBSTEP( pLootList->BInitFromKV( pKVLootList, *this, pVecErrors ) );
+ m_mapLootLists.Insert( pListName, pLootList );
+
+ return SCHEMA_INIT_SUCCESS();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Initializes the revolving loot lists section of the schema
+//-----------------------------------------------------------------------------
+bool CEconItemSchema::BInitRevolvingLootLists( KeyValues *pKVLootLists, CUtlVector<CUtlString> *pVecErrors )
+{
+ m_mapRevolvingLootLists.RemoveAll();
+
+ if ( NULL != pKVLootLists )
+ {
+ FOR_EACH_SUBKEY( pKVLootLists, pKVList )
+ {
+ int iListIdx = pKVList->GetInt();
+ const char* strListName = pKVList->GetName();
+ m_mapRevolvingLootLists.Insert( iListIdx, strListName );
+ }
+ }
+
+ FOR_EACH_MAP_FAST( m_mapRevolvingLootLists, i )
+ {
+ const CEconLootListDefinition* pLootList = GetLootListByName(m_mapRevolvingLootLists[i]);
+ BVerifyLootListItemDropDates( pLootList, pVecErrors );
+ }
+
+ return SCHEMA_INIT_SUCCESS();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Create and return a new quest objective definition. Verify that
+// a definition with the same name doesnt alreay exist.
+//-----------------------------------------------------------------------------
+bool CEconItemSchema::AddQuestObjective( const CQuestObjectiveDefinition **ppQuestObjective, KeyValues *pKVObjective, CUtlVector<CUtlString> *pVecErrors )
+{
+ // These need to be unique
+ int nDefIndex = pKVObjective->GetInt( "defindex", -1 );
+ SCHEMA_INIT_CHECK( nDefIndex != -1, "Missing defindex for quest objective" );
+ // Verify defindex is unique
+ auto nMapIndex = m_mapQuestObjectives.Find( nDefIndex );
+ SCHEMA_INIT_CHECK( nMapIndex == m_mapQuestObjectives.InvalidIndex(), "Multiple quest objectives with defindex: %d", nDefIndex );
+ // Create the quest def
+ nMapIndex = m_mapQuestObjectives.Insert( nDefIndex );
+ m_mapQuestObjectives[ nMapIndex ] = CreateQuestDefinition();
+ // Init
+ SCHEMA_INIT_SUBSTEP( m_mapQuestObjectives[nMapIndex]->BInitFromKV( pKVObjective, pVecErrors ) );
+
+ if ( ppQuestObjective )
+ {
+ (*ppQuestObjective) = m_mapQuestObjectives[nMapIndex];
+ }
+
+ return SCHEMA_INIT_SUCCESS();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Verify that the contents of visible lootlist do not have drop dates
+// associated with them. The thinking being that we dont want to have
+// items listed that could potentially not drop, or items disappear/appear
+// in from a list.
+//-----------------------------------------------------------------------------
+bool CEconItemSchema::BVerifyLootListItemDropDates( const CEconLootListDefinition* pLootList, CUtlVector<CUtlString> *pVecErrors ) const
+{
+ if ( pLootList && pLootList->BPublicListContents() )
+ {
+ BRecurseiveVerifyLootListItemDropDates( pLootList, pLootList, pVecErrors );
+ }
+
+ return SCHEMA_INIT_SUCCESS();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Recursively dig through all entries in the passed in lootlist to see
+// if any of the containted items have drop dates.
+//-----------------------------------------------------------------------------
+bool CEconItemSchema::BRecurseiveVerifyLootListItemDropDates( const CEconLootListDefinition* pLootList, const CEconLootListDefinition* pRootLootList, CUtlVector<CUtlString> *pVecErrors ) const
+{
+ FOR_EACH_VEC( pLootList->GetLootListContents(), j )
+ {
+ const CEconLootListDefinition::drop_item_t& item = pLootList->GetLootListContents()[j];
+ // 0 and greater means item. Less than 0 means nested lootlist
+ if( item.m_iItemOrLootlistDef >= 0 )
+ {
+ const CEconItemDefinition* pItemDef = GetItemSchema()->GetItemDefinition( item.m_iItemOrLootlistDef );
+ if( pItemDef )
+ {
+ static CSchemaAttributeDefHandle pAttribDef_StartDropDate( "start drop date" );
+ static CSchemaAttributeDefHandle pAttribDef_EndDropDate( "end drop date" );
+
+ CAttribute_String value;
+
+ // Check for start drop date attribute on this item
+ SCHEMA_INIT_CHECK( !FindAttribute( pItemDef, pAttribDef_StartDropDate, &value ),
+ "Lootlist \"%s\" contains lootlist \"%s\", which contains item \"%s\", which has start drop date.", pRootLootList->GetName(), pLootList->GetName(), pItemDef->GetDefinitionName() );
+ // Check for end drop date attribute on this item
+ SCHEMA_INIT_CHECK( !FindAttribute( pItemDef, pAttribDef_EndDropDate, &value ),
+ "Lootlist \"%s\" contains lootlist \"%s\", which contains item \"%s\", which has end drop date.", pRootLootList->GetName(), pLootList->GetName(), pItemDef->GetDefinitionName() );
+ }
+ }
+ else
+ {
+ // Get the nested lootlist
+ int iLLIndex = (item.m_iItemOrLootlistDef * -1) - 1;
+ const CEconLootListDefinition *pNestedLootList = GetItemSchema()->GetLootListByIndex( iLLIndex );
+ if ( !pNestedLootList )
+ return SCHEMA_INIT_SUCCESS();
+
+ // Dig through all of this lootlist's entries
+ BRecurseiveVerifyLootListItemDropDates( pNestedLootList, pRootLootList, pVecErrors );
+ }
+ }
+
+ return SCHEMA_INIT_SUCCESS();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Initializes the recipes section of the schema
+// Input: pKVRecipes - The recipes section of the KeyValues
+// representation of the schema
+// pVecErrors - An optional vector that will contain error messages if
+// the init fails.
+// Output: True if initialization succeeded, false otherwise
+//-----------------------------------------------------------------------------
+bool CEconItemSchema::BInitRecipes( KeyValues *pKVRecipes, CUtlVector<CUtlString> *pVecErrors )
+{
+ m_mapRecipes.RemoveAll();
+
+ // initialize the rewards sections
+ if ( NULL != pKVRecipes )
+ {
+ FOR_EACH_TRUE_SUBKEY( pKVRecipes, pKVRecipe )
+ {
+ int nRecipeIndex = Q_atoi( pKVRecipe->GetName() );
+ int nMapIndex = m_mapRecipes.Find( nRecipeIndex );
+
+ // Make sure the recipe index is correct because we use this index as a reference
+ SCHEMA_INIT_CHECK(
+ !m_mapRecipes.IsValidIndex( nMapIndex ),
+ "Duplicate recipe definition (%d)", nRecipeIndex );
+
+ // Check to make sure the index is positive
+ SCHEMA_INIT_CHECK(
+ nRecipeIndex >= 0,
+ "Recipe definition index %d must be greater than or equal to zero", nRecipeIndex );
+
+ CEconCraftingRecipeDefinition *recipeDef = CreateCraftingRecipeDefinition();
+ SCHEMA_INIT_SUBSTEP( recipeDef->BInitFromKV( pKVRecipe, pVecErrors ) );
+
+#ifdef _DEBUG
+ // Sanity check in debug builds so that we know we aren't putting the same recipe in
+ // multiple times.
+ FOR_EACH_MAP_FAST( m_mapRecipes, i )
+ {
+ Assert( i != nRecipeIndex );
+ Assert( m_mapRecipes[i] != recipeDef );
+ }
+#endif // _DEBUG
+
+ // Store this recipe.
+ m_mapRecipes.Insert( nRecipeIndex, recipeDef );
+ }
+ }
+
+ return SCHEMA_INIT_SUCCESS();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Builds the name of a achievement in the form App<ID>.<AchName>
+// Input: unAppID - native app ID
+// pchNativeAchievementName - name of the achievement in its native app
+// Returns: The combined achievement name
+//-----------------------------------------------------------------------------
+CUtlString CEconItemSchema::ComputeAchievementName( AppId_t unAppID, const char *pchNativeAchievementName )
+{
+ return CFmtStr1024( "App%u.%s", unAppID, pchNativeAchievementName ).Access();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Initializes the achievement rewards section of the schema
+// Input: pKVAchievementRewards - The achievement_rewards section of the KeyValues
+// representation of the schema
+// pVecErrors - An optional vector that will contain error messages if
+// the init fails.
+// Output: True if initialization succeeded, false otherwise
+//-----------------------------------------------------------------------------
+bool CEconItemSchema::BInitAchievementRewards( KeyValues *pKVAchievementRewards, CUtlVector<CUtlString> *pVecErrors )
+{
+ m_dictAchievementRewards.RemoveAll();
+ m_mapAchievementRewardsByData.PurgeAndDeleteElements();
+
+ // initialize the rewards sections
+ if ( NULL != pKVAchievementRewards )
+ {
+ FOR_EACH_SUBKEY( pKVAchievementRewards, pKVReward )
+ {
+ AchievementAward_t award;
+ if( pKVReward->GetDataType() == KeyValues::TYPE_NONE )
+ {
+ int32 nItemIndex = pKVReward->GetInt( "DefIndex", -1 );
+ if( nItemIndex != -1 )
+ {
+ award.m_vecDefIndex.AddToTail( (uint16)nItemIndex );
+ }
+ else
+ {
+ KeyValues *pkvItems = pKVReward->FindKey( "Items" );
+ SCHEMA_INIT_CHECK(
+ pkvItems != NULL,
+ "Complex achievement %s must have an Items key or a DefIndex field", pKVReward->GetName() );
+ if( !pkvItems )
+ {
+ continue;
+ }
+
+ FOR_EACH_VALUE( pkvItems, pkvItem )
+ {
+ award.m_vecDefIndex.AddToTail( (uint16)Q_atoi( pkvItem->GetName() ) );
+ }
+ }
+
+ }
+ else
+ {
+ award.m_vecDefIndex.AddToTail( (uint16)pKVReward->GetInt("", -1 ) );
+ }
+
+ // make sure all the item types are valid
+ bool bFoundAllItems = true;
+ FOR_EACH_VEC( award.m_vecDefIndex, nItem )
+ {
+ const CEconItemDefinition *pDefn = GetItemDefinition( award.m_vecDefIndex[nItem] );
+ SCHEMA_INIT_CHECK(
+ pDefn != NULL,
+ "Item definition index %d in achievement reward %s was not found", award.m_vecDefIndex[nItem], pKVReward->GetName() );
+ if( !pDefn )
+ {
+ bFoundAllItems = false;
+ }
+ }
+ if( !bFoundAllItems )
+ continue;
+
+ SCHEMA_INIT_CHECK(
+ award.m_vecDefIndex.Count() > 0,
+ "Achievement reward %s has no items!", pKVReward->GetName() );
+ if( award.m_vecDefIndex.Count() == 0 )
+ continue;
+
+#ifdef GC_DLL
+ award.m_unSourceAppId = GGCBase()->GetAppID();
+#else
+ award.m_unSourceAppId = k_uAppIdInvalid;
+#endif
+ if( pKVReward->GetDataType() == KeyValues::TYPE_NONE )
+ {
+ // cross game achievement
+ award.m_sNativeName = pKVReward->GetName();
+ award.m_unAuditData = pKVReward->GetInt( "AuditData", 0 );
+ award.m_unSourceAppId = pKVReward->GetInt( "SourceAppID", award.m_unSourceAppId );
+ }
+ else
+ {
+ award.m_sNativeName = pKVReward->GetName();
+ award.m_unAuditData = 0;
+ }
+
+
+#ifdef GC_DLL
+ // Check to make sure the audit data is valid
+ SCHEMA_INIT_CHECK(
+ award.m_unSourceAppId >= 0,
+ "Source App ID %d in achievement reward %s must be valid", award.m_unSourceAppId, pKVReward->GetName() );
+ if( award.m_unSourceAppId == k_uAppIdInvalid )
+ continue;
+
+ if( !GGCGameBase()->BYieldingLoadStats( award.m_unSourceAppId ) )
+ {
+ // this will often fail in a dev universe
+ if( GGCHost()->GetUniverse() != k_EUniverseDev )
+ {
+ SCHEMA_INIT_CHECK(
+ false,
+ "Unable to load stats schema for cross-game achievement %s for app %d", pKVReward->GetName(), award.m_unSourceAppId );
+ }
+ continue;
+ }
+
+ const CGCStatsSchema *pStatsSchema = GGCGameBase()->GetStatsSchema( award.m_unSourceAppId );
+ if( !pStatsSchema )
+ {
+ SCHEMA_INIT_CHECK(
+ false,
+ "Unable to retrieve stats schema for cross-game achievement %s for app %d", pKVReward->GetName(), award.m_unSourceAppId );
+ continue;
+ }
+
+ if( award.m_unAuditData == 0 )
+ {
+ uint16 usStatID, usBitID;
+ if( !pStatsSchema->BGetAchievementBit( award.m_sNativeName, &usStatID, &usBitID ) )
+ {
+ SCHEMA_INIT_CHECK(
+ false,
+ "Unable to find achievement %s for app %d", award.m_sNativeName.Get(), award.m_unSourceAppId );
+ continue;
+ }
+
+ award.m_unAuditData = ( usStatID <<16 ) | usBitID;
+ }
+#endif // GC_DLL
+
+
+ AchievementAward_t *pAward = new AchievementAward_t;
+ *pAward = award;
+
+ m_dictAchievementRewards.Insert( ComputeAchievementName( pAward->m_unSourceAppId, pAward->m_sNativeName ), pAward );
+ m_mapAchievementRewardsByData.Insert( pAward->m_unAuditData, pAward );
+ }
+ }
+
+ return SCHEMA_INIT_SUCCESS();
+}
+
+
+#ifdef GC_DLL
+bool CEconItemSchema::BInitRandomAttributeTemplates( KeyValues *pKVRandomAttributeTemplates, CUtlVector<CUtlString> *pVecErrors )
+{
+ m_dictRandomAttributeTemplates.PurgeAndDeleteElements();
+
+ FOR_EACH_TRUE_SUBKEY( pKVRandomAttributeTemplates, pKVAttributeTemplate )
+ {
+ const char *pszAttrName = pKVAttributeTemplate->GetName();
+
+ // try to create random attrib from template
+ random_attrib_t *pRandomAttr = CreateRandomAttribute( __FUNCTION__, pKVAttributeTemplate, pVecErrors );
+ SCHEMA_INIT_CHECK(
+ NULL != pRandomAttr,
+ CFmtStr( "%s: Failed to create random_attrib_t '%s'", __FUNCTION__, pszAttrName ) );
+
+ m_dictRandomAttributeTemplates.Insert( pszAttrName, pRandomAttr );
+ }
+
+ return true;
+}
+#endif // GC_DLL
+
+
+#ifdef TF_CLIENT_DLL
+//-----------------------------------------------------------------------------
+// Purpose: Go through all items and cache the number of concrete items in each.
+//-----------------------------------------------------------------------------
+bool CEconItemSchema::BInitConcreteItemCounts( CUtlVector<CUtlString> *pVecErrors )
+{
+ FOR_EACH_MAP_FAST( m_mapItems, i )
+ {
+ CEconItemDefinition *pItemDef = m_mapItems[ i ];
+ pItemDef->m_unNumConcreteItems = CalculateNumberOfConcreteItems( pItemDef );
+ }
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Returns the number of actual "real" items referenced by the item definition
+// (i.e. items that would take up space in the inventory)
+//-----------------------------------------------------------------------------
+int CEconItemSchema::CalculateNumberOfConcreteItems( const CEconItemDefinition *pItemDef )
+{
+ AssertMsg( pItemDef, "NULL item definition! This should not happen!" );
+ if ( !pItemDef )
+ return 0;
+
+ if ( pItemDef->IsBundle() )
+ {
+ uint32 unNumConcreteItems = 0;
+
+ const bundleinfo_t *pBundle = pItemDef->GetBundleInfo();
+ Assert( pBundle );
+
+ FOR_EACH_VEC( pBundle->vecItemDefs, i )
+ {
+ unNumConcreteItems += CalculateNumberOfConcreteItems( pBundle->vecItemDefs[i] );
+ }
+
+ return unNumConcreteItems;
+ }
+
+ return 1;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CEconItemSchema::BInitSteamPackageLocalizationToken( KeyValues *pKVSteamPackages, CUtlVector<CUtlString> *pVecErrors )
+{
+ if ( NULL != pKVSteamPackages )
+ {
+ FOR_EACH_TRUE_SUBKEY( pKVSteamPackages, pKVEntry )
+ {
+ // Check to make sure the index is positive
+ int iRawPackageId = atoi( pKVEntry->GetName() );
+ SCHEMA_INIT_CHECK(
+ iRawPackageId > 0,
+ "Invalid package ID %i for localization", iRawPackageId );
+
+ // Store off our data.
+ uint32 unPackageId = (uint32)iRawPackageId;
+ const char *pszLocalizationToken = pKVEntry->GetString( "localization_key" );
+
+ m_mapSteamPackageLocalizationTokens.InsertOrReplace( unPackageId, pszLocalizationToken );
+
+ }
+ }
+ return SCHEMA_INIT_SUCCESS();
+}
+#endif // TF_CLIENT_DLL
+
+static const char *s_particle_controlpoint_names[] =
+{
+ "attachment",
+ "control_point_1",
+ "control_point_2",
+ "control_point_3",
+ "control_point_4",
+ "control_point_5",
+ "control_point_6",
+};
+
+//-----------------------------------------------------------------------------
+// Purpose: Initializes the attribute-controlled-particle-systems section of the schema
+//-----------------------------------------------------------------------------
+bool CEconItemSchema::BInitAttributeControlledParticleSystems( KeyValues *pKVParticleSystems, CUtlVector<CUtlString> *pVecErrors )
+{
+ m_mapAttributeControlledParticleSystems.RemoveAll();
+ m_vecAttributeControlledParticleSystemsCosmetics.RemoveAll();
+ m_vecAttributeControlledParticleSystemsWeapons.RemoveAll();
+ m_vecAttributeControlledParticleSystemsTaunts.RemoveAll();
+
+ CUtlVector< int > *pVec = NULL;
+
+ // Addictional groups we are tracking for.
+ // "cosmetic_unusual_effects"
+ // "weapon_unusual_effects"
+ // "taunt_unusual_effects"
+
+ if ( NULL != pKVParticleSystems )
+ {
+ FOR_EACH_TRUE_SUBKEY( pKVParticleSystems, pKVCategory )
+ {
+ // There is 3 Categories we want to track with additional info
+ if ( !V_strcmp( pKVCategory->GetName(), "cosmetic_unusual_effects" ) )
+ {
+ pVec = &m_vecAttributeControlledParticleSystemsCosmetics;
+ }
+ else if ( !V_strcmp( pKVCategory->GetName(), "weapon_unusual_effects" ) )
+ {
+ pVec = &m_vecAttributeControlledParticleSystemsWeapons;
+ }
+ else if ( !V_strcmp( pKVCategory->GetName(), "taunt_unusual_effects" ) )
+ {
+ pVec = &m_vecAttributeControlledParticleSystemsTaunts;
+ }
+ else
+ {
+ pVec = NULL; // reset
+ }
+
+ FOR_EACH_TRUE_SUBKEY( pKVCategory, pKVEntry )
+ {
+ int32 nItemIndex = atoi( pKVEntry->GetName() );
+ // Check to make sure the index is positive
+ SCHEMA_INIT_CHECK(
+ nItemIndex > 0,
+ "Particle system index %d greater than zero", nItemIndex );
+ if ( nItemIndex <= 0 )
+ continue;
+ int iIndex = m_mapAttributeControlledParticleSystems.Insert( nItemIndex );
+ attachedparticlesystem_t &system = m_mapAttributeControlledParticleSystems[iIndex];
+ system.pszSystemName = pKVEntry->GetString( "system", NULL );
+ system.bFollowRootBone = pKVEntry->GetInt( "attach_to_rootbone", 0 ) != 0;
+ system.iCustomType = 0;
+ system.nSystemID = nItemIndex;
+ system.fRefireTime = pKVEntry->GetFloat( "refire_time", 0.0f );
+ system.bDrawInViewModel = pKVEntry->GetBool( "draw_in_viewmodel", false );
+ system.bUseSuffixName = pKVEntry->GetBool( "use_suffix_name", false );
+ system.bHasViewModelSpecificEffect = pKVEntry->GetBool( "has_viewmodel_specific_effect", false );
+
+ COMPILE_TIME_ASSERT( ARRAYSIZE( system.pszControlPoints ) == ARRAYSIZE( s_particle_controlpoint_names ) );
+ for ( int i=0; i<ARRAYSIZE( system.pszControlPoints ); ++i )
+ {
+ system.pszControlPoints[i] = pKVEntry->GetString( s_particle_controlpoint_names[i], NULL );
+ }
+
+ if ( pVec )
+ {
+ pVec->AddToTail( nItemIndex );
+ }
+ }
+ }
+ }
+ return SCHEMA_INIT_SUCCESS();
+}
+
+#ifdef CLIENT_DLL
+locchar_t *CEconItemSchema::GetParticleSystemLocalizedName( int index ) const
+{
+ const attachedparticlesystem_t *pSystem = GetItemSchema()->GetAttributeControlledParticleSystem( index );
+ if ( !pSystem )
+ return NULL;
+
+ char particleNameEntry[128];
+ Q_snprintf( particleNameEntry, ARRAYSIZE( particleNameEntry ), "#Attrib_Particle%d", pSystem->nSystemID );
+
+ return g_pVGuiLocalize->Find( particleNameEntry );
+}
+
+#endif
+//-----------------------------------------------------------------------------
+// Purpose: Inits data for items that can level up through kills, etc.
+//-----------------------------------------------------------------------------
+bool CEconItemSchema::BInitItemLevels( KeyValues *pKVItemLevels, CUtlVector<CUtlString> *pVecErrors )
+{
+ m_vecItemLevelingData.RemoveAll();
+
+ // initialize the rewards sections
+ if ( NULL != pKVItemLevels )
+ {
+ FOR_EACH_TRUE_SUBKEY( pKVItemLevels, pKVItemLevelBlock )
+ {
+ const char *pszLevelBlockName = pKVItemLevelBlock->GetName();
+ SCHEMA_INIT_CHECK( GetItemLevelingData( pszLevelBlockName ) == NULL,
+ "Duplicate leveling data block named \"%s\".", pszLevelBlockName );
+
+ // Allocate a new structure for this block and assign it. We'll fill in the contents later.
+ CUtlVector<CItemLevelingDefinition> *pLevelingData = new CUtlVector<CItemLevelingDefinition>;
+ m_vecItemLevelingData.Insert( pszLevelBlockName, pLevelingData );
+
+ FOR_EACH_TRUE_SUBKEY( pKVItemLevelBlock, pKVItemLevel )
+ {
+ int index = pLevelingData->AddToTail();
+ SCHEMA_INIT_SUBSTEP( (*pLevelingData)[index].BInitFromKV( pKVItemLevel, pszLevelBlockName, pVecErrors ) );
+ }
+ }
+ }
+
+ return SCHEMA_INIT_SUCCESS();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Inits data for kill eater types.
+//-----------------------------------------------------------------------------
+bool CEconItemSchema::BInitKillEaterScoreTypes( KeyValues *pKVKillEaterScoreTypes, CUtlVector<CUtlString> *pVecErrors )
+{
+ m_mapKillEaterScoreTypes.RemoveAll();
+
+ // initialize the rewards sections
+ if ( NULL != pKVKillEaterScoreTypes )
+ {
+ FOR_EACH_TRUE_SUBKEY( pKVKillEaterScoreTypes, pKVScoreType )
+ {
+ unsigned int unIndex = (unsigned int)atoi( pKVScoreType->GetName() );
+ SCHEMA_INIT_CHECK( m_mapKillEaterScoreTypes.Find( unIndex ) == KillEaterScoreMap_t::InvalidIndex(),
+ "Duplicate kill eater score type index %u.", unIndex );
+
+ kill_eater_score_type_t ScoreType;
+ ScoreType.m_pszTypeString = pKVScoreType->GetString( "type_name" );
+ ScoreType.m_bAllowBotVictims = pKVScoreType->GetBool( "allow_bot_victims", false );
+#ifdef GC_DLL
+ ScoreType.m_bGCUpdateOnly = pKVScoreType->GetBool( "gc_update_only", false );
+ ScoreType.m_AllowIncrementValues = pKVScoreType->GetBool( "gc_allow_increment_values", false );
+ ScoreType.m_bIsBaseKillType = pKVScoreType->GetBool( "gc_is_base_kill_type", false );
+#endif
+
+ const char *pszLevelBlockName = pKVScoreType->GetString( "level_data", "KillEaterRank" );
+ SCHEMA_INIT_CHECK( GetItemLevelingData( pszLevelBlockName ) != NULL,
+ "Unable to find leveling data block named \"%s\" for kill eater score type %u.", pszLevelBlockName, unIndex );
+
+ ScoreType.m_pszLevelBlockName = pszLevelBlockName;
+
+ m_mapKillEaterScoreTypes.Insert( unIndex, ScoreType );
+ }
+ }
+
+ return SCHEMA_INIT_SUCCESS();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CEconItemSchema::BInitStringTables( KeyValues *pKVStringTables, CUtlVector<CUtlString> *pVecErrors )
+{
+ m_dictStringTable.PurgeAndDeleteElements();
+
+ // initialize the rewards sections
+ if ( NULL != pKVStringTables )
+ {
+ FOR_EACH_SUBKEY( pKVStringTables, pKVTable )
+ {
+ SCHEMA_INIT_CHECK( !m_dictStringTable.IsValidIndex( m_dictStringTable.Find( pKVTable->GetName() ) ),
+ "Duplicate string table name '%s'.", pKVTable->GetName() );
+
+ SchemaStringTableDict_t::IndexType_t i = m_dictStringTable.Insert( pKVTable->GetName(), new CUtlVector< schema_string_table_entry_t > );
+ FOR_EACH_SUBKEY( pKVTable, pKVEntry )
+ {
+ schema_string_table_entry_t s = { atoi( pKVEntry->GetName() ), pKVEntry->GetString() };
+ m_dictStringTable[i]->AddToTail( s );
+ }
+ }
+ }
+
+ return SCHEMA_INIT_SUCCESS();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CEconItemSchema::BInitCommunityMarketRemaps( KeyValues *pKVCommunityMarketRemaps, CUtlVector<CUtlString> *pVecErrors )
+{
+ m_mapCommunityMarketDefinitionIndexRemap.Purge();
+
+ if ( NULL != pKVCommunityMarketRemaps )
+ {
+ FOR_EACH_SUBKEY( pKVCommunityMarketRemaps, pKVRemapBase )
+ {
+ const char *pszBaseDefName = pKVRemapBase->GetName();
+ const CEconItemDefinition *pBaseItemDef = GetItemSchema()->GetItemDefinitionByName( pszBaseDefName );
+ SCHEMA_INIT_CHECK( pBaseItemDef != NULL, "Unknown Market remap base definition '%s'.", pszBaseDefName );
+
+ FOR_EACH_SUBKEY( pKVRemapBase, pKVRemap )
+ {
+ const char *pszDefName = pKVRemap->GetName();
+ const CEconItemDefinition *pItemDef = GetItemSchema()->GetItemDefinitionByName( pszDefName );
+ SCHEMA_INIT_CHECK( pItemDef != NULL, "Unknown Market remap definition '%s' (under '%s').", pszDefName, pszBaseDefName );
+ SCHEMA_INIT_CHECK( m_mapCommunityMarketDefinitionIndexRemap.Find( pItemDef->GetDefinitionIndex() ) == m_mapCommunityMarketDefinitionIndexRemap.InvalidIndex(), "Duplicate Market remap definition '%s'.\n", pszDefName );
+
+ m_mapCommunityMarketDefinitionIndexRemap.Insert( pItemDef->GetDefinitionIndex(), pBaseItemDef->GetDefinitionIndex() );
+ }
+ }
+ }
+
+ return SCHEMA_INIT_SUCCESS();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+item_definition_index_t CEconItemSchema::GetCommunityMarketRemappedDefinitionIndex( item_definition_index_t unSearchItemDef ) const
+{
+ CommunityMarketDefinitionRemapMap_t::IndexType_t index = m_mapCommunityMarketDefinitionIndexRemap.Find( unSearchItemDef );
+ if ( index == m_mapCommunityMarketDefinitionIndexRemap.InvalidIndex() )
+ return unSearchItemDef;
+
+ return m_mapCommunityMarketDefinitionIndexRemap[index];
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+const ISchemaAttributeType *CEconItemSchema::GetAttributeType( const char *pszAttrTypeName ) const
+{
+ FOR_EACH_VEC( m_vecAttributeTypes, i )
+ {
+ if ( m_vecAttributeTypes[i].m_sName == pszAttrTypeName )
+ return m_vecAttributeTypes[i].m_pAttrType;
+ }
+
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+// CItemLevelingDefinition Accessor
+//-----------------------------------------------------------------------------
+const CItemLevelingDefinition *CEconItemSchema::GetItemLevelForScore( const char *pszLevelBlockName, uint32 unScore ) const
+{
+ const CUtlVector<CItemLevelingDefinition> *pLevelingData = GetItemLevelingData( pszLevelBlockName );
+ if ( !pLevelingData )
+ return NULL;
+
+ if ( pLevelingData->Count() == 0 )
+ return NULL;
+
+ FOR_EACH_VEC( (*pLevelingData), i )
+ {
+ if ( unScore < (*pLevelingData)[i].GetRequiredScore() )
+ return &(*pLevelingData)[i];
+ }
+
+ return &(*pLevelingData).Tail();
+}
+
+//-----------------------------------------------------------------------------
+// Kill eater score type accessor
+//-----------------------------------------------------------------------------
+const kill_eater_score_type_t *CEconItemSchema::FindKillEaterScoreType( uint32 unScoreType ) const
+{
+ KillEaterScoreMap_t::IndexType_t i = m_mapKillEaterScoreTypes.Find( unScoreType );
+ if ( i == KillEaterScoreMap_t::InvalidIndex() )
+ return NULL;
+
+ return &m_mapKillEaterScoreTypes[i];
+}
+
+//-----------------------------------------------------------------------------
+// Kill eater score type accessor
+//-----------------------------------------------------------------------------
+const char *CEconItemSchema::GetKillEaterScoreTypeLocString( uint32 unScoreType ) const
+{
+ const kill_eater_score_type_t *pScoreType = FindKillEaterScoreType( unScoreType );
+
+ return pScoreType
+ ? pScoreType->m_pszTypeString
+ : NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Kill eater score type accessor
+//-----------------------------------------------------------------------------
+const char *CEconItemSchema::GetKillEaterScoreTypeLevelingDataName( uint32 unScoreType ) const
+{
+ const kill_eater_score_type_t *pScoreType = FindKillEaterScoreType( unScoreType );
+
+ return pScoreType
+ ? pScoreType->m_pszLevelBlockName
+ : NULL;
+}
+
+#if defined(STAGING_ONLY) && ( defined(TF_CLIENT_DLL) || defined(TF_DLL) )
+ ConVar tf_allow_strange_bot_kills( "tf_allow_strange_bot_kills", "0", FCVAR_REPLICATED );
+#endif
+//-----------------------------------------------------------------------------
+// Kill eater score type accessor
+//-----------------------------------------------------------------------------
+bool CEconItemSchema::GetKillEaterScoreTypeAllowsBotVictims( uint32 unScoreType ) const
+{
+#if defined(STAGING_ONLY) && ( defined(TF_CLIENT_DLL) || defined(TF_DLL) )
+ if ( tf_allow_strange_bot_kills.GetBool() )
+ {
+ return true;
+ }
+#endif
+
+ const kill_eater_score_type_t *pScoreType = FindKillEaterScoreType( unScoreType );
+
+ return pScoreType
+ ? pScoreType->m_bAllowBotVictims
+ : false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+econ_tag_handle_t CEconItemSchema::GetHandleForTag( const char *pszTagName )
+{
+ EconTagDict_t::IndexType_t i = m_dictTags.Find( pszTagName );
+ if ( m_dictTags.IsValidIndex( i ) )
+ return i;
+
+ return m_dictTags.Insert( pszTagName );
+}
+
+#ifdef GC_DLL
+//-----------------------------------------------------------------------------
+// Kill eater score type accessor
+//-----------------------------------------------------------------------------
+bool CEconItemSchema::GetKillEaterScoreTypeGCOnlyUpdate( uint32 unScoreType ) const
+{
+ const kill_eater_score_type_t *pScoreType = FindKillEaterScoreType( unScoreType );
+
+ return pScoreType
+ ? pScoreType->m_bGCUpdateOnly
+ : true; // default to being more restrictive
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CEconItemSchema::GetKillEaterScoreTypeAllowsIncrementValues( uint32 unScoreType ) const
+{
+ const kill_eater_score_type_t *pScoreType = FindKillEaterScoreType( unScoreType );
+
+ return pScoreType
+ ? pScoreType->m_AllowIncrementValues
+ : true; // default to being more restrictive
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+const CEconItemSchema::periodic_score_t& CEconItemSchema::GetPeriodicScoreInfo( int iPeriodicScoreIndex ) const
+{
+ Assert( GetPeriodicScoreTypeList().IsValidIndex( iPeriodicScoreIndex ) );
+
+ return GetPeriodicScoreTypeList()[ iPeriodicScoreIndex ];
+}
+#endif
+
+#if defined(CLIENT_DLL) || defined(GAME_DLL)
+//-----------------------------------------------------------------------------
+// Purpose: Clones the specified item definition, and returns the new item def.
+//-----------------------------------------------------------------------------
+void CEconItemSchema::ItemTesting_CreateTestDefinition( int iCloneFromItemDef, int iNewDef, KeyValues *pNewKV )
+{
+ int nMapIndex = m_mapItems.Find( iNewDef );
+ if ( !m_mapItems.IsValidIndex( nMapIndex ) )
+ {
+ nMapIndex = m_mapItems.Insert( iNewDef, CreateEconItemDefinition() );
+ m_mapItemsSorted.Insert( iNewDef, m_mapItems[nMapIndex] );
+ }
+
+ // Find & copy the clone item def's data in
+ CEconItemDefinition *pCloneDef = GetItemDefinition( iCloneFromItemDef );
+ if ( !pCloneDef )
+ return;
+ m_mapItems[nMapIndex]->CopyPolymorphic( pCloneDef );
+
+ // Then stomp it with the KV test contents
+ m_mapItems[nMapIndex]->BInitFromTestItemKVs( iNewDef, pNewKV );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Discards the specified item definition
+//-----------------------------------------------------------------------------
+void CEconItemSchema::ItemTesting_DiscardTestDefinition( int iDef )
+{
+ m_mapItems.Remove( iDef );
+ m_mapItemsSorted.Remove( iDef );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Initializes the armory data section of the schema
+//-----------------------------------------------------------------------------
+bool CEconItemSchema::BInitArmoryData( KeyValues *pKVArmoryData, CUtlVector<CUtlString> *pVecErrors )
+{
+ m_dictArmoryItemDataStrings.RemoveAll();
+ m_dictArmoryAttributeDataStrings.RemoveAll();
+ if ( NULL != pKVArmoryData )
+ {
+ KeyValues *pKVItemTypes = pKVArmoryData->FindKey( "armory_item_types" );
+ if ( pKVItemTypes )
+ {
+ FOR_EACH_SUBKEY( pKVItemTypes, pKVEntry )
+ {
+ const char *pszDataKey = pKVEntry->GetName();
+ const CUtlConstString sLocString( pKVEntry->GetString() );
+ m_dictArmoryItemTypesDataStrings.Insert( pszDataKey, sLocString );
+ }
+ }
+
+ pKVItemTypes = pKVArmoryData->FindKey( "armory_item_classes" );
+ if ( pKVItemTypes )
+ {
+ FOR_EACH_SUBKEY( pKVItemTypes, pKVEntry )
+ {
+ const char *pszDataKey = pKVEntry->GetName();
+ const CUtlConstString sLocString( pKVEntry->GetString() );
+ m_dictArmoryItemClassesDataStrings.Insert( pszDataKey, sLocString );
+ }
+ }
+
+ KeyValues *pKVAttribs = pKVArmoryData->FindKey( "armory_attributes" );
+ if ( pKVAttribs )
+ {
+ FOR_EACH_SUBKEY( pKVAttribs, pKVEntry )
+ {
+ const char *pszDataKey = pKVEntry->GetName();
+ const CUtlConstString sLocString( pKVEntry->GetString() );
+ m_dictArmoryAttributeDataStrings.Insert( pszDataKey, sLocString );
+ }
+ }
+
+ KeyValues *pKVItems = pKVArmoryData->FindKey( "armory_items" );
+ if ( pKVItems )
+ {
+ FOR_EACH_SUBKEY( pKVItems, pKVEntry )
+ {
+ const char *pszDataKey = pKVEntry->GetName();
+ const CUtlConstString sLocString( pKVEntry->GetString() );
+ m_dictArmoryItemDataStrings.Insert( pszDataKey, sLocString );
+ }
+ }
+ }
+ return SCHEMA_INIT_SUCCESS();
+}
+#endif
+
+
+#ifdef GC_DLL
+//-----------------------------------------------------------------------------
+// Purpose: Returns the item awarded for an achievement.
+// Input: pchAchievementName - The achievement that was awarded.
+// Output: The achievement struct for this reward.
+//-----------------------------------------------------------------------------
+const AchievementAward_t * CEconItemSchema::GetAchievementReward( const char *pchAchievementName, AppId_t unAppID ) const
+{
+ int nRewardIndex = m_dictAchievementRewards.Find( ComputeAchievementName( unAppID, pchAchievementName ) );
+
+ if( m_dictAchievementRewards.IsValidIndex( nRewardIndex ) )
+ return m_dictAchievementRewards[ nRewardIndex ];
+ else
+ return NULL;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Returns the achievement award that matches the provided data or NULL
+// if there is no such award.
+// Input: unData - The data field that would be stored in ItemAudit
+//-----------------------------------------------------------------------------
+const AchievementAward_t *CEconItemSchema::GetAchievementRewardByData( uint32 unData ) const
+{
+ uint nIndex = m_mapAchievementRewardsByData.Find( unData );
+ if( m_mapAchievementRewardsByData.IsValidIndex( nIndex ) )
+ {
+ return m_mapAchievementRewardsByData[nIndex];
+ }
+ else
+ {
+ return NULL;
+ }
+}
+
+
+#endif // GC_DLL
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Returns the achievement award that matches the provided defindex or NULL
+// if there is no such award.
+// Input: unData - The data field that would be stored in ItemAudit
+//-----------------------------------------------------------------------------
+const AchievementAward_t *CEconItemSchema::GetAchievementRewardByDefIndex( uint16 usDefIndex ) const
+{
+ FOR_EACH_MAP_FAST( m_mapAchievementRewardsByData, nIndex )
+ {
+ if( m_mapAchievementRewardsByData[nIndex]->m_vecDefIndex.HasElement( usDefIndex ) )
+ return m_mapAchievementRewardsByData[nIndex];
+ }
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Gets a rarity value for a name.
+//-----------------------------------------------------------------------------
+bool CEconItemSchema::BGetItemRarityFromName( const char *pchName, uint8 *nRarity ) const
+{
+ if ( 0 == Q_stricmp( "any", pchName ) )
+ {
+ *nRarity = k_unItemRarity_Any;
+ return true;
+ }
+
+ FOR_EACH_MAP_FAST( m_mapRarities, i )
+ {
+ if ( 0 == Q_stricmp( m_mapRarities[i].GetName(), pchName ) )
+ {
+ *nRarity = m_mapRarities[i].GetDBValue();
+ return true;
+ }
+ }
+
+ return false;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Gets a quality value for a name.
+// Input: pchName - The name to translate.
+// nQuality - (out)The quality number for this name, if found.
+// Output: True if the string matched a quality for this schema, false otherwise.
+//-----------------------------------------------------------------------------
+bool CEconItemSchema::BGetItemQualityFromName( const char *pchName, uint8 *nQuality ) const
+{
+ if ( 0 == Q_stricmp( "any", pchName ) )
+ {
+ *nQuality = k_unItemQuality_Any;
+ return true;
+ }
+
+ FOR_EACH_MAP_FAST( m_mapQualities, i )
+ {
+ if ( 0 == Q_stricmp( m_mapQualities[i].GetName(), pchName ) )
+ {
+ *nQuality = m_mapQualities[i].GetDBValue();
+ return true;
+ }
+ }
+
+ return false;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Gets a quality definition for an index
+// Input: nQuality - The quality to get.
+// Output: A pointer to the desired definition, or NULL if it is not found.
+//-----------------------------------------------------------------------------
+const CEconItemQualityDefinition *CEconItemSchema::GetQualityDefinition( int nQuality ) const
+{
+ int iIndex = m_mapQualities.Find( nQuality );
+ if ( m_mapQualities.IsValidIndex( iIndex ) )
+ return &m_mapQualities[iIndex];
+ return NULL;
+}
+
+const CEconItemQualityDefinition *CEconItemSchema::GetQualityDefinitionByName( const char *pszDefName ) const
+{
+ FOR_EACH_MAP_FAST( m_mapQualities, i )
+ {
+ if ( V_stricmp( pszDefName, m_mapQualities[i].GetName()) == 0 )
+ return &m_mapQualities[i];
+ }
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+// ItemRarity
+//-----------------------------------------------------------------------------
+const CEconItemRarityDefinition *CEconItemSchema::GetRarityDefinitionByMapIndex( int nRarityIndex ) const
+{
+ if ( m_mapRarities.IsValidIndex( nRarityIndex ) )
+ return &m_mapRarities[nRarityIndex];
+
+ return NULL;
+}
+//-----------------------------------------------------------------------------
+const CEconItemRarityDefinition *CEconItemSchema::GetRarityDefinition( int nRarity ) const
+{
+ int iIndex = m_mapRarities.Find( nRarity );
+ if ( m_mapRarities.IsValidIndex( iIndex ) )
+ return &m_mapRarities[iIndex];
+ return NULL;
+}
+//-----------------------------------------------------------------------------
+const CEconItemRarityDefinition *CEconItemSchema::GetRarityDefinitionByName( const char *pszDefName ) const
+{
+ FOR_EACH_MAP_FAST( m_mapRarities, i )
+ {
+ if ( !strcmp( pszDefName, m_mapRarities[i].GetName() ) )
+ return &m_mapRarities[i];
+ }
+ return NULL;
+}
+//-----------------------------------------------------------------------------
+const char* CEconItemSchema::GetRarityName( uint8 iRarity )
+{
+ const CEconItemRarityDefinition* pItemRarity = GetRarityDefinition( iRarity );
+ if ( !pItemRarity )
+ return NULL;
+ else
+ return pItemRarity->GetName();
+}
+//-----------------------------------------------------------------------------
+const char* CEconItemSchema::GetRarityLocKey( uint8 iRarity )
+{
+ const CEconItemRarityDefinition* pItemRarity = GetRarityDefinition( iRarity );
+ if ( !pItemRarity )
+ return NULL;
+ else
+ return pItemRarity->GetLocKey();
+}
+//-----------------------------------------------------------------------------
+const char* CEconItemSchema::GetRarityColor( uint8 iRarity )
+{
+ const CEconItemRarityDefinition* pItemRarity = GetRarityDefinition( iRarity );
+ if ( !pItemRarity )
+ return NULL;
+ else
+ return GetColorNameForAttribColor( pItemRarity->GetAttribColor() );
+}
+//-----------------------------------------------------------------------------
+int CEconItemSchema::GetRarityIndex( const char* pszRarity )
+{
+ const CEconItemRarityDefinition* pRarity = GetRarityDefinitionByName( pszRarity );
+ if ( pRarity )
+ return pRarity->GetDBValue();
+ else
+ return 0;
+}
+
+//-----------------------------------------------------------------------------
+bool CEconItemSchema::BGetItemSeries( const char* pchName, uint8 *nItemSeries ) const
+{
+ FOR_EACH_MAP_FAST( m_mapItemSeries, i )
+ {
+ if ( 0 == Q_stricmp( m_mapItemSeries[i].GetName(), pchName ) )
+ {
+ *nItemSeries = m_mapItemSeries[i].GetDBValue();
+ return true;
+ }
+ }
+
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+const CEconItemSeriesDefinition *CEconItemSchema::GetItemSeriesDefinition( int iSeries ) const
+{
+ int iIndex = m_mapItemSeries.Find( iSeries );
+ if ( m_mapItemSeries.IsValidIndex( iIndex ) )
+ return &m_mapItemSeries[iIndex];
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Gets an item definition for the specified definition index
+// Input: iItemIndex - The index of the desired definition.
+// Output: A pointer to the desired definition, or NULL if it is not found.
+//-----------------------------------------------------------------------------
+CEconItemDefinition *CEconItemSchema::GetItemDefinition( int iItemIndex )
+{
+#if defined(CLIENT_DLL) || defined(GAME_DLL)
+#if !defined(CSTRIKE_DLL)
+ AssertMsg( GetDefaultItemDefinition(), "No default item definition set up for item schema." );
+#endif // CSTRIKE_DLL
+#endif // defined(CLIENT_DLL) || defined(GAME_DLL)
+
+ int iIndex = m_mapItems.Find( iItemIndex );
+ if ( m_mapItems.IsValidIndex( iIndex ) )
+ return m_mapItems[iIndex];
+
+#if defined( GC_DLL ) || defined( EXTERNALTESTS_DLL )
+ return NULL;
+#else // !GC_DLL
+ if ( GetDefaultItemDefinition() )
+ return GetDefaultItemDefinition();
+
+#if !defined(CSTRIKE_DLL)
+ // We shouldn't ever get down here, but all the same returning a valid pointer is very slightly
+ // a better plan than returning an invalid pointer to code that won't check to see if it's valid.
+ static CEconItemDefinition *s_pEmptyDefinition = CreateEconItemDefinition();
+ return s_pEmptyDefinition;
+#else
+ return NULL;
+#endif // CSTRIKE_DLL
+
+#endif // GC_DLL
+}
+const CEconItemDefinition *CEconItemSchema::GetItemDefinition( int iItemIndex ) const
+{
+ return const_cast<CEconItemSchema *>(this)->GetItemDefinition( iItemIndex );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Gets an item definition that has a name matching the specified name.
+// Input: pszDefName - The name of the desired definition.
+// Output: A pointer to the desired definition, or NULL if it is not found.
+//-----------------------------------------------------------------------------
+CEconItemDefinition *CEconItemSchema::GetItemDefinitionByName( const char *pszDefName )
+{
+ // This shouldn't happen, but let's not crash if it ever does.
+ Assert( pszDefName != NULL );
+ if ( pszDefName == NULL )
+ return NULL;
+
+ FOR_EACH_MAP_FAST( m_mapItems, i )
+ {
+ if ( V_stricmp( pszDefName, m_mapItems[i]->GetDefinitionName()) == 0 )
+ return m_mapItems[i];
+ }
+ return NULL;
+}
+
+const CEconItemDefinition *CEconItemSchema::GetItemDefinitionByName( const char *pszDefName ) const
+{
+ return const_cast<CEconItemSchema *>(this)->GetItemDefinitionByName( pszDefName );
+}
+
+
+#ifdef GC_DLL
+random_attrib_t *CEconItemSchema::GetRandomAttributeTemplateByName( const char *pszAttrTemplateName ) const
+{
+ int index = m_dictRandomAttributeTemplates.Find( pszAttrTemplateName );
+ if ( index != m_dictRandomAttributeTemplates.InvalidIndex() )
+ {
+ return m_dictRandomAttributeTemplates[index];
+ }
+
+ return NULL;
+}
+#endif // GC_DLL
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Gets an attribute definition for an index
+// Input: iAttribIndex - The index of the desired definition.
+// Output: A pointer to the desired definition, or NULL if it is not found.
+//-----------------------------------------------------------------------------
+CEconItemAttributeDefinition *CEconItemSchema::GetAttributeDefinition( int iAttribIndex )
+{
+ int iIndex = m_mapAttributes.Find( iAttribIndex );
+ if ( m_mapAttributes.IsValidIndex( iIndex ) )
+ return &m_mapAttributes[iIndex];
+ return NULL;
+}
+const CEconItemAttributeDefinition *CEconItemSchema::GetAttributeDefinition( int iAttribIndex ) const
+{
+ return const_cast<CEconItemSchema *>(this)->GetAttributeDefinition( iAttribIndex );
+}
+
+CEconItemAttributeDefinition *CEconItemSchema::GetAttributeDefinitionByName( const char *pszDefName )
+{
+ Assert( pszDefName );
+ if ( !pszDefName )
+ return NULL;
+
+ VPROF_BUDGET( "CEconItemSchema::GetAttributeDefinitionByName", VPROF_BUDGETGROUP_STEAM );
+ FOR_EACH_MAP_FAST( m_mapAttributes, i )
+ {
+ Assert( m_mapAttributes[i].GetDefinitionName() );
+ if ( !m_mapAttributes[i].GetDefinitionName() )
+ continue;
+
+ if ( V_stricmp( pszDefName, m_mapAttributes[i].GetDefinitionName() ) == 0 )
+ return &m_mapAttributes[i];
+ }
+ return NULL;
+}
+const CEconItemAttributeDefinition *CEconItemSchema::GetAttributeDefinitionByName( const char *pszDefName ) const
+{
+ return const_cast<CEconItemSchema *>(this)->GetAttributeDefinitionByName( pszDefName );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Gets a recipe definition for an index
+// Input: iRecipeIndex - The index of the desired definition.
+// Output: A pointer to the desired definition, or NULL if it is not found.
+//-----------------------------------------------------------------------------
+CEconCraftingRecipeDefinition *CEconItemSchema::GetRecipeDefinition( int iRecipeIndex )
+{
+ int iIndex = m_mapRecipes.Find( iRecipeIndex );
+ if ( m_mapRecipes.IsValidIndex( iIndex ) )
+ return m_mapRecipes[iIndex];
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CEconColorDefinition *CEconItemSchema::GetColorDefinitionByName( const char *pszDefName )
+{
+ FOR_EACH_VEC( m_vecColorDefs, i )
+ {
+ if ( !Q_stricmp( m_vecColorDefs[i]->GetName(), pszDefName ) )
+ return m_vecColorDefs[i];
+ }
+ return NULL;
+}
+const CEconColorDefinition *CEconItemSchema::GetColorDefinitionByName( const char *pszDefName ) const
+{
+ return const_cast<CEconItemSchema *>(this)->GetColorDefinitionByName( pszDefName );
+}
+
+
+#ifdef CLIENT_DLL
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+const char *CEconItemSchema::GetSteamPackageLocalizationToken( uint32 unPackageId ) const
+{
+ SteamPackageLocalizationTokenMap_t::IndexType_t i = m_mapSteamPackageLocalizationTokens.Find( unPackageId );
+ if ( m_mapSteamPackageLocalizationTokens.IsValidIndex( i ) )
+ return m_mapSteamPackageLocalizationTokens[i];
+ return NULL;
+}
+#endif // CLIENT_DLL
+
+//-----------------------------------------------------------------------------
+// Purpose: Return the attribute specified attachedparticlesystem_t* associated with the given id.
+//-----------------------------------------------------------------------------
+attachedparticlesystem_t* CEconItemSchema::GetAttributeControlledParticleSystem( int id )
+{
+ int iIndex = m_mapAttributeControlledParticleSystems.Find( id );
+ if ( m_mapAttributeControlledParticleSystems.IsValidIndex( iIndex ) )
+ return &m_mapAttributeControlledParticleSystems[iIndex];
+ return NULL;
+}
+
+attachedparticlesystem_t* CEconItemSchema::FindAttributeControlledParticleSystem( const char *pchSystemName )
+{
+ FOR_EACH_MAP_FAST( m_mapAttributeControlledParticleSystems, nSystem )
+ {
+ if( !Q_stricmp( m_mapAttributeControlledParticleSystems[nSystem].pszSystemName, pchSystemName ) )
+ return &m_mapAttributeControlledParticleSystems[nSystem];
+ }
+ return NULL;
+}
+
+
+#if defined(CLIENT_DLL) || defined(GAME_DLL)
+bool CEconItemSchema::SetupPreviewItemDefinition( KeyValues *pKV )
+{
+ int nMapIndex = m_mapItems.Find( PREVIEW_ITEM_DEFINITION_INDEX );
+ if ( !m_mapItems.IsValidIndex( nMapIndex ) )
+ {
+ nMapIndex = m_mapItems.Insert( PREVIEW_ITEM_DEFINITION_INDEX, CreateEconItemDefinition() );
+ }
+
+ CEconItemDefinition *pItemDef = m_mapItems[ nMapIndex ];
+ return pItemDef->BInitFromKV( pKV );
+}
+#endif // defined(CLIENT_DLL) || defined(GAME_DLL)
+
+#ifdef GC_DLL
+//-----------------------------------------------------------------------------
+// Purpose: Returns all the foreign item imports for an app ID
+//-----------------------------------------------------------------------------
+const CEconItemDefinition *CEconItemSchema::GetAppItemImport( AppId_t unAppID, uint16 usDefIndex ) const
+{
+ int i = m_mapForeignImports.Find( unAppID );
+ if( m_mapForeignImports.IsValidIndex( i ) )
+ return m_mapForeignImports[i]->FindMapping( usDefIndex );
+ else
+ return NULL;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Returns all the foreign item imports for an app ID
+//-----------------------------------------------------------------------------
+bool CEconItemSchema::BInitForeignImports( CUtlVector<CUtlString> *pVecErrors )
+{
+ FOR_EACH_MAP_FAST( m_mapItems, nItem )
+ {
+ CEconItemDefinition *pDefn = m_mapItems[nItem];
+
+ KeyValues *pkvImport = pDefn->GetDefinitionKey( "import_from" );
+ if( !pkvImport )
+ continue;
+
+ FOR_EACH_VALUE( pkvImport, pkvApp )
+ {
+ CForeignAppImports *pAppImports = FindOrAddAppImports( Q_atoi( pkvApp->GetName() ) );
+ pAppImports->AddMapping( pkvApp->GetInt(), pDefn );
+ }
+ }
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Returns all the foreign item imports for an app ID
+//-----------------------------------------------------------------------------
+CForeignAppImports *CEconItemSchema::FindOrAddAppImports( AppId_t unAppID )
+{
+ int i = m_mapForeignImports.Find( unAppID );
+ if( m_mapForeignImports.IsValidIndex( i ) )
+ return m_mapForeignImports[i];
+ else
+ {
+ m_vecForeignApps.AddToTail( unAppID );
+ CForeignAppImports *pApp = new CForeignAppImports();
+ m_mapForeignImports.Insert( unAppID, pApp );
+ return pApp;
+ }
+}
+#endif // GC_DLL
+
+bool CEconItemSchema::BCanStrangeFilterApplyToStrangeSlotInItem( uint32 /*strange_event_restriction_t*/ unRestrictionType, uint32 unRestrictionValue, const IEconItemInterface *pItem, int iStrangeSlot, uint32 *out_pOptionalScoreType ) const
+{
+ Assert( unRestrictionType != kStrangeEventRestriction_None );
+
+ // Do we have a type for this slot? If not, move on, with the exception: all user-custom scores
+ // we expect to have types, but certain weapons may not specify a base type and just use
+ // kills implicitly.
+ uint32 unStrangeScoreTypeBits = kKillEaterEvent_PlayerKill;
+ if ( !pItem->FindAttribute( GetKillEaterAttr_Type( iStrangeSlot ), &unStrangeScoreTypeBits ) && iStrangeSlot != 0 )
+ return false;
+
+ // Do we have a restriction already in this slot? If so, move on.
+ if ( pItem->FindAttribute( GetKillEaterAttr_Restriction( iStrangeSlot ) ) )
+ return false;
+
+ // We've found an open slot. Make sure that adding our restriction to this slot
+ // won't result in a duplicate score-type/restriction-type entry.
+ for ( int j = 0; j < GetKillEaterAttrCount(); j++ )
+ {
+ // Don't compare against ourself.
+ if ( iStrangeSlot == j )
+ continue;
+
+ // Ignore this entry if we don't have a score type or if the score type differs from
+ // our search criteria above.
+ uint32 unAltStrangeScoreType;
+ if ( !pItem->FindAttribute( GetKillEaterAttr_Type( j ), &unAltStrangeScoreType ) ||
+ unAltStrangeScoreType != unStrangeScoreTypeBits )
+ {
+ continue;
+ }
+
+ // This entry does have the same type, so tag us as a duplicate if we also have the same
+ // restriction that we're trying to apply.
+ uint32 unAltRestrictionType;
+ uint32 unAltRestrictionValue;
+ if ( pItem->FindAttribute( GetKillEaterAttr_Restriction( j ), &unAltRestrictionType ) &&
+ unAltRestrictionType == unRestrictionType &&
+ pItem->FindAttribute( GetKillEaterAttr_RestrictionValue( j ), &unAltRestrictionValue ) &&
+ unAltRestrictionValue == unRestrictionValue )
+ {
+ return false;
+ }
+ }
+
+ if ( out_pOptionalScoreType )
+ {
+ *out_pOptionalScoreType = *(float *)&unStrangeScoreTypeBits;
+ }
+
+ // Everything seems alright.
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Ensure that all of our internal structures are consistent, and
+// account for all memory that we've allocated.
+// Input: validator - Our global validator object
+// pchName - Our name (typically a member var in our container)
+//-----------------------------------------------------------------------------
+#ifdef DBGFLAG_VALIDATE
+void CEconItemSchema::Validate( CValidator &validator, const char *pchName )
+{
+ VALIDATE_SCOPE();
+ ValidateObj( m_mapQualities );
+
+ FOR_EACH_MAP_FAST( m_mapQualities, i )
+ {
+ ValidateObj( m_mapQualities[i] );
+ }
+
+ ValidateObj( m_mapItems );
+
+ FOR_EACH_MAP_FAST( m_mapItems, i )
+ {
+ ValidateObj( m_mapItems[i] );
+ }
+
+ ValidateObj( m_mapUpgradeableBaseItems );
+
+ FOR_EACH_MAP_FAST( m_mapUpgradeableBaseItems, i )
+ {
+ ValidateObj( m_mapUpgradeableBaseItems[i] );
+ }
+
+ ValidateObj( m_mapAttributes );
+
+ FOR_EACH_MAP_FAST( m_mapAttributes, i )
+ {
+ ValidateObj( m_mapAttributes[i] );
+ }
+
+ ValidateObj( m_mapRecipes );
+
+ FOR_EACH_MAP_FAST( m_mapRecipes, i )
+ {
+ ValidateObj( m_mapRecipes[i] );
+ }
+
+ FOR_EACH_VEC( m_vecTimedRewards, i )
+ {
+ ValidateObj( m_vecTimedRewards[i] );
+ }
+ ValidateObj( m_vecTimedRewards );
+
+}
+#endif // DBGFLAG_VALIDATE
+
+#ifdef GC_DLL
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CEconItemSchema::BInitExperiements( KeyValues *pKVExperiments, CUtlVector<CUtlString> *pVecErrors )
+{
+ m_vecExperiments.RemoveAll();
+
+ if ( NULL != pKVExperiments )
+ {
+ FOR_EACH_TRUE_SUBKEY( pKVExperiments, pKVEntry )
+ {
+ const char *listName = pKVEntry->GetName();
+
+ SCHEMA_INIT_CHECK( listName != NULL, "All experiments must have titles.");
+
+ int idx = m_vecExperiments.AddToTail();
+ SCHEMA_INIT_SUBSTEP( m_vecExperiments[idx].BInitFromKV( pKVEntry, pVecErrors ) );
+ }
+ }
+
+ return SCHEMA_INIT_SUCCESS();
+}
+
+//-----------------------------------------------------------------------------
+// CExperimentDefinition
+//-----------------------------------------------------------------------------
+CExperimentDefinition::CExperimentDefinition( void )
+ : m_bEnabled( false )
+ , m_unExperimentID( 0 )
+ , m_unNumParticipants( 0 )
+ , m_unMaxParticipants( 0 )
+ , m_pKeyValues( NULL )
+{
+}
+
+CExperimentDefinition::~CExperimentDefinition( void )
+{
+ if ( m_pKeyValues )
+ {
+ m_pKeyValues->deleteThis();
+ }
+}
+
+bool CExperimentDefinition::BInitFromKV( KeyValues *pKVExperiment, CUtlVector<CUtlString> *pVecErrors )
+{
+ m_pKeyValues = pKVExperiment->MakeCopy();
+
+ m_unExperimentID = Q_atoi( m_pKeyValues->GetName() );
+ m_bEnabled = m_pKeyValues->GetBool( "enabled" );
+ m_unNumParticipants = 0;
+ m_unMaxParticipants = 0;
+
+ KeyValues *pKVGroups = m_pKeyValues->FindKey( "groups" );
+ if ( pKVGroups )
+ {
+ FOR_EACH_TRUE_SUBKEY( pKVGroups, pKVEntry )
+ {
+ int idx = m_vecGroups.AddToTail();
+ experiment_group_t &group = m_vecGroups[idx];
+ group.m_pKeyValues = pKVEntry;
+ group.m_pName = pKVEntry->GetName();
+ group.m_unNumParticipants = 0;
+ group.m_unMaxParticipants = pKVEntry->GetInt( "num_participants", 0 );
+ m_unMaxParticipants += group.m_unMaxParticipants;
+ }
+ }
+
+ return SCHEMA_INIT_SUCCESS();
+}
+
+bool CExperimentDefinition::ChooseGroup( uint32 &unGroup )
+{
+ CUtlVector< uint32 > vecGroupIndices;
+ FOR_EACH_VEC( m_vecGroups, i )
+ {
+ experiment_group_t &group = m_vecGroups[i];
+ if ( group.m_unNumParticipants < group.m_unMaxParticipants )
+ {
+ vecGroupIndices.AddToTail( i );
+ }
+ }
+ if ( vecGroupIndices.Count() == 0 )
+ {
+ return false;
+ }
+
+ uint32 idx = vecGroupIndices[ RandomInt( 0, vecGroupIndices.Count() - 1 ) ];
+ experiment_group_t &group = m_vecGroups[ idx ];
+ ++group.m_unNumParticipants;
+ unGroup = idx;
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Give the chance after the schema has been initialized to sanity-check
+// individual parts or interactions before moving forward.
+//-----------------------------------------------------------------------------
+#ifdef TF_GC_DLL
+static bool BTestToolApplicability( CUtlVector<CUtlString> *pVecErrors )
+{
+ static CSchemaItemDefHandle pItem_TeamPaint ( "Paint Can Team Color" ), // tools
+ pItem_Paint ( "Paint Can 14" ),
+ pItem_DescriptionTag ( "Description Tag" ),
+ pItem_NameTag ( "Name Tag" ),
+ pItem_Key ( "Decoder Ring" ),
+ pItem_SummerKey ( "Summer Key" ),
+ pItem_GiftWrap ( "Gift Wrap" ),
+ pItem_CustomTextureTool ( "Customize Texture Tool" ),
+ pItem_SupplyCrateGeneric ( "Supply Crate 2" ), // tool targets
+ pItem_SummerCrate ( "Summer Crate" ),
+ pItem_PaintableItem ( "Summer Hat" ),
+ //pItem_TeamPaintableItem ( "" ),
+ pItem_UnpaintableItem ( "Big Steel Jaw of Summer Fun" ),
+ //pItem_UnnameableWeapon ( "" ),
+ pItem_NameableWeapon ( "The Axtinguisher" ),
+ pItem_GiftWrappableItem ( "Supply Crate 3" ),
+ pItem_NonGiftWrappableItem ( "Wrapped Gift" ),
+ pItem_StampableObject ( "The Conscientious Objector" ),
+ pItem_NonstampableObject ( "Spiral Sallet" );
+
+ struct ToolValidityTest_t
+ {
+ const CEconItemDefinition *m_pTool;
+ const CEconItemDefinition *m_pTarget;
+ bool m_bExpectedValidity;
+ };
+
+ ToolValidityTest_t definitionTests[] =
+ {
+ { pItem_TeamPaint, pItem_TeamPaint, false },
+ { pItem_TeamPaint, pItem_PaintableItem, true },
+ //{ pItem_TeamPaint, pItem_TeamPaintableItem, true },
+ { pItem_TeamPaint, pItem_UnpaintableItem, false },
+ { pItem_Paint, pItem_PaintableItem, true },
+ //{ pItem_Paint, pItem_TeamPaintableItem, false },
+ { pItem_Paint, pItem_UnpaintableItem, false },
+ { pItem_Paint, pItem_SupplyCrateGeneric, false },
+ { pItem_Key, pItem_SupplyCrateGeneric, true },
+ { pItem_Key, pItem_SummerCrate, false },
+ { pItem_SummerKey, pItem_SupplyCrateGeneric, true }, // summer keys in staging are now regular keys and should...
+ { pItem_SummerKey, pItem_SummerCrate, false }, // ...be able to open regular crates
+ { pItem_NameTag, pItem_NameableWeapon, true },
+ //{ pItem_NameTag, pItem_UnnameableWeapon, false },
+ { pItem_DescriptionTag, pItem_NameableWeapon, true },
+ //{ pItem_DescriptionTag, pItem_UnnameableWeapon, false },
+ { pItem_CustomTextureTool, pItem_StampableObject, true },
+ { pItem_CustomTextureTool, pItem_NonstampableObject, false },
+ };
+
+ bool bAllSuccess = true;
+ for ( int i = 0; i < ARRAYSIZE( definitionTests ); i++ )
+ {
+ const GameItemDefinition_t *pToolDef = dynamic_cast<const GameItemDefinition_t *>( definitionTests[i].m_pTool ),
+ *pTargetDef = dynamic_cast<const GameItemDefinition_t *>( definitionTests[i].m_pTarget );
+
+ if ( !pToolDef )
+ {
+ bAllSuccess = false;
+ pVecErrors->AddToTail( CFmtStr( "Tool validity test %i failed: Tool is NULL.", i ).Access() );
+ continue;
+ }
+
+ if ( !pTargetDef )
+ {
+ bAllSuccess = false;
+ pVecErrors->AddToTail( CFmtStr( "Tool validity test %i failed: Target is NULL.", i ).Access() );
+ continue;
+ }
+
+ if ( CEconSharedToolSupport::ToolCanApplyToDefinition( pToolDef, pTargetDef ) != definitionTests[i].m_bExpectedValidity )
+ {
+ bAllSuccess = false;
+ pVecErrors->AddToTail( CFmtStr( "Tool validity test %i failed: %s %s have been able to apply to %s.",
+ i,
+ pToolDef->GetDefinitionName(),
+ definitionTests[i].m_bExpectedValidity ? "should" : "shouldn't",
+ pTargetDef->GetDefinitionName() ).Access() );
+ }
+ }
+
+ // These tests require actual instances of the item--not just the definitions.
+ ToolValidityTest_t interfaceTests[] =
+ {
+ { pItem_GiftWrap, pItem_GiftWrappableItem, true },
+ { pItem_GiftWrap, pItem_NonGiftWrappableItem, false },
+ };
+
+ // Skip if we already have failures.
+ if ( bAllSuccess )
+ {
+ for ( int i = 0; i < ARRAYSIZE( interfaceTests ); i++ )
+ {
+ const GameItemDefinition_t *pToolDef = dynamic_cast< const GameItemDefinition_t * >( interfaceTests[ i ].m_pTool ),
+ *pTargetDef = dynamic_cast< const GameItemDefinition_t * >( interfaceTests[ i ].m_pTarget );
+
+ if ( !pToolDef )
+ {
+ bAllSuccess = false;
+ pVecErrors->AddToTail( CFmtStr( "Tool validity test %i failed: Tool is NULL.", i ).Access() );
+ continue;
+ }
+
+ if ( !pTargetDef )
+ {
+ bAllSuccess = false;
+ pVecErrors->AddToTail( CFmtStr( "Tool validity test %i failed: Target is NULL.", i ).Access() );
+ continue;
+ }
+
+ CEconItem *pTool = GEconManager()->GetItemFactory().CreateSpecificItem( ( const CEconGameAccount *) NULL, pToolDef->GetDefinitionIndex() );
+ CEconItem *pTarget = GEconManager()->GetItemFactory().CreateSpecificItem( ( const CEconGameAccount *) NULL, pTargetDef->GetDefinitionIndex() );
+
+ if ( CEconSharedToolSupport::ToolCanApplyTo( pTool, pTarget ) != interfaceTests[ i ].m_bExpectedValidity )
+ {
+ bAllSuccess = false;
+ pVecErrors->AddToTail( CFmtStr( "Tool validity test %i failed: %s %s have been able to apply to %s.",
+ i,
+ pToolDef->GetDefinitionName(),
+ interfaceTests[ i ].m_bExpectedValidity ? "should" : "shouldn't",
+ pTargetDef->GetDefinitionName() ).Access() );
+ }
+
+ delete pTool;
+ delete pTarget;
+ }
+ }
+
+
+
+
+ return bAllSuccess;
+}
+#endif // TF_GC_DLL
+#endif // defined(GC_DLL)
+
+bool CEconItemSchema::BPostSchemaInit( CUtlVector<CUtlString> *pVecErrors ) const
+{
+ bool bAllSuccess = true;
+
+ // Make sure all of our tools are valid. We have to do this after the whole schema is initialized so
+ // that we don't run into circular reference problems with items referencing loot lists that reference
+ // items, etc.
+ FOR_EACH_MAP_FAST( m_mapItems, i )
+ {
+ const CEconItemDefinition *pItemDef = m_mapItems[i];
+ const IEconTool *pTool = pItemDef->GetEconTool();
+
+ if ( pTool && !const_cast<IEconTool *>( pTool )->BFinishInitialization() )
+ {
+#ifdef GC_DLL
+ bAllSuccess = false;
+ pVecErrors->AddToTail( CFmtStr( "BPostSchemaInit(): tool '%s' is invalid.", pItemDef->GetDefinitionName() ).Get() );
+#endif // GC_DLL
+ }
+#if TF_GC_DLL
+ else
+ {
+ // all cosmetic items should have these two attributes from the
+ // cosmetic_killeater_attribs prefab in case we ever try to drop them as Strange
+ static CSchemaAttributeDefHandle pAttribDef_KillEaterScoreType( "kill eater score type" );
+ static CSchemaAttributeDefHandle pAttribDef_KillEaterKillType( "kill eater kill type" );
+
+ const CTFItemDefinition *pTFItemDef = assert_cast< const CTFItemDefinition* >( pItemDef );
+ if ( pTFItemDef )
+ {
+ int nSlot = pTFItemDef->GetLoadoutSlot( 0 ); // 0 gives use the default slot
+ if ( ( nSlot == LOADOUT_POSITION_HEAD ) || ( nSlot == LOADOUT_POSITION_MISC ) || ( nSlot == LOADOUT_POSITION_MISC2 ) )
+ {
+ bool bFoundScore = false;
+ bool bFoundKill = false;
+
+ FOR_EACH_VEC( pTFItemDef->GetStaticAttributes(), iIndex )
+ {
+ const static_attrib_t& staticAttrib = pTFItemDef->GetStaticAttributes()[iIndex];
+ const CEconItemAttributeDefinition *pAttrDef = GetItemSchema()->GetAttributeDefinition( staticAttrib.iDefIndex );
+ if ( pAttrDef == pAttribDef_KillEaterScoreType )
+ {
+ bFoundScore = true;
+ }
+ else if ( pAttrDef == pAttribDef_KillEaterKillType )
+ {
+ bFoundKill = true;
+ }
+ }
+
+ if ( !bFoundScore || !bFoundKill )
+ {
+ bAllSuccess = false;
+ pVecErrors->AddToTail( CFmtStr( "BPostSchemaInit(): '%s' is missing the standard cosmetic killeater attributes.", pItemDef->GetDefinitionName() ).Get() );
+ }
+ }
+ }
+ }
+#endif // TF_GC_DLL
+ }
+
+#if TF_GC_DLL
+ // Make sure our tool application code validity works correctly.
+ if ( !BTestToolApplicability( pVecErrors ) )
+ {
+ bAllSuccess = false;
+ pVecErrors->AddToTail( "BPostSchemaInit(): error with tool application validity." );
+ }
+
+ const CEconLootListDefinition* pUnusualLootlist = GetItemSchema()->GetLootListByName( "all_particle_hats" );
+ if ( !pUnusualLootlist )
+ {
+ bAllSuccess = false;
+ pVecErrors->AddToTail( "No lootlist \"all_particle_hats\"" );
+ }
+ else
+ {
+ auto& contents = pUnusualLootlist->GetLootListContents();
+ FOR_EACH_VEC( contents, i )
+ {
+ if ( contents[ i ].m_iItemOrLootlistDef > 0 )
+ {
+ const CEconItemDefinition* pItemDef = GetItemDefinition( contents[ i ].m_iItemOrLootlistDef );
+ if ( !( pItemDef->GetEquipRegionMask() & GetItemSchema()->GetEquipRegionBitMaskByName( "hat" ) )
+ && !( pItemDef->GetEquipRegionMask() & GetItemSchema()->GetEquipRegionBitMaskByName( "whole_head" ) ) )
+ {
+ bAllSuccess = false;
+ pVecErrors->AddToTail( CFmtStr( "Item \"%s\" is in all_particle_hats, but doesn't have equip region hat or whole_head, meaning it can't become unusual. REMOVE IT!", pItemDef->GetDefinitionName() ).Get() );
+ }
+ }
+ }
+ }
+#endif // TF_GC_DLL
+
+ return bAllSuccess;
+}
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+CItemLevelingDefinition::CItemLevelingDefinition( void )
+{
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Copy constructor
+//-----------------------------------------------------------------------------
+CItemLevelingDefinition::CItemLevelingDefinition( const CItemLevelingDefinition &that )
+{
+ (*this) = that;
+}
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+CItemLevelingDefinition::~CItemLevelingDefinition()
+{
+ // Free up strdup() memory.
+ free( m_pszLocalizedName_LocalStorage );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Operator=
+//-----------------------------------------------------------------------------
+CItemLevelingDefinition &CItemLevelingDefinition::operator=( const CItemLevelingDefinition &other )
+{
+ m_unLevel = other.m_unLevel;
+ m_unRequiredScore = other.m_unRequiredScore;
+ m_pszLocalizedName_LocalStorage = strdup( other.m_pszLocalizedName_LocalStorage );
+
+ return *this;
+}
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+bool CItemLevelingDefinition::BInitFromKV( KeyValues *pKVItemLevel, const char *pszLevelBlockName, CUtlVector<CUtlString> *pVecErrors )
+{
+ m_unLevel = Q_atoi( pKVItemLevel->GetName() );
+ m_unRequiredScore = pKVItemLevel->GetInt( "score" );
+ m_pszLocalizedName_LocalStorage = strdup( pKVItemLevel->GetString( "rank_name", CFmtStr( "%s%i", pszLevelBlockName, m_unLevel ).Access() ) );
+
+ return SCHEMA_INIT_SUCCESS();
+}
+
+#ifdef GC_DLL
+EUniverse GetUniverse()
+{
+ return GGCHost()->GetUniverse();
+}
+#endif // GC_DLL
diff --git a/game/shared/econ/econ_item_schema.h b/game/shared/econ/econ_item_schema.h
new file mode 100644
index 0000000..283526a
--- /dev/null
+++ b/game/shared/econ/econ_item_schema.h
@@ -0,0 +1,3374 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: EconItemSchema: Defines a schema for econ items
+//
+//=============================================================================
+
+#ifndef ECONITEMSCHEMA_H
+#define ECONITEMSCHEMA_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+// Valve code doesn't play nicely with standard headers on some platforms sometimes.
+#ifdef min
+ #undef min
+#endif
+
+#ifdef max
+ #undef max
+#endif
+
+#include <string>
+
+#include "KeyValues.h"
+#include "tier1/utldict.h"
+#include "tier1/utlhashmaplarge.h"
+#include "econ_item_constants.h"
+
+#include "item_selection_criteria.h"
+#include "bitvec.h"
+#include "language.h"
+#include "smartptr.h"
+#include "rtime.h"
+#include "checksum_sha1.h"
+
+#if defined(CLIENT_DLL) || defined(GAME_DLL)
+#include "engine/ivmodelinfo.h"
+#include "engine/ivmodelrender.h"
+#include "ilocalize.h"
+#endif
+#include "gamestringpool.h"
+
+class CEconItemSchema;
+class CEconItem;
+class CEconSharedObjectCache;
+class CSOItemRecipe;
+
+union attribute_data_union_t
+{
+ float asFloat;
+ uint32 asUint32;
+ byte *asBlobPointer;
+};
+
+struct static_attrib_t
+{
+ static_attrib_t()
+ {
+ iDefIndex = 0;
+ m_value.asBlobPointer = NULL;
+#ifdef GC_DLL
+ bForceGCToGenerate = false;
+ m_pKVCustomData = NULL;
+#endif // GC_DLL
+ }
+
+ ~static_attrib_t()
+ {
+#ifdef GC_DLL
+ if ( m_pKVCustomData )
+ m_pKVCustomData->deleteThis();
+ m_pKVCustomData = NULL;
+#endif
+ }
+
+ static_attrib_t( const static_attrib_t& rhs )
+ {
+ iDefIndex = rhs.iDefIndex;
+ m_value = rhs.m_value;
+#ifdef GC_DLL
+ m_pKVCustomData = rhs.m_pKVCustomData ? rhs.m_pKVCustomData->MakeCopy() : NULL;
+ bForceGCToGenerate = rhs.bForceGCToGenerate;
+#endif
+ }
+
+ attrib_definition_index_t iDefIndex;
+ attribute_data_union_t m_value;
+#ifdef GC_DLL
+ bool bForceGCToGenerate;
+ KeyValues *m_pKVCustomData;
+#endif // GC_DLL
+
+ // Parses a single subsection from a multi-line attribute block that looks like:
+ //
+ // "attributes"
+ // {
+ // "cannot trade"
+ // {
+ // "attribute_class" "cannot_trade"
+ // "value" "1"
+ // }
+ // "kill eater"
+ // {
+ // "attribute_class" "kill_eater"
+ // "force_gc_to_generate" "1"
+ // "use_custom_logic" "gifts_given_out"
+ // }
+ // }
+ //
+ // The "force_gc_to_generate" and "use_custom_logic" fields will only be parsed on the GC. Will return
+ // true/false based on whether the whole attribute and value parsed successfully.
+ bool BInitFromKV_MultiLine( const char *pszContext, KeyValues *pKVAttribute, CUtlVector<CUtlString> *pVecErrors );
+
+ // Parses a single subsection from a single-line attribute block that looks like:
+ //
+ // CharacterAttributes
+ // {
+ // "increase buff duration" 9.0
+ // "damage bonus" 2.0
+ // }
+ //
+ // It's impossible to specify GC-generated attributes in this format. Will return true/false based on
+ // whether the whole attribute and value parsed successfully.
+ bool BInitFromKV_SingleLine( const char *pszContext, KeyValues *pKVAttribute, CUtlVector<CUtlString> *pVecErrors, bool bEnableTerribleBackwardsCompatibilitySchemaParsingCode = true );
+
+ // Data access helpers.
+ const class CEconItemAttributeDefinition *GetAttributeDefinition() const;
+ const class ISchemaAttributeType *GetAttributeType() const;
+};
+
+typedef uint16 equipped_class_t;
+typedef uint16 equipped_slot_t;
+typedef uint8 equipped_preset_t;
+
+#define INVALID_EQUIPPED_SLOT ((equipped_slot_t)-1)
+#define INVALID_STYLE_INDEX ((style_index_t)-1)
+#define INVALID_PRESET_INDEX ((equipped_preset_t)-1)
+
+enum EEquipType_t
+{
+ EQUIP_TYPE_CLASS = 0,
+ EQUIP_TYPE_ACCOUNT,
+
+ EQUIP_TYPE_INVALID,
+};
+
+// Streamable weapons cause stutters when people enter PVS. Turn it off for now.
+// #define WITH_STREAMABLE_WEAPONS
+
+//-----------------------------------------------------------------------------
+// IEconItemPropertyGenerator
+//-----------------------------------------------------------------------------
+class IEconItemPropertyGenerator
+{
+public:
+ virtual ~IEconItemPropertyGenerator() { }
+
+ MUST_CHECK_RETURN virtual bool BGenerateProperties( CEconItem *pItem ) const = 0;
+};
+
+//-----------------------------------------------------------------------------
+// Item Series
+//-----------------------------------------------------------------------------
+class CEconItemSeriesDefinition
+{
+public:
+ CEconItemSeriesDefinition( void );
+ CEconItemSeriesDefinition( const CEconItemSeriesDefinition &that );
+ CEconItemSeriesDefinition &operator=( const CEconItemSeriesDefinition& rhs );
+
+ ~CEconItemSeriesDefinition( void ) { }
+
+ bool BInitFromKV( KeyValues *pKVItem, CUtlVector<CUtlString> *pVecErrors = NULL );
+
+ int32 GetDBValue( void ) const { return m_nValue; }
+ const char *GetName( void ) const { return !m_strName.IsEmpty() ? m_strName.String() : "unknown"; }
+ const char *GetLocKey( void ) const { return !m_strLockKey.IsEmpty() ? m_strLockKey.String() : "unknown"; }
+ const char *GetUiFile( void ) const { return !m_strUiFile.IsEmpty() ? m_strUiFile.String() : "unknown"; }
+
+private:
+
+ // The value that the game/DB will know this series by
+ int32 m_nValue;
+
+ CUtlString m_strName; // Key Name
+ CUtlString m_strLockKey; // Localization key
+ CUtlString m_strUiFile; // Ui File (.res file)
+};
+//-----------------------------------------------------------------------------
+// CEconItemRarityDefinition
+//-----------------------------------------------------------------------------
+class CEconItemRarityDefinition
+{
+public:
+ CEconItemRarityDefinition( void );
+
+ ~CEconItemRarityDefinition( void ) { }
+
+ bool BInitFromKV( KeyValues *pKVItem, KeyValues *pKVRarityWeights, CEconItemSchema &pschema, CUtlVector<CUtlString> *pVecErrors = NULL );
+
+ int32 GetDBValue( void ) const { return m_nValue; }
+ const char *GetName( void ) const { return !m_strName.IsEmpty() ? m_strName.String() : "unknown"; }
+ const char *GetLocKey( void ) const { return m_strLocKey.String(); }
+ const char *GetWepLocKey( void ) const { return m_strWepLocKey.String(); }
+ const char *GetDropSound( void ) const { return m_strDropSound.String(); }
+ attrib_colors_t GetAttribColor( void ) const { return m_iAttribColor; }
+ const char *GetNextRarity( void ) const { return m_strNextRarity.String(); }
+ int32 GetLootlistWeight( void ) const { return m_nLootlistWeight; }
+
+private:
+
+ // The value that the game/DB will know this rarity by
+ int32 m_nValue;
+
+ attrib_colors_t m_iAttribColor;
+
+ // The English name of the rarity
+ CUtlString m_strName;
+
+ // The localization key for this rarity.
+ CUtlString m_strLocKey;
+ // The localization key for this rarity, for weapons.
+ CUtlString m_strWepLocKey;
+
+ // The loot list name associated with this rarity.
+ CUtlString m_strDropSound;
+
+ CUtlString m_strNextRarity;
+
+ int32 m_nLootlistWeight;
+
+};
+
+//-----------------------------------------------------------------------------
+// CEconItemQualityDefinition
+// Template Definition of a randomly created item
+//-----------------------------------------------------------------------------
+class CEconItemQualityDefinition
+{
+public:
+ CEconItemQualityDefinition( void );
+ CEconItemQualityDefinition( const CEconItemQualityDefinition &that );
+ CEconItemQualityDefinition &operator=( const CEconItemQualityDefinition& rhs );
+
+ ~CEconItemQualityDefinition( void ) { }
+
+ bool BInitFromKV( KeyValues *pKVItem, CUtlVector<CUtlString> *pVecErrors = NULL );
+
+
+ int32 GetDBValue( void ) const { return m_nValue; }
+ const char *GetName( void ) const { return !m_strName.IsEmpty() ? m_strName.Get() : "unknown"; }
+ bool CanSupportSet( void ) const { return m_bCanSupportSet; }
+ const char *GetHexColor( void ) const { return !m_strHexColor.IsEmpty() ? m_strHexColor.Get() : "B2B2B2"; }
+
+#ifdef DBGFLAG_VALIDATE
+ void Validate( CValidator &validator, const char *pchName )
+ {
+ VALIDATE_SCOPE();
+ ValidateObj( m_strName );
+ }
+#endif // DBGFLAG_VALIDATE
+
+private:
+
+ // The value that the game/DB will know this quality by
+ int32 m_nValue;
+
+ // The English name of the quality
+ CUtlConstString m_strName;
+
+ // if this is true the support tool is allowed to set this quality level on any item
+ bool m_bCanSupportSet;
+
+ // A hex string representing the color this quality should display as. Used primarily for display on the Web.
+ CUtlConstString m_strHexColor;
+};
+
+//-----------------------------------------------------------------------------
+// CEconColorDefinition
+//-----------------------------------------------------------------------------
+class CEconColorDefinition
+{
+public:
+ bool BInitFromKV( KeyValues *pKVColor, CUtlVector<CUtlString> *pVecErrors = NULL );
+
+ const char *GetName( void ) const { return m_strName.Get(); }
+ const char *GetColorName( void ) const { return m_strColorName.Get(); } // meant for passing into VGUI styles, etc.
+ const char *GetHexColor( void ) const { return m_strHexColor.Get(); }
+
+private:
+ // The English name of this color. Only used for lookup.
+ CUtlConstString m_strName;
+
+ // The VGUI name of the color in our schema. This will be used to set values
+ // for VGUI controls.
+ CUtlConstString m_strColorName;
+
+ // The hex string value of this color. This will be used for Web display.
+ CUtlConstString m_strHexColor;
+};
+
+//-----------------------------------------------------------------------------
+// CEconItemSetDefinition
+// Definition of an item set
+//-----------------------------------------------------------------------------
+class CEconItemSetDefinition
+{
+public:
+ CEconItemSetDefinition( void );
+ CEconItemSetDefinition( const CEconItemSetDefinition &that );
+ CEconItemSetDefinition &operator=( const CEconItemSetDefinition& rhs );
+
+ ~CEconItemSetDefinition( void ) {}
+
+ bool BInitFromKV( KeyValues *pKVItemSet, CUtlVector<CUtlString> *pVecErrors = NULL );
+
+ void IterateAttributes( class IEconItemAttributeIterator *pIterator ) const;
+
+public:
+
+ const char *m_pszName;
+ const char *m_pszLocalizedName;
+ CUtlVector<item_definition_index_t> m_iItemDefs;
+ int m_iBundleItemDef; // Item def of the store bundle for this set, if any
+ bool m_bIsHiddenSet; // If true, this set and any bonuses will only be visible if the whole set is equipped.
+
+ struct itemset_attrib_t
+ {
+ attrib_definition_index_t m_iAttribDefIndex;
+ float m_flValue;
+ };
+ CUtlVector<itemset_attrib_t> m_iAttributes;
+};
+
+//-----------------------------------------------------------------------------
+class CEconItemCollectionDefinition
+{
+public:
+ CEconItemCollectionDefinition( void );
+ ~CEconItemCollectionDefinition( void ) {}
+
+ bool BInitFromKV( KeyValues *pKVItemCollection, CUtlVector<CUtlString> *pVecErrors = NULL );
+
+ uint8 GetMinRarity() const { return m_iRarityMin; }
+ uint8 GetMaxRarity() const { return m_iRarityMax; }
+
+public:
+
+ const char *m_pszName;
+ const char *m_pszLocalizedName;
+ const char *m_pszLocalizedDesc;
+ CUtlVector<item_definition_index_t> m_iItemDefs;
+
+private:
+ bool m_bIsReferenceCollection;
+
+ uint8 m_iRarityMin;
+ uint8 m_iRarityMax;
+};
+
+//-----------------------------------------------------------------------------
+class CEconItemPaintKitDefinition
+{
+public:
+ CEconItemPaintKitDefinition( void );
+ ~CEconItemPaintKitDefinition( void );
+
+ bool BInitFromKV( KeyValues *pKVItemPaintKit, CUtlVector<CUtlString> *pVecErrors = NULL );
+
+ //KeyValues *GetKVP() { return m_pKVItem; }
+
+ const char *GetName() const { return m_pszName; }
+ const char *GetLocalizeName() const { return m_pszLocalizedName; }
+
+ KeyValues *GetPaintKitWearKV( int nWear );
+
+private:
+ const char *m_pszName;
+ const char *m_pszLocalizedName;
+
+ CUtlVector< KeyValues * > m_vecPaintKitWearKVP;
+};
+
+//-----------------------------------------------------------------------------
+class CEconOperationDefinition
+{
+public:
+ CEconOperationDefinition( void );
+ ~CEconOperationDefinition( void );
+
+ bool BInitFromKV( KeyValues *pKVOperation, CUtlVector<CUtlString> *pVecErrors = NULL );
+
+ const char *GetName() const { return m_pszName; }
+ operation_definition_index_t GetOperationID() const { return m_unOperationID; }
+ item_definition_index_t GetRequiredItemDefIndex() const { return m_unRequiredItemDefIndex; }
+ item_definition_index_t GetGatewayItemDefIndex() const { return m_unGatewayItemDefIndex; }
+
+ KeyValues *GetKVP() { return m_pKVItem; }
+
+ // use the date that we stop giving things to players as expiry date
+ bool IsExpired() const { return CRTime::RTime32TimeCur() > GetStopGivingToPlayerDate(); }
+ bool IsActive() const { return CRTime::RTime32TimeCur() >= GetStartDate() && !IsExpired(); }
+
+ const char *GetQuestLogOverrideResFile() const { return m_pszQuestLogResFile; }
+ const char *GetQuestListOverrideResFile() const { return m_pszQuestListResFile; }
+
+ RTime32 GetStartDate() const { return m_OperationStartDate; }
+ RTime32 GetStopGivingToPlayerDate() const { return m_StopGivingToPlayerDate; }
+ RTime32 GetStopAddingToQueueDate() const { return m_StopAddingToQueueDate; }
+
+ const char *GetOperationLootlist() const { return m_pszOperationLootList; }
+ bool IsCampaign() const { return m_bIsCampaign; }
+ uint32 GetMaxDropCount() const { return m_unMaxDropCount; }
+
+#ifdef GC_DLL
+ enum EContractRewardLootlist_t
+ {
+ REWARD_CASE,
+ REWARD_WEAPON,
+
+ NUM_REWARDS
+ };
+ const char *GetContractRewardLootlist( EContractRewardLootlist_t eType ) const { return m_pszContractRewardLootlist[ eType ]; }
+
+ RTime32 GetMinQueueFreq() const;
+ RTime32 GetMaxQueueFreq() const;
+ RTime32 GetMinDropFreq() const;
+ RTime32 GetMaxDropFreq() const;
+
+ uint8 GetNumSeededContracts() const { return m_unSeed; }
+ uint16 GetNumMaxHeldDrops() const { return m_unMaxHeldDrops; }
+
+ int GetNumMaxQueueCount() const { return m_nMaxQueueCount; }
+
+ uint8 GetMaxDropPerThink() const { return m_unMaxDropPerThink; }
+
+#endif // GC_DLL
+
+private:
+ const char *m_pszName;
+ operation_definition_index_t m_unOperationID;
+
+ // things operation periodically drops
+ const char *m_pszOperationLootList;
+ bool m_bIsCampaign;
+ uint32 m_unMaxDropCount;
+
+ const char *m_pszQuestLogResFile;
+ const char *m_pszQuestListResFile;
+
+ item_definition_index_t m_unRequiredItemDefIndex;
+ item_definition_index_t m_unGatewayItemDefIndex; // Defindex of the item users need to acquire in order to get the required item. Could be the required item itself.
+
+ RTime32 m_OperationStartDate; // when the operation starts and gives out rewards
+ RTime32 m_StopGivingToPlayerDate; // when the operation stops giving quests to player
+ RTime32 m_StopAddingToQueueDate; // when the operation stops adding more quests to the bucket
+
+#ifdef GC_DLL
+ const char *m_pszContractRewardLootlist[ NUM_REWARDS ];
+
+ // in seconds
+ RTime32 m_rtQueueFreqMin;
+ RTime32 m_rtQueueFreqMax;
+ RTime32 m_rtDropFreqMin;
+ RTime32 m_rtDropFreqMax;
+
+ uint8 m_unSeed;
+ uint16 m_unMaxHeldDrops;
+ int m_nMaxQueueCount;
+
+ uint8 m_unMaxDropPerThink;
+
+#endif // GC_DLL
+
+ KeyValues *m_pKVItem;
+};
+
+//-----------------------------------------------------------------------------
+// CEconLootListDefinition
+// Definition of a loot list
+//-----------------------------------------------------------------------------
+class IEconLootList
+{
+public:
+ virtual ~IEconLootList() { }
+
+ MUST_CHECK_RETURN virtual bool BPublicListContents() const = 0;
+ MUST_CHECK_RETURN virtual const char *GetLootListHeaderLocalizationKey() const = 0;
+ MUST_CHECK_RETURN virtual const char *GetLootListFooterLocalizationKey() const = 0;
+ MUST_CHECK_RETURN virtual const char *GetLootListCollectionReference() const = 0;
+
+ class IEconLootListIterator
+ {
+ public:
+ virtual ~IEconLootListIterator() { }
+ virtual void OnIterate( item_definition_index_t unItemDefIndex ) = 0;
+ };
+
+ virtual void EnumerateUserFacingPotentialDrops( IEconLootListIterator *pIt ) const = 0;
+
+#ifdef GC_DLL
+ MUST_CHECK_RETURN virtual bool BGenerateSingleRollRandomItems( const CEconGameAccount *pGameAccount, bool bFreeAccount, CUtlVector<CEconItem *> *out_pvecItems, const CUtlVector< item_definition_index_t > *pVecAvoidItemDefs = NULL ) const = 0;
+#endif // GC_DLL
+};
+
+#ifdef GC_DLL
+struct lootlist_attrib_t
+{
+ static_attrib_t m_staticAttrib;
+ float m_flWeight;
+
+ bool BInitFromKV( const char *pszContext, KeyValues *pKVKey, CEconItemSchema &pschema, CUtlVector<CUtlString> *pVecErrors );
+};
+
+struct random_attrib_t
+{
+ float m_flChanceOfRandomAttribute;
+ float m_flTotalAttributeWeight;
+ bool m_bPickAllAttributes;
+ CUtlVector<lootlist_attrib_t> m_RandomAttributes;
+
+ bool RollRandomAttributes( CUtlVector< static_attrib_t >& vecAttributes, const CEconGameAccount *pGameAccount ) const;
+};
+#endif // GC_DLL
+
+class CEconLootListDefinition : public IEconLootList
+{
+public:
+ struct drop_period_t
+ {
+ bool IsValidForTime( const RTime32& time ) const;
+
+ RTime32 m_DropStartDate;
+ RTime32 m_DropEndDate;
+ };
+
+ struct drop_item_t
+ {
+ int m_iItemOrLootlistDef; // negative values indicate nested loot lists
+ float m_flWeight;
+ drop_period_t m_dropPeriod;
+ };
+
+ struct loot_list_additional_drop_t
+ {
+ float m_fChance;
+ bool m_bPremiumOnly;
+ const char *m_pszLootListDefName;
+ int m_iRequiredHolidayIndex;
+ drop_period_t m_dropPeriod;
+ };
+
+ virtual ~CEconLootListDefinition();
+
+ bool BInitFromKV( KeyValues *pKVLootList, CEconItemSchema &pschema, CUtlVector<CUtlString> *pVecErrors );
+
+ const char *GetName() const { return m_pszName; }
+ virtual const char *GetLootListHeaderLocalizationKey() const OVERRIDE { return m_pszLootListHeader; }
+ virtual const char *GetLootListFooterLocalizationKey() const OVERRIDE { return m_pszLootListFooter; }
+ virtual const char *GetLootListCollectionReference() const OVERRIDE { return m_pszCollectionReference; }
+
+ const CUtlVector<drop_item_t>& GetLootListContents() const { return m_DropList; }
+#ifdef GC_DLL
+ const CUtlVector<loot_list_additional_drop_t>& GetAdditionalDrops() const { return m_AdditionalDrops; }
+#endif
+ virtual void EnumerateUserFacingPotentialDrops( IEconLootListIterator *pIt ) const OVERRIDE;
+
+ virtual bool BPublicListContents() const OVERRIDE
+ {
+ return m_bPublicListContents;
+ }
+
+#ifdef GC_DLL
+
+public:
+ struct rolled_item_defs_t
+ {
+ const CEconItemDefinition *m_pItemDef;
+ CCopyableUtlVector< const CEconLootListDefinition * > m_vecAffectingLootLists;
+ };
+
+ bool AddRandomAtrributes( KeyValues *pRandomAttributesKV, CEconItemSchema &pschema, CUtlVector<CUtlString> *pVecErrors = NULL );
+ bool AddRandomAttributesFromTemplates( KeyValues *pRandomAttributesKV, CEconItemSchema &pschema, CUtlVector<CUtlString> *pVecErrors = NULL );
+
+
+ // Generates a single roll for this loot list as well as each "additional drop" loot list specified. This will return
+ // true if all items were created successfully or false if anything went wrong in any of the relevant lootlists. All
+ // items created will be returned via out_pvecItems.
+ MUST_CHECK_RETURN virtual bool BGenerateSingleRollRandomItems( const CEconGameAccount *pGameAccount, bool bFreeAccount, CUtlVector<CEconItem *> *out_pvecItems, const CUtlVector< item_definition_index_t > *pVecAvoidItemDefs = NULL ) const OVERRIDE;
+
+ void RollRandomAttributes( CUtlVector< static_attrib_t >& vecAttributes, const CEconGameAccount *pGameAccount ) const;
+ bool RollRandomItemsAndAdditionalItems( IUniformRandomStream *pRandomStream, bool bFreeAccount, CUtlVector<rolled_item_defs_t> *out_pVecRolledItems, const CUtlVector< item_definition_index_t > *pVecAvoidItemDefs = NULL ) const;
+
+ uint8 GetRarity() const { return m_unRarity; }
+ void GetRarityLootLists( CUtlVector< const CEconLootListDefinition* > *out_pVecRarityLootList ) const;
+ void GetItemDefs( CUtlVector< item_definition_index_t > *out_pVecItemDefs ) const;
+
+private:
+ bool RollRandomItemDef( IUniformRandomStream *pRandomStream, bool bFreeAccount, CUtlVector<rolled_item_defs_t> *out_pVecRolledItems, const CUtlVector< item_definition_index_t > *pVecAvoidItemDefs = NULL ) const;
+ bool BIsInternalNoDupesLootList() const { return m_iNoDupesIterations >= 0; }
+
+ MUST_CHECK_RETURN bool BInitPropertyGeneratorsFromKV( KeyValues *pKV, CUtlVector<CUtlString> *pVecErrors );
+#endif
+
+private:
+ const char *m_pszName;
+ const char *m_pszLootListHeader;
+ const char *m_pszLootListFooter;
+ const char *m_pszCollectionReference;
+ CUtlVector<drop_item_t> m_DropList;
+
+ bool m_bPublicListContents; // do not show loot list contents to users (ie., when listing crate contents on Steam)
+
+#ifdef GC_DLL
+
+ MUST_CHECK_RETURN bool BAttachLootListAttributes( const CEconGameAccount *pGameAccount, CEconItem *pItem ) const;
+
+ int m_iNoDupesIterations; // if less than zero, "no dupes" functionality disabled; if greater than or equal to zero, the number of iterations we want to run through passing no-dupe sets
+
+ CUtlVector<random_attrib_t*> m_RandomAttribs;
+ CUtlVector<loot_list_additional_drop_t> m_AdditionalDrops;
+ CUtlVector<const IEconItemPropertyGenerator *> m_PropertyGenerators;
+
+ uint8 m_unRarity;
+#endif // GC_DLL
+};
+
+//-----------------------------------------------------------------------------
+// CEconCraftingRecipeDefinition
+// Template Definition of an item recipe
+//-----------------------------------------------------------------------------
+class CEconCraftingRecipeDefinition
+{
+public:
+ CEconCraftingRecipeDefinition( void );
+ virtual ~CEconCraftingRecipeDefinition( void ) { }
+
+ bool BInitFromKV( KeyValues *pKVItem, CUtlVector<CUtlString> *pVecErrors = NULL );
+
+#ifdef GC_DLL
+ bool BIsCraftableByUnverifiedClients() const { return m_bIsCraftableByUnverifiedClient; }
+#endif // GC_DLL
+
+ virtual void CopyPolymorphic( const CEconCraftingRecipeDefinition *pSourceDef ) { *this = *pSourceDef; }
+
+ void SetDefinitionIndex( uint32 iIndex ) { m_nDefIndex = iIndex; }
+ int32 GetDefinitionIndex( void ) const { return m_nDefIndex; }
+ const char *GetName( void ) const { return !m_strName.IsEmpty() ? m_strName.String() : "unknown"; }
+ const char *GetName_A( void ) const { return !m_strN_A.IsEmpty() ? m_strN_A.String() : "unknown"; }
+ const char *GetDescInputs( void ) const { return !m_strDescInputs.IsEmpty() ? m_strDescInputs.String() : "unknown"; }
+ const char *GetDescOutputs( void ) const { return !m_strDescOutputs.IsEmpty() ? m_strDescOutputs.String() : "unknown"; }
+
+ const char *GetDescI_A( void ) const { return !m_strDI_A.IsEmpty() ? m_strDI_A.String() : "unknown"; }
+ const char *GetDescI_B( void ) const { return !m_strDI_B.IsEmpty() ? m_strDI_B.String() : "unknown"; }
+ const char *GetDescI_C( void ) const { return !m_strDI_C.IsEmpty() ? m_strDI_C.String() : "unknown"; }
+ const char *GetDescO_A( void ) const { return !m_strDO_A.IsEmpty() ? m_strDO_A.String() : "unknown"; }
+ const char *GetDescO_B( void ) const { return !m_strDO_B.IsEmpty() ? m_strDO_B.String() : "unknown"; }
+ const char *GetDescO_C( void ) const { return !m_strDO_C.IsEmpty() ? m_strDO_C.String() : "unknown"; }
+
+ bool IsDisabled( void ) const { return m_bDisabled; }
+ bool RequiresAllSameClass( void ) { return m_bRequiresAllSameClass; }
+ bool RequiresAllSameSlot( void ) { return m_bRequiresAllSameSlot; }
+ bool IsPremiumAccountOnly( void ) const { return m_bPremiumAccountOnly; }
+ recipecategories_t GetCategory( void ) const { return m_iCategory; }
+ int GetTotalInputItemsRequired( void ) const;
+ int GetTotalOutputItems( void ) const { return m_OutputItemsCriteria.Count(); }
+
+ // Returns true if the vector contains a set of items that matches the inputs for this recipe
+ virtual bool ItemListMatchesInputs( CUtlVector<CEconItem*> *vecCraftingItems, KeyValues *out_pCraftParams = NULL, bool bIgnoreSlop = false, CUtlVector<uint64> *vecChosenItems = NULL ) const;
+
+ const CUtlVector<CItemSelectionCriteria> *GetInputItems( void ) const { return &m_InputItemsCriteria; }
+ const CUtlVector<uint32> &GetInputItemDupeCounts( void ) const { return m_InputItemDupeCounts; }
+ const CUtlVector<CItemSelectionCriteria> &GetOutputItems( void ) const { return m_OutputItemsCriteria; }
+
+#ifdef DBGFLAG_VALIDATE
+ void Validate( CValidator &validator, const char *pchName )
+ {
+ VALIDATE_SCOPE();
+ ValidateObj( m_InputItemsCriteria );
+ ValidateObj( m_InputItemDupeCounts );
+ ValidateObj( m_OutputItemsCriteria );
+ }
+#endif // DBGFLAG_VALIDATE
+
+ // Serializes the criteria to and from messages
+ bool BSerializeToMsg( CSOItemRecipe & msg ) const;
+ bool BDeserializeFromMsg( const CSOItemRecipe & msg );
+
+protected:
+ // The number used to refer to this definition in the DB
+ int32 m_nDefIndex;
+
+ // Localization key strings
+ CUtlString m_strName;
+ CUtlString m_strN_A;
+ CUtlString m_strDescInputs;
+ CUtlString m_strDescOutputs;
+ CUtlString m_strDI_A;
+ CUtlString m_strDI_B;
+ CUtlString m_strDI_C;
+ CUtlString m_strDO_A;
+ CUtlString m_strDO_B;
+ CUtlString m_strDO_C;
+
+ bool m_bDisabled;
+#ifdef GC_DLL
+ bool m_bIsCraftableByUnverifiedClient;
+#endif // GC_DLL
+ bool m_bRequiresAllSameClass;
+ bool m_bRequiresAllSameSlot;
+ int m_iCacheClassUsageForOutputFromItem;
+ int m_iCacheSlotUsageForOutputFromItem;
+ int m_iCacheSetForOutputFromItem;
+ bool m_bPremiumAccountOnly;
+ recipecategories_t m_iCategory;
+
+ // The list of items that a required to make this recipe
+ CUtlVector<CItemSelectionCriteria> m_InputItemsCriteria;
+ CUtlVector<uint32> m_InputItemDupeCounts;
+
+ // The list of items that are generated by this recipe
+ CUtlVector<CItemSelectionCriteria> m_OutputItemsCriteria;
+};
+
+//-----------------------------------------------------------------------------
+// Purpose: Attribute definition details
+//-----------------------------------------------------------------------------
+enum
+{
+ ATTDESCFORM_VALUE_IS_PERCENTAGE, // Printed as: ((m_flValue*100)-100.0)
+ ATTDESCFORM_VALUE_IS_INVERTED_PERCENTAGE, // Printed as: ((m_flValue*100)-100.0) if it's > 1.0, or ((1.0-m_flModifier)*100) if it's < 1.0
+ ATTDESCFORM_VALUE_IS_ADDITIVE, // Printed as: m_flValue
+ ATTDESCFORM_VALUE_IS_ADDITIVE_PERCENTAGE, // Printed as: (m_flValue*100)
+ ATTDESCFORM_VALUE_IS_OR, // Printed as: m_flValue, but results are ORd together instead of added
+ ATTDESCFORM_VALUE_IS_DATE, // Printed as a date
+ ATTDESCFORM_VALUE_IS_ACCOUNT_ID, // Printed as steam user name
+ ATTDESCFORM_VALUE_IS_PARTICLE_INDEX, // Printed as a particle description
+ ATTDESCFORM_VALUE_IS_KILLSTREAKEFFECT_INDEX,// Printed as killstreak effect description
+ ATTDESCFORM_VALUE_IS_KILLSTREAK_IDLEEFFECT_INDEX, // Printed as idle effect description
+ ATTDESCFORM_VALUE_IS_ITEM_DEF, // Printed as item name
+ ATTDESCFORM_VALUE_IS_FROM_LOOKUP_TABLE, // Printed as a string from a lookup table, specified by the attribute definition name
+};
+
+// Coloring for attribute lines
+enum attrib_effect_types_t
+{
+ ATTRIB_EFFECT_UNUSUAL = 0,
+ ATTRIB_EFFECT_STRANGE,
+ ATTRIB_EFFECT_NEUTRAL,
+ ATTRIB_EFFECT_POSITIVE,
+ ATTRIB_EFFECT_NEGATIVE,
+
+ NUM_EFFECT_TYPES,
+};
+
+enum EAssetClassAttrExportRule_t
+{
+ k_EAssetClassAttrExportRule_Default = 0,
+ k_EAssetClassAttrExportRule_Bucketed = ( 1 << 0 ), // attribute exports bucketed value to Steam Community
+ k_EAssetClassAttrExportRule_Skip = ( 1 << 1 ), // attribute value is not exported to Steam Community
+ k_EAssetClassAttrExportRule_GCOnly = ( 1 << 2 ), // attribute only lives on GC and not exported to any external request
+};
+
+//-----------------------------------------------------------------------------
+// CEconItemAttributeDefinition
+// Template definition of a randomly created attribute
+//-----------------------------------------------------------------------------
+class CEconItemAttributeDefinition
+{
+public:
+ CEconItemAttributeDefinition( void );
+ CEconItemAttributeDefinition( const CEconItemAttributeDefinition &that );
+ CEconItemAttributeDefinition &operator=( const CEconItemAttributeDefinition& rhs );
+
+ ~CEconItemAttributeDefinition( void );
+
+ bool BInitFromKV( KeyValues *pKVAttribute, CUtlVector<CUtlString> *pVecErrors = NULL );
+
+ attrib_definition_index_t GetDefinitionIndex( void ) const { return m_nDefIndex; }
+ // Attribute name referenced in the db.
+ const char *GetDefinitionName( void ) const { return m_pszDefinitionName; }
+
+ KeyValues *GetRawDefinition( void ) const { return m_pKVAttribute; }
+
+ // Data accessing
+ bool IsHidden( void ) const { return m_bHidden; }
+ bool BForceWebSchemaOutput( void ) const { return m_bWebSchemaOutputForced; }
+ bool BIsSetBonusAttribute( void ) const { return m_bIsSetBonus; }
+ bool CanAffectMarketName( void ) const { return m_bCanAffectMarketName; }
+ bool CanAffectRecipeComponentName( void ) const { return m_bCanAffectRecipeComponentName; }
+ bool IsStoredAsInteger( void ) const { return m_bStoredAsInteger; }
+ bool IsStoredAsFloat( void ) const { return !m_bStoredAsInteger; }
+ int GetUserGenerationType( void ) const { return m_iUserGenerationType; }
+ bool IsInstanceData() const { return m_bInstanceData; }
+ EAssetClassAttrExportRule_t GetAssetClassAttrExportRule() const { return m_eAssetClassAttrExportRule; }
+ uint32 GetAssetClassBucket() const { return m_unAssetClassBucket; }
+ int GetDescriptionFormat( void ) const { return m_iDescriptionFormat; }
+ const char *GetDescriptionString( void ) const { return m_pszDescriptionString; }
+ const char *GetArmoryDescString( void ) const { return m_pszArmoryDesc; }
+ const char *GetAttributeClass( void ) const { return m_pszAttributeClass; }
+ econ_tag_handle_t GetItemDefinitionTag( void ) const { return m_ItemDefinitionTag; }
+ attrib_effect_types_t GetEffectType( void ) const { return m_iEffectType; }
+
+ const class ISchemaAttributeType *GetAttributeType( void ) const { return m_pAttrType; }
+
+#ifndef GC_DLL
+ void ClearStringCache( void ) const { m_iszAttributeClass = NULL_STRING; }
+ string_t GetCachedClass( void ) const
+ {
+ if ( m_iszAttributeClass == NULL_STRING && m_pszAttributeClass )
+ {
+ m_iszAttributeClass = AllocPooledString( m_pszAttributeClass );
+ }
+ return m_iszAttributeClass;
+ }
+#endif
+
+#ifdef DBGFLAG_VALIDATE
+ void Validate( CValidator &validator, const char *pchName )
+ {
+ VALIDATE_SCOPE();
+ ValidatePtr( m_pKVAttribute );
+ }
+#endif // DBGFLAG_VALIDATE
+
+private:
+ // The raw keyvalues for this attribute definition.
+ KeyValues *m_pKVAttribute;
+
+ // Required valued from m_pKVAttribute:
+
+ // The number used to refer to this definition in the DB
+ attrib_definition_index_t m_nDefIndex;
+
+ // A pointer to the schema-global type data for this attribute. This maps attribute types to functionality
+ // for loading/storing, both to memory and the DB.
+ const class ISchemaAttributeType *m_pAttrType;
+
+ // ---------------------------------------------
+ // Display related data
+ // ---------------------------------------------
+ // If true, this attribute isn't shown in the item description
+ bool m_bHidden;
+
+ // If true, this attribute's description is always output in web api calls regardless of the hidden flag.
+ bool m_bWebSchemaOutputForced;
+
+ // Whether or not the value is stored as an integer in the DB.
+ bool m_bStoredAsInteger;
+
+ // If this is true the attribute is counted as "instance" data for purposes of asset class in the Steam Economy. Non-instance
+ // properties are considered things that can differentiate items at a fundamental level (ie., definition index, quality); instance
+ // properties are more things like additional customizations -- score for strange items, paint color, etc.
+ bool m_bInstanceData;
+ EAssetClassAttrExportRule_t m_eAssetClassAttrExportRule; // if this is true the attribute will not be exported for asset class
+ uint32 m_unAssetClassBucket; // if this is set then attribute value is bucketed when exported for asset class
+
+ // Set item bonus attributes use a different attribute parser and make assumptions about memory layout. We
+ // don't really use these for any new content currently and it isn't worth touching all the old code.
+ //
+ // At runtime, this flag is used to determine whether or not to rebuild dynamic attributes attached to
+ // players on respawn.
+ bool m_bIsSetBonus;
+
+ // Whether or not this attribute is supposed to only come from user actions. These attributes are used for
+ // player item upgrades, etc. and cannot be set on items directly in the schema.
+ int m_iUserGenerationType;
+
+ // Overall positive/negative effect. Used to color the attribute.
+ attrib_effect_types_t m_iEffectType;
+
+ // Contains the description format & string for this attribute
+ int m_iDescriptionFormat;
+ const char *m_pszDescriptionString;
+
+ // Contains information on how to describe items with this attribute in the Armory
+ const char *m_pszArmoryDesc;
+
+ // Used to allow unique items to specify attributes by name.
+ const char *m_pszDefinitionName;
+
+ // The class name of this attribute. Used in creation, and to hook the attribute into the actual code that uses it.
+ const char *m_pszAttributeClass;
+
+ // Allowed to affect the market bucketization name. We dont want things like the strange level to affect the name,
+ // but we do want things like crate series number and strangifier targets to get their own buckets.
+ bool m_bCanAffectMarketName;
+
+ // Allowed to list itself in the name of an item in the recipe component description.
+ bool m_bCanAffectRecipeComponentName;
+
+ // Do item definitions with this attribute specified automatically get an additional tag applied?
+ econ_tag_handle_t m_ItemDefinitionTag;
+
+#ifndef GC_DLL
+ mutable string_t m_iszAttributeClass; // Same as the above, but used for fast lookup when applying attributes.
+#endif
+};
+
+
+//-----------------------------------------------------------------------------
+// Visual data storage in item definitions
+//-----------------------------------------------------------------------------
+#define TEAM_VISUAL_SECTIONS 5
+#define MAX_VISUALS_CUSTOM_SOUNDS 10
+
+struct attachedparticlesystem_t
+{
+ attachedparticlesystem_t() :
+ pszSystemName( NULL )
+ , bFollowRootBone( NULL )
+ , iCustomType( 0 )
+ , nSystemID( 0 )
+ , fRefireTime( 0 ) // only works for taunt effects, currently
+ , bDrawInViewModel( false )
+ , bUseSuffixName( false )
+ , bHasViewModelSpecificEffect ( false )
+ {
+ V_memset( pszControlPoints, 0, sizeof( pszControlPoints ) );
+ }
+
+ const char *pszSystemName;
+ bool bFollowRootBone;
+ int iCustomType;
+ int nSystemID;
+ float fRefireTime; // only works for taunt effects, currently
+ bool bDrawInViewModel;
+ bool bUseSuffixName;
+ bool bHasViewModelSpecificEffect;
+
+ const char *pszControlPoints[7];
+};
+
+
+#if defined(CLIENT_DLL) || defined(GAME_DLL)
+enum
+{
+ kAttachedModelDisplayFlag_WorldModel = 0x01,
+ kAttachedModelDisplayFlag_ViewModel = 0x02,
+
+ kAttachedModelDisplayFlag_MaskAll = kAttachedModelDisplayFlag_WorldModel | kAttachedModelDisplayFlag_ViewModel,
+};
+
+struct attachedmodel_t
+{
+ const char *m_pszModelName;
+ int m_iModelDisplayFlags;
+};
+
+enum wearableanimplayback_t
+{
+ WAP_ON_SPAWN, // Play this animation immediately on spawning the wearable
+ WAP_START_BUILDING, // Game code will start this anim whenever a player wearing this item deploys their builder weapon.
+ WAP_STOP_BUILDING, // Game code will start this anim whenever a player wearing this item holsters their builder weapon.
+ WAP_START_TAUNTING, // Game code will start this anim whenever a player wearing this item taunts
+ WAP_STOP_TAUNTING, // Game code will start this anim whenever a player wearing this item stops taunting
+
+ NUM_WAP_TYPES,
+};
+
+struct animation_on_wearable_t
+{
+ int iActivity;
+ const char *pszActivity;
+ const char *pszReplacement;
+ int iReplacement; // Replacement activity to play. Might be set to one of kActivityLookup_Unknown/kActivityLookup_Missing.
+ const char *pszSequence;
+ const char *pszRequiredItem;
+ const char *pszScene;
+};
+
+struct activity_on_wearable_t
+{
+ wearableanimplayback_t iPlayback;
+ int iActivity;
+ const char *pszActivity;
+};
+
+struct codecontrolledbodygroupdata_t
+{
+ const char *pFuncName;
+ void *pFunc;
+};
+
+// This is a workaround because Source practice is to disable operator=() for CUtlMap.
+struct perteamvisuals_maps_t
+{
+ perteamvisuals_maps_t()
+ {
+ m_ModifiedBodyGroupNames.SetLessFunc( StringLessThan );
+ m_CodeControlledBodyGroupNames.SetLessFunc( StringLessThan );
+ }
+
+ void operator=( const perteamvisuals_maps_t& other )
+ {
+ DeepCopyMap( other.m_ModifiedBodyGroupNames, &m_ModifiedBodyGroupNames );
+ DeepCopyMap( other.m_CodeControlledBodyGroupNames, &m_CodeControlledBodyGroupNames );
+ }
+
+ CUtlMap<const char*, int> m_ModifiedBodyGroupNames; // Better method: hide multiple body groups by name.
+ CUtlMap<const char*, codecontrolledbodygroupdata_t> m_CodeControlledBodyGroupNames;
+};
+
+#endif // defined(CLIENT_DLL) || defined(GAME_DLL)
+
+class CEconStyleInfo
+{
+public:
+ CEconStyleInfo()
+ {
+ for ( int i = 0; i < TEAM_VISUAL_SECTIONS; i++ )
+ {
+ m_iSkins[i] = 0;
+ m_iViewmodelSkins[i] = -1;
+ }
+
+ m_pszName = NULL;
+ m_pszBasePlayerModel = NULL;
+ m_bIsSelectable = true;
+ m_pszInventoryImage = NULL;
+
+ m_pszBodygroupName = NULL;
+ m_iBodygroupSubmodelIndex = -1;
+
+ m_sIconURLSmall = "";
+ m_sIconURLLarge = "";
+ }
+
+ virtual ~CEconStyleInfo()
+ {
+ //
+ }
+
+ virtual void BInitFromKV( KeyValues *pKVItem, CUtlVector<CUtlString> *pVecErrors );
+
+#if defined(CLIENT_DLL) || defined(GAME_DLL)
+ virtual void GeneratePrecacheModelStringsForStyle( CUtlVector<const char *> *out_pVecModelStrings ) const;
+#endif
+
+ int GetSkin( int iTeam, bool bViewmodel ) const
+ {
+ Assert( iTeam >= 0 );
+ Assert( iTeam < TEAM_VISUAL_SECTIONS );
+
+ if ( bViewmodel && m_iViewmodelSkins[ iTeam ] != -1 )
+ {
+ return m_iViewmodelSkins[ iTeam ];
+ }
+
+ return m_iSkins[iTeam];
+ }
+
+ const char *GetName() const { return m_pszName; }
+ const char *GetBasePlayerDisplayModel() const { return m_pszBasePlayerModel; }
+ const CUtlVector<const char *>& GetAdditionalHideBodygroups() const { return m_vecAdditionalHideBodygroups; }
+ bool IsSelectable() const { return m_bIsSelectable; }
+ const char *GetInventoryImage() const { return m_pszInventoryImage; }
+
+ const char *GetBodygroupName() const { return m_pszBodygroupName; }
+ int GetBodygroupSubmodelIndex() const { return m_iBodygroupSubmodelIndex; }
+
+ const char *GetIconURLSmall() const { return m_sIconURLSmall; }
+ const char *GetIconURLLarge() const { return m_sIconURLLarge; }
+ void SetIconURLSmall( const char *szURL ) { m_sIconURLSmall = szURL; }
+ void SetIconURLLarge( const char *szURL ) { m_sIconURLLarge = szURL; }
+
+protected:
+ int m_iSkins[TEAM_VISUAL_SECTIONS];
+ int m_iViewmodelSkins[TEAM_VISUAL_SECTIONS];
+ const char *m_pszName;
+ const char *m_pszBasePlayerModel;
+ bool m_bIsSelectable;
+ const char *m_pszInventoryImage;
+
+ const char *m_pszBodygroupName;
+ int m_iBodygroupSubmodelIndex;
+
+ CUtlVector<const char *> m_vecAdditionalHideBodygroups;
+
+private:
+
+ CUtlString m_sIconURLSmall;
+ CUtlString m_sIconURLLarge;
+};
+
+struct perteamvisuals_t
+{
+ perteamvisuals_t()
+ {
+#if defined(CLIENT_DLL) || defined(GAME_DLL)
+ iHideParentBodyGroup = -1;
+
+ iSkin = -1;
+ bUsePerClassBodygroups = false;
+ pszMaterialOverride = NULL;
+ pszMuzzleFlash = NULL;
+ pszTracerEffect = NULL;
+ pszParticleEffect = NULL;
+ for ( int i = 0; i < MAX_VISUALS_CUSTOM_SOUNDS; i++ )
+ {
+ pszCustomSounds[i] = NULL;
+ }
+
+ for ( int i = 0; i < NUM_SHOOT_SOUND_TYPES; i++ )
+ {
+ pszWeaponSoundReplacements[i] = NULL;
+ }
+
+ m_iViewModelBodyGroupOverride = -1;
+ m_iViewModelBodyGroupStateOverride = -1;
+ m_iWorldModelBodyGroupOverride = -1;
+ m_iWorldModelBodyGroupStateOverride = -1;
+
+#endif // defined(CLIENT_DLL) || defined(GAME_DLL)
+ }
+
+ ~perteamvisuals_t()
+ {
+ m_Styles.PurgeAndDeleteElements();
+ }
+
+#if defined(CLIENT_DLL) || defined(GAME_DLL)
+ int iHideParentBodyGroup;
+
+ // Properties necessary for the game client/server but not for the GC.
+ perteamvisuals_maps_t m_Maps;
+ int iSkin;
+ bool bUsePerClassBodygroups;
+ CUtlVector<attachedmodel_t> m_AttachedModels;
+ CUtlVector<attachedmodel_t> m_AttachedModelsFestive; // Attr controlled Festive Attachments
+ CUtlVector<attachedparticlesystem_t> m_AttachedParticles;
+ CUtlVector<animation_on_wearable_t> m_Animations;
+ CUtlVector<activity_on_wearable_t> m_Activities;
+ const char *pszCustomSounds[MAX_VISUALS_CUSTOM_SOUNDS];
+ const char *pszMaterialOverride;
+ const char *pszMuzzleFlash;
+ const char *pszTracerEffect;
+ const char *pszParticleEffect;
+ const char *pszWeaponSoundReplacements[NUM_SHOOT_SOUND_TYPES];
+ int m_iViewModelBodyGroupOverride;
+ int m_iViewModelBodyGroupStateOverride;
+ int m_iWorldModelBodyGroupOverride;
+ int m_iWorldModelBodyGroupStateOverride;
+#endif // defined(CLIENT_DLL) || defined(GAME_DLL)
+
+ // The GC does care about styles.
+ CUtlVector<CEconStyleInfo *> m_Styles;
+};
+
+enum item_capabilities_t
+{
+ ITEM_CAP_NONE = 0,
+ ITEM_CAP_PAINTABLE = 1 << 0,
+ ITEM_CAP_NAMEABLE = 1 << 1,
+ ITEM_CAP_DECODABLE = 1 << 2,
+ ITEM_CAP_CAN_BE_CRAFTED_IF_PURCHASED = 1 << 3, // was ITEM_CAP_CAN_MOD_SOCKET
+ ITEM_CAP_CAN_CUSTOMIZE_TEXTURE = 1 << 4,
+ ITEM_CAP_USABLE = 1 << 5,
+ ITEM_CAP_USABLE_GC = 1 << 6,
+ ITEM_CAP_CAN_GIFT_WRAP = 1 << 7,
+ ITEM_CAP_USABLE_OUT_OF_GAME = 1 << 8,
+ ITEM_CAP_CAN_COLLECT = 1 << 9,
+ ITEM_CAP_CAN_CRAFT_COUNT = 1 << 10,
+ ITEM_CAP_CAN_CRAFT_MARK = 1 << 11,
+ ITEM_CAP_PAINTABLE_TEAM_COLORS = 1 << 12,
+ ITEM_CAP_CAN_BE_RESTORED = 1 << 13, // can users remove properties (paint, nametag, etc.) from this item via the in-game UI?
+ ITEM_CAP_CAN_USE_STRANGE_PARTS = 1 << 14,
+ ITEM_CAP_CAN_CARD_UPGRADE = 1 << 15,
+ ITEM_CAP_CAN_STRANGIFY = 1 << 16,
+ ITEM_CAP_CAN_KILLSTREAKIFY = 1 << 17,
+ ITEM_CAP_CAN_CONSUME = 1 << 18,
+ ITEM_CAP_CAN_SPELLBOOK_PAGE = 1 << 19, // IT'S A VERB OKAY
+ ITEM_CAP_HAS_SLOTS = 1 << 20,
+ ITEM_CAP_DUCK_UPGRADABLE = 1 << 21,
+ ITEM_CAP_CAN_UNUSUALIFY = 1 << 22,
+ NUM_ITEM_CAPS = 23,
+};
+
+enum { ITEM_CAP_DEFAULT = ITEM_CAP_CAN_CRAFT_MARK | ITEM_CAP_CAN_BE_RESTORED | ITEM_CAP_CAN_USE_STRANGE_PARTS | ITEM_CAP_CAN_CARD_UPGRADE | ITEM_CAP_CAN_STRANGIFY | ITEM_CAP_CAN_KILLSTREAKIFY | ITEM_CAP_CAN_CONSUME | ITEM_CAP_CAN_GIFT_WRAP }; // what are the default capabilities on an item?
+enum { ITEM_CAP_TOOL_DEFAULT = ITEM_CAP_NONE }; // what are the default capabilities of a tool?
+
+struct bundleinfo_t
+{
+ CUtlVector<CEconItemDefinition *> vecItemDefs;
+};
+
+#ifdef GC_DLL
+enum EPaymentRuleType
+{
+ kPaymentRule_SteamWorkshopFileID = 0x01,
+ kPaymentRule_PartnerSteamID = 0x02,
+ kPaymentRule_Bundle = 0x04,
+};
+
+struct econ_item_payment_rule_t
+{
+ double m_RevenueShare;
+ EPaymentRuleType m_eRuleType;
+ CCopyableUtlVector<uint64> m_vecValues;
+};
+#endif // GC_DLL
+
+#ifdef CLIENT_DLL
+namespace vgui
+{
+ class Panel;
+}
+#endif // CLIENT_DLL
+
+class IEconTool
+{
+ friend class CEconSharedToolSupport;
+
+public:
+ IEconTool( const char *pszTypeName, const char *pszUseString, const char *pszUsageRestriction, item_capabilities_t unCapabilities )
+ : m_pszTypeName( pszTypeName )
+ , m_pszUseString( pszUseString )
+ , m_pszUsageRestriction( pszUsageRestriction )
+ , m_unCapabilities( unCapabilities )
+ {
+ //
+ }
+
+ virtual ~IEconTool() { }
+
+ // Shared code.
+ const char *GetUsageRestriction() const { return m_pszUsageRestriction; }
+ item_capabilities_t GetCapabilities() const { return m_unCapabilities; }
+
+ virtual bool CanApplyTo( const IEconItemInterface *pTool, const IEconItemInterface *pToolSubject ) const { Assert( pTool ); Assert( pToolSubject ); return true; }
+ virtual bool ShouldDisplayQuantity( const IEconItemInterface *pTool ) const;
+ virtual bool RequiresToolEscrowPeriod() const { return false; }
+
+ // We don't support throwing exceptions from tool construction so this is intended to be checked afterwards
+ // whenever a new tool is created. (See CreateEconToolImpl().)
+ virtual bool BFinishInitialization() { return true; }
+
+ // Used by the GC only for WebAPI responses and for some weird internal code.
+ const char *GetTypeName() const { return m_pszTypeName; } // would like to disable on the client so we aren't tempted to check against it, but used for building a unique tool list
+ const char *GetUseString() const { return m_pszUseString; }
+
+#ifdef CLIENT_DLL
+ virtual bool CanBeUsedNow( const IEconItemInterface *pItem ) const { return true; }
+ virtual bool ShouldShowContainedItemPanel( const IEconItemInterface *pItem ) const { Assert( !"IEconTool::ShouldShowContainedItemPanel(): we don't expect this to be called on anything besides gifts!" ); return false; }
+ virtual bool ShouldDisplayAsUseableOnItemsInArmory() const { return true; }
+ virtual const char *GetUseCommandLocalizationToken( const IEconItemInterface *pItem, int i = 0 ) const;
+ virtual int GetUseCommandCount( const IEconItemInterface *pItem ) const { return 1; }
+ virtual const char* GetUseCommand( const IEconItemInterface *pItem, int i = 0 ) const;
+
+
+ // Client "do something" interface. At least one of these functions must be implemented or your tool
+ // won't do anything on the client. Some tools (ie., collections) will implement both because they
+ // have one application behavior and one client-UI behavior.
+
+ // When the client attempts to use a consumable item of any kind, this function will be called. This
+ // is called from the UI in response to things like using dueling pistols, using a noisemaker, etc.
+ // Usually this opens up some UI, sends off a GC message, etc.
+ //
+ // There is a "default" implementation of this function in ClientConsumableTool_Generic() that can
+ // be called if specific behavior isn't needed.
+ virtual void OnClientUseConsumable( class C_EconItemView *pItem, vgui::Panel *pParent ) const
+ {
+ Assert( !"IEconTool::OnClientUseConsumable(): unimplemented call!" );
+ }
+
+ // When the client attempts to apply a tool to a specific other item in their inventory, this function
+ // will be called. This is called from the UI is response to things like putting paint on an item,
+ // using a key to unlock a crate, etc.
+ virtual void OnClientApplyTool( class C_EconItemView *pTool, class C_EconItemView *pSubject, vgui::Panel *pParent ) const
+ {
+ Assert( !"IEconTool::OnClientApplyTool(): unimplemented call!" );
+ }
+#endif // CLIENT_DLL
+
+#ifdef GC_DLL
+ virtual class CGCEconConsumableBehavior *CreateGCConsumableBehavior() const;
+ virtual bool BGenerateDynamicAttributes( CEconItem* pItem, const CEconGameAccount *pGameAccount ) const { return true; }
+#endif // GC_DLL
+
+private:
+ const char *m_pszTypeName;
+ const char *m_pszUseString;
+ const char *m_pszUsageRestriction;
+ item_capabilities_t m_unCapabilities;
+};
+
+//-----------------------------------------------------------------------------
+// CQuestObjectiveDefinition
+//-----------------------------------------------------------------------------
+class CQuestObjectiveDefinition
+{
+public:
+
+ CQuestObjectiveDefinition( void );
+ virtual ~CQuestObjectiveDefinition( void );
+
+ virtual bool BInitFromKV( KeyValues *pKVItem, CUtlVector<CUtlString> *pVecErrors = NULL );
+
+ uint32 GetDefinitionIndex( void ) const { return m_nDefIndex; }
+ const char *GetDescriptionToken( void ) const { return m_pszDescriptionToken; }
+ bool IsOptional() const { return m_bOptional; }
+ bool IsAdvanced() const { return m_bAdvanced; }
+ uint32 GetPoints() const { return m_nPoints; } // TODO: change to a float
+
+private:
+ const char *m_pszDescriptionToken;
+ uint32 m_nDefIndex;
+ uint32 m_nPoints;
+ bool m_bOptional;
+ bool m_bAdvanced;
+};
+
+//-----------------------------------------------------------------------------
+// CEconItemDefinition
+// Template Definition of a randomly created item
+//-----------------------------------------------------------------------------
+class CEconItemDefinition
+{
+public:
+ CEconItemDefinition( void );
+ virtual ~CEconItemDefinition( void );
+
+ // BInitFromKV can be implemented on subclasses to parse additional values.
+ virtual bool BInitFromKV( KeyValues *pKVItem, CUtlVector<CUtlString> *pVecErrors = NULL );
+#if defined(CLIENT_DLL) || defined(GAME_DLL)
+ virtual bool BInitFromTestItemKVs( int iNewDefIndex, KeyValues *pKVItem, CUtlVector<CUtlString>* pVecErrors = NULL );
+ virtual void GeneratePrecacheModelStrings( bool bDynamicLoad, CUtlVector<const char *> *out_pVecModelStrings ) const;
+ virtual void GeneratePrecacheSoundStrings( bool bDynamicLoad, CUtlVector<const char *> *out_pVecSoundStrings ) const;
+ virtual void CopyPolymorphic( const CEconItemDefinition *pSourceDef ) { *this = *pSourceDef; }
+#endif
+
+ bool BInitItemMappings( CUtlVector<CUtlString> *pVecErrors );
+
+ void BInitVisualBlockFromKV( KeyValues *pKVItem, CUtlVector<CUtlString> *pVecErrors = NULL );
+ void BInitStylesBlockFromKV( KeyValues *pKVStyles, perteamvisuals_t *pVisData, CUtlVector<CUtlString> *pVecErrors );
+
+ item_definition_index_t GetDefinitionIndex( void ) const { return m_nDefIndex; }
+ bool BEnabled( void ) const { return m_bEnabled; }
+ bool BLoadOnDemand( void ) const { return m_bLoadOnDemand; }
+ bool BHasBeenLoaded( void ) const { return m_bHasBeenLoaded; }
+ const char *GetDefinitionName( void ) const { return m_pszDefinitionName; }
+ const char *GetItemDefinitionName( void ) const { return m_pszDefinitionName; }
+ const char *GetItemClass( void ) const { return m_pszItemClassname; }
+ const char *GetItemBaseName( void ) const { return m_pszItemBaseName; }
+ const char *GetBrassModelOverride( void ) const{ return m_pszBrassModelOverride; }
+ const char *GetItemTypeName( void ) const { return m_pszItemTypeName; }
+ uint8 GetMinLevel( void ) const { return m_unMinItemLevel; }
+ uint8 GetMaxLevel( void ) const { return m_unMaxItemLevel; }
+ uint8 GetItemSeries( void ) const { return m_unItemSeries; }
+ uint8 GetQuality( void ) const { return m_nItemQuality; }
+ void SetRarity( uint8 nRarity ) { Assert( m_nItemRarity == k_unItemRarity_Any ); m_nItemRarity = nRarity; }
+ uint8 GetRarity( void ) const { return m_nItemRarity; }
+ uint8 GetForcedQuality( void ) const { return m_nForcedItemQuality; }
+ uint16 GetDefaultDropQuantity( void ) const { return m_nDefaultDropQuantity; }
+ KeyValues *GetRawDefinition( void ) const { return m_pKVItem; }
+ const char *GetDefinitionString( const char *pszKeyName, const char *pszDefaultValue = "" ) const;
+ KeyValues *GetDefinitionKey( const char *pszKeyName ) const;
+ const CUtlVector<static_attrib_t> &GetStaticAttributes( void ) const { return m_vecStaticAttributes; }
+#ifdef TF_CLIENT_DLL
+ uint32 GetNumConcreteItems() const { return m_unNumConcreteItems; }
+#endif // TF_CLIENT_DLL
+
+ // Data accessing
+ bool IsHidden( void ) const { return m_bHidden; }
+ bool IsImported( void ) const { return m_bImported; }
+ bool IsAllowedInMatch( void ) const { return m_bAllowedInThisMatch; }
+ bool IsBaseItem( void ) const { return m_bBaseItem; }
+ bool IsBundle( void ) const { return m_BundleInfo != NULL; }
+ bool HasProperName( void ) const { return m_bProperName; }
+ const char *GetClassToken( void ) const { return m_pszClassToken; }
+ const char *GetSlotToken( void ) const { return m_pszSlotToken; }
+ bool ShouldAttachToHands( void ) const { return m_bAttachToHands; }
+ bool ShouldAttachToHandsVMOnly( void ) const { return m_bAttachToHandsVMOnly; }
+ bool ShouldFlipViewmodels( void ) const { return m_bFlipViewModel; }
+ int GetInventoryImagePosition( int iIndex ) const { Assert( iIndex >= 0 && iIndex < 2); return m_iInventoryImagePosition[iIndex]; }
+ int GetInventoryImageSize( int iIndex ) const { Assert( iIndex >= 0 && iIndex < 2); return m_iInventoryImageSize[iIndex]; }
+ int GetDropType( void ) const { return m_iDropType; }
+ const char *GetHolidayRestriction( void ) const { return m_pszHolidayRestriction; }
+ int GetVisionFilterFlags( void ) const { return m_nVisionFilterFlags; }
+ int GetSubType( void ) const { return m_iSubType; }
+ item_capabilities_t GetCapabilities( void ) const { return m_iCapabilities; }
+ int GetArmoryRemap( void ) const { return m_iArmoryRemap; }
+ int GetStoreRemap( void ) const { return m_iStoreRemap; }
+ item_definition_index_t GetSetItemRemap() const { return m_unSetItemRemapDefIndex; } // what def index do we consider ourself for purposes of determining "is an item equipped that satisfies this set slot?" (ie., Festive Huntsman -> Huntsman); default is to point to itself
+ const char *GetXifierRemapClass() const { return m_pszXifierRemapClass; }
+ const char *GetBaseFunctionalItemName() const { return m_pszBaseFunctionalItemName; }
+ const char *GetParticleSuffix() const { return m_pszParticleSuffix; }
+
+ const CEconItemSetDefinition *GetItemSetDefinition( void ) const { return m_pItemSetDef; }
+ void SetItemSetDefinition( const CEconItemSetDefinition *pItemSetDef ) { Assert( !m_pItemSetDef ); m_pItemSetDef = pItemSetDef; }
+
+ const CEconItemCollectionDefinition *GetItemCollectionDefinition( void ) const { return m_pItemCollectionDef; }
+ void SetItemCollectionDefinition( const CEconItemCollectionDefinition *pItemCollectionDef ) { Assert( !m_pItemCollectionDef ); m_pItemCollectionDef = pItemCollectionDef; }
+
+ CEconItemPaintKitDefinition *GetCustomPainkKitDefinition( void ) const { return m_pItemPaintKitDef; }
+ void SetItemPaintKitDefinition( CEconItemPaintKitDefinition *pItemPaintKitDef ) { Assert( !m_pItemPaintKitDef ); m_pItemPaintKitDef = pItemPaintKitDef; }
+
+ perteamvisuals_t *GetPerTeamVisual( int iTeam ) const { return m_PerTeamVisuals[iTeam]; }
+
+ bool IsTool() const { return m_bIsTool; }
+ const IEconTool *GetEconTool() const { return m_pTool; }
+ template < class T >
+ const T *GetTypedEconTool() const { return dynamic_cast<const T *>( GetEconTool() ); }
+
+ const bundleinfo_t *GetBundleInfo( void ) const { return m_BundleInfo; }
+ virtual int GetBundleItemCount( void ) const { return m_BundleInfo ? m_BundleInfo->vecItemDefs.Count() : 0; }
+ virtual int GetBundleItem( int iIndex ) const { return m_BundleInfo ? m_BundleInfo->vecItemDefs[iIndex]->GetDefinitionIndex() : -1; }
+
+ // Is this item contained in any bundles? GetContainingBundles() gets the CEconItemDefinitions for those bundles.
+ const CUtlVector< const CEconItemDefinition * > &GetContainingBundles() const { return m_vecContainingBundleItemDefs; }
+ uint32 GetContainingBundleCount() const { return m_vecContainingBundleItemDefs.Count(); }
+
+ void AddSteamWorkshopContributor( uint32 unAccountID ) { if ( m_vecSteamWorkshopContributors.InvalidIndex() == m_vecSteamWorkshopContributors.Find( unAccountID ) ) { m_vecSteamWorkshopContributors.AddToTail( unAccountID ); } }
+ const CUtlVector< uint32 > &GetSteamWorkshopContributors() const { return m_vecSteamWorkshopContributors; }
+ bool BIsSteamWorkshopItem() const { return m_vecSteamWorkshopContributors.Count() > 0; }
+
+ const char *GetIconClassname( void ) const { return m_pszItemIconClassname; }
+ const char *GetLogClassname( void ) const { return m_pszItemLogClassname; }
+ const char *GetInventoryModel( void ) const { return m_pszInventoryModel; }
+ const char *GetInventoryImage( void ) const { return m_pszInventoryImage; }
+ const char *GetInventoryOverlayImage( int idx ) const { if ( m_pszInventoryOverlayImages.IsValidIndex( idx ) ) return m_pszInventoryOverlayImages[idx]; else return NULL; }
+ int GetInventoryOverlayImageCount( void ) const { return m_pszInventoryOverlayImages.Count(); }
+ int GetInspectPanelDistance( void ) const { return m_iInspectPanelDistance; }
+ const char *GetIconURLSmall() const { return GetIconURL( "s" ); } // Plain small
+ const char *GetIconURLLarge() const { return GetIconURL( "l" ); } // Plain large
+ void SetIconURL( const char* pszKey, const char *szURL ) { m_pDictIcons->Insert( pszKey, CUtlString( szURL ) ); }
+ const char *GetIconURL( const char* pszKey ) const;
+ const char *GetBasePlayerDisplayModel() const { return m_pszBaseDisplayModel; }
+ int GetDefaultSkin() const { return m_iDefaultSkin; }
+ const char *GetWorldDisplayModel() const { return m_pszWorldDisplayModel; }
+ const char *GetCollectionReference() const { return m_pszCollectionReference; }
+
+ // Some weapons need a custom model for icon generation. If this value is not present, the world model is used.
+ virtual const char *GetIconDisplayModel() const;
+
+ const char *GetExtraWearableModel( void ) const { return m_pszWorldExtraWearableModel; }
+ const char *GetExtraWearableViewModel( void ) const { return m_pszWorldExtraWearableViewModel; }
+ const char *GetVisionFilteredDisplayModel() const { return m_pszVisionFilteredDisplayModel; }
+ const char *GetItemDesc( void ) const { return m_pszItemDesc; }
+ const char *GetArmoryDescString( void ) const { return m_pszArmoryDesc; }
+ RTime32 GetExpirationDate( void ) const { return m_rtExpiration; }
+ bool ShouldShowInArmory( void ) const { return m_bShouldShowInArmory; }
+ bool IsActingAsAWearable( void ) const { return m_bActAsWearable; }
+ bool IsActingAsAWeapon( void ) const { return m_bActAsWeapon; }
+ bool GetHideBodyGroupsDeployedOnly( void ) const { return m_bHideBodyGroupsDeployedOnly; }
+ bool IsPackBundle( void ) const { return m_bIsPackBundle; }
+ bool IsPackItem( void ) const { return m_bIsPackItem; }
+ CEconItemDefinition *GetOwningPackBundle() { return m_pOwningPackBundle; }
+ const CEconItemDefinition *GetOwningPackBundle() const { return m_pOwningPackBundle; }
+ const char *GetDatabaseAuditTableName( void ) const { return m_pszDatabaseAuditTable; }
+
+ void SetIsPackItem( bool bIsPackItem ) { m_bIsPackItem = bIsPackItem; }
+
+ equip_region_mask_t GetEquipRegionMask( void ) const { return m_unEquipRegionMask; }
+ equip_region_mask_t GetEquipRegionConflictMask( void ) const { return m_unEquipRegionConflictMask; }
+
+ // Dynamic modification during gameplay
+ void SetAllowedInMatch( bool bAllowed ) { m_bAllowedInThisMatch = bAllowed; }
+ void SetHasBeenLoaded( bool bLoaded ) { m_bHasBeenLoaded = bLoaded; }
+
+ // Generate and return a random level according to whatever leveling curve this definition uses.
+ uint32 RollItemLevel( void ) const;
+
+ const char *GetFirstSaleDate( void ) const;
+
+ void IterateAttributes( class IEconItemAttributeIterator *pIterator ) const;
+
+#if defined(CLIENT_DLL) || defined(GAME_DLL)
+ // Visuals
+ // Attached models
+ int GetNumAttachedModels( int iTeam ) const;
+ attachedmodel_t *GetAttachedModelData( int iTeam, int iIdx ) const;
+
+ int GetNumAttachedModelsFestivized( int iTeam ) const;
+ attachedmodel_t *GetAttachedModelDataFestivized( int iTeam, int iIdx ) const;
+
+ // Attached particle systems
+ int GetNumAttachedParticles( int iTeam ) const;
+ attachedparticlesystem_t *GetAttachedParticleData( int iTeam, int iIdx ) const;
+ // Activities
+ int GetNumPlaybackActivities( int iTeam ) const;
+ activity_on_wearable_t *GetPlaybackActivityData( int iTeam, int iIdx ) const;
+ // Animations
+ int GetNumAnimations( int iTeam ) const;
+ animation_on_wearable_t *GetAnimationData( int iTeam, int iIdx ) const;
+ // Animation Overrides
+ Activity GetActivityOverride( int iTeam, Activity baseAct ) const;
+ const char *GetActivityOverride( int iTeam, const char *pszActivity ) const;
+ const char *GetReplacementForActivityOverride( int iTeam, Activity baseAct ) const;
+ // Should the content (meshes, etc.) for this be streamed or preloaded?
+ virtual bool IsContentStreamable() const;
+#endif // defined(CLIENT_DLL) || defined(GAME_DLL)
+
+ // FX Overrides
+ const char *GetMuzzleFlash( int iTeam ) const;
+ const char *GetTracerEffect( int iTeam ) const;
+ const char *GetParticleEffect( int iTeam ) const;
+ // Materials
+ const char *GetMaterialOverride( int iTeam ) const;
+ // Sounds
+ const char *GetCustomSound( int iTeam, int iSound ) const;
+ const char *GetWeaponReplacementSound( int iTeam, /*WeaponSound_t*/ int iSound ) const;
+ // Bodygroups
+ int GetHiddenParentBodygroup( int iTeam ) const;
+ int GetNumModifiedBodyGroups( int iTeam ) const;
+ const char* GetModifiedBodyGroup( int iTeam, int i, int& body ) const;
+ bool UsesPerClassBodygroups( int iTeam ) const;
+ int GetNumCodeControlledBodyGroups( int iTeam ) const;
+ const char* GetCodeControlledBodyGroup( int iIteam, int i, struct codecontrolledbodygroupdata_t &ccbgd ) const;
+
+ style_index_t GetNumStyles() const;
+ style_index_t GetNumSelectableStyles() const;
+ const CEconStyleInfo *GetStyleInfo( style_index_t unStyle ) const;
+
+ int GetViewmodelBodygroupOverride( int iTeam ) const;
+ int GetViewmodelBodygroupStateOverride( int iTeam ) const;
+ int GetWorldmodelBodygroupOverride( int iTeam ) const;
+ int GetWorldmodelBodygroupStateOverride( int iTeam ) const;
+
+ int GetPopularitySeed() const { return m_nPopularitySeed; }
+
+ bool HasEconTag( econ_tag_handle_t tag ) const { return m_vecTags.IsValidIndex( m_vecTags.Find( tag ) ); }
+
+ bool BValidForShuffle( void ) const { return m_bValidForShuffle; }
+ bool BValidForSelfMade( void ) const { return m_bValidForSelfMade; }
+
+#ifdef GC_DLL
+private:
+ MUST_CHECK_RETURN bool BInitializeEconItemGenerators( KeyValues *pKV, CUtlVector<CUtlString> *pVecErrors );
+
+public:
+ // If this returns true, all relevant property generators were applied to the item instance
+ // passed in. If this returns false, some or none of the generators may have been applied,
+ // but there are no guarantees about the item state.
+ MUST_CHECK_RETURN bool BApplyPropertyGenerators( CEconItem *pItem ) const;
+
+ const CUtlVector<econ_tag_handle_t>& GetEconTags() const { return m_vecTags; } // meant for internal/debug use only, not for runtime iteration
+ const CUtlVector<econ_item_payment_rule_t>& GetPaymentRules() const { return m_vecPaymentRules; }
+
+private:
+ int AddPaymentRule( const econ_item_payment_rule_t& newRule ); // returns which payment rule number was just created
+public:
+#endif // GC_DLL
+
+#if defined(CLIENT_DLL) || defined(GAME_DLL)
+ int GetStyleSkin( style_index_t unStyle, int iTeam, bool bViewmodel ) const;
+ const char* GetStyleInventoryImage( style_index_t unStyle ) const;
+ int GetBestVisualTeamData( int iTeam ) const;
+#endif // defined(CLIENT_DLL) || defined(GAME_DLL)
+
+
+#ifdef DBGFLAG_VALIDATE
+ void Validate( CValidator &validator, const char *pchName )
+ {
+ VALIDATE_SCOPE();
+ ValidateObj( m_vecStaticAttributes );
+ ValidatePtr( m_pKVItem );
+ ValidatePtr( m_pProxyCriteria );
+ }
+#endif // DBGFLAG_VALIDATE
+
+
+private:
+ // Pointer to the raw KeyValue definition of the item
+ KeyValues * m_pKVItem;
+
+ // Required values from m_pKVItem:
+
+ // The number used to refer to this definition in the DB
+ item_definition_index_t m_nDefIndex;
+
+ // False if this definition has been turned off and we're not using it to generate items
+ bool m_bEnabled;
+
+ // These values specify the range of item levels that an item based off this definition can be generated within.
+ uint8 m_unMinItemLevel;
+ uint8 m_unMaxItemLevel;
+
+ // This specifies an item quality that items from this definition must be set to. Used mostly to specify unique item definitions.
+ uint8 m_nItemQuality;
+ uint8 m_nForcedItemQuality;
+ uint8 m_nItemRarity;
+
+ // Default drop quantity
+ uint16 m_nDefaultDropQuantity;
+
+ // Item Series
+ uint8 m_unItemSeries;
+
+ // Static attributes (ones that are always on these items)
+ CUtlVector<static_attrib_t> m_vecStaticAttributes;
+
+ // Seeds the popular item list with this number of the item when the list is reset.
+ uint8 m_nPopularitySeed;
+
+ // ---------------------------------------------
+ // Display related data
+ // ---------------------------------------------
+ // The base name of this item. i.e. "The Kritz-Krieg".
+ const char *m_pszItemBaseName;
+ bool m_bProperName; // If set, the name will have "The" prepended to it, unless it's got a non-unique quality
+ // in which case it'll have "A" prepended to the quality. i.e. A Community Kritzkrieg
+
+ // The base type of this item. i.e. "Rocket Launcher" or "Shotgun".
+ // This is often the same as the base name, but not always.
+ const char *m_pszItemTypeName;
+
+ // The item's non-attribute description.
+ const char *m_pszItemDesc;
+
+ // expiration time
+ RTime32 m_rtExpiration;
+
+ // The .mdl file used for this item when it's displayed in inventory-style boxes.
+ const char *m_pszInventoryModel;
+ // Alternatively, the image used for this item when it's displayed in inventory-style boxes. If specified, it's used over the model.
+ const char *m_pszInventoryImage;
+ // An optional image that's overlayed over the top of the base inventory image. It'll be RGB colored by the tint color of the item.
+ CUtlVector<const char*> m_pszInventoryOverlayImages;
+ int m_iInventoryImagePosition[2];
+ int m_iInventoryImageSize[2];
+ int m_iInspectPanelDistance;
+
+ const char *m_pszBaseDisplayModel;
+ int m_iDefaultSkin;
+ bool m_bLoadOnDemand;
+ bool m_bHasBeenLoaded;
+
+ bool m_bHideBodyGroupsDeployedOnly;
+
+ // The .mdl file used for the world view.
+ // This is inferior to using a c_model, but because the geometry of the sticky bomb launcher's
+ // world model is significantly different from the view model the demoman pack requires
+ // using two separate models for now.
+ const char *m_pszWorldDisplayModel;
+ const char *m_pszWorldExtraWearableModel; // Some weapons attach an extra wearable item to the player
+ const char *m_pszWorldExtraWearableViewModel; // Some weapons attach an extra wearable view model item to the player
+ const char *m_pszVisionFilteredDisplayModel; // Some weapons display differently depending on the viewer's filters
+
+ const char *m_pszCollectionReference; // Reference a colletion
+
+ // If set, we use the base hands model for a viewmodel, and bonemerge the above player model
+ bool m_bAttachToHands;
+ bool m_bAttachToHandsVMOnly;
+
+ // If set, we will force the view model to render flipped. Good for models built left handed.
+ bool m_bFlipViewModel;
+
+ // This is a wearable that sits in a non-wearable loadout slot
+ bool m_bActAsWearable;
+
+ // This is a weapon that sits in a wearable slot (Action)
+ bool m_bActAsWeapon;
+
+ // Is this Item a tool
+ bool m_bIsTool;
+
+ // The set this item is a member of
+ const CEconItemSetDefinition *m_pItemSetDef;
+ const CEconItemCollectionDefinition *m_pItemCollectionDef;
+
+ CEconItemPaintKitDefinition *m_pItemPaintKitDef;
+
+ // A list of per-team visual data used to modify base model for visual recognition
+ perteamvisuals_t *m_PerTeamVisuals[TEAM_VISUAL_SECTIONS];
+
+ // Optional override for specifying a custom shell ejection model
+ const char *m_pszBrassModelOverride;
+
+ IEconTool *m_pTool;
+ bundleinfo_t *m_BundleInfo;
+ item_capabilities_t m_iCapabilities;
+
+#ifdef TF_CLIENT_DLL
+ uint32 m_unNumConcreteItems; // This is the number of items that will actually end up in a user's inventory - this can be 0 for some items (e.g. map stamps in TF), 1 for a "regular" item, or many for bundles, etc.
+#endif // TF_CLIENT_DLL
+
+ CUtlDict< CUtlString >* m_pDictIcons;
+
+ // ---------------------------------------------
+ // Creation related data
+ // ---------------------------------------------
+ // The entity classname for this item.
+ const char *m_pszItemClassname;
+
+ // The entity name that will be displayed in log files.
+ const char *m_pszItemLogClassname;
+
+ // The name of the icon used in the death notices.
+ const char *m_pszItemIconClassname;
+
+ // This is the script file name of this definition. Used to generate items by script name.
+ const char *m_pszDefinitionName;
+
+ // This is used for auditing purposes
+ const char *m_pszDatabaseAuditTable;
+
+ bool m_bHidden;
+ bool m_bShouldShowInArmory;
+ bool m_bBaseItem;
+ bool m_bImported;
+
+ // A pack bundle is a bundle that contains items that are not for sale individually
+ bool m_bIsPackBundle;
+
+ // A pack item is an item which is not for sale individually and is only for sale as part of a pack bundle. A 'regular' bundle can only include a pack bundle by explicitly including all of the pack bundle's items individually.
+ // If this pointer is non-NULL, this item is considered to be a pack item (see CEconItemDefinition::IsPackItem()).
+ CEconItemDefinition *m_pOwningPackBundle;
+ bool m_bIsPackItem;
+
+ // Contains information on how to describe items with this attribute in the Armory
+ const char *m_pszArmoryDesc;
+
+ // Temporary(?) solution to allow xifiers to work on botkiller and festive variants of weapons
+ const char *m_pszXifierRemapClass;
+
+ // Base item name -- used for grouping weapon functionality
+ const char *m_pszBaseFunctionalItemName;
+
+ // For particle effects that have derivatives, what is the suffix for this item
+ const char *m_pszParticleSuffix;
+
+ // ---------------------------------------------
+ // Remapping data for armory/store
+ // ---------------------------------------------
+ int m_iArmoryRemap;
+ int m_iStoreRemap;
+ const char *m_pszArmoryRemap;
+ const char *m_pszStoreRemap;
+
+ // ---------------------------------------------
+ // Crafting related data
+ // ---------------------------------------------
+ const char *m_pszClassToken;
+ const char *m_pszSlotToken;
+
+ // ---------------------------------------------
+ // Gameplay related data
+ // ---------------------------------------------
+ // How to behave when the player wearing the item dies.
+ int m_iDropType;
+
+ // Holiday restriction. Item only has an appearance when the holiday is in effect.
+ const char *m_pszHolidayRestriction;
+
+ // Meet the pyro makes some items invisible unless you're wearing Pyro Goggles
+ int m_nVisionFilterFlags;
+
+ // Temporary. Revisit this in the engineer update. Enables an additional buildable.
+ int m_iSubType;
+
+ // Whitelist support for tournament mode
+ bool m_bAllowedInThisMatch;
+
+ equip_region_mask_t m_unEquipRegionMask; // which equip regions does this item cover directly
+ equip_region_mask_t m_unEquipRegionConflictMask; // which equip regions does equipping this item prevent from having something in them
+
+ item_definition_index_t m_unSetItemRemapDefIndex; // reference to the definition index we want to consider this item for set matching purposes; see GetSetItemRemap()
+
+#ifdef GC_DLL
+ CUtlVector<const IEconItemPropertyGenerator *> m_vecPropertyGenerators;
+ CUtlVector<econ_item_payment_rule_t> m_vecPaymentRules;
+#endif // GC_DLL
+
+ // False if this definition is not allowed to be part of a shuffled crate's contents
+ bool m_bValidForShuffle;
+
+ // False if this definition should not grant self-made items
+ bool m_bValidForSelfMade;
+
+protected:
+ // Protected to allow subclasses to add/remove game-specific tags.
+ CUtlVector<econ_tag_handle_t> m_vecTags;
+ CUtlVector<const CEconItemDefinition *> m_vecContainingBundleItemDefs; // Item definition indices for any bundles which contain this item
+ CUtlVector<uint32> m_vecSteamWorkshopContributors;
+
+ friend class CEconItemSchema;
+};
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+inline style_index_t CEconItemDefinition::GetNumStyles() const
+{
+ const perteamvisuals_t *pVisData = GetPerTeamVisual( 0 );
+
+ if ( !pVisData )
+ return 0;
+
+ // Bad things will happen if we ever get more styles than will fit in our
+ // style index type. Not Very Bad things, but bad things. Mostly we'll fail
+ // to iterate over all our styles.
+ return pVisData->m_Styles.Count();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Similar to GetNumStyles, but only the selectable styles
+//-----------------------------------------------------------------------------
+inline style_index_t CEconItemDefinition::GetNumSelectableStyles() const
+{
+ const perteamvisuals_t *pVisData = GetPerTeamVisual(0);
+
+ if (!pVisData)
+ return 0;
+
+ style_index_t nCount = 0;
+ FOR_EACH_VEC( pVisData->m_Styles, i )
+ {
+ if( pVisData->m_Styles[i]->IsSelectable() )
+ {
+ ++nCount;
+ }
+ }
+
+ return nCount;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+inline const CEconStyleInfo *CEconItemDefinition::GetStyleInfo( style_index_t unStyle ) const
+{
+ const perteamvisuals_t *pBaseVisuals = GetPerTeamVisual( 0 );
+ if ( !pBaseVisuals || !pBaseVisuals->m_Styles.IsValidIndex( unStyle ) )
+ return NULL;
+
+ return pBaseVisuals->m_Styles[unStyle];
+}
+
+#if defined(CLIENT_DLL) || defined(GAME_DLL)
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+inline int CEconItemDefinition::GetNumAttachedModels( int iTeam ) const
+{
+#ifndef CSTRIKE_DLL
+ iTeam = GetBestVisualTeamData( iTeam );
+ if ( iTeam < 0 || iTeam >= TEAM_VISUAL_SECTIONS || !GetPerTeamVisual(iTeam) )
+ return 0;
+ return GetPerTeamVisual(iTeam)->m_AttachedModels.Count();
+#else
+ return 0;
+#endif
+}
+//-----------------------------------------------------------------------------
+inline attachedmodel_t *CEconItemDefinition::GetAttachedModelData( int iTeam, int iIdx ) const
+{
+#ifndef CSTRIKE_DLL
+ iTeam = GetBestVisualTeamData( iTeam );
+ perteamvisuals_t *pVisuals = GetPerTeamVisual(iTeam);
+ Assert( pVisuals );
+ if ( iTeam < 0 || iTeam >= TEAM_VISUAL_SECTIONS || !pVisuals )
+ return NULL;
+
+ Assert( iIdx < pVisuals->m_AttachedModels.Count() );
+ if ( iIdx >= pVisuals->m_AttachedModels.Count() )
+ return NULL;
+
+ return &pVisuals->m_AttachedModels[iIdx];
+#else
+ return NULL;
+#endif
+}
+//-----------------------------------------------------------------------------
+inline int CEconItemDefinition::GetNumAttachedModelsFestivized( int iTeam ) const
+{
+#ifndef CSTRIKE_DLL
+ iTeam = GetBestVisualTeamData( iTeam );
+ perteamvisuals_t *pVisuals = GetPerTeamVisual(iTeam);
+ if ( iTeam < 0 || iTeam >= TEAM_VISUAL_SECTIONS || !pVisuals )
+ return 0;
+ return pVisuals->m_AttachedModelsFestive.Count();
+#else
+ return 0;
+#endif
+}
+//-----------------------------------------------------------------------------
+inline attachedmodel_t *CEconItemDefinition::GetAttachedModelDataFestivized( int iTeam, int iIdx ) const
+{
+#ifndef CSTRIKE_DLL
+ iTeam = GetBestVisualTeamData( iTeam );
+ perteamvisuals_t *pVisuals = GetPerTeamVisual(iTeam);
+ Assert( pVisuals );
+ if ( iTeam < 0 || iTeam >= TEAM_VISUAL_SECTIONS || !pVisuals )
+ return NULL;
+
+ Assert( iIdx < pVisuals->m_AttachedModelsFestive.Count() );
+ if ( iIdx >= pVisuals->m_AttachedModelsFestive.Count() )
+ return NULL;
+
+ return &pVisuals->m_AttachedModelsFestive[iIdx];
+#else
+ return NULL;
+#endif
+}
+//-----------------------------------------------------------------------------
+inline int CEconItemDefinition::GetNumPlaybackActivities( int iTeam ) const
+{
+#ifndef CSTRIKE_DLL
+ iTeam = GetBestVisualTeamData( iTeam );
+ if ( iTeam < 0 || iTeam >= TEAM_VISUAL_SECTIONS || !GetPerTeamVisual(iTeam) )
+ return 0;
+ return GetPerTeamVisual(iTeam)->m_Activities.Count();
+#else
+ return 0;
+#endif
+}
+
+inline activity_on_wearable_t *CEconItemDefinition::GetPlaybackActivityData( int iTeam, int iIdx ) const
+{
+#ifndef CSTRIKE_DLL
+ iTeam = GetBestVisualTeamData( iTeam );
+ Assert( GetPerTeamVisual(iTeam) );
+ if ( iTeam < 0 || iTeam >= TEAM_VISUAL_SECTIONS || !GetPerTeamVisual(iTeam) )
+ return NULL;
+
+ Assert( iIdx < GetPerTeamVisual(iTeam)->m_Activities.Count() );
+ if ( iIdx >= GetPerTeamVisual(iTeam)->m_Activities.Count() )
+ return NULL;
+
+ return &GetPerTeamVisual(iTeam)->m_Activities[iIdx];
+#else
+ return NULL;
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+inline int CEconItemDefinition::GetNumAnimations( int iTeam ) const
+{
+#ifndef CSTRIKE_DLL
+ iTeam = GetBestVisualTeamData( iTeam );
+ if ( iTeam < 0 || iTeam >= TEAM_VISUAL_SECTIONS || !GetPerTeamVisual(iTeam) )
+ return 0;
+ return GetPerTeamVisual(iTeam)->m_Animations.Count();
+#else
+ return 0;
+#endif
+}
+inline animation_on_wearable_t *CEconItemDefinition::GetAnimationData( int iTeam, int iIdx ) const
+{
+#ifndef CSTRIKE_DLL
+ iTeam = GetBestVisualTeamData( iTeam );
+ Assert( GetPerTeamVisual(iTeam) );
+ if ( iTeam < 0 || iTeam >= TEAM_VISUAL_SECTIONS || !GetPerTeamVisual(iTeam) )
+ return NULL;
+
+ Assert( iIdx < GetPerTeamVisual(iTeam)->m_Animations.Count() );
+ if ( iIdx >= GetPerTeamVisual(iTeam)->m_Animations.Count() )
+ return NULL;
+
+ return &GetPerTeamVisual(iTeam)->m_Animations[iIdx];
+#else
+ return NULL;
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+inline int CEconItemDefinition::GetNumAttachedParticles( int iTeam ) const
+{
+#ifndef CSTRIKE_DLL
+ iTeam = GetBestVisualTeamData( iTeam );
+ if ( iTeam < 0 || iTeam >= TEAM_VISUAL_SECTIONS || !GetPerTeamVisual(iTeam) )
+ return 0;
+ return GetPerTeamVisual(iTeam)->m_AttachedParticles.Count();
+#else
+ return 0;
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+inline attachedparticlesystem_t *CEconItemDefinition::GetAttachedParticleData( int iTeam, int iIdx ) const
+{
+#ifndef CSTRIKE_DLL
+ iTeam = GetBestVisualTeamData( iTeam );
+ Assert( GetPerTeamVisual(iTeam) );
+ if ( iTeam < 0 || iTeam >= TEAM_VISUAL_SECTIONS || !GetPerTeamVisual(iTeam) )
+ return NULL;
+
+ Assert( iIdx < GetPerTeamVisual(iTeam)->m_AttachedParticles.Count() );
+ if ( iIdx >= GetPerTeamVisual(iTeam)->m_AttachedParticles.Count() )
+ return NULL;
+
+ return &GetPerTeamVisual(iTeam)->m_AttachedParticles[iIdx];
+#else
+ return NULL;
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+inline const char *CEconItemDefinition::GetMaterialOverride( int iTeam ) const
+{
+#ifndef CSTRIKE_DLL
+ iTeam = GetBestVisualTeamData( iTeam );
+ if ( iTeam < 0 || iTeam >= TEAM_VISUAL_SECTIONS || !GetPerTeamVisual(iTeam) )
+ return NULL;
+ return GetPerTeamVisual(iTeam)->pszMaterialOverride;
+#else
+ return NULL;
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+inline const char *CEconItemDefinition::GetMuzzleFlash( int iTeam ) const
+{
+#ifndef CSTRIKE_DLL
+ iTeam = GetBestVisualTeamData( iTeam );
+ if ( iTeam < 0 || iTeam >= TEAM_VISUAL_SECTIONS || !GetPerTeamVisual(iTeam) )
+ return NULL;
+ return GetPerTeamVisual(iTeam)->pszMuzzleFlash;
+#else
+ return NULL;
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+inline const char *CEconItemDefinition::GetTracerEffect( int iTeam ) const
+{
+#ifndef CSTRIKE_DLL
+ iTeam = GetBestVisualTeamData( iTeam );
+ if ( iTeam < 0 || iTeam >= TEAM_VISUAL_SECTIONS || !GetPerTeamVisual(iTeam) )
+ return NULL;
+ return GetPerTeamVisual(iTeam)->pszTracerEffect;
+#else
+ return NULL;
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+inline const char *CEconItemDefinition::GetParticleEffect( int iTeam ) const
+{
+#ifndef CSTRIKE_DLL
+ iTeam = GetBestVisualTeamData( iTeam );
+ if ( iTeam < 0 || iTeam >= TEAM_VISUAL_SECTIONS || !GetPerTeamVisual(iTeam) )
+ return NULL;
+ return GetPerTeamVisual(iTeam)->pszParticleEffect;
+#else
+ return NULL;
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+inline int CEconItemDefinition::GetHiddenParentBodygroup( int iTeam ) const
+{
+#ifndef CSTRIKE_DLL
+ iTeam = GetBestVisualTeamData( iTeam );
+ if ( iTeam < 0 || iTeam >= TEAM_VISUAL_SECTIONS || !GetPerTeamVisual(iTeam) )
+ return -1;
+ return GetPerTeamVisual(iTeam)->iHideParentBodyGroup;
+#else
+ return -1;
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+inline int CEconItemDefinition::GetNumModifiedBodyGroups( int iTeam ) const
+{
+#ifndef CSTRIKE_DLL
+ iTeam = GetBestVisualTeamData( iTeam );
+ if ( iTeam < 0 || iTeam >= TEAM_VISUAL_SECTIONS || !GetPerTeamVisual(iTeam) )
+ return -1;
+ return GetPerTeamVisual(iTeam)->m_Maps.m_ModifiedBodyGroupNames.Count();
+#else
+ return -1;
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+inline const char* CEconItemDefinition::GetModifiedBodyGroup( int iTeam, int i, int& body ) const
+{
+#ifndef CSTRIKE_DLL
+ iTeam = GetBestVisualTeamData( iTeam );
+ if ( iTeam < 0 || iTeam >= TEAM_VISUAL_SECTIONS || !GetPerTeamVisual(iTeam) )
+ return NULL;
+ body = GetPerTeamVisual(iTeam)->m_Maps.m_ModifiedBodyGroupNames[i];
+ return GetPerTeamVisual(iTeam)->m_Maps.m_ModifiedBodyGroupNames.Key(i);
+#else
+ return NULL;
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+inline int CEconItemDefinition::GetNumCodeControlledBodyGroups( int iTeam ) const
+{
+#ifndef CSTRIKE_DLL
+ iTeam = GetBestVisualTeamData( iTeam );
+ if ( iTeam < 0 || iTeam >= TEAM_VISUAL_SECTIONS || !GetPerTeamVisual(iTeam) )
+ return -1;
+ return GetPerTeamVisual(iTeam)->m_Maps.m_CodeControlledBodyGroupNames.Count();
+#else
+ return -1;
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+inline const char* CEconItemDefinition::GetCodeControlledBodyGroup( int iTeam, int i, codecontrolledbodygroupdata_t &ccbgd ) const
+{
+#ifndef CSTRIKE_DLL
+ iTeam = GetBestVisualTeamData( iTeam );
+ if ( iTeam < 0 || iTeam >= TEAM_VISUAL_SECTIONS || !GetPerTeamVisual(iTeam) )
+ return NULL;
+ ccbgd = GetPerTeamVisual(iTeam)->m_Maps.m_CodeControlledBodyGroupNames[i];
+ return GetPerTeamVisual(iTeam)->m_Maps.m_CodeControlledBodyGroupNames.Key(i);
+#else
+ return NULL;
+#endif
+}
+
+#if defined(CLIENT_DLL) || defined(GAME_DLL)
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+inline int CEconItemDefinition::GetStyleSkin( style_index_t unStyle, int iTeam, bool bViewmodel ) const
+{
+ const CEconStyleInfo *pStyle = GetStyleInfo( unStyle );
+
+ // Return our skin if we have a style or our default skin of -1 otherwise.
+ return pStyle
+ ? pStyle->GetSkin( iTeam, bViewmodel )
+ : GetDefaultSkin();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+inline const char* CEconItemDefinition::GetStyleInventoryImage( style_index_t unStyle ) const
+{
+ const CEconStyleInfo *pStyle = GetStyleInfo( unStyle );
+
+ return pStyle ? pStyle->GetInventoryImage() : NULL;
+}
+
+#endif // defined(CLIENT_DLL) || defined(GAME_DLL)
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+inline int CEconItemDefinition::GetViewmodelBodygroupOverride( int iTeam ) const
+{
+#ifndef CSTRIKE_DLL
+ iTeam = GetBestVisualTeamData( iTeam );
+ if ( iTeam < 0 || iTeam >= TEAM_VISUAL_SECTIONS || !GetPerTeamVisual(iTeam) )
+ return 0;
+ return GetPerTeamVisual(iTeam)->m_iViewModelBodyGroupOverride;
+#else
+ return 0;
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+inline int CEconItemDefinition::GetViewmodelBodygroupStateOverride( int iTeam ) const
+{
+#ifndef CSTRIKE_DLL
+ iTeam = GetBestVisualTeamData( iTeam );
+ if ( iTeam < 0 || iTeam >= TEAM_VISUAL_SECTIONS || !GetPerTeamVisual(iTeam) )
+ return 0;
+ return GetPerTeamVisual(iTeam)->m_iViewModelBodyGroupStateOverride;
+#else
+ return 0;
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+inline int CEconItemDefinition::GetWorldmodelBodygroupOverride( int iTeam ) const
+{
+#ifndef CSTRIKE_DLL
+ iTeam = GetBestVisualTeamData( iTeam );
+ if ( iTeam < 0 || iTeam >= TEAM_VISUAL_SECTIONS || !GetPerTeamVisual(iTeam) )
+ return 0;
+ return GetPerTeamVisual(iTeam)->m_iWorldModelBodyGroupOverride;
+#else
+ return 0;
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+inline int CEconItemDefinition::GetWorldmodelBodygroupStateOverride( int iTeam ) const
+{
+#ifndef CSTRIKE_DLL
+ iTeam = GetBestVisualTeamData( iTeam );
+ if ( iTeam < 0 || iTeam >= TEAM_VISUAL_SECTIONS || !GetPerTeamVisual(iTeam) )
+ return 0;
+ return GetPerTeamVisual(iTeam)->m_iWorldModelBodyGroupStateOverride;
+#else
+ return 0;
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+inline bool CEconItemDefinition::UsesPerClassBodygroups( int iTeam ) const
+{
+#ifndef CSTRIKE_DLL
+ iTeam = GetBestVisualTeamData( iTeam );
+ if ( iTeam < 0 || iTeam >= TEAM_VISUAL_SECTIONS || !GetPerTeamVisual(iTeam) )
+ return false;
+ return GetPerTeamVisual(iTeam)->bUsePerClassBodygroups;
+#else
+ return false;
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+inline const char *CEconItemDefinition::GetCustomSound( int iTeam, int iSound ) const
+{
+#ifndef CSTRIKE_DLL
+ iTeam = GetBestVisualTeamData( iTeam );
+ if ( iTeam < 0 || iTeam >= TEAM_VISUAL_SECTIONS || !GetPerTeamVisual(iTeam) )
+ return NULL;
+ if ( iSound < 0 || iSound >= MAX_VISUALS_CUSTOM_SOUNDS )
+ return NULL;
+ return GetPerTeamVisual(iTeam)->pszCustomSounds[iSound];
+#else
+ return NULL;
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+inline const char *CEconItemDefinition::GetWeaponReplacementSound( int iTeam, /* WeaponSound_t */ int iSound ) const
+{
+#ifndef CSTRIKE_DLL
+ iTeam = GetBestVisualTeamData( iTeam );
+ if ( iTeam < 0 || iTeam >= TEAM_VISUAL_SECTIONS || !GetPerTeamVisual(iTeam) )
+ return NULL;
+ if ( iSound < 0 || iSound >= NUM_SHOOT_SOUND_TYPES )
+ return NULL;
+ return GetPerTeamVisual(iTeam)->pszWeaponSoundReplacements[iSound];
+#else
+ return NULL;
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+inline int CEconItemDefinition::GetBestVisualTeamData( int iTeam ) const
+{
+#ifndef CSTRIKE_DLL
+ Assert( iTeam >= 0 && iTeam < TEAM_VISUAL_SECTIONS );
+ // If we don't have data for the specified team, try to fall back to the base
+ // if ( !GetStaticData() )
+ // return 0;
+ if ( (iTeam < 0 || iTeam >= TEAM_VISUAL_SECTIONS) || (iTeam > 0 && !GetPerTeamVisual(iTeam)) )
+ return 0;
+ return iTeam;
+#else
+ return 0;
+#endif
+}
+#endif // defined(CLIENT_DLL) || defined(GAME_DLL)
+
+//-----------------------------------------------------------------------------
+// CTimedItemRewardDefinition
+// Describes a periodic item reward
+//-----------------------------------------------------------------------------
+class CTimedItemRewardDefinition
+{
+public:
+ CTimedItemRewardDefinition( void );
+ CTimedItemRewardDefinition( const CTimedItemRewardDefinition &that );
+ CTimedItemRewardDefinition &operator=( const CTimedItemRewardDefinition& rhs );
+
+ ~CTimedItemRewardDefinition( void ) { }
+
+ bool BInitFromKV( KeyValues *pKVTimedReward, CUtlVector<CUtlString> *pVecErrors = NULL );
+
+ uint32 GetRandomFrequency( void ) const { return RandomFloat( m_unMinFreq, m_unMaxFreq ); }
+ uint32 GetMinFrequency( void ) const { return m_unMinFreq; }
+ uint32 GetMaxFrequency( void ) const { return m_unMaxFreq; }
+ float GetChance( void ) const { return m_flChance; }
+ const CItemSelectionCriteria &GetCriteria( void ) const { return m_criteria; }
+ const CEconLootListDefinition *GetLootList( void ) const { return m_pLootList; }
+
+ bool BHasRequiredItem() const { return m_iRequiredItemDef != INVALID_ITEM_DEF_INDEX; }
+ item_definition_index_t GetRequiredItem() const { return m_iRequiredItemDef; }
+
+#ifdef DBGFLAG_VALIDATE
+ void Validate( CValidator &validator, const char *pchName )
+ {
+ VALIDATE_SCOPE();
+ ValidateObj( m_criteria );
+ }
+#endif // DBGFLAG_VALIDATE
+
+private:
+ // Frequency of how often the item is awarded
+ uint32 m_unMinFreq;
+ uint32 m_unMaxFreq;
+
+ // The chance, between 0 and 1, that the item is rewarded
+ float m_flChance;
+
+ // The criteria to use to select the item to reward
+ CItemSelectionCriteria m_criteria;
+ // Alternatively, the loot_list to use instead
+ const CEconLootListDefinition *m_pLootList;
+
+ item_definition_index_t m_iRequiredItemDef;
+};
+
+#ifdef GC_DLL
+//-----------------------------------------------------------------------------
+// CExperimentDefinition
+//-----------------------------------------------------------------------------
+struct experiment_group_t
+{
+ const char* m_pName;
+ uint32 m_unNumParticipants;
+ uint32 m_unMaxParticipants;
+ KeyValues *m_pKeyValues;
+};
+
+class CExperimentDefinition
+{
+public:
+ CExperimentDefinition( void );
+ ~CExperimentDefinition( void );
+
+ bool BInitFromKV( KeyValues *pKVExperiment, CUtlVector<CUtlString> *pVecErrors = NULL );
+
+ CUtlVector< experiment_group_t > &GetGroups() { return m_vecGroups; }
+
+ uint32 GetID() const { return m_unExperimentID; }
+ const char *GetName( void ) const { return m_pKeyValues->GetString( "name", "unknown" ); }
+ const char *GetDescription( void ) const { return m_pKeyValues->GetString( "description", "unknown" ); }
+
+ bool IsEnabled() const { return m_bEnabled; }
+ bool IsFull() const { return m_unNumParticipants >= m_unMaxParticipants; }
+
+ uint32 GetNumParticipants() const { return m_unNumParticipants; }
+ void SetNumParticipants( uint32 unNumParticipants ) { m_unNumParticipants = unNumParticipants; }
+ uint32 GetMaxParticipants() const { return m_unMaxParticipants; }
+
+ bool ChooseGroup( uint32 &unGroup );
+
+private:
+ bool m_bEnabled;
+ uint32 m_unExperimentID;
+ uint32 m_unNumParticipants;
+ uint32 m_unMaxParticipants;
+ KeyValues *m_pKeyValues;
+ CUtlVector< experiment_group_t > m_vecGroups;
+};
+#endif
+
+//-----------------------------------------------------------------------------
+// CItemLevelingDefinition
+//-----------------------------------------------------------------------------
+class CItemLevelingDefinition
+{
+public:
+ CItemLevelingDefinition( void );
+ CItemLevelingDefinition( const CItemLevelingDefinition &that );
+ CItemLevelingDefinition &operator=( const CItemLevelingDefinition& rhs );
+
+ ~CItemLevelingDefinition( void );
+
+ bool BInitFromKV( KeyValues *pKVItemLevel, const char *pszLevelBlockName, CUtlVector<CUtlString> *pVecErrors = NULL );
+
+ uint32 GetLevel( void ) const { return m_unLevel; }
+ uint32 GetRequiredScore( void ) const { return m_unRequiredScore; }
+ const char *GetNameLocalizationKey( void ) const { return m_pszLocalizedName_LocalStorage; }
+
+private:
+ uint32 m_unLevel;
+ uint32 m_unRequiredScore;
+ char *m_pszLocalizedName_LocalStorage;
+};
+
+//-----------------------------------------------------------------------------
+// AchievementAward_t
+// Holds the item to give away and the Data value to audit it with ( for cross
+// game achievements)
+//-----------------------------------------------------------------------------
+struct AchievementAward_t
+{
+ AchievementAward_t( const AchievementAward_t & rhs )
+ : m_sNativeName( rhs.m_sNativeName ),
+ m_unSourceAppId( rhs.m_unSourceAppId ),
+ m_unAuditData( rhs.m_unAuditData )
+ {
+ m_vecDefIndex.CopyArray( rhs.m_vecDefIndex.Base(), rhs.m_vecDefIndex.Count() );
+ }
+ AchievementAward_t( ) {}
+
+ CUtlString m_sNativeName;
+ AppId_t m_unSourceAppId;
+ uint32 m_unAuditData;
+ CUtlVector<uint16> m_vecDefIndex;
+};
+
+enum eTimedRewardType
+{
+ kTimedRewards_RegularDrop,
+ kTimedRewards_SupplyCrate,
+ kTimedRewards_FreeTrialDrop,
+ kTimedRewards_RecipeDrop,
+ kTimedRewards_EventDrop02,
+ kNumTimedRewards
+};
+
+struct kill_eater_score_type_t
+{
+ const char *m_pszTypeString;
+ const char *m_pszLevelBlockName;
+ bool m_bAllowBotVictims; // if true, we don't check for a valid Steam ID on the client before sending or a valid session on the GC before incrementing
+#ifdef GC_DLL
+ bool m_bGCUpdateOnly;
+ bool m_AllowIncrementValues; // if true, clients are allowed to send up the amount to increment by (ie., "did 100 damage") rather than implicitly assuming a value of 1
+ bool m_bIsBaseKillType; // if true, when clients send up a notification of this type we'll also look for other relevant things on the GC, like whether the victim was a friend, etc.
+#endif
+};
+
+// Index-to-string table, currently used for attribute value string lookups.
+struct schema_string_table_entry_t
+{
+ int m_iIndex;
+ const char *m_pszStr;
+};
+
+//-----------------------------------------------------------------------------
+// CForeignAppImports
+// Defines the way a single foreign app's items are mapped into this app
+//-----------------------------------------------------------------------------
+
+class CForeignAppImports
+{
+public:
+ CForeignAppImports() : m_mapDefinitions( DefLessFunc( uint16 ) ) {}
+
+ void AddMapping( uint16 unForeignDefIndex, const CEconItemDefinition *pDefn );
+ const CEconItemDefinition *FindMapping( uint16 unForeignDefIndex ) const;
+
+private:
+ CUtlMap< uint16, const CEconItemDefinition *> m_mapDefinitions;
+};
+
+//-----------------------------------------------------------------------------
+// ISchemaAttributeType
+//-----------------------------------------------------------------------------
+#ifdef GC_DLL
+namespace GCSDK
+{
+ class CColumnSet;
+ class CRecordBase;
+ class CWebAPIValues;
+};
+#endif // GC_DLL
+
+// ISchemaAttributeType is the base interface for a "type" of attribute, where "type" is defined as
+// "something that describes the memory layout, the DB layout, how to convert between them, etc.".
+// Most of the low-level work done with attributes, including DB reading/writing, packing/unpacking
+// for wire traffic, and other leaf code works exclusively through this interface.
+//
+// The class hierarchy looks like:
+//
+// ISchemaAttributeTypeBase< TAttribInMemoryType >:
+//
+// This describes a specific in-memory format for an attribute, without any association to
+// a particular DB, wire format, etc. We can't template the base class because it's an
+// interface. This implements about half of ISchemaAttributeType and has its own mini
+// interface consisting of ConvertTypedValueToByteStream() and ConvertByteStreamToTypedValue(),
+// both of which do work on statically-typed values that don't exist at higher levels.
+//
+// CSchemaAttributeTypeBase< TAttribSchType, TAttribInMemoryType >:
+//
+// This handles the schema-related functions on ISchemaAttributeType. These exist at a lower
+// inheritance level than ISchemaAttributeTypeBase to allow code that needs to work type-safely
+// on attributes in memory, but that doesn't know or need to know anything about databases,
+// to exist. Examples of this include code that calls CEconItem::SetDynamicAttributeValue<T>().
+//
+// Individual implementations of custom attribute type start making sense immediately as
+// subclasses of CSchemaAttributeTypeBase, for example CSchemaAttributeType_Default, which
+// implements all of the old, untyped attribute system logic.
+//
+// CSchemaAttributeTypeProtobufBase< TAttribSchType, TProtobufValueType >
+//
+// An easy way of automating most of the work for making a new attribute type is to have
+// the in-memory format be a protobuf object, allowing reflection, automatic network support,
+// etc.
+//
+// Creating a new custom protobuf attribute consists of three steps:
+//
+// - create a new DB table that will hold your attribute data. This needs an itemid_t-sized item ID
+// column named "ItemID", an attrib_definition_index_t-sized definition index column named "AttrDefIndex",
+// and then whatever data you want to store.
+//
+// - create a new protobuf message type that will hold your custom attribute contents. This exists
+// on the client and the GC in the same format.
+//
+// - implement a subclass of CSchemaAttributeTypeProtobufBase<>, for example:
+//
+// class CSchemaAttributeType_StrangeScore : public CSchemaAttributeTypeProtobufBase< GC_SCH_REFERENCE( CSchItemAttributeStrangeScore ) CAttribute_StrangeScore >
+// {
+// virtual void ConvertEconAttributeValueToSch( itemid_t unItemId, const CEconItemAttributeDefinition *pAttrDef, const union attribute_data_union_t& value, GCSDK::CRecordBase *out_pSchRecord ) const OVERRIDE;
+// virtual void LoadSchToEconAttributeValue( CEconItem *pTargetItem, const CEconItemAttributeDefinition *pAttrDef, const GCSDK::CRecordBase *pSchRecord ) const OVERRIDE;
+// };
+//
+// Implement these two GC-only functions to convert from the in-memory format to the DB format and
+// vice versa and you're good to go.
+//
+// - register the new type in CEconItemSchema::BInitAttributeTypes().
+//
+// If the attribute type can't be silently converted to an already-existing attribute value type, a few other
+// places will also fail to compile -- things like typed iteration, or compile-time type checking.
+//
+// Functions that start with "Convert" change the format of an attribute value (in a type-safe way wherever
+// possible), copying the value from one of the passed-in parameters to the other. Functions that start with
+// "Load" do a format conversion, but also add the post-conversion value to the passed-in CEconItem. This
+// comes up most often when generating new items, either from the DB (LoadSch), the network (LoadByteSteam),
+// or creation of a new item on the GC (LoadOrGenerate).
+class ISchemaAttributeType
+{
+public:
+ virtual ~ISchemaAttributeType() { }
+
+ // Returns a unique integer describing the C++-in-memory-layout type used by this attribute type.
+ // For example, something that stores "int" might return 0 and "CSomeFancyWideAttributeType" might
+ // return 1. The actual values don't matter and can even differ between different runs of the game/GC.
+ // The only important thing is that during a single run the value for a single type is consistent.
+ virtual unsigned int GetTypeUniqueIdentifier() const = 0;
+
+#ifdef GC_DLL
+ // What's the whole column set (and associated DB table) that this attribute uses? Meant to be
+ // implemented by subclasses that have DB type information.
+ virtual const GCSDK::CColumnSet& GetFullColumnSet() const = 0;
+
+ // Create an instance of a schema row. Mananging the memory is the responsibility of the caller.
+ // Meant to be implemented by subclasses that have DB type information.
+ virtual GCSDK::CRecordBase *CreateTypedSchRecord() const = 0;
+
+ // ...
+ virtual bool BAssetClassExportedAttributeValue( const CEconItemAttributeDefinition *pAttrDef, const attribute_data_union_t& value ) const { return true; }
+
+ // Prepare a DB row describing an instance of this attribute for writing.
+ virtual void ConvertEconAttributeValueToSch( itemid_t unItemId, const CEconItemAttributeDefinition *pAttrDef, const union attribute_data_union_t& value, GCSDK::CRecordBase *out_pSchRecord ) const = 0;
+
+ // We have a row read from the database and an item to add it as an attribute for. This
+ // does the opposite work of ConvertEconAttributeValueToSch() and also adds it to the CEconItem.
+ virtual void LoadSchToEconAttributeValue( CEconItem *pTargetItem, const CEconItemAttributeDefinition *pAttrDef, const GCSDK::CRecordBase *pSchRecord ) const = 0;
+
+ // Have this attribute type either copy the data straight out of the value union, or run the logic
+ // described by pszCustomLogicDesc to generate a new value. Either way, some correctly-typed data
+ // will wind up in an attribute on the target item. This is intended to call through to LoadEconAttributeValue()
+ // to do the actual assignment. This is only accessible on the GC.
+ virtual void LoadOrGenerateEconAttributeValue( CEconItem *pTargetItem, const CEconItemAttributeDefinition *pAttrDef, const static_attrib_t& staticAttrib, const CEconGameAccount *pGameAccount ) const = 0;
+
+ virtual void GenerateEconAttributeValue( const CEconItemAttributeDefinition *pAttrDef, const static_attrib_t& staticAttrib, const CEconGameAccount *pGameAccount, attribute_data_union_t* out_pValue ) const = 0;
+#endif // GC_DLL
+
+ // Have this attribute type copy the data out of the value union and type-copy it onto the item. This
+ // is accessible on clients as well as the GC.
+ virtual void LoadEconAttributeValue( CEconItem *pTargetItem, const CEconItemAttributeDefinition *pAttrDef, const union attribute_data_union_t& value ) const = 0;
+
+ // ...
+ virtual void ConvertEconAttributeValueToByteStream( const union attribute_data_union_t& value, std::string *out_psBytes ) const = 0;
+
+ // ...
+ virtual bool BConvertStringToEconAttributeValue( const CEconItemAttributeDefinition *pAttrDef, const char *pszValue, union attribute_data_union_t *out_pValue, bool bEnableTerribleBackwardsCompatibilitySchemaParsingCode = false ) const = 0;
+
+ // ...
+ virtual void ConvertEconAttributeValueToString( const CEconItemAttributeDefinition *pAttrDef, const attribute_data_union_t& value, std::string *out_ps ) const = 0;
+
+ // Used to deserialize a byte stream, probably from an on-wire protobuf message, instead an instance
+ // of the attribute in memory. See ConvertByteStreamToTypedValue() for example implementation, or
+ // ConvertTypedValueToByteStream() for an example of the byte-stream generator code.
+ virtual void LoadByteStreamToEconAttributeValue( CEconItem *pTargetItem, const CEconItemAttributeDefinition *pAttrDef, const std::string& sBytes ) const = 0;
+
+ // Give the subclass a chance to default-initialize a new value. For larger types, this may hit the
+ // heap. This must be called before otherwise manipulating [out_pValue] through Convert*() functions.
+ virtual void InitializeNewEconAttributeValue( attribute_data_union_t *out_pValue ) const = 0;
+
+ // Free any heap-allocated memory from this attribute value. Is not responsible for zeroing out
+ // pointers, etc.
+ virtual void UnloadEconAttributeValue( union attribute_data_union_t *out_pValue ) const = 0;
+
+ // ...
+ virtual bool OnIterateAttributeValue( class IEconItemAttributeIterator *pIterator, const CEconItemAttributeDefinition *pAttrDef, const attribute_data_union_t& value ) const = 0;
+
+ // This could also be called "BIsHackyMessyOldAttributeType()". This determines whether the attribute
+ // can be set at runtime on a CEconItemView instance, whether the gameserver can replicate the value to
+ // game clients, etc. It really only makes sense for value types 32 bits or smaller.
+ virtual bool BSupportsGameplayModificationAndNetworking() const { return false; }
+};
+
+//-----------------------------------------------------------------------------
+// CEconItemSchema
+// Defines the way econ items can be used in a game
+//-----------------------------------------------------------------------------
+typedef CUtlDict<CUtlConstString, int> ArmoryStringDict_t;
+typedef CUtlDict< CUtlVector<CItemLevelingDefinition> * > LevelBlockDict_t;
+typedef CUtlMap<unsigned int, kill_eater_score_type_t> KillEaterScoreMap_t;
+typedef CUtlDict< CUtlVector< schema_string_table_entry_t > * > SchemaStringTableDict_t;
+
+struct attr_type_t
+{
+ CUtlConstString m_sName;
+ const ISchemaAttributeType *m_pAttrType;
+
+ attr_type_t( const char *pszName, const ISchemaAttributeType *pAttrType )
+ : m_sName( pszName )
+ , m_pAttrType( pAttrType )
+ {
+ }
+};
+
+#if defined(CLIENT_DLL) || defined(GAME_DLL)
+class IDelayedSchemaData
+{
+public:
+ virtual ~IDelayedSchemaData() {}
+ virtual bool InitializeSchema( CEconItemSchema *pItemSchema ) = 0;
+
+protected:
+ // Passing '0' as the expected version means "we weren't expecting any version in particular" and will
+ // skip the sanity checking.
+ bool InitializeSchemaInternal( CEconItemSchema *pItemSchema, CUtlBuffer& bufRawData, bool bInitAsBinary, uint32 nExpectedVersion );
+};
+#endif // defined(CLIENT_DLL) || defined(GAME_DLL)
+
+class CEconStorePriceSheet;
+
+class CEconItemSchema
+{
+public:
+ CEconItemSchema( );
+
+private:
+ CEconItemSchema( const CEconItemSchema & rhs );
+ CEconItemSchema &operator=( CEconItemSchema & rhs );
+
+public:
+ virtual ~CEconItemSchema( void ) { Reset(); };
+
+ // Setup & parse in the item data files.
+ virtual bool BInit( const char *fileName, const char *pathID, CUtlVector<CUtlString> *pVecErrors = NULL );
+ bool BInitBinaryBuffer( CUtlBuffer &buffer, CUtlVector<CUtlString> *pVecErrors = NULL );
+ bool BInitTextBuffer( CUtlBuffer &buffer, CUtlVector<CUtlString> *pVecErrors = NULL );
+#ifdef GC_DLL
+ virtual bool DoPostPriceSheetLoadInit( CEconStorePriceSheet *pPriceSheet ); // Called once the price sheet's been loaded
+#endif
+
+ uint32 GetVersion() const { return m_unVersion; }
+ CSHA GetSchemaSHA() const { return m_schemaSHA; }
+ uint32 GetResetCount() const { return m_unResetCount; }
+
+ // Dump the schema for debug purposes
+ bool DumpItems ( const char *fileName, const char *pathID = NULL );
+
+ // Perform the computation used to calculate the schema version
+ static uint32 CalculateKeyValuesVersion( KeyValues *pKV );
+
+#if defined(CLIENT_DLL) || defined(GAME_DLL)
+ // This function will immediately reinitialize the schema if it's safe to do so, or store off the data
+ // if it isn't safe to update at the moment.
+ bool MaybeInitFromBuffer( IDelayedSchemaData *pDelayedSchemaData );
+
+ // If there is saved schema initialization data, initialize it now. If there is no saved data, this
+ // will return success.
+ bool BInitFromDelayedBuffer();
+#endif // defined(CLIENT_DLL) || defined(GAME_DLL)
+
+ // Accessors to the base properties
+ EEquipType_t GetEquipTypeFromClassIndex( int iClass ) const;
+ equipped_class_t GetAccountIndex() const { return m_unAccoutClassIndex; }
+ equipped_class_t GetFirstValidClass() const { return m_unFirstValidClass; }
+ equipped_class_t GetLastValidClass() const { return m_unLastValidClass; }
+ bool IsValidClass( equipped_class_t unClass ) { return ( unClass >= m_unFirstValidClass && unClass <= m_unLastValidClass ) || unClass == GetAccountIndex(); }
+ bool IsValidItemSlot( equipped_slot_t unSlot, equipped_class_t unClass ) const { return IsValidItemSlot( unSlot, unClass == m_unAccoutClassIndex ? EQUIP_TYPE_ACCOUNT : EQUIP_TYPE_CLASS ); }
+ bool IsValidItemSlot( equipped_slot_t unSlot, EEquipType_t eType ) const
+ {
+ return eType == EQUIP_TYPE_ACCOUNT ? unSlot >= m_unFirstValidAccountItemSlot && unSlot <= m_unLastValidAccountItemSlot
+ : unSlot >= m_unFirstValidClassItemSlot && unSlot <= m_unLastValidClassItemSlot;
+ }
+
+ enum { kMaxItemPresetCount = 4 };
+ uint32 GetNumAllowedItemPresets() const { return kMaxItemPresetCount; }
+ bool IsValidPreset( equipped_preset_t unPreset ) const { return unPreset <= GetNumAllowedItemPresets(); }
+
+ uint32 GetMinLevel() const { return m_unMinLevel; }
+ uint32 GetMaxLevel() const { return m_unMaxLevel; }
+
+ // Accessors to the underlying sections
+ typedef CUtlHashMapLarge<int, CEconItemDefinition*> ItemDefinitionMap_t;
+ const ItemDefinitionMap_t &GetItemDefinitionMap() const { return m_mapItems; }
+
+ typedef CUtlMap<int, CEconItemDefinition*, int> SortedItemDefinitionMap_t;
+ const SortedItemDefinitionMap_t &GetSortedItemDefinitionMap() const { return m_mapItemsSorted; }
+
+ typedef CUtlMap<int, CEconItemDefinition*, int> ToolsItemDefinitionMap_t;
+ const ToolsItemDefinitionMap_t &GetToolsItemDefinitionMap() const { return m_mapToolsItems; }
+
+ typedef CUtlMap<int, CEconItemDefinition*, int> BaseItemDefinitionMap_t;
+ const BaseItemDefinitionMap_t &GetBaseItemDefinitionMap() const { return m_mapBaseItems; }
+
+ typedef CUtlMap<const char*, CEconLootListDefinition *, int> LootListDefinitionMap_t;
+ const LootListDefinitionMap_t &GetLootLists() const { return m_mapLootLists; }
+
+ typedef CUtlMap<int, const char*> RevolvingLootListDefinitionMap_t;
+ const RevolvingLootListDefinitionMap_t &GetRevolvingLootLists() const { return m_mapRevolvingLootLists; }
+
+ typedef CUtlMap<const char*, int> BodygroupStateMap_t;
+ const BodygroupStateMap_t &GetDefaultBodygroupStateMap() const { return m_mapDefaultBodygroupState; }
+
+ typedef CUtlVector<CEconColorDefinition *> ColorDefinitionsList_t;
+
+ typedef CUtlMap<const char *, KeyValues *, int> PrefabMap_t;
+
+#ifdef GC_DLL
+ struct periodic_score_t
+ {
+ eEconPeriodicScoreEvents m_eEventType;
+ bool m_bGCUpdateOnly; // if set, only code that runs on the GC can initiate a change of this stat (ie., counting gifts -> true; bots killed -> false)
+ uint32 m_unTimePeriodLengthInSeconds;
+ CEconItemDefinition *m_pRewardItemDefinition;
+ };
+
+ typedef CUtlVector<periodic_score_t> PeriodicScoreTypeList_t;
+#endif // GC_DLL
+
+#if defined(CLIENT_DLL) || defined(GAME_DLL)
+ CEconItemDefinition *GetDefaultItemDefinition() { return m_pDefaultItemDefinition; }
+
+ bool SetupPreviewItemDefinition( KeyValues *pKV );
+#endif
+
+ const CUtlMap<int, CEconItemQualityDefinition, int > &GetQualityDefinitionMap() const { return m_mapQualities; }
+ const CUtlMap<int, CEconItemAttributeDefinition, int > &GetAttributeDefinitionMap() const { return m_mapAttributes; }
+
+ typedef CUtlMap<int, CEconCraftingRecipeDefinition*, int > RecipeDefinitionMap_t;
+ const RecipeDefinitionMap_t &GetRecipeDefinitionMap() const { return m_mapRecipes; }
+
+ typedef CUtlMap<const char*, CEconItemSetDefinition*, int > ItemSetMap_t;
+ const ItemSetMap_t &GetItemSets() const { return m_mapItemSets; }
+
+ typedef CUtlMap<const char*, CEconItemCollectionDefinition*, int > ItemCollectionMap_t;
+ const ItemCollectionMap_t &GetItemCollections() const { return m_mapItemCollections; }
+
+ typedef CUtlVector< int > ItemCollectionCrateMap_t;
+ const ItemCollectionCrateMap_t &GetItemCollectionCrates() const { return m_vecItemCollectionCrates; }
+
+ typedef CUtlMap<const char*, CEconItemPaintKitDefinition*, int > ItemPaintKitMap_t;
+ const ItemPaintKitMap_t &GetItemPaintKits() const { return m_mapItemPaintKits; }
+
+ typedef CUtlMap<const char*, CEconOperationDefinition*, int > OperationDefinitionMap_t;
+ const OperationDefinitionMap_t &GetOperationDefinitions() const { return m_mapOperationDefinitions; }
+
+
+#if defined(CLIENT_DLL) || defined(GAME_DLL)
+ const ArmoryStringDict_t &GetArmoryDataItemClasses() const { return m_dictArmoryItemClassesDataStrings; }
+ const ArmoryStringDict_t &GetArmoryDataItemTypes() const { return m_dictArmoryItemTypesDataStrings; }
+ const ArmoryStringDict_t &GetArmoryDataItems() const { return m_dictArmoryItemDataStrings; }
+ const ArmoryStringDict_t &GetArmoryDataAttributes() const { return m_dictArmoryAttributeDataStrings; }
+#elif defined(GC_DLL)
+ CUtlVector< CExperimentDefinition > &GetExperiments() { return m_vecExperiments; }
+
+ const CUtlVector< AppId_t > & GetForeignApps() const { return m_vecForeignApps; }
+ const CEconItemDefinition *GetAppItemImport( AppId_t unAppID, uint16 usDefIndex ) const;
+#endif
+
+ const CTimedItemRewardDefinition* GetTimedReward( eTimedRewardType type ) const;
+
+ const CEconLootListDefinition* GetLootListByName( const char* pListName, int *out_piIndex = NULL ) const;
+ const CEconLootListDefinition* GetLootListByIndex( int iIdx ) const { return m_mapLootLists.IsValidIndex(iIdx) ? m_mapLootLists[iIdx] : NULL; }
+
+ const CQuestObjectiveDefinition* GetQuestObjectiveByDefIndex( int iIdx ) const;
+ const CUtlMap<int, CQuestObjectiveDefinition*, int >& GetQuestObjectives() const { return m_mapQuestObjectives; }
+
+ uint8 GetDefaultQuality() const { return AE_UNIQUE; }
+
+ void AssignDefaultBodygroupState( const char *pszBodygroupName, int iValue );
+
+ equip_region_mask_t GetEquipRegionMaskByName( const char *pRegionName ) const;
+
+ struct EquipRegion
+ {
+ CUtlConstString m_sName;
+ unsigned int m_unBitIndex; // which bit are we claiming ownership over? there might be multiple equip regions with the same bit if we're in a "shared" block
+ equip_region_mask_t m_unMask; // full region conflict mask
+ };
+
+ typedef CUtlVector<EquipRegion> EquipRegionsList_t;
+ const EquipRegionsList_t& GetEquipRegionsList() const { return m_vecEquipRegionsList; }
+
+ equip_region_mask_t GetEquipRegionBitMaskByName( const char *pRegionName ) const;
+
+ KeyValues *FindDefinitionPrefabByName( const char *pszPrefabName ) const;
+ const PrefabMap_t& GetPrefabMap() const { return m_mapDefinitionPrefabs; }
+
+ CUtlVector< CEconItemDefinition * > &GetBundles() { return m_vecBundles; } // Retrieve a cached list of all bundles
+
+ const char *FindStringTableEntry( const char *pszTableName, int iIndex ) const;
+
+private:
+ void SetEquipRegionConflict( int iRegion, unsigned int unBit );
+ int GetEquipRegionIndexByName( const char *pRegionName ) const;
+
+public:
+ // Common lookup methods
+ bool BGetItemQualityFromName( const char *pchName, uint8 *nQuality ) const;
+ const CEconItemQualityDefinition *GetQualityDefinition( int nQuality ) const;
+ const CEconItemQualityDefinition *GetQualityDefinitionByName( const char *pszDefName ) const;
+
+ bool BGetItemRarityFromName( const char* pchName, uint8 *nRarity ) const;
+ const CEconItemRarityDefinition *GetRarityDefinitionByMapIndex( int nRarityIndex ) const;
+ const CEconItemRarityDefinition *GetRarityDefinition( int nRarity ) const;
+ const CEconItemRarityDefinition *GetRarityDefinitionByName( const char *pszDefName ) const;
+ virtual int GetRarityDefinitionCount( void ) const { return m_mapRarities.Count(); }
+ virtual const char* GetRarityName( uint8 iRarity );
+ virtual const char* GetRarityLocKey( uint8 iRarity );
+ virtual const char* GetRarityColor( uint8 iRarity );
+ virtual int GetRarityIndex( const char* pszRarity );
+
+ const CEconItemCollectionDefinition *GetCollectionByName( const char* pCollectionName );
+
+ virtual int GetItemSeriesDefinitionCount( void ) const { return m_mapItemSeries.Count(); }
+ bool BGetItemSeries( const char* pchName, uint8 *nItemSeries ) const;
+ const CEconItemSeriesDefinition *GetItemSeriesDefinition( int nRarity ) const;
+
+ CEconItemDefinition *GetItemDefinition( int iItemIndex );
+ const CEconItemDefinition *GetItemDefinition( int iItemIndex ) const;
+ CEconItemAttributeDefinition *GetAttributeDefinition( int iAttribIndex );
+ const CEconItemAttributeDefinition *GetAttributeDefinition( int iAttribIndex ) const;
+ CEconItemAttributeDefinition *GetAttributeDefinitionByName( const char *pszDefName );
+ const CEconItemAttributeDefinition *GetAttributeDefinitionByName( const char *pszDefName ) const;
+ CEconCraftingRecipeDefinition *GetRecipeDefinition( int iRecipeIndex );
+ CEconColorDefinition *GetColorDefinitionByName( const char *pszDefName );
+ const CEconColorDefinition *GetColorDefinitionByName( const char *pszDefName ) const;
+#ifdef CLIENT_DLL
+ const char *GetSteamPackageLocalizationToken( uint32 unPackageId ) const;
+#endif // CLIENT_DLL
+
+ bool BCanGSCreateItems( uint32 unIP ) const;
+#ifdef GC_DLL
+ const AchievementAward_t *GetAchievementReward( const char *pchAchievementName, AppId_t unAppID ) const;
+ const AchievementAward_t *GetAchievementRewardByData( uint32 unData ) const;
+#endif
+ const AchievementAward_t *GetAchievementRewardByDefIndex( uint16 usDefIndex ) const;
+ bool BHasAchievementRewards( void ) const { return (m_dictAchievementRewards.Count() > 0); }
+
+ static CUtlString ComputeAchievementName( AppId_t unAppID, const char *pchNativeAchievementName );
+
+ // Iterating over the item definitions. Game needs this to precache data.
+ CEconItemDefinition *GetItemDefinitionByName( const char *pszDefName );
+ const CEconItemDefinition *GetItemDefinitionByName( const char *pszDefName ) const;
+
+#ifdef GC_DLL
+ random_attrib_t *GetRandomAttributeTemplateByName( const char *pszAttrTemplateName ) const;
+#endif // GC_DLL
+
+ attachedparticlesystem_t* GetAttributeControlledParticleSystem( int id );
+ attachedparticlesystem_t* FindAttributeControlledParticleSystem( const char *pchSystemName );
+ typedef CUtlMap<int, attachedparticlesystem_t > ParticleDefinitionMap_t;
+ const ParticleDefinitionMap_t& GetAttributeControlledParticleSystems() const { return m_mapAttributeControlledParticleSystems; }
+
+ const CUtlVector< int > *GetWeaponUnusualParticleIndexes() const { return &m_vecAttributeControlledParticleSystemsWeapons; }
+ const CUtlVector< int > *GetCosmeticUnusualParticleIndexes() const { return &m_vecAttributeControlledParticleSystemsCosmetics; }
+ const CUtlVector< int > *GetTauntUnusualParticleIndexes() const { return &m_vecAttributeControlledParticleSystemsTaunts; }
+
+#ifdef CLIENT_DLL
+ locchar_t *GetParticleSystemLocalizedName( int index ) const;
+#endif // CLIENT_DLL
+
+#ifdef GC_DLL
+ const PeriodicScoreTypeList_t& GetPeriodicScoreTypeList() const { return m_vecPeriodicScoreTypes; }
+
+ int GetPeriodicScoreTypeCount() const { return GetPeriodicScoreTypeList().Count(); } // how many types of events are we tracking? the range goes from 0 through this return value
+ const periodic_score_t& GetPeriodicScoreInfo( int iPeriodicScoreIndex ) const; // get the full info block for this periodic score -- event type, time period, etc.
+
+ // Only intended to be used for generating data for the WebAPI.
+ const KillEaterScoreMap_t& GetKillEaterScoreTypes() const { return m_mapKillEaterScoreTypes; }
+ const SchemaStringTableDict_t& GetStringTables() const { return m_dictStringTable; }
+#endif // GC_DLL
+
+ item_definition_index_t GetCommunityMarketRemappedDefinitionIndex( item_definition_index_t unSearchItemDef ) const;
+
+ const CUtlVector<attr_type_t>& GetAttributeTypes() const { return m_vecAttributeTypes; }
+ const ISchemaAttributeType *GetAttributeType( const char *pszAttrTypeName ) const;
+
+ const LevelBlockDict_t& GetItemLevelingDataDict() const { return m_vecItemLevelingData; }
+
+ const CUtlVector<CItemLevelingDefinition> *GetItemLevelingData( const char *pszLevelBlockName ) const
+ {
+ LevelBlockDict_t::IndexType_t i = m_vecItemLevelingData.Find( pszLevelBlockName );
+ if ( i == LevelBlockDict_t::InvalidIndex() )
+ return NULL;
+
+ return m_vecItemLevelingData[i];
+ }
+
+ const CItemLevelingDefinition *GetItemLevelForScore( const char *pszLevelBlockName, uint32 unScore ) const;
+ const char *GetKillEaterScoreTypeLocString( uint32 unScoreType ) const;
+ const char *GetKillEaterScoreTypeLevelingDataName( uint32 unScoreType ) const;
+ bool GetKillEaterScoreTypeAllowsBotVictims( uint32 unScoreType ) const;
+#ifdef GC_DLL
+ bool GetKillEaterScoreTypeGCOnlyUpdate( uint32 unScoreType ) const;
+ bool GetKillEaterScoreTypeAllowsIncrementValues( uint32 unScoreType ) const;
+#endif
+
+#if defined(CLIENT_DLL) || defined(GAME_DLL)
+ void ItemTesting_CreateTestDefinition( int iCloneFromItemDef, int iNewDef, KeyValues *pNewKV );
+ void ItemTesting_DiscardTestDefinition( int iDef );
+#endif
+
+#ifdef DBGFLAG_VALIDATE
+ void Validate( CValidator &validator, const char *pchName );
+#endif // DBGFLAG_VALIDATE
+
+ econ_tag_handle_t GetHandleForTag( const char *pszTagName ); // non-const because it may create a new tag handle
+
+ typedef CUtlDict<econ_tag_handle_t> EconTagDict_t;
+#ifdef GC_DLL
+ const EconTagDict_t& GetEconTagDict() const { return m_dictTags; } // meant for internal/debug use only, not for runtime iteration
+#endif // GC_DLL
+
+ virtual RTime32 GetCustomExpirationDate( const char *pszExpirationDate ) const { return k_RTime32Nil; }
+
+public:
+ // Subclass interface.
+ virtual CEconItemDefinition *CreateEconItemDefinition() { return new CEconItemDefinition; }
+ virtual CEconCraftingRecipeDefinition *CreateCraftingRecipeDefinition() { return new CEconCraftingRecipeDefinition; }
+ virtual CEconStyleInfo *CreateEconStyleInfo() { return new CEconStyleInfo; }
+ virtual CQuestObjectiveDefinition *CreateQuestDefinition() { return new CQuestObjectiveDefinition; }
+
+ virtual IEconTool *CreateEconToolImpl( const char *pszToolType, const char *pszUseString, const char *pszUsageRestriction, item_capabilities_t unCapabilities, KeyValues *pUsageKV );
+
+#ifdef GC_DLL
+ virtual random_attrib_t *CreateRandomAttribute( const char *pszContext, KeyValues *pRandomAttributesKV, CUtlVector<CUtlString> *pVecErrors = NULL );
+#endif // GC_DLL
+
+ virtual bool BCanStrangeFilterApplyToStrangeSlotInItem( uint32 /*strange_event_restriction_t*/ unRestrictionType, uint32 unRestrictionValue, const IEconItemInterface *pItem, int iStrangeSlot, uint32 *out_pOptionalScoreType ) const;
+ bool AddQuestObjective( const CQuestObjectiveDefinition **ppQuestObjective, KeyValues *pKVObjective, CUtlVector<CUtlString> *pVecErrors );
+
+ bool BInsertLootlist( const char *pListName, KeyValues *pKVLootList, CUtlVector<CUtlString> *pVecErrors );
+
+#ifdef GC_DLL
+ void PerformCaseBehaviorCheck();
+#endif
+protected:
+ virtual void Reset( void );
+
+ virtual bool BInitSchema( KeyValues *pKVRawDefinition, CUtlVector<CUtlString> *pVecErrors = NULL );
+#ifdef TF_CLIENT_DLL
+ virtual int CalculateNumberOfConcreteItems( const CEconItemDefinition *pItemDef ); // Let derived classes handle custom item types
+#endif // TF_CLIENT_DLL
+
+private:
+ bool BInitGameInfo( KeyValues *pKVGameInfo, CUtlVector<CUtlString> *pVecErrors );
+ bool BInitAttributeTypes( CUtlVector<CUtlString> *pVecErrors );
+#ifdef GC_DLL
+ bool BInitPeriodicScoring( KeyValues *pKVGameInfo, CUtlVector<CUtlString> *pVecErrors );
+#endif // GC_DLL
+ bool BInitDefinitionPrefabs( KeyValues *pKVPrefabs, CUtlVector<CUtlString> *pVecErrors );
+ bool BInitItemSeries( KeyValues *pKVSeries, CUtlVector<CUtlString> *pVecErrors );
+ bool BVerifyBaseItemNames( CUtlVector<CUtlString> *pVecErrors );
+ bool BInitRarities( KeyValues *pKVRarities, KeyValues *pKVRarityWeights, CUtlVector<CUtlString> *pVecErrors );
+ bool BInitQualities( KeyValues *pKVAttributes, CUtlVector<CUtlString> *pVecErrors );
+ bool BInitColors( KeyValues *pKVColors, CUtlVector<CUtlString> *pVecErrors );
+ bool BInitAttributes( KeyValues *pKVAttributes, CUtlVector<CUtlString> *pVecErrors );
+ bool BInitEquipRegions( KeyValues *pKVEquipRegions, CUtlVector<CUtlString> *pVecErrors );
+ bool BInitEquipRegionConflicts( KeyValues *pKVEquipRegions, CUtlVector<CUtlString> *pVecErrors );
+ bool BInitItems( KeyValues *pKVAttributes, CUtlVector<CUtlString> *pVecErrors );
+ bool BInitItemSets( KeyValues *pKVItemSets, CUtlVector<CUtlString> *pVecErrors );
+ bool BInitTimedRewards( KeyValues *pKVTimeRewards, CUtlVector<CUtlString> *pVecErrors );
+ bool BInitAchievementRewards( KeyValues *pKVTimeRewards, CUtlVector<CUtlString> *pVecErrors );
+#ifdef GC_DLL
+ bool BInitRandomAttributeTemplates( KeyValues *pKVRandomAttributeTemplates, CUtlVector<CUtlString> *pVecErrors );
+#endif // GC_DLL
+ bool BInitRecipes( KeyValues *pKVRecipes, CUtlVector<CUtlString> *pVecErrors );
+ bool BInitLootLists( KeyValues *pKVLootLists, CUtlVector<CUtlString> *pVecErrors );
+ bool BInitRevolvingLootLists( KeyValues *pKVRevolvingLootLists, CUtlVector<CUtlString> *pVecErrors );
+ bool BInitItemCollections( KeyValues *pKVItemSets, CUtlVector<CUtlString> *pVecErrors );
+ bool BInitCollectionReferences( CUtlVector<CUtlString> *pVecErrors );
+ bool BInitItemPaintKitDefinitions( KeyValues *pKVPaintKits, CUtlVector<CUtlString> *pVecErrors );
+ bool BInitOperationDefinitions( KeyValues *pKVGameInfo, KeyValues *pOperations, CUtlVector<CUtlString> *pVecErrors );
+
+#ifdef TF_CLIENT_DLL
+ bool BInitConcreteItemCounts( CUtlVector<CUtlString> *pVecErrors );
+ bool BInitSteamPackageLocalizationToken( KeyValues *pKVSteamPackages, CUtlVector<CUtlString> *pVecErrors );
+#endif // TF_CLIENT_DLL
+ bool BInitItemLevels( KeyValues *pKVItemLevels, CUtlVector<CUtlString> *pVecErrors );
+ bool BInitKillEaterScoreTypes( KeyValues *pKVItemLevels, CUtlVector<CUtlString> *pVecErrors );
+ bool BInitStringTables( KeyValues *pKVStringTables, CUtlVector<CUtlString> *pVecErrors );
+ bool BInitCommunityMarketRemaps( KeyValues *pKVCommunityMarketRemaps, CUtlVector<CUtlString> *pVecErrors );
+
+ bool BPostSchemaInit( CUtlVector<CUtlString> *pVecErrors ) const;
+ bool BInitAttributeControlledParticleSystems( KeyValues *pKVParticleSystems, CUtlVector<CUtlString> *pVecErrors );
+
+#if defined(CLIENT_DLL) || defined(GAME_DLL)
+ bool BInitArmoryData( KeyValues *pKVArmoryData, CUtlVector<CUtlString> *pVecErrors );
+#else
+ bool BInitExperiements( KeyValues *pKVExperiments, CUtlVector<CUtlString> *pVecErrors );
+ bool BInitForeignImports( CUtlVector<CUtlString> *pVecErrors );
+
+ CForeignAppImports *FindOrAddAppImports( AppId_t unAppID );
+#endif
+
+ bool BVerifyLootListItemDropDates( const CEconLootListDefinition* pLootList, CUtlVector<CUtlString> *pVecErrors ) const;
+ bool BRecurseiveVerifyLootListItemDropDates( const CEconLootListDefinition* pLootList, const CEconLootListDefinition* pRootLootList, CUtlVector<CUtlString> *pVecErrors ) const;
+
+ // Note: this returns pointers to the inside of a vector and/or NULL. Pointers are not intended to be
+ // saved off and used later.
+ const kill_eater_score_type_t *FindKillEaterScoreType( uint32 unScoreType ) const;
+
+ uint32 m_unResetCount;
+
+ KeyValues *m_pKVRawDefinition;
+ uint32 m_unVersion;
+ CSHA m_schemaSHA;
+
+ // Class range
+ equipped_class_t m_unFirstValidClass;
+ equipped_class_t m_unLastValidClass;
+ equipped_class_t m_unAccoutClassIndex;
+
+ // Item slot range
+ equipped_slot_t m_unFirstValidClassItemSlot;
+ equipped_slot_t m_unLastValidClassItemSlot;
+ equipped_slot_t m_unFirstValidAccountItemSlot;
+ equipped_slot_t m_unLastValidAccountItemSlot;
+
+ // Number of allowed presets
+ uint32 m_unNumItemPresets;
+
+ // Allowable range of item levels for this app
+ uint32 m_unMinLevel;
+ uint32 m_unMaxLevel;
+
+ // Total value of all the weights of the qualities
+ uint32 m_unSumQualityWeights;
+
+ // Name-to-implementation list of all unique attribute types (ie., "wide strange score").
+ CUtlVector<attr_type_t> m_vecAttributeTypes;
+
+ // Contains the list of rarity definitions
+ CUtlMap<int, CEconItemSeriesDefinition, int > m_mapItemSeries;
+
+ // Contains the list of rarity definitions
+ CUtlMap<int, CEconItemRarityDefinition, int > m_mapRarities;
+
+ // Contains the list of item definitions read in from all data files.
+ CUtlMap<int, CEconItemQualityDefinition, int > m_mapQualities;
+
+ // Contains the list of item definitions read in from all data files.
+ ItemDefinitionMap_t m_mapItems;
+
+ CUtlMap<int, CQuestObjectiveDefinition*, int > m_mapQuestObjectives;
+
+ // A sorted version of the same map, for instances where we really want sorted data
+ SortedItemDefinitionMap_t m_mapItemsSorted;
+
+ // List of all the tool items, is a sublist of mapItems
+ ToolsItemDefinitionMap_t m_mapToolsItems;
+
+ // List of all base items, is a sublist of mapItems
+ BaseItemDefinitionMap_t m_mapBaseItems;
+
+#if defined(CLIENT_DLL) || defined(GAME_DLL)
+ // What is the default item definition we'll return in the client code if we can't find the correct one?
+ CEconItemDefinition *m_pDefaultItemDefinition;
+#endif
+
+ // Contains the list of attribute definitions read in from all data files.
+ CUtlMap<int, CEconItemAttributeDefinition, int > m_mapAttributes;
+
+ // Contains the list of item recipes read in from all data files.
+ RecipeDefinitionMap_t m_mapRecipes;
+
+ // Contains the list of item sets.
+ ItemSetMap_t m_mapItemSets;
+ ItemCollectionMap_t m_mapItemCollections;
+ ItemCollectionCrateMap_t m_vecItemCollectionCrates;
+
+ OperationDefinitionMap_t m_mapOperationDefinitions;
+
+ // Paint Kit defintions
+ ItemPaintKitMap_t m_mapItemPaintKits;
+
+ // Revolving loot lists.
+ CUtlMap<int, const char*> m_mapRevolvingLootLists;
+
+ // Contains the list of loot lists.
+ LootListDefinitionMap_t m_mapLootLists;
+
+ // List of events that award items based on time played
+ CUtlVector<CTimedItemRewardDefinition> m_vecTimedRewards;
+
+ // list of items that will be awarded from achievements
+ CUtlDict< AchievementAward_t *, int > m_dictAchievementRewards;
+ CUtlMap< uint32, AchievementAward_t * > m_mapAchievementRewardsByData;
+
+#ifdef GC_DLL
+ // list of random attribute templates
+ CUtlDict< random_attrib_t * > m_dictRandomAttributeTemplates;
+#endif // GC_DLL
+
+ // Contains information for attribute attached particle systems
+ CUtlMap<int, attachedparticlesystem_t > m_mapAttributeControlledParticleSystems;
+ CUtlVector< int > m_vecAttributeControlledParticleSystemsCosmetics;
+ CUtlVector< int > m_vecAttributeControlledParticleSystemsWeapons;
+ CUtlVector< int > m_vecAttributeControlledParticleSystemsTaunts;
+
+
+ // Contains information on which equip regions conflict with each other regions and how to
+ // test for overlap.
+ EquipRegionsList_t m_vecEquipRegionsList;
+
+ // Contains information about prefab KeyValues blocks that be can referenced elsewhere
+ // in the schema.
+ PrefabMap_t m_mapDefinitionPrefabs;
+
+ // Contains runtime color information, looked-up by name.
+ ColorDefinitionsList_t m_vecColorDefs;
+
+ // Contains information about: a) every bodygroup that appears anywhere in the schema, and
+ // b) whether they default to on or off.
+ BodygroupStateMap_t m_mapDefaultBodygroupState;
+
+ // Various definitions can have any number of unique tags associated with them.
+ EconTagDict_t m_dictTags;
+
+#ifdef GC_DLL
+ // Information about our periodic score accumulators.
+ PeriodicScoreTypeList_t m_vecPeriodicScoreTypes;
+#endif // GC_DLL
+
+ // List of item leveling data.
+ KillEaterScoreMap_t m_mapKillEaterScoreTypes;
+
+ SchemaStringTableDict_t m_dictStringTable;
+
+ typedef CUtlMap< item_definition_index_t, item_definition_index_t, item_definition_index_t > CommunityMarketDefinitionRemapMap_t;
+ CommunityMarketDefinitionRemapMap_t m_mapCommunityMarketDefinitionIndexRemap;
+
+#ifdef CLIENT_DLL
+ // Steam-package-ID-to-localization-token map, used for modifying tooltips in the store.
+ typedef CUtlMap< uint32, const char * > SteamPackageLocalizationTokenMap_t;
+ SteamPackageLocalizationTokenMap_t m_mapSteamPackageLocalizationTokens;
+#endif // CLIENT_DLL
+
+ LevelBlockDict_t m_vecItemLevelingData;
+
+#if defined(CLIENT_DLL) || defined(GAME_DLL)
+ // Contains Armory data key->localization string mappings
+ ArmoryStringDict_t m_dictArmoryItemTypesDataStrings;
+ ArmoryStringDict_t m_dictArmoryItemClassesDataStrings;
+ ArmoryStringDict_t m_dictArmoryAttributeDataStrings;
+ ArmoryStringDict_t m_dictArmoryItemDataStrings;
+
+ // Used for delaying the parsing of the item schema until its safe to swap out the back end data.
+ IDelayedSchemaData *m_pDelayedSchemaData;
+#elif defined(GC_DLL)
+ // GC only
+ CUtlVector< CExperimentDefinition > m_vecExperiments;
+ CUtlMap< AppId_t, CForeignAppImports *> m_mapForeignImports;
+ CUtlVector< AppId_t > m_vecForeignApps;
+#endif
+
+ CUtlVector< CEconItemDefinition * > m_vecBundles; // A cached list of all bundles
+};
+
+#ifdef GC_DLL
+ void PerformIncrementKillEaterAttributeScore( CEconUserSession *pLockedOwnerSession, CEconItem *pItem, uint32 unEventType, uint32 unIncrementCount, bool bGCOrigination, GCSDK::CSharedObjectTransactionEx *pTransaction );
+#endif // GC_DLL
+
+extern CEconItemSchema & GEconItemSchema();
+
+//-----------------------------------------------------------------------------
+// CSchemaFieldHandle
+//-----------------------------------------------------------------------------
+template < class T >
+class CSchemaFieldHandle
+{
+public:
+ explicit CSchemaFieldHandle( const char *szName )
+ : m_szName( szName )
+ {
+ m_pRef = GetTypedRef();
+ m_unSchemaGeneration = GEconItemSchema().GetResetCount();
+#if _DEBUG
+ m_unVersion_Debug = GEconItemSchema().GetVersion();
+#endif
+ }
+
+ operator const T *( void ) const
+ {
+ uint32 unSchemaGeneration = GEconItemSchema().GetResetCount();
+ if ( m_unSchemaGeneration != unSchemaGeneration )
+ {
+ m_pRef = GetTypedRef();
+ m_unSchemaGeneration = unSchemaGeneration;
+#if _DEBUG
+ m_unVersion_Debug = GEconItemSchema().GetVersion();
+#endif
+ }
+
+#if _DEBUG
+ Assert( m_unVersion_Debug == GEconItemSchema().GetVersion() );
+#endif
+ return m_pRef;
+ }
+
+ const T *operator->( void ) const
+ {
+ return static_cast<const T *>( *this );
+ }
+
+ const char *GetName( void ) const
+ {
+ return m_szName;
+ }
+
+private:
+ const T *GetTypedRef() const;
+
+private:
+ const char *m_szName;
+
+ mutable const T *m_pRef;
+ mutable uint32 m_unSchemaGeneration;
+#if _DEBUG
+ mutable uint32 m_unVersion_Debug;
+#endif
+};
+
+template < >
+inline const CEconColorDefinition *CSchemaFieldHandle<CEconColorDefinition>::GetTypedRef( void ) const
+{
+ return GEconItemSchema().GetColorDefinitionByName( m_szName );
+}
+
+template < >
+inline const CEconItemAttributeDefinition *CSchemaFieldHandle<CEconItemAttributeDefinition>::GetTypedRef( void ) const
+{
+ return GEconItemSchema().GetAttributeDefinitionByName( m_szName );
+}
+
+template < >
+inline const CEconItemDefinition *CSchemaFieldHandle<CEconItemDefinition>::GetTypedRef( void ) const
+{
+ return GEconItemSchema().GetItemDefinitionByName( m_szName );
+}
+
+template < >
+inline const CEconLootListDefinition *CSchemaFieldHandle<CEconLootListDefinition>::GetTypedRef( void ) const
+{
+ return GEconItemSchema().GetLootListByName( m_szName );
+}
+
+template < >
+inline const attachedparticlesystem_t *CSchemaFieldHandle<attachedparticlesystem_t>::GetTypedRef( void ) const
+{
+ return GEconItemSchema().FindAttributeControlledParticleSystem( m_szName );
+}
+
+typedef CSchemaFieldHandle<CEconColorDefinition> CSchemaColorDefHandle;
+typedef CSchemaFieldHandle<CEconItemAttributeDefinition> CSchemaAttributeDefHandle;
+typedef CSchemaFieldHandle<CEconItemDefinition> CSchemaItemDefHandle;
+typedef CSchemaFieldHandle<CEconLootListDefinition> CSchemaLootListDefHandle;
+typedef CSchemaFieldHandle<attachedparticlesystem_t> CSchemaParticleHandle;
+
+
+struct steam_market_gc_identifier_t
+{
+ item_definition_index_t m_unDefIndex;
+ uint8 m_unQuality;
+
+ bool operator<( const struct steam_market_gc_identifier_t& b ) const
+ {
+ return (m_unDefIndex < b.m_unDefIndex)
+ || ((m_unDefIndex == b.m_unDefIndex) && (m_unQuality < b.m_unQuality));
+ }
+};
+
+// Implementation reliant on earlier class content.
+inline const CEconItemAttributeDefinition *static_attrib_t::GetAttributeDefinition() const
+{
+ return GEconItemSchema().GetAttributeDefinition( iDefIndex );
+}
+
+inline const ISchemaAttributeType *static_attrib_t::GetAttributeType() const
+{
+ const CEconItemAttributeDefinition *pAttrDef = GetAttributeDefinition();
+ if ( !pAttrDef )
+ return NULL;
+
+ return pAttrDef->GetAttributeType();
+}
+
+// Utility function to convert datafile strings to ints.
+int StringFieldToInt( const char *szValue, const char **pValueStrings, int iNumStrings, bool bDontAssert = false );
+int StringFieldToInt( const char *szValue, const CUtlVector<const char *>& vecValueStrings, bool bDontAssert = false );
+
+#ifdef GC_DLL
+// Global econ-level helper functionality.
+EUniverse GetUniverse();
+
+bool BYieldingGetChangedItemDefinitions( int iComparisonColumn, CUtlVector<item_definition_index_t>& out_vecChangedDefIndices );
+bool BYieldingUpdateItemDefinitionStateHashValue( GCSDK::CSQLAccess& sqlAccess, item_definition_index_t unItemDef, int iUpdatedColumn );
+#endif // GC_DLL
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+class CAttributeLineItemLootList : public IEconLootList
+{
+public:
+ static CSchemaAttributeDefHandle s_pAttrDef_RandomDropLineItems[4];
+#ifdef GC_DLL
+ static CSchemaAttributeDefHandle s_pAttrDef_RandomDropLineItemUnusualChance;
+ static CSchemaAttributeDefHandle s_pAttrDef_RandomDropLineItemUnusualList;
+#endif // GC_DLL
+ static CSchemaAttributeDefHandle s_pAttrDef_RandomDropLineItemFooterDesc;
+
+public:
+ CAttributeLineItemLootList( const IEconItemInterface *pEconItem )
+ : m_pEconItem( pEconItem )
+ {
+ //
+ }
+
+ virtual void EnumerateUserFacingPotentialDrops( IEconLootListIterator *pIt ) const OVERRIDE;
+ virtual bool BPublicListContents() const OVERRIDE { return true; } // any attribute data that clients have is public to them
+ virtual const char *GetLootListHeaderLocalizationKey() const OVERRIDE;
+ virtual const char *GetLootListFooterLocalizationKey() const OVERRIDE;
+ virtual const char *GetLootListCollectionReference() const OVERRIDE;
+
+#ifdef GC_DLL
+ MUST_CHECK_RETURN virtual bool BGenerateSingleRollRandomItems( const CEconGameAccount *pGameAccount, bool bFreeAccount, CUtlVector<CEconItem *> *out_pvecItems, const CUtlVector< item_definition_index_t > *pVecAvoidItemDefs = NULL ) const OVERRIDE;
+#endif // GC_DLL
+
+private:
+ const IEconItemInterface *m_pEconItem;
+};
+
+void MergeDefinitionPrefab( KeyValues *pKVWriteItem, KeyValues *pKVSourceItem );
+
+#endif //ECONITEMSCHEMA_H
diff --git a/game/shared/econ/econ_item_system.cpp b/game/shared/econ/econ_item_system.cpp
new file mode 100644
index 0000000..29026b9
--- /dev/null
+++ b/game/shared/econ/econ_item_system.cpp
@@ -0,0 +1,694 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+
+#include "cbase.h"
+#include "tier1/KeyValues.h"
+#include "econ_gcmessages.h"
+#include "econ_item_system.h"
+#include "econ_item_inventory.h"
+#include "game_item_schema.h"
+#include "gc_clientsystem.h"
+
+#include "utldict.h"
+#include "filesystem.h"
+#include "steam/isteamhttp.h"
+
+
+#if defined(CLIENT_DLL) || defined(GAME_DLL)
+#include "gamestringpool.h"
+#include "ihasattributes.h"
+#include "tier0/icommandline.h"
+#endif
+
+#if defined(CLIENT_DLL)
+#include "igameevents.h"
+#endif
+
+// FIXME FIXME FIXME
+#if defined(TF_DLL) || defined(TF_CLIENT_DLL)
+ #include "tf_item_system.h"
+#endif // defined(TF_DLL) || defined(TF_CLIENT_DLL)
+
+#if defined (DOTA_CLIENT_DLL) || defined (DOTA_DLL)
+ #include "econ/dota_item_system.h"
+#endif // defined (DOTA_CLIENT_DLL) || defined (DOTA_DLL)
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+#if ( defined( GAME_DLL ) || defined( CLIENT_DLL ) ) && ( defined( _DEBUG ) || defined( STAGING_ONLY ) )
+ConVar item_debug( "item_debug", "0", FCVAR_REPLICATED | FCVAR_DEVELOPMENTONLY );
+ConVar items_game_use_gc_copy( "items_game_use_gc_copy", "1", FCVAR_CHEAT | FCVAR_REPLICATED | FCVAR_ARCHIVE, "If set, items_game.txt will be stomped by the GC." );
+ConVar item_debug_validation( "item_debug_validation", "1", FCVAR_REPLICATED | FCVAR_ARCHIVE, "If set, CEconEntity::ValidateEntityAttachedToPlayer behaves as it would in release builds and also allows bot players to take the same code path as real players." );
+#endif
+
+static ConVar item_quality_chance_unique( "item_quality_chance_unique", "0.1", FCVAR_REPLICATED | FCVAR_DEVELOPMENTONLY, "Percentage chance that a random item is unique." );
+static ConVar item_quality_chance_rare( "item_quality_chance_rare", "0.5", FCVAR_REPLICATED | FCVAR_DEVELOPMENTONLY, "Percentage chance that a random item is a rare." );
+static ConVar item_quality_chance_common( "item_quality_chance_common", "1.0", FCVAR_REPLICATED | FCVAR_DEVELOPMENTONLY, "Percentage chance that a random item is common." );
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Get at the global item system
+//-----------------------------------------------------------------------------
+CEconItemSystem *ItemSystem( void )
+{
+ static GameItemSystem_t *pSystem = NULL;
+ if ( !pSystem )
+ {
+ pSystem = new GameItemSystem_t();
+ }
+
+ return pSystem;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Global schema access, declared in game_item_schema.h
+//-----------------------------------------------------------------------------
+GameItemSchema_t *GetItemSchema()
+{
+ return ItemSystem()->GetItemSchema();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CEconItemSystem::CEconItemSystem( void )
+{
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CEconItemSystem::~CEconItemSystem( void )
+{
+ Shutdown();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Parse in our data files.
+//-----------------------------------------------------------------------------
+void CEconItemSystem::Init( void )
+{
+#if defined(USES_ECON_ITEMS)
+ ParseItemSchemaFile( "scripts/items/items_game.txt" );
+#endif
+
+#ifdef CLIENT_DLL
+ IGameEvent *event = gameeventmanager->CreateEvent( "item_schema_initialized" );
+ if ( event )
+ {
+ gameeventmanager->FireEventClientSide( event );
+ }
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CEconItemSystem::Shutdown( void )
+{
+}
+
+extern ConVar mp_tournament;
+
+#ifdef GAME_DLL
+ConVar mp_tournament_whitelist( "mp_tournament_whitelist", "item_whitelist.txt", FCVAR_NONE, "Specifies the item whitelist file to use." );
+#endif
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CEconItemSystem::ReloadWhitelist( void )
+{
+ // Default state of items depends on whether we're in tourney mode, and whether there's a whitelist
+ bool bDefault = true;
+ bool bFoundWhitelist = false;
+
+ KeyValues *pWhitelistKV = new KeyValues( "item_whitelist" );
+
+#ifdef GAME_DLL
+ if ( mp_tournament.GetBool() && mp_tournament_whitelist.GetString() )
+ {
+ const char *pszWhitelistFile = mp_tournament_whitelist.GetString();
+ if ( pWhitelistKV->LoadFromFile( filesystem, pszWhitelistFile ) )
+ {
+ // Allow the whitelist to override the default, so they can turn it into a blacklist if they want to
+ bDefault = pWhitelistKV->GetBool( "unlisted_items_default_to" );
+ bFoundWhitelist = true;
+ }
+ else if ( pszWhitelistFile && pszWhitelistFile[0] )
+ {
+ Msg("Item Whitelist file '%s' could not be found. All items will be allowed.\n", pszWhitelistFile );
+ }
+ }
+#endif
+
+ const CEconItemSchema::ItemDefinitionMap_t& mapItemDefs = m_itemSchema.GetItemDefinitionMap();
+ FOR_EACH_MAP_FAST( mapItemDefs, i )
+ {
+ mapItemDefs[i]->SetAllowedInMatch( bDefault );
+ }
+
+ // If we didn't find a file, we're done.
+ if ( !bFoundWhitelist )
+ return;
+
+ // Otherwise, go through the KVs and turn on the matching items.
+ Msg("Parsing item whitelist (default: %s)\n", bDefault ? "allowed" : "disallowed" );
+ pWhitelistKV = pWhitelistKV->GetFirstSubKey();
+ while ( pWhitelistKV )
+ {
+ bool bAllow = pWhitelistKV->GetBool();
+
+ const char *pszItemName = pWhitelistKV->GetName();
+ if ( pszItemName && pszItemName[0] && !FStrEq("unlisted_items_default_to", pszItemName) )
+ {
+ CEconItemDefinition *pItemDef = m_itemSchema.GetItemDefinitionByName( pszItemName );
+ if ( pItemDef )
+ {
+ pItemDef->SetAllowedInMatch( bAllow );
+ Msg(" -> %s '%s'\n", bAllow ? "Allowing" : "Removing", pszItemName );
+ }
+ else
+ {
+ Warning(" -> Could not find an item definition named '%s'\n", pszItemName );
+ }
+ }
+
+ pWhitelistKV = pWhitelistKV->GetNextKey();
+ }
+ Msg("Finished.\n");
+}
+
+#ifdef GAME_DLL
+CON_COMMAND_F( item_show_whitelistable_definitions, "Lists the item definitions that can be whitelisted in the item_whitelist.txt file in tournament mode.", FCVAR_CHEAT )
+{
+ Msg("Available item definitions for whitelisting:\n");
+ const CEconItemSchema::SortedItemDefinitionMap_t& mapItemDefs = ItemSystem()->GetItemSchema()->GetSortedItemDefinitionMap();
+ FOR_EACH_MAP( mapItemDefs, i )
+ {
+ const CEconItemDefinition *pItemDef = mapItemDefs[i];
+ if ( pItemDef && pItemDef->GetQuality() != AE_NORMAL && !pItemDef->IsHidden() )
+ {
+ Msg(" '%s'\n", pItemDef->GetDefinitionName() );
+ }
+ }
+}
+#endif // GAME_DLL
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CEconItemSystem::ResetAttribStringCache( void )
+{
+ const CUtlMap<int, CEconItemAttributeDefinition, int> &mapDefs = m_itemSchema.GetAttributeDefinitionMap();
+ FOR_EACH_MAP_FAST( mapDefs, i )
+ {
+ mapDefs[i].ClearStringCache();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CEconItemSystem::DecryptItemFiles( KeyValues *pKV, const char *pName )
+{
+ char szFullName[512];
+ Q_snprintf(szFullName,sizeof(szFullName), "%s.ctx", pName );
+
+ FileHandle_t f = filesystem->Open( szFullName, "rb", "MOD" );
+
+ if (!f)
+ {
+#if !defined(CSTRIKE_DLL)
+ Warning("No %s file found. May be unable to create items.\n", pName );
+#endif // CSTRIKE_DLL
+ return false;
+ }
+
+ int fileSize = filesystem->Size(f);
+ char *buffer = (char*)MemAllocScratch(fileSize + 1);
+
+ Assert(buffer);
+
+ filesystem->Read(buffer, fileSize, f); // read into local buffer
+ buffer[fileSize] = 0; // null terminate file as EOF
+ filesystem->Close( f ); // close file after reading
+
+ UTIL_DecodeICE( (unsigned char*)buffer, fileSize, GetEncryptionKey() );
+
+ bool retOK = pKV->LoadFromBuffer( szFullName, buffer, filesystem );
+
+ MemFreeScratch();
+
+ if ( !retOK )
+ return false;
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Read the specified item schema file. Init the item schema with the contents
+//-----------------------------------------------------------------------------
+void CEconItemSystem::ParseItemSchemaFile( const char *pFilename )
+{
+ CUtlVector< CUtlString > vecErrors;
+ bool bSuccess = m_itemSchema.BInit( pFilename, "MOD", &vecErrors );
+
+ if( !bSuccess )
+ {
+ FOR_EACH_VEC( vecErrors, nError )
+ {
+ // we want this to be an Error because several
+ // places rely on loading a valid item schema
+ Error( "%s\n", vecErrors[nError].String() );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Generate a random item matching the specified criteria
+//-----------------------------------------------------------------------------
+item_definition_index_t CEconItemSystem::GenerateRandomItem( CItemSelectionCriteria *pCriteria, entityquality_t *outEntityQuality )
+{
+ // First, pick a random item quality (use the one passed in first)
+ if ( !pCriteria->BQualitySet() )
+ {
+ pCriteria->SetQuality( GetRandomQualityForItem() );
+ }
+
+ pCriteria->SetIgnoreEnabledFlag( true );
+
+ // Determine which item templates match the criteria
+ CUtlVector<item_definition_index_t> vecMatches;
+ const CEconItemSchema::ItemDefinitionMap_t &mapDefs = m_itemSchema.GetItemDefinitionMap();
+
+HackMakeValidList:
+ FOR_EACH_MAP_FAST( mapDefs, i )
+ {
+ if ( pCriteria->BEvaluate( mapDefs[i] ) )
+ {
+ vecMatches.AddToTail( mapDefs.Key( i ) );
+ }
+ }
+
+ // No valid items?
+ int iValidItems = vecMatches.Count();
+ if ( !iValidItems )
+ {
+ // If we were searching for a unique item, drop back to a non-unique
+ if ( pCriteria->GetQuality() == AE_UNIQUE )
+ {
+ pCriteria->SetQuality( GetRandomQualityForItem( true ) );
+ goto HackMakeValidList;
+ }
+ return INVALID_ITEM_DEF_INDEX;
+ }
+
+ // Choose a random match
+ int iChosenIdx = RandomInt( 0, (iValidItems-1) );
+ item_definition_index_t iChosenItem = vecMatches[iChosenIdx];
+
+ const CEconItemDefinition *pItemDef = m_itemSchema.GetItemDefinition( iChosenItem );
+ if ( !pItemDef )
+ return INVALID_ITEM_DEF_INDEX;
+
+ // If we haven't specified an entity quality, we want to use the item's specified one
+ if ( pCriteria->GetQuality() == AE_USE_SCRIPT_VALUE )
+ {
+ int32 iScriptQuality = pItemDef->GetQuality();
+ pCriteria->SetQuality( iScriptQuality == AE_UNDEFINED ? GetRandomQualityForItem( true ) : iScriptQuality );
+ }
+
+ // If we haven't specified an item level, we want to use the item's specified one.
+ if ( !pCriteria->BItemLevelSet() )
+ {
+ pCriteria->SetItemLevel( RandomInt( pItemDef->GetMinLevel(), pItemDef->GetMaxLevel() ) );
+ }
+
+ if ( outEntityQuality )
+ {
+ *outEntityQuality = pCriteria->GetQuality();
+ }
+ return iChosenItem;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Return a random quality for the item specified
+//-----------------------------------------------------------------------------
+entityquality_t CEconItemSystem::GetRandomQualityForItem( bool bPreventUnique )
+{
+ // Start on the rarest, and work backwards
+ if ( !bPreventUnique )
+ {
+ if ( RandomFloat(0,1) < item_quality_chance_unique.GetFloat() )
+ return AE_UNIQUE;
+ }
+
+ if ( RandomFloat(0,1) < item_quality_chance_rare.GetFloat() )
+ return AE_RARITY2;
+
+ if ( RandomFloat(0,1) < item_quality_chance_common.GetFloat() )
+ return AE_RARITY1;
+
+ return AE_NORMAL;
+}
+
+static ISteamHTTP *GetISteamHTTP()
+{
+ if ( steamapicontext != NULL && steamapicontext->SteamHTTP() )
+ {
+ return steamapicontext->SteamHTTP();
+ }
+ #ifndef CLIENT_DLL
+ if ( steamgameserverapicontext != NULL )
+ {
+ return steamgameserverapicontext->SteamHTTP();
+ }
+ #endif
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Common functionality for using our raw buffer data to initialize
+// the schema when safe.
+//-----------------------------------------------------------------------------
+bool IDelayedSchemaData::InitializeSchemaInternal( CEconItemSchema *pItemSchema, CUtlBuffer& bufRawData, bool bInitAsBinary, uint32 nExpectedVersion )
+{
+ Msg( "Applying new item schema, version %08X\n", nExpectedVersion );
+
+ CUtlVector<CUtlString> vecErrors;
+ bool bSuccess = bInitAsBinary
+ ? pItemSchema->BInitBinaryBuffer( bufRawData, &vecErrors )
+ : pItemSchema->BInitTextBuffer( bufRawData, &vecErrors );
+ if( bSuccess )
+ {
+ // Sanity-check that we received the version that they sent us
+ uint32 nOurVersion = pItemSchema->GetVersion();
+ if ( nExpectedVersion != 0 && nOurVersion != nExpectedVersion )
+ {
+ Warning( "**WARNING** Item schema mismatch after update!\n" );
+ Warning( "GC told us to expect %08X, we got %08X\n", nExpectedVersion, nOurVersion );
+ }
+ }
+ else
+ {
+ Warning( "**WARNING** Failed to apply item schema!\n" );
+ FOR_EACH_VEC( vecErrors, nError )
+ {
+ Warning( "%s\n", vecErrors[nError].Get() );
+ }
+ }
+ return bSuccess;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: The GC sent us a single block of binary data.
+//-----------------------------------------------------------------------------
+class DelayedSchemaData_GCDirectData : public IDelayedSchemaData
+{
+public:
+ DelayedSchemaData_GCDirectData( const std::string& strBuffer )
+ : m_bufRawData( strBuffer.data(), strBuffer.size(), CUtlBuffer::READ_ONLY )
+ {
+ //
+ }
+
+ virtual bool InitializeSchema( CEconItemSchema *pItemSchema )
+ {
+ return InitializeSchemaInternal( pItemSchema, m_bufRawData, true, 0 );
+ }
+
+private:
+ CUtlBuffer m_bufRawData;
+};
+
+extern bool CheckValveSignature( const void *data, uint32 nDataSize, const void *signature, uint32 nSignatureSize );
+
+//-----------------------------------------------------------------------------
+// Purpose: We received a text file from an HTML request.
+//-----------------------------------------------------------------------------
+class DelayedSchemaData_HTTPResponseData : public IDelayedSchemaData
+{
+public:
+ DelayedSchemaData_HTTPResponseData( ISteamHTTP *pHTTP, HTTPRequestHandle handleHTTPRequest, uint32 unBodySize, uint32 nExpectedVersion, const std::string &sSignature )
+ : m_nExpectedVersion( nExpectedVersion )
+ {
+ Assert( pHTTP );
+
+ m_bufRawData.SetBufferType( true, true );
+ m_bufRawData.SeekPut( CUtlBuffer::SEEK_HEAD, unBodySize );
+
+ m_bValid = pHTTP->GetHTTPResponseBodyData( handleHTTPRequest, (uint8*)m_bufRawData.Base(), m_bufRawData.TellPut() );
+ if ( m_bValid )
+ m_bValid = CheckValveSignature( m_bufRawData.Base(), m_bufRawData.TellPut(), sSignature.c_str(), sSignature.length() );
+ }
+
+ virtual bool InitializeSchema( CEconItemSchema *pItemSchema )
+ {
+ if ( !m_bValid )
+ return false;
+
+ return InitializeSchemaInternal( pItemSchema, m_bufRawData, false, m_nExpectedVersion );
+ }
+
+private:
+ bool m_bValid;
+ CUtlBuffer m_bufRawData;
+ uint32 m_nExpectedVersion;
+};
+
+#define GC_ITEM_SCHEMA_UPDATE_APPLIED "Applied updated item schema from GC. %d bytes, version %08X.\n"
+#define GC_ITEM_SCHEMA_UPDATE_QUEUED "Received %d bytes item schema version %08X direct data; update is queued.\n"
+
+//-----------------------------------------------------------------------------
+// Purpose: Update the item schema from the GC
+//-----------------------------------------------------------------------------
+class CGCUpdateItemSchema : public GCSDK::CGCClientJob
+{
+public:
+ CGCUpdateItemSchema( GCSDK::CGCClient *pClient ) : GCSDK::CGCClientJob( pClient ) {
+ m_szUrl[0] = '\0';
+ m_nExpectedVersion = 0;
+ bHTTPCompleted = false;
+ }
+
+ char m_szUrl[512];
+ uint32 m_nExpectedVersion;
+ bool bHTTPCompleted;
+ CCallResult< CGCUpdateItemSchema, HTTPRequestCompleted_t > callback;
+ std::string m_sSignature;
+
+ virtual bool BYieldingRunGCJob( GCSDK::IMsgNetPacket *pNetPacket )
+ {
+ GCSDK::CProtoBufMsg< CMsgUpdateItemSchema > msg( pNetPacket );
+
+#if ( defined( GAME_DLL ) || defined( CLIENT_DLL ) ) && ( defined( _DEBUG ) || defined( STAGING_ONLY ) )
+ const bool bUseGCCopy = items_game_use_gc_copy.GetBool();
+#else
+ const bool bUseGCCopy = true;
+#endif
+
+ if ( bUseGCCopy == false && k_EUniversePublic != GetUniverse() )
+ {
+ Msg( "Loading item schema from local file.\n" );
+ KeyValuesAD pItemsGameKV( "ItemsGameFile" );
+ if ( pItemsGameKV->LoadFromFile( g_pFullFileSystem, "scripts/items/items_game.txt", "GAME" ) )
+ {
+ CUtlBuffer buffer;
+ pItemsGameKV->WriteAsBinary( buffer );
+
+ CUtlVector< CUtlString > vecErrors;
+ bool bSuccess = ItemSystem()->GetItemSchema()->BInitBinaryBuffer( buffer, &vecErrors );
+ if( !bSuccess )
+ {
+ FOR_EACH_VEC( vecErrors, nError )
+ {
+ Warning( "%s\n", vecErrors[nError].Get() );
+ }
+ }
+ }
+
+ return true;
+ }
+
+ // Check if we're already up-to-date
+ m_nExpectedVersion = msg.Body().item_schema_version();
+ uint32 nCurrentSchemaVersion = ItemSystem()->GetItemSchema()->GetVersion();
+ if ( m_nExpectedVersion != 0 && m_nExpectedVersion == nCurrentSchemaVersion )
+ {
+ Msg( "Current item schema is up-to-date with version %08X.\n", nCurrentSchemaVersion );
+ return true;
+ }
+
+ m_sSignature = msg.Body().signature();
+
+ // !TEST!
+ //const char *szURL = "http://cdn.beta.steampowered.com/apps/440/scripts/items/items_game.b8b7a85b4dd98b139957004b86ec0bc070a59d18.txt";
+ if ( msg.Body().has_items_game() )
+ {
+ bool bDidInit = ItemSystem()->GetItemSchema()->MaybeInitFromBuffer( new DelayedSchemaData_GCDirectData( msg.Body().items_game() ) );
+ Msg( bDidInit ? GC_ITEM_SCHEMA_UPDATE_APPLIED : GC_ITEM_SCHEMA_UPDATE_QUEUED, (int)msg.Body().items_game().size(), m_nExpectedVersion );
+ }
+ else
+ {
+ // Remember URL
+ const char *szURL = msg.Body().items_game_url().c_str();
+ if ( !szURL || !szURL[0] )
+ {
+ Warning( "GC sent malformed CGCUpdateItemSchema message: No schema data, no URL\n" );
+ }
+ else
+ {
+ Q_strncpy( m_szUrl, szURL, sizeof( m_szUrl ) );
+ //Msg( "Fetching %s to update item schema\n", m_szUrl );
+
+ // Send an HTTP request for the file
+ ISteamHTTP *pHTTP = GetISteamHTTP();
+ if ( !pHTTP )
+ {
+ //Warning( "Can't get ISteamHTTP to update item schema\n");
+ return true;
+ }
+ HTTPRequestHandle hReq = pHTTP->CreateHTTPRequest( k_EHTTPMethodGET, m_szUrl );
+ pHTTP->SetHTTPRequestNetworkActivityTimeout( hReq, 10 );
+ SteamAPICall_t hCall;
+ if ( !pHTTP->SendHTTPRequest( hReq, &hCall ) )
+ {
+ Warning( "Failed to update item schema: couldn't fetch %s\n", m_szUrl );
+ return true;
+ }
+
+ //
+ // *Wait* for completion.
+ //
+ // This is important. The GC needs to be able to safely assume that
+ // we will not process the next message until we have finished
+ // dealing with this one.
+ //
+ bHTTPCompleted = false;
+ #ifndef CLIENT_DLL
+ if ( steamgameserverapicontext != NULL && pHTTP == steamgameserverapicontext->SteamHTTP() )
+ {
+ callback.SetGameserverFlag();
+ }
+ #endif
+ callback.Set( hCall, this, &CGCUpdateItemSchema::OnHTTPCompleted );
+
+ // Wait for it to finish.
+ while ( !bHTTPCompleted )
+ {
+ BYieldingWaitOneFrame();
+ }
+ }
+ }
+
+ return true;
+ }
+
+ void OnHTTPCompleted( HTTPRequestCompleted_t *arg, bool bFailed )
+ {
+ // Clear flag, no matter what else, so we can stop yielding
+ bHTTPCompleted = true;
+
+ ISteamHTTP *pHTTP = GetISteamHTTP();
+ Assert( pHTTP );
+ if ( !pHTTP ) return;
+
+ if ( arg->m_eStatusCode != k_EHTTPStatusCode200OK )
+ {
+ Warning( "Failed to update item schema: HTTP status %d fetching %s\n", arg->m_eStatusCode, m_szUrl );
+ }
+ else
+ {
+ if ( !arg->m_bRequestSuccessful )
+ {
+ bFailed = true;
+ }
+ if ( !bFailed )
+ {
+ uint32 unBodySize;
+ if ( !pHTTP->GetHTTPResponseBodySize( arg->m_hRequest, &unBodySize ) )
+ {
+ Assert( false );
+ bFailed = true;
+ }
+ else
+ {
+ bool bDidInit = ItemSystem()->GetItemSchema()->MaybeInitFromBuffer( new DelayedSchemaData_HTTPResponseData( pHTTP, arg->m_hRequest, unBodySize, m_nExpectedVersion, m_sSignature ) );
+ Msg( bDidInit ? GC_ITEM_SCHEMA_UPDATE_APPLIED : GC_ITEM_SCHEMA_UPDATE_QUEUED, unBodySize, m_nExpectedVersion );
+ }
+ }
+
+ if ( bFailed )
+ {
+ Warning( "Failed to update item schema from %s\n", m_szUrl );
+ }
+ }
+
+ pHTTP->ReleaseHTTPRequest( arg->m_hRequest );
+ }
+};
+GC_REG_JOB( GCSDK::CGCClient, CGCUpdateItemSchema, "CGCUpdateItemSchema", k_EMsgGCUpdateItemSchema, GCSDK::k_EServerTypeGCClient );
+
+#ifdef CLIENT_DLL
+//-----------------------------------------------------------------------------
+// Purpose: Update the item schema from the GC
+//-----------------------------------------------------------------------------
+CON_COMMAND_F( econ_show_items_with_tag, "Lists the item definitions that have a specified tag.", FCVAR_CLIENTDLL )
+{
+ if ( args.ArgC() != 2 )
+ return;
+
+ econ_tag_handle_t tagHandle = GetItemSchema()->GetHandleForTag( args.Arg( 1 ) );
+ FOR_EACH_MAP( GetItemSchema()->GetSortedItemDefinitionMap(), i )
+ {
+ const CEconItemDefinition *pItemDef = GetItemSchema()->GetItemDefinitionMap()[i];
+
+ if ( pItemDef->HasEconTag( tagHandle ) )
+ {
+ Msg(" '%s'\n", pItemDef->GetDefinitionName() );
+ }
+ }
+}
+#endif // CLIENT_DLL
+
+#ifdef STAGING_ONLY
+//-----------------------------------------------------------------------------
+// Purpose: Update the item schema from the GC
+//-----------------------------------------------------------------------------
+#ifdef CLIENT_DLL
+CON_COMMAND_F( cl_reload_local_item_schema, "Reloads the local item schema copy.", FCVAR_CLIENTDLL )
+#else
+CON_COMMAND_F( sv_reload_local_item_schema, "Reloads the local item schema copy.", FCVAR_GAMEDLL )
+#endif
+{
+#ifdef CLIENT_DLL
+ engine->ClientCmd_Unrestricted( "cmd sv_reload_local_item_schema" );
+#endif
+
+ Msg( "Loading item schema from local file.\n" );
+ KeyValuesAD pItemsGameKV( "ItemsGameFile" );
+ if ( pItemsGameKV->LoadFromFile( g_pFullFileSystem, "scripts/items/items_game.txt", "GAME" ) )
+ {
+ CUtlBuffer buffer;
+ pItemsGameKV->WriteAsBinary( buffer );
+
+ CUtlVector< CUtlString > vecErrors;
+ bool bSuccess = ItemSystem()->GetItemSchema()->BInitBinaryBuffer( buffer, &vecErrors );
+ if( !bSuccess )
+ {
+ FOR_EACH_VEC( vecErrors, nError )
+ {
+ Warning( "%s\n", vecErrors[nError].Get() );
+ }
+ }
+ }
+}
+#endif
diff --git a/game/shared/econ/econ_item_system.h b/game/shared/econ/econ_item_system.h
new file mode 100644
index 0000000..329a86a
--- /dev/null
+++ b/game/shared/econ/econ_item_system.h
@@ -0,0 +1,86 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+
+#ifndef ECON_ITEM_SYSTEM_H
+#define ECON_ITEM_SYSTEM_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "econ_item_view.h"
+#include "game_item_schema.h"
+
+//==================================================================================
+// ITEM SYSTEM
+//==================================================================================
+
+#define GC_MOTD_CACHE_FILE "cfg/motd_entries.txt"
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+class CEconItemSystem
+{
+public:
+ CEconItemSystem( void );
+ virtual ~CEconItemSystem( void );
+
+ // Setup & parse in the item data files.
+ void Init( void );
+ void Shutdown( void );
+
+ // Return the static item data for the specified item index
+ GameItemDefinition_t *GetStaticDataForItemByDefIndex( item_definition_index_t iItemDefIndex )
+ {
+ return (GameItemDefinition_t *)m_itemSchema.GetItemDefinition( iItemDefIndex );
+ }
+ CEconItemDefinition *GetStaticDataForItemByName( const char *pszDefName )
+ {
+ return m_itemSchema.GetItemDefinitionByName( pszDefName );
+ }
+ CEconItemAttributeDefinition *GetStaticDataForAttributeByDefIndex( attrib_definition_index_t iAttribDefinitionIndex )
+ {
+ return m_itemSchema.GetAttributeDefinition( iAttribDefinitionIndex );
+ }
+ CEconItemAttributeDefinition *GetStaticDataForAttributeByName( const char *pszDefName )
+ {
+ return m_itemSchema.GetAttributeDefinitionByName( pszDefName );
+ }
+
+ // Select and return a random item's definition index matching the specified criteria
+ item_definition_index_t GenerateRandomItem( CItemSelectionCriteria *pCriteria, entityquality_t *outEntityQuality );
+
+ // Select and return the base item definition index for a class's load-out slot
+ // Note: baseitemcriteria_t is game-specific and/or may not exist!
+ virtual item_definition_index_t GenerateBaseItem( struct baseitemcriteria_t *pCriteria ) { return INVALID_ITEM_DEF_INDEX; }
+
+ // Return a random item quality
+ entityquality_t GetRandomQualityForItem( bool bPreventUnique = false );
+
+ // Decrypt the item files and return the keyvalue
+ bool DecryptItemFiles( KeyValues *pKV, const char *pName );
+
+ GameItemSchema_t *GetItemSchema() { return &m_itemSchema; }
+
+ // Open the server's whitelist, and if it exists, set the appropriate items allowed.
+ void ReloadWhitelist( void );
+
+ void ResetAttribStringCache( void );
+
+protected:
+ // Read the specified item schema file. Init the item schema with the contents
+ void ParseItemSchemaFile( const char *pFilename );
+
+ // Key to decrypt the item description files
+ const unsigned char *GetEncryptionKey( void ) { return (unsigned char *)"A5fSXbf7"; }
+
+private:
+ GameItemSchema_t m_itemSchema;
+};
+
+CEconItemSystem *ItemSystem( void );
+
+#endif // ECON_ITEM_SYSTEM_H
diff --git a/game/shared/econ/econ_item_tools.cpp b/game/shared/econ/econ_item_tools.cpp
new file mode 100644
index 0000000..8fb70ee
--- /dev/null
+++ b/game/shared/econ/econ_item_tools.cpp
@@ -0,0 +1,1777 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+#include "cbase.h"
+#include "game_item_schema.h"
+#include "econ_item_interface.h"
+#include "econ_item_tools.h"
+#include "econ_item_constants.h"
+#include "econ_dynamic_recipe.h"
+#include "schemainitutils.h"
+
+
+
+bool BStringsEqual( const char *pszA, const char *pszB )
+{
+ if ( pszA == NULL )
+ return pszB == NULL;
+
+ if ( pszB == NULL )
+ return false;
+
+ return !V_strcmp( pszA, pszB );
+}
+
+const unsigned int g_CapabilityApplicationMap[] =
+{
+ // if we have a tool that has this capability...
+ // ...then we check to see if the tool target has
+ // this capability
+
+ ITEM_CAP_PAINTABLE, // ITEM_CAP_PAINTABLE
+ ITEM_CAP_NAMEABLE, // ITEM_CAP_NAMEABLE
+ ITEM_CAP_DECODABLE, // ITEM_CAP_DECODABLE
+ 0, // ITEM_CAP_UNUSED; was ITEM_CAP_CAN_MOD_SOCKET
+ ITEM_CAP_CAN_CUSTOMIZE_TEXTURE, // ITEM_CAP_CAN_CUSTOMIZE_TEXTURE
+ 0, // ITEM_CAP_USABLE
+ 0, // ITEM_CAP_USABLE_GC
+ ITEM_CAP_CAN_GIFT_WRAP, // ITEM_CAP_CAN_GIFT_WRAP
+ 0, // ITEM_CAP_USABLE_OUT_OF_GAME
+ ITEM_CAP_CAN_COLLECT, // ITEM_CAP_CAN_COLLECT
+ 0, // ITEM_CAP_CAN_CRAFT_COUNT
+ 0, // ITEM_CAP_CAN_CRAFT_MARK
+ ITEM_CAP_PAINTABLE_TEAM_COLORS | ITEM_CAP_PAINTABLE, // ITEM_CAP_PAINTABLE_TEAM_COLORS
+ 0, // ITEM_CAP_CAN_BE_RESTORED
+ ITEM_CAP_CAN_USE_STRANGE_PARTS, // ITEM_CAP_CAN_USE_STRANGE_PARTS
+ ITEM_CAP_CAN_CARD_UPGRADE, // ITEM_CAP_CAN_CARD_UPGRADE
+ ITEM_CAP_CAN_STRANGIFY, // ITEM_CAP_CAN_STRANGIFY
+ ITEM_CAP_CAN_KILLSTREAKIFY, // ITEM_CAP_CAN_KILLSTREAKIFY
+ ITEM_CAP_CAN_CONSUME, // ITEM_CAP_CAN_CONSUME
+ ITEM_CAP_CAN_SPELLBOOK_PAGE, // ITEM_CAP_CAN_SPELLBOOK_PAGE
+ ITEM_CAP_HAS_SLOTS, // ITEM_CAP_HAS_SLOTS
+ ITEM_CAP_DUCK_UPGRADABLE, // ITEM_CAP_DUCK_UPGRADABLE
+ ITEM_CAP_CAN_UNUSUALIFY, // ITEM_CAP_CAN_UNUSUALIFY
+};
+
+COMPILE_TIME_ASSERT( ARRAYSIZE( g_CapabilityApplicationMap ) == NUM_ITEM_CAPS );
+
+//---------------------------------------------------------------------------------------
+// Purpose:
+//---------------------------------------------------------------------------------------
+bool IEconTool::ShouldDisplayQuantity( const IEconItemInterface *pTool ) const
+{
+ Assert( pTool );
+
+ const GameItemDefinition_t *pItemDef = pTool->GetItemDefinition();
+ if ( !pItemDef )
+ return false;
+
+ static CSchemaAttributeDefHandle pAttrDef_UnlimitedQuantity( "unlimited quantity" );
+ if ( pTool->FindAttribute( pAttrDef_UnlimitedQuantity ) )
+ return false;
+
+ if ( pTool->GetQuantity() >= 0 )
+ {
+ if ( (pItemDef->GetCapabilities() & ITEM_CAP_USABLE_GC) != 0 )
+ return true;
+
+ if ( pItemDef->IsTool() )
+ return true;
+ }
+
+ return false;
+}
+
+//---------------------------------------------------------------------------------------
+// Purpose:
+//---------------------------------------------------------------------------------------
+CEconTool_WrappedGift::CEconTool_WrappedGift( const char *pszTypeName, const char *pszUseString, item_capabilities_t unCapabilities, KeyValues *pUsageKV )
+ : IEconTool( pszTypeName, pszUseString, NULL, unCapabilities )
+ , m_pszDeliveredGiftItemDefName( NULL )
+ , m_pDeliveredGiftItemDef( NULL )
+ , m_bIsGlobalGift( false )
+ , m_bIsDirectGift( false )
+{
+ if ( pUsageKV )
+ {
+ m_bIsGlobalGift = pUsageKV->GetBool( "target_type_global", false );
+ m_pszDeliveredGiftItemDefName = pUsageKV->GetString( "delivered_gift_item_def", NULL );
+ m_bIsDirectGift = pUsageKV->GetBool( "is_direct_gift", false );
+ }
+}
+
+//---------------------------------------------------------------------------------------
+// Purpose:
+//---------------------------------------------------------------------------------------
+bool CEconTool_WrappedGift::BFinishInitialization()
+{
+ // Now that we've finished parsing our definitions, look for a match.
+ if ( m_pszDeliveredGiftItemDefName )
+ {
+ m_pDeliveredGiftItemDef = GetItemSchema()->GetItemDefinitionByName( m_pszDeliveredGiftItemDefName );
+ }
+
+ // We're done with this value.
+ m_pszDeliveredGiftItemDefName = NULL;
+
+ return IEconTool::BFinishInitialization();
+}
+
+//---------------------------------------------------------------------------------------
+// Purpose:
+//---------------------------------------------------------------------------------------
+CEconTool_GiftWrap::CEconTool_GiftWrap( const char *pszTypeName, const char *pszUseString, item_capabilities_t unCapabilities, KeyValues *pUsageKV )
+ : IEconTool( pszTypeName, NULL, NULL, unCapabilities )
+ , m_pszWrappedGiftItemDefName( NULL )
+ , m_pWrappedGiftItemDef( NULL )
+{
+ if ( pUsageKV )
+ {
+ m_pszWrappedGiftItemDefName = pUsageKV->GetString( "wrapped_gift_item_def", NULL );
+ }
+}
+
+//---------------------------------------------------------------------------------------
+// Purpose:
+//---------------------------------------------------------------------------------------
+bool CEconTool_GiftWrap::BFinishInitialization()
+{
+ // Now that we've finished parsing our definitions, look for a match.
+ if ( m_pszWrappedGiftItemDefName )
+ {
+ m_pWrappedGiftItemDef = GetItemSchema()->GetItemDefinitionByName( m_pszWrappedGiftItemDefName );
+ }
+
+ // We're done with this value.
+ m_pszWrappedGiftItemDefName = NULL;
+
+ return m_pWrappedGiftItemDef != NULL
+ && IEconTool::BFinishInitialization();
+}
+
+//---------------------------------------------------------------------------------------
+// Purpose:
+//---------------------------------------------------------------------------------------
+bool CEconTool_GiftWrap::CanApplyTo( const IEconItemInterface *pTool, const IEconItemInterface *pToolSubject ) const
+{
+ Assert( pTool );
+ Assert( pToolSubject );
+
+ if ( pToolSubject->GetQuality() == AE_SELFMADE ||
+ pToolSubject->GetQuality() == AE_COMMUNITY ||
+ pToolSubject->GetQuality() == AE_CUSTOMIZED ||
+ pToolSubject->GetQuality() == AE_NORMAL )
+ {
+ return false;
+ }
+
+ // If an item is currently trade restricted, other flags don't matter (I think).
+ if ( ( pToolSubject->GetUntradabilityFlags() & k_Untradability_Temporary ) != 0 )
+ return false;
+
+ // One item still has the gift wrap cap, which is the engagement ring ( Something Special For Someone Special (Tool) ).
+ // However, the can_gift_wrap cap shouldn't overcome temporary trade restrictions.
+ static CSchemaAttributeDefHandle pAttrDef_ToolNeedsGiftwrap( "tool needs giftwrap" );
+
+ const bool cbSubjectNeedsGiftWrap = pToolSubject->FindAttribute( pAttrDef_ToolNeedsGiftwrap );
+ const bool cbSubjectCanTrade = pToolSubject->IsTradable();
+ const bool cbSubjectCanProceed = cbSubjectNeedsGiftWrap || cbSubjectCanTrade;
+
+ if ( !cbSubjectCanProceed )
+ return false;
+
+ static CSchemaAttributeDefHandle pAttrDef_CannotGiftwrap( "cannot giftwrap" );
+ if ( pToolSubject->FindAttribute( pAttrDef_CannotGiftwrap ) )
+ return false;
+
+ return IEconTool::CanApplyTo( pTool, pToolSubject );
+}
+
+CEconTool_StrangeCountTransfer::CEconTool_StrangeCountTransfer( const char *pszTypeName, item_capabilities_t unCapabilities )
+ : IEconTool( pszTypeName, NULL, NULL, unCapabilities )
+{
+#ifdef CLIENT_DLL
+ m_pItemSrc = NULL;
+ m_pItemDest = NULL;
+#endif // CLIENT_DLL
+}
+
+bool CEconTool_StrangeCountTransfer::AreItemsEligibleForStrangeCountTransfer( const IEconItemInterface *pItem1, const IEconItemInterface *pItem2 )
+{
+ if ( !pItem1 || !pItem2 )
+ return false;
+
+ const char *pItem1Xifier = pItem1->GetItemDefinition()->GetXifierRemapClass();
+ const char *pItem2Xifier = pItem2->GetItemDefinition()->GetXifierRemapClass();
+
+ // if no xifier class, check if the item defs are the same
+ if ( !pItem1Xifier || !pItem2Xifier )
+ {
+ if ( pItem1->GetItemDefinition() != pItem2->GetItemDefinition() )
+ {
+ return false;
+ }
+ }
+ else if ( V_stricmp( pItem1Xifier, pItem2Xifier ) != 0 )
+ {
+ return false;
+ }
+
+ // Item defs are compatabible, are there attributes? Check strange
+ // Check if they are both strange (have kill eater). Quality is less important
+ if ( !BIsItemStrange( pItem1) || !BIsItemStrange( pItem2 ) )
+ return false;
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CEconTool_StrangePart::CanApplyTo( const IEconItemInterface *pTool, const IEconItemInterface *pToolSubject ) const
+{
+ Assert( pTool );
+ Assert( pToolSubject );
+
+ // Abort if for some reason we don't know what type of attribute we're trying to track.
+ static CSchemaAttributeDefHandle pAttrDef_StrangePartCounterID( "strange part new counter ID" );
+
+ float flNewScoreType;
+ if ( !FindAttribute_UnsafeBitwiseCast<attrib_value_t>( pTool, pAttrDef_StrangePartCounterID, &flNewScoreType ) )
+ return false;
+
+ // Make sure we're "strange" in that we have at least one attribute tracking scores. We can't
+ // explicitly test for quality here because now we can have strange vintages, etc.
+ if ( GetKillEaterAttrCount() <= 0 )
+ return false;
+
+ if ( !pToolSubject->FindAttribute( GetKillEaterAttr_Score( 0 ) ) )
+ return false;
+
+ // Make sure the target item doesn't already have the property this tool is trying to
+ // apply, unless that counter is restricted, in which case we allow a second stat to be
+ // added with the same value.
+ for ( int i = 0; i < GetKillEaterAttrCount(); i++ )
+ {
+ float flScoreType;
+ if ( FindAttribute_UnsafeBitwiseCast<attrib_value_t>( pToolSubject, GetKillEaterAttr_Type( i ), &flScoreType ) && // if we have a counter in this slot...
+ RoundFloatToInt( flScoreType ) == RoundFloatToInt( flNewScoreType ) && // ...and it's counting the same thing...
+ !pToolSubject->FindAttribute( GetKillEaterAttr_Restriction( i ) ) ) // ...and that counter isn't restricted
+ {
+ return false;
+ }
+ }
+
+ // Make sure we have at least one empty customizable attribute slot.
+ bool bFoundEmptyAttributeSlot = false;
+
+ for ( int i = 0; i < GetKillEaterAttrCount(); i++ )
+ {
+ // Ignore non-user-customizable attributes.
+ if ( !GetKillEaterAttr_IsUserCustomizable( i ) )
+ continue;
+
+ // We expect to have both or neither of a user-customizable attribute. If this isn't the
+ // case the later logic will be wrong.
+ const bool bFoundTypeAttribute = pToolSubject->FindAttribute( GetKillEaterAttr_Type( i ) );
+ Assert( bFoundTypeAttribute == pToolSubject->FindAttribute( GetKillEaterAttr_Score( i ) ) );
+
+ if ( !bFoundTypeAttribute )
+ {
+ bFoundEmptyAttributeSlot = true;
+ break;
+ }
+ }
+
+ if ( !bFoundEmptyAttributeSlot )
+ return false;
+
+ // Check to make sure the definition of the item we're trying to apply to has the tags we need
+ // to match and doesn't have any tags that we need to avoid. We use these to avoid tracking
+ // damage done for a medigun, etc.
+ const GameItemDefinition_t *pSubjectItemDef = pToolSubject->GetItemDefinition();
+ if ( !pSubjectItemDef )
+ return false;
+
+#if defined( TF_DLL ) || defined( TF_GC_DLL ) || defined( TF_CLIENT_DLL )
+ // Strange Cosmetics can take on any part
+ if ( pSubjectItemDef->GetDefaultLoadoutSlot() == LOADOUT_POSITION_MISC )
+ {
+ return true;
+ }
+#endif
+
+ FOR_EACH_VEC( m_RequiredTags.GetTagsList(), i )
+ {
+ if ( !pSubjectItemDef->HasEconTag( m_RequiredTags.GetTagsList()[i] ) )
+ return false;
+ }
+
+ FOR_EACH_VEC( m_RequiredMissingTags.GetTagsList(), i )
+ {
+ if ( pSubjectItemDef->HasEconTag( m_RequiredMissingTags.GetTagsList()[i] ) )
+ return false;
+ }
+
+ return IEconTool::CanApplyTo( pTool, pToolSubject );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+static const char *s_pszStrangeRestrictionTypes[] =
+{
+ "", // kStrangeEventRestriction_None
+ "victim_account_id", // kStrangeEventRestriction_VictimSteamAccount
+#if defined( TF_DLL ) || defined( TF_GC_DLL ) || defined( TF_CLIENT_DLL )
+ "map", // kStrangeEventRestriction_Map
+ "competitive", // kStrangeEventRestriction_Competitive
+#endif // defined( TF_DLL ) || defined( TF_GC_DLL ) || defined( TF_CLIENT_DLL )
+};
+
+COMPILE_TIME_ASSERT( ARRAYSIZE( s_pszStrangeRestrictionTypes ) == kStrangeEventRestrictionCount );
+
+CEconTool_StrangePartRestriction::CEconTool_StrangePartRestriction( const char *pszTypeName, const char *pszUseString, item_capabilities_t unCapabilities, KeyValues *pUsageKV )
+ : IEconTool( pszTypeName, pszUseString, NULL, unCapabilities )
+ , m_eRestrictionType( kStrangeEventRestriction_None ) // default-initialize to failure
+ , m_unRestrictionValue( (unsigned int)-1 )
+{
+ Assert( pUsageKV != NULL );
+
+ // Parse our restriction type from a string. Anything invalid here will be handled below in the IsValid()
+ // check.
+ const int iRestrictionType = StringFieldToInt( pUsageKV->GetString( "restriction_type", "" ), &s_pszStrangeRestrictionTypes[0], ARRAYSIZE( s_pszStrangeRestrictionTypes ) );
+ if ( iRestrictionType > 0 )
+ {
+ m_eRestrictionType = (strange_event_restriction_t)iRestrictionType;
+ }
+
+ // We'll use this value later from inside BFinishInitialization(). We may have to look at other
+ // values from parts of the schema that haven't been parsed yet, like map or item names.
+ m_pszRestrictionValue = pUsageKV->GetString( "restriction_value", NULL );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CEconTool_StrangePartRestriction::BFinishInitialization()
+{
+ // Run different parsers based on our restriction type.
+ switch ( m_eRestrictionType )
+ {
+ // We don't expect anything for our sub-value when dealing with Steam accounts. Asserting based on
+ // bad data is dumb but we don't really have any way of sending an error all the way up the tree.
+ case kStrangeEventRestriction_None:
+ case kStrangeEventRestriction_VictimSteamAccount:
+ if ( m_pszRestrictionValue )
+ return false;
+
+ m_unRestrictionValue = 0;
+ break;
+
+#if defined( TF_DLL ) || defined( TF_GC_DLL ) || defined( TF_CLIENT_DLL )
+ case kStrangeEventRestriction_Map:
+ {
+ if ( !m_pszRestrictionValue )
+ return false;
+
+ const MapDef_t *pSchemaMap = GetItemSchema()->GetMasterMapDefByName( m_pszRestrictionValue );
+ if ( !pSchemaMap )
+ return false;
+
+ m_unRestrictionValue = pSchemaMap->m_nDefIndex;
+ }
+ break;
+ case kStrangeEventRestriction_Competitive:
+ {
+ if (!m_pszRestrictionValue)
+ return false;
+
+ // Season string to int
+ m_unRestrictionValue = V_atoi(m_pszRestrictionValue);
+ }
+ break;
+#endif // defined( TF_DLL ) || defined( TF_GC_DLL ) || defined( TF_CLIENT_DLL )
+
+ default:
+ AssertMsg1( false, "CEconTool_StrangePartRestriction() doesn't understand how to parse restriction type %i.", m_eRestrictionType );
+ return false;
+ }
+
+ // We're done with this value now.
+ m_pszRestrictionValue = NULL;
+
+ return m_eRestrictionType != kStrangeEventRestriction_None
+ && m_unRestrictionValue != (unsigned int)-1
+ && IEconTool::BFinishInitialization();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CEconTool_StrangePartRestriction::CanApplyTo( const IEconItemInterface *pTool, const IEconItemInterface *pToolSubject ) const
+{
+ Assert( pTool );
+ Assert( pTool->GetItemDefinition() );
+ Assert( pToolSubject );
+
+ if ( !IEconTool::CanApplyTo( pTool, pToolSubject ) )
+ return false;
+
+ // Abort if for some reason we don't know what type of restriction we're trying to apply.
+ if ( m_eRestrictionType == kStrangeEventRestriction_None )
+ return false;
+
+ // Abort if we don't have our tool information. We'll need this below to avoid duplicating
+ // scores/restrictions.
+ const CEconTool_StrangePartRestriction *pToolRestriction = pTool->GetItemDefinition()->GetTypedEconTool<CEconTool_StrangePartRestriction>();
+ if ( !pToolRestriction )
+ return false;
+
+ // Make sure we're "strange" in that we have at least one attribute tracking scores. We can't
+ // explicitly test for quality here because now we can have strange vintages, etc.
+ if ( GetKillEaterAttrCount() <= 0 )
+ return false;
+
+ if ( !pToolSubject->FindAttribute( GetKillEaterAttr_Score( 0 ) ) )
+ return false;
+
+ // Look through all of the strange attributes on this item. We're looking for a slot that
+ // has a score that we're tracking where that score doesn't already have a restriction.
+ // If we find one, we can restrict that particular slot as long as doing so wouldn't generate
+ // a duplicate score (ie., "soldier kills on Hightower"). We don't need to enumerate them
+ // here, just find existence/nonexistence of at least one valid target.
+ for ( int i = 0; i < GetKillEaterAttrCount(); i++ )
+ {
+ if ( GetItemSchema()->BCanStrangeFilterApplyToStrangeSlotInItem( pToolRestriction->GetRestrictionType(), pToolRestriction->GetRestrictionValue(), pToolSubject, i, NULL ) )
+ return true;
+ }
+
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CEconTool_ItemDynamicRecipe::CEconTool_ItemDynamicRecipe( const char *pszTypeName, const char *pszUseString, item_capabilities_t unCapabilities, KeyValues *pUsageKV )
+ : IEconTool( pszTypeName, pszUseString, NULL, unCapabilities )
+{
+ COMPILE_TIME_ASSERT( sizeof( attrib_value_t ) == sizeof( uint32 ) );
+ COMPILE_TIME_ASSERT( sizeof( attrib_value_t ) == sizeof( float ) );
+
+ if ( pUsageKV )
+ {
+ BInitFromKV( pUsageKV, &m_vecErrors );
+ }
+}
+
+const char* CEconTool_ItemDynamicRecipe::CBaseRecipeComponent::m_pszUseParentNameIdentifier = "use_parents_item_def";
+
+//-----------------------------------------------------------------------------
+// Purpose: Make sure each of our components are fully formed. Return false
+// if any components fail BFinishInitialization_Internal()
+//-----------------------------------------------------------------------------
+bool CEconTool_ItemDynamicRecipe::BFinishInitialization()
+{
+ CBaseRecipeComponent::ComponentAttribVector_t attribVec;
+
+ FOR_EACH_VEC( m_vecComponents, i )
+ {
+ m_vecComponents[i]->BFinishInitialization_Internal( &m_vecErrors, &attribVec );
+ }
+
+ // Emit any errors we've accumulated during initialization
+ FOR_EACH_VEC( m_vecErrors, i )
+ {
+#ifdef GC_DLL
+ EmitError( SPEW_GC, "%s\n", m_vecErrors[i].Get() );
+#else
+ AssertMsg1( 0, "%s\n", m_vecErrors[i].Get() );
+#endif
+ }
+
+ if( m_vecErrors.Count() > 0 )
+ return false;
+
+ // Make sure we have at least one item required
+ return IEconTool::BFinishInitialization();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Iterate through attributes on pTool to see if any can accept pToolSubject.
+// Return true if any attributes match.
+//-----------------------------------------------------------------------------
+bool CEconTool_ItemDynamicRecipe::CanApplyTo( const IEconItemInterface *pTool, const IEconItemInterface *pToolSubject ) const
+{
+ Assert( pTool );
+ Assert( pToolSubject );
+
+ // Iterate through all the attributes on the tool and see if the subject item matches
+ // any of the recipe component attributes
+ CRecipeComponentMatchingIterator matchingIterator( pTool, pToolSubject );
+ pTool->IterateAttributes( &matchingIterator );
+
+ const CUtlVector< const CEconItemAttributeDefinition* >& vecMatchingAttribs = matchingIterator.GetMatchingComponentInputs();
+ // No matches, can't apply!
+ if( vecMatchingAttribs.Count() == 0 )
+ {
+ return false;
+ }
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CEconTool_ItemDynamicRecipe::CBaseRecipeComponent::CBaseRecipeComponent( bool bIsOutput, const CBaseRecipeComponent* pParent )
+ : m_bIsOutput( bIsOutput )
+ , m_flChanceOfApplying( 1.f )
+ , m_pParent( pParent )
+ , m_flTotalWeights( 0.f )
+ , m_eQuality( AE_UNDEFINED )
+ , m_attributesMatchingType( ATTRIBUTES_MATCH_NONE )
+{}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CEconTool_ItemDynamicRecipe::CBaseRecipeComponent::~CBaseRecipeComponent()
+{
+ m_vecAdditionalComponents.PurgeAndDeleteElements();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Roll chance of component applying to the tool
+//-----------------------------------------------------------------------------
+#ifdef GC_DLL
+int CEconTool_ItemDynamicRecipe::CBaseRecipeComponent::RollCount() const
+{
+ if( m_vecCountChances.Count() == 0 )
+ return 1;
+
+ float flRand = RandomFloat( 0.f, 1.f ) * m_flTotalWeights;
+ float flAccum = 0.f;
+
+ // Go through and see which counts gets rolled
+ FOR_EACH_VEC( m_vecCountChances, i )
+ {
+ const CountChance_t& countChance = m_vecCountChances[i];
+
+ flAccum += countChance.m_flChance;
+ if ( flRand <= flAccum )
+ {
+ // Winner! Roll within its range
+ return RandomInt( countChance.m_nMinCount, countChance.m_nMaxCount );
+ }
+ }
+
+ AssertMsg( 0, "Failed to generate a count for recipe component. Defaulting to 1" );
+ return 1;
+}
+
+bool CEconTool_ItemDynamicRecipe::CBaseRecipeComponent::RollChanceOfApplying() const
+{
+ // Guaranteed!
+ if( m_flChanceOfApplying == 1.f )
+ return true;
+
+ return RandomFloat() < m_flChanceOfApplying;
+}
+#endif
+
+//-----------------------------------------------------------------------------
+// Purpose: Set chance for attributes to apply.
+//-----------------------------------------------------------------------------
+void CEconTool_ItemDynamicRecipe::CBaseRecipeComponent::SetChanceOfApplying( float flChance )
+{
+ Assert( flChance >= 0.f && flChance <= 1.f );
+ clamp( flChance, 0.f, 1.f );
+ m_flChanceOfApplying = flChance;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CEconTool_ItemDynamicRecipe::CBaseRecipeComponent::BFinishInitialization_Internal( CUtlVector<CUtlString>* pVecErrors, ComponentAttribVector_t* pAttribVec )
+{
+ FOR_EACH_VEC( m_vecAdditionalComponents, i )
+ {
+ SCHEMA_INIT_SUBSTEP( m_vecAdditionalComponents[i]->BFinishInitialization_Internal( pVecErrors, pAttribVec ) );
+ }
+
+ return SCHEMA_INIT_SUCCESS();
+}
+
+void CEconTool_ItemDynamicRecipe::CBaseRecipeComponent::GetIsGuaranteed( int &nFlags ) const
+{
+ // If we've got a 100% chance of applying, or we're the root (no parent) then we
+ // can mark ourselves as guaranteed and continue to check our children to see if
+ // any of them are guaranteed as well.
+ if ( m_flChanceOfApplying == 1.f || !m_pParent )
+ {
+ nFlags |= GetIsOutput() ? GUARANTEED_OUTPUT : GUARANTEED_INPUT;
+
+ FOR_EACH_VEC( m_vecAdditionalComponents, i )
+ {
+ m_vecAdditionalComponents[i]->GetIsGuaranteed( nFlags );
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CEconTool_ItemDynamicRecipe::CDynamicRecipeComponentDefinedItem::CDynamicRecipeComponentDefinedItem( bool bIsOutput, const CBaseRecipeComponent* pParent )
+ : CBaseRecipeComponent( bIsOutput, pParent )
+{}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CEconTool_ItemDynamicRecipe::CDynamicRecipeComponentDefinedItem::~CDynamicRecipeComponentDefinedItem()
+{}
+
+//-----------------------------------------------------------------------------
+// Purpose: Make sure m_strName refers to an actual item def. Return false
+// if it doesn not. Child components are allowed to have "use_parents_item_def"
+// as their item name.
+//-----------------------------------------------------------------------------
+bool CEconTool_ItemDynamicRecipe::CDynamicRecipeComponentDefinedItem::BFinishInitialization_Internal( CUtlVector<CUtlString>* pVecErrors, ComponentAttribVector_t* pAttribVec )
+{
+ // Go through and make sure we have a bit of information that let's us describe an item
+ bool bAnyDataSet = false;
+
+ // Item def?
+ if ( m_pParent == NULL && GetItemSchema()->GetItemDefinitionByName( m_strName ) )
+ {
+ bAnyDataSet = true;
+ }
+ else if ( BStringsEqual( m_strName, m_pszUseParentNameIdentifier ) || GetItemSchema()->GetItemDefinitionByName( m_strName ) )
+ {
+ bAnyDataSet = true;
+ }
+
+ // Quality?
+ if ( m_eQuality != AE_UNDEFINED )
+ {
+ bAnyDataSet = true;
+ }
+
+ // Attributes?
+ if ( m_vecDynamicAttributes.Count() )
+ {
+ bAnyDataSet = true;
+ }
+
+ // We better have one of the above
+ SCHEMA_INIT_CHECK( bAnyDataSet, "Not enough data to describe component" );
+
+#ifdef GC_DLL
+ // Get next available attrib def for defining the item
+ CEconItemAttributeDefinition* pAttribDef = GetNextAvailableAttributeWithBaseName( GetAttributeName(), pAttribVec );
+ SCHEMA_INIT_CHECK( pAttribDef, "Too many potential components" );
+ if( pAttribDef )
+ {
+ pAttribVec->AddToTail( pAttribDef );
+ }
+#endif
+
+ SCHEMA_INIT_SUBSTEP( BaseClass::BFinishInitialization_Internal( pVecErrors, pAttribVec ) );
+
+ return SCHEMA_INIT_SUCCESS();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Parse out our item definition name and quality. Return false if
+// either does not exist
+//-----------------------------------------------------------------------------
+bool CEconTool_ItemDynamicRecipe::CDynamicRecipeComponentDefinedItem::ParseKV( KeyValues *pKV, CUtlVector<CUtlString> *pVecErrors )
+{
+ bool bNoItemDef = !!pKV->FindKey( "no_item_def" );
+ bool bItemName = !!pKV->FindKey( "item_name" );
+
+ // Make sure only one of the above is set
+ SCHEMA_INIT_CHECK( bNoItemDef != bItemName,
+ "Both \"no_item_def\" and \"item_name\" specified in component." );
+
+ // Make sure at least one of the above is set
+ SCHEMA_INIT_CHECK( bNoItemDef || bItemName,
+ "Neither \"no_item_def\" or \"item_name\" specified in component." );
+
+ // If they specified an item name, then we need to grab it
+ if ( bItemName )
+ {
+ m_strName = pKV->GetString( "item_name", NULL );
+ }
+
+ return BaseClass::ParseKV( pKV, pVecErrors );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Convert ourselves into an attribute. Return false if our encoded
+// attributes exceed the allocated space for attributes
+//-----------------------------------------------------------------------------
+#ifdef GC_DLL
+bool CEconTool_ItemDynamicRecipe::CDynamicRecipeComponentDefinedItem::AddRecipeComponentAsAttribute( CEconItem *pItem, const CEconGameAccount *pGameAccount ) const
+{
+ // Check if we should even apply
+ if( !RollChanceOfApplying() )
+ return true;
+
+ // Gather up all the current attributes on the item
+ ComponentAttribVector_t attribVec;
+ CRecipeComponentMatchingIterator matchingIterator( pItem, NULL );
+ pItem->IterateAttributes( &matchingIterator );
+ attribVec.AddVectorToTail( matchingIterator.GetMatchingComponentInputs() );
+ attribVec.AddVectorToTail( matchingIterator.GetMatchingComponentOutputs() );
+
+ // Get next available attrib def for defining the item
+ const CEconItemAttributeDefinition* pAttribDef = GetNextAvailableAttributeWithBaseName( GetAttributeName(), &attribVec );
+ if( !pAttribDef )
+ return false;
+
+ uint32 nFlags = 0;
+ CAttribute_DynamicRecipeComponent typedValue;
+
+ // Check if our item name is specified to use our parent's
+ const char* pszItemDefName = m_strName;
+ if ( m_strName && m_strName[0] )
+ {
+ if( BStringsEqual( m_pszUseParentNameIdentifier, pszItemDefName ) && m_pParent )
+ {
+ // It's only possible to have another CDynamicRecipeComponentDefinedItem as a parent
+ const CDynamicRecipeComponentDefinedItem* pParentDefinedItemComponent = dynamic_cast< const CDynamicRecipeComponentDefinedItem* >( m_pParent );
+ AssertMsg( pParentDefinedItemComponent, "Parent attribute passed into defined item component is not a defined item component" );
+ if( !pParentDefinedItemComponent )
+ {
+ return false;
+ }
+ // Adopt our parent's item name
+ pszItemDefName = pParentDefinedItemComponent->m_strName;
+ }
+
+ // Make sure this item def exists
+ CEconItemDefinition* pItemDef = GetItemSchema()->GetItemDefinitionByName( pszItemDefName );
+ AssertMsg1( pItemDef, "No item def named %s found when applying defined item component", pszItemDefName );
+ if( !pItemDef )
+ {
+ return false;
+ }
+
+ // Set the item def
+ typedValue.set_def_index( (uint32)pItemDef->GetDefinitionIndex() );
+ nFlags |= DYNAMIC_RECIPE_FLAG_PARAM_ITEM_DEF_SET;
+ }
+
+ // Set the quality, if we're supposed to
+ if ( m_eQuality != AE_UNDEFINED )
+ {
+ typedValue.set_item_quality( (uint32)m_eQuality );
+ nFlags |= DYNAMIC_RECIPE_FLAG_PARAM_QUALITY_SET;
+ }
+
+ nFlags |= m_bIsOutput ? DYNAMIC_RECIPE_FLAG_IS_OUTPUT : 0;
+
+ if ( m_attributesMatchingType == ATTRIBUTES_MATCH_ALL )
+ {
+ nFlags |= DYNAMIC_RECIPE_FLAG_PARAM_ATTRIBUTE_SET_ALL;
+ }
+ else if ( m_attributesMatchingType == ATTRIBUTES_MATCH_ANY )
+ {
+ nFlags |= DYNAMIC_RECIPE_FLAG_PARAM_ATTRIBUTE_SET_ANY;
+ }
+
+ // Make sure any of the flags (besides the output flag) is set
+ if ( ( nFlags & ~DYNAMIC_RECIPE_FLAG_IS_OUTPUT ) == 0 )
+ {
+ AssertMsg(0, "Created component without any data flags set!" );
+ return false;
+ }
+
+ typedValue.set_component_flags( nFlags );
+ typedValue.set_num_required( RollCount() );
+ typedValue.set_num_fulfilled( 0 );
+
+ // Write out attribute all attribute indexes and values, separated by what we expect to be an invalid character sequence
+ CUtlString strAttribs;
+ FOR_EACH_VEC( m_vecDynamicAttributes, i )
+ {
+ if( i != 0 )
+ {
+ strAttribs.Append( g_pszAttrEncodeSeparator );
+ }
+ // Convert all of our attributes into a string.
+ strAttribs.Append( CFmtStr( "%d", m_vecDynamicAttributes[i].m_AttrIndex ) );
+ strAttribs.Append( g_pszAttrEncodeSeparator );
+ strAttribs.Append( m_vecDynamicAttributes[i].m_strAttrData.Get() );
+ }
+
+ // Make sure we're not too long
+ if( strAttribs.Length() >= 1024 )
+ {
+ AssertMsg1( 0, "String-encoded attributes exceeds 1024 characters, when encoding component %s", m_strName.Get() );
+ return false;
+ }
+
+ // Set it in there!
+ typedValue.set_attributes_string( strAttribs.Get() );
+
+ // Check to see if we're about to create a duplicate. There's no need to spend another
+ // attribute to describe the same item. Let's instead just increase the count on the
+ // already-existing attribute.
+ FOR_EACH_VEC( attribVec, i )
+ {
+ CAttribute_DynamicRecipeComponent existingValue;
+ if( pItem->FindAttribute( attribVec[i], &existingValue ) )
+ {
+ if( typedValue.def_index() == existingValue.def_index() &&
+ typedValue.item_quality() == existingValue.item_quality() &&
+ typedValue.component_flags() == existingValue.component_flags() &&
+ typedValue.attributes_string() == existingValue.attributes_string() )
+ {
+ pAttribDef = attribVec[i];
+ existingValue.set_num_required( existingValue.num_required() + typedValue.num_required() );
+ typedValue = existingValue;
+ break;
+ }
+ }
+ }
+
+
+ pItem->SetDynamicAttributeValue( pAttribDef, typedValue );
+
+ // Go through and add any additional components that depend on this component
+ FOR_EACH_VEC( m_vecAdditionalComponents, i )
+ {
+ CBaseRecipeComponent* pAdditionalComponent = m_vecAdditionalComponents[i];
+ pAdditionalComponent->AddRecipeComponentAsAttribute( pItem, pGameAccount );
+ }
+
+ return true;
+}
+
+#endif
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CEconTool_ItemDynamicRecipe::CDynamicRecipeComponentLootList::CDynamicRecipeComponentLootList( bool bIsOutput, const CBaseRecipeComponent* pParent )
+ : CEconTool_ItemDynamicRecipe::CBaseRecipeComponent( bIsOutput, pParent )
+#ifdef GC_DLL
+ , m_eUniqueness( UNIQUE_AMONG_NOTHING )
+#endif
+{}
+
+
+
+CEconTool_ItemDynamicRecipe::CDynamicRecipeComponentLootList::~CDynamicRecipeComponentLootList()
+{}
+
+//-----------------------------------------------------------------------------
+// Purpose: Make sure m_strName actually refers to a lootlist
+//-----------------------------------------------------------------------------
+bool CEconTool_ItemDynamicRecipe::CDynamicRecipeComponentLootList::BFinishInitialization_Internal( CUtlVector<CUtlString>* pVecErrors, ComponentAttribVector_t* pAttribVec )
+{
+ // The game doesn't have all of the lootlists
+#ifdef GC_DLL
+ SCHEMA_INIT_CHECK( GEconItemSchema().GetLootListByName( m_strName ),
+ "CDynamicRecipeComponentLootList has invalid loot list: %s", m_strName.Get() );
+
+ // Get next available attrib def for defining the item
+ CEconItemAttributeDefinition* pAttribDef = GetNextAvailableAttributeWithBaseName( GetAttributeName(), pAttribVec );
+ SCHEMA_INIT_CHECK( pAttribDef, "Too many potential recipe components!" );
+ if( pAttribDef )
+ {
+ pAttribVec->AddToTail( pAttribDef );
+ }
+
+ // Inputs are not allowed to be marked UNIQUE_AMONG_OUTPUTS. Rather, mark the outputs UNIQUE_AMONG_INPUTS
+ SCHEMA_INIT_CHECK( !(m_eUniqueness == UNIQUE_AMONG_OUTPUTS && !GetIsOutput() ), "Input component marked to be unique among inputs. Not supported!" );
+#endif
+
+ // Skip defined item
+ SCHEMA_INIT_SUBSTEP( BaseClass::BFinishInitialization_Internal( pVecErrors, pAttribVec ) );
+
+ return SCHEMA_INIT_SUCCESS();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Parse in our lootlist name and quality. Return false if either doesn't exist
+//-----------------------------------------------------------------------------
+bool CEconTool_ItemDynamicRecipe::CDynamicRecipeComponentLootList::ParseKV( KeyValues *pKV, CUtlVector<CUtlString> *pVecErrors )
+{
+ const char* pszLootListName = pKV->GetString( "lootlist_name" );
+ SCHEMA_INIT_CHECK( pszLootListName != NULL,
+ "Missing lootlist name in lootlist recipe component" );
+
+ m_strName = pszLootListName;
+
+ bool bBaseResult = BaseClass::ParseKV( pKV, pVecErrors );
+
+#ifdef GC_DLL
+ // By default, outputs try to avoid rolling as input, and inputs dont try to avoid anything
+ m_eUniqueness = GetIsOutput() ? UNIQUE_AMONG_INPUTS : UNIQUE_AMONG_NOTHING;
+ const char* pszUniqueness = pKV->GetString( "uniqueness" );
+ if ( !V_stricmp( "unique_among_inputs", pszUniqueness ) )
+ {
+ m_eUniqueness = UNIQUE_AMONG_INPUTS;
+ }
+ else if ( !V_stricmp( "unique_among_outputs", pszUniqueness ) )
+ {
+ m_eUniqueness = UNIQUE_AMONG_OUTPUTS;
+ }
+ else if ( !V_stricmp( "unique_among_everything", pszUniqueness ) )
+ {
+ m_eUniqueness = UNIQUE_AMONG_EVERYTHING;
+ }
+#endif
+
+ return bBaseResult;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Roll our item definition, then call our base to convert ourselves into an attribute
+//-----------------------------------------------------------------------------
+#ifdef GC_DLL
+bool CEconTool_ItemDynamicRecipe::CDynamicRecipeComponentLootList::AddRecipeComponentAsAttribute( CEconItem *pItem, const CEconGameAccount *pGameAccount ) const
+{
+ // Check if we should even apply
+ if( !RollChanceOfApplying() )
+ return true;
+
+ // See if there's any item defs we should try to avoid
+ CRecipeComponentInputDefIndexIterator inputIterator( m_eUniqueness );
+ pItem->IterateAttributes( &inputIterator );
+
+ const char* pszItemDefName = NULL;
+ // Roll the item and any additional attributes
+ CUtlVector< StringEncodedAttribute_t > vecLootlistGeneratedAttributes;
+ if (!RollLootlistItemAndAttributes( vecLootlistGeneratedAttributes, &pszItemDefName, &inputIterator.GetMatchingComponentInputs(), pGameAccount ) )
+ {
+ return false;
+ }
+ vecLootlistGeneratedAttributes.AddVectorToTail( m_vecDynamicAttributes );
+
+ // Create a temporary defined item component based on the lootlist roll
+ CDynamicRecipeComponentDefinedItem definedItem( m_bIsOutput, this );
+ definedItem.m_eQuality = m_eQuality;
+ definedItem.m_strName = pszItemDefName;
+ definedItem.m_attributesMatchingType = m_attributesMatchingType;
+ definedItem.m_vecDynamicAttributes = vecLootlistGeneratedAttributes;
+ definedItem.m_vecCountChances = m_vecCountChances;
+ definedItem.m_flTotalWeights = m_flTotalWeights;
+
+ // Write out this defined item
+ definedItem.AddRecipeComponentAsAttribute( pItem, pGameAccount );
+
+ // Create any additional components that depend on this component existing
+ FOR_EACH_VEC( m_vecAdditionalComponents, i )
+ {
+ CBaseRecipeComponent* pAdditionalComponent = m_vecAdditionalComponents[i];
+ // Update the parent to be the item we generated
+ pAdditionalComponent->SetParent( &definedItem );
+ pAdditionalComponent->AddRecipeComponentAsAttribute( pItem, pGameAccount );
+ }
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Roll our item definition, then call our base to convert ourselves into an attribute
+//-----------------------------------------------------------------------------
+class CAttributeToStringIterator : public IEconItemUntypedAttributeIterator
+{
+public:
+ CAttributeToStringIterator( CUtlVector< CEconTool_ItemDynamicRecipe::CDynamicRecipeComponentLootList::StringEncodedAttribute_t >& vecAdditionalAttribs, CEconItem* pItem )
+ : m_vecAdditionalAttribs( vecAdditionalAttribs )
+ , m_pItem( pItem )
+ {}
+
+ virtual bool OnIterateAttributeValueUntyped( const CEconItemAttributeDefinition *pAttrDef )
+ {
+ const CEconItem::attribute_t *pAttrInternalData = m_pItem->FindDynamicAttributeInternal( pAttrDef );
+
+ // Only export attributes that we have dynamic data for.
+ if ( pAttrInternalData )
+ {
+ const ISchemaAttributeType *pAttrType = pAttrDef->GetAttributeType();
+ Assert( pAttrType );
+
+ // Set the definition index
+ auto& attrib = m_vecAdditionalAttribs[ m_vecAdditionalAttribs.AddToTail() ];
+ attrib.m_AttrIndex = pAttrDef->GetDefinitionIndex();
+
+ // Convert the value to a string
+ std::string sAttrValue;
+ pAttrType->ConvertEconAttributeValueToString( pAttrDef, pAttrInternalData->m_value, &sAttrValue );
+
+ Assert( sAttrValue.length() > 0 );
+ attrib.m_strAttrData.Set( sAttrValue.c_str() );
+ }
+
+ return true;
+ }
+
+private:
+
+ CUtlVector< CEconTool_ItemDynamicRecipe::CDynamicRecipeComponentLootList::StringEncodedAttribute_t >& m_vecAdditionalAttribs;
+ CEconItem* m_pItem;
+};
+
+//-----------------------------------------------------------------------------
+// Purpose: Roll our item definition. This will give us a list of item definition. From this list
+// we take the first one and set our item def name and quality to be its. We then take any
+// attributes that it may have rolled and add them to our attributes as well. Return false
+// if any of these steps fail.
+//-----------------------------------------------------------------------------
+bool CEconTool_ItemDynamicRecipe::CDynamicRecipeComponentLootList::RollLootlistItemAndAttributes( CUtlVector< StringEncodedAttribute_t >& vecAdditionalAttribs
+ , const char** pszDefName
+ , const CUtlVector< item_definition_index_t > *pVecAvoidItemDefs
+ , const CEconGameAccount *pGameAccount ) const
+{
+ const CEconLootListDefinition* pLootList = GEconItemSchema().GetLootListByName( m_strName );
+
+ if( !pLootList )
+ {
+ AssertMsg1( 0, "Lootlist %s not found when adding dynamic attribute", m_strName.Get() );
+ return false;
+ }
+
+ CUtlVector<CEconItem *> vecRolledItems;
+ // Roll our items
+ CDefaultUniformRandomStream RandomStream;
+ if ( !pLootList->BGenerateSingleRollRandomItems( pGameAccount, false, &vecRolledItems ) )
+ {
+ AssertMsg1( 0, "Error generating item defs from lootlist \"%s\"", m_strName.Get() );
+ return false;
+ }
+
+ // We're just going to use the first one
+ CEconItem* pGeneratedItem = vecRolledItems.Head();
+
+ // Set our name and quality
+ (*pszDefName) = pGeneratedItem->GetItemDefinition()->GetDefinitionName();
+ const_cast<CDynamicRecipeComponentLootList*>(this)->m_eQuality = (EEconItemQuality)pGeneratedItem->GetQuality();
+
+ // Sniff and encode the attributes
+ CAttributeToStringIterator attrToString( vecAdditionalAttribs, pGeneratedItem );
+ pGeneratedItem->IterateAttributes( &attrToString );
+
+ // Cleanup
+ for( auto pItem : vecRolledItems )
+ {
+ delete pItem;
+ }
+
+ return true;
+}
+
+#endif
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Delete all the things
+//-----------------------------------------------------------------------------
+CEconTool_ItemDynamicRecipe::~CEconTool_ItemDynamicRecipe()
+{
+ m_vecComponents.PurgeAndDeleteElements();
+}
+
+CEconTool_ItemDynamicRecipe::CRecipeComponentInputDefIndexIterator::CRecipeComponentInputDefIndexIterator( EItemDefUniqueness_t eUniqueness )
+ : m_eUniqueness( eUniqueness )
+{}
+
+bool CEconTool_ItemDynamicRecipe::CRecipeComponentInputDefIndexIterator::OnIterateAttributeValue( const CEconItemAttributeDefinition *pAttrDef, const CAttribute_DynamicRecipeComponent& value )
+{
+ // We dont care
+ if ( m_eUniqueness == CEconTool_ItemDynamicRecipe::UNIQUE_AMONG_NOTHING )
+ return true;
+
+ // Only check against outputs
+ if ( m_eUniqueness == CEconTool_ItemDynamicRecipe::UNIQUE_AMONG_OUTPUTS && !( value.component_flags() & DYNAMIC_RECIPE_FLAG_IS_OUTPUT ) )
+ return true;
+
+ // Only check against inputs
+ if ( m_eUniqueness == CEconTool_ItemDynamicRecipe::UNIQUE_AMONG_INPUTS && value.component_flags() & DYNAMIC_RECIPE_FLAG_IS_OUTPUT )
+ return true;
+
+ // Add this item def if we haven't seen it
+ if ( m_vecInputItemDefs.Find( value.def_index() ) == m_vecInputItemDefs.InvalidIndex() )
+ {
+ m_vecInputItemDefs.AddToTail( value.def_index() );
+ }
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Parse all of our inputs then outputs
+//-----------------------------------------------------------------------------
+bool CEconTool_ItemDynamicRecipe::BInitFromKV( KeyValues *pKVDef, CUtlVector<CUtlString> *pVecErrors )
+{
+ // Parse the components blocks
+ SCHEMA_INIT_SUBSTEP( CBaseRecipeComponent::ParseComponentsBlock( pKVDef, m_vecComponents, pVecErrors, NULL ) );
+
+ // Sort the component vector such that inputs go first
+ struct RecipeComponentSorter
+ {
+ static int SortRecipeComponentVector( CEconTool_ItemDynamicRecipe::CBaseRecipeComponent* const *pComponent1, CEconTool_ItemDynamicRecipe::CBaseRecipeComponent* const *pComponent2 )
+ {
+ return (*pComponent1)->GetIsOutput() && !(*pComponent2)->GetIsOutput();
+ }
+ };
+
+ m_vecComponents.Sort( &RecipeComponentSorter::SortRecipeComponentVector );
+
+#ifdef GC_DLL
+ int nFlags = 0;
+ FOR_EACH_VEC( m_vecComponents, i )
+ {
+ m_vecComponents[i]->GetIsGuaranteed( nFlags );
+ }
+
+ SCHEMA_INIT_CHECK( nFlags & GUARANTEED_OUTPUT, "No guaranteed outputs for dynamic recipe" );
+ SCHEMA_INIT_CHECK( nFlags & GUARANTEED_INPUT, "No guaranteed inputs for dynamic recipe" );
+#endif
+
+ return SCHEMA_INIT_SUCCESS();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Parse the component blocks, determining if this component is an input or output
+//-----------------------------------------------------------------------------
+bool CEconTool_ItemDynamicRecipe::CBaseRecipeComponent::ParseComponentsBlock( KeyValues *pKV, CUtlVector<CBaseRecipeComponent*>& vecComponents, CUtlVector<CUtlString> *pVecErrors, const CBaseRecipeComponent* pParent )
+{
+ // The components block doesn't exist on the client
+
+ KeyValues *pKVComponents = pKV->FindKey( "components" );
+#ifdef GC_DLL
+ SCHEMA_INIT_CHECK( pKVComponents || pParent, "Failed to parse components block in dynamic recipe" );
+#endif
+ if ( pKVComponents )
+ {
+ // There's duplicate code when we read in like this, but it makes the
+ // item defs much easier to read
+
+ // Parse all the inputs
+ KeyValues *pKVParameters = pKVComponents->FindKey( "input" );
+ if( pKVParameters )
+ {
+ ParseComponents( pKVParameters, vecComponents, false, pVecErrors, pParent );
+ }
+
+#ifdef GC_DLL
+ // Parse all the outputs
+ pKVParameters = pKVComponents->FindKey( "output" );
+ if( pKVParameters )
+ {
+ ParseComponents( pKVParameters, vecComponents, true, pVecErrors, pParent );
+ }
+#endif
+ }
+
+
+
+ return SCHEMA_INIT_SUCCESS();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Create the appropriate component types and have them parse themselves.
+// Returns true if at least one of the components parsed has a 100% chance
+// of applying, and we don't have a parent
+//-----------------------------------------------------------------------------
+bool CEconTool_ItemDynamicRecipe::CBaseRecipeComponent::ParseComponents( KeyValues *pKV, CUtlVector<CBaseRecipeComponent*>& vecComponents, bool bIsOutput, CUtlVector<CUtlString> *pVecErrors, const CBaseRecipeComponent* pParent )
+{
+ // Go through each entry in the tool
+ KeyValues *pEntry = pKV->GetFirstSubKey();
+ while( pEntry )
+ {
+ // Find which type is being specified.
+ CBaseRecipeComponent *pComponent = NULL;
+ if( pEntry->FindKey( "lootlist_name" ) )
+ {
+ pComponent = vecComponents[vecComponents.AddToTail( new CDynamicRecipeComponentLootList( bIsOutput, pParent ) )];
+ }
+ else if ( pEntry->FindKey( "item_name" ) || pEntry->FindKey( "no_item_def" ) )
+ {
+ pComponent = vecComponents[vecComponents.AddToTail( new CDynamicRecipeComponentDefinedItem( bIsOutput, pParent ) )];
+ }
+ else
+ {
+ SCHEMA_INIT_CHECK( false, "Unrecognized recipe component type!" );
+ }
+
+ // Now that we've got the right type, parse!
+ if( pComponent )
+ {
+ pComponent->ParseKV( pEntry, pVecErrors );
+ }
+
+ pEntry = pEntry->GetNextKey();
+ }
+
+ return SCHEMA_INIT_SUCCESS();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Parse in all of the attributes for this component. Attributes have
+// already been parsed in, so we can immediately check if the attribute exists.
+//-----------------------------------------------------------------------------
+bool CEconTool_ItemDynamicRecipe::CBaseRecipeComponent::ParseKV( KeyValues *pKV, CUtlVector<CUtlString> *pVecErrors )
+{
+ // Get the quality string
+ const char* pszQuality = pKV->GetString( "quality", NULL );
+ if ( pszQuality )
+ {
+ // Convert the quality string to a item quality
+ m_eQuality = EconQuality_GetQualityFromString( pszQuality );
+ SCHEMA_INIT_CHECK( m_eQuality != AE_UNDEFINED, "Invalid item quality \"%s\" specified for component \"%s\"", pszQuality, m_strName.Get() );
+ }
+
+ const char* pszAttributesMatchingType = pKV->GetString( "attributes_matching_type", NULL );
+ if ( pszAttributesMatchingType )
+ {
+ if ( !V_stricmp( pszAttributesMatchingType, "all" ) )
+ {
+ m_attributesMatchingType = ATTRIBUTES_MATCH_ALL;
+ }
+ else if ( !V_stricmp( pszAttributesMatchingType, "any" ) )
+ {
+ m_attributesMatchingType = ATTRIBUTES_MATCH_ANY;
+ }
+ else
+ {
+ SCHEMA_INIT_CHECK( 0, "Invalid attributes_matching_type \"%s\"", pszAttributesMatchingType );
+ }
+ }
+
+ // Parse all of our attributes
+ KeyValues* pKVAttribs = pKV->FindKey( "attributes" );
+ if( pKVAttribs )
+ {
+ FOR_EACH_SUBKEY( pKVAttribs, pKVAttribute )
+ {
+ static_attrib_t staticAttrib;
+
+ SCHEMA_INIT_SUBSTEP( staticAttrib.BInitFromKV_SingleLine( "attributes", pKVAttribute, pVecErrors ) );
+
+ const CEconItemAttributeDefinition * pAttrDef = staticAttrib.GetAttributeDefinition();
+ SCHEMA_INIT_CHECK( pAttrDef != NULL, "Attribute index %i not found when specifying dynamic recipe", staticAttrib.iDefIndex );
+
+ StringEncodedAttribute_t& attrib = m_vecDynamicAttributes[ m_vecDynamicAttributes.AddToTail() ];
+ attrib.m_AttrIndex = pAttrDef->GetDefinitionIndex();
+ attrib.m_strAttrData = pKVAttribs->GetString( pKVAttribute->GetName(), "" );
+
+ Assert( !attrib.m_strAttrData.IsEmpty() );
+ }
+ }
+
+ // Get our chance of applying. Default to 100%
+ float flChance = pKV->GetFloat( "chance", 1.f );
+ // Make sure it's in the range (0,1]
+ SCHEMA_INIT_CHECK( flChance > 0.f && flChance <= 1.f, "Recipe component chance to apply out of bounds: %f", flChance );
+ SetChanceOfApplying( flChance );
+
+ // Read in the "counts" block if it exists
+ KeyValues *pKVCounts = pKV->FindKey( "counts" );
+ if( pKVCounts )
+ {
+ // Read check count entry
+ FOR_EACH_SUBKEY( pKVCounts, pKVEntry )
+ {
+ // Split out count range of the format "#-#", or just "#"
+ CUtlStringList vecCount;
+ const char* pszCount = pKVEntry->GetName();
+ V_SplitString( pszCount, "-", vecCount );
+ SCHEMA_INIT_CHECK( vecCount.Count() == 1 || vecCount.Count() == 2, "Malformed count value: %s", pszCount );
+
+ CountChance_t& countChance = m_vecCountChances[ m_vecCountChances.AddToTail() ];
+
+ // Add up the chances
+ countChance.m_flChance = (float)pKVEntry->GetInt();
+ m_flTotalWeights += countChance.m_flChance;
+ // Set the min and the max. If no max is specifid, then max is the min
+ countChance.m_nMinCount = V_atoi( vecCount[0] );
+ countChance.m_nMaxCount = vecCount.Count() > 1 ? V_atoi( vecCount[1] ) : countChance.m_nMinCount;
+ // Make sure max >= min and min > 0
+ SCHEMA_INIT_CHECK( countChance.m_nMaxCount >= countChance.m_nMinCount, "Recipe component count max is less than the min: %s", pszCount );
+ SCHEMA_INIT_CHECK( countChance.m_nMinCount > 0, "Recipe component count min less than 0: %s", pszCount );
+ }
+ }
+
+ // Init any components we may have in us
+ SCHEMA_INIT_CHECK( ParseComponentsBlock( pKV, m_vecAdditionalComponents, pVecErrors, this ), "Failed to parse nested components block in dynamic recipe" );
+
+ return SCHEMA_INIT_SUCCESS();
+}
+
+
+#ifdef GC_DLL
+//-----------------------------------------------------------------------------
+// Purpose: This tool will apply recipe components as attributes. Go through each
+// of our attributes, roll to see if it applies, and convert it to an attribute
+// and add it to the item if we do. Return false if we ever fail.
+//-----------------------------------------------------------------------------
+bool CEconTool_ItemDynamicRecipe::BGenerateDynamicAttributes( CEconItem* pItem, const CEconGameAccount *pGameAccount ) const
+{
+ // Go through our inputs and write them out into attributes
+ FOR_EACH_VEC( m_vecComponents, i )
+ {
+ if( !m_vecComponents[i]->AddRecipeComponentAsAttribute( pItem, pGameAccount ) )
+ return false;
+ }
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Scan the item for numbered attributes with the passed in base name.
+// Return the attribute of the next available index or NULL if there are
+// none available
+//-----------------------------------------------------------------------------
+CEconItemAttributeDefinition* CEconTool_ItemDynamicRecipe::CBaseRecipeComponent::GetNextAvailableAttributeWithBaseName( const char* pszBaseAttribName, ComponentAttribVector_t* pAttribVec )
+{
+ Assert( pAttribVec );
+ if( !pAttribVec )
+ return NULL;
+
+ CEconItemAttributeDefinition *pAttribDef = NULL;
+ int i=1;
+ while( pAttribDef == NULL )
+ {
+ const char* pszAttribName = CFmtStr( "%s %d", pszBaseAttribName, i++ );
+
+ CEconItemAttributeDefinition *pTempAttribDef = GEconItemSchema().GetAttributeDefinitionByName( pszAttribName );
+
+ // Check to see if this attribute we're talking about even exists
+ if( !pTempAttribDef )
+ {
+ return NULL;
+ }
+
+ // Check if the vector doesn't have this attribute. If not, it's available
+ if( pAttribVec->Find( pTempAttribDef ) == pAttribVec->InvalidIndex() )
+ {
+ pAttribDef = pTempAttribDef;
+ }
+ }
+
+ return pAttribDef;
+}
+#endif
+
+//-----------------------------------------------------------------------------
+bool CEconTool_Xifier::CanApplyTo( const IEconItemInterface *pTool, const IEconItemInterface *pToolSubject ) const
+{
+ Assert( pTool );
+ Assert( pToolSubject );
+
+ const CEconItemDefinition *pSubjectItemDef = pToolSubject->GetItemDefinition();
+ if ( !pSubjectItemDef )
+ return false;
+
+ // Check to make sure the definition of the item we're trying to apply to has the tags we need to match.
+ FOR_EACH_VEC( m_RequiredTags.GetTagsList(), i )
+ {
+ if ( !pSubjectItemDef->HasEconTag( m_RequiredTags.GetTagsList()[i] ) )
+ return false;
+ }
+
+ // If this xifier has target restrictions, ensure our subject is one of them
+ if ( m_ItemDefTargetRestrictions.Count() > 0 )
+ {
+ bool bPassed = false;
+ FOR_EACH_VEC( m_ItemDefTargetRestrictions, i )
+ {
+ const CEconItemDefinition *pTargetItemDef = GetItemSchema()->GetItemDefinition( m_ItemDefTargetRestrictions[i] );
+ if ( ItemDefMatch( pSubjectItemDef, pTargetItemDef ) )
+ {
+ bPassed = true;
+ break;
+ }
+ }
+
+ if ( bPassed == false )
+ return false;
+ }
+
+ // if rarity restriction, target needs rarity of this or lower
+ if ( m_ItemRarityRestriction != k_unItemRarity_Any )
+ {
+ uint8 unSubjectRarity = pToolSubject->GetItemDefinition()->GetRarity();
+ if ( unSubjectRarity == k_unItemRarity_Any || unSubjectRarity == 0 || unSubjectRarity > m_ItemRarityRestriction )
+ return false;
+
+ // needs to be equippable
+ static CSchemaAttributeDefHandle pAttribDef_StatModule( "weapon_uses_stattrak_module" );
+ if ( !pToolSubject->FindAttribute( pAttribDef_StatModule ) )
+ return false;
+ }
+
+ // Check if we have a restriction as an attribute
+ static CSchemaAttributeDefHandle pAttribDef_ToolTargetItem( "tool target item" );
+ float value;
+ if ( FindAttribute_UnsafeBitwiseCast<attrib_value_t>( pTool, pAttribDef_ToolTargetItem, &value ) )
+ {
+ const CEconItemDefinition *pTargetDef = GetItemSchema()->GetItemDefinition( value );
+
+ // Check for a match (might have NULL here for target definition but that's safe to pass in)
+ if ( !ItemDefMatch( pSubjectItemDef, pTargetDef ) )
+ {
+ return false;
+ }
+ }
+
+ return IEconTool::CanApplyTo( pTool, pToolSubject );
+}
+
+//-----------------------------------------------------------------------------
+bool CEconTool_Xifier::ItemDefMatch( const CEconItemDefinition* pTargetItemDef, const CEconItemDefinition* pSubjectItemDef ) const
+{
+ if ( pTargetItemDef && pSubjectItemDef )
+ {
+ // Item def match counts
+ if ( pTargetItemDef == pSubjectItemDef )
+ return true;
+
+ // If these item defs have the same XifierRemapClass then they are allowed to match as well
+ if ( pTargetItemDef->GetXifierRemapClass() && *pTargetItemDef->GetXifierRemapClass()
+ && pSubjectItemDef->GetXifierRemapClass() && *pSubjectItemDef->GetXifierRemapClass() )
+ {
+ if (BStringsEqual(pSubjectItemDef->GetXifierRemapClass(), pTargetItemDef->GetXifierRemapClass()))
+ return true;
+ }
+ }
+
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+bool CEconTool_Strangifier::CanApplyTo( const IEconItemInterface *pTool, const IEconItemInterface *pToolSubject ) const
+{
+ Assert( pTool );
+ Assert( pToolSubject );
+
+ // Do not allow for already strange items
+ if ( pToolSubject->GetQuality() == AE_STRANGE )
+ return false;
+
+ // Go over the attributes of the item, if it has any strange attributes the item is strange and don't apply
+ for ( int i = 0; i < GetKillEaterAttrCount(); i++ )
+ {
+ if ( pToolSubject->FindAttribute( GetKillEaterAttr_Score( i ) ) )
+ {
+ return false;
+ }
+ }
+
+ // Default rules
+ return CEconTool_Xifier::CanApplyTo( pTool, pToolSubject );
+}
+
+//-----------------------------------------------------------------------------
+bool CEconTool_KillStreakifier::CanApplyTo( const IEconItemInterface *pTool, const IEconItemInterface *pToolSubject ) const
+{
+ Assert( pTool );
+ Assert( pToolSubject );
+
+ // Make sure the item doesn't already have an effect
+ static CSchemaAttributeDefHandle pAttribDef_KillStreakEffect( "killstreak tier" );
+ float flEffectIndex = 0.0;
+ if ( FindAttribute_UnsafeBitwiseCast<attrib_value_t>( pToolSubject, pAttribDef_KillStreakEffect, &flEffectIndex ) )
+ return false;
+
+ // Default rules
+ return CEconTool_Xifier::CanApplyTo( pTool, pToolSubject );
+}
+
+//-----------------------------------------------------------------------------
+bool CEconTool_Festivizer::CanApplyTo( const IEconItemInterface *pTool, const IEconItemInterface *pToolSubject ) const
+{
+ Assert( pTool );
+ Assert( pToolSubject );
+
+ const CEconItemDefinition *pSubjectItemDef = pToolSubject->GetItemDefinition();
+ if ( !pSubjectItemDef )
+ return false;
+
+ // Make sure the item doesn't already have an effect
+ static CSchemaAttributeDefHandle pAttribDef_Festivizer( "is_festivized" );
+ if ( FindAttribute( pToolSubject, pAttribDef_Festivizer ) )
+ return false;
+
+ // Default rules
+ return CEconTool_Xifier::CanApplyTo( pTool, pToolSubject );
+}
+
+bool CEconTool_Unusualifier::CanApplyTo( const IEconItemInterface *pTool, const IEconItemInterface *pToolSubject ) const
+{
+ Assert( pTool );
+ Assert( pToolSubject );
+
+ // don't stomp item that's already unusual
+ if ( pToolSubject->GetQuality() == AE_UNUSUAL )
+ return false;
+
+ // Default rules
+ return CEconTool_Xifier::CanApplyTo( pTool, pToolSubject );
+}
+
+//-----------------------------------------------------------------------------
+bool CEconTool_ItemEaterRecharger::CanApplyTo( const IEconItemInterface *pTool, const IEconItemInterface *pToolSubject ) const
+{
+ Assert( pTool );
+ Assert( pToolSubject );
+
+ const CEconItemDefinition *pSubjectItemDef = pToolSubject->GetItemDefinition();
+ if ( !pSubjectItemDef )
+ return false;
+
+ // Check to make sure the definition of the item we're trying to apply to has the tags we need to match.
+ FOR_EACH_VEC( m_RequiredTags.GetTagsList(), i )
+ {
+ if ( !pSubjectItemDef->HasEconTag( m_RequiredTags.GetTagsList()[i] ) )
+ return false;
+ }
+
+ // If this eater has target restrictions, ensure our subject is one of them
+ if ( m_ItemDefTargetRestrictions.Count() > 0 )
+ {
+ bool bPassed = false;
+ item_definition_index_t iSubject = pSubjectItemDef->GetDefinitionIndex();
+ FOR_EACH_VEC( m_ItemDefTargetRestrictions, i )
+ {
+ if ( m_ItemDefTargetRestrictions[i] == iSubject )
+ {
+ bPassed = true;
+ break;
+ }
+ }
+
+ if ( bPassed == false )
+ return false;
+ }
+
+ return IEconTool::CanApplyTo( pTool, pToolSubject );
+}
+
+//-----------------------------------------------------------------------------
+int CEconTool_ItemEaterRecharger::GetChargesForItemDefId( item_definition_index_t defIndex ) const
+{
+ FOR_EACH_VEC( m_ItemDefTargetRestrictions, i )
+ {
+ if ( m_ItemDefTargetRestrictions[i] == defIndex )
+ {
+ return m_ItemDefTargetChargeValues[i];
+ }
+ }
+ return 0;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CEconTool_UpgradeCard::CanApplyTo( const IEconItemInterface *pTool, const IEconItemInterface *pToolSubject ) const
+{
+ Assert( pTool );
+ Assert( pToolSubject );
+
+ const CEconItemDefinition *pSubjectItemDef = pToolSubject->GetItemDefinition();
+ if ( !pSubjectItemDef )
+ return false;
+
+ // Abort if we're trying to apply to a base item.
+ if ( pSubjectItemDef->IsBaseItem() )
+ return false;
+
+ // Abort if for some reason we don't know what type of attribute we would attach.
+ if ( m_vecAttributes.Count() <= 0 )
+ return false;
+
+ // Make sure that none of the attributes we're going to try to apply already exist on the item. We don't
+ // allow double-stacking the same attribute partially for balance purposes, but also because the database
+ // back-end doesn't support two attributes of the same type on the same item.
+ FOR_EACH_VEC( m_vecAttributes, i )
+ {
+ if ( pToolSubject->FindAttribute( m_vecAttributes[i].m_pAttrDef ) )
+ return false;
+ }
+
+ // Make sure the item that we're thinking of applying to has enough room to have another card's
+ // worth of items attached. We do this in sort of a roundabout way, by having the attributes themselves
+ // know whether they came from a card or not.
+ CCountUserGeneratedAttributeIterator countIterator;
+ pToolSubject->IterateAttributes( &countIterator );
+
+ if ( countIterator.GetCount() >= GetMaxCardUpgradesPerItem() )
+ return false;
+
+ // Check to make sure the definition of the item we're trying to apply to has the tags we need
+ // to match.
+ FOR_EACH_VEC( m_RequiredTags.GetTagsList(), i )
+ {
+ if ( !pSubjectItemDef->HasEconTag( m_RequiredTags.GetTagsList()[i] ) )
+ return false;
+ }
+
+ return IEconTool::CanApplyTo( pTool, pToolSubject );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CEconTool_ClassTransmogrifier::CanApplyTo( const IEconItemInterface *pTool, const IEconItemInterface *pToolSubject ) const
+{
+ Assert( pTool );
+ Assert( pToolSubject );
+
+ const GameItemDefinition_t *pSubjectItemDef = pToolSubject->GetItemDefinition();
+ if ( !pSubjectItemDef )
+ return false;
+
+ // Abort if we're trying to apply to a base item.
+ if ( pSubjectItemDef->IsBaseItem() )
+ return false;
+
+ // Abort if we're trying to apply to a Self-Made or Community item
+ if ( pToolSubject->GetQuality() == AE_SELFMADE ||
+ pToolSubject->GetQuality() == AE_COMMUNITY )
+ {
+ return false;
+ }
+
+ // Abort if we somehow got here before we know what class we were trying to produce items for.
+ if ( m_iClass <= 0 || m_iClass >= LOADOUT_COUNT )
+ return false;
+
+ // Check to make sure the definition of the item we're trying to apply to has the tags we need
+ // to match.
+ FOR_EACH_VEC( m_RequiredTags.GetTagsList(), i )
+ {
+ if ( !pSubjectItemDef->HasEconTag( m_RequiredTags.GetTagsList()[i] ) )
+ return false;
+ }
+
+ return IEconTool::CanApplyTo( pTool, pToolSubject );
+}
+
+//-----------------------------------------------------------------------------
+bool CEconTool_DuckToken::CanApplyTo( const IEconItemInterface *pTool, const IEconItemInterface *pToolSubject ) const
+{
+ Assert( pTool );
+ Assert( pToolSubject );
+
+ static CSchemaAttributeDefHandle pAttrDef_DuckBadgeLevel( "duck badge level" );
+ uint32 unOldBadgeLevel = 0;
+ if ( !FindAttribute( pToolSubject, pAttrDef_DuckBadgeLevel, &unOldBadgeLevel ) )
+ return false;
+
+ if ( unOldBadgeLevel >= 5 )
+ return false;
+
+ // Default rules
+ return IEconTool::CanApplyTo( pTool, pToolSubject );
+}
+
+//---------------------------------------------------------------------------------------
+// Purpose: given a tool and an item to apply the tool's effects upon, return true if the
+// tool is allowed to affect the subject. This is used on the client for UI and
+// on the GC for actual application validity testing.
+//---------------------------------------------------------------------------------------
+/* static */ bool CEconSharedToolSupport::ToolCanApplyTo( const IEconItemInterface *pTool, const IEconItemInterface *pToolSubject )
+{
+ if ( pTool == NULL || pToolSubject == NULL )
+ return false;
+
+ const GameItemDefinition_t *pToolDef = pTool->GetItemDefinition();
+ if ( pToolDef == NULL )
+ return false;
+
+ // If we have a tool that's in escrow it can't be used on anything.
+ static CSchemaAttributeDefHandle pAttrib_ToolEscrowUntil( "tool escrow until date" );
+ if ( pTool->FindAttribute( pAttrib_ToolEscrowUntil ) )
+ return false;
+
+ if ( pToolSubject->IsTemporaryItem() )
+ return false;
+
+ // Cannot modify preview items. Should be caught by temporary-item check above.
+ Assert( pToolSubject->GetOrigin() != kEconItemOrigin_PreviewItem );
+
+ const GameItemDefinition_t *pToolSubjectDef = pToolSubject->GetItemDefinition();
+ if ( pToolSubjectDef == NULL )
+ return false;
+
+ if ( !ToolCanApplyToDefinition( pToolDef, pToolSubjectDef ) )
+ return false;
+
+ // If we can apply to the definition then we should be known to have valid tool data.
+ // If our tool has no tool metadata then we don't allow it to be applied to anything.
+ const IEconTool *pEconTool = pToolDef->GetEconTool();
+ Assert( pEconTool );
+
+ return pEconTool->CanApplyTo( pTool, pToolSubject );
+}
+
+/* static */ bool CEconSharedToolSupport::ToolCanApplyToDefinition( const GameItemDefinition_t *pToolDef, const GameItemDefinition_t *pToolSubjectDef )
+{
+ if ( !pToolDef || !pToolSubjectDef || !pToolDef->IsTool() )
+ {
+ // not a tool
+ return false;
+ }
+
+ // If our tool has no tool metadata then we don't allow it to be applied to anything.
+ const IEconTool *pEconTool = pToolDef->GetEconTool();
+ if ( !pEconTool )
+ return false;
+
+ unsigned int unToolUsageCaps = 0;
+ if ( pEconTool->GetCapabilities() )
+ {
+ for ( unsigned int i = 0; i < NUM_ITEM_CAPS; i++ )
+ {
+ if ( pEconTool->GetCapabilities() & (1 << i) )
+ {
+ unToolUsageCaps |= g_CapabilityApplicationMap[i];
+ }
+ }
+ }
+
+ // Check for base applicability of this tool to this object.
+ if ( (unToolUsageCaps & pToolSubjectDef->GetCapabilities()) == 0 )
+ return false;
+
+ // check to see if either the tool or the tool target have usage restriction
+ const IEconTool *pSubjectEconTool = pToolSubjectDef->GetEconTool();
+
+ if ( pSubjectEconTool )
+ {
+ // If this tool can apply to anything then we don't care about the checks below
+ // making sure restrictions match.
+ const char *pszToolRestriction = BStringsEqual( pEconTool->GetUsageRestriction(), "any" )
+ ? pSubjectEconTool->GetUsageRestriction()
+ : pEconTool->GetUsageRestriction();
+
+ if ( !BStringsEqual( pszToolRestriction, pSubjectEconTool->GetUsageRestriction() ) )
+ return false;
+ }
+
+ return true;
+}
+
+// WARNING
+// DO NOT USE THIS CODE IF YOUR TOOL HAS Attribute restrictions like "tool_target_item" or similar restriction attributes
+/* static */ bool CEconSharedToolSupport::ToolCanApplyToBaseItem( const GameItemDefinition_t *pToolDef, const GameItemDefinition_t *pToolSubjectDef )
+{
+ if ( !pToolSubjectDef )
+ return false;
+
+ // We are targetting the "Upgradeable" version of a base item and not a base item itself
+ if ( pToolSubjectDef->IsBaseItem() || pToolSubjectDef->IsHidden() || pToolSubjectDef->GetQuality() == AE_NORMAL || Q_strnicmp( pToolSubjectDef->GetDefinitionName(), "Upgradeable ", 12 ) )
+ return false;
+
+ return ToolCanApplyToDefinition( pToolDef, pToolSubjectDef );
+}
diff --git a/game/shared/econ/econ_item_tools.h b/game/shared/econ/econ_item_tools.h
new file mode 100644
index 0000000..b6adfe9
--- /dev/null
+++ b/game/shared/econ/econ_item_tools.h
@@ -0,0 +1,1043 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+
+#ifndef ECONITEMTOOLS_H
+#define ECONITEMTOOLS_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+enum EConsumptionAttemptResult
+{
+ kConsumptionResult_CannotConsume, // could be bum definitions or doesnt meet criteria or anything -- this is failure
+ kConsumptionResult_CanConsume, // able to consume
+ kConsumptionResult_WillCompleteCollection, // able to consume and is the final item to be consumed
+};
+
+//---------------------------------------------------------------------------------------
+// Purpose:
+//---------------------------------------------------------------------------------------
+class CEconSharedToolSupport
+{
+public:
+ // Can the given tool instance apply to a specific instance of an item. This should be used in the general
+ // case whenever a CEconItem or a CEconItemView is available.
+ static bool ToolCanApplyTo( const IEconItemInterface *pToolDef, const IEconItemInterface *pToolSubject );
+
+ // Can the given tool definition apply to an item definition? This will check things like restrictions,
+ // matching tool capabilities, etc. but will ignore instance-specific properties. This should only be used
+ // by code that doesn't have any access to an instance of the definition.
+ static bool ToolCanApplyToDefinition( const GameItemDefinition_t *pToolDef, const GameItemDefinition_t *pToolSubjectDef );
+
+ // Can the given tool definition apply to a base item definition?
+ static bool ToolCanApplyToBaseItem( const GameItemDefinition_t *pToolDef, const GameItemDefinition_t *pToolSubjectDef );
+};
+
+//---------------------------------------------------------------------------------------
+// Purpose:
+//---------------------------------------------------------------------------------------
+class CEconTool_DuelingMinigame : public IEconTool
+{
+public:
+ CEconTool_DuelingMinigame( const char *pszTypeName, const char *pszUseString ) : IEconTool( pszTypeName, pszUseString, NULL, ITEM_CAP_NONE ) { }
+
+#ifdef CLIENT_DLL
+ virtual void OnClientUseConsumable( CEconItemView *pItem, vgui::Panel *pParent ) const;
+#endif // CLIENT_DLL
+
+#ifdef GC_DLL
+ virtual class CGCEconConsumableBehavior *CreateGCConsumableBehavior() const;
+#endif // GC_DLL
+};
+
+//---------------------------------------------------------------------------------------
+// Purpose:
+//---------------------------------------------------------------------------------------
+class CEconTool_Noisemaker : public IEconTool
+{
+public:
+ CEconTool_Noisemaker( const char *pszTypeName, const char *pszUseString ) : IEconTool( pszTypeName, pszUseString, NULL, ITEM_CAP_NONE ) { }
+
+#ifdef CLIENT_DLL
+ virtual void OnClientUseConsumable( CEconItemView *pItem, vgui::Panel *pParent ) const;
+#endif // CLIENT_DLL
+};
+
+//---------------------------------------------------------------------------------------
+// Purpose:
+//---------------------------------------------------------------------------------------
+class CEconTool_WrappedGift : public IEconTool
+{
+public:
+ CEconTool_WrappedGift( const char *pszTypeName, const char *pszUseString, item_capabilities_t unCapabilities, KeyValues *pUsageKV );
+
+ virtual bool BFinishInitialization() OVERRIDE;
+
+ bool BIsGlobalGift() const { return m_bIsGlobalGift; }
+ // Allows the item to be directly used rather than via the trading system
+ bool BIsDirectGift() const { return m_bIsDirectGift; }
+ const CEconItemDefinition *GetDeliveredItemDefinition() const { return m_pDeliveredGiftItemDef; } // can return NULL! (means "don't change definitions on delivery")
+
+#ifdef CLIENT_DLL
+ virtual bool CanBeUsedNow( const IEconItemInterface *pItem ) const;
+ virtual bool ShouldShowContainedItemPanel( const IEconItemInterface *pItem ) const;
+ virtual const char *GetUseCommandLocalizationToken( const IEconItemInterface *pItem, int i = 0 ) const;
+ virtual void OnClientUseConsumable( CEconItemView *pItem, vgui::Panel *pParent ) const;
+ virtual int GetUseCommandCount( const IEconItemInterface *pItem ) const;
+ virtual const char* GetUseCommand( const IEconItemInterface *pItem, int i = 0 ) const;
+#endif // CLIENT_DLL
+
+#ifdef GC_DLL
+ virtual class CGCEconConsumableBehavior *CreateGCConsumableBehavior() const;
+#endif // GC_DLL
+
+private:
+ const char *m_pszDeliveredGiftItemDefName; // points to memory inside our init KV -- only valid between the constructor call and the BFinishInitialization() call (this is messy but Fletcher and I agree it makes more sense than switching to a full two-pass schema parse just for this)
+
+ const CEconItemDefinition *m_pDeliveredGiftItemDef;
+ bool m_bIsGlobalGift;
+ bool m_bIsDirectGift;
+};
+
+//---------------------------------------------------------------------------------------
+// Purpose:
+//---------------------------------------------------------------------------------------
+class CEconTool_WeddingRing : public IEconTool
+{
+public:
+ CEconTool_WeddingRing( const char *pszTypeName, const char *pszUseString, item_capabilities_t unCapabilities ) : IEconTool( pszTypeName, pszUseString, NULL, unCapabilities ) { }
+
+ virtual bool RequiresToolEscrowPeriod() const { return false; }
+
+#ifdef CLIENT_DLL
+ virtual const char *GetUseCommandLocalizationToken( const IEconItemInterface *pItem, int i = 0 ) const;
+ virtual void OnClientUseConsumable( CEconItemView *pItem, vgui::Panel *pParent ) const;
+#endif // CLIENT_DLL
+
+#ifdef GC_DLL
+ virtual class CGCEconConsumableBehavior *CreateGCConsumableBehavior() const;
+#endif // GC_DLL
+};
+
+//---------------------------------------------------------------------------------------
+// Purpose:
+//---------------------------------------------------------------------------------------
+class CEconTool_TagsList
+{
+public:
+ CEconTool_TagsList( KeyValues *pKVTags )
+ {
+ if ( pKVTags )
+ {
+ FOR_EACH_SUBKEY( pKVTags, pKVTag )
+ {
+ m_vecTags.AddToTail( GetItemSchema()->GetHandleForTag( pKVTag->GetName() ) );
+ }
+ }
+ }
+
+ const CUtlVector<econ_tag_handle_t>& GetTagsList() const { return m_vecTags; }
+
+private:
+ CUtlVector<econ_tag_handle_t> m_vecTags;
+};
+
+//---------------------------------------------------------------------------------------
+// Purpose:
+//---------------------------------------------------------------------------------------
+class CEconTool_StrangeCountTransfer : public IEconTool
+{
+public:
+ CEconTool_StrangeCountTransfer( const char *pszTypeName, item_capabilities_t unCapabilities );
+
+ static bool AreItemsEligibleForStrangeCountTransfer( const IEconItemInterface *pItem1, const IEconItemInterface *pItem2 );
+
+#ifdef CLIENT_DLL
+ virtual void OnClientApplyTool( CEconItemView *pTool, CEconItemView *pSubject, vgui::Panel *pParent ) const;
+ //virtual void OnClientApplyCommit( CEconItemView *pTool, CEconItemView *pSubject ) const;
+
+ bool SetItems( CEconItemView *pItem1, CEconItemView *pItem2 );
+
+ CEconItemView *m_pItemSrc;
+ CEconItemView *m_pItemDest;
+#endif
+};
+
+//---------------------------------------------------------------------------------------
+// Purpose:
+//---------------------------------------------------------------------------------------
+class CEconTool_StrangePart : public IEconTool
+{
+public:
+ CEconTool_StrangePart( const char *pszTypeName, const char *pszUseString, item_capabilities_t unCapabilities, KeyValues *pUsageKV )
+ : IEconTool( pszTypeName, pszUseString, NULL, unCapabilities )
+ , m_RequiredTags( pUsageKV ? pUsageKV->FindKey( "required_tags" ) : NULL )
+ , m_RequiredMissingTags( pUsageKV ? pUsageKV->FindKey( "required_missing_tags" ) : NULL )
+ {
+ //
+ }
+
+ virtual bool CanApplyTo( const IEconItemInterface *pTool, const IEconItemInterface *pToolSubject ) const;
+
+#ifdef CLIENT_DLL
+ virtual bool ShouldDisplayAsUseableOnItemsInArmory() const { return false; }
+
+ virtual void OnClientApplyTool( CEconItemView *pTool, CEconItemView *pSubject, vgui::Panel *pParent ) const;
+#endif // CLIENT_DLL
+
+private:
+ CEconTool_TagsList m_RequiredTags;
+ CEconTool_TagsList m_RequiredMissingTags;
+};
+
+//---------------------------------------------------------------------------------------
+// Purpose:
+//---------------------------------------------------------------------------------------
+class CEconTool_StrangePartRestriction : public IEconTool
+{
+public:
+ CEconTool_StrangePartRestriction( const char *pszTypeName, const char *pszUseString, item_capabilities_t unCapabilities, KeyValues *pUsageKV );
+
+ virtual bool CanApplyTo( const IEconItemInterface *pTool, const IEconItemInterface *pToolSubject ) const;
+ virtual bool BFinishInitialization() OVERRIDE;
+#ifdef CLIENT_DLL
+ virtual bool ShouldDisplayAsUseableOnItemsInArmory() const { return false; }
+
+ virtual void OnClientApplyTool( CEconItemView *pTool, CEconItemView *pSubject, vgui::Panel *pParent ) const;
+#endif // CLIENT_DLL
+
+ unsigned int GetRestrictionType() const { return m_eRestrictionType; }
+ unsigned int GetRestrictionValue() const { return m_unRestrictionValue; }
+
+private:
+ unsigned int /*strange_event_restriction_t*/ m_eRestrictionType;
+
+ const char *m_pszRestrictionValue; // points to memory inside our init KV -- only valid between the constructor call and the BFinishInitialization() call (this is messy but Fletcher and I agree it makes more sense than switching to a full two-pass schema parse just for this)
+ unsigned int m_unRestrictionValue;
+};
+
+//---------------------------------------------------------------------------------------
+// Purpose: New crafting! This new systems allows for dynamic crafting recipes to be
+// generated in the form of an item itself. Players can "feed" in items on the
+// recipe's input list, either all at once or once at a time, until the inputs
+// are all fulfilled. Once that happens at which the outputs of the recipe are given to the player.
+//
+// This is done using new attribute types that encode the recipe's inputs and outputs.
+// Inputs and outputs can either be specific items of specific qualities, or lootlist
+// with a specific quality -- for now. Lootlist will roll the specific item to be the input/output
+// when the recipe item is created. Any gc generated attributes that would come
+// from the lootlists will also get encoded as a string in the recipe's attribute, so things like
+// unusual particle effects will get applied to outputs. These string-encoded attributes
+// are ignored during input criteria matching for now.
+//
+// Components are allowed to have nested components defined within them. These child
+// components only roll their chance to apply if their parent successfully rolls their
+// chance to apply.
+//---------------------------------------------------------------------------------------
+class CEconTool_ItemDynamicRecipe : public IEconTool
+{
+public:
+
+ // This enum lets the CDynamicRecipeComponentLootList class specify
+ // "uniqueness" of the item def that it will roll. This allows us to
+ // do things like ensure that the output item will never be one of the
+ // input items, or there's never duplicate inputs.
+ enum EItemDefUniqueness_t
+ {
+ UNIQUE_AMONG_INPUTS = 0,
+ UNIQUE_AMONG_OUTPUTS,
+ UNIQUE_AMONG_EVERYTHING,
+ UNIQUE_AMONG_NOTHING,
+ };
+
+ CEconTool_ItemDynamicRecipe( const char *pszTypeName, const char *pszUseString, item_capabilities_t unCapabilities, KeyValues *pUsageKV );
+ ~CEconTool_ItemDynamicRecipe();
+
+ virtual bool CanApplyTo( const IEconItemInterface *pTool, const IEconItemInterface *pToolSubject ) const;
+ virtual bool BFinishInitialization() OVERRIDE;
+
+#ifdef CLIENT_DLL
+ virtual bool ShouldDisplayAsUseableOnItemsInArmory() const { return false; }
+#endif // CLIENT_DLL
+
+
+ virtual bool BInitFromKV( KeyValues *pKVDefinition, CUtlVector<CUtlString> *pVecErrors );
+#ifdef GC_DLL
+ virtual bool BGenerateDynamicAttributes( CEconItem* pItem, const CEconGameAccount *pGameAccount ) const OVERRIDE;
+#endif
+
+ class CBaseRecipeComponent
+ {
+ public:
+
+ struct StringEncodedAttribute_t
+ {
+ attrib_definition_index_t m_AttrIndex;
+ CUtlConstString m_strAttrData;
+ };
+
+ struct CountChance_t
+ {
+ int m_nMinCount;
+ int m_nMaxCount;
+ float m_flChance;
+ };
+
+ typedef CUtlVector<const CEconItemAttributeDefinition *> ComponentAttribVector_t;
+
+ CBaseRecipeComponent( bool bIsOutput, const CBaseRecipeComponent* pParent );
+ virtual ~CBaseRecipeComponent();
+
+ static bool ParseComponentsBlock( KeyValues *pKV, CUtlVector<CBaseRecipeComponent*>& vecComponents, CUtlVector<CUtlString> *pVecErrors, const CBaseRecipeComponent* pParent );
+ static bool ParseComponents( KeyValues *pKV, CUtlVector<CBaseRecipeComponent*>& vecComponents, bool bIsOutput, CUtlVector<CUtlString> *pVecErrors, const CBaseRecipeComponent* pParent );
+ virtual bool ParseKV( KeyValues *pKV, CUtlVector<CUtlString> *pVecErrors );
+ void SetIsOutput( bool bIsOutput ) { m_bIsOutput = bIsOutput; }
+ void SetParent( CBaseRecipeComponent* pParent ) { m_pParent = pParent; }
+ void SetChanceOfApplying( float flChance );
+ virtual bool BFinishInitialization_Internal( CUtlVector<CUtlString>* pVecErrors, ComponentAttribVector_t* attribVec );
+
+ bool GetIsOutput() const { return m_bIsOutput; }
+ void GetIsGuaranteed( int &nFlags ) const;
+ const CUtlVector< CountChance_t >& GetRollChances() const { return m_vecCountChances; }
+#ifdef GC_DLL
+ bool RollChanceOfApplying() const;
+ float GetRollChance() const { return m_flChanceOfApplying; }
+ int RollCount() const;
+ virtual bool AddRecipeComponentAsAttribute( CEconItem *pItem, const CEconGameAccount *pGameAccount ) const = 0;
+#endif
+ protected:
+#ifdef GC_DLL
+ virtual const char* GetAttributeName() const { return "recipe component defined item"; }
+ static CEconItemAttributeDefinition* GetNextAvailableAttributeWithBaseName( const char* pszBaseAttribName, ComponentAttribVector_t *pAttribVec );
+#endif
+ const CBaseRecipeComponent* m_pParent;
+ CUtlVector< CBaseRecipeComponent* > m_vecAdditionalComponents;
+ float m_flChanceOfApplying;
+ bool m_bIsOutput;
+ CUtlVector< CountChance_t > m_vecCountChances;
+ float m_flTotalWeights;
+
+ EEconItemQuality m_eQuality;
+
+ enum EAttributesMatchingType_t
+ {
+ ATTRIBUTES_MATCH_NONE = 0,
+ ATTRIBUTES_MATCH_ALL,
+ ATTRIBUTES_MATCH_ANY,
+ };
+
+ EAttributesMatchingType_t m_attributesMatchingType;
+ CUtlVector< StringEncodedAttribute_t > m_vecDynamicAttributes;
+
+ CUtlString m_strName;
+
+ static const char* m_pszUseParentNameIdentifier;
+ };
+
+public:
+
+ class CDynamicRecipeComponentLootList;
+ // Defined item type: Use this when you want to quickly define a specific item as
+ // a component for a recipe. A "defined item" is considered an
+ // itemdef, a quality, and any additional attributes.
+ class CDynamicRecipeComponentDefinedItem : public CBaseRecipeComponent
+ {
+ typedef CBaseRecipeComponent BaseClass;
+ public:
+ CDynamicRecipeComponentDefinedItem( bool bIsOutput, const CBaseRecipeComponent* pParent );
+ virtual ~CDynamicRecipeComponentDefinedItem();
+ virtual bool BFinishInitialization_Internal( CUtlVector<CUtlString>* pVecErrors, ComponentAttribVector_t* attribVec ) OVERRIDE;
+#ifdef GC_DLL
+ virtual bool AddRecipeComponentAsAttribute( CEconItem *pItem, const CEconGameAccount *pGameAccount ) const OVERRIDE;
+#endif
+ protected:
+
+ virtual bool ParseKV( KeyValues *pKV, CUtlVector<CUtlString> *pVecErrors ) OVERRIDE;
+
+ friend class CDynamicRecipeComponentLootList;
+ };
+
+ // Lootlist type: Use this when you want a random item from a lootlist to be a
+ // component in a recipe. You must specify a quality in the definition
+ // but it can get stomped if the lootlist-generated item gets an "elevate quality"
+ // attribute rolled onto it.
+ class CDynamicRecipeComponentLootList : public CBaseRecipeComponent
+ {
+ typedef CBaseRecipeComponent BaseClass;
+ public:
+ CDynamicRecipeComponentLootList( bool bIsOutput, const CBaseRecipeComponent* pParent );
+ virtual ~CDynamicRecipeComponentLootList();
+ virtual bool BFinishInitialization_Internal( CUtlVector<CUtlString>* pVecErrors, ComponentAttribVector_t* attribVec ) OVERRIDE;
+ protected:
+ virtual bool ParseKV( KeyValues *pKV, CUtlVector<CUtlString> *pVecErrors ) OVERRIDE;
+
+ private:
+#ifdef GC_DLL
+ virtual bool AddRecipeComponentAsAttribute( CEconItem *pItem, const CEconGameAccount *pGameAccount ) const OVERRIDE;
+ bool RollLootlistItemAndAttributes( CUtlVector< StringEncodedAttribute_t >& vecAdditionalAttributes
+ , const char** pszDefName
+ , const CUtlVector< item_definition_index_t > *pVecAvoidItemDefs
+ , const CEconGameAccount *pGameAccount ) const;
+
+ EItemDefUniqueness_t m_eUniqueness;
+#endif
+ };
+
+ class CRecipeComponentInputDefIndexIterator : public CEconItemSpecificAttributeIterator
+ {
+ public:
+ CRecipeComponentInputDefIndexIterator( EItemDefUniqueness_t eUniqueness );
+ virtual bool OnIterateAttributeValue( const CEconItemAttributeDefinition *pAttrDef,
+ const CAttribute_DynamicRecipeComponent& value ) OVERRIDE;
+
+ const CUtlVector< item_definition_index_t >& GetMatchingComponentInputs() const { return m_vecInputItemDefs; }
+
+ private:
+
+ CUtlVector< item_definition_index_t > m_vecInputItemDefs;
+ EItemDefUniqueness_t m_eUniqueness;
+ };
+
+ const CUtlVector<CBaseRecipeComponent*>& GetComponents() const { return m_vecComponents; }
+
+private:
+
+ // All the different components for this recipe
+ CUtlVector<CBaseRecipeComponent*> m_vecComponents;
+ // Errors we ecounter during initialization
+ CUtlVector<CUtlString> m_vecErrors;
+};
+
+class CWorldItemPlacementAttributeIterator : public CEconItemSpecificAttributeIterator
+{
+public:
+ CWorldItemPlacementAttributeIterator() {}
+
+ virtual bool OnIterateAttributeValue( const CEconItemAttributeDefinition *pAttrDef, const CAttribute_WorldItemPlacement &value ) OVERRIDE
+ {
+ Assert( pAttrDef );
+ if ( pAttrDef )
+ {
+ m_vecPlacementAttributes.AddToTail( pAttrDef );
+ }
+
+ return true;
+ }
+
+ const CUtlVector< const CEconItemAttributeDefinition* > &GetPlacementAttributes( void ) const { return m_vecPlacementAttributes; }
+private:
+
+ CUtlVector< const CEconItemAttributeDefinition* > m_vecPlacementAttributes;
+};
+
+//---------------------------------------------------------------------------------------
+// Purpose: Turns valid target items in to strange
+//---------------------------------------------------------------------------------------
+class CEconTool_Xifier : public IEconTool
+{
+public:
+ CEconTool_Xifier( const char *pszTypeName, const char *pszUseString, item_capabilities_t unCapabilities, KeyValues *pUsageKV )
+ : IEconTool( pszTypeName, pszUseString, NULL, unCapabilities )
+ , m_RequiredTags( pUsageKV ? pUsageKV->FindKey( "required_tags" ) : NULL )
+ {
+ if ( pUsageKV )
+ {
+ KeyValues *pKVItemDefRestrictions = pUsageKV->FindKey( "itemdef_restrictions" );
+ if ( pKVItemDefRestrictions )
+ {
+ FOR_EACH_SUBKEY( pKVItemDefRestrictions, pKVTag )
+ {
+ m_ItemDefTargetRestrictions.AddToTail( atoi(pKVTag->GetName()) );
+ }
+ }
+
+ m_ItemRarityRestriction = pUsageKV->GetInt( "itemrarity_restrictions", k_unItemRarity_Any );
+ }
+
+ m_sItemDescLocToken = pUsageKV ? pUsageKV->GetString( "item_desc_tool_target", "" ) : "";
+ }
+
+ virtual bool CanApplyTo( const IEconItemInterface *pTool, const IEconItemInterface *pToolSubject ) const;
+
+ const char *GetItemDescToolTargetLocToken() const { return m_sItemDescLocToken.String(); }
+
+#ifdef GC
+ virtual CEconItem *GenerateNewItem( const IEconItemInterface *pTool, const CEconItem *pTarget ) const = 0;
+#endif
+
+#ifdef CLIENT_DLL
+ virtual bool ShouldDisplayAsUseableOnItemsInArmory() const { return false; }
+ virtual void OnClientUseConsumable( CEconItemView *pItem, vgui::Panel *pParent ) const;
+#endif // CLIENT_DLL
+
+private:
+ bool ItemDefMatch( const CEconItemDefinition* pTargetItemDef, const CEconItemDefinition* pSubjectItemDef ) const;
+
+ CUtlString m_sItemDescLocToken;
+ CEconTool_TagsList m_RequiredTags;
+ CUtlVector<item_definition_index_t> m_ItemDefTargetRestrictions;
+
+ uint8 m_ItemRarityRestriction;
+};
+
+class CEconTool_Strangifier : public CEconTool_Xifier
+{
+public:
+ CEconTool_Strangifier( const char *pszTypeName, const char *pszUseString, item_capabilities_t unCapabilities, KeyValues *pUsageKV )
+ : CEconTool_Xifier( pszTypeName, pszUseString, unCapabilities, pUsageKV ) {}
+
+ virtual bool CanApplyTo( const IEconItemInterface *pTool, const IEconItemInterface *pToolSubject ) const;
+
+#ifdef GC
+ virtual CEconItem *GenerateNewItem( const IEconItemInterface *pTool, const CEconItem *pTarget ) const;
+#endif
+
+#ifdef CLIENT_DLL
+ virtual void OnClientApplyTool( CEconItemView *pTool, CEconItemView *pSubject, vgui::Panel *pParent ) const;
+#endif // CLIENT_DLL
+
+};
+//---------------------------------------------------------------------------------------
+class CEconTool_KillStreakifier : public CEconTool_Xifier
+{
+public:
+ CEconTool_KillStreakifier( const char *pszTypeName, const char *pszUseString, item_capabilities_t unCapabilities, KeyValues *pUsageKV )
+ : CEconTool_Xifier( pszTypeName, pszUseString, unCapabilities, pUsageKV ) {}
+
+ virtual bool CanApplyTo( const IEconItemInterface *pTool, const IEconItemInterface *pToolSubject ) const;
+
+#ifdef GC
+ virtual CEconItem *GenerateNewItem( const IEconItemInterface *pTool, const CEconItem *pTarget ) const;
+#endif
+
+#ifdef CLIENT_DLL
+ virtual void OnClientApplyTool( CEconItemView *pTool, CEconItemView *pSubject, vgui::Panel *pParent ) const;
+#endif // CLIENT_DLL
+};
+//---------------------------------------------------------------------------------------
+class CEconTool_Festivizer : public CEconTool_Xifier
+{
+public:
+ CEconTool_Festivizer( const char *pszTypeName, const char *pszUseString, item_capabilities_t unCapabilities, KeyValues *pUsageKV )
+ : CEconTool_Xifier( pszTypeName, pszUseString, unCapabilities, pUsageKV )
+ {
+ }
+
+ virtual bool CanApplyTo( const IEconItemInterface *pTool, const IEconItemInterface *pToolSubject ) const;
+
+#ifdef GC
+ virtual CEconItem *GenerateNewItem( const IEconItemInterface *pTool, const CEconItem *pTarget ) const;
+#endif
+
+#ifdef CLIENT_DLL
+ virtual void OnClientApplyTool( CEconItemView *pTool, CEconItemView *pSubject, vgui::Panel *pParent ) const;
+#endif // CLIENT_DLL
+};
+
+//---------------------------------------------------------------------------------------
+class CEconTool_Unusualifier : public CEconTool_Xifier
+{
+public:
+ CEconTool_Unusualifier( const char *pszTypeName, const char *pszUseString, item_capabilities_t unCapabilities, KeyValues *pUsageKV )
+ : CEconTool_Xifier( pszTypeName, pszUseString, unCapabilities, pUsageKV ) {}
+
+ virtual bool CanApplyTo( const IEconItemInterface *pTool, const IEconItemInterface *pToolSubject ) const;
+
+#ifdef GC
+ virtual CEconItem *GenerateNewItem( const IEconItemInterface *pTool, const CEconItem *pTarget ) const;
+#endif
+
+#ifdef CLIENT_DLL
+ virtual void OnClientApplyTool( CEconItemView *pTool, CEconItemView *pSubject, vgui::Panel *pParent ) const;
+#endif // CLIENT_DLL
+};
+
+//---------------------------------------------------------------------------------------
+// Eats an item to give it charges
+//---------------------------------------------------------------------------------------
+class CEconTool_ItemEaterRecharger: public IEconTool
+{
+public:
+ CEconTool_ItemEaterRecharger( const char *pszTypeName, const char *pszUseString, item_capabilities_t unCapabilities, KeyValues *pUsageKV )
+ : IEconTool( pszTypeName, pszUseString, NULL, unCapabilities )
+ , m_RequiredTags( pUsageKV ? pUsageKV->FindKey( "required_tags" ) : NULL )
+ {
+ if ( pUsageKV )
+ {
+ KeyValues *pKVItemDefRestrictions = pUsageKV->FindKey( "itemdef_restrictions" );
+
+ if ( pKVItemDefRestrictions )
+ {
+ FOR_EACH_SUBKEY( pKVItemDefRestrictions, pKVTag )
+ {
+ m_ItemDefTargetRestrictions.AddToTail( atoi(pKVTag->GetName()) );
+ m_ItemDefTargetChargeValues.AddToTail( pKVItemDefRestrictions->GetInt( pKVTag->GetName(), 0 ) );
+ }
+ }
+ }
+ }
+
+ int GetChargesForItemDefId ( item_definition_index_t defIndex ) const;
+
+ virtual bool CanApplyTo( const IEconItemInterface *pTool, const IEconItemInterface *pToolSubject ) const;
+
+#ifdef CLIENT_DLL
+ virtual bool ShouldDisplayAsUseableOnItemsInArmory() const { return false; }
+ virtual void OnClientUseConsumable( CEconItemView *pItem, vgui::Panel *pParent ) const;
+ virtual void OnClientApplyTool( CEconItemView *pTool, CEconItemView *pSubject, vgui::Panel *pParent ) const;
+#endif // CLIENT_DLL
+
+#ifdef GC_DLL
+ virtual class CGCEconConsumableBehavior *CreateGCConsumableBehavior() const;
+#endif // GC_DLL
+
+private:
+ CEconTool_TagsList m_RequiredTags;
+ CUtlVector<item_definition_index_t> m_ItemDefTargetRestrictions;
+ CUtlVector<int> m_ItemDefTargetChargeValues;
+};
+//---------------------------------------------------------------------------------------
+// Purpose:
+//---------------------------------------------------------------------------------------
+class CEconTool_UpgradeCard : public IEconTool
+{
+public:
+ struct upgrade_card_attr_value_t
+ {
+ const CEconItemAttributeDefinition *m_pAttrDef;
+ attrib_value_t m_value;
+ };
+
+ typedef CUtlVectorFixedGrowable<upgrade_card_attr_value_t, 1> UpgradeCardAttributeVec_t;
+
+ CEconTool_UpgradeCard( const char *pszTypeName, const char *pszUseString, item_capabilities_t unCapabilities, KeyValues *pUsageKV )
+ : IEconTool( pszTypeName, pszUseString, NULL, unCapabilities )
+ , m_RequiredTags( pUsageKV ? pUsageKV->FindKey( "required_tags" ) : NULL )
+ {
+ COMPILE_TIME_ASSERT( sizeof( attrib_value_t ) == sizeof( uint32 ) );
+ COMPILE_TIME_ASSERT( sizeof( attrib_value_t ) == sizeof( float ) );
+
+ if ( pUsageKV )
+ {
+ KeyValues *pAttributesKV = pUsageKV->FindKey( "attributes" );
+ if ( pAttributesKV )
+ {
+ FOR_EACH_SUBKEY( pAttributesKV, pAttrKV )
+ {
+ const char *pszAttributeName = pAttrKV->GetName();
+ const CEconItemAttributeDefinition *pAttrDef = GetItemSchema()->GetAttributeDefinitionByName( pszAttributeName );
+
+ // Kyle says: this is bad, dumb code, and more importantly it's bad dumb code that doesn't
+ // make any sense here, way down inside the "parse a tool" function.
+ attrib_value_t value;
+
+ const bool bParseAsFloat = pAttrDef && pAttrDef->IsStoredAsFloat();
+ if ( bParseAsFloat )
+ {
+ *(float *)&value = pAttrKV->GetFloat();
+ }
+ else
+ {
+ *(uint32 *)&value = pAttrKV->GetInt();
+ }
+
+ // Add this attribute to our list. Adding a NULL pointer is safe here. We'll use that to check
+ // later in BFinishInitialization() whether we had a successful init or not.
+ upgrade_card_attr_value_t attrValue = { pAttrDef, value };
+ m_vecAttributes.AddToTail( attrValue );
+ }
+ }
+ }
+ }
+
+ virtual bool BFinishInitialization() OVERRIDE
+ {
+ // Make sure we didn't fail to find any attributes.
+ FOR_EACH_VEC( m_vecAttributes, i )
+ {
+ if ( m_vecAttributes[i].m_pAttrDef == NULL )
+ return false;
+ }
+
+ // Make sure we have a non-zero number of attributes. If we don't have at least one, applicable would be
+ // a nonsensical action.
+ return m_vecAttributes.Count() > 0
+ && IEconTool::BFinishInitialization();
+ }
+
+ const UpgradeCardAttributeVec_t& GetAttributes() const { return m_vecAttributes; }
+
+ virtual bool CanApplyTo( const IEconItemInterface *pTool, const IEconItemInterface *pToolSubject ) const;
+
+#ifdef CLIENT_DLL
+ virtual bool ShouldDisplayAsUseableOnItemsInArmory() const { return false; }
+
+ virtual void OnClientApplyTool( CEconItemView *pTool, CEconItemView *pSubject, vgui::Panel *pParent ) const;
+#endif // CLIENT_DLL
+
+private:
+ CEconTool_TagsList m_RequiredTags;
+ UpgradeCardAttributeVec_t m_vecAttributes;
+};
+
+//---------------------------------------------------------------------------------------
+// Purpose:
+//---------------------------------------------------------------------------------------
+class CEconTool_ClassTransmogrifier : public IEconTool
+{
+public:
+ CEconTool_ClassTransmogrifier( const char *pszTypeName, const char *pszUseString, item_capabilities_t unCapabilities, KeyValues *pUsageKV )
+ : IEconTool( pszTypeName, pszUseString, NULL, unCapabilities )
+ , m_RequiredTags( pUsageKV ? pUsageKV->FindKey( "required_tags" ) : NULL )
+ , m_iClass( -1 )
+ {
+ KeyValues *pKVOutputClass = pUsageKV->FindKey( "output_class" );
+ if ( pKVOutputClass )
+ {
+ m_iClass = StringFieldToInt( pKVOutputClass->GetString( "" ), GetItemSchema()->GetClassUsabilityStrings() );
+ }
+ }
+
+ virtual bool BFinishInitialization() OVERRIDE
+ {
+ return m_iClass > 0
+ && m_iClass < LOADOUT_COUNT
+ && IEconTool::BFinishInitialization();
+ }
+
+ int GetOutputClass() const { return m_iClass; }
+ const CEconTool_TagsList& GetRequiredTags() const { return m_RequiredTags; }
+
+ virtual bool CanApplyTo( const IEconItemInterface *pTool, const IEconItemInterface *pToolSubject ) const;
+
+#ifdef CLIENT_DLL
+ virtual bool ShouldDisplayAsUseableOnItemsInArmory() const { return false; }
+
+ virtual void OnClientApplyTool( CEconItemView *pTool, CEconItemView *pSubject, vgui::Panel *pParent ) const;
+#endif // CLIENT_DLL
+
+private:
+ CEconTool_TagsList m_RequiredTags; // required for both the input item and the output item
+ int m_iClass;
+};
+
+//---------------------------------------------------------------------------------------
+// Purpose:
+//---------------------------------------------------------------------------------------
+class CEconTool_BackpackExpander : public IEconTool
+{
+public:
+ CEconTool_BackpackExpander ( const char *pszTypeName, const char *pszUseString, KeyValues *pUsageKV )
+ : IEconTool( pszTypeName, pszUseString, NULL, ITEM_CAP_NONE )
+ , m_iBackpackSlots( 0 )
+ {
+ if ( pUsageKV )
+ {
+ m_iBackpackSlots = pUsageKV->GetInt( "backpack_slots", 0 );
+ }
+ }
+
+ virtual bool BFinishInitialization() OVERRIDE
+ {
+ return m_iBackpackSlots > 0
+ && IEconTool::BFinishInitialization();
+ }
+
+ int GetBackpackSlots() const { return m_iBackpackSlots; }
+
+#ifdef CLIENT_DLL
+ virtual void OnClientUseConsumable( CEconItemView *pItem, vgui::Panel *pParent ) const;
+#endif // CLIENT_DLL
+
+#ifdef GC_DLL
+ virtual class CGCEconConsumableBehavior *CreateGCConsumableBehavior() const;
+#endif // GC_DLL
+
+private:
+ int m_iBackpackSlots;
+};
+
+//---------------------------------------------------------------------------------------
+// Purpose:
+//---------------------------------------------------------------------------------------
+class CEconTool_AccountUpgradeToPremium : public IEconTool
+{
+public:
+ CEconTool_AccountUpgradeToPremium( const char *pszTypeName, const char *pszUseString ) : IEconTool( pszTypeName, pszUseString, NULL, ITEM_CAP_NONE ) { }
+
+#ifdef CLIENT_DLL
+ virtual void OnClientUseConsumable( CEconItemView *pItem, vgui::Panel *pParent ) const;
+#endif // CLIENT_DLL
+
+#ifdef GC_DLL
+ virtual class CGCEconConsumableBehavior *CreateGCConsumableBehavior() const;
+#endif // GC_DLL
+};
+
+//---------------------------------------------------------------------------------------
+class CEconTool_DuckToken: public IEconTool
+{
+public:
+ CEconTool_DuckToken( const char *pszTypeName, item_capabilities_t unCapabilities ) : IEconTool( pszTypeName, NULL, NULL, unCapabilities ) { }
+
+ virtual bool CanApplyTo( const IEconItemInterface *pTool, const IEconItemInterface *pToolSubject ) const;
+
+#ifdef CLIENT_DLL
+ virtual void OnClientUseConsumable( CEconItemView *pItem, vgui::Panel *pParent ) const;
+ virtual void OnClientApplyTool( CEconItemView *pTool, CEconItemView *pSubject, vgui::Panel *pParent ) const;
+#endif // CLIENT_DLL
+
+#ifdef GC_DLL
+ //virtual class CGCEconConsumableBehavior *CreateGCConsumableBehavior() const;
+#endif // GC_DLL
+};
+
+//---------------------------------------------------------------------------------------
+class CEconTool_GrantOperationPass : public IEconTool
+{
+public:
+ CEconTool_GrantOperationPass( const char *pszTypeName, const char *pszUseString, item_capabilities_t unCapabilities, KeyValues *pUsageKV ) : IEconTool( pszTypeName, pszUseString, NULL, ITEM_CAP_NONE )
+ {
+ m_pOperationPassName = NULL;
+ m_pOptionalBonusLootList = NULL;
+ if ( pUsageKV )
+ {
+ // Find the Item
+ m_pOperationPassName = pUsageKV->GetString( "operation_pass", NULL );
+ m_pOptionalBonusLootList = pUsageKV->GetString( "bonus_lootlist", NULL );
+ }
+ }
+
+#ifdef CLIENT_DLL
+ virtual void OnClientUseConsumable( CEconItemView *pItem, vgui::Panel *pParent ) const;
+#endif // CLIENT_DLL
+
+#ifdef GC_DLL
+ virtual class CGCEconConsumableBehavior *CreateGCConsumableBehavior() const;
+#endif // GC_DLL
+
+ const char *m_pOperationPassName;
+ const char *m_pOptionalBonusLootList;
+};
+
+//---------------------------------------------------------------------------------------
+// Purpose:
+//---------------------------------------------------------------------------------------
+class CEconTool_ClaimCode : public IEconTool
+{
+public:
+ CEconTool_ClaimCode ( const char *pszTypeName, const char *pszUseString, KeyValues *pUsageKV )
+ : IEconTool( pszTypeName, pszUseString, NULL, ITEM_CAP_NONE )
+ , m_pszClaimType( NULL )
+ {
+ if ( pUsageKV )
+ {
+ m_pszClaimType = pUsageKV->GetString( "claim_type", NULL );
+ }
+ }
+
+ virtual bool BFinishInitialization() OVERRIDE
+ {
+ return m_pszClaimType
+ && IEconTool::BFinishInitialization();
+ }
+
+ const char *GetClaimType() const { return m_pszClaimType; }
+
+#ifdef CLIENT_DLL
+ virtual void OnClientUseConsumable( CEconItemView *pItem, vgui::Panel *pParent ) const;
+#endif // CLIENT_DLL
+
+#ifdef GC_DLL
+ virtual class CGCEconConsumableBehavior *CreateGCConsumableBehavior() const;
+#endif // GC_DLL
+
+private:
+ const char *m_pszClaimType;
+};
+
+//---------------------------------------------------------------------------------------
+// Purpose:
+//---------------------------------------------------------------------------------------
+enum EGiftTargetRule
+{
+ kGiftTargetRule_OnlyOthers = 0,
+ kGiftTargetRule_OnlySelf = 1,
+};
+
+class CEconTool_Gift : public IEconTool
+{
+public:
+ CEconTool_Gift ( const char *pszTypeName, const char *pszUseString, KeyValues *pUsageKV )
+ : IEconTool( pszTypeName, pszUseString, NULL, ITEM_CAP_NONE )
+ , m_pszLootListName( NULL )
+ , m_iMaxRecipients( 0 )
+ , m_eTargetRule( kGiftTargetRule_OnlySelf )
+ {
+ if ( pUsageKV )
+ {
+ m_pszLootListName = pUsageKV->GetString( "loot_list", NULL );
+ m_iMaxRecipients = pUsageKV->GetInt( "max_recipients", 0 );
+ m_eTargetRule = !Q_stricmp( pUsageKV->GetString( "target_rule", "only_others" ), "only_self" )
+ ? kGiftTargetRule_OnlySelf
+ : kGiftTargetRule_OnlyOthers;
+ }
+ }
+
+ virtual bool BFinishInitialization() OVERRIDE
+ {
+ return m_pszLootListName
+ && GetItemSchema()->GetLootListByName( m_pszLootListName )
+ && m_iMaxRecipients > 0
+ && IEconTool::BFinishInitialization();
+ }
+
+ const char *GetLootListName() const { return m_pszLootListName; }
+ int GetMaxRecipients() const { return m_iMaxRecipients; }
+ EGiftTargetRule GetTargetRule() const { return m_eTargetRule; }
+
+#ifdef CLIENT_DLL
+ virtual void OnClientUseConsumable( CEconItemView *pItem, vgui::Panel *pParent ) const;
+#endif // CLIENT_DLL
+
+#ifdef GC_DLL
+ virtual class CGCEconConsumableBehavior *CreateGCConsumableBehavior() const;
+#endif // GC_DLL
+
+private:
+ const char *m_pszLootListName;
+ int m_iMaxRecipients;
+ EGiftTargetRule m_eTargetRule;
+};
+
+//---------------------------------------------------------------------------------------
+// Purpose:
+//---------------------------------------------------------------------------------------
+class CEconTool_PaintCan : public IEconTool
+{
+public:
+ CEconTool_PaintCan( const char *pszTypeName, item_capabilities_t unCapabilities ) : IEconTool( pszTypeName, NULL, NULL, unCapabilities ) { }
+
+#ifdef CLIENT_DLL
+ virtual void OnClientUseConsumable( CEconItemView *pItem, vgui::Panel *pParent ) const;
+ virtual void OnClientApplyTool( CEconItemView *pTool, CEconItemView *pSubject, vgui::Panel *pParent ) const;
+#endif // CLIENT_DLL
+};
+
+//---------------------------------------------------------------------------------------
+// Purpose:
+//---------------------------------------------------------------------------------------
+class CEconTool_GiftWrap : public IEconTool
+{
+public:
+ CEconTool_GiftWrap( const char *pszTypeName, const char *pszUseString, item_capabilities_t unCapabilities, KeyValues *pUsageKV );
+
+ virtual bool BFinishInitialization() OVERRIDE;
+ virtual bool CanApplyTo( const IEconItemInterface *pTool, const IEconItemInterface *pToolSubject ) const;
+ virtual bool RequiresToolEscrowPeriod() const { return false; }
+
+ const CEconItemDefinition *GetWrappedItemDefinition() const { Assert( m_pWrappedGiftItemDef ); return m_pWrappedGiftItemDef; }
+
+#ifdef CLIENT_DLL
+ virtual void OnClientApplyTool( CEconItemView *pTool, CEconItemView *pSubject, vgui::Panel *pParent ) const;
+#endif // CLIENT_DLL
+
+private:
+ const char *m_pszWrappedGiftItemDefName; // points to memory inside our init KV -- only valid between the constructor call and the BFinishInitialization() call (this is messy but Fletcher and I agree it makes more sense than switching to a full two-pass schema parse just for this)
+ const CEconItemDefinition *m_pWrappedGiftItemDef;
+};
+
+//---------------------------------------------------------------------------------------
+// Purpose:
+//---------------------------------------------------------------------------------------
+class CEconTool_NameTag : public IEconTool
+{
+public:
+ CEconTool_NameTag( const char *pszTypeName, item_capabilities_t unCapabilities ) : IEconTool( pszTypeName, NULL, NULL, unCapabilities ) { }
+
+#ifdef CLIENT_DLL
+ virtual void OnClientApplyTool( CEconItemView *pTool, CEconItemView *pSubject, vgui::Panel *pParent ) const;
+#endif // CLIENT_DLL
+};
+
+//---------------------------------------------------------------------------------------
+// Purpose:
+//---------------------------------------------------------------------------------------
+class CEconTool_DescTag : public IEconTool
+{
+public:
+ CEconTool_DescTag( const char *pszTypeName, item_capabilities_t unCapabilities ) : IEconTool( pszTypeName, NULL, NULL, unCapabilities ) { }
+
+#ifdef CLIENT_DLL
+ virtual void OnClientApplyTool( CEconItemView *pTool, CEconItemView *pSubject, vgui::Panel *pParent ) const;
+#endif // CLIENT_DLL
+};
+
+//---------------------------------------------------------------------------------------
+// Purpose:
+//---------------------------------------------------------------------------------------
+class CEconTool_CustomizeTexture : public IEconTool
+{
+public:
+ CEconTool_CustomizeTexture( const char *pszTypeName, item_capabilities_t unCapabilities ) : IEconTool( pszTypeName, NULL, NULL, unCapabilities ) { }
+
+#ifdef CLIENT_DLL
+ virtual void OnClientApplyTool( CEconItemView *pTool, CEconItemView *pSubject, vgui::Panel *pParent ) const;
+#endif // CLIENT_DLL
+};
+
+//---------------------------------------------------------------------------------------
+// Purpose:
+//---------------------------------------------------------------------------------------
+class CEconTool_CrateKey : public IEconTool
+{
+public:
+ CEconTool_CrateKey( const char *pszTypeName, const char *pszUsageRestriction, item_capabilities_t unCapabilities ) : IEconTool( pszTypeName, NULL, pszUsageRestriction, unCapabilities ) { }
+
+#ifdef CLIENT_DLL
+ virtual void OnClientApplyTool( CEconItemView *pTool, CEconItemView *pSubject, vgui::Panel *pParent ) const;
+#endif // CLIENT_DLL
+};
+
+//---------------------------------------------------------------------------------------
+// Purpose:
+//---------------------------------------------------------------------------------------
+class CEconTool_Default : public IEconTool
+{
+public:
+ CEconTool_Default( const char *pszTypeName, const char *pszUseString, const char *pszUsageRestriction, item_capabilities_t unCapabilities )
+ : IEconTool( pszTypeName, pszUseString, pszUsageRestriction, unCapabilities )
+ {
+ Assert( pszTypeName );
+ }
+
+#ifdef CLIENT_DLL
+ virtual void OnClientUseConsumable( CEconItemView *pItem, vgui::Panel *pParent ) const;
+#endif // CLIENT_DLL
+};
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+
+class CCountUserGeneratedAttributeIterator : public IEconItemUntypedAttributeIterator
+{
+public:
+ CCountUserGeneratedAttributeIterator() : m_iCount( 0 ) { }
+
+ virtual bool OnIterateAttributeValueUntyped( const CEconItemAttributeDefinition *pAttrDef ) OVERRIDE
+ {
+ if ( pAttrDef->GetUserGenerationType() != 0 )
+ {
+ m_iCount++;
+ }
+
+ return true;
+ }
+
+ int GetCount() const { return m_iCount; }
+
+private:
+ int m_iCount;
+};
+
+#endif // ECONITEMTOOLS_H
diff --git a/game/shared/econ/econ_item_view.cpp b/game/shared/econ/econ_item_view.cpp
new file mode 100644
index 0000000..e25390b
--- /dev/null
+++ b/game/shared/econ/econ_item_view.cpp
@@ -0,0 +1,1971 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+
+#include "cbase.h"
+#include "econ_item_view.h"
+#include "econ_item_system.h"
+#include "econ_item_description.h"
+#include "econ_item_inventory.h"
+
+#include "rtime.h"
+#include "econ_gcmessages.h"
+#include "gamestringpool.h"
+
+// For localization
+#include "tier3/tier3.h"
+#include "vgui/ILocalize.h"
+
+#include "isaverestore.h"
+#include "dt_utlvector_send.h"
+#include "dt_utlvector_recv.h"
+#include <vgui_controls/Panel.h>
+
+#ifdef CLIENT_DLL
+#ifndef DEDICATED
+#include "vgui/IScheme.h"
+#endif
+#endif
+
+#if defined(TF_CLIENT_DLL)
+#include "tf_duel_summary.h"
+#include "econ_contribution.h"
+#include "tf_player_info.h"
+#include "tf_gcmessages.h"
+#include "c_tf_freeaccount.h"
+#include "c_tf_player.h"
+#endif
+
+#include "materialsystem/itexture.h"
+#include "materialsystem/itexturecompositor.h"
+
+#include "activitylist.h"
+
+#ifdef CLIENT_DLL
+#include "gc_clientsystem.h"
+#endif // CLIENT_DLL
+
+#ifdef GC_DLL
+#error "CEconItemView is not meant to be compiled on the GC! There are silent assumptions made about attributes, etc."
+#endif // GC_DLL
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+
+#ifdef STAGING_ONLY
+ConVar econ_force_style_index( "econ_force_style_index", "-1", FCVAR_REPLICATED );
+#endif // STAGING_ONLY
+
+
+// Networking tables for attributes
+BEGIN_NETWORK_TABLE_NOBASE( CEconItemAttribute, DT_ScriptCreatedAttribute )
+
+ // Note: we are networking the value as an int, even though it's a "float", because really it isn't
+ // a float. It's 32 raw bits.
+
+#ifndef CLIENT_DLL
+ SendPropInt( SENDINFO(m_iAttributeDefinitionIndex), -1, SPROP_UNSIGNED ),
+ SendPropInt( SENDINFO_NAME(m_flValue, m_iRawValue32), 32, SPROP_UNSIGNED ),
+#if ENABLE_ATTRIBUTE_CURRENCY_TRACKING
+ SendPropInt( SENDINFO(m_nRefundableCurrency), -1, SPROP_UNSIGNED ),
+#endif // ENABLE_ATTRIBUTE_CURRENCY_TRACKING
+#else
+ RecvPropInt( RECVINFO(m_iAttributeDefinitionIndex) ),
+ RecvPropInt( RECVINFO_NAME(m_flValue, m_iRawValue32) ),
+ RecvPropFloat( RECVINFO(m_flValue), SPROP_NOSCALE ), // for demo compatibility only
+#if ENABLE_ATTRIBUTE_CURRENCY_TRACKING
+ RecvPropInt( RECVINFO(m_nRefundableCurrency) ),
+#endif // ENABLE_ATTRIBUTE_CURRENCY_TRACKING
+#endif
+END_NETWORK_TABLE()
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CEconItemAttribute::CEconItemAttribute( void )
+{
+ Init();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CEconItemAttribute::CEconItemAttribute( const attrib_definition_index_t iAttributeIndex, float flValue )
+{
+ Init();
+
+ m_iAttributeDefinitionIndex = iAttributeIndex;
+ SetValue( flValue );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CEconItemAttribute::CEconItemAttribute( const attrib_definition_index_t iAttributeIndex, uint32 unValue )
+{
+ Init();
+
+ m_iAttributeDefinitionIndex = iAttributeIndex;
+ SetIntValue( unValue );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CEconItemAttribute::SetValue( float flValue )
+{
+// Assert( GetStaticData() && GetStaticData()->IsStoredAsFloat() );
+ m_flValue = flValue;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CEconItemAttribute::SetIntValue( uint32 unValue )
+{
+ // @note we don't check the storage type here, because this is how it is set from the data file
+ // Note that numbers approaching two billion cannot be stored in a float
+ // representation because they will map to NaNs. Numbers below 16 million
+ // will fail if denormals are disabled.
+ m_flValue = *(float*)&unValue;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CEconItemAttribute::Init( void )
+{
+ m_iAttributeDefinitionIndex = INVALID_ATTRIB_DEF_INDEX;
+ m_flValue = 0.0f;
+
+#if ENABLE_ATTRIBUTE_CURRENCY_TRACKING
+ m_nRefundableCurrency = 0;
+#endif // ENABLE_ATTRIBUTE_CURRENCY_TRACKING
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CEconItemAttribute::operator=( const CEconItemAttribute &val )
+{
+ m_iAttributeDefinitionIndex = val.m_iAttributeDefinitionIndex;
+ m_flValue = val.m_flValue;
+
+#if ENABLE_ATTRIBUTE_CURRENCY_TRACKING
+ m_nRefundableCurrency = val.m_nRefundableCurrency;
+#endif // ENABLE_ATTRIBUTE_CURRENCY_TRACKING
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+const CEconItemAttributeDefinition *CEconItemAttribute::GetStaticData( void ) const
+{
+ return GetItemSchema()->GetAttributeDefinition( m_iAttributeDefinitionIndex );
+}
+
+BEGIN_NETWORK_TABLE_NOBASE( CAttributeList, DT_AttributeList )
+#if !defined( CLIENT_DLL )
+SendPropUtlVectorDataTable( m_Attributes, MAX_ATTRIBUTES_PER_ITEM, DT_ScriptCreatedAttribute ),
+#else
+RecvPropUtlVectorDataTable( m_Attributes, MAX_ATTRIBUTES_PER_ITEM, DT_ScriptCreatedAttribute ),
+#endif // CLIENT_DLL
+END_NETWORK_TABLE()
+
+BEGIN_DATADESC_NO_BASE( CAttributeList )
+END_DATADESC()
+
+//===========================================================================================================================
+// SCRIPT CREATED ITEMS
+//===========================================================================================================================
+BEGIN_NETWORK_TABLE_NOBASE( CEconItemView, DT_ScriptCreatedItem )
+#if !defined( CLIENT_DLL )
+ SendPropInt( SENDINFO( m_iItemDefinitionIndex ), 20, SPROP_UNSIGNED ),
+ SendPropInt( SENDINFO( m_iEntityLevel ), 8 ),
+ //SendPropInt( SENDINFO( m_iItemID ), 64, SPROP_UNSIGNED ),
+ SendPropInt( SENDINFO( m_iItemIDHigh ), 32, SPROP_UNSIGNED ),
+ SendPropInt( SENDINFO( m_iItemIDLow ), 32, SPROP_UNSIGNED ),
+ SendPropInt( SENDINFO( m_iAccountID ), 32, SPROP_UNSIGNED ),
+ SendPropInt( SENDINFO( m_iEntityQuality ), 5 ),
+ SendPropBool( SENDINFO( m_bInitialized ) ),
+ SendPropBool( SENDINFO( m_bOnlyIterateItemViewAttributes) ),
+ SendPropDataTable(SENDINFO_DT(m_AttributeList), &REFERENCE_SEND_TABLE(DT_AttributeList)),
+ SendPropInt( SENDINFO( m_iTeamNumber ) ),
+ SendPropDataTable(SENDINFO_DT( m_NetworkedDynamicAttributesForDemos ), &REFERENCE_SEND_TABLE( DT_AttributeList ) ),
+#else
+ RecvPropInt( RECVINFO( m_iItemDefinitionIndex ) ),
+ RecvPropInt( RECVINFO( m_iEntityLevel ) ),
+ //RecvPropInt( RECVINFO( m_iItemID ) ),
+ RecvPropInt( RECVINFO( m_iItemIDHigh ) ),
+ RecvPropInt( RECVINFO( m_iItemIDLow ) ),
+ RecvPropInt( RECVINFO( m_iAccountID ) ),
+ RecvPropInt( RECVINFO( m_iEntityQuality ) ),
+ RecvPropBool( RECVINFO( m_bInitialized ) ),
+ RecvPropBool( RECVINFO( m_bOnlyIterateItemViewAttributes ) ),
+ RecvPropDataTable(RECVINFO_DT(m_AttributeList), 0, &REFERENCE_RECV_TABLE(DT_AttributeList)),
+ RecvPropInt( RECVINFO( m_iTeamNumber ) ),
+ RecvPropDataTable( RECVINFO_DT( m_NetworkedDynamicAttributesForDemos ), 0, &REFERENCE_RECV_TABLE( DT_AttributeList ) ),
+#endif // CLIENT_DLL
+END_NETWORK_TABLE()
+
+BEGIN_DATADESC_NO_BASE( CEconItemView )
+ DEFINE_FIELD( m_iItemDefinitionIndex, FIELD_INTEGER ),
+ DEFINE_FIELD( m_iEntityQuality, FIELD_INTEGER ),
+ DEFINE_FIELD( m_iEntityLevel, FIELD_INTEGER ),
+ DEFINE_FIELD( m_iItemID, FIELD_INTEGER ),
+ // DEFINE_FIELD( m_wszItemName, FIELD_STRING ), Regenerated post-save
+ // DEFINE_FIELD( m_szItemName, FIELD_STRING ), Regenerated post-save
+ // DEFINE_FIELD( m_szAttributeDescription, FIELD_STRING ), Regenerated post-save
+ // m_AttributeLineColors // Regenerated post-save
+ // m_Attributes // Custom handling in Save()/Restore()
+ DEFINE_FIELD( m_bInitialized, FIELD_BOOLEAN ),
+ DEFINE_FIELD( m_bOnlyIterateItemViewAttributes, FIELD_BOOLEAN ),
+ DEFINE_EMBEDDED( m_AttributeList ),
+ DEFINE_EMBEDDED( m_NetworkedDynamicAttributesForDemos ),
+END_DATADESC()
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CEconItemView::CEconItemView( void )
+{
+ m_iItemDefinitionIndex = INVALID_ITEM_DEF_INDEX;
+ m_iEntityQuality = (int)AE_UNDEFINED;
+ m_iEntityLevel = 0;
+ SetItemID( INVALID_ITEM_ID );
+ m_iInventoryPosition = 0;
+ m_bInitialized = false;
+ m_bOnlyIterateItemViewAttributes = false;
+ m_iAccountID = 0;
+ m_pNonSOEconItem = NULL;
+ m_bColorInit = false;
+ m_bPaintOverrideInit = false;
+ m_bHasPaintOverride = false;
+ m_flOverrideIndex = 0.f;
+#if defined( CLIENT_DLL )
+ m_bIsTradeItem = false;
+ m_iEntityQuantity = 1;
+ m_unClientFlags = 0;
+ m_unOverrideStyle = INVALID_STYLE_INDEX;
+ m_unOverrideOrigin = kEconItemOrigin_Max;
+#endif
+#if BUILD_ITEM_NAME_AND_DESC
+ m_pDescription = NULL;
+ m_pszGrayedOutReason = NULL;
+#endif
+
+#ifdef CLIENT_DLL
+ m_pWeaponSkinBase = NULL;
+ m_pWeaponSkinBaseCompositor = NULL;
+ m_iLastGeneratedTeamSkin = TF_TEAM_RED;
+ m_bWeaponSkinUseHighRes = false;
+ m_bWeaponSkinUseLowRes = false;
+#endif // CLIENT_DLL
+
+ m_iTeamNumber = TF_TEAM_RED;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CEconItemView::~CEconItemView( void )
+{
+#ifdef CLIENT_DLL
+ SafeRelease( &m_pWeaponSkinBase );
+ SafeRelease( &m_pWeaponSkinBaseCompositor );
+#endif // CLIENT_DLL
+
+ DestroyAllAttributes();
+
+#if BUILD_ITEM_NAME_AND_DESC
+ MarkDescriptionDirty();
+ free( m_pszGrayedOutReason );
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CEconItemView::CEconItemView( const CEconItemView &src )
+{
+#if BUILD_ITEM_NAME_AND_DESC
+ m_pDescription = NULL;
+ m_pszGrayedOutReason = NULL;
+#endif
+
+#ifdef CLIENT_DLL
+ // Need to null these out here for initial behavior.
+ m_pWeaponSkinBase = NULL;
+ m_pWeaponSkinBaseCompositor = NULL;
+ m_nWeaponSkinGeneration = 0;
+ m_iLastGeneratedTeamSkin = TF_TEAM_RED;
+ m_unWeaponSkinBaseCreateFlags = 0;
+ m_bWeaponSkinUseHighRes = src.m_bWeaponSkinUseHighRes;
+ m_bWeaponSkinUseLowRes = src.m_bWeaponSkinUseLowRes;
+#endif //CLIENT_DLL
+
+ m_iTeamNumber = src.m_iTeamNumber; // keep the same team from the first creation
+
+ *this = src;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CEconItemView::Init( int iDefIndex, int iQuality, int iLevel, uint32 iAccountID )
+{
+ m_AttributeList.Init();
+ m_NetworkedDynamicAttributesForDemos.Init();
+
+ m_iItemDefinitionIndex = iDefIndex;
+ CEconItemDefinition *pData = GetStaticData();
+ if ( !pData )
+ {
+ // We've got an item that we don't have static data for.
+ return;
+ }
+
+ SetItemID( INVALID_ITEM_ID );
+ m_bInitialized = true;
+ m_iAccountID = iAccountID;
+
+ if ( iQuality == AE_USE_SCRIPT_VALUE )
+ {
+ m_iEntityQuality = pData->GetQuality();
+
+ // Kyle says: this is a horrible hack because AE_UNDEFINED will get stuffed into a uint8 when
+ // loaded into the item definition, but then read back out into a regular int here.
+ if ( m_iEntityQuality == (uint8)AE_UNDEFINED )
+ {
+ m_iEntityQuality = (int)AE_NORMAL;
+ }
+ }
+ else if ( iQuality == k_unItemQuality_Any )
+ {
+ m_iEntityQuality = (int)AE_RARITY1;
+ }
+ else
+ {
+ m_iEntityQuality = iQuality;
+ }
+
+ if ( iLevel == AE_USE_SCRIPT_VALUE )
+ {
+ m_iEntityLevel = pData->RollItemLevel();
+ }
+ else
+ {
+ m_iEntityLevel = iLevel;
+ }
+
+ // We made changes to quality, level, etc. so mark the description as dirty.
+ MarkDescriptionDirty();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CEconItemView& CEconItemView::operator=( const CEconItemView& src )
+{
+ m_iItemDefinitionIndex = src.m_iItemDefinitionIndex;
+ m_iEntityQuality = src.m_iEntityQuality;
+ m_iEntityLevel = src.m_iEntityLevel;
+ SetItemID( src.GetItemID() );
+ m_iInventoryPosition = src.m_iInventoryPosition;
+ m_bInitialized = src.m_bInitialized;
+ m_bOnlyIterateItemViewAttributes = src.m_bOnlyIterateItemViewAttributes;
+ m_iAccountID = src.m_iAccountID;
+ SetNonSOEconItem(src.m_pNonSOEconItem);
+ m_bColorInit = false; // reset Color init
+ m_bPaintOverrideInit = false;
+ m_bHasPaintOverride = false;
+ m_flOverrideIndex = 0.f;
+#ifdef CLIENT_DLL
+ m_iLastGeneratedTeamSkin = src.m_iLastGeneratedTeamSkin;
+ m_bIsTradeItem = src.m_bIsTradeItem;
+ m_iEntityQuantity = src.m_iEntityQuantity;
+ m_unClientFlags = src.m_unClientFlags;
+ m_unOverrideStyle = src.m_unOverrideStyle;
+ m_unOverrideOrigin = src.m_unOverrideOrigin;
+
+ SafeAssign( &m_pWeaponSkinBase, src.m_pWeaponSkinBase );
+ SafeAssign( &m_pWeaponSkinBaseCompositor, src.m_pWeaponSkinBaseCompositor );
+
+ m_nWeaponSkinGeneration = src.m_nWeaponSkinGeneration;
+ m_unWeaponSkinBaseCreateFlags = src.m_unWeaponSkinBaseCreateFlags;
+
+ m_bWeaponSkinUseHighRes = src.m_bWeaponSkinUseHighRes;
+ m_bWeaponSkinUseLowRes = src.m_bWeaponSkinUseLowRes;
+
+#endif // CLIENT_DLL
+
+ m_iTeamNumber = src.m_iTeamNumber; // keep the same team from the first creation
+
+ DestroyAllAttributes();
+
+ m_AttributeList = src.m_AttributeList;
+ m_NetworkedDynamicAttributesForDemos = src.m_NetworkedDynamicAttributesForDemos;
+
+ // TODO: Copying the description pointer and refcounting it would work also.
+ MarkDescriptionDirty();
+
+ // Clear out any overrides we currently have, they'll get reset up on demand.
+ ResetMaterialOverrides();
+ return *this;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CEconItemView::operator==( const CEconItemView &other ) const
+{
+ if ( IsValid() != other.IsValid() )
+ return false;
+ if ( ( GetItemID() != INVALID_ITEM_ID || other.GetItemID() != INVALID_ITEM_ID ) && GetItemID() != other.GetItemID() )
+ return false;
+ if ( GetItemDefIndex() != other.GetItemDefIndex() )
+ return false;
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+GameItemDefinition_t *CEconItemView::GetStaticData( void ) const
+{
+ CEconItemDefinition *pRet = GetItemSchema()->GetItemDefinition( m_iItemDefinitionIndex );
+ GameItemDefinition_t *pTypedRet = dynamic_cast<GameItemDefinition_t *>( pRet );
+
+ AssertMsg( pRet == pTypedRet, "Item definition of inappropriate type." );
+
+ return pTypedRet;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+int32 CEconItemView::GetQuality() const
+{
+ return GetSOCData()
+ ? GetSOCData()->GetQuality()
+#ifdef TF_CLIENT_DLL
+ : GetFlags() & kEconItemFlagClient_StoreItem
+ ? AE_UNIQUE
+#endif
+ : GetOrigin() != kEconItemOrigin_Invalid
+ ? GetItemQuality()
+ : AE_NORMAL;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+style_index_t CEconItemView::GetStyle() const
+{
+ return GetItemStyle();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+uint8 CEconItemView::GetFlags() const
+{
+ uint8 unSOCFlags = GetSOCData() ? GetSOCData()->GetFlags() : 0;
+
+#if !defined( GAME_DLL )
+ return unSOCFlags | m_unClientFlags;
+#else // defined( GAME_DLL )
+ return unSOCFlags;
+#endif // !defined( GAME_DLL )
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+eEconItemOrigin CEconItemView::GetOrigin() const
+{
+#ifdef CLIENT_DLL
+ if( m_unOverrideOrigin != kEconItemOrigin_Max )
+ {
+ return m_unOverrideOrigin;
+ }
+#endif//CLIENT_DLL
+
+ return GetSOCData() ? GetSOCData()->GetOrigin() : kEconItemOrigin_Invalid;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+int CEconItemView::GetQuantity() const
+{
+ return GetItemQuantity();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+const char *CEconItemView::GetCustomName() const
+{
+ return GetSOCData() ? GetSOCData()->GetCustomName() : NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+const char *CEconItemView::GetCustomDesc() const
+{
+ return GetSOCData() ? GetSOCData()->GetCustomDesc() : NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CEconItemView::IterateAttributes( class IEconItemAttributeIterator *pIterator ) const
+{
+ Assert( pIterator );
+
+ // Note if we have network attribs, because m_NetworkedDynamicAttributesForDemos might be the iterator
+ // which we're about to fill up below.
+ const bool bHasNetworkedAttribsForDemos = m_NetworkedDynamicAttributesForDemos.GetNumAttributes() > 0;
+
+ // First, we iterate over the attributes we have local copies of. If we have any attribute
+ // values here they'll override whatever values we would otherwise have pulled from our
+ // definition/CEconItem.
+ const CAttributeList *pAttrList = GetAttributeList();
+ if ( pAttrList )
+ {
+ pAttrList->IterateAttributes( pIterator );
+ }
+
+ if ( m_bOnlyIterateItemViewAttributes )
+ return;
+
+ // This wraps any other iterator class and will prevent double iteration of any attributes
+ // that exist on us.
+ class CEconItemAttributeIterator_EconItemViewWrapper : public IEconItemAttributeIterator
+ {
+ public:
+ CEconItemAttributeIterator_EconItemViewWrapper( const CEconItemView *pEconItemView, IEconItemAttributeIterator *pIterator )
+ : m_pEconItemView( pEconItemView )
+ , m_pIterator( pIterator )
+ {
+ Assert( m_pEconItemView );
+ Assert( m_pIterator );
+ }
+
+ virtual bool OnIterateAttributeValue( const CEconItemAttributeDefinition *pAttrDef, attrib_value_t value )
+ {
+ Assert( pAttrDef );
+
+ return m_pEconItemView->GetAttributeList()->GetAttributeByID( pAttrDef->GetDefinitionIndex() )
+ ? true
+ : m_pIterator->OnIterateAttributeValue( pAttrDef, value );
+ }
+
+ virtual bool OnIterateAttributeValue( const CEconItemAttributeDefinition *pAttrDef, float value )
+ {
+ Assert( pAttrDef );
+
+ return m_pEconItemView->GetAttributeList()->GetAttributeByID( pAttrDef->GetDefinitionIndex() )
+ ? true
+ : m_pIterator->OnIterateAttributeValue( pAttrDef, value );
+ }
+
+ virtual bool OnIterateAttributeValue( const CEconItemAttributeDefinition *pAttrDef, const uint64& value )
+ {
+ Assert( pAttrDef );
+
+ return m_pEconItemView->GetAttributeList()->GetAttributeByID( pAttrDef->GetDefinitionIndex() )
+ ? true
+ : m_pIterator->OnIterateAttributeValue( pAttrDef, value );
+ }
+
+ virtual bool OnIterateAttributeValue( const CEconItemAttributeDefinition *pAttrDef, const CAttribute_String& value )
+ {
+ Assert( pAttrDef );
+
+ return m_pEconItemView->GetAttributeList()->GetAttributeByID( pAttrDef->GetDefinitionIndex() )
+ ? true
+ : m_pIterator->OnIterateAttributeValue( pAttrDef, value );
+ }
+
+ virtual bool OnIterateAttributeValue( const CEconItemAttributeDefinition *pAttrDef, const CAttribute_DynamicRecipeComponent& value )
+ {
+ Assert( pAttrDef );
+
+ return m_pEconItemView->GetAttributeList()->GetAttributeByID( pAttrDef->GetDefinitionIndex() )
+ ? true
+ : m_pIterator->OnIterateAttributeValue( pAttrDef, value );
+ }
+
+ virtual bool OnIterateAttributeValue( const CEconItemAttributeDefinition *pAttrDef, const CAttribute_ItemSlotCriteria& value )
+ {
+ Assert( pAttrDef );
+
+ return m_pEconItemView->GetAttributeList()->GetAttributeByID( pAttrDef->GetDefinitionIndex() )
+ ? true
+ : m_pIterator->OnIterateAttributeValue( pAttrDef, value );
+ }
+
+ virtual bool OnIterateAttributeValue( const CEconItemAttributeDefinition *pAttrDef, const CAttribute_WorldItemPlacement& value )
+ {
+ Assert( pAttrDef );
+
+ return m_pEconItemView->GetAttributeList()->GetAttributeByID( pAttrDef->GetDefinitionIndex() )
+ ? true
+ : m_pIterator->OnIterateAttributeValue( pAttrDef, value );
+ }
+
+ private:
+ const CEconItemView *m_pEconItemView;
+ IEconItemAttributeIterator *m_pIterator;
+ };
+
+ CEconItemAttributeIterator_EconItemViewWrapper iteratorWrapper( this, pIterator );
+
+ // Next, iterate over our database-backed item if we have one... if we do have a DB
+ // backing for our item here, that will also feed in the definition attributes.
+ CEconItem *pEconItem = GetSOCData();
+ if ( pEconItem )
+ {
+ pEconItem->IterateAttributes( &iteratorWrapper );
+ }
+ else if ( GetItemID() != INVALID_ITEM_ID && bHasNetworkedAttribsForDemos )
+ {
+ // Since there's no persistent data available, try the networked values!
+ // note: only copies the default type and floats
+ m_NetworkedDynamicAttributesForDemos.IterateAttributes( &iteratorWrapper );
+ }
+ // If we didn't have a DB backing, we can still iterate over our item definition
+ // attributes ourselves. This can happen if we're previewing an item in the store, etc.
+ else if ( GetStaticData() )
+ {
+ GetStaticData()->IterateAttributes( &iteratorWrapper );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CEconItemView::EnsureDescriptionIsBuilt() const
+{
+#if BUILD_ITEM_NAME_AND_DESC
+ if ( m_pDescription )
+ {
+ return;
+ }
+
+ m_pDescription = new CEconItemDescription;
+
+ IEconItemDescription::YieldingFillOutEconItemDescription( m_pDescription, GLocalizationProvider(), this );
+
+ // We use the empty string to mean "grey out but don't specify a user-facing reason".
+ if ( m_pszGrayedOutReason && m_pszGrayedOutReason[0] )
+ {
+ m_pDescription->AddEmptyDescLine();
+ m_pDescription->LocalizedAddDescLine( GLocalizationProvider(), m_pszGrayedOutReason, ATTRIB_COL_NEGATIVE, kDescLineFlag_Misc );
+ }
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CEconItemView::MarkDescriptionDirty()
+{
+#if BUILD_ITEM_NAME_AND_DESC
+ if ( m_pDescription )
+ {
+ delete m_pDescription;
+ m_pDescription = NULL;
+ }
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CEconItemView::SetGrayedOutReason( const char *pszGrayedOutReason )
+{
+#if BUILD_ITEM_NAME_AND_DESC
+ if ( m_pszGrayedOutReason )
+ {
+ free( m_pszGrayedOutReason );
+ m_pszGrayedOutReason = NULL;
+ }
+
+ if ( pszGrayedOutReason )
+ {
+ m_pszGrayedOutReason = strdup(pszGrayedOutReason);
+ }
+
+ MarkDescriptionDirty();
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+int CEconItemView::GetItemQuantity() const
+{
+ CEconItem *pSOCData = GetSOCData();
+ if ( pSOCData )
+ {
+ return pSOCData->GetQuantity();
+ }
+#ifdef CLIENT_DLL
+ return m_iEntityQuantity;
+#else
+ return 1;
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+style_index_t CEconItemView::GetItemStyle() const
+{
+#ifdef STAGING_ONLY
+ if ( econ_force_style_index.GetInt() != -1 )
+ return econ_force_style_index.GetInt();
+#endif // STAGING_ONLY
+
+#ifdef CLIENT_DLL
+ // Are we overriding the backing store style?
+ if ( m_unOverrideStyle != INVALID_STYLE_INDEX )
+ return m_unOverrideStyle;
+#endif // CLIENT_DLL
+
+ static CSchemaAttributeDefHandle pAttrDef_ItemStyleOverride( "item style override" );
+ float fStyleOverride = 0.f;
+ if ( FindAttribute_UnsafeBitwiseCast<attrib_value_t>( this, pAttrDef_ItemStyleOverride, &fStyleOverride ) )
+ {
+ return fStyleOverride;
+ }
+
+ static CSchemaAttributeDefHandle pAttrDef_ItemStyleStrange( "style changes on strange level" );
+ uint32 iMaxStyle = 0;
+ if ( pAttrDef_ItemStyleStrange && FindAttribute( pAttrDef_ItemStyleStrange, &iMaxStyle ) )
+ {
+ // Use the strange prefix if the weapon has one.
+ uint32 unScore = 0;
+ if ( !FindAttribute( GetKillEaterAttr_Score( 0 ), &unScore ) )
+ return 0;
+
+ // What type of event are we tracking and how does it describe itself?
+ uint32 unKillEaterEventType = 0;
+ // This will overwrite our default 0 value if we have a value set but leave it if not.
+ float fKillEaterEventType;
+ if ( FindAttribute_UnsafeBitwiseCast<attrib_value_t>( this, GetKillEaterAttr_Type( 0 ), &fKillEaterEventType ) )
+ {
+ unKillEaterEventType = fKillEaterEventType;
+ }
+
+ const char *pszLevelingDataName = GetItemSchema()->GetKillEaterScoreTypeLevelingDataName( unKillEaterEventType );
+ if ( !pszLevelingDataName )
+ {
+ pszLevelingDataName = KILL_EATER_RANK_LEVEL_BLOCK_NAME;
+ }
+
+ const CItemLevelingDefinition *pLevelDef = GetItemSchema()->GetItemLevelForScore( pszLevelingDataName, unScore );
+ if ( !pLevelDef )
+ return 0;
+
+ return Min( pLevelDef->GetLevel(), iMaxStyle );
+ }
+
+
+ CEconItem *pSOCData = GetSOCData();
+ if ( pSOCData )
+ return pSOCData->GetStyle();
+
+ return INVALID_STYLE_INDEX;
+}
+
+#ifdef CLIENT_DLL
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CEconItemView::SetClientItemFlags( uint8 unFlags )
+{
+ // Generally speaking, we have two uses for client flags:
+ //
+ // - we don't have a backing store (a real CEconItem) but want to pretend we do
+ // for purposes of generating tooltips, graying out icons, etc.
+ //
+ // - we may or may not have a backing store but want to shove client-specific
+ // information into the structure -- things like "this is the item being
+ // actively previewed", etc.
+ //
+ // If neither of these two cases is true, then we're going to get unexpected
+ // behavior where the GC and the client disagree about the item flags and then
+ // Terrible Things happen. We assert to make sure we're in one of the above cases.
+ Assert( !GetSOCData() || (unFlags & kEconItemFlags_CheckFlags_AllGCFlags) == 0 );
+
+ m_unClientFlags = unFlags;
+ MarkDescriptionDirty();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CEconItemView::SetItemStyleOverride( style_index_t unNewStyleOverride )
+{
+ // We should only ever override the style on items that don't have a real
+ // backing store or we'll start getting disagreements about what the client
+ // wants to happen and what's being stored on the GC. Unfortunately we can't
+ // assert on this because we do it sometimes when previewing items.
+ //Assert( !GetSOCData() );
+
+ m_unOverrideStyle = unNewStyleOverride;
+ MarkDescriptionDirty();
+}
+
+
+void CEconItemView::SetItemOriginOverride( eEconItemOrigin unNewOriginOverride )
+{
+ Assert( !GetSOCData() || m_pNonSOEconItem );
+ Assert( unNewOriginOverride >= kEconItemOrigin_Invalid );
+ Assert( unNewOriginOverride <= kEconItemOrigin_Max ); // Allow max. We ignore this value if it's max
+
+ m_unOverrideOrigin = unNewOriginOverride;
+}
+#endif // CLIENT_DLL
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CEconItem *CEconItemView::GetSOCData( void ) const
+{
+ if ( m_pNonSOEconItem )
+ return m_pNonSOEconItem;
+
+#ifdef CLIENT_DLL
+ // We need to find the inventory that contains this item. If we're not connected
+ // to a server, and the owner is the same as the local player, use the local inventory.
+ // We need to do this for trading, since we are subscribed to the other person's cache.
+ if ( !engine->IsInGame() && InventoryManager()->GetLocalInventory()->GetOwner().GetAccountID() == m_iAccountID )
+ return InventoryManager()->GetLocalInventory()->GetSOCDataForItem( GetItemID() );
+#endif // CLIENT_DLL
+
+ // We're in-game. Find the inventory with our account ID.
+ CPlayerInventory *pInventory = InventoryManager()->GetInventoryForAccount( m_iAccountID );
+ if ( pInventory )
+ return pInventory->GetSOCDataForItem( GetItemID() );
+
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Return the model to use for model panels containing this item
+//-----------------------------------------------------------------------------
+const char *CEconItemView::GetInventoryModel( void )
+{
+ if ( !GetStaticData() )
+ return NULL;
+ return GetStaticData()->GetInventoryModel();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Return the image to use for model panels containing this item
+//-----------------------------------------------------------------------------
+const char *CEconItemView::GetInventoryImage( void )
+{
+ if ( !GetStaticData() )
+ return NULL;
+
+ // Do we have a style set?
+ const char* pStyleImage = NULL;
+ if ( GetStaticData()->GetNumStyles() )
+ pStyleImage = GetStaticData()->GetStyleInventoryImage( GetItemStyle() );
+
+ if ( pStyleImage && *pStyleImage )
+ return pStyleImage;
+
+ return GetStaticData()->GetInventoryImage();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Return the drawing data for the image to use for model panels containing this item
+//-----------------------------------------------------------------------------
+bool CEconItemView::GetInventoryImageData( int *iPosition, int *iSize )
+{
+ if ( !GetStaticData() )
+ return false;
+ for ( int i = 0; i < 2; i++ )
+ {
+ iPosition[i] = GetStaticData()->GetInventoryImagePosition(i);
+ iSize[i] = GetStaticData()->GetInventoryImageSize(i);
+ }
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Return the image to use for model panels containing this item
+//-----------------------------------------------------------------------------
+const char *CEconItemView::GetInventoryOverlayImage( int idx )
+{
+ if ( !GetStaticData() )
+ return NULL;
+ return GetStaticData()->GetInventoryOverlayImage( idx );
+}
+
+int CEconItemView::GetInventoryOverlayImageCount( void )
+{
+ if ( !GetStaticData() )
+ return 0;
+ return GetStaticData()->GetInventoryOverlayImageCount();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Return the model to use when displaying this model on the player character model, if any
+//-----------------------------------------------------------------------------
+const char *CEconItemView::GetPlayerDisplayModel( int iClass, int iTeam ) const
+{
+ const CEconItemDefinition *pDef = GetStaticData();
+ if ( !pDef )
+ return NULL;
+
+ // If we have styles, give the style system a chance to change the mesh used for this
+ // player class.
+ if ( pDef->GetNumStyles() )
+ {
+ style_index_t unStyle = GetItemStyle();
+
+ const CEconStyleInfo *pStyle = pDef->GetStyleInfo( unStyle );
+
+ // It's possible to get back a NULL pStyle if GetItemStyle() returns INVALID_STYLE_INDEX.
+ if ( pStyle )
+ {
+#if defined( TF_DLL ) || defined( TF_CLIENT_DLL )
+ // TF styles support per-class models.
+ const CTFStyleInfo *pTFStyle = assert_cast<const CTFStyleInfo *>( pStyle );
+ if ( pTFStyle->GetPlayerDisplayModel( iClass, iTeam ) )
+ return pTFStyle->GetPlayerDisplayModel( iClass, iTeam );
+#endif // defined( TF_DLL ) || defined( TF_CLIENT_DLL )
+
+ if ( pStyle->GetBasePlayerDisplayModel() )
+ return pStyle->GetBasePlayerDisplayModel();
+ }
+ }
+
+#if defined( TF_DLL ) || defined( TF_CLIENT_DLL )
+ // If we don't have a style, we still a couple potential overrides.
+ if ( iClass >= 0 && iClass < LOADOUT_COUNT )
+ {
+ // We don't support overriding meshes in the visuals section, but we might still be overriding
+ // the model for each class at the schema level.
+ const CTFItemDefinition *pTFDef = dynamic_cast<const CTFItemDefinition *>( pDef );
+ if ( pTFDef )
+ {
+ const char *pszModel = pTFDef->GetPlayerDisplayModel(iClass);
+ if ( pszModel && pszModel[0] )
+ return pszModel;
+ }
+ }
+#endif // defined( TF_DLL ) || defined( TF_CLIENT_DLL )
+
+ return pDef->GetBasePlayerDisplayModel();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+int CEconItemView::GetSkin( int iTeam, bool bViewmodel /*= false*/ ) const
+{
+ int iDefaultSkin = -1;
+#ifndef CSTRIKE_DLL
+ // Immediately abort if we're out of range.
+ if ( iTeam < 0 || iTeam >= TEAM_VISUAL_SECTIONS )
+ return 0;
+
+ // Do we have a style set?
+ if ( GetStaticData()->GetNumStyles() )
+ return GetStaticData()->GetStyleSkin( GetItemStyle(), iTeam, bViewmodel );
+
+ iTeam = GetStaticData()->GetBestVisualTeamData( iTeam );
+ if ( iTeam < 0 || iTeam >= TEAM_VISUAL_SECTIONS )
+ return -1;
+
+ // Do we have per-team skins set?
+ const perteamvisuals_t *pVisData = GetStaticData()->GetPerTeamVisual( iTeam );
+ if ( pVisData )
+ return pVisData->iSkin;
+
+ iDefaultSkin = GetItemDefinition()->GetDefaultSkin();
+#endif
+
+ // Fallback case.
+ return iDefaultSkin;
+}
+
+#ifdef CLIENT_DLL
+
+//-----------------------------------------------------------------------------
+// Purpose: Handle assignment for textures, which involves some reference counting shenanigans.
+//-----------------------------------------------------------------------------
+void CEconItemView::SetWeaponSkinBase( ITexture* pBaseTex )
+{
+ SafeAssign( &m_pWeaponSkinBase, pBaseTex );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Handle assignment for compositors, which involves some reference counting shenanigans.
+//-----------------------------------------------------------------------------
+void CEconItemView::SetWeaponSkinBaseCompositor( ITextureCompositor * pTexCompositor )
+{
+ SafeAssign( &m_pWeaponSkinBaseCompositor, pTexCompositor );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Cancels a pending composite, if one is currently in process.
+//-----------------------------------------------------------------------------
+void CEconItemView::CancelWeaponSkinComposite( )
+{
+ SafeRelease( &m_pWeaponSkinBaseCompositor );
+}
+#endif // CLIENT_DLL
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+const char *CEconItemView::GetWorldDisplayModel() const
+{
+ CEconItemDefinition *pData = GetStaticData();
+ if ( !pData )
+ return NULL;
+
+ return pData->GetWorldDisplayModel();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+const char *CEconItemView::GetExtraWearableModel() const
+{
+ CEconItemDefinition *pData = GetStaticData();
+ if ( !pData )
+ return NULL;
+
+ return pData->GetExtraWearableModel();
+}
+
+const char *CEconItemView::GetExtraWearableViewModel() const
+{
+ CEconItemDefinition *pData = GetStaticData();
+ if ( !pData )
+ return NULL;
+
+ return pData->GetExtraWearableViewModel();
+}
+
+const char *CEconItemView::GetVisionFilteredDisplayModel() const
+{
+ CEconItemDefinition *pData = GetStaticData();
+ if ( !pData )
+ return NULL;
+
+ return pData->GetVisionFilteredDisplayModel();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+int CEconItemView::GetQualityParticleType() const
+{
+ static CSchemaParticleHandle pSparkleSystem( "community_sparkle" );
+
+ CEconItem* pItem = GetSOCData();
+ if ( !pItem )
+ return 0;
+
+ if( GetSOCData()->GetQuality() == AE_SELFMADE || GetSOCData()->GetQuality() == AE_COMMUNITY )
+ return pSparkleSystem ? pSparkleSystem->nSystemID : 0;
+ else
+ return 0;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Return the animation set that this item wants the player to use (ie., melee, item1, pda)
+//-----------------------------------------------------------------------------
+int CEconItemView::GetAnimationSlot( void ) const
+{
+ if ( !GetStaticData() )
+ return -1;
+
+#if defined( CSTRIKE_DLL ) || defined( DOTA_DLL )
+ return -1;
+#else
+ return GetStaticData()->GetAnimSlot();
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Return an int that indicates whether the item should be dropped from a dead owner.
+//-----------------------------------------------------------------------------
+int CEconItemView::GetDropType( void )
+{
+ if ( !GetStaticData() )
+ return 0;
+ return GetStaticData()->GetDropType();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CEconItemView::DestroyAllAttributes( void )
+{
+ m_AttributeList.DestroyAllAttributes();
+ m_NetworkedDynamicAttributesForDemos.DestroyAllAttributes();
+ NetworkStateChanged();
+ MarkDescriptionDirty();
+}
+
+extern const char *g_EffectTypes[NUM_EFFECT_TYPES];
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+#ifdef CLIENT_DLL
+const wchar_t *CEconItemView::GetItemName() const
+{
+ static const wchar_t *pwzDefaultName = L"";
+
+ const CEconItemDescription *pDescription = GetDescription();
+ if ( !pDescription )
+ return pwzDefaultName;
+
+ const econ_item_description_line_t *pNameDescLine = pDescription->GetFirstLineWithMetaType( kDescLineFlag_Name );
+ if ( !pNameDescLine )
+ return pwzDefaultName;
+
+ return pNameDescLine->sText.Get();
+}
+
+//-----------------------------------------------------------------------------
+void CEconItemView::GetRenderBounds( Vector& mins, Vector& maxs )
+{
+ const CEconItemDefinition *pDef = GetStaticData();
+ if ( !pDef )
+ return;
+
+ int iClass = 0;
+ int iTeam = 0;
+
+#ifdef TF_CLIENT_DLL
+ C_TFPlayer *pTFPlayer = ToTFPlayer( GetPlayerByAccountID( GetAccountID() ) );
+ if ( pTFPlayer )
+ {
+ iClass = pTFPlayer->GetPlayerClass()->GetClassIndex();
+ iTeam = pTFPlayer->GetTeamNumber();
+ }
+#endif // TF_CLIENT_DLL
+
+ const char * pszModel = GetPlayerDisplayModel( iClass, iTeam );
+ if ( !pszModel )
+ return;
+
+ int iIndex = modelinfo->GetModelIndex( pszModel );
+
+ if ( iIndex == -1 )
+ {
+ // hard load the model to get its bounds
+ MDLHandle_t hMDLFindResult = g_pMDLCache->FindMDL( pszModel );
+ MDLHandle_t hMDL = pszModel ? hMDLFindResult : MDLHANDLE_INVALID;
+ if ( g_pMDLCache->IsErrorModel( hMDL ) )
+ return;
+
+ const studiohdr_t * pStudioHdr = g_pMDLCache->GetStudioHdr( hMDL );
+ VectorCopy( pStudioHdr->hull_min, mins );
+ VectorCopy( pStudioHdr->hull_max, maxs );
+
+ g_pMDLCache->Release( hMDLFindResult );
+ }
+ else
+ {
+ const model_t *pModel = modelinfo->GetModel( iIndex );
+ modelinfo->GetModelRenderBounds( pModel, mins, maxs );
+ }
+}
+#endif
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CEconItemView::InitNetworkedDynamicAttributesForDemos( void )
+{
+ if ( !GetSOCData() )
+ return;
+
+ class CEconDynamicAttributesForDemosIterator : public CEconItemSpecificAttributeIterator
+ {
+ public:
+ CEconDynamicAttributesForDemosIterator( CAttributeList* out_NetworkedDynamicAttributesForDemos )
+ : m_NetworkedDynamicAttributesForDemos( out_NetworkedDynamicAttributesForDemos )
+ {
+ m_bAdded = false;
+ }
+
+ virtual bool OnIterateAttributeValue( const CEconItemAttributeDefinition *pAttrDef, attrib_value_t value ) OVERRIDE
+ {
+ CEconItemAttribute attribute( pAttrDef->GetDefinitionIndex(), value );
+ m_NetworkedDynamicAttributesForDemos->AddAttribute( &attribute );
+ m_bAdded = true;
+ return true;
+ }
+
+ virtual bool OnIterateAttributeValue( const CEconItemAttributeDefinition *pAttrDef, float value ) OVERRIDE
+ {
+ CEconItemAttribute attribute( pAttrDef->GetDefinitionIndex(), value );
+ m_NetworkedDynamicAttributesForDemos->AddAttribute( &attribute );
+ m_bAdded = true;
+ return true;
+ }
+
+ bool BAdded( void ){ return m_bAdded; }
+
+ private:
+ bool m_bAdded;
+ CAttributeList *m_NetworkedDynamicAttributesForDemos;
+ };
+
+ m_NetworkedDynamicAttributesForDemos.DestroyAllAttributes();
+
+ CEconDynamicAttributesForDemosIterator it( &m_NetworkedDynamicAttributesForDemos );
+ IterateAttributes( &it );
+
+ if ( it.BAdded() )
+ {
+ NetworkStateChanged();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+static int RemapOverridePaintIndexToRGB( uint32 unIndex, uint32 unTeamIndex )
+{
+ enum { kSamplePoints = 256, };
+
+ static uint32 k_unWitchYellow[] = {
+ 5328971, 5328971, 5328971, 5328971, 5328971, 5328971, 5328971, 5328971,
+ 5328971, 5328971, 5395018, 5591625, 5723465, 5855050, 5921095, 6052679,
+ 6315591, 6447429, 6513222, 6710852, 7039299, 7170884, 7433793, 7631425,
+ 7894336, 8222784, 8354622, 8618045, 8815420, 9078333, 9406779, 9604411,
+ 9802040, 10064952, 10328376, 10459959, 10722870, 11051318, 11314483, 11511859,
+ 11709490, 11841074, 12103729, 12301360, 12498479, 12630063, 12761901, 12959022,
+ 13090604, 13156651, 13288236, 13485099, 13616683, 13682474, 13748010, 13813801,
+ 13945386, 13945386, 14011433, 14011433, 14011433, 13945386, 13945386, 13879594,
+ 13814314, 13813801, 13748010, 13682474, 13616681, 13616683, 13550890, 13550890,
+ 13354027, 13288234, 13288236, 13222443, 13222443, 13156651, 13156651, 13025322,
+ 13025324, 12959531, 12893740, 12893740, 12893740, 12762413, 12696621, 12696621,
+ 12630828, 12630574, 12630574, 12499760, 12433713, 12433713, 12367922, 12302642,
+ 12302387, 12236596, 12236598, 12171575, 12105528, 12039736, 11908409, 11974204,
+ 11908413, 11842880, 11842880, 11842880, 11908421, 11776837, 11645765, 11579973,
+ 11579975, 11514182, 11448137, 11382344, 11316298, 11184712, 11053387, 11053387,
+ 10921802, 10855755, 10724941, 10659150, 10658896, 10527311, 10527057, 10395986,
+ 10330195, 10264147, 10264147, 10264149, 10263895, 10198104, 10132313, 10198106,
+ 10132058, 10263133, 10263133, 10328928, 10394721, 10394723, 10328675, 10394981,
+ 10460263, 10526056, 10329190, 10394983, 10329188, 10394723, 10328928, 10328926,
+ 10526042, 10526040, 10394452, 10459985, 10394446, 10460235, 10525511, 10525763,
+ 10657089, 10657341, 10723131, 10854456, 10920502, 11183156, 11380273, 11512111,
+ 11906350, 12038190, 12301100, 12629804, 12827436, 13221676, 13419306, 13616170,
+ 13813803, 13945386, 14077226, 13879593, 13813801, 13616170, 13485099, 13221674,
+ 12893227, 12630057, 12301098, 12103723, 11775018, 11511594, 11117353, 10853929,
+ 10459431, 10130984, 9868327, 9670182, 9472807, 9275686, 9013027, 8815396,
+ 8618019, 8420385, 8289569, 8223006, 8026399, 7894813, 7763484, 7829020,
+ 7763738, 7894810, 7960857, 8158234, 8552475, 8684059, 8881690, 9079324,
+ 9210908, 9407772, 9605405, 9671198, 9736989, 9736989, 9736991, 9605918,
+ 9473565, 9341979, 9078809, 8684059, 8355096, 7763224, 7434010, 6973206,
+ 6447895, 6118679, 5723416, 5394715, 5131549, 5000483, 5000234, 4999981,
+ 5000242, 4999990, 4868407, 4671291, 4539707, 4539709, 4605502, 4671041,
+ 4868420, 5000006, 5065799, 5131592, 5197385, 5263178, 5328971, 5263178,
+ };
+ COMPILE_TIME_ASSERT( ARRAYSIZE( k_unWitchYellow ) == kSamplePoints );
+
+ static uint32 k_unDistinctiveLackOfSanity[] =
+ {
+ 5720667, 5720667, 5786460, 5720667, 5786460, 5786460, 5851996, 5851741,
+ 6048606, 6048606, 6114145, 6114145, 6245474, 6311013, 6311013, 6441829,
+ 6507624, 6573417, 6704491, 6704491, 6704493, 6901359, 6901359, 7097970,
+ 7097970, 7163252, 7229045, 7294840, 7425656, 7491449, 7622523, 7688062,
+ 7819137, 7950209, 8147077, 8277894, 8409226, 8540301, 8540303, 8671376,
+ 8737171, 8737171, 8867985, 8671376, 8671374, 8540301, 8475019, 8475273,
+ 8278408, 8147334, 8082309, 7885441, 7819902, 7754366, 7754621, 7558012,
+ 7492217, 7426937, 7361142, 7164533, 7164531, 7164529, 7033456, 6967663,
+ 6902382, 6836587, 6771564, 6771564, 6639978, 6574185, 6640232, 6508903,
+ 6443110, 6574439, 6443110, 6442599, 6442599, 6573415, 6508392, 6573417,
+ 6507624, 6572905, 6572905, 6572907, 6638187, 6703212, 6768494, 6834289,
+ 6965105, 6964850, 7030132, 7029621, 7226486, 7160439, 7225720, 7356538,
+ 7421820, 7421820, 7487102, 7617920, 7617921, 7748226, 7814021, 7879303,
+ 7944328, 7944330, 8074635, 8271500, 8271246, 8336527, 8336529, 8598420,
+ 8532629, 8597911, 8794521, 8859803, 8859803, 8990878, 9121440, 9120928,
+ 9317793, 9317539, 9382823, 9448103, 9513898, 9644714, 9709995, 9840813,
+ 9840815, 9906095, 9971890, 9971378, 10102194, 10167989, 10233269, 10364085,
+ 10364087, 10363833, 10429369, 10429114, 10560186, 10625725, 10625468, 10691007,
+ 10691007, 10756543, 10756289, 10756289, 10756289, 10756289, 10691007, 10625214,
+ 10625468, 10559932, 10560186, 10429114, 10363576, 10298040, 10232501, 10101429,
+ 10101683, 9970608, 9970608, 9773998, 9773998, 9642923, 9511848, 9511848,
+ 9381030, 9381030, 9249956, 9053345, 8987806, 8922270, 8856731, 8725656,
+ 8594839, 8463510, 8397971, 8201360, 8070287, 8070285, 7939211, 7807625,
+ 7676805, 7545732, 7479937, 7283582, 7152508, 7086972, 6890361, 6759029,
+ 6627700, 6496882, 6366064, 6300269, 6169194, 6103399, 5840997, 5775716,
+ 5513312, 5448030, 5251162, 5054551, 5054806, 4857684, 4661073, 4595534,
+ 4398666, 4267337, 4136776, 4070981, 4005186, 3874111, 3677502, 3480634,
+ 3414840, 3283767, 3152951, 3152949, 3153203, 3153203, 3153460, 3153460,
+ 3219253, 3285046, 3350839, 3416632, 3482425, 3614011, 3811133, 3942465,
+ 4008260, 4336455, 4533321, 4664908, 4730701, 4993362, 5124948, 5256536,
+ 5650013, 5781599, 6044003, 6175589, 6306920, 6504042, 6701167, 6898035,
+ 7029621, 7292279, 7489404, 7686526, 7752064, 8014722, 8080261, 8277127,
+ };
+ COMPILE_TIME_ASSERT( ARRAYSIZE( k_unDistinctiveLackOfSanity ) == kSamplePoints );
+
+ static uint32 k_unOverabundanceOfRottingFlesh[] =
+ {
+ 12703514, 12703514, 12703514, 12703516, 12703516, 12703514, 12572700, 12506907,
+ 12506907, 12506652, 12506907, 12506907, 12506652, 12506652, 12506907, 12506652,
+ 12506652, 12506652, 12506654, 12506654, 12440861, 12441374, 12441374, 12375581,
+ 12375581, 12375583, 12375583, 12375583, 12375583, 12309536, 12309536, 12309536,
+ 12309536, 12309537, 12112928, 12243744, 12243744, 12177697, 12046881, 12046881,
+ 11981090, 11915299, 11915299, 11915044, 11915044, 11849253, 11783206, 11783206,
+ 11717415, 11717417, 11651624, 11585576, 11519785, 11519787, 11453994, 11388460,
+ 11387948, 11256621, 11190830, 11190832, 11124785, 11058993, 11058995, 10992948,
+ 10992950, 10861364, 10795572, 10729527, 10729527, 10663736, 10597690, 10466106,
+ 10466106, 10465852, 10400062, 10334015, 10202431, 10202433, 10136640, 10136385,
+ 10004801, 10004803, 10004549, 9938243, 10004037, 9938244, 9938246, 9872199,
+ 9937478, 9937224, 9871431, 9871433, 9871433, 9805640, 9870921, 9805128,
+ 9805128, 9805128, 9805128, 9805128, 9739337, 9804360, 9804360, 9804360,
+ 9804360, 9804360, 9869640, 9869640, 9803847, 9803847, 9803847, 9803847,
+ 9738054, 9803334, 9737541, 9672261, 9672261, 9672261, 9606468, 9606470,
+ 9606470, 9540677, 9475397, 9475397, 9409604, 9409604, 9212739, 9212741,
+ 9212741, 9146948, 9081668, 9081668, 8950596, 8950596, 9015875, 9015875,
+ 9081668, 9081668, 9081664, 8950850, 8950848, 8885568, 8819775, 8951359,
+ 8950846, 9016894, 9016892, 9016892, 9016890, 9147962, 9016376, 9147958,
+ 9082165, 9213494, 9081907, 9147698, 9147698, 9278768, 9212975, 9212973,
+ 9212716, 9278508, 9212713, 9212456, 9343272, 9212454, 9343524, 9343011,
+ 9277218, 9408034, 9342241, 9407523, 9341730, 9341730, 9341219, 9275426,
+ 9406242, 9339938, 9339938, 9339171, 9273380, 9272613, 9272613, 9141030,
+ 9206310, 9140519, 9140265, 9205290, 9138985, 9138985, 9138987, 9073453,
+ 9007662, 9007150, 9007150, 9137968, 9072945, 8941618, 9138484, 9007413,
+ 9138999, 9204788, 9270581, 9205556, 9271604, 9272115, 9469490, 9404210,
+ 9536049, 9536817, 9603119, 9734960, 9866542, 9801772, 9999147, 9999913,
+ 10197288, 10263848, 10264613, 10462244, 10462754, 10463776, 10661152, 10792988,
+ 10859547, 10991642, 11057688, 11190040, 11256086, 11388435, 11520274, 11521042,
+ 11652625, 11784463, 11784974, 11916813, 11916813, 11982604, 12048397, 12114188,
+ 12179981, 12246028, 12246028, 12311308, 12311308, 12311308, 12311308, 12377101,
+ 12377101, 12377101, 12377101, 12377101, 12377101, 12377101, 12377101, 12377101,
+ };
+ COMPILE_TIME_ASSERT( ARRAYSIZE( k_unOverabundanceOfRottingFlesh ) == kSamplePoints );
+
+ // orange_flash
+ static uint32 k_unTheFlamesBelow[] =
+ {
+ 11548953, 11614745, 11746074, 11877659, 12008987, 12140572, 12337693, 12469278,
+ 12666655, 12863776, 13060897, 13258274, 13455395, 13652772, 13849893, 14047014,
+ 14244391, 14375976, 14573097, 14704681, 14836010, 14967595, 15033387, 15164716,
+ 15164972, 15230508, 15230508, 15230508, 15164716, 15033387, 14967595, 14836010,
+ 14638889, 14507304, 14310183, 14112806, 13915685, 13718308, 13455651, 13258274,
+ 13060897, 12863776, 12666399, 12469278, 12272157, 12075036, 11943451, 11811866,
+ 11680538, 11614745, 11548953, 11548953, 11548953, 11614745, 11680538, 11746330,
+ 11877659, 12009244, 12206364, 12403485, 12600862, 12797983, 12995360, 13192482,
+ 13389859, 13652772, 13849893, 14047270, 14244391, 14441512, 14638889, 14770474,
+ 14901802, 15033387, 15099179, 15164972, 15230508, 15230508, 15098924, 14835755,
+ 14572586, 14177832, 13783078, 13322789, 12862755, 12468001, 12139295, 11811101,
+ 11614491, 11548954, 11549209, 11747351, 12011542, 12407317, 12869139, 13396754,
+ 13924369, 14451983, 14979598, 15506957, 15902989, 16298508, 16562443, 16759819,
+ 16760075, 16562188, 16166669, 15704590, 15110927, 14451217, 13791507, 13132053,
+ 12538390, 12011032, 11681049, 11548953, 11614745, 11812888, 12142615, 12538646,
+ 13066005, 13593875, 14187538, 14780944, 15308815, 15836430, 16232204, 16561932,
+ 16694283, 16760075, 16561932, 16100621, 15506958, 14846992, 14121490, 13395732,
+ 12670486, 12142359, 11746585, 11548953, 11614745, 11812632, 12076824, 12472343,
+ 12934165, 13395988, 13923859, 14517265, 15044880, 15506702, 15968269, 16298508,
+ 16562187, 16759819, 16760075, 16627979, 16298508, 15902733, 15375374, 14781967,
+ 14188560, 13594897, 13066771, 12539156, 12077334, 11747351, 11549209, 11548954,
+ 11680028, 11876637, 12139295, 12533793, 12928291, 13323045, 13783335, 14178088,
+ 14572586, 14836011, 15098924, 15230508, 15230508, 15164972, 15099179, 15033387,
+ 14901802, 14770474, 14638889, 14441768, 14244391, 14047270, 13849893, 13652772,
+ 13455395, 13192482, 12995360, 12797983, 12600862, 12403485, 12206364, 12074780,
+ 11877659, 11746330, 11680538, 11614745, 11548953, 11548953, 11548953, 11614745,
+ 11680538, 11811866, 11943451, 12075036, 12272157, 12403742, 12600863, 12863776,
+ 13060897, 13258274, 13455395, 13718308, 13915685, 14112806, 14310183, 14507304,
+ 14638889, 14836010, 14967339, 15033387, 15164716, 15230508, 15230508, 15230508,
+ 15164972, 15164716, 15033387, 14967595, 14836010, 14704681, 14573097, 14375976,
+ 14244391, 14047014, 13849893, 13652772, 13455395, 13258274, 13060897, 12863776,
+ 12666655, 12469278, 12337693, 12140572, 12008987, 11877659, 11746074, 11614745,
+ };
+ COMPILE_TIME_ASSERT( ARRAYSIZE( k_unTheFlamesBelow ) == kSamplePoints );
+
+ // green_pulse
+ static uint32 k_unThatQueesyFeeling[] =
+ {
+ 7439904, 7571489, 7703329, 7900706, 8032547, 8295716, 8493349, 8756518,
+ 9019943, 9283369, 9546794, 9810219, 10073644, 10402606, 10666031, 10929200,
+ 11192625, 11390259, 11653428, 11851061, 12048437, 12180278, 12312119, 12443703,
+ 12509496, 12575288, 12575288, 12509496, 12443703, 12312119, 12180278, 11982901,
+ 11785268, 11522099, 11258674, 10995249, 10731823, 10468398, 10139181, 9810219,
+ 9546794, 9217576, 8954151, 8690726, 8427557, 8229924, 7966755, 7834914,
+ 7637537, 7571488, 7440160, 7439904, 7439904, 7505696, 7637281, 7769121,
+ 7900962, 8098339, 8361508, 8624933, 8888359, 9151784, 9415209, 9744171,
+ 10073388, 10336814, 10666031, 10929456, 11192882, 11456051, 11719476, 11982645,
+ 12114486, 12311863, 12443447, 12509496, 12575288, 12575288, 12509496, 12443703,
+ 12311863, 12180278, 11982645, 11785012, 11521843, 11258674, 10995249, 10731823,
+ 10402862, 10139181, 9810219, 9546794, 9283368, 8954151, 8690982, 8427557,
+ 8229924, 8032291, 7834914, 7703073, 7571489, 7505696, 7439904, 7439904,
+ 7505696, 7571745, 7703329, 7900706, 8098083, 8295716, 8559141, 8822310,
+ 9085736, 9349161, 9678378, 9941804, 10270765, 10534447, 10863408, 11126833,
+ 11390258, 11653428, 11851061, 12048694, 12246070, 12377911, 12509240, 12575288,
+ 12575288, 12575288, 12509240, 12377911, 12246070, 12048694, 11851061, 11653428,
+ 11390258, 11126833, 10863408, 10534447, 10270765, 9941804, 9678378, 9349161,
+ 9085736, 8822310, 8559141, 8295716, 8098083, 7900706, 7703329, 7571745,
+ 7505696, 7439904, 7439904, 7505696, 7571489, 7703073, 7834914, 8032291,
+ 8229924, 8427557, 8690982, 8954151, 9283368, 9546794, 9810219, 10139181,
+ 10402862, 10731823, 10995249, 11258674, 11521843, 11785012, 11982645, 12180278,
+ 12311863, 12443703, 12509496, 12575288, 12575288, 12509496, 12443447, 12311863,
+ 12180022, 11982645, 11719476, 11521843, 11258418, 10929456, 10666031, 10336814,
+ 10073388, 9744427, 9480745, 9151784, 8888359, 8624933, 8361508, 8163875,
+ 7900962, 7769121, 7637281, 7505696, 7439904, 7439904, 7440160, 7571488,
+ 7637537, 7834914, 7966755, 8164132, 8427557, 8690726, 8954151, 9217576,
+ 9546538, 9810219, 10139181, 10402606, 10731823, 10995249, 11258674, 11521843,
+ 11785268, 11982645, 12180278, 12312119, 12443703, 12509496, 12575288, 12575288,
+ 12509496, 12443703, 12312119, 12180278, 12048437, 11851061, 11653428, 11390259,
+ 11192625, 10929200, 10666031, 10402606, 10073644, 9810219, 9546794, 9283369,
+ 9019943, 8756518, 8493349, 8295716, 8032547, 7900706, 7703329, 7571489,
+ };
+ COMPILE_TIME_ASSERT( ARRAYSIZE( k_unThatQueesyFeeling ) == kSamplePoints );
+
+ // blue_pulse
+ static uint32 k_unBubbleBubble[] =
+ {
+ 9094364, 9160156, 9291485, 9357278, 9488607, 9685472, 9816801, 10013922,
+ 10145251, 10342372, 10539237, 10736358, 10933479, 11130345, 11327466, 11458795,
+ 11655916, 11852781, 11984366, 12115695, 12247024, 12378352, 12444145, 12509681,
+ 12575474, 12641010, 12641010, 12575474, 12509681, 12444145, 12312816, 12181487,
+ 12049902, 11853037, 11655916, 11458795, 11261930, 11064808, 10802151, 10605030,
+ 10407908, 10211043, 10013922, 9816801, 9619936, 9488607, 9357022, 9225693,
+ 9160156, 9094364, 9094364, 9094364, 9159900, 9225693, 9357022, 9553886,
+ 9751008, 9948129, 10210786, 10473444, 10736102, 11064551, 11392745, 11655659,
+ 11983853, 12312303, 12640496, 12903410, 13166068, 13494262, 13691383, 13954040,
+ 14150905, 14282490, 14413819, 14545148, 14545148, 14545148, 14545148, 14413819,
+ 14282490, 14151162, 13954040, 13756919, 13494262, 13231604, 12968947, 12706289,
+ 12377839, 12115181, 11786732, 11458538, 11195880, 10867430, 10604773, 10342115,
+ 10079458, 9816800, 9619679, 9422814, 9291485, 9160156, 9094364, 9094364,
+ 9094364, 9094364, 9160157, 9291485, 9422814, 9554143, 9685472, 9882593,
+ 10079458, 10276579, 10473701, 10736358, 10933479, 11130345, 11327466, 11590123,
+ 11787245, 11918574, 12115695, 12247024, 12378352, 12509681, 12575218, 12641010,
+ 12641010, 12641010, 12575218, 12509681, 12378352, 12247024, 12115695, 11918574,
+ 11787245, 11590123, 11327466, 11130345, 10933479, 10736358, 10473701, 10276579,
+ 10079458, 9882593, 9685472, 9554143, 9422814, 9291485, 9160157, 9094364,
+ 9094364, 9094364, 9094364, 9160156, 9291485, 9422814, 9619679, 9816800,
+ 10079458, 10342115, 10604773, 10867430, 11195880, 11458538, 11786732, 12115181,
+ 12377839, 12706289, 12968947, 13231604, 13494262, 13756919, 13954040, 14151162,
+ 14282490, 14413819, 14545148, 14545148, 14545148, 14545148, 14413819, 14282490,
+ 14150905, 13954040, 13691383, 13494262, 13231604, 12903410, 12640497, 12312303,
+ 11983853, 11721195, 11392745, 11064551, 10801638, 10473444, 10210786, 9948129,
+ 9751008, 9553887, 9357022, 9225693, 9159900, 9094364, 9094364, 9094364,
+ 9160156, 9225693, 9357022, 9488351, 9619935, 9816801, 10013922, 10210787,
+ 10407908, 10605030, 10802151, 11064808, 11261674, 11458795, 11655916, 11853037,
+ 12049902, 12181231, 12312560, 12443889, 12575217, 12641010, 12641010, 12641010,
+ 12575474, 12575217, 12509681, 12378352, 12247024, 12115695, 11984366, 11852781,
+ 11655916, 11524331, 11327466, 11130345, 10933479, 10736358, 10539237, 10342372,
+ 10145251, 10013922, 9816801, 9685472, 9554143, 9422814, 9291485, 9160156,
+ };
+ COMPILE_TIME_ASSERT( ARRAYSIZE( k_unBubbleBubble ) == kSamplePoints );
+
+ // purple_orange_rand
+ static uint32 k_unAfraidOfShadowsDark[] =
+ {
+ 4536928, 4536928, 4602721, 4602721, 4668515, 4668515, 4734308, 4734308,
+ 4734308, 4734309, 4734309, 4734309, 4865896, 4865896, 4931689, 4931689,
+ 4997226, 4997226, 5063019, 5063019, 5128813, 5194606, 5194607, 5260400,
+ 5260400, 5391730, 5457523, 5457524, 5457524, 5523317, 5589111, 5654904,
+ 5654905, 5654905, 5720698, 5852028, 5917821, 5917822, 5983615, 5983615,
+ 6115202, 6115202, 6115203, 6180996, 6312326, 6378119, 6378120, 6443913,
+ 6444683, 6576785, 6577556, 6709913, 6710941, 6843555, 6844583, 6845868,
+ 6912945, 6914230, 6915515, 6982592, 6983877, 6985161, 6986446, 6987986,
+ 6989014, 6924762, 6926046, 6926817, 6862308, 6863079, 6863593, 6798827,
+ 6799597, 6799083, 6929385, 6863079, 6927845, 6992866, 7057632, 7187677,
+ 7252442, 7382487, 7447251, 7577296, 7641805, 7772362, 7836871, 7967172,
+ 8097473, 8227774, 8292283, 8422841, 8487607, 8749750, 8814516, 8879794,
+ 8945072, 9075888, 8946614, 8752575, 8427464, 8102353, 7842008, 7647197,
+ 7582176, 7842777, 8233938, 8559562, 8950979, 9277117, 9537977, 9733814,
+ 9734070, 9734070, 9734070, 9734070, 9734071, 9799863, 9799863, 9799863,
+ 9799863, 9799863, 9799863, 9799863, 9799863, 9799604, 9799863, 9799604,
+ 9799344, 9733548, 9733032, 9732771, 9732509, 9731992, 9731473, 9731211,
+ 9796485, 9861502, 9861239, 9926256, 10057321, 10122338, 10253403, 10450004,
+ 10580557, 10777414, 10974016, 11235897, 11432754, 11629612, 11825958, 12088353,
+ 12350747, 12547606, 12875538, 13072654, 13203723, 13531656, 13729032, 13729032,
+ 13794568, 13991688, 14188808, 14254344, 14385672, 14451464, 14648328, 14845704,
+ 14977032, 15108360, 15239689, 15436809, 15568393, 15831050, 15962892, 16160781,
+ 16226832, 16293140, 16294170, 16294943, 16295713, 16296229, 16297257, 16363566,
+ 16364595, 16365365, 16431930, 16432957, 16433729, 16500037, 16501065, 16501836,
+ 16502864, 16503634, 16569685, 16570712, 16571482, 16637789, 16638303, 16639072,
+ 16639586, 16640355, 16640869, 16707173, 16707430, 16707943, 16708456, 16708712,
+ 16709224, 16775016, 16775272, 16775784, 16776040, 16776552, 16776295, 16776551,
+ 16776551, 16776039, 16709991, 16709736, 16709224, 16511336, 16247913, 16116074,
+ 15786858, 15457899, 15193963, 14865004, 14601837, 14206573, 13811822, 13351022,
+ 12890479, 12429935, 11969392, 11508591, 11048049, 10587504, 10192752, 9732208,
+ 9337455, 8877166, 8482670, 8153453, 7758957, 7430252, 7101291, 6772586,
+ 6509418, 6246505, 5983337, 5786216, 5588840, 5391719, 5194599, 5063014,
+ };
+ COMPILE_TIME_ASSERT( ARRAYSIZE( k_unAfraidOfShadowsDark ) == kSamplePoints );
+
+ static uint32 *k_pPointSampleContent_Team0[] =
+ {
+ &k_unWitchYellow[0],
+ &k_unDistinctiveLackOfSanity[0],
+ &k_unOverabundanceOfRottingFlesh[0],
+ &k_unTheFlamesBelow[0],
+ &k_unThatQueesyFeeling[0],
+ &k_unAfraidOfShadowsDark[0],
+ };
+
+ static uint32 *k_pPointSampleContent_Team1[] =
+ {
+ &k_unWitchYellow[0],
+ &k_unDistinctiveLackOfSanity[0],
+ &k_unOverabundanceOfRottingFlesh[0],
+ &k_unBubbleBubble[0],
+ &k_unThatQueesyFeeling[0],
+ &k_unAfraidOfShadowsDark[0],
+ };
+ COMPILE_TIME_ASSERT( ARRAYSIZE( k_pPointSampleContent_Team0 ) == ARRAYSIZE( k_pPointSampleContent_Team1 ) );
+
+ if ( unIndex >= ARRAYSIZE( k_pPointSampleContent_Team0 ) )
+ return 0;
+
+ if ( unTeamIndex > 1 )
+ return 0;
+
+ const float fScaledTime = gpGlobals->curtime * 22.0f; // arbitrary time scalar people liked
+ const unsigned int unSamplePoint0 = (unsigned int)fScaledTime % kSamplePoints;
+ const unsigned int unSamplePoint1 = (unSamplePoint0 + 1) % kSamplePoints;
+
+ const float fDelta = fScaledTime - (unsigned int)fScaledTime; // offset between two sample points for blending
+
+ const uint32 *punData = (unTeamIndex == 0 ? k_pPointSampleContent_Team0 : k_pPointSampleContent_Team1)[unIndex];
+
+ Color c0;
+ c0.SetRawColor( punData[unSamplePoint0] );
+
+ Color c1;
+ c1.SetRawColor( punData[unSamplePoint1] );
+
+ const Color cBlend( Lerp( fDelta, c0.r(), c1.r() ),
+ Lerp( fDelta, c0.g(), c1.g() ),
+ Lerp( fDelta, c0.b(), c1.b() ) );
+
+ return cBlend.GetRawColor();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Get RGB modifying attribute value
+//-----------------------------------------------------------------------------
+int CEconItemView::GetModifiedRGBValue( bool bAltColor )
+{
+ enum
+ {
+ kPaintConstant_Default = 0,
+ kPaintConstant_OldTeamColor = 1,
+ };
+
+ static CSchemaAttributeDefHandle pAttr_Paint( "set item tint rgb" );
+ static CSchemaAttributeDefHandle pAttr_Paint2( "set item tint rgb 2" );
+
+ static CSchemaAttributeDefHandle pAttr_PaintOverride( "SPELL: set item tint RGB" );
+
+ // Do we have an override paint color? This takes precedence over base paints and team
+ // paints.
+#if defined( TF_DLL ) || defined( TF_CLIENT_DLL )
+ extern bool TF_IsHolidayActive( /*EHoliday*/ int eHoliday );
+
+ if ( TF_IsHolidayActive( kHoliday_HalloweenOrFullMoon ) )
+#endif // defined( TF_DLL ) || defined( TF_CLIENT_DLL )
+ {
+ if ( !m_bPaintOverrideInit )
+ {
+ m_bHasPaintOverride = FindAttribute_UnsafeBitwiseCast<attrib_value_t>( this, pAttr_PaintOverride, &m_flOverrideIndex );
+ m_bPaintOverrideInit = true;
+ }
+
+ if ( m_bHasPaintOverride )
+ return RemapOverridePaintIndexToRGB( (uint32)m_flOverrideIndex, bAltColor ? 1 : 0 );
+ }
+
+ if ( !m_bColorInit )
+ {
+ // See if we also have a secondary paint color.
+ uint32 unRGB = kPaintConstant_Default;
+ uint32 unRGBAlt = kPaintConstant_Default;
+ float fRGB;
+ float fRGBAlt;
+
+ // If we have no base paint color we don't do anything special.
+ if ( FindAttribute_UnsafeBitwiseCast<attrib_value_t>( this, pAttr_Paint, &fRGB ) )
+ {
+ unRGB = (uint32)fRGB;
+ unRGBAlt = unRGB;
+ }
+
+ // Backwards compatibility for old team colored items.
+ if ( unRGB == kPaintConstant_OldTeamColor )
+ {
+ unRGB = RGB_INT_RED;
+ unRGBAlt = RGB_INT_BLUE;
+ }
+ else if ( FindAttribute_UnsafeBitwiseCast<attrib_value_t>( this, pAttr_Paint2, &fRGBAlt ) )
+ {
+ unRGBAlt = (uint32)fRGBAlt;
+ }
+ else
+ {
+ // By default our secondary color will match our primary if we can't find a replacement.
+ unRGBAlt = unRGB;
+ }
+
+ m_unRGB = unRGB;
+ m_unAltRGB = unRGBAlt;
+
+ m_bColorInit = true;
+ }
+
+ return bAltColor ? m_unAltRGB : m_unRGB;
+}
+
+uint64 CEconItemView::GetCustomUserTextureID()
+{
+ static CSchemaAttributeDefHandle pAttr_CustomTextureLo( "custom texture lo" );
+ static CSchemaAttributeDefHandle pAttr_CustomTextureHi( "custom texture hi" );
+
+ uint32 unLowVal, unHighVal;
+ const bool bHasLowVal = FindAttribute( pAttr_CustomTextureLo, &unLowVal ),
+ bHasHighVal = FindAttribute( pAttr_CustomTextureHi, &unHighVal );
+
+ // We should have both, or neither. We should never have just one
+ Assert( bHasLowVal == bHasHighVal );
+
+ if ( bHasLowVal && bHasHighVal )
+ {
+ return ((uint64)unHighVal << 32) | (uint64)unLowVal;
+ }
+
+ // No attribute set
+ return 0;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CAttributeList::CAttributeList()
+{
+ m_pManager = NULL;
+ m_Attributes.Purge();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CAttributeList::SetManager( CAttributeManager *pManager )
+{
+ m_pManager = pManager;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CAttributeList::Init()
+{
+ m_Attributes.Purge();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CAttributeList::IterateAttributes( class IEconItemAttributeIterator *pIterator ) const
+{
+ Assert( pIterator );
+
+ FOR_EACH_VEC( m_Attributes, i )
+ {
+ const CEconItemAttribute *pAttrInst = &m_Attributes[i];
+
+ const CEconItemAttributeDefinition *pAttrDef = pAttrInst->GetStaticData();
+ if ( !pAttrDef )
+ continue;
+
+ const ISchemaAttributeType *pAttrType = pAttrDef->GetAttributeType();
+ Assert( pAttrType );
+ Assert( pAttrType->BSupportsGameplayModificationAndNetworking() );
+
+ // We know (and assert) that we only need 32 bits of data to store this attribute
+ // data. We don't know anything about the type but we'll let the type handle it
+ // below.
+ attribute_data_union_t value;
+ value.asFloat = pAttrInst->m_flValue;
+
+ if ( !pAttrType->OnIterateAttributeValue( pIterator, pAttrDef, value ) )
+ return;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CAttributeList::DestroyAllAttributes( void )
+{
+ if ( m_Attributes.Count() )
+ {
+ m_Attributes.Purge();
+ NotifyManagerOfAttributeValueChanges();
+ NetworkStateChanged();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CAttributeList::AddAttribute( CEconItemAttribute *pAttribute )
+{
+ Assert( pAttribute );
+
+ // Only add attributes to the attribute list if they have a definition we can
+ // pull data from.
+ if ( !pAttribute->GetStaticData() )
+ return;
+
+ m_Attributes.AddToTail( *pAttribute );
+ NetworkStateChanged();
+ NotifyManagerOfAttributeValueChanges();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CAttributeList::SetRuntimeAttributeValue( const CEconItemAttributeDefinition *pAttrDef, float flValue )
+{
+ Assert( pAttrDef );
+
+ // Look for an existing attribute.
+ const int iAttributes = GetNumAttributes();
+ for ( int i = 0; i < iAttributes; i++ )
+ {
+ CEconItemAttribute *pAttribute = GetAttribute(i);
+
+ if ( pAttribute->GetAttribIndex() == pAttrDef->GetDefinitionIndex() )
+ {
+ // Found existing attribute -- change value.
+ pAttribute->m_flValue = flValue;
+ NotifyManagerOfAttributeValueChanges();
+ return;
+ }
+ }
+
+ // Couldn't find an existing attribute for this definition -- make a new one.
+ CEconItemAttribute attribute;
+ attribute.m_iAttributeDefinitionIndex = pAttrDef->GetDefinitionIndex();
+ attribute.m_flValue = flValue;
+
+ m_Attributes.AddToTail( attribute );
+ NotifyManagerOfAttributeValueChanges();
+}
+
+#if ENABLE_ATTRIBUTE_CURRENCY_TRACKING
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CAttributeList::SetRuntimeAttributeRefundableCurrency( const CEconItemAttributeDefinition *pAttrDef, int iRefundableCurrency )
+{
+ Assert( pAttrDef );
+
+ // Look for an existing attribute.
+ const int iAttributes = GetNumAttributes();
+ for ( int i = 0; i < iAttributes; i++ )
+ {
+ CEconItemAttribute *pAttribute = GetAttribute(i);
+
+ if ( pAttribute->GetAttribIndex() == pAttrDef->GetDefinitionIndex() )
+ {
+ // Found existing attribute -- change value.
+ pAttribute->m_nRefundableCurrency = iRefundableCurrency;
+ return;
+ }
+ }
+
+ AssertMsg1( false, "Unable to find attribute '%s' for setting currency!", pAttrDef->GetDefinitionName() );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+int CAttributeList::GetRuntimeAttributeRefundableCurrency( const CEconItemAttributeDefinition *pAttrDef ) const
+{
+ const int iAttributes = GetNumAttributes();
+ for ( int i = 0; i < iAttributes; i++ )
+ {
+ const CEconItemAttribute *pAttribute = GetAttribute(i);
+
+ if ( pAttribute->GetAttribIndex() == pAttrDef->GetDefinitionIndex() )
+ return pAttribute->m_nRefundableCurrency;
+ }
+
+ AssertMsg1( false, "Unable to find attribute '%s' for getting currency!", pAttrDef->GetDefinitionName() );
+ return 0;
+}
+#endif // ENABLE_ATTRIBUTE_CURRENCY_TRACKING
+
+//-----------------------------------------------------------------------------
+// Purpose: Remove an attribute by name
+//-----------------------------------------------------------------------------
+void CAttributeList::RemoveAttribute( const CEconItemAttributeDefinition *pAttrDef )
+{
+ const int iAttributes = m_Attributes.Count();
+ for ( int i = 0; i < iAttributes; i++ )
+ {
+ if ( m_Attributes[i].GetStaticData() == pAttrDef )
+ {
+ m_Attributes.Remove( i );
+ NotifyManagerOfAttributeValueChanges();
+ return;
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Remove an attribute by index
+//-----------------------------------------------------------------------------
+void CAttributeList::RemoveAttributeByIndex( int iIndex )
+{
+ if ( iIndex < 0 || iIndex >= GetNumAttributes() )
+ return;
+
+ m_Attributes.Remove( iIndex );
+ NotifyManagerOfAttributeValueChanges();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+const CEconItemAttribute *CAttributeList::GetAttributeByID( int iAttributeID ) const
+{
+ int iAttributes = m_Attributes.Count();
+ for ( int i = 0; i < iAttributes; i++ )
+ {
+ const CEconItemAttributeDefinition *pData = m_Attributes[i].GetStaticData();
+
+ if ( pData && ( pData->GetDefinitionIndex() == iAttributeID ) )
+ return &m_Attributes[i];
+ }
+
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+const CEconItemAttribute *CAttributeList::GetAttributeByName( const char *pszAttribDefName ) const
+{
+ CEconItemAttributeDefinition *pDef = GetItemSchema()->GetAttributeDefinitionByName( pszAttribDefName );
+ if ( !pDef )
+ return NULL;
+
+ int iAttributes = m_Attributes.Count();
+ for ( int i = 0; i < iAttributes; i++ )
+ {
+ if ( m_Attributes[i].GetStaticData()->GetDefinitionIndex() == pDef->GetDefinitionIndex() )
+ return &m_Attributes[i];
+ }
+
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CAttributeList::operator=( const CAttributeList& src )
+{
+ m_Attributes = src.m_Attributes;
+
+ // HACK: We deliberately don't copy managers, because attributelists are contained inside
+ // CEconItemViews, which we duplicate inside CItemModelPanels all the time. If the manager
+ // is copied, copies will mess with the attribute caches of the copied item.
+ // Our manager will be setup properly by the CAttributeManager itself if we have an associated entity.
+ m_pManager = NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CAttributeList::NotifyManagerOfAttributeValueChanges( void )
+{
+ if ( m_pManager )
+ {
+ m_pManager->OnAttributeValuesChanged();
+ }
+}
+
+#ifdef CLIENT_DLL
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool DoesItemPassSearchFilter( const IEconItemDescription *pDescription, const wchar_t* wszFilter )
+{
+ // check if item matches name filter
+ if ( wszFilter && *wszFilter )
+ {
+ if ( !pDescription )
+ {
+ return false;
+ }
+
+ wchar_t wszBuffer[ 4096 ] = L"";
+ for ( unsigned int i = 0; i < pDescription->GetLineCount(); i++ )
+ {
+ const econ_item_description_line_t& line = pDescription->GetLine(i);
+
+ if ( !(line.unMetaType & ( kDescLineFlag_Collection | kDescLineFlag_CollectionCurrentItem ) ) )
+ {
+ V_wcscat_safe( wszBuffer, line.sText.Get() );
+ }
+ }
+
+ V_wcslower( wszBuffer );
+ if ( !wcsstr( wszBuffer, wszFilter ) )
+ {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CBasePlayer *GetPlayerByAccountID( uint32 unAccountID )
+{
+ for ( int i = 1; i <= gpGlobals->maxClients; i++ )
+ {
+ CBasePlayer *pPlayer = UTIL_PlayerByIndex( i );
+ if ( pPlayer == NULL )
+ continue;
+
+ CSteamID steamIDPlayer;
+ if ( !pPlayer->GetSteamID( &steamIDPlayer ) )
+ continue;
+
+ // return the player with the matching ID
+ if ( steamIDPlayer.GetAccountID() == unAccountID )
+ {
+ return pPlayer;
+ }
+ }
+
+ return NULL;
+}
+
+#endif // CLIENT_DLL
diff --git a/game/shared/econ/econ_item_view.h b/game/shared/econ/econ_item_view.h
new file mode 100644
index 0000000..5ccf825
--- /dev/null
+++ b/game/shared/econ/econ_item_view.h
@@ -0,0 +1,455 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+
+#ifndef ECON_ITEM_CONSTANTS_H
+#define ECON_ITEM_CONSTANTS_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "game_item_schema.h"
+#include "econ_item_constants.h"
+#include "localization_provider.h"
+#include "econ_item_interface.h"
+#include "econ_item.h"
+
+#if defined(CLIENT_DLL)
+#include "iclientrenderable.h"
+#endif
+
+#if defined(TF_DLL)
+#include "tf_item_schema.h"
+#endif
+
+#if defined(CLIENT_DLL)
+#define CEconItemView C_EconItemView
+#endif
+
+#if defined(GC_DLL)
+#error "econ_item_view.h is not intended to be built on the GC!"
+#endif
+
+#if defined(TF_DLL) || defined(TF_CLIENT_DLL)
+ #define ENABLE_ATTRIBUTE_CURRENCY_TRACKING 1
+#else
+ #define ENABLE_ATTRIBUTE_CURRENCY_TRACKING 0
+#endif
+
+class CEconItemAttribute;
+class CAttributeManager;
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+class CAttributeList
+{
+ friend class CEconItemView;
+ friend class CTFPlayer;
+
+ DECLARE_CLASS_NOBASE( CAttributeList );
+public:
+ DECLARE_EMBEDDED_NETWORKVAR();
+ DECLARE_DATADESC();
+
+ CAttributeList();
+ void operator=( const CAttributeList &src );
+
+ void Init();
+ void SetManager( CAttributeManager *pManager );
+
+ void IterateAttributes( class IEconItemAttributeIterator *pIterator ) const;
+
+ // Remove all attributes on this item
+ void DestroyAllAttributes( void );
+
+ void AddAttribute( CEconItemAttribute *pAttribute );
+
+ // Remove an attribute by name
+ void RemoveAttribute( const CEconItemAttributeDefinition *pAttrDef );
+ void RemoveAttributeByIndex( int iIndex );
+
+public:
+ // Returns the attribute that matches the attribute defname
+ const CEconItemAttribute *GetAttributeByName( const char *pszAttribDefName ) const;
+
+ // Returns the attribute that matches the attribute id
+ const CEconItemAttribute *GetAttributeByID( int iAttributeID ) const;
+
+ // The only way to set the value of an attribute after its creation is through the attribute list
+ // that contains it. This way the matching attribute manager is told one of its attributes has changed.
+ void SetRuntimeAttributeValue( const CEconItemAttributeDefinition *pAttrDef, float flValue );
+#if ENABLE_ATTRIBUTE_CURRENCY_TRACKING
+ void SetRuntimeAttributeRefundableCurrency( const CEconItemAttributeDefinition *pAttrDef, int iRefundableCurrency );
+ int GetRuntimeAttributeRefundableCurrency( const CEconItemAttributeDefinition *pAttrDef ) const;
+
+ void AdjustRuntimeAttributeRefundableCurrency( const CEconItemAttributeDefinition *pAttrDef, int iRefundableCurrencyAdjustment )
+ {
+ SetRuntimeAttributeRefundableCurrency( pAttrDef, GetRuntimeAttributeRefundableCurrency( pAttrDef ) + iRefundableCurrencyAdjustment );
+ }
+#endif // ENABLE_ATTRIBUTE_CURRENCY_TRACKING
+
+private:
+ void NotifyManagerOfAttributeValueChanges();
+
+ // Attribute accessing
+ int GetNumAttributes( void ) const { return m_Attributes.Count(); }
+ CEconItemAttribute *GetAttribute( int iIndex ) { Assert( iIndex >= 0 && iIndex < m_Attributes.Count()); return &m_Attributes[iIndex]; }
+ const CEconItemAttribute *GetAttribute( int iIndex ) const { Assert( iIndex >= 0 && iIndex < m_Attributes.Count()); return &m_Attributes[iIndex]; }
+
+ // Our list of attributes
+ CUtlVector<CEconItemAttribute> m_Attributes;
+
+ CAttributeManager *m_pManager;
+};
+
+//-----------------------------------------------------------------------------
+// Purpose: An attribute that knows how to read itself from a datafile, describe itself to the user,
+// and serialize itself between Servers, Clients, and Steam.
+// Unlike the attributes created in the Game DLL, this attribute doesn't know how to actually
+// do anything in the game, it just knows how to describe itself.
+//-----------------------------------------------------------------------------
+class CEconItemAttribute
+{
+ DECLARE_CLASS_NOBASE( CEconItemAttribute );
+public:
+ DECLARE_EMBEDDED_NETWORKVAR();
+
+ CEconItemAttribute();
+ CEconItemAttribute( const attrib_definition_index_t iAttributeIndex, float flValue );
+ CEconItemAttribute( const attrib_definition_index_t iAttributeIndex, uint32 unValue );
+
+ void operator=( const CEconItemAttribute &val );
+
+ // Get the index of this attribute's definition inside the script file
+ attrib_definition_index_t GetAttribIndex( void ) const { return m_iAttributeDefinitionIndex; }
+ void SetAttribIndex( attrib_definition_index_t iIndex ) { m_iAttributeDefinitionIndex = iIndex; }
+
+ // Get the static data contained in this attribute's definition
+ const CEconItemAttributeDefinition *GetStaticData( void ) const;
+
+ // Get the float value of this attribute.
+ //float GetValue( void ) const;
+
+#if ENABLE_ATTRIBUTE_CURRENCY_TRACKING
+ int GetRefundableCurrency( void ) const { return m_nRefundableCurrency; }
+#endif // ENABLE_ATTRIBUTE_CURRENCY_TRACKING
+
+private:
+ // The only way to set the value of an attribute after its creation is through the attribute list
+ // that contains it. This way the matching attribute manager is told one of its attributes has changed.
+
+ // Set the float value of this attribute.
+ // Note that the value must be stored as a float!
+ void SetValue( float flValue );
+
+ // Set the value of this attribute as an unsigned integer.
+ // Note that the value must be stored as an integer!
+ // See CEconItemAttributeDefinition
+ void SetIntValue( uint32 unValue );
+
+ friend class CAttributeList;
+
+ void Init( void );
+
+ //--------------------------------------------------------
+private:
+ // This is the index of the attribute into the attributes read from the data files
+ CNetworkVar( attrib_definition_index_t, m_iAttributeDefinitionIndex );
+
+ // This is the value of the attribute. Used to modify the item's variables.
+ CNetworkVar( float, m_flValue );
+
+#if ENABLE_ATTRIBUTE_CURRENCY_TRACKING
+ // This is the value that the attribute was first set to by an item definition
+ CNetworkVar( int, m_nRefundableCurrency );
+#endif // ENABLE_ATTRIBUTE_CURRENCY_TRACKING
+};
+
+//-----------------------------------------------------------------------------
+// Purpose: An item that knows how to read itself from a datafile, describe itself to the user,
+// and serialize itself between Servers, Clients, and Steam.
+//
+// In the client DLL, we derive it from CDefaultClientRenderable so that
+// it can be passed in the pProxyData parameter of material proxies.
+//-----------------------------------------------------------------------------
+#if defined(CLIENT_DLL)
+class CEconItemView : public CDefaultClientRenderable, public CMaterialOverrideContainer< IEconItemInterface >
+#else
+class CEconItemView : public CMaterialOverrideContainer< IEconItemInterface >
+#endif
+{
+ DECLARE_CLASS_NOBASE( CEconItemView );
+public:
+ DECLARE_EMBEDDED_NETWORKVAR();
+ DECLARE_DATADESC();
+
+public:
+ CEconItemView();
+ CEconItemView( const CEconItemView &src );
+ ~CEconItemView();
+ CEconItemView& operator=( const CEconItemView &src );
+ bool operator==( const CEconItemView &other ) const;
+ bool operator!=( const CEconItemView &other ) const { return !operator==( other ); }
+
+ virtual const GameItemDefinition_t *GetItemDefinition() const
+ {
+ return GetStaticData();
+ }
+
+public:
+
+ // IEconItemInterface implementation.
+ virtual itemid_t GetID() const { return GetItemID(); }
+ virtual int32 GetQuality() const;
+ virtual style_index_t GetStyle() const;
+ virtual uint8 GetFlags() const;
+ virtual eEconItemOrigin GetOrigin() const;
+ virtual int GetQuantity() const;
+ uint64 GetOriginalID() const { return GetSOCData() ? GetSOCData()->GetOriginalID() : 0; }
+
+ virtual const char *GetCustomName() const;
+ virtual const char *GetCustomDesc() const;
+
+ virtual bool GetInUse() const { return GetSOCData() ? GetSOCData()->GetInUse() : false; }
+
+ virtual void IterateAttributes( class IEconItemAttributeIterator *pIterator ) const OVERRIDE;
+
+ bool IsValid( void ) const { return m_bInitialized; }
+ void Invalidate( void ) { m_bInitialized = false; m_iItemDefinitionIndex = INVALID_ITEM_DEF_INDEX; m_iItemID = INVALID_ITEM_ID; }
+ void InvalidateColor() { m_bColorInit = false; }
+ void InvalidateOverrideColor() { m_bPaintOverrideInit = false; }
+
+ // Initialize from the specified data
+ // client will load SO cache as needed
+ void Init( int iDefIndex, int iQuality, int iLevel, uint32 iAccountID = 0 );
+ void SetInitialized( bool bInit ) { m_bInitialized = bInit; }
+
+ // Get the static data contained in this item's definition
+ GameItemDefinition_t *GetStaticData( void ) const;
+
+ void SetNonSOEconItem( CEconItem* pItem ) { m_pNonSOEconItem.SetItem( pItem ); }
+
+ void OnAttributeValuesChanged()
+ {
+ NetworkStateChanged();
+ MarkDescriptionDirty();
+ }
+
+private:
+ void EnsureDescriptionIsBuilt( void ) const;
+ void MarkDescriptionDirty( void );
+public:
+ void SetGrayedOutReason( const char *pszGrayedOutReason );
+
+ // Set & Get the index of this item's definition inside the script file
+ void SetItemDefIndex( item_definition_index_t iIndex ) { m_iItemDefinitionIndex = iIndex; MarkDescriptionDirty(); }
+ virtual item_definition_index_t GetItemDefIndex( void ) const { return m_iItemDefinitionIndex; }
+
+ // Set & Get the quality & level of this item.
+ void SetItemQuality( int iQuality ) { m_iEntityQuality = iQuality; MarkDescriptionDirty(); }
+ int GetItemQuality( void ) const { return m_iEntityQuality; }
+ void SetItemLevel( uint32 unLevel ) { m_iEntityLevel = unLevel; MarkDescriptionDirty(); }
+ uint32 GetItemLevel( void ) const { return m_iEntityLevel; }
+
+ int GetItemQuantity() const;
+#ifdef CLIENT_DLL
+ void SetIsTradeItem( bool bIsTradeItem ) { m_bIsTradeItem = bIsTradeItem; MarkDescriptionDirty(); }
+ void SetItemQuantity( int iQuantity ) { m_iEntityQuantity = iQuantity; MarkDescriptionDirty(); }
+ void SetClientItemFlags( uint8 unFlags );
+
+ void SetItemStyleOverride( style_index_t unNewStyleOverride );
+ void SetItemOriginOverride( eEconItemOrigin unNewOriginOverride );
+#endif
+ style_index_t GetItemStyle() const;
+
+ // Access the worldwide global index of this item
+ void SetItemID( itemid_t iIdx ) { m_iItemID = iIdx; m_iItemIDHigh = (m_iItemID >> 32); m_iItemIDLow = (m_iItemID & 0xFFFFFFFF); }
+#ifdef CLIENT_DLL
+ // On the client, we need to rebuild it from the high & low networked pieces
+ itemid_t GetItemID( void ) const { uint64 iTmp = ((((int64)m_iItemIDHigh)<<32) | m_iItemIDLow); return (itemid_t)iTmp; }
+#else
+ itemid_t GetItemID( void ) const { return m_iItemID; }
+#endif
+
+ uint32 GetAccountID( void ) const { return m_iAccountID; }
+ void SetOverrideAccountID( uint32 nAccountID ) { m_iAccountID = nAccountID; }
+
+ // Access the inventory position of this item
+ void SetInventoryPosition( uint32 iPosition ) { m_iInventoryPosition = iPosition; }
+ const uint32 GetInventoryPosition( void ) const { return m_iInventoryPosition; }
+
+ // Return the model to use for model panels containing this item
+ const char *GetInventoryModel( void );
+ // Return the image to use for model panels containing this item
+ const char *GetInventoryImage( void );
+ bool GetInventoryImageData( int *iPosition, int *iSize );
+ const char *GetInventoryOverlayImage( int idx );
+ int GetInventoryOverlayImageCount( void );
+
+ // Return the model to use when displaying this model on the player character model, if any
+ const char *GetPlayerDisplayModel( int iClass, int iTeam ) const;
+
+ // Return the model to use when displaying this model in the world. See the notes on this in econ_item_schema.h
+ const char *GetWorldDisplayModel() const;
+ const char *GetExtraWearableModel() const;
+ const char *GetExtraWearableViewModel() const;
+ const char *GetVisionFilteredDisplayModel() const;
+
+ // Return the load-out slot that this item must be placed into
+ int GetAnimationSlot( void ) const;
+
+ // Return an int that indicates whether the item should be dropped from a dead owner.
+ int GetDropType( void );
+
+ // Remove all attributes on this item
+ void DestroyAllAttributes( void );
+
+ void InitNetworkedDynamicAttributesForDemos( void );
+
+ // Items that have attributes that modify their RGB values
+ int GetModifiedRGBValue( bool bAltColor=false );
+
+ // Returns the UGC file ID of the custom texture assigned to this item. If non-zero, then it has a custom texture.
+ uint64 GetCustomUserTextureID();
+
+ CEconItem *GetSOCData( void ) const;
+
+ bool IsEquipped( void ) const { return GetSOCData() && GetSOCData()->IsEquipped(); }
+ bool IsEquippedForClass( equipped_class_t unClass ) const { return GetSOCData() && GetSOCData()->IsEquippedForClass( unClass ); }
+ equipped_slot_t GetEquippedPositionForClass( equipped_class_t unClass ) const { return GetSOCData() ? GetSOCData()->GetEquippedPositionForClass( unClass ) : INVALID_EQUIPPED_SLOT; }
+
+ // Attached particle systems
+ int GetQualityParticleType() const;
+
+ int GetSkin( int iTeam, bool bViewmodel = false ) const;
+
+public:
+ // ...
+ CAttributeList *GetAttributeList() { return &m_AttributeList; }
+ const CAttributeList *GetAttributeList() const { return &m_AttributeList; }
+
+public:
+ virtual CEconItemPaintKitDefinition *GetCustomPainkKitDefinition( void ) const { return GetItemDefinition()->GetCustomPainkKitDefinition(); }
+
+#ifdef CLIENT_DLL
+ void SetWeaponSkinBase( ITexture* pBaseTex );
+ void SetWeaponSkinBaseCompositor( ITextureCompositor * pTexCompositor );
+ inline void SetWeaponSkinGeneration( RTime32 nGeneration ) { m_nWeaponSkinGeneration = nGeneration; }
+ inline void SetWeaponSkinGenerationTeam( int iTeam ) { m_iLastGeneratedTeamSkin = iTeam; }
+ inline void SetWeaponSkinBaseCreateFlags( uint32 flags ) { m_unWeaponSkinBaseCreateFlags = flags; }
+ void CancelWeaponSkinComposite( );
+ inline void SetWeaponSkinUseHighRes( bool bUseHighRes ) { m_bWeaponSkinUseHighRes = bUseHighRes; }
+ inline void SetWeaponSkinUseLowRes( bool bUseLowRes ) { m_bWeaponSkinUseLowRes = bUseLowRes; }
+
+ inline ITexture *GetWeaponSkinBase() const { return m_pWeaponSkinBase; }
+ inline ITextureCompositor *GetWeaponSkinBaseCompositor() const { return m_pWeaponSkinBaseCompositor; }
+ inline uint32 GetWeaponSkinBaseCreateFlags() const { return m_unWeaponSkinBaseCreateFlags; }
+
+ inline RTime32 GetWeaponSkinGeneration() const { return m_nWeaponSkinGeneration; }
+ inline int GetWeaponSkinGenerationTeam() const { return m_iLastGeneratedTeamSkin; }
+
+ inline bool ShouldWeaponSkinUseHighRes() const { return m_bWeaponSkinUseHighRes; }
+ inline bool ShouldWeaponSkinUseLowRes() const { return m_bWeaponSkinUseLowRes; }
+#endif // CLIENT_DLL
+
+ inline int GetTeamNumber() const { return m_iTeamNumber; }
+ inline void SetTeamNumber( int iTeamNumber ) { m_iTeamNumber = iTeamNumber; }
+
+protected:
+ // Index of the item definition in the item script file.
+ CNetworkVar( item_definition_index_t, m_iItemDefinitionIndex );
+
+ // The quality of this item.
+ CNetworkVar( int, m_iEntityQuality );
+
+ // The level of this item.
+ CNetworkVar( uint32, m_iEntityLevel );
+
+ // The global index of this item, worldwide.
+ itemid_t m_iItemID;
+ CNetworkVar( uint32, m_iItemIDHigh );
+ CNetworkVar( uint32, m_iItemIDLow );
+
+ // Account ID of the person who has this in their inventory
+ CNetworkVar( uint32, m_iAccountID );
+
+ // Position inside the player's inventory
+ CNetworkVar( uint32, m_iInventoryPosition );
+
+ // This is an alternate source of data, if this item models something that isn't in the SO cache.
+ CEconItemHandle m_pNonSOEconItem;
+
+#if defined( CLIENT_DLL )
+ // exist on the client only
+ bool m_bIsTradeItem;
+ int m_iEntityQuantity;
+ uint8 m_unClientFlags;
+
+ // clients have the ability to force a style on an item view -- this is used for store previews,
+ // character panels, etc.
+ style_index_t m_unOverrideStyle;
+ // clients can also force an origin on an item view -- this is used for crafting item previews
+ eEconItemOrigin m_unOverrideOrigin;
+#endif
+
+ bool m_bColorInit;
+ bool m_bPaintOverrideInit;
+ bool m_bHasPaintOverride;
+ float m_flOverrideIndex;
+ uint32 m_unRGB;
+ uint32 m_unAltRGB;
+
+#ifdef CLIENT_DLL
+ ITexture* m_pWeaponSkinBase;
+ ITextureCompositor* m_pWeaponSkinBaseCompositor;
+ RTime32 m_nWeaponSkinGeneration;
+ uint32 m_unWeaponSkinBaseCreateFlags;
+ int m_iLastGeneratedTeamSkin;
+ bool m_bWeaponSkinUseHighRes;
+ bool m_bWeaponSkinUseLowRes;
+#endif // CLIENT_DLL
+
+ CNetworkVar( int, m_iTeamNumber );
+
+ CNetworkVar( bool, m_bInitialized );
+
+#ifdef CLIENT_DLL // we avoid using "BUILD_ITEM_NAME_AND_DESC" to prevent everything depending on the CEconItemDescription
+public:
+ // Return the single-line name of this item.
+ const wchar_t *GetItemName( void ) const;
+
+ // Return the full structure with all of our description lines.
+ const class CEconItemDescription *GetDescription() const { EnsureDescriptionIsBuilt(); return m_pDescription; }
+
+private:
+ mutable class CEconItemDescription *m_pDescription;
+ mutable char *m_pszGrayedOutReason;
+
+ // IClientRenderable
+ virtual const Vector& GetRenderOrigin( void ) { return vec3_origin; }
+ virtual const QAngle& GetRenderAngles( void ) { return vec3_angle; }
+ virtual bool ShouldDraw( void ) { return false; }
+ virtual bool IsTransparent( void ) { return false;}
+ virtual const matrix3x4_t &RenderableToWorldTransform() { static matrix3x4_t mat; SetIdentityMatrix( mat ); return mat; }
+ virtual void GetRenderBounds( Vector& mins, Vector& maxs );
+#endif
+
+private:
+ CNetworkVarEmbedded( CAttributeList, m_AttributeList );
+ CNetworkVarEmbedded( CAttributeList, m_NetworkedDynamicAttributesForDemos );
+
+ // Some custom gamemodes are using server plugins to modify weapon attributes.
+ // This variable allows them to completely set their own attributes on a weapon
+ // and have the client and server ignore the static attributes.
+ CNetworkVar( bool, m_bOnlyIterateItemViewAttributes );
+};
+
+#ifdef CLIENT_DLL
+bool DoesItemPassSearchFilter( const class IEconItemDescription *pDescription, const wchar_t* wszFilter );
+CBasePlayer *GetPlayerByAccountID( uint32 unAccountID );
+#endif // CLIENT_DLL
+
+#endif // ECON_ITEM_CONSTANTS_H
diff --git a/game/shared/econ/econ_quests.cpp b/game/shared/econ/econ_quests.cpp
new file mode 100644
index 0000000..b7a3ef7
--- /dev/null
+++ b/game/shared/econ/econ_quests.cpp
@@ -0,0 +1,71 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Functions related to dynamic recipes
+//
+//=============================================================================
+
+
+#include "cbase.h"
+#include "econ_quests.h"
+#ifndef GC_DLL
+#include "quest_objective_manager.h"
+#endif
+
+bool IsQuestItemUnidentified( const CEconItem* pQuestItem )
+{
+ return pQuestItem && IsUnacknowledged( pQuestItem->GetInventoryToken() );
+}
+
+bool IsQuestItemReadyToTurnIn( const IEconItemInterface* pQuestItem )
+{
+ uint32 nRequiredPoints = pQuestItem->GetItemDefinition()->GetQuestDef()->GetMaxStandardPoints();
+ uint32 nEarnedStandardPoints = GetEarnedStandardPoints( pQuestItem );
+ uint32 nEarnedBonusPoints = GetEarnedBonusPoints( pQuestItem );
+
+ return ( nEarnedStandardPoints + nEarnedBonusPoints ) >= nRequiredPoints;
+}
+
+bool IsQuestItemFullyCompleted( const IEconItemInterface* pQuestItem )
+{
+ uint32 nRequiredStandardPoints = pQuestItem->GetItemDefinition()->GetQuestDef()->GetMaxStandardPoints();
+ uint32 nRequiredBonusPoints = pQuestItem->GetItemDefinition()->GetQuestDef()->GetMaxBonusPoints();
+ uint32 nEarnedStandardPoints = GetEarnedStandardPoints( pQuestItem );
+ uint32 nEarnedBonusPoints = GetEarnedBonusPoints( pQuestItem );
+
+ return ( nEarnedStandardPoints + nEarnedBonusPoints ) == ( nRequiredStandardPoints + nRequiredBonusPoints );
+}
+
+uint32 GetEarnedStandardPoints( const IEconItemInterface* pQuestItem )
+{
+#ifndef GC_DLL
+ const CQuestItemTracker* pItemTracker = assert_cast< const CQuestItemTracker* >( QuestObjectiveManager()->GetTypedTracker< CQuestItemTracker* >( pQuestItem->GetID() ) );
+ if ( pItemTracker )
+ {
+ return pItemTracker->GetEarnedStandardPoints();
+ }
+#endif
+
+ uint32 nEarnedStandardPoints = 0;
+ static CSchemaAttributeDefHandle pAttribDef_EarnedStandardPoints( "quest earned standard points" );
+ pQuestItem->FindAttribute( pAttribDef_EarnedStandardPoints, &nEarnedStandardPoints );
+
+
+ return nEarnedStandardPoints;
+}
+
+uint32 GetEarnedBonusPoints( const IEconItemInterface* pQuestItem )
+{
+
+#ifndef GC_DLL
+ const CQuestItemTracker* pItemTracker = assert_cast< const CQuestItemTracker* >( QuestObjectiveManager()->GetTypedTracker< CQuestItemTracker* >( pQuestItem->GetID() ) );
+ if ( pItemTracker )
+ {
+ return pItemTracker->GetEarnedBonusPoints();
+ }
+#endif
+ uint32 nEarnedBonusPoints = 0;
+ static CSchemaAttributeDefHandle pAttribDef_EarnedBonusPoints( "quest earned bonus points" );
+ pQuestItem->FindAttribute( pAttribDef_EarnedBonusPoints, &nEarnedBonusPoints );
+
+ return nEarnedBonusPoints;
+} \ No newline at end of file
diff --git a/game/shared/econ/econ_quests.h b/game/shared/econ/econ_quests.h
new file mode 100644
index 0000000..e7beb8e
--- /dev/null
+++ b/game/shared/econ/econ_quests.h
@@ -0,0 +1,22 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Functions related to quests
+//
+//=============================================================================
+
+#ifndef ECON_QUESTS
+#define ECON_QUESTS
+#ifdef _WIN32
+#pragma once
+#endif
+
+//-----------------------------------------------------------------------------
+// Purpose: Given a quest item, return if the quest is considered "unidentified"
+//-----------------------------------------------------------------------------
+bool IsQuestItemUnidentified( const CEconItem* pQuestItem );
+bool IsQuestItemReadyToTurnIn( const IEconItemInterface* pQuestItem );
+bool IsQuestItemFullyCompleted( const IEconItemInterface* pQuestItem );
+uint32 GetEarnedStandardPoints( const IEconItemInterface* pQuestItem );
+uint32 GetEarnedBonusPoints( const IEconItemInterface* pQuestItem );
+
+#endif // ECON_QUESTS \ No newline at end of file
diff --git a/game/shared/econ/econ_store.cpp b/game/shared/econ/econ_store.cpp
new file mode 100644
index 0000000..9c69363c
--- /dev/null
+++ b/game/shared/econ/econ_store.cpp
@@ -0,0 +1,1672 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Common objects and utilities related to the in-game item store
+//
+//=============================================================================
+
+#include "cbase.h"
+#include "econ_store.h"
+#include "gcsdk/enumutils.h"
+
+#if defined(CLIENT_DLL)
+#include "econ_ui.h"
+#include "store/store_panel.h"
+#include "econ_item_inventory.h"
+#else
+#include "gcsdk/gcconstants.h"
+#endif
+
+#if defined(CLIENT_DLL) || defined(GAME_DLL)
+#include "econ_item_system.h"
+#endif
+
+// For localization
+#include "tier3/tier3.h"
+#include "vgui/ILocalize.h"
+#include "tier0/icommandline.h"
+
+#ifdef CLIENT_DLL
+// For formatting in locale
+#include <string>
+#include <sstream>
+#endif // CLIENT_DLL
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+ConVar store_version( "store_version", "1", FCVAR_CLIENTDLL | FCVAR_HIDDEN | FCVAR_ARCHIVE, "Which version of the store to display." );
+
+ENUMSTRINGS_START( ECurrency )
+ { k_ECurrencyUSD, "USD" },
+ { k_ECurrencyGBP, "GBP" },
+ { k_ECurrencyEUR, "EUR" },
+ { k_ECurrencyRUB, "RUB" },
+ { k_ECurrencyBRL, "BRL" },
+ { k_ECurrencyJPY, "JPY" },
+ { k_ECurrencyNOK, "NOK" },
+ { k_ECurrencyIDR, "IDR" },
+ { k_ECurrencyMYR, "MYR" },
+ { k_ECurrencyPHP, "PHP" },
+ { k_ECurrencySGD, "SGD" },
+ { k_ECurrencyTHB, "THB" },
+ { k_ECurrencyVND, "VND" },
+ { k_ECurrencyKRW, "KRW" },
+ { k_ECurrencyTRY, "TRY" },
+ { k_ECurrencyUAH, "UAH" },
+ { k_ECurrencyMXN, "MXN" },
+ { k_ECurrencyCAD, "CAD" },
+ { k_ECurrencyAUD, "AUD" },
+ { k_ECurrencyNZD, "NZD" },
+ { k_ECurrencyPLN, "PLN" },
+ { k_ECurrencyCHF, "CHF" },
+ { k_ECurrencyCNY, "CNY" },
+ { k_ECurrencyTWD, "TWD" },
+ { k_ECurrencyHKD, "HKD" },
+ { k_ECurrencyINR, "INR" },
+ { k_ECurrencyAED, "AED" },
+ { k_ECurrencySAR, "SAR" },
+ { k_ECurrencyZAR, "ZAR" },
+ { k_ECurrencyCOP, "COP" },
+ { k_ECurrencyPEN, "PEN" },
+ { k_ECurrencyCLP, "CLP" },
+ { k_ECurrencyInvalid, "Invalid" }
+ENUMSTRINGS_REVERSE( ECurrency, k_ECurrencyInvalid )
+
+ENUMSTRINGS_START( EPurchaseState )
+{ k_EPurchaseStateInvalid, "Invalid" },
+{ k_EPurchaseStateInit, "Init" },
+{ k_EPurchaseStateWaitingForAuthorization, "WaitingForAuthorization" },
+{ k_EPurchaseStatePending, "Pending" },
+{ k_EPurchaseStateComplete, "Complete" },
+{ k_EPurchaseStateFailed, "Failed" },
+{ k_EPurchaseStateCanceled, "Canceled" },
+{ k_EPurchaseStateRefunded, "Refunded" },
+{ k_EPurchaseStateChargeback, "Chargeback" },
+{ k_EPurchaseStateChargebackReversed, "Chargeback Reversed" },
+ENUMSTRINGS_END( EPurchaseState )
+
+ENUMSTRINGS_START( EPurchaseResult )
+{ k_EPurchaseResultOK, "OK" },
+{ k_EPurchaseResultFail, "Fail" },
+{ k_EPurchaseResultInvalidParam, "InvalidParam" },
+{ k_EPurchaseResultInternalError, "InternalError" },
+{ k_EPurchaseResultNotApproved, "NotApproved" },
+{ k_EPurchaseResultAlreadyCommitted, "AlreadyCommitted" },
+{ k_EPurchaseResultUserNotLoggedIn, "UserNotLoggedIn" },
+{ k_EPurchaseResultWrongCurrency, "WrongCurrency" },
+{ k_EPurchaseResultAccountError, "AccountError" },
+{ k_EPurchaseResultInsufficientFunds, "InsufficientFunds Reversed" },
+{ k_EPurchaseResultTimedOut, "TimedOut" },
+{ k_EPurchaseResultAcctDisabled, "AcctDisabled" },
+{ k_EPurchaseResultAcctCannotPurchase, "AcctCannotPurchase" },
+{ k_EMicroTxnResultFailedFraudChecks, "PurchaseFailedSupport" }, // this string is mismatched so "fraud" doesn't appear in the client code
+{ k_EPurchaseResultOldPriceSheet, "OldPriceSheet" },
+{ k_EPurchaseResultTxnNotFound, "TxnNotFound" },
+ENUMSTRINGS_END( EPurchaseResult )
+
+ENUMSTRINGS_START( EGCTransactionAuditReason )
+{ k_EGCTransactionAudit_GCTransactionCompleted, "Completed" },
+{ k_EGCTransactionAudit_GCTransactionInit, "Init" },
+{ k_EGCTransactionAudit_GCTransactionPostInit, "Post Init" },
+{ k_EGCTransactionAudit_GCTransactionFinalize, "Finalize" },
+{ k_EGCTransactionAudit_GCTransactionFinalizeFailed, "Finalize Failed" },
+{ k_EGCTransactionAudit_GCTransactionCanceled, "Canceled" },
+{ k_EGCTransactionAudit_SteamFailedMismatch, "Steam Failed State Mismatch" },
+{ k_EGCTransactionAudit_GCRemovePurchasedItems, "Remove Purchased Items" },
+{ k_EGCTransactionAudit_GCTransactionInsert, "Transaction Insert" },
+{ k_EGCTransactionAudit_GCTransactionCompletedPostChargeback, "Completed (Post-Chargeback)" },
+ENUMSTRINGS_END( EGCTransactionAuditReason )
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void econ_store_entry_t::InitCategoryTags( const char *pTags )
+{
+ // Default to "unrentable".
+ m_fRentalPriceScale = 1.0f;
+
+ if ( !pTags || !pTags[0] )
+ return;
+
+ // Cache tags - this pointer comes out of a KeyValues that isn't deleted
+ m_pchCategoryTags = pTags;
+
+ // Split the tags apart
+ CUtlVector< char * > vecTokens;
+ V_SplitString( pTags, "+", vecTokens );
+
+ // Sanity check the results of V_SplitString()
+#ifdef _DEBUG
+ const int nTagsLength = V_strlen( pTags );
+ int nSepCount = 0; // # of separators
+ for ( int i = 0; i < nTagsLength; ++i )
+ {
+ if ( pTags[i] == '+' )
+ ++nSepCount;
+ }
+// Assert( ( vecTokens.Size() - 1 ) == nSepCount );
+#endif
+
+ // Calculate the maximum that we want to charge for this rental.
+ float fMaxRentalPriceScale = 0.0f;
+
+ // Generate symbols for each tag
+ FOR_EACH_VEC( vecTokens, i )
+ {
+ CategoryTag_t info;
+ info.m_strName = vecTokens[i];
+ info.m_unID = CEconStoreCategoryManager::GetCategoryID( vecTokens[i] );
+ m_vecCategoryTags.AddToTail( info );
+
+ const float fCategoryRentalPriceScale = GetEconPriceSheet()->GetRentalPriceScale( vecTokens[i] );
+ fMaxRentalPriceScale = MAX( fMaxRentalPriceScale, fCategoryRentalPriceScale );
+ }
+
+ m_fRentalPriceScale = fMaxRentalPriceScale <= 0.0f || fMaxRentalPriceScale >= 100.0f
+ ? 1.0f
+ : fMaxRentalPriceScale * 0.01f;
+
+ // Clean up
+ vecTokens.PurgeAndDeleteElements();
+}
+
+void econ_store_entry_t::SetItemDefinitionIndex( item_definition_index_t usDefIndex )
+{
+ AssertMsg( usDefIndex != INVALID_ITEM_DEF_INDEX, "Invalid item definition index!" );
+ m_usDefIndex = usDefIndex;
+}
+
+bool econ_store_entry_t::IsListedInCategory( StoreCategoryID_t unID ) const
+{
+ AssertMsg( unID != CEconStoreCategoryManager::k_CategoryID_Invalid, "Did you mean to search for an invalid symbol?" );
+
+ FOR_EACH_VEC( m_vecCategoryTags, i )
+ {
+ if ( m_vecCategoryTags[i].m_unID == unID )
+ return true;
+ }
+
+ return false;
+}
+
+bool econ_store_entry_t::IsListedInSubcategories( const CEconStoreCategoryManager::StoreCategory_t &Category ) const
+{
+ FOR_EACH_VEC( Category.m_vecSubcategories, iSubCategory )
+ {
+ if ( IsListedInCategory( Category.m_vecSubcategories[iSubCategory]->m_unID ) )
+ return true;
+ }
+
+ return false;
+}
+
+bool econ_store_entry_t::IsListedInCategoryOrSubcategories( const CEconStoreCategoryManager::StoreCategory_t &Category ) const
+{
+ return IsListedInCategory( Category.m_unID ) ||
+ IsListedInSubcategories( Category );
+}
+
+bool econ_store_entry_t::IsOnSale( ECurrency eCurrency ) const
+{
+ return GetSalePrice( eCurrency ) > 0
+ && GetSalePrice( eCurrency ) < GetBasePrice( eCurrency );
+}
+
+bool econ_store_entry_t::IsRentable() const
+{
+ return m_fRentalPriceScale < 1.0f
+ && m_fRentalPriceScale > 0.0f;
+}
+
+#ifdef CLIENT_DLL
+bool econ_store_entry_t::HasDiscount( ECurrency eCurrency, item_price_t *out_punOptionalBasePrice ) const
+{
+ // Items on sale always report as being discounted.
+ if ( IsOnSale( eCurrency ) )
+ {
+ if ( out_punOptionalBasePrice )
+ {
+ *out_punOptionalBasePrice = GetBasePrice( eCurrency );
+ }
+
+ return true;
+ }
+
+ // If we're not a bundle we have no other way of being on sale -- abort.
+ const CEconItemDefinition *pItemDef = GetItemSchema()->GetItemDefinition( GetItemDefinitionIndex() );
+ if ( !pItemDef || !pItemDef->IsBundle() )
+ return false;
+
+ const bundleinfo_t *pBundleInfo = pItemDef->GetBundleInfo();
+ Assert( pBundleInfo );
+
+ item_price_t unTotalPriceOfBundleItems = 0;
+ FOR_EACH_VEC( pBundleInfo->vecItemDefs, bundleIdx )
+ {
+ const econ_store_entry_t *pCurEntry = pBundleInfo->vecItemDefs[bundleIdx]
+ ? GetEconPriceSheet()->GetEntry( pBundleInfo->vecItemDefs[bundleIdx]->GetDefinitionIndex() )
+ : NULL;
+ if ( pCurEntry )
+ {
+ unTotalPriceOfBundleItems += pCurEntry->GetCurrentPrice( eCurrency );
+ }
+ }
+
+ // Did the bundle provide an actual discount?
+ const item_price_t unBasePrice = GetCurrentPrice( eCurrency );
+ if ( unBasePrice < unTotalPriceOfBundleItems )
+ {
+ if ( out_punOptionalBasePrice )
+ {
+ *out_punOptionalBasePrice = unTotalPriceOfBundleItems;
+ }
+
+ return true;
+ }
+
+ // No discount. Leave the base price pointer untouched.
+ return false;
+}
+#endif
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+StoreCategoryID_t econ_store_entry_t::GetCategoryTagIDFromIndex( uint32 iIndex ) const
+{
+ if ( !IsValidCategoryTagIndex( iIndex ) )
+ return CEconStoreCategoryManager::k_CategoryID_Invalid;
+
+ return m_vecCategoryTags[ iIndex ].m_unID;
+}
+
+item_price_t econ_store_entry_t::GetCurrentPrice( ECurrency eCurrency ) const
+{
+#ifdef CLIENT_DLL
+ if ( m_bIsMarketItem )
+ {
+ const client_market_data_t *pClientMarketData = GetClientMarketData( GetItemDefinitionIndex(), AE_UNIQUE );
+ if ( !pClientMarketData )
+ return 0;
+
+ return pClientMarketData->m_unLowestPrice;
+ }
+#endif
+ return IsOnSale( eCurrency )
+ ? GetSalePrice( eCurrency )
+ : GetBasePrice( eCurrency );
+}
+
+float econ_store_entry_t::GetRentalPriceScale() const
+{
+ Assert( IsRentable() );
+
+ return m_fRentalPriceScale;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void econ_store_entry_t::ValidatePrice( ECurrency eCurrency, item_price_t unPrice )
+{
+ if ( m_bIsMarketItem )
+ return;
+
+ // If the price is 0 and this is not a pack item (an item that is not individually for sale, but is sold as part of a pack bundle), post an alert
+ if ( unPrice == 0 && !m_bIsPackItem )
+ {
+ CFmtStr fmtError( "Warning: Invalid price for item (item def=%i)", GetItemDefinitionIndex() );
+#if defined( GC_DLL )
+ GGCGameBase()->PostAlert( GCSDK::k_EAlertTypeReport, true, fmtError.Access() );
+#endif
+ AssertMsg( false, "%s", fmtError.Access() );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor
+//-----------------------------------------------------------------------------
+CEconStorePriceSheet::CEconStorePriceSheet()
+ : m_pKVRaw( NULL )
+ , m_mapEntries( DefLessFunc( uint16 ) )
+ , m_mapRentalPriceScales( DefLessFunc( const char * ) )
+ , m_RTimeVersionStamp( 0 )
+ , m_pStorePromotionFirstTimePurchaseItem( NULL )
+ , m_pStorePromotionFirstTimeWebPurchaseItem( NULL )
+ , m_unFeaturedItemIndex( 0 )
+ , m_eEconStoreSortType( kEconStoreSortType_Name_AToZ )
+{
+ Clear();
+ m_FeaturedItems.m_pchName = "featured_items";
+
+ m_mapCurrencyPricePoints.SetLessFunc( &price_point_map_key_t::Less );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Destructor
+//-----------------------------------------------------------------------------
+CEconStorePriceSheet::~CEconStorePriceSheet()
+{
+ Clear();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Clears out the parsed data
+//-----------------------------------------------------------------------------
+void CEconStorePriceSheet::Clear()
+{
+ if ( m_pKVRaw )
+ {
+ m_pKVRaw->deleteThis();
+ m_pKVRaw = NULL;
+ }
+
+ m_RTimeVersionStamp = 0;
+ m_mapEntries.RemoveAll();
+ m_FeaturedItems.m_vecEntries.RemoveAll();
+ m_flPreviewPeriodDiscount = 0;
+
+ m_mapCurrencyPricePoints.Purge();
+
+#ifdef CLIENT_DLL
+ m_vecFeaturedItems.Purge();
+#endif // CLIENT_DLL
+
+ // Clear categories in category manager
+ ClearEconStoreCategoryManager();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Gets the entry details for a specific item
+//-----------------------------------------------------------------------------
+const econ_store_entry_t *CEconStorePriceSheet::GetEntry( item_definition_index_t usDefIndex ) const
+{
+ int iIndex = m_mapEntries.Find( usDefIndex );
+ if ( m_mapEntries.IsValidIndex( iIndex ) )
+ return &m_mapEntries[iIndex];
+
+ return NULL;
+}
+
+#ifdef GC_DLL
+//-----------------------------------------------------------------------------
+// Purpose: Gets the entry details for a specific item and lets us modify the contents (GC-only)
+//-----------------------------------------------------------------------------
+econ_store_entry_t *CEconStorePriceSheet::GetEntryWriteable( item_definition_index_t unDefIndex )
+{
+ int iIndex = m_mapEntries.Find( unDefIndex );
+ if ( m_mapEntries.IsValidIndex( iIndex ) )
+ return &m_mapEntries[iIndex];
+
+ return NULL;
+}
+#endif // GC_DLL
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool BInsertSinglePricePoint( CurrencyPricePointMap_t& out_mapPricePoints, const price_point_map_key_t& key, item_price_t unValue )
+{
+ if ( out_mapPricePoints.Find( key ) != out_mapPricePoints.InvalidIndex() )
+ return false;
+
+ out_mapPricePoints.Insert( key, unValue );
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool BInitializeCurrencyPricePoints( CurrencyPricePointMap_t& out_mapPricePoints, KeyValues *pKVPricePoints )
+{
+ if ( !pKVPricePoints )
+ return false;
+
+ // Zero-price-point is universal.
+ FOR_EACH_CURRENCY( eCurrency )
+ {
+ const price_point_map_key_t key = { 0, eCurrency };
+ if ( !BInsertSinglePricePoint( out_mapPricePoints, key, 0 ) )
+ return false;
+ }
+
+ // Individual price points specified in our store config.
+ FOR_EACH_TRUE_SUBKEY( pKVPricePoints, pKVUSD )
+ {
+ const item_price_t unUSD = Q_atoi( pKVUSD->GetName() );
+
+ // USD key, for ease of lookup.
+ {
+ const price_point_map_key_t key = { unUSD, k_ECurrencyUSD };
+ if ( !BInsertSinglePricePoint( out_mapPricePoints, key, unUSD ) )
+ return false;
+ }
+
+ // Exchange rates.
+ FOR_EACH_VALUE( pKVUSD, pKVOtherCurrency )
+ {
+ const char *pszCurrencyName = pKVOtherCurrency->GetName();
+ const ECurrency eCurrency = ECurrencyFromName( pszCurrencyName );
+
+ if ( eCurrency == k_ECurrencyInvalid )
+ {
+ // Spew
+#ifdef GC_DLL
+ EmitError( SPEW_GC, "Unknown Currency [%s] found in price sheet. Currency is unsupported!\n", pszCurrencyName );
+#endif
+ // don't crash, just conintue
+ continue;
+ }
+
+ const price_point_map_key_t key = { unUSD, eCurrency };
+ if ( !BInsertSinglePricePoint( out_mapPricePoints, key, pKVOtherCurrency->GetInt() ) )
+ return false;
+ }
+ }
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Parses the KV version of the price sheet
+//-----------------------------------------------------------------------------
+bool CEconStorePriceSheet::InitFromKV( KeyValues *pKVRoot )
+{
+ Clear();
+ m_pKVRaw = pKVRoot->MakeCopy();
+
+ // Initialize categories - needed to initialize store entries
+ if ( !GEconStoreCategoryManager()->BInit( this, m_pKVRaw ) )
+ {
+#ifdef GC_DLL
+ EmitError( SPEW_GC, "Unable to Init GEconStoreCategoryManager \n" );
+#endif
+ return false;
+ }
+
+ // Initialize map of currency price points.
+ if ( !BInitializeCurrencyPricePoints( m_mapCurrencyPricePoints, pKVRoot->FindKey( "inventoryvalvegcpricesheet" ) ) )
+ {
+#ifdef GC_DLL
+ EmitError( SPEW_GC, "Unable to Init CurrencyPricePoints \n" );
+#endif
+ return false;
+ }
+
+ m_unFeaturedItemIndex = pKVRoot->GetInt( "featured_item_index", 0 );
+
+ // first time purchase gift
+ m_pStorePromotionFirstTimePurchaseItem = GetItemSchema()->GetItemDefinitionByName( pKVRoot->GetString( "promotion_first_time_purchase_gift" ) );
+ m_pStorePromotionFirstTimeWebPurchaseItem = GetItemSchema()->GetItemDefinitionByName( pKVRoot->GetString( "promotion_first_time_web_purchase_gift" ) );
+
+ EUniverse eUniverse = GetUniverse();
+
+ m_unPreviewPeriod = pKVRoot->GetInt( eUniverse == k_EUniversePublic ? "preview_period" : "preview_period_nonpublic" );
+ m_unBonusDiscountPeriod = pKVRoot->GetInt( eUniverse == k_EUniversePublic ? "bonus_discount_period" : "bonus_discount_period_nonpublic" );
+ m_flPreviewPeriodDiscount = pKVRoot->GetFloat( "preview_period_discount" );
+
+#ifdef ENABLE_STORE_RENTAL_BACKEND
+ KeyValues *pRentalPriceScalesKV = pKVRoot->FindKey( "rental_price_scale" );
+ if ( pRentalPriceScalesKV )
+ {
+ FOR_EACH_SUBKEY( pRentalPriceScalesKV, pCategoryKV )
+ {
+ m_mapRentalPriceScales.InsertOrReplace( pCategoryKV->GetName(), pCategoryKV->GetFloat() );
+ }
+ }
+#endif
+
+ memset( &m_StorePromotionSpendForFreeItem, 0, sizeof( m_StorePromotionSpendForFreeItem ) );
+ KeyValues *pPromotionsKV = m_pKVRaw->FindKey( "promotion_spend_for_free_item" );
+ if ( pPromotionsKV )
+ {
+ item_price_t unFreeItemSpendAmountUSD = pPromotionsKV->GetInt( "price_threshold", 0 );
+ FOR_EACH_CURRENCY( eCurrency )
+ {
+ const price_point_map_key_t key = { unFreeItemSpendAmountUSD, eCurrency };
+ const CurrencyPricePointMap_t::IndexType_t unIdx = m_mapCurrencyPricePoints.Find( key );
+ if ( unIdx == m_mapCurrencyPricePoints.InvalidIndex() )
+ {
+#ifdef GC_DLL
+ EmitError( SPEW_GC, "Unable to Find Currency %s in Currency Map. Likely missing from inventoryvalvegcpricesheet.vdf \n", PchNameFromECurrency(eCurrency) );
+#endif
+ continue;
+ }
+
+ m_StorePromotionSpendForFreeItem.m_rgusPriceThreshold[ eCurrency ] = m_mapCurrencyPricePoints[unIdx];
+ }
+
+#if !defined(CLIENT_DLL) && !defined(GAME_DLL)
+ CEconItemSchema *pSchema = GEconManager()->GetItemSchema();
+ m_StorePromotionSpendForFreeItem.m_pItemDef = pSchema->GetItemDefinitionByName( pPromotionsKV->GetString( "item_definition" ) );
+ AssertMsg( m_StorePromotionSpendForFreeItem.m_pItemDef || !pPromotionsKV->GetString( "item_definition", NULL ),
+ "Could find not the specified item for \"promotion_spend_for_free_item\"" );
+#endif
+ }
+
+ // Get the normal sections
+ KeyValues *pEntriesKV = m_pKVRaw->FindKey( "entries" );
+ if ( pEntriesKV )
+ {
+ FOR_EACH_TRUE_SUBKEY( pEntriesKV, pKVEntry )
+ {
+ if ( !BInitEntryFromKV( pKVEntry ) )
+ {
+#ifdef GC_DLL
+ EmitError( SPEW_GC, "Unable to Find Entries in Currency KVP \n" );
+#endif
+ continue;
+ }
+ }
+ }
+
+ // Get a list of Market entries to populate in to the 'store'. GC never cares or reads these items
+ // Only for Client
+#ifdef CLIENT_DLL
+ KeyValues *pMarketEntriesKV = m_pKVRaw->FindKey( "market_entries" );
+ if ( pMarketEntriesKV )
+ {
+ FOR_EACH_TRUE_SUBKEY( pMarketEntriesKV, pKVEntry )
+ {
+ if ( !BInitMarketEntryFromKV( pKVEntry ) )
+ {
+ return false;
+ }
+ }
+ }
+
+ KeyValues *pFeaturedItemsKV = m_pKVRaw->FindKey( "featured_items" );
+ if ( pFeaturedItemsKV )
+ {
+ FOR_EACH_SUBKEY( pFeaturedItemsKV, pKVEntry )
+ {
+ const char *pszItemName = pKVEntry->GetName();
+ const CEconItemDefinition *pDef = GetItemSchema()->GetItemDefinitionByName( pszItemName );
+ if ( !pDef )
+ {
+ AssertMsg1( 0, "Unable to find item: %s", pszItemName );
+ continue;
+ }
+
+ m_vecFeaturedItems.AddToTail( pDef->GetDefinitionIndex() );
+ }
+ }
+#endif
+
+ // Generate a hash of all item def indices and cache it off
+ m_unHashForAllItems = CalculateHashFromItems();
+
+#ifdef GC_DLL
+ // Parse the sales block on the GC. We'll use this to dynamically adjust prices.
+ KeyValues *pTimedSalesKV = m_pKVRaw->FindKey( "timed_sales" );
+ if ( pTimedSalesKV )
+ {
+ FOR_EACH_TRUE_SUBKEY( pTimedSalesKV, pKVSale )
+ {
+ if ( !InitTimedSaleEntryFromKV( pKVSale ) )
+ {
+ EmitError( SPEW_GC, "Unable to Init Timed Sale \n" );
+ }
+ return false;
+ }
+
+ // Verify that none of our timed sales have overlapping items.
+ if ( !VerifyTimedSaleEntries() )
+ return false;
+ }
+#endif // GC_DLL
+
+ // Now that store entries are loaded, let the category manager do more stuff
+ if ( !GEconStoreCategoryManager()->BOnPriceSheetLoaded( this ) )
+ return false;
+
+ return true;
+}
+
+uint32 CEconStorePriceSheet::CalculateHashFromItems() const
+{
+ CRC32_t unHash;
+ CRC32_Init( &unHash );
+
+ FOR_EACH_MAP_FAST( m_mapEntries, idx )
+ {
+ const econ_store_entry_t &entry = m_mapEntries[idx];
+ item_definition_index_t usDefIndexTmp = entry.GetItemDefinitionIndex();
+ CRC32_ProcessBuffer( &unHash, &usDefIndexTmp, sizeof( usDefIndexTmp ) );
+ }
+
+ CRC32_Final( &unHash );
+
+ return (uint32)unHash;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool BInitializeStoreEntryPricePoints( econ_store_entry_t& out_entry, const CurrencyPricePointMap_t& mapCurrencyPricePoints, KeyValues *pKVPrice, int nSalePercent )
+{
+ if ( !pKVPrice )
+ return false;
+
+ FOR_EACH_CURRENCY( eCurrency )
+ {
+ const price_point_map_key_t key = { (item_price_t)pKVPrice->GetInt(), eCurrency };
+ const CurrencyPricePointMap_t::IndexType_t unIdx = mapCurrencyPricePoints.Find( key );
+
+ // Looking for a price point that doesn't exist, or doesn't exist for this currency?
+ if ( unIdx == mapCurrencyPricePoints.InvalidIndex() )
+ {
+#ifdef GC_DLL
+ EmitError( SPEW_GC, "Unable to Find Currency %s in init price points. Currency is missing from inventoryvalvegcpricesheet.vdf \n", PchNameFromECurrency( eCurrency ) );
+#endif
+ continue;
+ }
+
+ // Weird initialization pattern: we're making sure that the value we read from the KeyValues
+ // block is the value we're storing in memory. We do this to avoid integer conversion problems,
+ // especially overflow (!).
+ const item_price_t unPrice = mapCurrencyPricePoints[unIdx];
+ out_entry.SetBasePrice( eCurrency, unPrice );
+#pragma warning(push)
+#pragma warning(disable : 4389)
+ Assert( out_entry.GetBasePrice( eCurrency ) == unPrice ); // NOTE: DO NOT CAST unPrice - CHECKING FOR OVERFLOW
+#pragma warning(pop)
+
+ if ( ( nSalePercent > 0 ) && ( nSalePercent < 100 ) )
+ {
+ const item_price_t unSalePrice = out_entry.CalculateSalePrice( &out_entry, eCurrency, (float)nSalePercent );
+ out_entry.SetSalePrice( eCurrency, unSalePrice );
+ }
+ }
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Parses the KV section piece and add a econ_store_entry_t
+//-----------------------------------------------------------------------------
+bool CEconStorePriceSheet::BInitEntryFromKV( KeyValues *pKVEntry )
+{
+ const bool bIsPackItem = pKVEntry->GetBool( "is_pack_item", false );
+
+#if defined( CLIENT_DLL )
+ // Skip pack items on the client
+ if ( bIsPackItem )
+ return true;
+#endif
+
+ econ_store_entry_t entry;
+ entry.InitCategoryTags( pKVEntry->GetString( "category_tags" ) );
+
+ const bool bIsNew = entry.IsListedInCategory( CEconStoreCategoryManager::k_CategoryID_New );
+ entry.m_bLimited = entry.IsListedInCategory( CEconStoreCategoryManager::k_CategoryID_Limited );
+ entry.m_bNew = bIsNew;
+ entry.m_bPreviewAllowed = !bIsNew && entry.IsListedInCategory( CEconStoreCategoryManager::k_CategoryID_Weapons );
+ entry.m_bIsMarketItem = false;
+ const bool bIsHighlighted = entry.IsListedInCategory( CEconStoreCategoryManager::k_CategoryID_Highlighted );
+ entry.m_bHighlighted = bIsHighlighted;
+
+ if ( entry.GetCategoryTagCount() == 0 )
+ {
+ AssertMsg1( 0, "Unable to get category_tags for entry: %s", pKVEntry->GetName() );
+ return false;
+ }
+
+ const CEconItemDefinition *pDef = GetItemSchema()->GetItemDefinitionByName( pKVEntry->GetString( "item_link" ) );
+ if ( !pDef )
+ {
+ AssertMsg1( 0, "Unable to find item: %s", pKVEntry->GetString( "item_link" ) );
+ return false;
+ }
+
+ entry.SetItemDefinitionIndex( pDef->GetDefinitionIndex() );
+
+ int iQuantity = pKVEntry->GetInt( "quantity", 1 );
+ entry.SetQuantity( iQuantity );
+ Assert( entry.GetQuantity() == iQuantity );
+
+ entry.SetSteamGiftPackageID( pKVEntry->GetUint64( "steam_gift_package_id", 0 ) );
+
+#if defined( TF_CLIENT_DLL ) || defined( TF_GC_DLL )
+ entry.SetDate( pDef->GetFirstSaleDate() );
+#else
+ entry.SetDate( pKVEntry->GetString( "date", "1/1/1900" ) );
+#endif
+
+ // Is the item sold out of the store?
+ entry.m_bSoldOut = pKVEntry->GetBool( "sold_out", false );
+
+ // Is the item a pack item?
+ entry.m_bIsPackItem = bIsPackItem;
+
+ int nSalePercent = pKVEntry->GetInt( "sale_percentage", 0 );
+ if ( ( nSalePercent < 0 ) || ( nSalePercent >= 100 ) )
+ {
+ AssertMsg( false, "Sale percentage for %s set to an invalid value: %d\n", pDef->GetDefinitionName(), nSalePercent );
+ return false;
+ }
+
+ if ( !BInitializeStoreEntryPricePoints( entry, m_mapCurrencyPricePoints, pKVEntry->FindKey( "base_price" ), nSalePercent ) )
+ return false;
+
+ m_mapEntries.InsertOrReplace( entry.GetItemDefinitionIndex(), entry );
+ return true;
+}
+
+#ifdef CLIENT_DLL
+//-----------------------------------------------------------------------------
+// Purpose: Parses the KV section piece and add a econ_store_entry_t
+//-----------------------------------------------------------------------------
+bool CEconStorePriceSheet::BInitMarketEntryFromKV( KeyValues *pKVEntry )
+{
+ //"The Alien Cranium"
+ //{
+ // "item_link" "The Alien Cranium"
+ // "category_tags" "Cosmetics+Halloween"
+ // "date" "11/13/2014"
+ // "base_price" "1299"
+ //}
+
+ econ_store_entry_t entry;
+ entry.InitCategoryTags( pKVEntry->GetString( "category_tags" ) );
+
+ const bool bIsNew = entry.IsListedInCategory( CEconStoreCategoryManager::k_CategoryID_New );
+ entry.m_bLimited = entry.IsListedInCategory( CEconStoreCategoryManager::k_CategoryID_Limited );
+ entry.m_bNew = bIsNew;
+ entry.m_bPreviewAllowed = false;
+ entry.m_bIsMarketItem = true;
+
+ if ( entry.GetCategoryTagCount() == 0 )
+ {
+ AssertMsg1( 0, "Unable to get category_tags for entry: %s", pKVEntry->GetName() );
+ return false;
+ }
+
+ const CEconItemDefinition *pDef = GetItemSchema()->GetItemDefinitionByName( pKVEntry->GetString( "item_link" ) );
+ if ( !pDef )
+ {
+ AssertMsg1( 0, "Unable to find item: %s", pKVEntry->GetString( "item_link" ) );
+ return false;
+ }
+
+ entry.SetItemDefinitionIndex( pDef->GetDefinitionIndex() );
+ entry.SetQuantity( 1 );
+ entry.SetDate( pDef->GetFirstSaleDate() ); // FIXME?
+
+ entry.m_bSoldOut = false;
+ entry.m_bIsPackItem = false;
+
+ // Entry should never already exist in mapEntries
+ if ( m_mapEntries.Find( entry.GetItemDefinitionIndex() ) != m_mapEntries.InvalidIndex() )
+ {
+ AssertMsg1( 0, "Market Entry already eixsts : %s", pKVEntry->GetString( "item_link" ) );
+ return false;
+ }
+
+ m_mapEntries.InsertOrReplace( entry.GetItemDefinitionIndex(), entry );
+ return true;
+}
+#endif // CLIENT_DLL
+
+#ifdef GC_DLL
+//-----------------------------------------------------------------------------
+// Purpose: Parses the KV section piece and add a econ_store_entry_t
+//-----------------------------------------------------------------------------
+static RTime32 ConvertKVDateToRTime32( KeyValues *pKV, const char *pszKey )
+{
+ Assert( pKV );
+ Assert( pszKey );
+
+ const char *pszTime = pKV->GetString( pszKey, NULL );
+ if ( !pszTime || !pszTime[0] )
+ return 0;
+
+ RTime32 unConvertedTime = CRTime::RTime32FromString( pszTime );
+ if ( unConvertedTime == (RTime32)-1 )
+ return 0;
+
+ return unConvertedTime;
+}
+
+bool CEconStorePriceSheet::InitTimedSaleEntryFromKV( KeyValues *pKVTimedSaleEntry )
+{
+ Assert( pKVTimedSaleEntry );
+
+ econ_store_timed_sale_t TimedSale;
+
+ TimedSale.m_bSaleCurrentlyActive = false;
+ TimedSale.m_sIdentifier = pKVTimedSaleEntry->GetName();
+
+ TimedSale.m_SaleStartTime = ConvertKVDateToRTime32( pKVTimedSaleEntry, "sale_start_date" );
+ TimedSale.m_SaleEndTime = ConvertKVDateToRTime32( pKVTimedSaleEntry, "sale_end_date" );
+
+ // Sanity check -- make sure this sale lasts a greater-than-zero amount of time. This will also
+ // catch any cases where the end time was invalid and so returned 0.
+ if ( TimedSale.m_SaleStartTime >= TimedSale.m_SaleEndTime )
+ {
+ EG_ERROR( GCSDK::SPEW_GC, "Timed sale '%s' has invalid duration\n", pKVTimedSaleEntry->GetName() );
+ return false;
+ }
+
+ // Make sure the end time is also greater than 0.
+ if ( TimedSale.m_SaleStartTime <= 0 )
+ {
+ EG_ERROR( GCSDK::SPEW_GC, "Timed sale '%s' has invalid start time\n", pKVTimedSaleEntry->GetName() );
+ return false;
+ }
+
+ // What items does this sale apply to?
+ KeyValues *pKVSaleItems = pKVTimedSaleEntry->FindKey( "sale_items" );
+ if ( !pKVSaleItems )
+ {
+ EG_ERROR( GCSDK::SPEW_GC, "Timed sale '%s' missing \"sale_items\" section\n", pKVTimedSaleEntry->GetName() );
+ return false;
+ }
+
+ FOR_EACH_TRUE_SUBKEY( pKVSaleItems, pKVSaleItem )
+ {
+ const char *pszName = pKVSaleItem->GetString( "name", NULL );
+ if ( !pszName )
+ {
+ EG_ERROR( GCSDK::SPEW_GC, "Timed sale '%s' has invalid/missing item name for entry '%s'\n", pKVTimedSaleEntry->GetName(), pKVSaleItem->GetName() );
+ return false;
+ }
+
+ const CEconItemDefinition *pItemDef = GetItemSchema()->GetItemDefinitionByName( pszName );
+ if ( !pItemDef )
+ {
+ EG_ERROR( GCSDK::SPEW_GC, "Timed sale '%s' has missing item named '%s' for entry '%s'\n", pKVTimedSaleEntry->GetName(), pszName, pKVSaleItem->GetName() );
+ return false;
+ }
+
+ const float fSalePercentageOff = pKVSaleItem->GetFloat( "sale_percentage_off", -1.0f );
+ if ( fSalePercentageOff <= 0.0f || fSalePercentageOff > 100.0f )
+ {
+ EG_ERROR( GCSDK::SPEW_GC, "Timed sale '%s' has invalid sale percentage for item named '%s' for entry '%s'\n", pKVTimedSaleEntry->GetName(), pszName, pKVSaleItem->GetName() );
+ return false;
+ }
+
+ const econ_store_entry_t *pStoreEntry = GetEntry( pItemDef->GetDefinitionIndex() );
+ if ( !pStoreEntry )
+ {
+ EG_ERROR( GCSDK::SPEW_GC, "Timed sale '%s' has item named '%s' for entry '%s' that is not in the store\n", pKVTimedSaleEntry->GetName(), pszName, pKVSaleItem->GetName() );
+ return false;
+ }
+
+ // Verify that no item in a timed sale is already in a hard-coded sale as well. This would break
+ // our fragile pricing math assumptions.
+ FOR_EACH_CURRENCY( eCurrency )
+ {
+ if ( pStoreEntry->IsOnSale( eCurrency ) )
+ {
+ EG_ERROR( GCSDK::SPEW_GC, "Timed sale '%s' has item named '%s' for entry '%s' that hard-coded to be on sale (currency: %i)\n", pKVTimedSaleEntry->GetName(), pszName, pKVSaleItem->GetName(), eCurrency );
+ return false;
+ }
+ }
+
+ econ_store_timed_sale_item_t SaleItem;
+ SaleItem.m_unItemDef = pItemDef->GetDefinitionIndex();
+ SaleItem.m_fPricePercentage = fSalePercentageOff;
+
+ TimedSale.m_vecSaleItems.AddToTail( SaleItem );
+ }
+
+
+ if ( TimedSale.m_vecSaleItems.Count() <= 0 )
+ {
+ EG_ERROR( GCSDK::SPEW_GC, "Timed sale '%s' has no valid items to put on sale\n", pKVTimedSaleEntry->GetName() );
+ return false;
+ }
+
+ // We made it this far with no errors, so add this as a timed sale block if it actually affects
+ // any items.
+ m_vecTimedSales.AddToTail( TimedSale );
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CEconStorePriceSheet::VerifyTimedSaleEntries()
+{
+ // We could write an interval tree and be smart about this, but Fletcher made
+ // faces at me when I suggested it so we just brute force it -- we find each
+ // item in each sale sequentially, find each sale that overlaps with the current
+ // sale, and make sure that it doesn't have the same item.
+ for ( int i = 0; i < m_vecTimedSales.Count(); i++ )
+ {
+ const econ_store_timed_sale_t& BaseSale = m_vecTimedSales[i];
+ Assert( BaseSale.m_vecSaleItems.Count() > 0 );
+
+ for ( int j = 0; j < BaseSale.m_vecSaleItems.Count(); j++ )
+ {
+ const item_definition_index_t unSearchDefIndex = BaseSale.m_vecSaleItems[j].m_unItemDef;
+
+ for ( int k = i; k < m_vecTimedSales.Count(); k++ )
+ {
+ // Does this sale overlap with our current sale?
+ const econ_store_timed_sale_t& OtherSale = m_vecTimedSales[k];
+
+ if ( k == i || MAX( BaseSale.m_SaleStartTime, OtherSale.m_SaleStartTime ) <= MIN( BaseSale.m_SaleEndTime, OtherSale.m_SaleEndTime ) )
+ {
+ // We overlap, so make sure this item doesn't show up in both lists. Start the search in our
+ // current sale to make sure the same entry doesn't show up twice and then look at the full
+ // list of items in other overlapping sales.
+ for ( int l = (k == i ? j + 1 : 0); l < OtherSale.m_vecSaleItems.Count(); l++ )
+ {
+ const item_definition_index_t unMaybeMatchDefIndex = OtherSale.m_vecSaleItems[l].m_unItemDef;
+ if ( unSearchDefIndex == unMaybeMatchDefIndex )
+ {
+ EG_ERROR( GCSDK::SPEW_GC, "Conflict detected for item index '%i' betweens timed sales '%s' / '%s'\n",
+ unSearchDefIndex,
+ BaseSale.m_sIdentifier.Get(),
+ OtherSale.m_sIdentifier.Get() );
+ return false;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CEconStorePriceSheet::UpdatePricesForTimedSales( const RTime32 curTime )
+{
+ FOR_EACH_VEC( m_vecTimedSales, i )
+ {
+ econ_store_timed_sale_t& TimedSale = m_vecTimedSales[i];
+ Assert( TimedSale.m_vecSaleItems.Count() > 0 );
+
+ // Is this sale active in our current time?
+ const bool bSaleShouldBeActive = (curTime >= TimedSale.m_SaleStartTime)
+ && (curTime <= TimedSale.m_SaleEndTime);
+
+ // Last time we processed it, was this sale active?
+ const bool bSaleIsActive = TimedSale.m_bSaleCurrentlyActive;
+
+ // State transition?
+ if ( bSaleShouldBeActive != bSaleIsActive )
+ {
+ FOR_EACH_VEC( TimedSale.m_vecSaleItems, i )
+ {
+ econ_store_entry_t *unSaleStoreEntry = GetEntryWriteable( TimedSale.m_vecSaleItems[i].m_unItemDef );
+ Assert( unSaleStoreEntry );
+
+ // We don't support items being on sale in multiple ways at the same time (ie., a
+ // sale specified in the base prices in store.txt and also a timed sale) so we just stomp
+ // whatever the sale price is with the price we think we should be on sale for.
+ FOR_EACH_CURRENCY( eCurrency )
+ {
+ unSaleStoreEntry->SetSalePrice( eCurrency,
+ bSaleIsActive ? unSaleStoreEntry->GetBasePrice( eCurrency ) * TimedSale.m_vecSaleItems[i].m_fPricePercentage : 0 );
+ }
+ }
+
+ // Update our last-updated timestamp if any of these sales changed -- use the most recent
+ // "start of sale" date. This will allow people using the store prices WebAPI to know whether
+ // prices have changed.
+
+ // If items just went on sale, their price changed at the start of this sale.
+ if ( bSaleIsActive )
+ {
+ m_RTimeVersionStamp = MAX( m_RTimeVersionStamp, TimedSale.m_SaleStartTime );
+ }
+
+ // If items just got taken off sale, their price changed at the end of this sale.
+ else // if ( !bSaleIsActive )
+ {
+ m_RTimeVersionStamp = MAX( m_RTimeVersionStamp, TimedSale.m_SaleEndTime );
+ }
+
+ // Update our cached state.
+ TimedSale.m_bSaleCurrentlyActive = bSaleShouldBeActive;
+
+ // Debug output.
+ EmitInfo( GCSDK::SPEW_GC, SPEW_ALWAYS, LOG_ALWAYS, "Timed sale '%s' has been %s.\n", TimedSale.m_sIdentifier.Get(), bSaleShouldBeActive ? "enabled" : "disabled" );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CEconStorePriceSheet::DumpTimeSaleState( const RTime32 curTime ) const
+{
+ char curTimeBuf[k_RTimeRenderBufferSize];
+ EmitInfo( GCSDK::SPEW_GC, SPEW_ALWAYS, LOG_ALWAYS, "Current sale calculation time: %s\n", CRTime::RTime32ToString( curTime, curTimeBuf ) );
+
+ FOR_EACH_VEC( m_vecTimedSales, i )
+ {
+ const econ_store_timed_sale_t& TimedSale = m_vecTimedSales[i];
+ Assert( TimedSale.m_vecSaleItems.Count() > 0 );
+
+ if ( !TimedSale.m_bSaleCurrentlyActive )
+ {
+ EmitInfo( GCSDK::SPEW_GC, SPEW_ALWAYS, LOG_ALWAYS, "\tSale '%s' (not active)\n", TimedSale.m_sIdentifier.Get() );
+ }
+ else
+ {
+ EmitInfo( GCSDK::SPEW_GC, SPEW_ALWAYS, LOG_ALWAYS, "\tSale '%s' (active):\n", TimedSale.m_sIdentifier.Get() );
+
+ FOR_EACH_VEC( TimedSale.m_vecSaleItems, i )
+ {
+ EmitInfo( GCSDK::SPEW_GC, SPEW_ALWAYS, LOG_ALWAYS, "\t\t%s is %.0f%% off\n",
+ GetItemSchema()->GetItemDefinition( TimedSale.m_vecSaleItems[i].m_unItemDef )->GetDefinitionName(),
+ TimedSale.m_vecSaleItems[i].m_fPricePercentage );
+ }
+ }
+ }
+}
+#endif // GC_DLL
+
+//-----------------------------------------------------------------------------
+// Performs calculation of a discounted price given a base price, and will then handle ensuring the appropriate number of zeros at the end of the price via rounding
+//-----------------------------------------------------------------------------
+/*static*/ item_price_t econ_store_entry_t::GetDiscountedPrice( ECurrency eCurrency, item_price_t unBasePrice, float fDiscountPercentage )
+{
+ Assert( fDiscountPercentage > 0.0f );
+ Assert( fDiscountPercentage < 100.0f );
+
+ //if the base price is zero, it should never be anything but zero
+ if( unBasePrice == 0 )
+ return unBasePrice;
+
+ item_price_t unNewPrice = ( item_price_t )( ( double )unBasePrice * ( clamp( 100.0 - fDiscountPercentage, 0.0, 100.0 ) / 100.0 ) );
+
+ //determine what the unit of granularity we want to use for pricing. For example if you use 1, we keep 1/100th level precision, if you use 100 we'll round it to the nearest 100th (in USD this would be round to
+ //the nearest dollar, etc)
+ item_price_t unPriceGranularity = 1;
+ switch ( eCurrency )
+ {
+ case k_ECurrencyRUB: unPriceGranularity = 100; break;
+ case k_ECurrencyJPY: unPriceGranularity = 100; break;
+ case k_ECurrencyNOK: unPriceGranularity = 100; break;
+ case k_ECurrencyPHP: unPriceGranularity = 100; break;
+ case k_ECurrencyTHB: unPriceGranularity = 100; break;
+ case k_ECurrencyKRW: unPriceGranularity = 1000; break;
+ case k_ECurrencyUAH: unPriceGranularity = 10; break;
+ case k_ECurrencyIDR: unPriceGranularity = 100; break;
+ case k_ECurrencyVND: unPriceGranularity = 1000; break;
+ case k_ECurrencyCNY: unPriceGranularity = 100; break;
+ case k_ECurrencyTWD: unPriceGranularity = 100; break;
+ case k_ECurrencyINR: unPriceGranularity = 100; break;
+ case k_ECurrencyCOP: unPriceGranularity = 100; break;
+ case k_ECurrencyCLP: unPriceGranularity = 100; break;
+ }
+
+
+ //now handle the rounding to the specified price granularity
+ if( unPriceGranularity > 1 )
+ {
+ const item_price_t unRemainder = unNewPrice % unPriceGranularity;
+ //make sure to never let the price go to zero though
+ if( ( unRemainder >= unPriceGranularity / 2 ) || ( unNewPrice < unPriceGranularity ) )
+ {
+ //round up
+ unNewPrice += unPriceGranularity - unRemainder;
+ }
+ else
+ {
+ //round down
+ unNewPrice -= unRemainder;
+ }
+
+ //sanity check
+ Assert( ( unNewPrice % unPriceGranularity == 0 ) && ( unNewPrice > 0 ) );
+ }
+
+ return unNewPrice;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: This will calculate what the sale price will be for a particular
+// item -- the result should be non-zero, but this does not mean the item is
+// currently on sale. You can optionally pass in a pointer to a uint32 which
+// will get you the actual discount percentage, which can be different for
+// NXP, which has its own nonlinear pricing structure.
+//-----------------------------------------------------------------------------
+/*static*/ item_price_t econ_store_entry_t::CalculateSalePrice( const econ_store_entry_t* pSaleStoreEntry, ECurrency eCurrency, float fDiscountPercentage, int32 *out_pAdjustedDiscountPercentage/*=NULL*/ )
+{
+ item_price_t unSalePrice = 0;
+/*
+ // TF2 doesn't support NXP or RMB yet
+ if ( eCurrency == k_ECurrencyNXP || eCurrency == k_ECurrencyRMB )
+ {
+ // For these currencies, we calculate the sale price based on the discount percentage times the *USD* base price -- rather than the discount percentage
+ // times the base price for the given currency.
+ const item_price_t unSalePrice_USD = econ_store_entry_t::GetDiscountedPrice( k_ECurrencyUSD, pSaleStoreEntry->GetBasePrice( k_ECurrencyUSD ), fDiscountPercentage );
+ unSalePrice = ( eCurrency == k_ECurrencyNXP ) ? ConvertUSDToNXP( unSalePrice_USD ) : ConvertUSDToRMB( unSalePrice_USD );
+
+ // Ensure that the sale price is strictly less
+ Assert( unSalePrice < pSaleStoreEntry->GetBasePrice( eCurrency ) );
+ if ( unSalePrice >= pSaleStoreEntry->GetBasePrice( eCurrency ) )
+ {
+ unSalePrice = pSaleStoreEntry->GetBasePrice( eCurrency );
+ }
+ }
+ else
+*/
+ {
+ unSalePrice = econ_store_entry_t::GetDiscountedPrice( eCurrency, pSaleStoreEntry->GetBasePrice( eCurrency ), fDiscountPercentage );
+ }
+
+ Assert( unSalePrice > 0 );
+
+ // Also set a percentage per currency, since they can be different. RMB and NXP, for example,
+ // calculate a sale price based on the USD sale price.
+ const bool bUseDefaultDiscountPercentage = ( eCurrency == k_ECurrencyUSD );
+ const double fActualPercent = 100.0 * ( 1.0 - ( double )unSalePrice / ( double )pSaleStoreEntry->GetBasePrice( eCurrency ) );
+
+ const int32 nDiscountPercentageForCurrency = bUseDefaultDiscountPercentage ?
+ RoundFloatToInt( fDiscountPercentage ) :
+ RoundFloatToInt( fActualPercent );
+ AssertMsg( nDiscountPercentageForCurrency >= 0 && nDiscountPercentageForCurrency < 100, "Invalid discount percentage of %u specified for item %u currency %u", nDiscountPercentageForCurrency, pSaleStoreEntry->GetItemDefinitionIndex(), eCurrency );
+
+ if ( out_pAdjustedDiscountPercentage )
+ {
+ *out_pAdjustedDiscountPercentage = nDiscountPercentageForCurrency;
+ }
+
+ return unSalePrice;
+}
+
+//----------------------------------------------------------------------------
+// Purpose: Sort Entries by Name
+//----------------------------------------------------------------------------
+int ItemNameSortComparator( const econ_store_entry_t *const *ppEntryA, const econ_store_entry_t *const *ppEntryB )
+{
+ Assert( ppEntryA );
+ Assert( ppEntryB );
+ Assert( *ppEntryA );
+ Assert( *ppEntryB );
+
+ const CEconItemDefinition *pItemDefA = GetItemSchema()->GetItemDefinition( (*ppEntryA)->GetItemDefinitionIndex() );
+ const CEconItemDefinition *pItemDefB = GetItemSchema()->GetItemDefinition( (*ppEntryB)->GetItemDefinitionIndex() );
+
+ int nComp = Q_stricmp( pItemDefA->GetItemTypeName(), pItemDefB->GetItemTypeName() );
+ if ( nComp != 0 )
+ return nComp;
+
+ // If the type matches, then sort by the item name
+ return Q_stricmp( pItemDefA->GetItemBaseName(), pItemDefB->GetItemBaseName() );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Sort by whatever sort type our price sheet is requesting
+//-----------------------------------------------------------------------------
+int FirstSaleDateSortComparator( const econ_store_entry_t *const *ppItemA, const econ_store_entry_t *const *ppItemB )
+{
+ Assert( ppItemA );
+ Assert( ppItemB );
+ Assert( *ppItemA );
+ Assert( *ppItemB );
+
+ const CEconItemDefinition *pItemDefA = GetItemSchema()->GetItemDefinition( (*ppItemA)->GetItemDefinitionIndex() );
+ const CEconItemDefinition *pItemDefB = GetItemSchema()->GetItemDefinition( (*ppItemB)->GetItemDefinitionIndex() );
+ Assert( pItemDefA );
+ Assert( pItemDefB );
+
+ const char *pDateAddedA = pItemDefA->GetFirstSaleDate();
+ const char *pDateAddedB = pItemDefB->GetFirstSaleDate();
+
+ return -strcmp( pDateAddedA, pDateAddedB );
+}
+
+bool CEconStoreEntryLess::Less( const uint16& lhs, const uint16& rhs, void *pContext )
+{
+ CEconItemSchema *pSchema = GetItemSchema();
+
+ CEconStorePriceSheet* pPriceSheet = (CEconStorePriceSheet*)pContext;
+ eEconStoreSortType sortType = pPriceSheet->GetEconStoreSortType();
+ const econ_store_entry_t *pItemA = pPriceSheet->GetEntry( lhs );
+ const econ_store_entry_t *pItemB = pPriceSheet->GetEntry( rhs );
+ Assert( pItemA );
+ Assert( pItemB );
+
+ CEconItemDefinition *pItemDefA = pSchema->GetItemDefinition( pItemA->GetItemDefinitionIndex() );
+ CEconItemDefinition *pItemDefB = pSchema->GetItemDefinition( pItemB->GetItemDefinitionIndex() );
+
+#ifdef CLIENT_DLL
+ ECurrency eCurrency = EconUI()->GetStorePanel()->GetCurrency();
+#else
+ ECurrency eCurrency = k_ECurrencyUSD;
+#endif
+
+ if ( pItemDefA && pItemDefB )
+ {
+ switch ( sortType )
+ {
+ case kEconStoreSortType_Price_HighestToLowest:
+ if ( pItemA->GetCurrentPrice( eCurrency ) == pItemB->GetCurrentPrice( eCurrency ) )
+ {
+ return Q_strcmp( pItemDefA->GetItemBaseName(), pItemDefB->GetItemBaseName() ) < 0;
+ }
+ return pItemA->GetCurrentPrice( eCurrency ) > pItemB->GetCurrentPrice( eCurrency );
+
+ case kEconStoreSortType_Price_LowestToHighest:
+ if ( pItemA->GetCurrentPrice( eCurrency ) == pItemB->GetCurrentPrice( eCurrency ) )
+ {
+ return Q_strcmp( pItemDefA->GetItemBaseName(), pItemDefB->GetItemBaseName() ) < 0;
+ }
+ return pItemA->GetCurrentPrice( eCurrency ) < pItemB->GetCurrentPrice( eCurrency );
+
+ case kEconStoreSortType_DevName_AToZ:
+ return Q_strcmp( pItemDefA->GetItemBaseName(), pItemDefB->GetItemBaseName() ) < 0;
+
+ case kEconStoreSortType_DevName_ZToA:
+ return Q_strcmp( pItemDefA->GetItemBaseName(), pItemDefB->GetItemBaseName() ) > 0;
+
+ case kEconStoreSortType_DateNewest:
+ case kEconStoreSortType_DateOldest:
+ {
+ int iSortResult = FirstSaleDateSortComparator( &pItemA, &pItemB );
+
+ if ( iSortResult < 0 )
+ return sortType == kEconStoreSortType_DateNewest;
+
+ if ( iSortResult > 0 )
+ return sortType == kEconStoreSortType_DateOldest;
+
+ // Intentionally fall through to sorting by localized name if our dates match.
+ }
+
+ case kEconStoreSortType_Name_AToZ:
+ case kEconStoreSortType_Name_ZToA:
+ if ( g_pVGuiLocalize )
+ {
+ wchar_t *wszItemNameA = g_pVGuiLocalize->Find( pItemDefA->GetItemBaseName() );
+ wchar_t *wszItemNameB = g_pVGuiLocalize->Find( pItemDefB->GetItemBaseName() );
+ if ( wszItemNameA && wszItemNameB )
+ {
+ // Note: locale-savvy string sorting uses wcscoll, not wcscmp
+ return sortType == kEconStoreSortType_Name_ZToA
+ ? wcscoll( wszItemNameA, wszItemNameB ) > 0 // only sort in reverse alphabetical order if asked to -- fall-through cases uses forward order
+ : wcscoll( wszItemNameA, wszItemNameB ) < 0;
+ }
+ }
+ break;
+
+ case kEconStoreSortType_ItemDefIndex:
+ return pItemDefA->GetDefinitionIndex() < pItemDefB->GetDefinitionIndex();
+
+ case kEconStoreSortType_ReverseItemDefIndex:
+ return pItemDefB->GetDefinitionIndex() < pItemDefA->GetDefinitionIndex();
+ }
+ }
+
+ // default, highest to lowest price
+ return pItemA->GetCurrentPrice( eCurrency ) > pItemB->GetCurrentPrice( eCurrency );
+}
+
+#ifdef CLIENT_DLL
+//-----------------------------------------------------------------------------
+// Purpose: return the CLocale name that works with setlocale()
+//-----------------------------------------------------------------------------
+const char *GetLanguageCLocaleName( ELanguage eLang )
+{
+ if ( eLang == k_Lang_None )
+ return "";
+
+#ifdef _WIN32
+ // table for Win32 is here: http://msdn.microsoft.com/en-us/library/hzz3tw78(v=VS.80).aspx
+ // shortname works except for chinese
+
+ switch ( eLang )
+ {
+ case k_Lang_Simplified_Chinese:
+ return "chs"; // or "chinese-simplified"
+ case k_Lang_Traditional_Chinese:
+ return "cht"; // or "chinese-traditional"
+ case k_Lang_Korean:
+ return "korean"; // steam likes "koreana" for the name for some reason.
+ case k_Lang_Brazilian:
+ return "ptb"; // "portuguese-brazil" - that string fails even though it's in the MS lang table; ptb does work.
+ default:
+ return GetLanguageShortName( eLang );
+ }
+
+#else
+ switch ( eLang )
+ {
+ case k_Lang_Simplified_Chinese:
+ case k_Lang_Traditional_Chinese:
+ return "zh_CN";
+ default:
+ ;
+ }
+
+ // ICU codes work on linux/osx
+ return GetLanguageICUName( eLang );
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Get an I/O stream into the right local/settings for printing money - so to speak
+//-----------------------------------------------------------------------------
+static void InitStreamLocale( std::wostringstream &stream, ELanguage eLang, uint32 nExpectedAmount, ECurrency eCurrencyCode )
+{
+ const char *pszLocale = GetLanguageCLocaleName( eLang );
+
+#ifdef _PS3
+ stream.imbue(std::locale(pszLocale)); // no exception for PS3
+#else
+ try
+ {
+ stream.imbue(std::locale(pszLocale));
+ }
+ catch (const std::exception &e)
+ {
+ Log( "stream::imbue() failed with locale: '%s', exception: %s\n", pszLocale, e.what() );
+ stream.imbue( std::locale("C") );
+ }
+#endif
+
+ // Don't display fractional rubles
+ // But if our amount is fractional, we should show it regardless
+ // hack hack - certain currencies should not show fractional symbol - see if we can wire this through from config at some point
+ if ( ( eCurrencyCode == k_ECurrencyRUB || eCurrencyCode == k_ECurrencyJPY || eCurrencyCode == k_ECurrencyIDR || eCurrencyCode == k_ECurrencyKRW )
+ && nExpectedAmount % 100 == 0 )
+ {
+ stream.precision( 0 );
+ stream.setf( std::ios_base::fixed );
+ }
+ else
+ {
+ stream.precision( 2 );
+ stream.setf( std::ios_base::fixed, std::ios_base::floatfield );
+ }
+}
+
+//
+// Partial Integration from \steam\main\src\common\amount.cpp
+//
+int MakeMoneyStringInternal( wchar_t *pchDest, uint32 nDest, item_price_t unPrice, ECurrency eCurrencyCode, ELanguage eLanguage )
+{
+ // Use the actual currency symbol with the local number formatting.
+ // assume local locale - should not be used from server to send to client
+ // without passing in a valid pszCLocale parameter.
+ std::wostringstream stream;
+ InitStreamLocale( stream, eLanguage, unPrice, eCurrencyCode );
+
+ stream << (unPrice/100.0);
+
+ const auto sAmount = stream.str();
+ const wchar_t *wszAmount = sAmount.c_str();
+
+ // this code will be used by the client. An old client might encounter a currency code it doesn't know about. Handle that.
+ if ( eCurrencyCode == k_ECurrencyInvalid )
+ {
+ // just return the value
+ return V_snwprintf( pchDest, nDest, L"%ls", wszAmount );
+ }
+
+ // select symbol
+ const char *pchSymbol = "";
+ switch( eCurrencyCode )
+ {
+ case k_ECurrencyUSD:
+ pchSymbol = "$";
+ break;
+
+ case k_ECurrencyGBP:
+ pchSymbol = "\xC2\xA3";
+ break;
+
+ case k_ECurrencyEUR:
+ pchSymbol = "\xE2\x82\xAC";
+ break;
+
+ case k_ECurrencyCHF:
+ pchSymbol = "CHF";
+ break;
+
+ case k_ECurrencyRUB:
+ pchSymbol = "\xD1\x80\xD1\x83\xD0\xB1"; // localized py6
+ break;
+
+ case k_ECurrencyBRL:
+ pchSymbol = "R$";
+ break;
+
+ case k_ECurrencyJPY:
+ pchSymbol = "\xC2\xA5";
+ break;
+
+ case k_ECurrencyIDR:
+ pchSymbol = "Rp";
+ break;
+
+ case k_ECurrencyMYR:
+ pchSymbol = "RM";
+ break;
+
+ case k_ECurrencyPHP:
+ pchSymbol = "\xE2\x82\xB1";
+ break;
+
+ case k_ECurrencySGD:
+ pchSymbol = "S$";
+ break;
+
+ case k_ECurrencyTHB:
+ pchSymbol = "\xE0\xB8\xBF";
+ break;
+
+ case k_ECurrencyVND:
+ pchSymbol = "\xE2\x82\xAB";
+ break;
+
+ case k_ECurrencyKRW:
+ pchSymbol = "\xe2\x82\xa9";
+ break;
+
+ case k_ECurrencyTRY:
+ pchSymbol = "TL";
+ break;
+
+ case k_ECurrencyUAH:
+ pchSymbol = "\xe2\x82\xb4";
+ break;
+
+ case k_ECurrencyMXN:
+ pchSymbol = "Mex$";
+ break;
+
+ case k_ECurrencyCAD:
+ pchSymbol = "C$";
+ break;
+
+ case k_ECurrencyAUD:
+ pchSymbol = "A$";
+ break;
+
+ case k_ECurrencyNZD:
+ pchSymbol = "NZ$";
+ break;
+
+ case k_ECurrencyNOK:
+ pchSymbol = "kr";
+ break;
+
+ case k_ECurrencyPLN:
+ pchSymbol = "z\xc5\x82";
+ break;
+
+ case k_ECurrencyCNY:
+ pchSymbol = "\xc2\xa5";
+ break;
+
+ case k_ECurrencyINR:
+ pchSymbol = "\xe2\x82\xb9";
+ break;
+
+ case k_ECurrencyCLP:
+ pchSymbol = "$"; // bugbug - prefix it with CLP?
+ break;
+
+ case k_ECurrencyPEN:
+ pchSymbol = "S/.";
+ break;
+
+ case k_ECurrencyCOP:
+ pchSymbol = "COL$";
+ break;
+
+ case k_ECurrencyZAR:
+ pchSymbol = "R";
+ break;
+
+ case k_ECurrencyHKD:
+ pchSymbol = "HK$";
+ break;
+
+ case k_ECurrencyTWD:
+ pchSymbol = "NT$";
+ break;
+
+ case k_ECurrencySAR:
+ pchSymbol = "SR";
+ break;
+
+ case k_ECurrencyAED:
+ pchSymbol = "DH";
+ break;
+
+ default:
+ AssertMsg( false, "Unknown currency code" );
+ pchSymbol = "$";
+ break;
+ }
+
+ // BEGIN HACK GAME CLIENT CHARACTER SET CONVERSION
+ wchar_t wsSymbol[ 16 ];
+ V_UTF8ToUnicode( pchSymbol, wsSymbol, ARRAYSIZE( wsSymbol ) );
+ // END HACK GAME CLIENT CHARACTER SET CONVERSION
+
+ bool bFirstSymbolThenAmount = true; // Whether to show "$5" or "5E"
+ bool bSpaceBetweenTokens = false; // Whether to render a space between tokens like "$5" has no space, but "5 pyb" has a space
+
+ switch ( eCurrencyCode )
+ {
+ case k_ECurrencyEUR:
+ case k_ECurrencyUAH:
+ bFirstSymbolThenAmount = false;
+ bSpaceBetweenTokens = false;
+ break;
+ case k_ECurrencyRUB:
+ case k_ECurrencyVND:
+ case k_ECurrencyNOK:
+ case k_ECurrencyTRY:
+ case k_ECurrencyPLN:
+ case k_ECurrencySAR:
+ case k_ECurrencyAED:
+ bFirstSymbolThenAmount = false;
+ bSpaceBetweenTokens = true;
+ break;
+ case k_ECurrencyIDR:
+ case k_ECurrencyMXN:
+ case k_ECurrencyCAD:
+ case k_ECurrencyAUD:
+ case k_ECurrencyNZD:
+ case k_ECurrencyPEN:
+ case k_ECurrencyCOP:
+ case k_ECurrencyZAR:
+ case k_ECurrencyHKD:
+ case k_ECurrencyTWD:
+ case k_ECurrencyKRW:
+ case k_ECurrencyCHF:
+ bFirstSymbolThenAmount = true;
+ bSpaceBetweenTokens = true;
+ default:
+ bFirstSymbolThenAmount = true;
+ bSpaceBetweenTokens = false;
+ }
+
+ return V_snwprintf( pchDest, nDest, L"%ls%ls%ls",
+ ( bFirstSymbolThenAmount ? wsSymbol : wszAmount ),
+ ( bSpaceBetweenTokens ? L" " : L"" ),
+ ( bFirstSymbolThenAmount ? wszAmount : wsSymbol ) );
+}
+
+static ELanguage GetStoreLanguage()
+{
+ if ( !engine )
+ return k_Lang_English;
+
+ char uilanguage[ 64 ];
+ uilanguage[0] = 0;
+ engine->GetUILanguage( uilanguage, sizeof( uilanguage ) );
+
+ return PchLanguageToELanguage( uilanguage );
+}
+
+void MakeMoneyString( wchar_t *pchDest, uint32 nDest, item_price_t unPrice, ECurrency eCurrencyCode )
+{
+ (void)MakeMoneyStringInternal( pchDest, nDest, unPrice, eCurrencyCode, GetStoreLanguage() );
+}
+#endif // CLIENT_DLL
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CEconStorePriceSheet::BItemExistsInPriceSheet( item_definition_index_t unDefIndex ) const
+{
+ CEconStoreCategoryManager *pCategoryManager = GEconStoreCategoryManager();
+ const int nCategoryCount = pCategoryManager->GetNumCategories();
+
+ for ( int i = 0; i < nCategoryCount; ++i )
+ {
+ const CEconStoreCategoryManager::StoreCategory_t *pCat = pCategoryManager->GetCategoryFromIndex( i );
+
+ // Intentionally not using CUtlSortVector<>::Find(), since it calls Less(), which is slow as shit for m_vecEntries.
+ FOR_EACH_VEC( pCat->m_vecEntries, j )
+ {
+ if ( pCat->m_vecEntries[j] == unDefIndex )
+ return true;
+ }
+ }
+
+ return false;
+}
+
+#ifdef CLIENT_DLL
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool ShouldUseNewStore()
+{
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+int GetStoreVersion()
+{
+ return 2;
+}
+#endif // CLIENT_DLL
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+const CEconStorePriceSheet *GetEconPriceSheet()
+{
+#ifdef GC_DLL
+ return GEconManager()->GetPriceSheet();
+#else
+ return EconUI() && EconUI()->GetStorePanel()
+ ? EconUI()->GetStorePanel()->GetPriceSheet()
+ : NULL;
+#endif
+}
+
diff --git a/game/shared/econ/econ_store.h b/game/shared/econ/econ_store.h
new file mode 100644
index 0000000..56cf599
--- /dev/null
+++ b/game/shared/econ/econ_store.h
@@ -0,0 +1,621 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Common objects and utilities related to the in-game item store
+//
+//=============================================================================
+
+#ifndef ECON_STORE_H
+#define ECON_STORE_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "UtlSortVector.h"
+#include "vstdlib/IKeyValuesSystem.h"
+#include "econ/econ_storecategory.h"
+#ifdef CLIENT_DLL
+#include "client_community_market.h"
+#endif // CLIENT_DLL
+//-----------------------------------------------------------------------------
+// Purpose: Error code enum for purchase messages
+//-----------------------------------------------------------------------------
+enum EPurchaseResult
+{
+ k_EPurchaseResultOK = 1, // Success
+ k_EPurchaseResultFail = 2, // Generic error
+ k_EPurchaseResultInvalidParam = 3, // Invalid parameter
+ k_EPurchaseResultInternalError = 4, // Internal error
+ k_EPurchaseResultNotApproved = 5, // Tried to finalize a transaction that has not yet been approved
+ k_EPurchaseResultAlreadyCommitted = 6, // Tried to finalize a transaction that has already been committed
+ k_EPurchaseResultUserNotLoggedIn = 7, // User is not logged into Steam
+ k_EPurchaseResultWrongCurrency = 8, // Microtransaction's currency does not match user's wallet currency
+ k_EPurchaseResultAccountError = 9, // User's account does not exist or is temporarily unavailable
+ k_EPurchaseResultInvalidItem = 10, // User is trying to purchase an item that doesn't exist or is not for sale
+ k_EPurchaseResultNotEnoughBackpackSpace = 11, // User did not have enough backpack space
+ k_EPurchaseResultLimitedQuantityItemsUnavailable = 12, // User tried to purchase limited-quantity items but there weren't enough left in stock
+
+ k_EPurchaseResultInsufficientFunds = 100, // User does not have wallet funds
+ k_EPurchaseResultTimedOut = 101, // Time limit for finalization has been exceeded
+ k_EPurchaseResultAcctDisabled = 102, // Steam account is disabled
+ k_EPurchaseResultAcctCannotPurchase = 103, // Steam account is not allowed to make a purchase
+ k_EMicroTxnResultFailedFraudChecks = 104, // Fraud checks inside of Steam failed
+
+ k_EPurchaseResultOldPriceSheet = 150, // Information on the purchase didn't match the current price sheet
+ k_EPurchaseResultTxnNotFound = 151 // Could not find the transaction specified
+};
+
+
+const char *PchNameFromEPurchaseResult( EPurchaseResult ePurchaseState );
+
+//-----------------------------------------------------------------------------
+// Purpose: State of a transaction
+//
+// WARNING: VALUES STORED IN DATABASE. DO NOT RENUMBER!!!
+//-----------------------------------------------------------------------------
+enum EPurchaseState
+{
+ k_EPurchaseStateInvalid = 0, // Invalid
+ k_EPurchaseStateInit = 1, // We have sent InitPurchase to Steam
+ k_EPurchaseStateWaitingForAuthorization = 2, // We have gotten initial authorization from Steam. Waiting for user to authorize.
+ k_EPurchaseStatePending = 3, // We are attempting to commit the transaction
+ k_EPurchaseStateComplete = 4, // The transaction was successful
+ k_EPurchaseStateFailed = 5, // The transaction failed
+ k_EPurchaseStateCanceled = 6, // The transaction was canceled
+ k_EPurchaseStateRefunded = 7, // The transaction was refunded
+ k_EPurchaseStateChargeback = 8, // The transaction was charged back
+ k_EPurchaseStateChargebackReversed = 9, // A chargeback has failed and we got the money
+ k_EPurchaseStateLast = k_EPurchaseStateChargebackReversed,
+};
+
+const char *PchNameFromEPurchaseState( EPurchaseState ePurchaseState );
+
+// DO NOT RENUMBER! These values are stored in the audit log table.
+enum EGCTransactionAuditReason
+{
+ k_EGCTransactionAudit_GCTransactionCompleted = 0, // The transaction completed successfully on the GC.
+ k_EGCTransactionAudit_GCTransactionInit = 1, // The transaction was initialized.
+ k_EGCTransactionAudit_GCTransactionPostInit = 2, // The result of attempting to initialize the transaction. This is where the SteamTxnID is set.
+ k_EGCTransactionAudit_GCTransactionFinalize = 3, // We have started to finalize the transaction (so probably set it to pending).
+ k_EGCTransactionAudit_GCTransactionFinalizeFailed = 4, // Our attempt to finalize the transaction failed for some reason (not due to a timeout).
+ k_EGCTransactionAudit_GCTransactionCanceled = 5, // The client requested that we cancel the transaction.
+ k_EGCTransactionAudit_SteamFailedMismatch = 6, // Steam failed the transaction but we did not.
+ k_EGCTransactionAudit_GCRemovePurchasedItems = 7, // We are attempting to remove the purchased items from their backpack (due to rollback or failure).
+ k_EGCTransactionAudit_GCTransactionInsert = 8, // We are inserting a transaction, usually one created by a web interface (i.e.: a cd-key operation) instead of a standard store interaction.
+ k_EGCTransactionAudit_GCTransactionCompletedPostChargeback = 9, // We thought this transaction had been charged back but Steam came back later and told us it was successful after all.
+
+ k_EGCTransactionAuditLast = k_EGCTransactionAudit_GCTransactionCompletedPostChargeback,
+};
+
+const char *PchNameFromEGCTransactionAuditReason( EGCTransactionAuditReason eAuditReason );
+
+enum EGCTransactionAuditInsertReason
+{
+ k_EGCTransactionAuditInsert_Invalid = 0, //
+ k_EGCTransactionAuditInsert_CDKey = 1, // A CD Key was used for this transaction.
+ k_EGCTransactionAuditInsert_SettlementNoMatch_Failed = 2, // We inserted a failed record during settlement to unblock the process.
+ k_EGCTransactionAuditInsert_SettlementNoMatch_Pending = 3, // We inserted a pending record during settlement to unblock the process.
+
+ k_EGCTransactionAuditInsertLast = k_EGCTransactionAuditInsert_SettlementNoMatch_Pending,
+};
+
+//-----------------------------------------------------------------------------
+// Purpose: Currencies we support
+//
+// WARNING: VALUES STORED IN DATABASE. DO NOT RENUMBER!!!
+// WARNING: THESE DON'T MATCH THE STEAM NUMERIC IDS!!! WE TALK USING CURRENCY
+// CODES LIKE "VND" AND IF YOU SAY "15" TERRIBLE THINGS WILL HAPPEN
+//-----------------------------------------------------------------------------
+enum ECurrency
+{
+ k_ECurrencyFirst = 0,
+ k_ECurrencyUSD = 0,
+ k_ECurrencyGBP = 1,
+ k_ECurrencyEUR = 2,
+ k_ECurrencyRUB = 3,
+ k_ECurrencyBRL = 4,
+ // space for Dota currencies
+ k_ECurrencyJPY = 8,
+ k_ECurrencyNOK = 9,
+ k_ECurrencyIDR = 10,
+ k_ECurrencyMYR = 11,
+ k_ECurrencyPHP = 12,
+ k_ECurrencySGD = 13,
+ k_ECurrencyTHB = 14,
+ k_ECurrencyVND = 15,
+ k_ECurrencyKRW = 16,
+ k_ECurrencyTRY = 17,
+ k_ECurrencyUAH = 18,
+ k_ECurrencyMXN = 19,
+ k_ECurrencyCAD = 20,
+ k_ECurrencyAUD = 21,
+ k_ECurrencyNZD = 22,
+ k_ECurrencyPLN = 23,
+ k_ECurrencyCHF = 24,
+ k_ECurrencyCNY = 25,
+ k_ECurrencyTWD = 26,
+ k_ECurrencyHKD = 27,
+ k_ECurrencyINR = 28,
+ k_ECurrencyAED = 29,
+ k_ECurrencySAR = 30,
+ k_ECurrencyZAR = 31,
+ k_ECurrencyCOP = 32,
+ k_ECurrencyPEN = 33,
+ k_ECurrencyCLP = 34,
+
+ // NOTE: Not actually the Maximum currency value, but the Terminator for the possible currency code range.
+ k_ECurrencyMax = 35,
+
+ // make this a big number so we can avoid having to move it when we add another currency type
+ k_ECurrencyInvalid = 255,
+ k_ECurrencyCDKeyTransaction = k_ECurrencyInvalid,
+};
+
+// Macro for looping across all valid currencies
+#define FOR_EACH_CURRENCY( _i ) for ( ECurrency _i = GetFirstValidCurrency(); _i != k_ECurrencyInvalid; _i = GetNextValidCurrency( _i ) )
+
+const char *PchNameFromECurrency( ECurrency eCurrency ); // NOTE: Defined with ENUMSTRINGS_START/ENUMSTRINGS_REVERSE macros
+ECurrency ECurrencyFromName( const char *pchName ); //
+
+inline bool BIsCurrencyValid( ECurrency eCurrency )
+{
+ switch ( eCurrency )
+ {
+ case k_ECurrencyUSD:
+ case k_ECurrencyGBP:
+ case k_ECurrencyEUR:
+ case k_ECurrencyRUB:
+ case k_ECurrencyBRL:
+ case k_ECurrencyJPY:
+ case k_ECurrencyNOK:
+ case k_ECurrencyIDR:
+ case k_ECurrencyMYR:
+ case k_ECurrencyPHP:
+ case k_ECurrencySGD:
+ case k_ECurrencyTHB:
+ case k_ECurrencyVND:
+ case k_ECurrencyKRW:
+ case k_ECurrencyTRY:
+ case k_ECurrencyUAH:
+ case k_ECurrencyMXN:
+ case k_ECurrencyCAD:
+ case k_ECurrencyAUD:
+ case k_ECurrencyNZD:
+ //case k_ECurrencyPLN:
+ case k_ECurrencyCHF:
+ case k_ECurrencyCNY:
+ case k_ECurrencyTWD:
+ case k_ECurrencyHKD:
+ case k_ECurrencyINR:
+ case k_ECurrencyAED:
+ case k_ECurrencySAR:
+ case k_ECurrencyZAR:
+ case k_ECurrencyCOP:
+ case k_ECurrencyPEN:
+ case k_ECurrencyCLP:
+ return true;
+ }
+
+ return false;
+}
+
+inline ECurrency GetFirstValidCurrency()
+{
+ for ( int i = k_ECurrencyFirst; i < k_ECurrencyMax; i++ )
+ {
+ if ( BIsCurrencyValid( (ECurrency)i ) )
+ return (ECurrency)i;
+ }
+ return k_ECurrencyInvalid;
+}
+
+inline ECurrency GetNextValidCurrency( ECurrency ePrevious )
+{
+ for ( int i = ePrevious + 1; i < k_ECurrencyMax; i++ )
+ {
+ if ( BIsCurrencyValid( (ECurrency)i ) )
+ return (ECurrency)i;
+ }
+ return k_ECurrencyInvalid;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Simple struct for pairing a sort type with a localization string
+//-----------------------------------------------------------------------------
+struct ItemSortTypeData_t
+{
+ const char *szSortDesc; // localization string
+ uint32 iSortType; // maps to the GC-specific sort value
+};
+
+// Description of a single item for sale
+struct econ_store_entry_t
+{
+ // Constructor
+ econ_store_entry_t()
+ : m_pchCategoryTags( NULL ),
+ m_unGiftSteamPackageID( 0 ),
+ m_bHighlighted( false )
+ {
+ V_memset( m_unBaseCosts, 0, sizeof(m_unBaseCosts) );
+ V_memset( m_unSaleCosts, 0, sizeof(m_unSaleCosts) );
+ }
+
+ void SetItemDefinitionIndex( item_definition_index_t usDefIndex );
+ item_definition_index_t GetItemDefinitionIndex() const { return m_usDefIndex; }
+
+ void InitCategoryTags( const char *pTags ); // Sets m_pchCategoryTags and initializes m_vecTagIds and m_fRentalPriceScale
+
+ bool IsListedInCategory( StoreCategoryID_t unID ) const; // Is this item listed in the given category?
+ bool IsListedInSubcategories( const CEconStoreCategoryManager::StoreCategory_t &Category ) const; // Is this item listed in one of Category's subcategories?
+ bool IsListedInCategoryOrSubcategories( const CEconStoreCategoryManager::StoreCategory_t &Category ) const; // Is this item listed in Category or one of Category's subcategories?
+
+ bool IsOnSale( ECurrency eCurrency ) const;
+ bool IsRentable() const;
+#ifdef CLIENT_DLL
+ bool HasDiscount( ECurrency eCurrency, item_price_t *out_punOptionalBasePrice ) const; // returns true if we're on sale or if we're a bundle with a discounted total price
+#endif // CLIENT_DLL
+ item_price_t GetCurrentPrice( ECurrency eCurrency ) const;
+ float GetRentalPriceScale() const;
+
+ uint32 GetGiftSteamPackageID() const { return m_unGiftSteamPackageID; }
+
+ // Helper function -- so we do this calculation in a single place.
+ static item_price_t GetDiscountedPrice( ECurrency eCurrency, item_price_t unBasePrice, float fDiscountPercentage );
+
+ static item_price_t CalculateSalePrice( const econ_store_entry_t* pSaleStoreEntry, ECurrency eCurrency, float fDiscountPercentage, int32 *out_pAdjustedDiscountPercentage = NULL );
+
+ item_price_t GetBasePrice( ECurrency eCurrency ) const
+ {
+ Assert( eCurrency >= k_ECurrencyFirst );
+ Assert( eCurrency < k_ECurrencyMax );
+ if ( !( eCurrency >= 0 && eCurrency < k_ECurrencyMax ) )
+ return 0;
+#ifdef CLIENT_DLL
+ if ( m_bIsMarketItem )
+ {
+ const client_market_data_t *pClientMarketData = GetClientMarketData( GetItemDefinitionIndex(), AE_UNIQUE );
+ if ( !pClientMarketData )
+ return 0;
+ return pClientMarketData->m_unLowestPrice;
+ }
+#endif
+ // Weird-looking pattern: we're making sure that the value we're about to return fits correctly
+ // into the variable we're about to put it into. We do this to avoid integer conversion problems,
+ // especially overflow (!) where someone changes one of the return type or the storage type but
+ // not the other.
+ Assert( (item_price_t)m_unBaseCosts[eCurrency] == m_unBaseCosts[eCurrency] );
+ return m_unBaseCosts[eCurrency];
+ }
+
+ item_price_t GetSalePrice( ECurrency eCurrency ) const
+ {
+ Assert( eCurrency >= k_ECurrencyFirst );
+ Assert( eCurrency < k_ECurrencyMax );
+ if ( !( eCurrency >= 0 && eCurrency < k_ECurrencyMax ) )
+ return 0;
+#ifdef CLIENT_DLL
+ if ( m_bIsMarketItem )
+ {
+ const client_market_data_t *pClientMarketData = GetClientMarketData( GetItemDefinitionIndex(), AE_UNIQUE );
+ if ( !pClientMarketData )
+ return 0;
+ return pClientMarketData->m_unLowestPrice;
+ }
+#endif
+ // Weird-looking pattern: we're making sure that the value we're about to return fits correctly
+ // into the variable we're about to put it into. We do this to avoid integer conversion problems,
+ // especially overflow (!) where someone changes one of the return type or the storage type but
+ // not the other.
+ Assert( (item_price_t)m_unSaleCosts[eCurrency] == m_unSaleCosts[eCurrency] );
+ return m_unSaleCosts[eCurrency];
+ }
+
+ uint16 GetQuantity() const
+ {
+ return m_usQuantity;
+ }
+
+ const char* GetDate() const
+ {
+ return m_strDate.Get();
+ }
+
+ bool CanPreview() const
+ {
+ // No previewing of new items or weapons.
+ return m_bPreviewAllowed;
+ }
+
+ void SetQuantity( uint16 usQuantity )
+ {
+ Assert( usQuantity > 0 );
+ m_usQuantity = usQuantity;
+ }
+
+ void ValidatePrice( ECurrency eCurrency, item_price_t unPrice );
+
+ void SetBasePrice( ECurrency eCurrency, item_price_t unPrice )
+ {
+ Assert( eCurrency >= k_ECurrencyFirst );
+ Assert( eCurrency < k_ECurrencyMax );
+ if ( !( eCurrency >= 0 && eCurrency < k_ECurrencyMax ) )
+ return;
+
+ ValidatePrice( eCurrency, unPrice );
+
+ m_unBaseCosts[eCurrency] = unPrice;
+ }
+
+ void SetSalePrice( ECurrency eCurrency, item_price_t unPrice )
+ {
+ Assert( eCurrency >= k_ECurrencyFirst );
+ Assert( eCurrency < k_ECurrencyMax );
+ if ( !( eCurrency >= 0 && eCurrency < k_ECurrencyMax ) )
+ return;
+
+ ValidatePrice( eCurrency, unPrice );
+
+ // It's legal to have a sale price of zero, meaninig "this item is not on sale" in this
+ // currency.
+ // Assert( unPrice > 0 );
+ m_unSaleCosts[eCurrency] = unPrice;
+ }
+
+ void SetSteamGiftPackageID( uint32 unGiftSteamPackageID )
+ {
+ m_unGiftSteamPackageID = unGiftSteamPackageID;
+ }
+
+ void SetDate( const char* pszDate )
+ {
+ m_strDate.Set( pszDate );
+ }
+
+
+ bool IsValidCategoryTagIndex( uint32 iIndex ) const
+ {
+ AssertMsg( m_vecCategoryTags.IsValidIndex( iIndex ), "Category tag index out of range." );
+ return m_vecCategoryTags.IsValidIndex( iIndex );
+ }
+
+ uint32 GetCategoryTagCount() const
+ {
+ return m_vecCategoryTags.Count();
+ }
+
+ const char *GetCategoryTagNameFromIndex( uint32 iIndex ) const
+ {
+ if ( !IsValidCategoryTagIndex( iIndex ) )
+ return NULL;
+
+ return m_vecCategoryTags[ iIndex ].m_strName;
+ }
+
+ StoreCategoryID_t GetCategoryTagIDFromIndex( uint32 iIndex ) const;
+
+ const char *GetCategoryTagString() const
+ {
+ return m_pchCategoryTags;
+ }
+
+ bool m_bLimited; // Item is a limited sale
+ bool m_bNew; // Item is new
+ bool m_bHighlighted; // Item is highlighted
+ CUtlString m_strDate; // Date Added
+ bool m_bSoldOut; // True if the item is sold out from the store (for example if the item is a ticket or another physical item)
+ bool m_bPreviewAllowed; // Is this item previewable?
+ bool m_bIsPackItem; // Is this item a pack item? Pack items are items which are not individually for sale, but are sold via a bundle known as a "pack bundle"
+
+ bool m_bIsMarketItem; // Is Market Item Link
+
+private:
+ item_definition_index_t m_usDefIndex; // DefIndex of the item
+
+ // Private data so that we can check in the accessor functions that the data fits before returning it.
+ item_price_t m_unBaseCosts[k_ECurrencyMax]; // Costs of the items indexed by ECurrency -- if the items are on sale, this will be the current sale price
+ item_price_t m_unSaleCosts[k_ECurrencyMax]; // Original costs of the items indexed by ECurrency -- if the items are on sale, this will be the pre-sale price
+ uint16 m_usQuantity; // Quantity sold in a single purchase (ie., dueling pistols come in stacks of five)
+ float m_fRentalPriceScale; // 100.0 or greater means "unavailable to rent"
+ uint32 m_unGiftSteamPackageID; // if non-zero, when this item is purchased (including inside bundles, etc.), grant a gift copy of this Steam package
+
+ struct CategoryTag_t
+ {
+ CUtlString m_strName; // Individual tag name, like "Weapons," "New," etc.
+ StoreCategoryID_t m_unID; // The category ID
+ };
+ CCopyableUtlVector< CategoryTag_t > m_vecCategoryTags; // Category tag data
+
+ const char *m_pchCategoryTags; // All tags - this string will something like: "New" or "Weapons+New" etc.
+};
+
+#ifdef GC_DLL
+struct econ_store_timed_sale_item_t
+{
+ item_definition_index_t m_unItemDef;
+ float m_fPricePercentage; // 100.0 = regular price; 50.0 = half price
+};
+
+struct econ_store_timed_sale_t
+{
+ bool m_bSaleCurrentlyActive; // set in ::UpdatePricesForTimedSales()
+ CUtlConstString m_sIdentifier; // can't point to memory in the base KV because we toss it afterwards
+ RTime32 m_SaleStartTime;
+ RTime32 m_SaleEndTime;
+ CUtlVector<econ_store_timed_sale_item_t> m_vecSaleItems;
+
+ // Work around protected default vector constructor.
+ econ_store_timed_sale_t() { }
+ econ_store_timed_sale_t( const econ_store_timed_sale_t& other )
+ : m_bSaleCurrentlyActive( other.m_bSaleCurrentlyActive )
+ , m_sIdentifier( other.m_sIdentifier )
+ , m_SaleStartTime( other.m_SaleStartTime )
+ , m_SaleEndTime( other.m_SaleEndTime )
+ {
+ m_vecSaleItems.CopyArray( other.m_vecSaleItems.Base(), other.m_vecSaleItems.Count() );
+ }
+};
+#endif // GC_DLL
+
+// Spend xxx amount of money, get a free item from the loot list
+struct store_promotion_spend_for_free_item_t
+{
+ const CEconItemDefinition *m_pItemDef;
+ item_price_t m_rgusPriceThreshold[k_ECurrencyMax]; // Price threshold to get an item from the loot list indexed by ECurrency
+};
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Class that represents what's currently for sale in TF
+//-----------------------------------------------------------------------------
+typedef enum
+{
+ kEconStoreSortType_Price_HighestToLowest = 0,
+ kEconStoreSortType_Price_LowestToHighest = 1,
+ kEconStoreSortType_DevName_AToZ = 2,
+ kEconStoreSortType_DevName_ZToA = 3,
+ kEconStoreSortType_Name_AToZ = 4,
+ kEconStoreSortType_Name_ZToA = 5,
+ kEconStoreSortType_ItemDefIndex = 6,
+ kEconStoreSortType_ReverseItemDefIndex = 7,
+ kEconStoreSortType_DateNewest = 8,
+ kEconStoreSortType_DateOldest = 9,
+} eEconStoreSortType;
+
+struct price_point_map_key_t
+{
+ item_price_t m_unPriceUSD;
+ ECurrency m_eCurrency;
+
+ static bool Less( const price_point_map_key_t& a, const price_point_map_key_t& b )
+ {
+ if ( a.m_eCurrency == b.m_eCurrency )
+ return a.m_unPriceUSD < b.m_unPriceUSD;
+
+ return a.m_eCurrency < b.m_eCurrency;
+ }
+};
+
+typedef CUtlMap<price_point_map_key_t, item_price_t> CurrencyPricePointMap_t;
+
+class CEconStorePriceSheet
+{
+public:
+ typedef CUtlMap<item_definition_index_t, econ_store_entry_t> StoreEntryMap_t;
+ typedef CUtlMap<const char *, float> RentalPriceScaleMap_t;
+ typedef CUtlVector<item_definition_index_t> FeaturedItems_t;
+
+ CEconStorePriceSheet();
+ ~CEconStorePriceSheet();
+
+ bool InitFromKV( KeyValues *pKVPrices );
+
+ // Gets or sets the version stamp. This is just a number the GC can use
+ // to know if the client is in sync without sending it down on every
+ // request.
+ RTime32 GetVersionStamp( void ) const { return m_RTimeVersionStamp; }
+ void SetVersionStamp( RTime32 stamp ) { m_RTimeVersionStamp = stamp; }
+
+ uint32 GetHashForAllItems() const { return m_unHashForAllItems; }
+
+ typedef CUtlMap<uint16, econ_store_entry_t> EconStoreEntryMap_t;
+ EconStoreEntryMap_t &GetEntries() { return m_mapEntries; }
+
+#ifdef GC_DLL
+ econ_store_entry_t *GetEntryWriteable( item_definition_index_t unDefIndex );
+#endif // GC_DLL
+
+ const StoreEntryMap_t &GetEntries() const { return m_mapEntries; }
+ const CEconStoreCategoryManager::StoreCategory_t *GetFeaturedItems( void ) { return &m_FeaturedItems; }
+ const econ_store_entry_t *GetEntry( item_definition_index_t usDefIndex ) const;
+
+ uint32 GetFeaturedItemIndex() const { return m_unFeaturedItemIndex; }
+ void SetFeaturedItemIndex( uint32 unIdx ) { m_unFeaturedItemIndex = unIdx; }
+
+ void SetEconStoreSortType( eEconStoreSortType eType ) { m_eEconStoreSortType = eType; }
+ eEconStoreSortType GetEconStoreSortType() { return m_eEconStoreSortType; }
+
+ const store_promotion_spend_for_free_item_t *GetStorePromotion_SpendForFreeItem() const { return &m_StorePromotionSpendForFreeItem; }
+ const CEconItemDefinition * GetStorePromotion_FirstTimePurchaseItem() const { return m_pStorePromotionFirstTimePurchaseItem; }
+ const CEconItemDefinition * GetStorePromotion_FirstTimeWebPurchaseItem() const { return m_pStorePromotionFirstTimeWebPurchaseItem; }
+
+ uint32 GetPreviewPeriod() const { return m_unPreviewPeriod; }
+ uint32 GetBonusDiscountPeriod() const { return m_unBonusDiscountPeriod; }
+ float GetPreviewPeriodDiscount() const { return m_flPreviewPeriodDiscount; }
+
+ bool BItemExistsInPriceSheet( item_definition_index_t unDefIndex ) const;
+
+ float GetRentalPriceScale( const char *pszCategory ) const
+ {
+ RentalPriceScaleMap_t::IndexType_t i = m_mapRentalPriceScales.Find( pszCategory );
+ if ( i == RentalPriceScaleMap_t::InvalidIndex() )
+ return 1.0f;
+
+ return m_mapRentalPriceScales[i];
+ }
+
+ KeyValues *GetRawData() const { return m_pKVRaw; }
+
+#ifdef GC_DLL
+ void UpdatePricesForTimedSales( const RTime32 curTime );
+ void DumpTimeSaleState( const RTime32 curTime ) const;
+#endif // GC_DLL
+
+#ifdef CLIENT_DLL
+ const FeaturedItems_t& GetFeaturedItems() const { return m_vecFeaturedItems; }
+#endif // CLIENT_DLL
+
+private:
+ bool BInitEntryFromKV( KeyValues *pKVEntry );
+#ifdef CLIENT_DLL
+ bool BInitMarketEntryFromKV( KeyValues *pKVEntry );
+#endif // CLIENT_DLL
+
+#ifdef GC_DLL
+ bool InitTimedSaleEntryFromKV( KeyValues *pKVTimedSaleEntry );
+ bool VerifyTimedSaleEntries();
+#endif // GC_DLL
+
+private:
+ void Clear();
+ uint32 CalculateHashFromItems() const;
+
+ KeyValues *m_pKVRaw;
+ RTime32 m_RTimeVersionStamp;
+ CEconStoreCategoryManager::StoreCategory_t m_FeaturedItems; // Special section, not a tab, kept outside m_vecContents
+ StoreEntryMap_t m_mapEntries;
+ RentalPriceScaleMap_t m_mapRentalPriceScales;
+ store_promotion_spend_for_free_item_t m_StorePromotionSpendForFreeItem;
+ CEconItemDefinition* m_pStorePromotionFirstTimePurchaseItem;
+ CEconItemDefinition* m_pStorePromotionFirstTimeWebPurchaseItem;
+
+#ifdef CLIENT_DLL
+ FeaturedItems_t m_vecFeaturedItems;
+#endif // CLIENT_DLL
+
+#ifdef GC_DLL
+ CUtlVector<econ_store_timed_sale_t> m_vecTimedSales;
+#endif // GC_DLL
+
+ // changes based on experiments
+ uint32 m_unFeaturedItemIndex;
+ eEconStoreSortType m_eEconStoreSortType;
+
+ uint32 m_unPreviewPeriod;
+ uint32 m_unBonusDiscountPeriod;
+ float m_flPreviewPeriodDiscount;
+ uint32 m_unHashForAllItems;
+
+ // price point lookup
+ CurrencyPricePointMap_t m_mapCurrencyPricePoints;
+};
+
+#ifdef CLIENT_DLL
+void MakeMoneyString( wchar_t *pchDest, uint32 nDest, item_price_t unPrice, ECurrency eCurrencyCode );
+
+bool ShouldUseNewStore();
+int GetStoreVersion();
+#endif // CLIENT_DLL
+
+const CEconStorePriceSheet *GetEconPriceSheet();
+
+#endif // ECON_STORE_H
diff --git a/game/shared/econ/econ_storecategory.cpp b/game/shared/econ/econ_storecategory.cpp
new file mode 100644
index 0000000..610f2da
--- /dev/null
+++ b/game/shared/econ/econ_storecategory.cpp
@@ -0,0 +1,267 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Base class for generating store meta data. Abstract methods need
+// to be overridden on a per-product basis.
+//
+//-------------------------------------------------------------------------------------------------------------------------------
+
+#include "cbase.h"
+#include "econ_storecategory.h"
+#include "econ_store.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+//-------------------------------------------------------------------------------------------------------------------------------
+
+/*static*/ StoreCategoryID_t CEconStoreCategoryManager::k_CategoryID_Invalid = (StoreCategoryID_t)0;
+/*static*/ StoreCategoryID_t CEconStoreCategoryManager::k_CategoryID_New = CEconStoreCategoryManager::GetCategoryID( "New" );
+/*static*/ StoreCategoryID_t CEconStoreCategoryManager::k_CategoryID_Weapons = CEconStoreCategoryManager::GetCategoryID( "Weapons" );
+/*static*/ StoreCategoryID_t CEconStoreCategoryManager::k_CategoryID_Limited = CEconStoreCategoryManager::GetCategoryID( "Limited" );
+/*static*/ StoreCategoryID_t CEconStoreCategoryManager::k_CategoryID_Maps = CEconStoreCategoryManager::GetCategoryID( "Maps" );
+/*static*/ StoreCategoryID_t CEconStoreCategoryManager::k_CategoryID_Cosmetics = CEconStoreCategoryManager::GetCategoryID( "Cosmetics" );
+/*static*/ StoreCategoryID_t CEconStoreCategoryManager::k_CategoryID_Taunts = CEconStoreCategoryManager::GetCategoryID( "Taunts" );
+/*static*/ StoreCategoryID_t CEconStoreCategoryManager::k_CategoryID_Tools = CEconStoreCategoryManager::GetCategoryID( "Tools" );
+/*static*/ StoreCategoryID_t CEconStoreCategoryManager::k_CategoryID_Bundles = CEconStoreCategoryManager::GetCategoryID( "Bundles" );
+/*static*/ StoreCategoryID_t CEconStoreCategoryManager::k_CategoryID_Collections= CEconStoreCategoryManager::GetCategoryID( "Collections" );
+/*static*/ StoreCategoryID_t CEconStoreCategoryManager::k_CategoryID_Popular = CEconStoreCategoryManager::GetCategoryID( "Popular" );
+/*static*/ StoreCategoryID_t CEconStoreCategoryManager::k_CategoryID_OnSale = CEconStoreCategoryManager::GetCategoryID( "OnSale" );
+/*static*/ StoreCategoryID_t CEconStoreCategoryManager::k_CategoryID_Featured = CEconStoreCategoryManager::GetCategoryID( "Featured" );
+/*static*/ StoreCategoryID_t CEconStoreCategoryManager::k_CategoryID_ClassBundles = CEconStoreCategoryManager::GetCategoryID( "Class_Bundles" );
+/*static*/ StoreCategoryID_t CEconStoreCategoryManager::k_CategoryID_Highlighted = CEconStoreCategoryManager::GetCategoryID( "Highlighted" );
+
+
+//-------------------------------------------------------------------------------------------------------------------------------
+
+CEconStoreCategoryManager::CEconStoreCategoryManager()
+{
+ m_unHomeCategoryID = k_CategoryID_Invalid;
+}
+
+bool CEconStoreCategoryManager::BInit( CEconStorePriceSheet *pPriceSheet, KeyValues *pStoreMetaDataKV )
+{
+ KeyValues *pCategoriesKV = pStoreMetaDataKV->FindKey( "categories" );
+ if ( !pCategoriesKV )
+ {
+ AssertMsg( 0, "Could not find 'categories' subkey!" );
+ return false;
+ }
+
+ FOR_EACH_TRUE_SUBKEY( pCategoriesKV, pKVCurCategory )
+ {
+ const int iIndex = m_vecCategories.AddToTail();
+ StoreCategory_t &curCategory = m_vecCategories[ iIndex ];
+ if ( !BInitCategory( pPriceSheet, &curCategory, pKVCurCategory ) )
+ return false;
+
+ // If the current category is the home page, cache off its ID
+ if ( curCategory.m_bIsHome )
+ {
+ m_unHomeCategoryID = curCategory.m_unID;
+ }
+ }
+
+ // Verify that any parents point to valid categories
+ FOR_EACH_VEC( m_vecCategories, i )
+ {
+ const StoreCategory_t &curCategory = m_vecCategories[i];
+
+ // Skip current category if it refers to invalid, which is fine
+ if ( curCategory.m_unParentCategoryID == k_CategoryID_Invalid )
+ continue;
+
+ // A category can't be a parent to itself
+ if ( curCategory.m_unID == curCategory.m_unParentCategoryID )
+ {
+ AssertMsg( 0, "Store category %s is using itself as a parent category!", curCategory.m_pchName );
+ }
+
+ // Attempt to find the current section's parent ID
+ bool bFound = false;
+ FOR_EACH_VEC( m_vecCategories, j )
+ {
+ // Don't compare against self
+ if ( i == j )
+ continue;
+
+ if ( m_vecCategories[j].m_unID == curCategory.m_unParentCategoryID )
+ {
+ bFound = true;
+ break;
+ }
+ }
+
+ // If we couldn't find the current section's parent ID, assert
+ if ( !bFound )
+ {
+ AssertMsg( 0, "Category %s refers to an unknown parent category - check your spelling!", curCategory.m_pchName );
+ }
+ }
+
+ // Setup child category lists - looping twice to keep this code clean and easy to read
+ FOR_EACH_VEC( m_vecCategories, i )
+ {
+ const StoreCategory_t &curCategory = m_vecCategories[i];
+
+ if ( k_CategoryID_Invalid == curCategory.m_unParentCategoryID )
+ continue;
+
+ StoreCategory_t *pParentCategory = GetStoreCategoryFromID( curCategory.m_unParentCategoryID );
+ if ( !pParentCategory )
+ continue;
+
+ pParentCategory->m_vecSubcategories.AddToTail( &curCategory );
+ }
+
+ return true;
+}
+
+//-------------------------------------------------------------------------------------------------------------------------------
+
+bool CEconStoreCategoryManager::BInitCategory( CEconStorePriceSheet *pPriceSheet, StoreCategory_t *pCategory, KeyValues* pKVTab )
+{
+ const char *pDefaultResFile = "Resource/UI/econ/store/v2/StorePage.res";
+
+ // Get the main category name
+ const char* pCategoryName = pKVTab->GetName();
+ if ( !pCategoryName || !pCategoryName[0] )
+ {
+ AssertMsg( 0, "Invalid category name!" );
+ return false;
+ }
+
+ const bool bIsHome = pKVTab->GetBool( "home", false );
+ pCategory->m_bIsHome = bIsHome;
+
+ pCategory->m_pchRawName = pCategoryName;
+ pCategory->m_unID = GetCategoryID( pCategoryName );
+
+ pCategory->m_bUseLargeCells = pKVTab->GetBool( "use_large_cells", false );
+ pCategory->m_bVisible = pKVTab->GetBool( "visible", true );
+ pCategory->m_bInGameOnly = pKVTab->GetBool( "ingame_only", false );
+ pCategory->m_bDefaultTab = pKVTab->GetBool( "default", false );
+
+#if defined( GC_DLL )
+ // Until we replace the in-game store with the web store, we have this hacky override property, so
+ // that we can call the home page "HOME" in the game client and "TOP SELLERS" on the web. The home page
+ // will be evolving shortly to include a lot more than just a list of top sellers.
+ const char *pchLabelTokenWebOverride = pKVTab->GetString( "web_label_token_override", NULL );
+#else
+ const char *pchLabelTokenWebOverride = NULL;
+#endif
+ pCategory->m_pchName = pchLabelTokenWebOverride ? pchLabelTokenWebOverride : pKVTab->GetString( "label_token", "#Store_Unknown" );
+
+ pCategory->m_pchPageClass = pKVTab->GetString( "page_class", "CStorePage" );
+ pCategory->m_pchPageRes = pKVTab->GetString( "page_res", pDefaultResFile );
+ pCategory->m_pchSortType = pKVTab->GetString( "sort_type", "" );
+
+ // Important for web store but not needed for VGUI store
+#if defined( GC_DLL )
+ if ( !bIsHome )
+ {
+ const char *pchDropdownPrefabName = pKVTab->GetString( "dropdown_prefab", NULL );
+ pCategory->m_pDropdownPrefab = GEconStoreMetaData()->FindDropdownPrefab( pchDropdownPrefabName );
+ if ( !pCategory->m_pDropdownPrefab )
+ {
+ AssertMsg( pCategory->m_pDropdownPrefab, CFmtStr( "Invalid dropdown prefab name, '%s'!", pchDropdownPrefabName ).Access() );
+ return false;
+ }
+ }
+ else
+ {
+ pCategory->m_pDropdownPrefab = NULL;
+ }
+#endif
+
+ // Look for a parent category for non-home categories
+ if ( !bIsHome )
+ {
+ const char *pParentCategoryName = pKVTab->GetString( "parent", NULL );
+ if ( pParentCategoryName )
+ {
+ pCategory->m_unParentCategoryID = GetCategoryID( pParentCategoryName );
+ }
+ else
+ {
+ pCategory->m_unParentCategoryID = k_CategoryID_Invalid;
+ }
+ }
+
+ return true;
+}
+
+bool CEconStoreCategoryManager::BOnPriceSheetLoaded( CEconStorePriceSheet *pPriceSheet )
+{
+ // Go through all categories/subcategories and add a list of items to each.
+ // If an item belongs to a subcategory, it will also be added to its parent category. For example,
+ // a hat will be added to both the "hats" subcategory and the "items" parent category.
+ FOR_EACH_VEC( m_vecCategories, iCategory )
+ {
+ StoreCategory_t &Category = m_vecCategories[iCategory];
+
+ // find all entries that match
+ const CEconStorePriceSheet::EconStoreEntryMap_t &mapEntries = pPriceSheet->GetEntries();
+ FOR_EACH_MAP_FAST( mapEntries, idx )
+ {
+ const econ_store_entry_t &entry = mapEntries[idx];
+
+ if ( entry.IsListedInCategoryOrSubcategories( Category ) )
+ {
+ Category.m_vecEntries.InsertNoSort( entry.GetItemDefinitionIndex() );
+ }
+ }
+ }
+
+ return true;
+}
+
+//-------------------------------------------------------------------------------------------------------------------------------
+
+/*static*/ StoreCategoryID_t CEconStoreCategoryManager::GetCategoryID( const char *pCategoryName )
+{
+ // Make the input lower case
+ CUtlString strLowerCase = pCategoryName;
+ strLowerCase.ToLower();
+
+ return CRC32_ProcessSingleBuffer( (void*)strLowerCase.Get(), strLowerCase.Length() );
+}
+
+//-------------------------------------------------------------------------------------------------------------------------------
+
+const CEconStoreCategoryManager::StoreCategory_t *CEconStoreCategoryManager::GetStoreCategoryFromID( StoreCategoryID_t unID ) const
+{
+ return const_cast< CEconStoreCategoryManager * >( this )->GetStoreCategoryFromID( unID );
+}
+
+CEconStoreCategoryManager::StoreCategory_t *CEconStoreCategoryManager::GetStoreCategoryFromID( StoreCategoryID_t unID )
+{
+ if ( k_CategoryID_Invalid != unID )
+ {
+ FOR_EACH_VEC( m_vecCategories, i )
+ {
+ if ( unID == m_vecCategories[i].m_unID )
+ return &m_vecCategories[i];
+ }
+ }
+
+ return NULL;
+}
+
+//-------------------------------------------------------------------------------------------------------------------------------
+
+static CEconStoreCategoryManager *gs_pEconStoreCategoryManager = NULL;
+CEconStoreCategoryManager *GEconStoreCategoryManager()
+{
+ if ( !gs_pEconStoreCategoryManager )
+ {
+ gs_pEconStoreCategoryManager = new CEconStoreCategoryManager();
+ }
+
+ return gs_pEconStoreCategoryManager;
+}
+
+void ClearEconStoreCategoryManager()
+{
+ delete gs_pEconStoreCategoryManager;
+ gs_pEconStoreCategoryManager = NULL;
+} \ No newline at end of file
diff --git a/game/shared/econ/econ_storecategory.h b/game/shared/econ/econ_storecategory.h
new file mode 100644
index 0000000..a36e1fa
--- /dev/null
+++ b/game/shared/econ/econ_storecategory.h
@@ -0,0 +1,110 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//-------------------------------------------------------------------------------------------------------------------------------
+
+#ifndef ECON_STORECATEGORY_H
+#define ECON_STORECATEGORY_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#if defined( GC_DLL )
+#include "econ/econ_storemetadata.h"
+#endif
+
+//-------------------------------------------------------------------------------------------------------------------------------
+
+typedef CRC32_t StoreCategoryID_t;
+
+//-------------------------------------------------------------------------------------------------------------------------------
+
+// sort by price highest to lowest
+class CEconStoreEntryLess
+{
+public:
+ bool Less( const uint16& lhs, const uint16& rhs, void *pContext );
+};
+
+//-------------------------------------------------------------------------------------------------------------------------------
+
+class CEconStoreCategoryManager
+{
+public:
+ CEconStoreCategoryManager();
+
+ bool BInit( CEconStorePriceSheet *pPriceSheet, KeyValues *pStoreMetaDataKV );
+ bool BOnPriceSheetLoaded( CEconStorePriceSheet *pPriceSheet );
+
+ static StoreCategoryID_t GetCategoryID( const char *pCategoryName );
+
+ static StoreCategoryID_t k_CategoryID_Invalid;
+ static StoreCategoryID_t k_CategoryID_New;
+ static StoreCategoryID_t k_CategoryID_Weapons;
+ static StoreCategoryID_t k_CategoryID_Limited;
+ static StoreCategoryID_t k_CategoryID_Maps;
+ static StoreCategoryID_t k_CategoryID_Cosmetics;
+ static StoreCategoryID_t k_CategoryID_Taunts;
+ static StoreCategoryID_t k_CategoryID_Tools;
+ static StoreCategoryID_t k_CategoryID_Bundles;
+ static StoreCategoryID_t k_CategoryID_Collections;
+ static StoreCategoryID_t k_CategoryID_Popular;
+ static StoreCategoryID_t k_CategoryID_OnSale;
+ static StoreCategoryID_t k_CategoryID_Featured;
+ static StoreCategoryID_t k_CategoryID_ClassBundles;
+ static StoreCategoryID_t k_CategoryID_Highlighted;
+
+ struct StoreCategory_t
+ {
+ StoreCategory_t() { V_memset( this, 0, sizeof( StoreCategory_t ) ); }
+
+ int GetNumSubcategories() const { return m_vecSubcategories.Count(); }
+ bool HasSubcategories() const { return m_vecSubcategories.Count() > 1; }
+ bool BIsSubcategory() const { return m_unParentCategoryID != CEconStoreCategoryManager::k_CategoryID_Invalid; }
+
+ bool m_bIsHome:1; // Home page? Default=no.
+ bool m_bUseLargeCells:1; // Display large icons in the store. Default=no.
+ bool m_bDefaultTab:1; // Is this the default tab? Default=no
+ bool m_bVisible:1; // Should this tab be displayed in the store? Default=yes.
+ bool m_bInGameOnly:1; // Is this category only to be displayed in the in-game store (vs. the web store)?
+ const char *m_pchRawName; // Raw name of the tab
+ const char *m_pchName; // Name of the tab
+ const char *m_pchPageClass; // Code class of the store page.
+ const char *m_pchSortType; // How to sort the page.
+ const char *m_pchPageRes; // Res file to use for the page.
+ StoreCategoryID_t m_unID; // A unique ID that is stable across sessions
+ StoreCategoryID_t m_unParentCategoryID;
+ CUtlVector<const StoreCategory_t *> m_vecSubcategories; // A list of ID's for all subcategories
+ CUtlSortVector<uint16, CEconStoreEntryLess> m_vecEntries; // Vector of items for sale
+#if defined( GC_DLL )
+ const CEconStoreMetaData::DropdownPrefabInfo_t *m_pDropdownPrefab;
+#endif
+ };
+
+ const StoreCategoryID_t GetHomeCategoryID() const { Assert( m_unHomeCategoryID != k_CategoryID_Invalid ); return m_unHomeCategoryID; }
+
+ const StoreCategory_t *GetStoreCategoryFromID( StoreCategoryID_t unID ) const;
+ int GetNumCategories( void ) const { return m_vecCategories.Count(); }
+ const StoreCategory_t *GetCategoryFromIndex( int i ) const { Assert(i >= 0 && i < m_vecCategories.Count()); return &m_vecCategories[i]; }
+
+ StoreCategory_t *GetFeaturedItems() const { return NULL; }
+
+private:
+ bool BInitCategory( CEconStorePriceSheet *pPriceSheet, StoreCategory_t *pCategory, KeyValues *pKVTab );
+
+ StoreCategory_t *GetStoreCategoryFromID( StoreCategoryID_t unID );
+
+ CUtlVector< StoreCategory_t > m_vecCategories;
+
+ StoreCategoryID_t m_unHomeCategoryID; // The ID for the home tab
+};
+
+//-------------------------------------------------------------------------------------------------------------------------------
+
+CEconStoreCategoryManager *GEconStoreCategoryManager();
+void ClearEconStoreCategoryManager();
+
+//-------------------------------------------------------------------------------------------------------------------------------
+
+#endif \ No newline at end of file
diff --git a/game/shared/econ/econ_wearable.cpp b/game/shared/econ/econ_wearable.cpp
new file mode 100644
index 0000000..b7bea59
--- /dev/null
+++ b/game/shared/econ/econ_wearable.cpp
@@ -0,0 +1,860 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+
+#include "cbase.h"
+#include "econ_wearable.h"
+#include "vcollide_parse.h"
+
+#ifdef CLIENT_DLL
+#include "functionproxy.h"
+#include "c_te_effect_dispatch.h"
+#endif // CLIENT_DLL
+
+#ifdef TF_CLIENT_DLL
+#include "c_team.h"
+#include "tf_shareddefs.h"
+#include "tf_weapon_jar.h"
+#include "c_tf_player.h"
+#endif // TF_CLIENT_DLL
+
+#ifdef TF_DLL
+#include "tf_player.h"
+#endif // TF_DLL
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+LINK_ENTITY_TO_CLASS( wearable_item, CEconWearable );
+IMPLEMENT_NETWORKCLASS_ALIASED( EconWearable, DT_WearableItem )
+
+// Network Table --
+BEGIN_NETWORK_TABLE( CEconWearable, DT_WearableItem )
+END_NETWORK_TABLE()
+// -- Network Table
+
+// Data Desc --
+BEGIN_DATADESC( CEconWearable )
+END_DATADESC()
+// -- Data Desc
+
+PRECACHE_REGISTER( wearable_item );
+
+IMPLEMENT_NETWORKCLASS_ALIASED( TFWearableItem, DT_TFWearableItem )
+
+// Network Table --
+BEGIN_NETWORK_TABLE( CTFWearableItem, DT_TFWearableItem )
+END_NETWORK_TABLE()
+// -- Network Table
+
+// Data Desc --
+BEGIN_DATADESC( CTFWearableItem )
+END_DATADESC()
+// -- Data Desc
+
+//-----------------------------------------------------------------------------
+// SHARED CODE
+//-----------------------------------------------------------------------------
+
+CEconWearable::CEconWearable()
+{
+ m_bAlwaysAllow = false;
+};
+
+void CEconWearable::InternalSetPlayerDisplayModel( void )
+{
+ int iClass = 0;
+ int iTeam = 0;
+
+#if defined( TF_DLL ) || defined( TF_CLIENT_DLL )
+ CTFPlayer *pTFPlayer = ToTFPlayer( GetOwnerEntity() );
+ if ( pTFPlayer )
+ {
+ iClass = pTFPlayer->GetPlayerClass()->GetClassIndex();
+ iTeam = pTFPlayer->GetTeamNumber();
+ }
+#endif // defined( TF_DLL ) || defined( TF_CLIENT_DLL )
+
+ // Set our model to the player model
+ CEconItemView *pItem = GetAttributeContainer()->GetItem();
+ if ( pItem && pItem->IsValid() )
+ {
+ const char *pszPlayerDisplayModel = pItem->GetPlayerDisplayModel( iClass, iTeam );
+ if ( pszPlayerDisplayModel )
+ {
+ if ( pItem->GetStaticData()->IsContentStreamable() )
+ {
+ modelinfo->RegisterDynamicModel( pszPlayerDisplayModel, IsClient() );
+
+ if ( pItem->GetVisionFilteredDisplayModel() && pItem->GetVisionFilteredDisplayModel()[ 0 ] != '\0' )
+ {
+ modelinfo->RegisterDynamicModel( pItem->GetVisionFilteredDisplayModel(), IsClient() );
+ }
+ }
+ SetModel( pszPlayerDisplayModel );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Set up the item. GC data may not be available here depending on
+// where we're called from.
+//-----------------------------------------------------------------------------
+void CEconWearable::Spawn( void )
+{
+ InitializeAttributes();
+
+ Precache();
+
+ InternalSetPlayerDisplayModel();
+
+ BaseClass::Spawn();
+
+ AddEffects( EF_BONEMERGE );
+ AddEffects( EF_BONEMERGE_FASTCULL );
+
+#if !defined( CLIENT_DLL )
+ SetCollisionGroup( COLLISION_GROUP_WEAPON );
+ SetBlocksLOS( false );
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Player touches the item. Currently wearables don't appear in the
+// world, so this is only called directly during equipment assignment.
+//-----------------------------------------------------------------------------
+void CEconWearable::GiveTo( CBaseEntity *pOther )
+{
+ CBasePlayer *pPlayer = ToBasePlayer(pOther);
+ if ( !pPlayer )
+ return;
+
+#if !defined( CLIENT_DLL )
+ pPlayer->EquipWearable( this );
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CEconWearable::RemoveFrom( CBaseEntity *pOther )
+{
+ CBasePlayer *pPlayer = ToBasePlayer(pOther);
+ if ( !pPlayer )
+ return;
+
+#if !defined( CLIENT_DLL )
+ pPlayer->RemoveWearable( this );
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+int CEconWearable::GetSkin( void )
+{
+ CEconItemView *pItem = GetAttributeContainer()->GetItem(); // Safe. Checked in base class call.
+ if ( pItem )
+ {
+ int iSkin = pItem->GetSkin( GetTeamNumber() );
+ if ( iSkin > -1 )
+ {
+ return iSkin;
+ }
+ }
+
+ return ( GetTeamNumber() == (LAST_SHARED_TEAM+1) ) ? 0 : 1;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Attaches the item to the player.
+//-----------------------------------------------------------------------------
+void CEconWearable::Equip( CBasePlayer* pOwner )
+{
+ if ( !CanEquip( pOwner ) )
+ {
+ RemoveFrom( pOwner );
+ return;
+ }
+
+ SetTouch( NULL );
+ SetAbsVelocity( vec3_origin );
+
+ CBaseEntity *pFollowEntity = pOwner;
+
+ if ( IsViewModelWearable() )
+ {
+ pFollowEntity = pOwner->GetViewModel();
+ }
+
+ FollowEntity( pFollowEntity, true );
+
+ SetOwnerEntity( pOwner );
+
+ ReapplyProvision();
+
+ ChangeTeam( pOwner->GetTeamNumber() );
+ m_nSkin = GetSkin();
+
+#ifdef GAME_DLL
+ UpdateModelToClass();
+ UpdateBodygroups( pOwner, true );
+ PlayAnimForPlaybackEvent( WAP_ON_SPAWN );
+#endif
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Remove item from the player.
+//-----------------------------------------------------------------------------
+void CEconWearable::UnEquip( CBasePlayer* pOwner )
+{
+#ifdef CLIENT_DLL
+ SetParticleSystemsVisible( PARTICLE_SYSTEM_STATE_NOT_VISIBLE );
+#endif
+
+#ifdef GAME_DLL
+ UpdateBodygroups( pOwner, false );
+#endif
+
+ StopFollowingEntity();
+ SetOwnerEntity( NULL );
+
+ ReapplyProvision();
+}
+/*
+//-----------------------------------------------------------------------------
+// Purpose: Hides or shows masked bodygroups associated with this item.
+//-----------------------------------------------------------------------------
+bool CEconWearable::UpdateBodygroups( CBaseCombatCharacter* pOwner, int iState )
+{
+ if ( !pOwner )
+ return false;
+
+ CAttributeContainer *pCont = GetAttributeContainer();
+ if ( !pCont )
+ return false;
+
+ CEconItemView *pItem = pCont->GetItem();
+ if ( !pItem )
+ return false;
+
+ int iTeam = pOwner->GetTeamNumber();
+ int iNumBodyGroups = pItem->GetNumModifiedBodyGroups( iTeam );
+ for ( int i=0; i<iNumBodyGroups; ++i )
+ {
+ int iBody = 0;
+ const char *pszBodyGroup = pItem->GetModifiedBodyGroup( iTeam, i, iBody );
+ int iBodyGroup = pOwner->FindBodygroupByName( pszBodyGroup );
+
+ if ( iBodyGroup == -1 )
+ continue;
+
+ pOwner->SetBodygroup( iBodyGroup, iState );
+ }
+
+ return true;
+}
+*/
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CEconWearable::OnWearerDeath( void )
+{
+#ifdef CLIENT_DLL
+ UpdateParticleSystems();
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+int CEconWearable::GetDropType()
+{
+ CAttributeContainer *pCont = GetAttributeContainer();
+ if ( !pCont )
+ return 0;
+
+ CEconItemView *pItem = pCont->GetItem();
+ if ( pItem )
+ return pItem->GetDropType();
+ else
+ return 0;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Ensures that a player's correct body groups are enabled on client respawn.
+//-----------------------------------------------------------------------------
+void CEconWearable::UpdateWearableBodyGroups( CBasePlayer* pPlayer )
+{
+ if ( !pPlayer )
+ return;
+
+ for ( int i=0; i<pPlayer->GetNumWearables(); ++i )
+ {
+ CEconWearable* pItem = pPlayer->GetWearable(i);
+ if ( !pItem )
+ continue;
+
+ // Dynamic models which are not yet rendering do not modify bodygroups
+ if ( pItem->IsDynamicModelLoading() )
+ continue;
+
+ // On the client, ignore items that aren't valid.
+#ifdef TF_CLIENT_DLL
+ if ( pItem->EntityDeemedInvalid() )
+ continue;
+#endif
+
+ int nVisibleState = 1;
+#ifdef TF_CLIENT_DLL
+ if ( pItem->ShouldHideForVisionFilterFlags() )
+ {
+ // Items that shouldn't draw (pyro-vision filtered) shouldn't change any body group states
+ // unless they have no model (hatless hats)
+ nVisibleState = 0;
+ }
+#endif
+
+ pItem->UpdateBodygroups( pPlayer, nVisibleState );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CTFWearableItem::CTFWearableItem()
+{
+}
+
+//-----------------------------------------------------------------------------
+// SERVER ONLY CODE
+//-----------------------------------------------------------------------------
+
+#if defined( GAME_DLL )
+
+#endif
+
+//-----------------------------------------------------------------------------
+// CLIENT ONLY CODE
+//-----------------------------------------------------------------------------
+
+#if defined( CLIENT_DLL )
+
+//-----------------------------------------------------------------------------
+// Purpose: Mirror should draw logic.
+//-----------------------------------------------------------------------------
+ShadowType_t CEconWearable::ShadowCastType()
+{
+ if ( ShouldDraw() )
+ {
+ return SHADOWS_RENDER_TO_TEXTURE_DYNAMIC;
+ }
+
+ return SHADOWS_NONE;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CEconWearable::ShouldDraw( void )
+{
+ CBasePlayer *pPlayerOwner = ToBasePlayer( GetOwnerEntity() );
+ if ( !pPlayerOwner )
+ {
+ return false;
+ }
+
+ bool bUseViewModel = !pPlayerOwner->ShouldDrawThisPlayer();
+
+ // Don't show view models if we're drawing the real player, and don't show non view models if using view models.
+ if ( bUseViewModel )
+ {
+ // VM mode.
+ if ( !IsViewModelWearable() )
+ {
+ return false;
+ }
+ }
+ else
+ {
+ // Non-viewmodel mode.
+ if ( IsViewModelWearable() )
+ {
+ return false;
+ }
+ }
+
+ if ( !ShouldDrawWhenPlayerIsDead() && !pPlayerOwner->IsAlive() )
+ {
+ return false;
+ }
+
+ if ( pPlayerOwner->GetTeamNumber() == TEAM_SPECTATOR )
+ {
+ return false;
+ }
+
+ return BaseClass::ShouldDraw();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CEconWearable::OnDataChanged( DataUpdateType_t updateType )
+{
+ BaseClass::OnDataChanged( updateType );
+
+ if ( updateType == DATA_UPDATE_CREATED )
+ {
+ SetNextClientThink( CLIENT_THINK_ALWAYS );
+ }
+
+ // Update our visibility in case our parents' has changed.
+ UpdateVisibility();
+ UpdateParticleSystems();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CEconWearable::ClientThink( void )
+{
+ BaseClass::ClientThink();
+
+ UpdateParticleSystems();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CEconWearable::ShouldDrawParticleSystems( void )
+{
+ // Make sure the entity we're attaching to is being drawn
+ CBasePlayer *pPlayerOwner = ToBasePlayer( GetOwnerEntity() );
+ if ( !pPlayerOwner )
+ {
+ Assert ( "CEconWearable has no owner?" ); // Not sure what this means - is is visible or not?
+ return false;
+ }
+ if ( pPlayerOwner->ShouldDrawThisPlayer() )
+ {
+ return true;
+ }
+ return false;
+}
+
+RenderGroup_t CEconWearable::GetRenderGroup()
+{
+ if ( IsViewModelWearable() )
+ return RENDER_GROUP_VIEW_MODEL_TRANSLUCENT;
+
+ return BaseClass::GetRenderGroup();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Wearable tint colors
+//-----------------------------------------------------------------------------
+class CProxyItemTintColor : public CResultProxy
+{
+public:
+ void OnBind( void *pC_BaseEntity )
+ {
+ Assert( m_pResult );
+ Vector vResult = Vector( 0, 0, 0 );
+
+ if ( pC_BaseEntity )
+ {
+ CEconItemView *pScriptItem = NULL;
+
+ IClientRenderable *pRend = (IClientRenderable *)pC_BaseEntity;
+ C_BaseEntity *pEntity = pRend->GetIClientUnknown()->GetBaseEntity();
+ if ( pEntity )
+ {
+ CEconEntity *pItem = dynamic_cast< CEconEntity* >( pEntity );
+ if ( pItem )
+ {
+ pScriptItem = pItem->GetAttributeContainer()->GetItem();
+ }
+ else if ( pEntity->GetOwnerEntity() )
+ {
+ // Try the owner (for viewmodels, etc).
+ pEntity = pEntity->GetOwnerEntity();
+ pItem = dynamic_cast< CEconEntity* >( pEntity );
+ if ( pItem )
+ {
+ pScriptItem = pItem->GetAttributeContainer()->GetItem();
+ }
+ }
+ }
+ else
+ {
+ // Proxy data can be a script created item itself, if we're in a vgui CModelPanel
+ pScriptItem = dynamic_cast< CEconItemView* >( pRend );
+ }
+
+#ifdef TF_CLIENT_DLL
+ if ( !pScriptItem )
+ {
+ // Might be a throwable
+ CTFWeaponBaseGrenadeProj *pProjectile = dynamic_cast< CTFWeaponBaseGrenadeProj* >( pEntity );
+ if ( pProjectile )
+ {
+ CEconEntity *pItem = dynamic_cast< CEconEntity* >( pProjectile->GetLauncher() );
+ if ( pItem )
+ {
+ pScriptItem = pItem->GetAttributeContainer()->GetItem();
+ }
+ }
+ }
+
+ if ( pScriptItem && pScriptItem->IsValid() )
+ {
+ const bool bAltColor = pEntity && pEntity->GetTeam() > 0
+ ? pEntity->GetTeam()->GetTeamNumber() == TF_TEAM_BLUE
+ : pScriptItem->GetFlags() & kEconItemFlagClient_ForceBlueTeam
+ ? true
+ : false;
+
+ int iModifiedRGB = pScriptItem->GetModifiedRGBValue( bAltColor );
+ if ( iModifiedRGB )
+ {
+ // The attrib returns a packed RGB with values between 0 & 255 packed into the bottom 3 bytes.
+ Color clr = Color( ((iModifiedRGB & 0xFF0000) >> 16), ((iModifiedRGB & 0xFF00) >> 8), (iModifiedRGB & 0xFF) );
+
+ vResult.x = clamp( clr.r() * (1.f / 255.0f), 0.f, 1.0f );
+ vResult.y = clamp( clr.g() * (1.f / 255.0f), 0.f, 1.0f );
+ vResult.z = clamp( clr.b() * (1.f / 255.0f), 0.f, 1.0f );
+ }
+ }
+#endif // TF_CLIENT_DLL
+ }
+
+ m_pResult->SetVecValue( vResult.x, vResult.y, vResult.z );
+ }
+};
+EXPOSE_INTERFACE( CProxyItemTintColor, IMaterialProxy, "ItemTintColor" IMATERIAL_PROXY_INTERFACE_VERSION );
+
+
+
+//============================================================================================================================
+extern ConVar r_propsmaxdist;
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+C_EconWearableGib::C_EconWearableGib()
+{
+ m_fDeathTime = -1;
+ m_iHealth = 0;
+ m_bParented = false;
+ m_bDelayedInit = false;
+}
+
+C_EconWearableGib::~C_EconWearableGib()
+{
+ PhysCleanupFrictionSounds( this );
+ VPhysicsDestroyObject();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool C_EconWearableGib::Initialize( bool bWillBeParented )
+{
+ m_bParented = bWillBeParented;
+ return InitializeAsClientEntity( STRING( GetModelName() ), RENDER_GROUP_OPAQUE_ENTITY );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CStudioHdr* C_EconWearableGib::OnNewModel()
+{
+ CStudioHdr* pCStudioHdr = BaseClass::OnNewModel();
+ if ( m_bDelayedInit && !IsDynamicModelLoading() )
+ {
+ FinishModelInitialization();
+ }
+ return pCStudioHdr;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void C_EconWearableGib::SpawnClientEntity( void )
+{
+ if ( !IsDynamicModelLoading() )
+ {
+ FinishModelInitialization();
+ }
+ else
+ {
+ m_bDelayedInit = true;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool C_EconWearableGib::FinishModelInitialization( void )
+{
+ UpdateThinkState();
+
+ const model_t *mod = GetModel();
+ if ( mod )
+ {
+ Vector mins, maxs;
+ modelinfo->GetModelBounds( mod, mins, maxs );
+ SetCollisionBounds( mins, maxs );
+ }
+
+ if ( !m_bParented )
+ {
+ // Create the object in the physics system
+ solid_t tmpSolid;
+ if ( !PhysModelParseSolid( tmpSolid, this, GetModelIndex() ) )
+ {
+ DevMsg("C_EconWearableGib::FinishModelInitialization: PhysModelParseSolid failed for entity %i.\n", GetModelIndex() );
+ return false;
+ }
+ else
+ {
+ m_pPhysicsObject = VPhysicsInitNormal( SOLID_VPHYSICS, 0, false, &tmpSolid );
+
+ if ( !m_pPhysicsObject )
+ {
+ // failed to create a physics object
+ DevMsg(" C_EconWearableGib::FinishModelInitialization: VPhysicsInitNormal() failed for %s.\n", STRING(GetModelName()) );
+ return false;
+ }
+ }
+ }
+
+ Spawn();
+
+ if ( m_fadeMinDist < 0 )
+ {
+ // start fading out at 75% of r_propsmaxdist
+ m_fadeMaxDist = r_propsmaxdist.GetFloat();
+ m_fadeMinDist = r_propsmaxdist.GetFloat() * 0.75f;
+ }
+
+ SetCollisionGroup( COLLISION_GROUP_DEBRIS );
+
+ UpdatePartitionListEntry();
+
+ CollisionProp()->UpdatePartition();
+
+ SetBlocksLOS( false ); // this should be a small object
+
+ // Set up shadows; do it here so that objects can change shadowcasting state
+ CreateShadow();
+
+ UpdateVisibility();
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void C_EconWearableGib::Spawn()
+{
+ BaseClass::Spawn();
+ m_takedamage = DAMAGE_EVENTS_ONLY;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool C_EconWearableGib::ValidateEntityAttachedToPlayer( bool &bShouldRetry )
+{
+ bShouldRetry = false;
+
+ // Always valid as long as we're not parented to anything
+ return (GetMoveParent() == NULL);
+}
+
+#define WEARABLE_FADEOUT_TIME 1.0f
+
+//-----------------------------------------------------------------------------
+// Purpose: Figure out if we need to think or not
+//-----------------------------------------------------------------------------
+bool C_EconWearableGib::UpdateThinkState( void )
+{
+ if ( m_fDeathTime > 0 )
+ {
+ // If we're in the active fadeout portion, think rapidly. Otherwise, wait for that time.
+ if ( (m_fDeathTime - gpGlobals->curtime) > WEARABLE_FADEOUT_TIME )
+ {
+ SetNextClientThink( m_fDeathTime - WEARABLE_FADEOUT_TIME );
+ }
+ else
+ {
+ SetNextClientThink( CLIENT_THINK_ALWAYS );
+ }
+ return true;
+ }
+
+ SetNextClientThink( CLIENT_THINK_NEVER );
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void C_EconWearableGib::ClientThink( void )
+{
+ if ( (m_fDeathTime > 0) && ((m_fDeathTime - gpGlobals->curtime) <= WEARABLE_FADEOUT_TIME) )
+ {
+ if ( m_fDeathTime <= gpGlobals->curtime )
+ {
+ Release(); // Die
+ return;
+ }
+
+ // fade out
+ float alpha = (m_fDeathTime - gpGlobals->curtime) / WEARABLE_FADEOUT_TIME;
+ SetRenderMode( kRenderTransTexture );
+ SetRenderColorA( alpha * 256 );
+ }
+
+ UpdateThinkState();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void C_EconWearableGib::StartFadeOut( float fDelay )
+{
+ m_fDeathTime = gpGlobals->curtime + fDelay + WEARABLE_FADEOUT_TIME;
+ UpdateThinkState();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void C_EconWearableGib::ImpactTrace( trace_t *pTrace, int iDamageType, const char *pCustomImpactName )
+{
+ IPhysicsObject *pPhysicsObject = VPhysicsGetObject();
+
+ if( !pPhysicsObject )
+ return;
+
+ Vector dir = pTrace->endpos - pTrace->startpos;
+ int iDamage = 0;
+
+ if ( iDamageType & DMG_BLAST )
+ {
+ iDamage = VectorLength( dir );
+ dir *= 500; // adjust impact strenght
+
+ // apply force at object mass center
+ pPhysicsObject->ApplyForceCenter( dir );
+ }
+ else
+ {
+ Vector hitpos;
+
+ VectorMA( pTrace->startpos, pTrace->fraction, dir, hitpos );
+ VectorNormalize( dir );
+
+ // guess avg damage
+ if ( iDamageType == DMG_BULLET )
+ {
+ iDamage = 30;
+ }
+ else
+ {
+ iDamage = 50;
+ }
+
+ dir *= 4000; // adjust impact strenght
+
+ // apply force where we hit it
+ pPhysicsObject->ApplyForceOffset( dir, hitpos );
+ }
+}
+
+#if 0
+#ifdef _DEBUG
+#include "econ_item_system.h"
+
+static CUtlVector< const char * > s_possibleModels;
+static CUtlVector< const GameItemDefinition_t * > s_possibleDefinitions;
+void Dbg_TestDynamicWearableGibs( void )
+{
+ C_BasePlayer *pLocalPlayer = C_BasePlayer::GetLocalPlayer();
+ if ( !pLocalPlayer )
+ return;
+
+ C_EconWearableGib *pEntity = new C_EconWearableGib();
+ if ( !pEntity )
+ return;
+
+ Vector forward;
+ pLocalPlayer->EyeVectors( &forward );
+ trace_t tr;
+ UTIL_TraceLine( pLocalPlayer->EyePosition(), pLocalPlayer->EyePosition() + (forward * 256), MASK_NPCSOLID, pLocalPlayer, COLLISION_GROUP_NONE, &tr );
+
+ Vector position = tr.endpos;
+
+ if ( s_possibleModels.Count() == 0 )
+ {
+ FOR_EACH_MAP( ItemSystem()->GetItemSchema()->GetItemDefinitionMap(), nDefn )
+ {
+ const GameItemDefinition_t *pDefn = dynamic_cast<GameItemDefinition_t *>( ItemSystem()->GetItemSchema()->GetItemDefinitionMap()[nDefn] );
+ if ( !pDefn )
+ continue;
+
+ const char *pszModel = pDefn->GetPlayerDisplayModel( 0 );
+ if ( pszModel && pszModel[0] && pszModel[0] != '?' && pDefn->BLoadOnDemand() && pDefn->GetDropType() == ITEM_DROP_TYPE_DROP )
+ {
+ s_possibleModels.AddToTail( pszModel );
+ s_possibleDefinitions.AddToTail( pDefn );
+ }
+ }
+ }
+
+ Assert( s_possibleModels.Count() );
+
+ int spawnIndex = random->RandomInt( 0, s_possibleModels.Count() - 1 );
+ const char *pszModelName = s_possibleModels[ spawnIndex ];
+ const GameItemDefinition_t *pDefn = s_possibleDefinitions[ spawnIndex ];
+ Msg( "Spawning: %s\n", pszModelName );
+ pEntity->SetModelName( AllocPooledString( pszModelName ) );
+ pEntity->SetAbsOrigin( position );
+ pEntity->SetAbsAngles( vec3_angle );
+ pEntity->SetOwnerEntity( pLocalPlayer );
+ pEntity->ChangeTeam( pLocalPlayer->GetTeamNumber() ); // our gibs will match our team; this will probably not be used for anything besides team coloring
+ // Copy the script created item data over
+ pEntity->GetAttributeContainer()->GetItem()->Init( pDefn->GetDefinitionIndex(), pDefn->GetQuality(), pDefn->GetMinLevel(), true );
+
+ if ( !pEntity->Initialize( false ) )
+ {
+ pEntity->Release();
+ return;
+ }
+
+ pEntity->StartFadeOut( 15.0f );
+ return;
+
+ IPhysicsObject *pPhysicsObject = pEntity->VPhysicsGetObject();
+ if ( !pPhysicsObject )
+ {
+ pEntity->Release();
+ return;
+ }
+
+ // randomize velocity by 5%
+ Vector rndVel = Vector(0,0,100);
+ pPhysicsObject->AddVelocity( &rndVel, &vec3_origin );
+}
+static ConCommand dbg_testdynamicwearablegib( "dbg_testdynamicwearablegib", Dbg_TestDynamicWearableGibs, "", FCVAR_CHEAT );
+#endif // _DEBUG
+#endif
+
+#endif // client only
diff --git a/game/shared/econ/econ_wearable.h b/game/shared/econ/econ_wearable.h
new file mode 100644
index 0000000..6bdea85
--- /dev/null
+++ b/game/shared/econ/econ_wearable.h
@@ -0,0 +1,138 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+
+#ifndef ECON_WEARABLE_H
+#define ECON_WEARABLE_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "econ_entity.h"
+
+enum
+{
+ MAX_WEARABLES_SENT_FROM_SERVER =
+#ifdef LOADOUT_MAX_WEARABLES_COUNT // we actually do want to just check for macro definition here -- undefined means "fall back to whatever default"
+ LOADOUT_MAX_WEARABLES_COUNT
+#else
+ 8 // hard-coded constant to match old behavior
+#endif
+};
+
+#if defined( CLIENT_DLL )
+#define CEconWearable C_EconWearable
+#define CTFWearableItem C_TFWearableItem
+#endif
+
+enum
+{
+ ITEM_DROP_TYPE_NULL,
+ ITEM_DROP_TYPE_NONE,
+ ITEM_DROP_TYPE_DROP,
+ ITEM_DROP_TYPE_BREAK,
+};
+
+class CEconWearable : public CEconEntity
+{
+ DECLARE_CLASS( CEconWearable, CEconEntity );
+public:
+ DECLARE_NETWORKCLASS();
+ DECLARE_DATADESC();
+
+ CEconWearable();
+
+ virtual bool IsWearable( void ) const { return true; }
+
+ // Shared
+ virtual void Spawn( void );
+ virtual void GiveTo( CBaseEntity *pOther );
+ virtual void RemoveFrom( CBaseEntity *pOther );
+ virtual bool CanEquip( CBaseEntity *pOther ) { return true; }
+ virtual void Equip( CBasePlayer *pOwner );
+ virtual void UnEquip( CBasePlayer* pOwner );
+ virtual void OnWearerDeath( void );
+ virtual int GetDropType( void );
+// virtual bool UpdateBodygroups( CBasePlayer* pOwner, int iState );
+
+ void SetAlwaysAllow( bool bVal ) { m_bAlwaysAllow = bVal; }
+ bool AlwaysAllow( void ) { return m_bAlwaysAllow; }
+
+ virtual bool IsViewModelWearable( void ) { return false; }
+
+ // Server
+#if defined( GAME_DLL )
+#endif
+
+ // Client
+#if defined( CLIENT_DLL )
+ virtual ShadowType_t ShadowCastType() OVERRIDE;
+ virtual bool ShouldDraw();
+ virtual bool ShouldDrawWhenPlayerIsDead() { return true; }
+ virtual void OnDataChanged( DataUpdateType_t updateType );
+ virtual void ClientThink( void );
+ virtual bool ShouldDrawParticleSystems( void );
+ virtual RenderGroup_t GetRenderGroup();
+#endif
+
+ virtual int GetSkin( void );
+
+ // Static
+ static void UpdateWearableBodyGroups( CBasePlayer *pPlayer );
+
+protected:
+ virtual void InternalSetPlayerDisplayModel( void );
+
+private:
+ bool m_bAlwaysAllow; // Wearable will not be removed by ManageRegularWeapons. Only use this for wearables managed by other items!
+};
+
+//-----------------------------------------------------------------------------
+// Purpose: For backwards compatibility with older demos
+//-----------------------------------------------------------------------------
+class CTFWearableItem : public CEconWearable
+{
+ DECLARE_CLASS( CTFWearableItem, CEconWearable );
+public:
+ DECLARE_NETWORKCLASS();
+ DECLARE_DATADESC();
+
+ CTFWearableItem();
+};
+
+#ifdef CLIENT_DLL
+// Clientside wearable physics props. Used to have wearables fall off dying players.
+class C_EconWearableGib : public CEconEntity
+{
+ DECLARE_CLASS( C_EconWearableGib, CEconEntity );
+public:
+ C_EconWearableGib();
+ ~C_EconWearableGib();
+
+ bool Initialize( bool bWillBeParented );
+ bool FinishModelInitialization( void );
+
+ virtual CStudioHdr *OnNewModel( void );
+
+ virtual bool ValidateEntityAttachedToPlayer( bool &bShouldRetry );
+
+ virtual void SpawnClientEntity();
+ virtual void Spawn();
+ virtual void ClientThink( void );
+ void StartFadeOut( float fDelay );
+ virtual void ImpactTrace( trace_t *pTrace, int iDamageType, const char *pCustomImpactName );
+ virtual CollideType_t GetCollideType( void ) { return ENTITY_SHOULD_RESPOND; }
+
+ bool UpdateThinkState( void );
+
+private:
+ bool m_bParented;
+ bool m_bDelayedInit;
+ float m_fDeathTime; // Point at which this object self destructs.
+ // The default of -1 indicates the object shouldn't destruct.
+};
+#endif
+
+#endif // ECON_WEARABLE_H \ No newline at end of file
diff --git a/game/shared/econ/game_item_schema.h b/game/shared/econ/game_item_schema.h
new file mode 100644
index 0000000..1f1c61c
--- /dev/null
+++ b/game/shared/econ/game_item_schema.h
@@ -0,0 +1,49 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+#ifndef GAME_ITEM_SCHEMA_H
+#define GAME_ITEM_SCHEMA_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#if defined(TF_CLIENT_DLL) || defined(TF_DLL) || defined(TF_GC_DLL)
+// TF
+ class CTFItemSchema;
+ class CTFItemDefinition;
+ class CTFItemSystem;
+
+ typedef CTFItemSchema GameItemSchema_t;
+ typedef CTFItemDefinition GameItemDefinition_t;
+ typedef CTFItemSystem GameItemSystem_t;
+
+ #include "tf_item_schema.h"
+#elif defined( DOTA_CLIENT_DLL ) || defined( DOTA_DLL ) || defined ( DOTA_GC_DLL )
+// DOTA
+ class CDOTAItemSchema;
+ class CDOTAItemDefinition;
+ class CDOTAItemSystem;
+
+ typedef CDOTAItemSchema GameItemSchema_t;
+ typedef CDOTAItemDefinition GameItemDefinition_t;
+ typedef CDOTAItemSystem GameItemSystem_t;
+
+ #include "econ/dota_item_schema.h"
+#else
+ // Fallback Case
+ class CEconItemSchema;
+ class CEconItemDefinition;
+ class CEconItemSystem;
+
+ typedef CEconItemSchema GameItemSchema_t;
+ typedef CEconItemDefinition GameItemDefinition_t;
+ typedef CEconItemSystem GameItemSystem_t;
+
+ #include "econ_item_schema.h"
+#endif
+
+extern GameItemSchema_t *GetItemSchema();
+
+#endif // GAME_ITEM_SYSTEM_H
diff --git a/game/shared/econ/ihasattributes.h b/game/shared/econ/ihasattributes.h
new file mode 100644
index 0000000..62b63e4
--- /dev/null
+++ b/game/shared/econ/ihasattributes.h
@@ -0,0 +1,46 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+
+#ifndef IHASATTRIBUTES_H
+#define IHASATTRIBUTES_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+//#include "attribute_manager.h"
+
+class CAttributeManager;
+class CAttributeContainer;
+class CBaseEntity;
+class CAttributeList;
+
+// To allow an entity to have attributes, derive it from IHasAttributes and
+// contain an CAttributeManager in it. Then:
+// - Call InitializeAttributes() before your entity's Spawn()
+// - Call AddAttribute() to add attributes to the entity
+// - Call all the CAttributeManager hooks at the appropriate times in your entity.
+// To get networking of the attributes to work on your entity:
+// - Add this to your entity's send table:
+// SendPropDataTable( SENDINFO_DT( m_AttributeManager ), &REFERENCE_SEND_TABLE(DT_AttributeManager) ),
+// - Call this inside your entity's OnDataChanged():
+// GetAttributeManager()->OnDataChanged( updateType );
+
+//-----------------------------------------------------------------------------
+// Purpose: Derive from this if your entity wants to contain attributes.
+//-----------------------------------------------------------------------------
+class IHasAttributes
+{
+public:
+ virtual CAttributeManager *GetAttributeManager( void ) = 0;
+ virtual CAttributeContainer *GetAttributeContainer( void ) = 0;
+ virtual CBaseEntity *GetAttributeOwner( void ) = 0;
+ virtual CAttributeList *GetAttributeList( void ) = 0;
+
+ // Reapply yourself to whoever you should be providing attributes to.
+ virtual void ReapplyProvision( void ) = 0;
+};
+
+#endif // IHASATTRIBUTES_H
diff --git a/game/shared/econ/ihasowner.h b/game/shared/econ/ihasowner.h
new file mode 100644
index 0000000..77ac139
--- /dev/null
+++ b/game/shared/econ/ihasowner.h
@@ -0,0 +1,24 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+
+#ifndef IHASOWNER_H
+#define IHASOWNER_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+class CBaseEntity;
+
+//-----------------------------------------------------------------------------
+// Purpose: Allows an entity to access its owner regardless of entity type
+//-----------------------------------------------------------------------------
+class IHasOwner
+{
+public:
+ virtual CBaseEntity *GetOwnerViaInterface( void ) = 0;
+};
+
+#endif // IHASOWNER_H
diff --git a/game/shared/econ/item_selection_criteria.cpp b/game/shared/econ/item_selection_criteria.cpp
new file mode 100644
index 0000000..c0f1f05
--- /dev/null
+++ b/game/shared/econ/item_selection_criteria.cpp
@@ -0,0 +1,614 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: CItemSelectionCriteria, which serves as a criteria for selection
+// of a econ item
+//
+//=============================================================================
+
+
+#include "cbase.h"
+#include "item_selection_criteria.h"
+
+#include "gcsdk/gcsystemmsgs.h"
+
+#if defined(TF_CLIENT_DLL) || defined(TF_DLL)
+#include "tf_gcmessages.h"
+#endif
+
+#include "gcsdk/enumutils.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+
+// copied from \common\econ_item_view.h
+#define AE_USE_SCRIPT_VALUE 9999 // Can't be -1, due to unsigned ints used on the backend
+
+ENUMSTRINGS_START( EItemCriteriaOperator )
+{ k_EOperator_String_EQ, "string==" },
+{ k_EOperator_String_Not_EQ, "!string==" },
+{ k_EOperator_Float_EQ, "float==" },
+{ k_EOperator_Float_Not_EQ, "!float==" },
+{ k_EOperator_Float_LT, "float<" },
+{ k_EOperator_Float_Not_LT, "!float<" },
+{ k_EOperator_Float_LTE, "float<=" },
+{ k_EOperator_Float_Not_LTE, "!float<=" },
+{ k_EOperator_Float_GT, "float>" },
+{ k_EOperator_Float_Not_GT, "!float>" },
+{ k_EOperator_Float_GTE, "float>=" },
+{ k_EOperator_Float_Not_GTE, "!float>=" },
+{ k_EOperator_Subkey_Contains, "contains" },
+{ k_EOperator_Subkey_Not_Contains, "!contains" },
+ENUMSTRINGS_REVERSE( EItemCriteriaOperator, k_EItemCriteriaOperator_Count )
+
+using namespace GCSDK;
+
+//-----------------------------------------------------------------------------
+// Purpose: Copy Constructor
+//-----------------------------------------------------------------------------
+CItemSelectionCriteria::CItemSelectionCriteria( const CItemSelectionCriteria &that )
+{
+ (*this) = that;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Operator=
+//-----------------------------------------------------------------------------
+CItemSelectionCriteria &CItemSelectionCriteria::operator=( const CItemSelectionCriteria &rhs )
+{
+
+ // Leverage the serialization code we already have for the copy
+ CSOItemCriteria msgTemp;
+ rhs.BSerializeToMsg( msgTemp );
+ BDeserializeFromMsg( msgTemp );
+
+ return *this;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Destructor
+//-----------------------------------------------------------------------------
+CItemSelectionCriteria::~CItemSelectionCriteria( void )
+{
+ m_vecConditions.PurgeAndDeleteElements();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Look through our conditions and find the first of the specified type,
+// and return the value it's looking for.
+//-----------------------------------------------------------------------------
+const char *CItemSelectionCriteria::GetValueForFirstConditionOfType( EItemCriteriaOperator eType ) const
+{
+ // Only supporting this for string conditions right now
+ Assert( eType == k_EOperator_String_EQ || eType == k_EOperator_String_Not_EQ );
+
+ FOR_EACH_VEC( m_vecConditions, i )
+ {
+ if ( m_vecConditions[i]->GetEOp() == eType )
+ return m_vecConditions[i]->GetValue();
+ }
+
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Look through our conditions and find the first of the specified type,
+// and return the value it's looking for.
+//-----------------------------------------------------------------------------
+const char *CItemSelectionCriteria::GetFieldForFirstConditionOfType( EItemCriteriaOperator eType ) const
+{
+ FOR_EACH_VEC( m_vecConditions, i )
+ {
+ if ( m_vecConditions[i]->GetEOp() == eType )
+ return m_vecConditions[i]->GetField();
+ }
+
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Initialize from a KV structure
+//-----------------------------------------------------------------------------
+bool CItemSelectionCriteria::BInitFromKV( KeyValues *pKVCriteria )
+{
+ // Read in the base fields
+ if ( pKVCriteria->FindKey( "level" ) )
+ {
+ SetItemLevel( pKVCriteria->GetInt( "level" ) );
+ }
+
+ if ( pKVCriteria->FindKey( "quality" ) )
+ {
+ uint8 nQuality;
+ if ( !GetItemSchema()->BGetItemQualityFromName( pKVCriteria->GetString( "quality" ), &nQuality ) )
+ return false;
+
+ SetQuality( nQuality );
+ }
+
+ if ( pKVCriteria->FindKey( "inventoryPos" ) )
+ {
+ SetInitialInventory( pKVCriteria->GetInt( "inventoryPos" ) );
+ }
+
+ if ( pKVCriteria->FindKey( "quantity" ) )
+ {
+ SetInitialQuantity( pKVCriteria->GetInt( "quantity" ) );
+ }
+
+ if ( pKVCriteria->FindKey( "ignore_enabled" ) )
+ {
+ SetIgnoreEnabledFlag( pKVCriteria->GetBool( "ignore_enabled" ) );
+ }
+
+ if ( pKVCriteria->FindKey( "tags" ) )
+ {
+ SetTags( pKVCriteria->GetString( "tags" ) );
+ }
+
+ KeyValues *pKVConditions = pKVCriteria->FindKey( "conditions", true );
+
+ FOR_EACH_TRUE_SUBKEY( pKVConditions, pKVElement )
+ {
+ // Check for required fields
+ if ( !pKVElement->FindKey( "field" ) ||
+ !pKVElement->FindKey( "operator" ) ||
+ !pKVElement->FindKey( "value" ) )
+ return false;
+
+ const char *pszField = pKVElement->GetString( "field" );
+ bool bRequired = pKVElement->GetBool( "required" );
+ const char *pszValue = pKVElement->GetString( "value" );
+
+ // Get the operator
+ const char *pszOperator = pKVElement->GetString( "operator" );
+ EItemCriteriaOperator eOp = EItemCriteriaOperatorFromName( pszOperator );
+ if ( k_EItemCriteriaOperator_Count == eOp )
+ return false;
+
+ BAddCondition( pszField, eOp, pszValue, bRequired );
+ }
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CItemSelectionCriteria::SetTags( const char *pszTags )
+{
+ m_vecTags.Purge();
+
+ m_strTags = pszTags;
+ CSplitString splitString( pszTags, " " );
+ for ( int i=0; i<splitString.Count(); ++i )
+ {
+ econ_tag_handle_t tagHandle = GetItemSchema()->GetHandleForTag( splitString[i] );
+ if ( !m_vecTags.HasElement( tagHandle ) )
+ {
+ m_vecTags.AddToTail( tagHandle );
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CItemSelectionCriteria::BAddCondition( CItemSelectionCriteria::ICondition *pCondition )
+{
+ CPlainAutoPtr<ICondition> pConditionPtr( pCondition );
+
+ // Check for condition limit
+ if ( UCHAR_MAX == GetConditionsCount() )
+ {
+ AssertMsg( false, "Too many conditions on a a CItemSelectionCriteria. Max: 255" );
+ return false;
+ }
+
+ m_vecConditions.AddToTail( pConditionPtr.Detach() );
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Adds a condition to the selection criteria
+// Input: pszField - Field to evaluate on
+// eOp - Operator to apply to the value of the field
+// flValue - The value to compare.
+// bRequired - When true, causes BEvauluate to fail if pszField doesn't
+// exist in the KV being checked.
+// Output: True if the condition was added, false otherwise
+//-----------------------------------------------------------------------------
+bool CItemSelectionCriteria::BAddCondition( const char *pszField, EItemCriteriaOperator eOp, float flValue, bool bRequired )
+{
+ // Enforce maximum string lengths
+ if ( Q_strlen( pszField ) >= k_cchCreateItemLen )
+ return false;
+
+ // Create the appropriate condition for the operator
+ switch ( eOp )
+ {
+ case k_EOperator_Float_EQ:
+ case k_EOperator_Float_Not_EQ:
+ case k_EOperator_Float_LT:
+ case k_EOperator_Float_Not_LT:
+ case k_EOperator_Float_LTE:
+ case k_EOperator_Float_Not_LTE:
+ case k_EOperator_Float_GT:
+ case k_EOperator_Float_Not_GT:
+ case k_EOperator_Float_GTE:
+ case k_EOperator_Float_Not_GTE:
+ return BAddCondition( new CFloatCondition( pszField, eOp, flValue, bRequired ) );
+
+ default:
+ AssertMsg1( false, "Bad operator (%d) passed to BAddCondition. Float based operator required for this overload.", eOp );
+ return false;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Adds a condition to the selection criteria
+// Input: pszField - Field to evaluate on
+// eOp - Operator to apply to the value of the field
+// pszValue - The value to compare.
+// bRequired - When true, causes BEvauluate to fail if pszField doesn't
+// exist in the KV being checked.
+// Output: True if the condition was added, false otherwise
+//-----------------------------------------------------------------------------
+bool CItemSelectionCriteria::BAddCondition( const char *pszField, EItemCriteriaOperator eOp, const char * pszValue, bool bRequired )
+{
+ // Enforce maximum string lengths
+ if ( Q_strlen( pszField ) >= k_cchCreateItemLen || Q_strlen( pszValue ) >= k_cchCreateItemLen )
+ return false;
+
+ // Create the appropriate condition for the operator
+ switch ( eOp )
+ {
+ case k_EOperator_String_EQ:
+ case k_EOperator_String_Not_EQ:
+ return BAddCondition( new CStringCondition( pszField, eOp, pszValue, bRequired ) );
+ return true;
+
+ case k_EOperator_Subkey_Contains:
+ case k_EOperator_Subkey_Not_Contains:
+ return BAddCondition( new CSetCondition( pszField, eOp, pszValue, bRequired ) );
+ return true;
+
+ default:
+ // Try the float operators
+ return BAddCondition( pszField, eOp, Q_atof( pszValue ), bRequired );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Checks if a given item matches the item selection criteria
+// Input: itemDef - The item definition to evaluate against
+// Output: True is the item passes the filter, false otherwise
+//-----------------------------------------------------------------------------
+bool CItemSelectionCriteria::BEvaluate( const CEconItemDefinition* pItemDef ) const
+{
+ // Disabled items never match
+ if ( !m_bIgnoreEnabledFlag && !pItemDef->BEnabled() )
+ return false;
+
+ // Filter against level
+ if ( BItemLevelSet() && (GetItemLevel() != AE_USE_SCRIPT_VALUE) &&
+ ( GetItemLevel() < pItemDef->GetMinLevel() || GetItemLevel() > pItemDef->GetMaxLevel() ) )
+ return false;
+
+ // Filter against quality
+ if ( BQualitySet() && (GetQuality() != AE_USE_SCRIPT_VALUE) )
+ {
+ if ( GetQuality() != pItemDef->GetQuality() )
+ {
+ // Filter out item defs that have a non-any quality if we have a non-matching & non-any quality criteria
+ if ( k_unItemQuality_Any != GetQuality() && k_unItemQuality_Any != pItemDef->GetQuality() )
+ return false;
+ }
+ }
+
+ // Filter against the additional conditions
+ FOR_EACH_VEC( m_vecConditions, i )
+ {
+ if ( !m_vecConditions[i]->BItemDefinitionPassesCriteria( pItemDef ) )
+ return false;
+ }
+
+ // Check if we have "any" tags
+ if ( m_vecTags.Count() > 0 )
+ {
+ bool bHasTag = false;
+ FOR_EACH_VEC( m_vecTags, i )
+ {
+ if ( pItemDef->HasEconTag( m_vecTags[i] ) )
+ {
+ bHasTag = true;
+ break;
+ }
+ }
+
+ if ( !bHasTag )
+ {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Determines if the item matches this condition of the criteria
+// Input: pKVItem - Pointer to the raw KeyValues definition of the item
+// Output: True is the item matches, false otherwise
+//-----------------------------------------------------------------------------
+bool CItemSelectionCriteria::CCondition::BEvaluate( KeyValues *pKVItem ) const
+{
+ KeyValues *pKVField = pKVItem->FindKey( m_sField.String() );
+
+ // Treat an empty string as a missing field as well.
+ bool bIsEmptyString = false;
+ if ( m_EOp == k_EOperator_String_EQ || m_EOp == k_EOperator_String_Not_EQ )
+ {
+ const char *pszItemVal = pKVField ? pKVField->GetString() : NULL;
+ bIsEmptyString = ( pszItemVal == NULL || pszItemVal[0] == '\0' );
+ }
+
+ // Deal with missing fields
+ if ( NULL == pKVField || bIsEmptyString )
+ {
+ if ( m_bRequired )
+ return false;
+ else
+ return true;
+ }
+
+ // Run the operator specific check
+ bool bRet = BInternalEvaluate( pKVItem );
+
+ // If this is a "not" operator, reverse the result
+ if ( m_EOp & k_EOperator_Not )
+ return !bRet;
+ else
+ return bRet;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Runs the operator specific check for this condition
+// Input: pKVItem - Pointer to the raw KeyValues definition of the item
+// Output: True is the item matches, false otherwise
+//-----------------------------------------------------------------------------
+bool CItemSelectionCriteria::CStringCondition::BInternalEvaluate( KeyValues *pKVItem ) const
+{
+ Assert( k_EOperator_String_EQ == m_EOp || k_EOperator_String_Not_EQ == m_EOp );
+ if( !( k_EOperator_String_EQ == m_EOp || k_EOperator_String_Not_EQ == m_EOp ) )
+ return false;
+
+ const char *pszItemVal = pKVItem->GetString( m_sField.String() );
+ return ( 0 == Q_stricmp( m_sValue.String(), pszItemVal ) );
+}
+
+bool CItemSelectionCriteria::CStringCondition::BSerializeToMsg( CSOItemCriteriaCondition & msg ) const
+{
+ CCondition::BSerializeToMsg( msg );
+ msg.set_string_value( m_sValue.Get() );
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Runs the operator specific check for this condition
+// Input: pKVItem - Pointer to the raw KeyValues definition of the item
+// Output: True is the item matches, false otherwise
+//-----------------------------------------------------------------------------
+bool CItemSelectionCriteria::CSetCondition::BInternalEvaluate( KeyValues *pKVItem ) const
+{
+ Assert( k_EOperator_Subkey_Contains == m_EOp || k_EOperator_Subkey_Not_Contains == m_EOp );
+ if( !( k_EOperator_Subkey_Contains == m_EOp || k_EOperator_Subkey_Not_Contains == m_EOp ) )
+ return false;
+
+ return ( NULL != pKVItem->FindKey( m_sField.String() )->FindKey( m_sValue.String() ) );
+}
+
+bool CItemSelectionCriteria::CSetCondition::BSerializeToMsg( CSOItemCriteriaCondition & msg ) const
+{
+ CCondition::BSerializeToMsg( msg );
+ msg.set_string_value( m_sValue.Get() );
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Runs the operator specific check for this condition
+// Input: pKVItem - Pointer to the raw KeyValues definition of the item
+// Output: True is the item matches, false otherwise
+//-----------------------------------------------------------------------------
+bool CItemSelectionCriteria::CFloatCondition::BInternalEvaluate( KeyValues *pKVItem ) const
+{
+ float itemValue = pKVItem->GetFloat( m_sField.String() );
+
+ switch ( m_EOp )
+ {
+ case k_EOperator_Float_EQ:
+ case k_EOperator_Float_Not_EQ:
+ return ( itemValue == m_flValue );
+
+ case k_EOperator_Float_LT:
+ case k_EOperator_Float_Not_LT:
+ return ( itemValue < m_flValue );
+
+ case k_EOperator_Float_LTE:
+ case k_EOperator_Float_Not_LTE:
+ return ( itemValue <= m_flValue );
+
+ case k_EOperator_Float_GT:
+ case k_EOperator_Float_Not_GT:
+ return ( itemValue > m_flValue );
+
+ case k_EOperator_Float_GTE:
+ case k_EOperator_Float_Not_GTE:
+ return ( itemValue >= m_flValue );
+
+ default:
+ AssertMsg1( false, "Unknown operator: %d", m_EOp );
+ return false;
+ }
+}
+
+bool CItemSelectionCriteria::CFloatCondition::BSerializeToMsg( CSOItemCriteriaCondition & msg ) const
+{
+ CCondition::BSerializeToMsg( msg );
+ msg.set_float_value( m_flValue );
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Serialize the item selection criteria to the given message
+// Input: msg - The message to serialize to.
+// Output: True if the operation was successful, false otherwise.
+//-----------------------------------------------------------------------------
+bool CItemSelectionCriteria::BSerializeToMsg( CSOItemCriteria & msg ) const
+{
+ msg.set_item_level( m_unItemLevel );
+ msg.set_item_quality( m_nItemQuality );
+ msg.set_item_level_set( m_bItemLevelSet );
+ msg.set_item_quality_set( m_bQualitySet );
+ msg.set_initial_inventory( m_unInitialInventory );
+ msg.set_initial_quantity( m_unInitialQuantity );
+ msg.set_ignore_enabled_flag( m_bIgnoreEnabledFlag );
+ msg.set_tags( m_strTags );
+
+ FOR_EACH_VEC( m_vecConditions, i )
+ {
+ CSOItemCriteriaCondition *pConditionMsg = msg.add_conditions();
+ m_vecConditions[i]->BSerializeToMsg( *pConditionMsg );
+ }
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Deserializes the item selection criteria from the given message
+// Input: msg - The message to deserialize from.
+// Output: True if the operation was successful, false otherwise.
+//-----------------------------------------------------------------------------
+bool CItemSelectionCriteria::BDeserializeFromMsg( const CSOItemCriteria & msg )
+{
+ m_unItemLevel = msg.item_level();
+ m_nItemQuality = msg.item_quality();
+ m_bItemLevelSet = msg.item_level_set();
+ m_bQualitySet = msg.item_quality_set();
+ m_unInitialInventory = msg.initial_inventory();
+ m_unInitialQuantity = msg.initial_quantity();
+ m_bIgnoreEnabledFlag = msg.ignore_enabled_flag();
+
+ SetTags( msg.tags().c_str() );
+
+ uint32 unCount = msg.conditions_size();
+ m_vecConditions.EnsureCapacity( unCount );
+
+ for ( uint32 i = 0; i < unCount; i++ )
+ {
+ const CSOItemCriteriaCondition & cond = msg.conditions( i );
+ EItemCriteriaOperator eOp = (EItemCriteriaOperator)cond.op();
+ bool bRequired = cond.required();
+
+ // Read the value specific to the condition and add the condition
+ switch ( eOp )
+ {
+ case k_EOperator_Float_EQ:
+ case k_EOperator_Float_Not_EQ:
+ case k_EOperator_Float_LT:
+ case k_EOperator_Float_Not_LT:
+ case k_EOperator_Float_LTE:
+ case k_EOperator_Float_Not_LTE:
+ case k_EOperator_Float_GT:
+ case k_EOperator_Float_Not_GT:
+ case k_EOperator_Float_GTE:
+ case k_EOperator_Float_Not_GTE:
+ {
+ if ( !BAddCondition( cond.field().c_str(), eOp, cond.float_value(), bRequired ) ) return false;
+ break;
+ }
+
+ case k_EOperator_String_EQ:
+ case k_EOperator_String_Not_EQ:
+ case k_EOperator_Subkey_Contains:
+ case k_EOperator_Subkey_Not_Contains:
+ {
+ if ( !BAddCondition( cond.field().c_str(), eOp, cond.string_value().c_str(), bRequired ) ) return false;
+ break;
+ }
+
+ default:
+ AssertMsg1( false, "Unknown operator (%d) read.", eOp );
+ return false;
+ }
+ }
+
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Serializes a condition to a message.
+// Input: msg - The message to serialize to.
+// Output: True if the operation was successful, false otherwise.
+//-----------------------------------------------------------------------------
+bool CItemSelectionCriteria::CCondition::BSerializeToMsg( CSOItemCriteriaCondition & msg ) const
+{
+ msg.set_op( m_EOp );
+ msg.set_field( m_sField.String() );
+ msg.set_required( m_bRequired );
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CItemSelectionCriteria::CCondition::BItemDefinitionPassesCriteria( const CEconItemDefinition *pItemDef ) const
+{
+ return BEvaluate( pItemDef->GetRawDefinition() );
+}
+
+// Validation
+#ifdef DBGFLAG_VALIDATE
+
+//-----------------------------------------------------------------------------
+// Purpose: Run a global validation pass on all of our data structures and memory
+// allocations.
+// Input: validator - Our global validator object
+// pchName - Our name (typically a member var in our container)
+//-----------------------------------------------------------------------------
+void CItemSelectionCriteria::Validate( CValidator &validator, const char *pchName )
+{
+ VALIDATE_SCOPE();
+ ValidateObj( m_vecConditions );
+ FOR_EACH_VEC( m_vecConditions, i )
+ {
+ ValidatePtr( m_vecConditions[i] );
+ }
+}
+
+void CItemSelectionCriteria::CCondition::Validate( CValidator &validator, const char *pchName )
+{
+ ValidateObj( m_sField );
+}
+
+void CItemSelectionCriteria::CStringCondition::Validate( CValidator &validator, const char *pchName )
+{
+ CCondition::Validate( validator, pchName );
+ ValidateObj( m_sValue );
+}
+
+void CItemSelectionCriteria::CSetCondition::Validate( CValidator &validator, const char *pchName )
+{
+ CCondition::Validate( validator, pchName );
+ ValidateObj( m_sValue );
+}
+
+#endif
diff --git a/game/shared/econ/item_selection_criteria.h b/game/shared/econ/item_selection_criteria.h
new file mode 100644
index 0000000..a4da52d
--- /dev/null
+++ b/game/shared/econ/item_selection_criteria.h
@@ -0,0 +1,290 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: CItemSelectionCriteria, which serves as a criteria for selection
+// of a econ item
+//
+//=============================================================================
+
+#ifndef ITEM_SELECTION_CRITERIA_H
+#define ITEM_SELECTION_CRITERIA_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+// Maximum string length in item create APIs
+const int k_cchCreateItemLen = 64;
+
+// Operators for BAddNewItemCriteria
+enum EItemCriteriaOperator
+{
+ k_EOperator_String_EQ = 0, // Field is string equal to value
+ k_EOperator_Not = 1, // Logical not
+ k_EOperator_String_Not_EQ = 1, // Field is not string equal to value
+ k_EOperator_Float_EQ = 2, // Field as a float is equal to value
+ k_EOperator_Float_Not_EQ = 3, // Field as a float is not equal to value
+ k_EOperator_Float_LT = 4, // Field as a float is less than value
+ k_EOperator_Float_Not_LT = 5, // Field as a float is not less than value
+ k_EOperator_Float_LTE = 6, // Field as a float is less than or equal value
+ k_EOperator_Float_Not_LTE = 7, // Field as a float is not less than or equal value
+ k_EOperator_Float_GT = 8, // Field as a float is greater than value
+ k_EOperator_Float_Not_GT = 9, // Field as a float is not greater than value
+ k_EOperator_Float_GTE = 10, // Field as a float is greater than or equal value
+ k_EOperator_Float_Not_GTE = 11, // Field as a float is not greater than or equal value
+ k_EOperator_Subkey_Contains = 12, // Field contains value as a subkey
+ k_EOperator_Subkey_Not_Contains = 13, // Field does not contain value as a subkey
+
+ // Must be last
+ k_EItemCriteriaOperator_Count = 14,
+};
+
+
+EItemCriteriaOperator EItemCriteriaOperatorFromName( const char *pch );
+const char *PchNameFromEItemCriteriaOperator( int eItemCriteriaOperator );
+
+class CEconItemSchema;
+class CEconItemDefinition;
+class CSOItemCriteria;
+class CSOItemCriteriaCondition;
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+
+
+//-----------------------------------------------------------------------------
+// CItemSelectionCriteria
+// A class that contains all the conditions a server needs to specify what
+// kind of random item they wish to generate.
+//-----------------------------------------------------------------------------
+class CItemSelectionCriteria
+{
+public:
+ // Constructors and destructor
+ CItemSelectionCriteria() :
+ m_bItemLevelSet( false ),
+ m_unItemLevel( 0 ),
+ m_bQualitySet( false ),
+ m_nItemQuality( k_unItemQuality_Any ),
+ m_unInitialInventory( 0 ),
+ m_bInitialQuantitySet( false ),
+ m_unInitialQuantity( 1 ),
+ m_bIgnoreEnabledFlag( false )
+ {
+ }
+
+ CItemSelectionCriteria( const CItemSelectionCriteria &that );
+ CItemSelectionCriteria &operator=( const CItemSelectionCriteria& rhs );
+ ~CItemSelectionCriteria();
+
+ // Accessors and Settors
+ bool BItemLevelSet( void ) const { return m_bItemLevelSet; }
+ uint32 GetItemLevel( void ) const { Assert( m_bItemLevelSet ); return m_unItemLevel; }
+ void SetItemLevel( uint32 unLevel ) { m_unItemLevel = unLevel; m_bItemLevelSet = true; }
+ bool BQualitySet( void ) const { return m_bQualitySet; }
+ int32 GetQuality( void ) const { Assert( m_bQualitySet ); return m_nItemQuality; }
+ void SetQuality( int32 nQuality ) { m_nItemQuality = nQuality; m_bQualitySet = true; }
+ uint32 GetInitialInventory( void ) const { return m_unInitialInventory; }
+ void SetInitialInventory( uint32 unInventory ) { m_unInitialInventory = unInventory; }
+ bool BInitialQuantitySet( void ) const { return m_bQualitySet; }
+ uint32 GetInitialQuantity( void ) const { Assert( m_bQualitySet ); return m_unInitialQuantity; }
+ void SetInitialQuantity( uint32 unQuantity ) { m_unInitialQuantity = unQuantity; m_bInitialQuantitySet = true; }
+ void SetIgnoreEnabledFlag( bool bIgnore ) { m_bIgnoreEnabledFlag = bIgnore; }
+
+ // Tags
+ void SetTags( const char *pszTags );
+
+
+ // Add conditions to the criteria
+ class ICondition
+ {
+ public:
+ virtual ~ICondition() { }
+
+ virtual bool BItemDefinitionPassesCriteria( const CEconItemDefinition *pItemDef ) const = 0;
+
+ virtual EItemCriteriaOperator GetEOp() const { return k_EItemCriteriaOperator_Count; }
+ virtual const char *GetField() const { return ""; }
+ virtual const char *GetValue() const { return ""; }
+
+ virtual bool BSerializeToMsg( CSOItemCriteriaCondition & msg ) const { Assert( !"BSerializeToMsg() called on for unimplementing ICondition!" ); return false; }
+ };
+
+ bool BAddCondition( const char *pszField, EItemCriteriaOperator eOp, float flValue, bool bRequired );
+ bool BAddCondition( const char *pszField, EItemCriteriaOperator eOp, const char * pszValue, bool bRequired );
+ bool BAddCondition( ICondition *pCondition );
+ int GetConditionsCount() { return m_vecConditions.Count(); }
+ const char *GetValueForFirstConditionOfType( EItemCriteriaOperator eType ) const;
+ const char *GetFieldForFirstConditionOfType( EItemCriteriaOperator eType ) const;
+
+ // Alternate ways of initializing
+ bool BInitFromKV( KeyValues *pKVCriteria );
+
+ // Serializes the criteria to and from messages
+ bool BSerializeToMsg( CSOItemCriteria & msg ) const;
+ bool BDeserializeFromMsg( const CSOItemCriteria & msg );
+
+ // Evaluates an item definition against this criteria. Returns true if
+ // the definition passes the filter
+ bool BEvaluate( const CEconItemDefinition* pItemDef ) const;
+
+ // Validation
+#ifdef DBGFLAG_VALIDATE
+ void Validate( CValidator &validator, const char *pchName );
+#endif
+
+private:
+ //-----------------------------------------------------------------------------
+ // CItemSelectionCriteria::CCondition
+ // Represents one condition of the criteria
+ //-----------------------------------------------------------------------------
+ class CCondition : public ICondition
+ {
+ public:
+ CCondition( const char *pszField, EItemCriteriaOperator eOp, bool bRequired )
+ : m_sField( pszField ), m_EOp( eOp ), m_bRequired( bRequired )
+ {
+ }
+
+ // ICondition interface.
+ virtual bool BItemDefinitionPassesCriteria( const CEconItemDefinition *pItemDef ) const OVERRIDE;
+
+ // Serializes the condition to the message
+ virtual bool BSerializeToMsg( CSOItemCriteriaCondition & msg ) const;
+
+ // Validation
+#ifdef DBGFLAG_VALIDATE
+ virtual void Validate( CValidator &validator, const char *pchName );
+#endif
+
+ EItemCriteriaOperator GetEOp( void ) const OVERRIDE { return m_EOp; }
+ virtual const char *GetField( void ) const OVERRIDE { return m_sField.Get(); }
+ virtual const char *GetValue( void ) const OVERRIDE { Assert(0); return NULL; }
+
+ private:
+ // Returns if the given KeyValues block passes this condition
+ // Performs common checks and calls BInternalEvaluate
+ bool BEvaluate( KeyValues *pKVItem ) const;
+
+ protected:
+ // Returns true if applying the element's operator on m_sField of
+ // pKVItem returns true. This is only called if m_pszField exists in pKVItem
+ virtual bool BInternalEvaluate( KeyValues *pKVItem ) const = 0;
+
+ // The field of the raw KeyValue form of the item definition to check
+ CUtlString m_sField;
+ // The operator this clause uses
+ EItemCriteriaOperator m_EOp;
+ // When true, BEvaluate returns false if m_sField does not exist in pKVItem
+ bool m_bRequired;
+ };
+
+
+ //-----------------------------------------------------------------------------
+ // CItemSelectionCriteria::CStringCondition
+ // CCondition that handles the string-based operators
+ //-----------------------------------------------------------------------------
+ class CStringCondition : public CCondition
+ {
+ public:
+ CStringCondition( const char *pszField, EItemCriteriaOperator eOp, const char *pszValue, bool bRequired )
+ : CCondition( pszField, eOp, bRequired ), m_sValue( pszValue )
+ {
+ }
+
+ virtual ~CStringCondition( ) { }
+
+ virtual const char *GetValue( void ) const OVERRIDE { return m_sValue.Get(); }
+
+ // Validation
+#ifdef DBGFLAG_VALIDATE
+ virtual void Validate( CValidator &validator, const char *pchName );
+#endif
+
+ protected:
+ virtual bool BInternalEvaluate( KeyValues *pKVItem ) const;
+ virtual bool BSerializeToMsg( CSOItemCriteriaCondition & msg ) const;
+
+ // The value to check against
+ CUtlString m_sValue;
+ };
+
+
+ //-----------------------------------------------------------------------------
+ // CItemSelectionCriteria::CFloatCondition
+ // CCondition that handles the float-based operators
+ //-----------------------------------------------------------------------------
+ class CFloatCondition : public CCondition
+ {
+ public:
+ CFloatCondition( const char *pszField, EItemCriteriaOperator eOp, float flValue, bool bRequired )
+ : CCondition( pszField, eOp, bRequired ), m_flValue( flValue )
+ {
+ }
+
+ virtual ~CFloatCondition( ) { }
+
+ protected:
+ virtual bool BInternalEvaluate( KeyValues *pKVItem ) const;
+ virtual bool BSerializeToMsg( CSOItemCriteriaCondition & msg ) const;
+
+ // The value to check against
+ float m_flValue;
+ };
+
+
+ //-----------------------------------------------------------------------------
+ // CItemSelectionCriteria::CSetCondition
+ // CCondition that handles subkey checks
+ //-----------------------------------------------------------------------------
+ class CSetCondition : public CCondition
+ {
+ public:
+ CSetCondition( const char *pszField, EItemCriteriaOperator eOp, const char *pszValue, bool bRequired )
+ : CCondition( pszField, eOp, bRequired ), m_sValue( pszValue )
+ {
+ }
+
+ virtual ~CSetCondition( ) { }
+
+ // Validation
+#ifdef DBGFLAG_VALIDATE
+ virtual void Validate( CValidator &validator, const char *pchName );
+#endif
+
+ protected:
+ virtual bool BInternalEvaluate( KeyValues *pKVItem ) const;
+
+ virtual bool BSerializeToMsg( CSOItemCriteriaCondition & msg ) const;
+
+ // The subkey to look for
+ CUtlString m_sValue;
+ };
+
+ // True if item level is specified in this criteria
+ bool m_bItemLevelSet;
+ // The level of the item to generate
+ uint32 m_unItemLevel;
+ // True if quality is specified in this criteria
+ bool m_bQualitySet;
+ // The quality of the item to generate
+ int32 m_nItemQuality;
+ // The initial inventory token of the item
+ uint32 m_unInitialInventory;
+ // True if initial quantity is specified in this criteria.
+ bool m_bInitialQuantitySet;
+ // The initial quantity of the item
+ uint32 m_unInitialQuantity;
+ // Enforced explicit quality matching
+ bool m_bForcedQualityMatch;
+ // Ignoring enabled flag (used when crafting)
+ bool m_bIgnoreEnabledFlag;
+
+ // A list of tags
+ CUtlString m_strTags;
+ CUtlVector<econ_tag_handle_t> m_vecTags;
+
+ // A list of the conditions
+ CUtlVector<ICondition *> m_vecConditions;
+};
+
+
+#endif //ITEM_SELECTION_CRITERIA_H
diff --git a/game/shared/econ/localization_provider.cpp b/game/shared/econ/localization_provider.cpp
new file mode 100644
index 0000000..78e5e2c
--- /dev/null
+++ b/game/shared/econ/localization_provider.cpp
@@ -0,0 +1,199 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+
+#include "cbase.h"
+#include "localization_provider.h"
+
+enum { kScratchBufferSize = 1024 };
+
+
+
+// ----------------------------------------------------------------------------
+// Find a localized string, but return something safe if the key is null or the localized
+// string is missing.
+// ----------------------------------------------------------------------------
+locchar_t* CLocalizationProvider::FindSafe( const char* pchKey ) const
+{
+ if ( pchKey )
+ {
+ locchar_t* wszLocalized = Find( pchKey );
+ if ( !wszLocalized )
+ {
+#ifdef STAGING_ONLY
+ return const_cast< locchar_t* >(LOCCHAR("<NULL LOC STRING>")); // Super janky cast alert! This method should really return a const locchar_t* but making that change breaks all the callsites...fix later.
+#else
+ return const_cast<locchar_t*>(LOCCHAR(""));
+#endif
+ }
+ else
+ {
+ return wszLocalized;
+ }
+ }
+ else
+ {
+#ifdef STAGING_ONLY
+ return const_cast<locchar_t*>(LOCCHAR("<NULL LOC KEY>"));
+#else
+ return const_cast<locchar_t*>(LOCCHAR(""));
+#endif
+ }
+}
+
+#ifdef GC
+#include "gcsdk/gcbase.h"
+
+// GC Localization implementation
+
+static CGCLocalizationProvider *GGCLocalizationProvider()
+{
+ static CGCLocalizationProvider *g_pGCLocalizationProvider = NULL;
+ if ( !g_pGCLocalizationProvider )
+ g_pGCLocalizationProvider = new CGCLocalizationProvider( GGCGameBase() );
+ return g_pGCLocalizationProvider;
+}
+
+CLocalizationProvider *GLocalizationProvider()
+{
+ AssertMsg( false, "Using global localization provider in GC - All strings will be in English. For proper localization, CLocalizationProvider instance should be created and passed in." );
+ return GGCLocalizationProvider();
+}
+
+locchar_t *CGCLocalizationProvider::Find( const char *pchKey ) const
+{
+ // we emulate VGUI's behavior of returning an empty string for keys that are not found
+ return (locchar_t*)m_pGC->LocalizeToken( pchKey, m_eLang, false );
+}
+
+bool CGCLocalizationProvider::BEnsureCleanUTF8Truncation( char *unicodeOutput )
+{
+ int nStringLength = V_strlen( unicodeOutput );
+
+ // make sure we're not in the middle of a multibyte character
+ int iPos = nStringLength - 1;
+ char c = unicodeOutput[iPos];
+ if ( (c & 0x80) != 0 )
+ {
+ // not an ascii char, so do some multibyte char checking
+ int cBytes = 0;
+ // count up all continuation bytes
+ while ( (c & 0xC0) == 0x80 && iPos > 0 ) // first two bits are 10xxxx, continuation
+ {
+ cBytes++;
+ c = unicodeOutput[--iPos];
+ }
+
+ // make sure we had the expected number of continuation bytes for the last
+ // multibyte lead character
+ bool bTruncateOK = true;
+ if ( ( c & 0xF8 ) == 0xF0 ) // first 5 bits are 11110, should be 3 following bytes
+ bTruncateOK = ( cBytes == 3 );
+ else if ( ( c & 0xF0 ) == 0xE0 ) // first 4 bits are 1110, should be 2 following bytes
+ bTruncateOK = ( cBytes == 2 );
+ else if ( ( c & 0xE0 ) == 0xC0 ) // first 3 bits are 110, should be 1 following byte
+ bTruncateOK = ( cBytes == 1 );
+
+ // if we truncated in the middle of a multi-byte char, move the end point back to this character
+ if ( !bTruncateOK )
+ unicodeOutput[iPos] = '\0';
+
+ return !bTruncateOK;
+ }
+
+ return false;
+}
+
+void CGCLocalizationProvider::ConvertLoccharToANSI( const locchar_t *loc_In, CUtlConstString *out_ansi ) const
+{
+ *out_ansi = loc_In;
+}
+
+void CGCLocalizationProvider::ConvertLoccharToUnicode( const locchar_t *loc_In, CUtlConstWideString *out_unicode ) const
+{
+ wchar_t utf16_Scratch[kScratchBufferSize];
+
+ V_UTF8ToUnicode( loc_In, utf16_Scratch, kScratchBufferSize );
+ *out_unicode = utf16_Scratch;
+}
+
+void CGCLocalizationProvider::ConvertUTF8ToLocchar( const char *utf8_In, CUtlConstStringBase<locchar_t> *out_loc ) const
+{
+ *out_loc = utf8_In;
+}
+
+int CGCLocalizationProvider::ConvertLoccharToANSI( const locchar_t *loc, char *ansi, int ansiBufferSize ) const
+{
+ Q_strncpy( ansi, loc, ansiBufferSize );
+ return 0;
+}
+
+int CGCLocalizationProvider::ConvertLoccharToUnicode( const locchar_t *loc, wchar_t *unicode, int unicodeBufferSize ) const
+{
+ return V_UTF8ToUnicode( loc, unicode, unicodeBufferSize );
+}
+
+
+void CGCLocalizationProvider::ConvertUTF8ToLocchar( const char *utf8, locchar_t *locchar, int loccharBufferSize ) const
+{
+ Q_strncpy( locchar, utf8, loccharBufferSize );
+}
+
+
+#else
+
+CLocalizationProvider *GLocalizationProvider()
+{
+ static CVGUILocalizationProvider g_VGUILocalizationProvider;
+ return &g_VGUILocalizationProvider;
+}
+
+// vgui localization implementation
+
+CVGUILocalizationProvider::CVGUILocalizationProvider()
+{
+
+}
+
+locchar_t *CVGUILocalizationProvider::Find( const char *pchKey ) const
+{
+ return (locchar_t*)g_pVGuiLocalize->Find( pchKey );
+}
+
+void CVGUILocalizationProvider::ConvertLoccharToANSI( const locchar_t *loc_In, CUtlConstString *out_ansi ) const
+{
+ char ansi_Scratch[kScratchBufferSize];
+
+ g_pVGuiLocalize->ConvertUnicodeToANSI( loc_In, ansi_Scratch, kScratchBufferSize );
+ *out_ansi = ansi_Scratch;
+}
+
+void CVGUILocalizationProvider::ConvertLoccharToUnicode( const locchar_t *loc_In, CUtlConstWideString *out_unicode ) const
+{
+ *out_unicode = loc_In;
+}
+
+void CVGUILocalizationProvider::ConvertUTF8ToLocchar( const char *utf8_In, CUtlConstStringBase<locchar_t> *out_loc ) const
+{
+ locchar_t loc_Scratch[kScratchBufferSize];
+
+ V_UTF8ToUnicode( utf8_In, loc_Scratch, kScratchBufferSize );
+ *out_loc = loc_Scratch;
+}
+
+void CVGUILocalizationProvider::ConvertUTF8ToLocchar( const char *utf8, locchar_t *locchar, int loccharBufferSize ) const
+{
+ V_UTF8ToUnicode( utf8, locchar, loccharBufferSize );
+}
+
+int CVGUILocalizationProvider::ConvertLoccharToANSI( const locchar_t *loc, char *ansi, int ansiBufferSize ) const
+{
+ return g_pVGuiLocalize->ConvertUnicodeToANSI( loc, ansi, ansiBufferSize );
+}
+
+int CVGUILocalizationProvider::ConvertLoccharToUnicode( const locchar_t *loc, wchar_t *unicode, int unicodeBufferSize ) const
+{
+ Q_wcsncpy( unicode, loc, unicodeBufferSize );
+ return 0;
+}
+
+
+#endif \ No newline at end of file
diff --git a/game/shared/econ/localization_provider.h b/game/shared/econ/localization_provider.h
new file mode 100644
index 0000000..0ee2fdd
--- /dev/null
+++ b/game/shared/econ/localization_provider.h
@@ -0,0 +1,103 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: provide a layer of abstraction between GC and vgui localization systems
+//
+//=============================================================================
+
+#ifndef LOCALIZATION_PROVIDER_H
+#define LOCALIZATION_PROVIDER_H
+
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "language.h"
+#include "ilocalize.h"
+
+
+// interface matches a subset of VGUI functions
+class CLocalizationProvider
+{
+public:
+ virtual locchar_t *Find( const char *pchKey ) const = 0;
+ locchar_t* FindSafe( const char* pchKey ) const;
+
+ // new interface
+ virtual void ConvertLoccharToANSI ( const locchar_t *loc_In, CUtlConstString *out_ansi ) const = 0;
+ virtual void ConvertLoccharToUnicode( const locchar_t *loc_In, CUtlConstWideString *out_unicode ) const = 0;
+ virtual void ConvertUTF8ToLocchar ( const char *utf8_In, CUtlConstStringBase<locchar_t> *out_loc ) const = 0;
+
+ // old C-style interface
+ virtual int ConvertLoccharToANSI( const locchar_t *loc, OUT_Z_BYTECAP(ansiBufferSize) char *ansi, int ansiBufferSize ) const = 0;
+ virtual int ConvertLoccharToUnicode( const locchar_t *loc, OUT_Z_BYTECAP(unicodeBufferSize) wchar_t *unicode, int unicodeBufferSize ) const = 0;
+
+ virtual void ConvertUTF8ToLocchar( const char *utf8, OUT_Z_BYTECAP(loccharBufferSize) locchar_t *locchar, int loccharBufferSize ) const = 0;
+
+ virtual ELanguage GetELang() const = 0;
+};
+CLocalizationProvider *GLocalizationProvider();
+
+#ifdef GC
+// GC localization is handled by the GC itself
+class CGCLocalizationProvider : public CLocalizationProvider
+{
+public:
+ CGCLocalizationProvider( CGCGameBase *pGC, ELanguage eLang = k_Lang_English )
+ {
+ m_pGC = pGC;
+ m_eLang = eLang;
+ }
+
+ static bool BEnsureCleanUTF8Truncation( char *unicodeOutput );
+
+ virtual locchar_t *Find( const char *pchKey ) const;
+
+ // new interface
+ virtual void ConvertLoccharToANSI ( const locchar_t *loc_In, CUtlConstString *out_ansi ) const;
+ virtual void ConvertLoccharToUnicode( const locchar_t *loc_In, CUtlConstWideString *out_unicode ) const;
+ virtual void ConvertUTF8ToLocchar ( const char *utf8_In, CUtlConstStringBase<locchar_t> *out_loc ) const;
+
+ // old C-style interface
+ virtual int ConvertLoccharToANSI( const locchar_t *loc, char *ansi, int ansiBufferSize ) const;
+ virtual int ConvertLoccharToUnicode( const locchar_t *loc, wchar_t *unicode, int unicodeBufferSize ) const;
+
+ virtual void ConvertUTF8ToLocchar( const char *utf8, locchar_t *locchar, int loccharBufferSize ) const;
+
+ virtual ELanguage GetELang() const { return m_eLang; }
+
+private:
+ CGCGameBase *m_pGC;
+ ELanguage m_eLang;
+};
+
+
+#else
+
+#include "vgui/ILocalize.h"
+extern vgui::ILocalize *g_pVGuiLocalize;
+
+// Game localization is handled by vgui
+class CVGUILocalizationProvider : public CLocalizationProvider
+{
+public:
+ CVGUILocalizationProvider();
+
+ virtual locchar_t *Find( const char *pchKey ) const;
+
+ // new interface
+ virtual void ConvertLoccharToANSI ( const locchar_t *loc_In, CUtlConstString *out_ansi ) const;
+ virtual void ConvertLoccharToUnicode( const locchar_t *loc_In, CUtlConstWideString *out_unicode ) const;
+ virtual void ConvertUTF8ToLocchar ( const char *utf8_In, CUtlConstStringBase<locchar_t> *out_loc ) const;
+
+ // old C-style interface
+ virtual int ConvertLoccharToANSI( const locchar_t *loc, char *ansi, int ansiBufferSize ) const;
+ virtual int ConvertLoccharToUnicode( const locchar_t *loc, wchar_t *unicode, int unicodeBufferSize ) const;
+
+ virtual void ConvertUTF8ToLocchar( const char *utf8, locchar_t *locchar, int loccharBufferSize ) const;
+
+ virtual ELanguage GetELang() const { return k_Lang_None; }
+
+};
+#endif
+
+#endif // LOCALIZATION_PROVIDER_H \ No newline at end of file