diff options
Diffstat (limited to 'game/client/tf/vgui/store')
26 files changed, 5174 insertions, 0 deletions
diff --git a/game/client/tf/vgui/store/tf_store.cpp b/game/client/tf/vgui/store/tf_store.cpp new file mode 100644 index 0000000..d1a827b --- /dev/null +++ b/game/client/tf/vgui/store/tf_store.cpp @@ -0,0 +1,7 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +//=======================================================================================// + + +#include "cbase.h" +#include "store/tf_store.h" diff --git a/game/client/tf/vgui/store/tf_store.h b/game/client/tf/vgui/store/tf_store.h new file mode 100644 index 0000000..67c83d2 --- /dev/null +++ b/game/client/tf/vgui/store/tf_store.h @@ -0,0 +1,12 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +//=======================================================================================// + +#ifndef TF_STORE_H +#define TF_STORE_H +#ifdef _WIN32 +#pragma once +#endif + + +#endif // TF_STORE_H diff --git a/game/client/tf/vgui/store/tf_store_page_base.cpp b/game/client/tf/vgui/store/tf_store_page_base.cpp new file mode 100644 index 0000000..ee867e8 --- /dev/null +++ b/game/client/tf/vgui/store/tf_store_page_base.cpp @@ -0,0 +1,278 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + + +#include "cbase.h" +#include "store/tf_store_page_base.h" +//#include "store/v1/tf_store_preview_item.h" +#include "econ_item_inventory.h" +#include "store/store_viewcart.h" +#include "c_tf_freeaccount.h" +#include "rtime.h" +#include "econ_ui.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include <tier0/memdbgon.h> + +extern const char *g_aPlayerClassNames[TF_CLASS_MENU_BUTTONS]; + +const char *g_szClassFilterStrings[] = +{ + "", // Undefined + "#Store_Items_Scout", + "#Store_Items_Sniper", + "#Store_Items_Soldier", + "#Store_Items_Demoman", + "#Store_Items_Medic", + "#Store_Items_HWGuy", + "#Store_Items_Pyro", + "#Store_Items_Spy", + "#Store_Items_Engineer" +}; + +DECLARE_BUILD_FACTORY( CStorePreviewClassIcon ); + +ConVar tf_explanations_store( "tf_explanations_store", "0", FCVAR_ARCHIVE, "Whether the user has seen explanations for this panel." ); + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CTFStorePageBase::CTFStorePageBase(Panel *parent, const CEconStoreCategoryManager::StoreCategory_t *pPageData, const char *pPreviewItemResFile ) : CStorePage(parent, pPageData, pPreviewItemResFile) +{ + m_flStartExplanationsAt = 0; + + // TF has an option for each class, all class items, all items, and an unowned item option. Let's make sure they all fit. + if ( m_pFilterComboBox ) + { + m_pFilterComboBox->SetNumberOfEditLines( 12 ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CTFStorePageBase::OnPageShow( void ) +{ + BaseClass::OnPageShow(); + + // If this is the first time we've opened the store, start the armory explanations + if ( !tf_explanations_store.GetBool() && m_pPageData ) + { + m_flStartExplanationsAt = engine->Time() + 0.5; + vgui::ivgui()->AddTickSignal( GetVPanel() ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CTFStorePageBase::OnCommand( const char *command ) +{ + if ( !Q_stricmp( command, "show_explanations" ) ) + { + if ( !m_flStartExplanationsAt ) + { + m_flStartExplanationsAt = engine->Time(); + vgui::ivgui()->AddTickSignal( GetVPanel() ); + } + RequestFocus(); + } + else + { + BaseClass::OnCommand( command ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CTFStorePageBase::GetFiltersForDef( GameItemDefinition_t *pDef, CUtlVector<int> *pVecFilters ) +{ + pVecFilters->AddToTail( FILTER_ALL_ITEMS ); + + // Add item to unowned filter only if it doesn't belong to these categories. + const econ_store_entry_t *pEntry = EconUI()->GetStorePanel()->GetPriceSheet()->GetEntry( pDef->GetDefinitionIndex() ); + if( !pEntry->IsListedInCategory( CEconStoreCategoryManager::k_CategoryID_Tools ) && + !pEntry->IsListedInCategory( CEconStoreCategoryManager::k_CategoryID_Maps ) && + !pEntry->IsListedInCategory( CEconStoreCategoryManager::k_CategoryID_Bundles ) && + !pEntry->IsListedInCategory( CEconStoreCategoryManager::k_CategoryID_Collections ) ) + { + bool bItemOwned = false; + int iCount = InventoryManager()->GetLocalInventory()->GetItemCount(); + for ( int i = 0; i < iCount; i++ ) + { + if ( InventoryManager()->GetLocalInventory()->GetItem( i )->GetItemDefIndex() == pDef->GetDefinitionIndex() ) + { + bItemOwned = true; + break; + } + } + + if ( !bItemOwned ) + { + pVecFilters->AddToTail( FILTER_UNOWNED_ITEMS ); + } + } + + if ( pDef->CanBeUsedByAllClasses() ) + pVecFilters->AddToTail( FILTER_ALLCLASS_ITEMS ); + + for ( int iClass = TF_FIRST_NORMAL_CLASS; iClass < TF_LAST_NORMAL_CLASS; iClass++ ) + { + if ( pDef->CanBeUsedByClass( iClass ) ) + pVecFilters->AddToTail( iClass ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CTFStorePageBase::OnItemDetails( vgui::Panel *panel ) +{ + CStoreItemControlsPanel *pControlsPanel = dynamic_cast< CStoreItemControlsPanel * >( panel ); + if ( pControlsPanel ) + { + const econ_store_entry_t *pEntry = pControlsPanel->GetItem(); + if ( pEntry ) + { + SelectItemPanel( pControlsPanel->GetItemModelPanel() ); + PostMessage( EconUI()->GetStorePanel(), new KeyValues("ArmoryOpened", "itemdef", pEntry->GetItemDefinitionIndex() ) ); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CTFStorePageBase::ShowPreview( int iClass, const econ_store_entry_t* pEntry ) +{ + if ( iClass < TF_FIRST_NORMAL_CLASS || iClass >= TF_LAST_NORMAL_CLASS ) + { + iClass = TF_CLASS_SCOUT; + } + + BaseClass::ShowPreview( iClass, pEntry ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CTFStorePageBase::UpdateFilterComboBox( void ) +{ + if ( !m_pFilterComboBox ) + return; + + wchar_t wzLocalized[256]; + wchar_t wszCount[16]; + + m_pFilterComboBox->RemoveAll(); + + // All items + KeyValues *pKeyValues = new KeyValues( "data" ); + pKeyValues->SetInt( "filter", FILTER_ALL_ITEMS ); + m_pFilterComboBox->AddItem( "#Store_ClassFilter_None", pKeyValues ); + +#if NEWFILTER + // All classes + int nCount = m_pPrimaryFilter->GetCountForFilterItem( FILTER_ALLCLASS_ITEMS ); + if ( nCount ) + { + pKeyValues->SetInt( "filter", FILTER_ALLCLASS_ITEMS ); + + _snwprintf( wszCount, ARRAYSIZE( wszCount ), L"%d", nCount ); + g_pVGuiLocalize->ConstructString_safe( wzLocalized, g_pVGuiLocalize->Find( "#Store_ClassFilter_AllClasses" ), 1, wszCount ); + m_pFilterComboBox->AddItem( wzLocalized, pKeyValues ); + } + + // Individual classes + for ( int iClass = TF_FIRST_NORMAL_CLASS; iClass < TF_LAST_NORMAL_CLASS; iClass++ ) + { + nCount = m_pPrimaryFilter->GetCountForFilterItem( iClass ); + if ( !nCount ) + continue; + + pKeyValues->SetInt( "filter", iClass ); + + _snwprintf( wszCount, ARRAYSIZE( wszCount ), L"%d", nCount ); + g_pVGuiLocalize->ConstructString_safe( wzLocalized, g_pVGuiLocalize->Find( g_szClassFilterStrings[iClass] ), 1, wszCount ); + m_pFilterComboBox->AddItem( wzLocalized, pKeyValues ); + } + + // Unowned item filter + nCount = m_pPrimaryFilter->GetCountForFilterItem( FILTER_UNOWNED_ITEMS ); + if ( nCount ) + { + pKeyValues->SetInt( "filter", FILTER_UNOWNED_ITEMS ); + + _snwprintf( wszCount, ARRAYSIZE( wszCount ), L"%d", nCount ); + g_pVGuiLocalize->ConstructString_safe( wzLocalized, g_pVGuiLocalize->Find( "#Store_Items_Unowned" ), 1, wszCount ); + m_pFilterComboBox->AddItem( wzLocalized, pKeyValues ); + } + +#else + // All classes + if ( m_vecFilterCounts[FILTER_ALLCLASS_ITEMS] ) + { + pKeyValues->SetInt( "filter", FILTER_ALLCLASS_ITEMS ); + + _snwprintf( wszCount, ARRAYSIZE( wszCount ), L"%d", m_vecFilterCounts[FILTER_ALLCLASS_ITEMS] ); + g_pVGuiLocalize->ConstructString_safe( wzLocalized, g_pVGuiLocalize->Find( "#Store_ClassFilter_AllClasses" ), 1, wszCount ); + m_pFilterComboBox->AddItem( wzLocalized, pKeyValues ); + } + + // Individual classes + for ( int iClass = TF_FIRST_NORMAL_CLASS; iClass < TF_LAST_NORMAL_CLASS; iClass++ ) + { + if ( m_vecFilterCounts[iClass] == 0 ) + continue; + + pKeyValues->SetInt( "filter", iClass ); + + _snwprintf( wszCount, ARRAYSIZE( wszCount ), L"%d", m_vecFilterCounts[iClass] ); + g_pVGuiLocalize->ConstructString_safe( wzLocalized, g_pVGuiLocalize->Find( g_szClassFilterStrings[iClass] ), 1, wszCount ); + m_pFilterComboBox->AddItem( wzLocalized, pKeyValues ); + } + + // Unowned item filter + if ( m_vecFilterCounts[FILTER_UNOWNED_ITEMS] ) + { + pKeyValues->SetInt( "filter", FILTER_UNOWNED_ITEMS ); + + _snwprintf( wszCount, ARRAYSIZE( wszCount ), L"%d", m_vecFilterCounts[FILTER_UNOWNED_ITEMS] ); + g_pVGuiLocalize->ConstructString_safe( wzLocalized, g_pVGuiLocalize->Find( "#Store_Items_Unowned" ), 1, wszCount ); + m_pFilterComboBox->AddItem( wzLocalized, pKeyValues ); + } +#endif + + pKeyValues->deleteThis(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CTFStorePageBase::OnTick( void ) +{ + BaseClass::OnTick(); + + if ( m_flStartExplanationsAt && m_flStartExplanationsAt < engine->Time() ) + { + m_flStartExplanationsAt = 0; + + tf_explanations_store.SetValue( 1 ); + + CExplanationPopup *pPopup = dynamic_cast<CExplanationPopup*>( FindChildByName("StartExplanation") ); + if ( pPopup ) + { + pPopup->Popup(); + } + } + + if ( !m_flStartExplanationsAt ) + { + vgui::ivgui()->RemoveTickSignal( GetVPanel() ); + } +} + diff --git a/game/client/tf/vgui/store/tf_store_page_base.h b/game/client/tf/vgui/store/tf_store_page_base.h new file mode 100644 index 0000000..9a4bcbe --- /dev/null +++ b/game/client/tf/vgui/store/tf_store_page_base.h @@ -0,0 +1,121 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef TF_STORE_PAGE_H +#define TF_STORE_PAGE_H +#ifdef _WIN32 +#pragma once +#endif + +#include <game/client/iviewport.h> +#include "vgui_controls/PropertyPage.h" +#include <vgui_controls/Button.h> +#include <vgui_controls/ComboBox.h> +#include <vgui_controls/ImagePanel.h> +#include "econ_controls.h" +#include "econ_ui.h" +#include "econ_store.h" +#include "item_model_panel.h" +#include "store/store_page.h" +#include "tf_shareddefs.h" + +class CItemModelPanel; +class CItemModelPanelToolTip; +class CTFPlayerModelPanel; +class CStorePreviewItemPanel; +class CStoreItemControlsPanel; + +extern const char *g_pszTipsClassImages[]; + +#define FILTER_ALLCLASS_ITEMS TF_LAST_NORMAL_CLASS +#define FILTER_UNOWNED_ITEMS (TF_LAST_NORMAL_CLASS + 1) + +//----------------------------------------------------------------------------- +// Purpose: A player class preview icon in the store's item preview panel +//----------------------------------------------------------------------------- +class CStorePreviewClassIcon : public CBaseStorePreviewIcon +{ + DECLARE_CLASS_SIMPLE( CStorePreviewClassIcon, CBaseStorePreviewIcon ); +public: + CStorePreviewClassIcon( vgui::Panel *parent, const char *name ) : CBaseStorePreviewIcon(parent,name) + { + m_pImagePanel = new vgui::ImagePanel( this, "classimage" ); + m_pImagePanel->SetShouldScaleImage( true ); + m_pImagePanel->SetMouseInputEnabled( false ); + m_pImagePanel->SetKeyBoardInputEnabled( false ); + m_iClass = 0; + } + + virtual void OnCursorEntered() + { + BaseClass::OnCursorEntered(); + PostActionSignal(new KeyValues("ShowClassIconMouseover", "class", m_iClass)); + } + virtual void OnCursorExited() + { + BaseClass::OnCursorExited(); + PostActionSignal(new KeyValues("HideClassIconMouseover")); + } + virtual void OnMouseReleased(vgui::MouseCode code) + { + BaseClass::OnMouseReleased(code); + PostActionSignal(new KeyValues("ClassIconSelected", "class", m_iClass)); + } + + virtual void SetInternalImageBounds( int iX, int iY, int iWide, int iTall ) + { + m_pImagePanel->SetBounds( iX, iY, iWide, iTall ); + } + + void SetClass( int iClass ) + { + if ( iClass >= TF_FIRST_NORMAL_CLASS && iClass < TF_LAST_NORMAL_CLASS ) + { + m_pImagePanel->SetImage( g_pszTipsClassImages[iClass] ); + } + else + { + m_pImagePanel->SetImage( "class_portraits/all_class" ); + } + m_iClass = iClass; + } + + int GetClass( void ) { return m_iClass; } + +private: + vgui::ImagePanel *m_pImagePanel; + int m_iClass; +}; + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +class CTFStorePageBase : public CStorePage +{ + DECLARE_CLASS_SIMPLE( CTFStorePageBase, CStorePage ); +protected: + // CTFStorePageBase should not be instantiated directly + CTFStorePageBase( Panel *parent, const CEconStoreCategoryManager::StoreCategory_t *pPageData, const char *pPreviewItemResFile = NULL ); + +public: + + virtual void OnCommand( const char *command ); + virtual void ShowPreview( int iClass, const econ_store_entry_t* pEntry ); + + MESSAGE_FUNC( OnPageShow, "PageShow" ); + MESSAGE_FUNC_PTR( OnItemDetails, "ItemDetails", panel ); + + virtual void UpdateFilterComboBox( void ); + virtual void GetFiltersForDef( GameItemDefinition_t *pDef, CUtlVector<int> *pVecFilters ); + virtual void OnTick( void ); + virtual int GetNumPrimaryFilters( void ) { return FILTER_UNOWNED_ITEMS+1; } + +protected: + float m_flStartExplanationsAt; +}; + +#endif // TF_STORE_PAGE_H diff --git a/game/client/tf/vgui/store/tf_store_panel_base.cpp b/game/client/tf/vgui/store/tf_store_panel_base.cpp new file mode 100644 index 0000000..5a80fbb --- /dev/null +++ b/game/client/tf/vgui/store/tf_store_panel_base.cpp @@ -0,0 +1,133 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + + +#include "cbase.h" +#include "store/tf_store_panel_base.h" +#include "vgui/IInput.h" +#include "iclientmode.h" +#include "econ_item_system.h" +#include "econ_notifications.h" +#include "c_tf_freeaccount.h" +#include <vgui_controls/AnimationController.h> +#include "charinfo_armory_subpanel.h" +#include "backpack_panel.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include <tier0/memdbgon.h> + +class CServerNotConnectedToSteamDialog; +CServerNotConnectedToSteamDialog *OpenServerNotConnectedToSteamDialog( vgui::Panel *pParent ); + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CTFBaseStorePanel::CTFBaseStorePanel( Panel *parent ) : CStorePanel(parent) +{ + m_pArmoryPanel = new CArmoryPanel( this, "armory_panel" ); + m_pNotificationsPresentPanel = NULL; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CTFBaseStorePanel::ApplySchemeSettings( vgui::IScheme *pScheme ) +{ + BaseClass::ApplySchemeSettings( pScheme ); + + m_pNotificationsPresentPanel = FindChildByName( "NotificationsPresentPanel" ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CTFBaseStorePanel::OnArmoryOpened( KeyValues *data ) +{ + int iItemDef = data->GetInt( "itemdef", 0 ); + + // If it's a bundle, open the armory to a custom page showing all the items in the bundle + CEconItemDefinition *pDef = ItemSystem()->GetStaticDataForItemByDefIndex( iItemDef ); + if ( pDef ) + { + const bundleinfo_t *pBundleInfo = pDef->GetBundleInfo(); + if ( pBundleInfo ) + { + CUtlVector<item_definition_index_t> vecItems; + FOR_EACH_VEC( pBundleInfo->vecItemDefs, j ) + { + if ( pBundleInfo->vecItemDefs[j] ) + { + vecItems.AddToTail( pBundleInfo->vecItemDefs[j]->GetDefinitionIndex() ); + } + } + + m_pArmoryPanel->ShowPanel( pDef->GetItemBaseName(), &vecItems ); + m_pArmoryPanel->MoveToFront(); + return; + } + } + + m_pArmoryPanel->ShowPanel( iItemDef ); + m_pArmoryPanel->MoveToFront(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CTFBaseStorePanel::OnArmoryClosed( void ) +{ + PostMessage( m_pArmoryPanel, new KeyValues("Closing") ); + m_pArmoryPanel->SetVisible( false ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CTFBaseStorePanel::OnThink() +{ + bool bShouldBeVisible = NotificationQueue_GetNumNotifications() != 0; + if ( m_pNotificationsPresentPanel != NULL && m_pNotificationsPresentPanel->IsVisible() != bShouldBeVisible ) + { + m_pNotificationsPresentPanel->SetVisible( bShouldBeVisible ); + if ( bShouldBeVisible ) + { + g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( this, "NotificationsPresentBlink" ); + } + else + { + g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( this, "NotificationsPresentBlinkStop" ); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CTFBaseStorePanel::PostTransactionCompleted( void ) +{ + // pop this dialog up + if ( NeedsToChooseMostHelpfulFriend() ) + { + // update main menu + IGameEvent *event = gameeventmanager->CreateEvent( "store_pricesheet_updated" ); + if ( event ) + { + gameeventmanager->FireEventClientSide( event ); + } + } + + EconUI()->GetBackpackPanel()->CheckForQuickOpenKey(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CTFBaseStorePanel::SetTransactionID( uint64 inID ) +{ + BaseClass::SetTransactionID( inID ); + + EconUI()->GetBackpackPanel()->SetCurrentTransactionID( inID ); +}
\ No newline at end of file diff --git a/game/client/tf/vgui/store/tf_store_panel_base.h b/game/client/tf/vgui/store/tf_store_panel_base.h new file mode 100644 index 0000000..33a7082 --- /dev/null +++ b/game/client/tf/vgui/store/tf_store_panel_base.h @@ -0,0 +1,54 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef TF_STORE_PANEL_BASE_H +#define TF_STORE_PANEL_BASE_H +#ifdef _WIN32 +#pragma once +#endif + +#include "store/store_panel.h" + +class CArmoryPanel; +class CStorePage; + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +class CTFBaseStorePanel : public CStorePanel +{ + DECLARE_CLASS_SIMPLE( CTFBaseStorePanel, CStorePanel ); +protected: + // CTFBaseStorePanel should not be instantiated directly + CTFBaseStorePanel( Panel *parent ); + +public: + // UI Layout + virtual void ApplySchemeSettings( vgui::IScheme *pScheme ); + virtual void OnThink(); + + // GC Management + virtual void PostTransactionCompleted( void ); + + // Cart Management + CStoreCart *GetCart( void ) { return &m_Cart; } + void ShowStorePanel( void ); + void InitiateCheckout( void ); + void CheckoutCancel( void ); + + virtual void SetTransactionID( uint64 inID ) OVERRIDE; + + // Armory management + MESSAGE_FUNC_PARAMS( OnArmoryOpened, "ArmoryOpened", data ); + MESSAGE_FUNC( OnArmoryClosed, "ArmoryClosed" ); + +private: + CArmoryPanel *m_pArmoryPanel; + vgui::Panel *m_pNotificationsPresentPanel; +}; + +#endif // TF_STORE_PANEL_BASE_H diff --git a/game/client/tf/vgui/store/tf_store_preview_item_base.cpp b/game/client/tf/vgui/store/tf_store_preview_item_base.cpp new file mode 100644 index 0000000..9f6e2b3 --- /dev/null +++ b/game/client/tf/vgui/store/tf_store_preview_item_base.cpp @@ -0,0 +1,1183 @@ +//========= 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 ); +} diff --git a/game/client/tf/vgui/store/tf_store_preview_item_base.h b/game/client/tf/vgui/store/tf_store_preview_item_base.h new file mode 100644 index 0000000..cd382a0 --- /dev/null +++ b/game/client/tf/vgui/store/tf_store_preview_item_base.h @@ -0,0 +1,102 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef TF_STORE_PREVIEW_ITEM_BASE_H +#define TF_STORE_PREVIEW_ITEM_BASE_H +#ifdef _WIN32 +#pragma once +#endif + +#include <vgui_controls/Panel.h> +#include "store/store_preview_item.h" +#include "store/v1/tf_store_page.h" +#include "tf_shareddefs.h" +#include "tf_hud_mainmenuoverride.h" + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +class CTFStorePreviewItemPanelBase : public CStorePreviewItemPanel +{ + DECLARE_CLASS_SIMPLE( CTFStorePreviewItemPanelBase, CStorePreviewItemPanel ); +protected: + // CTFStorePreviewItemPanelBase should not be intantiated directly + CTFStorePreviewItemPanelBase( vgui::Panel *pParent, const char *pResFile, const char *pPanelName, CStorePage *pOwner ); + +public: + virtual void ApplySchemeSettings( vgui::IScheme *pScheme ); + virtual void OnCommand( const char *command ); + virtual void PerformLayout( void ); + virtual void OnTick( void ); + + virtual void PreviewItem( int iClass, CEconItemView *pItem, const econ_store_entry_t* pEntry=NULL ) OVERRIDE; + virtual void SetState( preview_state_t iState ); + + virtual int GetPreviewTeam() const; + + MESSAGE_FUNC_PARAMS( OnClassIconSelected, "ClassIconSelected", data ); + MESSAGE_FUNC( OnHideClassIconMouseover, "HideClassIconMouseover" ); + MESSAGE_FUNC_PARAMS( OnShowClassIconMouseover, "ShowClassIconMouseover", data ); + +protected: + void UpdateModelPanel(); + virtual void SetPlayerModelVisible( bool bVisible ); + virtual void UpdatePlayerModelButtons( void ); + virtual void UpdateCustomizeMenu( void ); + void UpdateOptionsButton( void ); + void UpdateNextWeaponButton( void ); + void UpdateZoomButton( void ); + void UpdateTeamButton( void ); + virtual void UpdateIcons( void ); + + void SetPaint( item_definition_index_t iItemDef ); + void SetStyle( style_index_t unStyle ); + void SetUnusual( uint32 iUnusualIndex ); + const CUtlVector< int > *GetUnusualList() const; + virtual bool AllowUnusualPreview() const + { +#ifdef STAGING_ONLY + // we want to be able to use this everywhere in staging for testing purpose + return true; +#else + return false; +#endif + } + + void CyclePaint( bool bActuallyCycle = true ); + void CycleStyle( void ); + void ResetHandles( void ); + + // This can be overridden to capture *any* "cycle text" that is being set, so one generic label can be used + // by a derived class if needed. Base version just sets the label's text. + virtual void SetCycleLabelText( vgui::Label *pTargetLabel, const char *pCycleText ); + + vgui::Label *m_pClassIconMouseoverLabel; + CTFPlayerModelPanel *m_pPlayerModelPanel; + CUtlVector<CStorePreviewClassIcon*> m_pClassIcons; + + int m_iCurrentClass; + int m_iCurrentHeldItem; + + item_definition_index_t m_unPaintDef; + uint32 m_unPaintRGB0; + uint32 m_unPaintRGB1; + + CExButton *m_pRotRightButton; + CExButton *m_pRotLeftButton; + CExButton *m_pNextWeaponButton; + CExButton *m_pZoomButton; + CExButton *m_pOptionsButton; + CExButton *m_pTeamButton; + vgui::Label *m_pPaintNameLabel; + vgui::Label *m_pStyleNameLabel; + + Menu *m_pCustomizeMenu; + CUtlVector< item_definition_index_t > m_vecPaintCans; +}; + +#endif // TF_STORE_PREVIEW_ITEM_H diff --git a/game/client/tf/vgui/store/v1/tf_store_page.cpp b/game/client/tf/vgui/store/v1/tf_store_page.cpp new file mode 100644 index 0000000..9d55a9c --- /dev/null +++ b/game/client/tf/vgui/store/v1/tf_store_page.cpp @@ -0,0 +1,96 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + + +#include "cbase.h" +#include "store/v1/tf_store_page.h" +#include "store/v1/tf_store_preview_item.h" +#include "c_tf_freeaccount.h" +#include "store/store_panel.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include <tier0/memdbgon.h> + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CTFStorePage1::CTFStorePage1(Panel *parent, const CEconStoreCategoryManager::StoreCategory_t *pPageData, const char *pPreviewItemResFile ) : BaseClass(parent, pPageData, pPreviewItemResFile) +{ +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +const char *CTFStorePage1::GetPageResFile( void ) +{ + Assert( !"No code should currently reference the old store!" ); + + return m_pPageData->m_pchPageRes; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CTFStorePage1::OnPageShow( void ) +{ + BaseClass::OnPageShow(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CTFStorePage1::OnCommand( const char *command ) +{ + BaseClass::OnCommand( command ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CTFStorePage1::OnItemDetails( vgui::Panel *panel ) +{ + BaseClass::OnItemDetails( panel ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CTFStorePage1::ShowPreview( int iClass, const econ_store_entry_t* pEntry ) +{ + BaseClass::ShowPreview( iClass, pEntry ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CTFStorePage1::UpdateFilterComboBox( void ) +{ + BaseClass::UpdateFilterComboBox(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CTFStorePage1::GetFiltersForDef( GameItemDefinition_t *pDef, CUtlVector<int> *pVecFilters ) +{ + return BaseClass::GetFiltersForDef( pDef, pVecFilters ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CTFStorePage1::OnTick( void ) +{ + BaseClass::OnTick(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CStorePreviewItemPanel *CTFStorePage1::CreatePreviewPanel( void ) +{ + return new CTFStorePreviewItemPanel1( this, m_pPreviewItemResFile, "storepreviewitem", this ); +} diff --git a/game/client/tf/vgui/store/v1/tf_store_page.h b/game/client/tf/vgui/store/v1/tf_store_page.h new file mode 100644 index 0000000..814bc01 --- /dev/null +++ b/game/client/tf/vgui/store/v1/tf_store_page.h @@ -0,0 +1,39 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef TF_STORE_PAGE1_H +#define TF_STORE_PAGE1_H +#ifdef _WIN32 +#pragma once +#endif + +#include "store/tf_store_page_base.h" + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +class CTFStorePage1 : public CTFStorePageBase +{ + DECLARE_CLASS_SIMPLE( CTFStorePage1, CTFStorePageBase ); +public: + CTFStorePage1( Panel *parent, const CEconStoreCategoryManager::StoreCategory_t *pPageData, const char *pPreviewItemResFile = NULL ); + + virtual const char *GetPageResFile( void ); + virtual void OnCommand( const char *command ); + virtual void ShowPreview( int iClass, const econ_store_entry_t* pEntry ); + + MESSAGE_FUNC( OnPageShow, "PageShow" ); + MESSAGE_FUNC_PTR( OnItemDetails, "ItemDetails", panel ); + + virtual void UpdateFilterComboBox( void ); + virtual void GetFiltersForDef( GameItemDefinition_t *pDef, CUtlVector<int> *pVecFilters ); + virtual void OnTick( void ); + + virtual CStorePreviewItemPanel *CreatePreviewPanel( void ); +}; + +#endif // TF_STORE_PAGE1_H diff --git a/game/client/tf/vgui/store/v1/tf_store_page_maps.cpp b/game/client/tf/vgui/store/v1/tf_store_page_maps.cpp new file mode 100644 index 0000000..4b8a895 --- /dev/null +++ b/game/client/tf/vgui/store/v1/tf_store_page_maps.cpp @@ -0,0 +1,30 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + + +#include "cbase.h" +#include "store/v1/tf_store_page_maps.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include <tier0/memdbgon.h> + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CTFStorePage_Maps::CTFStorePage_Maps( Panel *parent, const CEconStoreCategoryManager::StoreCategory_t *pPageData ) +: BaseClass( parent, pPageData, "Resource/UI/econ/store/v1/StorePreviewItemPanel_Maps.res" ) +{ +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CTFStorePage_Maps::OnPageShow() +{ + BaseClass::OnPageShow(); + + SetDetailsVisible( false ); +}
\ No newline at end of file diff --git a/game/client/tf/vgui/store/v1/tf_store_page_maps.h b/game/client/tf/vgui/store/v1/tf_store_page_maps.h new file mode 100644 index 0000000..30fc0dc --- /dev/null +++ b/game/client/tf/vgui/store/v1/tf_store_page_maps.h @@ -0,0 +1,33 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef STORE_PAGE_MAPS_H +#define STORE_PAGE_MAPS_H +#ifdef _WIN32 +#pragma once +#endif + +#include "store/v1/tf_store_page.h" + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +class CTFStorePage_Maps : public CTFStorePage1 +{ + DECLARE_CLASS_SIMPLE( CTFStorePage_Maps, CTFStorePage1 ); +public: + CTFStorePage_Maps( Panel *parent, const CEconStoreCategoryManager::StoreCategory_t *pPageData ); + virtual ~CTFStorePage_Maps() {} + + virtual const char* GetPageResFile() { return "Resource/UI/econ/store/v1/StorePage_Maps.res"; } + + virtual void OnPageShow( void ); + +protected: +}; + +#endif // STORE_PAGE_MAPS_H diff --git a/game/client/tf/vgui/store/v1/tf_store_panel.cpp b/game/client/tf/vgui/store/v1/tf_store_panel.cpp new file mode 100644 index 0000000..4daf037 --- /dev/null +++ b/game/client/tf/vgui/store/v1/tf_store_panel.cpp @@ -0,0 +1,69 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + + +#include "cbase.h" +#include "store/v1/tf_store_page.h" +#include "store/v1/tf_store_panel.h" +#include "store/store_page_halloween.h" +#include "store/store_page_new.h" +#include "store/v1/tf_store_page_maps.h" +#include "store/store_viewcart.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include <tier0/memdbgon.h> + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CTFStorePanel1::CTFStorePanel1( vgui::Panel *parent ) : CTFBaseStorePanel(parent) +{ +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CTFStorePanel1::ApplySchemeSettings( vgui::IScheme *pScheme ) +{ + BaseClass::ApplySchemeSettings( pScheme ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CTFStorePanel1::OnThink() +{ + BaseClass::OnThink(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CTFStorePanel1::PostTransactionCompleted( void ) +{ + BaseClass::PostTransactionCompleted(); +} + +//----------------------------------------------------------------------------- +// Purpose: Static store page factory. +//----------------------------------------------------------------------------- +CStorePage *CTFStorePanel1::CreateStorePage( const CEconStoreCategoryManager::StoreCategory_t *pPageData ) +{ + if ( pPageData ) + { + if ( !Q_strcmp( pPageData->m_pchPageClass, "CStorePage_SpecialPromo" ) ) + return new CTFStorePage_SpecialPromo( this, pPageData ); + + if ( !Q_strcmp( pPageData->m_pchPageClass, "CStorePage_Maps" ) ) + return new CTFStorePage_Maps( this, pPageData ); + + if ( !Q_strcmp( pPageData->m_pchPageClass, "CStorePage_Popular" ) ) + return new CTFStorePage_Popular( this, pPageData ); + } + + // Default, standard store page. + return new CTFStorePage1( this, pPageData ); +} diff --git a/game/client/tf/vgui/store/v1/tf_store_panel.h b/game/client/tf/vgui/store/v1/tf_store_panel.h new file mode 100644 index 0000000..cac6b00 --- /dev/null +++ b/game/client/tf/vgui/store/v1/tf_store_panel.h @@ -0,0 +1,38 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef TF_STORE_PANEL1_H +#define TF_STORE_PANEL1_H +#ifdef _WIN32 +#pragma once +#endif + +#include "store/tf_store_panel_base.h" + +class CStorePage; + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +class CTFStorePanel1 : public CTFBaseStorePanel +{ + DECLARE_CLASS_SIMPLE( CTFStorePanel1, CTFBaseStorePanel ); +public: + CTFStorePanel1( vgui::Panel *parent ); + + // UI Layout + virtual void ApplySchemeSettings( vgui::IScheme *pScheme ); + virtual void OnThink(); + + // GC Management + virtual void PostTransactionCompleted( void ); + +private: + virtual CStorePage *CreateStorePage( const CEconStoreCategoryManager::StoreCategory_t *pPageData ); +}; + +#endif // TF_STORE_PANEL1_H diff --git a/game/client/tf/vgui/store/v1/tf_store_preview_item.cpp b/game/client/tf/vgui/store/v1/tf_store_preview_item.cpp new file mode 100644 index 0000000..7f23513 --- /dev/null +++ b/game/client/tf/vgui/store/v1/tf_store_preview_item.cpp @@ -0,0 +1,100 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + + +#include "cbase.h" +#include "store/v1/tf_store_preview_item.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include <tier0/memdbgon.h> + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CTFStorePreviewItemPanel1::CTFStorePreviewItemPanel1( vgui::Panel *pParent, const char *pResFile, const char *pPanelName, CStorePage *pOwner ) +: BaseClass( pParent, pResFile, "storepreviewitem", pOwner ) +{ +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CTFStorePreviewItemPanel1::ApplySchemeSettings( vgui::IScheme *pScheme ) +{ + BaseClass::ApplySchemeSettings( pScheme ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CTFStorePreviewItemPanel1::PerformLayout( void ) +{ + BaseClass::PerformLayout(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CTFStorePreviewItemPanel1::OnCommand( const char *command ) +{ + BaseClass::OnCommand( command ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CTFStorePreviewItemPanel1::OnClassIconSelected( KeyValues *data ) +{ + BaseClass::OnClassIconSelected( data ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CTFStorePreviewItemPanel1::OnHideClassIconMouseover( void ) +{ + BaseClass::OnHideClassIconMouseover(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CTFStorePreviewItemPanel1::OnShowClassIconMouseover( KeyValues *data ) +{ + BaseClass::OnShowClassIconMouseover( data ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CTFStorePreviewItemPanel1::PreviewItem( int iClass, CEconItemView *pItem, const econ_store_entry_t* pEntry ) +{ + BaseClass::PreviewItem( iClass, pItem, pEntry ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CTFStorePreviewItemPanel1::SetState( preview_state_t iState ) +{ + BaseClass::SetState( iState ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CTFStorePreviewItemPanel1::UpdateIcons( void ) +{ + BaseClass::UpdateIcons(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CTFStorePreviewItemPanel1::OnTick( void ) +{ + BaseClass::OnTick(); +} diff --git a/game/client/tf/vgui/store/v1/tf_store_preview_item.h b/game/client/tf/vgui/store/v1/tf_store_preview_item.h new file mode 100644 index 0000000..02994c4 --- /dev/null +++ b/game/client/tf/vgui/store/v1/tf_store_preview_item.h @@ -0,0 +1,41 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef TF_STORE_PREVIEW_ITEM1_H +#define TF_STORE_PREVIEW_ITEM1_H +#ifdef _WIN32 +#pragma once +#endif + +#include "store/tf_store_preview_item_base.h" + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +class CTFStorePreviewItemPanel1 : public CTFStorePreviewItemPanelBase +{ + DECLARE_CLASS_SIMPLE( CTFStorePreviewItemPanel1, CTFStorePreviewItemPanelBase ); +public: + CTFStorePreviewItemPanel1( vgui::Panel *pParent, const char *pResFile, const char *pPanelName, CStorePage *pOwner ); + + virtual void ApplySchemeSettings( vgui::IScheme *pScheme ); + virtual void OnCommand( const char *command ); + virtual void PerformLayout( void ); + virtual void OnTick( void ); + + virtual void PreviewItem( int iClass, CEconItemView *pItem, const econ_store_entry_t* pEntry=NULL ) OVERRIDE; + virtual void SetState( preview_state_t iState ); + + MESSAGE_FUNC_PARAMS( OnClassIconSelected, "ClassIconSelected", data ); + MESSAGE_FUNC( OnHideClassIconMouseover, "HideClassIconMouseover" ); + MESSAGE_FUNC_PARAMS( OnShowClassIconMouseover, "ShowClassIconMouseover", data ); + +private: + virtual void UpdateIcons( void ); +}; + +#endif // TF_STORE_PREVIEW_ITEM1_H diff --git a/game/client/tf/vgui/store/v2/tf_store_mapstamps_info_dialog.cpp b/game/client/tf/vgui/store/v2/tf_store_mapstamps_info_dialog.cpp new file mode 100644 index 0000000..1ee55a8 --- /dev/null +++ b/game/client/tf/vgui/store/v2/tf_store_mapstamps_info_dialog.cpp @@ -0,0 +1,83 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + + +#include "cbase.h" +#include "store/v2/tf_store_mapstamps_info_dialog.h" +#include "tf_mouseforwardingpanel.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include <tier0/memdbgon.h> + +DECLARE_BUILD_FACTORY( CTFMapStampsInfoDialog ); + +using namespace vgui; + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CTFMapStampsInfoDialog::CTFMapStampsInfoDialog( vgui::Panel *pParent, const char *pName ) +: BaseClass( pParent, "MapStampsInfoDialog" ) +{ + m_pBgPanel = new CMouseMessageForwardingPanel( this, "BgPanel" ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CTFMapStampsInfoDialog::ApplySchemeSettings( vgui::IScheme *pScheme ) +{ + BaseClass::ApplySchemeSettings( pScheme ); + + LoadControlSettings( "Resource/UI/econ/store/v2/StoreMapStampsInfoDialog.res" ); + + m_pDlgFrame = dynamic_cast<EditablePanel *>( FindChildByName( "DialogFrame" ) ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CTFMapStampsInfoDialog::PerformLayout( void ) +{ + BaseClass::PerformLayout(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CTFMapStampsInfoDialog::OnCommand( const char *command ) +{ + if ( !V_strnicmp( command, "close", 5 ) ) + { + DoClose(); + } + else + { + BaseClass::OnCommand( command ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CTFMapStampsInfoDialog::OnMouseReleased(MouseCode code) +{ + BaseClass::OnMouseReleased( code ); + + if ( m_pDlgFrame && !m_pDlgFrame->IsCursorOver() ) + { + DoClose(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CTFMapStampsInfoDialog::DoClose() +{ + SetVisible( false ); + MarkForDeletion(); +}
\ No newline at end of file diff --git a/game/client/tf/vgui/store/v2/tf_store_mapstamps_info_dialog.h b/game/client/tf/vgui/store/v2/tf_store_mapstamps_info_dialog.h new file mode 100644 index 0000000..7486e8c --- /dev/null +++ b/game/client/tf/vgui/store/v2/tf_store_mapstamps_info_dialog.h @@ -0,0 +1,36 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef TF_MAPS_INFO_DIALOG_H +#define TF_MAPS_INFO_DIALOG_H +#ifdef _WIN32 +#pragma once +#endif + +#include "vgui_controls/EditablePanel.h" + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +class CTFMapStampsInfoDialog : public vgui::EditablePanel +{ + DECLARE_CLASS_SIMPLE( CTFMapStampsInfoDialog, vgui::EditablePanel ); +public: + CTFMapStampsInfoDialog( vgui::Panel *pParent, const char *pName = "" ); + + void DoClose(); + + virtual void ApplySchemeSettings( vgui::IScheme *pScheme ); + virtual void OnCommand( const char *command ); + virtual void PerformLayout( void ); + virtual void OnMouseReleased(vgui::MouseCode code); + + Panel *m_pBgPanel; + EditablePanel *m_pDlgFrame; +}; + +#endif // TF_MAPS_INFO_DIALOG_H diff --git a/game/client/tf/vgui/store/v2/tf_store_page2.cpp b/game/client/tf/vgui/store/v2/tf_store_page2.cpp new file mode 100644 index 0000000..c7ac2f2 --- /dev/null +++ b/game/client/tf/vgui/store/v2/tf_store_page2.cpp @@ -0,0 +1,849 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + + +#include "cbase.h" +#include "store/v2/tf_store_page2.h" +#include "store/v2/tf_store_preview_item2.h" +#include "c_tf_freeaccount.h" +#include "store/store_panel.h" +#include "store/tf_store.h" +#include "navigationpanel.h" +#include "econ/store/store_page_new.h" +#include "econ_item_system.h" +#include "c_tf_gamestats.h" +#include "vgui_controls/TextImage.h" +#include "econ_item_description.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include <tier0/memdbgon.h> + +static const int kNumSortTypes = 5; +ItemSortTypeData_t g_StoreSortTypes[ kNumSortTypes ] = +{ + { "#Store_SortType_DateNewest", kEconStoreSortType_DateNewest }, + { "#Store_SortType_DateOldest", kEconStoreSortType_DateOldest }, + { "#Store_SortType_HighestPrice", kEconStoreSortType_Price_HighestToLowest }, + { "#Store_SortType_LowestPrice", kEconStoreSortType_Price_LowestToHighest }, + { "#Store_SortType_Alphabetical", kEconStoreSortType_Name_AToZ }, +}; + +class CClassFilterTooltip : public vgui::BaseTooltip +{ +public: + CClassFilterTooltip( CTFStorePage2 *pStorePage ) + : BaseTooltip( pStorePage ) + , m_pStorePage( pStorePage ) + { + } + + CTFStorePage2 *m_pStorePage; + + virtual void SetText(const char *text) + { + m_pStorePage->m_pClassFilterTooltipLabel->SetText( text ); + } + + virtual void ShowTooltip(Panel *currentPanel) + { + int x = 0; + int y = currentPanel->GetTall(); + currentPanel->LocalToScreen( x, y ); + m_pStorePage->ScreenToLocal( x, y ); + + // The tooltip wants to be centered around the given panel, but it's constrained by the left and right boundaries + // of the navigation panel. + if ( m_pStorePage->m_pClassFilterButtons ) + { + // Right side of tooltip should not pass right boundary of nav panel + int aClassFilterNavPos[2] = { 0, 0 }; + m_pStorePage->m_pClassFilterButtons->GetPos( aClassFilterNavPos[0], aClassFilterNavPos[1] ); + const int nTipWide = m_pStorePage->m_pClassFilterTooltipLabel->GetWide(); + x = clamp( + x + ( currentPanel->GetWide() - nTipWide ) / 2, + aClassFilterNavPos[0], + aClassFilterNavPos[0] + m_pStorePage->m_pClassFilterButtons->GetWide() - nTipWide ); + } + + m_pStorePage->m_pClassFilterTooltipLabel->SetPos( x, y ); + m_pStorePage->m_pClassFilterTooltipLabel->SetVisible( true ); + } + virtual void HideTooltip() + { + m_pStorePage->m_pClassFilterTooltipLabel->SetVisible( false ); + } +}; + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CTFStorePage2::CTFStorePage2(Panel *parent, const CEconStoreCategoryManager::StoreCategory_t *pPageData, const char *pPreviewItemResFile ) +: BaseClass( parent, pPageData, pPreviewItemResFile ), + m_pSubcategoriesFilterCombo( NULL ), + m_pSortByCombo( NULL ), + m_pHomeCategoryTabs( NULL ), + m_pClassFilterButtons( NULL ), + m_pNameFilterTextEntry( NULL ), + m_pSubcategoriesFilterLabel( NULL ), + m_iCurrentSubcategory( 2 ), // Switch to Featured items tab on home page when store launches; BRETT SAID I COULD DO THIS + m_pClassFilterTooltipLabel( NULL ), + m_pClassFilterTooltip( NULL ), + m_flFilterItemTime( 0.0f ) +{ + const CEconStorePriceSheet *pPriceSheet = EconUI()->GetStorePanel()->GetPriceSheet(); + + if ( IsHomePage() ) + { + bool bAtLeastOneItemIsOnSale = false; + if ( pPriceSheet ) + { + const CEconStorePriceSheet::StoreEntryMap_t& mapStoreEntries = pPriceSheet->GetEntries(); + FOR_EACH_MAP_FAST( mapStoreEntries, i ) + { + if ( mapStoreEntries[i].IsOnSale( EconUI()->GetStorePanel()->GetCurrency() ) ) + { + bAtLeastOneItemIsOnSale = true; + break; + } + } + } + + m_pHomeCategoryTabs = new CNavigationPanel( this, "ItemCategoryTabs" ); + + FOR_EACH_VEC( m_pPageData->m_vecSubcategories, i ) + { + // Skip over adding the "On Sale!" tab if no items are currently on sale. + const char *pchName = m_pPageData->m_vecSubcategories[i]->m_pchName; + bool bIsOnSaleCategory = m_pPageData->m_vecSubcategories[i]->m_unID == CEconStoreCategoryManager::k_CategoryID_OnSale; + + // Skip over the "Top Sellers" tab as we're replacing it with the 'Starter Packs' tab for now + bool bIsPopularCategory = m_pPageData->m_vecSubcategories[i]->m_unID == CEconStoreCategoryManager::k_CategoryID_Popular; + + // Skip over the "New" tab as we're replacing it with the 'Featured' tab for now + bool bIsNew = m_pPageData->m_vecSubcategories[i]->m_unID == CEconStoreCategoryManager::k_CategoryID_New; + + // We include all of the sale items in the Featured tab currently, so we don't need to add these categories back in at the moment + bAtLeastOneItemIsOnSale = false; + + if ( ( !bIsPopularCategory && !bIsOnSaleCategory && !bIsNew ) || ( bAtLeastOneItemIsOnSale && bIsOnSaleCategory ) ) + { + m_pHomeCategoryTabs->AddButton( i, pchName ); + } + } + } +} + +//----------------------------------------------------------------------------- +CTFStorePage2::~CTFStorePage2() +{ + delete m_pClassFilterTooltip; + m_pClassFilterTooltip = NULL; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CTFStorePage2::OnPostCreate() +{ + BaseClass::OnPostCreate(); + + m_bShouldDeletePreviewPanel = true; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CTFStorePage2::HasSubcategories() const +{ + return m_pPageData && m_pPageData->HasSubcategories(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CTFStorePage2::PerformLayout() +{ + BaseClass::PerformLayout(); + + if ( m_pSubcategoriesFilterCombo ) + { + m_pSubcategoriesFilterCombo->SetVisible( HasSubcategories() ); + } + + if ( m_pSubcategoriesFilterLabel ) + { + m_pSubcategoriesFilterLabel->SetVisible( HasSubcategories() ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CTFStorePage2::ApplySchemeSettings( IScheme *pScheme ) +{ + BaseClass::ApplySchemeSettings( pScheme ); + + m_pNameFilterTextEntry = FindControl<vgui::TextEntry>( "NameFilterTextEntry" ); + if ( m_pNameFilterTextEntry ) + { + m_pNameFilterTextEntry->AddActionSignalTarget( this ); + } + + m_pSortByCombo = dynamic_cast< ComboBox * >( FindChildByName( "SortFilterComboBox" ) ); + if ( m_pSortByCombo ) + { + m_pSortByCombo->RemoveAll(); + vgui::HFont hFont = pScheme->GetFont( "HudFontSmallestBold", true ); + m_pSortByCombo->SetFont( hFont ); + KeyValues *pKeyValues = new KeyValues( "data" ); + for ( int i = 0; i < ARRAYSIZE(g_StoreSortTypes); i++ ) + { + pKeyValues->SetInt( "sortby", i ); + m_pSortByCombo->AddItem( g_StoreSortTypes[i].szSortDesc, pKeyValues ); + } + pKeyValues->deleteThis(); + m_pSortByCombo->ActivateItemByRow( 0 ); + } + + m_pSubcategoriesFilterCombo = dynamic_cast< ComboBox * >( FindChildByName( "SubcategoryFilterComboBox" ) ); + if ( m_pSubcategoriesFilterCombo ) + { + vgui::HFont hFont = pScheme->GetFont( "HudFontSmallestBold", true ); + m_pSubcategoriesFilterCombo->SetFont( hFont ); + + m_pSubcategoriesFilterCombo->RemoveAll(); + + if ( m_pPageData ) + { + // Add "all items" explicitly + KeyValuesAD kvAllItems( "data" ); + kvAllItems->SetInt( "index", GetNumSubcategories() ); + m_pSubcategoriesFilterCombo->AddItem( "#Store_ClassFilter_None", kvAllItems ); + + FOR_EACH_VEC( m_pPageData->m_vecSubcategories, i ) + { + KeyValues *pData = new KeyValues( "data" ); + pData->SetInt( "index", i ); + + m_pSubcategoriesFilterCombo->AddItem( m_pPageData->m_vecSubcategories[i]->m_pchName, pData ); + + pData->deleteThis(); + } + } + + // Move to "All items" selected + m_pSubcategoriesFilterCombo->ActivateItemByRow( 0 ); + + m_pSubcategoriesFilterCombo->GetComboButton()->SetFgColor( Color( 117,107,94,255 ) ); + m_pSubcategoriesFilterCombo->GetComboButton()->SetDefaultColor( Color( 117,107,94,255), Color( 0,0,0,0) ); + m_pSubcategoriesFilterCombo->GetComboButton()->SetArmedColor( Color( 117,107,94,255), Color( 0,0,0,0) ); + m_pSubcategoriesFilterCombo->GetComboButton()->SetDepressedColor( Color( 117,107,94,255), Color( 0,0,0,0) ); + } + + m_pClassFilterButtons = dynamic_cast< CNavigationPanel * >( FindChildByName( "ClassFilterNavPanel" ) ); + m_pSubcategoriesFilterLabel = dynamic_cast< CExLabel * >( FindChildByName( "SubcategoryFiltersLabel" ) ); + + m_pClassFilterTooltipLabel = dynamic_cast< CExLabel * >( FindChildByName( "ClassFilterTooltipLabel" ) ); + if ( m_pClassFilterTooltipLabel && m_pClassFilterButtons ) + { + m_pClassFilterTooltip = new CClassFilterTooltip(this); + for ( int i = 0 ; i < m_pClassFilterButtons->NumButtons() ; ++i ) + { + CExButton *pButton = m_pClassFilterButtons->GetButton( i ); + CUtlString sSaveText = pButton->GetEffectiveTooltipText(); + pButton->SetTooltip( m_pClassFilterTooltip, sSaveText ); + } + } + + // Setup title text in home page + if ( IsHomePage() && g_pVGuiLocalize ) + { + CExLabel *pTitleLabel = dynamic_cast<CExLabel *>( FindChildByName( "TitleLabel" ) ); + wchar_t *pHomePageTitle = g_pVGuiLocalize->Find( "#Store_HomePageTitle" ); + wchar_t *pRedText = g_pVGuiLocalize->Find( "#Store_HomePageTitleRedText" ); + + if ( pTitleLabel && pHomePageTitle && pRedText ) + { + const store_promotion_spend_for_free_item_t *pPromotion = EconUI()->GetStorePanel()->GetPriceSheet()->GetStorePromotion_SpendForFreeItem(); + + ECurrency eCurrency = EconUI()->GetStorePanel()->GetCurrency(); + AssertMsg( eCurrency >= k_ECurrencyUSD && eCurrency < k_ECurrencyMax, "Invalid currency!" ); + int iPriceThreshold = pPromotion->m_rgusPriceThreshold[ eCurrency ]; + wchar_t wszPriceThreshold[ kLocalizedPriceSizeInChararacters ]; + MakeMoneyString( wszPriceThreshold, ARRAYSIZE( wszPriceThreshold ), iPriceThreshold, EconUI()->GetStorePanel()->GetCurrency() ); + + static wchar_t wszText[512]; + g_pVGuiLocalize->ConstructString_safe( wszText, pHomePageTitle, 2, pRedText, wszPriceThreshold ); + + pTitleLabel->SetText( wszText ); + TextImage *pTextImage = pTitleLabel->GetTextImage(); + const wchar_t *pFound = wcsstr( wszText, pRedText ); + if ( pTextImage && pFound ) + { + const int iRedTextPos = pFound - wszText; + const int nRedTextLen = wcslen( pRedText ); + pTextImage->ClearColorChangeStream(); + pTextImage->AddColorChange( Color(200,80,60,255), iRedTextPos ); + pTextImage->AddColorChange( pTitleLabel->GetFgColor(), iRedTextPos + nRedTextLen ); + } + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +const char *CTFStorePage2::GetPageResFile( void ) +{ + if ( IsHomePage() ) + { + Assert( ShouldUseNewStore() ); + + return "Resource/UI/econ/store/v2/StoreHome_Premium.res"; + } + + return m_pPageData->m_pchPageRes; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CTFStorePage2::OnPageShow( void ) +{ + BaseClass::OnPageShow(); + + if ( m_pClassFilterButtons ) + { + m_pClassFilterButtons->UpdateButtonSelectionStates( 0 ); + } + + ClearNameFilter( true ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CTFStorePage2::OnCommand( const char *command ) +{ + BaseClass::OnCommand( command ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CTFStorePage2::OnItemDetails( vgui::Panel *panel ) +{ + CStoreItemControlsPanel *pControlsPanel = dynamic_cast< CStoreItemControlsPanel * >( panel ); + if ( pControlsPanel ) + { + const econ_store_entry_t *pEntry = pControlsPanel->GetItem(); + if ( pEntry && m_pPreviewPanel ) + { + ShowPreviewWindow( pEntry->GetItemDefinitionIndex() ); + } + } +} + +//----------------------------------------------------------------------------- +void CTFStorePage2::OnItemDefDetails( KeyValues *pData ) +{ + ShowPreviewWindow( pData->GetInt("ItemDefIndex", -1) ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CTFStorePage2::ShowPreviewWindow( item_definition_index_t usDefIndex ) +{ + if ( m_pPreviewPanel ) + { + CEconItemView itemData; + itemData.Init( usDefIndex, AE_UNIQUE, AE_USE_SCRIPT_VALUE, true ); + itemData.SetClientItemFlags( kEconItemFlagClient_Preview ); + + m_pPreviewPanel->PreviewItem( 0, &itemData ); + m_pPreviewPanel->SetState( PS_ITEM ); // Adding this, since without it, only the item icon shows up + m_pPreviewPanel->SetVisible( true ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CTFStorePage2::OnNavButtonSelected( KeyValues *pData ) +{ + Panel *pPanel = (Panel *)pData->GetPtr( "panel" ); + + if ( pPanel == m_pClassFilterButtons ) + { + const int iFilter = pData->GetInt( "userdata", -1 ); AssertMsg( iFilter >= 0, "Bad filter" ); + if ( iFilter < 0 ) + return; + + SetFilter( iFilter ); + m_iCurrentPage = 0; + UpdateModelPanels(); + + if ( m_pCheckoutButton ) + { + m_pCheckoutButton->RequestFocus(); + } + + C_CTFGameStats::ImmediateWriteInterfaceEvent( "store_page_2_nav(class)", CFmtStr( "%i", iFilter ).Access() ); + } + else if ( pPanel == m_pHomeCategoryTabs ) + { + int iSelectedTab = pData->GetInt( "userdata", -1 ); + if ( iSelectedTab < 0 ) + return; + + if ( !m_pPageData || !m_pPageData->m_vecSubcategories.IsValidIndex( iSelectedTab ) ) + return; + + m_iCurrentSubcategory = iSelectedTab; + + FOR_EACH_VEC( m_vecItemPanels, i ) + { + // Delete the old one + m_vecItemPanels[i].m_pStorePricePanel->MarkForDeletion(); + + // Create a new one and cache it + CStorePricePanel *pPricePanel = CreatePricePanel( i ); + pPricePanel->InvalidateLayout(); + pPricePanel->SetMouseInputEnabled( false ); + pPricePanel->SetKeyBoardInputEnabled( false ); + + m_vecItemPanels[i].m_pStorePricePanel = pPricePanel; + + // Setup the mouse handler + m_vecItemPanels[i].m_pItemControlsPanel->SetMouseHoverHandler( pPricePanel ); + } + + m_iCurrentPage = 0; + + UpdateFilteredItems(); + UpdateModelPanels(); + + C_CTFGameStats::ImmediateWriteInterfaceEvent( "store_page_2_nav(category)", CFmtStr( "%i", iSelectedTab ).Access() ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Called when text changes in combo box +//----------------------------------------------------------------------------- +void CTFStorePage2::OnTextChanged( KeyValues *data ) +{ + Panel *pPanel = reinterpret_cast<vgui::Panel *>( data->GetPtr("panel") ); + + vgui::TextEntry *pTextEntry = dynamic_cast<vgui::TextEntry *>( pPanel ); + + if ( pTextEntry ) + { + if ( pTextEntry == m_pNameFilterTextEntry ) + { + m_wNameFilter.RemoveAll(); + if ( m_pNameFilterTextEntry->GetTextLength() ) + { + m_wNameFilter.EnsureCount( m_pNameFilterTextEntry->GetTextLength() + 1 ); + m_pNameFilterTextEntry->GetText( m_wNameFilter.Base(), m_wNameFilter.Count() * sizeof(wchar_t) ); + V_wcslower( m_wNameFilter.Base() ); + } + m_flFilterItemTime = gpGlobals->curtime + 0.5f; + return; + } + } + + vgui::ComboBox *pComboBox = dynamic_cast<vgui::ComboBox *>( pPanel ); + + if ( pComboBox ) + { + if ( pComboBox == m_pSubcategoriesFilterCombo ) + { + // the class selection combo box changed, update class details + KeyValues *pUserData = m_pSubcategoriesFilterCombo->GetActiveItemUserData(); + if ( !pUserData ) + return; + + // Update current subcategory filter + m_iCurrentSubcategory = pUserData->GetInt( "index", 0 ); + m_bFilterDirty = true; + + m_iCurrentPage = 0; + UpdateModelPanels(); + + if ( m_pCheckoutButton ) + { + m_pCheckoutButton->RequestFocus(); + } + + C_CTFGameStats::ImmediateWriteInterfaceEvent( "store_page_2_nav(subcategories)", CFmtStr( "%i", m_iCurrentSubcategory ).Access() ); + } + + else if ( pComboBox == m_pSortByCombo ) + { + // the class selection combo box changed, update class details + KeyValues *pUserData = m_pSortByCombo->GetActiveItemUserData(); + if ( !pUserData ) + return; + + int iSortTypeSelectionIndex = pUserData->GetInt( "sortby", -1 ); + m_bFilterDirty = true; + if ( iSortTypeSelectionIndex >= 0 ) + { + eEconStoreSortType iSortType = (eEconStoreSortType)g_StoreSortTypes[iSortTypeSelectionIndex].iSortType; + + UpdateFilteredItems(); + UpdateModelPanels(); + + C_CTFGameStats::ImmediateWriteInterfaceEvent( "store_page_2_nav(sort_by)", CFmtStr( "%i", iSortType ).Access() ); + } + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CTFStorePage2::GetFiltersForDef( GameItemDefinition_t *pDef, CUtlVector<int> *pVecFilters ) +{ + BaseClass::GetFiltersForDef( pDef, pVecFilters ); +} + +bool CTFStorePage2::FindAndSelectEntry( const econ_store_entry_t *pEntry ) +{ + m_iCurrentSubcategory = GetAllSubcategoriesIndex(); + m_bFilterDirty = true; + if ( BaseClass::FindAndSelectEntry( pEntry ) ) + { + ivgui()->PostMessage( GetVPanel(), new KeyValues( "ItemDefDetails", "ItemDefIndex", pEntry->GetItemDefinitionIndex() ), GetVPanel() ); + return true; + } + return false; +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CTFStorePage2::ClearNameFilter( bool bUpdateModelPanels ) +{ + // don't do anything if we don't have any filter + if ( m_wNameFilter.Count() == 0 ) + return; + + m_wNameFilter.RemoveAll(); + if( m_pNameFilterTextEntry ) + { + m_pNameFilterTextEntry->SetText( "" ); + } + + if ( bUpdateModelPanels ) + { + m_flFilterItemTime = gpGlobals->curtime + 0.1f; + } +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +int CTFStorePage2::GetAllSubcategoriesIndex() const +{ + return m_pPageData ? m_pPageData->GetNumSubcategories() : 0; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CTFStorePage2::UpdateFilteredItems() +{ + if ( m_bFilterDirty ) + { + // Make sure list of unfiltered items is sorted + m_pSortByCombo = dynamic_cast< ComboBox * >( FindChildByName( "SortFilterComboBox" ) ); + if ( m_pSortByCombo ) + { + KeyValues *pUserData = m_pSortByCombo->GetActiveItemUserData(); + if ( pUserData ) + { + int iSortTypeSelectionIndex = pUserData->GetInt( "sortby", -1 ); + if ( iSortTypeSelectionIndex >= 0 ) + { + eEconStoreSortType iSortType = (eEconStoreSortType)g_StoreSortTypes[iSortTypeSelectionIndex].iSortType; + CEconStorePriceSheet *pPriceSheet = EconUI()->GetStorePanel()->GetPriceSheetForEdit(); + pPriceSheet->SetEconStoreSortType( iSortType ); + + CEconStoreCategoryManager::StoreCategory_t *pPageData = const_cast< CEconStoreCategoryManager::StoreCategory_t * >( m_pPageData ); + pPageData->m_vecEntries.SetLessContext( pPriceSheet ); + pPageData->m_vecEntries.RedoSort( true ); + } + } + } + } + + if ( !IsHomePage() ) + { + BaseClass::UpdateFilteredItems(); + return; + } + + m_FilteredEntries.Purge(); + + // Subcategories on the home page are special cases, as they aren't based on + // an item's tags. + const StoreCategoryID_t unSubcategoryID = m_pPageData->m_vecSubcategories[ m_iCurrentSubcategory ]->m_unID; + + CStorePanel *pStorePanel = EconUI()->GetStorePanel(); + if ( !pStorePanel ) + return; + + FOR_EACH_VEC( m_vecItemPanels, idx ) + { + m_vecItemPanels[idx].m_pItemModelPanel->SetShowQuantity( unSubcategoryID != CEconStoreCategoryManager::k_CategoryID_Popular ); + } + + if ( unSubcategoryID == CEconStoreCategoryManager::k_CategoryID_Popular ) + { + const CUtlVector<uint32>& popularItems = pStorePanel->GetPopularItems(); + for ( int i = 0; i < MIN( m_vecItemPanels.Count(), popularItems.Count() ); ++i ) + { + const econ_store_entry_t *pEntry = pStorePanel->GetPriceSheet()->GetEntry( popularItems[i] ); + m_FilteredEntries.AddToTail( pEntry ); + } + } + else if ( unSubcategoryID == CEconStoreCategoryManager::k_CategoryID_New ) + { + // Add all new items + const CUtlMap< uint16, econ_store_entry_t > &mapEntries = pStorePanel->GetPriceSheet()->GetEntries(); + FOR_EACH_MAP_FAST( mapEntries, i ) + { + const econ_store_entry_t *pCurEntry = &mapEntries[i]; + if ( pCurEntry->m_bNew ) + { + m_FilteredEntries.AddToTail( pCurEntry ); + } + } + } + else if ( unSubcategoryID == CEconStoreCategoryManager::k_CategoryID_OnSale ) + { + ECurrency eCurrency = EconUI()->GetStorePanel()->GetCurrency(); + + // Add all entries that are on sale + const CEconStorePriceSheet::StoreEntryMap_t &mapEntries = pStorePanel->GetPriceSheet()->GetEntries(); + FOR_EACH_MAP_FAST( mapEntries, i ) + { + const econ_store_entry_t *pCurEntry = &mapEntries[i]; + + if ( pCurEntry->IsOnSale( eCurrency ) ) + { + m_FilteredEntries.AddToTail( pCurEntry ); + } + } + } + else if ( unSubcategoryID == CEconStoreCategoryManager::k_CategoryID_Featured ) + { + const CEconStorePriceSheet::FeaturedItems_t& vecFeaturedItems = pStorePanel->GetPriceSheet()->GetFeaturedItems(); + FOR_EACH_VEC( vecFeaturedItems, i ) + { + const econ_store_entry_t *pEntry = pStorePanel->GetPriceSheet()->GetEntry( vecFeaturedItems[i] ); + if ( pEntry ) + { + m_FilteredEntries.AddToTail( pEntry ); + } + else + { + AssertMsg( 0, "trying to add featured item that's not in the store price sheet.\n" ); + } + } + } + else if ( unSubcategoryID == CEconStoreCategoryManager::k_CategoryID_ClassBundles ) + { + // Let's find the class bundles + const CEconStorePriceSheet::StoreEntryMap_t &mapEntries = pStorePanel->GetPriceSheet()->GetEntries(); + FOR_EACH_MAP_FAST( mapEntries, i ) + { + const econ_store_entry_t *pCurEntry = &mapEntries[i]; + + if ( pCurEntry->IsListedInCategory( CEconStoreCategoryManager::k_CategoryID_ClassBundles ) ) + { + m_FilteredEntries.AddToTail( pCurEntry ); + } + } + + // Sort by date to get the weapon bundles before the keyless crates + //extern int ItemNameSortComparator( const econ_store_entry_t *const *ppEntryA, const econ_store_entry_t *const *ppEntryB ); + extern int FirstSaleDateSortComparator( const econ_store_entry_t *const *ppItemA, const econ_store_entry_t *const *ppItemB ); + + m_FilteredEntries.Sort( &FirstSaleDateSortComparator ); + + } + else if ( unSubcategoryID == CEconStoreCategoryManager::k_CategoryID_Taunts ) + { + const CEconStorePriceSheet::StoreEntryMap_t &mapEntries = pStorePanel->GetPriceSheet()->GetEntries(); + FOR_EACH_MAP_FAST( mapEntries, i ) + { + const econ_store_entry_t *pCurEntry = &mapEntries[i]; + + if ( pCurEntry->IsListedInCategory( CEconStoreCategoryManager::k_CategoryID_Taunts ) ) + { + m_FilteredEntries.AddToTail( pCurEntry ); + } + } + } + else + { + AssertMsg( 0, "Subcategory has no defined behavior in code" ); + } + + // If we're either "New" category or the "On Sale" category or the "Taunts" category, sort our contents + // by sale date. + if ( unSubcategoryID == CEconStoreCategoryManager::k_CategoryID_New || unSubcategoryID == CEconStoreCategoryManager::k_CategoryID_OnSale || unSubcategoryID == CEconStoreCategoryManager::k_CategoryID_Taunts ) + { + extern int FirstSaleDateSortComparator( const econ_store_entry_t *const *ppItemA, const econ_store_entry_t *const *ppItemB ); + + m_FilteredEntries.Sort( &FirstSaleDateSortComparator ); + } + + m_bFilterDirty = false; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CTFStorePage2::DoesEntryFilterPassSecondaryFilter( const econ_store_entry_t *pEntry ) +{ + if ( !DoesEntryFilterPassSubcategoryFilter( pEntry ) ) + { + return false; + } + + if ( m_wNameFilter.Count() > 0 ) + { + CEconItemView itemData; + itemData.Init( pEntry->GetItemDefinitionIndex(), AE_UNIQUE, AE_USE_SCRIPT_VALUE, true ); + itemData.SetClientItemFlags( kEconItemFlagClient_Preview | kEconItemFlagClient_StoreItem ); + return DoesItemPassSearchFilter( itemData.GetDescription(), m_wNameFilter.Base() ); + } + + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CTFStorePage2::DoesEntryFilterPassSubcategoryFilter( const econ_store_entry_t *pEntry ) +{ + Assert( pEntry ); + Assert( m_pPageData ); + + // Make sure pages without subcategories can still function + if ( !HasSubcategories() ) + return true; + + // "All subcategories" item selected? + if ( m_iCurrentSubcategory == GetAllSubcategoriesIndex() ) + return true; + + if ( !m_pPageData->m_vecSubcategories.IsValidIndex( m_iCurrentSubcategory ) ) + return false; + + // Get the subcategory ID + const StoreCategoryID_t unSubCategoryID = m_pPageData->m_vecSubcategories[ m_iCurrentSubcategory ]->m_unID; + + // If the store entry is covered by the currently selected category, return true. +// return pEntry->m_vecTagIds.Find( unSubCategoryID ) != pEntry->m_vecTagIds.InvalidIndex(); + return pEntry->IsListedInCategory( unSubCategoryID ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CTFStorePage2::UpdateFilterComboBox( void ) +{ + BaseClass::UpdateFilterComboBox(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CTFStorePage2::OnThink( void ) +{ + BaseClass::OnThink(); + + if ( m_flFilterItemTime && gpGlobals->curtime >= m_flFilterItemTime ) + { + m_bFilterDirty = true; + UpdateFilteredItems(); + UpdateModelPanels(); + m_flFilterItemTime = 0.0f; + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CStorePreviewItemPanel *CTFStorePage2::CreatePreviewPanel( void ) +{ + return new CTFStorePreviewItemPanel2( EconUI()->GetStorePanel(), m_pPreviewItemResFile, "storepreviewitem", this ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CStorePricePanel* CTFStorePage2::CreatePricePanel( int iIndex ) +{ + if ( m_pPageData && + m_pPageData->m_bIsHome && + HasSubcategories() && + m_pPageData->m_vecSubcategories.IsValidIndex( m_iCurrentSubcategory ) && + m_pPageData->m_vecSubcategories[ m_iCurrentSubcategory ]->m_unID == CEconStoreCategoryManager::k_CategoryID_Popular ) + { + return vgui::SETUP_PANEL( new CStorePricePanel_Popular( this, "StorePrice", iIndex + 1 ) ); + } + + return BaseClass::CreatePricePanel( iIndex ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CTFStorePage2::OnAddItemToCart( KeyValues *pData ) +{ + item_definition_index_t iItemDef = (item_definition_index_t)pData->GetInt( "item_def", INVALID_ITEM_DEF_INDEX ); + + AddItemToCartHelper( GetPageName(), iItemDef, (ECartItemType)pData->GetInt( "cart_add_type", kCartItem_Purchase ) ); + UpdateCart(); + + // Turn the free slots indicator red if we can't fit everything. + UpdateBackpackLabel(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CTFStorePage2::OnItemPanelMouseReleased( vgui::Panel *panel ) +{ + CItemModelPanel *pItemPanel = dynamic_cast < CItemModelPanel * > ( panel ); + if ( pItemPanel && IsVisible() && pItemPanel->HasItem() ) + { + FOR_EACH_VEC( m_vecItemPanels, i ) + { + if ( m_vecItemPanels[i].m_pItemModelPanel == pItemPanel ) + { + ShowPreviewWindow( m_vecItemPanels[i].m_pItemModelPanel->GetItem()->GetItemDefIndex() ); + } + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CTFStorePage2::OnItemPanelMouseDoublePressed( vgui::Panel *panel ) +{ + // Do nothing +} diff --git a/game/client/tf/vgui/store/v2/tf_store_page2.h b/game/client/tf/vgui/store/v2/tf_store_page2.h new file mode 100644 index 0000000..8c25c92 --- /dev/null +++ b/game/client/tf/vgui/store/v2/tf_store_page2.h @@ -0,0 +1,82 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef TF_STORE_PAGE2_H +#define TF_STORE_PAGE2_H +#ifdef _WIN32 +#pragma once +#endif + +#include "store/tf_store_page_base.h" + +class CNavigationPanel; +class CClassFilterTooltip; + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +class CTFStorePage2 : public CTFStorePageBase +{ + DECLARE_CLASS_SIMPLE( CTFStorePage2, CTFStorePageBase ); +public: + CTFStorePage2( Panel *parent, const CEconStoreCategoryManager::StoreCategory_t *pPageData, const char *pPreviewItemResFile = NULL ); + ~CTFStorePage2(); + + virtual void OnPostCreate(); + + bool HasSubcategories() const; + int GetNumSubcategories() const { return m_pPageData ? m_pPageData->m_vecSubcategories.Count() : 0; } + + virtual void PerformLayout(); + virtual void ApplySchemeSettings( vgui::IScheme *pScheme ); + + virtual const char *GetPageResFile( void ); + virtual void OnCommand( const char *command ); + + MESSAGE_FUNC( OnPageShow, "PageShow" ); + MESSAGE_FUNC_PTR( OnItemDetails, "ItemDetails", panel ); + MESSAGE_FUNC_PARAMS( OnItemDefDetails, "ItemDefDetails", pData ); + MESSAGE_FUNC_PARAMS( OnTextChanged, "TextChanged", pData ); + MESSAGE_FUNC_PARAMS( OnNavButtonSelected, "NavButtonSelected", pData ); + MESSAGE_FUNC_PARAMS( OnAddItemToCart, "AddItemToCart", data ); // Comes from preview panel + MESSAGE_FUNC_PTR( OnItemPanelMouseDoublePressed, "ItemPanelMouseDoublePressed", panel ); + MESSAGE_FUNC_PTR( OnItemPanelMouseReleased, "ItemPanelMouseReleased", panel ); // Comes from CStoreItemControlsPanel + + virtual bool DoesEntryFilterPassSecondaryFilter( const econ_store_entry_t *pEntry ); + bool DoesEntryFilterPassSubcategoryFilter( const econ_store_entry_t *pEntry ); + + virtual void UpdateFilteredItems( void ); + virtual void UpdateFilterComboBox( void ); + virtual void GetFiltersForDef( GameItemDefinition_t *pDef, CUtlVector<int> *pVecFilters ); + virtual void OnThink( void ); + virtual bool FindAndSelectEntry( const econ_store_entry_t *pEntry ); + + void ClearNameFilter( bool bUpdateModelPanels ); + + virtual CStorePreviewItemPanel *CreatePreviewPanel( void ); + virtual CStorePricePanel* CreatePricePanel( int iIndex ); + + void ShowPreviewWindow( item_definition_index_t usDefIndex ); + int GetAllSubcategoriesIndex() const; + + vgui::TextEntry *m_pNameFilterTextEntry; + CExLabel *m_pSubcategoriesFilterLabel; + vgui::ComboBox *m_pSubcategoriesFilterCombo; + vgui::ComboBox *m_pSortByCombo; + CNavigationPanel *m_pHomeCategoryTabs; + CNavigationPanel *m_pClassFilterButtons; + CExLabel *m_pClassFilterTooltipLabel; + CClassFilterTooltip *m_pClassFilterTooltip; + + int m_iCurrentSubcategory; + CUtlVector<wchar_t> m_wNameFilter; + float m_flFilterItemTime; + + friend class CClassFilterTooltip; +}; + +#endif // TF_STORE_PAGE2_H diff --git a/game/client/tf/vgui/store/v2/tf_store_page_maps2.cpp b/game/client/tf/vgui/store/v2/tf_store_page_maps2.cpp new file mode 100644 index 0000000..3fb0ccd --- /dev/null +++ b/game/client/tf/vgui/store/v2/tf_store_page_maps2.cpp @@ -0,0 +1,59 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + + +#include "cbase.h" +#include "store/v2/tf_store_page_maps2.h" +#include "store/v2/tf_store_mapstamps_info_dialog.h" +#include "store/store_panel.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include <tier0/memdbgon.h> + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CTFStorePage_Maps2::CTFStorePage_Maps2( Panel *parent, const CEconStoreCategoryManager::StoreCategory_t *pPageData ) +: BaseClass( parent, pPageData, "Resource/UI/econ/store/v2/StorePreviewItemPanel_Maps.res" ) +{ +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CTFStorePage_Maps2::OnPageShow() +{ + BaseClass::OnPageShow(); + + SetDetailsVisible( false ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CTFStorePage_Maps2::OnCommand( const char *command ) +{ + if ( !V_strnicmp( command, "maps_learnmore", 14 ) ) + { + DisplayMapStampsDialog(); + } + else + { + BaseClass::OnCommand( command ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CTFStorePage_Maps2::DisplayMapStampsDialog() +{ + CTFMapStampsInfoDialog *pDlg = vgui::SETUP_PANEL( new CTFMapStampsInfoDialog( EconUI()->GetStorePanel() ) ); + pDlg->SetVisible( true ); + pDlg->InvalidateLayout( true, true ); + pDlg->SetKeyBoardInputEnabled(true); + pDlg->SetMouseInputEnabled(true); +} diff --git a/game/client/tf/vgui/store/v2/tf_store_page_maps2.h b/game/client/tf/vgui/store/v2/tf_store_page_maps2.h new file mode 100644 index 0000000..567ea5d --- /dev/null +++ b/game/client/tf/vgui/store/v2/tf_store_page_maps2.h @@ -0,0 +1,35 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef STORE_PAGE_MAPS2_H +#define STORE_PAGE_MAPS2_H +#ifdef _WIN32 +#pragma once +#endif + +#include "store/v2/tf_store_page2.h" + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +class CTFStorePage_Maps2 : public CTFStorePage2 +{ + DECLARE_CLASS_SIMPLE( CTFStorePage_Maps2, CTFStorePage2 ); +public: + CTFStorePage_Maps2( Panel *parent, const CEconStoreCategoryManager::StoreCategory_t *pPageData ); + virtual ~CTFStorePage_Maps2() {} + + virtual const char* GetPageResFile() { return "Resource/UI/econ/store/v2/StorePage_Maps.res"; } + +protected: + virtual void OnCommand( const char *command ); + virtual void OnPageShow( void ); + + void DisplayMapStampsDialog(); +}; + +#endif // STORE_PAGE_MAPS2_H diff --git a/game/client/tf/vgui/store/v2/tf_store_panel2.cpp b/game/client/tf/vgui/store/v2/tf_store_panel2.cpp new file mode 100644 index 0000000..89fa857 --- /dev/null +++ b/game/client/tf/vgui/store/v2/tf_store_panel2.cpp @@ -0,0 +1,109 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + + +#include "cbase.h" +#include "store/v2/tf_store_panel2.h" +#include "store/v2/tf_store_page2.h" +#include "store/v2/tf_store_page_maps2.h" +#include "store/store_page_halloween.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include <tier0/memdbgon.h> + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CTFStorePanel2::CTFStorePanel2( vgui::Panel *parent ) : CTFBaseStorePanel(parent) +{ +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CTFStorePanel2::ApplySchemeSettings( vgui::IScheme *pScheme ) +{ + BaseClass::ApplySchemeSettings( pScheme ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CTFStorePanel2::ShowPanel( bool bShow ) +{ + BaseClass::ShowPanel( bShow ); + + // Base class should turn this on + if ( bShow && m_bOGSLogging ) + { + EconUI()->Gamestats_Store( IE_STORE2_ENTERED ); + } + + Panel *pCheckOutButton = FindChildByName( "CheckOutButton" ); + if ( pCheckOutButton ) + { + pCheckOutButton->RequestFocus(); + } +} + +void CTFStorePanel2::OnAddToCart( void ) +{ + Panel *pCheckOutButton = FindChildByName( "CheckOutButton" ); + if ( pCheckOutButton ) + { + pCheckOutButton->RequestFocus(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CTFStorePanel2::OnThink() +{ + BaseClass::OnThink(); +} + +void CTFStorePanel2::OnKeyCodePressed( vgui::KeyCode code ) +{ + // ESC cancels + if ( code == KEY_XBUTTON_B ) + { + OnCommand( "close" ); + } + else + { + BaseClass::OnKeyCodePressed( code ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CTFStorePanel2::PostTransactionCompleted( void ) +{ + BaseClass::PostTransactionCompleted(); +} + +//----------------------------------------------------------------------------- +// Purpose: Static store page factory. +//----------------------------------------------------------------------------- +CStorePage *CTFStorePanel2::CreateStorePage( const CEconStoreCategoryManager::StoreCategory_t *pPageData ) +{ + if ( pPageData ) + { + if ( !Q_strcmp( pPageData->m_pchPageClass, "CStorePage_SpecialPromo" ) ) + return new CTFStorePage_SpecialPromo( this, pPageData ); + + if ( !Q_strcmp( pPageData->m_pchPageClass, "CStorePage_Maps" ) ) + return new CTFStorePage_Maps2( this, pPageData ); + + if ( !Q_strcmp( pPageData->m_pchPageClass, "CStorePage_Popular" ) ) + return new CTFStorePage_Popular( this, pPageData ); + } + + // Default, standard store page. + return new CTFStorePage2( this, pPageData ); +} diff --git a/game/client/tf/vgui/store/v2/tf_store_panel2.h b/game/client/tf/vgui/store/v2/tf_store_panel2.h new file mode 100644 index 0000000..4364706 --- /dev/null +++ b/game/client/tf/vgui/store/v2/tf_store_panel2.h @@ -0,0 +1,41 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef TF_STORE_PANEL2_H +#define TF_STORE_PANEL2_H +#ifdef _WIN32 +#pragma once +#endif + +#include "store/tf_store_panel_base.h" + +class CStorePage; + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +class CTFStorePanel2 : public CTFBaseStorePanel +{ + DECLARE_CLASS_SIMPLE( CTFStorePanel2, CTFBaseStorePanel ); +public: + CTFStorePanel2( vgui::Panel *parent ); + + // UI Layout + virtual void ApplySchemeSettings( vgui::IScheme *pScheme ); + virtual void OnThink(); + virtual void OnKeyCodePressed( vgui::KeyCode code ); + virtual void ShowPanel( bool bShow ); + virtual void OnAddToCart( void ); + + // GC Management + virtual void PostTransactionCompleted( void ); + +private: + virtual CStorePage *CreateStorePage( const CEconStoreCategoryManager::StoreCategory_t *pPageData ); +}; + +#endif // TF_STORE_PANEL2_H diff --git a/game/client/tf/vgui/store/v2/tf_store_preview_item2.cpp b/game/client/tf/vgui/store/v2/tf_store_preview_item2.cpp new file mode 100644 index 0000000..c213e10 --- /dev/null +++ b/game/client/tf/vgui/store/v2/tf_store_preview_item2.cpp @@ -0,0 +1,1367 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + + +#include "cbase.h" +#include "store/v2/tf_store_preview_item2.h" +#include "econ_item_description.h" +#include "vgui_controls/TextImage.h" +#include "vgui_controls/ScrollBar.h" +#include "vgui_controls/ScrollBarSlider.h" +#include "vgui/IInput.h" +#include "tf_item_schema.h" +#include "econ_item_system.h" +#include "store/store_panel.h" +#include "c_tf_gamestats.h" +#include "tf_playermodelpanel.h" +#include "navigationpanel.h" +#include "tf_mouseforwardingpanel.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include <tier0/memdbgon.h> + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +inline float LerpScale( float flIn, float flInMin, float flInMax, float flOutMin, float flOutMax ) +{ + float flDenom = flInMax - flInMin; + if ( flDenom == 0.0f ) + return 0.0f; + + float t = clamp( ( flIn - flInMin ) / flDenom, 0.0f, 1.0f ); + return Lerp( t, flOutMin, flOutMax ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +inline float SCurve( float t ) +{ + t = clamp( t, 0.0f, 1.0f ); + return t * t * (3 - 2*t); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CFullscreenStorePreviewItem::CFullscreenStorePreviewItem( vgui::Panel *pParent, EditablePanel *pOwner ) +: BaseClass( pParent, "FullscreenStorePreview" ), + m_iItemDef( INVALID_ITEM_ID ), + m_pCycleTextLabel( NULL ), + m_pTeamNavPanel( NULL ), + m_pPreviewButton( NULL ), + m_pRotLeftButton( NULL ), + m_pRotRightButton( NULL ), + m_pZoomButton( NULL ), + m_pOverlayPanel( NULL ), + m_flGoFullscreenStartTime( 0.0f ), + m_flLastMouseMoveTime( 0.0f ), + m_bIsHalloweenOrFullmoonOnlyItem( false ) +{ + m_hOwner = pOwner; +} + +void CFullscreenStorePreviewItem::SetItemDef( itemid_t iItemDef ) +{ + m_iItemDef = iItemDef; + + CStorePanel *pStorePanel = EconUI()->GetStorePanel(); + if ( pStorePanel ) + { + const CEconStorePriceSheet *pPriceSheet = pStorePanel->GetPriceSheet(); + CExButton *pPreviewButton = dynamic_cast<CExButton *>( FindChildByName( "TryItOutButton" ) ); + if ( pPriceSheet && pPreviewButton ) + { + const econ_store_entry_t *pStoreEntry = pPriceSheet->GetEntry( m_iItemDef ); + pPreviewButton->SetVisible( pStoreEntry && pStoreEntry->CanPreview() ); + } + } +} + +void CFullscreenStorePreviewItem::GoFullscreen( CTFPlayerModelPanel *pPlayerModelPanel ) +{ + m_Stats.Clear(); + + m_flGoFullscreenStartTime = gpGlobals->realtime; + + SetAlpha( 0 ); + SetVisible( true ); + MoveToFront(); + + m_pPlayerModelPanel = pPlayerModelPanel; + + if ( !m_pPlayerModelPanel.Get() ) + return; + + // Cache old player model panel bounds and such + m_pPlayerModelPanel->GetBounds( m_OldModelState.m_aPlayerModelPanelBounds[0], m_OldModelState.m_aPlayerModelPanelBounds[1], m_OldModelState.m_aPlayerModelPanelBounds[2], m_OldModelState.m_aPlayerModelPanelBounds[3] ); + m_OldModelState.m_vecPlayerPos = m_pPlayerModelPanel->m_vecPlayerPos; + m_OldModelState.m_bZoomed = m_pPlayerModelPanel->IsZoomed(); + + // Fullscreen panel is new parent + m_pPlayerModelPanel->SetParent( this ); + + // Get team state + if ( m_pTeamNavPanel ) + { + m_pTeamNavPanel->UpdateButtonSelectionStates( m_pPlayerModelPanel->GetTeam() == TF_TEAM_RED ? 0 : 1 ); + } +} + +void CFullscreenStorePreviewItem::ExitFullscreen() +{ + if ( !m_hOwner.Get() ) + return; + + if ( m_pPlayerModelPanel.Get() ) + { + m_pPlayerModelPanel->SetParent( m_hOwner.Get() ); + m_pPlayerModelPanel->SetBounds( m_OldModelState.m_aPlayerModelPanelBounds[0], m_OldModelState.m_aPlayerModelPanelBounds[1], m_OldModelState.m_aPlayerModelPanelBounds[2], m_OldModelState.m_aPlayerModelPanelBounds[3] ); + + // Reset the player position to it's pre-fullscreen location, but add on any zoom delta if needed. + const Vector vecZoomOffset = m_OldModelState.m_bZoomed != m_pPlayerModelPanel->IsZoomed() ? m_pPlayerModelPanel->GetZoomOffset() : vec3_origin; + m_pPlayerModelPanel->m_vecPlayerPos = m_OldModelState.m_vecPlayerPos + vecZoomOffset; + } + + m_flGoFullscreenStartTime = 0.0f; + SetVisible( false ); + + PostMessage( m_hOwner.Get(), new KeyValues( "ExitFullscreen" ) ); +} + +bool CFullscreenStorePreviewItem::IsFullscreenMode() +{ + return IsVisible(); +} + +void CFullscreenStorePreviewItem::OnNavButtonSelected( KeyValues *pData ) +{ + const int iTeam = pData->GetInt( "userdata", -1 ); AssertMsg( iTeam >= 0, "Bad filter" ); + if ( iTeam < 0 ) + return; + + if ( !m_pPlayerModelPanel.Get() ) + return; + + m_pPlayerModelPanel->SetTeam( iTeam ); + + C_CTFGameStats::ImmediateWriteInterfaceEvent( "team_switch_%s(store_preview_item_panel_fullscreen)", iTeam == TF_TEAM_RED ? "red" : "blu" ); +} + +void CFullscreenStorePreviewItem::OnThink() +{ + BaseClass::OnThink(); + + if ( m_flGoFullscreenStartTime == 0.0f ) + { + SetVisible( false ); + return; + } + + // We are fading, or already faded in + SetVisible( true ); + + // Keep track of mouse movement - if the mouse button is down, force the mouse-moving state so we + // don't end up fading out while the player is rotating or clicking-and-holding anywhere else + int nMouseX, nMouseY; + vgui::input()->GetCursorPos( nMouseX, nMouseY ); + bool bMouseMoved = false; + const bool bMouseButtonDown = vgui::input()->IsMouseDown( MOUSE_LEFT ); + const bool bForceMouseMoving = bMouseButtonDown; + if ( bForceMouseMoving || nMouseX != m_nLastMouseX || nMouseY != m_nLastMouseY ) + { + bMouseMoved = true; + m_nLastMouseX = nMouseX; + m_nLastMouseY = nMouseY; + m_flLastMouseMoveTime = gpGlobals->realtime; + } + + // Fade in the button blocker if the mouse has been idle for some period of time + if ( m_pOverlayPanel ) + { + const float flButtonBlockerFade = SCurve( + LerpScale( + gpGlobals->realtime, + m_flLastMouseMoveTime + m_flUiFadeoutTime, + m_flLastMouseMoveTime + m_flUiFadeoutTime + m_flUiFadeoutDuration, + 0.0f, + 1.0f + ) + ); + + m_pOverlayPanel->SetVisible( flButtonBlockerFade > 0.0f ); + + m_pOverlayPanel->SetAlpha( (int)( 255 * flButtonBlockerFade ) ); + + // Set to layer above overlay panel, so it will always be visible + m_pPlayerModelPanel->SetZPos( flButtonBlockerFade > 0.0f ? ( m_pOverlayPanel->GetZPos() + 1 ) : 0 ); + } + + const float flFade = SCurve( + LerpScale( + gpGlobals->realtime, + m_flGoFullscreenStartTime, + m_flGoFullscreenStartTime + m_flFullscreenFadeToBlackDuration, + 0.0f, + 1.0f + ) + ); + + SetAlpha( (int)( 255 * flFade ) ); + + if ( !m_pPlayerModelPanel.Get() ) + return; + + // Resize 3D model panel + const int aDstBounds[4] = { 0, 0, ScreenWidth(), ScreenHeight() }; + const int aBounds[4] = { + Lerp( flFade, m_OldModelState.m_aPlayerModelPanelBounds[0], aDstBounds[0] ), + Lerp( flFade, m_OldModelState.m_aPlayerModelPanelBounds[1], aDstBounds[1] ), + Lerp( flFade, m_OldModelState.m_aPlayerModelPanelBounds[2], aDstBounds[2] ), + Lerp( flFade, m_OldModelState.m_aPlayerModelPanelBounds[3], aDstBounds[3] ) + }; + m_pPlayerModelPanel->SetBounds( aBounds[0], aBounds[1], aBounds[2], aBounds[3] ); + + if ( flFade < 0.999f ) + { + const Vector vecZoomOffset = m_pPlayerModelPanel->IsZoomed() ? m_pPlayerModelPanel->GetZoomOffset() : vec3_origin; + const Vector vecFullscreenOrigin( m_flModelPanelOriginX, m_flModelPanelOriginY, m_flModelPanelOriginZ ); + m_pPlayerModelPanel->m_vecPlayerPos = Lerp( flFade, m_OldModelState.m_vecPlayerPos, vecFullscreenOrigin + vecZoomOffset ); + } + + if ( m_pZoomButton ) + { + m_pZoomButton->SetEnabled( flFade == 1.0f ); + } + + if ( bMouseButtonDown && m_pRotLeftButton && m_pRotRightButton ) + { + float flDeltaAngle = 0; + const float kScale = 100.0f; + if ( m_pRotLeftButton->IsWithin( nMouseX, nMouseY ) ) + { + flDeltaAngle = -gpGlobals->frametime * kScale; + } + else if ( m_pRotRightButton->IsWithin( nMouseX, nMouseY ) ) + { + flDeltaAngle = gpGlobals->frametime * kScale; + } + + m_pPlayerModelPanel->RotateYaw( flDeltaAngle ); + + // Accumulate time rotation buttons are being pressed for stat tracking + m_Stats.m_flRotationTime += gpGlobals->frametime; + } +} + +void CFullscreenStorePreviewItem::ApplySchemeSettings( vgui::IScheme *pScheme ) +{ + BaseClass::ApplySchemeSettings( pScheme ); + + LoadControlSettings( "Resource/UI/econ/store/v2/StorePreviewItemPanel_Fullscreen.res" ); + + m_pOverlayPanel = dynamic_cast<EditablePanel *>( FindChildByName( "OverlayPanel" ) ); + m_pRotLeftButton = dynamic_cast<CExButton *>( FindChildByName( "RotateLeftButton" ) ); + m_pRotRightButton = dynamic_cast<CExButton *>( FindChildByName( "RotateRightButton" ) ); + m_pZoomButton = dynamic_cast<CExButton *>( FindChildByName( "ZoomButton" ) ); + m_pTeamNavPanel = dynamic_cast<CNavigationPanel *>( FindChildByName( "TeamNavPanel" ) ); +} + +void CFullscreenStorePreviewItem::OnCommand( const char *command ) +{ + C_CTFGameStats::ImmediateWriteInterfaceEvent( "on_command(store_preview_item_panel_fullscreen)", command ); + + if ( !V_strnicmp( command, "close", 6 ) ) + { + ExitFullscreen(); + return; + } + + if ( m_hOwner.Get() ) + { + // Let the owner handle the command + m_hOwner->OnCommand( command ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CTFStorePreviewItemPanel2::CTFStorePreviewItemPanel2( vgui::Panel *pParent, const char *pResFile, const char *pPanelName, CStorePage *pOwner ) +: BaseClass( pParent, pResFile, "storepreviewitem", pOwner ) +{ + m_pScrollBar = new ScrollBar( this, "ScrollBar", true ); + m_pScrollBar->AddActionSignalTarget( this ); + + m_pFullscreenPanel = new CFullscreenStorePreviewItem( this, this ); + m_pFullscreenPanel->AddActionSignalTarget( this ); + + m_pMouseOverItemPanel = vgui::SETUP_PANEL( new CItemModelPanel( this, "mouseoveritempanel" ) ); + m_pMouseOverTooltip = new CItemModelPanelToolTip( this ); + m_pMouseOverTooltip->SetupPanels( this, m_pMouseOverItemPanel ); + m_pMouseOverTooltip->SetPositioningStrategy( IPTTP_BOTTOM_SIDE ); + m_pMouseOverItemPanel->MoveToFront(); + + m_pItemViewData = NULL; + m_pSOEconItemData = NULL; + Clear(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CTFStorePreviewItemPanel2::Clear() +{ + m_pPlayerModelPanel = NULL; + + m_pPreviewButton = NULL; + m_pDialogFrame = NULL; + m_pPreviewViewportBg = NULL; + m_pItemNameLabel = NULL; + m_pAttributesLabel = NULL; + m_pItemCollectionHighlight = NULL; + m_pDetailsView = NULL; + m_pDetailsViewChild = NULL; // Scrollable + m_pItemWikiPageButton = NULL; + m_pTeamNavPanel = NULL; + m_pCycleTextLabel = NULL; + m_pScrollableChild = NULL; + m_pGoFullscreenButton = NULL; + m_nNumAttribLinesAdded = 0; + m_bArmoryTextAdded = false; + m_nNumAttribLinesAdded = 0; + m_iSliderPos = 0; + m_aClickPos[0] = m_aClickPos[1] = 0; + m_bCloseOnUp = false; + m_bMouseWasDown = false; + m_bIsHalloweenOrFullmoonOnlyItem = false; + + for ( int i = 0; i < ARRAYSIZE( m_pAddRentalToCartButtons ); i++ ) + { + m_pAddRentalToCartButtons[i] = NULL; + } + + m_vecReferenceItemPanels.RemoveAll(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CTFStorePreviewItemPanel2::ApplySchemeSettings( vgui::IScheme *pScheme ) +{ + Clear(); + + BaseClass::ApplySchemeSettings( pScheme ); + + FOR_EACH_VEC( m_pItemIcons, i ) + { + // Strip tooltips. + // We have all the same data already + m_pItemIcons[i]->GetItemPanel()->SetTooltip( NULL, NULL ); + } + + m_pDialogFrame = dynamic_cast<EditablePanel *>( FindChildByName( "DialogFrame" ) ); + m_pPreviewButton = dynamic_cast<CExButton *>( FindChildByName( "TryItOutButton" ) ); + m_pCycleTextLabel = dynamic_cast<CExLabel *>( FindChildByName( "CycleTextLabel" ) ); + m_pGoFullscreenButton = dynamic_cast<CExImageButton *>( FindChildByName( "GoFullscreenButton" ) ); + + COMPILE_TIME_ASSERT( ARRAYSIZE( m_pAddRentalToCartButtons ) == 3 ); +#ifdef ENABLE_STORE_RENTAL_BACKEND + m_pAddRentalToCartButtons[0] = dynamic_cast<CExButton *>( FindChildByName( "AddRentalToCartButton_1Day" ) ); + m_pAddRentalToCartButtons[1] = dynamic_cast<CExButton *>( FindChildByName( "AddRentalToCartButton_3Day" ) ); + m_pAddRentalToCartButtons[2] = dynamic_cast<CExButton *>( FindChildByName( "AddRentalToCartButton_7Day" ) ); +#endif + + if ( m_pDialogFrame ) + { + m_pPreviewViewportBg = dynamic_cast<EditablePanel *>( m_pDialogFrame->FindChildByName( "PreviewViewportBg" ) ); + m_pItemNameLabel = dynamic_cast<CExLabel *>( m_pDialogFrame->FindChildByName( "ItemNameLabel" ) ); + m_pDetailsView = dynamic_cast<EditablePanel *>( m_pDialogFrame->FindChildByName( "DetailsView" ) ); + + if ( m_pDetailsView ) + { + m_pDetailsViewChild = dynamic_cast<EditablePanel *>( m_pDetailsView->FindChildByName( "ScrollableChild" ) ); + + if ( !m_pDetailsViewChild ) + { + m_pDetailsViewChild = m_pDetailsView; + } + + m_pAttributesLabel = dynamic_cast<CExLabel *>( m_pDetailsViewChild->FindChildByName( "AttributesLabel" ) ); + m_pItemCollectionHighlight = dynamic_cast<EditablePanel *>( m_pDetailsViewChild->FindChildByName( "collectionhighlight" ) ); + if ( m_pItemCollectionHighlight ) + { + m_pItemCollectionHighlight->InvalidateLayout( true, true ); + } + m_pItemWikiPageButton = dynamic_cast<CExButton *>( m_pDetailsViewChild->FindChildByName( "ItemWikiPageButton" ) ); + + if ( m_pItemWikiPageButton ) + { + m_pItemWikiPageButton->AddActionSignalTarget( this ); + } + } + } + + m_pTeamNavPanel = dynamic_cast<CNavigationPanel *>( FindChildByName( "TeamNavPanel" ) ); + + m_pMouseOverItemPanel->SetBorder( pScheme->GetBorder("LoadoutItemPopupBorder") ); + + SetState( PS_ITEM ); +} + +//----------------------------------------------------------------------------- +// Purpose: Given two controls A and B, place B such that it is (nXOffset,nYOffset) +// pixels offset from A, using it's content width or height, depending on bVertical. +// pControlNameA can be NULL, in which case zeros will be used as the offset. +//----------------------------------------------------------------------------- +int CTFStorePreviewItemPanel2::PlaceControl( Panel *pParent, const char *pControlNameA, const char *pControlNameB, int nOffset, bool bVertical, bool bSizeAToContents/*=true*/, bool bUseContentSize/*=true*/ ) +{ + if ( !pParent || !pControlNameB ) + { + AssertMsg( 0, "Bad!" ); + return 0; + } + + Label *pControlA = pControlNameA ? dynamic_cast<Label *>( pParent->FindChildByName( pControlNameA ) ) : NULL; + Label *pControlB = dynamic_cast<Label *>( pParent->FindChildByName( pControlNameB ) ); + if ( !pControlB ) + { + return 0; + } + + if ( !pControlA && bVertical ) + { + pControlA = m_pLastNewLineControl; + } + + int aSize[2] = { 0, 0 }; + int aPos[2] = { 0, 0 }; + if ( pControlA ) + { + pControlA->SetVisible( true ); + + if ( bSizeAToContents ) + { + pControlA->SizeToContents(); + pControlA->InvalidateLayout( true ); + } + + if ( bUseContentSize ) + { + pControlA->GetContentSize( aSize[0], aSize[1] ); + } + else + { + pControlA->GetSize( aSize[0], aSize[1] ); + } + + pControlA->GetPos( aPos[0], aPos[1] ); + } + + int aOffset[2] = { 0, 0 }; + if ( bVertical ) + { + aOffset[1] = aSize[1] + nOffset; + } + else + { + aOffset[0] = aSize[0] + nOffset; + } + + // NOTE: We add in the slider position here + pControlB->SetPos( aPos[0] + aOffset[0], aPos[1] + aOffset[1] ); + + pControlB->SetVisible( true ); + +#if _DEBUG + /* + Msg( "control A: %s size: w=%i h=%i pos: (%i, %i)\n", pControlNameA, aSize[0], aSize[1], aPos[0], aPos[1] ); + int x,y; + pControlB->GetPos(x,y); + Msg( "control B: %s pos: (%i, %i)\n", pControlNameB, x,y ); + */ +#endif + + if ( bVertical ) + { + m_pLastNewLineControl = pControlB; + } + + m_nViewMaxHeight = MAX( m_nViewMaxHeight, aPos[1] + aOffset[1] + pControlB->GetTall() ); + return m_nViewMaxHeight; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CTFStorePreviewItemPanel2::PerformLayout( void ) +{ +// BaseClass::PerformLayout(); // We override completely here + + // 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 ( m_pDialogFrame && ( iNumItemIcons || iNumClassIcons ) ) + { + int aDialogFramePos[2]; + m_pDialogFrame->GetPos( aDialogFramePos[0], aDialogFramePos[1] ); + + int iCenterX = aDialogFramePos[0] + m_pDialogFrame->GetWide() / 4; + int interval = XRES(2); + int totalWidth = (iNumItemIcons > 0 ? iNumItemIcons * m_pItemIcons[0]->GetWide() : 0) + (iNumClassIcons * m_pClassIcons[0]->GetWide()) + (interval * (iNumItemIcons + iNumClassIcons - 1)); + int iX = iCenterX - ( totalWidth / 2 ); + + int posX, posY; + if ( iNumItemIcons > 0 ) + { + m_pItemIcons[0]->GetPos( posX, posY ); + } + else + { + m_pClassIcons[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++; + } + } + } + + if ( !m_pPreviewViewportBg || !m_pItemNameLabel || !m_pDetailsViewChild || !m_pDetailsView || !m_pDialogFrame ) + { + m_pScrollBar->SetVisible( false ); + return; + } + + // Make sure we size the item name in case it needs multiple lines. + m_pItemNameLabel->SizeToContents(); + m_pItemNameLabel->InvalidateLayout( true ); + if ( m_pItemNameLabel && m_item.IsValid() ) + { + const CEconItemRarityDefinition* pItemRarity = GetItemSchema()->GetRarityDefinition( m_item.GetItemDefinition()->GetRarity() ); + + // Setup the rarity color overlay + { + Color color( 255, 255, 255, 255 ); + if ( pItemRarity ) + { + attrib_colors_t attribColor = pItemRarity->GetAttribColor(); + vgui::IScheme *pScheme = scheme()->GetIScheme( GetScheme() ); + color = pScheme->GetColor( GetColorNameForAttribColor( attribColor ), Color( 255, 255, 255, 255 ) ); + } + m_pItemNameLabel->SetColorStr( color ); + } + } + + int aItemNameLabelPos[2]; + m_pItemNameLabel->GetPos( aItemNameLabelPos[0], aItemNameLabelPos[1] ); + + // This is the item label's new bottom y coordinate after it's been sized + const int nNewYRelativeToDlgFrame = aItemNameLabelPos[1] + m_pItemNameLabel->GetTall(); + + // Set ypos for details view and scroll bar + int aDetailsViewPos[2]; + m_pDetailsView->GetPos( aDetailsViewPos[0], aDetailsViewPos[1] ); + m_pDetailsView->SetPos( aDetailsViewPos[0], nNewYRelativeToDlgFrame ); + + int aDialogFramePos[2]; + m_pDialogFrame->GetPos( aDialogFramePos[0], aDialogFramePos[1] ); + + if ( m_pScrollBar ) + { + int aScrollBar[2]; + m_pScrollBar->GetPos( aScrollBar[0], aScrollBar[1] ); + m_pScrollBar->SetPos( aScrollBar[0], nNewYRelativeToDlgFrame + aDialogFramePos[1] ); + } + + int nNewHeight = m_pPreviewViewportBg->GetTall() - m_pItemNameLabel->GetTall(); + m_pDetailsView->SetTall( nNewHeight ); + if ( m_pScrollBar ) + { + m_pScrollBar->SetTall( nNewHeight ); + } + + // Place paint and style buttons + CUtlVector<CExButton *> vecVisibleButtons; + const int nNumPossibilyVisibleWeapons = 1; + CExButton *pPossiblyVisibleButtons[nNumPossibilyVisibleWeapons] = { m_pNextWeaponButton }; + for ( int i = 0; i < nNumPossibilyVisibleWeapons; ++i ) + { + CExButton *pCurButton = pPossiblyVisibleButtons[i]; + if ( !pCurButton || !pCurButton->IsVisible() ) + continue; + + vecVisibleButtons.AddToTail( pCurButton ); + } + + int nNumButtonsNeeded = vecVisibleButtons.Count(); + if ( nNumButtonsNeeded ) + { + // Center however many buttons we need to along the top of the viewport + int aViewportPos[2]; + m_pPreviewViewportBg->GetPos( aViewportPos[0], aViewportPos[1] ); + for ( int i = 0; i < nNumButtonsNeeded; ++i ) + { + CExButton *pCurButton = vecVisibleButtons[i]; + pCurButton->SetPos( aDialogFramePos[0] + aViewportPos[0] + ( i + 1 ) * m_pPreviewViewportBg->GetWide() / ( nNumButtonsNeeded + 1 ) - m_iControlButtonWidth / 2, m_iControlButtonY ); + pCurButton->SetSize( m_iControlButtonWidth, m_iControlButtonHeight ); + } + } + + m_pLastNewLineControl = NULL; + m_nViewMaxHeight = 0; + + PlaceControl( m_pDetailsViewChild, NULL, "ItemLevelInfoLabel", m_iSmallVerticalBreakSize, true ); + + if ( m_bIsHalloweenOrFullmoonOnlyItem ) + { + PlaceControl( m_pDetailsViewChild, "ItemLevelInfoLabel", "RestrictionsLabel", m_iMediumVerticalBreakSize, true ); + PlaceControl( m_pDetailsViewChild, "RestrictionsLabel", "RestrictionsTextLabel", m_iHorizontalBreakSize, false ); + PlaceControl( m_pDetailsViewChild, "RestrictionsLabel", "UsedByLabel", m_iSmallVerticalBreakSize, true ); + } + else + { + PlaceControl( m_pDetailsViewChild, "ItemLevelInfoLabel", "UsedByLabel", m_iMediumVerticalBreakSize, true ); + } + + PlaceControl( m_pDetailsViewChild, "UsedByLabel", "UsedByTextLabel", m_iHorizontalBreakSize, false ); + PlaceControl( m_pDetailsViewChild, "UsedByLabel", "SlotLabel", m_iSmallVerticalBreakSize, true ); + PlaceControl( m_pDetailsViewChild, "SlotLabel", "SlotTextLabel", m_iHorizontalBreakSize, false ); + PlaceControl( m_pDetailsViewChild, "SlotLabel", "PriceLabel", m_iBigVerticalBreakSize, true ); + + if ( m_bArmoryTextAdded ) + { + PlaceControl( m_pDetailsViewChild, "PriceLabel", "ArmoryTextLabel", m_iBigVerticalBreakSize, true ); + PlaceControl( m_pDetailsViewChild, "ArmoryTextLabel", "AttributesLabel", m_iBigVerticalBreakSize, true ); + } + else + { + PlaceControl( m_pDetailsViewChild, "PriceLabel", "AttributesLabel", m_iBigVerticalBreakSize, true ); + } + + PlaceControl( m_pDetailsViewChild, m_nNumAttribLinesAdded == 0 ? "PriceLabel" : "AttributesLabel", "ItemWikiPageButton", m_iBigVerticalBreakSize, true ); + + PlaceControl( m_pDetailsViewChild, "ItemWikiPageButton", "TradableLabel", m_iBigVerticalBreakSize, true, false, false ); + PlaceControl( m_pDetailsViewChild, "TradableLabel", "TradableTextLabel", m_iHorizontalBreakSize, false ); + PlaceControl( m_pDetailsViewChild, "TradableLabel", "CraftableLabel", m_iSmallVerticalBreakSize, true ); + PlaceControl( m_pDetailsViewChild, "CraftableLabel", "CraftableTextLabel", m_iHorizontalBreakSize, false ); + PlaceControl( m_pDetailsViewChild, "CraftableLabel", "GiftableLabel", m_iSmallVerticalBreakSize, true ); + PlaceControl( m_pDetailsViewChild, "GiftableLabel", "GiftableTextLabel", m_iHorizontalBreakSize, false ); + PlaceControl( m_pDetailsViewChild, "GiftableLabel", "NameableLabel", m_iSmallVerticalBreakSize, true ); + PlaceControl( m_pDetailsViewChild, "NameableLabel", "NameableTextLabel", m_iHorizontalBreakSize, false ); + + if ( m_pScrollBar ) + { + m_pScrollBar->SetVisible( true ); + m_pScrollBar->InvalidateLayout( true ); + m_pScrollBar->SetRange( 0, m_nViewMaxHeight ); + m_pScrollBar->SetRangeWindow( m_pScrollBar->GetTall() ); + + m_pDetailsViewChild->SetTall( m_nViewMaxHeight ); + UpdateScrollableChild(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CTFStorePreviewItemPanel2::UpdateScrollableChild() +{ + m_pDetailsViewChild->SetPos( 0, -m_iSliderPos ); +} + + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CTFStorePreviewItemPanel2::OnCommand( const char *command ) +{ + C_CTFGameStats::ImmediateWriteInterfaceEvent( "on_command(store_preview_item_panel)", command ); + + if ( !V_strnicmp( command, "closex", 5 ) ) + { + // This is just a way for us to differentiate between 'x' button being pressed vs. + // the "back" button vs. clicking outside the preview window. + DoClose(); + } + else if ( !V_strnicmp( command, "tryitout", 8 ) ) + { + // OGS data gets written elsewhere for this event + PostMessage( m_pOwner, new KeyValues( "PreviewItem", "item_def_index", m_item.GetItemDefIndex() ) ); + DoClose(); + } + else if ( !V_strnicmp( command, "addtocart", 9 ) +#ifdef ENABLE_STORE_RENTAL_BACKEND + || !V_strnicmp( command, "addrentaltocart", 15 ) +#endif + ) + { +#ifdef ENABLE_STORE_RENTAL_BACKEND + ECartItemType eCartItemType = !V_stricmp( command, "addrentaltocart_1day" ) + ? kCartItem_Rental_1Day + : !V_stricmp( command, "addrentaltocart_3day" ) + ? kCartItem_Rental_3Day + : !V_stricmp( command, "addrentaltocart_7day" ) + ? kCartItem_Rental_7Day + : kCartItem_Purchase; +#else + ECartItemType eCartItemType = kCartItem_Purchase; +#endif + + KeyValues *pParams = new KeyValues( "AddItemToCart" ); + pParams->SetInt( "item_def", m_item.GetItemDefIndex() ); + pParams->SetInt( "cart_add_type", eCartItemType ); + PostMessage( m_pOwner, pParams ); + DoClose(); + } + else if ( !V_strnicmp( command, "viewwikipage", 12 ) ) + { + if ( steamapicontext && steamapicontext->SteamFriends() && m_pItemFullImage ) + { + CEconItemView *pItem = m_pItemFullImage->GetItem(); + if ( pItem->IsValid() ) + { + // Determine which language we should use + char uilanguage[ 64 ]; + uilanguage[0] = 0; + engine->GetUILanguage( uilanguage, sizeof( uilanguage ) ); + ELanguage iLang = PchLanguageToELanguage( uilanguage ); + + char szURL[512]; + Q_snprintf( szURL, sizeof(szURL), "http://wiki.teamfortress.com/scripts/itemredirect.php?id=%d&lang=%s", pItem->GetItemDefIndex(), GetLanguageICUName( iLang ) ); + steamapicontext->SteamFriends()->ActivateGameOverlayToWebPage( szURL ); + + C_CTF_GameStats.Event_Catalog( IE_ARMORY_BROWSE_WIKI, NULL, pItem ); + } + } + } + else if ( !V_strnicmp( command, "team_", 5 ) ) + { + const char *pTeam = command + 5; + if ( !V_strnicmp( pTeam, "red", 3 ) ) + { + m_pPlayerModelPanel->SetTeam( TF_TEAM_RED ); + } + else + { + m_pPlayerModelPanel->SetTeam( TF_TEAM_BLUE ); + } + } + else if ( !V_strnicmp( command, "gofullscreen", 11 ) ) + { + if ( m_pFullscreenPanel ) + { + m_pFullscreenPanel->GoFullscreen( m_pPlayerModelPanel ); + } + } + else + { + BaseClass::OnCommand( command ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CTFStorePreviewItemPanel2::OnClassIconSelected( KeyValues *data ) +{ + BaseClass::OnClassIconSelected( data ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CTFStorePreviewItemPanel2::OnHideClassIconMouseover( void ) +{ + BaseClass::OnHideClassIconMouseover(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CTFStorePreviewItemPanel2::OnShowClassIconMouseover( KeyValues *data ) +{ + // We decided not to show the "this item is + // usable by the [Class name]" tooltip. + //BaseClass::OnShowClassIconMouseover( data ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CTFStorePreviewItemPanel2::PreviewItemCopy( int iClass, CEconItemView *pItem, const econ_store_entry_t* pEntry ) +{ + // Make a copy of SO data since it comes from the market and will fall out of scope + if ( m_pItemViewData ) + { + delete m_pItemViewData; + m_pItemViewData = NULL; + } + + if ( m_pSOEconItemData ) + { + delete m_pSOEconItemData; + m_pSOEconItemData = NULL; + } + + m_pItemViewData = new CEconItemView( *pItem ); + m_pSOEconItemData = new CEconItem( *pItem->GetSOCData() ); + m_pItemViewData->SetNonSOEconItem( m_pSOEconItemData ); + PreviewItem( iClass, m_pItemViewData, pEntry ); +} +//----------------------------------------------------------------------------- +void CTFStorePreviewItemPanel2::PreviewItem( int iClass, CEconItemView *pItem, const econ_store_entry_t* pEntry ) +{ + BaseClass::PreviewItem( iClass, pItem, pEntry ); + + C_CTFGameStats::ImmediateWriteInterfaceEvent( "store_preview_item_panel(preview_item)", CFmtStr( "%i", m_item.GetItemDefIndex() ).Access() ); + + // Reload the .res file right now, since we need to make sure the item name label, on which all other controls + // base their position, is valid. + InvalidateLayout( true, true ); + + // Update the fullscreen item def index + if ( m_pFullscreenPanel ) + { + m_pFullscreenPanel->SetItemDef( m_item.GetItemDefIndex() ); + } + + // If we didn't have a store entry passed in, look for one. + if ( !pEntry ) + { + CStorePanel *pStorePanel = EconUI()->GetStorePanel(); + if ( pStorePanel ) + { + pEntry = pStorePanel->GetPriceSheet()->GetEntry( m_item.GetItemDefIndex() ); + } + } + + if ( m_pDialogFrame && m_pDetailsView && m_pItemFullImage && m_pItemFullImage->GetItem() && m_pAttributesLabel ) + { + const CEconItemView *pFullItem = m_pItemFullImage->GetItem(); + const CEconItemDefinition *pBaseDef = pFullItem->GetItemDefinition(); + const CTFItemDefinition *pDef = dynamic_cast<const CTFItemDefinition *>( pBaseDef ); + + if ( pFullItem && pDef ) + { + m_pDialogFrame->SetDialogVariable( "itemname", pFullItem->GetItemName() ); + + // Holiday restrictions? + const char *pHolidayRestriction = pDef->GetHolidayRestriction() ? pDef->GetHolidayRestriction() : ""; + m_bIsHalloweenOrFullmoonOnlyItem = StringHasPrefix( pHolidayRestriction, "halloween" ); + + CTFItemSchema *pSchema = ItemSystem()->GetItemSchema(); + if ( pSchema ) + { + // Build a list of classes by which this item can be used + const CBitVec<LOADOUT_COUNT> *pbvClassUsability = pDef->GetClassUsability(); + + const int kClassNamesSize = 512; + wchar_t wszClassNames[ kClassNamesSize ] = L""; + int nClassAdded = 0; + bool bAllClasses = true; + for ( int i = TF_FIRST_NORMAL_CLASS; i < TF_LAST_NORMAL_CLASS; ++i ) + { + if ( !pbvClassUsability->IsBitSet( i ) ) + { + bAllClasses = false; + break; + } + } + + if ( bAllClasses ) + { + m_pDetailsViewChild->SetDialogVariable( "used_by_classes", g_pVGuiLocalize->Find( "#Store_ItemDesc_AllClasses" ) ); + } + else + { + for ( int i = 0; i < LOADOUT_COUNT; ++i ) + { + if ( pbvClassUsability->IsBitSet( i ) ) + { + V_wcscat_safe( wszClassNames, nClassAdded++ == 0 ? L"" : L", " ); // add empty lines everywhere except before the first line + V_wcscat_safe( wszClassNames, g_pVGuiLocalize->Find( g_aPlayerClassNames[i] ) ); + } + } + m_pDetailsViewChild->SetDialogVariable( "used_by_classes", wszClassNames ); + } + + // Setup the slot string + const CUtlVector< const char * > &vecLoadoutStrings = pSchema->GetLoadoutStrings( pDef->GetEquipType() ); + const int iSlot = pDef->GetDefaultLoadoutSlot(); + const bool bSlotValid = vecLoadoutStrings.IsValidIndex( iSlot ); + m_pDetailsViewChild->SetDialogVariable( "slot", g_pVGuiLocalize->Find( bSlotValid ? CFmtStr( "#LoadoutSlot_%s", vecLoadoutStrings[iSlot] ).Access() : "#Store_ItemDesc_Slot_None" ) ); + + // Make an attempt to display tradability accurately even though we don't have an item to pull from. + static CSchemaAttributeDefHandle pAttrib_CannotTrade( "cannot trade" ); + Assert( pAttrib_CannotTrade ); + + // Get localized versions of "yes" and "no" + const wchar_t *pYesNo[2] = { + g_pVGuiLocalize->Find( "#Store_ItemDesc_Yes" ), + g_pVGuiLocalize->Find( "#Store_ItemDesc_No" ) + }; + bool bIsMapStamp = pDef->GetItemClass() && !V_strncmp( pDef->GetItemClass(), "map_token", 9 ); + bool bIsTradeable = bIsMapStamp || FindAttribute( pDef, pAttrib_CannotTrade ) + ? pYesNo[ 1 ] + : g_pVGuiLocalize->Find( "#Attrib_Store_TradableAfterDate" ); + + m_pDetailsViewChild->SetDialogVariable( "giftable", bIsTradeable ); + m_pDetailsViewChild->SetDialogVariable( "nameable", ( pDef->GetCapabilities() & ITEM_CAP_NAMEABLE ) != 0 ? pYesNo[0] : pYesNo[1] ); + + m_pDetailsViewChild->SetDialogVariable( "tradable", bIsTradeable ); + + // No store-bought items are craftable, but items that were going to be tradable would show as craftable because + // they weren't real items with a real origin that would prevent them from being crafted. In the short term it makes + // more sense to just force this to always display "false" because at least it will never be wrong. + m_pDetailsViewChild->SetDialogVariable( "craftable", pDef->IsBundle() + ? g_pVGuiLocalize->Find( "#Attrib_CannotCraftWeapons" ) + : pDef->GetCapabilities() & ITEM_CAP_CAN_BE_CRAFTED_IF_PURCHASED + ? pYesNo[0] + : pYesNo[1] ); + + // Setup price + if ( pEntry ) + { + ECurrency eCurrency = EconUI()->GetStorePanel()->GetCurrency(); + + int iTotalPrice = pEntry->GetCurrentPrice( eCurrency ); + wchar_t wzLocalizedPrice[ kLocalizedPriceSizeInChararacters ]; + MakeMoneyString( wzLocalizedPrice, ARRAYSIZE( wzLocalizedPrice ), iTotalPrice, eCurrency ); + +#ifdef ENABLE_STORE_RENTAL_BACKEND + const wchar_t *pwsRentalPriceFormat = GLocalizationProvider()->Find( "#TF_Store_RentalPriceFormat" ); + if ( pEntry->IsRentable() && pwsRentalPriceFormat ) + { + wchar_t wzRentalLocalizedPrice[ kLocalizedPriceSizeInChararacters ]; + MakeMoneyString( wzRentalLocalizedPrice, ARRAYSIZE( wzRentalLocalizedPrice ), pEntry->GetRentalPriceScale() * iTotalPrice, eCurrency ) + + wchar_t wzLocalizedPriceString[96]; + ::ILocalize::ConstructString_safe( wzLocalizedPriceString, pwsRentalPriceFormat, 2, wzLocalizedPrice, wzRentalLocalizedPrice ); + m_pDetailsViewChild->SetDialogVariable( "price", wzLocalizedPriceString ); + } + else +#endif + { + if ( pEntry->m_bIsMarketItem ) + { + if ( iTotalPrice != 0 ) + { + wchar_t wzMarketString[96]; + g_pVGuiLocalize->ConstructString_safe( + wzMarketString, + LOCCHAR( "%s1 %s2" ), + 2, + g_pVGuiLocalize->Find( "#Store_StartingAt" ), + wzLocalizedPrice ); + + m_pDetailsViewChild->SetDialogVariable( "price", wzMarketString ); + } + else + { + m_pDetailsViewChild->SetDialogVariable( "price", "..." ); + } + } + else + { + // if market item. Prefix 'Starting at' + m_pDetailsViewChild->SetDialogVariable( "price", wzLocalizedPrice ); + } + } + } + + // Show/hide rental button. + for ( int i = 0; i < ARRAYSIZE( m_pAddRentalToCartButtons ); i++ ) + { + if ( m_pAddRentalToCartButtons[i] ) + { + m_pAddRentalToCartButtons[i]->SetVisible( pEntry && pEntry->IsRentable() ); + } + } + } + + // Final label value for our armory description text block. + const wchar_t *pwszLabelValue = L""; + m_bArmoryTextAdded = false; + + const char *pszArmoryDescString = pDef->GetArmoryDescString(); + if ( pszArmoryDescString ) + { + const ArmoryStringDict_t& ArmoryKeys = GetItemSchema()->GetArmoryDataItems(); + const ArmoryStringDict_t::IndexType_t armoryIndex = ArmoryKeys.Find( pszArmoryDescString ); + + if ( ArmoryKeys.IsValidIndex( armoryIndex ) ) + { + const char *pszArmoryDescLocalizationKey = ArmoryKeys[ armoryIndex ].Get(); + + pwszLabelValue = g_pVGuiLocalize->Find( pszArmoryDescLocalizationKey ); + m_bArmoryTextAdded = true; + } + } + + m_pDetailsViewChild->SetDialogVariable( "armory_text", pwszLabelValue ); + } + + // clear all old reference item panels before adding new ones + FOR_EACH_VEC( m_vecReferenceItemPanels, i ) + { + m_vecReferenceItemPanels[i]->MarkForDeletion(); + } + m_vecReferenceItemPanels.RemoveAll(); + int iReferenceItemHeight = 0; + + const CEconItemDescription *pDescription = m_pItemFullImage->GetItem()->GetDescription(); + if ( pDescription ) + { + TextImage *pAttributesTextImage = m_pAttributesLabel->GetTextImage(); // This pointer already verified above + pAttributesTextImage->ClearColorChangeStream(); + + const int kAttribBufferSize = 4 * 1024; + wchar_t wszAttribBuffer[ kAttribBufferSize ] = L""; + + int iAttribLine = 0; + m_nNumAttribLinesAdded = 0; + Color clrPrev( 0, 0, 0, 0 ); + uint32 unCurrentTextStreamIndex = 0; + IScheme *pScheme = scheme()->GetIScheme( GetScheme() ); + int iFontHeight = surface()->GetFontTall( m_pAttributesLabel->GetFont() ); + + if ( m_pItemCollectionHighlight ) + { + m_pItemCollectionHighlight->SetVisible( false ); + } + + iReferenceItemHeight = iFontHeight; + + int iAttributePanelX, iAttributePanelY; + m_pAttributesLabel->GetPos( iAttributePanelX, iAttributePanelY ); + + for ( uint32 i = 0; i < pDescription->GetLineCount(); ++i ) + { + const econ_item_description_line_t& line = pDescription->GetLine(i); + int nLineLength = StringFuncs<locchar_t>::Length( line.sText.Get() ); + if ( ( line.unMetaType & kDescLineFlag_Type ) != 0 ) + { + m_pDetailsViewChild->SetDialogVariable( "item_level_info", line.sText.Get() ); + } + else if ( ( line.unMetaType & kDescLineFlagSet_DisplayInAttributeBlock ) != 0 && m_pAttributesLabel ) + { + ++iAttribLine; + + // current collection item line + bool bIsCurrentCollectionItem = ( line.unMetaType & kDescLineFlag_CollectionCurrentItem ) != 0; + // use bg color as text color for current item for a better highlight + Color col = bIsCurrentCollectionItem ? Color( 0, 0, 0, 255 ) : pScheme->GetColor( GetColorNameForAttribColor( line.eColor ), Color( 255, 255, 255, 255 ) ); + + // Output a color change if necessary. + if ( i == 0 || clrPrev != col ) + { + pAttributesTextImage->AddColorChange( col, unCurrentTextStreamIndex ); + clrPrev = col; + } + + // Current line highlight + if ( bIsCurrentCollectionItem && m_pItemCollectionHighlight ) + { + // use text color as bg color for the current item for a better highlight + Color bgColor = pScheme->GetColor( GetColorNameForAttribColor( line.eColor ), Color( 255, 255, 255, 255 ) ); + + // Get the current ypos + int x, y; + m_pAttributesLabel->GetPos( x, y ); + m_pItemCollectionHighlight->SetPos( x, y + ( iAttribLine - 1 ) * iFontHeight ); + m_pItemCollectionHighlight->SetBgColor( bgColor ); + m_pItemCollectionHighlight->SetVisible( bIsCurrentCollectionItem ); + } + + if ( ( line.unMetaType & ( kDescLineFlag_Name | kDescLineFlag_Type ) ) == 0 ) + { + V_wcscat_safe( wszAttribBuffer, m_nNumAttribLinesAdded++ == 0 ? L"" : L"\n" ); // add empty lines everywhere except before the first line + V_wcscat_safe( wszAttribBuffer, line.sText.Get() ); + unCurrentTextStreamIndex += nLineLength + 1; // add one character to deal with newlines + } + + if ( line.unDefIndex != INVALID_ITEM_DEF_INDEX ) + { + // set text and recalculate the size now to compute for button pos + m_pAttributesLabel->SetText( wszAttribBuffer ); + m_pAttributesLabel->SizeToContents(); + + CItemModelPanel* pItemModelPanel = new CItemModelPanel( m_pAttributesLabel, CFmtStr( "reference_item_%d", m_vecReferenceItemPanels.Count() ) ); + pItemModelPanel->SetActAsButton( true, true ); + pItemModelPanel->SetAutoDelete( true ); + + pItemModelPanel->SetPos( 0, m_pAttributesLabel->GetTall() - iFontHeight ); + pItemModelPanel->SetZPos( m_pAttributesLabel->GetZPos() + 1 ); + + CEconItemView itemData; + itemData.Init( line.unDefIndex, AE_UNIQUE, AE_USE_SCRIPT_VALUE, true ); + itemData.SetClientItemFlags( kEconItemFlagClient_Preview ); + pItemModelPanel->SetItem( &itemData ); + pItemModelPanel->MakeFakeButton(); + + pItemModelPanel->SetTooltip( m_pMouseOverTooltip, "" ); + + m_vecReferenceItemPanels.AddToTail( pItemModelPanel ); + } + } + } + + // Make sure our string is NUL-terminated. + wszAttribBuffer[ kAttribBufferSize-1 ] = 0; + + m_pAttributesLabel->SetText( wszAttribBuffer ); + } + + // match the highlight width to attribute width + if ( m_pItemCollectionHighlight && m_pItemCollectionHighlight->IsVisible() ) + { + m_pAttributesLabel->SizeToContents(); + m_pItemCollectionHighlight->SetWide( m_pAttributesLabel->GetWide() ); + } + + if ( m_vecReferenceItemPanels.Count() ) + { + m_pAttributesLabel->SizeToContents(); + FOR_EACH_VEC( m_vecReferenceItemPanels, i ) + { + m_vecReferenceItemPanels[i]->SetSize( m_pAttributesLabel->GetWide(), iReferenceItemHeight ); + } + } + + // Get PerformLayout() called, now that we have text in our controls - without this, SizeToContents() sizes all the labels as tall and narrow. + InvalidateLayout( true ); + } + + // Set the visibility of the "Try it now!" button based on whether we as a client think this item should be able + // to be previewed. + const CEconStorePriceSheet *pPriceSheet = EconUI()->GetStorePanel()->GetPriceSheet(); + if ( pPriceSheet && m_pPreviewButton ) + { + const econ_store_entry_t *pStoreEntry = pPriceSheet->GetEntry( m_item.GetItemDefIndex() ); + m_pPreviewButton->SetVisible( pStoreEntry && pStoreEntry->CanPreview() ); + } + + m_pItemFullImage->InvalidateLayout(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CTFStorePreviewItemPanel2::SetState( preview_state_t iState ) +{ + BaseClass::SetState( iState ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CTFStorePreviewItemPanel2::UpdateIcons( void ) +{ + BaseClass::UpdateIcons(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CTFStorePreviewItemPanel2::UpdatePlayerModelButtons() +{ + BaseClass::UpdatePlayerModelButtons(); + + if ( m_pPlayerModelPanel ) + { + if ( m_pTeamNavPanel ) + { + m_pTeamNavPanel->SetVisible( m_pPlayerModelPanel->IsVisible() ); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CTFStorePreviewItemPanel2::SetPlayerModelVisible( bool bVisible ) +{ + BaseClass::SetPlayerModelVisible( bVisible ); + + if ( m_pCycleTextLabel ) + { + m_pCycleTextLabel->SetVisible( bVisible ); + } + + if ( m_pGoFullscreenButton ) + { + m_pGoFullscreenButton->SetVisible( bVisible ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CTFStorePreviewItemPanel2::SetCycleLabelText( vgui::Label *pTargetLabel, const char *pCycleText ) +{ + BaseClass::SetCycleLabelText( pTargetLabel, pCycleText ); + + if ( m_pCycleTextLabel ) + { + const wchar_t *pwszText = g_pVGuiLocalize->Find( pCycleText ); + m_pCycleTextLabel->SetText( pwszText ? pwszText : L"" ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CTFStorePreviewItemPanel2::OnNavButtonSelected( KeyValues *pData ) +{ + const int iTeam = pData->GetInt( "userdata", -1 ); AssertMsg( iTeam >= 0, "Bad filter" ); + if ( iTeam < 0 ) + return; + + if ( !m_pPlayerModelPanel ) + return; + + m_pPlayerModelPanel->SetTeam( iTeam ); + CyclePaint( false ); + + C_CTFGameStats::ImmediateWriteInterfaceEvent( "team_switch(store_preview_item_panel)", iTeam == TF_TEAM_RED ? "red" : "blu" ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CTFStorePreviewItemPanel2::OnExitFullscreen( KeyValues *pData ) +{ + if ( !m_pPlayerModelPanel ) + return; + + // If team or class changed in fullscreen mode, update our ui components here + if ( m_pTeamNavPanel ) + { + m_pTeamNavPanel->UpdateButtonSelectionStates( m_pPlayerModelPanel->GetTeam() == TF_TEAM_RED ? 0 : 1 ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CTFStorePreviewItemPanel2::OnTick( void ) +{ + BaseClass::OnTick(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CTFStorePreviewItemPanel2::OnMouseWheeled( int delta ) +{ + if ( !m_pScrollBar ) + return; + + int val = m_pScrollBar->GetValue(); + val -= (delta * 50); + m_pScrollBar->SetValue( val ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CTFStorePreviewItemPanel2::OnSliderMoved( int position ) +{ + m_iSliderPos = position; + UpdateScrollableChild(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CTFStorePreviewItemPanel2::OnThink() +{ + BaseClass::OnThink(); + + Assert( IsVisible() ); + + // If the user clicks outside of the dialog frame, close the preview, like + // many web sites do on the internet. + bool bMouseDown = vgui::input()->IsMouseDown( MOUSE_LEFT ); + + // User just clicked? + if ( !m_pFullscreenPanel || !m_pFullscreenPanel->IsFullscreenMode() ) + { + if ( !m_bMouseWasDown && bMouseDown ) + { + vgui::input()->GetCursorPos( m_aClickPos[0], m_aClickPos[1] ); + m_bMouseWasDown = true; + } + else if ( m_pDialogFrame && bMouseDown && !m_pDialogFrame->IsWithin( m_aClickPos[0], m_aClickPos[1] ) ) + { + //m_bCloseOnUp = true; + } + else if ( !bMouseDown ) + { + if ( m_bCloseOnUp ) + { + C_CTFGameStats::ImmediateWriteInterfaceEvent( "store_preview_item_panel", "close_from_outside_click" ); + + DoClose(); + } + m_bCloseOnUp = false; + m_bMouseWasDown = false; + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CTFStorePreviewItemPanel2::DoClose() +{ + if ( m_pFullscreenPanel ) + { + m_pFullscreenPanel->ExitFullscreen(); + } + + OnClose(); + + SetVisible( false ); +} diff --git a/game/client/tf/vgui/store/v2/tf_store_preview_item2.h b/game/client/tf/vgui/store/v2/tf_store_preview_item2.h new file mode 100644 index 0000000..05f0657 --- /dev/null +++ b/game/client/tf/vgui/store/v2/tf_store_preview_item2.h @@ -0,0 +1,177 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef TF_STORE_PREVIEW_ITEM2_H +#define TF_STORE_PREVIEW_ITEM2_H +#ifdef _WIN32 +#pragma once +#endif + +#include "store/tf_store_preview_item_base.h" + +namespace vgui +{ + class ScrollBar; +}; +class CNavigationPanel; +class CExLabel; + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +class CFullscreenStorePreviewItem : public EditablePanel +{ + DECLARE_CLASS_SIMPLE( CFullscreenStorePreviewItem, EditablePanel ); +public: + CFullscreenStorePreviewItem( vgui::Panel *pParent, EditablePanel *pOwner ); + + void SetItemDef( itemid_t iItemDef ); + + void GoFullscreen( CTFPlayerModelPanel *pPlayerModelPanel ); + void ExitFullscreen(); + bool IsFullscreenMode(); + +private: + MESSAGE_FUNC_PARAMS( OnNavButtonSelected, "NavButtonSelected", pData ); + + virtual void OnThink(); + virtual void ApplySchemeSettings( vgui::IScheme *pScheme ); + virtual void OnCommand( const char *command ); + + itemid_t m_iItemDef; + + CExLabel *m_pCycleTextLabel; + CNavigationPanel *m_pTeamNavPanel; + CExButton *m_pPreviewButton; + + struct ModelState_t + { + int m_aPlayerModelPanelBounds[4]; + Vector m_vecPlayerPos; + bool m_bZoomed; + } + m_OldModelState; + + struct Stats_t + { + Stats_t() { Clear(); } + void Clear() { V_memset( this, 0, sizeof( Stats_t ) ); } + + float m_flRotationTime; + } + m_Stats; + + float m_flGoFullscreenStartTime; + bool m_bIsHalloweenOrFullmoonOnlyItem; + vgui::DHANDLE< CTFPlayerModelPanel > m_pPlayerModelPanel; + + CExButton *m_pZoomButton; + CExButton *m_pRotLeftButton; + CExButton *m_pRotRightButton; + + EditablePanel *m_pOverlayPanel; + + PHandle m_hOwner; + + int m_nLastMouseX; + int m_nLastMouseY; + float m_flLastMouseMoveTime; + + CPanelAnimationVar( float, m_flFullscreenFadeToBlackDuration, "fullscreen_fade_to_black_duration", "1.0" ); + CPanelAnimationVar( float, m_flModelPanelOriginX, "fullscreen_modelpanel_origin_x", "170" ); + CPanelAnimationVar( float, m_flModelPanelOriginY, "fullscreen_modelpanel_origin_y", "0" ); + CPanelAnimationVar( float, m_flModelPanelOriginZ, "fullscreen_modelpanel_origin_z", "-36" ); + CPanelAnimationVar( float, m_flUiFadeoutTime, "ui_fadeout_time", "5.0" ); + CPanelAnimationVar( float, m_flUiFadeoutDuration, "ui_fadeout_duration", "1.0" ); +}; + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +class CTFStorePreviewItemPanel2 : public CTFStorePreviewItemPanelBase +{ + DECLARE_CLASS_SIMPLE( CTFStorePreviewItemPanel2, CTFStorePreviewItemPanelBase ); +public: + CTFStorePreviewItemPanel2( vgui::Panel *pParent, const char *pResFile, const char *pPanelName, CStorePage *pOwner ); + + virtual void PreviewItem( int iClass, CEconItemView *pItem, const econ_store_entry_t* pEntry=NULL ) OVERRIDE; + void PreviewItemCopy( int iClass, CEconItemView *pItem, const econ_store_entry_t* pEntry=NULL ); + virtual void SetState( preview_state_t iState ); + + MESSAGE_FUNC_PARAMS( OnClassIconSelected, "ClassIconSelected", data ); + MESSAGE_FUNC( OnHideClassIconMouseover, "HideClassIconMouseover" ); + MESSAGE_FUNC_PARAMS( OnShowClassIconMouseover, "ShowClassIconMouseover", data ); + +protected: + virtual void OnThink(); + virtual void ApplySchemeSettings( vgui::IScheme *pScheme ); + virtual void OnCommand( const char *command ); + virtual void PerformLayout( void ); + virtual void OnTick( void ); + virtual void OnMouseWheeled( int delta ); + + int PlaceControl( Panel *pParent, const char *pControlNameA, const char *pControlNameB, int nOffset, bool bVertical, + bool bSizeAToContents = true, bool bUseContentSize = true ); + void DoClose(); + void Clear(); + void UpdateScrollableChild(); + + virtual void SetPlayerModelVisible( bool bVisible ); + virtual void UpdateIcons( void ); + virtual void UpdatePlayerModelButtons( void ); + virtual void SetCycleLabelText( vgui::Label *pTargetLabel, const char *pCycleText ); + + MESSAGE_FUNC_PARAMS( OnNavButtonSelected, "NavButtonSelected", pData ); + MESSAGE_FUNC_PARAMS( OnExitFullscreen, "ExitFullscreen", pData ); + + Label *m_pLastNewLineControl; + EditablePanel *m_pDialogFrame; /// The background border + EditablePanel *m_pPreviewViewportBg; + CExLabel *m_pItemNameLabel; + CExLabel *m_pAttributesLabel; + vgui::EditablePanel *m_pItemCollectionHighlight; + CExLabel *m_pCycleTextLabel; + int m_nNumAttribLinesAdded; + bool m_bArmoryTextAdded; + EditablePanel *m_pDetailsView; + EditablePanel *m_pDetailsViewChild; + CExButton *m_pAddRentalToCartButtons[3]; + EditablePanel *m_pScrollableChild; + ScrollBar *m_pScrollBar; + int m_iSliderPos; + bool m_bCloseOnUp; + bool m_bMouseWasDown; + int m_aClickPos[2]; + CExButton *m_pItemWikiPageButton; + CNavigationPanel *m_pTeamNavPanel; + CExButton *m_pPreviewButton; + CExImageButton *m_pGoFullscreenButton; + int m_nViewMaxHeight; + + CFullscreenStorePreviewItem *m_pFullscreenPanel; + bool m_bIsHalloweenOrFullmoonOnlyItem; + + CEconItemView *m_pItemViewData; + CEconItem *m_pSOEconItemData; + + // mouse over reference item tooltip + CItemModelPanel *m_pMouseOverItemPanel; + CItemModelPanelToolTip *m_pMouseOverTooltip; + CUtlVector< CItemModelPanel* > m_vecReferenceItemPanels; + + CPanelAnimationVarAliasType( int, m_iSmallVerticalBreakSize, "small_vertical_break_size", "0", "proportional_ypos" ); + CPanelAnimationVarAliasType( int, m_iMediumVerticalBreakSize, "medium_vertical_break_size", "0", "proportional_ypos" ); + CPanelAnimationVarAliasType( int, m_iBigVerticalBreakSize, "big_vertical_break_size", "0", "proportional_ypos" ); + CPanelAnimationVarAliasType( int, m_iHorizontalBreakSize, "horizontal_break_size", "0", "proportional_xpos" ); + CPanelAnimationVarAliasType( int, m_iControlButtonWidth, "control_button_width", "0", "proportional_xpos" ); + CPanelAnimationVarAliasType( int, m_iControlButtonHeight, "control_button_height", "0", "proportional_ypos" ); + CPanelAnimationVarAliasType( int, m_iControlButtonY, "control_button_y", "0", "proportional_ypos" ); + + MESSAGE_FUNC_INT( OnSliderMoved, "ScrollBarSliderMoved", position ); +}; + +#endif // TF_STORE_PREVIEW_ITEM2_H |