You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
9705 lines
350 KiB
9705 lines
350 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 "tier1/UtlSortVector.h" |
|
#include "tier2/tier2.h" |
|
#include "filesystem.h" |
|
#include "schemainitutils.h" |
|
#include "gcsdk/gcsdk_auto.h" |
|
#include "rtime.h" |
|
#include "item_selection_criteria.h" |
|
#include "crypto.h" |
|
#include "checksum_sha1.h" |
|
|
|
#include <google/protobuf/text_format.h> |
|
#include <string.h> |
|
|
|
#include "materialsystem/imaterialsystem.h" |
|
#include "materialsystem/itexture.h" |
|
#include "materialsystem/itexturecompositor.h" |
|
|
|
#if ( defined( _MSC_VER ) && _MSC_VER >= 1900 ) |
|
#define timezone _timezone |
|
#define daylight _daylight |
|
#endif |
|
|
|
// For holiday-limited loot lists. |
|
#include "econ_holidays.h" |
|
|
|
// Only used for startup testing. |
|
#include "econ_item_tools.h" |
|
|
|
#if defined(CLIENT_DLL) || defined(GAME_DLL) |
|
#include "econ_item_system.h" |
|
#include "econ_item.h" |
|
#include "activitylist.h" |
|
|
|
#if defined(TF_CLIENT_DLL) || defined(TF_DLL) |
|
#include "tf_gcmessages.h" |
|
#endif |
|
#endif |
|
|
|
#ifdef GC_DLL |
|
#include "gcgamebase.h" |
|
#include <memory> // unique_ptr |
|
#endif |
|
|
|
// memdbgon must be the last include file in a .cpp file!!! |
|
#include "tier0/memdbgon.h" |
|
|
|
|
|
using namespace GCSDK; |
|
|
|
|
|
CEconItemSchema & GEconItemSchema() |
|
{ |
|
#if defined( EXTERNALTESTS_DLL ) |
|
static CEconItemSchema g_econItemSchema; |
|
return g_econItemSchema; |
|
#elif defined( GC_DLL ) |
|
return *GEconManager()->GetItemSchema(); |
|
#else |
|
return *ItemSystem()->GetItemSchema(); |
|
#endif |
|
} |
|
|
|
const char *g_szDropTypeStrings[] = |
|
{ |
|
"", // Blank and none mean the same thing: stay attached to the body. |
|
"none", |
|
"drop", // The item drops off the body. |
|
"break", // Not implemented, but an example of a type that could be added. |
|
}; |
|
|
|
const char *g_TeamVisualSections[TEAM_VISUAL_SECTIONS] = |
|
{ |
|
"visuals", // TF_TEAM_UNASSIGNED. Visual changes applied to both teams. |
|
NULL, // TF_TEAM_SPECTATOR. Unused. |
|
"visuals_red", // TF_TEAM_RED |
|
"visuals_blu", // TF_TEAM_BLUE |
|
"visuals_mvm_boss", // Hack to override things in MvM at a general level |
|
}; |
|
|
|
int GetTeamVisualsFromString( const char *pszString ) |
|
{ |
|
for ( int i = 0; i < TEAM_VISUAL_SECTIONS; i++ ) |
|
{ |
|
// There's a NULL hidden in g_TeamVisualSections |
|
if ( g_TeamVisualSections[i] && !Q_stricmp( pszString, g_TeamVisualSections[i] ) ) |
|
return i; |
|
} |
|
return -1; |
|
} |
|
|
|
#if defined(CLIENT_DLL) || defined(GAME_DLL) |
|
// Used to convert strings to ints for wearable animation types |
|
const char *g_WearableAnimTypeStrings[ NUM_WAP_TYPES ] = |
|
{ |
|
"on_spawn", // WAP_ON_SPAWN, |
|
"start_building", // WAP_START_BUILDING, |
|
"stop_building", // WAP_STOP_BUILDING, |
|
"start_taunting", // WAP_START_TAUNTING, |
|
"stop_taunting", // WAP_STOP_TAUNTING, |
|
}; |
|
#endif |
|
|
|
const char *g_AttributeDescriptionFormats[] = |
|
{ |
|
"value_is_percentage", // ATTDESCFORM_VALUE_IS_PERCENTAGE, |
|
"value_is_inverted_percentage", // ATTDESCFORM_VALUE_IS_INVERTED_PERCENTAGE |
|
"value_is_additive", // ATTDESCFORM_VALUE_IS_ADDITIVE |
|
"value_is_additive_percentage", // ATTDESCFORM_VALUE_IS_ADDITIVE_PERCENTAGE |
|
"value_is_or", // ATTDESCFORM_VALUE_IS_OR |
|
"value_is_date", // ATTDESCFORM_VALUE_IS_DATE |
|
"value_is_account_id", // ATTDESCFORM_VALUE_IS_ACCOUNT_ID |
|
"value_is_particle_index", // ATTDESCFORM_VALUE_IS_PARTICLE_INDEX -> Could change to "string index" |
|
"value_is_killstreakeffect_index", // ATTDESCFORM_VALUE_IS_KILLSTREAKEFFECT_INDEX -> Could change to "string index" |
|
"value_is_killstreak_idleeffect_index", // ATTDESCFORM_VALUE_IS_KILLSTREAK_IDLEEFFECT_INDEX |
|
"value_is_item_def", // ATTDESCFORM_VALUE_IS_ITEM_DEF |
|
"value_is_from_lookup_table", // ATTDESCFORM_VALUE_IS_FROM_LOOKUP_TABLE |
|
}; |
|
|
|
const char *g_EffectTypes[NUM_EFFECT_TYPES] = |
|
{ |
|
"unusual", // ATTRIB_EFFECT_UNUSUAL, |
|
"strange", // ATTRIB_EFFECT_STRANGE, |
|
"neutral", // ATTRIB_EFFECT_NEUTRAL = 0, |
|
"positive", // ATTRIB_EFFECT_POSITIVE, |
|
"negative", // ATTRIB_EFFECT_NEGATIVE, |
|
}; |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Set the capabilities bitfield based on whether the entry is true/false. |
|
//----------------------------------------------------------------------------- |
|
const char *g_Capabilities[] = |
|
{ |
|
"paintable", // ITEM_CAP_PAINTABLE |
|
"nameable", // ITEM_CAP_NAMEABLE |
|
"decodable", // ITEM_CAP_DECODABLE |
|
"can_craft_if_purchased", // ITEM_CAP_CAN_BE_CRAFTED_IF_PURCHASED |
|
"can_customize_texture", // ITEM_CAP_CAN_CUSTOMIZE_TEXTURE |
|
"usable", // ITEM_CAP_USABLE |
|
"usable_gc", // ITEM_CAP_USABLE_GC |
|
"can_gift_wrap", // ITEM_CAP_CAN_GIFT_WRAP |
|
"usable_out_of_game", // ITEM_CAP_USABLE_OUT_OF_GAME |
|
"can_collect", // ITEM_CAP_CAN_COLLECT |
|
"can_craft_count", // ITEM_CAP_CAN_CRAFT_COUNT |
|
"can_craft_mark", // ITEM_CAP_CAN_CRAFT_MARK |
|
"paintable_team_colors", // ITEM_CAP_PAINTABLE_TEAM_COLORS |
|
"can_be_restored", // ITEM_CAP_CAN_BE_RESTORED |
|
"strange_parts", // ITEM_CAP_CAN_USE_STRANGE_PARTS |
|
"can_card_upgrade", // ITEM_CAP_CAN_CARD_UPGRADE |
|
"can_strangify", // ITEM_CAP_CAN_STRANGIFY |
|
"can_killstreakify", // ITEM_CAP_CAN_KILLSTREAKIFY |
|
"can_consume", // ITEM_CAP_CAN_CONSUME_ITEMS |
|
"can_spell_page", // ITEM_CAP_CAN_SPELLBOOK_PAGE |
|
"has_slots", // ITEM_CAP_HAS_SLOTS |
|
"duck_upgradable", // ITEM_CAP_DUCK_UPGRADABLE |
|
"can_unusualify", // ITEM_CAP_CAN_UNUSUALIFY |
|
}; |
|
COMPILE_TIME_ASSERT( ARRAYSIZE(g_Capabilities) == NUM_ITEM_CAPS ); |
|
|
|
#define RETURN_ATTRIBUTE_STRING( attrib_name, default_string ) \ |
|
static CSchemaAttributeDefHandle pAttribString( attrib_name ); \ |
|
const char *pchResultAttribString = default_string; \ |
|
FindAttribute_UnsafeBitwiseCast< CAttribute_String >( this, pAttribString, &pchResultAttribString ); \ |
|
return pchResultAttribString; |
|
|
|
#define RETURN_ATTRIBUTE_STRING_F( func_name, attrib_name, default_string ) \ |
|
const char *func_name( void ) const { RETURN_ATTRIBUTE_STRING( attrib_name, default_string ) } |
|
|
|
static void ParseCapability( item_capabilities_t &capsBitfield, KeyValues* pEntry ) |
|
{ |
|
int idx = StringFieldToInt( pEntry->GetName(), g_Capabilities, ARRAYSIZE(g_Capabilities) ); |
|
if ( idx < 0 ) |
|
{ |
|
return; |
|
} |
|
int bit = 1 << idx; |
|
if ( pEntry->GetBool() ) |
|
{ |
|
(int&)capsBitfield |= bit; |
|
} |
|
else |
|
{ |
|
(int&)capsBitfield &= ~bit; |
|
} |
|
} |
|
|
|
#ifdef GC_DLL |
|
static bool BGetPaymentRule( KeyValues *pKVRule, EPaymentRuleType *out_pePaymentRuleType, double *out_pRevenueShare ) |
|
{ |
|
Assert( out_pePaymentRuleType ); |
|
Assert( out_pRevenueShare ); |
|
|
|
struct payment_rule_lookup_t |
|
{ |
|
const char *m_pszStr; |
|
EPaymentRuleType m_eRuleType; |
|
}; |
|
|
|
static payment_rule_lookup_t s_Lookup[] = |
|
{ |
|
{ "workshop_revenue_share", kPaymentRule_SteamWorkshopFileID }, |
|
{ "partner_revenue_share", kPaymentRule_PartnerSteamID }, |
|
{ "bundle_revenue_share", kPaymentRule_Bundle }, |
|
}; |
|
|
|
for ( int i = 0; i < ARRAYSIZE( s_Lookup ); i++ ) |
|
{ |
|
KeyValues *pKVKey = pKVRule->FindKey( s_Lookup[i].m_pszStr ); |
|
if ( !pKVKey ) |
|
continue; |
|
|
|
*out_pePaymentRuleType = s_Lookup[i].m_eRuleType; |
|
*out_pRevenueShare = atof( pKVKey->GetString() ) * 100.0; // KeyValues doesn't support parsing a string as a double-precision value, so we do it by hand |
|
return true; |
|
} |
|
|
|
return false; |
|
} |
|
#endif // GC_DLL |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: CEconItemSeriesDefinition |
|
//----------------------------------------------------------------------------- |
|
CEconItemSeriesDefinition::CEconItemSeriesDefinition( void ) |
|
: m_nValue( INT_MAX ) |
|
{ |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Copy constructor |
|
//----------------------------------------------------------------------------- |
|
CEconItemSeriesDefinition::CEconItemSeriesDefinition( const CEconItemSeriesDefinition &that ) |
|
{ |
|
( *this ) = that; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Operator= |
|
//----------------------------------------------------------------------------- |
|
CEconItemSeriesDefinition &CEconItemSeriesDefinition::operator=( const CEconItemSeriesDefinition &rhs ) |
|
{ |
|
m_nValue = rhs.m_nValue; |
|
m_strName = rhs.m_strName; |
|
|
|
return *this; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Initialize the quality definition |
|
// Input: pKVQuality - The KeyValues representation of the quality |
|
// schema - The overall item schema for this attribute |
|
// pVecErrors - An optional vector that will contain error messages if |
|
// the init fails. |
|
// Output: True if initialization succeeded, false otherwise |
|
//----------------------------------------------------------------------------- |
|
bool CEconItemSeriesDefinition::BInitFromKV( KeyValues *pKVSeries, CUtlVector<CUtlString> *pVecErrors /* = NULL */ ) |
|
{ |
|
|
|
m_nValue = pKVSeries->GetInt( "value", -1 ); |
|
m_strName = pKVSeries->GetName(); |
|
|
|
m_strLockKey = pKVSeries->GetString( "loc_key" ); |
|
m_strUiFile = pKVSeries->GetString( "ui" ); |
|
|
|
// Check for required fields |
|
SCHEMA_INIT_CHECK( |
|
NULL != pKVSeries->FindKey( "value" ), |
|
"Quality definition %s: Missing required field \"value\"", pKVSeries->GetName() ); |
|
|
|
return SCHEMA_INIT_SUCCESS(); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Constructor |
|
//----------------------------------------------------------------------------- |
|
CEconItemQualityDefinition::CEconItemQualityDefinition( void ) |
|
: m_nValue( INT_MAX ) |
|
, m_bCanSupportSet( false ) |
|
{ |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Copy constructor |
|
//----------------------------------------------------------------------------- |
|
CEconItemQualityDefinition::CEconItemQualityDefinition( const CEconItemQualityDefinition &that ) |
|
{ |
|
(*this) = that; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Operator= |
|
//----------------------------------------------------------------------------- |
|
CEconItemQualityDefinition &CEconItemQualityDefinition::operator=( const CEconItemQualityDefinition &rhs ) |
|
{ |
|
m_nValue = rhs.m_nValue; |
|
m_strName = rhs.m_strName; |
|
m_bCanSupportSet = rhs.m_bCanSupportSet; |
|
|
|
return *this; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Initialize the quality definition |
|
// Input: pKVQuality - The KeyValues representation of the quality |
|
// schema - The overall item schema for this attribute |
|
// pVecErrors - An optional vector that will contain error messages if |
|
// the init fails. |
|
// Output: True if initialization succeeded, false otherwise |
|
//----------------------------------------------------------------------------- |
|
bool CEconItemQualityDefinition::BInitFromKV( KeyValues *pKVQuality, CUtlVector<CUtlString> *pVecErrors /* = NULL */ ) |
|
{ |
|
|
|
m_nValue = pKVQuality->GetInt( "value", -1 ); |
|
m_strName = pKVQuality->GetName(); |
|
m_bCanSupportSet = pKVQuality->GetBool( "canSupportSet" ); |
|
#ifdef GC_DLL |
|
m_strHexColor = pKVQuality->GetString( "hexColor" ); |
|
#endif // GC_DLL |
|
|
|
// Check for required fields |
|
SCHEMA_INIT_CHECK( |
|
NULL != pKVQuality->FindKey( "value" ), |
|
"Quality definition %s: Missing required field \"value\"", pKVQuality->GetName() ); |
|
|
|
#if defined(CLIENT_DLL) || defined(GAME_DLL) |
|
return SCHEMA_INIT_SUCCESS(); |
|
#endif // GC_DLL |
|
|
|
// Check for data consistency |
|
SCHEMA_INIT_CHECK( |
|
0 != Q_stricmp( GetName(), "any" ), |
|
"Quality definition any: The quality name \"any\" is a reserved keyword and cannot be used." ); |
|
|
|
SCHEMA_INIT_CHECK( |
|
m_nValue != k_unItemQuality_Any, |
|
"Quality definition %s: Invalid value (%d). It is reserved for Any", GetName(), k_unItemQuality_Any ); |
|
|
|
return SCHEMA_INIT_SUCCESS(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// CEconItemRarityDefinition |
|
//----------------------------------------------------------------------------- |
|
CEconItemRarityDefinition::CEconItemRarityDefinition( void ) |
|
: m_nValue( INT_MAX ) |
|
, m_nLootlistWeight( 0 ) |
|
{ |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Initialize the rarity definition |
|
//----------------------------------------------------------------------------- |
|
bool CEconItemRarityDefinition::BInitFromKV( KeyValues *pKVRarity, KeyValues *pKVRarityWeights, CEconItemSchema &pschema, CUtlVector<CUtlString> *pVecErrors /* = NULL */ ) |
|
{ |
|
m_nValue = pKVRarity->GetInt( "value", -1 ); |
|
m_strName = pKVRarity->GetName(); |
|
m_strLocKey = pKVRarity->GetString( "loc_key" ); |
|
m_strWepLocKey = pKVRarity->GetString( "loc_key_weapon" ); |
|
|
|
m_iAttribColor = GetAttribColorIndexForName( pKVRarity->GetString( "color" ) ); |
|
m_strDropSound = pKVRarity->GetString( "drop_sound" ); |
|
m_strNextRarity = pKVRarity->GetString( "next_rarity" ); // Not required. |
|
|
|
#ifdef GC_DLL |
|
if ( pKVRarityWeights ) |
|
{ |
|
m_nLootlistWeight = pKVRarityWeights->GetInt( m_strName, 0 ); |
|
} |
|
#endif |
|
// |
|
|
|
// Check for required fields |
|
SCHEMA_INIT_CHECK( |
|
NULL != pKVRarity->FindKey( "value" ), |
|
"Rarity definition %s: Missing required field \"value\"", pKVRarity->GetName() ); |
|
|
|
SCHEMA_INIT_CHECK( |
|
NULL != pKVRarity->FindKey( "loc_key" ), |
|
"Rarity definition %s: Missing required field \"loc_key\"", pKVRarity->GetName() ); |
|
|
|
return SCHEMA_INIT_SUCCESS(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
bool CEconColorDefinition::BInitFromKV( KeyValues *pKVColor, CUtlVector<CUtlString> *pVecErrors /* = NULL */ ) |
|
{ |
|
m_strName = pKVColor->GetName(); |
|
m_strColorName = pKVColor->GetString( "color_name" ); |
|
#ifdef GC_DLL |
|
m_strHexColor = pKVColor->GetString( "hex_color" ); |
|
#endif // GC_DLL |
|
|
|
SCHEMA_INIT_CHECK( |
|
!m_strColorName.IsEmpty(), |
|
"Quality definition %s: missing \"color_name\"", GetName() ); |
|
|
|
#ifdef GC_DLL |
|
SCHEMA_INIT_CHECK( |
|
!m_strHexColor.IsEmpty(), |
|
"Quality definition %s: missing \"hex_color\"", GetName() ); |
|
#endif // GC_DLL |
|
|
|
return SCHEMA_INIT_SUCCESS(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// |
|
//----------------------------------------------------------------------------- |
|
CEconItemSetDefinition::CEconItemSetDefinition( void ) |
|
: m_pszName( NULL ) |
|
, m_pszLocalizedName( NULL ) |
|
, m_iBundleItemDef( INVALID_ITEM_DEF_INDEX ) |
|
, m_bIsHiddenSet( false ) |
|
{ |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Copy constructor |
|
//----------------------------------------------------------------------------- |
|
CEconItemSetDefinition::CEconItemSetDefinition( const CEconItemSetDefinition &that ) |
|
{ |
|
(*this) = that; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Operator= |
|
//----------------------------------------------------------------------------- |
|
CEconItemSetDefinition &CEconItemSetDefinition::operator=( const CEconItemSetDefinition &other ) |
|
{ |
|
m_pszName = other.m_pszName; |
|
m_pszLocalizedName = other.m_pszLocalizedName; |
|
m_iItemDefs = other.m_iItemDefs; |
|
m_iAttributes = other.m_iAttributes; |
|
m_iBundleItemDef = other.m_iBundleItemDef; |
|
m_bIsHiddenSet = other.m_bIsHiddenSet; |
|
|
|
return *this; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// |
|
//----------------------------------------------------------------------------- |
|
bool CEconItemSetDefinition::BInitFromKV( KeyValues *pKVItemSet, CUtlVector<CUtlString> *pVecErrors ) |
|
{ |
|
m_pszName = pKVItemSet->GetName(); |
|
|
|
m_iBundleItemDef = INVALID_ITEM_DEF_INDEX; |
|
const char *pszBundleName = pKVItemSet->GetString( "store_bundle" ); |
|
if ( pszBundleName && pszBundleName[0] ) |
|
{ |
|
CEconItemDefinition *pDef = GetItemSchema()->GetItemDefinitionByName( pszBundleName ); |
|
if ( pDef ) |
|
{ |
|
m_iBundleItemDef = pDef->GetDefinitionIndex(); |
|
} |
|
|
|
SCHEMA_INIT_CHECK( |
|
pDef != NULL, |
|
"Item set %s: Bundle definition \"%s\" was not found", m_pszName, pszBundleName ); |
|
} |
|
|
|
m_pszLocalizedName = pKVItemSet->GetString( "name", NULL ); |
|
m_bIsHiddenSet = pKVItemSet->GetBool( "is_hidden_set", false ); |
|
|
|
KeyValues *pKVItems = pKVItemSet->FindKey( "items" ); |
|
if ( pKVItems ) |
|
{ |
|
FOR_EACH_SUBKEY( pKVItems, pKVItem ) |
|
{ |
|
const char *pszName = pKVItem->GetName(); |
|
|
|
CEconItemDefinition *pDef = GetItemSchema()->GetItemDefinitionByName( pszName ); |
|
|
|
SCHEMA_INIT_CHECK( |
|
pDef != NULL, |
|
"Item set %s: Item definition \"%s\" was not found", m_pszName, pszName ); |
|
|
|
const item_definition_index_t unDefIndex = pDef->GetDefinitionIndex(); |
|
|
|
SCHEMA_INIT_CHECK( |
|
!m_iItemDefs.IsValidIndex( m_iItemDefs.Find( unDefIndex ) ), |
|
"Item set %s: item definition \"%s\" appears multiple times", m_pszName, pszName ); |
|
SCHEMA_INIT_CHECK( |
|
!pDef->GetItemSetDefinition(), |
|
"Item set %s: item definition \"%s\" specified in multiple item sets", m_pszName, pszName ); |
|
|
|
m_iItemDefs.AddToTail( unDefIndex ); |
|
pDef->SetItemSetDefinition( this ); |
|
|
|
// FIXME: hack to work around crafting item criteria |
|
pDef->GetRawDefinition()->SetString( "item_set", m_pszName ); |
|
} |
|
} |
|
|
|
KeyValues *pKVAttributes = pKVItemSet->FindKey( "attributes" ); |
|
if ( pKVAttributes ) |
|
{ |
|
FOR_EACH_SUBKEY( pKVAttributes, pKVAttribute ) |
|
{ |
|
const char *pszName = pKVAttribute->GetName(); |
|
|
|
const CEconItemAttributeDefinition *pAttrDef = GetItemSchema()->GetAttributeDefinitionByName( pszName ); |
|
SCHEMA_INIT_CHECK( |
|
pAttrDef != NULL, |
|
"Item set %s: Attribute definition \"%s\" was not found", m_pszName, pszName ); |
|
SCHEMA_INIT_CHECK( |
|
pAttrDef->BIsSetBonusAttribute(), |
|
"Item set %s: Attribute definition \"%s\" is not a set bonus attribute", m_pszName, pszName ); |
|
|
|
int iIndex = m_iAttributes.AddToTail(); |
|
m_iAttributes[iIndex].m_iAttribDefIndex = pAttrDef->GetDefinitionIndex(); |
|
m_iAttributes[iIndex].m_flValue = pKVAttribute->GetFloat( "value" ); |
|
} |
|
} |
|
|
|
// Sanity check. |
|
SCHEMA_INIT_CHECK( m_pszLocalizedName != NULL, |
|
"Item set %s: Set contains no localized name", m_pszName ); |
|
SCHEMA_INIT_CHECK( m_iItemDefs.Count() > 0, |
|
"Item set %s: Set contains no items", m_pszName ); |
|
|
|
return SCHEMA_INIT_SUCCESS(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// |
|
//----------------------------------------------------------------------------- |
|
void CEconItemSetDefinition::IterateAttributes( class IEconItemAttributeIterator *pIterator ) const |
|
{ |
|
FOR_EACH_VEC( m_iAttributes, i ) |
|
{ |
|
const itemset_attrib_t& itemsetAttrib = m_iAttributes[i]; |
|
|
|
const CEconItemAttributeDefinition *pAttrDef = GetItemSchema()->GetAttributeDefinition( itemsetAttrib.m_iAttribDefIndex ); |
|
if ( !pAttrDef ) |
|
continue; |
|
|
|
const ISchemaAttributeType *pAttrType = pAttrDef->GetAttributeType(); |
|
Assert( pAttrType ); |
|
|
|
// We know (and assert) that we only need 32 bits of data to store this attribute |
|
// data. We don't know anything about the type but we'll let the type handle it |
|
// below. |
|
attribute_data_union_t value; |
|
value.asFloat = itemsetAttrib.m_flValue; |
|
|
|
if ( !pAttrType->OnIterateAttributeValue( pIterator, pAttrDef, value ) ) |
|
return; |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
CEconItemCollectionDefinition::CEconItemCollectionDefinition( void ) |
|
: m_pszName( NULL ) |
|
, m_pszLocalizedName( NULL ) |
|
, m_pszLocalizedDesc( NULL ) |
|
, m_iRarityMin( k_unItemRarity_Any ) |
|
, m_iRarityMax( k_unItemRarity_Any ) |
|
{ |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// |
|
|
|
//----------------------------------------------------------------------------- |
|
static int SortCollectionByRarity( item_definition_index_t const *a, item_definition_index_t const *b ) |
|
{ |
|
Assert( a ); |
|
Assert( *a ); |
|
Assert( b ); |
|
Assert( *b ); |
|
|
|
CEconItemDefinition *pItemA = GetItemSchema()->GetItemDefinition( *a ); |
|
CEconItemDefinition *pItemB = GetItemSchema()->GetItemDefinition( *b ); |
|
|
|
if ( !pItemA || !pItemB ) |
|
{ |
|
AssertMsg( 0, "ItemDef Doesn't exist for sorting" ); |
|
return 1; |
|
} |
|
|
|
// If same Rarity, leave in current position? |
|
if ( pItemA->GetRarity() == pItemB->GetRarity() && pItemA->GetCustomPainkKitDefinition() && pItemB->GetCustomPainkKitDefinition() ) |
|
{ |
|
#ifdef CLIENT_DLL |
|
// Sort by localized name |
|
// paintkits sort by paintkit name |
|
auto paintkitA = pItemA->GetCustomPainkKitDefinition(); |
|
auto paintkitB = pItemB->GetCustomPainkKitDefinition(); |
|
auto paintkitALocName = paintkitA->GetLocalizeName(); |
|
auto paintkitBLocName = paintkitB->GetLocalizeName(); |
|
auto pkALocalized = g_pVGuiLocalize->Find( paintkitALocName ); |
|
auto pkBLocalized = g_pVGuiLocalize->Find( paintkitBLocName ); |
|
if ( pkALocalized ) |
|
{ |
|
if ( pkBLocalized ) |
|
{ |
|
return V_wcscmp( pkALocalized, pkBLocalized ); |
|
} |
|
else |
|
{ |
|
return -1; |
|
} |
|
} |
|
else |
|
{ |
|
return pkBLocalized ? 1 : -1; |
|
} |
|
#else |
|
return 0; |
|
#endif |
|
} |
|
|
|
return ( pItemA->GetRarity() > pItemB->GetRarity() ) ? -1 : 1; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
bool CEconItemCollectionDefinition::BInitFromKV( KeyValues *pKVPItemCollection, CUtlVector<CUtlString> *pVecErrors ) |
|
{ |
|
m_pszName = pKVPItemCollection->GetName(); |
|
|
|
m_pszLocalizedName = pKVPItemCollection->GetString( "name", NULL ); |
|
m_pszLocalizedDesc = pKVPItemCollection->GetString( "description", NULL ); |
|
|
|
m_bIsReferenceCollection = pKVPItemCollection->GetBool( "is_reference_collection", false ); |
|
|
|
KeyValues *pKVItems = pKVPItemCollection->FindKey( "items" ); |
|
|
|
// Create a 'lootlist' from this collection |
|
KeyValues *pCollectionLootList = NULL; |
|
bool bIsLootList = false; |
|
if ( !m_bIsReferenceCollection ) |
|
{ |
|
pCollectionLootList = new KeyValues( m_pszName ); |
|
} |
|
|
|
if ( pKVItems ) |
|
{ |
|
// Traverse rarity items and set rarity |
|
// Create a lootlist if applicable |
|
FOR_EACH_TRUE_SUBKEY( pKVItems, pKVRarity ) |
|
{ |
|
bIsLootList = true; |
|
// Get the Rarity Value |
|
const CEconItemRarityDefinition *pRarity = GetItemSchema()->GetRarityDefinitionByName( pKVRarity->GetName() ); |
|
SCHEMA_INIT_CHECK( pRarity != NULL, "Item collection %s: Rarity type \"%s\" was not found", m_pszName, pKVRarity->GetName() ); |
|
|
|
// Create a lootlist |
|
if ( !m_bIsReferenceCollection ) |
|
{ |
|
CFmtStr lootlistname( "%s_%s", m_pszName, pRarity->GetName() ); |
|
const char *pszName = V_strdup( lootlistname.Get() ); |
|
pKVRarity->SetInt( "rarity", pRarity->GetDBValue() ); |
|
SCHEMA_INIT_CHECK( GetItemSchema()->BInsertLootlist( pszName, pKVRarity, pVecErrors ), "Invalid collection lootlist %s", pszName ); |
|
KeyValues *pTempRarityKey = pKVRarity->FindKey( "rarity" ); |
|
Assert( pTempRarityKey ); |
|
pKVRarity->RemoveSubKey( pTempRarityKey ); |
|
pTempRarityKey->deleteThis(); |
|
pCollectionLootList->SetInt( pszName, pRarity->GetLootlistWeight() ); |
|
} |
|
|
|
// Items in the Rarity |
|
FOR_EACH_VALUE( pKVRarity, pKVItem ) |
|
{ |
|
const char *pszName = pKVItem->GetName(); |
|
|
|
CEconItemDefinition *pDef = GetItemSchema()->GetItemDefinitionByName( pszName ); |
|
|
|
SCHEMA_INIT_CHECK( |
|
pDef != NULL, |
|
"Item set %s: Item definition \"%s\" was not found", m_pszName, pszName ); |
|
|
|
const item_definition_index_t unDefIndex = pDef->GetDefinitionIndex(); |
|
|
|
SCHEMA_INIT_CHECK( |
|
!m_iItemDefs.IsValidIndex( m_iItemDefs.Find( unDefIndex ) ), |
|
"Item Collection %s: item definition \"%s\" appears multiple times", m_pszName, pszName ); |
|
|
|
m_iItemDefs.AddToTail( unDefIndex ); |
|
|
|
// Collection Reference |
|
if ( !m_bIsReferenceCollection ) |
|
{ |
|
SCHEMA_INIT_CHECK( |
|
!pDef->GetItemCollectionDefinition(), |
|
"Item Collection %s: item definition \"%s\" specified in multiple item sets", m_pszName, pszName ); |
|
pDef->SetItemCollectionDefinition( this ); |
|
} |
|
|
|
// Item Rarity |
|
pDef->SetRarity( pRarity->GetDBValue() ); |
|
} |
|
} |
|
|
|
// Loose Items |
|
FOR_EACH_VALUE( pKVItems, pKVItem ) |
|
{ |
|
const char *pszName = pKVItem->GetName(); |
|
|
|
CEconItemDefinition *pDef = GetItemSchema()->GetItemDefinitionByName( pszName ); |
|
|
|
SCHEMA_INIT_CHECK( |
|
pDef != NULL, |
|
"Item set %s: Item definition \"%s\" was not found", m_pszName, pszName ); |
|
|
|
const item_definition_index_t unDefIndex = pDef->GetDefinitionIndex(); |
|
|
|
SCHEMA_INIT_CHECK( |
|
!m_iItemDefs.IsValidIndex( m_iItemDefs.Find( unDefIndex ) ), |
|
"Item Collection %s: item definition \"%s\" appears multiple times", m_pszName, pszName ); |
|
|
|
m_iItemDefs.AddToTail( unDefIndex ); |
|
|
|
if ( !m_bIsReferenceCollection ) |
|
{ |
|
SCHEMA_INIT_CHECK( |
|
!pDef->GetItemCollectionDefinition(), |
|
"Item Collection %s: item definition \"%s\" specified in multiple item sets", m_pszName, pszName ); |
|
pDef->SetItemCollectionDefinition( this ); |
|
} |
|
} |
|
|
|
// Sort by Rarity |
|
m_iItemDefs.Sort( &SortCollectionByRarity ); |
|
} |
|
|
|
if ( !m_bIsReferenceCollection && bIsLootList ) |
|
{ |
|
// Insert collection lootlist |
|
GetItemSchema()->BInsertLootlist( m_pszName, pCollectionLootList, pVecErrors ); |
|
} |
|
|
|
if ( pCollectionLootList ) |
|
{ |
|
pCollectionLootList->deleteThis(); |
|
} |
|
|
|
// Sorted high to low |
|
m_iRarityMax = GetItemSchema()->GetItemDefinition( m_iItemDefs[ 0 ] )->GetRarity(); |
|
m_iRarityMin = GetItemSchema()->GetItemDefinition( m_iItemDefs[ m_iItemDefs.Count() - 1] )->GetRarity(); |
|
// Verify that there is no gaps in the Rarity (would cause crafting problems and makes no sense) |
|
|
|
if ( !m_bIsReferenceCollection ) |
|
{ |
|
int iRarityVerify = m_iRarityMax; |
|
FOR_EACH_VEC( m_iItemDefs, i ) |
|
{ |
|
int iNextRarity = GetItemSchema()->GetItemDefinition( m_iItemDefs[i] )->GetRarity(); |
|
SCHEMA_INIT_CHECK( iRarityVerify - iNextRarity <= 1, "Items in Collection %s: Have a gap in rarity tiers", m_pszName ); |
|
iRarityVerify = iNextRarity; |
|
} |
|
} |
|
|
|
// Sanity check. |
|
SCHEMA_INIT_CHECK( m_pszLocalizedName != NULL, |
|
"Item Collection %s: Collection contains no localized name", m_pszName ); |
|
SCHEMA_INIT_CHECK( m_pszLocalizedDesc != NULL, |
|
"Item Collection %s: Collection contains no localized description", m_pszName ); |
|
SCHEMA_INIT_CHECK( m_iItemDefs.Count() > 0, |
|
"Item Collection %s: Collection contains no items", m_pszName ); |
|
|
|
return SCHEMA_INIT_SUCCESS(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// CEconItemPaintKitDefinition |
|
//----------------------------------------------------------------------------- |
|
CEconItemPaintKitDefinition::CEconItemPaintKitDefinition( void ) |
|
: m_pszName( NULL ) |
|
{ |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
CEconItemPaintKitDefinition::~CEconItemPaintKitDefinition( void ) |
|
{ |
|
FOR_EACH_VEC( m_vecPaintKitWearKVP, i ) |
|
{ |
|
if ( m_vecPaintKitWearKVP[i] ) |
|
{ |
|
m_vecPaintKitWearKVP[i]->deleteThis(); |
|
} |
|
} |
|
|
|
m_vecPaintKitWearKVP.Purge(); |
|
} |
|
|
|
bool VerifyPaintKitComposite( KeyValues *pKVWearInputItems, const char* pName, int iWearLevel, CUtlVector<CUtlString> *pVecErrors ) |
|
{ |
|
SCHEMA_INIT_CHECK( pKVWearInputItems != NULL, "Paint Kit %s: Does not contain Wear Level %d", pName, iWearLevel ); |
|
#ifdef CLIENT_DLL |
|
int w = 1; |
|
int h = 1; |
|
int seed = 0; |
|
ITextureCompositor* pWeaponSkinBaseCompositor = NULL; |
|
|
|
SafeAssign( &pWeaponSkinBaseCompositor, materials->NewTextureCompositor( w, h, pName, TF_TEAM_RED, seed, pKVWearInputItems, TEX_COMPOSITE_CREATE_FLAGS_VERIFY_SCHEMA_ONLY ) ); |
|
SCHEMA_INIT_CHECK( pWeaponSkinBaseCompositor != NULL, "Could Not Create Weapon Skin Compositor for [%s][Wear %d][Team Red]", pName, iWearLevel); |
|
SafeRelease( &pWeaponSkinBaseCompositor ); |
|
|
|
SafeAssign( &pWeaponSkinBaseCompositor, materials->NewTextureCompositor( w, h, pName, TF_TEAM_BLUE, seed, pKVWearInputItems, TEX_COMPOSITE_CREATE_FLAGS_VERIFY_SCHEMA_ONLY ) ); |
|
SCHEMA_INIT_CHECK( pWeaponSkinBaseCompositor != NULL, "Could Not Create Weapon Skin Compositor for [%s][Wear %d][Team BLUE]", pName, iWearLevel ); |
|
SafeRelease( &pWeaponSkinBaseCompositor ); |
|
#endif // CLIENT_DLL |
|
return SCHEMA_INIT_SUCCESS(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
bool CEconItemPaintKitDefinition::BInitFromKV( KeyValues *pKVPItemPaintKit, CUtlVector<CUtlString> *pVecErrors ) |
|
{ |
|
m_pszName = pKVPItemPaintKit->GetName(); |
|
m_pszLocalizedName = m_pszName; // localization key is same as paintkit name for ease of generation |
|
|
|
SCHEMA_INIT_CHECK( m_pszLocalizedName != NULL, "Paint Kit %s: PaintKit contains no localized name", m_pszName ); |
|
|
|
KeyValues *pKVWearInputItems = NULL; |
|
|
|
pKVWearInputItems = pKVPItemPaintKit->FindKey( "wear_level_1", false ); |
|
SCHEMA_INIT_CHECK( VerifyPaintKitComposite( pKVWearInputItems, m_pszName, 1, pVecErrors ), "Could Not Create Weapon Skin Compositor for [%s][Wear %d]", m_pszName, 1 ); |
|
m_vecPaintKitWearKVP.AddToTail( pKVWearInputItems->MakeCopy() ); |
|
|
|
pKVWearInputItems = pKVPItemPaintKit->FindKey( "wear_level_2", false ); |
|
SCHEMA_INIT_CHECK( VerifyPaintKitComposite( pKVWearInputItems, m_pszName, 2, pVecErrors ), "Could Not Create Weapon Skin Compositor for [%s][Wear %d]", m_pszName, 2 ); |
|
m_vecPaintKitWearKVP.AddToTail( pKVWearInputItems->MakeCopy() ); |
|
|
|
pKVWearInputItems = pKVPItemPaintKit->FindKey( "wear_level_3", false ); |
|
SCHEMA_INIT_CHECK( VerifyPaintKitComposite( pKVWearInputItems, m_pszName, 3, pVecErrors ), "Could Not Create Weapon Skin Compositor for [%s][Wear %d]", m_pszName, 3 ); |
|
m_vecPaintKitWearKVP.AddToTail( pKVWearInputItems->MakeCopy() ); |
|
|
|
pKVWearInputItems = pKVPItemPaintKit->FindKey( "wear_level_4", false ); |
|
SCHEMA_INIT_CHECK( VerifyPaintKitComposite( pKVWearInputItems, m_pszName, 4, pVecErrors ), "Could Not Create Weapon Skin Compositor for [%s][Wear %d]", m_pszName, 4 ); |
|
m_vecPaintKitWearKVP.AddToTail( pKVWearInputItems->MakeCopy() ); |
|
|
|
pKVWearInputItems = pKVPItemPaintKit->FindKey( "wear_level_5", false ); |
|
SCHEMA_INIT_CHECK( VerifyPaintKitComposite( pKVWearInputItems, m_pszName, 5, pVecErrors ), "Could Not Create Weapon Skin Compositor for [%s][Wear %d]", m_pszName, 5 ); |
|
m_vecPaintKitWearKVP.AddToTail( pKVWearInputItems->MakeCopy() ); |
|
|
|
return SCHEMA_INIT_SUCCESS(); |
|
} |
|
|
|
KeyValues *CEconItemPaintKitDefinition::GetPaintKitWearKV( int nWear ) |
|
{ |
|
// Wear is 1-5 but this vec is 0-4 |
|
int iIndex = nWear - 1; |
|
|
|
if ( !m_vecPaintKitWearKVP.IsValidIndex( iIndex ) ) |
|
{ |
|
iIndex = 0; |
|
Assert( m_vecPaintKitWearKVP.IsValidIndex( iIndex ) ); |
|
DevMsg( "Invalid Paint Kit or Paint Kit Wear Entry (%s) Wear (%d).", m_pszName, nWear ); |
|
return NULL; |
|
} |
|
|
|
return m_vecPaintKitWearKVP[iIndex]; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// CEconOperationDefinition |
|
//----------------------------------------------------------------------------- |
|
CEconOperationDefinition::CEconOperationDefinition( void ) |
|
: m_pszName( NULL ) |
|
, m_unRequiredItemDefIndex( INVALID_ITEM_DEF_INDEX ) |
|
, m_unGatewayItemDefIndex( INVALID_ITEM_DEF_INDEX ) |
|
{ |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
CEconOperationDefinition::~CEconOperationDefinition( void ) |
|
{ |
|
if ( m_pKVItem ) |
|
m_pKVItem->deleteThis(); |
|
m_pKVItem = NULL; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
bool CEconOperationDefinition::BInitFromKV( KeyValues *pKVPOperation, CUtlVector<CUtlString> *pVecErrors ) |
|
{ |
|
m_unOperationID = V_atoi( pKVPOperation->GetName() ); |
|
|
|
m_pszName = pKVPOperation->GetString( "name", NULL ); |
|
SCHEMA_INIT_CHECK( m_pszName != NULL, "OperationDefinition %s does not have 'name'", m_pszName ); |
|
|
|
// initialize required item def index if we specified one |
|
const char *pszRequiredName = pKVPOperation->GetString( "required_item_name", NULL ); |
|
if ( pszRequiredName ) |
|
{ |
|
CEconItemDefinition *pDef = GetItemSchema()->GetItemDefinitionByName( pszRequiredName ); |
|
SCHEMA_INIT_CHECK( pDef != NULL, "OperationDefinition couldn't find item def from required name '%s'", pszRequiredName ); |
|
|
|
m_unRequiredItemDefIndex = pDef->GetDefinitionIndex(); |
|
} |
|
|
|
const char *pszGatewayItemName = pKVPOperation->GetString( "gateway_item_name", NULL ); |
|
if ( pszGatewayItemName ) |
|
{ |
|
CEconItemDefinition *pDef = GetItemSchema()->GetItemDefinitionByName( pszGatewayItemName ); |
|
SCHEMA_INIT_CHECK( pDef != NULL, "OperationDefinition couldn't find item def from gateway name '%s'", pszGatewayItemName ); |
|
|
|
m_unGatewayItemDefIndex = pDef->GetDefinitionIndex(); |
|
} |
|
|
|
if ( m_unGatewayItemDefIndex != INVALID_ITEM_DEF_INDEX ) |
|
{ |
|
SCHEMA_INIT_CHECK( m_unRequiredItemDefIndex != INVALID_ITEM_DEF_INDEX, "If a gateway item is set, a required item must be set! Mismatch in %d", m_unOperationID ); |
|
} |
|
|
|
const char *pszOperationStartDate = pKVPOperation->GetString( "operation_start_date", NULL ); |
|
SCHEMA_INIT_CHECK( pszOperationStartDate != NULL, "OperationDefinition %s does not have 'operation_start_date'", m_pszName ); |
|
m_OperationStartDate = ( pszOperationStartDate && pszOperationStartDate[0] ) |
|
? CRTime::RTime32FromFmtString( "YYYY-MM-DD hh:mm:ss" , pszOperationStartDate ) |
|
: RTime32(0); |
|
|
|
const char *pszDropEndDate = pKVPOperation->GetString( "stop_giving_to_player_date", NULL ); |
|
SCHEMA_INIT_CHECK( pszDropEndDate != NULL, "OperationDefinition %s does not have 'stop_giving_to_player_date'", m_pszName ); |
|
m_StopGivingToPlayerDate = ( pszDropEndDate && pszDropEndDate[0] ) |
|
? CRTime::RTime32FromFmtString( "YYYY-MM-DD hh:mm:ss" , pszDropEndDate ) |
|
: RTime32(0); |
|
|
|
const char *pszOperationEndDate = pKVPOperation->GetString( "stop_adding_to_queue_date", NULL ); |
|
SCHEMA_INIT_CHECK( pszOperationEndDate != NULL, "OperationDefinition %s does not have 'stop_adding_to_queue_date'", m_pszName ); |
|
m_StopAddingToQueueDate = ( pszOperationEndDate && pszOperationEndDate[0] ) |
|
? CRTime::RTime32FromFmtString( "YYYY-MM-DD hh:mm:ss" , pszOperationEndDate ) |
|
: RTime32(0); |
|
|
|
m_pszQuestLogResFile = pKVPOperation->GetString( "quest_log_res_file", NULL ); |
|
m_pszQuestListResFile = pKVPOperation->GetString( "quest_list_res_file", NULL ); |
|
|
|
m_pszOperationLootList = pKVPOperation->GetString( "operation_lootlist" ); |
|
SCHEMA_INIT_CHECK( m_pszOperationLootList != NULL, "OperationDefinition %s does not have 'operation_lootlist'", m_pszName ); |
|
|
|
m_bIsCampaign = pKVPOperation->GetBool( "is_campaign" ); |
|
m_unMaxDropCount = pKVPOperation->GetInt( "max_drop_count" ); |
|
|
|
#ifdef GC_DLL |
|
|
|
m_rtQueueFreqMin = pKVPOperation->GetFloat( "queue_freq_min" ) * k_nSecondsPerHour; // converts specified hours to seconds |
|
SCHEMA_INIT_CHECK( m_rtQueueFreqMin != 0.f, "OperationDefinition %s does not have 'queue_freq_min'", m_pszName ); |
|
|
|
m_rtQueueFreqMax = pKVPOperation->GetFloat( "queue_freq_max" ) * k_nSecondsPerHour; // converts specified hours to seconds |
|
SCHEMA_INIT_CHECK( m_rtQueueFreqMax != 0.f, "OperationDefinition %s does not have 'queue_freq_max'", m_pszName ); |
|
|
|
SCHEMA_INIT_CHECK( m_rtQueueFreqMin <= m_rtQueueFreqMax, "OperationDefinition %s 'queue_freq_min' must be less than 'queue_freq_max'", m_pszName ); |
|
|
|
m_rtDropFreqMin = pKVPOperation->GetFloat( "drop_freq_min" ) * k_nSecondsPerHour; // converts specified hours to seconds |
|
SCHEMA_INIT_CHECK( m_rtDropFreqMin != 0.f, "OperationDefinition %s does not have 'drop_freq_min'", m_pszName ); |
|
|
|
m_rtDropFreqMax = pKVPOperation->GetFloat( "drop_freq_max" ) * k_nSecondsPerHour; // converts specified hours to seconds |
|
SCHEMA_INIT_CHECK( m_rtDropFreqMax != 0.f, "OperationDefinition %s does not have 'drop_freq_max'", m_pszName ); |
|
|
|
SCHEMA_INIT_CHECK( m_rtDropFreqMin <= m_rtDropFreqMax, "OperationDefinition %s 'drop_freq_min' must be less than 'drop_freq_max'", m_pszName ); |
|
|
|
m_unSeed = pKVPOperation->GetInt( "seed_drops" ); |
|
m_unMaxHeldDrops = pKVPOperation->GetInt( "max_held_drops" ); |
|
m_nMaxQueueCount = pKVPOperation->GetInt( "max_queue_count" ); |
|
m_unMaxDropPerThink = pKVPOperation->GetInt( "max_drop_per_think", 1 ); |
|
|
|
m_pszContractRewardLootlist[ REWARD_CASE ] = pKVPOperation->GetString( "contract_reward_case_lootlist" ); |
|
m_pszContractRewardLootlist[ REWARD_WEAPON ] = pKVPOperation->GetString( "contract_reward_weapon_lootlist" ); |
|
|
|
#endif // GC_DLL |
|
|
|
m_pKVItem = pKVPOperation->MakeCopy(); |
|
|
|
return SCHEMA_INIT_SUCCESS(); |
|
} |
|
|
|
#ifdef GC_DLL |
|
#ifdef STAGING_ONLY |
|
GCConVar gc_quick_operation_drop_name( "gc_quick_operation_drop_name", "" ); |
|
GCConVar gc_quick_operation_drop_rate( "gc_quick_operation_drop_rate", "10" ); |
|
#endif // STAGING_ONLY |
|
|
|
RTime32 CEconOperationDefinition::GetMinQueueFreq() const |
|
{ |
|
#ifdef STAGING_ONLY |
|
if ( Q_stricmp( gc_quick_operation_drop_name.GetString(), m_pszName ) == 0 ) |
|
{ |
|
return gc_quick_operation_drop_rate.GetInt(); |
|
} |
|
#endif |
|
|
|
return m_rtQueueFreqMin; |
|
} |
|
|
|
RTime32 CEconOperationDefinition::GetMaxQueueFreq() const |
|
{ |
|
#ifdef STAGING_ONLY |
|
if ( Q_stricmp( gc_quick_operation_drop_name.GetString(), m_pszName ) == 0 ) |
|
{ |
|
return gc_quick_operation_drop_rate.GetInt() + 2; |
|
} |
|
#endif |
|
|
|
return m_rtQueueFreqMax; |
|
} |
|
|
|
RTime32 CEconOperationDefinition::GetMinDropFreq() const |
|
{ |
|
#ifdef STAGING_ONLY |
|
if ( Q_stricmp( gc_quick_operation_drop_name.GetString(), m_pszName ) == 0 ) |
|
{ |
|
return gc_quick_operation_drop_rate.GetInt(); |
|
} |
|
#endif |
|
|
|
return m_rtDropFreqMin; |
|
} |
|
|
|
RTime32 CEconOperationDefinition::GetMaxDropFreq() const |
|
{ |
|
#ifdef STAGING_ONLY |
|
if ( Q_stricmp( gc_quick_operation_drop_name.GetString(), m_pszName ) == 0 ) |
|
{ |
|
return gc_quick_operation_drop_rate.GetInt() + 2; |
|
} |
|
#endif |
|
|
|
return m_rtDropFreqMax; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
bool BCommonInitPropertyGeneratorsFromKV( const char *pszContext, CUtlVector<const IEconItemPropertyGenerator *> *out_pvecGenerators, KeyValues *pKV, CUtlVector<CUtlString> *pVecErrors ) |
|
{ |
|
// Forward declaration so factory functions can be wherever. |
|
IEconItemPropertyGenerator *CreateChangeQualityGenerator( KeyValues *, CUtlVector<CUtlString> * ); |
|
IEconItemPropertyGenerator *CreateRandomEvenChanceAttrGenerator( KeyValues *, CUtlVector<CUtlString> * ); |
|
IEconItemPropertyGenerator *CreateUniformLineItemLootListGenerator( KeyValues *, CUtlVector<CUtlString> * ); |
|
IEconItemPropertyGenerator *CreateDynamicAttrsGenerator( KeyValues *, CUtlVector<CUtlString> * ); |
|
|
|
// "Factory". |
|
struct econ_item_property_generator_factory_entry_t |
|
{ |
|
const char *m_pszGeneratorName; |
|
IEconItemPropertyGenerator * (* m_funcCreateGeneratorInstance)( KeyValues *, CUtlVector<CUtlString> * ); |
|
}; |
|
|
|
static econ_item_property_generator_factory_entry_t s_Generators[] = |
|
{ |
|
{ "change_quality", &CreateChangeQualityGenerator }, |
|
{ "random_even_chance_attr", &CreateRandomEvenChanceAttrGenerator }, |
|
{ "uniform_line_item_loot_list", &CreateUniformLineItemLootListGenerator }, |
|
{ "dynamic_attrs", &CreateDynamicAttrsGenerator }, |
|
}; |
|
|
|
Assert( out_pvecGenerators ); |
|
Assert( pVecErrors ); |
|
|
|
// No input data means "we succeeded here". |
|
if ( !pKV ) |
|
return true; |
|
|
|
// Handle each generator one at a time. We'll try to initialize the whole set to get as many |
|
// errors as possible and then return accumulated errors. |
|
FOR_EACH_SUBKEY( pKV, pKVGenerator ) |
|
{ |
|
const char *pszGeneratorName = pKVGenerator->GetName(); |
|
|
|
IEconItemPropertyGenerator *pGenerator = NULL; |
|
for ( const auto& gen : s_Generators ) |
|
{ |
|
if ( Q_stricmp( gen.m_pszGeneratorName, pszGeneratorName ) != 0 ) |
|
continue; |
|
|
|
pGenerator = (*gen.m_funcCreateGeneratorInstance)( pKVGenerator, pVecErrors ); |
|
SCHEMA_INIT_CHECK( pGenerator != nullptr, "%s: property generator \"%s\" failed to initialize.\n", pszContext, pszGeneratorName ); |
|
|
|
out_pvecGenerators->AddToTail( pGenerator ); |
|
break; |
|
} |
|
|
|
// Make sure we found a way to create this instance. |
|
SCHEMA_INIT_CHECK( pGenerator != nullptr, "%s: unknown type for property generator \"%s\".\n", pszContext, pszGeneratorName ); |
|
} |
|
|
|
return SCHEMA_INIT_SUCCESS(); |
|
} |
|
#endif // GC_DLL |
|
|
|
//----------------------------------------------------------------------------- |
|
// Dtor |
|
//----------------------------------------------------------------------------- |
|
CEconLootListDefinition::~CEconLootListDefinition( void ) |
|
{ |
|
#ifdef GC_DLL |
|
m_RandomAttribs.PurgeAndDeleteElements(); |
|
m_PropertyGenerators.PurgeAndDeleteElements(); |
|
#endif |
|
} |
|
|
|
#ifdef GC_DLL |
|
bool CEconLootListDefinition::AddRandomAtrributes( KeyValues *pRandomAttributesKV, CEconItemSchema &pschema, CUtlVector<CUtlString> *pVecErrors /*= NULL*/ ) |
|
{ |
|
const char *pszAttrName = pRandomAttributesKV->GetName(); |
|
|
|
// We've found the random attribute block. Parse it. |
|
random_attrib_t *pRandomAttr = pschema.CreateRandomAttribute( m_pszName, pRandomAttributesKV, pVecErrors ); |
|
|
|
SCHEMA_INIT_CHECK( |
|
NULL != pRandomAttr, |
|
CFmtStr( "Loot List %s: Failed to create random_attrib_t '%s'", m_pszName, pszAttrName ) ); |
|
|
|
m_RandomAttribs.AddToTail( pRandomAttr ); |
|
|
|
return true; |
|
} |
|
|
|
bool CEconLootListDefinition::AddRandomAttributesFromTemplates( KeyValues *pRandomAttributesKV, CEconItemSchema &pschema, CUtlVector<CUtlString> *pVecErrors /*= NULL*/ ) |
|
{ |
|
const char *pszAttrName = pRandomAttributesKV->GetName(); |
|
|
|
// try to find attr by template name |
|
random_attrib_t *pRandomAttrTemplate = pschema.GetRandomAttributeTemplateByName( pszAttrName ); |
|
|
|
SCHEMA_INIT_CHECK( |
|
NULL != pRandomAttrTemplate, |
|
CFmtStr( "Loot List %s: Couldn't find random_attrib_t '%s' from attribute_templates", m_pszName, pszAttrName ) ); |
|
|
|
// craete a copy of the template and add to the list |
|
random_attrib_t *pRandomAttr = new random_attrib_t; |
|
*pRandomAttr = *pRandomAttrTemplate; |
|
m_RandomAttribs.AddToTail( pRandomAttr ); |
|
|
|
return true; |
|
} |
|
#endif // GC_DLL |
|
//----------------------------------------------------------------------------- |
|
// |
|
//----------------------------------------------------------------------------- |
|
static const char *g_pszDefaultRevolvingLootListHeader = "#Econ_Revolving_Loot_List"; |
|
|
|
bool CEconLootListDefinition::BInitFromKV( KeyValues *pKVLootList, CEconItemSchema &pschema, CUtlVector<CUtlString> *pVecErrors ) |
|
{ |
|
m_pszName = pKVLootList->GetName(); |
|
m_pszLootListHeader = g_pszDefaultRevolvingLootListHeader; |
|
m_pszLootListFooter = NULL; |
|
m_pszCollectionReference = NULL; |
|
m_bPublicListContents = true; |
|
#ifdef GC_DLL |
|
m_iNoDupesIterations = -1; // disable no-dupes functionality by default |
|
m_unRarity = pKVLootList->GetInt( "rarity", k_unItemRarity_Any ); |
|
#endif // GC_DLL |
|
bool bCollectionLootList = false; |
|
|
|
FOR_EACH_SUBKEY( pKVLootList, pKVListItem ) |
|
{ |
|
const char *pszName = pKVListItem->GetName(); |
|
|
|
if ( !Q_strcmp( pszName, "loot_list_header_desc" ) ) |
|
{ |
|
// Make sure we didn't specify multiple entries. |
|
SCHEMA_INIT_CHECK( |
|
g_pszDefaultRevolvingLootListHeader == m_pszLootListHeader, |
|
"Loot list %s: Multiple header descriptions specified", m_pszName ); |
|
|
|
m_pszLootListHeader = pKVListItem->GetString(); |
|
|
|
SCHEMA_INIT_CHECK( |
|
NULL != m_pszLootListHeader, |
|
"Loot list %s: Invalid header description specified", m_pszName ); |
|
|
|
continue; |
|
} |
|
else if ( !Q_strcmp( pszName, "loot_list_footer_desc" ) ) |
|
{ |
|
// Make sure we didn't specify multiple entries. |
|
SCHEMA_INIT_CHECK( |
|
NULL == m_pszLootListFooter, |
|
"Loot list %s: Multiple footer descriptions specified", m_pszName ); |
|
|
|
m_pszLootListFooter = pKVListItem->GetString(); |
|
|
|
SCHEMA_INIT_CHECK( |
|
NULL != m_pszLootListFooter, |
|
"Loot list %s: Invalid header description specified", m_pszName ); |
|
|
|
continue; |
|
} |
|
else if ( !Q_strcmp( pszName, "loot_list_collection" ) ) |
|
{ |
|
// Set name as the collection lootlist name |
|
pszName = pKVListItem->GetString(); |
|
m_pszCollectionReference = pszName; |
|
bCollectionLootList = true; |
|
} |
|
else if ( !Q_strcmp( pszName, "hide_lootlist" ) ) |
|
{ |
|
m_bPublicListContents = !pKVListItem->GetBool( nullptr, true ); |
|
continue; |
|
} |
|
else if ( !Q_strcmp( pszName, "rarity" ) ) |
|
{ |
|
// already parsed up top |
|
continue; |
|
} |
|
#ifdef GC_DLL |
|
else if ( !Q_strcmp( pszName, "random_attributes" ) ) |
|
{ |
|
AddRandomAtrributes( pKVListItem, pschema, pVecErrors ); |
|
continue; |
|
} |
|
else if ( !Q_strcmp( pszName, "attribute_templates" ) ) |
|
{ |
|
FOR_EACH_SUBKEY( pKVListItem, pKVAttributeTemplate ) |
|
{ |
|
if ( pKVAttributeTemplate->GetInt() == 0 ) |
|
continue; |
|
|
|
bool bAdded = AddRandomAttributesFromTemplates( pKVAttributeTemplate, pschema, pVecErrors ); |
|
SCHEMA_INIT_CHECK( bAdded, "Loot list %s: Failed to attribute_templates '%s'", m_pszName, pKVAttributeTemplate->GetName() ); |
|
} |
|
|
|
continue; |
|
} |
|
else if ( !Q_strcmp( pszName, "public_list_contents" ) ) |
|
{ |
|
m_bPublicListContents = pKVListItem->GetBool( nullptr, true ); |
|
continue; |
|
} |
|
else if ( !Q_stricmp( pszName, "__no_dupes_iter_count" ) ) |
|
{ |
|
m_iNoDupesIterations = pKVListItem->GetInt( nullptr, -1 ); |
|
continue; |
|
} |
|
else if ( !Q_strcmp( pszName, "additional_drop" ) ) |
|
{ |
|
float fChance = pKVListItem->GetFloat( "chance", 0.0f ); |
|
bool bPremiumOnly = pKVListItem->GetBool( "premium_only", false ); |
|
const char *pszLootList = pKVListItem->GetString( "loot_list", "" ); |
|
const char *pszRequiredHoliday = pKVListItem->GetString( "required_holiday", NULL ); |
|
const char *pszDropPerdiodStartDate = pKVListItem->GetString( "start_date", NULL ); |
|
const char *pszDropPerdiodEndDate = pKVListItem->GetString( "end_date", NULL ); |
|
|
|
int iRequiredHolidayIndex = pszRequiredHoliday |
|
? EconHolidays_GetHolidayForString( pszRequiredHoliday ) |
|
: kHoliday_None; |
|
|
|
RTime32 dropStartDate = ( pszDropPerdiodStartDate && pszDropPerdiodStartDate[0] ) |
|
? CRTime::RTime32FromFmtString( "YYYY-MM-DD hh:mm:ss" , pszDropPerdiodStartDate ) |
|
: RTime32(0); // Default to the start of time |
|
|
|
// Check that if we convert back to a string, we get the same value |
|
char rtimeBuf[k_RTimeRenderBufferSize]; |
|
SCHEMA_INIT_CHECK( |
|
pszDropPerdiodStartDate == NULL || Q_strcmp( CRTime::RTime32ToString( dropStartDate, rtimeBuf ), pszDropPerdiodStartDate ) == 0, |
|
"Malformed start drop date \"%s\" for additional_drop in lootlist %s. Must be of the form \"YYYY-MM-DD hh:mm:ss\"", pszDropPerdiodStartDate, m_pszName ); |
|
|
|
|
|
RTime32 dropEndDate = ( pszDropPerdiodEndDate && pszDropPerdiodEndDate[0] ) |
|
? CRTime::RTime32FromFmtString( "YYYY-MM-DD hh:mm:ss" , pszDropPerdiodEndDate ) |
|
: ~RTime32(0); // Default to the end of time |
|
|
|
// Check that if we convert back to a string, we get the same value |
|
SCHEMA_INIT_CHECK( |
|
pszDropPerdiodEndDate == NULL || Q_strcmp( CRTime::RTime32ToString( dropEndDate, rtimeBuf ), pszDropPerdiodEndDate ) == 0, |
|
"Malformed end drop date \"%s\" for additional_drop in lootlist %s. Must be of the form \"YYYY-MM-DD hh:mm:ss\"", pszDropPerdiodEndDate, m_pszName ); |
|
|
|
SCHEMA_INIT_CHECK( |
|
fChance > 0.0f && fChance <= 1.0f, |
|
"Loot list %s: Invalid \"additional_drop\" chance %.2f", m_pszName, fChance ); |
|
|
|
SCHEMA_INIT_CHECK( |
|
pszLootList && pszLootList[0], |
|
"Loot list %s: Missing \"additional_drop\" loot list name", m_pszName ); |
|
|
|
SCHEMA_INIT_CHECK( |
|
(pszRequiredHoliday == NULL) == (iRequiredHolidayIndex == kHoliday_None), |
|
"Loot list %s: Unknown or missing holiday \"%s\"", m_pszName, pszRequiredHoliday ? pszRequiredHoliday : "(null)" ); |
|
|
|
if ( pszLootList ) |
|
{ |
|
const CEconLootListDefinition *pLootListDef = GetItemSchema()->GetLootListByName( pszLootList ); |
|
SCHEMA_INIT_CHECK( |
|
pLootListDef, |
|
"Loot list %s: Invalid \"additional_drop\" loot list \"%s\"", m_pszName, pszLootList ); |
|
|
|
if ( pLootListDef ) |
|
{ |
|
drop_period_t dropPeriod = { dropStartDate, dropEndDate }; |
|
loot_list_additional_drop_t additionalDrop = { fChance, bPremiumOnly, pszLootList, iRequiredHolidayIndex, dropPeriod }; |
|
m_AdditionalDrops.AddToTail( additionalDrop ); |
|
} |
|
} |
|
continue; |
|
} |
|
else if ( !Q_strcmp( pszName, "property_generators" ) ) |
|
{ |
|
SCHEMA_INIT_SUBSTEP( BCommonInitPropertyGeneratorsFromKV( m_pszName, &m_PropertyGenerators, pKVListItem, pVecErrors ) ); |
|
continue; |
|
} |
|
#endif // GC_DLL |
|
|
|
int iDef = 0; |
|
// First, see if we've got a loot list name, for embedded loot lists |
|
int iIdx = 0; |
|
if ( GetItemSchema()->GetLootListByName( pszName, &iIdx ) ) |
|
{ |
|
// HACKY: Store loot list indices as negatives, starting from -1, because 0 is a valid item index |
|
iDef = 0 - 1 - iIdx; |
|
} |
|
else |
|
{ |
|
// Not a loot list. See if it's an item index. Check the first character. |
|
if ( pszName[0] >= '0' && pszName[0] <= '9' ) |
|
{ |
|
iDef = atoi( pszName ); |
|
} |
|
else |
|
{ |
|
// Not a number. See if we can find an item def with a matching name. |
|
const CEconItemDefinition *pDef = GetItemSchema()->GetItemDefinitionByName( pszName ); |
|
if ( pDef ) |
|
{ |
|
iDef = pDef->GetDefinitionIndex(); |
|
} |
|
|
|
SCHEMA_INIT_CHECK( |
|
pDef != NULL, |
|
"Loot list %s: Item definition \"%s\" was not found", m_pszName, pszName ); |
|
} |
|
} |
|
|
|
// Default to the start dropping at the start of time and end dropping at the end of time |
|
drop_period_t dropPeriod = { RTime32(0), ~RTime32(0) }; |
|
|
|
// Make sure we never put non-enabled items into loot lists |
|
if ( iDef > 0 ) |
|
{ |
|
const CEconItemDefinition *pItemDef = GetItemSchema()->GetItemDefinition( iDef ); |
|
SCHEMA_INIT_CHECK( |
|
pItemDef != NULL, |
|
"Loot list %s: Item definition index \"%s\" (%d) was not found", m_pszName, pszName, iDef ); |
|
|
|
static CSchemaAttributeDefHandle pAttribDef_StartDropDate( "start drop date" ); |
|
static CSchemaAttributeDefHandle pAttribDef_EndDropDate( "end drop date" ); |
|
|
|
CAttribute_String value; |
|
// Check for start drop date attribute on this item |
|
if ( FindAttribute( pItemDef, pAttribDef_StartDropDate, &value ) ) |
|
{ |
|
const char* pszStartDate = value.value().c_str(); |
|
dropPeriod.m_DropStartDate = CRTime::RTime32FromFmtString( "YYYY-MM-DD hh:mm:ss" , pszStartDate ); |
|
|
|
// Check that if we convert back to a string, we get the same value |
|
char rtimeBuf[k_RTimeRenderBufferSize]; |
|
SCHEMA_INIT_CHECK( |
|
Q_strcmp( CRTime::RTime32ToString( dropPeriod.m_DropStartDate, rtimeBuf ), pszStartDate ) == 0, |
|
"Malformed start drop date \"%s\" for item %s. Must be of the form \"YYYY-MM-DD hh:mm:ss\"", |
|
pszStartDate, pItemDef->GetDefinitionName() ); |
|
} |
|
|
|
// Check for end drop date attribute on this item |
|
if ( FindAttribute( pItemDef, pAttribDef_EndDropDate, &value ) ) |
|
{ |
|
const char* pszEndDate = value.value().c_str(); |
|
dropPeriod.m_DropEndDate = CRTime::RTime32FromFmtString( "YYYY-MM-DD hh:mm:ss" , pszEndDate ); |
|
|
|
// Check that if we convert back to a string, we get the same value |
|
char rtimeBuf[k_RTimeRenderBufferSize]; |
|
SCHEMA_INIT_CHECK( |
|
Q_strcmp( CRTime::RTime32ToString( dropPeriod.m_DropEndDate, rtimeBuf ), pszEndDate ) == 0, |
|
"Malformed end drop date \"%s\" for item %s. Must be of the form \"YYYY-MM-DD hh:mm:ss\"", pszEndDate, pItemDef->GetDefinitionName() ); |
|
} |
|
|
|
#ifdef GC_DLL |
|
if ( pItemDef ) |
|
{ |
|
SCHEMA_INIT_CHECK( |
|
true == pItemDef->BEnabled(), |
|
"Loot list %s: Item definition \"%s\" (%d) isn't enabled, not allowed in loot lists", m_pszName, pItemDef->GetDefinitionName(), iDef ); |
|
} |
|
#endif // GC_DLL |
|
} |
|
|
|
float fItemWeight = 0.f; |
|
#ifdef GC_DLL |
|
fItemWeight = bCollectionLootList ? 1.0f : pKVListItem->GetFloat(); |
|
SCHEMA_INIT_CHECK( |
|
fItemWeight > 0.0f, |
|
"Loot list %s: Item definition index \"%s\" (%d) has invalid weight %.2f", m_pszName, pszName, iDef, fItemWeight ); |
|
#endif // GC_DLL |
|
|
|
// Add this item |
|
drop_item_t dropItem = { iDef, fItemWeight, dropPeriod }; |
|
m_DropList.AddToTail( dropItem ); |
|
} |
|
|
|
#ifdef GC_DLL |
|
|
|
int nNumTimeLimitedItems = 0; |
|
FOR_EACH_VEC( m_DropList, i ) |
|
{ |
|
// If either the start date or the end date is set, tally it up as a time limited item |
|
if( m_DropList[i].m_dropPeriod.m_DropStartDate != RTime32(0) || m_DropList[i].m_dropPeriod.m_DropEndDate != ~RTime32(0) ) |
|
{ |
|
++nNumTimeLimitedItems; |
|
} |
|
} |
|
|
|
// Verify that at least one item in a lootlist does not have a drop period that limits when it can drop. |
|
// This guarantees that we will always drop *something* |
|
SCHEMA_INIT_CHECK( m_DropList.Count() > nNumTimeLimitedItems, "Lootlist \"%s\" is made up entirely of limited-time items! At least one must not be time-limited.", m_pszName ); |
|
#endif |
|
|
|
return SCHEMA_INIT_SUCCESS(); |
|
} |
|
|
|
#ifdef GC_DLL |
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
static bool BContainsDuplicateItemDefs( const CUtlVector<CEconLootListDefinition::rolled_item_defs_t>& vecItemDefsA, const CUtlVector<CEconLootListDefinition::rolled_item_defs_t>& vecItemDefsB ) |
|
{ |
|
CUtlHashtable<const CEconItemDefinition *> hashItemDefs; |
|
|
|
auto BPopulateAndLookForDupes = [&] ( const CUtlVector<CEconLootListDefinition::rolled_item_defs_t>& vecItemDefs ) |
|
{ |
|
for ( const auto& rolledItemDef : vecItemDefs ) |
|
{ |
|
if ( hashItemDefs.HasElement( rolledItemDef.m_pItemDef ) ) |
|
return true; |
|
|
|
hashItemDefs.Insert( rolledItemDef.m_pItemDef ); |
|
} |
|
|
|
return false; |
|
}; |
|
|
|
return BPopulateAndLookForDupes( vecItemDefsA ) |
|
|| BPopulateAndLookForDupes( vecItemDefsB ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
bool CEconLootListDefinition::BGenerateSingleRollRandomItems( const CEconGameAccount *pGameAccount, bool bFreeAccount, CUtlVector<CEconItem *> *out_pvecItems, const CUtlVector< item_definition_index_t > *pVecAvoidItemDefs /*= NULL*/ ) const |
|
{ |
|
Assert( out_pvecItems ); |
|
|
|
// Where is our source of random numbers coming from? If we're a no-dupe list, |
|
// we want to have reproducible state so we use our account ID as a unique-ish |
|
// seed. |
|
// |
|
// Wrap the whole thing in a smart pointer so we clean up whenever/however we |
|
// leave. |
|
std::unique_ptr<IUniformRandomStream> pRandomStream( [=]() -> IUniformRandomStream * |
|
{ |
|
if ( !BIsInternalNoDupesLootList() || !pGameAccount ) |
|
return new CDefaultUniformRandomStream; |
|
|
|
|
|
CUniformRandomStream *pAccountUniformRandomStream = new CUniformRandomStream; |
|
pAccountUniformRandomStream->SetSeed( pGameAccount->Obj().m_unAccountID ); |
|
|
|
return pAccountUniformRandomStream; |
|
}() ); |
|
|
|
// Make however many passes through our loot list code until we've generated the |
|
// right number of passing sets. (Most loot lists let anything pass. Some specify |
|
// no duplicate definitions allowed.) |
|
CUtlVector<rolled_item_defs_t> vecCumulativeItemDefs; // total list of everything we've generated so far in any number of result sets |
|
CUtlVector<rolled_item_defs_t> vecItemDefs; // current list under evaluation |
|
int iNoDupesIterations = 0; |
|
|
|
// This actually isn't guaranteed to converge, and is guaranteed not to converge if |
|
// we set up a broken lootlist. We set a really high bar here to catch broken/pathologically |
|
// bad cases here without grinding the whole GC to a halt. |
|
enum { kUpperBoundIterationSanityCheck = 2000 }; |
|
int iTotalIterations = 0; |
|
|
|
while ( true ) |
|
{ |
|
// Don't runaway. |
|
iTotalIterations++; |
|
if ( iTotalIterations >= kUpperBoundIterationSanityCheck ) |
|
return false; |
|
|
|
// Generate all of our item defs and their lootlists |
|
vecItemDefs.Purge(); |
|
if ( !RollRandomItemsAndAdditionalItems( pRandomStream.get(), bFreeAccount, &vecItemDefs, pVecAvoidItemDefs ) ) |
|
return false; |
|
|
|
// If we don't care about dupes and we got any results at all we're done. |
|
if ( !BIsInternalNoDupesLootList() ) |
|
break; |
|
|
|
// If we do care about dupes and we have some, ignore this set of items. |
|
if ( BContainsDuplicateItemDefs( vecItemDefs, vecCumulativeItemDefs ) ) |
|
continue; |
|
|
|
// Did we get to the right result set? |
|
iNoDupesIterations++; |
|
if ( iNoDupesIterations > m_iNoDupesIterations ) |
|
break; |
|
|
|
// Store off the list of definition indices we've already used them so they don't get reused |
|
// in a later set. |
|
vecCumulativeItemDefs.AddVectorToTail( vecItemDefs ); |
|
} |
|
|
|
// If we get down to here, we expect that we've rolled at least one item def |
|
Assert( vecItemDefs.Count() > 0 ); |
|
FOR_EACH_VEC( vecItemDefs, i ) |
|
{ |
|
const rolled_item_defs_t& rolledDef = vecItemDefs[i]; |
|
Assert( rolledDef.m_pItemDef ); |
|
Assert( rolledDef.m_vecAffectingLootLists.Count() > 0 ); |
|
|
|
// Create the items |
|
CEconItem *pItem = GEconManager()->GetItemFactory().CreateSpecificItem( pGameAccount, rolledDef.m_pItemDef->GetDefinitionIndex() ); |
|
out_pvecItems->AddToTail( pItem ); |
|
|
|
// Go through and let all the affecting lootlists attach their attributes to the item |
|
FOR_EACH_VEC( rolledDef.m_vecAffectingLootLists, j ) |
|
{ |
|
const CEconLootListDefinition *pLootList = rolledDef.m_vecAffectingLootLists[j]; |
|
Assert( pLootList ); |
|
if ( !pLootList->BAttachLootListAttributes( pGameAccount, pItem ) ) |
|
return false; |
|
} |
|
} |
|
|
|
return ( out_pvecItems->Count() > 0 ); |
|
} |
|
|
|
class CRollSimulator |
|
{ |
|
public: |
|
|
|
CRollSimulator() |
|
: m_mapRarityCounts( StringLessThan ) |
|
, m_mapCounts( DefLessFunc( item_definition_index_t ) ) |
|
, m_mapUnusualHatEffectsCount( DefLessFunc( uint32 ) ) |
|
, m_mapUnusualTauntEffectsCount( DefLessFunc( uint32 ) ) |
|
, m_nNumIters( 0 ) |
|
{} |
|
|
|
void RollLootlist( const char* pszLootListName, int nRolls, bool bWipePreviousResults = false ) |
|
{ |
|
const CEconLootListDefinition* pLootlist = GetItemSchema()->GetLootListByName( pszLootListName ); |
|
|
|
if ( !pLootlist ) |
|
{ |
|
EmitInfo( SPEW_CONSOLE, SPEW_ALWAYS, LOG_ALWAYS, "Invalid lootlist \"%s\".\n", pszLootListName ); |
|
return; |
|
} |
|
|
|
// Clear out results first? |
|
if ( bWipePreviousResults ) |
|
{ |
|
m_mapCounts.Purge(); |
|
m_mapRarityCounts.Purge(); |
|
m_DropsMaxes.Clear(); |
|
m_DropsTotals.Clear(); |
|
} |
|
|
|
while( nRolls-- ) |
|
{ |
|
AutoYield(); |
|
CUtlVector<CEconItem *> vecItems; |
|
pLootlist->BGenerateSingleRollRandomItems( NULL, false, &vecItems ); |
|
|
|
// Tally up what we got. |
|
for( auto pItem : vecItems ) |
|
{ |
|
AutoYield(); |
|
// Insert item def if we need |
|
item_definition_index_t nDefIndex = pItem->GetDefinitionIndex(); |
|
auto idx = m_mapCounts.Find( nDefIndex ); |
|
if ( m_mapCounts.InvalidIndex() == idx ) |
|
{ |
|
idx = m_mapCounts.Insert( nDefIndex ); |
|
} |
|
|
|
// Insert rarity if needed |
|
const CEconItemRarityDefinition* pItemRarity = GetItemSchema()->GetRarityDefinition( pItem->GetItemDefinition()->GetRarity() ); |
|
if ( pItemRarity ) |
|
{ |
|
const char* pszRarity = GGCGameBase()->LocalizeToken( pItemRarity->GetLocKey() , k_Lang_English ); |
|
auto rarityIdx = m_mapRarityCounts.Find( pszRarity ); |
|
if ( m_mapRarityCounts.InvalidIndex() == rarityIdx ) |
|
{ |
|
rarityIdx = m_mapRarityCounts.Insert( pszRarity, 0 ); |
|
} |
|
|
|
m_mapRarityCounts[ rarityIdx ] += 1; |
|
} |
|
|
|
// Count |
|
DropResult_t& result = m_mapCounts[ idx ]; |
|
++result.m_nRollCount; |
|
++m_DropsTotals.m_nRollCount; |
|
m_DropsMaxes.m_nRollCount = Max( m_DropsMaxes.m_nRollCount, result.m_nRollCount ); |
|
|
|
// Strange count |
|
if ( BIsItemStrange( pItem ) ) |
|
{ |
|
++result.m_nStrangeCount; |
|
++m_DropsTotals.m_nStrangeCount; |
|
m_DropsMaxes.m_nStrangeCount = Max( m_DropsMaxes.m_nStrangeCount, result.m_nStrangeCount ); |
|
} |
|
|
|
// Unusual count |
|
static CSchemaAttributeDefHandle pAttrDef_ParticleEffect( "attach particle effect" ); |
|
static CSchemaAttributeDefHandle pAttrDef_TauntUnusualAttr( "on taunt attach particle index" ); |
|
if ( pAttrDef_ParticleEffect && pAttrDef_TauntUnusualAttr ) |
|
{ |
|
uint32 nUnusualHatValue = 0; |
|
uint32 nUnusualTauntValue = 0; |
|
pItem->FindAttribute( pAttrDef_ParticleEffect, &nUnusualHatValue ); |
|
pItem->FindAttribute( pAttrDef_TauntUnusualAttr, &nUnusualTauntValue ); |
|
// Cant use quality cause of old legacy items. Quality is just a quick test |
|
if ( nUnusualHatValue != 0 || nUnusualTauntValue != 0 ) |
|
{ |
|
++result.m_nUnusualCount; |
|
++m_DropsTotals.m_nUnusualCount; |
|
m_DropsMaxes.m_nUnusualCount = Max( m_DropsMaxes.m_nUnusualCount, result.m_nUnusualCount ); |
|
} |
|
|
|
if ( nUnusualHatValue ) |
|
{ |
|
nUnusualHatValue = (uint32)((float&)nUnusualHatValue); |
|
auto idx = m_mapUnusualHatEffectsCount.Find( nUnusualHatValue ); |
|
if ( idx == m_mapUnusualHatEffectsCount.InvalidIndex() ) |
|
{ |
|
idx = m_mapUnusualHatEffectsCount.Insert( nUnusualHatValue, 0 ); |
|
} |
|
|
|
++m_mapUnusualHatEffectsCount[ idx ]; |
|
} |
|
|
|
if ( nUnusualTauntValue ) |
|
{ |
|
nUnusualTauntValue = (uint32)((float&)nUnusualTauntValue); |
|
auto idx = m_mapUnusualTauntEffectsCount.Find( nUnusualTauntValue ); |
|
if ( idx == m_mapUnusualTauntEffectsCount.InvalidIndex() ) |
|
{ |
|
idx = m_mapUnusualTauntEffectsCount.Insert( nUnusualTauntValue, 0 ); |
|
} |
|
|
|
++m_mapUnusualTauntEffectsCount[ idx ]; |
|
} |
|
} |
|
} |
|
|
|
// Delete what we got |
|
vecItems.PurgeAndDeleteElements(); |
|
} |
|
} |
|
|
|
void PrintRarityBreakdwn() |
|
{ |
|
EmitInfo( SPEW_CONSOLE, SPEW_ALWAYS, LOG_ALWAYS, "-- Rarity breakdown:\n" ); |
|
int nMaxDigits = 0; |
|
while( m_mapRarityCounts.Count() ) |
|
{ |
|
// Go through and print the most rolled to least rolled rarities |
|
auto maxIdx = m_mapRarityCounts.InvalidIndex(); |
|
|
|
// Find the most hit |
|
FOR_EACH_MAP_FAST( m_mapRarityCounts, i ) |
|
{ |
|
AutoYield(); |
|
if ( maxIdx == m_mapRarityCounts.InvalidIndex() || m_mapRarityCounts[ i ] > m_mapRarityCounts[ maxIdx ] ) |
|
{ |
|
maxIdx = i; |
|
} |
|
|
|
nMaxDigits = Max( nMaxDigits, NumDigits( m_mapRarityCounts[ maxIdx ] ) ); |
|
} |
|
|
|
EmitInfo( SPEW_CONSOLE, SPEW_ALWAYS, LOG_ALWAYS, "%9s %*d %.3f%%\n", |
|
m_mapRarityCounts.Key( maxIdx ), nMaxDigits, m_mapRarityCounts[ maxIdx ], |
|
100.f * (float)m_mapRarityCounts[ maxIdx ] / m_DropsTotals.m_nRollCount ); |
|
m_mapRarityCounts.RemoveAt( maxIdx ); |
|
} |
|
|
|
EmitInfo( SPEW_CONSOLE, SPEW_ALWAYS, LOG_ALWAYS, "%-*s %*s %*s\n", 9 + 1 + NumDigits( m_DropsMaxes.m_nRollCount ) + 6, |
|
"-- Item breakdown", NumDigits( m_DropsMaxes.m_nStrangeCount ) + 1, "S", NumDigits( m_DropsMaxes.m_nUnusualCount ), "U" ); |
|
|
|
} |
|
|
|
void PrintUnusualCounts() |
|
{ |
|
EmitInfo( SPEW_CONSOLE, SPEW_ALWAYS, LOG_ALWAYS, "-- Unusual Hats breakdown:\n" ); |
|
int nTotal = PrintUnusualsForType( m_mapUnusualHatEffectsCount ); |
|
EmitInfo( SPEW_CONSOLE, SPEW_ALWAYS, LOG_ALWAYS, "-- %d total unusual hats\n", nTotal ); |
|
|
|
EmitInfo( SPEW_CONSOLE, SPEW_ALWAYS, LOG_ALWAYS, "-- Unusual Taunts breakdown:\n" ); |
|
nTotal = PrintUnusualsForType( m_mapUnusualTauntEffectsCount ); |
|
EmitInfo( SPEW_CONSOLE, SPEW_ALWAYS, LOG_ALWAYS, "-- %d total unusual taunts\n", nTotal ); |
|
} |
|
|
|
void PrintTotals() |
|
{ |
|
while( m_mapCounts.Count() ) |
|
{ |
|
// Go through and print the most rolled to least rolled |
|
auto maxIdx = m_mapCounts.InvalidIndex(); |
|
|
|
// Find the most hit |
|
FOR_EACH_MAP_FAST( m_mapCounts, i ) |
|
{ |
|
AutoYield(); |
|
if ( maxIdx == m_mapCounts.InvalidIndex() || m_mapCounts[ i ].m_nRollCount > m_mapCounts[ maxIdx ].m_nRollCount ) |
|
{ |
|
maxIdx = i; |
|
} |
|
} |
|
|
|
DropResult_t& result = m_mapCounts[ maxIdx ]; |
|
CEconItemDefinition *pItemDef = GetItemSchema()->GetItemDefinition( m_mapCounts.Key( maxIdx ) ); |
|
|
|
const CEconItemRarityDefinition* pItemRarity = GetItemSchema()->GetRarityDefinition( pItemDef->GetRarity() ); |
|
const char* pszRarity = pItemRarity ? GGCGameBase()->LocalizeToken( pItemRarity->GetLocKey() , k_Lang_English ) : ""; |
|
CFmtStr rollString( "%*d %2.3f%%", NumDigits( m_DropsMaxes.m_nRollCount ), result.m_nRollCount, |
|
100.f * (float)result.m_nRollCount / m_DropsTotals.m_nRollCount ); |
|
CFmtStr strangeString( "%*d", NumDigits( m_DropsMaxes.m_nStrangeCount ), result.m_nStrangeCount ); |
|
CFmtStr unusualString( "%*d", NumDigits( m_DropsMaxes.m_nUnusualCount ), result.m_nUnusualCount ); |
|
const char* pszItemname = GGCGameBase()->LocalizeToken( pItemDef->GetCustomPainkKitDefinition() ? pItemDef->GetCustomPainkKitDefinition()->GetName() : pItemDef->GetItemBaseName(), k_Lang_English ); |
|
|
|
EmitInfo( SPEW_CONSOLE, SPEW_ALWAYS, LOG_ALWAYS, "%9s %s %s %s %s\n", pszRarity, rollString.Get(), strangeString.Get(), unusualString.Get(), pszItemname ); |
|
|
|
m_mapCounts.RemoveAt( maxIdx ); |
|
} |
|
|
|
EmitInfo( SPEW_CONSOLE, SPEW_ALWAYS, LOG_ALWAYS, "-- Total Items: %d Strange: %d (%.3f%%) Unusual: %d (%.3f%%)\n" |
|
, m_DropsTotals.m_nRollCount |
|
, m_DropsTotals.m_nStrangeCount |
|
, ( 100.f * (float)m_DropsTotals.m_nStrangeCount / m_DropsTotals.m_nRollCount ) |
|
, m_DropsTotals.m_nUnusualCount |
|
, ( 100.f * (float)m_DropsTotals.m_nUnusualCount / m_DropsTotals.m_nRollCount ) ); |
|
} |
|
|
|
struct DropResult_t |
|
{ |
|
DropResult_t() { Clear(); } |
|
|
|
void Clear() |
|
{ |
|
m_nStrangeCount = 0; |
|
m_nUnusualCount = 0; |
|
m_nRollCount = 0; |
|
} |
|
|
|
int m_nStrangeCount; |
|
int m_nUnusualCount; |
|
int m_nRollCount; |
|
}; |
|
|
|
const DropResult_t& GetTotalDrops() const { return m_DropsTotals; } |
|
const DropResult_t& GetMaxesDrops() const { return m_DropsMaxes; } |
|
|
|
private: |
|
|
|
int PrintUnusualsForType( CUtlMap< uint32, int >& mapUnusuals ) |
|
{ |
|
int nMaxDigits = 0; |
|
int nTotal = 0; |
|
FOR_EACH_MAP_FAST( mapUnusuals, i ) |
|
{ |
|
nTotal += mapUnusuals[ i ]; |
|
} |
|
|
|
while( mapUnusuals.Count() ) |
|
{ |
|
// Go through and print the most rolled to least rolled unusual effects |
|
auto maxIdx = mapUnusuals.InvalidIndex(); |
|
|
|
// Find the most hit |
|
FOR_EACH_MAP_FAST( mapUnusuals, i ) |
|
{ |
|
AutoYield(); |
|
if ( maxIdx == mapUnusuals.InvalidIndex() || mapUnusuals[ i ] > mapUnusuals[ maxIdx ] ) |
|
{ |
|
maxIdx = i; |
|
} |
|
|
|
nMaxDigits = Max( nMaxDigits, NumDigits( mapUnusuals[ maxIdx ] ) ); |
|
} |
|
|
|
char particleNameEntry[128]; |
|
Q_snprintf( particleNameEntry, ARRAYSIZE( particleNameEntry ), "#Attrib_Particle%d", mapUnusuals.Key( maxIdx ) ); |
|
const char* pszParticleName = GGCGameBase()->LocalizeToken( particleNameEntry, k_Lang_English ); |
|
|
|
EmitInfo( SPEW_CONSOLE, SPEW_ALWAYS, LOG_ALWAYS, "%*d %.3f%% %s (%d)\n", |
|
nMaxDigits, |
|
mapUnusuals[ maxIdx ], |
|
100.f * (float)mapUnusuals[ maxIdx ] / nTotal, |
|
pszParticleName, |
|
mapUnusuals.Key( maxIdx ) ); |
|
|
|
mapUnusuals.RemoveAt( maxIdx ); |
|
} |
|
|
|
return nTotal; |
|
} |
|
|
|
void AutoYield() |
|
{ |
|
if ( ++m_nNumIters % 100 == 0 ) |
|
{ |
|
if ( GJobCur().BYieldIfNeeded() ) |
|
{ |
|
// If we re-entered logon surge we should go away for a while |
|
while ( GGCGameBase()->BIsInLogonSurge() ) |
|
{ |
|
GJobCur().BYieldingWaitOneFrame(); |
|
} |
|
} |
|
} |
|
}; |
|
|
|
int NumDigits( int nNumber ) |
|
{ |
|
int digits = 0; |
|
|
|
if ( nNumber <= 0) |
|
{ |
|
digits = 1; |
|
} |
|
|
|
while ( nNumber ) |
|
{ |
|
nNumber /= 10; |
|
++digits; |
|
} |
|
|
|
return digits; |
|
} |
|
|
|
|
|
|
|
CUtlMap< item_definition_index_t, DropResult_t > m_mapCounts; |
|
CUtlMap< uint32, int > m_mapUnusualHatEffectsCount; |
|
CUtlMap< uint32, int > m_mapUnusualTauntEffectsCount; |
|
// Rarity name is the key |
|
CUtlMap< const char*, int > m_mapRarityCounts; |
|
DropResult_t m_DropsTotals; |
|
DropResult_t m_DropsMaxes; |
|
size_t m_nNumIters; |
|
}; |
|
|
|
GC_CON_COMMAND( simulate_lootlist_contents, "<lootlist> <iterations> Check item distribution from a given lootlist n times" ) |
|
{ |
|
if ( !BCheckArgs( 2, args, simulate_lootlist_contents_command ) ) |
|
return; |
|
|
|
const char* pszLootListName = args[1]; |
|
int nRolls = args.ArgC() == 3 ? atoi( args[2] ) : 1000; |
|
|
|
CRollSimulator simulator; |
|
simulator.RollLootlist( pszLootListName, nRolls ); |
|
simulator.PrintRarityBreakdwn(); |
|
simulator.PrintUnusualCounts(); |
|
simulator.PrintTotals(); |
|
} |
|
|
|
|
|
#ifdef GC_DLL |
|
class CItemSourceFinder |
|
{ |
|
public: |
|
CItemSourceFinder( const char* pszItemName ) |
|
: m_pItemDef( GetItemSchema()->GetItemDefinitionByName( pszItemName ) ) |
|
{ |
|
Assert( m_pItemDef ); |
|
if ( !m_pItemDef ) |
|
{ |
|
EG_ERROR( SPEW_CONSOLE, "%s is not a valid item", pszItemName ); |
|
return; |
|
} |
|
|
|
// Find out what series this crate belongs to. |
|
static CSchemaAttributeDefHandle pAttr_CrateSeries( "set supply crate series" ); |
|
if ( !pAttr_CrateSeries ) |
|
return; |
|
|
|
auto& mapItemDefs = GetItemSchema()->GetItemDefinitionMap(); |
|
auto& mapRevolvingLootlists = GetItemSchema()->GetRevolvingLootLists(); |
|
|
|
CUtlDict< int > dictSeenLootlists; |
|
|
|
// Look through all the item defs and see if any of them statically specify a lootlist that they want to open |
|
FOR_EACH_MAP_FAST( mapItemDefs, i ) |
|
{ |
|
const CEconItemDefinition* pSourceItemDef = mapItemDefs[ i ]; |
|
const CEconLootListDefinition* pLootlist = NULL; |
|
|
|
const CEconTool_Gift* pGift = pSourceItemDef->GetTypedEconTool< CEconTool_Gift >(); |
|
// Self-opening crate? |
|
if ( pGift ) |
|
{ |
|
pLootlist = GetItemSchema()->GetLootListByName( pGift->GetLootListName() ); |
|
} |
|
else // Crate with an item series? |
|
{ |
|
int iCrateSeries; |
|
{ |
|
float fCrateSeries; // crate series ID is stored as a float internally because we hate ourselves |
|
if ( !FindAttribute_UnsafeBitwiseCast<attrib_value_t>( pSourceItemDef, pAttr_CrateSeries, &fCrateSeries ) || fCrateSeries == 0.0f ) |
|
continue; |
|
|
|
iCrateSeries = fCrateSeries; |
|
} |
|
|
|
auto idx = mapRevolvingLootlists.Find( iCrateSeries ); |
|
if ( idx == mapRevolvingLootlists.InvalidIndex() ) |
|
continue; |
|
|
|
pLootlist = GetItemSchema()->GetLootListByName( mapRevolvingLootlists[ idx ] ); |
|
} |
|
|
|
if ( !pLootlist ) |
|
continue; |
|
|
|
// Mark that we've seen this lootlist already |
|
dictSeenLootlists.Insert( pLootlist->GetName() ); |
|
|
|
DropSource_t& source = m_vecSources[ m_vecSources.AddToTail() ]; |
|
source.m_pDroppingItem = pSourceItemDef; |
|
ChanceForItemFromLootlist( m_pItemDef, pLootlist, source.lootlistSource ); |
|
} |
|
|
|
CEconItemDefinition* pCrateItemDef = GetItemSchema()->GetItemDefinitionByName( "Supply Crate" ); |
|
Assert( pCrateItemDef ); |
|
|
|
// Go through all the revolving lootlists and see if they have the item. Assume that |
|
// they're from a "Supply Crate". |
|
FOR_EACH_MAP_FAST( mapRevolvingLootlists, i ) |
|
{ |
|
if ( !pCrateItemDef ) |
|
continue; |
|
|
|
if ( mapRevolvingLootlists.Key( i ) <= 0 ) |
|
continue; |
|
|
|
auto pLootlist = GetItemSchema()->GetLootListByName( mapRevolvingLootlists[ i ] ); |
|
if ( !pLootlist ) |
|
continue; |
|
|
|
// This lootlist was on a different crate already |
|
if ( dictSeenLootlists.Find( pLootlist->GetName() ) != dictSeenLootlists.InvalidIndex() ) |
|
continue; |
|
|
|
DropSource_t& source = m_vecSources[ m_vecSources.AddToTail() ]; |
|
source.m_pDroppingItem = pCrateItemDef; |
|
ChanceForItemFromLootlist( m_pItemDef, pLootlist, source.lootlistSource ); |
|
} |
|
|
|
// Not available at all! |
|
if ( !m_vecSources.Count() ) |
|
{ |
|
EmitInfo( SPEW_CONSOLE, SPEW_ALWAYS, LOG_ALWAYS, "Not available from any sources!\n" ); |
|
return; |
|
} |
|
|
|
// Sort greatest % chance |
|
auto lambdaSort = [] ( DropSource_t const *pLHS, DropSource_t const *pRHS ) -> int |
|
{ |
|
return pLHS->lootlistSource.m_flChance < pRHS->lootlistSource.m_flChance; |
|
}; |
|
m_vecSources.Sort( lambdaSort ); |
|
} |
|
|
|
void PrintSources() |
|
{ |
|
FOR_EACH_VEC( m_vecSources, i ) |
|
{ |
|
m_vecSources[ i ].PrintSources(); |
|
} |
|
} |
|
|
|
bool BDropsFromLootlist( const CEconLootListDefinition* pLootlist ) |
|
{ |
|
FOR_EACH_VEC( m_vecSources, i ) |
|
{ |
|
if ( m_vecSources[ i ].lootlistSource.BDropsFromLootlist( pLootlist ) ) |
|
return true; |
|
} |
|
|
|
return false; |
|
} |
|
|
|
private: |
|
|
|
struct DropSource_t |
|
{ |
|
void PrintSources() |
|
{ |
|
if ( lootlistSource.m_flChance == 0.f ) |
|
return; |
|
|
|
bool bSelfOpening = m_pDroppingItem->GetTypedEconTool< CEconTool_Gift >() != NULL; |
|
|
|
// Print the name of the item, and whether it's a self-opening item, or a crate |
|
EmitInfo( SPEW_CONSOLE, SPEW_ALWAYS, LOG_ALWAYS, "%3.5f%% %s (%d - %s)\n", |
|
lootlistSource.m_flChance * 100.f, |
|
m_pDroppingItem->GetDefinitionName(), |
|
m_pDroppingItem->GetDefinitionIndex(), |
|
bSelfOpening ? "Self-Opening" : "Crate/Case" ); |
|
|
|
// Print all the sources |
|
lootlistSource.PrintSources( 1 ); |
|
|
|
EmitInfo( SPEW_CONSOLE, SPEW_ALWAYS, LOG_ALWAYS, "\n" ); |
|
} |
|
|
|
struct LootListSource_t |
|
{ |
|
LootListSource_t() |
|
: m_flChance( 0.f ) |
|
{} |
|
|
|
bool BDropsFromLootlist( const CEconLootListDefinition* pLootlist ) |
|
{ |
|
// Skip no-chance entries |
|
if ( m_flChance == 0.f ) |
|
return false; |
|
|
|
|
|
if ( m_pDroppingLootlist == pLootlist ) |
|
return true; |
|
|
|
FOR_EACH_VEC( m_vecLootlistSources, i ) |
|
{ |
|
if ( m_vecLootlistSources[ i ].BDropsFromLootlist( pLootlist ) ) |
|
return true; |
|
} |
|
|
|
return false; |
|
} |
|
|
|
void PrintSources( int nInset ) |
|
{ |
|
// Skip no-chance entries |
|
if ( m_flChance == 0.f ) |
|
return; |
|
|
|
auto& mapRevolvingLootlists = GetItemSchema()->GetRevolvingLootLists(); |
|
int nRevolvingIdx = mapRevolvingLootlists.InvalidIndex(); |
|
|
|
// Check if our lootlist is one of the revolving lootlists. If so, we want |
|
// to print out its index within revolving_lootlists, so we can map that to |
|
// the attribute value of supply_crate_series (187) |
|
FOR_EACH_MAP_FAST( mapRevolvingLootlists, i ) |
|
{ |
|
if ( V_stricmp( mapRevolvingLootlists[ i ], m_pDroppingLootlist->GetName() ) == 0 ) |
|
{ |
|
nRevolvingIdx = mapRevolvingLootlists.Key( i ); |
|
break; |
|
} |
|
} |
|
|
|
if ( nRevolvingIdx != mapRevolvingLootlists.InvalidIndex() && nRevolvingIdx > 0 ) |
|
{ |
|
// It's in revolving_lootlists. Print its index in there. |
|
EmitInfo( SPEW_CONSOLE, SPEW_ALWAYS, LOG_ALWAYS, "%*.5f%% (%d) %s\n", |
|
4 * nInset + 5, |
|
m_flChance * 100.f, |
|
nRevolvingIdx, |
|
m_pDroppingLootlist->GetName() ); |
|
} |
|
else |
|
{ |
|
// Not in the revolving lootlist |
|
EmitInfo( SPEW_CONSOLE, SPEW_ALWAYS, LOG_ALWAYS, "%*.5f%% %s\n", |
|
4 * nInset + 5, |
|
m_flChance * 100.f, |
|
m_pDroppingLootlist->GetName() ); |
|
} |
|
|
|
// Sort greatest % chance |
|
auto lambdaSort = [] ( LootListSource_t const *pLHS, LootListSource_t const *pRHS ) -> int |
|
{ |
|
return pLHS->m_flChance < pRHS->m_flChance; |
|
}; |
|
|
|
m_vecLootlistSources.Sort( lambdaSort ); |
|
|
|
// Print out children indented a lil bit |
|
FOR_EACH_VEC( m_vecLootlistSources, i ) |
|
{ |
|
m_vecLootlistSources[i].PrintSources( nInset + 1 ); |
|
} |
|
} |
|
|
|
float m_flChance; |
|
const CEconLootListDefinition* m_pDroppingLootlist; |
|
|
|
CCopyableUtlVector< LootListSource_t > m_vecLootlistSources; |
|
}; |
|
|
|
const CEconItemDefinition* m_pDroppingItem; |
|
LootListSource_t lootlistSource; |
|
}; |
|
|
|
float ChanceForItemFromLootlist( const CEconItemDefinition* pItemDef, const CEconLootListDefinition* pLootlist, DropSource_t::LootListSource_t& lootlistSource ) |
|
{ |
|
auto& vecContents = pLootlist->GetLootListContents(); |
|
// Accumulate our chance of dropping the specified item |
|
lootlistSource.m_flChance = 0.f; |
|
lootlistSource.m_pDroppingLootlist = pLootlist; |
|
|
|
// Gather the items in this lootlist that we're able to roll for at this time |
|
float flTotalWeight = 0.f; |
|
CUtlVector<CEconLootListDefinition::drop_item_t> vecValidContents; |
|
FOR_EACH_VEC( vecContents, i ) |
|
{ |
|
if( !vecContents[ i ].m_dropPeriod.IsValidForTime( CRTime::RTime32TimeCur() ) ) |
|
continue; |
|
|
|
flTotalWeight += vecContents[ i ].m_flWeight; |
|
vecValidContents.AddToTail( vecContents[ i ] ); |
|
} |
|
|
|
// Go through valid contents, and see if the specified item is in there |
|
FOR_EACH_VEC( vecValidContents, i ) |
|
{ |
|
const int iItemDef = vecValidContents[ i ].m_iItemOrLootlistDef; |
|
const float flChance = vecValidContents[ i ].m_flWeight / flTotalWeight; |
|
|
|
if ( iItemDef < 0 ) // Lootlist |
|
{ |
|
int iLLIndex = (iItemDef * -1) - 1; |
|
auto pSubLootlist = GetItemSchema()->GetLootListByIndex( iLLIndex ); |
|
|
|
// One of our sub-lootlists might drop it. Add in it's chance within |
|
// the sub-lootlist scaled by the chance to roll that sub-lootlist. |
|
auto& subSource = lootlistSource.m_vecLootlistSources[ lootlistSource.m_vecLootlistSources.AddToTail() ]; |
|
lootlistSource.m_flChance += ChanceForItemFromLootlist( pItemDef, pSubLootlist, subSource ) * flChance; |
|
} |
|
else if ( pItemDef->GetDefinitionIndex() == iItemDef ) |
|
{ |
|
// We drop it! Add the chance |
|
lootlistSource.m_flChance += flChance; |
|
} |
|
} |
|
|
|
// Treat additional drops just the same as nested lootlists. |
|
auto& vecAdditionalDrops = pLootlist->GetAdditionalDrops(); |
|
FOR_EACH_VEC( vecAdditionalDrops, i ) |
|
{ |
|
auto& additionalDrop = vecAdditionalDrops[ i ]; |
|
if ( !additionalDrop.m_dropPeriod.IsValidForTime( CRTime::RTime32TimeCur() ) ) |
|
continue; |
|
|
|
auto pSubLootlist = GetItemSchema()->GetLootListByName( additionalDrop.m_pszLootListDefName ); |
|
auto& subSource = lootlistSource.m_vecLootlistSources[ lootlistSource.m_vecLootlistSources.AddToTail() ]; |
|
lootlistSource.m_flChance += ChanceForItemFromLootlist( pItemDef, pSubLootlist, subSource ) * additionalDrop.m_fChance; |
|
} |
|
|
|
// Return the total chance |
|
return lootlistSource.m_flChance; |
|
} |
|
|
|
CUtlVector< DropSource_t > m_vecSources; |
|
const CEconItemDefinition* m_pItemDef; |
|
}; |
|
|
|
GC_CON_COMMAND( item_sources, "Lists the sources for obtaining a list of specific items" ) |
|
{ |
|
if ( !BCheckArgs( 1, args, item_sources_command ) ) |
|
return; |
|
|
|
for ( int i=1; i < args.ArgC(); ++i ) |
|
{ |
|
const char* pszItemName = args[i]; |
|
const CEconItemDefinition* pItemDef = GetItemSchema()->GetItemDefinitionByName( pszItemName ); |
|
if ( pItemDef ) |
|
{ |
|
EmitInfo( SPEW_CONSOLE, SPEW_ALWAYS, LOG_ALWAYS, "\nChecking items for lootlists containing (%d) %s...\n", pItemDef->GetDefinitionIndex(), pItemDef->GetDefinitionName() ); |
|
CItemSourceFinder source( pszItemName ); |
|
source.PrintSources(); |
|
} |
|
else |
|
{ |
|
EmitInfo( SPEW_CONSOLE, SPEW_ALWAYS, LOG_ALWAYS, "\n\"%s\" is not a valid item name\n", pszItemName ); |
|
} |
|
} |
|
} |
|
|
|
GC_CON_COMMAND( list_keys, "Lists all the keys in the schema" ) |
|
{ |
|
EmitInfo( SPEW_CONSOLE, SPEW_ALWAYS, LOG_ALWAYS, "Spewing keys...\n" ); |
|
|
|
auto& mapItemDefs = GetItemSchema()->GetItemDefinitionMap(); |
|
FOR_EACH_MAP_FAST( mapItemDefs, i ) |
|
{ |
|
const CEconItemDefinition* pItemDef = mapItemDefs[ i ]; |
|
if ( !pItemDef || !pItemDef->GetEconTool() || ( Q_strcmp( pItemDef->GetEconTool()->GetTypeName(), "decoder_ring" ) != 0 ) ) |
|
{ |
|
continue; |
|
} |
|
|
|
EmitInfo( SPEW_CONSOLE, SPEW_ALWAYS, LOG_ALWAYS, "%llu - %s\n", pItemDef->GetDefinitionIndex(), pItemDef->GetItemDefinitionName() ); |
|
} |
|
} |
|
|
|
ConVar case_behavior_rolls_per_lootlist( "case_behavior_rolls_per_lootlist", "1000000", FCVAR_REPLICATED, "How many times to roll a lootlist" ); |
|
class CJobCaseBehaviorCheck : public CGCGameBaseJob |
|
{ |
|
public: |
|
CJobCaseBehaviorCheck() |
|
: CGCGameBaseJob( GGCGameBase() ) |
|
{} |
|
|
|
virtual bool BYieldingRunGCJob() |
|
{ |
|
// Wait until logon surge ends to get going |
|
while ( GGCGameBase()->BIsInLogonSurge() ) |
|
{ |
|
GJobCur().BYieldingWaitOneFrame(); |
|
} |
|
|
|
CRollSimulator simulator; |
|
|
|
// Convert the hash to something readable |
|
char pchSHAHex[41]; |
|
memset( pchSHAHex, 0, sizeof( pchSHAHex ) ); |
|
V_binarytohex( GetItemSchema()->GetSchemaSHA().m_shaDigest, 20, pchSHAHex, 41 ); |
|
|
|
RTime32 now = CRTime::RTime32TimeCur(); |
|
int nNewRecords = 0; |
|
|
|
EmitInfo( SPEW_CONSOLE, SPEW_ALWAYS, LOG_ALWAYS, "Beginning CJobCaseBehaviorCheck\n" ); |
|
|
|
auto& lootlists = GetItemSchema()->GetRevolvingLootLists(); |
|
|
|
// Go through all of the lootlists |
|
FOR_EACH_MAP( lootlists, i ) |
|
{ |
|
const CEconLootListDefinition* pEconLootlist = GetItemSchema()->GetLootListByName( lootlists[ i ] ); |
|
if ( pEconLootlist ) |
|
{ |
|
// Check if we have data for this lootlist on this hash already |
|
{ |
|
CSQLAccess sqlReadAccess; |
|
CUtlVector< CSchCaseBehavior > vecExistingResult; |
|
sqlReadAccess.AddBindParam( pchSHAHex ); |
|
sqlReadAccess.AddBindParam( i ); |
|
if ( sqlReadAccess.BYieldingReadRecordsWithWhereClause( &vecExistingResult, "SchemaSHA = ? and Series = ?", CSET_FULL( CSchCaseBehavior ) ) ) |
|
{ |
|
// Already got it? Skip the work |
|
if ( vecExistingResult.Count() ) |
|
{ |
|
continue; |
|
} |
|
} |
|
} |
|
|
|
// Roll 'em up |
|
simulator.RollLootlist( pEconLootlist->GetName(), case_behavior_rolls_per_lootlist.GetInt(), true ); |
|
|
|
// Insert record |
|
CSchCaseBehavior behavior; |
|
behavior.SetVarCharField( behavior.m_VarCharSchemaSHA, pchSHAHex, true, CSchCaseBehavior::k_iField_VarCharSchemaSHA ); |
|
behavior.m_RTime32Date = now; |
|
behavior.m_unSeries = i; |
|
behavior.SetVarCharField( behavior.m_VarCharLootListName, pEconLootlist->GetName(), true, CSchCaseBehavior::k_iField_VarCharLootListName ); |
|
behavior.m_fStrangeChance = 100.f * (float)simulator.GetTotalDrops().m_nStrangeCount / simulator.GetTotalDrops().m_nRollCount; |
|
behavior.m_fUnusualChance = 100.f * (float)simulator.GetTotalDrops().m_nUnusualCount / simulator.GetTotalDrops().m_nRollCount; |
|
|
|
CSQLAccess sqlAccess; |
|
sqlAccess.BBeginTransaction( "CJobCaseBehaviorCheck" ); |
|
sqlAccess.BYieldingInsertOrUpdateOnPK( &behavior ); |
|
sqlAccess.BCommitTransaction( true ); |
|
++nNewRecords; |
|
} |
|
|
|
BYieldIfNeeded(); |
|
} |
|
|
|
EmitInfo( SPEW_CONSOLE, SPEW_ALWAYS, LOG_ALWAYS, "Completed CJobUnusualChecker. %d updated records.\n", nNewRecords ); |
|
|
|
return true; |
|
} |
|
}; |
|
|
|
void CEconItemSchema::PerformCaseBehaviorCheck() |
|
{ |
|
CJob* pJob = new CJobCaseBehaviorCheck(); |
|
pJob->StartJobDelayed( NULL ); |
|
} |
|
#endif |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
bool CEconLootListDefinition::RollRandomItemsAndAdditionalItems( IUniformRandomStream *pRandomStream, bool bFreeAccount, CUtlVector<rolled_item_defs_t> *out_pVecRolledItems, const CUtlVector< item_definition_index_t > *pVecAvoidItemDefs ) const |
|
{ |
|
Assert( out_pVecRolledItems ); |
|
|
|
// Roll to see what items we get from this loot list. |
|
bool bCreatedItems = RollRandomItemDef( pRandomStream, bFreeAccount, out_pVecRolledItems, pVecAvoidItemDefs ); |
|
|
|
// Do we have additional drops? |
|
FOR_EACH_VEC( m_AdditionalDrops, i ) |
|
{ |
|
if ( !bCreatedItems ) |
|
break; |
|
|
|
// Is this within the period this is allowed to drop? |
|
if ( !m_AdditionalDrops[i].m_dropPeriod.IsValidForTime( CRTime::RTime32TimeCur() ) ) |
|
continue; |
|
|
|
// Does this only apply to premium accounts? |
|
if ( m_AdditionalDrops[i].m_bPremiumOnly && bFreeAccount ) |
|
continue; |
|
|
|
// Does this only apply on certain holidays? |
|
if ( m_AdditionalDrops[i].m_iRequiredHolidayIndex != kHoliday_None && !EconHolidays_IsHolidayActive( m_AdditionalDrops[i].m_iRequiredHolidayIndex, CRTime::RTime32TimeCur() ) ) |
|
continue; |
|
|
|
// Random chance is in the range 0-1 so generate a value in that range to "roll". |
|
if ( pRandomStream->RandomFloat( 0.0f, 1.0f ) > m_AdditionalDrops[i].m_fChance ) |
|
continue; |
|
|
|
// Roll! |
|
const char *pszAdditionalDropLootList = m_AdditionalDrops[i].m_pszLootListDefName; |
|
const CEconLootListDefinition *pLootListDef = GetItemSchema()->GetLootListByName( pszAdditionalDropLootList ); |
|
if ( pLootListDef == NULL ) |
|
{ |
|
AssertMsg2( false, "Loot list '%s' specifies unknown additional drop '%s'", GetName(), pszAdditionalDropLootList ); |
|
return false; |
|
} |
|
|
|
bCreatedItems &= pLootListDef->RollRandomItemsAndAdditionalItems( pRandomStream, bFreeAccount, out_pVecRolledItems ); |
|
} |
|
|
|
// If we failed to create some items, we might still have chosen some item defs before those failures. In that case, clear |
|
// out any choices we've made so far |
|
if ( !bCreatedItems ) |
|
{ |
|
out_pVecRolledItems->Purge(); |
|
} |
|
|
|
Assert( bCreatedItems == (out_pVecRolledItems->Count() > 0) ); |
|
|
|
return bCreatedItems; |
|
} |
|
|
|
|
|
bool CEconLootListDefinition::RollRandomItemDef( IUniformRandomStream *pRandomStream, bool bFreeAccount, CUtlVector<rolled_item_defs_t> *out_pVecRolledItems, const CUtlVector< item_definition_index_t > *pVecAvoidItemDefs ) const |
|
{ |
|
Assert( out_pVecRolledItems ); |
|
|
|
CUtlVector< rolled_item_defs_t > vecScratchDefs; |
|
CUtlVector<const drop_item_t*> vecValidDrops; |
|
|
|
// Gather the items in this lootlist that we're able to roll for at this time |
|
float flTotalWeight = 0.f; |
|
FOR_EACH_VEC( m_DropList, i ) |
|
{ |
|
if( !m_DropList[i].m_dropPeriod.IsValidForTime( CRTime::RTime32TimeCur() ) ) |
|
continue; |
|
|
|
// Skip any item defs that are in our avoid list (if we have one) |
|
if ( pVecAvoidItemDefs ) |
|
{ |
|
item_definition_index_t defIndex = m_DropList[i].m_iItemOrLootlistDef; |
|
if ( pVecAvoidItemDefs->Find( defIndex ) != pVecAvoidItemDefs->InvalidIndex() ) |
|
continue; |
|
} |
|
|
|
// If this is valid, add it to the list and add its weight to the total |
|
vecValidDrops.AddToTail( &m_DropList[i] ); |
|
flTotalWeight += m_DropList[i].m_flWeight; |
|
} |
|
|
|
// Roll to see what item drops. |
|
float flRand = pRandomStream->RandomFloat(0.0f, 1.0f) * flTotalWeight; |
|
|
|
float flAccum = 0.0f; |
|
FOR_EACH_VEC( vecValidDrops, i ) |
|
{ |
|
flAccum += vecValidDrops[i]->m_flWeight; |
|
if ( flRand <= flAccum ) |
|
{ |
|
const int iItemDef = vecValidDrops[i]->m_iItemOrLootlistDef; // not item_definition_index because it might also be a negative value to indicate a sub lootlist |
|
|
|
if ( iItemDef >= 0 ) |
|
{ |
|
const CEconItemDefinition* pItemDef = GetItemSchema()->GetItemDefinition( iItemDef ); |
|
if( !pItemDef ) |
|
return false; |
|
|
|
// Add the item def and the lootlist |
|
rolled_item_defs_t& rolledDef = vecScratchDefs[ vecScratchDefs.AddToTail() ]; |
|
rolledDef.m_pItemDef = pItemDef; |
|
} |
|
else |
|
{ |
|
// In the case where iItemDef is negative, it's a nested loot list. Ask that list to choose an item. |
|
// HACKY: Store loot list indices as negatives, starting from -1, because 0 is a valid item index |
|
int iLLIndex = (iItemDef * -1) - 1; |
|
const CEconLootListDefinition *pNestedLootList = GetItemSchema()->GetLootListByIndex( iLLIndex ); |
|
if ( !pNestedLootList ) |
|
return false; |
|
|
|
if( !pNestedLootList->RollRandomItemsAndAdditionalItems( pRandomStream, bFreeAccount, &vecScratchDefs, pVecAvoidItemDefs ) ) |
|
return false; |
|
} |
|
|
|
// Add ourselves to the list of affecting lootlists, so that we and all nested loot lists will affect this item |
|
// We intentionally don't do this in our calling function because we don't want to include additional drops. |
|
FOR_EACH_VEC( vecScratchDefs, j ) |
|
{ |
|
vecScratchDefs[j].m_vecAffectingLootLists.AddToTail( this ); |
|
} |
|
|
|
// We want to exit the loop here regardless of whether items were successfully so that we only perform a single |
|
// item-generating roll on this list. |
|
break; |
|
} |
|
} |
|
|
|
// Feed item defs, if they exist, back to our caller |
|
out_pVecRolledItems->AddVectorToTail( vecScratchDefs ); |
|
|
|
// Did we successfully create any items? |
|
return ( vecScratchDefs.Count() > 0 ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: find a list of lootlists with rarity from this lootlist |
|
//----------------------------------------------------------------------------- |
|
void CEconLootListDefinition::GetRarityLootLists( CUtlVector< const CEconLootListDefinition* > *out_pVecRarityLootList ) const |
|
{ |
|
Assert( out_pVecRarityLootList ); |
|
if ( m_unRarity != k_unItemRarity_Any ) |
|
{ |
|
out_pVecRarityLootList->AddToTail( this ); |
|
} |
|
|
|
FOR_EACH_VEC( m_DropList, i ) |
|
{ |
|
const int iItemDef = m_DropList[i].m_iItemOrLootlistDef; // not item_definition_index because it might also be a negative value to indicate a sub lootlist |
|
if ( iItemDef < 0 ) |
|
{ |
|
// In the case where iItemDef is negative, it's a nested loot list. Ask that list to choose an item. |
|
// HACKY: Store loot list indices as negatives, starting from -1, because 0 is a valid item index |
|
int iLLIndex = (iItemDef * -1) - 1; |
|
const CEconLootListDefinition *pNestedLootList = GetItemSchema()->GetLootListByIndex( iLLIndex ); |
|
if ( !pNestedLootList ) |
|
return; |
|
|
|
pNestedLootList->GetRarityLootLists( out_pVecRarityLootList ); |
|
} |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: get all item defs from this lootlist ( not lootlist item def ) |
|
//----------------------------------------------------------------------------- |
|
void CEconLootListDefinition::GetItemDefs( CUtlVector< item_definition_index_t > *out_pVecItemDefs ) const |
|
{ |
|
Assert( out_pVecItemDefs ); |
|
|
|
FOR_EACH_VEC( m_DropList, i ) |
|
{ |
|
const int iItemDef = m_DropList[i].m_iItemOrLootlistDef; // not item_definition_index because it might also be a negative value to indicate a sub lootlist |
|
if ( iItemDef >= 0 ) |
|
{ |
|
out_pVecItemDefs->AddToTail( (item_definition_index_t)iItemDef ); |
|
} |
|
else |
|
{ |
|
// In the case where iItemDef is negative, it's a nested loot list. Ask that list to choose an item. |
|
// HACKY: Store loot list indices as negatives, starting from -1, because 0 is a valid item index |
|
int iLLIndex = (iItemDef * -1) - 1; |
|
const CEconLootListDefinition *pNestedLootList = GetItemSchema()->GetLootListByIndex( iLLIndex ); |
|
if ( !pNestedLootList ) |
|
return; |
|
|
|
pNestedLootList->GetItemDefs( out_pVecItemDefs ); |
|
} |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Given a vector of possible attributes, roll to see which ones are |
|
// chosen. We allocate memory for these new attributes, so it's the |
|
// responsibility of the caller to free these attributes when they're |
|
// done with them! |
|
//----------------------------------------------------------------------------- |
|
void CEconLootListDefinition::RollRandomAttributes( CUtlVector< static_attrib_t >& vecAttributes, const CEconGameAccount *pGameAccount ) const |
|
{ |
|
for ( int i=0; i<m_RandomAttribs.Count(); ++i ) |
|
{ |
|
const random_attrib_t* rattr = m_RandomAttribs[i]; |
|
rattr->RollRandomAttributes( vecAttributes, pGameAccount ); |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
bool CEconLootListDefinition::drop_period_t::IsValidForTime( const RTime32& time ) const |
|
{ |
|
if( time >= m_DropStartDate && time < m_DropEndDate ) |
|
return true; |
|
|
|
return false; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
bool CEconLootListDefinition::BAttachLootListAttributes( const CEconGameAccount *pGameAccount, CEconItem *pItem ) const |
|
{ |
|
//static CSchemaAttributeDefHandle pAttr_ElevateQuality( "elevate quality" ); |
|
|
|
// Gather and apply old-style random attributes. |
|
CUtlVector< static_attrib_t > vecAttributes; |
|
RollRandomAttributes( vecAttributes, pGameAccount ); |
|
|
|
FOR_EACH_VEC( vecAttributes, i ) |
|
{ |
|
GEconManager()->GetItemFactory().ApplyStaticAttributeToItem( pItem, vecAttributes[i], pGameAccount ); |
|
vecAttributes[i].GetAttributeDefinition()->GetAttributeType()->UnloadEconAttributeValue( &vecAttributes[i].m_value ); |
|
} |
|
|
|
// Apply all relevant property generators. |
|
for ( auto pGenerator : m_PropertyGenerators ) |
|
{ |
|
if ( !pGenerator->BGenerateProperties( pItem ) ) |
|
return false; |
|
} |
|
|
|
return true; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
bool lootlist_attrib_t::BInitFromKV( const char *pszContext, KeyValues *pKVKey, CEconItemSchema &pschema, CUtlVector<CUtlString> *pVecErrors ) |
|
{ |
|
SCHEMA_INIT_SUBSTEP( m_staticAttrib.BInitFromKV_MultiLine( pszContext, pKVKey, pVecErrors ) ); |
|
|
|
SCHEMA_INIT_CHECK( |
|
pKVKey->FindKey( "weight" ), |
|
"Context '%s': Attribute \"%s\" missing required 'weight' field", pszContext, pKVKey->GetName() ); |
|
|
|
m_flWeight = pKVKey->GetFloat( "weight" ); |
|
|
|
return SCHEMA_INIT_SUCCESS(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: returns true if we should stop rolling from this random_attrib_t |
|
//----------------------------------------------------------------------------- |
|
bool random_attrib_t::RollRandomAttributes( CUtlVector< static_attrib_t >& vecAttributes, const CEconGameAccount *pGameAccount ) const |
|
{ |
|
if ( m_flChanceOfRandomAttribute && RandomFloat() <= m_flChanceOfRandomAttribute ) |
|
{ |
|
// We're attaching a random attribute. Determine which attribute. |
|
float flRand = 0.0f; |
|
if ( !m_bPickAllAttributes ) |
|
{ |
|
// Pick one attribute to add |
|
// Otherwise we'll pick them all |
|
flRand = RandomFloat( 0.f, 1.f ) * m_flTotalAttributeWeight; |
|
} |
|
|
|
float flAccum = 0.f; |
|
for ( int iAttrib = 0; iAttrib < m_RandomAttributes.Count(); ++iAttrib ) |
|
{ |
|
const lootlist_attrib_t& randomAttrib = m_RandomAttributes[iAttrib]; |
|
|
|
flAccum += randomAttrib.m_flWeight; |
|
if ( flRand <= flAccum ) |
|
{ |
|
// Add the attribute |
|
static_attrib_t &staticAttrib = vecAttributes[ vecAttributes.AddToTail( randomAttrib.m_staticAttrib ) ]; |
|
const CEconItemAttributeDefinition *pAttrDef = staticAttrib.GetAttributeDefinition(); |
|
const ISchemaAttributeType *pAttrType = pAttrDef->GetAttributeType(); |
|
// Generate a special value? |
|
pAttrType->InitializeNewEconAttributeValue( &staticAttrib.m_value ); |
|
pAttrDef->GetAttributeType()->GenerateEconAttributeValue( pAttrDef, staticAttrib, pGameAccount, &staticAttrib.m_value ); |
|
|
|
if ( !m_bPickAllAttributes ) |
|
{ |
|
// We're only picking one attribute from the list |
|
return true; |
|
} |
|
} |
|
} |
|
} |
|
|
|
return false; |
|
} |
|
|
|
#endif // GC_DLL |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CEconLootListDefinition::EnumerateUserFacingPotentialDrops( IEconLootListIterator *pIt ) const |
|
{ |
|
Assert( pIt ); |
|
|
|
// Loot lists have the option of specifying that their contents should not be publicly |
|
// listed. This is used on the GC for things like the "rare item drop list" to prevent |
|
// every single potentially-unusual hat from showing up. |
|
if ( !BPublicListContents() ) |
|
return; |
|
|
|
FOR_EACH_VEC( GetLootListContents(), i ) |
|
{ |
|
const int iID = GetLootListContents()[i].m_iItemOrLootlistDef; |
|
|
|
// Nested loot lists are stored as negative indices. |
|
if ( iID < 0 ) |
|
{ |
|
const CEconLootListDefinition *pLootListDef = GetItemSchema()->GetLootListByIndex( (-iID) - 1 ); |
|
if ( !pLootListDef ) |
|
continue; |
|
|
|
pLootListDef->EnumerateUserFacingPotentialDrops( pIt ); |
|
} |
|
else |
|
{ |
|
pIt->OnIterate( iID ); |
|
} |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
/*static*/ CSchemaAttributeDefHandle CAttributeLineItemLootList::s_pAttrDef_RandomDropLineItems[] = |
|
{ |
|
CSchemaAttributeDefHandle( "random drop line item 0" ), |
|
CSchemaAttributeDefHandle( "random drop line item 1" ), |
|
CSchemaAttributeDefHandle( "random drop line item 2" ), |
|
CSchemaAttributeDefHandle( "random drop line item 3" ), |
|
}; |
|
|
|
#ifdef GC_DLL |
|
/*static*/ CSchemaAttributeDefHandle CAttributeLineItemLootList::s_pAttrDef_RandomDropLineItemUnusualChance( "random drop line item unusual chance" ); // "one out of this many" |
|
/*static*/ CSchemaAttributeDefHandle CAttributeLineItemLootList::s_pAttrDef_RandomDropLineItemUnusualList( "random drop line item unusual list" ); |
|
#endif // GC_DLL |
|
CSchemaAttributeDefHandle CAttributeLineItemLootList::s_pAttrDef_RandomDropLineItemFooterDesc( "random drop line item footer desc" ); |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CAttributeLineItemLootList::EnumerateUserFacingPotentialDrops( IEconLootListIterator *pIt ) const |
|
{ |
|
Assert( pIt ); |
|
|
|
for ( int i = 0; i < ARRAYSIZE( s_pAttrDef_RandomDropLineItems ); i++ ) |
|
{ |
|
uint32 unItemDef; |
|
COMPILE_TIME_ASSERT( sizeof( unItemDef ) >= sizeof( item_definition_index_t ) ); |
|
|
|
// If we run out of attributes we have set we're done. |
|
if ( !m_pEconItem->FindAttribute( s_pAttrDef_RandomDropLineItems[i], &unItemDef ) ) |
|
break; |
|
|
|
pIt->OnIterate( unItemDef ); |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
const char *CAttributeLineItemLootList::GetLootListHeaderLocalizationKey() const |
|
{ |
|
return g_pszDefaultRevolvingLootListHeader; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
const char *CAttributeLineItemLootList::GetLootListFooterLocalizationKey() const |
|
{ |
|
CAttribute_String sFooter; |
|
const char* pszFooter = NULL; |
|
if ( FindAttribute_UnsafeBitwiseCast<CAttribute_String>( m_pEconItem, s_pAttrDef_RandomDropLineItemFooterDesc, &pszFooter ) ) |
|
{ |
|
return pszFooter; |
|
} |
|
return NULL; |
|
} |
|
//----------------------------------------------------------------------------- |
|
const char *CAttributeLineItemLootList::GetLootListCollectionReference() const |
|
{ |
|
// TODO : Implement me! |
|
return NULL; |
|
} |
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
const CEconLootListDefinition* CEconItemSchema::GetLootListByName( const char* pListName, int *out_piIndex ) const |
|
{ |
|
auto idx = m_mapLootLists.Find( pListName ); |
|
if ( !m_mapLootLists.IsValidIndex( idx ) ) |
|
return NULL; |
|
|
|
if ( out_piIndex ) |
|
{ |
|
*out_piIndex = idx; |
|
} |
|
|
|
return m_mapLootLists[idx]; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
const CQuestObjectiveDefinition* CEconItemSchema::GetQuestObjectiveByDefIndex( int iIdx ) const |
|
{ |
|
auto nMapIndex = m_mapQuestObjectives.Find( iIdx ); |
|
if ( nMapIndex != m_mapQuestObjectives.InvalidIndex() ) |
|
{ |
|
return m_mapQuestObjectives[ nMapIndex ]; |
|
} |
|
|
|
return NULL; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Constructor |
|
//----------------------------------------------------------------------------- |
|
CEconCraftingRecipeDefinition::CEconCraftingRecipeDefinition( void ) |
|
: m_nDefIndex( 0 ) |
|
#ifdef GC_DLL |
|
, m_bIsCraftableByUnverifiedClient( false ) |
|
#endif // GC_DLL |
|
{ |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Initialize the attribute definition |
|
// Input: pKVAttribute - The KeyValues representation of the attribute |
|
// schema - The overall item schema for this attribute |
|
// pVecErrors - An optional vector that will contain error messages if |
|
// the init fails. |
|
// Output: True if initialization succeeded, false otherwise |
|
//----------------------------------------------------------------------------- |
|
bool CEconCraftingRecipeDefinition::BInitFromKV( KeyValues *pKVRecipe, CUtlVector<CUtlString> *pVecErrors /* = NULL */ ) |
|
{ |
|
m_nDefIndex = Q_atoi( pKVRecipe->GetName() ); |
|
|
|
// Check for required fields |
|
SCHEMA_INIT_CHECK( |
|
NULL != pKVRecipe->FindKey( "input_items" ), |
|
"Recipe definition %d: Missing required field \"input_items\"", m_nDefIndex ); |
|
|
|
SCHEMA_INIT_CHECK( |
|
NULL != pKVRecipe->FindKey( "output_items" ), |
|
"Recipe definition %d: Missing required field \"output_items\"", m_nDefIndex ); |
|
|
|
m_bDisabled = pKVRecipe->GetBool( "disabled" ); |
|
m_strName = pKVRecipe->GetString( "name" ); |
|
m_strN_A = pKVRecipe->GetString( "n_A" ); |
|
m_strDescInputs = pKVRecipe->GetString( "desc_inputs" ); |
|
m_strDescOutputs = pKVRecipe->GetString( "desc_outputs" ); |
|
m_strDI_A = pKVRecipe->GetString( "di_A" ); |
|
m_strDI_B = pKVRecipe->GetString( "di_B" ); |
|
m_strDI_C = pKVRecipe->GetString( "di_C" ); |
|
m_strDO_A = pKVRecipe->GetString( "do_A" ); |
|
m_strDO_B = pKVRecipe->GetString( "do_B" ); |
|
m_strDO_C = pKVRecipe->GetString( "do_C" ); |
|
|
|
#ifdef GC_DLL |
|
m_bIsCraftableByUnverifiedClient = pKVRecipe->GetBool( "is_craftable_by_unverified_clients", false ); |
|
#endif // GC_DLL |
|
m_bRequiresAllSameClass = pKVRecipe->GetBool( "all_same_class" ); |
|
m_bRequiresAllSameSlot = pKVRecipe->GetBool( "all_same_slot" ); |
|
m_iCacheClassUsageForOutputFromItem = pKVRecipe->GetInt( "add_class_usage_to_output", -1 ); |
|
m_iCacheSlotUsageForOutputFromItem = pKVRecipe->GetInt( "add_slot_usage_to_output", -1 ); |
|
m_iCacheSetForOutputFromItem = pKVRecipe->GetInt( "add_set_to_output", -1 ); |
|
m_bPremiumAccountOnly = pKVRecipe->GetBool( "premium_only", false ); |
|
m_iCategory = (recipecategories_t)StringFieldToInt( pKVRecipe->GetString("category"), g_szRecipeCategoryStrings, ARRAYSIZE(g_szRecipeCategoryStrings) ); |
|
|
|
// Read in all the input items |
|
KeyValues *pKVInputItems = pKVRecipe->FindKey( "input_items" ); |
|
if ( NULL != pKVInputItems ) |
|
{ |
|
FOR_EACH_TRUE_SUBKEY( pKVInputItems, pKVInputItem ) |
|
{ |
|
int index = m_InputItemsCriteria.AddToTail(); |
|
SCHEMA_INIT_SUBSTEP( m_InputItemsCriteria[index].BInitFromKV( pKVInputItem ) ); |
|
|
|
// Recipes ignore the enabled flag when generating items |
|
m_InputItemsCriteria[index].SetIgnoreEnabledFlag( true ); |
|
|
|
index = m_InputItemDupeCounts.AddToTail(); |
|
m_InputItemDupeCounts[index] = atoi( pKVInputItem->GetName() ); |
|
} |
|
} |
|
|
|
// Read in all the output items |
|
KeyValues *pKVOutputItems = pKVRecipe->FindKey( "output_items" ); |
|
if ( NULL != pKVOutputItems ) |
|
{ |
|
FOR_EACH_TRUE_SUBKEY( pKVOutputItems, pKVOutputItem ) |
|
{ |
|
int index = m_OutputItemsCriteria.AddToTail(); |
|
SCHEMA_INIT_SUBSTEP( m_OutputItemsCriteria[index].BInitFromKV( pKVOutputItem ) ); |
|
|
|
// Recipes ignore the enabled flag when generating items |
|
m_OutputItemsCriteria[index].SetIgnoreEnabledFlag( true ); |
|
} |
|
} |
|
|
|
return SCHEMA_INIT_SUCCESS(); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Serializes the criteria to and from messages |
|
//----------------------------------------------------------------------------- |
|
bool CEconCraftingRecipeDefinition::BSerializeToMsg( CSOItemRecipe & msg ) const |
|
{ |
|
msg.set_def_index( m_nDefIndex ); |
|
msg.set_name( m_strName ); |
|
msg.set_n_a( m_strN_A ); |
|
msg.set_desc_inputs( m_strDescInputs ); |
|
msg.set_desc_outputs( m_strDescOutputs ); |
|
msg.set_di_a( m_strDI_A ); |
|
msg.set_di_b( m_strDI_B ); |
|
msg.set_di_c( m_strDI_C ); |
|
msg.set_do_a( m_strDO_A ); |
|
msg.set_do_b( m_strDO_B ); |
|
msg.set_do_c( m_strDO_C ); |
|
msg.set_requires_all_same_class( m_bRequiresAllSameClass ); |
|
msg.set_requires_all_same_slot( m_bRequiresAllSameSlot ); |
|
msg.set_class_usage_for_output( m_iCacheClassUsageForOutputFromItem ); |
|
msg.set_slot_usage_for_output( m_iCacheSlotUsageForOutputFromItem ); |
|
msg.set_set_for_output( m_iCacheSetForOutputFromItem ); |
|
|
|
FOR_EACH_VEC( m_InputItemsCriteria, i ) |
|
{ |
|
CSOItemCriteria *pCrit = msg.add_input_items_criteria(); |
|
if ( !m_InputItemsCriteria[i].BSerializeToMsg( *pCrit ) ) |
|
return false; |
|
} |
|
|
|
FOR_EACH_VEC( m_InputItemDupeCounts, i ) |
|
{ |
|
msg.add_input_item_dupe_counts( m_InputItemDupeCounts[i] ); |
|
} |
|
|
|
FOR_EACH_VEC( m_OutputItemsCriteria, i ) |
|
{ |
|
CSOItemCriteria *pCrit = msg.add_output_items_criteria(); |
|
if ( !m_OutputItemsCriteria[i].BSerializeToMsg( *pCrit ) ) |
|
return false; |
|
} |
|
|
|
return true; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Serializes the criteria to and from messages |
|
//----------------------------------------------------------------------------- |
|
bool CEconCraftingRecipeDefinition::BDeserializeFromMsg( const CSOItemRecipe & msg ) |
|
{ |
|
m_nDefIndex = msg.def_index(); |
|
m_strName = msg.name().c_str(); |
|
m_strN_A = msg.n_a().c_str(); |
|
m_strDescInputs = msg.desc_inputs().c_str(); |
|
m_strDescOutputs = msg.desc_outputs().c_str(); |
|
m_strDI_A = msg.di_a().c_str(); |
|
m_strDI_B = msg.di_b().c_str(); |
|
m_strDI_C = msg.di_c().c_str(); |
|
m_strDO_A = msg.do_a().c_str(); |
|
m_strDO_B = msg.do_b().c_str(); |
|
m_strDO_C = msg.do_c().c_str(); |
|
|
|
m_bRequiresAllSameClass = msg.requires_all_same_class(); |
|
m_bRequiresAllSameSlot = msg.requires_all_same_slot(); |
|
m_iCacheClassUsageForOutputFromItem = msg.class_usage_for_output(); |
|
m_iCacheSlotUsageForOutputFromItem = msg.slot_usage_for_output(); |
|
m_iCacheSetForOutputFromItem = msg.set_for_output(); |
|
|
|
// Read how many input items there are |
|
uint32 unCount = msg.input_items_criteria_size(); |
|
m_InputItemsCriteria.SetSize( unCount ); |
|
for ( uint32 i = 0; i < unCount; i++ ) |
|
{ |
|
if ( !m_InputItemsCriteria[i].BDeserializeFromMsg( msg.input_items_criteria( i ) ) ) |
|
return false; |
|
} |
|
|
|
// Read how many input item dupe counts there are |
|
unCount = msg.input_item_dupe_counts_size(); |
|
m_InputItemDupeCounts.SetSize( unCount ); |
|
for ( uint32 i = 0; i < unCount; i++ ) |
|
{ |
|
m_InputItemDupeCounts[i] = msg.input_item_dupe_counts( i ); |
|
} |
|
|
|
// Read how many output items there are |
|
unCount = msg.output_items_criteria_size(); |
|
m_OutputItemsCriteria.SetSize( unCount ); |
|
for ( uint32 i = 0; i < unCount; i++ ) |
|
{ |
|
if ( !m_OutputItemsCriteria[i].BDeserializeFromMsg( msg.output_items_criteria( i ) ) ) |
|
return false; |
|
} |
|
|
|
return true; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Returns true if the vector contains a set of items that matches the inputs for this recipe |
|
// Note it will fail if the vector contains extra items that aren't needed. |
|
// |
|
//----------------------------------------------------------------------------- |
|
bool CEconCraftingRecipeDefinition::ItemListMatchesInputs( CUtlVector<CEconItem*> *vecCraftingItems, KeyValues *out_pkvCraftParams, bool bIgnoreSlop, CUtlVector<uint64> *vecChosenItems ) const |
|
{ |
|
return false; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Constructor |
|
//----------------------------------------------------------------------------- |
|
int CEconCraftingRecipeDefinition::GetTotalInputItemsRequired( void ) const |
|
{ |
|
int iCount = 0; |
|
FOR_EACH_VEC( m_InputItemsCriteria, i ) |
|
{ |
|
if ( m_InputItemDupeCounts[i] ) |
|
{ |
|
iCount += m_InputItemDupeCounts[i]; |
|
} |
|
else |
|
{ |
|
iCount++; |
|
} |
|
} |
|
return iCount; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
#ifdef GC_DLL |
|
#define GC_SCH_REFERENCE( TAttribSchType ) \ |
|
TAttribSchType, |
|
#else |
|
#define GC_SCH_REFERENCE( TAttribSchType ) |
|
#endif |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
unsigned int Internal_GetAttributeTypeUniqueIdentifierNextValue() |
|
{ |
|
static unsigned int s_unUniqueCounter = 0; |
|
|
|
unsigned int unCounter = s_unUniqueCounter; |
|
s_unUniqueCounter++; |
|
return unCounter; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
#ifdef GC_DLL |
|
template < typename TAttribSchType, typename TRecordBaseType > |
|
static TAttribSchType *GetTypedSch( TRecordBaseType *pRecordBase ) |
|
{ |
|
Assert( pRecordBase->GetITable() == TAttribSchType::k_iTable ); |
|
|
|
#if ENABLE_TYPED_ATTRIBUTE_PARANOIA |
|
TAttribSchType *pTypedSch = dynamic_cast<TAttribSchType *>( pRecordBase ); |
|
Assert( pTypedSch ); |
|
return pTypedSch; |
|
#else |
|
return static_cast<TAttribSchType *>( pRecordBase ); |
|
#endif |
|
} |
|
#endif // GC_DLL |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
template < GC_SCH_REFERENCE( typename TAttribSchType ) typename TAttribInMemoryType > |
|
class CSchemaAttributeTypeBase : public ISchemaAttributeTypeBase<TAttribInMemoryType> |
|
{ |
|
public: |
|
#ifdef GC_DLL |
|
virtual CColumnSet& GetFullColumnSet() const OVERRIDE |
|
{ |
|
static CColumnSet sFullColumnSet( CColumnSet::Full<TAttribSchType>() ); |
|
|
|
return sFullColumnSet; |
|
} |
|
|
|
virtual CRecordBase *CreateTypedSchRecord() const OVERRIDE |
|
{ |
|
return new TAttribSchType; |
|
} |
|
#endif // GC_DLL |
|
}; |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
template < GC_SCH_REFERENCE( typename TAttribSchType ) typename TProtobufValueType > |
|
class CSchemaAttributeTypeProtobufBase : public CSchemaAttributeTypeBase< GC_SCH_REFERENCE( TAttribSchType ) TProtobufValueType > |
|
{ |
|
public: |
|
virtual void ConvertTypedValueToByteStream( const TProtobufValueType& typedValue, ::std::string *out_psBytes ) const OVERRIDE |
|
{ |
|
DbgVerify( typedValue.SerializeToString( out_psBytes ) ); |
|
} |
|
|
|
virtual void ConvertByteStreamToTypedValue( const ::std::string& sBytes, TProtobufValueType *out_pTypedValue ) const OVERRIDE |
|
{ |
|
DbgVerify( out_pTypedValue->ParseFromString( sBytes ) ); |
|
} |
|
|
|
virtual bool BConvertStringToEconAttributeValue( const CEconItemAttributeDefinition *pAttrDef, const char *pszValue, union attribute_data_union_t *out_pValue, bool bEnableTerribleBackwardsCompatibilitySchemaParsingCode ) const OVERRIDE |
|
{ |
|
Assert( pAttrDef ); |
|
Assert( out_pValue ); |
|
|
|
std::string sValue( pszValue ); |
|
TProtobufValueType typedValue; |
|
if ( !google::protobuf::TextFormat::ParseFromString( sValue, &typedValue ) ) |
|
return false; |
|
|
|
this->ConvertTypedValueToEconAttributeValue( typedValue, out_pValue ); |
|
return true; |
|
} |
|
|
|
virtual void ConvertEconAttributeValueToString( const CEconItemAttributeDefinition *pAttrDef, const attribute_data_union_t& value, std::string *out_ps ) const OVERRIDE |
|
{ |
|
Assert( pAttrDef ); |
|
Assert( out_ps ); |
|
|
|
google::protobuf::TextFormat::PrintToString( this->GetTypedValueContentsFromEconAttributeValue( value ), out_ps ); |
|
} |
|
}; |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
class CSchemaAttributeType_String : public CSchemaAttributeTypeProtobufBase< GC_SCH_REFERENCE( CSchItemAttributeString ) CAttribute_String > |
|
{ |
|
public: |
|
#ifdef GC_DLL |
|
virtual void ConvertEconAttributeValueToSch( itemid_t unItemId, const CEconItemAttributeDefinition *pAttrDef, const attribute_data_union_t& value, GCSDK::CRecordBase *out_pSchRecord ) const OVERRIDE |
|
{ |
|
Assert( out_pSchRecord ); |
|
Assert( pAttrDef ); |
|
Assert( pAttrDef->GetAttributeType() == this ); |
|
|
|
CSchItemAttributeString *out_psch = GetTypedSch<CSchItemAttributeString>( out_pSchRecord ); |
|
|
|
CAttribute_String typedValue; |
|
this->ConvertEconAttributeValueToTypedValue( value, &typedValue ); |
|
|
|
// const CAttribute_String& typedValue = GetTypedValueContentsFromEconAttributeValue( value ); |
|
|
|
out_psch->m_ulItemID = unItemId; |
|
out_psch->m_unAttrDefIndex = pAttrDef->GetDefinitionIndex(); |
|
WRITE_VAR_CHAR_FIELD( (*out_psch), VarCharAttrStrValue, typedValue.value().c_str() ); |
|
} |
|
|
|
virtual void LoadSchToEconAttributeValue( CEconItem *pTargetItem, const CEconItemAttributeDefinition *pAttrDef, const GCSDK::CRecordBase *pSchRecord ) const OVERRIDE |
|
{ |
|
Assert( pTargetItem ); |
|
Assert( pAttrDef ); |
|
Assert( pSchRecord ); |
|
Assert( pAttrDef->GetAttributeType() == this ); |
|
|
|
const CSchItemAttributeString *psch = GetTypedSch<const CSchItemAttributeString>( pSchRecord ); |
|
|
|
CAttribute_String typedValue; |
|
typedValue.set_value( READ_VAR_CHAR_FIELD( (*psch), m_VarCharAttrStrValue ) ); |
|
|
|
pTargetItem->SetDynamicAttributeValue( pAttrDef, typedValue ); |
|
} |
|
#endif // GC_DLL |
|
|
|
// We intentionally override the convert-to-/convert-from-string functions for strings so that string literals can be |
|
// specified in the schema, etc. without worrying about the protobuf text format. |
|
virtual bool BConvertStringToEconAttributeValue( const CEconItemAttributeDefinition *pAttrDef, const char *pszValue, union attribute_data_union_t *out_pValue, bool bEnableTerribleBackwardsCompatibilitySchemaParsingCode ) const OVERRIDE |
|
{ |
|
Assert( pAttrDef ); |
|
Assert( out_pValue ); |
|
|
|
CAttribute_String typedValue; |
|
typedValue.set_value( pszValue ); |
|
|
|
this->ConvertTypedValueToEconAttributeValue( typedValue, out_pValue ); |
|
|
|
return true; |
|
} |
|
|
|
virtual void ConvertEconAttributeValueToString( const CEconItemAttributeDefinition *pAttrDef, const attribute_data_union_t& value, std::string *out_ps ) const OVERRIDE |
|
{ |
|
Assert( pAttrDef ); |
|
Assert( out_ps ); |
|
|
|
*out_ps = this->GetTypedValueContentsFromEconAttributeValue( value ).value().c_str(); |
|
} |
|
}; |
|
|
|
void CopyStringAttributeValueToCharPointerOutput( const CAttribute_String *pValue, const char **out_pValue ) |
|
{ |
|
Assert( pValue ); |
|
Assert( out_pValue ); |
|
|
|
*out_pValue = pValue->value().c_str(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
class CSchemaAttributeType_DynamicRecipeComponentDefinedItem : public CSchemaAttributeTypeProtobufBase< GC_SCH_REFERENCE( CSchItemAttributeDynamicRecipeComponentDefinedItem ) CAttribute_DynamicRecipeComponent > |
|
{ |
|
public: |
|
#ifdef GC_DLL |
|
virtual void ConvertEconAttributeValueToSch( itemid_t unItemId, const CEconItemAttributeDefinition *pAttrDef, const union attribute_data_union_t& value, GCSDK::CRecordBase *out_pSchRecord ) const OVERRIDE |
|
{ |
|
Assert( out_pSchRecord ); |
|
Assert( pAttrDef ); |
|
Assert( pAttrDef->GetAttributeType() == this ); |
|
|
|
CSchItemAttributeDynamicRecipeComponentDefinedItem *out_psch = GetTypedSch<CSchItemAttributeDynamicRecipeComponentDefinedItem>( out_pSchRecord ); |
|
|
|
CAttribute_DynamicRecipeComponent typedValue; |
|
ConvertEconAttributeValueToTypedValue( value, &typedValue ); |
|
|
|
out_psch->m_ulItemID = unItemId; |
|
out_psch->m_unAttrDefIndex = pAttrDef->GetDefinitionIndex(); |
|
out_psch->m_unItemDef = typedValue.def_index(); |
|
out_psch->m_unItemQuality = typedValue.item_quality(); |
|
out_psch->m_unFlags = typedValue.component_flags(); |
|
out_psch->m_unItemCount = typedValue.num_required(); |
|
out_psch->m_unItemsFulfilled = typedValue.num_fulfilled(); |
|
WRITE_VAR_CHAR_FIELD( (*out_psch), VarCharAttrStr, typedValue.attributes_string().c_str() ); |
|
} |
|
|
|
virtual void LoadSchToEconAttributeValue( CEconItem *pTargetItem, const CEconItemAttributeDefinition *pAttrDef, const GCSDK::CRecordBase *pSchRecord ) const OVERRIDE |
|
{ |
|
Assert( pTargetItem ); |
|
Assert( pAttrDef ); |
|
Assert( pSchRecord ); |
|
Assert( pAttrDef->GetAttributeType() == this ); |
|
|
|
const CSchItemAttributeDynamicRecipeComponentDefinedItem *psch = GetTypedSch<const CSchItemAttributeDynamicRecipeComponentDefinedItem>( pSchRecord ); |
|
|
|
CAttribute_DynamicRecipeComponent typedValue; |
|
typedValue.set_def_index( psch->m_unItemDef ); |
|
typedValue.set_item_quality( psch->m_unItemQuality ); |
|
typedValue.set_component_flags( psch->m_unFlags ); |
|
typedValue.set_attributes_string( READ_VAR_CHAR_FIELD( (*psch), m_VarCharAttrStr ) ); |
|
typedValue.set_num_required( psch->m_unItemCount ); |
|
typedValue.set_num_fulfilled( psch->m_unItemsFulfilled ); |
|
|
|
pTargetItem->SetDynamicAttributeValue( pAttrDef, typedValue ); |
|
} |
|
|
|
virtual bool BConvertStringToEconAttributeValue( const CEconItemAttributeDefinition *pAttrDef, const char *pszValue, union attribute_data_union_t *out_pValue, bool bEnableTerribleBackwardsCompatibilitySchemaParsingCode ) const OVERRIDE |
|
{ |
|
Assert( pAttrDef ); |
|
Assert( out_pValue ); |
|
|
|
std::string sValue( pszValue ); |
|
// What's happened here is we've renamed some fields within CAttribute_DynamicRecipeComponent, |
|
// but steam contains the strings of the old format serialized, and keeps sending them to us. |
|
// Rather than updating steam, we're going to made a protobuff class that can accept the new |
|
// and old formats, and put the corret values into the correct members of the new format. |
|
|
|
|
|
CAttribute_DynamicRecipeComponent_COMPAT_NEVER_SERIALIZE_THIS_OUT typedCompatValue; |
|
CAttribute_DynamicRecipeComponent typedActualValue; |
|
|
|
#ifdef STAGING_ONLY |
|
auto *pActualFields = typedActualValue.descriptor(); |
|
auto *pCompatFields = typedCompatValue.descriptor(); |
|
for ( int i=0; i < pActualFields->field_count(); ++i ) |
|
{ |
|
const bool bFoundField = pCompatFields->FindFieldByName( pActualFields->field( i )->name() ) != NULL; |
|
Assert( bFoundField ); |
|
if ( !bFoundField ) |
|
{ |
|
EmitError( SPEW_GC, "Missing field '%s' in CAttribute_DynamicRecipeComponent_COMPAT_NEVER_SERIALIZE_THIS_OUT\n", pActualFields->field( i )->name() ); |
|
return false; |
|
} |
|
} |
|
#endif // STAGING_ONLY |
|
|
|
if ( !google::protobuf::TextFormat::ParseFromString( sValue, &typedCompatValue ) ) |
|
{ |
|
EmitError( SPEW_GC, "Failed to parse recipe component into compatible protobuf\n" ); |
|
return false; |
|
} |
|
|
|
if ( typedCompatValue.has_component_flags() ) |
|
typedActualValue.set_component_flags( typedCompatValue.component_flags() ); |
|
else if ( typedCompatValue.has_item_flags() ) |
|
typedActualValue.set_component_flags( typedCompatValue.item_flags() ); |
|
else |
|
{ |
|
EmitError( SPEW_GC, "Failed to parse component_flags. component_flags: %d, item_flags: %d\n", typedCompatValue.component_flags(), typedCompatValue.item_flags() ); |
|
return false; |
|
} |
|
|
|
if ( typedCompatValue.has_def_index() ) |
|
typedActualValue.set_def_index( typedCompatValue.def_index() ); |
|
else if ( typedCompatValue.has_item_def() ) |
|
typedActualValue.set_def_index( typedCompatValue.item_def() ); |
|
else if ( typedActualValue.component_flags() & DYNAMIC_RECIPE_FLAG_PARAM_ITEM_DEF_SET ) |
|
{ |
|
EmitError( SPEW_GC, "Failed to parse item_def. def_index: %d, item_def: %d\n", typedCompatValue.def_index(), typedCompatValue.item_def() ); |
|
return false; |
|
} |
|
|
|
typedActualValue.set_item_quality( typedCompatValue.item_quality() ); |
|
|
|
|
|
|
|
typedActualValue.set_attributes_string( typedCompatValue.attributes_string() ); |
|
|
|
if( typedCompatValue.has_num_required() ) |
|
typedActualValue.set_num_required( typedCompatValue.num_required() ); |
|
else if ( typedCompatValue.has_item_count() ) |
|
typedActualValue.set_num_required( typedCompatValue.item_count() ); |
|
else |
|
{ |
|
EmitError( SPEW_GC, "Failed to parse component_flags. num_required: %d, item_count: %d\n", typedCompatValue.num_required(), typedCompatValue.item_count() ); |
|
return false; |
|
} |
|
|
|
if ( typedCompatValue.has_items_fulfilled() ) |
|
typedActualValue.set_num_fulfilled( typedCompatValue.items_fulfilled() ); |
|
else if ( typedCompatValue.has_num_fulfilled() ) |
|
typedActualValue.set_num_fulfilled( typedCompatValue.num_fulfilled() ); |
|
else |
|
{ |
|
EmitError( SPEW_GC, "Failed to parse num_fulfilled. items_fulfilled: %d, num_fulfilled: %d\n", typedCompatValue.items_fulfilled(), typedCompatValue.num_fulfilled() ); |
|
return false; |
|
} |
|
|
|
this->ConvertTypedValueToEconAttributeValue( typedActualValue, out_pValue ); |
|
return true; |
|
} |
|
|
|
#endif // GC_DLL |
|
}; |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
class CSchemaAttributeType_ItemSlotCriteria : public CSchemaAttributeTypeProtobufBase< GC_SCH_REFERENCE( CSchItemAttributeItemSlotCriteria ) CAttribute_ItemSlotCriteria > |
|
{ |
|
public: |
|
#ifdef GC_DLL |
|
virtual void ConvertEconAttributeValueToSch( itemid_t unItemId, const CEconItemAttributeDefinition *pAttrDef, const union attribute_data_union_t& value, GCSDK::CRecordBase *out_pSchRecord ) const OVERRIDE |
|
{ |
|
Assert( out_pSchRecord ); |
|
Assert( pAttrDef ); |
|
Assert( pAttrDef->GetAttributeType() == this ); |
|
|
|
AssertMsg( 0, "Implement this when we want this attribute to be dynamic" ); |
|
} |
|
|
|
virtual void LoadSchToEconAttributeValue( CEconItem *pTargetItem, const CEconItemAttributeDefinition *pAttrDef, const GCSDK::CRecordBase *pSchRecord ) const OVERRIDE |
|
{ |
|
Assert( pTargetItem ); |
|
Assert( pAttrDef ); |
|
Assert( pSchRecord ); |
|
Assert( pAttrDef->GetAttributeType() == this ); |
|
|
|
AssertMsg( 0, "Implement this when we want this attribute to be dynamic" ); |
|
} |
|
#endif // GC_DLL |
|
|
|
virtual bool BConvertStringToEconAttributeValue( const CEconItemAttributeDefinition *pAttrDef, const char *pszValue, union attribute_data_union_t *out_pValue, bool bEnableTerribleBackwardsCompatibilitySchemaParsingCode ) const OVERRIDE |
|
{ |
|
Assert( pAttrDef ); |
|
Assert( out_pValue ); |
|
|
|
std::string sValue( pszValue ); |
|
CAttribute_ItemSlotCriteria typedValue; |
|
if ( !google::protobuf::TextFormat::ParseFromString( sValue, &typedValue ) ) |
|
return false; |
|
|
|
this->ConvertTypedValueToEconAttributeValue( typedValue, out_pValue ); |
|
|
|
return true; |
|
} |
|
|
|
virtual void ConvertEconAttributeValueToString( const CEconItemAttributeDefinition *pAttrDef, const attribute_data_union_t& value, std::string *out_ps ) const OVERRIDE |
|
{ |
|
Assert( pAttrDef ); |
|
Assert( out_ps ); |
|
|
|
this->ConvertEconAttributeValueToString( pAttrDef, value, out_ps ); |
|
} |
|
}; |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
class CSchemaAttributeType_WorldItemPlacement : public CSchemaAttributeTypeProtobufBase < GC_SCH_REFERENCE( CSchItemAttributeWorldItemPlacement ) CAttribute_WorldItemPlacement > |
|
{ |
|
public: |
|
#ifdef GC_DLL |
|
virtual void ConvertEconAttributeValueToSch( itemid_t unItemId, const CEconItemAttributeDefinition *pAttrDef, const union attribute_data_union_t& value, GCSDK::CRecordBase *out_pSchRecord ) const OVERRIDE |
|
{ |
|
Assert( out_pSchRecord ); |
|
Assert( pAttrDef ); |
|
Assert( pAttrDef->GetAttributeType() == this ); |
|
|
|
CSchItemAttributeWorldItemPlacement *out_psch = GetTypedSch< CSchItemAttributeWorldItemPlacement >( out_pSchRecord ); |
|
|
|
CAttribute_WorldItemPlacement typedValue; |
|
ConvertEconAttributeValueToTypedValue( value, &typedValue ); |
|
|
|
out_psch->m_ulItemID = unItemId; |
|
out_psch->m_unAttrDefIndex = pAttrDef->GetDefinitionIndex(); |
|
out_psch->m_ulOriginalItemID = typedValue.original_item_id(); |
|
out_psch->m_fPosX = typedValue.pos_x(); |
|
out_psch->m_fPosY = typedValue.pos_y(); |
|
out_psch->m_fPosZ = typedValue.pos_z(); |
|
out_psch->m_fAngX = typedValue.ang_x(); |
|
out_psch->m_fAngY = typedValue.ang_y(); |
|
out_psch->m_fAngZ = typedValue.ang_z(); |
|
} |
|
|
|
virtual void LoadSchToEconAttributeValue( CEconItem *pTargetItem, const CEconItemAttributeDefinition *pAttrDef, const GCSDK::CRecordBase *pSchRecord ) const OVERRIDE |
|
{ |
|
Assert( pTargetItem ); |
|
Assert( pAttrDef ); |
|
Assert( pSchRecord ); |
|
Assert( pAttrDef->GetAttributeType() == this ); |
|
|
|
const CSchItemAttributeWorldItemPlacement *psch = GetTypedSch< const CSchItemAttributeWorldItemPlacement >( pSchRecord ); |
|
|
|
CAttribute_WorldItemPlacement typedValue; |
|
typedValue.set_original_item_id( psch->m_ulOriginalItemID ); |
|
typedValue.set_pos_x( psch->m_fPosX ); |
|
typedValue.set_pos_y( psch->m_fPosY ); |
|
typedValue.set_pos_x( psch->m_fPosZ ); |
|
typedValue.set_ang_x( psch->m_fAngX ); |
|
typedValue.set_ang_y( psch->m_fAngY ); |
|
typedValue.set_ang_z( psch->m_fAngZ ); |
|
|
|
pTargetItem->SetDynamicAttributeValue( pAttrDef, typedValue ); |
|
} |
|
#endif // GC_DLL |
|
|
|
virtual bool BConvertStringToEconAttributeValue( const CEconItemAttributeDefinition *pAttrDef, const char *pszValue, union attribute_data_union_t *out_pValue, bool bEnableTerribleBackwardsCompatibilitySchemaParsingCode ) const OVERRIDE |
|
{ |
|
Assert( pAttrDef ); |
|
Assert( out_pValue ); |
|
|
|
CAttribute_WorldItemPlacement typedValue; |
|
|
|
uint32 unValue = ( pszValue ) ? atoi( pszValue ) : 0; |
|
|
|
// Item forcing us to create the attribute (via force_gc_to_generate) |
|
if ( unValue == 0 ) |
|
{ |
|
typedValue.set_original_item_id( INVALID_ITEM_ID ); |
|
typedValue.set_pos_x( 0.f ); |
|
typedValue.set_pos_y( 0.f ); |
|
typedValue.set_pos_z( 0.f ); |
|
typedValue.set_ang_x( 0.f ); |
|
typedValue.set_ang_y( 0.f ); |
|
typedValue.set_ang_z( 0.f ); |
|
} |
|
else |
|
{ |
|
std::string sValue( pszValue ); |
|
if ( !google::protobuf::TextFormat::ParseFromString( sValue, &typedValue ) ) |
|
return false; |
|
} |
|
|
|
this->ConvertTypedValueToEconAttributeValue( typedValue, out_pValue ); |
|
return true; |
|
} |
|
|
|
virtual void ConvertEconAttributeValueToString( const CEconItemAttributeDefinition *pAttrDef, const attribute_data_union_t& value, std::string *out_ps ) const OVERRIDE |
|
{ |
|
Assert( pAttrDef ); |
|
Assert( out_ps ); |
|
|
|
this->ConvertEconAttributeValueToString( pAttrDef, value, out_ps ); |
|
} |
|
}; |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
class CSchemaAttributeType_Float : public CSchemaAttributeTypeBase< GC_SCH_REFERENCE( CSchItemAttributeFloat ) float > |
|
{ |
|
public: |
|
#ifdef GC_DLL |
|
virtual void ConvertEconAttributeValueToSch( itemid_t unItemId, const CEconItemAttributeDefinition *pAttrDef, const union attribute_data_union_t& value, GCSDK::CRecordBase *out_pSchRecord ) const OVERRIDE |
|
{ |
|
Assert( out_pSchRecord ); |
|
Assert( pAttrDef ); |
|
Assert( pAttrDef->GetAttributeType() == this ); |
|
|
|
CSchItemAttributeFloat *out_pschItemAttribute = GetTypedSch<CSchItemAttributeFloat>( out_pSchRecord ); |
|
|
|
// @note Tom Bui: we store the value as an unsigned integer in the DB, so just treat the field as a bunch of bits |
|
out_pschItemAttribute->m_ulItemID = unItemId; |
|
out_pschItemAttribute->m_unAttrDefIndex = pAttrDef->GetDefinitionIndex(); |
|
out_pschItemAttribute->m_fValue = value.asFloat; |
|
} |
|
|
|
virtual void LoadSchToEconAttributeValue( CEconItem *pTargetItem, const CEconItemAttributeDefinition *pAttrDef, const GCSDK::CRecordBase *pSchRecord ) const OVERRIDE |
|
{ |
|
Assert( pTargetItem ); |
|
Assert( pAttrDef ); |
|
Assert( pSchRecord ); |
|
Assert( pAttrDef->GetAttributeType() == this ); |
|
|
|
const CSchItemAttributeFloat *pschItemAttribute = GetTypedSch<const CSchItemAttributeFloat>( pSchRecord ); |
|
|
|
pTargetItem->SetDynamicAttributeValue( pAttrDef, pschItemAttribute->m_fValue ); |
|
} |
|
#endif // GC_DLL |
|
|
|
virtual bool BConvertStringToEconAttributeValue( const CEconItemAttributeDefinition *pAttrDef, const char *pszValue, union attribute_data_union_t *out_pValue, bool bEnableTerribleBackwardsCompatibilitySchemaParsingCode ) const OVERRIDE |
|
{ |
|
Assert( pAttrDef ); |
|
Assert( out_pValue ); |
|
|
|
out_pValue->asFloat = Q_atof( pszValue ); |
|
return true; |
|
} |
|
|
|
virtual void ConvertEconAttributeValueToString( const CEconItemAttributeDefinition *pAttrDef, const attribute_data_union_t& value, std::string *out_ps ) const OVERRIDE |
|
{ |
|
Assert( pAttrDef ); |
|
Assert( out_ps ); |
|
|
|
*out_ps = CFmtStr( "%f", value.asFloat ).Get(); |
|
} |
|
|
|
virtual void ConvertTypedValueToByteStream( const float& typedValue, ::std::string *out_psBytes ) const OVERRIDE |
|
{ |
|
Assert( out_psBytes ); |
|
Assert( out_psBytes->size() == 0 ); |
|
|
|
out_psBytes->resize( sizeof( float ) ); |
|
*reinterpret_cast<float *>( &((*out_psBytes)[0]) ) = typedValue; // overwrite string contents (sizeof( float ) bytes) |
|
} |
|
|
|
virtual void ConvertByteStreamToTypedValue( const ::std::string& sBytes, float *out_pTypedValue ) const OVERRIDE |
|
{ |
|
Assert( out_pTypedValue ); |
|
Assert( sBytes.size() == sizeof( float ) ); |
|
|
|
*out_pTypedValue = *reinterpret_cast<const float *>( &sBytes[0] ); |
|
} |
|
|
|
virtual bool BSupportsGameplayModificationAndNetworking() const OVERRIDE |
|
{ |
|
return true; |
|
} |
|
}; |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
class CSchemaAttributeType_UInt64 : public CSchemaAttributeTypeBase< GC_SCH_REFERENCE( CSchItemAttributeUInt64 ) uint64 > |
|
{ |
|
public: |
|
#ifdef GC_DLL |
|
virtual void ConvertEconAttributeValueToSch( itemid_t unItemId, const CEconItemAttributeDefinition *pAttrDef, const union attribute_data_union_t& value, GCSDK::CRecordBase *out_pSchRecord ) const OVERRIDE |
|
{ |
|
Assert( out_pSchRecord ); |
|
Assert( pAttrDef ); |
|
Assert( pAttrDef->GetAttributeType() == this ); |
|
|
|
uint64 ulValue; |
|
ConvertEconAttributeValueToTypedValue( value, &ulValue ); |
|
|
|
CSchItemAttributeUInt64 *out_pschItemAttribute = GetTypedSch<CSchItemAttributeUInt64>( out_pSchRecord ); |
|
|
|
// @note Tom Bui: we store the value as an unsigned integer in the DB, so just treat the field as a bunch of bits |
|
out_pschItemAttribute->m_ulItemID = unItemId; |
|
out_pschItemAttribute->m_unAttrDefIndex = pAttrDef->GetDefinitionIndex(); |
|
out_pschItemAttribute->m_ulValue = ulValue; |
|
} |
|
|
|
virtual void LoadSchToEconAttributeValue( CEconItem *pTargetItem, const CEconItemAttributeDefinition *pAttrDef, const GCSDK::CRecordBase *pSchRecord ) const OVERRIDE |
|
{ |
|
Assert( pTargetItem ); |
|
Assert( pAttrDef ); |
|
Assert( pSchRecord ); |
|
Assert( pAttrDef->GetAttributeType() == this ); |
|
|
|
const CSchItemAttributeUInt64 *pschItemAttribute = GetTypedSch<const CSchItemAttributeUInt64>( pSchRecord ); |
|
|
|
pTargetItem->SetDynamicAttributeValue( pAttrDef, pschItemAttribute->m_ulValue ); |
|
} |
|
#endif // GC_DLL |
|
|
|
virtual bool BConvertStringToEconAttributeValue( const CEconItemAttributeDefinition *pAttrDef, const char *pszValue, union attribute_data_union_t *out_pValue, bool bEnableTerribleBackwardsCompatibilitySchemaParsingCode ) const OVERRIDE |
|
{ |
|
Assert( pAttrDef ); |
|
Assert( out_pValue ); |
|
|
|
out_pValue->asUint32 = V_atoui64( pszValue ); |
|
return true; |
|
} |
|
|
|
virtual void ConvertEconAttributeValueToString( const CEconItemAttributeDefinition *pAttrDef, const attribute_data_union_t& value, std::string *out_ps ) const OVERRIDE |
|
{ |
|
Assert( pAttrDef ); |
|
Assert( out_ps ); |
|
|
|
uint64 ulValue; |
|
ConvertEconAttributeValueToTypedValue( value, &ulValue ); |
|
|
|
*out_ps = CFmtStr( "%llu", ulValue ).Get(); |
|
} |
|
|
|
virtual void ConvertTypedValueToByteStream( const uint64& typedValue, ::std::string *out_psBytes ) const OVERRIDE |
|
{ |
|
Assert( out_psBytes ); |
|
Assert( out_psBytes->size() == 0 ); |
|
|
|
out_psBytes->resize( sizeof( uint64 ) ); |
|
*reinterpret_cast<uint64 *>( &((*out_psBytes)[0]) ) = typedValue; // overwrite string contents (sizeof( uint64 ) bytes) |
|
} |
|
|
|
virtual void ConvertByteStreamToTypedValue( const ::std::string& sBytes, uint64 *out_pTypedValue ) const OVERRIDE |
|
{ |
|
Assert( out_pTypedValue ); |
|
Assert( sBytes.size() == sizeof( uint64 ) ); |
|
|
|
*out_pTypedValue = *reinterpret_cast<const uint64 *>( &sBytes[0] ); |
|
} |
|
}; |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
class CSchemaAttributeType_Default : public CSchemaAttributeTypeBase< GC_SCH_REFERENCE( CSchItemAttribute ) attrib_value_t > |
|
{ |
|
public: |
|
#ifdef GC_DLL |
|
virtual bool BAssetClassExportedAttributeValue( const CEconItemAttributeDefinition *pAttrDef, const attribute_data_union_t& value ) const OVERRIDE |
|
{ |
|
Assert( pAttrDef ); |
|
|
|
static CSchemaAttributeDefHandle pAttrib_TradableAfter( "tradable after date" ); |
|
|
|
// Don't include "tradable after date" if it's in the past |
|
// See IEconItemInterface::IsTradable for the specific logic on how this affects tradability |
|
if ( pAttrDef == pAttrib_TradableAfter && CRTime::RTime32TimeCur() > value.asUint32 ) |
|
return false; |
|
|
|
return CSchemaAttributeTypeBase< GC_SCH_REFERENCE( CSchItemAttribute ) attrib_value_t >::BAssetClassExportedAttributeValue( pAttrDef, value ); |
|
} |
|
|
|
virtual void ConvertEconAttributeValueToSch( itemid_t unItemId, const CEconItemAttributeDefinition *pAttrDef, const union attribute_data_union_t& value, GCSDK::CRecordBase *out_pSchRecord ) const OVERRIDE |
|
{ |
|
Assert( out_pSchRecord ); |
|
Assert( pAttrDef ); |
|
Assert( pAttrDef->GetAttributeType() == this ); |
|
|
|
CSchItemAttribute *out_pschItemAttribute = GetTypedSch<CSchItemAttribute>( out_pSchRecord ); |
|
|
|
// @note Tom Bui: we store the value as an unsigned integer in the DB, so just treat the field as a bunch of bits |
|
out_pschItemAttribute->m_ulItemID = unItemId; |
|
out_pschItemAttribute->m_unAttrDefIndex = pAttrDef->GetDefinitionIndex(); |
|
out_pschItemAttribute->m_unValue = value.asUint32; |
|
} |
|
|
|
virtual void LoadSchToEconAttributeValue( CEconItem *pTargetItem, const CEconItemAttributeDefinition *pAttrDef, const GCSDK::CRecordBase *pSchRecord ) const OVERRIDE |
|
{ |
|
Assert( pTargetItem ); |
|
Assert( pAttrDef ); |
|
Assert( pSchRecord ); |
|
Assert( pAttrDef->GetAttributeType() == this ); |
|
|
|
const CSchItemAttribute *pschItemAttribute = GetTypedSch<const CSchItemAttribute>( pSchRecord ); |
|
|
|
pTargetItem->SetDynamicAttributeValue( pAttrDef, pschItemAttribute->m_unValue ); |
|
} |
|
|
|
virtual void LoadOrGenerateEconAttributeValue( CEconItem *pTargetItem, const CEconItemAttributeDefinition *pAttrDef, const static_attrib_t& staticAttrib, const CEconGameAccount *pGameAccount ) const OVERRIDE |
|
{ |
|
Assert( pTargetItem ); |
|
Assert( pTargetItem->GetItemDefinition() ); |
|
Assert( pAttrDef ); |
|
|
|
// Wear is reassigned by attributes but has a default value from itemdef prefab |
|
static CSchemaAttributeDefHandle pAttrDef_PaintkitWear( "set_item_texture_wear" ); |
|
|
|
// do not apply an attribute if it already exists. If the new and the old attribute value is different then we assert (and use the latest value) |
|
attrib_value_t unValue = 0; |
|
if ( pTargetItem->FindAttribute( pAttrDef, &unValue ) && pAttrDef != pAttrDef_PaintkitWear ) |
|
{ |
|
AssertMsg4( unValue == staticAttrib.m_value.asUint32, |
|
"Item id %llu (%s) attempting to generate dynamic attribute value for '%s' (%d) when attribute already exists with a different Value! This probably indicates some sort of code flow error calling LoadOrGenerateEconAttributeValue() late.", |
|
pTargetItem->GetItemID(), pTargetItem->GetItemDefinition()->GetDefinitionName(), pAttrDef->GetDefinitionName(), pAttrDef->GetDefinitionIndex() ); |
|
|
|
if ( unValue == staticAttrib.m_value.asUint32 ) |
|
return; |
|
} |
|
|
|
// Could be raw integer bits or raw floating-point bits depending on where in the union we stored the value. We copy the |
|
// bit pattern indiscriminately. |
|
attribute_data_union_t ResultValue; |
|
ResultValue = staticAttrib.m_value; |
|
GenerateEconAttributeValue( pAttrDef, staticAttrib, pGameAccount, &ResultValue ); |
|
LoadEconAttributeValue( pTargetItem, pAttrDef, ResultValue ); |
|
} |
|
|
|
virtual void GenerateEconAttributeValue( const CEconItemAttributeDefinition *pAttrDef, const static_attrib_t& staticAttrib, const CEconGameAccount *pGameAccount, attribute_data_union_t* out_pValue ) const |
|
{ |
|
Assert( pAttrDef ); |
|
AssertMsg( pGameAccount || !staticAttrib.m_pKVCustomData, "Cannot run custom logic with no game account object! Passing in NULL for pGameAccount is only supported when we know we won't be running custom value generation code!" ); |
|
Assert( out_pValue ); |
|
|
|
if( staticAttrib.m_pKVCustomData ) |
|
{ |
|
Internal_RunCustomAttributeValueLogic( staticAttrib, pGameAccount, out_pValue ); |
|
} |
|
} |
|
#endif // GC_DLL |
|
|
|
virtual bool BConvertStringToEconAttributeValue( const CEconItemAttributeDefinition *pAttrDef, const char *pszValue, union attribute_data_union_t *out_pValue, bool bEnableTerribleBackwardsCompatibilitySchemaParsingCode ) const OVERRIDE |
|
{ |
|
Assert( pAttrDef ); |
|
Assert( out_pValue ); |
|
|
|
if ( bEnableTerribleBackwardsCompatibilitySchemaParsingCode ) |
|
{ |
|
// Not having any value specified is valid -- we interpret this as "default", or 0 as both an in int and a float. |
|
out_pValue->asFloat = pszValue |
|
? atof( pszValue ) |
|
: 0.0f; |
|
} |
|
// This is terrible backwards-compatibility code to support the pulling of values from econ asset classes. |
|
else |
|
{ |
|
if ( pAttrDef->IsStoredAsInteger() ) |
|
{ |
|
out_pValue->asUint32 = (uint32)Q_atoui64( pszValue ); |
|
} |
|
else if ( pAttrDef->IsStoredAsFloat() ) |
|
{ |
|
out_pValue->asFloat = Q_atof( pszValue ); |
|
} |
|
else |
|
{ |
|
Assert( !"Unknown storage type for CSchemaAttributeType_Default::BConvertStringToEconAttributeValue()!" ); |
|
return false; |
|
} |
|
} |
|
|
|
return true; |
|
} |
|
|
|
virtual void ConvertEconAttributeValueToString( const CEconItemAttributeDefinition *pAttrDef, const attribute_data_union_t& value, std::string *out_ps ) const OVERRIDE |
|
{ |
|
Assert( pAttrDef ); |
|
Assert( out_ps ); |
|
|
|
if( pAttrDef->IsStoredAsFloat() ) |
|
{ |
|
*out_ps = CFmtStr( "%f", value.asFloat ).Get(); |
|
} |
|
else if( pAttrDef->IsStoredAsInteger() ) |
|
{ |
|
*out_ps = CFmtStr( "%u", value.asUint32 ).Get(); |
|
} |
|
else |
|
{ |
|
Assert( !"Unknown storage type for CSchemaAttributeType_Default::ConvertEconAttributeValueToString()!" ); |
|
} |
|
} |
|
|
|
virtual void ConvertTypedValueToByteStream( const attrib_value_t& typedValue, ::std::string *out_psBytes ) const OVERRIDE |
|
{ |
|
Assert( out_psBytes ); |
|
Assert( out_psBytes->size() == 0 ); |
|
|
|
out_psBytes->resize( sizeof( attrib_value_t ) ); |
|
*reinterpret_cast<attrib_value_t *>( &((*out_psBytes)[0]) ) = typedValue; // overwrite string contents (sizeof( attrib_value_t ) bytes) |
|
} |
|
|
|
virtual void ConvertByteStreamToTypedValue( const ::std::string& sBytes, attrib_value_t *out_pTypedValue ) const OVERRIDE |
|
{ |
|
Assert( out_pTypedValue ); |
|
#ifdef GC_DLL |
|
// The GC is expected to always have internally-consistent information. |
|
Assert( sBytes.size() == sizeof( attrib_value_t ) ); |
|
#else |
|
// Game clients and servers may have partially out-of-date information, or may have downloaded a new schema |
|
// but not know how to parse an attribute of a certain type, etc. In these cases, because we know we |
|
// aren't on the GC, temporarily failing to load these values until the client shuts down and updates |
|
// is about the best we can hope for. |
|
if ( sBytes.size() < sizeof( attrib_value_t ) ) |
|
{ |
|
*out_pTypedValue = attrib_value_t(); |
|
return; |
|
} |
|
#endif |
|
|
|
*out_pTypedValue = *reinterpret_cast<const attrib_value_t *>( &sBytes[0] ); |
|
} |
|
|
|
virtual bool BSupportsGameplayModificationAndNetworking() const OVERRIDE |
|
{ |
|
return true; |
|
} |
|
|
|
private: |
|
#ifdef GC_DLL |
|
void Internal_RunCustomAttributeValueLogic( const static_attrib_t& staticAttrib, const CEconGameAccount *pGameAccount, attribute_data_union_t* out_pValue ) const |
|
{ |
|
AssertMsg( pGameAccount, "No game account when running custom attribute value logic!" ); |
|
|
|
float flValue = 0; |
|
|
|
const char *pszMethod = staticAttrib.m_pKVCustomData->GetString( "method", NULL ); |
|
|
|
if ( Q_stricmp( pszMethod, "employee_number" ) == 0 ) |
|
{ |
|
flValue = pGameAccount->Obj().m_rtime32FirstPlayed; |
|
} |
|
else if ( Q_stricmp( pszMethod, "date" ) == 0 ) // Not used? |
|
{ |
|
flValue = CRTime::RTime32TimeCur(); |
|
} |
|
else if ( Q_stricmp( pszMethod, "year" ) == 0 ) |
|
{ |
|
flValue = CRTime( CRTime::RTime32TimeCur() ).GetYear(); |
|
} |
|
else if ( Q_stricmp( pszMethod, "gifts_given_out" ) == 0 ) |
|
{ |
|
flValue = pGameAccount->Obj().m_unNumGiftsGiven; |
|
} |
|
else if ( Q_stricmp( pszMethod, "expiration_period_hours_from_now" ) == 0 ) |
|
{ |
|
flValue = CRTime::RTime32DateAdd( CRTime::RTime32TimeCur(), staticAttrib.m_value.asFloat, k_ETimeUnitHour ); |
|
} |
|
else if ( Q_stricmp( pszMethod, "def index from lootlist" ) == 0 ) |
|
{ |
|
const char* pszLootlistName = staticAttrib.m_pKVCustomData->GetString( "lootlist" ); |
|
// Custom data stores the lootlist |
|
const CEconLootListDefinition* pLootList = GEconItemSchema().GetLootListByName( pszLootlistName ); |
|
|
|
if( !pLootList ) |
|
{ |
|
AssertMsg1( 0, "Lootlist '%s' not found when performing custom attribute logic", pszLootlistName ); |
|
return; |
|
} |
|
|
|
CUtlVector<CEconLootListDefinition::rolled_item_defs_t> vecRolledItems; |
|
// Roll our item def |
|
CDefaultUniformRandomStream RandomStream; |
|
if( !pLootList->RollRandomItemsAndAdditionalItems( &RandomStream, false, &vecRolledItems ) ) |
|
{ |
|
AssertMsg1( 0, "Error generating item defs from lootlist '%s'", pszLootlistName ); |
|
return; |
|
} |
|
|
|
// Just take the first one's def index |
|
flValue = vecRolledItems.Head().m_pItemDef->GetDefinitionIndex(); |
|
} |
|
else |
|
{ |
|
AssertMsg1( false, "Unknown value for 'method': '%s'", pszMethod ); |
|
} |
|
|
|
// Put the value into the right part of the union |
|
if ( staticAttrib.GetAttributeDefinition()->IsStoredAsFloat() ) |
|
{ |
|
(*out_pValue).asFloat = flValue; |
|
} |
|
else if ( staticAttrib.GetAttributeDefinition()->IsStoredAsInteger() ) |
|
{ |
|
(*out_pValue).asUint32 = (uint32)flValue; |
|
} |
|
else |
|
{ |
|
AssertMsg1( 0, "Unknown storage type for CSchemaAttributeType_Default::Internal_RunCustomAttributeValueLogic() for attribute %s", staticAttrib.GetAttributeDefinition()->GetDefinitionName() ); |
|
} |
|
} |
|
#endif |
|
}; |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Constructor |
|
//----------------------------------------------------------------------------- |
|
CEconItemAttributeDefinition::CEconItemAttributeDefinition( void ) |
|
: m_pKVAttribute( NULL ), |
|
m_pAttrType( NULL ), |
|
m_bHidden( false ), |
|
m_bWebSchemaOutputForced( false ), |
|
m_bStoredAsInteger( false ), |
|
m_bInstanceData( false ), |
|
m_bIsSetBonus( false ), |
|
m_iUserGenerationType( 0 ), |
|
m_iEffectType( ATTRIB_EFFECT_NEUTRAL ), |
|
m_iDescriptionFormat( 0 ), |
|
m_pszDescriptionString( NULL ), |
|
m_pszArmoryDesc( NULL ), |
|
m_pszDefinitionName( NULL ), |
|
m_pszAttributeClass( NULL ), |
|
m_ItemDefinitionTag( INVALID_ECON_TAG_HANDLE ), |
|
m_bCanAffectMarketName( false ), |
|
m_bCanAffectRecipeComponentName( false ) |
|
#ifndef GC_DLL |
|
, m_iszAttributeClass( NULL_STRING ) |
|
#endif |
|
{ |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Copy constructor |
|
//----------------------------------------------------------------------------- |
|
CEconItemAttributeDefinition::CEconItemAttributeDefinition( const CEconItemAttributeDefinition &that ) |
|
{ |
|
(*this) = that; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Operator= |
|
//----------------------------------------------------------------------------- |
|
CEconItemAttributeDefinition &CEconItemAttributeDefinition::operator=( const CEconItemAttributeDefinition &rhs ) |
|
{ |
|
m_nDefIndex = rhs.m_nDefIndex; |
|
m_pAttrType = rhs.m_pAttrType; |
|
m_bHidden = rhs.m_bHidden; |
|
m_bWebSchemaOutputForced = rhs.m_bWebSchemaOutputForced; |
|
m_bStoredAsInteger = rhs.m_bStoredAsInteger; |
|
m_iUserGenerationType = rhs.m_iUserGenerationType; |
|
m_bInstanceData = rhs.m_bInstanceData; |
|
m_bIsSetBonus = rhs.m_bIsSetBonus; |
|
m_iEffectType = rhs.m_iEffectType; |
|
m_iDescriptionFormat = rhs.m_iDescriptionFormat; |
|
m_pszDescriptionString = rhs.m_pszDescriptionString; |
|
m_pszArmoryDesc = rhs.m_pszArmoryDesc; |
|
m_pszDefinitionName = rhs.m_pszDefinitionName; |
|
m_pszAttributeClass = rhs.m_pszAttributeClass; |
|
m_ItemDefinitionTag = rhs.m_ItemDefinitionTag; |
|
m_bCanAffectMarketName = rhs.m_bCanAffectMarketName; |
|
m_bCanAffectRecipeComponentName = rhs.m_bCanAffectRecipeComponentName; |
|
#ifndef GC_DLL |
|
m_iszAttributeClass = rhs.m_iszAttributeClass; |
|
#endif |
|
|
|
m_pKVAttribute = NULL; |
|
if ( NULL != rhs.m_pKVAttribute ) |
|
{ |
|
m_pKVAttribute = rhs.m_pKVAttribute->MakeCopy(); |
|
|
|
// Re-assign string pointers |
|
m_pszDefinitionName = m_pKVAttribute->GetString("name"); |
|
m_pszDescriptionString = m_pKVAttribute->GetString( "description_string", NULL ); |
|
m_pszArmoryDesc = m_pKVAttribute->GetString( "armory_desc", NULL ); |
|
m_pszAttributeClass = m_pKVAttribute->GetString( "attribute_class", NULL ); |
|
|
|
Assert( V_strcmp( m_pszDefinitionName, rhs.m_pszDefinitionName ) == 0 ); |
|
Assert( V_strcmp( m_pszDescriptionString, rhs.m_pszDescriptionString ) == 0 ); |
|
Assert( V_strcmp( m_pszArmoryDesc, rhs.m_pszArmoryDesc ) == 0 ); |
|
Assert( V_strcmp( m_pszAttributeClass, rhs.m_pszAttributeClass ) == 0 ); |
|
} |
|
else |
|
{ |
|
Assert( m_pszDefinitionName == NULL ); |
|
Assert( m_pszDescriptionString == NULL ); |
|
Assert( m_pszArmoryDesc == NULL ); |
|
Assert( m_pszAttributeClass == NULL ); |
|
} |
|
return *this; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Destructor |
|
//----------------------------------------------------------------------------- |
|
CEconItemAttributeDefinition::~CEconItemAttributeDefinition( void ) |
|
{ |
|
if ( m_pKVAttribute ) |
|
m_pKVAttribute->deleteThis(); |
|
m_pKVAttribute = NULL; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Initialize the attribute definition |
|
// Input: pKVAttribute - The KeyValues representation of the attribute |
|
// schema - The overall item schema for this attribute |
|
// pVecErrors - An optional vector that will contain error messages if |
|
// the init fails. |
|
// Output: True if initialization succeeded, false otherwise |
|
//----------------------------------------------------------------------------- |
|
bool CEconItemAttributeDefinition::BInitFromKV( KeyValues *pKVAttribute, CUtlVector<CUtlString> *pVecErrors /* = NULL */ ) |
|
{ |
|
m_pKVAttribute = pKVAttribute->MakeCopy(); |
|
m_nDefIndex = Q_atoi( m_pKVAttribute->GetName() ); |
|
|
|
m_pszDefinitionName = m_pKVAttribute->GetString("name", "(unnamed)"); |
|
m_bHidden = m_pKVAttribute->GetInt( "hidden", 0 ) != 0; |
|
m_bWebSchemaOutputForced = m_pKVAttribute->GetInt( "force_output_description", 0 ) != 0; |
|
m_bStoredAsInteger = m_pKVAttribute->GetInt( "stored_as_integer", 0 ) != 0; |
|
m_bIsSetBonus = m_pKVAttribute->GetBool( "is_set_bonus", false ); |
|
m_bCanAffectMarketName = m_pKVAttribute->GetBool( "can_affect_market_name", false ); |
|
m_bCanAffectRecipeComponentName = m_pKVAttribute->GetBool( "can_affect_recipe_component_name", false ); |
|
m_iUserGenerationType = m_pKVAttribute->GetInt( "is_user_generated", 0 ); |
|
m_iEffectType = (attrib_effect_types_t)StringFieldToInt( m_pKVAttribute->GetString("effect_type"), g_EffectTypes, ARRAYSIZE(g_EffectTypes) ); |
|
m_iDescriptionFormat = StringFieldToInt( m_pKVAttribute->GetString("description_format"), g_AttributeDescriptionFormats, ARRAYSIZE(g_AttributeDescriptionFormats) ); |
|
m_pszDescriptionString = m_pKVAttribute->GetString( "description_string", NULL ); |
|
m_pszArmoryDesc = m_pKVAttribute->GetString( "armory_desc", NULL ); |
|
m_pszAttributeClass = m_pKVAttribute->GetString( "attribute_class", NULL ); |
|
m_bInstanceData = pKVAttribute->GetBool( "instance_data", false ); |
|
|
|
const char *pszTag = m_pKVAttribute->GetString( "apply_tag_to_item_definition", NULL ); |
|
m_ItemDefinitionTag = pszTag ? GetItemSchema()->GetHandleForTag( pszTag ) : INVALID_ECON_TAG_HANDLE; |
|
|
|
#if defined(CLIENT_DLL) || defined(GAME_DLL) |
|
m_iszAttributeClass = NULL_STRING; |
|
#endif |
|
const char *pszAttrType = m_pKVAttribute->GetString( "attribute_type", NULL ); // NULL implies "default type" for backwards compatibility |
|
m_pAttrType = GetItemSchema()->GetAttributeType( pszAttrType ); |
|
|
|
SCHEMA_INIT_CHECK( |
|
NULL != m_pKVAttribute->FindKey( "name" ), |
|
"Attribute definition %s: Missing required field \"name\"", m_pKVAttribute->GetName() ); |
|
|
|
SCHEMA_INIT_CHECK( |
|
NULL != m_pAttrType, |
|
"Attribute definition %s: Unable to find attribute data type '%s'", m_pszDefinitionName, pszAttrType ? pszAttrType : "(default)" ); |
|
|
|
if ( m_bIsSetBonus ) |
|
{ |
|
SCHEMA_INIT_CHECK( |
|
m_pAttrType->BSupportsGameplayModificationAndNetworking(), |
|
"Attribute definition %s: set as set bonus attribute but does not support gameplay modification/networking!", m_pszDefinitionName ); |
|
} |
|
|
|
m_unAssetClassBucket = pKVAttribute->GetInt( "asset_class_bucket", 0 ); |
|
m_eAssetClassAttrExportRule = k_EAssetClassAttrExportRule_Default; |
|
if ( char const *szRule = pKVAttribute->GetString( "asset_class_export", NULL ) ) |
|
{ |
|
if ( !V_stricmp( szRule, "skip" ) ) |
|
{ |
|
m_eAssetClassAttrExportRule = k_EAssetClassAttrExportRule_Skip; |
|
} |
|
else if ( !V_stricmp( szRule, "gconly" ) ) |
|
{ |
|
m_eAssetClassAttrExportRule = EAssetClassAttrExportRule_t( k_EAssetClassAttrExportRule_GCOnly | k_EAssetClassAttrExportRule_Skip ); |
|
} |
|
else if ( !V_stricmp( szRule, "bucketed" ) ) |
|
{ |
|
SCHEMA_INIT_CHECK( m_unAssetClassBucket, "Attribute definition %s: Asset class export rule '%s' is incompatible", m_pszDefinitionName, szRule ); |
|
m_eAssetClassAttrExportRule = k_EAssetClassAttrExportRule_Bucketed; |
|
} |
|
else if ( !V_stricmp( szRule, "default" ) ) |
|
{ |
|
m_eAssetClassAttrExportRule = k_EAssetClassAttrExportRule_Default; |
|
} |
|
else |
|
{ |
|
SCHEMA_INIT_CHECK( false, "Attribute definition %s: Invalid asset class export rule '%s'", m_pszDefinitionName, szRule ); |
|
} |
|
} |
|
|
|
// Check for misuse of asset class bucket |
|
SCHEMA_INIT_CHECK( ( !m_unAssetClassBucket || m_bInstanceData ), "Attribute definition %s: Cannot use \"asset_class_bucket\" on class-level attributes", m_pKVAttribute->GetName() ); |
|
|
|
|
|
return SCHEMA_INIT_SUCCESS(); |
|
} |
|
|
|
CQuestObjectiveDefinition::CQuestObjectiveDefinition( void ) |
|
: m_pszDescriptionToken( NULL ) |
|
, m_nDefIndex( 0 ) |
|
, m_nPoints( 0 ) |
|
{} |
|
|
|
CQuestObjectiveDefinition::~CQuestObjectiveDefinition() |
|
{} |
|
|
|
bool CQuestObjectiveDefinition::BInitFromKV( KeyValues *pKVItem, CUtlVector<CUtlString> *pVecErrors /* = NULL */ ) |
|
{ |
|
m_nDefIndex = pKVItem->GetInt( "defindex", -1 ); |
|
m_pszDescriptionToken = pKVItem->GetString( "description_string" ); |
|
m_bOptional = pKVItem->GetBool( "optional", false ); |
|
m_bAdvanced = pKVItem->GetBool( "advanced", false ); |
|
m_nPoints = pKVItem->GetInt( "points", 0 ); |
|
|
|
SCHEMA_INIT_CHECK( m_nDefIndex != -1, "Quest objective missing def index" ); |
|
SCHEMA_INIT_CHECK( m_pszDescriptionToken != NULL, "Quest objective is missing a description" ); |
|
|
|
return SCHEMA_INIT_SUCCESS(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Constructor |
|
//----------------------------------------------------------------------------- |
|
CEconItemDefinition::CEconItemDefinition( void ) |
|
: m_pKVItem( NULL ), |
|
m_bEnabled( false ), |
|
m_unMinItemLevel( 1 ), |
|
m_unMaxItemLevel( 1 ), |
|
m_iArmoryRemap( 0 ), |
|
m_iStoreRemap( 0 ), |
|
m_nItemQuality( k_unItemQuality_Any ), |
|
m_nForcedItemQuality( k_unItemQuality_Any ), |
|
m_nDefaultDropQuantity( 1 ), |
|
m_bLoadOnDemand( false ), |
|
m_pTool( NULL ), |
|
m_rtExpiration( 0 ), |
|
m_BundleInfo( NULL ), |
|
#ifdef TF_CLIENT_DLL |
|
m_unNumConcreteItems( 0 ), |
|
#endif // TF_CLIENT_DLL |
|
m_nPopularitySeed( 0 ), |
|
m_pszDefinitionName( NULL ), |
|
m_pszItemClassname( NULL ), |
|
m_pszClassToken( NULL ), |
|
m_pszSlotToken( NULL ), |
|
m_pszItemBaseName( NULL ), |
|
m_pszItemTypeName( NULL ), |
|
m_pszItemDesc( NULL ), |
|
m_pszArmoryDesc( NULL ), |
|
m_pszInventoryModel( NULL ), |
|
m_pszInventoryImage( NULL ), |
|
m_pszHolidayRestriction( NULL ), |
|
m_iSubType( 0 ), |
|
m_pszBaseDisplayModel( NULL ), |
|
m_iDefaultSkin( -1 ), |
|
m_pszWorldDisplayModel( NULL ), |
|
m_pszWorldExtraWearableModel( NULL ), |
|
m_pszWorldExtraWearableViewModel( NULL ), |
|
m_pszVisionFilteredDisplayModel( NULL ), |
|
m_pszBrassModelOverride( NULL ), |
|
m_bHideBodyGroupsDeployedOnly( false ), |
|
m_bAttachToHands( false ), |
|
m_bAttachToHandsVMOnly( false ), |
|
m_bProperName( false ), |
|
m_bFlipViewModel( false ), |
|
m_bActAsWearable( false ), |
|
m_bActAsWeapon( false ), |
|
m_iDropType( 1 ), |
|
m_bHidden( false ), |
|
m_bShouldShowInArmory( false ), |
|
m_bIsPackBundle( false ), |
|
m_pOwningPackBundle( NULL ), |
|
m_bIsPackItem( false ), |
|
m_bBaseItem( false ), |
|
m_pszItemLogClassname( NULL ), |
|
m_pszItemIconClassname( NULL ), |
|
m_pszDatabaseAuditTable( NULL ), |
|
m_bImported( false ), |
|
m_pItemSetDef( NULL ), |
|
m_pItemCollectionDef( NULL ), |
|
m_pItemPaintKitDef( NULL ), |
|
m_pszArmoryRemap( NULL ), |
|
m_pszStoreRemap( NULL ), |
|
m_unSetItemRemapDefIndex( INVALID_ITEM_DEF_INDEX ), |
|
m_pszXifierRemapClass( NULL ), |
|
m_pszBaseFunctionalItemName( NULL ), |
|
m_pszParticleSuffix( NULL ), |
|
m_pszCollectionReference( NULL ), |
|
m_nItemRarity( k_unItemRarity_Any ), |
|
m_unItemSeries( 0 ), |
|
m_bValidForShuffle( false ), |
|
m_bValidForSelfMade( true ) |
|
{ |
|
for ( int team = 0; team < TEAM_VISUAL_SECTIONS; team++ ) |
|
{ |
|
m_PerTeamVisuals[team] = NULL; |
|
} |
|
|
|
m_pDictIcons = new CUtlDict< CUtlString >; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Destructor |
|
//----------------------------------------------------------------------------- |
|
CEconItemDefinition::~CEconItemDefinition( void ) |
|
{ |
|
for ( int i = 0; i < ARRAYSIZE( m_PerTeamVisuals ); i++ ) |
|
delete m_PerTeamVisuals[i]; |
|
|
|
#ifdef GC_DLL |
|
m_vecPropertyGenerators.PurgeAndDeleteElements(); |
|
#endif // GC_DLL |
|
|
|
if ( m_pKVItem ) |
|
m_pKVItem->deleteThis(); |
|
m_pKVItem = NULL; |
|
delete m_pTool; |
|
delete m_BundleInfo; |
|
delete m_pDictIcons; |
|
} |
|
|
|
#if defined(CLIENT_DLL) || defined(GAME_DLL) |
|
//----------------------------------------------------------------------------- |
|
// Purpose: Stomp our base data with extra testing data specified by the player |
|
//----------------------------------------------------------------------------- |
|
bool CEconItemDefinition::BInitFromTestItemKVs( int iNewDefIndex, KeyValues *pKVItem, CUtlVector<CUtlString>* pVecErrors ) |
|
{ |
|
// The KeyValues are stored in the player entity, so we can cache our name there |
|
|
|
m_nDefIndex = iNewDefIndex; |
|
m_unSetItemRemapDefIndex = m_nDefIndex; |
|
|
|
bool bTestingExistingItem = pKVItem->GetBool( "test_existing_item", false ); |
|
if ( !bTestingExistingItem ) |
|
{ |
|
m_pszDefinitionName = pKVItem->GetString( "name", NULL ); |
|
m_pszItemBaseName = pKVItem->GetString( "name", NULL ); |
|
|
|
#ifdef CLIENT_DLL |
|
pKVItem->SetString( "name", VarArgs("Test Item %d", iNewDefIndex) ); |
|
#else |
|
pKVItem->SetString( "name", UTIL_VarArgs("Test Item %d", iNewDefIndex) ); |
|
#endif |
|
|
|
m_pszBaseDisplayModel = pKVItem->GetString( "model_player", NULL ); |
|
m_pszVisionFilteredDisplayModel = pKVItem->GetString( "model_vision_filtered", NULL ); |
|
m_bAttachToHands = pKVItem->GetInt( "attach_to_hands", 0 ) != 0; |
|
|
|
BInitVisualBlockFromKV( pKVItem ); |
|
} |
|
|
|
// Handle attributes |
|
m_vecStaticAttributes.Purge(); |
|
int iPaintCanIndex = pKVItem->GetInt("paintcan_index", 0); |
|
if ( iPaintCanIndex ) |
|
{ |
|
static CSchemaAttributeDefHandle pAttrDef_PaintRGB( "set item tint RGB" ); |
|
|
|
const CEconItemDefinition *pCanDef = GetItemSchema()->GetItemDefinition(iPaintCanIndex); |
|
|
|
float flRGBVal; |
|
if ( pCanDef && pAttrDef_PaintRGB && FindAttribute_UnsafeBitwiseCast<attrib_value_t>( pCanDef, pAttrDef_PaintRGB, &flRGBVal ) ) |
|
{ |
|
static_attrib_t& StaticAttrib = m_vecStaticAttributes[ m_vecStaticAttributes.AddToTail() ]; |
|
|
|
StaticAttrib.iDefIndex = pAttrDef_PaintRGB->GetDefinitionIndex(); |
|
StaticAttrib.m_value.asFloat = flRGBVal; // this is bad! but we're in crazy hack code for UI customization of item definitions that don't exist so |
|
} |
|
} |
|
|
|
int iUnusualEffectIndex = pKVItem->GetInt( "unusual_index", 0 ); |
|
if ( iUnusualEffectIndex ) |
|
{ |
|
static CSchemaAttributeDefHandle pAttrDef_AttachParticleStatic( "attach particle effect static" ); |
|
|
|
const attachedparticlesystem_t *pSystem = GetItemSchema()->GetAttributeControlledParticleSystem( iUnusualEffectIndex ); |
|
|
|
if ( pAttrDef_AttachParticleStatic && pSystem ) |
|
{ |
|
static_attrib_t& StaticAttrib = m_vecStaticAttributes[ m_vecStaticAttributes.AddToTail() ]; |
|
|
|
StaticAttrib.iDefIndex = pAttrDef_AttachParticleStatic->GetDefinitionIndex(); |
|
StaticAttrib.m_value.asFloat = iUnusualEffectIndex; // this is bad! but we're in crazy hack code for UI customization of item definitions that don't exist so |
|
} |
|
} |
|
|
|
return true; |
|
} |
|
|
|
animation_on_wearable_t *GetOrCreateAnimationActivity( perteamvisuals_t *pVisData, const char *pszActivityName ) |
|
{ |
|
FOR_EACH_VEC( pVisData->m_Animations, i ) |
|
{ |
|
if ( Q_stricmp(pVisData->m_Animations[i].pszActivity, pszActivityName) == 0 ) |
|
return &pVisData->m_Animations[i]; |
|
} |
|
|
|
animation_on_wearable_t *pEntry = &pVisData->m_Animations[pVisData->m_Animations.AddToTail()]; |
|
|
|
pEntry->iActivity = kActivityLookup_Unknown; // We can't look it up yet, the activity list hasn't been populated. |
|
pEntry->pszActivity = pszActivityName; |
|
pEntry->iReplacement = kActivityLookup_Unknown; |
|
pEntry->pszReplacement = NULL; |
|
pEntry->pszSequence = NULL; |
|
pEntry->pszScene = NULL; |
|
pEntry->pszRequiredItem = NULL; |
|
|
|
return pEntry; |
|
} |
|
|
|
activity_on_wearable_t *GetOrCreatePlaybackActivity( perteamvisuals_t *pVisData, wearableanimplayback_t iPlayback ) |
|
{ |
|
FOR_EACH_VEC( pVisData->m_Animations, i ) |
|
{ |
|
if ( pVisData->m_Activities[i].iPlayback == iPlayback ) |
|
return &pVisData->m_Activities[i]; |
|
} |
|
|
|
activity_on_wearable_t *pEntry = &pVisData->m_Activities[pVisData->m_Activities.AddToTail()]; |
|
|
|
pEntry->iPlayback = iPlayback; |
|
pEntry->iActivity = kActivityLookup_Unknown; // We can't look it up yet, the activity list hasn't been populated. |
|
pEntry->pszActivity = NULL; |
|
|
|
return pEntry; |
|
} |
|
|
|
#endif // defined(CLIENT_DLL) || defined(GAME_DLL) |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Handle parsing the per-team visual block from the keyvalues |
|
//----------------------------------------------------------------------------- |
|
void CEconItemDefinition::BInitVisualBlockFromKV( KeyValues *pKVItem, CUtlVector<CUtlString> *pVecErrors ) |
|
{ |
|
// Visuals |
|
for ( int team = 0; team < TEAM_VISUAL_SECTIONS; team++ ) |
|
{ |
|
m_PerTeamVisuals[team] = NULL; |
|
|
|
if ( !g_TeamVisualSections[team] ) |
|
continue; |
|
|
|
KeyValues *pVisualsKV = pKVItem->FindKey( g_TeamVisualSections[team] ); |
|
if ( pVisualsKV ) |
|
{ |
|
perteamvisuals_t *pVisData = new perteamvisuals_t(); |
|
#if defined(CLIENT_DLL) || defined(GAME_DLL) |
|
KeyValues *pKVEntry = pVisualsKV->GetFirstSubKey(); |
|
while ( pKVEntry ) |
|
{ |
|
const char *pszEntry = pKVEntry->GetName(); |
|
|
|
if ( !Q_stricmp( pszEntry, "use_visualsblock_as_base" ) ) |
|
{ |
|
// Start with a copy of an existing PerTeamVisuals |
|
const char *pszString = pKVEntry->GetString(); |
|
int nOverrideTeam = GetTeamVisualsFromString( pszString ); |
|
if ( nOverrideTeam != -1 ) |
|
{ |
|
*pVisData = *m_PerTeamVisuals[nOverrideTeam]; |
|
} |
|
else |
|
{ |
|
pVecErrors->AddToTail( CFmtStr( "Unknown visuals block: %s", pszString ).Access() ); |
|
} |
|
} |
|
else if ( !Q_stricmp( pszEntry, "attached_models" ) ) |
|
{ |
|
FOR_EACH_SUBKEY( pKVEntry, pKVAttachedModelData ) |
|
{ |
|
int iAtt = pVisData->m_AttachedModels.AddToTail(); |
|
pVisData->m_AttachedModels[iAtt].m_iModelDisplayFlags = pKVAttachedModelData->GetInt( "model_display_flags", kAttachedModelDisplayFlag_MaskAll ); |
|
pVisData->m_AttachedModels[iAtt].m_pszModelName = pKVAttachedModelData->GetString( "model", NULL ); |
|
} |
|
} |
|
else if ( !Q_stricmp( pszEntry, "attached_models_festive" ) ) |
|
{ |
|
FOR_EACH_SUBKEY( pKVEntry, pKVAttachedModelData ) |
|
{ |
|
int iAtt = pVisData->m_AttachedModelsFestive.AddToTail(); |
|
pVisData->m_AttachedModelsFestive[iAtt].m_iModelDisplayFlags = pKVAttachedModelData->GetInt( "model_display_flags", kAttachedModelDisplayFlag_MaskAll ); |
|
pVisData->m_AttachedModelsFestive[iAtt].m_pszModelName = pKVAttachedModelData->GetString( "model", NULL ); |
|
} |
|
} |
|
else if ( !Q_stricmp( pszEntry, "attached_particlesystems" ) ) |
|
{ |
|
FOR_EACH_SUBKEY( pKVEntry, pKVAttachedParticleSystemData ) |
|
{ |
|
int iAtt = pVisData->m_AttachedParticles.AddToTail(); |
|
pVisData->m_AttachedParticles[iAtt].pszSystemName = pKVAttachedParticleSystemData->GetString( "system", NULL ); |
|
pVisData->m_AttachedParticles[iAtt].pszControlPoints[0] = pKVAttachedParticleSystemData->GetString( "attachment", NULL ); |
|
pVisData->m_AttachedParticles[iAtt].bFollowRootBone = pKVAttachedParticleSystemData->GetBool( "attach_to_rootbone" ); |
|
pVisData->m_AttachedParticles[iAtt].iCustomType = 0; |
|
} |
|
} |
|
else if ( !Q_stricmp( pszEntry, "custom_particlesystem2" ) ) |
|
{ |
|
int iAtt = pVisData->m_AttachedParticles.AddToTail(); |
|
pVisData->m_AttachedParticles[iAtt].pszSystemName = pKVEntry->GetString( "system", NULL ); |
|
pVisData->m_AttachedParticles[iAtt].iCustomType = 2; |
|
} |
|
else if ( !Q_stricmp( pszEntry, "custom_particlesystem" ) ) |
|
{ |
|
int iAtt = pVisData->m_AttachedParticles.AddToTail(); |
|
pVisData->m_AttachedParticles[iAtt].pszSystemName = pKVEntry->GetString( "system", NULL ); |
|
pVisData->m_AttachedParticles[iAtt].iCustomType = 1; |
|
} |
|
else if ( !Q_stricmp( pszEntry, "playback_activity" ) ) |
|
{ |
|
FOR_EACH_SUBKEY( pKVEntry, pKVSubKey ) |
|
{ |
|
int iPlaybackInt = StringFieldToInt( pKVSubKey->GetName(), g_WearableAnimTypeStrings, ARRAYSIZE(g_WearableAnimTypeStrings) ); |
|
if ( iPlaybackInt >= 0 ) |
|
{ |
|
activity_on_wearable_t *pEntry = GetOrCreatePlaybackActivity( pVisData, (wearableanimplayback_t)iPlaybackInt ); |
|
pEntry->pszActivity = pKVSubKey->GetString(); |
|
} |
|
} |
|
} |
|
else if ( !Q_stricmp( pszEntry, "animation_replacement" ) ) |
|
{ |
|
FOR_EACH_SUBKEY( pKVEntry, pKVSubKey ) |
|
{ |
|
animation_on_wearable_t *pEntry = GetOrCreateAnimationActivity( pVisData, pKVSubKey->GetName() ); |
|
pEntry->pszReplacement = pKVSubKey->GetString(); |
|
} |
|
} |
|
else if ( !Q_stricmp( pszEntry, "animation_sequence" ) ) |
|
{ |
|
FOR_EACH_SUBKEY( pKVEntry, pKVSubKey ) |
|
{ |
|
animation_on_wearable_t *pEntry = GetOrCreateAnimationActivity( pVisData, pKVSubKey->GetName() ); |
|
pEntry->pszSequence = pKVSubKey->GetString(); |
|
} |
|
} |
|
else if ( !Q_stricmp( pszEntry, "animation_scene" ) ) |
|
{ |
|
FOR_EACH_SUBKEY( pKVEntry, pKVSubKey ) |
|
{ |
|
animation_on_wearable_t *pEntry = GetOrCreateAnimationActivity( pVisData, pKVSubKey->GetName() ); |
|
pEntry->pszScene = pKVSubKey->GetString(); |
|
} |
|
} |
|
else if ( !Q_stricmp( pszEntry, "animation_required_item" ) ) |
|
{ |
|
FOR_EACH_SUBKEY( pKVEntry, pKVSubKey ) |
|
{ |
|
animation_on_wearable_t *pEntry = GetOrCreateAnimationActivity( pVisData, pKVSubKey->GetName() ); |
|
pEntry->pszRequiredItem = pKVSubKey->GetString(); |
|
} |
|
} |
|
else if ( !Q_stricmp( pszEntry, "player_bodygroups" ) ) |
|
{ |
|
FOR_EACH_SUBKEY( pKVEntry, pKVBodygroupKey ) |
|
{ |
|
const char *pszBodygroupName = pKVBodygroupKey->GetName(); |
|
int iValue = pKVBodygroupKey->GetInt(); |
|
|
|
// Track bodygroup information for this item in particular. |
|
pVisData->m_Maps.m_ModifiedBodyGroupNames.Insert( pszBodygroupName, iValue ); |
|
|
|
// Track global schema state. |
|
GetItemSchema()->AssignDefaultBodygroupState( pszBodygroupName, iValue ); |
|
} |
|
} |
|
else if ( !Q_stricmp( pszEntry, "skin" ) ) |
|
{ |
|
pVisData->iSkin = pKVEntry->GetInt(); |
|
} |
|
else if ( !Q_stricmp( pszEntry, "use_per_class_bodygroups" ) ) |
|
{ |
|
pVisData->bUsePerClassBodygroups = pKVEntry->GetBool(); |
|
} |
|
else if ( !Q_stricmp( pszEntry, "muzzle_flash" ) ) |
|
{ |
|
pVisData->pszMuzzleFlash = pKVEntry->GetString(); |
|
} |
|
else if ( !Q_stricmp( pszEntry, "tracer_effect" ) ) |
|
{ |
|
pVisData->pszTracerEffect = pKVEntry->GetString(); |
|
} |
|
else if ( !Q_stricmp( pszEntry, "particle_effect" ) ) |
|
{ |
|
pVisData->pszParticleEffect = pKVEntry->GetString(); |
|
} |
|
else if ( !Q_strnicmp( pszEntry, "custom_sound", 12 ) ) // intentionally comparing prefixes |
|
{ |
|
int iIndex = 0; |
|
if ( pszEntry[12] ) |
|
{ |
|
iIndex = clamp( atoi( &pszEntry[12] ), 0, MAX_VISUALS_CUSTOM_SOUNDS-1 ); |
|
} |
|
pVisData->pszCustomSounds[iIndex] = pKVEntry->GetString(); |
|
} |
|
else if ( !Q_stricmp( pszEntry, "material_override" ) ) |
|
{ |
|
pVisData->pszMaterialOverride = pKVEntry->GetString(); |
|
} |
|
else if ( !Q_strnicmp( pszEntry, "sound_", 6 ) ) // intentionally comparing prefixes |
|
{ |
|
int iIndex = GetWeaponSoundFromString( &pszEntry[6] ); |
|
if ( iIndex != -1 ) |
|
{ |
|
pVisData->pszWeaponSoundReplacements[iIndex] = pKVEntry->GetString(); |
|
} |
|
} |
|
else if ( !Q_stricmp( pszEntry, "code_controlled_bodygroup" ) ) |
|
{ |
|
const char *pBodyGroupName = pKVEntry->GetString( "bodygroup", NULL ); |
|
const char *pFuncName = pKVEntry->GetString( "function", NULL ); |
|
if ( pBodyGroupName && pFuncName ) |
|
{ |
|
codecontrolledbodygroupdata_t ccbgd = { pFuncName, NULL }; |
|
pVisData->m_Maps.m_CodeControlledBodyGroupNames.Insert( pBodyGroupName, ccbgd ); |
|
} |
|
} |
|
else if ( !Q_stricmp( pszEntry, "vm_bodygroup_override" ) ) |
|
{ |
|
pVisData->m_iViewModelBodyGroupOverride = pKVEntry->GetInt(); |
|
} |
|
else if ( !Q_stricmp( pszEntry, "vm_bodygroup_state_override" ) ) |
|
{ |
|
pVisData->m_iViewModelBodyGroupStateOverride = pKVEntry->GetInt(); |
|
} |
|
else if ( !Q_stricmp( pszEntry, "wm_bodygroup_override" ) ) |
|
{ |
|
pVisData->m_iWorldModelBodyGroupOverride = pKVEntry->GetInt(); |
|
} |
|
else if ( !Q_stricmp( pszEntry, "wm_bodygroup_state_override" ) ) |
|
{ |
|
pVisData->m_iWorldModelBodyGroupStateOverride = pKVEntry->GetInt(); |
|
} |
|
|
|
pKVEntry = pKVEntry->GetNextKey(); |
|
} |
|
#endif // defined(CLIENT_DLL) || defined(GAME_DLL) |
|
KeyValues *pStylesDataKV = pVisualsKV->FindKey( "styles" ); |
|
if ( pStylesDataKV ) |
|
{ |
|
// Styles are only valid in the base "visuals" section. |
|
if ( team == 0 ) |
|
{ |
|
BInitStylesBlockFromKV( pStylesDataKV, pVisData, pVecErrors ); |
|
} |
|
// ...but they used to be valid everywhere, so spit out a warning if people are trying to use |
|
// the old style of per-team styles. |
|
else |
|
{ |
|
pVecErrors->AddToTail( "Per-team styles blocks are no longer valid. Use \"skin_red\" and \"skin_blu\" in a style entry instead." ); |
|
} |
|
} |
|
|
|
m_PerTeamVisuals[team] = pVisData; |
|
} |
|
} |
|
} |
|
|
|
#ifdef GC_DLL |
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
template < typename T > |
|
static void NthPermutation ( T *pData, unsigned int unDataCount, unsigned int unIdx ) |
|
{ |
|
for ( unsigned int i = 1; i < unDataCount; i++ ) |
|
{ |
|
std::swap( pData[ unIdx % (i + 1) ], pData[ i ] ); |
|
unIdx = unIdx / (i + 1); |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
bool CEconItemDefinition::BApplyPropertyGenerators( CEconItem *pItem ) const |
|
{ |
|
Assert( pItem ); |
|
|
|
for ( const IEconItemPropertyGenerator *pPropertyGenerator : m_vecPropertyGenerators ) |
|
{ |
|
if ( !pPropertyGenerator->BGenerateProperties( pItem ) ) |
|
return false; |
|
} |
|
|
|
return true; |
|
} |
|
|
|
#endif // GC_DLL |
|
|
|
#if defined(CLIENT_DLL) || defined(GAME_DLL) |
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CEconItemDefinition::GeneratePrecacheModelStrings( bool bDynamicLoad, CUtlVector<const char *> *out_pVecModelStrings ) const |
|
{ |
|
Assert( out_pVecModelStrings ); |
|
|
|
// Add base model. |
|
out_pVecModelStrings->AddToTail( GetBasePlayerDisplayModel() ); |
|
|
|
// Add styles. |
|
if ( GetNumStyles() ) |
|
{ |
|
for ( style_index_t i=0; i<GetNumStyles(); ++i ) |
|
{ |
|
const CEconStyleInfo *pStyle = GetStyleInfo( i ); |
|
Assert( pStyle ); |
|
|
|
pStyle->GeneratePrecacheModelStringsForStyle( out_pVecModelStrings ); |
|
} |
|
} |
|
|
|
// Precache all the attached models |
|
for ( int team = 0; team < TEAM_VISUAL_SECTIONS; team++ ) |
|
{ |
|
perteamvisuals_t *pPerTeamVisuals = GetPerTeamVisual( team ); |
|
if ( !pPerTeamVisuals ) |
|
continue; |
|
|
|
for ( int model = 0; model < pPerTeamVisuals->m_AttachedModels.Count(); model++ ) |
|
{ |
|
out_pVecModelStrings->AddToTail( pPerTeamVisuals->m_AttachedModels[model].m_pszModelName ); |
|
} |
|
|
|
// Festive |
|
for ( int model = 0; model < pPerTeamVisuals->m_AttachedModelsFestive.Count(); model++ ) |
|
{ |
|
out_pVecModelStrings->AddToTail( pPerTeamVisuals->m_AttachedModelsFestive[model].m_pszModelName ); |
|
} |
|
} |
|
|
|
if ( GetExtraWearableModel() ) |
|
{ |
|
out_pVecModelStrings->AddToTail( GetExtraWearableModel() ); |
|
} |
|
|
|
if ( GetExtraWearableViewModel() ) |
|
{ |
|
out_pVecModelStrings->AddToTail( GetExtraWearableViewModel() ); |
|
} |
|
|
|
if ( GetVisionFilteredDisplayModel() ) |
|
{ |
|
out_pVecModelStrings->AddToTail( GetVisionFilteredDisplayModel() ); |
|
} |
|
|
|
// We don't need to cache the inventory model, because it's never loaded by the game |
|
} |
|
|
|
void CEconItemDefinition::GeneratePrecacheSoundStrings( bool bDynamicLoad, CUtlVector<const char *> *out_pVecSoundStrings ) const |
|
{ |
|
Assert( out_pVecSoundStrings ); |
|
|
|
for ( int iTeam = 0; iTeam < TEAM_VISUAL_SECTIONS; ++iTeam ) |
|
{ |
|
for ( int iSound = 0; iSound < MAX_VISUALS_CUSTOM_SOUNDS; ++iSound ) |
|
{ |
|
const char *pSoundName = GetCustomSound( iTeam, iSound ); |
|
if ( pSoundName && pSoundName[ 0 ] != '\0' ) |
|
{ |
|
out_pVecSoundStrings->AddToTail( pSoundName ); |
|
} |
|
} |
|
} |
|
} |
|
#endif // #if defined(CLIENT_DLL) || defined(GAME_DLL) |
|
|
|
//----------------------------------------------------------------------------- |
|
const char *CEconItemDefinition::GetDefinitionString( const char *pszKeyName, const char *pszDefaultValue ) const |
|
{ |
|
// !FIXME! Here we could do a dynamic lookup to apply the prefab overlay logic. |
|
// This could save a lot of duplicated data |
|
if ( m_pKVItem ) |
|
return m_pKVItem->GetString( pszKeyName, pszDefaultValue ); |
|
return pszDefaultValue; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
KeyValues *CEconItemDefinition::GetDefinitionKey( const char *pszKeyName ) const |
|
{ |
|
// !FIXME! Here we could do a dynamic lookup to apply the prefab overlay logic. |
|
// This could save a lot of duplicated data |
|
if ( m_pKVItem ) |
|
return m_pKVItem->FindKey( pszKeyName ); |
|
return NULL; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Parse the styles sub-section of the visuals block. |
|
//----------------------------------------------------------------------------- |
|
void CEconItemDefinition::BInitStylesBlockFromKV( KeyValues *pKVStyles, perteamvisuals_t *pVisData, CUtlVector<CUtlString> *pVecErrors ) |
|
{ |
|
FOR_EACH_SUBKEY( pKVStyles, pKVStyle ) |
|
{ |
|
CEconStyleInfo *pStyleInfo = GetItemSchema()->CreateEconStyleInfo(); |
|
Assert( pStyleInfo ); |
|
|
|
pStyleInfo->BInitFromKV( pKVStyle, pVecErrors ); |
|
|
|
pVisData->m_Styles.AddToTail( pStyleInfo ); |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Parse one style from the styles block. |
|
//----------------------------------------------------------------------------- |
|
void CEconStyleInfo::BInitFromKV( KeyValues *pKVStyle, CUtlVector<CUtlString> *pVecErrors ) |
|
{ |
|
enum { kInvalidSkinKey = -1, }; |
|
|
|
Assert( pKVStyle ); |
|
|
|
// A "skin" entry means "use this index for all of our teams, no matter how many we have". |
|
int iCommonSkin = pKVStyle->GetInt( "skin", kInvalidSkinKey ); |
|
if ( iCommonSkin != kInvalidSkinKey ) |
|
{ |
|
for ( int i = 0; i < TEAM_VISUAL_SECTIONS; i++ ) |
|
{ |
|
m_iSkins[i] = iCommonSkin; |
|
} |
|
} |
|
|
|
int iCommonViewmodelSkin = pKVStyle->GetInt( "v_skin", kInvalidSkinKey ); |
|
if ( iCommonViewmodelSkin != kInvalidSkinKey ) |
|
{ |
|
for ( int i=0; i<TEAM_VISUAL_SECTIONS; i++ ) |
|
{ |
|
m_iViewmodelSkins[i] = iCommonViewmodelSkin; |
|
} |
|
} |
|
|
|
// If we don't have a base entry, we look for a unique entry for each team. This will be |
|
// handled in a subclass if necessary. |
|
|
|
// Are we hiding additional bodygroups when this style is active? |
|
KeyValues *pKVHideBodygroups = pKVStyle->FindKey( "additional_hidden_bodygroups" ); |
|
if ( pKVHideBodygroups ) |
|
{ |
|
FOR_EACH_SUBKEY( pKVHideBodygroups, pKVBodygroup ) |
|
{ |
|
m_vecAdditionalHideBodygroups.AddToTail( pKVBodygroup->GetName() ); |
|
} |
|
} |
|
|
|
// Remaining common properties. |
|
m_pszName = pKVStyle->GetString( "name", "#TF_UnknownStyle" ); |
|
m_pszBasePlayerModel = pKVStyle->GetString( "model_player", NULL ); |
|
m_bIsSelectable = pKVStyle->GetBool( "selectable", true ); |
|
m_pszInventoryImage = pKVStyle->GetString( "image_inventory", NULL ); |
|
|
|
KeyValues *pKVBodygroup = pKVStyle->FindKey( "bodygroup" ); |
|
if ( pKVBodygroup ) |
|
{ |
|
m_pszBodygroupName = pKVBodygroup->GetString( "name", NULL ); |
|
Assert( m_pszBodygroupName ); |
|
m_iBodygroupSubmodelIndex = pKVBodygroup->GetInt( "submodel_index", -1 ); |
|
Assert( m_iBodygroupSubmodelIndex != -1 ); |
|
} |
|
} |
|
|
|
#if defined(CLIENT_DLL) || defined(GAME_DLL) |
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CEconStyleInfo::GeneratePrecacheModelStringsForStyle( CUtlVector<const char *> *out_pVecModelStrings ) const |
|
{ |
|
Assert( out_pVecModelStrings ); |
|
|
|
if ( GetBasePlayerDisplayModel() != NULL ) |
|
{ |
|
out_pVecModelStrings->AddToTail( GetBasePlayerDisplayModel() ); |
|
} |
|
} |
|
#endif |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Item definition initialization helpers. |
|
//----------------------------------------------------------------------------- |
|
static void RecursiveInheritKeyValues( KeyValues *out_pValues, KeyValues *pInstance ) |
|
{ |
|
KeyValues *pPrevSubKey = NULL; |
|
for ( KeyValues * pSubKey = pInstance->GetFirstSubKey(); pSubKey != NULL; pPrevSubKey = pSubKey, pSubKey = pSubKey->GetNextKey() ) |
|
{ |
|
// If this assert triggers, you have an item that uses a prefab but has multiple keys with the same name |
|
AssertMsg2 ( !pPrevSubKey || pPrevSubKey->GetNameSymbol() != pSubKey->GetNameSymbol(), |
|
"Item definition \"%s\" has multiple attributes of the same name (%s) can't use prefabs", pInstance->GetName(), pSubKey->GetName() ); |
|
|
|
KeyValues::types_t eType = pSubKey->GetDataType(); |
|
switch ( eType ) |
|
{ |
|
case KeyValues::TYPE_STRING: out_pValues->SetString( pSubKey->GetName(), pSubKey->GetString() ); break; |
|
case KeyValues::TYPE_INT: out_pValues->SetInt( pSubKey->GetName(), pSubKey->GetInt() ); break; |
|
case KeyValues::TYPE_FLOAT: out_pValues->SetFloat( pSubKey->GetName(), pSubKey->GetFloat() ); break; |
|
case KeyValues::TYPE_WSTRING: out_pValues->SetWString( pSubKey->GetName(), pSubKey->GetWString() ); break; |
|
case KeyValues::TYPE_COLOR: out_pValues->SetColor( pSubKey->GetName(), pSubKey->GetColor() ) ; break; |
|
case KeyValues::TYPE_UINT64: out_pValues->SetUint64( pSubKey->GetName(), pSubKey->GetUint64() ) ; break; |
|
|
|
// "NONE" means "KeyValues" |
|
case KeyValues::TYPE_NONE: |
|
{ |
|
// We may already have this part of the tree to stuff data into/overwrite, or we |
|
// may have to make a new block. |
|
KeyValues *pNewChild = out_pValues->FindKey( pSubKey->GetName() ); |
|
if ( !pNewChild ) |
|
{ |
|
pNewChild = out_pValues->CreateNewKey(); |
|
pNewChild->SetName( pSubKey->GetName() ); |
|
} |
|
|
|
RecursiveInheritKeyValues( pNewChild, pSubKey ); |
|
break; |
|
} |
|
|
|
case KeyValues::TYPE_PTR: |
|
default: |
|
Assert( !"Unhandled data type for KeyValues inheritance!" ); |
|
break; |
|
} |
|
} |
|
} |
|
|
|
void MergeDefinitionPrefab( KeyValues *pKVWriteItem, KeyValues *pKVSourceItem ) |
|
{ |
|
Assert( pKVWriteItem ); |
|
Assert( pKVSourceItem ); |
|
|
|
const char *svPrefabName = pKVSourceItem->GetString( "prefab", NULL ); |
|
|
|
if ( svPrefabName ) |
|
{ |
|
CUtlStringList vecPrefabs; |
|
|
|
Q_SplitString( svPrefabName, " ", vecPrefabs ); |
|
|
|
// Iterate backwards so adjectives get applied over the noun prefab |
|
// e.g. wet scared cat would apply cat first, then scared and wet. |
|
FOR_EACH_VEC_BACK( vecPrefabs, i ) |
|
{ |
|
KeyValues *pKVPrefab = GetItemSchema()->FindDefinitionPrefabByName( vecPrefabs[i] ); |
|
AssertMsg1( pKVPrefab, "Unable to find prefab \"%s\".", vecPrefabs[i] ); |
|
|
|
if ( pKVPrefab ) |
|
{ |
|
MergeDefinitionPrefab( pKVWriteItem, pKVPrefab ); |
|
} |
|
} |
|
} |
|
|
|
RecursiveInheritKeyValues( pKVWriteItem, pKVSourceItem ); |
|
} |
|
|
|
KeyValues *CEconItemSchema::FindDefinitionPrefabByName( const char *pszPrefabName ) const |
|
{ |
|
int iIndex = m_mapDefinitionPrefabs.Find( pszPrefabName ); |
|
if ( m_mapDefinitionPrefabs.IsValidIndex( iIndex ) ) |
|
return m_mapDefinitionPrefabs[iIndex]; |
|
|
|
return NULL; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
const char *CEconItemSchema::FindStringTableEntry( const char *pszTableName, int iIndex ) const |
|
{ |
|
SchemaStringTableDict_t::IndexType_t i = m_dictStringTable.Find( pszTableName ); |
|
if ( !m_dictStringTable.IsValidIndex( i ) ) |
|
return NULL; |
|
|
|
const CUtlVector< schema_string_table_entry_t >& vec = *m_dictStringTable[i]; |
|
FOR_EACH_VEC( vec, j ) |
|
{ |
|
if ( vec[j].m_iIndex == iIndex ) |
|
return vec[j].m_pszStr; |
|
} |
|
|
|
return NULL; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Initialize the item definition |
|
// Input: pKVItem - The KeyValues representation of the item |
|
// schema - The overall item schema for this item |
|
// pVecErrors - An optional vector that will contain error messages if |
|
// the init fails. |
|
// Output: True if initialization succeeded, false otherwise |
|
//----------------------------------------------------------------------------- |
|
#ifdef GC_DLL |
|
GCConVar gc_steam_payment_rules_kv_key( "gc_steam_payment_rules_kv_key", "payment_rules" ); |
|
#endif // GC_DLL |
|
|
|
|
|
#if defined( WITH_STREAMABLE_WEAPONS ) |
|
#if defined( CLIENT_DLL ) |
|
ConVar tf_loadondemand_default("cl_loadondemand_default", "1", FCVAR_ARCHIVE | FCVAR_CLIENTDLL, "The default value for whether items should be delay loaded (1) or loaded now (0)."); |
|
#elif defined( GAME_DLL ) |
|
// The server doesn't load on demand by default because it can crash sometimes when this is set. We need to run that down, but in the meantime |
|
// we just have it load on demand. |
|
ConVar tf_loadondemand_default("sv_loadondemand_default", "0", FCVAR_ARCHIVE | FCVAR_GAMEDLL, "The default value for whether items should be delay loaded (1) or loaded now (0)."); |
|
#elif defined( GC_DLL ) |
|
GCConVar tf_loadondemand_default("gc_loadondemand_default", "1", FCVAR_ARCHIVE, "The default value for whether items should be delay loaded (1) or loaded now (0)."); |
|
#else |
|
#error "Need to add support for streamable weapons to this configuration, or disable streamable weapons here." |
|
#endif |
|
#endif // WITH_STREAMABLE_WEAPONS |
|
|
|
bool CEconItemDefinition::BInitFromKV( KeyValues *pKVItem, CUtlVector<CUtlString> *pVecErrors /* = NULL */ ) |
|
{ |
|
// Set standard members |
|
m_pKVItem = new KeyValues( pKVItem->GetName() ); |
|
MergeDefinitionPrefab( m_pKVItem, pKVItem ); |
|
m_bEnabled = m_pKVItem->GetBool( "enabled" ); |
|
|
|
// initializing this one first so that it will be available for all the errors below |
|
m_pszDefinitionName = m_pKVItem->GetString( "name", NULL ); |
|
|
|
#if defined( WITH_STREAMABLE_WEAPONS ) |
|
bool bGotDefault = false; |
|
m_bLoadOnDemand = m_pKVItem->GetBool( "loadondemand", tf_loadondemand_default.GetBool(), &bGotDefault ); |
|
|
|
// This logging is useful for tracking down bugs that crop up because we've (possibly) swapped the default value for loadondemand. |
|
// But it can be removed once we're satisfied there aren't any bugs as a result of the change (when we cleanup WITH_STREAMABLE_WEAPONS). |
|
if (bGotDefault) |
|
{ |
|
DevMsg(10, "Item %s received default value for loadondemand\n", m_pszDefinitionName); |
|
} |
|
#else |
|
// Keep the old behavior, which is that loadondemand is defaulted to false. |
|
m_bLoadOnDemand = m_pKVItem->GetBool("loadondemand"); |
|
#endif |
|
|
|
m_nDefIndex = Q_atoi( m_pKVItem->GetName() ); |
|
m_unMinItemLevel = (uint32)m_pKVItem->GetInt( "min_ilevel", GetItemSchema()->GetMinLevel() ); |
|
m_unMaxItemLevel = (uint32)m_pKVItem->GetInt( "max_ilevel", GetItemSchema()->GetMaxLevel() ); |
|
m_nDefaultDropQuantity = m_pKVItem->GetInt( "default_drop_quantity", 1 ); |
|
|
|
m_nPopularitySeed = m_pKVItem->GetInt( "popularity_seed", 0 ); |
|
|
|
|
|
#if defined(CLIENT_DLL) || defined(GAME_DLL) |
|
// We read this manually here in the game dlls. The GC reads it below while checking the global schema. |
|
GetItemSchema()->BGetItemQualityFromName( m_pKVItem->GetString( "item_quality" ), &m_nItemQuality ); |
|
GetItemSchema()->BGetItemQualityFromName( m_pKVItem->GetString( "forced_item_quality" ), &m_nForcedItemQuality ); |
|
#endif |
|
|
|
// Check for required fields |
|
SCHEMA_INIT_CHECK( |
|
NULL != m_pKVItem->FindKey( "name" ), |
|
"Item definition %s: Missing required field \"name\"", m_pKVItem->GetName() ); |
|
|
|
SCHEMA_INIT_CHECK( |
|
NULL != m_pKVItem->FindKey( "item_class" ), |
|
"Item definition %s: Missing required field \"item_class\"", m_pKVItem->GetName() ); |
|
|
|
// Check value ranges |
|
SCHEMA_INIT_CHECK( |
|
m_pKVItem->GetInt( "min_ilevel" ) >= 0, |
|
"Item definition %s: \"min_ilevel\" must be greater than or equal to 0", GetDefinitionName() ); |
|
|
|
SCHEMA_INIT_CHECK( |
|
m_pKVItem->GetInt( "max_ilevel" ) >= 0, |
|
"Item definition %s: \"max_ilevel\" must be greater than or equal to 0", GetDefinitionName() ); |
|
|
|
// Check for consistency |
|
#ifdef GC_DLL |
|
// We don't do these consistency checks in the game, because it doesn't have the data to do them |
|
SCHEMA_INIT_CHECK( |
|
m_unMinItemLevel >= GetItemSchema()->GetMinLevel(), |
|
"Item definition %s: min_ilevel (%d) must be greater or equal to Minimum Item Level (%d)", GetDefinitionName(), m_unMinItemLevel, GetItemSchema()->GetMinLevel() ); |
|
|
|
SCHEMA_INIT_CHECK( |
|
m_unMinItemLevel <= m_unMaxItemLevel, |
|
"Item definition %s: min_ilevel (%d) must be greater or equal to min_ilevel (%d)", GetDefinitionName(), m_unMaxItemLevel, m_unMinItemLevel ); |
|
|
|
SCHEMA_INIT_CHECK( |
|
m_unMaxItemLevel <= GetItemSchema()->GetMaxLevel(), |
|
"Item definition %s: max_ilevel (%d) must be less than or equal to Maximum Item Level (%d)", GetDefinitionName(), m_unMaxItemLevel, GetItemSchema()->GetMaxLevel() ); |
|
|
|
// Read the item quality |
|
if ( m_pKVItem->FindKey( "item_quality" ) ) |
|
{ |
|
SCHEMA_INIT_CHECK( |
|
GetItemSchema()->BGetItemQualityFromName( m_pKVItem->GetString( "item_quality" ), &m_nItemQuality ), |
|
"Item definition %s: Undefined item_quality \"%s\"", GetDefinitionName(), m_pKVItem->GetString( "item_quality" ) ); |
|
} |
|
if ( m_pKVItem->FindKey( "forced_item_quality" ) ) |
|
{ |
|
SCHEMA_INIT_CHECK( |
|
GetItemSchema()->BGetItemQualityFromName( m_pKVItem->GetString( "forced_item_quality" ), &m_nForcedItemQuality ), |
|
"Item definition %s: Undefined item_quality \"%s\"", GetDefinitionName(), m_pKVItem->GetString( "forced_item_quality" ) ); |
|
} |
|
#endif |
|
|
|
// Rarity |
|
// Get Index from this string and save the index |
|
if ( m_pKVItem->FindKey( "item_rarity" ) ) |
|
{ |
|
SCHEMA_INIT_CHECK( |
|
GetItemSchema()->BGetItemRarityFromName( m_pKVItem->GetString( "item_rarity" ), &m_nItemRarity ), |
|
"Item definition %s: Undefined item_rarity \"%s\"", GetDefinitionName(), m_pKVItem->GetString( "item_rarity" ) ); |
|
} |
|
|
|
if ( m_pKVItem->FindKey( "item_series" ) ) |
|
{ |
|
// Make sure this is a valid series |
|
SCHEMA_INIT_CHECK( |
|
GetItemSchema()->BGetItemSeries( m_pKVItem->GetString( "item_series" ), &m_unItemSeries ), |
|
"Item definition %s: Undefined item_series \"%s\"", GetDefinitionName(), m_pKVItem->GetString( "item_series" ) ); |
|
} |
|
|
|
// Get the item class |
|
m_pszItemClassname = m_pKVItem->GetString( "item_class", NULL ); |
|
|
|
m_pszClassToken = m_pKVItem->GetString( "class_token_id", NULL ); |
|
m_pszSlotToken = m_pKVItem->GetString( "slot_token_id", NULL ); |
|
|
|
// expiration data |
|
const char *pchExpiration = m_pKVItem->GetString( "expiration_date", NULL ); |
|
if( pchExpiration && pchExpiration[0] ) |
|
{ |
|
if ( pchExpiration[0] == '!' ) |
|
{ |
|
m_rtExpiration = GetItemSchema()->GetCustomExpirationDate( &pchExpiration[1] ); |
|
SCHEMA_INIT_CHECK( |
|
m_rtExpiration != k_RTime32Nil, |
|
"Unknown/malformed expiration_date string \"%s\" in item %s.", pchExpiration, m_pszDefinitionName ); |
|
} |
|
else |
|
{ |
|
m_rtExpiration = CRTime::RTime32FromFmtString( "YYYY-MM-DD hh:mm:ss" , pchExpiration ); |
|
|
|
#ifdef GC_DLL |
|
// Check that if we convert back to a string, we get the same value |
|
char rtimeBuf[k_RTimeRenderBufferSize]; |
|
SCHEMA_INIT_CHECK( |
|
Q_strcmp( CRTime::RTime32ToString( m_rtExpiration, rtimeBuf ), pchExpiration ) == 0, |
|
"Malformed expiration_date \"%s\" for expiration_date in item %s. Must be of the form \"YYYY-MM-DD hh:mm:ss\". Input: %s Output: %s InputTime: %u LocalTime: %u Timezone: %lu", pchExpiration, m_pszDefinitionName, pchExpiration, rtimeBuf, m_rtExpiration, CRTime::RTime32TimeCur(), timezone ); |
|
#else |
|
// Check that if we convert back to a string, we get the same value. Emit an error, but don't fail in the game code |
|
char rtimeBuf[k_RTimeRenderBufferSize]; |
|
if ( Q_strcmp( CRTime::RTime32ToString( m_rtExpiration, rtimeBuf ), pchExpiration ) != 0 ) |
|
{ |
|
#if ( defined( _MSC_VER ) && _MSC_VER >= 1900 ) |
|
#define timezone _timezone |
|
#define daylight _daylight |
|
#endif |
|
Assert( false ); |
|
Warning( "Malformed expiration_date \"%s\" for expiration_date in item %s. Must be of the form \"YYYY-MM-DD hh:mm:ss\". Input: %s Output: %s InputTime: %u LocalTime: %u Timezone: %lu\n", pchExpiration, m_pszDefinitionName, pchExpiration, rtimeBuf, m_rtExpiration, CRTime::RTime32TimeCur(), timezone ); |
|
} |
|
#endif |
|
} |
|
} |
|
|
|
// Display data |
|
m_pszItemBaseName = m_pKVItem->GetString( "item_name", "" ); // non-NULL to ensure we can sort |
|
m_pszItemTypeName = m_pKVItem->GetString( "item_type_name", "" ); // non-NULL to ensure we can sort |
|
m_pszItemDesc = m_pKVItem->GetString( "item_description", NULL ); |
|
m_pszArmoryDesc = m_pKVItem->GetString( "armory_desc", NULL ); |
|
m_pszInventoryModel = m_pKVItem->GetString( "model_inventory", NULL ); |
|
m_pszInventoryImage = m_pKVItem->GetString( "image_inventory", NULL ); |
|
|
|
const char* pOverlay = m_pKVItem->GetString( "image_inventory_overlay", NULL ); |
|
if ( pOverlay ) |
|
{ |
|
m_pszInventoryOverlayImages.AddToTail( pOverlay ); |
|
} |
|
pOverlay = m_pKVItem->GetString( "image_inventory_overlay2", NULL ); |
|
if ( pOverlay ) |
|
{ |
|
m_pszInventoryOverlayImages.AddToTail( pOverlay ); |
|
} |
|
|
|
m_iInventoryImagePosition[0] = atoi( m_pKVItem->GetString( "image_inventory_pos_x", "0" ) ); |
|
m_iInventoryImagePosition[1] = atoi( m_pKVItem->GetString( "image_inventory_pos_y", "0" ) ); |
|
m_iInventoryImageSize[0] = atoi( m_pKVItem->GetString( "image_inventory_size_w", "128" ) ); |
|
m_iInventoryImageSize[1] = atoi( m_pKVItem->GetString( "image_inventory_size_h", "82" ) ); |
|
m_iInspectPanelDistance = m_pKVItem->GetInt( "inspect_panel_dist", 70 ); |
|
m_pszHolidayRestriction = m_pKVItem->GetString( "holiday_restriction", NULL ); |
|
m_nVisionFilterFlags = m_pKVItem->GetInt( "vision_filter_flags", 0 ); |
|
m_iSubType = atoi( m_pKVItem->GetString( "subtype", "0" ) ); |
|
m_pszBaseDisplayModel = m_pKVItem->GetString( "model_player", NULL ); |
|
m_iDefaultSkin = m_pKVItem->GetInt( "default_skin", -1 ); |
|
m_pszWorldDisplayModel = m_pKVItem->GetString( "model_world", NULL ); // Not the ideal method. c_models are better, but this is to solve a retrofit problem with the sticky launcher. |
|
m_pszWorldExtraWearableModel = m_pKVItem->GetString( "extra_wearable", NULL ); |
|
m_pszWorldExtraWearableViewModel = m_pKVItem->GetString( "extra_wearable_vm", NULL ); |
|
m_pszVisionFilteredDisplayModel = pKVItem->GetString( "model_vision_filtered", NULL ); |
|
m_pszBrassModelOverride = m_pKVItem->GetString( "brass_eject_model", NULL ); |
|
m_bHideBodyGroupsDeployedOnly = m_pKVItem->GetBool( "hide_bodygroups_deployed_only" ); |
|
m_bAttachToHands = m_pKVItem->GetInt( "attach_to_hands", 0 ) != 0; |
|
m_bAttachToHandsVMOnly = m_pKVItem->GetInt( "attach_to_hands_vm_only", 0 ) != 0; |
|
m_bProperName = m_pKVItem->GetInt( "propername", 0 ) != 0; |
|
m_bFlipViewModel = m_pKVItem->GetInt( "flip_viewmodel", 0 ) != 0; |
|
m_bActAsWearable = m_pKVItem->GetInt( "act_as_wearable", 0 ) != 0; |
|
m_bActAsWeapon = m_pKVItem->GetInt( "act_as_weapon", 0 ) != 0; |
|
m_bIsTool = m_pKVItem->GetBool( "is_tool", 0 ) || ( GetItemClass() && !V_stricmp( GetItemClass(), "tool" ) ); |
|
m_iDropType = StringFieldToInt( m_pKVItem->GetString("drop_type"), g_szDropTypeStrings, ARRAYSIZE(g_szDropTypeStrings) ); |
|
m_pszCollectionReference = m_pKVItem->GetString( "collection_reference", NULL ); |
|
|
|
// Creation data |
|
m_bHidden = m_pKVItem->GetInt( "hidden", 0 ) != 0; |
|
m_bShouldShowInArmory = m_pKVItem->GetInt( "show_in_armory", 0 ) != 0; |
|
m_bBaseItem = m_pKVItem->GetInt( "baseitem", 0 ) != 0; |
|
m_pszItemLogClassname = m_pKVItem->GetString( "item_logname", NULL ); |
|
m_pszItemIconClassname = m_pKVItem->GetString( "item_iconname", NULL ); |
|
m_pszDatabaseAuditTable = m_pKVItem->GetString( "database_audit_table", NULL ); |
|
m_bImported = m_pKVItem->FindKey( "import_from" ) != NULL; |
|
|
|
// Tool data |
|
m_pTool = NULL; |
|
KeyValues *pToolDataKV = m_pKVItem->FindKey( "tool" ); |
|
if ( pToolDataKV ) |
|
{ |
|
const char *pszType = pToolDataKV->GetString( "type", NULL ); |
|
SCHEMA_INIT_CHECK( pszType != NULL, "Tool '%s' missing required type.", m_pKVItem->GetName() ); |
|
|
|
// Common-to-all-tools settings. |
|
const char *pszUseString = pToolDataKV->GetString( "use_string", NULL ); |
|
const char *pszUsageRestriction = pToolDataKV->GetString( "restriction", NULL ); |
|
KeyValues *pToolUsageKV = pToolDataKV->FindKey( "usage" ); |
|
|
|
// Common-to-all-tools usage capability flags. |
|
item_capabilities_t usageCapabilities = (item_capabilities_t)ITEM_CAP_TOOL_DEFAULT; |
|
KeyValues *pToolUsageCapsKV = pToolDataKV->FindKey( "usage_capabilities" ); |
|
if ( pToolUsageCapsKV ) |
|
{ |
|
KeyValues *pEntry = pToolUsageCapsKV->GetFirstSubKey(); |
|
while ( pEntry ) |
|
{ |
|
ParseCapability( usageCapabilities, pEntry ); |
|
pEntry = pEntry->GetNextKey(); |
|
} |
|
} |
|
|
|
m_pTool = GetItemSchema()->CreateEconToolImpl( pszType, pszUseString, pszUsageRestriction, usageCapabilities, pToolUsageKV ); |
|
SCHEMA_INIT_CHECK( m_pTool != NULL, "Unable to create tool implementation for '%s', of type '%s'.", m_pKVItem->GetName(), pszType ); |
|
} |
|
|
|
// Bundle |
|
KeyValues *pBundleDataKV = m_pKVItem->FindKey( "bundle" ); |
|
if ( pBundleDataKV ) |
|
{ |
|
m_BundleInfo = new bundleinfo_t(); |
|
FOR_EACH_SUBKEY( pBundleDataKV, pKVCurItem ) |
|
{ |
|
CEconItemDefinition *pItemDef = GetItemSchema()->GetItemDefinitionByName( pKVCurItem->GetName() ); |
|
SCHEMA_INIT_CHECK( pItemDef != NULL, "Unable to find item definition '%s' for bundle '%s'.", pKVCurItem->GetName(), m_pszDefinitionName ); |
|
|
|
m_BundleInfo->vecItemDefs.AddToTail( pItemDef ); |
|
} |
|
|
|
// Only check for pack bundle if the item is actually a bundle - note that we could do this programatically by checking that all items in the bundle are flagged as a "pack item" - but for now the bundle needs to be explicitly flagged as a pack bundle. |
|
m_bIsPackBundle = m_pKVItem->GetInt( "is_pack_bundle", 0 ) != 0; |
|
} |
|
|
|
// capabilities |
|
m_iCapabilities = (item_capabilities_t)ITEM_CAP_DEFAULT; |
|
KeyValues *pCapsKV = m_pKVItem->FindKey( "capabilities" ); |
|
if ( pCapsKV ) |
|
{ |
|
KeyValues *pEntry = pCapsKV->GetFirstSubKey(); |
|
while ( pEntry ) |
|
{ |
|
ParseCapability( m_iCapabilities, pEntry ); |
|
pEntry = pEntry->GetNextKey(); |
|
} |
|
} |
|
|
|
// item_set |
|
SCHEMA_INIT_CHECK( (!m_pKVItem->GetString( "item_set", NULL )), "Item definition '%s' specifies deprecated \"item_set\" field. Items sets are now specified only in the set itself, not on the definition.", GetDefinitionName() ); |
|
|
|
const char *pszSetItemRemapDefIndexName = m_pKVItem->GetString( "set_item_remap", NULL ); |
|
if ( pszSetItemRemapDefIndexName ) |
|
{ |
|
const CEconItemDefinition *pRemapItemDef = GetItemSchema()->GetItemDefinitionByName( pszSetItemRemapDefIndexName ); |
|
m_unSetItemRemapDefIndex = pRemapItemDef ? pRemapItemDef->GetDefinitionIndex() : INVALID_ITEM_DEF_INDEX; |
|
|
|
SCHEMA_INIT_CHECK( m_unSetItemRemapDefIndex != INVALID_ITEM_DEF_INDEX, "Unable to find set item remap definition '%s' for '%s'.", pszSetItemRemapDefIndexName, GetDefinitionName() ); |
|
SCHEMA_INIT_CHECK( m_unSetItemRemapDefIndex != GetDefinitionIndex(), "Unable to set set item remap for definition '%s' to itself.", GetDefinitionName() ); |
|
} |
|
else |
|
{ |
|
m_unSetItemRemapDefIndex = GetDefinitionIndex(); |
|
} |
|
|
|
// cache item map names |
|
m_pszArmoryRemap = m_pKVItem->GetString( "armory_remap", NULL ); |
|
m_pszStoreRemap = m_pKVItem->GetString( "store_remap", NULL ); |
|
|
|
m_pszXifierRemapClass = m_pKVItem->GetString( "xifier_class_remap", NULL ); |
|
m_pszBaseFunctionalItemName = m_pKVItem->GetString( "base_item_name", "" ); |
|
m_pszParticleSuffix = m_pKVItem->GetString( "particle_suffix", NULL ); |
|
|
|
m_bValidForShuffle = m_pKVItem->GetBool( "valid_for_shuffle", false ); |
|
m_bValidForSelfMade = m_pKVItem->GetBool( "valid_for_self_made", true ); |
|
|
|
// Init our visuals blocks. |
|
BInitVisualBlockFromKV( m_pKVItem, pVecErrors ); |
|
|
|
// Calculate our equip region mask. |
|
{ |
|
m_unEquipRegionMask = 0; |
|
m_unEquipRegionConflictMask = 0; |
|
|
|
// Our equip region will come from one of two places -- either we have an "equip_regions" (plural) section, |
|
// in which case we have any number of regions specified; or we have an "equip_region" (singular) section |
|
// which will have one and exactly one region. If we have "equip_regions" (plural), we ignore whatever is |
|
// in "equip_region" (singular). |
|
// |
|
// Yes, this is sort of dumb. |
|
CUtlVector<const char *> vecEquipRegionNames; |
|
|
|
KeyValues *pKVMultiEquipRegions = m_pKVItem->FindKey( "equip_regions" ), |
|
*pKVSingleEquipRegion = m_pKVItem->FindKey( "equip_region" ); |
|
|
|
// Maybe we have multiple entries? |
|
if ( pKVMultiEquipRegions ) |
|
{ |
|
for ( KeyValues *pKVRegion = pKVMultiEquipRegions->GetFirstSubKey(); pKVRegion; pKVRegion = pKVRegion->GetNextKey() ) |
|
{ |
|
vecEquipRegionNames.AddToTail( pKVRegion->GetName() ); |
|
} |
|
} |
|
// This is our one-and-only-one equip region. |
|
else if ( pKVSingleEquipRegion ) |
|
{ |
|
const char *pEquipRegionName = pKVSingleEquipRegion->GetString( (const char *)NULL, NULL ); |
|
if ( pEquipRegionName ) |
|
{ |
|
vecEquipRegionNames.AddToTail( pEquipRegionName ); |
|
} |
|
} |
|
|
|
// For each of our regions, add to our conflict mask both ourself and all the regions |
|
// that we conflict with. |
|
FOR_EACH_VEC( vecEquipRegionNames, i ) |
|
{ |
|
const char *pszEquipRegionName = vecEquipRegionNames[i]; |
|
equip_region_mask_t unThisRegionMask = GetItemSchema()->GetEquipRegionMaskByName( pszEquipRegionName ); |
|
|
|
SCHEMA_INIT_CHECK( |
|
unThisRegionMask != 0, |
|
"Item definition %s: Unable to find equip region mask for region named \"%s\"", GetDefinitionName(), vecEquipRegionNames[i] ); |
|
|
|
m_unEquipRegionMask |= GetItemSchema()->GetEquipRegionBitMaskByName( pszEquipRegionName ); |
|
m_unEquipRegionConflictMask |= unThisRegionMask; |
|
} |
|
} |
|
|
|
// Single-line static attribute parsing. |
|
{ |
|
KeyValues *pKVStaticAttrsKey = m_pKVItem->FindKey( "static_attrs" ); |
|
if ( pKVStaticAttrsKey ) |
|
{ |
|
FOR_EACH_SUBKEY( pKVStaticAttrsKey, pKVKey ) |
|
{ |
|
static_attrib_t staticAttrib; |
|
|
|
SCHEMA_INIT_SUBSTEP( staticAttrib.BInitFromKV_SingleLine( GetDefinitionName(), pKVKey, pVecErrors, false ) ); |
|
m_vecStaticAttributes.AddToTail( staticAttrib ); |
|
|
|
// Does this attribute specify a tag to apply to this item definition? |
|
Assert( staticAttrib.GetAttributeDefinition() ); |
|
} |
|
} |
|
} |
|
|
|
// Old style attribute parsing. Really only useful now for GC-generated attributes. |
|
KeyValues *pKVAttribKey = m_pKVItem->FindKey( "attributes" ); |
|
if ( pKVAttribKey ) |
|
{ |
|
FOR_EACH_SUBKEY( pKVAttribKey, pKVKey ) |
|
{ |
|
static_attrib_t staticAttrib; |
|
|
|
SCHEMA_INIT_SUBSTEP( staticAttrib.BInitFromKV_MultiLine( GetDefinitionName(), pKVKey, pVecErrors ) ); |
|
m_vecStaticAttributes.AddToTail( staticAttrib ); |
|
|
|
// Does this attribute specify a tag to apply to this item definition? |
|
Assert( staticAttrib.GetAttributeDefinition() ); |
|
} |
|
} |
|
|
|
// Initialize tags based on all static attributes for this item. |
|
for ( const static_attrib_t& attr : m_vecStaticAttributes ) |
|
{ |
|
const econ_tag_handle_t tag = attr.GetAttributeDefinition()->GetItemDefinitionTag(); |
|
if ( tag != INVALID_ECON_TAG_HANDLE ) |
|
{ |
|
m_vecTags.AddToTail( tag ); |
|
} |
|
} |
|
|
|
// Auto-generate tags based on capabilities. |
|
for ( int i = 0; i < NUM_ITEM_CAPS; i++ ) |
|
{ |
|
if ( m_iCapabilities & (1 << i) ) |
|
{ |
|
m_vecTags.AddToTail( GetItemSchema()->GetHandleForTag( CFmtStr( "auto__cap_%s", g_Capabilities[i] ).Get() ) ); |
|
} |
|
} |
|
|
|
// Initialize used-specified tags for this item if present. |
|
KeyValues *pKVTags = m_pKVItem->FindKey( "tags" ); |
|
if ( pKVTags ) |
|
{ |
|
FOR_EACH_SUBKEY( pKVTags, pKVTag ) |
|
{ |
|
m_vecTags.AddToTail( GetItemSchema()->GetHandleForTag( pKVTag->GetName() ) ); |
|
} |
|
} |
|
|
|
#ifdef GC_DLL |
|
SCHEMA_INIT_SUBSTEP( BCommonInitPropertyGeneratorsFromKV( GetDefinitionName(), &m_vecPropertyGenerators, m_pKVItem->FindKey( "property_generators" ), pVecErrors ) ); |
|
|
|
// Parse payment rules on the GC if any exist. |
|
KeyValues *pKVPaymentRules = m_pKVItem->FindKey( gc_steam_payment_rules_kv_key.GetString() ); |
|
if ( pKVPaymentRules ) |
|
{ |
|
FOR_EACH_TRUE_SUBKEY( pKVPaymentRules, pKVRule ) |
|
{ |
|
econ_item_payment_rule_t rule; |
|
|
|
const bool bFoundPaymentRule = BGetPaymentRule( pKVRule, &rule.m_eRuleType, &rule.m_RevenueShare ); |
|
SCHEMA_INIT_CHECK( bFoundPaymentRule, "Item definition '%s': payment rule %s didn't specify a known payment rule type", GetDefinitionName(), pKVRule->GetName() ); |
|
|
|
// Allow us to override some of our checks if we want to claim we really know what we're doing. |
|
if ( !pKVRule->FindKey( "sanity_check_override" ) ) |
|
{ |
|
SCHEMA_INIT_CHECK( rule.m_RevenueShare > 0.0, "Item definition '%s': payment rule %s has invalid revenue share %0.2f", GetDefinitionName(), pKVRule->GetName(), rule.m_RevenueShare ); |
|
|
|
// Ordinarily, bundles can only specify the "bundle_revenue_share" payment rule type. However, for backwards compatability |
|
// with previous payment rules that pre-date the Workshop and had manual percentages set offline, we allow people who know |
|
// what they're doing to specify the "sanity_check_override" key and then use custom rules. |
|
SCHEMA_INIT_CHECK( (rule.m_eRuleType == kPaymentRule_Bundle) == IsBundle(), "Item definition '%s': payment rule %s has invalid bundle rules", GetDefinitionName(), pKVRule->GetName() ); |
|
} |
|
|
|
KeyValues *pPaymentRuleForItemdef = pKVRule->FindKey( "payment_rule_for_itemdef" ); |
|
SCHEMA_INIT_CHECK( pPaymentRuleForItemdef && ( pPaymentRuleForItemdef->GetInt() == m_nDefIndex ), "Item definition '%s': payment rule %s has invalid payment_rule_for_itemdef", GetDefinitionName(), pKVRule->GetName() ); |
|
|
|
KeyValues *pKVMultiTargets = pKVRule->FindKey( "targets" ); |
|
KeyValues *pKVSingleTarget = pKVRule->FindKey( "target" ); |
|
SCHEMA_INIT_CHECK( pKVMultiTargets == NULL || pKVSingleTarget == NULL, "Item definition '%s': payment rule %s specifies both single- and multi-targets", GetDefinitionName(), pKVRule->GetName() ); |
|
|
|
if ( pKVMultiTargets ) |
|
{ |
|
FOR_EACH_SUBKEY( pKVMultiTargets, pKVTarget ) |
|
{ |
|
rule.m_vecValues.AddToTail( (uint64)Q_atoi64( pKVTarget->GetName() ) ); |
|
} |
|
} |
|
|
|
if ( pKVSingleTarget ) |
|
{ |
|
rule.m_vecValues.AddToTail( pKVSingleTarget->GetUint64() ); |
|
} |
|
|
|
// We expect bundles to have no associated account data at all -- their payment processing |
|
// is done by splitting the total value of the bundle between each of the items in it. All |
|
// non-bundle payment rules require at least one data entry. |
|
SCHEMA_INIT_CHECK( (rule.m_eRuleType == kPaymentRule_Bundle) == (rule.m_vecValues.Count() == 0), "Item definition '%s': payment rule %s has invalid number of target entries %i", GetDefinitionName(), pKVRule->GetName(), rule.m_vecValues.Count() ); |
|
|
|
// It doesn't make any sense to have multiple entries for a bundle. We expect their to be one |
|
// rule, and that's "process this like a bundle". |
|
if ( rule.m_eRuleType == kPaymentRule_Bundle ) |
|
{ |
|
SCHEMA_INIT_CHECK( m_vecPaymentRules.Count() == 0, "Item definition '%s': only the first payment rule can be specified as 'bundle'", GetDefinitionName() ); |
|
|
|
// We only allow bundle payment rules to be applied to actual bundles with contents |
|
// specified. Without doing this, we would have nowhere to pull the metadata about |
|
// which items are contained. |
|
SCHEMA_INIT_CHECK( GetBundleInfo() && GetBundleInfo()->vecItemDefs.Count() > 0, "Item definition '%s': payment rule %s is specified as a bundle but outer item definition has no bundle contents.\n", GetDefinitionName(), pKVRule->GetName() ); |
|
|
|
// Bundles rely on sub-items for figuring out payment so the revenue share for the |
|
// bundle itself is expected to be 100%. |
|
SCHEMA_INIT_CHECK( rule.m_RevenueShare == 100.0, "Item definition '%s': payment rule %s has invalid bundle revenue share %0.2f", GetDefinitionName(), pKVRule->GetName(), rule.m_RevenueShare ); |
|
} |
|
|
|
const bool bWasNumberedCorrectly = (AddPaymentRule( rule ) == atoi( pKVRule->GetName() )); |
|
SCHEMA_INIT_CHECK( bWasNumberedCorrectly, "Item definition '%s': misnumbered payment rule %s", GetDefinitionName(), pKVRule->GetName() ); |
|
} |
|
} |
|
#endif // GC_DLL |
|
|
|
return SCHEMA_INIT_SUCCESS(); |
|
} |
|
|
|
#ifdef GC_DLL |
|
int CEconItemDefinition::AddPaymentRule( const econ_item_payment_rule_t& newRule ) |
|
{ |
|
return m_vecPaymentRules.AddToTail( newRule ); |
|
} |
|
#endif // GC_DLL |
|
|
|
bool static_attrib_t::BInitFromKV_MultiLine( const char *pszContext, KeyValues *pKVAttribute, CUtlVector<CUtlString> *pVecErrors ) |
|
{ |
|
const CEconItemAttributeDefinition *pAttrDef = GetItemSchema()->GetAttributeDefinitionByName( pKVAttribute->GetName() ); |
|
|
|
SCHEMA_INIT_CHECK( |
|
NULL != pAttrDef, |
|
"Context '%s': Attribute \"%s\" in \"attributes\" did not match any attribute definitions", pszContext, pKVAttribute->GetName() ); |
|
|
|
if ( pAttrDef ) |
|
{ |
|
iDefIndex = pAttrDef->GetDefinitionIndex(); |
|
|
|
const ISchemaAttributeType *pAttrType = pAttrDef->GetAttributeType(); |
|
Assert( pAttrType ); |
|
|
|
pAttrType->InitializeNewEconAttributeValue( &m_value ); |
|
|
|
const char *pszValue = pKVAttribute->GetString( "value", NULL ); |
|
const bool bSuccessfullyLoadedValue = pAttrType->BConvertStringToEconAttributeValue( pAttrDef, pszValue, &m_value, true ); |
|
|
|
SCHEMA_INIT_CHECK( |
|
bSuccessfullyLoadedValue, |
|
"Context '%s': Attribute \"%s\" could not parse value \"%s\"!", pszContext, pKVAttribute->GetName(), pszValue ? pszValue : "(null)" ); |
|
|
|
SCHEMA_INIT_CHECK( |
|
!pAttrDef->BIsSetBonusAttribute(), |
|
"Context '%s': Attribute \"%s\" is a set bonus attribute and not supported here", pszContext, pKVAttribute->GetName() ); |
|
#ifdef GC_DLL |
|
bForceGCToGenerate = pKVAttribute->GetBool( "force_gc_to_generate" ); |
|
|
|
KeyValues *pKVLogicData = pKVAttribute->FindKey( "custom_value_logic" ); |
|
if ( pKVLogicData ) |
|
{ |
|
m_pKVCustomData = pKVLogicData->MakeCopy(); |
|
} |
|
|
|
SCHEMA_INIT_CHECK( |
|
m_pKVCustomData == NULL || bForceGCToGenerate, |
|
"Context '%s': Attribute \"%s\" is set to have custom logic but is not GC-generated so that logic will never get used!", pszContext, pKVAttribute->GetName() ); |
|
|
|
SCHEMA_INIT_CHECK( |
|
m_pKVCustomData != NULL || pKVAttribute->FindKey( "value" ), |
|
"Context '%s': Attribute \"%s\" has no value set", pszContext, pKVAttribute->GetName() ); |
|
|
|
SCHEMA_INIT_CHECK( |
|
m_pKVCustomData == NULL || m_pKVCustomData->FindKey( "method" ) != NULL, |
|
"Context '%s': Attribute \"%s\" custom logic data is set, but custom logic method is not set!", pszContext, pKVAttribute->GetName() ); |
|
#endif // GC_DLL |
|
} |
|
|
|
return SCHEMA_INIT_SUCCESS(); |
|
} |
|
|
|
bool static_attrib_t::BInitFromKV_SingleLine( const char *pszContext, KeyValues *pKVAttribute, CUtlVector<CUtlString> *pVecErrors, bool bEnableTerribleBackwardsCompatibilitySchemaParsingCode /* = true */ ) |
|
{ |
|
const CEconItemAttributeDefinition *pAttrDef = GetItemSchema()->GetAttributeDefinitionByName( pKVAttribute->GetName() ); |
|
|
|
SCHEMA_INIT_CHECK( |
|
NULL != pAttrDef, |
|
"Context '%s': Attribute \"%s\" in \"attributes\" did not match any attribute definitions", pszContext, pKVAttribute->GetName() ); |
|
|
|
if ( pAttrDef ) |
|
{ |
|
iDefIndex = pAttrDef->GetDefinitionIndex(); |
|
|
|
const ISchemaAttributeType *pAttrType = pAttrDef->GetAttributeType(); |
|
Assert( pAttrType ); |
|
|
|
pAttrType->InitializeNewEconAttributeValue( &m_value ); |
|
|
|
const char *pszValue = pKVAttribute->GetString(); |
|
const bool bSuccessfullyLoadedValue = pAttrType->BConvertStringToEconAttributeValue( pAttrDef, pszValue, &m_value, bEnableTerribleBackwardsCompatibilitySchemaParsingCode ); |
|
|
|
SCHEMA_INIT_CHECK( |
|
bSuccessfullyLoadedValue, |
|
"Context '%s': Attribute \"%s\" could not parse value \"%s\"!", pszContext, pKVAttribute->GetName(), pszValue ? pszValue : "(null)" ); |
|
|
|
SCHEMA_INIT_CHECK( |
|
!pAttrDef->BIsSetBonusAttribute(), |
|
"Context '%s': Attribute \"%s\" is a set bonus attribute and not supported here", pszContext, pKVAttribute->GetName() ); |
|
|
|
#ifdef GC_DLL |
|
bForceGCToGenerate = false; |
|
m_pKVCustomData = NULL; |
|
#endif // GC_DLL |
|
} |
|
|
|
return SCHEMA_INIT_SUCCESS(); |
|
} |
|
|
|
|
|
|
|
bool CEconItemDefinition::BInitItemMappings( CUtlVector<CUtlString> *pVecErrors ) |
|
{ |
|
// Armory remapping |
|
if ( m_pszArmoryRemap && m_pszArmoryRemap[0] ) |
|
{ |
|
CEconItemDefinition *pDef = GetItemSchema()->GetItemDefinitionByName( m_pszArmoryRemap ); |
|
if ( pDef ) |
|
{ |
|
m_iArmoryRemap = pDef->GetDefinitionIndex(); |
|
} |
|
|
|
SCHEMA_INIT_CHECK( |
|
pDef != NULL, |
|
"Item %s: Armory remap definition \"%s\" was not found", m_pszItemBaseName, m_pszArmoryRemap ); |
|
} |
|
|
|
// Store remapping |
|
if ( m_pszStoreRemap && m_pszStoreRemap[0] ) |
|
{ |
|
CEconItemDefinition *pDef = GetItemSchema()->GetItemDefinitionByName( m_pszStoreRemap ); |
|
if ( pDef ) |
|
{ |
|
m_iStoreRemap = pDef->GetDefinitionIndex(); |
|
} |
|
|
|
SCHEMA_INIT_CHECK( |
|
pDef != NULL, |
|
"Item %s: Store remap definition \"%s\" was not found", m_pszItemBaseName, m_pszStoreRemap ); |
|
} |
|
|
|
return SCHEMA_INIT_SUCCESS(); |
|
} |
|
|
|
const char* CEconItemDefinition::GetIconURL( const char* pszKey ) const |
|
{ |
|
auto idx = m_pDictIcons->Find( pszKey ); |
|
if ( idx == m_pDictIcons->InvalidIndex() ) |
|
{ |
|
return NULL; |
|
} |
|
|
|
return (*m_pDictIcons)[ idx ]; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Generate and return a random level according to whatever leveling |
|
// curve this definition uses. |
|
//----------------------------------------------------------------------------- |
|
uint32 CEconItemDefinition::RollItemLevel( void ) const |
|
{ |
|
return RandomInt( GetMinLevel(), GetMaxLevel() ); |
|
} |
|
|
|
const char *CEconItemDefinition::GetFirstSaleDate() const |
|
{ |
|
return GetDefinitionString( "first_sale_date", "1960/00/00" ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CEconItemDefinition::IterateAttributes( IEconItemAttributeIterator *pIterator ) const |
|
{ |
|
FOR_EACH_VEC( GetStaticAttributes(), i ) |
|
{ |
|
const static_attrib_t& staticAttrib = GetStaticAttributes()[i]; |
|
|
|
#ifdef GC_DLL |
|
// we skip over static attributes that the GC will turn into dynamic attributes because otherwise we'll have |
|
// the appearance of iterating over them twice; for clients these attributes won't even make it into the |
|
// list |
|
if ( staticAttrib.bForceGCToGenerate ) |
|
continue; |
|
#endif // GC_DLL |
|
|
|
const CEconItemAttributeDefinition *pAttrDef = GetItemSchema()->GetAttributeDefinition( staticAttrib.iDefIndex ); |
|
if ( !pAttrDef ) |
|
continue; |
|
|
|
const ISchemaAttributeType *pAttrType = pAttrDef->GetAttributeType(); |
|
Assert( pAttrType ); |
|
|
|
if ( !pAttrType->OnIterateAttributeValue( pIterator, pAttrDef, staticAttrib.m_value ) ) |
|
return; |
|
} |
|
} |
|
|
|
#if defined(CLIENT_DLL) || defined(GAME_DLL) |
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
Activity CEconItemDefinition::GetActivityOverride( int iTeam, Activity baseAct ) const |
|
{ |
|
int iAnims = GetNumAnimations( iTeam ); |
|
for ( int i = 0; i < iAnims; i++ ) |
|
{ |
|
animation_on_wearable_t *pData = GetAnimationData( iTeam, i ); |
|
if ( !pData ) |
|
continue; |
|
if ( pData->iActivity == kActivityLookup_Unknown ) |
|
{ |
|
pData->iActivity = ActivityList_IndexForName( pData->pszActivity ); |
|
} |
|
|
|
if ( pData->iActivity == baseAct ) |
|
{ |
|
if ( pData->iReplacement == kActivityLookup_Unknown ) |
|
{ |
|
pData->iReplacement = ActivityList_IndexForName( pData->pszReplacement ); |
|
} |
|
|
|
if ( pData->iReplacement > 0 ) |
|
{ |
|
return (Activity) pData->iReplacement; |
|
} |
|
} |
|
} |
|
|
|
return baseAct; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
const char *CEconItemDefinition::GetActivityOverride( int iTeam, const char *pszActivity ) const |
|
{ |
|
int iAnims = GetNumAnimations( iTeam ); |
|
for ( int i = 0; i < iAnims; i++ ) |
|
{ |
|
animation_on_wearable_t *pData = GetAnimationData( iTeam, i ); |
|
if ( Q_stricmp( pszActivity, pData->pszActivity ) == 0 ) |
|
return pData->pszReplacement; |
|
} |
|
|
|
return NULL; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
const char *CEconItemDefinition::GetReplacementForActivityOverride( int iTeam, Activity baseAct ) const |
|
{ |
|
int iAnims = GetNumAnimations( iTeam ); |
|
for ( int i = 0; i < iAnims; i++ ) |
|
{ |
|
animation_on_wearable_t *pData = GetAnimationData( iTeam, i ); |
|
if ( pData->iActivity == kActivityLookup_Unknown ) |
|
{ |
|
pData->iActivity = ActivityList_IndexForName( pData->pszActivity ); |
|
} |
|
if ( pData && pData->iActivity == baseAct ) |
|
return pData->pszReplacement; |
|
} |
|
|
|
return NULL; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Returns true if the content for this item view should be streamed. If false, |
|
// it should be preloaded. |
|
//----------------------------------------------------------------------------- |
|
|
|
// DO NOT MERGE THIS CONSOLE VARIABLE TO REL WE SHOULD NOT SHIP THIS OH GOD |
|
#ifdef STAGING_ONLY |
|
ConVar item_enable_dynamic_loading( "item_enable_dynamic_loading", "1", FCVAR_REPLICATED, "Enable/disable dynamic streaming of econ content." ); |
|
#endif // STAGING_ONLY |
|
|
|
bool CEconItemDefinition::IsContentStreamable() const |
|
{ |
|
if ( !BLoadOnDemand() ) |
|
return false; |
|
|
|
#ifdef STAGING_ONLY |
|
return item_enable_dynamic_loading.GetBool(); |
|
#else |
|
return true; |
|
#endif |
|
} |
|
#endif // defined(CLIENT_DLL) || defined(GAME_DLL) |
|
|
|
RETURN_ATTRIBUTE_STRING_F( CEconItemDefinition::GetIconDisplayModel, "icon display model", m_pszWorldDisplayModel ); |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Constructor |
|
//----------------------------------------------------------------------------- |
|
CTimedItemRewardDefinition::CTimedItemRewardDefinition( void ) |
|
: m_unMinFreq( 0 ), |
|
m_unMaxFreq( UINT_MAX ), |
|
m_flChance( 0.0f ), |
|
m_pLootList( NULL ), |
|
m_iRequiredItemDef(INVALID_ITEM_DEF_INDEX) |
|
{ |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Copy constructor |
|
//----------------------------------------------------------------------------- |
|
CTimedItemRewardDefinition::CTimedItemRewardDefinition( const CTimedItemRewardDefinition &that ) |
|
{ |
|
(*this) = that; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Operator= |
|
//----------------------------------------------------------------------------- |
|
CTimedItemRewardDefinition &CTimedItemRewardDefinition::operator=( const CTimedItemRewardDefinition &rhs ) |
|
{ |
|
m_unMinFreq = rhs.m_unMinFreq; |
|
m_unMaxFreq = rhs.m_unMaxFreq; |
|
m_flChance = rhs.m_flChance; |
|
m_criteria = rhs.m_criteria; |
|
m_pLootList = rhs.m_pLootList; |
|
m_iRequiredItemDef = rhs.m_iRequiredItemDef; |
|
|
|
return *this; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Initialize the attribute definition |
|
// Input: pKVTimedReward - The KeyValues representation of the timed reward |
|
// schema - The overall item schema |
|
// pVecErrors - An optional vector that will contain error messages if |
|
// the init fails. |
|
// Output: True if initialization succeeded, false otherwise |
|
//----------------------------------------------------------------------------- |
|
bool CTimedItemRewardDefinition::BInitFromKV( KeyValues *pKVTimedReward, CUtlVector<CUtlString> *pVecErrors /* = NULL */ ) |
|
{ |
|
// Parse the basic values |
|
m_flChance = pKVTimedReward->GetFloat( "pctChance" ); |
|
m_unMinFreq = pKVTimedReward->GetInt( "value_min", 0 ); |
|
m_unMaxFreq = pKVTimedReward->GetInt( "value_max", UINT_MAX ); |
|
m_iRequiredItemDef = INVALID_ITEM_DEF_INDEX; |
|
|
|
const char *pszRequiredItem = pKVTimedReward->GetString( "required_item", NULL ); |
|
if ( pszRequiredItem ) |
|
{ |
|
// Find the ItemDef |
|
CEconItemDefinition *pDef = GetItemSchema()->GetItemDefinitionByName( pszRequiredItem ); |
|
SCHEMA_INIT_CHECK( pDef != NULL, "Invalid Item Def Required for a for TimedReward Definition"); |
|
m_iRequiredItemDef = pDef->GetDefinitionIndex(); |
|
} |
|
|
|
// Check required fields |
|
SCHEMA_INIT_CHECK( |
|
NULL != pKVTimedReward->FindKey( "value_min" ), |
|
"Time reward %s: Missing required field \"value_min\"", pKVTimedReward->GetName() ); |
|
SCHEMA_INIT_CHECK( |
|
NULL != pKVTimedReward->FindKey( "value_max" ), |
|
"Time reward %s: Missing required field \"value_max\"", pKVTimedReward->GetName() ); |
|
|
|
SCHEMA_INIT_CHECK( |
|
NULL != pKVTimedReward->FindKey( "pctChance" ), |
|
"Time reward %s: Missing required field \"pctChance\"", pKVTimedReward->GetName() ); |
|
|
|
SCHEMA_INIT_CHECK( |
|
NULL == pKVTimedReward->FindKey( "criteria" ), |
|
"Time reward %s: \"criteria\" is no longer supported. Restructure as \"loot_list\"?", pKVTimedReward->GetName() ); |
|
|
|
SCHEMA_INIT_CHECK( |
|
NULL != pKVTimedReward->FindKey( "loot_list" ), |
|
"Time reward %s: Missing required field \"loot_list\" ", pKVTimedReward->GetName() ); |
|
|
|
// Parse the loot list |
|
const char *pszLootList = pKVTimedReward->GetString("loot_list", NULL); |
|
if ( pszLootList && pszLootList[0] ) |
|
{ |
|
m_pLootList = GetItemSchema()->GetLootListByName( pszLootList ); |
|
|
|
// Make sure the item index is correct because we use this index as a reference |
|
SCHEMA_INIT_CHECK( |
|
NULL != m_pLootList, |
|
"Time Reward %s: loot_list (%s) does not exist", pKVTimedReward->GetName(), pszLootList ); |
|
} |
|
|
|
// Other integrity checks |
|
SCHEMA_INIT_CHECK( |
|
m_flChance >= 0.0f, |
|
"Time Reward %s: pctChance (%f) must be greater or equal to 0.0", pKVTimedReward->GetName(), m_flChance ); |
|
|
|
SCHEMA_INIT_CHECK( |
|
m_flChance <= 1.0f, |
|
"Time Reward %s: pctChance (%f) must be less than or equal to 1.0", pKVTimedReward->GetName(), m_flChance ); |
|
|
|
SCHEMA_INIT_CHECK( |
|
pKVTimedReward->GetInt( "value_min" ) > 0, |
|
"Time Reward %s: value_min (%d) must be greater than 0", pKVTimedReward->GetName(), m_unMinFreq ); |
|
SCHEMA_INIT_CHECK( |
|
pKVTimedReward->GetInt( "value_max" ) > 0, |
|
"Time Reward %s: value_max (%d) must be greater than 0", pKVTimedReward->GetName(), m_unMaxFreq ); |
|
SCHEMA_INIT_CHECK( |
|
(m_unMaxFreq >= m_unMinFreq), |
|
"Time Reward %s: value_max (%d) must be greater than or equal to value_min (%d)", pKVTimedReward->GetName(), m_unMaxFreq, m_unMinFreq ); |
|
|
|
return SCHEMA_INIT_SUCCESS(); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Adds a foreign item definition to local definition mapping for a |
|
// foreign app |
|
//----------------------------------------------------------------------------- |
|
void CForeignAppImports::AddMapping( uint16 unForeignDefIndex, const CEconItemDefinition *pDefn ) |
|
{ |
|
m_mapDefinitions.InsertOrReplace( unForeignDefIndex, pDefn ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Adds a foreign item definition to local definition mapping for a |
|
// foreign app |
|
//----------------------------------------------------------------------------- |
|
const CEconItemDefinition *CForeignAppImports::FindMapping( uint16 unForeignDefIndex ) const |
|
{ |
|
int i = m_mapDefinitions.Find( unForeignDefIndex ); |
|
if( m_mapDefinitions.IsValidIndex( i ) ) |
|
return m_mapDefinitions[i]; |
|
else |
|
return NULL; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Constructor |
|
//----------------------------------------------------------------------------- |
|
CEconItemSchema::CEconItemSchema( ) |
|
: m_unResetCount( 0 ) |
|
, m_pKVRawDefinition( NULL ) |
|
, m_mapItemSeries( DefLessFunc(int) ) |
|
, m_mapRarities( DefLessFunc(int) ) |
|
, m_mapQualities( DefLessFunc(int) ) |
|
, m_mapAttributes( DefLessFunc(int) ) |
|
, m_mapRecipes( DefLessFunc(int) ) |
|
, m_mapQuestObjectives( DefLessFunc(int) ) |
|
, m_mapItemsSorted( DefLessFunc(int) ) |
|
, m_mapToolsItems( DefLessFunc(int) ) |
|
, m_mapBaseItems( DefLessFunc(int) ) |
|
, m_unVersion( 0 ) |
|
#if defined(CLIENT_DLL) || defined(GAME_DLL) |
|
, m_pDefaultItemDefinition( NULL ) |
|
#endif |
|
, m_mapItemSets( CaselessStringLessThan ) |
|
, m_mapItemCollections( CaselessStringLessThan ) |
|
, m_mapItemPaintKits( CaselessStringLessThan ) |
|
, m_mapOperationDefinitions( CaselessStringLessThan ) |
|
, m_mapLootLists( CaselessStringLessThan ) |
|
, m_mapRevolvingLootLists( DefLessFunc(int) ) |
|
, m_mapDefinitionPrefabs( CaselessStringLessThan ) |
|
, m_mapAchievementRewardsByData( DefLessFunc( uint32 ) ) |
|
, m_mapAttributeControlledParticleSystems( DefLessFunc(int) ) |
|
, m_mapDefaultBodygroupState( CaselessStringLessThan ) |
|
#ifdef GC_DLL |
|
, m_mapForeignImports( DefLessFunc(AppId_t) ) |
|
#elif defined(CLIENT_DLL) || defined(GAME_DLL) |
|
, m_pDelayedSchemaData( NULL ) |
|
#endif |
|
, m_mapKillEaterScoreTypes( DefLessFunc( unsigned int ) ) |
|
, m_mapCommunityMarketDefinitionIndexRemap( DefLessFunc( item_definition_index_t ) ) |
|
#ifdef CLIENT_DLL |
|
, m_mapSteamPackageLocalizationTokens( DefLessFunc( uint32 ) ) |
|
#endif |
|
{ |
|
Reset(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
IEconTool *CEconItemSchema::CreateEconToolImpl( const char *pszToolType, const char *pszUseString, const char *pszUsageRestriction, item_capabilities_t unCapabilities, KeyValues *pUsageKV ) |
|
{ |
|
if ( pszToolType ) |
|
{ |
|
if ( !V_stricmp( pszToolType, "duel_minigame" ) ) |
|
{ |
|
// Error checking -- make sure we aren't setting properties in the schema that we don't support. |
|
if ( pszUsageRestriction ) return NULL; |
|
if ( unCapabilities != ITEM_CAP_NONE ) return NULL; |
|
if ( pUsageKV ) return NULL; |
|
|
|
return new CEconTool_DuelingMinigame( pszToolType, pszUseString ); |
|
} |
|
|
|
if ( !V_stricmp( pszToolType, "noise_maker" ) ) |
|
{ |
|
// Error checking -- make sure we aren't setting properties in the schema that we don't support. |
|
if ( pszUsageRestriction ) return NULL; |
|
if ( unCapabilities != ITEM_CAP_NONE ) return NULL; |
|
if ( pUsageKV ) return NULL; |
|
|
|
return new CEconTool_Noisemaker( pszToolType, pszUseString ); |
|
} |
|
|
|
if ( !V_stricmp( pszToolType, "wrapped_gift" ) ) |
|
{ |
|
// Error checking -- make sure we aren't setting properties in the schema that we don't support. |
|
if ( pszUsageRestriction ) return NULL; |
|
|
|
return new CEconTool_WrappedGift( pszToolType, pszUseString, unCapabilities, pUsageKV ); |
|
} |
|
|
|
if ( !V_stricmp( pszToolType, "backpack_expander" ) ) |
|
{ |
|
// Error checking -- make sure we aren't setting properties in the schema that we don't support. |
|
if ( pszUsageRestriction ) return NULL; |
|
if ( unCapabilities != ITEM_CAP_NONE ) return NULL; |
|
|
|
return new CEconTool_BackpackExpander( pszToolType, pszUseString, pUsageKV ); |
|
} |
|
|
|
if ( !V_stricmp( pszToolType, "account_upgrade_to_premium" ) ) |
|
{ |
|
// Error checking -- make sure we aren't setting properties in the schema that we don't support. |
|
if ( pszUsageRestriction ) return NULL; |
|
if ( unCapabilities != ITEM_CAP_NONE ) return NULL; |
|
if ( pUsageKV ) return NULL; |
|
|
|
return new CEconTool_AccountUpgradeToPremium( pszToolType, pszUseString ); |
|
} |
|
|
|
if ( !V_stricmp( pszToolType, "claimcode" ) ) |
|
{ |
|
// Error checking -- make sure we aren't setting properties in the schema that we don't support. |
|
if ( pszUsageRestriction ) return NULL; |
|
if ( unCapabilities != ITEM_CAP_NONE ) return NULL; |
|
|
|
return new CEconTool_ClaimCode( pszToolType, pszUseString, pUsageKV ); |
|
} |
|
|
|
if ( !V_stricmp( pszToolType, "gift" ) ) |
|
{ |
|
// Error checking -- make sure we aren't setting properties in the schema that we don't support. |
|
if ( pszUsageRestriction ) return NULL; |
|
if ( unCapabilities != ITEM_CAP_NONE ) return NULL; |
|
|
|
return new CEconTool_Gift( pszToolType, pszUseString, pUsageKV ); |
|
} |
|
|
|
if ( !V_stricmp( pszToolType, "paint_can" ) ) |
|
{ |
|
// Error checking -- make sure we aren't setting properties in the schema that we don't support. |
|
if ( pszUsageRestriction ) return NULL; |
|
if ( pUsageKV ) return NULL; |
|
|
|
return new CEconTool_PaintCan( pszToolType, unCapabilities ); |
|
} |
|
|
|
if ( !V_stricmp( pszToolType, "name" ) ) |
|
{ |
|
// Error checking -- make sure we aren't setting properties in the schema that we don't support. |
|
if ( pszUsageRestriction ) return NULL; |
|
if ( pUsageKV ) return NULL; |
|
|
|
return new CEconTool_NameTag( pszToolType, unCapabilities ); |
|
} |
|
|
|
if ( !V_stricmp( pszToolType, "desc" ) ) |
|
{ |
|
// Error checking -- make sure we aren't setting properties in the schema that we don't support. |
|
if ( pszUsageRestriction ) return NULL; |
|
if ( pUsageKV ) return NULL; |
|
|
|
return new CEconTool_DescTag( pszToolType, unCapabilities ); |
|
} |
|
|
|
if ( !V_stricmp( pszToolType, "decoder_ring" ) ) |
|
{ |
|
// Error checking -- make sure we aren't setting properties in the schema that we don't support. |
|
if ( pUsageKV ) return NULL; |
|
|
|
return new CEconTool_CrateKey( pszToolType, pszUsageRestriction, unCapabilities ); |
|
} |
|
|
|
if ( !V_stricmp( pszToolType, "customize_texture_item" ) ) |
|
{ |
|
// Error checking -- make sure we aren't setting properties in the schema that we don't support. |
|
if ( pszUsageRestriction ) return NULL; |
|
if ( pUsageKV ) return NULL; |
|
|
|
return new CEconTool_CustomizeTexture( pszToolType, unCapabilities ); |
|
} |
|
|
|
if ( !V_stricmp( pszToolType, "gift_wrap" ) ) |
|
{ |
|
// Error checking -- make sure we aren't setting properties in the schema that we don't support. |
|
if ( pszUsageRestriction ) return NULL; |
|
|
|
return new CEconTool_GiftWrap( pszToolType, pszUseString, unCapabilities, pUsageKV ); |
|
} |
|
|
|
if ( !V_stricmp( pszToolType, "wedding_ring" ) ) |
|
{ |
|
// Error checking -- make sure we aren't setting properties in the schema that we don't support. |
|
if ( pszUsageRestriction ) return NULL; |
|
if ( pUsageKV ) return NULL; |
|
|
|
return new CEconTool_WeddingRing( pszToolType, pszUseString, unCapabilities ); |
|
} |
|
|
|
if ( !V_stricmp( pszToolType, "strange_part" ) ) |
|
{ |
|
// Error checking -- make sure we aren't setting properties in the schema that we don't support. |
|
if ( pszUsageRestriction ) return NULL; |
|
|
|
return new CEconTool_StrangePart( pszToolType, pszUseString, unCapabilities, pUsageKV ); |
|
} |
|
|
|
if ( !V_stricmp( pszToolType, "strange_part_restriction" ) ) |
|
{ |
|
// Error checking -- make sure we aren't setting properties in the schema that we don't support. |
|
if ( pszUsageRestriction ) return NULL; |
|
if ( !pUsageKV ) return NULL; // required |
|
|
|
return new CEconTool_StrangePartRestriction( pszToolType, pszUseString, unCapabilities, pUsageKV ); |
|
} |
|
|
|
if ( !V_stricmp( pszToolType, "apply_custom_attrib" ) ) |
|
{ |
|
// Error checking -- make sure we aren't setting properties in the schema that we don't support. |
|
if ( pszUsageRestriction ) return NULL; |
|
|
|
return new CEconTool_UpgradeCard( pszToolType, pszUseString, unCapabilities, pUsageKV ); |
|
} |
|
|
|
if ( !V_stricmp( pszToolType, "strangifier" ) ) |
|
{ |
|
// Error checking -- make sure we aren't setting properties in the schema that we don't support. |
|
if ( pszUsageRestriction ) return NULL; |
|
|
|
return new CEconTool_Strangifier( pszToolType, pszUseString, unCapabilities, pUsageKV ); |
|
} |
|
|
|
if ( !V_stricmp( pszToolType, "killstreakifier" ) ) |
|
{ |
|
// Error checking -- make sure we aren't setting properties in the schema that we don't support. |
|
if ( pszUsageRestriction ) return NULL; |
|
|
|
return new CEconTool_KillStreakifier( pszToolType, pszUseString, unCapabilities, pUsageKV ); |
|
} |
|
|
|
if( !V_stricmp( pszToolType, "dynamic_recipe" ) ) |
|
{ |
|
// Error checking -- make sure we aren't setting properties in the schema that we don't support. |
|
if ( pszUsageRestriction ) return NULL; |
|
|
|
return new CEconTool_ItemDynamicRecipe( pszToolType, pszUseString, unCapabilities, pUsageKV ); |
|
} |
|
|
|
if ( !V_stricmp( pszToolType, "item_eater_recharger" ) ) |
|
{ |
|
// Error checking -- make sure we aren't setting properties in the schema that we don't support. |
|
if ( pszUsageRestriction ) return NULL; |
|
|
|
return new CEconTool_ItemEaterRecharger( pszToolType, pszUseString, unCapabilities, pUsageKV ); |
|
} |
|
|
|
if ( !V_stricmp( pszToolType, "class_transmogrifier" ) ) |
|
{ |
|
// Error checking -- make sure we aren't setting properties in the schema that we don't support. |
|
if ( pszUsageRestriction ) return NULL; |
|
|
|
return new CEconTool_ClassTransmogrifier( pszToolType, pszUseString, unCapabilities, pUsageKV ); |
|
} |
|
|
|
if ( !V_stricmp( pszToolType, "duck_token" ) ) |
|
{ |
|
// Error checking -- make sure we aren't setting properties in the schema that we don't support. |
|
if ( pszUsageRestriction ) return NULL; |
|
if ( pUsageKV ) return NULL; |
|
|
|
return new CEconTool_DuckToken( pszToolType, unCapabilities ); |
|
} |
|
|
|
if ( !V_stricmp( pszToolType, "grant_operation_pass" ) ) |
|
{ |
|
// Error checking -- make sure we aren't setting properties in the schema that we don't support. |
|
if ( pszUsageRestriction ) return NULL; |
|
|
|
return new CEconTool_GrantOperationPass( pszToolType, pszUseString, unCapabilities, pUsageKV ); |
|
} |
|
|
|
if ( !V_stricmp( pszToolType, "strange_count_transfer" ) ) |
|
{ |
|
// Error checking -- make sure we aren't setting properties in the schema that we don't support. |
|
if ( pszUsageRestriction ) return NULL; |
|
if ( pUsageKV ) return NULL; |
|
|
|
return new CEconTool_StrangeCountTransfer( pszToolType, unCapabilities ); |
|
} |
|
|
|
if ( !V_stricmp( pszToolType, "paintkit_weapon_festivizer" ) ) |
|
{ |
|
return new CEconTool_Festivizer( pszToolType, pszUseString, unCapabilities, pUsageKV ); |
|
} |
|
|
|
if ( !V_stricmp( pszToolType, "unusualifier" ) ) |
|
{ |
|
return new CEconTool_Unusualifier( pszToolType, pszUseString, unCapabilities, pUsageKV ); |
|
} |
|
} |
|
|
|
// Default behavior. |
|
return new CEconTool_Default( pszToolType, pszUseString, pszUsageRestriction, unCapabilities ); |
|
} |
|
|
|
#ifdef GC_DLL |
|
random_attrib_t *CEconItemSchema::CreateRandomAttribute( const char *pszContext, KeyValues *pRandomAttributesKV, CUtlVector<CUtlString> *pVecErrors /*= NULL*/ ) |
|
{ |
|
// We've found the random attribute block. Parse it. |
|
if ( pRandomAttributesKV->FindKey( "chance" ) == NULL ) |
|
{ |
|
CUtlString msg; \ |
|
msg.Format( CFmtStr( "Missing required field \"chance\" in the \"random_attributes\" block." ) ); |
|
if ( pVecErrors ) |
|
{ |
|
pVecErrors->AddToTail( msg ); |
|
} |
|
else |
|
{ |
|
AssertMsg( pRandomAttributesKV->FindKey( "chance" ) != NULL, msg.String() ); |
|
} |
|
|
|
return NULL; |
|
} |
|
|
|
random_attrib_t randomAttrib; |
|
|
|
randomAttrib.m_flChanceOfRandomAttribute = pRandomAttributesKV->GetFloat( "chance" ); |
|
randomAttrib.m_bPickAllAttributes = ( pRandomAttributesKV->GetFloat( "pick_all_attributes" ) != 0 ); |
|
randomAttrib.m_flTotalAttributeWeight = 0; |
|
|
|
FOR_EACH_TRUE_SUBKEY( pRandomAttributesKV, pKVAttribute ) |
|
{ |
|
const char *pszName = pKVAttribute->GetName(); |
|
|
|
if ( !Q_strcmp( pszName, "chance" ) ) |
|
continue; |
|
|
|
// Quick block list of attrs that have equal weight |
|
if ( !Q_strcmp( pszName, "is_even_chance_attr" ) ) |
|
{ |
|
FOR_EACH_VALUE( pKVAttribute, pKVListItem ) |
|
{ |
|
const CEconItemAttributeDefinition *pDef = GetAttributeDefinitionByName( pKVListItem->GetName() ); |
|
if ( pDef == NULL ) |
|
{ |
|
CUtlString msg; \ |
|
msg.Format( CFmtStr( "Attribute definition \"%s\" was not found", pszName ) ); |
|
if ( pVecErrors ) |
|
{ |
|
pVecErrors->AddToTail( msg ); |
|
} |
|
else |
|
{ |
|
AssertMsg( pDef != NULL, msg.String() ); |
|
} |
|
|
|
return NULL; |
|
} |
|
|
|
lootlist_attrib_t lootListAttrib; |
|
if ( !lootListAttrib.m_staticAttrib.BInitFromKV_SingleLine( __FUNCTION__, pKVListItem, pVecErrors, false ) ) |
|
{ |
|
if ( pVecErrors ) |
|
{ |
|
pVecErrors->AddToTail( __FUNCTION__ ": error initializing line-item attribute from lootlist definition (possible attr template).\n" ); |
|
} |
|
return NULL; |
|
} |
|
// Weight is set to 1 for even chance attr |
|
lootListAttrib.m_flWeight = 1.0f; |
|
randomAttrib.m_flTotalAttributeWeight += 1.0f; |
|
randomAttrib.m_RandomAttributes.AddToTail( lootListAttrib ); |
|
} |
|
} |
|
else |
|
{ |
|
const CEconItemAttributeDefinition *pDef = GetAttributeDefinitionByName( pszName ); |
|
if ( pDef == NULL ) |
|
{ |
|
CUtlString msg; \ |
|
msg.Format( CFmtStr( "Attribute definition \"%s\" was not found", pszName ) ); |
|
if ( pVecErrors ) |
|
{ |
|
pVecErrors->AddToTail( msg ); |
|
} |
|
else |
|
{ |
|
AssertMsg( pDef != NULL, msg.String() ); |
|
} |
|
|
|
return NULL; |
|
} |
|
|
|
lootlist_attrib_t lootListAttrib; |
|
if ( !lootListAttrib.BInitFromKV( pszContext, pKVAttribute, *this, pVecErrors ) ) |
|
{ |
|
return NULL; |
|
} |
|
|
|
randomAttrib.m_flTotalAttributeWeight += lootListAttrib.m_flWeight; |
|
randomAttrib.m_RandomAttributes.AddToTail( lootListAttrib ); |
|
} |
|
} |
|
|
|
random_attrib_t *pRandomAttr = new random_attrib_t; |
|
*pRandomAttr = randomAttrib; |
|
return pRandomAttr; |
|
} |
|
#endif // GC_DLL |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Resets the schema to before BInit was called |
|
//----------------------------------------------------------------------------- |
|
void CEconItemSchema::Reset( void ) |
|
{ |
|
++m_unResetCount; |
|
|
|
m_unFirstValidClass = 0; |
|
m_unLastValidClass = 0; |
|
m_unAccoutClassIndex = 0; |
|
m_unFirstValidClassItemSlot = 0; |
|
m_unLastValidClassItemSlot = 0; |
|
m_unFirstValidAccountItemSlot = 0; |
|
m_unLastValidAccountItemSlot = 0; |
|
m_unNumItemPresets = 0; |
|
m_unMinLevel = 0; |
|
m_unMaxLevel = 0; |
|
m_unVersion = 0; |
|
m_unSumQualityWeights = 0; |
|
FOR_EACH_VEC( m_vecAttributeTypes, i ) |
|
{ |
|
delete m_vecAttributeTypes[i].m_pAttrType; |
|
} |
|
m_vecAttributeTypes.Purge(); |
|
m_mapItems.PurgeAndDeleteElements(); |
|
m_mapItems.Purge(); |
|
m_mapRarities.Purge(); |
|
m_mapQualities.Purge(); |
|
m_mapItemsSorted.Purge(); |
|
m_mapToolsItems.Purge(); |
|
m_mapBaseItems.Purge(); |
|
m_mapRecipes.PurgeAndDeleteElements(); |
|
m_vecTimedRewards.Purge(); |
|
m_mapItemSets.PurgeAndDeleteElements(); |
|
m_mapLootLists.PurgeAndDeleteElements(); |
|
#ifdef GC_DLL |
|
m_dictRandomAttributeTemplates.PurgeAndDeleteElements(); |
|
#endif // GC_DLL |
|
m_mapAttributeControlledParticleSystems.Purge(); |
|
m_vecAttributeControlledParticleSystemsCosmetics.Purge(); |
|
m_vecAttributeControlledParticleSystemsWeapons.Purge(); |
|
m_vecAttributeControlledParticleSystemsTaunts.Purge(); |
|
|
|
m_mapAttributes.Purge(); |
|
if ( m_pKVRawDefinition ) |
|
{ |
|
m_pKVRawDefinition->deleteThis(); |
|
m_pKVRawDefinition = NULL; |
|
} |
|
|
|
#if defined(CLIENT_DLL) || defined(GAME_DLL) |
|
delete m_pDefaultItemDefinition; |
|
m_pDefaultItemDefinition = NULL; |
|
#endif |
|
|
|
FOR_EACH_MAP_FAST( m_mapRecipes, i ) |
|
{ |
|
delete m_mapRecipes[i]; |
|
} |
|
|
|
FOR_EACH_MAP_FAST( m_mapDefinitionPrefabs, i ) |
|
{ |
|
m_mapDefinitionPrefabs[i]->deleteThis(); |
|
} |
|
m_mapDefinitionPrefabs.Purge(); |
|
|
|
m_vecEquipRegionsList.Purge(); |
|
m_vecItemLevelingData.PurgeAndDeleteElements(); |
|
|
|
m_dictStringTable.PurgeAndDeleteElements(); |
|
m_mapCommunityMarketDefinitionIndexRemap.Purge(); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Operator= |
|
//----------------------------------------------------------------------------- |
|
CEconItemSchema &CEconItemSchema::operator=( CEconItemSchema &rhs ) |
|
{ |
|
Reset(); |
|
BInitSchema( rhs.m_pKVRawDefinition ); |
|
return *this; |
|
} |
|
|
|
bool g_bLastSignatureCheck; |
|
|
|
bool CheckValveSignature( const void *data, uint32 nDataSize, const void *signature, uint32 nSignatureSize ) |
|
{ |
|
// Must match the PUBLIC KEY in src\devtools\valve_source_officialcontent.privatekey.vdf |
|
static const unsigned char valvePublicKey[] = |
|
"\x30\x81\x9D\x30\x0D\x06\x09\x2A\x86\x48\x86\xF7\x0D\x01\x01\x01" |
|
"\x05\x00\x03\x81\x8B\x00\x30\x81\x87\x02\x81\x81\x00\xB1\xC0\xF1" |
|
"\x1C\xB2\x98\x2F\x29\x25\x95\x07\xA7\x74\xD4\x83\x43\x77\xC5\xB7" |
|
"\xA3\x8D\x9A\x4B\x38\x92\xB5\x98\x00\x9F\x16\xAA\x10\x95\x65\xCB" |
|
"\x09\xAD\x25\xDE\x0D\x3D\x1A\x08\x9C\x3C\xB6\x8E\x49\x19\x21\xCC" |
|
"\x14\x2F\x38\x33\x83\x20\x1D\xE9\x82\x62\xA7\x6E\xD8\xA6\xCC\x78" |
|
"\xBC\x51\x68\x5A\x0A\x64\xA6\x17\x2C\x67\x12\x7A\xF2\x3E\x78\x73" |
|
"\x1F\x4A\x82\xC2\x01\xD6\x4C\x9A\xB8\x09\x37\x32\x21\x84\xB6\x42" |
|
"\x72\x7F\xE1\x42\xD1\x5C\xC0\x45\xF3\x58\x3E\x19\xE3\xE3\xE1\xA9" |
|
"\xC5\x0C\x0F\xC8\x41\x13\x57\x3A\x52\x0A\x8F\x73\x23\x02\x01\x11"; |
|
|
|
// Put into a global var. Could help with VAC detection, if this |
|
// code gets detoured |
|
g_bLastSignatureCheck = CCrypto::RSAVerifySignatureSHA256( |
|
(const uint8 *)data, nDataSize, |
|
(const uint8 *)signature, nSignatureSize, |
|
valvePublicKey, sizeof(valvePublicKey) |
|
); |
|
|
|
return g_bLastSignatureCheck; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Initializes the schema, given KV filename |
|
//----------------------------------------------------------------------------- |
|
bool CEconItemSchema::BInit( const char *fileName, const char *pathID, CUtlVector<CUtlString> *pVecErrors /* = NULL */) |
|
{ |
|
Reset(); |
|
|
|
// Read the raw data |
|
CUtlBuffer bufRawData; |
|
bool bReadFileOK = g_pFullFileSystem->ReadFile( fileName, pathID, bufRawData ); |
|
SCHEMA_INIT_CHECK( bReadFileOK, "Cannot load file '%s'", fileName ); |
|
|
|
// Do we need to check the signature? |
|
#if defined(TF_DLL) || defined(TF_CLIENT_DLL) |
|
{ |
|
|
|
// Load up the signature |
|
CUtlString sSignatureFilename( fileName ); sSignatureFilename.Append( ".sig" ); |
|
CUtlBuffer bufSignatureBinary; |
|
bool bReadSignatureOK = g_pFullFileSystem->ReadFile( sSignatureFilename.String(), pathID, bufSignatureBinary ); |
|
SCHEMA_INIT_CHECK( bReadSignatureOK, "Cannot load file '%s'", sSignatureFilename.String() ); |
|
|
|
// Check it with the Valve public key |
|
bool bSignatureValid = CheckValveSignature( |
|
bufRawData.Base(), bufRawData.TellPut(), |
|
bufSignatureBinary.Base(), bufSignatureBinary.TellPut() |
|
); |
|
|
|
// If they have a signature for a zero-byte file, that's OK, too. |
|
// That's the secret code that is checked into P4 internally that |
|
// let's us run with any items_game file |
|
if ( !bSignatureValid ) |
|
{ |
|
bSignatureValid = CheckValveSignature( |
|
"", 0, |
|
bufSignatureBinary.Base(), bufSignatureBinary.TellPut() |
|
); |
|
} |
|
|
|
SCHEMA_INIT_CHECK( bSignatureValid, "'%s' is corrupt. Please verify your local game files. (https://support.steampowered.com/kb_article.php?ref=2037-QEUH-3335)", fileName ); |
|
} |
|
#endif |
|
|
|
// Compute version hash |
|
CSHA1 sha1; |
|
sha1.Update( (unsigned char *)bufRawData.Base(), bufRawData.Size() ); |
|
sha1.Final(); |
|
sha1.GetHash( m_schemaSHA.m_shaDigest ); |
|
|
|
// Wrap it with a text buffer reader |
|
CUtlBuffer bufText( bufRawData.Base(), bufRawData.TellPut(), CUtlBuffer::READ_ONLY | CUtlBuffer::TEXT_BUFFER ); |
|
|
|
// Use the standard init path |
|
return BInitTextBuffer( bufText, pVecErrors ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Initializes the schema, given KV in binary form |
|
//----------------------------------------------------------------------------- |
|
bool CEconItemSchema::BInitBinaryBuffer( CUtlBuffer &buffer, CUtlVector<CUtlString> *pVecErrors /* = NULL */ ) |
|
{ |
|
Reset(); |
|
m_pKVRawDefinition = new KeyValues( "CEconItemSchema" ); |
|
if ( m_pKVRawDefinition->ReadAsBinary( buffer ) ) |
|
{ |
|
return BInitSchema( m_pKVRawDefinition, pVecErrors ) |
|
&& BPostSchemaInit( pVecErrors ); |
|
} |
|
if ( pVecErrors ) |
|
{ |
|
pVecErrors->AddToTail( "Error parsing keyvalues" ); |
|
} |
|
return false; |
|
} |
|
|
|
unsigned char g_sha1ItemSchemaText[ k_cubHash ]; |
|
|
|
//----------------------------------------------------------------------------- |
|
// Initializes the schema, given KV in text form |
|
//----------------------------------------------------------------------------- |
|
bool CEconItemSchema::BInitTextBuffer( CUtlBuffer &buffer, CUtlVector<CUtlString> *pVecErrors /* = NULL */ ) |
|
{ |
|
// Save off the hash into a global variable, so VAC can check it |
|
// later |
|
GenerateHash( g_sha1ItemSchemaText, buffer.Base(), buffer.TellPut() ); |
|
|
|
Reset(); |
|
m_pKVRawDefinition = new KeyValues( "CEconItemSchema" ); |
|
if ( m_pKVRawDefinition->LoadFromBuffer( NULL, buffer ) ) |
|
{ |
|
return BInitSchema( m_pKVRawDefinition, pVecErrors ) |
|
&& BPostSchemaInit( pVecErrors ); |
|
} |
|
if ( pVecErrors ) |
|
{ |
|
pVecErrors->AddToTail( "Error parsing keyvalues" ); |
|
} |
|
return false; |
|
} |
|
|
|
bool CEconItemSchema::DumpItems ( const char *fileName, const char *pathID ) |
|
{ |
|
// create a write file |
|
FileHandle_t f = g_pFullFileSystem->Open(fileName, "wb", pathID); |
|
|
|
if ( f == FILESYSTEM_INVALID_HANDLE ) |
|
{ |
|
DevMsg(1, "CEconItemSchema::DumpItems: couldn't open file \"%s\" in path \"%s\".\n", |
|
fileName?fileName:"NULL", pathID?pathID:"NULL" ); |
|
return false; |
|
} |
|
|
|
CUtlSortVector< KeyValues*, CUtlSortVectorKeyValuesByName > vecSortedItems; |
|
|
|
FOR_EACH_MAP_FAST( m_mapItems, i ) |
|
{ |
|
vecSortedItems.InsertNoSort( m_mapItems[ i ]->GetRawDefinition() ); |
|
} |
|
vecSortedItems.RedoSort(); |
|
|
|
CUtlBuffer buf; |
|
FOR_EACH_VEC( vecSortedItems, i ) |
|
{ |
|
vecSortedItems[i]->RecursiveSaveToFile( buf, 0, true ); |
|
} |
|
|
|
int iBufSize = buf.GetBytesRemaining(); |
|
bool bSuccess = false; |
|
if ( g_pFullFileSystem->Write(buf.PeekGet(), iBufSize, f) == iBufSize ) |
|
bSuccess = true; |
|
|
|
g_pFullFileSystem->Close(f); |
|
|
|
return bSuccess; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Called once the price sheet's been loaded |
|
//----------------------------------------------------------------------------- |
|
#ifdef GC_DLL |
|
GCConVar econ_orphaned_sold_items_owned_by_account_id( "econ_orphaned_sold_items_owned_by_account_id", "121416792" ); |
|
|
|
bool CEconItemSchema::DoPostPriceSheetLoadInit( CEconStorePriceSheet *pPriceSheet ) |
|
{ |
|
FOR_EACH_MAP_FAST( m_mapItems, iItem ) |
|
{ |
|
CEconItemDefinition *pItemDef = m_mapItems[ iItem ]; |
|
|
|
// Is this item being sold? |
|
const econ_store_entry_t *pStoreEntry = pPriceSheet->GetEntry( pItemDef->GetDefinitionIndex() ); |
|
if ( pStoreEntry ) |
|
{ |
|
// Cache off whether this item is a pack item |
|
pItemDef->SetIsPackItem( pStoreEntry->m_bIsPackItem ); |
|
|
|
// If an item is being sold and it has no payment rules set up, we can optionally force-create |
|
// a dummy payment rule that will redirect that item revenue to a "hey, these are orphan items!" |
|
// account. |
|
if ( pItemDef->GetPaymentRules().Count() == 0 && econ_orphaned_sold_items_owned_by_account_id.GetInt() ) |
|
{ |
|
econ_item_payment_rule_t rule; |
|
rule.m_RevenueShare = 1.0; |
|
rule.m_eRuleType = kPaymentRule_PartnerSteamID; |
|
rule.m_vecValues.AddToTail( econ_orphaned_sold_items_owned_by_account_id.GetInt() ); |
|
|
|
DbgVerify( pItemDef->AddPaymentRule( rule ) == 0 ); |
|
} |
|
} |
|
|
|
// Go through the cache of all bundles... |
|
FOR_EACH_VEC( m_vecBundles, iBundle ) |
|
{ |
|
const CEconItemDefinition *pBundleItemDef = m_vecBundles[ iBundle ]; |
|
const bundleinfo_t *pBundle = pBundleItemDef->GetBundleInfo(); |
|
const bool bBundleItemIsForSale = pPriceSheet->BItemExistsInPriceSheet( pBundleItemDef->GetDefinitionIndex() ) != NULL; // Only add bundles that are actually for sale |
|
|
|
bool bAddToContainingBundleItemDefs = false; |
|
if ( pItemDef->IsPackBundle() ) |
|
{ |
|
// If the current item is a pack bundle, look for the first pack item in the current bundle (pBundle). We can safely assume that all pack items will be |
|
// in pBundle if the first pack item is, since the GC won't startup otherwise. Don't add self as a containing bundle. |
|
bAddToContainingBundleItemDefs = pItemDef->GetDefinitionIndex() != pBundleItemDef->GetDefinitionIndex() |
|
&& pBundle->vecItemDefs.HasElement( pItemDef->GetBundleInfo()->vecItemDefs[0] ); |
|
} |
|
else |
|
{ |
|
bAddToContainingBundleItemDefs = pBundle->vecItemDefs.HasElement( pItemDef ); |
|
} |
|
|
|
// Does the current bundle contain the given item? |
|
if ( bBundleItemIsForSale && bAddToContainingBundleItemDefs ) |
|
{ |
|
pItemDef->m_vecContainingBundleItemDefs.AddToTail( pBundleItemDef ); |
|
} |
|
} |
|
} |
|
|
|
return true; |
|
} |
|
#endif |
|
|
|
|
|
#if defined(CLIENT_DLL) || defined(GAME_DLL) |
|
//----------------------------------------------------------------------------- |
|
// Set up the buffer to use to reinitialize our schema next time we can do so safely. |
|
//----------------------------------------------------------------------------- |
|
bool CEconItemSchema::MaybeInitFromBuffer( IDelayedSchemaData *pDelayedSchemaData ) |
|
{ |
|
bool bDidInit = false; |
|
|
|
// Use whatever our most current data block is. |
|
if ( m_pDelayedSchemaData ) |
|
{ |
|
delete m_pDelayedSchemaData; |
|
} |
|
|
|
m_pDelayedSchemaData = pDelayedSchemaData; |
|
|
|
#ifdef CLIENT_DLL |
|
// If we aren't in a game we can parse immediately now. |
|
if ( !engine->IsInGame() ) |
|
{ |
|
BInitFromDelayedBuffer(); |
|
bDidInit = true; |
|
} |
|
#endif // CLIENT_DLL |
|
|
|
return bDidInit; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// We're in a safe place to change the contents of the schema, so do so and clean |
|
// up whatever memory we were using. |
|
//----------------------------------------------------------------------------- |
|
bool CEconItemSchema::BInitFromDelayedBuffer() |
|
{ |
|
if ( !m_pDelayedSchemaData ) |
|
return true; |
|
|
|
bool bSuccess = m_pDelayedSchemaData->InitializeSchema( this ); |
|
delete m_pDelayedSchemaData; |
|
m_pDelayedSchemaData = NULL; |
|
|
|
return bSuccess; |
|
} |
|
#endif // !GC_DLL |
|
|
|
static void CalculateKeyValuesCRCRecursive( KeyValues *pKV, CRC32_t *crc, bool bIgnoreName = false ) |
|
{ |
|
// Hash in the key name in LOWERCASE. Keyvalues files are not deterministic due |
|
// to the case insensitivity of the keys and the dependence on the existing |
|
// state of the name table upon entry. |
|
if ( !bIgnoreName ) |
|
{ |
|
const char *s = pKV->GetName(); |
|
for (;;) |
|
{ |
|
unsigned char x = tolower(*s); |
|
CRC32_ProcessBuffer( crc, &x, 1 ); // !SPEED! This is slow, but it works. |
|
if (*s == '\0') break; |
|
++s; |
|
} |
|
} |
|
|
|
// Now hash in value, depending on type |
|
// !FIXME! This is not byte-order independent! |
|
switch ( pKV->GetDataType() ) |
|
{ |
|
case KeyValues::TYPE_NONE: |
|
{ |
|
FOR_EACH_SUBKEY( pKV, pChild ) |
|
{ |
|
CalculateKeyValuesCRCRecursive( pChild, crc ); |
|
} |
|
break; |
|
} |
|
case KeyValues::TYPE_STRING: |
|
{ |
|
const char *val = pKV->GetString(); |
|
CRC32_ProcessBuffer( crc, val, strlen(val)+1 ); |
|
break; |
|
} |
|
|
|
case KeyValues::TYPE_INT: |
|
{ |
|
int val = pKV->GetInt(); |
|
CRC32_ProcessBuffer( crc, &val, sizeof(val) ); |
|
break; |
|
} |
|
|
|
case KeyValues::TYPE_UINT64: |
|
{ |
|
uint64 val = pKV->GetUint64(); |
|
CRC32_ProcessBuffer( crc, &val, sizeof(val) ); |
|
break; |
|
} |
|
|
|
case KeyValues::TYPE_FLOAT: |
|
{ |
|
float val = pKV->GetFloat(); |
|
CRC32_ProcessBuffer( crc, &val, sizeof(val) ); |
|
break; |
|
} |
|
case KeyValues::TYPE_COLOR: |
|
{ |
|
int val = pKV->GetColor().GetRawColor(); |
|
CRC32_ProcessBuffer( crc, &val, sizeof(val) ); |
|
break; |
|
} |
|
|
|
default: |
|
case KeyValues::TYPE_PTR: |
|
case KeyValues::TYPE_WSTRING: |
|
{ |
|
Assert( !"Unsupport data type!" ); |
|
break; |
|
} |
|
} |
|
} |
|
|
|
uint32 CEconItemSchema::CalculateKeyValuesVersion( KeyValues *pKV ) |
|
{ |
|
CRC32_t crc; |
|
CRC32_Init( &crc ); |
|
|
|
// Calc CRC recursively. Ignore the very top-most |
|
// key name, which isn't set consistently |
|
CalculateKeyValuesCRCRecursive( pKV, &crc, true ); |
|
CRC32_Final( &crc ); |
|
return crc; |
|
} |
|
|
|
EEquipType_t CEconItemSchema::GetEquipTypeFromClassIndex( int iClass ) const |
|
{ |
|
if ( iClass == GetAccountIndex() ) |
|
return EEquipType_t::EQUIP_TYPE_ACCOUNT; |
|
|
|
if ( iClass >= GetFirstValidClass() && iClass <= GetLastValidClass() ) |
|
return EEquipType_t::EQUIP_TYPE_CLASS; |
|
|
|
return EEquipType_t::EQUIP_TYPE_INVALID; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Initializes the schema |
|
// Input: pKVRawDefinition - The raw KeyValues representation of the schema |
|
// pVecErrors - An optional vector that will contain error messages if |
|
// the init fails. |
|
// Output: True if initialization succeeded, false otherwise |
|
//----------------------------------------------------------------------------- |
|
bool CEconItemSchema::BInitSchema( KeyValues *pKVRawDefinition, CUtlVector<CUtlString> *pVecErrors /* = NULL */ ) |
|
{ |
|
m_unMinLevel = pKVRawDefinition->GetInt( "item_level_min", 0 ); |
|
m_unMaxLevel = pKVRawDefinition->GetInt( "item_level_max", 0 ); |
|
|
|
#if !defined( GC_DLL ) |
|
m_unVersion = CalculateKeyValuesVersion( pKVRawDefinition ); |
|
#endif |
|
|
|
|
|
|
|
#ifdef GC_DLL |
|
// Validate the integrity of the base data. |
|
SCHEMA_INIT_CHECK( 0 <= m_unMinLevel, "Minimum Item Level must be at least 0" ); |
|
SCHEMA_INIT_CHECK( m_unMinLevel <= m_unMaxLevel, "Minimum Item Level must be less than or equal to Maximum Item Level" ); |
|
#endif // GC_DLL |
|
|
|
// Parse the prefabs block first so the prefabs will be populated in case anything else wants |
|
// to use them later. |
|
KeyValues *pKVPrefabs = pKVRawDefinition->FindKey( "prefabs" ); |
|
if ( NULL != pKVPrefabs ) |
|
{ |
|
SCHEMA_INIT_SUBSTEP( BInitDefinitionPrefabs( pKVPrefabs, pVecErrors ) ); |
|
} |
|
|
|
// Initialize the game info block |
|
KeyValues *pKVGameInfo = pKVRawDefinition->FindKey( "game_info" ); |
|
SCHEMA_INIT_CHECK( NULL != pKVGameInfo, "Required key \"game_info\" missing.\n" ); |
|
|
|
if ( NULL != pKVGameInfo ) |
|
{ |
|
SCHEMA_INIT_SUBSTEP( BInitGameInfo( pKVGameInfo, pVecErrors ) ); |
|
} |
|
|
|
// Initialize our attribute types. We don't actually pull this data from the schema right now but it |
|
// still makes sense to initialize it at this point. |
|
SCHEMA_INIT_SUBSTEP( BInitAttributeTypes( pVecErrors ) ); |
|
|
|
// Initialize the item series block |
|
KeyValues *pKVItemSeries = pKVRawDefinition->FindKey( "item_series_types" ); |
|
SCHEMA_INIT_CHECK( NULL != pKVItemSeries, "Required key \"item_series_types\" missing.\n" ); |
|
if ( NULL != pKVItemSeries ) |
|
{ |
|
SCHEMA_INIT_SUBSTEP( BInitItemSeries( pKVItemSeries, pVecErrors ) ); |
|
} |
|
|
|
// Initialize the rarity block |
|
KeyValues *pKVRarities = pKVRawDefinition->FindKey( "rarities" ); |
|
KeyValues *pKVRarityWeights = pKVRawDefinition->FindKey( "rarities_lootlist_weights" ); |
|
SCHEMA_INIT_CHECK( NULL != pKVRarities, "Required key \"rarities\" missing.\n" ); |
|
if ( NULL != pKVRarities ) |
|
{ |
|
SCHEMA_INIT_SUBSTEP( BInitRarities( pKVRarities, pKVRarityWeights, pVecErrors ) ); |
|
} |
|
|
|
// Initialize the qualities block |
|
KeyValues *pKVQualities = pKVRawDefinition->FindKey( "qualities" ); |
|
SCHEMA_INIT_CHECK( NULL != pKVQualities, "Required key \"qualities\" missing.\n" ); |
|
|
|
if ( NULL != pKVQualities ) |
|
{ |
|
SCHEMA_INIT_SUBSTEP( BInitQualities( pKVQualities, pVecErrors ) ); |
|
} |
|
|
|
// Initialize the colors block |
|
KeyValues *pKVColors = pKVRawDefinition->FindKey( "colors" ); |
|
SCHEMA_INIT_CHECK( NULL != pKVColors, "Required key \"colors\" missing.\n" ); |
|
|
|
if ( NULL != pKVColors ) |
|
{ |
|
SCHEMA_INIT_SUBSTEP( BInitColors( pKVColors, pVecErrors ) ); |
|
} |
|
|
|
// Initialize the attributes block |
|
KeyValues *pKVAttributes = pKVRawDefinition->FindKey( "attributes" ); |
|
SCHEMA_INIT_CHECK( NULL != pKVAttributes, "Required key \"attributes\" missing.\n" ); |
|
|
|
if ( NULL != pKVAttributes ) |
|
{ |
|
SCHEMA_INIT_SUBSTEP( BInitAttributes( pKVAttributes, pVecErrors ) ); |
|
} |
|
|
|
#ifdef GC |
|
// Initialize the motd block |
|
KeyValues *pKVMOTD = pKVRawDefinition->FindKey( "motd_entries" ); |
|
SCHEMA_INIT_CHECK( NULL != pKVMOTD, "Required key \"motd_entries\" missing.\n" ); |
|
|
|
if ( NULL != pKVMOTD ) |
|
{ |
|
SCHEMA_INIT_SUBSTEP( GGCGameBase()->GetMOTDManager().BInitMOTDEntries( pKVMOTD, pVecErrors ) ); |
|
} |
|
#endif |
|
|
|
// Initialize the "equip_regions_list" block -- this is an optional block |
|
KeyValues *pKVEquipRegions = pKVRawDefinition->FindKey( "equip_regions_list" ); |
|
if ( NULL != pKVEquipRegions ) |
|
{ |
|
SCHEMA_INIT_SUBSTEP( BInitEquipRegions( pKVEquipRegions, pVecErrors ) ); |
|
} |
|
|
|
// Initialize the "equip_conflicts" block -- this is an optional block, though it doesn't |
|
// make any sense and will probably fail internally if there is no corresponding "equip_regions" |
|
// block as well |
|
KeyValues *pKVEquipRegionConflicts = pKVRawDefinition->FindKey( "equip_conflicts" ); |
|
if ( NULL != pKVEquipRegionConflicts ) |
|
{ |
|
SCHEMA_INIT_SUBSTEP( BInitEquipRegionConflicts( pKVEquipRegionConflicts, pVecErrors ) ); |
|
} |
|
|
|
// TF2 Paint Kits |
|
// No included in schema file (Too Big). Loaded Seperately |
|
// Load the KV and add it to the pKVRawDefinition |
|
KeyValues *pPaintkitKV = new KeyValues( "item_paintkit_definitions" ); |
|
SCHEMA_INIT_CHECK( pPaintkitKV->LoadFromFile( g_pFullFileSystem, "scripts/items/paintkits_master.txt", "GAME" ), "Unable to Load paintkits_master.txt KV File!" ); |
|
pKVRawDefinition->AddSubKey( pPaintkitKV ); |
|
|
|
// Init Item Paint Kits |
|
// Must be BEFORE Item defs |
|
KeyValues *pKVItemPaintKits = pKVRawDefinition->FindKey( "item_paintkit_definitions" ); |
|
if ( NULL != pKVItemPaintKits ) |
|
{ |
|
SCHEMA_INIT_SUBSTEP( BInitItemPaintKitDefinitions( pKVItemPaintKits, pVecErrors ) ); |
|
} |
|
|
|
#ifdef GC_DLL |
|
// Parse the loot lists block (on the GC) |
|
// Must be BEFORE Item defs |
|
KeyValues *pKVRandomAttributeTemplates = pKVRawDefinition->FindKey( "random_attribute_templates" ); |
|
SCHEMA_INIT_SUBSTEP( BInitRandomAttributeTemplates( pKVRandomAttributeTemplates, pVecErrors ) ); |
|
#endif // GC_DLL |
|
|
|
// Initialize the items block |
|
KeyValues *pKVItems = pKVRawDefinition->FindKey( "items" ); |
|
SCHEMA_INIT_CHECK( NULL != pKVItems, "Required key \"items\" missing.\n" ); |
|
|
|
if ( NULL != pKVItems ) |
|
{ |
|
SCHEMA_INIT_SUBSTEP( BInitItems( pKVItems, pVecErrors ) ); |
|
} |
|
|
|
|
|
// Verify base item names are proper in item schema |
|
SCHEMA_INIT_SUBSTEP( BVerifyBaseItemNames( pVecErrors ) ); |
|
|
|
// Parse the item_sets block. |
|
KeyValues *pKVItemSets = pKVRawDefinition->FindKey( "item_sets" ); |
|
SCHEMA_INIT_SUBSTEP( BInitItemSets( pKVItemSets, pVecErrors ) ); |
|
|
|
// Particles |
|
KeyValues *pKVParticleSystems = pKVRawDefinition->FindKey( "attribute_controlled_attached_particles" ); |
|
SCHEMA_INIT_SUBSTEP( BInitAttributeControlledParticleSystems( pKVParticleSystems, pVecErrors ) ); |
|
|
|
// Parse any recipes block |
|
KeyValues *pKVRecipes = pKVRawDefinition->FindKey( "recipes" ); |
|
SCHEMA_INIT_SUBSTEP( BInitRecipes( pKVRecipes, pVecErrors ) ); |
|
|
|
// Reset our loot lists. |
|
m_mapLootLists.RemoveAll(); |
|
|
|
// Init Item Collections - Must be before lootlists since collections are lootlists themselves and are referenced by lootlists |
|
KeyValues *pKVItemCollections = pKVRawDefinition->FindKey( "item_collections" ); |
|
if ( NULL != pKVItemCollections ) |
|
{ |
|
SCHEMA_INIT_SUBSTEP( BInitItemCollections( pKVItemCollections, pVecErrors ) ); |
|
} |
|
|
|
#ifdef GC_DLL |
|
// Parse the loot lists block (on the GC) |
|
KeyValues *pKVLootLists = pKVRawDefinition->FindKey( "loot_lists" ); |
|
SCHEMA_INIT_SUBSTEP( BInitLootLists( pKVLootLists, pVecErrors ) ); |
|
|
|
// Initialize the periodic score accumulation block (this needs to take place after items) |
|
KeyValues *pKVPeriodicScoring = pKVRawDefinition->FindKey( "periodic_score_accumulation" ); |
|
if ( NULL != pKVPeriodicScoring ) |
|
{ |
|
SCHEMA_INIT_SUBSTEP( BInitPeriodicScoring( pKVPeriodicScoring, pVecErrors ) ); |
|
} |
|
#endif // GC_DLL |
|
|
|
// Parse the client loot lists block (everywhere) |
|
KeyValues *pKVClientLootLists = pKVRawDefinition->FindKey( "client_loot_lists" ); |
|
SCHEMA_INIT_SUBSTEP( BInitLootLists( pKVClientLootLists, pVecErrors ) ); |
|
|
|
// Parse the revolving loot lists block |
|
KeyValues *pKVRevolvingLootLists = pKVRawDefinition->FindKey( "revolving_loot_lists" ); |
|
SCHEMA_INIT_SUBSTEP( BInitRevolvingLootLists( pKVRevolvingLootLists, pVecErrors ) ); |
|
|
|
// Init Items that may reference Collections |
|
SCHEMA_INIT_SUBSTEP( BInitCollectionReferences( pVecErrors ) ); |
|
|
|
// Validate Operation Pass |
|
KeyValues *pKVOperationDefinitions = pKVRawDefinition->FindKey( "operations" ); |
|
if ( NULL != pKVOperationDefinitions ) |
|
{ |
|
SCHEMA_INIT_SUBSTEP( BInitOperationDefinitions( pKVGameInfo, pKVOperationDefinitions, pVecErrors ) ); |
|
} |
|
|
|
#if defined( GC_DLL ) |
|
// Parse any time-based rewards |
|
KeyValues *pKVTimeRewards = pKVRawDefinition->FindKey( "time_rewards" ); |
|
SCHEMA_INIT_SUBSTEP( BInitTimedRewards( pKVTimeRewards, pVecErrors ) ); |
|
|
|
KeyValues *pKVExperiments = pKVRawDefinition->FindKey( "experiments" ); |
|
SCHEMA_INIT_SUBSTEP( BInitExperiements( pKVExperiments, pVecErrors ) ); |
|
|
|
SCHEMA_INIT_SUBSTEP( BInitForeignImports( pVecErrors ) ); |
|
#elif defined( CLIENT_DLL ) || defined( GAME_DLL ) |
|
KeyValues *pKVArmoryData = pKVRawDefinition->FindKey( "armory_data" ); |
|
SCHEMA_INIT_SUBSTEP( BInitArmoryData( pKVArmoryData, pVecErrors ) ); |
|
#endif // GC_DLL |
|
|
|
// Parse any achievement rewards |
|
KeyValues *pKVAchievementRewards = pKVRawDefinition->FindKey( "achievement_rewards" ); |
|
SCHEMA_INIT_SUBSTEP( BInitAchievementRewards( pKVAchievementRewards, pVecErrors ) ); |
|
|
|
#ifdef TF_CLIENT_DLL |
|
// Compute the number of concrete items, for each item, and cache for quick access |
|
SCHEMA_INIT_SUBSTEP( BInitConcreteItemCounts( pVecErrors ) ); |
|
|
|
// We don't have access to Steam's full library of app data on the client so initialize whichever packages |
|
// we want to reference. |
|
KeyValues *pKVSteamPackages = pKVRawDefinition->FindKey( "steam_packages" ); |
|
SCHEMA_INIT_SUBSTEP( BInitSteamPackageLocalizationToken( pKVSteamPackages, pVecErrors ) ); |
|
#endif // TF_CLIENT_DLL |
|
|
|
// Parse the item levels block |
|
KeyValues *pKVItemLevels = pKVRawDefinition->FindKey( "item_levels" ); |
|
SCHEMA_INIT_SUBSTEP( BInitItemLevels( pKVItemLevels, pVecErrors ) ); |
|
|
|
// Parse the kill eater score types |
|
KeyValues *pKVKillEaterScoreTypes = pKVRawDefinition->FindKey( "kill_eater_score_types" ); |
|
SCHEMA_INIT_SUBSTEP( BInitKillEaterScoreTypes( pKVKillEaterScoreTypes, pVecErrors ) ); |
|
|
|
// Initialize the string tables, if present |
|
KeyValues *pKVStringTables = pKVRawDefinition->FindKey( "string_lookups" ); |
|
SCHEMA_INIT_SUBSTEP( BInitStringTables( pKVStringTables, pVecErrors ) ); |
|
|
|
// Initialize the community Market remaps, if present |
|
KeyValues *pKVCommunityMarketRemaps = pKVRawDefinition->FindKey( "community_market_item_remaps" ); |
|
SCHEMA_INIT_SUBSTEP( BInitCommunityMarketRemaps( pKVCommunityMarketRemaps, pVecErrors ) ); |
|
|
|
return SCHEMA_INIT_SUCCESS(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Initializes the "game_info" section of the schema |
|
//----------------------------------------------------------------------------- |
|
bool CEconItemSchema::BInitGameInfo( KeyValues *pKVGameInfo, CUtlVector<CUtlString> *pVecErrors ) |
|
{ |
|
m_unFirstValidClass = pKVGameInfo->GetInt( "first_valid_class", 0 ); |
|
m_unLastValidClass = pKVGameInfo->GetInt( "last_valid_class", 0 ); |
|
SCHEMA_INIT_CHECK( 0 < m_unFirstValidClass, "First valid class must be greater than 0." ); |
|
SCHEMA_INIT_CHECK( m_unFirstValidClass <= m_unLastValidClass, "First valid class must be less than or equal to last valid class." ); |
|
m_unAccoutClassIndex = pKVGameInfo->GetInt( "account_class_index", 0 ); |
|
SCHEMA_INIT_CHECK( m_unAccoutClassIndex > m_unLastValidClass, "Account class index must be greater than 'last_valid_class'" ); |
|
|
|
m_unFirstValidClassItemSlot = pKVGameInfo->GetInt( "first_valid_item_slot", INVALID_EQUIPPED_SLOT ); |
|
m_unLastValidClassItemSlot = pKVGameInfo->GetInt( "last_valid_item_slot", INVALID_EQUIPPED_SLOT ); |
|
SCHEMA_INIT_CHECK( INVALID_EQUIPPED_SLOT != m_unFirstValidClassItemSlot, "first_valid_item_slot not set!" ); |
|
SCHEMA_INIT_CHECK( INVALID_EQUIPPED_SLOT != m_unFirstValidClassItemSlot, "last_valid_item_slot not set!" ); |
|
SCHEMA_INIT_CHECK( m_unFirstValidClassItemSlot <= m_unLastValidClassItemSlot, "First valid item slot must be less than or equal to last valid item slot." ); |
|
|
|
m_unFirstValidAccountItemSlot = pKVGameInfo->GetInt( "account_first_valid_item_slot", INVALID_EQUIPPED_SLOT ); |
|
m_unLastValidAccountItemSlot = pKVGameInfo->GetInt( "account_last_valid_item_slot", INVALID_EQUIPPED_SLOT ); |
|
SCHEMA_INIT_CHECK( INVALID_EQUIPPED_SLOT != m_unFirstValidAccountItemSlot, "account_first_valid_item_slot not set!" ); |
|
SCHEMA_INIT_CHECK( INVALID_EQUIPPED_SLOT != m_unLastValidAccountItemSlot, "account_last_valid_item_slot not set!" ); |
|
SCHEMA_INIT_CHECK( m_unFirstValidAccountItemSlot <= m_unLastValidAccountItemSlot, "First vlid account item slot must be less than or equal to the last valid account item slot." ); |
|
|
|
m_unNumItemPresets = pKVGameInfo->GetInt( "num_item_presets", -1 ); |
|
SCHEMA_INIT_CHECK( (uint32)-1 != m_unNumItemPresets, "num_item_presets not set!" ); |
|
|
|
return SCHEMA_INIT_SUCCESS(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
bool CEconItemSchema::BInitAttributeTypes( CUtlVector<CUtlString> *pVecErrors ) |
|
{ |
|
FOR_EACH_VEC( m_vecAttributeTypes, i ) |
|
{ |
|
delete m_vecAttributeTypes[i].m_pAttrType; |
|
} |
|
m_vecAttributeTypes.Purge(); |
|
|
|
m_vecAttributeTypes.AddToTail( attr_type_t( NULL, new CSchemaAttributeType_Default ) ); |
|
m_vecAttributeTypes.AddToTail( attr_type_t( "float", new CSchemaAttributeType_Float ) ); |
|
m_vecAttributeTypes.AddToTail( attr_type_t( "uint64", new CSchemaAttributeType_UInt64 ) ); |
|
m_vecAttributeTypes.AddToTail( attr_type_t( "string", new CSchemaAttributeType_String ) ); |
|
m_vecAttributeTypes.AddToTail( attr_type_t( "dynamic_recipe_component_defined_item", new CSchemaAttributeType_DynamicRecipeComponentDefinedItem ) ); |
|
m_vecAttributeTypes.AddToTail( attr_type_t( "item_slot_criteria", new CSchemaAttributeType_ItemSlotCriteria ) ); |
|
m_vecAttributeTypes.AddToTail( attr_type_t( "item_placement", new CSchemaAttributeType_WorldItemPlacement ) ); |
|
|
|
// Make sure that all attribute types specified have the item ID in the 0th column. We use this |
|
// when loading items to map between item IDs and the attributes they own. |
|
FOR_EACH_VEC( m_vecAttributeTypes, i ) |
|
{ |
|
#ifdef GC_DLL |
|
const CColumnSet& cs = m_vecAttributeTypes[i].m_pAttrType->GetFullColumnSet(); |
|
|
|
SCHEMA_INIT_CHECK( cs.GetColumnCount() >= 2, "BInitAttributeTypes(): '%s' has invalid column count.\n", cs.GetRecordInfo()->GetName() ); |
|
|
|
const CColumnInfo& Column0 = cs.GetColumnInfo( 0 ); |
|
SCHEMA_INIT_CHECK( Column0.GetType() == k_EGCSQLType_int64, "BInitAttributeTypes(): '%s' column 0 has invalid data type %u.\n", cs.GetRecordInfo()->GetName(), Column0.GetType() ); |
|
SCHEMA_INIT_CHECK( Column0.GetName() && !V_stricmp( Column0.GetName(), "ItemID" ), "BInitAttributeTypes(): '%s' has invalid name '%s'.\n", cs.GetRecordInfo()->GetName(), Column0.GetName() ? Column0.GetName() : "[null]" ); |
|
SCHEMA_INIT_CHECK( Column0.BIsPrimaryKey(), "BInitAttributeTypes(): '%s' has an item ID column that isn't in the PK.\n", cs.GetRecordInfo()->GetName() ); |
|
|
|
const CColumnInfo& Column1 = cs.GetColumnInfo( 1 ); |
|
SCHEMA_INIT_CHECK( Column1.GetType() == k_EGCSQLType_int16, "BInitAttributeTypes(): '%s' column 1 has invalid data type %u.\n", cs.GetRecordInfo()->GetName(), Column0.GetType() ); |
|
SCHEMA_INIT_CHECK( Column1.GetName() && !V_stricmp( Column1.GetName(), "AttrDefIndex" ), "BInitAttributeTypes(): '%s' has invalid name '%s'.\n", cs.GetRecordInfo()->GetName(), Column1.GetName() ? Column1.GetName() : "[null]" ); |
|
|
|
// Make sure two different attribute types don't point to the same DB table. There's nothing |
|
// technically that would prevent this from working, but right now the way we load from the |
|
// DB would make this super-inefficient so we'd want to fix that code if we are rolling content |
|
// that would hit this error. |
|
for ( int j = i + 1; j < m_vecAttributeTypes.Count(); j++ ) |
|
{ |
|
SCHEMA_INIT_CHECK( cs.GetRecordInfo() != m_vecAttributeTypes[j].m_pAttrType->GetFullColumnSet().GetRecordInfo(), |
|
"BInitAttributeTypes(): multiple attribute types reference the same table '%s'.\n", cs.GetRecordInfo()->GetName() ); |
|
} |
|
#endif // GC_DLL |
|
} |
|
|
|
return SCHEMA_INIT_SUCCESS(); |
|
} |
|
|
|
#ifdef GC_DLL |
|
//----------------------------------------------------------------------------- |
|
// Purpose: Initializes the "periodic_score_accumulation" section of the schema |
|
//----------------------------------------------------------------------------- |
|
struct periodic_score_event_lookup_entry_t { const char *m_pszName; eEconPeriodicScoreEvents m_eValue; bool m_bGCUpdateOnly; }; |
|
static const periodic_score_event_lookup_entry_t sPeriodicScoreEvents[] = |
|
{ |
|
{ "gifts_distributed", kPeriodicScoreEvent_GiftsDistributed, true }, |
|
{ "duels_won", kPeriodicScoreEvent_DuelsWon, true }, |
|
{ "map_stamps_purchased", kPeriodicScoreEvent_MapStampsPurchased, true }, |
|
}; |
|
|
|
struct periodic_score_duration_lookup_entry_t { const char *m_pszName; uint32 m_unValue; }; |
|
static const periodic_score_duration_lookup_entry_t sPeriodicScoreDurations[] = |
|
{ |
|
{ "disabled", 0 }, |
|
{ "hourly", 60 * 60 }, |
|
{ "daily", 60 * 60 * 24 }, |
|
{ "weekly", 60 * 60 * 24 * 7 }, |
|
{ "monthly", 60 * 60 * 24 * 7 * 4 }, // four weeks, not necessarily a month boundary |
|
}; |
|
|
|
template < typename search_entry_type, int search_entry_array_size > |
|
static bool LookupValueFromString( const search_entry_type(&searchArray)[search_entry_array_size], const char *pszSearch, search_entry_type *out_pResult ) |
|
{ |
|
Assert( out_pResult ); |
|
|
|
for ( int i = 0; i < search_entry_array_size; i++ ) |
|
{ |
|
if ( !V_stricmp( pszSearch, searchArray[i].m_pszName ) ) |
|
{ |
|
*out_pResult = searchArray[i]; |
|
return true; |
|
} |
|
} |
|
|
|
return false; |
|
} |
|
|
|
bool CEconItemSchema::BInitPeriodicScoring( KeyValues *pKVPeriodicScoring, CUtlVector<CUtlString> *pVecErrors ) |
|
{ |
|
FOR_EACH_TRUE_SUBKEY( pKVPeriodicScoring, pKVScoreType ) |
|
{ |
|
int index = Q_atoi( pKVScoreType->GetName() ); |
|
SCHEMA_INIT_CHECK( index == m_vecPeriodicScoreTypes.Count(), "Invalid or out-of-order periodic score type '%s'", pKVScoreType->GetName() ); |
|
|
|
periodic_score_t PeriodicScore; |
|
|
|
// Reward item definition. |
|
const char *pszRewardItemDefName = pKVScoreType->GetString( "reward_item_def_name", NULL ); |
|
PeriodicScore.m_pRewardItemDefinition = pszRewardItemDefName ? GetItemDefinitionByName( pszRewardItemDefName ) : NULL; |
|
SCHEMA_INIT_CHECK( PeriodicScore.m_pRewardItemDefinition, "Periodic score type '%s' missing reward item definition name", pKVScoreType->GetName() ); |
|
|
|
// Event type via string lookup. |
|
const char *pszEventName = pKVScoreType->GetString( "event", "" ); |
|
{ |
|
periodic_score_event_lookup_entry_t EventEntry; |
|
SCHEMA_INIT_CHECK( LookupValueFromString( sPeriodicScoreEvents, pszEventName, &EventEntry ), |
|
"Periodic score type '%s' could not find event name '%s'", pKVScoreType->GetName(), pszEventName ); |
|
PeriodicScore.m_eEventType = EventEntry.m_eValue; |
|
|
|
// Note: other parts of the code assume that the event type is associated with the GC-only updatability flag.) |
|
PeriodicScore.m_bGCUpdateOnly = EventEntry.m_bGCUpdateOnly; |
|
} |
|
|
|
// Time period via string lookup. |
|
{ |
|
const char *pszTimePeriodName = pKVScoreType->GetString( "time_period", "" ); |
|
periodic_score_duration_lookup_entry_t DurationEntry; |
|
SCHEMA_INIT_CHECK( LookupValueFromString( sPeriodicScoreDurations, pszTimePeriodName, &DurationEntry ), |
|
"Periodic score type '%s' could not find time period name '%s'", pKVScoreType->GetName(), pszEventName ); |
|
PeriodicScore.m_unTimePeriodLengthInSeconds = DurationEntry.m_unValue; |
|
} |
|
|
|
// Alternate time period specified for use in internal Steam? |
|
if ( GGCHost()->GetUniverse() != k_EUniversePublic ) |
|
{ |
|
const char *pszInternalTimePeriodName = pKVScoreType->GetString( "time_period_internal", NULL ); |
|
if ( pszInternalTimePeriodName ) |
|
{ |
|
periodic_score_duration_lookup_entry_t InternalDurationEntry; |
|
SCHEMA_INIT_CHECK( LookupValueFromString( sPeriodicScoreDurations, pszInternalTimePeriodName, &InternalDurationEntry ), |
|
"Periodic score type '%s' could not find internal time period name '%s'", pKVScoreType->GetName(), pszEventName ); |
|
PeriodicScore.m_unTimePeriodLengthInSeconds = InternalDurationEntry.m_unValue; |
|
} |
|
} |
|
|
|
m_vecPeriodicScoreTypes.AddToTail( PeriodicScore ); |
|
} |
|
|
|
return SCHEMA_INIT_SUCCESS(); |
|
} |
|
#endif // GC_DLL |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Initializes the "prefabs" section of the schema |
|
//----------------------------------------------------------------------------- |
|
bool CEconItemSchema::BInitDefinitionPrefabs( KeyValues *pKVPrefabs, CUtlVector<CUtlString> *pVecErrors ) |
|
{ |
|
FOR_EACH_TRUE_SUBKEY( pKVPrefabs, pKVPrefab ) |
|
{ |
|
const char *pszPrefabName = pKVPrefab->GetName(); |
|
|
|
int nMapIndex = m_mapDefinitionPrefabs.Find( pszPrefabName ); |
|
|
|
// Make sure the item index is correct because we use this index as a reference |
|
SCHEMA_INIT_CHECK( |
|
!m_mapDefinitionPrefabs.IsValidIndex( nMapIndex ), |
|
"Duplicate prefab name (%s)", pszPrefabName ); |
|
|
|
m_mapDefinitionPrefabs.Insert( pszPrefabName, pKVPrefab->MakeCopy() ); |
|
} |
|
|
|
return SCHEMA_INIT_SUCCESS(); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Initializes the Item Series section of the schema |
|
//----------------------------------------------------------------------------- |
|
bool CEconItemSchema::BInitItemSeries( KeyValues *pKVSeries, CUtlVector<CUtlString> *pVecErrors ) |
|
{ |
|
// initialize the item definitions |
|
if ( NULL != pKVSeries) |
|
{ |
|
FOR_EACH_TRUE_SUBKEY( pKVSeries, pKVItem ) |
|
{ |
|
int nSeriesIndex = pKVItem->GetInt( "value" ); |
|
int nMapIndex = m_mapItemSeries.Find( nSeriesIndex ); |
|
|
|
// Make sure the item index is correct because we use this index as a reference |
|
SCHEMA_INIT_CHECK( |
|
!m_mapItemSeries.IsValidIndex( nMapIndex ), |
|
"Duplicate item series value (%d)", nSeriesIndex ); |
|
|
|
nMapIndex = m_mapItemSeries.Insert( nMapIndex ); |
|
SCHEMA_INIT_SUBSTEP( m_mapItemSeries[nMapIndex].BInitFromKV( pKVItem, pVecErrors ) ); |
|
} |
|
} |
|
|
|
return SCHEMA_INIT_SUCCESS(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Initializes the rarity section of the schema |
|
//----------------------------------------------------------------------------- |
|
bool CEconItemSchema::BInitRarities( KeyValues *pKVRarities, KeyValues *pKVRarityWeights, CUtlVector<CUtlString> *pVecErrors ) |
|
{ |
|
// initialize the item definitions |
|
if ( NULL != pKVRarities ) |
|
{ |
|
FOR_EACH_TRUE_SUBKEY( pKVRarities, pKVRarity ) |
|
{ |
|
int nRarityIndex = pKVRarity->GetInt( "value" ); |
|
int nMapIndex = m_mapRarities.Find( nRarityIndex ); |
|
|
|
// Make sure the item index is correct because we use this index as a reference |
|
SCHEMA_INIT_CHECK( |
|
!m_mapRarities.IsValidIndex( nMapIndex ), |
|
"Duplicate rarity value (%d)", nRarityIndex ); |
|
|
|
nMapIndex = m_mapRarities.Insert( nRarityIndex ); |
|
SCHEMA_INIT_SUBSTEP( m_mapRarities[nMapIndex].BInitFromKV( pKVRarity, pKVRarityWeights, *this, pVecErrors ) ); |
|
} |
|
} |
|
|
|
return SCHEMA_INIT_SUCCESS(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Initializes the qualities section of the schema |
|
// Input: pKVQualities - The qualities section of the KeyValues |
|
// representation of the schema |
|
// pVecErrors - An optional vector that will contain error messages if |
|
// the init fails. |
|
// Output: True if initialization succeeded, false otherwise |
|
//----------------------------------------------------------------------------- |
|
bool CEconItemSchema::BInitQualities( KeyValues *pKVQualities, CUtlVector<CUtlString> *pVecErrors ) |
|
{ |
|
// initialize the item definitions |
|
if ( NULL != pKVQualities ) |
|
{ |
|
FOR_EACH_TRUE_SUBKEY( pKVQualities, pKVQuality ) |
|
{ |
|
int nQualityIndex = pKVQuality->GetInt( "value" ); |
|
int nMapIndex = m_mapQualities.Find( nQualityIndex ); |
|
|
|
// Make sure the item index is correct because we use this index as a reference |
|
SCHEMA_INIT_CHECK( |
|
!m_mapQualities.IsValidIndex( nMapIndex ), |
|
"Duplicate quality value (%d)", nQualityIndex ); |
|
|
|
nMapIndex = m_mapQualities.Insert( nQualityIndex ); |
|
SCHEMA_INIT_SUBSTEP( m_mapQualities[nMapIndex].BInitFromKV( pKVQuality, pVecErrors ) ); |
|
} |
|
} |
|
|
|
// Check the integrity of the quality definitions |
|
|
|
// Check for duplicate quality names |
|
CUtlRBTree<const char *> rbQualityNames( CaselessStringLessThan ); |
|
rbQualityNames.EnsureCapacity( m_mapQualities.Count() ); |
|
FOR_EACH_MAP_FAST( m_mapQualities, i ) |
|
{ |
|
int iIndex = rbQualityNames.Find( m_mapQualities[i].GetName() ); |
|
SCHEMA_INIT_CHECK( |
|
!rbQualityNames.IsValidIndex( iIndex ), |
|
"Quality definition %d: Duplicate quality name %s", m_mapQualities[i].GetDBValue(), m_mapQualities[i].GetName() ); |
|
|
|
if( !rbQualityNames.IsValidIndex( iIndex ) ) |
|
rbQualityNames.Insert( m_mapQualities[i].GetName() ); |
|
} |
|
|
|
return SCHEMA_INIT_SUCCESS(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
bool CEconItemSchema::BInitColors( KeyValues *pKVColors, CUtlVector<CUtlString> *pVecErrors ) |
|
{ |
|
// initialize the color definitions |
|
if ( NULL != pKVColors ) |
|
{ |
|
FOR_EACH_TRUE_SUBKEY( pKVColors, pKVColor ) |
|
{ |
|
CEconColorDefinition *pNewColorDef = new CEconColorDefinition; |
|
|
|
SCHEMA_INIT_SUBSTEP( pNewColorDef->BInitFromKV( pKVColor, pVecErrors ) ); |
|
m_vecColorDefs.AddToTail( pNewColorDef ); |
|
} |
|
} |
|
|
|
return SCHEMA_INIT_SUCCESS(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
int CEconItemSchema::GetEquipRegionIndexByName( const char *pRegionName ) const |
|
{ |
|
FOR_EACH_VEC( m_vecEquipRegionsList, i ) |
|
{ |
|
const char *szEntryRegionName = m_vecEquipRegionsList[i].m_sName.Get(); |
|
if ( !V_stricmp( szEntryRegionName, pRegionName ) ) |
|
return i; |
|
} |
|
|
|
return -1; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
equip_region_mask_t CEconItemSchema::GetEquipRegionBitMaskByName( const char *pRegionName ) const |
|
{ |
|
int iRegionIndex = GetEquipRegionIndexByName( pRegionName ); |
|
if ( !m_vecEquipRegionsList.IsValidIndex( iRegionIndex ) ) |
|
return 0; |
|
|
|
equip_region_mask_t unRegionMask = 1 << m_vecEquipRegionsList[iRegionIndex].m_unBitIndex; |
|
Assert( unRegionMask > 0 ); |
|
|
|
return unRegionMask; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CEconItemSchema::SetEquipRegionConflict( int iRegion, unsigned int unBit ) |
|
{ |
|
Assert( m_vecEquipRegionsList.IsValidIndex( iRegion ) ); |
|
|
|
equip_region_mask_t unRegionMask = 1 << unBit; |
|
Assert( unRegionMask > 0 ); |
|
|
|
m_vecEquipRegionsList[iRegion].m_unMask |= unRegionMask; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
equip_region_mask_t CEconItemSchema::GetEquipRegionMaskByName( const char *pRegionName ) const |
|
{ |
|
int iRegionIdx = GetEquipRegionIndexByName( pRegionName ); |
|
if ( iRegionIdx < 0 ) |
|
return 0; |
|
|
|
return m_vecEquipRegionsList[iRegionIdx].m_unMask; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CEconItemSchema::AssignDefaultBodygroupState( const char *pszBodygroupName, int iValue ) |
|
{ |
|
// Flip the value passed in -- if we specify in the schema that a region should be off, we assume that it's |
|
// on by default. |
|
// actually the schemas are all authored assuming that the default is 0, so let's use that |
|
int iDefaultValue = 0; //iValue == 0 ? 1 : 0; |
|
|
|
// Make sure that we're constantly reinitializing our default value to the same default value. This is sort |
|
// of dumb but it works for everything we've got now. In the event that conflicts start cropping up it would |
|
// be easy enough to make a new schema section. |
|
int iIndex = m_mapDefaultBodygroupState.Find( pszBodygroupName ); |
|
if ( (m_mapDefaultBodygroupState.IsValidIndex( iIndex ) && m_mapDefaultBodygroupState[iIndex] != iDefaultValue) || |
|
(iValue < 0 || iValue > 1) ) |
|
{ |
|
EmitWarning( SPEW_GC, 4, "Unable to get accurate read on whether bodygroup '%s' is enabled or disabled by default. (The schema is fine, but the code is confused and could stand to be made smarter.)\n", pszBodygroupName ); |
|
} |
|
|
|
if ( !m_mapDefaultBodygroupState.IsValidIndex( iIndex ) ) |
|
{ |
|
m_mapDefaultBodygroupState.Insert( pszBodygroupName, iDefaultValue ); |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
bool CEconItemSchema::BInitEquipRegions( KeyValues *pKVEquipRegions, CUtlVector<CUtlString> *pVecErrors ) |
|
{ |
|
CUtlVector<const char *> vecNames; |
|
|
|
FOR_EACH_SUBKEY( pKVEquipRegions, pKVRegion ) |
|
{ |
|
const char *pRegionKeyName = pKVRegion->GetName(); |
|
|
|
vecNames.Purge(); |
|
|
|
// The "shared" name is special for equip regions -- it means that all of the sub-regions specified |
|
// will use the same bit to store equipped-or-not data, but that one bit can be accessed by a whole |
|
// bunch of different names. This is useful in TF where different classes have different regions, but |
|
// those regions cannot possibly conflict with each other. For example, "scout_backpack" cannot possibly |
|
// overlap with "pyro_shoulder" because they can't even be equipped on the same character. |
|
if ( pRegionKeyName && !Q_stricmp( pRegionKeyName, "shared" ) ) |
|
{ |
|
FOR_EACH_SUBKEY( pKVRegion, pKVSharedRegionName ) |
|
{ |
|
vecNames.AddToTail( pKVSharedRegionName->GetName() ); |
|
} |
|
} |
|
// We have a standard name -- this one entry is its own equip region. |
|
else |
|
{ |
|
vecNames.AddToTail( pRegionKeyName ); |
|
} |
|
|
|
// What bit will this equip region use to mask against conflicts? If we don't have any equip regions |
|
// at all, we'll use the base bit, otherwise we just grab one higher than whatever we used last. |
|
unsigned int unNewBitIndex = m_vecEquipRegionsList.Count() <= 0 ? 0 : m_vecEquipRegionsList.Tail().m_unBitIndex + 1; |
|
|
|
FOR_EACH_VEC( vecNames, i ) |
|
{ |
|
const char *pRegionName = vecNames[i]; |
|
|
|
// Make sure this name is unique. |
|
if ( GetEquipRegionIndexByName( pRegionName ) >= 0 ) |
|
{ |
|
pVecErrors->AddToTail( CFmtStr( "Duplicate equip region named \"%s\".", pRegionName ).Access() ); |
|
continue; |
|
} |
|
|
|
// Make a new region. |
|
EquipRegion newEquipRegion; |
|
newEquipRegion.m_sName = pRegionName; |
|
newEquipRegion.m_unMask = 0; // we'll update this mask later |
|
newEquipRegion.m_unBitIndex = unNewBitIndex; |
|
|
|
int iIdx = m_vecEquipRegionsList.AddToTail( newEquipRegion ); |
|
|
|
// Tag this region to conflict with itself so that if nothing else two items in the same |
|
// region can't equip over each other. |
|
SetEquipRegionConflict( iIdx, unNewBitIndex ); |
|
} |
|
} |
|
|
|
return SCHEMA_INIT_SUCCESS(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
bool CEconItemSchema::BInitEquipRegionConflicts( KeyValues *pKVEquipRegionConflicts, CUtlVector<CUtlString> *pVecErrors ) |
|
{ |
|
FOR_EACH_TRUE_SUBKEY( pKVEquipRegionConflicts, pKVConflict ) |
|
{ |
|
// What region is the base of this conflict? |
|
const char *pRegionName = pKVConflict->GetName(); |
|
int iRegionIdx = GetEquipRegionIndexByName( pRegionName ); |
|
if ( iRegionIdx < 0 ) |
|
{ |
|
pVecErrors->AddToTail( CFmtStr( "Unable to find base equip region named \"%s\" for conflicts.", pRegionName ).Access() ); |
|
continue; |
|
} |
|
|
|
FOR_EACH_SUBKEY( pKVConflict, pKVConflictOther ) |
|
{ |
|
const char *pOtherRegionName = pKVConflictOther->GetName(); |
|
int iOtherRegionIdx = GetEquipRegionIndexByName( pOtherRegionName ); |
|
if ( iOtherRegionIdx < 0 ) |
|
{ |
|
pVecErrors->AddToTail( CFmtStr( "Unable to find other equip region named \"%s\" for conflicts.", pOtherRegionName ).Access() ); |
|
continue; |
|
} |
|
|
|
SetEquipRegionConflict( iRegionIdx, m_vecEquipRegionsList[iOtherRegionIdx].m_unBitIndex ); |
|
SetEquipRegionConflict( iOtherRegionIdx, m_vecEquipRegionsList[iRegionIdx].m_unBitIndex ); |
|
} |
|
} |
|
|
|
return SCHEMA_INIT_SUCCESS(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Initializes the attributes section of the schema |
|
// Input: pKVAttributes - The attributes section of the KeyValues |
|
// representation of the schema |
|
// pVecErrors - An optional vector that will contain error messages if |
|
// the init fails. |
|
// Output: True if initialization succeeded, false otherwise |
|
//----------------------------------------------------------------------------- |
|
bool CEconItemSchema::BInitAttributes( KeyValues *pKVAttributes, CUtlVector<CUtlString> *pVecErrors ) |
|
{ |
|
// Initialize the attribute definitions |
|
FOR_EACH_TRUE_SUBKEY( pKVAttributes, pKVAttribute ) |
|
{ |
|
int nAttrIndex = Q_atoi( pKVAttribute->GetName() ); |
|
int nMapIndex = m_mapAttributes.Find( nAttrIndex ); |
|
|
|
// Make sure the index is positive |
|
SCHEMA_INIT_CHECK( |
|
nAttrIndex >= 0, |
|
"Attribute definition index %d must be greater than or equal to zero", nAttrIndex ); |
|
|
|
// Make sure the attribute index is not repeated |
|
SCHEMA_INIT_CHECK( |
|
!m_mapAttributes.IsValidIndex( nMapIndex ), |
|
"Duplicate attribute definition index (%d)", nAttrIndex ); |
|
|
|
nMapIndex = m_mapAttributes.Insert( nAttrIndex ); |
|
|
|
SCHEMA_INIT_SUBSTEP( m_mapAttributes[nMapIndex].BInitFromKV( pKVAttribute, pVecErrors ) ); |
|
} |
|
|
|
// Check the integrity of the attribute definitions |
|
|
|
// Check for duplicate attribute definition names |
|
CUtlRBTree<const char *> rbAttributeNames( CaselessStringLessThan ); |
|
rbAttributeNames.EnsureCapacity( m_mapAttributes.Count() ); |
|
FOR_EACH_MAP_FAST( m_mapAttributes, i ) |
|
{ |
|
int iIndex = rbAttributeNames.Find( m_mapAttributes[i].GetDefinitionName() ); |
|
SCHEMA_INIT_CHECK( |
|
!rbAttributeNames.IsValidIndex( iIndex ), |
|
"Attribute definition %d: Duplicate name \"%s\"", m_mapAttributes.Key( i ), m_mapAttributes[i].GetDefinitionName() ); |
|
if( !rbAttributeNames.IsValidIndex( iIndex ) ) |
|
rbAttributeNames.Insert( m_mapAttributes[i].GetDefinitionName() ); |
|
} |
|
|
|
return SCHEMA_INIT_SUCCESS(); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Initializes the items section of the schema |
|
// Input: pKVItems - The items section of the KeyValues |
|
// representation of the schema |
|
// pVecErrors - An optional vector that will contain error messages if |
|
// the init fails. |
|
// Output: True if initialization succeeded, false otherwise |
|
//----------------------------------------------------------------------------- |
|
bool CEconItemSchema::BInitItems( KeyValues *pKVItems, CUtlVector<CUtlString> *pVecErrors ) |
|
{ |
|
m_mapItems.PurgeAndDeleteElements(); |
|
m_mapItemsSorted.Purge(); |
|
m_mapToolsItems.Purge(); |
|
m_mapBaseItems.Purge(); |
|
m_vecBundles.Purge(); |
|
m_mapQuestObjectives.PurgeAndDeleteElements(); |
|
m_vecItemCollectionCrates.Purge(); |
|
|
|
#if defined(CLIENT_DLL) || defined(GAME_DLL) |
|
if ( m_pDefaultItemDefinition ) |
|
{ |
|
delete m_pDefaultItemDefinition; |
|
m_pDefaultItemDefinition = NULL; |
|
} |
|
#endif |
|
|
|
// initialize the item definitions |
|
if ( NULL != pKVItems ) |
|
{ |
|
FOR_EACH_TRUE_SUBKEY( pKVItems, pKVItem ) |
|
{ |
|
if ( Q_stricmp( pKVItem->GetName(), "default" ) == 0 ) |
|
{ |
|
#if defined(CLIENT_DLL) || defined(GAME_DLL) |
|
SCHEMA_INIT_CHECK( |
|
m_pDefaultItemDefinition == NULL, |
|
"Duplicate 'default' item definition." ); |
|
|
|
m_pDefaultItemDefinition = CreateEconItemDefinition(); |
|
SCHEMA_INIT_SUBSTEP( m_pDefaultItemDefinition->BInitFromKV( pKVItem, pVecErrors ) ); |
|
#endif |
|
} |
|
else |
|
{ |
|
int nItemIndex = Q_atoi( pKVItem->GetName() ); |
|
int nMapIndex = m_mapItems.Find( nItemIndex ); |
|
|
|
// Make sure the item index is correct because we use this index as a reference |
|
SCHEMA_INIT_CHECK( |
|
!m_mapItems.IsValidIndex( nMapIndex ), |
|
"Duplicate item definition (%d)", nItemIndex ); |
|
|
|
// Check to make sure the index is positive |
|
SCHEMA_INIT_CHECK( |
|
nItemIndex >= 0, |
|
"Item definition index %d must be greater than or equal to zero", nItemIndex ); |
|
|
|
CEconItemDefinition *pItemDef = CreateEconItemDefinition(); |
|
nMapIndex = m_mapItems.Insert( nItemIndex, pItemDef ); |
|
m_mapItemsSorted.Insert( nItemIndex, pItemDef ); |
|
SCHEMA_INIT_SUBSTEP( m_mapItems[nMapIndex]->BInitFromKV( pKVItem, pVecErrors ) ); |
|
|
|
// Cache off Tools references |
|
if ( pItemDef->IsTool() ) |
|
{ |
|
m_mapToolsItems.Insert( nItemIndex, pItemDef ); |
|
} |
|
|
|
if ( pItemDef->IsBaseItem() ) |
|
{ |
|
m_mapBaseItems.Insert( nItemIndex, pItemDef ); |
|
} |
|
|
|
// Cache off bundles for the link phase below. |
|
if ( pItemDef->IsBundle() ) |
|
{ |
|
// Cache off the item def for the bundle, since we'll need both the bundle info and the item def index later. |
|
m_vecBundles.AddToTail( pItemDef ); |
|
|
|
// If the bundle is a pack bundle, mark all the contained items as pack items / link to the owning pack bundle |
|
if ( pItemDef->IsPackBundle() ) |
|
{ |
|
const bundleinfo_t *pBundleInfo = pItemDef->GetBundleInfo(); |
|
FOR_EACH_VEC( pBundleInfo->vecItemDefs, iCurItem ) |
|
{ |
|
CEconItemDefinition *pCurItemDef = pBundleInfo->vecItemDefs[ iCurItem ]; |
|
SCHEMA_INIT_CHECK( NULL == pCurItemDef->m_pOwningPackBundle, "Pack item \"%s\" included in more than one pack bundle - not allowed!", pCurItemDef->GetDefinitionName() ); |
|
pCurItemDef->m_pOwningPackBundle = pItemDef; |
|
} |
|
} |
|
} |
|
|
|
static CSchemaAttributeDefHandle pAttrDef_ContainsCollection( "contains collection" ); |
|
if ( pAttrDef_ContainsCollection ) |
|
{ |
|
FOR_EACH_VEC( pItemDef->GetStaticAttributes(), i ) |
|
{ |
|
const static_attrib_t& staticAttrib = pItemDef->GetStaticAttributes()[i]; |
|
if ( staticAttrib.iDefIndex == pAttrDef_ContainsCollection->GetDefinitionIndex() ) |
|
{ |
|
// Add to collection crate list |
|
m_vecItemCollectionCrates.AddToTail( pItemDef->GetDefinitionIndex() ); |
|
} |
|
} |
|
} |
|
} |
|
} |
|
} |
|
|
|
// Check the integrity of the item definitions |
|
CUtlRBTree<const char *> rbItemNames( CaselessStringLessThan ); |
|
rbItemNames.EnsureCapacity( m_mapItems.Count() ); |
|
FOR_EACH_MAP_FAST( m_mapItems, i ) |
|
{ |
|
CEconItemDefinition *pItemDef = m_mapItems[ i ]; |
|
|
|
// Check for duplicate item definition names |
|
int iIndex = rbItemNames.Find( pItemDef->GetDefinitionName() ); |
|
SCHEMA_INIT_CHECK( |
|
!rbItemNames.IsValidIndex( iIndex ), |
|
"Item definition %s: Duplicate name on index %d", pItemDef->GetDefinitionName(), m_mapItems.Key( i ) ); |
|
if( !rbItemNames.IsValidIndex( iIndex ) ) |
|
rbItemNames.Insert( m_mapItems[i]->GetDefinitionName() ); |
|
|
|
// Link up armory and store mappings for the item |
|
SCHEMA_INIT_SUBSTEP( pItemDef->BInitItemMappings( pVecErrors ) ); |
|
} |
|
|
|
#ifdef DOTA |
|
// Go through all regular (ie non-pack) bundles and ensure that if any pack items are included, *all* pack items in the owning pack bundle are included |
|
FOR_EACH_VEC( m_vecBundles, iBundle ) |
|
{ |
|
const CEconItemDefinition *pBundleItemDef = m_vecBundles[ iBundle ]; |
|
if ( pBundleItemDef->IsPackBundle() ) |
|
continue; |
|
|
|
// Go through all items in the bundle and look for pack items |
|
const bundleinfo_t *pBundle = pBundleItemDef->GetBundleInfo(); |
|
if ( pBundle ) |
|
{ |
|
FOR_EACH_VEC( pBundle->vecItemDefs, iContainedBundleItem ) |
|
{ |
|
// Get the associated pack bundle |
|
const CEconItemDefinition *pContainedBundleItemDef = pBundle->vecItemDefs[ iContainedBundleItem ]; |
|
|
|
// Ignore non-pack items |
|
if ( !pContainedBundleItemDef || !pContainedBundleItemDef->IsPackItem() ) |
|
continue; |
|
|
|
// Get the pack bundle that contains this particular pack item |
|
const CEconItemDefinition *pOwningPackBundleItemDef = pContainedBundleItemDef->GetOwningPackBundle(); |
|
|
|
// Make sure all items in the owning pack bundle are in pBundleItemDef's bundle info (pBundle) |
|
const bundleinfo_t *pOwningPackBundle = pOwningPackBundleItemDef->GetBundleInfo(); |
|
FOR_EACH_VEC( pOwningPackBundle->vecItemDefs, iCurPackBundleItem ) |
|
{ |
|
CEconItemDefinition *pCurPackBundleItem = pOwningPackBundle->vecItemDefs[ iCurPackBundleItem ]; |
|
if ( !pBundle->vecItemDefs.HasElement( pCurPackBundleItem ) ) |
|
{ |
|
SCHEMA_INIT_CHECK( |
|
false, |
|
"The bundle \"%s\" contains some, but not all pack items required specified by pack bundle \"%s.\"", |
|
pBundleItemDef->GetDefinitionName(), |
|
pOwningPackBundleItemDef->GetDefinitionName() |
|
); |
|
} |
|
} |
|
} |
|
} |
|
} |
|
#endif |
|
|
|
return SCHEMA_INIT_SUCCESS(); |
|
} |
|
|
|
#if 0 // Compiled out until some DotA changes from the item editor are brought over |
|
//----------------------------------------------------------------------------- |
|
// Purpose: Delete an item definition. Moderately dangerous as cached references will become bad. |
|
// Intended for use by the item editor. |
|
//----------------------------------------------------------------------------- |
|
bool CEconItemSchema::DeleteItemDefinition( int iDefIndex ) |
|
{ |
|
m_mapItemsSorted.Remove( iDefIndex ); |
|
|
|
int nMapIndex = m_mapItems.Find( iDefIndex ); |
|
if ( m_mapItems.IsValidIndex( nMapIndex ) ) |
|
{ |
|
CEconItemDefinition* pItemDef = m_mapItems[nMapIndex]; |
|
if ( pItemDef ) |
|
{ |
|
m_mapItems.RemoveAt( nMapIndex ); |
|
delete pItemDef; |
|
return true; |
|
} |
|
} |
|
return false; |
|
} |
|
#endif |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Parses the Item Sets section. |
|
//----------------------------------------------------------------------------- |
|
bool CEconItemSchema::BInitItemSets( KeyValues *pKVItemSets, CUtlVector<CUtlString> *pVecErrors ) |
|
{ |
|
m_mapItemSets.RemoveAll(); |
|
|
|
if ( NULL != pKVItemSets ) |
|
{ |
|
FOR_EACH_TRUE_SUBKEY( pKVItemSets, pKVItemSet ) |
|
{ |
|
const char* setName = pKVItemSet->GetName(); |
|
|
|
SCHEMA_INIT_CHECK( setName != NULL, "All itemsets must have names." ); |
|
SCHEMA_INIT_CHECK( m_mapItemSets.Find( setName ) == m_mapItemSets.InvalidIndex(), "Duplicate itemset name (%s) found!", setName ); |
|
|
|
int idx = m_mapItemSets.Insert( setName, new CEconItemSetDefinition ); |
|
SCHEMA_INIT_SUBSTEP( m_mapItemSets[idx]->BInitFromKV( pKVItemSet, pVecErrors ) ); |
|
} |
|
|
|
// Once we've initialized all of our item sets, loop through all of our item definitions looking |
|
// for pseudo set items. For example, the Festive Holy Mackerel is a different item definition from |
|
// the regular Holy Mackerel, but for set completion and set listing purposes, we want it to show |
|
// as part of the base set. |
|
FOR_EACH_MAP_FAST( m_mapItems, i ) |
|
{ |
|
CEconItemDefinition *pItemDef = m_mapItems[i]; |
|
Assert( pItemDef ); |
|
|
|
// Items that point to themselves are the base set items and got initialized as part of the |
|
// set initialization above. |
|
if ( pItemDef->GetSetItemRemap() == pItemDef->GetDefinitionIndex() ) |
|
continue; |
|
|
|
// Which item are we stealing set information from? |
|
const CEconItemDefinition *pRemappedSetItemDef = GetItemDefinition( pItemDef->GetSetItemRemap() ); |
|
AssertMsg( pRemappedSetItemDef, "Somehow got through item and set initialization but have a broken set remap item!" ); |
|
|
|
pItemDef->SetItemSetDefinition( pRemappedSetItemDef->GetItemSetDefinition() ); |
|
} |
|
} |
|
|
|
return SCHEMA_INIT_SUCCESS(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
bool CEconItemSchema::BVerifyBaseItemNames( CUtlVector<CUtlString> *pVecErrors ) |
|
{ |
|
FOR_EACH_MAP_FAST( m_mapItems, i ) |
|
{ |
|
CEconItemDefinition *pItemDef = m_mapItems[i]; |
|
|
|
// get base item name |
|
const char* pBaseName = pItemDef->GetBaseFunctionalItemName(); |
|
|
|
if ( !pBaseName || pBaseName[0] == '\0' ) |
|
{ |
|
continue; |
|
} |
|
|
|
// look up base item name |
|
SCHEMA_INIT_CHECK( GetItemDefinitionByName( pBaseName ) != NULL, "Base item name not found %s.", pBaseName ); |
|
} |
|
|
|
return SCHEMA_INIT_SUCCESS(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
bool CEconItemSchema::BInitItemCollections( KeyValues *pKVItemCollections, CUtlVector<CUtlString> *pVecErrors ) |
|
{ |
|
m_mapItemCollections.Purge(); |
|
|
|
if ( NULL != pKVItemCollections ) |
|
{ |
|
FOR_EACH_TRUE_SUBKEY( pKVItemCollections, pKVItemCollection ) |
|
{ |
|
const char* setName = pKVItemCollection->GetName(); |
|
|
|
SCHEMA_INIT_CHECK( setName != NULL, "All item collections must have names." ); |
|
SCHEMA_INIT_CHECK( m_mapItemCollections.Find( setName ) == m_mapItemCollections.InvalidIndex(), "Duplicate item collection name (%s) found!", setName ); |
|
|
|
int idx = m_mapItemCollections.Insert( setName, new CEconItemCollectionDefinition ); |
|
SCHEMA_INIT_SUBSTEP( m_mapItemCollections[idx]->BInitFromKV( pKVItemCollection, pVecErrors ) ); |
|
} |
|
} |
|
|
|
return SCHEMA_INIT_SUCCESS(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
bool CEconItemSchema::BInitCollectionReferences( CUtlVector<CUtlString> *pVecErrors ) |
|
{ |
|
FOR_EACH_MAP_FAST( m_mapItems, i ) |
|
{ |
|
CEconItemDefinition *pItemDef = m_mapItems[i]; |
|
const char *pszCollectionName = pItemDef->GetCollectionReference(); |
|
if ( pszCollectionName ) |
|
{ |
|
// Find the collection |
|
bool bFound = false; |
|
FOR_EACH_MAP_FAST( m_mapItemCollections, iCollectionIndex ) |
|
{ |
|
const char * pszTemp = m_mapItemCollections[iCollectionIndex]->m_pszName; |
|
|
|
if ( !V_strcmp( pszTemp, pszCollectionName) ) |
|
{ |
|
bFound = true; |
|
pItemDef->SetItemCollectionDefinition( m_mapItemCollections[iCollectionIndex] ); |
|
break; |
|
} |
|
} |
|
SCHEMA_INIT_CHECK( bFound == true, "Collection %s referenced by item %s not found", pszCollectionName, pItemDef->GetDefinitionName() ); |
|
} |
|
} |
|
|
|
return SCHEMA_INIT_SUCCESS(); |
|
} |
|
//----------------------------------------------------------------------------- |
|
const CEconItemCollectionDefinition *CEconItemSchema::GetCollectionByName( const char* pCollectionName ) |
|
{ |
|
if ( !pCollectionName ) |
|
return NULL; |
|
|
|
FOR_EACH_MAP_FAST( m_mapItemCollections, iCollectionIndex ) |
|
{ |
|
const char * pszTemp = m_mapItemCollections[iCollectionIndex]->m_pszName; |
|
if ( !V_strcmp( pszTemp, pCollectionName ) ) |
|
{ |
|
return m_mapItemCollections[iCollectionIndex]; |
|
} |
|
} |
|
return NULL; |
|
} |
|
|
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
bool CEconItemSchema::BInitItemPaintKitDefinitions( KeyValues *pKVItemPaintKits, CUtlVector<CUtlString> *pVecErrors ) |
|
{ |
|
m_mapItemPaintKits.Purge(); |
|
|
|
const char* cWhitespace = " \r\n\t"; // space, end of line, tab. |
|
cWhitespace; // Compiler happiness for GC build |
|
|
|
if ( NULL != pKVItemPaintKits ) |
|
{ |
|
#ifdef CLIENT_DLL |
|
FOR_EACH_TRUE_SUBKEY( pKVItemPaintKits, pKVPaintKit ) |
|
{ |
|
const char* keyField = pKVPaintKit->GetName(); |
|
SCHEMA_INIT_CHECK( keyField != NULL, "All item collections must have names." ); |
|
|
|
if ( V_stristr( keyField, "paintkit_template" ) != NULL ) |
|
{ |
|
static const int cSkipLen = strlen( "paintkit_template" ); |
|
keyField += cSkipLen; |
|
keyField += strspn( keyField, cWhitespace ); |
|
|
|
bool createTmplResult = materials->AddTextureCompositorTemplate( keyField, pKVPaintKit ); |
|
SCHEMA_INIT_CHECK( createTmplResult, "Could Not Create paintkit_template '%s'", keyField ); |
|
} |
|
} |
|
|
|
// Do post-load validation before moving on to paintkits. |
|
SCHEMA_INIT_CHECK( materials->VerifyTextureCompositorTemplates(), "Paintkit template post-init validation failed." ); |
|
#endif |
|
|
|
// Now do all the paintkits |
|
FOR_EACH_TRUE_SUBKEY( pKVItemPaintKits, pKVPaintKit ) |
|
{ |
|
// We know the keyField is valid, it was checked above. |
|
const char* keyField = pKVPaintKit->GetName(); |
|
|
|
if ( V_stristr( keyField, "paintkit_template" ) == NULL ) |
|
{ |
|
SCHEMA_INIT_CHECK( m_mapItemPaintKits.Find( keyField ) == m_mapItemPaintKits.InvalidIndex(), "Duplicate paint kit definition name (%s) found!", keyField ); |
|
|
|
int idx = m_mapItemPaintKits.Insert( keyField, new CEconItemPaintKitDefinition ); |
|
SCHEMA_INIT_SUBSTEP( m_mapItemPaintKits[ idx ]->BInitFromKV( pKVPaintKit, pVecErrors ) ); |
|
} |
|
} |
|
} |
|
|
|
|
|
return SCHEMA_INIT_SUCCESS(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
bool CEconItemSchema::BInitOperationDefinitions( KeyValues *pKVGameInfo, KeyValues *pKVOperationDefinitions, CUtlVector<CUtlString> *pVecErrors ) |
|
{ |
|
m_mapOperationDefinitions.Purge(); |
|
|
|
if ( NULL != pKVOperationDefinitions ) |
|
{ |
|
FOR_EACH_TRUE_SUBKEY( pKVOperationDefinitions, pKVOperation ) |
|
{ |
|
const char* setName = pKVOperation->GetName(); |
|
SCHEMA_INIT_CHECK( setName != NULL, "All operations must have names." ); |
|
SCHEMA_INIT_CHECK( m_mapOperationDefinitions.Find( setName ) == m_mapOperationDefinitions.InvalidIndex(), "Duplicate operation definition name (%s) found!", setName ); |
|
|
|
CEconOperationDefinition *pNewOperation = new CEconOperationDefinition(); |
|
SCHEMA_INIT_SUBSTEP( pNewOperation->BInitFromKV( pKVOperation, pVecErrors ) ); |
|
|
|
// don't add expired operation to list |
|
if ( pNewOperation->IsExpired() ) |
|
{ |
|
delete pNewOperation; |
|
continue; |
|
} |
|
|
|
m_mapOperationDefinitions.Insert( setName, pNewOperation ); |
|
} |
|
} |
|
|
|
return SCHEMA_INIT_SUCCESS(); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Initializes the timed rewards section of the schema |
|
// Input: pKVTimedRewards - The timed_rewards section of the KeyValues |
|
// representation of the schema |
|
// pVecErrors - An optional vector that will contain error messages if |
|
// the init fails. |
|
// Output: True if initialization succeeded, false otherwise |
|
//----------------------------------------------------------------------------- |
|
bool CEconItemSchema::BInitTimedRewards( KeyValues *pKVTimedRewards, CUtlVector<CUtlString> *pVecErrors ) |
|
{ |
|
m_vecTimedRewards.RemoveAll(); |
|
|
|
// initialize the rewards sections |
|
if ( NULL != pKVTimedRewards ) |
|
{ |
|
FOR_EACH_TRUE_SUBKEY( pKVTimedRewards, pKVTimedReward ) |
|
{ |
|
int index = m_vecTimedRewards.AddToTail(); |
|
SCHEMA_INIT_SUBSTEP( m_vecTimedRewards[index].BInitFromKV( pKVTimedReward, pVecErrors ) ); |
|
} |
|
} |
|
|
|
return SCHEMA_INIT_SUCCESS(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
const CTimedItemRewardDefinition* CEconItemSchema::GetTimedReward( eTimedRewardType type ) const |
|
{ |
|
if ( (int)type < m_vecTimedRewards.Count() ) |
|
{ |
|
return &m_vecTimedRewards[type]; |
|
} |
|
return NULL; |
|
} |
|
|
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Initializes the loot lists section of the schema |
|
//----------------------------------------------------------------------------- |
|
bool CEconItemSchema::BInitLootLists( KeyValues *pKVLootLists, CUtlVector<CUtlString> *pVecErrors ) |
|
{ |
|
if ( NULL != pKVLootLists ) |
|
{ |
|
FOR_EACH_TRUE_SUBKEY( pKVLootLists, pKVLootList ) |
|
{ |
|
const char* pListName = pKVLootList->GetName(); |
|
SCHEMA_INIT_SUBSTEP( BInsertLootlist( pListName, pKVLootList, pVecErrors ) ); |
|
} |
|
} |
|
|
|
FOR_EACH_MAP_FAST( m_mapLootLists, i ) |
|
{ |
|
const CEconLootListDefinition *pLootList = m_mapLootLists[i]; |
|
BVerifyLootListItemDropDates( pLootList, pVecErrors ); |
|
} |
|
|
|
return SCHEMA_INIT_SUCCESS(); |
|
} |
|
//----------------------------------------------------------------------------- |
|
bool CEconItemSchema::BInsertLootlist( const char *pListName, KeyValues *pKVLootList, CUtlVector<CUtlString> *pVecErrors ) |
|
{ |
|
SCHEMA_INIT_CHECK( pListName != NULL, "All lootlists must have names." ); |
|
|
|
if ( m_mapLootLists.Count() > 0 ) |
|
{ |
|
SCHEMA_INIT_CHECK( GetLootListByName( pListName ) == NULL, "Duplicate lootlist name (%s) found!", pListName ); |
|
} |
|
|
|
CEconLootListDefinition *pLootList = new CEconLootListDefinition; |
|
SCHEMA_INIT_SUBSTEP( pLootList->BInitFromKV( pKVLootList, *this, pVecErrors ) ); |
|
m_mapLootLists.Insert( pListName, pLootList ); |
|
|
|
return SCHEMA_INIT_SUCCESS(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Initializes the revolving loot lists section of the schema |
|
//----------------------------------------------------------------------------- |
|
bool CEconItemSchema::BInitRevolvingLootLists( KeyValues *pKVLootLists, CUtlVector<CUtlString> *pVecErrors ) |
|
{ |
|
m_mapRevolvingLootLists.RemoveAll(); |
|
|
|
if ( NULL != pKVLootLists ) |
|
{ |
|
FOR_EACH_SUBKEY( pKVLootLists, pKVList ) |
|
{ |
|
int iListIdx = pKVList->GetInt(); |
|
const char* strListName = pKVList->GetName(); |
|
m_mapRevolvingLootLists.Insert( iListIdx, strListName ); |
|
} |
|
} |
|
|
|
FOR_EACH_MAP_FAST( m_mapRevolvingLootLists, i ) |
|
{ |
|
const CEconLootListDefinition* pLootList = GetLootListByName(m_mapRevolvingLootLists[i]); |
|
BVerifyLootListItemDropDates( pLootList, pVecErrors ); |
|
} |
|
|
|
return SCHEMA_INIT_SUCCESS(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Create and return a new quest objective definition. Verify that |
|
// a definition with the same name doesnt alreay exist. |
|
//----------------------------------------------------------------------------- |
|
bool CEconItemSchema::AddQuestObjective( const CQuestObjectiveDefinition **ppQuestObjective, KeyValues *pKVObjective, CUtlVector<CUtlString> *pVecErrors ) |
|
{ |
|
// These need to be unique |
|
int nDefIndex = pKVObjective->GetInt( "defindex", -1 ); |
|
SCHEMA_INIT_CHECK( nDefIndex != -1, "Missing defindex for quest objective" ); |
|
// Verify defindex is unique |
|
auto nMapIndex = m_mapQuestObjectives.Find( nDefIndex ); |
|
SCHEMA_INIT_CHECK( nMapIndex == m_mapQuestObjectives.InvalidIndex(), "Multiple quest objectives with defindex: %d", nDefIndex ); |
|
// Create the quest def |
|
nMapIndex = m_mapQuestObjectives.Insert( nDefIndex ); |
|
m_mapQuestObjectives[ nMapIndex ] = CreateQuestDefinition(); |
|
// Init |
|
SCHEMA_INIT_SUBSTEP( m_mapQuestObjectives[nMapIndex]->BInitFromKV( pKVObjective, pVecErrors ) ); |
|
|
|
if ( ppQuestObjective ) |
|
{ |
|
(*ppQuestObjective) = m_mapQuestObjectives[nMapIndex]; |
|
} |
|
|
|
return SCHEMA_INIT_SUCCESS(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Verify that the contents of visible lootlist do not have drop dates |
|
// associated with them. The thinking being that we dont want to have |
|
// items listed that could potentially not drop, or items disappear/appear |
|
// in from a list. |
|
//----------------------------------------------------------------------------- |
|
bool CEconItemSchema::BVerifyLootListItemDropDates( const CEconLootListDefinition* pLootList, CUtlVector<CUtlString> *pVecErrors ) const |
|
{ |
|
if ( pLootList && pLootList->BPublicListContents() ) |
|
{ |
|
BRecurseiveVerifyLootListItemDropDates( pLootList, pLootList, pVecErrors ); |
|
} |
|
|
|
return SCHEMA_INIT_SUCCESS(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Recursively dig through all entries in the passed in lootlist to see |
|
// if any of the containted items have drop dates. |
|
//----------------------------------------------------------------------------- |
|
bool CEconItemSchema::BRecurseiveVerifyLootListItemDropDates( const CEconLootListDefinition* pLootList, const CEconLootListDefinition* pRootLootList, CUtlVector<CUtlString> *pVecErrors ) const |
|
{ |
|
FOR_EACH_VEC( pLootList->GetLootListContents(), j ) |
|
{ |
|
const CEconLootListDefinition::drop_item_t& item = pLootList->GetLootListContents()[j]; |
|
// 0 and greater means item. Less than 0 means nested lootlist |
|
if( item.m_iItemOrLootlistDef >= 0 ) |
|
{ |
|
const CEconItemDefinition* pItemDef = GetItemSchema()->GetItemDefinition( item.m_iItemOrLootlistDef ); |
|
if( pItemDef ) |
|
{ |
|
static CSchemaAttributeDefHandle pAttribDef_StartDropDate( "start drop date" ); |
|
static CSchemaAttributeDefHandle pAttribDef_EndDropDate( "end drop date" ); |
|
|
|
CAttribute_String value; |
|
|
|
// Check for start drop date attribute on this item |
|
SCHEMA_INIT_CHECK( !FindAttribute( pItemDef, pAttribDef_StartDropDate, &value ), |
|
"Lootlist \"%s\" contains lootlist \"%s\", which contains item \"%s\", which has start drop date.", pRootLootList->GetName(), pLootList->GetName(), pItemDef->GetDefinitionName() ); |
|
// Check for end drop date attribute on this item |
|
SCHEMA_INIT_CHECK( !FindAttribute( pItemDef, pAttribDef_EndDropDate, &value ), |
|
"Lootlist \"%s\" contains lootlist \"%s\", which contains item \"%s\", which has end drop date.", pRootLootList->GetName(), pLootList->GetName(), pItemDef->GetDefinitionName() ); |
|
} |
|
} |
|
else |
|
{ |
|
// Get the nested lootlist |
|
int iLLIndex = (item.m_iItemOrLootlistDef * -1) - 1; |
|
const CEconLootListDefinition *pNestedLootList = GetItemSchema()->GetLootListByIndex( iLLIndex ); |
|
if ( !pNestedLootList ) |
|
return SCHEMA_INIT_SUCCESS(); |
|
|
|
// Dig through all of this lootlist's entries |
|
BRecurseiveVerifyLootListItemDropDates( pNestedLootList, pRootLootList, pVecErrors ); |
|
} |
|
} |
|
|
|
return SCHEMA_INIT_SUCCESS(); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Initializes the recipes section of the schema |
|
// Input: pKVRecipes - The recipes section of the KeyValues |
|
// representation of the schema |
|
// pVecErrors - An optional vector that will contain error messages if |
|
// the init fails. |
|
// Output: True if initialization succeeded, false otherwise |
|
//----------------------------------------------------------------------------- |
|
bool CEconItemSchema::BInitRecipes( KeyValues *pKVRecipes, CUtlVector<CUtlString> *pVecErrors ) |
|
{ |
|
m_mapRecipes.RemoveAll(); |
|
|
|
// initialize the rewards sections |
|
if ( NULL != pKVRecipes ) |
|
{ |
|
FOR_EACH_TRUE_SUBKEY( pKVRecipes, pKVRecipe ) |
|
{ |
|
int nRecipeIndex = Q_atoi( pKVRecipe->GetName() ); |
|
int nMapIndex = m_mapRecipes.Find( nRecipeIndex ); |
|
|
|
// Make sure the recipe index is correct because we use this index as a reference |
|
SCHEMA_INIT_CHECK( |
|
!m_mapRecipes.IsValidIndex( nMapIndex ), |
|
"Duplicate recipe definition (%d)", nRecipeIndex ); |
|
|
|
// Check to make sure the index is positive |
|
SCHEMA_INIT_CHECK( |
|
nRecipeIndex >= 0, |
|
"Recipe definition index %d must be greater than or equal to zero", nRecipeIndex ); |
|
|
|
CEconCraftingRecipeDefinition *recipeDef = CreateCraftingRecipeDefinition(); |
|
SCHEMA_INIT_SUBSTEP( recipeDef->BInitFromKV( pKVRecipe, pVecErrors ) ); |
|
|
|
#ifdef _DEBUG |
|
// Sanity check in debug builds so that we know we aren't putting the same recipe in |
|
// multiple times. |
|
FOR_EACH_MAP_FAST( m_mapRecipes, i ) |
|
{ |
|
Assert( i != nRecipeIndex ); |
|
Assert( m_mapRecipes[i] != recipeDef ); |
|
} |
|
#endif // _DEBUG |
|
|
|
// Store this recipe. |
|
m_mapRecipes.Insert( nRecipeIndex, recipeDef ); |
|
} |
|
} |
|
|
|
return SCHEMA_INIT_SUCCESS(); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Builds the name of a achievement in the form App<ID>.<AchName> |
|
// Input: unAppID - native app ID |
|
// pchNativeAchievementName - name of the achievement in its native app |
|
// Returns: The combined achievement name |
|
//----------------------------------------------------------------------------- |
|
CUtlString CEconItemSchema::ComputeAchievementName( AppId_t unAppID, const char *pchNativeAchievementName ) |
|
{ |
|
return CFmtStr1024( "App%u.%s", unAppID, pchNativeAchievementName ).Access(); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Initializes the achievement rewards section of the schema |
|
// Input: pKVAchievementRewards - The achievement_rewards section of the KeyValues |
|
// representation of the schema |
|
// pVecErrors - An optional vector that will contain error messages if |
|
// the init fails. |
|
// Output: True if initialization succeeded, false otherwise |
|
//----------------------------------------------------------------------------- |
|
bool CEconItemSchema::BInitAchievementRewards( KeyValues *pKVAchievementRewards, CUtlVector<CUtlString> *pVecErrors ) |
|
{ |
|
m_dictAchievementRewards.RemoveAll(); |
|
m_mapAchievementRewardsByData.PurgeAndDeleteElements(); |
|
|
|
// initialize the rewards sections |
|
if ( NULL != pKVAchievementRewards ) |
|
{ |
|
FOR_EACH_SUBKEY( pKVAchievementRewards, pKVReward ) |
|
{ |
|
AchievementAward_t award; |
|
if( pKVReward->GetDataType() == KeyValues::TYPE_NONE ) |
|
{ |
|
int32 nItemIndex = pKVReward->GetInt( "DefIndex", -1 ); |
|
if( nItemIndex != -1 ) |
|
{ |
|
award.m_vecDefIndex.AddToTail( (uint16)nItemIndex ); |
|
} |
|
else |
|
{ |
|
KeyValues *pkvItems = pKVReward->FindKey( "Items" ); |
|
SCHEMA_INIT_CHECK( |
|
pkvItems != NULL, |
|
"Complex achievement %s must have an Items key or a DefIndex field", pKVReward->GetName() ); |
|
if( !pkvItems ) |
|
{ |
|
continue; |
|
} |
|
|
|
FOR_EACH_VALUE( pkvItems, pkvItem ) |
|
{ |
|
award.m_vecDefIndex.AddToTail( (uint16)Q_atoi( pkvItem->GetName() ) ); |
|
} |
|
} |
|
|
|
} |
|
else |
|
{ |
|
award.m_vecDefIndex.AddToTail( (uint16)pKVReward->GetInt("", -1 ) ); |
|
} |
|
|
|
// make sure all the item types are valid |
|
bool bFoundAllItems = true; |
|
FOR_EACH_VEC( award.m_vecDefIndex, nItem ) |
|
{ |
|
const CEconItemDefinition *pDefn = GetItemDefinition( award.m_vecDefIndex[nItem] ); |
|
SCHEMA_INIT_CHECK( |
|
pDefn != NULL, |
|
"Item definition index %d in achievement reward %s was not found", award.m_vecDefIndex[nItem], pKVReward->GetName() ); |
|
if( !pDefn ) |
|
{ |
|
bFoundAllItems = false; |
|
} |
|
} |
|
if( !bFoundAllItems ) |
|
continue; |
|
|
|
SCHEMA_INIT_CHECK( |
|
award.m_vecDefIndex.Count() > 0, |
|
"Achievement reward %s has no items!", pKVReward->GetName() ); |
|
if( award.m_vecDefIndex.Count() == 0 ) |
|
continue; |
|
|
|
#ifdef GC_DLL |
|
award.m_unSourceAppId = GGCBase()->GetAppID(); |
|
#else |
|
award.m_unSourceAppId = k_uAppIdInvalid; |
|
#endif |
|
if( pKVReward->GetDataType() == KeyValues::TYPE_NONE ) |
|
{ |
|
// cross game achievement |
|
award.m_sNativeName = pKVReward->GetName(); |
|
award.m_unAuditData = pKVReward->GetInt( "AuditData", 0 ); |
|
award.m_unSourceAppId = pKVReward->GetInt( "SourceAppID", award.m_unSourceAppId ); |
|
} |
|
else |
|
{ |
|
award.m_sNativeName = pKVReward->GetName(); |
|
award.m_unAuditData = 0; |
|
} |
|
|
|
|
|
#ifdef GC_DLL |
|
// Check to make sure the audit data is valid |
|
SCHEMA_INIT_CHECK( |
|
award.m_unSourceAppId >= 0, |
|
"Source App ID %d in achievement reward %s must be valid", award.m_unSourceAppId, pKVReward->GetName() ); |
|
if( award.m_unSourceAppId == k_uAppIdInvalid ) |
|
continue; |
|
|
|
if( !GGCGameBase()->BYieldingLoadStats( award.m_unSourceAppId ) ) |
|
{ |
|
// this will often fail in a dev universe |
|
if( GGCHost()->GetUniverse() != k_EUniverseDev ) |
|
{ |
|
SCHEMA_INIT_CHECK( |
|
false, |
|
"Unable to load stats schema for cross-game achievement %s for app %d", pKVReward->GetName(), award.m_unSourceAppId ); |
|
} |
|
continue; |
|
} |
|
|
|
const CGCStatsSchema *pStatsSchema = GGCGameBase()->GetStatsSchema( award.m_unSourceAppId ); |
|
if( !pStatsSchema ) |
|
{ |
|
SCHEMA_INIT_CHECK( |
|
false, |
|
"Unable to retrieve stats schema for cross-game achievement %s for app %d", pKVReward->GetName(), award.m_unSourceAppId ); |
|
continue; |
|
} |
|
|
|
if( award.m_unAuditData == 0 ) |
|
{ |
|
uint16 usStatID, usBitID; |
|
if( !pStatsSchema->BGetAchievementBit( award.m_sNativeName, &usStatID, &usBitID ) ) |
|
{ |
|
SCHEMA_INIT_CHECK( |
|
false, |
|
"Unable to find achievement %s for app %d", award.m_sNativeName.Get(), award.m_unSourceAppId ); |
|
continue; |
|
} |
|
|
|
award.m_unAuditData = ( usStatID <<16 ) | usBitID; |
|
} |
|
#endif // GC_DLL |
|
|
|
|
|
AchievementAward_t *pAward = new AchievementAward_t; |
|
*pAward = award; |
|
|
|
m_dictAchievementRewards.Insert( ComputeAchievementName( pAward->m_unSourceAppId, pAward->m_sNativeName ), pAward ); |
|
m_mapAchievementRewardsByData.Insert( pAward->m_unAuditData, pAward ); |
|
} |
|
} |
|
|
|
return SCHEMA_INIT_SUCCESS(); |
|
} |
|
|
|
|
|
#ifdef GC_DLL |
|
bool CEconItemSchema::BInitRandomAttributeTemplates( KeyValues *pKVRandomAttributeTemplates, CUtlVector<CUtlString> *pVecErrors ) |
|
{ |
|
m_dictRandomAttributeTemplates.PurgeAndDeleteElements(); |
|
|
|
FOR_EACH_TRUE_SUBKEY( pKVRandomAttributeTemplates, pKVAttributeTemplate ) |
|
{ |
|
const char *pszAttrName = pKVAttributeTemplate->GetName(); |
|
|
|
// try to create random attrib from template |
|
random_attrib_t *pRandomAttr = CreateRandomAttribute( __FUNCTION__, pKVAttributeTemplate, pVecErrors ); |
|
SCHEMA_INIT_CHECK( |
|
NULL != pRandomAttr, |
|
CFmtStr( "%s: Failed to create random_attrib_t '%s'", __FUNCTION__, pszAttrName ) ); |
|
|
|
m_dictRandomAttributeTemplates.Insert( pszAttrName, pRandomAttr ); |
|
} |
|
|
|
return true; |
|
} |
|
#endif // GC_DLL |
|
|
|
|
|
#ifdef TF_CLIENT_DLL |
|
//----------------------------------------------------------------------------- |
|
// Purpose: Go through all items and cache the number of concrete items in each. |
|
//----------------------------------------------------------------------------- |
|
bool CEconItemSchema::BInitConcreteItemCounts( CUtlVector<CUtlString> *pVecErrors ) |
|
{ |
|
FOR_EACH_MAP_FAST( m_mapItems, i ) |
|
{ |
|
CEconItemDefinition *pItemDef = m_mapItems[ i ]; |
|
pItemDef->m_unNumConcreteItems = CalculateNumberOfConcreteItems( pItemDef ); |
|
} |
|
|
|
return true; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Returns the number of actual "real" items referenced by the item definition |
|
// (i.e. items that would take up space in the inventory) |
|
//----------------------------------------------------------------------------- |
|
int CEconItemSchema::CalculateNumberOfConcreteItems( const CEconItemDefinition *pItemDef ) |
|
{ |
|
AssertMsg( pItemDef, "NULL item definition! This should not happen!" ); |
|
if ( !pItemDef ) |
|
return 0; |
|
|
|
if ( pItemDef->IsBundle() ) |
|
{ |
|
uint32 unNumConcreteItems = 0; |
|
|
|
const bundleinfo_t *pBundle = pItemDef->GetBundleInfo(); |
|
Assert( pBundle ); |
|
|
|
FOR_EACH_VEC( pBundle->vecItemDefs, i ) |
|
{ |
|
unNumConcreteItems += CalculateNumberOfConcreteItems( pBundle->vecItemDefs[i] ); |
|
} |
|
|
|
return unNumConcreteItems; |
|
} |
|
|
|
return 1; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
bool CEconItemSchema::BInitSteamPackageLocalizationToken( KeyValues *pKVSteamPackages, CUtlVector<CUtlString> *pVecErrors ) |
|
{ |
|
if ( NULL != pKVSteamPackages ) |
|
{ |
|
FOR_EACH_TRUE_SUBKEY( pKVSteamPackages, pKVEntry ) |
|
{ |
|
// Check to make sure the index is positive |
|
int iRawPackageId = atoi( pKVEntry->GetName() ); |
|
SCHEMA_INIT_CHECK( |
|
iRawPackageId > 0, |
|
"Invalid package ID %i for localization", iRawPackageId ); |
|
|
|
// Store off our data. |
|
uint32 unPackageId = (uint32)iRawPackageId; |
|
const char *pszLocalizationToken = pKVEntry->GetString( "localization_key" ); |
|
|
|
m_mapSteamPackageLocalizationTokens.InsertOrReplace( unPackageId, pszLocalizationToken ); |
|
|
|
} |
|
} |
|
return SCHEMA_INIT_SUCCESS(); |
|
} |
|
#endif // TF_CLIENT_DLL |
|
|
|
static const char *s_particle_controlpoint_names[] = |
|
{ |
|
"attachment", |
|
"control_point_1", |
|
"control_point_2", |
|
"control_point_3", |
|
"control_point_4", |
|
"control_point_5", |
|
"control_point_6", |
|
}; |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Initializes the attribute-controlled-particle-systems section of the schema |
|
//----------------------------------------------------------------------------- |
|
bool CEconItemSchema::BInitAttributeControlledParticleSystems( KeyValues *pKVParticleSystems, CUtlVector<CUtlString> *pVecErrors ) |
|
{ |
|
m_mapAttributeControlledParticleSystems.RemoveAll(); |
|
m_vecAttributeControlledParticleSystemsCosmetics.RemoveAll(); |
|
m_vecAttributeControlledParticleSystemsWeapons.RemoveAll(); |
|
m_vecAttributeControlledParticleSystemsTaunts.RemoveAll(); |
|
|
|
CUtlVector< int > *pVec = NULL; |
|
|
|
// Addictional groups we are tracking for. |
|
// "cosmetic_unusual_effects" |
|
// "weapon_unusual_effects" |
|
// "taunt_unusual_effects" |
|
|
|
if ( NULL != pKVParticleSystems ) |
|
{ |
|
FOR_EACH_TRUE_SUBKEY( pKVParticleSystems, pKVCategory ) |
|
{ |
|
// There is 3 Categories we want to track with additional info |
|
if ( !V_strcmp( pKVCategory->GetName(), "cosmetic_unusual_effects" ) ) |
|
{ |
|
pVec = &m_vecAttributeControlledParticleSystemsCosmetics; |
|
} |
|
else if ( !V_strcmp( pKVCategory->GetName(), "weapon_unusual_effects" ) ) |
|
{ |
|
pVec = &m_vecAttributeControlledParticleSystemsWeapons; |
|
} |
|
else if ( !V_strcmp( pKVCategory->GetName(), "taunt_unusual_effects" ) ) |
|
{ |
|
pVec = &m_vecAttributeControlledParticleSystemsTaunts; |
|
} |
|
else |
|
{ |
|
pVec = NULL; // reset |
|
} |
|
|
|
FOR_EACH_TRUE_SUBKEY( pKVCategory, pKVEntry ) |
|
{ |
|
int32 nItemIndex = atoi( pKVEntry->GetName() ); |
|
// Check to make sure the index is positive |
|
SCHEMA_INIT_CHECK( |
|
nItemIndex > 0, |
|
"Particle system index %d greater than zero", nItemIndex ); |
|
if ( nItemIndex <= 0 ) |
|
continue; |
|
int iIndex = m_mapAttributeControlledParticleSystems.Insert( nItemIndex ); |
|
attachedparticlesystem_t &system = m_mapAttributeControlledParticleSystems[iIndex]; |
|
system.pszSystemName = pKVEntry->GetString( "system", NULL ); |
|
system.bFollowRootBone = pKVEntry->GetInt( "attach_to_rootbone", 0 ) != 0; |
|
system.iCustomType = 0; |
|
system.nSystemID = nItemIndex; |
|
system.fRefireTime = pKVEntry->GetFloat( "refire_time", 0.0f ); |
|
system.bDrawInViewModel = pKVEntry->GetBool( "draw_in_viewmodel", false ); |
|
system.bUseSuffixName = pKVEntry->GetBool( "use_suffix_name", false ); |
|
system.bHasViewModelSpecificEffect = pKVEntry->GetBool( "has_viewmodel_specific_effect", false ); |
|
|
|
COMPILE_TIME_ASSERT( ARRAYSIZE( system.pszControlPoints ) == ARRAYSIZE( s_particle_controlpoint_names ) ); |
|
for ( int i=0; i<ARRAYSIZE( system.pszControlPoints ); ++i ) |
|
{ |
|
system.pszControlPoints[i] = pKVEntry->GetString( s_particle_controlpoint_names[i], NULL ); |
|
} |
|
|
|
if ( pVec ) |
|
{ |
|
pVec->AddToTail( nItemIndex ); |
|
} |
|
} |
|
} |
|
} |
|
return SCHEMA_INIT_SUCCESS(); |
|
} |
|
|
|
#ifdef CLIENT_DLL |
|
locchar_t *CEconItemSchema::GetParticleSystemLocalizedName( int index ) const |
|
{ |
|
const attachedparticlesystem_t *pSystem = GetItemSchema()->GetAttributeControlledParticleSystem( index ); |
|
if ( !pSystem ) |
|
return NULL; |
|
|
|
char particleNameEntry[128]; |
|
Q_snprintf( particleNameEntry, ARRAYSIZE( particleNameEntry ), "#Attrib_Particle%d", pSystem->nSystemID ); |
|
|
|
return g_pVGuiLocalize->Find( particleNameEntry ); |
|
} |
|
|
|
#endif |
|
//----------------------------------------------------------------------------- |
|
// Purpose: Inits data for items that can level up through kills, etc. |
|
//----------------------------------------------------------------------------- |
|
bool CEconItemSchema::BInitItemLevels( KeyValues *pKVItemLevels, CUtlVector<CUtlString> *pVecErrors ) |
|
{ |
|
m_vecItemLevelingData.RemoveAll(); |
|
|
|
// initialize the rewards sections |
|
if ( NULL != pKVItemLevels ) |
|
{ |
|
FOR_EACH_TRUE_SUBKEY( pKVItemLevels, pKVItemLevelBlock ) |
|
{ |
|
const char *pszLevelBlockName = pKVItemLevelBlock->GetName(); |
|
SCHEMA_INIT_CHECK( GetItemLevelingData( pszLevelBlockName ) == NULL, |
|
"Duplicate leveling data block named \"%s\".", pszLevelBlockName ); |
|
|
|
// Allocate a new structure for this block and assign it. We'll fill in the contents later. |
|
CUtlVector<CItemLevelingDefinition> *pLevelingData = new CUtlVector<CItemLevelingDefinition>; |
|
m_vecItemLevelingData.Insert( pszLevelBlockName, pLevelingData ); |
|
|
|
FOR_EACH_TRUE_SUBKEY( pKVItemLevelBlock, pKVItemLevel ) |
|
{ |
|
int index = pLevelingData->AddToTail(); |
|
SCHEMA_INIT_SUBSTEP( (*pLevelingData)[index].BInitFromKV( pKVItemLevel, pszLevelBlockName, pVecErrors ) ); |
|
} |
|
} |
|
} |
|
|
|
return SCHEMA_INIT_SUCCESS(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Inits data for kill eater types. |
|
//----------------------------------------------------------------------------- |
|
bool CEconItemSchema::BInitKillEaterScoreTypes( KeyValues *pKVKillEaterScoreTypes, CUtlVector<CUtlString> *pVecErrors ) |
|
{ |
|
m_mapKillEaterScoreTypes.RemoveAll(); |
|
|
|
// initialize the rewards sections |
|
if ( NULL != pKVKillEaterScoreTypes ) |
|
{ |
|
FOR_EACH_TRUE_SUBKEY( pKVKillEaterScoreTypes, pKVScoreType ) |
|
{ |
|
unsigned int unIndex = (unsigned int)atoi( pKVScoreType->GetName() ); |
|
SCHEMA_INIT_CHECK( m_mapKillEaterScoreTypes.Find( unIndex ) == KillEaterScoreMap_t::InvalidIndex(), |
|
"Duplicate kill eater score type index %u.", unIndex ); |
|
|
|
kill_eater_score_type_t ScoreType; |
|
ScoreType.m_pszTypeString = pKVScoreType->GetString( "type_name" ); |
|
ScoreType.m_bAllowBotVictims = pKVScoreType->GetBool( "allow_bot_victims", false ); |
|
#ifdef GC_DLL |
|
ScoreType.m_bGCUpdateOnly = pKVScoreType->GetBool( "gc_update_only", false ); |
|
ScoreType.m_AllowIncrementValues = pKVScoreType->GetBool( "gc_allow_increment_values", false ); |
|
ScoreType.m_bIsBaseKillType = pKVScoreType->GetBool( "gc_is_base_kill_type", false ); |
|
#endif |
|
|
|
const char *pszLevelBlockName = pKVScoreType->GetString( "level_data", "KillEaterRank" ); |
|
SCHEMA_INIT_CHECK( GetItemLevelingData( pszLevelBlockName ) != NULL, |
|
"Unable to find leveling data block named \"%s\" for kill eater score type %u.", pszLevelBlockName, unIndex ); |
|
|
|
ScoreType.m_pszLevelBlockName = pszLevelBlockName; |
|
|
|
m_mapKillEaterScoreTypes.Insert( unIndex, ScoreType ); |
|
} |
|
} |
|
|
|
return SCHEMA_INIT_SUCCESS(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
bool CEconItemSchema::BInitStringTables( KeyValues *pKVStringTables, CUtlVector<CUtlString> *pVecErrors ) |
|
{ |
|
m_dictStringTable.PurgeAndDeleteElements(); |
|
|
|
// initialize the rewards sections |
|
if ( NULL != pKVStringTables ) |
|
{ |
|
FOR_EACH_SUBKEY( pKVStringTables, pKVTable ) |
|
{ |
|
SCHEMA_INIT_CHECK( !m_dictStringTable.IsValidIndex( m_dictStringTable.Find( pKVTable->GetName() ) ), |
|
"Duplicate string table name '%s'.", pKVTable->GetName() ); |
|
|
|
SchemaStringTableDict_t::IndexType_t i = m_dictStringTable.Insert( pKVTable->GetName(), new CUtlVector< schema_string_table_entry_t > ); |
|
FOR_EACH_SUBKEY( pKVTable, pKVEntry ) |
|
{ |
|
schema_string_table_entry_t s = { atoi( pKVEntry->GetName() ), pKVEntry->GetString() }; |
|
m_dictStringTable[i]->AddToTail( s ); |
|
} |
|
} |
|
} |
|
|
|
return SCHEMA_INIT_SUCCESS(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
bool CEconItemSchema::BInitCommunityMarketRemaps( KeyValues *pKVCommunityMarketRemaps, CUtlVector<CUtlString> *pVecErrors ) |
|
{ |
|
m_mapCommunityMarketDefinitionIndexRemap.Purge(); |
|
|
|
if ( NULL != pKVCommunityMarketRemaps ) |
|
{ |
|
FOR_EACH_SUBKEY( pKVCommunityMarketRemaps, pKVRemapBase ) |
|
{ |
|
const char *pszBaseDefName = pKVRemapBase->GetName(); |
|
const CEconItemDefinition *pBaseItemDef = GetItemSchema()->GetItemDefinitionByName( pszBaseDefName ); |
|
SCHEMA_INIT_CHECK( pBaseItemDef != NULL, "Unknown Market remap base definition '%s'.", pszBaseDefName ); |
|
|
|
FOR_EACH_SUBKEY( pKVRemapBase, pKVRemap ) |
|
{ |
|
const char *pszDefName = pKVRemap->GetName(); |
|
const CEconItemDefinition *pItemDef = GetItemSchema()->GetItemDefinitionByName( pszDefName ); |
|
SCHEMA_INIT_CHECK( pItemDef != NULL, "Unknown Market remap definition '%s' (under '%s').", pszDefName, pszBaseDefName ); |
|
SCHEMA_INIT_CHECK( m_mapCommunityMarketDefinitionIndexRemap.Find( pItemDef->GetDefinitionIndex() ) == m_mapCommunityMarketDefinitionIndexRemap.InvalidIndex(), "Duplicate Market remap definition '%s'.\n", pszDefName ); |
|
|
|
m_mapCommunityMarketDefinitionIndexRemap.Insert( pItemDef->GetDefinitionIndex(), pBaseItemDef->GetDefinitionIndex() ); |
|
} |
|
} |
|
} |
|
|
|
return SCHEMA_INIT_SUCCESS(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
item_definition_index_t CEconItemSchema::GetCommunityMarketRemappedDefinitionIndex( item_definition_index_t unSearchItemDef ) const |
|
{ |
|
CommunityMarketDefinitionRemapMap_t::IndexType_t index = m_mapCommunityMarketDefinitionIndexRemap.Find( unSearchItemDef ); |
|
if ( index == m_mapCommunityMarketDefinitionIndexRemap.InvalidIndex() ) |
|
return unSearchItemDef; |
|
|
|
return m_mapCommunityMarketDefinitionIndexRemap[index]; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
const ISchemaAttributeType *CEconItemSchema::GetAttributeType( const char *pszAttrTypeName ) const |
|
{ |
|
FOR_EACH_VEC( m_vecAttributeTypes, i ) |
|
{ |
|
if ( m_vecAttributeTypes[i].m_sName == pszAttrTypeName ) |
|
return m_vecAttributeTypes[i].m_pAttrType; |
|
} |
|
|
|
return NULL; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// CItemLevelingDefinition Accessor |
|
//----------------------------------------------------------------------------- |
|
const CItemLevelingDefinition *CEconItemSchema::GetItemLevelForScore( const char *pszLevelBlockName, uint32 unScore ) const |
|
{ |
|
const CUtlVector<CItemLevelingDefinition> *pLevelingData = GetItemLevelingData( pszLevelBlockName ); |
|
if ( !pLevelingData ) |
|
return NULL; |
|
|
|
if ( pLevelingData->Count() == 0 ) |
|
return NULL; |
|
|
|
FOR_EACH_VEC( (*pLevelingData), i ) |
|
{ |
|
if ( unScore < (*pLevelingData)[i].GetRequiredScore() ) |
|
return &(*pLevelingData)[i]; |
|
} |
|
|
|
return &(*pLevelingData).Tail(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Kill eater score type accessor |
|
//----------------------------------------------------------------------------- |
|
const kill_eater_score_type_t *CEconItemSchema::FindKillEaterScoreType( uint32 unScoreType ) const |
|
{ |
|
KillEaterScoreMap_t::IndexType_t i = m_mapKillEaterScoreTypes.Find( unScoreType ); |
|
if ( i == KillEaterScoreMap_t::InvalidIndex() ) |
|
return NULL; |
|
|
|
return &m_mapKillEaterScoreTypes[i]; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Kill eater score type accessor |
|
//----------------------------------------------------------------------------- |
|
const char *CEconItemSchema::GetKillEaterScoreTypeLocString( uint32 unScoreType ) const |
|
{ |
|
const kill_eater_score_type_t *pScoreType = FindKillEaterScoreType( unScoreType ); |
|
|
|
return pScoreType |
|
? pScoreType->m_pszTypeString |
|
: NULL; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Kill eater score type accessor |
|
//----------------------------------------------------------------------------- |
|
const char *CEconItemSchema::GetKillEaterScoreTypeLevelingDataName( uint32 unScoreType ) const |
|
{ |
|
const kill_eater_score_type_t *pScoreType = FindKillEaterScoreType( unScoreType ); |
|
|
|
return pScoreType |
|
? pScoreType->m_pszLevelBlockName |
|
: NULL; |
|
} |
|
|
|
#if defined(STAGING_ONLY) && ( defined(TF_CLIENT_DLL) || defined(TF_DLL) ) |
|
ConVar tf_allow_strange_bot_kills( "tf_allow_strange_bot_kills", "0", FCVAR_REPLICATED ); |
|
#endif |
|
//----------------------------------------------------------------------------- |
|
// Kill eater score type accessor |
|
//----------------------------------------------------------------------------- |
|
bool CEconItemSchema::GetKillEaterScoreTypeAllowsBotVictims( uint32 unScoreType ) const |
|
{ |
|
#if defined(STAGING_ONLY) && ( defined(TF_CLIENT_DLL) || defined(TF_DLL) ) |
|
if ( tf_allow_strange_bot_kills.GetBool() ) |
|
{ |
|
return true; |
|
} |
|
#endif |
|
|
|
const kill_eater_score_type_t *pScoreType = FindKillEaterScoreType( unScoreType ); |
|
|
|
return pScoreType |
|
? pScoreType->m_bAllowBotVictims |
|
: false; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
econ_tag_handle_t CEconItemSchema::GetHandleForTag( const char *pszTagName ) |
|
{ |
|
EconTagDict_t::IndexType_t i = m_dictTags.Find( pszTagName ); |
|
if ( m_dictTags.IsValidIndex( i ) ) |
|
return i; |
|
|
|
return m_dictTags.Insert( pszTagName ); |
|
} |
|
|
|
#ifdef GC_DLL |
|
//----------------------------------------------------------------------------- |
|
// Kill eater score type accessor |
|
//----------------------------------------------------------------------------- |
|
bool CEconItemSchema::GetKillEaterScoreTypeGCOnlyUpdate( uint32 unScoreType ) const |
|
{ |
|
const kill_eater_score_type_t *pScoreType = FindKillEaterScoreType( unScoreType ); |
|
|
|
return pScoreType |
|
? pScoreType->m_bGCUpdateOnly |
|
: true; // default to being more restrictive |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
bool CEconItemSchema::GetKillEaterScoreTypeAllowsIncrementValues( uint32 unScoreType ) const |
|
{ |
|
const kill_eater_score_type_t *pScoreType = FindKillEaterScoreType( unScoreType ); |
|
|
|
return pScoreType |
|
? pScoreType->m_AllowIncrementValues |
|
: true; // default to being more restrictive |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
const CEconItemSchema::periodic_score_t& CEconItemSchema::GetPeriodicScoreInfo( int iPeriodicScoreIndex ) const |
|
{ |
|
Assert( GetPeriodicScoreTypeList().IsValidIndex( iPeriodicScoreIndex ) ); |
|
|
|
return GetPeriodicScoreTypeList()[ iPeriodicScoreIndex ]; |
|
} |
|
#endif |
|
|
|
#if defined(CLIENT_DLL) || defined(GAME_DLL) |
|
//----------------------------------------------------------------------------- |
|
// Purpose: Clones the specified item definition, and returns the new item def. |
|
//----------------------------------------------------------------------------- |
|
void CEconItemSchema::ItemTesting_CreateTestDefinition( int iCloneFromItemDef, int iNewDef, KeyValues *pNewKV ) |
|
{ |
|
int nMapIndex = m_mapItems.Find( iNewDef ); |
|
if ( !m_mapItems.IsValidIndex( nMapIndex ) ) |
|
{ |
|
nMapIndex = m_mapItems.Insert( iNewDef, CreateEconItemDefinition() ); |
|
m_mapItemsSorted.Insert( iNewDef, m_mapItems[nMapIndex] ); |
|
} |
|
|
|
// Find & copy the clone item def's data in |
|
CEconItemDefinition *pCloneDef = GetItemDefinition( iCloneFromItemDef ); |
|
if ( !pCloneDef ) |
|
return; |
|
m_mapItems[nMapIndex]->CopyPolymorphic( pCloneDef ); |
|
|
|
// Then stomp it with the KV test contents |
|
m_mapItems[nMapIndex]->BInitFromTestItemKVs( iNewDef, pNewKV ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Discards the specified item definition |
|
//----------------------------------------------------------------------------- |
|
void CEconItemSchema::ItemTesting_DiscardTestDefinition( int iDef ) |
|
{ |
|
m_mapItems.Remove( iDef ); |
|
m_mapItemsSorted.Remove( iDef ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Initializes the armory data section of the schema |
|
//----------------------------------------------------------------------------- |
|
bool CEconItemSchema::BInitArmoryData( KeyValues *pKVArmoryData, CUtlVector<CUtlString> *pVecErrors ) |
|
{ |
|
m_dictArmoryItemDataStrings.RemoveAll(); |
|
m_dictArmoryAttributeDataStrings.RemoveAll(); |
|
if ( NULL != pKVArmoryData ) |
|
{ |
|
KeyValues *pKVItemTypes = pKVArmoryData->FindKey( "armory_item_types" ); |
|
if ( pKVItemTypes ) |
|
{ |
|
FOR_EACH_SUBKEY( pKVItemTypes, pKVEntry ) |
|
{ |
|
const char *pszDataKey = pKVEntry->GetName(); |
|
const CUtlConstString sLocString( pKVEntry->GetString() ); |
|
m_dictArmoryItemTypesDataStrings.Insert( pszDataKey, sLocString ); |
|
} |
|
} |
|
|
|
pKVItemTypes = pKVArmoryData->FindKey( "armory_item_classes" ); |
|
if ( pKVItemTypes ) |
|
{ |
|
FOR_EACH_SUBKEY( pKVItemTypes, pKVEntry ) |
|
{ |
|
const char *pszDataKey = pKVEntry->GetName(); |
|
const CUtlConstString sLocString( pKVEntry->GetString() ); |
|
m_dictArmoryItemClassesDataStrings.Insert( pszDataKey, sLocString ); |
|
} |
|
} |
|
|
|
KeyValues *pKVAttribs = pKVArmoryData->FindKey( "armory_attributes" ); |
|
if ( pKVAttribs ) |
|
{ |
|
FOR_EACH_SUBKEY( pKVAttribs, pKVEntry ) |
|
{ |
|
const char *pszDataKey = pKVEntry->GetName(); |
|
const CUtlConstString sLocString( pKVEntry->GetString() ); |
|
m_dictArmoryAttributeDataStrings.Insert( pszDataKey, sLocString ); |
|
} |
|
} |
|
|
|
KeyValues *pKVItems = pKVArmoryData->FindKey( "armory_items" ); |
|
if ( pKVItems ) |
|
{ |
|
FOR_EACH_SUBKEY( pKVItems, pKVEntry ) |
|
{ |
|
const char *pszDataKey = pKVEntry->GetName(); |
|
const CUtlConstString sLocString( pKVEntry->GetString() ); |
|
m_dictArmoryItemDataStrings.Insert( pszDataKey, sLocString ); |
|
} |
|
} |
|
} |
|
return SCHEMA_INIT_SUCCESS(); |
|
} |
|
#endif |
|
|
|
|
|
#ifdef GC_DLL |
|
//----------------------------------------------------------------------------- |
|
// Purpose: Returns the item awarded for an achievement. |
|
// Input: pchAchievementName - The achievement that was awarded. |
|
// Output: The achievement struct for this reward. |
|
//----------------------------------------------------------------------------- |
|
const AchievementAward_t * CEconItemSchema::GetAchievementReward( const char *pchAchievementName, AppId_t unAppID ) const |
|
{ |
|
int nRewardIndex = m_dictAchievementRewards.Find( ComputeAchievementName( unAppID, pchAchievementName ) ); |
|
|
|
if( m_dictAchievementRewards.IsValidIndex( nRewardIndex ) ) |
|
return m_dictAchievementRewards[ nRewardIndex ]; |
|
else |
|
return NULL; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Returns the achievement award that matches the provided data or NULL |
|
// if there is no such award. |
|
// Input: unData - The data field that would be stored in ItemAudit |
|
//----------------------------------------------------------------------------- |
|
const AchievementAward_t *CEconItemSchema::GetAchievementRewardByData( uint32 unData ) const |
|
{ |
|
uint nIndex = m_mapAchievementRewardsByData.Find( unData ); |
|
if( m_mapAchievementRewardsByData.IsValidIndex( nIndex ) ) |
|
{ |
|
return m_mapAchievementRewardsByData[nIndex]; |
|
} |
|
else |
|
{ |
|
return NULL; |
|
} |
|
} |
|
|
|
|
|
#endif // GC_DLL |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Returns the achievement award that matches the provided defindex or NULL |
|
// if there is no such award. |
|
// Input: unData - The data field that would be stored in ItemAudit |
|
//----------------------------------------------------------------------------- |
|
const AchievementAward_t *CEconItemSchema::GetAchievementRewardByDefIndex( uint16 usDefIndex ) const |
|
{ |
|
FOR_EACH_MAP_FAST( m_mapAchievementRewardsByData, nIndex ) |
|
{ |
|
if( m_mapAchievementRewardsByData[nIndex]->m_vecDefIndex.HasElement( usDefIndex ) ) |
|
return m_mapAchievementRewardsByData[nIndex]; |
|
} |
|
return NULL; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Gets a rarity value for a name. |
|
//----------------------------------------------------------------------------- |
|
bool CEconItemSchema::BGetItemRarityFromName( const char *pchName, uint8 *nRarity ) const |
|
{ |
|
if ( 0 == Q_stricmp( "any", pchName ) ) |
|
{ |
|
*nRarity = k_unItemRarity_Any; |
|
return true; |
|
} |
|
|
|
FOR_EACH_MAP_FAST( m_mapRarities, i ) |
|
{ |
|
if ( 0 == Q_stricmp( m_mapRarities[i].GetName(), pchName ) ) |
|
{ |
|
*nRarity = m_mapRarities[i].GetDBValue(); |
|
return true; |
|
} |
|
} |
|
|
|
return false; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Gets a quality value for a name. |
|
// Input: pchName - The name to translate. |
|
// nQuality - (out)The quality number for this name, if found. |
|
// Output: True if the string matched a quality for this schema, false otherwise. |
|
//----------------------------------------------------------------------------- |
|
bool CEconItemSchema::BGetItemQualityFromName( const char *pchName, uint8 *nQuality ) const |
|
{ |
|
if ( 0 == Q_stricmp( "any", pchName ) ) |
|
{ |
|
*nQuality = k_unItemQuality_Any; |
|
return true; |
|
} |
|
|
|
FOR_EACH_MAP_FAST( m_mapQualities, i ) |
|
{ |
|
if ( 0 == Q_stricmp( m_mapQualities[i].GetName(), pchName ) ) |
|
{ |
|
*nQuality = m_mapQualities[i].GetDBValue(); |
|
return true; |
|
} |
|
} |
|
|
|
return false; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Gets a quality definition for an index |
|
// Input: nQuality - The quality to get. |
|
// Output: A pointer to the desired definition, or NULL if it is not found. |
|
//----------------------------------------------------------------------------- |
|
const CEconItemQualityDefinition *CEconItemSchema::GetQualityDefinition( int nQuality ) const |
|
{ |
|
int iIndex = m_mapQualities.Find( nQuality ); |
|
if ( m_mapQualities.IsValidIndex( iIndex ) ) |
|
return &m_mapQualities[iIndex]; |
|
return NULL; |
|
} |
|
|
|
const CEconItemQualityDefinition *CEconItemSchema::GetQualityDefinitionByName( const char *pszDefName ) const |
|
{ |
|
FOR_EACH_MAP_FAST( m_mapQualities, i ) |
|
{ |
|
if ( V_stricmp( pszDefName, m_mapQualities[i].GetName()) == 0 ) |
|
return &m_mapQualities[i]; |
|
} |
|
return NULL; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// ItemRarity |
|
//----------------------------------------------------------------------------- |
|
const CEconItemRarityDefinition *CEconItemSchema::GetRarityDefinitionByMapIndex( int nRarityIndex ) const |
|
{ |
|
if ( m_mapRarities.IsValidIndex( nRarityIndex ) ) |
|
return &m_mapRarities[nRarityIndex]; |
|
|
|
return NULL; |
|
} |
|
//----------------------------------------------------------------------------- |
|
const CEconItemRarityDefinition *CEconItemSchema::GetRarityDefinition( int nRarity ) const |
|
{ |
|
int iIndex = m_mapRarities.Find( nRarity ); |
|
if ( m_mapRarities.IsValidIndex( iIndex ) ) |
|
return &m_mapRarities[iIndex]; |
|
return NULL; |
|
} |
|
//----------------------------------------------------------------------------- |
|
const CEconItemRarityDefinition *CEconItemSchema::GetRarityDefinitionByName( const char *pszDefName ) const |
|
{ |
|
FOR_EACH_MAP_FAST( m_mapRarities, i ) |
|
{ |
|
if ( !strcmp( pszDefName, m_mapRarities[i].GetName() ) ) |
|
return &m_mapRarities[i]; |
|
} |
|
return NULL; |
|
} |
|
//----------------------------------------------------------------------------- |
|
const char* CEconItemSchema::GetRarityName( uint8 iRarity ) |
|
{ |
|
const CEconItemRarityDefinition* pItemRarity = GetRarityDefinition( iRarity ); |
|
if ( !pItemRarity ) |
|
return NULL; |
|
else |
|
return pItemRarity->GetName(); |
|
} |
|
//----------------------------------------------------------------------------- |
|
const char* CEconItemSchema::GetRarityLocKey( uint8 iRarity ) |
|
{ |
|
const CEconItemRarityDefinition* pItemRarity = GetRarityDefinition( iRarity ); |
|
if ( !pItemRarity ) |
|
return NULL; |
|
else |
|
return pItemRarity->GetLocKey(); |
|
} |
|
//----------------------------------------------------------------------------- |
|
const char* CEconItemSchema::GetRarityColor( uint8 iRarity ) |
|
{ |
|
const CEconItemRarityDefinition* pItemRarity = GetRarityDefinition( iRarity ); |
|
if ( !pItemRarity ) |
|
return NULL; |
|
else |
|
return GetColorNameForAttribColor( pItemRarity->GetAttribColor() ); |
|
} |
|
//----------------------------------------------------------------------------- |
|
int CEconItemSchema::GetRarityIndex( const char* pszRarity ) |
|
{ |
|
const CEconItemRarityDefinition* pRarity = GetRarityDefinitionByName( pszRarity ); |
|
if ( pRarity ) |
|
return pRarity->GetDBValue(); |
|
else |
|
return 0; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
bool CEconItemSchema::BGetItemSeries( const char* pchName, uint8 *nItemSeries ) const |
|
{ |
|
FOR_EACH_MAP_FAST( m_mapItemSeries, i ) |
|
{ |
|
if ( 0 == Q_stricmp( m_mapItemSeries[i].GetName(), pchName ) ) |
|
{ |
|
*nItemSeries = m_mapItemSeries[i].GetDBValue(); |
|
return true; |
|
} |
|
} |
|
|
|
return false; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
const CEconItemSeriesDefinition *CEconItemSchema::GetItemSeriesDefinition( int iSeries ) const |
|
{ |
|
int iIndex = m_mapItemSeries.Find( iSeries ); |
|
if ( m_mapItemSeries.IsValidIndex( iIndex ) ) |
|
return &m_mapItemSeries[iIndex]; |
|
return NULL; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Gets an item definition for the specified definition index |
|
// Input: iItemIndex - The index of the desired definition. |
|
// Output: A pointer to the desired definition, or NULL if it is not found. |
|
//----------------------------------------------------------------------------- |
|
CEconItemDefinition *CEconItemSchema::GetItemDefinition( int iItemIndex ) |
|
{ |
|
#if defined(CLIENT_DLL) || defined(GAME_DLL) |
|
#if !defined(CSTRIKE_DLL) |
|
AssertMsg( GetDefaultItemDefinition(), "No default item definition set up for item schema." ); |
|
#endif // CSTRIKE_DLL |
|
#endif // defined(CLIENT_DLL) || defined(GAME_DLL) |
|
|
|
int iIndex = m_mapItems.Find( iItemIndex ); |
|
if ( m_mapItems.IsValidIndex( iIndex ) ) |
|
return m_mapItems[iIndex]; |
|
|
|
#if defined( GC_DLL ) || defined( EXTERNALTESTS_DLL ) |
|
return NULL; |
|
#else // !GC_DLL |
|
if ( GetDefaultItemDefinition() ) |
|
return GetDefaultItemDefinition(); |
|
|
|
#if !defined(CSTRIKE_DLL) |
|
// We shouldn't ever get down here, but all the same returning a valid pointer is very slightly |
|
// a better plan than returning an invalid pointer to code that won't check to see if it's valid. |
|
static CEconItemDefinition *s_pEmptyDefinition = CreateEconItemDefinition(); |
|
return s_pEmptyDefinition; |
|
#else |
|
return NULL; |
|
#endif // CSTRIKE_DLL |
|
|
|
#endif // GC_DLL |
|
} |
|
const CEconItemDefinition *CEconItemSchema::GetItemDefinition( int iItemIndex ) const |
|
{ |
|
return const_cast<CEconItemSchema *>(this)->GetItemDefinition( iItemIndex ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Gets an item definition that has a name matching the specified name. |
|
// Input: pszDefName - The name of the desired definition. |
|
// Output: A pointer to the desired definition, or NULL if it is not found. |
|
//----------------------------------------------------------------------------- |
|
CEconItemDefinition *CEconItemSchema::GetItemDefinitionByName( const char *pszDefName ) |
|
{ |
|
// This shouldn't happen, but let's not crash if it ever does. |
|
Assert( pszDefName != NULL ); |
|
if ( pszDefName == NULL ) |
|
return NULL; |
|
|
|
FOR_EACH_MAP_FAST( m_mapItems, i ) |
|
{ |
|
if ( V_stricmp( pszDefName, m_mapItems[i]->GetDefinitionName()) == 0 ) |
|
return m_mapItems[i]; |
|
} |
|
return NULL; |
|
} |
|
|
|
const CEconItemDefinition *CEconItemSchema::GetItemDefinitionByName( const char *pszDefName ) const |
|
{ |
|
return const_cast<CEconItemSchema *>(this)->GetItemDefinitionByName( pszDefName ); |
|
} |
|
|
|
|
|
#ifdef GC_DLL |
|
random_attrib_t *CEconItemSchema::GetRandomAttributeTemplateByName( const char *pszAttrTemplateName ) const |
|
{ |
|
int index = m_dictRandomAttributeTemplates.Find( pszAttrTemplateName ); |
|
if ( index != m_dictRandomAttributeTemplates.InvalidIndex() ) |
|
{ |
|
return m_dictRandomAttributeTemplates[index]; |
|
} |
|
|
|
return NULL; |
|
} |
|
#endif // GC_DLL |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Gets an attribute definition for an index |
|
// Input: iAttribIndex - The index of the desired definition. |
|
// Output: A pointer to the desired definition, or NULL if it is not found. |
|
//----------------------------------------------------------------------------- |
|
CEconItemAttributeDefinition *CEconItemSchema::GetAttributeDefinition( int iAttribIndex ) |
|
{ |
|
int iIndex = m_mapAttributes.Find( iAttribIndex ); |
|
if ( m_mapAttributes.IsValidIndex( iIndex ) ) |
|
return &m_mapAttributes[iIndex]; |
|
return NULL; |
|
} |
|
const CEconItemAttributeDefinition *CEconItemSchema::GetAttributeDefinition( int iAttribIndex ) const |
|
{ |
|
return const_cast<CEconItemSchema *>(this)->GetAttributeDefinition( iAttribIndex ); |
|
} |
|
|
|
CEconItemAttributeDefinition *CEconItemSchema::GetAttributeDefinitionByName( const char *pszDefName ) |
|
{ |
|
Assert( pszDefName ); |
|
if ( !pszDefName ) |
|
return NULL; |
|
|
|
VPROF_BUDGET( "CEconItemSchema::GetAttributeDefinitionByName", VPROF_BUDGETGROUP_STEAM ); |
|
FOR_EACH_MAP_FAST( m_mapAttributes, i ) |
|
{ |
|
Assert( m_mapAttributes[i].GetDefinitionName() ); |
|
if ( !m_mapAttributes[i].GetDefinitionName() ) |
|
continue; |
|
|
|
if ( V_stricmp( pszDefName, m_mapAttributes[i].GetDefinitionName() ) == 0 ) |
|
return &m_mapAttributes[i]; |
|
} |
|
return NULL; |
|
} |
|
const CEconItemAttributeDefinition *CEconItemSchema::GetAttributeDefinitionByName( const char *pszDefName ) const |
|
{ |
|
return const_cast<CEconItemSchema *>(this)->GetAttributeDefinitionByName( pszDefName ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Gets a recipe definition for an index |
|
// Input: iRecipeIndex - The index of the desired definition. |
|
// Output: A pointer to the desired definition, or NULL if it is not found. |
|
//----------------------------------------------------------------------------- |
|
CEconCraftingRecipeDefinition *CEconItemSchema::GetRecipeDefinition( int iRecipeIndex ) |
|
{ |
|
int iIndex = m_mapRecipes.Find( iRecipeIndex ); |
|
if ( m_mapRecipes.IsValidIndex( iIndex ) ) |
|
return m_mapRecipes[iIndex]; |
|
return NULL; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
CEconColorDefinition *CEconItemSchema::GetColorDefinitionByName( const char *pszDefName ) |
|
{ |
|
FOR_EACH_VEC( m_vecColorDefs, i ) |
|
{ |
|
if ( !Q_stricmp( m_vecColorDefs[i]->GetName(), pszDefName ) ) |
|
return m_vecColorDefs[i]; |
|
} |
|
return NULL; |
|
} |
|
const CEconColorDefinition *CEconItemSchema::GetColorDefinitionByName( const char *pszDefName ) const |
|
{ |
|
return const_cast<CEconItemSchema *>(this)->GetColorDefinitionByName( pszDefName ); |
|
} |
|
|
|
|
|
#ifdef CLIENT_DLL |
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
const char *CEconItemSchema::GetSteamPackageLocalizationToken( uint32 unPackageId ) const |
|
{ |
|
SteamPackageLocalizationTokenMap_t::IndexType_t i = m_mapSteamPackageLocalizationTokens.Find( unPackageId ); |
|
if ( m_mapSteamPackageLocalizationTokens.IsValidIndex( i ) ) |
|
return m_mapSteamPackageLocalizationTokens[i]; |
|
return NULL; |
|
} |
|
#endif // CLIENT_DLL |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Return the attribute specified attachedparticlesystem_t* associated with the given id. |
|
//----------------------------------------------------------------------------- |
|
attachedparticlesystem_t* CEconItemSchema::GetAttributeControlledParticleSystem( int id ) |
|
{ |
|
int iIndex = m_mapAttributeControlledParticleSystems.Find( id ); |
|
if ( m_mapAttributeControlledParticleSystems.IsValidIndex( iIndex ) ) |
|
return &m_mapAttributeControlledParticleSystems[iIndex]; |
|
return NULL; |
|
} |
|
|
|
attachedparticlesystem_t* CEconItemSchema::FindAttributeControlledParticleSystem( const char *pchSystemName ) |
|
{ |
|
FOR_EACH_MAP_FAST( m_mapAttributeControlledParticleSystems, nSystem ) |
|
{ |
|
if( !Q_stricmp( m_mapAttributeControlledParticleSystems[nSystem].pszSystemName, pchSystemName ) ) |
|
return &m_mapAttributeControlledParticleSystems[nSystem]; |
|
} |
|
return NULL; |
|
} |
|
|
|
|
|
#if defined(CLIENT_DLL) || defined(GAME_DLL) |
|
bool CEconItemSchema::SetupPreviewItemDefinition( KeyValues *pKV ) |
|
{ |
|
int nMapIndex = m_mapItems.Find( PREVIEW_ITEM_DEFINITION_INDEX ); |
|
if ( !m_mapItems.IsValidIndex( nMapIndex ) ) |
|
{ |
|
nMapIndex = m_mapItems.Insert( PREVIEW_ITEM_DEFINITION_INDEX, CreateEconItemDefinition() ); |
|
} |
|
|
|
CEconItemDefinition *pItemDef = m_mapItems[ nMapIndex ]; |
|
return pItemDef->BInitFromKV( pKV ); |
|
} |
|
#endif // defined(CLIENT_DLL) || defined(GAME_DLL) |
|
|
|
#ifdef GC_DLL |
|
//----------------------------------------------------------------------------- |
|
// Purpose: Returns all the foreign item imports for an app ID |
|
//----------------------------------------------------------------------------- |
|
const CEconItemDefinition *CEconItemSchema::GetAppItemImport( AppId_t unAppID, uint16 usDefIndex ) const |
|
{ |
|
int i = m_mapForeignImports.Find( unAppID ); |
|
if( m_mapForeignImports.IsValidIndex( i ) ) |
|
return m_mapForeignImports[i]->FindMapping( usDefIndex ); |
|
else |
|
return NULL; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Returns all the foreign item imports for an app ID |
|
//----------------------------------------------------------------------------- |
|
bool CEconItemSchema::BInitForeignImports( CUtlVector<CUtlString> *pVecErrors ) |
|
{ |
|
FOR_EACH_MAP_FAST( m_mapItems, nItem ) |
|
{ |
|
CEconItemDefinition *pDefn = m_mapItems[nItem]; |
|
|
|
KeyValues *pkvImport = pDefn->GetDefinitionKey( "import_from" ); |
|
if( !pkvImport ) |
|
continue; |
|
|
|
FOR_EACH_VALUE( pkvImport, pkvApp ) |
|
{ |
|
CForeignAppImports *pAppImports = FindOrAddAppImports( Q_atoi( pkvApp->GetName() ) ); |
|
pAppImports->AddMapping( pkvApp->GetInt(), pDefn ); |
|
} |
|
} |
|
|
|
return true; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Returns all the foreign item imports for an app ID |
|
//----------------------------------------------------------------------------- |
|
CForeignAppImports *CEconItemSchema::FindOrAddAppImports( AppId_t unAppID ) |
|
{ |
|
int i = m_mapForeignImports.Find( unAppID ); |
|
if( m_mapForeignImports.IsValidIndex( i ) ) |
|
return m_mapForeignImports[i]; |
|
else |
|
{ |
|
m_vecForeignApps.AddToTail( unAppID ); |
|
CForeignAppImports *pApp = new CForeignAppImports(); |
|
m_mapForeignImports.Insert( unAppID, pApp ); |
|
return pApp; |
|
} |
|
} |
|
#endif // GC_DLL |
|
|
|
bool CEconItemSchema::BCanStrangeFilterApplyToStrangeSlotInItem( uint32 /*strange_event_restriction_t*/ unRestrictionType, uint32 unRestrictionValue, const IEconItemInterface *pItem, int iStrangeSlot, uint32 *out_pOptionalScoreType ) const |
|
{ |
|
Assert( unRestrictionType != kStrangeEventRestriction_None ); |
|
|
|
// Do we have a type for this slot? If not, move on, with the exception: all user-custom scores |
|
// we expect to have types, but certain weapons may not specify a base type and just use |
|
// kills implicitly. |
|
uint32 unStrangeScoreTypeBits = kKillEaterEvent_PlayerKill; |
|
if ( !pItem->FindAttribute( GetKillEaterAttr_Type( iStrangeSlot ), &unStrangeScoreTypeBits ) && iStrangeSlot != 0 ) |
|
return false; |
|
|
|
// Do we have a restriction already in this slot? If so, move on. |
|
if ( pItem->FindAttribute( GetKillEaterAttr_Restriction( iStrangeSlot ) ) ) |
|
return false; |
|
|
|
// We've found an open slot. Make sure that adding our restriction to this slot |
|
// won't result in a duplicate score-type/restriction-type entry. |
|
for ( int j = 0; j < GetKillEaterAttrCount(); j++ ) |
|
{ |
|
// Don't compare against ourself. |
|
if ( iStrangeSlot == j ) |
|
continue; |
|
|
|
// Ignore this entry if we don't have a score type or if the score type differs from |
|
// our search criteria above. |
|
uint32 unAltStrangeScoreType; |
|
if ( !pItem->FindAttribute( GetKillEaterAttr_Type( j ), &unAltStrangeScoreType ) || |
|
unAltStrangeScoreType != unStrangeScoreTypeBits ) |
|
{ |
|
continue; |
|
} |
|
|
|
// This entry does have the same type, so tag us as a duplicate if we also have the same |
|
// restriction that we're trying to apply. |
|
uint32 unAltRestrictionType; |
|
uint32 unAltRestrictionValue; |
|
if ( pItem->FindAttribute( GetKillEaterAttr_Restriction( j ), &unAltRestrictionType ) && |
|
unAltRestrictionType == unRestrictionType && |
|
pItem->FindAttribute( GetKillEaterAttr_RestrictionValue( j ), &unAltRestrictionValue ) && |
|
unAltRestrictionValue == unRestrictionValue ) |
|
{ |
|
return false; |
|
} |
|
} |
|
|
|
if ( out_pOptionalScoreType ) |
|
{ |
|
*out_pOptionalScoreType = *(float *)&unStrangeScoreTypeBits; |
|
} |
|
|
|
// Everything seems alright. |
|
return true; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Ensure that all of our internal structures are consistent, and |
|
// account for all memory that we've allocated. |
|
// Input: validator - Our global validator object |
|
// pchName - Our name (typically a member var in our container) |
|
//----------------------------------------------------------------------------- |
|
#ifdef DBGFLAG_VALIDATE |
|
void CEconItemSchema::Validate( CValidator &validator, const char *pchName ) |
|
{ |
|
VALIDATE_SCOPE(); |
|
ValidateObj( m_mapQualities ); |
|
|
|
FOR_EACH_MAP_FAST( m_mapQualities, i ) |
|
{ |
|
ValidateObj( m_mapQualities[i] ); |
|
} |
|
|
|
ValidateObj( m_mapItems ); |
|
|
|
FOR_EACH_MAP_FAST( m_mapItems, i ) |
|
{ |
|
ValidateObj( m_mapItems[i] ); |
|
} |
|
|
|
ValidateObj( m_mapUpgradeableBaseItems ); |
|
|
|
FOR_EACH_MAP_FAST( m_mapUpgradeableBaseItems, i ) |
|
{ |
|
ValidateObj( m_mapUpgradeableBaseItems[i] ); |
|
} |
|
|
|
ValidateObj( m_mapAttributes ); |
|
|
|
FOR_EACH_MAP_FAST( m_mapAttributes, i ) |
|
{ |
|
ValidateObj( m_mapAttributes[i] ); |
|
} |
|
|
|
ValidateObj( m_mapRecipes ); |
|
|
|
FOR_EACH_MAP_FAST( m_mapRecipes, i ) |
|
{ |
|
ValidateObj( m_mapRecipes[i] ); |
|
} |
|
|
|
FOR_EACH_VEC( m_vecTimedRewards, i ) |
|
{ |
|
ValidateObj( m_vecTimedRewards[i] ); |
|
} |
|
ValidateObj( m_vecTimedRewards ); |
|
|
|
} |
|
#endif // DBGFLAG_VALIDATE |
|
|
|
#ifdef GC_DLL |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
bool CEconItemSchema::BInitExperiements( KeyValues *pKVExperiments, CUtlVector<CUtlString> *pVecErrors ) |
|
{ |
|
m_vecExperiments.RemoveAll(); |
|
|
|
if ( NULL != pKVExperiments ) |
|
{ |
|
FOR_EACH_TRUE_SUBKEY( pKVExperiments, pKVEntry ) |
|
{ |
|
const char *listName = pKVEntry->GetName(); |
|
|
|
SCHEMA_INIT_CHECK( listName != NULL, "All experiments must have titles."); |
|
|
|
int idx = m_vecExperiments.AddToTail(); |
|
SCHEMA_INIT_SUBSTEP( m_vecExperiments[idx].BInitFromKV( pKVEntry, pVecErrors ) ); |
|
} |
|
} |
|
|
|
return SCHEMA_INIT_SUCCESS(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// CExperimentDefinition |
|
//----------------------------------------------------------------------------- |
|
CExperimentDefinition::CExperimentDefinition( void ) |
|
: m_bEnabled( false ) |
|
, m_unExperimentID( 0 ) |
|
, m_unNumParticipants( 0 ) |
|
, m_unMaxParticipants( 0 ) |
|
, m_pKeyValues( NULL ) |
|
{ |
|
} |
|
|
|
CExperimentDefinition::~CExperimentDefinition( void ) |
|
{ |
|
if ( m_pKeyValues ) |
|
{ |
|
m_pKeyValues->deleteThis(); |
|
} |
|
} |
|
|
|
bool CExperimentDefinition::BInitFromKV( KeyValues *pKVExperiment, CUtlVector<CUtlString> *pVecErrors ) |
|
{ |
|
m_pKeyValues = pKVExperiment->MakeCopy(); |
|
|
|
m_unExperimentID = Q_atoi( m_pKeyValues->GetName() ); |
|
m_bEnabled = m_pKeyValues->GetBool( "enabled" ); |
|
m_unNumParticipants = 0; |
|
m_unMaxParticipants = 0; |
|
|
|
KeyValues *pKVGroups = m_pKeyValues->FindKey( "groups" ); |
|
if ( pKVGroups ) |
|
{ |
|
FOR_EACH_TRUE_SUBKEY( pKVGroups, pKVEntry ) |
|
{ |
|
int idx = m_vecGroups.AddToTail(); |
|
experiment_group_t &group = m_vecGroups[idx]; |
|
group.m_pKeyValues = pKVEntry; |
|
group.m_pName = pKVEntry->GetName(); |
|
group.m_unNumParticipants = 0; |
|
group.m_unMaxParticipants = pKVEntry->GetInt( "num_participants", 0 ); |
|
m_unMaxParticipants += group.m_unMaxParticipants; |
|
} |
|
} |
|
|
|
return SCHEMA_INIT_SUCCESS(); |
|
} |
|
|
|
bool CExperimentDefinition::ChooseGroup( uint32 &unGroup ) |
|
{ |
|
CUtlVector< uint32 > vecGroupIndices; |
|
FOR_EACH_VEC( m_vecGroups, i ) |
|
{ |
|
experiment_group_t &group = m_vecGroups[i]; |
|
if ( group.m_unNumParticipants < group.m_unMaxParticipants ) |
|
{ |
|
vecGroupIndices.AddToTail( i ); |
|
} |
|
} |
|
if ( vecGroupIndices.Count() == 0 ) |
|
{ |
|
return false; |
|
} |
|
|
|
uint32 idx = vecGroupIndices[ RandomInt( 0, vecGroupIndices.Count() - 1 ) ]; |
|
experiment_group_t &group = m_vecGroups[ idx ]; |
|
++group.m_unNumParticipants; |
|
unGroup = idx; |
|
|
|
return true; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Give the chance after the schema has been initialized to sanity-check |
|
// individual parts or interactions before moving forward. |
|
//----------------------------------------------------------------------------- |
|
#ifdef TF_GC_DLL |
|
static bool BTestToolApplicability( CUtlVector<CUtlString> *pVecErrors ) |
|
{ |
|
static CSchemaItemDefHandle pItem_TeamPaint ( "Paint Can Team Color" ), // tools |
|
pItem_Paint ( "Paint Can 14" ), |
|
pItem_DescriptionTag ( "Description Tag" ), |
|
pItem_NameTag ( "Name Tag" ), |
|
pItem_Key ( "Decoder Ring" ), |
|
pItem_SummerKey ( "Summer Key" ), |
|
pItem_GiftWrap ( "Gift Wrap" ), |
|
pItem_CustomTextureTool ( "Customize Texture Tool" ), |
|
pItem_SupplyCrateGeneric ( "Supply Crate 2" ), // tool targets |
|
pItem_SummerCrate ( "Summer Crate" ), |
|
pItem_PaintableItem ( "Summer Hat" ), |
|
//pItem_TeamPaintableItem ( "" ), |
|
pItem_UnpaintableItem ( "Big Steel Jaw of Summer Fun" ), |
|
//pItem_UnnameableWeapon ( "" ), |
|
pItem_NameableWeapon ( "The Axtinguisher" ), |
|
pItem_GiftWrappableItem ( "Supply Crate 3" ), |
|
pItem_NonGiftWrappableItem ( "Wrapped Gift" ), |
|
pItem_StampableObject ( "The Conscientious Objector" ), |
|
pItem_NonstampableObject ( "Spiral Sallet" ); |
|
|
|
struct ToolValidityTest_t |
|
{ |
|
const CEconItemDefinition *m_pTool; |
|
const CEconItemDefinition *m_pTarget; |
|
bool m_bExpectedValidity; |
|
}; |
|
|
|
ToolValidityTest_t definitionTests[] = |
|
{ |
|
{ pItem_TeamPaint, pItem_TeamPaint, false }, |
|
{ pItem_TeamPaint, pItem_PaintableItem, true }, |
|
//{ pItem_TeamPaint, pItem_TeamPaintableItem, true }, |
|
{ pItem_TeamPaint, pItem_UnpaintableItem, false }, |
|
{ pItem_Paint, pItem_PaintableItem, true }, |
|
//{ pItem_Paint, pItem_TeamPaintableItem, false }, |
|
{ pItem_Paint, pItem_UnpaintableItem, false }, |
|
{ pItem_Paint, pItem_SupplyCrateGeneric, false }, |
|
{ pItem_Key, pItem_SupplyCrateGeneric, true }, |
|
{ pItem_Key, pItem_SummerCrate, false }, |
|
{ pItem_SummerKey, pItem_SupplyCrateGeneric, true }, // summer keys in staging are now regular keys and should... |
|
{ pItem_SummerKey, pItem_SummerCrate, false }, // ...be able to open regular crates |
|
{ pItem_NameTag, pItem_NameableWeapon, true }, |
|
//{ pItem_NameTag, pItem_UnnameableWeapon, false }, |
|
{ pItem_DescriptionTag, pItem_NameableWeapon, true }, |
|
//{ pItem_DescriptionTag, pItem_UnnameableWeapon, false }, |
|
{ pItem_CustomTextureTool, pItem_StampableObject, true }, |
|
{ pItem_CustomTextureTool, pItem_NonstampableObject, false }, |
|
}; |
|
|
|
bool bAllSuccess = true; |
|
for ( int i = 0; i < ARRAYSIZE( definitionTests ); i++ ) |
|
{ |
|
const GameItemDefinition_t *pToolDef = dynamic_cast<const GameItemDefinition_t *>( definitionTests[i].m_pTool ), |
|
*pTargetDef = dynamic_cast<const GameItemDefinition_t *>( definitionTests[i].m_pTarget ); |
|
|
|
if ( !pToolDef ) |
|
{ |
|
bAllSuccess = false; |
|
pVecErrors->AddToTail( CFmtStr( "Tool validity test %i failed: Tool is NULL.", i ).Access() ); |
|
continue; |
|
} |
|
|
|
if ( !pTargetDef ) |
|
{ |
|
bAllSuccess = false; |
|
pVecErrors->AddToTail( CFmtStr( "Tool validity test %i failed: Target is NULL.", i ).Access() ); |
|
continue; |
|
} |
|
|
|
if ( CEconSharedToolSupport::ToolCanApplyToDefinition( pToolDef, pTargetDef ) != definitionTests[i].m_bExpectedValidity ) |
|
{ |
|
bAllSuccess = false; |
|
pVecErrors->AddToTail( CFmtStr( "Tool validity test %i failed: %s %s have been able to apply to %s.", |
|
i, |
|
pToolDef->GetDefinitionName(), |
|
definitionTests[i].m_bExpectedValidity ? "should" : "shouldn't", |
|
pTargetDef->GetDefinitionName() ).Access() ); |
|
} |
|
} |
|
|
|
// These tests require actual instances of the item--not just the definitions. |
|
ToolValidityTest_t interfaceTests[] = |
|
{ |
|
{ pItem_GiftWrap, pItem_GiftWrappableItem, true }, |
|
{ pItem_GiftWrap, pItem_NonGiftWrappableItem, false }, |
|
}; |
|
|
|
// Skip if we already have failures. |
|
if ( bAllSuccess ) |
|
{ |
|
for ( int i = 0; i < ARRAYSIZE( interfaceTests ); i++ ) |
|
{ |
|
const GameItemDefinition_t *pToolDef = dynamic_cast< const GameItemDefinition_t * >( interfaceTests[ i ].m_pTool ), |
|
*pTargetDef = dynamic_cast< const GameItemDefinition_t * >( interfaceTests[ i ].m_pTarget ); |
|
|
|
if ( !pToolDef ) |
|
{ |
|
bAllSuccess = false; |
|
pVecErrors->AddToTail( CFmtStr( "Tool validity test %i failed: Tool is NULL.", i ).Access() ); |
|
continue; |
|
} |
|
|
|
if ( !pTargetDef ) |
|
{ |
|
bAllSuccess = false; |
|
pVecErrors->AddToTail( CFmtStr( "Tool validity test %i failed: Target is NULL.", i ).Access() ); |
|
continue; |
|
} |
|
|
|
CEconItem *pTool = GEconManager()->GetItemFactory().CreateSpecificItem( ( const CEconGameAccount *) NULL, pToolDef->GetDefinitionIndex() ); |
|
CEconItem *pTarget = GEconManager()->GetItemFactory().CreateSpecificItem( ( const CEconGameAccount *) NULL, pTargetDef->GetDefinitionIndex() ); |
|
|
|
if ( CEconSharedToolSupport::ToolCanApplyTo( pTool, pTarget ) != interfaceTests[ i ].m_bExpectedValidity ) |
|
{ |
|
bAllSuccess = false; |
|
pVecErrors->AddToTail( CFmtStr( "Tool validity test %i failed: %s %s have been able to apply to %s.", |
|
i, |
|
pToolDef->GetDefinitionName(), |
|
interfaceTests[ i ].m_bExpectedValidity ? "should" : "shouldn't", |
|
pTargetDef->GetDefinitionName() ).Access() ); |
|
} |
|
|
|
delete pTool; |
|
delete pTarget; |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
return bAllSuccess; |
|
} |
|
#endif // TF_GC_DLL |
|
#endif // defined(GC_DLL) |
|
|
|
bool CEconItemSchema::BPostSchemaInit( CUtlVector<CUtlString> *pVecErrors ) const |
|
{ |
|
bool bAllSuccess = true; |
|
|
|
// Make sure all of our tools are valid. We have to do this after the whole schema is initialized so |
|
// that we don't run into circular reference problems with items referencing loot lists that reference |
|
// items, etc. |
|
FOR_EACH_MAP_FAST( m_mapItems, i ) |
|
{ |
|
const CEconItemDefinition *pItemDef = m_mapItems[i]; |
|
const IEconTool *pTool = pItemDef->GetEconTool(); |
|
|
|
if ( pTool && !const_cast<IEconTool *>( pTool )->BFinishInitialization() ) |
|
{ |
|
#ifdef GC_DLL |
|
bAllSuccess = false; |
|
pVecErrors->AddToTail( CFmtStr( "BPostSchemaInit(): tool '%s' is invalid.", pItemDef->GetDefinitionName() ).Get() ); |
|
#endif // GC_DLL |
|
} |
|
#if TF_GC_DLL |
|
else |
|
{ |
|
// all cosmetic items should have these two attributes from the |
|
// cosmetic_killeater_attribs prefab in case we ever try to drop them as Strange |
|
static CSchemaAttributeDefHandle pAttribDef_KillEaterScoreType( "kill eater score type" ); |
|
static CSchemaAttributeDefHandle pAttribDef_KillEaterKillType( "kill eater kill type" ); |
|
|
|
const CTFItemDefinition *pTFItemDef = assert_cast< const CTFItemDefinition* >( pItemDef ); |
|
if ( pTFItemDef ) |
|
{ |
|
int nSlot = pTFItemDef->GetLoadoutSlot( 0 ); // 0 gives use the default slot |
|
if ( ( nSlot == LOADOUT_POSITION_HEAD ) || ( nSlot == LOADOUT_POSITION_MISC ) || ( nSlot == LOADOUT_POSITION_MISC2 ) ) |
|
{ |
|
bool bFoundScore = false; |
|
bool bFoundKill = false; |
|
|
|
FOR_EACH_VEC( pTFItemDef->GetStaticAttributes(), iIndex ) |
|
{ |
|
const static_attrib_t& staticAttrib = pTFItemDef->GetStaticAttributes()[iIndex]; |
|
const CEconItemAttributeDefinition *pAttrDef = GetItemSchema()->GetAttributeDefinition( staticAttrib.iDefIndex ); |
|
if ( pAttrDef == pAttribDef_KillEaterScoreType ) |
|
{ |
|
bFoundScore = true; |
|
} |
|
else if ( pAttrDef == pAttribDef_KillEaterKillType ) |
|
{ |
|
bFoundKill = true; |
|
} |
|
} |
|
|
|
if ( !bFoundScore || !bFoundKill ) |
|
{ |
|
bAllSuccess = false; |
|
pVecErrors->AddToTail( CFmtStr( "BPostSchemaInit(): '%s' is missing the standard cosmetic killeater attributes.", pItemDef->GetDefinitionName() ).Get() ); |
|
} |
|
} |
|
} |
|
} |
|
#endif // TF_GC_DLL |
|
} |
|
|
|
#if TF_GC_DLL |
|
// Make sure our tool application code validity works correctly. |
|
if ( !BTestToolApplicability( pVecErrors ) ) |
|
{ |
|
bAllSuccess = false; |
|
pVecErrors->AddToTail( "BPostSchemaInit(): error with tool application validity." ); |
|
} |
|
|
|
const CEconLootListDefinition* pUnusualLootlist = GetItemSchema()->GetLootListByName( "all_particle_hats" ); |
|
if ( !pUnusualLootlist ) |
|
{ |
|
bAllSuccess = false; |
|
pVecErrors->AddToTail( "No lootlist \"all_particle_hats\"" ); |
|
} |
|
else |
|
{ |
|
auto& contents = pUnusualLootlist->GetLootListContents(); |
|
FOR_EACH_VEC( contents, i ) |
|
{ |
|
if ( contents[ i ].m_iItemOrLootlistDef > 0 ) |
|
{ |
|
const CEconItemDefinition* pItemDef = GetItemDefinition( contents[ i ].m_iItemOrLootlistDef ); |
|
if ( !( pItemDef->GetEquipRegionMask() & GetItemSchema()->GetEquipRegionBitMaskByName( "hat" ) ) |
|
&& !( pItemDef->GetEquipRegionMask() & GetItemSchema()->GetEquipRegionBitMaskByName( "whole_head" ) ) ) |
|
{ |
|
bAllSuccess = false; |
|
pVecErrors->AddToTail( CFmtStr( "Item \"%s\" is in all_particle_hats, but doesn't have equip region hat or whole_head, meaning it can't become unusual. REMOVE IT!", pItemDef->GetDefinitionName() ).Get() ); |
|
} |
|
} |
|
} |
|
} |
|
#endif // TF_GC_DLL |
|
|
|
return bAllSuccess; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// |
|
//----------------------------------------------------------------------------- |
|
CItemLevelingDefinition::CItemLevelingDefinition( void ) |
|
{ |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Copy constructor |
|
//----------------------------------------------------------------------------- |
|
CItemLevelingDefinition::CItemLevelingDefinition( const CItemLevelingDefinition &that ) |
|
{ |
|
(*this) = that; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// |
|
//----------------------------------------------------------------------------- |
|
CItemLevelingDefinition::~CItemLevelingDefinition() |
|
{ |
|
// Free up strdup() memory. |
|
free( m_pszLocalizedName_LocalStorage ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Operator= |
|
//----------------------------------------------------------------------------- |
|
CItemLevelingDefinition &CItemLevelingDefinition::operator=( const CItemLevelingDefinition &other ) |
|
{ |
|
m_unLevel = other.m_unLevel; |
|
m_unRequiredScore = other.m_unRequiredScore; |
|
m_pszLocalizedName_LocalStorage = strdup( other.m_pszLocalizedName_LocalStorage ); |
|
|
|
return *this; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// |
|
//----------------------------------------------------------------------------- |
|
bool CItemLevelingDefinition::BInitFromKV( KeyValues *pKVItemLevel, const char *pszLevelBlockName, CUtlVector<CUtlString> *pVecErrors ) |
|
{ |
|
m_unLevel = Q_atoi( pKVItemLevel->GetName() ); |
|
m_unRequiredScore = pKVItemLevel->GetInt( "score" ); |
|
m_pszLocalizedName_LocalStorage = strdup( pKVItemLevel->GetString( "rank_name", CFmtStr( "%s%i", pszLevelBlockName, m_unLevel ).Access() ) ); |
|
|
|
return SCHEMA_INIT_SUCCESS(); |
|
} |
|
|
|
#ifdef GC_DLL |
|
EUniverse GetUniverse() |
|
{ |
|
return GGCHost()->GetUniverse(); |
|
} |
|
#endif // GC_DLL
|
|
|