Counter Strike : Global Offensive Source Code
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

8972 lines
304 KiB

//====== Copyright ?, Valve Corporation, All rights reserved. =======
//
// Purpose: EconItemSchema: Defines a schema for econ items
//
//=============================================================================
#include "cbase.h"
#include "econ_item_schema.h"
#include "tier1/fmtstr.h"
#include "tier2/tier2.h"
#include "filesystem.h"
#include "schemainitutils.h"
#include "cstrike15_gcconstants.h"
#include <google/protobuf/text_format.h>
#if defined(CLIENT_DLL) || defined(GAME_DLL)
#include "econ_item_system.h"
#include "econ_item.h"
#include "activitylist.h"
#if defined(TF_CLIENT_DLL) || defined(TF_DLL)
#include "tf_gcmessages.h"
#endif
#endif
#if defined(CSTRIKE_CLIENT_DLL) || defined(CSTRIKE_DLL)
#include "cstrike15_gcmessages.pb.h"
#endif
#include "gametypes.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
using namespace GCSDK;
#if defined(GAME_DLL)
void PrecacheParticleFileAndSystems( const char *pParticleSystemFile );
#endif
#if defined(CLIENT_DLL)
bool CWebResource::s_Initialized = false;
#endif
CEconItemSchema & GEconItemSchema()
{
#if defined( EXTERNALTESTS_DLL )
static CEconItemSchema g_econItemSchema;
return g_econItemSchema;
#else
return *ItemSystem()->GetItemSchema();
#endif
}
const char * g_arrQuestVars[ k_EQuestVar_Last ] = { "" };
/** Removed for partner depot **/
static void HelperValidateLocalizationStringToken( char const *pszToken )
{
#ifdef CLIENT_DLL
if ( pszToken && *pszToken == '#' )
AssertMsg1( g_pVGuiLocalize->Find( pszToken ), "Schema is referencing a non-existant token: %s", pszToken );
#endif
}
const char *g_szDropTypeStrings[] =
{
"", // Blank and none mean the same thing: stay attached to the body.
"none",
"drop", // The item drops off the body.
"break", // Not implemented, but an example of a type that could be added.
};
#if defined(CLIENT_DLL) || defined(GAME_DLL)
// Used to convert strings to ints for wearable animation types
const char *g_WearableAnimTypeStrings[NUM_WAP_TYPES] =
{
"on_spawn", // WAP_ON_SPAWN,
"start_building", // WAP_START_BUILDING,
"stop_building", // WAP_STOP_BUILDING,
};
#endif
const char *g_AttributeDescriptionFormats[] =
{
"value_is_percentage", // ATTDESCFORM_VALUE_IS_PERCENTAGE,
"value_is_inverted_percentage", // ATTDESCFORM_VALUE_IS_INVERTED_PERCENTAGE
"value_is_additive", // ATTDESCFORM_VALUE_IS_ADDITIVE
"value_is_additive_percentage", // ATTDESCFORM_VALUE_IS_ADDITIVE_PERCENTAGE
"value_is_or", // ATTDESCFORM_VALUE_IS_OR
"value_is_date", // ATTDESCFORM_VALUE_IS_DATE
"value_is_account_id", // ATTDESCFORM_VALUE_IS_ACCOUNT_ID
"value_is_particle_index", // ATTDESCFORM_VALUE_IS_PARTICLE_INDEX -> Could change to "string index"
"value_is_item_def", // ATTDESCFORM_VALUE_IS_ITEM_DEF
"value_is_color", // ATTDESCFORM_VALUE_IS_COLOR
"value_is_game_time", // ATTDESCFORM_VALUE_IS_GAME_TIME
"value_is_mins_as_hours", // ATTDESCFORM_VALUE_IS_MINS_AS_HOURS
"value_is_replace", // ATTDESCFORM_VALUE_IS_REPLACE
};
const char *g_EffectTypes[NUM_EFFECT_TYPES] =
{
"neutral", // ATTRIB_EFFECT_NEUTRAL = 0,
"positive", // ATTRIB_EFFECT_POSITIVE,
"negative", // ATTRIB_EFFECT_NEGATIVE,
};
const char *g_szParticleAttachTypes[] =
{
"absorigin", // PATTACH_ABSORIGIN = 0, // Create at absorigin, but don't follow
"absorigin_follow", // PATTACH_ABSORIGIN_FOLLOW, // Create at absorigin, and update to follow the entity
"customorigin", // PATTACH_CUSTOMORIGIN, // Create at a custom origin, but don't follow
"customorigin_follow", // PATTACH_CUSTOMORIGIN_FOLLOW, // Create at a custom origin, follow relative position to specified entity
"point", // PATTACH_POINT, // Create on attachment point, but don't follow
"point_follow", // PATTACH_POINT_FOLLOW, // Create on attachment point, and update to follow the entity
"eyes_follow", // PATTACH_EYES_FOLLOW, // Create on eyes of the attached entity, and update to follow the entity
"overhead_follow", // PATTACH_OVERHEAD_FOLLOW, // Create at the top of the entity's bbox
"worldorigin", // PATTACH_WORLDORIGIN, // Used for control points that don't attach to an entity
"rootbone_follow", // PATTACH_ROOTBONE_FOLLOW, // Create at the root bone of the entity, and update to follow
"point_follow_substepped", // PATTACH_POINT_FOLLOW_SUBSTEPPED,// Like point_follow with interpolation
"renderorigin_follow", // PATTACH_RENDERORIGIN_FOLLOW // Create at the renderorigin of the entity, and update to follow
};
const char *g_szParticleAttachToEnt[] =
{
"self", // ATTPART_TO_SELF,
"parent", // ATTPART_TO_PARENT,
};
//-----------------------------------------------------------------------------
// Purpose: Set the capabilities bitfield based on whether the entry is true/false.
//-----------------------------------------------------------------------------
const char *g_Capabilities[] =
{
"paintable", // ITEM_CAP_PAINTABLE
"nameable", // ITEM_CAP_NAMEABLE
"decodable", // ITEM_CAP_DECODABLE
"can_delete", // ITEM_CAP_CAN_DELETE
"can_customize_texture", // ITEM_CAP_CAN_CUSTOMIZE_TEXTURE
"usable", // ITEM_CAP_USABLE
"usable_gc", // ITEM_CAP_USABLE_GC
"can_gift_wrap", // ITEM_CAP_CAN_GIFT_WRAP
"usable_out_of_game", // ITEM_CAP_USABLE_OUT_OF_GAME
"can_collect", // ITEM_CAP_CAN_COLLECT
"can_craft_count", // ITEM_CAP_CAN_CRAFT_COUNT
"can_craft_mark", // ITEM_CAP_CAN_CRAFT_MARK
"paintable_team_colors", // ITEM_CAP_PAINTABLE_TEAM_COLORS
"can_be_restored", // ITEM_CAP_CAN_BE_RESTORED
"strange_parts", // ITEM_CAP_CAN_USE_STRANGE_PARTS
"paintable_unusual", // ITEM_CAP_PAINTABLE_UNUSUAL
"can_increment", // ITEM_CAP_CAN_INCREMENT
"uses_essence", // ITEM_CAP_USES_ESSENCE
"autograph", // ITEM_CAP_AUTOGRAPH
"recipe", // ITEM_CAP_RECIPE
"can_sticker", // ITEM_CAP_CAN_STICKER
"can_stattrack_swap", // ITEM_CAP_STATTRACK_SWAP
};
const char* g_AssetModifiers[] =
{
"activity",
"announcer",
"announcer_preview",
"hud_skin",
"ability_name",
"sound",
"speech",
"particle",
"particle_snapshot",
"particle_control_point",
"entity_model",
"entity_scale",
"icon_replacement",
"ability_icon_replacement",
"courier",
"courier_flying",
"hero_model_change"
};
#define RETURN_ATTRIBUTE_STRING( attrib_name, default_string ) \
static CSchemaAttributeDefHandle pAttribString( attrib_name ); \
const char *pchResultAttribString = default_string; \
FindAttribute_UnsafeBitwiseCast< CAttribute_String >( this, pAttribString, &pchResultAttribString ); \
return pchResultAttribString;
#define RETURN_ATTRIBUTE_STRING_F( func_name, attrib_name, default_string ) \
const char *func_name( void ) const { RETURN_ATTRIBUTE_STRING( attrib_name, default_string ) }
EAssetModifier GetAssetModifierType( const char* pszType )
{
for ( int i=0; i<AM_MAX; ++i )
{
if ( Q_strcmp( pszType, g_AssetModifiers[i] ) == 0 )
return (EAssetModifier) i;
}
return AM_Invalid;
}
CUniformRandomStream CEconItemSchema::m_RandomStream;
static void ParseCapability( item_capabilities_t &capsBitfield, KeyValues* pEntry )
{
COMPILE_TIME_ASSERT( ARRAYSIZE(g_Capabilities) == NUM_ITEM_CAPS );
int idx = StringFieldToInt( pEntry->GetName(), g_Capabilities, ARRAYSIZE(g_Capabilities) );
if ( idx < 0 )
{
return;
}
int bit = 1 << idx;
if ( pEntry->GetBool() )
{
(int&)capsBitfield |= bit;
}
else
{
(int&)capsBitfield &= ~bit;
}
}
//-----------------------------------------------------------------------------
// Purpose: interpret tint colors as integers for the values
//-----------------------------------------------------------------------------
static bool Helper_ExtractIntegerFromValueStringEntry( const char *szValue, int *pnValue )
{
if ( V_isdigit( *szValue ) )
{
*pnValue = V_atoi( szValue );
return true;
}
else if ( char const *szTint = StringAfterPrefix( szValue, "tint_") )
{
if ( !V_stricmp( szTint, "min" ) )
{
*pnValue = 1;
return true;
}
if ( !V_stricmp( szTint, "max" ) )
{
*pnValue = GEconItemSchema().GetGraffitiTintMaxValidDefID();
return true;
}
if ( const CEconGraffitiTintDefinition *pDef = GEconItemSchema().GetGraffitiTintDefinitionByName( szTint ) )
{
*pnValue = pDef->GetID();
return true;
}
return false;
}
return false;
}
//-----------------------------------------------------------------------------
// Purpose: interprets a string such as "1-3,5-20,45,46,47" and adds the
// individual uint32s into the specified vector.
//-----------------------------------------------------------------------------
static bool Helper_ExtractIntegersFromValuesString( const char * pszValues, CCopyableUtlVector< uint32 > &vecValues )
{
bool bResult = true;
CUtlVector< char* > vecValueStrings;
V_SplitString( pszValues, ",", vecValueStrings );
if ( vecValueStrings.Count() < 1 )
bResult = false;
FOR_EACH_VEC( vecValueStrings, i )
{
if ( V_strstr( vecValueStrings[ i ], "-" ) )
{
CUtlVector< char* > vecRangeStrings;
V_SplitString( vecValueStrings[ i ], "-", vecRangeStrings );
if ( vecRangeStrings.Count() == 2 )
{
int iRangeMin = 0, iRangeMax = 0;
if ( !Helper_ExtractIntegerFromValueStringEntry( vecRangeStrings[ 0 ], &iRangeMin ) ||
!Helper_ExtractIntegerFromValueStringEntry( vecRangeStrings[ 1 ], &iRangeMax ) ||
( iRangeMin >= iRangeMax ) )
{
bResult = false;
break;
}
for ( int j = iRangeMin; j <= iRangeMax; j++ )
{
vecValues.AddToTail( j );
}
}
else
{
bResult = false;
break;
}
vecRangeStrings.PurgeAndDeleteElements();
}
else
{
int iValue = 0;
if ( !Helper_ExtractIntegerFromValueStringEntry( vecValueStrings[ i ], &iValue ) )
{
bResult = false;
break;
}
vecValues.AddToTail( iValue );
}
}
vecValueStrings.PurgeAndDeleteElements();
return bResult;
}
#if defined ( CSTRIKE15 )
const uint32 g_unNumWearBuckets = 3;
uint64 Helper_GetAlternateIconKeyForWeaponPaintWearItem( item_definition_index_t nDefIdx, uint32 nPaintId, uint32 nWear )
{
return ( nDefIdx << 16 ) + ( nPaintId << 2 ) + nWear;
}
uint64 Helper_GetAlternateIconKeyForTintedStickerItem( uint32 nStickerKitID, uint32 unTintID )
{
unTintID = CombinedTintIDGetHSVID( unTintID );
return ( uint64( 1 ) << 32 ) | ( ( nStickerKitID & 0xFFFFFF ) << 8 ) | ( unTintID & 0xFF );
}
#endif
//-----------------------------------------------------------------------------
// Purpose: Constructor
//-----------------------------------------------------------------------------
CEconItemQualityDefinition::CEconItemQualityDefinition( void )
: m_nValue( INT_MAX )
, m_bCanSupportSet( false )
, m_unWeight( 0 )
, m_bExplicitMatchesOnly( false )
{
}
//-----------------------------------------------------------------------------
// Purpose: Copy constructor
//-----------------------------------------------------------------------------
CEconItemQualityDefinition::CEconItemQualityDefinition( const CEconItemQualityDefinition &that )
{
(*this) = that;
}
//-----------------------------------------------------------------------------
// Purpose: Operator=
//-----------------------------------------------------------------------------
CEconItemQualityDefinition &CEconItemQualityDefinition::operator=( const CEconItemQualityDefinition &rhs )
{
m_nValue = rhs.m_nValue;
m_strName = rhs.m_strName;
m_bCanSupportSet = rhs.m_bCanSupportSet;
m_unWeight = rhs.m_unWeight;
m_bExplicitMatchesOnly = rhs.m_bExplicitMatchesOnly;
return *this;
}
//-----------------------------------------------------------------------------
// Purpose: Initialize the quality definition
// Input: pKVQuality - The KeyValues representation of the quality
// schema - The overall item schema for this attribute
// pVecErrors - An optional vector that will contain error messages if
// the init fails.
// Output: True if initialization succeeded, false otherwise
//-----------------------------------------------------------------------------
bool CEconItemQualityDefinition::BInitFromKV( KeyValues *pKVQuality, CEconItemSchema &pschema, CUtlVector<CUtlString> *pVecErrors /* = NULL */ )
{
m_nValue = pKVQuality->GetInt( "value", -1 );
m_strName = pKVQuality->GetName();
m_bCanSupportSet = pKVQuality->GetBool( "canSupportSet" );
m_strHexColor = pKVQuality->GetString( "hexColor" );
m_unWeight = pKVQuality->GetInt( "weight", 0 );
// Check for required fields
SCHEMA_INIT_CHECK(
NULL != pKVQuality->FindKey( "value" ),
CFmtStr( "Quality definition %s: Missing required field \"value\"", pKVQuality->GetName() ) );
#if defined(CLIENT_DLL) || defined(GAME_DLL)
return SCHEMA_INIT_SUCCESS();
#endif
// Check for data consistency
SCHEMA_INIT_CHECK(
0 != Q_stricmp( GetName(), "any" ),
CFmtStr( "Quality definition any: The quality name \"any\" is a reserved keyword and cannot be used." ) );
SCHEMA_INIT_CHECK(
m_nValue != k_unItemQuality_Any,
CFmtStr( "Quality definition %s: Invalid value (%d). It is reserved for Any", GetName(), k_unItemQuality_Any ) );
return SCHEMA_INIT_SUCCESS();
}
//-----------------------------------------------------------------------------
// Purpose: Constructor
//-----------------------------------------------------------------------------
CEconItemRarityDefinition::CEconItemRarityDefinition( void )
: m_nValue( INT_MAX )
{
}
//-----------------------------------------------------------------------------
// Purpose: Copy constructor
//-----------------------------------------------------------------------------
CEconItemRarityDefinition::CEconItemRarityDefinition( const CEconItemRarityDefinition &that )
{
(*this) = that;
}
//-----------------------------------------------------------------------------
// Purpose: Operator=
//-----------------------------------------------------------------------------
CEconItemRarityDefinition &CEconItemRarityDefinition::operator=( const CEconItemRarityDefinition &rhs )
{
m_nValue = rhs.m_nValue;
m_strName = rhs.m_strName;
m_strLocKey = rhs.m_strLocKey;
m_strWepLocKey = rhs.m_strWepLocKey;
m_strLootList = rhs.m_strLootList;
m_strRecycleLootList = rhs.m_strRecycleLootList;
m_strDropSound = rhs.m_strDropSound;
m_strNextRarity = rhs.m_strNextRarity;
m_iWhiteCount = rhs.m_iWhiteCount;
m_iBlackCount = rhs.m_iBlackCount;
m_flWeight = rhs.m_flWeight;
return *this;
}
//-----------------------------------------------------------------------------
// Purpose: Initialize the rarity definition
//-----------------------------------------------------------------------------
bool CEconItemRarityDefinition::BInitFromKV( KeyValues *pKVRarity, CEconItemSchema &pschema, CUtlVector<CUtlString> *pVecErrors /* = NULL */ )
{
m_nValue = pKVRarity->GetInt( "value", -1 );
m_strName = pKVRarity->GetName();
m_strLocKey = pKVRarity->GetString( "loc_key" );
m_strWepLocKey = pKVRarity->GetString( "loc_key_weapon" );
m_iAttribColor = GetAttribColorIndexForName( pKVRarity->GetString( "color" ) );
m_strLootList = pKVRarity->GetString( "loot_list" ); // Not required.
m_strRecycleLootList = pKVRarity->GetString( "recycle_list" ); // Not required.
m_strDropSound = pKVRarity->GetString( "drop_sound" );
m_strNextRarity = pKVRarity->GetString( "next_rarity" ); // Not required.
m_flWeight = pKVRarity->GetFloat( "weight", 0 );
// Check for required fields
SCHEMA_INIT_CHECK(
NULL != pKVRarity->FindKey( "value" ),
CFmtStr( "Rarity definition %s: Missing required field \"value\"", pKVRarity->GetName() ) );
SCHEMA_INIT_CHECK(
NULL != pKVRarity->FindKey( "loc_key" ),
CFmtStr( "Rarity definition %s: Missing required field \"loc_key\"", pKVRarity->GetName() ) );
#if defined(CLIENT_DLL) || defined(GAME_DLL)
return SCHEMA_INIT_SUCCESS();
#endif
return SCHEMA_INIT_SUCCESS();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CEconColorDefinition::BInitFromKV( KeyValues *pKVColor, CEconItemSchema &pschema, CUtlVector<CUtlString> *pVecErrors /* = NULL */ )
{
m_strName = pKVColor->GetName();
m_strColorName = pKVColor->GetString( "color_name" );
m_strHexColor = pKVColor->GetString( "hex_color" );
SCHEMA_INIT_CHECK(
!m_strColorName.IsEmpty(),
CFmtStr( "Quality definition %s: missing \"color_name\"", GetName() ) );
SCHEMA_INIT_CHECK(
!m_strHexColor.IsEmpty(),
CFmtStr( "Quality definition %s: missing \"hex_color\"", GetName() ) );
return SCHEMA_INIT_SUCCESS();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CEconGraffitiTintDefinition::BInitFromKV( KeyValues *pKVColor, CEconItemSchema &pschema, CUtlVector<CUtlString> *pVecErrors /* = NULL */ )
{
m_nID = pKVColor->GetInt( "id" );
m_strColorName = pKVColor->GetName();
m_strHexColor = pKVColor->GetString( "hex_color" );
SCHEMA_INIT_CHECK(
!m_strColorName.IsEmpty(),
CFmtStr( "Graffiti tint definition #%d %s: missing name", GetID(), GetColorName() ) );
SCHEMA_INIT_CHECK(
!m_strHexColor.IsEmpty() && ( m_strHexColor.Get()[0] == '#' ),
CFmtStr( "Graffiti tint #%d %s: missing or invalid \"hex_color\"", GetID(), GetColorName() ) );
m_unHexColorRGB = ( !m_strHexColor.IsEmpty() ) ? V_atoi( CFmtStr( "0x%s", m_strHexColor.Get() + 1 ) ) : 0;
return !m_strColorName.IsEmpty() && !m_strHexColor.IsEmpty() && ( m_strHexColor.Get()[0] == '#' );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CEconMusicDefinition::BInitFromKV( KeyValues *pKVMusicDef, CEconItemSchema &pschema, CUtlVector<CUtlString> *pVecErrors /* = NULL */ )
{
nID = Q_atoi( pKVMusicDef->GetName() );
m_strName = pKVMusicDef->GetString( "name" );
m_strNameLocToken = pKVMusicDef->GetString( "loc_name" );
m_strLocDescription = pKVMusicDef->GetString( "loc_description" );
m_strPedestalDisplayModel = pKVMusicDef->GetString( "pedestal_display_model" );
m_strInventoryImage = pKVMusicDef->GetString( "image_inventory" );
SCHEMA_INIT_CHECK(
( nID > 0 ),
CFmtStr( "Music definition %s: name must be a positive integer", GetName() ) );
SCHEMA_INIT_CHECK(
!m_strName.IsEmpty(),
CFmtStr( "Music definition %s: missing \"name\"", GetName() ) );
return SCHEMA_INIT_SUCCESS();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CEconQuestDefinition::BInitFromKV( KeyValues *pKVQuestDef, CEconItemSchema &pschema, CUtlVector<CUtlString> *pVecErrors /* = NULL */ )
{
//TODO - do we care to parse quest definitions on GC at all or only on game server and client?
nID = Q_atoi( pKVQuestDef->GetName() );
SCHEMA_INIT_CHECK(
( nID > 0 ),
CFmtStr( "Quest definition %s: name must be a positive integer", GetName() ) );
m_strName = pKVQuestDef->GetString( "name" );
SCHEMA_INIT_CHECK(
!m_strName.IsEmpty(),
CFmtStr( "Quest definition %s: missing \"name\"", GetName() ) );
m_strGameMode = pKVQuestDef->GetString( "gamemode" );
m_strMapGroup = pKVQuestDef->GetString( "mapgroup" );
m_strMap = pKVQuestDef->GetString( "map" );
#ifndef GC_DLL
static bool bLoadBannedWords = ( !!CommandLine()->FindParm( "-usebanlist" ) ) || (
!!CommandLine()->FindParm( "-perfectworld" )
);
if ( bLoadBannedWords && !V_stricmp( m_strMap.Get(), "ar_monastery" ) )
m_strMap = "ar_shoots";
#endif
m_bIsAnEvent = pKVQuestDef->GetBool( "is_an_event", false );
// Campaign quests require client-exposed points remaining.
// load non-GC only points if it exists.
// Otherwise look for "gc_generation_settings/points"
// quest events are always 1 point.
if ( m_bIsAnEvent )
{
m_vecQuestPoints.AddToTail( 1 );
}
else
{
const char* pszPointsRemaining = pKVQuestDef->GetString( "points" );
m_vecQuestPoints.Purge();
if ( pszPointsRemaining && pszPointsRemaining[ 0 ] )
{
SCHEMA_INIT_CHECK(
Helper_ExtractIntegersFromValuesString( pszPointsRemaining, m_vecQuestPoints ),
CFmtStr( "Quest definition %s specifies a malformed values range: %s", GetName(), pszPointsRemaining ) );
}
else
{
// if points aren't specified then default to '1'
m_vecQuestPoints.AddToTail( 1 );
}
}
// campaigns use quest-defined rewards rather than attributes rolled later.
m_strRewardLootList = pKVQuestDef->GetString( "quest_reward" );
m_nDifficulty = pKVQuestDef->GetInt( "difficulty", 1 );
m_nOperationalPoints = pKVQuestDef->GetInt( "operational_points", 0 );
if ( !m_bIsAnEvent )
{
m_nXPReward = pKVQuestDef->GetInt( "xp_reward", ( m_nDifficulty + 1 ) * 100 ); // campaign quest: 1 = 200, 2 = 300, 3 = 400
}
else
{
m_nXPReward = pKVQuestDef->GetInt( "gc_generation_settings/xp_reward", 1 ); // default quest event xp reward to 1 per action
}
m_nTargetTeam = pKVQuestDef->GetInt( "target_team", 0 );
m_strExpr = pKVQuestDef->GetString( "expression" );
// BONUS
m_strBonusExpr = pKVQuestDef->GetString( "expr_bonus" );
// set bonus percent only if a bonus expression exists
if ( !m_strBonusExpr.IsEmpty() )
{
m_nXPBonusPercent = pKVQuestDef->GetInt( "xp_bonus_percent", 100 ); // default bonus is 100%
}
else
{
m_nXPBonusPercent = 0;
}
#ifdef CLIENT_DLL // strings
m_strNameLocToken = pKVQuestDef->GetString( "loc_name" );
if ( !m_strNameLocToken.IsEmpty() )
HelperValidateLocalizationStringToken( m_strNameLocToken );
m_strShortNameLocToken = pKVQuestDef->GetString( "loc_shortname" );
if ( !m_strShortNameLocToken.IsEmpty() )
HelperValidateLocalizationStringToken( m_strShortNameLocToken );
m_strDescriptionLocToken = pKVQuestDef->GetString( "loc_description" );
if ( !m_strDescriptionLocToken.IsEmpty() )
HelperValidateLocalizationStringToken( m_strDescriptionLocToken );
m_strHudDescriptionLocToken = pKVQuestDef->GetString( "loc_huddescription" );
if ( !m_strHudDescriptionLocToken.IsEmpty() )
HelperValidateLocalizationStringToken( m_strHudDescriptionLocToken );
// Localizers have request the ability to explicitly write each quest's description because the
// programatic string construction in place is not flexible enough to accommodate various grammars.
//
// Description will defer to this string, if it exists. It should not be used in CSGO_English.
if ( g_pVGuiLocalize->Find( CFmtStr( "Override_Quest_Description_%s", pKVQuestDef->GetName( ) ) ) )
m_strDescriptionLocToken = CFmtStr( "Override_Quest_Description_%s", pKVQuestDef->GetName( ) );
m_strLocBonus = pKVQuestDef->GetString( "loc_bonus" );
if ( !m_strLocBonus.IsEmpty() )
HelperValidateLocalizationStringToken( m_strLocBonus );
m_strIcon = pKVQuestDef->GetString( "quest_icon" );
// Quests used to use explicitly define "string_tokens" in their schema definitions.
// Now the string tokens kv is populated by interpreting the expression
// but we may still want to add tokens manually in the future.
m_kvStringTokens = pKVQuestDef->FindKey( "string_tokens" );
if ( !m_kvStringTokens )
m_kvStringTokens = new KeyValues( "string_tokens" );
KeyValues * pkvExpressionTokens = new KeyValues( "ExpressionTokens" );
KeyValues::AutoDelete autodelete_pKVExpressionTokens( pkvExpressionTokens );
TokenizeQuestExpression( m_strExpr, pkvExpressionTokens );
Assert( pkvExpressionTokens->GetFirstSubKey() != NULL );
PopulateQuestStringTokens( *this, *pkvExpressionTokens, *m_kvStringTokens );
if ( !m_strBonusExpr.IsEmpty() )
{
KeyValues * pKVBonusExpressionTokens = new KeyValues( "BonusExpressionTokens" );
KeyValues::AutoDelete autodelete_pKVBonusExpressionTokens( pKVBonusExpressionTokens );
TokenizeQuestExpression( m_strBonusExpr, pKVBonusExpressionTokens );
Assert( pKVBonusExpressionTokens->GetFirstSubKey() != NULL );
PopulateQuestStringTokens( *this, *pKVBonusExpressionTokens, *m_kvStringTokens, true );
}
#endif // ifndef GAME_DLL
// MAPGROUP
//
//
bool bMapGroupValid = false;
if ( m_strMapGroup.IsEmpty() ) { bMapGroupValid = true; } // "": empty mapgroups are technically valid
/** Removed for partner depot **/
AssertMsg2( bMapGroupValid, "QUEST %d references a non-existant mapgroup: %s.\n", nID, m_strMapGroup.Get() );
#ifndef GAME_DLL // strings
KeyValues *pGameModestxt = new KeyValues( "GameModes.txt" );
KeyValues::AutoDelete autodelete( pGameModestxt );
if ( !pGameModestxt->LoadFromFile( g_pFullFileSystem, "GameModes.txt" ) )
{
pGameModestxt = NULL;
}
// MAP
//
//
// get map string from gamemodes.txt and insert it into m_kvStringTokens
//
//
if ( !m_strMap.IsEmpty( ) && StringIsEmpty( m_kvStringTokens->GetString( "map" ) ) )
{
KeyValues *pMaps = pGameModestxt->FindKey( "maps" );
if ( pMaps )
{
KeyValues *pMapKV = pMaps->FindKey( m_strMap );
Assert( pMapKV );
if ( pMapKV )
{
m_kvStringTokens->SetString( "map", pMapKV->GetString( "nameID" ) );
m_kvStringTokens->SetString( "location", pMapKV->GetString( "nameID" ) );
}
}
}
// get mapgroup string from gamemodes.txt and insert it into m_kvStringTokens
//
//
if ( !m_strMapGroup.IsEmpty() && StringIsEmpty( m_kvStringTokens->GetString( "mapgroup" ) ) )
{
KeyValues *pMapGroups = pGameModestxt->FindKey( "mapgroups" );
if ( pMapGroups )
{
KeyValues *pMapGroup = pMapGroups->FindKey( m_strMapGroup );
Assert( pMapGroup );
if ( pMapGroup )
{
m_kvStringTokens->SetString( "mapgroup", pMapGroup->GetString( "nameID" ) );
}
// if we didn't specify a map then use the mapgroup as the location
if ( !m_kvStringTokens->FindKey("location") )
{
m_kvStringTokens->SetString( "location", pMapGroup->GetString( "nameID" ) );
}
}
}
//GAMEMODE
//
//
#ifdef DBGFLAG_ASSERT
const char * szType;
bool bGameModeValid = g_pGameTypes->GetGameTypeFromMode( m_strGameMode, szType );
AssertMsg2( bGameModeValid, "QUEST %d references a non-existant gamemode: %s.\n", nID, m_strGameMode );
#endif
if ( StringIsEmpty( m_kvStringTokens->GetString( "gamemode" ) ) )
{
// get gamemode string from gamemodes.txt and insert it into m_kvStringTokens
//
//
KeyValues *pGameTypes = pGameModestxt->FindKey( "gameTypes" );
FOR_EACH_SUBKEY( pGameTypes, kvGameType )
{
KeyValues *pGameModes = kvGameType->FindKey( "GameModes" );
if ( pGameModes )
{
KeyValues *pGameMode = pGameModes->FindKey( m_strGameMode );
if ( pGameMode )
{
m_kvStringTokens->SetString( "gamemode", pGameModes->FindKey( m_strGameMode )->GetString( "NameID" ) );
break;
}
}
}
}
// check all strings in m_kvStringTokens
FOR_EACH_VALUE( m_kvStringTokens, pKVStringToken )
{
const char* token = pKVStringToken->GetString();
HelperValidateLocalizationStringToken( token );
}
FOR_EACH_TRUE_SUBKEY( m_kvStringTokens, pKVSubKey )
{
FOR_EACH_VALUE( pKVSubKey, pKVStringToken )
{
const char* token = pKVStringToken->GetString( );
HelperValidateLocalizationStringToken( token );
}
}
#endif // not GAME_DLL
return SCHEMA_INIT_SUCCESS();
}
#ifdef CLIENT_DLL
///////////////////////////////////////////////////////////////////////////////////////////
//
// read the expression ( i.e. " %act_kill_human% && %cond_gun_borrowed% && %weapon_smg% )
// and insert appropriate string tokens into the string token keyvalues structure
//
////////////////////////////////////////////////////////////////////////////////////////////
void CEconQuestDefinition::PopulateQuestStringTokens( CEconQuestDefinition &questDef, KeyValues &kvExpressionTokens, KeyValues &kvStringTokens, bool bBonus )
{
// 1. Validate that every token ( %TOKEN% ) is accounted for.
// 2. Populate pkvStringTokens with tokens derived from expression keywords e.g. %weapon_ak47%
KeyValues * pkvExpressionTokens = &kvExpressionTokens;
int nItemCount = 0;
FOR_EACH_SUBKEY( pkvExpressionTokens, kvSubKey )
{
bool bFound = false;
if ( V_stristr( kvSubKey->GetName(), "act_" ) || V_stristr( kvSubKey->GetName(), "cond_" ) )
{
// validate that action or condition is recognized.
for ( int i = 0; i < k_EQuestVar_Last; i++ )
{
if ( FStrEq( g_arrQuestVars[ i ], kvSubKey->GetName() ) )
{
if ( g_pVGuiLocalize->Find( CFmtStr( "#quest_action_singular_%s", kvSubKey->GetName() ) ) )
{
if ( !kvStringTokens.FindKey( "action" ) )
{
kvStringTokens.SetString( "action", CFmtStr( "#quest_action_singular_%s", kvSubKey->GetName() ) );
}
if ( !kvStringTokens.FindKey( "actions" ) )
{
kvStringTokens.SetString( "actions", CFmtStr( "#quest_action_plural_%s", kvSubKey->GetName() ) );
}
}
/** Removed for partner depot **/
// note that we have at least one generic weapon if we use any item condition.
if ( V_stristr( kvSubKey->GetName( ), "cond_item" ) )
{
if ( nItemCount == 0 ) nItemCount = 1;
}
// debug kvStringTokens.SaveToFile( g_pFullFileSystem, "~expressiontokens.txt" );
// debug pkvExpressionTokens->SaveToFile( g_pFullFileSystem, "~expressiontokens.txt" );
bFound = true;
break;
}
}
}
else if ( V_stristr( kvSubKey->GetName(), "weapon_" ) )
{
// find the items subkey or create one if it doesnt exist
KeyValues* pkvItems = kvStringTokens.FindKey( "items" );
if ( !pkvItems )
{
pkvItems = new KeyValues( "items" );
kvStringTokens.AddSubKey( pkvItems );
}
CEconItemDefinition * pWeaponItemDef = GetItemSchema( )->GetItemDefinitionByName( kvSubKey->GetName( ) );
if ( pWeaponItemDef )
{
// LEGACY PRE-2016
if ( !kvStringTokens.FindKey( "weapon" ) )
{
kvStringTokens.SetString( "weapon", pWeaponItemDef->GetItemBaseName() );
}
// LEGACY PRE-2016
// insert the weapon into the 'items' true subkey
for ( int i = 1; i < 20; i++ )
{
const char * szItemKeyName = CFmtStr( "item%d", i );
if ( !pkvItems->FindKey( szItemKeyName ) )
{
pkvItems->SetString( szItemKeyName, pWeaponItemDef->GetItemBaseName( ) );
nItemCount++;
break;
}
}
bFound = true;
// set the quest icon to the weapon if an icon was not explicitly set.
if ( questDef.m_strIcon.IsEmpty( ) && !bBonus )
{
questDef.m_strIcon = kvSubKey->GetName() + WEAPON_CLASSNAME_PREFIX_LENGTH;
}
}
else
{
// look for a weapon category/type
for ( int i = 0; i < LOADOUT_POSITION_COUNT; i++ )
{
if ( V_stristr( kvSubKey->GetName(), CFmtStr( "weapon_%s", g_szLoadoutStrings[ i ] ) ) )
{
// LEGACY PRE-2016
if ( !kvStringTokens.FindKey( "weapon" ) )
{
kvStringTokens.SetString( "weapon", g_szLoadoutStringsForDisplay[ i ] );
}
// LEGACY PRE-2016
// insert the weapon into the 'items' true subkey
for ( int j = 1; j < 20; j++ )
{
const char * szItemKeyName = CFmtStr( "item%d", j );
if ( !pkvItems->FindKey( szItemKeyName ) )
{
const char * szLoadoutStringNoHashMark = g_szLoadoutStringsForDisplay[ i ] + 1;
pkvItems->SetString( szItemKeyName, CFmtStr( "#quest_%s", szLoadoutStringNoHashMark ) );
nItemCount++;
break;
}
}
bFound = true;
break;
}
}
}
}
else if ( V_stristr( kvSubKey->GetName(), "set_" ) )
{
for ( int i = 0; i < GetItemSchema()->GetItemSetCount(); i++ )
{
const CEconItemSetDefinition *pItemSetDef = GetItemSchema()->GetItemSetByIndex( i );
if ( pItemSetDef && FStrEq( kvSubKey->GetName(), pItemSetDef->GetName() ) )
{
if ( !kvStringTokens.FindKey( "set" ) )
{
kvStringTokens.SetString( "set", CFmtStr( "#CSGO_%s", pItemSetDef->GetName() ) );
}
bFound = true;
break;
}
}
}
else if ( V_stristr( kvSubKey->GetName(), "map_" ) )
{
/** Removed for partner depot **/
}
// debug save
// kvStringTokens.SaveToFile( g_pFullFileSystem, "~expressiontokens.txt" );
AssertMsg2( bFound, "QUEST %u refs a non-existant var: %s\n", questDef.GetID(), kvSubKey->GetName() );
}
// defaults
if ( !kvStringTokens.FindKey( "commandverb" ) )
{
kvStringTokens.SetString( "commandverb", "#quest_commandverb_default" );
}
if ( !kvStringTokens.FindKey( "target" ) )
{
kvStringTokens.SetString( "target", "#emptystring" );
}
if ( !kvStringTokens.FindKey( "item_quality" ) )
{
kvStringTokens.SetString( "item_quality", "#emptystring" );
}
// no items were specified but due to specified item conditions we know we need one.
if ( !kvStringTokens.FindKey( "items/item1" ) && ( nItemCount > 0 ) )
{
kvStringTokens.SetString( "items/item1", "#quest_item_default" );
}
// no description was specified so use one of the default ones.
if ( questDef.m_strDescriptionLocToken.IsEmpty( ) && !bBonus )
{
questDef.m_strDescriptionLocToken = CFmtStr( "quest_default_%d_items_desc", nItemCount );
}
// no quest tracker description was specified, so use one of the default ones.
if ( questDef.m_strHudDescriptionLocToken.IsEmpty( ) && !bBonus )
{
questDef.m_strHudDescriptionLocToken = CFmtStr( "quest_default_hud_%d_items_desc", nItemCount );
}
// kvStringTokens.SaveToFile( g_pFullFileSystem, "~stringtokens.txt" ); // debug only
}
#endif
// NOTE: This does not detect syntax or unrecognized symbols. TODO: Still need to figure out how to detect those.
bool CEconQuestDefinition::IsQuestExpressionValid( const char * pszQuestExpr )
{
if ( !pszQuestExpr || !pszQuestExpr[0] )
return false;
CExpressionCalculator expr = CExpressionCalculator( pszQuestExpr );
ZeroOutQuestExpressionVariables( expr );
// This seemed like a good idea but it doesn't work.
//
// for ( int i = 0; i < expr.VariableCount(); i++ )
// {
// if ( !StringIsEmpty( expr.GetVariableName( i ) ) && expr.GetVariableValue( i ) != 1.0 )
// {
// AssertMsg1( 0, "unknown variable %s in quest expression", expr.GetVariableName( i ) );
// return false;
// }
// }
float flResult;
bool bSuccess = expr.Evaluate( flResult );
return ( bSuccess && IsFinite(flResult) );
}
void CEconQuestDefinition::SetQuestExpressionVariable( CExpressionCalculator &expQuest, EQuestVar_t questVar, float flValue )
{
expQuest.SetVariable( CFmtStr( "%%%s%%", g_arrQuestVars[ questVar ] ), flValue );
}
void CEconQuestDefinition::ZeroOutQuestExpressionVariables( CExpressionCalculator &expr )
{
// it's not possible to iterate through expression variables but this would be ideal. VariableCount() returns 1 :/
// expQuest.BuildVariableListFromExpression();
//
// for ( int i = 0; i < expQuest.VariableCount(); i++ )
// {
// expQuest.SetVariable( i, 0 );
// }
// ACTIONS and CONDITIONS
/** Removed for partner depot **/
// WEAPON TYPES
for ( int i = 0; i < LOADOUT_POSITION_COUNT; i++ )
{
expr.SetVariable( CFmtStr( "%%weapon_%s%%", g_szLoadoutStrings[ i ] ), 0 );
}
// SETS
for ( int i = 0; i < GetItemSchema()->GetItemSetCount(); i++ )
{
const CEconItemSetDefinition *pItemSetDef = GetItemSchema()->GetItemSetByIndex( i );
expr.SetVariable( CFmtStr( "%%%s%%", pItemSetDef->GetName() ), 0 );
}
// WEAPONS
static CUtlVector< const CEconItemDefinition * > s_vecWeaponItemDefs;
// cache these items so we don't do string compares every time we initialize an expression
if ( s_vecWeaponItemDefs.Count() == 0 )
{
FOR_EACH_MAP_FAST( GetItemSchema()->GetItemDefinitionMap(), i )
{
const CEconItemDefinition * pItemDef = GetItemSchema()->GetItemDefinitionByMapIndex( i );
if ( V_stristr( pItemDef->GetDefinitionName(), "weapon_" ) )
{
s_vecWeaponItemDefs.AddToTail( pItemDef );
}
}
}
FOR_EACH_VEC( s_vecWeaponItemDefs, i )
{
expr.SetVariable( CFmtStr( "%%%s%%", s_vecWeaponItemDefs[ i ]->GetDefinitionName() ), 0 );
}
// MAPS
/** Removed for partner depot **/
}
bool CEconCampaignDefinition::BInitFromKV( KeyValues *pKVCampaignDef, CEconItemSchema &pschema, CUtlVector<CUtlString> *pVecErrors /*= NULL */ )
{
m_nID = Q_atoi( pKVCampaignDef->GetName() );
SCHEMA_INIT_CHECK(
( m_nID > 0 ),
CFmtStr( "Campaign definition %d: name must be a positive integer", GetID() ) );
m_strNameLocToken = pKVCampaignDef->GetString( "loc_name" );
HelperValidateLocalizationStringToken( m_strNameLocToken );
m_strLocDescription = pKVCampaignDef->GetString( "loc_description" );
HelperValidateLocalizationStringToken( m_strLocDescription );
m_nSeasonNumber = pKVCampaignDef->GetInt( "season_number" );
SCHEMA_INIT_CHECK( m_nSeasonNumber > 0, CFmtStr( "Campaign %d must specifiy a season number", GetID() ) );
FOR_EACH_TRUE_SUBKEY( pKVCampaignDef, pKVCampaignNode )
{
CEconCampaignNodeDefinition *pNewCampaignNodeDef = new CEconCampaignNodeDefinition;
int CampaignNodeIndex = Q_atoi( pKVCampaignNode->GetName() );
uint32 index = m_mapCampaignNodes.Find( CampaignNodeIndex );
if ( !m_mapCampaignNodes.IsValidIndex( index ) )
{
m_mapCampaignNodes.Insert( CampaignNodeIndex, pNewCampaignNodeDef );
SCHEMA_INIT_SUBSTEP( pNewCampaignNodeDef->BInitFromKV( GetID(), pKVCampaignNode, pschema ) );
}
else
{
SCHEMA_INIT_CHECK(
false,
CFmtStr( "Campaign definition %d: campaign node index %d is not unique", GetID(), CampaignNodeIndex ) );
}
}
// go back and verify that all nodes point to valid nodes
FOR_EACH_MAP_FAST( m_mapCampaignNodes, i )
{
const CUtlVector< uint32 >& pNextNodes = m_mapCampaignNodes.Element( i )->GetNextNodes();
FOR_EACH_VEC( pNextNodes, j )
{
int nextnode = pNextNodes[ j ];
int index = m_mapCampaignNodes.Find( nextnode );
SCHEMA_INIT_CHECK(
m_mapCampaignNodes.IsValidIndex( index ),
CFmtStr( "Campaign definition %d: campaign node index %d leads to an invalid campaign node %d", GetID(), m_mapCampaignNodes.Element( i )->GetID(), nextnode ) );
}
}
// build list of start nodes ( nodes that have no predecessors )
m_mapStartNodes.Purge();
// collect all nodes
FOR_EACH_MAP_FAST( m_mapCampaignNodes, i )
{
m_mapStartNodes.Insert( m_mapCampaignNodes.Key( i ), m_mapCampaignNodes.Element( i ) );
}
// subtract nodes referenced as successors
FOR_EACH_MAP_FAST( m_mapCampaignNodes, i )
{
const CUtlVector< uint32 >& pCampaignNodes = m_mapCampaignNodes.Element( i )->GetNextNodes();
FOR_EACH_VEC( pCampaignNodes, j )
{
int nextnode = pCampaignNodes.Element( j );
m_mapStartNodes.Remove( nextnode );
}
}
return SCHEMA_INIT_SUCCESS();
}
void CEconCampaignDefinition::GetAccessibleCampaignNodes( const uint32 unCampaignCompletionBitfield, CUtlVector< CEconCampaignNodeDefinition* > &vecAccessibleNodes )
{
vecAccessibleNodes.Purge();
// If no campaign quests have been done yet then the only possible quests are the starting quests.
if ( unCampaignCompletionBitfield == 0 )
{
FOR_EACH_MAP_FAST( GetStartNodes(), i )
{
vecAccessibleNodes.AddToTail( GetStartNodes().Element( i ) );
}
}
else
{
// otherwise recursively look for quests that are successors of completed quests.
FOR_EACH_MAP_FAST( GetStartNodes(), i )
{
uint32 nCampaignNodeIndex = GetStartNodes().Key( i );
// is this start quest already complete?
if ( ( 1 << ( nCampaignNodeIndex - 1 ) ) & unCampaignCompletionBitfield )
{
// yes. recurse.
Helper_RecursiveGetAccessibleCampaignNodes( unCampaignCompletionBitfield, GetStartNodes().Element( i ), vecAccessibleNodes );
}
else
{
// no. add it to the list and return;
vecAccessibleNodes.AddToTail( GetStartNodes().Element( i ) );
}
}
}
}
void CEconCampaignDefinition::Helper_RecursiveGetAccessibleCampaignNodes( const uint32 unCampaignCompletionBitfield, const CEconCampaignNodeDefinition* pNode, CUtlVector< CEconCampaignNodeDefinition* > &vecAccessibleNodes )
{
// recursively look for nodes that don't represent completed quests. Do not recurse incomplete quests.
FOR_EACH_VEC( pNode->GetNextNodes(), i )
{
uint32 unNextNodeIndex = pNode->GetNextNodes().Element( i );
int index = GetCampaignNodes().Find( unNextNodeIndex );
if ( GetCampaignNodes().IsValidIndex( index ) )
{
CEconCampaignDefinition::CEconCampaignNodeDefinition* pNextNode = GetCampaignNodes().Element( index );
// is this successor quest already complete?
if ( ( 1 << ( unNextNodeIndex - 1 ) ) & unCampaignCompletionBitfield )
{
// yes. recurse.
Helper_RecursiveGetAccessibleCampaignNodes( unCampaignCompletionBitfield, pNextNode, vecAccessibleNodes );
}
else
{
// no. add it to the list and return;
vecAccessibleNodes.AddToTail( pNextNode );
}
}
}
}
bool ResolveQuestIdToCampaignAndIndex( uint16 unQuestID, uint32 &unCampaignID, uint32 &unCamapaignNodeID )
{
static bool s_bQuestMappingInitialized = false;
static CUtlMap< uint16, uint64, uint16, CDefLess< uint16 > > s_mapping;
if ( !s_bQuestMappingInitialized )
{
s_bQuestMappingInitialized = true;
for ( uint32 unPossibleCampaignID = 1; unPossibleCampaignID <= g_nNumCampaigns; ++unPossibleCampaignID )
{
CEconCampaignDefinition const *pCampaignDef = GetItemSchema()->GetCampaignDefinition( unPossibleCampaignID );
if ( !pCampaignDef )
continue;
FOR_EACH_MAP_FAST( pCampaignDef->GetCampaignNodes(), iCN )
{
CEconCampaignDefinition::CEconCampaignNodeDefinition const &node = *pCampaignDef->GetCampaignNodes().Element( iCN );
uint16 unMappingKey = node.GetQuestIndex();
if ( unMappingKey )
{
uint64 unMappingValue = ( uint64( unPossibleCampaignID ) << 32 ) | uint64( node.GetID() );
Assert( s_mapping.Find( unMappingKey ) == s_mapping.InvalidIndex() );
s_mapping.InsertOrReplace( unMappingKey, unMappingValue );
}
}
}
}
uint16 idxMapping = s_mapping.Find( unQuestID );
if ( idxMapping == s_mapping.InvalidIndex() )
return false;
unCampaignID = uint32( s_mapping.Element( idxMapping ) >> 32 );
unCamapaignNodeID = uint32( s_mapping.Element( idxMapping ) );
return true;
}
bool CEconCampaignDefinition::CEconCampaignNodeDefinition::BInitFromKV( int nCampaignIndex, KeyValues *pKVCampaignNodeDef, CEconItemSchema &pschema, CUtlVector<CUtlString> *pVecErrors /*= NULL */ )
{
m_nID = Q_atoi( pKVCampaignNodeDef->GetName() );
SCHEMA_INIT_CHECK(
( m_nID > 0 ),
CFmtStr( "Campaign Node definition %d: name must be a positive integer", GetID() ) );
m_CampaignID = nCampaignIndex;
m_nQuestIndex = Q_atoi( pKVCampaignNodeDef->GetString( "quest_index" ) );
if ( m_nQuestIndex ) // validate only if specified, story-only nodes don't have quest indices
{
SCHEMA_INIT_CHECK(
( GetItemSchema()->GetQuestDefinition( m_nQuestIndex ) != NULL ),
CFmtStr( "Campaign definition %d, Node definition %d: Quest Index %d is not a valid quest index", nCampaignIndex, m_nID, m_nQuestIndex ) );
}
m_vecNextNodes.Purge();
FOR_EACH_SUBKEY( pKVCampaignNodeDef, pKVCampaignNodeKey )
{
if ( !V_strcmp( pKVCampaignNodeKey->GetName(), "->" ) )
{
m_vecNextNodes.AddToTail( Q_atoi( pKVCampaignNodeKey->GetString() ) );
}
}
#ifdef CLIENT_DLL
FOR_EACH_TRUE_SUBKEY( pKVCampaignNodeDef, pKVCampaignNodeKey )
{
if ( FStrEq( pKVCampaignNodeKey->GetName(), "story_block" ) )
{
CEconCampaignNodeStoryBlockDefinition *pNewCampaignNodeStoryBlockDef = new CEconCampaignNodeStoryBlockDefinition;
m_vecStoryBlocks.AddToTail( pNewCampaignNodeStoryBlockDef );
SCHEMA_INIT_SUBSTEP( pNewCampaignNodeStoryBlockDef->BInitFromKV( nCampaignIndex, m_nID, pKVCampaignNodeKey, pschema ) );
}
}
#endif
return SCHEMA_INIT_SUCCESS();
}
#ifdef CLIENT_DLL
// score every story block and return the highest scoring one
CEconCampaignDefinition::CEconCampaignNodeDefinition::CEconCampaignNodeStoryBlockDefinition * CEconCampaignDefinition::CEconCampaignNodeDefinition::GetBestScoringStoryBlock( CEconItemView *pCampaignCoin ) const
{
CEconCampaignDefinition::CEconCampaignNodeDefinition::CEconCampaignNodeStoryBlockDefinition * pBestStoryBlock = NULL;
float flBestStoryBlockScore = 0;
FOR_EACH_VEC( GetStoryBlocks(), iSB )
{
float flCurStoryBlockScore = GetStoryBlocks()[ iSB ]->EvaluateStoryBlockExpression( pCampaignCoin );
// only consider story block expressions that have a positive score
if ( flCurStoryBlockScore > 0 )
{
if ( !pBestStoryBlock )
{
pBestStoryBlock = GetStoryBlocks()[ iSB ];
flBestStoryBlockScore = flCurStoryBlockScore;
}
else
{
if ( flCurStoryBlockScore > flBestStoryBlockScore )
{
pBestStoryBlock = GetStoryBlocks()[ iSB ];
flBestStoryBlockScore = flCurStoryBlockScore;
}
}
}
}
return pBestStoryBlock;
}
bool CEconCampaignDefinition::CEconCampaignNodeDefinition::CEconCampaignNodeStoryBlockDefinition::BInitFromKV( int nCampaignIndex, int nNodeID, KeyValues * pKVCampaignNodeStoryBlockDef, CEconItemSchema &pschema, CUtlVector<CUtlString> *pVecErrors /* = NULL */ )
{
m_strContentFile = pKVCampaignNodeStoryBlockDef->GetString( "content_file" );
m_strCharacterName = pKVCampaignNodeStoryBlockDef->GetString( "character_name" );
m_strDescription = pKVCampaignNodeStoryBlockDef->GetString( "description" );
if ( !m_strDescription.IsEmpty() )
HelperValidateLocalizationStringToken( m_strDescription );
m_strExpr = pKVCampaignNodeStoryBlockDef->GetString( "expression" );
return SCHEMA_INIT_SUCCESS();
}
// Take the expression of an CExpressionCalculator, such as " %weapon_ak47% && %kill% " and extract the % delimited keywords.
//
void CEconQuestDefinition::TokenizeQuestExpression( const char * szExpression, KeyValues * pKVExpressionTokens )
{
if ( !szExpression || !szExpression[0] )
return;
if ( !pKVExpressionTokens )
return;
const char * pCur = szExpression;
char szKeyword[ 256 ] = { '\0' };
char *pCurCopy = szKeyword;
bool bReadingKeyword = false;
while ( *pCur != '\0' )
{
if ( *pCur == '%' && !bReadingKeyword )
{
bReadingKeyword = true;
// reset destination buffer;
V_memset( szKeyword, '\0', sizeof( szKeyword ) );
pCurCopy = szKeyword;
// step past '%'
pCur++;
}
else if ( *pCur == '%' && bReadingKeyword )
{
// end of the keyword
bReadingKeyword = false;
// terminate the string with a null char and then look it up
pCurCopy++;
*pCurCopy = '\0';
pKVExpressionTokens->SetBool( szKeyword, true );
// step past '%'
pCur++;
}
if ( bReadingKeyword )
{
// keep copying the keyword until we reach the terminating '%'
*pCurCopy = *pCur;
pCurCopy++;
pCur++;
}
else
{
// keep walking until we find the start of a keyword: '%'
pCur++;
}
}
}
float CEconCampaignDefinition::CEconCampaignNodeDefinition::CEconCampaignNodeStoryBlockDefinition::EvaluateStoryBlockExpression( CEconItemView *pCampaignCoin ) const
{
// if there's no expression specified then return a minimally true value.
if ( m_strExpr.IsEmpty() )
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
// over any block with no expression.
CExpressionCalculator expStoryBlock = CExpressionCalculator( GetStoryBlockExpression() );
KeyValues * pKVExpressionTokens = new KeyValues( "ExpressionTokens" );
KeyValues::AutoDelete autodelete_pKVExpressionTokens( pKVExpressionTokens );
CEconQuestDefinition::TokenizeQuestExpression( GetStoryBlockExpression(), pKVExpressionTokens );
// go through each referenced quest and set the variable %questid% to the value of its completion state ( 0 or 1 )
FOR_EACH_SUBKEY( pKVExpressionTokens, kvSubKey )
{
uint16 unQuestID = V_atoi( kvSubKey->GetName() );
uint32 unCampaignID;
uint32 unCampaignNodeID;
ResolveQuestIdToCampaignAndIndex( unQuestID, unCampaignID, unCampaignNodeID );
const CSchemaAttributeDefHandle pAttr_CampaignCompletionBitfield = GetCampaignAttributeDefHandle( unCampaignID, k_ECampaignAttribute_CompletionBitfield );
uint32 unCampaignCompletionBitfield;
if ( !pCampaignCoin->FindAttribute( pAttr_CampaignCompletionBitfield, &unCampaignCompletionBitfield ) )
return 0;
uint32 unMask = ( 1 << ( unCampaignNodeID - 1 ) );
const char * szKeyWord = CFmtStr( "%%%s%%", kvSubKey->GetName() );
float flCompletionState = ( unCampaignCompletionBitfield & unMask ) ? 1.0 : 0.0;
expStoryBlock.SetVariable( szKeyWord, flCompletionState );
}
expStoryBlock.BuildVariableListFromExpression();
float flReturn;
expStoryBlock.Evaluate( flReturn );
return flReturn;
}
#endif
bool item_list_entry_t::InitFromName( const char *pchName )
{
if ( pchName[ 0 ] == '[' )
{
// This item has an attached paint type that can affect the name and rarity!
pchName++;
char szPaintName[ 256 ];
int i;
for( i = 0; i < ARRAYSIZE( szPaintName ) - 1 && pchName[ i ] != ']'; ++i )
{
szPaintName[ i ] = pchName[ i ];
}
szPaintName[ i ] = '\0';
if ( pchName[ i ] != ']' )
return false;
pchName += i + 1;
const CPaintKit *pPaintKit = GetItemSchema()->GetPaintKitDefinitionByName( szPaintName );
if ( pPaintKit )
{
m_nPaintKit = pPaintKit->nID;
}
else
{
const CStickerKit *pStickerKit = GetItemSchema()->GetStickerKitDefinitionByName( szPaintName );
if ( pStickerKit )
{
m_nStickerKit = pStickerKit->nID;
}
else
{
const CEconMusicDefinition *pMusicKit = GetItemSchema()->GetMusicKitDefinitionByName( szPaintName );
if ( pMusicKit )
{
m_nMusicKit = pMusicKit->GetID();
}
else
{
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 );
return false;
}
}
}
}
CEconItemDefinition *pDef = GetItemSchema()->GetItemDefinitionByName( pchName );
if ( pDef )
{
m_nItemDef = pDef->GetDefinitionIndex();
}
return true;
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
CEconItemSetDefinition::CEconItemSetDefinition( void )
: m_pszName( NULL )
, m_pszLocalizedName( NULL )
, m_pszLocalizedDescription( NULL )
, m_pszUnlocalizedName( NULL )
, m_iBundleItemDef( INVALID_ITEM_DEF_INDEX )
, m_bIsCollection( false )
, m_bIsHiddenSet( false )
, m_nCraftReward( 0 )
{
}
//-----------------------------------------------------------------------------
// Purpose: Copy constructor
//-----------------------------------------------------------------------------
CEconItemSetDefinition::CEconItemSetDefinition( const CEconItemSetDefinition &that )
{
(*this) = that;
}
//-----------------------------------------------------------------------------
// Purpose: Operator=
//-----------------------------------------------------------------------------
CEconItemSetDefinition &CEconItemSetDefinition::operator=( const CEconItemSetDefinition &other )
{
m_pszName = other.m_pszName;
m_pszLocalizedName = other.m_pszLocalizedName;
m_pszUnlocalizedName = other.m_pszUnlocalizedName;
m_pszLocalizedDescription = other.m_pszLocalizedDescription;
m_pszUnlocalizedName = other.m_pszUnlocalizedName;
m_ItemEntries = other.m_ItemEntries;
m_iAttributes = other.m_iAttributes;
m_iBundleItemDef = other.m_iBundleItemDef;
m_bIsCollection = other.m_bIsCollection;
m_bIsHiddenSet = other.m_bIsHiddenSet;
m_nCraftReward = other.m_nCraftReward;
return *this;
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
bool CEconItemSetDefinition::BInitFromKV( KeyValues *pKVItemSet, CEconItemSchema &pschema, CUtlVector<CUtlString> *pVecErrors )
{
m_pszName = pKVItemSet->GetName();
m_iBundleItemDef = INVALID_ITEM_DEF_INDEX;
const char *pszBundleName = pKVItemSet->GetString( "store_bundle" );
if ( pszBundleName && pszBundleName[0] )
{
const CEconItemDefinition *pDef = pschema.GetItemDefinitionByName( pszBundleName );
if ( pDef )
{
m_iBundleItemDef = pDef->GetDefinitionIndex();
}
SCHEMA_INIT_CHECK(
pDef != NULL,
CFmtStr( "Item Set %s: Bundle definition \"%s\" was not found", m_pszName, pszBundleName ) );
}
FOR_EACH_SUBKEY( pKVItemSet, pKVSetItem )
{
const char *pszName = pKVSetItem->GetName();
if ( !Q_strcmp( pszName, "name" ) )
{
SCHEMA_INIT_CHECK(
m_pszLocalizedName == NULL,
CFmtStr( "Item Set %s: Duplicate name specified", m_pszName ) );
m_pszLocalizedName = pKVSetItem->GetString();
}
else if ( !Q_strcmp( pszName, "set_description" ) )
{
m_pszLocalizedDescription = pKVSetItem->GetString();
}
else if ( !Q_strcmp( pszName, "unlocalized_name" ) )
{
m_pszUnlocalizedName = pKVSetItem->GetString();
}
else if ( !Q_strcmp( pszName, "is_collection" ) )
{
m_bIsCollection = pKVSetItem->GetBool();
}
else if ( !Q_strcmp( pszName, "is_hidden_set" ) )
{
m_bIsHiddenSet = pKVSetItem->GetBool();
}
// else if ( !Q_strcmp( pszName, "craft_reward" ) )
// {
// const char *pchCraftReward = pKVSetItem->GetString();
// if ( pchCraftReward && pchCraftReward[ 0 ] != '\0' )
// {
// const CEconItemDefinition *pItemDef = pschema.GetItemDefinitionByName( pKVSetItem->GetString() );
//
// SCHEMA_INIT_CHECK(
// pItemDef && pItemDef->GetDefinitionIndex() > 0,
// CFmtStr( "Item Collection Craft Reward \"%s\" specifies invalid item def", pKVItemSet->GetName() ) );
//
// m_nCraftReward = pItemDef->GetDefinitionIndex();
// }
// }
else if ( !Q_strcmp( pszName, "items" ) )
{
FOR_EACH_SUBKEY( pKVSetItem, pKVItem )
{
pszName = pKVItem->GetName();
item_list_entry_t entry;
bool bEntrySuccess = entry.InitFromName( pszName );
bEntrySuccess;
m_ItemEntries.AddToTail( entry );
}
}
else if ( !Q_strcmp( pszName, "attributes" ) )
{
FOR_EACH_SUBKEY( pKVSetItem, pKVAttribute )
{
pszName = pKVAttribute->GetName();
const CEconItemAttributeDefinition *pDef = pschema.GetAttributeDefinitionByName( pszName );
// SCHEMA_INIT_CHECK(
// pDef != NULL,
// CFmtStr( "Item set %s: Attribute definition \"%s\" was not found", m_pszName, pszName ) );
if ( pDef )
{
int iIndex = m_iAttributes.AddToTail();
m_iAttributes[iIndex].m_iAttribDefIndex = pDef->GetDefinitionIndex();
if ( pDef->IsStoredAsInteger() )
{
m_iAttributes[iIndex].m_valValue = pKVAttribute->GetInt( "value" );
}
else
{
float flValue = pKVAttribute->GetFloat( "value" );
Q_memcpy( &m_iAttributes[iIndex].m_valValue, &flValue, sizeof( float ) );
}
}
}
}
}
// Sanity check.
SCHEMA_INIT_CHECK( !(m_bIsCollection && m_bIsHiddenSet),
CFmtStr( "Item Set %s: Invalid to set a collection to be hidden", m_pszName ) );
SCHEMA_INIT_CHECK( m_ItemEntries.Count() > 0,
CFmtStr( "Item Set %s: Set contains no items", m_pszName ) );
return SCHEMA_INIT_SUCCESS();
}
int CEconItemSetDefinition::GetItemRarity( int iIndex ) const
{
int nKitRarity = 1;
const CStickerKit *pStickerKit = NULL;
const CPaintKit *pPaintKit = NULL;
const CEconMusicDefinition *pMusicKitDefinition = NULL;
item_list_entry_t const &entry = m_ItemEntries[iIndex];
if ( entry.m_nPaintKit )
{
pPaintKit = GetItemSchema()->GetPaintKitDefinition( entry.m_nPaintKit );
if ( pPaintKit )
{
nKitRarity = MAX( nKitRarity, pPaintKit->nRarity );
}
}
if ( entry.m_nStickerKit )
{
pStickerKit = GetItemSchema()->GetStickerKitDefinition( entry.m_nStickerKit );
if ( pStickerKit )
{
nKitRarity = MAX( nKitRarity, pStickerKit->nRarity );
}
}
if ( entry.m_nMusicKit )
{
pMusicKitDefinition = GetItemSchema()->GetMusicDefinition( entry.m_nMusicKit );
if ( pMusicKitDefinition )
{
nKitRarity = Max ( nKitRarity, 1/* pMusicKitDefinition->GetRarity() */);
}
}
// Now combine the rarity
const CEconItemDefinition *pDef = GetItemSchema()->GetItemDefinition( entry.m_nItemDef );
return pDef ? EconRarity_CombinedItemAndPaintRarity( pDef->GetRarity(), nKitRarity ) : -1;
}
int CEconItemSetDefinition::GetHighestItemRarityValue( void ) const
{
int nHighestRarityValue = -1;
for ( int i = 0; i < GetItemCount(); ++i )
{
int nRarity = GetItemRarity( i );
if ( nHighestRarityValue < nRarity )
{
nHighestRarityValue = nRarity;
}
}
return nHighestRarityValue;
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
CEconLootListDefinition::CEconLootListDefinition( void )
{
m_bServerList = false;
}
//-----------------------------------------------------------------------------
// Purpose: Copy constructor
//-----------------------------------------------------------------------------
CEconLootListDefinition::CEconLootListDefinition( const CEconLootListDefinition &that )
{
(*this) = that;
}
//-----------------------------------------------------------------------------
// Dtor
//-----------------------------------------------------------------------------
CEconLootListDefinition::~CEconLootListDefinition( void )
{
}
//-----------------------------------------------------------------------------
// Purpose: Operator=
//-----------------------------------------------------------------------------
CEconLootListDefinition &CEconLootListDefinition::operator=( const CEconLootListDefinition &other )
{
m_pszName = other.m_pszName;
m_ItemEntries = other.m_ItemEntries;
m_AdditionalDrops = other.m_AdditionalDrops;
m_bServerList = other.m_bServerList;
return *this;
}
bool CEconLootListDefinition::AddRandomAtrributes( KeyValues *pRandomAttributesKV, CEconItemSchema &pschema, CUtlVector<CUtlString> *pVecErrors /*= NULL*/ )
{
// We've found the random attribute block. Parse it.
SCHEMA_INIT_CHECK(
NULL != pRandomAttributesKV->FindKey( "chance" ),
CFmtStr( "Loot List %s: Missing required field \"chance\" in the \"random_attributes\" block.", m_pszName ) );
random_attrib_t *randomAttrib = new random_attrib_t;
randomAttrib->m_flChanceOfRandomAttribute = pRandomAttributesKV->GetFloat( "chance" );
randomAttrib->m_bPickAllAttributes = ( pRandomAttributesKV->GetFloat( "pick_all_attributes" ) != 0 );
randomAttrib->m_flTotalAttributeWeight = 0;
FOR_EACH_TRUE_SUBKEY( pRandomAttributesKV, pKVAttribute )
{
const char *pszName = pKVAttribute->GetName();
if ( !Q_strcmp( pszName, "chance" ) )
continue;
const CEconItemAttributeDefinition *pDef = pschema.GetAttributeDefinitionByName( pszName );
SCHEMA_INIT_CHECK(
pDef != NULL,
CFmtStr( "Loot list %s: Attribute definition \"%s\" was not found", m_pszName, pszName ) );
if ( pDef )
{
lootlist_attrib_t lootListAttrib;
SCHEMA_INIT_SUBSTEP( lootListAttrib.BInitFromKV( m_pszName, pKVAttribute, pschema, pVecErrors ) );
randomAttrib->m_flTotalAttributeWeight += lootListAttrib.m_flWeight;
randomAttrib->m_RandomAttributes.AddToTail( lootListAttrib );
}
}
m_RandomAttribs.AddToTail( randomAttrib );
return true;
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
bool CEconLootListDefinition::BInitFromKV( KeyValues *pKVLootList, KeyValues *pKVRandomAttributeTemplates, CEconItemSchema &pschema, CUtlVector<CUtlString> *pVecErrors, bool bServerList )
{
m_pszName = pKVLootList->GetName();
m_unHeroID = 0;
m_bServerList = bServerList;
FOR_EACH_SUBKEY( pKVLootList, pKVListItem )
{
const char *pszName = pKVListItem->GetName();
if ( !Q_strcmp( pszName, "random_attributes" ) )
{
AddRandomAtrributes( pKVListItem, pschema, pVecErrors );
continue;
}
else if ( !Q_strcmp( pszName, "attribute_templates" ) )
{
if ( !pKVRandomAttributeTemplates )
{
// Clients don't load the attribute template list
// Skip over attribute templates
continue;
}
FOR_EACH_SUBKEY( pKVListItem, pKVAttributeTemplate )
{
if ( pKVAttributeTemplate->GetDataType() == KeyValues::TYPE_NONE )
{ // Support full template specified inplace
AddRandomAtrributes( pKVAttributeTemplate, pschema, pVecErrors );
}
else
{
if ( pKVAttributeTemplate->GetInt() == 0 )
continue;
KeyValues *pKVRandomAttribute = pKVRandomAttributeTemplates->FindKey( pKVAttributeTemplate->GetName() );
SCHEMA_INIT_CHECK(
pKVRandomAttribute,
CFmtStr( "Loot List %s: Looking for attribute template \"%s\" that couldn't be found in \"random_attribute_templates\" block.\n", m_pszName, pKVAttributeTemplate->GetName() ) );
AddRandomAtrributes( pKVRandomAttribute, pschema, pVecErrors );
}
}
continue;
}
else if ( !Q_strcmp( pszName, "public_list_contents" ) )
{
m_bPublicListContents = pKVListItem->GetBool();
continue;
}
if ( !Q_strcmp( pszName, "additional_drop" ) )
{
float fChance = pKVListItem->GetFloat( "chance", 0.0f );
bool bPremiumOnly = pKVListItem->GetBool( "premium_only", false );
const char *pszLootList = pKVListItem->GetString( "loot_list", "" );
SCHEMA_INIT_CHECK(
fChance > 0.0f && fChance <= 1.0f,
CFmtStr( "Loot list %s: Invalid \"additional_drop\" chance %.2f\n", m_pszName, fChance ) );
SCHEMA_INIT_CHECK(
pszLootList && pszLootList[0],
CFmtStr( "Loot list %s: Missing \"additional_drop\" loot list name\n", m_pszName ) );
if ( pszLootList )
{
const CEconLootListDefinition *pLootListDef = pschema.GetLootListByName( pszLootList );
SCHEMA_INIT_CHECK(
pLootListDef,
CFmtStr( "Loot list %s: Invalid \"additional_drop\" loot list \"%s\"\n", m_pszName, pszLootList ) );
if ( pLootListDef )
{
loot_list_additional_drop_t additionalDrop = { fChance, bPremiumOnly, pszLootList };
m_AdditionalDrops.AddToTail( additionalDrop );
}
}
continue;
}
if ( !Q_strcmp( pszName, "hero" ) )
{
const char* pszHeroName = pKVListItem->GetString( "name" );
m_unHeroID = GEconItemSchema().GetHeroID( pszHeroName );
continue;
}
item_list_entry_t entry;
// First, see if we've got a loot list name, for embedded loot lists
int iIdx = 0;
if ( V_strstr( pszName, "unusual" ) != NULL )
{
entry.m_bIsUnusualList = true;
}
if ( pschema.GetLootListByName( pszName, &iIdx ) )
{
entry.m_nItemDef = iIdx;
entry.m_bIsNestedList = true;
}
else
{
bool bEntrySuccess = entry.InitFromName( pszName );
bEntrySuccess;
}
// Make sure we never put non-enabled items into loot lists
if ( !entry.m_bIsNestedList && entry.m_nItemDef > 0 )
{
const CEconItemDefinition *pItemDef = pschema.GetItemDefinition( entry.m_nItemDef );
SCHEMA_INIT_CHECK(
pItemDef,
CFmtStr( "Loot list %s: Item definition index \"%s\" (%d) was not found\n", m_pszName, pszName, entry.m_nItemDef ) );
}
m_ItemEntries.AddToTail( entry );
float fItemWeight = pKVListItem->GetFloat();
SCHEMA_INIT_CHECK(
fItemWeight > 0.0f,
CFmtStr( "Loot list %s: Item definition index \"%s\" (%d) has invalid weight %.2f\n", m_pszName, pszName, entry.m_nItemDef, fItemWeight ) );
m_flWeights.AddToTail( fItemWeight );
m_flTotalWeight += fItemWeight;
}
return SCHEMA_INIT_SUCCESS();
}
bool CEconLootListDefinition::HasUnusualLoot() const
{
FOR_EACH_VEC(GetLootListContents(), i)
{
const item_list_entry_t& entry = GetLootListContents()[i];
if (entry.m_bIsUnusualList)
return true;
if (entry.m_bIsNestedList)
{
const CEconLootListDefinition *pNestedLootList = GetItemSchema()->GetLootListByIndex(entry.m_nItemDef);
if (pNestedLootList)
{
if (pNestedLootList->HasUnusualLoot())
return true;
}
}
}
return false;
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
bool CEconLootListDefinition::GetAdditionalDrop( int iIndex, CUtlString& strLootList, float& flChance ) const
{
if ( !m_AdditionalDrops.IsValidIndex( iIndex ) )
return false;
strLootList = m_AdditionalDrops[iIndex].m_pszLootListDefName;
flChance = m_AdditionalDrops[iIndex].m_fChance;
return true;
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
bool CEconLootListDefinition::GetRandomAttributeGroup( int iIndex, float& flChance, float& flTotalWeight ) const
{
if ( !m_RandomAttribs.IsValidIndex( iIndex ) )
return false;
flChance = m_RandomAttribs[iIndex]->m_flChanceOfRandomAttribute;
flTotalWeight = m_RandomAttribs[iIndex]->m_flTotalAttributeWeight;
return true;
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
int CEconLootListDefinition::GetRandomAttributeCount( int iGroup ) const
{
if ( !m_RandomAttribs.IsValidIndex( iGroup ) )
return false;
return m_RandomAttribs[iGroup]->m_RandomAttributes.Count();
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
bool CEconLootListDefinition::GetRandomAttribute( int iGroup, int iIndex, float& flWeight, int& iValue, int& iDefIndex ) const
{
Assert( !"Tell whoever is working on the item editor that loot list random attributes don't support typed attributes!" );
if ( !m_RandomAttribs.IsValidIndex( iGroup ) )
return false;
if ( !m_RandomAttribs[iGroup]->m_RandomAttributes.IsValidIndex( iIndex ) )
return false;
flWeight = m_RandomAttribs[iGroup]->m_RandomAttributes[iIndex].m_flWeight;
iValue = (int) m_RandomAttribs[iGroup]->m_RandomAttributes[iIndex].m_staticAttrib.m_value.asFloat;
iDefIndex = m_RandomAttribs[iGroup]->m_RandomAttributes[iIndex].m_staticAttrib.iDefIndex;
return true;
}
//-----------------------------------------------------------------------------
// Outputs a keyvalues schema representation of this loot list.
//-----------------------------------------------------------------------------
KeyValues* CEconLootListDefinition::GenerateKeyValues() const
{
KeyValues* pLootListKV = new KeyValues( m_pszName );
// Write out our items and weights.
FOR_EACH_VEC( m_ItemEntries, i )
{
const CEconItemDefinition* pItemDef = GetItemSchema()->GetItemDefinition( m_ItemEntries[i].m_nItemDef );
if ( !pItemDef )
continue;
pLootListKV->SetFloat( pItemDef->GetRawDefinition()->GetString( "name" ), m_flWeights[i] );
}
// Write out additional drops.
// Write out random attributes.
return pLootListKV;
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
void CEconLootListDefinition::PurgeItems( void )
{
m_ItemEntries.Purge();
m_flWeights.Purge();
m_flTotalWeight = 0;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CEconLootListDefinition::lootlist_attrib_t::BInitFromKV( const char *pszContext, KeyValues *pKVKey, CEconItemSchema &pschema, CUtlVector<CUtlString> *pVecErrors )
{
SCHEMA_INIT_SUBSTEP( m_staticAttrib.BInitFromKV_MultiLine( pszContext, pKVKey, pVecErrors ) );
SCHEMA_INIT_CHECK(
pKVKey->FindKey( "weight" ),
CFmtStr( "Loot definition %s: Attribute \"%s\" missing required 'weight' field", pszContext, pKVKey->GetName() ) );
SCHEMA_INIT_CHECK(
( !pKVKey->FindKey( "range_min" ) && !pKVKey->FindKey( "range_max" ) ) ||
( !pKVKey->FindKey( "values" ) ),
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() ) );
m_flWeight = pKVKey->GetFloat( "weight" );
m_flRangeMin = pKVKey->GetFloat( "range_min" );
m_flRangeMax = pKVKey->GetFloat( "range_max" );
const char* pszValues = pKVKey->GetString( "values" );
m_vecValues.Purge();
if ( pszValues && pszValues[ 0 ] )
{
SCHEMA_INIT_CHECK(
Helper_ExtractIntegersFromValuesString( pszValues, m_vecValues ),
CFmtStr( "Loot definition %s: Attribute \"%s\" specifies a malformed values range: %s.", pszContext, pKVKey->GetName(), pszValues ) );
}
// validate that quest id values are valid
// TODO: create a common class for indexed definition classes and have this code validate all of them
if ( !strcmp( pKVKey->GetName(), "quest id" ) )
{
if ( m_flRangeMin != 0 || m_flRangeMax != 0 )
{
for ( int i = m_flRangeMin; i <= m_flRangeMax; i++ )
{
SCHEMA_INIT_CHECK(
GetItemSchema()->GetQuestDefinition( i ),
CFmtStr( "Loot definition %s: Attribute \"%s\" specifies a quest id that does not exist: %d.\n", pszContext, pKVKey->GetName(), i ) );
}
}
else if ( m_vecValues.Count() != 0 )
{
FOR_EACH_VEC( m_vecValues, i )
{
SCHEMA_INIT_CHECK(
GetItemSchema()->GetQuestDefinition( m_vecValues[ i ] ),
CFmtStr( "Loot definition %s: Attribute \"%s\" specifies a quest id that does not exist: %d.\n", pszContext, pKVKey->GetName(), m_vecValues[ i ] ) );
}
}
}
else if ( !strcmp( pKVKey->GetName(), "quest reward lootlist" ) )
{
const CEconItemSchema::RevolvingLootListDefinitionMap_t* pQuestRewardLL= &( GetItemSchema()->GetQuestRewardLootLists() );
if ( m_flRangeMin != 0 || m_flRangeMax != 0 )
{
for ( int i = m_flRangeMin; i <= m_flRangeMax; i++ )
{
int iLLIndex = pQuestRewardLL->Find( i );
SCHEMA_INIT_CHECK(
pQuestRewardLL->IsValidIndex( iLLIndex ),
CFmtStr( "Loot definition %s: Attribute \"%s\" specifies a quest reward lootlist that does not exist: %d.\n", pszContext, pKVKey->GetName(), i ) );
}
}
else if ( m_vecValues.Count() != 0 )
{
FOR_EACH_VEC( m_vecValues, i )
{
int iLLIndex = pQuestRewardLL->Find( m_vecValues[ i ] );
SCHEMA_INIT_CHECK(
pQuestRewardLL->IsValidIndex( iLLIndex ),
CFmtStr( "Loot definition %s: Attribute \"%s\" specifies a quest reward lootlist that does not exist: %d.\n", pszContext, pKVKey->GetName(), m_vecValues[ i ] ) );
}
}
}
return SCHEMA_INIT_SUCCESS();
}
//-----------------------------------------------------------------------------
// Purpose: Constructor
//-----------------------------------------------------------------------------
CEconCraftingRecipeDefinition::CEconCraftingRecipeDefinition( void )
{
m_nDefIndex = 0;
m_wszName[ 0 ] = L'\0';
m_wszDesc[ 0 ] = L'\0';
}
//-----------------------------------------------------------------------------
// Purpose: Initialize the attribute definition
// Input: pKVAttribute - The KeyValues representation of the attribute
// schema - The overall item schema for this attribute
// pVecErrors - An optional vector that will contain error messages if
// the init fails.
// Output: True if initialization succeeded, false otherwise
//-----------------------------------------------------------------------------
bool CEconCraftingRecipeDefinition::BInitFromKV( KeyValues *pKVRecipe, CEconItemSchema &pschema, CUtlVector<CUtlString> *pVecErrors /* = NULL */ )
{
m_nDefIndex = Q_atoi( pKVRecipe->GetName() );
// Check for required fields
SCHEMA_INIT_CHECK(
NULL != pKVRecipe->FindKey( "input_items" ),
CFmtStr( "Recipe definition %d: Missing required field \"input_items\"", m_nDefIndex ) );
SCHEMA_INIT_CHECK(
NULL != pKVRecipe->FindKey( "output_items" ),
CFmtStr( "Recipe definition %d: Missing required field \"output_items\"", m_nDefIndex ) );
m_bDisabled = pKVRecipe->GetBool( "disabled" );
m_strName = pKVRecipe->GetString( "name" );
m_strN_A = pKVRecipe->GetString( "n_A" );
m_strDescInputs = pKVRecipe->GetString( "desc_inputs" );
m_strDescOutputs = pKVRecipe->GetString( "desc_outputs" );
m_strDI_A = pKVRecipe->GetString( "di_A" );
m_strDI_B = pKVRecipe->GetString( "di_B" );
m_strDI_C = pKVRecipe->GetString( "di_C" );
m_strDO_A = pKVRecipe->GetString( "do_A" );
m_strDO_B = pKVRecipe->GetString( "do_B" );
m_strDO_C = pKVRecipe->GetString( "do_C" );
m_bRequiresAllSameClass = pKVRecipe->GetBool( "all_same_class" );
m_bRequiresAllSameSlot = pKVRecipe->GetBool( "all_same_slot" );
m_iCacheClassUsageForOutputFromItem = pKVRecipe->GetInt( "add_class_usage_to_output", -1 );
m_iCacheSlotUsageForOutputFromItem = pKVRecipe->GetInt( "add_slot_usage_to_output", -1 );
m_iCacheSetForOutputFromItem = pKVRecipe->GetInt( "add_set_to_output", -1 );
m_bAlwaysKnown = pKVRecipe->GetBool( "always_known", true );
m_bPremiumAccountOnly = pKVRecipe->GetBool( "premium_only", false );
m_iCategory = (recipecategories_t)StringFieldToInt( pKVRecipe->GetString("category"), g_szRecipeCategoryStrings, ARRAYSIZE(g_szRecipeCategoryStrings) );
m_iFilter = pKVRecipe->GetInt( "filter", -1 );
// Read in all the input items
KeyValues *pKVInputItems = pKVRecipe->FindKey( "input_items" );
if ( NULL != pKVInputItems )
{
FOR_EACH_TRUE_SUBKEY( pKVInputItems, pKVInputItem )
{
int index = m_InputItemsCriteria.AddToTail();
SCHEMA_INIT_SUBSTEP( m_InputItemsCriteria[index].BInitFromKV( pKVInputItem, pschema ) );
// Recipes ignore the enabled flag when generating items
m_InputItemsCriteria[index].SetIgnoreEnabledFlag( true );
index = m_InputItemDupeCounts.AddToTail();
m_InputItemDupeCounts[index] = atoi( pKVInputItem->GetName() );
}
}
// Read in all the output items
KeyValues *pKVOutputItems = pKVRecipe->FindKey( "output_items" );
if ( NULL != pKVOutputItems )
{
FOR_EACH_TRUE_SUBKEY( pKVOutputItems, pKVOutputItem )
{
int index = m_OutputItemsCriteria.AddToTail();
SCHEMA_INIT_SUBSTEP( m_OutputItemsCriteria[index].BInitFromKV( pKVOutputItem, pschema ) );
// Recipes ignore the enabled flag when generating items
m_OutputItemsCriteria[index].SetIgnoreEnabledFlag( true );
}
}
GenerateLocStrings();
return SCHEMA_INIT_SUCCESS();
}
bool CEconCraftingRecipeDefinition::BInitFromSet( const IEconItemSetDefinition *pSet, CEconItemSchema &pschema, CUtlVector<CUtlString> *pVecErrors /* = NULL */ )
{
m_bDisabled = false;
m_strName = "#RT_T_A"; // "Trade In %s1"
m_strN_A = pSet->GetLocKey(); // "The Office Collection"
m_strDescInputs = "#RDI_AB"; // "Requires: %s1 %s2"
m_strDescOutputs = "RDO_AB"; // "Produces: %s1 %s2"
m_strDI_A = "1"; // "1"
m_strDI_B = pSet->GetLocKey(); // "The Office Collection"
m_strDI_C = NULL;
m_strDO_A = "1"; // "1"
const CEconItemDefinition *pItemDef = pschema.GetItemDefinition( pSet->GetCraftReward() );
SCHEMA_INIT_CHECK(
pItemDef && pItemDef->GetDefinitionIndex() > 0,
CFmtStr( "Item Collection Craft Reward (%d) specifies invalid item def", pSet->GetCraftReward() ) );
m_strDO_B = pItemDef->GetItemBaseName();
m_strDO_C = NULL;
m_bRequiresAllSameClass = false;
m_bRequiresAllSameSlot = false;
m_iCacheClassUsageForOutputFromItem = -1;
m_iCacheSlotUsageForOutputFromItem = -1;
m_iCacheSetForOutputFromItem = -1;
m_bAlwaysKnown = true;
m_bPremiumAccountOnly = false;
m_iCategory = (recipecategories_t)StringFieldToInt( "crafting", g_szRecipeCategoryStrings, ARRAYSIZE(g_szRecipeCategoryStrings) );
m_iFilter = -1;
for ( int i = 0; i < pSet->GetItemCount(); ++i )
{
int index = m_InputItemsCriteria.AddToTail();
SCHEMA_INIT_SUBSTEP( m_InputItemsCriteria[index].BInitFromItemAndPaint( pSet->GetItemDef( i ), pSet->GetItemPaintKit( i ), pschema ) );
m_InputItemsCriteria[index].SetIgnoreEnabledFlag( true );
index = m_InputItemDupeCounts.AddToTail();
m_InputItemDupeCounts[index] = 1;
}
int index = m_OutputItemsCriteria.AddToTail();
SCHEMA_INIT_SUBSTEP( m_OutputItemsCriteria[index].BInitFromItemAndPaint( pSet->GetCraftReward(), 0, pschema ) );
GenerateLocStrings();
return SCHEMA_INIT_SUCCESS();
}
//-----------------------------------------------------------------------------
// Purpose: Serializes the criteria to and from messages
//-----------------------------------------------------------------------------
bool CEconCraftingRecipeDefinition::BSerializeToMsg( CSOItemRecipe & msg ) const
{
msg.set_def_index( m_nDefIndex );
msg.set_name( m_strName );
msg.set_n_a( m_strN_A );
msg.set_desc_inputs( m_strDescInputs );
msg.set_desc_outputs( m_strDescOutputs );
msg.set_di_a( m_strDI_A );
msg.set_di_b( m_strDI_B );
msg.set_di_c( m_strDI_C );
msg.set_do_a( m_strDO_A );
msg.set_do_b( m_strDO_B );
msg.set_do_c( m_strDO_C );
msg.set_requires_all_same_class( m_bRequiresAllSameClass );
msg.set_requires_all_same_slot( m_bRequiresAllSameSlot );
msg.set_class_usage_for_output( m_iCacheClassUsageForOutputFromItem );
msg.set_slot_usage_for_output( m_iCacheSlotUsageForOutputFromItem );
msg.set_set_for_output( m_iCacheSetForOutputFromItem );
FOR_EACH_VEC( m_InputItemsCriteria, i )
{
CSOItemCriteria *pCrit = msg.add_input_items_criteria();
if ( !m_InputItemsCriteria[i].BSerializeToMsg( *pCrit ) )
return false;
}
FOR_EACH_VEC( m_InputItemDupeCounts, i )
{
msg.add_input_item_dupe_counts( m_InputItemDupeCounts[i] );
}
FOR_EACH_VEC( m_OutputItemsCriteria, i )
{
CSOItemCriteria *pCrit = msg.add_output_items_criteria();
if ( !m_OutputItemsCriteria[i].BSerializeToMsg( *pCrit ) )
return false;
}
return true;
}
//-----------------------------------------------------------------------------
// Purpose: Serializes the criteria to and from messages
//-----------------------------------------------------------------------------
bool CEconCraftingRecipeDefinition::BDeserializeFromMsg( const CSOItemRecipe & msg )
{
m_nDefIndex = msg.def_index();
m_strName = msg.name().c_str();
m_strN_A = msg.n_a().c_str();
m_strDescInputs = msg.desc_inputs().c_str();
m_strDescOutputs = msg.desc_outputs().c_str();
m_strDI_A = msg.di_a().c_str();
m_strDI_B = msg.di_b().c_str();
m_strDI_C = msg.di_c().c_str();
m_strDO_A = msg.do_a().c_str();
m_strDO_B = msg.do_b().c_str();
m_strDO_C = msg.do_c().c_str();
m_bRequiresAllSameClass = msg.requires_all_same_class();
m_bRequiresAllSameSlot = msg.requires_all_same_slot();
m_iCacheClassUsageForOutputFromItem = msg.class_usage_for_output();
m_iCacheSlotUsageForOutputFromItem = msg.slot_usage_for_output();
m_iCacheSetForOutputFromItem = msg.set_for_output();
// Read how many input items there are
uint32 unCount = msg.input_items_criteria_size();
m_InputItemsCriteria.SetSize( unCount );
for ( uint32 i = 0; i < unCount; i++ )
{
if ( !m_InputItemsCriteria[i].BDeserializeFromMsg( msg.input_items_criteria( i ) ) )
return false;
}
// Read how many input item dupe counts there are
unCount = msg.input_item_dupe_counts_size();
m_InputItemDupeCounts.SetSize( unCount );
for ( uint32 i = 0; i < unCount; i++ )
{
m_InputItemDupeCounts[i] = msg.input_item_dupe_counts( i );
}
// Read how many output items there are
unCount = msg.output_items_criteria_size();
m_OutputItemsCriteria.SetSize( unCount );
for ( uint32 i = 0; i < unCount; i++ )
{
if ( !m_OutputItemsCriteria[i].BDeserializeFromMsg( msg.output_items_criteria( i ) ) )
return false;
}
GenerateLocStrings();
return true;
}
//-----------------------------------------------------------------------------
// Purpose: Returns true if the vector contains a set of items that matches the inputs for this recipe
// Note it will fail if the vector contains extra items that aren't needed.
//
//-----------------------------------------------------------------------------
bool CEconCraftingRecipeDefinition::ItemListMatchesInputs( const CUtlVector< CEconItem* > &vecCraftingItems, bool bAllowPartialMatch /*= false*/ ) const
{
bool bResult = true;
CUtlVector< uint8 > arrDupesUsed;
arrDupesUsed.SetCount( m_InputItemsCriteria.Count() );
uint8 *pDupesUsed = arrDupesUsed.Base();
memset( pDupesUsed, 0, m_InputItemsCriteria.Count() );
int nSet = -1;
if ( ( GetFilter() == CRAFT_FILTER_COLLECT || GetFilter() == CRAFT_FILTER_TRADEUP ) &&
vecCraftingItems.Count() > 0 )
{
nSet = vecCraftingItems[ 0 ]->GetItemSetIndex();
if ( nSet < 0 )
{
// This item must be in a set!
return false;
}
}
FOR_EACH_VEC( vecCraftingItems, nItem )
{
CEconItem *pEconItem = vecCraftingItems[ nItem ];
#if 0 // Relaxing set restriction on crafting
if ( nSet >= 0 && pEconItem->GetItemSetIndex() != nSet )
{
bResult = false;
break;
}
#endif
// Any items at the top rarity of their set are illegal in crafting
if ( GetFilter() == CRAFT_FILTER_TRADEUP )
{
const IEconItemSetDefinition *pSetDef = GetItemSchema()->GetItemSet( vecCraftingItems[ nItem ]->GetItemSetIndex() );
if ( !pSetDef )
{
return false;
}
bool bHasItemsAtNextRarityTier = false;
for ( int i = 0; i < pSetDef->GetItemCount(); ++i )
{
if ( pSetDef->GetItemRarity( i ) == pEconItem->GetRarity() + 1 )
{
bHasItemsAtNextRarityTier = true;
break;
}
}
if ( !bHasItemsAtNextRarityTier )
return false;
}
bool bFoundCriteriaMatch = false;
FOR_EACH_VEC( m_InputItemsCriteria, nCriteria )
{
const CItemSelectionCriteria *pCriteria = &( m_InputItemsCriteria[ nCriteria ] );
if ( !pCriteria )
continue;
// Skip if we've used this criteria to the limit
if ( pDupesUsed[ nCriteria ] >= m_InputItemDupeCounts[ nCriteria ] )
continue;
bFoundCriteriaMatch = pCriteria->BEvaluate( pEconItem, *GetItemSchema() );
if ( bFoundCriteriaMatch )
{
pDupesUsed[ nCriteria ]++;
break;
}
}
if ( !bFoundCriteriaMatch )
{
// Did the item not fit any criteria!
bResult = false;
break;
}
}
if ( !bAllowPartialMatch )
{
// Make sure we've matched ALL the criteria
FOR_EACH_VEC( m_InputItemsCriteria, nCriteria )
{
if ( pDupesUsed[ nCriteria ] < m_InputItemDupeCounts[ nCriteria ] )
{
// Not all criteria dupes were matched
bResult = false;
break;
}
}
}
return bResult;
}
//-----------------------------------------------------------------------------
// Purpose: Constructor
//-----------------------------------------------------------------------------
int CEconCraftingRecipeDefinition::GetTotalInputItemsRequired( void ) const
{
int iCount = 0;
FOR_EACH_VEC( m_InputItemsCriteria, i )
{
if ( m_InputItemDupeCounts[i] )
{
iCount += m_InputItemDupeCounts[i];
}
else
{
iCount++;
}
}
return iCount;
}
#if defined( CLIENT_DLL )
wchar_t *LocalizeRecipeStringPiece( const char *pszString, wchar_t *pszConverted, int nConvertedSizeInBytes )
{
if ( !pszString )
return L"";
if ( pszString[0] == '#' )
return g_pVGuiLocalize->Find( pszString );
g_pVGuiLocalize->ConvertANSIToUnicode( pszString, pszConverted, nConvertedSizeInBytes );
return pszConverted;
}
#endif
void CEconCraftingRecipeDefinition::GenerateLocStrings( void )
{
#if defined( CLIENT_DLL )
wchar_t *pName_A = g_pVGuiLocalize->Find( GetName_A() );
g_pVGuiLocalize->ConstructString( m_wszName, sizeof( m_wszName ), g_pVGuiLocalize->Find( GetName() ), 1, pName_A );
wchar_t wcTmpA[32];
wchar_t wcTmpB[32];
wchar_t wcTmpC[32];
wchar_t wcTmp[512];
// Build the input string
wchar_t *pInp_A = LocalizeRecipeStringPiece( GetDescI_A(), wcTmpA, sizeof( wcTmpA ) );
wchar_t *pInp_B = LocalizeRecipeStringPiece( GetDescI_B(), wcTmpB, sizeof( wcTmpB ) );
wchar_t *pInp_C = LocalizeRecipeStringPiece( GetDescI_C(), wcTmpC, sizeof( wcTmpC ) );
g_pVGuiLocalize->ConstructString( m_wszDesc, sizeof( m_wszDesc ), g_pVGuiLocalize->Find( GetDescInputs() ), 3, pInp_A, pInp_B, pInp_C );
// Build the output string
wchar_t *pOut_A = LocalizeRecipeStringPiece( GetDescO_A(), wcTmpA, sizeof( wcTmpA ) );
wchar_t *pOut_B = LocalizeRecipeStringPiece( GetDescO_B(), wcTmpB, sizeof( wcTmpB ) );
wchar_t *pOut_C = LocalizeRecipeStringPiece( GetDescO_C(), wcTmpC, sizeof( wcTmpC ) );
g_pVGuiLocalize->ConstructString( wcTmp, sizeof( wcTmp ), g_pVGuiLocalize->Find( GetDescOutputs() ), 3, pOut_A, pOut_B, pOut_C );
// Concatenate, and mark the text changes
V_wcscat_safe( m_wszDesc, L"\n" );
V_wcscat_safe( m_wszDesc, wcTmp );
#endif
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
#define GC_SCH_REFERENCE( TAttribSchType )
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
unsigned int Internal_GetAttributeTypeUniqueIdentifierNextValue()
{
static unsigned int s_unUniqueCounter = 0;
unsigned int unCounter = s_unUniqueCounter;
s_unUniqueCounter++;
return unCounter;
}
template < typename T >
unsigned int GetAttributeTypeUniqueIdentifier()
{
static unsigned int s_unUniqueCounter = Internal_GetAttributeTypeUniqueIdentifierNextValue();
return s_unUniqueCounter;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
template < GC_SCH_REFERENCE( typename TAttribSchType ) typename TAttribInMemoryType >
class CSchemaAttributeTypeBase : public ISchemaAttributeTypeBase<TAttribInMemoryType>
{
public:
};
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
template < GC_SCH_REFERENCE( typename TAttribSchType ) typename TProtobufValueType >
class CSchemaAttributeTypeProtobufBase : public CSchemaAttributeTypeBase< GC_SCH_REFERENCE( TAttribSchType ) TProtobufValueType >
{
public:
virtual void ConvertTypedValueToByteStream( const TProtobufValueType& typedValue, ::std::string *out_psBytes ) const OVERRIDE
{
DbgVerify( typedValue.SerializeToString( out_psBytes ) );
}
virtual void ConvertByteStreamToTypedValue( const ::std::string& sBytes, TProtobufValueType *out_pTypedValue ) const OVERRIDE
{
DbgVerify( out_pTypedValue->ParseFromString( sBytes ) );
}
virtual bool BConvertStringToEconAttributeValue( const CEconItemAttributeDefinition *pAttrDef, const char *pszValue, union attribute_data_union_t *out_pValue ) const OVERRIDE
{
Assert( pAttrDef );
Assert( out_pValue );
std::string sValue( pszValue );
TProtobufValueType typedValue;
if ( !google::protobuf::TextFormat::ParseFromString( sValue, &typedValue ) )
return false;
this->ConvertTypedValueToEconAttributeValue( typedValue, out_pValue );
return true;
}
virtual void ConvertEconAttributeValueToString( const CEconItemAttributeDefinition *pAttrDef, const attribute_data_union_t& value, std::string *out_ps ) const OVERRIDE
{
Assert( pAttrDef );
Assert( out_ps );
google::protobuf::TextFormat::PrintToString( this->GetTypedValueContentsFromEconAttributeValue( value ), out_ps );
}
};
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
class CSchemaAttributeType_String : public CSchemaAttributeTypeProtobufBase< GC_SCH_REFERENCE( CSchItemAttributeString ) CAttribute_String >
{
public:
// We intentionally override the convert-to-/convert-from-string functions for strings so that string literals can be
// specified in the schema, etc. without worrying about the protobuf text format.
virtual bool BConvertStringToEconAttributeValue( const CEconItemAttributeDefinition *pAttrDef, const char *pszValue, union attribute_data_union_t *out_pValue ) const OVERRIDE
{
Assert( pAttrDef );
Assert( out_pValue );
CAttribute_String typedValue;
typedValue.set_value( pszValue );
this->ConvertTypedValueToEconAttributeValue( typedValue, out_pValue );
return true;
}
virtual void ConvertEconAttributeValueToString( const CEconItemAttributeDefinition *pAttrDef, const attribute_data_union_t& value, std::string *out_ps ) const OVERRIDE
{
Assert( pAttrDef );
Assert( out_ps );
*out_ps = this->GetTypedValueContentsFromEconAttributeValue( value ).value().c_str();
}
};
void CopyStringAttributeValueToCharPointerOutput( const CAttribute_String *pValue, const char **out_pValue )
{
Assert( pValue );
Assert( out_pValue );
*out_pValue = pValue->value().c_str();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
class CSchemaAttributeType_Vector : public CSchemaAttributeTypeBase< GC_SCH_REFERENCE( CSchItemAttributeVector ) Vector >
{
public:
// We intentionally override the convert-to-/convert-from-string functions for strings so that string literals can be
// specified in the schema, etc. without worrying about the protobuf text format.
virtual bool BConvertStringToEconAttributeValue( const CEconItemAttributeDefinition *pAttrDef, const char *pszValue, union attribute_data_union_t *out_pValue ) const OVERRIDE
{
Assert( pAttrDef );
Assert( out_pValue );
Vector typedValue;
if ( sscanf( pszValue, "%f %f %f", &typedValue.x, &typedValue.y, &typedValue.z ) < 3 )
{
AssertMsg1( false, "Vector attribute value has invalid string: %s", pszValue );
}
this->ConvertTypedValueToEconAttributeValue( typedValue, out_pValue );
return true;
}
virtual void ConvertEconAttributeValueToString( const CEconItemAttributeDefinition *pAttrDef, const attribute_data_union_t& value, std::string *out_ps ) const OVERRIDE
{
Assert( pAttrDef );
Assert( out_ps );
Vector typedValue;
this->ConvertEconAttributeValueToTypedValue( value, &typedValue );
char szTemp[ 256 ];
V_sprintf_safe( szTemp, "%f %f %f", typedValue[ 0 ], typedValue[ 1 ], typedValue[ 2 ] );
*out_ps = szTemp;
}
virtual void ConvertTypedValueToByteStream( const Vector& typedValue, ::std::string *out_psBytes ) const OVERRIDE
{
Assert( out_psBytes );
Assert( out_psBytes->size() == 0 );
out_psBytes->resize( sizeof( Vector ) );
*reinterpret_cast<Vector *>( &((*out_psBytes)[0]) ) = typedValue;
}
virtual void ConvertByteStreamToTypedValue( const ::std::string& sBytes, Vector *out_pTypedValue ) const OVERRIDE
{
Assert( out_pTypedValue );
Assert( sBytes.size() == sizeof( Vector ) );
*out_pTypedValue = *reinterpret_cast<const Vector *>( &sBytes[0] );
}
};
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
class CSchemaAttributeType_Uint32 : public CSchemaAttributeTypeBase< GC_SCH_REFERENCE( CSchItemAttributeUint32 ) uint32 >
{
public:
virtual bool BConvertStringToEconAttributeValue( const CEconItemAttributeDefinition *pAttrDef, const char *pszValue, union attribute_data_union_t *out_pValue ) const OVERRIDE
{
Assert( pAttrDef );
Assert( out_pValue );
out_pValue->asUint32 = Q_atoi( pszValue );
return true;
}
virtual void ConvertEconAttributeValueToString( const CEconItemAttributeDefinition *pAttrDef, const attribute_data_union_t& value, std::string *out_ps ) const OVERRIDE
{
Assert( pAttrDef );
Assert( out_ps );
*out_ps = CFmtStr( "%u", value.asUint32 ).Get();
}
virtual void ConvertTypedValueToByteStream( const uint32& typedValue, ::std::string *out_psBytes ) const OVERRIDE
{
Assert( out_psBytes );
Assert( out_psBytes->size() == 0 );
out_psBytes->resize( sizeof( uint32 ) );
*reinterpret_cast<uint32 *>( &((*out_psBytes)[0]) ) = typedValue;
}
virtual void ConvertByteStreamToTypedValue( const ::std::string& sBytes, uint32 *out_pTypedValue ) const OVERRIDE
{
Assert( out_pTypedValue );
Assert( sBytes.size() == sizeof( uint32 ) );
*out_pTypedValue = *reinterpret_cast<const uint32 *>( &sBytes[0] );
}
virtual bool BSupportsGameplayModificationAndNetworking() const OVERRIDE
{
return true;
}
};
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
class CSchemaAttributeType_Float : public CSchemaAttributeTypeBase< GC_SCH_REFERENCE( CSchItemAttributeFloat ) float >
{
public:
virtual bool BConvertStringToEconAttributeValue( const CEconItemAttributeDefinition *pAttrDef, const char *pszValue, union attribute_data_union_t *out_pValue ) const OVERRIDE
{
Assert( pAttrDef );
Assert( out_pValue );
out_pValue->asFloat = Q_atof( pszValue );
return true;
}
virtual void ConvertEconAttributeValueToString( const CEconItemAttributeDefinition *pAttrDef, const attribute_data_union_t& value, std::string *out_ps ) const OVERRIDE
{
Assert( pAttrDef );
Assert( out_ps );
*out_ps = CFmtStr( "%f", value.asFloat ).Get();
}
virtual void ConvertTypedValueToByteStream( const float& typedValue, ::std::string *out_psBytes ) const OVERRIDE
{
Assert( out_psBytes );
Assert( out_psBytes->size() == 0 );
out_psBytes->resize( sizeof( float ) );
*reinterpret_cast<float *>( &((*out_psBytes)[0]) ) = typedValue; // overwrite string contents (sizeof( float ) bytes)
}
virtual void ConvertByteStreamToTypedValue( const ::std::string& sBytes, float *out_pTypedValue ) const OVERRIDE
{
Assert( out_pTypedValue );
Assert( sBytes.size() == sizeof( float ) );
*out_pTypedValue = *reinterpret_cast<const float *>( &sBytes[0] );
}
virtual bool BSupportsGameplayModificationAndNetworking() const OVERRIDE
{
return true;
}
virtual bool OnIterateAttributeValue( IEconItemAttributeIterator *pIterator, const CEconItemAttributeDefinition *pAttrDef, const attribute_data_union_t& value ) const OVERRIDE
{
Assert( pIterator );
Assert( pAttrDef );
// Call the appropriate virtual function on our iterator based on whatever type we represent.
return pIterator->OnIterateAttributeValue( pAttrDef, value.asFloat );
}
};
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
class CSchemaAttributeType_Default : public CSchemaAttributeTypeBase< GC_SCH_REFERENCE( CSchItemAttribute ) attrib_value_t >
{
public:
virtual bool BConvertStringToEconAttributeValue( const CEconItemAttributeDefinition *pAttrDef, const char *pszValue, union attribute_data_union_t *out_pValue ) const OVERRIDE
{
Assert( pAttrDef );
Assert( out_pValue );
// This is terrible backwards-compatibility code to support the pulling of values from econ asset classes.
if ( pAttrDef->IsStoredAsInteger() )
{
out_pValue->asUint32 = pszValue ? (uint32)V_atoui64( pszValue ) : 0;
}
else if ( pAttrDef->IsStoredAsFloat() )
{
out_pValue->asFloat = pszValue ? V_atof( pszValue ) : 0.0f;
}
else
{
Assert( !"Unknown storage type for CSchemaAttributeType_Default::BConvertStringToEconAttributeValue()!" );
return false;
}
return true;
}
virtual void ConvertEconAttributeValueToString( const CEconItemAttributeDefinition *pAttrDef, const attribute_data_union_t& value, std::string *out_ps ) const OVERRIDE
{
Assert( pAttrDef );
Assert( out_ps );
if( pAttrDef->IsStoredAsFloat() )
{
*out_ps = CFmtStr( "%f", value.asFloat ).Get();
}
else if( pAttrDef->IsStoredAsInteger() )
{
*out_ps = CFmtStr( "%u", value.asUint32 ).Get();
}
else
{
Assert( !"Unknown storage type for CSchemaAttributeType_Default::ConvertEconAttributeValueToString()!" );
}
}
virtual void ConvertTypedValueToByteStream( const attrib_value_t& typedValue, ::std::string *out_psBytes ) const OVERRIDE
{
Assert( out_psBytes );
Assert( out_psBytes->size() == 0 );
out_psBytes->resize( sizeof( attrib_value_t ) );
*reinterpret_cast<attrib_value_t *>( &((*out_psBytes)[0]) ) = typedValue; // overwrite string contents (sizeof( attrib_value_t ) bytes)
}
virtual void ConvertByteStreamToTypedValue( const ::std::string& sBytes, attrib_value_t *out_pTypedValue ) const OVERRIDE
{
Assert( out_pTypedValue );
// Game clients and servers may have partially out-of-date information, or may have downloaded a new schema
// but not know how to parse an attribute of a certain type, etc. In these cases, because we know we
// aren't on the GC, temporarily failing to load these values until the client shuts down and updates
// is about the best we can hope for.
if ( sBytes.size() < sizeof( attrib_value_t ) )
{
*out_pTypedValue = attrib_value_t();
return;
}
*out_pTypedValue = *reinterpret_cast<const attrib_value_t *>( &sBytes[0] );
}
virtual bool BSupportsGameplayModificationAndNetworking() const OVERRIDE
{
return true;
}
private:
};
//-----------------------------------------------------------------------------
// Purpose: Constructor
//-----------------------------------------------------------------------------
CEconItemAttributeDefinition::CEconItemAttributeDefinition( void )
: m_pKVAttribute( NULL ),
m_pAttrType( NULL ),
m_bHidden( false ),
m_bWebSchemaOutputForced( false ),
m_bStoredAsInteger( false ),
m_iEffectType( ATTRIB_EFFECT_NEUTRAL ),
m_iDescriptionFormat( 0 ),
m_pszDescriptionString( NULL ),
m_pszDescriptionTag( NULL ),
m_pszArmoryDesc( NULL ),
m_iScore( 0 ),
m_pszDefinitionName( NULL ),
m_pszAttributeClass( NULL )
#ifndef GC_DLL
, m_iszAttributeClass( NULL_STRING )
#endif
{
}
//-----------------------------------------------------------------------------
// Purpose: Copy constructor
//-----------------------------------------------------------------------------
CEconItemAttributeDefinition::CEconItemAttributeDefinition( const CEconItemAttributeDefinition &that )
{
(*this) = that;
}
//-----------------------------------------------------------------------------
// Purpose: Operator=
//-----------------------------------------------------------------------------
CEconItemAttributeDefinition &CEconItemAttributeDefinition::operator=( const CEconItemAttributeDefinition &rhs )
{
m_nDefIndex = rhs.m_nDefIndex;
m_pAttrType = rhs.m_pAttrType;
m_bHidden = rhs.m_bHidden;
m_bWebSchemaOutputForced = rhs.m_bWebSchemaOutputForced;
m_bStoredAsInteger = rhs.m_bStoredAsInteger;
m_bInstanceData = rhs.m_bInstanceData;
m_eAssetClassAttrExportRule = rhs.m_eAssetClassAttrExportRule;
m_iEffectType = rhs.m_iEffectType;
m_iDescriptionFormat = rhs.m_iDescriptionFormat;
m_pszDescriptionString = rhs.m_pszDescriptionString;
m_pszDescriptionTag = rhs.m_pszDescriptionTag;
m_pszArmoryDesc = rhs.m_pszArmoryDesc;
m_iScore = rhs.m_iScore;
m_pszDefinitionName = rhs.m_pszDefinitionName;
m_pszAttributeClass = rhs.m_pszAttributeClass;
m_unAssetClassBucket = rhs.m_unAssetClassBucket;
#ifndef GC_DLL
m_iszAttributeClass = rhs.m_iszAttributeClass;
#endif
m_pKVAttribute = NULL;
if ( NULL != rhs.m_pKVAttribute )
{
m_pKVAttribute = rhs.m_pKVAttribute->MakeCopy();
// Re-assign string pointers
m_pszDefinitionName = m_pKVAttribute->GetString("name");
m_pszDescriptionString = m_pKVAttribute->GetString( "description_string", NULL );
m_pszDescriptionTag = m_pKVAttribute->GetString( "description_tag", NULL );
m_pszArmoryDesc = m_pKVAttribute->GetString( "armory_desc", NULL );
m_pszAttributeClass = m_pKVAttribute->GetString( "attribute_class", NULL );
Assert( V_strcmp( m_pszDefinitionName, rhs.m_pszDefinitionName ) == 0 );
Assert( V_strcmp( m_pszDescriptionString, rhs.m_pszDescriptionString ) == 0 );
Assert( V_strcmp( m_pszDescriptionTag, rhs.m_pszDescriptionTag ) == 0 );
Assert( V_strcmp( m_pszArmoryDesc, rhs.m_pszArmoryDesc ) == 0 );
Assert( V_strcmp( m_pszAttributeClass, rhs.m_pszAttributeClass ) == 0 );
}
else
{
Assert( m_pszDefinitionName == NULL );
Assert( m_pszDescriptionString == NULL );
Assert( m_pszArmoryDesc == NULL );
Assert( m_pszAttributeClass == NULL );
}
return *this;
}
//-----------------------------------------------------------------------------
// Purpose: Destructor
//-----------------------------------------------------------------------------
CEconItemAttributeDefinition::~CEconItemAttributeDefinition( void )
{
if ( m_pKVAttribute )
m_pKVAttribute->deleteThis();
m_pKVAttribute = NULL;
}
//-----------------------------------------------------------------------------
// Purpose: Initialize the attribute definition
// Input: pKVAttribute - The KeyValues representation of the attribute
// schema - The overall item schema for this attribute
// pVecErrors - An optional vector that will contain error messages if
// the init fails.
// Output: True if initialization succeeded, false otherwise
//-----------------------------------------------------------------------------
bool CEconItemAttributeDefinition::BInitFromKV( KeyValues *pKVAttribute, CEconItemSchema &pschema, CUtlVector<CUtlString> *pVecErrors /* = NULL */ )
{
m_pKVAttribute = pKVAttribute->MakeCopy();
m_nDefIndex = Q_atoi( m_pKVAttribute->GetName() );
m_pszDefinitionName = m_pKVAttribute->GetString("name");
m_bHidden = m_pKVAttribute->GetInt( "hidden", 0 ) != 0;
m_bWebSchemaOutputForced = m_pKVAttribute->GetInt( "force_output_description", 0 ) != 0;
m_bStoredAsInteger = m_pKVAttribute->GetInt( "stored_as_integer", 0 ) != 0;
m_iScore = m_pKVAttribute->GetInt( "score", 0 );
m_iEffectType = (attrib_effect_types_t)StringFieldToInt( m_pKVAttribute->GetString("effect_type"), g_EffectTypes, ARRAYSIZE(g_EffectTypes) );
m_iDescriptionFormat = StringFieldToInt( m_pKVAttribute->GetString("description_format"), g_AttributeDescriptionFormats, ARRAYSIZE(g_AttributeDescriptionFormats) );
m_pszDescriptionString = m_pKVAttribute->GetString( "description_string", NULL );
m_pszDescriptionTag = m_pKVAttribute->GetString( "description_tag", NULL );
m_pszArmoryDesc = m_pKVAttribute->GetString( "armory_desc", NULL );
m_pszAttributeClass = m_pKVAttribute->GetString( "attribute_class", NULL );
m_bInstanceData = pKVAttribute->GetBool( "instance_data", false );
const char *pszAttrType = m_pKVAttribute->GetString( "attribute_type", NULL ); // NULL implies "default type" for backwards compatibility
m_pAttrType = pschema.GetAttributeType( pszAttrType );
SCHEMA_INIT_CHECK(
NULL != m_pAttrType,
CFmtStr( "Attribute definition %s: Unable to find attribute data type '%s'", m_pszDefinitionName, pszAttrType ? pszAttrType : "(default)" ) );
#if defined(CLIENT_DLL) || defined(GAME_DLL)
m_iszAttributeClass = NULL_STRING;
#endif
// Check for required fields
SCHEMA_INIT_CHECK(
NULL != m_pKVAttribute->FindKey( "name" ),
CFmtStr( "Attribute definition %s: Missing required field \"name\"", m_pKVAttribute->GetName() ) );
m_unAssetClassBucket = pKVAttribute->GetInt( "asset_class_bucket", 0 );
m_eAssetClassAttrExportRule = k_EAssetClassAttrExportRule_Default;
if ( char const *szRule = pKVAttribute->GetString( "asset_class_export", NULL ) )
{
if ( !V_stricmp( szRule, "skip" ) )
{
m_eAssetClassAttrExportRule = k_EAssetClassAttrExportRule_Skip;
}
else if ( !V_stricmp( szRule, "gconly" ) )
{
m_eAssetClassAttrExportRule = EAssetClassAttrExportRule_t( k_EAssetClassAttrExportRule_GCOnly | k_EAssetClassAttrExportRule_Skip );
}
else if ( !V_stricmp( szRule, "bucketed" ) )
{
SCHEMA_INIT_CHECK(
m_unAssetClassBucket,
CFmtStr( "Attribute definition %s: Asset class export rule '%s' is incompatible", m_pszDefinitionName, szRule ) );
m_eAssetClassAttrExportRule = k_EAssetClassAttrExportRule_Bucketed;
}
else if ( !V_stricmp( szRule, "default" ) )
{
m_eAssetClassAttrExportRule = k_EAssetClassAttrExportRule_Default;
}
else
{
SCHEMA_INIT_CHECK(
false,
CFmtStr( "Attribute definition %s: Invalid asset class export rule '%s'", m_pszDefinitionName, szRule ) );
}
}
// Check for misuse of asset class bucket
SCHEMA_INIT_CHECK(
( !m_unAssetClassBucket || m_bInstanceData ),
CFmtStr( "Attribute definition %s: Cannot use \"asset_class_bucket\" on class-level attributes", m_pKVAttribute->GetName() )
);
return SCHEMA_INIT_SUCCESS();
}
//-----------------------------------------------------------------------------
// Purpose: Constructor
//-----------------------------------------------------------------------------
CEconSoundMaterialDefinition::CEconSoundMaterialDefinition( void )
: m_nID( INT_MAX )
{
}
//-----------------------------------------------------------------------------
// Purpose: Copy constructor
//-----------------------------------------------------------------------------
CEconSoundMaterialDefinition::CEconSoundMaterialDefinition( const CEconSoundMaterialDefinition &that )
{
(*this) = that;
}
//-----------------------------------------------------------------------------
// Purpose: Operator=
//-----------------------------------------------------------------------------
CEconSoundMaterialDefinition &CEconSoundMaterialDefinition::operator=( const CEconSoundMaterialDefinition &rhs )
{
m_nID = rhs.m_nID;
m_strName = rhs.m_strName;
m_strStartDragSound = rhs.m_strStartDragSound;
m_strEndDragSound = rhs.m_strEndDragSound;
m_strEquipSound = rhs.m_strEquipSound;
return *this;
}
//-----------------------------------------------------------------------------
// Purpose: Initialize the rarity definition
//-----------------------------------------------------------------------------
bool CEconSoundMaterialDefinition::BInitFromKV( KeyValues *pKVSoundMaterial, CEconItemSchema &pschema, CUtlVector<CUtlString> *pVecErrors /* = NULL */ )
{
m_nID = pKVSoundMaterial->GetInt( "value", -1 );
m_strName = pKVSoundMaterial->GetName();
m_strStartDragSound = pKVSoundMaterial->GetString( "start_drag_sound" );
m_strEndDragSound = pKVSoundMaterial->GetString( "end_drag_sound" );
m_strEquipSound = pKVSoundMaterial->GetString( "equip_sound" );
// Check for required fields
SCHEMA_INIT_CHECK(
NULL != pKVSoundMaterial->FindKey( "value" ),
CFmtStr( "Sound material definition %s: Missing required field \"value\"", pKVSoundMaterial->GetName() ) );
#if defined(CLIENT_DLL) || defined(GAME_DLL)
return SCHEMA_INIT_SUCCESS();
#endif // GC_DLL
return SCHEMA_INIT_SUCCESS();
}
//-----------------------------------------------------------------------------
// Purpose: Constructor
//-----------------------------------------------------------------------------
CEconItemDefinition::CEconItemDefinition( void )
: m_pKVItem( NULL ),
m_bEnabled( false ),
m_unMinItemLevel( 0 ),
m_unMaxItemLevel( 100 ),
m_iArmoryRemap( 0 ),
m_iStoreRemap( 0 ),
m_nItemRarity( k_unItemRarity_Any ),
m_nItemQuality( k_unItemQuality_Any ),
m_nForcedItemQuality( k_unItemQuality_Any ),
m_nDefaultDropItemQuality( k_unItemQuality_Any ),
m_nDefaultDropQuantity( 1 ),
m_pProxyCriteria( NULL ),
m_bLoadOnDemand( false ),
m_pTool( NULL ),
m_rtExpiration( 0 ),
m_rtDefCreation( 0 ),
m_BundleInfo( NULL ),
m_unNumConcreteItems( 0 ),
m_nSoundMaterialID( 0 ),
m_nPopularitySeed( 0 ),
m_pszDefinitionName( NULL ),
m_pszItemClassname( NULL ),
m_pszClassToken( NULL ),
m_pszSlotToken( NULL ),
m_pszItemBaseName( NULL ),
m_pszItemTypeName( NULL ),
m_unItemTypeID( 0 ),
m_pszItemDesc( NULL ),
m_pszArmoryDesc( NULL ),
m_pszInventoryModel( NULL ),
m_pszInventoryImage( NULL ),
m_pszHolidayRestriction( NULL ),
m_iSubType( 0 ),
m_pszBaseDisplayModel( NULL ),
m_pszWorldDisplayModel( NULL ),
m_pszWorldDroppedModel( NULL ),
m_pszWorldExtraWearableModel( NULL ),
m_pszIconDefaultImage( NULL ),
m_pszParticleFile( NULL ),
m_pszParticleSnapshotFile( NULL ),
m_pInventoryImageData( NULL ),
m_pszBrassModelOverride( NULL ),
m_bHideBodyGroupsDeployedOnly( false ),
m_bAttachToHands( false ),
m_bAttachToHandsVMOnly( false ),
m_bProperName( false ),
m_bFlipViewModel( false ),
m_bActAsWearable( false ),
m_iDropType( 1 ),
m_bHidden( false ),
m_bShouldShowInArmory( false ),
m_bIsPackBundle( false ),
m_pOwningPackBundle( NULL ),
m_bBaseItem( false ),
m_pszItemLogClassname( NULL ),
m_pszItemIconClassname( NULL ),
#if ECONITEM_DATABASE_AUDIT_TABLES_FEATURE
m_pszDatabaseAuditTable( NULL ),
#endif
m_bImported( false ),
m_bOnePerAccountCDKEY( false ),
m_pszArmoryRemap( NULL ),
m_pszStoreRemap( NULL ),
m_bPublicItem( false ),
m_bIgnoreInCollectionView( false ),
m_nAssociatedItemsDefIndexes( NULL ),
m_pPortraitsKV( NULL ),
m_pAssetInfo( NULL ),
m_eItemType( k_EItemTypeNone ),
m_bAllowPurchaseStandalone( false )
{
m_pMapAlternateIcons = new CUtlMap<uint32, const char*>;
m_pMapAlternateIcons->SetLessFunc( DefLessFunc(uint32) );
}
//-----------------------------------------------------------------------------
// Purpose: Destructor
//-----------------------------------------------------------------------------
CEconItemDefinition::~CEconItemDefinition( void )
{
if ( m_pKVItem )
m_pKVItem->deleteThis();
m_pKVItem = NULL;
delete m_pProxyCriteria;
delete m_pTool;
delete m_BundleInfo;
delete m_pMapAlternateIcons;
if ( m_pInventoryImageData )
{
for ( int i = 0; i < MATERIAL_MAX_LIGHT_COUNT; i++ )
{
delete m_pInventoryImageData->m_pLightDesc[ i ];
}
delete m_pInventoryImageData->m_pCameraOffset;
delete m_pInventoryImageData->m_pCameraAngles;
delete m_pInventoryImageData;
}
PurgeStaticAttributes();
}
void CEconItemDefinition::PurgeStaticAttributes()
{
FOR_EACH_VEC( m_vecStaticAttributes, i )
{
static_attrib_t *pAttrib = &(m_vecStaticAttributes[ i ]);
const CEconItemAttributeDefinition *pAttrDef = pAttrib->GetAttributeDefinition();
Assert( pAttrDef );
const ISchemaAttributeType *pAttrType = pAttrDef->GetAttributeType();
Assert( pAttrType );
// Free up our temp loading memory.
pAttrType->UnloadEconAttributeValue( &pAttrib->m_value );
}
m_vecStaticAttributes.Purge();
}
#if defined(CLIENT_DLL) || defined(GAME_DLL)
//-----------------------------------------------------------------------------
// Purpose: Stomp our base data with extra testing data specified by the player
//-----------------------------------------------------------------------------
bool CEconItemDefinition::BInitFromTestItemKVs( int iNewDefIndex, KeyValues *pKVItem, CEconItemSchema &pschema )
{
// The KeyValues are stored in the player entity, so we can cache our name there
m_nDefIndex = iNewDefIndex;
bool bTestingExistingItem = pKVItem->GetInt( "test_existing_item", 0 ) != 0;
if ( !bTestingExistingItem )
{
m_pszDefinitionName = pKVItem->GetString( "name", NULL );
m_pszItemBaseName = pKVItem->GetString( "name", NULL );
#ifdef CLIENT_DLL
pKVItem->SetString( "name", VarArgs("Test Item %d", iNewDefIndex) );
#else
pKVItem->SetString( "name", UTIL_VarArgs("Test Item %d", iNewDefIndex) );
#endif
m_pszBaseDisplayModel = pKVItem->GetString( "model_player", NULL );
m_bAttachToHands = pKVItem->GetInt( "attach_to_hands", 0 ) != 0;
BInitVisualBlockFromKV( pKVItem, pschema );
m_pszParticleFile = pKVItem->GetString( "particle_file", NULL );
m_pszParticleSnapshotFile = pKVItem->GetString( "particle_snapshot", NULL );
}
// Handle attributes
PurgeStaticAttributes();
int iPaintCanIndex = pKVItem->GetInt("paintcan_index", 0);
if ( iPaintCanIndex )
{
static CSchemaAttributeDefHandle pAttrDef_PaintRGB( "set item tint RGB" );
const CEconItemDefinition *pCanDef = pschema.GetItemDefinition(iPaintCanIndex);
float flRGBVal;
if ( pCanDef && pAttrDef_PaintRGB && FindAttribute_UnsafeBitwiseCast<attrib_value_t>( pCanDef, pAttrDef_PaintRGB, &flRGBVal ) )
{
static_attrib_t& StaticAttrib = m_vecStaticAttributes[ m_vecStaticAttributes.AddToTail() ];
StaticAttrib.iDefIndex = pAttrDef_PaintRGB->GetDefinitionIndex();
StaticAttrib.m_value.asFloat = flRGBVal; // this is bad! but we're in crazy hack code for UI customization of item definitions that don't exist so
}
}
return true;
}
#endif
void AssetInfo::AddAssetModifier( AssetModifier* newMod )
{
int idx = m_mapAssetModifiers.Find( newMod->m_Type );
if ( !m_mapAssetModifiers.IsValidIndex( idx ) )
{
idx = m_mapAssetModifiers.Insert( newMod->m_Type, new CUtlVector<AssetModifier*> );
}
m_mapAssetModifiers[idx]->AddToTail( newMod );
}
CUtlVector<AssetModifier*>* AssetInfo::GetAssetModifiers( EAssetModifier type )
{
int idx = m_mapAssetModifiers.Find( type );
if ( m_mapAssetModifiers.IsValidIndex( idx ) )
{
return m_mapAssetModifiers[idx];
}
else
{
return NULL;
}
}
const char* AssetInfo::GetModifierByAsset( EAssetModifier type, const char* pszAsset, int iStyle )
{
CUtlVector<AssetModifier*>* pAssetModifierList = GetAssetModifiers( type );
if ( pAssetModifierList )
{
if ( iStyle > -1 )
{
// See if a modifier matches this style.
FOR_EACH_VEC( *pAssetModifierList, i )
{
AssetModifier* pMod = pAssetModifierList->Element(i);
if ( pMod->m_strAsset == pszAsset && pMod->m_iStyle == iStyle )
return pMod->m_strModifier;
}
}
// Return the first match.
FOR_EACH_VEC( *pAssetModifierList, i )
{
AssetModifier* pMod = pAssetModifierList->Element(i);
if ( pMod->m_strAsset == pszAsset )
return pMod->m_strModifier;
}
}
return NULL;
}
const char* AssetInfo::GetAssetByModifier( EAssetModifier type, const char* pszModifier, int iStyle )
{
CUtlVector<AssetModifier*>* pAssetModifierList = GetAssetModifiers( type );
if ( pAssetModifierList )
{
if ( iStyle > -1 )
{
// See if a modifier matches this style.
FOR_EACH_VEC( *pAssetModifierList, i )
{
AssetModifier* pMod = pAssetModifierList->Element(i);
if ( pMod->m_strModifier == pszModifier && pMod->m_iStyle == iStyle )
return pMod->m_strAsset;
}
}
// Return the first match.
FOR_EACH_VEC( *pAssetModifierList, i )
{
AssetModifier* pMod = pAssetModifierList->Element(i);
if ( pMod->m_strModifier == pszModifier )
return pMod->m_strAsset;
}
}
return NULL;
}
//-----------------------------------------------------------------------------
// Purpose: Handle parsing the per-team visual block from the keyvalues
//-----------------------------------------------------------------------------
void CEconItemDefinition::BInitVisualBlockFromKV( KeyValues *pKVItem, IEconItemSchema &pschema, CUtlVector<CUtlString> *pVecErrors )
{
// Visuals
m_pAssetInfo = NULL;
KeyValues *pVisualsKV = pKVItem->FindKey( "visuals" );
if ( pVisualsKV )
{
AssetInfo *pVisData = new AssetInfo();
#if defined(CLIENT_DLL) || defined(GAME_DLL)
KeyValues *pKVEntry = pVisualsKV->GetFirstSubKey();
while ( pKVEntry )
{
const char *pszEntry = pKVEntry->GetName();
if ( !Q_stricmp( pszEntry, "attached_models" ) )
{
FOR_EACH_SUBKEY( pKVEntry, pKVAttachedModelData )
{
int iAtt = pVisData->m_AttachedModels.AddToTail();
pVisData->m_AttachedModels[iAtt].m_iModelDisplayFlags = pKVAttachedModelData->GetInt( "model_display_flags", kAttachedModelDisplayFlag_MaskAll );
pVisData->m_AttachedModels[iAtt].m_pszModelName = pKVAttachedModelData->GetString( "model", NULL );
}
}
else if ( StringHasPrefix( pszEntry, "attached_particlesystem" ) )
{
// This replaces TF's "attached_particlesystem", "custom_particlesystem", and "custom_particlesystem2"
// It now indexes into the attributecontrolledparticlesystems list (use the "custom_type" field if you want a custom attribute)
int iParticleIndex;
if ( pschema.FindAttributeControlledParticleSystem( pKVEntry->GetString( "system", NULL ), &iParticleIndex ) )
{
attachedparticle_t attachedParticle;
attachedParticle.m_iParticleIndex = iParticleIndex;
attachedParticle.m_nStyle = pKVEntry->GetInt( "style", 0 );
pVisData->m_AttachedParticles.AddToTail( attachedParticle );
}
}
else if ( StringHasPrefix( pszEntry, "asset_modifier" ) )
{
AssetModifier* newMod = new AssetModifier();
newMod->m_Type = GetAssetModifierType( pKVEntry->GetString( "type", NULL ) );
newMod->m_strModifier = pKVEntry->GetString( "modifier", NULL );
newMod->m_flModifier = pKVEntry->GetFloat( "modifier", 0.f );
newMod->m_strAsset = pKVEntry->GetString( "asset", NULL );
newMod->m_iStyle = pKVEntry->GetInt( "style", 0 );
pVisData->AddAssetModifier( newMod );
// Items can have any number of asset modifiers.
// The variables can be used in any way, based on type, but the format is all the same to simplify the editor.
const char* pszAssetModifierType = pKVEntry->GetString( "type", NULL );
const char* pszAssetName = pKVEntry->GetString( "asset", NULL );
const char* pszModifierName = pKVEntry->GetString( "modifier", NULL );
float flFrequency = pKVEntry->GetFloat( "frequency", 1.0 );
if ( FStrEq( pszAssetModifierType, "activity" ) )
{
int iAtt = pVisData->m_Animations.AddToTail();
pVisData->m_Animations[iAtt].iActivity = -2;
pVisData->m_Animations[iAtt].pszActivity = pszAssetName;
pVisData->m_Animations[iAtt].iReplacement = kActivityLookup_Unknown;
pVisData->m_Animations[iAtt].pszReplacement = pszModifierName;
pVisData->m_Animations[iAtt].flFrequency = flFrequency;
}
else if ( FStrEq( pszAssetModifierType, "announcer" ) )
{
pVisData->m_pszAnnouncerName = pszAssetName;
pVisData->m_pszAnnouncerResource = pszModifierName;
}
else if ( FStrEq( pszAssetModifierType, "announcer_preview" ) )
{
CUtlString strFilename = pszAssetName;
announcer_preview_t preview;
preview.m_strFileName = pszAssetName;
preview.m_strCaption = pszModifierName;
pVisData->m_vecAnnouncerPreview.AddToTail( preview );
}
else if ( FStrEq( pszAssetModifierType, "hud_skin" ) )
{
pVisData->m_pszHudSkinName = pszAssetName;
}
else if ( FStrEq( pszAssetModifierType, "ability_name" ) )
{
pVisData->m_pszAbilityName = pszAssetName;
}
else if ( FStrEq( pszAssetModifierType, "sound" ) )
{
int iSound = pVisData->m_Sounds.AddToTail();
pVisData->m_Sounds[iSound].pszSound = pszAssetName;
pVisData->m_Sounds[iSound].pszReplacement = pszModifierName;
}
else if ( FStrEq( pszAssetModifierType, "speech" ) )
{
pVisData->m_pszSpeechConcept = pszAssetName;
pVisData->m_pszChatMessage = pszModifierName;
}
else if ( FStrEq( pszAssetModifierType, "particle" ) )
{
int iParticle = pVisData->m_Particles.AddToTail();
pVisData->m_Particles[iParticle].pszParticle = pszAssetName;
pVisData->m_Particles[iParticle].pszReplacement = pszModifierName;
}
else if ( FStrEq( pszAssetModifierType, "particle_snapshot" ) )
{
int iParticle = pVisData->m_ParticleSnapshots.AddToTail();
pVisData->m_ParticleSnapshots[iParticle].pszParticleSnapshot = pszAssetName;
pVisData->m_ParticleSnapshots[iParticle].pszReplacement = pszModifierName;
}
else if ( FStrEq( pszAssetModifierType, "particle_control_point" ) )
{
int iParticleCP = pVisData->m_ParticleControlPoints.AddToTail();
pVisData->m_ParticleControlPoints[iParticleCP].pszParticle = pszAssetName;
pVisData->m_ParticleControlPoints[iParticleCP].nParticleControlPoint = pKVEntry->GetInt( "control_point_number", 0 );
UTIL_StringToVector( pVisData->m_ParticleControlPoints[iParticleCP].vecCPValue.Base(), pKVEntry->GetString( "cp_position", "0 0 0" ) );
}
else if ( FStrEq( pszAssetModifierType, "entity_model" ) )
{
pVisData->m_pszEntityClass = pszAssetName;
pVisData->m_pszEntityModel = pszModifierName;
}
else if ( FStrEq( pszAssetModifierType, "view_model" ) )
{
pVisData->m_pszEntityClass = pszAssetName;
pVisData->m_pszViewModel = pszModifierName;
}
else if ( FStrEq( pszAssetModifierType, "icon_replacement" ) )
{
pVisData->m_pszOriginalIcon = pszAssetName;
pVisData->m_pszNewIcon = pszModifierName;
}
else if ( FStrEq( pszAssetModifierType, "ability_icon_replacement" ) )
{
ability_icon_replacement_t iconReplacement;
iconReplacement.m_strAbilityName = pszAssetName;
iconReplacement.m_strReplacement = pszModifierName;
pVisData->m_vecAbilityIconReplacements.AddToTail( iconReplacement );
}
else if ( FStrEq( pszAssetModifierType, "entity_scale" ) )
{
pVisData->m_pszScaleClass = pszAssetName;
pVisData->m_flScaleSize = pKVEntry->GetFloat( "modifier", 1.f );
}
}
else if ( !Q_stricmp( pszEntry, "animation" ) )
{
int iAtt = pVisData->m_Animations.AddToTail();
pVisData->m_Animations[iAtt].iActivity = -2; // We can't look it up yet, the activity list hasn't been populated.
pVisData->m_Animations[iAtt].pszActivity = pKVEntry->GetString( "activity", NULL );
const char* playback = pKVEntry->GetString( "playback" );
if ( playback )
{
pVisData->m_Animations[iAtt].iPlayback = (wearableanimplayback_t)StringFieldToInt( pKVEntry->GetString("playback"), g_WearableAnimTypeStrings, ARRAYSIZE(g_WearableAnimTypeStrings) );
}
pVisData->m_Animations[iAtt].iReplacement = kActivityLookup_Unknown;
pVisData->m_Animations[iAtt].pszReplacement = pKVEntry->GetString( "replacement", NULL );
pVisData->m_Animations[iAtt].pszSequence = pKVEntry->GetString( "sequence", NULL );
pVisData->m_Animations[iAtt].pszScene = pKVEntry->GetString( "scene", NULL );
pVisData->m_Animations[iAtt].pszRequiredItem = pKVEntry->GetString( "required_item", NULL );
pVisData->m_Animations[iAtt].flFrequency = pKVEntry->GetFloat( "frequency", 1.0 );
}
else if ( !Q_stricmp( pszEntry, "player_bodygroups" ) )
{
FOR_EACH_SUBKEY( pKVEntry, pKVBodygroupKey )
{
const char *pszBodygroupName = pKVBodygroupKey->GetName();
int iValue = pKVBodygroupKey->GetInt();
// Track bodygroup information for this item in particular.
pVisData->m_ModifiedBodyGroupNames.Insert( pszBodygroupName, iValue );
// Track global schema state.
CEconItemSchema* pItemSchema = (CEconItemSchema*) &pschema;
pItemSchema->AssignDefaultBodygroupState( pszBodygroupName, iValue );
}
}
else if ( !Q_stricmp( pszEntry, "skin" ) )
{
pVisData->iSkin = pKVEntry->GetInt();
}
else if ( !Q_stricmp( pszEntry, "use_per_class_bodygroups" ) )
{
pVisData->bUsePerClassBodygroups = pKVEntry->GetBool();
}
else if ( !Q_stricmp( pszEntry, "muzzle_flash" ) )
{
pVisData->m_pszMuzzleFlash = pKVEntry->GetString();
}
else if ( !Q_stricmp( pszEntry, "tracer_effect" ) )
{
pVisData->m_pszTracerEffect = pKVEntry->GetString();
}
else if ( !Q_stricmp( pszEntry, "particle_effect" ) )
{
pVisData->m_pszParticleEffect = pKVEntry->GetString();
}
else if ( !Q_stricmp( pszEntry, "particle_snapshot" ) )
{
pVisData->m_pszParticleSnapshot = pKVEntry->GetString();
}
else if ( StringHasPrefix( pszEntry, "custom_sound" ) ) // TF2 style custom sounds.
{
int iIndex = 0;
if ( pszEntry[12] )
{
iIndex = clamp( atoi( &pszEntry[12] ), 0, MAX_VISUALS_CUSTOM_SOUNDS-1 );
}
pVisData->m_pszCustomSounds[iIndex] = pKVEntry->GetString();
}
else if ( !Q_stricmp( pszEntry, "material_override" ) )
{
pVisData->m_pszMaterialOverride = pKVEntry->GetString();
}
else if ( StringHasPrefix( pszEntry, "sound_" ) ) // Orange box style weapon sounds.
{
int iIndex = GetWeaponSoundFromString( &pszEntry[6] );
if ( iIndex != -1 )
{
pVisData->m_pszWeaponSoundReplacements[iIndex] = pKVEntry->GetString();
}
}
else if ( !Q_stricmp( pszEntry, "code_controlled_bodygroup" ) )
{
const char *pBodyGroupName = pKVEntry->GetString( "bodygroup", NULL );
const char *pFuncName = pKVEntry->GetString( "function", NULL );
if ( pBodyGroupName && pFuncName )
{
codecontrolledbodygroupdata_t ccbgd = { pFuncName, NULL };
pVisData->m_CodeControlledBodyGroupNames.Insert( pBodyGroupName, ccbgd );
}
}
else if ( !Q_stricmp( pszEntry, "vm_bodygroup_override" ) )
{
pVisData->m_iViewModelBodyGroupOverride = pKVEntry->GetInt();
}
else if ( !Q_stricmp( pszEntry, "vm_bodygroup_state_override" ) )
{
pVisData->m_iViewModelBodyGroupStateOverride = pKVEntry->GetInt();
}
else if ( !Q_stricmp( pszEntry, "wm_bodygroup_override" ) )
{
pVisData->m_iWorldModelBodyGroupOverride = pKVEntry->GetInt();
}
else if ( !Q_stricmp( pszEntry, "wm_bodygroup_state_override" ) )
{
pVisData->m_iWorldModelBodyGroupStateOverride = pKVEntry->GetInt();
}
else if ( !Q_stricmp( pszEntry, "skip_model_combine" ) )
{
pVisData->m_bSkipModelCombine = pKVEntry->GetBool();
}
else if ( !Q_stricmp( pszEntry, "animation_modifiers" ) )
{
FOR_EACH_SUBKEY( pKVEntry, pKVAnimMod )
{
pVisData->m_vecAnimationModifiers.AddToTail( pKVAnimMod->GetName() );
}
}
#ifdef CSTRIKE15
////////////// CS:GO string attributes
else if ( !Q_stricmp( pszEntry, "primary_ammo" ) )
{
pVisData->m_pszPrimaryAmmo = pKVEntry->GetString();
}
else if ( !Q_stricmp( pszEntry, "weapon_type" ) )
{
pVisData->m_pszWeaponTypeString = pKVEntry->GetString();
}
else if ( !Q_stricmp( pszEntry, "addon_location" ) )
{
pVisData->m_pszAddonLocation = pKVEntry->GetString();
}
else if ( !Q_stricmp( pszEntry, "eject_brass_effect" ) )
{
pVisData->m_pszEjectBrassEffect = pKVEntry->GetString();
}
else if ( !Q_stricmp( pszEntry, "tracer_effect" ) )
{
pVisData->m_pszTracerEffect = pKVEntry->GetString();
}
else if ( !Q_stricmp( pszEntry, "muzzle_flash_effect_1st_person" ) )
{
pVisData->m_pszMuzzleFlashEffect1stPerson = pKVEntry->GetString();
}
else if ( !Q_stricmp( pszEntry, "muzzle_flash_effect_1st_person_alt" ) )
{
pVisData->m_pszMuzzleFlashEffect1stPersonAlt = pKVEntry->GetString();
}
else if ( !Q_stricmp( pszEntry, "muzzle_flash_effect_3rd_person" ) )
{
pVisData->m_pszMuzzleFlashEffect3rdPerson = pKVEntry->GetString();
}
else if ( !Q_stricmp( pszEntry, "muzzle_flash_effect_3rd_person_alt" ) )
{
pVisData->m_pszMuzzleFlashEffect3rdPersonAlt = pKVEntry->GetString();
}
else if ( !Q_stricmp( pszEntry, "heat_effect" ) )
{
pVisData->m_pszHeatEffect = pKVEntry->GetString();
}
else if ( !Q_stricmp( pszEntry, "player_animation_extension" ) )
{
pVisData->m_pszPlayerAnimationExtension = pKVEntry->GetString();
}
#endif //#ifdef CSTRIKE15
pKVEntry = pKVEntry->GetNextKey();
}
#endif // defined(CLIENT_DLL) || defined(GAME_DLL)
KeyValues *pStylesDataKV = pVisualsKV->FindKey( "styles" );
if ( pStylesDataKV )
{
BInitStylesBlockFromKV( pStylesDataKV, *(CEconItemSchema*)( &pschema ), pVisData, pVecErrors );
}
KeyValues * pKVAlternateIcons = pVisualsKV->FindKey( "alternate_icons" );
if ( NULL != pKVAlternateIcons )
{
BInitAlternateIconsFromKV( pKVAlternateIcons, *(CEconItemSchema*)( &pschema ), pVisData, pVecErrors );
}
m_pAssetInfo = pVisData;
}
}
#if defined(CLIENT_DLL) || defined(GAME_DLL)
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CEconItemDefinition::GeneratePrecacheModelStrings( bool bDynamicLoad, CUtlVector<const char *> *out_pVecModelStrings ) const
{
Assert( out_pVecModelStrings );
// Add base model.
out_pVecModelStrings->AddToTail( GetBasePlayerDisplayModel() );
// Add styles.
if ( GetNumStyles() )
{
for ( style_index_t i=0; i<GetNumStyles(); ++i )
{
const CEconStyleInfo *pStyle = GetStyleInfo( i );
Assert( pStyle );
pStyle->GeneratePrecacheModelStringsForStyle( out_pVecModelStrings );
}
}
if ( m_pAssetInfo )
{
// Precache all the attached models.
for ( int model = 0; model < m_pAssetInfo->m_AttachedModels.Count(); model++ )
{
out_pVecModelStrings->AddToTail( m_pAssetInfo->m_AttachedModels[model].m_pszModelName );
}
// Precache any model overrides.
if ( m_pAssetInfo->m_pszEntityModel )
{
out_pVecModelStrings->AddToTail( m_pAssetInfo->m_pszEntityModel );
}
if ( m_pAssetInfo->m_pszViewModel )
{
out_pVecModelStrings->AddToTail( m_pAssetInfo->m_pszViewModel );
}
// Precache hero model change.
CUtlVector<AssetModifier*>* pHeroModelChanges = m_pAssetInfo->GetAssetModifiers( AM_HeroModelChange );
if ( pHeroModelChanges )
{
FOR_EACH_VEC( *pHeroModelChanges, i )
{
AssetModifier* pAssetMod = pHeroModelChanges->Element( i );
if ( pAssetMod )
{
out_pVecModelStrings->AddToTail( pAssetMod->m_strModifier.Get() );
}
}
}
// Precache couriers.
pHeroModelChanges = m_pAssetInfo->GetAssetModifiers( AM_Courier );
if ( pHeroModelChanges )
{
FOR_EACH_VEC( *pHeroModelChanges, i )
{
AssetModifier* pAssetMod = pHeroModelChanges->Element( i );
if ( pAssetMod )
{
out_pVecModelStrings->AddToTail( pAssetMod->m_strModifier.Get() );
}
}
}
// Precache flying couriers.
pHeroModelChanges = m_pAssetInfo->GetAssetModifiers( AM_CourierFlying );
if ( pHeroModelChanges )
{
FOR_EACH_VEC( *pHeroModelChanges, i )
{
AssetModifier* pAssetMod = pHeroModelChanges->Element( i );
if ( pAssetMod )
{
out_pVecModelStrings->AddToTail( pAssetMod->m_strModifier.Get() );
}
}
}
}
#ifdef DOTA_DLL
// We don't need to cache the inventory model, because it's never loaded by the game.
#else
if ( GetIconDisplayModel() )
{
out_pVecModelStrings->AddToTail( GetIconDisplayModel() );
}
#endif
if ( GetBuyMenuDisplayModel() )
{
out_pVecModelStrings->AddToTail( GetBuyMenuDisplayModel() );
}
if ( GetWorldDroppedModel() )
{
out_pVecModelStrings->AddToTail( GetWorldDroppedModel() );
}
if ( GetMagazineModel() )
{
out_pVecModelStrings->AddToTail( GetMagazineModel() );
}
if ( GetScopeLensMaskModel() )
{
out_pVecModelStrings->AddToTail( GetScopeLensMaskModel() );
}
const KillEaterScoreMap_t& mapKillEaterScoreTypes = GetItemSchema()->GetKillEaterScoreTypes();
FOR_EACH_MAP( mapKillEaterScoreTypes, i )
{
const char *pchStatTrakModel = GetStatTrakModelByType( mapKillEaterScoreTypes[ i ].m_nValue );
if ( pchStatTrakModel )
{
out_pVecModelStrings->AddToTail( pchStatTrakModel );
}
}
if ( GetUidModel() )
{
out_pVecModelStrings->AddToTail( GetUidModel() );
}
for ( int i=0; i<GetNumSupportedStickerSlots(); ++i )
{
if ( GetStickerSlotModelBySlotIndex(i) )
{
out_pVecModelStrings->AddToTail( GetStickerSlotModelBySlotIndex(i) );
}
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CEconItemDefinition::GeneratePrecacheSoundStrings( CUtlVector<const char*> *out_pVecSoundStrings ) const
{
Assert( out_pVecSoundStrings );
// Precache all replacement sounds.
if ( m_pAssetInfo )
{
for ( int sound=0; sound<m_pAssetInfo->m_Sounds.Count(); sound++ )
{
out_pVecSoundStrings->AddToTail( m_pAssetInfo->m_Sounds[sound].pszReplacement );
}
for ( int sound = 0; sound < ARRAYSIZE( m_pAssetInfo->m_pszWeaponSoundReplacements ); sound++ )
{
if ( !m_pAssetInfo->m_pszWeaponSoundReplacements[ sound ] )
continue;
out_pVecSoundStrings->AddToTail( m_pAssetInfo->m_pszWeaponSoundReplacements[ sound ] );
}
}
}
void CEconItemDefinition::GeneratePrecacheEffectStrings( CUtlVector<const char*> *out_pVecEffectStrings ) const
{
Assert( out_pVecEffectStrings );
if ( !m_pAssetInfo )
return;
if ( m_pAssetInfo->m_pszEjectBrassEffect )
{
out_pVecEffectStrings->AddToTail( m_pAssetInfo->m_pszEjectBrassEffect );
}
if ( m_pAssetInfo->m_pszTracerEffect )
{
out_pVecEffectStrings->AddToTail( m_pAssetInfo->m_pszTracerEffect );
}
if ( m_pAssetInfo->m_pszMuzzleFlashEffect1stPerson )
{
out_pVecEffectStrings->AddToTail( m_pAssetInfo->m_pszMuzzleFlashEffect1stPerson );
}
if ( m_pAssetInfo->m_pszMuzzleFlashEffect1stPersonAlt )
{
out_pVecEffectStrings->AddToTail( m_pAssetInfo->m_pszMuzzleFlashEffect1stPersonAlt );
}
if ( m_pAssetInfo->m_pszMuzzleFlashEffect3rdPerson )
{
out_pVecEffectStrings->AddToTail( m_pAssetInfo->m_pszMuzzleFlashEffect3rdPerson );
}
if ( m_pAssetInfo->m_pszMuzzleFlashEffect3rdPersonAlt )
{
out_pVecEffectStrings->AddToTail( m_pAssetInfo->m_pszMuzzleFlashEffect3rdPersonAlt );
}
if ( m_pAssetInfo->m_pszHeatEffect )
{
out_pVecEffectStrings->AddToTail( m_pAssetInfo->m_pszHeatEffect );
}
}
#endif // #if defined(CLIENT_DLL) || defined(GAME_DLL)
//-----------------------------------------------------------------------------
// Purpose: Parse the alternate icons for this item.
//-----------------------------------------------------------------------------
bool CEconItemDefinition::BInitAlternateIconsFromKV( KeyValues *pKVAlternateIcons, CEconItemSchema &pschema, AssetInfo *pVisData, CUtlVector<CUtlString> *pVecErrors )
{
m_pMapAlternateIcons->Purge();
FOR_EACH_TRUE_SUBKEY( pKVAlternateIcons, pKVAlternateIcon )
{
int iIconIndex = Q_atoi( pKVAlternateIcon->GetName() );
SCHEMA_INIT_CHECK(
!m_pMapAlternateIcons->Find( iIconIndex ) != m_pMapAlternateIcons->InvalidIndex(),
CFmtStr( "Duplicate alternate icon definition (%d)", iIconIndex ) );
SCHEMA_INIT_CHECK(
iIconIndex >= 0,
CFmtStr( "Alternate icon definition index %d must be greater than or equal to zero", iIconIndex ) );
m_pMapAlternateIcons->Insert( iIconIndex, pKVAlternateIcon->GetString( "icon_path" ) );
}
return SCHEMA_INIT_SUCCESS();
}
//-----------------------------------------------------------------------------
// Purpose: Parse the styles sub-section of the visuals block.
//-----------------------------------------------------------------------------
void CEconItemDefinition::BInitStylesBlockFromKV( KeyValues *pKVStyles, CEconItemSchema &pschema, AssetInfo *pVisData, CUtlVector<CUtlString> *pVecErrors )
{
FOR_EACH_SUBKEY( pKVStyles, pKVStyle )
{
CEconStyleInfo *pStyleInfo = pschema.CreateEconStyleInfo();
Assert( pStyleInfo );
pStyleInfo->BInitFromKV( pKVStyle, pschema, pVecErrors );
pVisData->m_Styles.AddToTail( pStyleInfo );
}
}
//-----------------------------------------------------------------------------
// Purpose: Parse one style from the styles block.
//-----------------------------------------------------------------------------
void CEconStyleInfo::BInitFromKV( KeyValues *pKVStyle, CEconItemSchema &pschema, CUtlVector<CUtlString> *pVecErrors )
{
enum { kInvalidSkinKey = -1, };
Assert( pKVStyle );
m_iIndex = atoi( pKVStyle->GetName() );
// A "skin" entry means "use this index for all of our teams, no matter how many we have".
int iCommonSkin = pKVStyle->GetInt( "skin", kInvalidSkinKey );
if ( iCommonSkin != kInvalidSkinKey )
{
m_iSkin = iCommonSkin;
}
// If we don't have a base entry, we look for a unique entry for each team. This will be
// handled in a subclass if necessary.
// Are we hiding additional bodygroups when this style is active?
KeyValues *pKVHideBodygroups = pKVStyle->FindKey( "additional_hidden_bodygroups" );
if ( pKVHideBodygroups )
{
FOR_EACH_SUBKEY( pKVHideBodygroups, pKVBodygroup )
{
m_vecAdditionalHideBodygroups.AddToTail( pKVBodygroup->GetName() );
}
}
// Remaining common properties.
m_pszName = pKVStyle->GetString( "name", "#TF_UnknownStyle" );
m_pszBasePlayerModel = pKVStyle->GetString( "model_player", NULL );
// An alternate icon to use.
m_iIcon = pKVStyle->GetInt( "alternate_icon", 0 );
// Unlock rules.
KeyValues* pUnlockInfo = pKVStyle->FindKey( "unlock" );
if ( pUnlockInfo )
{
m_UnlockInfo.iPrice = pUnlockInfo->GetInt( "price", 0 );
m_UnlockInfo.pszItemName = pUnlockInfo->GetString( "item", NULL );
m_UnlockInfo.iStylePreReq = pUnlockInfo->GetInt( "style", 0 );
m_UnlockInfo.pszAttrib = pUnlockInfo->GetString( "attribute", NULL );
m_UnlockInfo.iAttribValue = pUnlockInfo->GetInt( "attribute_value", 0 );
}
}
#if defined(CLIENT_DLL) || defined(GAME_DLL)
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CEconStyleInfo::GeneratePrecacheModelStringsForStyle( CUtlVector<const char *> *out_pVecModelStrings ) const
{
Assert( out_pVecModelStrings );
if ( GetBasePlayerDisplayModel() != NULL )
{
out_pVecModelStrings->AddToTail( GetBasePlayerDisplayModel() );
}
}
#endif
//-----------------------------------------------------------------------------
// Purpose: Item definition initialization helpers.
//-----------------------------------------------------------------------------
static void RecursiveInheritKeyValues( KeyValues *out_pValues, KeyValues *pInstance )
{
FOR_EACH_SUBKEY( pInstance, pSubKey )
{
KeyValues::types_t eType = pSubKey->GetDataType();
switch ( eType )
{
case KeyValues::TYPE_STRING: out_pValues->SetString( pSubKey->GetName(), pSubKey->GetString() ); break;
case KeyValues::TYPE_INT: out_pValues->SetInt( pSubKey->GetName(), pSubKey->GetInt() ); break;
case KeyValues::TYPE_FLOAT: out_pValues->SetFloat( pSubKey->GetName(), pSubKey->GetFloat() ); break;
case KeyValues::TYPE_WSTRING: out_pValues->SetWString( pSubKey->GetName(), pSubKey->GetWString() ); break;
case KeyValues::TYPE_COLOR: out_pValues->SetColor( pSubKey->GetName(), pSubKey->GetColor() ) ; break;
case KeyValues::TYPE_UINT64: out_pValues->SetUint64( pSubKey->GetName(), pSubKey->GetUint64() ) ; break;
// "NONE" means "KeyValues"
case KeyValues::TYPE_NONE:
{
// We may already have this part of the tree to stuff data into/overwrite, or we
// may have to make a new block.
KeyValues *pNewChild = out_pValues->FindKey( pSubKey->GetName() );
if ( !pNewChild )
{
pNewChild = out_pValues->CreateNewKey();
pNewChild->SetName( pSubKey->GetName() );
}
RecursiveInheritKeyValues( pNewChild, pSubKey );
break;
}
case KeyValues::TYPE_PTR:
default:
Assert( !"Unhandled data type for KeyValues inheritance!" );
break;
}
}
}
#ifdef _DEBUG
#define DEBUG_SCHEMA_WRITE_TMP_FILE 0
#if DEBUG_SCHEMA_WRITE_TMP_FILE
#include <stdio.h>
class CKeyValuesDumpContextAsSchemaFile : public IKeyValuesDumpContextAsText
{
public:
// Overrides developer level to dump in DevMsg, zero to dump as Msg
CKeyValuesDumpContextAsSchemaFile() {}
public:
virtual bool KvWriteText( char const *szText )
{
FILE *f = NULL;
fopen_s( &f, "c:/tmp/econ_item_schema.txt", "a+t" );
if ( f )
{
fprintf( f, "%s", szText );
fclose( f );
}
return true;
}
}
g_schemadbg;
#endif
#endif
// Always returns a newly allocated copy of KeyValues
// Applies prefabs supporting multiple inheritance from Right-to-Left (RTL)
// this way "prefab" "valve tournament p250" will start with "p250" then apply "tournament" prefab on top of that,
// then will apply "valve" prefab on top of that and then the values of the actual instance.
static KeyValues *InheritKeyValuesRTLMulti( KeyValues *pInstance, CEconItemSchema &pschema )
{
Assert( pInstance );
KeyValues *pFinalValues = pInstance->MakeCopy();
#if DEBUG_SCHEMA_WRITE_TMP_FILE
static int s_nDebugIndentLevel = 0;
#endif
CUtlVector< char * > arrPrefabs;
if ( const char *svPrefabName = pFinalValues->GetString( "prefab", NULL ) )
{
Q_SplitString( svPrefabName, " ", arrPrefabs );
#if DEBUG_SCHEMA_WRITE_TMP_FILE
if ( arrPrefabs.Count() )
{
++ s_nDebugIndentLevel;
g_schemadbg.KvWriteIndent( s_nDebugIndentLevel );
g_schemadbg.KvWriteText( CFmtStr( "Expanding data with %d prefabs:--\n", arrPrefabs.Count() ) );
pFinalValues->Dump( &g_schemadbg, s_nDebugIndentLevel );
g_schemadbg.KvWriteIndent( s_nDebugIndentLevel );
g_schemadbg.KvWriteText( "--\n" );
}
#endif
}
FOR_EACH_VEC_BACK( arrPrefabs, i )
{
KeyValues *pKVPrefab = pschema.FindDefinitionPrefabByName( arrPrefabs[i] );
if ( !pKVPrefab ) continue;
#if DEBUG_SCHEMA_WRITE_TMP_FILE
{
g_schemadbg.KvWriteIndent( s_nDebugIndentLevel );
g_schemadbg.KvWriteText( CFmtStr( "Appending prefab '%s':\n", arrPrefabs[i] ) );
pKVPrefab->Dump( &g_schemadbg, s_nDebugIndentLevel );
}
#endif
pKVPrefab = InheritKeyValuesRTLMulti( pKVPrefab, pschema );
pKVPrefab->SetName( pFinalValues->GetName() );
RecursiveInheritKeyValues( pKVPrefab, pFinalValues );
pFinalValues->deleteThis();
pFinalValues = pKVPrefab;
#if DEBUG_SCHEMA_WRITE_TMP_FILE
{
g_schemadbg.KvWriteIndent( s_nDebugIndentLevel );
g_schemadbg.KvWriteText( CFmtStr( "After appending prefab '%s':\n", arrPrefabs[ i ] ) );
pFinalValues->Dump( &g_schemadbg, s_nDebugIndentLevel );
}
#endif
}
#if DEBUG_SCHEMA_WRITE_TMP_FILE
if ( arrPrefabs.Count() )
{
g_schemadbg.KvWriteIndent( s_nDebugIndentLevel );
g_schemadbg.KvWriteText( CFmtStr( "Finished expanding data with %d prefabs:--\n", arrPrefabs.Count() ) );
pFinalValues->Dump( &g_schemadbg, s_nDebugIndentLevel );
g_schemadbg.KvWriteIndent( s_nDebugIndentLevel );
g_schemadbg.KvWriteText( "--\n" );
-- s_nDebugIndentLevel;
}
#endif
arrPrefabs.PurgeAndDeleteElements();
return pFinalValues;
}
KeyValues *CEconItemSchema::FindDefinitionPrefabByName( const char *pszPrefabName ) const
{
int iIndex = m_mapDefinitionPrefabs.Find( pszPrefabName );
if ( m_mapDefinitionPrefabs.IsValidIndex( iIndex ) )
return m_mapDefinitionPrefabs[iIndex];
return NULL;
}
//-----------------------------------------------------------------------------
// Purpose: Initialize the item definition
// Input: pKVItem - The KeyValues representation of the item
// schema - The overall item schema for this item
// pVecErrors - An optional vector that will contain error messages if
// the init fails.
// Output: True if initialization succeeded, false otherwise
//-----------------------------------------------------------------------------
bool CEconItemDefinition::BInitFromKV( KeyValues *pKVItem, CEconItemSchema &pschema, CUtlVector<CUtlString> *pVecErrors /* = NULL */ )
{
// Set standard members
m_pKVItem = InheritKeyValuesRTLMulti( pKVItem, pschema );
m_bEnabled = m_pKVItem->GetBool( "enabled" );
m_bLoadOnDemand = m_pKVItem->GetBool( "loadondemand" );
m_nDefIndex = Q_atoi( m_pKVItem->GetName() );
m_unMinItemLevel = (uint32)m_pKVItem->GetInt( "min_ilevel", pschema.GetMinLevel() );
m_unMaxItemLevel = (uint32)m_pKVItem->GetInt( "max_ilevel", pschema.GetMaxLevel() );
m_nDefaultDropQuantity = m_pKVItem->GetInt( "default_drop_quantity", 1 );
KeyValues *pKVMultiAssocItems = m_pKVItem->FindKey( "associated_items" ),
*pKVSingleAssocItem = m_pKVItem->FindKey( "associated_item" );
// Maybe we have multiple entries?
if ( pKVMultiAssocItems )
{
for ( KeyValues *pKVAssocItem = pKVMultiAssocItems->GetFirstSubKey(); pKVAssocItem; pKVAssocItem = pKVAssocItem->GetNextKey() )
{
m_nAssociatedItemsDefIndexes.AddToTail( atoi( pKVAssocItem->GetName() ) );
}
}
// This is our one-and-only-one associated item
else if ( pKVSingleAssocItem )
{
const char *pAssocItem = pKVSingleAssocItem->GetString( (const char *)NULL, NULL );
if ( pAssocItem )
{
m_nAssociatedItemsDefIndexes.AddToTail( atoi( pAssocItem ) );
}
}
m_nSoundMaterialID = m_pKVItem->GetInt( "sound_material" );
m_nPopularitySeed = m_pKVItem->GetInt( "popularity_seed", 0 );
// initializing this one first so that it will be available for all the errors below
m_pszDefinitionName = m_pKVItem->GetString( "name", NULL );
m_bDisableStyleSelection = m_pKVItem->GetBool( "disable_style_selector", false );
#if defined(CLIENT_DLL) || defined(GAME_DLL)
// We read this manually here in the game dlls. The GC reads it below while checking pschema.
pschema.BGetItemQualityFromName( m_pKVItem->GetString( "item_quality" ), &m_nItemQuality );
pschema.BGetItemQualityFromName( m_pKVItem->GetString( "forced_item_quality" ), &m_nForcedItemQuality );
pschema.BGetItemQualityFromName( m_pKVItem->GetString( "default_drop_quality" ), &m_nDefaultDropItemQuality );
pschema.BGetItemRarityFromName( m_pKVItem->GetString( "item_rarity" ), &m_nItemRarity );
#endif
// Check for required fields
SCHEMA_INIT_CHECK(
NULL != m_pKVItem->FindKey( "name" ),
CFmtStr( "Item definition %s: Missing required field \"name\"", m_pKVItem->GetName() ) );
SCHEMA_INIT_CHECK(
NULL != m_pKVItem->FindKey( "item_class" ),
CFmtStr( "Item definition %s: Missing required field \"item_class\"", m_pKVItem->GetName() ) );
// Check value ranges
SCHEMA_INIT_CHECK(
m_pKVItem->GetInt( "min_ilevel" ) >= 0,
CFmtStr( "Item definition %s: \"min_ilevel\" must be greater than or equal to 0", GetDefinitionName() ) );
SCHEMA_INIT_CHECK(
m_pKVItem->GetInt( "max_ilevel" ) >= 0,
CFmtStr( "Item definition %s: \"max_ilevel\" must be greater than or equal to 0", GetDefinitionName() ) );
// Get the item class
m_pszItemClassname = m_pKVItem->GetString( "item_class", NULL );
m_bAllowPurchaseStandalone = m_pKVItem->GetBool( "allow_purchase_standalone", false );
m_pszClassToken = m_pKVItem->GetString( "class_token_id", NULL );
m_pszSlotToken = m_pKVItem->GetString( "slot_token_id", NULL );
m_bPublicItem = m_pKVItem->GetInt( "developer" ) == 0;
m_bIgnoreInCollectionView = m_pKVItem->GetBool( "ignore_in_collection_view", false );
// Display data
m_pszItemBaseName = m_pKVItem->GetString( "item_name", "" ); // non-NULL to ensure we can sort
m_pszItemTypeName = m_pKVItem->GetString( "item_type_name", "" ); // non-NULL to ensure we can sort
m_pszItemDesc = m_pKVItem->GetString( "item_description", NULL );
m_pszArmoryDesc = m_pKVItem->GetString( "armory_desc", NULL );
m_pszInventoryModel = m_pKVItem->GetString( "model_inventory", NULL );
m_pszInventoryImage = m_pKVItem->GetString( "image_inventory", NULL );
m_pPortraitsKV = m_pKVItem->FindKey( "portraits" );
m_unItemTypeID = CRC32_ProcessSingleBuffer( m_pszItemTypeName, V_strlen( m_pszItemTypeName ) );
const char* pOverlay = m_pKVItem->GetString( "image_inventory_overlay", NULL );
if ( pOverlay )
{
m_pszInventoryOverlayImages.AddToTail( pOverlay );
}
pOverlay = m_pKVItem->GetString( "image_inventory_overlay2", NULL );
if ( pOverlay )
{
m_pszInventoryOverlayImages.AddToTail( pOverlay );
}
m_iInventoryImagePosition[0] = m_pKVItem->GetInt( "image_inventory_pos_x", 0 );
m_iInventoryImagePosition[1] = m_pKVItem->GetInt( "image_inventory_pos_y", 0 );
m_iInventoryImageSize[0] = m_pKVItem->GetInt( "image_inventory_size_w", 0 );
m_iInventoryImageSize[1] = m_pKVItem->GetInt( "image_inventory_size_h", 0 );
m_pszHolidayRestriction = m_pKVItem->GetString( "holiday_restriction", NULL );
m_iSubType = m_pKVItem->GetInt( "subtype", 0 );
m_pszBaseDisplayModel = m_pKVItem->GetString( "model_player", NULL );
m_pszWorldDisplayModel = m_pKVItem->GetString( "model_world", NULL ); // Not the ideal method. c_models are better, but this is to solve a retrofit problem with the sticky launcher.
m_pszWorldDroppedModel = m_pKVItem->GetString( "model_dropped", NULL );
//build the world dropped model string by appending "_dropped"
if ( ( m_pszWorldDroppedModel == NULL ) && ( m_pszWorldDisplayModel != NULL ) )
{
V_StripExtension( m_pszWorldDisplayModel, m_szWorldDroppedModel, sizeof( m_szWorldDroppedModel ) );
V_strcat_safe( m_szWorldDroppedModel, "_dropped.mdl" );
m_pszWorldDroppedModel = m_szWorldDroppedModel;
}
// Add new StatTrak modules here.
m_pszIconDefaultImage = m_pKVItem->GetString( "icon_default_image", NULL );
m_pszWorldExtraWearableModel = m_pKVItem->GetString( "extra_wearable", NULL );
m_pszBrassModelOverride = m_pKVItem->GetString( "brass_eject_model", NULL );
m_pszWorldExtraWearableModel = m_pKVItem->GetString( "extra_wearable", NULL );
m_pszBrassModelOverride = m_pKVItem->GetString( "brass_eject_model", NULL );
m_bHideBodyGroupsDeployedOnly = m_pKVItem->GetBool( "hide_bodygroups_deployed_only" );
m_bAttachToHands = m_pKVItem->GetInt( "attach_to_hands", 0 ) != 0;
m_bAttachToHandsVMOnly = m_pKVItem->GetInt( "attach_to_hands_vm_only", 0 ) != 0;
m_bProperName = m_pKVItem->GetInt( "propername", 0 ) != 0;
m_bFlipViewModel = m_pKVItem->GetInt( "flip_viewmodel", 0 ) != 0;
m_bActAsWearable = m_pKVItem->GetInt( "act_as_wearable", 0 ) != 0;
m_iDropType = StringFieldToInt( m_pKVItem->GetString("drop_type"), g_szDropTypeStrings, ARRAYSIZE(g_szDropTypeStrings) );
// Creation data
m_bHidden = m_pKVItem->GetInt( "hidden", 0 ) != 0;
m_bShouldShowInArmory = m_pKVItem->GetInt( "show_in_armory", 0 ) != 0;
m_bBaseItem = m_pKVItem->GetInt( "baseitem", 0 ) != 0;
m_bDefaultSlotItem = m_pKVItem->GetInt( "default_slot_item", 0 ) != 0;
m_pszItemLogClassname = m_pKVItem->GetString( "item_logname", NULL );
m_pszItemIconClassname = m_pKVItem->GetString( "item_iconname", NULL );
#if ECONITEM_DATABASE_AUDIT_TABLES_FEATURE
m_pszDatabaseAuditTable = m_pKVItem->GetString( "database_audit_table", NULL );
#endif
m_bImported = m_pKVItem->FindKey( "import_from" ) != NULL;
m_bOnePerAccountCDKEY = m_pKVItem->GetInt( "one_per_account_cdkey", 0 ) != 0;
m_pszParticleFile = m_pKVItem->GetString( "particle_file", NULL );
m_pszParticleSnapshotFile = m_pKVItem->GetString( "particle_snapshot", NULL );
m_pszLootListName = m_pKVItem->GetString( "loot_list_name", NULL );
// Stickers
KeyValues *pStickerDataKV = m_pKVItem->FindKey( "stickers" );
if ( pStickerDataKV )
{
FOR_EACH_SUBKEY( pStickerDataKV, pStickerModelEntry )
{
StickerData_t *pStickerData = &m_vStickerModels[ m_vStickerModels.AddToTail() ];
const char *pStickerModelPath = pStickerModelEntry->GetString( "viewmodel_geometry" );
V_strcpy_safe( pStickerData->m_szStickerModelPath, pStickerModelPath );
const char *pStickerMaterialPath = pStickerModelEntry->GetString( "viewmodel_material" );
V_strcpy_safe( pStickerData->m_szStickerMaterialPath, pStickerMaterialPath );
const char *pStickerWorldModelProjectionPosition = pStickerModelEntry->GetString( "worldmodel_decal_pos" );
if ( pStickerWorldModelProjectionPosition[0] != 0 )
{
float flX = 0.0f, flY = 0.0f, flZ = 0.0f;
sscanf( pStickerWorldModelProjectionPosition, "%f %f %f", &flX, &flY, &flZ );
pStickerData->m_vWorldModelProjectionStart = Vector( flX, flY, flZ );
}
const char *pStickerWorldModelProjectionEnd = pStickerModelEntry->GetString( "worldmodel_decal_end" );
if ( pStickerWorldModelProjectionEnd[0] != 0 )
{
float flX = 0.0f, flY = 0.0f, flZ = 0.0f;
sscanf( pStickerWorldModelProjectionEnd, "%f %f %f", &flX, &flY, &flZ );
pStickerData->m_vWorldModelProjectionEnd = Vector( flX, flY, flZ );
}
else
{
pStickerData->m_vWorldModelProjectionEnd = Vector(-10,0,0) + pStickerData->m_vWorldModelProjectionStart;
}
V_strcpy_safe( pStickerData->m_szStickerBoneParentName, pStickerModelEntry->GetString( "worldmodel_decal_bone", "ValveBiped.weapon_bone" ) );
}
}
// Tool data
m_pTool = NULL;
KeyValues *pToolDataKV = m_pKVItem->FindKey( "tool" );
if ( pToolDataKV )
{
const char *pszType = pToolDataKV->GetString( "type", NULL );
SCHEMA_INIT_CHECK( pszType, CFmtStr( "Tool '%s' missing required type.", m_pKVItem->GetName() ) );
// Common-to-all-tools settings.
const char *pszUseString = pToolDataKV->GetString( "use_string", NULL );
const char *pszUsageRestriction = pToolDataKV->GetString( "restriction", NULL );
KeyValues *pToolUsageKV = pToolDataKV->FindKey( "usage" );
// Common-to-all-tools usage capability flags.
item_capabilities_t usageCapabilities = (item_capabilities_t)ITEM_CAP_TOOL_DEFAULT;
KeyValues *pToolUsageCapsKV = pToolDataKV->FindKey( "usage_capabilities" );
if ( pToolUsageCapsKV )
{
KeyValues *pEntry = pToolUsageCapsKV->GetFirstSubKey();
while ( pEntry )
{
ParseCapability( usageCapabilities, pEntry );
pEntry = pEntry->GetNextKey();
}
}
m_pTool = pschema.CreateEconToolImpl( pszType, pszUseString, pszUsageRestriction, usageCapabilities, pToolUsageKV );
SCHEMA_INIT_CHECK( m_pTool, CFmtStr( "Unable to create tool implementation for '%s', of type '%s'.", m_pKVItem->GetName(), pszType ) );
}
// capabilities
m_iCapabilities = (item_capabilities_t)ITEM_CAP_DEFAULT;
KeyValues *pCapsKV = m_pKVItem->FindKey( "capabilities" );
if ( pCapsKV )
{
KeyValues *pEntry = pCapsKV->GetFirstSubKey();
while ( pEntry )
{
ParseCapability( m_iCapabilities, pEntry );
pEntry = pEntry->GetNextKey();
}
}
// cache item map names
m_pszArmoryRemap = m_pKVItem->GetString( "armory_remap", NULL );
m_pszStoreRemap = m_pKVItem->GetString( "store_remap", NULL );
// Don't bother parsing visual data on the GC, it's unused.
BInitVisualBlockFromKV( m_pKVItem, pschema, pVecErrors );
// Calculate our equip region mask.
{
m_unEquipRegionMask = 0;
m_unEquipRegionConflictMask = 0;
// Our equip region will come from one of two places -- either we have an "equip_regions" (plural) section,
// in which case we have any number of regions specified; or we have an "equip_region" (singular) section
// which will have one and exactly one region. If we have "equip_regions" (plural), we ignore whatever is
// in "equip_region" (singular).
//
// Yes, this is sort of dumb.
CUtlVector<const char *> vecEquipRegionNames;
KeyValues *pKVMultiEquipRegions = m_pKVItem->FindKey( "equip_regions" ),
*pKVSingleEquipRegion = m_pKVItem->FindKey( "equip_region" );
// Maybe we have multiple entries?
if ( pKVMultiEquipRegions )
{
for ( KeyValues *pKVRegion = pKVMultiEquipRegions->GetFirstSubKey(); pKVRegion; pKVRegion = pKVRegion->GetNextKey() )
{
vecEquipRegionNames.AddToTail( pKVRegion->GetName() );
}
}
// This is our one-and-only-one equip region.
else if ( pKVSingleEquipRegion )
{
const char *pEquipRegionName = pKVSingleEquipRegion->GetString( (const char *)NULL, NULL );
if ( pEquipRegionName )
{
vecEquipRegionNames.AddToTail( pEquipRegionName );
}
}
// For each of our regions, add to our conflict mask both ourself and all the regions
// that we conflict with.
FOR_EACH_VEC( vecEquipRegionNames, i )
{
const char *pszEquipRegionName = vecEquipRegionNames[i];
equip_region_mask_t unThisRegionMask = pschema.GetEquipRegionMaskByName( pszEquipRegionName );
SCHEMA_INIT_CHECK(
unThisRegionMask != 0,
CFmtStr( "Item definition %s: Unable to find equip region mask for region named \"%s\"", GetDefinitionName(), vecEquipRegionNames[i] ) );
m_unEquipRegionMask |= pschema.GetEquipRegionBitMaskByName( pszEquipRegionName );
m_unEquipRegionConflictMask |= unThisRegionMask;
}
}
// Parse the static attributes for this definition
char const *szStaticAttributesSubkeyNames[] = { "attributes"
};
for ( int iStaticAttrGroup = 0; iStaticAttrGroup < Q_ARRAYSIZE( szStaticAttributesSubkeyNames ); ++ iStaticAttrGroup )
{
KeyValues *kvStaticAttrGroup = m_pKVItem->FindKey( szStaticAttributesSubkeyNames[iStaticAttrGroup] );
if ( !kvStaticAttrGroup ) continue;
FOR_EACH_SUBKEY( kvStaticAttrGroup, pKVKey )
{
static_attrib_t staticAttrib;
SCHEMA_INIT_SUBSTEP( staticAttrib.BInitFromKV_SingleLine( GetDefinitionName(), pKVKey, pVecErrors ) );
m_vecStaticAttributes.AddToTail( staticAttrib );
}
}
return SCHEMA_INIT_SUCCESS();
}
bool static_attrib_t::BInitFromKV_MultiLine( const char *pszContext, KeyValues *pKVAttribute, CUtlVector<CUtlString> *pVecErrors )
{
const CEconItemAttributeDefinition *pAttrDef = GetItemSchema()->GetAttributeDefinitionByName( pKVAttribute->GetName() );
SCHEMA_INIT_CHECK(
NULL != pAttrDef,
CFmtStr( "Context '%s': Attribute \"%s\" in \"attributes\" did not match any attribute definitions", pszContext, pKVAttribute->GetName() ) );
if ( pAttrDef )
{
const ISchemaAttributeType *pAttrType = pAttrDef->GetAttributeType();
Assert( pAttrType );
iDefIndex = pAttrDef->GetDefinitionIndex();
const char *pszValue = pKVAttribute->GetString( "value", NULL );
const bool bSuccessfullyLoadedValue = pAttrType->BConvertStringToEconAttributeValue( pAttrDef, pszValue, &m_value );
SCHEMA_INIT_CHECK(
bSuccessfullyLoadedValue,
CFmtStr( "Context '%s': Attribute \"%s\" could not parse value \"%s\"!", pszContext, pKVAttribute->GetName(), pszValue ? pszValue : "(null)" ) );
m_bForceGCToGenerate = pKVAttribute->GetBool( "force_gc_to_generate" );
}
return SCHEMA_INIT_SUCCESS();
}
bool static_attrib_t::BInitFromKV_SingleLine( const char *pszContext, KeyValues *pKVAttribute, CUtlVector<CUtlString> *pVecErrors )
{
if ( pKVAttribute->GetFirstSubKey() )
{
return BInitFromKV_MultiLine( pszContext, pKVAttribute, pVecErrors );
}
const CEconItemAttributeDefinition *pAttrDef = GetItemSchema()->GetAttributeDefinitionByName( pKVAttribute->GetName() );
SCHEMA_INIT_CHECK(
NULL != pAttrDef,
CFmtStr( "Context '%s': Attribute \"%s\" in \"attributes\" did not match any attribute definitions", pszContext, pKVAttribute->GetName() ) );
if ( pAttrDef )
{
const ISchemaAttributeType *pAttrType = pAttrDef->GetAttributeType();
Assert( pAttrType );
iDefIndex = pAttrDef->GetDefinitionIndex();
const char *pszValue = pKVAttribute->GetString();
const bool bSuccessfullyLoadedValue = pAttrType->BConvertStringToEconAttributeValue( pAttrDef, pszValue, &m_value );
SCHEMA_INIT_CHECK(
bSuccessfullyLoadedValue,
CFmtStr( "Context '%s': Attribute \"%s\" could not parse value \"%s\"!", pszContext, pKVAttribute->GetName(), pszValue ? pszValue : "(null)" ) );
m_bForceGCToGenerate = false;
}
return SCHEMA_INIT_SUCCESS();
}
bool CEconItemDefinition::BInitItemMappings( CEconItemSchema &pschema, CUtlVector<CUtlString> *pVecErrors )
{
// Armory remapping
if ( m_pszArmoryRemap && m_pszArmoryRemap[0] )
{
const CEconItemDefinition *pDef = pschema.GetItemDefinitionByName( m_pszArmoryRemap );
if ( pDef )
{
m_iArmoryRemap = pDef->GetDefinitionIndex();
}
SCHEMA_INIT_CHECK(
pDef != NULL,
CFmtStr( "Item %s: Armory remap definition \"%s\" was not found", m_pszItemBaseName, m_pszArmoryRemap ) );
}
// Store remapping
if ( m_pszStoreRemap && m_pszStoreRemap[0] )
{
const CEconItemDefinition *pDef = pschema.GetItemDefinitionByName( m_pszStoreRemap );
if ( pDef )
{
m_iStoreRemap = pDef->GetDefinitionIndex();
}
SCHEMA_INIT_CHECK(
pDef != NULL,
CFmtStr( "Item %s: Store remap definition \"%s\" was not found", m_pszItemBaseName, m_pszStoreRemap ) );
}
return SCHEMA_INIT_SUCCESS();
}
//-----------------------------------------------------------------------------
KeyValues* CEconItemDefinition::GetPortraitKVForModel( const char* pszModelName ) const
{
if ( !m_pPortraitsKV )
return NULL;
return m_pPortraitsKV->FindKey( pszModelName );
}
void CEconItemDefinition::AddItemSet( int nIndex )
{
if ( m_iItemSets.Find( nIndex ) != m_iItemSets.InvalidIndex() )
return;
m_iItemSets.AddToTail( nIndex );
}
const CUtlVector< int >& CEconItemDefinition::GetItemSets( void ) const
{
return m_iItemSets;
}
#if defined(CLIENT_DLL) || defined(GAME_DLL)
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
attachedparticlesystem_t *CEconItemDefinition::GetAttachedParticleData( int iIdx ) const
{
if ( !GetAssetInfo() )
return NULL;
Assert( iIdx < GetAssetInfo()->m_AttachedParticles.Count() );
if ( iIdx >= GetAssetInfo()->m_AttachedParticles.Count() )
return NULL;
int iParticleIndex = GetAssetInfo()->m_AttachedParticles[iIdx].m_iParticleIndex;
return ItemSystem()->GetItemSchema()->GetAttributeControlledParticleSystemByIndex( iParticleIndex );
}
bool CEconItemDefinition::IsAttachedParticleDataValidForStyle( int iIdx, int nStyle ) const
{
Assert( iIdx < GetAssetInfo()->m_AttachedParticles.Count() );
if ( iIdx >= GetAssetInfo()->m_AttachedParticles.Count() )
return false;
return ( GetAssetInfo()->m_AttachedParticles[iIdx].m_nStyle == nStyle );
}
#endif // #if defined(CLIENT_DLL) || defined(GAME_DLL)
//-----------------------------------------------------------------------------
// Purpose: Generate and return a random level according to whatever leveling
// curve this definition uses.
//-----------------------------------------------------------------------------
uint32 CEconItemDefinition::RollItemLevel( void ) const
{
return RandomInt( GetMinLevel(), GetMaxLevel() );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CEconItemDefinition::IterateAttributes( IEconItemAttributeIterator *pIterator ) const
{
FOR_EACH_VEC( GetStaticAttributes(), i )
{
const static_attrib_t& staticAttrib = GetStaticAttributes()[i];
// we skip over static attributes that the GC will turn into dynamic attributes because otherwise we'll have
// the appearance of iterating over them twice; for clients these attributes won't even make it into the
// list
if ( staticAttrib.m_bForceGCToGenerate )
continue;
const CEconItemAttributeDefinition *pAttrDef = GetItemSchema()->GetAttributeDefinition( staticAttrib.iDefIndex );
if ( !pAttrDef )
continue;
const ISchemaAttributeType *pAttrType = pAttrDef->GetAttributeType();
Assert( pAttrType );
if ( !pAttrType->OnIterateAttributeValue( pIterator, pAttrDef, staticAttrib.m_value ) )
return;
}
}
const char *CEconItemDefinition::GetFirstSaleDate() const
{
return GetRawDefinition()->GetString( "first_sale_date", "1970/01/01" );
}
#if defined(CLIENT_DLL) || defined(GAME_DLL)
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
Activity CEconItemDefinition::GetActivityOverride( Activity baseAct ) const
{
int iAnims = GetNumAnimations();
for ( int i = 0; i < iAnims; i++ )
{
animation_on_wearable_t *pData = GetAnimationData( i );
if ( !pData )
continue;
if ( pData->iActivity == kActivityLookup_Unknown )
{
pData->iActivity = ActivityList_IndexForName( pData->pszActivity );
}
if ( pData->iActivity == baseAct )
{
if ( pData->iReplacement == kActivityLookup_Unknown )
{
pData->iReplacement = ActivityList_IndexForName( pData->pszReplacement );
}
if ( pData->iReplacement > 0 )
{
return (Activity) pData->iReplacement;
}
}
}
return baseAct;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
const char *CEconItemDefinition::GetReplacementForActivityOverride( Activity baseAct ) const
{
int iAnims = GetNumAnimations();
for ( int i = 0; i < iAnims; i++ )
{
animation_on_wearable_t *pData = GetAnimationData( i );
if ( pData->iActivity == kActivityLookup_Unknown )
{
pData->iActivity = ActivityList_IndexForName( pData->pszActivity );
}
if ( pData && pData->iActivity == baseAct )
{
if ( CEconItemSchema::GetRandomStream().RandomFloat() < pData->flFrequency )
return pData->pszReplacement;
}
}
return NULL;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
const char *CEconItemDefinition::GetReplacementSound( const char* pszSoundName ) const
{
int iSounds = GetNumSounds();
for ( int i=0; i<iSounds; i++ )
{
sound_on_wearable_t *pData = GetSoundData( i );
if ( pData && FStrEq( pData->pszSound, pszSoundName ) )
return pData->pszReplacement;
}
return NULL;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
const char *CEconItemDefinition::GetReplacementParticleEffect( const char* pszParticleName ) const
{
int iParticles = GetNumParticles();
for ( int i=0; i<iParticles; i++ )
{
particle_on_wearable_t *pData = GetParticleData( i );
if ( pData && FStrEq( pData->pszParticle, pszParticleName ) )
return pData->pszReplacement;
}
return NULL;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
const char *CEconItemDefinition::GetReplacementParticleSnapshot( const char* pszParticleSnapshotName ) const
{
int iSnapshots = GetNumParticleSnapshots();
for ( int i=0; i<iSnapshots; i++ )
{
particlesnapshot_on_wearable_t *pData = GetParticleSnapshotData( i );
if ( pData && FStrEq( pData->pszParticleSnapshot, pszParticleSnapshotName ) )
return pData->pszReplacement;
}
return NULL;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CEconItemDefinition::GetReplacementControlPoint( int nIndex, const char* pszParticleName, int &nOutputCP, Vector &vecCPValue ) const
{
int iParticles = GetNumParticleControlPoints();
if ( nIndex < iParticles )
{
particle_control_point_on_wearable_t *pData = GetParticleControlPointData( nIndex );
if ( pData && FStrEq( pData->pszParticle, pszParticleName ) )
{
nOutputCP = pData->nParticleControlPoint;
vecCPValue = pData->vecCPValue;
return true;
}
}
return false;
}
//-----------------------------------------------------------------------------
// Purpose: Returns true if the content for this item view should be streamed. If false,
// it should be preloaded.
//-----------------------------------------------------------------------------
// DO NOT MERGE THIS CONSOLE VARIABLE TO REL WE SHOULD NOT SHIP THIS OH GOD
#define ITEM_ENABLE_DYNAMIC_LOADING true
//ConVar item_enable_dynamic_loading( "item_enable_dynamic_loading", "1", FCVAR_REPLICATED, "Enable/disable dynamic streaming of econ content." );
bool CEconItemDefinition::IsContentStreamable() const
{
if ( !BLoadOnDemand() )
return false;
return ITEM_ENABLE_DYNAMIC_LOADING;
}
#endif // defined(CLIENT_DLL) || defined(GAME_DLL)
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
const char* CEconItemDefinition::GetAlternateIcon( int iAlternateIcon ) const
{
int iIdx = m_pMapAlternateIcons->Find( iAlternateIcon );
if ( !m_pMapAlternateIcons->IsValidIndex( iIdx ) )
return NULL;
else
return m_pMapAlternateIcons->Element( iIdx );
}
//-----------------------------------------------------------------------------
// Purpose:
// Sticker model paths
//-----------------------------------------------------------------------------
const int CEconItemDefinition::GetNumSupportedStickerSlots() const
{
return m_vStickerModels.Count();
}
const char *CEconItemDefinition::GetStickerSlotModelBySlotIndex( uint32 index ) const
{
return m_vStickerModels[index].m_szStickerModelPath;
}
const Vector &CEconItemDefinition::GetStickerSlotWorldProjectionStartBySlotIndex( uint32 index ) const
{
return m_vStickerModels[index].m_vWorldModelProjectionStart;
}
const Vector &CEconItemDefinition::GetStickerSlotWorldProjectionEndBySlotIndex( uint32 index ) const
{
return m_vStickerModels[index].m_vWorldModelProjectionEnd;
}
const char *CEconItemDefinition::GetStickerWorldModelBoneParentNameBySlotIndex( uint32 index ) const
{
return m_vStickerModels[index].m_szStickerBoneParentName;
}
const char *CEconItemDefinition::GetStickerSlotMaterialBySlotIndex( uint32 index ) const
{
return m_vStickerModels[index].m_szStickerMaterialPath;
}
RETURN_ATTRIBUTE_STRING_F( CEconItemDefinition::GetIconDisplayModel, "icon display model", m_pszWorldDisplayModel );
RETURN_ATTRIBUTE_STRING_F( CEconItemDefinition::GetBuyMenuDisplayModel, "buymenu display model", m_pszWorldDisplayModel );
RETURN_ATTRIBUTE_STRING_F( CEconItemDefinition::GetPedestalDisplayModel, "pedestal display model", m_pszBaseDisplayModel );
RETURN_ATTRIBUTE_STRING_F( CEconItemDefinition::GetMagazineModel, "magazine model", NULL );
RETURN_ATTRIBUTE_STRING_F( CEconItemDefinition::GetUidModel, "uid model", "models/weapons/uid.mdl" );
RETURN_ATTRIBUTE_STRING_F( CEconItemDefinition::GetScopeLensMaskModel, "aimsight lens mask", NULL );
const char *CEconItemDefinition::GetStatTrakModelByType( uint32 nType ) const
{
const kill_eater_score_type_t *pScoreType = GetItemSchema()->FindKillEaterScoreType( nType );
if ( !pScoreType )
{
pScoreType = GetItemSchema()->FindKillEaterScoreType( 0 );
}
if ( !pScoreType )
return NULL;
CSchemaAttributeDefHandle pAttrib_StatTrakModel( pScoreType->m_pszModelAttributeString );
const char *pchStatTrakModel = NULL;
FindAttribute_UnsafeBitwiseCast< CAttribute_String >( this, pAttrib_StatTrakModel, &pchStatTrakModel );
return pchStatTrakModel;
}
//-----------------------------------------------------------------------------
// Purpose: Constructor
//-----------------------------------------------------------------------------
CTimedItemRewardDefinition::CTimedItemRewardDefinition( void )
: m_unMinFreq( 0 ),
m_unMaxFreq( UINT_MAX ),
m_rtForcedBaselineAdjustment( 0 ),
m_rtForcedLastDropTimeAdjustment( 0 ),
m_unHoursInRewardPeriod( 0 ),
m_unHoursBetweenDropsRealtime( 0 ),
m_unPointsPerHourOverplayed( 0 ),
m_flChance( 0.0f )
{
}
//-----------------------------------------------------------------------------
// Purpose: Copy constructor
//-----------------------------------------------------------------------------
CTimedItemRewardDefinition::CTimedItemRewardDefinition( const CTimedItemRewardDefinition &that )
{
(*this) = that;
}
//-----------------------------------------------------------------------------
// Purpose: Operator=
//-----------------------------------------------------------------------------
CTimedItemRewardDefinition &CTimedItemRewardDefinition::operator=( const CTimedItemRewardDefinition &rhs )
{
m_unMinFreq = rhs.m_unMinFreq;
m_unMaxFreq = rhs.m_unMaxFreq;
m_rtForcedBaselineAdjustment = rhs.m_rtForcedBaselineAdjustment;
m_rtForcedLastDropTimeAdjustment = rhs.m_rtForcedLastDropTimeAdjustment;
m_unHoursInRewardPeriod = rhs.m_unHoursInRewardPeriod;
m_unHoursBetweenDropsRealtime = rhs.m_unHoursBetweenDropsRealtime;
m_unPointsPerHourOverplayed = rhs.m_unPointsPerHourOverplayed;
m_arrTotalPointsBasedOnHoursPlayed.RemoveAll();
m_arrTotalPointsBasedOnHoursPlayed.EnsureCapacity( rhs.m_arrTotalPointsBasedOnHoursPlayed.Count() );
m_arrTotalPointsBasedOnHoursPlayed.AddMultipleToTail( rhs.m_arrTotalPointsBasedOnHoursPlayed.Count(), rhs.m_arrTotalPointsBasedOnHoursPlayed.Base() );
m_criteria = rhs.m_criteria;
m_arrLootLists.CopyArray( rhs.m_arrLootLists.Base(), rhs.m_arrLootLists.Count() );
m_flChance = rhs.m_flChance;
return *this;
}
CTimedItemRewardDefinition::~CTimedItemRewardDefinition()
{
m_arrDynamicLootLists.PurgeAndDeleteElements();
}
//-----------------------------------------------------------------------------
// Purpose: Initialize the attribute definition
// Input: pKVTimedReward - The KeyValues representation of the timed reward
// schema - The overall item schema
// pVecErrors - An optional vector that will contain error messages if
// the init fails.
// Output: True if initialization succeeded, false otherwise
//-----------------------------------------------------------------------------
bool CTimedItemRewardDefinition::BInitFromKV( KeyValues *pKVTimedReward, CEconItemSchema &pschema, CUtlVector<CUtlString> *pVecErrors /* = NULL */ )
{
// Parse the basic values
m_flChance = pKVTimedReward->GetFloat( "pctChance" );
#ifdef DOTA_DLL
m_unMinFreq = pKVTimedReward->GetInt( "value_min", 0 );
m_unMaxFreq = pKVTimedReward->GetInt( "value_max", UINT_MAX );
// Check required fields
SCHEMA_INIT_CHECK(
NULL != pKVTimedReward->FindKey( "value_min" ),
CFmtStr( "Time reward %s: Missing required field \"value_min\"", pKVTimedReward->GetName() ) );
SCHEMA_INIT_CHECK(
NULL != pKVTimedReward->FindKey( "value_max" ),
CFmtStr( "Time reward %s: Missing required field \"value_max\"", pKVTimedReward->GetName() ) );
#endif
//
// Parse the basic values
//
SCHEMA_INIT_CHECK(
NULL != pKVTimedReward->FindKey( "force_baseline_timestamp" ),
CFmtStr( "Time reward %s: Missing required field \"force_baseline_timestamp\"", pKVTimedReward->GetName() ) );
m_rtForcedBaselineAdjustment = pKVTimedReward->GetInt( "force_baseline_timestamp" );
m_rtForcedLastDropTimeAdjustment = 0;
if ( pKVTimedReward->FindKey( "force_lastdrop_timestamp" ) )
m_rtForcedLastDropTimeAdjustment = pKVTimedReward->GetInt( "force_lastdrop_timestamp" );
m_unHoursInRewardPeriod = pKVTimedReward->GetInt( "period_hours", 0 );
SCHEMA_INIT_CHECK(
NULL != pKVTimedReward->FindKey( "period_hours" ),
CFmtStr( "Time reward %s: Missing required field \"period_hours\"", pKVTimedReward->GetName() ) );
SCHEMA_INIT_CHECK(
m_unHoursInRewardPeriod > 0,
CFmtStr( "Time reward %s: Required field \"period_hours\" has invalid value", pKVTimedReward->GetName() ) );
m_unHoursBetweenDropsRealtime = pKVTimedReward->GetInt( "drop_interval_hours", 0 );
SCHEMA_INIT_CHECK(
NULL != pKVTimedReward->FindKey( "drop_interval_hours" ),
CFmtStr( "Time reward %s: Missing required field \"drop_interval_hours\"", pKVTimedReward->GetName() ) );
SCHEMA_INIT_CHECK(
m_unHoursBetweenDropsRealtime >= 0,
CFmtStr( "Time reward %s: Required field \"drop_interval_hours\" has invalid value", pKVTimedReward->GetName() ) );
SCHEMA_INIT_CHECK(
NULL != pKVTimedReward->FindKey( "points_progression_hourly" ),
CFmtStr( "Time reward %s: Missing required field \"points_progression_hourly\"", pKVTimedReward->GetName() ) );
uint32 numPointsAccumulatedValidation = 0;
for ( KeyValues *kvPoints = pKVTimedReward->FindKey( "points_progression_hourly" )->GetFirstSubKey(); kvPoints; kvPoints = kvPoints->GetNextKey() )
{
uint32 numPoints = kvPoints->GetInt();
SCHEMA_INIT_CHECK(
numPoints > numPointsAccumulatedValidation,
CFmtStr( "Time reward %s: Required field \"points_progression_hourly\" defines invalid points after %u hours", pKVTimedReward->GetName(), m_arrTotalPointsBasedOnHoursPlayed.Count() ) );
m_arrTotalPointsBasedOnHoursPlayed.AddToTail( numPoints );
numPointsAccumulatedValidation = numPoints;
}
SCHEMA_INIT_CHECK(
NULL != pKVTimedReward->FindKey( "points_per_additional_hour" ),
CFmtStr( "Time reward %s: Missing required field \"points_per_additional_hour\"", pKVTimedReward->GetName() ) );
m_unPointsPerHourOverplayed = pKVTimedReward->GetInt( "points_per_additional_hour", 0 );
SCHEMA_INIT_CHECK(
NULL != pKVTimedReward->FindKey( "period_points_rollover" ),
CFmtStr( "Time reward %s: Missing required field \"period_points_rollover\"", pKVTimedReward->GetName() ) );
m_unPointsPerPeriodRollover = pKVTimedReward->GetInt( "period_points_rollover", 0 );
SCHEMA_INIT_CHECK(
m_unPointsPerPeriodRollover >= numPointsAccumulatedValidation,
CFmtStr( "Time reward %s: Required field \"period_points_rollover\" defines invalid points after %u hours", pKVTimedReward->GetName(), m_arrTotalPointsBasedOnHoursPlayed.Count() ) );
SCHEMA_INIT_CHECK(
NULL != pKVTimedReward->FindKey( "pctChance" ),
CFmtStr( "Time reward %s: Missing required field \"pctChance\"", pKVTimedReward->GetName() ) );
SCHEMA_INIT_CHECK(
( m_flChance >= 0.0f ) && ( m_flChance <= 1.0f ),
CFmtStr( "Time reward %s: Required field \"pctChance\" has invalid value", pKVTimedReward->GetName() ) );
// Parse the criteria or loot_list
if ( pKVTimedReward->FindKey( "criteria" ) )
{
bool bCriteriaOK = m_criteria.BInitFromKV( pKVTimedReward->FindKey( "criteria", true ), pschema );
SCHEMA_INIT_CHECK( bCriteriaOK, CFmtStr( "Time Reward %s: Invalid criteria", pKVTimedReward->GetName() ) );
// Check to make sure this criteria doesn't filter to an empty set
if ( bCriteriaOK )
{
bool bMatch = false;
FOR_EACH_MAP_FAST( pschema.GetItemDefinitionMap(), i )
{
if ( m_criteria.BEvaluate( pschema.GetItemDefinitionMap()[i], pschema ) )
{
bMatch = true;
break;
}
}
SCHEMA_INIT_CHECK(
bMatch,
CFmtStr( "Time Reward %s: No items match the critera", pKVTimedReward->GetName() ) );
}
}
const char *pszLootList = pKVTimedReward->GetString("loot_list", NULL);
if ( pszLootList && pszLootList[0] )
{
CUtlVector< char * > arrLootListsTokens;
V_SplitString( pszLootList, ",", arrLootListsTokens );
SCHEMA_INIT_CHECK(
arrLootListsTokens.Count() != 0,
CFmtStr( "Time Reward %s: loot_list (%s) has zero elements", pKVTimedReward->GetName(), pszLootList ) );
FOR_EACH_VEC( arrLootListsTokens, iLootListToken )
{
const CEconLootListDefinition *pLootList = pschema.GetLootListByName( arrLootListsTokens[iLootListToken] );
// Make sure the item index is correct because we use this index as a reference
SCHEMA_INIT_CHECK(
NULL != pLootList,
CFmtStr( "Time Reward %s: loot_list (%s) element '%s' does not exist", pKVTimedReward->GetName(), pszLootList, arrLootListsTokens[iLootListToken] ) );
if ( pLootList )
m_arrLootLists.AddToTail( pLootList );
}
arrLootListsTokens.PurgeAndDeleteElements();
}
return SCHEMA_INIT_SUCCESS();
}
float CTimedItemRewardDefinition::GetChance( void ) const
{
return m_flChance;
}
const CSchemaAttributeDefHandle& GetCampaignAttributeDefHandle( int nCampaignID, ECampaignAttributeType type )
{
static CSchemaAttributeDefHandle* pAttrCampaignCompletionBitfield[ g_nNumCampaigns + 1 ];
static CSchemaAttributeDefHandle* pAttrCampaignLastCompletedQuest[ g_nNumCampaigns + 1 ];
static bool s_bCampaignAttrInitialized = false;
static bool s_bCampaignAttrValid = false;
if ( !s_bCampaignAttrInitialized )
{
s_bCampaignAttrInitialized = true;
s_bCampaignAttrValid = true;
for ( int j = 1; j <= g_nNumCampaigns; ++j )
{
pAttrCampaignCompletionBitfield[ j ]= new CSchemaAttributeDefHandle( ( new CFmtStr( "campaign %d completion bitfield", j ) )->Access() );
if ( !( *pAttrCampaignCompletionBitfield[ j ] ) )
s_bCampaignAttrValid = false;
pAttrCampaignLastCompletedQuest[ j ] = new CSchemaAttributeDefHandle( ( new CFmtStr( "campaign %d last completed quest", j ) )->Access() );
if ( !( *pAttrCampaignLastCompletedQuest[ j ] ) )
s_bCampaignAttrValid = false;
}
}
if ( ( nCampaignID <= 0 ) || ( nCampaignID > g_nNumCampaigns ) )
{
Assert( 0 );
Warning( "Attempting to lookup campaign %d, which does not exist. Verify that the code recognizes that campaigns are 1-based.", nCampaignID );
}
else if ( s_bCampaignAttrValid )
{
switch( type )
{
case k_ECampaignAttribute_CompletionBitfield:
return *pAttrCampaignCompletionBitfield[ nCampaignID ];
case k_ECampaignAttribute_LastCompletedQuest:
return *pAttrCampaignLastCompletedQuest[ nCampaignID ];
default:
Assert( 0 );
Warning( "Attempting to lookup campaign %d type %d, which does not exist.", nCampaignID, type );
break;
}
}
static CSchemaAttributeDefHandle s_DummyAttr( "undefined" );
return s_DummyAttr;
}
const CSchemaAttributeDefHandle& GetStickerAttributeDefHandle( int attrNum, EStickerAttributeType type )
{
// Attributes of the schema validation
static bool s_bStickerAttrsSetup = false;
static bool s_bStickerAttrsValid = false;
static CSchemaAttributeDefHandle* pAttrStickerSlotID[ g_nNumStickerAttrs ];
static CSchemaAttributeDefHandle* pAttrStickerSlotWear[ g_nNumStickerAttrs ];
static CSchemaAttributeDefHandle* pAttrStickerSlotScale[ g_nNumStickerAttrs ];
static CSchemaAttributeDefHandle* pAttrStickerSlotRotation[ g_nNumStickerAttrs ];
if ( !s_bStickerAttrsSetup )
{
s_bStickerAttrsSetup = true;
s_bStickerAttrsValid = true;
for ( int j = 0; j < g_nNumStickerAttrs; ++j )
{
pAttrStickerSlotID[ j ] = new CSchemaAttributeDefHandle( ( new CFmtStr( "sticker slot %d id", j ) )->Access() );
if ( !( *pAttrStickerSlotID[ j ] ) ) s_bStickerAttrsValid = false;
pAttrStickerSlotWear[ j ] = new CSchemaAttributeDefHandle( ( new CFmtStr( "sticker slot %d wear", j ) )->Access() );
if ( !( *pAttrStickerSlotWear[ j ] ) ) s_bStickerAttrsValid = false;
pAttrStickerSlotScale[ j ] = new CSchemaAttributeDefHandle( ( new CFmtStr( "sticker slot %d scale", j ) )->Access() );
if ( !( *pAttrStickerSlotScale[ j ] ) ) s_bStickerAttrsValid = false;
pAttrStickerSlotRotation[ j ] = new CSchemaAttributeDefHandle( ( new CFmtStr( "sticker slot %d rotation", j ) )->Access() );
if ( !( *pAttrStickerSlotRotation[ j ] ) ) s_bStickerAttrsValid = false;
}
}
if ( s_bStickerAttrsValid )
{
switch ( type )
{
case k_EStickerAttribute_ID:
return *pAttrStickerSlotID[ attrNum ];
break;
case k_EStickerAttribute_Wear:
return *pAttrStickerSlotWear[ attrNum ];
break;
case k_EStickerAttribute_Scale:
return *pAttrStickerSlotScale[ attrNum ];
break;
case k_EStickerAttribute_Rotation:
return *pAttrStickerSlotRotation[ attrNum ];
break;
default:
Assert( 0 );
break;
};
}
static CSchemaAttributeDefHandle s_DummyAttr( "undefined" );
return s_DummyAttr;
}
bool CStickerKit::InitFromKeyValues( KeyValues *pKVEntry, const CStickerKit *pDefault, CUtlVector<CUtlString> *pVecErrors /*= NULL*/ )
{
sName.Set( pKVEntry->GetString( "name", pDefault->sName.String() ) );
sDescriptionString.Set( pKVEntry->GetString( "description_string", pDefault->sDescriptionString.String() ) );
sItemName.Set( pKVEntry->GetString( "item_name", pDefault->sItemName.String() ) );
uint8 ucRarity;
GetItemSchema()->BGetItemRarityFromName( pKVEntry->GetString( "item_rarity", "common" ), &ucRarity );
nRarity = ucRarity;
sMaterialPath.Set( pKVEntry->GetString( "sticker_material", pDefault->sMaterialPath.String() ) );
if ( *sMaterialPath.String() )
{
sMaterialPathNoDrips.Set( pKVEntry->GetString( "sticker_material_nodrips", CFmtStr( "%s_nodrips", sMaterialPath.String() ) ) );
}
m_strInventoryImage.Set( pKVEntry->GetString( "image_inventory", CFmtStr( "econ/stickers/%s", sMaterialPath.String() ) ) );
SetIconURLSmall( GetInventoryImage() );
SetIconURLLarge( CFmtStr( "%s_large", GetInventoryImage() ) );
//
// gc_generation_settings
//
flRotateStart = pKVEntry->GetFloat( "gc_generation_settings/rotate_start", pDefault->flRotateStart );
flRotateEnd = pKVEntry->GetFloat( "gc_generation_settings/rotate_end", pDefault->flRotateEnd );
SCHEMA_INIT_CHECK(
( flRotateStart >= -360.0f ) && ( flRotateStart <= flRotateEnd ) && ( flRotateEnd <= 360.0f ),
CFmtStr( "Sticker kit '%s' rotate range is not valid %f:%f", pKVEntry->GetName(), flRotateStart, flRotateEnd ) );
flScaleMin = pKVEntry->GetFloat( "gc_generation_settings/scale_min", pDefault->flScaleMin );
flScaleMax = pKVEntry->GetFloat( "gc_generation_settings/scale_max", pDefault->flScaleMax );
SCHEMA_INIT_CHECK( // Scale is in UV space so max is a smaller float, min is a larger float
( flScaleMax > 0.0f ) && ( flScaleMax <= flScaleMin ),
CFmtStr( "Sticker kit '%s' scale range is not valid %f:%f", pKVEntry->GetName(), flScaleMax, flScaleMin ) );
flWearMin = pKVEntry->GetFloat( "gc_generation_settings/wear_min", pDefault->flWearMin );
flWearMax = pKVEntry->GetFloat( "gc_generation_settings/wear_max", pDefault->flWearMax );
SCHEMA_INIT_CHECK(
( flWearMin >= 0.0f ) && ( flWearMin <= flWearMax ) && ( flWearMax <= 1.0f ),
CFmtStr( "Sticker kit '%s' wear range is not valid %f:%f", pKVEntry->GetName(), flWearMin, flWearMax ) );
m_nEventID = pKVEntry->GetInt( "tournament_event_id", pDefault->m_nEventID );
m_nEventTeamID = pKVEntry->GetInt( "tournament_team_id", pDefault->m_nEventTeamID );
m_nPlayerID = pKVEntry->GetInt( "tournament_player_id", pDefault->m_nPlayerID );
m_pKVItem = pKVEntry ? pKVEntry->MakeCopy() : NULL;
return true;
}
bool CStickerList::InitFromKeyValues( KeyValues *pKVEntry, CUtlVector<CUtlString> *pVecErrors /*= NULL*/ )
{
flWearMin = pKVEntry->GetFloat( "gc_generation_settings/wear_min", 0.0f );
flWearMax = pKVEntry->GetFloat( "gc_generation_settings/wear_max", 1.0f );
SCHEMA_INIT_CHECK(
( flWearMin >= 0.0f ) && ( flWearMin <= flWearMax ) && ( flWearMax <= 1.0f ),
CFmtStr( "Sticker list '%s' wear range is not valid %f:%f", pKVEntry->GetName(), flWearMin, flWearMax ) );
flTotalWeight = 0.0f;
float flLargestMinWear = 0.0f;
float flSmallestMaxWear = 1.0f;
for ( KeyValues *kvChild = pKVEntry->GetFirstValue(); kvChild; kvChild = kvChild->GetNextValue() )
{
char const *szName = kvChild->GetName();
const CStickerKit *pStickerKit = GetItemSchema()->GetStickerKitDefinitionByName( szName );
const CStickerList *pStickerList = GetItemSchema()->GetStickerListDefinitionByName( szName );
SCHEMA_INIT_CHECK(
( pStickerKit || pStickerList ) && !( pStickerKit && pStickerList ),
CFmtStr( "Sticker list '%s' refers to an unknown stickerkit or stickerlist '%s'", pKVEntry->GetName(), szName ) );
float flWeight = kvChild->GetFloat();
SCHEMA_INIT_CHECK(
( flWeight > 0.0f ),
CFmtStr( "Sticker list '%s' has invalid weight %f", pKVEntry->GetName(), flWeight ) );
if ( pStickerKit )
{
flLargestMinWear = MAX( flLargestMinWear, pStickerKit->flWearMin );
flSmallestMaxWear = MIN( flSmallestMaxWear, pStickerKit->flWearMax );
}
if ( pStickerList )
{
flLargestMinWear = MAX( flLargestMinWear, pStickerList->flWearMin );
flSmallestMaxWear = MIN( flSmallestMaxWear, pStickerList->flWearMax );
}
// add the entry
sticker_list_entry_t entry;
entry.pKit = pStickerKit;
entry.pList = pStickerList;
entry.flWeight = flWeight;
arrElements.AddToTail( entry );
flTotalWeight += entry.flWeight;
}
SCHEMA_INIT_CHECK(
( arrElements.Count() > 0 ),
CFmtStr( "Sticker list '%s' has no elements", pKVEntry->GetName() ) );
SCHEMA_INIT_CHECK(
( flTotalWeight > 0.0f ),
CFmtStr( "Sticker list '%s' has no elements weight", pKVEntry->GetName() ) );
SCHEMA_INIT_CHECK(
( flSmallestMaxWear >= flWearMin ),
CFmtStr( "Sticker list '%s' wear_min %f exceeds elements max range wear %f", pKVEntry->GetName(), flWearMin, flSmallestMaxWear ) );
SCHEMA_INIT_CHECK(
( flLargestMinWear <= flWearMax ),
CFmtStr( "Sticker list '%s' wear_max %f below elements min range wear %f", pKVEntry->GetName(), flWearMax, flLargestMinWear ) );
return true;
}
bool CStickerKit::GenerateStickerApplicationInfo( CAppliedStickerInfo_t *pInfo ) const
{
if ( !pInfo )
return false;
pInfo->flWearMin = MAX( pInfo->flWearMin, flWearMin );
pInfo->flWearMax = MIN( pInfo->flWearMax, flWearMax );
if ( pInfo->flWearMin > pInfo->flWearMax )
return false;
pInfo->nID = nID;
pInfo->flScale = CEconItemSchema::GetRandomStream().RandomFloat( flScaleMax, flScaleMin );
pInfo->flRotate = CEconItemSchema::GetRandomStream().RandomFloat( flRotateStart, flRotateEnd );
return true;
}
bool CStickerList::GenerateStickerApplicationInfo( CAppliedStickerInfo_t *pInfo ) const
{
if ( !pInfo )
return false;
// Generate a random roll into our list
float flRand = CEconItemSchema::GetRandomStream().RandomFloat(0.f, 1.f) * flTotalWeight;
float flAccum = 0.f;
for ( int i=0; i<arrElements.Count(); ++i )
{
flAccum += arrElements[i].flWeight;
if ( flRand <= flAccum )
{
const sticker_list_entry_t &entry = arrElements[i];
pInfo->flWearMin = MAX( pInfo->flWearMin, flWearMin );
pInfo->flWearMax = MIN( pInfo->flWearMax, flWearMax );
if ( pInfo->flWearMin > pInfo->flWearMax )
return false;
if ( entry.pList )
return entry.pList->GenerateStickerApplicationInfo( pInfo );
if ( entry.pKit )
return entry.pKit->GenerateStickerApplicationInfo( pInfo );
return false;
}
}
return false;
}
bool CPaintKit::InitFromKeyValues( KeyValues *pKVEntry, const CPaintKit *pDefault, bool bHandleAbsolutePaths )
{
sName.Set( pKVEntry->GetString( "name", pDefault->sName.String() ) );
sDescriptionString.Set( pKVEntry->GetString( "description_string", pDefault->sDescriptionString.String() ) );
sDescriptionTag.Set( pKVEntry->GetString( "description_tag", pDefault->sDescriptionTag.String() ) );
nRarity = 1; // rarities set in the paint_kits_rarity block
// Character paint kit fields
sVmtPath.Set( pKVEntry->GetString( "vmt_path", pDefault->sVmtPath.String() ) );
if ( kvVmtOverrides != nullptr )
{
kvVmtOverrides->deleteThis();
kvVmtOverrides = nullptr;
}
KeyValues* pVmtOverrides = pKVEntry->FindKey( "vmt_overrides" );
if ( pVmtOverrides != nullptr )
{
kvVmtOverrides = new KeyValues( "CustomCharacter" );
kvVmtOverrides->MergeFrom( pVmtOverrides, KeyValues::MERGE_KV_UPDATE );
}
// Regular paint kit fields
if (bHandleAbsolutePaths)
{
// this solution is less than ideal, it'll only work for windows drive paths (not network paths), and in
// order to make this better we need to change things down inside the material system / texture code.
// it's only used for the workshop preview con command right now.
const char* pPatternPath = pKVEntry->GetString( "pattern", pDefault->sPattern.String() );
#ifdef PLATFORM_WINDOWS
int nLength = V_strlen( pPatternPath );
if ( nLength > 2 && !( pPatternPath[0] == '/' && pPatternPath[1] == '/' && pPatternPath[2] != '/' ) && V_IsAbsolutePath( pPatternPath ) )
{
sPattern.Format( "//./%s", pPatternPath );
}
else
#endif
{
sPattern.Set( pPatternPath );
}
}
else
{
sPattern.Set( pKVEntry->GetString( "pattern", pDefault->sPattern.String() ) );
}
sLogoMaterial.Set( pKVEntry->GetString( "logo_material", pDefault->sLogoMaterial.String() ) );
nStyle = pKVEntry->GetInt( "style", pDefault->nStyle );
flWearDefault = pKVEntry->GetFloat( "wear_default", pDefault->flWearDefault );
flWearRemapMin = pKVEntry->GetFloat( "wear_remap_min", pDefault->flWearRemapMin );
flWearRemapMax = pKVEntry->GetFloat( "wear_remap_max", pDefault->flWearRemapMax );
nFixedSeed = pKVEntry->GetInt( "seed", pDefault->nFixedSeed );
uchPhongExponent = pKVEntry->GetInt( "phongexponent", pDefault->uchPhongExponent );
uchPhongAlbedoBoost = pKVEntry->GetInt( "phongalbedoboost", static_cast< int >( pDefault->uchPhongAlbedoBoost ) - 1 ) + 1;
uchPhongIntensity = pKVEntry->GetInt( "phongintensity", pDefault->uchPhongIntensity );
flPatternScale = pKVEntry->GetFloat( "pattern_scale", pDefault->flPatternScale );
flPatternOffsetXStart = pKVEntry->GetFloat( "pattern_offset_x_start", pDefault->flPatternOffsetXStart );
flPatternOffsetXEnd = pKVEntry->GetFloat( "pattern_offset_x_end", pDefault->flPatternOffsetXEnd );
flPatternOffsetYStart = pKVEntry->GetFloat( "pattern_offset_y_start", pDefault->flPatternOffsetYStart );
flPatternOffsetYEnd = pKVEntry->GetFloat( "pattern_offset_y_end", pDefault->flPatternOffsetYEnd );
flPatternRotateStart = pKVEntry->GetFloat( "pattern_rotate_start", pDefault->flPatternRotateStart );
flPatternRotateEnd = pKVEntry->GetFloat( "pattern_rotate_end", pDefault->flPatternRotateEnd );
flLogoScale = pKVEntry->GetFloat( "logo_scale", pDefault->flLogoScale );
flLogoOffsetX = pKVEntry->GetFloat( "logo_offset_x", pDefault->flLogoOffsetX );
flLogoOffsetY = pKVEntry->GetFloat( "logo_offset_y", pDefault->flLogoOffsetY );
flLogoRotation = pKVEntry->GetFloat( "logo_rotation", pDefault->flLogoRotation );
bIgnoreWeaponSizeScale = pKVEntry->GetBool( "ignore_weapon_size_scale", pDefault->bIgnoreWeaponSizeScale );
nViewModelExponentOverrideSize = pKVEntry->GetInt( "view_model_exponent_override_size", pDefault->nViewModelExponentOverrideSize );
bOnlyFirstMaterial = pKVEntry->GetBool( "only_first_material", pDefault->bOnlyFirstMaterial );
char szColorName[ 16 ];
for ( int nColor = 0; nColor < CPaintKit::NUM_COLORS; ++nColor )
{
// Regular Colors
V_snprintf( szColorName, sizeof( szColorName ), "color%i", nColor );
KeyValues *pKVColor = pKVEntry->FindKey( szColorName );
if ( pKVColor )
{
const char *pchColor = pKVColor->GetString();
unsigned int cR, cG, cB;
sscanf( pchColor, "%u %u %u", &cR, &cG, &cB );
rgbaColor[ nColor ].SetColor( cR, cG, cB, 255 );
}
else
{
rgbaColor[ nColor ] = pDefault->rgbaColor[ nColor ];
}
// Logo Colors
V_snprintf( szColorName, sizeof( szColorName ), "logocolor%i", nColor );
pKVColor = pKVEntry->FindKey( szColorName );
if ( pKVColor )
{
const char *pchColor = pKVColor->GetString();
unsigned int cR, cG, cB;
sscanf( pchColor, "%u %u %u", &cR, &cG, &cB );
rgbaLogoColor[ nColor ].SetColor( cR, cG, cB, 255 );
}
else
{
rgbaLogoColor[ nColor ] = pDefault->rgbaLogoColor[ nColor ];
}
}
return true;
}
void CPaintKit::FillKeyValuesForWorkshop( KeyValues *pKVToFill ) const
{
pKVToFill->SetInt( "style", nStyle );
pKVToFill->SetString( "pattern", sPattern.String() );
char szColorName[ 16 ];
for ( int nColor = 0; nColor < CPaintKit::NUM_COLORS; ++nColor )
{
// Regular Colors
V_snprintf( szColorName, sizeof( szColorName ), "color%i", nColor );
pKVToFill->SetColor( szColorName, rgbaColor[ nColor ] );
}
pKVToFill->SetFloat( "pattern_scale", flPatternScale );
pKVToFill->SetFloat( "pattern_offset_x_start", flPatternOffsetXStart );
pKVToFill->SetFloat( "pattern_offset_x_end", flPatternOffsetXEnd );
pKVToFill->SetFloat( "pattern_offset_y_start", flPatternOffsetYStart );
pKVToFill->SetFloat( "pattern_offset_y_end", flPatternOffsetYEnd );
pKVToFill->SetFloat( "pattern_rotate_start", flPatternRotateStart );
pKVToFill->SetFloat( "pattern_rotate_end", flPatternRotateEnd );
pKVToFill->SetFloat( "wear_remap_min", flWearRemapMin );
pKVToFill->SetFloat( "wear_remap_max", flWearRemapMax );
pKVToFill->SetInt( "phongexponent", uchPhongExponent );
pKVToFill->SetInt( "phongalbedoboost", static_cast< int >( uchPhongAlbedoBoost ) - 1 );
pKVToFill->SetInt( "phongintensity", uchPhongIntensity );
pKVToFill->SetBool( "ignore_weapon_size_scale", bIgnoreWeaponSizeScale );
pKVToFill->SetInt( "view_model_exponent_override_size", nViewModelExponentOverrideSize );
pKVToFill->SetBool( "only_first_material", bOnlyFirstMaterial );
//pKVToFill->SetString( "logo_material", sLogoMaterial.String() );
//pKVToFill->SetFloat( "logo_scale", flLogoScale );
//pKVToFill->SetFloat( "logo_offset_x", flLogoOffsetX );
//pKVToFill->SetFloat( "logo_offset_y", flLogoOffsetY );
//pKVToFill->SetFloat( "logo_rotation", flLogoRotation );
//for ( int nColor = 0; nColor < CPaintKit::NUM_COLORS; ++nColor )
//{
// // Logo Colors
// V_snprintf( szColorName, sizeof( szColorName ), "logocolor%i", nColor );
// pKVToFill->SetColor( szColorName, rgbaLogoColor[ nColor ] );
//}
//pKVToFill->SetInt( "seed", nFixedSeed );
}
//-----------------------------------------------------------------------------
// Purpose: Adds a foreign item definition to local definition mapping for a
// foreign app
//-----------------------------------------------------------------------------
void CForeignAppImports::AddMapping( uint16 unForeignDefIndex, const CEconItemDefinition *pDefn )
{
m_mapDefinitions.InsertOrReplace( unForeignDefIndex, pDefn );
}
//-----------------------------------------------------------------------------
// Purpose: Adds a foreign item definition to local definition mapping for a
// foreign app
//-----------------------------------------------------------------------------
const CEconItemDefinition *CForeignAppImports::FindMapping( uint16 unForeignDefIndex ) const
{
int i = m_mapDefinitions.Find( unForeignDefIndex );
if( m_mapDefinitions.IsValidIndex( i ) )
return m_mapDefinitions[i];
else
return NULL;
}
//-----------------------------------------------------------------------------
// Purpose: Less function comparator for revolving loot lists map
//-----------------------------------------------------------------------------
static bool LLLessFunc( const int& e1, const int&e2 )
{
return e1 < e2;
}
//-----------------------------------------------------------------------------
// Purpose: Constructor
//-----------------------------------------------------------------------------
CEconItemSchema::CEconItemSchema( )
: m_unResetCount( 0 )
, m_pKVRawDefinition( NULL )
, m_unVersion( 0 )
, m_pDefaultItemDefinition( NULL )
, m_mapItemSets( DefLessFunc(const char*) )
, m_mapDefinitionPrefabs( DefLessFunc(const char*) )
, m_mapDefaultBodygroupState( DefLessFunc(const char*) )
, m_bSchemaParsingItems(false)
#if defined(CLIENT_DLL) || defined(GAME_DLL)
, m_pDelayedSchemaData( NULL )
#endif
, m_mapKillEaterScoreTypes( DefLessFunc( unsigned int ) )
{
Reset();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
IEconTool *CEconItemSchema::CreateEconToolImpl( const char *pszToolType, const char *pszUseString, const char *pszUsageRestriction, item_capabilities_t unCapabilities, KeyValues *pUsageKV )
{
return nullptr;
}
//-----------------------------------------------------------------------------
// Purpose: Resets the schema to before BInit was called
//-----------------------------------------------------------------------------
void CEconItemSchema::Reset( void )
{
++m_unResetCount;
m_unFirstValidClass = 0;
m_unLastValidClass = 0;
m_unFirstValidItemSlot = 0;
m_unLastValidItemSlot = 0;
m_unNumItemPresets = 0;
m_unMinLevel = 0;
m_unMaxLevel = 0;
m_nMaxValidGraffitiTintDefID = 0;
m_unSumQualityWeights = 0;
m_mapRarities.Purge();
m_mapSoundMaterials.Purge();
m_mapQualities.Purge();
m_mapItems.PurgeAndDeleteElements();
m_mapItemsSorted.Purge();
m_mapPaintKits.PurgeAndDeleteElements();
m_mapStickerKits.PurgeAndDeleteElements();
m_mapMusicDefs.PurgeAndDeleteElements();
m_dictStickerKits.Purge();
m_dictStickerLists.Purge();
m_mapAttributesContainer.PurgeAndDeleteElements();
FOR_EACH_VEC( m_vecAttributeTypes, i )
{
delete m_vecAttributeTypes[i].m_pAttrType;
}
m_vecAttributeTypes.Purge();
m_mapRecipes.Purge();
m_vecTimedRewards.Purge();
m_mapAlternateIcons.Purge();
m_mapItemSets.Purge();
m_dictLootLists.Purge();
m_mapAttributeControlledParticleSystems.Purge();
m_unVersion = 0;
if ( m_pKVRawDefinition )
{
m_pKVRawDefinition->deleteThis();
m_pKVRawDefinition = NULL;
}
#if defined(CLIENT_DLL) || defined(GAME_DLL)
delete m_pDefaultItemDefinition;
m_pDefaultItemDefinition = NULL;
#endif
FOR_EACH_MAP_FAST( m_mapRecipes, i )
{
delete m_mapRecipes[i];
}
FOR_EACH_MAP_FAST( m_mapDefinitionPrefabs, i )
{
m_mapDefinitionPrefabs[i]->deleteThis();
}
m_mapDefinitionPrefabs.Purge();
m_vecEquipRegionsList.Purge();
// m_vecItemLevelingData.PurgeAndDeleteElements();
m_vecItemLevelingData.Purge();
m_RandomStream.SetSeed( 0 );
}
//-----------------------------------------------------------------------------
// Purpose: Operator=
//-----------------------------------------------------------------------------
CEconItemSchema &CEconItemSchema::operator=( CEconItemSchema &rhs )
{
Reset();
BInitSchema( rhs.m_pKVRawDefinition );
return *this;
}
#if defined( CLIENT_DLL )
#define MERGE_LIVE_PLAYERS_CODE 0
static void Helper_MergeKeyValuesUsingTemplate( KeyValues *kvInto, KeyValues *kvSrc, KeyValues *kvTemplate )
{
FOR_EACH_SUBKEY( kvTemplate, kvTemplateSubkey )
{
if ( kvTemplateSubkey->GetDataType() == KeyValues::TYPE_NONE )
{
// This is a nested subkey
if ( !V_strcmp( kvTemplateSubkey->GetName(), "*" ) )
{
// This is a wildcard subkey
FOR_EACH_TRUE_SUBKEY( kvSrc, kvWildcardSrcSubkey )
{
KeyValues *pSubInto = kvInto->FindKey( kvWildcardSrcSubkey->GetNameSymbol() );
if ( !pSubInto )
{
#if MERGE_LIVE_PLAYERS_CODE
kvInto->AddSubKey( pSubInto = kvWildcardSrcSubkey->MakeCopy() );
#else
kvInto->AddSubKey( pSubInto = new KeyValues( kvWildcardSrcSubkey->GetName() ) );
#endif
}
Helper_MergeKeyValuesUsingTemplate( pSubInto, kvWildcardSrcSubkey, kvTemplateSubkey );
}
}
else
{
// This is a direct dive into subkey
KeyValues *pSubInto = kvInto->FindKey( kvTemplateSubkey->GetNameSymbol() );
KeyValues *pSubSrc = kvSrc->FindKey( kvTemplateSubkey->GetNameSymbol() );
if ( pSubInto && pSubSrc )
Helper_MergeKeyValuesUsingTemplate( pSubInto, pSubSrc, kvTemplateSubkey );
}
}
else
{
// This is a direct value copy
if ( KeyValues *pSubSrc = kvSrc->FindKey( kvTemplateSubkey->GetNameSymbol() ) )
{
if ( KeyValues *pSubInto = kvInto->FindKey( kvTemplateSubkey->GetNameSymbol() ) )
{
// Cannot override an existing value!
Assert( !"Cannot override existing schema value" );
}
else
{
// Make a copy and append
kvInto->AddSubKey( pSubSrc->MakeCopy() );
}
}
}
}
}
#endif
//-----------------------------------------------------------------------------
// Initializes the schema, given KV filename
//-----------------------------------------------------------------------------
bool CEconItemSchema::BInit( const char *fileName, const char *pathID, CUtlVector<CUtlString> *pVecErrors /* = NULL */)
{
Reset();
m_pKVRawDefinition = new KeyValues( "CEconItemSchema" );
m_pKVRawDefinition->UsesEscapeSequences( true );
if ( !m_pKVRawDefinition->LoadFromFile( g_pFullFileSystem, fileName, pathID ) )
{
m_pKVRawDefinition->deleteThis();
m_pKVRawDefinition = NULL;
}
if ( m_pKVRawDefinition )
{
#if defined( CLIENT_DLL )
if ( KeyValues *kvLiveTemplate = m_pKVRawDefinition->FindKey( "items_game_live" ) )
{
// for client code we also load the live file to supplement certain portions of schema
char chLiveFilename[MAX_PATH];
V_strcpy_safe( chLiveFilename, fileName );
if ( char *chExt = V_strrchr( chLiveFilename, '.' ) )
{
char chSuffix[ 64 ] = {};
V_sprintf_safe( chSuffix, "_live%s", chExt );
*chExt = 0;
V_strcat_safe( chLiveFilename, chSuffix );
KeyValues *kvLive = new KeyValues( "CEconItemSchema" );
kvLive->UsesEscapeSequences( true );
if ( kvLive->LoadFromFile( g_pFullFileSystem, chLiveFilename, pathID ) )
{
Helper_MergeKeyValuesUsingTemplate( m_pKVRawDefinition, kvLive, kvLiveTemplate );
}
#if MERGE_LIVE_PLAYERS_CODE
if ( KeyValues *kvDump = m_pKVRawDefinition->FindKey( "pro_players" ) )
{
kvDump->SaveToFile( g_pFullFileSystem, "pro_players_merged.txt" );
}
#endif
kvLive->deleteThis();
kvLive = NULL;
}
}
#endif
return BInitSchema( m_pKVRawDefinition, pVecErrors );
}
else
{
#if !defined(CSTRIKE_DLL)
Warning( "No %s file found. May be unable to create items.\n", fileName );
#endif // CSTRIKE_DLL
return false;
}
}
//-----------------------------------------------------------------------------
// Initializes the schema, given KV in binary form
//-----------------------------------------------------------------------------
bool CEconItemSchema::BInitBinaryBuffer( CUtlBuffer &buffer, CUtlVector<CUtlString> *pVecErrors /* = NULL */ )
{
Reset();
m_pKVRawDefinition = new KeyValues( "CEconItemSchema" );
m_pKVRawDefinition->UsesEscapeSequences( true );
if ( m_pKVRawDefinition->ReadAsBinary( buffer ) )
{
return BInitSchema( m_pKVRawDefinition, pVecErrors );
}
if ( pVecErrors )
{
pVecErrors->AddToTail( "Error parsing keyvalues" );
}
return false;
}
//-----------------------------------------------------------------------------
// Initializes the schema, given KV in text form
//-----------------------------------------------------------------------------
bool CEconItemSchema::BInitTextBuffer( CUtlBuffer &buffer, CUtlVector<CUtlString> *pVecErrors /* = NULL */ )
{
Reset();
m_pKVRawDefinition = new KeyValues( "CEconItemSchema" );
m_pKVRawDefinition->UsesEscapeSequences( true );
if ( m_pKVRawDefinition->LoadFromBuffer( NULL, buffer ) )
{
return BInitSchema( m_pKVRawDefinition, pVecErrors );
}
if ( pVecErrors )
{
pVecErrors->AddToTail( "Error parsing keyvalues" );
}
return false;
}
#if defined(CLIENT_DLL) || defined(GAME_DLL)
//-----------------------------------------------------------------------------
// Set up the buffer to use to reinitialize our schema next time we can do so safely.
//-----------------------------------------------------------------------------
void CEconItemSchema::MaybeInitFromBuffer( IDelayedSchemaData *pDelayedSchemaData )
{
// Use whatever our most current data block is.
if ( m_pDelayedSchemaData )
{
delete m_pDelayedSchemaData;
}
m_pDelayedSchemaData = pDelayedSchemaData;
#ifdef CLIENT_DLL
// If we aren't in a game we can parse immediately now.
if ( !engine->IsInGame() )
{
BInitFromDelayedBuffer();
}
#endif // CLIENT_DLL
}
//-----------------------------------------------------------------------------
// We're in a safe place to change the contents of the schema, so do so and clean
// up whatever memory we were using.
//-----------------------------------------------------------------------------
bool CEconItemSchema::BInitFromDelayedBuffer()
{
if ( !m_pDelayedSchemaData )
return true;
bool bSuccess = m_pDelayedSchemaData->InitializeSchema( this );
delete m_pDelayedSchemaData;
m_pDelayedSchemaData = NULL;
return bSuccess;
}
#endif // !GC_DLL
static void CalculateKeyValuesCRCRecursive( KeyValues *pKV, CRC32_t *crc, bool bIgnoreName = false )
{
// Hash in the key name in LOWERCASE. Keyvalues files are not deterministic due
// to the case insensitivity of the keys and the dependence on the existing
// state of the name table upon entry.
if ( !bIgnoreName )
{
const char *s = pKV->GetName();
for (;;)
{
unsigned char x = tolower(*s);
CRC32_ProcessBuffer( crc, &x, 1 ); // !SPEED! This is slow, but it works.
if (*s == '\0') break;
++s;
}
}
// Now hash in value, depending on type
// !FIXME! This is not byte-order independent!
switch ( pKV->GetDataType() )
{
case KeyValues::TYPE_NONE:
{
FOR_EACH_SUBKEY( pKV, pChild )
{
CalculateKeyValuesCRCRecursive( pChild, crc );
}
break;
}
case KeyValues::TYPE_STRING:
{
const char *val = pKV->GetString();
CRC32_ProcessBuffer( crc, val, V_strlen(val)+1 );
break;
}
case KeyValues::TYPE_INT:
{
int val = pKV->GetInt();
CRC32_ProcessBuffer( crc, &val, sizeof(val) );
break;
}
case KeyValues::TYPE_UINT64:
{
uint64 val = pKV->GetUint64();
CRC32_ProcessBuffer( crc, &val, sizeof(val) );
break;
}
case KeyValues::TYPE_FLOAT:
{
float val = pKV->GetFloat();
CRC32_ProcessBuffer( crc, &val, sizeof(val) );
break;
}
case KeyValues::TYPE_COLOR:
{
int val = pKV->GetColor().GetRawColor();
CRC32_ProcessBuffer( crc, &val, sizeof(val) );
break;
}
default:
case KeyValues::TYPE_PTR:
case KeyValues::TYPE_WSTRING:
{
Assert( !"Unsupport data type!" );
break;
}
}
}
uint32 CEconItemSchema::CalculateKeyValuesVersion( KeyValues *pKV )
{
CRC32_t crc;
CRC32_Init( &crc );
// Calc CRC recursively. Ignore the very top-most
// key name, which isn't set consistently
CalculateKeyValuesCRCRecursive( pKV, &crc, true );
CRC32_Final( &crc );
return crc;
}
//-----------------------------------------------------------------------------
// Purpose: Initializes the schema
// Input: pKVRawDefinition - The raw KeyValues representation of the schema
// pVecErrors - An optional vector that will contain error messages if
// the init fails.
// Output: True if initialization succeeded, false otherwise
//-----------------------------------------------------------------------------
bool CEconItemSchema::BInitSchema( KeyValues *pKVRawDefinition, CUtlVector<CUtlString> *pVecErrors /* = NULL */ )
{
#if !defined( GC_DLL )
m_unVersion = CalculateKeyValuesVersion( pKVRawDefinition );
#endif
m_unMinLevel = pKVRawDefinition->GetInt( "item_level_min", 0 );
m_unMaxLevel = pKVRawDefinition->GetInt( "item_level_max", 0 );
// Pre-parsed arrays in order of appearance for schema initialization
CUtlVector< KeyValues * > arrRootPrefabs;
CUtlVector< KeyValues * > arrRootClientLootLists;
CUtlVector< KeyValues * > arrRootRevolvingLootlists;
CUtlVector< KeyValues * > arrRootLootlists;
CUtlVector< KeyValues * > arrRootMiscLootlists;
CUtlVector< KeyValues * > arrRootStickerKits;
CUtlVector< KeyValues * > arrRootStickerLists;
CUtlVector< KeyValues * > arrRootPaintKits;
CUtlVector< KeyValues * > arrRootPaintKitsRarity;
CUtlVector< KeyValues * > arrRootItems;
CUtlVector< KeyValues * > arrRootItemSets;
CUtlVector< KeyValues * > arrRootMusicDefinitions;
FOR_EACH_TRUE_SUBKEY( pKVRawDefinition, kvRootSubKey )
{
if ( !V_stricmp( kvRootSubKey->GetName(), "prefabs" ) )
arrRootPrefabs.AddToTail( kvRootSubKey );
else if ( !V_stricmp( kvRootSubKey->GetName(), "loot_lists" ) )
arrRootLootlists.AddToTail( kvRootSubKey );
else if ( !V_stricmp( kvRootSubKey->GetName(), "client_loot_lists" ) )
arrRootClientLootLists.AddToTail( kvRootSubKey );
else if ( !V_stricmp( kvRootSubKey->GetName(), "revolving_loot_lists" ) )
arrRootRevolvingLootlists.AddToTail( kvRootSubKey );
else if ( !V_stricmp( kvRootSubKey->GetName(), "loot_lists_misc" ) )
arrRootMiscLootlists.AddToTail( kvRootSubKey );
else if ( !V_stricmp( kvRootSubKey->GetName(), "sticker_kits" ) )
arrRootStickerKits.AddToTail( kvRootSubKey );
else if ( !V_stricmp( kvRootSubKey->GetName(), "sticker_lists" ) )
arrRootStickerLists.AddToTail( kvRootSubKey );
else if ( !V_stricmp( kvRootSubKey->GetName(), "paint_kits" ) )
arrRootPaintKits.AddToTail( kvRootSubKey );
else if ( !V_stricmp( kvRootSubKey->GetName(), "paint_kits_rarity" ) )
arrRootPaintKitsRarity.AddToTail( kvRootSubKey );
else if ( !V_stricmp( kvRootSubKey->GetName(), "items" ) )
arrRootItems.AddToTail( kvRootSubKey );
else if ( !V_stricmp( kvRootSubKey->GetName(), "item_sets" ) )
arrRootItemSets.AddToTail( kvRootSubKey );
else if ( !V_stricmp( kvRootSubKey->GetName(), "music_definitions" ) )
arrRootMusicDefinitions.AddToTail( kvRootSubKey );
}
// Parse the prefabs block first so the prefabs will be populated in case anything else wants
// to use them later.
FOR_EACH_VEC( arrRootPrefabs, i )
{
SCHEMA_INIT_SUBSTEP( BInitDefinitionPrefabs( arrRootPrefabs[i], pVecErrors ) );
}
// Initialize the game info block
KeyValues *pKVGameInfo = pKVRawDefinition->FindKey( "game_info" );
SCHEMA_INIT_CHECK( NULL != pKVGameInfo, CFmtStr( "Required key \"game_info\" missing.\n" ) );
if ( NULL != pKVGameInfo )
{
SCHEMA_INIT_SUBSTEP( BInitGameInfo( pKVGameInfo, pVecErrors ) );
}
// Initialize our attribute types. We don't actually pull this data from the schema right now but it
// still makes sense to initialize it at this point.
SCHEMA_INIT_SUBSTEP( BInitAttributeTypes( pVecErrors ) );
// Initialize the rarity block
KeyValues *pKVRarities = pKVRawDefinition->FindKey( "rarities" );
SCHEMA_INIT_CHECK( NULL != pKVRarities, CFmtStr( "Required key \"rarities\" missing.\n" ) );
if ( NULL != pKVRarities )
{
SCHEMA_INIT_SUBSTEP( BInitRarities( pKVRarities, pVecErrors ) );
}
// Initialize the qualities block
KeyValues *pKVQualities = pKVRawDefinition->FindKey( "qualities" );
SCHEMA_INIT_CHECK( NULL != pKVQualities, CFmtStr( "Required key \"qualities\" missing.\n" ) );
if ( NULL != pKVQualities )
{
SCHEMA_INIT_SUBSTEP( BInitQualities( pKVQualities, pVecErrors ) );
}
// Initialize the colors block
KeyValues *pKVColors = pKVRawDefinition->FindKey( "colors" );
SCHEMA_INIT_CHECK( NULL != pKVColors, CFmtStr( "Required key \"colors\" missing.\n" ) );
if ( NULL != pKVColors )
{
SCHEMA_INIT_SUBSTEP( BInitColors( pKVColors, pVecErrors ) );
}
SCHEMA_INIT_SUBSTEP( BInitGraffitiTints( pKVRawDefinition->FindKey( "graffiti_tints" ), pVecErrors ) );
// Initialize the music block
FOR_EACH_VEC( arrRootMusicDefinitions, i )
{
SCHEMA_INIT_SUBSTEP( BInitMusicDefs( arrRootMusicDefinitions[i], pVecErrors ) );
}
// Initialize the attributes block
KeyValues *pKVAttributes = pKVRawDefinition->FindKey( "attributes" );
SCHEMA_INIT_CHECK( NULL != pKVAttributes, CFmtStr( "Required key \"attributes\" missing.\n" ) );
if ( NULL != pKVAttributes )
{
SCHEMA_INIT_SUBSTEP( BInitAttributes( pKVAttributes, pVecErrors ) );
}
// Initialize the sound materials block
KeyValues *pKVSoundMaterials = pKVRawDefinition->FindKey( "sound_materials" );
if ( NULL != pKVSoundMaterials )
{
SCHEMA_INIT_SUBSTEP( BInitSoundMaterials( pKVSoundMaterials, pVecErrors ) );
}
// Initialize the "equip_regions_list" block -- this is an optional block
KeyValues *pKVEquipRegions = pKVRawDefinition->FindKey( "equip_regions_list" );
if ( NULL != pKVEquipRegions )
{
SCHEMA_INIT_SUBSTEP( BInitEquipRegions( pKVEquipRegions, pVecErrors ) );
}
// Initialize the "equip_conflicts" block -- this is an optional block, though it doesn't
// make any sense and will probably fail internally if there is no corresponding "equip_regions"
// block as well
KeyValues *pKVEquipRegionConflicts = pKVRawDefinition->FindKey( "equip_conflicts" );
if ( NULL != pKVEquipRegionConflicts )
{
SCHEMA_INIT_SUBSTEP( BInitEquipRegionConflicts( pKVEquipRegionConflicts, pVecErrors ) );
}
// Do before items, so items can refer into it.
KeyValues *pKVParticleSystems = pKVRawDefinition->FindKey( "attribute_controlled_attached_particles" );
SCHEMA_INIT_SUBSTEP( BInitAttributeControlledParticleSystems( pKVParticleSystems, pVecErrors ) );
FOR_EACH_VEC( arrRootStickerKits, i )
{
SCHEMA_INIT_SUBSTEP( BInitStickerKits( arrRootStickerKits[i], pVecErrors ) );
}
FOR_EACH_VEC( arrRootPaintKits, i )
{
SCHEMA_INIT_SUBSTEP( BInitPaintKits( arrRootPaintKits[i], pVecErrors ) );
}
FOR_EACH_VEC( arrRootPaintKitsRarity, i )
{
SCHEMA_INIT_SUBSTEP( BInitPaintKitsRarity( arrRootPaintKitsRarity[i], pVecErrors ) );
}
// Initialize the items block
SCHEMA_INIT_CHECK( arrRootItems.Count(), CFmtStr( "Required key(s) \"items\" missing.\n" ) );
FOR_EACH_VEC( arrRootItems, i )
{
m_bSchemaParsingItems = true;
SCHEMA_INIT_SUBSTEP( BInitItems( arrRootItems[i], pVecErrors ) );
m_bSchemaParsingItems = false;
}
SCHEMA_INIT_SUBSTEP( BInitItemMappings( pVecErrors ) );
// Setup bundle links
SCHEMA_INIT_SUBSTEP( BInitBundles( pVecErrors ) );
// Setup payment rules.
SCHEMA_INIT_SUBSTEP( BInitPaymentRules( pVecErrors ) );
// Parse the item_sets block.
FOR_EACH_VEC( arrRootItemSets, i )
{
SCHEMA_INIT_SUBSTEP( BInitItemSets( arrRootItemSets[i], pVecErrors ) );
}
// Initialize the quest block
KeyValues *pKVQuestDefs = pKVRawDefinition->FindKey( "quest_definitions" );
SCHEMA_INIT_CHECK( NULL != pKVQuestDefs, CFmtStr( "Required key \"quest_definitions\" missing.\n" ) );
if ( NULL != pKVQuestDefs )
{
SCHEMA_INIT_SUBSTEP( BInitQuestDefs( pKVQuestDefs, pVecErrors ) );
}
// Initialize the quest event schedule block
SCHEMA_INIT_SUBSTEP( BInitQuestEvents( pKVRawDefinition->FindKey( "quest_schedule" ), pVecErrors ) );
// Initialize the campaign block
KeyValues *pKVCampaignDefs = pKVRawDefinition->FindKey( "campaign_definitions" );
SCHEMA_INIT_CHECK( NULL != pKVCampaignDefs, CFmtStr( "Required key \"campaign_definitions\" missing.\n" ) );
if ( NULL != pKVCampaignDefs )
{
SCHEMA_INIT_SUBSTEP( BInitCampaignDefs( pKVCampaignDefs, pVecErrors ) );
}
// Parse any recipes block
KeyValues *pKVRecipes = pKVRawDefinition->FindKey( "recipes" );
if ( NULL != pKVRecipes )
{
SCHEMA_INIT_SUBSTEP( BInitRecipes( pKVRecipes, pVecErrors ) );
}
// Parse alternate icons block.
KeyValues * pKVAlternateIcons = pKVRawDefinition->FindKey( "alternate_icons2" );
if ( NULL != pKVAlternateIcons )
{
SCHEMA_INIT_SUBSTEP( BInitAlternateIcons( pKVAlternateIcons, pVecErrors ) );
}
// Reset our loot lists.
m_dictLootLists.Purge();
// Parse the collection loot lists block
KeyValues *pKVQuestRewardLootLists = pKVRawDefinition->FindKey( "quest_reward_loot_lists" );
SCHEMA_INIT_SUBSTEP( BInitQuestRewardLootLists( pKVQuestRewardLootLists, pVecErrors ) );
// Parse the loot lists block (on the GC)
KeyValues *pKVRandomAttributeTemplates = pKVRawDefinition->FindKey( "random_attribute_templates" );
// Parse the client loot lists block (everywhere)
FOR_EACH_VEC( arrRootClientLootLists, i )
{
SCHEMA_INIT_SUBSTEP( BInitLootLists( arrRootClientLootLists[i], pKVRandomAttributeTemplates, pVecErrors, false ) );
}
// Parse the revolving loot lists block
FOR_EACH_VEC( arrRootRevolvingLootlists, i )
{
SCHEMA_INIT_SUBSTEP( BInitRevolvingLootLists( arrRootRevolvingLootlists[i], pVecErrors ) );
}
#if defined( CLIENT_DLL ) || defined( GAME_DLL )
KeyValues *pKVArmoryData = pKVRawDefinition->FindKey( "armory_data" );
if ( NULL != pKVArmoryData )
{
SCHEMA_INIT_SUBSTEP( BInitArmoryData( pKVArmoryData, pVecErrors ) );
}
#endif
// Parse any achievement rewards
KeyValues *pKVAchievementRewards = pKVRawDefinition->FindKey( "achievement_rewards" );
if ( NULL != pKVAchievementRewards )
{
SCHEMA_INIT_SUBSTEP( BInitAchievementRewards( pKVAchievementRewards, pVecErrors ) );
}
#ifdef TF_CLIENT_DLL
// Compute the number of concrete items, for each item, and cache for quick access
SCHEMA_INIT_SUBSTEP( BInitConcreteItemCounts( pVecErrors ) );
#endif // TF_CLIENT_DLL
// Parse the item levels block
KeyValues *pKVItemLevels = pKVRawDefinition->FindKey( "item_levels" );
SCHEMA_INIT_SUBSTEP( BInitItemLevels( pKVItemLevels, pVecErrors ) );
// Parse the kill eater score types
KeyValues *pKVKillEaterScoreTypes = pKVRawDefinition->FindKey( "kill_eater_score_types" );
SCHEMA_INIT_SUBSTEP( BInitKillEaterScoreTypes( pKVKillEaterScoreTypes, pVecErrors ) );
#ifdef CLIENT_DLL
// Load web resource references.
KeyValues* pKVWebResources = pKVRawDefinition->FindKey( "web_resources" );
SCHEMA_INIT_SUBSTEP( BInitWebResources( pKVWebResources, pVecErrors ) );
#endif
// Load web resource references.
KeyValues* pProPlayers = pKVRawDefinition->FindKey( "pro_players" );
SCHEMA_INIT_SUBSTEP( BInitProPlayers( pProPlayers, pVecErrors ) );
SCHEMA_INIT_SUBSTEP( BPostSchemaInitStartupChecks( pVecErrors ) );
return SCHEMA_INIT_SUCCESS();
}
//-----------------------------------------------------------------------------
// Purpose: Initializes the "game_info" section of the schema
//-----------------------------------------------------------------------------
bool CEconItemSchema::BInitGameInfo( KeyValues *pKVGameInfo, CUtlVector<CUtlString> *pVecErrors )
{
m_unFirstValidClass = pKVGameInfo->GetInt( "first_valid_class", 0 );
m_unLastValidClass = pKVGameInfo->GetInt( "last_valid_class", 0 );
SCHEMA_INIT_CHECK( 0 <= m_unFirstValidClass, CFmtStr( "First valid class must be greater or equal to 0." ) );
SCHEMA_INIT_CHECK( m_unFirstValidClass <= m_unLastValidClass, CFmtStr( "First valid class must be less than or equal to last valid class." ) );
m_unFirstValidItemSlot = pKVGameInfo->GetInt( "first_valid_item_slot", INVALID_EQUIPPED_SLOT );
m_unLastValidItemSlot = pKVGameInfo->GetInt( "last_valid_item_slot", INVALID_EQUIPPED_SLOT );
SCHEMA_INIT_CHECK( INVALID_EQUIPPED_SLOT != m_unFirstValidItemSlot, CFmtStr( "first_valid_item_slot not set!" ) );
SCHEMA_INIT_CHECK( INVALID_EQUIPPED_SLOT != m_unLastValidItemSlot, CFmtStr( "last_valid_item_slot not set!" ) );
SCHEMA_INIT_CHECK( m_unFirstValidItemSlot <= m_unLastValidItemSlot, CFmtStr( "First valid item slot must be less than or equal to last valid item slot." ) );
m_unNumItemPresets = pKVGameInfo->GetInt( "num_item_presets", -1 );
SCHEMA_INIT_CHECK( (uint32)-1 != m_unNumItemPresets, CFmtStr( "num_item_presets not set!" ) );
return SCHEMA_INIT_SUCCESS();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CEconItemSchema::BInitAttributeTypes( CUtlVector<CUtlString> *pVecErrors )
{
FOR_EACH_VEC( m_vecAttributeTypes, i )
{
delete m_vecAttributeTypes[i].m_pAttrType;
}
m_vecAttributeTypes.Purge();
m_vecAttributeTypes.AddToTail( attr_type_t( NULL, new CSchemaAttributeType_Default ) );
m_vecAttributeTypes.AddToTail( attr_type_t( "uint32", new CSchemaAttributeType_Uint32 ) );
m_vecAttributeTypes.AddToTail( attr_type_t( "float", new CSchemaAttributeType_Float ) );
m_vecAttributeTypes.AddToTail( attr_type_t( "string", new CSchemaAttributeType_String ) );
m_vecAttributeTypes.AddToTail( attr_type_t( "vector", new CSchemaAttributeType_Vector ) );
return SCHEMA_INIT_SUCCESS();
}
//-----------------------------------------------------------------------------
// Purpose: Initializes the "prefabs" section of the schema
//-----------------------------------------------------------------------------
bool CEconItemSchema::BInitDefinitionPrefabs( KeyValues *pKVPrefabs, CUtlVector<CUtlString> *pVecErrors )
{
FOR_EACH_TRUE_SUBKEY( pKVPrefabs, pKVPrefab )
{
const char *pszPrefabName = pKVPrefab->GetName();
int nMapIndex = m_mapDefinitionPrefabs.Find( pszPrefabName );
// Make sure the item index is correct because we use this index as a reference
SCHEMA_INIT_CHECK(
!m_mapDefinitionPrefabs.IsValidIndex( nMapIndex ),
CFmtStr( "Duplicate prefab name (%s)", pszPrefabName ) );
m_mapDefinitionPrefabs.Insert( pszPrefabName, pKVPrefab->MakeCopy() );
}
return SCHEMA_INIT_SUCCESS();
}
//-----------------------------------------------------------------------------
// Purpose: Initializes the rarity section of the schema
//-----------------------------------------------------------------------------
bool CEconItemSchema::BInitRarities( KeyValues *pKVRarities, CUtlVector<CUtlString> *pVecErrors )
{
// initialize the item definitions
if ( NULL != pKVRarities )
{
FOR_EACH_TRUE_SUBKEY( pKVRarities, pKVRarity )
{
int nRarityIndex = pKVRarity->GetInt( "value" );
int nMapIndex = m_mapRarities.Find( nRarityIndex );
// Make sure the item index is correct because we use this index as a reference
SCHEMA_INIT_CHECK(
!m_mapRarities.IsValidIndex( nMapIndex ),
CFmtStr( "Duplicate rarity value (%d)", nRarityIndex ) );
nMapIndex = m_mapRarities.Insert( nRarityIndex );
SCHEMA_INIT_SUBSTEP( m_mapRarities[nMapIndex].BInitFromKV( pKVRarity, *this, pVecErrors ) );
}
}
return SCHEMA_INIT_SUCCESS();
}
//-----------------------------------------------------------------------------
// Purpose: Initializes the qualities section of the schema
// Input: pKVQualities - The qualities section of the KeyValues
// representation of the schema
// pVecErrors - An optional vector that will contain error messages if
// the init fails.
// Output: True if initialization succeeded, false otherwise
//-----------------------------------------------------------------------------
bool CEconItemSchema::BInitQualities( KeyValues *pKVQualities, CUtlVector<CUtlString> *pVecErrors )
{
// initialize the item definitions
if ( NULL != pKVQualities )
{
FOR_EACH_TRUE_SUBKEY( pKVQualities, pKVQuality )
{
int nQualityIndex = pKVQuality->GetInt( "value" );
int nMapIndex = m_mapQualities.Find( nQualityIndex );
// Make sure the item index is correct because we use this index as a reference
SCHEMA_INIT_CHECK(
!m_mapQualities.IsValidIndex( nMapIndex ),
CFmtStr( "Duplicate quality value (%d)", nQualityIndex ) );
nMapIndex = m_mapQualities.Insert( nQualityIndex );
SCHEMA_INIT_SUBSTEP( m_mapQualities[nMapIndex].BInitFromKV( pKVQuality, *this, pVecErrors ) );
}
}
// Check the integrity of the quality definitions
// Check for duplicate quality names
CUtlRBTree<const char *> rbQualityNames( CaselessStringLessThan );
rbQualityNames.EnsureCapacity( m_mapQualities.Count() );
FOR_EACH_MAP_FAST( m_mapQualities, i )
{
int iIndex = rbQualityNames.Find( m_mapQualities[i].GetName() );
SCHEMA_INIT_CHECK(
!rbQualityNames.IsValidIndex( iIndex ),
CFmtStr( "Quality definition %d: Duplicate quality name %s", m_mapQualities[i].GetDBValue(), m_mapQualities[i].GetName() ) );
if( !rbQualityNames.IsValidIndex( iIndex ) )
rbQualityNames.Insert( m_mapQualities[i].GetName() );
}
return SCHEMA_INIT_SUCCESS();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CEconItemSchema::BInitColors( KeyValues *pKVColors, CUtlVector<CUtlString> *pVecErrors )
{
// initialize the color definitions
if ( NULL != pKVColors )
{
FOR_EACH_TRUE_SUBKEY( pKVColors, pKVColor )
{
CEconColorDefinition *pNewColorDef = new CEconColorDefinition;
SCHEMA_INIT_SUBSTEP( pNewColorDef->BInitFromKV( pKVColor, *this, pVecErrors ) );
m_vecColorDefs.AddToTail( pNewColorDef );
}
}
return SCHEMA_INIT_SUCCESS();
}
bool CEconItemSchema::BInitGraffitiTints( KeyValues *pKVColors, CUtlVector<CUtlString> *pVecErrors )
{
FOR_EACH_TRUE_SUBKEY( pKVColors, pKVColor )
{
CEconGraffitiTintDefinition *pNewDef = new CEconGraffitiTintDefinition;
bool bResult = pNewDef->BInitFromKV( pKVColor, *this, pVecErrors );
const int nMaxIDallowed = 64;
if ( bResult )
{
SCHEMA_INIT_CHECK( ( pNewDef->GetID() > 0 ) && ( pNewDef->GetID() < nMaxIDallowed ),
CFmtStr( "Graffiti tint definition id out of range [1..%d]: #%d %s\n", nMaxIDallowed, pNewDef->GetID(), pNewDef->GetColorName() ) );
SCHEMA_INIT_CHECK( !m_vecGraffitiTintDefs.IsValidIndex( pNewDef->GetID() ) || !m_vecGraffitiTintDefs[pNewDef->GetID()],
CFmtStr( "Graffiti tint definition duplicate: #%d %s\n", pNewDef->GetID(), pNewDef->GetColorName() ) );
SCHEMA_INIT_CHECK( pNewDef->GetColorName() && *pNewDef->GetColorName() && !m_mapGraffitiTintByName.Defined( pNewDef->GetColorName() ),
CFmtStr( "Graffiti tint name duplicate: #%d %s\n", pNewDef->GetID(), pNewDef->GetColorName() ) );
bResult &= ( pNewDef->GetID() > 0 ) && ( pNewDef->GetID() < nMaxIDallowed ) &&
( !m_vecGraffitiTintDefs.IsValidIndex( pNewDef->GetID() ) || !m_vecGraffitiTintDefs[pNewDef->GetID()] ) &&
pNewDef->GetColorName() && *pNewDef->GetColorName() && !m_mapGraffitiTintByName.Defined( pNewDef->GetColorName() );
}
if ( !bResult )
{
SCHEMA_INIT_SUBSTEP( bResult );
delete pNewDef;
}
else
{
if ( !m_vecGraffitiTintDefs.Count() )
{
for ( int j = 0; j < nMaxIDallowed; ++ j )
m_vecGraffitiTintDefs.AddToTail( NULL );
}
m_vecGraffitiTintDefs[pNewDef->GetID()] = pNewDef;
m_mapGraffitiTintByName[pNewDef->GetColorName()] = pNewDef;
m_nMaxValidGraffitiTintDefID = MAX( m_nMaxValidGraffitiTintDefID, pNewDef->GetID() );
}
}
SCHEMA_INIT_CHECK( m_vecGraffitiTintDefs.Count() > 0, CFmtStr( "Graffiti tint definitions failed to parse\n" ) );
return SCHEMA_INIT_SUCCESS();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
AlternateIconData_t* CEconItemSchema::GetAlternateIcon( uint64 ullAlternateIcon )
{
int iIdx = m_mapAlternateIcons.Find( ullAlternateIcon );
if ( !m_mapAlternateIcons.IsValidIndex( iIdx ) )
return NULL;
else
return &(m_mapAlternateIcons[iIdx]);
}
//-----------------------------------------------------------------------------
// Purpose: Initializes the rarity section of the schema
//-----------------------------------------------------------------------------
bool CEconItemSchema::BInitSoundMaterials( KeyValues *pKVSoundMaterials, CUtlVector<CUtlString> *pVecErrors )
{
// initialize the item definitions
if ( NULL != pKVSoundMaterials )
{
FOR_EACH_TRUE_SUBKEY( pKVSoundMaterials, pKVSoundMaterial )
{
int nSoundMaterialIndex = pKVSoundMaterial->GetInt( "value" );
int nMapIndex = m_mapSoundMaterials.Find( nSoundMaterialIndex );
// Make sure the item index is correct because we use this index as a reference
SCHEMA_INIT_CHECK(
!m_mapSoundMaterials.IsValidIndex( nMapIndex ),
CFmtStr( "Duplicate sound material value (%d)", nSoundMaterialIndex ) );
nMapIndex = m_mapSoundMaterials.Insert( nSoundMaterialIndex );
SCHEMA_INIT_SUBSTEP( m_mapSoundMaterials[nMapIndex].BInitFromKV( pKVSoundMaterial, *this, pVecErrors ) );
}
}
return SCHEMA_INIT_SUCCESS();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
int CEconItemSchema::GetEquipRegionIndexByName( const char *pRegionName ) const
{
FOR_EACH_VEC( m_vecEquipRegionsList, i )
{
const char *szEntryRegionName = m_vecEquipRegionsList[i].m_sName;
if ( !V_stricmp( szEntryRegionName, pRegionName ) )
return i;
}
return -1;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
equip_region_mask_t CEconItemSchema::GetEquipRegionBitMaskByName( const char *pRegionName ) const
{
int iRegionIndex = GetEquipRegionIndexByName( pRegionName );
if ( !m_vecEquipRegionsList.IsValidIndex( iRegionIndex ) )
return 0;
equip_region_mask_t unRegionMask = 1 << m_vecEquipRegionsList[iRegionIndex].m_unBitIndex;
Assert( unRegionMask > 0 );
return unRegionMask;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CEconItemSchema::SetEquipRegionConflict( int iRegion, unsigned int unBit )
{
Assert( m_vecEquipRegionsList.IsValidIndex( iRegion ) );
equip_region_mask_t unRegionMask = 1 << unBit;
Assert( unRegionMask > 0 );
m_vecEquipRegionsList[iRegion].m_unMask |= unRegionMask;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
equip_region_mask_t CEconItemSchema::GetEquipRegionMaskByName( const char *pRegionName ) const
{
int iRegionIdx = GetEquipRegionIndexByName( pRegionName );
if ( iRegionIdx < 0 )
return 0;
return m_vecEquipRegionsList[iRegionIdx].m_unMask;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CEconItemSchema::AssignDefaultBodygroupState( const char *pszBodygroupName, int iValue )
{
// Flip the value passed in -- if we specify in the schema that a region should be off, we assume that it's
// on by default.
int iDefaultValue = iValue == 0 ? 1 : 0;
// Make sure that we're constantly reinitializing our default value to the same default value. This is sort
// of dumb but it works for everything we've got now. In the event that conflicts start cropping up it would
// be easy enough to make a new schema section.
int iIndex = m_mapDefaultBodygroupState.Find( pszBodygroupName );
if ( (m_mapDefaultBodygroupState.IsValidIndex( iIndex ) && m_mapDefaultBodygroupState[iIndex] != iDefaultValue) ||
(iValue < 0 || iValue > 1) )
{
EmitWarning( SPEW_GC, 4, "Unable to get accurate read on whether bodygroup '%s' is enabled or disabled by default. (The schema is fine, but the code is confused and could stand to be made smarter.)\n", pszBodygroupName );
}
if ( !m_mapDefaultBodygroupState.IsValidIndex( iIndex ) )
{
m_mapDefaultBodygroupState.Insert( pszBodygroupName, iDefaultValue );
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CEconItemSchema::BInitEquipRegions( KeyValues *pKVEquipRegions, CUtlVector<CUtlString> *pVecErrors )
{
CUtlVector<const char *> vecNames;
FOR_EACH_SUBKEY( pKVEquipRegions, pKVRegion )
{
const char *pRegionKeyName = pKVRegion->GetName();
vecNames.Purge();
// The "shared" name is special for equip regions -- it means that all of the sub-regions specified
// will use the same bit to store equipped-or-not data, but that one bit can be accessed by a whole
// bunch of different names. This is useful in TF where different classes have different regions, but
// those regions cannot possibly conflict with each other. For example, "scout_backpack" cannot possibly
// overlap with "pyro_shoulder" because they can't even be equipped on the same character.
if ( pRegionKeyName && !Q_stricmp( pRegionKeyName, "shared" ) )
{
FOR_EACH_SUBKEY( pKVRegion, pKVSharedRegionName )
{
vecNames.AddToTail( pKVSharedRegionName->GetName() );
}
}
// We have a standard name -- this one entry is its own equip region.
else
{
vecNames.AddToTail( pRegionKeyName );
}
// What bit will this equip region use to mask against conflicts? If we don't have any equip regions
// at all, we'll use the base bit, otherwise we just grab one higher than whatever we used last.
unsigned int unNewBitIndex = m_vecEquipRegionsList.Count() <= 0 ? 0 : m_vecEquipRegionsList.Tail().m_unBitIndex + 1;
FOR_EACH_VEC( vecNames, i )
{
const char *pRegionName = vecNames[i];
// Make sure this name is unique.
if ( GetEquipRegionIndexByName( pRegionName ) >= 0 )
{
pVecErrors->AddToTail( CFmtStr( "Duplicate equip region named \"%s\".", pRegionName ).Access() );
continue;
}
// Make a new region.
EquipRegion newEquipRegion;
newEquipRegion.m_sName = pRegionName;
newEquipRegion.m_unMask = 0; // we'll update this mask later
newEquipRegion.m_unBitIndex = unNewBitIndex;
int iIdx = m_vecEquipRegionsList.AddToTail( newEquipRegion );
// Tag this region to conflict with itself so that if nothing else two items in the same
// region can't equip over each other.
SetEquipRegionConflict( iIdx, unNewBitIndex );
}
}
return SCHEMA_INIT_SUCCESS();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CEconItemSchema::BInitEquipRegionConflicts( KeyValues *pKVEquipRegionConflicts, CUtlVector<CUtlString> *pVecErrors )
{
FOR_EACH_TRUE_SUBKEY( pKVEquipRegionConflicts, pKVConflict )
{
// What region is the base of this conflict?
const char *pRegionName = pKVConflict->GetName();
int iRegionIdx = GetEquipRegionIndexByName( pRegionName );
if ( iRegionIdx < 0 )
{
pVecErrors->AddToTail( CFmtStr( "Unable to find base equip region named \"%s\" for conflicts.", pRegionName ).Access() );
continue;
}
FOR_EACH_SUBKEY( pKVConflict, pKVConflictOther )
{
const char *pOtherRegionName = pKVConflictOther->GetName();
int iOtherRegionIdx = GetEquipRegionIndexByName( pOtherRegionName );
if ( iOtherRegionIdx < 0 )
{
pVecErrors->AddToTail( CFmtStr( "Unable to find other equip region named \"%s\" for conflicts.", pOtherRegionName ).Access() );
continue;
}
SetEquipRegionConflict( iRegionIdx, m_vecEquipRegionsList[iOtherRegionIdx].m_unBitIndex );
SetEquipRegionConflict( iOtherRegionIdx, m_vecEquipRegionsList[iRegionIdx].m_unBitIndex );
}
}
return SCHEMA_INIT_SUCCESS();
}
//-----------------------------------------------------------------------------
// Purpose: Initializes the attributes section of the schema
// Input: pKVAttributes - The attributes section of the KeyValues
// representation of the schema
// pVecErrors - An optional vector that will contain error messages if
// the init fails.
// Output: True if initialization succeeded, false otherwise
//-----------------------------------------------------------------------------
bool CEconItemSchema::BInitAttributes( KeyValues *pKVAttributes, CUtlVector<CUtlString> *pVecErrors )
{
// Initialize the attribute definitions
FOR_EACH_TRUE_SUBKEY( pKVAttributes, pKVAttribute )
{
int nAttrIndex = Q_atoi( pKVAttribute->GetName() );
// Make sure the index is positive
SCHEMA_INIT_CHECK(
( nAttrIndex >= 0 ) && ( nAttrIndex < 30*1000 ),
CFmtStr( "Attribute definition index %d must be greater than or equal to zero", nAttrIndex ) );
if ( !( ( nAttrIndex >= 0 ) && ( nAttrIndex < 30*1000 ) ) )
continue;
// Build the direct mapping container
while ( nAttrIndex >= m_mapAttributesContainer.Count() )
m_mapAttributesContainer.AddToTail( NULL );
Assert( nAttrIndex < m_mapAttributesContainer.Count() );
// Make sure the attribute index is not repeated
SCHEMA_INIT_CHECK(
!m_mapAttributesContainer[nAttrIndex],
CFmtStr( "Duplicate attribute definition index (%d)", nAttrIndex ) );
if ( m_mapAttributesContainer[nAttrIndex] )
continue;
m_mapAttributesContainer[nAttrIndex] = new CEconItemAttributeDefinition;
SCHEMA_INIT_SUBSTEP( m_mapAttributesContainer[nAttrIndex]->BInitFromKV( pKVAttribute, *this, pVecErrors ) );
}
// Check the integrity of the attribute definitions
// Check for duplicate attribute definition names
CUtlRBTree<const char *> rbAttributeNames( CaselessStringLessThan );
rbAttributeNames.EnsureCapacity( m_mapAttributesContainer.Count() );
FOR_EACH_VEC( m_mapAttributesContainer, i )
{
if ( !m_mapAttributesContainer[i] )
continue;
int iIndex = rbAttributeNames.Find( m_mapAttributesContainer[i]->GetDefinitionName() );
SCHEMA_INIT_CHECK(
!rbAttributeNames.IsValidIndex( iIndex ),
CFmtStr( "Attribute definition %d: Duplicate name \"%s\"", i, m_mapAttributesContainer[i]->GetDefinitionName() ) );
if( !rbAttributeNames.IsValidIndex( iIndex ) )
rbAttributeNames.Insert( m_mapAttributesContainer[i]->GetDefinitionName() );
}
return SCHEMA_INIT_SUCCESS();
}
//-----------------------------------------------------------------------------
// Purpose: After bundles are set up, we can do payment rules.
//-----------------------------------------------------------------------------
bool CEconItemSchema::BInitPaymentRules( CUtlVector<CUtlString> *pVecErrors )
{
return true;
}
//-----------------------------------------------------------------------------
// Purpose: After items are initialized, this method links bundles to their contents.
//-----------------------------------------------------------------------------
bool CEconItemSchema::BInitBundles( CUtlVector<CUtlString> *pVecErrors )
{
// Link each bundle with its contents.
FOR_EACH_MAP_FAST( m_mapItems, i )
{
CEconItemDefinition *pItemDef = m_mapItems[ i ];
if ( !pItemDef )
continue;
KeyValues* pItemKV = pItemDef->GetRawDefinition();
if ( !pItemKV )
continue;
KeyValues *pBundleDataKV = pItemKV->FindKey( "bundle" );
if ( pBundleDataKV )
{
pItemDef->m_BundleInfo = new bundleinfo_t();
FOR_EACH_SUBKEY( pBundleDataKV, pKVCurItem )
{
item_list_entry_t entry;
bool bEntrySuccess = entry.InitFromName( pKVCurItem->GetName() );
SCHEMA_INIT_CHECK(
bEntrySuccess,
CFmtStr( "Bundle %s: Item definition \"%s\" failed to initialize\n", pItemDef->m_pszDefinitionName, pKVCurItem->GetName() ) );
const CEconItemDefinition *pBundleItemDef = GetItemDefinition( entry.m_nItemDef );
SCHEMA_INIT_CHECK( pBundleItemDef, CFmtStr( "Unable to find item definition '%s' for bundle '%s'.", pKVCurItem->GetName(), pItemDef->m_pszDefinitionName ) );
pItemDef->m_BundleInfo->vecItemEntries.AddToTail( entry );
}
// 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.
pItemDef->m_bIsPackBundle = pItemKV->GetInt( "is_pack_bundle", 0 ) != 0;
}
// Make a list of all of our bundles.
if ( pItemDef->IsBundle() )
{
// Cache off the item def for the bundle, since we'll need both the bundle info and the item def index later.
m_vecBundles.AddToTail( pItemDef );
// If the bundle is a pack bundle, mark all the contained items as pack items / link to the owning pack bundle
if ( pItemDef->IsPackBundle() )
{
const bundleinfo_t *pBundleInfo = pItemDef->GetBundleInfo();
FOR_EACH_VEC( pBundleInfo->vecItemEntries, iCurItem )
{
CEconItemDefinition *pCurItemDef = GetItemDefinitionMutable( pBundleInfo->vecItemEntries[ iCurItem ].m_nItemDef );
SCHEMA_INIT_CHECK( NULL == pCurItemDef->m_pOwningPackBundle, CFmtStr( "Pack item \"%s\" included in more than one pack bundle - not allowed!", pCurItemDef->GetDefinitionName() ) );
pCurItemDef->m_pOwningPackBundle = pItemDef;
}
}
}
}
// Go through all regular (ie non-pack) bundles and ensure that if any pack items are included, *all* pack items in the owning pack bundle are included.
FOR_EACH_VEC( m_vecBundles, iBundle )
{
const CEconItemDefinition *pBundleItemDef = m_vecBundles[ iBundle ];
if ( pBundleItemDef->IsPackBundle() )
continue;
// Go through all items in the bundle and look for pack items
const bundleinfo_t *pBundle = pBundleItemDef->GetBundleInfo();
if ( pBundle )
{
FOR_EACH_VEC( pBundle->vecItemEntries, iContainedBundleItem )
{
// Get the associated pack bundle
const CEconItemDefinition *pContainedBundleItemDef = GetItemDefinition( pBundle->vecItemEntries[ iContainedBundleItem ].m_nItemDef );
// Ignore non-pack items
if ( !pContainedBundleItemDef || !pContainedBundleItemDef->IsPackItem() )
continue;
// Get the pack bundle that contains this particular pack item
const CEconItemDefinition *pOwningPackBundleItemDef = pContainedBundleItemDef->GetOwningPackBundle();
// Make sure all items in the owning pack bundle are in pBundleItemDef's bundle info (pBundle)
const bundleinfo_t *pOwningPackBundle = pOwningPackBundleItemDef->GetBundleInfo();
FOR_EACH_VEC( pOwningPackBundle->vecItemEntries, iCurPackBundleItem )
{
bool bFound = false;
FOR_EACH_VEC( pBundle->vecItemEntries, i )
{
if ( pOwningPackBundle->vecItemEntries[ iCurPackBundleItem ].m_nItemDef == pBundle->vecItemEntries[ i ].m_nItemDef &&
pOwningPackBundle->vecItemEntries[ iCurPackBundleItem ].m_nPaintKit == pBundle->vecItemEntries[ i ].m_nPaintKit )
{
bFound = true;
break;
}
}
if ( !bFound )
{
SCHEMA_INIT_CHECK(
false,
CFmtStr( "The bundle \"%s\" contains some, but not all pack items required specified by pack bundle \"%s.\"",
pBundleItemDef->GetDefinitionName(),
pOwningPackBundleItemDef->GetDefinitionName()
)
);
}
}
}
}
}
return true;
}
//-----------------------------------------------------------------------------
// Purpose: Initializes the items section of the schema
// Input: pKVItems - The items section of the KeyValues
// representation of the schema
// pVecErrors - An optional vector that will contain error messages if
// the init fails.
// Output: True if initialization succeeded, false otherwise
//-----------------------------------------------------------------------------
bool CEconItemSchema::BInitItems( KeyValues *pKVItems, CUtlVector<CUtlString> *pVecErrors )
{
FOR_EACH_TRUE_SUBKEY( pKVItems, pKVItem )
{
if ( Q_stricmp( pKVItem->GetName(), "default" ) == 0 )
{
#if defined(CLIENT_DLL) || defined(GAME_DLL)
SCHEMA_INIT_CHECK(
m_pDefaultItemDefinition == NULL,
CFmtStr( "Duplicate 'default' item definition." ) );
if ( m_pDefaultItemDefinition == NULL )
{
m_pDefaultItemDefinition = CreateEconItemDefinition();
}
SCHEMA_INIT_SUBSTEP( m_pDefaultItemDefinition->BInitFromKV( pKVItem, *this, pVecErrors ) );
#endif
}
else
{
int nItemIndex = Q_atoi( pKVItem->GetName() );
int nMapIndex = m_mapItems.Find( nItemIndex );
// Make sure the item index is correct because we use this index as a reference
SCHEMA_INIT_CHECK(
!m_mapItems.IsValidIndex( nMapIndex ),
CFmtStr( "Duplicate item definition (%d)", nItemIndex ) );
// Check to make sure the index is positive
SCHEMA_INIT_CHECK(
nItemIndex >= 0,
CFmtStr( "Item definition index %d must be greater than or equal to zero", nItemIndex ) );
CEconItemDefinition *pItemDef = CreateEconItemDefinition();
nMapIndex = m_mapItems.Insert( nItemIndex, pItemDef );
m_mapItemsSorted.Insert( nItemIndex, pItemDef );
SCHEMA_INIT_SUBSTEP( m_mapItems[nMapIndex]->BInitFromKV( pKVItem, *this, pVecErrors ) );
}
}
return SCHEMA_INIT_SUCCESS();
}
bool CEconItemSchema::BInitItemMappings( CUtlVector<CUtlString> *pVecErrors )
{
// Check the integrity of the item definitions
CUtlRBTree<const char *> rbItemNames( CaselessStringLessThan );
rbItemNames.EnsureCapacity( m_mapItems.Count() );
FOR_EACH_MAP_FAST( m_mapItems, i )
{
CEconItemDefinition *pItemDef = m_mapItems[ i ];
// Check for duplicate item definition names
int iIndex = rbItemNames.Find( pItemDef->GetDefinitionName() );
SCHEMA_INIT_CHECK(
!rbItemNames.IsValidIndex( iIndex ),
CFmtStr( "Item definition %s: Duplicate name on index %d", pItemDef->GetDefinitionName(), m_mapItems.Key( i ) ) );
if( !rbItemNames.IsValidIndex( iIndex ) )
rbItemNames.Insert( m_mapItems[i]->GetDefinitionName() );
// Link up armory and store mappings for the item
SCHEMA_INIT_SUBSTEP( pItemDef->BInitItemMappings( *this, pVecErrors ) );
}
return SCHEMA_INIT_SUCCESS();
}
//-----------------------------------------------------------------------------
// Purpose: Delete an item definition. Moderately dangerous as cached references will become bad.
// Intended for use by the item editor.
//-----------------------------------------------------------------------------
bool CEconItemSchema::DeleteItemDefinition( int iDefIndex )
{
m_mapItemsSorted.Remove( iDefIndex );
int nMapIndex = m_mapItems.Find( iDefIndex );
if ( m_mapItems.IsValidIndex( nMapIndex ) )
{
CEconItemDefinition* pItemDef = m_mapItems[nMapIndex];
if ( pItemDef )
{
m_mapItems.RemoveAt( nMapIndex );
delete pItemDef;
return true;
}
}
return false;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
const CEconItemSetDefinition* CEconItemSchema::GetItemSet( const char* pSetName, int *piIndex ) const
{
for ( unsigned int i=0; i<m_mapItemSets.Count(); ++i )
{
if ( !Q_strcmp( m_mapItemSets.Key(i), pSetName ) )
{
if ( piIndex )
{
*piIndex = i;
}
return (CEconItemSetDefinition*) &(m_mapItemSets[i]);
}
}
return NULL;
}
const IEconItemSetDefinition* CEconItemSchema::GetItemSet( int iIndex ) const
{
if ( m_mapItemSets.IsValidIndex( iIndex ) )
return &m_mapItemSets[iIndex];
else
return NULL;
}
//-----------------------------------------------------------------------------
// Purpose: Parses the Item Sets section.
//-----------------------------------------------------------------------------
bool CEconItemSchema::BInitItemSets( KeyValues *pKVItemSets, CUtlVector<CUtlString> *pVecErrors )
{
FOR_EACH_TRUE_SUBKEY( pKVItemSets, pKVItemSet )
{
const char* setName = pKVItemSet->GetName();
SCHEMA_INIT_CHECK( setName != NULL, CFmtStr( "All itemsets must have names.") );
if ( m_mapItemSets.Count() > 0 )
{
SCHEMA_INIT_CHECK( GetItemSet( setName ) == NULL, CFmtStr( "Duplicate itemset name (%s) found!", setName ) );
}
int idx = m_mapItemSets.Insert( setName );
SCHEMA_INIT_SUBSTEP( m_mapItemSets[idx].BInitFromKV( pKVItemSet, *this, pVecErrors ) );
FOR_EACH_VEC( m_mapItemSets[idx].m_ItemEntries, nEntry )
{
item_list_entry_t &itemListEntry = m_mapItemSets[idx].m_ItemEntries[ nEntry ];
CEconItemDefinition *pItemDef = GetItemDefinitionMutable( itemListEntry.m_nItemDef );
if ( pItemDef )
{
pItemDef->AddItemSet( idx );
}
}
}
return SCHEMA_INIT_SUCCESS();
}
//-----------------------------------------------------------------------------
// Purpose: Initializes the timed rewards section of the schema
// Input: pKVTimedRewards - The timed_rewards section of the KeyValues
// representation of the schema
// pVecErrors - An optional vector that will contain error messages if
// the init fails.
// Output: True if initialization succeeded, false otherwise
//-----------------------------------------------------------------------------
bool CEconItemSchema::BInitTimedRewards( KeyValues *pKVTimedRewards, CUtlVector<CUtlString> *pVecErrors )
{
m_vecTimedRewards.Purge();
// initialize the rewards sections
if ( NULL != pKVTimedRewards )
{
FOR_EACH_TRUE_SUBKEY( pKVTimedRewards, pKVTimedReward )
{
int index = m_vecTimedRewards.AddToTail();
SCHEMA_INIT_SUBSTEP( m_vecTimedRewards[index].BInitFromKV( pKVTimedReward, *this, pVecErrors ) );
}
}
return SCHEMA_INIT_SUCCESS();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
const CTimedItemRewardDefinition* CEconItemSchema::GetTimedReward( eTimedRewardType type ) const
{
if ( (int)type < m_vecTimedRewards.Count() )
{
return &m_vecTimedRewards[type];
}
return NULL;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
const CEconLootListDefinition* CEconItemSchema::GetLootListByName( const char* pListName, int *out_piIndex ) const
{
int iIdx = m_dictLootLists.Find( pListName );
if ( out_piIndex )
*out_piIndex = iIdx;
if ( m_dictLootLists.IsValidIndex( iIdx ) )
return &m_dictLootLists[iIdx];
else
return NULL;
}
//-----------------------------------------------------------------------------
// Purpose: Initializes the loot lists section of the schema
//-----------------------------------------------------------------------------
bool CEconItemSchema::BInitLootLists( KeyValues *pKVLootLists, KeyValues *pKVRandomAttributeTemplates, CUtlVector<CUtlString> *pVecErrors, bool bServerLists )
{
FOR_EACH_TRUE_SUBKEY( pKVLootLists, pKVLootList )
{
const char* listName = pKVLootList->GetName();
SCHEMA_INIT_CHECK( listName != NULL, CFmtStr( "All lootlists must have names.") );
if ( m_dictLootLists.Count() > 0 )
{
SCHEMA_INIT_CHECK( GetLootListByName( listName ) == NULL, CFmtStr( "Duplicate lootlist name (%s) found!", listName ) );
}
int idx = m_dictLootLists.Insert( listName );
SCHEMA_INIT_SUBSTEP( m_dictLootLists[idx].BInitFromKV( pKVLootList, pKVRandomAttributeTemplates, *this, pVecErrors, bServerLists ) );
}
return SCHEMA_INIT_SUCCESS();
}
//-----------------------------------------------------------------------------
// Purpose: Initializes the revolving loot lists section of the schema
//-----------------------------------------------------------------------------
bool CEconItemSchema::BInitRevolvingLootLists( KeyValues *pKVLootLists, CUtlVector<CUtlString> *pVecErrors )
{
FOR_EACH_SUBKEY( pKVLootLists, pKVList )
{
int iListIdx = atoi( pKVList->GetName() );
const char* strListName = pKVList->GetString();
m_mapRevolvingLootLists.Insert( iListIdx, strListName );
}
return SCHEMA_INIT_SUCCESS();
}
//-----------------------------------------------------------------------------
// Purpose: Initializes the quest reward loot lists section of the schema
//-----------------------------------------------------------------------------
bool CEconItemSchema::BInitQuestRewardLootLists( KeyValues *pKVLootLists, CUtlVector<CUtlString> *pVecErrors )
{
m_mapQuestRewardLootLists.Purge();
if ( NULL != pKVLootLists )
{
FOR_EACH_SUBKEY( pKVLootLists, pKVList )
{
int iListIdx = atoi( pKVList->GetName() );
const char* strListName = pKVList->GetString();
m_mapQuestRewardLootLists.Insert( iListIdx, strListName );
}
}
return SCHEMA_INIT_SUCCESS();
}
//-----------------------------------------------------------------------------
// Alternate Icons
//-----------------------------------------------------------------------------
bool CEconItemSchema::BInitAlternateIcons( KeyValues *pKVAlternateIcons, CUtlVector<CUtlString> *pVecErrors )
{
m_mapAlternateIcons.Purge();
FOR_EACH_TRUE_SUBKEY( pKVAlternateIcons, pKVBlock )
{
bool bSprayTints = !V_stricmp( pKVBlock->GetName(), "spray_tint_icons" );
FOR_EACH_TRUE_SUBKEY( pKVBlock, pKVAlternateIcon )
{
if ( bSprayTints )
{
// Custom readable syntax for spray tints:
uint32 unSprayKitID = 0, unTintID1 = 0, unTintID2 = 0;
if ( 3 == sscanf( pKVAlternateIcon->GetName(), "%u:%u:%u", &unSprayKitID, &unTintID1, &unTintID2 ) )
{
CFmtStr fmtValueLookup( "icon_path_range:%u:%u", unTintID1, unTintID2 );
char const *szIconPath = pKVAlternateIcon->GetString( fmtValueLookup );
if ( szIconPath && *szIconPath )
{
for ( uint32 unTintID = unTintID1; unTintID <= unTintID2; ++unTintID )
{
uint64 ullAltIconKey = Helper_GetAlternateIconKeyForTintedStickerItem( unSprayKitID, unTintID );
BInitAlternateIcon( ullAltIconKey, CFmtStr( "%s_%u", szIconPath, unTintID ), pKVAlternateIcon, pVecErrors );
}
}
else
{
SCHEMA_INIT_CHECK( false,
CFmtStr( "Alternate icon '%s' > '%s' must define '%s'", pKVBlock->GetName(), pKVAlternateIcon->GetName(), fmtValueLookup.Get() ) );
}
}
}
else
{
uint64 ullAltIconKey = V_atoui64( pKVAlternateIcon->GetName() );
char const *szIconPath = pKVAlternateIcon->GetString( "icon_path" );
if ( szIconPath && *szIconPath )
{
BInitAlternateIcon( ullAltIconKey, szIconPath, pKVAlternateIcon, pVecErrors );
}
else
{
SCHEMA_INIT_CHECK( false,
CFmtStr( "Alternate icon '%s' > '%s' must define '%s'", pKVBlock->GetName(), pKVAlternateIcon->GetName(), "icon_path" ) );
}
}
}
}
return SCHEMA_INIT_SUCCESS();
}
bool CEconItemSchema::BInitAlternateIcon( uint64 ullAltIconKey, char const *szSimpleName, KeyValues *pKVAlternateIcon, CUtlVector<CUtlString> *pVecErrors )
{
SCHEMA_INIT_CHECK(
!m_mapAlternateIcons.IsValidIndex( m_mapAlternateIcons.Find( ullAltIconKey ) ),
CFmtStr( "Duplicate alternate icon definition '%s' (%llu)", pKVAlternateIcon->GetName(), ullAltIconKey ) );
SCHEMA_INIT_CHECK(
int64( ullAltIconKey ) > 0,
CFmtStr( "Alternate icon definition index '%s' (%llu) must be greater than zero", pKVAlternateIcon->GetName(), ullAltIconKey ) );
int nNew = m_mapAlternateIcons.Insert( ullAltIconKey );
m_mapAlternateIcons[ nNew ].sSimpleName = szSimpleName;
m_mapAlternateIcons[ nNew ].sLargeSimpleName.Format( "%s_large", szSimpleName );
return true;
}
#ifdef CLIENT_DLL
//-----------------------------------------------------------------------------
// Web Resources
//-----------------------------------------------------------------------------
bool CEconItemSchema::BInitWebResources( KeyValues *pKVWebResources, CUtlVector<CUtlString> *pVecErrors )
{
if ( CWebResource::s_Initialized || !pKVWebResources )
return true;
FOR_EACH_SUBKEY( pKVWebResources, pDef )
{
CWebResource* pWebResource = new CWebResource;
pWebResource->m_strName = pDef->GetString( "name" );
pWebResource->m_strURL = pDef->GetString( "url" );
pWebResource->m_bOnDemand = pDef->GetBool( "on_demand" );
pWebResource->m_pKeyValues = NULL;
pWebResource->m_fnLoadCallback = NULL;
m_dictWebResources.Insert( pDef->GetString( "name" ), pWebResource );
}
if ( CommandLine()->CheckParm( "-enabledownloadtest" ) )
{
CWebResource* pWebResource = new CWebResource;
pWebResource->m_strName = "Test";
pWebResource->m_strURL = "http://media.steampowered.com/apps/570/test/test.txt";
pWebResource->m_bOnDemand = true;
pWebResource->m_pKeyValues = NULL;
pWebResource->m_fnLoadCallback = NULL;
m_dictWebResources.Insert( "Test", pWebResource );
}
// If any of our resources need to be requested immediately, do it now.
FOR_EACH_DICT_FAST( m_dictWebResources, idx )
{
CWebResource* pWebResource = m_dictWebResources[idx];
if ( !pWebResource )
continue;
if ( !pWebResource->m_bOnDemand )
{
LoadWebResource( pWebResource->m_strName, NULL );
}
}
CWebResource::s_Initialized = true;
return SCHEMA_INIT_SUCCESS();
}
EWebResourceStatus CEconItemSchema::LoadWebResource( CUtlString strName, void (*fnCallback)( const char*, KeyValues* ), bool bForceReload )
{
return kWebResource_NotLoaded;
}
void CEconItemSchema::SetWebResource( CUtlString strName, KeyValues* pResourceKV )
{
int idx = m_dictWebResources.Find( strName.Get() );
if ( !m_dictWebResources.IsValidIndex( idx ) )
return;
CWebResource* pResource = m_dictWebResources[idx];
if ( pResource->m_pKeyValues )
{
pResource->m_pKeyValues->deleteThis();
pResource->m_pKeyValues = NULL;
}
pResource->m_pKeyValues = pResourceKV;
if ( pResource->m_fnLoadCallback )
pResource->m_fnLoadCallback( strName.Get(), pResourceKV );
}
#endif
bool CEconItemSchema::BInitProPlayers( KeyValues *pKVData, CUtlVector<CUtlString> *pVecErrors )
{
FOR_EACH_SUBKEY( pKVData, pDef )
{
// Create the pro player entry
CProPlayerData *pData = new CProPlayerData;
if ( !pData->BInitFromKeyValues( pDef, pVecErrors ) )
{
delete pData;
continue;
}
if ( m_mapProPlayersByAccountID.Find( pData->GetAccountID() ) != m_mapProPlayersByAccountID.InvalidIndex() )
{
SCHEMA_INIT_CHECK( false,
CFmtStr( "Pro-player entry '%s' maps to a duplicate AccountID", pDef->GetName() ) );
Assert( false );
delete pData;
continue;
}
if ( m_mapProPlayersByCode.Find( pData->GetCode() ) != UTL_INVAL_SYMBOL )
{
SCHEMA_INIT_CHECK( false,
CFmtStr( "Pro-player entry '%s' has a duplicate code '%s'", pDef->GetName(), pData->GetCode() ) );
Assert( false );
delete pData;
continue;
}
m_mapProPlayersByAccountID.InsertOrReplace( pData->GetAccountID(), pData );
FOR_EACH_TRUE_SUBKEY( pDef->FindKey( "events" ), kvEvent )
{
int nEventID = Q_atoi( kvEvent->GetName() );
int nTeamID = kvEvent->GetInt( "team" );
if ( nEventID && nTeamID )
{
uint64 uiKey = ( uint64( uint32( nEventID ) ) << 32 ) | uint32( nTeamID );
MapProPlayersByEventIDTeamID_t::IndexType_t idx = m_mapProPlayersByEventIDTeamID.Find( uiKey );
if ( idx == m_mapProPlayersByEventIDTeamID.InvalidIndex() )
idx = m_mapProPlayersByEventIDTeamID.InsertOrReplace( uiKey, new CUtlVector< const CProPlayerData * > );
m_mapProPlayersByEventIDTeamID.Element( idx )->AddToTail( pData );
}
}
}
return SCHEMA_INIT_SUCCESS();
}
const CProPlayerData * CEconItemSchema::GetProPlayerDataByAccountID( uint32 unAccountID ) const
{
MapProPlayersByAccountID_t::IndexType_t idx = m_mapProPlayersByAccountID.Find( unAccountID );
return ( idx == m_mapProPlayersByAccountID.InvalidIndex() ) ? NULL : m_mapProPlayersByAccountID.Element( idx );
}
const CUtlVector< const CProPlayerData * > * CEconItemSchema::GetProPlayersDataForEventIDTeamID( int nEventID, int nTeamID ) const
{
uint64 uiKey = ( uint64( uint32( nEventID ) ) << 32 ) | uint32( nTeamID );
MapProPlayersByEventIDTeamID_t::IndexType_t idx = m_mapProPlayersByEventIDTeamID.Find( uiKey );
return ( idx != m_mapProPlayersByEventIDTeamID.InvalidIndex() )
? m_mapProPlayersByEventIDTeamID.Element( idx ) : NULL;
}
bool CProPlayerData::BInitFromKeyValues( KeyValues *pDef, CUtlVector<CUtlString> *pVecErrors /* = NULL */ )
{
uint32 unAccountID = Q_atoi( pDef->GetName() );
SCHEMA_INIT_CHECK(
unAccountID != 0,
CFmtStr( "Pro-player entry '%s' doesn't have a valid AccountID", pDef->GetName() ) );
if ( !unAccountID )
return false;
m_nAccountID = unAccountID;
char const *szName = pDef->GetString( "name" );
if ( !szName || !*szName )
{
SCHEMA_INIT_CHECK( false,
CFmtStr( "Pro-player entry '%s' has no name", pDef->GetName() ) );
Assert( false );
return false;
}
m_sName = szName;
char const *szCode = pDef->GetString( "code" );
if ( !szCode || !*szCode )
{
SCHEMA_INIT_CHECK( false,
CFmtStr( "Pro-player entry '%s' has no code", pDef->GetName() ) );
Assert( false );
return false;
}
HelperValidateLocalizationStringToken( CFmtStr( "#SFUI_ProPlayer_%s", szCode ) );
m_sCode = szCode;
char const *szGeo = pDef->GetString( "geo" );
SCHEMA_INIT_CHECK( szGeo && *szGeo,
CFmtStr( "Pro-player entry '%s' has no geo defined", pDef->GetName() ) );
Assert( szGeo && *szGeo );
HelperValidateLocalizationStringToken( CFmtStr( "#SFUI_Country_%s", szGeo ) );
m_sGeo = szGeo;
m_pKVItem = pDef->MakeCopy();
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
}
//-----------------------------------------------------------------------------
// Purpose: Initializes the recipes section of the schema
// Input: pKVRecipes - The recipes section of the KeyValues
// representation of the schema
// pVecErrors - An optional vector that will contain error messages if
// the init fails.
// Output: True if initialization succeeded, false otherwise
//-----------------------------------------------------------------------------
bool CEconItemSchema::BInitRecipes( KeyValues *pKVRecipes, CUtlVector<CUtlString> *pVecErrors )
{
m_mapRecipes.Purge();
// initialize the rewards sections
if ( NULL != pKVRecipes )
{
int nHighestRecipeIndex = 0;
FOR_EACH_TRUE_SUBKEY( pKVRecipes, pKVRecipe )
{
int nRecipeIndex = Q_atoi( pKVRecipe->GetName() );
// Remember highest recipe index so we can make more past that
if ( nHighestRecipeIndex < nRecipeIndex + 1 )
{
nHighestRecipeIndex = nRecipeIndex + 1;
}
int nMapIndex = m_mapRecipes.Find( nRecipeIndex );
// Make sure the recipe index is correct because we use this index as a reference
SCHEMA_INIT_CHECK(
!m_mapRecipes.IsValidIndex( nMapIndex ),
CFmtStr( "Duplicate recipe definition (%d)", nRecipeIndex ) );
// Check to make sure the index is positive
SCHEMA_INIT_CHECK(
nRecipeIndex >= 0,
CFmtStr( "Recipe definition index %d must be greater than or equal to zero", nRecipeIndex ) );
CEconCraftingRecipeDefinition *recipeDef = CreateCraftingRecipeDefinition();
SCHEMA_INIT_SUBSTEP( recipeDef->BInitFromKV( pKVRecipe, *this, pVecErrors ) );
// On clients, toss out any recipes that aren't always known. (Really, the clients
// shouldn't ever get these, but just in case.)
#if defined(CLIENT_DLL) || defined(GAME_DLL)
if ( recipeDef->IsAlwaysKnown() )
#endif // !GC_DLL
{
#ifdef _DEBUG
// Sanity check in debug builds so that we know we aren't putting the same recipe in
// multiple times.
FOR_EACH_MAP_FAST( m_mapRecipes, i )
{
Assert( i != nRecipeIndex );
Assert( m_mapRecipes[i] != recipeDef );
}
#endif // _DEBUG
// Store this recipe.
m_mapRecipes.Insert( nRecipeIndex, recipeDef );
}
}
#ifdef CSTRIKE15
int nSetCount = GetItemSetCount();
for ( int i = 0; i < nSetCount; ++i )
{
const IEconItemSetDefinition *pSet = GetItemSet( i );
if ( !pSet || pSet->GetCraftReward() <= 0 )
continue;
CEconCraftingRecipeDefinition *recipeDef = CreateCraftingRecipeDefinition();
SCHEMA_INIT_SUBSTEP( recipeDef->BInitFromSet( pSet, *this, pVecErrors ) );
recipeDef->SetDefinitionIndex( nHighestRecipeIndex );
recipeDef->SetFilter( CRAFT_FILTER_COLLECT );
// Disabled for now until we ship collection badges!
recipeDef->SetDisabled( true );
m_mapRecipes.Insert( nHighestRecipeIndex, recipeDef );
nHighestRecipeIndex++;
}
#endif //#ifdef CSTRIKE15
}
return SCHEMA_INIT_SUCCESS();
}
//-----------------------------------------------------------------------------
// Purpose: Builds the name of a achievement in the form App<ID>.<AchName>
// Input: unAppID - native app ID
// pchNativeAchievementName - name of the achievement in its native app
// Returns: The combined achievement name
//-----------------------------------------------------------------------------
CUtlString CEconItemSchema::ComputeAchievementName( AppId_t unAppID, const char *pchNativeAchievementName )
{
return CFmtStr1024( "App%u.%s", unAppID, pchNativeAchievementName ).Access();
}
//-----------------------------------------------------------------------------
// Purpose: Initializes the achievement rewards section of the schema
// Input: pKVAchievementRewards - The achievement_rewards section of the KeyValues
// representation of the schema
// pVecErrors - An optional vector that will contain error messages if
// the init fails.
// Output: True if initialization succeeded, false otherwise
//-----------------------------------------------------------------------------
bool CEconItemSchema::BInitAchievementRewards( KeyValues *pKVAchievementRewards, CUtlVector<CUtlString> *pVecErrors )
{
m_dictAchievementRewards.Purge();
m_mapAchievementRewardsByData.PurgeAndDeleteElements();
// initialize the rewards sections
if ( NULL != pKVAchievementRewards )
{
FOR_EACH_SUBKEY( pKVAchievementRewards, pKVReward )
{
AchievementAward_t award;
if( pKVReward->GetDataType() == KeyValues::TYPE_NONE )
{
int32 nItemIndex = pKVReward->GetInt( "DefIndex", -1 );
if( nItemIndex != -1 )
{
award.m_vecDefIndex.AddToTail( (uint16)nItemIndex );
}
else
{
KeyValues *pkvItems = pKVReward->FindKey( "Items" );
SCHEMA_INIT_CHECK(
pkvItems != NULL,
CFmtStr( "Complex achievement %s must have an Items key or a DefIndex field", pKVReward->GetName() ) );
if( !pkvItems )
{
continue;
}
FOR_EACH_VALUE( pkvItems, pkvItem )
{
award.m_vecDefIndex.AddToTail( (uint16)Q_atoi( pkvItem->GetName() ) );
}
}
}
else
{
award.m_vecDefIndex.AddToTail( (uint16)pKVReward->GetInt("", -1 ) );
}
// make sure all the item types are valid
bool bFoundAllItems = true;
FOR_EACH_VEC( award.m_vecDefIndex, nItem )
{
const CEconItemDefinition *pDefn = GetItemDefinition( award.m_vecDefIndex[nItem] );
SCHEMA_INIT_CHECK(
pDefn != NULL,
CFmtStr( "Item definition index %d in achievement reward %s was not found", award.m_vecDefIndex[nItem], pKVReward->GetName() ) );
if( !pDefn )
{
bFoundAllItems = false;
}
}
if( !bFoundAllItems )
continue;
SCHEMA_INIT_CHECK(
award.m_vecDefIndex.Count() > 0,
CFmtStr( "Achievement reward %s has no items!", pKVReward->GetName() ) );
if( award.m_vecDefIndex.Count() == 0 )
continue;
award.m_unSourceAppId = k_uAppIdInvalid;
if( pKVReward->GetDataType() == KeyValues::TYPE_NONE )
{
// cross game achievement
award.m_sNativeName = pKVReward->GetName();
award.m_unAuditData = pKVReward->GetInt( "AuditData", 0 );
award.m_unSourceAppId = pKVReward->GetInt( "SourceAppID", award.m_unSourceAppId );
}
else
{
award.m_sNativeName = pKVReward->GetName();
award.m_unAuditData = 0;
}
AchievementAward_t *pAward = new AchievementAward_t;
*pAward = award;
m_dictAchievementRewards.Insert( ComputeAchievementName( pAward->m_unSourceAppId, pAward->m_sNativeName ), pAward );
m_mapAchievementRewardsByData.Insert( pAward->m_unAuditData, pAward );
}
}
return SCHEMA_INIT_SUCCESS();
}
#ifdef TF_CLIENT_DLL
//-----------------------------------------------------------------------------
// Purpose: Go through all items and cache the number of concrete items in each.
//-----------------------------------------------------------------------------
bool CEconItemSchema::BInitConcreteItemCounts( CUtlVector<CUtlString> *pVecErrors )
{
FOR_EACH_MAP_FAST( m_mapItems, i )
{
CEconItemDefinition *pItemDef = m_mapItems[ i ];
pItemDef->m_unNumConcreteItems = CalculateNumberOfConcreteItems( pItemDef );
}
return true;
}
#endif
//-----------------------------------------------------------------------------
// Purpose: Returns the number of actual "real" items referenced by the item definition
// (i.e. items that would take up space in the inventory)
//-----------------------------------------------------------------------------
int CEconItemSchema::CalculateNumberOfConcreteItems( const CEconItemDefinition *pItemDef )
{
AssertMsg( pItemDef, "NULL item definition! This should not happen!" );
if ( !pItemDef )
return 0;
if ( pItemDef->IsBundle() )
{
uint32 unNumConcreteItems = 0;
const bundleinfo_t *pBundle = pItemDef->GetBundleInfo();
Assert( pBundle );
FOR_EACH_VEC( pBundle->vecItemEntries, i )
{
unNumConcreteItems += CalculateNumberOfConcreteItems( GetItemDefinition( pBundle->vecItemEntries[i].m_nItemDef ) );
}
return unNumConcreteItems;
}
if ( pItemDef->GetItemClass() && !Q_strcmp( pItemDef->GetItemClass(), "map_token" ) )
return 0;
return 1;
}
bool CEconItemSchema::BInitStickerKits( KeyValues *pKVStickerKits, CUtlVector<CUtlString> *pVecErrors )
{
CStickerKit *pDefault = NULL;
FOR_EACH_TRUE_SUBKEY( pKVStickerKits, pKVEntry )
{
const char *pchName = pKVEntry->GetString( "name", "" );
if ( !pchName || pchName[ 0 ] == '\0' )
continue;
CStickerKit *pStickerKit = new CStickerKit();
if ( pStickerKit )
{
pStickerKit->nID = atoi( pKVEntry->GetName() );
if ( !pDefault )
{
if ( pStickerKit->nID == 0 )
pDefault = pStickerKit;
else
pDefault = m_mapStickerKits.Element( m_mapStickerKits.Find( 0 ) );
}
if ( !pStickerKit->InitFromKeyValues( pKVEntry, pDefault, pVecErrors ) )
{
SCHEMA_INIT_CHECK(
false,
CFmtStr( "Failed to initialize sticker kit ID %d '%s'", pStickerKit->nID, pStickerKit->sName.Get() ) );
continue;
}
if( m_mapStickerKits.Find( pStickerKit->nID ) != m_mapStickerKits.InvalidIndex() )
{
SCHEMA_INIT_CHECK(
false,
CFmtStr( "Duplicate sticker kit ID %d", pStickerKit->nID ) );
continue;
}
if( m_dictStickerKits.HasElement( pStickerKit->sName ) )
{
SCHEMA_INIT_CHECK(
false,
CFmtStr( "Duplicate sticker kit name '%s'", pStickerKit->sName.Get() ) );
continue;
}
m_mapStickerKits.Insert( pStickerKit->nID, pStickerKit );
m_dictStickerKits.Insert( pStickerKit->sName.Get(), pStickerKit );
}
}
return SCHEMA_INIT_SUCCESS();
}
bool CEconItemSchema::BInitStickerLists( KeyValues *pKVStickerLists, CUtlVector<CUtlString> *pVecErrors )
{
FOR_EACH_TRUE_SUBKEY( pKVStickerLists, pKVEntry )
{
char const *szName = pKVEntry->GetName();
if ( !szName || !*szName )
continue;
if( m_dictStickerKits.HasElement( szName ) )
{
SCHEMA_INIT_CHECK(
false,
CFmtStr( "Sticker list name duplicating sticker kit name '%s'", szName ) );
continue;
}
if( m_dictStickerLists.HasElement( szName ) )
{
SCHEMA_INIT_CHECK(
false,
CFmtStr( "Duplicate sticker list name '%s'", szName ) );
continue;
}
CStickerList *pStickerList = new CStickerList();
if ( !pStickerList->InitFromKeyValues( pKVEntry, pVecErrors ) )
{
SCHEMA_INIT_CHECK(
false,
CFmtStr( "Failed to initialize sticker list '%s'", szName ) );
continue;
}
m_dictStickerLists.Insert( szName, pStickerList );
}
return SCHEMA_INIT_SUCCESS();
}
bool CEconItemSchema::BInitPaintKits( KeyValues *pKVPaintKits, CUtlVector<CUtlString> *pVecErrors )
{
CPaintKit *pDefault = NULL;
FOR_EACH_TRUE_SUBKEY( pKVPaintKits, pKVEntry )
{
const char *pchName = pKVEntry->GetString( "name", "" );
if ( !pchName || pchName[ 0 ] == '\0' )
continue;
CPaintKit *pPaintKit = new CPaintKit();
if ( pPaintKit )
{
pPaintKit->nID = atoi( pKVEntry->GetName() );
if ( !pDefault )
{
if ( pPaintKit->nID == 0 )
pDefault = pPaintKit;
else
pDefault = m_mapPaintKits.Element( m_mapPaintKits.Find( 0 ) );
}
pPaintKit->InitFromKeyValues( pKVEntry, pDefault );
m_mapPaintKits.Insert( pPaintKit->nID, pPaintKit );
}
}
return SCHEMA_INIT_SUCCESS();
}
bool CEconItemSchema::BInitPaintKitsRarity( KeyValues *pKVPaintKitsRarity, CUtlVector<CUtlString> *pVecErrors )
{
CPaintKit *pDefault = const_cast< CPaintKit* >( GetPaintKitDefinitionByName( "default" ) );
if ( !pDefault )
{
SCHEMA_INIT_CHECK(
false,
CFmtStr( "Unable to find \"default\" paint kit in \"paint_kits_rarity\"" ) );
return false;
}
uint8 nRarity;
GetItemSchema()->BGetItemRarityFromName( pKVPaintKitsRarity->GetString( "default", "common" ), &nRarity );
pDefault->nRarity = nRarity;
FOR_EACH_SUBKEY( pKVPaintKitsRarity, pKVEntry )
{
CPaintKit *pPaintKit = const_cast< CPaintKit* >( GetPaintKitDefinitionByName( pKVEntry->GetName() ) );
if ( !pPaintKit )
continue;
const char *pchRarity = pKVEntry->GetString();
if ( pchRarity )
{
uint8 nRarity;
GetItemSchema()->BGetItemRarityFromName( pchRarity, &nRarity );
pPaintKit->nRarity = nRarity;
}
else
{
pPaintKit->nRarity = pDefault->nRarity;
}
}
return SCHEMA_INIT_SUCCESS();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CEconItemSchema::BInitMusicDefs( KeyValues *pKVMusicDefs, CUtlVector<CUtlString> *pVecErrors )
{
FOR_EACH_TRUE_SUBKEY( pKVMusicDefs, pKVMusicDef )
{
CEconMusicDefinition *pNewMusicDef = new CEconMusicDefinition;
SCHEMA_INIT_SUBSTEP( pNewMusicDef->BInitFromKV( pKVMusicDef, *this, pVecErrors ) );
if( m_mapMusicDefs.Find( pNewMusicDef->GetID() ) != m_mapMusicDefs.InvalidIndex() )
{
SCHEMA_INIT_CHECK(
false,
CFmtStr( "Duplicate music definition id %d", pNewMusicDef->GetID() ) );
continue;
}
m_mapMusicDefs.Insert( pNewMusicDef->GetID(), pNewMusicDef );
}
return SCHEMA_INIT_SUCCESS();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CEconItemSchema::BInitQuestDefs( KeyValues *pKVQuestDefs, CUtlVector<CUtlString> *pVecErrors )
{
m_mapQuestDefs.PurgeAndDeleteElements();
// initialize the Quest definitions
if ( NULL != pKVQuestDefs )
{
FOR_EACH_TRUE_SUBKEY( pKVQuestDefs, pKVQuestDef )
{
CEconQuestDefinition *pNewQuestDef = new CEconQuestDefinition;
SCHEMA_INIT_SUBSTEP( pNewQuestDef->BInitFromKV( pKVQuestDef, *this, pVecErrors ) );
if ( m_mapQuestDefs.Find( pNewQuestDef->GetID() ) != m_mapQuestDefs.InvalidIndex() )
{
SCHEMA_INIT_CHECK(
false,
CFmtStr( "Duplicate Quest definition id %d", pNewQuestDef->GetID() ) );
continue;
}
m_mapQuestDefs.Insert( pNewQuestDef->GetID(), pNewQuestDef );
}
}
return SCHEMA_INIT_SUCCESS();
}
bool CEconItemSchema::BInitQuestEvents( KeyValues *pKVQuestSchedule, CUtlVector<CUtlString> *pVecErrors )
{
m_vecQuestEvents.Purge();
// Removed for partner depot
return SCHEMA_INIT_SUCCESS();
}
const QuestEventsSchedule_t& CEconItemSchema::GetAndUpdateQuestEventsSchedule()
{
return m_mapQuestEventsSchedule;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CEconItemSchema::BInitCampaignDefs( KeyValues *pKVCampaignDefs, CUtlVector<CUtlString> *pVecErrors )
{
m_mapCampaignDefs.PurgeAndDeleteElements();
// initialize the Campaign definitions
if ( NULL != pKVCampaignDefs )
{
FOR_EACH_TRUE_SUBKEY( pKVCampaignDefs, pKVCampaignDef )
{
CEconCampaignDefinition *pNewCampaignDef = new CEconCampaignDefinition;
SCHEMA_INIT_SUBSTEP( pNewCampaignDef->BInitFromKV( pKVCampaignDef, *this, pVecErrors ) );
if ( m_mapCampaignDefs.Find( pNewCampaignDef->GetID() ) != m_mapCampaignDefs.InvalidIndex() )
{
SCHEMA_INIT_CHECK(
false,
CFmtStr( "Duplicate Campaign definition id %d", pNewCampaignDef->GetID() ) );
continue;
}
m_mapCampaignDefs.Insert( pNewCampaignDef->GetID(), pNewCampaignDef );
}
}
return SCHEMA_INIT_SUCCESS();
}
attachedparticlesystem_t CEconItemSchema::GetAttachedParticleSystemInfo( KeyValues* pKVEntry, int32 nItemIndex ) const
{
attachedparticlesystem_t system;
system.pEffectKV = pKVEntry;
system.pszResourceName = pKVEntry->GetString( "resource", NULL );
system.pszSystemName = pKVEntry->GetString( "system", NULL );
system.pszAttachmentName = pKVEntry->GetString( "attachment", NULL );
system.bFollowRootBone = pKVEntry->GetInt( "attach_to_rootbone", 0 ) != 0;
system.bFlyingCourier = pKVEntry->GetInt( "flying_courier_effect", 0 ) != 0;
system.iCustomType = pKVEntry->GetInt( "custom_type", 0 );
system.iCount = 0;
system.nSystemID = nItemIndex;
system.nRootAttachType = StringFieldToInt( pKVEntry->GetString("attach_type"), g_szParticleAttachTypes, ARRAYSIZE(g_szParticleAttachTypes) );
int iAttachToEnt = StringFieldToInt( pKVEntry->GetString("attach_entity"), g_szParticleAttachToEnt, ARRAYSIZE(g_szParticleAttachToEnt) );
system.nAttachToEntity = ( iAttachToEnt == -1 ) ? ATTPART_TO_SELF : (attachedparticle_toent_t)iAttachToEnt;
// Find any control point settings
KeyValues *pKVControlPoints = pKVEntry->FindKey( "control_points" );
if ( pKVControlPoints )
{
FOR_EACH_TRUE_SUBKEY( pKVControlPoints, pKVPoint )
{
int iIdx = system.vecControlPoints.AddToTail();
system.vecControlPoints[iIdx].nControlPoint = pKVPoint->GetInt( "control_point_index", -1 );
system.vecControlPoints[iIdx].nAttachType = StringFieldToInt( pKVPoint->GetString("attach_type"), g_szParticleAttachTypes, ARRAYSIZE(g_szParticleAttachTypes) );
system.vecControlPoints[iIdx].pszAttachmentName = pKVPoint->GetString( "attachment", NULL );
float flVec[3];
V_StringToVector( flVec, pKVPoint->GetString( "position", "0 0 0" ) );
system.vecControlPoints[iIdx].vecPosition = Vector( flVec[0], flVec[1], flVec[2] );
}
}
return system;
}
//-----------------------------------------------------------------------------
// Purpose: Initializes the attribute-controlled-particle-systems section of the schema
//-----------------------------------------------------------------------------
bool CEconItemSchema::BInitAttributeControlledParticleSystems( KeyValues *pKVParticleSystems, CUtlVector<CUtlString> *pVecErrors )
{
m_mapAttributeControlledParticleSystems.Purge();
if ( NULL != pKVParticleSystems )
{
FOR_EACH_TRUE_SUBKEY( pKVParticleSystems, pKVEntry )
{
int32 nItemIndex = atoi( pKVEntry->GetName() );
// Check to make sure the index is positive
SCHEMA_INIT_CHECK(
nItemIndex > 0,
CFmtStr( "Particle system index %d greater than zero", nItemIndex ) );
if ( nItemIndex <= 0 )
continue;
int iIndex = m_mapAttributeControlledParticleSystems.Insert( nItemIndex );
attachedparticlesystem_t *system = &m_mapAttributeControlledParticleSystems[iIndex];
*system = GetAttachedParticleSystemInfo( pKVEntry, nItemIndex );
}
}
return SCHEMA_INIT_SUCCESS();
}
//-----------------------------------------------------------------------------
// Purpose: Inits data for items that can level up through kills, etc.
//-----------------------------------------------------------------------------
bool CEconItemSchema::BInitItemLevels( KeyValues *pKVItemLevels, CUtlVector<CUtlString> *pVecErrors )
{
m_vecItemLevelingData.RemoveAll();
// initialize the rewards sections
if ( NULL != pKVItemLevels )
{
FOR_EACH_TRUE_SUBKEY( pKVItemLevels, pKVItemLevelBlock )
{
const char *pszLevelBlockName = pKVItemLevelBlock->GetName();
SCHEMA_INIT_CHECK( GetItemLevelingData( pszLevelBlockName ) == NULL,
CFmtStr( "Duplicate leveling data block named \"%s\".", pszLevelBlockName ) );
// Allocate a new structure for this block and assign it. We'll fill in the contents later.
CUtlVector<CItemLevelingDefinition> *pLevelingData = new CUtlVector<CItemLevelingDefinition>;
m_vecItemLevelingData.Insert( pszLevelBlockName, pLevelingData );
FOR_EACH_TRUE_SUBKEY( pKVItemLevelBlock, pKVItemLevel )
{
int index = pLevelingData->AddToTail();
SCHEMA_INIT_SUBSTEP( (*pLevelingData)[index].BInitFromKV( pKVItemLevel, *this, pszLevelBlockName, pVecErrors ) );
}
}
}
return SCHEMA_INIT_SUCCESS();
}
//-----------------------------------------------------------------------------
// Purpose: Inits data for kill eater types.
//-----------------------------------------------------------------------------
bool CEconItemSchema::BInitKillEaterScoreTypes( KeyValues *pKVKillEaterScoreTypes, CUtlVector<CUtlString> *pVecErrors )
{
m_mapKillEaterScoreTypes.RemoveAll();
// initialize the rewards sections
if ( NULL != pKVKillEaterScoreTypes )
{
FOR_EACH_TRUE_SUBKEY( pKVKillEaterScoreTypes, pKVScoreType )
{
unsigned int unIndex = (unsigned int)atoi( pKVScoreType->GetName() );
SCHEMA_INIT_CHECK( m_mapKillEaterScoreTypes.Find( unIndex ) == KillEaterScoreMap_t::InvalidIndex(),
CFmtStr( "Duplicate kill eater score type index %u.", unIndex ) );
kill_eater_score_type_t ScoreType;
ScoreType.m_nValue = V_atoi( pKVScoreType->GetName() );
ScoreType.m_pszTypeString = pKVScoreType->GetString( "type_name" );
ScoreType.m_pszModelAttributeString = pKVScoreType->GetString( "model_attribute" );
#ifdef CLIENT_DLL
HelperValidateLocalizationStringToken( CFmtStr( "#KillEaterDescriptionNotice_%s", ScoreType.m_pszTypeString ) );
HelperValidateLocalizationStringToken( CFmtStr( "#KillEaterEventType_%s", ScoreType.m_pszTypeString ) );
#endif
// Default to on.
ScoreType.m_bUseLevelBlock = pKVScoreType->GetBool( "use_level_data", 1 );
const char *pszLevelBlockName = pKVScoreType->GetString( "level_data", "KillEaterRank" );
SCHEMA_INIT_CHECK( GetItemLevelingData( pszLevelBlockName ) != NULL,
CFmtStr( "Unable to find leveling data block named \"%s\" for kill eater score type %u.", pszLevelBlockName, unIndex ) );
ScoreType.m_pszLevelBlockName = pszLevelBlockName;
m_mapKillEaterScoreTypes.Insert( unIndex, ScoreType );
}
}
return SCHEMA_INIT_SUCCESS();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
const ISchemaAttributeType *CEconItemSchema::GetAttributeType( const char *pszAttrTypeName ) const
{
FOR_EACH_VEC( m_vecAttributeTypes, i )
{
if ( m_vecAttributeTypes[i].m_sName == pszAttrTypeName )
return m_vecAttributeTypes[i].m_pAttrType;
}
return NULL;
}
//-----------------------------------------------------------------------------
// CItemLevelingDefinition Accessor
//-----------------------------------------------------------------------------
const CItemLevelingDefinition *CEconItemSchema::GetItemLevelForScore( const char *pszLevelBlockName, uint32 unScore ) const
{
const CUtlVector<CItemLevelingDefinition> *pLevelingData = GetItemLevelingData( pszLevelBlockName );
if ( !pLevelingData )
return NULL;
if ( pLevelingData->Count() == 0 )
return NULL;
FOR_EACH_VEC( (*pLevelingData), i )
{
if ( unScore < (*pLevelingData)[i].GetRequiredScore() )
return &(*pLevelingData)[i];
}
return &(*pLevelingData).Tail();
}
//-----------------------------------------------------------------------------
// Kill eater score type accessor
//-----------------------------------------------------------------------------
const kill_eater_score_type_t *CEconItemSchema::FindKillEaterScoreType( uint32 unScoreType ) const
{
KillEaterScoreMap_t::IndexType_t i = m_mapKillEaterScoreTypes.Find( unScoreType );
if ( i == KillEaterScoreMap_t::InvalidIndex() )
return NULL;
return &m_mapKillEaterScoreTypes[i];
}
//-----------------------------------------------------------------------------
// Kill eater score type accessor
//-----------------------------------------------------------------------------
const char *CEconItemSchema::GetKillEaterScoreTypeLocString( uint32 unScoreType ) const
{
const kill_eater_score_type_t *pScoreType = FindKillEaterScoreType( unScoreType );
return pScoreType
? pScoreType->m_pszTypeString
: NULL;
}
//-----------------------------------------------------------------------------
// Kill eater score type accessor for "use_level_data"
//-----------------------------------------------------------------------------
bool CEconItemSchema::GetKillEaterScoreTypeUseLevelData( uint32 unScoreType ) const
{
const kill_eater_score_type_t *pScoreType = FindKillEaterScoreType( unScoreType );
return pScoreType ? pScoreType->m_bUseLevelBlock : false;
}
//-----------------------------------------------------------------------------
// Kill eater score type accessor
//-----------------------------------------------------------------------------
const char *CEconItemSchema::GetKillEaterScoreTypeLevelingDataName( uint32 unScoreType ) const
{
const kill_eater_score_type_t *pScoreType = FindKillEaterScoreType( unScoreType );
return pScoreType
? pScoreType->m_pszLevelBlockName
: NULL;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
econ_tag_handle_t CEconItemSchema::GetHandleForTag( const char *pszTagName )
{
EconTagDict_t::IndexType_t i = m_dictTags.Find( pszTagName );
if ( m_dictTags.IsValidIndex( i ) )
return i;
return m_dictTags.Insert( pszTagName );
}
#if defined(CLIENT_DLL) || defined(GAME_DLL)
//-----------------------------------------------------------------------------
// Purpose: Clones the specified item definition, and returns the new item def.
//-----------------------------------------------------------------------------
void CEconItemSchema::ItemTesting_CreateTestDefinition( int iCloneFromItemDef, int iNewDef, KeyValues *pNewKV )
{
int nMapIndex = m_mapItems.Find( iNewDef );
if ( !m_mapItems.IsValidIndex( nMapIndex ) )
{
nMapIndex = m_mapItems.Insert( iNewDef, CreateEconItemDefinition() );
m_mapItemsSorted.Insert( iNewDef, m_mapItems[nMapIndex] );
}
// Find & copy the clone item def's data in
const CEconItemDefinition *pCloneDef = GetItemDefinition( iCloneFromItemDef );
if ( !pCloneDef )
return;
m_mapItems[nMapIndex]->CopyPolymorphic( pCloneDef );
// Then stomp it with the KV test contents
m_mapItems[nMapIndex]->BInitFromTestItemKVs( iNewDef, pNewKV, *this );
}
//-----------------------------------------------------------------------------
// Purpose: Discards the specified item definition
//-----------------------------------------------------------------------------
void CEconItemSchema::ItemTesting_DiscardTestDefinition( int iDef )
{
m_mapItems.Remove( iDef );
m_mapItemsSorted.Remove( iDef );
}
//-----------------------------------------------------------------------------
// Purpose: Initializes the armory data section of the schema
//-----------------------------------------------------------------------------
bool CEconItemSchema::BInitArmoryData( KeyValues *pKVArmoryData, CUtlVector<CUtlString> *pVecErrors )
{
m_dictArmoryItemDataStrings.Purge();
m_dictArmoryAttributeDataStrings.Purge();
if ( NULL != pKVArmoryData )
{
KeyValues *pKVItemTypes = pKVArmoryData->FindKey( "armory_item_types" );
if ( pKVItemTypes )
{
FOR_EACH_SUBKEY( pKVItemTypes, pKVEntry )
{
const char *pszDataKey = pKVEntry->GetName();
const char *pszLocString = pKVEntry->GetString();
m_dictArmoryItemTypesDataStrings.Insert( pszDataKey, pszLocString );
}
}
pKVItemTypes = pKVArmoryData->FindKey( "armory_item_classes" );
if ( pKVItemTypes )
{
FOR_EACH_SUBKEY( pKVItemTypes, pKVEntry )
{
const char *pszDataKey = pKVEntry->GetName();
const char *pszLocString = pKVEntry->GetString();
m_dictArmoryItemClassesDataStrings.Insert( pszDataKey, pszLocString );
}
}
KeyValues *pKVAttribs = pKVArmoryData->FindKey( "armory_attributes" );
if ( pKVAttribs )
{
FOR_EACH_SUBKEY( pKVAttribs, pKVEntry )
{
const char *pszDataKey = pKVEntry->GetName();
const char *pszLocString = pKVEntry->GetString();
m_dictArmoryAttributeDataStrings.Insert( pszDataKey, pszLocString );
}
}
KeyValues *pKVItems = pKVArmoryData->FindKey( "armory_items" );
if ( pKVItems )
{
FOR_EACH_SUBKEY( pKVItems, pKVEntry )
{
const char *pszDataKey = pKVEntry->GetName();
const char *pszLocString = pKVEntry->GetString();
m_dictArmoryItemDataStrings.Insert( pszDataKey, pszLocString );
}
}
}
return SCHEMA_INIT_SUCCESS();
}
#endif
//-----------------------------------------------------------------------------
// Purpose: Returns the achievement award that matches the provided defindex or NULL
// if there is no such award.
// Input: unData - The data field that would be stored in ItemAudit
//-----------------------------------------------------------------------------
const AchievementAward_t *CEconItemSchema::GetAchievementRewardByDefIndex( uint16 usDefIndex ) const
{
FOR_EACH_MAP_FAST( m_mapAchievementRewardsByData, nIndex )
{
if( m_mapAchievementRewardsByData[nIndex]->m_vecDefIndex.HasElement( usDefIndex ) )
return m_mapAchievementRewardsByData[nIndex];
}
return NULL;
}
//-----------------------------------------------------------------------------
// Purpose: Gets a rarity value for a name.
//-----------------------------------------------------------------------------
bool CEconItemSchema::BGetItemRarityFromName( const char *pchName, uint8 *nRarity ) const
{
if ( 0 == Q_stricmp( "any", pchName ) )
{
*nRarity = k_unItemRarity_Any;
return true;
}
FOR_EACH_MAP_FAST( m_mapRarities, i )
{
if ( 0 == Q_stricmp( m_mapRarities[i].GetName(), pchName ) )
{
*nRarity = m_mapRarities[i].GetDBValue();
return true;
}
}
return false;
}
//-----------------------------------------------------------------------------
// Purpose: Gets a quality value for a name.
// Input: pchName - The name to translate.
// nQuality - (out)The quality number for this name, if found.
// Output: True if the string matched a quality for this schema, false otherwise.
//-----------------------------------------------------------------------------
bool CEconItemSchema::BGetItemQualityFromName( const char *pchName, uint8 *nQuality ) const
{
if ( 0 == Q_stricmp( "any", pchName ) )
{
*nQuality = k_unItemQuality_Any;
return true;
}
FOR_EACH_MAP_FAST( m_mapQualities, i )
{
if ( 0 == Q_stricmp( m_mapQualities[i].GetName(), pchName ) )
{
*nQuality = m_mapQualities[i].GetDBValue();
return true;
}
}
return false;
}
//-----------------------------------------------------------------------------
// Purpose: Gets a quality definition for an index
// Input: nQuality - The quality to get.
// Output: A pointer to the desired definition, or NULL if it is not found.
//-----------------------------------------------------------------------------
const CEconItemQualityDefinition *CEconItemSchema::GetQualityDefinition( int nQuality ) const
{
int iIndex = m_mapQualities.Find( nQuality );
if ( m_mapQualities.IsValidIndex( iIndex ) )
return &m_mapQualities[iIndex];
return NULL;
}
const CEconItemQualityDefinition *CEconItemSchema::GetQualityDefinitionByName( const char *pszDefName ) const
{
FOR_EACH_MAP_FAST( m_mapQualities, i )
{
if ( !strcmp( pszDefName, m_mapQualities[i].GetName()) )
return &m_mapQualities[i];
}
return NULL;
}
const char* CEconItemSchema::GetQualityName( uint8 iQuality )
{
const CEconItemQualityDefinition* pItemQuality = GetQualityDefinition( iQuality );
if ( !pItemQuality )
return NULL;
else
return pItemQuality->GetName();
}
int CEconItemSchema::GetQualityIndex( const char* pszQuality )
{
const CEconItemQualityDefinition* pItemQuality = GetQualityDefinitionByName( pszQuality );
if ( pItemQuality )
return pItemQuality->GetDBValue();
else
return 0;
}
const CEconItemRarityDefinition *CEconItemSchema::GetRarityDefinitionByMapIndex( int nRarityIndex ) const
{
if ( m_mapRarities.IsValidIndex( nRarityIndex ) )
return &m_mapRarities[nRarityIndex];
return NULL;
}
const CEconItemRarityDefinition *CEconItemSchema::GetRarityDefinition( int nRarity ) const
{
int iIndex = m_mapRarities.Find( nRarity );
if ( m_mapRarities.IsValidIndex( iIndex ) )
return &m_mapRarities[iIndex];
return NULL;
}
const CEconItemRarityDefinition *CEconItemSchema::GetRarityDefinitionByName( const char *pszDefName ) const
{
FOR_EACH_MAP_FAST( m_mapRarities, i )
{
if ( !strcmp( pszDefName, m_mapRarities[i].GetName()) )
return &m_mapRarities[i];
}
return NULL;
}
const char* CEconItemSchema::GetRarityName( uint8 iRarity )
{
const CEconItemRarityDefinition* pItemRarity = GetRarityDefinition( iRarity );
if ( !pItemRarity )
return NULL;
else
return pItemRarity->GetName();
}
const char* CEconItemSchema::GetRarityLocKey( uint8 iRarity )
{
const CEconItemRarityDefinition* pItemRarity = GetRarityDefinition( iRarity );
if ( !pItemRarity )
return NULL;
else
return pItemRarity->GetLocKey();
}
const char* CEconItemSchema::GetRarityColor( uint8 iRarity )
{
const CEconItemRarityDefinition* pItemRarity = GetRarityDefinition( iRarity );
if ( !pItemRarity )
return NULL;
else
return GetHexColorForAttribColor( pItemRarity->GetAttribColor() );
}
const char* CEconItemSchema::GetRarityLootList( uint8 iRarity )
{
const CEconItemRarityDefinition* pItemRarity = GetRarityDefinition( iRarity );
if ( !pItemRarity )
return NULL;
else
return pItemRarity->GetLootList();
}
int CEconItemSchema::GetRarityIndex( const char* pszRarity )
{
const CEconItemRarityDefinition* pRarity = GetRarityDefinitionByName( pszRarity );
if ( pRarity )
return pRarity->GetDBValue();
else
return 0;
}
const CEconSoundMaterialDefinition *CEconItemSchema::GetSoundMaterialDefinitionByID( int nSoundMaterialID ) const
{
int iIndex = m_mapSoundMaterials.Find( nSoundMaterialID );
if ( m_mapSoundMaterials.IsValidIndex( iIndex ) )
return &m_mapSoundMaterials[iIndex];
return NULL;
}
const CEconSoundMaterialDefinition *CEconItemSchema::GetSoundMaterialDefinitionByName( const char *pszSoundMaterialName ) const
{
FOR_EACH_MAP_FAST( m_mapSoundMaterials, i )
{
if ( !strcmp( pszSoundMaterialName, m_mapSoundMaterials[i].GetName()) )
return &m_mapSoundMaterials[i];
}
return NULL;
}
const char* CEconItemSchema::GetSoundMaterialNameByID( int nSoundMaterialID )
{
const CEconSoundMaterialDefinition* pSoundMaterial = GetSoundMaterialDefinitionByID( nSoundMaterialID );
if ( !pSoundMaterial )
return NULL;
else
return pSoundMaterial->GetName();
}
int CEconItemSchema::GetSoundMaterialID( const char* pszSoundMaterial )
{
const CEconSoundMaterialDefinition* pSoundMaterial = GetSoundMaterialDefinitionByName( pszSoundMaterial );
if ( pSoundMaterial )
return pSoundMaterial->GetID();
else
return 0;
}
int CEconItemSchema::GetSoundMaterialIDByIndex( int nIndex )
{
// 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.
FOR_EACH_MAP( m_mapSoundMaterials, i )
{
nIndex--;
if ( nIndex < 0 )
{
return m_mapSoundMaterials[i].GetID();
}
}
return -1;
}
//-----------------------------------------------------------------------------
// Purpose: Gets an item definition for the specified map index
//-----------------------------------------------------------------------------
const CEconItemDefinition *CEconItemSchema::GetItemDefinitionByMapIndex( int iMapIndex ) const
{
if ( m_mapItems.IsValidIndex( iMapIndex ) )
return m_mapItems[iMapIndex];
return NULL;
}
//-----------------------------------------------------------------------------
// Purpose: Gets an item definition for the specified definition index
// Input: iItemIndex - The index of the desired definition.
// Output: A pointer to the desired definition, or NULL if it is not found.
//-----------------------------------------------------------------------------
CEconItemDefinition *CEconItemSchema::GetItemDefinitionMutable( int iItemIndex, bool bNoDefault )
{
#if defined(CLIENT_DLL) || defined(GAME_DLL)
#if !defined(CSTRIKE_DLL)
AssertMsg( m_pDefaultItemDefinition, "No default item definition set up for item schema." );
#endif // CSTRIKE_DLL
#endif // defined(CLIENT_DLL) || defined(GAME_DLL)
int iIndex = m_mapItems.Find( iItemIndex );
if ( m_mapItems.IsValidIndex( iIndex ) )
return m_mapItems[iIndex];
if ( bNoDefault )
return NULL;
if ( m_pDefaultItemDefinition )
return m_pDefaultItemDefinition;
#if !defined(CSTRIKE_DLL)
// We shouldn't ever get down here, but all the same returning a valid pointer is very slightly
// a better plan than returning an invalid pointer to code that won't check to see if it's valid.
static CEconItemDefinition *s_pEmptyDefinition = CreateEconItemDefinition();
return s_pEmptyDefinition;
#else
return NULL;
#endif // CSTRIKE_DLL
}
const CEconItemDefinition *CEconItemSchema::GetItemDefinition( int iItemIndex, bool bNoDefault ) const
{
return const_cast<CEconItemSchema *>(this)->GetItemDefinitionMutable( iItemIndex, bNoDefault );
}
//-----------------------------------------------------------------------------
// Purpose: Gets an item definition that has a name matching the specified name.
// Input: pszDefName - The name of the desired definition.
// Output: A pointer to the desired definition, or NULL if it is not found.
//-----------------------------------------------------------------------------
CEconItemDefinition *CEconItemSchema::GetItemDefinitionByName( const char *pszDefName )
{
SNPROF(__PRETTY_FUNCTION__);
if ( m_bSchemaParsingItems )
{
AssertMsg( 0, "GetItemDefinitionByName while parsing item definitions. This is not a valid operation." );
return NULL;
}
// This shouldn't happen, but let's not crash if it ever does.
Assert( pszDefName != NULL );
if ( pszDefName == NULL )
return NULL;
FOR_EACH_MAP_FAST( m_mapItems, i )
{
if ( !strcmp( pszDefName, m_mapItems[i]->GetDefinitionName()) )
return m_mapItems[i];
}
return NULL;
}
const CEconItemDefinition *CEconItemSchema::GetItemDefinitionByName( const char *pszDefName ) const
{
return const_cast<CEconItemSchema *>(this)->GetItemDefinitionByName( pszDefName );
}
//-----------------------------------------------------------------------------
// Purpose: Gets an attribute definition for an index
// Input: iAttribIndex - The index of the desired definition.
// Output: A pointer to the desired definition, or NULL if it is not found.
//-----------------------------------------------------------------------------
const CEconItemAttributeDefinition *CEconItemSchema::GetAttributeDefinition( int iAttribIndex ) const
{
if ( m_mapAttributesContainer.IsValidIndex( iAttribIndex ) )
return m_mapAttributesContainer[iAttribIndex];
return NULL;
}
const CEconItemAttributeDefinition *CEconItemSchema::GetAttributeDefinitionByName( const char *pszDefName ) const
{
VPROF_BUDGET( "CEconItemSchema::GetAttributeDefinitionByName", VPROF_BUDGETGROUP_STEAM );
SNPROF(__PRETTY_FUNCTION__);
FOR_EACH_VEC( m_mapAttributesContainer, i )
{
if ( m_mapAttributesContainer[i] && !strcmp( pszDefName, m_mapAttributesContainer[i]->GetDefinitionName()) )
return m_mapAttributesContainer[i];
}
return NULL;
}
//-----------------------------------------------------------------------------
// Purpose: Gets a recipe definition for an index
// Input: iRecipeIndex - The index of the desired definition.
// Output: A pointer to the desired definition, or NULL if it is not found.
//-----------------------------------------------------------------------------
const CEconCraftingRecipeDefinition *CEconItemSchema::GetRecipeDefinition( int iRecipeIndex ) const
{
int iIndex = m_mapRecipes.Find( iRecipeIndex );
if ( m_mapRecipes.IsValidIndex( iIndex ) )
return m_mapRecipes[iIndex];
return NULL;
}
//-----------------------------------------------------------------------------
void CEconItemSchema::AddStickerKitDefinition( int iStickerKitID, CStickerKit *pStickerKit )
{
int iIndex = m_mapStickerKits.Find( iStickerKitID );
if ( m_mapStickerKits.IsValidIndex( iIndex ) )
{
m_mapStickerKits.Remove( iStickerKitID );
}
m_mapStickerKits.Insert( iStickerKitID, pStickerKit );
}
void CEconItemSchema::RemoveStickerKitDefinition( int iStickerKitID )
{
m_mapStickerKits.Remove( iStickerKitID );
}
const CStickerKit *CEconItemSchema::GetStickerKitDefinition( int iStickerKitID ) const
{
int iIndex = m_mapStickerKits.Find( iStickerKitID );
if ( m_mapStickerKits.IsValidIndex( iIndex ) )
return m_mapStickerKits[iIndex];
return NULL;
}
const CStickerKit *CEconItemSchema::GetStickerKitDefinitionByMapIndex( int iMapIndex )
{
if ( m_mapStickerKits.IsValidIndex( iMapIndex ) )
return m_mapStickerKits[ iMapIndex ];
return NULL;
}
const CStickerKit *CEconItemSchema::GetStickerKitDefinitionByName( const char *pchName ) const
{
int idx = m_dictStickerKits.Find( pchName );
if ( idx != m_dictStickerKits.InvalidIndex() )
return m_dictStickerKits.Element( idx );
else
return NULL;
}
const CStickerList *CEconItemSchema::GetStickerListDefinitionByName( const char *pchName ) const
{
int idx = m_dictStickerLists.Find( pchName );
if ( idx != m_dictStickerLists.InvalidIndex() )
return m_dictStickerLists.Element( idx );
else
return NULL;
}
const CEconMusicDefinition *CEconItemSchema::GetMusicKitDefinitionByName( const char *pchName ) const
{
FOR_EACH_MAP_FAST( m_mapMusicDefs, i )
{
if ( !V_strcmp( m_mapMusicDefs.Element( i )->GetName(), pchName ) )
{
return m_mapMusicDefs.Element( i );
}
}
return NULL;
}
//-----------------------------------------------------------------------------
void CEconItemSchema::AddPaintKitDefinition( int iPaintKitID, CPaintKit *pPaintKit )
{
int iIndex = m_mapPaintKits.Find( iPaintKitID );
if ( m_mapPaintKits.IsValidIndex( iIndex ) )
{
m_mapPaintKits.Remove( iPaintKitID );
}
m_mapPaintKits.Insert( iPaintKitID, pPaintKit );
}
void CEconItemSchema::RemovePaintKitDefinition( int iPaintKitID )
{
m_mapPaintKits.Remove( iPaintKitID );
}
const CPaintKit *CEconItemSchema::GetPaintKitDefinition( int iPaintKitID ) const
{
int iIndex = m_mapPaintKits.Find( iPaintKitID );
if ( m_mapPaintKits.IsValidIndex( iIndex ) )
return m_mapPaintKits[iIndex];
return NULL;
}
const CPaintKit *CEconItemSchema::GetPaintKitDefinitionByMapIndex( int iMapIndex )
{
if ( m_mapPaintKits.IsValidIndex( iMapIndex ) )
return m_mapPaintKits[ iMapIndex ];
return NULL;
}
const CPaintKit *CEconItemSchema::GetPaintKitDefinitionByName( const char *pchName ) const
{
FOR_EACH_MAP_FAST( m_mapPaintKits, i )
{
CPaintKit *pPaintKit = m_mapPaintKits[ i ];
if ( V_strcmp( pPaintKit->sName.Access(), pchName ) == 0 )
{
return pPaintKit;
}
}
return NULL;
}
const unsigned int CEconItemSchema::GetPaintKitCount() const
{
return m_mapPaintKits.Count();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
const CEconColorDefinition *CEconItemSchema::GetColorDefinitionByName( const char *pszDefName ) const
{
FOR_EACH_VEC( m_vecColorDefs, i )
{
if ( !Q_stricmp( m_vecColorDefs[i]->GetName(), pszDefName ) )
return m_vecColorDefs[i];
}
return NULL;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
const CEconGraffitiTintDefinition * CEconItemSchema::GetGraffitiTintDefinitionByID( int nID ) const
{
return m_vecGraffitiTintDefs.IsValidIndex( nID ) ? m_vecGraffitiTintDefs[ nID ] : NULL;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
const CEconGraffitiTintDefinition * CEconItemSchema::GetGraffitiTintDefinitionByName( const char *pszDefName ) const
{
UtlSymId_t utlsym = m_mapGraffitiTintByName.Find( pszDefName );
return ( utlsym != UTL_INVAL_SYMBOL ) ? m_mapGraffitiTintByName[ utlsym ] : NULL;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
const CEconMusicDefinition *CEconItemSchema::GetMusicDefinition( uint32 unMusicID ) const
{
int iIndex = m_mapMusicDefs.Find( unMusicID );
if ( m_mapMusicDefs.IsValidIndex( iIndex ) )
return m_mapMusicDefs[iIndex];
return NULL;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CEconQuestDefinition *CEconItemSchema::GetQuestDefinition( uint32 unQuestID ) const
{
int iIndex = m_mapQuestDefs.Find( unQuestID );
if ( m_mapQuestDefs.IsValidIndex( iIndex ) )
return m_mapQuestDefs[ iIndex ];
return NULL;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CEconCampaignDefinition *CEconItemSchema::GetCampaignDefinition( uint32 unCampaignID ) const
{
int iIndex = m_mapCampaignDefs.Find( unCampaignID );
if ( m_mapCampaignDefs.IsValidIndex( iIndex ) )
return m_mapCampaignDefs[ iIndex ];
return NULL;
}
//-----------------------------------------------------------------------------
// Purpose: Return the attribute specified attachedparticlesystem_t* associated with the given id.
//-----------------------------------------------------------------------------
attachedparticlesystem_t* CEconItemSchema::GetAttributeControlledParticleSystem( int id )
{
int iIndex = m_mapAttributeControlledParticleSystems.Find( id );
if ( m_mapAttributeControlledParticleSystems.IsValidIndex( iIndex ) )
return &m_mapAttributeControlledParticleSystems[iIndex];
return NULL;
}
attachedparticlesystem_t* CEconItemSchema::GetAttributeControlledParticleSystemByIndex( int id )
{
return &m_mapAttributeControlledParticleSystems[id];
}
attachedparticlesystem_t* CEconItemSchema::FindAttributeControlledParticleSystem( const char *pchSystemName, int *outID )
{
FOR_EACH_MAP_FAST( m_mapAttributeControlledParticleSystems, nSystem )
{
if( !Q_stricmp( m_mapAttributeControlledParticleSystems[nSystem].pszSystemName, pchSystemName ) )
{
if ( outID )
{
*outID = nSystem;
}
return &m_mapAttributeControlledParticleSystems[nSystem];
}
}
if ( outID )
{
*outID = -1;
}
return NULL;
}
bool CEconItemSchema::BPostSchemaInitStartupChecks( CUtlVector<CUtlString> *pVecErrors )
{
#if defined( GC_DLL ) || defined( CLIENT_DLL )
// confirm that all stickers that reference players have valid link
FOR_EACH_MAP_FAST( m_mapStickerKits, i )
{
CStickerKit * pStickerKit = m_mapStickerKits.Element( i );
if ( !pStickerKit->m_nPlayerID ) continue;
SCHEMA_INIT_CHECK(
( m_mapProPlayersByAccountID.Find( pStickerKit->m_nPlayerID ) != m_mapProPlayersByAccountID.InvalidIndex() ),
CFmtStr( "Pro-player sticker %d reference player %u which doesn't have a matching pro-player entry", pStickerKit->nID, pStickerKit->m_nPlayerID ) );
}
#endif // #if defined( GC_DLL ) || defined( CLIENT_DLL )
#ifdef CLIENT_DLL
//// Confirm that every story block expression references only existing quest indices.
FOR_EACH_MAP_FAST( m_mapCampaignDefs, iC )
{
FOR_EACH_MAP_FAST( m_mapCampaignDefs.Element( iC )->GetCampaignNodes(), iCn )
{
CEconCampaignDefinition::CEconCampaignNodeDefinition * pCNodeDef = m_mapCampaignDefs.Element( iC )->GetCampaignNodes().Element( iCn );
FOR_EACH_VEC( pCNodeDef->GetStoryBlocks(), iSB )
{
KeyValues * pKVExpressionTokens = new KeyValues( "ExpressionTokens" );
KeyValues::AutoDelete autodelete( pKVExpressionTokens );
CEconQuestDefinition::TokenizeQuestExpression( pCNodeDef->GetStoryBlocks()[ iSB ]->GetStoryBlockExpression(), pKVExpressionTokens );
FOR_EACH_SUBKEY( pKVExpressionTokens, kvSubKey )
{
int iQ = m_mapQuestDefs.Find( V_atoi( kvSubKey->GetName() ) );
SCHEMA_INIT_CHECK(
( m_mapQuestDefs.IsValidIndex( iQ ) ),
CFmtStr( "campaign %d, node %d, Story_block %d, references a non-existant quest index %d.", iC, iCn, iSB, iQ ) );
}
}
}
}
// confirm that all quest expressions are valid
FOR_EACH_MAP_FAST( m_mapQuestDefs, i )
{
CEconQuestDefinition * pQuestDef = m_mapQuestDefs.Element( i );
SCHEMA_INIT_CHECK(
( CEconQuestDefinition::IsQuestExpressionValid( pQuestDef->GetQuestExpression() ) ),
CFmtStr( "Quest definition %s specifies an expression that does not evaluate.", pQuestDef->GetName() ) );
if ( pQuestDef->GetQuestBonusExpression() && pQuestDef->GetQuestBonusExpression()[ 0 ] )
{
SCHEMA_INIT_CHECK(
( CEconQuestDefinition::IsQuestExpressionValid( pQuestDef->GetQuestBonusExpression() ) ),
CFmtStr( "Quest definition %s specifies a bonus expression that does not evaluate.", pQuestDef->GetName() ) );
}
}
#endif // CLIENT_DLL
return true;
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
CItemLevelingDefinition::CItemLevelingDefinition( void )
{
}
//-----------------------------------------------------------------------------
// Purpose: Copy constructor
//-----------------------------------------------------------------------------
CItemLevelingDefinition::CItemLevelingDefinition( const CItemLevelingDefinition &that )
{
(*this) = that;
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
CItemLevelingDefinition::~CItemLevelingDefinition()
{
// Free up strdup() memory.
free( m_pszLocalizedName_LocalStorage );
CUtlVector< CUtlString > vecErrors;
bool bSuccess = GEconItemSchema().BInit( "scripts/items/unencrypted/items_master.txt", "MOD", &vecErrors );
if( !bSuccess )
{
FOR_EACH_VEC( vecErrors, nError )
{
Msg( "%s", vecErrors[nError].String() );
}
}
}
//-----------------------------------------------------------------------------
// Purpose: Operator=
//-----------------------------------------------------------------------------
CItemLevelingDefinition &CItemLevelingDefinition::operator=( const CItemLevelingDefinition &other )
{
m_unLevel = other.m_unLevel;
m_unRequiredScore = other.m_unRequiredScore;
m_pszLocalizedName_LocalStorage = strdup( other.m_pszLocalizedName_LocalStorage );
return *this;
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
bool CItemLevelingDefinition::BInitFromKV( KeyValues *pKVItemLevel, CEconItemSchema &pschema, const char *pszLevelBlockName, CUtlVector<CUtlString> *pVecErrors )
{
m_unLevel = Q_atoi( pKVItemLevel->GetName() );
m_unRequiredScore = pKVItemLevel->GetInt( "score" );
m_pszLocalizedName_LocalStorage = strdup( pKVItemLevel->GetString( "rank_name", CFmtStr( "%s%i", pszLevelBlockName, m_unLevel ).Access() ) );
return SCHEMA_INIT_SUCCESS();
}
#ifdef GAME_DLL
void ReloadMasterItemSchema( void )
{
// This command does nothing on the public universe.
if ( steamapicontext->SteamUtils() && steamapicontext->SteamUtils()->GetConnectedUniverse() == k_EUniversePublic )
return;
CUtlVector< CUtlString > vecErrors;
bool bSuccess = GEconItemSchema().BInit( "scripts/items/unencrypted/items_master.txt", "MOD", &vecErrors );
if( !bSuccess )
{
FOR_EACH_VEC( vecErrors, nError )
{
Msg( "%s", vecErrors[nError].String() );
}
}
}
CON_COMMAND_F( load_master_item_schema, "Reloads the item master schema.", FCVAR_GAMEDLL | FCVAR_CHEAT | FCVAR_DEVELOPMENTONLY | FCVAR_HIDDEN )
{
ReloadMasterItemSchema();
}
#endif