|
|
|
|
//========= Copyright Valve Corporation, All rights reserved. ============//
|
|
|
|
|
|
|
|
|
|
#include "cbase.h"
|
|
|
|
|
#include "econ_item_description.h"
|
|
|
|
|
#include "econ_item_interface.h"
|
|
|
|
|
#include "econ_item_tools.h"
|
|
|
|
|
#include "econ_holidays.h"
|
|
|
|
|
#include "econ_store.h"
|
|
|
|
|
#include "tier1/ilocalize.h"
|
|
|
|
|
#include "localization_provider.h"
|
|
|
|
|
#include "rtime.h"
|
|
|
|
|
#include "econ_dynamic_recipe.h"
|
|
|
|
|
|
|
|
|
|
#ifdef GC
|
|
|
|
|
// the GC needs accountdetails to get persona names
|
|
|
|
|
#include "gcsdk/accountdetails.h"
|
|
|
|
|
#else // !GC
|
|
|
|
|
|
|
|
|
|
#ifndef EXTERNALTESTS_DLL
|
|
|
|
|
#include "econ_item_inventory.h"
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#ifdef CLIENT_DLL
|
|
|
|
|
#include "gc_clientsystem.h"
|
|
|
|
|
#include "client_community_market.h" // for Market data in tooltips
|
|
|
|
|
#include "econ_ui.h" // for money-value-to-display-string formatting
|
|
|
|
|
#include "store/store_panel.h" // for money-value-to-display-string formatting
|
|
|
|
|
#endif // CLIENT_DLL
|
|
|
|
|
#endif // GC
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef PROJECT_TF
|
|
|
|
|
#include "tf_duel_summary.h"
|
|
|
|
|
#include "econ_contribution.h"
|
|
|
|
|
#include "tf_player_info.h"
|
|
|
|
|
#include "tf_wardata.h"
|
|
|
|
|
|
|
|
|
|
#ifdef TF_CLIENT_DLL
|
|
|
|
|
#include "tf_gamerules.h"
|
|
|
|
|
#include "tf_mapinfo.h"
|
|
|
|
|
#endif
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#ifdef VPROF_ENABLED
|
|
|
|
|
static const char *g_pszEconDescriptionVprofGroup = _T("Econ Description");
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
extern const char *GetWearLocalizationString( float flWear );
|
|
|
|
|
|
|
|
|
|
// --------------------------------------------------------------------------
|
|
|
|
|
// Local Helper
|
|
|
|
|
// --------------------------------------------------------------------------
|
|
|
|
|
const size_t k_VerboseStringBufferSize = 128;
|
|
|
|
|
static char *BuildVerboseStrings( char buf[k_VerboseStringBufferSize], bool bIsVerbose, const char *format, ... )
|
|
|
|
|
{
|
|
|
|
|
if ( !bIsVerbose )
|
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
|
|
va_list argptr;
|
|
|
|
|
va_start( argptr, format );
|
|
|
|
|
Q_vsnprintf( buf, k_VerboseStringBufferSize, format, argptr );
|
|
|
|
|
va_end(argptr);
|
|
|
|
|
|
|
|
|
|
return buf;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// --------------------------------------------------------------------------
|
|
|
|
|
// Purpose:
|
|
|
|
|
// --------------------------------------------------------------------------
|
|
|
|
|
static bool IsStorePreviewItem( const IEconItemInterface *pEconItem )
|
|
|
|
|
{
|
|
|
|
|
Assert( pEconItem );
|
|
|
|
|
|
|
|
|
|
#ifdef CLIENT_DLL
|
|
|
|
|
return pEconItem->GetFlags() & kEconItemFlagClient_StoreItem;
|
|
|
|
|
#else
|
|
|
|
|
return false;
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// --------------------------------------------------------------------------
|
|
|
|
|
// Purpose:
|
|
|
|
|
// --------------------------------------------------------------------------
|
|
|
|
|
void IEconItemDescription::YieldingFillOutEconItemDescription( IEconItemDescription *out_pDescription, CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem )
|
|
|
|
|
{
|
|
|
|
|
VPROF_BUDGET( "IEconItemDescription::YieldingFillOutEconItemDescription()", g_pszEconDescriptionVprofGroup );
|
|
|
|
|
|
|
|
|
|
Assert( out_pDescription );
|
|
|
|
|
Assert( pLocalizationProvider );
|
|
|
|
|
Assert( pEconItem );
|
|
|
|
|
|
|
|
|
|
out_pDescription->YieldingCacheDescriptionData( pLocalizationProvider, pEconItem );
|
|
|
|
|
out_pDescription->GenerateDescriptionLines( pLocalizationProvider, pEconItem );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// --------------------------------------------------------------------------
|
|
|
|
|
// Purpose:
|
|
|
|
|
// --------------------------------------------------------------------------
|
|
|
|
|
const econ_item_description_line_t *IEconItemDescription::GetFirstLineWithMetaType( uint32 unMetaTypeSearchFlags ) const
|
|
|
|
|
{
|
|
|
|
|
for ( unsigned int i = 0; i < GetLineCount(); i++ )
|
|
|
|
|
{
|
|
|
|
|
const econ_item_description_line_t& pLine = GetLine(i);
|
|
|
|
|
if ( (pLine.unMetaType & unMetaTypeSearchFlags) == unMetaTypeSearchFlags )
|
|
|
|
|
return &pLine;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#ifdef BUILD_ITEM_NAME_AND_DESC
|
|
|
|
|
|
|
|
|
|
// --------------------------------------------------------------------------
|
|
|
|
|
// Purpose:
|
|
|
|
|
// --------------------------------------------------------------------------
|
|
|
|
|
CLocalizedStringArg<CLocalizedRTime32>::CLocalizedStringArg( const CLocalizedRTime32& cTimeIn )
|
|
|
|
|
{
|
|
|
|
|
#if TF_ANTI_IDLEBOT_VERIFICATION
|
|
|
|
|
// We expect the client and the GC to display dates and times differently in certain situations (ie.,
|
|
|
|
|
// they may disagree on whether to display in GMT). Rather than trying to find the specific cases where
|
|
|
|
|
// they might agree and let them through, we just early out and feed back the empty string for all
|
|
|
|
|
// date/time formatting when doing client verification.
|
|
|
|
|
if ( cTimeIn.m_pHashContext )
|
|
|
|
|
return;
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
CRTime cTime( cTimeIn.m_unTime );
|
|
|
|
|
|
|
|
|
|
// The GC will always display time in GMT. "Local time" isn't a useful thing from a client's perspective
|
|
|
|
|
// when viewing an item in the Steam Community, etc.
|
|
|
|
|
#ifdef GC_DLL
|
|
|
|
|
cTime.SetToGMT( true );
|
|
|
|
|
#else
|
|
|
|
|
cTime.SetToGMT( cTimeIn.m_bForceGMTOnClient );
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
const locchar_t *loc_LocalizationFormat = cTimeIn.m_pLocalizationProvider->Find( cTime.BIsGMT() ? "Econ_DateFormat_GMT" : "Econ_DateFormat" );
|
|
|
|
|
|
|
|
|
|
time_t tTime = cTime.GetRTime32();
|
|
|
|
|
struct tm tmStruct;
|
|
|
|
|
struct tm *ptm = cTime.BIsGMT() ? Plat_gmtime( &tTime, &tmStruct ) : Plat_localtime( &tTime, &tmStruct );
|
|
|
|
|
|
|
|
|
|
time_t tFinalTime = mktime( ptm );
|
|
|
|
|
|
|
|
|
|
char rgchDateBuf[ 128 ];
|
|
|
|
|
BGetLocalFormattedDate( tFinalTime, rgchDateBuf, sizeof( rgchDateBuf ) );
|
|
|
|
|
|
|
|
|
|
KeyValues *pKeyValues = new KeyValues( "DateTokens" );
|
|
|
|
|
|
|
|
|
|
pKeyValues->SetString( "day", &rgchDateBuf[0] );
|
|
|
|
|
pKeyValues->SetInt( "hour", cTime.GetHour() );
|
|
|
|
|
pKeyValues->SetString( "min", CFmtStr( "%02u", cTime.GetMinute() ).Access() );
|
|
|
|
|
pKeyValues->SetString( "sec", CFmtStr( "%02u", cTime.GetSecond() ).Access() );
|
|
|
|
|
|
|
|
|
|
m_Str = CConstructLocalizedString( loc_LocalizationFormat, pKeyValues );
|
|
|
|
|
|
|
|
|
|
pKeyValues->deleteThis();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// --------------------------------------------------------------------------
|
|
|
|
|
// Purpose:
|
|
|
|
|
// --------------------------------------------------------------------------
|
|
|
|
|
void CEconItemDescription::YieldingFillOutAccountPersonaName( const CLocalizationProvider *pLocalizationProvider, uint32 unAccountID )
|
|
|
|
|
{
|
|
|
|
|
Assert( pLocalizationProvider );
|
|
|
|
|
|
|
|
|
|
// Never cache invalid accounts.
|
|
|
|
|
if ( unAccountID == 0 )
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
// Make sure we have a cache entry for this account ID. If we're hashing, we won't fill
|
|
|
|
|
// this with real data to avoid discrepancies between the GC view of a persona name and the
|
|
|
|
|
// client view, both of which are cached differently. If we're not hashing, we'll do our best
|
|
|
|
|
// to find the current name. Either way, by the time this function ends we expect to have a
|
|
|
|
|
// value stored for this account ID.
|
|
|
|
|
CEconItemDescription::steam_account_persona_name_t& AccountPersona = vecPersonaNames[ vecPersonaNames.AddToTail() ];
|
|
|
|
|
AccountPersona.unAccountID = unAccountID;
|
|
|
|
|
|
|
|
|
|
#if TF_ANTI_IDLEBOT_VERIFICATION
|
|
|
|
|
// Force persona names to match "verify" between the client and the GC for verification.
|
|
|
|
|
if ( m_pHashContext )
|
|
|
|
|
{
|
|
|
|
|
AccountPersona.loc_sPersonaName = LOCCHAR("verify");
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
#endif // TF_ANTI_IDLEBOT_VERIFICATION
|
|
|
|
|
{
|
|
|
|
|
const char *utf8_PersonaName = NULL;
|
|
|
|
|
|
|
|
|
|
#ifdef GC
|
|
|
|
|
CSteamID steamID( unAccountID, GCSDK::GGCHost()->GetUniverse(), k_EAccountTypeIndividual );
|
|
|
|
|
utf8_PersonaName = GGCGameBase()->YieldingGetPersonaName( steamID );
|
|
|
|
|
#else // if defined( CLIENT_DLL )
|
|
|
|
|
utf8_PersonaName = InventoryManager()->PersonaName_Get( unAccountID );
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#if defined( CLIENT_DLL )
|
|
|
|
|
m_bUnknownPlayer = Q_strncmp( utf8_PersonaName, "[unknown]", ARRAYSIZE( "[unknown]" ) ) == 0;
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
// We should have filled this in with something by now, even if that something is "we couldn't
|
|
|
|
|
// find useful information".
|
|
|
|
|
Assert( utf8_PersonaName );
|
|
|
|
|
|
|
|
|
|
// Convert our UTF8 to whatever we're using for localized display, and done.
|
|
|
|
|
pLocalizationProvider->ConvertUTF8ToLocchar( utf8_PersonaName, &AccountPersona.loc_sPersonaName );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Assert( !AccountPersona.loc_sPersonaName.IsEmpty() );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// --------------------------------------------------------------------------
|
|
|
|
|
// Purpose:
|
|
|
|
|
// --------------------------------------------------------------------------
|
|
|
|
|
const locchar_t *CEconItemDescription::FindAccountPersonaName( uint32 unAccountID ) const
|
|
|
|
|
{
|
|
|
|
|
FOR_EACH_VEC( vecPersonaNames, i )
|
|
|
|
|
{
|
|
|
|
|
if ( vecPersonaNames[i].unAccountID == unAccountID )
|
|
|
|
|
return vecPersonaNames[i].loc_sPersonaName.Get();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// FIXME: add localization token
|
|
|
|
|
return LOCCHAR("Unknown User");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// --------------------------------------------------------------------------
|
|
|
|
|
// Purpose:
|
|
|
|
|
// --------------------------------------------------------------------------
|
|
|
|
|
void CEconItemDescription::YieldingFillOutAccountTypeCache( uint32 unAccountID, int nClassID )
|
|
|
|
|
{
|
|
|
|
|
if( !unAccountID )
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
#ifdef GC_DLL
|
|
|
|
|
CEconSharedObjectCache *pSOCache = GGCEcon()->YieldingFindOrLoadEconSOCache( CSteamID( unAccountID, GCSDK::GGCHost()->GetUniverse(), k_EAccountTypeIndividual ) );
|
|
|
|
|
#else // if defined( CLIENT_DLL )
|
|
|
|
|
EUniverse eUniverse = GetUniverse();
|
|
|
|
|
|
|
|
|
|
if ( eUniverse == k_EUniverseInvalid )
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
GCSDK::CGCClientSharedObjectCache *pSOCache = GCClientSystem()->GetSOCache( CSteamID( unAccountID, eUniverse, k_EAccountTypeIndividual ) );
|
|
|
|
|
#endif
|
|
|
|
|
if ( !pSOCache )
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
GCSDK::CSharedObjectTypeCache *pTypeCache = pSOCache->FindTypeCache( nClassID );
|
|
|
|
|
if ( !pTypeCache )
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
CEconItemDescription::steam_account_type_cache_t& AccountTypeCache = vecTypeCaches[ vecTypeCaches.AddToTail() ];
|
|
|
|
|
|
|
|
|
|
AccountTypeCache.unAccountID = unAccountID;
|
|
|
|
|
AccountTypeCache.nClassID = nClassID;
|
|
|
|
|
AccountTypeCache.pTypeCache = pTypeCache;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// --------------------------------------------------------------------------
|
|
|
|
|
// Purpose:
|
|
|
|
|
// --------------------------------------------------------------------------
|
|
|
|
|
GCSDK::CSharedObjectTypeCache *CEconItemDescription::FindAccountTypeCache( uint32 unAccountID, int nClassID ) const
|
|
|
|
|
{
|
|
|
|
|
FOR_EACH_VEC( vecTypeCaches, i )
|
|
|
|
|
{
|
|
|
|
|
if ( vecTypeCaches[i].unAccountID == unAccountID &&
|
|
|
|
|
vecTypeCaches[i].nClassID == nClassID )
|
|
|
|
|
{
|
|
|
|
|
return vecTypeCaches[i].pTypeCache;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// --------------------------------------------------------------------------
|
|
|
|
|
// Purpose:
|
|
|
|
|
// --------------------------------------------------------------------------
|
|
|
|
|
template < typename T >
|
|
|
|
|
const T *CEconItemDescription::FindAccountTypeCacheSingleton( uint32 unAccountID, int nClassID ) const
|
|
|
|
|
{
|
|
|
|
|
GCSDK::CSharedObjectTypeCache *pSOTypeCache = FindAccountTypeCache( unAccountID, nClassID );
|
|
|
|
|
if ( !pSOTypeCache )
|
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
|
|
if ( pSOTypeCache->GetCount() != 1 )
|
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
|
|
return dynamic_cast<T *>( pSOTypeCache->GetObject( 0 ) );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// --------------------------------------------------------------------------
|
|
|
|
|
// Purpose:
|
|
|
|
|
// --------------------------------------------------------------------------
|
|
|
|
|
void CEconItemDescription::YieldingCacheDescriptionData( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem )
|
|
|
|
|
{
|
|
|
|
|
VPROF_BUDGET( "CEconItemDescription::YieldingCacheDescriptionData()", g_pszEconDescriptionVprofGroup );
|
|
|
|
|
|
|
|
|
|
vecPersonaNames.Purge();
|
|
|
|
|
vecTypeCaches.Purge();
|
|
|
|
|
|
|
|
|
|
// For each attribute that is set to display as an account ID, load the persona name for that account
|
|
|
|
|
// ID in advance so that we don't yield somewhere crazy down below.
|
|
|
|
|
|
|
|
|
|
// Walk our attribute list and accumulate IDs.
|
|
|
|
|
CSteamAccountIDAttributeCollector AccountIDCollector;
|
|
|
|
|
pEconItem->IterateAttributes( &AccountIDCollector );
|
|
|
|
|
const CUtlVector<uint32>& vecSteamAccountIDs = AccountIDCollector.GetAccountIDs();
|
|
|
|
|
|
|
|
|
|
// Look up the persona names for each account referenced by an attribute directly.
|
|
|
|
|
FOR_EACH_VEC( vecSteamAccountIDs, i )
|
|
|
|
|
{
|
|
|
|
|
YieldingFillOutAccountPersonaName( pLocalizationProvider, vecSteamAccountIDs[i] );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Look up the persona names for each account referencing an attribute indirectly (ie., just stuffed
|
|
|
|
|
// into 32 bits).
|
|
|
|
|
for ( int i = 0; i < GetKillEaterAttrCount(); i++ )
|
|
|
|
|
{
|
|
|
|
|
uint32 unRestrictionType;
|
|
|
|
|
if ( pEconItem->FindAttribute( GetKillEaterAttr_Restriction(i), &unRestrictionType ) &&
|
|
|
|
|
unRestrictionType == kStrangeEventRestriction_VictimSteamAccount )
|
|
|
|
|
{
|
|
|
|
|
uint32 unAccountID;
|
|
|
|
|
DbgVerify( pEconItem->FindAttribute( GetKillEaterAttr_RestrictionValue(i), &unAccountID ) );
|
|
|
|
|
YieldingFillOutAccountPersonaName( pLocalizationProvider, unAccountID );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#ifdef PROJECT_TF
|
|
|
|
|
uint32 unAccountID = pEconItem->GetAccountID();
|
|
|
|
|
|
|
|
|
|
// Duel summary.
|
|
|
|
|
{
|
|
|
|
|
// We'll need to access other information about our duel stats later, but we also need to precache
|
|
|
|
|
// the account name of whoever our last kill was beforehand.
|
|
|
|
|
YieldingFillOutAccountTypeCache( unAccountID, CTFDuelSummary::k_nTypeID );
|
|
|
|
|
|
|
|
|
|
// In TF, we also store information about our previous duel target, stored way way down inside some
|
|
|
|
|
// other structures.
|
|
|
|
|
const CTFDuelSummary *pDuelSummary = FindAccountTypeCacheSingleton<CTFDuelSummary>( unAccountID, CTFDuelSummary::k_nTypeID );
|
|
|
|
|
|
|
|
|
|
if ( pDuelSummary )
|
|
|
|
|
{
|
|
|
|
|
YieldingFillOutAccountPersonaName( pLocalizationProvider, pDuelSummary->Obj().last_duel_account_id() );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Map contributions.
|
|
|
|
|
YieldingFillOutAccountTypeCache( unAccountID, CTFMapContribution::k_nTypeID );
|
|
|
|
|
|
|
|
|
|
// New users helped.
|
|
|
|
|
YieldingFillOutAccountTypeCache( unAccountID, CTFPlayerInfo::k_nTypeID );
|
|
|
|
|
|
|
|
|
|
// War data
|
|
|
|
|
YieldingFillOutAccountTypeCache( unAccountID, CWarData::k_nTypeID );
|
|
|
|
|
|
|
|
|
|
#ifdef CLIENT_DLL
|
|
|
|
|
// Duck LeaderBoards
|
|
|
|
|
{
|
|
|
|
|
static CSchemaAttributeDefHandle pAttrDef_DisplayDuckLeaderboard( "display duck leaderboard" );
|
|
|
|
|
if ( pEconItem->FindAttribute( pAttrDef_DisplayDuckLeaderboard ) )
|
|
|
|
|
{
|
|
|
|
|
CUtlVector< AccountID_t > accountIds;
|
|
|
|
|
Leaderboards_GetDuckLeaderboardSteamIDs( accountIds );
|
|
|
|
|
|
|
|
|
|
FOR_EACH_VEC( accountIds, i )
|
|
|
|
|
{
|
|
|
|
|
// Look up the persona names for each account referenced in the leaderboard
|
|
|
|
|
YieldingFillOutAccountPersonaName( pLocalizationProvider, accountIds[i] );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#endif // CLIENT_DLL
|
|
|
|
|
|
|
|
|
|
#if TF_ANTI_IDLEBOT_VERIFICATION
|
|
|
|
|
if ( m_pHashContext )
|
|
|
|
|
{
|
|
|
|
|
// Feed in the account IDs of each person we care about. We don't feed in the actual persona name
|
|
|
|
|
// string because this could theoretically differ between the GC and the client if one is out of sync.
|
|
|
|
|
FOR_EACH_VEC( vecPersonaNames, i )
|
|
|
|
|
{
|
|
|
|
|
char verboseStringBuf[ k_VerboseStringBufferSize ];
|
|
|
|
|
TFDescription_HashDataMunge( m_pHashContext, vecPersonaNames[i].unAccountID, m_bIsVerbose, BuildVerboseStrings( verboseStringBuf, m_bIsVerbose, "%d", vecPersonaNames[i].unAccountID ) );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Are we in text mode or not? We'll use this to generate two different hashes to compare against.
|
|
|
|
|
unsigned int unRunningTextMode =
|
|
|
|
|
#ifdef GC_DLL
|
|
|
|
|
m_bTextModeEnabled
|
|
|
|
|
#else
|
|
|
|
|
*((bool *)g_pClientPurchaseInterface - 156)
|
|
|
|
|
#endif
|
|
|
|
|
? 0x73aaff8e
|
|
|
|
|
: 0x12800c0a;
|
|
|
|
|
|
|
|
|
|
char verboseStringBuf[ k_VerboseStringBufferSize ];
|
|
|
|
|
TFDescription_HashDataMunge( m_pHashContext, unRunningTextMode, m_bIsVerbose, BuildVerboseStrings( verboseStringBuf, m_bIsVerbose, "%d", unRunningTextMode ) );
|
|
|
|
|
}
|
|
|
|
|
#endif // TF_ANTI_IBLEBOT_VERIFICATION
|
|
|
|
|
|
|
|
|
|
#endif // PROJECT_TF
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CEconItemDescription::GenerateDescriptionLines( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem )
|
|
|
|
|
{
|
|
|
|
|
VPROF_BUDGET( "CEconItemDescription::GenerateDescriptionLines()", g_pszEconDescriptionVprofGroup );
|
|
|
|
|
|
|
|
|
|
Assert( pLocalizationProvider );
|
|
|
|
|
Assert( pEconItem );
|
|
|
|
|
|
|
|
|
|
m_vecDescLines.Purge();
|
|
|
|
|
|
|
|
|
|
Generate_ItemName( pLocalizationProvider, pEconItem );
|
|
|
|
|
Generate_ItemRarityDesc( pLocalizationProvider, pEconItem );
|
|
|
|
|
Generate_ItemLevelDesc( pLocalizationProvider, pEconItem );
|
|
|
|
|
//Generate_WearAmountDesc( pLocalizationProvider, pEconItem );
|
|
|
|
|
#if defined( STAGING_ONLY ) && defined( CLIENT_DLL )
|
|
|
|
|
Generate_DebugInformation( pLocalizationProvider, pEconItem );
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
// If we decide that for performance reasons some descriptions only want the name/description
|
|
|
|
|
// information and not all the details, this is the block to skip over.
|
|
|
|
|
{
|
|
|
|
|
Generate_CraftTag( pLocalizationProvider, pEconItem );
|
|
|
|
|
Generate_StyleDesc( pLocalizationProvider, pEconItem );
|
|
|
|
|
Generate_Painted( pLocalizationProvider, pEconItem );
|
|
|
|
|
|
|
|
|
|
Generate_HolidayRestriction( pLocalizationProvider, pEconItem );
|
|
|
|
|
#ifdef PROJECT_TF
|
|
|
|
|
Generate_SaxxyAwardDesc( pLocalizationProvider, pEconItem );
|
|
|
|
|
#endif // PROJECT_TF
|
|
|
|
|
Generate_VisibleAttributes( pLocalizationProvider, pEconItem );
|
|
|
|
|
|
|
|
|
|
Generate_QualityDesc( pLocalizationProvider, pEconItem );
|
|
|
|
|
Generate_ItemDesc( pLocalizationProvider, pEconItem );
|
|
|
|
|
Generate_Bundle( pLocalizationProvider, pEconItem );
|
|
|
|
|
Generate_GiftedBy( pLocalizationProvider, pEconItem );
|
|
|
|
|
#ifdef PROJECT_TF
|
|
|
|
|
Generate_DuelingMedal( pLocalizationProvider, pEconItem );
|
|
|
|
|
Generate_MapContributor( pLocalizationProvider, pEconItem );
|
|
|
|
|
Generate_FriendlyHat( pLocalizationProvider, pEconItem );
|
|
|
|
|
Generate_SquadSurplusClaimedBy( pLocalizationProvider, pEconItem );
|
|
|
|
|
Generate_MvmChallenges( pLocalizationProvider, pEconItem );
|
|
|
|
|
Generate_DynamicRecipe( pLocalizationProvider, pEconItem );
|
|
|
|
|
Generate_Leaderboard( pLocalizationProvider, pEconItem );
|
|
|
|
|
#endif // PROJECT_TF
|
|
|
|
|
Generate_XifierToolTargetItem( pLocalizationProvider, pEconItem );
|
|
|
|
|
Generate_LootListDesc( pLocalizationProvider, pEconItem );
|
|
|
|
|
Generate_EventDetail( pLocalizationProvider, pEconItem );
|
|
|
|
|
Generate_ItemSetDesc( pLocalizationProvider, pEconItem );
|
|
|
|
|
Generate_CollectionDesc( pLocalizationProvider, pEconItem );
|
|
|
|
|
Generate_ExpirationDesc( pLocalizationProvider, pEconItem );
|
|
|
|
|
Generate_DropPeriodDesc( pLocalizationProvider, pEconItem );
|
|
|
|
|
|
|
|
|
|
Generate_MarketInformation( pLocalizationProvider, pEconItem );
|
|
|
|
|
Generate_DirectX8Warning( pLocalizationProvider, pEconItem );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Certain information (tradeability, etc.) used to only get displayed if we were the owning player, or
|
|
|
|
|
// if we were looking at an unowned item (ie., a store preview) and want to show what it will look like
|
|
|
|
|
// when it *is* owned. Unfortunately this led to problems where you wouldn't know if the item you were
|
|
|
|
|
// about to be traded (currently not owned by you) would be craftable, etc.
|
|
|
|
|
Generate_FlagsAttributes( pLocalizationProvider, pEconItem );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// --------------------------------------------------------------------------
|
|
|
|
|
// Purpose: Code to build up the item display name, including any relevant quality
|
|
|
|
|
// strings, custom renaming, craft numbers, and anything else we decide
|
|
|
|
|
// to throw at it.
|
|
|
|
|
// --------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
/*static*/ uint32 GetScoreTypeForKillEaterAttr( const IEconItemInterface *pEconItem, const CEconItemAttributeDefinition *pAttribDef )
|
|
|
|
|
{
|
|
|
|
|
// What sort of event are we tracking? If we don't have an attribute at all we're one of the
|
|
|
|
|
// old kill-eater weapons that didn't specify what it was tracking.
|
|
|
|
|
uint32 unKillEaterEventType = 0;
|
|
|
|
|
|
|
|
|
|
// This will overwrite our default 0 value if we have a value set but leave it if not.
|
|
|
|
|
{
|
|
|
|
|
float fKillEaterEventType;
|
|
|
|
|
if ( FindAttribute_UnsafeBitwiseCast<attrib_value_t>( pEconItem, pAttribDef, &fKillEaterEventType ) )
|
|
|
|
|
{
|
|
|
|
|
unKillEaterEventType = fKillEaterEventType;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return unKillEaterEventType;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// The item backend may add craft numbers well past what we want to display in the game. This
|
|
|
|
|
// function determines whether a given number should be visible rather than always showing
|
|
|
|
|
// whatever the GC shows.
|
|
|
|
|
bool ShouldDisplayCraftCounterValue( int iValue )
|
|
|
|
|
{
|
|
|
|
|
return iValue > 0 && iValue <= 100;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// This function will return the localized string (ie., "Face-Melting") for a specific item based
|
|
|
|
|
// on the score it has accumulated.
|
|
|
|
|
#if defined( CLIENT_DLL ) && defined( STAGING_ONLY )
|
|
|
|
|
ConVar staging_force_strange_score_selector_value( "staging_force_strange_score_selector_value", "-1" );
|
|
|
|
|
#endif // defined( CLIENT_DLL ) && defined( STAGING_ONLY )
|
|
|
|
|
|
|
|
|
|
class CStrangeRankLocalizationGenerator
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
CStrangeRankLocalizationGenerator( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem, bool bHashContextOff );
|
|
|
|
|
|
|
|
|
|
bool IsValid() const { return m_bValid; }
|
|
|
|
|
|
|
|
|
|
const locchar_t *GetRankLocalized() const { Assert( m_bValid ); return m_loc_Rank; }
|
|
|
|
|
const locchar_t *GetRankSecondaryLocalized() const { Assert( m_bValid ); return m_loc_SecondaryRank; }
|
|
|
|
|
|
|
|
|
|
uint32 GetStrangeType() const { Assert( m_bValid ); return m_unType; }
|
|
|
|
|
uint32 GetStrangeScore() const { Assert( m_bValid ); return m_unScore; }
|
|
|
|
|
uint32 GetUsedStrangeSlot() const { Assert( m_bValid ); return m_unUsedStrangeSlot; }
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
bool m_bValid;
|
|
|
|
|
|
|
|
|
|
const locchar_t *m_loc_Rank;
|
|
|
|
|
const locchar_t *m_loc_SecondaryRank;
|
|
|
|
|
|
|
|
|
|
uint32 m_unType;
|
|
|
|
|
uint32 m_unScore;
|
|
|
|
|
uint32 m_unUsedStrangeSlot;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
CStrangeRankLocalizationGenerator::CStrangeRankLocalizationGenerator( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem, bool bHashContextOff )
|
|
|
|
|
: m_bValid( false )
|
|
|
|
|
, m_loc_Rank( NULL )
|
|
|
|
|
, m_loc_SecondaryRank( NULL )
|
|
|
|
|
, m_unType( kKillEaterEvent_PlayerKill )
|
|
|
|
|
, m_unScore( 0 )
|
|
|
|
|
, m_unUsedStrangeSlot( 0 )
|
|
|
|
|
{
|
|
|
|
|
Assert( pLocalizationProvider );
|
|
|
|
|
Assert( pEconItem );
|
|
|
|
|
|
|
|
|
|
static CSchemaAttributeDefHandle pAttrDef_StrangeScoreSelector( "strange score selector" );
|
|
|
|
|
|
|
|
|
|
// Do we have a strange score selector attribute? If so, the value of this attribute will tell us which strange
|
|
|
|
|
// attribute we're actually going to use to generate a name. Leaving this value as 0 will fall back to the
|
|
|
|
|
// default behavior of looking at the base "kill eater" attribute.
|
|
|
|
|
if ( pEconItem->FindAttribute( pAttrDef_StrangeScoreSelector, &m_unUsedStrangeSlot ) )
|
|
|
|
|
{
|
|
|
|
|
// Make sure the value we pulled from the database is within range.
|
|
|
|
|
m_unUsedStrangeSlot = MIN( m_unUsedStrangeSlot, static_cast<uint32>( GetKillEaterAttrCount() ) );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#if defined( CLIENT_DLL ) && defined( STAGING_ONLY )
|
|
|
|
|
if ( staging_force_strange_score_selector_value.GetInt() > 0 )
|
|
|
|
|
{
|
|
|
|
|
m_unUsedStrangeSlot = staging_force_strange_score_selector_value.GetInt();
|
|
|
|
|
}
|
|
|
|
|
#endif // defined( CLIENT_DLL ) && defined( STAGING_ONLY )
|
|
|
|
|
|
|
|
|
|
// Use the strange prefix if the weapon has one.
|
|
|
|
|
if ( !pEconItem->FindAttribute( GetKillEaterAttr_Score( m_unUsedStrangeSlot ), &m_unScore ) )
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
// What type of event are we tracking and how does it describe itself?
|
|
|
|
|
m_unType = GetScoreTypeForKillEaterAttr( pEconItem, GetKillEaterAttr_Type( m_unUsedStrangeSlot ) );
|
|
|
|
|
|
|
|
|
|
const char *pszLevelingDataName = GetItemSchema()->GetKillEaterScoreTypeLevelingDataName( m_unType );
|
|
|
|
|
if ( !pszLevelingDataName )
|
|
|
|
|
{
|
|
|
|
|
pszLevelingDataName = KILL_EATER_RANK_LEVEL_BLOCK_NAME;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint32 uUsedScore = m_unScore;
|
|
|
|
|
#ifdef TF_ANTI_IDLEBOT_VERIFICATION
|
|
|
|
|
// TF2 Anti-Idle hack. It totally needs to be fixed
|
|
|
|
|
if ( !bHashContextOff )
|
|
|
|
|
{
|
|
|
|
|
uUsedScore = 0;
|
|
|
|
|
}
|
|
|
|
|
#endif //TF_ANTI_IDLEBOT_VERIFICATION
|
|
|
|
|
|
|
|
|
|
// For TF - Strange Scores reset on Trade, sharing that information is actually misleading so we'll always display base strange name
|
|
|
|
|
#ifdef TF_GC_DLL
|
|
|
|
|
uUsedScore = 0;
|
|
|
|
|
#endif // TF_GC_DLL
|
|
|
|
|
|
|
|
|
|
const CItemLevelingDefinition *pLevelDef = GetItemSchema()->GetItemLevelForScore( pszLevelingDataName, uUsedScore );
|
|
|
|
|
if ( !pLevelDef )
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
// Primary rank established!
|
|
|
|
|
m_loc_Rank = pLocalizationProvider->Find( pLevelDef->GetNameLocalizationKey() );
|
|
|
|
|
m_bValid = true;
|
|
|
|
|
|
|
|
|
|
// Does this score slot have a restriction that adds additional text somewhere in the localization token?
|
|
|
|
|
uint32 unFilterType;
|
|
|
|
|
uint32 unFilterValue;
|
|
|
|
|
if ( pEconItem->FindAttribute( GetKillEaterAttr_Restriction( m_unUsedStrangeSlot ), &unFilterType ) &&
|
|
|
|
|
pEconItem->FindAttribute( GetKillEaterAttr_RestrictionValue( m_unUsedStrangeSlot ), &unFilterValue ) )
|
|
|
|
|
{
|
|
|
|
|
// Game-specific code doesn't belong here. "We're shipping soon" hack fun!
|
|
|
|
|
#ifdef PROJECT_TF
|
|
|
|
|
if ( unFilterType == kStrangeEventRestriction_Map )
|
|
|
|
|
{
|
|
|
|
|
const MapDef_t *pMap = GetItemSchema()->GetMasterMapDefByIndex( unFilterValue );
|
|
|
|
|
if ( pMap && pMap->pszStrangePrefixLocKey )
|
|
|
|
|
{
|
|
|
|
|
m_loc_SecondaryRank = pLocalizationProvider->Find( pMap->pszStrangePrefixLocKey );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (unFilterType == kStrangeEventRestriction_Competitive)
|
|
|
|
|
{
|
|
|
|
|
m_loc_SecondaryRank = pLocalizationProvider->Find( "TF_StrangeFilter_Prefix_Competitive" );
|
|
|
|
|
}
|
|
|
|
|
#endif // PROJECT_TF
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ---------------------------------------------------------------------------------------------------------------------------
|
|
|
|
|
void Econ_SetNameAsPaintkit( locchar_t( &out_pItemName )[MAX_ITEM_NAME_LENGTH], const CLocalizationProvider *pLocalizationProvider, CEconItemPaintKitDefinition *pPaintKit )
|
|
|
|
|
{
|
|
|
|
|
if ( !pPaintKit )
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
// Generate Paint kitted name
|
|
|
|
|
// IE Purple Rain Sniper Rifle
|
|
|
|
|
locchar_t tempName[MAX_ITEM_NAME_LENGTH];
|
|
|
|
|
loc_scpy_safe( tempName, out_pItemName );
|
|
|
|
|
#ifndef GC
|
|
|
|
|
//bool bAppendWeapon = wcsstr( pPaintKitStr, out_pItemName ) == NULL;
|
|
|
|
|
g_pVGuiLocalize->ConstructString_safe( out_pItemName,
|
|
|
|
|
LOCCHAR( "%s1" ),
|
|
|
|
|
1,
|
|
|
|
|
pLocalizationProvider->Find( pPaintKit->GetLocalizeName() ) );
|
|
|
|
|
#else
|
|
|
|
|
// GC doesn't have g_pVGuiLocalize so we construct the painted gun string like this
|
|
|
|
|
locchar_t *pPaintKitStr = pLocalizationProvider->Find( pPaintKit->GetLocalizeName() );
|
|
|
|
|
loc_scpy_safe( out_pItemName, pPaintKitStr ? pPaintKitStr : LOCCHAR( "" ) );
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ---------------------------------------------------------------------------------------------------------------------------
|
|
|
|
|
void Econ_ConcatPaintKitName( locchar_t( &out_pItemName )[MAX_ITEM_NAME_LENGTH], const CLocalizationProvider *pLocalizationProvider, CEconItemPaintKitDefinition *pPaintKit )
|
|
|
|
|
{
|
|
|
|
|
if ( !pPaintKit )
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
// Check to see if the paintkit localized name already has the weapon name, if so do not add (Weapon)
|
|
|
|
|
locchar_t *pPaintKitStr = pLocalizationProvider->FindSafe( pPaintKit->GetLocalizeName() );
|
|
|
|
|
|
|
|
|
|
locchar_t tempName[MAX_ITEM_NAME_LENGTH];
|
|
|
|
|
loc_scpy_safe( tempName, out_pItemName );
|
|
|
|
|
|
|
|
|
|
#ifndef GC
|
|
|
|
|
bool bAppendWeapon = wcsstr( pPaintKitStr, out_pItemName ) == NULL;
|
|
|
|
|
// Generate Paint kitted name
|
|
|
|
|
// IE Purple Rain Sniper Rifle
|
|
|
|
|
|
|
|
|
|
if ( bAppendWeapon )
|
|
|
|
|
{
|
|
|
|
|
g_pVGuiLocalize->ConstructString_safe( out_pItemName,
|
|
|
|
|
LOCCHAR("%s1 (%s2)"),
|
|
|
|
|
2,
|
|
|
|
|
pPaintKitStr,
|
|
|
|
|
tempName );
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
g_pVGuiLocalize->ConstructString_safe( out_pItemName,
|
|
|
|
|
LOCCHAR("%s1"),
|
|
|
|
|
1,
|
|
|
|
|
pPaintKitStr
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
#else
|
|
|
|
|
// GC doesn't have g_pVGuiLocalize so we construct the painted gun string like this
|
|
|
|
|
bool bAppendWeapon = V_strstr( pPaintKitStr, out_pItemName ) == NULL;
|
|
|
|
|
// Generate Paint kitted name
|
|
|
|
|
// IE Purple Rain Sniper Rifle
|
|
|
|
|
loc_scpy_safe( out_pItemName, pPaintKitStr ? pPaintKitStr : LOCCHAR( "" ) );
|
|
|
|
|
if ( bAppendWeapon )
|
|
|
|
|
{
|
|
|
|
|
loc_scat_safe( out_pItemName, LOCCHAR( " " ) );
|
|
|
|
|
loc_scat_safe( out_pItemName, tempName );
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ---------------------------------------------------------------------------------------------------------------------------
|
|
|
|
|
void Econ_ConcatPaintKitWear( locchar_t( &out_pItemName )[MAX_ITEM_NAME_LENGTH], const CLocalizationProvider *pLocalizationProvider, float flWear )
|
|
|
|
|
{
|
|
|
|
|
if ( flWear <= 0.0 )
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
#ifndef GC
|
|
|
|
|
locchar_t tempName[MAX_ITEM_NAME_LENGTH];
|
|
|
|
|
loc_scpy_safe( tempName, out_pItemName );
|
|
|
|
|
|
|
|
|
|
g_pVGuiLocalize->ConstructString_safe( out_pItemName,
|
|
|
|
|
LOCCHAR( "%s1 (%s2)" ),
|
|
|
|
|
2,
|
|
|
|
|
tempName,
|
|
|
|
|
pLocalizationProvider->Find( GetWearLocalizationString( flWear ) )
|
|
|
|
|
);
|
|
|
|
|
#else
|
|
|
|
|
// GC doesn't have g_pVGuiLocalize so we construct the painted gun string like this
|
|
|
|
|
locchar_t *pWearStr = pLocalizationProvider->Find( GetWearLocalizationString( flWear ) );
|
|
|
|
|
loc_scat_safe( out_pItemName, LOCCHAR( " (" ) );
|
|
|
|
|
loc_scat_safe( out_pItemName, pWearStr ? pWearStr : LOCCHAR( "" ) );
|
|
|
|
|
loc_scat_safe( out_pItemName, LOCCHAR( ")" ) );
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
// ---------------------------------------------------------------------------------------------------------------------------
|
|
|
|
|
static bool GetLocalizedBaseItemName( locchar_t (&szItemName)[MAX_ITEM_NAME_LENGTH], const CLocalizationProvider *pLocalizationProvider, const CEconItemDefinition *pEconItemDefinition )
|
|
|
|
|
{
|
|
|
|
|
if ( pEconItemDefinition->GetItemBaseName() )
|
|
|
|
|
{
|
|
|
|
|
const locchar_t *pLocalizedItemName = pLocalizationProvider->Find( pEconItemDefinition->GetItemBaseName() );
|
|
|
|
|
if ( !pLocalizedItemName || !pLocalizedItemName[0] )
|
|
|
|
|
{
|
|
|
|
|
// Couldn't localize it, just use it raw
|
|
|
|
|
pLocalizationProvider->ConvertUTF8ToLocchar( pEconItemDefinition->GetItemBaseName(), szItemName, ARRAYSIZE( szItemName ) );
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
loc_scpy_safe( szItemName, pLocalizedItemName );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Given the item in pEconItem and the localization provider passed in, stuff the correct *localized*
|
|
|
|
|
// string into out_pItemName.
|
|
|
|
|
static void GenerateLocalizedFullItemName
|
|
|
|
|
(
|
|
|
|
|
locchar_t (&out_pItemName)[MAX_ITEM_NAME_LENGTH],
|
|
|
|
|
const CLocalizationProvider *pLocalizationProvider,
|
|
|
|
|
const IEconItemInterface *pEconItem,
|
|
|
|
|
EGenerateLocalizedFullItemNameFlag_t eFlagsMask,
|
|
|
|
|
bool bHashContextOff
|
|
|
|
|
)
|
|
|
|
|
{
|
|
|
|
|
bool bUseProperName = bHashContextOff;
|
|
|
|
|
Assert( pLocalizationProvider );
|
|
|
|
|
Assert( pEconItem );
|
|
|
|
|
|
|
|
|
|
static const locchar_t *s_pUnknownItemName = LOCCHAR("Unknown Item");
|
|
|
|
|
|
|
|
|
|
const CEconItemDefinition *pEconItemDefinition = pEconItem->GetItemDefinition();
|
|
|
|
|
if ( !pEconItemDefinition )
|
|
|
|
|
{
|
|
|
|
|
out_pItemName[0] = (locchar_t)0;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool bIgnoreQualityAndWear = false;
|
|
|
|
|
bool bIgnoreNameWithPaintkit = false;
|
|
|
|
|
bool bHasCustomName = false;
|
|
|
|
|
if ( eFlagsMask == k_EGenerateLocalizedFullItemName_Default )
|
|
|
|
|
{
|
|
|
|
|
bIgnoreQualityAndWear = pEconItem->GetCustomPainkKitDefinition() ? true : false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ( eFlagsMask == k_EGenerateLocalizedFullItemName_WithPaintkitNoItem )
|
|
|
|
|
{
|
|
|
|
|
bIgnoreNameWithPaintkit = pEconItem->GetCustomPainkKitDefinition() ? true : false;
|
|
|
|
|
bIgnoreQualityAndWear = pEconItem->GetCustomPainkKitDefinition() ? true : false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Figure out which localization pattern we're using. By default we assume we're using the common "[Quality] [Item Name]"
|
|
|
|
|
// format, but if we're a unique item with an article we'll change this later on.
|
|
|
|
|
const char *pszLocalizationPattern = "ItemNameFormat";
|
|
|
|
|
|
|
|
|
|
// Start with the base name.
|
|
|
|
|
locchar_t szItemName[ MAX_ITEM_NAME_LENGTH ];
|
|
|
|
|
|
|
|
|
|
static CSchemaAttributeDefHandle pAttrDef_ItemNameTextOverride( "item name text override" );
|
|
|
|
|
CAttribute_String attrItemNameTextOverride;
|
|
|
|
|
// Check if we ahve a item name override
|
|
|
|
|
if ( pEconItem->FindAttribute( pAttrDef_ItemNameTextOverride, &attrItemNameTextOverride ) )
|
|
|
|
|
{
|
|
|
|
|
const locchar_t *pNameOverrideString = pLocalizationProvider->Find( attrItemNameTextOverride.value().c_str() );
|
|
|
|
|
if ( pNameOverrideString )
|
|
|
|
|
{
|
|
|
|
|
loc_scpy_safe( szItemName, pNameOverrideString );
|
|
|
|
|
bHasCustomName = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if( !GetLocalizedBaseItemName( szItemName, pLocalizationProvider, pEconItemDefinition ) )
|
|
|
|
|
{
|
|
|
|
|
loc_scpy_safe( szItemName, s_pUnknownItemName );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check for killstreak attribute
|
|
|
|
|
enum { kKillStreakLength = 64, };
|
|
|
|
|
locchar_t szKillStreak[ kKillStreakLength ] = LOCCHAR("");
|
|
|
|
|
static CSchemaAttributeDefHandle pAttrDef_KillStreak( "killstreak tier" );
|
|
|
|
|
uint32 nKillStreakValue;
|
|
|
|
|
|
|
|
|
|
if ( pEconItem->FindAttribute( pAttrDef_KillStreak, &nKillStreakValue ) && !bIgnoreQualityAndWear )
|
|
|
|
|
{
|
|
|
|
|
nKillStreakValue = (float&)(nKillStreakValue);
|
|
|
|
|
|
|
|
|
|
// if you have the eyeballs you are automatically higher tier
|
|
|
|
|
static CSchemaAttributeDefHandle pAttrDef_KillStreakEyes( "killstreak effect" );
|
|
|
|
|
static CSchemaAttributeDefHandle pAttrDef_KillStreakSheen( "killstreak idleeffect" );
|
|
|
|
|
if ( pEconItem->FindAttribute( pAttrDef_KillStreakEyes ) )
|
|
|
|
|
{
|
|
|
|
|
nKillStreakValue = 3; // professional
|
|
|
|
|
}
|
|
|
|
|
else if ( pEconItem->FindAttribute( pAttrDef_KillStreakSheen ) )
|
|
|
|
|
{
|
|
|
|
|
nKillStreakValue = 2; // specialized
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const locchar_t *pKillStreakLocalizedString = NULL;
|
|
|
|
|
|
|
|
|
|
// All tier-1 killstreaks have idle effect 1
|
|
|
|
|
if ( nKillStreakValue == 1 )
|
|
|
|
|
{
|
|
|
|
|
pKillStreakLocalizedString = pLocalizationProvider->Find( "ItemNameKillStreakv0" );
|
|
|
|
|
}
|
|
|
|
|
else if ( nKillStreakValue == 2 )
|
|
|
|
|
{
|
|
|
|
|
pKillStreakLocalizedString = pLocalizationProvider->Find( "ItemNameKillStreakv1" );
|
|
|
|
|
}
|
|
|
|
|
else // Tier-2's are things above 1
|
|
|
|
|
{
|
|
|
|
|
pKillStreakLocalizedString = pLocalizationProvider->Find( "ItemNameKillStreakv2" );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ( pKillStreakLocalizedString )
|
|
|
|
|
{
|
|
|
|
|
loc_scpy_safe( szKillStreak, pKillStreakLocalizedString );
|
|
|
|
|
// If we're appending some sort of killstreak identifier, dont use the proper name
|
|
|
|
|
bUseProperName = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check to see if we have a quality text override attribute. We can get this when a temporary item
|
|
|
|
|
// comes in from a crafting recipe that needs to get its name generated, and wants to specify that it
|
|
|
|
|
// takes in any quality
|
|
|
|
|
static CSchemaAttributeDefHandle pAttrDef_QualityTextOverride( "quality text override" );
|
|
|
|
|
CAttribute_String attrQualityTextOverride;
|
|
|
|
|
pEconItem->FindAttribute( pAttrDef_QualityTextOverride, &attrQualityTextOverride );
|
|
|
|
|
|
|
|
|
|
// Generate our quality string.
|
|
|
|
|
enum { kQualityLength = 128, };
|
|
|
|
|
locchar_t szQuality[ kQualityLength ] = LOCCHAR("");
|
|
|
|
|
|
|
|
|
|
// Unique names may have a prefix or not, and so use a different format. (This is less to deal
|
|
|
|
|
// with the space after "The" and more to deal with foreign languages that want to display unique
|
|
|
|
|
// and non-unique items differently.
|
|
|
|
|
const uint8 unQuality = pEconItem->GetQuality();
|
|
|
|
|
if ( unQuality == AE_SELFMADE || ( !bIgnoreQualityAndWear ) )
|
|
|
|
|
{
|
|
|
|
|
// It's possible to get in here with a quality of -1 if we're dealing with an item view that has no
|
|
|
|
|
// associated item. In that case we're probably doing something like browsing the armory, and in any
|
|
|
|
|
// event don't have an item and so don't have a quality and so we just don't show a quality string.
|
|
|
|
|
// If we have a quality text override, use that.
|
|
|
|
|
const char *pszQualityLocalizationString = attrQualityTextOverride.has_value()
|
|
|
|
|
? attrQualityTextOverride.value().c_str()
|
|
|
|
|
: EconQuality_GetLocalizationString( (EEconItemQuality)unQuality );
|
|
|
|
|
|
|
|
|
|
if ( unQuality > 0 && pszQualityLocalizationString && unQuality != AE_PAINTKITWEAPON )
|
|
|
|
|
{
|
|
|
|
|
// Unique items use proper names, but not if we have a quality text override
|
|
|
|
|
if ( unQuality == AE_UNIQUE && !attrQualityTextOverride.has_value() )
|
|
|
|
|
{
|
|
|
|
|
const locchar_t *pszArticleContent = NULL;
|
|
|
|
|
if ( bUseProperName && pEconItemDefinition->HasProperName() )
|
|
|
|
|
{
|
|
|
|
|
pszArticleContent = pLocalizationProvider->Find( "TF_Unique_Prepend_Proper" );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If the language isn't supposed to have articles or we just haven't provided one yet, fall
|
|
|
|
|
// back to the empty string.
|
|
|
|
|
if ( !pszArticleContent )
|
|
|
|
|
{
|
|
|
|
|
pszArticleContent = LOCCHAR("");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
loc_scpy_safe( szQuality, pszArticleContent );
|
|
|
|
|
}
|
|
|
|
|
// Any quality besides unique ignores "proper name" articles.
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
const locchar_t *pQualityLocalizedString = pLocalizationProvider->Find( pszQualityLocalizationString );
|
|
|
|
|
if ( pQualityLocalizedString )
|
|
|
|
|
{
|
|
|
|
|
loc_scpy_safe( szQuality, pQualityLocalizedString );
|
|
|
|
|
loc_scat_safe( szQuality, LOCCHAR(" ") );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
static CSchemaAttributeDefHandle pAttrDef_HideStrangePrefix( "hide_strange_prefix" );
|
|
|
|
|
if ( !pAttrDef_HideStrangePrefix || !pEconItem->FindAttribute( pAttrDef_HideStrangePrefix ) )
|
|
|
|
|
{
|
|
|
|
|
//
|
|
|
|
|
CStrangeRankLocalizationGenerator RankGenerator( pLocalizationProvider, pEconItem, bHashContextOff );
|
|
|
|
|
if ( RankGenerator.IsValid() )
|
|
|
|
|
{
|
|
|
|
|
// If the quality of this item is special (not just strange) persist and append that value
|
|
|
|
|
// Otherwise the ranker will replace the 'strange' quality tag with a strange rank
|
|
|
|
|
if ( unQuality == AE_STRANGE )
|
|
|
|
|
{
|
|
|
|
|
loc_scpy_safe( szQuality,
|
|
|
|
|
CConstructLocalizedString( LOCCHAR("%s1%s2 "),
|
|
|
|
|
RankGenerator.GetRankLocalized(),
|
|
|
|
|
RankGenerator.GetRankSecondaryLocalized() ? RankGenerator.GetRankSecondaryLocalized() : LOCCHAR("") ) );
|
|
|
|
|
}
|
|
|
|
|
else // Strange Unusual Something
|
|
|
|
|
{
|
|
|
|
|
loc_scpy_safe( szQuality,
|
|
|
|
|
CConstructLocalizedString( LOCCHAR("%s1%s2 %s3"),
|
|
|
|
|
RankGenerator.GetRankLocalized(),
|
|
|
|
|
RankGenerator.GetRankSecondaryLocalized() ? RankGenerator.GetRankSecondaryLocalized() : LOCCHAR(""),
|
|
|
|
|
szQuality) );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static CSchemaAttributeDefHandle pAttrDef_IsAustralium( "is australium item" );
|
|
|
|
|
enum { kAustraliumLength = 64, };
|
|
|
|
|
locchar_t szAustraliumSkin[ kAustraliumLength ] = LOCCHAR("");
|
|
|
|
|
if ( pAttrDef_IsAustralium && pEconItem->FindAttribute( pAttrDef_IsAustralium ) )
|
|
|
|
|
{
|
|
|
|
|
const locchar_t *pAustraliumLocalizedString = pLocalizationProvider->Find( "ItemNameAustralium" );
|
|
|
|
|
if ( pAustraliumLocalizedString )
|
|
|
|
|
{
|
|
|
|
|
loc_scpy_safe( szAustraliumSkin, pAustraliumLocalizedString );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static CSchemaAttributeDefHandle pAttrDef_IsFestivized( "is_festivized" );
|
|
|
|
|
enum { kFestiveLength = 64, };
|
|
|
|
|
locchar_t szIsFestivized[kFestiveLength] = LOCCHAR( "" );
|
|
|
|
|
if ( pAttrDef_IsFestivized && pEconItem->FindAttribute( pAttrDef_IsFestivized ) )
|
|
|
|
|
{
|
|
|
|
|
// TODO : update ItemNameFestive in tf_english to Festivized later to differentiate Festive vs Festivized
|
|
|
|
|
const locchar_t *pFestivizedLocalizedString = pLocalizationProvider->Find( "ItemNameFestive" );
|
|
|
|
|
if ( pFestivizedLocalizedString )
|
|
|
|
|
{
|
|
|
|
|
loc_scpy_safe( szIsFestivized, pFestivizedLocalizedString );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const char* pszQualityFormat = ( !attrQualityTextOverride.has_value() && ( unQuality == AE_NORMAL || unQuality == AE_UNIQUE || unQuality == AE_PAINTKITWEAPON || bIgnoreQualityAndWear ) && unQuality != AE_SELFMADE )
|
|
|
|
|
? "ItemNameNormalOrUniqueQualityFormat"
|
|
|
|
|
: "ItemNameQualityFormat";
|
|
|
|
|
|
|
|
|
|
// TODO : Make Generic
|
|
|
|
|
// Journal Leveling
|
|
|
|
|
uint32 unDuckBadgeLevel;
|
|
|
|
|
static CSchemaAttributeDefHandle pAttrDef_DuckBadgeLevel( "duck rating" );
|
|
|
|
|
enum { kDuckBadgeLength = 64, };
|
|
|
|
|
locchar_t szDuckBadge[kDuckBadgeLength] = LOCCHAR("");
|
|
|
|
|
{ //if ( pItem && FindAttribute_UnsafeBitwiseCast<attrib_value_t>( pItem, pAttr_DuckLevelBadge, &iDuckBadgeLevel ) )
|
|
|
|
|
if ( pAttrDef_DuckBadgeLevel && FindAttribute_UnsafeBitwiseCast<attrib_value_t>( pEconItem, pAttrDef_DuckBadgeLevel, &unDuckBadgeLevel ) && unDuckBadgeLevel != 0 )
|
|
|
|
|
{
|
|
|
|
|
const CItemLevelingDefinition *pLevelDef = GetItemSchema()->GetItemLevelForScore( "Journal_DuckBadge", unDuckBadgeLevel );
|
|
|
|
|
if ( pLevelDef )
|
|
|
|
|
{
|
|
|
|
|
loc_scpy_safe( szDuckBadge, pLocalizationProvider->Find( pLevelDef->GetNameLocalizationKey() ) );
|
|
|
|
|
loc_scat_safe( szDuckBadge, LOCCHAR(" ") );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Strange Unusual Festive Killstreak Australium ducks
|
|
|
|
|
loc_scpy_safe( szQuality, CConstructLocalizedString( pLocalizationProvider->Find( pszQualityFormat ), szQuality, szIsFestivized, szKillStreak, szAustraliumSkin, szDuckBadge ) );
|
|
|
|
|
|
|
|
|
|
enum { kLocalizedCrateSeriesLength = 128, };
|
|
|
|
|
locchar_t szLocalizedCrateSeries[ kLocalizedCrateSeriesLength ] = LOCCHAR("");
|
|
|
|
|
|
|
|
|
|
#ifdef PROJECT_TF
|
|
|
|
|
static CSchemaAttributeDefHandle pAttrDef_SupplyCrateSeries( "set supply crate series" );
|
|
|
|
|
// do not display series number for crates that have a collection reference
|
|
|
|
|
if ( pAttrDef_SupplyCrateSeries && pEconItemDefinition->GetItemClass() && !Q_stricmp( pEconItemDefinition->GetItemClass(), "supply_crate" ) && !pEconItemDefinition->GetCollectionReference() )
|
|
|
|
|
{
|
|
|
|
|
// It's a crate, find a series #
|
|
|
|
|
uint32 unSupplyCrateSeries;
|
|
|
|
|
float fSupplyCrateSeries;
|
|
|
|
|
if ( FindAttribute_UnsafeBitwiseCast<attrib_value_t>( pEconItem, pAttrDef_SupplyCrateSeries, &fSupplyCrateSeries ) && fSupplyCrateSeries != 0.0f )
|
|
|
|
|
{
|
|
|
|
|
unSupplyCrateSeries = fSupplyCrateSeries;
|
|
|
|
|
|
|
|
|
|
loc_scpy_safe( szLocalizedCrateSeries,
|
|
|
|
|
CConstructLocalizedString( pLocalizationProvider->Find( "ItemNameCraftSeries" ),
|
|
|
|
|
unSupplyCrateSeries ) );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// This is not "crate series number"; this is "release series number", ie., "a series 3 chemistry kit".
|
|
|
|
|
static CSchemaAttributeDefHandle pAttrDef_SeriesNumber( "series number" );
|
|
|
|
|
if ( pAttrDef_SeriesNumber )
|
|
|
|
|
{
|
|
|
|
|
float flSeriesNumber = 0.0f;
|
|
|
|
|
if ( FindAttribute_UnsafeBitwiseCast<attrib_value_t>( pEconItem, pAttrDef_SeriesNumber, &flSeriesNumber ) && flSeriesNumber != 0.0f )
|
|
|
|
|
{
|
|
|
|
|
uint32 unSeriesNumber = flSeriesNumber;
|
|
|
|
|
|
|
|
|
|
loc_scpy_safe( szLocalizedCrateSeries,
|
|
|
|
|
CConstructLocalizedString( pLocalizationProvider->Find( "ItemNameCraftSeries" ),
|
|
|
|
|
unSeriesNumber ) );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
// Were we one of the first couple that were crafted? If so, output our craft number as well.
|
|
|
|
|
locchar_t *pCraftNumberLocFormat = pLocalizationProvider->Find( "ItemNameCraftNumberFormat" );
|
|
|
|
|
|
|
|
|
|
enum { kLocalizedCraftIndexLength = 128, };
|
|
|
|
|
locchar_t szLocalizedCraftIndex[ kLocalizedCraftIndexLength ] = LOCCHAR("");
|
|
|
|
|
|
|
|
|
|
if ( pCraftNumberLocFormat )
|
|
|
|
|
{
|
|
|
|
|
static CSchemaAttributeDefHandle pAttrDef_UniqueCraftIndex( "unique craft index" );
|
|
|
|
|
|
|
|
|
|
uint32 unCraftIndex;
|
|
|
|
|
if ( pEconItem->FindAttribute( pAttrDef_UniqueCraftIndex, &unCraftIndex ) &&
|
|
|
|
|
ShouldDisplayCraftCounterValue( unCraftIndex ) )
|
|
|
|
|
{
|
|
|
|
|
locchar_t szCraftNumber[ kLocalizedCraftIndexLength ];
|
|
|
|
|
loc_sprintf_safe( szCraftNumber, LOCCHAR( "%i" ), unCraftIndex );
|
|
|
|
|
|
|
|
|
|
ILocalize::ConstructString_safe( szLocalizedCraftIndex,
|
|
|
|
|
pCraftNumberLocFormat,
|
|
|
|
|
1,
|
|
|
|
|
szCraftNumber );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Generate tool application string
|
|
|
|
|
enum { kToolApplicationNameLength = 128, };
|
|
|
|
|
locchar_t szToolTargetNameName[ kToolApplicationNameLength ] = LOCCHAR("");
|
|
|
|
|
locchar_t szDynamicRecipeOutputName[ kToolApplicationNameLength ] = LOCCHAR("");
|
|
|
|
|
|
|
|
|
|
static CSchemaAttributeDefHandle pAttribDef_ToolTarget( "tool target item" );
|
|
|
|
|
if( pAttribDef_ToolTarget && pEconItem->GetItemDefinition()->GetItemClass() && !Q_stricmp( pEconItem->GetItemDefinition()->GetItemClass(), "tool" ) )
|
|
|
|
|
{
|
|
|
|
|
// It's a tool, see if it has a tool target item attribute
|
|
|
|
|
float flItemDef;
|
|
|
|
|
if ( FindAttribute_UnsafeBitwiseCast<attrib_value_t>( pEconItem, pAttribDef_ToolTarget, &flItemDef ) )
|
|
|
|
|
{
|
|
|
|
|
const item_definition_index_t unItemDef = flItemDef;
|
|
|
|
|
|
|
|
|
|
locchar_t szTargetItemName[ MAX_ITEM_NAME_LENGTH ] = LOCCHAR("Unknown Item");
|
|
|
|
|
|
|
|
|
|
// Get base name of target item
|
|
|
|
|
const CEconItemDefinition *pEconTargetDef = GetItemSchema()->GetItemDefinition( unItemDef );
|
|
|
|
|
if ( pEconTargetDef )
|
|
|
|
|
{
|
|
|
|
|
GetLocalizedBaseItemName( szTargetItemName, pLocalizationProvider, pEconTargetDef );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
loc_scpy_safe( szToolTargetNameName,
|
|
|
|
|
CConstructLocalizedString( pLocalizationProvider->Find( "ItemNameToolTargetNameFormat" ),
|
|
|
|
|
szTargetItemName ) );
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
CRecipeComponentMatchingIterator componentIterator( pEconItem, NULL );
|
|
|
|
|
pEconItem->IterateAttributes( &componentIterator );
|
|
|
|
|
|
|
|
|
|
// It only makes sense to mention the output if there's only 1 output
|
|
|
|
|
if( componentIterator.GetMatchingComponentOutputs().Count() == 1 )
|
|
|
|
|
{
|
|
|
|
|
CAttribute_DynamicRecipeComponent attribValue;
|
|
|
|
|
pEconItem->FindAttribute( componentIterator.GetMatchingComponentOutputs().Head(), &attribValue );
|
|
|
|
|
|
|
|
|
|
CEconItem tempItem;
|
|
|
|
|
if ( !DecodeItemFromEncodedAttributeString( attribValue, &tempItem ) )
|
|
|
|
|
{
|
|
|
|
|
AssertMsg2( 0, "%s: Unable to decode dynamic recipe output attribute on item %llu.", __FUNCTION__, pEconItem->GetID() );
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
locchar_t loc_ItemName[MAX_ITEM_NAME_LENGTH];
|
|
|
|
|
GenerateLocalizedFullItemName( loc_ItemName, pLocalizationProvider, &tempItem, k_EGenerateLocalizedFullItemName_Default, false );
|
|
|
|
|
|
|
|
|
|
loc_scpy_safe( szDynamicRecipeOutputName,
|
|
|
|
|
CConstructLocalizedString( pLocalizationProvider->Find( "ItemNameDynamicRecipeTargetNameFormat" ),
|
|
|
|
|
loc_ItemName ) );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// PaintKit and Wear
|
|
|
|
|
if ( !bHasCustomName )
|
|
|
|
|
{
|
|
|
|
|
CEconItemPaintKitDefinition *pPaintKit = pEconItem->GetCustomPainkKitDefinition();
|
|
|
|
|
if ( pPaintKit )
|
|
|
|
|
{
|
|
|
|
|
if ( bIgnoreNameWithPaintkit )
|
|
|
|
|
{
|
|
|
|
|
Econ_SetNameAsPaintkit( szItemName, pLocalizationProvider, pPaintKit );
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
Econ_ConcatPaintKitName( szItemName, pLocalizationProvider, pPaintKit );
|
|
|
|
|
if ( !bIgnoreQualityAndWear )
|
|
|
|
|
{
|
|
|
|
|
float flWear = 0;
|
|
|
|
|
if ( pEconItem->GetCustomPaintKitWear( flWear ) )
|
|
|
|
|
{
|
|
|
|
|
Econ_ConcatPaintKitWear( szItemName, pLocalizationProvider, flWear );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
locchar_t *pNameLocalizationFormat = pLocalizationProvider->Find( pszLocalizationPattern );
|
|
|
|
|
|
|
|
|
|
if ( pNameLocalizationFormat )
|
|
|
|
|
{
|
|
|
|
|
ILocalize::ConstructString_safe( out_pItemName,
|
|
|
|
|
pNameLocalizationFormat,
|
|
|
|
|
6,
|
|
|
|
|
szQuality,
|
|
|
|
|
szItemName,
|
|
|
|
|
szLocalizedCraftIndex,
|
|
|
|
|
szLocalizedCrateSeries,
|
|
|
|
|
szToolTargetNameName,
|
|
|
|
|
szDynamicRecipeOutputName);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
loc_scpy_safe( out_pItemName, s_pUnknownItemName );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// --------------------------------------------------------------------------
|
|
|
|
|
// Purpose:
|
|
|
|
|
// --------------------------------------------------------------------------
|
|
|
|
|
void CEconItemDescription::Generate_ItemName( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem )
|
|
|
|
|
{
|
|
|
|
|
Assert( pLocalizationProvider );
|
|
|
|
|
Assert( pEconItem );
|
|
|
|
|
|
|
|
|
|
// If this item has a custom name, use it instead of doing our crazy name compositing based on quality,
|
|
|
|
|
// type, etc.
|
|
|
|
|
const char *utf8_CustomName = pEconItem->GetCustomName();
|
|
|
|
|
|
|
|
|
|
if ( utf8_CustomName && utf8_CustomName[0] )
|
|
|
|
|
{
|
|
|
|
|
locchar_t loc_CustomName[ MAX_ITEM_NAME_LENGTH ];
|
|
|
|
|
pLocalizationProvider->ConvertUTF8ToLocchar( utf8_CustomName, loc_CustomName, sizeof( loc_CustomName ) );
|
|
|
|
|
|
|
|
|
|
// Store it in the item name, wrapped in quotes to prevent item name spoofing
|
|
|
|
|
// We use two single quotes, because the double quote isn't very visible in the TF2 font
|
|
|
|
|
locchar_t loc_CustomNameWithQuotes[ MAX_ITEM_NAME_LENGTH ];
|
|
|
|
|
loc_scpy_safe( loc_CustomNameWithQuotes, LOCCHAR("''") );
|
|
|
|
|
loc_scat_safe( loc_CustomNameWithQuotes, loc_CustomName );
|
|
|
|
|
loc_scat_safe( loc_CustomNameWithQuotes, LOCCHAR("''") );
|
|
|
|
|
|
|
|
|
|
AddDescLine( loc_CustomNameWithQuotes, /* this will be ignored: */ ATTRIB_COL_LEVEL, kDescLineFlag_Name );
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
locchar_t loc_ItemName[MAX_ITEM_NAME_LENGTH];
|
|
|
|
|
GenerateLocalizedFullItemName( loc_ItemName, pLocalizationProvider, pEconItem, k_EGenerateLocalizedFullItemName_WithPaintkitNoItem, m_pHashContext == NULL );
|
|
|
|
|
|
|
|
|
|
AddDescLine( loc_ItemName, /* this will be ignored: */ ATTRIB_COL_LEVEL, kDescLineFlag_Name );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// --------------------------------------------------------------------------
|
|
|
|
|
// Purpose:
|
|
|
|
|
// --------------------------------------------------------------------------
|
|
|
|
|
const locchar_t *GetLocalizedStringForKillEaterTypeAttr( const CLocalizationProvider *pLocalizationProvider, uint32 unKillEaterEventType )
|
|
|
|
|
{
|
|
|
|
|
Assert( pLocalizationProvider );
|
|
|
|
|
|
|
|
|
|
// Generate localized string.
|
|
|
|
|
const char *pszLocString = GetItemSchema()->GetKillEaterScoreTypeLocString( unKillEaterEventType );
|
|
|
|
|
|
|
|
|
|
return pszLocString != NULL
|
|
|
|
|
? pLocalizationProvider->Find( pszLocString )
|
|
|
|
|
: LOCCHAR("");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
class CStrangeRestrictionAttrWrapper
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
CStrangeRestrictionAttrWrapper( const CLocalizationProvider *pLocalizationProvider, const locchar_t *loc_In )
|
|
|
|
|
: m_str( loc_In ? pLocalizationProvider->Find( "ItemTypeDescStrangeFilterSubStr" ) : LOCCHAR(""), loc_In ? loc_In : LOCCHAR("") )
|
|
|
|
|
{
|
|
|
|
|
//
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const locchar_t *operator *() const
|
|
|
|
|
{
|
|
|
|
|
return static_cast<const locchar_t *>( m_str );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
CConstructLocalizedString m_str;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const locchar_t *CEconItemDescription::GetLocalizedStringForStrangeRestrictionAttr( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem, int iAttrIndex ) const
|
|
|
|
|
{
|
|
|
|
|
uint32 unRestrictionType;
|
|
|
|
|
uint32 unRestrictionValue;
|
|
|
|
|
if ( !pEconItem->FindAttribute( GetKillEaterAttr_Restriction( iAttrIndex ), &unRestrictionType ) ||
|
|
|
|
|
!pEconItem->FindAttribute( GetKillEaterAttr_RestrictionValue( iAttrIndex ), &unRestrictionValue ) ||
|
|
|
|
|
unRestrictionType == kStrangeEventRestriction_None )
|
|
|
|
|
{
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch ( unRestrictionType )
|
|
|
|
|
{
|
|
|
|
|
#ifdef PROJECT_TF
|
|
|
|
|
case kStrangeEventRestriction_Map:
|
|
|
|
|
{
|
|
|
|
|
const MapDef_t *pMap = GetItemSchema()->GetMasterMapDefByIndex( unRestrictionValue );
|
|
|
|
|
if ( pMap )
|
|
|
|
|
return pLocalizationProvider->Find( pMap->pszMapNameLocKey );
|
|
|
|
|
}
|
|
|
|
|
case kStrangeEventRestriction_Competitive:
|
|
|
|
|
{
|
|
|
|
|
return pLocalizationProvider->Find( "ItemTypeDescStrangeFilterCompetitive" );
|
|
|
|
|
}
|
|
|
|
|
#endif // PROJECT_TF
|
|
|
|
|
|
|
|
|
|
case kStrangeEventRestriction_VictimSteamAccount:
|
|
|
|
|
return FindAccountPersonaName( unRestrictionValue );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool CEconItemDescription::BGenerate_ItemLevelDesc_StrangeNameAndStats( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem, const locchar_t *locTypename )
|
|
|
|
|
{
|
|
|
|
|
CStrangeRankLocalizationGenerator RankGenerator( pLocalizationProvider, pEconItem, m_pHashContext == NULL );
|
|
|
|
|
if ( !RankGenerator.IsValid() )
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
// For Collection Items
|
|
|
|
|
if ( pEconItem->GetCustomPainkKitDefinition() )
|
|
|
|
|
{
|
|
|
|
|
AddDescLine( CConstructLocalizedString( pLocalizationProvider->Find( "Attrib_stattrakmodule" ), RankGenerator.GetRankLocalized() ),
|
|
|
|
|
ATTRIB_COL_STRANGE,
|
|
|
|
|
kDescLineFlag_Misc
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// Are we tracking alternate stats as well?
|
|
|
|
|
for ( int i = 0; i < GetKillEaterAttrCount(); i++ )
|
|
|
|
|
{
|
|
|
|
|
const CEconItemAttributeDefinition *pKillEaterAltAttrDef = GetKillEaterAttr_Score( i ),
|
|
|
|
|
*pKillEaterAltScoreTypeAttrDef = GetKillEaterAttr_Type( i );
|
|
|
|
|
if ( !pKillEaterAltAttrDef || !pKillEaterAltScoreTypeAttrDef )
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
uint32 unKillEaterAltScore;
|
|
|
|
|
if ( !pEconItem->FindAttribute( pKillEaterAltAttrDef, &unKillEaterAltScore ) )
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
// Older items can optionally not specify a type attribute at all and have an implicit "I'm tracking
|
|
|
|
|
// kills" zeroth attribute. We require a score type for any slot besides that.
|
|
|
|
|
if ( i != 0 && !pEconItem->FindAttribute( pKillEaterAltScoreTypeAttrDef ) )
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
const uint32 unKillEaterAltType = GetScoreTypeForKillEaterAttr( pEconItem, pKillEaterAltScoreTypeAttrDef );
|
|
|
|
|
|
|
|
|
|
AddDescLine( CConstructLocalizedString( pLocalizationProvider->Find( "ItemTypeDescKillEaterAltv2" ),
|
|
|
|
|
unKillEaterAltScore,
|
|
|
|
|
GetLocalizedStringForKillEaterTypeAttr( pLocalizationProvider, unKillEaterAltType ),
|
|
|
|
|
*CStrangeRestrictionAttrWrapper( pLocalizationProvider, GetLocalizedStringForStrangeRestrictionAttr( pLocalizationProvider, pEconItem, i ) ) ),
|
|
|
|
|
ATTRIB_COL_LEVEL,
|
|
|
|
|
kDescLineFlag_Misc ); // strange item scores past the first are not considered part of the type
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
} // End Collection Items
|
|
|
|
|
|
|
|
|
|
// Normal old way
|
|
|
|
|
|
|
|
|
|
// Look for Limited Item Attr
|
|
|
|
|
bool bLimitedQuantity = false;
|
|
|
|
|
static CSchemaAttributeDefHandle pAttrDef_LimitedQuantityItem( "limited quantity item" );
|
|
|
|
|
bLimitedQuantity = pEconItem->FindAttribute( pAttrDef_LimitedQuantityItem );
|
|
|
|
|
|
|
|
|
|
AddDescLine( CConstructLocalizedString( pLocalizationProvider->Find( "ItemTypeDescKillEater" ),
|
|
|
|
|
RankGenerator.GetRankLocalized(),
|
|
|
|
|
locTypename ? locTypename : LOCCHAR(""),
|
|
|
|
|
RankGenerator.GetStrangeScore(),
|
|
|
|
|
GetLocalizedStringForKillEaterTypeAttr( pLocalizationProvider, RankGenerator.GetStrangeType() ),
|
|
|
|
|
*CStrangeRestrictionAttrWrapper( pLocalizationProvider, GetLocalizedStringForStrangeRestrictionAttr( pLocalizationProvider, pEconItem, RankGenerator.GetUsedStrangeSlot() ) ),
|
|
|
|
|
RankGenerator.GetRankSecondaryLocalized() ? RankGenerator.GetRankSecondaryLocalized() : LOCCHAR(""),
|
|
|
|
|
bLimitedQuantity ? pLocalizationProvider->Find( "LimitedQualityDesc" ) : LOCCHAR("")
|
|
|
|
|
),
|
|
|
|
|
bLimitedQuantity ? ATTRIB_COL_LIMITED_QUANTITY : ATTRIB_COL_LEVEL,
|
|
|
|
|
kDescLineFlag_Type );
|
|
|
|
|
|
|
|
|
|
// Are we tracking alternate stats as well?
|
|
|
|
|
for ( int i = 0; i < GetKillEaterAttrCount(); i++ )
|
|
|
|
|
{
|
|
|
|
|
const CEconItemAttributeDefinition *pKillEaterAltAttrDef = GetKillEaterAttr_Score(i),
|
|
|
|
|
*pKillEaterAltScoreTypeAttrDef = GetKillEaterAttr_Type(i);
|
|
|
|
|
if ( !pKillEaterAltAttrDef || !pKillEaterAltScoreTypeAttrDef )
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
uint32 unKillEaterAltScore;
|
|
|
|
|
if ( !pEconItem->FindAttribute( pKillEaterAltAttrDef, &unKillEaterAltScore ) )
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
// Older items can optionally not specify a type attribute at all and have an implicit "I'm tracking
|
|
|
|
|
// kills" zeroth attribute. We require a score type for any slot besides that.
|
|
|
|
|
if ( i != 0 && !pEconItem->FindAttribute( pKillEaterAltScoreTypeAttrDef ) )
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
const uint32 unKillEaterAltType = GetScoreTypeForKillEaterAttr( pEconItem, pKillEaterAltScoreTypeAttrDef );
|
|
|
|
|
|
|
|
|
|
// Skip if this is our primary stat and we already output it above.
|
|
|
|
|
if ( unKillEaterAltType == RankGenerator.GetStrangeType() )
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
AddDescLine( CConstructLocalizedString( pLocalizationProvider->Find( "ItemTypeDescKillEaterAlt" ),
|
|
|
|
|
unKillEaterAltScore,
|
|
|
|
|
GetLocalizedStringForKillEaterTypeAttr( pLocalizationProvider, unKillEaterAltType ),
|
|
|
|
|
*CStrangeRestrictionAttrWrapper( pLocalizationProvider, GetLocalizedStringForStrangeRestrictionAttr( pLocalizationProvider, pEconItem, i ) ) ),
|
|
|
|
|
ATTRIB_COL_LEVEL,
|
|
|
|
|
kDescLineFlag_Misc ); // strange item scores past the first are not considered part of the type
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// --------------------------------------------------------------------------
|
|
|
|
|
// Purpose:
|
|
|
|
|
// --------------------------------------------------------------------------
|
|
|
|
|
uint32 GetItemDescriptionDisplayLevel( const IEconItemInterface *pEconItem )
|
|
|
|
|
{
|
|
|
|
|
Assert( pEconItem );
|
|
|
|
|
|
|
|
|
|
static CSchemaAttributeDefHandle pAttrDef_WideItemLevel( "wide item level" );
|
|
|
|
|
|
|
|
|
|
uint32 unWideLevelValue;
|
|
|
|
|
if ( pEconItem->FindAttribute( pAttrDef_WideItemLevel, &unWideLevelValue ) )
|
|
|
|
|
return unWideLevelValue;
|
|
|
|
|
|
|
|
|
|
return pEconItem->GetItemLevel();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CEconItemDescription::Generate_ItemLevelDesc_Default( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem, const locchar_t *locTypename )
|
|
|
|
|
{
|
|
|
|
|
// By default, items will only show the level if there is an item type to go along with it.
|
|
|
|
|
// Combined, these will build a string like "Level 10 Shotgun". We allow a custom attribute
|
|
|
|
|
// to force the level to be displayed by itself even if there is no item class ("Level 10").
|
|
|
|
|
static CSchemaAttributeDefHandle pAttrDef_ForceLevelDisplay( "force_level_display" );
|
|
|
|
|
|
|
|
|
|
item_definition_index_t usDefIndex = pEconItem->GetItemDefIndex();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef CLIENT_DLL
|
|
|
|
|
const bool bIsStoreItem = IsStorePreviewItem( pEconItem );
|
|
|
|
|
const bool bIsPreviewItem = pEconItem->GetFlags() & kEconItemFlagClient_Preview;
|
|
|
|
|
|
|
|
|
|
// If the item doesn't have a valid itemID, we'll just use the locTypename for the item level description.
|
|
|
|
|
// We don't want to display "Level 0 Hat" in places like the Mann Co. Store and Armory. We'll just display "Hat".
|
|
|
|
|
if ( bIsStoreItem || bIsPreviewItem || pEconItem->GetItemDefinition()->GetRarity() != k_unItemRarity_Any )
|
|
|
|
|
{
|
|
|
|
|
if ( locTypename && *locTypename )
|
|
|
|
|
{
|
|
|
|
|
AddDescLine( locTypename, ATTRIB_COL_LEVEL, kDescLineFlag_Type, NULL, usDefIndex );
|
|
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
float fForceLevelDisplayValue;
|
|
|
|
|
bool bForceLevelDisplay = FindAttribute_UnsafeBitwiseCast<attrib_value_t>( pEconItem, pAttrDef_ForceLevelDisplay, &fForceLevelDisplayValue )
|
|
|
|
|
&& fForceLevelDisplayValue > 0.0f;
|
|
|
|
|
|
|
|
|
|
if ( ( locTypename && *locTypename ) || bForceLevelDisplay )
|
|
|
|
|
{
|
|
|
|
|
if ( locTypename )
|
|
|
|
|
{
|
|
|
|
|
// How are we going to format our level number and base type string?
|
|
|
|
|
const locchar_t *pszFormatString = NULL;
|
|
|
|
|
|
|
|
|
|
#ifdef PROJECT_TF
|
|
|
|
|
static CSchemaAttributeDefHandle pAttrDef_OverrideItemLevelDescString( CTFItemSchema::k_rchOverrideItemLevelDescStringAttribName );
|
|
|
|
|
static const char *s_pszCustomItemLevelDescLocalizationTokens[] =
|
|
|
|
|
{
|
|
|
|
|
"ItemTypeDescCustomLevelString_MvMTour",
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// ...are we going to use a custom format string specified in an attribute?
|
|
|
|
|
uint32 unOverrideItemLevelDescString = 0;
|
|
|
|
|
if ( pEconItem->FindAttribute( pAttrDef_OverrideItemLevelDescString, &unOverrideItemLevelDescString )
|
|
|
|
|
&& unOverrideItemLevelDescString != 0
|
|
|
|
|
&& unOverrideItemLevelDescString <= ARRAYSIZE( s_pszCustomItemLevelDescLocalizationTokens ) )
|
|
|
|
|
{
|
|
|
|
|
const char *pszLevelLocalizationToken = s_pszCustomItemLevelDescLocalizationTokens[ unOverrideItemLevelDescString - 1 ];
|
|
|
|
|
Assert( pszLevelLocalizationToken );
|
|
|
|
|
|
|
|
|
|
pszFormatString = pLocalizationProvider->Find( pszLevelLocalizationToken );
|
|
|
|
|
}
|
|
|
|
|
#endif // PROJECT_TF
|
|
|
|
|
|
|
|
|
|
// Either we didn't have a custom override attribute, or we did and it had an invalid value, or it had a valid
|
|
|
|
|
// value but the localization system failed to find something for that key. In any event, we fall back to our default
|
|
|
|
|
// format string here.
|
|
|
|
|
if ( pszFormatString == NULL )
|
|
|
|
|
{
|
|
|
|
|
bool bLimitedQuantity = false;
|
|
|
|
|
static CSchemaAttributeDefHandle pAttrDef_LimitedQuantityItem( "limited quantity item" );
|
|
|
|
|
bLimitedQuantity = pEconItem->FindAttribute( pAttrDef_LimitedQuantityItem );
|
|
|
|
|
|
|
|
|
|
#if defined( TF_CLIENT_DLL )
|
|
|
|
|
if ( pEconItem->GetItemDefinition()->GetItemClass() && V_strcmp( pEconItem->GetItemDefinition()->GetItemClass(), "map_token" ) == 0 )
|
|
|
|
|
{
|
|
|
|
|
// For map stamps on the client we can show how many hours they've played each map
|
|
|
|
|
// And how many times they've donated to it instead of the generic "level"
|
|
|
|
|
for ( int i = 0; i < GetItemSchema()->GetMapCount(); i++ )
|
|
|
|
|
{
|
|
|
|
|
const MapDef_t* pMap = GetItemSchema()->GetMasterMapDefByIndex( i );
|
|
|
|
|
|
|
|
|
|
if ( pMap->mapStampDef != pEconItem->GetItemDefinition() )
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
int nItemLevel = MapInfo_GetDonationAmount( steamapicontext->SteamUser()->GetSteamID().GetAccountID(), pMap->pszMapName );
|
|
|
|
|
|
|
|
|
|
MapStats_t &mapStats = GetMapStats( pMap->GetStatsIdentifier() );
|
|
|
|
|
int nNumHours = ( mapStats.accumulated.m_iStat[TFMAPSTAT_PLAYTIME] ) / ( 60 /*sec*/ * 60 /*min*/ );
|
|
|
|
|
|
|
|
|
|
AddDescLine( CConstructLocalizedString( pLocalizationProvider->Find( "ItemTypeDescCustomLevelString_MapStamp" ), (uint32)nItemLevel, (uint32)nNumHours ), ATTRIB_COL_LEVEL, kDescLineFlag_Type );
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
#endif
|
|
|
|
|
{
|
|
|
|
|
if ( bLimitedQuantity )
|
|
|
|
|
{
|
|
|
|
|
// Limited Item Description
|
|
|
|
|
pszFormatString = pLocalizationProvider->Find( "ItemTypeDescLimited" );
|
|
|
|
|
AddDescLine( CConstructLocalizedString(
|
|
|
|
|
pszFormatString,
|
|
|
|
|
GetItemDescriptionDisplayLevel( pEconItem ),
|
|
|
|
|
locTypename,
|
|
|
|
|
pLocalizationProvider->Find( "LimitedQualityDesc" ) ),
|
|
|
|
|
ATTRIB_COL_LIMITED_QUANTITY,
|
|
|
|
|
kDescLineFlag_Type,
|
|
|
|
|
NULL,
|
|
|
|
|
usDefIndex
|
|
|
|
|
);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
pszFormatString = pLocalizationProvider->Find( "ItemTypeDesc" );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If we still don't have a format string here, it means our default also failed, but CConstructLocalizedString will
|
|
|
|
|
// handle that safely.
|
|
|
|
|
AddDescLine( CConstructLocalizedString( pszFormatString, GetItemDescriptionDisplayLevel( pEconItem ), locTypename ), ATTRIB_COL_LEVEL, kDescLineFlag_Type, NULL, usDefIndex );
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
Assert( bForceLevelDisplay );
|
|
|
|
|
|
|
|
|
|
AddDescLine( CConstructLocalizedString( pLocalizationProvider->Find( "ItemTypeDescNoLevel" ), GetItemDescriptionDisplayLevel( pEconItem ) ), ATTRIB_COL_LEVEL, kDescLineFlag_Type, NULL, usDefIndex );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// --------------------------------------------------------------------------
|
|
|
|
|
// Purpose:
|
|
|
|
|
// --------------------------------------------------------------------------
|
|
|
|
|
void CEconItemDescription::Generate_ItemLevelDesc( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem )
|
|
|
|
|
{
|
|
|
|
|
Assert( pLocalizationProvider );
|
|
|
|
|
Assert( pEconItem );
|
|
|
|
|
|
|
|
|
|
const GameItemDefinition_t *pItemDef = pEconItem->GetItemDefinition();
|
|
|
|
|
if ( !pItemDef )
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
const locchar_t *locTypename = pLocalizationProvider->Find( pItemDef->GetItemTypeName() );
|
|
|
|
|
|
|
|
|
|
// Kill-eating weapons replace the standard weapon name/level line with a label
|
|
|
|
|
// describing the current class of the item instead of the level. This overrides
|
|
|
|
|
// even "force_level_display".
|
|
|
|
|
if ( BGenerate_ItemLevelDesc_StrangeNameAndStats( pLocalizationProvider, pEconItem, locTypename ) )
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
// Not strange, but if you are paint kitted or have a collection reference dont create this
|
|
|
|
|
if ( pEconItem->GetCustomPainkKitDefinition() || pItemDef->GetCollectionReference() )
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
// If we didn't generate a fancy strange name, we fall back to our default behavior.
|
|
|
|
|
Generate_ItemLevelDesc_Default( pLocalizationProvider, pEconItem, locTypename );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#if defined( STAGING_ONLY ) && defined( CLIENT_DLL )
|
|
|
|
|
ConVar econ_include_debug_item_description( "econ_include_debug_item_description","0", FCVAR_CLIENTDLL | FCVAR_ARCHIVE, "Controls display of the additional debug fields in CEconItemDescription (definition index, etc.)." );
|
|
|
|
|
|
|
|
|
|
// --------------------------------------------------------------------------
|
|
|
|
|
// Purpose:
|
|
|
|
|
// --------------------------------------------------------------------------
|
|
|
|
|
void CEconItemDescription::Generate_DebugInformation( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem )
|
|
|
|
|
{
|
|
|
|
|
Assert( pLocalizationProvider );
|
|
|
|
|
Assert( pEconItem );
|
|
|
|
|
|
|
|
|
|
if ( !econ_include_debug_item_description.GetBool() )
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
#if TF_ANTI_IDLEBOT_VERIFICATION
|
|
|
|
|
// Adding these extra description lines would mess with our GC/client sync.
|
|
|
|
|
if ( m_pHashContext )
|
|
|
|
|
return;
|
|
|
|
|
#endif // TF_ANTI_IDLEBOT_VERIFICATION
|
|
|
|
|
|
|
|
|
|
AddDescLine( CConstructLocalizedString( LOCCHAR("([ Item ID: %s1 ])"), pEconItem->GetID() ), ATTRIB_COL_POSITIVE, kDescLineFlag_Misc );
|
|
|
|
|
AddDescLine( CConstructLocalizedString( LOCCHAR("([ Item Definition Index: %s1 ])"), (uint32)pEconItem->GetItemDefIndex() ), ATTRIB_COL_POSITIVE, kDescLineFlag_Misc );
|
|
|
|
|
AddDescLine( CConstructLocalizedString( LOCCHAR("([ In Use?: %s1 ])"), pEconItem->GetInUse() ? LOCCHAR("true") : LOCCHAR("false") ), ATTRIB_COL_POSITIVE, kDescLineFlag_Misc );
|
|
|
|
|
AddEmptyDescLine();
|
|
|
|
|
|
|
|
|
|
class CDebugAttributeDisplayer : public IEconItemAttributeIterator
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
CDebugAttributeDisplayer( CEconItemDescription *pOut_Desc ) : m_pDesc( pOut_Desc ) { Assert( m_pDesc ); }
|
|
|
|
|
|
|
|
|
|
virtual bool OnIterateAttributeValue( const CEconItemAttributeDefinition *pAttrDef, attrib_value_t value ) OVERRIDE
|
|
|
|
|
{
|
|
|
|
|
// Ugh.
|
|
|
|
|
wchar_t wszAttrDef[ 256 ];
|
|
|
|
|
ILocalize::ConvertANSIToUnicode( pAttrDef->GetDefinitionName(), &wszAttrDef[0], sizeof( wszAttrDef ) );
|
|
|
|
|
|
|
|
|
|
m_pDesc->AddDescLine( CConstructLocalizedString( LOCCHAR("([ Attribute [%s1]: '%s2' ( %s3 | %s4 ) ])"),
|
|
|
|
|
(uint32)pAttrDef->GetDefinitionIndex(),
|
|
|
|
|
wszAttrDef,
|
|
|
|
|
*(uint32 *)&value,
|
|
|
|
|
*(float *)&value ), ATTRIB_COL_POSITIVE, kDescLineFlag_Misc );
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
virtual bool OnIterateAttributeValue( const CEconItemAttributeDefinition *pAttrDef, float value ) OVERRIDE
|
|
|
|
|
{
|
|
|
|
|
// Ugh.
|
|
|
|
|
wchar_t wszAttrDef[ 256 ];
|
|
|
|
|
ILocalize::ConvertANSIToUnicode( pAttrDef->GetDefinitionName(), &wszAttrDef[0], sizeof( wszAttrDef ) );
|
|
|
|
|
|
|
|
|
|
m_pDesc->AddDescLine( CConstructLocalizedString( LOCCHAR("([ Attribute [%s1]: '%s2' ( %f ) ])"),
|
|
|
|
|
(uint32)pAttrDef->GetDefinitionIndex(),
|
|
|
|
|
wszAttrDef,
|
|
|
|
|
value ), ATTRIB_COL_POSITIVE, kDescLineFlag_Misc );
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
virtual bool OnIterateAttributeValue( const CEconItemAttributeDefinition *pAttrDef, const uint64& value ) OVERRIDE
|
|
|
|
|
{
|
|
|
|
|
// Ugh.
|
|
|
|
|
wchar_t wszAttrDef[ 256 ];
|
|
|
|
|
ILocalize::ConvertANSIToUnicode( pAttrDef->GetDefinitionName(), &wszAttrDef[0], sizeof( wszAttrDef ) );
|
|
|
|
|
|
|
|
|
|
m_pDesc->AddDescLine( CConstructLocalizedString( LOCCHAR("([ Attribute [%s1]: '%s2' ( %llu ) ])"),
|
|
|
|
|
(uint32)pAttrDef->GetDefinitionIndex(),
|
|
|
|
|
wszAttrDef,
|
|
|
|
|
value ), ATTRIB_COL_POSITIVE, kDescLineFlag_Misc );
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
virtual bool OnIterateAttributeValue( const CEconItemAttributeDefinition *pAttrDef, const CAttribute_String& value ) OVERRIDE
|
|
|
|
|
{
|
|
|
|
|
// Ugh.
|
|
|
|
|
wchar_t wszAttrDef[ 256 ];
|
|
|
|
|
ILocalize::ConvertANSIToUnicode( pAttrDef->GetDefinitionName(), &wszAttrDef[0], sizeof( wszAttrDef ) );
|
|
|
|
|
|
|
|
|
|
wchar_t wszAttrContents[ 1024 ];
|
|
|
|
|
ILocalize::ConvertANSIToUnicode( value.value().c_str(), &wszAttrContents[0], sizeof( wszAttrContents ) );
|
|
|
|
|
|
|
|
|
|
m_pDesc->AddDescLine( CConstructLocalizedString( LOCCHAR("([ Attribute [%s1]: '%s2' ( \"%s3\" ) ])"),
|
|
|
|
|
(uint32)pAttrDef->GetDefinitionIndex(),
|
|
|
|
|
wszAttrDef,
|
|
|
|
|
wszAttrContents ), ATTRIB_COL_POSITIVE, kDescLineFlag_Misc );
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
virtual bool OnIterateAttributeValue( const CEconItemAttributeDefinition *pAttrDef, const CAttribute_DynamicRecipeComponent& value ) OVERRIDE
|
|
|
|
|
{
|
|
|
|
|
const char* pszItemName = GetItemSchema()->GetItemDefinition( value.def_index() )->GetItemBaseName();
|
|
|
|
|
|
|
|
|
|
wchar_t wszItemQuality[ 256 ];
|
|
|
|
|
ILocalize::ConvertANSIToUnicode( EconQuality_GetQualityString(EEconItemQuality(value.item_quality())), &wszItemQuality[0], sizeof( wszItemQuality ) );
|
|
|
|
|
|
|
|
|
|
wchar_t wszAttrString[ 256 ];
|
|
|
|
|
ILocalize::ConvertANSIToUnicode( value.attributes_string().c_str(), &wszAttrString[0], sizeof( wszAttrString ) );
|
|
|
|
|
|
|
|
|
|
wchar_t wszCount[ 64 ];
|
|
|
|
|
ILocalize::ConvertANSIToUnicode( CFmtStr( "%d/%d", value.num_fulfilled(), value.num_required() ), &wszCount[0], sizeof( wszCount ) );
|
|
|
|
|
|
|
|
|
|
locchar_t wszLocalizedItemName[ 128 ];
|
|
|
|
|
const locchar_t *pLocalizedItemName = GLocalizationProvider()->Find( pszItemName );
|
|
|
|
|
if ( pLocalizedItemName )
|
|
|
|
|
{
|
|
|
|
|
V_wcscpy_safe( wszLocalizedItemName, pLocalizedItemName );
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// name wasn't found by Find(), so just convert the pszItemName
|
|
|
|
|
ILocalize::ConvertANSIToUnicode( pszItemName, &wszLocalizedItemName[0], sizeof( wszLocalizedItemName ) );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
m_pDesc->AddDescLine( CConstructLocalizedString( LOCCHAR( "\nItem: \"%s1\"\nQuality: %s2\nAttribs:%s3\nCount: %s4" ),
|
|
|
|
|
wszLocalizedItemName,
|
|
|
|
|
wszItemQuality,
|
|
|
|
|
wszAttrString,
|
|
|
|
|
wszCount ),
|
|
|
|
|
ATTRIB_COL_POSITIVE, kDescLineFlag_Misc );
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
virtual bool OnIterateAttributeValue( const CEconItemAttributeDefinition *pAttrDef, const CAttribute_ItemSlotCriteria& value ) OVERRIDE
|
|
|
|
|
{
|
|
|
|
|
// Ugh.
|
|
|
|
|
wchar_t wszAttrDef[ 256 ];
|
|
|
|
|
ILocalize::ConvertANSIToUnicode( pAttrDef->GetDefinitionName(), &wszAttrDef[0], sizeof( wszAttrDef ) );
|
|
|
|
|
|
|
|
|
|
wchar_t wszAttrTags[ 256 ];
|
|
|
|
|
ILocalize::ConvertANSIToUnicode( value.tags().c_str(), &wszAttrTags[0], sizeof( wszAttrTags ) );
|
|
|
|
|
|
|
|
|
|
m_pDesc->AddDescLine( CConstructLocalizedString( LOCCHAR("([ Attribute [%s1]: '%s2'])\nTags: \"%s3\""),
|
|
|
|
|
(uint32)pAttrDef->GetDefinitionIndex(),
|
|
|
|
|
wszAttrDef,
|
|
|
|
|
wszAttrTags ),
|
|
|
|
|
ATTRIB_COL_POSITIVE, kDescLineFlag_Misc );
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
virtual bool OnIterateAttributeValue( const CEconItemAttributeDefinition *pAttrDef, const CAttribute_WorldItemPlacement& value ) OVERRIDE
|
|
|
|
|
{
|
|
|
|
|
wchar_t wszAttrDef[ 256 ];
|
|
|
|
|
ILocalize::ConvertANSIToUnicode( pAttrDef->GetDefinitionName(), &wszAttrDef[0], sizeof( wszAttrDef ) );
|
|
|
|
|
|
|
|
|
|
m_pDesc->AddDescLine( CConstructLocalizedString( LOCCHAR( "([ Attribute [%s1]: '%s2'])\n" ),
|
|
|
|
|
(uint32)pAttrDef->GetDefinitionIndex(),
|
|
|
|
|
wszAttrDef ),
|
|
|
|
|
ATTRIB_COL_POSITIVE, kDescLineFlag_Misc );
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
CEconItemDescription *m_pDesc;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
CDebugAttributeDisplayer DebugAttributeDisplayer( this );
|
|
|
|
|
pEconItem->IterateAttributes( &DebugAttributeDisplayer );
|
|
|
|
|
}
|
|
|
|
|
#endif // defined( STAGING_ONLY ) && defined( CLIENT_DLL )
|
|
|
|
|
|
|
|
|
|
// --------------------------------------------------------------------------
|
|
|
|
|
// Purpose:
|
|
|
|
|
// --------------------------------------------------------------------------
|
|
|
|
|
void CEconItemDescription::Generate_CraftTag( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem )
|
|
|
|
|
{
|
|
|
|
|
Assert( pLocalizationProvider );
|
|
|
|
|
Assert( pEconItem );
|
|
|
|
|
|
|
|
|
|
static CSchemaAttributeDefHandle pAttribDef_MakersMarkId( "makers mark id" );
|
|
|
|
|
|
|
|
|
|
attrib_value_t value;
|
|
|
|
|
if ( !pEconItem->FindAttribute( pAttribDef_MakersMarkId, &value ) )
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
AddAttributeDescription( pLocalizationProvider, pAttribDef_MakersMarkId, value );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// --------------------------------------------------------------------------
|
|
|
|
|
// Purpose:
|
|
|
|
|
// --------------------------------------------------------------------------
|
|
|
|
|
void CEconItemDescription::Generate_StyleDesc( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem )
|
|
|
|
|
{
|
|
|
|
|
Assert( pLocalizationProvider );
|
|
|
|
|
Assert( pEconItem );
|
|
|
|
|
|
|
|
|
|
const GameItemDefinition_t *pItemDef = pEconItem->GetItemDefinition();
|
|
|
|
|
if ( !pItemDef )
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
const CEconStyleInfo *pStyle = pItemDef->GetStyleInfo( pEconItem->GetStyle() );
|
|
|
|
|
if ( !pStyle )
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
const locchar_t *loc_StyleName = pLocalizationProvider->Find( pStyle->GetName() );
|
|
|
|
|
if ( !loc_StyleName )
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
AddDescLine( CConstructLocalizedString( pLocalizationProvider->Find( "#Econ_Style_Desc" ), loc_StyleName ), ATTRIB_COL_LEVEL, kDescLineFlag_Misc );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
// Purpose:
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
void CEconItemDescription::Generate_HolidayRestriction( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem )
|
|
|
|
|
{
|
|
|
|
|
Assert( pLocalizationProvider );
|
|
|
|
|
Assert( pEconItem );
|
|
|
|
|
|
|
|
|
|
const GameItemDefinition_t *pItemDef = pEconItem->GetItemDefinition();
|
|
|
|
|
if ( !pItemDef )
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
const char *pszHolidayRestriction = pItemDef->GetHolidayRestriction();
|
|
|
|
|
if ( !pszHolidayRestriction )
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
// Report any special restrictions. We'll output in a different color depending on whether or not
|
|
|
|
|
// the restriction currently prevents the item from showing up.
|
|
|
|
|
LocalizedAddDescLine( pLocalizationProvider,
|
|
|
|
|
CFmtStr( "Econ_holiday_restriction_%s", pszHolidayRestriction ).Access(),
|
|
|
|
|
EconHolidays_IsHolidayActive( EconHolidays_GetHolidayForString( pszHolidayRestriction ), CRTime::RTime32TimeCur() ) ? ATTRIB_COL_LEVEL : ATTRIB_COL_NEGATIVE,
|
|
|
|
|
kDescLineFlag_Misc );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
// Purpose:
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
void CEconItemDescription::Generate_QualityDesc( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem )
|
|
|
|
|
{
|
|
|
|
|
Assert( pLocalizationProvider );
|
|
|
|
|
Assert( pEconItem );
|
|
|
|
|
|
|
|
|
|
// Does this item quality have additional description information that goes along with
|
|
|
|
|
// besides the usual name/coloration changes?
|
|
|
|
|
const char *pszQualityDescLocalizationKey = NULL;
|
|
|
|
|
|
|
|
|
|
switch( pEconItem->GetQuality() )
|
|
|
|
|
{
|
|
|
|
|
case AE_SELFMADE:
|
|
|
|
|
pszQualityDescLocalizationKey = "Attrib_Selfmade_Description";
|
|
|
|
|
break;
|
|
|
|
|
case AE_COMMUNITY:
|
|
|
|
|
pszQualityDescLocalizationKey = "Attrib_Community_Description";
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// We don't need to do anything special.
|
|
|
|
|
if ( !pszQualityDescLocalizationKey )
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
// If this item has a particle system attached but doesn't have the attribute that we usually use
|
|
|
|
|
// to attach particles, we hack it and dump out an extra line to show the particle system description
|
|
|
|
|
// as well.
|
|
|
|
|
static CSchemaAttributeDefHandle pAttrDef_ParticleEffect( "attach particle effect" );
|
|
|
|
|
static attachedparticlesystem_t *pSparkleSystem = GetItemSchema()->FindAttributeControlledParticleSystem( "community_sparkle" );
|
|
|
|
|
|
|
|
|
|
// If the schema understands these properties...
|
|
|
|
|
if ( pAttrDef_ParticleEffect && pSparkleSystem )
|
|
|
|
|
{
|
|
|
|
|
// ...and we don't have a real particle effect attribute attribute...
|
|
|
|
|
if ( !pEconItem->FindAttribute( pAttrDef_ParticleEffect ) )
|
|
|
|
|
{
|
|
|
|
|
// check for Unusual Cap def index (1173)
|
|
|
|
|
// We manually assign unusual effect to content author. No community sparkle
|
|
|
|
|
if ( pEconItem->GetItemDefIndex() != 1173 )
|
|
|
|
|
{
|
|
|
|
|
// ...then manually add the description as if we did.
|
|
|
|
|
float flSystemID = pSparkleSystem->nSystemID;
|
|
|
|
|
AddAttributeDescription( pLocalizationProvider, pAttrDef_ParticleEffect, *(uint32*)&flSystemID );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LocalizedAddDescLine( pLocalizationProvider, pszQualityDescLocalizationKey, ATTRIB_COL_NEUTRAL, kDescLineFlag_Misc );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
void CEconItemDescription::Generate_ItemRarityDesc( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem )
|
|
|
|
|
{
|
|
|
|
|
const CEconItemRarityDefinition* pItemRarity = GetItemSchema()->GetRarityDefinition( pEconItem->GetItemDefinition()->GetRarity() );
|
|
|
|
|
if ( !pItemRarity )
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
const GameItemDefinition_t *pItemDef = pEconItem->GetItemDefinition();
|
|
|
|
|
if ( !pItemDef )
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
const char *pszTooltip = "TFUI_InvTooltip_Rarity";
|
|
|
|
|
|
|
|
|
|
attrib_colors_t colorRarity = pItemRarity->GetAttribColor();
|
|
|
|
|
|
|
|
|
|
const locchar_t *loc_RarityText = pLocalizationProvider->Find( pItemRarity->GetLocKey() );
|
|
|
|
|
const locchar_t *locTypename = pLocalizationProvider->Find( pItemDef->GetItemTypeName() );
|
|
|
|
|
const locchar_t *loc_WearText = LOCCHAR("");
|
|
|
|
|
|
|
|
|
|
float flWear = 0;
|
|
|
|
|
if ( pEconItem->GetCustomPaintKitWear( flWear ) )
|
|
|
|
|
{
|
|
|
|
|
loc_WearText = pLocalizationProvider->Find( GetWearLocalizationString( flWear ) );
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
pszTooltip = "TFUI_InvTooltip_RarityNoWear";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
AddDescLine( CConstructLocalizedString( pLocalizationProvider->Find( pszTooltip ), loc_RarityText, locTypename, loc_WearText ), colorRarity, kDescLineFlag_Misc );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
void CEconItemDescription::Generate_WearAmountDesc( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem )
|
|
|
|
|
{
|
|
|
|
|
if ( pEconItem->GetCustomPainkKitDefinition() == 0 )
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
Assert( pLocalizationProvider );
|
|
|
|
|
Assert( pEconItem );
|
|
|
|
|
|
|
|
|
|
float flWear = 0;
|
|
|
|
|
if ( pEconItem->GetCustomPaintKitWear( flWear ) )
|
|
|
|
|
{
|
|
|
|
|
locchar_t loc_WearText[MAX_ATTRIBUTE_DESCRIPTION_LENGTH];
|
|
|
|
|
|
|
|
|
|
loc_scpy_safe( loc_WearText, pLocalizationProvider->Find( "#TFUI_InvTooltip_Wear" ) );
|
|
|
|
|
loc_scat_safe( loc_WearText, LOCCHAR( " " ) );
|
|
|
|
|
loc_scat_safe( loc_WearText, pLocalizationProvider->Find( GetWearLocalizationString( flWear ) ) );
|
|
|
|
|
|
|
|
|
|
AddDescLine( loc_WearText, ATTRIB_COL_NEUTRAL, kDescLineFlag_Misc );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
// Purpose:
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
void CEconItemDescription::Generate_ItemDesc( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem )
|
|
|
|
|
{
|
|
|
|
|
Assert( pLocalizationProvider );
|
|
|
|
|
Assert( pEconItem );
|
|
|
|
|
|
|
|
|
|
// Show the custom description if it has one.
|
|
|
|
|
const char *utf8_CustomDesc = pEconItem->GetCustomDesc();
|
|
|
|
|
if ( utf8_CustomDesc && utf8_CustomDesc[0] )
|
|
|
|
|
{
|
|
|
|
|
locchar_t loc_CustomDesc[ MAX_ITEM_DESC_LENGTH ];
|
|
|
|
|
pLocalizationProvider->ConvertUTF8ToLocchar( utf8_CustomDesc, loc_CustomDesc, sizeof( loc_CustomDesc ) );
|
|
|
|
|
|
|
|
|
|
locchar_t loc_CustomDescWithQuotes[ MAX_ITEM_DESC_LENGTH ];
|
|
|
|
|
loc_scpy_safe( loc_CustomDescWithQuotes, LOCCHAR("''") );
|
|
|
|
|
loc_scat_safe( loc_CustomDescWithQuotes, loc_CustomDesc );
|
|
|
|
|
loc_scat_safe( loc_CustomDescWithQuotes, LOCCHAR("''") );
|
|
|
|
|
|
|
|
|
|
AddDescLine( loc_CustomDescWithQuotes, ATTRIB_COL_NEUTRAL, kDescLineFlag_Desc );
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// No custom description -- see if the item has a default description as part of the definition.
|
|
|
|
|
const GameItemDefinition_t *pItemDef = pEconItem->GetItemDefinition();
|
|
|
|
|
if ( !pItemDef )
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
// Add any additional item description
|
|
|
|
|
if ( pItemDef->GetItemDesc() )
|
|
|
|
|
{
|
|
|
|
|
LocalizedAddDescLine( pLocalizationProvider, pItemDef->GetItemDesc(), ATTRIB_COL_NEUTRAL, kDescLineFlag_Desc );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If we're a store preview item, show the available styles in the tooltip so potential buyers
|
|
|
|
|
// have more information.
|
|
|
|
|
if ( IsStorePreviewItem( pEconItem ) )
|
|
|
|
|
{
|
|
|
|
|
if ( pItemDef && pItemDef->GetNumStyles() > 0 )
|
|
|
|
|
{
|
|
|
|
|
AddEmptyDescLine();
|
|
|
|
|
LocalizedAddDescLine( pLocalizationProvider, "#Store_AvailableStyles_Header", ATTRIB_COL_LEVEL, kDescLineFlag_Misc );
|
|
|
|
|
|
|
|
|
|
for ( int i = 0; i < pItemDef->GetNumStyles(); i++ )
|
|
|
|
|
{
|
|
|
|
|
LocalizedAddDescLine( pLocalizationProvider, pItemDef->GetStyleInfo( i )->GetName(), ATTRIB_COL_LEVEL, kDescLineFlag_Misc );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
// Purpose:
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
// If we have at least this many items in our bundle then display multiple entries
|
|
|
|
|
// per line. Otherwise display one item per line for clarity.
|
|
|
|
|
enum { kDescription_CompositeBundleEntriesCount = 15 };
|
|
|
|
|
|
|
|
|
|
void CEconItemDescription::Generate_Bundle( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem )
|
|
|
|
|
{
|
|
|
|
|
Assert( pLocalizationProvider );
|
|
|
|
|
Assert( pEconItem );
|
|
|
|
|
|
|
|
|
|
const GameItemDefinition_t *pItemDef = pEconItem->GetItemDefinition();
|
|
|
|
|
if ( !pItemDef )
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
const bundleinfo_t *pBundleInfo = pItemDef->GetBundleInfo();
|
|
|
|
|
if ( !pBundleInfo )
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
enum EBundleEntryDisplayStyle
|
|
|
|
|
{
|
|
|
|
|
kBundleDisplay_SingleEntry, // one entry per line
|
|
|
|
|
kBundleDisplay_PairEntry, // "Some Item, Some Other Item," (with ending comma)
|
|
|
|
|
kBundleDisplay_PairEntryFinal, // "Some Item, Some Other Item" (with no ending comma)
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
#ifdef GC_DLL
|
|
|
|
|
AddEmptyDescLine();
|
|
|
|
|
#endif // GC_DLL
|
|
|
|
|
|
|
|
|
|
CUtlVector< item_definition_index_t > vecPackBundlesAdded;
|
|
|
|
|
|
|
|
|
|
FOR_EACH_VEC( pBundleInfo->vecItemDefs, i )
|
|
|
|
|
{
|
|
|
|
|
// Sanity check.
|
|
|
|
|
const CEconItemDefinition *pBundleItemDef = pBundleInfo->vecItemDefs[i];
|
|
|
|
|
if ( !pBundleItemDef )
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
// If the current item is part of a pack bundle, add the pack bundle to the description, rather than the individual item
|
|
|
|
|
#ifdef DOTA
|
|
|
|
|
if ( pBundleItemDef->IsPackItem() )
|
|
|
|
|
{
|
|
|
|
|
const CUtlVector< CEconItemDefinition * > &vecPackBundleItemDefs = pBundleItemDef->GetOwningPackBundles();
|
|
|
|
|
|
|
|
|
|
item_definition_index_t usPackBundleItemDefIndex = vecPackBundleItemDefs[i]->GetDefinitionIndex();
|
|
|
|
|
if ( vecPackBundlesAdded.HasElement( usPackBundleItemDefIndex ) )
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
// Remember the def index so we don't add the reference to the pack bundle more than once
|
|
|
|
|
vecPackBundlesAdded.AddToTail( usPackBundleItemDefIndex );
|
|
|
|
|
|
|
|
|
|
// Now, point pBundleItemDef at the pack bundle itself and carry on
|
|
|
|
|
pBundleItemDef = pPackBundleItemDef;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
// Figure out which display style to use for this item. By default we put one item one each line...
|
|
|
|
|
EBundleEntryDisplayStyle eDisplayStyle = kBundleDisplay_SingleEntry;
|
|
|
|
|
|
|
|
|
|
// ...but if we have a whole bunch of items in a single bundle, we lump them together two per line to
|
|
|
|
|
// save space. Only do this on the client. On the GC, use single lines so that link meta data can be passed
|
|
|
|
|
// along per-line to the store bundle pages.
|
|
|
|
|
#if defined( CLIENT_DLL ) && !defined( TF_CLIENT_DLL )
|
|
|
|
|
if ( pBundleInfo->vecItemDefs.Count() >= kDescription_CompositeBundleEntriesCount )
|
|
|
|
|
{
|
|
|
|
|
const int iRemainingItems = pBundleInfo->vecItemDefs.Count() - i;
|
|
|
|
|
|
|
|
|
|
// We distinguish between "there are at least three entries left", which means we'll end the line
|
|
|
|
|
// with a comma, etc.
|
|
|
|
|
if ( iRemainingItems > 2 )
|
|
|
|
|
{
|
|
|
|
|
eDisplayStyle = kBundleDisplay_PairEntry;
|
|
|
|
|
}
|
|
|
|
|
// ...or if these are our very last two items, we list our last two items and that's it.
|
|
|
|
|
else if ( iRemainingItems == 2 )
|
|
|
|
|
{
|
|
|
|
|
eDisplayStyle = kBundleDisplay_PairEntryFinal;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
if ( eDisplayStyle == kBundleDisplay_SingleEntry )
|
|
|
|
|
{
|
|
|
|
|
// pBundleItemDef will point at the pack bundle if pBundleItemDef is a pack item. In DotA, pack bundles *only* include pack items, whereas in TF, there are bundles which include some items where are individually for sale and others that are not. For example, the Scout Starter Bundle, etc.
|
|
|
|
|
#ifdef DOTA
|
|
|
|
|
LocalizedAddDescLine( pLocalizationProvider, pBundleItemDef->GetItemBaseName(), ATTRIB_COL_BUNDLE_ITEM, kDescLineFlag_Misc, NULL, pBundleItemDef->GetDefinitionIndex() );
|
|
|
|
|
#else
|
|
|
|
|
LocalizedAddDescLine( pLocalizationProvider, pBundleItemDef->GetItemBaseName(), pBundleItemDef->IsPackItem() ? ATTRIB_COL_NEUTRAL : ATTRIB_COL_BUNDLE_ITEM, kDescLineFlag_Misc, NULL, pBundleItemDef->IsPackItem() ? INVALID_ITEM_DEF_INDEX : pBundleItemDef->GetDefinitionIndex(), !pBundleItemDef->IsPackItem() );
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
Assert( eDisplayStyle == kBundleDisplay_PairEntry || eDisplayStyle == kBundleDisplay_PairEntryFinal );
|
|
|
|
|
|
|
|
|
|
const CEconItemDefinition *pOtherBundleItem = pBundleInfo->vecItemDefs[i + 1];
|
|
|
|
|
const char *pOtherBundleItemBaseName = pOtherBundleItem ? pOtherBundleItem->GetItemBaseName() : "";
|
|
|
|
|
|
|
|
|
|
AddDescLine( CConstructLocalizedString( pLocalizationProvider->Find( eDisplayStyle == kBundleDisplay_PairEntryFinal ? "Econ_Bundle_Double" : "Econ_Bundle_DoubleContinued" ),
|
|
|
|
|
pLocalizationProvider->Find( pBundleItemDef->GetItemBaseName() ),
|
|
|
|
|
pLocalizationProvider->Find( pOtherBundleItemBaseName ) ),
|
|
|
|
|
ATTRIB_COL_BUNDLE_ITEM,
|
|
|
|
|
kDescLineFlag_Misc,
|
|
|
|
|
NULL,
|
|
|
|
|
pBundleItemDef->GetDefinitionIndex() );
|
|
|
|
|
|
|
|
|
|
// We consumed a second element as well.
|
|
|
|
|
i++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
// Purpose:
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
void CEconItemDescription::Generate_GiftedBy( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem )
|
|
|
|
|
{
|
|
|
|
|
Assert( pLocalizationProvider );
|
|
|
|
|
Assert( pEconItem );
|
|
|
|
|
|
|
|
|
|
static CSchemaAttributeDefHandle pAttrDef_GiftedBy( "gifter account id" );
|
|
|
|
|
static CSchemaAttributeDefHandle pAttrDef_EventDate( "event date" );
|
|
|
|
|
|
|
|
|
|
attrib_value_t val_GifterId;
|
|
|
|
|
if ( pAttrDef_GiftedBy && pEconItem->FindAttribute( pAttrDef_GiftedBy, &val_GifterId ) )
|
|
|
|
|
{
|
|
|
|
|
// Who gifted us this present?
|
|
|
|
|
AddAttributeDescription( pLocalizationProvider, pAttrDef_GiftedBy, val_GifterId );
|
|
|
|
|
|
|
|
|
|
// Do we also have (optional) information about when it happened?
|
|
|
|
|
attrib_value_t val_EventData;
|
|
|
|
|
if ( pAttrDef_EventDate && pEconItem->FindAttribute( pAttrDef_EventDate, &val_EventData ) )
|
|
|
|
|
{
|
|
|
|
|
AddAttributeDescription( pLocalizationProvider, pAttrDef_EventDate, val_EventData );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#ifdef PROJECT_TF
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
// Purpose:
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
static bool IsDuelingMedal( const GameItemDefinition_t *pItemDef )
|
|
|
|
|
{
|
|
|
|
|
static CSchemaItemDefHandle pAttrDef_DuelingMedals[] =
|
|
|
|
|
{
|
|
|
|
|
CSchemaItemDefHandle( "Duel Medal Bronze" ),
|
|
|
|
|
CSchemaItemDefHandle( "Duel Medal Silver" ),
|
|
|
|
|
CSchemaItemDefHandle( "Duel Medal Gold" ),
|
|
|
|
|
CSchemaItemDefHandle( "Duel Medal Plat" ),
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
Assert( pItemDef );
|
|
|
|
|
|
|
|
|
|
for ( int i = 0; i < ARRAYSIZE( pAttrDef_DuelingMedals ); i++ )
|
|
|
|
|
if ( pItemDef == pAttrDef_DuelingMedals[i] )
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CEconItemDescription::Generate_DuelingMedal( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem )
|
|
|
|
|
{
|
|
|
|
|
Assert( pLocalizationProvider );
|
|
|
|
|
Assert( pEconItem );
|
|
|
|
|
|
|
|
|
|
static CSchemaAttributeDefHandle pAttrDef_EventDate( "event date" );
|
|
|
|
|
|
|
|
|
|
const GameItemDefinition_t *pItemDef = pEconItem->GetItemDefinition();
|
|
|
|
|
if ( !pItemDef )
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if ( !IsDuelingMedal( pItemDef ) )
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
const CTFDuelSummary *pDuelSummary = FindAccountTypeCacheSingleton<CTFDuelSummary>( pEconItem->GetAccountID(), CTFDuelSummary::k_nTypeID );
|
|
|
|
|
if ( !pDuelSummary )
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
// Add the date received first.
|
|
|
|
|
attrib_value_t value;
|
|
|
|
|
if ( !pEconItem->FindAttribute( pAttrDef_EventDate, &value ) )
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
// We feed our format-string parameters in via KeyValues.
|
|
|
|
|
KeyValues *pKeyValues = new KeyValues( "DuelStrings" );
|
|
|
|
|
|
|
|
|
|
CLocalizedRTime32 time = { pDuelSummary->Obj().last_duel_timestamp(), false, pLocalizationProvider TF_ANTI_IDLEBOT_VERIFICATION_ONLY_COMMA TF_ANTI_IDLEBOT_VERIFICATION_ONLY_ARG( m_pHashContext ) };
|
|
|
|
|
|
|
|
|
|
TypedKeyValuesStringHelper<locchar_t>::Write( pKeyValues, "last_date", CLocalizedStringArg<CLocalizedRTime32>( time ).GetLocArg() );
|
|
|
|
|
TypedKeyValuesStringHelper<locchar_t>::Write( pKeyValues, "wins", CLocalizedStringArg<uint32>( pDuelSummary->Obj().duel_wins() ).GetLocArg() );
|
|
|
|
|
TypedKeyValuesStringHelper<locchar_t>::Write( pKeyValues, "last_target", FindAccountPersonaName( pDuelSummary->Obj().last_duel_account_id() ) );
|
|
|
|
|
|
|
|
|
|
// What happened in our last duel? This will be used as a format string to wrap the above data.
|
|
|
|
|
const char *pszTextFormat;
|
|
|
|
|
switch ( pDuelSummary->Obj().last_duel_status() )
|
|
|
|
|
{
|
|
|
|
|
case kDuelStatus_Loss:
|
|
|
|
|
pszTextFormat = "#TF_Duel_Desc_Lost";
|
|
|
|
|
break;
|
|
|
|
|
case kDuelStatus_Tie:
|
|
|
|
|
pszTextFormat = "#TF_Duel_Desc_Tied";
|
|
|
|
|
break;
|
|
|
|
|
case kDuelStatus_Win:
|
|
|
|
|
default:
|
|
|
|
|
pszTextFormat = "#TF_Duel_Desc_Won";
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Output our whole description.
|
|
|
|
|
AddEmptyDescLine();
|
|
|
|
|
AddAttributeDescription( pLocalizationProvider, pAttrDef_EventDate, value );
|
|
|
|
|
AddEmptyDescLine();
|
|
|
|
|
AddDescLine( CConstructLocalizedString( pLocalizationProvider->Find( pszTextFormat ), pKeyValues ), ATTRIB_COL_NEUTRAL, kDescLineFlag_Misc );
|
|
|
|
|
|
|
|
|
|
pKeyValues->deleteThis();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
// Purpose:
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
void CEconItemDescription::Generate_MapContributor( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem )
|
|
|
|
|
{
|
|
|
|
|
Assert( pLocalizationProvider );
|
|
|
|
|
Assert( pEconItem );
|
|
|
|
|
|
|
|
|
|
static CSchemaItemDefHandle pItemDef_WorldTraveler( "World Traveler" );
|
|
|
|
|
if ( !pItemDef_WorldTraveler || pEconItem->GetItemDefinition() != pItemDef_WorldTraveler )
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
GCSDK::CSharedObjectTypeCache *pTypeCache = FindAccountTypeCache( pEconItem->GetAccountID(), CTFMapContribution::k_nTypeID );
|
|
|
|
|
if ( !pTypeCache )
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
static const char *kDonationLevels[] =
|
|
|
|
|
{
|
|
|
|
|
"#TF_MapDonationLevel_Bronze",
|
|
|
|
|
"#TF_MapDonationLevel_Silver",
|
|
|
|
|
"#TF_MapDonationLevel_Gold",
|
|
|
|
|
"#TF_MapDonationLevel_Platinum",
|
|
|
|
|
"#TF_MapDonationLevel_Diamond",
|
|
|
|
|
"#TF_MapDonationLevel_Australium1",
|
|
|
|
|
"#TF_MapDonationLevel_Australium2",
|
|
|
|
|
"#TF_MapDonationLevel_Australium3",
|
|
|
|
|
"#TF_MapDonationLevel_Unobtainium"
|
|
|
|
|
};
|
|
|
|
|
const int kNumDonationLevels = ARRAYSIZE( kDonationLevels );
|
|
|
|
|
const int kNumDonationsPerLevel = 25;
|
|
|
|
|
|
|
|
|
|
CUtlVector<const CTFMapContribution *> vecContributionsPerLevel[ kNumDonationLevels ];
|
|
|
|
|
|
|
|
|
|
for ( uint32 i = 0; i < pTypeCache->GetCount(); ++i )
|
|
|
|
|
{
|
|
|
|
|
CTFMapContribution *pMapContribution = (CTFMapContribution*)( pTypeCache->GetObject( i ) );
|
|
|
|
|
const CEconItemDefinition *pMapItemDef = GetItemSchema()->GetItemDefinition( pMapContribution->Obj().def_index() );
|
|
|
|
|
if ( pMapItemDef )
|
|
|
|
|
{
|
|
|
|
|
int iLevel = MIN( pMapContribution->Obj().contribution_level() / kNumDonationsPerLevel, kNumDonationLevels - 1 );
|
|
|
|
|
vecContributionsPerLevel[iLevel].AddToTail( pMapContribution );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
for ( int i = 0; i < kNumDonationLevels; ++i )
|
|
|
|
|
{
|
|
|
|
|
const CUtlVector<const CTFMapContribution *>& vecContributions = vecContributionsPerLevel[i];
|
|
|
|
|
if ( vecContributions.Count() > 0 )
|
|
|
|
|
{
|
|
|
|
|
// Add header like "Silver:" to show the level of contribution for each of the maps following.
|
|
|
|
|
LocalizedAddDescLine( pLocalizationProvider, kDonationLevels[i], ATTRIB_COL_ITEMSET_NAME, kDescLineFlag_Misc );
|
|
|
|
|
|
|
|
|
|
// Add a label showing the map names and number of contributions for each map.
|
|
|
|
|
locchar_t tempDescription[MAX_ITEM_DESCRIPTION_LENGTH] = { 0 };
|
|
|
|
|
FOR_EACH_VEC( vecContributions, j )
|
|
|
|
|
{
|
|
|
|
|
const CTFMapContribution *pMapContribution = vecContributions[j];
|
|
|
|
|
const CEconItemDefinition *pMapItemDef = GetItemSchema()->GetItemDefinition( pMapContribution->Obj().def_index() );
|
|
|
|
|
Assert( pMapItemDef );
|
|
|
|
|
|
|
|
|
|
const char *pszMapNameLocalizationToken = pMapItemDef->GetDefinitionString( "map_name", NULL );
|
|
|
|
|
if ( pszMapNameLocalizationToken )
|
|
|
|
|
{
|
|
|
|
|
loc_sncat( tempDescription,
|
|
|
|
|
CConstructLocalizedString( pLocalizationProvider->Find( "#Attrib_MapDonation" ),
|
|
|
|
|
pLocalizationProvider->Find( pszMapNameLocalizationToken ),
|
|
|
|
|
(uint32)pMapContribution->Obj().contribution_level() ),
|
|
|
|
|
MAX_ITEM_DESCRIPTION_LENGTH );
|
|
|
|
|
|
|
|
|
|
if ( j < ( vecContributions.Count() - 1 ) )
|
|
|
|
|
{
|
|
|
|
|
loc_sncat( tempDescription, LOCCHAR( ", " ), MAX_ITEM_DESCRIPTION_LENGTH );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ( tempDescription[0] )
|
|
|
|
|
{
|
|
|
|
|
AddDescLine( tempDescription, ATTRIB_COL_POSITIVE, kDescLineFlag_Misc );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
// Purpose:
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
void CEconItemDescription::Generate_FriendlyHat( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem )
|
|
|
|
|
{
|
|
|
|
|
Assert( pLocalizationProvider );
|
|
|
|
|
Assert( pEconItem );
|
|
|
|
|
|
|
|
|
|
static CSchemaItemDefHandle pItemDef_FriendlyHat( "Friendly Item" );
|
|
|
|
|
if ( !pItemDef_FriendlyHat || pEconItem->GetItemDefinition() != pItemDef_FriendlyHat )
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
const CTFPlayerInfo *pPlayerInfo = FindAccountTypeCacheSingleton<CTFPlayerInfo>( pEconItem->GetAccountID(), CTFPlayerInfo::k_nTypeID );
|
|
|
|
|
if ( !pPlayerInfo )
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
AddDescLine( CConstructLocalizedString( pLocalizationProvider->Find( "#Attrib_NewUsersHelped" ), (uint32)pPlayerInfo->Obj().num_new_users_helped() ), ATTRIB_COL_POSITIVE, kDescLineFlag_Misc );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
// Purpose:
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
void CEconItemDescription::Generate_SaxxyAwardDesc( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem )
|
|
|
|
|
{
|
|
|
|
|
Assert( pLocalizationProvider );
|
|
|
|
|
Assert( pEconItem );
|
|
|
|
|
|
|
|
|
|
// Don't display anything for items besides the Saxxy itself.
|
|
|
|
|
static CSchemaItemDefHandle pItemDef_Saxxy( "Saxxy" );
|
|
|
|
|
static CSchemaItemDefHandle pItemDef_MemoryMaker( "The Memory Maker" );
|
|
|
|
|
if ( ( !pItemDef_Saxxy || pEconItem->GetItemDefinition() != pItemDef_Saxxy ) &&
|
|
|
|
|
( !pItemDef_MemoryMaker || pEconItem->GetItemDefinition() != pItemDef_MemoryMaker ) )
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Output our award category if present, or abort if absent.
|
|
|
|
|
static CSchemaAttributeDefHandle pAttrDef_SaxxyAwardCategory( "saxxy award category" );
|
|
|
|
|
static CSchemaAttributeDefHandle pAttrDef_EventDate( "event date" );
|
|
|
|
|
|
|
|
|
|
uint32 unAwardCategory,
|
|
|
|
|
unEventDate;
|
|
|
|
|
if ( !pEconItem->FindAttribute( pAttrDef_SaxxyAwardCategory, &unAwardCategory ) ||
|
|
|
|
|
!pEconItem->FindAttribute( pAttrDef_EventDate, &unEventDate ) )
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CRTime cTime( unEventDate );
|
|
|
|
|
cTime.SetToGMT( false );
|
|
|
|
|
|
|
|
|
|
const char *pszFormatString = "#Attrib_SaxxyAward";
|
|
|
|
|
if ( pEconItem->GetItemDefinition() == pItemDef_MemoryMaker )
|
|
|
|
|
{
|
|
|
|
|
pszFormatString = "#Attrib_MemoryMakerAward";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
AddDescLine( CConstructLocalizedString( pLocalizationProvider->Find( pszFormatString ),
|
|
|
|
|
pLocalizationProvider->Find( CFmtStr( "Replay_Contest_Category%d", unAwardCategory ).Access() ),
|
|
|
|
|
(uint32)cTime.GetYear() ),
|
|
|
|
|
ATTRIB_COL_POSITIVE,
|
|
|
|
|
kDescLineFlag_Misc );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
// Purpose:
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
void CEconItemDescription::Generate_MvmChallenges( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem )
|
|
|
|
|
{
|
|
|
|
|
// Look for our "challenges completed" attribute. If we have this, we assume we're a badge
|
|
|
|
|
// of some kind. If we don't, we don't display MvM information. This would be a little weird
|
|
|
|
|
// for level 0 badges that have no completed challenges, but those are something that currently
|
|
|
|
|
// exist.
|
|
|
|
|
static CSchemaAttributeDefHandle pAttrDef_ChallengesCompleted( CTFItemSchema::k_rchMvMChallengeCompletedMaskAttribName );
|
|
|
|
|
|
|
|
|
|
uint32 unMask = 0;
|
|
|
|
|
if ( !pEconItem->FindAttribute( pAttrDef_ChallengesCompleted, &unMask ) )
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
// Look through our list of MvM tours to figure out which badge this came from. The badge itself
|
|
|
|
|
// doesn't know and we need this information to figure out which completion bits map to which
|
|
|
|
|
// missions.
|
|
|
|
|
const MvMTour_t *pTour = NULL;
|
|
|
|
|
|
|
|
|
|
FOR_EACH_VEC( GetItemSchema()->GetMvmTours(), i )
|
|
|
|
|
{
|
|
|
|
|
const MvMTour_t& tour = GetItemSchema()->GetMvmTours()[i];
|
|
|
|
|
|
|
|
|
|
if ( tour.m_pBadgeItemDef == pEconItem->GetItemDefinition() )
|
|
|
|
|
{
|
|
|
|
|
pTour = &tour;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Couldn't find a tour matching this badge? (This can happen if a client has a busted schema or if
|
|
|
|
|
// we remove a tour for some reason.)
|
|
|
|
|
if ( !pTour )
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
const CUtlVector<MvMMission_t>& vecAllMissions = GetItemSchema()->GetMvmMissions();
|
|
|
|
|
CUtlVector<int> vecCompletedMissions;
|
|
|
|
|
|
|
|
|
|
FOR_EACH_VEC( pTour->m_vecMissions, i )
|
|
|
|
|
{
|
|
|
|
|
// Make sure our mission index is valid based on our current schema. If we're a client playing a
|
|
|
|
|
// game during a GC roll, we could wind up looking at someone else's badge where they have a
|
|
|
|
|
// mission that we don't understand.
|
|
|
|
|
const int iMissionIndex = pTour->m_vecMissions[i].m_iMissionIndex;
|
|
|
|
|
if ( !vecAllMissions.IsValidIndex( iMissionIndex ) )
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
const int iBadgeSlot = pTour->m_vecMissions[i].m_iBadgeSlot;
|
|
|
|
|
if ( iBadgeSlot >= 0 && ((unMask & (1U << iBadgeSlot)) != 0) )
|
|
|
|
|
{
|
|
|
|
|
vecCompletedMissions.AddToTail( iMissionIndex );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Add a summary line for the number they have completed
|
|
|
|
|
AddDescLine(
|
|
|
|
|
CConstructLocalizedString(
|
|
|
|
|
pLocalizationProvider->Find( "#Attrib_MvMChallengesCompletedSummary" ),
|
|
|
|
|
uint32( vecCompletedMissions.Count() )
|
|
|
|
|
),
|
|
|
|
|
ATTRIB_COL_POSITIVE,
|
|
|
|
|
kDescLineFlag_Misc
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// Detail lines for each completed challenge
|
|
|
|
|
FOR_EACH_VEC( vecCompletedMissions, i )
|
|
|
|
|
{
|
|
|
|
|
const MvMMission_t& mission = vecAllMissions[ vecCompletedMissions[i] ];
|
|
|
|
|
const MvMMap_t& map = GetItemSchema()->GetMvmMaps()[ mission.m_iDisplayMapIndex ];
|
|
|
|
|
const locchar_t *pszLocFmt = pLocalizationProvider->Find( "#Attrib_MvMChallengeCompletedDetail" );
|
|
|
|
|
const locchar_t *pszLocMap = pLocalizationProvider->Find( map.m_sDisplayName.Get() );
|
|
|
|
|
const locchar_t *pszLocChal = pLocalizationProvider->Find( mission.m_sDisplayName.Get() );
|
|
|
|
|
if ( pszLocFmt && pszLocMap && pszLocChal )
|
|
|
|
|
{
|
|
|
|
|
CConstructLocalizedString locLine(
|
|
|
|
|
pszLocFmt,
|
|
|
|
|
pszLocMap,
|
|
|
|
|
pszLocChal
|
|
|
|
|
);
|
|
|
|
|
AddDescLine(
|
|
|
|
|
locLine,
|
|
|
|
|
ATTRIB_COL_POSITIVE,
|
|
|
|
|
kDescLineFlag_Misc
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
// Purpose:
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
void CEconItemDescription::Generate_SquadSurplusClaimedBy( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem )
|
|
|
|
|
{
|
|
|
|
|
Assert( pLocalizationProvider );
|
|
|
|
|
Assert( pEconItem );
|
|
|
|
|
|
|
|
|
|
static CSchemaAttributeDefHandle pAttrDef_SquadSurplusClaimer( "squad surplus claimer id" );
|
|
|
|
|
static CSchemaAttributeDefHandle pAttrDef_EventDate( "event date" );
|
|
|
|
|
|
|
|
|
|
attrib_value_t val_GifterId;
|
|
|
|
|
if ( pAttrDef_SquadSurplusClaimer&& pEconItem->FindAttribute( pAttrDef_SquadSurplusClaimer, &val_GifterId ) )
|
|
|
|
|
{
|
|
|
|
|
// Who gifted us this present?
|
|
|
|
|
AddAttributeDescription( pLocalizationProvider, pAttrDef_SquadSurplusClaimer, val_GifterId );
|
|
|
|
|
|
|
|
|
|
// Do we also have (optional) information about when it happened?
|
|
|
|
|
attrib_value_t val_EventData;
|
|
|
|
|
if ( pAttrDef_EventDate && pEconItem->FindAttribute( pAttrDef_EventDate, &val_EventData ) )
|
|
|
|
|
{
|
|
|
|
|
AddAttributeDescription( pLocalizationProvider, pAttrDef_EventDate, val_EventData );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
// Purpose:
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
void CEconItemDescription::Generate_DynamicRecipe( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem )
|
|
|
|
|
{
|
|
|
|
|
// Gather our attributes we care about
|
|
|
|
|
CRecipeComponentMatchingIterator componentIterator( pEconItem, NULL );
|
|
|
|
|
pEconItem->IterateAttributes( &componentIterator );
|
|
|
|
|
|
|
|
|
|
// Nothing to say, bail!
|
|
|
|
|
if( !componentIterator.GetMatchingComponentInputs().Count() &&
|
|
|
|
|
!componentIterator.GetMatchingComponentOutputs().Count() )
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Add the no partial complete tag if the attribute exists
|
|
|
|
|
static CSchemaAttributeDefHandle pAttrib_NoPartialComplete( "recipe no partial complete" );
|
|
|
|
|
if ( pEconItem->FindAttribute( pAttrib_NoPartialComplete ) )
|
|
|
|
|
{
|
|
|
|
|
LocalizedAddDescLine( pLocalizationProvider, "TF_ItemDynamic_Recipe_No_Partial_Completion", ATTRIB_COL_POSITIVE, kDescLineFlag_Misc );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
AddEmptyDescLine();
|
|
|
|
|
|
|
|
|
|
if ( componentIterator.GetMatchingComponentInputs().Count() )
|
|
|
|
|
{
|
|
|
|
|
// Print out item input header
|
|
|
|
|
LocalizedAddDescLine( pLocalizationProvider, "TF_ItemDynamic_Recipe_Inputs", ATTRIB_COL_NEUTRAL, kDescLineFlag_Misc );
|
|
|
|
|
// Print out inputs
|
|
|
|
|
FOR_EACH_VEC( componentIterator.GetMatchingComponentInputs(), i )
|
|
|
|
|
{
|
|
|
|
|
CAttribute_DynamicRecipeComponent attribValue;
|
|
|
|
|
pEconItem->FindAttribute( componentIterator.GetMatchingComponentInputs()[i], &attribValue );
|
|
|
|
|
|
|
|
|
|
const GameItemDefinition_t *pItemDef = dynamic_cast<GameItemDefinition_t *>( GetItemSchema()->GetItemDefinition( attribValue.def_index() ) );
|
|
|
|
|
if ( !pItemDef )
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
int nCount = attribValue.num_required() - attribValue.num_fulfilled();
|
|
|
|
|
|
|
|
|
|
// This is a completed component. We don't want to show it (for now)
|
|
|
|
|
if( nCount == 0 )
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
CEconItem tempItem;
|
|
|
|
|
if ( !DecodeItemFromEncodedAttributeString( attribValue, &tempItem ) )
|
|
|
|
|
{
|
|
|
|
|
AssertMsg2( 0, "%s: Unable to decode dynamic recipe input attribute on item %llu.", __FUNCTION__, pEconItem->GetID() );
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
locchar_t lineItem[256];
|
|
|
|
|
locchar_t loc_ItemName[MAX_ITEM_NAME_LENGTH];
|
|
|
|
|
GenerateLocalizedFullItemName( loc_ItemName, pLocalizationProvider, &tempItem, k_EGenerateLocalizedFullItemName_Default, m_pHashContext == NULL );
|
|
|
|
|
|
|
|
|
|
loc_sprintf_safe( lineItem,
|
|
|
|
|
LOCCHAR("%s x %d"),
|
|
|
|
|
loc_ItemName,
|
|
|
|
|
nCount
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
AddDescLine( lineItem, ATTRIB_COL_ITEMSET_MISSING, kDescLineFlag_Misc );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
AddEmptyDescLine();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Print out outputs
|
|
|
|
|
LocalizedAddDescLine( pLocalizationProvider, "TF_ItemDynamic_Recipe_Outputs", ATTRIB_COL_NEUTRAL, kDescLineFlag_Misc );
|
|
|
|
|
FOR_EACH_VEC( componentIterator.GetMatchingComponentOutputs(), i )
|
|
|
|
|
{
|
|
|
|
|
CAttribute_DynamicRecipeComponent attribValue;
|
|
|
|
|
pEconItem->FindAttribute( componentIterator.GetMatchingComponentOutputs()[i], &attribValue );
|
|
|
|
|
|
|
|
|
|
CEconItem tempItem;
|
|
|
|
|
if ( !DecodeItemFromEncodedAttributeString( attribValue, &tempItem ) )
|
|
|
|
|
{
|
|
|
|
|
AssertMsg2( 0, "%s: Unable to decode dynamic recipe output attribute on item %llu.", __FUNCTION__, pEconItem->GetID() );
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
locchar_t loc_ItemName[MAX_ITEM_NAME_LENGTH];
|
|
|
|
|
GenerateLocalizedFullItemName( loc_ItemName, pLocalizationProvider, &tempItem, k_EGenerateLocalizedFullItemName_Default, m_pHashContext == NULL );
|
|
|
|
|
|
|
|
|
|
AddDescLine( loc_ItemName, /* this will be ignored: */ ATTRIB_COL_ITEMSET_MISSING, kDescLineFlag_Misc );
|
|
|
|
|
|
|
|
|
|
// Iterate through the attributes on this temp item and have it store the attributes that should affect
|
|
|
|
|
// this component's name. Once we have that, have it fill out a temporary CEconItemDescription.
|
|
|
|
|
CRecipeNameAttributeDisplayer recipeAttributeIterator;
|
|
|
|
|
tempItem.IterateAttributes( &recipeAttributeIterator );
|
|
|
|
|
recipeAttributeIterator.SortAttributes();
|
|
|
|
|
CEconItemDescription tempDescription;
|
|
|
|
|
recipeAttributeIterator.Finalize( &tempItem, &tempDescription, pLocalizationProvider );
|
|
|
|
|
|
|
|
|
|
// Check if that temp CEconItemDescription has any attributes we want. If so, steal them.
|
|
|
|
|
if ( tempDescription.m_vecDescLines.Count() > 0 )
|
|
|
|
|
{
|
|
|
|
|
locchar_t loc_Attribs[MAX_ITEM_NAME_LENGTH] = LOCCHAR("");
|
|
|
|
|
|
|
|
|
|
// Put the attributes on the next line in parenthesis
|
|
|
|
|
loc_scat_safe( loc_Attribs, LOCCHAR("(") );
|
|
|
|
|
|
|
|
|
|
// Put in each attribute
|
|
|
|
|
FOR_EACH_VEC( tempDescription.m_vecDescLines, j )
|
|
|
|
|
{
|
|
|
|
|
// Comma separated
|
|
|
|
|
if ( j > 0 )
|
|
|
|
|
{
|
|
|
|
|
loc_scat_safe( loc_Attribs, LOCCHAR(", ") );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
loc_sprintf_safe( loc_Attribs,
|
|
|
|
|
LOCCHAR("%s%s"),
|
|
|
|
|
loc_Attribs,
|
|
|
|
|
tempDescription.m_vecDescLines[j].sText.Get() );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
loc_scat_safe( loc_Attribs, LOCCHAR(")") );
|
|
|
|
|
|
|
|
|
|
// Print out in the same color as the item name above
|
|
|
|
|
AddDescLine( loc_Attribs, /* this will be ignored: */ ATTRIB_COL_ITEMSET_MISSING, kDescLineFlag_Misc );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
void CEconItemDescription::Generate_Leaderboard( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem )
|
|
|
|
|
{
|
|
|
|
|
#ifdef GC_DLL
|
|
|
|
|
return;
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#ifdef TF_CLIENT_DLL
|
|
|
|
|
Assert( pLocalizationProvider );
|
|
|
|
|
Assert( pEconItem );
|
|
|
|
|
|
|
|
|
|
static CSchemaAttributeDefHandle pAttrDef_DisplayDuckLeaderboard( "display duck leaderboard" );
|
|
|
|
|
|
|
|
|
|
if ( pEconItem->FindAttribute( pAttrDef_DisplayDuckLeaderboard ) )
|
|
|
|
|
{
|
|
|
|
|
// Friend Board
|
|
|
|
|
//locchar_t lineItem[256];
|
|
|
|
|
//
|
|
|
|
|
//AddDescLine( pLocalizationProvider->Find( "#TF_DuckLeaderboard_Friends" ), ATTRIB_COL_POSITIVE, kDescLineFlag_Misc );
|
|
|
|
|
//
|
|
|
|
|
//CUtlVector< LeaderboardEntry_t > scores;
|
|
|
|
|
//Leaderboards_GetDuckLeaderboard( scores, g_szDuckLeaderboardNames[0] );
|
|
|
|
|
|
|
|
|
|
//// Show max of top 10
|
|
|
|
|
//for ( int i = 0; i < scores.Count() && i < 10; i++ )
|
|
|
|
|
//{
|
|
|
|
|
// const locchar_t *pName = FindAccountPersonaName( scores[i].m_steamIDUser.GetAccountID() );
|
|
|
|
|
// uint32 iRank = scores[i].m_nGlobalRank;
|
|
|
|
|
// uint32 iScore = scores[i].m_nScore;
|
|
|
|
|
//
|
|
|
|
|
// AddDescLine(
|
|
|
|
|
// CConstructLocalizedString(
|
|
|
|
|
// pLocalizationProvider->Find( "#TF_DuckLeaderboard_Entry" ),
|
|
|
|
|
// iRank, pName, iScore
|
|
|
|
|
// ),
|
|
|
|
|
// ATTRIB_COL_POSITIVE,
|
|
|
|
|
// kDescLineFlag_Misc
|
|
|
|
|
// );
|
|
|
|
|
//}
|
|
|
|
|
}
|
|
|
|
|
#endif // TF_CLIENT_DLL
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endif // PROJECT_TF
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
// Purpose:
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
const CEconItemDefinition *GetPaintItemDefinitionForPaintedItem( const IEconItemInterface *pEconItem )
|
|
|
|
|
{
|
|
|
|
|
static CSchemaAttributeDefHandle pAttribDef_Paint( "set item tint RGB" );
|
|
|
|
|
|
|
|
|
|
attrib_value_t unPaintRGBAttrBits;
|
|
|
|
|
if ( !pAttribDef_Paint || !pEconItem->FindAttribute( pAttribDef_Paint, &unPaintRGBAttrBits ) )
|
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
|
|
const CEconItemSchema::ToolsItemDefinitionMap_t &toolDefs = GetItemSchema()->GetToolsItemDefinitionMap();
|
|
|
|
|
|
|
|
|
|
FOR_EACH_MAP_FAST( toolDefs, i )
|
|
|
|
|
{
|
|
|
|
|
const CEconItemDefinition *pItemDef = toolDefs[i];
|
|
|
|
|
|
|
|
|
|
// ignore everything that is not a paint can tool
|
|
|
|
|
const IEconTool *pEconTool = pItemDef->GetEconTool();
|
|
|
|
|
if ( pEconTool && !V_strcmp( pEconTool->GetTypeName(), "paint_can" ) )
|
|
|
|
|
{
|
|
|
|
|
attrib_value_t unPaintRGBAttrCompareBits;
|
|
|
|
|
if ( FindAttribute( pItemDef, pAttribDef_Paint, &unPaintRGBAttrCompareBits ) && unPaintRGBAttrCompareBits == unPaintRGBAttrBits )
|
|
|
|
|
return pItemDef;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
// Purpose: Specify target (strangifiers, etc that can only be applied to specific items)
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
void CEconItemDescription::Generate_XifierToolTargetItem( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem )
|
|
|
|
|
{
|
|
|
|
|
Assert( pLocalizationProvider );
|
|
|
|
|
Assert( pEconItem );
|
|
|
|
|
|
|
|
|
|
// Make sure it's a tool of the appropriate type
|
|
|
|
|
const CEconTool_Xifier *pTool = pEconItem->GetItemDefinition()->GetTypedEconTool<CEconTool_Xifier>();
|
|
|
|
|
if ( pTool == NULL )
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
// Make sure there is a specific target item
|
|
|
|
|
static CSchemaAttributeDefHandle pAttribDef_ToolTargetItem( "tool target item" );
|
|
|
|
|
float flItemDef;
|
|
|
|
|
if( pAttribDef_ToolTargetItem && FindAttribute_UnsafeBitwiseCast<attrib_value_t>( pEconItem, pAttribDef_ToolTargetItem, &flItemDef ) )
|
|
|
|
|
{
|
|
|
|
|
locchar_t szTargetItemName[ MAX_ITEM_NAME_LENGTH ] = LOCCHAR("Unknown Item");
|
|
|
|
|
|
|
|
|
|
// It's a tool, see if it has a tool target item attribute
|
|
|
|
|
const item_definition_index_t unItemDef = flItemDef;
|
|
|
|
|
const CEconItemDefinition *pEconTargetDef = GetItemSchema()->GetItemDefinition( unItemDef );
|
|
|
|
|
|
|
|
|
|
// Start with the base name.
|
|
|
|
|
if ( pEconTargetDef )
|
|
|
|
|
{
|
|
|
|
|
GetLocalizedBaseItemName( szTargetItemName, pLocalizationProvider, pEconTargetDef );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const char *pszDesc = pTool->GetItemDescToolTargetLocToken();
|
|
|
|
|
AssertMsg( pszDesc && *pszDesc, "%s: missing 'item_desc_tool_target' key", pTool->GetTypeName() );
|
|
|
|
|
AddDescLine( CConstructLocalizedString( pLocalizationProvider->Find( pszDesc ),
|
|
|
|
|
szTargetItemName ),
|
|
|
|
|
ATTRIB_COL_NEUTRAL,
|
|
|
|
|
kDescLineFlag_Desc );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
// Purpose:
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
void CEconItemDescription::Generate_Painted( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem )
|
|
|
|
|
{
|
|
|
|
|
Assert( pLocalizationProvider );
|
|
|
|
|
Assert( pEconItem );
|
|
|
|
|
|
|
|
|
|
static CSchemaAttributeDefHandle pAttrDef_PaintEffect( "Paint Effect" );
|
|
|
|
|
|
|
|
|
|
float fPaintEffectType;
|
|
|
|
|
if ( pAttrDef_PaintEffect && FindAttribute_UnsafeBitwiseCast<attrib_value_t>( pEconItem, pAttrDef_PaintEffect, &fPaintEffectType ) )
|
|
|
|
|
{
|
|
|
|
|
if ( fPaintEffectType == 1 )
|
|
|
|
|
{
|
|
|
|
|
LocalizedAddDescLine( pLocalizationProvider, "Econ_Paint_Effect_Oscillating", ATTRIB_COL_NEUTRAL, kDescLineFlag_Misc );
|
|
|
|
|
}
|
|
|
|
|
else if ( fPaintEffectType == 2 )
|
|
|
|
|
{
|
|
|
|
|
LocalizedAddDescLine( pLocalizationProvider, "Econ_Paint_Effect_Position", ATTRIB_COL_NEUTRAL, kDescLineFlag_Misc );
|
|
|
|
|
}
|
|
|
|
|
else if ( fPaintEffectType == 3 )
|
|
|
|
|
{
|
|
|
|
|
LocalizedAddDescLine( pLocalizationProvider, "Econ_Paint_Effect_LowHealthWarning", ATTRIB_COL_NEUTRAL, kDescLineFlag_Misc );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Find the name of the paint we have applied in the least efficient way imaginable!
|
|
|
|
|
const CEconItemDefinition *pItemDef = pEconItem->GetItemDefinition();
|
|
|
|
|
static CSchemaAttributeDefHandle pAttrDef_ShowPaint( "show paint description" );
|
|
|
|
|
if ( pItemDef && ( !pItemDef->IsTool() || FindAttribute( pEconItem, pAttrDef_ShowPaint ) ) )
|
|
|
|
|
{
|
|
|
|
|
const CEconItemDefinition *pTempDef = GetPaintItemDefinitionForPaintedItem( pEconItem );
|
|
|
|
|
if ( pTempDef )
|
|
|
|
|
{
|
|
|
|
|
const locchar_t *locLocalizedPaintName = pLocalizationProvider->Find( pTempDef->GetItemBaseName() );
|
|
|
|
|
|
|
|
|
|
if ( locLocalizedPaintName )
|
|
|
|
|
{
|
|
|
|
|
AddDescLine( CConstructLocalizedString( pLocalizationProvider->Find( "Econ_Paint_Name" ),
|
|
|
|
|
locLocalizedPaintName ),
|
|
|
|
|
ATTRIB_COL_LEVEL,
|
|
|
|
|
kDescLineFlag_Misc );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
// Purpose:
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
void CEconItemDescription::Generate_Uses( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem )
|
|
|
|
|
{
|
|
|
|
|
Assert( pLocalizationProvider );
|
|
|
|
|
Assert( pEconItem );
|
|
|
|
|
|
|
|
|
|
// don't display a quantity if we have the unlimited quantity attribute
|
|
|
|
|
static CSchemaAttributeDefHandle unlimitedQuantityAttribute( "unlimited quantity" );
|
|
|
|
|
if ( pEconItem->FindAttribute( unlimitedQuantityAttribute ) )
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
// Collection tools don't display this.
|
|
|
|
|
const GameItemDefinition_t *pItemDef = pEconItem->GetItemDefinition();
|
|
|
|
|
if ( !pItemDef )
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
const IEconTool *pEconTool = pItemDef->GetEconTool();
|
|
|
|
|
if ( !pEconTool->ShouldDisplayQuantity( pEconItem ) )
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
int iQuantity = pEconItem->GetQuantity();
|
|
|
|
|
bool bIsTool = pItemDef->GetItemClass() && !Q_strcmp( pItemDef->GetItemClass(), "tool" );
|
|
|
|
|
bool bIsConsumable = ( pItemDef->GetCapabilities() & ITEM_CAP_USABLE_GC ) != 0 && iQuantity != 0;
|
|
|
|
|
|
|
|
|
|
if ( bIsTool || bIsConsumable )
|
|
|
|
|
{
|
|
|
|
|
locchar_t wszQuantity[10];
|
|
|
|
|
loc_sprintf_safe( wszQuantity, LOCCHAR( "%d" ), iQuantity );
|
|
|
|
|
|
|
|
|
|
// Add an empty line before the usage display.
|
|
|
|
|
AddEmptyDescLine();
|
|
|
|
|
|
|
|
|
|
// Display our usage count.
|
|
|
|
|
AddDescLine( CConstructLocalizedString( pLocalizationProvider->Find( "#Attrib_LimitedUse" ), &wszQuantity[0] ), ATTRIB_COL_LIMITED_USE, kDescLineFlag_Misc );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// --------------------------------------------------------------------------
|
|
|
|
|
// Purpose:
|
|
|
|
|
// --------------------------------------------------------------------------
|
|
|
|
|
void CEconItemDescription::Generate_LootListDesc( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem )
|
|
|
|
|
{
|
|
|
|
|
Assert( pLocalizationProvider );
|
|
|
|
|
Assert( pEconItem );
|
|
|
|
|
|
|
|
|
|
const CEconItemDefinition *pItemDef = pEconItem->GetItemDefinition();
|
|
|
|
|
if ( !pItemDef )
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
// Don't add this description if the item is a special crate type.
|
|
|
|
|
const IEconTool *pEconTool = pItemDef->GetEconTool();
|
|
|
|
|
const bool bIsRestrictedCrate = pEconTool && pEconTool->GetUsageRestriction()
|
|
|
|
|
? !V_stricmp( pEconTool->GetUsageRestriction(), "winter" ) || !V_stricmp( pEconTool->GetUsageRestriction(), "summer" )
|
|
|
|
|
: false;
|
|
|
|
|
|
|
|
|
|
if ( bIsRestrictedCrate )
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
// Do we have a generation code we want to make public? We do this regardless of whether we're describing our
|
|
|
|
|
// loot list contents in detail.
|
|
|
|
|
{
|
|
|
|
|
static CSchemaAttributeDefHandle pAttrDef_CrateGenerationCode( "crate generation code" );
|
|
|
|
|
CAttribute_String sCrateGenerationCode;
|
|
|
|
|
|
|
|
|
|
const locchar_t *pszCrateGenerationCodeLoc = pLocalizationProvider->Find( "Attrib_CrateGenerationCode" );
|
|
|
|
|
|
|
|
|
|
if ( pEconItem->FindAttribute( pAttrDef_CrateGenerationCode, &sCrateGenerationCode ) && sCrateGenerationCode.value().length() > 0 )
|
|
|
|
|
{
|
|
|
|
|
CUtlConstStringBase<locchar_t> loc_sAttrValue;
|
|
|
|
|
pLocalizationProvider->ConvertUTF8ToLocchar( sCrateGenerationCode.value().c_str(), &loc_sAttrValue );
|
|
|
|
|
|
|
|
|
|
AddDescLine( CConstructLocalizedString( pszCrateGenerationCodeLoc, loc_sAttrValue.Get() ),
|
|
|
|
|
ATTRIB_COL_POSITIVE,
|
|
|
|
|
kDescLineFlag_Misc );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Grab the actual contents of our loot list.
|
|
|
|
|
CCrateLootListWrapper LootListWrapper( pEconItem );
|
|
|
|
|
const IEconLootList *pLootList = LootListWrapper.GetEconLootList();
|
|
|
|
|
|
|
|
|
|
if ( pLootList == nullptr )
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
// If our base loot list is set not to list contents, skip the header/footer as well and don't display anything.
|
|
|
|
|
if ( !pLootList->BPublicListContents() )
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
AddEmptyDescLine();
|
|
|
|
|
|
|
|
|
|
if ( !pLootList->GetLootListCollectionReference() )
|
|
|
|
|
{
|
|
|
|
|
LocalizedAddDescLine( pLocalizationProvider, pLootList->GetLootListHeaderLocalizationKey(), ATTRIB_COL_NEUTRAL, kDescLineFlag_Misc );
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
int iCollectionIndex = GetItemSchema()->GetItemCollections().Find( pLootList->GetLootListCollectionReference() );
|
|
|
|
|
if ( GetItemSchema()->GetItemCollections().IsValidIndex( iCollectionIndex ) )
|
|
|
|
|
{
|
|
|
|
|
LocalizedAddDescLine( pLocalizationProvider, (GetItemSchema()->GetItemCollections()[iCollectionIndex])->m_pszLocalizedDesc, ATTRIB_COL_NEUTRAL, kDescLineFlag_Misc );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
class CDescriptionLootListIterator : public IEconLootList::IEconLootListIterator
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
CDescriptionLootListIterator( CEconItemDescription *pThis, const CLocalizationProvider *pLocalizationProvider, bool bUseProperName )
|
|
|
|
|
: m_pThis( pThis )
|
|
|
|
|
, m_pLocalizationProvider( pLocalizationProvider )
|
|
|
|
|
, m_bUseProperName( bUseProperName )
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
virtual void OnIterate( item_definition_index_t unItemDefIndex ) OVERRIDE
|
|
|
|
|
{
|
|
|
|
|
const CEconItemDefinition *pItemDef = GetItemSchema()->GetItemDefinition( unItemDefIndex );
|
|
|
|
|
if ( pItemDef )
|
|
|
|
|
{
|
|
|
|
|
// Check if this item is already owned
|
|
|
|
|
bool bOwned = false;
|
|
|
|
|
bool bUnusual = false;
|
|
|
|
|
#ifdef CLIENT_DLL
|
|
|
|
|
CPlayerInventory *pLocalInv = TFInventoryManager()->GetLocalInventory();
|
|
|
|
|
if ( pLocalInv )
|
|
|
|
|
{
|
|
|
|
|
for ( int i = 0; i < pLocalInv->GetItemCount(); ++i )
|
|
|
|
|
{
|
|
|
|
|
CEconItemView *pItem = pLocalInv->GetItem( i );
|
|
|
|
|
if ( pItem->GetItemDefinition() == pItemDef )
|
|
|
|
|
{
|
|
|
|
|
bOwned = true;
|
|
|
|
|
// Check Quality
|
|
|
|
|
if ( pItem->GetQuality() == AE_UNUSUAL )
|
|
|
|
|
{
|
|
|
|
|
bUnusual = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
const locchar_t * pCheckmark = bOwned ? m_pLocalizationProvider->Find( "TF_Checkmark" ) : m_pLocalizationProvider->Find( "TF_LackOfCheckmark" );
|
|
|
|
|
if ( bOwned && bUnusual )
|
|
|
|
|
{
|
|
|
|
|
pCheckmark = m_pLocalizationProvider->Find( "TF_Checkmark_Unusual" );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
attrib_colors_t colorRarity = ATTRIB_COL_RARITY_DEFAULT;
|
|
|
|
|
const CEconItemRarityDefinition* pItemRarity = GetItemSchema()->GetRarityDefinition( pItemDef->GetRarity() );
|
|
|
|
|
if ( pItemRarity )
|
|
|
|
|
{
|
|
|
|
|
colorRarity = pItemRarity->GetAttribColor();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
m_pThis->AddDescLine(
|
|
|
|
|
CConstructLocalizedString( LOCCHAR( "%s1%s2" ), pCheckmark,
|
|
|
|
|
CEconItemLocalizedFullNameGenerator(
|
|
|
|
|
m_pLocalizationProvider,
|
|
|
|
|
pItemDef,
|
|
|
|
|
m_bUseProperName
|
|
|
|
|
).GetFullName() ),
|
|
|
|
|
colorRarity,
|
|
|
|
|
kDescLineFlag_Misc,
|
|
|
|
|
NULL,
|
|
|
|
|
pItemDef->GetDefinitionIndex()
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
CEconItemDescription *m_pThis; // look at me I'm a lambda!
|
|
|
|
|
const CLocalizationProvider *m_pLocalizationProvider;
|
|
|
|
|
bool m_bUseProperName;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
CDescriptionLootListIterator it( this, pLocalizationProvider, m_pHashContext == NULL );
|
|
|
|
|
pLootList->EnumerateUserFacingPotentialDrops( &it );
|
|
|
|
|
|
|
|
|
|
if ( pLootList->GetLootListFooterLocalizationKey() )
|
|
|
|
|
{
|
|
|
|
|
LocalizedAddDescLine( pLocalizationProvider, pLootList->GetLootListFooterLocalizationKey(), ATTRIB_COL_POSITIVE, kDescLineFlag_Misc );
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
const char *pszRareLootListFooterLocalizationKey = pItemDef->GetDefinitionString( "loot_list_rare_item_footer", "#Econ_Revolving_Loot_List_Rare_Item" );
|
|
|
|
|
LocalizedAddDescLine( pLocalizationProvider, pszRareLootListFooterLocalizationKey, ATTRIB_COL_POSITIVE, kDescLineFlag_Misc );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// --------------------------------------------------------------------------
|
|
|
|
|
void CEconItemDescription::Generate_EventDetail( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem )
|
|
|
|
|
{
|
|
|
|
|
Assert( pLocalizationProvider );
|
|
|
|
|
Assert( pEconItem );
|
|
|
|
|
|
|
|
|
|
const CEconItemDefinition *pItemDef = pEconItem->GetItemDefinition();
|
|
|
|
|
if ( !pItemDef )
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
// Check to see if we should append any extra description information
|
|
|
|
|
const char *pszEventLocalizationKey = pItemDef->GetDefinitionString( "event_desc_footer", NULL );
|
|
|
|
|
if ( pszEventLocalizationKey )
|
|
|
|
|
{
|
|
|
|
|
AddEmptyDescLine();
|
|
|
|
|
LocalizedAddDescLine( pLocalizationProvider, pszEventLocalizationKey, ATTRIB_COL_POSITIVE, kDescLineFlag_Misc );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// --------------------------------------------------------------------------
|
|
|
|
|
// Purpose:
|
|
|
|
|
// --------------------------------------------------------------------------
|
|
|
|
|
#ifdef CLIENT_DLL
|
|
|
|
|
|
|
|
|
|
static bool IsItemEquipped( uint32 unAccountID, const CEconItemDefinition *pSearchItemDef, const GameItemDefinition_t **ppFoundSetItemDef )
|
|
|
|
|
{
|
|
|
|
|
Assert( pSearchItemDef );
|
|
|
|
|
Assert( ppFoundSetItemDef );
|
|
|
|
|
|
|
|
|
|
CPlayerInventory *pInv = InventoryManager()->GetInventoryForAccount( unAccountID );
|
|
|
|
|
if ( !pInv )
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
for ( int i = 0; i < pInv->GetItemCount(); i++ )
|
|
|
|
|
{
|
|
|
|
|
const CEconItemView *pInvItem = pInv->GetItem( i );
|
|
|
|
|
if ( !pInvItem )
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
// This code is client-only so we expect to always get back an item definition pointer.
|
|
|
|
|
const GameItemDefinition_t *pInvItemDef = pInvItem->GetItemDefinition();
|
|
|
|
|
Assert( pInvItemDef );
|
|
|
|
|
|
|
|
|
|
if ( pInvItemDef->GetSetItemRemap() != pSearchItemDef->GetDefinitionIndex() )
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
if ( !pInvItem->IsEquipped() )
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
*ppFoundSetItemDef = pInvItemDef;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endif // CLIENT_DLL
|
|
|
|
|
|
|
|
|
|
void CEconItemDescription::Generate_ItemSetDesc( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem )
|
|
|
|
|
{
|
|
|
|
|
Assert( pLocalizationProvider );
|
|
|
|
|
Assert( pEconItem );
|
|
|
|
|
|
|
|
|
|
const GameItemDefinition_t *pItemDef = pEconItem->GetItemDefinition();
|
|
|
|
|
if ( !pItemDef )
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
const CEconItemSetDefinition *pItemSetDef = pItemDef->GetItemSetDefinition();
|
|
|
|
|
if ( !pItemSetDef )
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
bool bAllItemsEquipped = true; // filled in below when iterating over items
|
|
|
|
|
|
|
|
|
|
// Some item sets are tagged to only appear on items at all if the entire set is visible. Rather than
|
|
|
|
|
// walk the whole set multiple times checking for item equipped state, we build up a set of description lines
|
|
|
|
|
// that we *will* display if we display anything at all. Later, we either submit all those lines for real or
|
|
|
|
|
// return before adding any of them.
|
|
|
|
|
{
|
|
|
|
|
CUtlVector<econ_item_description_line_t> vecPotentialDescLines;
|
|
|
|
|
|
|
|
|
|
AddEmptyDescLine( &vecPotentialDescLines );
|
|
|
|
|
LocalizedAddDescLine( pLocalizationProvider, pItemSetDef->m_pszLocalizedName, ATTRIB_COL_ITEMSET_NAME, kDescLineFlag_Set | kDescLineFlag_SetName, &vecPotentialDescLines, pItemSetDef->m_iBundleItemDef );
|
|
|
|
|
|
|
|
|
|
// Kyle says: Jon wants different formatting on the GC for sets.
|
|
|
|
|
#if defined( GC_DLL )
|
|
|
|
|
#if TF_ANTI_IDLEBOT_VERIFICATION
|
|
|
|
|
if ( !m_pHashContext )
|
|
|
|
|
#endif // TF_ANTI_IDLEBOT_VERIFICATION
|
|
|
|
|
{
|
|
|
|
|
AddEmptyDescLine( &vecPotentialDescLines );
|
|
|
|
|
}
|
|
|
|
|
#endif // defined( GC_DLL ) &&
|
|
|
|
|
|
|
|
|
|
// Iterate over the items in the set. We'll output a line in different colors to show
|
|
|
|
|
// the current state of this item. For normal item sets, the color is based on whether
|
|
|
|
|
// the owner has the item in question equipped (except on the GC, where we always say
|
|
|
|
|
// "it's not equipped" to avoid confusion). For collections, the color is based on whether
|
|
|
|
|
// the item has been collected.
|
|
|
|
|
FOR_EACH_VEC( pItemSetDef->m_iItemDefs, i )
|
|
|
|
|
{
|
|
|
|
|
const GameItemDefinition_t *pOtherSetItem = dynamic_cast<const GameItemDefinition_t *>( GetItemSchema()->GetItemDefinition( pItemSetDef->m_iItemDefs[i] ) );
|
|
|
|
|
if ( !pOtherSetItem )
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
item_definition_index_t usLinkItemDefIndex = pOtherSetItem->GetDefinitionIndex();
|
|
|
|
|
|
|
|
|
|
#ifdef DOTA
|
|
|
|
|
// If the current item is part of a pack bundle, add the pack bundle to the description, rather than the individual item
|
|
|
|
|
if ( pOtherSetItem->IsPackItem() )
|
|
|
|
|
{
|
|
|
|
|
// Link to the pack bundle, not the individual pack item
|
|
|
|
|
usLinkItemDefIndex = pOtherSetItem->GetOwningPackBundle()->GetDefinitionIndex();
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
// Only used on non-GC in case we have an item misrepresenting itself intentionally for set
|
|
|
|
|
// grouping purposes. NULL elsewhere.
|
|
|
|
|
const GameItemDefinition_t *pFoundSetItemDef = NULL;
|
|
|
|
|
|
|
|
|
|
const bool bItemPresent =
|
|
|
|
|
#ifdef GC_DLL
|
|
|
|
|
false; // the GC always treats set items as unequipped for clarity in trading
|
|
|
|
|
#else
|
|
|
|
|
#if TF_ANTI_IDLEBOT_VERIFICATION
|
|
|
|
|
m_pHashContext
|
|
|
|
|
? false // when generating descriptions for GC verification we treat item sets as unequipped to match the GC
|
|
|
|
|
#endif
|
|
|
|
|
: IsItemEquipped( pEconItem->GetAccountID(), pOtherSetItem, &pFoundSetItemDef ); // non-GC display will find out whether the player in question has this item actively equipped
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
AddDescLine( CEconItemLocalizedFullNameGenerator(
|
|
|
|
|
pLocalizationProvider,
|
|
|
|
|
pFoundSetItemDef ? pFoundSetItemDef : pOtherSetItem,
|
|
|
|
|
m_pHashContext == NULL
|
|
|
|
|
).GetFullName(), bItemPresent ? ATTRIB_COL_ITEMSET_EQUIPPED : ATTRIB_COL_ITEMSET_MISSING, kDescLineFlag_Set, &vecPotentialDescLines, usLinkItemDefIndex );
|
|
|
|
|
|
|
|
|
|
bAllItemsEquipped &= bItemPresent;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If the item set is set to be only displayed when the full set is equipped, give up here and
|
|
|
|
|
// toss out our potential lines. Otherwise submit them for real.
|
|
|
|
|
if ( pItemSetDef->m_bIsHiddenSet && !bAllItemsEquipped )
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
FOR_EACH_VEC( vecPotentialDescLines, i )
|
|
|
|
|
{
|
|
|
|
|
AddDescLine( vecPotentialDescLines[i].sText.Get(), vecPotentialDescLines[i].eColor, vecPotentialDescLines[i].unMetaType, NULL, vecPotentialDescLines[i].unDefIndex );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Show the set only attributes if we have the entire set and we have bonus attributes to display.
|
|
|
|
|
bool bHasVisible = false;
|
|
|
|
|
FOR_EACH_VEC( pItemSetDef->m_iAttributes, i )
|
|
|
|
|
{
|
|
|
|
|
const CEconItemSetDefinition::itemset_attrib_t& attrib = pItemSetDef->m_iAttributes[i];
|
|
|
|
|
const CEconItemAttributeDefinition *pAttrDef = GetItemSchema()->GetAttributeDefinition( attrib.m_iAttribDefIndex );
|
|
|
|
|
|
|
|
|
|
if ( !pAttrDef->IsHidden() )
|
|
|
|
|
{
|
|
|
|
|
bHasVisible = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ( !bHasVisible )
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
AddEmptyDescLine();
|
|
|
|
|
LocalizedAddDescLine( pLocalizationProvider, "#Econ_Set_Bonus", ATTRIB_COL_ITEMSET_NAME, kDescLineFlag_Set );
|
|
|
|
|
|
|
|
|
|
FOR_EACH_VEC( pItemSetDef->m_iAttributes, i )
|
|
|
|
|
{
|
|
|
|
|
const CEconItemSetDefinition::itemset_attrib_t& attrib = pItemSetDef->m_iAttributes[i];
|
|
|
|
|
const CEconItemAttributeDefinition *pAttrDef = GetItemSchema()->GetAttributeDefinition( attrib.m_iAttribDefIndex );
|
|
|
|
|
|
|
|
|
|
if ( pAttrDef )
|
|
|
|
|
{
|
|
|
|
|
// Add the attribute description. Override the color to be grayed out if we don't have the
|
|
|
|
|
// full set equipped.
|
|
|
|
|
AddAttributeDescription( pLocalizationProvider,
|
|
|
|
|
pAttrDef,
|
|
|
|
|
*(attrib_value_t *)&attrib.m_flValue,
|
|
|
|
|
bAllItemsEquipped ? /* "do not override": */ NUM_ATTRIB_COLORS : ATTRIB_COL_ITEMSET_MISSING );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// --------------------------------------------------------------------------
|
|
|
|
|
void CEconItemDescription::Generate_CollectionDesc( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem )
|
|
|
|
|
{
|
|
|
|
|
Assert( pLocalizationProvider );
|
|
|
|
|
Assert( pEconItem );
|
|
|
|
|
|
|
|
|
|
const CEconItemDefinition *pItemDef = pEconItem->GetItemDefinition();
|
|
|
|
|
if ( !pItemDef )
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
const CEconItemCollectionDefinition *pCollection = pItemDef->GetItemCollectionDefinition();
|
|
|
|
|
if ( !pCollection )
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
// Collection Header ..
|
|
|
|
|
const locchar_t *loc_name = pLocalizationProvider->Find( pCollection->m_pszLocalizedName );
|
|
|
|
|
|
|
|
|
|
// Add a bit of spacing, this is only for the market
|
|
|
|
|
// Add empty line
|
|
|
|
|
AddDescLine( LOCCHAR( " " ), ATTRIB_COL_NEUTRAL, kDescLineFlag_CollectionName | kDescLineFlag_Empty );
|
|
|
|
|
AddDescLine( LOCCHAR( " " ), ATTRIB_COL_NEUTRAL, kDescLineFlag_CollectionName | kDescLineFlag_Empty );
|
|
|
|
|
|
|
|
|
|
AddDescLine(
|
|
|
|
|
CConstructLocalizedString( LOCCHAR( "%s1" ), loc_name ),
|
|
|
|
|
ATTRIB_COL_NEUTRAL,
|
|
|
|
|
kDescLineFlag_CollectionName
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
FOR_EACH_VEC( pCollection->m_iItemDefs, index )
|
|
|
|
|
{
|
|
|
|
|
int eFlag = kDescLineFlag_Collection;
|
|
|
|
|
const CEconItemDefinition *pTempItemDef = GetItemSchema()->GetItemDefinition( pCollection->m_iItemDefs[index] );
|
|
|
|
|
if ( pTempItemDef )
|
|
|
|
|
{
|
|
|
|
|
// Check if this item is already owned
|
|
|
|
|
bool bOwned = false;
|
|
|
|
|
bool bUnusual = false;
|
|
|
|
|
if ( pTempItemDef == pItemDef )
|
|
|
|
|
{
|
|
|
|
|
bOwned = true;
|
|
|
|
|
eFlag |= kDescLineFlag_CollectionCurrentItem;
|
|
|
|
|
if ( pEconItem->GetQuality() == AE_UNUSUAL )
|
|
|
|
|
{
|
|
|
|
|
bUnusual = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#ifdef CLIENT_DLL
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
CPlayerInventory *pLocalInv = TFInventoryManager()->GetLocalInventory();
|
|
|
|
|
if ( pLocalInv )
|
|
|
|
|
{
|
|
|
|
|
const CEconItemCollectionDefinition *pRefCollection = GetItemSchema()->GetCollectionByName( pTempItemDef->GetCollectionReference() );
|
|
|
|
|
// if item has a collection reference, we are looking for all those items and this item
|
|
|
|
|
if ( pRefCollection )
|
|
|
|
|
{
|
|
|
|
|
bOwned = true;
|
|
|
|
|
FOR_EACH_VEC( pRefCollection->m_iItemDefs, iRefCollectionItem )
|
|
|
|
|
{
|
|
|
|
|
const CEconItemView *pRefItem = pLocalInv->FindFirstItembyItemDef( pRefCollection->m_iItemDefs[ iRefCollectionItem ] );
|
|
|
|
|
if ( !pRefItem )
|
|
|
|
|
{
|
|
|
|
|
bOwned = false;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// Normal Backpack scan
|
|
|
|
|
for ( int i = 0; i < pLocalInv->GetItemCount(); ++i )
|
|
|
|
|
{
|
|
|
|
|
CEconItemView *pItem = pLocalInv->GetItem( i );
|
|
|
|
|
if ( pItem->GetItemDefinition() == pTempItemDef )
|
|
|
|
|
{
|
|
|
|
|
bOwned = true;
|
|
|
|
|
// Check Quality
|
|
|
|
|
if ( pItem->GetQuality() == AE_UNUSUAL )
|
|
|
|
|
{
|
|
|
|
|
bUnusual = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
const locchar_t * pCheckmark = bOwned ? pLocalizationProvider->Find( "TF_Checkmark" ) : pLocalizationProvider->Find( "TF_LackOfCheckmark" );
|
|
|
|
|
if ( bOwned && bUnusual )
|
|
|
|
|
{
|
|
|
|
|
pCheckmark = pLocalizationProvider->Find( "TF_Checkmark_Unusual" );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
attrib_colors_t colorRarity = ATTRIB_COL_RARITY_DEFAULT;
|
|
|
|
|
const CEconItemRarityDefinition* pItemRarity = GetItemSchema()->GetRarityDefinition( pTempItemDef->GetRarity() );
|
|
|
|
|
if ( pItemRarity )
|
|
|
|
|
{
|
|
|
|
|
colorRarity = pItemRarity->GetAttribColor();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
AddDescLine(
|
|
|
|
|
CConstructLocalizedString( LOCCHAR("%s1%s2"), pCheckmark,
|
|
|
|
|
CEconItemLocalizedFullNameGenerator(
|
|
|
|
|
pLocalizationProvider,
|
|
|
|
|
pTempItemDef,
|
|
|
|
|
m_pHashContext == NULL
|
|
|
|
|
).GetFullName() ),
|
|
|
|
|
colorRarity,
|
|
|
|
|
eFlag,
|
|
|
|
|
NULL,
|
|
|
|
|
pTempItemDef->GetDefinitionIndex()
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// --------------------------------------------------------------------------
|
|
|
|
|
// Purpose:
|
|
|
|
|
// --------------------------------------------------------------------------
|
|
|
|
|
void CEconItemDescription::Generate_ExpirationDesc( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem )
|
|
|
|
|
{
|
|
|
|
|
Assert( pLocalizationProvider );
|
|
|
|
|
Assert( pEconItem );
|
|
|
|
|
|
|
|
|
|
const CEconItemDefinition *pItemDef = pEconItem->GetItemDefinition();
|
|
|
|
|
if ( !pItemDef )
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
// Look for schema-specified static expiration date.
|
|
|
|
|
RTime32 timeSchemaExpiration = pItemDef->GetExpirationDate();
|
|
|
|
|
|
|
|
|
|
// Look also for a dynamic attribute -- this could come from item tryouts.
|
|
|
|
|
static CSchemaAttributeDefHandle pAttrDef_ExpirationDate( "expiration date" );
|
|
|
|
|
|
|
|
|
|
RTime32 timeAttrExpiration = 0;
|
|
|
|
|
pEconItem->FindAttribute( pAttrDef_ExpirationDate, &timeAttrExpiration ); // if we don't have the attribute we'll use our starting value of 0
|
|
|
|
|
|
|
|
|
|
// Which will have us expire first?
|
|
|
|
|
RTime32 timeExpiration = MAX( timeSchemaExpiration, timeAttrExpiration );
|
|
|
|
|
|
|
|
|
|
// If we still don't have an expiration date we don't display anything.
|
|
|
|
|
if ( !timeExpiration )
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
AddEmptyDescLine();
|
|
|
|
|
|
|
|
|
|
#ifdef TF_CLIENT_DLL
|
|
|
|
|
// is this a loaner item?
|
|
|
|
|
if ( GetAssociatedQuestItemID( pEconItem ) != INVALID_ITEM_ID )
|
|
|
|
|
{
|
|
|
|
|
AddDescLine( pLocalizationProvider->Find( "#Attrib_LoanerItemExpirationDate" ),
|
|
|
|
|
ATTRIB_COL_NEGATIVE,
|
|
|
|
|
kDescLineFlag_Misc );
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
#endif // TF_CLIENT_DLL
|
|
|
|
|
{
|
|
|
|
|
CLocalizedRTime32 time = { timeExpiration, false, pLocalizationProvider TF_ANTI_IDLEBOT_VERIFICATION_ONLY_COMMA TF_ANTI_IDLEBOT_VERIFICATION_ONLY_ARG( m_pHashContext ) };
|
|
|
|
|
AddDescLine( CConstructLocalizedString( pLocalizationProvider->Find( "#Attrib_ExpirationDate" ),
|
|
|
|
|
time ),
|
|
|
|
|
ATTRIB_COL_NEGATIVE,
|
|
|
|
|
kDescLineFlag_Misc );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void CEconItemDescription::Generate_DropPeriodDesc( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem )
|
|
|
|
|
{
|
|
|
|
|
Assert( pLocalizationProvider );
|
|
|
|
|
Assert( pEconItem );
|
|
|
|
|
|
|
|
|
|
static CSchemaAttributeDefHandle pAttr_EndDropDate( "end drop date" );
|
|
|
|
|
|
|
|
|
|
// See if we have the drop date period end attribute
|
|
|
|
|
CAttribute_String value;
|
|
|
|
|
if ( !FindAttribute( pEconItem, pAttr_EndDropDate, &value ) )
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
AddEmptyDescLine();
|
|
|
|
|
|
|
|
|
|
// Convert the string value to an RTime32
|
|
|
|
|
RTime32 endDate = CRTime::RTime32FromString( value.value().c_str() );
|
|
|
|
|
// Is the time before or after now? Use different strings for each
|
|
|
|
|
const char* pszDropString = endDate > CRTime::RTime32TimeCur()
|
|
|
|
|
? "#Attrib_DropPeriodComing"
|
|
|
|
|
: "#Attrib_DropPeriodPast";
|
|
|
|
|
|
|
|
|
|
CLocalizedRTime32 time = { endDate, false, pLocalizationProvider TF_ANTI_IDLEBOT_VERIFICATION_ONLY_COMMA TF_ANTI_IDLEBOT_VERIFICATION_ONLY_ARG( m_pHashContext ) };
|
|
|
|
|
AddDescLine( CConstructLocalizedString( pLocalizationProvider->Find( pszDropString ),
|
|
|
|
|
time ),
|
|
|
|
|
ATTRIB_COL_LEVEL,
|
|
|
|
|
kDescLineFlag_Misc );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// --------------------------------------------------------------------------
|
|
|
|
|
// Purpose:
|
|
|
|
|
// --------------------------------------------------------------------------
|
|
|
|
|
#ifdef TF_CLIENT_DLL
|
|
|
|
|
extern ConVar cl_showbackpackrarities;
|
|
|
|
|
extern ConVar cl_show_market_data_on_items;
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
void CEconItemDescription::Generate_MarketInformation( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem )
|
|
|
|
|
{
|
|
|
|
|
// Deprecated;
|
|
|
|
|
// We now have right click go to market
|
|
|
|
|
return;
|
|
|
|
|
//
|
|
|
|
|
// Assert( pLocalizationProvider );
|
|
|
|
|
// Assert( pEconItem );
|
|
|
|
|
//
|
|
|
|
|
// // Early-out to avoid doing more expensive name lookups for items that can't possibly have
|
|
|
|
|
// // entries.
|
|
|
|
|
// if ( !pEconItem->IsMarketable() )
|
|
|
|
|
// return;
|
|
|
|
|
//
|
|
|
|
|
// // For now, Market information is only shown on clients, and even then only sometimes.
|
|
|
|
|
//#ifdef CLIENT_DLL
|
|
|
|
|
//#if TF_ANTI_IDLEBOT_VERIFICATION
|
|
|
|
|
// // Don't generate this client-only information when we're trying to match GC output.
|
|
|
|
|
// if ( m_pHashContext )
|
|
|
|
|
// return;
|
|
|
|
|
//#endif // TF_ANTI_IDLEBOT_VERIFICATION
|
|
|
|
|
//
|
|
|
|
|
//#ifdef TF_CLIENT_DLL
|
|
|
|
|
// if ( cl_show_market_data_on_items.GetInt() == 0 )
|
|
|
|
|
// return;
|
|
|
|
|
//
|
|
|
|
|
// if ( cl_show_market_data_on_items.GetInt() == 1 && cl_showbackpackrarities.GetInt() != 2 )
|
|
|
|
|
// return;
|
|
|
|
|
//#endif // TF_CLIENT_DLL
|
|
|
|
|
//
|
|
|
|
|
// steam_market_gc_identifier_t ident;
|
|
|
|
|
// ident.m_unDefIndex = pEconItem->GetItemDefIndex();
|
|
|
|
|
// ident.m_unQuality = pEconItem->GetQuality();
|
|
|
|
|
//
|
|
|
|
|
// const client_market_data_t *pClientMarketData = GetClientMarketData( ident );
|
|
|
|
|
// if ( !pClientMarketData )
|
|
|
|
|
// return;
|
|
|
|
|
//
|
|
|
|
|
// locchar_t loc_Price[ kLocalizedPriceSizeInChararacters ];
|
|
|
|
|
// MakeMoneyString( loc_Price, ARRAYSIZE( loc_Price ), pClientMarketData->m_unLowestPrice, EconUI()->GetStorePanel()->GetCurrency() );
|
|
|
|
|
//
|
|
|
|
|
// AddEmptyDescLine();
|
|
|
|
|
// AddDescLine( CConstructLocalizedString( pLocalizationProvider->Find( "#Econ_MarketTooltipFormat" ),
|
|
|
|
|
// pClientMarketData->m_unQuantityAvailable,
|
|
|
|
|
// loc_Price ),
|
|
|
|
|
// ATTRIB_COL_POSITIVE,
|
|
|
|
|
// kDescLineFlag_Misc );
|
|
|
|
|
//#endif // CLIENT_DLL
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// --------------------------------------------------------------------------
|
|
|
|
|
// Purpose:
|
|
|
|
|
// --------------------------------------------------------------------------
|
|
|
|
|
struct localized_localplayer_line_t
|
|
|
|
|
{
|
|
|
|
|
localized_localplayer_line_t( const char *pLocalizationKey, attrib_colors_t eAttribColor, const char *pLocalizationSubKey = NULL )
|
|
|
|
|
: m_pLocalizationKey( pLocalizationKey )
|
|
|
|
|
, m_pLocalizationSubKey( pLocalizationSubKey )
|
|
|
|
|
, m_eAttribColor( eAttribColor )
|
|
|
|
|
{
|
|
|
|
|
//
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const char *m_pLocalizationKey;
|
|
|
|
|
const char *m_pLocalizationSubKey;
|
|
|
|
|
attrib_colors_t m_eAttribColor;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
void CEconItemDescription::Generate_FlagsAttributes( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem )
|
|
|
|
|
{
|
|
|
|
|
Assert( pLocalizationProvider );
|
|
|
|
|
Assert( pEconItem );
|
|
|
|
|
|
|
|
|
|
const CEconItemDefinition *pItemDef = pEconItem->GetItemDefinition();
|
|
|
|
|
if ( pItemDef && pItemDef->GetEconTool() && pItemDef->GetEconTool()->ShouldDisplayQuantity( pEconItem ) )
|
|
|
|
|
{
|
|
|
|
|
AddEmptyDescLine();
|
|
|
|
|
AddDescLine( CConstructLocalizedString( pLocalizationProvider->Find( "#Attrib_LimitedUse" ), (uint32)pEconItem->GetQuantity() ),
|
|
|
|
|
ATTRIB_COL_LIMITED_USE,
|
|
|
|
|
kDescLineFlag_LimitedUse );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CUtlVector<localized_localplayer_line_t> vecLines;
|
|
|
|
|
|
|
|
|
|
// Is this item in use? (ie., being used as part of a cross-game trade)
|
|
|
|
|
if ( pEconItem->GetInUse() )
|
|
|
|
|
{
|
|
|
|
|
vecLines.AddToTail( localized_localplayer_line_t( "#Attrib_InUse", ATTRIB_COL_NEUTRAL ) );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static CSchemaAttributeDefHandle pAttrDef_TradableAfter( "tradable after date" );
|
|
|
|
|
static CSchemaAttributeDefHandle pAttrDef_ToolEscrowUntil( "tool escrow until date" );
|
|
|
|
|
static CSchemaAttributeDefHandle pAttrDef_AlwaysTradableAndUsableInCrafting( "always tradable" );
|
|
|
|
|
|
|
|
|
|
uint32 unTradeTime = 0,
|
|
|
|
|
unEscrowTime = 0;
|
|
|
|
|
const bool bHasTradableAfterDate = pEconItem->FindAttribute( pAttrDef_TradableAfter, &unTradeTime );
|
|
|
|
|
const bool bHasToolEscrowUntilDate = pEconItem->FindAttribute( pAttrDef_ToolEscrowUntil, &unEscrowTime );
|
|
|
|
|
const bool bHasExpiringTimer = bHasTradableAfterDate || bHasToolEscrowUntilDate;
|
|
|
|
|
|
|
|
|
|
#ifdef CLIENT_DLL
|
|
|
|
|
const bool bIsStoreItem = IsStorePreviewItem( pEconItem );
|
|
|
|
|
const bool bIsPreviewItem = pEconItem->GetFlags() & kEconItemFlagClient_Preview;
|
|
|
|
|
|
|
|
|
|
if ( bIsStoreItem || bIsPreviewItem )
|
|
|
|
|
{
|
|
|
|
|
#if TF_ANTI_IDLEBOT_VERIFICATION
|
|
|
|
|
if ( !m_pHashContext )
|
|
|
|
|
#endif // TF_ANTI_IDLEBOT_VERIFICATION
|
|
|
|
|
{
|
|
|
|
|
// Does this item come with other packages on Steam?
|
|
|
|
|
const econ_store_entry_t *pStoreEntry = GetEconPriceSheet() ? GetEconPriceSheet()->GetEntry( pItemDef->GetDefinitionIndex() ) : NULL;
|
|
|
|
|
if ( pStoreEntry && pStoreEntry->GetGiftSteamPackageID() != 0 )
|
|
|
|
|
{
|
|
|
|
|
const char *pszSteamPackageLocalizationToken = GetItemSchema()->GetSteamPackageLocalizationToken( pStoreEntry->GetGiftSteamPackageID() );
|
|
|
|
|
if ( pszSteamPackageLocalizationToken )
|
|
|
|
|
{
|
|
|
|
|
vecLines.AddToTail( localized_localplayer_line_t( "#Attrib_Store_IncludesSteamGiftPackage", ATTRIB_COL_POSITIVE, pszSteamPackageLocalizationToken ) );
|
|
|
|
|
vecLines.AddToTail( localized_localplayer_line_t( NULL, ATTRIB_COL_POSITIVE ) );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// While the above apply to store *and* preview items, the below only apply to store items.
|
|
|
|
|
if ( bIsStoreItem )
|
|
|
|
|
{
|
|
|
|
|
// Don't display this line for map stamps because they can't be traded.
|
|
|
|
|
if ( pItemDef && pItemDef->GetItemClass() && !FStrEq( pItemDef->GetItemClass(), "map_token" ) )
|
|
|
|
|
{
|
|
|
|
|
static CSchemaAttributeDefHandle pAttrib_CannotTrade( "cannot trade" );
|
|
|
|
|
Assert( pAttrib_CannotTrade );
|
|
|
|
|
|
|
|
|
|
// Some items cannot ever be traded, so don't indicate to the users that they'll be tradeable after a few days.
|
|
|
|
|
if ( !FindAttribute( pItemDef, pAttrib_CannotTrade ) )
|
|
|
|
|
{
|
|
|
|
|
vecLines.AddToTail( localized_localplayer_line_t( "#Attrib_Store_TradableAfterDate", ATTRIB_COL_NEGATIVE ) );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ( pItemDef->GetEconTool() && pItemDef->GetEconTool()->RequiresToolEscrowPeriod() )
|
|
|
|
|
{
|
|
|
|
|
vecLines.AddToTail( localized_localplayer_line_t( "#Attrib_Store_ToolEscrowUntilDate", ATTRIB_COL_NEGATIVE ) );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ( !pItemDef || (pItemDef->GetCapabilities() & ITEM_CAP_CAN_BE_CRAFTED_IF_PURCHASED) == 0 )
|
|
|
|
|
{
|
|
|
|
|
if ( pItemDef->IsBundle() )
|
|
|
|
|
{
|
|
|
|
|
vecLines.AddToTail( localized_localplayer_line_t( "#Attrib_CannotCraftWeapons", ATTRIB_COL_NEGATIVE ) );
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
vecLines.AddToTail( localized_localplayer_line_t( "#Attrib_CannotCraft", ATTRIB_COL_NEGATIVE ) );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
#endif // CLIENT_DLL
|
|
|
|
|
if ( bHasExpiringTimer )
|
|
|
|
|
{
|
|
|
|
|
if ( unTradeTime > CRTime::RTime32TimeCur() )
|
|
|
|
|
{
|
|
|
|
|
AddAttributeDescription( pLocalizationProvider, pAttrDef_TradableAfter, unTradeTime );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ( unEscrowTime > CRTime::RTime32TimeCur() )
|
|
|
|
|
{
|
|
|
|
|
AddAttributeDescription( pLocalizationProvider, pAttrDef_ToolEscrowUntil, unEscrowTime );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ( !pEconItem->IsUsableInCrafting() )
|
|
|
|
|
{
|
|
|
|
|
vecLines.AddToTail( localized_localplayer_line_t( "#Attrib_CannotCraft", ATTRIB_COL_NEUTRAL ) );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if ( pEconItem->FindAttribute( pAttrDef_AlwaysTradableAndUsableInCrafting ) && pEconItem->IsTradable() )
|
|
|
|
|
{
|
|
|
|
|
// do nothing if we are always tradable or usable in crafting
|
|
|
|
|
//
|
|
|
|
|
// some items are marked as "always_tradable" in their itemDef but the specific item may have the
|
|
|
|
|
// "non_economy" flag, so we need to also check that this specific item is tradable before doing nothing
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
const int32 iQuality = pEconItem->GetQuality();
|
|
|
|
|
const eEconItemOrigin eOrigin = pEconItem->GetOrigin();
|
|
|
|
|
|
|
|
|
|
if ( iQuality >= AE_COMMUNITY && iQuality <= AE_SELFMADE )
|
|
|
|
|
{
|
|
|
|
|
vecLines.AddToTail( localized_localplayer_line_t( "#Attrib_SpecialItem", ATTRIB_COL_NEUTRAL ) );
|
|
|
|
|
}
|
|
|
|
|
else if ( eOrigin == kEconItemOrigin_Achievement )
|
|
|
|
|
{
|
|
|
|
|
vecLines.AddToTail( localized_localplayer_line_t( "#Attrib_AchievementItem", ATTRIB_COL_NEUTRAL ) );
|
|
|
|
|
}
|
|
|
|
|
else if ( eOrigin == kEconItemOrigin_CollectionReward )
|
|
|
|
|
{
|
|
|
|
|
vecLines.AddToTail( localized_localplayer_line_t( "#Attrib_CollectionReward", ATTRIB_COL_NEUTRAL ) );
|
|
|
|
|
}
|
|
|
|
|
else if ( eOrigin == kEconItemOrigin_PreviewItem )
|
|
|
|
|
{
|
|
|
|
|
vecLines.AddToTail( localized_localplayer_line_t( "#Attrib_PreviewItem", ATTRIB_COL_NEUTRAL ) );
|
|
|
|
|
}
|
|
|
|
|
else if ( eOrigin == kEconItemOrigin_QuestLoanerItem )
|
|
|
|
|
{
|
|
|
|
|
vecLines.AddToTail( localized_localplayer_line_t( "#Attrib_LoanerItem", ATTRIB_COL_NEUTRAL ) );
|
|
|
|
|
}
|
|
|
|
|
else if ( eOrigin == kEconItemOrigin_Invalid )
|
|
|
|
|
{
|
|
|
|
|
// do nothing, but skip the below "cannot trade/cannot craft" block below"
|
|
|
|
|
}
|
|
|
|
|
else if ( (pEconItem->GetFlags() & kEconItemFlag_NonEconomy) != 0 )
|
|
|
|
|
{
|
|
|
|
|
vecLines.AddToTail( localized_localplayer_line_t( "#Attrib_NonEconomyItem", ATTRIB_COL_NEUTRAL ) );
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
const bool bIsTradable = pEconItem->IsTradable(),
|
|
|
|
|
bIsCraftable = pEconItem->IsUsableInCrafting();
|
|
|
|
|
|
|
|
|
|
if ( !bIsTradable && !bIsCraftable )
|
|
|
|
|
{
|
|
|
|
|
vecLines.AddToTail( localized_localplayer_line_t( "#Attrib_CannotTradeOrCraft", ATTRIB_COL_NEUTRAL ) );
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if ( !bIsTradable )
|
|
|
|
|
{
|
|
|
|
|
vecLines.AddToTail( localized_localplayer_line_t( "#Attrib_CannotTrade", ATTRIB_COL_NEUTRAL ) );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ( !bIsCraftable )
|
|
|
|
|
{
|
|
|
|
|
vecLines.AddToTail( localized_localplayer_line_t( "#Attrib_CannotCraft", ATTRIB_COL_NEUTRAL ) );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ( vecLines.Count() > 0 )
|
|
|
|
|
{
|
|
|
|
|
const locchar_t *loc_AttribFormat_AdditionalNode = pLocalizationProvider->Find( "#AttribFormat_AdditionalNote" );
|
|
|
|
|
if ( loc_AttribFormat_AdditionalNode )
|
|
|
|
|
{
|
|
|
|
|
AddEmptyDescLine();
|
|
|
|
|
|
|
|
|
|
FOR_EACH_VEC( vecLines, i )
|
|
|
|
|
{
|
|
|
|
|
const char *pszLocalizationKey = vecLines[i].m_pLocalizationKey;
|
|
|
|
|
const char *pszLocalizationSubKey = vecLines[i].m_pLocalizationSubKey;
|
|
|
|
|
|
|
|
|
|
if ( pszLocalizationKey )
|
|
|
|
|
{
|
|
|
|
|
AddDescLine( pszLocalizationSubKey ?
|
|
|
|
|
CConstructLocalizedString( pLocalizationProvider->Find( pszLocalizationKey ), pLocalizationProvider->Find( pszLocalizationSubKey ) ): // has subtoken, doesn't use additional note format
|
|
|
|
|
CConstructLocalizedString( loc_AttribFormat_AdditionalNode, pLocalizationProvider->Find( pszLocalizationKey ) ), // no subtoken, uses base additional note format
|
|
|
|
|
vecLines[i].m_eAttribColor,
|
|
|
|
|
kDescLineFlag_Misc );
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
AddEmptyDescLine();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// --------------------------------------------------------------------------
|
|
|
|
|
// Purpose:
|
|
|
|
|
// --------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
bool CEconItemDescription::CVisibleAttributeDisplayer::OnIterateAttributeValue( const CEconItemAttributeDefinition *pAttrDef, attrib_value_t value )
|
|
|
|
|
{
|
|
|
|
|
if ( !pAttrDef->IsHidden() )
|
|
|
|
|
{
|
|
|
|
|
attrib_iterator_value_t attrVal = { pAttrDef, value };
|
|
|
|
|
m_vecAttributes.AddToTail( attrVal );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CEconItemDescription::CVisibleAttributeDisplayer::SortAttributes()
|
|
|
|
|
{
|
|
|
|
|
// We need to make sure we process attributes in the same order when iterating on the GC and the client
|
|
|
|
|
// when looking for agreement. We take advantage of this to also sort our attributes into a coherent
|
|
|
|
|
// order for display -- first come neutral attributes, then positive, then negative. In the event of a
|
|
|
|
|
// tie, we sort by attribute index, which is arbitrary but consistent across the client/GC.
|
|
|
|
|
struct AttributeValueSorter
|
|
|
|
|
{
|
|
|
|
|
static int sSort( const attrib_iterator_value_t *pA, const attrib_iterator_value_t *pB )
|
|
|
|
|
{
|
|
|
|
|
const int iEffectTypeDelta = pA->m_pAttrDef->GetEffectType() - pB->m_pAttrDef->GetEffectType();
|
|
|
|
|
if ( iEffectTypeDelta != 0 )
|
|
|
|
|
return iEffectTypeDelta;
|
|
|
|
|
|
|
|
|
|
return pA->m_pAttrDef->GetDefinitionIndex() - pB->m_pAttrDef->GetDefinitionIndex();
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
m_vecAttributes.Sort( &AttributeValueSorter::sSort );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CEconItemDescription::CVisibleAttributeDisplayer::Finalize( const IEconItemInterface *pEconItem, CEconItemDescription *pEconItemDescription, const CLocalizationProvider *pLocalizationProvider )
|
|
|
|
|
{
|
|
|
|
|
// HACK so we dont show series number on select crates since they are self describing (Event Crates, Collection Crates)
|
|
|
|
|
static CSchemaAttributeDefHandle pAttrDef_SupplyCrateSeries( "set supply crate series" );
|
|
|
|
|
static CSchemaAttributeDefHandle pAttrDef_HideSeries( "hide crate series number" );
|
|
|
|
|
|
|
|
|
|
FOR_EACH_VEC( m_vecAttributes, i )
|
|
|
|
|
{
|
|
|
|
|
if ( pEconItem && m_vecAttributes[i].m_pAttrDef == pAttrDef_SupplyCrateSeries && pEconItem->FindAttribute( pAttrDef_HideSeries ) )
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
pEconItemDescription->AddAttributeDescription( pLocalizationProvider, m_vecAttributes[i].m_pAttrDef, m_vecAttributes[i].m_value );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// --------------------------------------------------------------------------
|
|
|
|
|
// Purpose:
|
|
|
|
|
// --------------------------------------------------------------------------
|
|
|
|
|
void CEconItemDescription::Generate_VisibleAttributes( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem )
|
|
|
|
|
{
|
|
|
|
|
Assert( pLocalizationProvider );
|
|
|
|
|
Assert( pEconItem );
|
|
|
|
|
|
|
|
|
|
CVisibleAttributeDisplayer AttributeDisplayer;
|
|
|
|
|
pEconItem->IterateAttributes( &AttributeDisplayer );
|
|
|
|
|
AttributeDisplayer.SortAttributes();
|
|
|
|
|
AttributeDisplayer.Finalize( pEconItem, this, pLocalizationProvider );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// --------------------------------------------------------------------------
|
|
|
|
|
void CEconItemDescription::Generate_DirectX8Warning( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem )
|
|
|
|
|
{
|
|
|
|
|
#ifdef CLIENT_DLL
|
|
|
|
|
static ConVarRef mat_dxlevel( "mat_dxlevel" );
|
|
|
|
|
const CEconItemDefinition *pEconItemDefinition = pEconItem->GetItemDefinition();
|
|
|
|
|
// If less than 90, we<EFBFBD>re in DX8 mode.
|
|
|
|
|
// Display warning if you are looking at a painthit item or case
|
|
|
|
|
if ( mat_dxlevel.GetInt() < 90 && pEconItemDefinition && ( pEconItemDefinition->GetItemCollectionDefinition() || pEconItemDefinition->GetCollectionReference() ) )
|
|
|
|
|
{
|
|
|
|
|
AddEmptyDescLine();
|
|
|
|
|
AddDescLine( pLocalizationProvider->Find( "#Attrib_DirectX8Warning" ),
|
|
|
|
|
ATTRIB_COL_NEGATIVE,
|
|
|
|
|
kDescLineFlag_Misc );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bool CEconItemDescription::CRecipeNameAttributeDisplayer::OnIterateAttributeValue( const CEconItemAttributeDefinition *pAttrDef, attrib_value_t value )
|
|
|
|
|
{
|
|
|
|
|
if ( pAttrDef->CanAffectRecipeComponentName() )
|
|
|
|
|
{
|
|
|
|
|
return CVisibleAttributeDisplayer::OnIterateAttributeValue( pAttrDef, value );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// --------------------------------------------------------------------------
|
|
|
|
|
// Purpose:
|
|
|
|
|
// --------------------------------------------------------------------------
|
|
|
|
|
static attrib_colors_t GetAttributeDefaultColor( const CEconItemAttributeDefinition *pAttribDef )
|
|
|
|
|
{
|
|
|
|
|
// positive attribute?
|
|
|
|
|
switch ( pAttribDef->GetEffectType() )
|
|
|
|
|
{
|
|
|
|
|
case ATTRIB_EFFECT_NEUTRAL: return ATTRIB_COL_NEUTRAL;
|
|
|
|
|
case ATTRIB_EFFECT_POSITIVE: return ATTRIB_COL_POSITIVE;
|
|
|
|
|
case ATTRIB_EFFECT_NEGATIVE: return ATTRIB_COL_NEGATIVE;
|
|
|
|
|
case ATTRIB_EFFECT_STRANGE: return ATTRIB_COL_STRANGE;
|
|
|
|
|
case ATTRIB_EFFECT_UNUSUAL: return ATTRIB_COL_UNUSUAL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// hell if we know
|
|
|
|
|
return ATTRIB_COL_NEUTRAL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// --------------------------------------------------------------------------
|
|
|
|
|
// Purpose:
|
|
|
|
|
// --------------------------------------------------------------------------
|
|
|
|
|
void CEconAttributeDescription::InternalConstruct
|
|
|
|
|
(
|
|
|
|
|
const CLocalizationProvider *pLocalizationProvider,
|
|
|
|
|
const CEconItemAttributeDefinition *pAttribDef,
|
|
|
|
|
attrib_value_t value,
|
|
|
|
|
TF_ANTI_IDLEBOT_VERIFICATION_ONLY_ARG( MD5Context_t *pHashContext ) TF_ANTI_IDLEBOT_VERIFICATION_ONLY_COMMA
|
|
|
|
|
IAccountPersonaLocalizer *pOptionalAccountPersonaLocalizer
|
|
|
|
|
)
|
|
|
|
|
{
|
|
|
|
|
Assert( pAttribDef != NULL );
|
|
|
|
|
|
|
|
|
|
const float& value_as_float = (float&)value;
|
|
|
|
|
const uint32& value_as_uint32 = (uint32&)value;
|
|
|
|
|
|
|
|
|
|
// Calculate our color first -- if we don't know what to do, we'll wind up as neutral.
|
|
|
|
|
m_eDefaultColor = GetAttributeDefaultColor( pAttribDef );
|
|
|
|
|
|
|
|
|
|
// Early out abort if we don't have a localization string for this attribute.
|
|
|
|
|
locchar_t *loc_String = pAttribDef->GetDescriptionString() && pLocalizationProvider
|
|
|
|
|
? pLocalizationProvider->Find( pAttribDef->GetDescriptionString() )
|
|
|
|
|
: NULL;
|
|
|
|
|
|
|
|
|
|
if ( !loc_String )
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
char szAttrShortDescToken[MAX_PATH];
|
|
|
|
|
V_sprintf_safe( szAttrShortDescToken, "%s%s", pAttribDef->GetDescriptionString(), "_shortdesc" );
|
|
|
|
|
|
|
|
|
|
locchar_t *loc_ShortString = pLocalizationProvider
|
|
|
|
|
? pLocalizationProvider->Find( szAttrShortDescToken )
|
|
|
|
|
: NULL;
|
|
|
|
|
|
|
|
|
|
// How do we format an attribute value of this type?
|
|
|
|
|
switch ( pAttribDef->GetDescriptionFormat() )
|
|
|
|
|
{
|
|
|
|
|
case ATTDESCFORM_VALUE_IS_ADDITIVE_PERCENTAGE:
|
|
|
|
|
m_loc_sValue = CLocalizedStringArg<float>( value_as_float * 100.0f ).GetLocArg();
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case ATTDESCFORM_VALUE_IS_ACCOUNT_ID:
|
|
|
|
|
#ifdef CLIENT_DLL
|
|
|
|
|
// If this assert fires, it means that the client fed in an attribute that should be localized
|
|
|
|
|
// as a Steam persona name but didn't feed it any way to get that information. The GC won't
|
|
|
|
|
// assert, but also won't generate anything for the attribute text.
|
|
|
|
|
//
|
|
|
|
|
// It's still totally fine to pass in NULL for the persona localizer as long as you don't
|
|
|
|
|
// expect to have any attributes that have account IDs.
|
|
|
|
|
Assert( pOptionalAccountPersonaLocalizer );
|
|
|
|
|
#endif
|
|
|
|
|
if ( pOptionalAccountPersonaLocalizer )
|
|
|
|
|
{
|
|
|
|
|
m_loc_sValue = pOptionalAccountPersonaLocalizer->FindAccountPersonaName( value_as_uint32 );
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case ATTDESCFORM_VALUE_IS_ADDITIVE:
|
|
|
|
|
m_loc_sValue = pAttribDef->IsStoredAsFloat()
|
|
|
|
|
? CLocalizedStringArg<float>( value_as_float ).GetLocArg()
|
|
|
|
|
: CLocalizedStringArg<uint32>( value_as_uint32 ).GetLocArg();
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case ATTDESCFORM_VALUE_IS_INVERTED_PERCENTAGE:
|
|
|
|
|
if ( value_as_float < 1.0 )
|
|
|
|
|
{
|
|
|
|
|
m_loc_sValue = CLocalizedStringArg<float>( (1.0 - value_as_float) * 100.0f ).GetLocArg();
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// We intentionally fall through when value_as_float >= 1.0f to treat it the same as "value as
|
|
|
|
|
// percentage".
|
|
|
|
|
case ATTDESCFORM_VALUE_IS_PERCENTAGE:
|
|
|
|
|
m_loc_sValue = CLocalizedStringArg<float>( (value_as_float * 100.0f) - 100.0f ).GetLocArg();
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case ATTDESCFORM_VALUE_IS_DATE:
|
|
|
|
|
{
|
|
|
|
|
bool bUseGMT = false;
|
|
|
|
|
|
|
|
|
|
#ifdef PROJECT_TF
|
|
|
|
|
static CSchemaAttributeDefHandle pAttribDef_SetEmployeeNumber( "custom employee number" );
|
|
|
|
|
|
|
|
|
|
// only use GMT for custom employee number -- not doing this generated a bunch of support
|
|
|
|
|
// tickets because items were granted based on GC time but would display local time, causing
|
|
|
|
|
// people on the border to think they deserved a better badge, etc.
|
|
|
|
|
bUseGMT = (pAttribDef == pAttribDef_SetEmployeeNumber);
|
|
|
|
|
#endif // PROJECT_TF
|
|
|
|
|
|
|
|
|
|
CLocalizedRTime32 time = { value_as_uint32, bUseGMT, pLocalizationProvider TF_ANTI_IDLEBOT_VERIFICATION_ONLY_COMMA TF_ANTI_IDLEBOT_VERIFICATION_ONLY_ARG( pHashContext ) };
|
|
|
|
|
m_loc_sValue = CLocalizedStringArg<CLocalizedRTime32>( time ).GetLocArg();
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case ATTDESCFORM_VALUE_IS_PARTICLE_INDEX:
|
|
|
|
|
{
|
|
|
|
|
// This is a horrible, horrible line of code. It exists because old particle references are
|
|
|
|
|
// ints stored as floats as float bit patterns and new particle references are ints stored
|
|
|
|
|
// as ints all the way through.
|
|
|
|
|
CUtlConstString utf8_ParticleKeyName( CFmtStr( "#Attrib_Particle%i", pAttribDef->IsStoredAsInteger() ? value_as_uint32 : (int)value_as_float ).Access() ); // this value is stored as a float but interpreted as an int (1.0 -> 1)
|
|
|
|
|
if ( utf8_ParticleKeyName.IsEmpty() )
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
m_loc_sValue = pLocalizationProvider->Find( utf8_ParticleKeyName.Get() );
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case ATTDESCFORM_VALUE_IS_KILLSTREAKEFFECT_INDEX:
|
|
|
|
|
{
|
|
|
|
|
CUtlConstString utf8_KeyName( CFmtStr( "#Attrib_KillStreakEffect%i", (int)value_as_float ).Access() ); // this value is stored as a float but interpreted as an int (1.0 -> 1)
|
|
|
|
|
if ( utf8_KeyName.IsEmpty() )
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
m_loc_sValue = pLocalizationProvider->Find( utf8_KeyName.Get() );
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case ATTDESCFORM_VALUE_IS_KILLSTREAK_IDLEEFFECT_INDEX:
|
|
|
|
|
{
|
|
|
|
|
CUtlConstString utf8_KeyName( CFmtStr( "#Attrib_KillStreakIdleEffect%i", (int)value_as_float ).Access() ); // this value is stored as a float but interpreted as an int (1.0 -> 1)
|
|
|
|
|
if ( utf8_KeyName.IsEmpty() )
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
m_loc_sValue = pLocalizationProvider->Find( utf8_KeyName.Get() );
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
// Don't output any value for bitmasks, but let the attribute text display.
|
|
|
|
|
case ATTDESCFORM_VALUE_IS_OR:
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
#ifdef CLIENT_DLL
|
|
|
|
|
// Only assert on the client -- the GC will just silently fail rather than crash if we ever run into
|
|
|
|
|
// this case, but if we are adding a new display type this will help us catch a reason why it isn't
|
|
|
|
|
// showing up.
|
|
|
|
|
Assert( !"Unhandled attribute value display type in CEconAttributeDescription." );
|
|
|
|
|
|
|
|
|
|
// Anywhere besides the client, we intentionally fall through to return immediately.
|
|
|
|
|
#endif
|
|
|
|
|
case ATTDESCFORM_VALUE_IS_ITEM_DEF: // referencing definitions is handled per-attribute
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
case ATTDESCFORM_VALUE_IS_FROM_LOOKUP_TABLE:
|
|
|
|
|
{
|
|
|
|
|
const char *pszLocalizationToken = GetItemSchema()->FindStringTableEntry( pAttribDef->GetDefinitionName(), (int)value_as_float );
|
|
|
|
|
if ( !pszLocalizationToken )
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
const locchar_t *loc_Entry = pLocalizationProvider->Find( pszLocalizationToken );
|
|
|
|
|
if ( !loc_Entry )
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
m_loc_sValue = loc_Entry;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Some attributes have a short description for the upgrade
|
|
|
|
|
if ( loc_ShortString )
|
|
|
|
|
{
|
|
|
|
|
m_loc_sShortValue = CConstructLocalizedString( loc_ShortString, m_loc_sValue.Get() );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Combine the value string we just generated with the localized display for that value. (ie., the value
|
|
|
|
|
// might be "10" and the display would be "health is increased by 10%".)
|
|
|
|
|
m_loc_sValue = CConstructLocalizedString( loc_String, m_loc_sValue.Get() );
|
|
|
|
|
|
|
|
|
|
// Is this an attribute that needs a custom wrapper around the default attribute text? (ie.,
|
|
|
|
|
// if our string was "Damage +10%" we want that to be "(only on Hightower: Damage +10%)")
|
|
|
|
|
if ( pAttribDef->GetUserGenerationType() )
|
|
|
|
|
{
|
|
|
|
|
const locchar_t *locUGTLocalizationKey = pLocalizationProvider->Find( CFmtStr( "#Econ_Attrib_UserGeneratedWrapper_%i", pAttribDef->GetUserGenerationType() ).Get() );
|
|
|
|
|
|
|
|
|
|
if ( locUGTLocalizationKey )
|
|
|
|
|
{
|
|
|
|
|
m_loc_sValue = CConstructLocalizedString( locUGTLocalizationKey, m_loc_sValue.Get() );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If there's no short description, just copy the normal one
|
|
|
|
|
if ( !loc_ShortString )
|
|
|
|
|
{
|
|
|
|
|
m_loc_sShortValue = m_loc_sValue;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// --------------------------------------------------------------------------
|
|
|
|
|
// Purpose:
|
|
|
|
|
// --------------------------------------------------------------------------
|
|
|
|
|
void CEconItemDescription::AddAttributeDescription( const CLocalizationProvider *pLocalizationProvider, const CEconItemAttributeDefinition *pAttribDef, attrib_value_t value, attrib_colors_t eOverrideDisplayColor /* = NUM_ATTRIB_COLORS */ )
|
|
|
|
|
{
|
|
|
|
|
Assert( pLocalizationProvider );
|
|
|
|
|
Assert( pAttribDef );
|
|
|
|
|
|
|
|
|
|
CEconAttributeDescription AttrDesc( pLocalizationProvider,
|
|
|
|
|
pAttribDef,
|
|
|
|
|
value,
|
|
|
|
|
TF_ANTI_IDLEBOT_VERIFICATION_ONLY_ARG( m_pHashContext ) TF_ANTI_IDLEBOT_VERIFICATION_ONLY_COMMA
|
|
|
|
|
this );
|
|
|
|
|
|
|
|
|
|
if ( AttrDesc.GetDescription().IsEmpty() )
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
// Is this an attribute that needs a custom wrapper around the default attribute text? (ie.,
|
|
|
|
|
// if our string was "Damage +10%" we want that to be "(only on Hightower: Damage +10%)")
|
|
|
|
|
attrib_colors_t eDefaultAttribColor = GetAttributeDefaultColor( pAttribDef );
|
|
|
|
|
|
|
|
|
|
#ifdef TF_CLIENT_DLL
|
|
|
|
|
enum
|
|
|
|
|
{
|
|
|
|
|
kUserGeneratedAttributeType_None = 0,
|
|
|
|
|
kUserGeneratedAttributeType_MVMEngineering = 1,
|
|
|
|
|
kUserGeneratedAttributeType_HalloweenSpell = 2
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// On TF, these user-generated attributes can be from upgrade cards which only apply in MvM.
|
|
|
|
|
// We then colorize them based on whether they'll be active, with the caveat that out-of-game
|
|
|
|
|
// views always say yes (GC, loadout when not on a server, etc.).
|
|
|
|
|
if ( pAttribDef->GetUserGenerationType() == kUserGeneratedAttributeType_MVMEngineering && TFGameRules() && !TFGameRules()->IsMannVsMachineMode() )
|
|
|
|
|
{
|
|
|
|
|
eDefaultAttribColor = ATTRIB_COL_ITEMSET_MISSING;
|
|
|
|
|
}
|
|
|
|
|
// They can also be from Halloween spells. These are intended to expire after Halloween in any
|
|
|
|
|
// event, but for display purposes they'll appear in grey unless the holiday is active.
|
|
|
|
|
else if ( pAttribDef->GetUserGenerationType() == kUserGeneratedAttributeType_HalloweenSpell && !EconHolidays_IsHolidayActive( kHoliday_Halloween, CRTime::RTime32TimeCur() ) )
|
|
|
|
|
{
|
|
|
|
|
eDefaultAttribColor = ATTRIB_COL_ITEMSET_MISSING;
|
|
|
|
|
}
|
|
|
|
|
#endif // TF_CLIENT_DLL
|
|
|
|
|
|
|
|
|
|
AddDescLine( AttrDesc.GetDescription().Get(),
|
|
|
|
|
eOverrideDisplayColor != NUM_ATTRIB_COLORS ? // are we overriding the output color?
|
|
|
|
|
eOverrideDisplayColor : // we are
|
|
|
|
|
eDefaultAttribColor, // fall back to normal attribute color
|
|
|
|
|
kDescLineFlag_Attribute );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// --------------------------------------------------------------------------
|
|
|
|
|
// Purpose:
|
|
|
|
|
// --------------------------------------------------------------------------
|
|
|
|
|
void CEconItemDescription::AddDescLine( const locchar_t *pString, attrib_colors_t eColor, uint32 unMetaType, CUtlVector<econ_item_description_line_t> *out_pOptionalDescLineDest /* = NULL */, item_definition_index_t unDefIndex /* = INVALID_ITEM_DEF_INDEX */, bool bIsItemForSale /*= true*/ )
|
|
|
|
|
{
|
|
|
|
|
CUtlVector<econ_item_description_line_t>& vecTargetDescLines = out_pOptionalDescLineDest ? *out_pOptionalDescLineDest : m_vecDescLines;
|
|
|
|
|
|
|
|
|
|
econ_item_description_line_t& line = vecTargetDescLines[ vecTargetDescLines.AddToTail() ];
|
|
|
|
|
|
|
|
|
|
line.eColor = eColor;
|
|
|
|
|
line.unMetaType = unMetaType;
|
|
|
|
|
line.sText = pString;
|
|
|
|
|
line.unDefIndex = unDefIndex;
|
|
|
|
|
line.bIsItemForSale = bIsItemForSale;
|
|
|
|
|
|
|
|
|
|
#if TF_ANTI_IDLEBOT_VERIFICATION
|
|
|
|
|
if ( m_pHashContext )
|
|
|
|
|
{
|
|
|
|
|
const int iLineCount = vecTargetDescLines.Count() + 1;
|
|
|
|
|
|
|
|
|
|
char verboseStringBuf[ k_VerboseStringBufferSize ];
|
|
|
|
|
TFDescription_HashDataMunge( m_pHashContext, iLineCount, m_bIsVerbose, BuildVerboseStrings( verboseStringBuf, m_bIsVerbose, "%d", iLineCount ) ); // which line did we just add?
|
|
|
|
|
TFDescription_HashDataMunge( m_pHashContext, line.eColor, m_bIsVerbose, BuildVerboseStrings( verboseStringBuf, m_bIsVerbose,"%d", line.eColor ) );
|
|
|
|
|
TFDescription_HashDataMunge( m_pHashContext, line.unMetaType, m_bIsVerbose, BuildVerboseStrings( verboseStringBuf, m_bIsVerbose, "%d", line.unMetaType ) );
|
|
|
|
|
|
|
|
|
|
#ifdef GC_DLL
|
|
|
|
|
COMPILE_TIME_ASSERT( sizeof( locchar_t ) == sizeof( char ) );
|
|
|
|
|
TFDescription_HashDataMungeContents( m_pHashContext, pString, StringFuncs<locchar_t>::Length( pString ) * sizeof( locchar_t ), m_bIsVerbose, pString );
|
|
|
|
|
#else
|
|
|
|
|
COMPILE_TIME_ASSERT( sizeof( locchar_t ) == sizeof( wchar_t ) );
|
|
|
|
|
|
|
|
|
|
CUtlConstString ansiString;
|
|
|
|
|
GLocalizationProvider()->ConvertLoccharToANSI( pString, &ansiString );
|
|
|
|
|
TFDescription_HashDataMungeContents( m_pHashContext, ansiString.Get(), StringFuncs<char>::Length( ansiString.Get() ) * sizeof( char ), m_bIsVerbose, ansiString.Get() );
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
#endif // TF_ANTI_IDLEBOT_VERIFICATION
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// --------------------------------------------------------------------------
|
|
|
|
|
// Purpose:
|
|
|
|
|
// --------------------------------------------------------------------------
|
|
|
|
|
void CEconItemDescription::AddEmptyDescLine( CUtlVector<econ_item_description_line_t> *out_pOptionalDescLineDest )
|
|
|
|
|
{
|
|
|
|
|
AddDescLine( LOCCHAR(" "), ATTRIB_COL_NEUTRAL, kDescLineFlag_Empty, out_pOptionalDescLineDest );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// --------------------------------------------------------------------------
|
|
|
|
|
// Purpose:
|
|
|
|
|
// --------------------------------------------------------------------------
|
|
|
|
|
void CEconItemDescription::LocalizedAddDescLine( const CLocalizationProvider *pLocalizationProvider, const char *pLocalizationToken, attrib_colors_t eColor, uint32 unMetaType, CUtlVector<econ_item_description_line_t> *out_pOptionalDescLineDest /* = NULL */, item_definition_index_t unDefIndex /* = INVALID_ITEM_DEF_INDEX */, bool bIsItemForSale /* = true */ )
|
|
|
|
|
{
|
|
|
|
|
Assert( pLocalizationToken );
|
|
|
|
|
|
|
|
|
|
const locchar_t *pTextToAdd = pLocalizationProvider->Find( pLocalizationToken );
|
|
|
|
|
|
|
|
|
|
if ( pTextToAdd )
|
|
|
|
|
{
|
|
|
|
|
AddDescLine( pTextToAdd, eColor, unMetaType, out_pOptionalDescLineDest, unDefIndex, bIsItemForSale );
|
|
|
|
|
}
|
|
|
|
|
else if ( pLocalizationToken && (pLocalizationToken[0] != '#') )
|
|
|
|
|
{
|
|
|
|
|
// If we couldn't localize correctly, we might be a string literal like "My temp item desc.". In
|
|
|
|
|
// this case, we use that string as-is.
|
|
|
|
|
CUtlConstStringBase<locchar_t> loc_sText;
|
|
|
|
|
pLocalizationProvider->ConvertUTF8ToLocchar( pLocalizationToken, &loc_sText );
|
|
|
|
|
AddDescLine( loc_sText.Get(), eColor, unMetaType, out_pOptionalDescLineDest, unDefIndex, bIsItemForSale );
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// We couldn't localize this token, but also don't think it was a string meant to be user-facing so
|
|
|
|
|
// just silently fail.
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#if TF_ANTI_IDLEBOT_VERIFICATION
|
|
|
|
|
if ( m_pHashContext )
|
|
|
|
|
{
|
|
|
|
|
TFDescription_HashDataMungeContents( m_pHashContext, pLocalizationToken, V_strlen( pLocalizationToken ), m_bIsVerbose, pLocalizationToken );
|
|
|
|
|
}
|
|
|
|
|
#endif // TF_ANTI_IDLEBOT_VERIFICATION
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// --------------------------------------------------------------------------
|
|
|
|
|
// Purpose:
|
|
|
|
|
// --------------------------------------------------------------------------
|
|
|
|
|
class CGameItemDefinition_EconItemInterfaceWrapper : public CMaterialOverrideContainer< IEconItemInterface >
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
CGameItemDefinition_EconItemInterfaceWrapper( const CEconItemDefinition *pEconItemDefinition, entityquality_t eQuality )
|
|
|
|
|
: m_pEconItemDefinition( pEconItemDefinition )
|
|
|
|
|
, m_eQuality( eQuality )
|
|
|
|
|
{
|
|
|
|
|
Assert( m_pEconItemDefinition );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
virtual const GameItemDefinition_t *GetItemDefinition() const { return assert_cast<const GameItemDefinition_t *>( m_pEconItemDefinition ); }
|
|
|
|
|
|
|
|
|
|
virtual itemid_t GetID() const { return INVALID_ITEM_ID; }
|
|
|
|
|
virtual uint32 GetAccountID() const { return 0; }
|
|
|
|
|
virtual int32 GetQuality() const { return m_eQuality; }
|
|
|
|
|
virtual style_index_t GetStyle() const { return INVALID_STYLE_INDEX; }
|
|
|
|
|
virtual uint8 GetFlags() const { return 0; }
|
|
|
|
|
virtual eEconItemOrigin GetOrigin() const { return kEconItemOrigin_Invalid; }
|
|
|
|
|
virtual int GetQuantity() const { return 1; }
|
|
|
|
|
virtual uint32 GetItemLevel() const { return 0; }
|
|
|
|
|
virtual bool GetInUse() const { return false; }
|
|
|
|
|
|
|
|
|
|
virtual const char *GetCustomName() const { return NULL; }
|
|
|
|
|
virtual const char *GetCustomDesc() const { return NULL; }
|
|
|
|
|
|
|
|
|
|
virtual CEconItemPaintKitDefinition *GetCustomPainkKitDefinition( void ) const { return GetItemDefinition()->GetCustomPainkKitDefinition(); }
|
|
|
|
|
|
|
|
|
|
// IEconItemInterface attribute iteration interface. This is not meant to be used for
|
|
|
|
|
// attribute lookup! This is meant for anything that requires iterating over the full
|
|
|
|
|
// attribute list.
|
|
|
|
|
virtual void IterateAttributes( class IEconItemAttributeIterator *pIterator ) const OVERRIDE
|
|
|
|
|
{
|
|
|
|
|
Assert( pIterator );
|
|
|
|
|
|
|
|
|
|
m_pEconItemDefinition->IterateAttributes( pIterator );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
const CEconItemDefinition *m_pEconItemDefinition;
|
|
|
|
|
entityquality_t m_eQuality;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// --------------------------------------------------------------------------
|
|
|
|
|
// Purpose:
|
|
|
|
|
// --------------------------------------------------------------------------
|
|
|
|
|
CEconItemLocalizedFullNameGenerator::CEconItemLocalizedFullNameGenerator( const CLocalizationProvider *pLocalizationProvider, const CEconItemDefinition *pItemDef, bool bUseProperName, entityquality_t eQuality )
|
|
|
|
|
{
|
|
|
|
|
Assert( pItemDef );
|
|
|
|
|
|
|
|
|
|
CGameItemDefinition_EconItemInterfaceWrapper EconItemDefinitionWrapper( pItemDef, eQuality );
|
|
|
|
|
GenerateLocalizedFullItemName( m_loc_LocalizedItemName, pLocalizationProvider, &EconItemDefinitionWrapper, k_EGenerateLocalizedFullItemName_Default, bUseProperName );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// --------------------------------------------------------------------------
|
|
|
|
|
// Purpose:
|
|
|
|
|
// --------------------------------------------------------------------------
|
|
|
|
|
class CMarketNameGenerator_EconItemInterfaceWrapper : public IEconItemInterface
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
CMarketNameGenerator_EconItemInterfaceWrapper( CEconItem *pItem )
|
|
|
|
|
: m_pItem( pItem )
|
|
|
|
|
{
|
|
|
|
|
Assert( m_pItem );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
virtual const GameItemDefinition_t *GetItemDefinition() const { return m_pItem->GetItemDefinition(); }
|
|
|
|
|
|
|
|
|
|
virtual itemid_t GetID() const { return m_pItem->GetID(); }
|
|
|
|
|
virtual uint32 GetAccountID() const { return 0; }
|
|
|
|
|
virtual int32 GetQuality() const { return m_pItem->GetQuality(); }
|
|
|
|
|
virtual style_index_t GetStyle() const { return INVALID_STYLE_INDEX; }
|
|
|
|
|
virtual uint8 GetFlags() const { return 0; }
|
|
|
|
|
virtual eEconItemOrigin GetOrigin() const { return kEconItemOrigin_Invalid; }
|
|
|
|
|
virtual int GetQuantity() const { return 1; }
|
|
|
|
|
virtual uint32 GetItemLevel() const { return 0; }
|
|
|
|
|
virtual bool GetInUse() const { return false; }
|
|
|
|
|
|
|
|
|
|
virtual const char *GetCustomName() const { return NULL; }
|
|
|
|
|
virtual const char *GetCustomDesc() const { return NULL; }
|
|
|
|
|
|
|
|
|
|
virtual CEconItemPaintKitDefinition *GetCustomPainkKitDefinition( void ) const { return m_pItem->GetCustomPainkKitDefinition(); }
|
|
|
|
|
virtual bool GetCustomPaintKitWear( float &flWear ) const { return m_pItem->GetCustomPaintKitWear( flWear ); }
|
|
|
|
|
|
|
|
|
|
virtual IMaterial *GetMaterialOverride( int iTeam ) OVERRIDE { return m_pItem->GetMaterialOverride( iTeam ); }
|
|
|
|
|
|
|
|
|
|
// IEconItemInterface attribute iteration interface. This is not meant to be used for
|
|
|
|
|
// attribute lookup! This is meant for anything that requires iterating over the full
|
|
|
|
|
// attribute list.
|
|
|
|
|
virtual void IterateAttributes( class IEconItemAttributeIterator *pIterator ) const OVERRIDE
|
|
|
|
|
{
|
|
|
|
|
Assert( pIterator );
|
|
|
|
|
|
|
|
|
|
// Wrap their iterator in our iterator that will selectively let specific attributes
|
|
|
|
|
// get iterated on by the wrapped iterator.
|
|
|
|
|
CMarketNameGenerator_SelectiveAttributeIterator iteratorWrapper( pIterator );
|
|
|
|
|
m_pItem->IterateAttributes( &iteratorWrapper );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
|
|
|
|
|
CEconItem *m_pItem;
|
|
|
|
|
|
|
|
|
|
// Iterator class that wraps another iterator and selectively allows specific attributes to be
|
|
|
|
|
// iterated by the passed in iterator
|
|
|
|
|
class CMarketNameGenerator_SelectiveAttributeIterator : public IEconItemAttributeIterator
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
CMarketNameGenerator_SelectiveAttributeIterator( IEconItemAttributeIterator* pIterator )
|
|
|
|
|
: m_pIterator( pIterator )
|
|
|
|
|
{}
|
|
|
|
|
|
|
|
|
|
virtual bool OnIterateAttributeValue( const CEconItemAttributeDefinition *pAttrDef, attrib_value_t value ) OVERRIDE
|
|
|
|
|
{
|
|
|
|
|
if( pAttrDef->CanAffectMarketName() )
|
|
|
|
|
{
|
|
|
|
|
m_pIterator->OnIterateAttributeValue( pAttrDef, value );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
virtual bool OnIterateAttributeValue( const CEconItemAttributeDefinition *pAttrDef, float value ) OVERRIDE
|
|
|
|
|
{
|
|
|
|
|
if( pAttrDef->CanAffectMarketName() )
|
|
|
|
|
{
|
|
|
|
|
m_pIterator->OnIterateAttributeValue( pAttrDef, value );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
virtual bool OnIterateAttributeValue( const CEconItemAttributeDefinition *pAttrDef, const uint64& value ) OVERRIDE
|
|
|
|
|
{
|
|
|
|
|
if( pAttrDef->CanAffectMarketName() )
|
|
|
|
|
{
|
|
|
|
|
m_pIterator->OnIterateAttributeValue( pAttrDef, value );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
virtual bool OnIterateAttributeValue( const CEconItemAttributeDefinition *pAttrDef, const CAttribute_String& value ) OVERRIDE
|
|
|
|
|
{
|
|
|
|
|
if( pAttrDef->CanAffectMarketName() )
|
|
|
|
|
{
|
|
|
|
|
m_pIterator->OnIterateAttributeValue( pAttrDef, value );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
virtual bool OnIterateAttributeValue( const CEconItemAttributeDefinition *pAttrDef, const CAttribute_DynamicRecipeComponent& value ) OVERRIDE
|
|
|
|
|
{
|
|
|
|
|
if( pAttrDef->CanAffectMarketName() )
|
|
|
|
|
{
|
|
|
|
|
m_pIterator->OnIterateAttributeValue( pAttrDef, value );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
virtual bool OnIterateAttributeValue( const CEconItemAttributeDefinition *pAttrDef, const CAttribute_ItemSlotCriteria& value ) OVERRIDE
|
|
|
|
|
{
|
|
|
|
|
if( pAttrDef->CanAffectMarketName() )
|
|
|
|
|
{
|
|
|
|
|
m_pIterator->OnIterateAttributeValue( pAttrDef, value );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
virtual bool OnIterateAttributeValue( const CEconItemAttributeDefinition *pAttrDef, const CAttribute_WorldItemPlacement& value ) OVERRIDE
|
|
|
|
|
{
|
|
|
|
|
if ( pAttrDef->CanAffectMarketName() )
|
|
|
|
|
{
|
|
|
|
|
m_pIterator->OnIterateAttributeValue( pAttrDef, value );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
|
|
|
|
|
IEconItemAttributeIterator *m_pIterator;
|
|
|
|
|
};
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// --------------------------------------------------------------------------
|
|
|
|
|
// Purpose:
|
|
|
|
|
// --------------------------------------------------------------------------
|
|
|
|
|
CEconItemLocalizedMarketNameGenerator::CEconItemLocalizedMarketNameGenerator( const CLocalizationProvider *pLocalizationProvider, CEconItem *pItem, bool bUseProperName )
|
|
|
|
|
{
|
|
|
|
|
Assert( pItem );
|
|
|
|
|
|
|
|
|
|
CMarketNameGenerator_EconItemInterfaceWrapper EconItemWrapper( pItem );
|
|
|
|
|
GenerateLocalizedFullItemName( m_loc_LocalizedItemName, pLocalizationProvider, &EconItemWrapper, k_EGenerateLocalizedFullItemName_WithPaintWear, bUseProperName );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endif // BUILD_ITEM_NAME_AND_DESC
|