Modified source engine (2017) developed by valve and leaked in 2020. Not for commercial purporses
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.
 
 
 
 
 
 

1183 lines
37 KiB

//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=============================================================================//
#include "cbase.h"
#include "store/tf_store_preview_item_base.h"
#include "store/store_page.h"
#include "vgui/ISurface.h"
#include "vgui/IInput.h"
#include "vgui/ILocalize.h"
#include "gamestringpool.h"
#include "tf_item_inventory.h"
#include "tf_playermodelpanel.h"
#include "econ_item_system.h"
#include "item_model_panel.h"
#include "c_tf_gamestats.h"
#include "econ_ui.h"
#include "econ_item_tools.h"
#include "vgui_controls/MenuItem.h"
// memdbgon must be the last include file in a .cpp file!!!
#include <tier0/memdbgon.h>
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CTFStorePreviewItemPanelBase::CTFStorePreviewItemPanelBase( vgui::Panel *pParent, const char *pResFile, const char *pPanelName, CStorePage *pOwner )
: CStorePreviewItemPanel( pParent, pResFile, "storepreviewitem", pOwner )
{
ResetHandles();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFStorePreviewItemPanelBase::ResetHandles( void )
{
m_pPlayerModelPanel = NULL;
m_iCurrentClass = TF_CLASS_SCOUT;
m_iCurrentHeldItem = 0;
m_pClassIconMouseoverLabel = NULL;
m_pRotRightButton = NULL;
m_pRotLeftButton = NULL;
m_pNextWeaponButton = NULL;
m_pZoomButton = NULL;
m_pOptionsButton = NULL;
m_pTeamButton = NULL;
m_pDataTextRichText = NULL;
m_iCurrentIconPosition = 0;
m_iState = PS_ITEM;
m_unPaintDef = 0;
m_unPaintRGB0 = 0;
m_unPaintRGB1 = 0;
m_pPaintNameLabel = NULL;
m_pStyleNameLabel = NULL;
m_pCustomizeMenu = NULL;
m_pClassIcons.Purge();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFStorePreviewItemPanelBase::SetCycleLabelText( vgui::Label *pTargetLabel, const char *pCycleText )
{
if ( pTargetLabel )
{
pTargetLabel->SetText( pCycleText );
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFStorePreviewItemPanelBase::ApplySchemeSettings( vgui::IScheme *pScheme )
{
ResetHandles();
BaseClass::ApplySchemeSettings( pScheme );
m_pPlayerModelPanel = dynamic_cast<CTFPlayerModelPanel*>( FindChildByName("classmodelpanel") );
m_pClassIconMouseoverLabel = dynamic_cast<vgui::Label*>( FindChildByName("ClassUsageMouseoverLabel") );
m_pRotRightButton = dynamic_cast<CExButton*>( FindChildByName("RotRightButton") );
m_pRotLeftButton = dynamic_cast<CExButton*>( FindChildByName("RotLeftButton") );
m_pNextWeaponButton = dynamic_cast<CExButton*>( FindChildByName("NextWeaponButton") );
m_pZoomButton = dynamic_cast<CExButton*>( FindChildByName("ZoomButton") );
m_pOptionsButton = dynamic_cast<CExButton*>( FindChildByName("OptionsButton") );
m_pTeamButton = dynamic_cast<CExButton*>( FindChildByName("TeamButton") );
m_pPaintNameLabel = dynamic_cast<vgui::Label*>( FindChildByName("PaintNameLabel") );
m_pStyleNameLabel = dynamic_cast<vgui::Label*>( FindChildByName("StyleNameLabel") );
if ( m_pClassIconMouseoverLabel )
{
m_pClassIconMouseoverLabel->SetVisible( false );
}
// Find all the class images
CStorePreviewClassIcon *pClassImage = NULL;
int iIcon = 1;
do
{
pClassImage = dynamic_cast<CStorePreviewClassIcon*>( FindChildByName( VarArgs("ClassUsageImage%d",iIcon)) );
if ( pClassImage )
{
m_pClassIcons.AddToTail( pClassImage );
}
iIcon++;
} while ( pClassImage );
// Update our class icons. Hide them all first. The code below will unhide ones used.
for ( int i = 0; i < m_pClassIcons.Count(); i++ )
{
m_pClassIcons[i]->SetVisible( false );
}
SetState( PS_ITEM );
m_vecPaintCans.Purge();
const CEconItemSchema::ToolsItemDefinitionMap_t &toolDefs = GetItemSchema()->GetToolsItemDefinitionMap();
// Store all of the active paint can item defs
FOR_EACH_MAP_FAST( toolDefs, i )
{
const CEconItemDefinition *pItemDef = toolDefs[i];
// ignore everything that is not a paint can tool
const IEconTool *pEconTool = pItemDef->GetEconTool();
if ( pEconTool && !V_strcmp( pEconTool->GetTypeName(), "paint_can" ) )
{
m_vecPaintCans.AddToTail( pItemDef->GetDefinitionIndex() );
}
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFStorePreviewItemPanelBase::PerformLayout( void )
{
BaseClass::PerformLayout();
// center the icons (we need to redo some of the work of CStorePreviewItemPanel, because we
// center the base item icons along with our TF specific class ones)
int iNumItemIcons = 0;
FOR_EACH_VEC( m_pItemIcons, i )
{
if ( m_pItemIcons[i]->IsVisible() )
{
++iNumItemIcons;
}
}
int iNumClassIcons = 0;
FOR_EACH_VEC( m_pClassIcons, i )
{
if ( m_pClassIcons[i]->IsVisible() )
{
++iNumClassIcons;
}
}
if ( iNumItemIcons || iNumClassIcons )
{
int iCenterX = GetWide() / 2;
int interval = XRES(2);
int totalWidth = (iNumItemIcons * m_pItemIcons[0]->GetWide()) + (iNumClassIcons * m_pClassIcons[0]->GetWide()) + (interval * (iNumItemIcons + iNumClassIcons - 1));
int iX = iCenterX - ( totalWidth / 2 );
int posX, posY;
m_pItemIcons[0]->GetPos( posX, posY );
int iButton = 0;
for ( int i = 0; i < m_pItemIcons.Count(); i++ )
{
if ( m_pItemIcons[i]->IsVisible() )
{
m_pItemIcons[i]->SetPos( iX, posY );
iX += m_pItemIcons[i]->GetWide() + interval;
iButton++;
}
}
for ( int i = 0; i < m_pClassIcons.Count(); i++ )
{
if ( m_pClassIcons[i]->IsVisible() )
{
m_pClassIcons[i]->SetPos( iX, posY );
iX += m_pClassIcons[i]->GetWide() + interval;
iButton++;
}
}
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
static bool IsAnythingPaintable( const CUtlVector<CEconItemView*>& vecItems )
{
FOR_EACH_VEC( vecItems, i )
{
if ( vecItems[i]->GetStaticData()->GetCapabilities() & ITEM_CAP_PAINTABLE )
return true;
}
return false;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
static void UpdatePaintColorsForTeam( CTFPlayerModelPanel *pPlayerModelPanel, uint32 unRGB0, uint32 unRGB1 )
{
Assert( pPlayerModelPanel );
static CSchemaAttributeDefHandle pAttrDef_ItemTintRGB( "set item tint RGB" );
if ( !pAttrDef_ItemTintRGB )
return;
const CUtlVector<CEconItemView*> &items = pPlayerModelPanel->GetCarriedItems();
if ( !IsAnythingPaintable( items ) )
return;
for ( int i=0; i<items.Count(); ++i )
{
if ( items[i]->GetStaticData()->GetCapabilities() & ITEM_CAP_PAINTABLE )
{
items[i]->GetAttributeList()->SetRuntimeAttributeValue( pAttrDef_ItemTintRGB, pPlayerModelPanel->GetTeam() == TF_TEAM_RED ? unRGB0 : unRGB1 );
items[i]->InvalidateColor();
}
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFStorePreviewItemPanelBase::OnCommand( const char *command )
{
if ( !Q_strnicmp( command, "team_toggle", 11 ) )
{
if ( m_pPlayerModelPanel )
{
m_pPlayerModelPanel->SetTeam( m_pPlayerModelPanel->GetTeam() == TF_TEAM_RED ? TF_TEAM_BLUE : TF_TEAM_RED );
}
// Also toggle team paint color if necessary.
if ( m_unPaintRGB0 != m_unPaintRGB1 )
{
UpdatePaintColorsForTeam( m_pPlayerModelPanel, m_unPaintRGB0, m_unPaintRGB1 );
}
}
else if ( !Q_strnicmp( command, "zoom_toggle", 11 ) )
{
if ( m_pPlayerModelPanel )
{
m_pPlayerModelPanel->ToggleZoom();
}
}
else if ( !Q_strnicmp( command, "paint_toggle", 12 ) )
{
CyclePaint();
}
else if ( !Q_strnicmp( command, "set_red", 7 ) )
{
if ( m_pPlayerModelPanel )
{
m_pPlayerModelPanel->SetSkin( 0 );
}
return;
}
else if ( !Q_strnicmp( command, "set_blu", 7 ) )
{
if ( m_pPlayerModelPanel )
{
m_pPlayerModelPanel->SetSkin( 1 );
}
return;
}
else if ( !Q_strnicmp( command, "next_weapon", 11 ) )
{
if ( m_pPlayerModelPanel )
{
const CUtlVector<CEconItemView*> &items = m_pPlayerModelPanel->GetCarriedItems();
int iLastItem = m_iCurrentHeldItem;
do
{
m_iCurrentHeldItem = ( m_iCurrentHeldItem + 1 ) % items.Count();
} while ( m_iCurrentHeldItem != iLastItem && m_pPlayerModelPanel->HoldItem( m_iCurrentHeldItem ) == false );
}
m_pPlayerModelPanel->SetTeam( m_pPlayerModelPanel->GetTeam() );
return;
}
else if ( !Q_strnicmp( command, "next_style", 10 ) )
{
CycleStyle();
return;
}
else if ( V_strncasecmp( command, "SetPaint", V_strlen( "SetPaint" ) ) == 0 )
{
item_definition_index_t iItemDef = V_atoi( &command[ V_strlen( "SetPaint" ) ] );
SetPaint( iItemDef );
}
else if ( V_strncasecmp( command, "SetStyle", V_strlen( "SetStyle" ) ) == 0 )
{
style_index_t unStyle = V_atoi( &command[ V_strlen( "SetStyle" ) ] );
SetStyle( unStyle );
}
else if ( V_strncasecmp( command, "SetUnusual", V_strlen( "SetUnusual" ) ) == 0 )
{
int iUnusual = V_atoi( &command[ V_strlen( "SetUnusual" ) ] );
SetUnusual( iUnusual );
}
else if ( FStrEq( command, "options" ) )
{
UpdateCustomizeMenu();
}
else
{
BaseClass::OnCommand( command );
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFStorePreviewItemPanelBase::OnClassIconSelected( KeyValues *data )
{
int iClass = data->GetInt( "class", 0 );
if ( iClass < TF_FIRST_NORMAL_CLASS || iClass >= TF_LAST_NORMAL_CLASS )
{
iClass = TF_CLASS_SCOUT;
}
m_iCurrentClass = iClass;
UpdateModelPanel();
SetState( PS_PLAYER );
// C_CTF_GameStats.Event_Store( IE_STORE_ITEM_PREVIEWED, NULL, NULL, m_iCurrentClass );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFStorePreviewItemPanelBase::SetPlayerModelVisible( bool bVisible )
{
if( m_pPlayerModelPanel )
{
m_pPlayerModelPanel->SetVisible( bVisible );
if ( m_pRotRightButton )
{
m_pRotRightButton->SetVisible( bVisible );
}
if ( m_pRotLeftButton )
{
m_pRotLeftButton->SetVisible( bVisible );
}
UpdatePlayerModelButtons();
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFStorePreviewItemPanelBase::PreviewItem( int iClass, CEconItemView *pItem, const econ_store_entry_t* pEntry )
{
m_iCurrentClass = 0;
BaseClass::PreviewItem( iClass, pItem, pEntry );
UpdateModelPanel();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFStorePreviewItemPanelBase::SetState( preview_state_t iState )
{
BaseClass::SetState( iState );
SetPlayerModelVisible( m_iState == PS_PLAYER );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
int CTFStorePreviewItemPanelBase::GetPreviewTeam() const
{
return m_pPlayerModelPanel
? m_pPlayerModelPanel->GetTeam()
: TF_TEAM_RED;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFStorePreviewItemPanelBase::UpdateIcons( void )
{
// Don't bother calling back to the base class UpdateIcons, because
// we'd need to redo it all with the class icons factored in.
bool bAdditionalIcons = false;
// Do the item icons first
if ( m_iState == PS_DETAILS )
{
// Show as many of the items in the bundle as possible
const CEconItemDefinition *pItemData = m_item.GetItemDefinition();
if ( pItemData )
{
const bundleinfo_t *pBundleInfo = pItemData->GetBundleInfo();
if ( pBundleInfo )
{
FOR_EACH_VEC( m_pItemIcons, i )
{
// If we haven't scrolled, the first item is the bundle itself
if ( m_iCurrentIconPosition == 0 && i == 0 )
{
m_pItemIcons[0]->SetItem( 0, &m_item );
continue;
}
int iItemPos = (i - 1 + m_iCurrentIconPosition);
if ( pBundleInfo->vecItemDefs.Count() > iItemPos && pBundleInfo->vecItemDefs[iItemPos] )
{
m_pItemIcons[i]->SetItem( i, pBundleInfo->vecItemDefs[iItemPos]->GetDefinitionIndex() );
m_pItemIcons[i]->SetVisible( true );
}
else
{
m_pItemIcons[i]->SetVisible( false );
}
}
bAdditionalIcons = (m_iCurrentIconPosition + m_pItemIcons.Count()) <= pBundleInfo->vecItemDefs.Count();
}
else if ( m_pItemIcons.Count() )
{
m_pItemIcons[0]->SetVisible( true );
m_pItemIcons[0]->SetItem( 0, &m_item );
FOR_EACH_VEC( m_pItemIcons, i )
{
if ( i != 0 )
{
m_pItemIcons[i]->SetVisible( false );
}
}
}
}
// Hide all the class icons
for ( int i = 0; i < m_pClassIcons.Count(); i++ )
{
m_pClassIcons[i]->SetVisible( false );
}
}
else
{
// Hide all item icons first (but not the first if we haven't scrolled)
FOR_EACH_VEC( m_pItemIcons, i )
{
m_pItemIcons[i]->SetVisible( m_iCurrentIconPosition == 0 && i == 0 );
}
// First icon is always the store entry (item/bundle), if we haven't scrolled right
if ( m_iCurrentIconPosition == 0 && m_pItemIcons.Count() )
{
m_pItemIcons[0]->SetItem( 0, &m_item );
}
// Then do the class icons
const CTFItemDefinition *pItemData = m_item.GetItemDefinition();
if ( pItemData )
{
int iButton = 0;
int iMaxButtons = m_pClassIcons.Count();
int iNumClasses = (TF_LAST_NORMAL_CLASS - TF_FIRST_NORMAL_CLASS);
// we show one less class icon when the item is visible
if ( iMaxButtons < iNumClasses && m_iCurrentIconPosition == 0 )
{
iMaxButtons -= 1;
}
for ( int iClass = TF_FIRST_NORMAL_CLASS + m_iCurrentIconPosition; iClass < TF_LAST_NORMAL_CLASS; iClass++ )
{
if ( !pItemData->CanBeUsedByClass(iClass) )
continue;
// Run out of buttons?
if ( iButton >= iMaxButtons )
{
bAdditionalIcons = true;
break;
}
m_pClassIcons[iButton]->SetVisible( true );
m_pClassIcons[iButton]->SetClass(iClass);
iButton++;
if ( !m_iCurrentClass )
{
m_iCurrentClass = iClass;
}
}
for ( ; iButton < m_pClassIcons.Count(); ++iButton )
{
m_pClassIcons[iButton]->SetVisible( false );
}
}
}
if( m_pIconsMoveLeftButton )
m_pIconsMoveLeftButton->SetVisible( (m_iCurrentIconPosition > 0) );
if( m_pIconsMoveRightButton )
m_pIconsMoveRightButton->SetVisible( bAdditionalIcons );
InvalidateLayout();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFStorePreviewItemPanelBase::OnTick( void )
{
BaseClass::OnTick();
if ( !IsVisible() )
{
vgui::ivgui()->RemoveTickSignal( GetVPanel() );
return;
}
if ( m_iCurrentRotation )
{
m_pPlayerModelPanel->RotateYaw( m_iCurrentRotation );
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFStorePreviewItemPanelBase::UpdateModelPanel()
{
if ( m_pPlayerModelPanel )
{
m_iCurrentHeldItem = 0;
m_pPlayerModelPanel->SetToPlayerClass( m_iCurrentClass, false );
m_pPlayerModelPanel->ClearCarriedItems();
if ( m_item.IsValid() )
{
CTFItemDefinition *pItemDef = m_item.GetStaticData();
if ( pItemDef->GetBundleInfo() != NULL )
{
const bundleinfo_t *pBundleInfo = pItemDef->GetBundleInfo();
FOR_EACH_VEC( pBundleInfo->vecItemDefs, i )
{
CTFItemDefinition *pBundledItem = dynamic_cast<CTFItemDefinition *>( pBundleInfo->vecItemDefs[i] );
if ( pBundledItem && pBundledItem->CanBeUsedByClass( m_iCurrentClass ) )
{
CEconItemView bundleItemData;
bundleItemData.Init( pBundledItem->GetDefinitionIndex(), AE_UNIQUE, AE_USE_SCRIPT_VALUE, true );
bundleItemData.SetClientItemFlags( kEconItemFlagClient_Preview );
int iItemIdx = m_pPlayerModelPanel->AddCarriedItem( &bundleItemData );
// try to hold it
if ( m_pPlayerModelPanel->HoldItem( iItemIdx ) )
{
m_iCurrentHeldItem = iItemIdx;
}
}
}
}
else
{
m_pPlayerModelPanel->AddCarriedItem( &m_item );
// Now make sure we're holding it if it's a non-wearable
int iLoadoutSlot = m_item.GetStaticData()->GetLoadoutSlot( m_iCurrentClass );
m_pPlayerModelPanel->HoldItemInSlot( iLoadoutSlot );
}
}
UpdatePlayerModelButtons();
// Fix a problem where changing the class would change the preview mesh but wouldn't
// update the paint. This is a hack and won't work if we have multi-class styles or
// just about anything else.
CyclePaint( false );
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFStorePreviewItemPanelBase::UpdatePlayerModelButtons()
{
UpdateOptionsButton();
UpdateNextWeaponButton();
UpdateZoomButton();
UpdateTeamButton();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFStorePreviewItemPanelBase::UpdateCustomizeMenu( void )
{
if ( m_pCustomizeMenu )
{
delete m_pCustomizeMenu;
m_pCustomizeMenu = NULL;
}
if ( !m_pPlayerModelPanel->IsVisible() )
return;
if ( !m_pOptionsButton || !m_pOptionsButton->IsVisible() )
return;
if ( !m_item.IsValid() )
return;
m_pCustomizeMenu = new Menu( this, "CustomizeMenu" );
MenuBuilder contextMenuBuilder( m_pCustomizeMenu, this );
const char *pszContextMenuBorder = "NotificationDefault";
const char *pszContextMenuFont = "HudFontMediumSecondary";
m_pCustomizeMenu->SetBorder( scheme()->GetIScheme( GetScheme() )->GetBorder( pszContextMenuBorder ) );
m_pCustomizeMenu->SetFont( scheme()->GetIScheme( GetScheme() )->GetFont( pszContextMenuFont ) );
// Add paint options sub menu
{
Menu *pPaintSubMenu = NULL;
FOR_EACH_VEC( m_vecPaintCans, i )
{
item_definition_index_t paintItemDefIndex = m_vecPaintCans[ i ];
GameItemDefinition_t * pPaintCanDef = dynamic_cast<GameItemDefinition_t*>( GEconItemSchema().GetItemDefinition( paintItemDefIndex ) );
if ( !CEconSharedToolSupport::ToolCanApplyToDefinition( dynamic_cast<const GameItemDefinition_t *>( pPaintCanDef ), m_item.GetStaticData() ) )
continue;
if ( pPaintSubMenu == NULL )
{
pPaintSubMenu = new Menu( this, "PaintSubMenu" );
pPaintSubMenu->SetBorder( scheme()->GetIScheme( GetScheme() )->GetBorder( pszContextMenuBorder ) );
pPaintSubMenu->SetFont( scheme()->GetIScheme( GetScheme() )->GetFont( pszContextMenuFont ) );
contextMenuBuilder.AddCascadingMenuItem( "#Context_Paint", pPaintSubMenu, "customization" );
}
wchar_t wBuff[256] = { 0 };
V_swprintf_safe( wBuff, L" %ls", g_pVGuiLocalize->Find( pPaintCanDef->GetItemBaseName() ) );
int nIndex = pPaintSubMenu->AddMenuItem( "", new KeyValues( "Command", "command", CFmtStr( "SetPaint%d", paintItemDefIndex ) ), this );
vgui::MenuItem *pMenuItem = pPaintSubMenu->GetMenuItem( nIndex );
pMenuItem->SetText( wBuff );
pMenuItem->InvalidateLayout( true, false );
uint32 unPaintRGB0 = 0;
uint32 unPaintRGB1 = 0;
static CSchemaAttributeDefHandle pAttrDef_PaintRGB( "set item tint RGB" );
static CSchemaAttributeDefHandle pAttrDef_PaintRGB2( "set item tint RGB 2" );
float fRGB = 0.0f;
if ( FindAttribute_UnsafeBitwiseCast<attrib_value_t>( pPaintCanDef, pAttrDef_PaintRGB, &fRGB ) && fRGB != 0.0f )
{
unPaintRGB0 = fRGB;
// We may or may not have a secondary paint color as well. If we don't, we just use the primary
// paint color to fill both slots.
if ( FindAttribute_UnsafeBitwiseCast<attrib_value_t>( pPaintCanDef, pAttrDef_PaintRGB2, &fRGB ) )
{
unPaintRGB1 = fRGB;
}
else
{
unPaintRGB1 = unPaintRGB0;
}
}
CItemMaterialCustomizationIconPanel *pCustomPanel = new CItemMaterialCustomizationIconPanel( pMenuItem, "paint" );
pCustomPanel->SetZPos( -100 );
pCustomPanel->SetTall( 30 );
pCustomPanel->SetWide( 30 );
pCustomPanel->m_colPaintColors.AddToTail( Color( clamp( (unPaintRGB0 & 0xFF0000) >> 16, 0, 255 ), clamp( (unPaintRGB0 & 0xFF00) >> 8, 0, 255 ), clamp( (unPaintRGB0 & 0xFF), 0, 255 ), 255 ) );
pCustomPanel->m_colPaintColors.AddToTail( Color( clamp( (unPaintRGB1 & 0xFF0000) >> 16, 0, 255 ), clamp( (unPaintRGB1 & 0xFF00) >> 8, 0, 255 ), clamp( (unPaintRGB1 & 0xFF), 0, 255 ), 255 ) );
}
}
// Add style
{
Menu *pStyleSubMenu = NULL;
if ( m_item.GetStaticData()->GetNumSelectableStyles() > 1 )
{
for ( style_index_t unStyle=0; unStyle<m_item.GetStaticData()->GetNumStyles(); ++unStyle )
{
const CEconStyleInfo *pStyle = m_item.GetStaticData()->GetStyleInfo( unStyle );
if ( !pStyle )
continue;
if ( !pStyle->IsSelectable() )
continue;
if ( pStyleSubMenu == NULL )
{
pStyleSubMenu = new Menu( this, "StyleSubMenu" );
pStyleSubMenu->SetBorder( scheme()->GetIScheme( GetScheme() )->GetBorder( pszContextMenuBorder ) );
pStyleSubMenu->SetFont( scheme()->GetIScheme( GetScheme() )->GetFont( pszContextMenuFont ) );
contextMenuBuilder.AddCascadingMenuItem( "#Context_Style", pStyleSubMenu, "customization" );
}
int nIndex = pStyleSubMenu->AddMenuItem( "", new KeyValues( "Command", "command", CFmtStr( "SetStyle%d", unStyle ) ), this );
vgui::MenuItem *pMenuItem = pStyleSubMenu->GetMenuItem( nIndex );
pMenuItem->SetText( pStyle->GetName() );
pMenuItem->InvalidateLayout( true, false );
}
}
}
// Add unusual
{
const CUtlVector< int > *pUnusualList = GetUnusualList();
if ( pUnusualList )
{
Menu *pUnusualSubMenu = NULL;
for ( int i=0; i<pUnusualList->Count(); ++i )
{
if ( pUnusualSubMenu == NULL )
{
pUnusualSubMenu = new Menu( this, "UnusualSubMenu" );
pUnusualSubMenu->SetBorder( scheme()->GetIScheme( GetScheme() )->GetBorder( pszContextMenuBorder ) );
pUnusualSubMenu->SetFont( scheme()->GetIScheme( GetScheme() )->GetFont( pszContextMenuFont ) );
contextMenuBuilder.AddCascadingMenuItem( "#Context_Unusual", pUnusualSubMenu, "customization" );
}
int iParticleIndex = pUnusualList->Element( i );
int nIndex = pUnusualSubMenu->AddMenuItem( "", new KeyValues( "Command", "command", CFmtStr( "SetUnusual%d", iParticleIndex ) ), this );
vgui::MenuItem *pMenuItem = pUnusualSubMenu->GetMenuItem( nIndex );
pMenuItem->SetText( GetItemSchema()->GetParticleSystemLocalizedName( iParticleIndex ) );
pMenuItem->InvalidateLayout( true, false );
}
}
}
int nX, nY;
g_pVGuiInput->GetCursorPosition( nX, nY );
m_pCustomizeMenu->SetPos( nX - 1, nY - 1 );
m_pCustomizeMenu->SetVisible(true);
m_pCustomizeMenu->AddActionSignalTarget(this);
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFStorePreviewItemPanelBase::UpdateOptionsButton( void )
{
if ( !m_pOptionsButton )
return;
m_pOptionsButton->SetVisible( false );
if ( !m_pPlayerModelPanel->IsVisible() )
return;
if ( !m_item.IsValid() )
return;
const CEconItemDefinition *pItemData = m_item.GetItemDefinition();
if ( !pItemData )
return;
bool bVisible = false;
// Is the selected item paintable
if ( pItemData->GetCapabilities() & ITEM_CAP_PAINTABLE )
{
bVisible = true;
}
// has multiple styles?
else if ( pItemData->GetNumSelectableStyles() > 1 )
{
bVisible = true;
}
// can have unusual?
else if ( GetUnusualList() != NULL )
{
bVisible = true;
}
m_pOptionsButton->SetVisible( bVisible );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFStorePreviewItemPanelBase::UpdateZoomButton( void )
{
if ( !m_pZoomButton )
return;
m_pZoomButton->SetVisible( m_pPlayerModelPanel->IsVisible() );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFStorePreviewItemPanelBase::UpdateTeamButton( void )
{
if ( !m_pTeamButton )
return;
m_pTeamButton->SetVisible( m_pPlayerModelPanel->IsVisible() );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFStorePreviewItemPanelBase::UpdateNextWeaponButton( void )
{
if ( !m_pNextWeaponButton )
return;
if ( !m_pPlayerModelPanel->IsVisible() )
{
m_pNextWeaponButton->SetVisible( false );
return;
}
bool bShowNextWeaponsButton = false;
const CUtlVector<CEconItemView*> &items = m_pPlayerModelPanel->GetCarriedItems();
int iNumItemsArray[CLASS_LOADOUT_POSITION_COUNT];
memset( iNumItemsArray, 0, sizeof( iNumItemsArray ) );
FOR_EACH_VEC( items, i )
{
CEconItemView *pItem = items[i];
int iLoadoutSlot = pItem->GetStaticData()->GetLoadoutSlot( m_iCurrentClass );
if ( iLoadoutSlot >= 0 && iLoadoutSlot < CLASS_LOADOUT_POSITION_COUNT )
{
++iNumItemsArray[iLoadoutSlot];
}
}
bShowNextWeaponsButton |= iNumItemsArray[LOADOUT_POSITION_PRIMARY] + iNumItemsArray[LOADOUT_POSITION_SECONDARY] + iNumItemsArray[LOADOUT_POSITION_MELEE] > 1;
m_pNextWeaponButton->SetVisible( bShowNextWeaponsButton );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFStorePreviewItemPanelBase::OnHideClassIconMouseover( void )
{
if ( m_pClassIconMouseoverLabel )
{
m_pClassIconMouseoverLabel->SetVisible( false );
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFStorePreviewItemPanelBase::OnShowClassIconMouseover( KeyValues *data )
{
if ( m_pClassIconMouseoverLabel )
{
const CEconItemDefinition *pItemData = m_item.GetItemDefinition();
bool bIsABundle = pItemData ? (pItemData->GetBundleInfo() != NULL) : false;
// Set the text to the correct string
int iClass = data->GetInt( "class", 0 );
if ( iClass >= TF_FIRST_NORMAL_CLASS && iClass < TF_LAST_NORMAL_CLASS )
{
wchar_t wzLocalized[256];
const char *pszLocString = bIsABundle ? "#Store_ClassImageMouseoverBundle" : "#Store_ClassImageMouseover";
g_pVGuiLocalize->ConstructString_safe( wzLocalized, g_pVGuiLocalize->Find( pszLocString ), 1, g_pVGuiLocalize->Find( g_aPlayerClassNames[iClass] ) );
m_pClassIconMouseoverLabel->SetText( wzLocalized );
}
else
{
const char *pszLocString = bIsABundle ? "#Store_ClassImageMouseoverAllBundle" : "#Store_ClassImageMouseoverAll";
m_pClassIconMouseoverLabel->SetText( pszLocString );
}
m_pClassIconMouseoverLabel->SetVisible( true );
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFStorePreviewItemPanelBase::CycleStyle( void )
{
if ( !m_pPlayerModelPanel )
return;
// Find and cycle the style based on the first item we're previewing that has
// styles. If we are previewing multiple items at the same time where more than
// one of them has styles, we'll only cycle through the names/options of the
// first one in the list.
CEconItemView *pPreviewItemView = NULL;
const CUtlVector<CEconItemView*> &vecItems = m_pPlayerModelPanel->GetCarriedItems();
FOR_EACH_VEC( vecItems, i )
{
CEconItemView *pItem = vecItems[i];
if ( pItem->GetStaticData()->GetNumStyles() && ( pItem->GetFlags() & kEconItemFlagClient_Preview ) )
{
pPreviewItemView = pItem;
break;
}
}
if ( !pPreviewItemView )
return;
// Cycle.
style_index_t unStyleCount = pPreviewItemView->GetStaticData()->GetNumStyles();
Assert( unStyleCount >= 1 );
style_index_t unStyle = pPreviewItemView->GetItemStyle();
// Default to style 0 if we're getting an invalid index
unStyle = unStyle == INVALID_STYLE_INDEX ? 0 : unStyle;
// Try to find the next selectable style
const CEconStyleInfo *pStyle = NULL;
style_index_t unStartingStyle = unStyle;
do
{
unStyle = (unStyle + 1) % unStyleCount;
pStyle = pPreviewItemView->GetStaticData()->GetStyleInfo( unStyle );
Assert( pStyle );
}
while ( unStyle != unStartingStyle && ( !pStyle || !pStyle->IsSelectable() ) );
pPreviewItemView->SetItemStyleOverride( unStyle );
SetCycleLabelText( m_pStyleNameLabel, pStyle->GetName() );
// Re-equip our held item. This causes all of our equipped items to get reloaded, and thus
// their styles updated
m_pPlayerModelPanel->SwitchHeldItemTo( m_pPlayerModelPanel->GetHeldItem() );
m_pPlayerModelPanel->UpdatePreviewVisuals();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFStorePreviewItemPanelBase::SetPaint( item_definition_index_t iItemDef )
{
if ( !m_pPlayerModelPanel )
return;
if ( !IsAnythingPaintable( m_pPlayerModelPanel->GetCarriedItems() ) )
return;
static CSchemaAttributeDefHandle pAttribDef_Paint( "set item tint RGB" );
static CSchemaAttributeDefHandle pAttribDef_Paint2( "set item tint RGB 2" );
// Find the next paint color.
const CEconItemSchema::SortedItemDefinitionMap_t &mapDefs = GetItemSchema()->GetSortedItemDefinitionMap();
m_unPaintRGB0 = 0;
m_unPaintRGB1 = 0;
if ( iItemDef != INVALID_ITEM_DEF_INDEX )
{
int iteratorName = mapDefs.FirstInorder();
while ( iteratorName != mapDefs.InvalidIndex() )
{
// Find the next sub
int iIndex = mapDefs[iteratorName]->GetDefinitionIndex();
if ( iIndex == iItemDef )
{
// Is this definition something that has paint attributes on it?
const CEconItemDefinition *pData = mapDefs[iteratorName];
float fRGB = 0.0f;
if ( FindAttribute_UnsafeBitwiseCast<attrib_value_t>( pData, pAttribDef_Paint, &fRGB ) && fRGB != 0.0f )
{
m_unPaintRGB0 = fRGB;
// We may or may not have a secondary paint color as well. If we don't, we just use the primary
// paint color to fill both slots.
if ( FindAttribute_UnsafeBitwiseCast<attrib_value_t>( pData, pAttribDef_Paint2, &fRGB ) )
{
m_unPaintRGB1 = fRGB;
}
else
{
m_unPaintRGB1 = m_unPaintRGB0;
}
m_unPaintDef = pData->GetDefinitionIndex();
SetCycleLabelText( m_pPaintNameLabel, pData->GetItemBaseName() );
}
else
{
Warning( "CTFStorePreviewItemPanelBase::SetPaint iItemDef[%d] is not paint item", iItemDef );
}
break;
}
iteratorName = mapDefs.NextInorder( iteratorName );
}
}
if ( m_unPaintRGB0 == 0 )
{
m_unPaintDef = 0;
SetCycleLabelText( m_pPaintNameLabel, "#Store_NoPaint" );
}
UpdatePaintColorsForTeam( m_pPlayerModelPanel, m_unPaintRGB0, m_unPaintRGB1 );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFStorePreviewItemPanelBase::SetStyle( style_index_t unStyle )
{
if ( !m_pPlayerModelPanel )
return;
// Find and cycle the style based on the first item we're previewing that has
// styles. If we are previewing multiple items at the same time where more than
// one of them has styles, we'll only cycle through the names/options of the
// first one in the list.
CEconItemView *pPreviewItemView = NULL;
const CUtlVector<CEconItemView*> &vecItems = m_pPlayerModelPanel->GetCarriedItems();
FOR_EACH_VEC( vecItems, i )
{
CEconItemView *pItem = vecItems[i];
if ( pItem->GetStaticData()->GetNumSelectableStyles() > 1 )
{
pPreviewItemView = pItem;
break;
}
}
if ( !pPreviewItemView )
return;
pPreviewItemView->SetItemStyleOverride( unStyle );
const CEconStyleInfo *pStyle = pPreviewItemView->GetStaticData()->GetStyleInfo( unStyle );
Assert( pStyle && pStyle->IsSelectable() );
if ( pStyle )
{
SetCycleLabelText( m_pStyleNameLabel, pStyle->GetName() );
}
// Re-equip our held item. This causes all of our equipped items to get reloaded, and thus
// their styles updated
m_pPlayerModelPanel->SwitchHeldItemTo( m_pPlayerModelPanel->GetHeldItem() );
m_pPlayerModelPanel->UpdatePreviewVisuals();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFStorePreviewItemPanelBase::SetUnusual( uint32 iUnusualIndex )
{
if ( !m_pPlayerModelPanel )
return;
static CSchemaAttributeDefHandle pAttrDef_AttachParticleEffect( "attach particle effect" );
static CSchemaAttributeDefHandle pAttrDef_OnTauntAttachParticleIndex( "on taunt attach particle index" );
const CUtlVector<CEconItemView*> &vecItems = m_pPlayerModelPanel->GetCarriedItems();
FOR_EACH_VEC( vecItems, i )
{
CEconItemView *pItem = vecItems[i];
if ( pItem->GetStaticData()->GetTauntData() )
{
const float& value_as_float = (float&)iUnusualIndex;
pItem->GetAttributeList()->SetRuntimeAttributeValue( pAttrDef_OnTauntAttachParticleIndex, value_as_float );
}
else
{
pItem->GetAttributeList()->SetRuntimeAttributeValue( pAttrDef_AttachParticleEffect, iUnusualIndex );
}
}
m_pPlayerModelPanel->InvalidateParticleEffects();
m_pPlayerModelPanel->SwitchHeldItemTo( m_pPlayerModelPanel->GetHeldItem() );
}
const CUtlVector< int > *CTFStorePreviewItemPanelBase::GetUnusualList() const
{
if ( !AllowUnusualPreview() )
return NULL;
int iLoadoutSlot = m_item.GetStaticData()->GetLoadoutSlot( m_iCurrentClass );
if ( IsValidPickupWeaponSlot( iLoadoutSlot ) )
{
return GetItemSchema()->GetWeaponUnusualParticleIndexes();
}
// is hat or whole head?
else if ( m_item.GetItemDefinition()->GetEquipRegionMask() & GetItemSchema()->GetEquipRegionBitMaskByName( "hat" ) || m_item.GetItemDefinition()->GetEquipRegionMask() & GetItemSchema()->GetEquipRegionBitMaskByName( "whole_head" ) )
{
return GetItemSchema()->GetCosmeticUnusualParticleIndexes();
}
else if ( IsTauntSlot( iLoadoutSlot ) )
{
return GetItemSchema()->GetTauntUnusualParticleIndexes();
}
return NULL;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFStorePreviewItemPanelBase::CyclePaint( bool bActuallyCycle )
{
if ( !m_pPlayerModelPanel )
return;
if ( !IsAnythingPaintable( m_pPlayerModelPanel->GetCarriedItems() ) )
return;
static CSchemaAttributeDefHandle pAttribDef_Paint( "set item tint RGB" );
static CSchemaAttributeDefHandle pAttribDef_Paint2( "set item tint RGB 2" );
// Find the next paint color.
const CEconItemSchema::SortedItemDefinitionMap_t &mapDefs = GetItemSchema()->GetSortedItemDefinitionMap();
m_unPaintRGB0 = 0;
m_unPaintRGB1 = 0;
if ( bActuallyCycle || m_unPaintDef > 0 )
{
int iteratorName = mapDefs.FirstInorder();
while ( iteratorName != mapDefs.InvalidIndex() )
{
// Find the next sub
int iIndex = mapDefs[iteratorName]->GetDefinitionIndex();
if ( bActuallyCycle ? iIndex > m_unPaintDef : iIndex >= m_unPaintDef )
{
// Is this definition something that has paint attributes on it?
const CEconItemDefinition *pData = mapDefs[iteratorName];
float fRGB = 0.0f;
if ( FindAttribute_UnsafeBitwiseCast<attrib_value_t>( pData, pAttribDef_Paint, &fRGB ) && fRGB != 0.0f )
{
if ( V_strstr( pData->GetItemBaseName(), "Halloween" ) )
{
iteratorName = mapDefs.NextInorder( iteratorName );
continue;
}
m_unPaintRGB0 = fRGB;
// We may or may not have a secondary paint color as well. If we don't, we just use the primary
// paint color to fill both slots.
if ( FindAttribute_UnsafeBitwiseCast<attrib_value_t>( pData, pAttribDef_Paint2, &fRGB ) )
{
m_unPaintRGB1 = fRGB;
}
else
{
m_unPaintRGB1 = m_unPaintRGB0;
}
m_unPaintDef = pData->GetDefinitionIndex();
SetCycleLabelText( m_pPaintNameLabel, pData->GetItemBaseName() );
break;
}
}
iteratorName = mapDefs.NextInorder( iteratorName );
}
}
if ( m_unPaintRGB0 == 0 )
{
m_unPaintDef = 0;
SetCycleLabelText( m_pPaintNameLabel, "#Store_NoPaint" );
}
UpdatePaintColorsForTeam( m_pPlayerModelPanel, m_unPaintRGB0, m_unPaintRGB1 );
}