summaryrefslogtreecommitdiff
path: root/game/client/tf/vgui/quest_item_panel.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'game/client/tf/vgui/quest_item_panel.cpp')
-rw-r--r--game/client/tf/vgui/quest_item_panel.cpp1625
1 files changed, 1625 insertions, 0 deletions
diff --git a/game/client/tf/vgui/quest_item_panel.cpp b/game/client/tf/vgui/quest_item_panel.cpp
new file mode 100644
index 0000000..cfa05fe
--- /dev/null
+++ b/game/client/tf/vgui/quest_item_panel.cpp
@@ -0,0 +1,1625 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#include "cbase.h"
+#include "quest_item_panel.h"
+#include "tf_hud_item_progress_tracker.h"
+#include "quest_log_panel.h"
+#include "c_tf_player.h"
+#include "econ_item_description.h"
+#include "clientmode_tf.h"
+#include <vgui_controls/AnimationController.h>
+#include "quest_objective_manager.h"
+#include "econ_quests.h"
+#include "confirm_dialog.h"
+#include "tf_quest_restriction.h"
+#include "item_model_panel.h"
+#include "tf_gc_client.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include <tier0/memdbgon.h>
+
+void AddSubKeyNamed( KeyValues *pKeys, const char *pszName );
+
+const float k_flQuestDecodeTime = 2.f;
+const float k_flQuestTurnInTime = 5.f;
+ConVar tf_quest_turn_in_confirm_opt_out( "tf_quest_turn_in_confirm_opt_out", "0", FCVAR_ARCHIVE, "If nonzero, don't confirm submitting a contract that does not have all of the bonus points" );
+
+extern CQuestLogPanel *GetQuestLog();
+extern CQuestTooltip* g_spTextTooltip;
+
+extern const char *s_pszMMTypes[kMatchmakingTypeCount];
+extern const char *s_pszGameModes[eNumGameCategories];
+
+void SelectGroup( EMatchmakingGroupType eGroup, bool bSelected );
+void SelectCategory( EGameCategory eCategory, bool bSelected );
+
+void PromptOrFireCommand( const char* pszCommand );
+
+static void ConfirmDiscardQuest( bool bConfirmed, void* pContext )
+{
+ CQuestItemPanel *pQuestItemPanel = ( CQuestItemPanel* )pContext;
+ if ( pQuestItemPanel )
+ {
+ pQuestItemPanel->OnConfirmDelete( bConfirmed );
+ }
+}
+
+static void ConfirmEquipLoaners( bool bConfirmed, void* pContext )
+{
+ CQuestItemPanel *pQuestItemPanel = ( CQuestItemPanel* )pContext;
+ if ( pQuestItemPanel )
+ {
+ pQuestItemPanel->OnConfirmEquipLoaners( bConfirmed );
+ }
+}
+
+static void ConfirmTurnInQuest( bool bConfirmed, void* pContext )
+{
+ if ( bConfirmed )
+ {
+ CQuestItemPanel *pQuestItemPanel = (CQuestItemPanel*)pContext;
+ if ( pQuestItemPanel )
+ {
+ pQuestItemPanel->OnCompleteQuest();
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: fill vecLoanerItems with loaners def indices from pQuest
+//-----------------------------------------------------------------------------
+static int GetLoanerListFromQuest( const CEconItemView *pQuest, CUtlVector< item_definition_index_t >& vecLoanerItems )
+{
+ if ( !pQuest )
+ return 0;
+
+ // loaners from the quest
+ const CUtlVector< CTFRequiredQuestItemsSet >& vecQuestRequiredItems = pQuest->GetItemDefinition()->GetQuestDef()->GetRequiredItemSets();
+ FOR_EACH_VEC( vecQuestRequiredItems, i )
+ {
+ // don't add dups
+ if ( vecLoanerItems.Find( vecQuestRequiredItems[i].GetLoanerItemDef() ) == vecLoanerItems.InvalidIndex() )
+ {
+ vecLoanerItems.AddToTail( vecQuestRequiredItems[i].GetLoanerItemDef() );
+ }
+ }
+
+ // loaners from the objectives
+ //{
+ // // Get all the objectives
+ // QuestObjectiveDefVec_t vecChosenObjectives;
+ // pQuest->GetItemDefinition()->GetQuestDef()->GetRolledObjectivesForItem( vecChosenObjectives, pQuest );
+
+ // // Get all the items we need to give as loaners from the objectives
+ // FOR_EACH_VEC( vecChosenObjectives, i )
+ // {
+ // const CUtlVector< CTFRequiredQuestItemsSet >& vecObjectiveRequiredItems = vecChosenObjectives[ i ]->GetConditions()->GetRequiredItemSets();
+ // FOR_EACH_VEC( vecObjectiveRequiredItems, iRequired )
+ // {
+ // // don't add dups
+ // if ( vecLoanerItems.Find( vecObjectiveRequiredItems[ iRequired ].GetLoanerItemDef() ) == vecLoanerItems.InvalidIndex() )
+ // {
+ // vecLoanerItems.AddToTail( vecObjectiveRequiredItems[ iRequired ].GetLoanerItemDef() );
+ // }
+ // }
+ // }
+ //}
+
+ return vecLoanerItems.Count();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: fill vecGrantedLoaners with granted loaners from specific quest ID
+//-----------------------------------------------------------------------------
+static int GetLoanersFromLocalInventory( const itemid_t& questID, const CUtlVector< item_definition_index_t >& vecLoanerItems, CUtlVector< CEconItemView* >& vecGrantedLoaners )
+{
+ if ( vecLoanerItems.Count() > 0 )
+ {
+ CPlayerInventory *pLocalInv = InventoryManager()->GetLocalInventory();
+ int nCount = pLocalInv->GetItemCount();
+ for ( int i = 0; i < nCount; ++i )
+ {
+ CEconItemView* pItem = pLocalInv->GetItem( i );
+
+ bool bIsLoaner = false;
+ // check if the item is a loaner and is associated with this quest
+ FOR_EACH_VEC( vecLoanerItems, iLoaner )
+ {
+ if ( vecLoanerItems[iLoaner] == pItem->GetItemDefIndex() && GetAssociatedQuestItemID( pItem ) == questID )
+ {
+ bIsLoaner = true;
+ break;
+ }
+ }
+
+ // already granted this loaner, remove from the list to give
+ if ( bIsLoaner )
+ {
+ vecGrantedLoaners.AddToTail( pItem );
+ }
+
+ // found all given loaners
+ if ( vecLoanerItems.Count() == vecGrantedLoaners.Count() )
+ {
+ break;
+ }
+ }
+ }
+
+ return vecGrantedLoaners.Count();
+}
+
+
+DECLARE_BUILD_FACTORY( CInputProxyPanel )
+CInputProxyPanel::CInputProxyPanel( Panel *parent, const char *pszPanelName )
+ : BaseClass( parent, pszPanelName )
+{}
+
+void CInputProxyPanel::AddPanelForCommand( EInputTypes eInputType, Panel* pPanel, const char* pszCommand )
+{
+ m_vecRedirectPanels[ eInputType ].AddToTail( { pPanel, pszCommand } );
+}
+
+void CInputProxyPanel::OnCursorMoved( int x, int y )
+{
+ FOR_EACH_VEC( m_vecRedirectPanels[ INPUT_MOUSE_MOVE ], i )
+ {
+ PostMessage( m_vecRedirectPanels[ INPUT_MOUSE_MOVE ][ i ].m_pPanel, new KeyValues( m_vecRedirectPanels[ INPUT_MOUSE_MOVE ][ i ].m_pszCommand, "x", x, "y", y ) );
+ }
+}
+
+void CInputProxyPanel::OnCursorEntered()
+{
+ FOR_EACH_VEC( m_vecRedirectPanels[ INPUT_MOUSE_ENTER ], i )
+ {
+ PostMessage( m_vecRedirectPanels[ INPUT_MOUSE_ENTER ][ i ].m_pPanel, new KeyValues( m_vecRedirectPanels[ INPUT_MOUSE_ENTER ][ i ].m_pszCommand ) );
+ }
+}
+
+void CInputProxyPanel::OnCursorExited()
+{
+ FOR_EACH_VEC( m_vecRedirectPanels[ INPUT_MOUSE_EXIT ], i )
+ {
+ PostMessage( m_vecRedirectPanels[ INPUT_MOUSE_EXIT ][ i ].m_pPanel, new KeyValues( m_vecRedirectPanels[ INPUT_MOUSE_EXIT ][ i ].m_pszCommand ) );
+ }
+}
+
+void CInputProxyPanel::OnMousePressed(MouseCode code)
+{
+ FOR_EACH_VEC( m_vecRedirectPanels[ INPUT_MOUSE_PRESS ], i )
+ {
+ PostMessage( m_vecRedirectPanels[ INPUT_MOUSE_PRESS ][ i ].m_pPanel, new KeyValues( m_vecRedirectPanels[ INPUT_MOUSE_PRESS ][ i ].m_pszCommand, "code", code ) );
+ }
+}
+
+void CInputProxyPanel::OnMouseDoublePressed(MouseCode code)
+{
+ FOR_EACH_VEC( m_vecRedirectPanels[ INPUT_MOUSE_DOUBLE_PRESS ], i )
+ {
+ PostMessage( m_vecRedirectPanels[ INPUT_MOUSE_DOUBLE_PRESS ][ i ].m_pPanel, new KeyValues( m_vecRedirectPanels[ INPUT_MOUSE_DOUBLE_PRESS ][ i ].m_pszCommand, "code", code ) );
+ }
+}
+
+void CInputProxyPanel::OnMouseReleased(MouseCode code)
+{
+ FOR_EACH_VEC( m_vecRedirectPanels[ INPUT_MOUSE_RELEASED ], i )
+ {
+ PostMessage( m_vecRedirectPanels[ INPUT_MOUSE_RELEASED ][ i ].m_pPanel, new KeyValues( m_vecRedirectPanels[ INPUT_MOUSE_RELEASED ][ i ].m_pszCommand, "code", code ) );
+ }
+}
+
+void CInputProxyPanel::OnMouseWheeled(int delta)
+{
+ FOR_EACH_VEC( m_vecRedirectPanels[ INPUT_MOUSE_WHEEL ], i )
+ {
+ PostMessage( m_vecRedirectPanels[ INPUT_MOUSE_WHEEL ][ i ].m_pPanel, new KeyValues( m_vecRedirectPanels[ INPUT_MOUSE_WHEEL ][ i ].m_pszCommand, "delta", delta ) );
+ }
+}
+
+
+DECLARE_BUILD_FACTORY( CQuestStatusPanel )
+
+CQuestStatusPanel::CQuestStatusPanel( Panel *parent, const char *pszPanelName )
+ : EditablePanel( parent, pszPanelName )
+ , m_pMovingContainer( NULL )
+ , m_bShouldBeVisible( false )
+{
+ m_pMovingContainer = new EditablePanel( this, "movingcontainer" );
+ m_transitionTimer.Invalidate();
+}
+
+void CQuestStatusPanel::SetShow( bool bShow )
+{
+ if ( bShow != m_bShouldBeVisible )
+ {
+ m_transitionTimer.Start( 0.6f );
+ }
+ m_bShouldBeVisible = bShow;
+ SetVisible( m_bShouldBeVisible );
+}
+
+void CQuestStatusPanel::OnThink()
+{
+ BaseClass::OnThink();
+
+ const int nStartY = m_bShouldBeVisible ? m_iHiddenY : m_iVisibleY;
+ const int nEndY = m_bShouldBeVisible ? m_iVisibleY : m_iHiddenY;
+
+ float flProgress = 1.f;
+ if ( !m_transitionTimer.IsElapsed() )
+ {
+ flProgress = Bias( RemapValClamped( m_transitionTimer.GetElapsedTime(), 0.f , m_transitionTimer.GetCountdownDuration(), 0.f, 1.f ), 0.7f );
+ }
+ flProgress = RemapVal( flProgress, 0.f, 1.f, (float)nStartY, (float)nEndY );
+
+ m_pMovingContainer->SetPos( m_pMovingContainer->GetXPos(), flProgress );
+ SetVisible( m_bShouldBeVisible || flProgress > 0.f );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CQuestItemPanel::CQuestItemPanel( Panel *parent, const char *pszPanelName, CEconItemView* pQuestItem, CScrollableQuestList* pQuestList )
+ : EditablePanel( parent, pszPanelName )
+ , m_hQuestItem( NULL )
+ , m_eState( STATE_NORMAL )
+ , m_pTurnInContainer( NULL )
+ , m_pTurnInDimmer( NULL )
+ , m_pszCompleteSound( NULL )
+ , m_pFrontFolderContainer( NULL )
+ , m_pBackFolderContainer( NULL )
+ , m_bCollapsed( true )
+ , m_pQuestList( pQuestList )
+ , m_pQuestPaperContainer( NULL )
+ , m_pTitleButton( NULL )
+ , m_pIdentifyContainer( NULL )
+ , m_pIdentifyDimmer( NULL )
+ , m_pKVCipherStrings( NULL )
+ , m_pPhotoStatic( NULL )
+ , m_pFlavorScrollingContainer( NULL )
+ , m_pTurningInLabel( NULL )
+ , m_pFindServerButton( NULL )
+ , m_pLoanerContainerPanel( NULL )
+ , m_pRequestLoanerItemsButton( NULL )
+ , m_pEquipLoanerItemsButton( NULL )
+ , m_pItemTrackerPanel( NULL )
+ , m_pKVItemTracker( NULL )
+ , m_pObjectiveExplanationLabel( NULL )
+ , m_pEncodedStatus( NULL )
+ , m_pInactiveStatus( NULL )
+ , m_pReadyToTurnInStatus( NULL )
+ , m_pExpirationLabel( NULL )
+ , m_pTurnInButton( NULL )
+ , m_bHasAllControls( false )
+ , m_pDiscardButton( NULL )
+{
+ SetItem( pQuestItem );
+ m_StateTimer.Invalidate();
+
+ ListenForGameEvent( "quest_objective_completed" );
+ ListenForGameEvent( "player_spawn" );
+ ListenForGameEvent( "client_disconnect" );
+ ListenForGameEvent( "inventory_updated" );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CQuestItemPanel::~CQuestItemPanel()
+{
+ if ( m_pItemTrackerPanel )
+ {
+ m_pItemTrackerPanel->MarkForDeletion();
+ m_pItemTrackerPanel = NULL;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CQuestItemPanel::ApplySchemeSettings( IScheme *pScheme )
+{
+ BaseClass::ApplySchemeSettings ( pScheme );
+ AddActionSignalTarget( GetQuestLog() );
+ LoadResFileForCurrentItem();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CQuestItemPanel::LoadResFileForCurrentItem()
+{
+ tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s", __FUNCTION__ );
+ const char *pszResFile = "Resource/UI/quests/QuestItemPanel_Base.res";
+
+ if ( m_hQuestItem )
+ {
+ const GameItemDefinition_t *pItemDef = m_hQuestItem->GetItemDefinition();
+ // Get our quest theme
+ const CQuestThemeDefinition *pTheme = pItemDef->GetQuestDef()->GetQuestTheme();
+ if ( pTheme )
+ {
+ pszResFile = pTheme->GetQuestItemResFile();
+ }
+ }
+
+ KeyValues *pConditions = new KeyValues( "conditions" );
+ if ( pConditions )
+ {
+ char uilanguage[64];
+ uilanguage[0] = 0;
+ engine->GetUILanguage( uilanguage, sizeof( uilanguage ) );
+ char szCondition[64];
+ Q_snprintf( szCondition, sizeof( szCondition ), "if_%s", uilanguage );
+ AddSubKeyNamed( pConditions, szCondition );
+ }
+
+ SetMouseInputEnabled( true ); // Slam this to true. When panels get created, they'll inherit their parents' mouse enabled state
+ // and if we've been fiddling with it, we might accidently create all child panels with mouse input disabled.
+ // Setting this to true just before the controls are made gives them a chance to be mouse enabled if they want.
+ LoadControlSettings( pszResFile, NULL, NULL, pConditions );
+ g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( this, m_strReset );
+
+ m_pMainContainer = FindControl<EditablePanel>( "MainContainer" );
+ if ( m_pMainContainer )
+ {
+ m_pMainContainer->AddActionSignalTarget( this );
+ }
+
+ m_pQuestPaperContainer = FindControl<EditablePanel>( "QuestPaperContainer", true );
+ m_pFrontFolderContainer = FindControl<EditablePanel>( "FrontFolderContainer", true );
+ Assert( m_pFrontFolderContainer );
+ if ( m_pFrontFolderContainer )
+ {
+ m_pFrontFolderImage = m_pFrontFolderContainer->FindControl<ImagePanel>( "FrontFolderImage", true );
+ Assert( m_pFrontFolderImage );
+
+ m_pEncodedStatus = m_pFrontFolderContainer->FindControl< CQuestStatusPanel >( "EncodedStatus", true );
+ m_pInactiveStatus = m_pFrontFolderContainer->FindControl< CQuestStatusPanel >( "InactiveStatus", true );
+ m_pReadyToTurnInStatus = m_pFrontFolderContainer->FindControl< CQuestStatusPanel >( "ReadyToTurnInStatus", true );
+ }
+ m_pBackFolderContainer = FindControl<EditablePanel>( "BackFolderContainer", true );
+ Assert( m_pBackFolderContainer );
+ if ( m_pBackFolderContainer )
+ {
+ m_pBackFolderImage = m_pBackFolderContainer->FindControl<ImagePanel>( "BackFolderImage", true );
+ Assert( m_pBackFolderImage );
+ }
+
+ if ( m_pQuestPaperContainer )
+ {
+#if defined( STAGING_ONLY ) || defined( DEBUG )
+ // don't do this in public
+ m_pDiscardButton = new CExButton( m_pQuestPaperContainer, "Discard", "Discard", this, "discard_quest" );
+ m_pDiscardButton->SetEnabled( true );
+ m_pDiscardButton->SizeToContents();
+ m_pDiscardButton->SetZPos( 101 );
+ m_pDiscardButton->SetPos( 70, 40 );
+ m_pDiscardButton->SetVisible( false );
+#endif // STAGING_ONLY || DEBUG
+
+ m_pFindServerButton = m_pQuestPaperContainer->FindControl< CExButton >( "FindServerButton", true );
+
+ m_pLoanerContainerPanel = m_pQuestPaperContainer->FindControl< EditablePanel >( "LoanerContainerPanel", true );
+ if ( m_pLoanerContainerPanel )
+ {
+ m_pRequestLoanerItemsButton = m_pLoanerContainerPanel->FindControl< CExButton >( "RequestLoanerItemsButton", true );
+ m_pEquipLoanerItemsButton = m_pLoanerContainerPanel->FindControl< CExButton >( "EquipLoanerItemsButton", true );
+ for ( int i = 0; i < ARRAYSIZE( m_pLoanerItemModelPanel ); ++i )
+ {
+ m_pLoanerItemModelPanel[i] = m_pLoanerContainerPanel->FindControl< CItemModelPanel >( CFmtStr( "Loaner%dItemModelPanel", i + 1 ), true );
+ }
+ }
+
+ m_pTitleButton = m_pQuestPaperContainer->FindControl<CExButton>( "TitleButton", true );
+ m_pIdentifyContainer = m_pQuestPaperContainer->FindControl<EditablePanel>( "IdentifyButtonContainer", true );
+ if ( m_pIdentifyContainer )
+ {
+ m_pIdentifyDimmer = m_pIdentifyContainer->FindControl<EditablePanel>( "Dimmer", true );
+ m_pIdentifyButton = m_pIdentifyContainer->FindControl<CExButton>( "IdentifyButton", true );
+ }
+ Assert( m_pIdentifyContainer );
+
+ m_pEncodedImage = m_pQuestPaperContainer->FindControl<ImagePanel>( "EncodedImage", true );
+
+ m_pPhotoStatic = m_pQuestPaperContainer->FindControl<ImagePanel>( "StaticPhoto", true );
+ Assert( m_pPhotoStatic );
+
+ m_pFlavorScrollingContainer = m_pQuestPaperContainer->FindControl<CExScrollingEditablePanel>( "ScrollableBottomContainer", true );
+ Assert( m_pFlavorScrollingContainer );
+
+ if ( m_pFlavorScrollingContainer )
+ {
+ m_pObjectiveExplanationLabel = m_pFlavorScrollingContainer->FindControl< Label >( "QuestObjectiveExplanation", true );
+ }
+
+ CInputProxyPanel* pInputProxy = m_pQuestPaperContainer->FindControl< CInputProxyPanel >( "PaperInputProxyPanel", true );
+ if ( pInputProxy )
+ {
+ // Make the scroller scroll
+ pInputProxy->AddPanelForCommand( CInputProxyPanel::INPUT_MOUSE_WHEEL, m_pFlavorScrollingContainer, "MouseWheeled" );
+
+ // Make the title glow
+ pInputProxy->AddPanelForCommand( CInputProxyPanel::INPUT_MOUSE_ENTER, m_pTitleButton, "CursorEntered" );
+ pInputProxy->AddPanelForCommand( CInputProxyPanel::INPUT_MOUSE_EXIT, m_pTitleButton, "CursorExited" );
+
+ // Capture clicks to expand/contract
+ pInputProxy->AddPanelForCommand( CInputProxyPanel::INPUT_MOUSE_RELEASED, this, "MouseReleased" );
+ }
+
+ m_pTurnInContainer = m_pQuestPaperContainer->FindControl< EditablePanel >( "TurnInContainer" );
+ Assert( m_pTurnInContainer );
+ if ( m_pTurnInContainer )
+ {
+ m_pTurnInDimmer = m_pTurnInContainer->FindControl< EditablePanel >( "Dimmer", true );
+ Assert( m_pTurnInContainer );
+
+ m_pTurnInButton = m_pTurnInContainer->FindControl< Button >( "TurnInButton", true );
+ Assert( m_pTurnInButton );
+
+ m_pTurnInSpinnerContainer = m_pTurnInContainer->FindControl< EditablePanel>( "TurnInSpinnerContainer", true );
+ Assert( m_pTurnInSpinnerContainer );
+
+ if ( m_pTurnInSpinnerContainer )
+ {
+ m_pTurningInLabel = m_pTurnInSpinnerContainer->FindControl< Label >( "TurningInLabel", true );
+ Assert( m_pTurningInLabel );
+ }
+ }
+
+ m_pAcceptedImage = m_pQuestPaperContainer->FindControl< ImagePanel >( "AcceptedImage", true );
+ Assert( m_pAcceptedImage );
+ }
+
+ if ( m_pFrontFolderContainer )
+ {
+ CInputProxyPanel* pInputProxy = m_pFrontFolderContainer->FindControl< CInputProxyPanel >( "FrontInputProxyPanel", true );
+ if ( pInputProxy )
+ {
+ pInputProxy->AddPanelForCommand( CInputProxyPanel::INPUT_MOUSE_ENTER, m_pInactiveStatus, "CursorEntered" );
+ pInputProxy->AddPanelForCommand( CInputProxyPanel::INPUT_MOUSE_EXIT, m_pInactiveStatus, "CursorExited" );
+
+ // Make the title glow
+ pInputProxy->AddPanelForCommand( CInputProxyPanel::INPUT_MOUSE_ENTER, m_pTitleButton, "CursorEntered" );
+ pInputProxy->AddPanelForCommand( CInputProxyPanel::INPUT_MOUSE_EXIT, m_pTitleButton, "CursorExited" );
+
+ // Make the backdrop highlight
+ pInputProxy->AddPanelForCommand( CInputProxyPanel::INPUT_MOUSE_ENTER, this, "CollapsedGlowStart" );
+ pInputProxy->AddPanelForCommand( CInputProxyPanel::INPUT_MOUSE_EXIT, this, "CollapsedGlowEnd" );
+
+ // Capture clicks to expand/contract
+ pInputProxy->AddPanelForCommand( CInputProxyPanel::INPUT_MOUSE_RELEASED, this, "MouseReleased" );
+ }
+ }
+
+ m_pExpirationLabel = FindControl<Label>( "QuestExpirationWarning", true );
+ m_pFlavorText = FindControl<Label>( "QuestFlavorText", true );
+
+ SetupObjectivesPanels( true );
+
+ if ( pConditions )
+ {
+ pConditions->deleteThis();
+ }
+
+ m_bHasAllControls = m_pQuestPaperContainer
+ && m_pFrontFolderContainer
+ && m_pFrontFolderImage
+ && m_pBackFolderContainer
+ && m_pBackFolderImage
+ && m_pEncodedStatus
+ && m_pInactiveStatus
+ && m_pReadyToTurnInStatus
+ && m_pFlavorText
+ && m_pObjectiveExplanationLabel
+ && m_pExpirationLabel
+ && m_pTurnInContainer
+ && m_pTurnInDimmer
+ && m_pTurnInButton
+ && m_pIdentifyButton
+ && m_pTurnInSpinnerContainer
+ && m_pTitleButton
+ && m_pIdentifyDimmer
+ && m_pIdentifyContainer
+ && m_pPhotoStatic
+ && m_pAcceptedImage
+ && m_pTurningInLabel
+ && m_pFlavorScrollingContainer
+ && m_pItemTrackerPanel
+ && m_pEncodedImage
+ && m_pMainContainer;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CQuestItemPanel::ApplySettings( KeyValues *inResourceData )
+{
+ tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s", __FUNCTION__ );
+ BaseClass::ApplySettings( inResourceData );
+
+ if ( m_hQuestItem )
+ {
+ m_vecFoldersImages.Purge();
+ KeyValues *pKVFoldersBlock = inResourceData->FindKey( "folders" );
+ Assert( pKVFoldersBlock );
+ if ( pKVFoldersBlock )
+ {
+ FOR_EACH_TRUE_SUBKEY( pKVFoldersBlock, pKVFolder )
+ {
+ auto& folder = m_vecFoldersImages[ m_vecFoldersImages.AddToTail() ];
+ folder.m_strFront = pKVFolder->GetString( "front", NULL);
+ folder.m_strBack = pKVFolder->GetString( "back", NULL );
+ }
+ }
+ else
+ {
+ const GameItemDefinition_t *pItemDef = m_hQuestItem->GetItemDefinition();
+ // Get our quest theme
+ const CQuestThemeDefinition *pTheme = pItemDef->GetQuestDef()->GetQuestTheme();
+ if ( pTheme )
+ {
+ Warning( "%s %s is missing 'folders' data\n", pItemDef->GetQuestDef()->GetCorrespondingOperationName(), pTheme->GetQuestItemResFile() );
+ }
+ }
+ }
+
+ if ( 1/*m_pKVItemTracker == NULL*/ )
+ {
+ KeyValues *pTrackerKV = inResourceData->FindKey( "tracker_kv" );
+
+ if ( pTrackerKV )
+ {
+ m_pKVItemTracker = pTrackerKV->MakeCopy();
+ }
+ }
+
+ m_strEncodedText = inResourceData->GetString( "encoded_text", NULL );
+ m_strExpireText = inResourceData->GetString( "expire_text", NULL );
+ m_strItemTrackerResFile = inResourceData->GetString( "TrackerPanelResFile", NULL );
+ // Sound effects
+ m_strTurnInSound = inResourceData->GetString( "turn_in_sound", NULL );
+ m_strTurnInSuccessSound = inResourceData->GetString( "turn_in_success_sound", NULL );
+ m_strDecodeSound = inResourceData->GetString( "decode_sound", NULL );
+ m_strExpandSound = inResourceData->GetString( "expand_sound", NULL );
+ m_strCollapseSound = inResourceData->GetString( "collapse_sound", NULL );
+
+ // Animations
+ m_strReset = inResourceData->GetString( "anim_reset", NULL );
+ m_strAnimExpand = inResourceData->GetString( "anim_expand", NULL );
+ m_strAnimCollapse = inResourceData->GetString( "anim_collapse", NULL );
+ m_strTurningIn = inResourceData->GetString( "anim_turning_in", NULL );
+ m_strHighlightOn = inResourceData->GetString( "anim_highlight_on", NULL );
+ m_strHighlightOff = inResourceData->GetString( "anim_highlight_off", NULL );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CQuestItemPanel::PerformLayout( void )
+{
+ tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s", __FUNCTION__ );
+ BaseClass::PerformLayout();
+
+ if ( !HasAllControls() )
+ return;
+
+ m_pIdentifyContainer->SetVisible( m_eState == STATE_UNIDENTIFIED );
+ m_pTurnInContainer->SetVisible( m_eState == STATE_COMPLETED || m_eState == STATE_TURNING_IN__GC_RESPONDED || m_eState == STATE_TURNING_IN__WAITING_FOR_GC);
+ m_pTurnInButton->SetVisible( m_eState == STATE_COMPLETED );
+ m_pTurnInSpinnerContainer->SetVisible( m_eState == STATE_TURNING_IN__GC_RESPONDED || m_eState == STATE_TURNING_IN__WAITING_FOR_GC );
+ m_pPhotoStatic->SetVisible( m_eState == STATE_UNIDENTIFIED || m_eState == STATE_IDENTIFYING );
+ m_pFindServerButton->SetVisible( m_eState == STATE_NORMAL );
+
+ // only exist in non public build
+ if ( m_pDiscardButton )
+ {
+ m_pDiscardButton->SetVisible( m_eState == STATE_NORMAL );
+ }
+
+ // loaners
+ if ( m_eState == STATE_NORMAL || m_eState == STATE_COMPLETED )
+ {
+ // get all loaners required from quest
+ CUtlVector< item_definition_index_t > vecLoanerItems;
+ bool bRequiredLoaners = GetLoanerListFromQuest( m_hQuestItem, vecLoanerItems ) > 0;
+
+ // get all granted loaners from this quest
+ CUtlVector< CEconItemView* > vecGrantedLoaners;
+ if ( bRequiredLoaners )
+ {
+ GetLoanersFromLocalInventory( m_hQuestItem->GetItemID(), vecLoanerItems, vecGrantedLoaners );
+ }
+
+ if ( bRequiredLoaners )
+ {
+ m_pLoanerContainerPanel->SetVisible( true );
+ bool bAllGranted = vecLoanerItems.Count() == vecGrantedLoaners.Count();
+ m_pRequestLoanerItemsButton->SetVisible( !bAllGranted );
+ m_pEquipLoanerItemsButton->SetVisible( bAllGranted );
+
+ for ( int i = 0; i < ARRAYSIZE( m_pLoanerItemModelPanel ); ++i )
+ {
+ // try to use the granted items first
+ if ( i < vecGrantedLoaners.Count() )
+ {
+ m_pLoanerItemModelPanel[i]->SetItem( vecGrantedLoaners[i] );
+ m_pLoanerItemModelPanel[i]->SetVisible( true );
+ }
+ // In case we don't get all the loaner items, use fake items as second option
+ else if ( i < vecLoanerItems.Count() )
+ {
+ CEconItemView tempItem;
+ tempItem.Init( vecLoanerItems[i], AE_UNIQUE, AE_USE_SCRIPT_VALUE, true );
+
+ m_pLoanerItemModelPanel[i]->SetItem( &tempItem );
+ m_pLoanerItemModelPanel[i]->SetVisible( true );
+ }
+ else
+ {
+ m_pLoanerItemModelPanel[i]->SetVisible( false );
+ }
+ }
+ }
+ else
+ {
+ m_pLoanerContainerPanel->SetVisible( false );
+ }
+ }
+ else
+ {
+ m_pLoanerContainerPanel->SetVisible( false );
+ }
+
+
+
+ m_pEncodedStatus->SetShow( m_eState == STATE_UNIDENTIFIED );
+ m_pReadyToTurnInStatus->SetShow( m_eState == STATE_COMPLETED || m_eState == STATE_TURNING_IN__GC_RESPONDED || m_eState == STATE_TURNING_IN__WAITING_FOR_GC );
+
+ float flDecodeAmount = 1.f;
+ // Only cypher-style decoding needs to decode
+ if ( m_eDecodeStyle == DECODE_STYLE_CYPHER && m_eState == STATE_UNIDENTIFIED )
+ {
+ flDecodeAmount = 0.f;
+ }
+
+ m_pEncodedImage->SetAlpha( m_eState == STATE_UNIDENTIFIED ? 255 : 0 );
+
+ if ( m_hQuestItem )
+ {
+ m_pTitleButton->SetText( GetDecodedString( "name", flDecodeAmount ) );
+
+ int nScrollableYOffset = 0;
+ // Check if the quest is going to expire soon (within a week). If so, show a "This is going to be destroyed" message.
+ const CRTime nExpirationTime = m_hQuestItem->GetExpirationDate();
+ const CRTime nOneWeekFromNow = CRTime::RTime32DateAdd( CRTime::RTime32TimeCur(), 1, k_ETimeUnitWeek );
+ const bool bExpiringSoon = nExpirationTime.GetRTime32() != RTime32(0) && nExpirationTime < nOneWeekFromNow;
+ m_pExpirationLabel->SetVisible( bExpiringSoon );
+ if ( bExpiringSoon )
+ {
+ CLocalizedRTime32 locTime = { nExpirationTime.GetRTime32(), false, GLocalizationProvider(), NULL };
+ m_pExpirationLabel->SetText( CConstructLocalizedString( g_pVGuiLocalize->Find( m_strExpireText ), locTime ) );
+ m_pExpirationLabel->InvalidateLayout( true );
+ m_pExpirationLabel->SizeToContents();
+ nScrollableYOffset += m_pExpirationLabel->GetTall();
+ }
+
+ m_pObjectiveExplanationLabel->SetPos( 0, nScrollableYOffset );
+ m_pObjectiveExplanationLabel->SetText( GetDecodedString( "explanation", flDecodeAmount ) );
+ m_pObjectiveExplanationLabel->InvalidateLayout( true ); // So we get the right height when we do SizeToContents below
+ m_pObjectiveExplanationLabel->SizeToContents();
+ nScrollableYOffset += m_pObjectiveExplanationLabel->GetTall();
+
+ m_pFlavorText->SetText( GetDecodedString( "desc", flDecodeAmount ) );
+ int nWide, nTall;
+ m_pFlavorText->GetTextImage()->GetContentSize( nWide, nTall );
+ m_pFlavorText->SetTall( nTall + 20 );
+
+ m_pItemTrackerPanel->SetPos( m_pItemTrackerPanel->GetXPos(), nScrollableYOffset );
+ nScrollableYOffset += m_pItemTrackerPanel->GetTall();
+
+ // Put the flavor text below the obectives
+ m_pFlavorText->SetPos( m_pFlavorText->GetXPos(), nScrollableYOffset );
+
+ m_pFlavorScrollingContainer->InvalidateLayout( true );
+ m_pFlavorScrollingContainer->InvalidateLayout();
+ m_pFlavorScrollingContainer->ResetScrollAmount();
+
+
+ // Randomize our folder images based on original ID
+ if ( m_vecFoldersImages.Count() )
+ {
+ RandomSeed( m_hQuestItem->GetSOCData() ? m_hQuestItem->GetSOCData()->GetOriginalID() : m_hQuestItem->GetItemDefIndex() );
+ int idx = RandomInt( 0, m_vecFoldersImages.Count() - 1 );
+
+ m_pFrontFolderImage->SetImage( m_vecFoldersImages[ idx ].m_strFront );
+ m_pBackFolderImage->SetImage( m_vecFoldersImages[ idx ].m_strBack );
+ }
+ }
+
+ UpdateInvalidReasons();
+
+ if ( m_pItemTrackerPanel )
+ {
+ auto& vecObjectives = m_pItemTrackerPanel->GetAttributePanels();
+ FOR_EACH_VEC( vecObjectives, i )
+ {
+ // Only do this when unidentified. The panel updates its own string, and we don't want to stomp it
+ if ( m_eState == STATE_UNIDENTIFIED )
+ {
+ const wchar_t *pszString = GetDecodedString( CFmtStr( "objective%d", i ), flDecodeAmount );
+ vecObjectives[ i ]->SetDialogVariable( "attr_desc", pszString );
+ }
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CQuestItemPanel::IsCursorOverMainContainer() const
+{
+ return m_pMainContainer ? m_pMainContainer->IsCursorOver() : false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CQuestItemPanel::SetupObjectivesPanels( bool bRecreate )
+{
+ if ( m_pItemTrackerPanel && bRecreate )
+ {
+ m_pItemTrackerPanel->MarkForDeletion();
+ m_pItemTrackerPanel = NULL;
+ }
+
+ if ( !m_hQuestItem )
+ return;
+
+ if ( !m_pItemTrackerPanel )
+ {
+ m_pItemTrackerPanel = new CItemTrackerPanel( m_pFlavorScrollingContainer, "ItemTrackerPanel", m_hQuestItem->GetSOCData(), m_strItemTrackerResFile );
+ m_pItemTrackerPanel->SetAutoDelete( false );
+ SETUP_PANEL( m_pItemTrackerPanel );
+ }
+ else
+ {
+ // Get all the panels created
+ m_pItemTrackerPanel->SetItem( m_hQuestItem->GetSOCData() );
+ m_pItemTrackerPanel->InvalidateLayout( true );
+ }
+
+ m_pItemTrackerPanel->SetTall( m_pItemTrackerPanel->GetContentTall() );
+
+ // Need to re-layout so the flavor text gets properly positioned under
+ // all of the objectives
+ InvalidateLayout();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CQuestItemPanel::SetItem( CEconItemView* pItem )
+{
+ tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s", __FUNCTION__ );
+ if ( pItem == m_hQuestItem )
+ {
+ return;
+ }
+
+ m_bCollapsed = true;
+ m_hQuestItem.SetItem( pItem );
+
+ if ( m_pItemTrackerPanel && pItem )
+ {
+ m_pItemTrackerPanel->SetItem( pItem->GetSOCData() );
+ }
+
+ // By default
+ SetState( STATE_NORMAL );
+
+ if ( m_hQuestItem )
+ {
+ if ( IsQuestItemReadyToTurnIn( m_hQuestItem ) )
+ {
+ SetState( STATE_COMPLETED );
+ }
+ else if ( IsUnacknowledged() )
+ {
+ SetState( STATE_UNIDENTIFIED );
+ }
+
+ // Snag the quickplay map (if there is one)
+ m_strQuickPlayMap = m_hQuestItem->GetItemDefinition()->GetQuestDef()->GetQuickplayMapName();
+
+ m_strMatchmakingGroupName = m_hQuestItem->GetItemDefinition()->GetQuestDef()->GetMatchmakingGroupName();
+ m_strMatchmakingCategoryName = m_hQuestItem->GetItemDefinition()->GetQuestDef()->GetMatchmakingCategoryName();
+ m_strMatchmakingMapName = m_hQuestItem->GetItemDefinition()->GetQuestDef()->GetMatchmakingMapName();
+ }
+
+ // Reload res file so we get the right art
+ LoadResFileForCurrentItem();
+
+ // Capture strings after controls are created
+ CaptureAndEncodeStrings();
+
+ InvalidateLayout();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Returns if the character is one that we don't want to re-encode
+// as another, or one that we don't want to encode another to in
+// order to maintain line breaks so that the decoding sequence is
+// easier for the user to follow.
+//-----------------------------------------------------------------------------
+bool IsNonEncodeCharacter( const wchar_t& wch)
+{
+ switch ( wch )
+ {
+ case L'\x000A':
+ case L'\x000B':
+ case L'\x000C':
+ case L'\x000D':
+ case L'\x0085':
+ case L'\x2028':
+ case L'\x2029':
+ case L' ':
+ return true;
+ }
+
+ return false;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CQuestItemPanel::CaptureAndEncodeStrings()
+{
+ tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s", __FUNCTION__ );
+
+ if ( !m_hQuestItem )
+ return;
+
+ // Clean up any existing values
+ if ( m_pKVCipherStrings )
+ {
+ m_pKVCipherStrings->deleteThis();
+ m_pKVCipherStrings = NULL;
+ }
+
+ m_pKVCipherStrings = new KeyValues( "cipherstrings" );
+ KeyValues *pKVDecoded = m_pKVCipherStrings->CreateNewKey();
+ pKVDecoded->SetName( "decoded" );
+
+ {
+ // Capture the description/flavor string
+ const char *pszLocToken = m_hQuestItem->GetItemDefinition()->GetQuestDef()->GetRolledDescriptionForItem( m_hQuestItem->GetSOCData() );
+ pKVDecoded->SetWString( "desc", g_pVGuiLocalize->Find( pszLocToken ) );
+ }
+
+ if ( m_pObjectiveExplanationLabel )
+ {
+ wchar_t wszBuff[512];
+ m_pObjectiveExplanationLabel->GetText( wszBuff, ARRAYSIZE( wszBuff ) );
+ pKVDecoded->SetWString( "explanation", wszBuff );
+ }
+
+ auto& vecObjectives = m_pItemTrackerPanel->GetAttributePanels();
+ // Capture objective strings
+ FOR_EACH_VEC( vecObjectives, i )
+ {
+ CItemAttributeProgressPanel *pObjective = vecObjectives[ i ];
+ KeyValues *pKV = pObjective->GetDialogVariables();
+ pKVDecoded->SetWString( CFmtStr( "objective%d", i ), pKV->GetWString( "attr_desc" ) );
+ }
+
+ // Create encoded strings from the decoded strings
+ KeyValues *pKVEncoded = pKVDecoded->MakeCopy();
+ pKVEncoded->SetName( "encoded" );
+
+ m_pKVCipherStrings->AddSubKey( pKVEncoded );
+
+ RandomSeed( m_hQuestItem->GetSOCData() ? m_hQuestItem->GetSOCData()->GetOriginalID() : m_hQuestItem->GetItemDefIndex() );
+
+ // "encode" each string by scrambling
+ FOR_EACH_VALUE( pKVEncoded, pKVString )
+ {
+ const wchar_t *pWString = pKVString->GetWString();
+ wchar wszBuff[4096];
+ loc_scpy_safe( wszBuff, pWString );
+ int nStrLen = Q_wcslen( wszBuff );
+
+ // Go through the entire string and swap each character
+ // with another random character in the string
+ int i=0;
+ while( wszBuff[i] != 0 && i < ARRAYSIZE( wszBuff ) )
+ {
+ // Dont scramble spaces to maintain line breaks
+ if ( !IsNonEncodeCharacter( wszBuff[i] ) )
+ {
+ // Scramble, but keep trying if we scramble to a space
+ do
+ {
+ wszBuff[i] = *(pWString + RandomInt( 0, nStrLen - 1 ) );
+ } while ( IsNonEncodeCharacter( wszBuff[i] ) );
+
+ }
+
+ i++;
+ }
+
+ pKVEncoded->SetWString( pKVString->GetName(), wszBuff );
+ }
+
+ const char *pszLocToken = m_hQuestItem->GetItemDefinition()->GetQuestDef()->GetRolledNameForItem( m_hQuestItem->GetSOCData() );
+ const wchar_t* pwszName = g_pVGuiLocalize->Find( pszLocToken );
+ // Force the encrypted version of the quest title to be "<Encrypted>".
+ pKVEncoded->SetWString( "name", g_pVGuiLocalize->Find( m_strEncodedText ) );
+ pKVDecoded->SetWString( "name", pwszName );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CQuestItemPanel::OnCommand( const char *command )
+{
+ tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s", __FUNCTION__ );
+ BaseClass::OnCommand( command );
+
+ if ( FStrEq( command, "discard_quest" ) )
+ {
+ OnDiscardQuest();
+ }
+ else if ( FStrEq( command, "select" ) )
+ {
+ m_pQuestList->SetSelected( this, false );
+ }
+ else if ( FStrEq( command, "turnin" ) )
+ {
+ if ( m_hQuestItem && m_hQuestItem->GetItemDefinition() && m_hQuestItem->GetItemDefinition()->GetQuestDef() )
+ {
+ if ( !tf_quest_turn_in_confirm_opt_out.GetBool() && ( GetEarnedBonusPoints( m_hQuestItem ) != m_hQuestItem->GetItemDefinition()->GetQuestDef()->GetMaxBonusPoints() ) )
+ {
+ CTFGenericConfirmOptOutDialog *pPanel = ShowConfirmOptOutDialog( "#TF_Quest_TurnIn_Title", "#TF_Quest_TurnIn_Text",
+ "#TF_Quest_TurnIn_Yes", "#TF_Quest_TurnIn_No",
+ "#TF_Quest_TurnIn_Ask_Opt_Out", "tf_quest_turn_in_confirm_opt_out",
+ ConfirmTurnInQuest );
+ if ( pPanel )
+ {
+ pPanel->SetContext( this );
+ return;
+ }
+ }
+ else
+ {
+ OnCompleteQuest();
+ }
+ }
+ }
+ else if ( FStrEq( command, "identify" ) )
+ {
+ OnIdentify();
+ }
+ else if ( FStrEq( command, "request_loaner_items" ) )
+ {
+ GCSDK::CProtoBufMsg< CMsgGCQuestObjective_RequestLoanerItems > msg( k_EMsgGCQuestObjective_RequestLoanerItems );
+ msg.Body().set_quest_item_id( m_hQuestItem->GetItemID() );
+ GCClientSystem()->BSendMessage( msg );
+ }
+ else if ( FStrEq( command, "equip_loaner_items" ) )
+ {
+ OnEquipLoaners();
+ }
+ else if( Q_strnicmp( "playsound", command, 9 ) == 0 )
+ {
+ vgui::surface()->PlaySound( command + 10 );
+ }
+ else if ( FStrEq( "mm_casual_open", command ) )
+ {
+ if ( GTFGCClientSystem() )
+ {
+ if ( ( m_strMatchmakingGroupName != 0 ) || ( m_strMatchmakingCategoryName != 0 ) || ( m_strMatchmakingMapName != 0 ) )
+ {
+ GTFGCClientSystem()->ClearCasualSearchCriteria();
+
+ if ( m_strMatchmakingGroupName != 0 )
+ {
+ int iGroupType = StringFieldToInt( m_strMatchmakingGroupName.Get(), s_pszMMTypes, ARRAYSIZE( s_pszMMTypes ) );
+ if ( iGroupType > -1 )
+ {
+ SelectGroup( (EMatchmakingGroupType)iGroupType, true );
+ }
+ }
+
+ if ( m_strMatchmakingCategoryName != 0 )
+ {
+ int iCategoryType = StringFieldToInt( m_strMatchmakingCategoryName.Get(), s_pszGameModes, ARRAYSIZE( s_pszGameModes ) );
+ if ( iCategoryType > -1 )
+ {
+ SelectCategory( (EGameCategory)iCategoryType, true );
+ }
+ }
+
+ if ( m_strMatchmakingMapName != 0 )
+ {
+ if ( GetItemSchema() )
+ {
+ const MapDef_t *pMap = GetItemSchema()->GetMasterMapDefByName( m_strMatchmakingMapName.Get() );
+ if ( pMap )
+ {
+ GTFGCClientSystem()->SelectCasualMap( pMap->m_nDefIndex, true );
+ }
+ }
+ }
+ }
+ }
+
+ // Defaulting to 12v12
+ GTFGCClientSystem()->SetLadderType( k_nMatchGroup_Casual_12v12 );
+ PromptOrFireCommand( "OpenMatchmakingLobby casual" );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+const wchar_t* CQuestItemPanel::GetDecodedString( const char* pszKeyName, float flPercentDecoded )
+{
+ tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s", __FUNCTION__ );
+ static wchar_t wszBuff[4096];
+ KeyValues *pKVEncoded = m_pKVCipherStrings->FindKey( "encoded" );
+ KeyValues *pKVDecoded = m_pKVCipherStrings->FindKey( "decoded" );
+
+ // Trivial work?
+ if ( flPercentDecoded <= 0.f )
+ {
+ return pKVEncoded->GetWString( pszKeyName );
+ }
+ else if ( flPercentDecoded >= 1.f )
+ {
+ return pKVDecoded->GetWString( pszKeyName );
+ }
+
+ loc_scpy_safe( wszBuff, pKVEncoded->GetWString( pszKeyName ) );
+ const locchar_t* pwszDecoded = pKVDecoded->GetWString( pszKeyName );
+ int nLength = loc_strlen( pwszDecoded );
+ int nMaxCopy = nLength * flPercentDecoded;
+ // Not using V_wcsncpy because it null terminates.
+ wcsncpy( wszBuff, pwszDecoded, nMaxCopy );
+
+ return wszBuff;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CQuestItemPanel::OnThink()
+{
+ tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s", __FUNCTION__ );
+
+ switch ( m_eState )
+ {
+ case STATE_IDENTIFYING:
+ {
+ // Have we finished?
+ if ( m_StateTimer.HasStarted() && m_StateTimer.IsElapsed() )
+ {
+ m_pItemTrackerPanel->InvalidateLayout();
+ m_StateTimer.Invalidate();
+ SetState( STATE_NORMAL );
+
+ // Play a reveal sound?
+ const GameItemDefinition_t *pItemDef = m_hQuestItem->GetItemDefinition();
+ const CQuestThemeDefinition *pTheme = pItemDef->GetQuestDef()->GetQuestTheme();
+ if ( pTheme )
+ {
+ const char *pszRevealSound = pTheme->GetRevealSound();
+ if ( pszRevealSound && pszRevealSound[0] )
+ {
+ vgui::surface()->PlaySound( pszRevealSound );
+ }
+ }
+ }
+
+ float flPercent = m_StateTimer.GetElapsedTime() / m_StateTimer.GetCountdownDuration();
+
+ switch( m_eDecodeStyle )
+ {
+ case DECODE_STYLE_CYPHER:
+ {
+ // Slowly "decode" the text in the lables
+ m_pTitleButton->SetText( GetDecodedString( "name", flPercent ) );
+ m_pFlavorText->SetText( GetDecodedString( "desc", flPercent ) );
+ m_pObjectiveExplanationLabel->SetText( GetDecodedString( "explanation", flPercent ) );
+
+ auto& vecObjectives = m_pItemTrackerPanel->GetAttributePanels();
+ FOR_EACH_VEC( vecObjectives, i )
+ {
+ const wchar_t *pszString = GetDecodedString( CFmtStr( "objective%d", i ), flPercent );
+ vecObjectives[ i ]->SetDialogVariable( "attr_desc", pszString );
+ }
+ break;
+ }
+ case DECODE_STYLE_PANEL_FADE:
+ {
+ // Slowly fade out the encode image
+ m_pEncodedImage->SetAlpha( 255 * ( 1.f - flPercent ) );
+ break;
+ }
+ default:
+ Assert( 0 );
+ }
+
+ break;
+ }
+ case STATE_TURNING_IN__WAITING_FOR_GC:
+ {
+ // Have we finished?
+ if ( m_StateTimer.HasStarted() && m_StateTimer.IsElapsed() )
+ {
+ m_pQuestList->SetCompletingPanel( NULL );
+ m_StateTimer.Invalidate();
+
+ // Bring up confirm dialog
+ CTFGenericConfirmDialog *pDialog = new CTFGenericConfirmDialog( "#TF_Trading_Timeout_Title", "#TF_Trading_Timeout_Text", "#TF_OK", NULL, NULL, NULL );
+
+ if ( pDialog )
+ {
+ pDialog->SetContext( this );
+ pDialog->Show();
+ }
+
+ SetState( STATE_COMPLETED );
+ }
+
+ // Intentionally fall through
+ }
+ case STATE_TURNING_IN__GC_RESPONDED:
+ {
+ // Have we finished?
+ if ( m_StateTimer.HasStarted() && m_StateTimer.IsElapsed() )
+ {
+ SetState( STATE_SHOW_ACCEPTED );
+ m_StateTimer.Start( 3.f );
+ m_pAcceptedImage->SetVisible( true );
+
+ vgui::surface()->PlaySound( m_strTurnInSuccessSound );
+ }
+
+ if ( m_pTurningInLabel )
+ {
+ int nPeriods = m_StateTimer.GetElapsedTime() / 0.3f;
+ nPeriods %= 4; // Only do up to 3 periods
+
+ wchar_t wszTurningInText[64];
+ char szTurningInLocToken[128];
+ m_pTurningInLabel->GetTextImage()->GetUnlocalizedText( szTurningInLocToken, ARRAYSIZE( szTurningInLocToken ) );
+ V_snwprintf( wszTurningInText, ARRAYSIZE( wszTurningInText ), L"%ls", g_pVGuiLocalize->Find( szTurningInLocToken ) );
+
+ while ( nPeriods > 0 )
+ {
+ V_wcsncat( wszTurningInText, L".", ARRAYSIZE( wszTurningInText ) );
+ --nPeriods;
+ }
+
+ m_pTurningInLabel->SetText( wszTurningInText );
+ }
+
+ break;
+ }
+ case STATE_SHOW_ACCEPTED:
+ {
+ if ( m_StateTimer.HasStarted() && m_StateTimer.IsElapsed() )
+ {
+ m_pQuestList->SetCompletingPanel( NULL );
+ m_StateTimer.Invalidate();
+
+ engine->ClientCmd_Unrestricted( "gameui_allowescapetoshow\n" );
+
+ InventoryManager()->ShowItemsPickedUp( true, false );
+ GetQuestLog()->AttachToGameUI();
+ GetQuestLog()->MarkQuestsDirty();
+ m_pQuestList->PopulateQuestLists();
+
+ engine->ClientCmd_Unrestricted( "gameui_preventescapetoshow\n" );
+
+ if ( m_pszCompleteSound )
+ {
+ vgui::surface()->PlaySound( m_pszCompleteSound );
+ }
+ }
+ else
+ {
+ float flPercent = Clamp( m_StateTimer.GetElapsedTime() / 0.2f, 0.f, 1.f );
+ m_nPaperXShakePos = sin( m_StateTimer.GetElapsedTime() * 200.f ) * ( 1.f - flPercent ) * 8.f;
+ m_nPaperYShakePos = sin( m_StateTimer.GetElapsedTime() * 200.f ) * ( 1.f - flPercent ) * 8.f;
+ m_pQuestPaperContainer->SetPos( m_nPaperXPos + m_nPaperXShakePos, m_nPaperYPos + m_nPaperYShakePos );
+ }
+
+ break;
+ }
+ case STATE_UNIDENTIFIED:
+ {
+ // Do nothing
+ break;
+ }
+ case STATE_COMPLETED:
+ {
+ // Do nothing
+ break;
+ }
+ default:
+ // Do nothing
+ break;
+ }
+
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CQuestItemPanel::FireGameEvent( IGameEvent *event )
+{
+ tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s", __FUNCTION__ );
+ if( FStrEq( event->GetName(), "quest_objective_completed" ) )
+ {
+ itemid_t nIDLow = 0x00000000FFFFFFFF & (itemid_t)event->GetInt( "quest_item_id_low" );
+ itemid_t nIDHi = 0xFFFFFFFF00000000 & (itemid_t)event->GetInt( "quest_item_id_hi" ) << 32;
+ itemid_t nID = nIDLow | nIDHi;
+ if ( m_hQuestItem && nID == m_hQuestItem->GetID() )
+ {
+ SetupObjectivesPanels( false );
+
+ if ( IsQuestItemReadyToTurnIn( m_hQuestItem ) )
+ {
+ SetState( STATE_COMPLETED );
+ }
+
+ PerformLayout();
+ }
+ }
+ else if ( FStrEq( event->GetName(), "player_spawn" )
+ || FStrEq( event->GetName(), "client_disconnect" ) )
+ {
+ InvalidateLayout();
+ }
+ else if ( FStrEq( "inventory_updated", event->GetName() ) )
+ {
+ // InvalidateLayout();
+ }
+}
+
+void CQuestItemPanel::OnMouseReleased( MouseCode code )
+{
+ OnCommand( "select" );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Update our invalid reasons
+//-----------------------------------------------------------------------------
+void CQuestItemPanel::UpdateInvalidReasons()
+{
+ tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s", __FUNCTION__ );
+ InvalidReasonsContainer_t invalidReasons;
+ bool bAllAreInvalid = false;
+
+ C_TFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer();
+ if ( pLocalPlayer && m_hQuestItem )
+ {
+ // Get the tracker for the items
+ const CQuestItemTracker* pItemTracker = QuestObjectiveManager()->GetTypedTracker< CQuestItemTracker* >( m_hQuestItem->GetItemID() );
+ // Get invalid reasons
+ if ( pItemTracker )
+ {
+ int nNumInvalid = pItemTracker->IsValidForPlayer( pLocalPlayer, invalidReasons );
+ bAllAreInvalid = pItemTracker->GetTrackers().Count() == nNumInvalid;
+ }
+
+ // Build a string describing why the current quest can't be worked on
+ if ( !invalidReasons.IsValid() )
+ {
+ CUtlVector< CUtlString > vecStrings;
+ // Get the strings that explain each reasons
+ GetInvalidReasonsNames( invalidReasons, vecStrings );
+
+ wchar_t wszBuff[ 1024 ];
+
+ // Start with the explanation
+ V_swprintf_safe( wszBuff, L"%ls", g_pVGuiLocalize->Find( "#TF_QuestInvalid_Explanation" ) );
+
+ // Add in each reason why the quest is invalid
+ for( int i = 0; i < vecStrings.Count(); ++ i )
+ {
+ V_wcscat_safe( wszBuff, L"\n\n" );
+ V_wcscat_safe( wszBuff, g_pVGuiLocalize->Find( vecStrings[i] ) );
+ }
+
+ // This gets snagged by CQuestTooltip
+ m_pInactiveStatus->SetDialogVariable( "tiptext", wszBuff );
+ }
+ }
+
+ // Visible if there's a reason why we're invalid
+ bool bShow = bAllAreInvalid && m_eState == STATE_NORMAL;
+ m_pInactiveStatus->SetShow( bShow );
+ m_pInactiveStatus->SetMouseInputEnabled( bShow );
+ m_pInactiveStatus->SetTooltip( g_spTextTooltip, NULL );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Start a glow
+//-----------------------------------------------------------------------------
+void CQuestItemPanel::OnCollapsedGlowStart( void )
+{
+ g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( this, m_strHighlightOn );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Stop the glow
+//-----------------------------------------------------------------------------
+void CQuestItemPanel::OnCollapsedGlowEnd( void )
+{
+ g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( this, m_strHighlightOff );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Delete the quest.
+//-----------------------------------------------------------------------------
+void CQuestItemPanel::OnDiscardQuest( void )
+{
+#if !defined(STAGING_ONLY) && !defined(DEBUG)
+ // Not in public!
+ return;
+#endif
+
+ if ( m_pQuestList->GetCompletingPanel() == NULL )
+ {
+ // Bring up confirm dialog
+ CTFGenericConfirmDialog *pDialog = new CTFGenericConfirmDialog( "#QuestConfirmDiscard_Title", "#QuestConfirmDiscard_Body", "#X_DiscardItem", "#Cancel", &ConfirmDiscardQuest, NULL );
+ if ( pDialog )
+ {
+ pDialog->SetContext( this );
+ pDialog->Show();
+ }
+
+ const GameItemDefinition_t *pItemDef = m_hQuestItem->GetItemDefinition();
+ // Get our quest theme
+ const CQuestThemeDefinition *pTheme = pItemDef->GetQuestDef()->GetQuestTheme();
+ if ( pTheme )
+ {
+ const char *pszDiscardSound = pTheme->GetDiscardSound();
+ if ( pszDiscardSound && pszDiscardSound[0] )
+ {
+ vgui::surface()->PlaySound( pszDiscardSound );
+ }
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Equip loaners for local player
+//-----------------------------------------------------------------------------
+void CQuestItemPanel::OnEquipLoaners( void )
+{
+ if ( !m_hQuestItem )
+ return;
+
+ if ( m_pQuestList->GetCompletingPanel() == NULL )
+ {
+ // Bring up confirm dialog
+ CTFGenericConfirmDialog *pDialog = new CTFGenericConfirmDialog( "#QuestConfirmEquipLoaners_Title", "#QuestConfirmEquipLoaners_Body", "#Equip", "#Cancel", &ConfirmEquipLoaners, NULL );
+ if ( pDialog )
+ {
+ pDialog->SetContext( this );
+ pDialog->Show();
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Send a message to the GC to evaluate completion of this quest
+//-----------------------------------------------------------------------------
+void CQuestItemPanel::OnCompleteQuest( void )
+{
+ if ( !m_hQuestItem )
+ return;
+
+ // Double check that they're not just forcing the command
+ if ( IsQuestItemReadyToTurnIn( m_hQuestItem ) && m_pQuestList->GetCompletingPanel() == NULL )
+ {
+ m_pQuestList->SetCompletingPanel( this );
+
+ SetState( STATE_TURNING_IN__WAITING_FOR_GC );
+
+ // Use the timer for turning in the quest
+ m_StateTimer.Start( k_flQuestTurnInTime );
+ vgui::surface()->PlaySound( m_strTurnInSound );
+
+ GCSDK::CProtoBufMsg< CMsgGCQuestComplete_Request > msg( k_EMsgGCQuestComplete_Request );
+
+ msg.Body().set_quest_item_id( m_hQuestItem->GetItemID() );
+
+ GCClientSystem()->BSendMessage( msg );
+
+ PostActionSignal( new KeyValues("CompleteQuest") );
+
+ g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( this, m_strTurningIn );
+
+ const GameItemDefinition_t *pItemDef = m_hQuestItem->GetItemDefinition();
+ // Get our quest theme
+ const CQuestThemeDefinition *pTheme = pItemDef->GetQuestDef()->GetQuestTheme();
+ if ( pTheme )
+ {
+ m_pszCompleteSound = pTheme->GetRewardSound();
+ }
+ }
+}
+
+void CQuestItemPanel::OnIdentify()
+{
+ if ( IsUnacknowledged() )
+ {
+ SetState( STATE_IDENTIFYING );
+
+ // Use the timer for identifying progress
+ m_StateTimer.Start( k_flQuestDecodeTime );
+ vgui::surface()->PlaySound( m_strDecodeSound );
+
+ // ack item
+ CEconItemView *pModifyItem = m_hQuestItem;
+ TFInventoryManager()->AcknowledgeItem( pModifyItem, false );
+ TFInventoryManager()->SetItemBackpackPosition( pModifyItem, (uint32)-1, false, true );
+
+ g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( m_pQuestPaperContainer, "QuestItem_StaticPhoto_Reveal" );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CQuestItemPanel::OnConfirmDelete( bool bConfirm )
+{
+ // Delete the quest
+ if ( bConfirm && m_hQuestItem )
+ {
+ GCSDK::CProtoBufMsg< CMsgGCQuestDiscard_Request > msg( k_EMsgGCQuestDiscard_Request );
+
+ msg.Body().set_quest_item_id( m_hQuestItem->GetItemID() );
+
+ GCClientSystem()->BSendMessage( msg );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CQuestItemPanel::OnConfirmEquipLoaners( bool bConfirm )
+{
+ // equip loaners
+ if ( bConfirm && m_hQuestItem )
+ {
+ // get all loaners required from quest
+ CUtlVector< item_definition_index_t > vecLoanerItems;
+ bool bRequiredLoaners = GetLoanerListFromQuest( m_hQuestItem, vecLoanerItems );
+
+ // get all granted loaners from this quest
+ CUtlVector< CEconItemView* > vecGrantedLoaners;
+ if ( bRequiredLoaners )
+ {
+ GetLoanersFromLocalInventory( m_hQuestItem->GetItemID(), vecLoanerItems, vecGrantedLoaners );
+ }
+
+ for ( int i=0; i<vecGrantedLoaners.Count(); ++i )
+ {
+ CEconItemView *pItem = vecGrantedLoaners[i];
+ if ( pItem )
+ {
+ // do it for first class that can equip
+ for ( int iClass = TF_FIRST_NORMAL_CLASS; iClass < TF_LAST_NORMAL_CLASS; ++iClass )
+ {
+ if ( pItem->GetStaticData()->CanBeUsedByClass( iClass ) )
+ {
+ int iSlot = pItem->GetStaticData()->GetLoadoutSlot( iClass );
+ TFInventoryManager()->EquipItemInLoadout( iClass, iSlot, pItem->GetItemID() );
+
+ // take the player to character loadout page
+ engine->ClientCmd_Unrestricted( CFmtStr( "open_charinfo_direct %d", iClass ) );
+ break;
+ }
+ }
+ }
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CQuestItemPanel::QuestCompletedResponse()
+{
+ // If we werent the one listening, dont bother
+ if ( m_eState != STATE_TURNING_IN__WAITING_FOR_GC )
+ return;
+
+ m_pQuestPaperContainer->GetPos( m_nPaperXPos, m_nPaperYPos );
+ m_nPaperXShakePos = m_nPaperYShakePos = 0;
+
+ SetState( STATE_TURNING_IN__GC_RESPONDED );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CQuestItemPanel::SetSelected( bool bSelected, bool bImmediate )
+{
+ bool bPrevCollapsedSide = m_bCollapsed;
+ m_bCollapsed = ( !m_bCollapsed && bSelected ) || ( !bSelected );
+
+ if ( !bImmediate && bPrevCollapsedSide != m_bCollapsed )
+ {
+ if ( m_bCollapsed )
+ {
+ vgui::surface()->PlaySound( m_strCollapseSound );
+ g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( this, m_strAnimCollapse );
+ }
+ else
+ {
+ vgui::surface()->PlaySound( m_strExpandSound );
+ g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( this, m_strAnimExpand );
+ }
+ }
+ else
+ {
+ g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( this, m_strReset );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CQuestItemPanel::IsUnacknowledged()
+{
+ if ( !m_hQuestItem )
+ return false;
+
+ return IsQuestItemUnidentified( m_hQuestItem->GetSOCData() );
+}
+
+void CQuestItemPanel::SetState( EItemPanelState_t eState )
+{
+ m_eState = eState;
+ InvalidateLayout();
+}
+
+
+
+//-----------------------------------------------------------------------------
+// Purpose: GC Msg handler to handle a loaner item response
+//-----------------------------------------------------------------------------
+class CGCLoanerRequestResponse : public GCSDK::CGCClientJob
+{
+public:
+ CGCLoanerRequestResponse( GCSDK::CGCClient *pClient ) : GCSDK::CGCClientJob( pClient ) {}
+
+ virtual bool BYieldingRunGCJob( GCSDK::IMsgNetPacket *pNetPacket )
+ {
+ GCSDK::CProtoBufMsg<CMsgGCQuestObjective_RequestLoanerResponse> msg( pNetPacket );
+
+ // Show them the items they just got loaned!
+ InventoryManager()->ShowItemsPickedUp( true, false );
+
+ return true;
+ }
+};
+
+GC_REG_JOB( GCSDK::CGCClient, CGCLoanerRequestResponse, "CGCLoanerRequestResponse", k_EMsgGCQuestObjective_RequestLoanerResponse, GCSDK::k_EServerTypeGCClient );