diff options
| author | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
|---|---|---|
| committer | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
| commit | 3bf9df6b2785fa6d951086978a3e66f49427166a (patch) | |
| tree | 2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /game/client/tf/vgui/store/v2 | |
| download | archived-source-engine-2018-hl2-src-master.tar.xz archived-source-engine-2018-hl2-src-master.zip | |
Diffstat (limited to 'game/client/tf/vgui/store/v2')
| -rw-r--r-- | game/client/tf/vgui/store/v2/tf_store_mapstamps_info_dialog.cpp | 83 | ||||
| -rw-r--r-- | game/client/tf/vgui/store/v2/tf_store_mapstamps_info_dialog.h | 36 | ||||
| -rw-r--r-- | game/client/tf/vgui/store/v2/tf_store_page2.cpp | 849 | ||||
| -rw-r--r-- | game/client/tf/vgui/store/v2/tf_store_page2.h | 82 | ||||
| -rw-r--r-- | game/client/tf/vgui/store/v2/tf_store_page_maps2.cpp | 59 | ||||
| -rw-r--r-- | game/client/tf/vgui/store/v2/tf_store_page_maps2.h | 35 | ||||
| -rw-r--r-- | game/client/tf/vgui/store/v2/tf_store_panel2.cpp | 109 | ||||
| -rw-r--r-- | game/client/tf/vgui/store/v2/tf_store_panel2.h | 41 | ||||
| -rw-r--r-- | game/client/tf/vgui/store/v2/tf_store_preview_item2.cpp | 1367 | ||||
| -rw-r--r-- | game/client/tf/vgui/store/v2/tf_store_preview_item2.h | 177 |
10 files changed, 2838 insertions, 0 deletions
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 |