Modified source engine (2017) developed by valve and leaked in 2020. Not for commercial purporses
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1183 lines
37 KiB

5 years ago
//========= 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