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.
413 lines
14 KiB
413 lines
14 KiB
![]()
4 years ago
|
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||
|
|
||
|
#include "cbase.h"
|
||
|
#include "econ_item_interface.h"
|
||
|
#include "econ_item_tools.h" // needed for CEconTool_WrappedGift definition for IsMarketable()
|
||
|
#include "rtime.h"
|
||
|
|
||
|
#ifdef STAGING_ONLY
|
||
|
ConVar tf_paint_kit_force_wear( "tf_paint_kit_force_wear", "0", FCVAR_REPLICATED, "Set to force the wear level of paink kit weapons and ignore the GC dynamic attribute value." );
|
||
|
#endif
|
||
|
|
||
|
// --------------------------------------------------------------------------
|
||
|
bool IEconItemInterface::GetCustomPaintKitWear( float &flWear ) const
|
||
|
{
|
||
|
|
||
|
#ifdef STAGING_ONLY
|
||
|
// don't assert in staging if this ConVar is set
|
||
|
if ( tf_paint_kit_force_wear.GetInt() > 0 )
|
||
|
{
|
||
|
flWear = tf_paint_kit_force_wear.GetFloat();
|
||
|
return true;
|
||
|
}
|
||
|
#endif // STAGING_ONLY
|
||
|
|
||
|
static CSchemaAttributeDefHandle pAttrDef_PaintKitWear( "set_item_texture_wear" );
|
||
|
float flPaintKitWear = 0;
|
||
|
if ( pAttrDef_PaintKitWear && FindAttribute_UnsafeBitwiseCast<attrib_value_t>( this, pAttrDef_PaintKitWear, &flPaintKitWear ) )
|
||
|
{
|
||
|
flWear = flPaintKitWear;
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
static CSchemaAttributeDefHandle pAttrDef_DefaultWear( "texture_wear_default" );
|
||
|
if ( pAttrDef_DefaultWear && FindAttribute_UnsafeBitwiseCast<attrib_value_t>( this, pAttrDef_DefaultWear, &flPaintKitWear ) )
|
||
|
{
|
||
|
flWear = flPaintKitWear;
|
||
|
return true;
|
||
|
}
|
||
|
// If you have no wear, you also should not have a paint kit
|
||
|
AssertMsg( !GetCustomPainkKitDefinition(), "No Wear Found on Item [%llu - %s] that has a Paintkit!", GetID(), GetItemDefinition()->GetDefinitionName() );
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// --------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
// --------------------------------------------------------------------------
|
||
|
bool IEconItemInterface::IsTemporaryItem() const
|
||
|
{
|
||
|
// store preview items are also temporary
|
||
|
if ( GetOrigin() == kEconItemOrigin_PreviewItem )
|
||
|
return true;
|
||
|
|
||
|
RTime32 rtTime = GetExpirationDate();
|
||
|
if ( rtTime > 0 )
|
||
|
return true;
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// --------------------------------------------------------------------------
|
||
|
RTime32 IEconItemInterface::GetExpirationDate() const
|
||
|
{
|
||
|
COMPILE_TIME_ASSERT( sizeof( float ) == sizeof( RTime32 ) );
|
||
|
|
||
|
// dynamic attributes, if present, will override any static expiration timer
|
||
|
static CSchemaAttributeDefHandle pAttrib_ExpirationDate( "expiration date" );
|
||
|
|
||
|
attrib_value_t unAttribExpirationTimeBits;
|
||
|
COMPILE_TIME_ASSERT( sizeof( unAttribExpirationTimeBits ) == sizeof( RTime32 ) );
|
||
|
|
||
|
if ( pAttrib_ExpirationDate && FindAttribute( pAttrib_ExpirationDate, &unAttribExpirationTimeBits ) )
|
||
|
return *(RTime32 *)&unAttribExpirationTimeBits;
|
||
|
|
||
|
// do we have a static timer set in the schema for all instances to expire?
|
||
|
return GetItemDefinition()
|
||
|
? GetItemDefinition()->GetExpirationDate()
|
||
|
: RTime32( 0 );
|
||
|
}
|
||
|
|
||
|
// --------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
// --------------------------------------------------------------------------
|
||
|
RTime32 IEconItemInterface::GetTradableAfterDateTime() const
|
||
|
{
|
||
|
static CSchemaAttributeDefHandle pAttrib_TradableAfter( "tradable after date" );
|
||
|
Assert( pAttrib_TradableAfter );
|
||
|
|
||
|
if ( !pAttrib_TradableAfter )
|
||
|
return 0;
|
||
|
|
||
|
RTime32 rtTimestamp;
|
||
|
if ( !FindAttribute( pAttrib_TradableAfter, &rtTimestamp ) )
|
||
|
return 0;
|
||
|
|
||
|
return rtTimestamp;
|
||
|
}
|
||
|
|
||
|
// --------------------------------------------------------------------------
|
||
|
// Purpose: Return true if this item can never be traded
|
||
|
// --------------------------------------------------------------------------
|
||
|
bool IEconItemInterface::IsPermanentlyUntradable() const
|
||
|
{
|
||
|
if ( GetItemDefinition() == NULL )
|
||
|
return true;
|
||
|
|
||
|
// tagged to not be a part of the economy?
|
||
|
if ( ( kEconItemFlag_NonEconomy & GetFlags() ) != 0 )
|
||
|
return true;
|
||
|
|
||
|
// check attributes
|
||
|
|
||
|
static CSchemaAttributeDefHandle pAttrib_AlwaysTradable( "always tradable" );
|
||
|
static CSchemaAttributeDefHandle pAttrib_CannotTrade( "cannot trade" );
|
||
|
static CSchemaAttributeDefHandle pAttrib_NonEconomy( "non economy" );
|
||
|
|
||
|
Assert( pAttrib_AlwaysTradable != NULL );
|
||
|
Assert( pAttrib_CannotTrade != NULL );
|
||
|
|
||
|
if ( pAttrib_AlwaysTradable == NULL || pAttrib_CannotTrade == NULL || pAttrib_NonEconomy == NULL )
|
||
|
return true;
|
||
|
|
||
|
// Order matters, check for nonecon first. Always tradable overrides cannot trade.
|
||
|
if ( FindAttribute( pAttrib_NonEconomy ) )
|
||
|
return true;
|
||
|
|
||
|
if ( FindAttribute( pAttrib_AlwaysTradable ) ) // *sigh*
|
||
|
return false;
|
||
|
|
||
|
if ( FindAttribute( pAttrib_CannotTrade ) )
|
||
|
return true;
|
||
|
|
||
|
// items gained in this way are not tradable
|
||
|
switch ( GetOrigin() )
|
||
|
{
|
||
|
case kEconItemOrigin_Invalid:
|
||
|
case kEconItemOrigin_Achievement:
|
||
|
case kEconItemOrigin_Foreign:
|
||
|
case kEconItemOrigin_PreviewItem:
|
||
|
case kEconItemOrigin_SteamWorkshopContribution:
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
// temporary items (items that will expire for any reason) cannot be traded
|
||
|
if ( IsTemporaryItem() )
|
||
|
return true;
|
||
|
|
||
|
// certain quality levels are not tradable
|
||
|
if ( GetQuality() >= AE_COMMUNITY && GetQuality() <= AE_SELFMADE )
|
||
|
return true;
|
||
|
|
||
|
// explicitly marked cannot trade?
|
||
|
if ( ( kEconItemFlag_CannotTrade & GetFlags() ) != 0 )
|
||
|
return true;
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// --------------------------------------------------------------------------
|
||
|
// Purpose: Return true if this item is a commodity on the Market (can place buy orders)
|
||
|
// --------------------------------------------------------------------------
|
||
|
bool IEconItemInterface::IsCommodity() const
|
||
|
{
|
||
|
if ( GetItemDefinition() == NULL )
|
||
|
return false;
|
||
|
|
||
|
static CSchemaAttributeDefHandle pAttrib_IsCommodity( "is commodity" );
|
||
|
if ( FindAttribute( pAttrib_IsCommodity ) )
|
||
|
return true;
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// --------------------------------------------------------------------------
|
||
|
// Purpose: Return true if temporarily untradable
|
||
|
// --------------------------------------------------------------------------
|
||
|
bool IEconItemInterface::IsTemporarilyUntradable() const
|
||
|
{
|
||
|
// Temporary untradability does NOT take "always tradable" into account
|
||
|
if ( GetTradableAfterDateTime() >= CRTime::RTime32TimeCur() )
|
||
|
return true;
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// --------------------------------------------------------------------------
|
||
|
// Purpose: Return true if this item is untradable
|
||
|
// --------------------------------------------------------------------------
|
||
|
bool IEconItemInterface::IsTradable() const
|
||
|
{
|
||
|
// Items that are expired are never listable, regardless of other rules.
|
||
|
//RTime32 timeExpirationDate = GetExpirationDate();
|
||
|
//if ( timeExpirationDate > 0 && timeExpirationDate < CRTime::RTime32TimeCur() )
|
||
|
// return false;
|
||
|
|
||
|
return GetUntradabilityFlags() == 0;
|
||
|
}
|
||
|
|
||
|
// --------------------------------------------------------------------------
|
||
|
// Purpose: Return untradability flags
|
||
|
// --------------------------------------------------------------------------
|
||
|
int IEconItemInterface::GetUntradabilityFlags() const
|
||
|
{
|
||
|
int nFlags = 0;
|
||
|
if ( IsTemporarilyUntradable() )
|
||
|
{
|
||
|
nFlags |= k_Untradability_Temporary;
|
||
|
}
|
||
|
|
||
|
if ( IsPermanentlyUntradable() )
|
||
|
{
|
||
|
nFlags |= k_Untradability_Permanent;
|
||
|
}
|
||
|
|
||
|
return nFlags;
|
||
|
}
|
||
|
|
||
|
// --------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
// --------------------------------------------------------------------------
|
||
|
bool IEconItemInterface::IsUsableInCrafting() const
|
||
|
{
|
||
|
if ( GetItemDefinition() == NULL )
|
||
|
return false;
|
||
|
|
||
|
// tagged to not be a part of the economy?
|
||
|
if ( ( kEconItemFlag_NonEconomy & GetFlags() ) != 0 )
|
||
|
return false;
|
||
|
|
||
|
// always craftable?
|
||
|
static CSchemaAttributeDefHandle pAttrib_AlwaysUsableInCraft( "always tradable" );
|
||
|
Assert( pAttrib_AlwaysUsableInCraft );
|
||
|
|
||
|
if ( FindAttribute( pAttrib_AlwaysUsableInCraft ) )
|
||
|
return true;
|
||
|
|
||
|
// never craftable?
|
||
|
static CSchemaAttributeDefHandle pAttrib_NeverCraftable( "never craftable" );
|
||
|
Assert( pAttrib_NeverCraftable );
|
||
|
|
||
|
if ( FindAttribute( pAttrib_NeverCraftable ) )
|
||
|
return false;
|
||
|
|
||
|
// temporary items (items that will expire for any reason) cannot be turned into
|
||
|
// permanent items
|
||
|
if ( IsTemporaryItem() )
|
||
|
return false;
|
||
|
|
||
|
// explicitly marked not usable in crafting?
|
||
|
if ( ( kEconItemFlag_CannotBeUsedInCrafting & GetFlags() ) != 0 )
|
||
|
return false;
|
||
|
|
||
|
// items gained in this way are not craftable
|
||
|
switch ( GetOrigin() )
|
||
|
{
|
||
|
case kEconItemOrigin_Invalid:
|
||
|
case kEconItemOrigin_Foreign:
|
||
|
case kEconItemOrigin_StorePromotion:
|
||
|
case kEconItemOrigin_SteamWorkshopContribution:
|
||
|
return false;
|
||
|
|
||
|
// purchased items can be used in crafting if explicitly tagged, but not by default
|
||
|
case kEconItemOrigin_Purchased:
|
||
|
// deny items the GC didn't flag at purchase time
|
||
|
if ( (GetFlags() & kEconItemFlag_PurchasedAfterStoreCraftabilityChanges2012) == 0 )
|
||
|
return false;
|
||
|
|
||
|
// deny items that can never be used
|
||
|
if ( (GetItemDefinition()->GetCapabilities() & ITEM_CAP_CAN_BE_CRAFTED_IF_PURCHASED) == 0 )
|
||
|
return false;
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
// certain quality levels are not craftable
|
||
|
if ( GetQuality() >= AE_COMMUNITY && GetQuality() <= AE_SELFMADE )
|
||
|
return false;
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
// --------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
// --------------------------------------------------------------------------
|
||
|
bool IEconItemInterface::IsMarketable() const
|
||
|
{
|
||
|
const CEconItemDefinition *pItemDef = GetItemDefinition();
|
||
|
if ( pItemDef == NULL )
|
||
|
return false;
|
||
|
|
||
|
// Untradeable items can never be marketed, regardless of other rules.
|
||
|
// Temporarily untradable items can be marketed, only permanent untradable items cannot be marketed
|
||
|
if ( IsPermanentlyUntradable() )
|
||
|
return false;
|
||
|
|
||
|
// Items that are expired are never listable, regardless of other rules.
|
||
|
RTime32 timeExpirationDate = GetExpirationDate();
|
||
|
if ( timeExpirationDate > 0 && timeExpirationDate < CRTime::RTime32TimeCur() )
|
||
|
return false;
|
||
|
|
||
|
// Initially, only TF2 supports listing items in the Marketplace.
|
||
|
#if defined( TF_DLL ) || defined( TF_CLIENT_DLL ) || defined( TF_GC_DLL )
|
||
|
{
|
||
|
// User-created wrapped gifts are untradeable for the moment. This would provide a backdoor
|
||
|
// for users to sell anything they wanted, which is interesting but not what we want in
|
||
|
// the initial launch.
|
||
|
if ( pItemDef->GetTypedEconTool<CEconTool_WrappedGift>() )
|
||
|
return false;
|
||
|
|
||
|
// All other tools are listable. This includes keys, paints, backpack expanders, strange
|
||
|
// parts, Halloween spells, wedding rings, etc. It does not includes gifts (see above),
|
||
|
// noisemakers, or crates (see below).
|
||
|
if ( pItemDef->IsTool() )
|
||
|
return true;
|
||
|
|
||
|
// All crates are listable. Anything with the "decodable" flag is considered a crate.
|
||
|
if ( (pItemDef->GetCapabilities() & ITEM_CAP_DECODABLE) != 0 )
|
||
|
return true;
|
||
|
|
||
|
// Genuine-quality items come from time-limited purchase promos and are listable. Vintage
|
||
|
// items are from one-time transitions and are all finite quality. Haunted quality items are
|
||
|
// TF-Halloween-event specific. Some of the older haunted items didn't generate revenue, but
|
||
|
// the content is all old and there seems to be little harm in letting it be listed. The
|
||
|
// haunted items from 2013 all come from crates, which means they all generated revenue.
|
||
|
// Collectors items are created from a finite set of recipes.
|
||
|
// Paintkit Weapons are from cases or operations
|
||
|
if ( GetQuality() == AE_RARITY1 || GetQuality() == AE_VINTAGE || GetQuality() == AE_HAUNTED
|
||
|
|| GetQuality() == AE_COLLECTORS || GetQuality() == AE_PAINTKITWEAPON )
|
||
|
return true;
|
||
|
|
||
|
// All festive items are from time-limited holiday crates and are listable. This code seems
|
||
|
// safe. (...) (This code is in fact so safe that if we just do a substring match we'll also
|
||
|
// allow "A Rather Festive Tree".)
|
||
|
if ( !V_strncmp( pItemDef->GetDefinitionName(), "Festive", 7 ) )
|
||
|
return true;
|
||
|
|
||
|
// All botkiller items come from MvM rewards and are listable. This does a substring search
|
||
|
// to find all varieties (gold, silver, rust, etc.), etc.
|
||
|
if ( V_strstr( pItemDef->GetDefinitionName(), " Botkiller " ) )
|
||
|
return true;
|
||
|
|
||
|
// Mvm V2 Robit Parts
|
||
|
if ( V_strstr( pItemDef->GetDefinitionName(), "Robits " ) )
|
||
|
return true;
|
||
|
|
||
|
// MvM Killstreak Weapons
|
||
|
static CSchemaAttributeDefHandle pAttr_killstreak( "killstreak tier" );
|
||
|
if ( FindAttribute( pAttr_killstreak ) )
|
||
|
return true;
|
||
|
|
||
|
// Australium Items
|
||
|
static CSchemaAttributeDefHandle pAttrDef_IsAustralium( "is australium item" );
|
||
|
if ( FindAttribute( pAttrDef_IsAustralium ) )
|
||
|
return true;
|
||
|
|
||
|
// Glitch GateHat Replacement Item
|
||
|
static CSchemaItemDefHandle pItemDef_GlitchedCircuit( "Glitched Circuit Board" );
|
||
|
if ( pItemDef == pItemDef_GlitchedCircuit )
|
||
|
return true;
|
||
|
|
||
|
// Anything that says it wants to be marketable.
|
||
|
static CSchemaAttributeDefHandle pAttrDef_IsMarketable( "is marketable" );
|
||
|
if ( FindAttribute( pAttrDef_IsMarketable ) )
|
||
|
return true;
|
||
|
|
||
|
// Anything that is of limited quantity (ie limited promos)
|
||
|
static CSchemaAttributeDefHandle pAttrDef_IsLimited( "limited quantity item" );
|
||
|
if ( FindAttribute( pAttrDef_IsLimited ) )
|
||
|
return true;
|
||
|
|
||
|
// Allow the Giving items (not a wrapped_gift but a gift, ie Secret Saxton, Pile O Gifts, Pallet of Keys)
|
||
|
const CEconTool_Gift *pEconToolGift = pItemDef->GetTypedEconTool<CEconTool_Gift>();
|
||
|
if ( pEconToolGift )
|
||
|
return true;
|
||
|
|
||
|
// Unusual Cosmetics and Taunts
|
||
|
if ( GetQuality() == AE_UNUSUAL && ( GetItemDefinition()->GetLoadoutSlot( 0 ) == LOADOUT_POSITION_MISC || GetItemDefinition()->GetLoadoutSlot( 0 ) == LOADOUT_POSITION_TAUNT ) )
|
||
|
return true;
|
||
|
|
||
|
// Strange items. Dont just check for strange quality, actually check for a strange attribute.
|
||
|
// See if we've got any strange attributes.
|
||
|
for ( int i = 0; i < GetKillEaterAttrCount(); i++ )
|
||
|
{
|
||
|
if ( FindAttribute( GetKillEaterAttr_Score( i ) ) )
|
||
|
{
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
#endif // defined( TF_DLL ) || defined( TF_CLIENT_DLL ) || defined( TF_GC_DLL )
|
||
|
|
||
|
// By default, items aren't listable.
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// --------------------------------------------------------------------------
|
||
|
const char *IEconItemInterface::GetDefinitionString( const char *pszKeyName, const char *pszDefaultValue ) const
|
||
|
{
|
||
|
const GameItemDefinition_t *pDef = GetItemDefinition();
|
||
|
if ( pDef )
|
||
|
return pDef->GetDefinitionString( pszKeyName, pszDefaultValue );
|
||
|
return pszDefaultValue;
|
||
|
}
|
||
|
|
||
|
// --------------------------------------------------------------------------
|
||
|
KeyValues *IEconItemInterface::GetDefinitionKey( const char *pszKeyName ) const
|
||
|
{
|
||
|
const GameItemDefinition_t *pDef = GetItemDefinition();
|
||
|
if ( pDef )
|
||
|
return pDef->GetDefinitionKey( pszKeyName );
|
||
|
return NULL;
|
||
|
}
|