//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: // //=============================================================================// #include "cbase.h" #include "item_ad_panel.h" #include "econ_item_system.h" #include "item_model_panel.h" #include "econ_store.h" #include "econ_ui.h" #include "store/store_panel.h" #include "tf_controls.h" #include "econ_item_description.h" #include "vgui/IInput.h" // memdbgon must be the last include file in a .cpp file!!! #include //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- CBaseAdPanel::CBaseAdPanel( Panel *parent, const char *panelName ) : BaseClass( parent, panelName ) {} //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CBaseAdPanel::ApplySettings( KeyValues *inResourceData ) { BaseClass::ApplySettings( inResourceData ); m_flPresentTime = inResourceData->GetFloat( "present_time", 10.f ); } bool CBaseAdPanel::CheckForRequiredSteamComponents( const char* pszSteamRequried, const char* pszOverlayRequired ) { // Make sure we've got the appropriate connections to Steam if ( !steamapicontext || !steamapicontext->SteamUtils() ) { OpenStoreStatusDialog( NULL, pszSteamRequried, true, false ); return false; } if ( !steamapicontext->SteamUtils()->IsOverlayEnabled() ) { OpenStoreStatusDialog( NULL, pszOverlayRequired, true, false ); return false; } return true; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- CItemAdPanel::CItemAdPanel( Panel *parent, const char *panelName, item_definition_index_t itemDefIndex ) : BaseClass( parent, panelName ) , m_ItemDefIndex( itemDefIndex ) , m_bShowMarketButton( true ) { SetDialogVariable( "price", "..." ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CItemAdPanel::ApplySchemeSettings( IScheme *pScheme ) { BaseClass::ApplySchemeSettings( pScheme ); LoadControlSettings( GetItemDef()->GetAdResFile() ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CItemAdPanel::ApplySettings( KeyValues *inResourceData ) { BaseClass::ApplySettings( inResourceData ); m_bShowMarketButton = inResourceData->GetBool( "show_market", true ); // Default to showing market if ( !m_bShowMarketButton ) { // Tick every second as we try to get our price from the store vgui::ivgui()->AddTickSignal( GetVPanel(), 1000 ); } const CTFItemDefinition* pItemDef = GetItemDef(); CItemModelPanel* pItemImage = FindControl< CItemModelPanel >( "ItemIcon" ); if ( pItemImage ) { CEconItemView adItem; adItem.Init( pItemDef->GetDefinitionIndex(), AE_UNIQUE, 1, 1 ); pItemImage->InvalidateLayout( true, true ); pItemImage->SetItem( &adItem ); KeyValuesAD modelpanelKV( "modelpanel_kv" ); KeyValues *itemKV = new KeyValues( "itemmodelpanel" ); itemKV->SetBool( "inventory_image_type", true ); itemKV->SetBool( "use_item_rendertarget", false ); itemKV->SetBool( "allow_rot", false ); modelpanelKV->AddSubKey( itemKV ); pItemImage->ApplySettings( modelpanelKV ); } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CItemAdPanel::PerformLayout() { BaseClass::PerformLayout(); const CTFItemDefinition* pItemDef = GetItemDef(); // Get the ad text for the item. If it's not there, juse use the description text. SetDialogVariable( "item_name", g_pVGuiLocalize->Find( pItemDef->GetItemBaseName() ) ); const char* pszAdtext = pItemDef->GetAdTextToken() ? pItemDef->GetAdTextToken() : pItemDef->GetItemDesc(); CExScrollingEditablePanel* pScrollableItemText = FindControl< CExScrollingEditablePanel >( "ScrollableItemText", true ); if ( pszAdtext && pScrollableItemText ) { pScrollableItemText->SetDialogVariable( "item_ad_text", g_pVGuiLocalize->Find( pszAdtext ) ); Label* pAdLabel = pScrollableItemText->FindControl< Label >( "ItemAdText", true ); if ( pAdLabel ) { int nWide, nTall; pAdLabel->GetContentSize( nWide, nTall ); pAdLabel->SetTall( nTall ); } pScrollableItemText->InvalidateLayout( true ); } CExButton* pBuyButton = FindControl< CExButton >( "BuyButton", true ); CExButton* pMarketButton = FindControl< CExButton >( "MarketButton", true ); if ( pBuyButton && pMarketButton ) { pBuyButton->SetVisible( !m_bShowMarketButton ); pMarketButton->SetVisible( m_bShowMarketButton ); } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CItemAdPanel::OnTick() { const CTFItemDefinition* pItemDef = GetItemDef(); bool bStoreIsReady = EconUI()->GetStorePanel() && EconUI()->GetStorePanel()->GetPriceSheet() && EconUI()->GetStorePanel()->GetCart() && steamapicontext && steamapicontext->SteamUser() && pItemDef; if ( bStoreIsReady ) { // Get the price of the item const ECurrency eCurrency = EconUI()->GetStorePanel()->GetCurrency(); const econ_store_entry_t *pEntry = EconUI()->GetStorePanel()->GetPriceSheet()->GetEntry( pItemDef->GetDefinitionIndex() ); if ( pEntry ) { item_price_t unPrice = pEntry->GetCurrentPrice( eCurrency ); // Set that price into the button wchar_t wzLocalizedPrice[ kLocalizedPriceSizeInChararacters ]; MakeMoneyString( wzLocalizedPrice, ARRAYSIZE( wzLocalizedPrice ), unPrice, eCurrency ); SetDialogVariable( "price", wzLocalizedPrice ); // Don't need to tick anymore vgui::ivgui()->RemoveTickSignal( GetVPanel() ); } } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- const CTFItemDefinition* CItemAdPanel::GetItemDef() const { return (CTFItemDefinition*)ItemSystem()->GetItemSchema()->GetItemDefinition( m_ItemDefIndex ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CItemAdPanel::OnCommand( const char *command ) { if ( FStrEq( "purchase", command ) ) { if ( !CheckForRequiredSteamComponents( "#StoreUpdate_SteamRequired", "#MMenu_OverlayRequired" ) ) return; const CTFItemDefinition* pItemDef = GetItemDef(); if ( pItemDef ) { if ( EconUI()->GetStorePanel() && EconUI()->GetStorePanel()->GetPriceSheet() && EconUI()->GetStorePanel()->GetCart() && steamapicontext && steamapicontext->SteamUser() ) { // Add a the item to the users cart and checkout EconUI()->GetStorePanel()->GetCart()->EmptyCart(); AddItemToCartHelper( NULL, pItemDef->GetDefinitionIndex(), kCartItem_Purchase ); EconUI()->GetStorePanel()->InitiateCheckout( true ); } } } else if ( FStrEq( "market", command ) ) { if ( !CheckForRequiredSteamComponents( "#StoreUpdate_SteamRequired", "#MMenu_OverlayRequired" ) ) return; const CTFItemDefinition* pItemDef = GetItemDef(); if ( pItemDef && steamapicontext && steamapicontext->SteamFriends() ) { const char *pszPrefix = ""; if ( GetUniverse() == k_EUniverseBeta ) { pszPrefix = "beta."; } static char pszItemName[256]; g_pVGuiLocalize->ConvertUnicodeToANSI( g_pVGuiLocalize->Find ( pItemDef->GetItemBaseName() ) , pszItemName, sizeof(pszItemName) ); char szURL[512]; V_snprintf( szURL, sizeof(szURL), "http://%ssteamcommunity.com/market/listings/%d/%s", pszPrefix, engine->GetAppID(), pszItemName ); steamapicontext->SteamFriends()->ActivateGameOverlayToWebPage( szURL ); } } } DECLARE_BUILD_FACTORY( CCyclingAdContainerPanel ); //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- CCyclingAdContainerPanel::CCyclingAdContainerPanel( Panel *parent, const char *panelName ) : BaseClass( parent, panelName ) , m_pAdsContainer( NULL ) , m_pKVItems( NULL ) , m_nCurrentIndex( 0 ) , m_nXPos( 0 ) , m_nTargetIndex( 0 ) , m_nTransitionStartOffsetX( 0 ) , m_bTransitionRight( true ) , m_bSettingsApplied( false ) , m_bNeedsToCreatePanels( false ) { m_pAdsContainer = new EditablePanel( this, "AdsContainer" ); m_pFadePanel = new EditablePanel( this, "FadeTransition" ); m_pNextButton = new CExButton( this, "NextButton", ">", this, "next" ); m_pPrevButton = new CExButton( this, "PrevButton", "<", this, "prev" ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- CCyclingAdContainerPanel::~CCyclingAdContainerPanel() { if ( m_pKVItems ) { m_pKVItems->deleteThis(); m_pKVItems = NULL; } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CCyclingAdContainerPanel::ApplySchemeSettings( IScheme *pScheme ) { BaseClass::ApplySchemeSettings( pScheme ); LoadControlSettings( "Resource/UI/econ/CyclingAdContainer.res" ); m_bSettingsApplied = true; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CCyclingAdContainerPanel::ApplySettings( KeyValues *inResourceData ) { BaseClass::ApplySettings( inResourceData ); KeyValues* pKVItems = inResourceData->FindKey( "items" ); if ( pKVItems ) { SetItemKVs( pKVItems ); } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CCyclingAdContainerPanel::CreatePanels() { if ( ItemSystem()->GetItemSchema()->GetVersion() == 0 ) return; m_vecPossibleAds.Purge(); FOR_EACH_TRUE_SUBKEY( m_pKVItems, pKVItem ) { const char* pszItemName = pKVItem->GetString( "item" ); const CEconItemDefinition *pDef = ItemSystem()->GetItemSchema()->GetItemDefinitionByName( pszItemName ); if ( pDef ) { AdData_t& adData = m_vecPossibleAds[ m_vecPossibleAds.AddToTail() ]; adData.m_pAdPanel = new CItemAdPanel( m_pAdsContainer, "ad", pDef->GetDefinitionIndex() ); adData.m_pAdPanel->InvalidateLayout( true, true ); // Default settings adData.m_pAdPanel->ApplySettings( pKVItem ); adData.m_pAdPanel->InvalidateLayout(); } else { AssertMsg( 0, "Invalid item def '%s'!", pszItemName ); } } m_bNeedsToCreatePanels = false; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CCyclingAdContainerPanel::PerformLayout() { BaseClass::PerformLayout(); PresentIndex( 0 ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CCyclingAdContainerPanel::OnThink() { BaseClass::OnThink(); if ( m_bNeedsToCreatePanels && m_bSettingsApplied ) { CreatePanels(); PresentIndex( 0 ); } UpdateAdPanelPositions(); // See if it's time to auto-cycle to the next ad if ( m_ShowTimer.HasStarted() && m_ShowTimer.IsElapsed() && m_vecPossibleAds.Count() > 1 ) { m_ShowTimer.Invalidate(); PresentIndex( m_nTargetIndex + 1 ); } int nMouseX, nMouseY; vgui::input()->GetCursorPos( nMouseX, nMouseY ); bool bControlsVisible = IsWithin( nMouseX, nMouseY ) && m_vecPossibleAds.Count() > 1; m_pPrevButton->SetVisible( bControlsVisible ); m_pNextButton->SetVisible( bControlsVisible ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CCyclingAdContainerPanel::SetItemKVs( KeyValues* pKVItems ) { if ( pKVItems ) { if ( m_pKVItems ) { m_pKVItems->deleteThis(); m_pKVItems = NULL; } m_pKVItems = pKVItems->MakeCopy(); } m_bNeedsToCreatePanels = true; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CCyclingAdContainerPanel::OnCommand( const char *command ) { if ( FStrEq( "next", command ) ) { PresentIndex( m_nTargetIndex + 1 ); } else if ( FStrEq( "prev", command ) ) { PresentIndex( m_nTargetIndex - 1 ); } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CCyclingAdContainerPanel::PresentIndex( int nIndex ) { if ( m_vecPossibleAds.IsEmpty() ) return; if ( m_nCurrentIndex == nIndex ) return; // Figure out which way we want to ransition m_bTransitionRight = nIndex > m_nCurrentIndex; // Wrap if needed if ( nIndex >= m_vecPossibleAds.Count() ) { nIndex = 0; } else if ( nIndex < 0 ) { nIndex = m_vecPossibleAds.Count() - 1; } m_nTargetIndex = nIndex; // If they click more times while transitioning out, just change the target. If we're // into transitioning in to the next panel, then we need to start the whole thing over. if ( !IsTransitioningOut() ) { m_nTransitionStartOffsetX = m_nXPos; float flTransitionTime = 1.f; m_TransitionTimer.Start( flTransitionTime ); m_ShowTimer.Start( flTransitionTime + m_vecPossibleAds[ m_nCurrentIndex ].m_pAdPanel->GetPresentTime() ); } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CCyclingAdContainerPanel::UpdateAdPanelPositions() { // Figure out how far along a transition we are float flPercent = Clamp( m_TransitionTimer.GetElapsedTime() / m_TransitionTimer.GetCountdownDuration(), 0.f, 1.f ); flPercent = Gain( flPercent, 0.8f ); // At a certain point, we're no longer transitioning out the old -- we're transitioning in the new const float flTransitionCutOff = m_TransitionTimer.GetCountdownDuration() / 2.f; bool bTransitionOut = flPercent < flTransitionCutOff; int nStartX = 0; int nTargetX = 0; float flFadeAmount = 0.f; if ( bTransitionOut ) { nStartX = m_nTransitionStartOffsetX; nTargetX = m_bTransitionRight ? -100 : 100; flFadeAmount = RemapValClamped( flPercent, 0.f, flTransitionCutOff * 0.75f, 0.f, 255.f ); } else { // Once we've passed the middle, show the target m_nCurrentIndex = m_nTargetIndex; nStartX = m_bTransitionRight ? 100 : -100; nTargetX = 0; flFadeAmount = RemapValClamped( flPercent, flTransitionCutOff * 1.25f, 1.f, 255.f, 0.f ); } // Alpha fades up entirely near the middle to cover the swap m_pFadePanel->SetAlpha( flFadeAmount ); m_nXPos = RemapVal( flPercent, 0.f, 1.f, nStartX, nTargetX ); FOR_EACH_VEC( m_vecPossibleAds, i ) { m_vecPossibleAds[i].m_pAdPanel->SetPos( m_nXPos, m_vecPossibleAds[i].m_pAdPanel->GetYPos() ); m_vecPossibleAds[i].m_pAdPanel->SetVisible( i == m_nCurrentIndex ); } } float CCyclingAdContainerPanel::GetTransitionProgress() const { float flPercent = Clamp( m_TransitionTimer.GetElapsedTime() / m_TransitionTimer.GetCountdownDuration(), 0.f, 1.f ); return Gain( flPercent, 0.8f ); } bool CCyclingAdContainerPanel::IsTransitioningOut() const { const float flTransitionCutOff = m_TransitionTimer.GetCountdownDuration() / 2.f; return GetTransitionProgress() < flTransitionCutOff; }