summaryrefslogtreecommitdiff
path: root/game/client/tf/vgui/quest_log_panel.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'game/client/tf/vgui/quest_log_panel.cpp')
-rw-r--r--game/client/tf/vgui/quest_log_panel.cpp1182
1 files changed, 1182 insertions, 0 deletions
diff --git a/game/client/tf/vgui/quest_log_panel.cpp b/game/client/tf/vgui/quest_log_panel.cpp
new file mode 100644
index 0000000..e65c7b0
--- /dev/null
+++ b/game/client/tf/vgui/quest_log_panel.cpp
@@ -0,0 +1,1182 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#include "cbase.h"
+#include "quest_log_panel.h"
+#include "ienginevgui.h"
+#include "c_tf_gamestats.h"
+#include "store/store_panel.h"
+#include "econ/econ_ui.h"
+#include "clientmode_tf.h"
+#include "tf_hud_mainmenuoverride.h"
+#include "vgui_int.h"
+#include "IGameUIFuncs.h" // for key bindings
+#include <vgui_controls/AnimationController.h>
+#include "tf_item_inventory.h"
+#include "vgui/IInput.h"
+#include "item_ad_panel.h"
+#include "vgui_controls/ProgressBar.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include <tier0/memdbgon.h>
+
+void AddSubKeyNamed( KeyValues *pKeys, const char *pszName );
+
+static CItemModelPanelToolTip* g_spItemTooltip = NULL;
+CQuestTooltip* g_spTextTooltip = NULL;
+
+CQuestLogPanel *GetQuestLog()
+{
+ CQuestLogPanel *pQuestLogPanel = (CQuestLogPanel*)gViewPortInterface->FindPanelByName( PANEL_QUEST_LOG );
+ return pQuestLogPanel;
+}
+
+//-------------------------------------------------------------------------------------------------------------------
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CScrollableQuestList::CScrollableQuestList( vgui::Panel *parent, const char *pszPanelName )
+ : EditablePanel( parent, pszPanelName )
+ , m_pCompletingPanel( NULL )
+ , m_bQuestsLayoutDirty( false )
+ , m_pszNoQuests( NULL )
+ , m_pszNeedAPass( NULL )
+ , m_pszNotPossible( NULL )
+{
+ m_pContainer = new EditablePanel( this, "Container" );
+ m_vecQuestItemPanels.SetSize( 2 );
+
+ FOR_EACH_VEC( m_vecQuestItemPanels, i )
+ {
+ m_vecQuestItemPanels[ i ] = new CQuestItemPanel( m_pContainer, "QuestItemPanel", NULL, this );
+ }
+}
+
+CScrollableQuestList::~CScrollableQuestList()
+{
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CScrollableQuestList::ApplySchemeSettings( vgui::IScheme *pScheme )
+{
+ tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s", __FUNCTION__ );
+ BaseClass::ApplySchemeSettings( pScheme );
+
+ const char *pszResFile = "Resource/UI/econ/ScrollableQuestList.res";
+
+ // Check if the operation wants to override our default res file
+ const auto& mapOperations = GetItemSchema()->GetOperationDefinitions();
+ FOR_EACH_MAP_FAST( mapOperations, i )
+ {
+ CEconOperationDefinition* pCurrentOperation = mapOperations[i];
+ // Take the first active operation's res file that's different than default
+ if ( pCurrentOperation->IsActive() && pCurrentOperation->GetQuestListOverrideResFile() )
+ {
+ // Use the first found for now
+ pszResFile = pCurrentOperation->GetQuestListOverrideResFile();
+ break;
+ }
+ }
+
+ LoadControlSettings( pszResFile );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CScrollableQuestList::ApplySettings( KeyValues *inResourceData )
+{
+ tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s", __FUNCTION__ );
+ BaseClass::ApplySettings( inResourceData );
+
+ m_pszNoQuests = inResourceData->GetString( "no_quests", "#QuestLog_NoQuests" );
+ m_pszNeedAPass = inResourceData->GetString( "need_a_pass", "#QuestLog_NeedPassForContracts" );
+ m_pszNotPossible = inResourceData->GetString( "not_possible", "#QuestLog_NoContractsPossible" );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CScrollableQuestList::PerformLayout( void )
+{
+ tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s", __FUNCTION__ );
+ BaseClass::PerformLayout();
+
+ m_pContainer->InvalidateLayout( true );
+
+ FOR_EACH_VEC( m_vecQuestItemPanels, i )
+ {
+ m_vecQuestItemPanels[ i ]->InvalidateLayout( true, true );
+ m_vecQuestItemPanels[ i ]->SetZPos( 1 + i );
+ }
+
+ PositionQuestItemPanels();
+
+ UpdateEmptyMessage();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CScrollableQuestList::OnThink()
+{
+ tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s", __FUNCTION__ );
+ if ( m_bQuestsLayoutDirty )
+ {
+ m_bQuestsLayoutDirty = false;
+
+ PositionQuestItemPanels();
+ }
+
+ // Conditionally turn mouse input on/off based on where the mouse is
+ FOR_EACH_VEC( m_vecQuestItemPanels, i )
+ {
+ m_vecQuestItemPanels[i]->SetMouseInputEnabled( m_vecQuestItemPanels[i]->IsCursorOverMainContainer() );
+ }
+}
+
+void CScrollableQuestList::OnCommand( const char *command )
+{
+ if ( FStrEq( command, "deselect_all" ) )
+ {
+ SetSelected( NULL, false );
+ }
+
+ BaseClass::OnCommand( command );
+}
+
+int QuestSort_AcquiredTime( CQuestItemPanel* const* p1, CQuestItemPanel* const* p2 )
+{
+ if ( !(*p1)->GetItem() )
+ return -1;
+
+ if ( !(*p2)->GetItem() )
+ return 1;
+
+ // Newest items first
+ return (*p1)->GetItem()->GetID() - (*p2)->GetItem()->GetID();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CScrollableQuestList::PositionQuestItemPanels()
+{
+ tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s", __FUNCTION__ );
+ // We dont do anything when a quest is completing
+ if ( m_pCompletingPanel != NULL )
+ return;
+
+ int nVisible = 0;
+ FOR_EACH_VEC( m_vecQuestItemPanels, i )
+ {
+ CQuestItemPanel* pPanel = m_vecQuestItemPanels[i];
+ if ( pPanel )
+ {
+ pPanel->SetVisible( pPanel->GetItem() );
+
+ if ( pPanel->GetItem() )
+ {
+ ++nVisible;
+ }
+ }
+ }
+
+ CExLabel *pLabel = FindControl<CExLabel>( "EmptyLabel", true );
+ if ( pLabel )
+ {
+ pLabel->SetVisible( nVisible == 0 );
+ }
+
+ // Check for a selected panel
+ const CQuestItemPanel* pSelected = NULL;
+ FOR_EACH_VEC( m_vecQuestItemPanels, i )
+ {
+ if ( m_vecQuestItemPanels[i]->IsSelected() )
+ {
+ Assert( pSelected == NULL );
+ pSelected = m_vecQuestItemPanels[i];
+ }
+ }
+
+ struct FolderCommands_t
+ {
+ const char* m_pszSelected;
+ const char* m_pszOtherIsSelected;
+ const char* m_pszNoneSelected;
+ };
+
+ const FolderCommands_t folderCommands[] = { { "QuestItem_Back_Selected", "QuestItem_Back_OtherSelected", "QuestItem_Back_NoneSelected" }
+ , { "QuestItem_Front_Selected", "QuestItem_Front_OtherSelected", "QuestItem_Front_NoneSelected" } };
+
+ // Update the positions
+ FOR_EACH_VEC( m_vecQuestItemPanels, i )
+ {
+ // This is the selected panel
+ if ( pSelected == m_vecQuestItemPanels[ i ] )
+ {
+ g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( m_vecQuestItemPanels[ i ], folderCommands[i].m_pszSelected );
+ }
+ else if ( pSelected ) // Some other panel is selected
+ {
+ g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( m_vecQuestItemPanels[ i ], folderCommands[i].m_pszOtherIsSelected );
+ }
+ else // No panel is selected
+ {
+ g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( m_vecQuestItemPanels[ i ], folderCommands[i].m_pszNoneSelected );
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CScrollableQuestList::SetSelected( CQuestItemPanel *pItem, bool bImmediately )
+{
+ tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s", __FUNCTION__ );
+ FOR_EACH_VEC( m_vecQuestItemPanels, i )
+ {
+ m_vecQuestItemPanels[ i ]->SetSelected( m_vecQuestItemPanels[ i ] == pItem, bImmediately );
+ }
+
+ PositionQuestItemPanels();
+}
+
+bool DoesLootlistDropQuests( const CEconLootListDefinition* pLootList )
+{
+ FOR_EACH_VEC( pLootList->GetLootListContents(), j )
+ {
+ const CEconLootListDefinition::drop_item_t& item = pLootList->GetLootListContents()[j];
+ // 0 and greater means item. Less than 0 means nested lootlist
+ if( item.m_iItemOrLootlistDef >= 0 )
+ {
+ const GameItemDefinition_t* pItemDef = assert_cast<const GameItemDefinition_t *>( GetItemSchema()->GetItemDefinition( item.m_iItemOrLootlistDef ) );
+ if( pItemDef )
+ {
+ return pItemDef->GetQuestDef();
+ }
+ }
+ else
+ {
+ // Get the nested lootlist
+ int iLLIndex = (item.m_iItemOrLootlistDef * -1) - 1;
+ const CEconLootListDefinition *pNestedLootList = GetItemSchema()->GetLootListByIndex( iLLIndex );
+ Assert( pNestedLootList );
+ if ( !pNestedLootList )
+ continue;
+
+ // Dig through all of this lootlist's entries
+ return DoesLootlistDropQuests( pNestedLootList );
+ }
+ }
+
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Update what message we show when we have no quests
+//-----------------------------------------------------------------------------
+void CScrollableQuestList::UpdateEmptyMessage()
+{
+ tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s", __FUNCTION__ );
+
+ CCyclingAdContainerPanel* pPassStoreAd = FindControl< CCyclingAdContainerPanel >( "ItemAd", true );
+ if ( !pPassStoreAd )
+ return;
+
+ pPassStoreAd->SetVisible( false );
+ SetDialogVariable( "noquests", "" );
+
+ FOR_EACH_VEC( m_vecQuestItemPanels, i )
+ {
+ // Case 1 above
+ if ( m_vecQuestItemPanels[ i ]->GetItem() )
+ return;
+ }
+
+ // By default, there's no operations going on, and there's no ad to show
+ const char *pszNoQuestsText = m_pszNotPossible;
+
+ // Find any active quest-dropping operations
+ const auto& mapOperations = GetItemSchema()->GetOperationDefinitions();
+ FOR_EACH_MAP_FAST( mapOperations, iOperation )
+ {
+ CEconOperationDefinition *pOperation = mapOperations[ iOperation ];
+ const CSchemaLootListDefHandle pOperationLootlist( pOperation->GetOperationLootlist() );
+ // Must still be dropping, and be dropping quests
+ if ( CRTime::RTime32TimeCur() < pOperation->GetStopGivingToPlayerDate() && pOperationLootlist && DoesLootlistDropQuests( pOperationLootlist ) )
+ {
+ // If there's a required item and a gateway item
+ if ( pOperation->GetRequiredItemDefIndex() != INVALID_ITEM_DEF_INDEX && pOperation->GetGatewayItemDefIndex() != INVALID_ITEM_DEF_INDEX )
+ {
+ // And the user doesn't have the required item
+ if ( TFInventoryManager()->GetLocalTFInventory()->FindFirstItembyItemDef( pOperation->GetRequiredItemDefIndex() ) == NULL )
+ {
+ // The user needs to get the item
+ pszNoQuestsText = m_pszNeedAPass;
+
+ bool bStoreIsReady = EconUI()->GetStorePanel() && EconUI()->GetStorePanel()->GetPriceSheet() && EconUI()->GetStorePanel()->GetCart() && steamapicontext && steamapicontext->SteamUser();
+ bool bGatewayItemInStore = false;
+ // Check if the gateway item is in the Mann Co Store
+ if ( bStoreIsReady )
+ {
+ bGatewayItemInStore = EconUI()->GetStorePanel()->GetPriceSheet()->GetEntry( pOperation->GetGatewayItemDefIndex() ) != NULL;
+ }
+
+ CEconItemDefinition* pGatewayItemDef = GetItemSchema()->GetItemDefinition( pOperation->GetGatewayItemDefIndex() );
+ Assert( pGatewayItemDef );
+ if ( !pGatewayItemDef )
+ return;
+
+ // Cook up KVs for this item ad
+ KeyValuesAD pKVItemAd( "items" ); // The panel will copy these
+ KeyValues* pKVItem = pKVItemAd->CreateNewKey();
+ pKVItem->SetName( "0" );
+ pKVItem->SetString( "item", pGatewayItemDef->GetDefinitionName() );
+ pKVItem->SetInt( "show_market", bGatewayItemInStore ? 0 : 1 );
+
+ pPassStoreAd->SetVisible( true );
+ pPassStoreAd->SetItemKVs( pKVItemAd );
+
+ // This is the most important thing to communicate. Don't let other operations stomp it.
+ break;
+ }
+ }
+
+ pszNoQuestsText = m_pszNoQuests;
+ // Don't break. Give more important operations a chance to present
+ }
+ }
+
+ SetDialogVariable( "noquests", g_pVGuiLocalize->Find( pszNoQuestsText ) );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CScrollableQuestList::PopulateQuestLists()
+{
+ tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s", __FUNCTION__ );
+ // We dont do anything when a quest is completing
+ if ( m_pCompletingPanel != NULL )
+ return;
+
+ DirtyQuestLayout();
+
+ CUtlVector< CEconItemView * > vecQuestItems;
+ TFInventoryManager()->GetAllQuestItems( &vecQuestItems );
+
+ CUtlVector< CQuestItemPanel* > vecAvailablePanels;
+
+ FOR_EACH_VEC( m_vecQuestItemPanels, i )
+ {
+ bool bFound = false;
+ if ( m_vecQuestItemPanels[ i ]->GetItem() )
+ {
+ FOR_EACH_VEC_BACK( vecQuestItems, j )
+ {
+ // See if the item in the panel is still in the list of items we own
+ if ( m_vecQuestItemPanels[ i ]->GetItem()->GetOriginalID() == vecQuestItems[ j ]->GetOriginalID() )
+ {
+ // Refresh it
+ m_vecQuestItemPanels[ i ]->InvalidateLayout();
+ vecQuestItems.Remove( j );
+ bFound = true;
+ break;
+ }
+ }
+ }
+
+ // Didn't find it. Clear it out
+ if ( !bFound )
+ {
+ if ( m_vecQuestItemPanels[ i ]->GetItem() )
+ {
+ m_vecQuestItemPanels[ i ]->SetItem( NULL );
+ }
+
+ if ( m_vecQuestItemPanels[ i ]->IsSelected() )
+ {
+ SetSelected( m_vecQuestItemPanels[ i ], false );
+ }
+
+ vecAvailablePanels.AddToTail( m_vecQuestItemPanels[ i ] );
+ }
+ }
+
+ for ( int i = 0 ; i < vecQuestItems.Count(); ++i )
+ {
+ CEconItemView *pItem = vecQuestItems[i];
+
+ if ( i < vecAvailablePanels.Count() )
+ {
+ vecAvailablePanels[ i ]->SetItem( pItem );
+ }
+ else if ( i >= m_vecQuestItemPanels.Count() )
+ {
+ Assert( !"Ran out of quest panels!" );
+ }
+ }
+
+ // Sort the panels to make sure they're in order
+ m_vecQuestItemPanels.Sort( &QuestSort_AcquiredTime );
+
+ // Sorting is done manually
+ FOR_EACH_VEC( m_vecQuestItemPanels, i )
+ {
+ m_vecQuestItemPanels[ i ]->SetZPos( i + 1 );
+ }
+
+ PositionQuestItemPanels();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CScrollableQuestList::QuestCompletedResponse()
+{
+ FOR_EACH_VEC( m_vecQuestItemPanels, i )
+ {
+ m_vecQuestItemPanels[i]->QuestCompletedResponse();
+ }
+
+// PopulateQuestLists();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Return true if any quest item panels are in the passed in state
+//-----------------------------------------------------------------------------
+bool CScrollableQuestList::AnyQuestItemPanelsInState( CQuestItemPanel::EItemPanelState_t eState ) const
+{
+ FOR_EACH_VEC( m_vecQuestItemPanels, i )
+ {
+ if ( m_vecQuestItemPanels[i]->GetState() == eState )
+ return true;
+ }
+
+ return false;
+}
+
+//-------------------------------------------------------------------------------------------------------------------
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CQuestLogPanel::CQuestLogPanel( IViewPort *pViewPort )
+ : EditablePanel( NULL, PANEL_QUEST_LOG )
+ , m_bWaitingForComplete( false )
+ , m_pQuestList( NULL )
+ , m_bInventoryDirty( true )
+ , m_iQuestLogKey( BUTTON_CODE_INVALID )
+{
+ if (g_pVGuiLocalize)
+ {
+ g_pVGuiLocalize->AddFile( "resource/tf_quests_%language%.txt" );
+ }
+
+ vgui::HScheme scheme = vgui::scheme()->LoadSchemeFromFileEx( enginevgui->GetPanel( PANEL_CLIENTDLL ), "resource/ClientScheme.res", "ClientScheme");
+ SetScheme(scheme);
+ SetProportional( true );
+
+ ListenForGameEvent( "inventory_updated" );
+ ListenForGameEvent( "gameui_hidden" );
+ ListenForGameEvent( "gc_connected" );
+
+ // Create the item model panel tooltip
+ m_pMouseOverItemPanel = new CItemModelPanel( this, "mouseoveritempanel" );
+ m_pMouseOverTooltip = new CItemModelPanelToolTip( this );
+ m_pMouseOverTooltip->SetupPanels( this, m_pMouseOverItemPanel );
+
+ // Create the text tooltip
+ m_pToolTip = new CQuestTooltip( this );
+ m_pToolTipEmbeddedPanel = new vgui::EditablePanel( this, "TooltipPanel" );
+ m_pToolTipEmbeddedPanel->SetKeyBoardInputEnabled( false );
+ m_pToolTipEmbeddedPanel->SetMouseInputEnabled( false );
+// m_pToolTipEmbeddedPanel->MakePopup();
+ m_pToolTip->SetEmbeddedPanel( m_pToolTipEmbeddedPanel );
+ m_pToolTip->SetTooltipDelay( 0 );
+
+ EditablePanel *pMainContainer = new EditablePanel( this, "MainContainer" );
+ m_pQuestList = new CScrollableQuestList( pMainContainer, "QuestList" );
+
+ m_pProgressPanel = new EditablePanel( this, "ProgressPanel" );
+
+ m_pDebugButton = new CExButton( pMainContainer, "Options", "Options", this, "open_debug_menu" );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Look into the moused-over panel and take "tiptext" from its dialog
+// variables and set it as our own.
+//-----------------------------------------------------------------------------
+void CQuestTooltip::ShowTooltip( Panel *pCurrentPanel )
+{
+ EditablePanel* pEditableCurrentPanel = dynamic_cast< EditablePanel* >( pCurrentPanel );
+ if ( pEditableCurrentPanel )
+ {
+ KeyValues* pKVVariables = pEditableCurrentPanel->GetDialogVariables();
+ const wchar_t *pwszTipText = pKVVariables->GetWString( "tiptext", L"" );
+ m_pEmbeddedPanel->SetDialogVariable( "tiptext", pwszTipText );
+ }
+
+ BaseClass::ShowTooltip( pCurrentPanel );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Position ourselves down and to the right as far as posible
+//-----------------------------------------------------------------------------
+void CQuestTooltip::PositionWindow( Panel *pTipPanel )
+{
+ tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s", __FUNCTION__ );
+ int iTipW, iTipH;
+ pTipPanel->GetSize( iTipW, iTipH );
+
+ int cursorX, cursorY;
+ vgui::input()->GetCursorPos(cursorX, cursorY);
+
+ int px, py, wide, tall;
+ ipanel()->GetAbsPos( m_pEmbeddedPanel->GetParent()->GetVPanel(), px, py );
+ m_pEmbeddedPanel->GetParent()->GetSize(wide, tall);
+
+ if ( !m_pEmbeddedPanel->IsPopup() )
+ {
+ // Move the cursor into our parent space
+ cursorX -= px;
+ cursorY -= py;
+ }
+
+ // Dangle as far down and as far right as possible
+ int nXPos = cursorX - Max( 0, ( ( iTipW + cursorX ) - wide ) );
+ int nYPos = ( cursorY + 20 )- Max( 0, ( ( iTipH + cursorY + 20 ) - tall ) ) ;
+
+ pTipPanel->SetPos( nXPos, nYPos );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CQuestLogPanel::~CQuestLogPanel()
+{
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CQuestLogPanel::AttachToGameUI( void )
+{
+ C_CTFGameStats::ImmediateWriteInterfaceEvent( "interface_open", "quest_log_panel" );
+
+ if ( GetClientModeTFNormal()->GameUI() )
+ {
+ GetClientModeTFNormal()->GameUI()->SetMainMenuOverride( GetVPanel() );
+ }
+
+ SetKeyBoardInputEnabled( true );
+ SetMouseInputEnabled( true );
+ SetCursor(dc_arrow);
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+const char *CQuestLogPanel::GetName( void )
+{
+ return PANEL_QUEST_LOG;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CQuestLogPanel::ApplySchemeSettings( IScheme *pScheme )
+{
+ tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s", __FUNCTION__ );
+ BaseClass::ApplySchemeSettings( pScheme );
+
+ const char *pszResFile = "Resource/UI/econ/QuestLogPanel.res";
+
+ // Check if the operation wants to override our default res file
+ const auto& mapOperations = GetItemSchema()->GetOperationDefinitions();
+ FOR_EACH_MAP_FAST( mapOperations, i )
+ {
+ CEconOperationDefinition* pOperation = mapOperations[i];
+ if ( pOperation->IsActive() && pOperation->IsCampaign() )
+ {
+ // Use the first found for now
+ if ( pOperation->GetQuestLogOverrideResFile() )
+ {
+ pszResFile = pOperation->GetQuestLogOverrideResFile();
+ }
+ break;
+ }
+ }
+
+ LoadControlSettings( pszResFile );
+
+ g_spItemTooltip = m_pMouseOverTooltip;
+ g_spTextTooltip = m_pToolTip;
+
+ // The outer dim / close button
+ {
+ Button* pButton = FindControl<Button>( "OutsideCloseButton" );
+ if ( pButton )
+ {
+ pButton->AddActionSignalTarget( this );
+ pButton->SetPaintBackgroundEnabled( false );
+ }
+ }
+
+ m_pQuestList->InvalidateLayout( false, true );
+
+ Assert( m_pQuestList );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CQuestLogPanel::PerformLayout()
+{
+ BaseClass::PerformLayout();
+
+ if ( GetUniverse() != k_EUniversePublic )
+ {
+ int x, y;
+ m_pDebugButton->GetParent()->GetPos( x, y );
+ int w, h;
+ m_pDebugButton->GetParent()->GetSize( w, h );
+ m_pDebugButton->SizeToContents();
+ m_pDebugButton->SetVisible( true );
+ m_pDebugButton->SetPos( x + w - m_pDebugButton->GetWide() - 60, y + 15 );
+ m_pDebugButton->SetZPos( 1000 );
+ }
+ else
+ {
+ m_pDebugButton->SetVisible( false );
+ }
+
+ UpdateQuestsItemPanels();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CQuestLogPanel::OnCommand( const char *pCommand )
+{
+ if ( FStrEq( pCommand, "close" ) )
+ {
+ if ( enginevgui->IsGameUIVisible() )
+ {
+ ShowPanel( false );
+ }
+ else
+ {
+ IViewPortPanel *pQuestLog = ( gViewPortInterface->FindPanelByName( PANEL_QUEST_LOG ) );
+ if ( pQuestLog )
+ {
+ gViewPortInterface->ShowPanel( pQuestLog, false );
+ }
+ }
+ }
+ else if ( Q_stricmp( "open_debug_menu", pCommand ) == 0 )
+ {
+ if ( GetUniverse() == k_EUniverseBeta || GetUniverse() == k_EUniverseDev )
+ {
+ const char *pszContextMenuBorder = "NotificationDefault";
+ const char *pszContextMenuFont = "HudFontMediumSecondary";
+
+ Menu *pContextMenu = new Menu( this, "ContextMenu" );
+ pContextMenu->SetBorder( scheme()->GetIScheme( GetScheme() )->GetBorder( pszContextMenuBorder ) );
+ pContextMenu->SetFont( scheme()->GetIScheme( GetScheme() )->GetFont( pszContextMenuFont ) );
+
+
+ MenuBuilder contextMenuBuilder( pContextMenu, this );
+
+ const auto& mapOperations = GetItemSchema()->GetOperationDefinitions();
+ FOR_EACH_MAP_FAST( mapOperations, iOperation )
+ {
+ bool bHasAnyQuests = false;
+ Menu* pOperationSubMenu = NULL;
+
+ CEconOperationDefinition *pOperation = mapOperations[ iOperation ];
+ const CEconLootListDefinition *pLootListDef = GetItemSchema()->GetLootListByName( pOperation->GetOperationLootlist() );
+ if ( pLootListDef )
+ {
+ auto& vecContents = pLootListDef->GetLootListContents();
+ FOR_EACH_VEC( vecContents, i )
+ {
+ if ( vecContents[i].m_iItemOrLootlistDef > 0 )
+ {
+ const GameItemDefinition_t* pItemDef = (GameItemDefinition_t*)GetItemSchema()->GetItemDefinition( vecContents[i].m_iItemOrLootlistDef );
+ if ( pItemDef && pItemDef->GetQuestDef() )
+ {
+ if ( !bHasAnyQuests )
+ {
+ bHasAnyQuests = true;
+
+ pOperationSubMenu = new Menu( this, "OperationSubMenu" );
+ pOperationSubMenu->SetBorder( scheme()->GetIScheme( GetScheme() )->GetBorder( pszContextMenuBorder ) );
+ pOperationSubMenu->SetFont( scheme()->GetIScheme( GetScheme() )->GetFont( pszContextMenuFont ) );
+
+ contextMenuBuilder.AddCascadingMenuItem( pOperation->GetName(), pOperationSubMenu, "operations" );
+ }
+
+ pOperationSubMenu->AddMenuItem( pItemDef->GetItemBaseName(), CFmtStr( "give%s", pItemDef->GetDefinitionName() ), this );
+ }
+ }
+ }
+ }
+ }
+
+ bool bHasAnyQuests = false;
+ Menu* pAllSubMenu = NULL;
+
+ FOR_EACH_MAP_FAST( GetItemSchema()->GetItemDefinitionMap(), i )
+ {
+ const GameItemDefinition_t* pItemDef = (GameItemDefinition_t*)GetItemSchema()->GetItemDefinitionMap()[ i ];
+ if ( pItemDef->GetQuestDef() )
+ {
+ if ( !bHasAnyQuests )
+ {
+ bHasAnyQuests = true;
+
+ pAllSubMenu = new Menu( this, "AllSubMenu" );
+ pAllSubMenu->SetBorder( scheme()->GetIScheme( GetScheme() )->GetBorder( pszContextMenuBorder ) );
+ pAllSubMenu->SetFont( scheme()->GetIScheme( GetScheme() )->GetFont( pszContextMenuFont ) );
+
+ contextMenuBuilder.AddCascadingMenuItem( "all", pAllSubMenu, "all" );
+ }
+
+ pAllSubMenu->AddMenuItem( pItemDef->GetItemBaseName(), CFmtStr( "give%s", pItemDef->GetDefinitionName() ), this );
+ }
+ }
+
+ // Position to the cursor's position
+ int nX, nY;
+ g_pVGuiInput->GetCursorPosition( nX, nY );
+ pContextMenu->SetPos( nX - 1, nY - 1 );
+
+ pContextMenu->SetVisible(true);
+ pContextMenu->AddActionSignalTarget(this);
+ }
+ }
+ else if ( Q_strnicmp( "give", pCommand, 4 ) == 0 )
+ {
+ if ( GetUniverse() != k_EUniversePublic )
+ {
+ if ( !steamapicontext || !steamapicontext->SteamUser() )
+ {
+ Msg("Not connected to Steam.\n");
+ return;
+ }
+
+ CSteamID steamIDForPlayer = steamapicontext->SteamUser()->GetSteamID();
+ if ( !steamIDForPlayer.IsValid() )
+ {
+ Msg("Failed to find a valid steamID for the local player.\n");
+ return;
+ }
+
+ const char* pszItemToGive = pCommand + 4;
+ Msg( "Sending request to generate '%s' for Local Player (%llu)\n", pszItemToGive, steamIDForPlayer.ConvertToUint64() );
+
+ CItemSelectionCriteria criteria;
+
+ GCSDK::CProtoBufMsg<CMsgDevNewItemRequest> msg( k_EMsgGCDev_NewItemRequest );
+ msg.Body().set_receiver( steamIDForPlayer.ConvertToUint64() );
+
+ criteria.SetIgnoreEnabledFlag( true );
+ if ( !criteria.BAddCondition( "name", k_EOperator_String_EQ, pszItemToGive, true ) ||
+ !criteria.BSerializeToMsg( *msg.Body().mutable_criteria() ) )
+ {
+ Msg( "Failed to add condition and/or serialize item grant request. This is probably caused by having a string that's too long.\n" );
+ return;
+ }
+ GCClientSystem()->BSendMessage( msg );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CQuestLogPanel::FireGameEvent( IGameEvent *event )
+{
+ tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s", __FUNCTION__ );
+ // Listen for inventory updates in case our item gets changed while the user
+ // is looking at us. We want to re-do our entire layout since a quest might
+ // have been equipped / destroyed / completed and we need to re-categorize
+ // all the user's quests.
+ if ( FStrEq( event->GetName(), "inventory_updated" ) && !m_bWaitingForComplete )
+ {
+ m_bInventoryDirty = true;
+
+ if ( IsVisible() )
+ {
+ if ( m_pQuestList )
+ {
+ m_pQuestList->PopulateQuestLists();
+ m_pQuestList->UpdateEmptyMessage();
+ }
+
+ UpdateQuestsItemPanels();
+ }
+ }
+ else if ( FStrEq( event->GetName(), "gameui_hidden" ) )
+ {
+ ShowPanel( false );
+ return;
+ }
+ else if ( FStrEq( event->GetName(), "gc_connected" ) )
+ {
+ m_bInventoryDirty = true;
+ InvalidateLayout( false, true );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CQuestLogPanel::ShowPanel( bool bShow )
+{
+ // Snag this so we know what to listen for
+ m_iQuestLogKey = gameuifuncs->GetButtonCodeForBind( "show_quest_log" );
+
+ if ( m_pQuestList && bShow )
+ {
+ m_pQuestList->SetSelected( NULL, true );
+ m_pQuestList->DirtyQuestLayout();
+ }
+
+ SetVisible( bShow );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CQuestLogPanel::OnKeyCodePressed( KeyCode code )
+{
+ if ( code == m_iQuestLogKey || code == STEAMCONTROLLER_B )
+ {
+ ShowPanel( false );
+ return;
+ }
+
+ BaseClass::OnKeyCodePressed( code );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CQuestLogPanel::OnKeyCodeTyped( KeyCode code )
+{
+ if ( code == KEY_ESCAPE )
+ {
+ if ( IsVisible() )
+ {
+ SetVisible( false );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CQuestLogPanel::SetVisible( bool bState )
+{
+ tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s", __FUNCTION__ );
+ // Default to showing the active quests upon opening
+ if ( bState == true )
+ {
+ UpdateQuestsItemPanels();
+
+ IGameEvent *event = gameeventmanager->CreateEvent( "questlog_opened" );
+ if ( event )
+ {
+ gameeventmanager->FireEventClientSide( event );
+ }
+
+ if ( enginevgui->IsGameUIVisible() )
+ {
+ AttachToGameUI();
+ }
+ else
+ {
+ ipanel()->SetParent( GetVPanel(), VGui_GetClientDLLRootPanel() );
+
+ MakePopup( false, true );
+ SetKeyBoardInputEnabled( true );
+ SetMouseInputEnabled( true );
+ MoveToFront();
+ }
+
+ engine->ClientCmd_Unrestricted( "gameui_preventescapetoshow\n" );
+ vgui::surface()->PlaySound( "ui/panel_open.wav" );
+ }
+ else if ( IsVisible() )
+ {
+ // Detach from the GameUI when we hide
+ IViewPortPanel *pMMOverride = gViewPortInterface->FindPanelByName( PANEL_MAINMENUOVERRIDE );
+ if ( pMMOverride )
+ {
+ ((CHudMainMenuOverride*)pMMOverride)->AttachToGameUI();
+ }
+
+ engine->ClientCmd_Unrestricted( "gameui_allowescapetoshow\n" );
+ vgui::surface()->PlaySound( "ui/panel_close.wav" );
+ }
+
+ BaseClass::SetVisible( bState );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CQuestLogPanel::QuestCompletedResponse()
+{
+ m_bWaitingForComplete = false;
+ m_bInventoryDirty = true;
+
+ if ( m_pQuestList )
+ m_pQuestList->QuestCompletedResponse();
+
+ //UpdateQuestsItemPanels();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CQuestLogPanel::UpdateQuestsItemPanels()
+{
+ UpdateBadgeProgressPanels();
+
+ tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s", __FUNCTION__ );
+ if ( m_bInventoryDirty )
+ {
+ m_pQuestList->QuestCompletedResponse();
+
+ if ( m_pQuestList )
+ {
+ m_pQuestList->PopulateQuestLists();
+ m_pQuestList->UpdateEmptyMessage();
+ }
+ }
+
+ m_bInventoryDirty = false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CQuestLogPanel::MarkQuestsDirty()
+{
+ m_bInventoryDirty = true;
+
+ if ( IsVisible() )
+ {
+ UpdateQuestsItemPanels();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Return true if any quest item panels are in the passed in state
+//-----------------------------------------------------------------------------
+bool CQuestLogPanel::AnyQuestItemPanelsInState( CQuestItemPanel::EItemPanelState_t eState ) const
+{
+ return m_pQuestList->AnyQuestItemPanelsInState( eState );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CQuestLogPanel::OnCompleteQuest( void )
+{
+ m_bWaitingForComplete = true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CQuestLogPanel::UpdateBadgeProgressPanels()
+{
+ CEconOperationDefinition *pCurrentOperation = NULL;
+ const auto& mapOperations = GetItemSchema()->GetOperationDefinitions();
+ FOR_EACH_MAP_FAST( mapOperations, i )
+ {
+ CEconOperationDefinition* pOperation = mapOperations[i];
+ if ( pOperation->IsActive() && pOperation->IsCampaign() )
+ {
+ pCurrentOperation = pOperation;
+ break;
+ }
+ }
+
+ if ( pCurrentOperation )
+ {
+ CEconItemView *pCoin = TFInventoryManager()->GetLocalTFInventory()->FindFirstItembyItemDef( pCurrentOperation->GetRequiredItemDefIndex() );
+ if ( pCoin )
+ {
+ m_pProgressPanel->SetVisible( true );
+ CItemModelPanel *pCoinPanel = m_pProgressPanel->FindControl< CItemModelPanel >( "CoinModelPanel" );
+ if ( pCoinPanel )
+ {
+ pCoinPanel->SetItem( pCoin );
+ }
+
+ uint32 nNumCompletedContracts = 0;
+ {
+ uint32 nCompletedContractsTemp = 0;
+ GetKilleaterValueByEvent( pCoin, kKillEaterEvent_CosmeticOperationContractsCompleted, nCompletedContractsTemp );
+ nNumCompletedContracts = Max( nNumCompletedContracts, nCompletedContractsTemp );
+ // Halloween has it's own thing for whatever reason
+ GetKilleaterValueByEvent( pCoin, kKillEaterEvent_HalloweenContractsCompleted, nCompletedContractsTemp );
+ nNumCompletedContracts = Max( nNumCompletedContracts, nCompletedContractsTemp );
+ Assert( pCurrentOperation->GetMaxDropCount() > 0 );
+ }
+
+ uint32 nNumContractPoints = 0;
+ {
+ uint32 nContractsPointsTemp = 0;
+ GetKilleaterValueByEvent( pCoin, kKillEaterEvent_CosmeticOperationContractsPoints, nContractsPointsTemp );
+ nNumContractPoints = Max( nContractsPointsTemp, nNumContractPoints );
+ // Halloween has it's own thing for whatever reason
+ GetKilleaterValueByEvent( pCoin, kKillEaterEvent_HalloweenSouls, nContractsPointsTemp );
+ nNumContractPoints = Max( nContractsPointsTemp, nNumContractPoints );
+ }
+
+ EditablePanel *pBadgeContainer = m_pProgressPanel->FindControl< EditablePanel >( "BadgeMeterContainer" );
+ if ( pBadgeContainer )
+ {
+ ContinuousProgressBar *pBadgeProgressBar = pBadgeContainer->FindControl< ContinuousProgressBar >( "BadgeProgressMeter" );
+ if ( pBadgeProgressBar )
+ {
+ const char *pszLevelingDataName = GetItemSchema()->GetKillEaterScoreTypeLevelingDataName( kKillEaterEvent_HalloweenSouls );
+ Assert( pszLevelingDataName );
+
+ const CUtlVector<CItemLevelingDefinition> *pLevelingData = GetItemSchema()->GetItemLevelingData( pszLevelingDataName );
+ Assert( pLevelingData );
+
+ int nRequiredPointsToNextRank = 0;
+ int nRequiredPointsToCurrentRank = 0;
+ const char *pszCurrentLevelName = NULL;
+ FOR_EACH_VEC( (*pLevelingData), i )
+ {
+ pszCurrentLevelName = (*pLevelingData)[i].GetNameLocalizationKey();
+
+ const uint32 nRank = (*pLevelingData)[i].GetRequiredScore();
+ if ( nNumContractPoints < nRank )
+ {
+ nRequiredPointsToNextRank = nRank;
+ break;
+ }
+ else
+ {
+ nRequiredPointsToCurrentRank = nRank;
+ }
+ }
+
+ // if no next level, just use max points
+ if ( nRequiredPointsToNextRank == 0 )
+ {
+ // assuming each contract's worth 130 (100 point + 30 bonus)
+ nRequiredPointsToNextRank = pCurrentOperation->GetMaxDropCount() * 130;
+ }
+
+ float flProgress = (float)( nNumContractPoints - nRequiredPointsToCurrentRank ) / (float)( nRequiredPointsToNextRank - nRequiredPointsToCurrentRank );
+ pBadgeProgressBar->SetProgress( flProgress );
+
+ CExLabel *pLabel = m_pProgressPanel->FindControl< CExLabel >( "BadgeProgressLabel" );
+ if ( pLabel )
+ {
+ pLabel->SetText( CConstructLocalizedString( g_pVGuiLocalize->Find( "QuestLog_BadgeProgress" ), g_pVGuiLocalize->Find( pszCurrentLevelName ) ) );
+ }
+
+ CExLabel *pScoreLabel = pBadgeContainer->FindControl< CExLabel >( "BadgeProgressMeterText" );
+ if ( pScoreLabel )
+ {
+ pScoreLabel->SetText( CFmtStr( "%d/%d", nNumContractPoints, nRequiredPointsToNextRank ) );
+ }
+ }
+ }
+
+ EditablePanel *pContractContainer = m_pProgressPanel->FindControl< EditablePanel >( "ContractMeterContainer" );
+ if ( pContractContainer )
+ {
+ ContinuousProgressBar *pContractProgressBar = pContractContainer->FindControl< ContinuousProgressBar >( "ContractsCompletedProgressMeter" );
+ if ( pContractProgressBar )
+ {
+ pContractProgressBar->SetProgress( (float)nNumCompletedContracts / (float)pCurrentOperation->GetMaxDropCount() );
+
+ CExLabel *pLabel = pContractContainer->FindControl< CExLabel >( "ContractsCompletedProgressMeterText" );
+ if ( pLabel )
+ {
+ pLabel->SetText( CFmtStr( "%d", nNumCompletedContracts ) );
+ }
+ }
+ }
+ }
+ else
+ {
+ m_pProgressPanel->SetVisible( false );
+ }
+ }
+ else
+ {
+ m_pProgressPanel->SetVisible( false );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: GC Msg handler for when a quest has been completed
+//-----------------------------------------------------------------------------
+class CGCCompleteQuestCompleteResponse : public GCSDK::CGCClientJob
+{
+public:
+ CGCCompleteQuestCompleteResponse( GCSDK::CGCClient *pClient ) : GCSDK::CGCClientJob( pClient ) {}
+
+ virtual bool BYieldingRunGCJob( GCSDK::IMsgNetPacket *pNetPacket )
+ {
+ GCSDK::CGCMsg<MsgGCStandardResponse_t> msg( pNetPacket );
+
+ itemid_t nNewToolID = 0;
+ if( !msg.BReadUint64Data( &nNewToolID ) )
+ return true;
+
+ CQuestLogPanel *pQuestLog = GetQuestLog();
+ if ( pQuestLog )
+ {
+ pQuestLog->QuestCompletedResponse();
+ }
+
+ return true;
+ }
+};
+
+GC_REG_JOB( GCSDK::CGCClient, CGCCompleteQuestCompleteResponse, "CGCCompleteQuestCompleteResponse", k_EMsgGCQuestCompleted, GCSDK::k_EServerTypeGCClient );
+
+
+#ifdef STAGING_ONLY
+static void cc_tf_quest_log_reload()
+{
+ CQuestLogPanel *pQuestLog = GetQuestLog();
+ if ( pQuestLog )
+ {
+ pQuestLog->MarkQuestsDirty();
+ pQuestLog->InvalidateLayout( true, true );
+ gViewPortInterface->ShowPanel( pQuestLog, true );
+ }
+}
+ConCommand tf_quest_log_reload( "tf_quest_log_reload", cc_tf_quest_log_reload );
+#endif