Team Fortress 2 Source Code as on 22/4/2020
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

4146 lines
158 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. #include "cbase.h"
  3. #include "econ_item_description.h"
  4. #include "econ_item_interface.h"
  5. #include "econ_item_tools.h"
  6. #include "econ_holidays.h"
  7. #include "econ_store.h"
  8. #include "tier1/ilocalize.h"
  9. #include "localization_provider.h"
  10. #include "rtime.h"
  11. #include "econ_dynamic_recipe.h"
  12. #ifdef GC
  13. // the GC needs accountdetails to get persona names
  14. #include "gcsdk/accountdetails.h"
  15. #else // !GC
  16. #ifndef EXTERNALTESTS_DLL
  17. #include "econ_item_inventory.h"
  18. #endif
  19. #ifdef CLIENT_DLL
  20. #include "gc_clientsystem.h"
  21. #include "client_community_market.h" // for Market data in tooltips
  22. #include "econ_ui.h" // for money-value-to-display-string formatting
  23. #include "store/store_panel.h" // for money-value-to-display-string formatting
  24. #endif // CLIENT_DLL
  25. #endif // GC
  26. #ifdef PROJECT_TF
  27. #include "tf_duel_summary.h"
  28. #include "econ_contribution.h"
  29. #include "tf_player_info.h"
  30. #include "tf_wardata.h"
  31. #ifdef TF_CLIENT_DLL
  32. #include "tf_gamerules.h"
  33. #include "tf_mapinfo.h"
  34. #endif
  35. #endif
  36. #ifdef VPROF_ENABLED
  37. static const char *g_pszEconDescriptionVprofGroup = _T("Econ Description");
  38. #endif
  39. extern const char *GetWearLocalizationString( float flWear );
  40. // --------------------------------------------------------------------------
  41. // Local Helper
  42. // --------------------------------------------------------------------------
  43. const size_t k_VerboseStringBufferSize = 128;
  44. static char *BuildVerboseStrings( char buf[k_VerboseStringBufferSize], bool bIsVerbose, const char *format, ... )
  45. {
  46. if ( !bIsVerbose )
  47. return NULL;
  48. va_list argptr;
  49. va_start( argptr, format );
  50. Q_vsnprintf( buf, k_VerboseStringBufferSize, format, argptr );
  51. va_end(argptr);
  52. return buf;
  53. }
  54. // --------------------------------------------------------------------------
  55. // Purpose:
  56. // --------------------------------------------------------------------------
  57. static bool IsStorePreviewItem( const IEconItemInterface *pEconItem )
  58. {
  59. Assert( pEconItem );
  60. #ifdef CLIENT_DLL
  61. return pEconItem->GetFlags() & kEconItemFlagClient_StoreItem;
  62. #else
  63. return false;
  64. #endif
  65. }
  66. // --------------------------------------------------------------------------
  67. // Purpose:
  68. // --------------------------------------------------------------------------
  69. void IEconItemDescription::YieldingFillOutEconItemDescription( IEconItemDescription *out_pDescription, CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem )
  70. {
  71. VPROF_BUDGET( "IEconItemDescription::YieldingFillOutEconItemDescription()", g_pszEconDescriptionVprofGroup );
  72. Assert( out_pDescription );
  73. Assert( pLocalizationProvider );
  74. Assert( pEconItem );
  75. out_pDescription->YieldingCacheDescriptionData( pLocalizationProvider, pEconItem );
  76. out_pDescription->GenerateDescriptionLines( pLocalizationProvider, pEconItem );
  77. }
  78. // --------------------------------------------------------------------------
  79. // Purpose:
  80. // --------------------------------------------------------------------------
  81. const econ_item_description_line_t *IEconItemDescription::GetFirstLineWithMetaType( uint32 unMetaTypeSearchFlags ) const
  82. {
  83. for ( unsigned int i = 0; i < GetLineCount(); i++ )
  84. {
  85. const econ_item_description_line_t& pLine = GetLine(i);
  86. if ( (pLine.unMetaType & unMetaTypeSearchFlags) == unMetaTypeSearchFlags )
  87. return &pLine;
  88. }
  89. return NULL;
  90. }
  91. #ifdef BUILD_ITEM_NAME_AND_DESC
  92. // --------------------------------------------------------------------------
  93. // Purpose:
  94. // --------------------------------------------------------------------------
  95. CLocalizedStringArg<CLocalizedRTime32>::CLocalizedStringArg( const CLocalizedRTime32& cTimeIn )
  96. {
  97. #if TF_ANTI_IDLEBOT_VERIFICATION
  98. // We expect the client and the GC to display dates and times differently in certain situations (ie.,
  99. // they may disagree on whether to display in GMT). Rather than trying to find the specific cases where
  100. // they might agree and let them through, we just early out and feed back the empty string for all
  101. // date/time formatting when doing client verification.
  102. if ( cTimeIn.m_pHashContext )
  103. return;
  104. #endif
  105. CRTime cTime( cTimeIn.m_unTime );
  106. // The GC will always display time in GMT. "Local time" isn't a useful thing from a client's perspective
  107. // when viewing an item in the Steam Community, etc.
  108. #ifdef GC_DLL
  109. cTime.SetToGMT( true );
  110. #else
  111. cTime.SetToGMT( cTimeIn.m_bForceGMTOnClient );
  112. #endif
  113. const locchar_t *loc_LocalizationFormat = cTimeIn.m_pLocalizationProvider->Find( cTime.BIsGMT() ? "Econ_DateFormat_GMT" : "Econ_DateFormat" );
  114. time_t tTime = cTime.GetRTime32();
  115. struct tm tmStruct;
  116. struct tm *ptm = cTime.BIsGMT() ? Plat_gmtime( &tTime, &tmStruct ) : Plat_localtime( &tTime, &tmStruct );
  117. time_t tFinalTime = mktime( ptm );
  118. char rgchDateBuf[ 128 ];
  119. BGetLocalFormattedDate( tFinalTime, rgchDateBuf, sizeof( rgchDateBuf ) );
  120. KeyValues *pKeyValues = new KeyValues( "DateTokens" );
  121. pKeyValues->SetString( "day", &rgchDateBuf[0] );
  122. pKeyValues->SetInt( "hour", cTime.GetHour() );
  123. pKeyValues->SetString( "min", CFmtStr( "%02u", cTime.GetMinute() ).Access() );
  124. pKeyValues->SetString( "sec", CFmtStr( "%02u", cTime.GetSecond() ).Access() );
  125. m_Str = CConstructLocalizedString( loc_LocalizationFormat, pKeyValues );
  126. pKeyValues->deleteThis();
  127. }
  128. // --------------------------------------------------------------------------
  129. // Purpose:
  130. // --------------------------------------------------------------------------
  131. void CEconItemDescription::YieldingFillOutAccountPersonaName( const CLocalizationProvider *pLocalizationProvider, uint32 unAccountID )
  132. {
  133. Assert( pLocalizationProvider );
  134. // Never cache invalid accounts.
  135. if ( unAccountID == 0 )
  136. return;
  137. // Make sure we have a cache entry for this account ID. If we're hashing, we won't fill
  138. // this with real data to avoid discrepancies between the GC view of a persona name and the
  139. // client view, both of which are cached differently. If we're not hashing, we'll do our best
  140. // to find the current name. Either way, by the time this function ends we expect to have a
  141. // value stored for this account ID.
  142. CEconItemDescription::steam_account_persona_name_t& AccountPersona = vecPersonaNames[ vecPersonaNames.AddToTail() ];
  143. AccountPersona.unAccountID = unAccountID;
  144. #if TF_ANTI_IDLEBOT_VERIFICATION
  145. // Force persona names to match "verify" between the client and the GC for verification.
  146. if ( m_pHashContext )
  147. {
  148. AccountPersona.loc_sPersonaName = LOCCHAR("verify");
  149. }
  150. else
  151. #endif // TF_ANTI_IDLEBOT_VERIFICATION
  152. {
  153. const char *utf8_PersonaName = NULL;
  154. #ifdef GC
  155. CSteamID steamID( unAccountID, GCSDK::GGCHost()->GetUniverse(), k_EAccountTypeIndividual );
  156. utf8_PersonaName = GGCGameBase()->YieldingGetPersonaName( steamID );
  157. #else // if defined( CLIENT_DLL )
  158. utf8_PersonaName = InventoryManager()->PersonaName_Get( unAccountID );
  159. #endif
  160. #if defined( CLIENT_DLL )
  161. m_bUnknownPlayer = Q_strncmp( utf8_PersonaName, "[unknown]", ARRAYSIZE( "[unknown]" ) ) == 0;
  162. #endif
  163. // We should have filled this in with something by now, even if that something is "we couldn't
  164. // find useful information".
  165. Assert( utf8_PersonaName );
  166. // Convert our UTF8 to whatever we're using for localized display, and done.
  167. pLocalizationProvider->ConvertUTF8ToLocchar( utf8_PersonaName, &AccountPersona.loc_sPersonaName );
  168. }
  169. Assert( !AccountPersona.loc_sPersonaName.IsEmpty() );
  170. }
  171. // --------------------------------------------------------------------------
  172. // Purpose:
  173. // --------------------------------------------------------------------------
  174. const locchar_t *CEconItemDescription::FindAccountPersonaName( uint32 unAccountID ) const
  175. {
  176. FOR_EACH_VEC( vecPersonaNames, i )
  177. {
  178. if ( vecPersonaNames[i].unAccountID == unAccountID )
  179. return vecPersonaNames[i].loc_sPersonaName.Get();
  180. }
  181. // FIXME: add localization token
  182. return LOCCHAR("Unknown User");
  183. }
  184. // --------------------------------------------------------------------------
  185. // Purpose:
  186. // --------------------------------------------------------------------------
  187. void CEconItemDescription::YieldingFillOutAccountTypeCache( uint32 unAccountID, int nClassID )
  188. {
  189. if( !unAccountID )
  190. return;
  191. #ifdef GC_DLL
  192. CEconSharedObjectCache *pSOCache = GGCEcon()->YieldingFindOrLoadEconSOCache( CSteamID( unAccountID, GCSDK::GGCHost()->GetUniverse(), k_EAccountTypeIndividual ) );
  193. #else // if defined( CLIENT_DLL )
  194. EUniverse eUniverse = GetUniverse();
  195. if ( eUniverse == k_EUniverseInvalid )
  196. return;
  197. GCSDK::CGCClientSharedObjectCache *pSOCache = GCClientSystem()->GetSOCache( CSteamID( unAccountID, eUniverse, k_EAccountTypeIndividual ) );
  198. #endif
  199. if ( !pSOCache )
  200. return;
  201. GCSDK::CSharedObjectTypeCache *pTypeCache = pSOCache->FindTypeCache( nClassID );
  202. if ( !pTypeCache )
  203. return;
  204. CEconItemDescription::steam_account_type_cache_t& AccountTypeCache = vecTypeCaches[ vecTypeCaches.AddToTail() ];
  205. AccountTypeCache.unAccountID = unAccountID;
  206. AccountTypeCache.nClassID = nClassID;
  207. AccountTypeCache.pTypeCache = pTypeCache;
  208. }
  209. // --------------------------------------------------------------------------
  210. // Purpose:
  211. // --------------------------------------------------------------------------
  212. GCSDK::CSharedObjectTypeCache *CEconItemDescription::FindAccountTypeCache( uint32 unAccountID, int nClassID ) const
  213. {
  214. FOR_EACH_VEC( vecTypeCaches, i )
  215. {
  216. if ( vecTypeCaches[i].unAccountID == unAccountID &&
  217. vecTypeCaches[i].nClassID == nClassID )
  218. {
  219. return vecTypeCaches[i].pTypeCache;
  220. }
  221. }
  222. return NULL;
  223. }
  224. // --------------------------------------------------------------------------
  225. // Purpose:
  226. // --------------------------------------------------------------------------
  227. template < typename T >
  228. const T *CEconItemDescription::FindAccountTypeCacheSingleton( uint32 unAccountID, int nClassID ) const
  229. {
  230. GCSDK::CSharedObjectTypeCache *pSOTypeCache = FindAccountTypeCache( unAccountID, nClassID );
  231. if ( !pSOTypeCache )
  232. return NULL;
  233. if ( pSOTypeCache->GetCount() != 1 )
  234. return NULL;
  235. return dynamic_cast<T *>( pSOTypeCache->GetObject( 0 ) );
  236. }
  237. // --------------------------------------------------------------------------
  238. // Purpose:
  239. // --------------------------------------------------------------------------
  240. void CEconItemDescription::YieldingCacheDescriptionData( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem )
  241. {
  242. VPROF_BUDGET( "CEconItemDescription::YieldingCacheDescriptionData()", g_pszEconDescriptionVprofGroup );
  243. vecPersonaNames.Purge();
  244. vecTypeCaches.Purge();
  245. // For each attribute that is set to display as an account ID, load the persona name for that account
  246. // ID in advance so that we don't yield somewhere crazy down below.
  247. // Walk our attribute list and accumulate IDs.
  248. CSteamAccountIDAttributeCollector AccountIDCollector;
  249. pEconItem->IterateAttributes( &AccountIDCollector );
  250. const CUtlVector<uint32>& vecSteamAccountIDs = AccountIDCollector.GetAccountIDs();
  251. // Look up the persona names for each account referenced by an attribute directly.
  252. FOR_EACH_VEC( vecSteamAccountIDs, i )
  253. {
  254. YieldingFillOutAccountPersonaName( pLocalizationProvider, vecSteamAccountIDs[i] );
  255. }
  256. // Look up the persona names for each account referencing an attribute indirectly (ie., just stuffed
  257. // into 32 bits).
  258. for ( int i = 0; i < GetKillEaterAttrCount(); i++ )
  259. {
  260. uint32 unRestrictionType;
  261. if ( pEconItem->FindAttribute( GetKillEaterAttr_Restriction(i), &unRestrictionType ) &&
  262. unRestrictionType == kStrangeEventRestriction_VictimSteamAccount )
  263. {
  264. uint32 unAccountID;
  265. DbgVerify( pEconItem->FindAttribute( GetKillEaterAttr_RestrictionValue(i), &unAccountID ) );
  266. YieldingFillOutAccountPersonaName( pLocalizationProvider, unAccountID );
  267. }
  268. }
  269. #ifdef PROJECT_TF
  270. uint32 unAccountID = pEconItem->GetAccountID();
  271. // Duel summary.
  272. {
  273. // We'll need to access other information about our duel stats later, but we also need to precache
  274. // the account name of whoever our last kill was beforehand.
  275. YieldingFillOutAccountTypeCache( unAccountID, CTFDuelSummary::k_nTypeID );
  276. // In TF, we also store information about our previous duel target, stored way way down inside some
  277. // other structures.
  278. const CTFDuelSummary *pDuelSummary = FindAccountTypeCacheSingleton<CTFDuelSummary>( unAccountID, CTFDuelSummary::k_nTypeID );
  279. if ( pDuelSummary )
  280. {
  281. YieldingFillOutAccountPersonaName( pLocalizationProvider, pDuelSummary->Obj().last_duel_account_id() );
  282. }
  283. }
  284. // Map contributions.
  285. YieldingFillOutAccountTypeCache( unAccountID, CTFMapContribution::k_nTypeID );
  286. // New users helped.
  287. YieldingFillOutAccountTypeCache( unAccountID, CTFPlayerInfo::k_nTypeID );
  288. // War data
  289. YieldingFillOutAccountTypeCache( unAccountID, CWarData::k_nTypeID );
  290. #ifdef CLIENT_DLL
  291. // Duck LeaderBoards
  292. {
  293. static CSchemaAttributeDefHandle pAttrDef_DisplayDuckLeaderboard( "display duck leaderboard" );
  294. if ( pEconItem->FindAttribute( pAttrDef_DisplayDuckLeaderboard ) )
  295. {
  296. CUtlVector< AccountID_t > accountIds;
  297. Leaderboards_GetDuckLeaderboardSteamIDs( accountIds );
  298. FOR_EACH_VEC( accountIds, i )
  299. {
  300. // Look up the persona names for each account referenced in the leaderboard
  301. YieldingFillOutAccountPersonaName( pLocalizationProvider, accountIds[i] );
  302. }
  303. }
  304. }
  305. #endif // CLIENT_DLL
  306. #if TF_ANTI_IDLEBOT_VERIFICATION
  307. if ( m_pHashContext )
  308. {
  309. // Feed in the account IDs of each person we care about. We don't feed in the actual persona name
  310. // string because this could theoretically differ between the GC and the client if one is out of sync.
  311. FOR_EACH_VEC( vecPersonaNames, i )
  312. {
  313. char verboseStringBuf[ k_VerboseStringBufferSize ];
  314. TFDescription_HashDataMunge( m_pHashContext, vecPersonaNames[i].unAccountID, m_bIsVerbose, BuildVerboseStrings( verboseStringBuf, m_bIsVerbose, "%d", vecPersonaNames[i].unAccountID ) );
  315. }
  316. // Are we in text mode or not? We'll use this to generate two different hashes to compare against.
  317. unsigned int unRunningTextMode =
  318. #ifdef GC_DLL
  319. m_bTextModeEnabled
  320. #else
  321. *((bool *)g_pClientPurchaseInterface - 156)
  322. #endif
  323. ? 0x73aaff8e
  324. : 0x12800c0a;
  325. char verboseStringBuf[ k_VerboseStringBufferSize ];
  326. TFDescription_HashDataMunge( m_pHashContext, unRunningTextMode, m_bIsVerbose, BuildVerboseStrings( verboseStringBuf, m_bIsVerbose, "%d", unRunningTextMode ) );
  327. }
  328. #endif // TF_ANTI_IBLEBOT_VERIFICATION
  329. #endif // PROJECT_TF
  330. }
  331. void CEconItemDescription::GenerateDescriptionLines( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem )
  332. {
  333. VPROF_BUDGET( "CEconItemDescription::GenerateDescriptionLines()", g_pszEconDescriptionVprofGroup );
  334. Assert( pLocalizationProvider );
  335. Assert( pEconItem );
  336. m_vecDescLines.Purge();
  337. Generate_ItemName( pLocalizationProvider, pEconItem );
  338. Generate_ItemRarityDesc( pLocalizationProvider, pEconItem );
  339. Generate_ItemLevelDesc( pLocalizationProvider, pEconItem );
  340. //Generate_WearAmountDesc( pLocalizationProvider, pEconItem );
  341. #if defined( STAGING_ONLY ) && defined( CLIENT_DLL )
  342. Generate_DebugInformation( pLocalizationProvider, pEconItem );
  343. #endif
  344. // If we decide that for performance reasons some descriptions only want the name/description
  345. // information and not all the details, this is the block to skip over.
  346. {
  347. Generate_CraftTag( pLocalizationProvider, pEconItem );
  348. Generate_StyleDesc( pLocalizationProvider, pEconItem );
  349. Generate_Painted( pLocalizationProvider, pEconItem );
  350. Generate_HolidayRestriction( pLocalizationProvider, pEconItem );
  351. #ifdef PROJECT_TF
  352. Generate_SaxxyAwardDesc( pLocalizationProvider, pEconItem );
  353. #endif // PROJECT_TF
  354. Generate_VisibleAttributes( pLocalizationProvider, pEconItem );
  355. Generate_QualityDesc( pLocalizationProvider, pEconItem );
  356. Generate_ItemDesc( pLocalizationProvider, pEconItem );
  357. Generate_Bundle( pLocalizationProvider, pEconItem );
  358. Generate_GiftedBy( pLocalizationProvider, pEconItem );
  359. #ifdef PROJECT_TF
  360. Generate_DuelingMedal( pLocalizationProvider, pEconItem );
  361. Generate_MapContributor( pLocalizationProvider, pEconItem );
  362. Generate_FriendlyHat( pLocalizationProvider, pEconItem );
  363. Generate_SquadSurplusClaimedBy( pLocalizationProvider, pEconItem );
  364. Generate_MvmChallenges( pLocalizationProvider, pEconItem );
  365. Generate_DynamicRecipe( pLocalizationProvider, pEconItem );
  366. Generate_Leaderboard( pLocalizationProvider, pEconItem );
  367. #endif // PROJECT_TF
  368. Generate_XifierToolTargetItem( pLocalizationProvider, pEconItem );
  369. Generate_LootListDesc( pLocalizationProvider, pEconItem );
  370. Generate_EventDetail( pLocalizationProvider, pEconItem );
  371. Generate_ItemSetDesc( pLocalizationProvider, pEconItem );
  372. Generate_CollectionDesc( pLocalizationProvider, pEconItem );
  373. Generate_ExpirationDesc( pLocalizationProvider, pEconItem );
  374. Generate_DropPeriodDesc( pLocalizationProvider, pEconItem );
  375. Generate_MarketInformation( pLocalizationProvider, pEconItem );
  376. Generate_DirectX8Warning( pLocalizationProvider, pEconItem );
  377. }
  378. // Certain information (tradeability, etc.) used to only get displayed if we were the owning player, or
  379. // if we were looking at an unowned item (ie., a store preview) and want to show what it will look like
  380. // when it *is* owned. Unfortunately this led to problems where you wouldn't know if the item you were
  381. // about to be traded (currently not owned by you) would be craftable, etc.
  382. Generate_FlagsAttributes( pLocalizationProvider, pEconItem );
  383. }
  384. // --------------------------------------------------------------------------
  385. // Purpose: Code to build up the item display name, including any relevant quality
  386. // strings, custom renaming, craft numbers, and anything else we decide
  387. // to throw at it.
  388. // --------------------------------------------------------------------------
  389. /*static*/ uint32 GetScoreTypeForKillEaterAttr( const IEconItemInterface *pEconItem, const CEconItemAttributeDefinition *pAttribDef )
  390. {
  391. // What sort of event are we tracking? If we don't have an attribute at all we're one of the
  392. // old kill-eater weapons that didn't specify what it was tracking.
  393. uint32 unKillEaterEventType = 0;
  394. // This will overwrite our default 0 value if we have a value set but leave it if not.
  395. {
  396. float fKillEaterEventType;
  397. if ( FindAttribute_UnsafeBitwiseCast<attrib_value_t>( pEconItem, pAttribDef, &fKillEaterEventType ) )
  398. {
  399. unKillEaterEventType = fKillEaterEventType;
  400. }
  401. }
  402. return unKillEaterEventType;
  403. }
  404. // The item backend may add craft numbers well past what we want to display in the game. This
  405. // function determines whether a given number should be visible rather than always showing
  406. // whatever the GC shows.
  407. bool ShouldDisplayCraftCounterValue( int iValue )
  408. {
  409. return iValue > 0 && iValue <= 100;
  410. }
  411. // This function will return the localized string (ie., "Face-Melting") for a specific item based
  412. // on the score it has accumulated.
  413. #if defined( CLIENT_DLL ) && defined( STAGING_ONLY )
  414. ConVar staging_force_strange_score_selector_value( "staging_force_strange_score_selector_value", "-1" );
  415. #endif // defined( CLIENT_DLL ) && defined( STAGING_ONLY )
  416. class CStrangeRankLocalizationGenerator
  417. {
  418. public:
  419. CStrangeRankLocalizationGenerator( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem, bool bHashContextOff );
  420. bool IsValid() const { return m_bValid; }
  421. const locchar_t *GetRankLocalized() const { Assert( m_bValid ); return m_loc_Rank; }
  422. const locchar_t *GetRankSecondaryLocalized() const { Assert( m_bValid ); return m_loc_SecondaryRank; }
  423. uint32 GetStrangeType() const { Assert( m_bValid ); return m_unType; }
  424. uint32 GetStrangeScore() const { Assert( m_bValid ); return m_unScore; }
  425. uint32 GetUsedStrangeSlot() const { Assert( m_bValid ); return m_unUsedStrangeSlot; }
  426. private:
  427. bool m_bValid;
  428. const locchar_t *m_loc_Rank;
  429. const locchar_t *m_loc_SecondaryRank;
  430. uint32 m_unType;
  431. uint32 m_unScore;
  432. uint32 m_unUsedStrangeSlot;
  433. };
  434. CStrangeRankLocalizationGenerator::CStrangeRankLocalizationGenerator( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem, bool bHashContextOff )
  435. : m_bValid( false )
  436. , m_loc_Rank( NULL )
  437. , m_loc_SecondaryRank( NULL )
  438. , m_unType( kKillEaterEvent_PlayerKill )
  439. , m_unScore( 0 )
  440. , m_unUsedStrangeSlot( 0 )
  441. {
  442. Assert( pLocalizationProvider );
  443. Assert( pEconItem );
  444. static CSchemaAttributeDefHandle pAttrDef_StrangeScoreSelector( "strange score selector" );
  445. // Do we have a strange score selector attribute? If so, the value of this attribute will tell us which strange
  446. // attribute we're actually going to use to generate a name. Leaving this value as 0 will fall back to the
  447. // default behavior of looking at the base "kill eater" attribute.
  448. if ( pEconItem->FindAttribute( pAttrDef_StrangeScoreSelector, &m_unUsedStrangeSlot ) )
  449. {
  450. // Make sure the value we pulled from the database is within range.
  451. m_unUsedStrangeSlot = MIN( m_unUsedStrangeSlot, static_cast<uint32>( GetKillEaterAttrCount() ) );
  452. }
  453. #if defined( CLIENT_DLL ) && defined( STAGING_ONLY )
  454. if ( staging_force_strange_score_selector_value.GetInt() > 0 )
  455. {
  456. m_unUsedStrangeSlot = staging_force_strange_score_selector_value.GetInt();
  457. }
  458. #endif // defined( CLIENT_DLL ) && defined( STAGING_ONLY )
  459. // Use the strange prefix if the weapon has one.
  460. if ( !pEconItem->FindAttribute( GetKillEaterAttr_Score( m_unUsedStrangeSlot ), &m_unScore ) )
  461. return;
  462. // What type of event are we tracking and how does it describe itself?
  463. m_unType = GetScoreTypeForKillEaterAttr( pEconItem, GetKillEaterAttr_Type( m_unUsedStrangeSlot ) );
  464. const char *pszLevelingDataName = GetItemSchema()->GetKillEaterScoreTypeLevelingDataName( m_unType );
  465. if ( !pszLevelingDataName )
  466. {
  467. pszLevelingDataName = KILL_EATER_RANK_LEVEL_BLOCK_NAME;
  468. }
  469. uint32 uUsedScore = m_unScore;
  470. #ifdef TF_ANTI_IDLEBOT_VERIFICATION
  471. // TF2 Anti-Idle hack. It totally needs to be fixed
  472. if ( !bHashContextOff )
  473. {
  474. uUsedScore = 0;
  475. }
  476. #endif //TF_ANTI_IDLEBOT_VERIFICATION
  477. // For TF - Strange Scores reset on Trade, sharing that information is actually misleading so we'll always display base strange name
  478. #ifdef TF_GC_DLL
  479. uUsedScore = 0;
  480. #endif // TF_GC_DLL
  481. const CItemLevelingDefinition *pLevelDef = GetItemSchema()->GetItemLevelForScore( pszLevelingDataName, uUsedScore );
  482. if ( !pLevelDef )
  483. return;
  484. // Primary rank established!
  485. m_loc_Rank = pLocalizationProvider->Find( pLevelDef->GetNameLocalizationKey() );
  486. m_bValid = true;
  487. // Does this score slot have a restriction that adds additional text somewhere in the localization token?
  488. uint32 unFilterType;
  489. uint32 unFilterValue;
  490. if ( pEconItem->FindAttribute( GetKillEaterAttr_Restriction( m_unUsedStrangeSlot ), &unFilterType ) &&
  491. pEconItem->FindAttribute( GetKillEaterAttr_RestrictionValue( m_unUsedStrangeSlot ), &unFilterValue ) )
  492. {
  493. // Game-specific code doesn't belong here. "We're shipping soon" hack fun!
  494. #ifdef PROJECT_TF
  495. if ( unFilterType == kStrangeEventRestriction_Map )
  496. {
  497. const MapDef_t *pMap = GetItemSchema()->GetMasterMapDefByIndex( unFilterValue );
  498. if ( pMap && pMap->pszStrangePrefixLocKey )
  499. {
  500. m_loc_SecondaryRank = pLocalizationProvider->Find( pMap->pszStrangePrefixLocKey );
  501. }
  502. }
  503. else if (unFilterType == kStrangeEventRestriction_Competitive)
  504. {
  505. m_loc_SecondaryRank = pLocalizationProvider->Find( "TF_StrangeFilter_Prefix_Competitive" );
  506. }
  507. #endif // PROJECT_TF
  508. }
  509. }
  510. // ---------------------------------------------------------------------------------------------------------------------------
  511. void Econ_SetNameAsPaintkit( locchar_t( &out_pItemName )[MAX_ITEM_NAME_LENGTH], const CLocalizationProvider *pLocalizationProvider, CEconItemPaintKitDefinition *pPaintKit )
  512. {
  513. if ( !pPaintKit )
  514. return;
  515. // Generate Paint kitted name
  516. // IE Purple Rain Sniper Rifle
  517. locchar_t tempName[MAX_ITEM_NAME_LENGTH];
  518. loc_scpy_safe( tempName, out_pItemName );
  519. #ifndef GC
  520. //bool bAppendWeapon = wcsstr( pPaintKitStr, out_pItemName ) == NULL;
  521. g_pVGuiLocalize->ConstructString_safe( out_pItemName,
  522. LOCCHAR( "%s1" ),
  523. 1,
  524. pLocalizationProvider->Find( pPaintKit->GetLocalizeName() ) );
  525. #else
  526. // GC doesn't have g_pVGuiLocalize so we construct the painted gun string like this
  527. locchar_t *pPaintKitStr = pLocalizationProvider->Find( pPaintKit->GetLocalizeName() );
  528. loc_scpy_safe( out_pItemName, pPaintKitStr ? pPaintKitStr : LOCCHAR( "" ) );
  529. #endif
  530. }
  531. // ---------------------------------------------------------------------------------------------------------------------------
  532. void Econ_ConcatPaintKitName( locchar_t( &out_pItemName )[MAX_ITEM_NAME_LENGTH], const CLocalizationProvider *pLocalizationProvider, CEconItemPaintKitDefinition *pPaintKit )
  533. {
  534. if ( !pPaintKit )
  535. return;
  536. // Check to see if the paintkit localized name already has the weapon name, if so do not add (Weapon)
  537. locchar_t *pPaintKitStr = pLocalizationProvider->FindSafe( pPaintKit->GetLocalizeName() );
  538. locchar_t tempName[MAX_ITEM_NAME_LENGTH];
  539. loc_scpy_safe( tempName, out_pItemName );
  540. #ifndef GC
  541. bool bAppendWeapon = wcsstr( pPaintKitStr, out_pItemName ) == NULL;
  542. // Generate Paint kitted name
  543. // IE Purple Rain Sniper Rifle
  544. if ( bAppendWeapon )
  545. {
  546. g_pVGuiLocalize->ConstructString_safe( out_pItemName,
  547. LOCCHAR("%s1 (%s2)"),
  548. 2,
  549. pPaintKitStr,
  550. tempName );
  551. }
  552. else
  553. {
  554. g_pVGuiLocalize->ConstructString_safe( out_pItemName,
  555. LOCCHAR("%s1"),
  556. 1,
  557. pPaintKitStr
  558. );
  559. }
  560. #else
  561. // GC doesn't have g_pVGuiLocalize so we construct the painted gun string like this
  562. bool bAppendWeapon = V_strstr( pPaintKitStr, out_pItemName ) == NULL;
  563. // Generate Paint kitted name
  564. // IE Purple Rain Sniper Rifle
  565. loc_scpy_safe( out_pItemName, pPaintKitStr ? pPaintKitStr : LOCCHAR( "" ) );
  566. if ( bAppendWeapon )
  567. {
  568. loc_scat_safe( out_pItemName, LOCCHAR( " " ) );
  569. loc_scat_safe( out_pItemName, tempName );
  570. }
  571. #endif
  572. }
  573. // ---------------------------------------------------------------------------------------------------------------------------
  574. void Econ_ConcatPaintKitWear( locchar_t( &out_pItemName )[MAX_ITEM_NAME_LENGTH], const CLocalizationProvider *pLocalizationProvider, float flWear )
  575. {
  576. if ( flWear <= 0.0 )
  577. return;
  578. #ifndef GC
  579. locchar_t tempName[MAX_ITEM_NAME_LENGTH];
  580. loc_scpy_safe( tempName, out_pItemName );
  581. g_pVGuiLocalize->ConstructString_safe( out_pItemName,
  582. LOCCHAR( "%s1 (%s2)" ),
  583. 2,
  584. tempName,
  585. pLocalizationProvider->Find( GetWearLocalizationString( flWear ) )
  586. );
  587. #else
  588. // GC doesn't have g_pVGuiLocalize so we construct the painted gun string like this
  589. locchar_t *pWearStr = pLocalizationProvider->Find( GetWearLocalizationString( flWear ) );
  590. loc_scat_safe( out_pItemName, LOCCHAR( " (" ) );
  591. loc_scat_safe( out_pItemName, pWearStr ? pWearStr : LOCCHAR( "" ) );
  592. loc_scat_safe( out_pItemName, LOCCHAR( ")" ) );
  593. #endif
  594. }
  595. // ---------------------------------------------------------------------------------------------------------------------------
  596. static bool GetLocalizedBaseItemName( locchar_t (&szItemName)[MAX_ITEM_NAME_LENGTH], const CLocalizationProvider *pLocalizationProvider, const CEconItemDefinition *pEconItemDefinition )
  597. {
  598. if ( pEconItemDefinition->GetItemBaseName() )
  599. {
  600. const locchar_t *pLocalizedItemName = pLocalizationProvider->Find( pEconItemDefinition->GetItemBaseName() );
  601. if ( !pLocalizedItemName || !pLocalizedItemName[0] )
  602. {
  603. // Couldn't localize it, just use it raw
  604. pLocalizationProvider->ConvertUTF8ToLocchar( pEconItemDefinition->GetItemBaseName(), szItemName, ARRAYSIZE( szItemName ) );
  605. }
  606. else
  607. {
  608. loc_scpy_safe( szItemName, pLocalizedItemName );
  609. }
  610. return true;
  611. }
  612. return false;
  613. }
  614. // Given the item in pEconItem and the localization provider passed in, stuff the correct *localized*
  615. // string into out_pItemName.
  616. static void GenerateLocalizedFullItemName
  617. (
  618. locchar_t (&out_pItemName)[MAX_ITEM_NAME_LENGTH],
  619. const CLocalizationProvider *pLocalizationProvider,
  620. const IEconItemInterface *pEconItem,
  621. EGenerateLocalizedFullItemNameFlag_t eFlagsMask,
  622. bool bHashContextOff
  623. )
  624. {
  625. bool bUseProperName = bHashContextOff;
  626. Assert( pLocalizationProvider );
  627. Assert( pEconItem );
  628. static const locchar_t *s_pUnknownItemName = LOCCHAR("Unknown Item");
  629. const CEconItemDefinition *pEconItemDefinition = pEconItem->GetItemDefinition();
  630. if ( !pEconItemDefinition )
  631. {
  632. out_pItemName[0] = (locchar_t)0;
  633. return;
  634. }
  635. bool bIgnoreQualityAndWear = false;
  636. bool bIgnoreNameWithPaintkit = false;
  637. bool bHasCustomName = false;
  638. if ( eFlagsMask == k_EGenerateLocalizedFullItemName_Default )
  639. {
  640. bIgnoreQualityAndWear = pEconItem->GetCustomPainkKitDefinition() ? true : false;
  641. }
  642. if ( eFlagsMask == k_EGenerateLocalizedFullItemName_WithPaintkitNoItem )
  643. {
  644. bIgnoreNameWithPaintkit = pEconItem->GetCustomPainkKitDefinition() ? true : false;
  645. bIgnoreQualityAndWear = pEconItem->GetCustomPainkKitDefinition() ? true : false;
  646. }
  647. // Figure out which localization pattern we're using. By default we assume we're using the common "[Quality] [Item Name]"
  648. // format, but if we're a unique item with an article we'll change this later on.
  649. const char *pszLocalizationPattern = "ItemNameFormat";
  650. // Start with the base name.
  651. locchar_t szItemName[ MAX_ITEM_NAME_LENGTH ];
  652. static CSchemaAttributeDefHandle pAttrDef_ItemNameTextOverride( "item name text override" );
  653. CAttribute_String attrItemNameTextOverride;
  654. // Check if we ahve a item name override
  655. if ( pEconItem->FindAttribute( pAttrDef_ItemNameTextOverride, &attrItemNameTextOverride ) )
  656. {
  657. const locchar_t *pNameOverrideString = pLocalizationProvider->Find( attrItemNameTextOverride.value().c_str() );
  658. if ( pNameOverrideString )
  659. {
  660. loc_scpy_safe( szItemName, pNameOverrideString );
  661. bHasCustomName = true;
  662. }
  663. }
  664. else if( !GetLocalizedBaseItemName( szItemName, pLocalizationProvider, pEconItemDefinition ) )
  665. {
  666. loc_scpy_safe( szItemName, s_pUnknownItemName );
  667. }
  668. // Check for killstreak attribute
  669. enum { kKillStreakLength = 64, };
  670. locchar_t szKillStreak[ kKillStreakLength ] = LOCCHAR("");
  671. static CSchemaAttributeDefHandle pAttrDef_KillStreak( "killstreak tier" );
  672. uint32 nKillStreakValue;
  673. if ( pEconItem->FindAttribute( pAttrDef_KillStreak, &nKillStreakValue ) && !bIgnoreQualityAndWear )
  674. {
  675. nKillStreakValue = (float&)(nKillStreakValue);
  676. // if you have the eyeballs you are automatically higher tier
  677. static CSchemaAttributeDefHandle pAttrDef_KillStreakEyes( "killstreak effect" );
  678. static CSchemaAttributeDefHandle pAttrDef_KillStreakSheen( "killstreak idleeffect" );
  679. if ( pEconItem->FindAttribute( pAttrDef_KillStreakEyes ) )
  680. {
  681. nKillStreakValue = 3; // professional
  682. }
  683. else if ( pEconItem->FindAttribute( pAttrDef_KillStreakSheen ) )
  684. {
  685. nKillStreakValue = 2; // specialized
  686. }
  687. const locchar_t *pKillStreakLocalizedString = NULL;
  688. // All tier-1 killstreaks have idle effect 1
  689. if ( nKillStreakValue == 1 )
  690. {
  691. pKillStreakLocalizedString = pLocalizationProvider->Find( "ItemNameKillStreakv0" );
  692. }
  693. else if ( nKillStreakValue == 2 )
  694. {
  695. pKillStreakLocalizedString = pLocalizationProvider->Find( "ItemNameKillStreakv1" );
  696. }
  697. else // Tier-2's are things above 1
  698. {
  699. pKillStreakLocalizedString = pLocalizationProvider->Find( "ItemNameKillStreakv2" );
  700. }
  701. if ( pKillStreakLocalizedString )
  702. {
  703. loc_scpy_safe( szKillStreak, pKillStreakLocalizedString );
  704. // If we're appending some sort of killstreak identifier, dont use the proper name
  705. bUseProperName = false;
  706. }
  707. }
  708. // Check to see if we have a quality text override attribute. We can get this when a temporary item
  709. // comes in from a crafting recipe that needs to get its name generated, and wants to specify that it
  710. // takes in any quality
  711. static CSchemaAttributeDefHandle pAttrDef_QualityTextOverride( "quality text override" );
  712. CAttribute_String attrQualityTextOverride;
  713. pEconItem->FindAttribute( pAttrDef_QualityTextOverride, &attrQualityTextOverride );
  714. // Generate our quality string.
  715. enum { kQualityLength = 128, };
  716. locchar_t szQuality[ kQualityLength ] = LOCCHAR("");
  717. // Unique names may have a prefix or not, and so use a different format. (This is less to deal
  718. // with the space after "The" and more to deal with foreign languages that want to display unique
  719. // and non-unique items differently.
  720. const uint8 unQuality = pEconItem->GetQuality();
  721. if ( unQuality == AE_SELFMADE || ( !bIgnoreQualityAndWear ) )
  722. {
  723. // It's possible to get in here with a quality of -1 if we're dealing with an item view that has no
  724. // associated item. In that case we're probably doing something like browsing the armory, and in any
  725. // event don't have an item and so don't have a quality and so we just don't show a quality string.
  726. // If we have a quality text override, use that.
  727. const char *pszQualityLocalizationString = attrQualityTextOverride.has_value()
  728. ? attrQualityTextOverride.value().c_str()
  729. : EconQuality_GetLocalizationString( (EEconItemQuality)unQuality );
  730. if ( unQuality > 0 && pszQualityLocalizationString && unQuality != AE_PAINTKITWEAPON )
  731. {
  732. // Unique items use proper names, but not if we have a quality text override
  733. if ( unQuality == AE_UNIQUE && !attrQualityTextOverride.has_value() )
  734. {
  735. const locchar_t *pszArticleContent = NULL;
  736. if ( bUseProperName && pEconItemDefinition->HasProperName() )
  737. {
  738. pszArticleContent = pLocalizationProvider->Find( "TF_Unique_Prepend_Proper" );
  739. }
  740. // If the language isn't supposed to have articles or we just haven't provided one yet, fall
  741. // back to the empty string.
  742. if ( !pszArticleContent )
  743. {
  744. pszArticleContent = LOCCHAR("");
  745. }
  746. loc_scpy_safe( szQuality, pszArticleContent );
  747. }
  748. // Any quality besides unique ignores "proper name" articles.
  749. else
  750. {
  751. const locchar_t *pQualityLocalizedString = pLocalizationProvider->Find( pszQualityLocalizationString );
  752. if ( pQualityLocalizedString )
  753. {
  754. loc_scpy_safe( szQuality, pQualityLocalizedString );
  755. loc_scat_safe( szQuality, LOCCHAR(" ") );
  756. }
  757. }
  758. }
  759. {
  760. static CSchemaAttributeDefHandle pAttrDef_HideStrangePrefix( "hide_strange_prefix" );
  761. if ( !pAttrDef_HideStrangePrefix || !pEconItem->FindAttribute( pAttrDef_HideStrangePrefix ) )
  762. {
  763. //
  764. CStrangeRankLocalizationGenerator RankGenerator( pLocalizationProvider, pEconItem, bHashContextOff );
  765. if ( RankGenerator.IsValid() )
  766. {
  767. // If the quality of this item is special (not just strange) persist and append that value
  768. // Otherwise the ranker will replace the 'strange' quality tag with a strange rank
  769. if ( unQuality == AE_STRANGE )
  770. {
  771. loc_scpy_safe( szQuality,
  772. CConstructLocalizedString( LOCCHAR("%s1%s2 "),
  773. RankGenerator.GetRankLocalized(),
  774. RankGenerator.GetRankSecondaryLocalized() ? RankGenerator.GetRankSecondaryLocalized() : LOCCHAR("") ) );
  775. }
  776. else // Strange Unusual Something
  777. {
  778. loc_scpy_safe( szQuality,
  779. CConstructLocalizedString( LOCCHAR("%s1%s2 %s3"),
  780. RankGenerator.GetRankLocalized(),
  781. RankGenerator.GetRankSecondaryLocalized() ? RankGenerator.GetRankSecondaryLocalized() : LOCCHAR(""),
  782. szQuality) );
  783. }
  784. }
  785. }
  786. }
  787. }
  788. static CSchemaAttributeDefHandle pAttrDef_IsAustralium( "is australium item" );
  789. enum { kAustraliumLength = 64, };
  790. locchar_t szAustraliumSkin[ kAustraliumLength ] = LOCCHAR("");
  791. if ( pAttrDef_IsAustralium && pEconItem->FindAttribute( pAttrDef_IsAustralium ) )
  792. {
  793. const locchar_t *pAustraliumLocalizedString = pLocalizationProvider->Find( "ItemNameAustralium" );
  794. if ( pAustraliumLocalizedString )
  795. {
  796. loc_scpy_safe( szAustraliumSkin, pAustraliumLocalizedString );
  797. }
  798. }
  799. static CSchemaAttributeDefHandle pAttrDef_IsFestivized( "is_festivized" );
  800. enum { kFestiveLength = 64, };
  801. locchar_t szIsFestivized[kFestiveLength] = LOCCHAR( "" );
  802. if ( pAttrDef_IsFestivized && pEconItem->FindAttribute( pAttrDef_IsFestivized ) )
  803. {
  804. // TODO : update ItemNameFestive in tf_english to Festivized later to differentiate Festive vs Festivized
  805. const locchar_t *pFestivizedLocalizedString = pLocalizationProvider->Find( "ItemNameFestive" );
  806. if ( pFestivizedLocalizedString )
  807. {
  808. loc_scpy_safe( szIsFestivized, pFestivizedLocalizedString );
  809. }
  810. }
  811. const char* pszQualityFormat = ( !attrQualityTextOverride.has_value() && ( unQuality == AE_NORMAL || unQuality == AE_UNIQUE || unQuality == AE_PAINTKITWEAPON || bIgnoreQualityAndWear ) && unQuality != AE_SELFMADE )
  812. ? "ItemNameNormalOrUniqueQualityFormat"
  813. : "ItemNameQualityFormat";
  814. // TODO : Make Generic
  815. // Journal Leveling
  816. uint32 unDuckBadgeLevel;
  817. static CSchemaAttributeDefHandle pAttrDef_DuckBadgeLevel( "duck rating" );
  818. enum { kDuckBadgeLength = 64, };
  819. locchar_t szDuckBadge[kDuckBadgeLength] = LOCCHAR("");
  820. { //if ( pItem && FindAttribute_UnsafeBitwiseCast<attrib_value_t>( pItem, pAttr_DuckLevelBadge, &iDuckBadgeLevel ) )
  821. if ( pAttrDef_DuckBadgeLevel && FindAttribute_UnsafeBitwiseCast<attrib_value_t>( pEconItem, pAttrDef_DuckBadgeLevel, &unDuckBadgeLevel ) && unDuckBadgeLevel != 0 )
  822. {
  823. const CItemLevelingDefinition *pLevelDef = GetItemSchema()->GetItemLevelForScore( "Journal_DuckBadge", unDuckBadgeLevel );
  824. if ( pLevelDef )
  825. {
  826. loc_scpy_safe( szDuckBadge, pLocalizationProvider->Find( pLevelDef->GetNameLocalizationKey() ) );
  827. loc_scat_safe( szDuckBadge, LOCCHAR(" ") );
  828. }
  829. }
  830. }
  831. // Strange Unusual Festive Killstreak Australium ducks
  832. loc_scpy_safe( szQuality, CConstructLocalizedString( pLocalizationProvider->Find( pszQualityFormat ), szQuality, szIsFestivized, szKillStreak, szAustraliumSkin, szDuckBadge ) );
  833. enum { kLocalizedCrateSeriesLength = 128, };
  834. locchar_t szLocalizedCrateSeries[ kLocalizedCrateSeriesLength ] = LOCCHAR("");
  835. #ifdef PROJECT_TF
  836. static CSchemaAttributeDefHandle pAttrDef_SupplyCrateSeries( "set supply crate series" );
  837. // do not display series number for crates that have a collection reference
  838. if ( pAttrDef_SupplyCrateSeries && pEconItemDefinition->GetItemClass() && !Q_stricmp( pEconItemDefinition->GetItemClass(), "supply_crate" ) && !pEconItemDefinition->GetCollectionReference() )
  839. {
  840. // It's a crate, find a series #
  841. uint32 unSupplyCrateSeries;
  842. float fSupplyCrateSeries;
  843. if ( FindAttribute_UnsafeBitwiseCast<attrib_value_t>( pEconItem, pAttrDef_SupplyCrateSeries, &fSupplyCrateSeries ) && fSupplyCrateSeries != 0.0f )
  844. {
  845. unSupplyCrateSeries = fSupplyCrateSeries;
  846. loc_scpy_safe( szLocalizedCrateSeries,
  847. CConstructLocalizedString( pLocalizationProvider->Find( "ItemNameCraftSeries" ),
  848. unSupplyCrateSeries ) );
  849. }
  850. }
  851. // This is not "crate series number"; this is "release series number", ie., "a series 3 chemistry kit".
  852. static CSchemaAttributeDefHandle pAttrDef_SeriesNumber( "series number" );
  853. if ( pAttrDef_SeriesNumber )
  854. {
  855. float flSeriesNumber = 0.0f;
  856. if ( FindAttribute_UnsafeBitwiseCast<attrib_value_t>( pEconItem, pAttrDef_SeriesNumber, &flSeriesNumber ) && flSeriesNumber != 0.0f )
  857. {
  858. uint32 unSeriesNumber = flSeriesNumber;
  859. loc_scpy_safe( szLocalizedCrateSeries,
  860. CConstructLocalizedString( pLocalizationProvider->Find( "ItemNameCraftSeries" ),
  861. unSeriesNumber ) );
  862. }
  863. }
  864. #endif
  865. // Were we one of the first couple that were crafted? If so, output our craft number as well.
  866. locchar_t *pCraftNumberLocFormat = pLocalizationProvider->Find( "ItemNameCraftNumberFormat" );
  867. enum { kLocalizedCraftIndexLength = 128, };
  868. locchar_t szLocalizedCraftIndex[ kLocalizedCraftIndexLength ] = LOCCHAR("");
  869. if ( pCraftNumberLocFormat )
  870. {
  871. static CSchemaAttributeDefHandle pAttrDef_UniqueCraftIndex( "unique craft index" );
  872. uint32 unCraftIndex;
  873. if ( pEconItem->FindAttribute( pAttrDef_UniqueCraftIndex, &unCraftIndex ) &&
  874. ShouldDisplayCraftCounterValue( unCraftIndex ) )
  875. {
  876. locchar_t szCraftNumber[ kLocalizedCraftIndexLength ];
  877. loc_sprintf_safe( szCraftNumber, LOCCHAR( "%i" ), unCraftIndex );
  878. ILocalize::ConstructString_safe( szLocalizedCraftIndex,
  879. pCraftNumberLocFormat,
  880. 1,
  881. szCraftNumber );
  882. }
  883. }
  884. // Generate tool application string
  885. enum { kToolApplicationNameLength = 128, };
  886. locchar_t szToolTargetNameName[ kToolApplicationNameLength ] = LOCCHAR("");
  887. locchar_t szDynamicRecipeOutputName[ kToolApplicationNameLength ] = LOCCHAR("");
  888. static CSchemaAttributeDefHandle pAttribDef_ToolTarget( "tool target item" );
  889. if( pAttribDef_ToolTarget && pEconItem->GetItemDefinition()->GetItemClass() && !Q_stricmp( pEconItem->GetItemDefinition()->GetItemClass(), "tool" ) )
  890. {
  891. // It's a tool, see if it has a tool target item attribute
  892. float flItemDef;
  893. if ( FindAttribute_UnsafeBitwiseCast<attrib_value_t>( pEconItem, pAttribDef_ToolTarget, &flItemDef ) )
  894. {
  895. const item_definition_index_t unItemDef = flItemDef;
  896. locchar_t szTargetItemName[ MAX_ITEM_NAME_LENGTH ] = LOCCHAR("Unknown Item");
  897. // Get base name of target item
  898. const CEconItemDefinition *pEconTargetDef = GetItemSchema()->GetItemDefinition( unItemDef );
  899. if ( pEconTargetDef )
  900. {
  901. GetLocalizedBaseItemName( szTargetItemName, pLocalizationProvider, pEconTargetDef );
  902. }
  903. loc_scpy_safe( szToolTargetNameName,
  904. CConstructLocalizedString( pLocalizationProvider->Find( "ItemNameToolTargetNameFormat" ),
  905. szTargetItemName ) );
  906. }
  907. else
  908. {
  909. CRecipeComponentMatchingIterator componentIterator( pEconItem, NULL );
  910. pEconItem->IterateAttributes( &componentIterator );
  911. // It only makes sense to mention the output if there's only 1 output
  912. if( componentIterator.GetMatchingComponentOutputs().Count() == 1 )
  913. {
  914. CAttribute_DynamicRecipeComponent attribValue;
  915. pEconItem->FindAttribute( componentIterator.GetMatchingComponentOutputs().Head(), &attribValue );
  916. CEconItem tempItem;
  917. if ( !DecodeItemFromEncodedAttributeString( attribValue, &tempItem ) )
  918. {
  919. AssertMsg2( 0, "%s: Unable to decode dynamic recipe output attribute on item %llu.", __FUNCTION__, pEconItem->GetID() );
  920. }
  921. else
  922. {
  923. locchar_t loc_ItemName[MAX_ITEM_NAME_LENGTH];
  924. GenerateLocalizedFullItemName( loc_ItemName, pLocalizationProvider, &tempItem, k_EGenerateLocalizedFullItemName_Default, false );
  925. loc_scpy_safe( szDynamicRecipeOutputName,
  926. CConstructLocalizedString( pLocalizationProvider->Find( "ItemNameDynamicRecipeTargetNameFormat" ),
  927. loc_ItemName ) );
  928. }
  929. }
  930. }
  931. }
  932. // PaintKit and Wear
  933. if ( !bHasCustomName )
  934. {
  935. CEconItemPaintKitDefinition *pPaintKit = pEconItem->GetCustomPainkKitDefinition();
  936. if ( pPaintKit )
  937. {
  938. if ( bIgnoreNameWithPaintkit )
  939. {
  940. Econ_SetNameAsPaintkit( szItemName, pLocalizationProvider, pPaintKit );
  941. }
  942. else
  943. {
  944. Econ_ConcatPaintKitName( szItemName, pLocalizationProvider, pPaintKit );
  945. if ( !bIgnoreQualityAndWear )
  946. {
  947. float flWear = 0;
  948. if ( pEconItem->GetCustomPaintKitWear( flWear ) )
  949. {
  950. Econ_ConcatPaintKitWear( szItemName, pLocalizationProvider, flWear );
  951. }
  952. }
  953. }
  954. }
  955. }
  956. locchar_t *pNameLocalizationFormat = pLocalizationProvider->Find( pszLocalizationPattern );
  957. if ( pNameLocalizationFormat )
  958. {
  959. ILocalize::ConstructString_safe( out_pItemName,
  960. pNameLocalizationFormat,
  961. 6,
  962. szQuality,
  963. szItemName,
  964. szLocalizedCraftIndex,
  965. szLocalizedCrateSeries,
  966. szToolTargetNameName,
  967. szDynamicRecipeOutputName);
  968. }
  969. else
  970. {
  971. loc_scpy_safe( out_pItemName, s_pUnknownItemName );
  972. }
  973. }
  974. // --------------------------------------------------------------------------
  975. // Purpose:
  976. // --------------------------------------------------------------------------
  977. void CEconItemDescription::Generate_ItemName( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem )
  978. {
  979. Assert( pLocalizationProvider );
  980. Assert( pEconItem );
  981. // If this item has a custom name, use it instead of doing our crazy name compositing based on quality,
  982. // type, etc.
  983. const char *utf8_CustomName = pEconItem->GetCustomName();
  984. if ( utf8_CustomName && utf8_CustomName[0] )
  985. {
  986. locchar_t loc_CustomName[ MAX_ITEM_NAME_LENGTH ];
  987. pLocalizationProvider->ConvertUTF8ToLocchar( utf8_CustomName, loc_CustomName, sizeof( loc_CustomName ) );
  988. // Store it in the item name, wrapped in quotes to prevent item name spoofing
  989. // We use two single quotes, because the double quote isn't very visible in the TF2 font
  990. locchar_t loc_CustomNameWithQuotes[ MAX_ITEM_NAME_LENGTH ];
  991. loc_scpy_safe( loc_CustomNameWithQuotes, LOCCHAR("''") );
  992. loc_scat_safe( loc_CustomNameWithQuotes, loc_CustomName );
  993. loc_scat_safe( loc_CustomNameWithQuotes, LOCCHAR("''") );
  994. AddDescLine( loc_CustomNameWithQuotes, /* this will be ignored: */ ATTRIB_COL_LEVEL, kDescLineFlag_Name );
  995. }
  996. else
  997. {
  998. locchar_t loc_ItemName[MAX_ITEM_NAME_LENGTH];
  999. GenerateLocalizedFullItemName( loc_ItemName, pLocalizationProvider, pEconItem, k_EGenerateLocalizedFullItemName_WithPaintkitNoItem, m_pHashContext == NULL );
  1000. AddDescLine( loc_ItemName, /* this will be ignored: */ ATTRIB_COL_LEVEL, kDescLineFlag_Name );
  1001. }
  1002. }
  1003. // --------------------------------------------------------------------------
  1004. // Purpose:
  1005. // --------------------------------------------------------------------------
  1006. const locchar_t *GetLocalizedStringForKillEaterTypeAttr( const CLocalizationProvider *pLocalizationProvider, uint32 unKillEaterEventType )
  1007. {
  1008. Assert( pLocalizationProvider );
  1009. // Generate localized string.
  1010. const char *pszLocString = GetItemSchema()->GetKillEaterScoreTypeLocString( unKillEaterEventType );
  1011. return pszLocString != NULL
  1012. ? pLocalizationProvider->Find( pszLocString )
  1013. : LOCCHAR("");
  1014. }
  1015. class CStrangeRestrictionAttrWrapper
  1016. {
  1017. public:
  1018. CStrangeRestrictionAttrWrapper( const CLocalizationProvider *pLocalizationProvider, const locchar_t *loc_In )
  1019. : m_str( loc_In ? pLocalizationProvider->Find( "ItemTypeDescStrangeFilterSubStr" ) : LOCCHAR(""), loc_In ? loc_In : LOCCHAR("") )
  1020. {
  1021. //
  1022. }
  1023. const locchar_t *operator *() const
  1024. {
  1025. return static_cast<const locchar_t *>( m_str );
  1026. }
  1027. private:
  1028. CConstructLocalizedString m_str;
  1029. };
  1030. const locchar_t *CEconItemDescription::GetLocalizedStringForStrangeRestrictionAttr( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem, int iAttrIndex ) const
  1031. {
  1032. uint32 unRestrictionType;
  1033. uint32 unRestrictionValue;
  1034. if ( !pEconItem->FindAttribute( GetKillEaterAttr_Restriction( iAttrIndex ), &unRestrictionType ) ||
  1035. !pEconItem->FindAttribute( GetKillEaterAttr_RestrictionValue( iAttrIndex ), &unRestrictionValue ) ||
  1036. unRestrictionType == kStrangeEventRestriction_None )
  1037. {
  1038. return NULL;
  1039. }
  1040. switch ( unRestrictionType )
  1041. {
  1042. #ifdef PROJECT_TF
  1043. case kStrangeEventRestriction_Map:
  1044. {
  1045. const MapDef_t *pMap = GetItemSchema()->GetMasterMapDefByIndex( unRestrictionValue );
  1046. if ( pMap )
  1047. return pLocalizationProvider->Find( pMap->pszMapNameLocKey );
  1048. }
  1049. case kStrangeEventRestriction_Competitive:
  1050. {
  1051. return pLocalizationProvider->Find( "ItemTypeDescStrangeFilterCompetitive" );
  1052. }
  1053. #endif // PROJECT_TF
  1054. case kStrangeEventRestriction_VictimSteamAccount:
  1055. return FindAccountPersonaName( unRestrictionValue );
  1056. }
  1057. return NULL;
  1058. }
  1059. bool CEconItemDescription::BGenerate_ItemLevelDesc_StrangeNameAndStats( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem, const locchar_t *locTypename )
  1060. {
  1061. CStrangeRankLocalizationGenerator RankGenerator( pLocalizationProvider, pEconItem, m_pHashContext == NULL );
  1062. if ( !RankGenerator.IsValid() )
  1063. return false;
  1064. // For Collection Items
  1065. if ( pEconItem->GetCustomPainkKitDefinition() )
  1066. {
  1067. AddDescLine( CConstructLocalizedString( pLocalizationProvider->Find( "Attrib_stattrakmodule" ), RankGenerator.GetRankLocalized() ),
  1068. ATTRIB_COL_STRANGE,
  1069. kDescLineFlag_Misc
  1070. );
  1071. // Are we tracking alternate stats as well?
  1072. for ( int i = 0; i < GetKillEaterAttrCount(); i++ )
  1073. {
  1074. const CEconItemAttributeDefinition *pKillEaterAltAttrDef = GetKillEaterAttr_Score( i ),
  1075. *pKillEaterAltScoreTypeAttrDef = GetKillEaterAttr_Type( i );
  1076. if ( !pKillEaterAltAttrDef || !pKillEaterAltScoreTypeAttrDef )
  1077. continue;
  1078. uint32 unKillEaterAltScore;
  1079. if ( !pEconItem->FindAttribute( pKillEaterAltAttrDef, &unKillEaterAltScore ) )
  1080. continue;
  1081. // Older items can optionally not specify a type attribute at all and have an implicit "I'm tracking
  1082. // kills" zeroth attribute. We require a score type for any slot besides that.
  1083. if ( i != 0 && !pEconItem->FindAttribute( pKillEaterAltScoreTypeAttrDef ) )
  1084. continue;
  1085. const uint32 unKillEaterAltType = GetScoreTypeForKillEaterAttr( pEconItem, pKillEaterAltScoreTypeAttrDef );
  1086. AddDescLine( CConstructLocalizedString( pLocalizationProvider->Find( "ItemTypeDescKillEaterAltv2" ),
  1087. unKillEaterAltScore,
  1088. GetLocalizedStringForKillEaterTypeAttr( pLocalizationProvider, unKillEaterAltType ),
  1089. *CStrangeRestrictionAttrWrapper( pLocalizationProvider, GetLocalizedStringForStrangeRestrictionAttr( pLocalizationProvider, pEconItem, i ) ) ),
  1090. ATTRIB_COL_LEVEL,
  1091. kDescLineFlag_Misc ); // strange item scores past the first are not considered part of the type
  1092. }
  1093. return true;
  1094. } // End Collection Items
  1095. // Normal old way
  1096. // Look for Limited Item Attr
  1097. bool bLimitedQuantity = false;
  1098. static CSchemaAttributeDefHandle pAttrDef_LimitedQuantityItem( "limited quantity item" );
  1099. bLimitedQuantity = pEconItem->FindAttribute( pAttrDef_LimitedQuantityItem );
  1100. AddDescLine( CConstructLocalizedString( pLocalizationProvider->Find( "ItemTypeDescKillEater" ),
  1101. RankGenerator.GetRankLocalized(),
  1102. locTypename ? locTypename : LOCCHAR(""),
  1103. RankGenerator.GetStrangeScore(),
  1104. GetLocalizedStringForKillEaterTypeAttr( pLocalizationProvider, RankGenerator.GetStrangeType() ),
  1105. *CStrangeRestrictionAttrWrapper( pLocalizationProvider, GetLocalizedStringForStrangeRestrictionAttr( pLocalizationProvider, pEconItem, RankGenerator.GetUsedStrangeSlot() ) ),
  1106. RankGenerator.GetRankSecondaryLocalized() ? RankGenerator.GetRankSecondaryLocalized() : LOCCHAR(""),
  1107. bLimitedQuantity ? pLocalizationProvider->Find( "LimitedQualityDesc" ) : LOCCHAR("")
  1108. ),
  1109. bLimitedQuantity ? ATTRIB_COL_LIMITED_QUANTITY : ATTRIB_COL_LEVEL,
  1110. kDescLineFlag_Type );
  1111. // Are we tracking alternate stats as well?
  1112. for ( int i = 0; i < GetKillEaterAttrCount(); i++ )
  1113. {
  1114. const CEconItemAttributeDefinition *pKillEaterAltAttrDef = GetKillEaterAttr_Score(i),
  1115. *pKillEaterAltScoreTypeAttrDef = GetKillEaterAttr_Type(i);
  1116. if ( !pKillEaterAltAttrDef || !pKillEaterAltScoreTypeAttrDef )
  1117. continue;
  1118. uint32 unKillEaterAltScore;
  1119. if ( !pEconItem->FindAttribute( pKillEaterAltAttrDef, &unKillEaterAltScore ) )
  1120. continue;
  1121. // Older items can optionally not specify a type attribute at all and have an implicit "I'm tracking
  1122. // kills" zeroth attribute. We require a score type for any slot besides that.
  1123. if ( i != 0 && !pEconItem->FindAttribute( pKillEaterAltScoreTypeAttrDef ) )
  1124. continue;
  1125. const uint32 unKillEaterAltType = GetScoreTypeForKillEaterAttr( pEconItem, pKillEaterAltScoreTypeAttrDef );
  1126. // Skip if this is our primary stat and we already output it above.
  1127. if ( unKillEaterAltType == RankGenerator.GetStrangeType() )
  1128. continue;
  1129. AddDescLine( CConstructLocalizedString( pLocalizationProvider->Find( "ItemTypeDescKillEaterAlt" ),
  1130. unKillEaterAltScore,
  1131. GetLocalizedStringForKillEaterTypeAttr( pLocalizationProvider, unKillEaterAltType ),
  1132. *CStrangeRestrictionAttrWrapper( pLocalizationProvider, GetLocalizedStringForStrangeRestrictionAttr( pLocalizationProvider, pEconItem, i ) ) ),
  1133. ATTRIB_COL_LEVEL,
  1134. kDescLineFlag_Misc ); // strange item scores past the first are not considered part of the type
  1135. }
  1136. return true;
  1137. }
  1138. // --------------------------------------------------------------------------
  1139. // Purpose:
  1140. // --------------------------------------------------------------------------
  1141. uint32 GetItemDescriptionDisplayLevel( const IEconItemInterface *pEconItem )
  1142. {
  1143. Assert( pEconItem );
  1144. static CSchemaAttributeDefHandle pAttrDef_WideItemLevel( "wide item level" );
  1145. uint32 unWideLevelValue;
  1146. if ( pEconItem->FindAttribute( pAttrDef_WideItemLevel, &unWideLevelValue ) )
  1147. return unWideLevelValue;
  1148. return pEconItem->GetItemLevel();
  1149. }
  1150. void CEconItemDescription::Generate_ItemLevelDesc_Default( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem, const locchar_t *locTypename )
  1151. {
  1152. // By default, items will only show the level if there is an item type to go along with it.
  1153. // Combined, these will build a string like "Level 10 Shotgun". We allow a custom attribute
  1154. // to force the level to be displayed by itself even if there is no item class ("Level 10").
  1155. static CSchemaAttributeDefHandle pAttrDef_ForceLevelDisplay( "force_level_display" );
  1156. item_definition_index_t usDefIndex = pEconItem->GetItemDefIndex();
  1157. #ifdef CLIENT_DLL
  1158. const bool bIsStoreItem = IsStorePreviewItem( pEconItem );
  1159. const bool bIsPreviewItem = pEconItem->GetFlags() & kEconItemFlagClient_Preview;
  1160. // If the item doesn't have a valid itemID, we'll just use the locTypename for the item level description.
  1161. // We don't want to display "Level 0 Hat" in places like the Mann Co. Store and Armory. We'll just display "Hat".
  1162. if ( bIsStoreItem || bIsPreviewItem || pEconItem->GetItemDefinition()->GetRarity() != k_unItemRarity_Any )
  1163. {
  1164. if ( locTypename && *locTypename )
  1165. {
  1166. AddDescLine( locTypename, ATTRIB_COL_LEVEL, kDescLineFlag_Type, NULL, usDefIndex );
  1167. }
  1168. return;
  1169. }
  1170. #endif
  1171. float fForceLevelDisplayValue;
  1172. bool bForceLevelDisplay = FindAttribute_UnsafeBitwiseCast<attrib_value_t>( pEconItem, pAttrDef_ForceLevelDisplay, &fForceLevelDisplayValue )
  1173. && fForceLevelDisplayValue > 0.0f;
  1174. if ( ( locTypename && *locTypename ) || bForceLevelDisplay )
  1175. {
  1176. if ( locTypename )
  1177. {
  1178. // How are we going to format our level number and base type string?
  1179. const locchar_t *pszFormatString = NULL;
  1180. #ifdef PROJECT_TF
  1181. static CSchemaAttributeDefHandle pAttrDef_OverrideItemLevelDescString( CTFItemSchema::k_rchOverrideItemLevelDescStringAttribName );
  1182. static const char *s_pszCustomItemLevelDescLocalizationTokens[] =
  1183. {
  1184. "ItemTypeDescCustomLevelString_MvMTour",
  1185. };
  1186. // ...are we going to use a custom format string specified in an attribute?
  1187. uint32 unOverrideItemLevelDescString = 0;
  1188. if ( pEconItem->FindAttribute( pAttrDef_OverrideItemLevelDescString, &unOverrideItemLevelDescString )
  1189. && unOverrideItemLevelDescString != 0
  1190. && unOverrideItemLevelDescString <= ARRAYSIZE( s_pszCustomItemLevelDescLocalizationTokens ) )
  1191. {
  1192. const char *pszLevelLocalizationToken = s_pszCustomItemLevelDescLocalizationTokens[ unOverrideItemLevelDescString - 1 ];
  1193. Assert( pszLevelLocalizationToken );
  1194. pszFormatString = pLocalizationProvider->Find( pszLevelLocalizationToken );
  1195. }
  1196. #endif // PROJECT_TF
  1197. // Either we didn't have a custom override attribute, or we did and it had an invalid value, or it had a valid
  1198. // value but the localization system failed to find something for that key. In any event, we fall back to our default
  1199. // format string here.
  1200. if ( pszFormatString == NULL )
  1201. {
  1202. bool bLimitedQuantity = false;
  1203. static CSchemaAttributeDefHandle pAttrDef_LimitedQuantityItem( "limited quantity item" );
  1204. bLimitedQuantity = pEconItem->FindAttribute( pAttrDef_LimitedQuantityItem );
  1205. #if defined( TF_CLIENT_DLL )
  1206. if ( pEconItem->GetItemDefinition()->GetItemClass() && V_strcmp( pEconItem->GetItemDefinition()->GetItemClass(), "map_token" ) == 0 )
  1207. {
  1208. // For map stamps on the client we can show how many hours they've played each map
  1209. // And how many times they've donated to it instead of the generic "level"
  1210. for ( int i = 0; i < GetItemSchema()->GetMapCount(); i++ )
  1211. {
  1212. const MapDef_t* pMap = GetItemSchema()->GetMasterMapDefByIndex( i );
  1213. if ( pMap->mapStampDef != pEconItem->GetItemDefinition() )
  1214. continue;
  1215. int nItemLevel = MapInfo_GetDonationAmount( steamapicontext->SteamUser()->GetSteamID().GetAccountID(), pMap->pszMapName );
  1216. MapStats_t &mapStats = GetMapStats( pMap->GetStatsIdentifier() );
  1217. int nNumHours = ( mapStats.accumulated.m_iStat[TFMAPSTAT_PLAYTIME] ) / ( 60 /*sec*/ * 60 /*min*/ );
  1218. AddDescLine( CConstructLocalizedString( pLocalizationProvider->Find( "ItemTypeDescCustomLevelString_MapStamp" ), (uint32)nItemLevel, (uint32)nNumHours ), ATTRIB_COL_LEVEL, kDescLineFlag_Type );
  1219. return;
  1220. }
  1221. }
  1222. else
  1223. #endif
  1224. {
  1225. if ( bLimitedQuantity )
  1226. {
  1227. // Limited Item Description
  1228. pszFormatString = pLocalizationProvider->Find( "ItemTypeDescLimited" );
  1229. AddDescLine( CConstructLocalizedString(
  1230. pszFormatString,
  1231. GetItemDescriptionDisplayLevel( pEconItem ),
  1232. locTypename,
  1233. pLocalizationProvider->Find( "LimitedQualityDesc" ) ),
  1234. ATTRIB_COL_LIMITED_QUANTITY,
  1235. kDescLineFlag_Type,
  1236. NULL,
  1237. usDefIndex
  1238. );
  1239. return;
  1240. }
  1241. pszFormatString = pLocalizationProvider->Find( "ItemTypeDesc" );
  1242. }
  1243. }
  1244. // If we still don't have a format string here, it means our default also failed, but CConstructLocalizedString will
  1245. // handle that safely.
  1246. AddDescLine( CConstructLocalizedString( pszFormatString, GetItemDescriptionDisplayLevel( pEconItem ), locTypename ), ATTRIB_COL_LEVEL, kDescLineFlag_Type, NULL, usDefIndex );
  1247. }
  1248. else
  1249. {
  1250. Assert( bForceLevelDisplay );
  1251. AddDescLine( CConstructLocalizedString( pLocalizationProvider->Find( "ItemTypeDescNoLevel" ), GetItemDescriptionDisplayLevel( pEconItem ) ), ATTRIB_COL_LEVEL, kDescLineFlag_Type, NULL, usDefIndex );
  1252. }
  1253. }
  1254. }
  1255. // --------------------------------------------------------------------------
  1256. // Purpose:
  1257. // --------------------------------------------------------------------------
  1258. void CEconItemDescription::Generate_ItemLevelDesc( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem )
  1259. {
  1260. Assert( pLocalizationProvider );
  1261. Assert( pEconItem );
  1262. const GameItemDefinition_t *pItemDef = pEconItem->GetItemDefinition();
  1263. if ( !pItemDef )
  1264. return;
  1265. const locchar_t *locTypename = pLocalizationProvider->Find( pItemDef->GetItemTypeName() );
  1266. // Kill-eating weapons replace the standard weapon name/level line with a label
  1267. // describing the current class of the item instead of the level. This overrides
  1268. // even "force_level_display".
  1269. if ( BGenerate_ItemLevelDesc_StrangeNameAndStats( pLocalizationProvider, pEconItem, locTypename ) )
  1270. return;
  1271. // Not strange, but if you are paint kitted or have a collection reference dont create this
  1272. if ( pEconItem->GetCustomPainkKitDefinition() || pItemDef->GetCollectionReference() )
  1273. return;
  1274. // If we didn't generate a fancy strange name, we fall back to our default behavior.
  1275. Generate_ItemLevelDesc_Default( pLocalizationProvider, pEconItem, locTypename );
  1276. }
  1277. #if defined( STAGING_ONLY ) && defined( CLIENT_DLL )
  1278. 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.)." );
  1279. // --------------------------------------------------------------------------
  1280. // Purpose:
  1281. // --------------------------------------------------------------------------
  1282. void CEconItemDescription::Generate_DebugInformation( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem )
  1283. {
  1284. Assert( pLocalizationProvider );
  1285. Assert( pEconItem );
  1286. if ( !econ_include_debug_item_description.GetBool() )
  1287. return;
  1288. #if TF_ANTI_IDLEBOT_VERIFICATION
  1289. // Adding these extra description lines would mess with our GC/client sync.
  1290. if ( m_pHashContext )
  1291. return;
  1292. #endif // TF_ANTI_IDLEBOT_VERIFICATION
  1293. AddDescLine( CConstructLocalizedString( LOCCHAR("([ Item ID: %s1 ])"), pEconItem->GetID() ), ATTRIB_COL_POSITIVE, kDescLineFlag_Misc );
  1294. AddDescLine( CConstructLocalizedString( LOCCHAR("([ Item Definition Index: %s1 ])"), (uint32)pEconItem->GetItemDefIndex() ), ATTRIB_COL_POSITIVE, kDescLineFlag_Misc );
  1295. AddDescLine( CConstructLocalizedString( LOCCHAR("([ In Use?: %s1 ])"), pEconItem->GetInUse() ? LOCCHAR("true") : LOCCHAR("false") ), ATTRIB_COL_POSITIVE, kDescLineFlag_Misc );
  1296. AddEmptyDescLine();
  1297. class CDebugAttributeDisplayer : public IEconItemAttributeIterator
  1298. {
  1299. public:
  1300. CDebugAttributeDisplayer( CEconItemDescription *pOut_Desc ) : m_pDesc( pOut_Desc ) { Assert( m_pDesc ); }
  1301. virtual bool OnIterateAttributeValue( const CEconItemAttributeDefinition *pAttrDef, attrib_value_t value ) OVERRIDE
  1302. {
  1303. // Ugh.
  1304. wchar_t wszAttrDef[ 256 ];
  1305. ILocalize::ConvertANSIToUnicode( pAttrDef->GetDefinitionName(), &wszAttrDef[0], sizeof( wszAttrDef ) );
  1306. m_pDesc->AddDescLine( CConstructLocalizedString( LOCCHAR("([ Attribute [%s1]: '%s2' ( %s3 | %s4 ) ])"),
  1307. (uint32)pAttrDef->GetDefinitionIndex(),
  1308. wszAttrDef,
  1309. *(uint32 *)&value,
  1310. *(float *)&value ), ATTRIB_COL_POSITIVE, kDescLineFlag_Misc );
  1311. return true;
  1312. }
  1313. virtual bool OnIterateAttributeValue( const CEconItemAttributeDefinition *pAttrDef, float value ) OVERRIDE
  1314. {
  1315. // Ugh.
  1316. wchar_t wszAttrDef[ 256 ];
  1317. ILocalize::ConvertANSIToUnicode( pAttrDef->GetDefinitionName(), &wszAttrDef[0], sizeof( wszAttrDef ) );
  1318. m_pDesc->AddDescLine( CConstructLocalizedString( LOCCHAR("([ Attribute [%s1]: '%s2' ( %f ) ])"),
  1319. (uint32)pAttrDef->GetDefinitionIndex(),
  1320. wszAttrDef,
  1321. value ), ATTRIB_COL_POSITIVE, kDescLineFlag_Misc );
  1322. return true;
  1323. }
  1324. virtual bool OnIterateAttributeValue( const CEconItemAttributeDefinition *pAttrDef, const uint64& value ) OVERRIDE
  1325. {
  1326. // Ugh.
  1327. wchar_t wszAttrDef[ 256 ];
  1328. ILocalize::ConvertANSIToUnicode( pAttrDef->GetDefinitionName(), &wszAttrDef[0], sizeof( wszAttrDef ) );
  1329. m_pDesc->AddDescLine( CConstructLocalizedString( LOCCHAR("([ Attribute [%s1]: '%s2' ( %llu ) ])"),
  1330. (uint32)pAttrDef->GetDefinitionIndex(),
  1331. wszAttrDef,
  1332. value ), ATTRIB_COL_POSITIVE, kDescLineFlag_Misc );
  1333. return true;
  1334. }
  1335. virtual bool OnIterateAttributeValue( const CEconItemAttributeDefinition *pAttrDef, const CAttribute_String& value ) OVERRIDE
  1336. {
  1337. // Ugh.
  1338. wchar_t wszAttrDef[ 256 ];
  1339. ILocalize::ConvertANSIToUnicode( pAttrDef->GetDefinitionName(), &wszAttrDef[0], sizeof( wszAttrDef ) );
  1340. wchar_t wszAttrContents[ 1024 ];
  1341. ILocalize::ConvertANSIToUnicode( value.value().c_str(), &wszAttrContents[0], sizeof( wszAttrContents ) );
  1342. m_pDesc->AddDescLine( CConstructLocalizedString( LOCCHAR("([ Attribute [%s1]: '%s2' ( \"%s3\" ) ])"),
  1343. (uint32)pAttrDef->GetDefinitionIndex(),
  1344. wszAttrDef,
  1345. wszAttrContents ), ATTRIB_COL_POSITIVE, kDescLineFlag_Misc );
  1346. return true;
  1347. }
  1348. virtual bool OnIterateAttributeValue( const CEconItemAttributeDefinition *pAttrDef, const CAttribute_DynamicRecipeComponent& value ) OVERRIDE
  1349. {
  1350. const char* pszItemName = GetItemSchema()->GetItemDefinition( value.def_index() )->GetItemBaseName();
  1351. wchar_t wszItemQuality[ 256 ];
  1352. ILocalize::ConvertANSIToUnicode( EconQuality_GetQualityString(EEconItemQuality(value.item_quality())), &wszItemQuality[0], sizeof( wszItemQuality ) );
  1353. wchar_t wszAttrString[ 256 ];
  1354. ILocalize::ConvertANSIToUnicode( value.attributes_string().c_str(), &wszAttrString[0], sizeof( wszAttrString ) );
  1355. wchar_t wszCount[ 64 ];
  1356. ILocalize::ConvertANSIToUnicode( CFmtStr( "%d/%d", value.num_fulfilled(), value.num_required() ), &wszCount[0], sizeof( wszCount ) );
  1357. locchar_t wszLocalizedItemName[ 128 ];
  1358. const locchar_t *pLocalizedItemName = GLocalizationProvider()->Find( pszItemName );
  1359. if ( pLocalizedItemName )
  1360. {
  1361. V_wcscpy_safe( wszLocalizedItemName, pLocalizedItemName );
  1362. }
  1363. else
  1364. {
  1365. // name wasn't found by Find(), so just convert the pszItemName
  1366. ILocalize::ConvertANSIToUnicode( pszItemName, &wszLocalizedItemName[0], sizeof( wszLocalizedItemName ) );
  1367. }
  1368. m_pDesc->AddDescLine( CConstructLocalizedString( LOCCHAR( "\nItem: \"%s1\"\nQuality: %s2\nAttribs:%s3\nCount: %s4" ),
  1369. wszLocalizedItemName,
  1370. wszItemQuality,
  1371. wszAttrString,
  1372. wszCount ),
  1373. ATTRIB_COL_POSITIVE, kDescLineFlag_Misc );
  1374. return true;
  1375. }
  1376. virtual bool OnIterateAttributeValue( const CEconItemAttributeDefinition *pAttrDef, const CAttribute_ItemSlotCriteria& value ) OVERRIDE
  1377. {
  1378. // Ugh.
  1379. wchar_t wszAttrDef[ 256 ];
  1380. ILocalize::ConvertANSIToUnicode( pAttrDef->GetDefinitionName(), &wszAttrDef[0], sizeof( wszAttrDef ) );
  1381. wchar_t wszAttrTags[ 256 ];
  1382. ILocalize::ConvertANSIToUnicode( value.tags().c_str(), &wszAttrTags[0], sizeof( wszAttrTags ) );
  1383. m_pDesc->AddDescLine( CConstructLocalizedString( LOCCHAR("([ Attribute [%s1]: '%s2'])\nTags: \"%s3\""),
  1384. (uint32)pAttrDef->GetDefinitionIndex(),
  1385. wszAttrDef,
  1386. wszAttrTags ),
  1387. ATTRIB_COL_POSITIVE, kDescLineFlag_Misc );
  1388. return true;
  1389. }
  1390. virtual bool OnIterateAttributeValue( const CEconItemAttributeDefinition *pAttrDef, const CAttribute_WorldItemPlacement& value ) OVERRIDE
  1391. {
  1392. wchar_t wszAttrDef[ 256 ];
  1393. ILocalize::ConvertANSIToUnicode( pAttrDef->GetDefinitionName(), &wszAttrDef[0], sizeof( wszAttrDef ) );
  1394. m_pDesc->AddDescLine( CConstructLocalizedString( LOCCHAR( "([ Attribute [%s1]: '%s2'])\n" ),
  1395. (uint32)pAttrDef->GetDefinitionIndex(),
  1396. wszAttrDef ),
  1397. ATTRIB_COL_POSITIVE, kDescLineFlag_Misc );
  1398. return true;
  1399. }
  1400. private:
  1401. CEconItemDescription *m_pDesc;
  1402. };
  1403. CDebugAttributeDisplayer DebugAttributeDisplayer( this );
  1404. pEconItem->IterateAttributes( &DebugAttributeDisplayer );
  1405. }
  1406. #endif // defined( STAGING_ONLY ) && defined( CLIENT_DLL )
  1407. // --------------------------------------------------------------------------
  1408. // Purpose:
  1409. // --------------------------------------------------------------------------
  1410. void CEconItemDescription::Generate_CraftTag( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem )
  1411. {
  1412. Assert( pLocalizationProvider );
  1413. Assert( pEconItem );
  1414. static CSchemaAttributeDefHandle pAttribDef_MakersMarkId( "makers mark id" );
  1415. attrib_value_t value;
  1416. if ( !pEconItem->FindAttribute( pAttribDef_MakersMarkId, &value ) )
  1417. return;
  1418. AddAttributeDescription( pLocalizationProvider, pAttribDef_MakersMarkId, value );
  1419. }
  1420. // --------------------------------------------------------------------------
  1421. // Purpose:
  1422. // --------------------------------------------------------------------------
  1423. void CEconItemDescription::Generate_StyleDesc( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem )
  1424. {
  1425. Assert( pLocalizationProvider );
  1426. Assert( pEconItem );
  1427. const GameItemDefinition_t *pItemDef = pEconItem->GetItemDefinition();
  1428. if ( !pItemDef )
  1429. return;
  1430. const CEconStyleInfo *pStyle = pItemDef->GetStyleInfo( pEconItem->GetStyle() );
  1431. if ( !pStyle )
  1432. return;
  1433. const locchar_t *loc_StyleName = pLocalizationProvider->Find( pStyle->GetName() );
  1434. if ( !loc_StyleName )
  1435. return;
  1436. AddDescLine( CConstructLocalizedString( pLocalizationProvider->Find( "#Econ_Style_Desc" ), loc_StyleName ), ATTRIB_COL_LEVEL, kDescLineFlag_Misc );
  1437. }
  1438. //-----------------------------------------------------------------------------
  1439. // Purpose:
  1440. //-----------------------------------------------------------------------------
  1441. void CEconItemDescription::Generate_HolidayRestriction( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem )
  1442. {
  1443. Assert( pLocalizationProvider );
  1444. Assert( pEconItem );
  1445. const GameItemDefinition_t *pItemDef = pEconItem->GetItemDefinition();
  1446. if ( !pItemDef )
  1447. return;
  1448. const char *pszHolidayRestriction = pItemDef->GetHolidayRestriction();
  1449. if ( !pszHolidayRestriction )
  1450. return;
  1451. // Report any special restrictions. We'll output in a different color depending on whether or not
  1452. // the restriction currently prevents the item from showing up.
  1453. LocalizedAddDescLine( pLocalizationProvider,
  1454. CFmtStr( "Econ_holiday_restriction_%s", pszHolidayRestriction ).Access(),
  1455. EconHolidays_IsHolidayActive( EconHolidays_GetHolidayForString( pszHolidayRestriction ), CRTime::RTime32TimeCur() ) ? ATTRIB_COL_LEVEL : ATTRIB_COL_NEGATIVE,
  1456. kDescLineFlag_Misc );
  1457. }
  1458. //-----------------------------------------------------------------------------
  1459. // Purpose:
  1460. //-----------------------------------------------------------------------------
  1461. void CEconItemDescription::Generate_QualityDesc( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem )
  1462. {
  1463. Assert( pLocalizationProvider );
  1464. Assert( pEconItem );
  1465. // Does this item quality have additional description information that goes along with
  1466. // besides the usual name/coloration changes?
  1467. const char *pszQualityDescLocalizationKey = NULL;
  1468. switch( pEconItem->GetQuality() )
  1469. {
  1470. case AE_SELFMADE:
  1471. pszQualityDescLocalizationKey = "Attrib_Selfmade_Description";
  1472. break;
  1473. case AE_COMMUNITY:
  1474. pszQualityDescLocalizationKey = "Attrib_Community_Description";
  1475. break;
  1476. }
  1477. // We don't need to do anything special.
  1478. if ( !pszQualityDescLocalizationKey )
  1479. return;
  1480. // If this item has a particle system attached but doesn't have the attribute that we usually use
  1481. // to attach particles, we hack it and dump out an extra line to show the particle system description
  1482. // as well.
  1483. static CSchemaAttributeDefHandle pAttrDef_ParticleEffect( "attach particle effect" );
  1484. static attachedparticlesystem_t *pSparkleSystem = GetItemSchema()->FindAttributeControlledParticleSystem( "community_sparkle" );
  1485. // If the schema understands these properties...
  1486. if ( pAttrDef_ParticleEffect && pSparkleSystem )
  1487. {
  1488. // ...and we don't have a real particle effect attribute attribute...
  1489. if ( !pEconItem->FindAttribute( pAttrDef_ParticleEffect ) )
  1490. {
  1491. // check for Unusual Cap def index (1173)
  1492. // We manually assign unusual effect to content author. No community sparkle
  1493. if ( pEconItem->GetItemDefIndex() != 1173 )
  1494. {
  1495. // ...then manually add the description as if we did.
  1496. float flSystemID = pSparkleSystem->nSystemID;
  1497. AddAttributeDescription( pLocalizationProvider, pAttrDef_ParticleEffect, *(uint32*)&flSystemID );
  1498. }
  1499. }
  1500. }
  1501. LocalizedAddDescLine( pLocalizationProvider, pszQualityDescLocalizationKey, ATTRIB_COL_NEUTRAL, kDescLineFlag_Misc );
  1502. }
  1503. //-----------------------------------------------------------------------------
  1504. void CEconItemDescription::Generate_ItemRarityDesc( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem )
  1505. {
  1506. const CEconItemRarityDefinition* pItemRarity = GetItemSchema()->GetRarityDefinition( pEconItem->GetItemDefinition()->GetRarity() );
  1507. if ( !pItemRarity )
  1508. return;
  1509. const GameItemDefinition_t *pItemDef = pEconItem->GetItemDefinition();
  1510. if ( !pItemDef )
  1511. return;
  1512. const char *pszTooltip = "TFUI_InvTooltip_Rarity";
  1513. attrib_colors_t colorRarity = pItemRarity->GetAttribColor();
  1514. const locchar_t *loc_RarityText = pLocalizationProvider->Find( pItemRarity->GetLocKey() );
  1515. const locchar_t *locTypename = pLocalizationProvider->Find( pItemDef->GetItemTypeName() );
  1516. const locchar_t *loc_WearText = LOCCHAR("");
  1517. float flWear = 0;
  1518. if ( pEconItem->GetCustomPaintKitWear( flWear ) )
  1519. {
  1520. loc_WearText = pLocalizationProvider->Find( GetWearLocalizationString( flWear ) );
  1521. }
  1522. else
  1523. {
  1524. pszTooltip = "TFUI_InvTooltip_RarityNoWear";
  1525. }
  1526. AddDescLine( CConstructLocalizedString( pLocalizationProvider->Find( pszTooltip ), loc_RarityText, locTypename, loc_WearText ), colorRarity, kDescLineFlag_Misc );
  1527. }
  1528. //-----------------------------------------------------------------------------
  1529. void CEconItemDescription::Generate_WearAmountDesc( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem )
  1530. {
  1531. if ( pEconItem->GetCustomPainkKitDefinition() == 0 )
  1532. return;
  1533. Assert( pLocalizationProvider );
  1534. Assert( pEconItem );
  1535. float flWear = 0;
  1536. if ( pEconItem->GetCustomPaintKitWear( flWear ) )
  1537. {
  1538. locchar_t loc_WearText[MAX_ATTRIBUTE_DESCRIPTION_LENGTH];
  1539. loc_scpy_safe( loc_WearText, pLocalizationProvider->Find( "#TFUI_InvTooltip_Wear" ) );
  1540. loc_scat_safe( loc_WearText, LOCCHAR( " " ) );
  1541. loc_scat_safe( loc_WearText, pLocalizationProvider->Find( GetWearLocalizationString( flWear ) ) );
  1542. AddDescLine( loc_WearText, ATTRIB_COL_NEUTRAL, kDescLineFlag_Misc );
  1543. }
  1544. }
  1545. //-----------------------------------------------------------------------------
  1546. // Purpose:
  1547. //-----------------------------------------------------------------------------
  1548. void CEconItemDescription::Generate_ItemDesc( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem )
  1549. {
  1550. Assert( pLocalizationProvider );
  1551. Assert( pEconItem );
  1552. // Show the custom description if it has one.
  1553. const char *utf8_CustomDesc = pEconItem->GetCustomDesc();
  1554. if ( utf8_CustomDesc && utf8_CustomDesc[0] )
  1555. {
  1556. locchar_t loc_CustomDesc[ MAX_ITEM_DESC_LENGTH ];
  1557. pLocalizationProvider->ConvertUTF8ToLocchar( utf8_CustomDesc, loc_CustomDesc, sizeof( loc_CustomDesc ) );
  1558. locchar_t loc_CustomDescWithQuotes[ MAX_ITEM_DESC_LENGTH ];
  1559. loc_scpy_safe( loc_CustomDescWithQuotes, LOCCHAR("''") );
  1560. loc_scat_safe( loc_CustomDescWithQuotes, loc_CustomDesc );
  1561. loc_scat_safe( loc_CustomDescWithQuotes, LOCCHAR("''") );
  1562. AddDescLine( loc_CustomDescWithQuotes, ATTRIB_COL_NEUTRAL, kDescLineFlag_Desc );
  1563. return;
  1564. }
  1565. // No custom description -- see if the item has a default description as part of the definition.
  1566. const GameItemDefinition_t *pItemDef = pEconItem->GetItemDefinition();
  1567. if ( !pItemDef )
  1568. return;
  1569. // Add any additional item description
  1570. if ( pItemDef->GetItemDesc() )
  1571. {
  1572. LocalizedAddDescLine( pLocalizationProvider, pItemDef->GetItemDesc(), ATTRIB_COL_NEUTRAL, kDescLineFlag_Desc );
  1573. }
  1574. // If we're a store preview item, show the available styles in the tooltip so potential buyers
  1575. // have more information.
  1576. if ( IsStorePreviewItem( pEconItem ) )
  1577. {
  1578. if ( pItemDef && pItemDef->GetNumStyles() > 0 )
  1579. {
  1580. AddEmptyDescLine();
  1581. LocalizedAddDescLine( pLocalizationProvider, "#Store_AvailableStyles_Header", ATTRIB_COL_LEVEL, kDescLineFlag_Misc );
  1582. for ( int i = 0; i < pItemDef->GetNumStyles(); i++ )
  1583. {
  1584. LocalizedAddDescLine( pLocalizationProvider, pItemDef->GetStyleInfo( i )->GetName(), ATTRIB_COL_LEVEL, kDescLineFlag_Misc );
  1585. }
  1586. }
  1587. }
  1588. }
  1589. //-----------------------------------------------------------------------------
  1590. // Purpose:
  1591. //-----------------------------------------------------------------------------
  1592. // If we have at least this many items in our bundle then display multiple entries
  1593. // per line. Otherwise display one item per line for clarity.
  1594. enum { kDescription_CompositeBundleEntriesCount = 15 };
  1595. void CEconItemDescription::Generate_Bundle( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem )
  1596. {
  1597. Assert( pLocalizationProvider );
  1598. Assert( pEconItem );
  1599. const GameItemDefinition_t *pItemDef = pEconItem->GetItemDefinition();
  1600. if ( !pItemDef )
  1601. return;
  1602. const bundleinfo_t *pBundleInfo = pItemDef->GetBundleInfo();
  1603. if ( !pBundleInfo )
  1604. return;
  1605. enum EBundleEntryDisplayStyle
  1606. {
  1607. kBundleDisplay_SingleEntry, // one entry per line
  1608. kBundleDisplay_PairEntry, // "Some Item, Some Other Item," (with ending comma)
  1609. kBundleDisplay_PairEntryFinal, // "Some Item, Some Other Item" (with no ending comma)
  1610. };
  1611. #ifdef GC_DLL
  1612. AddEmptyDescLine();
  1613. #endif // GC_DLL
  1614. CUtlVector< item_definition_index_t > vecPackBundlesAdded;
  1615. FOR_EACH_VEC( pBundleInfo->vecItemDefs, i )
  1616. {
  1617. // Sanity check.
  1618. const CEconItemDefinition *pBundleItemDef = pBundleInfo->vecItemDefs[i];
  1619. if ( !pBundleItemDef )
  1620. continue;
  1621. // If the current item is part of a pack bundle, add the pack bundle to the description, rather than the individual item
  1622. #ifdef DOTA
  1623. if ( pBundleItemDef->IsPackItem() )
  1624. {
  1625. const CUtlVector< CEconItemDefinition * > &vecPackBundleItemDefs = pBundleItemDef->GetOwningPackBundles();
  1626. item_definition_index_t usPackBundleItemDefIndex = vecPackBundleItemDefs[i]->GetDefinitionIndex();
  1627. if ( vecPackBundlesAdded.HasElement( usPackBundleItemDefIndex ) )
  1628. continue;
  1629. // Remember the def index so we don't add the reference to the pack bundle more than once
  1630. vecPackBundlesAdded.AddToTail( usPackBundleItemDefIndex );
  1631. // Now, point pBundleItemDef at the pack bundle itself and carry on
  1632. pBundleItemDef = pPackBundleItemDef;
  1633. }
  1634. #endif
  1635. // Figure out which display style to use for this item. By default we put one item one each line...
  1636. EBundleEntryDisplayStyle eDisplayStyle = kBundleDisplay_SingleEntry;
  1637. // ...but if we have a whole bunch of items in a single bundle, we lump them together two per line to
  1638. // save space. Only do this on the client. On the GC, use single lines so that link meta data can be passed
  1639. // along per-line to the store bundle pages.
  1640. #if defined( CLIENT_DLL ) && !defined( TF_CLIENT_DLL )
  1641. if ( pBundleInfo->vecItemDefs.Count() >= kDescription_CompositeBundleEntriesCount )
  1642. {
  1643. const int iRemainingItems = pBundleInfo->vecItemDefs.Count() - i;
  1644. // We distinguish between "there are at least three entries left", which means we'll end the line
  1645. // with a comma, etc.
  1646. if ( iRemainingItems > 2 )
  1647. {
  1648. eDisplayStyle = kBundleDisplay_PairEntry;
  1649. }
  1650. // ...or if these are our very last two items, we list our last two items and that's it.
  1651. else if ( iRemainingItems == 2 )
  1652. {
  1653. eDisplayStyle = kBundleDisplay_PairEntryFinal;
  1654. }
  1655. }
  1656. #endif
  1657. if ( eDisplayStyle == kBundleDisplay_SingleEntry )
  1658. {
  1659. // 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.
  1660. #ifdef DOTA
  1661. LocalizedAddDescLine( pLocalizationProvider, pBundleItemDef->GetItemBaseName(), ATTRIB_COL_BUNDLE_ITEM, kDescLineFlag_Misc, NULL, pBundleItemDef->GetDefinitionIndex() );
  1662. #else
  1663. 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() );
  1664. #endif
  1665. }
  1666. else
  1667. {
  1668. Assert( eDisplayStyle == kBundleDisplay_PairEntry || eDisplayStyle == kBundleDisplay_PairEntryFinal );
  1669. const CEconItemDefinition *pOtherBundleItem = pBundleInfo->vecItemDefs[i + 1];
  1670. const char *pOtherBundleItemBaseName = pOtherBundleItem ? pOtherBundleItem->GetItemBaseName() : "";
  1671. AddDescLine( CConstructLocalizedString( pLocalizationProvider->Find( eDisplayStyle == kBundleDisplay_PairEntryFinal ? "Econ_Bundle_Double" : "Econ_Bundle_DoubleContinued" ),
  1672. pLocalizationProvider->Find( pBundleItemDef->GetItemBaseName() ),
  1673. pLocalizationProvider->Find( pOtherBundleItemBaseName ) ),
  1674. ATTRIB_COL_BUNDLE_ITEM,
  1675. kDescLineFlag_Misc,
  1676. NULL,
  1677. pBundleItemDef->GetDefinitionIndex() );
  1678. // We consumed a second element as well.
  1679. i++;
  1680. }
  1681. }
  1682. }
  1683. //-----------------------------------------------------------------------------
  1684. // Purpose:
  1685. //-----------------------------------------------------------------------------
  1686. void CEconItemDescription::Generate_GiftedBy( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem )
  1687. {
  1688. Assert( pLocalizationProvider );
  1689. Assert( pEconItem );
  1690. static CSchemaAttributeDefHandle pAttrDef_GiftedBy( "gifter account id" );
  1691. static CSchemaAttributeDefHandle pAttrDef_EventDate( "event date" );
  1692. attrib_value_t val_GifterId;
  1693. if ( pAttrDef_GiftedBy && pEconItem->FindAttribute( pAttrDef_GiftedBy, &val_GifterId ) )
  1694. {
  1695. // Who gifted us this present?
  1696. AddAttributeDescription( pLocalizationProvider, pAttrDef_GiftedBy, val_GifterId );
  1697. // Do we also have (optional) information about when it happened?
  1698. attrib_value_t val_EventData;
  1699. if ( pAttrDef_EventDate && pEconItem->FindAttribute( pAttrDef_EventDate, &val_EventData ) )
  1700. {
  1701. AddAttributeDescription( pLocalizationProvider, pAttrDef_EventDate, val_EventData );
  1702. }
  1703. }
  1704. }
  1705. #ifdef PROJECT_TF
  1706. //-----------------------------------------------------------------------------
  1707. // Purpose:
  1708. //-----------------------------------------------------------------------------
  1709. static bool IsDuelingMedal( const GameItemDefinition_t *pItemDef )
  1710. {
  1711. static CSchemaItemDefHandle pAttrDef_DuelingMedals[] =
  1712. {
  1713. CSchemaItemDefHandle( "Duel Medal Bronze" ),
  1714. CSchemaItemDefHandle( "Duel Medal Silver" ),
  1715. CSchemaItemDefHandle( "Duel Medal Gold" ),
  1716. CSchemaItemDefHandle( "Duel Medal Plat" ),
  1717. };
  1718. Assert( pItemDef );
  1719. for ( int i = 0; i < ARRAYSIZE( pAttrDef_DuelingMedals ); i++ )
  1720. if ( pItemDef == pAttrDef_DuelingMedals[i] )
  1721. return true;
  1722. return false;
  1723. }
  1724. void CEconItemDescription::Generate_DuelingMedal( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem )
  1725. {
  1726. Assert( pLocalizationProvider );
  1727. Assert( pEconItem );
  1728. static CSchemaAttributeDefHandle pAttrDef_EventDate( "event date" );
  1729. const GameItemDefinition_t *pItemDef = pEconItem->GetItemDefinition();
  1730. if ( !pItemDef )
  1731. return;
  1732. if ( !IsDuelingMedal( pItemDef ) )
  1733. return;
  1734. const CTFDuelSummary *pDuelSummary = FindAccountTypeCacheSingleton<CTFDuelSummary>( pEconItem->GetAccountID(), CTFDuelSummary::k_nTypeID );
  1735. if ( !pDuelSummary )
  1736. return;
  1737. // Add the date received first.
  1738. attrib_value_t value;
  1739. if ( !pEconItem->FindAttribute( pAttrDef_EventDate, &value ) )
  1740. return;
  1741. // We feed our format-string parameters in via KeyValues.
  1742. KeyValues *pKeyValues = new KeyValues( "DuelStrings" );
  1743. CLocalizedRTime32 time = { pDuelSummary->Obj().last_duel_timestamp(), false, pLocalizationProvider TF_ANTI_IDLEBOT_VERIFICATION_ONLY_COMMA TF_ANTI_IDLEBOT_VERIFICATION_ONLY_ARG( m_pHashContext ) };
  1744. TypedKeyValuesStringHelper<locchar_t>::Write( pKeyValues, "last_date", CLocalizedStringArg<CLocalizedRTime32>( time ).GetLocArg() );
  1745. TypedKeyValuesStringHelper<locchar_t>::Write( pKeyValues, "wins", CLocalizedStringArg<uint32>( pDuelSummary->Obj().duel_wins() ).GetLocArg() );
  1746. TypedKeyValuesStringHelper<locchar_t>::Write( pKeyValues, "last_target", FindAccountPersonaName( pDuelSummary->Obj().last_duel_account_id() ) );
  1747. // What happened in our last duel? This will be used as a format string to wrap the above data.
  1748. const char *pszTextFormat;
  1749. switch ( pDuelSummary->Obj().last_duel_status() )
  1750. {
  1751. case kDuelStatus_Loss:
  1752. pszTextFormat = "#TF_Duel_Desc_Lost";
  1753. break;
  1754. case kDuelStatus_Tie:
  1755. pszTextFormat = "#TF_Duel_Desc_Tied";
  1756. break;
  1757. case kDuelStatus_Win:
  1758. default:
  1759. pszTextFormat = "#TF_Duel_Desc_Won";
  1760. break;
  1761. }
  1762. // Output our whole description.
  1763. AddEmptyDescLine();
  1764. AddAttributeDescription( pLocalizationProvider, pAttrDef_EventDate, value );
  1765. AddEmptyDescLine();
  1766. AddDescLine( CConstructLocalizedString( pLocalizationProvider->Find( pszTextFormat ), pKeyValues ), ATTRIB_COL_NEUTRAL, kDescLineFlag_Misc );
  1767. pKeyValues->deleteThis();
  1768. }
  1769. //-----------------------------------------------------------------------------
  1770. // Purpose:
  1771. //-----------------------------------------------------------------------------
  1772. void CEconItemDescription::Generate_MapContributor( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem )
  1773. {
  1774. Assert( pLocalizationProvider );
  1775. Assert( pEconItem );
  1776. static CSchemaItemDefHandle pItemDef_WorldTraveler( "World Traveler" );
  1777. if ( !pItemDef_WorldTraveler || pEconItem->GetItemDefinition() != pItemDef_WorldTraveler )
  1778. return;
  1779. GCSDK::CSharedObjectTypeCache *pTypeCache = FindAccountTypeCache( pEconItem->GetAccountID(), CTFMapContribution::k_nTypeID );
  1780. if ( !pTypeCache )
  1781. return;
  1782. static const char *kDonationLevels[] =
  1783. {
  1784. "#TF_MapDonationLevel_Bronze",
  1785. "#TF_MapDonationLevel_Silver",
  1786. "#TF_MapDonationLevel_Gold",
  1787. "#TF_MapDonationLevel_Platinum",
  1788. "#TF_MapDonationLevel_Diamond",
  1789. "#TF_MapDonationLevel_Australium1",
  1790. "#TF_MapDonationLevel_Australium2",
  1791. "#TF_MapDonationLevel_Australium3",
  1792. "#TF_MapDonationLevel_Unobtainium"
  1793. };
  1794. const int kNumDonationLevels = ARRAYSIZE( kDonationLevels );
  1795. const int kNumDonationsPerLevel = 25;
  1796. CUtlVector<const CTFMapContribution *> vecContributionsPerLevel[ kNumDonationLevels ];
  1797. for ( uint32 i = 0; i < pTypeCache->GetCount(); ++i )
  1798. {
  1799. CTFMapContribution *pMapContribution = (CTFMapContribution*)( pTypeCache->GetObject( i ) );
  1800. const CEconItemDefinition *pMapItemDef = GetItemSchema()->GetItemDefinition( pMapContribution->Obj().def_index() );
  1801. if ( pMapItemDef )
  1802. {
  1803. int iLevel = MIN( pMapContribution->Obj().contribution_level() / kNumDonationsPerLevel, kNumDonationLevels - 1 );
  1804. vecContributionsPerLevel[iLevel].AddToTail( pMapContribution );
  1805. }
  1806. }
  1807. for ( int i = 0; i < kNumDonationLevels; ++i )
  1808. {
  1809. const CUtlVector<const CTFMapContribution *>& vecContributions = vecContributionsPerLevel[i];
  1810. if ( vecContributions.Count() > 0 )
  1811. {
  1812. // Add header like "Silver:" to show the level of contribution for each of the maps following.
  1813. LocalizedAddDescLine( pLocalizationProvider, kDonationLevels[i], ATTRIB_COL_ITEMSET_NAME, kDescLineFlag_Misc );
  1814. // Add a label showing the map names and number of contributions for each map.
  1815. locchar_t tempDescription[MAX_ITEM_DESCRIPTION_LENGTH] = { 0 };
  1816. FOR_EACH_VEC( vecContributions, j )
  1817. {
  1818. const CTFMapContribution *pMapContribution = vecContributions[j];
  1819. const CEconItemDefinition *pMapItemDef = GetItemSchema()->GetItemDefinition( pMapContribution->Obj().def_index() );
  1820. Assert( pMapItemDef );
  1821. const char *pszMapNameLocalizationToken = pMapItemDef->GetDefinitionString( "map_name", NULL );
  1822. if ( pszMapNameLocalizationToken )
  1823. {
  1824. loc_sncat( tempDescription,
  1825. CConstructLocalizedString( pLocalizationProvider->Find( "#Attrib_MapDonation" ),
  1826. pLocalizationProvider->Find( pszMapNameLocalizationToken ),
  1827. (uint32)pMapContribution->Obj().contribution_level() ),
  1828. MAX_ITEM_DESCRIPTION_LENGTH );
  1829. if ( j < ( vecContributions.Count() - 1 ) )
  1830. {
  1831. loc_sncat( tempDescription, LOCCHAR( ", " ), MAX_ITEM_DESCRIPTION_LENGTH );
  1832. }
  1833. }
  1834. }
  1835. if ( tempDescription[0] )
  1836. {
  1837. AddDescLine( tempDescription, ATTRIB_COL_POSITIVE, kDescLineFlag_Misc );
  1838. }
  1839. }
  1840. }
  1841. }
  1842. //-----------------------------------------------------------------------------
  1843. // Purpose:
  1844. //-----------------------------------------------------------------------------
  1845. void CEconItemDescription::Generate_FriendlyHat( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem )
  1846. {
  1847. Assert( pLocalizationProvider );
  1848. Assert( pEconItem );
  1849. static CSchemaItemDefHandle pItemDef_FriendlyHat( "Friendly Item" );
  1850. if ( !pItemDef_FriendlyHat || pEconItem->GetItemDefinition() != pItemDef_FriendlyHat )
  1851. return;
  1852. const CTFPlayerInfo *pPlayerInfo = FindAccountTypeCacheSingleton<CTFPlayerInfo>( pEconItem->GetAccountID(), CTFPlayerInfo::k_nTypeID );
  1853. if ( !pPlayerInfo )
  1854. return;
  1855. AddDescLine( CConstructLocalizedString( pLocalizationProvider->Find( "#Attrib_NewUsersHelped" ), (uint32)pPlayerInfo->Obj().num_new_users_helped() ), ATTRIB_COL_POSITIVE, kDescLineFlag_Misc );
  1856. }
  1857. //-----------------------------------------------------------------------------
  1858. // Purpose:
  1859. //-----------------------------------------------------------------------------
  1860. void CEconItemDescription::Generate_SaxxyAwardDesc( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem )
  1861. {
  1862. Assert( pLocalizationProvider );
  1863. Assert( pEconItem );
  1864. // Don't display anything for items besides the Saxxy itself.
  1865. static CSchemaItemDefHandle pItemDef_Saxxy( "Saxxy" );
  1866. static CSchemaItemDefHandle pItemDef_MemoryMaker( "The Memory Maker" );
  1867. if ( ( !pItemDef_Saxxy || pEconItem->GetItemDefinition() != pItemDef_Saxxy ) &&
  1868. ( !pItemDef_MemoryMaker || pEconItem->GetItemDefinition() != pItemDef_MemoryMaker ) )
  1869. {
  1870. return;
  1871. }
  1872. // Output our award category if present, or abort if absent.
  1873. static CSchemaAttributeDefHandle pAttrDef_SaxxyAwardCategory( "saxxy award category" );
  1874. static CSchemaAttributeDefHandle pAttrDef_EventDate( "event date" );
  1875. uint32 unAwardCategory,
  1876. unEventDate;
  1877. if ( !pEconItem->FindAttribute( pAttrDef_SaxxyAwardCategory, &unAwardCategory ) ||
  1878. !pEconItem->FindAttribute( pAttrDef_EventDate, &unEventDate ) )
  1879. {
  1880. return;
  1881. }
  1882. CRTime cTime( unEventDate );
  1883. cTime.SetToGMT( false );
  1884. const char *pszFormatString = "#Attrib_SaxxyAward";
  1885. if ( pEconItem->GetItemDefinition() == pItemDef_MemoryMaker )
  1886. {
  1887. pszFormatString = "#Attrib_MemoryMakerAward";
  1888. }
  1889. AddDescLine( CConstructLocalizedString( pLocalizationProvider->Find( pszFormatString ),
  1890. pLocalizationProvider->Find( CFmtStr( "Replay_Contest_Category%d", unAwardCategory ).Access() ),
  1891. (uint32)cTime.GetYear() ),
  1892. ATTRIB_COL_POSITIVE,
  1893. kDescLineFlag_Misc );
  1894. }
  1895. //-----------------------------------------------------------------------------
  1896. // Purpose:
  1897. //-----------------------------------------------------------------------------
  1898. void CEconItemDescription::Generate_MvmChallenges( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem )
  1899. {
  1900. // Look for our "challenges completed" attribute. If we have this, we assume we're a badge
  1901. // of some kind. If we don't, we don't display MvM information. This would be a little weird
  1902. // for level 0 badges that have no completed challenges, but those are something that currently
  1903. // exist.
  1904. static CSchemaAttributeDefHandle pAttrDef_ChallengesCompleted( CTFItemSchema::k_rchMvMChallengeCompletedMaskAttribName );
  1905. uint32 unMask = 0;
  1906. if ( !pEconItem->FindAttribute( pAttrDef_ChallengesCompleted, &unMask ) )
  1907. return;
  1908. // Look through our list of MvM tours to figure out which badge this came from. The badge itself
  1909. // doesn't know and we need this information to figure out which completion bits map to which
  1910. // missions.
  1911. const MvMTour_t *pTour = NULL;
  1912. FOR_EACH_VEC( GetItemSchema()->GetMvmTours(), i )
  1913. {
  1914. const MvMTour_t& tour = GetItemSchema()->GetMvmTours()[i];
  1915. if ( tour.m_pBadgeItemDef == pEconItem->GetItemDefinition() )
  1916. {
  1917. pTour = &tour;
  1918. break;
  1919. }
  1920. }
  1921. // Couldn't find a tour matching this badge? (This can happen if a client has a busted schema or if
  1922. // we remove a tour for some reason.)
  1923. if ( !pTour )
  1924. return;
  1925. const CUtlVector<MvMMission_t>& vecAllMissions = GetItemSchema()->GetMvmMissions();
  1926. CUtlVector<int> vecCompletedMissions;
  1927. FOR_EACH_VEC( pTour->m_vecMissions, i )
  1928. {
  1929. // Make sure our mission index is valid based on our current schema. If we're a client playing a
  1930. // game during a GC roll, we could wind up looking at someone else's badge where they have a
  1931. // mission that we don't understand.
  1932. const int iMissionIndex = pTour->m_vecMissions[i].m_iMissionIndex;
  1933. if ( !vecAllMissions.IsValidIndex( iMissionIndex ) )
  1934. continue;
  1935. const int iBadgeSlot = pTour->m_vecMissions[i].m_iBadgeSlot;
  1936. if ( iBadgeSlot >= 0 && ((unMask & (1U << iBadgeSlot)) != 0) )
  1937. {
  1938. vecCompletedMissions.AddToTail( iMissionIndex );
  1939. }
  1940. }
  1941. // Add a summary line for the number they have completed
  1942. AddDescLine(
  1943. CConstructLocalizedString(
  1944. pLocalizationProvider->Find( "#Attrib_MvMChallengesCompletedSummary" ),
  1945. uint32( vecCompletedMissions.Count() )
  1946. ),
  1947. ATTRIB_COL_POSITIVE,
  1948. kDescLineFlag_Misc
  1949. );
  1950. // Detail lines for each completed challenge
  1951. FOR_EACH_VEC( vecCompletedMissions, i )
  1952. {
  1953. const MvMMission_t& mission = vecAllMissions[ vecCompletedMissions[i] ];
  1954. const MvMMap_t& map = GetItemSchema()->GetMvmMaps()[ mission.m_iDisplayMapIndex ];
  1955. const locchar_t *pszLocFmt = pLocalizationProvider->Find( "#Attrib_MvMChallengeCompletedDetail" );
  1956. const locchar_t *pszLocMap = pLocalizationProvider->Find( map.m_sDisplayName.Get() );
  1957. const locchar_t *pszLocChal = pLocalizationProvider->Find( mission.m_sDisplayName.Get() );
  1958. if ( pszLocFmt && pszLocMap && pszLocChal )
  1959. {
  1960. CConstructLocalizedString locLine(
  1961. pszLocFmt,
  1962. pszLocMap,
  1963. pszLocChal
  1964. );
  1965. AddDescLine(
  1966. locLine,
  1967. ATTRIB_COL_POSITIVE,
  1968. kDescLineFlag_Misc
  1969. );
  1970. }
  1971. }
  1972. }
  1973. //-----------------------------------------------------------------------------
  1974. // Purpose:
  1975. //-----------------------------------------------------------------------------
  1976. void CEconItemDescription::Generate_SquadSurplusClaimedBy( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem )
  1977. {
  1978. Assert( pLocalizationProvider );
  1979. Assert( pEconItem );
  1980. static CSchemaAttributeDefHandle pAttrDef_SquadSurplusClaimer( "squad surplus claimer id" );
  1981. static CSchemaAttributeDefHandle pAttrDef_EventDate( "event date" );
  1982. attrib_value_t val_GifterId;
  1983. if ( pAttrDef_SquadSurplusClaimer&& pEconItem->FindAttribute( pAttrDef_SquadSurplusClaimer, &val_GifterId ) )
  1984. {
  1985. // Who gifted us this present?
  1986. AddAttributeDescription( pLocalizationProvider, pAttrDef_SquadSurplusClaimer, val_GifterId );
  1987. // Do we also have (optional) information about when it happened?
  1988. attrib_value_t val_EventData;
  1989. if ( pAttrDef_EventDate && pEconItem->FindAttribute( pAttrDef_EventDate, &val_EventData ) )
  1990. {
  1991. AddAttributeDescription( pLocalizationProvider, pAttrDef_EventDate, val_EventData );
  1992. }
  1993. }
  1994. }
  1995. //-----------------------------------------------------------------------------
  1996. // Purpose:
  1997. //-----------------------------------------------------------------------------
  1998. void CEconItemDescription::Generate_DynamicRecipe( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem )
  1999. {
  2000. // Gather our attributes we care about
  2001. CRecipeComponentMatchingIterator componentIterator( pEconItem, NULL );
  2002. pEconItem->IterateAttributes( &componentIterator );
  2003. // Nothing to say, bail!
  2004. if( !componentIterator.GetMatchingComponentInputs().Count() &&
  2005. !componentIterator.GetMatchingComponentOutputs().Count() )
  2006. {
  2007. return;
  2008. }
  2009. // Add the no partial complete tag if the attribute exists
  2010. static CSchemaAttributeDefHandle pAttrib_NoPartialComplete( "recipe no partial complete" );
  2011. if ( pEconItem->FindAttribute( pAttrib_NoPartialComplete ) )
  2012. {
  2013. LocalizedAddDescLine( pLocalizationProvider, "TF_ItemDynamic_Recipe_No_Partial_Completion", ATTRIB_COL_POSITIVE, kDescLineFlag_Misc );
  2014. }
  2015. AddEmptyDescLine();
  2016. if ( componentIterator.GetMatchingComponentInputs().Count() )
  2017. {
  2018. // Print out item input header
  2019. LocalizedAddDescLine( pLocalizationProvider, "TF_ItemDynamic_Recipe_Inputs", ATTRIB_COL_NEUTRAL, kDescLineFlag_Misc );
  2020. // Print out inputs
  2021. FOR_EACH_VEC( componentIterator.GetMatchingComponentInputs(), i )
  2022. {
  2023. CAttribute_DynamicRecipeComponent attribValue;
  2024. pEconItem->FindAttribute( componentIterator.GetMatchingComponentInputs()[i], &attribValue );
  2025. const GameItemDefinition_t *pItemDef = dynamic_cast<GameItemDefinition_t *>( GetItemSchema()->GetItemDefinition( attribValue.def_index() ) );
  2026. if ( !pItemDef )
  2027. continue;
  2028. int nCount = attribValue.num_required() - attribValue.num_fulfilled();
  2029. // This is a completed component. We don't want to show it (for now)
  2030. if( nCount == 0 )
  2031. continue;
  2032. CEconItem tempItem;
  2033. if ( !DecodeItemFromEncodedAttributeString( attribValue, &tempItem ) )
  2034. {
  2035. AssertMsg2( 0, "%s: Unable to decode dynamic recipe input attribute on item %llu.", __FUNCTION__, pEconItem->GetID() );
  2036. continue;
  2037. }
  2038. locchar_t lineItem[256];
  2039. locchar_t loc_ItemName[MAX_ITEM_NAME_LENGTH];
  2040. GenerateLocalizedFullItemName( loc_ItemName, pLocalizationProvider, &tempItem, k_EGenerateLocalizedFullItemName_Default, m_pHashContext == NULL );
  2041. loc_sprintf_safe( lineItem,
  2042. LOCCHAR("%s x %d"),
  2043. loc_ItemName,
  2044. nCount
  2045. );
  2046. AddDescLine( lineItem, ATTRIB_COL_ITEMSET_MISSING, kDescLineFlag_Misc );
  2047. }
  2048. AddEmptyDescLine();
  2049. }
  2050. // Print out outputs
  2051. LocalizedAddDescLine( pLocalizationProvider, "TF_ItemDynamic_Recipe_Outputs", ATTRIB_COL_NEUTRAL, kDescLineFlag_Misc );
  2052. FOR_EACH_VEC( componentIterator.GetMatchingComponentOutputs(), i )
  2053. {
  2054. CAttribute_DynamicRecipeComponent attribValue;
  2055. pEconItem->FindAttribute( componentIterator.GetMatchingComponentOutputs()[i], &attribValue );
  2056. CEconItem tempItem;
  2057. if ( !DecodeItemFromEncodedAttributeString( attribValue, &tempItem ) )
  2058. {
  2059. AssertMsg2( 0, "%s: Unable to decode dynamic recipe output attribute on item %llu.", __FUNCTION__, pEconItem->GetID() );
  2060. continue;
  2061. }
  2062. locchar_t loc_ItemName[MAX_ITEM_NAME_LENGTH];
  2063. GenerateLocalizedFullItemName( loc_ItemName, pLocalizationProvider, &tempItem, k_EGenerateLocalizedFullItemName_Default, m_pHashContext == NULL );
  2064. AddDescLine( loc_ItemName, /* this will be ignored: */ ATTRIB_COL_ITEMSET_MISSING, kDescLineFlag_Misc );
  2065. // Iterate through the attributes on this temp item and have it store the attributes that should affect
  2066. // this component's name. Once we have that, have it fill out a temporary CEconItemDescription.
  2067. CRecipeNameAttributeDisplayer recipeAttributeIterator;
  2068. tempItem.IterateAttributes( &recipeAttributeIterator );
  2069. recipeAttributeIterator.SortAttributes();
  2070. CEconItemDescription tempDescription;
  2071. recipeAttributeIterator.Finalize( &tempItem, &tempDescription, pLocalizationProvider );
  2072. // Check if that temp CEconItemDescription has any attributes we want. If so, steal them.
  2073. if ( tempDescription.m_vecDescLines.Count() > 0 )
  2074. {
  2075. locchar_t loc_Attribs[MAX_ITEM_NAME_LENGTH] = LOCCHAR("");
  2076. // Put the attributes on the next line in parenthesis
  2077. loc_scat_safe( loc_Attribs, LOCCHAR("(") );
  2078. // Put in each attribute
  2079. FOR_EACH_VEC( tempDescription.m_vecDescLines, j )
  2080. {
  2081. // Comma separated
  2082. if ( j > 0 )
  2083. {
  2084. loc_scat_safe( loc_Attribs, LOCCHAR(", ") );
  2085. }
  2086. loc_sprintf_safe( loc_Attribs,
  2087. LOCCHAR("%s%s"),
  2088. loc_Attribs,
  2089. tempDescription.m_vecDescLines[j].sText.Get() );
  2090. }
  2091. loc_scat_safe( loc_Attribs, LOCCHAR(")") );
  2092. // Print out in the same color as the item name above
  2093. AddDescLine( loc_Attribs, /* this will be ignored: */ ATTRIB_COL_ITEMSET_MISSING, kDescLineFlag_Misc );
  2094. }
  2095. }
  2096. }
  2097. //-----------------------------------------------------------------------------
  2098. void CEconItemDescription::Generate_Leaderboard( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem )
  2099. {
  2100. #ifdef GC_DLL
  2101. return;
  2102. #endif
  2103. #ifdef TF_CLIENT_DLL
  2104. Assert( pLocalizationProvider );
  2105. Assert( pEconItem );
  2106. static CSchemaAttributeDefHandle pAttrDef_DisplayDuckLeaderboard( "display duck leaderboard" );
  2107. if ( pEconItem->FindAttribute( pAttrDef_DisplayDuckLeaderboard ) )
  2108. {
  2109. // Friend Board
  2110. //locchar_t lineItem[256];
  2111. //
  2112. //AddDescLine( pLocalizationProvider->Find( "#TF_DuckLeaderboard_Friends" ), ATTRIB_COL_POSITIVE, kDescLineFlag_Misc );
  2113. //
  2114. //CUtlVector< LeaderboardEntry_t > scores;
  2115. //Leaderboards_GetDuckLeaderboard( scores, g_szDuckLeaderboardNames[0] );
  2116. //// Show max of top 10
  2117. //for ( int i = 0; i < scores.Count() && i < 10; i++ )
  2118. //{
  2119. // const locchar_t *pName = FindAccountPersonaName( scores[i].m_steamIDUser.GetAccountID() );
  2120. // uint32 iRank = scores[i].m_nGlobalRank;
  2121. // uint32 iScore = scores[i].m_nScore;
  2122. //
  2123. // AddDescLine(
  2124. // CConstructLocalizedString(
  2125. // pLocalizationProvider->Find( "#TF_DuckLeaderboard_Entry" ),
  2126. // iRank, pName, iScore
  2127. // ),
  2128. // ATTRIB_COL_POSITIVE,
  2129. // kDescLineFlag_Misc
  2130. // );
  2131. //}
  2132. }
  2133. #endif // TF_CLIENT_DLL
  2134. }
  2135. #endif // PROJECT_TF
  2136. //-----------------------------------------------------------------------------
  2137. // Purpose:
  2138. //-----------------------------------------------------------------------------
  2139. const CEconItemDefinition *GetPaintItemDefinitionForPaintedItem( const IEconItemInterface *pEconItem )
  2140. {
  2141. static CSchemaAttributeDefHandle pAttribDef_Paint( "set item tint RGB" );
  2142. attrib_value_t unPaintRGBAttrBits;
  2143. if ( !pAttribDef_Paint || !pEconItem->FindAttribute( pAttribDef_Paint, &unPaintRGBAttrBits ) )
  2144. return NULL;
  2145. const CEconItemSchema::ToolsItemDefinitionMap_t &toolDefs = GetItemSchema()->GetToolsItemDefinitionMap();
  2146. FOR_EACH_MAP_FAST( toolDefs, i )
  2147. {
  2148. const CEconItemDefinition *pItemDef = toolDefs[i];
  2149. // ignore everything that is not a paint can tool
  2150. const IEconTool *pEconTool = pItemDef->GetEconTool();
  2151. if ( pEconTool && !V_strcmp( pEconTool->GetTypeName(), "paint_can" ) )
  2152. {
  2153. attrib_value_t unPaintRGBAttrCompareBits;
  2154. if ( FindAttribute( pItemDef, pAttribDef_Paint, &unPaintRGBAttrCompareBits ) && unPaintRGBAttrCompareBits == unPaintRGBAttrBits )
  2155. return pItemDef;
  2156. }
  2157. }
  2158. return NULL;
  2159. }
  2160. //-----------------------------------------------------------------------------
  2161. // Purpose: Specify target (strangifiers, etc that can only be applied to specific items)
  2162. //-----------------------------------------------------------------------------
  2163. void CEconItemDescription::Generate_XifierToolTargetItem( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem )
  2164. {
  2165. Assert( pLocalizationProvider );
  2166. Assert( pEconItem );
  2167. // Make sure it's a tool of the appropriate type
  2168. const CEconTool_Xifier *pTool = pEconItem->GetItemDefinition()->GetTypedEconTool<CEconTool_Xifier>();
  2169. if ( pTool == NULL )
  2170. return;
  2171. // Make sure there is a specific target item
  2172. static CSchemaAttributeDefHandle pAttribDef_ToolTargetItem( "tool target item" );
  2173. float flItemDef;
  2174. if( pAttribDef_ToolTargetItem && FindAttribute_UnsafeBitwiseCast<attrib_value_t>( pEconItem, pAttribDef_ToolTargetItem, &flItemDef ) )
  2175. {
  2176. locchar_t szTargetItemName[ MAX_ITEM_NAME_LENGTH ] = LOCCHAR("Unknown Item");
  2177. // It's a tool, see if it has a tool target item attribute
  2178. const item_definition_index_t unItemDef = flItemDef;
  2179. const CEconItemDefinition *pEconTargetDef = GetItemSchema()->GetItemDefinition( unItemDef );
  2180. // Start with the base name.
  2181. if ( pEconTargetDef )
  2182. {
  2183. GetLocalizedBaseItemName( szTargetItemName, pLocalizationProvider, pEconTargetDef );
  2184. }
  2185. const char *pszDesc = pTool->GetItemDescToolTargetLocToken();
  2186. AssertMsg( pszDesc && *pszDesc, "%s: missing 'item_desc_tool_target' key", pTool->GetTypeName() );
  2187. AddDescLine( CConstructLocalizedString( pLocalizationProvider->Find( pszDesc ),
  2188. szTargetItemName ),
  2189. ATTRIB_COL_NEUTRAL,
  2190. kDescLineFlag_Desc );
  2191. }
  2192. }
  2193. //-----------------------------------------------------------------------------
  2194. // Purpose:
  2195. //-----------------------------------------------------------------------------
  2196. void CEconItemDescription::Generate_Painted( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem )
  2197. {
  2198. Assert( pLocalizationProvider );
  2199. Assert( pEconItem );
  2200. static CSchemaAttributeDefHandle pAttrDef_PaintEffect( "Paint Effect" );
  2201. float fPaintEffectType;
  2202. if ( pAttrDef_PaintEffect && FindAttribute_UnsafeBitwiseCast<attrib_value_t>( pEconItem, pAttrDef_PaintEffect, &fPaintEffectType ) )
  2203. {
  2204. if ( fPaintEffectType == 1 )
  2205. {
  2206. LocalizedAddDescLine( pLocalizationProvider, "Econ_Paint_Effect_Oscillating", ATTRIB_COL_NEUTRAL, kDescLineFlag_Misc );
  2207. }
  2208. else if ( fPaintEffectType == 2 )
  2209. {
  2210. LocalizedAddDescLine( pLocalizationProvider, "Econ_Paint_Effect_Position", ATTRIB_COL_NEUTRAL, kDescLineFlag_Misc );
  2211. }
  2212. else if ( fPaintEffectType == 3 )
  2213. {
  2214. LocalizedAddDescLine( pLocalizationProvider, "Econ_Paint_Effect_LowHealthWarning", ATTRIB_COL_NEUTRAL, kDescLineFlag_Misc );
  2215. }
  2216. }
  2217. // Find the name of the paint we have applied in the least efficient way imaginable!
  2218. const CEconItemDefinition *pItemDef = pEconItem->GetItemDefinition();
  2219. static CSchemaAttributeDefHandle pAttrDef_ShowPaint( "show paint description" );
  2220. if ( pItemDef && ( !pItemDef->IsTool() || FindAttribute( pEconItem, pAttrDef_ShowPaint ) ) )
  2221. {
  2222. const CEconItemDefinition *pTempDef = GetPaintItemDefinitionForPaintedItem( pEconItem );
  2223. if ( pTempDef )
  2224. {
  2225. const locchar_t *locLocalizedPaintName = pLocalizationProvider->Find( pTempDef->GetItemBaseName() );
  2226. if ( locLocalizedPaintName )
  2227. {
  2228. AddDescLine( CConstructLocalizedString( pLocalizationProvider->Find( "Econ_Paint_Name" ),
  2229. locLocalizedPaintName ),
  2230. ATTRIB_COL_LEVEL,
  2231. kDescLineFlag_Misc );
  2232. }
  2233. }
  2234. }
  2235. }
  2236. //-----------------------------------------------------------------------------
  2237. // Purpose:
  2238. //-----------------------------------------------------------------------------
  2239. void CEconItemDescription::Generate_Uses( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem )
  2240. {
  2241. Assert( pLocalizationProvider );
  2242. Assert( pEconItem );
  2243. // don't display a quantity if we have the unlimited quantity attribute
  2244. static CSchemaAttributeDefHandle unlimitedQuantityAttribute( "unlimited quantity" );
  2245. if ( pEconItem->FindAttribute( unlimitedQuantityAttribute ) )
  2246. return;
  2247. // Collection tools don't display this.
  2248. const GameItemDefinition_t *pItemDef = pEconItem->GetItemDefinition();
  2249. if ( !pItemDef )
  2250. return;
  2251. const IEconTool *pEconTool = pItemDef->GetEconTool();
  2252. if ( !pEconTool->ShouldDisplayQuantity( pEconItem ) )
  2253. return;
  2254. int iQuantity = pEconItem->GetQuantity();
  2255. bool bIsTool = pItemDef->GetItemClass() && !Q_strcmp( pItemDef->GetItemClass(), "tool" );
  2256. bool bIsConsumable = ( pItemDef->GetCapabilities() & ITEM_CAP_USABLE_GC ) != 0 && iQuantity != 0;
  2257. if ( bIsTool || bIsConsumable )
  2258. {
  2259. locchar_t wszQuantity[10];
  2260. loc_sprintf_safe( wszQuantity, LOCCHAR( "%d" ), iQuantity );
  2261. // Add an empty line before the usage display.
  2262. AddEmptyDescLine();
  2263. // Display our usage count.
  2264. AddDescLine( CConstructLocalizedString( pLocalizationProvider->Find( "#Attrib_LimitedUse" ), &wszQuantity[0] ), ATTRIB_COL_LIMITED_USE, kDescLineFlag_Misc );
  2265. }
  2266. }
  2267. // --------------------------------------------------------------------------
  2268. // Purpose:
  2269. // --------------------------------------------------------------------------
  2270. void CEconItemDescription::Generate_LootListDesc( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem )
  2271. {
  2272. Assert( pLocalizationProvider );
  2273. Assert( pEconItem );
  2274. const CEconItemDefinition *pItemDef = pEconItem->GetItemDefinition();
  2275. if ( !pItemDef )
  2276. return;
  2277. // Don't add this description if the item is a special crate type.
  2278. const IEconTool *pEconTool = pItemDef->GetEconTool();
  2279. const bool bIsRestrictedCrate = pEconTool && pEconTool->GetUsageRestriction()
  2280. ? !V_stricmp( pEconTool->GetUsageRestriction(), "winter" ) || !V_stricmp( pEconTool->GetUsageRestriction(), "summer" )
  2281. : false;
  2282. if ( bIsRestrictedCrate )
  2283. return;
  2284. // Do we have a generation code we want to make public? We do this regardless of whether we're describing our
  2285. // loot list contents in detail.
  2286. {
  2287. static CSchemaAttributeDefHandle pAttrDef_CrateGenerationCode( "crate generation code" );
  2288. CAttribute_String sCrateGenerationCode;
  2289. const locchar_t *pszCrateGenerationCodeLoc = pLocalizationProvider->Find( "Attrib_CrateGenerationCode" );
  2290. if ( pEconItem->FindAttribute( pAttrDef_CrateGenerationCode, &sCrateGenerationCode ) && sCrateGenerationCode.value().length() > 0 )
  2291. {
  2292. CUtlConstStringBase<locchar_t> loc_sAttrValue;
  2293. pLocalizationProvider->ConvertUTF8ToLocchar( sCrateGenerationCode.value().c_str(), &loc_sAttrValue );
  2294. AddDescLine( CConstructLocalizedString( pszCrateGenerationCodeLoc, loc_sAttrValue.Get() ),
  2295. ATTRIB_COL_POSITIVE,
  2296. kDescLineFlag_Misc );
  2297. }
  2298. }
  2299. // Grab the actual contents of our loot list.
  2300. CCrateLootListWrapper LootListWrapper( pEconItem );
  2301. const IEconLootList *pLootList = LootListWrapper.GetEconLootList();
  2302. if ( pLootList == nullptr )
  2303. return;
  2304. // If our base loot list is set not to list contents, skip the header/footer as well and don't display anything.
  2305. if ( !pLootList->BPublicListContents() )
  2306. return;
  2307. AddEmptyDescLine();
  2308. if ( !pLootList->GetLootListCollectionReference() )
  2309. {
  2310. LocalizedAddDescLine( pLocalizationProvider, pLootList->GetLootListHeaderLocalizationKey(), ATTRIB_COL_NEUTRAL, kDescLineFlag_Misc );
  2311. }
  2312. else
  2313. {
  2314. int iCollectionIndex = GetItemSchema()->GetItemCollections().Find( pLootList->GetLootListCollectionReference() );
  2315. if ( GetItemSchema()->GetItemCollections().IsValidIndex( iCollectionIndex ) )
  2316. {
  2317. LocalizedAddDescLine( pLocalizationProvider, (GetItemSchema()->GetItemCollections()[iCollectionIndex])->m_pszLocalizedDesc, ATTRIB_COL_NEUTRAL, kDescLineFlag_Misc );
  2318. }
  2319. }
  2320. class CDescriptionLootListIterator : public IEconLootList::IEconLootListIterator
  2321. {
  2322. public:
  2323. CDescriptionLootListIterator( CEconItemDescription *pThis, const CLocalizationProvider *pLocalizationProvider, bool bUseProperName )
  2324. : m_pThis( pThis )
  2325. , m_pLocalizationProvider( pLocalizationProvider )
  2326. , m_bUseProperName( bUseProperName )
  2327. {
  2328. }
  2329. virtual void OnIterate( item_definition_index_t unItemDefIndex ) OVERRIDE
  2330. {
  2331. const CEconItemDefinition *pItemDef = GetItemSchema()->GetItemDefinition( unItemDefIndex );
  2332. if ( pItemDef )
  2333. {
  2334. // Check if this item is already owned
  2335. bool bOwned = false;
  2336. bool bUnusual = false;
  2337. #ifdef CLIENT_DLL
  2338. CPlayerInventory *pLocalInv = TFInventoryManager()->GetLocalInventory();
  2339. if ( pLocalInv )
  2340. {
  2341. for ( int i = 0; i < pLocalInv->GetItemCount(); ++i )
  2342. {
  2343. CEconItemView *pItem = pLocalInv->GetItem( i );
  2344. if ( pItem->GetItemDefinition() == pItemDef )
  2345. {
  2346. bOwned = true;
  2347. // Check Quality
  2348. if ( pItem->GetQuality() == AE_UNUSUAL )
  2349. {
  2350. bUnusual = true;
  2351. break;
  2352. }
  2353. }
  2354. }
  2355. }
  2356. #endif
  2357. const locchar_t * pCheckmark = bOwned ? m_pLocalizationProvider->Find( "TF_Checkmark" ) : m_pLocalizationProvider->Find( "TF_LackOfCheckmark" );
  2358. if ( bOwned && bUnusual )
  2359. {
  2360. pCheckmark = m_pLocalizationProvider->Find( "TF_Checkmark_Unusual" );
  2361. }
  2362. attrib_colors_t colorRarity = ATTRIB_COL_RARITY_DEFAULT;
  2363. const CEconItemRarityDefinition* pItemRarity = GetItemSchema()->GetRarityDefinition( pItemDef->GetRarity() );
  2364. if ( pItemRarity )
  2365. {
  2366. colorRarity = pItemRarity->GetAttribColor();
  2367. }
  2368. m_pThis->AddDescLine(
  2369. CConstructLocalizedString( LOCCHAR( "%s1%s2" ), pCheckmark,
  2370. CEconItemLocalizedFullNameGenerator(
  2371. m_pLocalizationProvider,
  2372. pItemDef,
  2373. m_bUseProperName
  2374. ).GetFullName() ),
  2375. colorRarity,
  2376. kDescLineFlag_Misc,
  2377. NULL,
  2378. pItemDef->GetDefinitionIndex()
  2379. );
  2380. }
  2381. }
  2382. private:
  2383. CEconItemDescription *m_pThis; // look at me I'm a lambda!
  2384. const CLocalizationProvider *m_pLocalizationProvider;
  2385. bool m_bUseProperName;
  2386. };
  2387. CDescriptionLootListIterator it( this, pLocalizationProvider, m_pHashContext == NULL );
  2388. pLootList->EnumerateUserFacingPotentialDrops( &it );
  2389. if ( pLootList->GetLootListFooterLocalizationKey() )
  2390. {
  2391. LocalizedAddDescLine( pLocalizationProvider, pLootList->GetLootListFooterLocalizationKey(), ATTRIB_COL_POSITIVE, kDescLineFlag_Misc );
  2392. }
  2393. else
  2394. {
  2395. const char *pszRareLootListFooterLocalizationKey = pItemDef->GetDefinitionString( "loot_list_rare_item_footer", "#Econ_Revolving_Loot_List_Rare_Item" );
  2396. LocalizedAddDescLine( pLocalizationProvider, pszRareLootListFooterLocalizationKey, ATTRIB_COL_POSITIVE, kDescLineFlag_Misc );
  2397. }
  2398. }
  2399. // --------------------------------------------------------------------------
  2400. void CEconItemDescription::Generate_EventDetail( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem )
  2401. {
  2402. Assert( pLocalizationProvider );
  2403. Assert( pEconItem );
  2404. const CEconItemDefinition *pItemDef = pEconItem->GetItemDefinition();
  2405. if ( !pItemDef )
  2406. return;
  2407. // Check to see if we should append any extra description information
  2408. const char *pszEventLocalizationKey = pItemDef->GetDefinitionString( "event_desc_footer", NULL );
  2409. if ( pszEventLocalizationKey )
  2410. {
  2411. AddEmptyDescLine();
  2412. LocalizedAddDescLine( pLocalizationProvider, pszEventLocalizationKey, ATTRIB_COL_POSITIVE, kDescLineFlag_Misc );
  2413. }
  2414. }
  2415. // --------------------------------------------------------------------------
  2416. // Purpose:
  2417. // --------------------------------------------------------------------------
  2418. #ifdef CLIENT_DLL
  2419. static bool IsItemEquipped( uint32 unAccountID, const CEconItemDefinition *pSearchItemDef, const GameItemDefinition_t **ppFoundSetItemDef )
  2420. {
  2421. Assert( pSearchItemDef );
  2422. Assert( ppFoundSetItemDef );
  2423. CPlayerInventory *pInv = InventoryManager()->GetInventoryForAccount( unAccountID );
  2424. if ( !pInv )
  2425. return false;
  2426. for ( int i = 0; i < pInv->GetItemCount(); i++ )
  2427. {
  2428. const CEconItemView *pInvItem = pInv->GetItem( i );
  2429. if ( !pInvItem )
  2430. continue;
  2431. // This code is client-only so we expect to always get back an item definition pointer.
  2432. const GameItemDefinition_t *pInvItemDef = pInvItem->GetItemDefinition();
  2433. Assert( pInvItemDef );
  2434. if ( pInvItemDef->GetSetItemRemap() != pSearchItemDef->GetDefinitionIndex() )
  2435. continue;
  2436. if ( !pInvItem->IsEquipped() )
  2437. continue;
  2438. *ppFoundSetItemDef = pInvItemDef;
  2439. return true;
  2440. }
  2441. return false;
  2442. }
  2443. #endif // CLIENT_DLL
  2444. void CEconItemDescription::Generate_ItemSetDesc( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem )
  2445. {
  2446. Assert( pLocalizationProvider );
  2447. Assert( pEconItem );
  2448. const GameItemDefinition_t *pItemDef = pEconItem->GetItemDefinition();
  2449. if ( !pItemDef )
  2450. return;
  2451. const CEconItemSetDefinition *pItemSetDef = pItemDef->GetItemSetDefinition();
  2452. if ( !pItemSetDef )
  2453. return;
  2454. bool bAllItemsEquipped = true; // filled in below when iterating over items
  2455. // Some item sets are tagged to only appear on items at all if the entire set is visible. Rather than
  2456. // walk the whole set multiple times checking for item equipped state, we build up a set of description lines
  2457. // that we *will* display if we display anything at all. Later, we either submit all those lines for real or
  2458. // return before adding any of them.
  2459. {
  2460. CUtlVector<econ_item_description_line_t> vecPotentialDescLines;
  2461. AddEmptyDescLine( &vecPotentialDescLines );
  2462. LocalizedAddDescLine( pLocalizationProvider, pItemSetDef->m_pszLocalizedName, ATTRIB_COL_ITEMSET_NAME, kDescLineFlag_Set | kDescLineFlag_SetName, &vecPotentialDescLines, pItemSetDef->m_iBundleItemDef );
  2463. // Kyle says: Jon wants different formatting on the GC for sets.
  2464. #if defined( GC_DLL )
  2465. #if TF_ANTI_IDLEBOT_VERIFICATION
  2466. if ( !m_pHashContext )
  2467. #endif // TF_ANTI_IDLEBOT_VERIFICATION
  2468. {
  2469. AddEmptyDescLine( &vecPotentialDescLines );
  2470. }
  2471. #endif // defined( GC_DLL ) &&
  2472. // Iterate over the items in the set. We'll output a line in different colors to show
  2473. // the current state of this item. For normal item sets, the color is based on whether
  2474. // the owner has the item in question equipped (except on the GC, where we always say
  2475. // "it's not equipped" to avoid confusion). For collections, the color is based on whether
  2476. // the item has been collected.
  2477. FOR_EACH_VEC( pItemSetDef->m_iItemDefs, i )
  2478. {
  2479. const GameItemDefinition_t *pOtherSetItem = dynamic_cast<const GameItemDefinition_t *>( GetItemSchema()->GetItemDefinition( pItemSetDef->m_iItemDefs[i] ) );
  2480. if ( !pOtherSetItem )
  2481. continue;
  2482. item_definition_index_t usLinkItemDefIndex = pOtherSetItem->GetDefinitionIndex();
  2483. #ifdef DOTA
  2484. // If the current item is part of a pack bundle, add the pack bundle to the description, rather than the individual item
  2485. if ( pOtherSetItem->IsPackItem() )
  2486. {
  2487. // Link to the pack bundle, not the individual pack item
  2488. usLinkItemDefIndex = pOtherSetItem->GetOwningPackBundle()->GetDefinitionIndex();
  2489. }
  2490. #endif
  2491. // Only used on non-GC in case we have an item misrepresenting itself intentionally for set
  2492. // grouping purposes. NULL elsewhere.
  2493. const GameItemDefinition_t *pFoundSetItemDef = NULL;
  2494. const bool bItemPresent =
  2495. #ifdef GC_DLL
  2496. false; // the GC always treats set items as unequipped for clarity in trading
  2497. #else
  2498. #if TF_ANTI_IDLEBOT_VERIFICATION
  2499. m_pHashContext
  2500. ? false // when generating descriptions for GC verification we treat item sets as unequipped to match the GC
  2501. #endif
  2502. : IsItemEquipped( pEconItem->GetAccountID(), pOtherSetItem, &pFoundSetItemDef ); // non-GC display will find out whether the player in question has this item actively equipped
  2503. #endif
  2504. AddDescLine( CEconItemLocalizedFullNameGenerator(
  2505. pLocalizationProvider,
  2506. pFoundSetItemDef ? pFoundSetItemDef : pOtherSetItem,
  2507. m_pHashContext == NULL
  2508. ).GetFullName(), bItemPresent ? ATTRIB_COL_ITEMSET_EQUIPPED : ATTRIB_COL_ITEMSET_MISSING, kDescLineFlag_Set, &vecPotentialDescLines, usLinkItemDefIndex );
  2509. bAllItemsEquipped &= bItemPresent;
  2510. }
  2511. // If the item set is set to be only displayed when the full set is equipped, give up here and
  2512. // toss out our potential lines. Otherwise submit them for real.
  2513. if ( pItemSetDef->m_bIsHiddenSet && !bAllItemsEquipped )
  2514. return;
  2515. FOR_EACH_VEC( vecPotentialDescLines, i )
  2516. {
  2517. AddDescLine( vecPotentialDescLines[i].sText.Get(), vecPotentialDescLines[i].eColor, vecPotentialDescLines[i].unMetaType, NULL, vecPotentialDescLines[i].unDefIndex );
  2518. }
  2519. }
  2520. // Show the set only attributes if we have the entire set and we have bonus attributes to display.
  2521. bool bHasVisible = false;
  2522. FOR_EACH_VEC( pItemSetDef->m_iAttributes, i )
  2523. {
  2524. const CEconItemSetDefinition::itemset_attrib_t& attrib = pItemSetDef->m_iAttributes[i];
  2525. const CEconItemAttributeDefinition *pAttrDef = GetItemSchema()->GetAttributeDefinition( attrib.m_iAttribDefIndex );
  2526. if ( !pAttrDef->IsHidden() )
  2527. {
  2528. bHasVisible = true;
  2529. break;
  2530. }
  2531. }
  2532. if ( !bHasVisible )
  2533. return;
  2534. AddEmptyDescLine();
  2535. LocalizedAddDescLine( pLocalizationProvider, "#Econ_Set_Bonus", ATTRIB_COL_ITEMSET_NAME, kDescLineFlag_Set );
  2536. FOR_EACH_VEC( pItemSetDef->m_iAttributes, i )
  2537. {
  2538. const CEconItemSetDefinition::itemset_attrib_t& attrib = pItemSetDef->m_iAttributes[i];
  2539. const CEconItemAttributeDefinition *pAttrDef = GetItemSchema()->GetAttributeDefinition( attrib.m_iAttribDefIndex );
  2540. if ( pAttrDef )
  2541. {
  2542. // Add the attribute description. Override the color to be grayed out if we don't have the
  2543. // full set equipped.
  2544. AddAttributeDescription( pLocalizationProvider,
  2545. pAttrDef,
  2546. *(attrib_value_t *)&attrib.m_flValue,
  2547. bAllItemsEquipped ? /* "do not override": */ NUM_ATTRIB_COLORS : ATTRIB_COL_ITEMSET_MISSING );
  2548. }
  2549. }
  2550. }
  2551. // --------------------------------------------------------------------------
  2552. void CEconItemDescription::Generate_CollectionDesc( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem )
  2553. {
  2554. Assert( pLocalizationProvider );
  2555. Assert( pEconItem );
  2556. const CEconItemDefinition *pItemDef = pEconItem->GetItemDefinition();
  2557. if ( !pItemDef )
  2558. return;
  2559. const CEconItemCollectionDefinition *pCollection = pItemDef->GetItemCollectionDefinition();
  2560. if ( !pCollection )
  2561. return;
  2562. // Collection Header ..
  2563. const locchar_t *loc_name = pLocalizationProvider->Find( pCollection->m_pszLocalizedName );
  2564. // Add a bit of spacing, this is only for the market
  2565. // Add empty line
  2566. AddDescLine( LOCCHAR( " " ), ATTRIB_COL_NEUTRAL, kDescLineFlag_CollectionName | kDescLineFlag_Empty );
  2567. AddDescLine( LOCCHAR( " " ), ATTRIB_COL_NEUTRAL, kDescLineFlag_CollectionName | kDescLineFlag_Empty );
  2568. AddDescLine(
  2569. CConstructLocalizedString( LOCCHAR( "%s1" ), loc_name ),
  2570. ATTRIB_COL_NEUTRAL,
  2571. kDescLineFlag_CollectionName
  2572. );
  2573. FOR_EACH_VEC( pCollection->m_iItemDefs, index )
  2574. {
  2575. int eFlag = kDescLineFlag_Collection;
  2576. const CEconItemDefinition *pTempItemDef = GetItemSchema()->GetItemDefinition( pCollection->m_iItemDefs[index] );
  2577. if ( pTempItemDef )
  2578. {
  2579. // Check if this item is already owned
  2580. bool bOwned = false;
  2581. bool bUnusual = false;
  2582. if ( pTempItemDef == pItemDef )
  2583. {
  2584. bOwned = true;
  2585. eFlag |= kDescLineFlag_CollectionCurrentItem;
  2586. if ( pEconItem->GetQuality() == AE_UNUSUAL )
  2587. {
  2588. bUnusual = true;
  2589. }
  2590. }
  2591. #ifdef CLIENT_DLL
  2592. else
  2593. {
  2594. CPlayerInventory *pLocalInv = TFInventoryManager()->GetLocalInventory();
  2595. if ( pLocalInv )
  2596. {
  2597. const CEconItemCollectionDefinition *pRefCollection = GetItemSchema()->GetCollectionByName( pTempItemDef->GetCollectionReference() );
  2598. // if item has a collection reference, we are looking for all those items and this item
  2599. if ( pRefCollection )
  2600. {
  2601. bOwned = true;
  2602. FOR_EACH_VEC( pRefCollection->m_iItemDefs, iRefCollectionItem )
  2603. {
  2604. const CEconItemView *pRefItem = pLocalInv->FindFirstItembyItemDef( pRefCollection->m_iItemDefs[ iRefCollectionItem ] );
  2605. if ( !pRefItem )
  2606. {
  2607. bOwned = false;
  2608. break;
  2609. }
  2610. }
  2611. }
  2612. else
  2613. {
  2614. // Normal Backpack scan
  2615. for ( int i = 0; i < pLocalInv->GetItemCount(); ++i )
  2616. {
  2617. CEconItemView *pItem = pLocalInv->GetItem( i );
  2618. if ( pItem->GetItemDefinition() == pTempItemDef )
  2619. {
  2620. bOwned = true;
  2621. // Check Quality
  2622. if ( pItem->GetQuality() == AE_UNUSUAL )
  2623. {
  2624. bUnusual = true;
  2625. break;
  2626. }
  2627. }
  2628. }
  2629. }
  2630. }
  2631. }
  2632. #endif
  2633. const locchar_t * pCheckmark = bOwned ? pLocalizationProvider->Find( "TF_Checkmark" ) : pLocalizationProvider->Find( "TF_LackOfCheckmark" );
  2634. if ( bOwned && bUnusual )
  2635. {
  2636. pCheckmark = pLocalizationProvider->Find( "TF_Checkmark_Unusual" );
  2637. }
  2638. attrib_colors_t colorRarity = ATTRIB_COL_RARITY_DEFAULT;
  2639. const CEconItemRarityDefinition* pItemRarity = GetItemSchema()->GetRarityDefinition( pTempItemDef->GetRarity() );
  2640. if ( pItemRarity )
  2641. {
  2642. colorRarity = pItemRarity->GetAttribColor();
  2643. }
  2644. AddDescLine(
  2645. CConstructLocalizedString( LOCCHAR("%s1%s2"), pCheckmark,
  2646. CEconItemLocalizedFullNameGenerator(
  2647. pLocalizationProvider,
  2648. pTempItemDef,
  2649. m_pHashContext == NULL
  2650. ).GetFullName() ),
  2651. colorRarity,
  2652. eFlag,
  2653. NULL,
  2654. pTempItemDef->GetDefinitionIndex()
  2655. );
  2656. }
  2657. }
  2658. }
  2659. // --------------------------------------------------------------------------
  2660. // Purpose:
  2661. // --------------------------------------------------------------------------
  2662. void CEconItemDescription::Generate_ExpirationDesc( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem )
  2663. {
  2664. Assert( pLocalizationProvider );
  2665. Assert( pEconItem );
  2666. const CEconItemDefinition *pItemDef = pEconItem->GetItemDefinition();
  2667. if ( !pItemDef )
  2668. return;
  2669. // Look for schema-specified static expiration date.
  2670. RTime32 timeSchemaExpiration = pItemDef->GetExpirationDate();
  2671. // Look also for a dynamic attribute -- this could come from item tryouts.
  2672. static CSchemaAttributeDefHandle pAttrDef_ExpirationDate( "expiration date" );
  2673. RTime32 timeAttrExpiration = 0;
  2674. pEconItem->FindAttribute( pAttrDef_ExpirationDate, &timeAttrExpiration ); // if we don't have the attribute we'll use our starting value of 0
  2675. // Which will have us expire first?
  2676. RTime32 timeExpiration = MAX( timeSchemaExpiration, timeAttrExpiration );
  2677. // If we still don't have an expiration date we don't display anything.
  2678. if ( !timeExpiration )
  2679. return;
  2680. AddEmptyDescLine();
  2681. #ifdef TF_CLIENT_DLL
  2682. // is this a loaner item?
  2683. if ( GetAssociatedQuestItemID( pEconItem ) != INVALID_ITEM_ID )
  2684. {
  2685. AddDescLine( pLocalizationProvider->Find( "#Attrib_LoanerItemExpirationDate" ),
  2686. ATTRIB_COL_NEGATIVE,
  2687. kDescLineFlag_Misc );
  2688. }
  2689. else
  2690. #endif // TF_CLIENT_DLL
  2691. {
  2692. CLocalizedRTime32 time = { timeExpiration, false, pLocalizationProvider TF_ANTI_IDLEBOT_VERIFICATION_ONLY_COMMA TF_ANTI_IDLEBOT_VERIFICATION_ONLY_ARG( m_pHashContext ) };
  2693. AddDescLine( CConstructLocalizedString( pLocalizationProvider->Find( "#Attrib_ExpirationDate" ),
  2694. time ),
  2695. ATTRIB_COL_NEGATIVE,
  2696. kDescLineFlag_Misc );
  2697. }
  2698. }
  2699. void CEconItemDescription::Generate_DropPeriodDesc( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem )
  2700. {
  2701. Assert( pLocalizationProvider );
  2702. Assert( pEconItem );
  2703. static CSchemaAttributeDefHandle pAttr_EndDropDate( "end drop date" );
  2704. // See if we have the drop date period end attribute
  2705. CAttribute_String value;
  2706. if ( !FindAttribute( pEconItem, pAttr_EndDropDate, &value ) )
  2707. return;
  2708. AddEmptyDescLine();
  2709. // Convert the string value to an RTime32
  2710. RTime32 endDate = CRTime::RTime32FromString( value.value().c_str() );
  2711. // Is the time before or after now? Use different strings for each
  2712. const char* pszDropString = endDate > CRTime::RTime32TimeCur()
  2713. ? "#Attrib_DropPeriodComing"
  2714. : "#Attrib_DropPeriodPast";
  2715. CLocalizedRTime32 time = { endDate, false, pLocalizationProvider TF_ANTI_IDLEBOT_VERIFICATION_ONLY_COMMA TF_ANTI_IDLEBOT_VERIFICATION_ONLY_ARG( m_pHashContext ) };
  2716. AddDescLine( CConstructLocalizedString( pLocalizationProvider->Find( pszDropString ),
  2717. time ),
  2718. ATTRIB_COL_LEVEL,
  2719. kDescLineFlag_Misc );
  2720. }
  2721. // --------------------------------------------------------------------------
  2722. // Purpose:
  2723. // --------------------------------------------------------------------------
  2724. #ifdef TF_CLIENT_DLL
  2725. extern ConVar cl_showbackpackrarities;
  2726. extern ConVar cl_show_market_data_on_items;
  2727. #endif
  2728. void CEconItemDescription::Generate_MarketInformation( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem )
  2729. {
  2730. // Deprecated;
  2731. // We now have right click go to market
  2732. return;
  2733. //
  2734. // Assert( pLocalizationProvider );
  2735. // Assert( pEconItem );
  2736. //
  2737. // // Early-out to avoid doing more expensive name lookups for items that can't possibly have
  2738. // // entries.
  2739. // if ( !pEconItem->IsMarketable() )
  2740. // return;
  2741. //
  2742. // // For now, Market information is only shown on clients, and even then only sometimes.
  2743. //#ifdef CLIENT_DLL
  2744. //#if TF_ANTI_IDLEBOT_VERIFICATION
  2745. // // Don't generate this client-only information when we're trying to match GC output.
  2746. // if ( m_pHashContext )
  2747. // return;
  2748. //#endif // TF_ANTI_IDLEBOT_VERIFICATION
  2749. //
  2750. //#ifdef TF_CLIENT_DLL
  2751. // if ( cl_show_market_data_on_items.GetInt() == 0 )
  2752. // return;
  2753. //
  2754. // if ( cl_show_market_data_on_items.GetInt() == 1 && cl_showbackpackrarities.GetInt() != 2 )
  2755. // return;
  2756. //#endif // TF_CLIENT_DLL
  2757. //
  2758. // steam_market_gc_identifier_t ident;
  2759. // ident.m_unDefIndex = pEconItem->GetItemDefIndex();
  2760. // ident.m_unQuality = pEconItem->GetQuality();
  2761. //
  2762. // const client_market_data_t *pClientMarketData = GetClientMarketData( ident );
  2763. // if ( !pClientMarketData )
  2764. // return;
  2765. //
  2766. // locchar_t loc_Price[ kLocalizedPriceSizeInChararacters ];
  2767. // MakeMoneyString( loc_Price, ARRAYSIZE( loc_Price ), pClientMarketData->m_unLowestPrice, EconUI()->GetStorePanel()->GetCurrency() );
  2768. //
  2769. // AddEmptyDescLine();
  2770. // AddDescLine( CConstructLocalizedString( pLocalizationProvider->Find( "#Econ_MarketTooltipFormat" ),
  2771. // pClientMarketData->m_unQuantityAvailable,
  2772. // loc_Price ),
  2773. // ATTRIB_COL_POSITIVE,
  2774. // kDescLineFlag_Misc );
  2775. //#endif // CLIENT_DLL
  2776. }
  2777. // --------------------------------------------------------------------------
  2778. // Purpose:
  2779. // --------------------------------------------------------------------------
  2780. struct localized_localplayer_line_t
  2781. {
  2782. localized_localplayer_line_t( const char *pLocalizationKey, attrib_colors_t eAttribColor, const char *pLocalizationSubKey = NULL )
  2783. : m_pLocalizationKey( pLocalizationKey )
  2784. , m_pLocalizationSubKey( pLocalizationSubKey )
  2785. , m_eAttribColor( eAttribColor )
  2786. {
  2787. //
  2788. }
  2789. const char *m_pLocalizationKey;
  2790. const char *m_pLocalizationSubKey;
  2791. attrib_colors_t m_eAttribColor;
  2792. };
  2793. void CEconItemDescription::Generate_FlagsAttributes( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem )
  2794. {
  2795. Assert( pLocalizationProvider );
  2796. Assert( pEconItem );
  2797. const CEconItemDefinition *pItemDef = pEconItem->GetItemDefinition();
  2798. if ( pItemDef && pItemDef->GetEconTool() && pItemDef->GetEconTool()->ShouldDisplayQuantity( pEconItem ) )
  2799. {
  2800. AddEmptyDescLine();
  2801. AddDescLine( CConstructLocalizedString( pLocalizationProvider->Find( "#Attrib_LimitedUse" ), (uint32)pEconItem->GetQuantity() ),
  2802. ATTRIB_COL_LIMITED_USE,
  2803. kDescLineFlag_LimitedUse );
  2804. }
  2805. CUtlVector<localized_localplayer_line_t> vecLines;
  2806. // Is this item in use? (ie., being used as part of a cross-game trade)
  2807. if ( pEconItem->GetInUse() )
  2808. {
  2809. vecLines.AddToTail( localized_localplayer_line_t( "#Attrib_InUse", ATTRIB_COL_NEUTRAL ) );
  2810. }
  2811. static CSchemaAttributeDefHandle pAttrDef_TradableAfter( "tradable after date" );
  2812. static CSchemaAttributeDefHandle pAttrDef_ToolEscrowUntil( "tool escrow until date" );
  2813. static CSchemaAttributeDefHandle pAttrDef_AlwaysTradableAndUsableInCrafting( "always tradable" );
  2814. uint32 unTradeTime = 0,
  2815. unEscrowTime = 0;
  2816. const bool bHasTradableAfterDate = pEconItem->FindAttribute( pAttrDef_TradableAfter, &unTradeTime );
  2817. const bool bHasToolEscrowUntilDate = pEconItem->FindAttribute( pAttrDef_ToolEscrowUntil, &unEscrowTime );
  2818. const bool bHasExpiringTimer = bHasTradableAfterDate || bHasToolEscrowUntilDate;
  2819. #ifdef CLIENT_DLL
  2820. const bool bIsStoreItem = IsStorePreviewItem( pEconItem );
  2821. const bool bIsPreviewItem = pEconItem->GetFlags() & kEconItemFlagClient_Preview;
  2822. if ( bIsStoreItem || bIsPreviewItem )
  2823. {
  2824. #if TF_ANTI_IDLEBOT_VERIFICATION
  2825. if ( !m_pHashContext )
  2826. #endif // TF_ANTI_IDLEBOT_VERIFICATION
  2827. {
  2828. // Does this item come with other packages on Steam?
  2829. const econ_store_entry_t *pStoreEntry = GetEconPriceSheet() ? GetEconPriceSheet()->GetEntry( pItemDef->GetDefinitionIndex() ) : NULL;
  2830. if ( pStoreEntry && pStoreEntry->GetGiftSteamPackageID() != 0 )
  2831. {
  2832. const char *pszSteamPackageLocalizationToken = GetItemSchema()->GetSteamPackageLocalizationToken( pStoreEntry->GetGiftSteamPackageID() );
  2833. if ( pszSteamPackageLocalizationToken )
  2834. {
  2835. vecLines.AddToTail( localized_localplayer_line_t( "#Attrib_Store_IncludesSteamGiftPackage", ATTRIB_COL_POSITIVE, pszSteamPackageLocalizationToken ) );
  2836. vecLines.AddToTail( localized_localplayer_line_t( NULL, ATTRIB_COL_POSITIVE ) );
  2837. }
  2838. }
  2839. // While the above apply to store *and* preview items, the below only apply to store items.
  2840. if ( bIsStoreItem )
  2841. {
  2842. // Don't display this line for map stamps because they can't be traded.
  2843. if ( pItemDef && pItemDef->GetItemClass() && !FStrEq( pItemDef->GetItemClass(), "map_token" ) )
  2844. {
  2845. static CSchemaAttributeDefHandle pAttrib_CannotTrade( "cannot trade" );
  2846. Assert( pAttrib_CannotTrade );
  2847. // Some items cannot ever be traded, so don't indicate to the users that they'll be tradeable after a few days.
  2848. if ( !FindAttribute( pItemDef, pAttrib_CannotTrade ) )
  2849. {
  2850. vecLines.AddToTail( localized_localplayer_line_t( "#Attrib_Store_TradableAfterDate", ATTRIB_COL_NEGATIVE ) );
  2851. }
  2852. if ( pItemDef->GetEconTool() && pItemDef->GetEconTool()->RequiresToolEscrowPeriod() )
  2853. {
  2854. vecLines.AddToTail( localized_localplayer_line_t( "#Attrib_Store_ToolEscrowUntilDate", ATTRIB_COL_NEGATIVE ) );
  2855. }
  2856. }
  2857. if ( !pItemDef || (pItemDef->GetCapabilities() & ITEM_CAP_CAN_BE_CRAFTED_IF_PURCHASED) == 0 )
  2858. {
  2859. if ( pItemDef->IsBundle() )
  2860. {
  2861. vecLines.AddToTail( localized_localplayer_line_t( "#Attrib_CannotCraftWeapons", ATTRIB_COL_NEGATIVE ) );
  2862. }
  2863. else
  2864. {
  2865. vecLines.AddToTail( localized_localplayer_line_t( "#Attrib_CannotCraft", ATTRIB_COL_NEGATIVE ) );
  2866. }
  2867. }
  2868. }
  2869. }
  2870. }
  2871. else
  2872. #endif // CLIENT_DLL
  2873. if ( bHasExpiringTimer )
  2874. {
  2875. if ( unTradeTime > CRTime::RTime32TimeCur() )
  2876. {
  2877. AddAttributeDescription( pLocalizationProvider, pAttrDef_TradableAfter, unTradeTime );
  2878. }
  2879. if ( unEscrowTime > CRTime::RTime32TimeCur() )
  2880. {
  2881. AddAttributeDescription( pLocalizationProvider, pAttrDef_ToolEscrowUntil, unEscrowTime );
  2882. }
  2883. if ( !pEconItem->IsUsableInCrafting() )
  2884. {
  2885. vecLines.AddToTail( localized_localplayer_line_t( "#Attrib_CannotCraft", ATTRIB_COL_NEUTRAL ) );
  2886. }
  2887. }
  2888. else if ( pEconItem->FindAttribute( pAttrDef_AlwaysTradableAndUsableInCrafting ) && pEconItem->IsTradable() )
  2889. {
  2890. // do nothing if we are always tradable or usable in crafting
  2891. //
  2892. // some items are marked as "always_tradable" in their itemDef but the specific item may have the
  2893. // "non_economy" flag, so we need to also check that this specific item is tradable before doing nothing
  2894. }
  2895. else
  2896. {
  2897. const int32 iQuality = pEconItem->GetQuality();
  2898. const eEconItemOrigin eOrigin = pEconItem->GetOrigin();
  2899. if ( iQuality >= AE_COMMUNITY && iQuality <= AE_SELFMADE )
  2900. {
  2901. vecLines.AddToTail( localized_localplayer_line_t( "#Attrib_SpecialItem", ATTRIB_COL_NEUTRAL ) );
  2902. }
  2903. else if ( eOrigin == kEconItemOrigin_Achievement )
  2904. {
  2905. vecLines.AddToTail( localized_localplayer_line_t( "#Attrib_AchievementItem", ATTRIB_COL_NEUTRAL ) );
  2906. }
  2907. else if ( eOrigin == kEconItemOrigin_CollectionReward )
  2908. {
  2909. vecLines.AddToTail( localized_localplayer_line_t( "#Attrib_CollectionReward", ATTRIB_COL_NEUTRAL ) );
  2910. }
  2911. else if ( eOrigin == kEconItemOrigin_PreviewItem )
  2912. {
  2913. vecLines.AddToTail( localized_localplayer_line_t( "#Attrib_PreviewItem", ATTRIB_COL_NEUTRAL ) );
  2914. }
  2915. else if ( eOrigin == kEconItemOrigin_QuestLoanerItem )
  2916. {
  2917. vecLines.AddToTail( localized_localplayer_line_t( "#Attrib_LoanerItem", ATTRIB_COL_NEUTRAL ) );
  2918. }
  2919. else if ( eOrigin == kEconItemOrigin_Invalid )
  2920. {
  2921. // do nothing, but skip the below "cannot trade/cannot craft" block below"
  2922. }
  2923. else if ( (pEconItem->GetFlags() & kEconItemFlag_NonEconomy) != 0 )
  2924. {
  2925. vecLines.AddToTail( localized_localplayer_line_t( "#Attrib_NonEconomyItem", ATTRIB_COL_NEUTRAL ) );
  2926. }
  2927. else
  2928. {
  2929. const bool bIsTradable = pEconItem->IsTradable(),
  2930. bIsCraftable = pEconItem->IsUsableInCrafting();
  2931. if ( !bIsTradable && !bIsCraftable )
  2932. {
  2933. vecLines.AddToTail( localized_localplayer_line_t( "#Attrib_CannotTradeOrCraft", ATTRIB_COL_NEUTRAL ) );
  2934. }
  2935. else
  2936. {
  2937. if ( !bIsTradable )
  2938. {
  2939. vecLines.AddToTail( localized_localplayer_line_t( "#Attrib_CannotTrade", ATTRIB_COL_NEUTRAL ) );
  2940. }
  2941. if ( !bIsCraftable )
  2942. {
  2943. vecLines.AddToTail( localized_localplayer_line_t( "#Attrib_CannotCraft", ATTRIB_COL_NEUTRAL ) );
  2944. }
  2945. }
  2946. }
  2947. }
  2948. if ( vecLines.Count() > 0 )
  2949. {
  2950. const locchar_t *loc_AttribFormat_AdditionalNode = pLocalizationProvider->Find( "#AttribFormat_AdditionalNote" );
  2951. if ( loc_AttribFormat_AdditionalNode )
  2952. {
  2953. AddEmptyDescLine();
  2954. FOR_EACH_VEC( vecLines, i )
  2955. {
  2956. const char *pszLocalizationKey = vecLines[i].m_pLocalizationKey;
  2957. const char *pszLocalizationSubKey = vecLines[i].m_pLocalizationSubKey;
  2958. if ( pszLocalizationKey )
  2959. {
  2960. AddDescLine( pszLocalizationSubKey ?
  2961. CConstructLocalizedString( pLocalizationProvider->Find( pszLocalizationKey ), pLocalizationProvider->Find( pszLocalizationSubKey ) ): // has subtoken, doesn't use additional note format
  2962. CConstructLocalizedString( loc_AttribFormat_AdditionalNode, pLocalizationProvider->Find( pszLocalizationKey ) ), // no subtoken, uses base additional note format
  2963. vecLines[i].m_eAttribColor,
  2964. kDescLineFlag_Misc );
  2965. }
  2966. else
  2967. {
  2968. AddEmptyDescLine();
  2969. }
  2970. }
  2971. }
  2972. }
  2973. }
  2974. // --------------------------------------------------------------------------
  2975. // Purpose:
  2976. // --------------------------------------------------------------------------
  2977. bool CEconItemDescription::CVisibleAttributeDisplayer::OnIterateAttributeValue( const CEconItemAttributeDefinition *pAttrDef, attrib_value_t value )
  2978. {
  2979. if ( !pAttrDef->IsHidden() )
  2980. {
  2981. attrib_iterator_value_t attrVal = { pAttrDef, value };
  2982. m_vecAttributes.AddToTail( attrVal );
  2983. }
  2984. return true;
  2985. }
  2986. void CEconItemDescription::CVisibleAttributeDisplayer::SortAttributes()
  2987. {
  2988. // We need to make sure we process attributes in the same order when iterating on the GC and the client
  2989. // when looking for agreement. We take advantage of this to also sort our attributes into a coherent
  2990. // order for display -- first come neutral attributes, then positive, then negative. In the event of a
  2991. // tie, we sort by attribute index, which is arbitrary but consistent across the client/GC.
  2992. struct AttributeValueSorter
  2993. {
  2994. static int sSort( const attrib_iterator_value_t *pA, const attrib_iterator_value_t *pB )
  2995. {
  2996. const int iEffectTypeDelta = pA->m_pAttrDef->GetEffectType() - pB->m_pAttrDef->GetEffectType();
  2997. if ( iEffectTypeDelta != 0 )
  2998. return iEffectTypeDelta;
  2999. return pA->m_pAttrDef->GetDefinitionIndex() - pB->m_pAttrDef->GetDefinitionIndex();
  3000. }
  3001. };
  3002. m_vecAttributes.Sort( &AttributeValueSorter::sSort );
  3003. }
  3004. void CEconItemDescription::CVisibleAttributeDisplayer::Finalize( const IEconItemInterface *pEconItem, CEconItemDescription *pEconItemDescription, const CLocalizationProvider *pLocalizationProvider )
  3005. {
  3006. // HACK so we dont show series number on select crates since they are self describing (Event Crates, Collection Crates)
  3007. static CSchemaAttributeDefHandle pAttrDef_SupplyCrateSeries( "set supply crate series" );
  3008. static CSchemaAttributeDefHandle pAttrDef_HideSeries( "hide crate series number" );
  3009. FOR_EACH_VEC( m_vecAttributes, i )
  3010. {
  3011. if ( pEconItem && m_vecAttributes[i].m_pAttrDef == pAttrDef_SupplyCrateSeries && pEconItem->FindAttribute( pAttrDef_HideSeries ) )
  3012. continue;
  3013. pEconItemDescription->AddAttributeDescription( pLocalizationProvider, m_vecAttributes[i].m_pAttrDef, m_vecAttributes[i].m_value );
  3014. }
  3015. }
  3016. // --------------------------------------------------------------------------
  3017. // Purpose:
  3018. // --------------------------------------------------------------------------
  3019. void CEconItemDescription::Generate_VisibleAttributes( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem )
  3020. {
  3021. Assert( pLocalizationProvider );
  3022. Assert( pEconItem );
  3023. CVisibleAttributeDisplayer AttributeDisplayer;
  3024. pEconItem->IterateAttributes( &AttributeDisplayer );
  3025. AttributeDisplayer.SortAttributes();
  3026. AttributeDisplayer.Finalize( pEconItem, this, pLocalizationProvider );
  3027. }
  3028. // --------------------------------------------------------------------------
  3029. void CEconItemDescription::Generate_DirectX8Warning( const CLocalizationProvider *pLocalizationProvider, const IEconItemInterface *pEconItem )
  3030. {
  3031. #ifdef CLIENT_DLL
  3032. static ConVarRef mat_dxlevel( "mat_dxlevel" );
  3033. const CEconItemDefinition *pEconItemDefinition = pEconItem->GetItemDefinition();
  3034. // If less than 90, we�re in DX8 mode.
  3035. // Display warning if you are looking at a painthit item or case
  3036. if ( mat_dxlevel.GetInt() < 90 && pEconItemDefinition && ( pEconItemDefinition->GetItemCollectionDefinition() || pEconItemDefinition->GetCollectionReference() ) )
  3037. {
  3038. AddEmptyDescLine();
  3039. AddDescLine( pLocalizationProvider->Find( "#Attrib_DirectX8Warning" ),
  3040. ATTRIB_COL_NEGATIVE,
  3041. kDescLineFlag_Misc );
  3042. }
  3043. #endif
  3044. }
  3045. bool CEconItemDescription::CRecipeNameAttributeDisplayer::OnIterateAttributeValue( const CEconItemAttributeDefinition *pAttrDef, attrib_value_t value )
  3046. {
  3047. if ( pAttrDef->CanAffectRecipeComponentName() )
  3048. {
  3049. return CVisibleAttributeDisplayer::OnIterateAttributeValue( pAttrDef, value );
  3050. }
  3051. return true;
  3052. }
  3053. // --------------------------------------------------------------------------
  3054. // Purpose:
  3055. // --------------------------------------------------------------------------
  3056. static attrib_colors_t GetAttributeDefaultColor( const CEconItemAttributeDefinition *pAttribDef )
  3057. {
  3058. // positive attribute?
  3059. switch ( pAttribDef->GetEffectType() )
  3060. {
  3061. case ATTRIB_EFFECT_NEUTRAL: return ATTRIB_COL_NEUTRAL;
  3062. case ATTRIB_EFFECT_POSITIVE: return ATTRIB_COL_POSITIVE;
  3063. case ATTRIB_EFFECT_NEGATIVE: return ATTRIB_COL_NEGATIVE;
  3064. case ATTRIB_EFFECT_STRANGE: return ATTRIB_COL_STRANGE;
  3065. case ATTRIB_EFFECT_UNUSUAL: return ATTRIB_COL_UNUSUAL;
  3066. }
  3067. // hell if we know
  3068. return ATTRIB_COL_NEUTRAL;
  3069. }
  3070. // --------------------------------------------------------------------------
  3071. // Purpose:
  3072. // --------------------------------------------------------------------------
  3073. void CEconAttributeDescription::InternalConstruct
  3074. (
  3075. const CLocalizationProvider *pLocalizationProvider,
  3076. const CEconItemAttributeDefinition *pAttribDef,
  3077. attrib_value_t value,
  3078. TF_ANTI_IDLEBOT_VERIFICATION_ONLY_ARG( MD5Context_t *pHashContext ) TF_ANTI_IDLEBOT_VERIFICATION_ONLY_COMMA
  3079. IAccountPersonaLocalizer *pOptionalAccountPersonaLocalizer
  3080. )
  3081. {
  3082. Assert( pAttribDef != NULL );
  3083. const float& value_as_float = (float&)value;
  3084. const uint32& value_as_uint32 = (uint32&)value;
  3085. // Calculate our color first -- if we don't know what to do, we'll wind up as neutral.
  3086. m_eDefaultColor = GetAttributeDefaultColor( pAttribDef );
  3087. // Early out abort if we don't have a localization string for this attribute.
  3088. locchar_t *loc_String = pAttribDef->GetDescriptionString() && pLocalizationProvider
  3089. ? pLocalizationProvider->Find( pAttribDef->GetDescriptionString() )
  3090. : NULL;
  3091. if ( !loc_String )
  3092. return;
  3093. char szAttrShortDescToken[MAX_PATH];
  3094. V_sprintf_safe( szAttrShortDescToken, "%s%s", pAttribDef->GetDescriptionString(), "_shortdesc" );
  3095. locchar_t *loc_ShortString = pLocalizationProvider
  3096. ? pLocalizationProvider->Find( szAttrShortDescToken )
  3097. : NULL;
  3098. // How do we format an attribute value of this type?
  3099. switch ( pAttribDef->GetDescriptionFormat() )
  3100. {
  3101. case ATTDESCFORM_VALUE_IS_ADDITIVE_PERCENTAGE:
  3102. m_loc_sValue = CLocalizedStringArg<float>( value_as_float * 100.0f ).GetLocArg();
  3103. break;
  3104. case ATTDESCFORM_VALUE_IS_ACCOUNT_ID:
  3105. #ifdef CLIENT_DLL
  3106. // If this assert fires, it means that the client fed in an attribute that should be localized
  3107. // as a Steam persona name but didn't feed it any way to get that information. The GC won't
  3108. // assert, but also won't generate anything for the attribute text.
  3109. //
  3110. // It's still totally fine to pass in NULL for the persona localizer as long as you don't
  3111. // expect to have any attributes that have account IDs.
  3112. Assert( pOptionalAccountPersonaLocalizer );
  3113. #endif
  3114. if ( pOptionalAccountPersonaLocalizer )
  3115. {
  3116. m_loc_sValue = pOptionalAccountPersonaLocalizer->FindAccountPersonaName( value_as_uint32 );
  3117. }
  3118. break;
  3119. case ATTDESCFORM_VALUE_IS_ADDITIVE:
  3120. m_loc_sValue = pAttribDef->IsStoredAsFloat()
  3121. ? CLocalizedStringArg<float>( value_as_float ).GetLocArg()
  3122. : CLocalizedStringArg<uint32>( value_as_uint32 ).GetLocArg();
  3123. break;
  3124. case ATTDESCFORM_VALUE_IS_INVERTED_PERCENTAGE:
  3125. if ( value_as_float < 1.0 )
  3126. {
  3127. m_loc_sValue = CLocalizedStringArg<float>( (1.0 - value_as_float) * 100.0f ).GetLocArg();
  3128. break;
  3129. }
  3130. // We intentionally fall through when value_as_float >= 1.0f to treat it the same as "value as
  3131. // percentage".
  3132. case ATTDESCFORM_VALUE_IS_PERCENTAGE:
  3133. m_loc_sValue = CLocalizedStringArg<float>( (value_as_float * 100.0f) - 100.0f ).GetLocArg();
  3134. break;
  3135. case ATTDESCFORM_VALUE_IS_DATE:
  3136. {
  3137. bool bUseGMT = false;
  3138. #ifdef PROJECT_TF
  3139. static CSchemaAttributeDefHandle pAttribDef_SetEmployeeNumber( "custom employee number" );
  3140. // only use GMT for custom employee number -- not doing this generated a bunch of support
  3141. // tickets because items were granted based on GC time but would display local time, causing
  3142. // people on the border to think they deserved a better badge, etc.
  3143. bUseGMT = (pAttribDef == pAttribDef_SetEmployeeNumber);
  3144. #endif // PROJECT_TF
  3145. CLocalizedRTime32 time = { value_as_uint32, bUseGMT, pLocalizationProvider TF_ANTI_IDLEBOT_VERIFICATION_ONLY_COMMA TF_ANTI_IDLEBOT_VERIFICATION_ONLY_ARG( pHashContext ) };
  3146. m_loc_sValue = CLocalizedStringArg<CLocalizedRTime32>( time ).GetLocArg();
  3147. break;
  3148. }
  3149. case ATTDESCFORM_VALUE_IS_PARTICLE_INDEX:
  3150. {
  3151. // This is a horrible, horrible line of code. It exists because old particle references are
  3152. // ints stored as floats as float bit patterns and new particle references are ints stored
  3153. // as ints all the way through.
  3154. 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)
  3155. if ( utf8_ParticleKeyName.IsEmpty() )
  3156. return;
  3157. m_loc_sValue = pLocalizationProvider->Find( utf8_ParticleKeyName.Get() );
  3158. break;
  3159. }
  3160. case ATTDESCFORM_VALUE_IS_KILLSTREAKEFFECT_INDEX:
  3161. {
  3162. 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)
  3163. if ( utf8_KeyName.IsEmpty() )
  3164. return;
  3165. m_loc_sValue = pLocalizationProvider->Find( utf8_KeyName.Get() );
  3166. break;
  3167. }
  3168. case ATTDESCFORM_VALUE_IS_KILLSTREAK_IDLEEFFECT_INDEX:
  3169. {
  3170. 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)
  3171. if ( utf8_KeyName.IsEmpty() )
  3172. return;
  3173. m_loc_sValue = pLocalizationProvider->Find( utf8_KeyName.Get() );
  3174. break;
  3175. }
  3176. // Don't output any value for bitmasks, but let the attribute text display.
  3177. case ATTDESCFORM_VALUE_IS_OR:
  3178. break;
  3179. default:
  3180. #ifdef CLIENT_DLL
  3181. // Only assert on the client -- the GC will just silently fail rather than crash if we ever run into
  3182. // this case, but if we are adding a new display type this will help us catch a reason why it isn't
  3183. // showing up.
  3184. Assert( !"Unhandled attribute value display type in CEconAttributeDescription." );
  3185. // Anywhere besides the client, we intentionally fall through to return immediately.
  3186. #endif
  3187. case ATTDESCFORM_VALUE_IS_ITEM_DEF: // referencing definitions is handled per-attribute
  3188. return;
  3189. case ATTDESCFORM_VALUE_IS_FROM_LOOKUP_TABLE:
  3190. {
  3191. const char *pszLocalizationToken = GetItemSchema()->FindStringTableEntry( pAttribDef->GetDefinitionName(), (int)value_as_float );
  3192. if ( !pszLocalizationToken )
  3193. return;
  3194. const locchar_t *loc_Entry = pLocalizationProvider->Find( pszLocalizationToken );
  3195. if ( !loc_Entry )
  3196. return;
  3197. m_loc_sValue = loc_Entry;
  3198. break;
  3199. }
  3200. }
  3201. // Some attributes have a short description for the upgrade
  3202. if ( loc_ShortString )
  3203. {
  3204. m_loc_sShortValue = CConstructLocalizedString( loc_ShortString, m_loc_sValue.Get() );
  3205. }
  3206. // Combine the value string we just generated with the localized display for that value. (ie., the value
  3207. // might be "10" and the display would be "health is increased by 10%".)
  3208. m_loc_sValue = CConstructLocalizedString( loc_String, m_loc_sValue.Get() );
  3209. // Is this an attribute that needs a custom wrapper around the default attribute text? (ie.,
  3210. // if our string was "Damage +10%" we want that to be "(only on Hightower: Damage +10%)")
  3211. if ( pAttribDef->GetUserGenerationType() )
  3212. {
  3213. const locchar_t *locUGTLocalizationKey = pLocalizationProvider->Find( CFmtStr( "#Econ_Attrib_UserGeneratedWrapper_%i", pAttribDef->GetUserGenerationType() ).Get() );
  3214. if ( locUGTLocalizationKey )
  3215. {
  3216. m_loc_sValue = CConstructLocalizedString( locUGTLocalizationKey, m_loc_sValue.Get() );
  3217. }
  3218. }
  3219. // If there's no short description, just copy the normal one
  3220. if ( !loc_ShortString )
  3221. {
  3222. m_loc_sShortValue = m_loc_sValue;
  3223. }
  3224. }
  3225. // --------------------------------------------------------------------------
  3226. // Purpose:
  3227. // --------------------------------------------------------------------------
  3228. void CEconItemDescription::AddAttributeDescription( const CLocalizationProvider *pLocalizationProvider, const CEconItemAttributeDefinition *pAttribDef, attrib_value_t value, attrib_colors_t eOverrideDisplayColor /* = NUM_ATTRIB_COLORS */ )
  3229. {
  3230. Assert( pLocalizationProvider );
  3231. Assert( pAttribDef );
  3232. CEconAttributeDescription AttrDesc( pLocalizationProvider,
  3233. pAttribDef,
  3234. value,
  3235. TF_ANTI_IDLEBOT_VERIFICATION_ONLY_ARG( m_pHashContext ) TF_ANTI_IDLEBOT_VERIFICATION_ONLY_COMMA
  3236. this );
  3237. if ( AttrDesc.GetDescription().IsEmpty() )
  3238. return;
  3239. // Is this an attribute that needs a custom wrapper around the default attribute text? (ie.,
  3240. // if our string was "Damage +10%" we want that to be "(only on Hightower: Damage +10%)")
  3241. attrib_colors_t eDefaultAttribColor = GetAttributeDefaultColor( pAttribDef );
  3242. #ifdef TF_CLIENT_DLL
  3243. enum
  3244. {
  3245. kUserGeneratedAttributeType_None = 0,
  3246. kUserGeneratedAttributeType_MVMEngineering = 1,
  3247. kUserGeneratedAttributeType_HalloweenSpell = 2
  3248. };
  3249. // On TF, these user-generated attributes can be from upgrade cards which only apply in MvM.
  3250. // We then colorize them based on whether they'll be active, with the caveat that out-of-game
  3251. // views always say yes (GC, loadout when not on a server, etc.).
  3252. if ( pAttribDef->GetUserGenerationType() == kUserGeneratedAttributeType_MVMEngineering && TFGameRules() && !TFGameRules()->IsMannVsMachineMode() )
  3253. {
  3254. eDefaultAttribColor = ATTRIB_COL_ITEMSET_MISSING;
  3255. }
  3256. // They can also be from Halloween spells. These are intended to expire after Halloween in any
  3257. // event, but for display purposes they'll appear in grey unless the holiday is active.
  3258. else if ( pAttribDef->GetUserGenerationType() == kUserGeneratedAttributeType_HalloweenSpell && !EconHolidays_IsHolidayActive( kHoliday_Halloween, CRTime::RTime32TimeCur() ) )
  3259. {
  3260. eDefaultAttribColor = ATTRIB_COL_ITEMSET_MISSING;
  3261. }
  3262. #endif // TF_CLIENT_DLL
  3263. AddDescLine( AttrDesc.GetDescription().Get(),
  3264. eOverrideDisplayColor != NUM_ATTRIB_COLORS ? // are we overriding the output color?
  3265. eOverrideDisplayColor : // we are
  3266. eDefaultAttribColor, // fall back to normal attribute color
  3267. kDescLineFlag_Attribute );
  3268. }
  3269. // --------------------------------------------------------------------------
  3270. // Purpose:
  3271. // --------------------------------------------------------------------------
  3272. 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*/ )
  3273. {
  3274. CUtlVector<econ_item_description_line_t>& vecTargetDescLines = out_pOptionalDescLineDest ? *out_pOptionalDescLineDest : m_vecDescLines;
  3275. econ_item_description_line_t& line = vecTargetDescLines[ vecTargetDescLines.AddToTail() ];
  3276. line.eColor = eColor;
  3277. line.unMetaType = unMetaType;
  3278. line.sText = pString;
  3279. line.unDefIndex = unDefIndex;
  3280. line.bIsItemForSale = bIsItemForSale;
  3281. #if TF_ANTI_IDLEBOT_VERIFICATION
  3282. if ( m_pHashContext )
  3283. {
  3284. const int iLineCount = vecTargetDescLines.Count() + 1;
  3285. char verboseStringBuf[ k_VerboseStringBufferSize ];
  3286. TFDescription_HashDataMunge( m_pHashContext, iLineCount, m_bIsVerbose, BuildVerboseStrings( verboseStringBuf, m_bIsVerbose, "%d", iLineCount ) ); // which line did we just add?
  3287. TFDescription_HashDataMunge( m_pHashContext, line.eColor, m_bIsVerbose, BuildVerboseStrings( verboseStringBuf, m_bIsVerbose,"%d", line.eColor ) );
  3288. TFDescription_HashDataMunge( m_pHashContext, line.unMetaType, m_bIsVerbose, BuildVerboseStrings( verboseStringBuf, m_bIsVerbose, "%d", line.unMetaType ) );
  3289. #ifdef GC_DLL
  3290. COMPILE_TIME_ASSERT( sizeof( locchar_t ) == sizeof( char ) );
  3291. TFDescription_HashDataMungeContents( m_pHashContext, pString, StringFuncs<locchar_t>::Length( pString ) * sizeof( locchar_t ), m_bIsVerbose, pString );
  3292. #else
  3293. COMPILE_TIME_ASSERT( sizeof( locchar_t ) == sizeof( wchar_t ) );
  3294. CUtlConstString ansiString;
  3295. GLocalizationProvider()->ConvertLoccharToANSI( pString, &ansiString );
  3296. TFDescription_HashDataMungeContents( m_pHashContext, ansiString.Get(), StringFuncs<char>::Length( ansiString.Get() ) * sizeof( char ), m_bIsVerbose, ansiString.Get() );
  3297. #endif
  3298. }
  3299. #endif // TF_ANTI_IDLEBOT_VERIFICATION
  3300. }
  3301. // --------------------------------------------------------------------------
  3302. // Purpose:
  3303. // --------------------------------------------------------------------------
  3304. void CEconItemDescription::AddEmptyDescLine( CUtlVector<econ_item_description_line_t> *out_pOptionalDescLineDest )
  3305. {
  3306. AddDescLine( LOCCHAR(" "), ATTRIB_COL_NEUTRAL, kDescLineFlag_Empty, out_pOptionalDescLineDest );
  3307. }
  3308. // --------------------------------------------------------------------------
  3309. // Purpose:
  3310. // --------------------------------------------------------------------------
  3311. 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 */ )
  3312. {
  3313. Assert( pLocalizationToken );
  3314. const locchar_t *pTextToAdd = pLocalizationProvider->Find( pLocalizationToken );
  3315. if ( pTextToAdd )
  3316. {
  3317. AddDescLine( pTextToAdd, eColor, unMetaType, out_pOptionalDescLineDest, unDefIndex, bIsItemForSale );
  3318. }
  3319. else if ( pLocalizationToken && (pLocalizationToken[0] != '#') )
  3320. {
  3321. // If we couldn't localize correctly, we might be a string literal like "My temp item desc.". In
  3322. // this case, we use that string as-is.
  3323. CUtlConstStringBase<locchar_t> loc_sText;
  3324. pLocalizationProvider->ConvertUTF8ToLocchar( pLocalizationToken, &loc_sText );
  3325. AddDescLine( loc_sText.Get(), eColor, unMetaType, out_pOptionalDescLineDest, unDefIndex, bIsItemForSale );
  3326. }
  3327. else
  3328. {
  3329. // We couldn't localize this token, but also don't think it was a string meant to be user-facing so
  3330. // just silently fail.
  3331. }
  3332. #if TF_ANTI_IDLEBOT_VERIFICATION
  3333. if ( m_pHashContext )
  3334. {
  3335. TFDescription_HashDataMungeContents( m_pHashContext, pLocalizationToken, V_strlen( pLocalizationToken ), m_bIsVerbose, pLocalizationToken );
  3336. }
  3337. #endif // TF_ANTI_IDLEBOT_VERIFICATION
  3338. }
  3339. // --------------------------------------------------------------------------
  3340. // Purpose:
  3341. // --------------------------------------------------------------------------
  3342. class CGameItemDefinition_EconItemInterfaceWrapper : public CMaterialOverrideContainer< IEconItemInterface >
  3343. {
  3344. public:
  3345. CGameItemDefinition_EconItemInterfaceWrapper( const CEconItemDefinition *pEconItemDefinition, entityquality_t eQuality )
  3346. : m_pEconItemDefinition( pEconItemDefinition )
  3347. , m_eQuality( eQuality )
  3348. {
  3349. Assert( m_pEconItemDefinition );
  3350. }
  3351. virtual const GameItemDefinition_t *GetItemDefinition() const { return assert_cast<const GameItemDefinition_t *>( m_pEconItemDefinition ); }
  3352. virtual itemid_t GetID() const { return INVALID_ITEM_ID; }
  3353. virtual uint32 GetAccountID() const { return 0; }
  3354. virtual int32 GetQuality() const { return m_eQuality; }
  3355. virtual style_index_t GetStyle() const { return INVALID_STYLE_INDEX; }
  3356. virtual uint8 GetFlags() const { return 0; }
  3357. virtual eEconItemOrigin GetOrigin() const { return kEconItemOrigin_Invalid; }
  3358. virtual int GetQuantity() const { return 1; }
  3359. virtual uint32 GetItemLevel() const { return 0; }
  3360. virtual bool GetInUse() const { return false; }
  3361. virtual const char *GetCustomName() const { return NULL; }
  3362. virtual const char *GetCustomDesc() const { return NULL; }
  3363. virtual CEconItemPaintKitDefinition *GetCustomPainkKitDefinition( void ) const { return GetItemDefinition()->GetCustomPainkKitDefinition(); }
  3364. // IEconItemInterface attribute iteration interface. This is not meant to be used for
  3365. // attribute lookup! This is meant for anything that requires iterating over the full
  3366. // attribute list.
  3367. virtual void IterateAttributes( class IEconItemAttributeIterator *pIterator ) const OVERRIDE
  3368. {
  3369. Assert( pIterator );
  3370. m_pEconItemDefinition->IterateAttributes( pIterator );
  3371. }
  3372. private:
  3373. const CEconItemDefinition *m_pEconItemDefinition;
  3374. entityquality_t m_eQuality;
  3375. };
  3376. // --------------------------------------------------------------------------
  3377. // Purpose:
  3378. // --------------------------------------------------------------------------
  3379. CEconItemLocalizedFullNameGenerator::CEconItemLocalizedFullNameGenerator( const CLocalizationProvider *pLocalizationProvider, const CEconItemDefinition *pItemDef, bool bUseProperName, entityquality_t eQuality )
  3380. {
  3381. Assert( pItemDef );
  3382. CGameItemDefinition_EconItemInterfaceWrapper EconItemDefinitionWrapper( pItemDef, eQuality );
  3383. GenerateLocalizedFullItemName( m_loc_LocalizedItemName, pLocalizationProvider, &EconItemDefinitionWrapper, k_EGenerateLocalizedFullItemName_Default, bUseProperName );
  3384. }
  3385. // --------------------------------------------------------------------------
  3386. // Purpose:
  3387. // --------------------------------------------------------------------------
  3388. class CMarketNameGenerator_EconItemInterfaceWrapper : public IEconItemInterface
  3389. {
  3390. public:
  3391. CMarketNameGenerator_EconItemInterfaceWrapper( CEconItem *pItem )
  3392. : m_pItem( pItem )
  3393. {
  3394. Assert( m_pItem );
  3395. }
  3396. virtual const GameItemDefinition_t *GetItemDefinition() const { return m_pItem->GetItemDefinition(); }
  3397. virtual itemid_t GetID() const { return m_pItem->GetID(); }
  3398. virtual uint32 GetAccountID() const { return 0; }
  3399. virtual int32 GetQuality() const { return m_pItem->GetQuality(); }
  3400. virtual style_index_t GetStyle() const { return INVALID_STYLE_INDEX; }
  3401. virtual uint8 GetFlags() const { return 0; }
  3402. virtual eEconItemOrigin GetOrigin() const { return kEconItemOrigin_Invalid; }
  3403. virtual int GetQuantity() const { return 1; }
  3404. virtual uint32 GetItemLevel() const { return 0; }
  3405. virtual bool GetInUse() const { return false; }
  3406. virtual const char *GetCustomName() const { return NULL; }
  3407. virtual const char *GetCustomDesc() const { return NULL; }
  3408. virtual CEconItemPaintKitDefinition *GetCustomPainkKitDefinition( void ) const { return m_pItem->GetCustomPainkKitDefinition(); }
  3409. virtual bool GetCustomPaintKitWear( float &flWear ) const { return m_pItem->GetCustomPaintKitWear( flWear ); }
  3410. virtual IMaterial *GetMaterialOverride( int iTeam ) OVERRIDE { return m_pItem->GetMaterialOverride( iTeam ); }
  3411. // IEconItemInterface attribute iteration interface. This is not meant to be used for
  3412. // attribute lookup! This is meant for anything that requires iterating over the full
  3413. // attribute list.
  3414. virtual void IterateAttributes( class IEconItemAttributeIterator *pIterator ) const OVERRIDE
  3415. {
  3416. Assert( pIterator );
  3417. // Wrap their iterator in our iterator that will selectively let specific attributes
  3418. // get iterated on by the wrapped iterator.
  3419. CMarketNameGenerator_SelectiveAttributeIterator iteratorWrapper( pIterator );
  3420. m_pItem->IterateAttributes( &iteratorWrapper );
  3421. }
  3422. private:
  3423. CEconItem *m_pItem;
  3424. // Iterator class that wraps another iterator and selectively allows specific attributes to be
  3425. // iterated by the passed in iterator
  3426. class CMarketNameGenerator_SelectiveAttributeIterator : public IEconItemAttributeIterator
  3427. {
  3428. public:
  3429. CMarketNameGenerator_SelectiveAttributeIterator( IEconItemAttributeIterator* pIterator )
  3430. : m_pIterator( pIterator )
  3431. {}
  3432. virtual bool OnIterateAttributeValue( const CEconItemAttributeDefinition *pAttrDef, attrib_value_t value ) OVERRIDE
  3433. {
  3434. if( pAttrDef->CanAffectMarketName() )
  3435. {
  3436. m_pIterator->OnIterateAttributeValue( pAttrDef, value );
  3437. }
  3438. return true;
  3439. }
  3440. virtual bool OnIterateAttributeValue( const CEconItemAttributeDefinition *pAttrDef, float value ) OVERRIDE
  3441. {
  3442. if( pAttrDef->CanAffectMarketName() )
  3443. {
  3444. m_pIterator->OnIterateAttributeValue( pAttrDef, value );
  3445. }
  3446. return true;
  3447. }
  3448. virtual bool OnIterateAttributeValue( const CEconItemAttributeDefinition *pAttrDef, const uint64& value ) OVERRIDE
  3449. {
  3450. if( pAttrDef->CanAffectMarketName() )
  3451. {
  3452. m_pIterator->OnIterateAttributeValue( pAttrDef, value );
  3453. }
  3454. return true;
  3455. }
  3456. virtual bool OnIterateAttributeValue( const CEconItemAttributeDefinition *pAttrDef, const CAttribute_String& value ) OVERRIDE
  3457. {
  3458. if( pAttrDef->CanAffectMarketName() )
  3459. {
  3460. m_pIterator->OnIterateAttributeValue( pAttrDef, value );
  3461. }
  3462. return true;
  3463. }
  3464. virtual bool OnIterateAttributeValue( const CEconItemAttributeDefinition *pAttrDef, const CAttribute_DynamicRecipeComponent& value ) OVERRIDE
  3465. {
  3466. if( pAttrDef->CanAffectMarketName() )
  3467. {
  3468. m_pIterator->OnIterateAttributeValue( pAttrDef, value );
  3469. }
  3470. return true;
  3471. }
  3472. virtual bool OnIterateAttributeValue( const CEconItemAttributeDefinition *pAttrDef, const CAttribute_ItemSlotCriteria& value ) OVERRIDE
  3473. {
  3474. if( pAttrDef->CanAffectMarketName() )
  3475. {
  3476. m_pIterator->OnIterateAttributeValue( pAttrDef, value );
  3477. }
  3478. return true;
  3479. }
  3480. virtual bool OnIterateAttributeValue( const CEconItemAttributeDefinition *pAttrDef, const CAttribute_WorldItemPlacement& value ) OVERRIDE
  3481. {
  3482. if ( pAttrDef->CanAffectMarketName() )
  3483. {
  3484. m_pIterator->OnIterateAttributeValue( pAttrDef, value );
  3485. }
  3486. return true;
  3487. }
  3488. private:
  3489. IEconItemAttributeIterator *m_pIterator;
  3490. };
  3491. };
  3492. // --------------------------------------------------------------------------
  3493. // Purpose:
  3494. // --------------------------------------------------------------------------
  3495. CEconItemLocalizedMarketNameGenerator::CEconItemLocalizedMarketNameGenerator( const CLocalizationProvider *pLocalizationProvider, CEconItem *pItem, bool bUseProperName )
  3496. {
  3497. Assert( pItem );
  3498. CMarketNameGenerator_EconItemInterfaceWrapper EconItemWrapper( pItem );
  3499. GenerateLocalizedFullItemName( m_loc_LocalizedItemName, pLocalizationProvider, &EconItemWrapper, k_EGenerateLocalizedFullItemName_WithPaintWear, bUseProperName );
  3500. }
  3501. #endif // BUILD_ITEM_NAME_AND_DESC