diff options
| author | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
|---|---|---|
| committer | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
| commit | 3bf9df6b2785fa6d951086978a3e66f49427166a (patch) | |
| tree | 2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /game/client/tf/vgui/quest_item_panel.cpp | |
| download | archived-source-engine-2018-hl2-src-master.tar.xz archived-source-engine-2018-hl2-src-master.zip | |
Diffstat (limited to 'game/client/tf/vgui/quest_item_panel.cpp')
| -rw-r--r-- | game/client/tf/vgui/quest_item_panel.cpp | 1625 |
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 ); |