summaryrefslogtreecommitdiff
path: root/game/shared/econ/econ_item_schema.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'game/shared/econ/econ_item_schema.cpp')
-rw-r--r--game/shared/econ/econ_item_schema.cpp9705
1 files changed, 9705 insertions, 0 deletions
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