· 5 years ago · Jul 09, 2020, 03:04 AM
1cstrike15_src-master/game/shared/econ/econ_item_schema.cpp
2const CSchemaAttributeDefHandle& GetStickerAttributeDefHandle( int attrNum, EStickerAttributeType type )
3
4//====== Copyright ?, Valve Corporation, All rights reserved. =======
5//
6// Purpose: EconItemSchema: Defines a schema for econ items
7//
8//=============================================================================
9
10#include "cbase.h"
11#include "econ_item_schema.h"
12#include "tier1/fmtstr.h"
13#include "tier2/tier2.h"
14#include "filesystem.h"
15#include "schemainitutils.h"
16#include "cstrike15_gcconstants.h"
17
18#include <google/protobuf/text_format.h>
19
20#if defined(CLIENT_DLL) || defined(GAME_DLL)
21 #include "econ_item_system.h"
22 #include "econ_item.h"
23 #include "activitylist.h"
24
25 #if defined(TF_CLIENT_DLL) || defined(TF_DLL)
26 #include "tf_gcmessages.h"
27 #endif
28#endif
29
30#if defined(CSTRIKE_CLIENT_DLL) || defined(CSTRIKE_DLL)
31#include "cstrike15_gcmessages.pb.h"
32#endif
33
34#include "gametypes.h"
35
36
37// memdbgon must be the last include file in a .cpp file!!!
38#include "tier0/memdbgon.h"
39
40
41using namespace GCSDK;
42
43#if defined(GAME_DLL)
44void PrecacheParticleFileAndSystems( const char *pParticleSystemFile );
45#endif
46
47#if defined(CLIENT_DLL)
48bool CWebResource::s_Initialized = false;
49#endif
50
51CEconItemSchema & GEconItemSchema()
52{
53#if defined( EXTERNALTESTS_DLL )
54 static CEconItemSchema g_econItemSchema;
55 return g_econItemSchema;
56#else
57 return *ItemSystem()->GetItemSchema();
58#endif
59}
60
61const char * g_arrQuestVars[ k_EQuestVar_Last ] = { "" };
62 /** Removed for partner depot **/
63
64
65static void HelperValidateLocalizationStringToken( char const *pszToken )
66{
67#ifdef CLIENT_DLL
68 if ( pszToken && *pszToken == '#' )
69 AssertMsg1( g_pVGuiLocalize->Find( pszToken ), "Schema is referencing a non-existant token: %s", pszToken );
70#endif
71}
72
73const char *g_szDropTypeStrings[] =
74{
75 "", // Blank and none mean the same thing: stay attached to the body.
76 "none",
77 "drop", // The item drops off the body.
78 "break", // Not implemented, but an example of a type that could be added.
79};
80
81#if defined(CLIENT_DLL) || defined(GAME_DLL)
82// Used to convert strings to ints for wearable animation types
83const char *g_WearableAnimTypeStrings[NUM_WAP_TYPES] =
84{
85 "on_spawn", // WAP_ON_SPAWN,
86 "start_building", // WAP_START_BUILDING,
87 "stop_building", // WAP_STOP_BUILDING,
88};
89#endif
90
91const char *g_AttributeDescriptionFormats[] =
92{
93 "value_is_percentage", // ATTDESCFORM_VALUE_IS_PERCENTAGE,
94 "value_is_inverted_percentage", // ATTDESCFORM_VALUE_IS_INVERTED_PERCENTAGE
95 "value_is_additive", // ATTDESCFORM_VALUE_IS_ADDITIVE
96 "value_is_additive_percentage", // ATTDESCFORM_VALUE_IS_ADDITIVE_PERCENTAGE
97 "value_is_or", // ATTDESCFORM_VALUE_IS_OR
98 "value_is_date", // ATTDESCFORM_VALUE_IS_DATE
99 "value_is_account_id", // ATTDESCFORM_VALUE_IS_ACCOUNT_ID
100 "value_is_particle_index", // ATTDESCFORM_VALUE_IS_PARTICLE_INDEX -> Could change to "string index"
101 "value_is_item_def", // ATTDESCFORM_VALUE_IS_ITEM_DEF
102 "value_is_color", // ATTDESCFORM_VALUE_IS_COLOR
103 "value_is_game_time", // ATTDESCFORM_VALUE_IS_GAME_TIME
104 "value_is_mins_as_hours", // ATTDESCFORM_VALUE_IS_MINS_AS_HOURS
105 "value_is_replace", // ATTDESCFORM_VALUE_IS_REPLACE
106};
107
108const char *g_EffectTypes[NUM_EFFECT_TYPES] =
109{
110 "neutral", // ATTRIB_EFFECT_NEUTRAL = 0,
111 "positive", // ATTRIB_EFFECT_POSITIVE,
112 "negative", // ATTRIB_EFFECT_NEGATIVE,
113};
114
115const char *g_szParticleAttachTypes[] =
116{
117 "absorigin", // PATTACH_ABSORIGIN = 0, // Create at absorigin, but don't follow
118 "absorigin_follow", // PATTACH_ABSORIGIN_FOLLOW, // Create at absorigin, and update to follow the entity
119 "customorigin", // PATTACH_CUSTOMORIGIN, // Create at a custom origin, but don't follow
120 "customorigin_follow", // PATTACH_CUSTOMORIGIN_FOLLOW, // Create at a custom origin, follow relative position to specified entity
121 "point", // PATTACH_POINT, // Create on attachment point, but don't follow
122 "point_follow", // PATTACH_POINT_FOLLOW, // Create on attachment point, and update to follow the entity
123 "eyes_follow", // PATTACH_EYES_FOLLOW, // Create on eyes of the attached entity, and update to follow the entity
124 "overhead_follow", // PATTACH_OVERHEAD_FOLLOW, // Create at the top of the entity's bbox
125 "worldorigin", // PATTACH_WORLDORIGIN, // Used for control points that don't attach to an entity
126 "rootbone_follow", // PATTACH_ROOTBONE_FOLLOW, // Create at the root bone of the entity, and update to follow
127 "point_follow_substepped", // PATTACH_POINT_FOLLOW_SUBSTEPPED,// Like point_follow with interpolation
128 "renderorigin_follow", // PATTACH_RENDERORIGIN_FOLLOW // Create at the renderorigin of the entity, and update to follow
129};
130
131const char *g_szParticleAttachToEnt[] =
132{
133 "self", // ATTPART_TO_SELF,
134 "parent", // ATTPART_TO_PARENT,
135};
136
137//-----------------------------------------------------------------------------
138// Purpose: Set the capabilities bitfield based on whether the entry is true/false.
139//-----------------------------------------------------------------------------
140const char *g_Capabilities[] =
141{
142 "paintable", // ITEM_CAP_PAINTABLE
143 "nameable", // ITEM_CAP_NAMEABLE
144 "decodable", // ITEM_CAP_DECODABLE
145 "can_delete", // ITEM_CAP_CAN_DELETE
146 "can_customize_texture", // ITEM_CAP_CAN_CUSTOMIZE_TEXTURE
147 "usable", // ITEM_CAP_USABLE
148 "usable_gc", // ITEM_CAP_USABLE_GC
149 "can_gift_wrap", // ITEM_CAP_CAN_GIFT_WRAP
150 "usable_out_of_game", // ITEM_CAP_USABLE_OUT_OF_GAME
151 "can_collect", // ITEM_CAP_CAN_COLLECT
152 "can_craft_count", // ITEM_CAP_CAN_CRAFT_COUNT
153 "can_craft_mark", // ITEM_CAP_CAN_CRAFT_MARK
154 "paintable_team_colors", // ITEM_CAP_PAINTABLE_TEAM_COLORS
155 "can_be_restored", // ITEM_CAP_CAN_BE_RESTORED
156 "strange_parts", // ITEM_CAP_CAN_USE_STRANGE_PARTS
157 "paintable_unusual", // ITEM_CAP_PAINTABLE_UNUSUAL
158 "can_increment", // ITEM_CAP_CAN_INCREMENT
159 "uses_essence", // ITEM_CAP_USES_ESSENCE
160 "autograph", // ITEM_CAP_AUTOGRAPH
161 "recipe", // ITEM_CAP_RECIPE
162 "can_sticker", // ITEM_CAP_CAN_STICKER
163 "can_stattrack_swap", // ITEM_CAP_STATTRACK_SWAP
164};
165
166const char* g_AssetModifiers[] =
167{
168 "activity",
169 "announcer",
170 "announcer_preview",
171 "hud_skin",
172 "ability_name",
173 "sound",
174 "speech",
175 "particle",
176 "particle_snapshot",
177 "particle_control_point",
178 "entity_model",
179 "entity_scale",
180 "icon_replacement",
181 "ability_icon_replacement",
182 "courier",
183 "courier_flying",
184 "hero_model_change"
185};
186
187
188#define RETURN_ATTRIBUTE_STRING( attrib_name, default_string ) \
189 static CSchemaAttributeDefHandle pAttribString( attrib_name ); \
190 const char *pchResultAttribString = default_string; \
191 FindAttribute_UnsafeBitwiseCast< CAttribute_String >( this, pAttribString, &pchResultAttribString ); \
192 return pchResultAttribString;
193
194#define RETURN_ATTRIBUTE_STRING_F( func_name, attrib_name, default_string ) \
195 const char *func_name( void ) const { RETURN_ATTRIBUTE_STRING( attrib_name, default_string ) }
196
197EAssetModifier GetAssetModifierType( const char* pszType )
198{
199 for ( int i=0; i<AM_MAX; ++i )
200 {
201 if ( Q_strcmp( pszType, g_AssetModifiers[i] ) == 0 )
202 return (EAssetModifier) i;
203 }
204
205 return AM_Invalid;
206}
207
208CUniformRandomStream CEconItemSchema::m_RandomStream;
209
210static void ParseCapability( item_capabilities_t &capsBitfield, KeyValues* pEntry )
211{
212 COMPILE_TIME_ASSERT( ARRAYSIZE(g_Capabilities) == NUM_ITEM_CAPS );
213 int idx = StringFieldToInt( pEntry->GetName(), g_Capabilities, ARRAYSIZE(g_Capabilities) );
214 if ( idx < 0 )
215 {
216 return;
217 }
218 int bit = 1 << idx;
219 if ( pEntry->GetBool() )
220 {
221 (int&)capsBitfield |= bit;
222 }
223 else
224 {
225 (int&)capsBitfield &= ~bit;
226 }
227}
228
229//-----------------------------------------------------------------------------
230// Purpose: interpret tint colors as integers for the values
231//-----------------------------------------------------------------------------
232static bool Helper_ExtractIntegerFromValueStringEntry( const char *szValue, int *pnValue )
233{
234 if ( V_isdigit( *szValue ) )
235 {
236 *pnValue = V_atoi( szValue );
237 return true;
238 }
239 else if ( char const *szTint = StringAfterPrefix( szValue, "tint_") )
240 {
241 if ( !V_stricmp( szTint, "min" ) )
242 {
243 *pnValue = 1;
244 return true;
245 }
246 if ( !V_stricmp( szTint, "max" ) )
247 {
248 *pnValue = GEconItemSchema().GetGraffitiTintMaxValidDefID();
249 return true;
250 }
251 if ( const CEconGraffitiTintDefinition *pDef = GEconItemSchema().GetGraffitiTintDefinitionByName( szTint ) )
252 {
253 *pnValue = pDef->GetID();
254 return true;
255 }
256 return false;
257 }
258 return false;
259}
260
261//-----------------------------------------------------------------------------
262// Purpose: interprets a string such as "1-3,5-20,45,46,47" and adds the
263// individual uint32s into the specified vector.
264//-----------------------------------------------------------------------------
265static bool Helper_ExtractIntegersFromValuesString( const char * pszValues, CCopyableUtlVector< uint32 > &vecValues )
266{
267 bool bResult = true;
268 CUtlVector< char* > vecValueStrings;
269 V_SplitString( pszValues, ",", vecValueStrings );
270
271 if ( vecValueStrings.Count() < 1 )
272 bResult = false;
273
274
275 FOR_EACH_VEC( vecValueStrings, i )
276 {
277 if ( V_strstr( vecValueStrings[ i ], "-" ) )
278 {
279 CUtlVector< char* > vecRangeStrings;
280 V_SplitString( vecValueStrings[ i ], "-", vecRangeStrings );
281
282 if ( vecRangeStrings.Count() == 2 )
283 {
284 int iRangeMin = 0, iRangeMax = 0;
285 if ( !Helper_ExtractIntegerFromValueStringEntry( vecRangeStrings[ 0 ], &iRangeMin ) ||
286 !Helper_ExtractIntegerFromValueStringEntry( vecRangeStrings[ 1 ], &iRangeMax ) ||
287 ( iRangeMin >= iRangeMax ) )
288 {
289 bResult = false;
290 break;
291 }
292
293 for ( int j = iRangeMin; j <= iRangeMax; j++ )
294 {
295 vecValues.AddToTail( j );
296 }
297 }
298 else
299 {
300 bResult = false;
301 break;
302 }
303
304 vecRangeStrings.PurgeAndDeleteElements();
305 }
306 else
307 {
308 int iValue = 0;
309 if ( !Helper_ExtractIntegerFromValueStringEntry( vecValueStrings[ i ], &iValue ) )
310 {
311 bResult = false;
312 break;
313 }
314 vecValues.AddToTail( iValue );
315 }
316
317 }
318
319 vecValueStrings.PurgeAndDeleteElements();
320
321 return bResult;
322
323}
324
325
326#if defined ( CSTRIKE15 )
327const uint32 g_unNumWearBuckets = 3;
328uint64 Helper_GetAlternateIconKeyForWeaponPaintWearItem( item_definition_index_t nDefIdx, uint32 nPaintId, uint32 nWear )
329{
330 return ( nDefIdx << 16 ) + ( nPaintId << 2 ) + nWear;
331
332}
333uint64 Helper_GetAlternateIconKeyForTintedStickerItem( uint32 nStickerKitID, uint32 unTintID )
334{
335 unTintID = CombinedTintIDGetHSVID( unTintID );
336 return ( uint64( 1 ) << 32 ) | ( ( nStickerKitID & 0xFFFFFF ) << 8 ) | ( unTintID & 0xFF );
337}
338#endif
339
340//-----------------------------------------------------------------------------
341// Purpose: Constructor
342//-----------------------------------------------------------------------------
343CEconItemQualityDefinition::CEconItemQualityDefinition( void )
344: m_nValue( INT_MAX )
345, m_bCanSupportSet( false )
346, m_unWeight( 0 )
347, m_bExplicitMatchesOnly( false )
348{
349}
350
351
352//-----------------------------------------------------------------------------
353// Purpose: Copy constructor
354//-----------------------------------------------------------------------------
355CEconItemQualityDefinition::CEconItemQualityDefinition( const CEconItemQualityDefinition &that )
356{
357 (*this) = that;
358}
359
360
361//-----------------------------------------------------------------------------
362// Purpose: Operator=
363//-----------------------------------------------------------------------------
364CEconItemQualityDefinition &CEconItemQualityDefinition::operator=( const CEconItemQualityDefinition &rhs )
365{
366 m_nValue = rhs.m_nValue;
367 m_strName = rhs.m_strName;
368 m_bCanSupportSet = rhs.m_bCanSupportSet;
369 m_unWeight = rhs.m_unWeight;
370 m_bExplicitMatchesOnly = rhs.m_bExplicitMatchesOnly;
371
372 return *this;
373}
374
375
376//-----------------------------------------------------------------------------
377// Purpose: Initialize the quality definition
378// Input: pKVQuality - The KeyValues representation of the quality
379// schema - The overall item schema for this attribute
380// pVecErrors - An optional vector that will contain error messages if
381// the init fails.
382// Output: True if initialization succeeded, false otherwise
383//-----------------------------------------------------------------------------
384bool CEconItemQualityDefinition::BInitFromKV( KeyValues *pKVQuality, CEconItemSchema &pschema, CUtlVector<CUtlString> *pVecErrors /* = NULL */ )
385{
386
387 m_nValue = pKVQuality->GetInt( "value", -1 );
388 m_strName = pKVQuality->GetName();
389 m_bCanSupportSet = pKVQuality->GetBool( "canSupportSet" );
390 m_strHexColor = pKVQuality->GetString( "hexColor" );
391 m_unWeight = pKVQuality->GetInt( "weight", 0 );
392
393 // Check for required fields
394 SCHEMA_INIT_CHECK(
395 NULL != pKVQuality->FindKey( "value" ),
396 CFmtStr( "Quality definition %s: Missing required field \"value\"", pKVQuality->GetName() ) );
397
398#if defined(CLIENT_DLL) || defined(GAME_DLL)
399 return SCHEMA_INIT_SUCCESS();
400#endif
401
402 // Check for data consistency
403 SCHEMA_INIT_CHECK(
404 0 != Q_stricmp( GetName(), "any" ),
405 CFmtStr( "Quality definition any: The quality name \"any\" is a reserved keyword and cannot be used." ) );
406
407 SCHEMA_INIT_CHECK(
408 m_nValue != k_unItemQuality_Any,
409 CFmtStr( "Quality definition %s: Invalid value (%d). It is reserved for Any", GetName(), k_unItemQuality_Any ) );
410
411 return SCHEMA_INIT_SUCCESS();
412}
413
414//-----------------------------------------------------------------------------
415// Purpose: Constructor
416//-----------------------------------------------------------------------------
417CEconItemRarityDefinition::CEconItemRarityDefinition( void )
418 : m_nValue( INT_MAX )
419{
420}
421
422
423//-----------------------------------------------------------------------------
424// Purpose: Copy constructor
425//-----------------------------------------------------------------------------
426CEconItemRarityDefinition::CEconItemRarityDefinition( const CEconItemRarityDefinition &that )
427{
428 (*this) = that;
429}
430
431
432//-----------------------------------------------------------------------------
433// Purpose: Operator=
434//-----------------------------------------------------------------------------
435CEconItemRarityDefinition &CEconItemRarityDefinition::operator=( const CEconItemRarityDefinition &rhs )
436{
437 m_nValue = rhs.m_nValue;
438 m_strName = rhs.m_strName;
439 m_strLocKey = rhs.m_strLocKey;
440 m_strWepLocKey = rhs.m_strWepLocKey;
441 m_strLootList = rhs.m_strLootList;
442 m_strRecycleLootList = rhs.m_strRecycleLootList;
443 m_strDropSound = rhs.m_strDropSound;
444 m_strNextRarity = rhs.m_strNextRarity;
445 m_iWhiteCount = rhs.m_iWhiteCount;
446 m_iBlackCount = rhs.m_iBlackCount;
447 m_flWeight = rhs.m_flWeight;
448
449 return *this;
450}
451
452
453//-----------------------------------------------------------------------------
454// Purpose: Initialize the rarity definition
455//-----------------------------------------------------------------------------
456bool CEconItemRarityDefinition::BInitFromKV( KeyValues *pKVRarity, CEconItemSchema &pschema, CUtlVector<CUtlString> *pVecErrors /* = NULL */ )
457{
458 m_nValue = pKVRarity->GetInt( "value", -1 );
459 m_strName = pKVRarity->GetName();
460 m_strLocKey = pKVRarity->GetString( "loc_key" );
461 m_strWepLocKey = pKVRarity->GetString( "loc_key_weapon" );
462
463 m_iAttribColor = GetAttribColorIndexForName( pKVRarity->GetString( "color" ) );
464 m_strLootList = pKVRarity->GetString( "loot_list" ); // Not required.
465 m_strRecycleLootList = pKVRarity->GetString( "recycle_list" ); // Not required.
466 m_strDropSound = pKVRarity->GetString( "drop_sound" );
467 m_strNextRarity = pKVRarity->GetString( "next_rarity" ); // Not required.
468 m_flWeight = pKVRarity->GetFloat( "weight", 0 );
469
470 // Check for required fields
471 SCHEMA_INIT_CHECK(
472 NULL != pKVRarity->FindKey( "value" ),
473 CFmtStr( "Rarity definition %s: Missing required field \"value\"", pKVRarity->GetName() ) );
474
475 SCHEMA_INIT_CHECK(
476 NULL != pKVRarity->FindKey( "loc_key" ),
477 CFmtStr( "Rarity definition %s: Missing required field \"loc_key\"", pKVRarity->GetName() ) );
478
479#if defined(CLIENT_DLL) || defined(GAME_DLL)
480 return SCHEMA_INIT_SUCCESS();
481#endif
482
483 return SCHEMA_INIT_SUCCESS();
484}
485
486//-----------------------------------------------------------------------------
487// Purpose:
488//-----------------------------------------------------------------------------
489bool CEconColorDefinition::BInitFromKV( KeyValues *pKVColor, CEconItemSchema &pschema, CUtlVector<CUtlString> *pVecErrors /* = NULL */ )
490{
491 m_strName = pKVColor->GetName();
492 m_strColorName = pKVColor->GetString( "color_name" );
493 m_strHexColor = pKVColor->GetString( "hex_color" );
494
495 SCHEMA_INIT_CHECK(
496 !m_strColorName.IsEmpty(),
497 CFmtStr( "Quality definition %s: missing \"color_name\"", GetName() ) );
498
499 SCHEMA_INIT_CHECK(
500 !m_strHexColor.IsEmpty(),
501 CFmtStr( "Quality definition %s: missing \"hex_color\"", GetName() ) );
502
503 return SCHEMA_INIT_SUCCESS();
504}
505
506//-----------------------------------------------------------------------------
507// Purpose:
508//-----------------------------------------------------------------------------
509bool CEconGraffitiTintDefinition::BInitFromKV( KeyValues *pKVColor, CEconItemSchema &pschema, CUtlVector<CUtlString> *pVecErrors /* = NULL */ )
510{
511 m_nID = pKVColor->GetInt( "id" );
512 m_strColorName = pKVColor->GetName();
513 m_strHexColor = pKVColor->GetString( "hex_color" );
514
515 SCHEMA_INIT_CHECK(
516 !m_strColorName.IsEmpty(),
517 CFmtStr( "Graffiti tint definition #%d %s: missing name", GetID(), GetColorName() ) );
518
519 SCHEMA_INIT_CHECK(
520 !m_strHexColor.IsEmpty() && ( m_strHexColor.Get()[0] == '#' ),
521 CFmtStr( "Graffiti tint #%d %s: missing or invalid \"hex_color\"", GetID(), GetColorName() ) );
522
523 m_unHexColorRGB = ( !m_strHexColor.IsEmpty() ) ? V_atoi( CFmtStr( "0x%s", m_strHexColor.Get() + 1 ) ) : 0;
524
525 return !m_strColorName.IsEmpty() && !m_strHexColor.IsEmpty() && ( m_strHexColor.Get()[0] == '#' );
526}
527
528//-----------------------------------------------------------------------------
529// Purpose:
530//-----------------------------------------------------------------------------
531bool CEconMusicDefinition::BInitFromKV( KeyValues *pKVMusicDef, CEconItemSchema &pschema, CUtlVector<CUtlString> *pVecErrors /* = NULL */ )
532{
533 nID = Q_atoi( pKVMusicDef->GetName() );
534 m_strName = pKVMusicDef->GetString( "name" );
535 m_strNameLocToken = pKVMusicDef->GetString( "loc_name" );
536 m_strLocDescription = pKVMusicDef->GetString( "loc_description" );
537 m_strPedestalDisplayModel = pKVMusicDef->GetString( "pedestal_display_model" );
538 m_strInventoryImage = pKVMusicDef->GetString( "image_inventory" );
539
540 SCHEMA_INIT_CHECK(
541 ( nID > 0 ),
542 CFmtStr( "Music definition %s: name must be a positive integer", GetName() ) );
543
544 SCHEMA_INIT_CHECK(
545 !m_strName.IsEmpty(),
546 CFmtStr( "Music definition %s: missing \"name\"", GetName() ) );
547
548 return SCHEMA_INIT_SUCCESS();
549}
550
551
552//-----------------------------------------------------------------------------
553// Purpose:
554//-----------------------------------------------------------------------------
555bool CEconQuestDefinition::BInitFromKV( KeyValues *pKVQuestDef, CEconItemSchema &pschema, CUtlVector<CUtlString> *pVecErrors /* = NULL */ )
556{
557 //TODO - do we care to parse quest definitions on GC at all or only on game server and client?
558 nID = Q_atoi( pKVQuestDef->GetName() );
559 SCHEMA_INIT_CHECK(
560 ( nID > 0 ),
561 CFmtStr( "Quest definition %s: name must be a positive integer", GetName() ) );
562
563 m_strName = pKVQuestDef->GetString( "name" );
564 SCHEMA_INIT_CHECK(
565 !m_strName.IsEmpty(),
566 CFmtStr( "Quest definition %s: missing \"name\"", GetName() ) );
567
568 m_strGameMode = pKVQuestDef->GetString( "gamemode" );
569 m_strMapGroup = pKVQuestDef->GetString( "mapgroup" );
570 m_strMap = pKVQuestDef->GetString( "map" );
571
572 #ifndef GC_DLL
573 static bool bLoadBannedWords = ( !!CommandLine()->FindParm( "-usebanlist" ) ) || (
574 !!CommandLine()->FindParm( "-perfectworld" )
575 );
576 if ( bLoadBannedWords && !V_stricmp( m_strMap.Get(), "ar_monastery" ) )
577 m_strMap = "ar_shoots";
578 #endif
579
580 m_bIsAnEvent = pKVQuestDef->GetBool( "is_an_event", false );
581
582 // Campaign quests require client-exposed points remaining.
583 // load non-GC only points if it exists.
584 // Otherwise look for "gc_generation_settings/points"
585
586 // quest events are always 1 point.
587 if ( m_bIsAnEvent )
588 {
589 m_vecQuestPoints.AddToTail( 1 );
590 }
591 else
592 {
593 const char* pszPointsRemaining = pKVQuestDef->GetString( "points" );
594
595 m_vecQuestPoints.Purge();
596
597 if ( pszPointsRemaining && pszPointsRemaining[ 0 ] )
598 {
599 SCHEMA_INIT_CHECK(
600 Helper_ExtractIntegersFromValuesString( pszPointsRemaining, m_vecQuestPoints ),
601 CFmtStr( "Quest definition %s specifies a malformed values range: %s", GetName(), pszPointsRemaining ) );
602 }
603 else
604 {
605 // if points aren't specified then default to '1'
606 m_vecQuestPoints.AddToTail( 1 );
607 }
608 }
609
610
611 // campaigns use quest-defined rewards rather than attributes rolled later.
612 m_strRewardLootList = pKVQuestDef->GetString( "quest_reward" );
613
614 m_nDifficulty = pKVQuestDef->GetInt( "difficulty", 1 );
615
616 m_nOperationalPoints = pKVQuestDef->GetInt( "operational_points", 0 );
617
618 if ( !m_bIsAnEvent )
619 {
620 m_nXPReward = pKVQuestDef->GetInt( "xp_reward", ( m_nDifficulty + 1 ) * 100 ); // campaign quest: 1 = 200, 2 = 300, 3 = 400
621 }
622 else
623 {
624 m_nXPReward = pKVQuestDef->GetInt( "gc_generation_settings/xp_reward", 1 ); // default quest event xp reward to 1 per action
625 }
626
627 m_nTargetTeam = pKVQuestDef->GetInt( "target_team", 0 );
628
629 m_strExpr = pKVQuestDef->GetString( "expression" );
630
631 // BONUS
632 m_strBonusExpr = pKVQuestDef->GetString( "expr_bonus" );
633
634 // set bonus percent only if a bonus expression exists
635 if ( !m_strBonusExpr.IsEmpty() )
636 {
637 m_nXPBonusPercent = pKVQuestDef->GetInt( "xp_bonus_percent", 100 ); // default bonus is 100%
638 }
639 else
640 {
641 m_nXPBonusPercent = 0;
642 }
643
644#ifdef CLIENT_DLL // strings
645
646 m_strNameLocToken = pKVQuestDef->GetString( "loc_name" );
647 if ( !m_strNameLocToken.IsEmpty() )
648 HelperValidateLocalizationStringToken( m_strNameLocToken );
649
650 m_strShortNameLocToken = pKVQuestDef->GetString( "loc_shortname" );
651 if ( !m_strShortNameLocToken.IsEmpty() )
652 HelperValidateLocalizationStringToken( m_strShortNameLocToken );
653
654 m_strDescriptionLocToken = pKVQuestDef->GetString( "loc_description" );
655 if ( !m_strDescriptionLocToken.IsEmpty() )
656 HelperValidateLocalizationStringToken( m_strDescriptionLocToken );
657
658 m_strHudDescriptionLocToken = pKVQuestDef->GetString( "loc_huddescription" );
659 if ( !m_strHudDescriptionLocToken.IsEmpty() )
660 HelperValidateLocalizationStringToken( m_strHudDescriptionLocToken );
661
662 // Localizers have request the ability to explicitly write each quest's description because the
663 // programatic string construction in place is not flexible enough to accommodate various grammars.
664 //
665 // Description will defer to this string, if it exists. It should not be used in CSGO_English.
666
667 if ( g_pVGuiLocalize->Find( CFmtStr( "Override_Quest_Description_%s", pKVQuestDef->GetName( ) ) ) )
668 m_strDescriptionLocToken = CFmtStr( "Override_Quest_Description_%s", pKVQuestDef->GetName( ) );
669
670 m_strLocBonus = pKVQuestDef->GetString( "loc_bonus" );
671 if ( !m_strLocBonus.IsEmpty() )
672 HelperValidateLocalizationStringToken( m_strLocBonus );
673
674 m_strIcon = pKVQuestDef->GetString( "quest_icon" );
675
676
677 // Quests used to use explicitly define "string_tokens" in their schema definitions.
678 // Now the string tokens kv is populated by interpreting the expression
679 // but we may still want to add tokens manually in the future.
680
681 m_kvStringTokens = pKVQuestDef->FindKey( "string_tokens" );
682
683 if ( !m_kvStringTokens )
684 m_kvStringTokens = new KeyValues( "string_tokens" );
685
686 KeyValues * pkvExpressionTokens = new KeyValues( "ExpressionTokens" );
687 KeyValues::AutoDelete autodelete_pKVExpressionTokens( pkvExpressionTokens );
688
689 TokenizeQuestExpression( m_strExpr, pkvExpressionTokens );
690
691 Assert( pkvExpressionTokens->GetFirstSubKey() != NULL );
692
693 PopulateQuestStringTokens( *this, *pkvExpressionTokens, *m_kvStringTokens );
694
695 if ( !m_strBonusExpr.IsEmpty() )
696 {
697 KeyValues * pKVBonusExpressionTokens = new KeyValues( "BonusExpressionTokens" );
698 KeyValues::AutoDelete autodelete_pKVBonusExpressionTokens( pKVBonusExpressionTokens );
699
700 TokenizeQuestExpression( m_strBonusExpr, pKVBonusExpressionTokens );
701
702 Assert( pKVBonusExpressionTokens->GetFirstSubKey() != NULL );
703
704 PopulateQuestStringTokens( *this, *pKVBonusExpressionTokens, *m_kvStringTokens, true );
705 }
706
707
708#endif // ifndef GAME_DLL
709
710 // MAPGROUP
711 //
712 //
713 bool bMapGroupValid = false;
714 if ( m_strMapGroup.IsEmpty() ) { bMapGroupValid = true; } // "": empty mapgroups are technically valid
715 /** Removed for partner depot **/
716
717 AssertMsg2( bMapGroupValid, "QUEST %d references a non-existant mapgroup: %s.\n", nID, m_strMapGroup.Get() );
718
719#ifndef GAME_DLL // strings
720
721 KeyValues *pGameModestxt = new KeyValues( "GameModes.txt" );
722 KeyValues::AutoDelete autodelete( pGameModestxt );
723
724 if ( !pGameModestxt->LoadFromFile( g_pFullFileSystem, "GameModes.txt" ) )
725 {
726 pGameModestxt = NULL;
727 }
728
729 // MAP
730 //
731 //
732
733 // get map string from gamemodes.txt and insert it into m_kvStringTokens
734 //
735 //
736 if ( !m_strMap.IsEmpty( ) && StringIsEmpty( m_kvStringTokens->GetString( "map" ) ) )
737 {
738 KeyValues *pMaps = pGameModestxt->FindKey( "maps" );
739
740 if ( pMaps )
741 {
742 KeyValues *pMapKV = pMaps->FindKey( m_strMap );
743 Assert( pMapKV );
744
745 if ( pMapKV )
746 {
747 m_kvStringTokens->SetString( "map", pMapKV->GetString( "nameID" ) );
748 m_kvStringTokens->SetString( "location", pMapKV->GetString( "nameID" ) );
749 }
750 }
751 }
752
753 // get mapgroup string from gamemodes.txt and insert it into m_kvStringTokens
754 //
755 //
756 if ( !m_strMapGroup.IsEmpty() && StringIsEmpty( m_kvStringTokens->GetString( "mapgroup" ) ) )
757 {
758 KeyValues *pMapGroups = pGameModestxt->FindKey( "mapgroups" );
759
760 if ( pMapGroups )
761 {
762 KeyValues *pMapGroup = pMapGroups->FindKey( m_strMapGroup );
763 Assert( pMapGroup );
764
765 if ( pMapGroup )
766 {
767 m_kvStringTokens->SetString( "mapgroup", pMapGroup->GetString( "nameID" ) );
768 }
769
770 // if we didn't specify a map then use the mapgroup as the location
771 if ( !m_kvStringTokens->FindKey("location") )
772 {
773 m_kvStringTokens->SetString( "location", pMapGroup->GetString( "nameID" ) );
774 }
775 }
776 }
777
778
779 //GAMEMODE
780 //
781 //
782#ifdef DBGFLAG_ASSERT
783 const char * szType;
784 bool bGameModeValid = g_pGameTypes->GetGameTypeFromMode( m_strGameMode, szType );
785 AssertMsg2( bGameModeValid, "QUEST %d references a non-existant gamemode: %s.\n", nID, m_strGameMode );
786#endif
787 if ( StringIsEmpty( m_kvStringTokens->GetString( "gamemode" ) ) )
788 {
789
790 // get gamemode string from gamemodes.txt and insert it into m_kvStringTokens
791 //
792 //
793 KeyValues *pGameTypes = pGameModestxt->FindKey( "gameTypes" );
794
795 FOR_EACH_SUBKEY( pGameTypes, kvGameType )
796 {
797 KeyValues *pGameModes = kvGameType->FindKey( "GameModes" );
798 if ( pGameModes )
799 {
800 KeyValues *pGameMode = pGameModes->FindKey( m_strGameMode );
801 if ( pGameMode )
802 {
803 m_kvStringTokens->SetString( "gamemode", pGameModes->FindKey( m_strGameMode )->GetString( "NameID" ) );
804
805 break;
806 }
807 }
808 }
809 }
810
811 // check all strings in m_kvStringTokens
812 FOR_EACH_VALUE( m_kvStringTokens, pKVStringToken )
813 {
814 const char* token = pKVStringToken->GetString();
815 HelperValidateLocalizationStringToken( token );
816 }
817
818 FOR_EACH_TRUE_SUBKEY( m_kvStringTokens, pKVSubKey )
819 {
820 FOR_EACH_VALUE( pKVSubKey, pKVStringToken )
821 {
822 const char* token = pKVStringToken->GetString( );
823 HelperValidateLocalizationStringToken( token );
824 }
825 }
826#endif // not GAME_DLL
827
828 return SCHEMA_INIT_SUCCESS();
829}
830
831#ifdef CLIENT_DLL
832///////////////////////////////////////////////////////////////////////////////////////////
833//
834// read the expression ( i.e. " %act_kill_human% && %cond_gun_borrowed% && %weapon_smg% )
835// and insert appropriate string tokens into the string token keyvalues structure
836//
837////////////////////////////////////////////////////////////////////////////////////////////
838void CEconQuestDefinition::PopulateQuestStringTokens( CEconQuestDefinition &questDef, KeyValues &kvExpressionTokens, KeyValues &kvStringTokens, bool bBonus )
839{
840 // 1. Validate that every token ( %TOKEN% ) is accounted for.
841 // 2. Populate pkvStringTokens with tokens derived from expression keywords e.g. %weapon_ak47%
842
843 KeyValues * pkvExpressionTokens = &kvExpressionTokens;
844
845 int nItemCount = 0;
846
847 FOR_EACH_SUBKEY( pkvExpressionTokens, kvSubKey )
848 {
849 bool bFound = false;
850
851 if ( V_stristr( kvSubKey->GetName(), "act_" ) || V_stristr( kvSubKey->GetName(), "cond_" ) )
852 {
853 // validate that action or condition is recognized.
854 for ( int i = 0; i < k_EQuestVar_Last; i++ )
855 {
856 if ( FStrEq( g_arrQuestVars[ i ], kvSubKey->GetName() ) )
857 {
858 if ( g_pVGuiLocalize->Find( CFmtStr( "#quest_action_singular_%s", kvSubKey->GetName() ) ) )
859 {
860 if ( !kvStringTokens.FindKey( "action" ) )
861 {
862 kvStringTokens.SetString( "action", CFmtStr( "#quest_action_singular_%s", kvSubKey->GetName() ) );
863 }
864
865 if ( !kvStringTokens.FindKey( "actions" ) )
866 {
867 kvStringTokens.SetString( "actions", CFmtStr( "#quest_action_plural_%s", kvSubKey->GetName() ) );
868 }
869 }
870
871 /** Removed for partner depot **/
872
873 // note that we have at least one generic weapon if we use any item condition.
874 if ( V_stristr( kvSubKey->GetName( ), "cond_item" ) )
875 {
876 if ( nItemCount == 0 ) nItemCount = 1;
877 }
878
879// debug kvStringTokens.SaveToFile( g_pFullFileSystem, "~expressiontokens.txt" );
880// debug pkvExpressionTokens->SaveToFile( g_pFullFileSystem, "~expressiontokens.txt" );
881
882 bFound = true;
883 break;
884 }
885 }
886 }
887 else if ( V_stristr( kvSubKey->GetName(), "weapon_" ) )
888 {
889 // find the items subkey or create one if it doesnt exist
890 KeyValues* pkvItems = kvStringTokens.FindKey( "items" );
891 if ( !pkvItems )
892 {
893 pkvItems = new KeyValues( "items" );
894 kvStringTokens.AddSubKey( pkvItems );
895 }
896
897 CEconItemDefinition * pWeaponItemDef = GetItemSchema( )->GetItemDefinitionByName( kvSubKey->GetName( ) );
898
899 if ( pWeaponItemDef )
900 {
901 // LEGACY PRE-2016
902 if ( !kvStringTokens.FindKey( "weapon" ) )
903 {
904 kvStringTokens.SetString( "weapon", pWeaponItemDef->GetItemBaseName() );
905 }
906 // LEGACY PRE-2016
907
908 // insert the weapon into the 'items' true subkey
909 for ( int i = 1; i < 20; i++ )
910 {
911 const char * szItemKeyName = CFmtStr( "item%d", i );
912 if ( !pkvItems->FindKey( szItemKeyName ) )
913 {
914 pkvItems->SetString( szItemKeyName, pWeaponItemDef->GetItemBaseName( ) );
915
916 nItemCount++;
917 break;
918 }
919 }
920
921 bFound = true;
922
923 // set the quest icon to the weapon if an icon was not explicitly set.
924 if ( questDef.m_strIcon.IsEmpty( ) && !bBonus )
925 {
926 questDef.m_strIcon = kvSubKey->GetName() + WEAPON_CLASSNAME_PREFIX_LENGTH;
927 }
928 }
929 else
930 {
931 // look for a weapon category/type
932 for ( int i = 0; i < LOADOUT_POSITION_COUNT; i++ )
933 {
934 if ( V_stristr( kvSubKey->GetName(), CFmtStr( "weapon_%s", g_szLoadoutStrings[ i ] ) ) )
935 {
936 // LEGACY PRE-2016
937 if ( !kvStringTokens.FindKey( "weapon" ) )
938 {
939 kvStringTokens.SetString( "weapon", g_szLoadoutStringsForDisplay[ i ] );
940 }
941 // LEGACY PRE-2016
942
943 // insert the weapon into the 'items' true subkey
944 for ( int j = 1; j < 20; j++ )
945 {
946 const char * szItemKeyName = CFmtStr( "item%d", j );
947 if ( !pkvItems->FindKey( szItemKeyName ) )
948 {
949 const char * szLoadoutStringNoHashMark = g_szLoadoutStringsForDisplay[ i ] + 1;
950 pkvItems->SetString( szItemKeyName, CFmtStr( "#quest_%s", szLoadoutStringNoHashMark ) );
951
952 nItemCount++;
953 break;
954 }
955 }
956
957 bFound = true;
958 break;
959 }
960 }
961 }
962 }
963 else if ( V_stristr( kvSubKey->GetName(), "set_" ) )
964 {
965 for ( int i = 0; i < GetItemSchema()->GetItemSetCount(); i++ )
966 {
967 const CEconItemSetDefinition *pItemSetDef = GetItemSchema()->GetItemSetByIndex( i );
968
969 if ( pItemSetDef && FStrEq( kvSubKey->GetName(), pItemSetDef->GetName() ) )
970 {
971 if ( !kvStringTokens.FindKey( "set" ) )
972 {
973 kvStringTokens.SetString( "set", CFmtStr( "#CSGO_%s", pItemSetDef->GetName() ) );
974 }
975
976 bFound = true;
977 break;
978 }
979 }
980 }
981 else if ( V_stristr( kvSubKey->GetName(), "map_" ) )
982 {
983
984 /** Removed for partner depot **/
985
986 }
987
988 // debug save
989// kvStringTokens.SaveToFile( g_pFullFileSystem, "~expressiontokens.txt" );
990
991 AssertMsg2( bFound, "QUEST %u refs a non-existant var: %s\n", questDef.GetID(), kvSubKey->GetName() );
992 }
993
994 // defaults
995 if ( !kvStringTokens.FindKey( "commandverb" ) )
996 {
997 kvStringTokens.SetString( "commandverb", "#quest_commandverb_default" );
998 }
999
1000 if ( !kvStringTokens.FindKey( "target" ) )
1001 {
1002 kvStringTokens.SetString( "target", "#emptystring" );
1003 }
1004
1005 if ( !kvStringTokens.FindKey( "item_quality" ) )
1006 {
1007 kvStringTokens.SetString( "item_quality", "#emptystring" );
1008 }
1009
1010 // no items were specified but due to specified item conditions we know we need one.
1011 if ( !kvStringTokens.FindKey( "items/item1" ) && ( nItemCount > 0 ) )
1012 {
1013 kvStringTokens.SetString( "items/item1", "#quest_item_default" );
1014 }
1015
1016 // no description was specified so use one of the default ones.
1017 if ( questDef.m_strDescriptionLocToken.IsEmpty( ) && !bBonus )
1018 {
1019 questDef.m_strDescriptionLocToken = CFmtStr( "quest_default_%d_items_desc", nItemCount );
1020 }
1021
1022 // no quest tracker description was specified, so use one of the default ones.
1023 if ( questDef.m_strHudDescriptionLocToken.IsEmpty( ) && !bBonus )
1024 {
1025 questDef.m_strHudDescriptionLocToken = CFmtStr( "quest_default_hud_%d_items_desc", nItemCount );
1026 }
1027
1028// kvStringTokens.SaveToFile( g_pFullFileSystem, "~stringtokens.txt" ); // debug only
1029}
1030
1031#endif
1032
1033// NOTE: This does not detect syntax or unrecognized symbols. TODO: Still need to figure out how to detect those.
1034bool CEconQuestDefinition::IsQuestExpressionValid( const char * pszQuestExpr )
1035{
1036 if ( !pszQuestExpr || !pszQuestExpr[0] )
1037 return false;
1038
1039 CExpressionCalculator expr = CExpressionCalculator( pszQuestExpr );
1040
1041 ZeroOutQuestExpressionVariables( expr );
1042
1043
1044 // This seemed like a good idea but it doesn't work.
1045 //
1046// for ( int i = 0; i < expr.VariableCount(); i++ )
1047// {
1048// if ( !StringIsEmpty( expr.GetVariableName( i ) ) && expr.GetVariableValue( i ) != 1.0 )
1049// {
1050// AssertMsg1( 0, "unknown variable %s in quest expression", expr.GetVariableName( i ) );
1051// return false;
1052// }
1053// }
1054
1055 float flResult;
1056 bool bSuccess = expr.Evaluate( flResult );
1057
1058 return ( bSuccess && IsFinite(flResult) );
1059}
1060
1061void CEconQuestDefinition::SetQuestExpressionVariable( CExpressionCalculator &expQuest, EQuestVar_t questVar, float flValue )
1062{
1063 expQuest.SetVariable( CFmtStr( "%%%s%%", g_arrQuestVars[ questVar ] ), flValue );
1064}
1065
1066void CEconQuestDefinition::ZeroOutQuestExpressionVariables( CExpressionCalculator &expr )
1067{
1068 // it's not possible to iterate through expression variables but this would be ideal. VariableCount() returns 1 :/
1069// expQuest.BuildVariableListFromExpression();
1070//
1071// for ( int i = 0; i < expQuest.VariableCount(); i++ )
1072// {
1073// expQuest.SetVariable( i, 0 );
1074// }
1075
1076
1077 // ACTIONS and CONDITIONS
1078 /** Removed for partner depot **/
1079
1080 // WEAPON TYPES
1081 for ( int i = 0; i < LOADOUT_POSITION_COUNT; i++ )
1082 {
1083 expr.SetVariable( CFmtStr( "%%weapon_%s%%", g_szLoadoutStrings[ i ] ), 0 );
1084 }
1085
1086 // SETS
1087 for ( int i = 0; i < GetItemSchema()->GetItemSetCount(); i++ )
1088 {
1089 const CEconItemSetDefinition *pItemSetDef = GetItemSchema()->GetItemSetByIndex( i );
1090
1091 expr.SetVariable( CFmtStr( "%%%s%%", pItemSetDef->GetName() ), 0 );
1092 }
1093
1094 // WEAPONS
1095 static CUtlVector< const CEconItemDefinition * > s_vecWeaponItemDefs;
1096
1097 // cache these items so we don't do string compares every time we initialize an expression
1098 if ( s_vecWeaponItemDefs.Count() == 0 )
1099 {
1100 FOR_EACH_MAP_FAST( GetItemSchema()->GetItemDefinitionMap(), i )
1101 {
1102 const CEconItemDefinition * pItemDef = GetItemSchema()->GetItemDefinitionByMapIndex( i );
1103
1104 if ( V_stristr( pItemDef->GetDefinitionName(), "weapon_" ) )
1105 {
1106 s_vecWeaponItemDefs.AddToTail( pItemDef );
1107 }
1108 }
1109 }
1110
1111 FOR_EACH_VEC( s_vecWeaponItemDefs, i )
1112 {
1113 expr.SetVariable( CFmtStr( "%%%s%%", s_vecWeaponItemDefs[ i ]->GetDefinitionName() ), 0 );
1114 }
1115
1116 // MAPS
1117 /** Removed for partner depot **/
1118
1119}
1120
1121
1122bool CEconCampaignDefinition::BInitFromKV( KeyValues *pKVCampaignDef, CEconItemSchema &pschema, CUtlVector<CUtlString> *pVecErrors /*= NULL */ )
1123{
1124 m_nID = Q_atoi( pKVCampaignDef->GetName() );
1125 SCHEMA_INIT_CHECK(
1126 ( m_nID > 0 ),
1127 CFmtStr( "Campaign definition %d: name must be a positive integer", GetID() ) );
1128
1129 m_strNameLocToken = pKVCampaignDef->GetString( "loc_name" );
1130 HelperValidateLocalizationStringToken( m_strNameLocToken );
1131
1132 m_strLocDescription = pKVCampaignDef->GetString( "loc_description" );
1133 HelperValidateLocalizationStringToken( m_strLocDescription );
1134
1135 m_nSeasonNumber = pKVCampaignDef->GetInt( "season_number" );
1136
1137 SCHEMA_INIT_CHECK( m_nSeasonNumber > 0, CFmtStr( "Campaign %d must specifiy a season number", GetID() ) );
1138
1139 FOR_EACH_TRUE_SUBKEY( pKVCampaignDef, pKVCampaignNode )
1140 {
1141 CEconCampaignNodeDefinition *pNewCampaignNodeDef = new CEconCampaignNodeDefinition;
1142
1143 int CampaignNodeIndex = Q_atoi( pKVCampaignNode->GetName() );
1144
1145 uint32 index = m_mapCampaignNodes.Find( CampaignNodeIndex );
1146 if ( !m_mapCampaignNodes.IsValidIndex( index ) )
1147 {
1148 m_mapCampaignNodes.Insert( CampaignNodeIndex, pNewCampaignNodeDef );
1149 SCHEMA_INIT_SUBSTEP( pNewCampaignNodeDef->BInitFromKV( GetID(), pKVCampaignNode, pschema ) );
1150 }
1151 else
1152 {
1153 SCHEMA_INIT_CHECK(
1154 false,
1155 CFmtStr( "Campaign definition %d: campaign node index %d is not unique", GetID(), CampaignNodeIndex ) );
1156 }
1157 }
1158
1159 // go back and verify that all nodes point to valid nodes
1160 FOR_EACH_MAP_FAST( m_mapCampaignNodes, i )
1161 {
1162 const CUtlVector< uint32 >& pNextNodes = m_mapCampaignNodes.Element( i )->GetNextNodes();
1163
1164 FOR_EACH_VEC( pNextNodes, j )
1165 {
1166 int nextnode = pNextNodes[ j ];
1167
1168 int index = m_mapCampaignNodes.Find( nextnode );
1169
1170 SCHEMA_INIT_CHECK(
1171 m_mapCampaignNodes.IsValidIndex( index ),
1172 CFmtStr( "Campaign definition %d: campaign node index %d leads to an invalid campaign node %d", GetID(), m_mapCampaignNodes.Element( i )->GetID(), nextnode ) );
1173
1174 }
1175
1176 }
1177
1178 // build list of start nodes ( nodes that have no predecessors )
1179
1180 m_mapStartNodes.Purge();
1181
1182 // collect all nodes
1183 FOR_EACH_MAP_FAST( m_mapCampaignNodes, i )
1184 {
1185 m_mapStartNodes.Insert( m_mapCampaignNodes.Key( i ), m_mapCampaignNodes.Element( i ) );
1186 }
1187
1188 // subtract nodes referenced as successors
1189 FOR_EACH_MAP_FAST( m_mapCampaignNodes, i )
1190 {
1191 const CUtlVector< uint32 >& pCampaignNodes = m_mapCampaignNodes.Element( i )->GetNextNodes();
1192
1193 FOR_EACH_VEC( pCampaignNodes, j )
1194 {
1195 int nextnode = pCampaignNodes.Element( j );
1196
1197 m_mapStartNodes.Remove( nextnode );
1198 }
1199 }
1200
1201 return SCHEMA_INIT_SUCCESS();
1202
1203}
1204
1205void CEconCampaignDefinition::GetAccessibleCampaignNodes( const uint32 unCampaignCompletionBitfield, CUtlVector< CEconCampaignNodeDefinition* > &vecAccessibleNodes )
1206{
1207 vecAccessibleNodes.Purge();
1208
1209 // If no campaign quests have been done yet then the only possible quests are the starting quests.
1210 if ( unCampaignCompletionBitfield == 0 )
1211 {
1212 FOR_EACH_MAP_FAST( GetStartNodes(), i )
1213 {
1214 vecAccessibleNodes.AddToTail( GetStartNodes().Element( i ) );
1215 }
1216 }
1217 else
1218 {
1219 // otherwise recursively look for quests that are successors of completed quests.
1220 FOR_EACH_MAP_FAST( GetStartNodes(), i )
1221 {
1222 uint32 nCampaignNodeIndex = GetStartNodes().Key( i );
1223
1224 // is this start quest already complete?
1225 if ( ( 1 << ( nCampaignNodeIndex - 1 ) ) & unCampaignCompletionBitfield )
1226 {
1227 // yes. recurse.
1228 Helper_RecursiveGetAccessibleCampaignNodes( unCampaignCompletionBitfield, GetStartNodes().Element( i ), vecAccessibleNodes );
1229 }
1230 else
1231 {
1232 // no. add it to the list and return;
1233 vecAccessibleNodes.AddToTail( GetStartNodes().Element( i ) );
1234 }
1235 }
1236 }
1237}
1238
1239void CEconCampaignDefinition::Helper_RecursiveGetAccessibleCampaignNodes( const uint32 unCampaignCompletionBitfield, const CEconCampaignNodeDefinition* pNode, CUtlVector< CEconCampaignNodeDefinition* > &vecAccessibleNodes )
1240{
1241 // recursively look for nodes that don't represent completed quests. Do not recurse incomplete quests.
1242 FOR_EACH_VEC( pNode->GetNextNodes(), i )
1243 {
1244 uint32 unNextNodeIndex = pNode->GetNextNodes().Element( i );
1245 int index = GetCampaignNodes().Find( unNextNodeIndex );
1246 if ( GetCampaignNodes().IsValidIndex( index ) )
1247 {
1248 CEconCampaignDefinition::CEconCampaignNodeDefinition* pNextNode = GetCampaignNodes().Element( index );
1249
1250 // is this successor quest already complete?
1251 if ( ( 1 << ( unNextNodeIndex - 1 ) ) & unCampaignCompletionBitfield )
1252 {
1253 // yes. recurse.
1254 Helper_RecursiveGetAccessibleCampaignNodes( unCampaignCompletionBitfield, pNextNode, vecAccessibleNodes );
1255 }
1256 else
1257 {
1258 // no. add it to the list and return;
1259 vecAccessibleNodes.AddToTail( pNextNode );
1260 }
1261 }
1262 }
1263
1264}
1265
1266bool ResolveQuestIdToCampaignAndIndex( uint16 unQuestID, uint32 &unCampaignID, uint32 &unCamapaignNodeID )
1267{
1268 static bool s_bQuestMappingInitialized = false;
1269 static CUtlMap< uint16, uint64, uint16, CDefLess< uint16 > > s_mapping;
1270 if ( !s_bQuestMappingInitialized )
1271 {
1272 s_bQuestMappingInitialized = true;
1273 for ( uint32 unPossibleCampaignID = 1; unPossibleCampaignID <= g_nNumCampaigns; ++unPossibleCampaignID )
1274 {
1275 CEconCampaignDefinition const *pCampaignDef = GetItemSchema()->GetCampaignDefinition( unPossibleCampaignID );
1276 if ( !pCampaignDef )
1277 continue;
1278
1279 FOR_EACH_MAP_FAST( pCampaignDef->GetCampaignNodes(), iCN )
1280 {
1281 CEconCampaignDefinition::CEconCampaignNodeDefinition const &node = *pCampaignDef->GetCampaignNodes().Element( iCN );
1282 uint16 unMappingKey = node.GetQuestIndex();
1283 if ( unMappingKey )
1284 {
1285 uint64 unMappingValue = ( uint64( unPossibleCampaignID ) << 32 ) | uint64( node.GetID() );
1286 Assert( s_mapping.Find( unMappingKey ) == s_mapping.InvalidIndex() );
1287 s_mapping.InsertOrReplace( unMappingKey, unMappingValue );
1288 }
1289 }
1290 }
1291 }
1292
1293 uint16 idxMapping = s_mapping.Find( unQuestID );
1294 if ( idxMapping == s_mapping.InvalidIndex() )
1295 return false;
1296
1297 unCampaignID = uint32( s_mapping.Element( idxMapping ) >> 32 );
1298 unCamapaignNodeID = uint32( s_mapping.Element( idxMapping ) );
1299 return true;
1300}
1301
1302bool CEconCampaignDefinition::CEconCampaignNodeDefinition::BInitFromKV( int nCampaignIndex, KeyValues *pKVCampaignNodeDef, CEconItemSchema &pschema, CUtlVector<CUtlString> *pVecErrors /*= NULL */ )
1303{
1304 m_nID = Q_atoi( pKVCampaignNodeDef->GetName() );
1305 SCHEMA_INIT_CHECK(
1306 ( m_nID > 0 ),
1307 CFmtStr( "Campaign Node definition %d: name must be a positive integer", GetID() ) );
1308
1309 m_CampaignID = nCampaignIndex;
1310
1311
1312 m_nQuestIndex = Q_atoi( pKVCampaignNodeDef->GetString( "quest_index" ) );
1313 if ( m_nQuestIndex ) // validate only if specified, story-only nodes don't have quest indices
1314 {
1315 SCHEMA_INIT_CHECK(
1316 ( GetItemSchema()->GetQuestDefinition( m_nQuestIndex ) != NULL ),
1317 CFmtStr( "Campaign definition %d, Node definition %d: Quest Index %d is not a valid quest index", nCampaignIndex, m_nID, m_nQuestIndex ) );
1318 }
1319 m_vecNextNodes.Purge();
1320
1321 FOR_EACH_SUBKEY( pKVCampaignNodeDef, pKVCampaignNodeKey )
1322 {
1323 if ( !V_strcmp( pKVCampaignNodeKey->GetName(), "->" ) )
1324 {
1325 m_vecNextNodes.AddToTail( Q_atoi( pKVCampaignNodeKey->GetString() ) );
1326 }
1327 }
1328
1329#ifdef CLIENT_DLL
1330 FOR_EACH_TRUE_SUBKEY( pKVCampaignNodeDef, pKVCampaignNodeKey )
1331 {
1332 if ( FStrEq( pKVCampaignNodeKey->GetName(), "story_block" ) )
1333 {
1334 CEconCampaignNodeStoryBlockDefinition *pNewCampaignNodeStoryBlockDef = new CEconCampaignNodeStoryBlockDefinition;
1335
1336 m_vecStoryBlocks.AddToTail( pNewCampaignNodeStoryBlockDef );
1337 SCHEMA_INIT_SUBSTEP( pNewCampaignNodeStoryBlockDef->BInitFromKV( nCampaignIndex, m_nID, pKVCampaignNodeKey, pschema ) );
1338 }
1339 }
1340#endif
1341
1342 return SCHEMA_INIT_SUCCESS();
1343
1344}
1345
1346#ifdef CLIENT_DLL
1347// score every story block and return the highest scoring one
1348CEconCampaignDefinition::CEconCampaignNodeDefinition::CEconCampaignNodeStoryBlockDefinition * CEconCampaignDefinition::CEconCampaignNodeDefinition::GetBestScoringStoryBlock( CEconItemView *pCampaignCoin ) const
1349{
1350 CEconCampaignDefinition::CEconCampaignNodeDefinition::CEconCampaignNodeStoryBlockDefinition * pBestStoryBlock = NULL;
1351 float flBestStoryBlockScore = 0;
1352
1353
1354 FOR_EACH_VEC( GetStoryBlocks(), iSB )
1355 {
1356 float flCurStoryBlockScore = GetStoryBlocks()[ iSB ]->EvaluateStoryBlockExpression( pCampaignCoin );
1357
1358 // only consider story block expressions that have a positive score
1359 if ( flCurStoryBlockScore > 0 )
1360 {
1361 if ( !pBestStoryBlock )
1362 {
1363 pBestStoryBlock = GetStoryBlocks()[ iSB ];
1364 flBestStoryBlockScore = flCurStoryBlockScore;
1365 }
1366 else
1367 {
1368 if ( flCurStoryBlockScore > flBestStoryBlockScore )
1369 {
1370 pBestStoryBlock = GetStoryBlocks()[ iSB ];
1371 flBestStoryBlockScore = flCurStoryBlockScore;
1372 }
1373 }
1374 }
1375 }
1376
1377 return pBestStoryBlock;
1378}
1379
1380bool CEconCampaignDefinition::CEconCampaignNodeDefinition::CEconCampaignNodeStoryBlockDefinition::BInitFromKV( int nCampaignIndex, int nNodeID, KeyValues * pKVCampaignNodeStoryBlockDef, CEconItemSchema &pschema, CUtlVector<CUtlString> *pVecErrors /* = NULL */ )
1381{
1382
1383 m_strContentFile = pKVCampaignNodeStoryBlockDef->GetString( "content_file" );
1384 m_strCharacterName = pKVCampaignNodeStoryBlockDef->GetString( "character_name" );
1385 m_strDescription = pKVCampaignNodeStoryBlockDef->GetString( "description" );
1386 if ( !m_strDescription.IsEmpty() )
1387 HelperValidateLocalizationStringToken( m_strDescription );
1388
1389 m_strExpr = pKVCampaignNodeStoryBlockDef->GetString( "expression" );
1390
1391 return SCHEMA_INIT_SUCCESS();
1392}
1393
1394// Take the expression of an CExpressionCalculator, such as " %weapon_ak47% && %kill% " and extract the % delimited keywords.
1395//
1396void CEconQuestDefinition::TokenizeQuestExpression( const char * szExpression, KeyValues * pKVExpressionTokens )
1397{
1398 if ( !szExpression || !szExpression[0] )
1399 return;
1400
1401 if ( !pKVExpressionTokens )
1402 return;
1403
1404 const char * pCur = szExpression;
1405
1406 char szKeyword[ 256 ] = { '\0' };
1407 char *pCurCopy = szKeyword;
1408
1409 bool bReadingKeyword = false;
1410
1411 while ( *pCur != '\0' )
1412 {
1413 if ( *pCur == '%' && !bReadingKeyword )
1414 {
1415 bReadingKeyword = true;
1416
1417 // reset destination buffer;
1418 V_memset( szKeyword, '\0', sizeof( szKeyword ) );
1419 pCurCopy = szKeyword;
1420
1421 // step past '%'
1422 pCur++;
1423 }
1424 else if ( *pCur == '%' && bReadingKeyword )
1425 {
1426 // end of the keyword
1427
1428 bReadingKeyword = false;
1429
1430 // terminate the string with a null char and then look it up
1431 pCurCopy++;
1432 *pCurCopy = '\0';
1433
1434 pKVExpressionTokens->SetBool( szKeyword, true );
1435
1436 // step past '%'
1437 pCur++;
1438 }
1439
1440 if ( bReadingKeyword )
1441 {
1442 // keep copying the keyword until we reach the terminating '%'
1443 *pCurCopy = *pCur;
1444
1445 pCurCopy++;
1446 pCur++;
1447 }
1448 else
1449 {
1450 // keep walking until we find the start of a keyword: '%'
1451 pCur++;
1452 }
1453 }
1454
1455
1456}
1457
1458float CEconCampaignDefinition::CEconCampaignNodeDefinition::CEconCampaignNodeStoryBlockDefinition::EvaluateStoryBlockExpression( CEconItemView *pCampaignCoin ) const
1459{
1460 // if there's no expression specified then return a minimally true value.
1461 if ( m_strExpr.IsEmpty() )
1462 return 0.99; // default without an expression is trumped by any block with an expression result of 1.0. This means that any true expression block will get picked
1463 // over any block with no expression.
1464
1465 CExpressionCalculator expStoryBlock = CExpressionCalculator( GetStoryBlockExpression() );
1466
1467 KeyValues * pKVExpressionTokens = new KeyValues( "ExpressionTokens" );
1468 KeyValues::AutoDelete autodelete_pKVExpressionTokens( pKVExpressionTokens );
1469
1470 CEconQuestDefinition::TokenizeQuestExpression( GetStoryBlockExpression(), pKVExpressionTokens );
1471
1472 // go through each referenced quest and set the variable %questid% to the value of its completion state ( 0 or 1 )
1473 FOR_EACH_SUBKEY( pKVExpressionTokens, kvSubKey )
1474 {
1475 uint16 unQuestID = V_atoi( kvSubKey->GetName() );
1476
1477 uint32 unCampaignID;
1478 uint32 unCampaignNodeID;
1479
1480 ResolveQuestIdToCampaignAndIndex( unQuestID, unCampaignID, unCampaignNodeID );
1481
1482 const CSchemaAttributeDefHandle pAttr_CampaignCompletionBitfield = GetCampaignAttributeDefHandle( unCampaignID, k_ECampaignAttribute_CompletionBitfield );
1483
1484 uint32 unCampaignCompletionBitfield;
1485 if ( !pCampaignCoin->FindAttribute( pAttr_CampaignCompletionBitfield, &unCampaignCompletionBitfield ) )
1486 return 0;
1487
1488 uint32 unMask = ( 1 << ( unCampaignNodeID - 1 ) );
1489
1490 const char * szKeyWord = CFmtStr( "%%%s%%", kvSubKey->GetName() );
1491 float flCompletionState = ( unCampaignCompletionBitfield & unMask ) ? 1.0 : 0.0;
1492 expStoryBlock.SetVariable( szKeyWord, flCompletionState );
1493 }
1494
1495 expStoryBlock.BuildVariableListFromExpression();
1496
1497 float flReturn;
1498 expStoryBlock.Evaluate( flReturn );
1499
1500 return flReturn;
1501
1502}
1503#endif
1504
1505bool item_list_entry_t::InitFromName( const char *pchName )
1506{
1507 if ( pchName[ 0 ] == '[' )
1508 {
1509 // This item has an attached paint type that can affect the name and rarity!
1510 pchName++;
1511
1512 char szPaintName[ 256 ];
1513 int i;
1514 for( i = 0; i < ARRAYSIZE( szPaintName ) - 1 && pchName[ i ] != ']'; ++i )
1515 {
1516 szPaintName[ i ] = pchName[ i ];
1517 }
1518
1519 szPaintName[ i ] = '\0';
1520
1521 if ( pchName[ i ] != ']' )
1522 return false;
1523
1524 pchName += i + 1;
1525
1526 const CPaintKit *pPaintKit = GetItemSchema()->GetPaintKitDefinitionByName( szPaintName );
1527 if ( pPaintKit )
1528 {
1529 m_nPaintKit = pPaintKit->nID;
1530 }
1531 else
1532 {
1533 const CStickerKit *pStickerKit = GetItemSchema()->GetStickerKitDefinitionByName( szPaintName );
1534 if ( pStickerKit )
1535 {
1536 m_nStickerKit = pStickerKit->nID;
1537 }
1538 else
1539 {
1540 const CEconMusicDefinition *pMusicKit = GetItemSchema()->GetMusicKitDefinitionByName( szPaintName );
1541 if ( pMusicKit )
1542 {
1543 m_nMusicKit = pMusicKit->GetID();
1544 }
1545 else
1546 {
1547 DevWarning( "Kit \"[%s]\" specified, but doesn't exist!! You're probably missing an entry in items_paintkits.txt or items_stickerkits.txt or need to run with -use_local_item_data\n", szPaintName );
1548 return false;
1549 }
1550 }
1551 }
1552 }
1553
1554 CEconItemDefinition *pDef = GetItemSchema()->GetItemDefinitionByName( pchName );
1555 if ( pDef )
1556 {
1557 m_nItemDef = pDef->GetDefinitionIndex();
1558 }
1559
1560 return true;
1561}
1562
1563//-----------------------------------------------------------------------------
1564//
1565//-----------------------------------------------------------------------------
1566CEconItemSetDefinition::CEconItemSetDefinition( void )
1567 : m_pszName( NULL )
1568 , m_pszLocalizedName( NULL )
1569 , m_pszLocalizedDescription( NULL )
1570 , m_pszUnlocalizedName( NULL )
1571 , m_iBundleItemDef( INVALID_ITEM_DEF_INDEX )
1572 , m_bIsCollection( false )
1573 , m_bIsHiddenSet( false )
1574 , m_nCraftReward( 0 )
1575{
1576}
1577
1578//-----------------------------------------------------------------------------
1579// Purpose: Copy constructor
1580//-----------------------------------------------------------------------------
1581CEconItemSetDefinition::CEconItemSetDefinition( const CEconItemSetDefinition &that )
1582{
1583 (*this) = that;
1584}
1585
1586//-----------------------------------------------------------------------------
1587// Purpose: Operator=
1588//-----------------------------------------------------------------------------
1589CEconItemSetDefinition &CEconItemSetDefinition::operator=( const CEconItemSetDefinition &other )
1590{
1591 m_pszName = other.m_pszName;
1592 m_pszLocalizedName = other.m_pszLocalizedName;
1593 m_pszUnlocalizedName = other.m_pszUnlocalizedName;
1594 m_pszLocalizedDescription = other.m_pszLocalizedDescription;
1595 m_pszUnlocalizedName = other.m_pszUnlocalizedName;
1596 m_ItemEntries = other.m_ItemEntries;
1597 m_iAttributes = other.m_iAttributes;
1598 m_iBundleItemDef = other.m_iBundleItemDef;
1599 m_bIsCollection = other.m_bIsCollection;
1600 m_bIsHiddenSet = other.m_bIsHiddenSet;
1601 m_nCraftReward = other.m_nCraftReward;
1602
1603 return *this;
1604}
1605
1606//-----------------------------------------------------------------------------
1607//
1608//-----------------------------------------------------------------------------
1609bool CEconItemSetDefinition::BInitFromKV( KeyValues *pKVItemSet, CEconItemSchema &pschema, CUtlVector<CUtlString> *pVecErrors )
1610{
1611 m_pszName = pKVItemSet->GetName();
1612
1613 m_iBundleItemDef = INVALID_ITEM_DEF_INDEX;
1614 const char *pszBundleName = pKVItemSet->GetString( "store_bundle" );
1615 if ( pszBundleName && pszBundleName[0] )
1616 {
1617 const CEconItemDefinition *pDef = pschema.GetItemDefinitionByName( pszBundleName );
1618 if ( pDef )
1619 {
1620 m_iBundleItemDef = pDef->GetDefinitionIndex();
1621 }
1622
1623 SCHEMA_INIT_CHECK(
1624 pDef != NULL,
1625 CFmtStr( "Item Set %s: Bundle definition \"%s\" was not found", m_pszName, pszBundleName ) );
1626 }
1627
1628 FOR_EACH_SUBKEY( pKVItemSet, pKVSetItem )
1629 {
1630 const char *pszName = pKVSetItem->GetName();
1631
1632 if ( !Q_strcmp( pszName, "name" ) )
1633 {
1634 SCHEMA_INIT_CHECK(
1635 m_pszLocalizedName == NULL,
1636 CFmtStr( "Item Set %s: Duplicate name specified", m_pszName ) );
1637
1638 m_pszLocalizedName = pKVSetItem->GetString();
1639 }
1640 else if ( !Q_strcmp( pszName, "set_description" ) )
1641 {
1642 m_pszLocalizedDescription = pKVSetItem->GetString();
1643 }
1644 else if ( !Q_strcmp( pszName, "unlocalized_name" ) )
1645 {
1646 m_pszUnlocalizedName = pKVSetItem->GetString();
1647 }
1648 else if ( !Q_strcmp( pszName, "is_collection" ) )
1649 {
1650 m_bIsCollection = pKVSetItem->GetBool();
1651 }
1652 else if ( !Q_strcmp( pszName, "is_hidden_set" ) )
1653 {
1654 m_bIsHiddenSet = pKVSetItem->GetBool();
1655 }
1656// else if ( !Q_strcmp( pszName, "craft_reward" ) )
1657// {
1658// const char *pchCraftReward = pKVSetItem->GetString();
1659// if ( pchCraftReward && pchCraftReward[ 0 ] != '\0' )
1660// {
1661// const CEconItemDefinition *pItemDef = pschema.GetItemDefinitionByName( pKVSetItem->GetString() );
1662//
1663// SCHEMA_INIT_CHECK(
1664// pItemDef && pItemDef->GetDefinitionIndex() > 0,
1665// CFmtStr( "Item Collection Craft Reward \"%s\" specifies invalid item def", pKVItemSet->GetName() ) );
1666//
1667// m_nCraftReward = pItemDef->GetDefinitionIndex();
1668// }
1669// }
1670 else if ( !Q_strcmp( pszName, "items" ) )
1671 {
1672 FOR_EACH_SUBKEY( pKVSetItem, pKVItem )
1673 {
1674 pszName = pKVItem->GetName();
1675
1676 item_list_entry_t entry;
1677 bool bEntrySuccess = entry.InitFromName( pszName );
1678 bEntrySuccess;
1679
1680 m_ItemEntries.AddToTail( entry );
1681 }
1682 }
1683 else if ( !Q_strcmp( pszName, "attributes" ) )
1684 {
1685 FOR_EACH_SUBKEY( pKVSetItem, pKVAttribute )
1686 {
1687 pszName = pKVAttribute->GetName();
1688
1689 const CEconItemAttributeDefinition *pDef = pschema.GetAttributeDefinitionByName( pszName );
1690// SCHEMA_INIT_CHECK(
1691// pDef != NULL,
1692// CFmtStr( "Item set %s: Attribute definition \"%s\" was not found", m_pszName, pszName ) );
1693
1694 if ( pDef )
1695 {
1696 int iIndex = m_iAttributes.AddToTail();
1697 m_iAttributes[iIndex].m_iAttribDefIndex = pDef->GetDefinitionIndex();
1698 if ( pDef->IsStoredAsInteger() )
1699 {
1700 m_iAttributes[iIndex].m_valValue = pKVAttribute->GetInt( "value" );
1701 }
1702 else
1703 {
1704 float flValue = pKVAttribute->GetFloat( "value" );
1705 Q_memcpy( &m_iAttributes[iIndex].m_valValue, &flValue, sizeof( float ) );
1706 }
1707 }
1708 }
1709 }
1710 }
1711
1712 // Sanity check.
1713 SCHEMA_INIT_CHECK( !(m_bIsCollection && m_bIsHiddenSet),
1714 CFmtStr( "Item Set %s: Invalid to set a collection to be hidden", m_pszName ) );
1715
1716 SCHEMA_INIT_CHECK( m_ItemEntries.Count() > 0,
1717 CFmtStr( "Item Set %s: Set contains no items", m_pszName ) );
1718
1719 return SCHEMA_INIT_SUCCESS();
1720}
1721
1722int CEconItemSetDefinition::GetItemRarity( int iIndex ) const
1723{
1724 int nKitRarity = 1;
1725 const CStickerKit *pStickerKit = NULL;
1726 const CPaintKit *pPaintKit = NULL;
1727 const CEconMusicDefinition *pMusicKitDefinition = NULL;
1728
1729 item_list_entry_t const &entry = m_ItemEntries[iIndex];
1730
1731 if ( entry.m_nPaintKit )
1732 {
1733 pPaintKit = GetItemSchema()->GetPaintKitDefinition( entry.m_nPaintKit );
1734 if ( pPaintKit )
1735 {
1736 nKitRarity = MAX( nKitRarity, pPaintKit->nRarity );
1737 }
1738 }
1739 if ( entry.m_nStickerKit )
1740 {
1741 pStickerKit = GetItemSchema()->GetStickerKitDefinition( entry.m_nStickerKit );
1742 if ( pStickerKit )
1743 {
1744 nKitRarity = MAX( nKitRarity, pStickerKit->nRarity );
1745 }
1746 }
1747 if ( entry.m_nMusicKit )
1748 {
1749 pMusicKitDefinition = GetItemSchema()->GetMusicDefinition( entry.m_nMusicKit );
1750 if ( pMusicKitDefinition )
1751 {
1752 nKitRarity = Max ( nKitRarity, 1/* pMusicKitDefinition->GetRarity() */);
1753 }
1754
1755 }
1756
1757 // Now combine the rarity
1758 const CEconItemDefinition *pDef = GetItemSchema()->GetItemDefinition( entry.m_nItemDef );
1759 return pDef ? EconRarity_CombinedItemAndPaintRarity( pDef->GetRarity(), nKitRarity ) : -1;
1760}
1761
1762int CEconItemSetDefinition::GetHighestItemRarityValue( void ) const
1763{
1764 int nHighestRarityValue = -1;
1765 for ( int i = 0; i < GetItemCount(); ++i )
1766 {
1767 int nRarity = GetItemRarity( i );
1768 if ( nHighestRarityValue < nRarity )
1769 {
1770 nHighestRarityValue = nRarity;
1771 }
1772 }
1773
1774 return nHighestRarityValue;
1775}
1776
1777//-----------------------------------------------------------------------------
1778//
1779//-----------------------------------------------------------------------------
1780CEconLootListDefinition::CEconLootListDefinition( void )
1781{
1782 m_bServerList = false;
1783}
1784
1785//-----------------------------------------------------------------------------
1786// Purpose: Copy constructor
1787//-----------------------------------------------------------------------------
1788CEconLootListDefinition::CEconLootListDefinition( const CEconLootListDefinition &that )
1789{
1790 (*this) = that;
1791}
1792
1793//-----------------------------------------------------------------------------
1794// Dtor
1795//-----------------------------------------------------------------------------
1796CEconLootListDefinition::~CEconLootListDefinition( void )
1797{
1798}
1799
1800
1801//-----------------------------------------------------------------------------
1802// Purpose: Operator=
1803//-----------------------------------------------------------------------------
1804CEconLootListDefinition &CEconLootListDefinition::operator=( const CEconLootListDefinition &other )
1805{
1806 m_pszName = other.m_pszName;
1807 m_ItemEntries = other.m_ItemEntries;
1808 m_AdditionalDrops = other.m_AdditionalDrops;
1809 m_bServerList = other.m_bServerList;
1810
1811 return *this;
1812}
1813
1814bool CEconLootListDefinition::AddRandomAtrributes( KeyValues *pRandomAttributesKV, CEconItemSchema &pschema, CUtlVector<CUtlString> *pVecErrors /*= NULL*/ )
1815{
1816 // We've found the random attribute block. Parse it.
1817 SCHEMA_INIT_CHECK(
1818 NULL != pRandomAttributesKV->FindKey( "chance" ),
1819 CFmtStr( "Loot List %s: Missing required field \"chance\" in the \"random_attributes\" block.", m_pszName ) );
1820
1821 random_attrib_t *randomAttrib = new random_attrib_t;
1822
1823 randomAttrib->m_flChanceOfRandomAttribute = pRandomAttributesKV->GetFloat( "chance" );
1824 randomAttrib->m_bPickAllAttributes = ( pRandomAttributesKV->GetFloat( "pick_all_attributes" ) != 0 );
1825 randomAttrib->m_flTotalAttributeWeight = 0;
1826
1827 FOR_EACH_TRUE_SUBKEY( pRandomAttributesKV, pKVAttribute )
1828 {
1829 const char *pszName = pKVAttribute->GetName();
1830
1831 if ( !Q_strcmp( pszName, "chance" ) )
1832 continue;
1833
1834 const CEconItemAttributeDefinition *pDef = pschema.GetAttributeDefinitionByName( pszName );
1835 SCHEMA_INIT_CHECK(
1836 pDef != NULL,
1837 CFmtStr( "Loot list %s: Attribute definition \"%s\" was not found", m_pszName, pszName ) );
1838
1839 if ( pDef )
1840 {
1841 lootlist_attrib_t lootListAttrib;
1842
1843 SCHEMA_INIT_SUBSTEP( lootListAttrib.BInitFromKV( m_pszName, pKVAttribute, pschema, pVecErrors ) );
1844
1845 randomAttrib->m_flTotalAttributeWeight += lootListAttrib.m_flWeight;
1846
1847 randomAttrib->m_RandomAttributes.AddToTail( lootListAttrib );
1848 }
1849 }
1850
1851 m_RandomAttribs.AddToTail( randomAttrib );
1852
1853 return true;
1854}
1855
1856//-----------------------------------------------------------------------------
1857//
1858//-----------------------------------------------------------------------------
1859bool CEconLootListDefinition::BInitFromKV( KeyValues *pKVLootList, KeyValues *pKVRandomAttributeTemplates, CEconItemSchema &pschema, CUtlVector<CUtlString> *pVecErrors, bool bServerList )
1860{
1861 m_pszName = pKVLootList->GetName();
1862
1863 m_unHeroID = 0;
1864
1865 m_bServerList = bServerList;
1866
1867 FOR_EACH_SUBKEY( pKVLootList, pKVListItem )
1868 {
1869 const char *pszName = pKVListItem->GetName();
1870
1871 if ( !Q_strcmp( pszName, "random_attributes" ) )
1872 {
1873 AddRandomAtrributes( pKVListItem, pschema, pVecErrors );
1874
1875 continue;
1876 }
1877 else if ( !Q_strcmp( pszName, "attribute_templates" ) )
1878 {
1879 if ( !pKVRandomAttributeTemplates )
1880 {
1881 // Clients don't load the attribute template list
1882 // Skip over attribute templates
1883 continue;
1884 }
1885
1886 FOR_EACH_SUBKEY( pKVListItem, pKVAttributeTemplate )
1887 {
1888 if ( pKVAttributeTemplate->GetDataType() == KeyValues::TYPE_NONE )
1889 { // Support full template specified inplace
1890 AddRandomAtrributes( pKVAttributeTemplate, pschema, pVecErrors );
1891 }
1892 else
1893 {
1894 if ( pKVAttributeTemplate->GetInt() == 0 )
1895 continue;
1896
1897 KeyValues *pKVRandomAttribute = pKVRandomAttributeTemplates->FindKey( pKVAttributeTemplate->GetName() );
1898 SCHEMA_INIT_CHECK(
1899 pKVRandomAttribute,
1900 CFmtStr( "Loot List %s: Looking for attribute template \"%s\" that couldn't be found in \"random_attribute_templates\" block.\n", m_pszName, pKVAttributeTemplate->GetName() ) );
1901
1902 AddRandomAtrributes( pKVRandomAttribute, pschema, pVecErrors );
1903 }
1904 }
1905
1906 continue;
1907 }
1908 else if ( !Q_strcmp( pszName, "public_list_contents" ) )
1909 {
1910 m_bPublicListContents = pKVListItem->GetBool();
1911 continue;
1912 }
1913 if ( !Q_strcmp( pszName, "additional_drop" ) )
1914 {
1915 float fChance = pKVListItem->GetFloat( "chance", 0.0f );
1916 bool bPremiumOnly = pKVListItem->GetBool( "premium_only", false );
1917 const char *pszLootList = pKVListItem->GetString( "loot_list", "" );
1918
1919 SCHEMA_INIT_CHECK(
1920 fChance > 0.0f && fChance <= 1.0f,
1921 CFmtStr( "Loot list %s: Invalid \"additional_drop\" chance %.2f\n", m_pszName, fChance ) );
1922
1923 SCHEMA_INIT_CHECK(
1924 pszLootList && pszLootList[0],
1925 CFmtStr( "Loot list %s: Missing \"additional_drop\" loot list name\n", m_pszName ) );
1926
1927 if ( pszLootList )
1928 {
1929 const CEconLootListDefinition *pLootListDef = pschema.GetLootListByName( pszLootList );
1930 SCHEMA_INIT_CHECK(
1931 pLootListDef,
1932 CFmtStr( "Loot list %s: Invalid \"additional_drop\" loot list \"%s\"\n", m_pszName, pszLootList ) );
1933
1934 if ( pLootListDef )
1935 {
1936 loot_list_additional_drop_t additionalDrop = { fChance, bPremiumOnly, pszLootList };
1937 m_AdditionalDrops.AddToTail( additionalDrop );
1938 }
1939 }
1940 continue;
1941 }
1942
1943 if ( !Q_strcmp( pszName, "hero" ) )
1944 {
1945 const char* pszHeroName = pKVListItem->GetString( "name" );
1946 m_unHeroID = GEconItemSchema().GetHeroID( pszHeroName );
1947 continue;
1948 }
1949
1950 item_list_entry_t entry;
1951
1952 // First, see if we've got a loot list name, for embedded loot lists
1953 int iIdx = 0;
1954
1955 if ( V_strstr( pszName, "unusual" ) != NULL )
1956 {
1957 entry.m_bIsUnusualList = true;
1958 }
1959
1960 if ( pschema.GetLootListByName( pszName, &iIdx ) )
1961 {
1962 entry.m_nItemDef = iIdx;
1963 entry.m_bIsNestedList = true;
1964 }
1965 else
1966 {
1967 bool bEntrySuccess = entry.InitFromName( pszName );
1968 bEntrySuccess;
1969
1970 }
1971
1972 // Make sure we never put non-enabled items into loot lists
1973 if ( !entry.m_bIsNestedList && entry.m_nItemDef > 0 )
1974 {
1975 const CEconItemDefinition *pItemDef = pschema.GetItemDefinition( entry.m_nItemDef );
1976 SCHEMA_INIT_CHECK(
1977 pItemDef,
1978 CFmtStr( "Loot list %s: Item definition index \"%s\" (%d) was not found\n", m_pszName, pszName, entry.m_nItemDef ) );
1979 }
1980
1981 m_ItemEntries.AddToTail( entry );
1982
1983 float fItemWeight = pKVListItem->GetFloat();
1984 SCHEMA_INIT_CHECK(
1985 fItemWeight > 0.0f,
1986 CFmtStr( "Loot list %s: Item definition index \"%s\" (%d) has invalid weight %.2f\n", m_pszName, pszName, entry.m_nItemDef, fItemWeight ) );
1987
1988 m_flWeights.AddToTail( fItemWeight );
1989 m_flTotalWeight += fItemWeight;
1990 }
1991
1992 return SCHEMA_INIT_SUCCESS();
1993}
1994
1995bool CEconLootListDefinition::HasUnusualLoot() const
1996{
1997 FOR_EACH_VEC(GetLootListContents(), i)
1998 {
1999 const item_list_entry_t& entry = GetLootListContents()[i];
2000
2001 if (entry.m_bIsUnusualList)
2002 return true;
2003
2004 if (entry.m_bIsNestedList)
2005 {
2006 const CEconLootListDefinition *pNestedLootList = GetItemSchema()->GetLootListByIndex(entry.m_nItemDef);
2007 if (pNestedLootList)
2008 {
2009 if (pNestedLootList->HasUnusualLoot())
2010 return true;
2011 }
2012 }
2013 }
2014
2015 return false;
2016}
2017
2018//-----------------------------------------------------------------------------
2019//
2020//-----------------------------------------------------------------------------
2021bool CEconLootListDefinition::GetAdditionalDrop( int iIndex, CUtlString& strLootList, float& flChance ) const
2022{
2023 if ( !m_AdditionalDrops.IsValidIndex( iIndex ) )
2024 return false;
2025
2026 strLootList = m_AdditionalDrops[iIndex].m_pszLootListDefName;
2027 flChance = m_AdditionalDrops[iIndex].m_fChance;
2028 return true;
2029}
2030
2031//-----------------------------------------------------------------------------
2032//
2033//-----------------------------------------------------------------------------
2034bool CEconLootListDefinition::GetRandomAttributeGroup( int iIndex, float& flChance, float& flTotalWeight ) const
2035{
2036 if ( !m_RandomAttribs.IsValidIndex( iIndex ) )
2037 return false;
2038
2039 flChance = m_RandomAttribs[iIndex]->m_flChanceOfRandomAttribute;
2040 flTotalWeight = m_RandomAttribs[iIndex]->m_flTotalAttributeWeight;
2041
2042 return true;
2043}
2044
2045//-----------------------------------------------------------------------------
2046//
2047//-----------------------------------------------------------------------------
2048int CEconLootListDefinition::GetRandomAttributeCount( int iGroup ) const
2049{
2050 if ( !m_RandomAttribs.IsValidIndex( iGroup ) )
2051 return false;
2052
2053 return m_RandomAttribs[iGroup]->m_RandomAttributes.Count();
2054}
2055
2056//-----------------------------------------------------------------------------
2057//
2058//-----------------------------------------------------------------------------
2059bool CEconLootListDefinition::GetRandomAttribute( int iGroup, int iIndex, float& flWeight, int& iValue, int& iDefIndex ) const
2060{
2061 Assert( !"Tell whoever is working on the item editor that loot list random attributes don't support typed attributes!" );
2062
2063 if ( !m_RandomAttribs.IsValidIndex( iGroup ) )
2064 return false;
2065
2066 if ( !m_RandomAttribs[iGroup]->m_RandomAttributes.IsValidIndex( iIndex ) )
2067 return false;
2068
2069 flWeight = m_RandomAttribs[iGroup]->m_RandomAttributes[iIndex].m_flWeight;
2070 iValue = (int) m_RandomAttribs[iGroup]->m_RandomAttributes[iIndex].m_staticAttrib.m_value.asFloat;
2071 iDefIndex = m_RandomAttribs[iGroup]->m_RandomAttributes[iIndex].m_staticAttrib.iDefIndex;
2072
2073 return true;
2074}
2075
2076//-----------------------------------------------------------------------------
2077// Outputs a keyvalues schema representation of this loot list.
2078//-----------------------------------------------------------------------------
2079KeyValues* CEconLootListDefinition::GenerateKeyValues() const
2080{
2081 KeyValues* pLootListKV = new KeyValues( m_pszName );
2082
2083 // Write out our items and weights.
2084 FOR_EACH_VEC( m_ItemEntries, i )
2085 {
2086 const CEconItemDefinition* pItemDef = GetItemSchema()->GetItemDefinition( m_ItemEntries[i].m_nItemDef );
2087 if ( !pItemDef )
2088 continue;
2089 pLootListKV->SetFloat( pItemDef->GetRawDefinition()->GetString( "name" ), m_flWeights[i] );
2090 }
2091
2092 // Write out additional drops.
2093
2094 // Write out random attributes.
2095
2096 return pLootListKV;
2097}
2098
2099//-----------------------------------------------------------------------------
2100//
2101//-----------------------------------------------------------------------------
2102void CEconLootListDefinition::PurgeItems( void )
2103{
2104 m_ItemEntries.Purge();
2105 m_flWeights.Purge();
2106 m_flTotalWeight = 0;
2107}
2108
2109
2110//-----------------------------------------------------------------------------
2111// Purpose:
2112//-----------------------------------------------------------------------------
2113bool CEconLootListDefinition::lootlist_attrib_t::BInitFromKV( const char *pszContext, KeyValues *pKVKey, CEconItemSchema &pschema, CUtlVector<CUtlString> *pVecErrors )
2114{
2115 SCHEMA_INIT_SUBSTEP( m_staticAttrib.BInitFromKV_MultiLine( pszContext, pKVKey, pVecErrors ) );
2116
2117 SCHEMA_INIT_CHECK(
2118 pKVKey->FindKey( "weight" ),
2119 CFmtStr( "Loot definition %s: Attribute \"%s\" missing required 'weight' field", pszContext, pKVKey->GetName() ) );
2120
2121 SCHEMA_INIT_CHECK(
2122 ( !pKVKey->FindKey( "range_min" ) && !pKVKey->FindKey( "range_max" ) ) ||
2123 ( !pKVKey->FindKey( "values" ) ),
2124 CFmtStr( "Loot definition %s: Attribute \"%s\" specifies both value ranges and explicit values but should only use one method or the other", pszContext, pKVKey->GetName() ) );
2125
2126 m_flWeight = pKVKey->GetFloat( "weight" );
2127 m_flRangeMin = pKVKey->GetFloat( "range_min" );
2128 m_flRangeMax = pKVKey->GetFloat( "range_max" );
2129
2130 const char* pszValues = pKVKey->GetString( "values" );
2131
2132 m_vecValues.Purge();
2133
2134 if ( pszValues && pszValues[ 0 ] )
2135 {
2136 SCHEMA_INIT_CHECK(
2137 Helper_ExtractIntegersFromValuesString( pszValues, m_vecValues ),
2138 CFmtStr( "Loot definition %s: Attribute \"%s\" specifies a malformed values range: %s.", pszContext, pKVKey->GetName(), pszValues ) );
2139 }
2140
2141
2142 // validate that quest id values are valid
2143 // TODO: create a common class for indexed definition classes and have this code validate all of them
2144 if ( !strcmp( pKVKey->GetName(), "quest id" ) )
2145 {
2146 if ( m_flRangeMin != 0 || m_flRangeMax != 0 )
2147 {
2148 for ( int i = m_flRangeMin; i <= m_flRangeMax; i++ )
2149 {
2150 SCHEMA_INIT_CHECK(
2151 GetItemSchema()->GetQuestDefinition( i ),
2152 CFmtStr( "Loot definition %s: Attribute \"%s\" specifies a quest id that does not exist: %d.\n", pszContext, pKVKey->GetName(), i ) );
2153 }
2154 }
2155 else if ( m_vecValues.Count() != 0 )
2156 {
2157 FOR_EACH_VEC( m_vecValues, i )
2158 {
2159 SCHEMA_INIT_CHECK(
2160 GetItemSchema()->GetQuestDefinition( m_vecValues[ i ] ),
2161 CFmtStr( "Loot definition %s: Attribute \"%s\" specifies a quest id that does not exist: %d.\n", pszContext, pKVKey->GetName(), m_vecValues[ i ] ) );
2162 }
2163 }
2164 }
2165 else if ( !strcmp( pKVKey->GetName(), "quest reward lootlist" ) )
2166 {
2167 const CEconItemSchema::RevolvingLootListDefinitionMap_t* pQuestRewardLL= &( GetItemSchema()->GetQuestRewardLootLists() );
2168
2169 if ( m_flRangeMin != 0 || m_flRangeMax != 0 )
2170 {
2171 for ( int i = m_flRangeMin; i <= m_flRangeMax; i++ )
2172 {
2173 int iLLIndex = pQuestRewardLL->Find( i );
2174
2175 SCHEMA_INIT_CHECK(
2176 pQuestRewardLL->IsValidIndex( iLLIndex ),
2177 CFmtStr( "Loot definition %s: Attribute \"%s\" specifies a quest reward lootlist that does not exist: %d.\n", pszContext, pKVKey->GetName(), i ) );
2178 }
2179 }
2180 else if ( m_vecValues.Count() != 0 )
2181 {
2182 FOR_EACH_VEC( m_vecValues, i )
2183 {
2184 int iLLIndex = pQuestRewardLL->Find( m_vecValues[ i ] );
2185
2186 SCHEMA_INIT_CHECK(
2187 pQuestRewardLL->IsValidIndex( iLLIndex ),
2188 CFmtStr( "Loot definition %s: Attribute \"%s\" specifies a quest reward lootlist that does not exist: %d.\n", pszContext, pKVKey->GetName(), m_vecValues[ i ] ) );
2189 }
2190 }
2191
2192
2193 }
2194
2195 return SCHEMA_INIT_SUCCESS();
2196}
2197
2198//-----------------------------------------------------------------------------
2199// Purpose: Constructor
2200//-----------------------------------------------------------------------------
2201CEconCraftingRecipeDefinition::CEconCraftingRecipeDefinition( void )
2202{
2203 m_nDefIndex = 0;
2204 m_wszName[ 0 ] = L'\0';
2205 m_wszDesc[ 0 ] = L'\0';
2206}
2207
2208//-----------------------------------------------------------------------------
2209// Purpose: Initialize the attribute definition
2210// Input: pKVAttribute - The KeyValues representation of the attribute
2211// schema - The overall item schema for this attribute
2212// pVecErrors - An optional vector that will contain error messages if
2213// the init fails.
2214// Output: True if initialization succeeded, false otherwise
2215//-----------------------------------------------------------------------------
2216bool CEconCraftingRecipeDefinition::BInitFromKV( KeyValues *pKVRecipe, CEconItemSchema &pschema, CUtlVector<CUtlString> *pVecErrors /* = NULL */ )
2217{
2218 m_nDefIndex = Q_atoi( pKVRecipe->GetName() );
2219
2220 // Check for required fields
2221 SCHEMA_INIT_CHECK(
2222 NULL != pKVRecipe->FindKey( "input_items" ),
2223 CFmtStr( "Recipe definition %d: Missing required field \"input_items\"", m_nDefIndex ) );
2224
2225 SCHEMA_INIT_CHECK(
2226 NULL != pKVRecipe->FindKey( "output_items" ),
2227 CFmtStr( "Recipe definition %d: Missing required field \"output_items\"", m_nDefIndex ) );
2228
2229 m_bDisabled = pKVRecipe->GetBool( "disabled" );
2230 m_strName = pKVRecipe->GetString( "name" );
2231 m_strN_A = pKVRecipe->GetString( "n_A" );
2232 m_strDescInputs = pKVRecipe->GetString( "desc_inputs" );
2233 m_strDescOutputs = pKVRecipe->GetString( "desc_outputs" );
2234 m_strDI_A = pKVRecipe->GetString( "di_A" );
2235 m_strDI_B = pKVRecipe->GetString( "di_B" );
2236 m_strDI_C = pKVRecipe->GetString( "di_C" );
2237 m_strDO_A = pKVRecipe->GetString( "do_A" );
2238 m_strDO_B = pKVRecipe->GetString( "do_B" );
2239 m_strDO_C = pKVRecipe->GetString( "do_C" );
2240
2241 m_bRequiresAllSameClass = pKVRecipe->GetBool( "all_same_class" );
2242 m_bRequiresAllSameSlot = pKVRecipe->GetBool( "all_same_slot" );
2243 m_iCacheClassUsageForOutputFromItem = pKVRecipe->GetInt( "add_class_usage_to_output", -1 );
2244 m_iCacheSlotUsageForOutputFromItem = pKVRecipe->GetInt( "add_slot_usage_to_output", -1 );
2245 m_iCacheSetForOutputFromItem = pKVRecipe->GetInt( "add_set_to_output", -1 );
2246 m_bAlwaysKnown = pKVRecipe->GetBool( "always_known", true );
2247 m_bPremiumAccountOnly = pKVRecipe->GetBool( "premium_only", false );
2248 m_iCategory = (recipecategories_t)StringFieldToInt( pKVRecipe->GetString("category"), g_szRecipeCategoryStrings, ARRAYSIZE(g_szRecipeCategoryStrings) );
2249 m_iFilter = pKVRecipe->GetInt( "filter", -1 );
2250
2251 // Read in all the input items
2252 KeyValues *pKVInputItems = pKVRecipe->FindKey( "input_items" );
2253 if ( NULL != pKVInputItems )
2254 {
2255 FOR_EACH_TRUE_SUBKEY( pKVInputItems, pKVInputItem )
2256 {
2257 int index = m_InputItemsCriteria.AddToTail();
2258 SCHEMA_INIT_SUBSTEP( m_InputItemsCriteria[index].BInitFromKV( pKVInputItem, pschema ) );
2259
2260 // Recipes ignore the enabled flag when generating items
2261 m_InputItemsCriteria[index].SetIgnoreEnabledFlag( true );
2262
2263 index = m_InputItemDupeCounts.AddToTail();
2264 m_InputItemDupeCounts[index] = atoi( pKVInputItem->GetName() );
2265 }
2266 }
2267
2268 // Read in all the output items
2269 KeyValues *pKVOutputItems = pKVRecipe->FindKey( "output_items" );
2270 if ( NULL != pKVOutputItems )
2271 {
2272 FOR_EACH_TRUE_SUBKEY( pKVOutputItems, pKVOutputItem )
2273 {
2274 int index = m_OutputItemsCriteria.AddToTail();
2275 SCHEMA_INIT_SUBSTEP( m_OutputItemsCriteria[index].BInitFromKV( pKVOutputItem, pschema ) );
2276
2277 // Recipes ignore the enabled flag when generating items
2278 m_OutputItemsCriteria[index].SetIgnoreEnabledFlag( true );
2279 }
2280 }
2281
2282 GenerateLocStrings();
2283
2284 return SCHEMA_INIT_SUCCESS();
2285}
2286
2287bool CEconCraftingRecipeDefinition::BInitFromSet( const IEconItemSetDefinition *pSet, CEconItemSchema &pschema, CUtlVector<CUtlString> *pVecErrors /* = NULL */ )
2288{
2289 m_bDisabled = false;
2290 m_strName = "#RT_T_A"; // "Trade In %s1"
2291 m_strN_A = pSet->GetLocKey(); // "The Office Collection"
2292 m_strDescInputs = "#RDI_AB"; // "Requires: %s1 %s2"
2293 m_strDescOutputs = "RDO_AB"; // "Produces: %s1 %s2"
2294 m_strDI_A = "1"; // "1"
2295 m_strDI_B = pSet->GetLocKey(); // "The Office Collection"
2296 m_strDI_C = NULL;
2297 m_strDO_A = "1"; // "1"
2298
2299 const CEconItemDefinition *pItemDef = pschema.GetItemDefinition( pSet->GetCraftReward() );
2300
2301 SCHEMA_INIT_CHECK(
2302 pItemDef && pItemDef->GetDefinitionIndex() > 0,
2303 CFmtStr( "Item Collection Craft Reward (%d) specifies invalid item def", pSet->GetCraftReward() ) );
2304
2305 m_strDO_B = pItemDef->GetItemBaseName();
2306 m_strDO_C = NULL;
2307
2308 m_bRequiresAllSameClass = false;
2309 m_bRequiresAllSameSlot = false;
2310 m_iCacheClassUsageForOutputFromItem = -1;
2311 m_iCacheSlotUsageForOutputFromItem = -1;
2312 m_iCacheSetForOutputFromItem = -1;
2313 m_bAlwaysKnown = true;
2314 m_bPremiumAccountOnly = false;
2315 m_iCategory = (recipecategories_t)StringFieldToInt( "crafting", g_szRecipeCategoryStrings, ARRAYSIZE(g_szRecipeCategoryStrings) );
2316 m_iFilter = -1;
2317
2318 for ( int i = 0; i < pSet->GetItemCount(); ++i )
2319 {
2320 int index = m_InputItemsCriteria.AddToTail();
2321 SCHEMA_INIT_SUBSTEP( m_InputItemsCriteria[index].BInitFromItemAndPaint( pSet->GetItemDef( i ), pSet->GetItemPaintKit( i ), pschema ) );
2322
2323 m_InputItemsCriteria[index].SetIgnoreEnabledFlag( true );
2324
2325 index = m_InputItemDupeCounts.AddToTail();
2326 m_InputItemDupeCounts[index] = 1;
2327 }
2328
2329 int index = m_OutputItemsCriteria.AddToTail();
2330 SCHEMA_INIT_SUBSTEP( m_OutputItemsCriteria[index].BInitFromItemAndPaint( pSet->GetCraftReward(), 0, pschema ) );
2331
2332 GenerateLocStrings();
2333
2334 return SCHEMA_INIT_SUCCESS();
2335}
2336
2337
2338//-----------------------------------------------------------------------------
2339// Purpose: Serializes the criteria to and from messages
2340//-----------------------------------------------------------------------------
2341bool CEconCraftingRecipeDefinition::BSerializeToMsg( CSOItemRecipe & msg ) const
2342{
2343 msg.set_def_index( m_nDefIndex );
2344 msg.set_name( m_strName );
2345 msg.set_n_a( m_strN_A );
2346 msg.set_desc_inputs( m_strDescInputs );
2347 msg.set_desc_outputs( m_strDescOutputs );
2348 msg.set_di_a( m_strDI_A );
2349 msg.set_di_b( m_strDI_B );
2350 msg.set_di_c( m_strDI_C );
2351 msg.set_do_a( m_strDO_A );
2352 msg.set_do_b( m_strDO_B );
2353 msg.set_do_c( m_strDO_C );
2354 msg.set_requires_all_same_class( m_bRequiresAllSameClass );
2355 msg.set_requires_all_same_slot( m_bRequiresAllSameSlot );
2356 msg.set_class_usage_for_output( m_iCacheClassUsageForOutputFromItem );
2357 msg.set_slot_usage_for_output( m_iCacheSlotUsageForOutputFromItem );
2358 msg.set_set_for_output( m_iCacheSetForOutputFromItem );
2359
2360 FOR_EACH_VEC( m_InputItemsCriteria, i )
2361 {
2362 CSOItemCriteria *pCrit = msg.add_input_items_criteria();
2363 if ( !m_InputItemsCriteria[i].BSerializeToMsg( *pCrit ) )
2364 return false;
2365 }
2366
2367 FOR_EACH_VEC( m_InputItemDupeCounts, i )
2368 {
2369 msg.add_input_item_dupe_counts( m_InputItemDupeCounts[i] );
2370 }
2371
2372 FOR_EACH_VEC( m_OutputItemsCriteria, i )
2373 {
2374 CSOItemCriteria *pCrit = msg.add_output_items_criteria();
2375 if ( !m_OutputItemsCriteria[i].BSerializeToMsg( *pCrit ) )
2376 return false;
2377 }
2378
2379 return true;
2380}
2381
2382//-----------------------------------------------------------------------------
2383// Purpose: Serializes the criteria to and from messages
2384//-----------------------------------------------------------------------------
2385bool CEconCraftingRecipeDefinition::BDeserializeFromMsg( const CSOItemRecipe & msg )
2386{
2387 m_nDefIndex = msg.def_index();
2388 m_strName = msg.name().c_str();
2389 m_strN_A = msg.n_a().c_str();
2390 m_strDescInputs = msg.desc_inputs().c_str();
2391 m_strDescOutputs = msg.desc_outputs().c_str();
2392 m_strDI_A = msg.di_a().c_str();
2393 m_strDI_B = msg.di_b().c_str();
2394 m_strDI_C = msg.di_c().c_str();
2395 m_strDO_A = msg.do_a().c_str();
2396 m_strDO_B = msg.do_b().c_str();
2397 m_strDO_C = msg.do_c().c_str();
2398
2399 m_bRequiresAllSameClass = msg.requires_all_same_class();
2400 m_bRequiresAllSameSlot = msg.requires_all_same_slot();
2401 m_iCacheClassUsageForOutputFromItem = msg.class_usage_for_output();
2402 m_iCacheSlotUsageForOutputFromItem = msg.slot_usage_for_output();
2403 m_iCacheSetForOutputFromItem = msg.set_for_output();
2404
2405 // Read how many input items there are
2406 uint32 unCount = msg.input_items_criteria_size();
2407 m_InputItemsCriteria.SetSize( unCount );
2408 for ( uint32 i = 0; i < unCount; i++ )
2409 {
2410 if ( !m_InputItemsCriteria[i].BDeserializeFromMsg( msg.input_items_criteria( i ) ) )
2411 return false;
2412 }
2413
2414 // Read how many input item dupe counts there are
2415 unCount = msg.input_item_dupe_counts_size();
2416 m_InputItemDupeCounts.SetSize( unCount );
2417 for ( uint32 i = 0; i < unCount; i++ )
2418 {
2419 m_InputItemDupeCounts[i] = msg.input_item_dupe_counts( i );
2420 }
2421
2422 // Read how many output items there are
2423 unCount = msg.output_items_criteria_size();
2424 m_OutputItemsCriteria.SetSize( unCount );
2425 for ( uint32 i = 0; i < unCount; i++ )
2426 {
2427 if ( !m_OutputItemsCriteria[i].BDeserializeFromMsg( msg.output_items_criteria( i ) ) )
2428 return false;
2429 }
2430
2431 GenerateLocStrings();
2432
2433 return true;
2434}
2435
2436//-----------------------------------------------------------------------------
2437// Purpose: Returns true if the vector contains a set of items that matches the inputs for this recipe
2438// Note it will fail if the vector contains extra items that aren't needed.
2439//
2440//-----------------------------------------------------------------------------
2441bool CEconCraftingRecipeDefinition::ItemListMatchesInputs( const CUtlVector< CEconItem* > &vecCraftingItems, bool bAllowPartialMatch /*= false*/ ) const
2442{
2443 bool bResult = true;
2444
2445 CUtlVector< uint8 > arrDupesUsed;
2446 arrDupesUsed.SetCount( m_InputItemsCriteria.Count() );
2447
2448 uint8 *pDupesUsed = arrDupesUsed.Base();
2449 memset( pDupesUsed, 0, m_InputItemsCriteria.Count() );
2450
2451 int nSet = -1;
2452
2453 if ( ( GetFilter() == CRAFT_FILTER_COLLECT || GetFilter() == CRAFT_FILTER_TRADEUP ) &&
2454 vecCraftingItems.Count() > 0 )
2455 {
2456 nSet = vecCraftingItems[ 0 ]->GetItemSetIndex();
2457
2458 if ( nSet < 0 )
2459 {
2460 // This item must be in a set!
2461 return false;
2462 }
2463 }
2464
2465 FOR_EACH_VEC( vecCraftingItems, nItem )
2466 {
2467 CEconItem *pEconItem = vecCraftingItems[ nItem ];
2468
2469#if 0 // Relaxing set restriction on crafting
2470 if ( nSet >= 0 && pEconItem->GetItemSetIndex() != nSet )
2471 {
2472 bResult = false;
2473 break;
2474 }
2475#endif
2476
2477 // Any items at the top rarity of their set are illegal in crafting
2478 if ( GetFilter() == CRAFT_FILTER_TRADEUP )
2479 {
2480 const IEconItemSetDefinition *pSetDef = GetItemSchema()->GetItemSet( vecCraftingItems[ nItem ]->GetItemSetIndex() );
2481 if ( !pSetDef )
2482 {
2483 return false;
2484 }
2485
2486 bool bHasItemsAtNextRarityTier = false;
2487 for ( int i = 0; i < pSetDef->GetItemCount(); ++i )
2488 {
2489 if ( pSetDef->GetItemRarity( i ) == pEconItem->GetRarity() + 1 )
2490 {
2491 bHasItemsAtNextRarityTier = true;
2492 break;
2493 }
2494 }
2495
2496 if ( !bHasItemsAtNextRarityTier )
2497 return false;
2498 }
2499
2500 bool bFoundCriteriaMatch = false;
2501
2502 FOR_EACH_VEC( m_InputItemsCriteria, nCriteria )
2503 {
2504 const CItemSelectionCriteria *pCriteria = &( m_InputItemsCriteria[ nCriteria ] );
2505 if ( !pCriteria )
2506 continue;
2507
2508 // Skip if we've used this criteria to the limit
2509 if ( pDupesUsed[ nCriteria ] >= m_InputItemDupeCounts[ nCriteria ] )
2510 continue;
2511
2512 bFoundCriteriaMatch = pCriteria->BEvaluate( pEconItem, *GetItemSchema() );
2513 if ( bFoundCriteriaMatch )
2514 {
2515 pDupesUsed[ nCriteria ]++;
2516 break;
2517 }
2518 }
2519
2520 if ( !bFoundCriteriaMatch )
2521 {
2522 // Did the item not fit any criteria!
2523 bResult = false;
2524 break;
2525 }
2526 }
2527
2528 if ( !bAllowPartialMatch )
2529 {
2530 // Make sure we've matched ALL the criteria
2531 FOR_EACH_VEC( m_InputItemsCriteria, nCriteria )
2532 {
2533 if ( pDupesUsed[ nCriteria ] < m_InputItemDupeCounts[ nCriteria ] )
2534 {
2535 // Not all criteria dupes were matched
2536 bResult = false;
2537 break;
2538 }
2539 }
2540 }
2541
2542 return bResult;
2543}
2544
2545//-----------------------------------------------------------------------------
2546// Purpose: Constructor
2547//-----------------------------------------------------------------------------
2548int CEconCraftingRecipeDefinition::GetTotalInputItemsRequired( void ) const
2549{
2550 int iCount = 0;
2551 FOR_EACH_VEC( m_InputItemsCriteria, i )
2552 {
2553 if ( m_InputItemDupeCounts[i] )
2554 {
2555 iCount += m_InputItemDupeCounts[i];
2556 }
2557 else
2558 {
2559 iCount++;
2560 }
2561 }
2562 return iCount;
2563}
2564
2565#if defined( CLIENT_DLL )
2566wchar_t *LocalizeRecipeStringPiece( const char *pszString, wchar_t *pszConverted, int nConvertedSizeInBytes )
2567{
2568 if ( !pszString )
2569 return L"";
2570
2571 if ( pszString[0] == '#' )
2572 return g_pVGuiLocalize->Find( pszString );
2573
2574 g_pVGuiLocalize->ConvertANSIToUnicode( pszString, pszConverted, nConvertedSizeInBytes );
2575 return pszConverted;
2576}
2577#endif
2578
2579void CEconCraftingRecipeDefinition::GenerateLocStrings( void )
2580{
2581#if defined( CLIENT_DLL )
2582 wchar_t *pName_A = g_pVGuiLocalize->Find( GetName_A() );
2583 g_pVGuiLocalize->ConstructString( m_wszName, sizeof( m_wszName ), g_pVGuiLocalize->Find( GetName() ), 1, pName_A );
2584
2585 wchar_t wcTmpA[32];
2586 wchar_t wcTmpB[32];
2587 wchar_t wcTmpC[32];
2588 wchar_t wcTmp[512];
2589
2590 // Build the input string
2591 wchar_t *pInp_A = LocalizeRecipeStringPiece( GetDescI_A(), wcTmpA, sizeof( wcTmpA ) );
2592 wchar_t *pInp_B = LocalizeRecipeStringPiece( GetDescI_B(), wcTmpB, sizeof( wcTmpB ) );
2593 wchar_t *pInp_C = LocalizeRecipeStringPiece( GetDescI_C(), wcTmpC, sizeof( wcTmpC ) );
2594 g_pVGuiLocalize->ConstructString( m_wszDesc, sizeof( m_wszDesc ), g_pVGuiLocalize->Find( GetDescInputs() ), 3, pInp_A, pInp_B, pInp_C );
2595
2596 // Build the output string
2597 wchar_t *pOut_A = LocalizeRecipeStringPiece( GetDescO_A(), wcTmpA, sizeof( wcTmpA ) );
2598 wchar_t *pOut_B = LocalizeRecipeStringPiece( GetDescO_B(), wcTmpB, sizeof( wcTmpB ) );
2599 wchar_t *pOut_C = LocalizeRecipeStringPiece( GetDescO_C(), wcTmpC, sizeof( wcTmpC ) );
2600 g_pVGuiLocalize->ConstructString( wcTmp, sizeof( wcTmp ), g_pVGuiLocalize->Find( GetDescOutputs() ), 3, pOut_A, pOut_B, pOut_C );
2601
2602 // Concatenate, and mark the text changes
2603 V_wcscat_safe( m_wszDesc, L"\n" );
2604 V_wcscat_safe( m_wszDesc, wcTmp );
2605#endif
2606}
2607
2608//-----------------------------------------------------------------------------
2609// Purpose:
2610//-----------------------------------------------------------------------------
2611#define GC_SCH_REFERENCE( TAttribSchType )
2612
2613
2614//-----------------------------------------------------------------------------
2615// Purpose:
2616//-----------------------------------------------------------------------------
2617unsigned int Internal_GetAttributeTypeUniqueIdentifierNextValue()
2618{
2619 static unsigned int s_unUniqueCounter = 0;
2620
2621 unsigned int unCounter = s_unUniqueCounter;
2622 s_unUniqueCounter++;
2623 return unCounter;
2624}
2625
2626
2627template < typename T >
2628unsigned int GetAttributeTypeUniqueIdentifier()
2629{
2630 static unsigned int s_unUniqueCounter = Internal_GetAttributeTypeUniqueIdentifierNextValue();
2631 return s_unUniqueCounter;
2632}
2633
2634//-----------------------------------------------------------------------------
2635// Purpose:
2636//-----------------------------------------------------------------------------
2637template < GC_SCH_REFERENCE( typename TAttribSchType ) typename TAttribInMemoryType >
2638class CSchemaAttributeTypeBase : public ISchemaAttributeTypeBase<TAttribInMemoryType>
2639{
2640public:
2641};
2642
2643//-----------------------------------------------------------------------------
2644// Purpose:
2645//-----------------------------------------------------------------------------
2646template < GC_SCH_REFERENCE( typename TAttribSchType ) typename TProtobufValueType >
2647class CSchemaAttributeTypeProtobufBase : public CSchemaAttributeTypeBase< GC_SCH_REFERENCE( TAttribSchType ) TProtobufValueType >
2648{
2649public:
2650 virtual void ConvertTypedValueToByteStream( const TProtobufValueType& typedValue, ::std::string *out_psBytes ) const OVERRIDE
2651 {
2652 DbgVerify( typedValue.SerializeToString( out_psBytes ) );
2653 }
2654
2655 virtual void ConvertByteStreamToTypedValue( const ::std::string& sBytes, TProtobufValueType *out_pTypedValue ) const OVERRIDE
2656 {
2657 DbgVerify( out_pTypedValue->ParseFromString( sBytes ) );
2658 }
2659
2660 virtual bool BConvertStringToEconAttributeValue( const CEconItemAttributeDefinition *pAttrDef, const char *pszValue, union attribute_data_union_t *out_pValue ) const OVERRIDE
2661 {
2662 Assert( pAttrDef );
2663 Assert( out_pValue );
2664
2665 std::string sValue( pszValue );
2666 TProtobufValueType typedValue;
2667 if ( !google::protobuf::TextFormat::ParseFromString( sValue, &typedValue ) )
2668 return false;
2669
2670 this->ConvertTypedValueToEconAttributeValue( typedValue, out_pValue );
2671 return true;
2672 }
2673
2674 virtual void ConvertEconAttributeValueToString( const CEconItemAttributeDefinition *pAttrDef, const attribute_data_union_t& value, std::string *out_ps ) const OVERRIDE
2675 {
2676 Assert( pAttrDef );
2677 Assert( out_ps );
2678
2679 google::protobuf::TextFormat::PrintToString( this->GetTypedValueContentsFromEconAttributeValue( value ), out_ps );
2680 }
2681};
2682
2683//-----------------------------------------------------------------------------
2684// Purpose:
2685//-----------------------------------------------------------------------------
2686class CSchemaAttributeType_String : public CSchemaAttributeTypeProtobufBase< GC_SCH_REFERENCE( CSchItemAttributeString ) CAttribute_String >
2687{
2688public:
2689 // We intentionally override the convert-to-/convert-from-string functions for strings so that string literals can be
2690 // specified in the schema, etc. without worrying about the protobuf text format.
2691 virtual bool BConvertStringToEconAttributeValue( const CEconItemAttributeDefinition *pAttrDef, const char *pszValue, union attribute_data_union_t *out_pValue ) const OVERRIDE
2692 {
2693 Assert( pAttrDef );
2694 Assert( out_pValue );
2695
2696 CAttribute_String typedValue;
2697 typedValue.set_value( pszValue );
2698
2699 this->ConvertTypedValueToEconAttributeValue( typedValue, out_pValue );
2700
2701 return true;
2702 }
2703
2704 virtual void ConvertEconAttributeValueToString( const CEconItemAttributeDefinition *pAttrDef, const attribute_data_union_t& value, std::string *out_ps ) const OVERRIDE
2705 {
2706 Assert( pAttrDef );
2707 Assert( out_ps );
2708
2709 *out_ps = this->GetTypedValueContentsFromEconAttributeValue( value ).value().c_str();
2710 }
2711};
2712
2713void CopyStringAttributeValueToCharPointerOutput( const CAttribute_String *pValue, const char **out_pValue )
2714{
2715 Assert( pValue );
2716 Assert( out_pValue );
2717
2718 *out_pValue = pValue->value().c_str();
2719}
2720
2721//-----------------------------------------------------------------------------
2722// Purpose:
2723//-----------------------------------------------------------------------------
2724class CSchemaAttributeType_Vector : public CSchemaAttributeTypeBase< GC_SCH_REFERENCE( CSchItemAttributeVector ) Vector >
2725{
2726public:
2727 // We intentionally override the convert-to-/convert-from-string functions for strings so that string literals can be
2728 // specified in the schema, etc. without worrying about the protobuf text format.
2729 virtual bool BConvertStringToEconAttributeValue( const CEconItemAttributeDefinition *pAttrDef, const char *pszValue, union attribute_data_union_t *out_pValue ) const OVERRIDE
2730 {
2731 Assert( pAttrDef );
2732 Assert( out_pValue );
2733
2734 Vector typedValue;
2735 if ( sscanf( pszValue, "%f %f %f", &typedValue.x, &typedValue.y, &typedValue.z ) < 3 )
2736 {
2737 AssertMsg1( false, "Vector attribute value has invalid string: %s", pszValue );
2738 }
2739
2740 this->ConvertTypedValueToEconAttributeValue( typedValue, out_pValue );
2741
2742 return true;
2743 }
2744
2745 virtual void ConvertEconAttributeValueToString( const CEconItemAttributeDefinition *pAttrDef, const attribute_data_union_t& value, std::string *out_ps ) const OVERRIDE
2746 {
2747 Assert( pAttrDef );
2748 Assert( out_ps );
2749
2750 Vector typedValue;
2751 this->ConvertEconAttributeValueToTypedValue( value, &typedValue );
2752
2753 char szTemp[ 256 ];
2754 V_sprintf_safe( szTemp, "%f %f %f", typedValue[ 0 ], typedValue[ 1 ], typedValue[ 2 ] );
2755
2756 *out_ps = szTemp;
2757 }
2758
2759 virtual void ConvertTypedValueToByteStream( const Vector& typedValue, ::std::string *out_psBytes ) const OVERRIDE
2760 {
2761 Assert( out_psBytes );
2762 Assert( out_psBytes->size() == 0 );
2763
2764 out_psBytes->resize( sizeof( Vector ) );
2765 *reinterpret_cast<Vector *>( &((*out_psBytes)[0]) ) = typedValue;
2766 }
2767
2768 virtual void ConvertByteStreamToTypedValue( const ::std::string& sBytes, Vector *out_pTypedValue ) const OVERRIDE
2769 {
2770 Assert( out_pTypedValue );
2771 Assert( sBytes.size() == sizeof( Vector ) );
2772
2773 *out_pTypedValue = *reinterpret_cast<const Vector *>( &sBytes[0] );
2774 }
2775};
2776
2777//-----------------------------------------------------------------------------
2778// Purpose:
2779//-----------------------------------------------------------------------------
2780class CSchemaAttributeType_Uint32 : public CSchemaAttributeTypeBase< GC_SCH_REFERENCE( CSchItemAttributeUint32 ) uint32 >
2781{
2782public:
2783 virtual bool BConvertStringToEconAttributeValue( const CEconItemAttributeDefinition *pAttrDef, const char *pszValue, union attribute_data_union_t *out_pValue ) const OVERRIDE
2784 {
2785 Assert( pAttrDef );
2786 Assert( out_pValue );
2787
2788 out_pValue->asUint32 = Q_atoi( pszValue );
2789 return true;
2790 }
2791
2792 virtual void ConvertEconAttributeValueToString( const CEconItemAttributeDefinition *pAttrDef, const attribute_data_union_t& value, std::string *out_ps ) const OVERRIDE
2793 {
2794 Assert( pAttrDef );
2795 Assert( out_ps );
2796
2797 *out_ps = CFmtStr( "%u", value.asUint32 ).Get();
2798 }
2799
2800 virtual void ConvertTypedValueToByteStream( const uint32& typedValue, ::std::string *out_psBytes ) const OVERRIDE
2801 {
2802 Assert( out_psBytes );
2803 Assert( out_psBytes->size() == 0 );
2804
2805 out_psBytes->resize( sizeof( uint32 ) );
2806 *reinterpret_cast<uint32 *>( &((*out_psBytes)[0]) ) = typedValue;
2807 }
2808
2809 virtual void ConvertByteStreamToTypedValue( const ::std::string& sBytes, uint32 *out_pTypedValue ) const OVERRIDE
2810 {
2811 Assert( out_pTypedValue );
2812 Assert( sBytes.size() == sizeof( uint32 ) );
2813
2814 *out_pTypedValue = *reinterpret_cast<const uint32 *>( &sBytes[0] );
2815 }
2816
2817 virtual bool BSupportsGameplayModificationAndNetworking() const OVERRIDE
2818 {
2819 return true;
2820 }
2821};
2822
2823//-----------------------------------------------------------------------------
2824// Purpose:
2825//-----------------------------------------------------------------------------
2826class CSchemaAttributeType_Float : public CSchemaAttributeTypeBase< GC_SCH_REFERENCE( CSchItemAttributeFloat ) float >
2827{
2828public:
2829 virtual bool BConvertStringToEconAttributeValue( const CEconItemAttributeDefinition *pAttrDef, const char *pszValue, union attribute_data_union_t *out_pValue ) const OVERRIDE
2830 {
2831 Assert( pAttrDef );
2832 Assert( out_pValue );
2833
2834 out_pValue->asFloat = Q_atof( pszValue );
2835 return true;
2836 }
2837
2838 virtual void ConvertEconAttributeValueToString( const CEconItemAttributeDefinition *pAttrDef, const attribute_data_union_t& value, std::string *out_ps ) const OVERRIDE
2839 {
2840 Assert( pAttrDef );
2841 Assert( out_ps );
2842
2843 *out_ps = CFmtStr( "%f", value.asFloat ).Get();
2844 }
2845
2846 virtual void ConvertTypedValueToByteStream( const float& typedValue, ::std::string *out_psBytes ) const OVERRIDE
2847 {
2848 Assert( out_psBytes );
2849 Assert( out_psBytes->size() == 0 );
2850
2851 out_psBytes->resize( sizeof( float ) );
2852 *reinterpret_cast<float *>( &((*out_psBytes)[0]) ) = typedValue; // overwrite string contents (sizeof( float ) bytes)
2853 }
2854
2855 virtual void ConvertByteStreamToTypedValue( const ::std::string& sBytes, float *out_pTypedValue ) const OVERRIDE
2856 {
2857 Assert( out_pTypedValue );
2858 Assert( sBytes.size() == sizeof( float ) );
2859
2860 *out_pTypedValue = *reinterpret_cast<const float *>( &sBytes[0] );
2861 }
2862
2863 virtual bool BSupportsGameplayModificationAndNetworking() const OVERRIDE
2864 {
2865 return true;
2866 }
2867
2868 virtual bool OnIterateAttributeValue( IEconItemAttributeIterator *pIterator, const CEconItemAttributeDefinition *pAttrDef, const attribute_data_union_t& value ) const OVERRIDE
2869 {
2870 Assert( pIterator );
2871 Assert( pAttrDef );
2872
2873 // Call the appropriate virtual function on our iterator based on whatever type we represent.
2874 return pIterator->OnIterateAttributeValue( pAttrDef, value.asFloat );
2875 }
2876};
2877
2878//-----------------------------------------------------------------------------
2879// Purpose:
2880//-----------------------------------------------------------------------------
2881class CSchemaAttributeType_Default : public CSchemaAttributeTypeBase< GC_SCH_REFERENCE( CSchItemAttribute ) attrib_value_t >
2882{
2883public:
2884 virtual bool BConvertStringToEconAttributeValue( const CEconItemAttributeDefinition *pAttrDef, const char *pszValue, union attribute_data_union_t *out_pValue ) const OVERRIDE
2885 {
2886 Assert( pAttrDef );
2887 Assert( out_pValue );
2888
2889 // This is terrible backwards-compatibility code to support the pulling of values from econ asset classes.
2890 if ( pAttrDef->IsStoredAsInteger() )
2891 {
2892 out_pValue->asUint32 = pszValue ? (uint32)V_atoui64( pszValue ) : 0;
2893 }
2894 else if ( pAttrDef->IsStoredAsFloat() )
2895 {
2896 out_pValue->asFloat = pszValue ? V_atof( pszValue ) : 0.0f;
2897 }
2898 else
2899 {
2900 Assert( !"Unknown storage type for CSchemaAttributeType_Default::BConvertStringToEconAttributeValue()!" );
2901 return false;
2902 }
2903
2904 return true;
2905 }
2906
2907 virtual void ConvertEconAttributeValueToString( const CEconItemAttributeDefinition *pAttrDef, const attribute_data_union_t& value, std::string *out_ps ) const OVERRIDE
2908 {
2909 Assert( pAttrDef );
2910 Assert( out_ps );
2911
2912 if( pAttrDef->IsStoredAsFloat() )
2913 {
2914 *out_ps = CFmtStr( "%f", value.asFloat ).Get();
2915 }
2916 else if( pAttrDef->IsStoredAsInteger() )
2917 {
2918 *out_ps = CFmtStr( "%u", value.asUint32 ).Get();
2919 }
2920 else
2921 {
2922 Assert( !"Unknown storage type for CSchemaAttributeType_Default::ConvertEconAttributeValueToString()!" );
2923 }
2924 }
2925
2926 virtual void ConvertTypedValueToByteStream( const attrib_value_t& typedValue, ::std::string *out_psBytes ) const OVERRIDE
2927 {
2928 Assert( out_psBytes );
2929 Assert( out_psBytes->size() == 0 );
2930
2931 out_psBytes->resize( sizeof( attrib_value_t ) );
2932 *reinterpret_cast<attrib_value_t *>( &((*out_psBytes)[0]) ) = typedValue; // overwrite string contents (sizeof( attrib_value_t ) bytes)
2933 }
2934
2935 virtual void ConvertByteStreamToTypedValue( const ::std::string& sBytes, attrib_value_t *out_pTypedValue ) const OVERRIDE
2936 {
2937 Assert( out_pTypedValue );
2938 // Game clients and servers may have partially out-of-date information, or may have downloaded a new schema
2939 // but not know how to parse an attribute of a certain type, etc. In these cases, because we know we
2940 // aren't on the GC, temporarily failing to load these values until the client shuts down and updates
2941 // is about the best we can hope for.
2942 if ( sBytes.size() < sizeof( attrib_value_t ) )
2943 {
2944 *out_pTypedValue = attrib_value_t();
2945 return;
2946 }
2947
2948 *out_pTypedValue = *reinterpret_cast<const attrib_value_t *>( &sBytes[0] );
2949 }
2950
2951 virtual bool BSupportsGameplayModificationAndNetworking() const OVERRIDE
2952 {
2953 return true;
2954 }
2955
2956private:
2957};
2958
2959//-----------------------------------------------------------------------------
2960// Purpose: Constructor
2961//-----------------------------------------------------------------------------
2962CEconItemAttributeDefinition::CEconItemAttributeDefinition( void )
2963: m_pKVAttribute( NULL ),
2964 m_pAttrType( NULL ),
2965 m_bHidden( false ),
2966 m_bWebSchemaOutputForced( false ),
2967 m_bStoredAsInteger( false ),
2968 m_iEffectType( ATTRIB_EFFECT_NEUTRAL ),
2969 m_iDescriptionFormat( 0 ),
2970 m_pszDescriptionString( NULL ),
2971 m_pszDescriptionTag( NULL ),
2972 m_pszArmoryDesc( NULL ),
2973 m_iScore( 0 ),
2974 m_pszDefinitionName( NULL ),
2975 m_pszAttributeClass( NULL )
2976#ifndef GC_DLL
2977 , m_iszAttributeClass( NULL_STRING )
2978#endif
2979{
2980}
2981
2982
2983//-----------------------------------------------------------------------------
2984// Purpose: Copy constructor
2985//-----------------------------------------------------------------------------
2986CEconItemAttributeDefinition::CEconItemAttributeDefinition( const CEconItemAttributeDefinition &that )
2987{
2988 (*this) = that;
2989}
2990
2991
2992//-----------------------------------------------------------------------------
2993// Purpose: Operator=
2994//-----------------------------------------------------------------------------
2995CEconItemAttributeDefinition &CEconItemAttributeDefinition::operator=( const CEconItemAttributeDefinition &rhs )
2996{
2997 m_nDefIndex = rhs.m_nDefIndex;
2998 m_pAttrType = rhs.m_pAttrType;
2999 m_bHidden = rhs.m_bHidden;
3000 m_bWebSchemaOutputForced = rhs.m_bWebSchemaOutputForced;
3001 m_bStoredAsInteger = rhs.m_bStoredAsInteger;
3002 m_bInstanceData = rhs.m_bInstanceData;
3003 m_eAssetClassAttrExportRule = rhs.m_eAssetClassAttrExportRule;
3004 m_iEffectType = rhs.m_iEffectType;
3005 m_iDescriptionFormat = rhs.m_iDescriptionFormat;
3006 m_pszDescriptionString = rhs.m_pszDescriptionString;
3007 m_pszDescriptionTag = rhs.m_pszDescriptionTag;
3008 m_pszArmoryDesc = rhs.m_pszArmoryDesc;
3009 m_iScore = rhs.m_iScore;
3010 m_pszDefinitionName = rhs.m_pszDefinitionName;
3011 m_pszAttributeClass = rhs.m_pszAttributeClass;
3012 m_unAssetClassBucket = rhs.m_unAssetClassBucket;
3013#ifndef GC_DLL
3014 m_iszAttributeClass = rhs.m_iszAttributeClass;
3015#endif
3016
3017 m_pKVAttribute = NULL;
3018 if ( NULL != rhs.m_pKVAttribute )
3019 {
3020 m_pKVAttribute = rhs.m_pKVAttribute->MakeCopy();
3021
3022 // Re-assign string pointers
3023 m_pszDefinitionName = m_pKVAttribute->GetString("name");
3024 m_pszDescriptionString = m_pKVAttribute->GetString( "description_string", NULL );
3025 m_pszDescriptionTag = m_pKVAttribute->GetString( "description_tag", NULL );
3026
3027 m_pszArmoryDesc = m_pKVAttribute->GetString( "armory_desc", NULL );
3028 m_pszAttributeClass = m_pKVAttribute->GetString( "attribute_class", NULL );
3029
3030 Assert( V_strcmp( m_pszDefinitionName, rhs.m_pszDefinitionName ) == 0 );
3031 Assert( V_strcmp( m_pszDescriptionString, rhs.m_pszDescriptionString ) == 0 );
3032 Assert( V_strcmp( m_pszDescriptionTag, rhs.m_pszDescriptionTag ) == 0 );
3033 Assert( V_strcmp( m_pszArmoryDesc, rhs.m_pszArmoryDesc ) == 0 );
3034 Assert( V_strcmp( m_pszAttributeClass, rhs.m_pszAttributeClass ) == 0 );
3035 }
3036 else
3037 {
3038 Assert( m_pszDefinitionName == NULL );
3039 Assert( m_pszDescriptionString == NULL );
3040 Assert( m_pszArmoryDesc == NULL );
3041 Assert( m_pszAttributeClass == NULL );
3042 }
3043 return *this;
3044}
3045
3046
3047//-----------------------------------------------------------------------------
3048// Purpose: Destructor
3049//-----------------------------------------------------------------------------
3050CEconItemAttributeDefinition::~CEconItemAttributeDefinition( void )
3051{
3052 if ( m_pKVAttribute )
3053 m_pKVAttribute->deleteThis();
3054 m_pKVAttribute = NULL;
3055}
3056
3057
3058//-----------------------------------------------------------------------------
3059// Purpose: Initialize the attribute definition
3060// Input: pKVAttribute - The KeyValues representation of the attribute
3061// schema - The overall item schema for this attribute
3062// pVecErrors - An optional vector that will contain error messages if
3063// the init fails.
3064// Output: True if initialization succeeded, false otherwise
3065//-----------------------------------------------------------------------------
3066bool CEconItemAttributeDefinition::BInitFromKV( KeyValues *pKVAttribute, CEconItemSchema &pschema, CUtlVector<CUtlString> *pVecErrors /* = NULL */ )
3067{
3068 m_pKVAttribute = pKVAttribute->MakeCopy();
3069 m_nDefIndex = Q_atoi( m_pKVAttribute->GetName() );
3070
3071 m_pszDefinitionName = m_pKVAttribute->GetString("name");
3072 m_bHidden = m_pKVAttribute->GetInt( "hidden", 0 ) != 0;
3073 m_bWebSchemaOutputForced = m_pKVAttribute->GetInt( "force_output_description", 0 ) != 0;
3074 m_bStoredAsInteger = m_pKVAttribute->GetInt( "stored_as_integer", 0 ) != 0;
3075 m_iScore = m_pKVAttribute->GetInt( "score", 0 );
3076 m_iEffectType = (attrib_effect_types_t)StringFieldToInt( m_pKVAttribute->GetString("effect_type"), g_EffectTypes, ARRAYSIZE(g_EffectTypes) );
3077 m_iDescriptionFormat = StringFieldToInt( m_pKVAttribute->GetString("description_format"), g_AttributeDescriptionFormats, ARRAYSIZE(g_AttributeDescriptionFormats) );
3078 m_pszDescriptionString = m_pKVAttribute->GetString( "description_string", NULL );
3079 m_pszDescriptionTag = m_pKVAttribute->GetString( "description_tag", NULL );
3080 m_pszArmoryDesc = m_pKVAttribute->GetString( "armory_desc", NULL );
3081 m_pszAttributeClass = m_pKVAttribute->GetString( "attribute_class", NULL );
3082 m_bInstanceData = pKVAttribute->GetBool( "instance_data", false );
3083
3084 const char *pszAttrType = m_pKVAttribute->GetString( "attribute_type", NULL ); // NULL implies "default type" for backwards compatibility
3085 m_pAttrType = pschema.GetAttributeType( pszAttrType );
3086 SCHEMA_INIT_CHECK(
3087 NULL != m_pAttrType,
3088 CFmtStr( "Attribute definition %s: Unable to find attribute data type '%s'", m_pszDefinitionName, pszAttrType ? pszAttrType : "(default)" ) );
3089
3090#if defined(CLIENT_DLL) || defined(GAME_DLL)
3091 m_iszAttributeClass = NULL_STRING;
3092#endif
3093
3094 // Check for required fields
3095 SCHEMA_INIT_CHECK(
3096 NULL != m_pKVAttribute->FindKey( "name" ),
3097 CFmtStr( "Attribute definition %s: Missing required field \"name\"", m_pKVAttribute->GetName() ) );
3098
3099 m_unAssetClassBucket = pKVAttribute->GetInt( "asset_class_bucket", 0 );
3100 m_eAssetClassAttrExportRule = k_EAssetClassAttrExportRule_Default;
3101 if ( char const *szRule = pKVAttribute->GetString( "asset_class_export", NULL ) )
3102 {
3103 if ( !V_stricmp( szRule, "skip" ) )
3104 {
3105 m_eAssetClassAttrExportRule = k_EAssetClassAttrExportRule_Skip;
3106 }
3107 else if ( !V_stricmp( szRule, "gconly" ) )
3108 {
3109 m_eAssetClassAttrExportRule = EAssetClassAttrExportRule_t( k_EAssetClassAttrExportRule_GCOnly | k_EAssetClassAttrExportRule_Skip );
3110 }
3111 else if ( !V_stricmp( szRule, "bucketed" ) )
3112 {
3113 SCHEMA_INIT_CHECK(
3114 m_unAssetClassBucket,
3115 CFmtStr( "Attribute definition %s: Asset class export rule '%s' is incompatible", m_pszDefinitionName, szRule ) );
3116 m_eAssetClassAttrExportRule = k_EAssetClassAttrExportRule_Bucketed;
3117 }
3118 else if ( !V_stricmp( szRule, "default" ) )
3119 {
3120 m_eAssetClassAttrExportRule = k_EAssetClassAttrExportRule_Default;
3121 }
3122 else
3123 {
3124 SCHEMA_INIT_CHECK(
3125 false,
3126 CFmtStr( "Attribute definition %s: Invalid asset class export rule '%s'", m_pszDefinitionName, szRule ) );
3127 }
3128 }
3129
3130 // Check for misuse of asset class bucket
3131 SCHEMA_INIT_CHECK(
3132 ( !m_unAssetClassBucket || m_bInstanceData ),
3133 CFmtStr( "Attribute definition %s: Cannot use \"asset_class_bucket\" on class-level attributes", m_pKVAttribute->GetName() )
3134 );
3135
3136 return SCHEMA_INIT_SUCCESS();
3137}
3138
3139//-----------------------------------------------------------------------------
3140// Purpose: Constructor
3141//-----------------------------------------------------------------------------
3142CEconSoundMaterialDefinition::CEconSoundMaterialDefinition( void )
3143 : m_nID( INT_MAX )
3144{
3145}
3146
3147
3148//-----------------------------------------------------------------------------
3149// Purpose: Copy constructor
3150//-----------------------------------------------------------------------------
3151CEconSoundMaterialDefinition::CEconSoundMaterialDefinition( const CEconSoundMaterialDefinition &that )
3152{
3153 (*this) = that;
3154}
3155
3156
3157//-----------------------------------------------------------------------------
3158// Purpose: Operator=
3159//-----------------------------------------------------------------------------
3160CEconSoundMaterialDefinition &CEconSoundMaterialDefinition::operator=( const CEconSoundMaterialDefinition &rhs )
3161{
3162 m_nID = rhs.m_nID;
3163 m_strName = rhs.m_strName;
3164 m_strStartDragSound = rhs.m_strStartDragSound;
3165 m_strEndDragSound = rhs.m_strEndDragSound;
3166 m_strEquipSound = rhs.m_strEquipSound;
3167
3168 return *this;
3169}
3170
3171
3172//-----------------------------------------------------------------------------
3173// Purpose: Initialize the rarity definition
3174//-----------------------------------------------------------------------------
3175bool CEconSoundMaterialDefinition::BInitFromKV( KeyValues *pKVSoundMaterial, CEconItemSchema &pschema, CUtlVector<CUtlString> *pVecErrors /* = NULL */ )
3176{
3177 m_nID = pKVSoundMaterial->GetInt( "value", -1 );
3178 m_strName = pKVSoundMaterial->GetName();
3179 m_strStartDragSound = pKVSoundMaterial->GetString( "start_drag_sound" );
3180 m_strEndDragSound = pKVSoundMaterial->GetString( "end_drag_sound" );
3181 m_strEquipSound = pKVSoundMaterial->GetString( "equip_sound" );
3182
3183 // Check for required fields
3184 SCHEMA_INIT_CHECK(
3185 NULL != pKVSoundMaterial->FindKey( "value" ),
3186 CFmtStr( "Sound material definition %s: Missing required field \"value\"", pKVSoundMaterial->GetName() ) );
3187
3188#if defined(CLIENT_DLL) || defined(GAME_DLL)
3189 return SCHEMA_INIT_SUCCESS();
3190#endif // GC_DLL
3191
3192 return SCHEMA_INIT_SUCCESS();
3193}
3194
3195//-----------------------------------------------------------------------------
3196// Purpose: Constructor
3197//-----------------------------------------------------------------------------
3198CEconItemDefinition::CEconItemDefinition( void )
3199: m_pKVItem( NULL ),
3200m_bEnabled( false ),
3201m_unMinItemLevel( 0 ),
3202m_unMaxItemLevel( 100 ),
3203m_iArmoryRemap( 0 ),
3204m_iStoreRemap( 0 ),
3205m_nItemRarity( k_unItemRarity_Any ),
3206m_nItemQuality( k_unItemQuality_Any ),
3207m_nForcedItemQuality( k_unItemQuality_Any ),
3208m_nDefaultDropItemQuality( k_unItemQuality_Any ),
3209m_nDefaultDropQuantity( 1 ),
3210m_pProxyCriteria( NULL ),
3211m_bLoadOnDemand( false ),
3212m_pTool( NULL ),
3213m_rtExpiration( 0 ),
3214m_rtDefCreation( 0 ),
3215m_BundleInfo( NULL ),
3216m_unNumConcreteItems( 0 ),
3217m_nSoundMaterialID( 0 ),
3218m_nPopularitySeed( 0 ),
3219m_pszDefinitionName( NULL ),
3220m_pszItemClassname( NULL ),
3221m_pszClassToken( NULL ),
3222m_pszSlotToken( NULL ),
3223m_pszItemBaseName( NULL ),
3224m_pszItemTypeName( NULL ),
3225m_unItemTypeID( 0 ),
3226m_pszItemDesc( NULL ),
3227m_pszArmoryDesc( NULL ),
3228m_pszInventoryModel( NULL ),
3229m_pszInventoryImage( NULL ),
3230m_pszHolidayRestriction( NULL ),
3231m_iSubType( 0 ),
3232m_pszBaseDisplayModel( NULL ),
3233m_pszWorldDisplayModel( NULL ),
3234m_pszWorldDroppedModel( NULL ),
3235m_pszWorldExtraWearableModel( NULL ),
3236m_pszIconDefaultImage( NULL ),
3237m_pszParticleFile( NULL ),
3238m_pszParticleSnapshotFile( NULL ),
3239m_pInventoryImageData( NULL ),
3240m_pszBrassModelOverride( NULL ),
3241m_bHideBodyGroupsDeployedOnly( false ),
3242m_bAttachToHands( false ),
3243m_bAttachToHandsVMOnly( false ),
3244m_bProperName( false ),
3245m_bFlipViewModel( false ),
3246m_bActAsWearable( false ),
3247m_iDropType( 1 ),
3248m_bHidden( false ),
3249m_bShouldShowInArmory( false ),
3250m_bIsPackBundle( false ),
3251m_pOwningPackBundle( NULL ),
3252m_bBaseItem( false ),
3253m_pszItemLogClassname( NULL ),
3254m_pszItemIconClassname( NULL ),
3255#if ECONITEM_DATABASE_AUDIT_TABLES_FEATURE
3256m_pszDatabaseAuditTable( NULL ),
3257#endif
3258m_bImported( false ),
3259m_bOnePerAccountCDKEY( false ),
3260m_pszArmoryRemap( NULL ),
3261m_pszStoreRemap( NULL ),
3262m_bPublicItem( false ),
3263m_bIgnoreInCollectionView( false ),
3264m_nAssociatedItemsDefIndexes( NULL ),
3265m_pPortraitsKV( NULL ),
3266m_pAssetInfo( NULL ),
3267m_eItemType( k_EItemTypeNone ),
3268m_bAllowPurchaseStandalone( false )
3269{
3270 m_pMapAlternateIcons = new CUtlMap<uint32, const char*>;
3271 m_pMapAlternateIcons->SetLessFunc( DefLessFunc(uint32) );
3272}
3273
3274//-----------------------------------------------------------------------------
3275// Purpose: Destructor
3276//-----------------------------------------------------------------------------
3277CEconItemDefinition::~CEconItemDefinition( void )
3278{
3279 if ( m_pKVItem )
3280 m_pKVItem->deleteThis();
3281 m_pKVItem = NULL;
3282 delete m_pProxyCriteria;
3283 delete m_pTool;
3284 delete m_BundleInfo;
3285 delete m_pMapAlternateIcons;
3286
3287 if ( m_pInventoryImageData )
3288 {
3289 for ( int i = 0; i < MATERIAL_MAX_LIGHT_COUNT; i++ )
3290 {
3291 delete m_pInventoryImageData->m_pLightDesc[ i ];
3292 }
3293 delete m_pInventoryImageData->m_pCameraOffset;
3294 delete m_pInventoryImageData->m_pCameraAngles;
3295 delete m_pInventoryImageData;
3296 }
3297
3298 PurgeStaticAttributes();
3299}
3300
3301void CEconItemDefinition::PurgeStaticAttributes()
3302{
3303 FOR_EACH_VEC( m_vecStaticAttributes, i )
3304 {
3305 static_attrib_t *pAttrib = &(m_vecStaticAttributes[ i ]);
3306
3307 const CEconItemAttributeDefinition *pAttrDef = pAttrib->GetAttributeDefinition();
3308 Assert( pAttrDef );
3309
3310 const ISchemaAttributeType *pAttrType = pAttrDef->GetAttributeType();
3311 Assert( pAttrType );
3312
3313 // Free up our temp loading memory.
3314 pAttrType->UnloadEconAttributeValue( &pAttrib->m_value );
3315 }
3316
3317 m_vecStaticAttributes.Purge();
3318}
3319
3320#if defined(CLIENT_DLL) || defined(GAME_DLL)
3321//-----------------------------------------------------------------------------
3322// Purpose: Stomp our base data with extra testing data specified by the player
3323//-----------------------------------------------------------------------------
3324bool CEconItemDefinition::BInitFromTestItemKVs( int iNewDefIndex, KeyValues *pKVItem, CEconItemSchema &pschema )
3325{
3326 // The KeyValues are stored in the player entity, so we can cache our name there
3327
3328 m_nDefIndex = iNewDefIndex;
3329
3330 bool bTestingExistingItem = pKVItem->GetInt( "test_existing_item", 0 ) != 0;
3331 if ( !bTestingExistingItem )
3332 {
3333 m_pszDefinitionName = pKVItem->GetString( "name", NULL );
3334 m_pszItemBaseName = pKVItem->GetString( "name", NULL );
3335
3336#ifdef CLIENT_DLL
3337 pKVItem->SetString( "name", VarArgs("Test Item %d", iNewDefIndex) );
3338#else
3339 pKVItem->SetString( "name", UTIL_VarArgs("Test Item %d", iNewDefIndex) );
3340#endif
3341
3342 m_pszBaseDisplayModel = pKVItem->GetString( "model_player", NULL );
3343 m_bAttachToHands = pKVItem->GetInt( "attach_to_hands", 0 ) != 0;
3344
3345 BInitVisualBlockFromKV( pKVItem, pschema );
3346
3347 m_pszParticleFile = pKVItem->GetString( "particle_file", NULL );
3348 m_pszParticleSnapshotFile = pKVItem->GetString( "particle_snapshot", NULL );
3349 }
3350
3351 // Handle attributes
3352 PurgeStaticAttributes();
3353
3354 int iPaintCanIndex = pKVItem->GetInt("paintcan_index", 0);
3355 if ( iPaintCanIndex )
3356 {
3357 static CSchemaAttributeDefHandle pAttrDef_PaintRGB( "set item tint RGB" );
3358
3359 const CEconItemDefinition *pCanDef = pschema.GetItemDefinition(iPaintCanIndex);
3360
3361 float flRGBVal;
3362 if ( pCanDef && pAttrDef_PaintRGB && FindAttribute_UnsafeBitwiseCast<attrib_value_t>( pCanDef, pAttrDef_PaintRGB, &flRGBVal ) )
3363 {
3364 static_attrib_t& StaticAttrib = m_vecStaticAttributes[ m_vecStaticAttributes.AddToTail() ];
3365
3366 StaticAttrib.iDefIndex = pAttrDef_PaintRGB->GetDefinitionIndex();
3367 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
3368 }
3369 }
3370
3371 return true;
3372}
3373#endif
3374
3375void AssetInfo::AddAssetModifier( AssetModifier* newMod )
3376{
3377 int idx = m_mapAssetModifiers.Find( newMod->m_Type );
3378 if ( !m_mapAssetModifiers.IsValidIndex( idx ) )
3379 {
3380 idx = m_mapAssetModifiers.Insert( newMod->m_Type, new CUtlVector<AssetModifier*> );
3381 }
3382 m_mapAssetModifiers[idx]->AddToTail( newMod );
3383}
3384
3385CUtlVector<AssetModifier*>* AssetInfo::GetAssetModifiers( EAssetModifier type )
3386{
3387 int idx = m_mapAssetModifiers.Find( type );
3388 if ( m_mapAssetModifiers.IsValidIndex( idx ) )
3389 {
3390 return m_mapAssetModifiers[idx];
3391 }
3392 else
3393 {
3394 return NULL;
3395 }
3396}
3397
3398const char* AssetInfo::GetModifierByAsset( EAssetModifier type, const char* pszAsset, int iStyle )
3399{
3400 CUtlVector<AssetModifier*>* pAssetModifierList = GetAssetModifiers( type );
3401 if ( pAssetModifierList )
3402 {
3403 if ( iStyle > -1 )
3404 {
3405 // See if a modifier matches this style.
3406 FOR_EACH_VEC( *pAssetModifierList, i )
3407 {
3408 AssetModifier* pMod = pAssetModifierList->Element(i);
3409 if ( pMod->m_strAsset == pszAsset && pMod->m_iStyle == iStyle )
3410 return pMod->m_strModifier;
3411 }
3412 }
3413
3414 // Return the first match.
3415 FOR_EACH_VEC( *pAssetModifierList, i )
3416 {
3417 AssetModifier* pMod = pAssetModifierList->Element(i);
3418 if ( pMod->m_strAsset == pszAsset )
3419 return pMod->m_strModifier;
3420 }
3421 }
3422
3423 return NULL;
3424}
3425
3426const char* AssetInfo::GetAssetByModifier( EAssetModifier type, const char* pszModifier, int iStyle )
3427{
3428 CUtlVector<AssetModifier*>* pAssetModifierList = GetAssetModifiers( type );
3429 if ( pAssetModifierList )
3430 {
3431 if ( iStyle > -1 )
3432 {
3433 // See if a modifier matches this style.
3434 FOR_EACH_VEC( *pAssetModifierList, i )
3435 {
3436 AssetModifier* pMod = pAssetModifierList->Element(i);
3437 if ( pMod->m_strModifier == pszModifier && pMod->m_iStyle == iStyle )
3438 return pMod->m_strAsset;
3439 }
3440 }
3441
3442 // Return the first match.
3443 FOR_EACH_VEC( *pAssetModifierList, i )
3444 {
3445 AssetModifier* pMod = pAssetModifierList->Element(i);
3446 if ( pMod->m_strModifier == pszModifier )
3447 return pMod->m_strAsset;
3448 }
3449 }
3450
3451 return NULL;
3452}
3453
3454//-----------------------------------------------------------------------------
3455// Purpose: Handle parsing the per-team visual block from the keyvalues
3456//-----------------------------------------------------------------------------
3457void CEconItemDefinition::BInitVisualBlockFromKV( KeyValues *pKVItem, IEconItemSchema &pschema, CUtlVector<CUtlString> *pVecErrors )
3458{
3459 // Visuals
3460 m_pAssetInfo = NULL;
3461 KeyValues *pVisualsKV = pKVItem->FindKey( "visuals" );
3462 if ( pVisualsKV )
3463 {
3464 AssetInfo *pVisData = new AssetInfo();
3465
3466#if defined(CLIENT_DLL) || defined(GAME_DLL)
3467 KeyValues *pKVEntry = pVisualsKV->GetFirstSubKey();
3468 while ( pKVEntry )
3469 {
3470 const char *pszEntry = pKVEntry->GetName();
3471
3472 if ( !Q_stricmp( pszEntry, "attached_models" ) )
3473 {
3474 FOR_EACH_SUBKEY( pKVEntry, pKVAttachedModelData )
3475 {
3476 int iAtt = pVisData->m_AttachedModels.AddToTail();
3477 pVisData->m_AttachedModels[iAtt].m_iModelDisplayFlags = pKVAttachedModelData->GetInt( "model_display_flags", kAttachedModelDisplayFlag_MaskAll );
3478 pVisData->m_AttachedModels[iAtt].m_pszModelName = pKVAttachedModelData->GetString( "model", NULL );
3479 }
3480 }
3481 else if ( StringHasPrefix( pszEntry, "attached_particlesystem" ) )
3482 {
3483 // This replaces TF's "attached_particlesystem", "custom_particlesystem", and "custom_particlesystem2"
3484 // It now indexes into the attributecontrolledparticlesystems list (use the "custom_type" field if you want a custom attribute)
3485 int iParticleIndex;
3486 if ( pschema.FindAttributeControlledParticleSystem( pKVEntry->GetString( "system", NULL ), &iParticleIndex ) )
3487 {
3488 attachedparticle_t attachedParticle;
3489 attachedParticle.m_iParticleIndex = iParticleIndex;
3490 attachedParticle.m_nStyle = pKVEntry->GetInt( "style", 0 );
3491
3492 pVisData->m_AttachedParticles.AddToTail( attachedParticle );
3493 }
3494 }
3495 else if ( StringHasPrefix( pszEntry, "asset_modifier" ) )
3496 {
3497 AssetModifier* newMod = new AssetModifier();
3498 newMod->m_Type = GetAssetModifierType( pKVEntry->GetString( "type", NULL ) );
3499 newMod->m_strModifier = pKVEntry->GetString( "modifier", NULL );
3500 newMod->m_flModifier = pKVEntry->GetFloat( "modifier", 0.f );
3501 newMod->m_strAsset = pKVEntry->GetString( "asset", NULL );
3502 newMod->m_iStyle = pKVEntry->GetInt( "style", 0 );
3503 pVisData->AddAssetModifier( newMod );
3504
3505 // Items can have any number of asset modifiers.
3506 // The variables can be used in any way, based on type, but the format is all the same to simplify the editor.
3507 const char* pszAssetModifierType = pKVEntry->GetString( "type", NULL );
3508
3509 const char* pszAssetName = pKVEntry->GetString( "asset", NULL );
3510 const char* pszModifierName = pKVEntry->GetString( "modifier", NULL );
3511 float flFrequency = pKVEntry->GetFloat( "frequency", 1.0 );
3512
3513 if ( FStrEq( pszAssetModifierType, "activity" ) )
3514 {
3515 int iAtt = pVisData->m_Animations.AddToTail();
3516 pVisData->m_Animations[iAtt].iActivity = -2;
3517 pVisData->m_Animations[iAtt].pszActivity = pszAssetName;
3518 pVisData->m_Animations[iAtt].iReplacement = kActivityLookup_Unknown;
3519 pVisData->m_Animations[iAtt].pszReplacement = pszModifierName;
3520 pVisData->m_Animations[iAtt].flFrequency = flFrequency;
3521 }
3522 else if ( FStrEq( pszAssetModifierType, "announcer" ) )
3523 {
3524 pVisData->m_pszAnnouncerName = pszAssetName;
3525 pVisData->m_pszAnnouncerResource = pszModifierName;
3526 }
3527 else if ( FStrEq( pszAssetModifierType, "announcer_preview" ) )
3528 {
3529 CUtlString strFilename = pszAssetName;
3530
3531 announcer_preview_t preview;
3532
3533 preview.m_strFileName = pszAssetName;
3534 preview.m_strCaption = pszModifierName;
3535
3536 pVisData->m_vecAnnouncerPreview.AddToTail( preview );
3537 }
3538 else if ( FStrEq( pszAssetModifierType, "hud_skin" ) )
3539 {
3540 pVisData->m_pszHudSkinName = pszAssetName;
3541 }
3542 else if ( FStrEq( pszAssetModifierType, "ability_name" ) )
3543 {
3544 pVisData->m_pszAbilityName = pszAssetName;
3545 }
3546 else if ( FStrEq( pszAssetModifierType, "sound" ) )
3547 {
3548 int iSound = pVisData->m_Sounds.AddToTail();
3549 pVisData->m_Sounds[iSound].pszSound = pszAssetName;
3550 pVisData->m_Sounds[iSound].pszReplacement = pszModifierName;
3551 }
3552 else if ( FStrEq( pszAssetModifierType, "speech" ) )
3553 {
3554 pVisData->m_pszSpeechConcept = pszAssetName;
3555 pVisData->m_pszChatMessage = pszModifierName;
3556 }
3557 else if ( FStrEq( pszAssetModifierType, "particle" ) )
3558 {
3559 int iParticle = pVisData->m_Particles.AddToTail();
3560 pVisData->m_Particles[iParticle].pszParticle = pszAssetName;
3561 pVisData->m_Particles[iParticle].pszReplacement = pszModifierName;
3562 }
3563 else if ( FStrEq( pszAssetModifierType, "particle_snapshot" ) )
3564 {
3565 int iParticle = pVisData->m_ParticleSnapshots.AddToTail();
3566 pVisData->m_ParticleSnapshots[iParticle].pszParticleSnapshot = pszAssetName;
3567 pVisData->m_ParticleSnapshots[iParticle].pszReplacement = pszModifierName;
3568 }
3569 else if ( FStrEq( pszAssetModifierType, "particle_control_point" ) )
3570 {
3571 int iParticleCP = pVisData->m_ParticleControlPoints.AddToTail();
3572 pVisData->m_ParticleControlPoints[iParticleCP].pszParticle = pszAssetName;
3573 pVisData->m_ParticleControlPoints[iParticleCP].nParticleControlPoint = pKVEntry->GetInt( "control_point_number", 0 );
3574 UTIL_StringToVector( pVisData->m_ParticleControlPoints[iParticleCP].vecCPValue.Base(), pKVEntry->GetString( "cp_position", "0 0 0" ) );
3575 }
3576 else if ( FStrEq( pszAssetModifierType, "entity_model" ) )
3577 {
3578 pVisData->m_pszEntityClass = pszAssetName;
3579 pVisData->m_pszEntityModel = pszModifierName;
3580 }
3581 else if ( FStrEq( pszAssetModifierType, "view_model" ) )
3582 {
3583 pVisData->m_pszEntityClass = pszAssetName;
3584 pVisData->m_pszViewModel = pszModifierName;
3585 }
3586 else if ( FStrEq( pszAssetModifierType, "icon_replacement" ) )
3587 {
3588 pVisData->m_pszOriginalIcon = pszAssetName;
3589 pVisData->m_pszNewIcon = pszModifierName;
3590 }
3591 else if ( FStrEq( pszAssetModifierType, "ability_icon_replacement" ) )
3592 {
3593 ability_icon_replacement_t iconReplacement;
3594
3595 iconReplacement.m_strAbilityName = pszAssetName;
3596 iconReplacement.m_strReplacement = pszModifierName;
3597
3598 pVisData->m_vecAbilityIconReplacements.AddToTail( iconReplacement );
3599 }
3600 else if ( FStrEq( pszAssetModifierType, "entity_scale" ) )
3601 {
3602 pVisData->m_pszScaleClass = pszAssetName;
3603 pVisData->m_flScaleSize = pKVEntry->GetFloat( "modifier", 1.f );
3604 }
3605 }
3606 else if ( !Q_stricmp( pszEntry, "animation" ) )
3607 {
3608 int iAtt = pVisData->m_Animations.AddToTail();
3609 pVisData->m_Animations[iAtt].iActivity = -2; // We can't look it up yet, the activity list hasn't been populated.
3610 pVisData->m_Animations[iAtt].pszActivity = pKVEntry->GetString( "activity", NULL );
3611
3612 const char* playback = pKVEntry->GetString( "playback" );
3613 if ( playback )
3614 {
3615 pVisData->m_Animations[iAtt].iPlayback = (wearableanimplayback_t)StringFieldToInt( pKVEntry->GetString("playback"), g_WearableAnimTypeStrings, ARRAYSIZE(g_WearableAnimTypeStrings) );
3616 }
3617
3618 pVisData->m_Animations[iAtt].iReplacement = kActivityLookup_Unknown;
3619 pVisData->m_Animations[iAtt].pszReplacement = pKVEntry->GetString( "replacement", NULL );
3620
3621 pVisData->m_Animations[iAtt].pszSequence = pKVEntry->GetString( "sequence", NULL );
3622 pVisData->m_Animations[iAtt].pszScene = pKVEntry->GetString( "scene", NULL );
3623 pVisData->m_Animations[iAtt].pszRequiredItem = pKVEntry->GetString( "required_item", NULL );
3624 pVisData->m_Animations[iAtt].flFrequency = pKVEntry->GetFloat( "frequency", 1.0 );
3625 }
3626 else if ( !Q_stricmp( pszEntry, "player_bodygroups" ) )
3627 {
3628 FOR_EACH_SUBKEY( pKVEntry, pKVBodygroupKey )
3629 {
3630 const char *pszBodygroupName = pKVBodygroupKey->GetName();
3631 int iValue = pKVBodygroupKey->GetInt();
3632
3633 // Track bodygroup information for this item in particular.
3634 pVisData->m_ModifiedBodyGroupNames.Insert( pszBodygroupName, iValue );
3635
3636 // Track global schema state.
3637 CEconItemSchema* pItemSchema = (CEconItemSchema*) &pschema;
3638 pItemSchema->AssignDefaultBodygroupState( pszBodygroupName, iValue );
3639 }
3640 }
3641 else if ( !Q_stricmp( pszEntry, "skin" ) )
3642 {
3643 pVisData->iSkin = pKVEntry->GetInt();
3644 }
3645 else if ( !Q_stricmp( pszEntry, "use_per_class_bodygroups" ) )
3646 {
3647 pVisData->bUsePerClassBodygroups = pKVEntry->GetBool();
3648 }
3649 else if ( !Q_stricmp( pszEntry, "muzzle_flash" ) )
3650 {
3651 pVisData->m_pszMuzzleFlash = pKVEntry->GetString();
3652 }
3653 else if ( !Q_stricmp( pszEntry, "tracer_effect" ) )
3654 {
3655 pVisData->m_pszTracerEffect = pKVEntry->GetString();
3656 }
3657 else if ( !Q_stricmp( pszEntry, "particle_effect" ) )
3658 {
3659 pVisData->m_pszParticleEffect = pKVEntry->GetString();
3660 }
3661 else if ( !Q_stricmp( pszEntry, "particle_snapshot" ) )
3662 {
3663 pVisData->m_pszParticleSnapshot = pKVEntry->GetString();
3664 }
3665 else if ( StringHasPrefix( pszEntry, "custom_sound" ) ) // TF2 style custom sounds.
3666 {
3667 int iIndex = 0;
3668 if ( pszEntry[12] )
3669 {
3670 iIndex = clamp( atoi( &pszEntry[12] ), 0, MAX_VISUALS_CUSTOM_SOUNDS-1 );
3671 }
3672 pVisData->m_pszCustomSounds[iIndex] = pKVEntry->GetString();
3673 }
3674 else if ( !Q_stricmp( pszEntry, "material_override" ) )
3675 {
3676 pVisData->m_pszMaterialOverride = pKVEntry->GetString();
3677 }
3678 else if ( StringHasPrefix( pszEntry, "sound_" ) ) // Orange box style weapon sounds.
3679 {
3680 int iIndex = GetWeaponSoundFromString( &pszEntry[6] );
3681 if ( iIndex != -1 )
3682 {
3683 pVisData->m_pszWeaponSoundReplacements[iIndex] = pKVEntry->GetString();
3684 }
3685 }
3686 else if ( !Q_stricmp( pszEntry, "code_controlled_bodygroup" ) )
3687 {
3688 const char *pBodyGroupName = pKVEntry->GetString( "bodygroup", NULL );
3689 const char *pFuncName = pKVEntry->GetString( "function", NULL );
3690 if ( pBodyGroupName && pFuncName )
3691 {
3692 codecontrolledbodygroupdata_t ccbgd = { pFuncName, NULL };
3693 pVisData->m_CodeControlledBodyGroupNames.Insert( pBodyGroupName, ccbgd );
3694 }
3695 }
3696 else if ( !Q_stricmp( pszEntry, "vm_bodygroup_override" ) )
3697 {
3698 pVisData->m_iViewModelBodyGroupOverride = pKVEntry->GetInt();
3699 }
3700 else if ( !Q_stricmp( pszEntry, "vm_bodygroup_state_override" ) )
3701 {
3702 pVisData->m_iViewModelBodyGroupStateOverride = pKVEntry->GetInt();
3703 }
3704 else if ( !Q_stricmp( pszEntry, "wm_bodygroup_override" ) )
3705 {
3706 pVisData->m_iWorldModelBodyGroupOverride = pKVEntry->GetInt();
3707 }
3708 else if ( !Q_stricmp( pszEntry, "wm_bodygroup_state_override" ) )
3709 {
3710 pVisData->m_iWorldModelBodyGroupStateOverride = pKVEntry->GetInt();
3711 }
3712 else if ( !Q_stricmp( pszEntry, "skip_model_combine" ) )
3713 {
3714 pVisData->m_bSkipModelCombine = pKVEntry->GetBool();
3715 }
3716 else if ( !Q_stricmp( pszEntry, "animation_modifiers" ) )
3717 {
3718 FOR_EACH_SUBKEY( pKVEntry, pKVAnimMod )
3719 {
3720 pVisData->m_vecAnimationModifiers.AddToTail( pKVAnimMod->GetName() );
3721 }
3722 }
3723#ifdef CSTRIKE15
3724 ////////////// CS:GO string attributes
3725 else if ( !Q_stricmp( pszEntry, "primary_ammo" ) )
3726 {
3727 pVisData->m_pszPrimaryAmmo = pKVEntry->GetString();
3728 }
3729 else if ( !Q_stricmp( pszEntry, "weapon_type" ) )
3730 {
3731 pVisData->m_pszWeaponTypeString = pKVEntry->GetString();
3732 }
3733 else if ( !Q_stricmp( pszEntry, "addon_location" ) )
3734 {
3735 pVisData->m_pszAddonLocation = pKVEntry->GetString();
3736 }
3737 else if ( !Q_stricmp( pszEntry, "eject_brass_effect" ) )
3738 {
3739 pVisData->m_pszEjectBrassEffect = pKVEntry->GetString();
3740 }
3741 else if ( !Q_stricmp( pszEntry, "tracer_effect" ) )
3742 {
3743 pVisData->m_pszTracerEffect = pKVEntry->GetString();
3744 }
3745 else if ( !Q_stricmp( pszEntry, "muzzle_flash_effect_1st_person" ) )
3746 {
3747 pVisData->m_pszMuzzleFlashEffect1stPerson = pKVEntry->GetString();
3748 }
3749 else if ( !Q_stricmp( pszEntry, "muzzle_flash_effect_1st_person_alt" ) )
3750 {
3751 pVisData->m_pszMuzzleFlashEffect1stPersonAlt = pKVEntry->GetString();
3752 }
3753 else if ( !Q_stricmp( pszEntry, "muzzle_flash_effect_3rd_person" ) )
3754 {
3755 pVisData->m_pszMuzzleFlashEffect3rdPerson = pKVEntry->GetString();
3756 }
3757 else if ( !Q_stricmp( pszEntry, "muzzle_flash_effect_3rd_person_alt" ) )
3758 {
3759 pVisData->m_pszMuzzleFlashEffect3rdPersonAlt = pKVEntry->GetString();
3760 }
3761 else if ( !Q_stricmp( pszEntry, "heat_effect" ) )
3762 {
3763 pVisData->m_pszHeatEffect = pKVEntry->GetString();
3764 }
3765 else if ( !Q_stricmp( pszEntry, "player_animation_extension" ) )
3766 {
3767 pVisData->m_pszPlayerAnimationExtension = pKVEntry->GetString();
3768 }
3769
3770#endif //#ifdef CSTRIKE15
3771
3772 pKVEntry = pKVEntry->GetNextKey();
3773 }
3774#endif // defined(CLIENT_DLL) || defined(GAME_DLL)
3775 KeyValues *pStylesDataKV = pVisualsKV->FindKey( "styles" );
3776 if ( pStylesDataKV )
3777 {
3778 BInitStylesBlockFromKV( pStylesDataKV, *(CEconItemSchema*)( &pschema ), pVisData, pVecErrors );
3779 }
3780
3781 KeyValues * pKVAlternateIcons = pVisualsKV->FindKey( "alternate_icons" );
3782 if ( NULL != pKVAlternateIcons )
3783 {
3784 BInitAlternateIconsFromKV( pKVAlternateIcons, *(CEconItemSchema*)( &pschema ), pVisData, pVecErrors );
3785 }
3786
3787
3788 m_pAssetInfo = pVisData;
3789 }
3790}
3791
3792#if defined(CLIENT_DLL) || defined(GAME_DLL)
3793//-----------------------------------------------------------------------------
3794// Purpose:
3795//-----------------------------------------------------------------------------
3796void CEconItemDefinition::GeneratePrecacheModelStrings( bool bDynamicLoad, CUtlVector<const char *> *out_pVecModelStrings ) const
3797{
3798 Assert( out_pVecModelStrings );
3799
3800 // Add base model.
3801 out_pVecModelStrings->AddToTail( GetBasePlayerDisplayModel() );
3802
3803 // Add styles.
3804 if ( GetNumStyles() )
3805 {
3806 for ( style_index_t i=0; i<GetNumStyles(); ++i )
3807 {
3808 const CEconStyleInfo *pStyle = GetStyleInfo( i );
3809 Assert( pStyle );
3810
3811 pStyle->GeneratePrecacheModelStringsForStyle( out_pVecModelStrings );
3812 }
3813 }
3814
3815 if ( m_pAssetInfo )
3816 {
3817 // Precache all the attached models.
3818 for ( int model = 0; model < m_pAssetInfo->m_AttachedModels.Count(); model++ )
3819 {
3820 out_pVecModelStrings->AddToTail( m_pAssetInfo->m_AttachedModels[model].m_pszModelName );
3821 }
3822
3823 // Precache any model overrides.
3824 if ( m_pAssetInfo->m_pszEntityModel )
3825 {
3826 out_pVecModelStrings->AddToTail( m_pAssetInfo->m_pszEntityModel );
3827 }
3828
3829 if ( m_pAssetInfo->m_pszViewModel )
3830 {
3831 out_pVecModelStrings->AddToTail( m_pAssetInfo->m_pszViewModel );
3832 }
3833
3834 // Precache hero model change.
3835 CUtlVector<AssetModifier*>* pHeroModelChanges = m_pAssetInfo->GetAssetModifiers( AM_HeroModelChange );
3836 if ( pHeroModelChanges )
3837 {
3838 FOR_EACH_VEC( *pHeroModelChanges, i )
3839 {
3840 AssetModifier* pAssetMod = pHeroModelChanges->Element( i );
3841 if ( pAssetMod )
3842 {
3843 out_pVecModelStrings->AddToTail( pAssetMod->m_strModifier.Get() );
3844 }
3845 }
3846 }
3847
3848 // Precache couriers.
3849 pHeroModelChanges = m_pAssetInfo->GetAssetModifiers( AM_Courier );
3850 if ( pHeroModelChanges )
3851 {
3852 FOR_EACH_VEC( *pHeroModelChanges, i )
3853 {
3854 AssetModifier* pAssetMod = pHeroModelChanges->Element( i );
3855 if ( pAssetMod )
3856 {
3857 out_pVecModelStrings->AddToTail( pAssetMod->m_strModifier.Get() );
3858 }
3859 }
3860 }
3861
3862 // Precache flying couriers.
3863 pHeroModelChanges = m_pAssetInfo->GetAssetModifiers( AM_CourierFlying );
3864 if ( pHeroModelChanges )
3865 {
3866 FOR_EACH_VEC( *pHeroModelChanges, i )
3867 {
3868 AssetModifier* pAssetMod = pHeroModelChanges->Element( i );
3869 if ( pAssetMod )
3870 {
3871 out_pVecModelStrings->AddToTail( pAssetMod->m_strModifier.Get() );
3872 }
3873 }
3874 }
3875 }
3876
3877#ifdef DOTA_DLL
3878 // We don't need to cache the inventory model, because it's never loaded by the game.
3879#else
3880 if ( GetIconDisplayModel() )
3881 {
3882 out_pVecModelStrings->AddToTail( GetIconDisplayModel() );
3883 }
3884#endif
3885 if ( GetBuyMenuDisplayModel() )
3886 {
3887 out_pVecModelStrings->AddToTail( GetBuyMenuDisplayModel() );
3888 }
3889
3890 if ( GetWorldDroppedModel() )
3891 {
3892 out_pVecModelStrings->AddToTail( GetWorldDroppedModel() );
3893 }
3894
3895 if ( GetMagazineModel() )
3896 {
3897 out_pVecModelStrings->AddToTail( GetMagazineModel() );
3898 }
3899
3900 if ( GetScopeLensMaskModel() )
3901 {
3902 out_pVecModelStrings->AddToTail( GetScopeLensMaskModel() );
3903 }
3904
3905 const KillEaterScoreMap_t& mapKillEaterScoreTypes = GetItemSchema()->GetKillEaterScoreTypes();
3906 FOR_EACH_MAP( mapKillEaterScoreTypes, i )
3907 {
3908 const char *pchStatTrakModel = GetStatTrakModelByType( mapKillEaterScoreTypes[ i ].m_nValue );
3909 if ( pchStatTrakModel )
3910 {
3911 out_pVecModelStrings->AddToTail( pchStatTrakModel );
3912 }
3913 }
3914
3915 if ( GetUidModel() )
3916 {
3917 out_pVecModelStrings->AddToTail( GetUidModel() );
3918 }
3919
3920 for ( int i=0; i<GetNumSupportedStickerSlots(); ++i )
3921 {
3922 if ( GetStickerSlotModelBySlotIndex(i) )
3923 {
3924 out_pVecModelStrings->AddToTail( GetStickerSlotModelBySlotIndex(i) );
3925 }
3926 }
3927
3928}
3929
3930//-----------------------------------------------------------------------------
3931// Purpose:
3932//-----------------------------------------------------------------------------
3933void CEconItemDefinition::GeneratePrecacheSoundStrings( CUtlVector<const char*> *out_pVecSoundStrings ) const
3934{
3935 Assert( out_pVecSoundStrings );
3936
3937 // Precache all replacement sounds.
3938 if ( m_pAssetInfo )
3939 {
3940 for ( int sound=0; sound<m_pAssetInfo->m_Sounds.Count(); sound++ )
3941 {
3942 out_pVecSoundStrings->AddToTail( m_pAssetInfo->m_Sounds[sound].pszReplacement );
3943 }
3944 for ( int sound = 0; sound < ARRAYSIZE( m_pAssetInfo->m_pszWeaponSoundReplacements ); sound++ )
3945 {
3946 if ( !m_pAssetInfo->m_pszWeaponSoundReplacements[ sound ] )
3947 continue;
3948
3949 out_pVecSoundStrings->AddToTail( m_pAssetInfo->m_pszWeaponSoundReplacements[ sound ] );
3950 }
3951 }
3952}
3953
3954void CEconItemDefinition::GeneratePrecacheEffectStrings( CUtlVector<const char*> *out_pVecEffectStrings ) const
3955{
3956 Assert( out_pVecEffectStrings );
3957
3958 if ( !m_pAssetInfo )
3959 return;
3960
3961 if ( m_pAssetInfo->m_pszEjectBrassEffect )
3962 {
3963 out_pVecEffectStrings->AddToTail( m_pAssetInfo->m_pszEjectBrassEffect );
3964 }
3965 if ( m_pAssetInfo->m_pszTracerEffect )
3966 {
3967 out_pVecEffectStrings->AddToTail( m_pAssetInfo->m_pszTracerEffect );
3968 }
3969 if ( m_pAssetInfo->m_pszMuzzleFlashEffect1stPerson )
3970 {
3971 out_pVecEffectStrings->AddToTail( m_pAssetInfo->m_pszMuzzleFlashEffect1stPerson );
3972 }
3973 if ( m_pAssetInfo->m_pszMuzzleFlashEffect1stPersonAlt )
3974 {
3975 out_pVecEffectStrings->AddToTail( m_pAssetInfo->m_pszMuzzleFlashEffect1stPersonAlt );
3976 }
3977 if ( m_pAssetInfo->m_pszMuzzleFlashEffect3rdPerson )
3978 {
3979 out_pVecEffectStrings->AddToTail( m_pAssetInfo->m_pszMuzzleFlashEffect3rdPerson );
3980 }
3981 if ( m_pAssetInfo->m_pszMuzzleFlashEffect3rdPersonAlt )
3982 {
3983 out_pVecEffectStrings->AddToTail( m_pAssetInfo->m_pszMuzzleFlashEffect3rdPersonAlt );
3984 }
3985 if ( m_pAssetInfo->m_pszHeatEffect )
3986 {
3987 out_pVecEffectStrings->AddToTail( m_pAssetInfo->m_pszHeatEffect );
3988 }
3989}
3990
3991#endif // #if defined(CLIENT_DLL) || defined(GAME_DLL)
3992
3993//-----------------------------------------------------------------------------
3994// Purpose: Parse the alternate icons for this item.
3995//-----------------------------------------------------------------------------
3996bool CEconItemDefinition::BInitAlternateIconsFromKV( KeyValues *pKVAlternateIcons, CEconItemSchema &pschema, AssetInfo *pVisData, CUtlVector<CUtlString> *pVecErrors )
3997{
3998 m_pMapAlternateIcons->Purge();
3999
4000 FOR_EACH_TRUE_SUBKEY( pKVAlternateIcons, pKVAlternateIcon )
4001 {
4002 int iIconIndex = Q_atoi( pKVAlternateIcon->GetName() );
4003
4004 SCHEMA_INIT_CHECK(
4005 !m_pMapAlternateIcons->Find( iIconIndex ) != m_pMapAlternateIcons->InvalidIndex(),
4006 CFmtStr( "Duplicate alternate icon definition (%d)", iIconIndex ) );
4007
4008 SCHEMA_INIT_CHECK(
4009 iIconIndex >= 0,
4010 CFmtStr( "Alternate icon definition index %d must be greater than or equal to zero", iIconIndex ) );
4011
4012 m_pMapAlternateIcons->Insert( iIconIndex, pKVAlternateIcon->GetString( "icon_path" ) );
4013 }
4014
4015 return SCHEMA_INIT_SUCCESS();
4016}
4017
4018//-----------------------------------------------------------------------------
4019// Purpose: Parse the styles sub-section of the visuals block.
4020//-----------------------------------------------------------------------------
4021void CEconItemDefinition::BInitStylesBlockFromKV( KeyValues *pKVStyles, CEconItemSchema &pschema, AssetInfo *pVisData, CUtlVector<CUtlString> *pVecErrors )
4022{
4023 FOR_EACH_SUBKEY( pKVStyles, pKVStyle )
4024 {
4025 CEconStyleInfo *pStyleInfo = pschema.CreateEconStyleInfo();
4026 Assert( pStyleInfo );
4027
4028 pStyleInfo->BInitFromKV( pKVStyle, pschema, pVecErrors );
4029
4030 pVisData->m_Styles.AddToTail( pStyleInfo );
4031 }
4032}
4033
4034//-----------------------------------------------------------------------------
4035// Purpose: Parse one style from the styles block.
4036//-----------------------------------------------------------------------------
4037void CEconStyleInfo::BInitFromKV( KeyValues *pKVStyle, CEconItemSchema &pschema, CUtlVector<CUtlString> *pVecErrors )
4038{
4039 enum { kInvalidSkinKey = -1, };
4040
4041 Assert( pKVStyle );
4042
4043 m_iIndex = atoi( pKVStyle->GetName() );
4044
4045 // A "skin" entry means "use this index for all of our teams, no matter how many we have".
4046 int iCommonSkin = pKVStyle->GetInt( "skin", kInvalidSkinKey );
4047 if ( iCommonSkin != kInvalidSkinKey )
4048 {
4049 m_iSkin = iCommonSkin;
4050 }
4051
4052 // If we don't have a base entry, we look for a unique entry for each team. This will be
4053 // handled in a subclass if necessary.
4054
4055 // Are we hiding additional bodygroups when this style is active?
4056 KeyValues *pKVHideBodygroups = pKVStyle->FindKey( "additional_hidden_bodygroups" );
4057 if ( pKVHideBodygroups )
4058 {
4059 FOR_EACH_SUBKEY( pKVHideBodygroups, pKVBodygroup )
4060 {
4061 m_vecAdditionalHideBodygroups.AddToTail( pKVBodygroup->GetName() );
4062 }
4063 }
4064
4065 // Remaining common properties.
4066 m_pszName = pKVStyle->GetString( "name", "#TF_UnknownStyle" );
4067 m_pszBasePlayerModel = pKVStyle->GetString( "model_player", NULL );
4068
4069 // An alternate icon to use.
4070 m_iIcon = pKVStyle->GetInt( "alternate_icon", 0 );
4071
4072 // Unlock rules.
4073 KeyValues* pUnlockInfo = pKVStyle->FindKey( "unlock" );
4074 if ( pUnlockInfo )
4075 {
4076 m_UnlockInfo.iPrice = pUnlockInfo->GetInt( "price", 0 );
4077 m_UnlockInfo.pszItemName = pUnlockInfo->GetString( "item", NULL );
4078 m_UnlockInfo.iStylePreReq = pUnlockInfo->GetInt( "style", 0 );
4079 m_UnlockInfo.pszAttrib = pUnlockInfo->GetString( "attribute", NULL );
4080 m_UnlockInfo.iAttribValue = pUnlockInfo->GetInt( "attribute_value", 0 );
4081 }
4082}
4083
4084#if defined(CLIENT_DLL) || defined(GAME_DLL)
4085//-----------------------------------------------------------------------------
4086// Purpose:
4087//-----------------------------------------------------------------------------
4088void CEconStyleInfo::GeneratePrecacheModelStringsForStyle( CUtlVector<const char *> *out_pVecModelStrings ) const
4089{
4090 Assert( out_pVecModelStrings );
4091
4092 if ( GetBasePlayerDisplayModel() != NULL )
4093 {
4094 out_pVecModelStrings->AddToTail( GetBasePlayerDisplayModel() );
4095 }
4096}
4097#endif
4098
4099//-----------------------------------------------------------------------------
4100// Purpose: Item definition initialization helpers.
4101//-----------------------------------------------------------------------------
4102static void RecursiveInheritKeyValues( KeyValues *out_pValues, KeyValues *pInstance )
4103{
4104 FOR_EACH_SUBKEY( pInstance, pSubKey )
4105 {
4106 KeyValues::types_t eType = pSubKey->GetDataType();
4107 switch ( eType )
4108 {
4109 case KeyValues::TYPE_STRING: out_pValues->SetString( pSubKey->GetName(), pSubKey->GetString() ); break;
4110 case KeyValues::TYPE_INT: out_pValues->SetInt( pSubKey->GetName(), pSubKey->GetInt() ); break;
4111 case KeyValues::TYPE_FLOAT: out_pValues->SetFloat( pSubKey->GetName(), pSubKey->GetFloat() ); break;
4112 case KeyValues::TYPE_WSTRING: out_pValues->SetWString( pSubKey->GetName(), pSubKey->GetWString() ); break;
4113 case KeyValues::TYPE_COLOR: out_pValues->SetColor( pSubKey->GetName(), pSubKey->GetColor() ) ; break;
4114 case KeyValues::TYPE_UINT64: out_pValues->SetUint64( pSubKey->GetName(), pSubKey->GetUint64() ) ; break;
4115
4116 // "NONE" means "KeyValues"
4117 case KeyValues::TYPE_NONE:
4118 {
4119 // We may already have this part of the tree to stuff data into/overwrite, or we
4120 // may have to make a new block.
4121 KeyValues *pNewChild = out_pValues->FindKey( pSubKey->GetName() );
4122 if ( !pNewChild )
4123 {
4124 pNewChild = out_pValues->CreateNewKey();
4125 pNewChild->SetName( pSubKey->GetName() );
4126 }
4127
4128 RecursiveInheritKeyValues( pNewChild, pSubKey );
4129 break;
4130 }
4131
4132 case KeyValues::TYPE_PTR:
4133 default:
4134 Assert( !"Unhandled data type for KeyValues inheritance!" );
4135 break;
4136 }
4137 }
4138}
4139
4140#ifdef _DEBUG
4141#define DEBUG_SCHEMA_WRITE_TMP_FILE 0
4142#if DEBUG_SCHEMA_WRITE_TMP_FILE
4143#include <stdio.h>
4144class CKeyValuesDumpContextAsSchemaFile : public IKeyValuesDumpContextAsText
4145{
4146public:
4147 // Overrides developer level to dump in DevMsg, zero to dump as Msg
4148 CKeyValuesDumpContextAsSchemaFile() {}
4149
4150public:
4151 virtual bool KvWriteText( char const *szText )
4152 {
4153 FILE *f = NULL;
4154 fopen_s( &f, "c:/tmp/econ_item_schema.txt", "a+t" );
4155 if ( f )
4156 {
4157 fprintf( f, "%s", szText );
4158 fclose( f );
4159 }
4160 return true;
4161 }
4162}
4163g_schemadbg;
4164#endif
4165#endif
4166
4167// Always returns a newly allocated copy of KeyValues
4168// Applies prefabs supporting multiple inheritance from Right-to-Left (RTL)
4169// this way "prefab" "valve tournament p250" will start with "p250" then apply "tournament" prefab on top of that,
4170// then will apply "valve" prefab on top of that and then the values of the actual instance.
4171static KeyValues *InheritKeyValuesRTLMulti( KeyValues *pInstance, CEconItemSchema &pschema )
4172{
4173 Assert( pInstance );
4174 KeyValues *pFinalValues = pInstance->MakeCopy();
4175
4176#if DEBUG_SCHEMA_WRITE_TMP_FILE
4177 static int s_nDebugIndentLevel = 0;
4178#endif
4179
4180 CUtlVector< char * > arrPrefabs;
4181 if ( const char *svPrefabName = pFinalValues->GetString( "prefab", NULL ) )
4182 {
4183 Q_SplitString( svPrefabName, " ", arrPrefabs );
4184
4185#if DEBUG_SCHEMA_WRITE_TMP_FILE
4186 if ( arrPrefabs.Count() )
4187 {
4188 ++ s_nDebugIndentLevel;
4189
4190 g_schemadbg.KvWriteIndent( s_nDebugIndentLevel );
4191 g_schemadbg.KvWriteText( CFmtStr( "Expanding data with %d prefabs:--\n", arrPrefabs.Count() ) );
4192 pFinalValues->Dump( &g_schemadbg, s_nDebugIndentLevel );
4193 g_schemadbg.KvWriteIndent( s_nDebugIndentLevel );
4194 g_schemadbg.KvWriteText( "--\n" );
4195 }
4196#endif
4197 }
4198 FOR_EACH_VEC_BACK( arrPrefabs, i )
4199 {
4200 KeyValues *pKVPrefab = pschema.FindDefinitionPrefabByName( arrPrefabs[i] );
4201 if ( !pKVPrefab ) continue;
4202#if DEBUG_SCHEMA_WRITE_TMP_FILE
4203 {
4204 g_schemadbg.KvWriteIndent( s_nDebugIndentLevel );
4205 g_schemadbg.KvWriteText( CFmtStr( "Appending prefab '%s':\n", arrPrefabs[i] ) );
4206 pKVPrefab->Dump( &g_schemadbg, s_nDebugIndentLevel );
4207 }
4208#endif
4209 pKVPrefab = InheritKeyValuesRTLMulti( pKVPrefab, pschema );
4210 pKVPrefab->SetName( pFinalValues->GetName() );
4211
4212 RecursiveInheritKeyValues( pKVPrefab, pFinalValues );
4213 pFinalValues->deleteThis();
4214 pFinalValues = pKVPrefab;
4215
4216#if DEBUG_SCHEMA_WRITE_TMP_FILE
4217 {
4218 g_schemadbg.KvWriteIndent( s_nDebugIndentLevel );
4219 g_schemadbg.KvWriteText( CFmtStr( "After appending prefab '%s':\n", arrPrefabs[ i ] ) );
4220 pFinalValues->Dump( &g_schemadbg, s_nDebugIndentLevel );
4221 }
4222#endif
4223 }
4224
4225#if DEBUG_SCHEMA_WRITE_TMP_FILE
4226 if ( arrPrefabs.Count() )
4227 {
4228 g_schemadbg.KvWriteIndent( s_nDebugIndentLevel );
4229 g_schemadbg.KvWriteText( CFmtStr( "Finished expanding data with %d prefabs:--\n", arrPrefabs.Count() ) );
4230 pFinalValues->Dump( &g_schemadbg, s_nDebugIndentLevel );
4231 g_schemadbg.KvWriteIndent( s_nDebugIndentLevel );
4232 g_schemadbg.KvWriteText( "--\n" );
4233
4234 -- s_nDebugIndentLevel;
4235 }
4236#endif
4237
4238 arrPrefabs.PurgeAndDeleteElements();
4239
4240 return pFinalValues;
4241}
4242
4243KeyValues *CEconItemSchema::FindDefinitionPrefabByName( const char *pszPrefabName ) const
4244{
4245 int iIndex = m_mapDefinitionPrefabs.Find( pszPrefabName );
4246 if ( m_mapDefinitionPrefabs.IsValidIndex( iIndex ) )
4247 return m_mapDefinitionPrefabs[iIndex];
4248
4249 return NULL;
4250}
4251
4252//-----------------------------------------------------------------------------
4253// Purpose: Initialize the item definition
4254// Input: pKVItem - The KeyValues representation of the item
4255// schema - The overall item schema for this item
4256// pVecErrors - An optional vector that will contain error messages if
4257// the init fails.
4258// Output: True if initialization succeeded, false otherwise
4259//-----------------------------------------------------------------------------
4260bool CEconItemDefinition::BInitFromKV( KeyValues *pKVItem, CEconItemSchema &pschema, CUtlVector<CUtlString> *pVecErrors /* = NULL */ )
4261{
4262 // Set standard members
4263 m_pKVItem = InheritKeyValuesRTLMulti( pKVItem, pschema );
4264 m_bEnabled = m_pKVItem->GetBool( "enabled" );
4265 m_bLoadOnDemand = m_pKVItem->GetBool( "loadondemand" );
4266 m_nDefIndex = Q_atoi( m_pKVItem->GetName() );
4267 m_unMinItemLevel = (uint32)m_pKVItem->GetInt( "min_ilevel", pschema.GetMinLevel() );
4268 m_unMaxItemLevel = (uint32)m_pKVItem->GetInt( "max_ilevel", pschema.GetMaxLevel() );
4269 m_nDefaultDropQuantity = m_pKVItem->GetInt( "default_drop_quantity", 1 );
4270
4271 KeyValues *pKVMultiAssocItems = m_pKVItem->FindKey( "associated_items" ),
4272 *pKVSingleAssocItem = m_pKVItem->FindKey( "associated_item" );
4273
4274 // Maybe we have multiple entries?
4275 if ( pKVMultiAssocItems )
4276 {
4277 for ( KeyValues *pKVAssocItem = pKVMultiAssocItems->GetFirstSubKey(); pKVAssocItem; pKVAssocItem = pKVAssocItem->GetNextKey() )
4278 {
4279 m_nAssociatedItemsDefIndexes.AddToTail( atoi( pKVAssocItem->GetName() ) );
4280 }
4281 }
4282 // This is our one-and-only-one associated item
4283 else if ( pKVSingleAssocItem )
4284 {
4285 const char *pAssocItem = pKVSingleAssocItem->GetString( (const char *)NULL, NULL );
4286 if ( pAssocItem )
4287 {
4288 m_nAssociatedItemsDefIndexes.AddToTail( atoi( pAssocItem ) );
4289 }
4290 }
4291
4292 m_nSoundMaterialID = m_pKVItem->GetInt( "sound_material" );
4293
4294 m_nPopularitySeed = m_pKVItem->GetInt( "popularity_seed", 0 );
4295
4296 // initializing this one first so that it will be available for all the errors below
4297 m_pszDefinitionName = m_pKVItem->GetString( "name", NULL );
4298
4299 m_bDisableStyleSelection = m_pKVItem->GetBool( "disable_style_selector", false );
4300
4301#if defined(CLIENT_DLL) || defined(GAME_DLL)
4302 // We read this manually here in the game dlls. The GC reads it below while checking pschema.
4303 pschema.BGetItemQualityFromName( m_pKVItem->GetString( "item_quality" ), &m_nItemQuality );
4304 pschema.BGetItemQualityFromName( m_pKVItem->GetString( "forced_item_quality" ), &m_nForcedItemQuality );
4305 pschema.BGetItemQualityFromName( m_pKVItem->GetString( "default_drop_quality" ), &m_nDefaultDropItemQuality );
4306
4307 pschema.BGetItemRarityFromName( m_pKVItem->GetString( "item_rarity" ), &m_nItemRarity );
4308#endif
4309
4310 // Check for required fields
4311 SCHEMA_INIT_CHECK(
4312 NULL != m_pKVItem->FindKey( "name" ),
4313 CFmtStr( "Item definition %s: Missing required field \"name\"", m_pKVItem->GetName() ) );
4314
4315 SCHEMA_INIT_CHECK(
4316 NULL != m_pKVItem->FindKey( "item_class" ),
4317 CFmtStr( "Item definition %s: Missing required field \"item_class\"", m_pKVItem->GetName() ) );
4318
4319 // Check value ranges
4320 SCHEMA_INIT_CHECK(
4321 m_pKVItem->GetInt( "min_ilevel" ) >= 0,
4322 CFmtStr( "Item definition %s: \"min_ilevel\" must be greater than or equal to 0", GetDefinitionName() ) );
4323
4324 SCHEMA_INIT_CHECK(
4325 m_pKVItem->GetInt( "max_ilevel" ) >= 0,
4326 CFmtStr( "Item definition %s: \"max_ilevel\" must be greater than or equal to 0", GetDefinitionName() ) );
4327
4328 // Get the item class
4329 m_pszItemClassname = m_pKVItem->GetString( "item_class", NULL );
4330
4331 m_bAllowPurchaseStandalone = m_pKVItem->GetBool( "allow_purchase_standalone", false );
4332
4333 m_pszClassToken = m_pKVItem->GetString( "class_token_id", NULL );
4334 m_pszSlotToken = m_pKVItem->GetString( "slot_token_id", NULL );
4335
4336 m_bPublicItem = m_pKVItem->GetInt( "developer" ) == 0;
4337 m_bIgnoreInCollectionView = m_pKVItem->GetBool( "ignore_in_collection_view", false );
4338
4339 // Display data
4340 m_pszItemBaseName = m_pKVItem->GetString( "item_name", "" ); // non-NULL to ensure we can sort
4341 m_pszItemTypeName = m_pKVItem->GetString( "item_type_name", "" ); // non-NULL to ensure we can sort
4342 m_pszItemDesc = m_pKVItem->GetString( "item_description", NULL );
4343 m_pszArmoryDesc = m_pKVItem->GetString( "armory_desc", NULL );
4344 m_pszInventoryModel = m_pKVItem->GetString( "model_inventory", NULL );
4345 m_pszInventoryImage = m_pKVItem->GetString( "image_inventory", NULL );
4346
4347 m_pPortraitsKV = m_pKVItem->FindKey( "portraits" );
4348
4349 m_unItemTypeID = CRC32_ProcessSingleBuffer( m_pszItemTypeName, V_strlen( m_pszItemTypeName ) );
4350 const char* pOverlay = m_pKVItem->GetString( "image_inventory_overlay", NULL );
4351 if ( pOverlay )
4352 {
4353 m_pszInventoryOverlayImages.AddToTail( pOverlay );
4354 }
4355 pOverlay = m_pKVItem->GetString( "image_inventory_overlay2", NULL );
4356 if ( pOverlay )
4357 {
4358 m_pszInventoryOverlayImages.AddToTail( pOverlay );
4359 }
4360
4361 m_iInventoryImagePosition[0] = m_pKVItem->GetInt( "image_inventory_pos_x", 0 );
4362 m_iInventoryImagePosition[1] = m_pKVItem->GetInt( "image_inventory_pos_y", 0 );
4363 m_iInventoryImageSize[0] = m_pKVItem->GetInt( "image_inventory_size_w", 0 );
4364 m_iInventoryImageSize[1] = m_pKVItem->GetInt( "image_inventory_size_h", 0 );
4365 m_pszHolidayRestriction = m_pKVItem->GetString( "holiday_restriction", NULL );
4366 m_iSubType = m_pKVItem->GetInt( "subtype", 0 );
4367 m_pszBaseDisplayModel = m_pKVItem->GetString( "model_player", NULL );
4368 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.
4369 m_pszWorldDroppedModel = m_pKVItem->GetString( "model_dropped", NULL );
4370
4371 //build the world dropped model string by appending "_dropped"
4372 if ( ( m_pszWorldDroppedModel == NULL ) && ( m_pszWorldDisplayModel != NULL ) )
4373 {
4374 V_StripExtension( m_pszWorldDisplayModel, m_szWorldDroppedModel, sizeof( m_szWorldDroppedModel ) );
4375 V_strcat_safe( m_szWorldDroppedModel, "_dropped.mdl" );
4376 m_pszWorldDroppedModel = m_szWorldDroppedModel;
4377 }
4378
4379 // Add new StatTrak modules here.
4380
4381 m_pszIconDefaultImage = m_pKVItem->GetString( "icon_default_image", NULL );
4382 m_pszWorldExtraWearableModel = m_pKVItem->GetString( "extra_wearable", NULL );
4383 m_pszBrassModelOverride = m_pKVItem->GetString( "brass_eject_model", NULL );
4384 m_pszWorldExtraWearableModel = m_pKVItem->GetString( "extra_wearable", NULL );
4385 m_pszBrassModelOverride = m_pKVItem->GetString( "brass_eject_model", NULL );
4386 m_bHideBodyGroupsDeployedOnly = m_pKVItem->GetBool( "hide_bodygroups_deployed_only" );
4387 m_bAttachToHands = m_pKVItem->GetInt( "attach_to_hands", 0 ) != 0;
4388 m_bAttachToHandsVMOnly = m_pKVItem->GetInt( "attach_to_hands_vm_only", 0 ) != 0;
4389 m_bProperName = m_pKVItem->GetInt( "propername", 0 ) != 0;
4390 m_bFlipViewModel = m_pKVItem->GetInt( "flip_viewmodel", 0 ) != 0;
4391 m_bActAsWearable = m_pKVItem->GetInt( "act_as_wearable", 0 ) != 0;
4392 m_iDropType = StringFieldToInt( m_pKVItem->GetString("drop_type"), g_szDropTypeStrings, ARRAYSIZE(g_szDropTypeStrings) );
4393
4394 // Creation data
4395 m_bHidden = m_pKVItem->GetInt( "hidden", 0 ) != 0;
4396 m_bShouldShowInArmory = m_pKVItem->GetInt( "show_in_armory", 0 ) != 0;
4397 m_bBaseItem = m_pKVItem->GetInt( "baseitem", 0 ) != 0;
4398 m_bDefaultSlotItem = m_pKVItem->GetInt( "default_slot_item", 0 ) != 0;
4399 m_pszItemLogClassname = m_pKVItem->GetString( "item_logname", NULL );
4400 m_pszItemIconClassname = m_pKVItem->GetString( "item_iconname", NULL );
4401#if ECONITEM_DATABASE_AUDIT_TABLES_FEATURE
4402 m_pszDatabaseAuditTable = m_pKVItem->GetString( "database_audit_table", NULL );
4403#endif
4404 m_bImported = m_pKVItem->FindKey( "import_from" ) != NULL;
4405 m_bOnePerAccountCDKEY = m_pKVItem->GetInt( "one_per_account_cdkey", 0 ) != 0;
4406
4407 m_pszParticleFile = m_pKVItem->GetString( "particle_file", NULL );
4408 m_pszParticleSnapshotFile = m_pKVItem->GetString( "particle_snapshot", NULL );
4409
4410 m_pszLootListName = m_pKVItem->GetString( "loot_list_name", NULL );
4411
4412 // Stickers
4413 KeyValues *pStickerDataKV = m_pKVItem->FindKey( "stickers" );
4414 if ( pStickerDataKV )
4415 {
4416 FOR_EACH_SUBKEY( pStickerDataKV, pStickerModelEntry )
4417 {
4418
4419 StickerData_t *pStickerData = &m_vStickerModels[ m_vStickerModels.AddToTail() ];
4420
4421 const char *pStickerModelPath = pStickerModelEntry->GetString( "viewmodel_geometry" );
4422 V_strcpy_safe( pStickerData->m_szStickerModelPath, pStickerModelPath );
4423
4424 const char *pStickerMaterialPath = pStickerModelEntry->GetString( "viewmodel_material" );
4425 V_strcpy_safe( pStickerData->m_szStickerMaterialPath, pStickerMaterialPath );
4426
4427 const char *pStickerWorldModelProjectionPosition = pStickerModelEntry->GetString( "worldmodel_decal_pos" );
4428 if ( pStickerWorldModelProjectionPosition[0] != 0 )
4429 {
4430 float flX = 0.0f, flY = 0.0f, flZ = 0.0f;
4431 sscanf( pStickerWorldModelProjectionPosition, "%f %f %f", &flX, &flY, &flZ );
4432 pStickerData->m_vWorldModelProjectionStart = Vector( flX, flY, flZ );
4433 }
4434
4435 const char *pStickerWorldModelProjectionEnd = pStickerModelEntry->GetString( "worldmodel_decal_end" );
4436 if ( pStickerWorldModelProjectionEnd[0] != 0 )
4437 {
4438 float flX = 0.0f, flY = 0.0f, flZ = 0.0f;
4439 sscanf( pStickerWorldModelProjectionEnd, "%f %f %f", &flX, &flY, &flZ );
4440 pStickerData->m_vWorldModelProjectionEnd = Vector( flX, flY, flZ );
4441 }
4442 else
4443 {
4444 pStickerData->m_vWorldModelProjectionEnd = Vector(-10,0,0) + pStickerData->m_vWorldModelProjectionStart;
4445 }
4446
4447 V_strcpy_safe( pStickerData->m_szStickerBoneParentName, pStickerModelEntry->GetString( "worldmodel_decal_bone", "ValveBiped.weapon_bone" ) );
4448
4449 }
4450 }
4451
4452 // Tool data
4453 m_pTool = NULL;
4454 KeyValues *pToolDataKV = m_pKVItem->FindKey( "tool" );
4455 if ( pToolDataKV )
4456 {
4457 const char *pszType = pToolDataKV->GetString( "type", NULL );
4458 SCHEMA_INIT_CHECK( pszType, CFmtStr( "Tool '%s' missing required type.", m_pKVItem->GetName() ) );
4459
4460 // Common-to-all-tools settings.
4461 const char *pszUseString = pToolDataKV->GetString( "use_string", NULL );
4462 const char *pszUsageRestriction = pToolDataKV->GetString( "restriction", NULL );
4463 KeyValues *pToolUsageKV = pToolDataKV->FindKey( "usage" );
4464
4465 // Common-to-all-tools usage capability flags.
4466 item_capabilities_t usageCapabilities = (item_capabilities_t)ITEM_CAP_TOOL_DEFAULT;
4467 KeyValues *pToolUsageCapsKV = pToolDataKV->FindKey( "usage_capabilities" );
4468 if ( pToolUsageCapsKV )
4469 {
4470 KeyValues *pEntry = pToolUsageCapsKV->GetFirstSubKey();
4471 while ( pEntry )
4472 {
4473 ParseCapability( usageCapabilities, pEntry );
4474 pEntry = pEntry->GetNextKey();
4475 }
4476 }
4477
4478 m_pTool = pschema.CreateEconToolImpl( pszType, pszUseString, pszUsageRestriction, usageCapabilities, pToolUsageKV );
4479 SCHEMA_INIT_CHECK( m_pTool, CFmtStr( "Unable to create tool implementation for '%s', of type '%s'.", m_pKVItem->GetName(), pszType ) );
4480 }
4481
4482 // capabilities
4483 m_iCapabilities = (item_capabilities_t)ITEM_CAP_DEFAULT;
4484 KeyValues *pCapsKV = m_pKVItem->FindKey( "capabilities" );
4485 if ( pCapsKV )
4486 {
4487 KeyValues *pEntry = pCapsKV->GetFirstSubKey();
4488 while ( pEntry )
4489 {
4490 ParseCapability( m_iCapabilities, pEntry );
4491 pEntry = pEntry->GetNextKey();
4492 }
4493 }
4494
4495 // cache item map names
4496 m_pszArmoryRemap = m_pKVItem->GetString( "armory_remap", NULL );
4497 m_pszStoreRemap = m_pKVItem->GetString( "store_remap", NULL );
4498
4499 // Don't bother parsing visual data on the GC, it's unused.
4500 BInitVisualBlockFromKV( m_pKVItem, pschema, pVecErrors );
4501
4502 // Calculate our equip region mask.
4503 {
4504 m_unEquipRegionMask = 0;
4505 m_unEquipRegionConflictMask = 0;
4506
4507 // Our equip region will come from one of two places -- either we have an "equip_regions" (plural) section,
4508 // in which case we have any number of regions specified; or we have an "equip_region" (singular) section
4509 // which will have one and exactly one region. If we have "equip_regions" (plural), we ignore whatever is
4510 // in "equip_region" (singular).
4511 //
4512 // Yes, this is sort of dumb.
4513 CUtlVector<const char *> vecEquipRegionNames;
4514
4515 KeyValues *pKVMultiEquipRegions = m_pKVItem->FindKey( "equip_regions" ),
4516 *pKVSingleEquipRegion = m_pKVItem->FindKey( "equip_region" );
4517
4518 // Maybe we have multiple entries?
4519 if ( pKVMultiEquipRegions )
4520 {
4521 for ( KeyValues *pKVRegion = pKVMultiEquipRegions->GetFirstSubKey(); pKVRegion; pKVRegion = pKVRegion->GetNextKey() )
4522 {
4523 vecEquipRegionNames.AddToTail( pKVRegion->GetName() );
4524 }
4525 }
4526 // This is our one-and-only-one equip region.
4527 else if ( pKVSingleEquipRegion )
4528 {
4529 const char *pEquipRegionName = pKVSingleEquipRegion->GetString( (const char *)NULL, NULL );
4530 if ( pEquipRegionName )
4531 {
4532 vecEquipRegionNames.AddToTail( pEquipRegionName );
4533 }
4534 }
4535
4536 // For each of our regions, add to our conflict mask both ourself and all the regions
4537 // that we conflict with.
4538 FOR_EACH_VEC( vecEquipRegionNames, i )
4539 {
4540 const char *pszEquipRegionName = vecEquipRegionNames[i];
4541 equip_region_mask_t unThisRegionMask = pschema.GetEquipRegionMaskByName( pszEquipRegionName );
4542
4543 SCHEMA_INIT_CHECK(
4544 unThisRegionMask != 0,
4545 CFmtStr( "Item definition %s: Unable to find equip region mask for region named \"%s\"", GetDefinitionName(), vecEquipRegionNames[i] ) );
4546
4547 m_unEquipRegionMask |= pschema.GetEquipRegionBitMaskByName( pszEquipRegionName );
4548 m_unEquipRegionConflictMask |= unThisRegionMask;
4549 }
4550 }
4551
4552 // Parse the static attributes for this definition
4553 char const *szStaticAttributesSubkeyNames[] = { "attributes"
4554 };
4555 for ( int iStaticAttrGroup = 0; iStaticAttrGroup < Q_ARRAYSIZE( szStaticAttributesSubkeyNames ); ++ iStaticAttrGroup )
4556 {
4557 KeyValues *kvStaticAttrGroup = m_pKVItem->FindKey( szStaticAttributesSubkeyNames[iStaticAttrGroup] );
4558 if ( !kvStaticAttrGroup ) continue;
4559 FOR_EACH_SUBKEY( kvStaticAttrGroup, pKVKey )
4560 {
4561 static_attrib_t staticAttrib;
4562
4563 SCHEMA_INIT_SUBSTEP( staticAttrib.BInitFromKV_SingleLine( GetDefinitionName(), pKVKey, pVecErrors ) );
4564
4565 m_vecStaticAttributes.AddToTail( staticAttrib );
4566 }
4567 }
4568
4569 return SCHEMA_INIT_SUCCESS();
4570}
4571
4572bool static_attrib_t::BInitFromKV_MultiLine( const char *pszContext, KeyValues *pKVAttribute, CUtlVector<CUtlString> *pVecErrors )
4573{
4574 const CEconItemAttributeDefinition *pAttrDef = GetItemSchema()->GetAttributeDefinitionByName( pKVAttribute->GetName() );
4575
4576 SCHEMA_INIT_CHECK(
4577 NULL != pAttrDef,
4578 CFmtStr( "Context '%s': Attribute \"%s\" in \"attributes\" did not match any attribute definitions", pszContext, pKVAttribute->GetName() ) );
4579
4580 if ( pAttrDef )
4581 {
4582 const ISchemaAttributeType *pAttrType = pAttrDef->GetAttributeType();
4583 Assert( pAttrType );
4584
4585 iDefIndex = pAttrDef->GetDefinitionIndex();
4586
4587 const char *pszValue = pKVAttribute->GetString( "value", NULL );
4588 const bool bSuccessfullyLoadedValue = pAttrType->BConvertStringToEconAttributeValue( pAttrDef, pszValue, &m_value );
4589
4590 SCHEMA_INIT_CHECK(
4591 bSuccessfullyLoadedValue,
4592 CFmtStr( "Context '%s': Attribute \"%s\" could not parse value \"%s\"!", pszContext, pKVAttribute->GetName(), pszValue ? pszValue : "(null)" ) );
4593
4594 m_bForceGCToGenerate = pKVAttribute->GetBool( "force_gc_to_generate" );
4595 }
4596
4597 return SCHEMA_INIT_SUCCESS();
4598}
4599
4600bool static_attrib_t::BInitFromKV_SingleLine( const char *pszContext, KeyValues *pKVAttribute, CUtlVector<CUtlString> *pVecErrors )
4601{
4602 if ( pKVAttribute->GetFirstSubKey() )
4603 {
4604 return BInitFromKV_MultiLine( pszContext, pKVAttribute, pVecErrors );
4605 }
4606
4607 const CEconItemAttributeDefinition *pAttrDef = GetItemSchema()->GetAttributeDefinitionByName( pKVAttribute->GetName() );
4608
4609 SCHEMA_INIT_CHECK(
4610 NULL != pAttrDef,
4611 CFmtStr( "Context '%s': Attribute \"%s\" in \"attributes\" did not match any attribute definitions", pszContext, pKVAttribute->GetName() ) );
4612
4613 if ( pAttrDef )
4614 {
4615 const ISchemaAttributeType *pAttrType = pAttrDef->GetAttributeType();
4616 Assert( pAttrType );
4617
4618 iDefIndex = pAttrDef->GetDefinitionIndex();
4619
4620 const char *pszValue = pKVAttribute->GetString();
4621 const bool bSuccessfullyLoadedValue = pAttrType->BConvertStringToEconAttributeValue( pAttrDef, pszValue, &m_value );
4622
4623 SCHEMA_INIT_CHECK(
4624 bSuccessfullyLoadedValue,
4625 CFmtStr( "Context '%s': Attribute \"%s\" could not parse value \"%s\"!", pszContext, pKVAttribute->GetName(), pszValue ? pszValue : "(null)" ) );
4626
4627 m_bForceGCToGenerate = false;
4628 }
4629
4630 return SCHEMA_INIT_SUCCESS();
4631}
4632
4633bool CEconItemDefinition::BInitItemMappings( CEconItemSchema &pschema, CUtlVector<CUtlString> *pVecErrors )
4634{
4635 // Armory remapping
4636 if ( m_pszArmoryRemap && m_pszArmoryRemap[0] )
4637 {
4638 const CEconItemDefinition *pDef = pschema.GetItemDefinitionByName( m_pszArmoryRemap );
4639 if ( pDef )
4640 {
4641 m_iArmoryRemap = pDef->GetDefinitionIndex();
4642 }
4643
4644 SCHEMA_INIT_CHECK(
4645 pDef != NULL,
4646 CFmtStr( "Item %s: Armory remap definition \"%s\" was not found", m_pszItemBaseName, m_pszArmoryRemap ) );
4647 }
4648
4649 // Store remapping
4650 if ( m_pszStoreRemap && m_pszStoreRemap[0] )
4651 {
4652 const CEconItemDefinition *pDef = pschema.GetItemDefinitionByName( m_pszStoreRemap );
4653 if ( pDef )
4654 {
4655 m_iStoreRemap = pDef->GetDefinitionIndex();
4656 }
4657
4658 SCHEMA_INIT_CHECK(
4659 pDef != NULL,
4660 CFmtStr( "Item %s: Store remap definition \"%s\" was not found", m_pszItemBaseName, m_pszStoreRemap ) );
4661 }
4662
4663 return SCHEMA_INIT_SUCCESS();
4664}
4665
4666//-----------------------------------------------------------------------------
4667KeyValues* CEconItemDefinition::GetPortraitKVForModel( const char* pszModelName ) const
4668{
4669 if ( !m_pPortraitsKV )
4670 return NULL;
4671
4672 return m_pPortraitsKV->FindKey( pszModelName );
4673}
4674
4675
4676void CEconItemDefinition::AddItemSet( int nIndex )
4677{
4678 if ( m_iItemSets.Find( nIndex ) != m_iItemSets.InvalidIndex() )
4679 return;
4680
4681 m_iItemSets.AddToTail( nIndex );
4682}
4683
4684const CUtlVector< int >& CEconItemDefinition::GetItemSets( void ) const
4685{
4686 return m_iItemSets;
4687}
4688
4689#if defined(CLIENT_DLL) || defined(GAME_DLL)
4690//-----------------------------------------------------------------------------
4691// Purpose:
4692//-----------------------------------------------------------------------------
4693attachedparticlesystem_t *CEconItemDefinition::GetAttachedParticleData( int iIdx ) const
4694{
4695 if ( !GetAssetInfo() )
4696 return NULL;
4697
4698 Assert( iIdx < GetAssetInfo()->m_AttachedParticles.Count() );
4699 if ( iIdx >= GetAssetInfo()->m_AttachedParticles.Count() )
4700 return NULL;
4701
4702 int iParticleIndex = GetAssetInfo()->m_AttachedParticles[iIdx].m_iParticleIndex;
4703 return ItemSystem()->GetItemSchema()->GetAttributeControlledParticleSystemByIndex( iParticleIndex );
4704}
4705
4706bool CEconItemDefinition::IsAttachedParticleDataValidForStyle( int iIdx, int nStyle ) const
4707{
4708 Assert( iIdx < GetAssetInfo()->m_AttachedParticles.Count() );
4709 if ( iIdx >= GetAssetInfo()->m_AttachedParticles.Count() )
4710 return false;
4711
4712 return ( GetAssetInfo()->m_AttachedParticles[iIdx].m_nStyle == nStyle );
4713}
4714
4715#endif // #if defined(CLIENT_DLL) || defined(GAME_DLL)
4716
4717//-----------------------------------------------------------------------------
4718// Purpose: Generate and return a random level according to whatever leveling
4719// curve this definition uses.
4720//-----------------------------------------------------------------------------
4721uint32 CEconItemDefinition::RollItemLevel( void ) const
4722{
4723 return RandomInt( GetMinLevel(), GetMaxLevel() );
4724}
4725
4726
4727//-----------------------------------------------------------------------------
4728// Purpose:
4729//-----------------------------------------------------------------------------
4730void CEconItemDefinition::IterateAttributes( IEconItemAttributeIterator *pIterator ) const
4731{
4732 FOR_EACH_VEC( GetStaticAttributes(), i )
4733 {
4734 const static_attrib_t& staticAttrib = GetStaticAttributes()[i];
4735
4736 // we skip over static attributes that the GC will turn into dynamic attributes because otherwise we'll have
4737 // the appearance of iterating over them twice; for clients these attributes won't even make it into the
4738 // list
4739 if ( staticAttrib.m_bForceGCToGenerate )
4740 continue;
4741
4742 const CEconItemAttributeDefinition *pAttrDef = GetItemSchema()->GetAttributeDefinition( staticAttrib.iDefIndex );
4743 if ( !pAttrDef )
4744 continue;
4745
4746 const ISchemaAttributeType *pAttrType = pAttrDef->GetAttributeType();
4747 Assert( pAttrType );
4748
4749 if ( !pAttrType->OnIterateAttributeValue( pIterator, pAttrDef, staticAttrib.m_value ) )
4750 return;
4751 }
4752}
4753
4754const char *CEconItemDefinition::GetFirstSaleDate() const
4755{
4756 return GetRawDefinition()->GetString( "first_sale_date", "1970/01/01" );
4757}
4758
4759#if defined(CLIENT_DLL) || defined(GAME_DLL)
4760//-----------------------------------------------------------------------------
4761// Purpose:
4762//-----------------------------------------------------------------------------
4763Activity CEconItemDefinition::GetActivityOverride( Activity baseAct ) const
4764{
4765 int iAnims = GetNumAnimations();
4766 for ( int i = 0; i < iAnims; i++ )
4767 {
4768 animation_on_wearable_t *pData = GetAnimationData( i );
4769 if ( !pData )
4770 continue;
4771 if ( pData->iActivity == kActivityLookup_Unknown )
4772 {
4773 pData->iActivity = ActivityList_IndexForName( pData->pszActivity );
4774 }
4775
4776 if ( pData->iActivity == baseAct )
4777 {
4778 if ( pData->iReplacement == kActivityLookup_Unknown )
4779 {
4780 pData->iReplacement = ActivityList_IndexForName( pData->pszReplacement );
4781 }
4782
4783 if ( pData->iReplacement > 0 )
4784 {
4785 return (Activity) pData->iReplacement;
4786 }
4787 }
4788 }
4789
4790 return baseAct;
4791}
4792
4793//-----------------------------------------------------------------------------
4794// Purpose:
4795//-----------------------------------------------------------------------------
4796const char *CEconItemDefinition::GetReplacementForActivityOverride( Activity baseAct ) const
4797{
4798 int iAnims = GetNumAnimations();
4799 for ( int i = 0; i < iAnims; i++ )
4800 {
4801 animation_on_wearable_t *pData = GetAnimationData( i );
4802 if ( pData->iActivity == kActivityLookup_Unknown )
4803 {
4804 pData->iActivity = ActivityList_IndexForName( pData->pszActivity );
4805 }
4806 if ( pData && pData->iActivity == baseAct )
4807 {
4808 if ( CEconItemSchema::GetRandomStream().RandomFloat() < pData->flFrequency )
4809 return pData->pszReplacement;
4810 }
4811 }
4812
4813 return NULL;
4814}
4815
4816
4817//-----------------------------------------------------------------------------
4818// Purpose:
4819//-----------------------------------------------------------------------------
4820const char *CEconItemDefinition::GetReplacementSound( const char* pszSoundName ) const
4821{
4822 int iSounds = GetNumSounds();
4823 for ( int i=0; i<iSounds; i++ )
4824 {
4825 sound_on_wearable_t *pData = GetSoundData( i );
4826 if ( pData && FStrEq( pData->pszSound, pszSoundName ) )
4827 return pData->pszReplacement;
4828 }
4829
4830 return NULL;
4831}
4832
4833//-----------------------------------------------------------------------------
4834// Purpose:
4835//-----------------------------------------------------------------------------
4836const char *CEconItemDefinition::GetReplacementParticleEffect( const char* pszParticleName ) const
4837{
4838 int iParticles = GetNumParticles();
4839 for ( int i=0; i<iParticles; i++ )
4840 {
4841 particle_on_wearable_t *pData = GetParticleData( i );
4842 if ( pData && FStrEq( pData->pszParticle, pszParticleName ) )
4843 return pData->pszReplacement;
4844 }
4845
4846 return NULL;
4847}
4848
4849//-----------------------------------------------------------------------------
4850// Purpose:
4851//-----------------------------------------------------------------------------
4852const char *CEconItemDefinition::GetReplacementParticleSnapshot( const char* pszParticleSnapshotName ) const
4853{
4854 int iSnapshots = GetNumParticleSnapshots();
4855 for ( int i=0; i<iSnapshots; i++ )
4856 {
4857 particlesnapshot_on_wearable_t *pData = GetParticleSnapshotData( i );
4858 if ( pData && FStrEq( pData->pszParticleSnapshot, pszParticleSnapshotName ) )
4859 return pData->pszReplacement;
4860 }
4861
4862 return NULL;
4863}
4864
4865
4866//-----------------------------------------------------------------------------
4867// Purpose:
4868//-----------------------------------------------------------------------------
4869bool CEconItemDefinition::GetReplacementControlPoint( int nIndex, const char* pszParticleName, int &nOutputCP, Vector &vecCPValue ) const
4870{
4871 int iParticles = GetNumParticleControlPoints();
4872 if ( nIndex < iParticles )
4873 {
4874 particle_control_point_on_wearable_t *pData = GetParticleControlPointData( nIndex );
4875 if ( pData && FStrEq( pData->pszParticle, pszParticleName ) )
4876 {
4877 nOutputCP = pData->nParticleControlPoint;
4878 vecCPValue = pData->vecCPValue;
4879 return true;
4880 }
4881 }
4882 return false;
4883}
4884
4885//-----------------------------------------------------------------------------
4886// Purpose: Returns true if the content for this item view should be streamed. If false,
4887// it should be preloaded.
4888//-----------------------------------------------------------------------------
4889
4890// DO NOT MERGE THIS CONSOLE VARIABLE TO REL WE SHOULD NOT SHIP THIS OH GOD
4891#define ITEM_ENABLE_DYNAMIC_LOADING true
4892//ConVar item_enable_dynamic_loading( "item_enable_dynamic_loading", "1", FCVAR_REPLICATED, "Enable/disable dynamic streaming of econ content." );
4893
4894bool CEconItemDefinition::IsContentStreamable() const
4895{
4896 if ( !BLoadOnDemand() )
4897 return false;
4898
4899 return ITEM_ENABLE_DYNAMIC_LOADING;
4900}
4901#endif // defined(CLIENT_DLL) || defined(GAME_DLL)
4902
4903//-----------------------------------------------------------------------------
4904// Purpose:
4905//-----------------------------------------------------------------------------
4906const char* CEconItemDefinition::GetAlternateIcon( int iAlternateIcon ) const
4907{
4908 int iIdx = m_pMapAlternateIcons->Find( iAlternateIcon );
4909 if ( !m_pMapAlternateIcons->IsValidIndex( iIdx ) )
4910 return NULL;
4911 else
4912 return m_pMapAlternateIcons->Element( iIdx );
4913}
4914//-----------------------------------------------------------------------------
4915// Purpose:
4916// Sticker model paths
4917//-----------------------------------------------------------------------------
4918const int CEconItemDefinition::GetNumSupportedStickerSlots() const
4919{
4920 return m_vStickerModels.Count();
4921}
4922const char *CEconItemDefinition::GetStickerSlotModelBySlotIndex( uint32 index ) const
4923{
4924 return m_vStickerModels[index].m_szStickerModelPath;
4925}
4926const Vector &CEconItemDefinition::GetStickerSlotWorldProjectionStartBySlotIndex( uint32 index ) const
4927{
4928 return m_vStickerModels[index].m_vWorldModelProjectionStart;
4929}
4930const Vector &CEconItemDefinition::GetStickerSlotWorldProjectionEndBySlotIndex( uint32 index ) const
4931{
4932 return m_vStickerModels[index].m_vWorldModelProjectionEnd;
4933}
4934const char *CEconItemDefinition::GetStickerWorldModelBoneParentNameBySlotIndex( uint32 index ) const
4935{
4936 return m_vStickerModels[index].m_szStickerBoneParentName;
4937}
4938const char *CEconItemDefinition::GetStickerSlotMaterialBySlotIndex( uint32 index ) const
4939{
4940 return m_vStickerModels[index].m_szStickerMaterialPath;
4941}
4942
4943RETURN_ATTRIBUTE_STRING_F( CEconItemDefinition::GetIconDisplayModel, "icon display model", m_pszWorldDisplayModel );
4944RETURN_ATTRIBUTE_STRING_F( CEconItemDefinition::GetBuyMenuDisplayModel, "buymenu display model", m_pszWorldDisplayModel );
4945RETURN_ATTRIBUTE_STRING_F( CEconItemDefinition::GetPedestalDisplayModel, "pedestal display model", m_pszBaseDisplayModel );
4946RETURN_ATTRIBUTE_STRING_F( CEconItemDefinition::GetMagazineModel, "magazine model", NULL );
4947RETURN_ATTRIBUTE_STRING_F( CEconItemDefinition::GetUidModel, "uid model", "models/weapons/uid.mdl" );
4948RETURN_ATTRIBUTE_STRING_F( CEconItemDefinition::GetScopeLensMaskModel, "aimsight lens mask", NULL );
4949
4950const char *CEconItemDefinition::GetStatTrakModelByType( uint32 nType ) const
4951{
4952 const kill_eater_score_type_t *pScoreType = GetItemSchema()->FindKillEaterScoreType( nType );
4953 if ( !pScoreType )
4954 {
4955 pScoreType = GetItemSchema()->FindKillEaterScoreType( 0 );
4956 }
4957
4958 if ( !pScoreType )
4959 return NULL;
4960
4961 CSchemaAttributeDefHandle pAttrib_StatTrakModel( pScoreType->m_pszModelAttributeString );
4962 const char *pchStatTrakModel = NULL;
4963 FindAttribute_UnsafeBitwiseCast< CAttribute_String >( this, pAttrib_StatTrakModel, &pchStatTrakModel );
4964
4965 return pchStatTrakModel;
4966}
4967
4968
4969//-----------------------------------------------------------------------------
4970// Purpose: Constructor
4971//-----------------------------------------------------------------------------
4972CTimedItemRewardDefinition::CTimedItemRewardDefinition( void )
4973: m_unMinFreq( 0 ),
4974 m_unMaxFreq( UINT_MAX ),
4975 m_rtForcedBaselineAdjustment( 0 ),
4976 m_rtForcedLastDropTimeAdjustment( 0 ),
4977 m_unHoursInRewardPeriod( 0 ),
4978 m_unHoursBetweenDropsRealtime( 0 ),
4979 m_unPointsPerHourOverplayed( 0 ),
4980 m_flChance( 0.0f )
4981{
4982}
4983
4984
4985//-----------------------------------------------------------------------------
4986// Purpose: Copy constructor
4987//-----------------------------------------------------------------------------
4988CTimedItemRewardDefinition::CTimedItemRewardDefinition( const CTimedItemRewardDefinition &that )
4989{
4990 (*this) = that;
4991}
4992
4993
4994//-----------------------------------------------------------------------------
4995// Purpose: Operator=
4996//-----------------------------------------------------------------------------
4997CTimedItemRewardDefinition &CTimedItemRewardDefinition::operator=( const CTimedItemRewardDefinition &rhs )
4998{
4999 m_unMinFreq = rhs.m_unMinFreq;
5000 m_unMaxFreq = rhs.m_unMaxFreq;
5001 m_rtForcedBaselineAdjustment = rhs.m_rtForcedBaselineAdjustment;
5002 m_rtForcedLastDropTimeAdjustment = rhs.m_rtForcedLastDropTimeAdjustment;
5003 m_unHoursInRewardPeriod = rhs.m_unHoursInRewardPeriod;
5004 m_unHoursBetweenDropsRealtime = rhs.m_unHoursBetweenDropsRealtime;
5005 m_unPointsPerHourOverplayed = rhs.m_unPointsPerHourOverplayed;
5006 m_arrTotalPointsBasedOnHoursPlayed.RemoveAll();
5007 m_arrTotalPointsBasedOnHoursPlayed.EnsureCapacity( rhs.m_arrTotalPointsBasedOnHoursPlayed.Count() );
5008 m_arrTotalPointsBasedOnHoursPlayed.AddMultipleToTail( rhs.m_arrTotalPointsBasedOnHoursPlayed.Count(), rhs.m_arrTotalPointsBasedOnHoursPlayed.Base() );
5009 m_criteria = rhs.m_criteria;
5010 m_arrLootLists.CopyArray( rhs.m_arrLootLists.Base(), rhs.m_arrLootLists.Count() );
5011 m_flChance = rhs.m_flChance;
5012
5013 return *this;
5014}
5015
5016CTimedItemRewardDefinition::~CTimedItemRewardDefinition()
5017{
5018 m_arrDynamicLootLists.PurgeAndDeleteElements();
5019}
5020
5021//-----------------------------------------------------------------------------
5022// Purpose: Initialize the attribute definition
5023// Input: pKVTimedReward - The KeyValues representation of the timed reward
5024// schema - The overall item schema
5025// pVecErrors - An optional vector that will contain error messages if
5026// the init fails.
5027// Output: True if initialization succeeded, false otherwise
5028//-----------------------------------------------------------------------------
5029bool CTimedItemRewardDefinition::BInitFromKV( KeyValues *pKVTimedReward, CEconItemSchema &pschema, CUtlVector<CUtlString> *pVecErrors /* = NULL */ )
5030{
5031 // Parse the basic values
5032 m_flChance = pKVTimedReward->GetFloat( "pctChance" );
5033
5034#ifdef DOTA_DLL
5035 m_unMinFreq = pKVTimedReward->GetInt( "value_min", 0 );
5036 m_unMaxFreq = pKVTimedReward->GetInt( "value_max", UINT_MAX );
5037
5038 // Check required fields
5039 SCHEMA_INIT_CHECK(
5040 NULL != pKVTimedReward->FindKey( "value_min" ),
5041 CFmtStr( "Time reward %s: Missing required field \"value_min\"", pKVTimedReward->GetName() ) );
5042 SCHEMA_INIT_CHECK(
5043 NULL != pKVTimedReward->FindKey( "value_max" ),
5044 CFmtStr( "Time reward %s: Missing required field \"value_max\"", pKVTimedReward->GetName() ) );
5045#endif
5046
5047 //
5048 // Parse the basic values
5049 //
5050 SCHEMA_INIT_CHECK(
5051 NULL != pKVTimedReward->FindKey( "force_baseline_timestamp" ),
5052 CFmtStr( "Time reward %s: Missing required field \"force_baseline_timestamp\"", pKVTimedReward->GetName() ) );
5053 m_rtForcedBaselineAdjustment = pKVTimedReward->GetInt( "force_baseline_timestamp" );
5054
5055 m_rtForcedLastDropTimeAdjustment = 0;
5056 if ( pKVTimedReward->FindKey( "force_lastdrop_timestamp" ) )
5057 m_rtForcedLastDropTimeAdjustment = pKVTimedReward->GetInt( "force_lastdrop_timestamp" );
5058
5059 m_unHoursInRewardPeriod = pKVTimedReward->GetInt( "period_hours", 0 );
5060 SCHEMA_INIT_CHECK(
5061 NULL != pKVTimedReward->FindKey( "period_hours" ),
5062 CFmtStr( "Time reward %s: Missing required field \"period_hours\"", pKVTimedReward->GetName() ) );
5063 SCHEMA_INIT_CHECK(
5064 m_unHoursInRewardPeriod > 0,
5065 CFmtStr( "Time reward %s: Required field \"period_hours\" has invalid value", pKVTimedReward->GetName() ) );
5066
5067 m_unHoursBetweenDropsRealtime = pKVTimedReward->GetInt( "drop_interval_hours", 0 );
5068 SCHEMA_INIT_CHECK(
5069 NULL != pKVTimedReward->FindKey( "drop_interval_hours" ),
5070 CFmtStr( "Time reward %s: Missing required field \"drop_interval_hours\"", pKVTimedReward->GetName() ) );
5071 SCHEMA_INIT_CHECK(
5072 m_unHoursBetweenDropsRealtime >= 0,
5073 CFmtStr( "Time reward %s: Required field \"drop_interval_hours\" has invalid value", pKVTimedReward->GetName() ) );
5074
5075 SCHEMA_INIT_CHECK(
5076 NULL != pKVTimedReward->FindKey( "points_progression_hourly" ),
5077 CFmtStr( "Time reward %s: Missing required field \"points_progression_hourly\"", pKVTimedReward->GetName() ) );
5078 uint32 numPointsAccumulatedValidation = 0;
5079 for ( KeyValues *kvPoints = pKVTimedReward->FindKey( "points_progression_hourly" )->GetFirstSubKey(); kvPoints; kvPoints = kvPoints->GetNextKey() )
5080 {
5081 uint32 numPoints = kvPoints->GetInt();
5082 SCHEMA_INIT_CHECK(
5083 numPoints > numPointsAccumulatedValidation,
5084 CFmtStr( "Time reward %s: Required field \"points_progression_hourly\" defines invalid points after %u hours", pKVTimedReward->GetName(), m_arrTotalPointsBasedOnHoursPlayed.Count() ) );
5085 m_arrTotalPointsBasedOnHoursPlayed.AddToTail( numPoints );
5086 numPointsAccumulatedValidation = numPoints;
5087 }
5088 SCHEMA_INIT_CHECK(
5089 NULL != pKVTimedReward->FindKey( "points_per_additional_hour" ),
5090 CFmtStr( "Time reward %s: Missing required field \"points_per_additional_hour\"", pKVTimedReward->GetName() ) );
5091 m_unPointsPerHourOverplayed = pKVTimedReward->GetInt( "points_per_additional_hour", 0 );
5092
5093 SCHEMA_INIT_CHECK(
5094 NULL != pKVTimedReward->FindKey( "period_points_rollover" ),
5095 CFmtStr( "Time reward %s: Missing required field \"period_points_rollover\"", pKVTimedReward->GetName() ) );
5096 m_unPointsPerPeriodRollover = pKVTimedReward->GetInt( "period_points_rollover", 0 );
5097 SCHEMA_INIT_CHECK(
5098 m_unPointsPerPeriodRollover >= numPointsAccumulatedValidation,
5099 CFmtStr( "Time reward %s: Required field \"period_points_rollover\" defines invalid points after %u hours", pKVTimedReward->GetName(), m_arrTotalPointsBasedOnHoursPlayed.Count() ) );
5100
5101 SCHEMA_INIT_CHECK(
5102 NULL != pKVTimedReward->FindKey( "pctChance" ),
5103 CFmtStr( "Time reward %s: Missing required field \"pctChance\"", pKVTimedReward->GetName() ) );
5104
5105 SCHEMA_INIT_CHECK(
5106 ( m_flChance >= 0.0f ) && ( m_flChance <= 1.0f ),
5107 CFmtStr( "Time reward %s: Required field \"pctChance\" has invalid value", pKVTimedReward->GetName() ) );
5108
5109 // Parse the criteria or loot_list
5110 if ( pKVTimedReward->FindKey( "criteria" ) )
5111 {
5112 bool bCriteriaOK = m_criteria.BInitFromKV( pKVTimedReward->FindKey( "criteria", true ), pschema );
5113 SCHEMA_INIT_CHECK( bCriteriaOK, CFmtStr( "Time Reward %s: Invalid criteria", pKVTimedReward->GetName() ) );
5114
5115 // Check to make sure this criteria doesn't filter to an empty set
5116 if ( bCriteriaOK )
5117 {
5118 bool bMatch = false;
5119 FOR_EACH_MAP_FAST( pschema.GetItemDefinitionMap(), i )
5120 {
5121 if ( m_criteria.BEvaluate( pschema.GetItemDefinitionMap()[i], pschema ) )
5122 {
5123 bMatch = true;
5124 break;
5125 }
5126 }
5127
5128 SCHEMA_INIT_CHECK(
5129 bMatch,
5130 CFmtStr( "Time Reward %s: No items match the critera", pKVTimedReward->GetName() ) );
5131 }
5132 }
5133
5134 const char *pszLootList = pKVTimedReward->GetString("loot_list", NULL);
5135 if ( pszLootList && pszLootList[0] )
5136 {
5137 CUtlVector< char * > arrLootListsTokens;
5138 V_SplitString( pszLootList, ",", arrLootListsTokens );
5139
5140 SCHEMA_INIT_CHECK(
5141 arrLootListsTokens.Count() != 0,
5142 CFmtStr( "Time Reward %s: loot_list (%s) has zero elements", pKVTimedReward->GetName(), pszLootList ) );
5143
5144 FOR_EACH_VEC( arrLootListsTokens, iLootListToken )
5145 {
5146 const CEconLootListDefinition *pLootList = pschema.GetLootListByName( arrLootListsTokens[iLootListToken] );
5147
5148 // Make sure the item index is correct because we use this index as a reference
5149 SCHEMA_INIT_CHECK(
5150 NULL != pLootList,
5151 CFmtStr( "Time Reward %s: loot_list (%s) element '%s' does not exist", pKVTimedReward->GetName(), pszLootList, arrLootListsTokens[iLootListToken] ) );
5152
5153 if ( pLootList )
5154 m_arrLootLists.AddToTail( pLootList );
5155 }
5156
5157 arrLootListsTokens.PurgeAndDeleteElements();
5158 }
5159
5160 return SCHEMA_INIT_SUCCESS();
5161}
5162
5163float CTimedItemRewardDefinition::GetChance( void ) const
5164{
5165 return m_flChance;
5166}
5167
5168const CSchemaAttributeDefHandle& GetCampaignAttributeDefHandle( int nCampaignID, ECampaignAttributeType type )
5169{
5170 static CSchemaAttributeDefHandle* pAttrCampaignCompletionBitfield[ g_nNumCampaigns + 1 ];
5171 static CSchemaAttributeDefHandle* pAttrCampaignLastCompletedQuest[ g_nNumCampaigns + 1 ];
5172
5173 static bool s_bCampaignAttrInitialized = false;
5174 static bool s_bCampaignAttrValid = false;
5175
5176 if ( !s_bCampaignAttrInitialized )
5177 {
5178 s_bCampaignAttrInitialized = true;
5179 s_bCampaignAttrValid = true;
5180 for ( int j = 1; j <= g_nNumCampaigns; ++j )
5181 {
5182 pAttrCampaignCompletionBitfield[ j ]= new CSchemaAttributeDefHandle( ( new CFmtStr( "campaign %d completion bitfield", j ) )->Access() );
5183 if ( !( *pAttrCampaignCompletionBitfield[ j ] ) )
5184 s_bCampaignAttrValid = false;
5185
5186 pAttrCampaignLastCompletedQuest[ j ] = new CSchemaAttributeDefHandle( ( new CFmtStr( "campaign %d last completed quest", j ) )->Access() );
5187 if ( !( *pAttrCampaignLastCompletedQuest[ j ] ) )
5188 s_bCampaignAttrValid = false;
5189 }
5190 }
5191
5192
5193 if ( ( nCampaignID <= 0 ) || ( nCampaignID > g_nNumCampaigns ) )
5194 {
5195 Assert( 0 );
5196 Warning( "Attempting to lookup campaign %d, which does not exist. Verify that the code recognizes that campaigns are 1-based.", nCampaignID );
5197 }
5198 else if ( s_bCampaignAttrValid )
5199 {
5200 switch( type )
5201 {
5202 case k_ECampaignAttribute_CompletionBitfield:
5203 return *pAttrCampaignCompletionBitfield[ nCampaignID ];
5204
5205 case k_ECampaignAttribute_LastCompletedQuest:
5206 return *pAttrCampaignLastCompletedQuest[ nCampaignID ];
5207
5208 default:
5209 Assert( 0 );
5210 Warning( "Attempting to lookup campaign %d type %d, which does not exist.", nCampaignID, type );
5211 break;
5212 }
5213
5214 }
5215
5216 static CSchemaAttributeDefHandle s_DummyAttr( "undefined" );
5217 return s_DummyAttr;
5218}
5219
5220
5221const CSchemaAttributeDefHandle& GetStickerAttributeDefHandle( int attrNum, EStickerAttributeType type )
5222{
5223 // Attributes of the schema validation
5224 static bool s_bStickerAttrsSetup = false;
5225 static bool s_bStickerAttrsValid = false;
5226 static CSchemaAttributeDefHandle* pAttrStickerSlotID[ g_nNumStickerAttrs ];
5227 static CSchemaAttributeDefHandle* pAttrStickerSlotWear[ g_nNumStickerAttrs ];
5228 static CSchemaAttributeDefHandle* pAttrStickerSlotScale[ g_nNumStickerAttrs ];
5229 static CSchemaAttributeDefHandle* pAttrStickerSlotRotation[ g_nNumStickerAttrs ];
5230
5231 if ( !s_bStickerAttrsSetup )
5232 {
5233 s_bStickerAttrsSetup = true;
5234 s_bStickerAttrsValid = true;
5235 for ( int j = 0; j < g_nNumStickerAttrs; ++j )
5236 {
5237 pAttrStickerSlotID[ j ] = new CSchemaAttributeDefHandle( ( new CFmtStr( "sticker slot %d id", j ) )->Access() );
5238 if ( !( *pAttrStickerSlotID[ j ] ) ) s_bStickerAttrsValid = false;
5239 pAttrStickerSlotWear[ j ] = new CSchemaAttributeDefHandle( ( new CFmtStr( "sticker slot %d wear", j ) )->Access() );
5240 if ( !( *pAttrStickerSlotWear[ j ] ) ) s_bStickerAttrsValid = false;
5241 pAttrStickerSlotScale[ j ] = new CSchemaAttributeDefHandle( ( new CFmtStr( "sticker slot %d scale", j ) )->Access() );
5242 if ( !( *pAttrStickerSlotScale[ j ] ) ) s_bStickerAttrsValid = false;
5243 pAttrStickerSlotRotation[ j ] = new CSchemaAttributeDefHandle( ( new CFmtStr( "sticker slot %d rotation", j ) )->Access() );
5244
5245 if ( !( *pAttrStickerSlotRotation[ j ] ) ) s_bStickerAttrsValid = false;
5246 }
5247 }
5248
5249 if ( s_bStickerAttrsValid )
5250 {
5251 switch ( type )
5252 {
5253 case k_EStickerAttribute_ID:
5254 return *pAttrStickerSlotID[ attrNum ];
5255 break;
5256 case k_EStickerAttribute_Wear:
5257 return *pAttrStickerSlotWear[ attrNum ];
5258 break;
5259 case k_EStickerAttribute_Scale:
5260 return *pAttrStickerSlotScale[ attrNum ];
5261 break;
5262 case k_EStickerAttribute_Rotation:
5263 return *pAttrStickerSlotRotation[ attrNum ];
5264 break;
5265
5266 default:
5267 Assert( 0 );
5268 break;
5269 };
5270 }
5271
5272 static CSchemaAttributeDefHandle s_DummyAttr( "undefined" );
5273 return s_DummyAttr;
5274}
5275
5276bool CStickerKit::InitFromKeyValues( KeyValues *pKVEntry, const CStickerKit *pDefault, CUtlVector<CUtlString> *pVecErrors /*= NULL*/ )
5277{
5278 sName.Set( pKVEntry->GetString( "name", pDefault->sName.String() ) );
5279 sDescriptionString.Set( pKVEntry->GetString( "description_string", pDefault->sDescriptionString.String() ) );
5280 sItemName.Set( pKVEntry->GetString( "item_name", pDefault->sItemName.String() ) );
5281
5282 uint8 ucRarity;
5283 GetItemSchema()->BGetItemRarityFromName( pKVEntry->GetString( "item_rarity", "common" ), &ucRarity );
5284
5285 nRarity = ucRarity;
5286
5287 sMaterialPath.Set( pKVEntry->GetString( "sticker_material", pDefault->sMaterialPath.String() ) );
5288 if ( *sMaterialPath.String() )
5289 {
5290 sMaterialPathNoDrips.Set( pKVEntry->GetString( "sticker_material_nodrips", CFmtStr( "%s_nodrips", sMaterialPath.String() ) ) );
5291 }
5292
5293 m_strInventoryImage.Set( pKVEntry->GetString( "image_inventory", CFmtStr( "econ/stickers/%s", sMaterialPath.String() ) ) );
5294
5295 SetIconURLSmall( GetInventoryImage() );
5296 SetIconURLLarge( CFmtStr( "%s_large", GetInventoryImage() ) );
5297
5298 //
5299 // gc_generation_settings
5300 //
5301
5302 flRotateStart = pKVEntry->GetFloat( "gc_generation_settings/rotate_start", pDefault->flRotateStart );
5303 flRotateEnd = pKVEntry->GetFloat( "gc_generation_settings/rotate_end", pDefault->flRotateEnd );
5304 SCHEMA_INIT_CHECK(
5305 ( flRotateStart >= -360.0f ) && ( flRotateStart <= flRotateEnd ) && ( flRotateEnd <= 360.0f ),
5306 CFmtStr( "Sticker kit '%s' rotate range is not valid %f:%f", pKVEntry->GetName(), flRotateStart, flRotateEnd ) );
5307
5308 flScaleMin = pKVEntry->GetFloat( "gc_generation_settings/scale_min", pDefault->flScaleMin );
5309 flScaleMax = pKVEntry->GetFloat( "gc_generation_settings/scale_max", pDefault->flScaleMax );
5310 SCHEMA_INIT_CHECK( // Scale is in UV space so max is a smaller float, min is a larger float
5311 ( flScaleMax > 0.0f ) && ( flScaleMax <= flScaleMin ),
5312 CFmtStr( "Sticker kit '%s' scale range is not valid %f:%f", pKVEntry->GetName(), flScaleMax, flScaleMin ) );
5313
5314 flWearMin = pKVEntry->GetFloat( "gc_generation_settings/wear_min", pDefault->flWearMin );
5315 flWearMax = pKVEntry->GetFloat( "gc_generation_settings/wear_max", pDefault->flWearMax );
5316 SCHEMA_INIT_CHECK(
5317 ( flWearMin >= 0.0f ) && ( flWearMin <= flWearMax ) && ( flWearMax <= 1.0f ),
5318 CFmtStr( "Sticker kit '%s' wear range is not valid %f:%f", pKVEntry->GetName(), flWearMin, flWearMax ) );
5319
5320 m_nEventID = pKVEntry->GetInt( "tournament_event_id", pDefault->m_nEventID );
5321 m_nEventTeamID = pKVEntry->GetInt( "tournament_team_id", pDefault->m_nEventTeamID );
5322 m_nPlayerID = pKVEntry->GetInt( "tournament_player_id", pDefault->m_nPlayerID );
5323
5324 m_pKVItem = pKVEntry ? pKVEntry->MakeCopy() : NULL;
5325
5326 return true;
5327}
5328
5329bool CStickerList::InitFromKeyValues( KeyValues *pKVEntry, CUtlVector<CUtlString> *pVecErrors /*= NULL*/ )
5330{
5331 flWearMin = pKVEntry->GetFloat( "gc_generation_settings/wear_min", 0.0f );
5332 flWearMax = pKVEntry->GetFloat( "gc_generation_settings/wear_max", 1.0f );
5333 SCHEMA_INIT_CHECK(
5334 ( flWearMin >= 0.0f ) && ( flWearMin <= flWearMax ) && ( flWearMax <= 1.0f ),
5335 CFmtStr( "Sticker list '%s' wear range is not valid %f:%f", pKVEntry->GetName(), flWearMin, flWearMax ) );
5336
5337 flTotalWeight = 0.0f;
5338
5339 float flLargestMinWear = 0.0f;
5340 float flSmallestMaxWear = 1.0f;
5341
5342 for ( KeyValues *kvChild = pKVEntry->GetFirstValue(); kvChild; kvChild = kvChild->GetNextValue() )
5343 {
5344 char const *szName = kvChild->GetName();
5345 const CStickerKit *pStickerKit = GetItemSchema()->GetStickerKitDefinitionByName( szName );
5346 const CStickerList *pStickerList = GetItemSchema()->GetStickerListDefinitionByName( szName );
5347
5348 SCHEMA_INIT_CHECK(
5349 ( pStickerKit || pStickerList ) && !( pStickerKit && pStickerList ),
5350 CFmtStr( "Sticker list '%s' refers to an unknown stickerkit or stickerlist '%s'", pKVEntry->GetName(), szName ) );
5351
5352 float flWeight = kvChild->GetFloat();
5353 SCHEMA_INIT_CHECK(
5354 ( flWeight > 0.0f ),
5355 CFmtStr( "Sticker list '%s' has invalid weight %f", pKVEntry->GetName(), flWeight ) );
5356
5357 if ( pStickerKit )
5358 {
5359 flLargestMinWear = MAX( flLargestMinWear, pStickerKit->flWearMin );
5360 flSmallestMaxWear = MIN( flSmallestMaxWear, pStickerKit->flWearMax );
5361 }
5362
5363 if ( pStickerList )
5364 {
5365 flLargestMinWear = MAX( flLargestMinWear, pStickerList->flWearMin );
5366 flSmallestMaxWear = MIN( flSmallestMaxWear, pStickerList->flWearMax );
5367 }
5368
5369 // add the entry
5370 sticker_list_entry_t entry;
5371 entry.pKit = pStickerKit;
5372 entry.pList = pStickerList;
5373 entry.flWeight = flWeight;
5374
5375 arrElements.AddToTail( entry );
5376 flTotalWeight += entry.flWeight;
5377 }
5378
5379 SCHEMA_INIT_CHECK(
5380 ( arrElements.Count() > 0 ),
5381 CFmtStr( "Sticker list '%s' has no elements", pKVEntry->GetName() ) );
5382
5383 SCHEMA_INIT_CHECK(
5384 ( flTotalWeight > 0.0f ),
5385 CFmtStr( "Sticker list '%s' has no elements weight", pKVEntry->GetName() ) );
5386
5387 SCHEMA_INIT_CHECK(
5388 ( flSmallestMaxWear >= flWearMin ),
5389 CFmtStr( "Sticker list '%s' wear_min %f exceeds elements max range wear %f", pKVEntry->GetName(), flWearMin, flSmallestMaxWear ) );
5390
5391 SCHEMA_INIT_CHECK(
5392 ( flLargestMinWear <= flWearMax ),
5393 CFmtStr( "Sticker list '%s' wear_max %f below elements min range wear %f", pKVEntry->GetName(), flWearMax, flLargestMinWear ) );
5394
5395 return true;
5396}
5397
5398bool CStickerKit::GenerateStickerApplicationInfo( CAppliedStickerInfo_t *pInfo ) const
5399{
5400 if ( !pInfo )
5401 return false;
5402
5403 pInfo->flWearMin = MAX( pInfo->flWearMin, flWearMin );
5404 pInfo->flWearMax = MIN( pInfo->flWearMax, flWearMax );
5405 if ( pInfo->flWearMin > pInfo->flWearMax )
5406 return false;
5407
5408 pInfo->nID = nID;
5409 pInfo->flScale = CEconItemSchema::GetRandomStream().RandomFloat( flScaleMax, flScaleMin );
5410 pInfo->flRotate = CEconItemSchema::GetRandomStream().RandomFloat( flRotateStart, flRotateEnd );
5411
5412 return true;
5413}
5414
5415bool CStickerList::GenerateStickerApplicationInfo( CAppliedStickerInfo_t *pInfo ) const
5416{
5417 if ( !pInfo )
5418 return false;
5419
5420 // Generate a random roll into our list
5421 float flRand = CEconItemSchema::GetRandomStream().RandomFloat(0.f, 1.f) * flTotalWeight;
5422
5423 float flAccum = 0.f;
5424 for ( int i=0; i<arrElements.Count(); ++i )
5425 {
5426 flAccum += arrElements[i].flWeight;
5427 if ( flRand <= flAccum )
5428 {
5429 const sticker_list_entry_t &entry = arrElements[i];
5430
5431 pInfo->flWearMin = MAX( pInfo->flWearMin, flWearMin );
5432 pInfo->flWearMax = MIN( pInfo->flWearMax, flWearMax );
5433 if ( pInfo->flWearMin > pInfo->flWearMax )
5434 return false;
5435
5436 if ( entry.pList )
5437 return entry.pList->GenerateStickerApplicationInfo( pInfo );
5438
5439 if ( entry.pKit )
5440 return entry.pKit->GenerateStickerApplicationInfo( pInfo );
5441
5442 return false;
5443 }
5444 }
5445
5446 return false;
5447}
5448
5449
5450bool CPaintKit::InitFromKeyValues( KeyValues *pKVEntry, const CPaintKit *pDefault, bool bHandleAbsolutePaths )
5451{
5452 sName.Set( pKVEntry->GetString( "name", pDefault->sName.String() ) );
5453 sDescriptionString.Set( pKVEntry->GetString( "description_string", pDefault->sDescriptionString.String() ) );
5454 sDescriptionTag.Set( pKVEntry->GetString( "description_tag", pDefault->sDescriptionTag.String() ) );
5455 nRarity = 1; // rarities set in the paint_kits_rarity block
5456
5457 // Character paint kit fields
5458 sVmtPath.Set( pKVEntry->GetString( "vmt_path", pDefault->sVmtPath.String() ) );
5459
5460 if ( kvVmtOverrides != nullptr )
5461 {
5462 kvVmtOverrides->deleteThis();
5463 kvVmtOverrides = nullptr;
5464 }
5465
5466 KeyValues* pVmtOverrides = pKVEntry->FindKey( "vmt_overrides" );
5467 if ( pVmtOverrides != nullptr )
5468 {
5469 kvVmtOverrides = new KeyValues( "CustomCharacter" );
5470 kvVmtOverrides->MergeFrom( pVmtOverrides, KeyValues::MERGE_KV_UPDATE );
5471 }
5472
5473 // Regular paint kit fields
5474 if (bHandleAbsolutePaths)
5475 {
5476 // this solution is less than ideal, it'll only work for windows drive paths (not network paths), and in
5477 // order to make this better we need to change things down inside the material system / texture code.
5478 // it's only used for the workshop preview con command right now.
5479 const char* pPatternPath = pKVEntry->GetString( "pattern", pDefault->sPattern.String() );
5480#ifdef PLATFORM_WINDOWS
5481 int nLength = V_strlen( pPatternPath );
5482 if ( nLength > 2 && !( pPatternPath[0] == '/' && pPatternPath[1] == '/' && pPatternPath[2] != '/' ) && V_IsAbsolutePath( pPatternPath ) )
5483 {
5484 sPattern.Format( "//./%s", pPatternPath );
5485 }
5486 else
5487#endif
5488 {
5489 sPattern.Set( pPatternPath );
5490 }
5491 }
5492 else
5493 {
5494 sPattern.Set( pKVEntry->GetString( "pattern", pDefault->sPattern.String() ) );
5495 }
5496
5497 sLogoMaterial.Set( pKVEntry->GetString( "logo_material", pDefault->sLogoMaterial.String() ) );
5498
5499 nStyle = pKVEntry->GetInt( "style", pDefault->nStyle );
5500
5501 flWearDefault = pKVEntry->GetFloat( "wear_default", pDefault->flWearDefault );
5502 flWearRemapMin = pKVEntry->GetFloat( "wear_remap_min", pDefault->flWearRemapMin );
5503 flWearRemapMax = pKVEntry->GetFloat( "wear_remap_max", pDefault->flWearRemapMax );
5504 nFixedSeed = pKVEntry->GetInt( "seed", pDefault->nFixedSeed );
5505 uchPhongExponent = pKVEntry->GetInt( "phongexponent", pDefault->uchPhongExponent );
5506 uchPhongAlbedoBoost = pKVEntry->GetInt( "phongalbedoboost", static_cast< int >( pDefault->uchPhongAlbedoBoost ) - 1 ) + 1;
5507 uchPhongIntensity = pKVEntry->GetInt( "phongintensity", pDefault->uchPhongIntensity );
5508 flPatternScale = pKVEntry->GetFloat( "pattern_scale", pDefault->flPatternScale );
5509 flPatternOffsetXStart = pKVEntry->GetFloat( "pattern_offset_x_start", pDefault->flPatternOffsetXStart );
5510 flPatternOffsetXEnd = pKVEntry->GetFloat( "pattern_offset_x_end", pDefault->flPatternOffsetXEnd );
5511 flPatternOffsetYStart = pKVEntry->GetFloat( "pattern_offset_y_start", pDefault->flPatternOffsetYStart );
5512 flPatternOffsetYEnd = pKVEntry->GetFloat( "pattern_offset_y_end", pDefault->flPatternOffsetYEnd );
5513 flPatternRotateStart = pKVEntry->GetFloat( "pattern_rotate_start", pDefault->flPatternRotateStart );
5514 flPatternRotateEnd = pKVEntry->GetFloat( "pattern_rotate_end", pDefault->flPatternRotateEnd );
5515 flLogoScale = pKVEntry->GetFloat( "logo_scale", pDefault->flLogoScale );
5516 flLogoOffsetX = pKVEntry->GetFloat( "logo_offset_x", pDefault->flLogoOffsetX );
5517 flLogoOffsetY = pKVEntry->GetFloat( "logo_offset_y", pDefault->flLogoOffsetY );
5518 flLogoRotation = pKVEntry->GetFloat( "logo_rotation", pDefault->flLogoRotation );
5519 bIgnoreWeaponSizeScale = pKVEntry->GetBool( "ignore_weapon_size_scale", pDefault->bIgnoreWeaponSizeScale );
5520 nViewModelExponentOverrideSize = pKVEntry->GetInt( "view_model_exponent_override_size", pDefault->nViewModelExponentOverrideSize );
5521 bOnlyFirstMaterial = pKVEntry->GetBool( "only_first_material", pDefault->bOnlyFirstMaterial );
5522
5523 char szColorName[ 16 ];
5524 for ( int nColor = 0; nColor < CPaintKit::NUM_COLORS; ++nColor )
5525 {
5526 // Regular Colors
5527 V_snprintf( szColorName, sizeof( szColorName ), "color%i", nColor );
5528
5529 KeyValues *pKVColor = pKVEntry->FindKey( szColorName );
5530 if ( pKVColor )
5531 {
5532 const char *pchColor = pKVColor->GetString();
5533 unsigned int cR, cG, cB;
5534 sscanf( pchColor, "%u %u %u", &cR, &cG, &cB );
5535 rgbaColor[ nColor ].SetColor( cR, cG, cB, 255 );
5536 }
5537 else
5538 {
5539 rgbaColor[ nColor ] = pDefault->rgbaColor[ nColor ];
5540 }
5541
5542 // Logo Colors
5543 V_snprintf( szColorName, sizeof( szColorName ), "logocolor%i", nColor );
5544
5545 pKVColor = pKVEntry->FindKey( szColorName );
5546 if ( pKVColor )
5547 {
5548 const char *pchColor = pKVColor->GetString();
5549 unsigned int cR, cG, cB;
5550 sscanf( pchColor, "%u %u %u", &cR, &cG, &cB );
5551 rgbaLogoColor[ nColor ].SetColor( cR, cG, cB, 255 );
5552 }
5553 else
5554 {
5555 rgbaLogoColor[ nColor ] = pDefault->rgbaLogoColor[ nColor ];
5556 }
5557 }
5558
5559 return true;
5560}
5561
5562void CPaintKit::FillKeyValuesForWorkshop( KeyValues *pKVToFill ) const
5563{
5564 pKVToFill->SetInt( "style", nStyle );
5565 pKVToFill->SetString( "pattern", sPattern.String() );
5566
5567 char szColorName[ 16 ];
5568 for ( int nColor = 0; nColor < CPaintKit::NUM_COLORS; ++nColor )
5569 {
5570 // Regular Colors
5571 V_snprintf( szColorName, sizeof( szColorName ), "color%i", nColor );
5572 pKVToFill->SetColor( szColorName, rgbaColor[ nColor ] );
5573 }
5574
5575 pKVToFill->SetFloat( "pattern_scale", flPatternScale );
5576 pKVToFill->SetFloat( "pattern_offset_x_start", flPatternOffsetXStart );
5577 pKVToFill->SetFloat( "pattern_offset_x_end", flPatternOffsetXEnd );
5578 pKVToFill->SetFloat( "pattern_offset_y_start", flPatternOffsetYStart );
5579 pKVToFill->SetFloat( "pattern_offset_y_end", flPatternOffsetYEnd );
5580 pKVToFill->SetFloat( "pattern_rotate_start", flPatternRotateStart );
5581 pKVToFill->SetFloat( "pattern_rotate_end", flPatternRotateEnd );
5582
5583 pKVToFill->SetFloat( "wear_remap_min", flWearRemapMin );
5584 pKVToFill->SetFloat( "wear_remap_max", flWearRemapMax );
5585 pKVToFill->SetInt( "phongexponent", uchPhongExponent );
5586 pKVToFill->SetInt( "phongalbedoboost", static_cast< int >( uchPhongAlbedoBoost ) - 1 );
5587 pKVToFill->SetInt( "phongintensity", uchPhongIntensity );
5588 pKVToFill->SetBool( "ignore_weapon_size_scale", bIgnoreWeaponSizeScale );
5589 pKVToFill->SetInt( "view_model_exponent_override_size", nViewModelExponentOverrideSize );
5590 pKVToFill->SetBool( "only_first_material", bOnlyFirstMaterial );
5591
5592 //pKVToFill->SetString( "logo_material", sLogoMaterial.String() );
5593 //pKVToFill->SetFloat( "logo_scale", flLogoScale );
5594 //pKVToFill->SetFloat( "logo_offset_x", flLogoOffsetX );
5595 //pKVToFill->SetFloat( "logo_offset_y", flLogoOffsetY );
5596 //pKVToFill->SetFloat( "logo_rotation", flLogoRotation );
5597
5598 //for ( int nColor = 0; nColor < CPaintKit::NUM_COLORS; ++nColor )
5599 //{
5600 // // Logo Colors
5601 // V_snprintf( szColorName, sizeof( szColorName ), "logocolor%i", nColor );
5602
5603 // pKVToFill->SetColor( szColorName, rgbaLogoColor[ nColor ] );
5604 //}
5605
5606 //pKVToFill->SetInt( "seed", nFixedSeed );
5607}
5608
5609
5610//-----------------------------------------------------------------------------
5611// Purpose: Adds a foreign item definition to local definition mapping for a
5612// foreign app
5613//-----------------------------------------------------------------------------
5614void CForeignAppImports::AddMapping( uint16 unForeignDefIndex, const CEconItemDefinition *pDefn )
5615{
5616 m_mapDefinitions.InsertOrReplace( unForeignDefIndex, pDefn );
5617}
5618
5619
5620//-----------------------------------------------------------------------------
5621// Purpose: Adds a foreign item definition to local definition mapping for a
5622// foreign app
5623//-----------------------------------------------------------------------------
5624const CEconItemDefinition *CForeignAppImports::FindMapping( uint16 unForeignDefIndex ) const
5625{
5626 int i = m_mapDefinitions.Find( unForeignDefIndex );
5627 if( m_mapDefinitions.IsValidIndex( i ) )
5628 return m_mapDefinitions[i];
5629 else
5630 return NULL;
5631}
5632
5633//-----------------------------------------------------------------------------
5634// Purpose: Less function comparator for revolving loot lists map
5635//-----------------------------------------------------------------------------
5636static bool LLLessFunc( const int& e1, const int&e2 )
5637{
5638 return e1 < e2;
5639}
5640
5641//-----------------------------------------------------------------------------
5642// Purpose: Constructor
5643//-----------------------------------------------------------------------------
5644CEconItemSchema::CEconItemSchema( )
5645: m_unResetCount( 0 )
5646, m_pKVRawDefinition( NULL )
5647, m_unVersion( 0 )
5648, m_pDefaultItemDefinition( NULL )
5649, m_mapItemSets( DefLessFunc(const char*) )
5650, m_mapDefinitionPrefabs( DefLessFunc(const char*) )
5651, m_mapDefaultBodygroupState( DefLessFunc(const char*) )
5652, m_bSchemaParsingItems(false)
5653#if defined(CLIENT_DLL) || defined(GAME_DLL)
5654, m_pDelayedSchemaData( NULL )
5655#endif
5656, m_mapKillEaterScoreTypes( DefLessFunc( unsigned int ) )
5657{
5658 Reset();
5659}
5660
5661//-----------------------------------------------------------------------------
5662// Purpose:
5663//-----------------------------------------------------------------------------
5664IEconTool *CEconItemSchema::CreateEconToolImpl( const char *pszToolType, const char *pszUseString, const char *pszUsageRestriction, item_capabilities_t unCapabilities, KeyValues *pUsageKV )
5665{
5666 return nullptr;
5667}
5668
5669//-----------------------------------------------------------------------------
5670// Purpose: Resets the schema to before BInit was called
5671//-----------------------------------------------------------------------------
5672void CEconItemSchema::Reset( void )
5673{
5674 ++m_unResetCount;
5675
5676 m_unFirstValidClass = 0;
5677 m_unLastValidClass = 0;
5678 m_unFirstValidItemSlot = 0;
5679 m_unLastValidItemSlot = 0;
5680 m_unNumItemPresets = 0;
5681 m_unMinLevel = 0;
5682 m_unMaxLevel = 0;
5683 m_nMaxValidGraffitiTintDefID = 0;
5684 m_unSumQualityWeights = 0;
5685 m_mapRarities.Purge();
5686 m_mapSoundMaterials.Purge();
5687 m_mapQualities.Purge();
5688 m_mapItems.PurgeAndDeleteElements();
5689 m_mapItemsSorted.Purge();
5690 m_mapPaintKits.PurgeAndDeleteElements();
5691 m_mapStickerKits.PurgeAndDeleteElements();
5692 m_mapMusicDefs.PurgeAndDeleteElements();
5693 m_dictStickerKits.Purge();
5694 m_dictStickerLists.Purge();
5695 m_mapAttributesContainer.PurgeAndDeleteElements();
5696 FOR_EACH_VEC( m_vecAttributeTypes, i )
5697 {
5698 delete m_vecAttributeTypes[i].m_pAttrType;
5699 }
5700 m_vecAttributeTypes.Purge();
5701 m_mapRecipes.Purge();
5702 m_vecTimedRewards.Purge();
5703 m_mapAlternateIcons.Purge();
5704 m_mapItemSets.Purge();
5705 m_dictLootLists.Purge();
5706 m_mapAttributeControlledParticleSystems.Purge();
5707 m_unVersion = 0;
5708 if ( m_pKVRawDefinition )
5709 {
5710 m_pKVRawDefinition->deleteThis();
5711 m_pKVRawDefinition = NULL;
5712 }
5713
5714#if defined(CLIENT_DLL) || defined(GAME_DLL)
5715 delete m_pDefaultItemDefinition;
5716 m_pDefaultItemDefinition = NULL;
5717#endif
5718
5719 FOR_EACH_MAP_FAST( m_mapRecipes, i )
5720 {
5721 delete m_mapRecipes[i];
5722 }
5723
5724 FOR_EACH_MAP_FAST( m_mapDefinitionPrefabs, i )
5725 {
5726 m_mapDefinitionPrefabs[i]->deleteThis();
5727 }
5728 m_mapDefinitionPrefabs.Purge();
5729
5730 m_vecEquipRegionsList.Purge();
5731// m_vecItemLevelingData.PurgeAndDeleteElements();
5732 m_vecItemLevelingData.Purge();
5733
5734 m_RandomStream.SetSeed( 0 );
5735}
5736
5737
5738//-----------------------------------------------------------------------------
5739// Purpose: Operator=
5740//-----------------------------------------------------------------------------
5741CEconItemSchema &CEconItemSchema::operator=( CEconItemSchema &rhs )
5742{
5743 Reset();
5744 BInitSchema( rhs.m_pKVRawDefinition );
5745 return *this;
5746}
5747
5748#if defined( CLIENT_DLL )
5749#define MERGE_LIVE_PLAYERS_CODE 0
5750static void Helper_MergeKeyValuesUsingTemplate( KeyValues *kvInto, KeyValues *kvSrc, KeyValues *kvTemplate )
5751{
5752 FOR_EACH_SUBKEY( kvTemplate, kvTemplateSubkey )
5753 {
5754 if ( kvTemplateSubkey->GetDataType() == KeyValues::TYPE_NONE )
5755 {
5756 // This is a nested subkey
5757 if ( !V_strcmp( kvTemplateSubkey->GetName(), "*" ) )
5758 {
5759 // This is a wildcard subkey
5760 FOR_EACH_TRUE_SUBKEY( kvSrc, kvWildcardSrcSubkey )
5761 {
5762 KeyValues *pSubInto = kvInto->FindKey( kvWildcardSrcSubkey->GetNameSymbol() );
5763 if ( !pSubInto )
5764 {
5765#if MERGE_LIVE_PLAYERS_CODE
5766 kvInto->AddSubKey( pSubInto = kvWildcardSrcSubkey->MakeCopy() );
5767#else
5768 kvInto->AddSubKey( pSubInto = new KeyValues( kvWildcardSrcSubkey->GetName() ) );
5769#endif
5770 }
5771 Helper_MergeKeyValuesUsingTemplate( pSubInto, kvWildcardSrcSubkey, kvTemplateSubkey );
5772 }
5773 }
5774 else
5775 {
5776 // This is a direct dive into subkey
5777 KeyValues *pSubInto = kvInto->FindKey( kvTemplateSubkey->GetNameSymbol() );
5778 KeyValues *pSubSrc = kvSrc->FindKey( kvTemplateSubkey->GetNameSymbol() );
5779 if ( pSubInto && pSubSrc )
5780 Helper_MergeKeyValuesUsingTemplate( pSubInto, pSubSrc, kvTemplateSubkey );
5781 }
5782 }
5783 else
5784 {
5785 // This is a direct value copy
5786 if ( KeyValues *pSubSrc = kvSrc->FindKey( kvTemplateSubkey->GetNameSymbol() ) )
5787 {
5788 if ( KeyValues *pSubInto = kvInto->FindKey( kvTemplateSubkey->GetNameSymbol() ) )
5789 {
5790 // Cannot override an existing value!
5791 Assert( !"Cannot override existing schema value" );
5792 }
5793 else
5794 {
5795 // Make a copy and append
5796 kvInto->AddSubKey( pSubSrc->MakeCopy() );
5797 }
5798 }
5799 }
5800 }
5801}
5802#endif
5803
5804//-----------------------------------------------------------------------------
5805// Initializes the schema, given KV filename
5806//-----------------------------------------------------------------------------
5807bool CEconItemSchema::BInit( const char *fileName, const char *pathID, CUtlVector<CUtlString> *pVecErrors /* = NULL */)
5808{
5809 Reset();
5810 m_pKVRawDefinition = new KeyValues( "CEconItemSchema" );
5811 m_pKVRawDefinition->UsesEscapeSequences( true );
5812 if ( !m_pKVRawDefinition->LoadFromFile( g_pFullFileSystem, fileName, pathID ) )
5813 {
5814 m_pKVRawDefinition->deleteThis();
5815 m_pKVRawDefinition = NULL;
5816 }
5817
5818 if ( m_pKVRawDefinition )
5819 {
5820#if defined( CLIENT_DLL )
5821 if ( KeyValues *kvLiveTemplate = m_pKVRawDefinition->FindKey( "items_game_live" ) )
5822 {
5823 // for client code we also load the live file to supplement certain portions of schema
5824 char chLiveFilename[MAX_PATH];
5825 V_strcpy_safe( chLiveFilename, fileName );
5826 if ( char *chExt = V_strrchr( chLiveFilename, '.' ) )
5827 {
5828 char chSuffix[ 64 ] = {};
5829 V_sprintf_safe( chSuffix, "_live%s", chExt );
5830
5831 *chExt = 0;
5832 V_strcat_safe( chLiveFilename, chSuffix );
5833
5834 KeyValues *kvLive = new KeyValues( "CEconItemSchema" );
5835 kvLive->UsesEscapeSequences( true );
5836
5837 if ( kvLive->LoadFromFile( g_pFullFileSystem, chLiveFilename, pathID ) )
5838 {
5839 Helper_MergeKeyValuesUsingTemplate( m_pKVRawDefinition, kvLive, kvLiveTemplate );
5840 }
5841
5842#if MERGE_LIVE_PLAYERS_CODE
5843 if ( KeyValues *kvDump = m_pKVRawDefinition->FindKey( "pro_players" ) )
5844 {
5845 kvDump->SaveToFile( g_pFullFileSystem, "pro_players_merged.txt" );
5846 }
5847#endif
5848
5849 kvLive->deleteThis();
5850 kvLive = NULL;
5851 }
5852 }
5853#endif
5854 return BInitSchema( m_pKVRawDefinition, pVecErrors );
5855 }
5856 else
5857 {
5858#if !defined(CSTRIKE_DLL)
5859 Warning( "No %s file found. May be unable to create items.\n", fileName );
5860#endif // CSTRIKE_DLL
5861 return false;
5862 }
5863}
5864
5865//-----------------------------------------------------------------------------
5866// Initializes the schema, given KV in binary form
5867//-----------------------------------------------------------------------------
5868bool CEconItemSchema::BInitBinaryBuffer( CUtlBuffer &buffer, CUtlVector<CUtlString> *pVecErrors /* = NULL */ )
5869{
5870 Reset();
5871 m_pKVRawDefinition = new KeyValues( "CEconItemSchema" );
5872 m_pKVRawDefinition->UsesEscapeSequences( true );
5873 if ( m_pKVRawDefinition->ReadAsBinary( buffer ) )
5874 {
5875 return BInitSchema( m_pKVRawDefinition, pVecErrors );
5876 }
5877 if ( pVecErrors )
5878 {
5879 pVecErrors->AddToTail( "Error parsing keyvalues" );
5880 }
5881 return false;
5882}
5883
5884//-----------------------------------------------------------------------------
5885// Initializes the schema, given KV in text form
5886//-----------------------------------------------------------------------------
5887bool CEconItemSchema::BInitTextBuffer( CUtlBuffer &buffer, CUtlVector<CUtlString> *pVecErrors /* = NULL */ )
5888{
5889 Reset();
5890 m_pKVRawDefinition = new KeyValues( "CEconItemSchema" );
5891 m_pKVRawDefinition->UsesEscapeSequences( true );
5892 if ( m_pKVRawDefinition->LoadFromBuffer( NULL, buffer ) )
5893 {
5894 return BInitSchema( m_pKVRawDefinition, pVecErrors );
5895 }
5896 if ( pVecErrors )
5897 {
5898 pVecErrors->AddToTail( "Error parsing keyvalues" );
5899 }
5900 return false;
5901}
5902
5903#if defined(CLIENT_DLL) || defined(GAME_DLL)
5904//-----------------------------------------------------------------------------
5905// Set up the buffer to use to reinitialize our schema next time we can do so safely.
5906//-----------------------------------------------------------------------------
5907void CEconItemSchema::MaybeInitFromBuffer( IDelayedSchemaData *pDelayedSchemaData )
5908{
5909 // Use whatever our most current data block is.
5910 if ( m_pDelayedSchemaData )
5911 {
5912 delete m_pDelayedSchemaData;
5913 }
5914
5915 m_pDelayedSchemaData = pDelayedSchemaData;
5916
5917#ifdef CLIENT_DLL
5918 // If we aren't in a game we can parse immediately now.
5919 if ( !engine->IsInGame() )
5920 {
5921 BInitFromDelayedBuffer();
5922 }
5923#endif // CLIENT_DLL
5924}
5925
5926//-----------------------------------------------------------------------------
5927// We're in a safe place to change the contents of the schema, so do so and clean
5928// up whatever memory we were using.
5929//-----------------------------------------------------------------------------
5930bool CEconItemSchema::BInitFromDelayedBuffer()
5931{
5932 if ( !m_pDelayedSchemaData )
5933 return true;
5934
5935 bool bSuccess = m_pDelayedSchemaData->InitializeSchema( this );
5936 delete m_pDelayedSchemaData;
5937 m_pDelayedSchemaData = NULL;
5938
5939 return bSuccess;
5940}
5941#endif // !GC_DLL
5942
5943static void CalculateKeyValuesCRCRecursive( KeyValues *pKV, CRC32_t *crc, bool bIgnoreName = false )
5944{
5945 // Hash in the key name in LOWERCASE. Keyvalues files are not deterministic due
5946 // to the case insensitivity of the keys and the dependence on the existing
5947 // state of the name table upon entry.
5948 if ( !bIgnoreName )
5949 {
5950 const char *s = pKV->GetName();
5951 for (;;)
5952 {
5953 unsigned char x = tolower(*s);
5954 CRC32_ProcessBuffer( crc, &x, 1 ); // !SPEED! This is slow, but it works.
5955 if (*s == '\0') break;
5956 ++s;
5957 }
5958 }
5959
5960 // Now hash in value, depending on type
5961 // !FIXME! This is not byte-order independent!
5962 switch ( pKV->GetDataType() )
5963 {
5964 case KeyValues::TYPE_NONE:
5965 {
5966 FOR_EACH_SUBKEY( pKV, pChild )
5967 {
5968 CalculateKeyValuesCRCRecursive( pChild, crc );
5969 }
5970 break;
5971 }
5972 case KeyValues::TYPE_STRING:
5973 {
5974 const char *val = pKV->GetString();
5975 CRC32_ProcessBuffer( crc, val, V_strlen(val)+1 );
5976 break;
5977 }
5978
5979 case KeyValues::TYPE_INT:
5980 {
5981 int val = pKV->GetInt();
5982 CRC32_ProcessBuffer( crc, &val, sizeof(val) );
5983 break;
5984 }
5985
5986 case KeyValues::TYPE_UINT64:
5987 {
5988 uint64 val = pKV->GetUint64();
5989 CRC32_ProcessBuffer( crc, &val, sizeof(val) );
5990 break;
5991 }
5992
5993 case KeyValues::TYPE_FLOAT:
5994 {
5995 float val = pKV->GetFloat();
5996 CRC32_ProcessBuffer( crc, &val, sizeof(val) );
5997 break;
5998 }
5999 case KeyValues::TYPE_COLOR:
6000 {
6001 int val = pKV->GetColor().GetRawColor();
6002 CRC32_ProcessBuffer( crc, &val, sizeof(val) );
6003 break;
6004 }
6005
6006 default:
6007 case KeyValues::TYPE_PTR:
6008 case KeyValues::TYPE_WSTRING:
6009 {
6010 Assert( !"Unsupport data type!" );
6011 break;
6012 }
6013 }
6014}
6015
6016uint32 CEconItemSchema::CalculateKeyValuesVersion( KeyValues *pKV )
6017{
6018 CRC32_t crc;
6019 CRC32_Init( &crc );
6020
6021 // Calc CRC recursively. Ignore the very top-most
6022 // key name, which isn't set consistently
6023 CalculateKeyValuesCRCRecursive( pKV, &crc, true );
6024 CRC32_Final( &crc );
6025 return crc;
6026}
6027
6028//-----------------------------------------------------------------------------
6029// Purpose: Initializes the schema
6030// Input: pKVRawDefinition - The raw KeyValues representation of the schema
6031// pVecErrors - An optional vector that will contain error messages if
6032// the init fails.
6033// Output: True if initialization succeeded, false otherwise
6034//-----------------------------------------------------------------------------
6035bool CEconItemSchema::BInitSchema( KeyValues *pKVRawDefinition, CUtlVector<CUtlString> *pVecErrors /* = NULL */ )
6036{
6037#if !defined( GC_DLL )
6038 m_unVersion = CalculateKeyValuesVersion( pKVRawDefinition );
6039#endif
6040
6041 m_unMinLevel = pKVRawDefinition->GetInt( "item_level_min", 0 );
6042 m_unMaxLevel = pKVRawDefinition->GetInt( "item_level_max", 0 );
6043
6044 // Pre-parsed arrays in order of appearance for schema initialization
6045 CUtlVector< KeyValues * > arrRootPrefabs;
6046 CUtlVector< KeyValues * > arrRootClientLootLists;
6047 CUtlVector< KeyValues * > arrRootRevolvingLootlists;
6048 CUtlVector< KeyValues * > arrRootLootlists;
6049 CUtlVector< KeyValues * > arrRootMiscLootlists;
6050 CUtlVector< KeyValues * > arrRootStickerKits;
6051 CUtlVector< KeyValues * > arrRootStickerLists;
6052 CUtlVector< KeyValues * > arrRootPaintKits;
6053 CUtlVector< KeyValues * > arrRootPaintKitsRarity;
6054 CUtlVector< KeyValues * > arrRootItems;
6055 CUtlVector< KeyValues * > arrRootItemSets;
6056 CUtlVector< KeyValues * > arrRootMusicDefinitions;
6057 FOR_EACH_TRUE_SUBKEY( pKVRawDefinition, kvRootSubKey )
6058 {
6059 if ( !V_stricmp( kvRootSubKey->GetName(), "prefabs" ) )
6060 arrRootPrefabs.AddToTail( kvRootSubKey );
6061 else if ( !V_stricmp( kvRootSubKey->GetName(), "loot_lists" ) )
6062 arrRootLootlists.AddToTail( kvRootSubKey );
6063 else if ( !V_stricmp( kvRootSubKey->GetName(), "client_loot_lists" ) )
6064 arrRootClientLootLists.AddToTail( kvRootSubKey );
6065 else if ( !V_stricmp( kvRootSubKey->GetName(), "revolving_loot_lists" ) )
6066 arrRootRevolvingLootlists.AddToTail( kvRootSubKey );
6067 else if ( !V_stricmp( kvRootSubKey->GetName(), "loot_lists_misc" ) )
6068 arrRootMiscLootlists.AddToTail( kvRootSubKey );
6069 else if ( !V_stricmp( kvRootSubKey->GetName(), "sticker_kits" ) )
6070 arrRootStickerKits.AddToTail( kvRootSubKey );
6071 else if ( !V_stricmp( kvRootSubKey->GetName(), "sticker_lists" ) )
6072 arrRootStickerLists.AddToTail( kvRootSubKey );
6073 else if ( !V_stricmp( kvRootSubKey->GetName(), "paint_kits" ) )
6074 arrRootPaintKits.AddToTail( kvRootSubKey );
6075 else if ( !V_stricmp( kvRootSubKey->GetName(), "paint_kits_rarity" ) )
6076 arrRootPaintKitsRarity.AddToTail( kvRootSubKey );
6077 else if ( !V_stricmp( kvRootSubKey->GetName(), "items" ) )
6078 arrRootItems.AddToTail( kvRootSubKey );
6079 else if ( !V_stricmp( kvRootSubKey->GetName(), "item_sets" ) )
6080 arrRootItemSets.AddToTail( kvRootSubKey );
6081 else if ( !V_stricmp( kvRootSubKey->GetName(), "music_definitions" ) )
6082 arrRootMusicDefinitions.AddToTail( kvRootSubKey );
6083 }
6084
6085 // Parse the prefabs block first so the prefabs will be populated in case anything else wants
6086 // to use them later.
6087 FOR_EACH_VEC( arrRootPrefabs, i )
6088 {
6089 SCHEMA_INIT_SUBSTEP( BInitDefinitionPrefabs( arrRootPrefabs[i], pVecErrors ) );
6090 }
6091
6092 // Initialize the game info block
6093 KeyValues *pKVGameInfo = pKVRawDefinition->FindKey( "game_info" );
6094 SCHEMA_INIT_CHECK( NULL != pKVGameInfo, CFmtStr( "Required key \"game_info\" missing.\n" ) );
6095
6096 if ( NULL != pKVGameInfo )
6097 {
6098 SCHEMA_INIT_SUBSTEP( BInitGameInfo( pKVGameInfo, pVecErrors ) );
6099 }
6100
6101 // Initialize our attribute types. We don't actually pull this data from the schema right now but it
6102 // still makes sense to initialize it at this point.
6103 SCHEMA_INIT_SUBSTEP( BInitAttributeTypes( pVecErrors ) );
6104
6105 // Initialize the rarity block
6106 KeyValues *pKVRarities = pKVRawDefinition->FindKey( "rarities" );
6107 SCHEMA_INIT_CHECK( NULL != pKVRarities, CFmtStr( "Required key \"rarities\" missing.\n" ) );
6108 if ( NULL != pKVRarities )
6109 {
6110 SCHEMA_INIT_SUBSTEP( BInitRarities( pKVRarities, pVecErrors ) );
6111 }
6112
6113 // Initialize the qualities block
6114 KeyValues *pKVQualities = pKVRawDefinition->FindKey( "qualities" );
6115 SCHEMA_INIT_CHECK( NULL != pKVQualities, CFmtStr( "Required key \"qualities\" missing.\n" ) );
6116 if ( NULL != pKVQualities )
6117 {
6118 SCHEMA_INIT_SUBSTEP( BInitQualities( pKVQualities, pVecErrors ) );
6119 }
6120
6121 // Initialize the colors block
6122 KeyValues *pKVColors = pKVRawDefinition->FindKey( "colors" );
6123 SCHEMA_INIT_CHECK( NULL != pKVColors, CFmtStr( "Required key \"colors\" missing.\n" ) );
6124
6125 if ( NULL != pKVColors )
6126 {
6127 SCHEMA_INIT_SUBSTEP( BInitColors( pKVColors, pVecErrors ) );
6128 }
6129
6130 SCHEMA_INIT_SUBSTEP( BInitGraffitiTints( pKVRawDefinition->FindKey( "graffiti_tints" ), pVecErrors ) );
6131
6132 // Initialize the music block
6133 FOR_EACH_VEC( arrRootMusicDefinitions, i )
6134 {
6135 SCHEMA_INIT_SUBSTEP( BInitMusicDefs( arrRootMusicDefinitions[i], pVecErrors ) );
6136 }
6137
6138
6139 // Initialize the attributes block
6140 KeyValues *pKVAttributes = pKVRawDefinition->FindKey( "attributes" );
6141 SCHEMA_INIT_CHECK( NULL != pKVAttributes, CFmtStr( "Required key \"attributes\" missing.\n" ) );
6142 if ( NULL != pKVAttributes )
6143 {
6144 SCHEMA_INIT_SUBSTEP( BInitAttributes( pKVAttributes, pVecErrors ) );
6145 }
6146
6147 // Initialize the sound materials block
6148 KeyValues *pKVSoundMaterials = pKVRawDefinition->FindKey( "sound_materials" );
6149 if ( NULL != pKVSoundMaterials )
6150 {
6151 SCHEMA_INIT_SUBSTEP( BInitSoundMaterials( pKVSoundMaterials, pVecErrors ) );
6152 }
6153
6154 // Initialize the "equip_regions_list" block -- this is an optional block
6155 KeyValues *pKVEquipRegions = pKVRawDefinition->FindKey( "equip_regions_list" );
6156 if ( NULL != pKVEquipRegions )
6157 {
6158 SCHEMA_INIT_SUBSTEP( BInitEquipRegions( pKVEquipRegions, pVecErrors ) );
6159 }
6160
6161 // Initialize the "equip_conflicts" block -- this is an optional block, though it doesn't
6162 // make any sense and will probably fail internally if there is no corresponding "equip_regions"
6163 // block as well
6164 KeyValues *pKVEquipRegionConflicts = pKVRawDefinition->FindKey( "equip_conflicts" );
6165 if ( NULL != pKVEquipRegionConflicts )
6166 {
6167 SCHEMA_INIT_SUBSTEP( BInitEquipRegionConflicts( pKVEquipRegionConflicts, pVecErrors ) );
6168 }
6169
6170 // Do before items, so items can refer into it.
6171 KeyValues *pKVParticleSystems = pKVRawDefinition->FindKey( "attribute_controlled_attached_particles" );
6172 SCHEMA_INIT_SUBSTEP( BInitAttributeControlledParticleSystems( pKVParticleSystems, pVecErrors ) );
6173
6174 FOR_EACH_VEC( arrRootStickerKits, i )
6175 {
6176 SCHEMA_INIT_SUBSTEP( BInitStickerKits( arrRootStickerKits[i], pVecErrors ) );
6177 }
6178
6179 FOR_EACH_VEC( arrRootPaintKits, i )
6180 {
6181 SCHEMA_INIT_SUBSTEP( BInitPaintKits( arrRootPaintKits[i], pVecErrors ) );
6182 }
6183
6184 FOR_EACH_VEC( arrRootPaintKitsRarity, i )
6185 {
6186 SCHEMA_INIT_SUBSTEP( BInitPaintKitsRarity( arrRootPaintKitsRarity[i], pVecErrors ) );
6187 }
6188
6189 // Initialize the items block
6190 SCHEMA_INIT_CHECK( arrRootItems.Count(), CFmtStr( "Required key(s) \"items\" missing.\n" ) );
6191 FOR_EACH_VEC( arrRootItems, i )
6192 {
6193 m_bSchemaParsingItems = true;
6194 SCHEMA_INIT_SUBSTEP( BInitItems( arrRootItems[i], pVecErrors ) );
6195 m_bSchemaParsingItems = false;
6196 }
6197 SCHEMA_INIT_SUBSTEP( BInitItemMappings( pVecErrors ) );
6198
6199 // Setup bundle links
6200 SCHEMA_INIT_SUBSTEP( BInitBundles( pVecErrors ) );
6201
6202 // Setup payment rules.
6203 SCHEMA_INIT_SUBSTEP( BInitPaymentRules( pVecErrors ) );
6204
6205 // Parse the item_sets block.
6206 FOR_EACH_VEC( arrRootItemSets, i )
6207 {
6208 SCHEMA_INIT_SUBSTEP( BInitItemSets( arrRootItemSets[i], pVecErrors ) );
6209 }
6210
6211
6212 // Initialize the quest block
6213 KeyValues *pKVQuestDefs = pKVRawDefinition->FindKey( "quest_definitions" );
6214 SCHEMA_INIT_CHECK( NULL != pKVQuestDefs, CFmtStr( "Required key \"quest_definitions\" missing.\n" ) );
6215
6216 if ( NULL != pKVQuestDefs )
6217 {
6218 SCHEMA_INIT_SUBSTEP( BInitQuestDefs( pKVQuestDefs, pVecErrors ) );
6219 }
6220
6221 // Initialize the quest event schedule block
6222 SCHEMA_INIT_SUBSTEP( BInitQuestEvents( pKVRawDefinition->FindKey( "quest_schedule" ), pVecErrors ) );
6223
6224 // Initialize the campaign block
6225 KeyValues *pKVCampaignDefs = pKVRawDefinition->FindKey( "campaign_definitions" );
6226 SCHEMA_INIT_CHECK( NULL != pKVCampaignDefs, CFmtStr( "Required key \"campaign_definitions\" missing.\n" ) );
6227
6228 if ( NULL != pKVCampaignDefs )
6229 {
6230 SCHEMA_INIT_SUBSTEP( BInitCampaignDefs( pKVCampaignDefs, pVecErrors ) );
6231 }
6232
6233 // Parse any recipes block
6234 KeyValues *pKVRecipes = pKVRawDefinition->FindKey( "recipes" );
6235 if ( NULL != pKVRecipes )
6236 {
6237 SCHEMA_INIT_SUBSTEP( BInitRecipes( pKVRecipes, pVecErrors ) );
6238 }
6239
6240 // Parse alternate icons block.
6241 KeyValues * pKVAlternateIcons = pKVRawDefinition->FindKey( "alternate_icons2" );
6242 if ( NULL != pKVAlternateIcons )
6243 {
6244 SCHEMA_INIT_SUBSTEP( BInitAlternateIcons( pKVAlternateIcons, pVecErrors ) );
6245 }
6246
6247 // Reset our loot lists.
6248 m_dictLootLists.Purge();
6249
6250 // Parse the collection loot lists block
6251 KeyValues *pKVQuestRewardLootLists = pKVRawDefinition->FindKey( "quest_reward_loot_lists" );
6252 SCHEMA_INIT_SUBSTEP( BInitQuestRewardLootLists( pKVQuestRewardLootLists, pVecErrors ) );
6253
6254 // Parse the loot lists block (on the GC)
6255 KeyValues *pKVRandomAttributeTemplates = pKVRawDefinition->FindKey( "random_attribute_templates" );
6256
6257 // Parse the client loot lists block (everywhere)
6258 FOR_EACH_VEC( arrRootClientLootLists, i )
6259 {
6260 SCHEMA_INIT_SUBSTEP( BInitLootLists( arrRootClientLootLists[i], pKVRandomAttributeTemplates, pVecErrors, false ) );
6261 }
6262
6263 // Parse the revolving loot lists block
6264 FOR_EACH_VEC( arrRootRevolvingLootlists, i )
6265 {
6266 SCHEMA_INIT_SUBSTEP( BInitRevolvingLootLists( arrRootRevolvingLootlists[i], pVecErrors ) );
6267 }
6268
6269#if defined( CLIENT_DLL ) || defined( GAME_DLL )
6270 KeyValues *pKVArmoryData = pKVRawDefinition->FindKey( "armory_data" );
6271 if ( NULL != pKVArmoryData )
6272 {
6273 SCHEMA_INIT_SUBSTEP( BInitArmoryData( pKVArmoryData, pVecErrors ) );
6274 }
6275#endif
6276
6277 // Parse any achievement rewards
6278 KeyValues *pKVAchievementRewards = pKVRawDefinition->FindKey( "achievement_rewards" );
6279 if ( NULL != pKVAchievementRewards )
6280 {
6281 SCHEMA_INIT_SUBSTEP( BInitAchievementRewards( pKVAchievementRewards, pVecErrors ) );
6282 }
6283
6284#ifdef TF_CLIENT_DLL
6285 // Compute the number of concrete items, for each item, and cache for quick access
6286 SCHEMA_INIT_SUBSTEP( BInitConcreteItemCounts( pVecErrors ) );
6287#endif // TF_CLIENT_DLL
6288
6289 // Parse the item levels block
6290 KeyValues *pKVItemLevels = pKVRawDefinition->FindKey( "item_levels" );
6291 SCHEMA_INIT_SUBSTEP( BInitItemLevels( pKVItemLevels, pVecErrors ) );
6292
6293 // Parse the kill eater score types
6294 KeyValues *pKVKillEaterScoreTypes = pKVRawDefinition->FindKey( "kill_eater_score_types" );
6295 SCHEMA_INIT_SUBSTEP( BInitKillEaterScoreTypes( pKVKillEaterScoreTypes, pVecErrors ) );
6296
6297#ifdef CLIENT_DLL
6298 // Load web resource references.
6299 KeyValues* pKVWebResources = pKVRawDefinition->FindKey( "web_resources" );
6300 SCHEMA_INIT_SUBSTEP( BInitWebResources( pKVWebResources, pVecErrors ) );
6301#endif
6302
6303 // Load web resource references.
6304 KeyValues* pProPlayers = pKVRawDefinition->FindKey( "pro_players" );
6305 SCHEMA_INIT_SUBSTEP( BInitProPlayers( pProPlayers, pVecErrors ) );
6306
6307 SCHEMA_INIT_SUBSTEP( BPostSchemaInitStartupChecks( pVecErrors ) );
6308
6309 return SCHEMA_INIT_SUCCESS();
6310}
6311
6312//-----------------------------------------------------------------------------
6313// Purpose: Initializes the "game_info" section of the schema
6314//-----------------------------------------------------------------------------
6315bool CEconItemSchema::BInitGameInfo( KeyValues *pKVGameInfo, CUtlVector<CUtlString> *pVecErrors )
6316{
6317 m_unFirstValidClass = pKVGameInfo->GetInt( "first_valid_class", 0 );
6318 m_unLastValidClass = pKVGameInfo->GetInt( "last_valid_class", 0 );
6319 SCHEMA_INIT_CHECK( 0 <= m_unFirstValidClass, CFmtStr( "First valid class must be greater or equal to 0." ) );
6320 SCHEMA_INIT_CHECK( m_unFirstValidClass <= m_unLastValidClass, CFmtStr( "First valid class must be less than or equal to last valid class." ) );
6321
6322 m_unFirstValidItemSlot = pKVGameInfo->GetInt( "first_valid_item_slot", INVALID_EQUIPPED_SLOT );
6323 m_unLastValidItemSlot = pKVGameInfo->GetInt( "last_valid_item_slot", INVALID_EQUIPPED_SLOT );
6324 SCHEMA_INIT_CHECK( INVALID_EQUIPPED_SLOT != m_unFirstValidItemSlot, CFmtStr( "first_valid_item_slot not set!" ) );
6325 SCHEMA_INIT_CHECK( INVALID_EQUIPPED_SLOT != m_unLastValidItemSlot, CFmtStr( "last_valid_item_slot not set!" ) );
6326 SCHEMA_INIT_CHECK( m_unFirstValidItemSlot <= m_unLastValidItemSlot, CFmtStr( "First valid item slot must be less than or equal to last valid item slot." ) );
6327
6328 m_unNumItemPresets = pKVGameInfo->GetInt( "num_item_presets", -1 );
6329 SCHEMA_INIT_CHECK( (uint32)-1 != m_unNumItemPresets, CFmtStr( "num_item_presets not set!" ) );
6330
6331 return SCHEMA_INIT_SUCCESS();
6332}
6333
6334//-----------------------------------------------------------------------------
6335// Purpose:
6336//-----------------------------------------------------------------------------
6337bool CEconItemSchema::BInitAttributeTypes( CUtlVector<CUtlString> *pVecErrors )
6338{
6339 FOR_EACH_VEC( m_vecAttributeTypes, i )
6340 {
6341 delete m_vecAttributeTypes[i].m_pAttrType;
6342 }
6343 m_vecAttributeTypes.Purge();
6344
6345 m_vecAttributeTypes.AddToTail( attr_type_t( NULL, new CSchemaAttributeType_Default ) );
6346 m_vecAttributeTypes.AddToTail( attr_type_t( "uint32", new CSchemaAttributeType_Uint32 ) );
6347 m_vecAttributeTypes.AddToTail( attr_type_t( "float", new CSchemaAttributeType_Float ) );
6348 m_vecAttributeTypes.AddToTail( attr_type_t( "string", new CSchemaAttributeType_String ) );
6349 m_vecAttributeTypes.AddToTail( attr_type_t( "vector", new CSchemaAttributeType_Vector ) );
6350
6351 return SCHEMA_INIT_SUCCESS();
6352}
6353
6354//-----------------------------------------------------------------------------
6355// Purpose: Initializes the "prefabs" section of the schema
6356//-----------------------------------------------------------------------------
6357bool CEconItemSchema::BInitDefinitionPrefabs( KeyValues *pKVPrefabs, CUtlVector<CUtlString> *pVecErrors )
6358{
6359 FOR_EACH_TRUE_SUBKEY( pKVPrefabs, pKVPrefab )
6360 {
6361 const char *pszPrefabName = pKVPrefab->GetName();
6362
6363 int nMapIndex = m_mapDefinitionPrefabs.Find( pszPrefabName );
6364
6365 // Make sure the item index is correct because we use this index as a reference
6366 SCHEMA_INIT_CHECK(
6367 !m_mapDefinitionPrefabs.IsValidIndex( nMapIndex ),
6368 CFmtStr( "Duplicate prefab name (%s)", pszPrefabName ) );
6369
6370 m_mapDefinitionPrefabs.Insert( pszPrefabName, pKVPrefab->MakeCopy() );
6371 }
6372
6373 return SCHEMA_INIT_SUCCESS();
6374}
6375
6376//-----------------------------------------------------------------------------
6377// Purpose: Initializes the rarity section of the schema
6378//-----------------------------------------------------------------------------
6379bool CEconItemSchema::BInitRarities( KeyValues *pKVRarities, CUtlVector<CUtlString> *pVecErrors )
6380{
6381 // initialize the item definitions
6382 if ( NULL != pKVRarities )
6383 {
6384 FOR_EACH_TRUE_SUBKEY( pKVRarities, pKVRarity )
6385 {
6386 int nRarityIndex = pKVRarity->GetInt( "value" );
6387 int nMapIndex = m_mapRarities.Find( nRarityIndex );
6388
6389 // Make sure the item index is correct because we use this index as a reference
6390 SCHEMA_INIT_CHECK(
6391 !m_mapRarities.IsValidIndex( nMapIndex ),
6392 CFmtStr( "Duplicate rarity value (%d)", nRarityIndex ) );
6393
6394 nMapIndex = m_mapRarities.Insert( nRarityIndex );
6395 SCHEMA_INIT_SUBSTEP( m_mapRarities[nMapIndex].BInitFromKV( pKVRarity, *this, pVecErrors ) );
6396 }
6397 }
6398
6399 return SCHEMA_INIT_SUCCESS();
6400}
6401
6402//-----------------------------------------------------------------------------
6403// Purpose: Initializes the qualities section of the schema
6404// Input: pKVQualities - The qualities section of the KeyValues
6405// representation of the schema
6406// pVecErrors - An optional vector that will contain error messages if
6407// the init fails.
6408// Output: True if initialization succeeded, false otherwise
6409//-----------------------------------------------------------------------------
6410bool CEconItemSchema::BInitQualities( KeyValues *pKVQualities, CUtlVector<CUtlString> *pVecErrors )
6411{
6412 // initialize the item definitions
6413 if ( NULL != pKVQualities )
6414 {
6415 FOR_EACH_TRUE_SUBKEY( pKVQualities, pKVQuality )
6416 {
6417 int nQualityIndex = pKVQuality->GetInt( "value" );
6418 int nMapIndex = m_mapQualities.Find( nQualityIndex );
6419
6420 // Make sure the item index is correct because we use this index as a reference
6421 SCHEMA_INIT_CHECK(
6422 !m_mapQualities.IsValidIndex( nMapIndex ),
6423 CFmtStr( "Duplicate quality value (%d)", nQualityIndex ) );
6424
6425 nMapIndex = m_mapQualities.Insert( nQualityIndex );
6426 SCHEMA_INIT_SUBSTEP( m_mapQualities[nMapIndex].BInitFromKV( pKVQuality, *this, pVecErrors ) );
6427 }
6428 }
6429
6430 // Check the integrity of the quality definitions
6431
6432 // Check for duplicate quality names
6433 CUtlRBTree<const char *> rbQualityNames( CaselessStringLessThan );
6434 rbQualityNames.EnsureCapacity( m_mapQualities.Count() );
6435 FOR_EACH_MAP_FAST( m_mapQualities, i )
6436 {
6437 int iIndex = rbQualityNames.Find( m_mapQualities[i].GetName() );
6438 SCHEMA_INIT_CHECK(
6439 !rbQualityNames.IsValidIndex( iIndex ),
6440 CFmtStr( "Quality definition %d: Duplicate quality name %s", m_mapQualities[i].GetDBValue(), m_mapQualities[i].GetName() ) );
6441
6442 if( !rbQualityNames.IsValidIndex( iIndex ) )
6443 rbQualityNames.Insert( m_mapQualities[i].GetName() );
6444 }
6445
6446 return SCHEMA_INIT_SUCCESS();
6447}
6448
6449//-----------------------------------------------------------------------------
6450// Purpose:
6451//-----------------------------------------------------------------------------
6452bool CEconItemSchema::BInitColors( KeyValues *pKVColors, CUtlVector<CUtlString> *pVecErrors )
6453{
6454 // initialize the color definitions
6455 if ( NULL != pKVColors )
6456 {
6457 FOR_EACH_TRUE_SUBKEY( pKVColors, pKVColor )
6458 {
6459 CEconColorDefinition *pNewColorDef = new CEconColorDefinition;
6460
6461 SCHEMA_INIT_SUBSTEP( pNewColorDef->BInitFromKV( pKVColor, *this, pVecErrors ) );
6462 m_vecColorDefs.AddToTail( pNewColorDef );
6463 }
6464 }
6465
6466 return SCHEMA_INIT_SUCCESS();
6467}
6468
6469bool CEconItemSchema::BInitGraffitiTints( KeyValues *pKVColors, CUtlVector<CUtlString> *pVecErrors )
6470{
6471 FOR_EACH_TRUE_SUBKEY( pKVColors, pKVColor )
6472 {
6473 CEconGraffitiTintDefinition *pNewDef = new CEconGraffitiTintDefinition;
6474
6475 bool bResult = pNewDef->BInitFromKV( pKVColor, *this, pVecErrors );
6476 const int nMaxIDallowed = 64;
6477 if ( bResult )
6478 {
6479 SCHEMA_INIT_CHECK( ( pNewDef->GetID() > 0 ) && ( pNewDef->GetID() < nMaxIDallowed ),
6480 CFmtStr( "Graffiti tint definition id out of range [1..%d]: #%d %s\n", nMaxIDallowed, pNewDef->GetID(), pNewDef->GetColorName() ) );
6481 SCHEMA_INIT_CHECK( !m_vecGraffitiTintDefs.IsValidIndex( pNewDef->GetID() ) || !m_vecGraffitiTintDefs[pNewDef->GetID()],
6482 CFmtStr( "Graffiti tint definition duplicate: #%d %s\n", pNewDef->GetID(), pNewDef->GetColorName() ) );
6483 SCHEMA_INIT_CHECK( pNewDef->GetColorName() && *pNewDef->GetColorName() && !m_mapGraffitiTintByName.Defined( pNewDef->GetColorName() ),
6484 CFmtStr( "Graffiti tint name duplicate: #%d %s\n", pNewDef->GetID(), pNewDef->GetColorName() ) );
6485 bResult &= ( pNewDef->GetID() > 0 ) && ( pNewDef->GetID() < nMaxIDallowed ) &&
6486 ( !m_vecGraffitiTintDefs.IsValidIndex( pNewDef->GetID() ) || !m_vecGraffitiTintDefs[pNewDef->GetID()] ) &&
6487 pNewDef->GetColorName() && *pNewDef->GetColorName() && !m_mapGraffitiTintByName.Defined( pNewDef->GetColorName() );
6488 }
6489 if ( !bResult )
6490 {
6491 SCHEMA_INIT_SUBSTEP( bResult );
6492 delete pNewDef;
6493 }
6494 else
6495 {
6496 if ( !m_vecGraffitiTintDefs.Count() )
6497 {
6498 for ( int j = 0; j < nMaxIDallowed; ++ j )
6499 m_vecGraffitiTintDefs.AddToTail( NULL );
6500 }
6501 m_vecGraffitiTintDefs[pNewDef->GetID()] = pNewDef;
6502 m_mapGraffitiTintByName[pNewDef->GetColorName()] = pNewDef;
6503 m_nMaxValidGraffitiTintDefID = MAX( m_nMaxValidGraffitiTintDefID, pNewDef->GetID() );
6504 }
6505 }
6506
6507 SCHEMA_INIT_CHECK( m_vecGraffitiTintDefs.Count() > 0, CFmtStr( "Graffiti tint definitions failed to parse\n" ) );
6508
6509 return SCHEMA_INIT_SUCCESS();
6510}
6511
6512//-----------------------------------------------------------------------------
6513// Purpose:
6514//-----------------------------------------------------------------------------
6515AlternateIconData_t* CEconItemSchema::GetAlternateIcon( uint64 ullAlternateIcon )
6516{
6517 int iIdx = m_mapAlternateIcons.Find( ullAlternateIcon );
6518 if ( !m_mapAlternateIcons.IsValidIndex( iIdx ) )
6519 return NULL;
6520 else
6521 return &(m_mapAlternateIcons[iIdx]);
6522}
6523
6524//-----------------------------------------------------------------------------
6525// Purpose: Initializes the rarity section of the schema
6526//-----------------------------------------------------------------------------
6527bool CEconItemSchema::BInitSoundMaterials( KeyValues *pKVSoundMaterials, CUtlVector<CUtlString> *pVecErrors )
6528{
6529 // initialize the item definitions
6530 if ( NULL != pKVSoundMaterials )
6531 {
6532 FOR_EACH_TRUE_SUBKEY( pKVSoundMaterials, pKVSoundMaterial )
6533 {
6534 int nSoundMaterialIndex = pKVSoundMaterial->GetInt( "value" );
6535 int nMapIndex = m_mapSoundMaterials.Find( nSoundMaterialIndex );
6536
6537 // Make sure the item index is correct because we use this index as a reference
6538 SCHEMA_INIT_CHECK(
6539 !m_mapSoundMaterials.IsValidIndex( nMapIndex ),
6540 CFmtStr( "Duplicate sound material value (%d)", nSoundMaterialIndex ) );
6541
6542 nMapIndex = m_mapSoundMaterials.Insert( nSoundMaterialIndex );
6543 SCHEMA_INIT_SUBSTEP( m_mapSoundMaterials[nMapIndex].BInitFromKV( pKVSoundMaterial, *this, pVecErrors ) );
6544 }
6545 }
6546
6547 return SCHEMA_INIT_SUCCESS();
6548}
6549
6550//-----------------------------------------------------------------------------
6551// Purpose:
6552//-----------------------------------------------------------------------------
6553int CEconItemSchema::GetEquipRegionIndexByName( const char *pRegionName ) const
6554{
6555 FOR_EACH_VEC( m_vecEquipRegionsList, i )
6556 {
6557 const char *szEntryRegionName = m_vecEquipRegionsList[i].m_sName;
6558 if ( !V_stricmp( szEntryRegionName, pRegionName ) )
6559 return i;
6560 }
6561
6562 return -1;
6563}
6564
6565//-----------------------------------------------------------------------------
6566// Purpose:
6567//-----------------------------------------------------------------------------
6568equip_region_mask_t CEconItemSchema::GetEquipRegionBitMaskByName( const char *pRegionName ) const
6569{
6570 int iRegionIndex = GetEquipRegionIndexByName( pRegionName );
6571 if ( !m_vecEquipRegionsList.IsValidIndex( iRegionIndex ) )
6572 return 0;
6573
6574 equip_region_mask_t unRegionMask = 1 << m_vecEquipRegionsList[iRegionIndex].m_unBitIndex;
6575 Assert( unRegionMask > 0 );
6576
6577 return unRegionMask;
6578}
6579
6580//-----------------------------------------------------------------------------
6581// Purpose:
6582//-----------------------------------------------------------------------------
6583void CEconItemSchema::SetEquipRegionConflict( int iRegion, unsigned int unBit )
6584{
6585 Assert( m_vecEquipRegionsList.IsValidIndex( iRegion ) );
6586
6587 equip_region_mask_t unRegionMask = 1 << unBit;
6588 Assert( unRegionMask > 0 );
6589
6590 m_vecEquipRegionsList[iRegion].m_unMask |= unRegionMask;
6591}
6592
6593//-----------------------------------------------------------------------------
6594// Purpose:
6595//-----------------------------------------------------------------------------
6596equip_region_mask_t CEconItemSchema::GetEquipRegionMaskByName( const char *pRegionName ) const
6597{
6598 int iRegionIdx = GetEquipRegionIndexByName( pRegionName );
6599 if ( iRegionIdx < 0 )
6600 return 0;
6601
6602 return m_vecEquipRegionsList[iRegionIdx].m_unMask;
6603}
6604
6605//-----------------------------------------------------------------------------
6606// Purpose:
6607//-----------------------------------------------------------------------------
6608void CEconItemSchema::AssignDefaultBodygroupState( const char *pszBodygroupName, int iValue )
6609{
6610 // Flip the value passed in -- if we specify in the schema that a region should be off, we assume that it's
6611 // on by default.
6612 int iDefaultValue = iValue == 0 ? 1 : 0;
6613
6614 // Make sure that we're constantly reinitializing our default value to the same default value. This is sort
6615 // of dumb but it works for everything we've got now. In the event that conflicts start cropping up it would
6616 // be easy enough to make a new schema section.
6617 int iIndex = m_mapDefaultBodygroupState.Find( pszBodygroupName );
6618 if ( (m_mapDefaultBodygroupState.IsValidIndex( iIndex ) && m_mapDefaultBodygroupState[iIndex] != iDefaultValue) ||
6619 (iValue < 0 || iValue > 1) )
6620 {
6621 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 );
6622 }
6623
6624 if ( !m_mapDefaultBodygroupState.IsValidIndex( iIndex ) )
6625 {
6626 m_mapDefaultBodygroupState.Insert( pszBodygroupName, iDefaultValue );
6627 }
6628}
6629
6630//-----------------------------------------------------------------------------
6631// Purpose:
6632//-----------------------------------------------------------------------------
6633bool CEconItemSchema::BInitEquipRegions( KeyValues *pKVEquipRegions, CUtlVector<CUtlString> *pVecErrors )
6634{
6635 CUtlVector<const char *> vecNames;
6636
6637 FOR_EACH_SUBKEY( pKVEquipRegions, pKVRegion )
6638 {
6639 const char *pRegionKeyName = pKVRegion->GetName();
6640
6641 vecNames.Purge();
6642
6643 // The "shared" name is special for equip regions -- it means that all of the sub-regions specified
6644 // will use the same bit to store equipped-or-not data, but that one bit can be accessed by a whole
6645 // bunch of different names. This is useful in TF where different classes have different regions, but
6646 // those regions cannot possibly conflict with each other. For example, "scout_backpack" cannot possibly
6647 // overlap with "pyro_shoulder" because they can't even be equipped on the same character.
6648 if ( pRegionKeyName && !Q_stricmp( pRegionKeyName, "shared" ) )
6649 {
6650 FOR_EACH_SUBKEY( pKVRegion, pKVSharedRegionName )
6651 {
6652 vecNames.AddToTail( pKVSharedRegionName->GetName() );
6653 }
6654 }
6655 // We have a standard name -- this one entry is its own equip region.
6656 else
6657 {
6658 vecNames.AddToTail( pRegionKeyName );
6659 }
6660
6661 // What bit will this equip region use to mask against conflicts? If we don't have any equip regions
6662 // at all, we'll use the base bit, otherwise we just grab one higher than whatever we used last.
6663 unsigned int unNewBitIndex = m_vecEquipRegionsList.Count() <= 0 ? 0 : m_vecEquipRegionsList.Tail().m_unBitIndex + 1;
6664
6665 FOR_EACH_VEC( vecNames, i )
6666 {
6667 const char *pRegionName = vecNames[i];
6668
6669 // Make sure this name is unique.
6670 if ( GetEquipRegionIndexByName( pRegionName ) >= 0 )
6671 {
6672 pVecErrors->AddToTail( CFmtStr( "Duplicate equip region named \"%s\".", pRegionName ).Access() );
6673 continue;
6674 }
6675
6676 // Make a new region.
6677 EquipRegion newEquipRegion;
6678 newEquipRegion.m_sName = pRegionName;
6679 newEquipRegion.m_unMask = 0; // we'll update this mask later
6680 newEquipRegion.m_unBitIndex = unNewBitIndex;
6681
6682 int iIdx = m_vecEquipRegionsList.AddToTail( newEquipRegion );
6683
6684 // Tag this region to conflict with itself so that if nothing else two items in the same
6685 // region can't equip over each other.
6686 SetEquipRegionConflict( iIdx, unNewBitIndex );
6687 }
6688 }
6689
6690 return SCHEMA_INIT_SUCCESS();
6691}
6692
6693//-----------------------------------------------------------------------------
6694// Purpose:
6695//-----------------------------------------------------------------------------
6696bool CEconItemSchema::BInitEquipRegionConflicts( KeyValues *pKVEquipRegionConflicts, CUtlVector<CUtlString> *pVecErrors )
6697{
6698 FOR_EACH_TRUE_SUBKEY( pKVEquipRegionConflicts, pKVConflict )
6699 {
6700 // What region is the base of this conflict?
6701 const char *pRegionName = pKVConflict->GetName();
6702 int iRegionIdx = GetEquipRegionIndexByName( pRegionName );
6703 if ( iRegionIdx < 0 )
6704 {
6705 pVecErrors->AddToTail( CFmtStr( "Unable to find base equip region named \"%s\" for conflicts.", pRegionName ).Access() );
6706 continue;
6707 }
6708
6709 FOR_EACH_SUBKEY( pKVConflict, pKVConflictOther )
6710 {
6711 const char *pOtherRegionName = pKVConflictOther->GetName();
6712 int iOtherRegionIdx = GetEquipRegionIndexByName( pOtherRegionName );
6713 if ( iOtherRegionIdx < 0 )
6714 {
6715 pVecErrors->AddToTail( CFmtStr( "Unable to find other equip region named \"%s\" for conflicts.", pOtherRegionName ).Access() );
6716 continue;
6717 }
6718
6719 SetEquipRegionConflict( iRegionIdx, m_vecEquipRegionsList[iOtherRegionIdx].m_unBitIndex );
6720 SetEquipRegionConflict( iOtherRegionIdx, m_vecEquipRegionsList[iRegionIdx].m_unBitIndex );
6721 }
6722 }
6723
6724 return SCHEMA_INIT_SUCCESS();
6725}
6726
6727//-----------------------------------------------------------------------------
6728// Purpose: Initializes the attributes section of the schema
6729// Input: pKVAttributes - The attributes section of the KeyValues
6730// representation of the schema
6731// pVecErrors - An optional vector that will contain error messages if
6732// the init fails.
6733// Output: True if initialization succeeded, false otherwise
6734//-----------------------------------------------------------------------------
6735bool CEconItemSchema::BInitAttributes( KeyValues *pKVAttributes, CUtlVector<CUtlString> *pVecErrors )
6736{
6737 // Initialize the attribute definitions
6738 FOR_EACH_TRUE_SUBKEY( pKVAttributes, pKVAttribute )
6739 {
6740 int nAttrIndex = Q_atoi( pKVAttribute->GetName() );
6741
6742 // Make sure the index is positive
6743 SCHEMA_INIT_CHECK(
6744 ( nAttrIndex >= 0 ) && ( nAttrIndex < 30*1000 ),
6745 CFmtStr( "Attribute definition index %d must be greater than or equal to zero", nAttrIndex ) );
6746
6747 if ( !( ( nAttrIndex >= 0 ) && ( nAttrIndex < 30*1000 ) ) )
6748 continue;
6749
6750 // Build the direct mapping container
6751 while ( nAttrIndex >= m_mapAttributesContainer.Count() )
6752 m_mapAttributesContainer.AddToTail( NULL );
6753 Assert( nAttrIndex < m_mapAttributesContainer.Count() );
6754
6755 // Make sure the attribute index is not repeated
6756 SCHEMA_INIT_CHECK(
6757 !m_mapAttributesContainer[nAttrIndex],
6758 CFmtStr( "Duplicate attribute definition index (%d)", nAttrIndex ) );
6759 if ( m_mapAttributesContainer[nAttrIndex] )
6760 continue;
6761
6762 m_mapAttributesContainer[nAttrIndex] = new CEconItemAttributeDefinition;
6763 SCHEMA_INIT_SUBSTEP( m_mapAttributesContainer[nAttrIndex]->BInitFromKV( pKVAttribute, *this, pVecErrors ) );
6764 }
6765
6766 // Check the integrity of the attribute definitions
6767
6768 // Check for duplicate attribute definition names
6769 CUtlRBTree<const char *> rbAttributeNames( CaselessStringLessThan );
6770 rbAttributeNames.EnsureCapacity( m_mapAttributesContainer.Count() );
6771 FOR_EACH_VEC( m_mapAttributesContainer, i )
6772 {
6773 if ( !m_mapAttributesContainer[i] )
6774 continue;
6775
6776 int iIndex = rbAttributeNames.Find( m_mapAttributesContainer[i]->GetDefinitionName() );
6777 SCHEMA_INIT_CHECK(
6778 !rbAttributeNames.IsValidIndex( iIndex ),
6779 CFmtStr( "Attribute definition %d: Duplicate name \"%s\"", i, m_mapAttributesContainer[i]->GetDefinitionName() ) );
6780 if( !rbAttributeNames.IsValidIndex( iIndex ) )
6781 rbAttributeNames.Insert( m_mapAttributesContainer[i]->GetDefinitionName() );
6782 }
6783
6784 return SCHEMA_INIT_SUCCESS();
6785}
6786
6787//-----------------------------------------------------------------------------
6788// Purpose: After bundles are set up, we can do payment rules.
6789//-----------------------------------------------------------------------------
6790bool CEconItemSchema::BInitPaymentRules( CUtlVector<CUtlString> *pVecErrors )
6791{
6792 return true;
6793}
6794
6795//-----------------------------------------------------------------------------
6796// Purpose: After items are initialized, this method links bundles to their contents.
6797//-----------------------------------------------------------------------------
6798bool CEconItemSchema::BInitBundles( CUtlVector<CUtlString> *pVecErrors )
6799{
6800 // Link each bundle with its contents.
6801 FOR_EACH_MAP_FAST( m_mapItems, i )
6802 {
6803 CEconItemDefinition *pItemDef = m_mapItems[ i ];
6804 if ( !pItemDef )
6805 continue;
6806
6807 KeyValues* pItemKV = pItemDef->GetRawDefinition();
6808 if ( !pItemKV )
6809 continue;
6810
6811 KeyValues *pBundleDataKV = pItemKV->FindKey( "bundle" );
6812 if ( pBundleDataKV )
6813 {
6814 pItemDef->m_BundleInfo = new bundleinfo_t();
6815 FOR_EACH_SUBKEY( pBundleDataKV, pKVCurItem )
6816 {
6817 item_list_entry_t entry;
6818 bool bEntrySuccess = entry.InitFromName( pKVCurItem->GetName() );
6819
6820 SCHEMA_INIT_CHECK(
6821 bEntrySuccess,
6822 CFmtStr( "Bundle %s: Item definition \"%s\" failed to initialize\n", pItemDef->m_pszDefinitionName, pKVCurItem->GetName() ) );
6823
6824 const CEconItemDefinition *pBundleItemDef = GetItemDefinition( entry.m_nItemDef );
6825 SCHEMA_INIT_CHECK( pBundleItemDef, CFmtStr( "Unable to find item definition '%s' for bundle '%s'.", pKVCurItem->GetName(), pItemDef->m_pszDefinitionName ) );
6826
6827 pItemDef->m_BundleInfo->vecItemEntries.AddToTail( entry );
6828 }
6829
6830 // 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.
6831 pItemDef->m_bIsPackBundle = pItemKV->GetInt( "is_pack_bundle", 0 ) != 0;
6832 }
6833
6834 // Make a list of all of our bundles.
6835 if ( pItemDef->IsBundle() )
6836 {
6837 // Cache off the item def for the bundle, since we'll need both the bundle info and the item def index later.
6838 m_vecBundles.AddToTail( pItemDef );
6839
6840 // If the bundle is a pack bundle, mark all the contained items as pack items / link to the owning pack bundle
6841 if ( pItemDef->IsPackBundle() )
6842 {
6843 const bundleinfo_t *pBundleInfo = pItemDef->GetBundleInfo();
6844 FOR_EACH_VEC( pBundleInfo->vecItemEntries, iCurItem )
6845 {
6846 CEconItemDefinition *pCurItemDef = GetItemDefinitionMutable( pBundleInfo->vecItemEntries[ iCurItem ].m_nItemDef );
6847 SCHEMA_INIT_CHECK( NULL == pCurItemDef->m_pOwningPackBundle, CFmtStr( "Pack item \"%s\" included in more than one pack bundle - not allowed!", pCurItemDef->GetDefinitionName() ) );
6848 pCurItemDef->m_pOwningPackBundle = pItemDef;
6849 }
6850 }
6851 }
6852 }
6853
6854 // 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.
6855 FOR_EACH_VEC( m_vecBundles, iBundle )
6856 {
6857 const CEconItemDefinition *pBundleItemDef = m_vecBundles[ iBundle ];
6858 if ( pBundleItemDef->IsPackBundle() )
6859 continue;
6860
6861 // Go through all items in the bundle and look for pack items
6862 const bundleinfo_t *pBundle = pBundleItemDef->GetBundleInfo();
6863 if ( pBundle )
6864 {
6865 FOR_EACH_VEC( pBundle->vecItemEntries, iContainedBundleItem )
6866 {
6867 // Get the associated pack bundle
6868 const CEconItemDefinition *pContainedBundleItemDef = GetItemDefinition( pBundle->vecItemEntries[ iContainedBundleItem ].m_nItemDef );
6869
6870 // Ignore non-pack items
6871 if ( !pContainedBundleItemDef || !pContainedBundleItemDef->IsPackItem() )
6872 continue;
6873
6874 // Get the pack bundle that contains this particular pack item
6875 const CEconItemDefinition *pOwningPackBundleItemDef = pContainedBundleItemDef->GetOwningPackBundle();
6876
6877 // Make sure all items in the owning pack bundle are in pBundleItemDef's bundle info (pBundle)
6878 const bundleinfo_t *pOwningPackBundle = pOwningPackBundleItemDef->GetBundleInfo();
6879 FOR_EACH_VEC( pOwningPackBundle->vecItemEntries, iCurPackBundleItem )
6880 {
6881 bool bFound = false;
6882 FOR_EACH_VEC( pBundle->vecItemEntries, i )
6883 {
6884 if ( pOwningPackBundle->vecItemEntries[ iCurPackBundleItem ].m_nItemDef == pBundle->vecItemEntries[ i ].m_nItemDef &&
6885 pOwningPackBundle->vecItemEntries[ iCurPackBundleItem ].m_nPaintKit == pBundle->vecItemEntries[ i ].m_nPaintKit )
6886 {
6887 bFound = true;
6888 break;
6889 }
6890 }
6891
6892
6893 if ( !bFound )
6894 {
6895 SCHEMA_INIT_CHECK(
6896 false,
6897 CFmtStr( "The bundle \"%s\" contains some, but not all pack items required specified by pack bundle \"%s.\"",
6898 pBundleItemDef->GetDefinitionName(),
6899 pOwningPackBundleItemDef->GetDefinitionName()
6900 )
6901 );
6902 }
6903 }
6904 }
6905 }
6906 }
6907
6908 return true;
6909}
6910
6911//-----------------------------------------------------------------------------
6912// Purpose: Initializes the items section of the schema
6913// Input: pKVItems - The items section of the KeyValues
6914// representation of the schema
6915// pVecErrors - An optional vector that will contain error messages if
6916// the init fails.
6917// Output: True if initialization succeeded, false otherwise
6918//-----------------------------------------------------------------------------
6919bool CEconItemSchema::BInitItems( KeyValues *pKVItems, CUtlVector<CUtlString> *pVecErrors )
6920{
6921 FOR_EACH_TRUE_SUBKEY( pKVItems, pKVItem )
6922 {
6923 if ( Q_stricmp( pKVItem->GetName(), "default" ) == 0 )
6924 {
6925#if defined(CLIENT_DLL) || defined(GAME_DLL)
6926 SCHEMA_INIT_CHECK(
6927 m_pDefaultItemDefinition == NULL,
6928 CFmtStr( "Duplicate 'default' item definition." ) );
6929
6930 if ( m_pDefaultItemDefinition == NULL )
6931 {
6932 m_pDefaultItemDefinition = CreateEconItemDefinition();
6933 }
6934 SCHEMA_INIT_SUBSTEP( m_pDefaultItemDefinition->BInitFromKV( pKVItem, *this, pVecErrors ) );
6935#endif
6936 }
6937 else
6938 {
6939 int nItemIndex = Q_atoi( pKVItem->GetName() );
6940 int nMapIndex = m_mapItems.Find( nItemIndex );
6941
6942 // Make sure the item index is correct because we use this index as a reference
6943 SCHEMA_INIT_CHECK(
6944 !m_mapItems.IsValidIndex( nMapIndex ),
6945 CFmtStr( "Duplicate item definition (%d)", nItemIndex ) );
6946
6947 // Check to make sure the index is positive
6948 SCHEMA_INIT_CHECK(
6949 nItemIndex >= 0,
6950 CFmtStr( "Item definition index %d must be greater than or equal to zero", nItemIndex ) );
6951
6952 CEconItemDefinition *pItemDef = CreateEconItemDefinition();
6953 nMapIndex = m_mapItems.Insert( nItemIndex, pItemDef );
6954 m_mapItemsSorted.Insert( nItemIndex, pItemDef );
6955 SCHEMA_INIT_SUBSTEP( m_mapItems[nMapIndex]->BInitFromKV( pKVItem, *this, pVecErrors ) );
6956 }
6957 }
6958
6959 return SCHEMA_INIT_SUCCESS();
6960}
6961
6962bool CEconItemSchema::BInitItemMappings( CUtlVector<CUtlString> *pVecErrors )
6963{
6964 // Check the integrity of the item definitions
6965 CUtlRBTree<const char *> rbItemNames( CaselessStringLessThan );
6966 rbItemNames.EnsureCapacity( m_mapItems.Count() );
6967 FOR_EACH_MAP_FAST( m_mapItems, i )
6968 {
6969 CEconItemDefinition *pItemDef = m_mapItems[ i ];
6970
6971 // Check for duplicate item definition names
6972 int iIndex = rbItemNames.Find( pItemDef->GetDefinitionName() );
6973 SCHEMA_INIT_CHECK(
6974 !rbItemNames.IsValidIndex( iIndex ),
6975 CFmtStr( "Item definition %s: Duplicate name on index %d", pItemDef->GetDefinitionName(), m_mapItems.Key( i ) ) );
6976 if( !rbItemNames.IsValidIndex( iIndex ) )
6977 rbItemNames.Insert( m_mapItems[i]->GetDefinitionName() );
6978
6979 // Link up armory and store mappings for the item
6980 SCHEMA_INIT_SUBSTEP( pItemDef->BInitItemMappings( *this, pVecErrors ) );
6981 }
6982
6983 return SCHEMA_INIT_SUCCESS();
6984}
6985
6986//-----------------------------------------------------------------------------
6987// Purpose: Delete an item definition. Moderately dangerous as cached references will become bad.
6988// Intended for use by the item editor.
6989//-----------------------------------------------------------------------------
6990bool CEconItemSchema::DeleteItemDefinition( int iDefIndex )
6991{
6992 m_mapItemsSorted.Remove( iDefIndex );
6993
6994 int nMapIndex = m_mapItems.Find( iDefIndex );
6995 if ( m_mapItems.IsValidIndex( nMapIndex ) )
6996 {
6997 CEconItemDefinition* pItemDef = m_mapItems[nMapIndex];
6998 if ( pItemDef )
6999 {
7000 m_mapItems.RemoveAt( nMapIndex );
7001 delete pItemDef;
7002 return true;
7003 }
7004 }
7005 return false;
7006}
7007
7008//-----------------------------------------------------------------------------
7009// Purpose:
7010//-----------------------------------------------------------------------------
7011const CEconItemSetDefinition* CEconItemSchema::GetItemSet( const char* pSetName, int *piIndex ) const
7012{
7013 for ( unsigned int i=0; i<m_mapItemSets.Count(); ++i )
7014 {
7015 if ( !Q_strcmp( m_mapItemSets.Key(i), pSetName ) )
7016 {
7017 if ( piIndex )
7018 {
7019 *piIndex = i;
7020 }
7021 return (CEconItemSetDefinition*) &(m_mapItemSets[i]);
7022 }
7023 }
7024
7025 return NULL;
7026}
7027
7028const IEconItemSetDefinition* CEconItemSchema::GetItemSet( int iIndex ) const
7029{
7030 if ( m_mapItemSets.IsValidIndex( iIndex ) )
7031 return &m_mapItemSets[iIndex];
7032 else
7033 return NULL;
7034}
7035
7036//-----------------------------------------------------------------------------
7037// Purpose: Parses the Item Sets section.
7038//-----------------------------------------------------------------------------
7039bool CEconItemSchema::BInitItemSets( KeyValues *pKVItemSets, CUtlVector<CUtlString> *pVecErrors )
7040{
7041 FOR_EACH_TRUE_SUBKEY( pKVItemSets, pKVItemSet )
7042 {
7043 const char* setName = pKVItemSet->GetName();
7044
7045 SCHEMA_INIT_CHECK( setName != NULL, CFmtStr( "All itemsets must have names.") );
7046
7047 if ( m_mapItemSets.Count() > 0 )
7048 {
7049 SCHEMA_INIT_CHECK( GetItemSet( setName ) == NULL, CFmtStr( "Duplicate itemset name (%s) found!", setName ) );
7050 }
7051
7052 int idx = m_mapItemSets.Insert( setName );
7053 SCHEMA_INIT_SUBSTEP( m_mapItemSets[idx].BInitFromKV( pKVItemSet, *this, pVecErrors ) );
7054
7055 FOR_EACH_VEC( m_mapItemSets[idx].m_ItemEntries, nEntry )
7056 {
7057 item_list_entry_t &itemListEntry = m_mapItemSets[idx].m_ItemEntries[ nEntry ];
7058 CEconItemDefinition *pItemDef = GetItemDefinitionMutable( itemListEntry.m_nItemDef );
7059 if ( pItemDef )
7060 {
7061 pItemDef->AddItemSet( idx );
7062 }
7063 }
7064 }
7065
7066 return SCHEMA_INIT_SUCCESS();
7067}
7068
7069//-----------------------------------------------------------------------------
7070// Purpose: Initializes the timed rewards section of the schema
7071// Input: pKVTimedRewards - The timed_rewards section of the KeyValues
7072// representation of the schema
7073// pVecErrors - An optional vector that will contain error messages if
7074// the init fails.
7075// Output: True if initialization succeeded, false otherwise
7076//-----------------------------------------------------------------------------
7077bool CEconItemSchema::BInitTimedRewards( KeyValues *pKVTimedRewards, CUtlVector<CUtlString> *pVecErrors )
7078{
7079 m_vecTimedRewards.Purge();
7080
7081 // initialize the rewards sections
7082 if ( NULL != pKVTimedRewards )
7083 {
7084 FOR_EACH_TRUE_SUBKEY( pKVTimedRewards, pKVTimedReward )
7085 {
7086 int index = m_vecTimedRewards.AddToTail();
7087 SCHEMA_INIT_SUBSTEP( m_vecTimedRewards[index].BInitFromKV( pKVTimedReward, *this, pVecErrors ) );
7088 }
7089 }
7090
7091 return SCHEMA_INIT_SUCCESS();
7092}
7093
7094//-----------------------------------------------------------------------------
7095// Purpose:
7096//-----------------------------------------------------------------------------
7097const CTimedItemRewardDefinition* CEconItemSchema::GetTimedReward( eTimedRewardType type ) const
7098{
7099 if ( (int)type < m_vecTimedRewards.Count() )
7100 {
7101 return &m_vecTimedRewards[type];
7102 }
7103 return NULL;
7104}
7105
7106//-----------------------------------------------------------------------------
7107// Purpose:
7108//-----------------------------------------------------------------------------
7109const CEconLootListDefinition* CEconItemSchema::GetLootListByName( const char* pListName, int *out_piIndex ) const
7110{
7111 int iIdx = m_dictLootLists.Find( pListName );
7112 if ( out_piIndex )
7113 *out_piIndex = iIdx;
7114 if ( m_dictLootLists.IsValidIndex( iIdx ) )
7115 return &m_dictLootLists[iIdx];
7116 else
7117 return NULL;
7118}
7119
7120//-----------------------------------------------------------------------------
7121// Purpose: Initializes the loot lists section of the schema
7122//-----------------------------------------------------------------------------
7123bool CEconItemSchema::BInitLootLists( KeyValues *pKVLootLists, KeyValues *pKVRandomAttributeTemplates, CUtlVector<CUtlString> *pVecErrors, bool bServerLists )
7124{
7125 FOR_EACH_TRUE_SUBKEY( pKVLootLists, pKVLootList )
7126 {
7127 const char* listName = pKVLootList->GetName();
7128
7129 SCHEMA_INIT_CHECK( listName != NULL, CFmtStr( "All lootlists must have names.") );
7130
7131 if ( m_dictLootLists.Count() > 0 )
7132 {
7133 SCHEMA_INIT_CHECK( GetLootListByName( listName ) == NULL, CFmtStr( "Duplicate lootlist name (%s) found!", listName ) );
7134 }
7135
7136 int idx = m_dictLootLists.Insert( listName );
7137 SCHEMA_INIT_SUBSTEP( m_dictLootLists[idx].BInitFromKV( pKVLootList, pKVRandomAttributeTemplates, *this, pVecErrors, bServerLists ) );
7138 }
7139
7140 return SCHEMA_INIT_SUCCESS();
7141}
7142
7143//-----------------------------------------------------------------------------
7144// Purpose: Initializes the revolving loot lists section of the schema
7145//-----------------------------------------------------------------------------
7146bool CEconItemSchema::BInitRevolvingLootLists( KeyValues *pKVLootLists, CUtlVector<CUtlString> *pVecErrors )
7147{
7148 FOR_EACH_SUBKEY( pKVLootLists, pKVList )
7149 {
7150 int iListIdx = atoi( pKVList->GetName() );
7151 const char* strListName = pKVList->GetString();
7152 m_mapRevolvingLootLists.Insert( iListIdx, strListName );
7153 }
7154
7155 return SCHEMA_INIT_SUCCESS();
7156}
7157
7158
7159//-----------------------------------------------------------------------------
7160// Purpose: Initializes the quest reward loot lists section of the schema
7161//-----------------------------------------------------------------------------
7162bool CEconItemSchema::BInitQuestRewardLootLists( KeyValues *pKVLootLists, CUtlVector<CUtlString> *pVecErrors )
7163{
7164 m_mapQuestRewardLootLists.Purge();
7165
7166 if ( NULL != pKVLootLists )
7167 {
7168 FOR_EACH_SUBKEY( pKVLootLists, pKVList )
7169 {
7170 int iListIdx = atoi( pKVList->GetName() );
7171 const char* strListName = pKVList->GetString();
7172 m_mapQuestRewardLootLists.Insert( iListIdx, strListName );
7173 }
7174 }
7175
7176 return SCHEMA_INIT_SUCCESS();
7177}
7178
7179
7180//-----------------------------------------------------------------------------
7181// Alternate Icons
7182//-----------------------------------------------------------------------------
7183bool CEconItemSchema::BInitAlternateIcons( KeyValues *pKVAlternateIcons, CUtlVector<CUtlString> *pVecErrors )
7184{
7185 m_mapAlternateIcons.Purge();
7186
7187 FOR_EACH_TRUE_SUBKEY( pKVAlternateIcons, pKVBlock )
7188 {
7189 bool bSprayTints = !V_stricmp( pKVBlock->GetName(), "spray_tint_icons" );
7190
7191 FOR_EACH_TRUE_SUBKEY( pKVBlock, pKVAlternateIcon )
7192 {
7193 if ( bSprayTints )
7194 {
7195 // Custom readable syntax for spray tints:
7196 uint32 unSprayKitID = 0, unTintID1 = 0, unTintID2 = 0;
7197 if ( 3 == sscanf( pKVAlternateIcon->GetName(), "%u:%u:%u", &unSprayKitID, &unTintID1, &unTintID2 ) )
7198 {
7199 CFmtStr fmtValueLookup( "icon_path_range:%u:%u", unTintID1, unTintID2 );
7200 char const *szIconPath = pKVAlternateIcon->GetString( fmtValueLookup );
7201 if ( szIconPath && *szIconPath )
7202 {
7203 for ( uint32 unTintID = unTintID1; unTintID <= unTintID2; ++unTintID )
7204 {
7205 uint64 ullAltIconKey = Helper_GetAlternateIconKeyForTintedStickerItem( unSprayKitID, unTintID );
7206 BInitAlternateIcon( ullAltIconKey, CFmtStr( "%s_%u", szIconPath, unTintID ), pKVAlternateIcon, pVecErrors );
7207 }
7208 }
7209 else
7210 {
7211 SCHEMA_INIT_CHECK( false,
7212 CFmtStr( "Alternate icon '%s' > '%s' must define '%s'", pKVBlock->GetName(), pKVAlternateIcon->GetName(), fmtValueLookup.Get() ) );
7213 }
7214 }
7215 }
7216 else
7217 {
7218 uint64 ullAltIconKey = V_atoui64( pKVAlternateIcon->GetName() );
7219 char const *szIconPath = pKVAlternateIcon->GetString( "icon_path" );
7220 if ( szIconPath && *szIconPath )
7221 {
7222 BInitAlternateIcon( ullAltIconKey, szIconPath, pKVAlternateIcon, pVecErrors );
7223 }
7224 else
7225 {
7226 SCHEMA_INIT_CHECK( false,
7227 CFmtStr( "Alternate icon '%s' > '%s' must define '%s'", pKVBlock->GetName(), pKVAlternateIcon->GetName(), "icon_path" ) );
7228 }
7229 }
7230 }
7231 }
7232
7233 return SCHEMA_INIT_SUCCESS();
7234}
7235
7236bool CEconItemSchema::BInitAlternateIcon( uint64 ullAltIconKey, char const *szSimpleName, KeyValues *pKVAlternateIcon, CUtlVector<CUtlString> *pVecErrors )
7237{
7238 SCHEMA_INIT_CHECK(
7239 !m_mapAlternateIcons.IsValidIndex( m_mapAlternateIcons.Find( ullAltIconKey ) ),
7240 CFmtStr( "Duplicate alternate icon definition '%s' (%llu)", pKVAlternateIcon->GetName(), ullAltIconKey ) );
7241
7242 SCHEMA_INIT_CHECK(
7243 int64( ullAltIconKey ) > 0,
7244 CFmtStr( "Alternate icon definition index '%s' (%llu) must be greater than zero", pKVAlternateIcon->GetName(), ullAltIconKey ) );
7245
7246 int nNew = m_mapAlternateIcons.Insert( ullAltIconKey );
7247 m_mapAlternateIcons[ nNew ].sSimpleName = szSimpleName;
7248 m_mapAlternateIcons[ nNew ].sLargeSimpleName.Format( "%s_large", szSimpleName );
7249
7250 return true;
7251}
7252
7253#ifdef CLIENT_DLL
7254//-----------------------------------------------------------------------------
7255// Web Resources
7256//-----------------------------------------------------------------------------
7257bool CEconItemSchema::BInitWebResources( KeyValues *pKVWebResources, CUtlVector<CUtlString> *pVecErrors )
7258{
7259 if ( CWebResource::s_Initialized || !pKVWebResources )
7260 return true;
7261
7262 FOR_EACH_SUBKEY( pKVWebResources, pDef )
7263 {
7264 CWebResource* pWebResource = new CWebResource;
7265 pWebResource->m_strName = pDef->GetString( "name" );
7266 pWebResource->m_strURL = pDef->GetString( "url" );
7267 pWebResource->m_bOnDemand = pDef->GetBool( "on_demand" );
7268 pWebResource->m_pKeyValues = NULL;
7269 pWebResource->m_fnLoadCallback = NULL;
7270 m_dictWebResources.Insert( pDef->GetString( "name" ), pWebResource );
7271 }
7272
7273 if ( CommandLine()->CheckParm( "-enabledownloadtest" ) )
7274 {
7275 CWebResource* pWebResource = new CWebResource;
7276 pWebResource->m_strName = "Test";
7277 pWebResource->m_strURL = "http://media.steampowered.com/apps/570/test/test.txt";
7278 pWebResource->m_bOnDemand = true;
7279 pWebResource->m_pKeyValues = NULL;
7280 pWebResource->m_fnLoadCallback = NULL;
7281 m_dictWebResources.Insert( "Test", pWebResource );
7282 }
7283
7284 // If any of our resources need to be requested immediately, do it now.
7285 FOR_EACH_DICT_FAST( m_dictWebResources, idx )
7286 {
7287 CWebResource* pWebResource = m_dictWebResources[idx];
7288 if ( !pWebResource )
7289 continue;
7290
7291 if ( !pWebResource->m_bOnDemand )
7292 {
7293 LoadWebResource( pWebResource->m_strName, NULL );
7294 }
7295 }
7296
7297 CWebResource::s_Initialized = true;
7298
7299 return SCHEMA_INIT_SUCCESS();
7300}
7301
7302EWebResourceStatus CEconItemSchema::LoadWebResource( CUtlString strName, void (*fnCallback)( const char*, KeyValues* ), bool bForceReload )
7303{
7304 return kWebResource_NotLoaded;
7305}
7306
7307void CEconItemSchema::SetWebResource( CUtlString strName, KeyValues* pResourceKV )
7308{
7309 int idx = m_dictWebResources.Find( strName.Get() );
7310 if ( !m_dictWebResources.IsValidIndex( idx ) )
7311 return;
7312
7313 CWebResource* pResource = m_dictWebResources[idx];
7314 if ( pResource->m_pKeyValues )
7315 {
7316 pResource->m_pKeyValues->deleteThis();
7317 pResource->m_pKeyValues = NULL;
7318 }
7319 pResource->m_pKeyValues = pResourceKV;
7320 if ( pResource->m_fnLoadCallback )
7321 pResource->m_fnLoadCallback( strName.Get(), pResourceKV );
7322}
7323
7324#endif
7325
7326bool CEconItemSchema::BInitProPlayers( KeyValues *pKVData, CUtlVector<CUtlString> *pVecErrors )
7327{
7328 FOR_EACH_SUBKEY( pKVData, pDef )
7329 {
7330 // Create the pro player entry
7331 CProPlayerData *pData = new CProPlayerData;
7332 if ( !pData->BInitFromKeyValues( pDef, pVecErrors ) )
7333 {
7334 delete pData;
7335 continue;
7336 }
7337
7338 if ( m_mapProPlayersByAccountID.Find( pData->GetAccountID() ) != m_mapProPlayersByAccountID.InvalidIndex() )
7339 {
7340 SCHEMA_INIT_CHECK( false,
7341 CFmtStr( "Pro-player entry '%s' maps to a duplicate AccountID", pDef->GetName() ) );
7342 Assert( false );
7343 delete pData;
7344 continue;
7345 }
7346
7347 if ( m_mapProPlayersByCode.Find( pData->GetCode() ) != UTL_INVAL_SYMBOL )
7348 {
7349 SCHEMA_INIT_CHECK( false,
7350 CFmtStr( "Pro-player entry '%s' has a duplicate code '%s'", pDef->GetName(), pData->GetCode() ) );
7351 Assert( false );
7352 delete pData;
7353 continue;
7354 }
7355
7356 m_mapProPlayersByAccountID.InsertOrReplace( pData->GetAccountID(), pData );
7357 FOR_EACH_TRUE_SUBKEY( pDef->FindKey( "events" ), kvEvent )
7358 {
7359 int nEventID = Q_atoi( kvEvent->GetName() );
7360 int nTeamID = kvEvent->GetInt( "team" );
7361 if ( nEventID && nTeamID )
7362 {
7363 uint64 uiKey = ( uint64( uint32( nEventID ) ) << 32 ) | uint32( nTeamID );
7364 MapProPlayersByEventIDTeamID_t::IndexType_t idx = m_mapProPlayersByEventIDTeamID.Find( uiKey );
7365 if ( idx == m_mapProPlayersByEventIDTeamID.InvalidIndex() )
7366 idx = m_mapProPlayersByEventIDTeamID.InsertOrReplace( uiKey, new CUtlVector< const CProPlayerData * > );
7367 m_mapProPlayersByEventIDTeamID.Element( idx )->AddToTail( pData );
7368 }
7369 }
7370 }
7371
7372 return SCHEMA_INIT_SUCCESS();
7373}
7374
7375const CProPlayerData * CEconItemSchema::GetProPlayerDataByAccountID( uint32 unAccountID ) const
7376{
7377 MapProPlayersByAccountID_t::IndexType_t idx = m_mapProPlayersByAccountID.Find( unAccountID );
7378 return ( idx == m_mapProPlayersByAccountID.InvalidIndex() ) ? NULL : m_mapProPlayersByAccountID.Element( idx );
7379}
7380
7381const CUtlVector< const CProPlayerData * > * CEconItemSchema::GetProPlayersDataForEventIDTeamID( int nEventID, int nTeamID ) const
7382{
7383 uint64 uiKey = ( uint64( uint32( nEventID ) ) << 32 ) | uint32( nTeamID );
7384 MapProPlayersByEventIDTeamID_t::IndexType_t idx = m_mapProPlayersByEventIDTeamID.Find( uiKey );
7385 return ( idx != m_mapProPlayersByEventIDTeamID.InvalidIndex() )
7386 ? m_mapProPlayersByEventIDTeamID.Element( idx ) : NULL;
7387}
7388
7389bool CProPlayerData::BInitFromKeyValues( KeyValues *pDef, CUtlVector<CUtlString> *pVecErrors /* = NULL */ )
7390{
7391 uint32 unAccountID = Q_atoi( pDef->GetName() );
7392 SCHEMA_INIT_CHECK(
7393 unAccountID != 0,
7394 CFmtStr( "Pro-player entry '%s' doesn't have a valid AccountID", pDef->GetName() ) );
7395 if ( !unAccountID )
7396 return false;
7397 m_nAccountID = unAccountID;
7398
7399 char const *szName = pDef->GetString( "name" );
7400 if ( !szName || !*szName )
7401 {
7402 SCHEMA_INIT_CHECK( false,
7403 CFmtStr( "Pro-player entry '%s' has no name", pDef->GetName() ) );
7404 Assert( false );
7405 return false;
7406 }
7407 m_sName = szName;
7408
7409 char const *szCode = pDef->GetString( "code" );
7410 if ( !szCode || !*szCode )
7411 {
7412 SCHEMA_INIT_CHECK( false,
7413 CFmtStr( "Pro-player entry '%s' has no code", pDef->GetName() ) );
7414 Assert( false );
7415 return false;
7416 }
7417 HelperValidateLocalizationStringToken( CFmtStr( "#SFUI_ProPlayer_%s", szCode ) );
7418 m_sCode = szCode;
7419
7420 char const *szGeo = pDef->GetString( "geo" );
7421 SCHEMA_INIT_CHECK( szGeo && *szGeo,
7422 CFmtStr( "Pro-player entry '%s' has no geo defined", pDef->GetName() ) );
7423 Assert( szGeo && *szGeo );
7424 HelperValidateLocalizationStringToken( CFmtStr( "#SFUI_Country_%s", szGeo ) );
7425 m_sGeo = szGeo;
7426
7427 m_pKVItem = pDef->MakeCopy();
7428
7429 return true; // typically it should be return SCHEMA_INIT_SUCCESS(); but we don't want spurious "doesn't have a matching pro-player entry" errors
7430}
7431
7432//-----------------------------------------------------------------------------
7433// Purpose: Initializes the recipes section of the schema
7434// Input: pKVRecipes - The recipes section of the KeyValues
7435// representation of the schema
7436// pVecErrors - An optional vector that will contain error messages if
7437// the init fails.
7438// Output: True if initialization succeeded, false otherwise
7439//-----------------------------------------------------------------------------
7440bool CEconItemSchema::BInitRecipes( KeyValues *pKVRecipes, CUtlVector<CUtlString> *pVecErrors )
7441{
7442 m_mapRecipes.Purge();
7443
7444 // initialize the rewards sections
7445 if ( NULL != pKVRecipes )
7446 {
7447 int nHighestRecipeIndex = 0;
7448 FOR_EACH_TRUE_SUBKEY( pKVRecipes, pKVRecipe )
7449 {
7450 int nRecipeIndex = Q_atoi( pKVRecipe->GetName() );
7451
7452 // Remember highest recipe index so we can make more past that
7453 if ( nHighestRecipeIndex < nRecipeIndex + 1 )
7454 {
7455 nHighestRecipeIndex = nRecipeIndex + 1;
7456 }
7457
7458 int nMapIndex = m_mapRecipes.Find( nRecipeIndex );
7459
7460 // Make sure the recipe index is correct because we use this index as a reference
7461 SCHEMA_INIT_CHECK(
7462 !m_mapRecipes.IsValidIndex( nMapIndex ),
7463 CFmtStr( "Duplicate recipe definition (%d)", nRecipeIndex ) );
7464
7465 // Check to make sure the index is positive
7466 SCHEMA_INIT_CHECK(
7467 nRecipeIndex >= 0,
7468 CFmtStr( "Recipe definition index %d must be greater than or equal to zero", nRecipeIndex ) );
7469
7470 CEconCraftingRecipeDefinition *recipeDef = CreateCraftingRecipeDefinition();
7471 SCHEMA_INIT_SUBSTEP( recipeDef->BInitFromKV( pKVRecipe, *this, pVecErrors ) );
7472
7473 // On clients, toss out any recipes that aren't always known. (Really, the clients
7474 // shouldn't ever get these, but just in case.)
7475#if defined(CLIENT_DLL) || defined(GAME_DLL)
7476 if ( recipeDef->IsAlwaysKnown() )
7477#endif // !GC_DLL
7478 {
7479#ifdef _DEBUG
7480 // Sanity check in debug builds so that we know we aren't putting the same recipe in
7481 // multiple times.
7482 FOR_EACH_MAP_FAST( m_mapRecipes, i )
7483 {
7484 Assert( i != nRecipeIndex );
7485 Assert( m_mapRecipes[i] != recipeDef );
7486 }
7487#endif // _DEBUG
7488
7489 // Store this recipe.
7490 m_mapRecipes.Insert( nRecipeIndex, recipeDef );
7491 }
7492 }
7493
7494#ifdef CSTRIKE15
7495 int nSetCount = GetItemSetCount();
7496 for ( int i = 0; i < nSetCount; ++i )
7497 {
7498 const IEconItemSetDefinition *pSet = GetItemSet( i );
7499 if ( !pSet || pSet->GetCraftReward() <= 0 )
7500 continue;
7501
7502 CEconCraftingRecipeDefinition *recipeDef = CreateCraftingRecipeDefinition();
7503 SCHEMA_INIT_SUBSTEP( recipeDef->BInitFromSet( pSet, *this, pVecErrors ) );
7504 recipeDef->SetDefinitionIndex( nHighestRecipeIndex );
7505 recipeDef->SetFilter( CRAFT_FILTER_COLLECT );
7506
7507 // Disabled for now until we ship collection badges!
7508 recipeDef->SetDisabled( true );
7509
7510 m_mapRecipes.Insert( nHighestRecipeIndex, recipeDef );
7511
7512 nHighestRecipeIndex++;
7513 }
7514#endif //#ifdef CSTRIKE15
7515 }
7516
7517 return SCHEMA_INIT_SUCCESS();
7518}
7519
7520
7521//-----------------------------------------------------------------------------
7522// Purpose: Builds the name of a achievement in the form App<ID>.<AchName>
7523// Input: unAppID - native app ID
7524// pchNativeAchievementName - name of the achievement in its native app
7525// Returns: The combined achievement name
7526//-----------------------------------------------------------------------------
7527CUtlString CEconItemSchema::ComputeAchievementName( AppId_t unAppID, const char *pchNativeAchievementName )
7528{
7529 return CFmtStr1024( "App%u.%s", unAppID, pchNativeAchievementName ).Access();
7530}
7531
7532
7533//-----------------------------------------------------------------------------
7534// Purpose: Initializes the achievement rewards section of the schema
7535// Input: pKVAchievementRewards - The achievement_rewards section of the KeyValues
7536// representation of the schema
7537// pVecErrors - An optional vector that will contain error messages if
7538// the init fails.
7539// Output: True if initialization succeeded, false otherwise
7540//-----------------------------------------------------------------------------
7541bool CEconItemSchema::BInitAchievementRewards( KeyValues *pKVAchievementRewards, CUtlVector<CUtlString> *pVecErrors )
7542{
7543 m_dictAchievementRewards.Purge();
7544 m_mapAchievementRewardsByData.PurgeAndDeleteElements();
7545
7546 // initialize the rewards sections
7547 if ( NULL != pKVAchievementRewards )
7548 {
7549 FOR_EACH_SUBKEY( pKVAchievementRewards, pKVReward )
7550 {
7551 AchievementAward_t award;
7552 if( pKVReward->GetDataType() == KeyValues::TYPE_NONE )
7553 {
7554 int32 nItemIndex = pKVReward->GetInt( "DefIndex", -1 );
7555 if( nItemIndex != -1 )
7556 {
7557 award.m_vecDefIndex.AddToTail( (uint16)nItemIndex );
7558 }
7559 else
7560 {
7561 KeyValues *pkvItems = pKVReward->FindKey( "Items" );
7562 SCHEMA_INIT_CHECK(
7563 pkvItems != NULL,
7564 CFmtStr( "Complex achievement %s must have an Items key or a DefIndex field", pKVReward->GetName() ) );
7565 if( !pkvItems )
7566 {
7567 continue;
7568 }
7569
7570 FOR_EACH_VALUE( pkvItems, pkvItem )
7571 {
7572 award.m_vecDefIndex.AddToTail( (uint16)Q_atoi( pkvItem->GetName() ) );
7573 }
7574 }
7575
7576 }
7577 else
7578 {
7579 award.m_vecDefIndex.AddToTail( (uint16)pKVReward->GetInt("", -1 ) );
7580 }
7581
7582 // make sure all the item types are valid
7583 bool bFoundAllItems = true;
7584 FOR_EACH_VEC( award.m_vecDefIndex, nItem )
7585 {
7586 const CEconItemDefinition *pDefn = GetItemDefinition( award.m_vecDefIndex[nItem] );
7587 SCHEMA_INIT_CHECK(
7588 pDefn != NULL,
7589 CFmtStr( "Item definition index %d in achievement reward %s was not found", award.m_vecDefIndex[nItem], pKVReward->GetName() ) );
7590 if( !pDefn )
7591 {
7592 bFoundAllItems = false;
7593 }
7594 }
7595 if( !bFoundAllItems )
7596 continue;
7597
7598 SCHEMA_INIT_CHECK(
7599 award.m_vecDefIndex.Count() > 0,
7600 CFmtStr( "Achievement reward %s has no items!", pKVReward->GetName() ) );
7601 if( award.m_vecDefIndex.Count() == 0 )
7602 continue;
7603
7604 award.m_unSourceAppId = k_uAppIdInvalid;
7605 if( pKVReward->GetDataType() == KeyValues::TYPE_NONE )
7606 {
7607 // cross game achievement
7608 award.m_sNativeName = pKVReward->GetName();
7609 award.m_unAuditData = pKVReward->GetInt( "AuditData", 0 );
7610 award.m_unSourceAppId = pKVReward->GetInt( "SourceAppID", award.m_unSourceAppId );
7611 }
7612 else
7613 {
7614 award.m_sNativeName = pKVReward->GetName();
7615 award.m_unAuditData = 0;
7616 }
7617
7618 AchievementAward_t *pAward = new AchievementAward_t;
7619 *pAward = award;
7620
7621 m_dictAchievementRewards.Insert( ComputeAchievementName( pAward->m_unSourceAppId, pAward->m_sNativeName ), pAward );
7622 m_mapAchievementRewardsByData.Insert( pAward->m_unAuditData, pAward );
7623 }
7624 }
7625
7626 return SCHEMA_INIT_SUCCESS();
7627}
7628
7629#ifdef TF_CLIENT_DLL
7630//-----------------------------------------------------------------------------
7631// Purpose: Go through all items and cache the number of concrete items in each.
7632//-----------------------------------------------------------------------------
7633bool CEconItemSchema::BInitConcreteItemCounts( CUtlVector<CUtlString> *pVecErrors )
7634{
7635 FOR_EACH_MAP_FAST( m_mapItems, i )
7636 {
7637 CEconItemDefinition *pItemDef = m_mapItems[ i ];
7638 pItemDef->m_unNumConcreteItems = CalculateNumberOfConcreteItems( pItemDef );
7639 }
7640
7641 return true;
7642}
7643#endif
7644
7645//-----------------------------------------------------------------------------
7646// Purpose: Returns the number of actual "real" items referenced by the item definition
7647// (i.e. items that would take up space in the inventory)
7648//-----------------------------------------------------------------------------
7649int CEconItemSchema::CalculateNumberOfConcreteItems( const CEconItemDefinition *pItemDef )
7650{
7651 AssertMsg( pItemDef, "NULL item definition! This should not happen!" );
7652 if ( !pItemDef )
7653 return 0;
7654
7655 if ( pItemDef->IsBundle() )
7656 {
7657 uint32 unNumConcreteItems = 0;
7658
7659 const bundleinfo_t *pBundle = pItemDef->GetBundleInfo();
7660 Assert( pBundle );
7661
7662 FOR_EACH_VEC( pBundle->vecItemEntries, i )
7663 {
7664 unNumConcreteItems += CalculateNumberOfConcreteItems( GetItemDefinition( pBundle->vecItemEntries[i].m_nItemDef ) );
7665 }
7666
7667 return unNumConcreteItems;
7668 }
7669
7670 if ( pItemDef->GetItemClass() && !Q_strcmp( pItemDef->GetItemClass(), "map_token" ) )
7671 return 0;
7672
7673 return 1;
7674}
7675
7676
7677bool CEconItemSchema::BInitStickerKits( KeyValues *pKVStickerKits, CUtlVector<CUtlString> *pVecErrors )
7678{
7679 CStickerKit *pDefault = NULL;
7680 FOR_EACH_TRUE_SUBKEY( pKVStickerKits, pKVEntry )
7681 {
7682 const char *pchName = pKVEntry->GetString( "name", "" );
7683 if ( !pchName || pchName[ 0 ] == '\0' )
7684 continue;
7685
7686 CStickerKit *pStickerKit = new CStickerKit();
7687 if ( pStickerKit )
7688 {
7689 pStickerKit->nID = atoi( pKVEntry->GetName() );
7690
7691 if ( !pDefault )
7692 {
7693 if ( pStickerKit->nID == 0 )
7694 pDefault = pStickerKit;
7695 else
7696 pDefault = m_mapStickerKits.Element( m_mapStickerKits.Find( 0 ) );
7697 }
7698
7699 if ( !pStickerKit->InitFromKeyValues( pKVEntry, pDefault, pVecErrors ) )
7700 {
7701 SCHEMA_INIT_CHECK(
7702 false,
7703 CFmtStr( "Failed to initialize sticker kit ID %d '%s'", pStickerKit->nID, pStickerKit->sName.Get() ) );
7704 continue;
7705 }
7706
7707 if( m_mapStickerKits.Find( pStickerKit->nID ) != m_mapStickerKits.InvalidIndex() )
7708 {
7709 SCHEMA_INIT_CHECK(
7710 false,
7711 CFmtStr( "Duplicate sticker kit ID %d", pStickerKit->nID ) );
7712 continue;
7713 }
7714
7715 if( m_dictStickerKits.HasElement( pStickerKit->sName ) )
7716 {
7717 SCHEMA_INIT_CHECK(
7718 false,
7719 CFmtStr( "Duplicate sticker kit name '%s'", pStickerKit->sName.Get() ) );
7720 continue;
7721 }
7722
7723 m_mapStickerKits.Insert( pStickerKit->nID, pStickerKit );
7724 m_dictStickerKits.Insert( pStickerKit->sName.Get(), pStickerKit );
7725 }
7726 }
7727
7728 return SCHEMA_INIT_SUCCESS();
7729}
7730
7731bool CEconItemSchema::BInitStickerLists( KeyValues *pKVStickerLists, CUtlVector<CUtlString> *pVecErrors )
7732{
7733 FOR_EACH_TRUE_SUBKEY( pKVStickerLists, pKVEntry )
7734 {
7735 char const *szName = pKVEntry->GetName();
7736 if ( !szName || !*szName )
7737 continue;
7738
7739 if( m_dictStickerKits.HasElement( szName ) )
7740 {
7741 SCHEMA_INIT_CHECK(
7742 false,
7743 CFmtStr( "Sticker list name duplicating sticker kit name '%s'", szName ) );
7744 continue;
7745 }
7746
7747 if( m_dictStickerLists.HasElement( szName ) )
7748 {
7749 SCHEMA_INIT_CHECK(
7750 false,
7751 CFmtStr( "Duplicate sticker list name '%s'", szName ) );
7752 continue;
7753 }
7754
7755 CStickerList *pStickerList = new CStickerList();
7756 if ( !pStickerList->InitFromKeyValues( pKVEntry, pVecErrors ) )
7757 {
7758 SCHEMA_INIT_CHECK(
7759 false,
7760 CFmtStr( "Failed to initialize sticker list '%s'", szName ) );
7761 continue;
7762 }
7763
7764 m_dictStickerLists.Insert( szName, pStickerList );
7765 }
7766
7767 return SCHEMA_INIT_SUCCESS();
7768}
7769
7770bool CEconItemSchema::BInitPaintKits( KeyValues *pKVPaintKits, CUtlVector<CUtlString> *pVecErrors )
7771{
7772 CPaintKit *pDefault = NULL;
7773
7774 FOR_EACH_TRUE_SUBKEY( pKVPaintKits, pKVEntry )
7775 {
7776 const char *pchName = pKVEntry->GetString( "name", "" );
7777 if ( !pchName || pchName[ 0 ] == '\0' )
7778 continue;
7779
7780 CPaintKit *pPaintKit = new CPaintKit();
7781 if ( pPaintKit )
7782 {
7783 pPaintKit->nID = atoi( pKVEntry->GetName() );
7784
7785 if ( !pDefault )
7786 {
7787 if ( pPaintKit->nID == 0 )
7788 pDefault = pPaintKit;
7789 else
7790 pDefault = m_mapPaintKits.Element( m_mapPaintKits.Find( 0 ) );
7791 }
7792
7793 pPaintKit->InitFromKeyValues( pKVEntry, pDefault );
7794
7795 m_mapPaintKits.Insert( pPaintKit->nID, pPaintKit );
7796 }
7797 }
7798
7799 return SCHEMA_INIT_SUCCESS();
7800}
7801
7802bool CEconItemSchema::BInitPaintKitsRarity( KeyValues *pKVPaintKitsRarity, CUtlVector<CUtlString> *pVecErrors )
7803{
7804 CPaintKit *pDefault = const_cast< CPaintKit* >( GetPaintKitDefinitionByName( "default" ) );
7805 if ( !pDefault )
7806 {
7807 SCHEMA_INIT_CHECK(
7808 false,
7809 CFmtStr( "Unable to find \"default\" paint kit in \"paint_kits_rarity\"" ) );
7810 return false;
7811 }
7812
7813 uint8 nRarity;
7814 GetItemSchema()->BGetItemRarityFromName( pKVPaintKitsRarity->GetString( "default", "common" ), &nRarity );
7815 pDefault->nRarity = nRarity;
7816
7817 FOR_EACH_SUBKEY( pKVPaintKitsRarity, pKVEntry )
7818 {
7819 CPaintKit *pPaintKit = const_cast< CPaintKit* >( GetPaintKitDefinitionByName( pKVEntry->GetName() ) );
7820 if ( !pPaintKit )
7821 continue;
7822
7823 const char *pchRarity = pKVEntry->GetString();
7824 if ( pchRarity )
7825 {
7826 uint8 nRarity;
7827 GetItemSchema()->BGetItemRarityFromName( pchRarity, &nRarity );
7828 pPaintKit->nRarity = nRarity;
7829 }
7830 else
7831 {
7832 pPaintKit->nRarity = pDefault->nRarity;
7833 }
7834 }
7835
7836 return SCHEMA_INIT_SUCCESS();
7837}
7838
7839
7840//-----------------------------------------------------------------------------
7841// Purpose:
7842//-----------------------------------------------------------------------------
7843bool CEconItemSchema::BInitMusicDefs( KeyValues *pKVMusicDefs, CUtlVector<CUtlString> *pVecErrors )
7844{
7845 FOR_EACH_TRUE_SUBKEY( pKVMusicDefs, pKVMusicDef )
7846 {
7847 CEconMusicDefinition *pNewMusicDef = new CEconMusicDefinition;
7848
7849 SCHEMA_INIT_SUBSTEP( pNewMusicDef->BInitFromKV( pKVMusicDef, *this, pVecErrors ) );
7850
7851 if( m_mapMusicDefs.Find( pNewMusicDef->GetID() ) != m_mapMusicDefs.InvalidIndex() )
7852 {
7853 SCHEMA_INIT_CHECK(
7854 false,
7855 CFmtStr( "Duplicate music definition id %d", pNewMusicDef->GetID() ) );
7856 continue;
7857 }
7858
7859 m_mapMusicDefs.Insert( pNewMusicDef->GetID(), pNewMusicDef );
7860 }
7861
7862 return SCHEMA_INIT_SUCCESS();
7863}
7864
7865
7866//-----------------------------------------------------------------------------
7867// Purpose:
7868//-----------------------------------------------------------------------------
7869bool CEconItemSchema::BInitQuestDefs( KeyValues *pKVQuestDefs, CUtlVector<CUtlString> *pVecErrors )
7870{
7871 m_mapQuestDefs.PurgeAndDeleteElements();
7872
7873 // initialize the Quest definitions
7874 if ( NULL != pKVQuestDefs )
7875 {
7876 FOR_EACH_TRUE_SUBKEY( pKVQuestDefs, pKVQuestDef )
7877 {
7878 CEconQuestDefinition *pNewQuestDef = new CEconQuestDefinition;
7879
7880 SCHEMA_INIT_SUBSTEP( pNewQuestDef->BInitFromKV( pKVQuestDef, *this, pVecErrors ) );
7881
7882 if ( m_mapQuestDefs.Find( pNewQuestDef->GetID() ) != m_mapQuestDefs.InvalidIndex() )
7883 {
7884 SCHEMA_INIT_CHECK(
7885 false,
7886 CFmtStr( "Duplicate Quest definition id %d", pNewQuestDef->GetID() ) );
7887 continue;
7888 }
7889
7890 m_mapQuestDefs.Insert( pNewQuestDef->GetID(), pNewQuestDef );
7891 }
7892 }
7893
7894 return SCHEMA_INIT_SUCCESS();
7895}
7896
7897
7898bool CEconItemSchema::BInitQuestEvents( KeyValues *pKVQuestSchedule, CUtlVector<CUtlString> *pVecErrors )
7899{
7900 m_vecQuestEvents.Purge();
7901
7902 // Removed for partner depot
7903
7904 return SCHEMA_INIT_SUCCESS();
7905}
7906
7907const QuestEventsSchedule_t& CEconItemSchema::GetAndUpdateQuestEventsSchedule()
7908{
7909 return m_mapQuestEventsSchedule;
7910}
7911
7912//-----------------------------------------------------------------------------
7913// Purpose:
7914//-----------------------------------------------------------------------------
7915bool CEconItemSchema::BInitCampaignDefs( KeyValues *pKVCampaignDefs, CUtlVector<CUtlString> *pVecErrors )
7916{
7917 m_mapCampaignDefs.PurgeAndDeleteElements();
7918
7919 // initialize the Campaign definitions
7920 if ( NULL != pKVCampaignDefs )
7921 {
7922 FOR_EACH_TRUE_SUBKEY( pKVCampaignDefs, pKVCampaignDef )
7923 {
7924 CEconCampaignDefinition *pNewCampaignDef = new CEconCampaignDefinition;
7925
7926 SCHEMA_INIT_SUBSTEP( pNewCampaignDef->BInitFromKV( pKVCampaignDef, *this, pVecErrors ) );
7927
7928 if ( m_mapCampaignDefs.Find( pNewCampaignDef->GetID() ) != m_mapCampaignDefs.InvalidIndex() )
7929 {
7930 SCHEMA_INIT_CHECK(
7931 false,
7932 CFmtStr( "Duplicate Campaign definition id %d", pNewCampaignDef->GetID() ) );
7933 continue;
7934 }
7935
7936 m_mapCampaignDefs.Insert( pNewCampaignDef->GetID(), pNewCampaignDef );
7937 }
7938 }
7939
7940 return SCHEMA_INIT_SUCCESS();
7941}
7942
7943
7944attachedparticlesystem_t CEconItemSchema::GetAttachedParticleSystemInfo( KeyValues* pKVEntry, int32 nItemIndex ) const
7945{
7946 attachedparticlesystem_t system;
7947
7948 system.pEffectKV = pKVEntry;
7949 system.pszResourceName = pKVEntry->GetString( "resource", NULL );
7950 system.pszSystemName = pKVEntry->GetString( "system", NULL );
7951 system.pszAttachmentName = pKVEntry->GetString( "attachment", NULL );
7952 system.bFollowRootBone = pKVEntry->GetInt( "attach_to_rootbone", 0 ) != 0;
7953 system.bFlyingCourier = pKVEntry->GetInt( "flying_courier_effect", 0 ) != 0;
7954 system.iCustomType = pKVEntry->GetInt( "custom_type", 0 );
7955 system.iCount = 0;
7956 system.nSystemID = nItemIndex;
7957 system.nRootAttachType = StringFieldToInt( pKVEntry->GetString("attach_type"), g_szParticleAttachTypes, ARRAYSIZE(g_szParticleAttachTypes) );
7958 int iAttachToEnt = StringFieldToInt( pKVEntry->GetString("attach_entity"), g_szParticleAttachToEnt, ARRAYSIZE(g_szParticleAttachToEnt) );
7959 system.nAttachToEntity = ( iAttachToEnt == -1 ) ? ATTPART_TO_SELF : (attachedparticle_toent_t)iAttachToEnt;
7960
7961 // Find any control point settings
7962 KeyValues *pKVControlPoints = pKVEntry->FindKey( "control_points" );
7963 if ( pKVControlPoints )
7964 {
7965 FOR_EACH_TRUE_SUBKEY( pKVControlPoints, pKVPoint )
7966 {
7967 int iIdx = system.vecControlPoints.AddToTail();
7968 system.vecControlPoints[iIdx].nControlPoint = pKVPoint->GetInt( "control_point_index", -1 );
7969 system.vecControlPoints[iIdx].nAttachType = StringFieldToInt( pKVPoint->GetString("attach_type"), g_szParticleAttachTypes, ARRAYSIZE(g_szParticleAttachTypes) );
7970 system.vecControlPoints[iIdx].pszAttachmentName = pKVPoint->GetString( "attachment", NULL );
7971 float flVec[3];
7972 V_StringToVector( flVec, pKVPoint->GetString( "position", "0 0 0" ) );
7973 system.vecControlPoints[iIdx].vecPosition = Vector( flVec[0], flVec[1], flVec[2] );
7974 }
7975 }
7976
7977 return system;
7978}
7979
7980//-----------------------------------------------------------------------------
7981// Purpose: Initializes the attribute-controlled-particle-systems section of the schema
7982//-----------------------------------------------------------------------------
7983bool CEconItemSchema::BInitAttributeControlledParticleSystems( KeyValues *pKVParticleSystems, CUtlVector<CUtlString> *pVecErrors )
7984{
7985 m_mapAttributeControlledParticleSystems.Purge();
7986 if ( NULL != pKVParticleSystems )
7987 {
7988 FOR_EACH_TRUE_SUBKEY( pKVParticleSystems, pKVEntry )
7989 {
7990 int32 nItemIndex = atoi( pKVEntry->GetName() );
7991 // Check to make sure the index is positive
7992 SCHEMA_INIT_CHECK(
7993 nItemIndex > 0,
7994 CFmtStr( "Particle system index %d greater than zero", nItemIndex ) );
7995 if ( nItemIndex <= 0 )
7996 continue;
7997 int iIndex = m_mapAttributeControlledParticleSystems.Insert( nItemIndex );
7998 attachedparticlesystem_t *system = &m_mapAttributeControlledParticleSystems[iIndex];
7999 *system = GetAttachedParticleSystemInfo( pKVEntry, nItemIndex );
8000 }
8001 }
8002 return SCHEMA_INIT_SUCCESS();
8003}
8004
8005//-----------------------------------------------------------------------------
8006// Purpose: Inits data for items that can level up through kills, etc.
8007//-----------------------------------------------------------------------------
8008bool CEconItemSchema::BInitItemLevels( KeyValues *pKVItemLevels, CUtlVector<CUtlString> *pVecErrors )
8009{
8010 m_vecItemLevelingData.RemoveAll();
8011
8012 // initialize the rewards sections
8013 if ( NULL != pKVItemLevels )
8014 {
8015 FOR_EACH_TRUE_SUBKEY( pKVItemLevels, pKVItemLevelBlock )
8016 {
8017 const char *pszLevelBlockName = pKVItemLevelBlock->GetName();
8018 SCHEMA_INIT_CHECK( GetItemLevelingData( pszLevelBlockName ) == NULL,
8019 CFmtStr( "Duplicate leveling data block named \"%s\".", pszLevelBlockName ) );
8020
8021 // Allocate a new structure for this block and assign it. We'll fill in the contents later.
8022 CUtlVector<CItemLevelingDefinition> *pLevelingData = new CUtlVector<CItemLevelingDefinition>;
8023 m_vecItemLevelingData.Insert( pszLevelBlockName, pLevelingData );
8024
8025 FOR_EACH_TRUE_SUBKEY( pKVItemLevelBlock, pKVItemLevel )
8026 {
8027 int index = pLevelingData->AddToTail();
8028 SCHEMA_INIT_SUBSTEP( (*pLevelingData)[index].BInitFromKV( pKVItemLevel, *this, pszLevelBlockName, pVecErrors ) );
8029 }
8030 }
8031 }
8032
8033 return SCHEMA_INIT_SUCCESS();
8034}
8035
8036//-----------------------------------------------------------------------------
8037// Purpose: Inits data for kill eater types.
8038//-----------------------------------------------------------------------------
8039bool CEconItemSchema::BInitKillEaterScoreTypes( KeyValues *pKVKillEaterScoreTypes, CUtlVector<CUtlString> *pVecErrors )
8040{
8041 m_mapKillEaterScoreTypes.RemoveAll();
8042
8043 // initialize the rewards sections
8044 if ( NULL != pKVKillEaterScoreTypes )
8045 {
8046 FOR_EACH_TRUE_SUBKEY( pKVKillEaterScoreTypes, pKVScoreType )
8047 {
8048 unsigned int unIndex = (unsigned int)atoi( pKVScoreType->GetName() );
8049 SCHEMA_INIT_CHECK( m_mapKillEaterScoreTypes.Find( unIndex ) == KillEaterScoreMap_t::InvalidIndex(),
8050 CFmtStr( "Duplicate kill eater score type index %u.", unIndex ) );
8051
8052 kill_eater_score_type_t ScoreType;
8053 ScoreType.m_nValue = V_atoi( pKVScoreType->GetName() );
8054 ScoreType.m_pszTypeString = pKVScoreType->GetString( "type_name" );
8055 ScoreType.m_pszModelAttributeString = pKVScoreType->GetString( "model_attribute" );
8056#ifdef CLIENT_DLL
8057 HelperValidateLocalizationStringToken( CFmtStr( "#KillEaterDescriptionNotice_%s", ScoreType.m_pszTypeString ) );
8058 HelperValidateLocalizationStringToken( CFmtStr( "#KillEaterEventType_%s", ScoreType.m_pszTypeString ) );
8059#endif
8060
8061 // Default to on.
8062 ScoreType.m_bUseLevelBlock = pKVScoreType->GetBool( "use_level_data", 1 );
8063
8064 const char *pszLevelBlockName = pKVScoreType->GetString( "level_data", "KillEaterRank" );
8065 SCHEMA_INIT_CHECK( GetItemLevelingData( pszLevelBlockName ) != NULL,
8066 CFmtStr( "Unable to find leveling data block named \"%s\" for kill eater score type %u.", pszLevelBlockName, unIndex ) );
8067
8068 ScoreType.m_pszLevelBlockName = pszLevelBlockName;
8069
8070 m_mapKillEaterScoreTypes.Insert( unIndex, ScoreType );
8071 }
8072 }
8073
8074 return SCHEMA_INIT_SUCCESS();
8075}
8076
8077//-----------------------------------------------------------------------------
8078// Purpose:
8079//-----------------------------------------------------------------------------
8080const ISchemaAttributeType *CEconItemSchema::GetAttributeType( const char *pszAttrTypeName ) const
8081{
8082 FOR_EACH_VEC( m_vecAttributeTypes, i )
8083 {
8084 if ( m_vecAttributeTypes[i].m_sName == pszAttrTypeName )
8085 return m_vecAttributeTypes[i].m_pAttrType;
8086 }
8087
8088 return NULL;
8089}
8090
8091//-----------------------------------------------------------------------------
8092// CItemLevelingDefinition Accessor
8093//-----------------------------------------------------------------------------
8094const CItemLevelingDefinition *CEconItemSchema::GetItemLevelForScore( const char *pszLevelBlockName, uint32 unScore ) const
8095{
8096 const CUtlVector<CItemLevelingDefinition> *pLevelingData = GetItemLevelingData( pszLevelBlockName );
8097 if ( !pLevelingData )
8098 return NULL;
8099
8100 if ( pLevelingData->Count() == 0 )
8101 return NULL;
8102
8103 FOR_EACH_VEC( (*pLevelingData), i )
8104 {
8105 if ( unScore < (*pLevelingData)[i].GetRequiredScore() )
8106 return &(*pLevelingData)[i];
8107 }
8108
8109 return &(*pLevelingData).Tail();
8110}
8111
8112//-----------------------------------------------------------------------------
8113// Kill eater score type accessor
8114//-----------------------------------------------------------------------------
8115const kill_eater_score_type_t *CEconItemSchema::FindKillEaterScoreType( uint32 unScoreType ) const
8116{
8117 KillEaterScoreMap_t::IndexType_t i = m_mapKillEaterScoreTypes.Find( unScoreType );
8118 if ( i == KillEaterScoreMap_t::InvalidIndex() )
8119 return NULL;
8120
8121 return &m_mapKillEaterScoreTypes[i];
8122}
8123
8124//-----------------------------------------------------------------------------
8125// Kill eater score type accessor
8126//-----------------------------------------------------------------------------
8127const char *CEconItemSchema::GetKillEaterScoreTypeLocString( uint32 unScoreType ) const
8128{
8129 const kill_eater_score_type_t *pScoreType = FindKillEaterScoreType( unScoreType );
8130
8131 return pScoreType
8132 ? pScoreType->m_pszTypeString
8133 : NULL;
8134}
8135
8136
8137//-----------------------------------------------------------------------------
8138// Kill eater score type accessor for "use_level_data"
8139//-----------------------------------------------------------------------------
8140bool CEconItemSchema::GetKillEaterScoreTypeUseLevelData( uint32 unScoreType ) const
8141{
8142 const kill_eater_score_type_t *pScoreType = FindKillEaterScoreType( unScoreType );
8143 return pScoreType ? pScoreType->m_bUseLevelBlock : false;
8144}
8145
8146
8147//-----------------------------------------------------------------------------
8148// Kill eater score type accessor
8149//-----------------------------------------------------------------------------
8150const char *CEconItemSchema::GetKillEaterScoreTypeLevelingDataName( uint32 unScoreType ) const
8151{
8152 const kill_eater_score_type_t *pScoreType = FindKillEaterScoreType( unScoreType );
8153
8154 return pScoreType
8155 ? pScoreType->m_pszLevelBlockName
8156 : NULL;
8157}
8158
8159//-----------------------------------------------------------------------------
8160// Purpose:
8161//-----------------------------------------------------------------------------
8162econ_tag_handle_t CEconItemSchema::GetHandleForTag( const char *pszTagName )
8163{
8164 EconTagDict_t::IndexType_t i = m_dictTags.Find( pszTagName );
8165 if ( m_dictTags.IsValidIndex( i ) )
8166 return i;
8167
8168 return m_dictTags.Insert( pszTagName );
8169}
8170
8171#if defined(CLIENT_DLL) || defined(GAME_DLL)
8172//-----------------------------------------------------------------------------
8173// Purpose: Clones the specified item definition, and returns the new item def.
8174//-----------------------------------------------------------------------------
8175void CEconItemSchema::ItemTesting_CreateTestDefinition( int iCloneFromItemDef, int iNewDef, KeyValues *pNewKV )
8176{
8177 int nMapIndex = m_mapItems.Find( iNewDef );
8178 if ( !m_mapItems.IsValidIndex( nMapIndex ) )
8179 {
8180 nMapIndex = m_mapItems.Insert( iNewDef, CreateEconItemDefinition() );
8181 m_mapItemsSorted.Insert( iNewDef, m_mapItems[nMapIndex] );
8182 }
8183
8184 // Find & copy the clone item def's data in
8185 const CEconItemDefinition *pCloneDef = GetItemDefinition( iCloneFromItemDef );
8186 if ( !pCloneDef )
8187 return;
8188 m_mapItems[nMapIndex]->CopyPolymorphic( pCloneDef );
8189
8190 // Then stomp it with the KV test contents
8191 m_mapItems[nMapIndex]->BInitFromTestItemKVs( iNewDef, pNewKV, *this );
8192}
8193
8194//-----------------------------------------------------------------------------
8195// Purpose: Discards the specified item definition
8196//-----------------------------------------------------------------------------
8197void CEconItemSchema::ItemTesting_DiscardTestDefinition( int iDef )
8198{
8199 m_mapItems.Remove( iDef );
8200 m_mapItemsSorted.Remove( iDef );
8201}
8202
8203//-----------------------------------------------------------------------------
8204// Purpose: Initializes the armory data section of the schema
8205//-----------------------------------------------------------------------------
8206bool CEconItemSchema::BInitArmoryData( KeyValues *pKVArmoryData, CUtlVector<CUtlString> *pVecErrors )
8207{
8208 m_dictArmoryItemDataStrings.Purge();
8209 m_dictArmoryAttributeDataStrings.Purge();
8210 if ( NULL != pKVArmoryData )
8211 {
8212 KeyValues *pKVItemTypes = pKVArmoryData->FindKey( "armory_item_types" );
8213 if ( pKVItemTypes )
8214 {
8215 FOR_EACH_SUBKEY( pKVItemTypes, pKVEntry )
8216 {
8217 const char *pszDataKey = pKVEntry->GetName();
8218 const char *pszLocString = pKVEntry->GetString();
8219 m_dictArmoryItemTypesDataStrings.Insert( pszDataKey, pszLocString );
8220 }
8221 }
8222
8223 pKVItemTypes = pKVArmoryData->FindKey( "armory_item_classes" );
8224 if ( pKVItemTypes )
8225 {
8226 FOR_EACH_SUBKEY( pKVItemTypes, pKVEntry )
8227 {
8228 const char *pszDataKey = pKVEntry->GetName();
8229 const char *pszLocString = pKVEntry->GetString();
8230 m_dictArmoryItemClassesDataStrings.Insert( pszDataKey, pszLocString );
8231 }
8232 }
8233
8234 KeyValues *pKVAttribs = pKVArmoryData->FindKey( "armory_attributes" );
8235 if ( pKVAttribs )
8236 {
8237 FOR_EACH_SUBKEY( pKVAttribs, pKVEntry )
8238 {
8239 const char *pszDataKey = pKVEntry->GetName();
8240 const char *pszLocString = pKVEntry->GetString();
8241 m_dictArmoryAttributeDataStrings.Insert( pszDataKey, pszLocString );
8242 }
8243 }
8244
8245 KeyValues *pKVItems = pKVArmoryData->FindKey( "armory_items" );
8246 if ( pKVItems )
8247 {
8248 FOR_EACH_SUBKEY( pKVItems, pKVEntry )
8249 {
8250 const char *pszDataKey = pKVEntry->GetName();
8251 const char *pszLocString = pKVEntry->GetString();
8252 m_dictArmoryItemDataStrings.Insert( pszDataKey, pszLocString );
8253 }
8254 }
8255 }
8256 return SCHEMA_INIT_SUCCESS();
8257}
8258#endif
8259
8260
8261//-----------------------------------------------------------------------------
8262// Purpose: Returns the achievement award that matches the provided defindex or NULL
8263// if there is no such award.
8264// Input: unData - The data field that would be stored in ItemAudit
8265//-----------------------------------------------------------------------------
8266const AchievementAward_t *CEconItemSchema::GetAchievementRewardByDefIndex( uint16 usDefIndex ) const
8267{
8268 FOR_EACH_MAP_FAST( m_mapAchievementRewardsByData, nIndex )
8269 {
8270 if( m_mapAchievementRewardsByData[nIndex]->m_vecDefIndex.HasElement( usDefIndex ) )
8271 return m_mapAchievementRewardsByData[nIndex];
8272 }
8273 return NULL;
8274}
8275
8276//-----------------------------------------------------------------------------
8277// Purpose: Gets a rarity value for a name.
8278//-----------------------------------------------------------------------------
8279bool CEconItemSchema::BGetItemRarityFromName( const char *pchName, uint8 *nRarity ) const
8280{
8281 if ( 0 == Q_stricmp( "any", pchName ) )
8282 {
8283 *nRarity = k_unItemRarity_Any;
8284 return true;
8285 }
8286
8287 FOR_EACH_MAP_FAST( m_mapRarities, i )
8288 {
8289 if ( 0 == Q_stricmp( m_mapRarities[i].GetName(), pchName ) )
8290 {
8291 *nRarity = m_mapRarities[i].GetDBValue();
8292 return true;
8293 }
8294 }
8295
8296 return false;
8297}
8298
8299//-----------------------------------------------------------------------------
8300// Purpose: Gets a quality value for a name.
8301// Input: pchName - The name to translate.
8302// nQuality - (out)The quality number for this name, if found.
8303// Output: True if the string matched a quality for this schema, false otherwise.
8304//-----------------------------------------------------------------------------
8305bool CEconItemSchema::BGetItemQualityFromName( const char *pchName, uint8 *nQuality ) const
8306{
8307 if ( 0 == Q_stricmp( "any", pchName ) )
8308 {
8309 *nQuality = k_unItemQuality_Any;
8310 return true;
8311 }
8312
8313 FOR_EACH_MAP_FAST( m_mapQualities, i )
8314 {
8315 if ( 0 == Q_stricmp( m_mapQualities[i].GetName(), pchName ) )
8316 {
8317 *nQuality = m_mapQualities[i].GetDBValue();
8318 return true;
8319 }
8320 }
8321
8322 return false;
8323}
8324
8325
8326//-----------------------------------------------------------------------------
8327// Purpose: Gets a quality definition for an index
8328// Input: nQuality - The quality to get.
8329// Output: A pointer to the desired definition, or NULL if it is not found.
8330//-----------------------------------------------------------------------------
8331const CEconItemQualityDefinition *CEconItemSchema::GetQualityDefinition( int nQuality ) const
8332{
8333 int iIndex = m_mapQualities.Find( nQuality );
8334 if ( m_mapQualities.IsValidIndex( iIndex ) )
8335 return &m_mapQualities[iIndex];
8336 return NULL;
8337}
8338
8339const CEconItemQualityDefinition *CEconItemSchema::GetQualityDefinitionByName( const char *pszDefName ) const
8340{
8341 FOR_EACH_MAP_FAST( m_mapQualities, i )
8342 {
8343 if ( !strcmp( pszDefName, m_mapQualities[i].GetName()) )
8344 return &m_mapQualities[i];
8345 }
8346 return NULL;
8347}
8348
8349const char* CEconItemSchema::GetQualityName( uint8 iQuality )
8350{
8351 const CEconItemQualityDefinition* pItemQuality = GetQualityDefinition( iQuality );
8352 if ( !pItemQuality )
8353 return NULL;
8354 else
8355 return pItemQuality->GetName();
8356}
8357
8358int CEconItemSchema::GetQualityIndex( const char* pszQuality )
8359{
8360 const CEconItemQualityDefinition* pItemQuality = GetQualityDefinitionByName( pszQuality );
8361 if ( pItemQuality )
8362 return pItemQuality->GetDBValue();
8363 else
8364 return 0;
8365}
8366
8367const CEconItemRarityDefinition *CEconItemSchema::GetRarityDefinitionByMapIndex( int nRarityIndex ) const
8368{
8369 if ( m_mapRarities.IsValidIndex( nRarityIndex ) )
8370 return &m_mapRarities[nRarityIndex];
8371
8372 return NULL;
8373}
8374
8375const CEconItemRarityDefinition *CEconItemSchema::GetRarityDefinition( int nRarity ) const
8376{
8377 int iIndex = m_mapRarities.Find( nRarity );
8378 if ( m_mapRarities.IsValidIndex( iIndex ) )
8379 return &m_mapRarities[iIndex];
8380 return NULL;
8381}
8382
8383const CEconItemRarityDefinition *CEconItemSchema::GetRarityDefinitionByName( const char *pszDefName ) const
8384{
8385 FOR_EACH_MAP_FAST( m_mapRarities, i )
8386 {
8387 if ( !strcmp( pszDefName, m_mapRarities[i].GetName()) )
8388 return &m_mapRarities[i];
8389 }
8390 return NULL;
8391}
8392
8393const char* CEconItemSchema::GetRarityName( uint8 iRarity )
8394{
8395 const CEconItemRarityDefinition* pItemRarity = GetRarityDefinition( iRarity );
8396 if ( !pItemRarity )
8397 return NULL;
8398 else
8399 return pItemRarity->GetName();
8400}
8401
8402const char* CEconItemSchema::GetRarityLocKey( uint8 iRarity )
8403{
8404 const CEconItemRarityDefinition* pItemRarity = GetRarityDefinition( iRarity );
8405 if ( !pItemRarity )
8406 return NULL;
8407 else
8408 return pItemRarity->GetLocKey();
8409}
8410
8411const char* CEconItemSchema::GetRarityColor( uint8 iRarity )
8412{
8413 const CEconItemRarityDefinition* pItemRarity = GetRarityDefinition( iRarity );
8414 if ( !pItemRarity )
8415 return NULL;
8416 else
8417 return GetHexColorForAttribColor( pItemRarity->GetAttribColor() );
8418}
8419
8420const char* CEconItemSchema::GetRarityLootList( uint8 iRarity )
8421{
8422 const CEconItemRarityDefinition* pItemRarity = GetRarityDefinition( iRarity );
8423 if ( !pItemRarity )
8424 return NULL;
8425 else
8426 return pItemRarity->GetLootList();
8427}
8428
8429int CEconItemSchema::GetRarityIndex( const char* pszRarity )
8430{
8431 const CEconItemRarityDefinition* pRarity = GetRarityDefinitionByName( pszRarity );
8432 if ( pRarity )
8433 return pRarity->GetDBValue();
8434 else
8435 return 0;
8436}
8437
8438const CEconSoundMaterialDefinition *CEconItemSchema::GetSoundMaterialDefinitionByID( int nSoundMaterialID ) const
8439{
8440 int iIndex = m_mapSoundMaterials.Find( nSoundMaterialID );
8441 if ( m_mapSoundMaterials.IsValidIndex( iIndex ) )
8442 return &m_mapSoundMaterials[iIndex];
8443 return NULL;
8444}
8445
8446const CEconSoundMaterialDefinition *CEconItemSchema::GetSoundMaterialDefinitionByName( const char *pszSoundMaterialName ) const
8447{
8448 FOR_EACH_MAP_FAST( m_mapSoundMaterials, i )
8449 {
8450 if ( !strcmp( pszSoundMaterialName, m_mapSoundMaterials[i].GetName()) )
8451 return &m_mapSoundMaterials[i];
8452 }
8453 return NULL;
8454}
8455
8456const char* CEconItemSchema::GetSoundMaterialNameByID( int nSoundMaterialID )
8457{
8458 const CEconSoundMaterialDefinition* pSoundMaterial = GetSoundMaterialDefinitionByID( nSoundMaterialID );
8459 if ( !pSoundMaterial )
8460 return NULL;
8461 else
8462 return pSoundMaterial->GetName();
8463}
8464
8465int CEconItemSchema::GetSoundMaterialID( const char* pszSoundMaterial )
8466{
8467 const CEconSoundMaterialDefinition* pSoundMaterial = GetSoundMaterialDefinitionByName( pszSoundMaterial );
8468 if ( pSoundMaterial )
8469 return pSoundMaterial->GetID();
8470 else
8471 return 0;
8472}
8473
8474int CEconItemSchema::GetSoundMaterialIDByIndex( int nIndex )
8475{
8476 // This makes for a slow iteration pattern. Only used by the item editor currently, should make it a UtlVector/UtlMap pair if it's too slow.
8477 FOR_EACH_MAP( m_mapSoundMaterials, i )
8478 {
8479 nIndex--;
8480 if ( nIndex < 0 )
8481 {
8482 return m_mapSoundMaterials[i].GetID();
8483 }
8484 }
8485 return -1;
8486}
8487
8488//-----------------------------------------------------------------------------
8489// Purpose: Gets an item definition for the specified map index
8490//-----------------------------------------------------------------------------
8491const CEconItemDefinition *CEconItemSchema::GetItemDefinitionByMapIndex( int iMapIndex ) const
8492{
8493 if ( m_mapItems.IsValidIndex( iMapIndex ) )
8494 return m_mapItems[iMapIndex];
8495
8496 return NULL;
8497}
8498
8499//-----------------------------------------------------------------------------
8500// Purpose: Gets an item definition for the specified definition index
8501// Input: iItemIndex - The index of the desired definition.
8502// Output: A pointer to the desired definition, or NULL if it is not found.
8503//-----------------------------------------------------------------------------
8504CEconItemDefinition *CEconItemSchema::GetItemDefinitionMutable( int iItemIndex, bool bNoDefault )
8505{
8506#if defined(CLIENT_DLL) || defined(GAME_DLL)
8507#if !defined(CSTRIKE_DLL)
8508 AssertMsg( m_pDefaultItemDefinition, "No default item definition set up for item schema." );
8509#endif // CSTRIKE_DLL
8510#endif // defined(CLIENT_DLL) || defined(GAME_DLL)
8511
8512 int iIndex = m_mapItems.Find( iItemIndex );
8513 if ( m_mapItems.IsValidIndex( iIndex ) )
8514 return m_mapItems[iIndex];
8515
8516 if ( bNoDefault )
8517 return NULL;
8518
8519 if ( m_pDefaultItemDefinition )
8520 return m_pDefaultItemDefinition;
8521
8522#if !defined(CSTRIKE_DLL)
8523 // We shouldn't ever get down here, but all the same returning a valid pointer is very slightly
8524 // a better plan than returning an invalid pointer to code that won't check to see if it's valid.
8525 static CEconItemDefinition *s_pEmptyDefinition = CreateEconItemDefinition();
8526 return s_pEmptyDefinition;
8527#else
8528 return NULL;
8529#endif // CSTRIKE_DLL
8530}
8531const CEconItemDefinition *CEconItemSchema::GetItemDefinition( int iItemIndex, bool bNoDefault ) const
8532{
8533 return const_cast<CEconItemSchema *>(this)->GetItemDefinitionMutable( iItemIndex, bNoDefault );
8534}
8535
8536//-----------------------------------------------------------------------------
8537// Purpose: Gets an item definition that has a name matching the specified name.
8538// Input: pszDefName - The name of the desired definition.
8539// Output: A pointer to the desired definition, or NULL if it is not found.
8540//-----------------------------------------------------------------------------
8541CEconItemDefinition *CEconItemSchema::GetItemDefinitionByName( const char *pszDefName )
8542{
8543 SNPROF(__PRETTY_FUNCTION__);
8544
8545 if ( m_bSchemaParsingItems )
8546 {
8547 AssertMsg( 0, "GetItemDefinitionByName while parsing item definitions. This is not a valid operation." );
8548 return NULL;
8549 }
8550
8551 // This shouldn't happen, but let's not crash if it ever does.
8552 Assert( pszDefName != NULL );
8553 if ( pszDefName == NULL )
8554 return NULL;
8555
8556 FOR_EACH_MAP_FAST( m_mapItems, i )
8557 {
8558 if ( !strcmp( pszDefName, m_mapItems[i]->GetDefinitionName()) )
8559 return m_mapItems[i];
8560 }
8561 return NULL;
8562}
8563
8564const CEconItemDefinition *CEconItemSchema::GetItemDefinitionByName( const char *pszDefName ) const
8565{
8566 return const_cast<CEconItemSchema *>(this)->GetItemDefinitionByName( pszDefName );
8567}
8568
8569//-----------------------------------------------------------------------------
8570// Purpose: Gets an attribute definition for an index
8571// Input: iAttribIndex - The index of the desired definition.
8572// Output: A pointer to the desired definition, or NULL if it is not found.
8573//-----------------------------------------------------------------------------
8574const CEconItemAttributeDefinition *CEconItemSchema::GetAttributeDefinition( int iAttribIndex ) const
8575{
8576 if ( m_mapAttributesContainer.IsValidIndex( iAttribIndex ) )
8577 return m_mapAttributesContainer[iAttribIndex];
8578 return NULL;
8579}
8580
8581const CEconItemAttributeDefinition *CEconItemSchema::GetAttributeDefinitionByName( const char *pszDefName ) const
8582{
8583 VPROF_BUDGET( "CEconItemSchema::GetAttributeDefinitionByName", VPROF_BUDGETGROUP_STEAM );
8584 SNPROF(__PRETTY_FUNCTION__);
8585
8586 FOR_EACH_VEC( m_mapAttributesContainer, i )
8587 {
8588 if ( m_mapAttributesContainer[i] && !strcmp( pszDefName, m_mapAttributesContainer[i]->GetDefinitionName()) )
8589 return m_mapAttributesContainer[i];
8590 }
8591 return NULL;
8592}
8593
8594//-----------------------------------------------------------------------------
8595// Purpose: Gets a recipe definition for an index
8596// Input: iRecipeIndex - The index of the desired definition.
8597// Output: A pointer to the desired definition, or NULL if it is not found.
8598//-----------------------------------------------------------------------------
8599const CEconCraftingRecipeDefinition *CEconItemSchema::GetRecipeDefinition( int iRecipeIndex ) const
8600{
8601 int iIndex = m_mapRecipes.Find( iRecipeIndex );
8602 if ( m_mapRecipes.IsValidIndex( iIndex ) )
8603 return m_mapRecipes[iIndex];
8604 return NULL;
8605}
8606
8607//-----------------------------------------------------------------------------
8608
8609void CEconItemSchema::AddStickerKitDefinition( int iStickerKitID, CStickerKit *pStickerKit )
8610{
8611 int iIndex = m_mapStickerKits.Find( iStickerKitID );
8612 if ( m_mapStickerKits.IsValidIndex( iIndex ) )
8613 {
8614 m_mapStickerKits.Remove( iStickerKitID );
8615 }
8616
8617 m_mapStickerKits.Insert( iStickerKitID, pStickerKit );
8618}
8619
8620void CEconItemSchema::RemoveStickerKitDefinition( int iStickerKitID )
8621{
8622 m_mapStickerKits.Remove( iStickerKitID );
8623}
8624
8625const CStickerKit *CEconItemSchema::GetStickerKitDefinition( int iStickerKitID ) const
8626{
8627 int iIndex = m_mapStickerKits.Find( iStickerKitID );
8628 if ( m_mapStickerKits.IsValidIndex( iIndex ) )
8629 return m_mapStickerKits[iIndex];
8630 return NULL;
8631}
8632
8633const CStickerKit *CEconItemSchema::GetStickerKitDefinitionByMapIndex( int iMapIndex )
8634{
8635 if ( m_mapStickerKits.IsValidIndex( iMapIndex ) )
8636 return m_mapStickerKits[ iMapIndex ];
8637
8638 return NULL;
8639}
8640
8641const CStickerKit *CEconItemSchema::GetStickerKitDefinitionByName( const char *pchName ) const
8642{
8643 int idx = m_dictStickerKits.Find( pchName );
8644 if ( idx != m_dictStickerKits.InvalidIndex() )
8645 return m_dictStickerKits.Element( idx );
8646 else
8647 return NULL;
8648}
8649
8650const CStickerList *CEconItemSchema::GetStickerListDefinitionByName( const char *pchName ) const
8651{
8652 int idx = m_dictStickerLists.Find( pchName );
8653 if ( idx != m_dictStickerLists.InvalidIndex() )
8654 return m_dictStickerLists.Element( idx );
8655 else
8656 return NULL;
8657}
8658
8659const CEconMusicDefinition *CEconItemSchema::GetMusicKitDefinitionByName( const char *pchName ) const
8660{
8661 FOR_EACH_MAP_FAST( m_mapMusicDefs, i )
8662 {
8663 if ( !V_strcmp( m_mapMusicDefs.Element( i )->GetName(), pchName ) )
8664 {
8665 return m_mapMusicDefs.Element( i );
8666 }
8667 }
8668
8669 return NULL;
8670}
8671
8672
8673//-----------------------------------------------------------------------------
8674
8675void CEconItemSchema::AddPaintKitDefinition( int iPaintKitID, CPaintKit *pPaintKit )
8676{
8677 int iIndex = m_mapPaintKits.Find( iPaintKitID );
8678 if ( m_mapPaintKits.IsValidIndex( iIndex ) )
8679 {
8680 m_mapPaintKits.Remove( iPaintKitID );
8681 }
8682
8683 m_mapPaintKits.Insert( iPaintKitID, pPaintKit );
8684}
8685
8686void CEconItemSchema::RemovePaintKitDefinition( int iPaintKitID )
8687{
8688 m_mapPaintKits.Remove( iPaintKitID );
8689}
8690
8691const CPaintKit *CEconItemSchema::GetPaintKitDefinition( int iPaintKitID ) const
8692{
8693 int iIndex = m_mapPaintKits.Find( iPaintKitID );
8694 if ( m_mapPaintKits.IsValidIndex( iIndex ) )
8695 return m_mapPaintKits[iIndex];
8696 return NULL;
8697}
8698
8699const CPaintKit *CEconItemSchema::GetPaintKitDefinitionByMapIndex( int iMapIndex )
8700{
8701 if ( m_mapPaintKits.IsValidIndex( iMapIndex ) )
8702 return m_mapPaintKits[ iMapIndex ];
8703
8704 return NULL;
8705}
8706
8707const CPaintKit *CEconItemSchema::GetPaintKitDefinitionByName( const char *pchName ) const
8708{
8709 FOR_EACH_MAP_FAST( m_mapPaintKits, i )
8710 {
8711 CPaintKit *pPaintKit = m_mapPaintKits[ i ];
8712 if ( V_strcmp( pPaintKit->sName.Access(), pchName ) == 0 )
8713 {
8714 return pPaintKit;
8715 }
8716 }
8717
8718 return NULL;
8719}
8720
8721const unsigned int CEconItemSchema::GetPaintKitCount() const
8722{
8723 return m_mapPaintKits.Count();
8724}
8725
8726//-----------------------------------------------------------------------------
8727// Purpose:
8728//-----------------------------------------------------------------------------
8729const CEconColorDefinition *CEconItemSchema::GetColorDefinitionByName( const char *pszDefName ) const
8730{
8731 FOR_EACH_VEC( m_vecColorDefs, i )
8732 {
8733 if ( !Q_stricmp( m_vecColorDefs[i]->GetName(), pszDefName ) )
8734 return m_vecColorDefs[i];
8735 }
8736 return NULL;
8737}
8738
8739//-----------------------------------------------------------------------------
8740// Purpose:
8741//-----------------------------------------------------------------------------
8742const CEconGraffitiTintDefinition * CEconItemSchema::GetGraffitiTintDefinitionByID( int nID ) const
8743{
8744 return m_vecGraffitiTintDefs.IsValidIndex( nID ) ? m_vecGraffitiTintDefs[ nID ] : NULL;
8745}
8746
8747//-----------------------------------------------------------------------------
8748// Purpose:
8749//-----------------------------------------------------------------------------
8750const CEconGraffitiTintDefinition * CEconItemSchema::GetGraffitiTintDefinitionByName( const char *pszDefName ) const
8751{
8752 UtlSymId_t utlsym = m_mapGraffitiTintByName.Find( pszDefName );
8753 return ( utlsym != UTL_INVAL_SYMBOL ) ? m_mapGraffitiTintByName[ utlsym ] : NULL;
8754}
8755
8756
8757//-----------------------------------------------------------------------------
8758// Purpose:
8759//-----------------------------------------------------------------------------
8760const CEconMusicDefinition *CEconItemSchema::GetMusicDefinition( uint32 unMusicID ) const
8761{
8762 int iIndex = m_mapMusicDefs.Find( unMusicID );
8763 if ( m_mapMusicDefs.IsValidIndex( iIndex ) )
8764 return m_mapMusicDefs[iIndex];
8765 return NULL;
8766}
8767
8768
8769//-----------------------------------------------------------------------------
8770// Purpose:
8771//-----------------------------------------------------------------------------
8772CEconQuestDefinition *CEconItemSchema::GetQuestDefinition( uint32 unQuestID ) const
8773{
8774 int iIndex = m_mapQuestDefs.Find( unQuestID );
8775 if ( m_mapQuestDefs.IsValidIndex( iIndex ) )
8776 return m_mapQuestDefs[ iIndex ];
8777 return NULL;
8778}
8779
8780//-----------------------------------------------------------------------------
8781// Purpose:
8782//-----------------------------------------------------------------------------
8783CEconCampaignDefinition *CEconItemSchema::GetCampaignDefinition( uint32 unCampaignID ) const
8784{
8785 int iIndex = m_mapCampaignDefs.Find( unCampaignID );
8786 if ( m_mapCampaignDefs.IsValidIndex( iIndex ) )
8787 return m_mapCampaignDefs[ iIndex ];
8788 return NULL;
8789}
8790
8791//-----------------------------------------------------------------------------
8792// Purpose: Return the attribute specified attachedparticlesystem_t* associated with the given id.
8793//-----------------------------------------------------------------------------
8794attachedparticlesystem_t* CEconItemSchema::GetAttributeControlledParticleSystem( int id )
8795{
8796 int iIndex = m_mapAttributeControlledParticleSystems.Find( id );
8797 if ( m_mapAttributeControlledParticleSystems.IsValidIndex( iIndex ) )
8798 return &m_mapAttributeControlledParticleSystems[iIndex];
8799 return NULL;
8800}
8801
8802attachedparticlesystem_t* CEconItemSchema::GetAttributeControlledParticleSystemByIndex( int id )
8803{
8804 return &m_mapAttributeControlledParticleSystems[id];
8805}
8806
8807attachedparticlesystem_t* CEconItemSchema::FindAttributeControlledParticleSystem( const char *pchSystemName, int *outID )
8808{
8809 FOR_EACH_MAP_FAST( m_mapAttributeControlledParticleSystems, nSystem )
8810 {
8811 if( !Q_stricmp( m_mapAttributeControlledParticleSystems[nSystem].pszSystemName, pchSystemName ) )
8812 {
8813 if ( outID )
8814 {
8815 *outID = nSystem;
8816 }
8817 return &m_mapAttributeControlledParticleSystems[nSystem];
8818 }
8819 }
8820 if ( outID )
8821 {
8822 *outID = -1;
8823 }
8824 return NULL;
8825}
8826
8827bool CEconItemSchema::BPostSchemaInitStartupChecks( CUtlVector<CUtlString> *pVecErrors )
8828{
8829#if defined( GC_DLL ) || defined( CLIENT_DLL )
8830 // confirm that all stickers that reference players have valid link
8831 FOR_EACH_MAP_FAST( m_mapStickerKits, i )
8832 {
8833 CStickerKit * pStickerKit = m_mapStickerKits.Element( i );
8834 if ( !pStickerKit->m_nPlayerID ) continue;
8835 SCHEMA_INIT_CHECK(
8836 ( m_mapProPlayersByAccountID.Find( pStickerKit->m_nPlayerID ) != m_mapProPlayersByAccountID.InvalidIndex() ),
8837 CFmtStr( "Pro-player sticker %d reference player %u which doesn't have a matching pro-player entry", pStickerKit->nID, pStickerKit->m_nPlayerID ) );
8838 }
8839
8840#endif // #if defined( GC_DLL ) || defined( CLIENT_DLL )
8841
8842#ifdef CLIENT_DLL
8843 //// Confirm that every story block expression references only existing quest indices.
8844 FOR_EACH_MAP_FAST( m_mapCampaignDefs, iC )
8845 {
8846 FOR_EACH_MAP_FAST( m_mapCampaignDefs.Element( iC )->GetCampaignNodes(), iCn )
8847 {
8848 CEconCampaignDefinition::CEconCampaignNodeDefinition * pCNodeDef = m_mapCampaignDefs.Element( iC )->GetCampaignNodes().Element( iCn );
8849
8850 FOR_EACH_VEC( pCNodeDef->GetStoryBlocks(), iSB )
8851 {
8852
8853 KeyValues * pKVExpressionTokens = new KeyValues( "ExpressionTokens" );
8854 KeyValues::AutoDelete autodelete( pKVExpressionTokens );
8855
8856 CEconQuestDefinition::TokenizeQuestExpression( pCNodeDef->GetStoryBlocks()[ iSB ]->GetStoryBlockExpression(), pKVExpressionTokens );
8857
8858 FOR_EACH_SUBKEY( pKVExpressionTokens, kvSubKey )
8859 {
8860 int iQ = m_mapQuestDefs.Find( V_atoi( kvSubKey->GetName() ) );
8861
8862 SCHEMA_INIT_CHECK(
8863 ( m_mapQuestDefs.IsValidIndex( iQ ) ),
8864 CFmtStr( "campaign %d, node %d, Story_block %d, references a non-existant quest index %d.", iC, iCn, iSB, iQ ) );
8865
8866 }
8867 }
8868 }
8869 }
8870
8871 // confirm that all quest expressions are valid
8872
8873 FOR_EACH_MAP_FAST( m_mapQuestDefs, i )
8874 {
8875 CEconQuestDefinition * pQuestDef = m_mapQuestDefs.Element( i );
8876
8877 SCHEMA_INIT_CHECK(
8878 ( CEconQuestDefinition::IsQuestExpressionValid( pQuestDef->GetQuestExpression() ) ),
8879 CFmtStr( "Quest definition %s specifies an expression that does not evaluate.", pQuestDef->GetName() ) );
8880
8881 if ( pQuestDef->GetQuestBonusExpression() && pQuestDef->GetQuestBonusExpression()[ 0 ] )
8882 {
8883 SCHEMA_INIT_CHECK(
8884 ( CEconQuestDefinition::IsQuestExpressionValid( pQuestDef->GetQuestBonusExpression() ) ),
8885 CFmtStr( "Quest definition %s specifies a bonus expression that does not evaluate.", pQuestDef->GetName() ) );
8886 }
8887 }
8888
8889#endif // CLIENT_DLL
8890
8891 return true;
8892}
8893
8894//-----------------------------------------------------------------------------
8895//
8896//-----------------------------------------------------------------------------
8897CItemLevelingDefinition::CItemLevelingDefinition( void )
8898{
8899}
8900
8901//-----------------------------------------------------------------------------
8902// Purpose: Copy constructor
8903//-----------------------------------------------------------------------------
8904CItemLevelingDefinition::CItemLevelingDefinition( const CItemLevelingDefinition &that )
8905{
8906 (*this) = that;
8907}
8908
8909//-----------------------------------------------------------------------------
8910//
8911//-----------------------------------------------------------------------------
8912CItemLevelingDefinition::~CItemLevelingDefinition()
8913{
8914 // Free up strdup() memory.
8915 free( m_pszLocalizedName_LocalStorage );
8916 CUtlVector< CUtlString > vecErrors;
8917 bool bSuccess = GEconItemSchema().BInit( "scripts/items/unencrypted/items_master.txt", "MOD", &vecErrors );
8918 if( !bSuccess )
8919 {
8920 FOR_EACH_VEC( vecErrors, nError )
8921 {
8922 Msg( "%s", vecErrors[nError].String() );
8923 }
8924 }
8925}
8926
8927//-----------------------------------------------------------------------------
8928// Purpose: Operator=
8929//-----------------------------------------------------------------------------
8930CItemLevelingDefinition &CItemLevelingDefinition::operator=( const CItemLevelingDefinition &other )
8931{
8932 m_unLevel = other.m_unLevel;
8933 m_unRequiredScore = other.m_unRequiredScore;
8934 m_pszLocalizedName_LocalStorage = strdup( other.m_pszLocalizedName_LocalStorage );
8935
8936 return *this;
8937}
8938
8939//-----------------------------------------------------------------------------
8940//
8941//-----------------------------------------------------------------------------
8942bool CItemLevelingDefinition::BInitFromKV( KeyValues *pKVItemLevel, CEconItemSchema &pschema, const char *pszLevelBlockName, CUtlVector<CUtlString> *pVecErrors )
8943{
8944 m_unLevel = Q_atoi( pKVItemLevel->GetName() );
8945 m_unRequiredScore = pKVItemLevel->GetInt( "score" );
8946 m_pszLocalizedName_LocalStorage = strdup( pKVItemLevel->GetString( "rank_name", CFmtStr( "%s%i", pszLevelBlockName, m_unLevel ).Access() ) );
8947
8948 return SCHEMA_INIT_SUCCESS();
8949}
8950
8951#ifdef GAME_DLL
8952
8953void ReloadMasterItemSchema( void )
8954{
8955 // This command does nothing on the public universe.
8956 if ( steamapicontext->SteamUtils() && steamapicontext->SteamUtils()->GetConnectedUniverse() == k_EUniversePublic )
8957 return;
8958
8959 CUtlVector< CUtlString > vecErrors;
8960 bool bSuccess = GEconItemSchema().BInit( "scripts/items/unencrypted/items_master.txt", "MOD", &vecErrors );
8961 if( !bSuccess )
8962 {
8963 FOR_EACH_VEC( vecErrors, nError )
8964 {
8965 Msg( "%s", vecErrors[nError].String() );
8966 }
8967 }
8968}
8969
8970CON_COMMAND_F( load_master_item_schema, "Reloads the item master schema.", FCVAR_GAMEDLL | FCVAR_CHEAT | FCVAR_DEVELOPMENTONLY | FCVAR_HIDDEN )
8971{
8972 ReloadMasterItemSchema();
8973}
8974
8975#endif