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

3708 lines
121 KiB

//====== Copyright � 1996-2005, Valve Corporation, All rights reserved. =======
//
// Purpose:
//
//=============================================================================
#include "cbase.h"
#include "econ_item_view.h"
#include "econ_item_system.h"
#include "econ_item_description.h"
#include "econ_item_inventory.h"
#include "econ_gcmessages.h"
// For localization
#include "tier3/tier3.h"
#include "vgui/ILocalize.h"
#include "tier2/p4helpers.h"
#include "p4lib/ip4.h"
#include "imageutils.h"
#if defined(CLIENT_DLL) || defined(GAME_DLL)
#include "isaverestore.h"
#include "dt_utlvector_send.h"
#include "dt_utlvector_recv.h"
#endif
#ifdef CLIENT_DLL
#ifndef DEDICATED
#include "vgui_controls/Panel.h"
#include "vgui/IScheme.h"
#endif
#endif
#if defined(TF_CLIENT_DLL)
#include "tf_duel_summary.h"
#include "econ_contribution.h"
#include "tf_player_info.h"
#include "tf_gcmessages.h"
#include "c_tf_freeaccount.h"
#endif
#if defined(DOTA_DLL)
#include "dota_sharedfuncs.h"
#endif
#include "activitylist.h"
#if defined(CSTRIKE_CLIENT_DLL)
#include "materialsystem/icompositetexturegenerator.h"
#include "materialsystem/icustommaterialmanager.h"
#include "mathlib/camera.h"
#include "tier3/mdlutils.h"
#include "irendertorthelperobject.h"
#include "cs_weapon_parse.h"
#include "cs_custom_weapon_visualsdata_processor.h"
#include "cs_custom_clothing_visualsdata_processor.h"
#include "cs_custom_epidermis_visualsdata_processor.h"
#include "cs_custom_embroider_visualsdata_processor.h"
#include "cs_custom_texture_saver.h"
#include "materialsystem/imaterialvar.h"
#endif //#if defined(CSTRIKE_CLIENT_DLL)
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
#define ECON_ITEM_ICON_CACHE_VERSION 0x10
// Dynamic attributes in the the Econ_Item SOCache need to be networked for demo recording!
#define ECON_NETWORK_DYNAMIC_ATTRIBUTES_FOR_DEMOS
#if defined(CLIENT_DLL) || defined(GAME_DLL)
// Networking tables for attributes
BEGIN_NETWORK_TABLE_NOBASE( CEconItemAttribute, DT_ScriptCreatedAttribute )
// Note: we are networking the value as an int, even though it's a "float", because really it isn't
// a float. It's 32 raw bits.
#ifndef CLIENT_DLL
SendPropInt( SENDINFO(m_iAttributeDefinitionIndex), -1, SPROP_UNSIGNED ),
SendPropInt( SENDINFO_NAME(m_flValue, m_iRawValue32), 32, SPROP_UNSIGNED ),
SendPropInt( SENDINFO_NAME(m_flInitialValue, m_iRawInitialValue32), 32, SPROP_UNSIGNED ),
SendPropInt( SENDINFO(m_nRefundableCurrency), -1, SPROP_UNSIGNED ),
SendPropBool( SENDINFO(m_bSetBonus) ),
#else
RecvPropInt( RECVINFO(m_iAttributeDefinitionIndex) ),
RecvPropInt( RECVINFO_NAME(m_flValue, m_iRawValue32) ),
RecvPropFloat( RECVINFO(m_flValue), SPROP_NOSCALE ), // for demo compatibility only
RecvPropInt( RECVINFO_NAME(m_flInitialValue, m_iRawInitialValue32) ),
RecvPropInt( RECVINFO( m_nRefundableCurrency ) ),
RecvPropBool( RECVINFO( m_bSetBonus ) ),
#endif
END_NETWORK_TABLE()
#endif
#if defined(CSTRIKE_CLIENT_DLL)
CCSCustomTextureSaver g_Generated_Texture_Saver;
#endif //#if defined(CSTRIKE_CLIENT_DLL)
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CEconItemAttribute::CEconItemAttribute( void )
{
Init();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CEconItemAttribute::Init( void )
{
m_iAttributeDefinitionIndex = (attrib_definition_index_t)-1;
m_flValue = 0.0f;
#if defined(CLIENT_DLL) || defined(GAME_DLL)
m_flInitialValue = 0;
m_nRefundableCurrency = 0;
m_bSetBonus = false;
#endif
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CEconItemAttribute & CEconItemAttribute::operator=( const CEconItemAttribute &val )
{
m_iAttributeDefinitionIndex = val.m_iAttributeDefinitionIndex;
m_flValue = val.m_flValue;
#if defined(CLIENT_DLL) || defined(GAME_DLL)
m_flInitialValue = m_flValue;
m_bSetBonus = val.m_bSetBonus;
#endif
return *this;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CEconItemAttribute::CEconItemAttribute( const attrib_definition_index_t iAttributeIndex, float flValue )
{
Init();
m_iAttributeDefinitionIndex = iAttributeIndex;
SetValue( flValue );
#if defined(CLIENT_DLL) || defined(GAME_DLL)
m_flInitialValue = m_flValue;
#endif
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CEconItemAttribute::CEconItemAttribute( const attrib_definition_index_t iAttributeIndex, uint32 unValue )
{
Init();
m_iAttributeDefinitionIndex = iAttributeIndex;
SetIntValue( unValue );
#if defined(CLIENT_DLL) || defined(GAME_DLL)
m_flInitialValue = m_flValue;
#endif
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CEconItemAttribute::SetValue( float flValue )
{
// Assert( GetStaticData() && GetStaticData()->IsStoredAsFloat() );
m_flValue = flValue;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
float CEconItemAttribute::GetValue( void ) const
{
// Assert( GetStaticData() && GetStaticData()->IsStoredAsFloat() );
return m_flValue;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CEconItemAttribute::SetIntValue( uint32 unValue )
{
// @note we don't check the storage type here, because this is how it is set from the data file
// Note that numbers approaching two billion cannot be stored in a float
// representation because they will map to NaNs. Numbers below 16 million
// will fail if denormals are disabled.
m_flValue = *(float*)&unValue;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
uint32 CEconItemAttribute::GetIntValue( void ) const
{
// Assert( GetStaticData() && GetStaticData()->IsStoredAsInteger() );
return *(uint32*)&m_flValue;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
const CEconItemAttributeDefinition *CEconItemAttribute::GetStaticData( void ) const
{
return GetItemSchema()->GetAttributeDefinition( m_iAttributeDefinitionIndex );
}
#if defined(CLIENT_DLL) || defined(GAME_DLL)
#if defined( ECON_NETWORK_ATTRIBUTES ) || defined( ECON_NETWORK_DYNAMIC_ATTRIBUTES_FOR_DEMOS )
BEGIN_NETWORK_TABLE_NOBASE( CAttributeList, DT_AttributeList )
#if !defined( CLIENT_DLL )
SendPropUtlVectorDataTable( m_Attributes, MAX_ATTRIBUTES_PER_ITEM, DT_ScriptCreatedAttribute ),
#else
RecvPropUtlVectorDataTable( m_Attributes, MAX_ATTRIBUTES_PER_ITEM, DT_ScriptCreatedAttribute ),
#endif // CLIENT_DLL
END_NETWORK_TABLE()
#endif // #ifdef defined( ECON_NETWORK_ATTRIBUTES ) || defined( ECON_NETWORK_DYNAMIC_ATTRIBUTES_FOR_DEMOS )
BEGIN_DATADESC_NO_BASE( CAttributeList )
END_DATADESC()
#endif // #if defined(CLIENT_DLL) || defined(GAME_DLL)
#if defined(CLIENT_DLL)
bool CEconItemView::m_sbHasCleanedInventoryImageCacheDir = false;
#endif //#if defined(CLIENT_DLL)
//===========================================================================================================================
// SCRIPT CREATED ITEMS
//===========================================================================================================================
#if defined(CLIENT_DLL) || defined(GAME_DLL)
BEGIN_NETWORK_TABLE_NOBASE( CEconItemView, DT_ScriptCreatedItem )
#if !defined( CLIENT_DLL )
SendPropInt( SENDINFO( m_iItemDefinitionIndex ), 20, SPROP_UNSIGNED ),
SendPropInt( SENDINFO( m_iEntityLevel ), 8 ),
//SendPropInt( SENDINFO( m_iItemID ), 64, SPROP_UNSIGNED ),
SendPropInt( SENDINFO( m_iItemIDHigh ), 32, SPROP_UNSIGNED ),
SendPropInt( SENDINFO( m_iItemIDLow ), 32, SPROP_UNSIGNED ),
SendPropInt( SENDINFO( m_iAccountID ), 32, SPROP_UNSIGNED ),
SendPropInt( SENDINFO( m_iEntityQuality ), 5 ),
SendPropBool( SENDINFO( m_bInitialized ) ),
SendPropString( SENDINFO( m_szCustomName ) ),
#ifdef ECON_NETWORK_ATTRIBUTES
SendPropDataTable(SENDINFO_DT(m_AttributeList), &REFERENCE_SEND_TABLE(DT_AttributeList)),
#endif // #ifdef ECON_NETWORK_ATTRIBUTES
#ifdef ECON_NETWORK_DYNAMIC_ATTRIBUTES_FOR_DEMOS
SendPropDataTable(SENDINFO_DT(m_NetworkedDynamicAttributesForDemos), &REFERENCE_SEND_TABLE(DT_AttributeList)),
#endif // #ifdef ECON_NETWORK_DYNAMIC_ATTRIBUTES_FOR_DEMOS
#else
RecvPropInt( RECVINFO( m_iItemDefinitionIndex ) ),
RecvPropInt( RECVINFO( m_iEntityLevel ) ),
//RecvPropInt( RECVINFO( m_iItemID ) ),
RecvPropInt( RECVINFO( m_iItemIDHigh ) ),
RecvPropInt( RECVINFO( m_iItemIDLow ) ),
RecvPropInt( RECVINFO( m_iAccountID ) ),
RecvPropInt( RECVINFO( m_iEntityQuality ) ),
RecvPropBool( RECVINFO( m_bInitialized ) ),
RecvPropString( RECVINFO( m_szCustomName ) ),
#ifdef ECON_NETWORK_ATTRIBUTES
RecvPropDataTable(RECVINFO_DT(m_AttributeList), 0, &REFERENCE_RECV_TABLE(DT_AttributeList)),
#endif // #ifdef ECON_NETWORK_ATTRIBUTES
#ifdef ECON_NETWORK_DYNAMIC_ATTRIBUTES_FOR_DEMOS
RecvPropDataTable(RECVINFO_DT(m_NetworkedDynamicAttributesForDemos), 0, &REFERENCE_RECV_TABLE(DT_AttributeList)),
#endif // #ifdef ECON_NETWORK_DYNAMIC_ATTRIBUTES_FOR_DEMOS
#endif // CLIENT_DLL
END_NETWORK_TABLE()
BEGIN_DATADESC_NO_BASE( CEconItemView )
DEFINE_FIELD( m_iItemDefinitionIndex, FIELD_SHORT ),
DEFINE_FIELD( m_iEntityQuality, FIELD_INTEGER ),
DEFINE_FIELD( m_iEntityLevel, FIELD_INTEGER ),
DEFINE_FIELD( m_iItemID, FIELD_INTEGER ),
// DEFINE_FIELD( m_wszItemName, FIELD_STRING ), Regenerated post-save
// DEFINE_FIELD( m_szItemName, FIELD_STRING ), Regenerated post-save
// DEFINE_FIELD( m_szAttributeDescription, FIELD_STRING ), Regenerated post-save
// m_AttributeLineColors // Regenerated post-save
// m_Attributes // Custom handling in Save()/Restore()
DEFINE_FIELD( m_bInitialized, FIELD_BOOLEAN ),
DEFINE_EMBEDDED( m_AttributeList ),
END_DATADESC()
#endif
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CEconItemView::CEconItemView( void )
{
m_iItemDefinitionIndex = 0;
m_iEntityQuality = (int)AE_UNDEFINED;
m_iEntityLevel = 0;
SetItemID(0);
m_iInventoryPosition = 0;
m_bInitialized = false;
m_iAccountID = 0;
m_pNonSOEconItem = NULL;
#if defined( CLIENT_DLL )
m_bIsTradeItem = false;
m_iEntityQuantity = 1;
m_iRarityOverride = -1;
m_iQualityOverride = 0;
m_unClientFlags = 0;
m_unOverrideStyle = INVALID_STYLE_INDEX;
m_bInventoryImageRgbaRequested = false;
m_bInventoryImageTriedCache = false;
m_nInventoryImageRgbaWidth = 0;
m_nInventoryImageRgbaHeight = 0;
m_pImageReadyCallback = NULL;
m_pRenderToRTData = NULL;
m_pScratchVTF = NULL;
m_pRenderToRTMDL = NULL;
m_hAsyncControl = NULL;
m_asyncFixupState = AFS_Init;
#endif
m_szCustomNameOverride[ 0 ] = '\0';
m_bKillEaterTypesCached = false;
m_nKillEaterValuesCacheFrame = -2;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CEconItemView::~CEconItemView( void )
{
#ifdef CLIENT_DLL
// don't want to call this during destruction
m_pImageReadyCallback = NULL;
g_InventoryItemUpdateManager.RemoveItemViewFromFixupList( this );
if ( InventoryManager() )
{
InventoryManager()->OnDestroyEconItemView( this );
}
if ( m_asyncFixupState == AFS_LoadingInProgress )
{
if ( filesystem && m_hAsyncControl )
{
filesystem->AsyncAbort( m_hAsyncControl );
filesystem->AsyncRelease( m_hAsyncControl );
}
m_hAsyncControl = NULL;
m_asyncFixupState = AFS_Init;
}
#endif
DestroyAllAttributes();
Cleanup();
}
void CEconItemView::Cleanup( void )
{
MarkDescriptionDirty();
#if defined( CSTRIKE_CLIENT_DLL )
for ( int i = 0; i < m_ppVisualsDataProcessors.Count(); i++ )
{
if ( m_ppVisualsDataProcessors[ i ] != NULL )
{
m_ppVisualsDataProcessors[ i ]->Release();
m_ppVisualsDataProcessors[ i ] = NULL;
}
}
m_ppVisualsDataProcessors.RemoveAll();
if ( m_pRenderToRTData != NULL )
{
g_pRenderToRTHelper->DestroyRenderToRTData( m_pRenderToRTData );
m_pRenderToRTData = NULL;
}
if ( m_pRenderToRTMDL != NULL )
{
delete m_pRenderToRTMDL;
m_pRenderToRTMDL = NULL;
}
if ( m_pScratchVTF != NULL )
{
DestroyVTFTexture( m_pScratchVTF );
m_pScratchVTF = NULL;
}
m_pStickerMaterials.RemoveAll();
#endif //#if defined( CSTRIKE_CLIENT_DLL )
if ( m_pNonSOEconItem )
{
delete m_pNonSOEconItem;
m_pNonSOEconItem = NULL;
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CEconItemView::CEconItemView( const CEconItemView &src )
{
m_pNonSOEconItem = NULL;
#if defined( CSTRIKE_CLIENT_DLL )
m_bInventoryImageRgbaRequested = false;
m_bInventoryImageTriedCache = false;
m_pImageReadyCallback = NULL;
m_pRenderToRTData = NULL;
m_pScratchVTF = NULL;
m_pRenderToRTMDL = NULL;
m_hAsyncControl = NULL;
m_asyncFixupState = AFS_Init;
#endif
*this = src;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CEconItemView::Init( int iDefIndex, int iQuality, int iLevel, uint32 iAccountID )
{
m_AttributeList.Init();
m_NetworkedDynamicAttributesForDemos.Init();
m_szCustomName.GetForModify()[0] = '\0';
m_iItemDefinitionIndex = iDefIndex;
const CEconItemDefinition *pData = GetStaticData();
if ( !pData )
{
// We've got an item that we don't have static data for.
return;
}
SetItemID(0);
m_bInitialized = true;
m_iAccountID = iAccountID;
if ( iQuality == AE_USE_SCRIPT_VALUE )
{
m_iEntityQuality = pData->GetQuality();
// Kyle says: this is a horrible hack because AE_UNDEFINED will get stuffed into a uint8 when
// loaded into the item definition, but then read back out into a regular int here.
if ( m_iEntityQuality == (uint8)AE_UNDEFINED )
{
m_iEntityQuality = (int)AE_NORMAL;
}
}
else if ( iQuality == k_unItemQuality_Any )
{
m_iEntityQuality = (int) AE_UNIQUE;
}
else
{
m_iEntityQuality = iQuality;
}
if ( iLevel == AE_USE_SCRIPT_VALUE )
{
m_iEntityLevel = pData->RollItemLevel();
}
else
{
m_iEntityLevel = iLevel;
}
// We made changes to quality, level, etc. so mark the description as dirty.
MarkDescriptionDirty();
}
bool CEconItemView::Init( CEconItem* pItem )
{
Init( pItem->GetDefinitionIndex(), pItem->GetQuality(), pItem->GetItemLevel(), pItem->GetAccountID() );
if ( !IsValid() )
{
return false;
}
SetItemID( pItem->GetItemID() );
SetInventoryPosition( pItem->GetInventoryToken() );
return true;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CEconItemView& CEconItemView::operator=( const CEconItemView& src )
{
Cleanup();
m_iItemDefinitionIndex = src.m_iItemDefinitionIndex;
m_iEntityQuality = src.m_iEntityQuality;
m_iEntityLevel = src.m_iEntityLevel;
SetItemID( src.GetItemID() );
m_iInventoryPosition = src.m_iInventoryPosition;
m_bInitialized = src.m_bInitialized;
m_iAccountID = src.m_iAccountID;
m_pNonSOEconItem = src.m_pNonSOEconItem;
#if defined( CLIENT_DLL )
m_bIsTradeItem = src.m_bIsTradeItem;
m_iEntityQuantity = src.m_iEntityQuantity;
m_iRarityOverride = src.m_iRarityOverride;
m_iQualityOverride = src.m_iQualityOverride;
m_unClientFlags = src.m_unClientFlags;
m_unOverrideStyle = src.m_unOverrideStyle;
#endif //#if defined( CLIENT_DLL )
#if defined( CSTRIKE_CLIENT_DLL )
if ( src.m_bInventoryImageRgbaRequested )
{
m_bInventoryImageRgbaRequested = src.m_bInventoryImageRgbaRequested;
m_bInventoryImageTriedCache = src.m_bInventoryImageTriedCache;
m_nInventoryImageRgbaWidth = src.m_nInventoryImageRgbaWidth;
m_nInventoryImageRgbaHeight = src.m_nInventoryImageRgbaHeight;
m_pImageReadyCallback = src.m_pImageReadyCallback;
if ( src.m_inventoryImageRgba.TellPut() > 0 )
{
m_inventoryImageRgba.EnsureCapacity( src.m_inventoryImageRgba.TellPut() );
m_inventoryImageRgba.CopyBuffer( src.m_inventoryImageRgba );
}
else
{
m_inventoryImageRgba.Purge();
}
}
else
{
m_nInventoryImageRgbaWidth = 0;
m_nInventoryImageRgbaHeight = 0;
m_bInventoryImageRgbaRequested = false;
m_bInventoryImageTriedCache = false;
m_inventoryImageRgba.Purge();
m_pImageReadyCallback = NULL;
}
src.DuplicateCustomMaterialsToOther( this );
// copy and addref processors from src
for ( int i = 0; i < src.m_ppVisualsDataProcessors.Count(); i++ )
{
if ( src.m_ppVisualsDataProcessors[ i ] != NULL )
{
m_ppVisualsDataProcessors.AddToTail( src.m_ppVisualsDataProcessors[ i ] );
src.m_ppVisualsDataProcessors[ i ]->AddRef();
}
}
#endif //#if defined( CSTRIKE_CLIENT_DLL )
DestroyAllAttributes();
m_AttributeList = src.m_AttributeList;
m_NetworkedDynamicAttributesForDemos = src.m_NetworkedDynamicAttributesForDemos;
if ( src.m_szCustomNameOverride[0] != '\0' )
{
V_strncpy(m_szCustomNameOverride, src.m_szCustomNameOverride, sizeof( m_szCustomNameOverride ) );
}
V_strncpy( m_szCustomName.GetForModify(), src.m_szCustomName.Get(), MAX_ITEM_CUSTOM_NAME_DATABASE_SIZE );
return *this;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CEconItemView::operator==( const CEconItemView &other ) const
{
if ( IsValid() != other.IsValid() )
return false;
if ( (GetItemID() > 0 || other.GetItemID() > 0) && GetItemID() != other.GetItemID() )
return false;
if ( GetItemIndex() != other.GetItemIndex() )
return false;
return true;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
const GameItemDefinition_t *CEconItemView::GetStaticData( void ) const
{
const CEconItemDefinition *pRet = GetItemSchema()->GetItemDefinition( m_iItemDefinitionIndex );
const GameItemDefinition_t *pTypedRet = dynamic_cast<const GameItemDefinition_t *>( pRet );
AssertMsg( pRet == pTypedRet, "Item definition of inappropriate type." );
return pTypedRet;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
int32 CEconItemView::GetQuality() const
{
#if defined( CLIENT_DLL )
if ( m_iQualityOverride )
return m_iQualityOverride;
#endif
return GetSOCData()
? GetSOCData()->GetQuality()
#ifdef TF_CLIENT_DLL
: GetFlags() & kEconItemFlagClient_StoreItem
? AE_UNIQUE
#endif
: ( ( m_iEntityQuality.Get() > int( AE_NORMAL ) ) ? m_iEntityQuality.Get() : int( AE_NORMAL ) );
}
int32 CEconItemView::GetRarity() const
{
#if defined( CLIENT_DLL )
if ( m_iRarityOverride > -1 )
return m_iRarityOverride;
#endif
if ( GetSOCData() && ( GetSOCData()->GetRarity() != k_unItemRarity_Any ) )
{
return GetSOCData()->GetRarity();
}
else
{
int nRarity = GetItemDefinition()->GetRarity();
const CPaintKit *pPaintKit = GetCustomPaintKit();
if ( pPaintKit && pPaintKit->nID != 0 )
{
nRarity = EconRarity_CombinedItemAndPaintRarity( nRarity, pPaintKit->nRarity );
}
else if ( GetItemDefinition()->GetDefinitionIndex() < SCHEMA_BASE_ITEM_MAX )
{
nRarity = 0; // Stock
}
return nRarity;
}
// TODO: This should probably return EconRarity_CombinedItemAndPaintRarity( pItemDef->GetRarity(), pPaintKit->nRarity )
// but there is no known case where the absence of paint consideration is a problem.
// If it ain't broken, doth one fixeth?
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
style_index_t CEconItemView::GetStyle() const
{
return GetItemStyle();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
uint8 CEconItemView::GetFlags() const
{
uint8 unSOCFlags = GetSOCData() ? GetSOCData()->GetFlags() : 0;
#if !defined( GAME_DLL )
return unSOCFlags | m_unClientFlags;
#else // defined( GAME_DLL )
return unSOCFlags;
#endif // !defined( GAME_DLL )
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
eEconItemOrigin CEconItemView::GetOrigin() const
{
return GetSOCData() ? GetSOCData()->GetOrigin() : kEconItemOrigin_Invalid;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
uint16 CEconItemView::GetQuantity() const
{
return GetItemQuantity();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
const char *CEconItemView::GetCustomName() const
{
if ( m_szCustomNameOverride[ 0 ] != '\0' )
{
return m_szCustomNameOverride;
}
// If we have an so cache for this item, use the attribute in it
if ( GetSOCData() )
return GetSOCData()->GetCustomName();
extern ConVar sv_spec_use_tournament_content_standards;
#if defined (CLIENT_DLL)
// If no SO cache, use anything networked from the server or
// null if that string is empty (to support legacy behavior of this function)
if ( *m_szCustomName )
{
if ( CDemoPlaybackParameters_t const *pParameters = engine->GetDemoPlaybackParameters() )
{
if ( pParameters->m_bAnonymousPlayerIdentity )
return NULL;
}
extern ConVar cl_spec_use_tournament_content_standards;
C_BasePlayer *pLocalPlayer = C_BasePlayer::GetLocalPlayer( );
if ( ( pLocalPlayer->IsSpectator( ) || pLocalPlayer->IsHLTV( ) ) &&
( sv_spec_use_tournament_content_standards.GetBool( ) || cl_spec_use_tournament_content_standards.GetBool( ) ) )
return NULL;
return m_szCustomName;
}
#endif
return NULL;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
const char *CEconItemView::GetCustomDesc() const
{
return GetSOCData() ? GetSOCData()->GetCustomDesc() : NULL;
}
int CEconItemView::GetItemSetIndex() const
{
return GetSOCData() ? GetSOCData()->GetItemSetIndex() : -1;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CEconItemView::Invalidate( void )
{
m_bInitialized = false;
m_iItemDefinitionIndex = m_iItemID = 0;
if ( m_pNonSOEconItem )
{
delete m_pNonSOEconItem;
m_pNonSOEconItem = NULL;
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CEconItemView::IterateAttributes( class IEconItemAttributeIterator *pIterator ) const
{
Assert( pIterator );
// First, we iterate over the attributes we have local copies of. If we have any attribute
// values here they'll override whatever values we would otherwise have pulled from our
// definition/CEconItem.
for ( int i = 0; i < GetNumAttributes(); i++ )
{
const CEconItemAttribute *pAttrInst = GetAttribute(i);
Assert( pAttrInst );
const CEconItemAttributeDefinition *pAttrDef = pAttrInst->GetStaticData();
if ( !pAttrDef )
continue;
const ISchemaAttributeType *pAttrType = pAttrDef->GetAttributeType();
Assert( pAttrType );
Assert( pAttrType->BSupportsGameplayModificationAndNetworking() );
attribute_data_union_t value;
value.asFloat = pAttrInst->GetValue();
if ( !pAttrType->OnIterateAttributeValue( pIterator, pAttrDef, value ) )
return;
}
// This wraps any other iterator class and will prevent double iteration of any attributes
// that exist on us.
class CEconItemAttributeIterator_EconItemViewWrapper : public IEconItemAttributeIterator
{
public:
CEconItemAttributeIterator_EconItemViewWrapper( const CEconItemView *pEconItemView, IEconItemAttributeIterator *pIterator )
: m_pEconItemView( pEconItemView )
, m_pIterator( pIterator )
{
Assert( m_pEconItemView );
Assert( m_pIterator );
}
virtual bool OnIterateAttributeValue( const CEconItemAttributeDefinition *pAttrDef, attrib_value_t value )
{
Assert( pAttrDef );
return m_pEconItemView->GetAttributeByDefIndex( pAttrDef->GetDefinitionIndex() )
? true
: m_pIterator->OnIterateAttributeValue( pAttrDef, value );
}
virtual bool OnIterateAttributeValue( const CEconItemAttributeDefinition *pAttrDef, float value )
{
Assert( pAttrDef );
return m_pEconItemView->GetAttributeByDefIndex( pAttrDef->GetDefinitionIndex() )
? true
: m_pIterator->OnIterateAttributeValue( pAttrDef, value );
}
virtual bool OnIterateAttributeValue( const CEconItemAttributeDefinition *pAttrDef, const CAttribute_String& value )
{
Assert( pAttrDef );
return m_pEconItemView->GetAttributeByDefIndex( pAttrDef->GetDefinitionIndex() )
? true
: m_pIterator->OnIterateAttributeValue( pAttrDef, value );
}
virtual bool OnIterateAttributeValue( const CEconItemAttributeDefinition *pAttrDef, const Vector& value )
{
Assert( pAttrDef );
return m_pEconItemView->GetAttributeByDefIndex( pAttrDef->GetDefinitionIndex() )
? true
: m_pIterator->OnIterateAttributeValue( pAttrDef, value );
}
private:
const CEconItemView *m_pEconItemView;
IEconItemAttributeIterator *m_pIterator;
};
CEconItemAttributeIterator_EconItemViewWrapper iteratorWrapper( this, pIterator );
// Next, iterate over our database-backed item if we have one... if we do have a DB
// backing for our item here, that will also feed in the definition attributes.
if ( GetSOCData() )
{
GetSOCData()->IterateAttributes( &iteratorWrapper );
}
#ifdef ECON_NETWORK_DYNAMIC_ATTRIBUTES_FOR_DEMOS
else if ( GetItemID() > 0 )
{
// Since there's no persistent data available, try the networked values!
for ( int i = 0; i < m_NetworkedDynamicAttributesForDemos.GetNumAttributes(); i++ )
{
const CEconItemAttribute *pAttrInst = m_NetworkedDynamicAttributesForDemos.GetAttribute(i);
Assert( pAttrInst );
const CEconItemAttributeDefinition *pAttrDef = pAttrInst->GetStaticData();
if ( !pAttrDef )
continue;
const ISchemaAttributeType *pAttrType = pAttrDef->GetAttributeType();
Assert( pAttrType );
attribute_data_union_t value;
value.asFloat = pAttrInst->GetValue();
if ( !pAttrType->OnIterateAttributeValue( pIterator, pAttrDef, value ) )
return;
}
if ( GetStaticData() )
{
GetStaticData()->IterateAttributes( &iteratorWrapper );
}
}
#endif
// If we didn't have a DB backing, we can still iterate over our item definition
// attributes ourselves. This can happen if we're previewing an item in the store, etc.
else if ( GetStaticData() )
{
GetStaticData()->IterateAttributes( &iteratorWrapper );
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CEconItemView::EnsureDescriptionIsBuilt() const
{
/** Removed for partner depot **/
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CEconItemView::MarkDescriptionDirty()
{
/** Removed for partner depot **/
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CEconItemView::SetGrayedOutReason( const char *pszGrayedOutReason )
{
/** Removed for partner depot **/
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
int CEconItemView::GetItemQuantity() const
{
CEconItem *pSOCData = GetSOCData();
if ( pSOCData )
{
return pSOCData->GetQuantity();
}
#ifdef CLIENT_DLL
return m_iEntityQuantity;
#else
return 1;
#endif
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
style_index_t CEconItemView::GetItemStyle() const
{
#ifdef CLIENT_DLL
// Are we overriding the backing store style?
if ( m_unOverrideStyle != INVALID_STYLE_INDEX )
return m_unOverrideStyle;
#endif // CLIENT_DLL
// CEconItem *pSOCData = GetSOCData();
// if ( pSOCData )
// return pSOCData->GetStyle();
return INVALID_STYLE_INDEX;
}
#ifdef CLIENT_DLL
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CEconItemView::SetClientItemFlags( uint8 unFlags )
{
// Generally speaking, we have two uses for client flags:
//
// - we don't have a backing store (a real CEconItem) but want to pretend we do
// for purposes of generating tooltips, graying out icons, etc.
//
// - we may or may not have a backing store but want to shove client-specific
// information into the structure -- things like "this is the item being
// actively previewed", etc.
//
// If neither of these two cases is true, then we're going to get unexpected
// behavior where the GC and the client disagree about the item flags and then
// Terrible Things happen. We assert to make sure we're in one of the above cases.
Assert( !GetSOCData() || (unFlags & kEconItemFlags_CheckFlags_AllGCFlags) == 0 );
m_unClientFlags = unFlags;
MarkDescriptionDirty();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CEconItemView::SetItemStyleOverride( style_index_t unNewStyleOverride )
{
// We should only ever override the style on items that don't have a real
// backing store or we'll start getting disagreements about what the client
// wants to happen and what's being stored on the GC. Unfortunately we can't
// assert on this because we do it sometimes when previewing items.
//Assert( !GetSOCData() );
m_unOverrideStyle = unNewStyleOverride;
MarkDescriptionDirty();
}
#endif // CLIENT_DLL
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CEconItem *CEconItemView::GetSOCData( void ) const
{
if ( m_pNonSOEconItem )
return m_pNonSOEconItem;
#ifdef CLIENT_DLL
// We need to find the inventory that contains this item. If we're not connected
// to a server, and the owner is the same as the local player, use the local inventory.
// We need to do this for trading, since we are subscribed to the other person's cache.
if ( !engine->IsInGame() && GetSteamIDFromSOID( InventoryManager()->GetLocalInventory()->GetOwner() ).GetAccountID() == m_iAccountID )
return InventoryManager()->GetLocalInventory()->GetSOCDataForItem( GetItemID() );
#endif // CLIENT_DLL
// We're in-game. Find the inventory with our account ID.
CPlayerInventory *pInventory = InventoryManager()->GetInventoryForAccount( m_iAccountID );
#if defined ( CLIENT_DLL )
Assert( !pInventory || pInventory == InventoryManager()->GetLocalInventory() );
#endif
if ( pInventory )
return pInventory->GetSOCDataForItem( GetItemID() );
return NULL;
}
itemid_t CEconItemView::GetFauxItemIDFromDefinitionIndex( void ) const
{
return m_iItemDefinitionIndex
? CombinedItemIdMakeFromDefIndexAndPaint( m_iItemDefinitionIndex, 0 )
: 0ull; // invalid items cannot represent a faux ItemID
}
// Purpose: Return the model to use for model panels containing this item
//-----------------------------------------------------------------------------
const char *CEconItemView::GetInventoryModel( void )
{
if ( !GetStaticData() )
return NULL;
return GetStaticData()->GetInventoryModel();
}
bool CEconItemView::IsStickerTool( void )
{
const GameItemDefinition_t *pStaticData = GetStaticData();
if ( !pStaticData )
return false;
if ( pStaticData->IsTool() && pStaticData->GetEconTool() && ( pStaticData->GetEconTool()->GetCapabilities() & ITEM_CAP_CAN_STICKER ) != 0 )
return true;
return false;
}
IMaterial *CEconItemView::GetToolStickerMaterial( void )
{
if ( !IsStickerTool() )
return NULL;
uint32 nStickerId = GetStickerAttributeBySlotIndexInt( 0, k_EStickerAttribute_ID, 0 );
if ( nStickerId > 0 && GetItemSchema() && GetItemSchema()->GetStickerKitDefinition( nStickerId ) )
{
char szDesiredStickerMaterialPath[128];
V_snprintf( szDesiredStickerMaterialPath, sizeof(szDesiredStickerMaterialPath),
"materials/models/weapons/customization/stickers/%s.vmt",
GetItemSchema()->GetStickerKitDefinition( nStickerId )->sMaterialPath.String() );
IMaterial *pMatStickerOverride = materials->FindMaterial( szDesiredStickerMaterialPath, TEXTURE_GROUP_OTHER, false );
if ( pMatStickerOverride->IsErrorMaterial() )
{
KeyValues *pSpecificStickerMaterialKeyValues = new KeyValues( "vmt" );
KeyValues::AutoDelete autodelete_pSpecificStickerMaterialKeyValues( pSpecificStickerMaterialKeyValues );
if ( pSpecificStickerMaterialKeyValues->LoadFromFile( g_pFullFileSystem, szDesiredStickerMaterialPath, "GAME" ) )
{
//pSpecificStickerMaterialKeyValues->SetString( "$envmap", "Editor/cube_vertigo" );
//pSpecificStickerMaterialKeyValues->SetString( "$aotexture", "models/weapons/customization/rif_famas/rif_famas_decal_A" );
//KeyValuesDumpAsDevMsg( pSpecificStickerMaterialKeyValues, 2 );
pMatStickerOverride = materials->CreateMaterial( szDesiredStickerMaterialPath, pSpecificStickerMaterialKeyValues );
}
autodelete_pSpecificStickerMaterialKeyValues.Detach();
if ( !pMatStickerOverride->IsErrorMaterial() )
return pMatStickerOverride;
}
}
return NULL;
}
//-----------------------------------------------------------------------------
// Purpose: Return the image to use for model panels containing this item
//-----------------------------------------------------------------------------
const char *CEconItemView::GetInventoryImage( void ) const
{
const GameItemDefinition_t *pStaticData = GetStaticData();
if ( !pStaticData )
return NULL;
const CEconStyleInfo *pStyle = GetStaticData()->GetStyleInfo( GetItemStyle() );
static CSchemaAttributeDefHandle pAttr_AlternateIcon( "alternate icon" );
uint32 unAlternateIcon = 0;
extern bool Helper_IsGraphicTool( const CEconItemDefinition * pEconItemDefinition );
static CSchemaItemDefHandle hItemDefMusicKit( "musickit" );
static CSchemaItemDefHandle hItemDefMusicKitDefault( "musickit_default" );
#ifdef DOTA_DLL
if ( pStaticData->GetCapabilities() & ITEM_CAP_USES_ESSENCE )
{
// Backwards compatibility to eggs, which were implemented before this feature.
return pStaticData->GetAlternateIcon( GetEggColor( this ) );
}
else if ( FindAttribute( pAttr_AlternateIcon, &unAlternateIcon ) )
{
return pStaticData->GetAlternateIcon( unAlternateIcon );
}
else if ( pStyle && pStyle->GetIcon() )
{
return pStaticData->GetAlternateIcon( pStyle->GetIcon() );
}
#else
if ( FindAttribute( pAttr_AlternateIcon, &unAlternateIcon ) )
{
return GetItemSchema()->GetAlternateIcon( unAlternateIcon )->GetInventoryImage();
}
else if ( pStyle && pStyle->GetIcon() )
{
return GetItemSchema()->GetAlternateIcon( pStyle->GetIcon() )->GetInventoryImage();
}
else if ( Helper_IsGraphicTool( GetItemDefinition() ) )
{
uint32 nStickerId = GetStickerAttributeBySlotIndexInt( 0, k_EStickerAttribute_ID, 0 );
if ( const CStickerKit *pStickerKitDef = ( ( nStickerId > 0 ) ? GetItemSchema()->GetStickerKitDefinition( nStickerId ) : NULL ) )
{
char const *szResult = pStickerKitDef->GetIconURLLarge();
// exclude code providing a different image for client UI, client will color-multiply on its own
#if SPRAY_TINT_IMAGE_FOR_CLIENT_UI
static CSchemaAttributeDefHandle pAttr_SprayTintID( "spray tint id" );
uint32 unSprayTintID = 0;
if ( pAttr_SprayTintID && FindAttribute( pAttr_SprayTintID, &unSprayTintID ) && unSprayTintID )
{
unSprayTintID = CombinedTintIDGetHSVID( unSprayTintID );
if ( ! ( char * ) m_autoptrInventoryImageGeneratedPath )
m_autoptrInventoryImageGeneratedPath.reset( new char[ MAX_PATH ] );
int nImagePathLen = V_strlen( szResult );
Assert( nImagePathLen + 4 < MAX_PATH );
if ( ( nImagePathLen > 6 ) && !V_stricmp( szResult + nImagePathLen - 6, "_large" ) )
{
V_snprintf( m_autoptrInventoryImageGeneratedPath, MAX_PATH, "%.*s_%u_large", nImagePathLen - 6, szResult, unSprayTintID );
}
else
{
V_snprintf( m_autoptrInventoryImageGeneratedPath, MAX_PATH, "%s_%u", szResult, unSprayTintID );
}
szResult = m_autoptrInventoryImageGeneratedPath;
}
#endif
return szResult;
}
}
else if (
( hItemDefMusicKit && GetItemDefinition()->GetDefinitionIndex() == hItemDefMusicKit->GetDefinitionIndex() ) ||
( hItemDefMusicKitDefault && GetItemDefinition()->GetDefinitionIndex() == hItemDefMusicKitDefault->GetDefinitionIndex() )
)
{
static const CEconItemAttributeDefinition *pAttr_MusicID = GetItemSchema()->GetAttributeDefinitionByName( "music id" );
uint32 unMusicID;
if ( FindAttribute( pAttr_MusicID, &unMusicID ) )
{
const CEconMusicDefinition *pMusicDef = GetItemSchema()->GetMusicDefinition( unMusicID );
if ( pMusicDef )
{
return pMusicDef->GetInventoryImage();
}
}
}
#endif
return GetStaticData()->GetInventoryImage();
}
// Does this item use a custom generated image due to a custom paint and wear
bool CEconItemView::HasGeneratedInventoryImage( void ) const
{
return GetCustomPaintKitIndex() != 0 && ( GetCustomPaintKitWear() != 0.0f || GetCustomPaintKitSeed() != 0 );
}
//-----------------------------------------------------------------------------
// Purpose: Return the drawing data for the image to use for model panels containing this item
//-----------------------------------------------------------------------------
bool CEconItemView::GetInventoryImageData( int *iPosition, int *iSize )
{
if ( !GetStaticData() )
return false;
for ( int i = 0; i < 2; i++ )
{
iPosition[i] = GetStaticData()->GetInventoryImagePosition(i);
iSize[i] = GetStaticData()->GetInventoryImageSize(i);
}
return true;
}
//-----------------------------------------------------------------------------
// Purpose: Return the image to use for model panels containing this item
//-----------------------------------------------------------------------------
const char *CEconItemView::GetInventoryOverlayImage( int idx )
{
if ( !GetStaticData() )
return NULL;
return GetStaticData()->GetInventoryOverlayImage( idx );
}
int CEconItemView::GetInventoryOverlayImageCount( void )
{
if ( !GetStaticData() )
return 0;
return GetStaticData()->GetInventoryOverlayImageCount();
}
//-----------------------------------------------------------------------------
// Purpose: Return the model to use when displaying this model on the player character model, if any
//-----------------------------------------------------------------------------
const char *CEconItemView::GetPlayerDisplayModel( int iClass ) const
{
const CEconItemDefinition *pDef = GetStaticData();
if ( !pDef )
return NULL;
// If we have styles, give the style system a chance to change the mesh used for this
// player class.
if ( pDef->GetNumStyles() )
{
const CEconStyleInfo *pStyle = pDef->GetStyleInfo( GetItemStyle() );
// It's possible to get back a NULL pStyle if GetItemStyle() returns INVALID_STYLE_INDEX.
if ( pStyle )
{
#if defined( TF_DLL ) || defined( TF_CLIENT_DLL )
// TF styles support per-class models.
const CTFStyleInfo *pTFStyle = assert_cast<const CTFStyleInfo *>( pStyle );
if ( pTFStyle->GetPlayerDisplayModel( iClass ) )
return pTFStyle->GetPlayerDisplayModel( iClass );
#endif // defined( TF_DLL ) || defined( TF_CLIENT_DLL )
if ( pStyle->GetBasePlayerDisplayModel() )
return pStyle->GetBasePlayerDisplayModel();
}
}
#if defined( TF_DLL ) || defined( TF_CLIENT_DLL )
// If we don't have a style, we still a couple potential overrides.
if ( iClass >= 0 && iClass < LOADOUT_COUNT )
{
// We don't support overriding meshes in the visuals section, but we might still be overriding
// the model for each class at the schema level.
const CTFItemDefinition *pTFDef = dynamic_cast<const CTFItemDefinition *>( pDef );
if ( pTFDef )
{
const char *pszModel = pTFDef->GetPlayerDisplayModel(iClass);
if ( pszModel && pszModel[0] )
return pszModel;
}
}
#endif // defined( TF_DLL ) || defined( TF_CLIENT_DLL )
return pDef->GetBasePlayerDisplayModel();
}
#if defined(CLIENT_DLL) || defined(GAME_DLL)
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
int CEconItemView::GetSkin() const
{
// Do we have a style set?
if ( GetStaticData()->GetNumStyles() )
return GetStaticData()->GetStyleSkin( GetItemStyle() );
// Do we have per-team skins set?
const AssetInfo *pVisData = GetStaticData()->GetAssetInfo();
if ( pVisData )
return pVisData->iSkin;
return 0;
}
#endif // defined(CLIENT_DLL) || defined(GAME_DLL)
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
const char *CEconItemView::GetIconDisplayModel()
{
const CEconItemDefinition *pData = GetStaticData();
if ( !pData )
return NULL;
return pData->GetIconDisplayModel();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
const char *CEconItemView::GetPedestalDisplayModel()
{
const CEconItemDefinition *pData = GetStaticData();
if ( !pData )
return NULL;
static CSchemaItemDefHandle hItemDefMusicKit( "musickit" );
static CSchemaItemDefHandle hItemDefMusicKitDefault( "musickit_default" );
if (
( hItemDefMusicKit && GetItemDefinition()->GetDefinitionIndex() == hItemDefMusicKit->GetDefinitionIndex() ) ||
( hItemDefMusicKitDefault && GetItemDefinition()->GetDefinitionIndex() == hItemDefMusicKitDefault->GetDefinitionIndex() )
)
{
static const CEconItemAttributeDefinition *pAttr_MusicID = GetItemSchema()->GetAttributeDefinitionByName( "music id" );
uint32 unMusicID;
if ( FindAttribute( pAttr_MusicID, &unMusicID ) )
{
const CEconMusicDefinition *pMusicDef = GetItemSchema()->GetMusicDefinition( unMusicID );
if ( pMusicDef )
{
return pMusicDef->GetPedestalDisplayModel();
}
}
}
return pData->GetPedestalDisplayModel();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
const char *CEconItemView::GetBuyMenuDisplayModel()
{
const CEconItemDefinition *pData = GetStaticData();
if ( !pData )
return NULL;
return pData->GetBuyMenuDisplayModel();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
const char *CEconItemView::GetWorldDroppedModel()
{
const CEconItemDefinition *pData = GetStaticData();
if ( !pData )
return NULL;
return pData->GetWorldDroppedModel();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
const char *CEconItemView::GetMagazineModel()
{
const CEconItemDefinition *pData = GetStaticData();
if ( !pData )
return NULL;
return pData->GetMagazineModel();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
const char *CEconItemView::GetScopeLensMaskModel()
{
const CEconItemDefinition *pData = GetStaticData();
if ( !pData )
return NULL;
return pData->GetScopeLensMaskModel();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
const char *CEconItemView::GetStatTrakModelByType( int nStatTrakType )
{
const CEconItemDefinition *pData = GetStaticData();
if ( !pData )
return NULL;
return pData->GetStatTrakModelByType( nStatTrakType );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
const char *CEconItemView::GetUidModel()
{
const CEconItemDefinition *pData = GetStaticData();
if ( !pData )
return NULL;
return pData->GetUidModel();
}
// Purpose:
#if defined(CSTRIKE_CLIENT_DLL)
const char *CEconItemView::GetStickerSlotModelBySlotIndex( int nIndex )
{
const CEconItemDefinition *pData = GetStaticData();
if ( !pData )
return NULL;
return pData->GetStickerSlotModelBySlotIndex( nIndex );
}
int CEconItemView::GetNumSupportedStickerSlots( void )
{
const CEconItemDefinition *pData = GetStaticData();
if ( !pData )
return 0;
return pData->GetNumSupportedStickerSlots();
}
Vector CEconItemView::GetStickerSlotWorldProjectionStartBySlotIndex( int nIndex )
{
const CEconItemDefinition *pData = GetStaticData();
if ( !pData )
return Vector(0,0,0);
return pData->GetStickerSlotWorldProjectionStartBySlotIndex( nIndex );
}
Vector CEconItemView::GetStickerSlotWorldProjectionEndBySlotIndex( int nIndex )
{
const CEconItemDefinition *pData = GetStaticData();
if ( !pData )
return Vector(0,0,0);
return pData->GetStickerSlotWorldProjectionEndBySlotIndex( nIndex );
}
const char *CEconItemView::GetStickerWorldModelBoneParentNameBySlotIndex( int nIndex )
{
const CEconItemDefinition *pData = GetStaticData();
if ( !pData )
return NULL;
return pData->GetStickerWorldModelBoneParentNameBySlotIndex( nIndex );
}
const char *CEconItemView::GetStickerSlotMaterialBySlotIndex( int nIndex )
{
const CEconItemDefinition *pData = GetStaticData();
if ( !pData )
return NULL;
return pData->GetStickerSlotMaterialBySlotIndex( nIndex );
}
IMaterial *CEconItemView::GetStickerIMaterialBySlotIndex( int nIndex, bool bThirdPerson )
{
FOR_EACH_VEC( m_pStickerMaterials, i )
{
if (m_pStickerMaterials[i].m_nSlotIndex == nIndex)
{
if ( bThirdPerson == true )
{
return m_pStickerMaterials[i].m_pMaterialReferenceThirdPerson;
}
else
{
return m_pStickerMaterials[i].m_pMaterialReferenceFirstPerson;
}
}
}
return NULL;
}
#endif //(CSTRIKE_CLIENT_DLL)
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
const char *CEconItemView::GetWorldDisplayModel()
{
const CEconItemDefinition *pData = GetStaticData();
if ( !pData )
return NULL;
return pData->GetWorldDisplayModel();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
const char *CEconItemView::GetExtraWearableModel()
{
const CEconItemDefinition *pData = GetStaticData();
if ( !pData )
return NULL;
return pData->GetExtraWearableModel();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
int CEconItemView::GetQualityParticleType()
{
static attachedparticlesystem_t *pSparkleSystem = GetItemSchema()->FindAttributeControlledParticleSystem( "community_sparkle" );
CEconItem* pItem = GetSOCData();
if ( !pItem )
return 0;
if( GetSOCData()->GetQuality() == AE_SELFMADE || GetSOCData()->GetQuality() == AE_COMMUNITY )
return pSparkleSystem ? pSparkleSystem->nSystemID : 0;
else
return 0;
}
//-----------------------------------------------------------------------------
// Purpose: Return the animation set that this item wants the player to use (ie., melee, item1, pda)
//-----------------------------------------------------------------------------
int CEconItemView::GetAnimationSlot( void )
{
if ( !GetStaticData() )
return -1;
#if defined( CSTRIKE_DLL ) || defined( CSTRIKE_GC_DLL )
return -1;
#else
return GetStaticData()->GetAnimSlot();
#endif
}
//-----------------------------------------------------------------------------
// Purpose: Return an int that indicates whether the item should be dropped from a dead owner.
//-----------------------------------------------------------------------------
int CEconItemView::GetDropType( void )
{
if ( !GetStaticData() )
return 0;
return GetStaticData()->GetDropType();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CEconItemView::DestroyAllAttributes( void )
{
m_AttributeList.DestroyAllAttributes();
m_NetworkedDynamicAttributesForDemos.DestroyAllAttributes();
NetworkStateChanged();
MarkDescriptionDirty();
}
#if defined ( GAME_DLL )
void CEconItemView::UpdateNetworkedCustomName()
{
const char* szName = GetSOCData() ? GetSOCData()->GetCustomName() : NULL;
if ( szName )
{
V_strncpy( m_szCustomName.GetForModify(), szName, MAX_ITEM_CUSTOM_NAME_DATABASE_SIZE );
}
}
#endif
// Done once, the first time need these values, since new KillEater attributes cannot be added during play
//
void CEconItemView::GenerateKillEaterTypeVector()
{
m_vCachedKillEaterTypes.RemoveAll();
m_vCachedKillEaterValues.RemoveAll();
for ( int i = 0; i < GetKillEaterAttrPairCount(); i++ )
{
const CEconItemAttributeDefinition *pScoreTypeAttribDef = GetKillEaterAttrPair_Type(i);
if ( !pScoreTypeAttribDef )
continue;
attrib_value_t nValue;
if ( FindAttribute( pScoreTypeAttribDef, &nValue ) )
{
m_vCachedKillEaterTypes.Insert( nValue );
}
}
}
void CEconItemView::GetKillEaterTypes( CUtlSortVector<uint32> &types )
{
if ( !m_bKillEaterTypesCached )
{
GenerateKillEaterTypeVector();
m_bKillEaterTypesCached = true;
}
types = m_vCachedKillEaterTypes;
return;
}
int32 CEconItemView::GetKillEaterValueByType( uint32 uKillEaterType )
{
uint32 uiThisSectionOfCodeMightHaveBugs = 1;
/*
BUG - BUG - BUG - BUG - BUG - BUG
There are a bunch of bugs with the way StatTrak values are networked.
I fixed it in shipped code, but cannot back merge the fix due to how
concepts changed in trunk. Together with additional StatTrak module
and new attribute networking it's highly likely that this system needs
to be rewritten. If somebody integrates this to rel and sees this huge
diff then make sure you test all combinations of following:
Weapon Type Test Cases
------------------- -----------------
non-StatTrak weapon live match, you playing with your own weapon
StatTrak weapon with 0 kills live match, somebody else on TEAM_SPECTATOR watching you
StatTrak weapon with >0 kills live match, watching via GOTV master connection on TV port of active game server
StatTrak weapon getting a kill from 0 live match, watching via GOTV relay, relay connected to GOTV master
StatTrak weapon with >0 kills getting a kill playing back GOTV demo while logged in to Steam as your own account
playing back GOTV demo while logged in to Steam as a different account
all the above cases, but StatTrak weapon
wielded by non-owner
Change: 1958395
Date: 10/18/2013 2:52:36 PM
Client: vitaliy_valve_csgo_work_2012
User: vitaliy
Status: submitted
Type: public
Description:
[ CSGO - StatTrak's GOTV and Demos ]
Fixed different code paths that were incorrectly
handling bitpatterns of stattrak values.
When networking -1 value as only 20 bits on the
recieving side we would not get -1, but rather some
large 20-bit number and that was causing bad display.
Also on GOTV relays we wouldn't have the SO caches
of players and we were incorrectly sending bitpattern
for the fallback value through a bunch of broken attrlist
code that would on the display end get float bit pattern
into an integer pointer resulting in a garbage number.
Now StatTrak guns seem to display all correct values
in game, in GOTV demos, on GOTV masters and on GOTV
relays.
Also increased streams count to 6 for new UI.
JobStatus:
Jobs:
Files:
//depot/Valve/branch/cstrike15_pcbeta/source/cstrike15/src/game/client/cstrike15/Scaleform/HUD/sfhudweaponpanel.cpp#22
//depot/Valve/branch/cstrike15_pcbeta/source/cstrike15/src/game/client/cstrike15/Scaleform/components/scaleformcomponent_streams.cpp#6
//depot/Valve/branch/cstrike15_pcbeta/source/cstrike15/src/game/shared/econ/econ_entity.cpp#15
BUG - BUG - BUG - BUG - BUG - BUG
BUG - BUG - BUG - BUG - BUG - BUG
There are a bunch of bugs with the way StatTrak values are networked.
I fixed it in shipped code, but cannot back merge the fix due to how
concepts changed in trunk. Together with additional StatTrak module
and new attribute networking it's highly likely that this system needs
to be rewritten. If somebody integrates this to rel and sees this huge
diff then make sure you test all combinations of following:
Weapon Type Test Cases
------------------- -----------------
non-StatTrak weapon live match, you playing with your own weapon
StatTrak weapon with 0 kills live match, somebody else on TEAM_SPECTATOR watching you
StatTrak weapon with >0 kills live match, watching via GOTV master connection on TV port of active game server
StatTrak weapon getting a kill from 0 live match, watching via GOTV relay, relay connected to GOTV master
StatTrak weapon with >0 kills getting a kill playing back GOTV demo while logged in to Steam as your own account
playing back GOTV demo while logged in to Steam as a different account
all the above cases, but StatTrak weapon
wielded by non-owner
Change: 1958395
Date: 10/18/2013 2:52:36 PM
Client: vitaliy_valve_csgo_work_2012
User: vitaliy
Status: submitted
Type: public
Description:
[ CSGO - StatTrak's GOTV and Demos ]
Fixed different code paths that were incorrectly
handling bitpatterns of stattrak values.
When networking -1 value as only 20 bits on the
recieving side we would not get -1, but rather some
large 20-bit number and that was causing bad display.
Also on GOTV relays we wouldn't have the SO caches
of players and we were incorrectly sending bitpattern
for the fallback value through a bunch of broken attrlist
code that would on the display end get float bit pattern
into an integer pointer resulting in a garbage number.
Now StatTrak guns seem to display all correct values
in game, in GOTV demos, on GOTV masters and on GOTV
relays.
Also increased streams count to 6 for new UI.
JobStatus:
Jobs:
Files:
//depot/Valve/branch/cstrike15_pcbeta/source/cstrike15/src/game/client/cstrike15/Scaleform/HUD/sfhudweaponpanel.cpp#22
//depot/Valve/branch/cstrike15_pcbeta/source/cstrike15/src/game/client/cstrike15/Scaleform/components/scaleformcomponent_streams.cpp#6
//depot/Valve/branch/cstrike15_pcbeta/source/cstrike15/src/game/shared/econ/econ_entity.cpp#15
BUG - BUG - BUG - BUG - BUG - BUG
*/
++ uiThisSectionOfCodeMightHaveBugs;
CEconItem *pSOItem = GetSOCData();
if ( pSOItem && m_nKillEaterValuesCacheFrame >= pSOItem->GetSOUpdateFrame() ) // The SO cache has not been recently updated
{
// ..so we return our cached values
CUtlHashtable<uint32, int32>::handle_t hh = m_vCachedKillEaterValues.Find( uKillEaterType );
if ( m_vCachedKillEaterValues.IsValidHandle( hh ) )
return m_vCachedKillEaterValues.Element( hh );
}
#ifdef CLIENT_DLL
pSOItem = 0;
#endif
// Only use fallback if there's no SO cache!
int32 nKillEater = -1;
// TODO: This function does not update the killeater count after each kill; rather it only updates when the weapon
// is instantiated in the world (buying)
for ( int32 i = 0; i < GetKillEaterAttrPairCount(); i++ )
{
const CEconItemAttributeDefinition *pKillEaterAltScoreTypeAttrDef = GetKillEaterAttrPair_Type( i );
if ( !pKillEaterAltScoreTypeAttrDef )
continue;
attrib_value_t unValue;
if ( !FindAttribute( pKillEaterAltScoreTypeAttrDef, &unValue ) )
continue;
// If this isn't the type we're tracking, ignore it
if ( unValue != uKillEaterType )
continue;
const CEconItemAttributeDefinition *pKillEaterAltAttrDef = GetKillEaterAttrPair_Score( i );
if ( pKillEaterAltAttrDef )
{
// To get live attribute changes we need to get them from the SOCache because the instance of EconItemView won't have them until we spawn a new copy.
attrib_value_t unKillEaterAltScore = 0;
if ( pSOItem )
{
if ( pSOItem->FindAttribute( pKillEaterAltAttrDef, &unKillEaterAltScore ) )
{
nKillEater = unKillEaterAltScore;
}
}
else
{
if ( FindAttribute( pKillEaterAltAttrDef, &unKillEaterAltScore ) )
{
// The fallback might be larger
nKillEater = Max( nKillEater, (int32)unKillEaterAltScore );
}
}
break;
}
}
if ( !pSOItem )
{
// Don't cache when playing back from networked dynamic attributes
m_vCachedKillEaterValues.Remove( uKillEaterType );
m_vCachedKillEaterValues.Insert( uKillEaterType, nKillEater );
}
m_nKillEaterValuesCacheFrame = gpGlobals->framecount;
return nKillEater;
}
#if defined(CSTRIKE_CLIENT_DLL)
IVisualsDataProcessor *CEconItemView::GetVisualsDataProcessorByName( const char* szName ) const
{
FOR_EACH_VEC( m_ppVisualsDataProcessors, i )
{
IVisualsDataProcessor* pVDP = m_ppVisualsDataProcessors[ i ];
if ( V_strcmp( pVDP->GetOriginalMaterialBaseName(), szName ) == 0 )
return pVDP;
}
return NULL;
}
void CEconItemView::CreateCustomClothingMaterials( const char *pchSkinIdent, int nSlotId, int nTeam, bool bIgnorePicMip, CompositeTextureSize_t diffuseTextureSize )
{
int nPaintKit = GetCustomPaintKitIndex();
if ( nPaintKit == 0 ) // default paintkit, no need to composite
return;
if ( !GetPlayerDisplayModel( 0 ) )
{
AssertMsg1( false, "Failed to GetPlayerDisplayModel for %s.", pchSkinIdent );
return;
}
//read in paint kit
const CPaintKit *pPaintKit = GetItemSchema()->GetPaintKitDefinition( nPaintKit );
if ( !pPaintKit )
{
AssertMsg2( false, "Failed to find paintkit index %i for %s.", nPaintKit, pchSkinIdent);
return;
}
MDLHandle_t modelHandle = mdlcache->FindMDL( GetPlayerDisplayModel( 0 ) );
//MDLHandle_t modelHandle = mdlcache->FindMDL( GetWorldDisplayModel() );
if ( modelHandle == MDLHANDLE_INVALID )
{
AssertMsg1( false, "Failed to find player model handle for %s.", pchSkinIdent );
return;
}
studiohdr_t *pStudioHdr = g_pMDLCache->GetStudioHdr( modelHandle );
int nSeed = GetCustomPaintKitSeed();
float flWear = GetCustomPaintKitWear( 0 );
const CUtlVector< WeaponPaintableMaterial_t > *pPaintData = GetStaticData()->GetPaintData();
int nNumMaterialsToPaint = pPaintData->Count();
for ( int nCustomMaterialIndex = 0; nCustomMaterialIndex < nNumMaterialsToPaint; nCustomMaterialIndex++ )
{
CCSClothingVisualsDataCompare clothingCompareObject;
clothingCompareObject.m_nIndex = nPaintKit;
clothingCompareObject.m_nSeed = nSeed;
clothingCompareObject.m_flWear = flWear;
clothingCompareObject.m_nModelID = GetItemIndex();
clothingCompareObject.m_nLOD = 0;
clothingCompareObject.m_nTeamId = -1; // may need this later to differentiate teams
clothingCompareObject.m_bMirrorPattern = (*pPaintData)[ nCustomMaterialIndex ].m_bMirrorPattern;
clothingCompareObject.m_nMaterialId = nCustomMaterialIndex;
CCSClothingVisualsDataProcessor* pVisualsDataProcessor = new CCSClothingVisualsDataProcessor( Move(clothingCompareObject), &( (*pPaintData)[ nCustomMaterialIndex ] ), "CustomCharacter" );
bool bCreatedMaterial = false;
if ( pVisualsDataProcessor->HasCustomMaterial() )
{
// find the material in the models materials to get its index (used for overriding)
bool bFound = false;
int nModelMaterialIndex;
char szBaseName[ MAX_PATH ];
for ( nModelMaterialIndex = 0; nModelMaterialIndex < pStudioHdr->numtextures; nModelMaterialIndex++ )
{
mstudiotexture_t *pTexture = pStudioHdr->pTexture( nModelMaterialIndex );
V_FileBase( pTexture->pszName(), szBaseName, sizeof( szBaseName ) );
if ( V_stricmp( szBaseName, pVisualsDataProcessor->GetOriginalMaterialBaseName() ) == 0 )
{
bFound = true;
break;
}
}
if ( !bFound )
{
DevMsg( "Original material not found! Name: %s \n", pVisualsDataProcessor->GetOriginalMaterialBaseName() );
}
else
{
m_ppVisualsDataProcessors.AddToTail( pVisualsDataProcessor );
pVisualsDataProcessor->AddRef();
KeyValues *pVMTKeyValues = pVisualsDataProcessor->GenerateCustomMaterialKeyValues();
Assert( pVMTKeyValues );
CUtlVector< SCompositeTextureInfo > vecTextureInfo;
int nIndex = vecTextureInfo.AddToTail();
vecTextureInfo[nIndex].m_size = diffuseTextureSize;
vecTextureInfo[nIndex].m_format = COMPOSITE_TEXTURE_FORMAT_DXT5;
vecTextureInfo[nIndex].m_nMaterialParamID = MATERIAL_PARAM_ID_BASE_DIFFUSE_TEXTURE;
vecTextureInfo[nIndex].m_bSRGB = true;
vecTextureInfo[nIndex].m_pVisualsDataProcessor = pVisualsDataProcessor;
if (pVMTKeyValues->FindKey("$bumpmap", false))
{
nIndex = vecTextureInfo.AddToTail();
vecTextureInfo[nIndex].m_size = diffuseTextureSize;
vecTextureInfo[nIndex].m_format = COMPOSITE_TEXTURE_FORMAT_DXT5;
vecTextureInfo[nIndex].m_nMaterialParamID = MATERIAL_PARAM_ID_BUMP_MAP;
vecTextureInfo[nIndex].m_bSRGB = false;
vecTextureInfo[nIndex].m_pVisualsDataProcessor = pVisualsDataProcessor;
}
if (pVMTKeyValues->FindKey("$masks1", false))
{
nIndex = vecTextureInfo.AddToTail();
vecTextureInfo[nIndex].m_size = diffuseTextureSize;
vecTextureInfo[nIndex].m_format = COMPOSITE_TEXTURE_FORMAT_DXT5;
vecTextureInfo[nIndex].m_nMaterialParamID = MATERIAL_PARAM_ID_MASKS1_MAP;
vecTextureInfo[nIndex].m_bSRGB = false;
vecTextureInfo[nIndex].m_pVisualsDataProcessor = pVisualsDataProcessor;
}
ICustomMaterial *pCustomMaterial = g_pMaterialSystem->GetCustomMaterialManager()->GetOrCreateCustomMaterial( pVMTKeyValues, vecTextureInfo, bIgnorePicMip );
delete pVMTKeyValues; // copied inside GetCustomMaterial, no longer needed
if ( pCustomMaterial )
{
SetCustomMaterial( pCustomMaterial, nModelMaterialIndex );
bCreatedMaterial = true;
}
}
}
pVisualsDataProcessor->Release();
}
mdlcache->Release( modelHandle );
}
void CEconItemView::SetCustomNameOverride( const char *pszCustomName )
{
V_strncpy( m_szCustomNameOverride, pszCustomName, sizeof( m_szCustomNameOverride ) );
}
bool CEconItemView::ItemHasAnyStickersApplied( void )
{
return ( GetNumSupportedStickerSlots() > 0 && m_pStickerMaterials.Count() > 0 );
}
bool CEconItemView::ItemHasAnyFreeStickerSlots( void )
{
return ( GetNumSupportedStickerSlots() != m_pStickerMaterials.Count() );
}
int CEconItemView::GetStickerSlotFirstFreeFromIndex( int nIndex )
{
if ( !ItemHasAnyFreeStickerSlots() )
return -1;
for ( int i=0; i<=GetNumSupportedStickerSlots(); i++ )
{
if ( nIndex >= GetNumSupportedStickerSlots() )
nIndex = 0;
if ( !GetStickerAttributeBySlotIndexInt( nIndex, k_EStickerAttribute_ID, 0 ) )
return nIndex;
nIndex++;
}
return -1;
}
#ifdef _DEBUG
ConVar stickers_debug_material_spew( "stickers_debug_material_spew", "0", FCVAR_DEVELOPMENTONLY, "Spew generated sticker materials to the console." );
extern ConVar stickers_debug_randomize;
ConVar stickers_debug_randomize_min_index( "stickers_debug_randomize_min_index", "28", FCVAR_DEVELOPMENTONLY, "" );
ConVar stickers_debug_randomize_max_index( "stickers_debug_randomize_max_index", "47", FCVAR_DEVELOPMENTONLY, "" );
ConVar stickers_always_regenerate( "stickers_always_regenerate", "0", FCVAR_DEVELOPMENTONLY, "Don't attempt to load cached sticker materials" );
char g_szStickersOverridePath[256];
ConVar stickers_override_path_enabled( "stickers_override_path_enabled", "0", FCVAR_DEVELOPMENTONLY | FCVAR_CHEAT, "" );
ConVar stickers_override_wear( "stickers_override_wear", "0.0", FCVAR_DEVELOPMENTONLY | FCVAR_CHEAT, "" );
ConVar stickers_override_scale( "stickers_override_scale", "1.0", FCVAR_DEVELOPMENTONLY | FCVAR_CHEAT, "" );
ConVar stickers_override_rotation( "stickers_override_rotation", "0.0", FCVAR_DEVELOPMENTONLY | FCVAR_CHEAT, "" );
void cc_StickersOverridePath(const CCommand& args)
{
//e.g.: "stickers2/banana.vmt"
V_StripExtension( args[1], g_szStickersOverridePath, sizeof(g_szStickersOverridePath) );
stickers_always_regenerate.SetValue(1);
stickers_override_path_enabled.SetValue(1);
}
static ConCommand stickers_override_path("stickers_override_path", cc_StickersOverridePath, "", FCVAR_DEVELOPMENTONLY | FCVAR_CHEAT );
#endif
extern ConVar cl_righthand;
void CEconItemView::GenerateStickerMaterials( void )
{
//create sticker materials per item using the base sticker material for a particular slot + the item's sticker attributes for that slot
if ( !GetItemSchema() )
return;
int nSupportedStickerSlots = GetNumSupportedStickerSlots();
m_pStickerMaterials.RemoveAll();
for ( int i=0; i<nSupportedStickerSlots; i++ )
{
uint32 nStickerId = GetStickerAttributeBySlotIndexInt( i, k_EStickerAttribute_ID, 0 );
#ifdef _DEBUG
if ( stickers_debug_randomize.GetBool() )
{
if ( stickers_debug_randomize_min_index.GetInt() != -1 && stickers_debug_randomize_max_index.GetInt() != -1 )
{
stickers_debug_randomize_min_index.SetValue( MAX( stickers_debug_randomize_min_index.GetInt(), 0 ) );
stickers_debug_randomize_max_index.SetValue( MIN( stickers_debug_randomize_max_index.GetInt(), GetItemSchema()->GetStickerKitDefinitionCount()-1 ) );
nStickerId = RandomInt( stickers_debug_randomize_min_index.GetInt(), stickers_debug_randomize_max_index.GetInt() );
}
else
{
nStickerId = RandomInt( 1, GetItemSchema()->GetStickerKitDefinitionCount()-1 );
}
}
#endif
if ( nStickerId <= 0
#ifdef _DEBUG
&& !stickers_override_path_enabled.GetBool()
#endif
)
continue; // the sticker id for this slot is invalid, no need to build a sticker material
//get additional sticker attributes
float flStickerWear = GetStickerAttributeBySlotIndexFloat( i, k_EStickerAttribute_Wear, 0.0f );
float flStickerScale = GetStickerAttributeBySlotIndexFloat( i, k_EStickerAttribute_Scale, 1.0f );
float flStickerRotation = GetStickerAttributeBySlotIndexFloat( i, k_EStickerAttribute_Rotation, 0.0f );
#ifdef _DEBUG
if ( stickers_debug_randomize.GetBool() )
{
//get additional sticker attributes
flStickerWear = RandomFloat( 0.2f, 0.8f );
flStickerScale = RandomFloat( 0.95f, 1.05f );
flStickerRotation = RandomFloat( -10.0f, 10.0f );
}
if ( stickers_override_path_enabled.GetBool() )
{
//get additional sticker attributes
flStickerWear = stickers_override_wear.GetFloat();
flStickerScale = stickers_override_scale.GetFloat();
flStickerRotation = stickers_override_rotation.GetFloat();
}
#endif
//get the sticker base material path
const char *szStickerBaseMaterialPath = GetStickerSlotMaterialBySlotIndex(i);
if ( !szStickerBaseMaterialPath || szStickerBaseMaterialPath[0] == 0 )
{
#ifdef _DEBUG
ConColorMsg( Color(240,170,10,255), "Failed to get the base material path for sticker ID: %i\n", nStickerId );
#endif
continue; //failed to get the base material path
}
char szStickerShortName[32]; //this is the base name of the material AND model, by convention. e.g. "pist_deagle_decal_A"
V_FileBase( szStickerBaseMaterialPath, szStickerShortName, sizeof(szStickerShortName) );
const CStickerKit* pStickerKit = GetItemSchema()->GetStickerKitDefinition(nStickerId);
const char *szStickerMaterialSchemaName;
//get the sticker material schema name, e.g.: "navi"
#ifdef _DEBUG
if ( stickers_override_path_enabled.GetBool() )
{
szStickerMaterialSchemaName = g_szStickersOverridePath;
}
else
#endif
if (pStickerKit)
{
szStickerMaterialSchemaName = pStickerKit->sMaterialPath.String();
}
else
{
#ifdef _DEBUG
ConColorMsg(Color(240, 170, 10, 255), "Sticker kit definition is invalid for sticker ID: %i\n", nStickerId );
#endif
continue; //sticker kit definition is invalid
}
if ( !szStickerMaterialSchemaName || szStickerMaterialSchemaName[0] == 0 )
{
#ifdef _DEBUG
ConColorMsg(Color(240, 170, 10, 255), "Failed to get material schema name: %s\n", szStickerBaseMaterialPath);
#endif
continue; //failed to get material schema name
}
//build the full material vmt path from the schema name
char szStickerMaterialPath[MAX_PATH];
if ( pStickerKit && pStickerKit->bMaterialPathIsAbsolute )
{
V_snprintf( szStickerMaterialPath, sizeof(szStickerMaterialPath), "%s", szStickerMaterialSchemaName );
}
else
{
V_snprintf( szStickerMaterialPath, sizeof(szStickerMaterialPath), "materials/models/weapons/customization/stickers/%s.vmt", szStickerMaterialSchemaName );
}
//generate a unique but deterministic char sequence using rotation, scale, wear
char szStickerUniqueParamStr[32];
V_snprintf( szStickerUniqueParamStr, sizeof(szStickerUniqueParamStr), "%f_%f_%f", flStickerRotation, flStickerScale, flStickerWear );
int nParamHash = HashStringCaseless( szStickerUniqueParamStr );
//generate the desired material names
char szDesiredStickerMaterialNameFirstPerson[128];
V_snprintf( szDesiredStickerMaterialNameFirstPerson, sizeof(szDesiredStickerMaterialNameFirstPerson), "sticker_%s_%s_%i_1st", szStickerShortName, szStickerMaterialSchemaName, nParamHash );
char szDesiredStickerMaterialNameThirdPerson[128];
V_snprintf( szDesiredStickerMaterialNameThirdPerson, sizeof(szDesiredStickerMaterialNameThirdPerson), "sticker_%s_%s_%i_3rd", szStickerShortName, szStickerMaterialSchemaName, nParamHash );
//these materials may already exist, try to find them
IMaterial *stickerMaterialFirstPerson = materials->FindProceduralMaterial( szDesiredStickerMaterialNameFirstPerson, TEXTURE_GROUP_OTHER );
IMaterial *stickerMaterialThirdPerson = materials->FindProceduralMaterial( szDesiredStickerMaterialNameThirdPerson, TEXTURE_GROUP_OTHER );
#ifdef _DEBUG
if ( stickers_debug_material_spew.GetBool() )
{
if ( !stickerMaterialFirstPerson->IsErrorMaterial() )
{
ConColorMsg( Color(240,170,10,255), "Found pre-existing 1st-person sticker material: %s\n", szDesiredStickerMaterialNameFirstPerson );
}
if ( !stickerMaterialThirdPerson->IsErrorMaterial() )
{
ConColorMsg( Color(240,170,10,255), "Found pre-existing 3rd-person sticker material: %s\n", szDesiredStickerMaterialNameThirdPerson );
}
}
#endif
//if it exists already, great - otherwise we need to create it
if ( stickerMaterialFirstPerson->IsErrorMaterial() || stickerMaterialThirdPerson->IsErrorMaterial()
#ifdef _DEBUG
|| stickers_always_regenerate.GetBool()
#endif
)
{
//load the specific sticker material params, these are needed for both first and third person versions
KeyValues *pSpecificStickerMaterialKeyValues = new KeyValues( "vmt" );
KeyValues::AutoDelete autodelete_pSpecificStickerMaterialKeyValues( pSpecificStickerMaterialKeyValues );
if ( !pSpecificStickerMaterialKeyValues->LoadFromFile( g_pFullFileSystem, szStickerMaterialPath, "GAME" ) )
{
#ifdef _DEBUG
ConColorMsg(Color(240, 170, 10, 255), "Failed to load specific sticker material keyvalues: %s\n", szStickerMaterialPath);
#endif
continue; //failed to load specific sticker material keyvalues
}
//set wear, scale and rotation params in the specific sticker keyvalues
pSpecificStickerMaterialKeyValues->SetFloat( "$wearprogress", flStickerWear );
pSpecificStickerMaterialKeyValues->SetFloat( "$patternrotation", flStickerRotation );
pSpecificStickerMaterialKeyValues->SetFloat( "$patternscale", flStickerScale );
if ( stickerMaterialFirstPerson->IsErrorMaterial()
#ifdef _DEBUG
|| stickers_always_regenerate.GetBool()
#endif
)
{
//first person stickers need the base material params for custom per-sticker AO and wear remapping
KeyValues *pFirstPersonKeyValues = new KeyValues( "vmt" );
KeyValues::AutoDelete autodelete_pFirstPersonKeyValues( pFirstPersonKeyValues );
if ( !pFirstPersonKeyValues->LoadFromFile( g_pFullFileSystem, szStickerBaseMaterialPath, "GAME" ) )
{
#ifdef _DEBUG
ConColorMsg(Color(240, 170, 10, 255), "Failed to load base material keyvalues: %s\n", szStickerBaseMaterialPath);
#endif
continue; //failed to load base material keyvalues
}
if ( cl_righthand.GetBool() == false )
{
// left-handed viewmodels flip their stickers so they render correctly
pFirstPersonKeyValues->SetInt( "$mirrorhorizontal", 1 );
}
//now combine the keyvalues - this will produce the final sticker material params
pFirstPersonKeyValues->MergeFrom( pSpecificStickerMaterialKeyValues, KeyValues::MERGE_KV_UPDATE );
//now add the relevant sticker slot proxy list
KeyValues *pFirstPersonStickerProxyKeyValues = new KeyValues("vmt");
KeyValues::AutoDelete autodelete_pFirstPersonStickerProxyKeyValues(pFirstPersonStickerProxyKeyValues);
char szStickerMaterialProxyListPath[MAX_PATH];
V_snprintf(szStickerMaterialProxyListPath, sizeof(szStickerMaterialProxyListPath),
"materials/models/weapons/customization/stickers/default/sticker_proxies_%i.vmt", i);
if ( pFirstPersonStickerProxyKeyValues->LoadFromFile(g_pFullFileSystem, szStickerMaterialProxyListPath, "GAME"))
{
pFirstPersonKeyValues->MergeFrom( pFirstPersonStickerProxyKeyValues, KeyValues::MERGE_KV_UPDATE );
}
autodelete_pFirstPersonStickerProxyKeyValues.Detach();
#ifdef _DEBUG
if ( !stickerMaterialFirstPerson->IsErrorMaterial() && stickers_always_regenerate.GetBool())
{
ConColorMsg( Color(240,170,10,255), "WARNING: stickers_always_regenerate is TRUE, regenerating: %s\n", szDesiredStickerMaterialNameFirstPerson );
stickerMaterialFirstPerson->SetShaderAndParams( pFirstPersonKeyValues );
stickerMaterialFirstPerson->RefreshPreservingMaterialVars();
}
else
#endif
{
//create a new material with the final sticker material params
stickerMaterialFirstPerson = materials->CreateMaterial(szDesiredStickerMaterialNameFirstPerson, pFirstPersonKeyValues);
stickerMaterialFirstPerson->Refresh();
}
#ifdef _DEBUG
if ( stickers_debug_material_spew.GetBool() )
{
ConColorMsg( Color(240,170,10,255), "Generated first person sticker material: %s\n", szDesiredStickerMaterialNameFirstPerson );
KeyValuesDumpAsDevMsg( pFirstPersonKeyValues, 2 );
}
#endif
autodelete_pFirstPersonKeyValues.Detach();
}
if ( stickerMaterialThirdPerson->IsErrorMaterial()
#ifdef _DEBUG
|| stickers_always_regenerate.GetBool()
#endif
)
{
//create a 'thirdperson' material that only needs the specific sticker material params and a special $thirdperson param set
KeyValues *pThirdPersonKeyValues = new KeyValues("WeaponDecal");
KeyValues::AutoDelete autodelete_pThirdPersonKeyValues(pThirdPersonKeyValues);
pThirdPersonKeyValues->MergeFrom( pSpecificStickerMaterialKeyValues, KeyValues::MERGE_KV_UPDATE );
pThirdPersonKeyValues->SetInt( "$thirdperson", 1 );
#ifdef _DEBUG
if ( !stickerMaterialThirdPerson->IsErrorMaterial() && stickers_always_regenerate.GetBool())
{
ConColorMsg( Color(240,170,10,255), "WARNING: stickers_always_regenerate is TRUE, regenerating: %s\n", szDesiredStickerMaterialNameThirdPerson );
stickerMaterialThirdPerson->SetShaderAndParams( pThirdPersonKeyValues );
stickerMaterialThirdPerson->RefreshPreservingMaterialVars();
}
else
#endif
{
stickerMaterialThirdPerson = materials->CreateMaterial( szDesiredStickerMaterialNameThirdPerson, pThirdPersonKeyValues );
stickerMaterialThirdPerson->Refresh();
}
#ifdef _DEBUG
if ( stickers_debug_material_spew.GetBool() )
{
ConColorMsg( Color(240,170,10,255), "Generated third person sticker material: %s\n", szDesiredStickerMaterialNameThirdPerson );
KeyValuesDumpAsDevMsg( pThirdPersonKeyValues, 2 );
}
#endif
autodelete_pThirdPersonKeyValues.Detach();
}
}
//add material references for the materials to the econ item sticker material list
int nNewStickerIndex = m_pStickerMaterials.AddToTail();
m_pStickerMaterials[ nNewStickerIndex ].m_pMaterialReferenceFirstPerson.Init(stickerMaterialFirstPerson);
m_pStickerMaterials[ nNewStickerIndex ].m_pMaterialReferenceThirdPerson.Init(stickerMaterialThirdPerson);
m_pStickerMaterials[ nNewStickerIndex ].m_nSlotIndex = i;
}
}
void CEconItemView::UpdateGeneratedMaterial( bool bIgnorePicMip, CompositeTextureSize_t diffuseTextureSize )
{
if ( diffuseTextureSize != COMPOSITE_TEXTURE_SIZE_512 )
{
ClearCustomMaterials();
}
if ( ( GetCustomMaterialCount() == 0 ) && GetStaticData() )
{
const char *pItemClass = GetStaticData()->GetItemClass();
if ( pItemClass )
{
if ( V_strcmp( pItemClass, "tool" ) == 0 )
{
// paint
}
else if ( V_strcmp( pItemClass, "wearable_item" ) == 0 )
{
// clothing items
int nTeam = -1; // should remove this? Will we do anything team specific?
int nSlotId = GetStaticData()->GetLoadoutSlot( nTeam );
if ( GetStaticData()->GetPaintData()->Count() > 0 )
{
CreateCustomClothingMaterials( GetStaticData()->GetDefinitionName(), nSlotId, nTeam, bIgnorePicMip, diffuseTextureSize );
}
}
else
{
WEAPON_FILE_INFO_HANDLE handle = LookupWeaponInfoSlot( pItemClass );
if ( handle != GetInvalidWeaponInfoHandle() )
{
int nWeaponId = WeaponIdFromString( pItemClass );
if ( GetStaticData()->GetPaintData()->Count() > 0 )
{
CreateCustomWeaponMaterials( nWeaponId, bIgnorePicMip, diffuseTextureSize );
}
}
}
}
}
}
void CEconItemView::CreateCustomWeaponMaterials( int nWeaponId, bool bIgnorePicMip, CompositeTextureSize_t diffuseTextureSize )
{
if ( !GetWorldDisplayModel() )
{
AssertMsg1( false, "Failed to GetWorldDisplayModel for %s.", WeaponIdAsString((CSWeaponID)nWeaponId) );
return;
}
//read in paint kit
int nPaintKit = GetCustomPaintKitIndex();
const CPaintKit *pPaintKit = GetItemSchema()->GetPaintKitDefinition( nPaintKit );
if ( !pPaintKit )
{
AssertMsg2( false, "Failed to find paintkit index %i for %s.", nPaintKit, WeaponIdAsString((CSWeaponID)nWeaponId) );
return;
}
MDLHandle_t modelHandle = mdlcache->FindMDL( GetWorldDisplayModel() );
if ( modelHandle == MDLHANDLE_INVALID )
{
AssertMsg1( false, "Failed to find world display model handle for %s.", WeaponIdAsString((CSWeaponID)nWeaponId) );
return;
}
studiohdr_t *pStudioHdr = g_pMDLCache->GetStudioHdr( modelHandle );
//read in random seed
int nSeed = GetCustomPaintKitSeed();
float flWear = GetCustomPaintKitWear( pPaintKit->flWearDefault );
const CUtlVector< WeaponPaintableMaterial_t > *pPaintData = GetStaticData()->GetPaintData();
int nNumMaterialsToPaint = pPaintKit->bOnlyFirstMaterial ? 1 : pPaintData->Count();
for ( int nCustomMaterialIndex = 0; nCustomMaterialIndex < nNumMaterialsToPaint; nCustomMaterialIndex++ )
{
CCSWeaponVisualsDataCompare compareObject;
compareObject.m_nIndex = nPaintKit;
compareObject.m_nSeed = nSeed;
compareObject.m_flWear = flWear;
compareObject.m_nModelID = nWeaponId;
compareObject.m_nLOD = 1;
compareObject.m_flWeaponLength = (*pPaintData)[ nCustomMaterialIndex ].m_flWeaponLength;
compareObject.m_flUVScale = (*pPaintData)[ nCustomMaterialIndex ].m_flUVScale;
CCSWeaponVisualsDataProcessor* pVisualsDataProcessor = new CCSWeaponVisualsDataProcessor( Move(compareObject), &( (*pPaintData)[ nCustomMaterialIndex ] ), "CustomWeapon" );
bool bCreatedMaterial = false;
if ( pVisualsDataProcessor->HasCustomMaterial() )
{
// find the material in the models materials to get it's index (used for overriding)
bool bFound = false;
int nModelMaterialIndex;
char szBaseName[ MAX_PATH ];
for ( nModelMaterialIndex = 0; nModelMaterialIndex < pStudioHdr->numtextures; nModelMaterialIndex++ )
{
mstudiotexture_t *pTexture = pStudioHdr->pTexture( nModelMaterialIndex );
V_FileBase( pTexture->pszName(), szBaseName, sizeof( szBaseName ) );
if ( V_stricmp( szBaseName, pVisualsDataProcessor->GetOriginalMaterialBaseName() ) == 0 )
{
bFound = true;
break;
}
}
if ( !bFound )
{
DevMsg( "Original material not found! Name: %s \n \n", pVisualsDataProcessor->GetOriginalMaterialBaseName() );
}
else
{
m_ppVisualsDataProcessors.AddToTail( pVisualsDataProcessor );
pVisualsDataProcessor->AddRef();
KeyValues *pVMTKeyValues = pVisualsDataProcessor->GenerateCustomMaterialKeyValues();
Assert( pVMTKeyValues );
CUtlVector< SCompositeTextureInfo > vecTextureInfo;
int nIndex = vecTextureInfo.AddToTail();
vecTextureInfo[ nIndex ].m_size = diffuseTextureSize;
vecTextureInfo[ nIndex ].m_format = COMPOSITE_TEXTURE_FORMAT_DXT5;
vecTextureInfo[ nIndex ].m_nMaterialParamID = MATERIAL_PARAM_ID_BASE_DIFFUSE_TEXTURE;
vecTextureInfo[ nIndex ].m_bSRGB = true;
vecTextureInfo[ nIndex ].m_pVisualsDataProcessor = pVisualsDataProcessor;
nIndex = vecTextureInfo.AddToTail();
vecTextureInfo[ nIndex ].m_size = ( diffuseTextureSize != COMPOSITE_TEXTURE_SIZE_512 ) ? static_cast< CompositeTextureSize_t > ( Q_log2( pPaintKit->nViewModelExponentOverrideSize ) ) : COMPOSITE_TEXTURE_SIZE_256;
vecTextureInfo[ nIndex ].m_format = COMPOSITE_TEXTURE_FORMAT_DXT1;
vecTextureInfo[ nIndex ].m_nMaterialParamID = MATERIAL_PARAM_ID_PHONG_EXPONENT_TEXTURE;
vecTextureInfo[ nIndex ].m_bSRGB = false;
vecTextureInfo[ nIndex ].m_pVisualsDataProcessor = pVisualsDataProcessor;
ICustomMaterial *pCustomMaterial = g_pMaterialSystem->GetCustomMaterialManager()->GetOrCreateCustomMaterial( pVMTKeyValues, vecTextureInfo, bIgnorePicMip );
delete pVMTKeyValues; // copied inside GetCustomMaterial, no longer needed
if ( pCustomMaterial )
{
SetCustomMaterial( pCustomMaterial, nModelMaterialIndex );
bCreatedMaterial = true;
}
}
}
pVisualsDataProcessor->Release();
}
mdlcache->Release( modelHandle );
}
#endif
#ifdef CLIENT_DLL
ConVar econ_enable_inventory_images( "econ_enable_inventory_images", "1", FCVAR_CLIENTDLL | FCVAR_DEVELOPMENTONLY, "allow inventory image rendering for use by scaleform" );
ConVar econ_inventory_image_pinboard( "econ_inventory_image_pinboard", "0", FCVAR_CLIENTDLL | FCVAR_DEVELOPMENTONLY );
void CEconItemView::Update( void )
{
if ( !econ_enable_inventory_images.GetBool() || g_pRenderToRTHelper == NULL )
return;
// if an async load from cache is in progress, just return
if ( m_asyncFixupState == AFS_LoadingInProgress )
return;
// if we need to render our inventory image rgba, then attempt to now
if ( m_bInventoryImageRgbaRequested && m_pRenderToRTData == NULL && m_inventoryImageRgba.TellPut() == 0 )
{
if ( !m_bInventoryImageTriedCache )
{
// See if we've already got it on disc
if ( LoadCachedInventoryImage() )
{
return;
}
m_bInventoryImageTriedCache = true;
}
UpdateGeneratedMaterial( true );
// check to see if we can render now (custom materials ready?)
for ( int i = 0; i < GetCustomMaterialCount(); i++ )
{
if ( GetCustomMaterial( i ) && !GetCustomMaterial( i )->IsValid() )
{
InventoryManager()->InsertMaterialGenerationJob( this );
return;
}
}
m_pScratchVTF = CreateVTFTexture();
m_pRenderToRTMDL = new CMergedMDL;
m_pRenderToRTMDL->SetMDL( GetIconDisplayModel(), this );
m_pRenderToRTData = g_pRenderToRTHelper->CreateRenderToRTData( m_pRenderToRTMDL, m_pScratchVTF );
m_pRenderToRTData->m_pszIconNameSuffix = GetStaticData()->GetDefinitionName();
const InventoryImageData_t *pInventoryImageData = GetStaticData()->GetInventoryImageData();
if ( pInventoryImageData )
{
if ( econ_inventory_image_pinboard.GetBool() )
{
// Force the camera to a nice position for the pinboard
m_pRenderToRTData->m_cameraAngles = QAngle( 0.0f, -90.0f, 0.0f );
m_pRenderToRTData->m_cameraOffset = Vector( -25.0f, 0.0f, 0.0f );
}
else
{
matrix3x4_t matCamera;
m_pRenderToRTMDL->SetupBonesForAttachmentQueries();
if ( m_pRenderToRTMDL->GetAttachment( "camera_inventory", matCamera ) )
{
MatrixAngles( matCamera, m_pRenderToRTData->m_cameraAngles, m_pRenderToRTData->m_cameraOffset );
m_pRenderToRTData->m_bUsingExplicitModelCameraPosAnglesFromAttachment = true;
}
else
{
if ( pInventoryImageData->m_pCameraAngles )
{
m_pRenderToRTData->m_cameraAngles = *( pInventoryImageData->m_pCameraAngles );
}
if ( pInventoryImageData->m_pCameraOffset )
{
m_pRenderToRTData->m_cameraOffset = *( pInventoryImageData->m_pCameraOffset );
}
}
}
if ( pInventoryImageData->m_cameraFOV != -1.0f )
{
m_pRenderToRTData->m_cameraFOV = pInventoryImageData->m_cameraFOV;
}
int nLightDescIndex = ( pInventoryImageData->m_bOverrideDefaultLight ) ? 0 : m_pRenderToRTData->m_LightingState.m_nLocalLightCount;
for ( int i = 0; i < MATERIAL_MAX_LIGHT_COUNT; i++ )
{
if ( pInventoryImageData->m_pLightDesc[i] && nLightDescIndex < MATERIAL_MAX_LIGHT_COUNT )
{
m_pRenderToRTData->m_LightingState.m_pLocalLightDesc[ nLightDescIndex ] = *( pInventoryImageData->m_pLightDesc[ i ] );
nLightDescIndex++;
}
}
m_pRenderToRTData->m_LightingState.m_nLocalLightCount = nLightDescIndex;
}
g_pRenderToRTHelper->StartRenderToRT( m_pRenderToRTData );
}
if ( m_pRenderToRTData && m_pRenderToRTData->m_stage == RENDER_TO_RT_STAGE_DONE )
{
unsigned char *pSourceImage = m_pScratchVTF->ImageData( 0, 0, 0 );
// copy out the result into our buffer, allowing less than the full height (we use 4:3 icon images in CS:GO)
m_inventoryImageRgba.Purge();
if ( m_nInventoryImageRgbaWidth == m_pScratchVTF->Width() && m_nInventoryImageRgbaHeight <= m_pScratchVTF->Height() )
{
m_inventoryImageRgba.EnsureCapacity( m_nInventoryImageRgbaWidth * m_nInventoryImageRgbaHeight * 4 );
m_inventoryImageRgba.Put( pSourceImage, m_nInventoryImageRgbaWidth * m_nInventoryImageRgbaHeight * 4 );
}
else
{
// this normally shouldn't occur, but if it did then it's likely the requested width doesn't match the RenderToRtHelper RT width (currently that's 256)
Assert(0);
}
// call back to update the image in the UI
if ( m_pImageReadyCallback )
{
uint64 nItemID = GetItemID();
if ( !nItemID )
nItemID = CombinedItemIdMakeFromDefIndexAndPaint( GetItemDefinition()->GetDefinitionIndex(), GetCustomPaintKitIndex() );
// Request to save the buffer before calling the callback - that way the callback
// can modify m_inventoryImageRgba, such as swapping channels (ef when wrting the PNG file),
// without affecting the cached image
SaveInventoryImage( m_inventoryImageRgba );
m_pImageReadyCallback( this, m_inventoryImageRgba, m_nInventoryImageRgbaWidth, m_nInventoryImageRgbaHeight, nItemID );
m_inventoryImageRgba.Purge(); // no longer need to keep this buffer around
m_bInventoryImageRgbaRequested = false; // clear the requested flag, so that it can be requested again
m_bInventoryImageTriedCache = false; // clear tried cache flag, since it's now in the cache and we want it to get from there next time.
}
g_pRenderToRTHelper->DestroyRenderToRTData( m_pRenderToRTData );
m_pRenderToRTData = NULL;
delete m_pRenderToRTMDL;
m_pRenderToRTMDL = NULL;
DestroyVTFTexture( m_pScratchVTF );
m_pScratchVTF = NULL;
// No longer need the custom material - inventory image has been generated !
ClearCustomMaterials();
return;
}
if ( m_inventoryImageRgba.TellPut() == 0 && m_bInventoryImageRgbaRequested )
{
InventoryManager()->InsertMaterialGenerationJob( this );
}
}
static float g_pAttribWearMins[ 3 ] = { 0.00f, 0.20f, 0.56f };
static float g_pAttribWearMaxs[ 3 ] = { 0.07f, 0.48f, 1.00f };
char g_szGeneratingTradingIconPath[ MAX_PATH ] = "";
void BuildInventoryImagePath( char *pchOutfile, int nMaxPath, const char *pchDefName, const char *pchPaintKitName, bool bWear, float flWear, bool bLarge )
{
const char *pchOutputFolder = "resource/Flash/econ/default_generated/";
if ( g_szGeneratingTradingIconPath[ 0 ] )
{
pchOutputFolder = g_szGeneratingTradingIconPath;
}
if ( bWear )
{
V_snprintf( pchOutfile, nMaxPath, "%s%s_%s_%s%s.png",
pchOutputFolder,
pchDefName,
pchPaintKitName,
( flWear >= g_pAttribWearMins[ 2 ] ? "heavy" : ( flWear <= g_pAttribWearMaxs[ 0 ] ? "light" : "medium" ) ),
bLarge ? "_large" : "" );
}
else
{
//if reading the wear attribute fails, don't try to pack any attributes into the output image name
if ( !pchPaintKitName )
{
V_snprintf( pchOutfile, nMaxPath, "%s%s%s.png",
pchOutputFolder, pchDefName, bLarge ? "_large" : "" );
}
else
{
V_snprintf( pchOutfile, nMaxPath, "%s%s_%s%s.png",
pchOutputFolder, pchDefName, pchPaintKitName, bLarge ? "_large" : "" );
}
}
}
void SaveAndAddToP4( char *outfile, CUtlBuffer &outBuffer, bool bSkipAdd )
{
bool bNewFile = false;
FileHandle_t hFileOut = g_pFullFileSystem->Open( outfile, "rb" );
if ( hFileOut != FILESYSTEM_INVALID_HANDLE )
{
g_pFullFileSystem->Close( hFileOut );
}
else
{
bNewFile = true;
}
hFileOut = g_pFullFileSystem->Open( outfile, "wb" );
if ( hFileOut != FILESYSTEM_INVALID_HANDLE )
{
g_pFullFileSystem->Write( outBuffer.Base(), outBuffer.TellPut(), hFileOut );
g_pFullFileSystem->Close( hFileOut );
}
if ( bNewFile && !bSkipAdd )
{
char szFullIconPath[ MAX_PATH ];
g_pFullFileSystem->RelativePathToFullPath( outfile, "MOD", szFullIconPath, sizeof( szFullIconPath ) );
if ( !p4->IsFileInPerforce( szFullIconPath ) )
{
DevMsg( "P4 Add: %s\n", outfile );
g_p4factory->SetDummyMode( p4 == NULL );
g_p4factory->SetOpenFileChangeList( "Item Icons Auto Checkout" );
CP4AutoAddFile autop4_add( szFullIconPath );
}
else
{
DevMsg( "File Already in P4: %s\n", szFullIconPath );
}
}
}
void InventoryImageReadyCallback( const CEconItemView *pItemView, CUtlBuffer &rawImageRgba, int nWidth, int nHeight, uint64 nItemID )
{
const CEconItemDefinition *pItemDef = GetItemSchema()->GetItemDefinition( pItemView->GetStaticData()->GetDefinitionIndex() );
if ( !pItemDef )
return;
// It's actually BGRA! Swap B and R to get RGBA!
for ( int i = 0; i < rawImageRgba.Size(); i += 4 )
{
if ( i + 3 > rawImageRgba.Size() )
break;
unsigned char *pCurrent = reinterpret_cast< unsigned char* >( rawImageRgba.Base() ) + i;
unsigned char chTemp = *pCurrent;
*pCurrent = *( pCurrent + 2 );
*( pCurrent + 2 ) = chTemp;
}
CUtlBuffer outBuffer;
ImgUtl_WriteRGBAAsPNGToBuffer( reinterpret_cast< const unsigned char *>( rawImageRgba.Base() ), nWidth, nHeight, outBuffer );
static CSchemaAttributeDefHandle pAttr_PaintKitWear( "set item texture wear" );
float flWear = 0.0f;
bool bHasWear = FindAttribute_UnsafeBitwiseCast<attrib_value_t>( pItemView, pAttr_PaintKitWear, &flWear );
char outfile[ MAX_PATH ];
BuildInventoryImagePath( outfile, sizeof( outfile ),
pItemView->GetStaticData()->GetDefinitionName(),
pItemView->GetCustomPaintKitIndex() == 0 ? NULL : pItemView->GetCustomPaintKit()->sName.String(),
bHasWear, flWear,
false );
SaveAndAddToP4( outfile, outBuffer, econ_inventory_image_pinboard.GetBool() );
if ( g_szGeneratingTradingIconPath[ 0 ] && !econ_inventory_image_pinboard.GetBool() )
{
BuildInventoryImagePath( outfile, sizeof( outfile ),
pItemView->GetStaticData()->GetDefinitionName(),
pItemView->GetCustomPaintKitIndex() == 0 ? NULL : pItemView->GetCustomPaintKit()->sName.String(),
bHasWear, flWear,
true );
SaveAndAddToP4( outfile, outBuffer, false );
}
}
void CEconItemView::SaveInventoryImageAsPNG( int nWidth, int nHeight )
{
GetInventoryImageRgba( nWidth, nHeight, InventoryImageReadyCallback );
InventoryManager()->InsertMaterialGenerationJob( this );
}
struct KeyValueWith64BitID_t
{
uint64 nID;
KeyValues *pKV;
};
class CIconKeyValuesLess
{
public:
bool Less( KeyValueWith64BitID_t * const & src1, KeyValueWith64BitID_t * const & src2, void *pCtx )
{
return ( src1->nID < src2->nID );
}
};
const CUtlBuffer* CEconItemView::GetInventoryImageRgba( int nWidth, int nHeight, ImageReadyCallback_t pImageReadyCallback )
{
if ( !econ_enable_inventory_images.GetBool() )
{
m_inventoryImageRgba.EnsureCapacity( nWidth * nHeight * sizeof( int ) );
// just make a blank image of the size requested
for ( int i = 0; i < nWidth * nHeight; i++ )
{
m_inventoryImageRgba.PutInt( 0 );
}
return &m_inventoryImageRgba;
}
if ( !m_bInventoryImageRgbaRequested )
{
InventoryManager()->InsertMaterialGenerationJob( this );
}
m_bInventoryImageRgbaRequested = true;
if ( ( nWidth != m_nInventoryImageRgbaWidth ) || ( nHeight != m_nInventoryImageRgbaHeight ) )
{
m_inventoryImageRgba.Purge();
m_nInventoryImageRgbaWidth = nWidth;
m_nInventoryImageRgbaHeight = nHeight;
}
if ( m_inventoryImageRgba.TellPut() == 0 )
{
Assert( !m_pImageReadyCallback || ( m_pImageReadyCallback == pImageReadyCallback ) ); // only support generating one image at a time
m_pImageReadyCallback = pImageReadyCallback;
}
return &m_inventoryImageRgba;
}
bool CEconItemView::CanGenerateInventoryImageRgba()
{
// we can currently only generate images for weapons and gloves
// when that changes, we need to update this code.
if ( econ_enable_inventory_images.GetBool() )
{
if ( GetStaticData() )
{
const char *pItemClass = GetStaticData()->GetItemClass();
if ( pItemClass && V_strnicmp( pItemClass, "weapon_", 7 ) == 0 )
{
return true;
}
const char* szSubPos = GetStaticData()->GetRawDefinition()->GetString( "item_sub_position", NULL );
if ( szSubPos && V_stricmp( "clothing_hands", szSubPos ) == 0 )
{
return !GetItemDefinition()->IsDefaultSlotItem();
}
}
#if 0 // Relies on prebuilt maps of item tags which isn't in staging yet
if ( HasTag( "Weapon" ) )
{
return true;
}
if ( HasTag( "Hands" ) )
{
return true;
}
#endif
}
return false;
}
void CEconItemView::ClearInventoryImageRgba()
{
m_inventoryImageRgba.Purge();
int nItemIdHigh = m_iItemIDHigh.Get();
int nItemIdLow = m_iItemIDLow.Get();
if ( m_iItemID == 0 )
{
nItemIdHigh = 0xFFFFFFFF;
nItemIdLow = ( GetItemDefinition()->GetDefinitionIndex() << 16 );
nItemIdLow += GetCustomPaintKitIndex();
}
char szFileName[ MAX_PATH ];
V_snprintf( szFileName, sizeof( szFileName ), ECON_ITEM_GENERATED_ICON_DIR "%X-%X-%X.iic", ECON_ITEM_ICON_CACHE_VERSION, nItemIdHigh, nItemIdLow );
if ( g_pFullFileSystem->FileExists( szFileName ) )
g_pFullFileSystem->RemoveFile( szFileName );
InventoryManager()->InsertMaterialGenerationJob( this );
}
static void AsyncLoadCachedIntevtoryImageCallback( const FileAsyncRequest_t &request, int numReadBytes, FSAsyncStatus_t asyncStatus )
{
CEconItemView *pEconItemView = reinterpret_cast< CEconItemView * >( request.pContext );
if ( pEconItemView )
{
pEconItemView->m_nNumAsyncReadBytes = numReadBytes;
pEconItemView->m_asyncStatus = asyncStatus;
pEconItemView->m_asyncFixupState = CEconItemView::AFS_LoadingDone;
g_InventoryItemUpdateManager.AddItemViewToFixupList( pEconItemView );
}
}
void CEconItemView::FinishLoadCachedInventoryImage( void* pData, int numReadBytes, FSAsyncStatus_t asyncStatus )
{
if ( asyncStatus == FSASYNC_OK )
{
if ( m_pImageReadyCallback )
{
uint64 nItemID = GetItemID();
if ( !nItemID )
{
nItemID = CombinedItemIdMakeFromDefIndexAndPaint( GetItemDefinition()->GetDefinitionIndex(), GetCustomPaintKitIndex() );
}
m_pImageReadyCallback( this, m_inventoryImageRgba, m_nInventoryImageRgbaWidth, m_nInventoryImageRgbaHeight, nItemID );
}
m_bInventoryImageRgbaRequested = false; // clear the requested flag, so that it can be requested again
}
else
{
m_bInventoryImageTriedCache = true;
InventoryManager()->InsertMaterialGenerationJob( this );
}
m_inventoryImageRgba.Purge(); // no longer need to keep this buffer around
m_asyncFixupState = AFS_FixupDone;
// it's possible to get here without m_hAsyncControl being set when async mode is not on.
if ( m_hAsyncControl )
{
filesystem->AsyncRelease( m_hAsyncControl );
m_hAsyncControl = NULL;
}
}
bool CEconItemView::LoadCachedInventoryImage( void )
{
if ( !m_pImageReadyCallback )
return false;
// already Async Loading
if ( m_asyncFixupState == AFS_LoadingInProgress )
return true;
CleanInventoryImageCacheDir();
GenerateCachedInventoryImageName();
const int nBuffSize = ECON_ITEM_GENERATED_ICON_WIDTH * ECON_ITEM_GENERATED_ICON_HEIGHT * 4;
m_inventoryImageRgba.EnsureCapacity( nBuffSize );
// Already pending
Assert ( !m_hAsyncControl );
m_inventoryImageRgba.SeekPut( CUtlBuffer::SEEK_HEAD, nBuffSize );
// async load the file
FileAsyncRequest_t fileRequest;
fileRequest.pContext = (void *)this;
fileRequest.pfnCallback = ::AsyncLoadCachedIntevtoryImageCallback;
fileRequest.pData = m_inventoryImageRgba.Base();
fileRequest.pszFilename = m_szCurrentLoadCachedFileName;
fileRequest.priority = -1;
// queue for async load
filesystem->AsyncRead( fileRequest, &m_hAsyncControl );
// it's possible to get here without m_hAsyncControl being set when async mode is not on.
if ( m_hAsyncControl )
{
m_asyncFixupState = AFS_LoadingInProgress;
}
return true;
}
void CEconItemView::SaveInventoryImage( CUtlBuffer &rawImageRgba )
{
GenerateCachedInventoryImageName();
CUtlBuffer *pBuffer = new CUtlBuffer();
pBuffer->EnsureCapacity( rawImageRgba.TellPut() );
pBuffer->CopyBuffer( rawImageRgba );
FSAsyncStatus_t status = g_pFullFileSystem->AsyncWriteFile( m_szCurrentLoadCachedFileName, pBuffer, pBuffer->TellPut(), true );
if ( status < FSASYNC_OK )
{
Msg( "EconItemView: Failed to write cache file %s\n", m_szCurrentLoadCachedFileName );
}
}
void CEconItemView::GenerateCachedInventoryImageName()
{
int nItemIdHigh = m_iItemIDHigh.Get();
int nItemIdLow = m_iItemIDLow.Get();
if (m_iItemID == 0)
{
nItemIdHigh = 0xFFFFFFFF;
nItemIdLow = (GetItemDefinition()->GetDefinitionIndex() << 16);
nItemIdLow += GetCustomPaintKitIndex();
// Get the wear attribute
static CSchemaAttributeDefHandle pAttr_PaintKitWear( "set item texture wear" );
float flWear = 0.0f;
bool bHasWear = FindAttribute_UnsafeBitwiseCast<attrib_value_t>( this, pAttr_PaintKitWear, &flWear );
if ( bHasWear )
{
int nItemWear = ( flWear >= g_pAttribWearMins[ 2 ] ) ? 2 : ( flWear <= g_pAttribWearMaxs[ 0 ] ? 0 : 1 );
V_snprintf( m_szCurrentLoadCachedFileName, sizeof( m_szCurrentLoadCachedFileName ), ECON_ITEM_GENERATED_ICON_DIR "%X-%X-%X-%X.iic", ECON_ITEM_ICON_CACHE_VERSION, nItemIdHigh, nItemIdLow, nItemWear );
}
else
{
V_snprintf( m_szCurrentLoadCachedFileName, sizeof( m_szCurrentLoadCachedFileName ), ECON_ITEM_GENERATED_ICON_DIR "%X-%X-%X.iic", ECON_ITEM_ICON_CACHE_VERSION, nItemIdHigh, nItemIdLow );
}
}
else
{
V_snprintf( m_szCurrentLoadCachedFileName, sizeof( m_szCurrentLoadCachedFileName ), ECON_ITEM_GENERATED_ICON_DIR "%X-%X-%X.iic", ECON_ITEM_ICON_CACHE_VERSION, nItemIdHigh, nItemIdLow );
}
}
void CEconItemView::CleanInventoryImageCacheDir( void )
{
// Only do this check once per session
// and ensure that we've loaded at least one item into the inventory before doing the check
if ( m_sbHasCleanedInventoryImageCacheDir || InventoryManager()->GetLocalInventory()->GetItemCount() == 0 )
return;
// Only do this once per session!
m_sbHasCleanedInventoryImageCacheDir = true;
// Loop through all the cache files in the cache dir and delete ones that are an older version
FileFindHandle_t hSearchFile;
char szPath[ 512 ];
Q_strncpy( szPath, ECON_ITEM_GENERATED_ICON_DIR "*.iic", sizeof( szPath ) );
//char szCorrectPrefix[ MAX_PATH ];
//V_snprintf( szCorrectPrefix, sizeof( szCorrectPrefix ), "%X-", ECON_ITEM_ICON_CACHE_VERSION );
const char *pchFileName = g_pFullFileSystem->FindFirstEx( szPath, "MOD", &hSearchFile );
if ( pchFileName )
{
while ( pchFileName )
{
// read in exactly 3 numbers from the name
uint32 nVersion = 0;
uint32 nItemIdHigh = 0;
uint32 nItemIdLow = 0;
uint32 nItemWear = 0;
int nScanResult = sscanf( pchFileName, "%X-%X-%X-%X.iic", &nVersion, &nItemIdHigh, &nItemIdLow, &nItemWear );
bool bWipeIt = ( nScanResult != 3 ) && ( nScanResult != 4 );
if ( !bWipeIt )
{
if ( nVersion != ECON_ITEM_ICON_CACHE_VERSION )
{
bWipeIt = true;
}
else if ( nItemIdHigh != 0xFFFFFFFF )
{
bWipeIt = ( nScanResult != 3 ); // don't expect to have the wear in this case (item coming from the inventory)
if ( !bWipeIt )
{
// If we don't still have that item wipe it!
itemid_t ullItem = ( static_cast< uint64 >( nItemIdHigh ) << 32 ) + static_cast< uint64 >( nItemIdLow ) ;
bWipeIt = !InventoryManager()->GetLocalInventory()->GetInventoryItemByItemID( ullItem );
}
}
}
if ( bWipeIt )
{
char szFileName[ MAX_PATH ];
V_snprintf( szFileName, sizeof( szFileName ), ECON_ITEM_GENERATED_ICON_DIR "%s", pchFileName );
g_pFullFileSystem->RemoveFile( szFileName );
}
pchFileName = g_pFullFileSystem->FindNext( hSearchFile );
}
g_pFullFileSystem->FindClose( hSearchFile );
}
}
#endif
void CEconItemView::InitNetworkedDynamicAttributesForDemos( void )
{
#if defined( ECON_NETWORK_DYNAMIC_ATTRIBUTES_FOR_DEMOS )
if ( !GetSOCData() )
return;
m_NetworkedDynamicAttributesForDemos.DestroyAllAttributes();
bool bAdded = false;
const uint32 nNumAttributes = GetSOCData()->m_pCustomDataOptimizedObject ? GetSOCData()->m_pCustomDataOptimizedObject->m_numAttributes : 0;
for ( uint32 nAttr = 0; nAttr < nNumAttributes; nAttr++ )
{
const CEconItem::attribute_t &attr = * GetSOCData()->m_pCustomDataOptimizedObject->GetAttribute( nAttr );
const CEconItemAttributeDefinition *pDef = GetItemSchema()->GetAttributeDefinition( attr.m_unDefinitionIndex );
if ( !pDef )
continue;
const ISchemaAttributeType *pAttributeType = pDef->GetAttributeType();
if ( pAttributeType && !pAttributeType->BSupportsGameplayModificationAndNetworking() )
continue;
float flValue = 0.0f;
if ( pDef->IsStoredAsFloat() )
{
flValue = attr.m_value.asFloat;
}
else
{
flValue = *reinterpret_cast< const float* >( &attr.m_value.asUint32 );
}
CEconItemAttribute attribute( attr.m_unDefinitionIndex, flValue );
m_NetworkedDynamicAttributesForDemos.AddAttribute( &attribute );
bAdded = true;
}
if ( bAdded )
{
NetworkStateChanged();
}
#endif // #ifdef ECON_NETWORK_ATTRIBUTES
}
void CEconItemView::UpdateNetworkedDynamicAttributesForDemos( attrib_definition_index_t nDef, float flNewValue )
{
for( int nAttr = 0; nAttr < m_NetworkedDynamicAttributesForDemos.GetNumAttributes(); nAttr++ )
{
CEconItemAttribute *pAttribute = m_NetworkedDynamicAttributesForDemos.GetAttribute( nAttr );
if ( !pAttribute || pAttribute->GetAttribIndex() != nDef )
continue;
CEconItemAttribute attribute( nDef, flNewValue );
*pAttribute = attribute;
return;
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CEconItemView::AddAttribute( CEconItemAttribute *pAttribute )
{
m_AttributeList.AddAttribute( pAttribute );
#ifdef ECON_NETWORK_ATTRIBUTES
NetworkStateChanged();
#endif // #ifdef ECON_NETWORK_ATTRIBUTES
MarkDescriptionDirty();
}
//-----------------------------------------------------------------------------
// Purpose: Remove an attribute by name
//-----------------------------------------------------------------------------
void CEconItemView::SetOrAddAttributeValueByName( const char *pszAttribDefName, float flValue )
{
m_AttributeList.SetOrAddAttributeValueByName( pszAttribDefName, flValue );
MarkDescriptionDirty();
}
//-----------------------------------------------------------------------------
// Purpose: Remove an attribute by name
//-----------------------------------------------------------------------------
void CEconItemView::RemoveAttribute( const char *pszAttribDefName )
{
m_AttributeList.RemoveAttribute( pszAttribDefName );
MarkDescriptionDirty();
}
const bool CEconItemView::GetCombinedAttributeClassValue( float &flValue, string_t iszAttribClass ) const
{
for( int i = 0; i < m_AttributeList.m_Attributes.Count(); i++ )
{
const CEconItemAttribute *pAttrib = &(m_AttributeList.m_Attributes[ i ]);
if ( pAttrib )
{
const CEconItemAttributeDefinition *pStaticData = pAttrib->GetStaticData();
if ( pStaticData->GetCachedClass() == iszAttribClass )
{
switch ( pStaticData->GetDescriptionFormat() )
{
case ATTDESCFORM_VALUE_IS_PERCENTAGE:
case ATTDESCFORM_VALUE_IS_INVERTED_PERCENTAGE:
{
flValue *= pAttrib->GetValue();
}
return true;
case ATTDESCFORM_VALUE_IS_ADDITIVE:
case ATTDESCFORM_VALUE_IS_ADDITIVE_PERCENTAGE:
{
flValue += pAttrib->GetValue();
}
return true;
case ATTDESCFORM_VALUE_IS_REPLACE:
{
flValue = pAttrib->GetValue();
}
return true;
case ATTDESCFORM_VALUE_IS_OR:
{
int iTmp = flValue;
iTmp |= (int)pAttrib->GetValue();
flValue = iTmp;
}
return true;
default:
// Unknown value format.
Assert( 0 );
return true;
}
}
}
}
return false;
}
//-----------------------------------------------------------------------------
// Purpose: Returns the attribute matching the specified class, carried by this entity, if it exists.
//-----------------------------------------------------------------------------
CEconItemAttribute *CEconItemView::GetAttributeByClass( const char *szAttribClass )
{
return m_AttributeList.GetAttributeByClass( szAttribClass );
}
const CEconItemAttribute *CEconItemView::GetAttributeByClass( const char *szAttribClass ) const
{
return m_AttributeList.GetAttributeByClass( szAttribClass );
}
//-----------------------------------------------------------------------------
// Purpose: Returns the attribute that matches the attribute def index, if it exists
//-----------------------------------------------------------------------------
CEconItemAttribute *CEconItemView::GetAttributeByDefIndex( int iAttributeDefIndex )
{
return m_AttributeList.GetAttributeByDefIndex( iAttributeDefIndex );
}
const CEconItemAttribute *CEconItemView::GetAttributeByDefIndex( int iAttributeDefIndex ) const
{
return m_AttributeList.GetAttributeByDefIndex( iAttributeDefIndex );
}
//-----------------------------------------------------------------------------
// Purpose: Returns the attribute that matches the def name, if it exists
//-----------------------------------------------------------------------------
CEconItemAttribute *CEconItemView::GetAttributeByName( const char *pszAttribDefName )
{
return m_AttributeList.GetAttributeByName( pszAttribDefName );
}
const CEconItemAttribute *CEconItemView::GetAttributeByName( const char *pszAttribDefName ) const
{
return m_AttributeList.GetAttributeByName( pszAttribDefName );
}
extern const char *g_EffectTypes[NUM_EFFECT_TYPES];
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
#ifdef CLIENT_DLL
const wchar_t *CEconItemView::GetItemName( bool bUncustomized /*= false*/ ) const
{
static const wchar_t *pwzDefaultName = L"";
/** Removed for partner depot **/
return pwzDefaultName;
}
#endif
//-----------------------------------------------------------------------------
// Purpose: Get RGB modifying attribute value
//-----------------------------------------------------------------------------
int CEconItemView::GetModifiedRGBValue( bool bAltColor )
{
enum
{
kPaintConstant_Default = 0,
kPaintConstant_OldTeamColor = 1,
};
static CSchemaAttributeDefHandle pAttr_Paint( "set item tint rgb" );
static CSchemaAttributeDefHandle pAttr_Paint2( "set item tint rgb 2" );
// If we have no base paint color we don't do anything special.
float fRGB;
if ( !FindAttribute_UnsafeBitwiseCast<attrib_value_t>( this, pAttr_Paint, &fRGB ) )
return kPaintConstant_Default;
// See if we also have a secondary paint color.
uint32 unRGB = (uint32)fRGB,
unRGBAlt;
float fRGBAlt;
// Backwards compatibility for old team colored items.
if ( unRGB == kPaintConstant_OldTeamColor )
{
unRGB = RGB_INT_RED;
unRGBAlt = RGB_INT_BLUE;
}
else if ( FindAttribute_UnsafeBitwiseCast<attrib_value_t>( this, pAttr_Paint2, &fRGBAlt ) )
{
unRGBAlt = (uint32)fRGBAlt;
}
else
{
// By default our secondary color will match our primary if we can't find a replacement.
unRGBAlt = unRGB;
}
return bAltColor ? unRGBAlt : unRGB;
}
int CEconItemView::GetCustomPaintKitIndex( void ) const
{
static CSchemaAttributeDefHandle pAttr_PaintKit( "set item texture prefab" );
// If we have no base paint kit color we don't do anything special.
float flIndex;
if ( !FindAttribute_UnsafeBitwiseCast<attrib_value_t>( this, pAttr_PaintKit, &flIndex ) )
{
return 0;
}
return flIndex;
}
const char* CEconItemView::GetCustomPaintKitDbgName( void ) const
{
int idx = GetCustomPaintKitIndex();
const CPaintKit* pKit = GetItemSchema()->GetPaintKitDefinition( idx );
return pKit ? pKit->sName.Get() : "<UNPAINTED>";
}
bool CEconItemView::IsStyleUnlocked( int iStyle ) const
{
const CEconStyleInfo* pStyle = GetItemDefinition()->GetStyleInfo( iStyle );
if ( pStyle && !pStyle->GetUnlockInfo()->IsLockable() )
return true;
// Do we have the style unlocked?
static CSchemaAttributeDefHandle pAttr_UnlockedStyles( "unlocked styles" );
uint32 nUnlockedStyles;
if ( FindAttribute( pAttr_UnlockedStyles, &nUnlockedStyles ) )
{
if ( nUnlockedStyles & (1 << iStyle) )
{
return true;
}
}
return false;
}
bool CEconItemView::CanCollect( CEconItemView &subject )
{
#if 0
int nItemSet = GetItemSetIndex();
if ( nItemSet < 0 )
return false;
CEconItemSchema *pSchema = GetItemSchema();
if ( !pSchema )
return false;
const CEconItemSetDefinition *pItemSet = pSchema->GetItemSetByIndex( nItemSet );
if ( !pItemSet )
return false;
if ( !pItemSet->m_bIsCollection )
return false;
// Check and see if subject is in our item set.
FOR_EACH_VEC( pItemSet->m_ItemEntries, i )
{
unsigned int iIndex = pItemSet->m_ItemEntries[i].m_nItemDef;
const CEconItemDefinition *pItemDef = GetItemSchema()->GetItemDefinition( iIndex );
if ( !pItemDef )
continue;
if ( subject.GetStaticData()->GetDefinitionIndex() != pItemDef->GetDefinitionIndex() )
continue;
// Check and see if this item is already collected.
CEconItem* pItem = GetSOCData();
if ( !pItem )
continue;
const CEconItemAttributeDefinition* pCollectionAttrib = GetItemSchema()->GetAttributeDefinitionByName( "collection bits" );
if ( !pCollectionAttrib )
continue;
float flValue = 0;
pItem->HasCustomAttribute( pCollectionAttrib->GetDefinitionIndex(), &flValue );
uint32 iCollectionBits = *(int *) &flValue;
if ( iCollectionBits & (1 << i) )
{
return false;
}
else
{
return true;
}
}
#endif
return false;
}
uint64 CEconItemView::GetCustomUserTextureID()
{
static CSchemaAttributeDefHandle pAttr_CustomTextureLo( "custom texture lo" );
static CSchemaAttributeDefHandle pAttr_CustomTextureHi( "custom texture hi" );
uint32 unLowVal, unHighVal;
const bool bHasLowVal = FindAttribute( pAttr_CustomTextureLo, &unLowVal ),
bHasHighVal = FindAttribute( pAttr_CustomTextureHi, &unHighVal );
// We should have both, or neither. We should never have just one
Assert( bHasLowVal == bHasHighVal );
if ( bHasLowVal && bHasHighVal )
{
return ((uint64)unHighVal << 32) | (uint64)unLowVal;
}
// No attribute set
return 0;
}
const CPaintKit *CEconItemView::GetCustomPaintKit( void ) const
{
int nPaintKit = GetCustomPaintKitIndex();
return GetItemSchema()->GetPaintKitDefinition( nPaintKit );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CAttributeList::CAttributeList()
{
#if defined(CLIENT_DLL) || defined(GAME_DLL)
m_pManager = NULL;
#endif
m_Attributes.Purge();
}
#if defined(CLIENT_DLL) || defined(GAME_DLL)
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CAttributeList::SetManager( CAttributeManager *pManager )
{
m_pManager = pManager;
}
#endif
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CAttributeList::Init()
{
m_Attributes.Purge();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CAttributeList::IterateAttributes( class IEconItemAttributeIterator *pIterator )
{
Assert( pIterator );
FOR_EACH_VEC( m_Attributes, i )
{
CEconItemAttribute *pAttrInst = &m_Attributes[i];
const CEconItemAttributeDefinition *pAttrDef = pAttrInst->GetStaticData();
if ( !pAttrDef )
continue;
const ISchemaAttributeType *pAttrType = pAttrDef->GetAttributeType();
Assert( pAttrType );
Assert( pAttrType->BSupportsGameplayModificationAndNetworking() );
attribute_data_union_t value;
value.asFloat = pAttrInst->GetValue();
if ( !pAttrType->OnIterateAttributeValue( pIterator, pAttrDef, value ) )
return;
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CAttributeList::DestroyAllAttributes( void )
{
if ( m_Attributes.Count() )
{
m_Attributes.Purge();
UpdateManagerCache();
#if defined( ECON_NETWORK_ATTRIBUTES ) || defined( ECON_NETWORK_DYNAMIC_ATTRIBUTES_FOR_DEMOS )
NetworkStateChanged();
#endif // #if defined( ECON_NETWORK_ATTRIBUTES ) || defined( ECON_NETWORK_DYNAMIC_ATTRIBUTES_FOR_DEMOS )
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CAttributeList::AddAttribute( CEconItemAttribute *pAttribute )
{
Assert( pAttribute );
// Only add attributes to the attribute list if they have a definition we can
// pull data from.
if ( !pAttribute->GetStaticData() )
return;
m_Attributes.AddToTail( *pAttribute );
#ifdef ECON_NETWORK_ATTRIBUTES
NetworkStateChanged();
#endif // #ifdef ECON_NETWORK_ATTRIBUTES
UpdateManagerCache();
}
//-----------------------------------------------------------------------------
// Purpose: Remove an attribute by name
//-----------------------------------------------------------------------------
void CAttributeList::SetOrAddAttributeValueByName( const char *pszAttribDefName, float flValue )
{
// This should already be in the string table
const CEconItemAttributeDefinition *pDef = GetItemSchema()->GetAttributeDefinitionByName( pszAttribDefName );
if ( !pDef )
return;
int iAttributes = GetNumAttributes();
for ( int i = 0; i < iAttributes; i++ )
{
CEconItemAttribute *pAttribute = GetAttribute(i);
#if defined(CLIENT_DLL) || defined(GAME_DLL)
// Never combine into set bonus attributes, they're recreated regularly
if ( pAttribute->m_bSetBonus )
continue;
#endif
if ( pAttribute->GetAttribIndex() == pDef->GetDefinitionIndex() )
{
pAttribute->SetValue( flValue );
return;
}
}
// Didn't find it. Add a new one.
CEconItemAttribute attribute( pDef->GetDefinitionIndex(), flValue );
AddAttribute( &attribute );
}
//-----------------------------------------------------------------------------
// Purpose: Remove an attribute by name
//-----------------------------------------------------------------------------
void CAttributeList::RemoveAttribute( const char *pszAttribDefName )
{
int iAttributes = m_Attributes.Count();
for ( int i = 0; i < iAttributes; i++ )
{
if ( !strcmp( m_Attributes[i].GetStaticData()->GetDefinitionName(), pszAttribDefName ) )
{
m_Attributes.Remove( i );
UpdateManagerCache();
return;
}
}
}
//-----------------------------------------------------------------------------
// Purpose: Remove an attribute by index
//-----------------------------------------------------------------------------
void CAttributeList::RemoveAttributeByIndex( int iIndex )
{
if ( iIndex < 0 || iIndex >= GetNumAttributes() )
return;
m_Attributes.Remove( iIndex );
UpdateManagerCache();
}
//-----------------------------------------------------------------------------
// Purpose: Returns the attribute matching the specified class, carried by this entity, if it exists.
//-----------------------------------------------------------------------------
CEconItemAttribute *CAttributeList::GetAttributeByClass( const char *szAttribClass )
{
int iAttributes = GetNumAttributes();
for ( int i = 0; i < iAttributes; i++ )
{
CEconItemAttribute *pAttr = GetAttribute( i );
Assert( pAttr && pAttr->GetStaticData() );
if ( pAttr && pAttr->GetStaticData() && !V_strcmp( pAttr->GetStaticData()->GetAttributeClass(), szAttribClass ) )
return pAttr;
}
return NULL;
}
const CEconItemAttribute *CAttributeList::GetAttributeByClass( const char *szAttribClass ) const
{
int iAttributes = GetNumAttributes();
for ( int i = 0; i < iAttributes; i++ )
{
const CEconItemAttribute *pAttr = GetAttribute( i );
Assert( pAttr && pAttr->GetStaticData() );
if ( pAttr && pAttr->GetStaticData() && !V_strcmp( pAttr->GetStaticData()->GetAttributeClass(), szAttribClass ) )
return pAttr;
}
return NULL;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CEconItemAttribute *CAttributeList::GetAttributeByDefIndex( uint16 unAttrDefIndex )
{
int iAttributes = GetNumAttributes();
for ( int i = 0; i < iAttributes; i++ )
{
CEconItemAttribute *pAttr = GetAttribute( i );
Assert( pAttr && pAttr->GetStaticData() );
if ( pAttr && pAttr->GetStaticData() && pAttr->GetStaticData()->GetDefinitionIndex() == unAttrDefIndex )
return pAttr;
}
return NULL;
}
const CEconItemAttribute *CAttributeList::GetAttributeByDefIndex( uint16 unAttrDefIndex ) const
{
int iAttributes = GetNumAttributes();
for ( int i = 0; i < iAttributes; i++ )
{
const CEconItemAttribute *pAttr = GetAttribute( i );
Assert( pAttr && pAttr->GetStaticData() );
if ( pAttr && pAttr->GetStaticData() && pAttr->GetStaticData()->GetDefinitionIndex() == unAttrDefIndex )
return pAttr;
}
return NULL;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CEconItemAttribute *CAttributeList::GetAttributeByName( const char *pszAttribDefName )
{
const CEconItemAttributeDefinition *pDef = GetItemSchema()->GetAttributeDefinitionByName( pszAttribDefName );
if ( !pDef )
return NULL;
int iAttributes = m_Attributes.Count();
for ( int i = 0; i < iAttributes; i++ )
{
if ( m_Attributes[i].GetStaticData()->GetDefinitionIndex() == pDef->GetDefinitionIndex() )
return &m_Attributes[i];
}
return NULL;
}
const CEconItemAttribute *CAttributeList::GetAttributeByName( const char *pszAttribDefName ) const
{
const CEconItemAttributeDefinition *pDef = GetItemSchema()->GetAttributeDefinitionByName( pszAttribDefName );
if ( !pDef )
return NULL;
int iAttributes = m_Attributes.Count();
for ( int i = 0; i < iAttributes; i++ )
{
if ( m_Attributes[i].GetStaticData()->GetDefinitionIndex() == pDef->GetDefinitionIndex() )
return &m_Attributes[i];
}
return NULL;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CAttributeList& CAttributeList::operator=( const CAttributeList& src )
{
m_Attributes = src.m_Attributes;
#if defined(CLIENT_DLL) || defined(GAME_DLL)
// HACK: We deliberately don't copy managers, because attributelists are contained inside
// CEconItemViews, which we duplicate inside CItemModelPanels all the time. If the manager
// is copied, copies will mess with the attribute caches of the copied item.
// Our manager will be setup properly by the CAttributeManager itself if we have an associated entity.
m_pManager = NULL;
#endif
return *this;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CAttributeList::SetValue( CEconItemAttribute *pAttrib, float flValue )
{
#ifdef DEBUG
FOR_EACH_VEC( m_Attributes, i )
{
if ( &m_Attributes[i] == pAttrib )
{
pAttrib->SetValue( flValue );
return;
}
}
// Someone's not being honest, and setting an attribute's value via a CAttributeList that doesn't contain it.
Assert( 0 );
#else
pAttrib->SetValue( flValue );
UpdateManagerCache();
#endif
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CAttributeList::UpdateManagerCache( void )
{
#if defined(CLIENT_DLL) || defined(GAME_DLL)
if ( m_pManager )
{
m_pManager->ClearCache();
}
#endif
}