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

9705 lines
350 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: EconItemSchema: Defines a schema for econ items
  4. //
  5. //=============================================================================
  6. #include "cbase.h"
  7. #include "econ_item_schema.h"
  8. #include "tier1/fmtstr.h"
  9. #include "tier1/UtlSortVector.h"
  10. #include "tier2/tier2.h"
  11. #include "filesystem.h"
  12. #include "schemainitutils.h"
  13. #include "gcsdk/gcsdk_auto.h"
  14. #include "rtime.h"
  15. #include "item_selection_criteria.h"
  16. #include "crypto.h"
  17. #include "checksum_sha1.h"
  18. #include <google/protobuf/text_format.h>
  19. #include <string.h>
  20. #include "materialsystem/imaterialsystem.h"
  21. #include "materialsystem/itexture.h"
  22. #include "materialsystem/itexturecompositor.h"
  23. #if ( defined( _MSC_VER ) && _MSC_VER >= 1900 )
  24. #define timezone _timezone
  25. #define daylight _daylight
  26. #endif
  27. // For holiday-limited loot lists.
  28. #include "econ_holidays.h"
  29. // Only used for startup testing.
  30. #include "econ_item_tools.h"
  31. #if defined(CLIENT_DLL) || defined(GAME_DLL)
  32. #include "econ_item_system.h"
  33. #include "econ_item.h"
  34. #include "activitylist.h"
  35. #if defined(TF_CLIENT_DLL) || defined(TF_DLL)
  36. #include "tf_gcmessages.h"
  37. #endif
  38. #endif
  39. #ifdef GC_DLL
  40. #include "gcgamebase.h"
  41. #include <memory> // unique_ptr
  42. #endif
  43. // memdbgon must be the last include file in a .cpp file!!!
  44. #include "tier0/memdbgon.h"
  45. using namespace GCSDK;
  46. CEconItemSchema & GEconItemSchema()
  47. {
  48. #if defined( EXTERNALTESTS_DLL )
  49. static CEconItemSchema g_econItemSchema;
  50. return g_econItemSchema;
  51. #elif defined( GC_DLL )
  52. return *GEconManager()->GetItemSchema();
  53. #else
  54. return *ItemSystem()->GetItemSchema();
  55. #endif
  56. }
  57. const char *g_szDropTypeStrings[] =
  58. {
  59. "", // Blank and none mean the same thing: stay attached to the body.
  60. "none",
  61. "drop", // The item drops off the body.
  62. "break", // Not implemented, but an example of a type that could be added.
  63. };
  64. const char *g_TeamVisualSections[TEAM_VISUAL_SECTIONS] =
  65. {
  66. "visuals", // TF_TEAM_UNASSIGNED. Visual changes applied to both teams.
  67. NULL, // TF_TEAM_SPECTATOR. Unused.
  68. "visuals_red", // TF_TEAM_RED
  69. "visuals_blu", // TF_TEAM_BLUE
  70. "visuals_mvm_boss", // Hack to override things in MvM at a general level
  71. };
  72. int GetTeamVisualsFromString( const char *pszString )
  73. {
  74. for ( int i = 0; i < TEAM_VISUAL_SECTIONS; i++ )
  75. {
  76. // There's a NULL hidden in g_TeamVisualSections
  77. if ( g_TeamVisualSections[i] && !Q_stricmp( pszString, g_TeamVisualSections[i] ) )
  78. return i;
  79. }
  80. return -1;
  81. }
  82. #if defined(CLIENT_DLL) || defined(GAME_DLL)
  83. // Used to convert strings to ints for wearable animation types
  84. const char *g_WearableAnimTypeStrings[ NUM_WAP_TYPES ] =
  85. {
  86. "on_spawn", // WAP_ON_SPAWN,
  87. "start_building", // WAP_START_BUILDING,
  88. "stop_building", // WAP_STOP_BUILDING,
  89. "start_taunting", // WAP_START_TAUNTING,
  90. "stop_taunting", // WAP_STOP_TAUNTING,
  91. };
  92. #endif
  93. const char *g_AttributeDescriptionFormats[] =
  94. {
  95. "value_is_percentage", // ATTDESCFORM_VALUE_IS_PERCENTAGE,
  96. "value_is_inverted_percentage", // ATTDESCFORM_VALUE_IS_INVERTED_PERCENTAGE
  97. "value_is_additive", // ATTDESCFORM_VALUE_IS_ADDITIVE
  98. "value_is_additive_percentage", // ATTDESCFORM_VALUE_IS_ADDITIVE_PERCENTAGE
  99. "value_is_or", // ATTDESCFORM_VALUE_IS_OR
  100. "value_is_date", // ATTDESCFORM_VALUE_IS_DATE
  101. "value_is_account_id", // ATTDESCFORM_VALUE_IS_ACCOUNT_ID
  102. "value_is_particle_index", // ATTDESCFORM_VALUE_IS_PARTICLE_INDEX -> Could change to "string index"
  103. "value_is_killstreakeffect_index", // ATTDESCFORM_VALUE_IS_KILLSTREAKEFFECT_INDEX -> Could change to "string index"
  104. "value_is_killstreak_idleeffect_index", // ATTDESCFORM_VALUE_IS_KILLSTREAK_IDLEEFFECT_INDEX
  105. "value_is_item_def", // ATTDESCFORM_VALUE_IS_ITEM_DEF
  106. "value_is_from_lookup_table", // ATTDESCFORM_VALUE_IS_FROM_LOOKUP_TABLE
  107. };
  108. const char *g_EffectTypes[NUM_EFFECT_TYPES] =
  109. {
  110. "unusual", // ATTRIB_EFFECT_UNUSUAL,
  111. "strange", // ATTRIB_EFFECT_STRANGE,
  112. "neutral", // ATTRIB_EFFECT_NEUTRAL = 0,
  113. "positive", // ATTRIB_EFFECT_POSITIVE,
  114. "negative", // ATTRIB_EFFECT_NEGATIVE,
  115. };
  116. //-----------------------------------------------------------------------------
  117. // Purpose: Set the capabilities bitfield based on whether the entry is true/false.
  118. //-----------------------------------------------------------------------------
  119. const char *g_Capabilities[] =
  120. {
  121. "paintable", // ITEM_CAP_PAINTABLE
  122. "nameable", // ITEM_CAP_NAMEABLE
  123. "decodable", // ITEM_CAP_DECODABLE
  124. "can_craft_if_purchased", // ITEM_CAP_CAN_BE_CRAFTED_IF_PURCHASED
  125. "can_customize_texture", // ITEM_CAP_CAN_CUSTOMIZE_TEXTURE
  126. "usable", // ITEM_CAP_USABLE
  127. "usable_gc", // ITEM_CAP_USABLE_GC
  128. "can_gift_wrap", // ITEM_CAP_CAN_GIFT_WRAP
  129. "usable_out_of_game", // ITEM_CAP_USABLE_OUT_OF_GAME
  130. "can_collect", // ITEM_CAP_CAN_COLLECT
  131. "can_craft_count", // ITEM_CAP_CAN_CRAFT_COUNT
  132. "can_craft_mark", // ITEM_CAP_CAN_CRAFT_MARK
  133. "paintable_team_colors", // ITEM_CAP_PAINTABLE_TEAM_COLORS
  134. "can_be_restored", // ITEM_CAP_CAN_BE_RESTORED
  135. "strange_parts", // ITEM_CAP_CAN_USE_STRANGE_PARTS
  136. "can_card_upgrade", // ITEM_CAP_CAN_CARD_UPGRADE
  137. "can_strangify", // ITEM_CAP_CAN_STRANGIFY
  138. "can_killstreakify", // ITEM_CAP_CAN_KILLSTREAKIFY
  139. "can_consume", // ITEM_CAP_CAN_CONSUME_ITEMS
  140. "can_spell_page", // ITEM_CAP_CAN_SPELLBOOK_PAGE
  141. "has_slots", // ITEM_CAP_HAS_SLOTS
  142. "duck_upgradable", // ITEM_CAP_DUCK_UPGRADABLE
  143. "can_unusualify", // ITEM_CAP_CAN_UNUSUALIFY
  144. };
  145. COMPILE_TIME_ASSERT( ARRAYSIZE(g_Capabilities) == NUM_ITEM_CAPS );
  146. #define RETURN_ATTRIBUTE_STRING( attrib_name, default_string ) \
  147. static CSchemaAttributeDefHandle pAttribString( attrib_name ); \
  148. const char *pchResultAttribString = default_string; \
  149. FindAttribute_UnsafeBitwiseCast< CAttribute_String >( this, pAttribString, &pchResultAttribString ); \
  150. return pchResultAttribString;
  151. #define RETURN_ATTRIBUTE_STRING_F( func_name, attrib_name, default_string ) \
  152. const char *func_name( void ) const { RETURN_ATTRIBUTE_STRING( attrib_name, default_string ) }
  153. static void ParseCapability( item_capabilities_t &capsBitfield, KeyValues* pEntry )
  154. {
  155. int idx = StringFieldToInt( pEntry->GetName(), g_Capabilities, ARRAYSIZE(g_Capabilities) );
  156. if ( idx < 0 )
  157. {
  158. return;
  159. }
  160. int bit = 1 << idx;
  161. if ( pEntry->GetBool() )
  162. {
  163. (int&)capsBitfield |= bit;
  164. }
  165. else
  166. {
  167. (int&)capsBitfield &= ~bit;
  168. }
  169. }
  170. #ifdef GC_DLL
  171. static bool BGetPaymentRule( KeyValues *pKVRule, EPaymentRuleType *out_pePaymentRuleType, double *out_pRevenueShare )
  172. {
  173. Assert( out_pePaymentRuleType );
  174. Assert( out_pRevenueShare );
  175. struct payment_rule_lookup_t
  176. {
  177. const char *m_pszStr;
  178. EPaymentRuleType m_eRuleType;
  179. };
  180. static payment_rule_lookup_t s_Lookup[] =
  181. {
  182. { "workshop_revenue_share", kPaymentRule_SteamWorkshopFileID },
  183. { "partner_revenue_share", kPaymentRule_PartnerSteamID },
  184. { "bundle_revenue_share", kPaymentRule_Bundle },
  185. };
  186. for ( int i = 0; i < ARRAYSIZE( s_Lookup ); i++ )
  187. {
  188. KeyValues *pKVKey = pKVRule->FindKey( s_Lookup[i].m_pszStr );
  189. if ( !pKVKey )
  190. continue;
  191. *out_pePaymentRuleType = s_Lookup[i].m_eRuleType;
  192. *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
  193. return true;
  194. }
  195. return false;
  196. }
  197. #endif // GC_DLL
  198. //-----------------------------------------------------------------------------
  199. // Purpose: CEconItemSeriesDefinition
  200. //-----------------------------------------------------------------------------
  201. CEconItemSeriesDefinition::CEconItemSeriesDefinition( void )
  202. : m_nValue( INT_MAX )
  203. {
  204. }
  205. //-----------------------------------------------------------------------------
  206. // Purpose: Copy constructor
  207. //-----------------------------------------------------------------------------
  208. CEconItemSeriesDefinition::CEconItemSeriesDefinition( const CEconItemSeriesDefinition &that )
  209. {
  210. ( *this ) = that;
  211. }
  212. //-----------------------------------------------------------------------------
  213. // Purpose: Operator=
  214. //-----------------------------------------------------------------------------
  215. CEconItemSeriesDefinition &CEconItemSeriesDefinition::operator=( const CEconItemSeriesDefinition &rhs )
  216. {
  217. m_nValue = rhs.m_nValue;
  218. m_strName = rhs.m_strName;
  219. return *this;
  220. }
  221. //-----------------------------------------------------------------------------
  222. // Purpose: Initialize the quality definition
  223. // Input: pKVQuality - The KeyValues representation of the quality
  224. // schema - The overall item schema for this attribute
  225. // pVecErrors - An optional vector that will contain error messages if
  226. // the init fails.
  227. // Output: True if initialization succeeded, false otherwise
  228. //-----------------------------------------------------------------------------
  229. bool CEconItemSeriesDefinition::BInitFromKV( KeyValues *pKVSeries, CUtlVector<CUtlString> *pVecErrors /* = NULL */ )
  230. {
  231. m_nValue = pKVSeries->GetInt( "value", -1 );
  232. m_strName = pKVSeries->GetName();
  233. m_strLockKey = pKVSeries->GetString( "loc_key" );
  234. m_strUiFile = pKVSeries->GetString( "ui" );
  235. // Check for required fields
  236. SCHEMA_INIT_CHECK(
  237. NULL != pKVSeries->FindKey( "value" ),
  238. "Quality definition %s: Missing required field \"value\"", pKVSeries->GetName() );
  239. return SCHEMA_INIT_SUCCESS();
  240. }
  241. //-----------------------------------------------------------------------------
  242. // Purpose: Constructor
  243. //-----------------------------------------------------------------------------
  244. CEconItemQualityDefinition::CEconItemQualityDefinition( void )
  245. : m_nValue( INT_MAX )
  246. , m_bCanSupportSet( false )
  247. {
  248. }
  249. //-----------------------------------------------------------------------------
  250. // Purpose: Copy constructor
  251. //-----------------------------------------------------------------------------
  252. CEconItemQualityDefinition::CEconItemQualityDefinition( const CEconItemQualityDefinition &that )
  253. {
  254. (*this) = that;
  255. }
  256. //-----------------------------------------------------------------------------
  257. // Purpose: Operator=
  258. //-----------------------------------------------------------------------------
  259. CEconItemQualityDefinition &CEconItemQualityDefinition::operator=( const CEconItemQualityDefinition &rhs )
  260. {
  261. m_nValue = rhs.m_nValue;
  262. m_strName = rhs.m_strName;
  263. m_bCanSupportSet = rhs.m_bCanSupportSet;
  264. return *this;
  265. }
  266. //-----------------------------------------------------------------------------
  267. // Purpose: Initialize the quality definition
  268. // Input: pKVQuality - The KeyValues representation of the quality
  269. // schema - The overall item schema for this attribute
  270. // pVecErrors - An optional vector that will contain error messages if
  271. // the init fails.
  272. // Output: True if initialization succeeded, false otherwise
  273. //-----------------------------------------------------------------------------
  274. bool CEconItemQualityDefinition::BInitFromKV( KeyValues *pKVQuality, CUtlVector<CUtlString> *pVecErrors /* = NULL */ )
  275. {
  276. m_nValue = pKVQuality->GetInt( "value", -1 );
  277. m_strName = pKVQuality->GetName();
  278. m_bCanSupportSet = pKVQuality->GetBool( "canSupportSet" );
  279. #ifdef GC_DLL
  280. m_strHexColor = pKVQuality->GetString( "hexColor" );
  281. #endif // GC_DLL
  282. // Check for required fields
  283. SCHEMA_INIT_CHECK(
  284. NULL != pKVQuality->FindKey( "value" ),
  285. "Quality definition %s: Missing required field \"value\"", pKVQuality->GetName() );
  286. #if defined(CLIENT_DLL) || defined(GAME_DLL)
  287. return SCHEMA_INIT_SUCCESS();
  288. #endif // GC_DLL
  289. // Check for data consistency
  290. SCHEMA_INIT_CHECK(
  291. 0 != Q_stricmp( GetName(), "any" ),
  292. "Quality definition any: The quality name \"any\" is a reserved keyword and cannot be used." );
  293. SCHEMA_INIT_CHECK(
  294. m_nValue != k_unItemQuality_Any,
  295. "Quality definition %s: Invalid value (%d). It is reserved for Any", GetName(), k_unItemQuality_Any );
  296. return SCHEMA_INIT_SUCCESS();
  297. }
  298. //-----------------------------------------------------------------------------
  299. // CEconItemRarityDefinition
  300. //-----------------------------------------------------------------------------
  301. CEconItemRarityDefinition::CEconItemRarityDefinition( void )
  302. : m_nValue( INT_MAX )
  303. , m_nLootlistWeight( 0 )
  304. {
  305. }
  306. //-----------------------------------------------------------------------------
  307. // Purpose: Initialize the rarity definition
  308. //-----------------------------------------------------------------------------
  309. bool CEconItemRarityDefinition::BInitFromKV( KeyValues *pKVRarity, KeyValues *pKVRarityWeights, CEconItemSchema &pschema, CUtlVector<CUtlString> *pVecErrors /* = NULL */ )
  310. {
  311. m_nValue = pKVRarity->GetInt( "value", -1 );
  312. m_strName = pKVRarity->GetName();
  313. m_strLocKey = pKVRarity->GetString( "loc_key" );
  314. m_strWepLocKey = pKVRarity->GetString( "loc_key_weapon" );
  315. m_iAttribColor = GetAttribColorIndexForName( pKVRarity->GetString( "color" ) );
  316. m_strDropSound = pKVRarity->GetString( "drop_sound" );
  317. m_strNextRarity = pKVRarity->GetString( "next_rarity" ); // Not required.
  318. #ifdef GC_DLL
  319. if ( pKVRarityWeights )
  320. {
  321. m_nLootlistWeight = pKVRarityWeights->GetInt( m_strName, 0 );
  322. }
  323. #endif
  324. //
  325. // Check for required fields
  326. SCHEMA_INIT_CHECK(
  327. NULL != pKVRarity->FindKey( "value" ),
  328. "Rarity definition %s: Missing required field \"value\"", pKVRarity->GetName() );
  329. SCHEMA_INIT_CHECK(
  330. NULL != pKVRarity->FindKey( "loc_key" ),
  331. "Rarity definition %s: Missing required field \"loc_key\"", pKVRarity->GetName() );
  332. return SCHEMA_INIT_SUCCESS();
  333. }
  334. //-----------------------------------------------------------------------------
  335. // Purpose:
  336. //-----------------------------------------------------------------------------
  337. bool CEconColorDefinition::BInitFromKV( KeyValues *pKVColor, CUtlVector<CUtlString> *pVecErrors /* = NULL */ )
  338. {
  339. m_strName = pKVColor->GetName();
  340. m_strColorName = pKVColor->GetString( "color_name" );
  341. #ifdef GC_DLL
  342. m_strHexColor = pKVColor->GetString( "hex_color" );
  343. #endif // GC_DLL
  344. SCHEMA_INIT_CHECK(
  345. !m_strColorName.IsEmpty(),
  346. "Quality definition %s: missing \"color_name\"", GetName() );
  347. #ifdef GC_DLL
  348. SCHEMA_INIT_CHECK(
  349. !m_strHexColor.IsEmpty(),
  350. "Quality definition %s: missing \"hex_color\"", GetName() );
  351. #endif // GC_DLL
  352. return SCHEMA_INIT_SUCCESS();
  353. }
  354. //-----------------------------------------------------------------------------
  355. //
  356. //-----------------------------------------------------------------------------
  357. CEconItemSetDefinition::CEconItemSetDefinition( void )
  358. : m_pszName( NULL )
  359. , m_pszLocalizedName( NULL )
  360. , m_iBundleItemDef( INVALID_ITEM_DEF_INDEX )
  361. , m_bIsHiddenSet( false )
  362. {
  363. }
  364. //-----------------------------------------------------------------------------
  365. // Purpose: Copy constructor
  366. //-----------------------------------------------------------------------------
  367. CEconItemSetDefinition::CEconItemSetDefinition( const CEconItemSetDefinition &that )
  368. {
  369. (*this) = that;
  370. }
  371. //-----------------------------------------------------------------------------
  372. // Purpose: Operator=
  373. //-----------------------------------------------------------------------------
  374. CEconItemSetDefinition &CEconItemSetDefinition::operator=( const CEconItemSetDefinition &other )
  375. {
  376. m_pszName = other.m_pszName;
  377. m_pszLocalizedName = other.m_pszLocalizedName;
  378. m_iItemDefs = other.m_iItemDefs;
  379. m_iAttributes = other.m_iAttributes;
  380. m_iBundleItemDef = other.m_iBundleItemDef;
  381. m_bIsHiddenSet = other.m_bIsHiddenSet;
  382. return *this;
  383. }
  384. //-----------------------------------------------------------------------------
  385. //
  386. //-----------------------------------------------------------------------------
  387. bool CEconItemSetDefinition::BInitFromKV( KeyValues *pKVItemSet, CUtlVector<CUtlString> *pVecErrors )
  388. {
  389. m_pszName = pKVItemSet->GetName();
  390. m_iBundleItemDef = INVALID_ITEM_DEF_INDEX;
  391. const char *pszBundleName = pKVItemSet->GetString( "store_bundle" );
  392. if ( pszBundleName && pszBundleName[0] )
  393. {
  394. CEconItemDefinition *pDef = GetItemSchema()->GetItemDefinitionByName( pszBundleName );
  395. if ( pDef )
  396. {
  397. m_iBundleItemDef = pDef->GetDefinitionIndex();
  398. }
  399. SCHEMA_INIT_CHECK(
  400. pDef != NULL,
  401. "Item set %s: Bundle definition \"%s\" was not found", m_pszName, pszBundleName );
  402. }
  403. m_pszLocalizedName = pKVItemSet->GetString( "name", NULL );
  404. m_bIsHiddenSet = pKVItemSet->GetBool( "is_hidden_set", false );
  405. KeyValues *pKVItems = pKVItemSet->FindKey( "items" );
  406. if ( pKVItems )
  407. {
  408. FOR_EACH_SUBKEY( pKVItems, pKVItem )
  409. {
  410. const char *pszName = pKVItem->GetName();
  411. CEconItemDefinition *pDef = GetItemSchema()->GetItemDefinitionByName( pszName );
  412. SCHEMA_INIT_CHECK(
  413. pDef != NULL,
  414. "Item set %s: Item definition \"%s\" was not found", m_pszName, pszName );
  415. const item_definition_index_t unDefIndex = pDef->GetDefinitionIndex();
  416. SCHEMA_INIT_CHECK(
  417. !m_iItemDefs.IsValidIndex( m_iItemDefs.Find( unDefIndex ) ),
  418. "Item set %s: item definition \"%s\" appears multiple times", m_pszName, pszName );
  419. SCHEMA_INIT_CHECK(
  420. !pDef->GetItemSetDefinition(),
  421. "Item set %s: item definition \"%s\" specified in multiple item sets", m_pszName, pszName );
  422. m_iItemDefs.AddToTail( unDefIndex );
  423. pDef->SetItemSetDefinition( this );
  424. // FIXME: hack to work around crafting item criteria
  425. pDef->GetRawDefinition()->SetString( "item_set", m_pszName );
  426. }
  427. }
  428. KeyValues *pKVAttributes = pKVItemSet->FindKey( "attributes" );
  429. if ( pKVAttributes )
  430. {
  431. FOR_EACH_SUBKEY( pKVAttributes, pKVAttribute )
  432. {
  433. const char *pszName = pKVAttribute->GetName();
  434. const CEconItemAttributeDefinition *pAttrDef = GetItemSchema()->GetAttributeDefinitionByName( pszName );
  435. SCHEMA_INIT_CHECK(
  436. pAttrDef != NULL,
  437. "Item set %s: Attribute definition \"%s\" was not found", m_pszName, pszName );
  438. SCHEMA_INIT_CHECK(
  439. pAttrDef->BIsSetBonusAttribute(),
  440. "Item set %s: Attribute definition \"%s\" is not a set bonus attribute", m_pszName, pszName );
  441. int iIndex = m_iAttributes.AddToTail();
  442. m_iAttributes[iIndex].m_iAttribDefIndex = pAttrDef->GetDefinitionIndex();
  443. m_iAttributes[iIndex].m_flValue = pKVAttribute->GetFloat( "value" );
  444. }
  445. }
  446. // Sanity check.
  447. SCHEMA_INIT_CHECK( m_pszLocalizedName != NULL,
  448. "Item set %s: Set contains no localized name", m_pszName );
  449. SCHEMA_INIT_CHECK( m_iItemDefs.Count() > 0,
  450. "Item set %s: Set contains no items", m_pszName );
  451. return SCHEMA_INIT_SUCCESS();
  452. }
  453. //-----------------------------------------------------------------------------
  454. //
  455. //-----------------------------------------------------------------------------
  456. void CEconItemSetDefinition::IterateAttributes( class IEconItemAttributeIterator *pIterator ) const
  457. {
  458. FOR_EACH_VEC( m_iAttributes, i )
  459. {
  460. const itemset_attrib_t& itemsetAttrib = m_iAttributes[i];
  461. const CEconItemAttributeDefinition *pAttrDef = GetItemSchema()->GetAttributeDefinition( itemsetAttrib.m_iAttribDefIndex );
  462. if ( !pAttrDef )
  463. continue;
  464. const ISchemaAttributeType *pAttrType = pAttrDef->GetAttributeType();
  465. Assert( pAttrType );
  466. // We know (and assert) that we only need 32 bits of data to store this attribute
  467. // data. We don't know anything about the type but we'll let the type handle it
  468. // below.
  469. attribute_data_union_t value;
  470. value.asFloat = itemsetAttrib.m_flValue;
  471. if ( !pAttrType->OnIterateAttributeValue( pIterator, pAttrDef, value ) )
  472. return;
  473. }
  474. }
  475. //-----------------------------------------------------------------------------
  476. CEconItemCollectionDefinition::CEconItemCollectionDefinition( void )
  477. : m_pszName( NULL )
  478. , m_pszLocalizedName( NULL )
  479. , m_pszLocalizedDesc( NULL )
  480. , m_iRarityMin( k_unItemRarity_Any )
  481. , m_iRarityMax( k_unItemRarity_Any )
  482. {
  483. }
  484. //-----------------------------------------------------------------------------
  485. //
  486. //-----------------------------------------------------------------------------
  487. static int SortCollectionByRarity( item_definition_index_t const *a, item_definition_index_t const *b )
  488. {
  489. Assert( a );
  490. Assert( *a );
  491. Assert( b );
  492. Assert( *b );
  493. CEconItemDefinition *pItemA = GetItemSchema()->GetItemDefinition( *a );
  494. CEconItemDefinition *pItemB = GetItemSchema()->GetItemDefinition( *b );
  495. if ( !pItemA || !pItemB )
  496. {
  497. AssertMsg( 0, "ItemDef Doesn't exist for sorting" );
  498. return 1;
  499. }
  500. // If same Rarity, leave in current position?
  501. if ( pItemA->GetRarity() == pItemB->GetRarity() && pItemA->GetCustomPainkKitDefinition() && pItemB->GetCustomPainkKitDefinition() )
  502. {
  503. #ifdef CLIENT_DLL
  504. // Sort by localized name
  505. // paintkits sort by paintkit name
  506. auto paintkitA = pItemA->GetCustomPainkKitDefinition();
  507. auto paintkitB = pItemB->GetCustomPainkKitDefinition();
  508. auto paintkitALocName = paintkitA->GetLocalizeName();
  509. auto paintkitBLocName = paintkitB->GetLocalizeName();
  510. auto pkALocalized = g_pVGuiLocalize->Find( paintkitALocName );
  511. auto pkBLocalized = g_pVGuiLocalize->Find( paintkitBLocName );
  512. if ( pkALocalized )
  513. {
  514. if ( pkBLocalized )
  515. {
  516. return V_wcscmp( pkALocalized, pkBLocalized );
  517. }
  518. else
  519. {
  520. return -1;
  521. }
  522. }
  523. else
  524. {
  525. return pkBLocalized ? 1 : -1;
  526. }
  527. #else
  528. return 0;
  529. #endif
  530. }
  531. return ( pItemA->GetRarity() > pItemB->GetRarity() ) ? -1 : 1;
  532. }
  533. //-----------------------------------------------------------------------------
  534. bool CEconItemCollectionDefinition::BInitFromKV( KeyValues *pKVPItemCollection, CUtlVector<CUtlString> *pVecErrors )
  535. {
  536. m_pszName = pKVPItemCollection->GetName();
  537. m_pszLocalizedName = pKVPItemCollection->GetString( "name", NULL );
  538. m_pszLocalizedDesc = pKVPItemCollection->GetString( "description", NULL );
  539. m_bIsReferenceCollection = pKVPItemCollection->GetBool( "is_reference_collection", false );
  540. KeyValues *pKVItems = pKVPItemCollection->FindKey( "items" );
  541. // Create a 'lootlist' from this collection
  542. KeyValues *pCollectionLootList = NULL;
  543. bool bIsLootList = false;
  544. if ( !m_bIsReferenceCollection )
  545. {
  546. pCollectionLootList = new KeyValues( m_pszName );
  547. }
  548. if ( pKVItems )
  549. {
  550. // Traverse rarity items and set rarity
  551. // Create a lootlist if applicable
  552. FOR_EACH_TRUE_SUBKEY( pKVItems, pKVRarity )
  553. {
  554. bIsLootList = true;
  555. // Get the Rarity Value
  556. const CEconItemRarityDefinition *pRarity = GetItemSchema()->GetRarityDefinitionByName( pKVRarity->GetName() );
  557. SCHEMA_INIT_CHECK( pRarity != NULL, "Item collection %s: Rarity type \"%s\" was not found", m_pszName, pKVRarity->GetName() );
  558. // Create a lootlist
  559. if ( !m_bIsReferenceCollection )
  560. {
  561. CFmtStr lootlistname( "%s_%s", m_pszName, pRarity->GetName() );
  562. const char *pszName = V_strdup( lootlistname.Get() );
  563. pKVRarity->SetInt( "rarity", pRarity->GetDBValue() );
  564. SCHEMA_INIT_CHECK( GetItemSchema()->BInsertLootlist( pszName, pKVRarity, pVecErrors ), "Invalid collection lootlist %s", pszName );
  565. KeyValues *pTempRarityKey = pKVRarity->FindKey( "rarity" );
  566. Assert( pTempRarityKey );
  567. pKVRarity->RemoveSubKey( pTempRarityKey );
  568. pTempRarityKey->deleteThis();
  569. pCollectionLootList->SetInt( pszName, pRarity->GetLootlistWeight() );
  570. }
  571. // Items in the Rarity
  572. FOR_EACH_VALUE( pKVRarity, pKVItem )
  573. {
  574. const char *pszName = pKVItem->GetName();
  575. CEconItemDefinition *pDef = GetItemSchema()->GetItemDefinitionByName( pszName );
  576. SCHEMA_INIT_CHECK(
  577. pDef != NULL,
  578. "Item set %s: Item definition \"%s\" was not found", m_pszName, pszName );
  579. const item_definition_index_t unDefIndex = pDef->GetDefinitionIndex();
  580. SCHEMA_INIT_CHECK(
  581. !m_iItemDefs.IsValidIndex( m_iItemDefs.Find( unDefIndex ) ),
  582. "Item Collection %s: item definition \"%s\" appears multiple times", m_pszName, pszName );
  583. m_iItemDefs.AddToTail( unDefIndex );
  584. // Collection Reference
  585. if ( !m_bIsReferenceCollection )
  586. {
  587. SCHEMA_INIT_CHECK(
  588. !pDef->GetItemCollectionDefinition(),
  589. "Item Collection %s: item definition \"%s\" specified in multiple item sets", m_pszName, pszName );
  590. pDef->SetItemCollectionDefinition( this );
  591. }
  592. // Item Rarity
  593. pDef->SetRarity( pRarity->GetDBValue() );
  594. }
  595. }
  596. // Loose Items
  597. FOR_EACH_VALUE( pKVItems, pKVItem )
  598. {
  599. const char *pszName = pKVItem->GetName();
  600. CEconItemDefinition *pDef = GetItemSchema()->GetItemDefinitionByName( pszName );
  601. SCHEMA_INIT_CHECK(
  602. pDef != NULL,
  603. "Item set %s: Item definition \"%s\" was not found", m_pszName, pszName );
  604. const item_definition_index_t unDefIndex = pDef->GetDefinitionIndex();
  605. SCHEMA_INIT_CHECK(
  606. !m_iItemDefs.IsValidIndex( m_iItemDefs.Find( unDefIndex ) ),
  607. "Item Collection %s: item definition \"%s\" appears multiple times", m_pszName, pszName );
  608. m_iItemDefs.AddToTail( unDefIndex );
  609. if ( !m_bIsReferenceCollection )
  610. {
  611. SCHEMA_INIT_CHECK(
  612. !pDef->GetItemCollectionDefinition(),
  613. "Item Collection %s: item definition \"%s\" specified in multiple item sets", m_pszName, pszName );
  614. pDef->SetItemCollectionDefinition( this );
  615. }
  616. }
  617. // Sort by Rarity
  618. m_iItemDefs.Sort( &SortCollectionByRarity );
  619. }
  620. if ( !m_bIsReferenceCollection && bIsLootList )
  621. {
  622. // Insert collection lootlist
  623. GetItemSchema()->BInsertLootlist( m_pszName, pCollectionLootList, pVecErrors );
  624. }
  625. if ( pCollectionLootList )
  626. {
  627. pCollectionLootList->deleteThis();
  628. }
  629. // Sorted high to low
  630. m_iRarityMax = GetItemSchema()->GetItemDefinition( m_iItemDefs[ 0 ] )->GetRarity();
  631. m_iRarityMin = GetItemSchema()->GetItemDefinition( m_iItemDefs[ m_iItemDefs.Count() - 1] )->GetRarity();
  632. // Verify that there is no gaps in the Rarity (would cause crafting problems and makes no sense)
  633. if ( !m_bIsReferenceCollection )
  634. {
  635. int iRarityVerify = m_iRarityMax;
  636. FOR_EACH_VEC( m_iItemDefs, i )
  637. {
  638. int iNextRarity = GetItemSchema()->GetItemDefinition( m_iItemDefs[i] )->GetRarity();
  639. SCHEMA_INIT_CHECK( iRarityVerify - iNextRarity <= 1, "Items in Collection %s: Have a gap in rarity tiers", m_pszName );
  640. iRarityVerify = iNextRarity;
  641. }
  642. }
  643. // Sanity check.
  644. SCHEMA_INIT_CHECK( m_pszLocalizedName != NULL,
  645. "Item Collection %s: Collection contains no localized name", m_pszName );
  646. SCHEMA_INIT_CHECK( m_pszLocalizedDesc != NULL,
  647. "Item Collection %s: Collection contains no localized description", m_pszName );
  648. SCHEMA_INIT_CHECK( m_iItemDefs.Count() > 0,
  649. "Item Collection %s: Collection contains no items", m_pszName );
  650. return SCHEMA_INIT_SUCCESS();
  651. }
  652. //-----------------------------------------------------------------------------
  653. // CEconItemPaintKitDefinition
  654. //-----------------------------------------------------------------------------
  655. CEconItemPaintKitDefinition::CEconItemPaintKitDefinition( void )
  656. : m_pszName( NULL )
  657. {
  658. }
  659. //-----------------------------------------------------------------------------
  660. CEconItemPaintKitDefinition::~CEconItemPaintKitDefinition( void )
  661. {
  662. FOR_EACH_VEC( m_vecPaintKitWearKVP, i )
  663. {
  664. if ( m_vecPaintKitWearKVP[i] )
  665. {
  666. m_vecPaintKitWearKVP[i]->deleteThis();
  667. }
  668. }
  669. m_vecPaintKitWearKVP.Purge();
  670. }
  671. bool VerifyPaintKitComposite( KeyValues *pKVWearInputItems, const char* pName, int iWearLevel, CUtlVector<CUtlString> *pVecErrors )
  672. {
  673. SCHEMA_INIT_CHECK( pKVWearInputItems != NULL, "Paint Kit %s: Does not contain Wear Level %d", pName, iWearLevel );
  674. #ifdef CLIENT_DLL
  675. int w = 1;
  676. int h = 1;
  677. int seed = 0;
  678. ITextureCompositor* pWeaponSkinBaseCompositor = NULL;
  679. SafeAssign( &pWeaponSkinBaseCompositor, materials->NewTextureCompositor( w, h, pName, TF_TEAM_RED, seed, pKVWearInputItems, TEX_COMPOSITE_CREATE_FLAGS_VERIFY_SCHEMA_ONLY ) );
  680. SCHEMA_INIT_CHECK( pWeaponSkinBaseCompositor != NULL, "Could Not Create Weapon Skin Compositor for [%s][Wear %d][Team Red]", pName, iWearLevel);
  681. SafeRelease( &pWeaponSkinBaseCompositor );
  682. SafeAssign( &pWeaponSkinBaseCompositor, materials->NewTextureCompositor( w, h, pName, TF_TEAM_BLUE, seed, pKVWearInputItems, TEX_COMPOSITE_CREATE_FLAGS_VERIFY_SCHEMA_ONLY ) );
  683. SCHEMA_INIT_CHECK( pWeaponSkinBaseCompositor != NULL, "Could Not Create Weapon Skin Compositor for [%s][Wear %d][Team BLUE]", pName, iWearLevel );
  684. SafeRelease( &pWeaponSkinBaseCompositor );
  685. #endif // CLIENT_DLL
  686. return SCHEMA_INIT_SUCCESS();
  687. }
  688. //-----------------------------------------------------------------------------
  689. bool CEconItemPaintKitDefinition::BInitFromKV( KeyValues *pKVPItemPaintKit, CUtlVector<CUtlString> *pVecErrors )
  690. {
  691. m_pszName = pKVPItemPaintKit->GetName();
  692. m_pszLocalizedName = m_pszName; // localization key is same as paintkit name for ease of generation
  693. SCHEMA_INIT_CHECK( m_pszLocalizedName != NULL, "Paint Kit %s: PaintKit contains no localized name", m_pszName );
  694. KeyValues *pKVWearInputItems = NULL;
  695. pKVWearInputItems = pKVPItemPaintKit->FindKey( "wear_level_1", false );
  696. SCHEMA_INIT_CHECK( VerifyPaintKitComposite( pKVWearInputItems, m_pszName, 1, pVecErrors ), "Could Not Create Weapon Skin Compositor for [%s][Wear %d]", m_pszName, 1 );
  697. m_vecPaintKitWearKVP.AddToTail( pKVWearInputItems->MakeCopy() );
  698. pKVWearInputItems = pKVPItemPaintKit->FindKey( "wear_level_2", false );
  699. SCHEMA_INIT_CHECK( VerifyPaintKitComposite( pKVWearInputItems, m_pszName, 2, pVecErrors ), "Could Not Create Weapon Skin Compositor for [%s][Wear %d]", m_pszName, 2 );
  700. m_vecPaintKitWearKVP.AddToTail( pKVWearInputItems->MakeCopy() );
  701. pKVWearInputItems = pKVPItemPaintKit->FindKey( "wear_level_3", false );
  702. SCHEMA_INIT_CHECK( VerifyPaintKitComposite( pKVWearInputItems, m_pszName, 3, pVecErrors ), "Could Not Create Weapon Skin Compositor for [%s][Wear %d]", m_pszName, 3 );
  703. m_vecPaintKitWearKVP.AddToTail( pKVWearInputItems->MakeCopy() );
  704. pKVWearInputItems = pKVPItemPaintKit->FindKey( "wear_level_4", false );
  705. SCHEMA_INIT_CHECK( VerifyPaintKitComposite( pKVWearInputItems, m_pszName, 4, pVecErrors ), "Could Not Create Weapon Skin Compositor for [%s][Wear %d]", m_pszName, 4 );
  706. m_vecPaintKitWearKVP.AddToTail( pKVWearInputItems->MakeCopy() );
  707. pKVWearInputItems = pKVPItemPaintKit->FindKey( "wear_level_5", false );
  708. SCHEMA_INIT_CHECK( VerifyPaintKitComposite( pKVWearInputItems, m_pszName, 5, pVecErrors ), "Could Not Create Weapon Skin Compositor for [%s][Wear %d]", m_pszName, 5 );
  709. m_vecPaintKitWearKVP.AddToTail( pKVWearInputItems->MakeCopy() );
  710. return SCHEMA_INIT_SUCCESS();
  711. }
  712. KeyValues *CEconItemPaintKitDefinition::GetPaintKitWearKV( int nWear )
  713. {
  714. // Wear is 1-5 but this vec is 0-4
  715. int iIndex = nWear - 1;
  716. if ( !m_vecPaintKitWearKVP.IsValidIndex( iIndex ) )
  717. {
  718. iIndex = 0;
  719. Assert( m_vecPaintKitWearKVP.IsValidIndex( iIndex ) );
  720. DevMsg( "Invalid Paint Kit or Paint Kit Wear Entry (%s) Wear (%d).", m_pszName, nWear );
  721. return NULL;
  722. }
  723. return m_vecPaintKitWearKVP[iIndex];
  724. }
  725. //-----------------------------------------------------------------------------
  726. // CEconOperationDefinition
  727. //-----------------------------------------------------------------------------
  728. CEconOperationDefinition::CEconOperationDefinition( void )
  729. : m_pszName( NULL )
  730. , m_unRequiredItemDefIndex( INVALID_ITEM_DEF_INDEX )
  731. , m_unGatewayItemDefIndex( INVALID_ITEM_DEF_INDEX )
  732. {
  733. }
  734. //-----------------------------------------------------------------------------
  735. CEconOperationDefinition::~CEconOperationDefinition( void )
  736. {
  737. if ( m_pKVItem )
  738. m_pKVItem->deleteThis();
  739. m_pKVItem = NULL;
  740. }
  741. //-----------------------------------------------------------------------------
  742. bool CEconOperationDefinition::BInitFromKV( KeyValues *pKVPOperation, CUtlVector<CUtlString> *pVecErrors )
  743. {
  744. m_unOperationID = V_atoi( pKVPOperation->GetName() );
  745. m_pszName = pKVPOperation->GetString( "name", NULL );
  746. SCHEMA_INIT_CHECK( m_pszName != NULL, "OperationDefinition %s does not have 'name'", m_pszName );
  747. // initialize required item def index if we specified one
  748. const char *pszRequiredName = pKVPOperation->GetString( "required_item_name", NULL );
  749. if ( pszRequiredName )
  750. {
  751. CEconItemDefinition *pDef = GetItemSchema()->GetItemDefinitionByName( pszRequiredName );
  752. SCHEMA_INIT_CHECK( pDef != NULL, "OperationDefinition couldn't find item def from required name '%s'", pszRequiredName );
  753. m_unRequiredItemDefIndex = pDef->GetDefinitionIndex();
  754. }
  755. const char *pszGatewayItemName = pKVPOperation->GetString( "gateway_item_name", NULL );
  756. if ( pszGatewayItemName )
  757. {
  758. CEconItemDefinition *pDef = GetItemSchema()->GetItemDefinitionByName( pszGatewayItemName );
  759. SCHEMA_INIT_CHECK( pDef != NULL, "OperationDefinition couldn't find item def from gateway name '%s'", pszGatewayItemName );
  760. m_unGatewayItemDefIndex = pDef->GetDefinitionIndex();
  761. }
  762. if ( m_unGatewayItemDefIndex != INVALID_ITEM_DEF_INDEX )
  763. {
  764. 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 );
  765. }
  766. const char *pszOperationStartDate = pKVPOperation->GetString( "operation_start_date", NULL );
  767. SCHEMA_INIT_CHECK( pszOperationStartDate != NULL, "OperationDefinition %s does not have 'operation_start_date'", m_pszName );
  768. m_OperationStartDate = ( pszOperationStartDate && pszOperationStartDate[0] )
  769. ? CRTime::RTime32FromFmtString( "YYYY-MM-DD hh:mm:ss" , pszOperationStartDate )
  770. : RTime32(0);
  771. const char *pszDropEndDate = pKVPOperation->GetString( "stop_giving_to_player_date", NULL );
  772. SCHEMA_INIT_CHECK( pszDropEndDate != NULL, "OperationDefinition %s does not have 'stop_giving_to_player_date'", m_pszName );
  773. m_StopGivingToPlayerDate = ( pszDropEndDate && pszDropEndDate[0] )
  774. ? CRTime::RTime32FromFmtString( "YYYY-MM-DD hh:mm:ss" , pszDropEndDate )
  775. : RTime32(0);
  776. const char *pszOperationEndDate = pKVPOperation->GetString( "stop_adding_to_queue_date", NULL );
  777. SCHEMA_INIT_CHECK( pszOperationEndDate != NULL, "OperationDefinition %s does not have 'stop_adding_to_queue_date'", m_pszName );
  778. m_StopAddingToQueueDate = ( pszOperationEndDate && pszOperationEndDate[0] )
  779. ? CRTime::RTime32FromFmtString( "YYYY-MM-DD hh:mm:ss" , pszOperationEndDate )
  780. : RTime32(0);
  781. m_pszQuestLogResFile = pKVPOperation->GetString( "quest_log_res_file", NULL );
  782. m_pszQuestListResFile = pKVPOperation->GetString( "quest_list_res_file", NULL );
  783. m_pszOperationLootList = pKVPOperation->GetString( "operation_lootlist" );
  784. SCHEMA_INIT_CHECK( m_pszOperationLootList != NULL, "OperationDefinition %s does not have 'operation_lootlist'", m_pszName );
  785. m_bIsCampaign = pKVPOperation->GetBool( "is_campaign" );
  786. m_unMaxDropCount = pKVPOperation->GetInt( "max_drop_count" );
  787. #ifdef GC_DLL
  788. m_rtQueueFreqMin = pKVPOperation->GetFloat( "queue_freq_min" ) * k_nSecondsPerHour; // converts specified hours to seconds
  789. SCHEMA_INIT_CHECK( m_rtQueueFreqMin != 0.f, "OperationDefinition %s does not have 'queue_freq_min'", m_pszName );
  790. m_rtQueueFreqMax = pKVPOperation->GetFloat( "queue_freq_max" ) * k_nSecondsPerHour; // converts specified hours to seconds
  791. SCHEMA_INIT_CHECK( m_rtQueueFreqMax != 0.f, "OperationDefinition %s does not have 'queue_freq_max'", m_pszName );
  792. SCHEMA_INIT_CHECK( m_rtQueueFreqMin <= m_rtQueueFreqMax, "OperationDefinition %s 'queue_freq_min' must be less than 'queue_freq_max'", m_pszName );
  793. m_rtDropFreqMin = pKVPOperation->GetFloat( "drop_freq_min" ) * k_nSecondsPerHour; // converts specified hours to seconds
  794. SCHEMA_INIT_CHECK( m_rtDropFreqMin != 0.f, "OperationDefinition %s does not have 'drop_freq_min'", m_pszName );
  795. m_rtDropFreqMax = pKVPOperation->GetFloat( "drop_freq_max" ) * k_nSecondsPerHour; // converts specified hours to seconds
  796. SCHEMA_INIT_CHECK( m_rtDropFreqMax != 0.f, "OperationDefinition %s does not have 'drop_freq_max'", m_pszName );
  797. SCHEMA_INIT_CHECK( m_rtDropFreqMin <= m_rtDropFreqMax, "OperationDefinition %s 'drop_freq_min' must be less than 'drop_freq_max'", m_pszName );
  798. m_unSeed = pKVPOperation->GetInt( "seed_drops" );
  799. m_unMaxHeldDrops = pKVPOperation->GetInt( "max_held_drops" );
  800. m_nMaxQueueCount = pKVPOperation->GetInt( "max_queue_count" );
  801. m_unMaxDropPerThink = pKVPOperation->GetInt( "max_drop_per_think", 1 );
  802. m_pszContractRewardLootlist[ REWARD_CASE ] = pKVPOperation->GetString( "contract_reward_case_lootlist" );
  803. m_pszContractRewardLootlist[ REWARD_WEAPON ] = pKVPOperation->GetString( "contract_reward_weapon_lootlist" );
  804. #endif // GC_DLL
  805. m_pKVItem = pKVPOperation->MakeCopy();
  806. return SCHEMA_INIT_SUCCESS();
  807. }
  808. #ifdef GC_DLL
  809. #ifdef STAGING_ONLY
  810. GCConVar gc_quick_operation_drop_name( "gc_quick_operation_drop_name", "" );
  811. GCConVar gc_quick_operation_drop_rate( "gc_quick_operation_drop_rate", "10" );
  812. #endif // STAGING_ONLY
  813. RTime32 CEconOperationDefinition::GetMinQueueFreq() const
  814. {
  815. #ifdef STAGING_ONLY
  816. if ( Q_stricmp( gc_quick_operation_drop_name.GetString(), m_pszName ) == 0 )
  817. {
  818. return gc_quick_operation_drop_rate.GetInt();
  819. }
  820. #endif
  821. return m_rtQueueFreqMin;
  822. }
  823. RTime32 CEconOperationDefinition::GetMaxQueueFreq() const
  824. {
  825. #ifdef STAGING_ONLY
  826. if ( Q_stricmp( gc_quick_operation_drop_name.GetString(), m_pszName ) == 0 )
  827. {
  828. return gc_quick_operation_drop_rate.GetInt() + 2;
  829. }
  830. #endif
  831. return m_rtQueueFreqMax;
  832. }
  833. RTime32 CEconOperationDefinition::GetMinDropFreq() const
  834. {
  835. #ifdef STAGING_ONLY
  836. if ( Q_stricmp( gc_quick_operation_drop_name.GetString(), m_pszName ) == 0 )
  837. {
  838. return gc_quick_operation_drop_rate.GetInt();
  839. }
  840. #endif
  841. return m_rtDropFreqMin;
  842. }
  843. RTime32 CEconOperationDefinition::GetMaxDropFreq() const
  844. {
  845. #ifdef STAGING_ONLY
  846. if ( Q_stricmp( gc_quick_operation_drop_name.GetString(), m_pszName ) == 0 )
  847. {
  848. return gc_quick_operation_drop_rate.GetInt() + 2;
  849. }
  850. #endif
  851. return m_rtDropFreqMax;
  852. }
  853. //-----------------------------------------------------------------------------
  854. // Purpose:
  855. //-----------------------------------------------------------------------------
  856. bool BCommonInitPropertyGeneratorsFromKV( const char *pszContext, CUtlVector<const IEconItemPropertyGenerator *> *out_pvecGenerators, KeyValues *pKV, CUtlVector<CUtlString> *pVecErrors )
  857. {
  858. // Forward declaration so factory functions can be wherever.
  859. IEconItemPropertyGenerator *CreateChangeQualityGenerator( KeyValues *, CUtlVector<CUtlString> * );
  860. IEconItemPropertyGenerator *CreateRandomEvenChanceAttrGenerator( KeyValues *, CUtlVector<CUtlString> * );
  861. IEconItemPropertyGenerator *CreateUniformLineItemLootListGenerator( KeyValues *, CUtlVector<CUtlString> * );
  862. IEconItemPropertyGenerator *CreateDynamicAttrsGenerator( KeyValues *, CUtlVector<CUtlString> * );
  863. // "Factory".
  864. struct econ_item_property_generator_factory_entry_t
  865. {
  866. const char *m_pszGeneratorName;
  867. IEconItemPropertyGenerator * (* m_funcCreateGeneratorInstance)( KeyValues *, CUtlVector<CUtlString> * );
  868. };
  869. static econ_item_property_generator_factory_entry_t s_Generators[] =
  870. {
  871. { "change_quality", &CreateChangeQualityGenerator },
  872. { "random_even_chance_attr", &CreateRandomEvenChanceAttrGenerator },
  873. { "uniform_line_item_loot_list", &CreateUniformLineItemLootListGenerator },
  874. { "dynamic_attrs", &CreateDynamicAttrsGenerator },
  875. };
  876. Assert( out_pvecGenerators );
  877. Assert( pVecErrors );
  878. // No input data means "we succeeded here".
  879. if ( !pKV )
  880. return true;
  881. // Handle each generator one at a time. We'll try to initialize the whole set to get as many
  882. // errors as possible and then return accumulated errors.
  883. FOR_EACH_SUBKEY( pKV, pKVGenerator )
  884. {
  885. const char *pszGeneratorName = pKVGenerator->GetName();
  886. IEconItemPropertyGenerator *pGenerator = NULL;
  887. for ( const auto& gen : s_Generators )
  888. {
  889. if ( Q_stricmp( gen.m_pszGeneratorName, pszGeneratorName ) != 0 )
  890. continue;
  891. pGenerator = (*gen.m_funcCreateGeneratorInstance)( pKVGenerator, pVecErrors );
  892. SCHEMA_INIT_CHECK( pGenerator != nullptr, "%s: property generator \"%s\" failed to initialize.\n", pszContext, pszGeneratorName );
  893. out_pvecGenerators->AddToTail( pGenerator );
  894. break;
  895. }
  896. // Make sure we found a way to create this instance.
  897. SCHEMA_INIT_CHECK( pGenerator != nullptr, "%s: unknown type for property generator \"%s\".\n", pszContext, pszGeneratorName );
  898. }
  899. return SCHEMA_INIT_SUCCESS();
  900. }
  901. #endif // GC_DLL
  902. //-----------------------------------------------------------------------------
  903. // Dtor
  904. //-----------------------------------------------------------------------------
  905. CEconLootListDefinition::~CEconLootListDefinition( void )
  906. {
  907. #ifdef GC_DLL
  908. m_RandomAttribs.PurgeAndDeleteElements();
  909. m_PropertyGenerators.PurgeAndDeleteElements();
  910. #endif
  911. }
  912. #ifdef GC_DLL
  913. bool CEconLootListDefinition::AddRandomAtrributes( KeyValues *pRandomAttributesKV, CEconItemSchema &pschema, CUtlVector<CUtlString> *pVecErrors /*= NULL*/ )
  914. {
  915. const char *pszAttrName = pRandomAttributesKV->GetName();
  916. // We've found the random attribute block. Parse it.
  917. random_attrib_t *pRandomAttr = pschema.CreateRandomAttribute( m_pszName, pRandomAttributesKV, pVecErrors );
  918. SCHEMA_INIT_CHECK(
  919. NULL != pRandomAttr,
  920. CFmtStr( "Loot List %s: Failed to create random_attrib_t '%s'", m_pszName, pszAttrName ) );
  921. m_RandomAttribs.AddToTail( pRandomAttr );
  922. return true;
  923. }
  924. bool CEconLootListDefinition::AddRandomAttributesFromTemplates( KeyValues *pRandomAttributesKV, CEconItemSchema &pschema, CUtlVector<CUtlString> *pVecErrors /*= NULL*/ )
  925. {
  926. const char *pszAttrName = pRandomAttributesKV->GetName();
  927. // try to find attr by template name
  928. random_attrib_t *pRandomAttrTemplate = pschema.GetRandomAttributeTemplateByName( pszAttrName );
  929. SCHEMA_INIT_CHECK(
  930. NULL != pRandomAttrTemplate,
  931. CFmtStr( "Loot List %s: Couldn't find random_attrib_t '%s' from attribute_templates", m_pszName, pszAttrName ) );
  932. // craete a copy of the template and add to the list
  933. random_attrib_t *pRandomAttr = new random_attrib_t;
  934. *pRandomAttr = *pRandomAttrTemplate;
  935. m_RandomAttribs.AddToTail( pRandomAttr );
  936. return true;
  937. }
  938. #endif // GC_DLL
  939. //-----------------------------------------------------------------------------
  940. //
  941. //-----------------------------------------------------------------------------
  942. static const char *g_pszDefaultRevolvingLootListHeader = "#Econ_Revolving_Loot_List";
  943. bool CEconLootListDefinition::BInitFromKV( KeyValues *pKVLootList, CEconItemSchema &pschema, CUtlVector<CUtlString> *pVecErrors )
  944. {
  945. m_pszName = pKVLootList->GetName();
  946. m_pszLootListHeader = g_pszDefaultRevolvingLootListHeader;
  947. m_pszLootListFooter = NULL;
  948. m_pszCollectionReference = NULL;
  949. m_bPublicListContents = true;
  950. #ifdef GC_DLL
  951. m_iNoDupesIterations = -1; // disable no-dupes functionality by default
  952. m_unRarity = pKVLootList->GetInt( "rarity", k_unItemRarity_Any );
  953. #endif // GC_DLL
  954. bool bCollectionLootList = false;
  955. FOR_EACH_SUBKEY( pKVLootList, pKVListItem )
  956. {
  957. const char *pszName = pKVListItem->GetName();
  958. if ( !Q_strcmp( pszName, "loot_list_header_desc" ) )
  959. {
  960. // Make sure we didn't specify multiple entries.
  961. SCHEMA_INIT_CHECK(
  962. g_pszDefaultRevolvingLootListHeader == m_pszLootListHeader,
  963. "Loot list %s: Multiple header descriptions specified", m_pszName );
  964. m_pszLootListHeader = pKVListItem->GetString();
  965. SCHEMA_INIT_CHECK(
  966. NULL != m_pszLootListHeader,
  967. "Loot list %s: Invalid header description specified", m_pszName );
  968. continue;
  969. }
  970. else if ( !Q_strcmp( pszName, "loot_list_footer_desc" ) )
  971. {
  972. // Make sure we didn't specify multiple entries.
  973. SCHEMA_INIT_CHECK(
  974. NULL == m_pszLootListFooter,
  975. "Loot list %s: Multiple footer descriptions specified", m_pszName );
  976. m_pszLootListFooter = pKVListItem->GetString();
  977. SCHEMA_INIT_CHECK(
  978. NULL != m_pszLootListFooter,
  979. "Loot list %s: Invalid header description specified", m_pszName );
  980. continue;
  981. }
  982. else if ( !Q_strcmp( pszName, "loot_list_collection" ) )
  983. {
  984. // Set name as the collection lootlist name
  985. pszName = pKVListItem->GetString();
  986. m_pszCollectionReference = pszName;
  987. bCollectionLootList = true;
  988. }
  989. else if ( !Q_strcmp( pszName, "hide_lootlist" ) )
  990. {
  991. m_bPublicListContents = !pKVListItem->GetBool( nullptr, true );
  992. continue;
  993. }
  994. else if ( !Q_strcmp( pszName, "rarity" ) )
  995. {
  996. // already parsed up top
  997. continue;
  998. }
  999. #ifdef GC_DLL
  1000. else if ( !Q_strcmp( pszName, "random_attributes" ) )
  1001. {
  1002. AddRandomAtrributes( pKVListItem, pschema, pVecErrors );
  1003. continue;
  1004. }
  1005. else if ( !Q_strcmp( pszName, "attribute_templates" ) )
  1006. {
  1007. FOR_EACH_SUBKEY( pKVListItem, pKVAttributeTemplate )
  1008. {
  1009. if ( pKVAttributeTemplate->GetInt() == 0 )
  1010. continue;
  1011. bool bAdded = AddRandomAttributesFromTemplates( pKVAttributeTemplate, pschema, pVecErrors );
  1012. SCHEMA_INIT_CHECK( bAdded, "Loot list %s: Failed to attribute_templates '%s'", m_pszName, pKVAttributeTemplate->GetName() );
  1013. }
  1014. continue;
  1015. }
  1016. else if ( !Q_strcmp( pszName, "public_list_contents" ) )
  1017. {
  1018. m_bPublicListContents = pKVListItem->GetBool( nullptr, true );
  1019. continue;
  1020. }
  1021. else if ( !Q_stricmp( pszName, "__no_dupes_iter_count" ) )
  1022. {
  1023. m_iNoDupesIterations = pKVListItem->GetInt( nullptr, -1 );
  1024. continue;
  1025. }
  1026. else if ( !Q_strcmp( pszName, "additional_drop" ) )
  1027. {
  1028. float fChance = pKVListItem->GetFloat( "chance", 0.0f );
  1029. bool bPremiumOnly = pKVListItem->GetBool( "premium_only", false );
  1030. const char *pszLootList = pKVListItem->GetString( "loot_list", "" );
  1031. const char *pszRequiredHoliday = pKVListItem->GetString( "required_holiday", NULL );
  1032. const char *pszDropPerdiodStartDate = pKVListItem->GetString( "start_date", NULL );
  1033. const char *pszDropPerdiodEndDate = pKVListItem->GetString( "end_date", NULL );
  1034. int iRequiredHolidayIndex = pszRequiredHoliday
  1035. ? EconHolidays_GetHolidayForString( pszRequiredHoliday )
  1036. : kHoliday_None;
  1037. RTime32 dropStartDate = ( pszDropPerdiodStartDate && pszDropPerdiodStartDate[0] )
  1038. ? CRTime::RTime32FromFmtString( "YYYY-MM-DD hh:mm:ss" , pszDropPerdiodStartDate )
  1039. : RTime32(0); // Default to the start of time
  1040. // Check that if we convert back to a string, we get the same value
  1041. char rtimeBuf[k_RTimeRenderBufferSize];
  1042. SCHEMA_INIT_CHECK(
  1043. pszDropPerdiodStartDate == NULL || Q_strcmp( CRTime::RTime32ToString( dropStartDate, rtimeBuf ), pszDropPerdiodStartDate ) == 0,
  1044. "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 );
  1045. RTime32 dropEndDate = ( pszDropPerdiodEndDate && pszDropPerdiodEndDate[0] )
  1046. ? CRTime::RTime32FromFmtString( "YYYY-MM-DD hh:mm:ss" , pszDropPerdiodEndDate )
  1047. : ~RTime32(0); // Default to the end of time
  1048. // Check that if we convert back to a string, we get the same value
  1049. SCHEMA_INIT_CHECK(
  1050. pszDropPerdiodEndDate == NULL || Q_strcmp( CRTime::RTime32ToString( dropEndDate, rtimeBuf ), pszDropPerdiodEndDate ) == 0,
  1051. "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 );
  1052. SCHEMA_INIT_CHECK(
  1053. fChance > 0.0f && fChance <= 1.0f,
  1054. "Loot list %s: Invalid \"additional_drop\" chance %.2f", m_pszName, fChance );
  1055. SCHEMA_INIT_CHECK(
  1056. pszLootList && pszLootList[0],
  1057. "Loot list %s: Missing \"additional_drop\" loot list name", m_pszName );
  1058. SCHEMA_INIT_CHECK(
  1059. (pszRequiredHoliday == NULL) == (iRequiredHolidayIndex == kHoliday_None),
  1060. "Loot list %s: Unknown or missing holiday \"%s\"", m_pszName, pszRequiredHoliday ? pszRequiredHoliday : "(null)" );
  1061. if ( pszLootList )
  1062. {
  1063. const CEconLootListDefinition *pLootListDef = GetItemSchema()->GetLootListByName( pszLootList );
  1064. SCHEMA_INIT_CHECK(
  1065. pLootListDef,
  1066. "Loot list %s: Invalid \"additional_drop\" loot list \"%s\"", m_pszName, pszLootList );
  1067. if ( pLootListDef )
  1068. {
  1069. drop_period_t dropPeriod = { dropStartDate, dropEndDate };
  1070. loot_list_additional_drop_t additionalDrop = { fChance, bPremiumOnly, pszLootList, iRequiredHolidayIndex, dropPeriod };
  1071. m_AdditionalDrops.AddToTail( additionalDrop );
  1072. }
  1073. }
  1074. continue;
  1075. }
  1076. else if ( !Q_strcmp( pszName, "property_generators" ) )
  1077. {
  1078. SCHEMA_INIT_SUBSTEP( BCommonInitPropertyGeneratorsFromKV( m_pszName, &m_PropertyGenerators, pKVListItem, pVecErrors ) );
  1079. continue;
  1080. }
  1081. #endif // GC_DLL
  1082. int iDef = 0;
  1083. // First, see if we've got a loot list name, for embedded loot lists
  1084. int iIdx = 0;
  1085. if ( GetItemSchema()->GetLootListByName( pszName, &iIdx ) )
  1086. {
  1087. // HACKY: Store loot list indices as negatives, starting from -1, because 0 is a valid item index
  1088. iDef = 0 - 1 - iIdx;
  1089. }
  1090. else
  1091. {
  1092. // Not a loot list. See if it's an item index. Check the first character.
  1093. if ( pszName[0] >= '0' && pszName[0] <= '9' )
  1094. {
  1095. iDef = atoi( pszName );
  1096. }
  1097. else
  1098. {
  1099. // Not a number. See if we can find an item def with a matching name.
  1100. const CEconItemDefinition *pDef = GetItemSchema()->GetItemDefinitionByName( pszName );
  1101. if ( pDef )
  1102. {
  1103. iDef = pDef->GetDefinitionIndex();
  1104. }
  1105. SCHEMA_INIT_CHECK(
  1106. pDef != NULL,
  1107. "Loot list %s: Item definition \"%s\" was not found", m_pszName, pszName );
  1108. }
  1109. }
  1110. // Default to the start dropping at the start of time and end dropping at the end of time
  1111. drop_period_t dropPeriod = { RTime32(0), ~RTime32(0) };
  1112. // Make sure we never put non-enabled items into loot lists
  1113. if ( iDef > 0 )
  1114. {
  1115. const CEconItemDefinition *pItemDef = GetItemSchema()->GetItemDefinition( iDef );
  1116. SCHEMA_INIT_CHECK(
  1117. pItemDef != NULL,
  1118. "Loot list %s: Item definition index \"%s\" (%d) was not found", m_pszName, pszName, iDef );
  1119. static CSchemaAttributeDefHandle pAttribDef_StartDropDate( "start drop date" );
  1120. static CSchemaAttributeDefHandle pAttribDef_EndDropDate( "end drop date" );
  1121. CAttribute_String value;
  1122. // Check for start drop date attribute on this item
  1123. if ( FindAttribute( pItemDef, pAttribDef_StartDropDate, &value ) )
  1124. {
  1125. const char* pszStartDate = value.value().c_str();
  1126. dropPeriod.m_DropStartDate = CRTime::RTime32FromFmtString( "YYYY-MM-DD hh:mm:ss" , pszStartDate );
  1127. // Check that if we convert back to a string, we get the same value
  1128. char rtimeBuf[k_RTimeRenderBufferSize];
  1129. SCHEMA_INIT_CHECK(
  1130. Q_strcmp( CRTime::RTime32ToString( dropPeriod.m_DropStartDate, rtimeBuf ), pszStartDate ) == 0,
  1131. "Malformed start drop date \"%s\" for item %s. Must be of the form \"YYYY-MM-DD hh:mm:ss\"",
  1132. pszStartDate, pItemDef->GetDefinitionName() );
  1133. }
  1134. // Check for end drop date attribute on this item
  1135. if ( FindAttribute( pItemDef, pAttribDef_EndDropDate, &value ) )
  1136. {
  1137. const char* pszEndDate = value.value().c_str();
  1138. dropPeriod.m_DropEndDate = CRTime::RTime32FromFmtString( "YYYY-MM-DD hh:mm:ss" , pszEndDate );
  1139. // Check that if we convert back to a string, we get the same value
  1140. char rtimeBuf[k_RTimeRenderBufferSize];
  1141. SCHEMA_INIT_CHECK(
  1142. Q_strcmp( CRTime::RTime32ToString( dropPeriod.m_DropEndDate, rtimeBuf ), pszEndDate ) == 0,
  1143. "Malformed end drop date \"%s\" for item %s. Must be of the form \"YYYY-MM-DD hh:mm:ss\"", pszEndDate, pItemDef->GetDefinitionName() );
  1144. }
  1145. #ifdef GC_DLL
  1146. if ( pItemDef )
  1147. {
  1148. SCHEMA_INIT_CHECK(
  1149. true == pItemDef->BEnabled(),
  1150. "Loot list %s: Item definition \"%s\" (%d) isn't enabled, not allowed in loot lists", m_pszName, pItemDef->GetDefinitionName(), iDef );
  1151. }
  1152. #endif // GC_DLL
  1153. }
  1154. float fItemWeight = 0.f;
  1155. #ifdef GC_DLL
  1156. fItemWeight = bCollectionLootList ? 1.0f : pKVListItem->GetFloat();
  1157. SCHEMA_INIT_CHECK(
  1158. fItemWeight > 0.0f,
  1159. "Loot list %s: Item definition index \"%s\" (%d) has invalid weight %.2f", m_pszName, pszName, iDef, fItemWeight );
  1160. #endif // GC_DLL
  1161. // Add this item
  1162. drop_item_t dropItem = { iDef, fItemWeight, dropPeriod };
  1163. m_DropList.AddToTail( dropItem );
  1164. }
  1165. #ifdef GC_DLL
  1166. int nNumTimeLimitedItems = 0;
  1167. FOR_EACH_VEC( m_DropList, i )
  1168. {
  1169. // If either the start date or the end date is set, tally it up as a time limited item
  1170. if( m_DropList[i].m_dropPeriod.m_DropStartDate != RTime32(0) || m_DropList[i].m_dropPeriod.m_DropEndDate != ~RTime32(0) )
  1171. {
  1172. ++nNumTimeLimitedItems;
  1173. }
  1174. }
  1175. // Verify that at least one item in a lootlist does not have a drop period that limits when it can drop.
  1176. // This guarantees that we will always drop *something*
  1177. 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 );
  1178. #endif
  1179. return SCHEMA_INIT_SUCCESS();
  1180. }
  1181. #ifdef GC_DLL
  1182. //-----------------------------------------------------------------------------
  1183. // Purpose:
  1184. //-----------------------------------------------------------------------------
  1185. static bool BContainsDuplicateItemDefs( const CUtlVector<CEconLootListDefinition::rolled_item_defs_t>& vecItemDefsA, const CUtlVector<CEconLootListDefinition::rolled_item_defs_t>& vecItemDefsB )
  1186. {
  1187. CUtlHashtable<const CEconItemDefinition *> hashItemDefs;
  1188. auto BPopulateAndLookForDupes = [&] ( const CUtlVector<CEconLootListDefinition::rolled_item_defs_t>& vecItemDefs )
  1189. {
  1190. for ( const auto& rolledItemDef : vecItemDefs )
  1191. {
  1192. if ( hashItemDefs.HasElement( rolledItemDef.m_pItemDef ) )
  1193. return true;
  1194. hashItemDefs.Insert( rolledItemDef.m_pItemDef );
  1195. }
  1196. return false;
  1197. };
  1198. return BPopulateAndLookForDupes( vecItemDefsA )
  1199. || BPopulateAndLookForDupes( vecItemDefsB );
  1200. }
  1201. //-----------------------------------------------------------------------------
  1202. // Purpose:
  1203. //-----------------------------------------------------------------------------
  1204. bool CEconLootListDefinition::BGenerateSingleRollRandomItems( const CEconGameAccount *pGameAccount, bool bFreeAccount, CUtlVector<CEconItem *> *out_pvecItems, const CUtlVector< item_definition_index_t > *pVecAvoidItemDefs /*= NULL*/ ) const
  1205. {
  1206. Assert( out_pvecItems );
  1207. // Where is our source of random numbers coming from? If we're a no-dupe list,
  1208. // we want to have reproducible state so we use our account ID as a unique-ish
  1209. // seed.
  1210. //
  1211. // Wrap the whole thing in a smart pointer so we clean up whenever/however we
  1212. // leave.
  1213. std::unique_ptr<IUniformRandomStream> pRandomStream( [=]() -> IUniformRandomStream *
  1214. {
  1215. if ( !BIsInternalNoDupesLootList() || !pGameAccount )
  1216. return new CDefaultUniformRandomStream;
  1217. CUniformRandomStream *pAccountUniformRandomStream = new CUniformRandomStream;
  1218. pAccountUniformRandomStream->SetSeed( pGameAccount->Obj().m_unAccountID );
  1219. return pAccountUniformRandomStream;
  1220. }() );
  1221. // Make however many passes through our loot list code until we've generated the
  1222. // right number of passing sets. (Most loot lists let anything pass. Some specify
  1223. // no duplicate definitions allowed.)
  1224. CUtlVector<rolled_item_defs_t> vecCumulativeItemDefs; // total list of everything we've generated so far in any number of result sets
  1225. CUtlVector<rolled_item_defs_t> vecItemDefs; // current list under evaluation
  1226. int iNoDupesIterations = 0;
  1227. // This actually isn't guaranteed to converge, and is guaranteed not to converge if
  1228. // we set up a broken lootlist. We set a really high bar here to catch broken/pathologically
  1229. // bad cases here without grinding the whole GC to a halt.
  1230. enum { kUpperBoundIterationSanityCheck = 2000 };
  1231. int iTotalIterations = 0;
  1232. while ( true )
  1233. {
  1234. // Don't runaway.
  1235. iTotalIterations++;
  1236. if ( iTotalIterations >= kUpperBoundIterationSanityCheck )
  1237. return false;
  1238. // Generate all of our item defs and their lootlists
  1239. vecItemDefs.Purge();
  1240. if ( !RollRandomItemsAndAdditionalItems( pRandomStream.get(), bFreeAccount, &vecItemDefs, pVecAvoidItemDefs ) )
  1241. return false;
  1242. // If we don't care about dupes and we got any results at all we're done.
  1243. if ( !BIsInternalNoDupesLootList() )
  1244. break;
  1245. // If we do care about dupes and we have some, ignore this set of items.
  1246. if ( BContainsDuplicateItemDefs( vecItemDefs, vecCumulativeItemDefs ) )
  1247. continue;
  1248. // Did we get to the right result set?
  1249. iNoDupesIterations++;
  1250. if ( iNoDupesIterations > m_iNoDupesIterations )
  1251. break;
  1252. // Store off the list of definition indices we've already used them so they don't get reused
  1253. // in a later set.
  1254. vecCumulativeItemDefs.AddVectorToTail( vecItemDefs );
  1255. }
  1256. // If we get down to here, we expect that we've rolled at least one item def
  1257. Assert( vecItemDefs.Count() > 0 );
  1258. FOR_EACH_VEC( vecItemDefs, i )
  1259. {
  1260. const rolled_item_defs_t& rolledDef = vecItemDefs[i];
  1261. Assert( rolledDef.m_pItemDef );
  1262. Assert( rolledDef.m_vecAffectingLootLists.Count() > 0 );
  1263. // Create the items
  1264. CEconItem *pItem = GEconManager()->GetItemFactory().CreateSpecificItem( pGameAccount, rolledDef.m_pItemDef->GetDefinitionIndex() );
  1265. out_pvecItems->AddToTail( pItem );
  1266. // Go through and let all the affecting lootlists attach their attributes to the item
  1267. FOR_EACH_VEC( rolledDef.m_vecAffectingLootLists, j )
  1268. {
  1269. const CEconLootListDefinition *pLootList = rolledDef.m_vecAffectingLootLists[j];
  1270. Assert( pLootList );
  1271. if ( !pLootList->BAttachLootListAttributes( pGameAccount, pItem ) )
  1272. return false;
  1273. }
  1274. }
  1275. return ( out_pvecItems->Count() > 0 );
  1276. }
  1277. class CRollSimulator
  1278. {
  1279. public:
  1280. CRollSimulator()
  1281. : m_mapRarityCounts( StringLessThan )
  1282. , m_mapCounts( DefLessFunc( item_definition_index_t ) )
  1283. , m_mapUnusualHatEffectsCount( DefLessFunc( uint32 ) )
  1284. , m_mapUnusualTauntEffectsCount( DefLessFunc( uint32 ) )
  1285. , m_nNumIters( 0 )
  1286. {}
  1287. void RollLootlist( const char* pszLootListName, int nRolls, bool bWipePreviousResults = false )
  1288. {
  1289. const CEconLootListDefinition* pLootlist = GetItemSchema()->GetLootListByName( pszLootListName );
  1290. if ( !pLootlist )
  1291. {
  1292. EmitInfo( SPEW_CONSOLE, SPEW_ALWAYS, LOG_ALWAYS, "Invalid lootlist \"%s\".\n", pszLootListName );
  1293. return;
  1294. }
  1295. // Clear out results first?
  1296. if ( bWipePreviousResults )
  1297. {
  1298. m_mapCounts.Purge();
  1299. m_mapRarityCounts.Purge();
  1300. m_DropsMaxes.Clear();
  1301. m_DropsTotals.Clear();
  1302. }
  1303. while( nRolls-- )
  1304. {
  1305. AutoYield();
  1306. CUtlVector<CEconItem *> vecItems;
  1307. pLootlist->BGenerateSingleRollRandomItems( NULL, false, &vecItems );
  1308. // Tally up what we got.
  1309. for( auto pItem : vecItems )
  1310. {
  1311. AutoYield();
  1312. // Insert item def if we need
  1313. item_definition_index_t nDefIndex = pItem->GetDefinitionIndex();
  1314. auto idx = m_mapCounts.Find( nDefIndex );
  1315. if ( m_mapCounts.InvalidIndex() == idx )
  1316. {
  1317. idx = m_mapCounts.Insert( nDefIndex );
  1318. }
  1319. // Insert rarity if needed
  1320. const CEconItemRarityDefinition* pItemRarity = GetItemSchema()->GetRarityDefinition( pItem->GetItemDefinition()->GetRarity() );
  1321. if ( pItemRarity )
  1322. {
  1323. const char* pszRarity = GGCGameBase()->LocalizeToken( pItemRarity->GetLocKey() , k_Lang_English );
  1324. auto rarityIdx = m_mapRarityCounts.Find( pszRarity );
  1325. if ( m_mapRarityCounts.InvalidIndex() == rarityIdx )
  1326. {
  1327. rarityIdx = m_mapRarityCounts.Insert( pszRarity, 0 );
  1328. }
  1329. m_mapRarityCounts[ rarityIdx ] += 1;
  1330. }
  1331. // Count
  1332. DropResult_t& result = m_mapCounts[ idx ];
  1333. ++result.m_nRollCount;
  1334. ++m_DropsTotals.m_nRollCount;
  1335. m_DropsMaxes.m_nRollCount = Max( m_DropsMaxes.m_nRollCount, result.m_nRollCount );
  1336. // Strange count
  1337. if ( BIsItemStrange( pItem ) )
  1338. {
  1339. ++result.m_nStrangeCount;
  1340. ++m_DropsTotals.m_nStrangeCount;
  1341. m_DropsMaxes.m_nStrangeCount = Max( m_DropsMaxes.m_nStrangeCount, result.m_nStrangeCount );
  1342. }
  1343. // Unusual count
  1344. static CSchemaAttributeDefHandle pAttrDef_ParticleEffect( "attach particle effect" );
  1345. static CSchemaAttributeDefHandle pAttrDef_TauntUnusualAttr( "on taunt attach particle index" );
  1346. if ( pAttrDef_ParticleEffect && pAttrDef_TauntUnusualAttr )
  1347. {
  1348. uint32 nUnusualHatValue = 0;
  1349. uint32 nUnusualTauntValue = 0;
  1350. pItem->FindAttribute( pAttrDef_ParticleEffect, &nUnusualHatValue );
  1351. pItem->FindAttribute( pAttrDef_TauntUnusualAttr, &nUnusualTauntValue );
  1352. // Cant use quality cause of old legacy items. Quality is just a quick test
  1353. if ( nUnusualHatValue != 0 || nUnusualTauntValue != 0 )
  1354. {
  1355. ++result.m_nUnusualCount;
  1356. ++m_DropsTotals.m_nUnusualCount;
  1357. m_DropsMaxes.m_nUnusualCount = Max( m_DropsMaxes.m_nUnusualCount, result.m_nUnusualCount );
  1358. }
  1359. if ( nUnusualHatValue )
  1360. {
  1361. nUnusualHatValue = (uint32)((float&)nUnusualHatValue);
  1362. auto idx = m_mapUnusualHatEffectsCount.Find( nUnusualHatValue );
  1363. if ( idx == m_mapUnusualHatEffectsCount.InvalidIndex() )
  1364. {
  1365. idx = m_mapUnusualHatEffectsCount.Insert( nUnusualHatValue, 0 );
  1366. }
  1367. ++m_mapUnusualHatEffectsCount[ idx ];
  1368. }
  1369. if ( nUnusualTauntValue )
  1370. {
  1371. nUnusualTauntValue = (uint32)((float&)nUnusualTauntValue);
  1372. auto idx = m_mapUnusualTauntEffectsCount.Find( nUnusualTauntValue );
  1373. if ( idx == m_mapUnusualTauntEffectsCount.InvalidIndex() )
  1374. {
  1375. idx = m_mapUnusualTauntEffectsCount.Insert( nUnusualTauntValue, 0 );
  1376. }
  1377. ++m_mapUnusualTauntEffectsCount[ idx ];
  1378. }
  1379. }
  1380. }
  1381. // Delete what we got
  1382. vecItems.PurgeAndDeleteElements();
  1383. }
  1384. }
  1385. void PrintRarityBreakdwn()
  1386. {
  1387. EmitInfo( SPEW_CONSOLE, SPEW_ALWAYS, LOG_ALWAYS, "-- Rarity breakdown:\n" );
  1388. int nMaxDigits = 0;
  1389. while( m_mapRarityCounts.Count() )
  1390. {
  1391. // Go through and print the most rolled to least rolled rarities
  1392. auto maxIdx = m_mapRarityCounts.InvalidIndex();
  1393. // Find the most hit
  1394. FOR_EACH_MAP_FAST( m_mapRarityCounts, i )
  1395. {
  1396. AutoYield();
  1397. if ( maxIdx == m_mapRarityCounts.InvalidIndex() || m_mapRarityCounts[ i ] > m_mapRarityCounts[ maxIdx ] )
  1398. {
  1399. maxIdx = i;
  1400. }
  1401. nMaxDigits = Max( nMaxDigits, NumDigits( m_mapRarityCounts[ maxIdx ] ) );
  1402. }
  1403. EmitInfo( SPEW_CONSOLE, SPEW_ALWAYS, LOG_ALWAYS, "%9s %*d %.3f%%\n",
  1404. m_mapRarityCounts.Key( maxIdx ), nMaxDigits, m_mapRarityCounts[ maxIdx ],
  1405. 100.f * (float)m_mapRarityCounts[ maxIdx ] / m_DropsTotals.m_nRollCount );
  1406. m_mapRarityCounts.RemoveAt( maxIdx );
  1407. }
  1408. EmitInfo( SPEW_CONSOLE, SPEW_ALWAYS, LOG_ALWAYS, "%-*s %*s %*s\n", 9 + 1 + NumDigits( m_DropsMaxes.m_nRollCount ) + 6,
  1409. "-- Item breakdown", NumDigits( m_DropsMaxes.m_nStrangeCount ) + 1, "S", NumDigits( m_DropsMaxes.m_nUnusualCount ), "U" );
  1410. }
  1411. void PrintUnusualCounts()
  1412. {
  1413. EmitInfo( SPEW_CONSOLE, SPEW_ALWAYS, LOG_ALWAYS, "-- Unusual Hats breakdown:\n" );
  1414. int nTotal = PrintUnusualsForType( m_mapUnusualHatEffectsCount );
  1415. EmitInfo( SPEW_CONSOLE, SPEW_ALWAYS, LOG_ALWAYS, "-- %d total unusual hats\n", nTotal );
  1416. EmitInfo( SPEW_CONSOLE, SPEW_ALWAYS, LOG_ALWAYS, "-- Unusual Taunts breakdown:\n" );
  1417. nTotal = PrintUnusualsForType( m_mapUnusualTauntEffectsCount );
  1418. EmitInfo( SPEW_CONSOLE, SPEW_ALWAYS, LOG_ALWAYS, "-- %d total unusual taunts\n", nTotal );
  1419. }
  1420. void PrintTotals()
  1421. {
  1422. while( m_mapCounts.Count() )
  1423. {
  1424. // Go through and print the most rolled to least rolled
  1425. auto maxIdx = m_mapCounts.InvalidIndex();
  1426. // Find the most hit
  1427. FOR_EACH_MAP_FAST( m_mapCounts, i )
  1428. {
  1429. AutoYield();
  1430. if ( maxIdx == m_mapCounts.InvalidIndex() || m_mapCounts[ i ].m_nRollCount > m_mapCounts[ maxIdx ].m_nRollCount )
  1431. {
  1432. maxIdx = i;
  1433. }
  1434. }
  1435. DropResult_t& result = m_mapCounts[ maxIdx ];
  1436. CEconItemDefinition *pItemDef = GetItemSchema()->GetItemDefinition( m_mapCounts.Key( maxIdx ) );
  1437. const CEconItemRarityDefinition* pItemRarity = GetItemSchema()->GetRarityDefinition( pItemDef->GetRarity() );
  1438. const char* pszRarity = pItemRarity ? GGCGameBase()->LocalizeToken( pItemRarity->GetLocKey() , k_Lang_English ) : "";
  1439. CFmtStr rollString( "%*d %2.3f%%", NumDigits( m_DropsMaxes.m_nRollCount ), result.m_nRollCount,
  1440. 100.f * (float)result.m_nRollCount / m_DropsTotals.m_nRollCount );
  1441. CFmtStr strangeString( "%*d", NumDigits( m_DropsMaxes.m_nStrangeCount ), result.m_nStrangeCount );
  1442. CFmtStr unusualString( "%*d", NumDigits( m_DropsMaxes.m_nUnusualCount ), result.m_nUnusualCount );
  1443. const char* pszItemname = GGCGameBase()->LocalizeToken( pItemDef->GetCustomPainkKitDefinition() ? pItemDef->GetCustomPainkKitDefinition()->GetName() : pItemDef->GetItemBaseName(), k_Lang_English );
  1444. EmitInfo( SPEW_CONSOLE, SPEW_ALWAYS, LOG_ALWAYS, "%9s %s %s %s %s\n", pszRarity, rollString.Get(), strangeString.Get(), unusualString.Get(), pszItemname );
  1445. m_mapCounts.RemoveAt( maxIdx );
  1446. }
  1447. EmitInfo( SPEW_CONSOLE, SPEW_ALWAYS, LOG_ALWAYS, "-- Total Items: %d Strange: %d (%.3f%%) Unusual: %d (%.3f%%)\n"
  1448. , m_DropsTotals.m_nRollCount
  1449. , m_DropsTotals.m_nStrangeCount
  1450. , ( 100.f * (float)m_DropsTotals.m_nStrangeCount / m_DropsTotals.m_nRollCount )
  1451. , m_DropsTotals.m_nUnusualCount
  1452. , ( 100.f * (float)m_DropsTotals.m_nUnusualCount / m_DropsTotals.m_nRollCount ) );
  1453. }
  1454. struct DropResult_t
  1455. {
  1456. DropResult_t() { Clear(); }
  1457. void Clear()
  1458. {
  1459. m_nStrangeCount = 0;
  1460. m_nUnusualCount = 0;
  1461. m_nRollCount = 0;
  1462. }
  1463. int m_nStrangeCount;
  1464. int m_nUnusualCount;
  1465. int m_nRollCount;
  1466. };
  1467. const DropResult_t& GetTotalDrops() const { return m_DropsTotals; }
  1468. const DropResult_t& GetMaxesDrops() const { return m_DropsMaxes; }
  1469. private:
  1470. int PrintUnusualsForType( CUtlMap< uint32, int >& mapUnusuals )
  1471. {
  1472. int nMaxDigits = 0;
  1473. int nTotal = 0;
  1474. FOR_EACH_MAP_FAST( mapUnusuals, i )
  1475. {
  1476. nTotal += mapUnusuals[ i ];
  1477. }
  1478. while( mapUnusuals.Count() )
  1479. {
  1480. // Go through and print the most rolled to least rolled unusual effects
  1481. auto maxIdx = mapUnusuals.InvalidIndex();
  1482. // Find the most hit
  1483. FOR_EACH_MAP_FAST( mapUnusuals, i )
  1484. {
  1485. AutoYield();
  1486. if ( maxIdx == mapUnusuals.InvalidIndex() || mapUnusuals[ i ] > mapUnusuals[ maxIdx ] )
  1487. {
  1488. maxIdx = i;
  1489. }
  1490. nMaxDigits = Max( nMaxDigits, NumDigits( mapUnusuals[ maxIdx ] ) );
  1491. }
  1492. char particleNameEntry[128];
  1493. Q_snprintf( particleNameEntry, ARRAYSIZE( particleNameEntry ), "#Attrib_Particle%d", mapUnusuals.Key( maxIdx ) );
  1494. const char* pszParticleName = GGCGameBase()->LocalizeToken( particleNameEntry, k_Lang_English );
  1495. EmitInfo( SPEW_CONSOLE, SPEW_ALWAYS, LOG_ALWAYS, "%*d %.3f%% %s (%d)\n",
  1496. nMaxDigits,
  1497. mapUnusuals[ maxIdx ],
  1498. 100.f * (float)mapUnusuals[ maxIdx ] / nTotal,
  1499. pszParticleName,
  1500. mapUnusuals.Key( maxIdx ) );
  1501. mapUnusuals.RemoveAt( maxIdx );
  1502. }
  1503. return nTotal;
  1504. }
  1505. void AutoYield()
  1506. {
  1507. if ( ++m_nNumIters % 100 == 0 )
  1508. {
  1509. if ( GJobCur().BYieldIfNeeded() )
  1510. {
  1511. // If we re-entered logon surge we should go away for a while
  1512. while ( GGCGameBase()->BIsInLogonSurge() )
  1513. {
  1514. GJobCur().BYieldingWaitOneFrame();
  1515. }
  1516. }
  1517. }
  1518. };
  1519. int NumDigits( int nNumber )
  1520. {
  1521. int digits = 0;
  1522. if ( nNumber <= 0)
  1523. {
  1524. digits = 1;
  1525. }
  1526. while ( nNumber )
  1527. {
  1528. nNumber /= 10;
  1529. ++digits;
  1530. }
  1531. return digits;
  1532. }
  1533. CUtlMap< item_definition_index_t, DropResult_t > m_mapCounts;
  1534. CUtlMap< uint32, int > m_mapUnusualHatEffectsCount;
  1535. CUtlMap< uint32, int > m_mapUnusualTauntEffectsCount;
  1536. // Rarity name is the key
  1537. CUtlMap< const char*, int > m_mapRarityCounts;
  1538. DropResult_t m_DropsTotals;
  1539. DropResult_t m_DropsMaxes;
  1540. size_t m_nNumIters;
  1541. };
  1542. GC_CON_COMMAND( simulate_lootlist_contents, "<lootlist> <iterations> Check item distribution from a given lootlist n times" )
  1543. {
  1544. if ( !BCheckArgs( 2, args, simulate_lootlist_contents_command ) )
  1545. return;
  1546. const char* pszLootListName = args[1];
  1547. int nRolls = args.ArgC() == 3 ? atoi( args[2] ) : 1000;
  1548. CRollSimulator simulator;
  1549. simulator.RollLootlist( pszLootListName, nRolls );
  1550. simulator.PrintRarityBreakdwn();
  1551. simulator.PrintUnusualCounts();
  1552. simulator.PrintTotals();
  1553. }
  1554. #ifdef GC_DLL
  1555. class CItemSourceFinder
  1556. {
  1557. public:
  1558. CItemSourceFinder( const char* pszItemName )
  1559. : m_pItemDef( GetItemSchema()->GetItemDefinitionByName( pszItemName ) )
  1560. {
  1561. Assert( m_pItemDef );
  1562. if ( !m_pItemDef )
  1563. {
  1564. EG_ERROR( SPEW_CONSOLE, "%s is not a valid item", pszItemName );
  1565. return;
  1566. }
  1567. // Find out what series this crate belongs to.
  1568. static CSchemaAttributeDefHandle pAttr_CrateSeries( "set supply crate series" );
  1569. if ( !pAttr_CrateSeries )
  1570. return;
  1571. auto& mapItemDefs = GetItemSchema()->GetItemDefinitionMap();
  1572. auto& mapRevolvingLootlists = GetItemSchema()->GetRevolvingLootLists();
  1573. CUtlDict< int > dictSeenLootlists;
  1574. // Look through all the item defs and see if any of them statically specify a lootlist that they want to open
  1575. FOR_EACH_MAP_FAST( mapItemDefs, i )
  1576. {
  1577. const CEconItemDefinition* pSourceItemDef = mapItemDefs[ i ];
  1578. const CEconLootListDefinition* pLootlist = NULL;
  1579. const CEconTool_Gift* pGift = pSourceItemDef->GetTypedEconTool< CEconTool_Gift >();
  1580. // Self-opening crate?
  1581. if ( pGift )
  1582. {
  1583. pLootlist = GetItemSchema()->GetLootListByName( pGift->GetLootListName() );
  1584. }
  1585. else // Crate with an item series?
  1586. {
  1587. int iCrateSeries;
  1588. {
  1589. float fCrateSeries; // crate series ID is stored as a float internally because we hate ourselves
  1590. if ( !FindAttribute_UnsafeBitwiseCast<attrib_value_t>( pSourceItemDef, pAttr_CrateSeries, &fCrateSeries ) || fCrateSeries == 0.0f )
  1591. continue;
  1592. iCrateSeries = fCrateSeries;
  1593. }
  1594. auto idx = mapRevolvingLootlists.Find( iCrateSeries );
  1595. if ( idx == mapRevolvingLootlists.InvalidIndex() )
  1596. continue;
  1597. pLootlist = GetItemSchema()->GetLootListByName( mapRevolvingLootlists[ idx ] );
  1598. }
  1599. if ( !pLootlist )
  1600. continue;
  1601. // Mark that we've seen this lootlist already
  1602. dictSeenLootlists.Insert( pLootlist->GetName() );
  1603. DropSource_t& source = m_vecSources[ m_vecSources.AddToTail() ];
  1604. source.m_pDroppingItem = pSourceItemDef;
  1605. ChanceForItemFromLootlist( m_pItemDef, pLootlist, source.lootlistSource );
  1606. }
  1607. CEconItemDefinition* pCrateItemDef = GetItemSchema()->GetItemDefinitionByName( "Supply Crate" );
  1608. Assert( pCrateItemDef );
  1609. // Go through all the revolving lootlists and see if they have the item. Assume that
  1610. // they're from a "Supply Crate".
  1611. FOR_EACH_MAP_FAST( mapRevolvingLootlists, i )
  1612. {
  1613. if ( !pCrateItemDef )
  1614. continue;
  1615. if ( mapRevolvingLootlists.Key( i ) <= 0 )
  1616. continue;
  1617. auto pLootlist = GetItemSchema()->GetLootListByName( mapRevolvingLootlists[ i ] );
  1618. if ( !pLootlist )
  1619. continue;
  1620. // This lootlist was on a different crate already
  1621. if ( dictSeenLootlists.Find( pLootlist->GetName() ) != dictSeenLootlists.InvalidIndex() )
  1622. continue;
  1623. DropSource_t& source = m_vecSources[ m_vecSources.AddToTail() ];
  1624. source.m_pDroppingItem = pCrateItemDef;
  1625. ChanceForItemFromLootlist( m_pItemDef, pLootlist, source.lootlistSource );
  1626. }
  1627. // Not available at all!
  1628. if ( !m_vecSources.Count() )
  1629. {
  1630. EmitInfo( SPEW_CONSOLE, SPEW_ALWAYS, LOG_ALWAYS, "Not available from any sources!\n" );
  1631. return;
  1632. }
  1633. // Sort greatest % chance
  1634. auto lambdaSort = [] ( DropSource_t const *pLHS, DropSource_t const *pRHS ) -> int
  1635. {
  1636. return pLHS->lootlistSource.m_flChance < pRHS->lootlistSource.m_flChance;
  1637. };
  1638. m_vecSources.Sort( lambdaSort );
  1639. }
  1640. void PrintSources()
  1641. {
  1642. FOR_EACH_VEC( m_vecSources, i )
  1643. {
  1644. m_vecSources[ i ].PrintSources();
  1645. }
  1646. }
  1647. bool BDropsFromLootlist( const CEconLootListDefinition* pLootlist )
  1648. {
  1649. FOR_EACH_VEC( m_vecSources, i )
  1650. {
  1651. if ( m_vecSources[ i ].lootlistSource.BDropsFromLootlist( pLootlist ) )
  1652. return true;
  1653. }
  1654. return false;
  1655. }
  1656. private:
  1657. struct DropSource_t
  1658. {
  1659. void PrintSources()
  1660. {
  1661. if ( lootlistSource.m_flChance == 0.f )
  1662. return;
  1663. bool bSelfOpening = m_pDroppingItem->GetTypedEconTool< CEconTool_Gift >() != NULL;
  1664. // Print the name of the item, and whether it's a self-opening item, or a crate
  1665. EmitInfo( SPEW_CONSOLE, SPEW_ALWAYS, LOG_ALWAYS, "%3.5f%% %s (%d - %s)\n",
  1666. lootlistSource.m_flChance * 100.f,
  1667. m_pDroppingItem->GetDefinitionName(),
  1668. m_pDroppingItem->GetDefinitionIndex(),
  1669. bSelfOpening ? "Self-Opening" : "Crate/Case" );
  1670. // Print all the sources
  1671. lootlistSource.PrintSources( 1 );
  1672. EmitInfo( SPEW_CONSOLE, SPEW_ALWAYS, LOG_ALWAYS, "\n" );
  1673. }
  1674. struct LootListSource_t
  1675. {
  1676. LootListSource_t()
  1677. : m_flChance( 0.f )
  1678. {}
  1679. bool BDropsFromLootlist( const CEconLootListDefinition* pLootlist )
  1680. {
  1681. // Skip no-chance entries
  1682. if ( m_flChance == 0.f )
  1683. return false;
  1684. if ( m_pDroppingLootlist == pLootlist )
  1685. return true;
  1686. FOR_EACH_VEC( m_vecLootlistSources, i )
  1687. {
  1688. if ( m_vecLootlistSources[ i ].BDropsFromLootlist( pLootlist ) )
  1689. return true;
  1690. }
  1691. return false;
  1692. }
  1693. void PrintSources( int nInset )
  1694. {
  1695. // Skip no-chance entries
  1696. if ( m_flChance == 0.f )
  1697. return;
  1698. auto& mapRevolvingLootlists = GetItemSchema()->GetRevolvingLootLists();
  1699. int nRevolvingIdx = mapRevolvingLootlists.InvalidIndex();
  1700. // Check if our lootlist is one of the revolving lootlists. If so, we want
  1701. // to print out its index within revolving_lootlists, so we can map that to
  1702. // the attribute value of supply_crate_series (187)
  1703. FOR_EACH_MAP_FAST( mapRevolvingLootlists, i )
  1704. {
  1705. if ( V_stricmp( mapRevolvingLootlists[ i ], m_pDroppingLootlist->GetName() ) == 0 )
  1706. {
  1707. nRevolvingIdx = mapRevolvingLootlists.Key( i );
  1708. break;
  1709. }
  1710. }
  1711. if ( nRevolvingIdx != mapRevolvingLootlists.InvalidIndex() && nRevolvingIdx > 0 )
  1712. {
  1713. // It's in revolving_lootlists. Print its index in there.
  1714. EmitInfo( SPEW_CONSOLE, SPEW_ALWAYS, LOG_ALWAYS, "%*.5f%% (%d) %s\n",
  1715. 4 * nInset + 5,
  1716. m_flChance * 100.f,
  1717. nRevolvingIdx,
  1718. m_pDroppingLootlist->GetName() );
  1719. }
  1720. else
  1721. {
  1722. // Not in the revolving lootlist
  1723. EmitInfo( SPEW_CONSOLE, SPEW_ALWAYS, LOG_ALWAYS, "%*.5f%% %s\n",
  1724. 4 * nInset + 5,
  1725. m_flChance * 100.f,
  1726. m_pDroppingLootlist->GetName() );
  1727. }
  1728. // Sort greatest % chance
  1729. auto lambdaSort = [] ( LootListSource_t const *pLHS, LootListSource_t const *pRHS ) -> int
  1730. {
  1731. return pLHS->m_flChance < pRHS->m_flChance;
  1732. };
  1733. m_vecLootlistSources.Sort( lambdaSort );
  1734. // Print out children indented a lil bit
  1735. FOR_EACH_VEC( m_vecLootlistSources, i )
  1736. {
  1737. m_vecLootlistSources[i].PrintSources( nInset + 1 );
  1738. }
  1739. }
  1740. float m_flChance;
  1741. const CEconLootListDefinition* m_pDroppingLootlist;
  1742. CCopyableUtlVector< LootListSource_t > m_vecLootlistSources;
  1743. };
  1744. const CEconItemDefinition* m_pDroppingItem;
  1745. LootListSource_t lootlistSource;
  1746. };
  1747. float ChanceForItemFromLootlist( const CEconItemDefinition* pItemDef, const CEconLootListDefinition* pLootlist, DropSource_t::LootListSource_t& lootlistSource )
  1748. {
  1749. auto& vecContents = pLootlist->GetLootListContents();
  1750. // Accumulate our chance of dropping the specified item
  1751. lootlistSource.m_flChance = 0.f;
  1752. lootlistSource.m_pDroppingLootlist = pLootlist;
  1753. // Gather the items in this lootlist that we're able to roll for at this time
  1754. float flTotalWeight = 0.f;
  1755. CUtlVector<CEconLootListDefinition::drop_item_t> vecValidContents;
  1756. FOR_EACH_VEC( vecContents, i )
  1757. {
  1758. if( !vecContents[ i ].m_dropPeriod.IsValidForTime( CRTime::RTime32TimeCur() ) )
  1759. continue;
  1760. flTotalWeight += vecContents[ i ].m_flWeight;
  1761. vecValidContents.AddToTail( vecContents[ i ] );
  1762. }
  1763. // Go through valid contents, and see if the specified item is in there
  1764. FOR_EACH_VEC( vecValidContents, i )
  1765. {
  1766. const int iItemDef = vecValidContents[ i ].m_iItemOrLootlistDef;
  1767. const float flChance = vecValidContents[ i ].m_flWeight / flTotalWeight;
  1768. if ( iItemDef < 0 ) // Lootlist
  1769. {
  1770. int iLLIndex = (iItemDef * -1) - 1;
  1771. auto pSubLootlist = GetItemSchema()->GetLootListByIndex( iLLIndex );
  1772. // One of our sub-lootlists might drop it. Add in it's chance within
  1773. // the sub-lootlist scaled by the chance to roll that sub-lootlist.
  1774. auto& subSource = lootlistSource.m_vecLootlistSources[ lootlistSource.m_vecLootlistSources.AddToTail() ];
  1775. lootlistSource.m_flChance += ChanceForItemFromLootlist( pItemDef, pSubLootlist, subSource ) * flChance;
  1776. }
  1777. else if ( pItemDef->GetDefinitionIndex() == iItemDef )
  1778. {
  1779. // We drop it! Add the chance
  1780. lootlistSource.m_flChance += flChance;
  1781. }
  1782. }
  1783. // Treat additional drops just the same as nested lootlists.
  1784. auto& vecAdditionalDrops = pLootlist->GetAdditionalDrops();
  1785. FOR_EACH_VEC( vecAdditionalDrops, i )
  1786. {
  1787. auto& additionalDrop = vecAdditionalDrops[ i ];
  1788. if ( !additionalDrop.m_dropPeriod.IsValidForTime( CRTime::RTime32TimeCur() ) )
  1789. continue;
  1790. auto pSubLootlist = GetItemSchema()->GetLootListByName( additionalDrop.m_pszLootListDefName );
  1791. auto& subSource = lootlistSource.m_vecLootlistSources[ lootlistSource.m_vecLootlistSources.AddToTail() ];
  1792. lootlistSource.m_flChance += ChanceForItemFromLootlist( pItemDef, pSubLootlist, subSource ) * additionalDrop.m_fChance;
  1793. }
  1794. // Return the total chance
  1795. return lootlistSource.m_flChance;
  1796. }
  1797. CUtlVector< DropSource_t > m_vecSources;
  1798. const CEconItemDefinition* m_pItemDef;
  1799. };
  1800. GC_CON_COMMAND( item_sources, "Lists the sources for obtaining a list of specific items" )
  1801. {
  1802. if ( !BCheckArgs( 1, args, item_sources_command ) )
  1803. return;
  1804. for ( int i=1; i < args.ArgC(); ++i )
  1805. {
  1806. const char* pszItemName = args[i];
  1807. const CEconItemDefinition* pItemDef = GetItemSchema()->GetItemDefinitionByName( pszItemName );
  1808. if ( pItemDef )
  1809. {
  1810. EmitInfo( SPEW_CONSOLE, SPEW_ALWAYS, LOG_ALWAYS, "\nChecking items for lootlists containing (%d) %s...\n", pItemDef->GetDefinitionIndex(), pItemDef->GetDefinitionName() );
  1811. CItemSourceFinder source( pszItemName );
  1812. source.PrintSources();
  1813. }
  1814. else
  1815. {
  1816. EmitInfo( SPEW_CONSOLE, SPEW_ALWAYS, LOG_ALWAYS, "\n\"%s\" is not a valid item name\n", pszItemName );
  1817. }
  1818. }
  1819. }
  1820. GC_CON_COMMAND( list_keys, "Lists all the keys in the schema" )
  1821. {
  1822. EmitInfo( SPEW_CONSOLE, SPEW_ALWAYS, LOG_ALWAYS, "Spewing keys...\n" );
  1823. auto& mapItemDefs = GetItemSchema()->GetItemDefinitionMap();
  1824. FOR_EACH_MAP_FAST( mapItemDefs, i )
  1825. {
  1826. const CEconItemDefinition* pItemDef = mapItemDefs[ i ];
  1827. if ( !pItemDef || !pItemDef->GetEconTool() || ( Q_strcmp( pItemDef->GetEconTool()->GetTypeName(), "decoder_ring" ) != 0 ) )
  1828. {
  1829. continue;
  1830. }
  1831. EmitInfo( SPEW_CONSOLE, SPEW_ALWAYS, LOG_ALWAYS, "%llu - %s\n", pItemDef->GetDefinitionIndex(), pItemDef->GetItemDefinitionName() );
  1832. }
  1833. }
  1834. ConVar case_behavior_rolls_per_lootlist( "case_behavior_rolls_per_lootlist", "1000000", FCVAR_REPLICATED, "How many times to roll a lootlist" );
  1835. class CJobCaseBehaviorCheck : public CGCGameBaseJob
  1836. {
  1837. public:
  1838. CJobCaseBehaviorCheck()
  1839. : CGCGameBaseJob( GGCGameBase() )
  1840. {}
  1841. virtual bool BYieldingRunGCJob()
  1842. {
  1843. // Wait until logon surge ends to get going
  1844. while ( GGCGameBase()->BIsInLogonSurge() )
  1845. {
  1846. GJobCur().BYieldingWaitOneFrame();
  1847. }
  1848. CRollSimulator simulator;
  1849. // Convert the hash to something readable
  1850. char pchSHAHex[41];
  1851. memset( pchSHAHex, 0, sizeof( pchSHAHex ) );
  1852. V_binarytohex( GetItemSchema()->GetSchemaSHA().m_shaDigest, 20, pchSHAHex, 41 );
  1853. RTime32 now = CRTime::RTime32TimeCur();
  1854. int nNewRecords = 0;
  1855. EmitInfo( SPEW_CONSOLE, SPEW_ALWAYS, LOG_ALWAYS, "Beginning CJobCaseBehaviorCheck\n" );
  1856. auto& lootlists = GetItemSchema()->GetRevolvingLootLists();
  1857. // Go through all of the lootlists
  1858. FOR_EACH_MAP( lootlists, i )
  1859. {
  1860. const CEconLootListDefinition* pEconLootlist = GetItemSchema()->GetLootListByName( lootlists[ i ] );
  1861. if ( pEconLootlist )
  1862. {
  1863. // Check if we have data for this lootlist on this hash already
  1864. {
  1865. CSQLAccess sqlReadAccess;
  1866. CUtlVector< CSchCaseBehavior > vecExistingResult;
  1867. sqlReadAccess.AddBindParam( pchSHAHex );
  1868. sqlReadAccess.AddBindParam( i );
  1869. if ( sqlReadAccess.BYieldingReadRecordsWithWhereClause( &vecExistingResult, "SchemaSHA = ? and Series = ?", CSET_FULL( CSchCaseBehavior ) ) )
  1870. {
  1871. // Already got it? Skip the work
  1872. if ( vecExistingResult.Count() )
  1873. {
  1874. continue;
  1875. }
  1876. }
  1877. }
  1878. // Roll 'em up
  1879. simulator.RollLootlist( pEconLootlist->GetName(), case_behavior_rolls_per_lootlist.GetInt(), true );
  1880. // Insert record
  1881. CSchCaseBehavior behavior;
  1882. behavior.SetVarCharField( behavior.m_VarCharSchemaSHA, pchSHAHex, true, CSchCaseBehavior::k_iField_VarCharSchemaSHA );
  1883. behavior.m_RTime32Date = now;
  1884. behavior.m_unSeries = i;
  1885. behavior.SetVarCharField( behavior.m_VarCharLootListName, pEconLootlist->GetName(), true, CSchCaseBehavior::k_iField_VarCharLootListName );
  1886. behavior.m_fStrangeChance = 100.f * (float)simulator.GetTotalDrops().m_nStrangeCount / simulator.GetTotalDrops().m_nRollCount;
  1887. behavior.m_fUnusualChance = 100.f * (float)simulator.GetTotalDrops().m_nUnusualCount / simulator.GetTotalDrops().m_nRollCount;
  1888. CSQLAccess sqlAccess;
  1889. sqlAccess.BBeginTransaction( "CJobCaseBehaviorCheck" );
  1890. sqlAccess.BYieldingInsertOrUpdateOnPK( &behavior );
  1891. sqlAccess.BCommitTransaction( true );
  1892. ++nNewRecords;
  1893. }
  1894. BYieldIfNeeded();
  1895. }
  1896. EmitInfo( SPEW_CONSOLE, SPEW_ALWAYS, LOG_ALWAYS, "Completed CJobUnusualChecker. %d updated records.\n", nNewRecords );
  1897. return true;
  1898. }
  1899. };
  1900. void CEconItemSchema::PerformCaseBehaviorCheck()
  1901. {
  1902. CJob* pJob = new CJobCaseBehaviorCheck();
  1903. pJob->StartJobDelayed( NULL );
  1904. }
  1905. #endif
  1906. //-----------------------------------------------------------------------------
  1907. // Purpose:
  1908. //-----------------------------------------------------------------------------
  1909. bool CEconLootListDefinition::RollRandomItemsAndAdditionalItems( IUniformRandomStream *pRandomStream, bool bFreeAccount, CUtlVector<rolled_item_defs_t> *out_pVecRolledItems, const CUtlVector< item_definition_index_t > *pVecAvoidItemDefs ) const
  1910. {
  1911. Assert( out_pVecRolledItems );
  1912. // Roll to see what items we get from this loot list.
  1913. bool bCreatedItems = RollRandomItemDef( pRandomStream, bFreeAccount, out_pVecRolledItems, pVecAvoidItemDefs );
  1914. // Do we have additional drops?
  1915. FOR_EACH_VEC( m_AdditionalDrops, i )
  1916. {
  1917. if ( !bCreatedItems )
  1918. break;
  1919. // Is this within the period this is allowed to drop?
  1920. if ( !m_AdditionalDrops[i].m_dropPeriod.IsValidForTime( CRTime::RTime32TimeCur() ) )
  1921. continue;
  1922. // Does this only apply to premium accounts?
  1923. if ( m_AdditionalDrops[i].m_bPremiumOnly && bFreeAccount )
  1924. continue;
  1925. // Does this only apply on certain holidays?
  1926. if ( m_AdditionalDrops[i].m_iRequiredHolidayIndex != kHoliday_None && !EconHolidays_IsHolidayActive( m_AdditionalDrops[i].m_iRequiredHolidayIndex, CRTime::RTime32TimeCur() ) )
  1927. continue;
  1928. // Random chance is in the range 0-1 so generate a value in that range to "roll".
  1929. if ( pRandomStream->RandomFloat( 0.0f, 1.0f ) > m_AdditionalDrops[i].m_fChance )
  1930. continue;
  1931. // Roll!
  1932. const char *pszAdditionalDropLootList = m_AdditionalDrops[i].m_pszLootListDefName;
  1933. const CEconLootListDefinition *pLootListDef = GetItemSchema()->GetLootListByName( pszAdditionalDropLootList );
  1934. if ( pLootListDef == NULL )
  1935. {
  1936. AssertMsg2( false, "Loot list '%s' specifies unknown additional drop '%s'", GetName(), pszAdditionalDropLootList );
  1937. return false;
  1938. }
  1939. bCreatedItems &= pLootListDef->RollRandomItemsAndAdditionalItems( pRandomStream, bFreeAccount, out_pVecRolledItems );
  1940. }
  1941. // If we failed to create some items, we might still have chosen some item defs before those failures. In that case, clear
  1942. // out any choices we've made so far
  1943. if ( !bCreatedItems )
  1944. {
  1945. out_pVecRolledItems->Purge();
  1946. }
  1947. Assert( bCreatedItems == (out_pVecRolledItems->Count() > 0) );
  1948. return bCreatedItems;
  1949. }
  1950. bool CEconLootListDefinition::RollRandomItemDef( IUniformRandomStream *pRandomStream, bool bFreeAccount, CUtlVector<rolled_item_defs_t> *out_pVecRolledItems, const CUtlVector< item_definition_index_t > *pVecAvoidItemDefs ) const
  1951. {
  1952. Assert( out_pVecRolledItems );
  1953. CUtlVector< rolled_item_defs_t > vecScratchDefs;
  1954. CUtlVector<const drop_item_t*> vecValidDrops;
  1955. // Gather the items in this lootlist that we're able to roll for at this time
  1956. float flTotalWeight = 0.f;
  1957. FOR_EACH_VEC( m_DropList, i )
  1958. {
  1959. if( !m_DropList[i].m_dropPeriod.IsValidForTime( CRTime::RTime32TimeCur() ) )
  1960. continue;
  1961. // Skip any item defs that are in our avoid list (if we have one)
  1962. if ( pVecAvoidItemDefs )
  1963. {
  1964. item_definition_index_t defIndex = m_DropList[i].m_iItemOrLootlistDef;
  1965. if ( pVecAvoidItemDefs->Find( defIndex ) != pVecAvoidItemDefs->InvalidIndex() )
  1966. continue;
  1967. }
  1968. // If this is valid, add it to the list and add its weight to the total
  1969. vecValidDrops.AddToTail( &m_DropList[i] );
  1970. flTotalWeight += m_DropList[i].m_flWeight;
  1971. }
  1972. // Roll to see what item drops.
  1973. float flRand = pRandomStream->RandomFloat(0.0f, 1.0f) * flTotalWeight;
  1974. float flAccum = 0.0f;
  1975. FOR_EACH_VEC( vecValidDrops, i )
  1976. {
  1977. flAccum += vecValidDrops[i]->m_flWeight;
  1978. if ( flRand <= flAccum )
  1979. {
  1980. const int iItemDef = vecValidDrops[i]->m_iItemOrLootlistDef; // not item_definition_index because it might also be a negative value to indicate a sub lootlist
  1981. if ( iItemDef >= 0 )
  1982. {
  1983. const CEconItemDefinition* pItemDef = GetItemSchema()->GetItemDefinition( iItemDef );
  1984. if( !pItemDef )
  1985. return false;
  1986. // Add the item def and the lootlist
  1987. rolled_item_defs_t& rolledDef = vecScratchDefs[ vecScratchDefs.AddToTail() ];
  1988. rolledDef.m_pItemDef = pItemDef;
  1989. }
  1990. else
  1991. {
  1992. // In the case where iItemDef is negative, it's a nested loot list. Ask that list to choose an item.
  1993. // HACKY: Store loot list indices as negatives, starting from -1, because 0 is a valid item index
  1994. int iLLIndex = (iItemDef * -1) - 1;
  1995. const CEconLootListDefinition *pNestedLootList = GetItemSchema()->GetLootListByIndex( iLLIndex );
  1996. if ( !pNestedLootList )
  1997. return false;
  1998. if( !pNestedLootList->RollRandomItemsAndAdditionalItems( pRandomStream, bFreeAccount, &vecScratchDefs, pVecAvoidItemDefs ) )
  1999. return false;
  2000. }
  2001. // Add ourselves to the list of affecting lootlists, so that we and all nested loot lists will affect this item
  2002. // We intentionally don't do this in our calling function because we don't want to include additional drops.
  2003. FOR_EACH_VEC( vecScratchDefs, j )
  2004. {
  2005. vecScratchDefs[j].m_vecAffectingLootLists.AddToTail( this );
  2006. }
  2007. // We want to exit the loop here regardless of whether items were successfully so that we only perform a single
  2008. // item-generating roll on this list.
  2009. break;
  2010. }
  2011. }
  2012. // Feed item defs, if they exist, back to our caller
  2013. out_pVecRolledItems->AddVectorToTail( vecScratchDefs );
  2014. // Did we successfully create any items?
  2015. return ( vecScratchDefs.Count() > 0 );
  2016. }
  2017. //-----------------------------------------------------------------------------
  2018. // Purpose: find a list of lootlists with rarity from this lootlist
  2019. //-----------------------------------------------------------------------------
  2020. void CEconLootListDefinition::GetRarityLootLists( CUtlVector< const CEconLootListDefinition* > *out_pVecRarityLootList ) const
  2021. {
  2022. Assert( out_pVecRarityLootList );
  2023. if ( m_unRarity != k_unItemRarity_Any )
  2024. {
  2025. out_pVecRarityLootList->AddToTail( this );
  2026. }
  2027. FOR_EACH_VEC( m_DropList, i )
  2028. {
  2029. 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
  2030. if ( iItemDef < 0 )
  2031. {
  2032. // In the case where iItemDef is negative, it's a nested loot list. Ask that list to choose an item.
  2033. // HACKY: Store loot list indices as negatives, starting from -1, because 0 is a valid item index
  2034. int iLLIndex = (iItemDef * -1) - 1;
  2035. const CEconLootListDefinition *pNestedLootList = GetItemSchema()->GetLootListByIndex( iLLIndex );
  2036. if ( !pNestedLootList )
  2037. return;
  2038. pNestedLootList->GetRarityLootLists( out_pVecRarityLootList );
  2039. }
  2040. }
  2041. }
  2042. //-----------------------------------------------------------------------------
  2043. // Purpose: get all item defs from this lootlist ( not lootlist item def )
  2044. //-----------------------------------------------------------------------------
  2045. void CEconLootListDefinition::GetItemDefs( CUtlVector< item_definition_index_t > *out_pVecItemDefs ) const
  2046. {
  2047. Assert( out_pVecItemDefs );
  2048. FOR_EACH_VEC( m_DropList, i )
  2049. {
  2050. 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
  2051. if ( iItemDef >= 0 )
  2052. {
  2053. out_pVecItemDefs->AddToTail( (item_definition_index_t)iItemDef );
  2054. }
  2055. else
  2056. {
  2057. // In the case where iItemDef is negative, it's a nested loot list. Ask that list to choose an item.
  2058. // HACKY: Store loot list indices as negatives, starting from -1, because 0 is a valid item index
  2059. int iLLIndex = (iItemDef * -1) - 1;
  2060. const CEconLootListDefinition *pNestedLootList = GetItemSchema()->GetLootListByIndex( iLLIndex );
  2061. if ( !pNestedLootList )
  2062. return;
  2063. pNestedLootList->GetItemDefs( out_pVecItemDefs );
  2064. }
  2065. }
  2066. }
  2067. //-----------------------------------------------------------------------------
  2068. // Purpose: Given a vector of possible attributes, roll to see which ones are
  2069. // chosen. We allocate memory for these new attributes, so it's the
  2070. // responsibility of the caller to free these attributes when they're
  2071. // done with them!
  2072. //-----------------------------------------------------------------------------
  2073. void CEconLootListDefinition::RollRandomAttributes( CUtlVector< static_attrib_t >& vecAttributes, const CEconGameAccount *pGameAccount ) const
  2074. {
  2075. for ( int i=0; i<m_RandomAttribs.Count(); ++i )
  2076. {
  2077. const random_attrib_t* rattr = m_RandomAttribs[i];
  2078. rattr->RollRandomAttributes( vecAttributes, pGameAccount );
  2079. }
  2080. }
  2081. //-----------------------------------------------------------------------------
  2082. // Purpose:
  2083. //-----------------------------------------------------------------------------
  2084. bool CEconLootListDefinition::drop_period_t::IsValidForTime( const RTime32& time ) const
  2085. {
  2086. if( time >= m_DropStartDate && time < m_DropEndDate )
  2087. return true;
  2088. return false;
  2089. }
  2090. //-----------------------------------------------------------------------------
  2091. // Purpose:
  2092. //-----------------------------------------------------------------------------
  2093. bool CEconLootListDefinition::BAttachLootListAttributes( const CEconGameAccount *pGameAccount, CEconItem *pItem ) const
  2094. {
  2095. //static CSchemaAttributeDefHandle pAttr_ElevateQuality( "elevate quality" );
  2096. // Gather and apply old-style random attributes.
  2097. CUtlVector< static_attrib_t > vecAttributes;
  2098. RollRandomAttributes( vecAttributes, pGameAccount );
  2099. FOR_EACH_VEC( vecAttributes, i )
  2100. {
  2101. GEconManager()->GetItemFactory().ApplyStaticAttributeToItem( pItem, vecAttributes[i], pGameAccount );
  2102. vecAttributes[i].GetAttributeDefinition()->GetAttributeType()->UnloadEconAttributeValue( &vecAttributes[i].m_value );
  2103. }
  2104. // Apply all relevant property generators.
  2105. for ( auto pGenerator : m_PropertyGenerators )
  2106. {
  2107. if ( !pGenerator->BGenerateProperties( pItem ) )
  2108. return false;
  2109. }
  2110. return true;
  2111. }
  2112. //-----------------------------------------------------------------------------
  2113. // Purpose:
  2114. //-----------------------------------------------------------------------------
  2115. bool lootlist_attrib_t::BInitFromKV( const char *pszContext, KeyValues *pKVKey, CEconItemSchema &pschema, CUtlVector<CUtlString> *pVecErrors )
  2116. {
  2117. SCHEMA_INIT_SUBSTEP( m_staticAttrib.BInitFromKV_MultiLine( pszContext, pKVKey, pVecErrors ) );
  2118. SCHEMA_INIT_CHECK(
  2119. pKVKey->FindKey( "weight" ),
  2120. "Context '%s': Attribute \"%s\" missing required 'weight' field", pszContext, pKVKey->GetName() );
  2121. m_flWeight = pKVKey->GetFloat( "weight" );
  2122. return SCHEMA_INIT_SUCCESS();
  2123. }
  2124. //-----------------------------------------------------------------------------
  2125. // Purpose: returns true if we should stop rolling from this random_attrib_t
  2126. //-----------------------------------------------------------------------------
  2127. bool random_attrib_t::RollRandomAttributes( CUtlVector< static_attrib_t >& vecAttributes, const CEconGameAccount *pGameAccount ) const
  2128. {
  2129. if ( m_flChanceOfRandomAttribute && RandomFloat() <= m_flChanceOfRandomAttribute )
  2130. {
  2131. // We're attaching a random attribute. Determine which attribute.
  2132. float flRand = 0.0f;
  2133. if ( !m_bPickAllAttributes )
  2134. {
  2135. // Pick one attribute to add
  2136. // Otherwise we'll pick them all
  2137. flRand = RandomFloat( 0.f, 1.f ) * m_flTotalAttributeWeight;
  2138. }
  2139. float flAccum = 0.f;
  2140. for ( int iAttrib = 0; iAttrib < m_RandomAttributes.Count(); ++iAttrib )
  2141. {
  2142. const lootlist_attrib_t& randomAttrib = m_RandomAttributes[iAttrib];
  2143. flAccum += randomAttrib.m_flWeight;
  2144. if ( flRand <= flAccum )
  2145. {
  2146. // Add the attribute
  2147. static_attrib_t &staticAttrib = vecAttributes[ vecAttributes.AddToTail( randomAttrib.m_staticAttrib ) ];
  2148. const CEconItemAttributeDefinition *pAttrDef = staticAttrib.GetAttributeDefinition();
  2149. const ISchemaAttributeType *pAttrType = pAttrDef->GetAttributeType();
  2150. // Generate a special value?
  2151. pAttrType->InitializeNewEconAttributeValue( &staticAttrib.m_value );
  2152. pAttrDef->GetAttributeType()->GenerateEconAttributeValue( pAttrDef, staticAttrib, pGameAccount, &staticAttrib.m_value );
  2153. if ( !m_bPickAllAttributes )
  2154. {
  2155. // We're only picking one attribute from the list
  2156. return true;
  2157. }
  2158. }
  2159. }
  2160. }
  2161. return false;
  2162. }
  2163. #endif // GC_DLL
  2164. //-----------------------------------------------------------------------------
  2165. // Purpose:
  2166. //-----------------------------------------------------------------------------
  2167. void CEconLootListDefinition::EnumerateUserFacingPotentialDrops( IEconLootListIterator *pIt ) const
  2168. {
  2169. Assert( pIt );
  2170. // Loot lists have the option of specifying that their contents should not be publicly
  2171. // listed. This is used on the GC for things like the "rare item drop list" to prevent
  2172. // every single potentially-unusual hat from showing up.
  2173. if ( !BPublicListContents() )
  2174. return;
  2175. FOR_EACH_VEC( GetLootListContents(), i )
  2176. {
  2177. const int iID = GetLootListContents()[i].m_iItemOrLootlistDef;
  2178. // Nested loot lists are stored as negative indices.
  2179. if ( iID < 0 )
  2180. {
  2181. const CEconLootListDefinition *pLootListDef = GetItemSchema()->GetLootListByIndex( (-iID) - 1 );
  2182. if ( !pLootListDef )
  2183. continue;
  2184. pLootListDef->EnumerateUserFacingPotentialDrops( pIt );
  2185. }
  2186. else
  2187. {
  2188. pIt->OnIterate( iID );
  2189. }
  2190. }
  2191. }
  2192. //-----------------------------------------------------------------------------
  2193. // Purpose:
  2194. //-----------------------------------------------------------------------------
  2195. /*static*/ CSchemaAttributeDefHandle CAttributeLineItemLootList::s_pAttrDef_RandomDropLineItems[] =
  2196. {
  2197. CSchemaAttributeDefHandle( "random drop line item 0" ),
  2198. CSchemaAttributeDefHandle( "random drop line item 1" ),
  2199. CSchemaAttributeDefHandle( "random drop line item 2" ),
  2200. CSchemaAttributeDefHandle( "random drop line item 3" ),
  2201. };
  2202. #ifdef GC_DLL
  2203. /*static*/ CSchemaAttributeDefHandle CAttributeLineItemLootList::s_pAttrDef_RandomDropLineItemUnusualChance( "random drop line item unusual chance" ); // "one out of this many"
  2204. /*static*/ CSchemaAttributeDefHandle CAttributeLineItemLootList::s_pAttrDef_RandomDropLineItemUnusualList( "random drop line item unusual list" );
  2205. #endif // GC_DLL
  2206. CSchemaAttributeDefHandle CAttributeLineItemLootList::s_pAttrDef_RandomDropLineItemFooterDesc( "random drop line item footer desc" );
  2207. //-----------------------------------------------------------------------------
  2208. // Purpose:
  2209. //-----------------------------------------------------------------------------
  2210. void CAttributeLineItemLootList::EnumerateUserFacingPotentialDrops( IEconLootListIterator *pIt ) const
  2211. {
  2212. Assert( pIt );
  2213. for ( int i = 0; i < ARRAYSIZE( s_pAttrDef_RandomDropLineItems ); i++ )
  2214. {
  2215. uint32 unItemDef;
  2216. COMPILE_TIME_ASSERT( sizeof( unItemDef ) >= sizeof( item_definition_index_t ) );
  2217. // If we run out of attributes we have set we're done.
  2218. if ( !m_pEconItem->FindAttribute( s_pAttrDef_RandomDropLineItems[i], &unItemDef ) )
  2219. break;
  2220. pIt->OnIterate( unItemDef );
  2221. }
  2222. }
  2223. //-----------------------------------------------------------------------------
  2224. // Purpose:
  2225. //-----------------------------------------------------------------------------
  2226. const char *CAttributeLineItemLootList::GetLootListHeaderLocalizationKey() const
  2227. {
  2228. return g_pszDefaultRevolvingLootListHeader;
  2229. }
  2230. //-----------------------------------------------------------------------------
  2231. const char *CAttributeLineItemLootList::GetLootListFooterLocalizationKey() const
  2232. {
  2233. CAttribute_String sFooter;
  2234. const char* pszFooter = NULL;
  2235. if ( FindAttribute_UnsafeBitwiseCast<CAttribute_String>( m_pEconItem, s_pAttrDef_RandomDropLineItemFooterDesc, &pszFooter ) )
  2236. {
  2237. return pszFooter;
  2238. }
  2239. return NULL;
  2240. }
  2241. //-----------------------------------------------------------------------------
  2242. const char *CAttributeLineItemLootList::GetLootListCollectionReference() const
  2243. {
  2244. // TODO : Implement me!
  2245. return NULL;
  2246. }
  2247. //-----------------------------------------------------------------------------
  2248. // Purpose:
  2249. //-----------------------------------------------------------------------------
  2250. const CEconLootListDefinition* CEconItemSchema::GetLootListByName( const char* pListName, int *out_piIndex ) const
  2251. {
  2252. auto idx = m_mapLootLists.Find( pListName );
  2253. if ( !m_mapLootLists.IsValidIndex( idx ) )
  2254. return NULL;
  2255. if ( out_piIndex )
  2256. {
  2257. *out_piIndex = idx;
  2258. }
  2259. return m_mapLootLists[idx];
  2260. }
  2261. //-----------------------------------------------------------------------------
  2262. // Purpose:
  2263. //-----------------------------------------------------------------------------
  2264. const CQuestObjectiveDefinition* CEconItemSchema::GetQuestObjectiveByDefIndex( int iIdx ) const
  2265. {
  2266. auto nMapIndex = m_mapQuestObjectives.Find( iIdx );
  2267. if ( nMapIndex != m_mapQuestObjectives.InvalidIndex() )
  2268. {
  2269. return m_mapQuestObjectives[ nMapIndex ];
  2270. }
  2271. return NULL;
  2272. }
  2273. //-----------------------------------------------------------------------------
  2274. // Purpose: Constructor
  2275. //-----------------------------------------------------------------------------
  2276. CEconCraftingRecipeDefinition::CEconCraftingRecipeDefinition( void )
  2277. : m_nDefIndex( 0 )
  2278. #ifdef GC_DLL
  2279. , m_bIsCraftableByUnverifiedClient( false )
  2280. #endif // GC_DLL
  2281. {
  2282. }
  2283. //-----------------------------------------------------------------------------
  2284. // Purpose: Initialize the attribute definition
  2285. // Input: pKVAttribute - The KeyValues representation of the attribute
  2286. // schema - The overall item schema for this attribute
  2287. // pVecErrors - An optional vector that will contain error messages if
  2288. // the init fails.
  2289. // Output: True if initialization succeeded, false otherwise
  2290. //-----------------------------------------------------------------------------
  2291. bool CEconCraftingRecipeDefinition::BInitFromKV( KeyValues *pKVRecipe, CUtlVector<CUtlString> *pVecErrors /* = NULL */ )
  2292. {
  2293. m_nDefIndex = Q_atoi( pKVRecipe->GetName() );
  2294. // Check for required fields
  2295. SCHEMA_INIT_CHECK(
  2296. NULL != pKVRecipe->FindKey( "input_items" ),
  2297. "Recipe definition %d: Missing required field \"input_items\"", m_nDefIndex );
  2298. SCHEMA_INIT_CHECK(
  2299. NULL != pKVRecipe->FindKey( "output_items" ),
  2300. "Recipe definition %d: Missing required field \"output_items\"", m_nDefIndex );
  2301. m_bDisabled = pKVRecipe->GetBool( "disabled" );
  2302. m_strName = pKVRecipe->GetString( "name" );
  2303. m_strN_A = pKVRecipe->GetString( "n_A" );
  2304. m_strDescInputs = pKVRecipe->GetString( "desc_inputs" );
  2305. m_strDescOutputs = pKVRecipe->GetString( "desc_outputs" );
  2306. m_strDI_A = pKVRecipe->GetString( "di_A" );
  2307. m_strDI_B = pKVRecipe->GetString( "di_B" );
  2308. m_strDI_C = pKVRecipe->GetString( "di_C" );
  2309. m_strDO_A = pKVRecipe->GetString( "do_A" );
  2310. m_strDO_B = pKVRecipe->GetString( "do_B" );
  2311. m_strDO_C = pKVRecipe->GetString( "do_C" );
  2312. #ifdef GC_DLL
  2313. m_bIsCraftableByUnverifiedClient = pKVRecipe->GetBool( "is_craftable_by_unverified_clients", false );
  2314. #endif // GC_DLL
  2315. m_bRequiresAllSameClass = pKVRecipe->GetBool( "all_same_class" );
  2316. m_bRequiresAllSameSlot = pKVRecipe->GetBool( "all_same_slot" );
  2317. m_iCacheClassUsageForOutputFromItem = pKVRecipe->GetInt( "add_class_usage_to_output", -1 );
  2318. m_iCacheSlotUsageForOutputFromItem = pKVRecipe->GetInt( "add_slot_usage_to_output", -1 );
  2319. m_iCacheSetForOutputFromItem = pKVRecipe->GetInt( "add_set_to_output", -1 );
  2320. m_bPremiumAccountOnly = pKVRecipe->GetBool( "premium_only", false );
  2321. m_iCategory = (recipecategories_t)StringFieldToInt( pKVRecipe->GetString("category"), g_szRecipeCategoryStrings, ARRAYSIZE(g_szRecipeCategoryStrings) );
  2322. // Read in all the input items
  2323. KeyValues *pKVInputItems = pKVRecipe->FindKey( "input_items" );
  2324. if ( NULL != pKVInputItems )
  2325. {
  2326. FOR_EACH_TRUE_SUBKEY( pKVInputItems, pKVInputItem )
  2327. {
  2328. int index = m_InputItemsCriteria.AddToTail();
  2329. SCHEMA_INIT_SUBSTEP( m_InputItemsCriteria[index].BInitFromKV( pKVInputItem ) );
  2330. // Recipes ignore the enabled flag when generating items
  2331. m_InputItemsCriteria[index].SetIgnoreEnabledFlag( true );
  2332. index = m_InputItemDupeCounts.AddToTail();
  2333. m_InputItemDupeCounts[index] = atoi( pKVInputItem->GetName() );
  2334. }
  2335. }
  2336. // Read in all the output items
  2337. KeyValues *pKVOutputItems = pKVRecipe->FindKey( "output_items" );
  2338. if ( NULL != pKVOutputItems )
  2339. {
  2340. FOR_EACH_TRUE_SUBKEY( pKVOutputItems, pKVOutputItem )
  2341. {
  2342. int index = m_OutputItemsCriteria.AddToTail();
  2343. SCHEMA_INIT_SUBSTEP( m_OutputItemsCriteria[index].BInitFromKV( pKVOutputItem ) );
  2344. // Recipes ignore the enabled flag when generating items
  2345. m_OutputItemsCriteria[index].SetIgnoreEnabledFlag( true );
  2346. }
  2347. }
  2348. return SCHEMA_INIT_SUCCESS();
  2349. }
  2350. //-----------------------------------------------------------------------------
  2351. // Purpose: Serializes the criteria to and from messages
  2352. //-----------------------------------------------------------------------------
  2353. bool CEconCraftingRecipeDefinition::BSerializeToMsg( CSOItemRecipe & msg ) const
  2354. {
  2355. msg.set_def_index( m_nDefIndex );
  2356. msg.set_name( m_strName );
  2357. msg.set_n_a( m_strN_A );
  2358. msg.set_desc_inputs( m_strDescInputs );
  2359. msg.set_desc_outputs( m_strDescOutputs );
  2360. msg.set_di_a( m_strDI_A );
  2361. msg.set_di_b( m_strDI_B );
  2362. msg.set_di_c( m_strDI_C );
  2363. msg.set_do_a( m_strDO_A );
  2364. msg.set_do_b( m_strDO_B );
  2365. msg.set_do_c( m_strDO_C );
  2366. msg.set_requires_all_same_class( m_bRequiresAllSameClass );
  2367. msg.set_requires_all_same_slot( m_bRequiresAllSameSlot );
  2368. msg.set_class_usage_for_output( m_iCacheClassUsageForOutputFromItem );
  2369. msg.set_slot_usage_for_output( m_iCacheSlotUsageForOutputFromItem );
  2370. msg.set_set_for_output( m_iCacheSetForOutputFromItem );
  2371. FOR_EACH_VEC( m_InputItemsCriteria, i )
  2372. {
  2373. CSOItemCriteria *pCrit = msg.add_input_items_criteria();
  2374. if ( !m_InputItemsCriteria[i].BSerializeToMsg( *pCrit ) )
  2375. return false;
  2376. }
  2377. FOR_EACH_VEC( m_InputItemDupeCounts, i )
  2378. {
  2379. msg.add_input_item_dupe_counts( m_InputItemDupeCounts[i] );
  2380. }
  2381. FOR_EACH_VEC( m_OutputItemsCriteria, i )
  2382. {
  2383. CSOItemCriteria *pCrit = msg.add_output_items_criteria();
  2384. if ( !m_OutputItemsCriteria[i].BSerializeToMsg( *pCrit ) )
  2385. return false;
  2386. }
  2387. return true;
  2388. }
  2389. //-----------------------------------------------------------------------------
  2390. // Purpose: Serializes the criteria to and from messages
  2391. //-----------------------------------------------------------------------------
  2392. bool CEconCraftingRecipeDefinition::BDeserializeFromMsg( const CSOItemRecipe & msg )
  2393. {
  2394. m_nDefIndex = msg.def_index();
  2395. m_strName = msg.name().c_str();
  2396. m_strN_A = msg.n_a().c_str();
  2397. m_strDescInputs = msg.desc_inputs().c_str();
  2398. m_strDescOutputs = msg.desc_outputs().c_str();
  2399. m_strDI_A = msg.di_a().c_str();
  2400. m_strDI_B = msg.di_b().c_str();
  2401. m_strDI_C = msg.di_c().c_str();
  2402. m_strDO_A = msg.do_a().c_str();
  2403. m_strDO_B = msg.do_b().c_str();
  2404. m_strDO_C = msg.do_c().c_str();
  2405. m_bRequiresAllSameClass = msg.requires_all_same_class();
  2406. m_bRequiresAllSameSlot = msg.requires_all_same_slot();
  2407. m_iCacheClassUsageForOutputFromItem = msg.class_usage_for_output();
  2408. m_iCacheSlotUsageForOutputFromItem = msg.slot_usage_for_output();
  2409. m_iCacheSetForOutputFromItem = msg.set_for_output();
  2410. // Read how many input items there are
  2411. uint32 unCount = msg.input_items_criteria_size();
  2412. m_InputItemsCriteria.SetSize( unCount );
  2413. for ( uint32 i = 0; i < unCount; i++ )
  2414. {
  2415. if ( !m_InputItemsCriteria[i].BDeserializeFromMsg( msg.input_items_criteria( i ) ) )
  2416. return false;
  2417. }
  2418. // Read how many input item dupe counts there are
  2419. unCount = msg.input_item_dupe_counts_size();
  2420. m_InputItemDupeCounts.SetSize( unCount );
  2421. for ( uint32 i = 0; i < unCount; i++ )
  2422. {
  2423. m_InputItemDupeCounts[i] = msg.input_item_dupe_counts( i );
  2424. }
  2425. // Read how many output items there are
  2426. unCount = msg.output_items_criteria_size();
  2427. m_OutputItemsCriteria.SetSize( unCount );
  2428. for ( uint32 i = 0; i < unCount; i++ )
  2429. {
  2430. if ( !m_OutputItemsCriteria[i].BDeserializeFromMsg( msg.output_items_criteria( i ) ) )
  2431. return false;
  2432. }
  2433. return true;
  2434. }
  2435. //-----------------------------------------------------------------------------
  2436. // Purpose: Returns true if the vector contains a set of items that matches the inputs for this recipe
  2437. // Note it will fail if the vector contains extra items that aren't needed.
  2438. //
  2439. //-----------------------------------------------------------------------------
  2440. bool CEconCraftingRecipeDefinition::ItemListMatchesInputs( CUtlVector<CEconItem*> *vecCraftingItems, KeyValues *out_pkvCraftParams, bool bIgnoreSlop, CUtlVector<uint64> *vecChosenItems ) const
  2441. {
  2442. return false;
  2443. }
  2444. //-----------------------------------------------------------------------------
  2445. // Purpose: Constructor
  2446. //-----------------------------------------------------------------------------
  2447. int CEconCraftingRecipeDefinition::GetTotalInputItemsRequired( void ) const
  2448. {
  2449. int iCount = 0;
  2450. FOR_EACH_VEC( m_InputItemsCriteria, i )
  2451. {
  2452. if ( m_InputItemDupeCounts[i] )
  2453. {
  2454. iCount += m_InputItemDupeCounts[i];
  2455. }
  2456. else
  2457. {
  2458. iCount++;
  2459. }
  2460. }
  2461. return iCount;
  2462. }
  2463. //-----------------------------------------------------------------------------
  2464. // Purpose:
  2465. //-----------------------------------------------------------------------------
  2466. #ifdef GC_DLL
  2467. #define GC_SCH_REFERENCE( TAttribSchType ) \
  2468. TAttribSchType,
  2469. #else
  2470. #define GC_SCH_REFERENCE( TAttribSchType )
  2471. #endif
  2472. //-----------------------------------------------------------------------------
  2473. // Purpose:
  2474. //-----------------------------------------------------------------------------
  2475. unsigned int Internal_GetAttributeTypeUniqueIdentifierNextValue()
  2476. {
  2477. static unsigned int s_unUniqueCounter = 0;
  2478. unsigned int unCounter = s_unUniqueCounter;
  2479. s_unUniqueCounter++;
  2480. return unCounter;
  2481. }
  2482. //-----------------------------------------------------------------------------
  2483. // Purpose:
  2484. //-----------------------------------------------------------------------------
  2485. #ifdef GC_DLL
  2486. template < typename TAttribSchType, typename TRecordBaseType >
  2487. static TAttribSchType *GetTypedSch( TRecordBaseType *pRecordBase )
  2488. {
  2489. Assert( pRecordBase->GetITable() == TAttribSchType::k_iTable );
  2490. #if ENABLE_TYPED_ATTRIBUTE_PARANOIA
  2491. TAttribSchType *pTypedSch = dynamic_cast<TAttribSchType *>( pRecordBase );
  2492. Assert( pTypedSch );
  2493. return pTypedSch;
  2494. #else
  2495. return static_cast<TAttribSchType *>( pRecordBase );
  2496. #endif
  2497. }
  2498. #endif // GC_DLL
  2499. //-----------------------------------------------------------------------------
  2500. // Purpose:
  2501. //-----------------------------------------------------------------------------
  2502. template < GC_SCH_REFERENCE( typename TAttribSchType ) typename TAttribInMemoryType >
  2503. class CSchemaAttributeTypeBase : public ISchemaAttributeTypeBase<TAttribInMemoryType>
  2504. {
  2505. public:
  2506. #ifdef GC_DLL
  2507. virtual CColumnSet& GetFullColumnSet() const OVERRIDE
  2508. {
  2509. static CColumnSet sFullColumnSet( CColumnSet::Full<TAttribSchType>() );
  2510. return sFullColumnSet;
  2511. }
  2512. virtual CRecordBase *CreateTypedSchRecord() const OVERRIDE
  2513. {
  2514. return new TAttribSchType;
  2515. }
  2516. #endif // GC_DLL
  2517. };
  2518. //-----------------------------------------------------------------------------
  2519. // Purpose:
  2520. //-----------------------------------------------------------------------------
  2521. template < GC_SCH_REFERENCE( typename TAttribSchType ) typename TProtobufValueType >
  2522. class CSchemaAttributeTypeProtobufBase : public CSchemaAttributeTypeBase< GC_SCH_REFERENCE( TAttribSchType ) TProtobufValueType >
  2523. {
  2524. public:
  2525. virtual void ConvertTypedValueToByteStream( const TProtobufValueType& typedValue, ::std::string *out_psBytes ) const OVERRIDE
  2526. {
  2527. DbgVerify( typedValue.SerializeToString( out_psBytes ) );
  2528. }
  2529. virtual void ConvertByteStreamToTypedValue( const ::std::string& sBytes, TProtobufValueType *out_pTypedValue ) const OVERRIDE
  2530. {
  2531. DbgVerify( out_pTypedValue->ParseFromString( sBytes ) );
  2532. }
  2533. virtual bool BConvertStringToEconAttributeValue( const CEconItemAttributeDefinition *pAttrDef, const char *pszValue, union attribute_data_union_t *out_pValue, bool bEnableTerribleBackwardsCompatibilitySchemaParsingCode ) const OVERRIDE
  2534. {
  2535. Assert( pAttrDef );
  2536. Assert( out_pValue );
  2537. std::string sValue( pszValue );
  2538. TProtobufValueType typedValue;
  2539. if ( !google::protobuf::TextFormat::ParseFromString( sValue, &typedValue ) )
  2540. return false;
  2541. this->ConvertTypedValueToEconAttributeValue( typedValue, out_pValue );
  2542. return true;
  2543. }
  2544. virtual void ConvertEconAttributeValueToString( const CEconItemAttributeDefinition *pAttrDef, const attribute_data_union_t& value, std::string *out_ps ) const OVERRIDE
  2545. {
  2546. Assert( pAttrDef );
  2547. Assert( out_ps );
  2548. google::protobuf::TextFormat::PrintToString( this->GetTypedValueContentsFromEconAttributeValue( value ), out_ps );
  2549. }
  2550. };
  2551. //-----------------------------------------------------------------------------
  2552. // Purpose:
  2553. //-----------------------------------------------------------------------------
  2554. class CSchemaAttributeType_String : public CSchemaAttributeTypeProtobufBase< GC_SCH_REFERENCE( CSchItemAttributeString ) CAttribute_String >
  2555. {
  2556. public:
  2557. #ifdef GC_DLL
  2558. virtual void ConvertEconAttributeValueToSch( itemid_t unItemId, const CEconItemAttributeDefinition *pAttrDef, const attribute_data_union_t& value, GCSDK::CRecordBase *out_pSchRecord ) const OVERRIDE
  2559. {
  2560. Assert( out_pSchRecord );
  2561. Assert( pAttrDef );
  2562. Assert( pAttrDef->GetAttributeType() == this );
  2563. CSchItemAttributeString *out_psch = GetTypedSch<CSchItemAttributeString>( out_pSchRecord );
  2564. CAttribute_String typedValue;
  2565. this->ConvertEconAttributeValueToTypedValue( value, &typedValue );
  2566. // const CAttribute_String& typedValue = GetTypedValueContentsFromEconAttributeValue( value );
  2567. out_psch->m_ulItemID = unItemId;
  2568. out_psch->m_unAttrDefIndex = pAttrDef->GetDefinitionIndex();
  2569. WRITE_VAR_CHAR_FIELD( (*out_psch), VarCharAttrStrValue, typedValue.value().c_str() );
  2570. }
  2571. virtual void LoadSchToEconAttributeValue( CEconItem *pTargetItem, const CEconItemAttributeDefinition *pAttrDef, const GCSDK::CRecordBase *pSchRecord ) const OVERRIDE
  2572. {
  2573. Assert( pTargetItem );
  2574. Assert( pAttrDef );
  2575. Assert( pSchRecord );
  2576. Assert( pAttrDef->GetAttributeType() == this );
  2577. const CSchItemAttributeString *psch = GetTypedSch<const CSchItemAttributeString>( pSchRecord );
  2578. CAttribute_String typedValue;
  2579. typedValue.set_value( READ_VAR_CHAR_FIELD( (*psch), m_VarCharAttrStrValue ) );
  2580. pTargetItem->SetDynamicAttributeValue( pAttrDef, typedValue );
  2581. }
  2582. #endif // GC_DLL
  2583. // We intentionally override the convert-to-/convert-from-string functions for strings so that string literals can be
  2584. // specified in the schema, etc. without worrying about the protobuf text format.
  2585. virtual bool BConvertStringToEconAttributeValue( const CEconItemAttributeDefinition *pAttrDef, const char *pszValue, union attribute_data_union_t *out_pValue, bool bEnableTerribleBackwardsCompatibilitySchemaParsingCode ) const OVERRIDE
  2586. {
  2587. Assert( pAttrDef );
  2588. Assert( out_pValue );
  2589. CAttribute_String typedValue;
  2590. typedValue.set_value( pszValue );
  2591. this->ConvertTypedValueToEconAttributeValue( typedValue, out_pValue );
  2592. return true;
  2593. }
  2594. virtual void ConvertEconAttributeValueToString( const CEconItemAttributeDefinition *pAttrDef, const attribute_data_union_t& value, std::string *out_ps ) const OVERRIDE
  2595. {
  2596. Assert( pAttrDef );
  2597. Assert( out_ps );
  2598. *out_ps = this->GetTypedValueContentsFromEconAttributeValue( value ).value().c_str();
  2599. }
  2600. };
  2601. void CopyStringAttributeValueToCharPointerOutput( const CAttribute_String *pValue, const char **out_pValue )
  2602. {
  2603. Assert( pValue );
  2604. Assert( out_pValue );
  2605. *out_pValue = pValue->value().c_str();
  2606. }
  2607. //-----------------------------------------------------------------------------
  2608. // Purpose:
  2609. //-----------------------------------------------------------------------------
  2610. class CSchemaAttributeType_DynamicRecipeComponentDefinedItem : public CSchemaAttributeTypeProtobufBase< GC_SCH_REFERENCE( CSchItemAttributeDynamicRecipeComponentDefinedItem ) CAttribute_DynamicRecipeComponent >
  2611. {
  2612. public:
  2613. #ifdef GC_DLL
  2614. virtual void ConvertEconAttributeValueToSch( itemid_t unItemId, const CEconItemAttributeDefinition *pAttrDef, const union attribute_data_union_t& value, GCSDK::CRecordBase *out_pSchRecord ) const OVERRIDE
  2615. {
  2616. Assert( out_pSchRecord );
  2617. Assert( pAttrDef );
  2618. Assert( pAttrDef->GetAttributeType() == this );
  2619. CSchItemAttributeDynamicRecipeComponentDefinedItem *out_psch = GetTypedSch<CSchItemAttributeDynamicRecipeComponentDefinedItem>( out_pSchRecord );
  2620. CAttribute_DynamicRecipeComponent typedValue;
  2621. ConvertEconAttributeValueToTypedValue( value, &typedValue );
  2622. out_psch->m_ulItemID = unItemId;
  2623. out_psch->m_unAttrDefIndex = pAttrDef->GetDefinitionIndex();
  2624. out_psch->m_unItemDef = typedValue.def_index();
  2625. out_psch->m_unItemQuality = typedValue.item_quality();
  2626. out_psch->m_unFlags = typedValue.component_flags();
  2627. out_psch->m_unItemCount = typedValue.num_required();
  2628. out_psch->m_unItemsFulfilled = typedValue.num_fulfilled();
  2629. WRITE_VAR_CHAR_FIELD( (*out_psch), VarCharAttrStr, typedValue.attributes_string().c_str() );
  2630. }
  2631. virtual void LoadSchToEconAttributeValue( CEconItem *pTargetItem, const CEconItemAttributeDefinition *pAttrDef, const GCSDK::CRecordBase *pSchRecord ) const OVERRIDE
  2632. {
  2633. Assert( pTargetItem );
  2634. Assert( pAttrDef );
  2635. Assert( pSchRecord );
  2636. Assert( pAttrDef->GetAttributeType() == this );
  2637. const CSchItemAttributeDynamicRecipeComponentDefinedItem *psch = GetTypedSch<const CSchItemAttributeDynamicRecipeComponentDefinedItem>( pSchRecord );
  2638. CAttribute_DynamicRecipeComponent typedValue;
  2639. typedValue.set_def_index( psch->m_unItemDef );
  2640. typedValue.set_item_quality( psch->m_unItemQuality );
  2641. typedValue.set_component_flags( psch->m_unFlags );
  2642. typedValue.set_attributes_string( READ_VAR_CHAR_FIELD( (*psch), m_VarCharAttrStr ) );
  2643. typedValue.set_num_required( psch->m_unItemCount );
  2644. typedValue.set_num_fulfilled( psch->m_unItemsFulfilled );
  2645. pTargetItem->SetDynamicAttributeValue( pAttrDef, typedValue );
  2646. }
  2647. virtual bool BConvertStringToEconAttributeValue( const CEconItemAttributeDefinition *pAttrDef, const char *pszValue, union attribute_data_union_t *out_pValue, bool bEnableTerribleBackwardsCompatibilitySchemaParsingCode ) const OVERRIDE
  2648. {
  2649. Assert( pAttrDef );
  2650. Assert( out_pValue );
  2651. std::string sValue( pszValue );
  2652. // What's happened here is we've renamed some fields within CAttribute_DynamicRecipeComponent,
  2653. // but steam contains the strings of the old format serialized, and keeps sending them to us.
  2654. // Rather than updating steam, we're going to made a protobuff class that can accept the new
  2655. // and old formats, and put the corret values into the correct members of the new format.
  2656. CAttribute_DynamicRecipeComponent_COMPAT_NEVER_SERIALIZE_THIS_OUT typedCompatValue;
  2657. CAttribute_DynamicRecipeComponent typedActualValue;
  2658. #ifdef STAGING_ONLY
  2659. auto *pActualFields = typedActualValue.descriptor();
  2660. auto *pCompatFields = typedCompatValue.descriptor();
  2661. for ( int i=0; i < pActualFields->field_count(); ++i )
  2662. {
  2663. const bool bFoundField = pCompatFields->FindFieldByName( pActualFields->field( i )->name() ) != NULL;
  2664. Assert( bFoundField );
  2665. if ( !bFoundField )
  2666. {
  2667. EmitError( SPEW_GC, "Missing field '%s' in CAttribute_DynamicRecipeComponent_COMPAT_NEVER_SERIALIZE_THIS_OUT\n", pActualFields->field( i )->name() );
  2668. return false;
  2669. }
  2670. }
  2671. #endif // STAGING_ONLY
  2672. if ( !google::protobuf::TextFormat::ParseFromString( sValue, &typedCompatValue ) )
  2673. {
  2674. EmitError( SPEW_GC, "Failed to parse recipe component into compatible protobuf\n" );
  2675. return false;
  2676. }
  2677. if ( typedCompatValue.has_component_flags() )
  2678. typedActualValue.set_component_flags( typedCompatValue.component_flags() );
  2679. else if ( typedCompatValue.has_item_flags() )
  2680. typedActualValue.set_component_flags( typedCompatValue.item_flags() );
  2681. else
  2682. {
  2683. EmitError( SPEW_GC, "Failed to parse component_flags. component_flags: %d, item_flags: %d\n", typedCompatValue.component_flags(), typedCompatValue.item_flags() );
  2684. return false;
  2685. }
  2686. if ( typedCompatValue.has_def_index() )
  2687. typedActualValue.set_def_index( typedCompatValue.def_index() );
  2688. else if ( typedCompatValue.has_item_def() )
  2689. typedActualValue.set_def_index( typedCompatValue.item_def() );
  2690. else if ( typedActualValue.component_flags() & DYNAMIC_RECIPE_FLAG_PARAM_ITEM_DEF_SET )
  2691. {
  2692. EmitError( SPEW_GC, "Failed to parse item_def. def_index: %d, item_def: %d\n", typedCompatValue.def_index(), typedCompatValue.item_def() );
  2693. return false;
  2694. }
  2695. typedActualValue.set_item_quality( typedCompatValue.item_quality() );
  2696. typedActualValue.set_attributes_string( typedCompatValue.attributes_string() );
  2697. if( typedCompatValue.has_num_required() )
  2698. typedActualValue.set_num_required( typedCompatValue.num_required() );
  2699. else if ( typedCompatValue.has_item_count() )
  2700. typedActualValue.set_num_required( typedCompatValue.item_count() );
  2701. else
  2702. {
  2703. EmitError( SPEW_GC, "Failed to parse component_flags. num_required: %d, item_count: %d\n", typedCompatValue.num_required(), typedCompatValue.item_count() );
  2704. return false;
  2705. }
  2706. if ( typedCompatValue.has_items_fulfilled() )
  2707. typedActualValue.set_num_fulfilled( typedCompatValue.items_fulfilled() );
  2708. else if ( typedCompatValue.has_num_fulfilled() )
  2709. typedActualValue.set_num_fulfilled( typedCompatValue.num_fulfilled() );
  2710. else
  2711. {
  2712. EmitError( SPEW_GC, "Failed to parse num_fulfilled. items_fulfilled: %d, num_fulfilled: %d\n", typedCompatValue.items_fulfilled(), typedCompatValue.num_fulfilled() );
  2713. return false;
  2714. }
  2715. this->ConvertTypedValueToEconAttributeValue( typedActualValue, out_pValue );
  2716. return true;
  2717. }
  2718. #endif // GC_DLL
  2719. };
  2720. //-----------------------------------------------------------------------------
  2721. // Purpose:
  2722. //-----------------------------------------------------------------------------
  2723. class CSchemaAttributeType_ItemSlotCriteria : public CSchemaAttributeTypeProtobufBase< GC_SCH_REFERENCE( CSchItemAttributeItemSlotCriteria ) CAttribute_ItemSlotCriteria >
  2724. {
  2725. public:
  2726. #ifdef GC_DLL
  2727. virtual void ConvertEconAttributeValueToSch( itemid_t unItemId, const CEconItemAttributeDefinition *pAttrDef, const union attribute_data_union_t& value, GCSDK::CRecordBase *out_pSchRecord ) const OVERRIDE
  2728. {
  2729. Assert( out_pSchRecord );
  2730. Assert( pAttrDef );
  2731. Assert( pAttrDef->GetAttributeType() == this );
  2732. AssertMsg( 0, "Implement this when we want this attribute to be dynamic" );
  2733. }
  2734. virtual void LoadSchToEconAttributeValue( CEconItem *pTargetItem, const CEconItemAttributeDefinition *pAttrDef, const GCSDK::CRecordBase *pSchRecord ) const OVERRIDE
  2735. {
  2736. Assert( pTargetItem );
  2737. Assert( pAttrDef );
  2738. Assert( pSchRecord );
  2739. Assert( pAttrDef->GetAttributeType() == this );
  2740. AssertMsg( 0, "Implement this when we want this attribute to be dynamic" );
  2741. }
  2742. #endif // GC_DLL
  2743. virtual bool BConvertStringToEconAttributeValue( const CEconItemAttributeDefinition *pAttrDef, const char *pszValue, union attribute_data_union_t *out_pValue, bool bEnableTerribleBackwardsCompatibilitySchemaParsingCode ) const OVERRIDE
  2744. {
  2745. Assert( pAttrDef );
  2746. Assert( out_pValue );
  2747. std::string sValue( pszValue );
  2748. CAttribute_ItemSlotCriteria typedValue;
  2749. if ( !google::protobuf::TextFormat::ParseFromString( sValue, &typedValue ) )
  2750. return false;
  2751. this->ConvertTypedValueToEconAttributeValue( typedValue, out_pValue );
  2752. return true;
  2753. }
  2754. virtual void ConvertEconAttributeValueToString( const CEconItemAttributeDefinition *pAttrDef, const attribute_data_union_t& value, std::string *out_ps ) const OVERRIDE
  2755. {
  2756. Assert( pAttrDef );
  2757. Assert( out_ps );
  2758. this->ConvertEconAttributeValueToString( pAttrDef, value, out_ps );
  2759. }
  2760. };
  2761. //-----------------------------------------------------------------------------
  2762. // Purpose:
  2763. //-----------------------------------------------------------------------------
  2764. class CSchemaAttributeType_WorldItemPlacement : public CSchemaAttributeTypeProtobufBase < GC_SCH_REFERENCE( CSchItemAttributeWorldItemPlacement ) CAttribute_WorldItemPlacement >
  2765. {
  2766. public:
  2767. #ifdef GC_DLL
  2768. virtual void ConvertEconAttributeValueToSch( itemid_t unItemId, const CEconItemAttributeDefinition *pAttrDef, const union attribute_data_union_t& value, GCSDK::CRecordBase *out_pSchRecord ) const OVERRIDE
  2769. {
  2770. Assert( out_pSchRecord );
  2771. Assert( pAttrDef );
  2772. Assert( pAttrDef->GetAttributeType() == this );
  2773. CSchItemAttributeWorldItemPlacement *out_psch = GetTypedSch< CSchItemAttributeWorldItemPlacement >( out_pSchRecord );
  2774. CAttribute_WorldItemPlacement typedValue;
  2775. ConvertEconAttributeValueToTypedValue( value, &typedValue );
  2776. out_psch->m_ulItemID = unItemId;
  2777. out_psch->m_unAttrDefIndex = pAttrDef->GetDefinitionIndex();
  2778. out_psch->m_ulOriginalItemID = typedValue.original_item_id();
  2779. out_psch->m_fPosX = typedValue.pos_x();
  2780. out_psch->m_fPosY = typedValue.pos_y();
  2781. out_psch->m_fPosZ = typedValue.pos_z();
  2782. out_psch->m_fAngX = typedValue.ang_x();
  2783. out_psch->m_fAngY = typedValue.ang_y();
  2784. out_psch->m_fAngZ = typedValue.ang_z();
  2785. }
  2786. virtual void LoadSchToEconAttributeValue( CEconItem *pTargetItem, const CEconItemAttributeDefinition *pAttrDef, const GCSDK::CRecordBase *pSchRecord ) const OVERRIDE
  2787. {
  2788. Assert( pTargetItem );
  2789. Assert( pAttrDef );
  2790. Assert( pSchRecord );
  2791. Assert( pAttrDef->GetAttributeType() == this );
  2792. const CSchItemAttributeWorldItemPlacement *psch = GetTypedSch< const CSchItemAttributeWorldItemPlacement >( pSchRecord );
  2793. CAttribute_WorldItemPlacement typedValue;
  2794. typedValue.set_original_item_id( psch->m_ulOriginalItemID );
  2795. typedValue.set_pos_x( psch->m_fPosX );
  2796. typedValue.set_pos_y( psch->m_fPosY );
  2797. typedValue.set_pos_x( psch->m_fPosZ );
  2798. typedValue.set_ang_x( psch->m_fAngX );
  2799. typedValue.set_ang_y( psch->m_fAngY );
  2800. typedValue.set_ang_z( psch->m_fAngZ );
  2801. pTargetItem->SetDynamicAttributeValue( pAttrDef, typedValue );
  2802. }
  2803. #endif // GC_DLL
  2804. virtual bool BConvertStringToEconAttributeValue( const CEconItemAttributeDefinition *pAttrDef, const char *pszValue, union attribute_data_union_t *out_pValue, bool bEnableTerribleBackwardsCompatibilitySchemaParsingCode ) const OVERRIDE
  2805. {
  2806. Assert( pAttrDef );
  2807. Assert( out_pValue );
  2808. CAttribute_WorldItemPlacement typedValue;
  2809. uint32 unValue = ( pszValue ) ? atoi( pszValue ) : 0;
  2810. // Item forcing us to create the attribute (via force_gc_to_generate)
  2811. if ( unValue == 0 )
  2812. {
  2813. typedValue.set_original_item_id( INVALID_ITEM_ID );
  2814. typedValue.set_pos_x( 0.f );
  2815. typedValue.set_pos_y( 0.f );
  2816. typedValue.set_pos_z( 0.f );
  2817. typedValue.set_ang_x( 0.f );
  2818. typedValue.set_ang_y( 0.f );
  2819. typedValue.set_ang_z( 0.f );
  2820. }
  2821. else
  2822. {
  2823. std::string sValue( pszValue );
  2824. if ( !google::protobuf::TextFormat::ParseFromString( sValue, &typedValue ) )
  2825. return false;
  2826. }
  2827. this->ConvertTypedValueToEconAttributeValue( typedValue, out_pValue );
  2828. return true;
  2829. }
  2830. virtual void ConvertEconAttributeValueToString( const CEconItemAttributeDefinition *pAttrDef, const attribute_data_union_t& value, std::string *out_ps ) const OVERRIDE
  2831. {
  2832. Assert( pAttrDef );
  2833. Assert( out_ps );
  2834. this->ConvertEconAttributeValueToString( pAttrDef, value, out_ps );
  2835. }
  2836. };
  2837. //-----------------------------------------------------------------------------
  2838. // Purpose:
  2839. //-----------------------------------------------------------------------------
  2840. class CSchemaAttributeType_Float : public CSchemaAttributeTypeBase< GC_SCH_REFERENCE( CSchItemAttributeFloat ) float >
  2841. {
  2842. public:
  2843. #ifdef GC_DLL
  2844. virtual void ConvertEconAttributeValueToSch( itemid_t unItemId, const CEconItemAttributeDefinition *pAttrDef, const union attribute_data_union_t& value, GCSDK::CRecordBase *out_pSchRecord ) const OVERRIDE
  2845. {
  2846. Assert( out_pSchRecord );
  2847. Assert( pAttrDef );
  2848. Assert( pAttrDef->GetAttributeType() == this );
  2849. CSchItemAttributeFloat *out_pschItemAttribute = GetTypedSch<CSchItemAttributeFloat>( out_pSchRecord );
  2850. // @note Tom Bui: we store the value as an unsigned integer in the DB, so just treat the field as a bunch of bits
  2851. out_pschItemAttribute->m_ulItemID = unItemId;
  2852. out_pschItemAttribute->m_unAttrDefIndex = pAttrDef->GetDefinitionIndex();
  2853. out_pschItemAttribute->m_fValue = value.asFloat;
  2854. }
  2855. virtual void LoadSchToEconAttributeValue( CEconItem *pTargetItem, const CEconItemAttributeDefinition *pAttrDef, const GCSDK::CRecordBase *pSchRecord ) const OVERRIDE
  2856. {
  2857. Assert( pTargetItem );
  2858. Assert( pAttrDef );
  2859. Assert( pSchRecord );
  2860. Assert( pAttrDef->GetAttributeType() == this );
  2861. const CSchItemAttributeFloat *pschItemAttribute = GetTypedSch<const CSchItemAttributeFloat>( pSchRecord );
  2862. pTargetItem->SetDynamicAttributeValue( pAttrDef, pschItemAttribute->m_fValue );
  2863. }
  2864. #endif // GC_DLL
  2865. virtual bool BConvertStringToEconAttributeValue( const CEconItemAttributeDefinition *pAttrDef, const char *pszValue, union attribute_data_union_t *out_pValue, bool bEnableTerribleBackwardsCompatibilitySchemaParsingCode ) const OVERRIDE
  2866. {
  2867. Assert( pAttrDef );
  2868. Assert( out_pValue );
  2869. out_pValue->asFloat = Q_atof( pszValue );
  2870. return true;
  2871. }
  2872. virtual void ConvertEconAttributeValueToString( const CEconItemAttributeDefinition *pAttrDef, const attribute_data_union_t& value, std::string *out_ps ) const OVERRIDE
  2873. {
  2874. Assert( pAttrDef );
  2875. Assert( out_ps );
  2876. *out_ps = CFmtStr( "%f", value.asFloat ).Get();
  2877. }
  2878. virtual void ConvertTypedValueToByteStream( const float& typedValue, ::std::string *out_psBytes ) const OVERRIDE
  2879. {
  2880. Assert( out_psBytes );
  2881. Assert( out_psBytes->size() == 0 );
  2882. out_psBytes->resize( sizeof( float ) );
  2883. *reinterpret_cast<float *>( &((*out_psBytes)[0]) ) = typedValue; // overwrite string contents (sizeof( float ) bytes)
  2884. }
  2885. virtual void ConvertByteStreamToTypedValue( const ::std::string& sBytes, float *out_pTypedValue ) const OVERRIDE
  2886. {
  2887. Assert( out_pTypedValue );
  2888. Assert( sBytes.size() == sizeof( float ) );
  2889. *out_pTypedValue = *reinterpret_cast<const float *>( &sBytes[0] );
  2890. }
  2891. virtual bool BSupportsGameplayModificationAndNetworking() const OVERRIDE
  2892. {
  2893. return true;
  2894. }
  2895. };
  2896. //-----------------------------------------------------------------------------
  2897. // Purpose:
  2898. //-----------------------------------------------------------------------------
  2899. class CSchemaAttributeType_UInt64 : public CSchemaAttributeTypeBase< GC_SCH_REFERENCE( CSchItemAttributeUInt64 ) uint64 >
  2900. {
  2901. public:
  2902. #ifdef GC_DLL
  2903. virtual void ConvertEconAttributeValueToSch( itemid_t unItemId, const CEconItemAttributeDefinition *pAttrDef, const union attribute_data_union_t& value, GCSDK::CRecordBase *out_pSchRecord ) const OVERRIDE
  2904. {
  2905. Assert( out_pSchRecord );
  2906. Assert( pAttrDef );
  2907. Assert( pAttrDef->GetAttributeType() == this );
  2908. uint64 ulValue;
  2909. ConvertEconAttributeValueToTypedValue( value, &ulValue );
  2910. CSchItemAttributeUInt64 *out_pschItemAttribute = GetTypedSch<CSchItemAttributeUInt64>( out_pSchRecord );
  2911. // @note Tom Bui: we store the value as an unsigned integer in the DB, so just treat the field as a bunch of bits
  2912. out_pschItemAttribute->m_ulItemID = unItemId;
  2913. out_pschItemAttribute->m_unAttrDefIndex = pAttrDef->GetDefinitionIndex();
  2914. out_pschItemAttribute->m_ulValue = ulValue;
  2915. }
  2916. virtual void LoadSchToEconAttributeValue( CEconItem *pTargetItem, const CEconItemAttributeDefinition *pAttrDef, const GCSDK::CRecordBase *pSchRecord ) const OVERRIDE
  2917. {
  2918. Assert( pTargetItem );
  2919. Assert( pAttrDef );
  2920. Assert( pSchRecord );
  2921. Assert( pAttrDef->GetAttributeType() == this );
  2922. const CSchItemAttributeUInt64 *pschItemAttribute = GetTypedSch<const CSchItemAttributeUInt64>( pSchRecord );
  2923. pTargetItem->SetDynamicAttributeValue( pAttrDef, pschItemAttribute->m_ulValue );
  2924. }
  2925. #endif // GC_DLL
  2926. virtual bool BConvertStringToEconAttributeValue( const CEconItemAttributeDefinition *pAttrDef, const char *pszValue, union attribute_data_union_t *out_pValue, bool bEnableTerribleBackwardsCompatibilitySchemaParsingCode ) const OVERRIDE
  2927. {
  2928. Assert( pAttrDef );
  2929. Assert( out_pValue );
  2930. out_pValue->asUint32 = V_atoui64( pszValue );
  2931. return true;
  2932. }
  2933. virtual void ConvertEconAttributeValueToString( const CEconItemAttributeDefinition *pAttrDef, const attribute_data_union_t& value, std::string *out_ps ) const OVERRIDE
  2934. {
  2935. Assert( pAttrDef );
  2936. Assert( out_ps );
  2937. uint64 ulValue;
  2938. ConvertEconAttributeValueToTypedValue( value, &ulValue );
  2939. *out_ps = CFmtStr( "%llu", ulValue ).Get();
  2940. }
  2941. virtual void ConvertTypedValueToByteStream( const uint64& typedValue, ::std::string *out_psBytes ) const OVERRIDE
  2942. {
  2943. Assert( out_psBytes );
  2944. Assert( out_psBytes->size() == 0 );
  2945. out_psBytes->resize( sizeof( uint64 ) );
  2946. *reinterpret_cast<uint64 *>( &((*out_psBytes)[0]) ) = typedValue; // overwrite string contents (sizeof( uint64 ) bytes)
  2947. }
  2948. virtual void ConvertByteStreamToTypedValue( const ::std::string& sBytes, uint64 *out_pTypedValue ) const OVERRIDE
  2949. {
  2950. Assert( out_pTypedValue );
  2951. Assert( sBytes.size() == sizeof( uint64 ) );
  2952. *out_pTypedValue = *reinterpret_cast<const uint64 *>( &sBytes[0] );
  2953. }
  2954. };
  2955. //-----------------------------------------------------------------------------
  2956. // Purpose:
  2957. //-----------------------------------------------------------------------------
  2958. class CSchemaAttributeType_Default : public CSchemaAttributeTypeBase< GC_SCH_REFERENCE( CSchItemAttribute ) attrib_value_t >
  2959. {
  2960. public:
  2961. #ifdef GC_DLL
  2962. virtual bool BAssetClassExportedAttributeValue( const CEconItemAttributeDefinition *pAttrDef, const attribute_data_union_t& value ) const OVERRIDE
  2963. {
  2964. Assert( pAttrDef );
  2965. static CSchemaAttributeDefHandle pAttrib_TradableAfter( "tradable after date" );
  2966. // Don't include "tradable after date" if it's in the past
  2967. // See IEconItemInterface::IsTradable for the specific logic on how this affects tradability
  2968. if ( pAttrDef == pAttrib_TradableAfter && CRTime::RTime32TimeCur() > value.asUint32 )
  2969. return false;
  2970. return CSchemaAttributeTypeBase< GC_SCH_REFERENCE( CSchItemAttribute ) attrib_value_t >::BAssetClassExportedAttributeValue( pAttrDef, value );
  2971. }
  2972. virtual void ConvertEconAttributeValueToSch( itemid_t unItemId, const CEconItemAttributeDefinition *pAttrDef, const union attribute_data_union_t& value, GCSDK::CRecordBase *out_pSchRecord ) const OVERRIDE
  2973. {
  2974. Assert( out_pSchRecord );
  2975. Assert( pAttrDef );
  2976. Assert( pAttrDef->GetAttributeType() == this );
  2977. CSchItemAttribute *out_pschItemAttribute = GetTypedSch<CSchItemAttribute>( out_pSchRecord );
  2978. // @note Tom Bui: we store the value as an unsigned integer in the DB, so just treat the field as a bunch of bits
  2979. out_pschItemAttribute->m_ulItemID = unItemId;
  2980. out_pschItemAttribute->m_unAttrDefIndex = pAttrDef->GetDefinitionIndex();
  2981. out_pschItemAttribute->m_unValue = value.asUint32;
  2982. }
  2983. virtual void LoadSchToEconAttributeValue( CEconItem *pTargetItem, const CEconItemAttributeDefinition *pAttrDef, const GCSDK::CRecordBase *pSchRecord ) const OVERRIDE
  2984. {
  2985. Assert( pTargetItem );
  2986. Assert( pAttrDef );
  2987. Assert( pSchRecord );
  2988. Assert( pAttrDef->GetAttributeType() == this );
  2989. const CSchItemAttribute *pschItemAttribute = GetTypedSch<const CSchItemAttribute>( pSchRecord );
  2990. pTargetItem->SetDynamicAttributeValue( pAttrDef, pschItemAttribute->m_unValue );
  2991. }
  2992. virtual void LoadOrGenerateEconAttributeValue( CEconItem *pTargetItem, const CEconItemAttributeDefinition *pAttrDef, const static_attrib_t& staticAttrib, const CEconGameAccount *pGameAccount ) const OVERRIDE
  2993. {
  2994. Assert( pTargetItem );
  2995. Assert( pTargetItem->GetItemDefinition() );
  2996. Assert( pAttrDef );
  2997. // Wear is reassigned by attributes but has a default value from itemdef prefab
  2998. static CSchemaAttributeDefHandle pAttrDef_PaintkitWear( "set_item_texture_wear" );
  2999. // 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)
  3000. attrib_value_t unValue = 0;
  3001. if ( pTargetItem->FindAttribute( pAttrDef, &unValue ) && pAttrDef != pAttrDef_PaintkitWear )
  3002. {
  3003. AssertMsg4( unValue == staticAttrib.m_value.asUint32,
  3004. "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.",
  3005. pTargetItem->GetItemID(), pTargetItem->GetItemDefinition()->GetDefinitionName(), pAttrDef->GetDefinitionName(), pAttrDef->GetDefinitionIndex() );
  3006. if ( unValue == staticAttrib.m_value.asUint32 )
  3007. return;
  3008. }
  3009. // Could be raw integer bits or raw floating-point bits depending on where in the union we stored the value. We copy the
  3010. // bit pattern indiscriminately.
  3011. attribute_data_union_t ResultValue;
  3012. ResultValue = staticAttrib.m_value;
  3013. GenerateEconAttributeValue( pAttrDef, staticAttrib, pGameAccount, &ResultValue );
  3014. LoadEconAttributeValue( pTargetItem, pAttrDef, ResultValue );
  3015. }
  3016. virtual void GenerateEconAttributeValue( const CEconItemAttributeDefinition *pAttrDef, const static_attrib_t& staticAttrib, const CEconGameAccount *pGameAccount, attribute_data_union_t* out_pValue ) const
  3017. {
  3018. Assert( pAttrDef );
  3019. 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!" );
  3020. Assert( out_pValue );
  3021. if( staticAttrib.m_pKVCustomData )
  3022. {
  3023. Internal_RunCustomAttributeValueLogic( staticAttrib, pGameAccount, out_pValue );
  3024. }
  3025. }
  3026. #endif // GC_DLL
  3027. virtual bool BConvertStringToEconAttributeValue( const CEconItemAttributeDefinition *pAttrDef, const char *pszValue, union attribute_data_union_t *out_pValue, bool bEnableTerribleBackwardsCompatibilitySchemaParsingCode ) const OVERRIDE
  3028. {
  3029. Assert( pAttrDef );
  3030. Assert( out_pValue );
  3031. if ( bEnableTerribleBackwardsCompatibilitySchemaParsingCode )
  3032. {
  3033. // Not having any value specified is valid -- we interpret this as "default", or 0 as both an in int and a float.
  3034. out_pValue->asFloat = pszValue
  3035. ? atof( pszValue )
  3036. : 0.0f;
  3037. }
  3038. // This is terrible backwards-compatibility code to support the pulling of values from econ asset classes.
  3039. else
  3040. {
  3041. if ( pAttrDef->IsStoredAsInteger() )
  3042. {
  3043. out_pValue->asUint32 = (uint32)Q_atoui64( pszValue );
  3044. }
  3045. else if ( pAttrDef->IsStoredAsFloat() )
  3046. {
  3047. out_pValue->asFloat = Q_atof( pszValue );
  3048. }
  3049. else
  3050. {
  3051. Assert( !"Unknown storage type for CSchemaAttributeType_Default::BConvertStringToEconAttributeValue()!" );
  3052. return false;
  3053. }
  3054. }
  3055. return true;
  3056. }
  3057. virtual void ConvertEconAttributeValueToString( const CEconItemAttributeDefinition *pAttrDef, const attribute_data_union_t& value, std::string *out_ps ) const OVERRIDE
  3058. {
  3059. Assert( pAttrDef );
  3060. Assert( out_ps );
  3061. if( pAttrDef->IsStoredAsFloat() )
  3062. {
  3063. *out_ps = CFmtStr( "%f", value.asFloat ).Get();
  3064. }
  3065. else if( pAttrDef->IsStoredAsInteger() )
  3066. {
  3067. *out_ps = CFmtStr( "%u", value.asUint32 ).Get();
  3068. }
  3069. else
  3070. {
  3071. Assert( !"Unknown storage type for CSchemaAttributeType_Default::ConvertEconAttributeValueToString()!" );
  3072. }
  3073. }
  3074. virtual void ConvertTypedValueToByteStream( const attrib_value_t& typedValue, ::std::string *out_psBytes ) const OVERRIDE
  3075. {
  3076. Assert( out_psBytes );
  3077. Assert( out_psBytes->size() == 0 );
  3078. out_psBytes->resize( sizeof( attrib_value_t ) );
  3079. *reinterpret_cast<attrib_value_t *>( &((*out_psBytes)[0]) ) = typedValue; // overwrite string contents (sizeof( attrib_value_t ) bytes)
  3080. }
  3081. virtual void ConvertByteStreamToTypedValue( const ::std::string& sBytes, attrib_value_t *out_pTypedValue ) const OVERRIDE
  3082. {
  3083. Assert( out_pTypedValue );
  3084. #ifdef GC_DLL
  3085. // The GC is expected to always have internally-consistent information.
  3086. Assert( sBytes.size() == sizeof( attrib_value_t ) );
  3087. #else
  3088. // Game clients and servers may have partially out-of-date information, or may have downloaded a new schema
  3089. // but not know how to parse an attribute of a certain type, etc. In these cases, because we know we
  3090. // aren't on the GC, temporarily failing to load these values until the client shuts down and updates
  3091. // is about the best we can hope for.
  3092. if ( sBytes.size() < sizeof( attrib_value_t ) )
  3093. {
  3094. *out_pTypedValue = attrib_value_t();
  3095. return;
  3096. }
  3097. #endif
  3098. *out_pTypedValue = *reinterpret_cast<const attrib_value_t *>( &sBytes[0] );
  3099. }
  3100. virtual bool BSupportsGameplayModificationAndNetworking() const OVERRIDE
  3101. {
  3102. return true;
  3103. }
  3104. private:
  3105. #ifdef GC_DLL
  3106. void Internal_RunCustomAttributeValueLogic( const static_attrib_t& staticAttrib, const CEconGameAccount *pGameAccount, attribute_data_union_t* out_pValue ) const
  3107. {
  3108. AssertMsg( pGameAccount, "No game account when running custom attribute value logic!" );
  3109. float flValue = 0;
  3110. const char *pszMethod = staticAttrib.m_pKVCustomData->GetString( "method", NULL );
  3111. if ( Q_stricmp( pszMethod, "employee_number" ) == 0 )
  3112. {
  3113. flValue = pGameAccount->Obj().m_rtime32FirstPlayed;
  3114. }
  3115. else if ( Q_stricmp( pszMethod, "date" ) == 0 ) // Not used?
  3116. {
  3117. flValue = CRTime::RTime32TimeCur();
  3118. }
  3119. else if ( Q_stricmp( pszMethod, "year" ) == 0 )
  3120. {
  3121. flValue = CRTime( CRTime::RTime32TimeCur() ).GetYear();
  3122. }
  3123. else if ( Q_stricmp( pszMethod, "gifts_given_out" ) == 0 )
  3124. {
  3125. flValue = pGameAccount->Obj().m_unNumGiftsGiven;
  3126. }
  3127. else if ( Q_stricmp( pszMethod, "expiration_period_hours_from_now" ) == 0 )
  3128. {
  3129. flValue = CRTime::RTime32DateAdd( CRTime::RTime32TimeCur(), staticAttrib.m_value.asFloat, k_ETimeUnitHour );
  3130. }
  3131. else if ( Q_stricmp( pszMethod, "def index from lootlist" ) == 0 )
  3132. {
  3133. const char* pszLootlistName = staticAttrib.m_pKVCustomData->GetString( "lootlist" );
  3134. // Custom data stores the lootlist
  3135. const CEconLootListDefinition* pLootList = GEconItemSchema().GetLootListByName( pszLootlistName );
  3136. if( !pLootList )
  3137. {
  3138. AssertMsg1( 0, "Lootlist '%s' not found when performing custom attribute logic", pszLootlistName );
  3139. return;
  3140. }
  3141. CUtlVector<CEconLootListDefinition::rolled_item_defs_t> vecRolledItems;
  3142. // Roll our item def
  3143. CDefaultUniformRandomStream RandomStream;
  3144. if( !pLootList->RollRandomItemsAndAdditionalItems( &RandomStream, false, &vecRolledItems ) )
  3145. {
  3146. AssertMsg1( 0, "Error generating item defs from lootlist '%s'", pszLootlistName );
  3147. return;
  3148. }
  3149. // Just take the first one's def index
  3150. flValue = vecRolledItems.Head().m_pItemDef->GetDefinitionIndex();
  3151. }
  3152. else
  3153. {
  3154. AssertMsg1( false, "Unknown value for 'method': '%s'", pszMethod );
  3155. }
  3156. // Put the value into the right part of the union
  3157. if ( staticAttrib.GetAttributeDefinition()->IsStoredAsFloat() )
  3158. {
  3159. (*out_pValue).asFloat = flValue;
  3160. }
  3161. else if ( staticAttrib.GetAttributeDefinition()->IsStoredAsInteger() )
  3162. {
  3163. (*out_pValue).asUint32 = (uint32)flValue;
  3164. }
  3165. else
  3166. {
  3167. AssertMsg1( 0, "Unknown storage type for CSchemaAttributeType_Default::Internal_RunCustomAttributeValueLogic() for attribute %s", staticAttrib.GetAttributeDefinition()->GetDefinitionName() );
  3168. }
  3169. }
  3170. #endif
  3171. };
  3172. //-----------------------------------------------------------------------------
  3173. // Purpose: Constructor
  3174. //-----------------------------------------------------------------------------
  3175. CEconItemAttributeDefinition::CEconItemAttributeDefinition( void )
  3176. : m_pKVAttribute( NULL ),
  3177. m_pAttrType( NULL ),
  3178. m_bHidden( false ),
  3179. m_bWebSchemaOutputForced( false ),
  3180. m_bStoredAsInteger( false ),
  3181. m_bInstanceData( false ),
  3182. m_bIsSetBonus( false ),
  3183. m_iUserGenerationType( 0 ),
  3184. m_iEffectType( ATTRIB_EFFECT_NEUTRAL ),
  3185. m_iDescriptionFormat( 0 ),
  3186. m_pszDescriptionString( NULL ),
  3187. m_pszArmoryDesc( NULL ),
  3188. m_pszDefinitionName( NULL ),
  3189. m_pszAttributeClass( NULL ),
  3190. m_ItemDefinitionTag( INVALID_ECON_TAG_HANDLE ),
  3191. m_bCanAffectMarketName( false ),
  3192. m_bCanAffectRecipeComponentName( false )
  3193. #ifndef GC_DLL
  3194. , m_iszAttributeClass( NULL_STRING )
  3195. #endif
  3196. {
  3197. }
  3198. //-----------------------------------------------------------------------------
  3199. // Purpose: Copy constructor
  3200. //-----------------------------------------------------------------------------
  3201. CEconItemAttributeDefinition::CEconItemAttributeDefinition( const CEconItemAttributeDefinition &that )
  3202. {
  3203. (*this) = that;
  3204. }
  3205. //-----------------------------------------------------------------------------
  3206. // Purpose: Operator=
  3207. //-----------------------------------------------------------------------------
  3208. CEconItemAttributeDefinition &CEconItemAttributeDefinition::operator=( const CEconItemAttributeDefinition &rhs )
  3209. {
  3210. m_nDefIndex = rhs.m_nDefIndex;
  3211. m_pAttrType = rhs.m_pAttrType;
  3212. m_bHidden = rhs.m_bHidden;
  3213. m_bWebSchemaOutputForced = rhs.m_bWebSchemaOutputForced;
  3214. m_bStoredAsInteger = rhs.m_bStoredAsInteger;
  3215. m_iUserGenerationType = rhs.m_iUserGenerationType;
  3216. m_bInstanceData = rhs.m_bInstanceData;
  3217. m_bIsSetBonus = rhs.m_bIsSetBonus;
  3218. m_iEffectType = rhs.m_iEffectType;
  3219. m_iDescriptionFormat = rhs.m_iDescriptionFormat;
  3220. m_pszDescriptionString = rhs.m_pszDescriptionString;
  3221. m_pszArmoryDesc = rhs.m_pszArmoryDesc;
  3222. m_pszDefinitionName = rhs.m_pszDefinitionName;
  3223. m_pszAttributeClass = rhs.m_pszAttributeClass;
  3224. m_ItemDefinitionTag = rhs.m_ItemDefinitionTag;
  3225. m_bCanAffectMarketName = rhs.m_bCanAffectMarketName;
  3226. m_bCanAffectRecipeComponentName = rhs.m_bCanAffectRecipeComponentName;
  3227. #ifndef GC_DLL
  3228. m_iszAttributeClass = rhs.m_iszAttributeClass;
  3229. #endif
  3230. m_pKVAttribute = NULL;
  3231. if ( NULL != rhs.m_pKVAttribute )
  3232. {
  3233. m_pKVAttribute = rhs.m_pKVAttribute->MakeCopy();
  3234. // Re-assign string pointers
  3235. m_pszDefinitionName = m_pKVAttribute->GetString("name");
  3236. m_pszDescriptionString = m_pKVAttribute->GetString( "description_string", NULL );
  3237. m_pszArmoryDesc = m_pKVAttribute->GetString( "armory_desc", NULL );
  3238. m_pszAttributeClass = m_pKVAttribute->GetString( "attribute_class", NULL );
  3239. Assert( V_strcmp( m_pszDefinitionName, rhs.m_pszDefinitionName ) == 0 );
  3240. Assert( V_strcmp( m_pszDescriptionString, rhs.m_pszDescriptionString ) == 0 );
  3241. Assert( V_strcmp( m_pszArmoryDesc, rhs.m_pszArmoryDesc ) == 0 );
  3242. Assert( V_strcmp( m_pszAttributeClass, rhs.m_pszAttributeClass ) == 0 );
  3243. }
  3244. else
  3245. {
  3246. Assert( m_pszDefinitionName == NULL );
  3247. Assert( m_pszDescriptionString == NULL );
  3248. Assert( m_pszArmoryDesc == NULL );
  3249. Assert( m_pszAttributeClass == NULL );
  3250. }
  3251. return *this;
  3252. }
  3253. //-----------------------------------------------------------------------------
  3254. // Purpose: Destructor
  3255. //-----------------------------------------------------------------------------
  3256. CEconItemAttributeDefinition::~CEconItemAttributeDefinition( void )
  3257. {
  3258. if ( m_pKVAttribute )
  3259. m_pKVAttribute->deleteThis();
  3260. m_pKVAttribute = NULL;
  3261. }
  3262. //-----------------------------------------------------------------------------
  3263. // Purpose: Initialize the attribute definition
  3264. // Input: pKVAttribute - The KeyValues representation of the attribute
  3265. // schema - The overall item schema for this attribute
  3266. // pVecErrors - An optional vector that will contain error messages if
  3267. // the init fails.
  3268. // Output: True if initialization succeeded, false otherwise
  3269. //-----------------------------------------------------------------------------
  3270. bool CEconItemAttributeDefinition::BInitFromKV( KeyValues *pKVAttribute, CUtlVector<CUtlString> *pVecErrors /* = NULL */ )
  3271. {
  3272. m_pKVAttribute = pKVAttribute->MakeCopy();
  3273. m_nDefIndex = Q_atoi( m_pKVAttribute->GetName() );
  3274. m_pszDefinitionName = m_pKVAttribute->GetString("name", "(unnamed)");
  3275. m_bHidden = m_pKVAttribute->GetInt( "hidden", 0 ) != 0;
  3276. m_bWebSchemaOutputForced = m_pKVAttribute->GetInt( "force_output_description", 0 ) != 0;
  3277. m_bStoredAsInteger = m_pKVAttribute->GetInt( "stored_as_integer", 0 ) != 0;
  3278. m_bIsSetBonus = m_pKVAttribute->GetBool( "is_set_bonus", false );
  3279. m_bCanAffectMarketName = m_pKVAttribute->GetBool( "can_affect_market_name", false );
  3280. m_bCanAffectRecipeComponentName = m_pKVAttribute->GetBool( "can_affect_recipe_component_name", false );
  3281. m_iUserGenerationType = m_pKVAttribute->GetInt( "is_user_generated", 0 );
  3282. m_iEffectType = (attrib_effect_types_t)StringFieldToInt( m_pKVAttribute->GetString("effect_type"), g_EffectTypes, ARRAYSIZE(g_EffectTypes) );
  3283. m_iDescriptionFormat = StringFieldToInt( m_pKVAttribute->GetString("description_format"), g_AttributeDescriptionFormats, ARRAYSIZE(g_AttributeDescriptionFormats) );
  3284. m_pszDescriptionString = m_pKVAttribute->GetString( "description_string", NULL );
  3285. m_pszArmoryDesc = m_pKVAttribute->GetString( "armory_desc", NULL );
  3286. m_pszAttributeClass = m_pKVAttribute->GetString( "attribute_class", NULL );
  3287. m_bInstanceData = pKVAttribute->GetBool( "instance_data", false );
  3288. const char *pszTag = m_pKVAttribute->GetString( "apply_tag_to_item_definition", NULL );
  3289. m_ItemDefinitionTag = pszTag ? GetItemSchema()->GetHandleForTag( pszTag ) : INVALID_ECON_TAG_HANDLE;
  3290. #if defined(CLIENT_DLL) || defined(GAME_DLL)
  3291. m_iszAttributeClass = NULL_STRING;
  3292. #endif
  3293. const char *pszAttrType = m_pKVAttribute->GetString( "attribute_type", NULL ); // NULL implies "default type" for backwards compatibility
  3294. m_pAttrType = GetItemSchema()->GetAttributeType( pszAttrType );
  3295. SCHEMA_INIT_CHECK(
  3296. NULL != m_pKVAttribute->FindKey( "name" ),
  3297. "Attribute definition %s: Missing required field \"name\"", m_pKVAttribute->GetName() );
  3298. SCHEMA_INIT_CHECK(
  3299. NULL != m_pAttrType,
  3300. "Attribute definition %s: Unable to find attribute data type '%s'", m_pszDefinitionName, pszAttrType ? pszAttrType : "(default)" );
  3301. if ( m_bIsSetBonus )
  3302. {
  3303. SCHEMA_INIT_CHECK(
  3304. m_pAttrType->BSupportsGameplayModificationAndNetworking(),
  3305. "Attribute definition %s: set as set bonus attribute but does not support gameplay modification/networking!", m_pszDefinitionName );
  3306. }
  3307. m_unAssetClassBucket = pKVAttribute->GetInt( "asset_class_bucket", 0 );
  3308. m_eAssetClassAttrExportRule = k_EAssetClassAttrExportRule_Default;
  3309. if ( char const *szRule = pKVAttribute->GetString( "asset_class_export", NULL ) )
  3310. {
  3311. if ( !V_stricmp( szRule, "skip" ) )
  3312. {
  3313. m_eAssetClassAttrExportRule = k_EAssetClassAttrExportRule_Skip;
  3314. }
  3315. else if ( !V_stricmp( szRule, "gconly" ) )
  3316. {
  3317. m_eAssetClassAttrExportRule = EAssetClassAttrExportRule_t( k_EAssetClassAttrExportRule_GCOnly | k_EAssetClassAttrExportRule_Skip );
  3318. }
  3319. else if ( !V_stricmp( szRule, "bucketed" ) )
  3320. {
  3321. SCHEMA_INIT_CHECK( m_unAssetClassBucket, "Attribute definition %s: Asset class export rule '%s' is incompatible", m_pszDefinitionName, szRule );
  3322. m_eAssetClassAttrExportRule = k_EAssetClassAttrExportRule_Bucketed;
  3323. }
  3324. else if ( !V_stricmp( szRule, "default" ) )
  3325. {
  3326. m_eAssetClassAttrExportRule = k_EAssetClassAttrExportRule_Default;
  3327. }
  3328. else
  3329. {
  3330. SCHEMA_INIT_CHECK( false, "Attribute definition %s: Invalid asset class export rule '%s'", m_pszDefinitionName, szRule );
  3331. }
  3332. }
  3333. // Check for misuse of asset class bucket
  3334. SCHEMA_INIT_CHECK( ( !m_unAssetClassBucket || m_bInstanceData ), "Attribute definition %s: Cannot use \"asset_class_bucket\" on class-level attributes", m_pKVAttribute->GetName() );
  3335. return SCHEMA_INIT_SUCCESS();
  3336. }
  3337. CQuestObjectiveDefinition::CQuestObjectiveDefinition( void )
  3338. : m_pszDescriptionToken( NULL )
  3339. , m_nDefIndex( 0 )
  3340. , m_nPoints( 0 )
  3341. {}
  3342. CQuestObjectiveDefinition::~CQuestObjectiveDefinition()
  3343. {}
  3344. bool CQuestObjectiveDefinition::BInitFromKV( KeyValues *pKVItem, CUtlVector<CUtlString> *pVecErrors /* = NULL */ )
  3345. {
  3346. m_nDefIndex = pKVItem->GetInt( "defindex", -1 );
  3347. m_pszDescriptionToken = pKVItem->GetString( "description_string" );
  3348. m_bOptional = pKVItem->GetBool( "optional", false );
  3349. m_bAdvanced = pKVItem->GetBool( "advanced", false );
  3350. m_nPoints = pKVItem->GetInt( "points", 0 );
  3351. SCHEMA_INIT_CHECK( m_nDefIndex != -1, "Quest objective missing def index" );
  3352. SCHEMA_INIT_CHECK( m_pszDescriptionToken != NULL, "Quest objective is missing a description" );
  3353. return SCHEMA_INIT_SUCCESS();
  3354. }
  3355. //-----------------------------------------------------------------------------
  3356. // Purpose: Constructor
  3357. //-----------------------------------------------------------------------------
  3358. CEconItemDefinition::CEconItemDefinition( void )
  3359. : m_pKVItem( NULL ),
  3360. m_bEnabled( false ),
  3361. m_unMinItemLevel( 1 ),
  3362. m_unMaxItemLevel( 1 ),
  3363. m_iArmoryRemap( 0 ),
  3364. m_iStoreRemap( 0 ),
  3365. m_nItemQuality( k_unItemQuality_Any ),
  3366. m_nForcedItemQuality( k_unItemQuality_Any ),
  3367. m_nDefaultDropQuantity( 1 ),
  3368. m_bLoadOnDemand( false ),
  3369. m_pTool( NULL ),
  3370. m_rtExpiration( 0 ),
  3371. m_BundleInfo( NULL ),
  3372. #ifdef TF_CLIENT_DLL
  3373. m_unNumConcreteItems( 0 ),
  3374. #endif // TF_CLIENT_DLL
  3375. m_nPopularitySeed( 0 ),
  3376. m_pszDefinitionName( NULL ),
  3377. m_pszItemClassname( NULL ),
  3378. m_pszClassToken( NULL ),
  3379. m_pszSlotToken( NULL ),
  3380. m_pszItemBaseName( NULL ),
  3381. m_pszItemTypeName( NULL ),
  3382. m_pszItemDesc( NULL ),
  3383. m_pszArmoryDesc( NULL ),
  3384. m_pszInventoryModel( NULL ),
  3385. m_pszInventoryImage( NULL ),
  3386. m_pszHolidayRestriction( NULL ),
  3387. m_iSubType( 0 ),
  3388. m_pszBaseDisplayModel( NULL ),
  3389. m_iDefaultSkin( -1 ),
  3390. m_pszWorldDisplayModel( NULL ),
  3391. m_pszWorldExtraWearableModel( NULL ),
  3392. m_pszWorldExtraWearableViewModel( NULL ),
  3393. m_pszVisionFilteredDisplayModel( NULL ),
  3394. m_pszBrassModelOverride( NULL ),
  3395. m_bHideBodyGroupsDeployedOnly( false ),
  3396. m_bAttachToHands( false ),
  3397. m_bAttachToHandsVMOnly( false ),
  3398. m_bProperName( false ),
  3399. m_bFlipViewModel( false ),
  3400. m_bActAsWearable( false ),
  3401. m_bActAsWeapon( false ),
  3402. m_iDropType( 1 ),
  3403. m_bHidden( false ),
  3404. m_bShouldShowInArmory( false ),
  3405. m_bIsPackBundle( false ),
  3406. m_pOwningPackBundle( NULL ),
  3407. m_bIsPackItem( false ),
  3408. m_bBaseItem( false ),
  3409. m_pszItemLogClassname( NULL ),
  3410. m_pszItemIconClassname( NULL ),
  3411. m_pszDatabaseAuditTable( NULL ),
  3412. m_bImported( false ),
  3413. m_pItemSetDef( NULL ),
  3414. m_pItemCollectionDef( NULL ),
  3415. m_pItemPaintKitDef( NULL ),
  3416. m_pszArmoryRemap( NULL ),
  3417. m_pszStoreRemap( NULL ),
  3418. m_unSetItemRemapDefIndex( INVALID_ITEM_DEF_INDEX ),
  3419. m_pszXifierRemapClass( NULL ),
  3420. m_pszBaseFunctionalItemName( NULL ),
  3421. m_pszParticleSuffix( NULL ),
  3422. m_pszCollectionReference( NULL ),
  3423. m_nItemRarity( k_unItemRarity_Any ),
  3424. m_unItemSeries( 0 ),
  3425. m_bValidForShuffle( false ),
  3426. m_bValidForSelfMade( true )
  3427. {
  3428. for ( int team = 0; team < TEAM_VISUAL_SECTIONS; team++ )
  3429. {
  3430. m_PerTeamVisuals[team] = NULL;
  3431. }
  3432. m_pDictIcons = new CUtlDict< CUtlString >;
  3433. }
  3434. //-----------------------------------------------------------------------------
  3435. // Purpose: Destructor
  3436. //-----------------------------------------------------------------------------
  3437. CEconItemDefinition::~CEconItemDefinition( void )
  3438. {
  3439. for ( int i = 0; i < ARRAYSIZE( m_PerTeamVisuals ); i++ )
  3440. delete m_PerTeamVisuals[i];
  3441. #ifdef GC_DLL
  3442. m_vecPropertyGenerators.PurgeAndDeleteElements();
  3443. #endif // GC_DLL
  3444. if ( m_pKVItem )
  3445. m_pKVItem->deleteThis();
  3446. m_pKVItem = NULL;
  3447. delete m_pTool;
  3448. delete m_BundleInfo;
  3449. delete m_pDictIcons;
  3450. }
  3451. #if defined(CLIENT_DLL) || defined(GAME_DLL)
  3452. //-----------------------------------------------------------------------------
  3453. // Purpose: Stomp our base data with extra testing data specified by the player
  3454. //-----------------------------------------------------------------------------
  3455. bool CEconItemDefinition::BInitFromTestItemKVs( int iNewDefIndex, KeyValues *pKVItem, CUtlVector<CUtlString>* pVecErrors )
  3456. {
  3457. // The KeyValues are stored in the player entity, so we can cache our name there
  3458. m_nDefIndex = iNewDefIndex;
  3459. m_unSetItemRemapDefIndex = m_nDefIndex;
  3460. bool bTestingExistingItem = pKVItem->GetBool( "test_existing_item", false );
  3461. if ( !bTestingExistingItem )
  3462. {
  3463. m_pszDefinitionName = pKVItem->GetString( "name", NULL );
  3464. m_pszItemBaseName = pKVItem->GetString( "name", NULL );
  3465. #ifdef CLIENT_DLL
  3466. pKVItem->SetString( "name", VarArgs("Test Item %d", iNewDefIndex) );
  3467. #else
  3468. pKVItem->SetString( "name", UTIL_VarArgs("Test Item %d", iNewDefIndex) );
  3469. #endif
  3470. m_pszBaseDisplayModel = pKVItem->GetString( "model_player", NULL );
  3471. m_pszVisionFilteredDisplayModel = pKVItem->GetString( "model_vision_filtered", NULL );
  3472. m_bAttachToHands = pKVItem->GetInt( "attach_to_hands", 0 ) != 0;
  3473. BInitVisualBlockFromKV( pKVItem );
  3474. }
  3475. // Handle attributes
  3476. m_vecStaticAttributes.Purge();
  3477. int iPaintCanIndex = pKVItem->GetInt("paintcan_index", 0);
  3478. if ( iPaintCanIndex )
  3479. {
  3480. static CSchemaAttributeDefHandle pAttrDef_PaintRGB( "set item tint RGB" );
  3481. const CEconItemDefinition *pCanDef = GetItemSchema()->GetItemDefinition(iPaintCanIndex);
  3482. float flRGBVal;
  3483. if ( pCanDef && pAttrDef_PaintRGB && FindAttribute_UnsafeBitwiseCast<attrib_value_t>( pCanDef, pAttrDef_PaintRGB, &flRGBVal ) )
  3484. {
  3485. static_attrib_t& StaticAttrib = m_vecStaticAttributes[ m_vecStaticAttributes.AddToTail() ];
  3486. StaticAttrib.iDefIndex = pAttrDef_PaintRGB->GetDefinitionIndex();
  3487. 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
  3488. }
  3489. }
  3490. int iUnusualEffectIndex = pKVItem->GetInt( "unusual_index", 0 );
  3491. if ( iUnusualEffectIndex )
  3492. {
  3493. static CSchemaAttributeDefHandle pAttrDef_AttachParticleStatic( "attach particle effect static" );
  3494. const attachedparticlesystem_t *pSystem = GetItemSchema()->GetAttributeControlledParticleSystem( iUnusualEffectIndex );
  3495. if ( pAttrDef_AttachParticleStatic && pSystem )
  3496. {
  3497. static_attrib_t& StaticAttrib = m_vecStaticAttributes[ m_vecStaticAttributes.AddToTail() ];
  3498. StaticAttrib.iDefIndex = pAttrDef_AttachParticleStatic->GetDefinitionIndex();
  3499. 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
  3500. }
  3501. }
  3502. return true;
  3503. }
  3504. animation_on_wearable_t *GetOrCreateAnimationActivity( perteamvisuals_t *pVisData, const char *pszActivityName )
  3505. {
  3506. FOR_EACH_VEC( pVisData->m_Animations, i )
  3507. {
  3508. if ( Q_stricmp(pVisData->m_Animations[i].pszActivity, pszActivityName) == 0 )
  3509. return &pVisData->m_Animations[i];
  3510. }
  3511. animation_on_wearable_t *pEntry = &pVisData->m_Animations[pVisData->m_Animations.AddToTail()];
  3512. pEntry->iActivity = kActivityLookup_Unknown; // We can't look it up yet, the activity list hasn't been populated.
  3513. pEntry->pszActivity = pszActivityName;
  3514. pEntry->iReplacement = kActivityLookup_Unknown;
  3515. pEntry->pszReplacement = NULL;
  3516. pEntry->pszSequence = NULL;
  3517. pEntry->pszScene = NULL;
  3518. pEntry->pszRequiredItem = NULL;
  3519. return pEntry;
  3520. }
  3521. activity_on_wearable_t *GetOrCreatePlaybackActivity( perteamvisuals_t *pVisData, wearableanimplayback_t iPlayback )
  3522. {
  3523. FOR_EACH_VEC( pVisData->m_Animations, i )
  3524. {
  3525. if ( pVisData->m_Activities[i].iPlayback == iPlayback )
  3526. return &pVisData->m_Activities[i];
  3527. }
  3528. activity_on_wearable_t *pEntry = &pVisData->m_Activities[pVisData->m_Activities.AddToTail()];
  3529. pEntry->iPlayback = iPlayback;
  3530. pEntry->iActivity = kActivityLookup_Unknown; // We can't look it up yet, the activity list hasn't been populated.
  3531. pEntry->pszActivity = NULL;
  3532. return pEntry;
  3533. }
  3534. #endif // defined(CLIENT_DLL) || defined(GAME_DLL)
  3535. //-----------------------------------------------------------------------------
  3536. // Purpose: Handle parsing the per-team visual block from the keyvalues
  3537. //-----------------------------------------------------------------------------
  3538. void CEconItemDefinition::BInitVisualBlockFromKV( KeyValues *pKVItem, CUtlVector<CUtlString> *pVecErrors )
  3539. {
  3540. // Visuals
  3541. for ( int team = 0; team < TEAM_VISUAL_SECTIONS; team++ )
  3542. {
  3543. m_PerTeamVisuals[team] = NULL;
  3544. if ( !g_TeamVisualSections[team] )
  3545. continue;
  3546. KeyValues *pVisualsKV = pKVItem->FindKey( g_TeamVisualSections[team] );
  3547. if ( pVisualsKV )
  3548. {
  3549. perteamvisuals_t *pVisData = new perteamvisuals_t();
  3550. #if defined(CLIENT_DLL) || defined(GAME_DLL)
  3551. KeyValues *pKVEntry = pVisualsKV->GetFirstSubKey();
  3552. while ( pKVEntry )
  3553. {
  3554. const char *pszEntry = pKVEntry->GetName();
  3555. if ( !Q_stricmp( pszEntry, "use_visualsblock_as_base" ) )
  3556. {
  3557. // Start with a copy of an existing PerTeamVisuals
  3558. const char *pszString = pKVEntry->GetString();
  3559. int nOverrideTeam = GetTeamVisualsFromString( pszString );
  3560. if ( nOverrideTeam != -1 )
  3561. {
  3562. *pVisData = *m_PerTeamVisuals[nOverrideTeam];
  3563. }
  3564. else
  3565. {
  3566. pVecErrors->AddToTail( CFmtStr( "Unknown visuals block: %s", pszString ).Access() );
  3567. }
  3568. }
  3569. else if ( !Q_stricmp( pszEntry, "attached_models" ) )
  3570. {
  3571. FOR_EACH_SUBKEY( pKVEntry, pKVAttachedModelData )
  3572. {
  3573. int iAtt = pVisData->m_AttachedModels.AddToTail();
  3574. pVisData->m_AttachedModels[iAtt].m_iModelDisplayFlags = pKVAttachedModelData->GetInt( "model_display_flags", kAttachedModelDisplayFlag_MaskAll );
  3575. pVisData->m_AttachedModels[iAtt].m_pszModelName = pKVAttachedModelData->GetString( "model", NULL );
  3576. }
  3577. }
  3578. else if ( !Q_stricmp( pszEntry, "attached_models_festive" ) )
  3579. {
  3580. FOR_EACH_SUBKEY( pKVEntry, pKVAttachedModelData )
  3581. {
  3582. int iAtt = pVisData->m_AttachedModelsFestive.AddToTail();
  3583. pVisData->m_AttachedModelsFestive[iAtt].m_iModelDisplayFlags = pKVAttachedModelData->GetInt( "model_display_flags", kAttachedModelDisplayFlag_MaskAll );
  3584. pVisData->m_AttachedModelsFestive[iAtt].m_pszModelName = pKVAttachedModelData->GetString( "model", NULL );
  3585. }
  3586. }
  3587. else if ( !Q_stricmp( pszEntry, "attached_particlesystems" ) )
  3588. {
  3589. FOR_EACH_SUBKEY( pKVEntry, pKVAttachedParticleSystemData )
  3590. {
  3591. int iAtt = pVisData->m_AttachedParticles.AddToTail();
  3592. pVisData->m_AttachedParticles[iAtt].pszSystemName = pKVAttachedParticleSystemData->GetString( "system", NULL );
  3593. pVisData->m_AttachedParticles[iAtt].pszControlPoints[0] = pKVAttachedParticleSystemData->GetString( "attachment", NULL );
  3594. pVisData->m_AttachedParticles[iAtt].bFollowRootBone = pKVAttachedParticleSystemData->GetBool( "attach_to_rootbone" );
  3595. pVisData->m_AttachedParticles[iAtt].iCustomType = 0;
  3596. }
  3597. }
  3598. else if ( !Q_stricmp( pszEntry, "custom_particlesystem2" ) )
  3599. {
  3600. int iAtt = pVisData->m_AttachedParticles.AddToTail();
  3601. pVisData->m_AttachedParticles[iAtt].pszSystemName = pKVEntry->GetString( "system", NULL );
  3602. pVisData->m_AttachedParticles[iAtt].iCustomType = 2;
  3603. }
  3604. else if ( !Q_stricmp( pszEntry, "custom_particlesystem" ) )
  3605. {
  3606. int iAtt = pVisData->m_AttachedParticles.AddToTail();
  3607. pVisData->m_AttachedParticles[iAtt].pszSystemName = pKVEntry->GetString( "system", NULL );
  3608. pVisData->m_AttachedParticles[iAtt].iCustomType = 1;
  3609. }
  3610. else if ( !Q_stricmp( pszEntry, "playback_activity" ) )
  3611. {
  3612. FOR_EACH_SUBKEY( pKVEntry, pKVSubKey )
  3613. {
  3614. int iPlaybackInt = StringFieldToInt( pKVSubKey->GetName(), g_WearableAnimTypeStrings, ARRAYSIZE(g_WearableAnimTypeStrings) );
  3615. if ( iPlaybackInt >= 0 )
  3616. {
  3617. activity_on_wearable_t *pEntry = GetOrCreatePlaybackActivity( pVisData, (wearableanimplayback_t)iPlaybackInt );
  3618. pEntry->pszActivity = pKVSubKey->GetString();
  3619. }
  3620. }
  3621. }
  3622. else if ( !Q_stricmp( pszEntry, "animation_replacement" ) )
  3623. {
  3624. FOR_EACH_SUBKEY( pKVEntry, pKVSubKey )
  3625. {
  3626. animation_on_wearable_t *pEntry = GetOrCreateAnimationActivity( pVisData, pKVSubKey->GetName() );
  3627. pEntry->pszReplacement = pKVSubKey->GetString();
  3628. }
  3629. }
  3630. else if ( !Q_stricmp( pszEntry, "animation_sequence" ) )
  3631. {
  3632. FOR_EACH_SUBKEY( pKVEntry, pKVSubKey )
  3633. {
  3634. animation_on_wearable_t *pEntry = GetOrCreateAnimationActivity( pVisData, pKVSubKey->GetName() );
  3635. pEntry->pszSequence = pKVSubKey->GetString();
  3636. }
  3637. }
  3638. else if ( !Q_stricmp( pszEntry, "animation_scene" ) )
  3639. {
  3640. FOR_EACH_SUBKEY( pKVEntry, pKVSubKey )
  3641. {
  3642. animation_on_wearable_t *pEntry = GetOrCreateAnimationActivity( pVisData, pKVSubKey->GetName() );
  3643. pEntry->pszScene = pKVSubKey->GetString();
  3644. }
  3645. }
  3646. else if ( !Q_stricmp( pszEntry, "animation_required_item" ) )
  3647. {
  3648. FOR_EACH_SUBKEY( pKVEntry, pKVSubKey )
  3649. {
  3650. animation_on_wearable_t *pEntry = GetOrCreateAnimationActivity( pVisData, pKVSubKey->GetName() );
  3651. pEntry->pszRequiredItem = pKVSubKey->GetString();
  3652. }
  3653. }
  3654. else if ( !Q_stricmp( pszEntry, "player_bodygroups" ) )
  3655. {
  3656. FOR_EACH_SUBKEY( pKVEntry, pKVBodygroupKey )
  3657. {
  3658. const char *pszBodygroupName = pKVBodygroupKey->GetName();
  3659. int iValue = pKVBodygroupKey->GetInt();
  3660. // Track bodygroup information for this item in particular.
  3661. pVisData->m_Maps.m_ModifiedBodyGroupNames.Insert( pszBodygroupName, iValue );
  3662. // Track global schema state.
  3663. GetItemSchema()->AssignDefaultBodygroupState( pszBodygroupName, iValue );
  3664. }
  3665. }
  3666. else if ( !Q_stricmp( pszEntry, "skin" ) )
  3667. {
  3668. pVisData->iSkin = pKVEntry->GetInt();
  3669. }
  3670. else if ( !Q_stricmp( pszEntry, "use_per_class_bodygroups" ) )
  3671. {
  3672. pVisData->bUsePerClassBodygroups = pKVEntry->GetBool();
  3673. }
  3674. else if ( !Q_stricmp( pszEntry, "muzzle_flash" ) )
  3675. {
  3676. pVisData->pszMuzzleFlash = pKVEntry->GetString();
  3677. }
  3678. else if ( !Q_stricmp( pszEntry, "tracer_effect" ) )
  3679. {
  3680. pVisData->pszTracerEffect = pKVEntry->GetString();
  3681. }
  3682. else if ( !Q_stricmp( pszEntry, "particle_effect" ) )
  3683. {
  3684. pVisData->pszParticleEffect = pKVEntry->GetString();
  3685. }
  3686. else if ( !Q_strnicmp( pszEntry, "custom_sound", 12 ) ) // intentionally comparing prefixes
  3687. {
  3688. int iIndex = 0;
  3689. if ( pszEntry[12] )
  3690. {
  3691. iIndex = clamp( atoi( &pszEntry[12] ), 0, MAX_VISUALS_CUSTOM_SOUNDS-1 );
  3692. }
  3693. pVisData->pszCustomSounds[iIndex] = pKVEntry->GetString();
  3694. }
  3695. else if ( !Q_stricmp( pszEntry, "material_override" ) )
  3696. {
  3697. pVisData->pszMaterialOverride = pKVEntry->GetString();
  3698. }
  3699. else if ( !Q_strnicmp( pszEntry, "sound_", 6 ) ) // intentionally comparing prefixes
  3700. {
  3701. int iIndex = GetWeaponSoundFromString( &pszEntry[6] );
  3702. if ( iIndex != -1 )
  3703. {
  3704. pVisData->pszWeaponSoundReplacements[iIndex] = pKVEntry->GetString();
  3705. }
  3706. }
  3707. else if ( !Q_stricmp( pszEntry, "code_controlled_bodygroup" ) )
  3708. {
  3709. const char *pBodyGroupName = pKVEntry->GetString( "bodygroup", NULL );
  3710. const char *pFuncName = pKVEntry->GetString( "function", NULL );
  3711. if ( pBodyGroupName && pFuncName )
  3712. {
  3713. codecontrolledbodygroupdata_t ccbgd = { pFuncName, NULL };
  3714. pVisData->m_Maps.m_CodeControlledBodyGroupNames.Insert( pBodyGroupName, ccbgd );
  3715. }
  3716. }
  3717. else if ( !Q_stricmp( pszEntry, "vm_bodygroup_override" ) )
  3718. {
  3719. pVisData->m_iViewModelBodyGroupOverride = pKVEntry->GetInt();
  3720. }
  3721. else if ( !Q_stricmp( pszEntry, "vm_bodygroup_state_override" ) )
  3722. {
  3723. pVisData->m_iViewModelBodyGroupStateOverride = pKVEntry->GetInt();
  3724. }
  3725. else if ( !Q_stricmp( pszEntry, "wm_bodygroup_override" ) )
  3726. {
  3727. pVisData->m_iWorldModelBodyGroupOverride = pKVEntry->GetInt();
  3728. }
  3729. else if ( !Q_stricmp( pszEntry, "wm_bodygroup_state_override" ) )
  3730. {
  3731. pVisData->m_iWorldModelBodyGroupStateOverride = pKVEntry->GetInt();
  3732. }
  3733. pKVEntry = pKVEntry->GetNextKey();
  3734. }
  3735. #endif // defined(CLIENT_DLL) || defined(GAME_DLL)
  3736. KeyValues *pStylesDataKV = pVisualsKV->FindKey( "styles" );
  3737. if ( pStylesDataKV )
  3738. {
  3739. // Styles are only valid in the base "visuals" section.
  3740. if ( team == 0 )
  3741. {
  3742. BInitStylesBlockFromKV( pStylesDataKV, pVisData, pVecErrors );
  3743. }
  3744. // ...but they used to be valid everywhere, so spit out a warning if people are trying to use
  3745. // the old style of per-team styles.
  3746. else
  3747. {
  3748. pVecErrors->AddToTail( "Per-team styles blocks are no longer valid. Use \"skin_red\" and \"skin_blu\" in a style entry instead." );
  3749. }
  3750. }
  3751. m_PerTeamVisuals[team] = pVisData;
  3752. }
  3753. }
  3754. }
  3755. #ifdef GC_DLL
  3756. //-----------------------------------------------------------------------------
  3757. // Purpose:
  3758. //-----------------------------------------------------------------------------
  3759. template < typename T >
  3760. static void NthPermutation ( T *pData, unsigned int unDataCount, unsigned int unIdx )
  3761. {
  3762. for ( unsigned int i = 1; i < unDataCount; i++ )
  3763. {
  3764. std::swap( pData[ unIdx % (i + 1) ], pData[ i ] );
  3765. unIdx = unIdx / (i + 1);
  3766. }
  3767. }
  3768. //-----------------------------------------------------------------------------
  3769. // Purpose:
  3770. //-----------------------------------------------------------------------------
  3771. bool CEconItemDefinition::BApplyPropertyGenerators( CEconItem *pItem ) const
  3772. {
  3773. Assert( pItem );
  3774. for ( const IEconItemPropertyGenerator *pPropertyGenerator : m_vecPropertyGenerators )
  3775. {
  3776. if ( !pPropertyGenerator->BGenerateProperties( pItem ) )
  3777. return false;
  3778. }
  3779. return true;
  3780. }
  3781. #endif // GC_DLL
  3782. #if defined(CLIENT_DLL) || defined(GAME_DLL)
  3783. //-----------------------------------------------------------------------------
  3784. // Purpose:
  3785. //-----------------------------------------------------------------------------
  3786. void CEconItemDefinition::GeneratePrecacheModelStrings( bool bDynamicLoad, CUtlVector<const char *> *out_pVecModelStrings ) const
  3787. {
  3788. Assert( out_pVecModelStrings );
  3789. // Add base model.
  3790. out_pVecModelStrings->AddToTail( GetBasePlayerDisplayModel() );
  3791. // Add styles.
  3792. if ( GetNumStyles() )
  3793. {
  3794. for ( style_index_t i=0; i<GetNumStyles(); ++i )
  3795. {
  3796. const CEconStyleInfo *pStyle = GetStyleInfo( i );
  3797. Assert( pStyle );
  3798. pStyle->GeneratePrecacheModelStringsForStyle( out_pVecModelStrings );
  3799. }
  3800. }
  3801. // Precache all the attached models
  3802. for ( int team = 0; team < TEAM_VISUAL_SECTIONS; team++ )
  3803. {
  3804. perteamvisuals_t *pPerTeamVisuals = GetPerTeamVisual( team );
  3805. if ( !pPerTeamVisuals )
  3806. continue;
  3807. for ( int model = 0; model < pPerTeamVisuals->m_AttachedModels.Count(); model++ )
  3808. {
  3809. out_pVecModelStrings->AddToTail( pPerTeamVisuals->m_AttachedModels[model].m_pszModelName );
  3810. }
  3811. // Festive
  3812. for ( int model = 0; model < pPerTeamVisuals->m_AttachedModelsFestive.Count(); model++ )
  3813. {
  3814. out_pVecModelStrings->AddToTail( pPerTeamVisuals->m_AttachedModelsFestive[model].m_pszModelName );
  3815. }
  3816. }
  3817. if ( GetExtraWearableModel() )
  3818. {
  3819. out_pVecModelStrings->AddToTail( GetExtraWearableModel() );
  3820. }
  3821. if ( GetExtraWearableViewModel() )
  3822. {
  3823. out_pVecModelStrings->AddToTail( GetExtraWearableViewModel() );
  3824. }
  3825. if ( GetVisionFilteredDisplayModel() )
  3826. {
  3827. out_pVecModelStrings->AddToTail( GetVisionFilteredDisplayModel() );
  3828. }
  3829. // We don't need to cache the inventory model, because it's never loaded by the game
  3830. }
  3831. void CEconItemDefinition::GeneratePrecacheSoundStrings( bool bDynamicLoad, CUtlVector<const char *> *out_pVecSoundStrings ) const
  3832. {
  3833. Assert( out_pVecSoundStrings );
  3834. for ( int iTeam = 0; iTeam < TEAM_VISUAL_SECTIONS; ++iTeam )
  3835. {
  3836. for ( int iSound = 0; iSound < MAX_VISUALS_CUSTOM_SOUNDS; ++iSound )
  3837. {
  3838. const char *pSoundName = GetCustomSound( iTeam, iSound );
  3839. if ( pSoundName && pSoundName[ 0 ] != '\0' )
  3840. {
  3841. out_pVecSoundStrings->AddToTail( pSoundName );
  3842. }
  3843. }
  3844. }
  3845. }
  3846. #endif // #if defined(CLIENT_DLL) || defined(GAME_DLL)
  3847. //-----------------------------------------------------------------------------
  3848. const char *CEconItemDefinition::GetDefinitionString( const char *pszKeyName, const char *pszDefaultValue ) const
  3849. {
  3850. // !FIXME! Here we could do a dynamic lookup to apply the prefab overlay logic.
  3851. // This could save a lot of duplicated data
  3852. if ( m_pKVItem )
  3853. return m_pKVItem->GetString( pszKeyName, pszDefaultValue );
  3854. return pszDefaultValue;
  3855. }
  3856. //-----------------------------------------------------------------------------
  3857. KeyValues *CEconItemDefinition::GetDefinitionKey( const char *pszKeyName ) const
  3858. {
  3859. // !FIXME! Here we could do a dynamic lookup to apply the prefab overlay logic.
  3860. // This could save a lot of duplicated data
  3861. if ( m_pKVItem )
  3862. return m_pKVItem->FindKey( pszKeyName );
  3863. return NULL;
  3864. }
  3865. //-----------------------------------------------------------------------------
  3866. // Purpose: Parse the styles sub-section of the visuals block.
  3867. //-----------------------------------------------------------------------------
  3868. void CEconItemDefinition::BInitStylesBlockFromKV( KeyValues *pKVStyles, perteamvisuals_t *pVisData, CUtlVector<CUtlString> *pVecErrors )
  3869. {
  3870. FOR_EACH_SUBKEY( pKVStyles, pKVStyle )
  3871. {
  3872. CEconStyleInfo *pStyleInfo = GetItemSchema()->CreateEconStyleInfo();
  3873. Assert( pStyleInfo );
  3874. pStyleInfo->BInitFromKV( pKVStyle, pVecErrors );
  3875. pVisData->m_Styles.AddToTail( pStyleInfo );
  3876. }
  3877. }
  3878. //-----------------------------------------------------------------------------
  3879. // Purpose: Parse one style from the styles block.
  3880. //-----------------------------------------------------------------------------
  3881. void CEconStyleInfo::BInitFromKV( KeyValues *pKVStyle, CUtlVector<CUtlString> *pVecErrors )
  3882. {
  3883. enum { kInvalidSkinKey = -1, };
  3884. Assert( pKVStyle );
  3885. // A "skin" entry means "use this index for all of our teams, no matter how many we have".
  3886. int iCommonSkin = pKVStyle->GetInt( "skin", kInvalidSkinKey );
  3887. if ( iCommonSkin != kInvalidSkinKey )
  3888. {
  3889. for ( int i = 0; i < TEAM_VISUAL_SECTIONS; i++ )
  3890. {
  3891. m_iSkins[i] = iCommonSkin;
  3892. }
  3893. }
  3894. int iCommonViewmodelSkin = pKVStyle->GetInt( "v_skin", kInvalidSkinKey );
  3895. if ( iCommonViewmodelSkin != kInvalidSkinKey )
  3896. {
  3897. for ( int i=0; i<TEAM_VISUAL_SECTIONS; i++ )
  3898. {
  3899. m_iViewmodelSkins[i] = iCommonViewmodelSkin;
  3900. }
  3901. }
  3902. // If we don't have a base entry, we look for a unique entry for each team. This will be
  3903. // handled in a subclass if necessary.
  3904. // Are we hiding additional bodygroups when this style is active?
  3905. KeyValues *pKVHideBodygroups = pKVStyle->FindKey( "additional_hidden_bodygroups" );
  3906. if ( pKVHideBodygroups )
  3907. {
  3908. FOR_EACH_SUBKEY( pKVHideBodygroups, pKVBodygroup )
  3909. {
  3910. m_vecAdditionalHideBodygroups.AddToTail( pKVBodygroup->GetName() );
  3911. }
  3912. }
  3913. // Remaining common properties.
  3914. m_pszName = pKVStyle->GetString( "name", "#TF_UnknownStyle" );
  3915. m_pszBasePlayerModel = pKVStyle->GetString( "model_player", NULL );
  3916. m_bIsSelectable = pKVStyle->GetBool( "selectable", true );
  3917. m_pszInventoryImage = pKVStyle->GetString( "image_inventory", NULL );
  3918. KeyValues *pKVBodygroup = pKVStyle->FindKey( "bodygroup" );
  3919. if ( pKVBodygroup )
  3920. {
  3921. m_pszBodygroupName = pKVBodygroup->GetString( "name", NULL );
  3922. Assert( m_pszBodygroupName );
  3923. m_iBodygroupSubmodelIndex = pKVBodygroup->GetInt( "submodel_index", -1 );
  3924. Assert( m_iBodygroupSubmodelIndex != -1 );
  3925. }
  3926. }
  3927. #if defined(CLIENT_DLL) || defined(GAME_DLL)
  3928. //-----------------------------------------------------------------------------
  3929. // Purpose:
  3930. //-----------------------------------------------------------------------------
  3931. void CEconStyleInfo::GeneratePrecacheModelStringsForStyle( CUtlVector<const char *> *out_pVecModelStrings ) const
  3932. {
  3933. Assert( out_pVecModelStrings );
  3934. if ( GetBasePlayerDisplayModel() != NULL )
  3935. {
  3936. out_pVecModelStrings->AddToTail( GetBasePlayerDisplayModel() );
  3937. }
  3938. }
  3939. #endif
  3940. //-----------------------------------------------------------------------------
  3941. // Purpose: Item definition initialization helpers.
  3942. //-----------------------------------------------------------------------------
  3943. static void RecursiveInheritKeyValues( KeyValues *out_pValues, KeyValues *pInstance )
  3944. {
  3945. KeyValues *pPrevSubKey = NULL;
  3946. for ( KeyValues * pSubKey = pInstance->GetFirstSubKey(); pSubKey != NULL; pPrevSubKey = pSubKey, pSubKey = pSubKey->GetNextKey() )
  3947. {
  3948. // If this assert triggers, you have an item that uses a prefab but has multiple keys with the same name
  3949. AssertMsg2 ( !pPrevSubKey || pPrevSubKey->GetNameSymbol() != pSubKey->GetNameSymbol(),
  3950. "Item definition \"%s\" has multiple attributes of the same name (%s) can't use prefabs", pInstance->GetName(), pSubKey->GetName() );
  3951. KeyValues::types_t eType = pSubKey->GetDataType();
  3952. switch ( eType )
  3953. {
  3954. case KeyValues::TYPE_STRING: out_pValues->SetString( pSubKey->GetName(), pSubKey->GetString() ); break;
  3955. case KeyValues::TYPE_INT: out_pValues->SetInt( pSubKey->GetName(), pSubKey->GetInt() ); break;
  3956. case KeyValues::TYPE_FLOAT: out_pValues->SetFloat( pSubKey->GetName(), pSubKey->GetFloat() ); break;
  3957. case KeyValues::TYPE_WSTRING: out_pValues->SetWString( pSubKey->GetName(), pSubKey->GetWString() ); break;
  3958. case KeyValues::TYPE_COLOR: out_pValues->SetColor( pSubKey->GetName(), pSubKey->GetColor() ) ; break;
  3959. case KeyValues::TYPE_UINT64: out_pValues->SetUint64( pSubKey->GetName(), pSubKey->GetUint64() ) ; break;
  3960. // "NONE" means "KeyValues"
  3961. case KeyValues::TYPE_NONE:
  3962. {
  3963. // We may already have this part of the tree to stuff data into/overwrite, or we
  3964. // may have to make a new block.
  3965. KeyValues *pNewChild = out_pValues->FindKey( pSubKey->GetName() );
  3966. if ( !pNewChild )
  3967. {
  3968. pNewChild = out_pValues->CreateNewKey();
  3969. pNewChild->SetName( pSubKey->GetName() );
  3970. }
  3971. RecursiveInheritKeyValues( pNewChild, pSubKey );
  3972. break;
  3973. }
  3974. case KeyValues::TYPE_PTR:
  3975. default:
  3976. Assert( !"Unhandled data type for KeyValues inheritance!" );
  3977. break;
  3978. }
  3979. }
  3980. }
  3981. void MergeDefinitionPrefab( KeyValues *pKVWriteItem, KeyValues *pKVSourceItem )
  3982. {
  3983. Assert( pKVWriteItem );
  3984. Assert( pKVSourceItem );
  3985. const char *svPrefabName = pKVSourceItem->GetString( "prefab", NULL );
  3986. if ( svPrefabName )
  3987. {
  3988. CUtlStringList vecPrefabs;
  3989. Q_SplitString( svPrefabName, " ", vecPrefabs );
  3990. // Iterate backwards so adjectives get applied over the noun prefab
  3991. // e.g. wet scared cat would apply cat first, then scared and wet.
  3992. FOR_EACH_VEC_BACK( vecPrefabs, i )
  3993. {
  3994. KeyValues *pKVPrefab = GetItemSchema()->FindDefinitionPrefabByName( vecPrefabs[i] );
  3995. AssertMsg1( pKVPrefab, "Unable to find prefab \"%s\".", vecPrefabs[i] );
  3996. if ( pKVPrefab )
  3997. {
  3998. MergeDefinitionPrefab( pKVWriteItem, pKVPrefab );
  3999. }
  4000. }
  4001. }
  4002. RecursiveInheritKeyValues( pKVWriteItem, pKVSourceItem );
  4003. }
  4004. KeyValues *CEconItemSchema::FindDefinitionPrefabByName( const char *pszPrefabName ) const
  4005. {
  4006. int iIndex = m_mapDefinitionPrefabs.Find( pszPrefabName );
  4007. if ( m_mapDefinitionPrefabs.IsValidIndex( iIndex ) )
  4008. return m_mapDefinitionPrefabs[iIndex];
  4009. return NULL;
  4010. }
  4011. //-----------------------------------------------------------------------------
  4012. // Purpose:
  4013. //-----------------------------------------------------------------------------
  4014. const char *CEconItemSchema::FindStringTableEntry( const char *pszTableName, int iIndex ) const
  4015. {
  4016. SchemaStringTableDict_t::IndexType_t i = m_dictStringTable.Find( pszTableName );
  4017. if ( !m_dictStringTable.IsValidIndex( i ) )
  4018. return NULL;
  4019. const CUtlVector< schema_string_table_entry_t >& vec = *m_dictStringTable[i];
  4020. FOR_EACH_VEC( vec, j )
  4021. {
  4022. if ( vec[j].m_iIndex == iIndex )
  4023. return vec[j].m_pszStr;
  4024. }
  4025. return NULL;
  4026. }
  4027. //-----------------------------------------------------------------------------
  4028. // Purpose: Initialize the item definition
  4029. // Input: pKVItem - The KeyValues representation of the item
  4030. // schema - The overall item schema for this item
  4031. // pVecErrors - An optional vector that will contain error messages if
  4032. // the init fails.
  4033. // Output: True if initialization succeeded, false otherwise
  4034. //-----------------------------------------------------------------------------
  4035. #ifdef GC_DLL
  4036. GCConVar gc_steam_payment_rules_kv_key( "gc_steam_payment_rules_kv_key", "payment_rules" );
  4037. #endif // GC_DLL
  4038. #if defined( WITH_STREAMABLE_WEAPONS )
  4039. #if defined( CLIENT_DLL )
  4040. 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).");
  4041. #elif defined( GAME_DLL )
  4042. // 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
  4043. // we just have it load on demand.
  4044. 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).");
  4045. #elif defined( GC_DLL )
  4046. 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).");
  4047. #else
  4048. #error "Need to add support for streamable weapons to this configuration, or disable streamable weapons here."
  4049. #endif
  4050. #endif // WITH_STREAMABLE_WEAPONS
  4051. bool CEconItemDefinition::BInitFromKV( KeyValues *pKVItem, CUtlVector<CUtlString> *pVecErrors /* = NULL */ )
  4052. {
  4053. // Set standard members
  4054. m_pKVItem = new KeyValues( pKVItem->GetName() );
  4055. MergeDefinitionPrefab( m_pKVItem, pKVItem );
  4056. m_bEnabled = m_pKVItem->GetBool( "enabled" );
  4057. // initializing this one first so that it will be available for all the errors below
  4058. m_pszDefinitionName = m_pKVItem->GetString( "name", NULL );
  4059. #if defined( WITH_STREAMABLE_WEAPONS )
  4060. bool bGotDefault = false;
  4061. m_bLoadOnDemand = m_pKVItem->GetBool( "loadondemand", tf_loadondemand_default.GetBool(), &bGotDefault );
  4062. // This logging is useful for tracking down bugs that crop up because we've (possibly) swapped the default value for loadondemand.
  4063. // 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).
  4064. if (bGotDefault)
  4065. {
  4066. DevMsg(10, "Item %s received default value for loadondemand\n", m_pszDefinitionName);
  4067. }
  4068. #else
  4069. // Keep the old behavior, which is that loadondemand is defaulted to false.
  4070. m_bLoadOnDemand = m_pKVItem->GetBool("loadondemand");
  4071. #endif
  4072. m_nDefIndex = Q_atoi( m_pKVItem->GetName() );
  4073. m_unMinItemLevel = (uint32)m_pKVItem->GetInt( "min_ilevel", GetItemSchema()->GetMinLevel() );
  4074. m_unMaxItemLevel = (uint32)m_pKVItem->GetInt( "max_ilevel", GetItemSchema()->GetMaxLevel() );
  4075. m_nDefaultDropQuantity = m_pKVItem->GetInt( "default_drop_quantity", 1 );
  4076. m_nPopularitySeed = m_pKVItem->GetInt( "popularity_seed", 0 );
  4077. #if defined(CLIENT_DLL) || defined(GAME_DLL)
  4078. // We read this manually here in the game dlls. The GC reads it below while checking the global schema.
  4079. GetItemSchema()->BGetItemQualityFromName( m_pKVItem->GetString( "item_quality" ), &m_nItemQuality );
  4080. GetItemSchema()->BGetItemQualityFromName( m_pKVItem->GetString( "forced_item_quality" ), &m_nForcedItemQuality );
  4081. #endif
  4082. // Check for required fields
  4083. SCHEMA_INIT_CHECK(
  4084. NULL != m_pKVItem->FindKey( "name" ),
  4085. "Item definition %s: Missing required field \"name\"", m_pKVItem->GetName() );
  4086. SCHEMA_INIT_CHECK(
  4087. NULL != m_pKVItem->FindKey( "item_class" ),
  4088. "Item definition %s: Missing required field \"item_class\"", m_pKVItem->GetName() );
  4089. // Check value ranges
  4090. SCHEMA_INIT_CHECK(
  4091. m_pKVItem->GetInt( "min_ilevel" ) >= 0,
  4092. "Item definition %s: \"min_ilevel\" must be greater than or equal to 0", GetDefinitionName() );
  4093. SCHEMA_INIT_CHECK(
  4094. m_pKVItem->GetInt( "max_ilevel" ) >= 0,
  4095. "Item definition %s: \"max_ilevel\" must be greater than or equal to 0", GetDefinitionName() );
  4096. // Check for consistency
  4097. #ifdef GC_DLL
  4098. // We don't do these consistency checks in the game, because it doesn't have the data to do them
  4099. SCHEMA_INIT_CHECK(
  4100. m_unMinItemLevel >= GetItemSchema()->GetMinLevel(),
  4101. "Item definition %s: min_ilevel (%d) must be greater or equal to Minimum Item Level (%d)", GetDefinitionName(), m_unMinItemLevel, GetItemSchema()->GetMinLevel() );
  4102. SCHEMA_INIT_CHECK(
  4103. m_unMinItemLevel <= m_unMaxItemLevel,
  4104. "Item definition %s: min_ilevel (%d) must be greater or equal to min_ilevel (%d)", GetDefinitionName(), m_unMaxItemLevel, m_unMinItemLevel );
  4105. SCHEMA_INIT_CHECK(
  4106. m_unMaxItemLevel <= GetItemSchema()->GetMaxLevel(),
  4107. "Item definition %s: max_ilevel (%d) must be less than or equal to Maximum Item Level (%d)", GetDefinitionName(), m_unMaxItemLevel, GetItemSchema()->GetMaxLevel() );
  4108. // Read the item quality
  4109. if ( m_pKVItem->FindKey( "item_quality" ) )
  4110. {
  4111. SCHEMA_INIT_CHECK(
  4112. GetItemSchema()->BGetItemQualityFromName( m_pKVItem->GetString( "item_quality" ), &m_nItemQuality ),
  4113. "Item definition %s: Undefined item_quality \"%s\"", GetDefinitionName(), m_pKVItem->GetString( "item_quality" ) );
  4114. }
  4115. if ( m_pKVItem->FindKey( "forced_item_quality" ) )
  4116. {
  4117. SCHEMA_INIT_CHECK(
  4118. GetItemSchema()->BGetItemQualityFromName( m_pKVItem->GetString( "forced_item_quality" ), &m_nForcedItemQuality ),
  4119. "Item definition %s: Undefined item_quality \"%s\"", GetDefinitionName(), m_pKVItem->GetString( "forced_item_quality" ) );
  4120. }
  4121. #endif
  4122. // Rarity
  4123. // Get Index from this string and save the index
  4124. if ( m_pKVItem->FindKey( "item_rarity" ) )
  4125. {
  4126. SCHEMA_INIT_CHECK(
  4127. GetItemSchema()->BGetItemRarityFromName( m_pKVItem->GetString( "item_rarity" ), &m_nItemRarity ),
  4128. "Item definition %s: Undefined item_rarity \"%s\"", GetDefinitionName(), m_pKVItem->GetString( "item_rarity" ) );
  4129. }
  4130. if ( m_pKVItem->FindKey( "item_series" ) )
  4131. {
  4132. // Make sure this is a valid series
  4133. SCHEMA_INIT_CHECK(
  4134. GetItemSchema()->BGetItemSeries( m_pKVItem->GetString( "item_series" ), &m_unItemSeries ),
  4135. "Item definition %s: Undefined item_series \"%s\"", GetDefinitionName(), m_pKVItem->GetString( "item_series" ) );
  4136. }
  4137. // Get the item class
  4138. m_pszItemClassname = m_pKVItem->GetString( "item_class", NULL );
  4139. m_pszClassToken = m_pKVItem->GetString( "class_token_id", NULL );
  4140. m_pszSlotToken = m_pKVItem->GetString( "slot_token_id", NULL );
  4141. // expiration data
  4142. const char *pchExpiration = m_pKVItem->GetString( "expiration_date", NULL );
  4143. if( pchExpiration && pchExpiration[0] )
  4144. {
  4145. if ( pchExpiration[0] == '!' )
  4146. {
  4147. m_rtExpiration = GetItemSchema()->GetCustomExpirationDate( &pchExpiration[1] );
  4148. SCHEMA_INIT_CHECK(
  4149. m_rtExpiration != k_RTime32Nil,
  4150. "Unknown/malformed expiration_date string \"%s\" in item %s.", pchExpiration, m_pszDefinitionName );
  4151. }
  4152. else
  4153. {
  4154. m_rtExpiration = CRTime::RTime32FromFmtString( "YYYY-MM-DD hh:mm:ss" , pchExpiration );
  4155. #ifdef GC_DLL
  4156. // Check that if we convert back to a string, we get the same value
  4157. char rtimeBuf[k_RTimeRenderBufferSize];
  4158. SCHEMA_INIT_CHECK(
  4159. Q_strcmp( CRTime::RTime32ToString( m_rtExpiration, rtimeBuf ), pchExpiration ) == 0,
  4160. "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 );
  4161. #else
  4162. // 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
  4163. char rtimeBuf[k_RTimeRenderBufferSize];
  4164. if ( Q_strcmp( CRTime::RTime32ToString( m_rtExpiration, rtimeBuf ), pchExpiration ) != 0 )
  4165. {
  4166. #if ( defined( _MSC_VER ) && _MSC_VER >= 1900 )
  4167. #define timezone _timezone
  4168. #define daylight _daylight
  4169. #endif
  4170. Assert( false );
  4171. 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 );
  4172. }
  4173. #endif
  4174. }
  4175. }
  4176. // Display data
  4177. m_pszItemBaseName = m_pKVItem->GetString( "item_name", "" ); // non-NULL to ensure we can sort
  4178. m_pszItemTypeName = m_pKVItem->GetString( "item_type_name", "" ); // non-NULL to ensure we can sort
  4179. m_pszItemDesc = m_pKVItem->GetString( "item_description", NULL );
  4180. m_pszArmoryDesc = m_pKVItem->GetString( "armory_desc", NULL );
  4181. m_pszInventoryModel = m_pKVItem->GetString( "model_inventory", NULL );
  4182. m_pszInventoryImage = m_pKVItem->GetString( "image_inventory", NULL );
  4183. const char* pOverlay = m_pKVItem->GetString( "image_inventory_overlay", NULL );
  4184. if ( pOverlay )
  4185. {
  4186. m_pszInventoryOverlayImages.AddToTail( pOverlay );
  4187. }
  4188. pOverlay = m_pKVItem->GetString( "image_inventory_overlay2", NULL );
  4189. if ( pOverlay )
  4190. {
  4191. m_pszInventoryOverlayImages.AddToTail( pOverlay );
  4192. }
  4193. m_iInventoryImagePosition[0] = atoi( m_pKVItem->GetString( "image_inventory_pos_x", "0" ) );
  4194. m_iInventoryImagePosition[1] = atoi( m_pKVItem->GetString( "image_inventory_pos_y", "0" ) );
  4195. m_iInventoryImageSize[0] = atoi( m_pKVItem->GetString( "image_inventory_size_w", "128" ) );
  4196. m_iInventoryImageSize[1] = atoi( m_pKVItem->GetString( "image_inventory_size_h", "82" ) );
  4197. m_iInspectPanelDistance = m_pKVItem->GetInt( "inspect_panel_dist", 70 );
  4198. m_pszHolidayRestriction = m_pKVItem->GetString( "holiday_restriction", NULL );
  4199. m_nVisionFilterFlags = m_pKVItem->GetInt( "vision_filter_flags", 0 );
  4200. m_iSubType = atoi( m_pKVItem->GetString( "subtype", "0" ) );
  4201. m_pszBaseDisplayModel = m_pKVItem->GetString( "model_player", NULL );
  4202. m_iDefaultSkin = m_pKVItem->GetInt( "default_skin", -1 );
  4203. 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.
  4204. m_pszWorldExtraWearableModel = m_pKVItem->GetString( "extra_wearable", NULL );
  4205. m_pszWorldExtraWearableViewModel = m_pKVItem->GetString( "extra_wearable_vm", NULL );
  4206. m_pszVisionFilteredDisplayModel = pKVItem->GetString( "model_vision_filtered", NULL );
  4207. m_pszBrassModelOverride = m_pKVItem->GetString( "brass_eject_model", NULL );
  4208. m_bHideBodyGroupsDeployedOnly = m_pKVItem->GetBool( "hide_bodygroups_deployed_only" );
  4209. m_bAttachToHands = m_pKVItem->GetInt( "attach_to_hands", 0 ) != 0;
  4210. m_bAttachToHandsVMOnly = m_pKVItem->GetInt( "attach_to_hands_vm_only", 0 ) != 0;
  4211. m_bProperName = m_pKVItem->GetInt( "propername", 0 ) != 0;
  4212. m_bFlipViewModel = m_pKVItem->GetInt( "flip_viewmodel", 0 ) != 0;
  4213. m_bActAsWearable = m_pKVItem->GetInt( "act_as_wearable", 0 ) != 0;
  4214. m_bActAsWeapon = m_pKVItem->GetInt( "act_as_weapon", 0 ) != 0;
  4215. m_bIsTool = m_pKVItem->GetBool( "is_tool", 0 ) || ( GetItemClass() && !V_stricmp( GetItemClass(), "tool" ) );
  4216. m_iDropType = StringFieldToInt( m_pKVItem->GetString("drop_type"), g_szDropTypeStrings, ARRAYSIZE(g_szDropTypeStrings) );
  4217. m_pszCollectionReference = m_pKVItem->GetString( "collection_reference", NULL );
  4218. // Creation data
  4219. m_bHidden = m_pKVItem->GetInt( "hidden", 0 ) != 0;
  4220. m_bShouldShowInArmory = m_pKVItem->GetInt( "show_in_armory", 0 ) != 0;
  4221. m_bBaseItem = m_pKVItem->GetInt( "baseitem", 0 ) != 0;
  4222. m_pszItemLogClassname = m_pKVItem->GetString( "item_logname", NULL );
  4223. m_pszItemIconClassname = m_pKVItem->GetString( "item_iconname", NULL );
  4224. m_pszDatabaseAuditTable = m_pKVItem->GetString( "database_audit_table", NULL );
  4225. m_bImported = m_pKVItem->FindKey( "import_from" ) != NULL;
  4226. // Tool data
  4227. m_pTool = NULL;
  4228. KeyValues *pToolDataKV = m_pKVItem->FindKey( "tool" );
  4229. if ( pToolDataKV )
  4230. {
  4231. const char *pszType = pToolDataKV->GetString( "type", NULL );
  4232. SCHEMA_INIT_CHECK( pszType != NULL, "Tool '%s' missing required type.", m_pKVItem->GetName() );
  4233. // Common-to-all-tools settings.
  4234. const char *pszUseString = pToolDataKV->GetString( "use_string", NULL );
  4235. const char *pszUsageRestriction = pToolDataKV->GetString( "restriction", NULL );
  4236. KeyValues *pToolUsageKV = pToolDataKV->FindKey( "usage" );
  4237. // Common-to-all-tools usage capability flags.
  4238. item_capabilities_t usageCapabilities = (item_capabilities_t)ITEM_CAP_TOOL_DEFAULT;
  4239. KeyValues *pToolUsageCapsKV = pToolDataKV->FindKey( "usage_capabilities" );
  4240. if ( pToolUsageCapsKV )
  4241. {
  4242. KeyValues *pEntry = pToolUsageCapsKV->GetFirstSubKey();
  4243. while ( pEntry )
  4244. {
  4245. ParseCapability( usageCapabilities, pEntry );
  4246. pEntry = pEntry->GetNextKey();
  4247. }
  4248. }
  4249. m_pTool = GetItemSchema()->CreateEconToolImpl( pszType, pszUseString, pszUsageRestriction, usageCapabilities, pToolUsageKV );
  4250. SCHEMA_INIT_CHECK( m_pTool != NULL, "Unable to create tool implementation for '%s', of type '%s'.", m_pKVItem->GetName(), pszType );
  4251. }
  4252. // Bundle
  4253. KeyValues *pBundleDataKV = m_pKVItem->FindKey( "bundle" );
  4254. if ( pBundleDataKV )
  4255. {
  4256. m_BundleInfo = new bundleinfo_t();
  4257. FOR_EACH_SUBKEY( pBundleDataKV, pKVCurItem )
  4258. {
  4259. CEconItemDefinition *pItemDef = GetItemSchema()->GetItemDefinitionByName( pKVCurItem->GetName() );
  4260. SCHEMA_INIT_CHECK( pItemDef != NULL, "Unable to find item definition '%s' for bundle '%s'.", pKVCurItem->GetName(), m_pszDefinitionName );
  4261. m_BundleInfo->vecItemDefs.AddToTail( pItemDef );
  4262. }
  4263. // 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.
  4264. m_bIsPackBundle = m_pKVItem->GetInt( "is_pack_bundle", 0 ) != 0;
  4265. }
  4266. // capabilities
  4267. m_iCapabilities = (item_capabilities_t)ITEM_CAP_DEFAULT;
  4268. KeyValues *pCapsKV = m_pKVItem->FindKey( "capabilities" );
  4269. if ( pCapsKV )
  4270. {
  4271. KeyValues *pEntry = pCapsKV->GetFirstSubKey();
  4272. while ( pEntry )
  4273. {
  4274. ParseCapability( m_iCapabilities, pEntry );
  4275. pEntry = pEntry->GetNextKey();
  4276. }
  4277. }
  4278. // item_set
  4279. 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() );
  4280. const char *pszSetItemRemapDefIndexName = m_pKVItem->GetString( "set_item_remap", NULL );
  4281. if ( pszSetItemRemapDefIndexName )
  4282. {
  4283. const CEconItemDefinition *pRemapItemDef = GetItemSchema()->GetItemDefinitionByName( pszSetItemRemapDefIndexName );
  4284. m_unSetItemRemapDefIndex = pRemapItemDef ? pRemapItemDef->GetDefinitionIndex() : INVALID_ITEM_DEF_INDEX;
  4285. SCHEMA_INIT_CHECK( m_unSetItemRemapDefIndex != INVALID_ITEM_DEF_INDEX, "Unable to find set item remap definition '%s' for '%s'.", pszSetItemRemapDefIndexName, GetDefinitionName() );
  4286. SCHEMA_INIT_CHECK( m_unSetItemRemapDefIndex != GetDefinitionIndex(), "Unable to set set item remap for definition '%s' to itself.", GetDefinitionName() );
  4287. }
  4288. else
  4289. {
  4290. m_unSetItemRemapDefIndex = GetDefinitionIndex();
  4291. }
  4292. // cache item map names
  4293. m_pszArmoryRemap = m_pKVItem->GetString( "armory_remap", NULL );
  4294. m_pszStoreRemap = m_pKVItem->GetString( "store_remap", NULL );
  4295. m_pszXifierRemapClass = m_pKVItem->GetString( "xifier_class_remap", NULL );
  4296. m_pszBaseFunctionalItemName = m_pKVItem->GetString( "base_item_name", "" );
  4297. m_pszParticleSuffix = m_pKVItem->GetString( "particle_suffix", NULL );
  4298. m_bValidForShuffle = m_pKVItem->GetBool( "valid_for_shuffle", false );
  4299. m_bValidForSelfMade = m_pKVItem->GetBool( "valid_for_self_made", true );
  4300. // Init our visuals blocks.
  4301. BInitVisualBlockFromKV( m_pKVItem, pVecErrors );
  4302. // Calculate our equip region mask.
  4303. {
  4304. m_unEquipRegionMask = 0;
  4305. m_unEquipRegionConflictMask = 0;
  4306. // Our equip region will come from one of two places -- either we have an "equip_regions" (plural) section,
  4307. // in which case we have any number of regions specified; or we have an "equip_region" (singular) section
  4308. // which will have one and exactly one region. If we have "equip_regions" (plural), we ignore whatever is
  4309. // in "equip_region" (singular).
  4310. //
  4311. // Yes, this is sort of dumb.
  4312. CUtlVector<const char *> vecEquipRegionNames;
  4313. KeyValues *pKVMultiEquipRegions = m_pKVItem->FindKey( "equip_regions" ),
  4314. *pKVSingleEquipRegion = m_pKVItem->FindKey( "equip_region" );
  4315. // Maybe we have multiple entries?
  4316. if ( pKVMultiEquipRegions )
  4317. {
  4318. for ( KeyValues *pKVRegion = pKVMultiEquipRegions->GetFirstSubKey(); pKVRegion; pKVRegion = pKVRegion->GetNextKey() )
  4319. {
  4320. vecEquipRegionNames.AddToTail( pKVRegion->GetName() );
  4321. }
  4322. }
  4323. // This is our one-and-only-one equip region.
  4324. else if ( pKVSingleEquipRegion )
  4325. {
  4326. const char *pEquipRegionName = pKVSingleEquipRegion->GetString( (const char *)NULL, NULL );
  4327. if ( pEquipRegionName )
  4328. {
  4329. vecEquipRegionNames.AddToTail( pEquipRegionName );
  4330. }
  4331. }
  4332. // For each of our regions, add to our conflict mask both ourself and all the regions
  4333. // that we conflict with.
  4334. FOR_EACH_VEC( vecEquipRegionNames, i )
  4335. {
  4336. const char *pszEquipRegionName = vecEquipRegionNames[i];
  4337. equip_region_mask_t unThisRegionMask = GetItemSchema()->GetEquipRegionMaskByName( pszEquipRegionName );
  4338. SCHEMA_INIT_CHECK(
  4339. unThisRegionMask != 0,
  4340. "Item definition %s: Unable to find equip region mask for region named \"%s\"", GetDefinitionName(), vecEquipRegionNames[i] );
  4341. m_unEquipRegionMask |= GetItemSchema()->GetEquipRegionBitMaskByName( pszEquipRegionName );
  4342. m_unEquipRegionConflictMask |= unThisRegionMask;
  4343. }
  4344. }
  4345. // Single-line static attribute parsing.
  4346. {
  4347. KeyValues *pKVStaticAttrsKey = m_pKVItem->FindKey( "static_attrs" );
  4348. if ( pKVStaticAttrsKey )
  4349. {
  4350. FOR_EACH_SUBKEY( pKVStaticAttrsKey, pKVKey )
  4351. {
  4352. static_attrib_t staticAttrib;
  4353. SCHEMA_INIT_SUBSTEP( staticAttrib.BInitFromKV_SingleLine( GetDefinitionName(), pKVKey, pVecErrors, false ) );
  4354. m_vecStaticAttributes.AddToTail( staticAttrib );
  4355. // Does this attribute specify a tag to apply to this item definition?
  4356. Assert( staticAttrib.GetAttributeDefinition() );
  4357. }
  4358. }
  4359. }
  4360. // Old style attribute parsing. Really only useful now for GC-generated attributes.
  4361. KeyValues *pKVAttribKey = m_pKVItem->FindKey( "attributes" );
  4362. if ( pKVAttribKey )
  4363. {
  4364. FOR_EACH_SUBKEY( pKVAttribKey, pKVKey )
  4365. {
  4366. static_attrib_t staticAttrib;
  4367. SCHEMA_INIT_SUBSTEP( staticAttrib.BInitFromKV_MultiLine( GetDefinitionName(), pKVKey, pVecErrors ) );
  4368. m_vecStaticAttributes.AddToTail( staticAttrib );
  4369. // Does this attribute specify a tag to apply to this item definition?
  4370. Assert( staticAttrib.GetAttributeDefinition() );
  4371. }
  4372. }
  4373. // Initialize tags based on all static attributes for this item.
  4374. for ( const static_attrib_t& attr : m_vecStaticAttributes )
  4375. {
  4376. const econ_tag_handle_t tag = attr.GetAttributeDefinition()->GetItemDefinitionTag();
  4377. if ( tag != INVALID_ECON_TAG_HANDLE )
  4378. {
  4379. m_vecTags.AddToTail( tag );
  4380. }
  4381. }
  4382. // Auto-generate tags based on capabilities.
  4383. for ( int i = 0; i < NUM_ITEM_CAPS; i++ )
  4384. {
  4385. if ( m_iCapabilities & (1 << i) )
  4386. {
  4387. m_vecTags.AddToTail( GetItemSchema()->GetHandleForTag( CFmtStr( "auto__cap_%s", g_Capabilities[i] ).Get() ) );
  4388. }
  4389. }
  4390. // Initialize used-specified tags for this item if present.
  4391. KeyValues *pKVTags = m_pKVItem->FindKey( "tags" );
  4392. if ( pKVTags )
  4393. {
  4394. FOR_EACH_SUBKEY( pKVTags, pKVTag )
  4395. {
  4396. m_vecTags.AddToTail( GetItemSchema()->GetHandleForTag( pKVTag->GetName() ) );
  4397. }
  4398. }
  4399. #ifdef GC_DLL
  4400. SCHEMA_INIT_SUBSTEP( BCommonInitPropertyGeneratorsFromKV( GetDefinitionName(), &m_vecPropertyGenerators, m_pKVItem->FindKey( "property_generators" ), pVecErrors ) );
  4401. // Parse payment rules on the GC if any exist.
  4402. KeyValues *pKVPaymentRules = m_pKVItem->FindKey( gc_steam_payment_rules_kv_key.GetString() );
  4403. if ( pKVPaymentRules )
  4404. {
  4405. FOR_EACH_TRUE_SUBKEY( pKVPaymentRules, pKVRule )
  4406. {
  4407. econ_item_payment_rule_t rule;
  4408. const bool bFoundPaymentRule = BGetPaymentRule( pKVRule, &rule.m_eRuleType, &rule.m_RevenueShare );
  4409. SCHEMA_INIT_CHECK( bFoundPaymentRule, "Item definition '%s': payment rule %s didn't specify a known payment rule type", GetDefinitionName(), pKVRule->GetName() );
  4410. // Allow us to override some of our checks if we want to claim we really know what we're doing.
  4411. if ( !pKVRule->FindKey( "sanity_check_override" ) )
  4412. {
  4413. 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 );
  4414. // Ordinarily, bundles can only specify the "bundle_revenue_share" payment rule type. However, for backwards compatability
  4415. // with previous payment rules that pre-date the Workshop and had manual percentages set offline, we allow people who know
  4416. // what they're doing to specify the "sanity_check_override" key and then use custom rules.
  4417. SCHEMA_INIT_CHECK( (rule.m_eRuleType == kPaymentRule_Bundle) == IsBundle(), "Item definition '%s': payment rule %s has invalid bundle rules", GetDefinitionName(), pKVRule->GetName() );
  4418. }
  4419. KeyValues *pPaymentRuleForItemdef = pKVRule->FindKey( "payment_rule_for_itemdef" );
  4420. SCHEMA_INIT_CHECK( pPaymentRuleForItemdef && ( pPaymentRuleForItemdef->GetInt() == m_nDefIndex ), "Item definition '%s': payment rule %s has invalid payment_rule_for_itemdef", GetDefinitionName(), pKVRule->GetName() );
  4421. KeyValues *pKVMultiTargets = pKVRule->FindKey( "targets" );
  4422. KeyValues *pKVSingleTarget = pKVRule->FindKey( "target" );
  4423. SCHEMA_INIT_CHECK( pKVMultiTargets == NULL || pKVSingleTarget == NULL, "Item definition '%s': payment rule %s specifies both single- and multi-targets", GetDefinitionName(), pKVRule->GetName() );
  4424. if ( pKVMultiTargets )
  4425. {
  4426. FOR_EACH_SUBKEY( pKVMultiTargets, pKVTarget )
  4427. {
  4428. rule.m_vecValues.AddToTail( (uint64)Q_atoi64( pKVTarget->GetName() ) );
  4429. }
  4430. }
  4431. if ( pKVSingleTarget )
  4432. {
  4433. rule.m_vecValues.AddToTail( pKVSingleTarget->GetUint64() );
  4434. }
  4435. // We expect bundles to have no associated account data at all -- their payment processing
  4436. // is done by splitting the total value of the bundle between each of the items in it. All
  4437. // non-bundle payment rules require at least one data entry.
  4438. 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() );
  4439. // It doesn't make any sense to have multiple entries for a bundle. We expect their to be one
  4440. // rule, and that's "process this like a bundle".
  4441. if ( rule.m_eRuleType == kPaymentRule_Bundle )
  4442. {
  4443. SCHEMA_INIT_CHECK( m_vecPaymentRules.Count() == 0, "Item definition '%s': only the first payment rule can be specified as 'bundle'", GetDefinitionName() );
  4444. // We only allow bundle payment rules to be applied to actual bundles with contents
  4445. // specified. Without doing this, we would have nowhere to pull the metadata about
  4446. // which items are contained.
  4447. 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() );
  4448. // Bundles rely on sub-items for figuring out payment so the revenue share for the
  4449. // bundle itself is expected to be 100%.
  4450. 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 );
  4451. }
  4452. const bool bWasNumberedCorrectly = (AddPaymentRule( rule ) == atoi( pKVRule->GetName() ));
  4453. SCHEMA_INIT_CHECK( bWasNumberedCorrectly, "Item definition '%s': misnumbered payment rule %s", GetDefinitionName(), pKVRule->GetName() );
  4454. }
  4455. }
  4456. #endif // GC_DLL
  4457. return SCHEMA_INIT_SUCCESS();
  4458. }
  4459. #ifdef GC_DLL
  4460. int CEconItemDefinition::AddPaymentRule( const econ_item_payment_rule_t& newRule )
  4461. {
  4462. return m_vecPaymentRules.AddToTail( newRule );
  4463. }
  4464. #endif // GC_DLL
  4465. bool static_attrib_t::BInitFromKV_MultiLine( const char *pszContext, KeyValues *pKVAttribute, CUtlVector<CUtlString> *pVecErrors )
  4466. {
  4467. const CEconItemAttributeDefinition *pAttrDef = GetItemSchema()->GetAttributeDefinitionByName( pKVAttribute->GetName() );
  4468. SCHEMA_INIT_CHECK(
  4469. NULL != pAttrDef,
  4470. "Context '%s': Attribute \"%s\" in \"attributes\" did not match any attribute definitions", pszContext, pKVAttribute->GetName() );
  4471. if ( pAttrDef )
  4472. {
  4473. iDefIndex = pAttrDef->GetDefinitionIndex();
  4474. const ISchemaAttributeType *pAttrType = pAttrDef->GetAttributeType();
  4475. Assert( pAttrType );
  4476. pAttrType->InitializeNewEconAttributeValue( &m_value );
  4477. const char *pszValue = pKVAttribute->GetString( "value", NULL );
  4478. const bool bSuccessfullyLoadedValue = pAttrType->BConvertStringToEconAttributeValue( pAttrDef, pszValue, &m_value, true );
  4479. SCHEMA_INIT_CHECK(
  4480. bSuccessfullyLoadedValue,
  4481. "Context '%s': Attribute \"%s\" could not parse value \"%s\"!", pszContext, pKVAttribute->GetName(), pszValue ? pszValue : "(null)" );
  4482. SCHEMA_INIT_CHECK(
  4483. !pAttrDef->BIsSetBonusAttribute(),
  4484. "Context '%s': Attribute \"%s\" is a set bonus attribute and not supported here", pszContext, pKVAttribute->GetName() );
  4485. #ifdef GC_DLL
  4486. bForceGCToGenerate = pKVAttribute->GetBool( "force_gc_to_generate" );
  4487. KeyValues *pKVLogicData = pKVAttribute->FindKey( "custom_value_logic" );
  4488. if ( pKVLogicData )
  4489. {
  4490. m_pKVCustomData = pKVLogicData->MakeCopy();
  4491. }
  4492. SCHEMA_INIT_CHECK(
  4493. m_pKVCustomData == NULL || bForceGCToGenerate,
  4494. "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() );
  4495. SCHEMA_INIT_CHECK(
  4496. m_pKVCustomData != NULL || pKVAttribute->FindKey( "value" ),
  4497. "Context '%s': Attribute \"%s\" has no value set", pszContext, pKVAttribute->GetName() );
  4498. SCHEMA_INIT_CHECK(
  4499. m_pKVCustomData == NULL || m_pKVCustomData->FindKey( "method" ) != NULL,
  4500. "Context '%s': Attribute \"%s\" custom logic data is set, but custom logic method is not set!", pszContext, pKVAttribute->GetName() );
  4501. #endif // GC_DLL
  4502. }
  4503. return SCHEMA_INIT_SUCCESS();
  4504. }
  4505. bool static_attrib_t::BInitFromKV_SingleLine( const char *pszContext, KeyValues *pKVAttribute, CUtlVector<CUtlString> *pVecErrors, bool bEnableTerribleBackwardsCompatibilitySchemaParsingCode /* = true */ )
  4506. {
  4507. const CEconItemAttributeDefinition *pAttrDef = GetItemSchema()->GetAttributeDefinitionByName( pKVAttribute->GetName() );
  4508. SCHEMA_INIT_CHECK(
  4509. NULL != pAttrDef,
  4510. "Context '%s': Attribute \"%s\" in \"attributes\" did not match any attribute definitions", pszContext, pKVAttribute->GetName() );
  4511. if ( pAttrDef )
  4512. {
  4513. iDefIndex = pAttrDef->GetDefinitionIndex();
  4514. const ISchemaAttributeType *pAttrType = pAttrDef->GetAttributeType();
  4515. Assert( pAttrType );
  4516. pAttrType->InitializeNewEconAttributeValue( &m_value );
  4517. const char *pszValue = pKVAttribute->GetString();
  4518. const bool bSuccessfullyLoadedValue = pAttrType->BConvertStringToEconAttributeValue( pAttrDef, pszValue, &m_value, bEnableTerribleBackwardsCompatibilitySchemaParsingCode );
  4519. SCHEMA_INIT_CHECK(
  4520. bSuccessfullyLoadedValue,
  4521. "Context '%s': Attribute \"%s\" could not parse value \"%s\"!", pszContext, pKVAttribute->GetName(), pszValue ? pszValue : "(null)" );
  4522. SCHEMA_INIT_CHECK(
  4523. !pAttrDef->BIsSetBonusAttribute(),
  4524. "Context '%s': Attribute \"%s\" is a set bonus attribute and not supported here", pszContext, pKVAttribute->GetName() );
  4525. #ifdef GC_DLL
  4526. bForceGCToGenerate = false;
  4527. m_pKVCustomData = NULL;
  4528. #endif // GC_DLL
  4529. }
  4530. return SCHEMA_INIT_SUCCESS();
  4531. }
  4532. bool CEconItemDefinition::BInitItemMappings( CUtlVector<CUtlString> *pVecErrors )
  4533. {
  4534. // Armory remapping
  4535. if ( m_pszArmoryRemap && m_pszArmoryRemap[0] )
  4536. {
  4537. CEconItemDefinition *pDef = GetItemSchema()->GetItemDefinitionByName( m_pszArmoryRemap );
  4538. if ( pDef )
  4539. {
  4540. m_iArmoryRemap = pDef->GetDefinitionIndex();
  4541. }
  4542. SCHEMA_INIT_CHECK(
  4543. pDef != NULL,
  4544. "Item %s: Armory remap definition \"%s\" was not found", m_pszItemBaseName, m_pszArmoryRemap );
  4545. }
  4546. // Store remapping
  4547. if ( m_pszStoreRemap && m_pszStoreRemap[0] )
  4548. {
  4549. CEconItemDefinition *pDef = GetItemSchema()->GetItemDefinitionByName( m_pszStoreRemap );
  4550. if ( pDef )
  4551. {
  4552. m_iStoreRemap = pDef->GetDefinitionIndex();
  4553. }
  4554. SCHEMA_INIT_CHECK(
  4555. pDef != NULL,
  4556. "Item %s: Store remap definition \"%s\" was not found", m_pszItemBaseName, m_pszStoreRemap );
  4557. }
  4558. return SCHEMA_INIT_SUCCESS();
  4559. }
  4560. const char* CEconItemDefinition::GetIconURL( const char* pszKey ) const
  4561. {
  4562. auto idx = m_pDictIcons->Find( pszKey );
  4563. if ( idx == m_pDictIcons->InvalidIndex() )
  4564. {
  4565. return NULL;
  4566. }
  4567. return (*m_pDictIcons)[ idx ];
  4568. }
  4569. //-----------------------------------------------------------------------------
  4570. // Purpose: Generate and return a random level according to whatever leveling
  4571. // curve this definition uses.
  4572. //-----------------------------------------------------------------------------
  4573. uint32 CEconItemDefinition::RollItemLevel( void ) const
  4574. {
  4575. return RandomInt( GetMinLevel(), GetMaxLevel() );
  4576. }
  4577. const char *CEconItemDefinition::GetFirstSaleDate() const
  4578. {
  4579. return GetDefinitionString( "first_sale_date", "1960/00/00" );
  4580. }
  4581. //-----------------------------------------------------------------------------
  4582. // Purpose:
  4583. //-----------------------------------------------------------------------------
  4584. void CEconItemDefinition::IterateAttributes( IEconItemAttributeIterator *pIterator ) const
  4585. {
  4586. FOR_EACH_VEC( GetStaticAttributes(), i )
  4587. {
  4588. const static_attrib_t& staticAttrib = GetStaticAttributes()[i];
  4589. #ifdef GC_DLL
  4590. // we skip over static attributes that the GC will turn into dynamic attributes because otherwise we'll have
  4591. // the appearance of iterating over them twice; for clients these attributes won't even make it into the
  4592. // list
  4593. if ( staticAttrib.bForceGCToGenerate )
  4594. continue;
  4595. #endif // GC_DLL
  4596. const CEconItemAttributeDefinition *pAttrDef = GetItemSchema()->GetAttributeDefinition( staticAttrib.iDefIndex );
  4597. if ( !pAttrDef )
  4598. continue;
  4599. const ISchemaAttributeType *pAttrType = pAttrDef->GetAttributeType();
  4600. Assert( pAttrType );
  4601. if ( !pAttrType->OnIterateAttributeValue( pIterator, pAttrDef, staticAttrib.m_value ) )
  4602. return;
  4603. }
  4604. }
  4605. #if defined(CLIENT_DLL) || defined(GAME_DLL)
  4606. //-----------------------------------------------------------------------------
  4607. // Purpose:
  4608. //-----------------------------------------------------------------------------
  4609. Activity CEconItemDefinition::GetActivityOverride( int iTeam, Activity baseAct ) const
  4610. {
  4611. int iAnims = GetNumAnimations( iTeam );
  4612. for ( int i = 0; i < iAnims; i++ )
  4613. {
  4614. animation_on_wearable_t *pData = GetAnimationData( iTeam, i );
  4615. if ( !pData )
  4616. continue;
  4617. if ( pData->iActivity == kActivityLookup_Unknown )
  4618. {
  4619. pData->iActivity = ActivityList_IndexForName( pData->pszActivity );
  4620. }
  4621. if ( pData->iActivity == baseAct )
  4622. {
  4623. if ( pData->iReplacement == kActivityLookup_Unknown )
  4624. {
  4625. pData->iReplacement = ActivityList_IndexForName( pData->pszReplacement );
  4626. }
  4627. if ( pData->iReplacement > 0 )
  4628. {
  4629. return (Activity) pData->iReplacement;
  4630. }
  4631. }
  4632. }
  4633. return baseAct;
  4634. }
  4635. //-----------------------------------------------------------------------------
  4636. // Purpose:
  4637. //-----------------------------------------------------------------------------
  4638. const char *CEconItemDefinition::GetActivityOverride( int iTeam, const char *pszActivity ) const
  4639. {
  4640. int iAnims = GetNumAnimations( iTeam );
  4641. for ( int i = 0; i < iAnims; i++ )
  4642. {
  4643. animation_on_wearable_t *pData = GetAnimationData( iTeam, i );
  4644. if ( Q_stricmp( pszActivity, pData->pszActivity ) == 0 )
  4645. return pData->pszReplacement;
  4646. }
  4647. return NULL;
  4648. }
  4649. //-----------------------------------------------------------------------------
  4650. // Purpose:
  4651. //-----------------------------------------------------------------------------
  4652. const char *CEconItemDefinition::GetReplacementForActivityOverride( int iTeam, Activity baseAct ) const
  4653. {
  4654. int iAnims = GetNumAnimations( iTeam );
  4655. for ( int i = 0; i < iAnims; i++ )
  4656. {
  4657. animation_on_wearable_t *pData = GetAnimationData( iTeam, i );
  4658. if ( pData->iActivity == kActivityLookup_Unknown )
  4659. {
  4660. pData->iActivity = ActivityList_IndexForName( pData->pszActivity );
  4661. }
  4662. if ( pData && pData->iActivity == baseAct )
  4663. return pData->pszReplacement;
  4664. }
  4665. return NULL;
  4666. }
  4667. //-----------------------------------------------------------------------------
  4668. // Purpose: Returns true if the content for this item view should be streamed. If false,
  4669. // it should be preloaded.
  4670. //-----------------------------------------------------------------------------
  4671. // DO NOT MERGE THIS CONSOLE VARIABLE TO REL WE SHOULD NOT SHIP THIS OH GOD
  4672. #ifdef STAGING_ONLY
  4673. ConVar item_enable_dynamic_loading( "item_enable_dynamic_loading", "1", FCVAR_REPLICATED, "Enable/disable dynamic streaming of econ content." );
  4674. #endif // STAGING_ONLY
  4675. bool CEconItemDefinition::IsContentStreamable() const
  4676. {
  4677. if ( !BLoadOnDemand() )
  4678. return false;
  4679. #ifdef STAGING_ONLY
  4680. return item_enable_dynamic_loading.GetBool();
  4681. #else
  4682. return true;
  4683. #endif
  4684. }
  4685. #endif // defined(CLIENT_DLL) || defined(GAME_DLL)
  4686. RETURN_ATTRIBUTE_STRING_F( CEconItemDefinition::GetIconDisplayModel, "icon display model", m_pszWorldDisplayModel );
  4687. //-----------------------------------------------------------------------------
  4688. // Purpose: Constructor
  4689. //-----------------------------------------------------------------------------
  4690. CTimedItemRewardDefinition::CTimedItemRewardDefinition( void )
  4691. : m_unMinFreq( 0 ),
  4692. m_unMaxFreq( UINT_MAX ),
  4693. m_flChance( 0.0f ),
  4694. m_pLootList( NULL ),
  4695. m_iRequiredItemDef(INVALID_ITEM_DEF_INDEX)
  4696. {
  4697. }
  4698. //-----------------------------------------------------------------------------
  4699. // Purpose: Copy constructor
  4700. //-----------------------------------------------------------------------------
  4701. CTimedItemRewardDefinition::CTimedItemRewardDefinition( const CTimedItemRewardDefinition &that )
  4702. {
  4703. (*this) = that;
  4704. }
  4705. //-----------------------------------------------------------------------------
  4706. // Purpose: Operator=
  4707. //-----------------------------------------------------------------------------
  4708. CTimedItemRewardDefinition &CTimedItemRewardDefinition::operator=( const CTimedItemRewardDefinition &rhs )
  4709. {
  4710. m_unMinFreq = rhs.m_unMinFreq;
  4711. m_unMaxFreq = rhs.m_unMaxFreq;
  4712. m_flChance = rhs.m_flChance;
  4713. m_criteria = rhs.m_criteria;
  4714. m_pLootList = rhs.m_pLootList;
  4715. m_iRequiredItemDef = rhs.m_iRequiredItemDef;
  4716. return *this;
  4717. }
  4718. //-----------------------------------------------------------------------------
  4719. // Purpose: Initialize the attribute definition
  4720. // Input: pKVTimedReward - The KeyValues representation of the timed reward
  4721. // schema - The overall item schema
  4722. // pVecErrors - An optional vector that will contain error messages if
  4723. // the init fails.
  4724. // Output: True if initialization succeeded, false otherwise
  4725. //-----------------------------------------------------------------------------
  4726. bool CTimedItemRewardDefinition::BInitFromKV( KeyValues *pKVTimedReward, CUtlVector<CUtlString> *pVecErrors /* = NULL */ )
  4727. {
  4728. // Parse the basic values
  4729. m_flChance = pKVTimedReward->GetFloat( "pctChance" );
  4730. m_unMinFreq = pKVTimedReward->GetInt( "value_min", 0 );
  4731. m_unMaxFreq = pKVTimedReward->GetInt( "value_max", UINT_MAX );
  4732. m_iRequiredItemDef = INVALID_ITEM_DEF_INDEX;
  4733. const char *pszRequiredItem = pKVTimedReward->GetString( "required_item", NULL );
  4734. if ( pszRequiredItem )
  4735. {
  4736. // Find the ItemDef
  4737. CEconItemDefinition *pDef = GetItemSchema()->GetItemDefinitionByName( pszRequiredItem );
  4738. SCHEMA_INIT_CHECK( pDef != NULL, "Invalid Item Def Required for a for TimedReward Definition");
  4739. m_iRequiredItemDef = pDef->GetDefinitionIndex();
  4740. }
  4741. // Check required fields
  4742. SCHEMA_INIT_CHECK(
  4743. NULL != pKVTimedReward->FindKey( "value_min" ),
  4744. "Time reward %s: Missing required field \"value_min\"", pKVTimedReward->GetName() );
  4745. SCHEMA_INIT_CHECK(
  4746. NULL != pKVTimedReward->FindKey( "value_max" ),
  4747. "Time reward %s: Missing required field \"value_max\"", pKVTimedReward->GetName() );
  4748. SCHEMA_INIT_CHECK(
  4749. NULL != pKVTimedReward->FindKey( "pctChance" ),
  4750. "Time reward %s: Missing required field \"pctChance\"", pKVTimedReward->GetName() );
  4751. SCHEMA_INIT_CHECK(
  4752. NULL == pKVTimedReward->FindKey( "criteria" ),
  4753. "Time reward %s: \"criteria\" is no longer supported. Restructure as \"loot_list\"?", pKVTimedReward->GetName() );
  4754. SCHEMA_INIT_CHECK(
  4755. NULL != pKVTimedReward->FindKey( "loot_list" ),
  4756. "Time reward %s: Missing required field \"loot_list\" ", pKVTimedReward->GetName() );
  4757. // Parse the loot list
  4758. const char *pszLootList = pKVTimedReward->GetString("loot_list", NULL);
  4759. if ( pszLootList && pszLootList[0] )
  4760. {
  4761. m_pLootList = GetItemSchema()->GetLootListByName( pszLootList );
  4762. // Make sure the item index is correct because we use this index as a reference
  4763. SCHEMA_INIT_CHECK(
  4764. NULL != m_pLootList,
  4765. "Time Reward %s: loot_list (%s) does not exist", pKVTimedReward->GetName(), pszLootList );
  4766. }
  4767. // Other integrity checks
  4768. SCHEMA_INIT_CHECK(
  4769. m_flChance >= 0.0f,
  4770. "Time Reward %s: pctChance (%f) must be greater or equal to 0.0", pKVTimedReward->GetName(), m_flChance );
  4771. SCHEMA_INIT_CHECK(
  4772. m_flChance <= 1.0f,
  4773. "Time Reward %s: pctChance (%f) must be less than or equal to 1.0", pKVTimedReward->GetName(), m_flChance );
  4774. SCHEMA_INIT_CHECK(
  4775. pKVTimedReward->GetInt( "value_min" ) > 0,
  4776. "Time Reward %s: value_min (%d) must be greater than 0", pKVTimedReward->GetName(), m_unMinFreq );
  4777. SCHEMA_INIT_CHECK(
  4778. pKVTimedReward->GetInt( "value_max" ) > 0,
  4779. "Time Reward %s: value_max (%d) must be greater than 0", pKVTimedReward->GetName(), m_unMaxFreq );
  4780. SCHEMA_INIT_CHECK(
  4781. (m_unMaxFreq >= m_unMinFreq),
  4782. "Time Reward %s: value_max (%d) must be greater than or equal to value_min (%d)", pKVTimedReward->GetName(), m_unMaxFreq, m_unMinFreq );
  4783. return SCHEMA_INIT_SUCCESS();
  4784. }
  4785. //-----------------------------------------------------------------------------
  4786. // Purpose: Adds a foreign item definition to local definition mapping for a
  4787. // foreign app
  4788. //-----------------------------------------------------------------------------
  4789. void CForeignAppImports::AddMapping( uint16 unForeignDefIndex, const CEconItemDefinition *pDefn )
  4790. {
  4791. m_mapDefinitions.InsertOrReplace( unForeignDefIndex, pDefn );
  4792. }
  4793. //-----------------------------------------------------------------------------
  4794. // Purpose: Adds a foreign item definition to local definition mapping for a
  4795. // foreign app
  4796. //-----------------------------------------------------------------------------
  4797. const CEconItemDefinition *CForeignAppImports::FindMapping( uint16 unForeignDefIndex ) const
  4798. {
  4799. int i = m_mapDefinitions.Find( unForeignDefIndex );
  4800. if( m_mapDefinitions.IsValidIndex( i ) )
  4801. return m_mapDefinitions[i];
  4802. else
  4803. return NULL;
  4804. }
  4805. //-----------------------------------------------------------------------------
  4806. // Purpose: Constructor
  4807. //-----------------------------------------------------------------------------
  4808. CEconItemSchema::CEconItemSchema( )
  4809. : m_unResetCount( 0 )
  4810. , m_pKVRawDefinition( NULL )
  4811. , m_mapItemSeries( DefLessFunc(int) )
  4812. , m_mapRarities( DefLessFunc(int) )
  4813. , m_mapQualities( DefLessFunc(int) )
  4814. , m_mapAttributes( DefLessFunc(int) )
  4815. , m_mapRecipes( DefLessFunc(int) )
  4816. , m_mapQuestObjectives( DefLessFunc(int) )
  4817. , m_mapItemsSorted( DefLessFunc(int) )
  4818. , m_mapToolsItems( DefLessFunc(int) )
  4819. , m_mapBaseItems( DefLessFunc(int) )
  4820. , m_unVersion( 0 )
  4821. #if defined(CLIENT_DLL) || defined(GAME_DLL)
  4822. , m_pDefaultItemDefinition( NULL )
  4823. #endif
  4824. , m_mapItemSets( CaselessStringLessThan )
  4825. , m_mapItemCollections( CaselessStringLessThan )
  4826. , m_mapItemPaintKits( CaselessStringLessThan )
  4827. , m_mapOperationDefinitions( CaselessStringLessThan )
  4828. , m_mapLootLists( CaselessStringLessThan )
  4829. , m_mapRevolvingLootLists( DefLessFunc(int) )
  4830. , m_mapDefinitionPrefabs( CaselessStringLessThan )
  4831. , m_mapAchievementRewardsByData( DefLessFunc( uint32 ) )
  4832. , m_mapAttributeControlledParticleSystems( DefLessFunc(int) )
  4833. , m_mapDefaultBodygroupState( CaselessStringLessThan )
  4834. #ifdef GC_DLL
  4835. , m_mapForeignImports( DefLessFunc(AppId_t) )
  4836. #elif defined(CLIENT_DLL) || defined(GAME_DLL)
  4837. , m_pDelayedSchemaData( NULL )
  4838. #endif
  4839. , m_mapKillEaterScoreTypes( DefLessFunc( unsigned int ) )
  4840. , m_mapCommunityMarketDefinitionIndexRemap( DefLessFunc( item_definition_index_t ) )
  4841. #ifdef CLIENT_DLL
  4842. , m_mapSteamPackageLocalizationTokens( DefLessFunc( uint32 ) )
  4843. #endif
  4844. {
  4845. Reset();
  4846. }
  4847. //-----------------------------------------------------------------------------
  4848. // Purpose:
  4849. //-----------------------------------------------------------------------------
  4850. IEconTool *CEconItemSchema::CreateEconToolImpl( const char *pszToolType, const char *pszUseString, const char *pszUsageRestriction, item_capabilities_t unCapabilities, KeyValues *pUsageKV )
  4851. {
  4852. if ( pszToolType )
  4853. {
  4854. if ( !V_stricmp( pszToolType, "duel_minigame" ) )
  4855. {
  4856. // Error checking -- make sure we aren't setting properties in the schema that we don't support.
  4857. if ( pszUsageRestriction ) return NULL;
  4858. if ( unCapabilities != ITEM_CAP_NONE ) return NULL;
  4859. if ( pUsageKV ) return NULL;
  4860. return new CEconTool_DuelingMinigame( pszToolType, pszUseString );
  4861. }
  4862. if ( !V_stricmp( pszToolType, "noise_maker" ) )
  4863. {
  4864. // Error checking -- make sure we aren't setting properties in the schema that we don't support.
  4865. if ( pszUsageRestriction ) return NULL;
  4866. if ( unCapabilities != ITEM_CAP_NONE ) return NULL;
  4867. if ( pUsageKV ) return NULL;
  4868. return new CEconTool_Noisemaker( pszToolType, pszUseString );
  4869. }
  4870. if ( !V_stricmp( pszToolType, "wrapped_gift" ) )
  4871. {
  4872. // Error checking -- make sure we aren't setting properties in the schema that we don't support.
  4873. if ( pszUsageRestriction ) return NULL;
  4874. return new CEconTool_WrappedGift( pszToolType, pszUseString, unCapabilities, pUsageKV );
  4875. }
  4876. if ( !V_stricmp( pszToolType, "backpack_expander" ) )
  4877. {
  4878. // Error checking -- make sure we aren't setting properties in the schema that we don't support.
  4879. if ( pszUsageRestriction ) return NULL;
  4880. if ( unCapabilities != ITEM_CAP_NONE ) return NULL;
  4881. return new CEconTool_BackpackExpander( pszToolType, pszUseString, pUsageKV );
  4882. }
  4883. if ( !V_stricmp( pszToolType, "account_upgrade_to_premium" ) )
  4884. {
  4885. // Error checking -- make sure we aren't setting properties in the schema that we don't support.
  4886. if ( pszUsageRestriction ) return NULL;
  4887. if ( unCapabilities != ITEM_CAP_NONE ) return NULL;
  4888. if ( pUsageKV ) return NULL;
  4889. return new CEconTool_AccountUpgradeToPremium( pszToolType, pszUseString );
  4890. }
  4891. if ( !V_stricmp( pszToolType, "claimcode" ) )
  4892. {
  4893. // Error checking -- make sure we aren't setting properties in the schema that we don't support.
  4894. if ( pszUsageRestriction ) return NULL;
  4895. if ( unCapabilities != ITEM_CAP_NONE ) return NULL;
  4896. return new CEconTool_ClaimCode( pszToolType, pszUseString, pUsageKV );
  4897. }
  4898. if ( !V_stricmp( pszToolType, "gift" ) )
  4899. {
  4900. // Error checking -- make sure we aren't setting properties in the schema that we don't support.
  4901. if ( pszUsageRestriction ) return NULL;
  4902. if ( unCapabilities != ITEM_CAP_NONE ) return NULL;
  4903. return new CEconTool_Gift( pszToolType, pszUseString, pUsageKV );
  4904. }
  4905. if ( !V_stricmp( pszToolType, "paint_can" ) )
  4906. {
  4907. // Error checking -- make sure we aren't setting properties in the schema that we don't support.
  4908. if ( pszUsageRestriction ) return NULL;
  4909. if ( pUsageKV ) return NULL;
  4910. return new CEconTool_PaintCan( pszToolType, unCapabilities );
  4911. }
  4912. if ( !V_stricmp( pszToolType, "name" ) )
  4913. {
  4914. // Error checking -- make sure we aren't setting properties in the schema that we don't support.
  4915. if ( pszUsageRestriction ) return NULL;
  4916. if ( pUsageKV ) return NULL;
  4917. return new CEconTool_NameTag( pszToolType, unCapabilities );
  4918. }
  4919. if ( !V_stricmp( pszToolType, "desc" ) )
  4920. {
  4921. // Error checking -- make sure we aren't setting properties in the schema that we don't support.
  4922. if ( pszUsageRestriction ) return NULL;
  4923. if ( pUsageKV ) return NULL;
  4924. return new CEconTool_DescTag( pszToolType, unCapabilities );
  4925. }
  4926. if ( !V_stricmp( pszToolType, "decoder_ring" ) )
  4927. {
  4928. // Error checking -- make sure we aren't setting properties in the schema that we don't support.
  4929. if ( pUsageKV ) return NULL;
  4930. return new CEconTool_CrateKey( pszToolType, pszUsageRestriction, unCapabilities );
  4931. }
  4932. if ( !V_stricmp( pszToolType, "customize_texture_item" ) )
  4933. {
  4934. // Error checking -- make sure we aren't setting properties in the schema that we don't support.
  4935. if ( pszUsageRestriction ) return NULL;
  4936. if ( pUsageKV ) return NULL;
  4937. return new CEconTool_CustomizeTexture( pszToolType, unCapabilities );
  4938. }
  4939. if ( !V_stricmp( pszToolType, "gift_wrap" ) )
  4940. {
  4941. // Error checking -- make sure we aren't setting properties in the schema that we don't support.
  4942. if ( pszUsageRestriction ) return NULL;
  4943. return new CEconTool_GiftWrap( pszToolType, pszUseString, unCapabilities, pUsageKV );
  4944. }
  4945. if ( !V_stricmp( pszToolType, "wedding_ring" ) )
  4946. {
  4947. // Error checking -- make sure we aren't setting properties in the schema that we don't support.
  4948. if ( pszUsageRestriction ) return NULL;
  4949. if ( pUsageKV ) return NULL;
  4950. return new CEconTool_WeddingRing( pszToolType, pszUseString, unCapabilities );
  4951. }
  4952. if ( !V_stricmp( pszToolType, "strange_part" ) )
  4953. {
  4954. // Error checking -- make sure we aren't setting properties in the schema that we don't support.
  4955. if ( pszUsageRestriction ) return NULL;
  4956. return new CEconTool_StrangePart( pszToolType, pszUseString, unCapabilities, pUsageKV );
  4957. }
  4958. if ( !V_stricmp( pszToolType, "strange_part_restriction" ) )
  4959. {
  4960. // Error checking -- make sure we aren't setting properties in the schema that we don't support.
  4961. if ( pszUsageRestriction ) return NULL;
  4962. if ( !pUsageKV ) return NULL; // required
  4963. return new CEconTool_StrangePartRestriction( pszToolType, pszUseString, unCapabilities, pUsageKV );
  4964. }
  4965. if ( !V_stricmp( pszToolType, "apply_custom_attrib" ) )
  4966. {
  4967. // Error checking -- make sure we aren't setting properties in the schema that we don't support.
  4968. if ( pszUsageRestriction ) return NULL;
  4969. return new CEconTool_UpgradeCard( pszToolType, pszUseString, unCapabilities, pUsageKV );
  4970. }
  4971. if ( !V_stricmp( pszToolType, "strangifier" ) )
  4972. {
  4973. // Error checking -- make sure we aren't setting properties in the schema that we don't support.
  4974. if ( pszUsageRestriction ) return NULL;
  4975. return new CEconTool_Strangifier( pszToolType, pszUseString, unCapabilities, pUsageKV );
  4976. }
  4977. if ( !V_stricmp( pszToolType, "killstreakifier" ) )
  4978. {
  4979. // Error checking -- make sure we aren't setting properties in the schema that we don't support.
  4980. if ( pszUsageRestriction ) return NULL;
  4981. return new CEconTool_KillStreakifier( pszToolType, pszUseString, unCapabilities, pUsageKV );
  4982. }
  4983. if( !V_stricmp( pszToolType, "dynamic_recipe" ) )
  4984. {
  4985. // Error checking -- make sure we aren't setting properties in the schema that we don't support.
  4986. if ( pszUsageRestriction ) return NULL;
  4987. return new CEconTool_ItemDynamicRecipe( pszToolType, pszUseString, unCapabilities, pUsageKV );
  4988. }
  4989. if ( !V_stricmp( pszToolType, "item_eater_recharger" ) )
  4990. {
  4991. // Error checking -- make sure we aren't setting properties in the schema that we don't support.
  4992. if ( pszUsageRestriction ) return NULL;
  4993. return new CEconTool_ItemEaterRecharger( pszToolType, pszUseString, unCapabilities, pUsageKV );
  4994. }
  4995. if ( !V_stricmp( pszToolType, "class_transmogrifier" ) )
  4996. {
  4997. // Error checking -- make sure we aren't setting properties in the schema that we don't support.
  4998. if ( pszUsageRestriction ) return NULL;
  4999. return new CEconTool_ClassTransmogrifier( pszToolType, pszUseString, unCapabilities, pUsageKV );
  5000. }
  5001. if ( !V_stricmp( pszToolType, "duck_token" ) )
  5002. {
  5003. // Error checking -- make sure we aren't setting properties in the schema that we don't support.
  5004. if ( pszUsageRestriction ) return NULL;
  5005. if ( pUsageKV ) return NULL;
  5006. return new CEconTool_DuckToken( pszToolType, unCapabilities );
  5007. }
  5008. if ( !V_stricmp( pszToolType, "grant_operation_pass" ) )
  5009. {
  5010. // Error checking -- make sure we aren't setting properties in the schema that we don't support.
  5011. if ( pszUsageRestriction ) return NULL;
  5012. return new CEconTool_GrantOperationPass( pszToolType, pszUseString, unCapabilities, pUsageKV );
  5013. }
  5014. if ( !V_stricmp( pszToolType, "strange_count_transfer" ) )
  5015. {
  5016. // Error checking -- make sure we aren't setting properties in the schema that we don't support.
  5017. if ( pszUsageRestriction ) return NULL;
  5018. if ( pUsageKV ) return NULL;
  5019. return new CEconTool_StrangeCountTransfer( pszToolType, unCapabilities );
  5020. }
  5021. if ( !V_stricmp( pszToolType, "paintkit_weapon_festivizer" ) )
  5022. {
  5023. return new CEconTool_Festivizer( pszToolType, pszUseString, unCapabilities, pUsageKV );
  5024. }
  5025. if ( !V_stricmp( pszToolType, "unusualifier" ) )
  5026. {
  5027. return new CEconTool_Unusualifier( pszToolType, pszUseString, unCapabilities, pUsageKV );
  5028. }
  5029. }
  5030. // Default behavior.
  5031. return new CEconTool_Default( pszToolType, pszUseString, pszUsageRestriction, unCapabilities );
  5032. }
  5033. #ifdef GC_DLL
  5034. random_attrib_t *CEconItemSchema::CreateRandomAttribute( const char *pszContext, KeyValues *pRandomAttributesKV, CUtlVector<CUtlString> *pVecErrors /*= NULL*/ )
  5035. {
  5036. // We've found the random attribute block. Parse it.
  5037. if ( pRandomAttributesKV->FindKey( "chance" ) == NULL )
  5038. {
  5039. CUtlString msg; \
  5040. msg.Format( CFmtStr( "Missing required field \"chance\" in the \"random_attributes\" block." ) );
  5041. if ( pVecErrors )
  5042. {
  5043. pVecErrors->AddToTail( msg );
  5044. }
  5045. else
  5046. {
  5047. AssertMsg( pRandomAttributesKV->FindKey( "chance" ) != NULL, msg.String() );
  5048. }
  5049. return NULL;
  5050. }
  5051. random_attrib_t randomAttrib;
  5052. randomAttrib.m_flChanceOfRandomAttribute = pRandomAttributesKV->GetFloat( "chance" );
  5053. randomAttrib.m_bPickAllAttributes = ( pRandomAttributesKV->GetFloat( "pick_all_attributes" ) != 0 );
  5054. randomAttrib.m_flTotalAttributeWeight = 0;
  5055. FOR_EACH_TRUE_SUBKEY( pRandomAttributesKV, pKVAttribute )
  5056. {
  5057. const char *pszName = pKVAttribute->GetName();
  5058. if ( !Q_strcmp( pszName, "chance" ) )
  5059. continue;
  5060. // Quick block list of attrs that have equal weight
  5061. if ( !Q_strcmp( pszName, "is_even_chance_attr" ) )
  5062. {
  5063. FOR_EACH_VALUE( pKVAttribute, pKVListItem )
  5064. {
  5065. const CEconItemAttributeDefinition *pDef = GetAttributeDefinitionByName( pKVListItem->GetName() );
  5066. if ( pDef == NULL )
  5067. {
  5068. CUtlString msg; \
  5069. msg.Format( CFmtStr( "Attribute definition \"%s\" was not found", pszName ) );
  5070. if ( pVecErrors )
  5071. {
  5072. pVecErrors->AddToTail( msg );
  5073. }
  5074. else
  5075. {
  5076. AssertMsg( pDef != NULL, msg.String() );
  5077. }
  5078. return NULL;
  5079. }
  5080. lootlist_attrib_t lootListAttrib;
  5081. if ( !lootListAttrib.m_staticAttrib.BInitFromKV_SingleLine( __FUNCTION__, pKVListItem, pVecErrors, false ) )
  5082. {
  5083. if ( pVecErrors )
  5084. {
  5085. pVecErrors->AddToTail( __FUNCTION__ ": error initializing line-item attribute from lootlist definition (possible attr template).\n" );
  5086. }
  5087. return NULL;
  5088. }
  5089. // Weight is set to 1 for even chance attr
  5090. lootListAttrib.m_flWeight = 1.0f;
  5091. randomAttrib.m_flTotalAttributeWeight += 1.0f;
  5092. randomAttrib.m_RandomAttributes.AddToTail( lootListAttrib );
  5093. }
  5094. }
  5095. else
  5096. {
  5097. const CEconItemAttributeDefinition *pDef = GetAttributeDefinitionByName( pszName );
  5098. if ( pDef == NULL )
  5099. {
  5100. CUtlString msg; \
  5101. msg.Format( CFmtStr( "Attribute definition \"%s\" was not found", pszName ) );
  5102. if ( pVecErrors )
  5103. {
  5104. pVecErrors->AddToTail( msg );
  5105. }
  5106. else
  5107. {
  5108. AssertMsg( pDef != NULL, msg.String() );
  5109. }
  5110. return NULL;
  5111. }
  5112. lootlist_attrib_t lootListAttrib;
  5113. if ( !lootListAttrib.BInitFromKV( pszContext, pKVAttribute, *this, pVecErrors ) )
  5114. {
  5115. return NULL;
  5116. }
  5117. randomAttrib.m_flTotalAttributeWeight += lootListAttrib.m_flWeight;
  5118. randomAttrib.m_RandomAttributes.AddToTail( lootListAttrib );
  5119. }
  5120. }
  5121. random_attrib_t *pRandomAttr = new random_attrib_t;
  5122. *pRandomAttr = randomAttrib;
  5123. return pRandomAttr;
  5124. }
  5125. #endif // GC_DLL
  5126. //-----------------------------------------------------------------------------
  5127. // Purpose: Resets the schema to before BInit was called
  5128. //-----------------------------------------------------------------------------
  5129. void CEconItemSchema::Reset( void )
  5130. {
  5131. ++m_unResetCount;
  5132. m_unFirstValidClass = 0;
  5133. m_unLastValidClass = 0;
  5134. m_unAccoutClassIndex = 0;
  5135. m_unFirstValidClassItemSlot = 0;
  5136. m_unLastValidClassItemSlot = 0;
  5137. m_unFirstValidAccountItemSlot = 0;
  5138. m_unLastValidAccountItemSlot = 0;
  5139. m_unNumItemPresets = 0;
  5140. m_unMinLevel = 0;
  5141. m_unMaxLevel = 0;
  5142. m_unVersion = 0;
  5143. m_unSumQualityWeights = 0;
  5144. FOR_EACH_VEC( m_vecAttributeTypes, i )
  5145. {
  5146. delete m_vecAttributeTypes[i].m_pAttrType;
  5147. }
  5148. m_vecAttributeTypes.Purge();
  5149. m_mapItems.PurgeAndDeleteElements();
  5150. m_mapItems.Purge();
  5151. m_mapRarities.Purge();
  5152. m_mapQualities.Purge();
  5153. m_mapItemsSorted.Purge();
  5154. m_mapToolsItems.Purge();
  5155. m_mapBaseItems.Purge();
  5156. m_mapRecipes.PurgeAndDeleteElements();
  5157. m_vecTimedRewards.Purge();
  5158. m_mapItemSets.PurgeAndDeleteElements();
  5159. m_mapLootLists.PurgeAndDeleteElements();
  5160. #ifdef GC_DLL
  5161. m_dictRandomAttributeTemplates.PurgeAndDeleteElements();
  5162. #endif // GC_DLL
  5163. m_mapAttributeControlledParticleSystems.Purge();
  5164. m_vecAttributeControlledParticleSystemsCosmetics.Purge();
  5165. m_vecAttributeControlledParticleSystemsWeapons.Purge();
  5166. m_vecAttributeControlledParticleSystemsTaunts.Purge();
  5167. m_mapAttributes.Purge();
  5168. if ( m_pKVRawDefinition )
  5169. {
  5170. m_pKVRawDefinition->deleteThis();
  5171. m_pKVRawDefinition = NULL;
  5172. }
  5173. #if defined(CLIENT_DLL) || defined(GAME_DLL)
  5174. delete m_pDefaultItemDefinition;
  5175. m_pDefaultItemDefinition = NULL;
  5176. #endif
  5177. FOR_EACH_MAP_FAST( m_mapRecipes, i )
  5178. {
  5179. delete m_mapRecipes[i];
  5180. }
  5181. FOR_EACH_MAP_FAST( m_mapDefinitionPrefabs, i )
  5182. {
  5183. m_mapDefinitionPrefabs[i]->deleteThis();
  5184. }
  5185. m_mapDefinitionPrefabs.Purge();
  5186. m_vecEquipRegionsList.Purge();
  5187. m_vecItemLevelingData.PurgeAndDeleteElements();
  5188. m_dictStringTable.PurgeAndDeleteElements();
  5189. m_mapCommunityMarketDefinitionIndexRemap.Purge();
  5190. }
  5191. //-----------------------------------------------------------------------------
  5192. // Purpose: Operator=
  5193. //-----------------------------------------------------------------------------
  5194. CEconItemSchema &CEconItemSchema::operator=( CEconItemSchema &rhs )
  5195. {
  5196. Reset();
  5197. BInitSchema( rhs.m_pKVRawDefinition );
  5198. return *this;
  5199. }
  5200. bool g_bLastSignatureCheck;
  5201. bool CheckValveSignature( const void *data, uint32 nDataSize, const void *signature, uint32 nSignatureSize )
  5202. {
  5203. // Must match the PUBLIC KEY in src\devtools\valve_source_officialcontent.privatekey.vdf
  5204. static const unsigned char valvePublicKey[] =
  5205. "\x30\x81\x9D\x30\x0D\x06\x09\x2A\x86\x48\x86\xF7\x0D\x01\x01\x01"
  5206. "\x05\x00\x03\x81\x8B\x00\x30\x81\x87\x02\x81\x81\x00\xB1\xC0\xF1"
  5207. "\x1C\xB2\x98\x2F\x29\x25\x95\x07\xA7\x74\xD4\x83\x43\x77\xC5\xB7"
  5208. "\xA3\x8D\x9A\x4B\x38\x92\xB5\x98\x00\x9F\x16\xAA\x10\x95\x65\xCB"
  5209. "\x09\xAD\x25\xDE\x0D\x3D\x1A\x08\x9C\x3C\xB6\x8E\x49\x19\x21\xCC"
  5210. "\x14\x2F\x38\x33\x83\x20\x1D\xE9\x82\x62\xA7\x6E\xD8\xA6\xCC\x78"
  5211. "\xBC\x51\x68\x5A\x0A\x64\xA6\x17\x2C\x67\x12\x7A\xF2\x3E\x78\x73"
  5212. "\x1F\x4A\x82\xC2\x01\xD6\x4C\x9A\xB8\x09\x37\x32\x21\x84\xB6\x42"
  5213. "\x72\x7F\xE1\x42\xD1\x5C\xC0\x45\xF3\x58\x3E\x19\xE3\xE3\xE1\xA9"
  5214. "\xC5\x0C\x0F\xC8\x41\x13\x57\x3A\x52\x0A\x8F\x73\x23\x02\x01\x11";
  5215. // Put into a global var. Could help with VAC detection, if this
  5216. // code gets detoured
  5217. g_bLastSignatureCheck = CCrypto::RSAVerifySignatureSHA256(
  5218. (const uint8 *)data, nDataSize,
  5219. (const uint8 *)signature, nSignatureSize,
  5220. valvePublicKey, sizeof(valvePublicKey)
  5221. );
  5222. return g_bLastSignatureCheck;
  5223. }
  5224. //-----------------------------------------------------------------------------
  5225. // Initializes the schema, given KV filename
  5226. //-----------------------------------------------------------------------------
  5227. bool CEconItemSchema::BInit( const char *fileName, const char *pathID, CUtlVector<CUtlString> *pVecErrors /* = NULL */)
  5228. {
  5229. Reset();
  5230. // Read the raw data
  5231. CUtlBuffer bufRawData;
  5232. bool bReadFileOK = g_pFullFileSystem->ReadFile( fileName, pathID, bufRawData );
  5233. SCHEMA_INIT_CHECK( bReadFileOK, "Cannot load file '%s'", fileName );
  5234. // Do we need to check the signature?
  5235. #if defined(TF_DLL) || defined(TF_CLIENT_DLL)
  5236. {
  5237. // Load up the signature
  5238. CUtlString sSignatureFilename( fileName ); sSignatureFilename.Append( ".sig" );
  5239. CUtlBuffer bufSignatureBinary;
  5240. bool bReadSignatureOK = g_pFullFileSystem->ReadFile( sSignatureFilename.String(), pathID, bufSignatureBinary );
  5241. SCHEMA_INIT_CHECK( bReadSignatureOK, "Cannot load file '%s'", sSignatureFilename.String() );
  5242. // Check it with the Valve public key
  5243. bool bSignatureValid = CheckValveSignature(
  5244. bufRawData.Base(), bufRawData.TellPut(),
  5245. bufSignatureBinary.Base(), bufSignatureBinary.TellPut()
  5246. );
  5247. // If they have a signature for a zero-byte file, that's OK, too.
  5248. // That's the secret code that is checked into P4 internally that
  5249. // let's us run with any items_game file
  5250. if ( !bSignatureValid )
  5251. {
  5252. bSignatureValid = CheckValveSignature(
  5253. "", 0,
  5254. bufSignatureBinary.Base(), bufSignatureBinary.TellPut()
  5255. );
  5256. }
  5257. 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 );
  5258. }
  5259. #endif
  5260. // Compute version hash
  5261. CSHA1 sha1;
  5262. sha1.Update( (unsigned char *)bufRawData.Base(), bufRawData.Size() );
  5263. sha1.Final();
  5264. sha1.GetHash( m_schemaSHA.m_shaDigest );
  5265. // Wrap it with a text buffer reader
  5266. CUtlBuffer bufText( bufRawData.Base(), bufRawData.TellPut(), CUtlBuffer::READ_ONLY | CUtlBuffer::TEXT_BUFFER );
  5267. // Use the standard init path
  5268. return BInitTextBuffer( bufText, pVecErrors );
  5269. }
  5270. //-----------------------------------------------------------------------------
  5271. // Initializes the schema, given KV in binary form
  5272. //-----------------------------------------------------------------------------
  5273. bool CEconItemSchema::BInitBinaryBuffer( CUtlBuffer &buffer, CUtlVector<CUtlString> *pVecErrors /* = NULL */ )
  5274. {
  5275. Reset();
  5276. m_pKVRawDefinition = new KeyValues( "CEconItemSchema" );
  5277. if ( m_pKVRawDefinition->ReadAsBinary( buffer ) )
  5278. {
  5279. return BInitSchema( m_pKVRawDefinition, pVecErrors )
  5280. && BPostSchemaInit( pVecErrors );
  5281. }
  5282. if ( pVecErrors )
  5283. {
  5284. pVecErrors->AddToTail( "Error parsing keyvalues" );
  5285. }
  5286. return false;
  5287. }
  5288. unsigned char g_sha1ItemSchemaText[ k_cubHash ];
  5289. //-----------------------------------------------------------------------------
  5290. // Initializes the schema, given KV in text form
  5291. //-----------------------------------------------------------------------------
  5292. bool CEconItemSchema::BInitTextBuffer( CUtlBuffer &buffer, CUtlVector<CUtlString> *pVecErrors /* = NULL */ )
  5293. {
  5294. // Save off the hash into a global variable, so VAC can check it
  5295. // later
  5296. GenerateHash( g_sha1ItemSchemaText, buffer.Base(), buffer.TellPut() );
  5297. Reset();
  5298. m_pKVRawDefinition = new KeyValues( "CEconItemSchema" );
  5299. if ( m_pKVRawDefinition->LoadFromBuffer( NULL, buffer ) )
  5300. {
  5301. return BInitSchema( m_pKVRawDefinition, pVecErrors )
  5302. && BPostSchemaInit( pVecErrors );
  5303. }
  5304. if ( pVecErrors )
  5305. {
  5306. pVecErrors->AddToTail( "Error parsing keyvalues" );
  5307. }
  5308. return false;
  5309. }
  5310. bool CEconItemSchema::DumpItems ( const char *fileName, const char *pathID )
  5311. {
  5312. // create a write file
  5313. FileHandle_t f = g_pFullFileSystem->Open(fileName, "wb", pathID);
  5314. if ( f == FILESYSTEM_INVALID_HANDLE )
  5315. {
  5316. DevMsg(1, "CEconItemSchema::DumpItems: couldn't open file \"%s\" in path \"%s\".\n",
  5317. fileName?fileName:"NULL", pathID?pathID:"NULL" );
  5318. return false;
  5319. }
  5320. CUtlSortVector< KeyValues*, CUtlSortVectorKeyValuesByName > vecSortedItems;
  5321. FOR_EACH_MAP_FAST( m_mapItems, i )
  5322. {
  5323. vecSortedItems.InsertNoSort( m_mapItems[ i ]->GetRawDefinition() );
  5324. }
  5325. vecSortedItems.RedoSort();
  5326. CUtlBuffer buf;
  5327. FOR_EACH_VEC( vecSortedItems, i )
  5328. {
  5329. vecSortedItems[i]->RecursiveSaveToFile( buf, 0, true );
  5330. }
  5331. int iBufSize = buf.GetBytesRemaining();
  5332. bool bSuccess = false;
  5333. if ( g_pFullFileSystem->Write(buf.PeekGet(), iBufSize, f) == iBufSize )
  5334. bSuccess = true;
  5335. g_pFullFileSystem->Close(f);
  5336. return bSuccess;
  5337. }
  5338. //-----------------------------------------------------------------------------
  5339. // Called once the price sheet's been loaded
  5340. //-----------------------------------------------------------------------------
  5341. #ifdef GC_DLL
  5342. GCConVar econ_orphaned_sold_items_owned_by_account_id( "econ_orphaned_sold_items_owned_by_account_id", "121416792" );
  5343. bool CEconItemSchema::DoPostPriceSheetLoadInit( CEconStorePriceSheet *pPriceSheet )
  5344. {
  5345. FOR_EACH_MAP_FAST( m_mapItems, iItem )
  5346. {
  5347. CEconItemDefinition *pItemDef = m_mapItems[ iItem ];
  5348. // Is this item being sold?
  5349. const econ_store_entry_t *pStoreEntry = pPriceSheet->GetEntry( pItemDef->GetDefinitionIndex() );
  5350. if ( pStoreEntry )
  5351. {
  5352. // Cache off whether this item is a pack item
  5353. pItemDef->SetIsPackItem( pStoreEntry->m_bIsPackItem );
  5354. // If an item is being sold and it has no payment rules set up, we can optionally force-create
  5355. // a dummy payment rule that will redirect that item revenue to a "hey, these are orphan items!"
  5356. // account.
  5357. if ( pItemDef->GetPaymentRules().Count() == 0 && econ_orphaned_sold_items_owned_by_account_id.GetInt() )
  5358. {
  5359. econ_item_payment_rule_t rule;
  5360. rule.m_RevenueShare = 1.0;
  5361. rule.m_eRuleType = kPaymentRule_PartnerSteamID;
  5362. rule.m_vecValues.AddToTail( econ_orphaned_sold_items_owned_by_account_id.GetInt() );
  5363. DbgVerify( pItemDef->AddPaymentRule( rule ) == 0 );
  5364. }
  5365. }
  5366. // Go through the cache of all bundles...
  5367. FOR_EACH_VEC( m_vecBundles, iBundle )
  5368. {
  5369. const CEconItemDefinition *pBundleItemDef = m_vecBundles[ iBundle ];
  5370. const bundleinfo_t *pBundle = pBundleItemDef->GetBundleInfo();
  5371. const bool bBundleItemIsForSale = pPriceSheet->BItemExistsInPriceSheet( pBundleItemDef->GetDefinitionIndex() ) != NULL; // Only add bundles that are actually for sale
  5372. bool bAddToContainingBundleItemDefs = false;
  5373. if ( pItemDef->IsPackBundle() )
  5374. {
  5375. // 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
  5376. // in pBundle if the first pack item is, since the GC won't startup otherwise. Don't add self as a containing bundle.
  5377. bAddToContainingBundleItemDefs = pItemDef->GetDefinitionIndex() != pBundleItemDef->GetDefinitionIndex()
  5378. && pBundle->vecItemDefs.HasElement( pItemDef->GetBundleInfo()->vecItemDefs[0] );
  5379. }
  5380. else
  5381. {
  5382. bAddToContainingBundleItemDefs = pBundle->vecItemDefs.HasElement( pItemDef );
  5383. }
  5384. // Does the current bundle contain the given item?
  5385. if ( bBundleItemIsForSale && bAddToContainingBundleItemDefs )
  5386. {
  5387. pItemDef->m_vecContainingBundleItemDefs.AddToTail( pBundleItemDef );
  5388. }
  5389. }
  5390. }
  5391. return true;
  5392. }
  5393. #endif
  5394. #if defined(CLIENT_DLL) || defined(GAME_DLL)
  5395. //-----------------------------------------------------------------------------
  5396. // Set up the buffer to use to reinitialize our schema next time we can do so safely.
  5397. //-----------------------------------------------------------------------------
  5398. bool CEconItemSchema::MaybeInitFromBuffer( IDelayedSchemaData *pDelayedSchemaData )
  5399. {
  5400. bool bDidInit = false;
  5401. // Use whatever our most current data block is.
  5402. if ( m_pDelayedSchemaData )
  5403. {
  5404. delete m_pDelayedSchemaData;
  5405. }
  5406. m_pDelayedSchemaData = pDelayedSchemaData;
  5407. #ifdef CLIENT_DLL
  5408. // If we aren't in a game we can parse immediately now.
  5409. if ( !engine->IsInGame() )
  5410. {
  5411. BInitFromDelayedBuffer();
  5412. bDidInit = true;
  5413. }
  5414. #endif // CLIENT_DLL
  5415. return bDidInit;
  5416. }
  5417. //-----------------------------------------------------------------------------
  5418. // We're in a safe place to change the contents of the schema, so do so and clean
  5419. // up whatever memory we were using.
  5420. //-----------------------------------------------------------------------------
  5421. bool CEconItemSchema::BInitFromDelayedBuffer()
  5422. {
  5423. if ( !m_pDelayedSchemaData )
  5424. return true;
  5425. bool bSuccess = m_pDelayedSchemaData->InitializeSchema( this );
  5426. delete m_pDelayedSchemaData;
  5427. m_pDelayedSchemaData = NULL;
  5428. return bSuccess;
  5429. }
  5430. #endif // !GC_DLL
  5431. static void CalculateKeyValuesCRCRecursive( KeyValues *pKV, CRC32_t *crc, bool bIgnoreName = false )
  5432. {
  5433. // Hash in the key name in LOWERCASE. Keyvalues files are not deterministic due
  5434. // to the case insensitivity of the keys and the dependence on the existing
  5435. // state of the name table upon entry.
  5436. if ( !bIgnoreName )
  5437. {
  5438. const char *s = pKV->GetName();
  5439. for (;;)
  5440. {
  5441. unsigned char x = tolower(*s);
  5442. CRC32_ProcessBuffer( crc, &x, 1 ); // !SPEED! This is slow, but it works.
  5443. if (*s == '\0') break;
  5444. ++s;
  5445. }
  5446. }
  5447. // Now hash in value, depending on type
  5448. // !FIXME! This is not byte-order independent!
  5449. switch ( pKV->GetDataType() )
  5450. {
  5451. case KeyValues::TYPE_NONE:
  5452. {
  5453. FOR_EACH_SUBKEY( pKV, pChild )
  5454. {
  5455. CalculateKeyValuesCRCRecursive( pChild, crc );
  5456. }
  5457. break;
  5458. }
  5459. case KeyValues::TYPE_STRING:
  5460. {
  5461. const char *val = pKV->GetString();
  5462. CRC32_ProcessBuffer( crc, val, strlen(val)+1 );
  5463. break;
  5464. }
  5465. case KeyValues::TYPE_INT:
  5466. {
  5467. int val = pKV->GetInt();
  5468. CRC32_ProcessBuffer( crc, &val, sizeof(val) );
  5469. break;
  5470. }
  5471. case KeyValues::TYPE_UINT64:
  5472. {
  5473. uint64 val = pKV->GetUint64();
  5474. CRC32_ProcessBuffer( crc, &val, sizeof(val) );
  5475. break;
  5476. }
  5477. case KeyValues::TYPE_FLOAT:
  5478. {
  5479. float val = pKV->GetFloat();
  5480. CRC32_ProcessBuffer( crc, &val, sizeof(val) );
  5481. break;
  5482. }
  5483. case KeyValues::TYPE_COLOR:
  5484. {
  5485. int val = pKV->GetColor().GetRawColor();
  5486. CRC32_ProcessBuffer( crc, &val, sizeof(val) );
  5487. break;
  5488. }
  5489. default:
  5490. case KeyValues::TYPE_PTR:
  5491. case KeyValues::TYPE_WSTRING:
  5492. {
  5493. Assert( !"Unsupport data type!" );
  5494. break;
  5495. }
  5496. }
  5497. }
  5498. uint32 CEconItemSchema::CalculateKeyValuesVersion( KeyValues *pKV )
  5499. {
  5500. CRC32_t crc;
  5501. CRC32_Init( &crc );
  5502. // Calc CRC recursively. Ignore the very top-most
  5503. // key name, which isn't set consistently
  5504. CalculateKeyValuesCRCRecursive( pKV, &crc, true );
  5505. CRC32_Final( &crc );
  5506. return crc;
  5507. }
  5508. EEquipType_t CEconItemSchema::GetEquipTypeFromClassIndex( int iClass ) const
  5509. {
  5510. if ( iClass == GetAccountIndex() )
  5511. return EEquipType_t::EQUIP_TYPE_ACCOUNT;
  5512. if ( iClass >= GetFirstValidClass() && iClass <= GetLastValidClass() )
  5513. return EEquipType_t::EQUIP_TYPE_CLASS;
  5514. return EEquipType_t::EQUIP_TYPE_INVALID;
  5515. }
  5516. //-----------------------------------------------------------------------------
  5517. // Purpose: Initializes the schema
  5518. // Input: pKVRawDefinition - The raw KeyValues representation of the schema
  5519. // pVecErrors - An optional vector that will contain error messages if
  5520. // the init fails.
  5521. // Output: True if initialization succeeded, false otherwise
  5522. //-----------------------------------------------------------------------------
  5523. bool CEconItemSchema::BInitSchema( KeyValues *pKVRawDefinition, CUtlVector<CUtlString> *pVecErrors /* = NULL */ )
  5524. {
  5525. m_unMinLevel = pKVRawDefinition->GetInt( "item_level_min", 0 );
  5526. m_unMaxLevel = pKVRawDefinition->GetInt( "item_level_max", 0 );
  5527. #if !defined( GC_DLL )
  5528. m_unVersion = CalculateKeyValuesVersion( pKVRawDefinition );
  5529. #endif
  5530. #ifdef GC_DLL
  5531. // Validate the integrity of the base data.
  5532. SCHEMA_INIT_CHECK( 0 <= m_unMinLevel, "Minimum Item Level must be at least 0" );
  5533. SCHEMA_INIT_CHECK( m_unMinLevel <= m_unMaxLevel, "Minimum Item Level must be less than or equal to Maximum Item Level" );
  5534. #endif // GC_DLL
  5535. // Parse the prefabs block first so the prefabs will be populated in case anything else wants
  5536. // to use them later.
  5537. KeyValues *pKVPrefabs = pKVRawDefinition->FindKey( "prefabs" );
  5538. if ( NULL != pKVPrefabs )
  5539. {
  5540. SCHEMA_INIT_SUBSTEP( BInitDefinitionPrefabs( pKVPrefabs, pVecErrors ) );
  5541. }
  5542. // Initialize the game info block
  5543. KeyValues *pKVGameInfo = pKVRawDefinition->FindKey( "game_info" );
  5544. SCHEMA_INIT_CHECK( NULL != pKVGameInfo, "Required key \"game_info\" missing.\n" );
  5545. if ( NULL != pKVGameInfo )
  5546. {
  5547. SCHEMA_INIT_SUBSTEP( BInitGameInfo( pKVGameInfo, pVecErrors ) );
  5548. }
  5549. // Initialize our attribute types. We don't actually pull this data from the schema right now but it
  5550. // still makes sense to initialize it at this point.
  5551. SCHEMA_INIT_SUBSTEP( BInitAttributeTypes( pVecErrors ) );
  5552. // Initialize the item series block
  5553. KeyValues *pKVItemSeries = pKVRawDefinition->FindKey( "item_series_types" );
  5554. SCHEMA_INIT_CHECK( NULL != pKVItemSeries, "Required key \"item_series_types\" missing.\n" );
  5555. if ( NULL != pKVItemSeries )
  5556. {
  5557. SCHEMA_INIT_SUBSTEP( BInitItemSeries( pKVItemSeries, pVecErrors ) );
  5558. }
  5559. // Initialize the rarity block
  5560. KeyValues *pKVRarities = pKVRawDefinition->FindKey( "rarities" );
  5561. KeyValues *pKVRarityWeights = pKVRawDefinition->FindKey( "rarities_lootlist_weights" );
  5562. SCHEMA_INIT_CHECK( NULL != pKVRarities, "Required key \"rarities\" missing.\n" );
  5563. if ( NULL != pKVRarities )
  5564. {
  5565. SCHEMA_INIT_SUBSTEP( BInitRarities( pKVRarities, pKVRarityWeights, pVecErrors ) );
  5566. }
  5567. // Initialize the qualities block
  5568. KeyValues *pKVQualities = pKVRawDefinition->FindKey( "qualities" );
  5569. SCHEMA_INIT_CHECK( NULL != pKVQualities, "Required key \"qualities\" missing.\n" );
  5570. if ( NULL != pKVQualities )
  5571. {
  5572. SCHEMA_INIT_SUBSTEP( BInitQualities( pKVQualities, pVecErrors ) );
  5573. }
  5574. // Initialize the colors block
  5575. KeyValues *pKVColors = pKVRawDefinition->FindKey( "colors" );
  5576. SCHEMA_INIT_CHECK( NULL != pKVColors, "Required key \"colors\" missing.\n" );
  5577. if ( NULL != pKVColors )
  5578. {
  5579. SCHEMA_INIT_SUBSTEP( BInitColors( pKVColors, pVecErrors ) );
  5580. }
  5581. // Initialize the attributes block
  5582. KeyValues *pKVAttributes = pKVRawDefinition->FindKey( "attributes" );
  5583. SCHEMA_INIT_CHECK( NULL != pKVAttributes, "Required key \"attributes\" missing.\n" );
  5584. if ( NULL != pKVAttributes )
  5585. {
  5586. SCHEMA_INIT_SUBSTEP( BInitAttributes( pKVAttributes, pVecErrors ) );
  5587. }
  5588. #ifdef GC
  5589. // Initialize the motd block
  5590. KeyValues *pKVMOTD = pKVRawDefinition->FindKey( "motd_entries" );
  5591. SCHEMA_INIT_CHECK( NULL != pKVMOTD, "Required key \"motd_entries\" missing.\n" );
  5592. if ( NULL != pKVMOTD )
  5593. {
  5594. SCHEMA_INIT_SUBSTEP( GGCGameBase()->GetMOTDManager().BInitMOTDEntries( pKVMOTD, pVecErrors ) );
  5595. }
  5596. #endif
  5597. // Initialize the "equip_regions_list" block -- this is an optional block
  5598. KeyValues *pKVEquipRegions = pKVRawDefinition->FindKey( "equip_regions_list" );
  5599. if ( NULL != pKVEquipRegions )
  5600. {
  5601. SCHEMA_INIT_SUBSTEP( BInitEquipRegions( pKVEquipRegions, pVecErrors ) );
  5602. }
  5603. // Initialize the "equip_conflicts" block -- this is an optional block, though it doesn't
  5604. // make any sense and will probably fail internally if there is no corresponding "equip_regions"
  5605. // block as well
  5606. KeyValues *pKVEquipRegionConflicts = pKVRawDefinition->FindKey( "equip_conflicts" );
  5607. if ( NULL != pKVEquipRegionConflicts )
  5608. {
  5609. SCHEMA_INIT_SUBSTEP( BInitEquipRegionConflicts( pKVEquipRegionConflicts, pVecErrors ) );
  5610. }
  5611. // TF2 Paint Kits
  5612. // No included in schema file (Too Big). Loaded Seperately
  5613. // Load the KV and add it to the pKVRawDefinition
  5614. KeyValues *pPaintkitKV = new KeyValues( "item_paintkit_definitions" );
  5615. SCHEMA_INIT_CHECK( pPaintkitKV->LoadFromFile( g_pFullFileSystem, "scripts/items/paintkits_master.txt", "GAME" ), "Unable to Load paintkits_master.txt KV File!" );
  5616. pKVRawDefinition->AddSubKey( pPaintkitKV );
  5617. // Init Item Paint Kits
  5618. // Must be BEFORE Item defs
  5619. KeyValues *pKVItemPaintKits = pKVRawDefinition->FindKey( "item_paintkit_definitions" );
  5620. if ( NULL != pKVItemPaintKits )
  5621. {
  5622. SCHEMA_INIT_SUBSTEP( BInitItemPaintKitDefinitions( pKVItemPaintKits, pVecErrors ) );
  5623. }
  5624. #ifdef GC_DLL
  5625. // Parse the loot lists block (on the GC)
  5626. // Must be BEFORE Item defs
  5627. KeyValues *pKVRandomAttributeTemplates = pKVRawDefinition->FindKey( "random_attribute_templates" );
  5628. SCHEMA_INIT_SUBSTEP( BInitRandomAttributeTemplates( pKVRandomAttributeTemplates, pVecErrors ) );
  5629. #endif // GC_DLL
  5630. // Initialize the items block
  5631. KeyValues *pKVItems = pKVRawDefinition->FindKey( "items" );
  5632. SCHEMA_INIT_CHECK( NULL != pKVItems, "Required key \"items\" missing.\n" );
  5633. if ( NULL != pKVItems )
  5634. {
  5635. SCHEMA_INIT_SUBSTEP( BInitItems( pKVItems, pVecErrors ) );
  5636. }
  5637. // Verify base item names are proper in item schema
  5638. SCHEMA_INIT_SUBSTEP( BVerifyBaseItemNames( pVecErrors ) );
  5639. // Parse the item_sets block.
  5640. KeyValues *pKVItemSets = pKVRawDefinition->FindKey( "item_sets" );
  5641. SCHEMA_INIT_SUBSTEP( BInitItemSets( pKVItemSets, pVecErrors ) );
  5642. // Particles
  5643. KeyValues *pKVParticleSystems = pKVRawDefinition->FindKey( "attribute_controlled_attached_particles" );
  5644. SCHEMA_INIT_SUBSTEP( BInitAttributeControlledParticleSystems( pKVParticleSystems, pVecErrors ) );
  5645. // Parse any recipes block
  5646. KeyValues *pKVRecipes = pKVRawDefinition->FindKey( "recipes" );
  5647. SCHEMA_INIT_SUBSTEP( BInitRecipes( pKVRecipes, pVecErrors ) );
  5648. // Reset our loot lists.
  5649. m_mapLootLists.RemoveAll();
  5650. // Init Item Collections - Must be before lootlists since collections are lootlists themselves and are referenced by lootlists
  5651. KeyValues *pKVItemCollections = pKVRawDefinition->FindKey( "item_collections" );
  5652. if ( NULL != pKVItemCollections )
  5653. {
  5654. SCHEMA_INIT_SUBSTEP( BInitItemCollections( pKVItemCollections, pVecErrors ) );
  5655. }
  5656. #ifdef GC_DLL
  5657. // Parse the loot lists block (on the GC)
  5658. KeyValues *pKVLootLists = pKVRawDefinition->FindKey( "loot_lists" );
  5659. SCHEMA_INIT_SUBSTEP( BInitLootLists( pKVLootLists, pVecErrors ) );
  5660. // Initialize the periodic score accumulation block (this needs to take place after items)
  5661. KeyValues *pKVPeriodicScoring = pKVRawDefinition->FindKey( "periodic_score_accumulation" );
  5662. if ( NULL != pKVPeriodicScoring )
  5663. {
  5664. SCHEMA_INIT_SUBSTEP( BInitPeriodicScoring( pKVPeriodicScoring, pVecErrors ) );
  5665. }
  5666. #endif // GC_DLL
  5667. // Parse the client loot lists block (everywhere)
  5668. KeyValues *pKVClientLootLists = pKVRawDefinition->FindKey( "client_loot_lists" );
  5669. SCHEMA_INIT_SUBSTEP( BInitLootLists( pKVClientLootLists, pVecErrors ) );
  5670. // Parse the revolving loot lists block
  5671. KeyValues *pKVRevolvingLootLists = pKVRawDefinition->FindKey( "revolving_loot_lists" );
  5672. SCHEMA_INIT_SUBSTEP( BInitRevolvingLootLists( pKVRevolvingLootLists, pVecErrors ) );
  5673. // Init Items that may reference Collections
  5674. SCHEMA_INIT_SUBSTEP( BInitCollectionReferences( pVecErrors ) );
  5675. // Validate Operation Pass
  5676. KeyValues *pKVOperationDefinitions = pKVRawDefinition->FindKey( "operations" );
  5677. if ( NULL != pKVOperationDefinitions )
  5678. {
  5679. SCHEMA_INIT_SUBSTEP( BInitOperationDefinitions( pKVGameInfo, pKVOperationDefinitions, pVecErrors ) );
  5680. }
  5681. #if defined( GC_DLL )
  5682. // Parse any time-based rewards
  5683. KeyValues *pKVTimeRewards = pKVRawDefinition->FindKey( "time_rewards" );
  5684. SCHEMA_INIT_SUBSTEP( BInitTimedRewards( pKVTimeRewards, pVecErrors ) );
  5685. KeyValues *pKVExperiments = pKVRawDefinition->FindKey( "experiments" );
  5686. SCHEMA_INIT_SUBSTEP( BInitExperiements( pKVExperiments, pVecErrors ) );
  5687. SCHEMA_INIT_SUBSTEP( BInitForeignImports( pVecErrors ) );
  5688. #elif defined( CLIENT_DLL ) || defined( GAME_DLL )
  5689. KeyValues *pKVArmoryData = pKVRawDefinition->FindKey( "armory_data" );
  5690. SCHEMA_INIT_SUBSTEP( BInitArmoryData( pKVArmoryData, pVecErrors ) );
  5691. #endif // GC_DLL
  5692. // Parse any achievement rewards
  5693. KeyValues *pKVAchievementRewards = pKVRawDefinition->FindKey( "achievement_rewards" );
  5694. SCHEMA_INIT_SUBSTEP( BInitAchievementRewards( pKVAchievementRewards, pVecErrors ) );
  5695. #ifdef TF_CLIENT_DLL
  5696. // Compute the number of concrete items, for each item, and cache for quick access
  5697. SCHEMA_INIT_SUBSTEP( BInitConcreteItemCounts( pVecErrors ) );
  5698. // We don't have access to Steam's full library of app data on the client so initialize whichever packages
  5699. // we want to reference.
  5700. KeyValues *pKVSteamPackages = pKVRawDefinition->FindKey( "steam_packages" );
  5701. SCHEMA_INIT_SUBSTEP( BInitSteamPackageLocalizationToken( pKVSteamPackages, pVecErrors ) );
  5702. #endif // TF_CLIENT_DLL
  5703. // Parse the item levels block
  5704. KeyValues *pKVItemLevels = pKVRawDefinition->FindKey( "item_levels" );
  5705. SCHEMA_INIT_SUBSTEP( BInitItemLevels( pKVItemLevels, pVecErrors ) );
  5706. // Parse the kill eater score types
  5707. KeyValues *pKVKillEaterScoreTypes = pKVRawDefinition->FindKey( "kill_eater_score_types" );
  5708. SCHEMA_INIT_SUBSTEP( BInitKillEaterScoreTypes( pKVKillEaterScoreTypes, pVecErrors ) );
  5709. // Initialize the string tables, if present
  5710. KeyValues *pKVStringTables = pKVRawDefinition->FindKey( "string_lookups" );
  5711. SCHEMA_INIT_SUBSTEP( BInitStringTables( pKVStringTables, pVecErrors ) );
  5712. // Initialize the community Market remaps, if present
  5713. KeyValues *pKVCommunityMarketRemaps = pKVRawDefinition->FindKey( "community_market_item_remaps" );
  5714. SCHEMA_INIT_SUBSTEP( BInitCommunityMarketRemaps( pKVCommunityMarketRemaps, pVecErrors ) );
  5715. return SCHEMA_INIT_SUCCESS();
  5716. }
  5717. //-----------------------------------------------------------------------------
  5718. // Purpose: Initializes the "game_info" section of the schema
  5719. //-----------------------------------------------------------------------------
  5720. bool CEconItemSchema::BInitGameInfo( KeyValues *pKVGameInfo, CUtlVector<CUtlString> *pVecErrors )
  5721. {
  5722. m_unFirstValidClass = pKVGameInfo->GetInt( "first_valid_class", 0 );
  5723. m_unLastValidClass = pKVGameInfo->GetInt( "last_valid_class", 0 );
  5724. SCHEMA_INIT_CHECK( 0 < m_unFirstValidClass, "First valid class must be greater than 0." );
  5725. SCHEMA_INIT_CHECK( m_unFirstValidClass <= m_unLastValidClass, "First valid class must be less than or equal to last valid class." );
  5726. m_unAccoutClassIndex = pKVGameInfo->GetInt( "account_class_index", 0 );
  5727. SCHEMA_INIT_CHECK( m_unAccoutClassIndex > m_unLastValidClass, "Account class index must be greater than 'last_valid_class'" );
  5728. m_unFirstValidClassItemSlot = pKVGameInfo->GetInt( "first_valid_item_slot", INVALID_EQUIPPED_SLOT );
  5729. m_unLastValidClassItemSlot = pKVGameInfo->GetInt( "last_valid_item_slot", INVALID_EQUIPPED_SLOT );
  5730. SCHEMA_INIT_CHECK( INVALID_EQUIPPED_SLOT != m_unFirstValidClassItemSlot, "first_valid_item_slot not set!" );
  5731. SCHEMA_INIT_CHECK( INVALID_EQUIPPED_SLOT != m_unFirstValidClassItemSlot, "last_valid_item_slot not set!" );
  5732. SCHEMA_INIT_CHECK( m_unFirstValidClassItemSlot <= m_unLastValidClassItemSlot, "First valid item slot must be less than or equal to last valid item slot." );
  5733. m_unFirstValidAccountItemSlot = pKVGameInfo->GetInt( "account_first_valid_item_slot", INVALID_EQUIPPED_SLOT );
  5734. m_unLastValidAccountItemSlot = pKVGameInfo->GetInt( "account_last_valid_item_slot", INVALID_EQUIPPED_SLOT );
  5735. SCHEMA_INIT_CHECK( INVALID_EQUIPPED_SLOT != m_unFirstValidAccountItemSlot, "account_first_valid_item_slot not set!" );
  5736. SCHEMA_INIT_CHECK( INVALID_EQUIPPED_SLOT != m_unLastValidAccountItemSlot, "account_last_valid_item_slot not set!" );
  5737. 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." );
  5738. m_unNumItemPresets = pKVGameInfo->GetInt( "num_item_presets", -1 );
  5739. SCHEMA_INIT_CHECK( (uint32)-1 != m_unNumItemPresets, "num_item_presets not set!" );
  5740. return SCHEMA_INIT_SUCCESS();
  5741. }
  5742. //-----------------------------------------------------------------------------
  5743. // Purpose:
  5744. //-----------------------------------------------------------------------------
  5745. bool CEconItemSchema::BInitAttributeTypes( CUtlVector<CUtlString> *pVecErrors )
  5746. {
  5747. FOR_EACH_VEC( m_vecAttributeTypes, i )
  5748. {
  5749. delete m_vecAttributeTypes[i].m_pAttrType;
  5750. }
  5751. m_vecAttributeTypes.Purge();
  5752. m_vecAttributeTypes.AddToTail( attr_type_t( NULL, new CSchemaAttributeType_Default ) );
  5753. m_vecAttributeTypes.AddToTail( attr_type_t( "float", new CSchemaAttributeType_Float ) );
  5754. m_vecAttributeTypes.AddToTail( attr_type_t( "uint64", new CSchemaAttributeType_UInt64 ) );
  5755. m_vecAttributeTypes.AddToTail( attr_type_t( "string", new CSchemaAttributeType_String ) );
  5756. m_vecAttributeTypes.AddToTail( attr_type_t( "dynamic_recipe_component_defined_item", new CSchemaAttributeType_DynamicRecipeComponentDefinedItem ) );
  5757. m_vecAttributeTypes.AddToTail( attr_type_t( "item_slot_criteria", new CSchemaAttributeType_ItemSlotCriteria ) );
  5758. m_vecAttributeTypes.AddToTail( attr_type_t( "item_placement", new CSchemaAttributeType_WorldItemPlacement ) );
  5759. // Make sure that all attribute types specified have the item ID in the 0th column. We use this
  5760. // when loading items to map between item IDs and the attributes they own.
  5761. FOR_EACH_VEC( m_vecAttributeTypes, i )
  5762. {
  5763. #ifdef GC_DLL
  5764. const CColumnSet& cs = m_vecAttributeTypes[i].m_pAttrType->GetFullColumnSet();
  5765. SCHEMA_INIT_CHECK( cs.GetColumnCount() >= 2, "BInitAttributeTypes(): '%s' has invalid column count.\n", cs.GetRecordInfo()->GetName() );
  5766. const CColumnInfo& Column0 = cs.GetColumnInfo( 0 );
  5767. SCHEMA_INIT_CHECK( Column0.GetType() == k_EGCSQLType_int64, "BInitAttributeTypes(): '%s' column 0 has invalid data type %u.\n", cs.GetRecordInfo()->GetName(), Column0.GetType() );
  5768. 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]" );
  5769. SCHEMA_INIT_CHECK( Column0.BIsPrimaryKey(), "BInitAttributeTypes(): '%s' has an item ID column that isn't in the PK.\n", cs.GetRecordInfo()->GetName() );
  5770. const CColumnInfo& Column1 = cs.GetColumnInfo( 1 );
  5771. SCHEMA_INIT_CHECK( Column1.GetType() == k_EGCSQLType_int16, "BInitAttributeTypes(): '%s' column 1 has invalid data type %u.\n", cs.GetRecordInfo()->GetName(), Column0.GetType() );
  5772. 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]" );
  5773. // Make sure two different attribute types don't point to the same DB table. There's nothing
  5774. // technically that would prevent this from working, but right now the way we load from the
  5775. // DB would make this super-inefficient so we'd want to fix that code if we are rolling content
  5776. // that would hit this error.
  5777. for ( int j = i + 1; j < m_vecAttributeTypes.Count(); j++ )
  5778. {
  5779. SCHEMA_INIT_CHECK( cs.GetRecordInfo() != m_vecAttributeTypes[j].m_pAttrType->GetFullColumnSet().GetRecordInfo(),
  5780. "BInitAttributeTypes(): multiple attribute types reference the same table '%s'.\n", cs.GetRecordInfo()->GetName() );
  5781. }
  5782. #endif // GC_DLL
  5783. }
  5784. return SCHEMA_INIT_SUCCESS();
  5785. }
  5786. #ifdef GC_DLL
  5787. //-----------------------------------------------------------------------------
  5788. // Purpose: Initializes the "periodic_score_accumulation" section of the schema
  5789. //-----------------------------------------------------------------------------
  5790. struct periodic_score_event_lookup_entry_t { const char *m_pszName; eEconPeriodicScoreEvents m_eValue; bool m_bGCUpdateOnly; };
  5791. static const periodic_score_event_lookup_entry_t sPeriodicScoreEvents[] =
  5792. {
  5793. { "gifts_distributed", kPeriodicScoreEvent_GiftsDistributed, true },
  5794. { "duels_won", kPeriodicScoreEvent_DuelsWon, true },
  5795. { "map_stamps_purchased", kPeriodicScoreEvent_MapStampsPurchased, true },
  5796. };
  5797. struct periodic_score_duration_lookup_entry_t { const char *m_pszName; uint32 m_unValue; };
  5798. static const periodic_score_duration_lookup_entry_t sPeriodicScoreDurations[] =
  5799. {
  5800. { "disabled", 0 },
  5801. { "hourly", 60 * 60 },
  5802. { "daily", 60 * 60 * 24 },
  5803. { "weekly", 60 * 60 * 24 * 7 },
  5804. { "monthly", 60 * 60 * 24 * 7 * 4 }, // four weeks, not necessarily a month boundary
  5805. };
  5806. template < typename search_entry_type, int search_entry_array_size >
  5807. static bool LookupValueFromString( const search_entry_type(&searchArray)[search_entry_array_size], const char *pszSearch, search_entry_type *out_pResult )
  5808. {
  5809. Assert( out_pResult );
  5810. for ( int i = 0; i < search_entry_array_size; i++ )
  5811. {
  5812. if ( !V_stricmp( pszSearch, searchArray[i].m_pszName ) )
  5813. {
  5814. *out_pResult = searchArray[i];
  5815. return true;
  5816. }
  5817. }
  5818. return false;
  5819. }
  5820. bool CEconItemSchema::BInitPeriodicScoring( KeyValues *pKVPeriodicScoring, CUtlVector<CUtlString> *pVecErrors )
  5821. {
  5822. FOR_EACH_TRUE_SUBKEY( pKVPeriodicScoring, pKVScoreType )
  5823. {
  5824. int index = Q_atoi( pKVScoreType->GetName() );
  5825. SCHEMA_INIT_CHECK( index == m_vecPeriodicScoreTypes.Count(), "Invalid or out-of-order periodic score type '%s'", pKVScoreType->GetName() );
  5826. periodic_score_t PeriodicScore;
  5827. // Reward item definition.
  5828. const char *pszRewardItemDefName = pKVScoreType->GetString( "reward_item_def_name", NULL );
  5829. PeriodicScore.m_pRewardItemDefinition = pszRewardItemDefName ? GetItemDefinitionByName( pszRewardItemDefName ) : NULL;
  5830. SCHEMA_INIT_CHECK( PeriodicScore.m_pRewardItemDefinition, "Periodic score type '%s' missing reward item definition name", pKVScoreType->GetName() );
  5831. // Event type via string lookup.
  5832. const char *pszEventName = pKVScoreType->GetString( "event", "" );
  5833. {
  5834. periodic_score_event_lookup_entry_t EventEntry;
  5835. SCHEMA_INIT_CHECK( LookupValueFromString( sPeriodicScoreEvents, pszEventName, &EventEntry ),
  5836. "Periodic score type '%s' could not find event name '%s'", pKVScoreType->GetName(), pszEventName );
  5837. PeriodicScore.m_eEventType = EventEntry.m_eValue;
  5838. // Note: other parts of the code assume that the event type is associated with the GC-only updatability flag.)
  5839. PeriodicScore.m_bGCUpdateOnly = EventEntry.m_bGCUpdateOnly;
  5840. }
  5841. // Time period via string lookup.
  5842. {
  5843. const char *pszTimePeriodName = pKVScoreType->GetString( "time_period", "" );
  5844. periodic_score_duration_lookup_entry_t DurationEntry;
  5845. SCHEMA_INIT_CHECK( LookupValueFromString( sPeriodicScoreDurations, pszTimePeriodName, &DurationEntry ),
  5846. "Periodic score type '%s' could not find time period name '%s'", pKVScoreType->GetName(), pszEventName );
  5847. PeriodicScore.m_unTimePeriodLengthInSeconds = DurationEntry.m_unValue;
  5848. }
  5849. // Alternate time period specified for use in internal Steam?
  5850. if ( GGCHost()->GetUniverse() != k_EUniversePublic )
  5851. {
  5852. const char *pszInternalTimePeriodName = pKVScoreType->GetString( "time_period_internal", NULL );
  5853. if ( pszInternalTimePeriodName )
  5854. {
  5855. periodic_score_duration_lookup_entry_t InternalDurationEntry;
  5856. SCHEMA_INIT_CHECK( LookupValueFromString( sPeriodicScoreDurations, pszInternalTimePeriodName, &InternalDurationEntry ),
  5857. "Periodic score type '%s' could not find internal time period name '%s'", pKVScoreType->GetName(), pszEventName );
  5858. PeriodicScore.m_unTimePeriodLengthInSeconds = InternalDurationEntry.m_unValue;
  5859. }
  5860. }
  5861. m_vecPeriodicScoreTypes.AddToTail( PeriodicScore );
  5862. }
  5863. return SCHEMA_INIT_SUCCESS();
  5864. }
  5865. #endif // GC_DLL
  5866. //-----------------------------------------------------------------------------
  5867. // Purpose: Initializes the "prefabs" section of the schema
  5868. //-----------------------------------------------------------------------------
  5869. bool CEconItemSchema::BInitDefinitionPrefabs( KeyValues *pKVPrefabs, CUtlVector<CUtlString> *pVecErrors )
  5870. {
  5871. FOR_EACH_TRUE_SUBKEY( pKVPrefabs, pKVPrefab )
  5872. {
  5873. const char *pszPrefabName = pKVPrefab->GetName();
  5874. int nMapIndex = m_mapDefinitionPrefabs.Find( pszPrefabName );
  5875. // Make sure the item index is correct because we use this index as a reference
  5876. SCHEMA_INIT_CHECK(
  5877. !m_mapDefinitionPrefabs.IsValidIndex( nMapIndex ),
  5878. "Duplicate prefab name (%s)", pszPrefabName );
  5879. m_mapDefinitionPrefabs.Insert( pszPrefabName, pKVPrefab->MakeCopy() );
  5880. }
  5881. return SCHEMA_INIT_SUCCESS();
  5882. }
  5883. //-----------------------------------------------------------------------------
  5884. // Purpose: Initializes the Item Series section of the schema
  5885. //-----------------------------------------------------------------------------
  5886. bool CEconItemSchema::BInitItemSeries( KeyValues *pKVSeries, CUtlVector<CUtlString> *pVecErrors )
  5887. {
  5888. // initialize the item definitions
  5889. if ( NULL != pKVSeries)
  5890. {
  5891. FOR_EACH_TRUE_SUBKEY( pKVSeries, pKVItem )
  5892. {
  5893. int nSeriesIndex = pKVItem->GetInt( "value" );
  5894. int nMapIndex = m_mapItemSeries.Find( nSeriesIndex );
  5895. // Make sure the item index is correct because we use this index as a reference
  5896. SCHEMA_INIT_CHECK(
  5897. !m_mapItemSeries.IsValidIndex( nMapIndex ),
  5898. "Duplicate item series value (%d)", nSeriesIndex );
  5899. nMapIndex = m_mapItemSeries.Insert( nMapIndex );
  5900. SCHEMA_INIT_SUBSTEP( m_mapItemSeries[nMapIndex].BInitFromKV( pKVItem, pVecErrors ) );
  5901. }
  5902. }
  5903. return SCHEMA_INIT_SUCCESS();
  5904. }
  5905. //-----------------------------------------------------------------------------
  5906. // Purpose: Initializes the rarity section of the schema
  5907. //-----------------------------------------------------------------------------
  5908. bool CEconItemSchema::BInitRarities( KeyValues *pKVRarities, KeyValues *pKVRarityWeights, CUtlVector<CUtlString> *pVecErrors )
  5909. {
  5910. // initialize the item definitions
  5911. if ( NULL != pKVRarities )
  5912. {
  5913. FOR_EACH_TRUE_SUBKEY( pKVRarities, pKVRarity )
  5914. {
  5915. int nRarityIndex = pKVRarity->GetInt( "value" );
  5916. int nMapIndex = m_mapRarities.Find( nRarityIndex );
  5917. // Make sure the item index is correct because we use this index as a reference
  5918. SCHEMA_INIT_CHECK(
  5919. !m_mapRarities.IsValidIndex( nMapIndex ),
  5920. "Duplicate rarity value (%d)", nRarityIndex );
  5921. nMapIndex = m_mapRarities.Insert( nRarityIndex );
  5922. SCHEMA_INIT_SUBSTEP( m_mapRarities[nMapIndex].BInitFromKV( pKVRarity, pKVRarityWeights, *this, pVecErrors ) );
  5923. }
  5924. }
  5925. return SCHEMA_INIT_SUCCESS();
  5926. }
  5927. //-----------------------------------------------------------------------------
  5928. // Purpose: Initializes the qualities section of the schema
  5929. // Input: pKVQualities - The qualities section of the KeyValues
  5930. // representation of the schema
  5931. // pVecErrors - An optional vector that will contain error messages if
  5932. // the init fails.
  5933. // Output: True if initialization succeeded, false otherwise
  5934. //-----------------------------------------------------------------------------
  5935. bool CEconItemSchema::BInitQualities( KeyValues *pKVQualities, CUtlVector<CUtlString> *pVecErrors )
  5936. {
  5937. // initialize the item definitions
  5938. if ( NULL != pKVQualities )
  5939. {
  5940. FOR_EACH_TRUE_SUBKEY( pKVQualities, pKVQuality )
  5941. {
  5942. int nQualityIndex = pKVQuality->GetInt( "value" );
  5943. int nMapIndex = m_mapQualities.Find( nQualityIndex );
  5944. // Make sure the item index is correct because we use this index as a reference
  5945. SCHEMA_INIT_CHECK(
  5946. !m_mapQualities.IsValidIndex( nMapIndex ),
  5947. "Duplicate quality value (%d)", nQualityIndex );
  5948. nMapIndex = m_mapQualities.Insert( nQualityIndex );
  5949. SCHEMA_INIT_SUBSTEP( m_mapQualities[nMapIndex].BInitFromKV( pKVQuality, pVecErrors ) );
  5950. }
  5951. }
  5952. // Check the integrity of the quality definitions
  5953. // Check for duplicate quality names
  5954. CUtlRBTree<const char *> rbQualityNames( CaselessStringLessThan );
  5955. rbQualityNames.EnsureCapacity( m_mapQualities.Count() );
  5956. FOR_EACH_MAP_FAST( m_mapQualities, i )
  5957. {
  5958. int iIndex = rbQualityNames.Find( m_mapQualities[i].GetName() );
  5959. SCHEMA_INIT_CHECK(
  5960. !rbQualityNames.IsValidIndex( iIndex ),
  5961. "Quality definition %d: Duplicate quality name %s", m_mapQualities[i].GetDBValue(), m_mapQualities[i].GetName() );
  5962. if( !rbQualityNames.IsValidIndex( iIndex ) )
  5963. rbQualityNames.Insert( m_mapQualities[i].GetName() );
  5964. }
  5965. return SCHEMA_INIT_SUCCESS();
  5966. }
  5967. //-----------------------------------------------------------------------------
  5968. // Purpose:
  5969. //-----------------------------------------------------------------------------
  5970. bool CEconItemSchema::BInitColors( KeyValues *pKVColors, CUtlVector<CUtlString> *pVecErrors )
  5971. {
  5972. // initialize the color definitions
  5973. if ( NULL != pKVColors )
  5974. {
  5975. FOR_EACH_TRUE_SUBKEY( pKVColors, pKVColor )
  5976. {
  5977. CEconColorDefinition *pNewColorDef = new CEconColorDefinition;
  5978. SCHEMA_INIT_SUBSTEP( pNewColorDef->BInitFromKV( pKVColor, pVecErrors ) );
  5979. m_vecColorDefs.AddToTail( pNewColorDef );
  5980. }
  5981. }
  5982. return SCHEMA_INIT_SUCCESS();
  5983. }
  5984. //-----------------------------------------------------------------------------
  5985. // Purpose:
  5986. //-----------------------------------------------------------------------------
  5987. int CEconItemSchema::GetEquipRegionIndexByName( const char *pRegionName ) const
  5988. {
  5989. FOR_EACH_VEC( m_vecEquipRegionsList, i )
  5990. {
  5991. const char *szEntryRegionName = m_vecEquipRegionsList[i].m_sName.Get();
  5992. if ( !V_stricmp( szEntryRegionName, pRegionName ) )
  5993. return i;
  5994. }
  5995. return -1;
  5996. }
  5997. //-----------------------------------------------------------------------------
  5998. // Purpose:
  5999. //-----------------------------------------------------------------------------
  6000. equip_region_mask_t CEconItemSchema::GetEquipRegionBitMaskByName( const char *pRegionName ) const
  6001. {
  6002. int iRegionIndex = GetEquipRegionIndexByName( pRegionName );
  6003. if ( !m_vecEquipRegionsList.IsValidIndex( iRegionIndex ) )
  6004. return 0;
  6005. equip_region_mask_t unRegionMask = 1 << m_vecEquipRegionsList[iRegionIndex].m_unBitIndex;
  6006. Assert( unRegionMask > 0 );
  6007. return unRegionMask;
  6008. }
  6009. //-----------------------------------------------------------------------------
  6010. // Purpose:
  6011. //-----------------------------------------------------------------------------
  6012. void CEconItemSchema::SetEquipRegionConflict( int iRegion, unsigned int unBit )
  6013. {
  6014. Assert( m_vecEquipRegionsList.IsValidIndex( iRegion ) );
  6015. equip_region_mask_t unRegionMask = 1 << unBit;
  6016. Assert( unRegionMask > 0 );
  6017. m_vecEquipRegionsList[iRegion].m_unMask |= unRegionMask;
  6018. }
  6019. //-----------------------------------------------------------------------------
  6020. // Purpose:
  6021. //-----------------------------------------------------------------------------
  6022. equip_region_mask_t CEconItemSchema::GetEquipRegionMaskByName( const char *pRegionName ) const
  6023. {
  6024. int iRegionIdx = GetEquipRegionIndexByName( pRegionName );
  6025. if ( iRegionIdx < 0 )
  6026. return 0;
  6027. return m_vecEquipRegionsList[iRegionIdx].m_unMask;
  6028. }
  6029. //-----------------------------------------------------------------------------
  6030. // Purpose:
  6031. //-----------------------------------------------------------------------------
  6032. void CEconItemSchema::AssignDefaultBodygroupState( const char *pszBodygroupName, int iValue )
  6033. {
  6034. // Flip the value passed in -- if we specify in the schema that a region should be off, we assume that it's
  6035. // on by default.
  6036. // actually the schemas are all authored assuming that the default is 0, so let's use that
  6037. int iDefaultValue = 0; //iValue == 0 ? 1 : 0;
  6038. // Make sure that we're constantly reinitializing our default value to the same default value. This is sort
  6039. // of dumb but it works for everything we've got now. In the event that conflicts start cropping up it would
  6040. // be easy enough to make a new schema section.
  6041. int iIndex = m_mapDefaultBodygroupState.Find( pszBodygroupName );
  6042. if ( (m_mapDefaultBodygroupState.IsValidIndex( iIndex ) && m_mapDefaultBodygroupState[iIndex] != iDefaultValue) ||
  6043. (iValue < 0 || iValue > 1) )
  6044. {
  6045. 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 );
  6046. }
  6047. if ( !m_mapDefaultBodygroupState.IsValidIndex( iIndex ) )
  6048. {
  6049. m_mapDefaultBodygroupState.Insert( pszBodygroupName, iDefaultValue );
  6050. }
  6051. }
  6052. //-----------------------------------------------------------------------------
  6053. // Purpose:
  6054. //-----------------------------------------------------------------------------
  6055. bool CEconItemSchema::BInitEquipRegions( KeyValues *pKVEquipRegions, CUtlVector<CUtlString> *pVecErrors )
  6056. {
  6057. CUtlVector<const char *> vecNames;
  6058. FOR_EACH_SUBKEY( pKVEquipRegions, pKVRegion )
  6059. {
  6060. const char *pRegionKeyName = pKVRegion->GetName();
  6061. vecNames.Purge();
  6062. // The "shared" name is special for equip regions -- it means that all of the sub-regions specified
  6063. // will use the same bit to store equipped-or-not data, but that one bit can be accessed by a whole
  6064. // bunch of different names. This is useful in TF where different classes have different regions, but
  6065. // those regions cannot possibly conflict with each other. For example, "scout_backpack" cannot possibly
  6066. // overlap with "pyro_shoulder" because they can't even be equipped on the same character.
  6067. if ( pRegionKeyName && !Q_stricmp( pRegionKeyName, "shared" ) )
  6068. {
  6069. FOR_EACH_SUBKEY( pKVRegion, pKVSharedRegionName )
  6070. {
  6071. vecNames.AddToTail( pKVSharedRegionName->GetName() );
  6072. }
  6073. }
  6074. // We have a standard name -- this one entry is its own equip region.
  6075. else
  6076. {
  6077. vecNames.AddToTail( pRegionKeyName );
  6078. }
  6079. // What bit will this equip region use to mask against conflicts? If we don't have any equip regions
  6080. // at all, we'll use the base bit, otherwise we just grab one higher than whatever we used last.
  6081. unsigned int unNewBitIndex = m_vecEquipRegionsList.Count() <= 0 ? 0 : m_vecEquipRegionsList.Tail().m_unBitIndex + 1;
  6082. FOR_EACH_VEC( vecNames, i )
  6083. {
  6084. const char *pRegionName = vecNames[i];
  6085. // Make sure this name is unique.
  6086. if ( GetEquipRegionIndexByName( pRegionName ) >= 0 )
  6087. {
  6088. pVecErrors->AddToTail( CFmtStr( "Duplicate equip region named \"%s\".", pRegionName ).Access() );
  6089. continue;
  6090. }
  6091. // Make a new region.
  6092. EquipRegion newEquipRegion;
  6093. newEquipRegion.m_sName = pRegionName;
  6094. newEquipRegion.m_unMask = 0; // we'll update this mask later
  6095. newEquipRegion.m_unBitIndex = unNewBitIndex;
  6096. int iIdx = m_vecEquipRegionsList.AddToTail( newEquipRegion );
  6097. // Tag this region to conflict with itself so that if nothing else two items in the same
  6098. // region can't equip over each other.
  6099. SetEquipRegionConflict( iIdx, unNewBitIndex );
  6100. }
  6101. }
  6102. return SCHEMA_INIT_SUCCESS();
  6103. }
  6104. //-----------------------------------------------------------------------------
  6105. // Purpose:
  6106. //-----------------------------------------------------------------------------
  6107. bool CEconItemSchema::BInitEquipRegionConflicts( KeyValues *pKVEquipRegionConflicts, CUtlVector<CUtlString> *pVecErrors )
  6108. {
  6109. FOR_EACH_TRUE_SUBKEY( pKVEquipRegionConflicts, pKVConflict )
  6110. {
  6111. // What region is the base of this conflict?
  6112. const char *pRegionName = pKVConflict->GetName();
  6113. int iRegionIdx = GetEquipRegionIndexByName( pRegionName );
  6114. if ( iRegionIdx < 0 )
  6115. {
  6116. pVecErrors->AddToTail( CFmtStr( "Unable to find base equip region named \"%s\" for conflicts.", pRegionName ).Access() );
  6117. continue;
  6118. }
  6119. FOR_EACH_SUBKEY( pKVConflict, pKVConflictOther )
  6120. {
  6121. const char *pOtherRegionName = pKVConflictOther->GetName();
  6122. int iOtherRegionIdx = GetEquipRegionIndexByName( pOtherRegionName );
  6123. if ( iOtherRegionIdx < 0 )
  6124. {
  6125. pVecErrors->AddToTail( CFmtStr( "Unable to find other equip region named \"%s\" for conflicts.", pOtherRegionName ).Access() );
  6126. continue;
  6127. }
  6128. SetEquipRegionConflict( iRegionIdx, m_vecEquipRegionsList[iOtherRegionIdx].m_unBitIndex );
  6129. SetEquipRegionConflict( iOtherRegionIdx, m_vecEquipRegionsList[iRegionIdx].m_unBitIndex );
  6130. }
  6131. }
  6132. return SCHEMA_INIT_SUCCESS();
  6133. }
  6134. //-----------------------------------------------------------------------------
  6135. // Purpose: Initializes the attributes section of the schema
  6136. // Input: pKVAttributes - The attributes section of the KeyValues
  6137. // representation of the schema
  6138. // pVecErrors - An optional vector that will contain error messages if
  6139. // the init fails.
  6140. // Output: True if initialization succeeded, false otherwise
  6141. //-----------------------------------------------------------------------------
  6142. bool CEconItemSchema::BInitAttributes( KeyValues *pKVAttributes, CUtlVector<CUtlString> *pVecErrors )
  6143. {
  6144. // Initialize the attribute definitions
  6145. FOR_EACH_TRUE_SUBKEY( pKVAttributes, pKVAttribute )
  6146. {
  6147. int nAttrIndex = Q_atoi( pKVAttribute->GetName() );
  6148. int nMapIndex = m_mapAttributes.Find( nAttrIndex );
  6149. // Make sure the index is positive
  6150. SCHEMA_INIT_CHECK(
  6151. nAttrIndex >= 0,
  6152. "Attribute definition index %d must be greater than or equal to zero", nAttrIndex );
  6153. // Make sure the attribute index is not repeated
  6154. SCHEMA_INIT_CHECK(
  6155. !m_mapAttributes.IsValidIndex( nMapIndex ),
  6156. "Duplicate attribute definition index (%d)", nAttrIndex );
  6157. nMapIndex = m_mapAttributes.Insert( nAttrIndex );
  6158. SCHEMA_INIT_SUBSTEP( m_mapAttributes[nMapIndex].BInitFromKV( pKVAttribute, pVecErrors ) );
  6159. }
  6160. // Check the integrity of the attribute definitions
  6161. // Check for duplicate attribute definition names
  6162. CUtlRBTree<const char *> rbAttributeNames( CaselessStringLessThan );
  6163. rbAttributeNames.EnsureCapacity( m_mapAttributes.Count() );
  6164. FOR_EACH_MAP_FAST( m_mapAttributes, i )
  6165. {
  6166. int iIndex = rbAttributeNames.Find( m_mapAttributes[i].GetDefinitionName() );
  6167. SCHEMA_INIT_CHECK(
  6168. !rbAttributeNames.IsValidIndex( iIndex ),
  6169. "Attribute definition %d: Duplicate name \"%s\"", m_mapAttributes.Key( i ), m_mapAttributes[i].GetDefinitionName() );
  6170. if( !rbAttributeNames.IsValidIndex( iIndex ) )
  6171. rbAttributeNames.Insert( m_mapAttributes[i].GetDefinitionName() );
  6172. }
  6173. return SCHEMA_INIT_SUCCESS();
  6174. }
  6175. //-----------------------------------------------------------------------------
  6176. // Purpose: Initializes the items section of the schema
  6177. // Input: pKVItems - The items section of the KeyValues
  6178. // representation of the schema
  6179. // pVecErrors - An optional vector that will contain error messages if
  6180. // the init fails.
  6181. // Output: True if initialization succeeded, false otherwise
  6182. //-----------------------------------------------------------------------------
  6183. bool CEconItemSchema::BInitItems( KeyValues *pKVItems, CUtlVector<CUtlString> *pVecErrors )
  6184. {
  6185. m_mapItems.PurgeAndDeleteElements();
  6186. m_mapItemsSorted.Purge();
  6187. m_mapToolsItems.Purge();
  6188. m_mapBaseItems.Purge();
  6189. m_vecBundles.Purge();
  6190. m_mapQuestObjectives.PurgeAndDeleteElements();
  6191. m_vecItemCollectionCrates.Purge();
  6192. #if defined(CLIENT_DLL) || defined(GAME_DLL)
  6193. if ( m_pDefaultItemDefinition )
  6194. {
  6195. delete m_pDefaultItemDefinition;
  6196. m_pDefaultItemDefinition = NULL;
  6197. }
  6198. #endif
  6199. // initialize the item definitions
  6200. if ( NULL != pKVItems )
  6201. {
  6202. FOR_EACH_TRUE_SUBKEY( pKVItems, pKVItem )
  6203. {
  6204. if ( Q_stricmp( pKVItem->GetName(), "default" ) == 0 )
  6205. {
  6206. #if defined(CLIENT_DLL) || defined(GAME_DLL)
  6207. SCHEMA_INIT_CHECK(
  6208. m_pDefaultItemDefinition == NULL,
  6209. "Duplicate 'default' item definition." );
  6210. m_pDefaultItemDefinition = CreateEconItemDefinition();
  6211. SCHEMA_INIT_SUBSTEP( m_pDefaultItemDefinition->BInitFromKV( pKVItem, pVecErrors ) );
  6212. #endif
  6213. }
  6214. else
  6215. {
  6216. int nItemIndex = Q_atoi( pKVItem->GetName() );
  6217. int nMapIndex = m_mapItems.Find( nItemIndex );
  6218. // Make sure the item index is correct because we use this index as a reference
  6219. SCHEMA_INIT_CHECK(
  6220. !m_mapItems.IsValidIndex( nMapIndex ),
  6221. "Duplicate item definition (%d)", nItemIndex );
  6222. // Check to make sure the index is positive
  6223. SCHEMA_INIT_CHECK(
  6224. nItemIndex >= 0,
  6225. "Item definition index %d must be greater than or equal to zero", nItemIndex );
  6226. CEconItemDefinition *pItemDef = CreateEconItemDefinition();
  6227. nMapIndex = m_mapItems.Insert( nItemIndex, pItemDef );
  6228. m_mapItemsSorted.Insert( nItemIndex, pItemDef );
  6229. SCHEMA_INIT_SUBSTEP( m_mapItems[nMapIndex]->BInitFromKV( pKVItem, pVecErrors ) );
  6230. // Cache off Tools references
  6231. if ( pItemDef->IsTool() )
  6232. {
  6233. m_mapToolsItems.Insert( nItemIndex, pItemDef );
  6234. }
  6235. if ( pItemDef->IsBaseItem() )
  6236. {
  6237. m_mapBaseItems.Insert( nItemIndex, pItemDef );
  6238. }
  6239. // Cache off bundles for the link phase below.
  6240. if ( pItemDef->IsBundle() )
  6241. {
  6242. // Cache off the item def for the bundle, since we'll need both the bundle info and the item def index later.
  6243. m_vecBundles.AddToTail( pItemDef );
  6244. // If the bundle is a pack bundle, mark all the contained items as pack items / link to the owning pack bundle
  6245. if ( pItemDef->IsPackBundle() )
  6246. {
  6247. const bundleinfo_t *pBundleInfo = pItemDef->GetBundleInfo();
  6248. FOR_EACH_VEC( pBundleInfo->vecItemDefs, iCurItem )
  6249. {
  6250. CEconItemDefinition *pCurItemDef = pBundleInfo->vecItemDefs[ iCurItem ];
  6251. SCHEMA_INIT_CHECK( NULL == pCurItemDef->m_pOwningPackBundle, "Pack item \"%s\" included in more than one pack bundle - not allowed!", pCurItemDef->GetDefinitionName() );
  6252. pCurItemDef->m_pOwningPackBundle = pItemDef;
  6253. }
  6254. }
  6255. }
  6256. static CSchemaAttributeDefHandle pAttrDef_ContainsCollection( "contains collection" );
  6257. if ( pAttrDef_ContainsCollection )
  6258. {
  6259. FOR_EACH_VEC( pItemDef->GetStaticAttributes(), i )
  6260. {
  6261. const static_attrib_t& staticAttrib = pItemDef->GetStaticAttributes()[i];
  6262. if ( staticAttrib.iDefIndex == pAttrDef_ContainsCollection->GetDefinitionIndex() )
  6263. {
  6264. // Add to collection crate list
  6265. m_vecItemCollectionCrates.AddToTail( pItemDef->GetDefinitionIndex() );
  6266. }
  6267. }
  6268. }
  6269. }
  6270. }
  6271. }
  6272. // Check the integrity of the item definitions
  6273. CUtlRBTree<const char *> rbItemNames( CaselessStringLessThan );
  6274. rbItemNames.EnsureCapacity( m_mapItems.Count() );
  6275. FOR_EACH_MAP_FAST( m_mapItems, i )
  6276. {
  6277. CEconItemDefinition *pItemDef = m_mapItems[ i ];
  6278. // Check for duplicate item definition names
  6279. int iIndex = rbItemNames.Find( pItemDef->GetDefinitionName() );
  6280. SCHEMA_INIT_CHECK(
  6281. !rbItemNames.IsValidIndex( iIndex ),
  6282. "Item definition %s: Duplicate name on index %d", pItemDef->GetDefinitionName(), m_mapItems.Key( i ) );
  6283. if( !rbItemNames.IsValidIndex( iIndex ) )
  6284. rbItemNames.Insert( m_mapItems[i]->GetDefinitionName() );
  6285. // Link up armory and store mappings for the item
  6286. SCHEMA_INIT_SUBSTEP( pItemDef->BInitItemMappings( pVecErrors ) );
  6287. }
  6288. #ifdef DOTA
  6289. // 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
  6290. FOR_EACH_VEC( m_vecBundles, iBundle )
  6291. {
  6292. const CEconItemDefinition *pBundleItemDef = m_vecBundles[ iBundle ];
  6293. if ( pBundleItemDef->IsPackBundle() )
  6294. continue;
  6295. // Go through all items in the bundle and look for pack items
  6296. const bundleinfo_t *pBundle = pBundleItemDef->GetBundleInfo();
  6297. if ( pBundle )
  6298. {
  6299. FOR_EACH_VEC( pBundle->vecItemDefs, iContainedBundleItem )
  6300. {
  6301. // Get the associated pack bundle
  6302. const CEconItemDefinition *pContainedBundleItemDef = pBundle->vecItemDefs[ iContainedBundleItem ];
  6303. // Ignore non-pack items
  6304. if ( !pContainedBundleItemDef || !pContainedBundleItemDef->IsPackItem() )
  6305. continue;
  6306. // Get the pack bundle that contains this particular pack item
  6307. const CEconItemDefinition *pOwningPackBundleItemDef = pContainedBundleItemDef->GetOwningPackBundle();
  6308. // Make sure all items in the owning pack bundle are in pBundleItemDef's bundle info (pBundle)
  6309. const bundleinfo_t *pOwningPackBundle = pOwningPackBundleItemDef->GetBundleInfo();
  6310. FOR_EACH_VEC( pOwningPackBundle->vecItemDefs, iCurPackBundleItem )
  6311. {
  6312. CEconItemDefinition *pCurPackBundleItem = pOwningPackBundle->vecItemDefs[ iCurPackBundleItem ];
  6313. if ( !pBundle->vecItemDefs.HasElement( pCurPackBundleItem ) )
  6314. {
  6315. SCHEMA_INIT_CHECK(
  6316. false,
  6317. "The bundle \"%s\" contains some, but not all pack items required specified by pack bundle \"%s.\"",
  6318. pBundleItemDef->GetDefinitionName(),
  6319. pOwningPackBundleItemDef->GetDefinitionName()
  6320. );
  6321. }
  6322. }
  6323. }
  6324. }
  6325. }
  6326. #endif
  6327. return SCHEMA_INIT_SUCCESS();
  6328. }
  6329. #if 0 // Compiled out until some DotA changes from the item editor are brought over
  6330. //-----------------------------------------------------------------------------
  6331. // Purpose: Delete an item definition. Moderately dangerous as cached references will become bad.
  6332. // Intended for use by the item editor.
  6333. //-----------------------------------------------------------------------------
  6334. bool CEconItemSchema::DeleteItemDefinition( int iDefIndex )
  6335. {
  6336. m_mapItemsSorted.Remove( iDefIndex );
  6337. int nMapIndex = m_mapItems.Find( iDefIndex );
  6338. if ( m_mapItems.IsValidIndex( nMapIndex ) )
  6339. {
  6340. CEconItemDefinition* pItemDef = m_mapItems[nMapIndex];
  6341. if ( pItemDef )
  6342. {
  6343. m_mapItems.RemoveAt( nMapIndex );
  6344. delete pItemDef;
  6345. return true;
  6346. }
  6347. }
  6348. return false;
  6349. }
  6350. #endif
  6351. //-----------------------------------------------------------------------------
  6352. // Purpose: Parses the Item Sets section.
  6353. //-----------------------------------------------------------------------------
  6354. bool CEconItemSchema::BInitItemSets( KeyValues *pKVItemSets, CUtlVector<CUtlString> *pVecErrors )
  6355. {
  6356. m_mapItemSets.RemoveAll();
  6357. if ( NULL != pKVItemSets )
  6358. {
  6359. FOR_EACH_TRUE_SUBKEY( pKVItemSets, pKVItemSet )
  6360. {
  6361. const char* setName = pKVItemSet->GetName();
  6362. SCHEMA_INIT_CHECK( setName != NULL, "All itemsets must have names." );
  6363. SCHEMA_INIT_CHECK( m_mapItemSets.Find( setName ) == m_mapItemSets.InvalidIndex(), "Duplicate itemset name (%s) found!", setName );
  6364. int idx = m_mapItemSets.Insert( setName, new CEconItemSetDefinition );
  6365. SCHEMA_INIT_SUBSTEP( m_mapItemSets[idx]->BInitFromKV( pKVItemSet, pVecErrors ) );
  6366. }
  6367. // Once we've initialized all of our item sets, loop through all of our item definitions looking
  6368. // for pseudo set items. For example, the Festive Holy Mackerel is a different item definition from
  6369. // the regular Holy Mackerel, but for set completion and set listing purposes, we want it to show
  6370. // as part of the base set.
  6371. FOR_EACH_MAP_FAST( m_mapItems, i )
  6372. {
  6373. CEconItemDefinition *pItemDef = m_mapItems[i];
  6374. Assert( pItemDef );
  6375. // Items that point to themselves are the base set items and got initialized as part of the
  6376. // set initialization above.
  6377. if ( pItemDef->GetSetItemRemap() == pItemDef->GetDefinitionIndex() )
  6378. continue;
  6379. // Which item are we stealing set information from?
  6380. const CEconItemDefinition *pRemappedSetItemDef = GetItemDefinition( pItemDef->GetSetItemRemap() );
  6381. AssertMsg( pRemappedSetItemDef, "Somehow got through item and set initialization but have a broken set remap item!" );
  6382. pItemDef->SetItemSetDefinition( pRemappedSetItemDef->GetItemSetDefinition() );
  6383. }
  6384. }
  6385. return SCHEMA_INIT_SUCCESS();
  6386. }
  6387. //-----------------------------------------------------------------------------
  6388. bool CEconItemSchema::BVerifyBaseItemNames( CUtlVector<CUtlString> *pVecErrors )
  6389. {
  6390. FOR_EACH_MAP_FAST( m_mapItems, i )
  6391. {
  6392. CEconItemDefinition *pItemDef = m_mapItems[i];
  6393. // get base item name
  6394. const char* pBaseName = pItemDef->GetBaseFunctionalItemName();
  6395. if ( !pBaseName || pBaseName[0] == '\0' )
  6396. {
  6397. continue;
  6398. }
  6399. // look up base item name
  6400. SCHEMA_INIT_CHECK( GetItemDefinitionByName( pBaseName ) != NULL, "Base item name not found %s.", pBaseName );
  6401. }
  6402. return SCHEMA_INIT_SUCCESS();
  6403. }
  6404. //-----------------------------------------------------------------------------
  6405. bool CEconItemSchema::BInitItemCollections( KeyValues *pKVItemCollections, CUtlVector<CUtlString> *pVecErrors )
  6406. {
  6407. m_mapItemCollections.Purge();
  6408. if ( NULL != pKVItemCollections )
  6409. {
  6410. FOR_EACH_TRUE_SUBKEY( pKVItemCollections, pKVItemCollection )
  6411. {
  6412. const char* setName = pKVItemCollection->GetName();
  6413. SCHEMA_INIT_CHECK( setName != NULL, "All item collections must have names." );
  6414. SCHEMA_INIT_CHECK( m_mapItemCollections.Find( setName ) == m_mapItemCollections.InvalidIndex(), "Duplicate item collection name (%s) found!", setName );
  6415. int idx = m_mapItemCollections.Insert( setName, new CEconItemCollectionDefinition );
  6416. SCHEMA_INIT_SUBSTEP( m_mapItemCollections[idx]->BInitFromKV( pKVItemCollection, pVecErrors ) );
  6417. }
  6418. }
  6419. return SCHEMA_INIT_SUCCESS();
  6420. }
  6421. //-----------------------------------------------------------------------------
  6422. bool CEconItemSchema::BInitCollectionReferences( CUtlVector<CUtlString> *pVecErrors )
  6423. {
  6424. FOR_EACH_MAP_FAST( m_mapItems, i )
  6425. {
  6426. CEconItemDefinition *pItemDef = m_mapItems[i];
  6427. const char *pszCollectionName = pItemDef->GetCollectionReference();
  6428. if ( pszCollectionName )
  6429. {
  6430. // Find the collection
  6431. bool bFound = false;
  6432. FOR_EACH_MAP_FAST( m_mapItemCollections, iCollectionIndex )
  6433. {
  6434. const char * pszTemp = m_mapItemCollections[iCollectionIndex]->m_pszName;
  6435. if ( !V_strcmp( pszTemp, pszCollectionName) )
  6436. {
  6437. bFound = true;
  6438. pItemDef->SetItemCollectionDefinition( m_mapItemCollections[iCollectionIndex] );
  6439. break;
  6440. }
  6441. }
  6442. SCHEMA_INIT_CHECK( bFound == true, "Collection %s referenced by item %s not found", pszCollectionName, pItemDef->GetDefinitionName() );
  6443. }
  6444. }
  6445. return SCHEMA_INIT_SUCCESS();
  6446. }
  6447. //-----------------------------------------------------------------------------
  6448. const CEconItemCollectionDefinition *CEconItemSchema::GetCollectionByName( const char* pCollectionName )
  6449. {
  6450. if ( !pCollectionName )
  6451. return NULL;
  6452. FOR_EACH_MAP_FAST( m_mapItemCollections, iCollectionIndex )
  6453. {
  6454. const char * pszTemp = m_mapItemCollections[iCollectionIndex]->m_pszName;
  6455. if ( !V_strcmp( pszTemp, pCollectionName ) )
  6456. {
  6457. return m_mapItemCollections[iCollectionIndex];
  6458. }
  6459. }
  6460. return NULL;
  6461. }
  6462. //-----------------------------------------------------------------------------
  6463. bool CEconItemSchema::BInitItemPaintKitDefinitions( KeyValues *pKVItemPaintKits, CUtlVector<CUtlString> *pVecErrors )
  6464. {
  6465. m_mapItemPaintKits.Purge();
  6466. const char* cWhitespace = " \r\n\t"; // space, end of line, tab.
  6467. cWhitespace; // Compiler happiness for GC build
  6468. if ( NULL != pKVItemPaintKits )
  6469. {
  6470. #ifdef CLIENT_DLL
  6471. FOR_EACH_TRUE_SUBKEY( pKVItemPaintKits, pKVPaintKit )
  6472. {
  6473. const char* keyField = pKVPaintKit->GetName();
  6474. SCHEMA_INIT_CHECK( keyField != NULL, "All item collections must have names." );
  6475. if ( V_stristr( keyField, "paintkit_template" ) != NULL )
  6476. {
  6477. static const int cSkipLen = strlen( "paintkit_template" );
  6478. keyField += cSkipLen;
  6479. keyField += strspn( keyField, cWhitespace );
  6480. bool createTmplResult = materials->AddTextureCompositorTemplate( keyField, pKVPaintKit );
  6481. SCHEMA_INIT_CHECK( createTmplResult, "Could Not Create paintkit_template '%s'", keyField );
  6482. }
  6483. }
  6484. // Do post-load validation before moving on to paintkits.
  6485. SCHEMA_INIT_CHECK( materials->VerifyTextureCompositorTemplates(), "Paintkit template post-init validation failed." );
  6486. #endif
  6487. // Now do all the paintkits
  6488. FOR_EACH_TRUE_SUBKEY( pKVItemPaintKits, pKVPaintKit )
  6489. {
  6490. // We know the keyField is valid, it was checked above.
  6491. const char* keyField = pKVPaintKit->GetName();
  6492. if ( V_stristr( keyField, "paintkit_template" ) == NULL )
  6493. {
  6494. SCHEMA_INIT_CHECK( m_mapItemPaintKits.Find( keyField ) == m_mapItemPaintKits.InvalidIndex(), "Duplicate paint kit definition name (%s) found!", keyField );
  6495. int idx = m_mapItemPaintKits.Insert( keyField, new CEconItemPaintKitDefinition );
  6496. SCHEMA_INIT_SUBSTEP( m_mapItemPaintKits[ idx ]->BInitFromKV( pKVPaintKit, pVecErrors ) );
  6497. }
  6498. }
  6499. }
  6500. return SCHEMA_INIT_SUCCESS();
  6501. }
  6502. //-----------------------------------------------------------------------------
  6503. bool CEconItemSchema::BInitOperationDefinitions( KeyValues *pKVGameInfo, KeyValues *pKVOperationDefinitions, CUtlVector<CUtlString> *pVecErrors )
  6504. {
  6505. m_mapOperationDefinitions.Purge();
  6506. if ( NULL != pKVOperationDefinitions )
  6507. {
  6508. FOR_EACH_TRUE_SUBKEY( pKVOperationDefinitions, pKVOperation )
  6509. {
  6510. const char* setName = pKVOperation->GetName();
  6511. SCHEMA_INIT_CHECK( setName != NULL, "All operations must have names." );
  6512. SCHEMA_INIT_CHECK( m_mapOperationDefinitions.Find( setName ) == m_mapOperationDefinitions.InvalidIndex(), "Duplicate operation definition name (%s) found!", setName );
  6513. CEconOperationDefinition *pNewOperation = new CEconOperationDefinition();
  6514. SCHEMA_INIT_SUBSTEP( pNewOperation->BInitFromKV( pKVOperation, pVecErrors ) );
  6515. // don't add expired operation to list
  6516. if ( pNewOperation->IsExpired() )
  6517. {
  6518. delete pNewOperation;
  6519. continue;
  6520. }
  6521. m_mapOperationDefinitions.Insert( setName, pNewOperation );
  6522. }
  6523. }
  6524. return SCHEMA_INIT_SUCCESS();
  6525. }
  6526. //-----------------------------------------------------------------------------
  6527. // Purpose: Initializes the timed rewards section of the schema
  6528. // Input: pKVTimedRewards - The timed_rewards section of the KeyValues
  6529. // representation of the schema
  6530. // pVecErrors - An optional vector that will contain error messages if
  6531. // the init fails.
  6532. // Output: True if initialization succeeded, false otherwise
  6533. //-----------------------------------------------------------------------------
  6534. bool CEconItemSchema::BInitTimedRewards( KeyValues *pKVTimedRewards, CUtlVector<CUtlString> *pVecErrors )
  6535. {
  6536. m_vecTimedRewards.RemoveAll();
  6537. // initialize the rewards sections
  6538. if ( NULL != pKVTimedRewards )
  6539. {
  6540. FOR_EACH_TRUE_SUBKEY( pKVTimedRewards, pKVTimedReward )
  6541. {
  6542. int index = m_vecTimedRewards.AddToTail();
  6543. SCHEMA_INIT_SUBSTEP( m_vecTimedRewards[index].BInitFromKV( pKVTimedReward, pVecErrors ) );
  6544. }
  6545. }
  6546. return SCHEMA_INIT_SUCCESS();
  6547. }
  6548. //-----------------------------------------------------------------------------
  6549. // Purpose:
  6550. //-----------------------------------------------------------------------------
  6551. const CTimedItemRewardDefinition* CEconItemSchema::GetTimedReward( eTimedRewardType type ) const
  6552. {
  6553. if ( (int)type < m_vecTimedRewards.Count() )
  6554. {
  6555. return &m_vecTimedRewards[type];
  6556. }
  6557. return NULL;
  6558. }
  6559. //-----------------------------------------------------------------------------
  6560. // Purpose: Initializes the loot lists section of the schema
  6561. //-----------------------------------------------------------------------------
  6562. bool CEconItemSchema::BInitLootLists( KeyValues *pKVLootLists, CUtlVector<CUtlString> *pVecErrors )
  6563. {
  6564. if ( NULL != pKVLootLists )
  6565. {
  6566. FOR_EACH_TRUE_SUBKEY( pKVLootLists, pKVLootList )
  6567. {
  6568. const char* pListName = pKVLootList->GetName();
  6569. SCHEMA_INIT_SUBSTEP( BInsertLootlist( pListName, pKVLootList, pVecErrors ) );
  6570. }
  6571. }
  6572. FOR_EACH_MAP_FAST( m_mapLootLists, i )
  6573. {
  6574. const CEconLootListDefinition *pLootList = m_mapLootLists[i];
  6575. BVerifyLootListItemDropDates( pLootList, pVecErrors );
  6576. }
  6577. return SCHEMA_INIT_SUCCESS();
  6578. }
  6579. //-----------------------------------------------------------------------------
  6580. bool CEconItemSchema::BInsertLootlist( const char *pListName, KeyValues *pKVLootList, CUtlVector<CUtlString> *pVecErrors )
  6581. {
  6582. SCHEMA_INIT_CHECK( pListName != NULL, "All lootlists must have names." );
  6583. if ( m_mapLootLists.Count() > 0 )
  6584. {
  6585. SCHEMA_INIT_CHECK( GetLootListByName( pListName ) == NULL, "Duplicate lootlist name (%s) found!", pListName );
  6586. }
  6587. CEconLootListDefinition *pLootList = new CEconLootListDefinition;
  6588. SCHEMA_INIT_SUBSTEP( pLootList->BInitFromKV( pKVLootList, *this, pVecErrors ) );
  6589. m_mapLootLists.Insert( pListName, pLootList );
  6590. return SCHEMA_INIT_SUCCESS();
  6591. }
  6592. //-----------------------------------------------------------------------------
  6593. // Purpose: Initializes the revolving loot lists section of the schema
  6594. //-----------------------------------------------------------------------------
  6595. bool CEconItemSchema::BInitRevolvingLootLists( KeyValues *pKVLootLists, CUtlVector<CUtlString> *pVecErrors )
  6596. {
  6597. m_mapRevolvingLootLists.RemoveAll();
  6598. if ( NULL != pKVLootLists )
  6599. {
  6600. FOR_EACH_SUBKEY( pKVLootLists, pKVList )
  6601. {
  6602. int iListIdx = pKVList->GetInt();
  6603. const char* strListName = pKVList->GetName();
  6604. m_mapRevolvingLootLists.Insert( iListIdx, strListName );
  6605. }
  6606. }
  6607. FOR_EACH_MAP_FAST( m_mapRevolvingLootLists, i )
  6608. {
  6609. const CEconLootListDefinition* pLootList = GetLootListByName(m_mapRevolvingLootLists[i]);
  6610. BVerifyLootListItemDropDates( pLootList, pVecErrors );
  6611. }
  6612. return SCHEMA_INIT_SUCCESS();
  6613. }
  6614. //-----------------------------------------------------------------------------
  6615. // Purpose: Create and return a new quest objective definition. Verify that
  6616. // a definition with the same name doesnt alreay exist.
  6617. //-----------------------------------------------------------------------------
  6618. bool CEconItemSchema::AddQuestObjective( const CQuestObjectiveDefinition **ppQuestObjective, KeyValues *pKVObjective, CUtlVector<CUtlString> *pVecErrors )
  6619. {
  6620. // These need to be unique
  6621. int nDefIndex = pKVObjective->GetInt( "defindex", -1 );
  6622. SCHEMA_INIT_CHECK( nDefIndex != -1, "Missing defindex for quest objective" );
  6623. // Verify defindex is unique
  6624. auto nMapIndex = m_mapQuestObjectives.Find( nDefIndex );
  6625. SCHEMA_INIT_CHECK( nMapIndex == m_mapQuestObjectives.InvalidIndex(), "Multiple quest objectives with defindex: %d", nDefIndex );
  6626. // Create the quest def
  6627. nMapIndex = m_mapQuestObjectives.Insert( nDefIndex );
  6628. m_mapQuestObjectives[ nMapIndex ] = CreateQuestDefinition();
  6629. // Init
  6630. SCHEMA_INIT_SUBSTEP( m_mapQuestObjectives[nMapIndex]->BInitFromKV( pKVObjective, pVecErrors ) );
  6631. if ( ppQuestObjective )
  6632. {
  6633. (*ppQuestObjective) = m_mapQuestObjectives[nMapIndex];
  6634. }
  6635. return SCHEMA_INIT_SUCCESS();
  6636. }
  6637. //-----------------------------------------------------------------------------
  6638. // Purpose: Verify that the contents of visible lootlist do not have drop dates
  6639. // associated with them. The thinking being that we dont want to have
  6640. // items listed that could potentially not drop, or items disappear/appear
  6641. // in from a list.
  6642. //-----------------------------------------------------------------------------
  6643. bool CEconItemSchema::BVerifyLootListItemDropDates( const CEconLootListDefinition* pLootList, CUtlVector<CUtlString> *pVecErrors ) const
  6644. {
  6645. if ( pLootList && pLootList->BPublicListContents() )
  6646. {
  6647. BRecurseiveVerifyLootListItemDropDates( pLootList, pLootList, pVecErrors );
  6648. }
  6649. return SCHEMA_INIT_SUCCESS();
  6650. }
  6651. //-----------------------------------------------------------------------------
  6652. // Purpose: Recursively dig through all entries in the passed in lootlist to see
  6653. // if any of the containted items have drop dates.
  6654. //-----------------------------------------------------------------------------
  6655. bool CEconItemSchema::BRecurseiveVerifyLootListItemDropDates( const CEconLootListDefinition* pLootList, const CEconLootListDefinition* pRootLootList, CUtlVector<CUtlString> *pVecErrors ) const
  6656. {
  6657. FOR_EACH_VEC( pLootList->GetLootListContents(), j )
  6658. {
  6659. const CEconLootListDefinition::drop_item_t& item = pLootList->GetLootListContents()[j];
  6660. // 0 and greater means item. Less than 0 means nested lootlist
  6661. if( item.m_iItemOrLootlistDef >= 0 )
  6662. {
  6663. const CEconItemDefinition* pItemDef = GetItemSchema()->GetItemDefinition( item.m_iItemOrLootlistDef );
  6664. if( pItemDef )
  6665. {
  6666. static CSchemaAttributeDefHandle pAttribDef_StartDropDate( "start drop date" );
  6667. static CSchemaAttributeDefHandle pAttribDef_EndDropDate( "end drop date" );
  6668. CAttribute_String value;
  6669. // Check for start drop date attribute on this item
  6670. SCHEMA_INIT_CHECK( !FindAttribute( pItemDef, pAttribDef_StartDropDate, &value ),
  6671. "Lootlist \"%s\" contains lootlist \"%s\", which contains item \"%s\", which has start drop date.", pRootLootList->GetName(), pLootList->GetName(), pItemDef->GetDefinitionName() );
  6672. // Check for end drop date attribute on this item
  6673. SCHEMA_INIT_CHECK( !FindAttribute( pItemDef, pAttribDef_EndDropDate, &value ),
  6674. "Lootlist \"%s\" contains lootlist \"%s\", which contains item \"%s\", which has end drop date.", pRootLootList->GetName(), pLootList->GetName(), pItemDef->GetDefinitionName() );
  6675. }
  6676. }
  6677. else
  6678. {
  6679. // Get the nested lootlist
  6680. int iLLIndex = (item.m_iItemOrLootlistDef * -1) - 1;
  6681. const CEconLootListDefinition *pNestedLootList = GetItemSchema()->GetLootListByIndex( iLLIndex );
  6682. if ( !pNestedLootList )
  6683. return SCHEMA_INIT_SUCCESS();
  6684. // Dig through all of this lootlist's entries
  6685. BRecurseiveVerifyLootListItemDropDates( pNestedLootList, pRootLootList, pVecErrors );
  6686. }
  6687. }
  6688. return SCHEMA_INIT_SUCCESS();
  6689. }
  6690. //-----------------------------------------------------------------------------
  6691. // Purpose: Initializes the recipes section of the schema
  6692. // Input: pKVRecipes - The recipes section of the KeyValues
  6693. // representation of the schema
  6694. // pVecErrors - An optional vector that will contain error messages if
  6695. // the init fails.
  6696. // Output: True if initialization succeeded, false otherwise
  6697. //-----------------------------------------------------------------------------
  6698. bool CEconItemSchema::BInitRecipes( KeyValues *pKVRecipes, CUtlVector<CUtlString> *pVecErrors )
  6699. {
  6700. m_mapRecipes.RemoveAll();
  6701. // initialize the rewards sections
  6702. if ( NULL != pKVRecipes )
  6703. {
  6704. FOR_EACH_TRUE_SUBKEY( pKVRecipes, pKVRecipe )
  6705. {
  6706. int nRecipeIndex = Q_atoi( pKVRecipe->GetName() );
  6707. int nMapIndex = m_mapRecipes.Find( nRecipeIndex );
  6708. // Make sure the recipe index is correct because we use this index as a reference
  6709. SCHEMA_INIT_CHECK(
  6710. !m_mapRecipes.IsValidIndex( nMapIndex ),
  6711. "Duplicate recipe definition (%d)", nRecipeIndex );
  6712. // Check to make sure the index is positive
  6713. SCHEMA_INIT_CHECK(
  6714. nRecipeIndex >= 0,
  6715. "Recipe definition index %d must be greater than or equal to zero", nRecipeIndex );
  6716. CEconCraftingRecipeDefinition *recipeDef = CreateCraftingRecipeDefinition();
  6717. SCHEMA_INIT_SUBSTEP( recipeDef->BInitFromKV( pKVRecipe, pVecErrors ) );
  6718. #ifdef _DEBUG
  6719. // Sanity check in debug builds so that we know we aren't putting the same recipe in
  6720. // multiple times.
  6721. FOR_EACH_MAP_FAST( m_mapRecipes, i )
  6722. {
  6723. Assert( i != nRecipeIndex );
  6724. Assert( m_mapRecipes[i] != recipeDef );
  6725. }
  6726. #endif // _DEBUG
  6727. // Store this recipe.
  6728. m_mapRecipes.Insert( nRecipeIndex, recipeDef );
  6729. }
  6730. }
  6731. return SCHEMA_INIT_SUCCESS();
  6732. }
  6733. //-----------------------------------------------------------------------------
  6734. // Purpose: Builds the name of a achievement in the form App<ID>.<AchName>
  6735. // Input: unAppID - native app ID
  6736. // pchNativeAchievementName - name of the achievement in its native app
  6737. // Returns: The combined achievement name
  6738. //-----------------------------------------------------------------------------
  6739. CUtlString CEconItemSchema::ComputeAchievementName( AppId_t unAppID, const char *pchNativeAchievementName )
  6740. {
  6741. return CFmtStr1024( "App%u.%s", unAppID, pchNativeAchievementName ).Access();
  6742. }
  6743. //-----------------------------------------------------------------------------
  6744. // Purpose: Initializes the achievement rewards section of the schema
  6745. // Input: pKVAchievementRewards - The achievement_rewards section of the KeyValues
  6746. // representation of the schema
  6747. // pVecErrors - An optional vector that will contain error messages if
  6748. // the init fails.
  6749. // Output: True if initialization succeeded, false otherwise
  6750. //-----------------------------------------------------------------------------
  6751. bool CEconItemSchema::BInitAchievementRewards( KeyValues *pKVAchievementRewards, CUtlVector<CUtlString> *pVecErrors )
  6752. {
  6753. m_dictAchievementRewards.RemoveAll();
  6754. m_mapAchievementRewardsByData.PurgeAndDeleteElements();
  6755. // initialize the rewards sections
  6756. if ( NULL != pKVAchievementRewards )
  6757. {
  6758. FOR_EACH_SUBKEY( pKVAchievementRewards, pKVReward )
  6759. {
  6760. AchievementAward_t award;
  6761. if( pKVReward->GetDataType() == KeyValues::TYPE_NONE )
  6762. {
  6763. int32 nItemIndex = pKVReward->GetInt( "DefIndex", -1 );
  6764. if( nItemIndex != -1 )
  6765. {
  6766. award.m_vecDefIndex.AddToTail( (uint16)nItemIndex );
  6767. }
  6768. else
  6769. {
  6770. KeyValues *pkvItems = pKVReward->FindKey( "Items" );
  6771. SCHEMA_INIT_CHECK(
  6772. pkvItems != NULL,
  6773. "Complex achievement %s must have an Items key or a DefIndex field", pKVReward->GetName() );
  6774. if( !pkvItems )
  6775. {
  6776. continue;
  6777. }
  6778. FOR_EACH_VALUE( pkvItems, pkvItem )
  6779. {
  6780. award.m_vecDefIndex.AddToTail( (uint16)Q_atoi( pkvItem->GetName() ) );
  6781. }
  6782. }
  6783. }
  6784. else
  6785. {
  6786. award.m_vecDefIndex.AddToTail( (uint16)pKVReward->GetInt("", -1 ) );
  6787. }
  6788. // make sure all the item types are valid
  6789. bool bFoundAllItems = true;
  6790. FOR_EACH_VEC( award.m_vecDefIndex, nItem )
  6791. {
  6792. const CEconItemDefinition *pDefn = GetItemDefinition( award.m_vecDefIndex[nItem] );
  6793. SCHEMA_INIT_CHECK(
  6794. pDefn != NULL,
  6795. "Item definition index %d in achievement reward %s was not found", award.m_vecDefIndex[nItem], pKVReward->GetName() );
  6796. if( !pDefn )
  6797. {
  6798. bFoundAllItems = false;
  6799. }
  6800. }
  6801. if( !bFoundAllItems )
  6802. continue;
  6803. SCHEMA_INIT_CHECK(
  6804. award.m_vecDefIndex.Count() > 0,
  6805. "Achievement reward %s has no items!", pKVReward->GetName() );
  6806. if( award.m_vecDefIndex.Count() == 0 )
  6807. continue;
  6808. #ifdef GC_DLL
  6809. award.m_unSourceAppId = GGCBase()->GetAppID();
  6810. #else
  6811. award.m_unSourceAppId = k_uAppIdInvalid;
  6812. #endif
  6813. if( pKVReward->GetDataType() == KeyValues::TYPE_NONE )
  6814. {
  6815. // cross game achievement
  6816. award.m_sNativeName = pKVReward->GetName();
  6817. award.m_unAuditData = pKVReward->GetInt( "AuditData", 0 );
  6818. award.m_unSourceAppId = pKVReward->GetInt( "SourceAppID", award.m_unSourceAppId );
  6819. }
  6820. else
  6821. {
  6822. award.m_sNativeName = pKVReward->GetName();
  6823. award.m_unAuditData = 0;
  6824. }
  6825. #ifdef GC_DLL
  6826. // Check to make sure the audit data is valid
  6827. SCHEMA_INIT_CHECK(
  6828. award.m_unSourceAppId >= 0,
  6829. "Source App ID %d in achievement reward %s must be valid", award.m_unSourceAppId, pKVReward->GetName() );
  6830. if( award.m_unSourceAppId == k_uAppIdInvalid )
  6831. continue;
  6832. if( !GGCGameBase()->BYieldingLoadStats( award.m_unSourceAppId ) )
  6833. {
  6834. // this will often fail in a dev universe
  6835. if( GGCHost()->GetUniverse() != k_EUniverseDev )
  6836. {
  6837. SCHEMA_INIT_CHECK(
  6838. false,
  6839. "Unable to load stats schema for cross-game achievement %s for app %d", pKVReward->GetName(), award.m_unSourceAppId );
  6840. }
  6841. continue;
  6842. }
  6843. const CGCStatsSchema *pStatsSchema = GGCGameBase()->GetStatsSchema( award.m_unSourceAppId );
  6844. if( !pStatsSchema )
  6845. {
  6846. SCHEMA_INIT_CHECK(
  6847. false,
  6848. "Unable to retrieve stats schema for cross-game achievement %s for app %d", pKVReward->GetName(), award.m_unSourceAppId );
  6849. continue;
  6850. }
  6851. if( award.m_unAuditData == 0 )
  6852. {
  6853. uint16 usStatID, usBitID;
  6854. if( !pStatsSchema->BGetAchievementBit( award.m_sNativeName, &usStatID, &usBitID ) )
  6855. {
  6856. SCHEMA_INIT_CHECK(
  6857. false,
  6858. "Unable to find achievement %s for app %d", award.m_sNativeName.Get(), award.m_unSourceAppId );
  6859. continue;
  6860. }
  6861. award.m_unAuditData = ( usStatID <<16 ) | usBitID;
  6862. }
  6863. #endif // GC_DLL
  6864. AchievementAward_t *pAward = new AchievementAward_t;
  6865. *pAward = award;
  6866. m_dictAchievementRewards.Insert( ComputeAchievementName( pAward->m_unSourceAppId, pAward->m_sNativeName ), pAward );
  6867. m_mapAchievementRewardsByData.Insert( pAward->m_unAuditData, pAward );
  6868. }
  6869. }
  6870. return SCHEMA_INIT_SUCCESS();
  6871. }
  6872. #ifdef GC_DLL
  6873. bool CEconItemSchema::BInitRandomAttributeTemplates( KeyValues *pKVRandomAttributeTemplates, CUtlVector<CUtlString> *pVecErrors )
  6874. {
  6875. m_dictRandomAttributeTemplates.PurgeAndDeleteElements();
  6876. FOR_EACH_TRUE_SUBKEY( pKVRandomAttributeTemplates, pKVAttributeTemplate )
  6877. {
  6878. const char *pszAttrName = pKVAttributeTemplate->GetName();
  6879. // try to create random attrib from template
  6880. random_attrib_t *pRandomAttr = CreateRandomAttribute( __FUNCTION__, pKVAttributeTemplate, pVecErrors );
  6881. SCHEMA_INIT_CHECK(
  6882. NULL != pRandomAttr,
  6883. CFmtStr( "%s: Failed to create random_attrib_t '%s'", __FUNCTION__, pszAttrName ) );
  6884. m_dictRandomAttributeTemplates.Insert( pszAttrName, pRandomAttr );
  6885. }
  6886. return true;
  6887. }
  6888. #endif // GC_DLL
  6889. #ifdef TF_CLIENT_DLL
  6890. //-----------------------------------------------------------------------------
  6891. // Purpose: Go through all items and cache the number of concrete items in each.
  6892. //-----------------------------------------------------------------------------
  6893. bool CEconItemSchema::BInitConcreteItemCounts( CUtlVector<CUtlString> *pVecErrors )
  6894. {
  6895. FOR_EACH_MAP_FAST( m_mapItems, i )
  6896. {
  6897. CEconItemDefinition *pItemDef = m_mapItems[ i ];
  6898. pItemDef->m_unNumConcreteItems = CalculateNumberOfConcreteItems( pItemDef );
  6899. }
  6900. return true;
  6901. }
  6902. //-----------------------------------------------------------------------------
  6903. // Purpose: Returns the number of actual "real" items referenced by the item definition
  6904. // (i.e. items that would take up space in the inventory)
  6905. //-----------------------------------------------------------------------------
  6906. int CEconItemSchema::CalculateNumberOfConcreteItems( const CEconItemDefinition *pItemDef )
  6907. {
  6908. AssertMsg( pItemDef, "NULL item definition! This should not happen!" );
  6909. if ( !pItemDef )
  6910. return 0;
  6911. if ( pItemDef->IsBundle() )
  6912. {
  6913. uint32 unNumConcreteItems = 0;
  6914. const bundleinfo_t *pBundle = pItemDef->GetBundleInfo();
  6915. Assert( pBundle );
  6916. FOR_EACH_VEC( pBundle->vecItemDefs, i )
  6917. {
  6918. unNumConcreteItems += CalculateNumberOfConcreteItems( pBundle->vecItemDefs[i] );
  6919. }
  6920. return unNumConcreteItems;
  6921. }
  6922. return 1;
  6923. }
  6924. //-----------------------------------------------------------------------------
  6925. // Purpose:
  6926. //-----------------------------------------------------------------------------
  6927. bool CEconItemSchema::BInitSteamPackageLocalizationToken( KeyValues *pKVSteamPackages, CUtlVector<CUtlString> *pVecErrors )
  6928. {
  6929. if ( NULL != pKVSteamPackages )
  6930. {
  6931. FOR_EACH_TRUE_SUBKEY( pKVSteamPackages, pKVEntry )
  6932. {
  6933. // Check to make sure the index is positive
  6934. int iRawPackageId = atoi( pKVEntry->GetName() );
  6935. SCHEMA_INIT_CHECK(
  6936. iRawPackageId > 0,
  6937. "Invalid package ID %i for localization", iRawPackageId );
  6938. // Store off our data.
  6939. uint32 unPackageId = (uint32)iRawPackageId;
  6940. const char *pszLocalizationToken = pKVEntry->GetString( "localization_key" );
  6941. m_mapSteamPackageLocalizationTokens.InsertOrReplace( unPackageId, pszLocalizationToken );
  6942. }
  6943. }
  6944. return SCHEMA_INIT_SUCCESS();
  6945. }
  6946. #endif // TF_CLIENT_DLL
  6947. static const char *s_particle_controlpoint_names[] =
  6948. {
  6949. "attachment",
  6950. "control_point_1",
  6951. "control_point_2",
  6952. "control_point_3",
  6953. "control_point_4",
  6954. "control_point_5",
  6955. "control_point_6",
  6956. };
  6957. //-----------------------------------------------------------------------------
  6958. // Purpose: Initializes the attribute-controlled-particle-systems section of the schema
  6959. //-----------------------------------------------------------------------------
  6960. bool CEconItemSchema::BInitAttributeControlledParticleSystems( KeyValues *pKVParticleSystems, CUtlVector<CUtlString> *pVecErrors )
  6961. {
  6962. m_mapAttributeControlledParticleSystems.RemoveAll();
  6963. m_vecAttributeControlledParticleSystemsCosmetics.RemoveAll();
  6964. m_vecAttributeControlledParticleSystemsWeapons.RemoveAll();
  6965. m_vecAttributeControlledParticleSystemsTaunts.RemoveAll();
  6966. CUtlVector< int > *pVec = NULL;
  6967. // Addictional groups we are tracking for.
  6968. // "cosmetic_unusual_effects"
  6969. // "weapon_unusual_effects"
  6970. // "taunt_unusual_effects"
  6971. if ( NULL != pKVParticleSystems )
  6972. {
  6973. FOR_EACH_TRUE_SUBKEY( pKVParticleSystems, pKVCategory )
  6974. {
  6975. // There is 3 Categories we want to track with additional info
  6976. if ( !V_strcmp( pKVCategory->GetName(), "cosmetic_unusual_effects" ) )
  6977. {
  6978. pVec = &m_vecAttributeControlledParticleSystemsCosmetics;
  6979. }
  6980. else if ( !V_strcmp( pKVCategory->GetName(), "weapon_unusual_effects" ) )
  6981. {
  6982. pVec = &m_vecAttributeControlledParticleSystemsWeapons;
  6983. }
  6984. else if ( !V_strcmp( pKVCategory->GetName(), "taunt_unusual_effects" ) )
  6985. {
  6986. pVec = &m_vecAttributeControlledParticleSystemsTaunts;
  6987. }
  6988. else
  6989. {
  6990. pVec = NULL; // reset
  6991. }
  6992. FOR_EACH_TRUE_SUBKEY( pKVCategory, pKVEntry )
  6993. {
  6994. int32 nItemIndex = atoi( pKVEntry->GetName() );
  6995. // Check to make sure the index is positive
  6996. SCHEMA_INIT_CHECK(
  6997. nItemIndex > 0,
  6998. "Particle system index %d greater than zero", nItemIndex );
  6999. if ( nItemIndex <= 0 )
  7000. continue;
  7001. int iIndex = m_mapAttributeControlledParticleSystems.Insert( nItemIndex );
  7002. attachedparticlesystem_t &system = m_mapAttributeControlledParticleSystems[iIndex];
  7003. system.pszSystemName = pKVEntry->GetString( "system", NULL );
  7004. system.bFollowRootBone = pKVEntry->GetInt( "attach_to_rootbone", 0 ) != 0;
  7005. system.iCustomType = 0;
  7006. system.nSystemID = nItemIndex;
  7007. system.fRefireTime = pKVEntry->GetFloat( "refire_time", 0.0f );
  7008. system.bDrawInViewModel = pKVEntry->GetBool( "draw_in_viewmodel", false );
  7009. system.bUseSuffixName = pKVEntry->GetBool( "use_suffix_name", false );
  7010. system.bHasViewModelSpecificEffect = pKVEntry->GetBool( "has_viewmodel_specific_effect", false );
  7011. COMPILE_TIME_ASSERT( ARRAYSIZE( system.pszControlPoints ) == ARRAYSIZE( s_particle_controlpoint_names ) );
  7012. for ( int i=0; i<ARRAYSIZE( system.pszControlPoints ); ++i )
  7013. {
  7014. system.pszControlPoints[i] = pKVEntry->GetString( s_particle_controlpoint_names[i], NULL );
  7015. }
  7016. if ( pVec )
  7017. {
  7018. pVec->AddToTail( nItemIndex );
  7019. }
  7020. }
  7021. }
  7022. }
  7023. return SCHEMA_INIT_SUCCESS();
  7024. }
  7025. #ifdef CLIENT_DLL
  7026. locchar_t *CEconItemSchema::GetParticleSystemLocalizedName( int index ) const
  7027. {
  7028. const attachedparticlesystem_t *pSystem = GetItemSchema()->GetAttributeControlledParticleSystem( index );
  7029. if ( !pSystem )
  7030. return NULL;
  7031. char particleNameEntry[128];
  7032. Q_snprintf( particleNameEntry, ARRAYSIZE( particleNameEntry ), "#Attrib_Particle%d", pSystem->nSystemID );
  7033. return g_pVGuiLocalize->Find( particleNameEntry );
  7034. }
  7035. #endif
  7036. //-----------------------------------------------------------------------------
  7037. // Purpose: Inits data for items that can level up through kills, etc.
  7038. //-----------------------------------------------------------------------------
  7039. bool CEconItemSchema::BInitItemLevels( KeyValues *pKVItemLevels, CUtlVector<CUtlString> *pVecErrors )
  7040. {
  7041. m_vecItemLevelingData.RemoveAll();
  7042. // initialize the rewards sections
  7043. if ( NULL != pKVItemLevels )
  7044. {
  7045. FOR_EACH_TRUE_SUBKEY( pKVItemLevels, pKVItemLevelBlock )
  7046. {
  7047. const char *pszLevelBlockName = pKVItemLevelBlock->GetName();
  7048. SCHEMA_INIT_CHECK( GetItemLevelingData( pszLevelBlockName ) == NULL,
  7049. "Duplicate leveling data block named \"%s\".", pszLevelBlockName );
  7050. // Allocate a new structure for this block and assign it. We'll fill in the contents later.
  7051. CUtlVector<CItemLevelingDefinition> *pLevelingData = new CUtlVector<CItemLevelingDefinition>;
  7052. m_vecItemLevelingData.Insert( pszLevelBlockName, pLevelingData );
  7053. FOR_EACH_TRUE_SUBKEY( pKVItemLevelBlock, pKVItemLevel )
  7054. {
  7055. int index = pLevelingData->AddToTail();
  7056. SCHEMA_INIT_SUBSTEP( (*pLevelingData)[index].BInitFromKV( pKVItemLevel, pszLevelBlockName, pVecErrors ) );
  7057. }
  7058. }
  7059. }
  7060. return SCHEMA_INIT_SUCCESS();
  7061. }
  7062. //-----------------------------------------------------------------------------
  7063. // Purpose: Inits data for kill eater types.
  7064. //-----------------------------------------------------------------------------
  7065. bool CEconItemSchema::BInitKillEaterScoreTypes( KeyValues *pKVKillEaterScoreTypes, CUtlVector<CUtlString> *pVecErrors )
  7066. {
  7067. m_mapKillEaterScoreTypes.RemoveAll();
  7068. // initialize the rewards sections
  7069. if ( NULL != pKVKillEaterScoreTypes )
  7070. {
  7071. FOR_EACH_TRUE_SUBKEY( pKVKillEaterScoreTypes, pKVScoreType )
  7072. {
  7073. unsigned int unIndex = (unsigned int)atoi( pKVScoreType->GetName() );
  7074. SCHEMA_INIT_CHECK( m_mapKillEaterScoreTypes.Find( unIndex ) == KillEaterScoreMap_t::InvalidIndex(),
  7075. "Duplicate kill eater score type index %u.", unIndex );
  7076. kill_eater_score_type_t ScoreType;
  7077. ScoreType.m_pszTypeString = pKVScoreType->GetString( "type_name" );
  7078. ScoreType.m_bAllowBotVictims = pKVScoreType->GetBool( "allow_bot_victims", false );
  7079. #ifdef GC_DLL
  7080. ScoreType.m_bGCUpdateOnly = pKVScoreType->GetBool( "gc_update_only", false );
  7081. ScoreType.m_AllowIncrementValues = pKVScoreType->GetBool( "gc_allow_increment_values", false );
  7082. ScoreType.m_bIsBaseKillType = pKVScoreType->GetBool( "gc_is_base_kill_type", false );
  7083. #endif
  7084. const char *pszLevelBlockName = pKVScoreType->GetString( "level_data", "KillEaterRank" );
  7085. SCHEMA_INIT_CHECK( GetItemLevelingData( pszLevelBlockName ) != NULL,
  7086. "Unable to find leveling data block named \"%s\" for kill eater score type %u.", pszLevelBlockName, unIndex );
  7087. ScoreType.m_pszLevelBlockName = pszLevelBlockName;
  7088. m_mapKillEaterScoreTypes.Insert( unIndex, ScoreType );
  7089. }
  7090. }
  7091. return SCHEMA_INIT_SUCCESS();
  7092. }
  7093. //-----------------------------------------------------------------------------
  7094. // Purpose:
  7095. //-----------------------------------------------------------------------------
  7096. bool CEconItemSchema::BInitStringTables( KeyValues *pKVStringTables, CUtlVector<CUtlString> *pVecErrors )
  7097. {
  7098. m_dictStringTable.PurgeAndDeleteElements();
  7099. // initialize the rewards sections
  7100. if ( NULL != pKVStringTables )
  7101. {
  7102. FOR_EACH_SUBKEY( pKVStringTables, pKVTable )
  7103. {
  7104. SCHEMA_INIT_CHECK( !m_dictStringTable.IsValidIndex( m_dictStringTable.Find( pKVTable->GetName() ) ),
  7105. "Duplicate string table name '%s'.", pKVTable->GetName() );
  7106. SchemaStringTableDict_t::IndexType_t i = m_dictStringTable.Insert( pKVTable->GetName(), new CUtlVector< schema_string_table_entry_t > );
  7107. FOR_EACH_SUBKEY( pKVTable, pKVEntry )
  7108. {
  7109. schema_string_table_entry_t s = { atoi( pKVEntry->GetName() ), pKVEntry->GetString() };
  7110. m_dictStringTable[i]->AddToTail( s );
  7111. }
  7112. }
  7113. }
  7114. return SCHEMA_INIT_SUCCESS();
  7115. }
  7116. //-----------------------------------------------------------------------------
  7117. // Purpose:
  7118. //-----------------------------------------------------------------------------
  7119. bool CEconItemSchema::BInitCommunityMarketRemaps( KeyValues *pKVCommunityMarketRemaps, CUtlVector<CUtlString> *pVecErrors )
  7120. {
  7121. m_mapCommunityMarketDefinitionIndexRemap.Purge();
  7122. if ( NULL != pKVCommunityMarketRemaps )
  7123. {
  7124. FOR_EACH_SUBKEY( pKVCommunityMarketRemaps, pKVRemapBase )
  7125. {
  7126. const char *pszBaseDefName = pKVRemapBase->GetName();
  7127. const CEconItemDefinition *pBaseItemDef = GetItemSchema()->GetItemDefinitionByName( pszBaseDefName );
  7128. SCHEMA_INIT_CHECK( pBaseItemDef != NULL, "Unknown Market remap base definition '%s'.", pszBaseDefName );
  7129. FOR_EACH_SUBKEY( pKVRemapBase, pKVRemap )
  7130. {
  7131. const char *pszDefName = pKVRemap->GetName();
  7132. const CEconItemDefinition *pItemDef = GetItemSchema()->GetItemDefinitionByName( pszDefName );
  7133. SCHEMA_INIT_CHECK( pItemDef != NULL, "Unknown Market remap definition '%s' (under '%s').", pszDefName, pszBaseDefName );
  7134. SCHEMA_INIT_CHECK( m_mapCommunityMarketDefinitionIndexRemap.Find( pItemDef->GetDefinitionIndex() ) == m_mapCommunityMarketDefinitionIndexRemap.InvalidIndex(), "Duplicate Market remap definition '%s'.\n", pszDefName );
  7135. m_mapCommunityMarketDefinitionIndexRemap.Insert( pItemDef->GetDefinitionIndex(), pBaseItemDef->GetDefinitionIndex() );
  7136. }
  7137. }
  7138. }
  7139. return SCHEMA_INIT_SUCCESS();
  7140. }
  7141. //-----------------------------------------------------------------------------
  7142. // Purpose:
  7143. //-----------------------------------------------------------------------------
  7144. item_definition_index_t CEconItemSchema::GetCommunityMarketRemappedDefinitionIndex( item_definition_index_t unSearchItemDef ) const
  7145. {
  7146. CommunityMarketDefinitionRemapMap_t::IndexType_t index = m_mapCommunityMarketDefinitionIndexRemap.Find( unSearchItemDef );
  7147. if ( index == m_mapCommunityMarketDefinitionIndexRemap.InvalidIndex() )
  7148. return unSearchItemDef;
  7149. return m_mapCommunityMarketDefinitionIndexRemap[index];
  7150. }
  7151. //-----------------------------------------------------------------------------
  7152. // Purpose:
  7153. //-----------------------------------------------------------------------------
  7154. const ISchemaAttributeType *CEconItemSchema::GetAttributeType( const char *pszAttrTypeName ) const
  7155. {
  7156. FOR_EACH_VEC( m_vecAttributeTypes, i )
  7157. {
  7158. if ( m_vecAttributeTypes[i].m_sName == pszAttrTypeName )
  7159. return m_vecAttributeTypes[i].m_pAttrType;
  7160. }
  7161. return NULL;
  7162. }
  7163. //-----------------------------------------------------------------------------
  7164. // CItemLevelingDefinition Accessor
  7165. //-----------------------------------------------------------------------------
  7166. const CItemLevelingDefinition *CEconItemSchema::GetItemLevelForScore( const char *pszLevelBlockName, uint32 unScore ) const
  7167. {
  7168. const CUtlVector<CItemLevelingDefinition> *pLevelingData = GetItemLevelingData( pszLevelBlockName );
  7169. if ( !pLevelingData )
  7170. return NULL;
  7171. if ( pLevelingData->Count() == 0 )
  7172. return NULL;
  7173. FOR_EACH_VEC( (*pLevelingData), i )
  7174. {
  7175. if ( unScore < (*pLevelingData)[i].GetRequiredScore() )
  7176. return &(*pLevelingData)[i];
  7177. }
  7178. return &(*pLevelingData).Tail();
  7179. }
  7180. //-----------------------------------------------------------------------------
  7181. // Kill eater score type accessor
  7182. //-----------------------------------------------------------------------------
  7183. const kill_eater_score_type_t *CEconItemSchema::FindKillEaterScoreType( uint32 unScoreType ) const
  7184. {
  7185. KillEaterScoreMap_t::IndexType_t i = m_mapKillEaterScoreTypes.Find( unScoreType );
  7186. if ( i == KillEaterScoreMap_t::InvalidIndex() )
  7187. return NULL;
  7188. return &m_mapKillEaterScoreTypes[i];
  7189. }
  7190. //-----------------------------------------------------------------------------
  7191. // Kill eater score type accessor
  7192. //-----------------------------------------------------------------------------
  7193. const char *CEconItemSchema::GetKillEaterScoreTypeLocString( uint32 unScoreType ) const
  7194. {
  7195. const kill_eater_score_type_t *pScoreType = FindKillEaterScoreType( unScoreType );
  7196. return pScoreType
  7197. ? pScoreType->m_pszTypeString
  7198. : NULL;
  7199. }
  7200. //-----------------------------------------------------------------------------
  7201. // Kill eater score type accessor
  7202. //-----------------------------------------------------------------------------
  7203. const char *CEconItemSchema::GetKillEaterScoreTypeLevelingDataName( uint32 unScoreType ) const
  7204. {
  7205. const kill_eater_score_type_t *pScoreType = FindKillEaterScoreType( unScoreType );
  7206. return pScoreType
  7207. ? pScoreType->m_pszLevelBlockName
  7208. : NULL;
  7209. }
  7210. #if defined(STAGING_ONLY) && ( defined(TF_CLIENT_DLL) || defined(TF_DLL) )
  7211. ConVar tf_allow_strange_bot_kills( "tf_allow_strange_bot_kills", "0", FCVAR_REPLICATED );
  7212. #endif
  7213. //-----------------------------------------------------------------------------
  7214. // Kill eater score type accessor
  7215. //-----------------------------------------------------------------------------
  7216. bool CEconItemSchema::GetKillEaterScoreTypeAllowsBotVictims( uint32 unScoreType ) const
  7217. {
  7218. #if defined(STAGING_ONLY) && ( defined(TF_CLIENT_DLL) || defined(TF_DLL) )
  7219. if ( tf_allow_strange_bot_kills.GetBool() )
  7220. {
  7221. return true;
  7222. }
  7223. #endif
  7224. const kill_eater_score_type_t *pScoreType = FindKillEaterScoreType( unScoreType );
  7225. return pScoreType
  7226. ? pScoreType->m_bAllowBotVictims
  7227. : false;
  7228. }
  7229. //-----------------------------------------------------------------------------
  7230. // Purpose:
  7231. //-----------------------------------------------------------------------------
  7232. econ_tag_handle_t CEconItemSchema::GetHandleForTag( const char *pszTagName )
  7233. {
  7234. EconTagDict_t::IndexType_t i = m_dictTags.Find( pszTagName );
  7235. if ( m_dictTags.IsValidIndex( i ) )
  7236. return i;
  7237. return m_dictTags.Insert( pszTagName );
  7238. }
  7239. #ifdef GC_DLL
  7240. //-----------------------------------------------------------------------------
  7241. // Kill eater score type accessor
  7242. //-----------------------------------------------------------------------------
  7243. bool CEconItemSchema::GetKillEaterScoreTypeGCOnlyUpdate( uint32 unScoreType ) const
  7244. {
  7245. const kill_eater_score_type_t *pScoreType = FindKillEaterScoreType( unScoreType );
  7246. return pScoreType
  7247. ? pScoreType->m_bGCUpdateOnly
  7248. : true; // default to being more restrictive
  7249. }
  7250. //-----------------------------------------------------------------------------
  7251. // Purpose:
  7252. //-----------------------------------------------------------------------------
  7253. bool CEconItemSchema::GetKillEaterScoreTypeAllowsIncrementValues( uint32 unScoreType ) const
  7254. {
  7255. const kill_eater_score_type_t *pScoreType = FindKillEaterScoreType( unScoreType );
  7256. return pScoreType
  7257. ? pScoreType->m_AllowIncrementValues
  7258. : true; // default to being more restrictive
  7259. }
  7260. //-----------------------------------------------------------------------------
  7261. // Purpose:
  7262. //-----------------------------------------------------------------------------
  7263. const CEconItemSchema::periodic_score_t& CEconItemSchema::GetPeriodicScoreInfo( int iPeriodicScoreIndex ) const
  7264. {
  7265. Assert( GetPeriodicScoreTypeList().IsValidIndex( iPeriodicScoreIndex ) );
  7266. return GetPeriodicScoreTypeList()[ iPeriodicScoreIndex ];
  7267. }
  7268. #endif
  7269. #if defined(CLIENT_DLL) || defined(GAME_DLL)
  7270. //-----------------------------------------------------------------------------
  7271. // Purpose: Clones the specified item definition, and returns the new item def.
  7272. //-----------------------------------------------------------------------------
  7273. void CEconItemSchema::ItemTesting_CreateTestDefinition( int iCloneFromItemDef, int iNewDef, KeyValues *pNewKV )
  7274. {
  7275. int nMapIndex = m_mapItems.Find( iNewDef );
  7276. if ( !m_mapItems.IsValidIndex( nMapIndex ) )
  7277. {
  7278. nMapIndex = m_mapItems.Insert( iNewDef, CreateEconItemDefinition() );
  7279. m_mapItemsSorted.Insert( iNewDef, m_mapItems[nMapIndex] );
  7280. }
  7281. // Find & copy the clone item def's data in
  7282. CEconItemDefinition *pCloneDef = GetItemDefinition( iCloneFromItemDef );
  7283. if ( !pCloneDef )
  7284. return;
  7285. m_mapItems[nMapIndex]->CopyPolymorphic( pCloneDef );
  7286. // Then stomp it with the KV test contents
  7287. m_mapItems[nMapIndex]->BInitFromTestItemKVs( iNewDef, pNewKV );
  7288. }
  7289. //-----------------------------------------------------------------------------
  7290. // Purpose: Discards the specified item definition
  7291. //-----------------------------------------------------------------------------
  7292. void CEconItemSchema::ItemTesting_DiscardTestDefinition( int iDef )
  7293. {
  7294. m_mapItems.Remove( iDef );
  7295. m_mapItemsSorted.Remove( iDef );
  7296. }
  7297. //-----------------------------------------------------------------------------
  7298. // Purpose: Initializes the armory data section of the schema
  7299. //-----------------------------------------------------------------------------
  7300. bool CEconItemSchema::BInitArmoryData( KeyValues *pKVArmoryData, CUtlVector<CUtlString> *pVecErrors )
  7301. {
  7302. m_dictArmoryItemDataStrings.RemoveAll();
  7303. m_dictArmoryAttributeDataStrings.RemoveAll();
  7304. if ( NULL != pKVArmoryData )
  7305. {
  7306. KeyValues *pKVItemTypes = pKVArmoryData->FindKey( "armory_item_types" );
  7307. if ( pKVItemTypes )
  7308. {
  7309. FOR_EACH_SUBKEY( pKVItemTypes, pKVEntry )
  7310. {
  7311. const char *pszDataKey = pKVEntry->GetName();
  7312. const CUtlConstString sLocString( pKVEntry->GetString() );
  7313. m_dictArmoryItemTypesDataStrings.Insert( pszDataKey, sLocString );
  7314. }
  7315. }
  7316. pKVItemTypes = pKVArmoryData->FindKey( "armory_item_classes" );
  7317. if ( pKVItemTypes )
  7318. {
  7319. FOR_EACH_SUBKEY( pKVItemTypes, pKVEntry )
  7320. {
  7321. const char *pszDataKey = pKVEntry->GetName();
  7322. const CUtlConstString sLocString( pKVEntry->GetString() );
  7323. m_dictArmoryItemClassesDataStrings.Insert( pszDataKey, sLocString );
  7324. }
  7325. }
  7326. KeyValues *pKVAttribs = pKVArmoryData->FindKey( "armory_attributes" );
  7327. if ( pKVAttribs )
  7328. {
  7329. FOR_EACH_SUBKEY( pKVAttribs, pKVEntry )
  7330. {
  7331. const char *pszDataKey = pKVEntry->GetName();
  7332. const CUtlConstString sLocString( pKVEntry->GetString() );
  7333. m_dictArmoryAttributeDataStrings.Insert( pszDataKey, sLocString );
  7334. }
  7335. }
  7336. KeyValues *pKVItems = pKVArmoryData->FindKey( "armory_items" );
  7337. if ( pKVItems )
  7338. {
  7339. FOR_EACH_SUBKEY( pKVItems, pKVEntry )
  7340. {
  7341. const char *pszDataKey = pKVEntry->GetName();
  7342. const CUtlConstString sLocString( pKVEntry->GetString() );
  7343. m_dictArmoryItemDataStrings.Insert( pszDataKey, sLocString );
  7344. }
  7345. }
  7346. }
  7347. return SCHEMA_INIT_SUCCESS();
  7348. }
  7349. #endif
  7350. #ifdef GC_DLL
  7351. //-----------------------------------------------------------------------------
  7352. // Purpose: Returns the item awarded for an achievement.
  7353. // Input: pchAchievementName - The achievement that was awarded.
  7354. // Output: The achievement struct for this reward.
  7355. //-----------------------------------------------------------------------------
  7356. const AchievementAward_t * CEconItemSchema::GetAchievementReward( const char *pchAchievementName, AppId_t unAppID ) const
  7357. {
  7358. int nRewardIndex = m_dictAchievementRewards.Find( ComputeAchievementName( unAppID, pchAchievementName ) );
  7359. if( m_dictAchievementRewards.IsValidIndex( nRewardIndex ) )
  7360. return m_dictAchievementRewards[ nRewardIndex ];
  7361. else
  7362. return NULL;
  7363. }
  7364. //-----------------------------------------------------------------------------
  7365. // Purpose: Returns the achievement award that matches the provided data or NULL
  7366. // if there is no such award.
  7367. // Input: unData - The data field that would be stored in ItemAudit
  7368. //-----------------------------------------------------------------------------
  7369. const AchievementAward_t *CEconItemSchema::GetAchievementRewardByData( uint32 unData ) const
  7370. {
  7371. uint nIndex = m_mapAchievementRewardsByData.Find( unData );
  7372. if( m_mapAchievementRewardsByData.IsValidIndex( nIndex ) )
  7373. {
  7374. return m_mapAchievementRewardsByData[nIndex];
  7375. }
  7376. else
  7377. {
  7378. return NULL;
  7379. }
  7380. }
  7381. #endif // GC_DLL
  7382. //-----------------------------------------------------------------------------
  7383. // Purpose: Returns the achievement award that matches the provided defindex or NULL
  7384. // if there is no such award.
  7385. // Input: unData - The data field that would be stored in ItemAudit
  7386. //-----------------------------------------------------------------------------
  7387. const AchievementAward_t *CEconItemSchema::GetAchievementRewardByDefIndex( uint16 usDefIndex ) const
  7388. {
  7389. FOR_EACH_MAP_FAST( m_mapAchievementRewardsByData, nIndex )
  7390. {
  7391. if( m_mapAchievementRewardsByData[nIndex]->m_vecDefIndex.HasElement( usDefIndex ) )
  7392. return m_mapAchievementRewardsByData[nIndex];
  7393. }
  7394. return NULL;
  7395. }
  7396. //-----------------------------------------------------------------------------
  7397. // Purpose: Gets a rarity value for a name.
  7398. //-----------------------------------------------------------------------------
  7399. bool CEconItemSchema::BGetItemRarityFromName( const char *pchName, uint8 *nRarity ) const
  7400. {
  7401. if ( 0 == Q_stricmp( "any", pchName ) )
  7402. {
  7403. *nRarity = k_unItemRarity_Any;
  7404. return true;
  7405. }
  7406. FOR_EACH_MAP_FAST( m_mapRarities, i )
  7407. {
  7408. if ( 0 == Q_stricmp( m_mapRarities[i].GetName(), pchName ) )
  7409. {
  7410. *nRarity = m_mapRarities[i].GetDBValue();
  7411. return true;
  7412. }
  7413. }
  7414. return false;
  7415. }
  7416. //-----------------------------------------------------------------------------
  7417. // Purpose: Gets a quality value for a name.
  7418. // Input: pchName - The name to translate.
  7419. // nQuality - (out)The quality number for this name, if found.
  7420. // Output: True if the string matched a quality for this schema, false otherwise.
  7421. //-----------------------------------------------------------------------------
  7422. bool CEconItemSchema::BGetItemQualityFromName( const char *pchName, uint8 *nQuality ) const
  7423. {
  7424. if ( 0 == Q_stricmp( "any", pchName ) )
  7425. {
  7426. *nQuality = k_unItemQuality_Any;
  7427. return true;
  7428. }
  7429. FOR_EACH_MAP_FAST( m_mapQualities, i )
  7430. {
  7431. if ( 0 == Q_stricmp( m_mapQualities[i].GetName(), pchName ) )
  7432. {
  7433. *nQuality = m_mapQualities[i].GetDBValue();
  7434. return true;
  7435. }
  7436. }
  7437. return false;
  7438. }
  7439. //-----------------------------------------------------------------------------
  7440. // Purpose: Gets a quality definition for an index
  7441. // Input: nQuality - The quality to get.
  7442. // Output: A pointer to the desired definition, or NULL if it is not found.
  7443. //-----------------------------------------------------------------------------
  7444. const CEconItemQualityDefinition *CEconItemSchema::GetQualityDefinition( int nQuality ) const
  7445. {
  7446. int iIndex = m_mapQualities.Find( nQuality );
  7447. if ( m_mapQualities.IsValidIndex( iIndex ) )
  7448. return &m_mapQualities[iIndex];
  7449. return NULL;
  7450. }
  7451. const CEconItemQualityDefinition *CEconItemSchema::GetQualityDefinitionByName( const char *pszDefName ) const
  7452. {
  7453. FOR_EACH_MAP_FAST( m_mapQualities, i )
  7454. {
  7455. if ( V_stricmp( pszDefName, m_mapQualities[i].GetName()) == 0 )
  7456. return &m_mapQualities[i];
  7457. }
  7458. return NULL;
  7459. }
  7460. //-----------------------------------------------------------------------------
  7461. // ItemRarity
  7462. //-----------------------------------------------------------------------------
  7463. const CEconItemRarityDefinition *CEconItemSchema::GetRarityDefinitionByMapIndex( int nRarityIndex ) const
  7464. {
  7465. if ( m_mapRarities.IsValidIndex( nRarityIndex ) )
  7466. return &m_mapRarities[nRarityIndex];
  7467. return NULL;
  7468. }
  7469. //-----------------------------------------------------------------------------
  7470. const CEconItemRarityDefinition *CEconItemSchema::GetRarityDefinition( int nRarity ) const
  7471. {
  7472. int iIndex = m_mapRarities.Find( nRarity );
  7473. if ( m_mapRarities.IsValidIndex( iIndex ) )
  7474. return &m_mapRarities[iIndex];
  7475. return NULL;
  7476. }
  7477. //-----------------------------------------------------------------------------
  7478. const CEconItemRarityDefinition *CEconItemSchema::GetRarityDefinitionByName( const char *pszDefName ) const
  7479. {
  7480. FOR_EACH_MAP_FAST( m_mapRarities, i )
  7481. {
  7482. if ( !strcmp( pszDefName, m_mapRarities[i].GetName() ) )
  7483. return &m_mapRarities[i];
  7484. }
  7485. return NULL;
  7486. }
  7487. //-----------------------------------------------------------------------------
  7488. const char* CEconItemSchema::GetRarityName( uint8 iRarity )
  7489. {
  7490. const CEconItemRarityDefinition* pItemRarity = GetRarityDefinition( iRarity );
  7491. if ( !pItemRarity )
  7492. return NULL;
  7493. else
  7494. return pItemRarity->GetName();
  7495. }
  7496. //-----------------------------------------------------------------------------
  7497. const char* CEconItemSchema::GetRarityLocKey( uint8 iRarity )
  7498. {
  7499. const CEconItemRarityDefinition* pItemRarity = GetRarityDefinition( iRarity );
  7500. if ( !pItemRarity )
  7501. return NULL;
  7502. else
  7503. return pItemRarity->GetLocKey();
  7504. }
  7505. //-----------------------------------------------------------------------------
  7506. const char* CEconItemSchema::GetRarityColor( uint8 iRarity )
  7507. {
  7508. const CEconItemRarityDefinition* pItemRarity = GetRarityDefinition( iRarity );
  7509. if ( !pItemRarity )
  7510. return NULL;
  7511. else
  7512. return GetColorNameForAttribColor( pItemRarity->GetAttribColor() );
  7513. }
  7514. //-----------------------------------------------------------------------------
  7515. int CEconItemSchema::GetRarityIndex( const char* pszRarity )
  7516. {
  7517. const CEconItemRarityDefinition* pRarity = GetRarityDefinitionByName( pszRarity );
  7518. if ( pRarity )
  7519. return pRarity->GetDBValue();
  7520. else
  7521. return 0;
  7522. }
  7523. //-----------------------------------------------------------------------------
  7524. bool CEconItemSchema::BGetItemSeries( const char* pchName, uint8 *nItemSeries ) const
  7525. {
  7526. FOR_EACH_MAP_FAST( m_mapItemSeries, i )
  7527. {
  7528. if ( 0 == Q_stricmp( m_mapItemSeries[i].GetName(), pchName ) )
  7529. {
  7530. *nItemSeries = m_mapItemSeries[i].GetDBValue();
  7531. return true;
  7532. }
  7533. }
  7534. return false;
  7535. }
  7536. //-----------------------------------------------------------------------------
  7537. const CEconItemSeriesDefinition *CEconItemSchema::GetItemSeriesDefinition( int iSeries ) const
  7538. {
  7539. int iIndex = m_mapItemSeries.Find( iSeries );
  7540. if ( m_mapItemSeries.IsValidIndex( iIndex ) )
  7541. return &m_mapItemSeries[iIndex];
  7542. return NULL;
  7543. }
  7544. //-----------------------------------------------------------------------------
  7545. // Purpose: Gets an item definition for the specified definition index
  7546. // Input: iItemIndex - The index of the desired definition.
  7547. // Output: A pointer to the desired definition, or NULL if it is not found.
  7548. //-----------------------------------------------------------------------------
  7549. CEconItemDefinition *CEconItemSchema::GetItemDefinition( int iItemIndex )
  7550. {
  7551. #if defined(CLIENT_DLL) || defined(GAME_DLL)
  7552. #if !defined(CSTRIKE_DLL)
  7553. AssertMsg( GetDefaultItemDefinition(), "No default item definition set up for item schema." );
  7554. #endif // CSTRIKE_DLL
  7555. #endif // defined(CLIENT_DLL) || defined(GAME_DLL)
  7556. int iIndex = m_mapItems.Find( iItemIndex );
  7557. if ( m_mapItems.IsValidIndex( iIndex ) )
  7558. return m_mapItems[iIndex];
  7559. #if defined( GC_DLL ) || defined( EXTERNALTESTS_DLL )
  7560. return NULL;
  7561. #else // !GC_DLL
  7562. if ( GetDefaultItemDefinition() )
  7563. return GetDefaultItemDefinition();
  7564. #if !defined(CSTRIKE_DLL)
  7565. // We shouldn't ever get down here, but all the same returning a valid pointer is very slightly
  7566. // a better plan than returning an invalid pointer to code that won't check to see if it's valid.
  7567. static CEconItemDefinition *s_pEmptyDefinition = CreateEconItemDefinition();
  7568. return s_pEmptyDefinition;
  7569. #else
  7570. return NULL;
  7571. #endif // CSTRIKE_DLL
  7572. #endif // GC_DLL
  7573. }
  7574. const CEconItemDefinition *CEconItemSchema::GetItemDefinition( int iItemIndex ) const
  7575. {
  7576. return const_cast<CEconItemSchema *>(this)->GetItemDefinition( iItemIndex );
  7577. }
  7578. //-----------------------------------------------------------------------------
  7579. // Purpose: Gets an item definition that has a name matching the specified name.
  7580. // Input: pszDefName - The name of the desired definition.
  7581. // Output: A pointer to the desired definition, or NULL if it is not found.
  7582. //-----------------------------------------------------------------------------
  7583. CEconItemDefinition *CEconItemSchema::GetItemDefinitionByName( const char *pszDefName )
  7584. {
  7585. // This shouldn't happen, but let's not crash if it ever does.
  7586. Assert( pszDefName != NULL );
  7587. if ( pszDefName == NULL )
  7588. return NULL;
  7589. FOR_EACH_MAP_FAST( m_mapItems, i )
  7590. {
  7591. if ( V_stricmp( pszDefName, m_mapItems[i]->GetDefinitionName()) == 0 )
  7592. return m_mapItems[i];
  7593. }
  7594. return NULL;
  7595. }
  7596. const CEconItemDefinition *CEconItemSchema::GetItemDefinitionByName( const char *pszDefName ) const
  7597. {
  7598. return const_cast<CEconItemSchema *>(this)->GetItemDefinitionByName( pszDefName );
  7599. }
  7600. #ifdef GC_DLL
  7601. random_attrib_t *CEconItemSchema::GetRandomAttributeTemplateByName( const char *pszAttrTemplateName ) const
  7602. {
  7603. int index = m_dictRandomAttributeTemplates.Find( pszAttrTemplateName );
  7604. if ( index != m_dictRandomAttributeTemplates.InvalidIndex() )
  7605. {
  7606. return m_dictRandomAttributeTemplates[index];
  7607. }
  7608. return NULL;
  7609. }
  7610. #endif // GC_DLL
  7611. //-----------------------------------------------------------------------------
  7612. // Purpose: Gets an attribute definition for an index
  7613. // Input: iAttribIndex - The index of the desired definition.
  7614. // Output: A pointer to the desired definition, or NULL if it is not found.
  7615. //-----------------------------------------------------------------------------
  7616. CEconItemAttributeDefinition *CEconItemSchema::GetAttributeDefinition( int iAttribIndex )
  7617. {
  7618. int iIndex = m_mapAttributes.Find( iAttribIndex );
  7619. if ( m_mapAttributes.IsValidIndex( iIndex ) )
  7620. return &m_mapAttributes[iIndex];
  7621. return NULL;
  7622. }
  7623. const CEconItemAttributeDefinition *CEconItemSchema::GetAttributeDefinition( int iAttribIndex ) const
  7624. {
  7625. return const_cast<CEconItemSchema *>(this)->GetAttributeDefinition( iAttribIndex );
  7626. }
  7627. CEconItemAttributeDefinition *CEconItemSchema::GetAttributeDefinitionByName( const char *pszDefName )
  7628. {
  7629. Assert( pszDefName );
  7630. if ( !pszDefName )
  7631. return NULL;
  7632. VPROF_BUDGET( "CEconItemSchema::GetAttributeDefinitionByName", VPROF_BUDGETGROUP_STEAM );
  7633. FOR_EACH_MAP_FAST( m_mapAttributes, i )
  7634. {
  7635. Assert( m_mapAttributes[i].GetDefinitionName() );
  7636. if ( !m_mapAttributes[i].GetDefinitionName() )
  7637. continue;
  7638. if ( V_stricmp( pszDefName, m_mapAttributes[i].GetDefinitionName() ) == 0 )
  7639. return &m_mapAttributes[i];
  7640. }
  7641. return NULL;
  7642. }
  7643. const CEconItemAttributeDefinition *CEconItemSchema::GetAttributeDefinitionByName( const char *pszDefName ) const
  7644. {
  7645. return const_cast<CEconItemSchema *>(this)->GetAttributeDefinitionByName( pszDefName );
  7646. }
  7647. //-----------------------------------------------------------------------------
  7648. // Purpose: Gets a recipe definition for an index
  7649. // Input: iRecipeIndex - The index of the desired definition.
  7650. // Output: A pointer to the desired definition, or NULL if it is not found.
  7651. //-----------------------------------------------------------------------------
  7652. CEconCraftingRecipeDefinition *CEconItemSchema::GetRecipeDefinition( int iRecipeIndex )
  7653. {
  7654. int iIndex = m_mapRecipes.Find( iRecipeIndex );
  7655. if ( m_mapRecipes.IsValidIndex( iIndex ) )
  7656. return m_mapRecipes[iIndex];
  7657. return NULL;
  7658. }
  7659. //-----------------------------------------------------------------------------
  7660. // Purpose:
  7661. //-----------------------------------------------------------------------------
  7662. CEconColorDefinition *CEconItemSchema::GetColorDefinitionByName( const char *pszDefName )
  7663. {
  7664. FOR_EACH_VEC( m_vecColorDefs, i )
  7665. {
  7666. if ( !Q_stricmp( m_vecColorDefs[i]->GetName(), pszDefName ) )
  7667. return m_vecColorDefs[i];
  7668. }
  7669. return NULL;
  7670. }
  7671. const CEconColorDefinition *CEconItemSchema::GetColorDefinitionByName( const char *pszDefName ) const
  7672. {
  7673. return const_cast<CEconItemSchema *>(this)->GetColorDefinitionByName( pszDefName );
  7674. }
  7675. #ifdef CLIENT_DLL
  7676. //-----------------------------------------------------------------------------
  7677. // Purpose:
  7678. //-----------------------------------------------------------------------------
  7679. const char *CEconItemSchema::GetSteamPackageLocalizationToken( uint32 unPackageId ) const
  7680. {
  7681. SteamPackageLocalizationTokenMap_t::IndexType_t i = m_mapSteamPackageLocalizationTokens.Find( unPackageId );
  7682. if ( m_mapSteamPackageLocalizationTokens.IsValidIndex( i ) )
  7683. return m_mapSteamPackageLocalizationTokens[i];
  7684. return NULL;
  7685. }
  7686. #endif // CLIENT_DLL
  7687. //-----------------------------------------------------------------------------
  7688. // Purpose: Return the attribute specified attachedparticlesystem_t* associated with the given id.
  7689. //-----------------------------------------------------------------------------
  7690. attachedparticlesystem_t* CEconItemSchema::GetAttributeControlledParticleSystem( int id )
  7691. {
  7692. int iIndex = m_mapAttributeControlledParticleSystems.Find( id );
  7693. if ( m_mapAttributeControlledParticleSystems.IsValidIndex( iIndex ) )
  7694. return &m_mapAttributeControlledParticleSystems[iIndex];
  7695. return NULL;
  7696. }
  7697. attachedparticlesystem_t* CEconItemSchema::FindAttributeControlledParticleSystem( const char *pchSystemName )
  7698. {
  7699. FOR_EACH_MAP_FAST( m_mapAttributeControlledParticleSystems, nSystem )
  7700. {
  7701. if( !Q_stricmp( m_mapAttributeControlledParticleSystems[nSystem].pszSystemName, pchSystemName ) )
  7702. return &m_mapAttributeControlledParticleSystems[nSystem];
  7703. }
  7704. return NULL;
  7705. }
  7706. #if defined(CLIENT_DLL) || defined(GAME_DLL)
  7707. bool CEconItemSchema::SetupPreviewItemDefinition( KeyValues *pKV )
  7708. {
  7709. int nMapIndex = m_mapItems.Find( PREVIEW_ITEM_DEFINITION_INDEX );
  7710. if ( !m_mapItems.IsValidIndex( nMapIndex ) )
  7711. {
  7712. nMapIndex = m_mapItems.Insert( PREVIEW_ITEM_DEFINITION_INDEX, CreateEconItemDefinition() );
  7713. }
  7714. CEconItemDefinition *pItemDef = m_mapItems[ nMapIndex ];
  7715. return pItemDef->BInitFromKV( pKV );
  7716. }
  7717. #endif // defined(CLIENT_DLL) || defined(GAME_DLL)
  7718. #ifdef GC_DLL
  7719. //-----------------------------------------------------------------------------
  7720. // Purpose: Returns all the foreign item imports for an app ID
  7721. //-----------------------------------------------------------------------------
  7722. const CEconItemDefinition *CEconItemSchema::GetAppItemImport( AppId_t unAppID, uint16 usDefIndex ) const
  7723. {
  7724. int i = m_mapForeignImports.Find( unAppID );
  7725. if( m_mapForeignImports.IsValidIndex( i ) )
  7726. return m_mapForeignImports[i]->FindMapping( usDefIndex );
  7727. else
  7728. return NULL;
  7729. }
  7730. //-----------------------------------------------------------------------------
  7731. // Purpose: Returns all the foreign item imports for an app ID
  7732. //-----------------------------------------------------------------------------
  7733. bool CEconItemSchema::BInitForeignImports( CUtlVector<CUtlString> *pVecErrors )
  7734. {
  7735. FOR_EACH_MAP_FAST( m_mapItems, nItem )
  7736. {
  7737. CEconItemDefinition *pDefn = m_mapItems[nItem];
  7738. KeyValues *pkvImport = pDefn->GetDefinitionKey( "import_from" );
  7739. if( !pkvImport )
  7740. continue;
  7741. FOR_EACH_VALUE( pkvImport, pkvApp )
  7742. {
  7743. CForeignAppImports *pAppImports = FindOrAddAppImports( Q_atoi( pkvApp->GetName() ) );
  7744. pAppImports->AddMapping( pkvApp->GetInt(), pDefn );
  7745. }
  7746. }
  7747. return true;
  7748. }
  7749. //-----------------------------------------------------------------------------
  7750. // Purpose: Returns all the foreign item imports for an app ID
  7751. //-----------------------------------------------------------------------------
  7752. CForeignAppImports *CEconItemSchema::FindOrAddAppImports( AppId_t unAppID )
  7753. {
  7754. int i = m_mapForeignImports.Find( unAppID );
  7755. if( m_mapForeignImports.IsValidIndex( i ) )
  7756. return m_mapForeignImports[i];
  7757. else
  7758. {
  7759. m_vecForeignApps.AddToTail( unAppID );
  7760. CForeignAppImports *pApp = new CForeignAppImports();
  7761. m_mapForeignImports.Insert( unAppID, pApp );
  7762. return pApp;
  7763. }
  7764. }
  7765. #endif // GC_DLL
  7766. bool CEconItemSchema::BCanStrangeFilterApplyToStrangeSlotInItem( uint32 /*strange_event_restriction_t*/ unRestrictionType, uint32 unRestrictionValue, const IEconItemInterface *pItem, int iStrangeSlot, uint32 *out_pOptionalScoreType ) const
  7767. {
  7768. Assert( unRestrictionType != kStrangeEventRestriction_None );
  7769. // Do we have a type for this slot? If not, move on, with the exception: all user-custom scores
  7770. // we expect to have types, but certain weapons may not specify a base type and just use
  7771. // kills implicitly.
  7772. uint32 unStrangeScoreTypeBits = kKillEaterEvent_PlayerKill;
  7773. if ( !pItem->FindAttribute( GetKillEaterAttr_Type( iStrangeSlot ), &unStrangeScoreTypeBits ) && iStrangeSlot != 0 )
  7774. return false;
  7775. // Do we have a restriction already in this slot? If so, move on.
  7776. if ( pItem->FindAttribute( GetKillEaterAttr_Restriction( iStrangeSlot ) ) )
  7777. return false;
  7778. // We've found an open slot. Make sure that adding our restriction to this slot
  7779. // won't result in a duplicate score-type/restriction-type entry.
  7780. for ( int j = 0; j < GetKillEaterAttrCount(); j++ )
  7781. {
  7782. // Don't compare against ourself.
  7783. if ( iStrangeSlot == j )
  7784. continue;
  7785. // Ignore this entry if we don't have a score type or if the score type differs from
  7786. // our search criteria above.
  7787. uint32 unAltStrangeScoreType;
  7788. if ( !pItem->FindAttribute( GetKillEaterAttr_Type( j ), &unAltStrangeScoreType ) ||
  7789. unAltStrangeScoreType != unStrangeScoreTypeBits )
  7790. {
  7791. continue;
  7792. }
  7793. // This entry does have the same type, so tag us as a duplicate if we also have the same
  7794. // restriction that we're trying to apply.
  7795. uint32 unAltRestrictionType;
  7796. uint32 unAltRestrictionValue;
  7797. if ( pItem->FindAttribute( GetKillEaterAttr_Restriction( j ), &unAltRestrictionType ) &&
  7798. unAltRestrictionType == unRestrictionType &&
  7799. pItem->FindAttribute( GetKillEaterAttr_RestrictionValue( j ), &unAltRestrictionValue ) &&
  7800. unAltRestrictionValue == unRestrictionValue )
  7801. {
  7802. return false;
  7803. }
  7804. }
  7805. if ( out_pOptionalScoreType )
  7806. {
  7807. *out_pOptionalScoreType = *(float *)&unStrangeScoreTypeBits;
  7808. }
  7809. // Everything seems alright.
  7810. return true;
  7811. }
  7812. //-----------------------------------------------------------------------------
  7813. // Purpose: Ensure that all of our internal structures are consistent, and
  7814. // account for all memory that we've allocated.
  7815. // Input: validator - Our global validator object
  7816. // pchName - Our name (typically a member var in our container)
  7817. //-----------------------------------------------------------------------------
  7818. #ifdef DBGFLAG_VALIDATE
  7819. void CEconItemSchema::Validate( CValidator &validator, const char *pchName )
  7820. {
  7821. VALIDATE_SCOPE();
  7822. ValidateObj( m_mapQualities );
  7823. FOR_EACH_MAP_FAST( m_mapQualities, i )
  7824. {
  7825. ValidateObj( m_mapQualities[i] );
  7826. }
  7827. ValidateObj( m_mapItems );
  7828. FOR_EACH_MAP_FAST( m_mapItems, i )
  7829. {
  7830. ValidateObj( m_mapItems[i] );
  7831. }
  7832. ValidateObj( m_mapUpgradeableBaseItems );
  7833. FOR_EACH_MAP_FAST( m_mapUpgradeableBaseItems, i )
  7834. {
  7835. ValidateObj( m_mapUpgradeableBaseItems[i] );
  7836. }
  7837. ValidateObj( m_mapAttributes );
  7838. FOR_EACH_MAP_FAST( m_mapAttributes, i )
  7839. {
  7840. ValidateObj( m_mapAttributes[i] );
  7841. }
  7842. ValidateObj( m_mapRecipes );
  7843. FOR_EACH_MAP_FAST( m_mapRecipes, i )
  7844. {
  7845. ValidateObj( m_mapRecipes[i] );
  7846. }
  7847. FOR_EACH_VEC( m_vecTimedRewards, i )
  7848. {
  7849. ValidateObj( m_vecTimedRewards[i] );
  7850. }
  7851. ValidateObj( m_vecTimedRewards );
  7852. }
  7853. #endif // DBGFLAG_VALIDATE
  7854. #ifdef GC_DLL
  7855. //-----------------------------------------------------------------------------
  7856. // Purpose:
  7857. //-----------------------------------------------------------------------------
  7858. bool CEconItemSchema::BInitExperiements( KeyValues *pKVExperiments, CUtlVector<CUtlString> *pVecErrors )
  7859. {
  7860. m_vecExperiments.RemoveAll();
  7861. if ( NULL != pKVExperiments )
  7862. {
  7863. FOR_EACH_TRUE_SUBKEY( pKVExperiments, pKVEntry )
  7864. {
  7865. const char *listName = pKVEntry->GetName();
  7866. SCHEMA_INIT_CHECK( listName != NULL, "All experiments must have titles.");
  7867. int idx = m_vecExperiments.AddToTail();
  7868. SCHEMA_INIT_SUBSTEP( m_vecExperiments[idx].BInitFromKV( pKVEntry, pVecErrors ) );
  7869. }
  7870. }
  7871. return SCHEMA_INIT_SUCCESS();
  7872. }
  7873. //-----------------------------------------------------------------------------
  7874. // CExperimentDefinition
  7875. //-----------------------------------------------------------------------------
  7876. CExperimentDefinition::CExperimentDefinition( void )
  7877. : m_bEnabled( false )
  7878. , m_unExperimentID( 0 )
  7879. , m_unNumParticipants( 0 )
  7880. , m_unMaxParticipants( 0 )
  7881. , m_pKeyValues( NULL )
  7882. {
  7883. }
  7884. CExperimentDefinition::~CExperimentDefinition( void )
  7885. {
  7886. if ( m_pKeyValues )
  7887. {
  7888. m_pKeyValues->deleteThis();
  7889. }
  7890. }
  7891. bool CExperimentDefinition::BInitFromKV( KeyValues *pKVExperiment, CUtlVector<CUtlString> *pVecErrors )
  7892. {
  7893. m_pKeyValues = pKVExperiment->MakeCopy();
  7894. m_unExperimentID = Q_atoi( m_pKeyValues->GetName() );
  7895. m_bEnabled = m_pKeyValues->GetBool( "enabled" );
  7896. m_unNumParticipants = 0;
  7897. m_unMaxParticipants = 0;
  7898. KeyValues *pKVGroups = m_pKeyValues->FindKey( "groups" );
  7899. if ( pKVGroups )
  7900. {
  7901. FOR_EACH_TRUE_SUBKEY( pKVGroups, pKVEntry )
  7902. {
  7903. int idx = m_vecGroups.AddToTail();
  7904. experiment_group_t &group = m_vecGroups[idx];
  7905. group.m_pKeyValues = pKVEntry;
  7906. group.m_pName = pKVEntry->GetName();
  7907. group.m_unNumParticipants = 0;
  7908. group.m_unMaxParticipants = pKVEntry->GetInt( "num_participants", 0 );
  7909. m_unMaxParticipants += group.m_unMaxParticipants;
  7910. }
  7911. }
  7912. return SCHEMA_INIT_SUCCESS();
  7913. }
  7914. bool CExperimentDefinition::ChooseGroup( uint32 &unGroup )
  7915. {
  7916. CUtlVector< uint32 > vecGroupIndices;
  7917. FOR_EACH_VEC( m_vecGroups, i )
  7918. {
  7919. experiment_group_t &group = m_vecGroups[i];
  7920. if ( group.m_unNumParticipants < group.m_unMaxParticipants )
  7921. {
  7922. vecGroupIndices.AddToTail( i );
  7923. }
  7924. }
  7925. if ( vecGroupIndices.Count() == 0 )
  7926. {
  7927. return false;
  7928. }
  7929. uint32 idx = vecGroupIndices[ RandomInt( 0, vecGroupIndices.Count() - 1 ) ];
  7930. experiment_group_t &group = m_vecGroups[ idx ];
  7931. ++group.m_unNumParticipants;
  7932. unGroup = idx;
  7933. return true;
  7934. }
  7935. //-----------------------------------------------------------------------------
  7936. // Purpose: Give the chance after the schema has been initialized to sanity-check
  7937. // individual parts or interactions before moving forward.
  7938. //-----------------------------------------------------------------------------
  7939. #ifdef TF_GC_DLL
  7940. static bool BTestToolApplicability( CUtlVector<CUtlString> *pVecErrors )
  7941. {
  7942. static CSchemaItemDefHandle pItem_TeamPaint ( "Paint Can Team Color" ), // tools
  7943. pItem_Paint ( "Paint Can 14" ),
  7944. pItem_DescriptionTag ( "Description Tag" ),
  7945. pItem_NameTag ( "Name Tag" ),
  7946. pItem_Key ( "Decoder Ring" ),
  7947. pItem_SummerKey ( "Summer Key" ),
  7948. pItem_GiftWrap ( "Gift Wrap" ),
  7949. pItem_CustomTextureTool ( "Customize Texture Tool" ),
  7950. pItem_SupplyCrateGeneric ( "Supply Crate 2" ), // tool targets
  7951. pItem_SummerCrate ( "Summer Crate" ),
  7952. pItem_PaintableItem ( "Summer Hat" ),
  7953. //pItem_TeamPaintableItem ( "" ),
  7954. pItem_UnpaintableItem ( "Big Steel Jaw of Summer Fun" ),
  7955. //pItem_UnnameableWeapon ( "" ),
  7956. pItem_NameableWeapon ( "The Axtinguisher" ),
  7957. pItem_GiftWrappableItem ( "Supply Crate 3" ),
  7958. pItem_NonGiftWrappableItem ( "Wrapped Gift" ),
  7959. pItem_StampableObject ( "The Conscientious Objector" ),
  7960. pItem_NonstampableObject ( "Spiral Sallet" );
  7961. struct ToolValidityTest_t
  7962. {
  7963. const CEconItemDefinition *m_pTool;
  7964. const CEconItemDefinition *m_pTarget;
  7965. bool m_bExpectedValidity;
  7966. };
  7967. ToolValidityTest_t definitionTests[] =
  7968. {
  7969. { pItem_TeamPaint, pItem_TeamPaint, false },
  7970. { pItem_TeamPaint, pItem_PaintableItem, true },
  7971. //{ pItem_TeamPaint, pItem_TeamPaintableItem, true },
  7972. { pItem_TeamPaint, pItem_UnpaintableItem, false },
  7973. { pItem_Paint, pItem_PaintableItem, true },
  7974. //{ pItem_Paint, pItem_TeamPaintableItem, false },
  7975. { pItem_Paint, pItem_UnpaintableItem, false },
  7976. { pItem_Paint, pItem_SupplyCrateGeneric, false },
  7977. { pItem_Key, pItem_SupplyCrateGeneric, true },
  7978. { pItem_Key, pItem_SummerCrate, false },
  7979. { pItem_SummerKey, pItem_SupplyCrateGeneric, true }, // summer keys in staging are now regular keys and should...
  7980. { pItem_SummerKey, pItem_SummerCrate, false }, // ...be able to open regular crates
  7981. { pItem_NameTag, pItem_NameableWeapon, true },
  7982. //{ pItem_NameTag, pItem_UnnameableWeapon, false },
  7983. { pItem_DescriptionTag, pItem_NameableWeapon, true },
  7984. //{ pItem_DescriptionTag, pItem_UnnameableWeapon, false },
  7985. { pItem_CustomTextureTool, pItem_StampableObject, true },
  7986. { pItem_CustomTextureTool, pItem_NonstampableObject, false },
  7987. };
  7988. bool bAllSuccess = true;
  7989. for ( int i = 0; i < ARRAYSIZE( definitionTests ); i++ )
  7990. {
  7991. const GameItemDefinition_t *pToolDef = dynamic_cast<const GameItemDefinition_t *>( definitionTests[i].m_pTool ),
  7992. *pTargetDef = dynamic_cast<const GameItemDefinition_t *>( definitionTests[i].m_pTarget );
  7993. if ( !pToolDef )
  7994. {
  7995. bAllSuccess = false;
  7996. pVecErrors->AddToTail( CFmtStr( "Tool validity test %i failed: Tool is NULL.", i ).Access() );
  7997. continue;
  7998. }
  7999. if ( !pTargetDef )
  8000. {
  8001. bAllSuccess = false;
  8002. pVecErrors->AddToTail( CFmtStr( "Tool validity test %i failed: Target is NULL.", i ).Access() );
  8003. continue;
  8004. }
  8005. if ( CEconSharedToolSupport::ToolCanApplyToDefinition( pToolDef, pTargetDef ) != definitionTests[i].m_bExpectedValidity )
  8006. {
  8007. bAllSuccess = false;
  8008. pVecErrors->AddToTail( CFmtStr( "Tool validity test %i failed: %s %s have been able to apply to %s.",
  8009. i,
  8010. pToolDef->GetDefinitionName(),
  8011. definitionTests[i].m_bExpectedValidity ? "should" : "shouldn't",
  8012. pTargetDef->GetDefinitionName() ).Access() );
  8013. }
  8014. }
  8015. // These tests require actual instances of the item--not just the definitions.
  8016. ToolValidityTest_t interfaceTests[] =
  8017. {
  8018. { pItem_GiftWrap, pItem_GiftWrappableItem, true },
  8019. { pItem_GiftWrap, pItem_NonGiftWrappableItem, false },
  8020. };
  8021. // Skip if we already have failures.
  8022. if ( bAllSuccess )
  8023. {
  8024. for ( int i = 0; i < ARRAYSIZE( interfaceTests ); i++ )
  8025. {
  8026. const GameItemDefinition_t *pToolDef = dynamic_cast< const GameItemDefinition_t * >( interfaceTests[ i ].m_pTool ),
  8027. *pTargetDef = dynamic_cast< const GameItemDefinition_t * >( interfaceTests[ i ].m_pTarget );
  8028. if ( !pToolDef )
  8029. {
  8030. bAllSuccess = false;
  8031. pVecErrors->AddToTail( CFmtStr( "Tool validity test %i failed: Tool is NULL.", i ).Access() );
  8032. continue;
  8033. }
  8034. if ( !pTargetDef )
  8035. {
  8036. bAllSuccess = false;
  8037. pVecErrors->AddToTail( CFmtStr( "Tool validity test %i failed: Target is NULL.", i ).Access() );
  8038. continue;
  8039. }
  8040. CEconItem *pTool = GEconManager()->GetItemFactory().CreateSpecificItem( ( const CEconGameAccount *) NULL, pToolDef->GetDefinitionIndex() );
  8041. CEconItem *pTarget = GEconManager()->GetItemFactory().CreateSpecificItem( ( const CEconGameAccount *) NULL, pTargetDef->GetDefinitionIndex() );
  8042. if ( CEconSharedToolSupport::ToolCanApplyTo( pTool, pTarget ) != interfaceTests[ i ].m_bExpectedValidity )
  8043. {
  8044. bAllSuccess = false;
  8045. pVecErrors->AddToTail( CFmtStr( "Tool validity test %i failed: %s %s have been able to apply to %s.",
  8046. i,
  8047. pToolDef->GetDefinitionName(),
  8048. interfaceTests[ i ].m_bExpectedValidity ? "should" : "shouldn't",
  8049. pTargetDef->GetDefinitionName() ).Access() );
  8050. }
  8051. delete pTool;
  8052. delete pTarget;
  8053. }
  8054. }
  8055. return bAllSuccess;
  8056. }
  8057. #endif // TF_GC_DLL
  8058. #endif // defined(GC_DLL)
  8059. bool CEconItemSchema::BPostSchemaInit( CUtlVector<CUtlString> *pVecErrors ) const
  8060. {
  8061. bool bAllSuccess = true;
  8062. // Make sure all of our tools are valid. We have to do this after the whole schema is initialized so
  8063. // that we don't run into circular reference problems with items referencing loot lists that reference
  8064. // items, etc.
  8065. FOR_EACH_MAP_FAST( m_mapItems, i )
  8066. {
  8067. const CEconItemDefinition *pItemDef = m_mapItems[i];
  8068. const IEconTool *pTool = pItemDef->GetEconTool();
  8069. if ( pTool && !const_cast<IEconTool *>( pTool )->BFinishInitialization() )
  8070. {
  8071. #ifdef GC_DLL
  8072. bAllSuccess = false;
  8073. pVecErrors->AddToTail( CFmtStr( "BPostSchemaInit(): tool '%s' is invalid.", pItemDef->GetDefinitionName() ).Get() );
  8074. #endif // GC_DLL
  8075. }
  8076. #if TF_GC_DLL
  8077. else
  8078. {
  8079. // all cosmetic items should have these two attributes from the
  8080. // cosmetic_killeater_attribs prefab in case we ever try to drop them as Strange
  8081. static CSchemaAttributeDefHandle pAttribDef_KillEaterScoreType( "kill eater score type" );
  8082. static CSchemaAttributeDefHandle pAttribDef_KillEaterKillType( "kill eater kill type" );
  8083. const CTFItemDefinition *pTFItemDef = assert_cast< const CTFItemDefinition* >( pItemDef );
  8084. if ( pTFItemDef )
  8085. {
  8086. int nSlot = pTFItemDef->GetLoadoutSlot( 0 ); // 0 gives use the default slot
  8087. if ( ( nSlot == LOADOUT_POSITION_HEAD ) || ( nSlot == LOADOUT_POSITION_MISC ) || ( nSlot == LOADOUT_POSITION_MISC2 ) )
  8088. {
  8089. bool bFoundScore = false;
  8090. bool bFoundKill = false;
  8091. FOR_EACH_VEC( pTFItemDef->GetStaticAttributes(), iIndex )
  8092. {
  8093. const static_attrib_t& staticAttrib = pTFItemDef->GetStaticAttributes()[iIndex];
  8094. const CEconItemAttributeDefinition *pAttrDef = GetItemSchema()->GetAttributeDefinition( staticAttrib.iDefIndex );
  8095. if ( pAttrDef == pAttribDef_KillEaterScoreType )
  8096. {
  8097. bFoundScore = true;
  8098. }
  8099. else if ( pAttrDef == pAttribDef_KillEaterKillType )
  8100. {
  8101. bFoundKill = true;
  8102. }
  8103. }
  8104. if ( !bFoundScore || !bFoundKill )
  8105. {
  8106. bAllSuccess = false;
  8107. pVecErrors->AddToTail( CFmtStr( "BPostSchemaInit(): '%s' is missing the standard cosmetic killeater attributes.", pItemDef->GetDefinitionName() ).Get() );
  8108. }
  8109. }
  8110. }
  8111. }
  8112. #endif // TF_GC_DLL
  8113. }
  8114. #if TF_GC_DLL
  8115. // Make sure our tool application code validity works correctly.
  8116. if ( !BTestToolApplicability( pVecErrors ) )
  8117. {
  8118. bAllSuccess = false;
  8119. pVecErrors->AddToTail( "BPostSchemaInit(): error with tool application validity." );
  8120. }
  8121. const CEconLootListDefinition* pUnusualLootlist = GetItemSchema()->GetLootListByName( "all_particle_hats" );
  8122. if ( !pUnusualLootlist )
  8123. {
  8124. bAllSuccess = false;
  8125. pVecErrors->AddToTail( "No lootlist \"all_particle_hats\"" );
  8126. }
  8127. else
  8128. {
  8129. auto& contents = pUnusualLootlist->GetLootListContents();
  8130. FOR_EACH_VEC( contents, i )
  8131. {
  8132. if ( contents[ i ].m_iItemOrLootlistDef > 0 )
  8133. {
  8134. const CEconItemDefinition* pItemDef = GetItemDefinition( contents[ i ].m_iItemOrLootlistDef );
  8135. if ( !( pItemDef->GetEquipRegionMask() & GetItemSchema()->GetEquipRegionBitMaskByName( "hat" ) )
  8136. && !( pItemDef->GetEquipRegionMask() & GetItemSchema()->GetEquipRegionBitMaskByName( "whole_head" ) ) )
  8137. {
  8138. bAllSuccess = false;
  8139. 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() );
  8140. }
  8141. }
  8142. }
  8143. }
  8144. #endif // TF_GC_DLL
  8145. return bAllSuccess;
  8146. }
  8147. //-----------------------------------------------------------------------------
  8148. //
  8149. //-----------------------------------------------------------------------------
  8150. CItemLevelingDefinition::CItemLevelingDefinition( void )
  8151. {
  8152. }
  8153. //-----------------------------------------------------------------------------
  8154. // Purpose: Copy constructor
  8155. //-----------------------------------------------------------------------------
  8156. CItemLevelingDefinition::CItemLevelingDefinition( const CItemLevelingDefinition &that )
  8157. {
  8158. (*this) = that;
  8159. }
  8160. //-----------------------------------------------------------------------------
  8161. //
  8162. //-----------------------------------------------------------------------------
  8163. CItemLevelingDefinition::~CItemLevelingDefinition()
  8164. {
  8165. // Free up strdup() memory.
  8166. free( m_pszLocalizedName_LocalStorage );
  8167. }
  8168. //-----------------------------------------------------------------------------
  8169. // Purpose: Operator=
  8170. //-----------------------------------------------------------------------------
  8171. CItemLevelingDefinition &CItemLevelingDefinition::operator=( const CItemLevelingDefinition &other )
  8172. {
  8173. m_unLevel = other.m_unLevel;
  8174. m_unRequiredScore = other.m_unRequiredScore;
  8175. m_pszLocalizedName_LocalStorage = strdup( other.m_pszLocalizedName_LocalStorage );
  8176. return *this;
  8177. }
  8178. //-----------------------------------------------------------------------------
  8179. //
  8180. //-----------------------------------------------------------------------------
  8181. bool CItemLevelingDefinition::BInitFromKV( KeyValues *pKVItemLevel, const char *pszLevelBlockName, CUtlVector<CUtlString> *pVecErrors )
  8182. {
  8183. m_unLevel = Q_atoi( pKVItemLevel->GetName() );
  8184. m_unRequiredScore = pKVItemLevel->GetInt( "score" );
  8185. m_pszLocalizedName_LocalStorage = strdup( pKVItemLevel->GetString( "rank_name", CFmtStr( "%s%i", pszLevelBlockName, m_unLevel ).Access() ) );
  8186. return SCHEMA_INIT_SUCCESS();
  8187. }
  8188. #ifdef GC_DLL
  8189. EUniverse GetUniverse()
  8190. {
  8191. return GGCHost()->GetUniverse();
  8192. }
  8193. #endif // GC_DLL