Team Fortress 2 Source Code as on 22/4/2020
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

488 lines
16 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================//
  6. #include "cbase.h"
  7. #include "item_ad_panel.h"
  8. #include "econ_item_system.h"
  9. #include "item_model_panel.h"
  10. #include "econ_store.h"
  11. #include "econ_ui.h"
  12. #include "store/store_panel.h"
  13. #include "tf_controls.h"
  14. #include "econ_item_description.h"
  15. #include "vgui/IInput.h"
  16. // memdbgon must be the last include file in a .cpp file!!!
  17. #include <tier0/memdbgon.h>
  18. //-----------------------------------------------------------------------------
  19. // Purpose:
  20. //-----------------------------------------------------------------------------
  21. CBaseAdPanel::CBaseAdPanel( Panel *parent, const char *panelName )
  22. : BaseClass( parent, panelName )
  23. {}
  24. //-----------------------------------------------------------------------------
  25. // Purpose:
  26. //-----------------------------------------------------------------------------
  27. void CBaseAdPanel::ApplySettings( KeyValues *inResourceData )
  28. {
  29. BaseClass::ApplySettings( inResourceData );
  30. m_flPresentTime = inResourceData->GetFloat( "present_time", 10.f );
  31. }
  32. bool CBaseAdPanel::CheckForRequiredSteamComponents( const char* pszSteamRequried, const char* pszOverlayRequired )
  33. {
  34. // Make sure we've got the appropriate connections to Steam
  35. if ( !steamapicontext || !steamapicontext->SteamUtils() )
  36. {
  37. OpenStoreStatusDialog( NULL, pszSteamRequried, true, false );
  38. return false;
  39. }
  40. if ( !steamapicontext->SteamUtils()->IsOverlayEnabled() )
  41. {
  42. OpenStoreStatusDialog( NULL, pszOverlayRequired, true, false );
  43. return false;
  44. }
  45. return true;
  46. }
  47. //-----------------------------------------------------------------------------
  48. // Purpose:
  49. //-----------------------------------------------------------------------------
  50. CItemAdPanel::CItemAdPanel( Panel *parent, const char *panelName, item_definition_index_t itemDefIndex )
  51. : BaseClass( parent, panelName )
  52. , m_ItemDefIndex( itemDefIndex )
  53. , m_bShowMarketButton( true )
  54. {
  55. SetDialogVariable( "price", "..." );
  56. }
  57. //-----------------------------------------------------------------------------
  58. // Purpose:
  59. //-----------------------------------------------------------------------------
  60. void CItemAdPanel::ApplySchemeSettings( IScheme *pScheme )
  61. {
  62. BaseClass::ApplySchemeSettings( pScheme );
  63. LoadControlSettings( GetItemDef()->GetAdResFile() );
  64. }
  65. //-----------------------------------------------------------------------------
  66. // Purpose:
  67. //-----------------------------------------------------------------------------
  68. void CItemAdPanel::ApplySettings( KeyValues *inResourceData )
  69. {
  70. BaseClass::ApplySettings( inResourceData );
  71. m_bShowMarketButton = inResourceData->GetBool( "show_market", true ); // Default to showing market
  72. if ( !m_bShowMarketButton )
  73. {
  74. // Tick every second as we try to get our price from the store
  75. vgui::ivgui()->AddTickSignal( GetVPanel(), 1000 );
  76. }
  77. const CTFItemDefinition* pItemDef = GetItemDef();
  78. CItemModelPanel* pItemImage = FindControl< CItemModelPanel >( "ItemIcon" );
  79. if ( pItemImage )
  80. {
  81. CEconItemView adItem;
  82. adItem.Init( pItemDef->GetDefinitionIndex(), AE_UNIQUE, 1, 1 );
  83. pItemImage->InvalidateLayout( true, true );
  84. pItemImage->SetItem( &adItem );
  85. KeyValuesAD modelpanelKV( "modelpanel_kv" );
  86. KeyValues *itemKV = new KeyValues( "itemmodelpanel" );
  87. itemKV->SetBool( "inventory_image_type", true );
  88. itemKV->SetBool( "use_item_rendertarget", false );
  89. itemKV->SetBool( "allow_rot", false );
  90. modelpanelKV->AddSubKey( itemKV );
  91. pItemImage->ApplySettings( modelpanelKV );
  92. }
  93. }
  94. //-----------------------------------------------------------------------------
  95. // Purpose:
  96. //-----------------------------------------------------------------------------
  97. void CItemAdPanel::PerformLayout()
  98. {
  99. BaseClass::PerformLayout();
  100. const CTFItemDefinition* pItemDef = GetItemDef();
  101. // Get the ad text for the item. If it's not there, juse use the description text.
  102. SetDialogVariable( "item_name", g_pVGuiLocalize->Find( pItemDef->GetItemBaseName() ) );
  103. const char* pszAdtext = pItemDef->GetAdTextToken() ? pItemDef->GetAdTextToken() : pItemDef->GetItemDesc();
  104. CExScrollingEditablePanel* pScrollableItemText = FindControl< CExScrollingEditablePanel >( "ScrollableItemText", true );
  105. if ( pszAdtext && pScrollableItemText )
  106. {
  107. pScrollableItemText->SetDialogVariable( "item_ad_text", g_pVGuiLocalize->Find( pszAdtext ) );
  108. Label* pAdLabel = pScrollableItemText->FindControl< Label >( "ItemAdText", true );
  109. if ( pAdLabel )
  110. {
  111. int nWide, nTall;
  112. pAdLabel->GetContentSize( nWide, nTall );
  113. pAdLabel->SetTall( nTall );
  114. }
  115. pScrollableItemText->InvalidateLayout( true );
  116. }
  117. CExButton* pBuyButton = FindControl< CExButton >( "BuyButton", true );
  118. CExButton* pMarketButton = FindControl< CExButton >( "MarketButton", true );
  119. if ( pBuyButton && pMarketButton )
  120. {
  121. pBuyButton->SetVisible( !m_bShowMarketButton );
  122. pMarketButton->SetVisible( m_bShowMarketButton );
  123. }
  124. }
  125. //-----------------------------------------------------------------------------
  126. // Purpose:
  127. //-----------------------------------------------------------------------------
  128. void CItemAdPanel::OnTick()
  129. {
  130. const CTFItemDefinition* pItemDef = GetItemDef();
  131. bool bStoreIsReady = EconUI()->GetStorePanel() && EconUI()->GetStorePanel()->GetPriceSheet() && EconUI()->GetStorePanel()->GetCart() && steamapicontext && steamapicontext->SteamUser() && pItemDef;
  132. if ( bStoreIsReady )
  133. {
  134. // Get the price of the item
  135. const ECurrency eCurrency = EconUI()->GetStorePanel()->GetCurrency();
  136. const econ_store_entry_t *pEntry = EconUI()->GetStorePanel()->GetPriceSheet()->GetEntry( pItemDef->GetDefinitionIndex() );
  137. if ( pEntry )
  138. {
  139. item_price_t unPrice = pEntry->GetCurrentPrice( eCurrency );
  140. // Set that price into the button
  141. wchar_t wzLocalizedPrice[ kLocalizedPriceSizeInChararacters ];
  142. MakeMoneyString( wzLocalizedPrice, ARRAYSIZE( wzLocalizedPrice ), unPrice, eCurrency );
  143. SetDialogVariable( "price", wzLocalizedPrice );
  144. // Don't need to tick anymore
  145. vgui::ivgui()->RemoveTickSignal( GetVPanel() );
  146. }
  147. }
  148. }
  149. //-----------------------------------------------------------------------------
  150. // Purpose:
  151. //-----------------------------------------------------------------------------
  152. const CTFItemDefinition* CItemAdPanel::GetItemDef() const
  153. {
  154. return (CTFItemDefinition*)ItemSystem()->GetItemSchema()->GetItemDefinition( m_ItemDefIndex );
  155. }
  156. //-----------------------------------------------------------------------------
  157. // Purpose:
  158. //-----------------------------------------------------------------------------
  159. void CItemAdPanel::OnCommand( const char *command )
  160. {
  161. if ( FStrEq( "purchase", command ) )
  162. {
  163. if ( !CheckForRequiredSteamComponents( "#StoreUpdate_SteamRequired", "#MMenu_OverlayRequired" ) )
  164. return;
  165. const CTFItemDefinition* pItemDef = GetItemDef();
  166. if ( pItemDef )
  167. {
  168. if ( EconUI()->GetStorePanel() && EconUI()->GetStorePanel()->GetPriceSheet() && EconUI()->GetStorePanel()->GetCart() && steamapicontext && steamapicontext->SteamUser() )
  169. {
  170. // Add a the item to the users cart and checkout
  171. EconUI()->GetStorePanel()->GetCart()->EmptyCart();
  172. AddItemToCartHelper( NULL, pItemDef->GetDefinitionIndex(), kCartItem_Purchase );
  173. EconUI()->GetStorePanel()->InitiateCheckout( true );
  174. }
  175. }
  176. }
  177. else if ( FStrEq( "market", command ) )
  178. {
  179. if ( !CheckForRequiredSteamComponents( "#StoreUpdate_SteamRequired", "#MMenu_OverlayRequired" ) )
  180. return;
  181. const CTFItemDefinition* pItemDef = GetItemDef();
  182. if ( pItemDef && steamapicontext && steamapicontext->SteamFriends() )
  183. {
  184. const char *pszPrefix = "";
  185. if ( GetUniverse() == k_EUniverseBeta )
  186. {
  187. pszPrefix = "beta.";
  188. }
  189. static char pszItemName[256];
  190. g_pVGuiLocalize->ConvertUnicodeToANSI( g_pVGuiLocalize->Find ( pItemDef->GetItemBaseName() ) , pszItemName, sizeof(pszItemName) );
  191. char szURL[512];
  192. V_snprintf( szURL, sizeof(szURL), "http://%ssteamcommunity.com/market/listings/%d/%s", pszPrefix, engine->GetAppID(), pszItemName );
  193. steamapicontext->SteamFriends()->ActivateGameOverlayToWebPage( szURL );
  194. }
  195. }
  196. }
  197. DECLARE_BUILD_FACTORY( CCyclingAdContainerPanel );
  198. //-----------------------------------------------------------------------------
  199. // Purpose:
  200. //-----------------------------------------------------------------------------
  201. CCyclingAdContainerPanel::CCyclingAdContainerPanel( Panel *parent, const char *panelName )
  202. : BaseClass( parent, panelName )
  203. , m_pAdsContainer( NULL )
  204. , m_pKVItems( NULL )
  205. , m_nCurrentIndex( 0 )
  206. , m_nXPos( 0 )
  207. , m_nTargetIndex( 0 )
  208. , m_nTransitionStartOffsetX( 0 )
  209. , m_bTransitionRight( true )
  210. , m_bSettingsApplied( false )
  211. , m_bNeedsToCreatePanels( false )
  212. {
  213. m_pAdsContainer = new EditablePanel( this, "AdsContainer" );
  214. m_pFadePanel = new EditablePanel( this, "FadeTransition" );
  215. m_pNextButton = new CExButton( this, "NextButton", ">", this, "next" );
  216. m_pPrevButton = new CExButton( this, "PrevButton", "<", this, "prev" );
  217. }
  218. //-----------------------------------------------------------------------------
  219. // Purpose:
  220. //-----------------------------------------------------------------------------
  221. CCyclingAdContainerPanel::~CCyclingAdContainerPanel()
  222. {
  223. if ( m_pKVItems )
  224. {
  225. m_pKVItems->deleteThis();
  226. m_pKVItems = NULL;
  227. }
  228. }
  229. //-----------------------------------------------------------------------------
  230. // Purpose:
  231. //-----------------------------------------------------------------------------
  232. void CCyclingAdContainerPanel::ApplySchemeSettings( IScheme *pScheme )
  233. {
  234. BaseClass::ApplySchemeSettings( pScheme );
  235. LoadControlSettings( "Resource/UI/econ/CyclingAdContainer.res" );
  236. m_bSettingsApplied = true;
  237. }
  238. //-----------------------------------------------------------------------------
  239. // Purpose:
  240. //-----------------------------------------------------------------------------
  241. void CCyclingAdContainerPanel::ApplySettings( KeyValues *inResourceData )
  242. {
  243. BaseClass::ApplySettings( inResourceData );
  244. KeyValues* pKVItems = inResourceData->FindKey( "items" );
  245. if ( pKVItems )
  246. {
  247. SetItemKVs( pKVItems );
  248. }
  249. }
  250. //-----------------------------------------------------------------------------
  251. // Purpose:
  252. //-----------------------------------------------------------------------------
  253. void CCyclingAdContainerPanel::CreatePanels()
  254. {
  255. if ( ItemSystem()->GetItemSchema()->GetVersion() == 0 )
  256. return;
  257. m_vecPossibleAds.Purge();
  258. FOR_EACH_TRUE_SUBKEY( m_pKVItems, pKVItem )
  259. {
  260. const char* pszItemName = pKVItem->GetString( "item" );
  261. const CEconItemDefinition *pDef = ItemSystem()->GetItemSchema()->GetItemDefinitionByName( pszItemName );
  262. if ( pDef )
  263. {
  264. AdData_t& adData = m_vecPossibleAds[ m_vecPossibleAds.AddToTail() ];
  265. adData.m_pAdPanel = new CItemAdPanel( m_pAdsContainer, "ad", pDef->GetDefinitionIndex() );
  266. adData.m_pAdPanel->InvalidateLayout( true, true ); // Default settings
  267. adData.m_pAdPanel->ApplySettings( pKVItem );
  268. adData.m_pAdPanel->InvalidateLayout();
  269. }
  270. else
  271. {
  272. AssertMsg( 0, "Invalid item def '%s'!", pszItemName );
  273. }
  274. }
  275. m_bNeedsToCreatePanels = false;
  276. }
  277. //-----------------------------------------------------------------------------
  278. // Purpose:
  279. //-----------------------------------------------------------------------------
  280. void CCyclingAdContainerPanel::PerformLayout()
  281. {
  282. BaseClass::PerformLayout();
  283. PresentIndex( 0 );
  284. }
  285. //-----------------------------------------------------------------------------
  286. // Purpose:
  287. //-----------------------------------------------------------------------------
  288. void CCyclingAdContainerPanel::OnThink()
  289. {
  290. BaseClass::OnThink();
  291. if ( m_bNeedsToCreatePanels && m_bSettingsApplied )
  292. {
  293. CreatePanels();
  294. PresentIndex( 0 );
  295. }
  296. UpdateAdPanelPositions();
  297. // See if it's time to auto-cycle to the next ad
  298. if ( m_ShowTimer.HasStarted() && m_ShowTimer.IsElapsed() && m_vecPossibleAds.Count() > 1 )
  299. {
  300. m_ShowTimer.Invalidate();
  301. PresentIndex( m_nTargetIndex + 1 );
  302. }
  303. int nMouseX, nMouseY;
  304. vgui::input()->GetCursorPos( nMouseX, nMouseY );
  305. bool bControlsVisible = IsWithin( nMouseX, nMouseY ) && m_vecPossibleAds.Count() > 1;
  306. m_pPrevButton->SetVisible( bControlsVisible );
  307. m_pNextButton->SetVisible( bControlsVisible );
  308. }
  309. //-----------------------------------------------------------------------------
  310. // Purpose:
  311. //-----------------------------------------------------------------------------
  312. void CCyclingAdContainerPanel::SetItemKVs( KeyValues* pKVItems )
  313. {
  314. if ( pKVItems )
  315. {
  316. if ( m_pKVItems )
  317. {
  318. m_pKVItems->deleteThis();
  319. m_pKVItems = NULL;
  320. }
  321. m_pKVItems = pKVItems->MakeCopy();
  322. }
  323. m_bNeedsToCreatePanels = true;
  324. }
  325. //-----------------------------------------------------------------------------
  326. // Purpose:
  327. //-----------------------------------------------------------------------------
  328. void CCyclingAdContainerPanel::OnCommand( const char *command )
  329. {
  330. if ( FStrEq( "next", command ) )
  331. {
  332. PresentIndex( m_nTargetIndex + 1 );
  333. }
  334. else if ( FStrEq( "prev", command ) )
  335. {
  336. PresentIndex( m_nTargetIndex - 1 );
  337. }
  338. }
  339. //-----------------------------------------------------------------------------
  340. // Purpose:
  341. //-----------------------------------------------------------------------------
  342. void CCyclingAdContainerPanel::PresentIndex( int nIndex )
  343. {
  344. if ( m_vecPossibleAds.IsEmpty() )
  345. return;
  346. if ( m_nCurrentIndex == nIndex )
  347. return;
  348. // Figure out which way we want to ransition
  349. m_bTransitionRight = nIndex > m_nCurrentIndex;
  350. // Wrap if needed
  351. if ( nIndex >= m_vecPossibleAds.Count() )
  352. {
  353. nIndex = 0;
  354. }
  355. else if ( nIndex < 0 )
  356. {
  357. nIndex = m_vecPossibleAds.Count() - 1;
  358. }
  359. m_nTargetIndex = nIndex;
  360. // If they click more times while transitioning out, just change the target. If we're
  361. // into transitioning in to the next panel, then we need to start the whole thing over.
  362. if ( !IsTransitioningOut() )
  363. {
  364. m_nTransitionStartOffsetX = m_nXPos;
  365. float flTransitionTime = 1.f;
  366. m_TransitionTimer.Start( flTransitionTime );
  367. m_ShowTimer.Start( flTransitionTime + m_vecPossibleAds[ m_nCurrentIndex ].m_pAdPanel->GetPresentTime() );
  368. }
  369. }
  370. //-----------------------------------------------------------------------------
  371. // Purpose:
  372. //-----------------------------------------------------------------------------
  373. void CCyclingAdContainerPanel::UpdateAdPanelPositions()
  374. {
  375. // Figure out how far along a transition we are
  376. float flPercent = Clamp( m_TransitionTimer.GetElapsedTime() / m_TransitionTimer.GetCountdownDuration(), 0.f, 1.f );
  377. flPercent = Gain( flPercent, 0.8f );
  378. // At a certain point, we're no longer transitioning out the old -- we're transitioning in the new
  379. const float flTransitionCutOff = m_TransitionTimer.GetCountdownDuration() / 2.f;
  380. bool bTransitionOut = flPercent < flTransitionCutOff;
  381. int nStartX = 0;
  382. int nTargetX = 0;
  383. float flFadeAmount = 0.f;
  384. if ( bTransitionOut )
  385. {
  386. nStartX = m_nTransitionStartOffsetX;
  387. nTargetX = m_bTransitionRight ? -100 : 100;
  388. flFadeAmount = RemapValClamped( flPercent, 0.f, flTransitionCutOff * 0.75f, 0.f, 255.f );
  389. }
  390. else
  391. {
  392. // Once we've passed the middle, show the target
  393. m_nCurrentIndex = m_nTargetIndex;
  394. nStartX = m_bTransitionRight ? 100 : -100;
  395. nTargetX = 0;
  396. flFadeAmount = RemapValClamped( flPercent, flTransitionCutOff * 1.25f, 1.f, 255.f, 0.f );
  397. }
  398. // Alpha fades up entirely near the middle to cover the swap
  399. m_pFadePanel->SetAlpha( flFadeAmount );
  400. m_nXPos = RemapVal( flPercent, 0.f, 1.f, nStartX, nTargetX );
  401. FOR_EACH_VEC( m_vecPossibleAds, i )
  402. {
  403. m_vecPossibleAds[i].m_pAdPanel->SetPos( m_nXPos, m_vecPossibleAds[i].m_pAdPanel->GetYPos() );
  404. m_vecPossibleAds[i].m_pAdPanel->SetVisible( i == m_nCurrentIndex );
  405. }
  406. }
  407. float CCyclingAdContainerPanel::GetTransitionProgress() const
  408. {
  409. float flPercent = Clamp( m_TransitionTimer.GetElapsedTime() / m_TransitionTimer.GetCountdownDuration(), 0.f, 1.f );
  410. return Gain( flPercent, 0.8f );
  411. }
  412. bool CCyclingAdContainerPanel::IsTransitioningOut() const
  413. {
  414. const float flTransitionCutOff = m_TransitionTimer.GetCountdownDuration() / 2.f;
  415. return GetTransitionProgress() < flTransitionCutOff;
  416. }