//========= 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 //----------------------------------------------------------------------------- // 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( 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( FindChildByName( "OverlayPanel" ) ); m_pRotLeftButton = dynamic_cast( FindChildByName( "RotateLeftButton" ) ); m_pRotRightButton = dynamic_cast( FindChildByName( "RotateRightButton" ) ); m_pZoomButton = dynamic_cast( FindChildByName( "ZoomButton" ) ); m_pTeamNavPanel = dynamic_cast( 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( FindChildByName( "DialogFrame" ) ); m_pPreviewButton = dynamic_cast( FindChildByName( "TryItOutButton" ) ); m_pCycleTextLabel = dynamic_cast( FindChildByName( "CycleTextLabel" ) ); m_pGoFullscreenButton = dynamic_cast( FindChildByName( "GoFullscreenButton" ) ); COMPILE_TIME_ASSERT( ARRAYSIZE( m_pAddRentalToCartButtons ) == 3 ); #ifdef ENABLE_STORE_RENTAL_BACKEND m_pAddRentalToCartButtons[0] = dynamic_cast( FindChildByName( "AddRentalToCartButton_1Day" ) ); m_pAddRentalToCartButtons[1] = dynamic_cast( FindChildByName( "AddRentalToCartButton_3Day" ) ); m_pAddRentalToCartButtons[2] = dynamic_cast( FindChildByName( "AddRentalToCartButton_7Day" ) ); #endif if ( m_pDialogFrame ) { m_pPreviewViewportBg = dynamic_cast( m_pDialogFrame->FindChildByName( "PreviewViewportBg" ) ); m_pItemNameLabel = dynamic_cast( m_pDialogFrame->FindChildByName( "ItemNameLabel" ) ); m_pDetailsView = dynamic_cast( m_pDialogFrame->FindChildByName( "DetailsView" ) ); if ( m_pDetailsView ) { m_pDetailsViewChild = dynamic_cast( m_pDetailsView->FindChildByName( "ScrollableChild" ) ); if ( !m_pDetailsViewChild ) { m_pDetailsViewChild = m_pDetailsView; } m_pAttributesLabel = dynamic_cast( m_pDetailsViewChild->FindChildByName( "AttributesLabel" ) ); m_pItemCollectionHighlight = dynamic_cast( m_pDetailsViewChild->FindChildByName( "collectionhighlight" ) ); if ( m_pItemCollectionHighlight ) { m_pItemCollectionHighlight->InvalidateLayout( true, true ); } m_pItemWikiPageButton = dynamic_cast( m_pDetailsViewChild->FindChildByName( "ItemWikiPageButton" ) ); if ( m_pItemWikiPageButton ) { m_pItemWikiPageButton->AddActionSignalTarget( this ); } } } m_pTeamNavPanel = dynamic_cast( 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