Team Fortress 2 Source Code as on 22/4/2020
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.

1625 lines
53 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================//
  6. #include "cbase.h"
  7. #include "quest_item_panel.h"
  8. #include "tf_hud_item_progress_tracker.h"
  9. #include "quest_log_panel.h"
  10. #include "c_tf_player.h"
  11. #include "econ_item_description.h"
  12. #include "clientmode_tf.h"
  13. #include <vgui_controls/AnimationController.h>
  14. #include "quest_objective_manager.h"
  15. #include "econ_quests.h"
  16. #include "confirm_dialog.h"
  17. #include "tf_quest_restriction.h"
  18. #include "item_model_panel.h"
  19. #include "tf_gc_client.h"
  20. // memdbgon must be the last include file in a .cpp file!!!
  21. #include <tier0/memdbgon.h>
  22. void AddSubKeyNamed( KeyValues *pKeys, const char *pszName );
  23. const float k_flQuestDecodeTime = 2.f;
  24. const float k_flQuestTurnInTime = 5.f;
  25. 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" );
  26. extern CQuestLogPanel *GetQuestLog();
  27. extern CQuestTooltip* g_spTextTooltip;
  28. extern const char *s_pszMMTypes[kMatchmakingTypeCount];
  29. extern const char *s_pszGameModes[eNumGameCategories];
  30. void SelectGroup( EMatchmakingGroupType eGroup, bool bSelected );
  31. void SelectCategory( EGameCategory eCategory, bool bSelected );
  32. void PromptOrFireCommand( const char* pszCommand );
  33. static void ConfirmDiscardQuest( bool bConfirmed, void* pContext )
  34. {
  35. CQuestItemPanel *pQuestItemPanel = ( CQuestItemPanel* )pContext;
  36. if ( pQuestItemPanel )
  37. {
  38. pQuestItemPanel->OnConfirmDelete( bConfirmed );
  39. }
  40. }
  41. static void ConfirmEquipLoaners( bool bConfirmed, void* pContext )
  42. {
  43. CQuestItemPanel *pQuestItemPanel = ( CQuestItemPanel* )pContext;
  44. if ( pQuestItemPanel )
  45. {
  46. pQuestItemPanel->OnConfirmEquipLoaners( bConfirmed );
  47. }
  48. }
  49. static void ConfirmTurnInQuest( bool bConfirmed, void* pContext )
  50. {
  51. if ( bConfirmed )
  52. {
  53. CQuestItemPanel *pQuestItemPanel = (CQuestItemPanel*)pContext;
  54. if ( pQuestItemPanel )
  55. {
  56. pQuestItemPanel->OnCompleteQuest();
  57. }
  58. }
  59. }
  60. //-----------------------------------------------------------------------------
  61. // Purpose: fill vecLoanerItems with loaners def indices from pQuest
  62. //-----------------------------------------------------------------------------
  63. static int GetLoanerListFromQuest( const CEconItemView *pQuest, CUtlVector< item_definition_index_t >& vecLoanerItems )
  64. {
  65. if ( !pQuest )
  66. return 0;
  67. // loaners from the quest
  68. const CUtlVector< CTFRequiredQuestItemsSet >& vecQuestRequiredItems = pQuest->GetItemDefinition()->GetQuestDef()->GetRequiredItemSets();
  69. FOR_EACH_VEC( vecQuestRequiredItems, i )
  70. {
  71. // don't add dups
  72. if ( vecLoanerItems.Find( vecQuestRequiredItems[i].GetLoanerItemDef() ) == vecLoanerItems.InvalidIndex() )
  73. {
  74. vecLoanerItems.AddToTail( vecQuestRequiredItems[i].GetLoanerItemDef() );
  75. }
  76. }
  77. // loaners from the objectives
  78. //{
  79. // // Get all the objectives
  80. // QuestObjectiveDefVec_t vecChosenObjectives;
  81. // pQuest->GetItemDefinition()->GetQuestDef()->GetRolledObjectivesForItem( vecChosenObjectives, pQuest );
  82. // // Get all the items we need to give as loaners from the objectives
  83. // FOR_EACH_VEC( vecChosenObjectives, i )
  84. // {
  85. // const CUtlVector< CTFRequiredQuestItemsSet >& vecObjectiveRequiredItems = vecChosenObjectives[ i ]->GetConditions()->GetRequiredItemSets();
  86. // FOR_EACH_VEC( vecObjectiveRequiredItems, iRequired )
  87. // {
  88. // // don't add dups
  89. // if ( vecLoanerItems.Find( vecObjectiveRequiredItems[ iRequired ].GetLoanerItemDef() ) == vecLoanerItems.InvalidIndex() )
  90. // {
  91. // vecLoanerItems.AddToTail( vecObjectiveRequiredItems[ iRequired ].GetLoanerItemDef() );
  92. // }
  93. // }
  94. // }
  95. //}
  96. return vecLoanerItems.Count();
  97. }
  98. //-----------------------------------------------------------------------------
  99. // Purpose: fill vecGrantedLoaners with granted loaners from specific quest ID
  100. //-----------------------------------------------------------------------------
  101. static int GetLoanersFromLocalInventory( const itemid_t& questID, const CUtlVector< item_definition_index_t >& vecLoanerItems, CUtlVector< CEconItemView* >& vecGrantedLoaners )
  102. {
  103. if ( vecLoanerItems.Count() > 0 )
  104. {
  105. CPlayerInventory *pLocalInv = InventoryManager()->GetLocalInventory();
  106. int nCount = pLocalInv->GetItemCount();
  107. for ( int i = 0; i < nCount; ++i )
  108. {
  109. CEconItemView* pItem = pLocalInv->GetItem( i );
  110. bool bIsLoaner = false;
  111. // check if the item is a loaner and is associated with this quest
  112. FOR_EACH_VEC( vecLoanerItems, iLoaner )
  113. {
  114. if ( vecLoanerItems[iLoaner] == pItem->GetItemDefIndex() && GetAssociatedQuestItemID( pItem ) == questID )
  115. {
  116. bIsLoaner = true;
  117. break;
  118. }
  119. }
  120. // already granted this loaner, remove from the list to give
  121. if ( bIsLoaner )
  122. {
  123. vecGrantedLoaners.AddToTail( pItem );
  124. }
  125. // found all given loaners
  126. if ( vecLoanerItems.Count() == vecGrantedLoaners.Count() )
  127. {
  128. break;
  129. }
  130. }
  131. }
  132. return vecGrantedLoaners.Count();
  133. }
  134. DECLARE_BUILD_FACTORY( CInputProxyPanel )
  135. CInputProxyPanel::CInputProxyPanel( Panel *parent, const char *pszPanelName )
  136. : BaseClass( parent, pszPanelName )
  137. {}
  138. void CInputProxyPanel::AddPanelForCommand( EInputTypes eInputType, Panel* pPanel, const char* pszCommand )
  139. {
  140. m_vecRedirectPanels[ eInputType ].AddToTail( { pPanel, pszCommand } );
  141. }
  142. void CInputProxyPanel::OnCursorMoved( int x, int y )
  143. {
  144. FOR_EACH_VEC( m_vecRedirectPanels[ INPUT_MOUSE_MOVE ], i )
  145. {
  146. PostMessage( m_vecRedirectPanels[ INPUT_MOUSE_MOVE ][ i ].m_pPanel, new KeyValues( m_vecRedirectPanels[ INPUT_MOUSE_MOVE ][ i ].m_pszCommand, "x", x, "y", y ) );
  147. }
  148. }
  149. void CInputProxyPanel::OnCursorEntered()
  150. {
  151. FOR_EACH_VEC( m_vecRedirectPanels[ INPUT_MOUSE_ENTER ], i )
  152. {
  153. PostMessage( m_vecRedirectPanels[ INPUT_MOUSE_ENTER ][ i ].m_pPanel, new KeyValues( m_vecRedirectPanels[ INPUT_MOUSE_ENTER ][ i ].m_pszCommand ) );
  154. }
  155. }
  156. void CInputProxyPanel::OnCursorExited()
  157. {
  158. FOR_EACH_VEC( m_vecRedirectPanels[ INPUT_MOUSE_EXIT ], i )
  159. {
  160. PostMessage( m_vecRedirectPanels[ INPUT_MOUSE_EXIT ][ i ].m_pPanel, new KeyValues( m_vecRedirectPanels[ INPUT_MOUSE_EXIT ][ i ].m_pszCommand ) );
  161. }
  162. }
  163. void CInputProxyPanel::OnMousePressed(MouseCode code)
  164. {
  165. FOR_EACH_VEC( m_vecRedirectPanels[ INPUT_MOUSE_PRESS ], i )
  166. {
  167. PostMessage( m_vecRedirectPanels[ INPUT_MOUSE_PRESS ][ i ].m_pPanel, new KeyValues( m_vecRedirectPanels[ INPUT_MOUSE_PRESS ][ i ].m_pszCommand, "code", code ) );
  168. }
  169. }
  170. void CInputProxyPanel::OnMouseDoublePressed(MouseCode code)
  171. {
  172. FOR_EACH_VEC( m_vecRedirectPanels[ INPUT_MOUSE_DOUBLE_PRESS ], i )
  173. {
  174. PostMessage( m_vecRedirectPanels[ INPUT_MOUSE_DOUBLE_PRESS ][ i ].m_pPanel, new KeyValues( m_vecRedirectPanels[ INPUT_MOUSE_DOUBLE_PRESS ][ i ].m_pszCommand, "code", code ) );
  175. }
  176. }
  177. void CInputProxyPanel::OnMouseReleased(MouseCode code)
  178. {
  179. FOR_EACH_VEC( m_vecRedirectPanels[ INPUT_MOUSE_RELEASED ], i )
  180. {
  181. PostMessage( m_vecRedirectPanels[ INPUT_MOUSE_RELEASED ][ i ].m_pPanel, new KeyValues( m_vecRedirectPanels[ INPUT_MOUSE_RELEASED ][ i ].m_pszCommand, "code", code ) );
  182. }
  183. }
  184. void CInputProxyPanel::OnMouseWheeled(int delta)
  185. {
  186. FOR_EACH_VEC( m_vecRedirectPanels[ INPUT_MOUSE_WHEEL ], i )
  187. {
  188. PostMessage( m_vecRedirectPanels[ INPUT_MOUSE_WHEEL ][ i ].m_pPanel, new KeyValues( m_vecRedirectPanels[ INPUT_MOUSE_WHEEL ][ i ].m_pszCommand, "delta", delta ) );
  189. }
  190. }
  191. DECLARE_BUILD_FACTORY( CQuestStatusPanel )
  192. CQuestStatusPanel::CQuestStatusPanel( Panel *parent, const char *pszPanelName )
  193. : EditablePanel( parent, pszPanelName )
  194. , m_pMovingContainer( NULL )
  195. , m_bShouldBeVisible( false )
  196. {
  197. m_pMovingContainer = new EditablePanel( this, "movingcontainer" );
  198. m_transitionTimer.Invalidate();
  199. }
  200. void CQuestStatusPanel::SetShow( bool bShow )
  201. {
  202. if ( bShow != m_bShouldBeVisible )
  203. {
  204. m_transitionTimer.Start( 0.6f );
  205. }
  206. m_bShouldBeVisible = bShow;
  207. SetVisible( m_bShouldBeVisible );
  208. }
  209. void CQuestStatusPanel::OnThink()
  210. {
  211. BaseClass::OnThink();
  212. const int nStartY = m_bShouldBeVisible ? m_iHiddenY : m_iVisibleY;
  213. const int nEndY = m_bShouldBeVisible ? m_iVisibleY : m_iHiddenY;
  214. float flProgress = 1.f;
  215. if ( !m_transitionTimer.IsElapsed() )
  216. {
  217. flProgress = Bias( RemapValClamped( m_transitionTimer.GetElapsedTime(), 0.f , m_transitionTimer.GetCountdownDuration(), 0.f, 1.f ), 0.7f );
  218. }
  219. flProgress = RemapVal( flProgress, 0.f, 1.f, (float)nStartY, (float)nEndY );
  220. m_pMovingContainer->SetPos( m_pMovingContainer->GetXPos(), flProgress );
  221. SetVisible( m_bShouldBeVisible || flProgress > 0.f );
  222. }
  223. //-----------------------------------------------------------------------------
  224. // Purpose:
  225. //-----------------------------------------------------------------------------
  226. CQuestItemPanel::CQuestItemPanel( Panel *parent, const char *pszPanelName, CEconItemView* pQuestItem, CScrollableQuestList* pQuestList )
  227. : EditablePanel( parent, pszPanelName )
  228. , m_hQuestItem( NULL )
  229. , m_eState( STATE_NORMAL )
  230. , m_pTurnInContainer( NULL )
  231. , m_pTurnInDimmer( NULL )
  232. , m_pszCompleteSound( NULL )
  233. , m_pFrontFolderContainer( NULL )
  234. , m_pBackFolderContainer( NULL )
  235. , m_bCollapsed( true )
  236. , m_pQuestList( pQuestList )
  237. , m_pQuestPaperContainer( NULL )
  238. , m_pTitleButton( NULL )
  239. , m_pIdentifyContainer( NULL )
  240. , m_pIdentifyDimmer( NULL )
  241. , m_pKVCipherStrings( NULL )
  242. , m_pPhotoStatic( NULL )
  243. , m_pFlavorScrollingContainer( NULL )
  244. , m_pTurningInLabel( NULL )
  245. , m_pFindServerButton( NULL )
  246. , m_pLoanerContainerPanel( NULL )
  247. , m_pRequestLoanerItemsButton( NULL )
  248. , m_pEquipLoanerItemsButton( NULL )
  249. , m_pItemTrackerPanel( NULL )
  250. , m_pKVItemTracker( NULL )
  251. , m_pObjectiveExplanationLabel( NULL )
  252. , m_pEncodedStatus( NULL )
  253. , m_pInactiveStatus( NULL )
  254. , m_pReadyToTurnInStatus( NULL )
  255. , m_pExpirationLabel( NULL )
  256. , m_pTurnInButton( NULL )
  257. , m_bHasAllControls( false )
  258. , m_pDiscardButton( NULL )
  259. {
  260. SetItem( pQuestItem );
  261. m_StateTimer.Invalidate();
  262. ListenForGameEvent( "quest_objective_completed" );
  263. ListenForGameEvent( "player_spawn" );
  264. ListenForGameEvent( "client_disconnect" );
  265. ListenForGameEvent( "inventory_updated" );
  266. }
  267. //-----------------------------------------------------------------------------
  268. // Purpose:
  269. //-----------------------------------------------------------------------------
  270. CQuestItemPanel::~CQuestItemPanel()
  271. {
  272. if ( m_pItemTrackerPanel )
  273. {
  274. m_pItemTrackerPanel->MarkForDeletion();
  275. m_pItemTrackerPanel = NULL;
  276. }
  277. }
  278. //-----------------------------------------------------------------------------
  279. // Purpose:
  280. //-----------------------------------------------------------------------------
  281. void CQuestItemPanel::ApplySchemeSettings( IScheme *pScheme )
  282. {
  283. BaseClass::ApplySchemeSettings ( pScheme );
  284. AddActionSignalTarget( GetQuestLog() );
  285. LoadResFileForCurrentItem();
  286. }
  287. //-----------------------------------------------------------------------------
  288. // Purpose:
  289. //-----------------------------------------------------------------------------
  290. void CQuestItemPanel::LoadResFileForCurrentItem()
  291. {
  292. tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s", __FUNCTION__ );
  293. const char *pszResFile = "Resource/UI/quests/QuestItemPanel_Base.res";
  294. if ( m_hQuestItem )
  295. {
  296. const GameItemDefinition_t *pItemDef = m_hQuestItem->GetItemDefinition();
  297. // Get our quest theme
  298. const CQuestThemeDefinition *pTheme = pItemDef->GetQuestDef()->GetQuestTheme();
  299. if ( pTheme )
  300. {
  301. pszResFile = pTheme->GetQuestItemResFile();
  302. }
  303. }
  304. KeyValues *pConditions = new KeyValues( "conditions" );
  305. if ( pConditions )
  306. {
  307. char uilanguage[64];
  308. uilanguage[0] = 0;
  309. engine->GetUILanguage( uilanguage, sizeof( uilanguage ) );
  310. char szCondition[64];
  311. Q_snprintf( szCondition, sizeof( szCondition ), "if_%s", uilanguage );
  312. AddSubKeyNamed( pConditions, szCondition );
  313. }
  314. SetMouseInputEnabled( true ); // Slam this to true. When panels get created, they'll inherit their parents' mouse enabled state
  315. // and if we've been fiddling with it, we might accidently create all child panels with mouse input disabled.
  316. // Setting this to true just before the controls are made gives them a chance to be mouse enabled if they want.
  317. LoadControlSettings( pszResFile, NULL, NULL, pConditions );
  318. g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( this, m_strReset );
  319. m_pMainContainer = FindControl<EditablePanel>( "MainContainer" );
  320. if ( m_pMainContainer )
  321. {
  322. m_pMainContainer->AddActionSignalTarget( this );
  323. }
  324. m_pQuestPaperContainer = FindControl<EditablePanel>( "QuestPaperContainer", true );
  325. m_pFrontFolderContainer = FindControl<EditablePanel>( "FrontFolderContainer", true );
  326. Assert( m_pFrontFolderContainer );
  327. if ( m_pFrontFolderContainer )
  328. {
  329. m_pFrontFolderImage = m_pFrontFolderContainer->FindControl<ImagePanel>( "FrontFolderImage", true );
  330. Assert( m_pFrontFolderImage );
  331. m_pEncodedStatus = m_pFrontFolderContainer->FindControl< CQuestStatusPanel >( "EncodedStatus", true );
  332. m_pInactiveStatus = m_pFrontFolderContainer->FindControl< CQuestStatusPanel >( "InactiveStatus", true );
  333. m_pReadyToTurnInStatus = m_pFrontFolderContainer->FindControl< CQuestStatusPanel >( "ReadyToTurnInStatus", true );
  334. }
  335. m_pBackFolderContainer = FindControl<EditablePanel>( "BackFolderContainer", true );
  336. Assert( m_pBackFolderContainer );
  337. if ( m_pBackFolderContainer )
  338. {
  339. m_pBackFolderImage = m_pBackFolderContainer->FindControl<ImagePanel>( "BackFolderImage", true );
  340. Assert( m_pBackFolderImage );
  341. }
  342. if ( m_pQuestPaperContainer )
  343. {
  344. #if defined( STAGING_ONLY ) || defined( DEBUG )
  345. // don't do this in public
  346. m_pDiscardButton = new CExButton( m_pQuestPaperContainer, "Discard", "Discard", this, "discard_quest" );
  347. m_pDiscardButton->SetEnabled( true );
  348. m_pDiscardButton->SizeToContents();
  349. m_pDiscardButton->SetZPos( 101 );
  350. m_pDiscardButton->SetPos( 70, 40 );
  351. m_pDiscardButton->SetVisible( false );
  352. #endif // STAGING_ONLY || DEBUG
  353. m_pFindServerButton = m_pQuestPaperContainer->FindControl< CExButton >( "FindServerButton", true );
  354. m_pLoanerContainerPanel = m_pQuestPaperContainer->FindControl< EditablePanel >( "LoanerContainerPanel", true );
  355. if ( m_pLoanerContainerPanel )
  356. {
  357. m_pRequestLoanerItemsButton = m_pLoanerContainerPanel->FindControl< CExButton >( "RequestLoanerItemsButton", true );
  358. m_pEquipLoanerItemsButton = m_pLoanerContainerPanel->FindControl< CExButton >( "EquipLoanerItemsButton", true );
  359. for ( int i = 0; i < ARRAYSIZE( m_pLoanerItemModelPanel ); ++i )
  360. {
  361. m_pLoanerItemModelPanel[i] = m_pLoanerContainerPanel->FindControl< CItemModelPanel >( CFmtStr( "Loaner%dItemModelPanel", i + 1 ), true );
  362. }
  363. }
  364. m_pTitleButton = m_pQuestPaperContainer->FindControl<CExButton>( "TitleButton", true );
  365. m_pIdentifyContainer = m_pQuestPaperContainer->FindControl<EditablePanel>( "IdentifyButtonContainer", true );
  366. if ( m_pIdentifyContainer )
  367. {
  368. m_pIdentifyDimmer = m_pIdentifyContainer->FindControl<EditablePanel>( "Dimmer", true );
  369. m_pIdentifyButton = m_pIdentifyContainer->FindControl<CExButton>( "IdentifyButton", true );
  370. }
  371. Assert( m_pIdentifyContainer );
  372. m_pEncodedImage = m_pQuestPaperContainer->FindControl<ImagePanel>( "EncodedImage", true );
  373. m_pPhotoStatic = m_pQuestPaperContainer->FindControl<ImagePanel>( "StaticPhoto", true );
  374. Assert( m_pPhotoStatic );
  375. m_pFlavorScrollingContainer = m_pQuestPaperContainer->FindControl<CExScrollingEditablePanel>( "ScrollableBottomContainer", true );
  376. Assert( m_pFlavorScrollingContainer );
  377. if ( m_pFlavorScrollingContainer )
  378. {
  379. m_pObjectiveExplanationLabel = m_pFlavorScrollingContainer->FindControl< Label >( "QuestObjectiveExplanation", true );
  380. }
  381. CInputProxyPanel* pInputProxy = m_pQuestPaperContainer->FindControl< CInputProxyPanel >( "PaperInputProxyPanel", true );
  382. if ( pInputProxy )
  383. {
  384. // Make the scroller scroll
  385. pInputProxy->AddPanelForCommand( CInputProxyPanel::INPUT_MOUSE_WHEEL, m_pFlavorScrollingContainer, "MouseWheeled" );
  386. // Make the title glow
  387. pInputProxy->AddPanelForCommand( CInputProxyPanel::INPUT_MOUSE_ENTER, m_pTitleButton, "CursorEntered" );
  388. pInputProxy->AddPanelForCommand( CInputProxyPanel::INPUT_MOUSE_EXIT, m_pTitleButton, "CursorExited" );
  389. // Capture clicks to expand/contract
  390. pInputProxy->AddPanelForCommand( CInputProxyPanel::INPUT_MOUSE_RELEASED, this, "MouseReleased" );
  391. }
  392. m_pTurnInContainer = m_pQuestPaperContainer->FindControl< EditablePanel >( "TurnInContainer" );
  393. Assert( m_pTurnInContainer );
  394. if ( m_pTurnInContainer )
  395. {
  396. m_pTurnInDimmer = m_pTurnInContainer->FindControl< EditablePanel >( "Dimmer", true );
  397. Assert( m_pTurnInContainer );
  398. m_pTurnInButton = m_pTurnInContainer->FindControl< Button >( "TurnInButton", true );
  399. Assert( m_pTurnInButton );
  400. m_pTurnInSpinnerContainer = m_pTurnInContainer->FindControl< EditablePanel>( "TurnInSpinnerContainer", true );
  401. Assert( m_pTurnInSpinnerContainer );
  402. if ( m_pTurnInSpinnerContainer )
  403. {
  404. m_pTurningInLabel = m_pTurnInSpinnerContainer->FindControl< Label >( "TurningInLabel", true );
  405. Assert( m_pTurningInLabel );
  406. }
  407. }
  408. m_pAcceptedImage = m_pQuestPaperContainer->FindControl< ImagePanel >( "AcceptedImage", true );
  409. Assert( m_pAcceptedImage );
  410. }
  411. if ( m_pFrontFolderContainer )
  412. {
  413. CInputProxyPanel* pInputProxy = m_pFrontFolderContainer->FindControl< CInputProxyPanel >( "FrontInputProxyPanel", true );
  414. if ( pInputProxy )
  415. {
  416. pInputProxy->AddPanelForCommand( CInputProxyPanel::INPUT_MOUSE_ENTER, m_pInactiveStatus, "CursorEntered" );
  417. pInputProxy->AddPanelForCommand( CInputProxyPanel::INPUT_MOUSE_EXIT, m_pInactiveStatus, "CursorExited" );
  418. // Make the title glow
  419. pInputProxy->AddPanelForCommand( CInputProxyPanel::INPUT_MOUSE_ENTER, m_pTitleButton, "CursorEntered" );
  420. pInputProxy->AddPanelForCommand( CInputProxyPanel::INPUT_MOUSE_EXIT, m_pTitleButton, "CursorExited" );
  421. // Make the backdrop highlight
  422. pInputProxy->AddPanelForCommand( CInputProxyPanel::INPUT_MOUSE_ENTER, this, "CollapsedGlowStart" );
  423. pInputProxy->AddPanelForCommand( CInputProxyPanel::INPUT_MOUSE_EXIT, this, "CollapsedGlowEnd" );
  424. // Capture clicks to expand/contract
  425. pInputProxy->AddPanelForCommand( CInputProxyPanel::INPUT_MOUSE_RELEASED, this, "MouseReleased" );
  426. }
  427. }
  428. m_pExpirationLabel = FindControl<Label>( "QuestExpirationWarning", true );
  429. m_pFlavorText = FindControl<Label>( "QuestFlavorText", true );
  430. SetupObjectivesPanels( true );
  431. if ( pConditions )
  432. {
  433. pConditions->deleteThis();
  434. }
  435. m_bHasAllControls = m_pQuestPaperContainer
  436. && m_pFrontFolderContainer
  437. && m_pFrontFolderImage
  438. && m_pBackFolderContainer
  439. && m_pBackFolderImage
  440. && m_pEncodedStatus
  441. && m_pInactiveStatus
  442. && m_pReadyToTurnInStatus
  443. && m_pFlavorText
  444. && m_pObjectiveExplanationLabel
  445. && m_pExpirationLabel
  446. && m_pTurnInContainer
  447. && m_pTurnInDimmer
  448. && m_pTurnInButton
  449. && m_pIdentifyButton
  450. && m_pTurnInSpinnerContainer
  451. && m_pTitleButton
  452. && m_pIdentifyDimmer
  453. && m_pIdentifyContainer
  454. && m_pPhotoStatic
  455. && m_pAcceptedImage
  456. && m_pTurningInLabel
  457. && m_pFlavorScrollingContainer
  458. && m_pItemTrackerPanel
  459. && m_pEncodedImage
  460. && m_pMainContainer;
  461. }
  462. //-----------------------------------------------------------------------------
  463. // Purpose:
  464. //-----------------------------------------------------------------------------
  465. void CQuestItemPanel::ApplySettings( KeyValues *inResourceData )
  466. {
  467. tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s", __FUNCTION__ );
  468. BaseClass::ApplySettings( inResourceData );
  469. if ( m_hQuestItem )
  470. {
  471. m_vecFoldersImages.Purge();
  472. KeyValues *pKVFoldersBlock = inResourceData->FindKey( "folders" );
  473. Assert( pKVFoldersBlock );
  474. if ( pKVFoldersBlock )
  475. {
  476. FOR_EACH_TRUE_SUBKEY( pKVFoldersBlock, pKVFolder )
  477. {
  478. auto& folder = m_vecFoldersImages[ m_vecFoldersImages.AddToTail() ];
  479. folder.m_strFront = pKVFolder->GetString( "front", NULL);
  480. folder.m_strBack = pKVFolder->GetString( "back", NULL );
  481. }
  482. }
  483. else
  484. {
  485. const GameItemDefinition_t *pItemDef = m_hQuestItem->GetItemDefinition();
  486. // Get our quest theme
  487. const CQuestThemeDefinition *pTheme = pItemDef->GetQuestDef()->GetQuestTheme();
  488. if ( pTheme )
  489. {
  490. Warning( "%s %s is missing 'folders' data\n", pItemDef->GetQuestDef()->GetCorrespondingOperationName(), pTheme->GetQuestItemResFile() );
  491. }
  492. }
  493. }
  494. if ( 1/*m_pKVItemTracker == NULL*/ )
  495. {
  496. KeyValues *pTrackerKV = inResourceData->FindKey( "tracker_kv" );
  497. if ( pTrackerKV )
  498. {
  499. m_pKVItemTracker = pTrackerKV->MakeCopy();
  500. }
  501. }
  502. m_strEncodedText = inResourceData->GetString( "encoded_text", NULL );
  503. m_strExpireText = inResourceData->GetString( "expire_text", NULL );
  504. m_strItemTrackerResFile = inResourceData->GetString( "TrackerPanelResFile", NULL );
  505. // Sound effects
  506. m_strTurnInSound = inResourceData->GetString( "turn_in_sound", NULL );
  507. m_strTurnInSuccessSound = inResourceData->GetString( "turn_in_success_sound", NULL );
  508. m_strDecodeSound = inResourceData->GetString( "decode_sound", NULL );
  509. m_strExpandSound = inResourceData->GetString( "expand_sound", NULL );
  510. m_strCollapseSound = inResourceData->GetString( "collapse_sound", NULL );
  511. // Animations
  512. m_strReset = inResourceData->GetString( "anim_reset", NULL );
  513. m_strAnimExpand = inResourceData->GetString( "anim_expand", NULL );
  514. m_strAnimCollapse = inResourceData->GetString( "anim_collapse", NULL );
  515. m_strTurningIn = inResourceData->GetString( "anim_turning_in", NULL );
  516. m_strHighlightOn = inResourceData->GetString( "anim_highlight_on", NULL );
  517. m_strHighlightOff = inResourceData->GetString( "anim_highlight_off", NULL );
  518. }
  519. //-----------------------------------------------------------------------------
  520. // Purpose:
  521. //-----------------------------------------------------------------------------
  522. void CQuestItemPanel::PerformLayout( void )
  523. {
  524. tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s", __FUNCTION__ );
  525. BaseClass::PerformLayout();
  526. if ( !HasAllControls() )
  527. return;
  528. m_pIdentifyContainer->SetVisible( m_eState == STATE_UNIDENTIFIED );
  529. m_pTurnInContainer->SetVisible( m_eState == STATE_COMPLETED || m_eState == STATE_TURNING_IN__GC_RESPONDED || m_eState == STATE_TURNING_IN__WAITING_FOR_GC);
  530. m_pTurnInButton->SetVisible( m_eState == STATE_COMPLETED );
  531. m_pTurnInSpinnerContainer->SetVisible( m_eState == STATE_TURNING_IN__GC_RESPONDED || m_eState == STATE_TURNING_IN__WAITING_FOR_GC );
  532. m_pPhotoStatic->SetVisible( m_eState == STATE_UNIDENTIFIED || m_eState == STATE_IDENTIFYING );
  533. m_pFindServerButton->SetVisible( m_eState == STATE_NORMAL );
  534. // only exist in non public build
  535. if ( m_pDiscardButton )
  536. {
  537. m_pDiscardButton->SetVisible( m_eState == STATE_NORMAL );
  538. }
  539. // loaners
  540. if ( m_eState == STATE_NORMAL || m_eState == STATE_COMPLETED )
  541. {
  542. // get all loaners required from quest
  543. CUtlVector< item_definition_index_t > vecLoanerItems;
  544. bool bRequiredLoaners = GetLoanerListFromQuest( m_hQuestItem, vecLoanerItems ) > 0;
  545. // get all granted loaners from this quest
  546. CUtlVector< CEconItemView* > vecGrantedLoaners;
  547. if ( bRequiredLoaners )
  548. {
  549. GetLoanersFromLocalInventory( m_hQuestItem->GetItemID(), vecLoanerItems, vecGrantedLoaners );
  550. }
  551. if ( bRequiredLoaners )
  552. {
  553. m_pLoanerContainerPanel->SetVisible( true );
  554. bool bAllGranted = vecLoanerItems.Count() == vecGrantedLoaners.Count();
  555. m_pRequestLoanerItemsButton->SetVisible( !bAllGranted );
  556. m_pEquipLoanerItemsButton->SetVisible( bAllGranted );
  557. for ( int i = 0; i < ARRAYSIZE( m_pLoanerItemModelPanel ); ++i )
  558. {
  559. // try to use the granted items first
  560. if ( i < vecGrantedLoaners.Count() )
  561. {
  562. m_pLoanerItemModelPanel[i]->SetItem( vecGrantedLoaners[i] );
  563. m_pLoanerItemModelPanel[i]->SetVisible( true );
  564. }
  565. // In case we don't get all the loaner items, use fake items as second option
  566. else if ( i < vecLoanerItems.Count() )
  567. {
  568. CEconItemView tempItem;
  569. tempItem.Init( vecLoanerItems[i], AE_UNIQUE, AE_USE_SCRIPT_VALUE, true );
  570. m_pLoanerItemModelPanel[i]->SetItem( &tempItem );
  571. m_pLoanerItemModelPanel[i]->SetVisible( true );
  572. }
  573. else
  574. {
  575. m_pLoanerItemModelPanel[i]->SetVisible( false );
  576. }
  577. }
  578. }
  579. else
  580. {
  581. m_pLoanerContainerPanel->SetVisible( false );
  582. }
  583. }
  584. else
  585. {
  586. m_pLoanerContainerPanel->SetVisible( false );
  587. }
  588. m_pEncodedStatus->SetShow( m_eState == STATE_UNIDENTIFIED );
  589. m_pReadyToTurnInStatus->SetShow( m_eState == STATE_COMPLETED || m_eState == STATE_TURNING_IN__GC_RESPONDED || m_eState == STATE_TURNING_IN__WAITING_FOR_GC );
  590. float flDecodeAmount = 1.f;
  591. // Only cypher-style decoding needs to decode
  592. if ( m_eDecodeStyle == DECODE_STYLE_CYPHER && m_eState == STATE_UNIDENTIFIED )
  593. {
  594. flDecodeAmount = 0.f;
  595. }
  596. m_pEncodedImage->SetAlpha( m_eState == STATE_UNIDENTIFIED ? 255 : 0 );
  597. if ( m_hQuestItem )
  598. {
  599. m_pTitleButton->SetText( GetDecodedString( "name", flDecodeAmount ) );
  600. int nScrollableYOffset = 0;
  601. // Check if the quest is going to expire soon (within a week). If so, show a "This is going to be destroyed" message.
  602. const CRTime nExpirationTime = m_hQuestItem->GetExpirationDate();
  603. const CRTime nOneWeekFromNow = CRTime::RTime32DateAdd( CRTime::RTime32TimeCur(), 1, k_ETimeUnitWeek );
  604. const bool bExpiringSoon = nExpirationTime.GetRTime32() != RTime32(0) && nExpirationTime < nOneWeekFromNow;
  605. m_pExpirationLabel->SetVisible( bExpiringSoon );
  606. if ( bExpiringSoon )
  607. {
  608. CLocalizedRTime32 locTime = { nExpirationTime.GetRTime32(), false, GLocalizationProvider(), NULL };
  609. m_pExpirationLabel->SetText( CConstructLocalizedString( g_pVGuiLocalize->Find( m_strExpireText ), locTime ) );
  610. m_pExpirationLabel->InvalidateLayout( true );
  611. m_pExpirationLabel->SizeToContents();
  612. nScrollableYOffset += m_pExpirationLabel->GetTall();
  613. }
  614. m_pObjectiveExplanationLabel->SetPos( 0, nScrollableYOffset );
  615. m_pObjectiveExplanationLabel->SetText( GetDecodedString( "explanation", flDecodeAmount ) );
  616. m_pObjectiveExplanationLabel->InvalidateLayout( true ); // So we get the right height when we do SizeToContents below
  617. m_pObjectiveExplanationLabel->SizeToContents();
  618. nScrollableYOffset += m_pObjectiveExplanationLabel->GetTall();
  619. m_pFlavorText->SetText( GetDecodedString( "desc", flDecodeAmount ) );
  620. int nWide, nTall;
  621. m_pFlavorText->GetTextImage()->GetContentSize( nWide, nTall );
  622. m_pFlavorText->SetTall( nTall + 20 );
  623. m_pItemTrackerPanel->SetPos( m_pItemTrackerPanel->GetXPos(), nScrollableYOffset );
  624. nScrollableYOffset += m_pItemTrackerPanel->GetTall();
  625. // Put the flavor text below the obectives
  626. m_pFlavorText->SetPos( m_pFlavorText->GetXPos(), nScrollableYOffset );
  627. m_pFlavorScrollingContainer->InvalidateLayout( true );
  628. m_pFlavorScrollingContainer->InvalidateLayout();
  629. m_pFlavorScrollingContainer->ResetScrollAmount();
  630. // Randomize our folder images based on original ID
  631. if ( m_vecFoldersImages.Count() )
  632. {
  633. RandomSeed( m_hQuestItem->GetSOCData() ? m_hQuestItem->GetSOCData()->GetOriginalID() : m_hQuestItem->GetItemDefIndex() );
  634. int idx = RandomInt( 0, m_vecFoldersImages.Count() - 1 );
  635. m_pFrontFolderImage->SetImage( m_vecFoldersImages[ idx ].m_strFront );
  636. m_pBackFolderImage->SetImage( m_vecFoldersImages[ idx ].m_strBack );
  637. }
  638. }
  639. UpdateInvalidReasons();
  640. if ( m_pItemTrackerPanel )
  641. {
  642. auto& vecObjectives = m_pItemTrackerPanel->GetAttributePanels();
  643. FOR_EACH_VEC( vecObjectives, i )
  644. {
  645. // Only do this when unidentified. The panel updates its own string, and we don't want to stomp it
  646. if ( m_eState == STATE_UNIDENTIFIED )
  647. {
  648. const wchar_t *pszString = GetDecodedString( CFmtStr( "objective%d", i ), flDecodeAmount );
  649. vecObjectives[ i ]->SetDialogVariable( "attr_desc", pszString );
  650. }
  651. }
  652. }
  653. }
  654. //-----------------------------------------------------------------------------
  655. // Purpose:
  656. //-----------------------------------------------------------------------------
  657. bool CQuestItemPanel::IsCursorOverMainContainer() const
  658. {
  659. return m_pMainContainer ? m_pMainContainer->IsCursorOver() : false;
  660. }
  661. //-----------------------------------------------------------------------------
  662. // Purpose:
  663. //-----------------------------------------------------------------------------
  664. void CQuestItemPanel::SetupObjectivesPanels( bool bRecreate )
  665. {
  666. if ( m_pItemTrackerPanel && bRecreate )
  667. {
  668. m_pItemTrackerPanel->MarkForDeletion();
  669. m_pItemTrackerPanel = NULL;
  670. }
  671. if ( !m_hQuestItem )
  672. return;
  673. if ( !m_pItemTrackerPanel )
  674. {
  675. m_pItemTrackerPanel = new CItemTrackerPanel( m_pFlavorScrollingContainer, "ItemTrackerPanel", m_hQuestItem->GetSOCData(), m_strItemTrackerResFile );
  676. m_pItemTrackerPanel->SetAutoDelete( false );
  677. SETUP_PANEL( m_pItemTrackerPanel );
  678. }
  679. else
  680. {
  681. // Get all the panels created
  682. m_pItemTrackerPanel->SetItem( m_hQuestItem->GetSOCData() );
  683. m_pItemTrackerPanel->InvalidateLayout( true );
  684. }
  685. m_pItemTrackerPanel->SetTall( m_pItemTrackerPanel->GetContentTall() );
  686. // Need to re-layout so the flavor text gets properly positioned under
  687. // all of the objectives
  688. InvalidateLayout();
  689. }
  690. //-----------------------------------------------------------------------------
  691. // Purpose:
  692. //-----------------------------------------------------------------------------
  693. void CQuestItemPanel::SetItem( CEconItemView* pItem )
  694. {
  695. tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s", __FUNCTION__ );
  696. if ( pItem == m_hQuestItem )
  697. {
  698. return;
  699. }
  700. m_bCollapsed = true;
  701. m_hQuestItem.SetItem( pItem );
  702. if ( m_pItemTrackerPanel && pItem )
  703. {
  704. m_pItemTrackerPanel->SetItem( pItem->GetSOCData() );
  705. }
  706. // By default
  707. SetState( STATE_NORMAL );
  708. if ( m_hQuestItem )
  709. {
  710. if ( IsQuestItemReadyToTurnIn( m_hQuestItem ) )
  711. {
  712. SetState( STATE_COMPLETED );
  713. }
  714. else if ( IsUnacknowledged() )
  715. {
  716. SetState( STATE_UNIDENTIFIED );
  717. }
  718. // Snag the quickplay map (if there is one)
  719. m_strQuickPlayMap = m_hQuestItem->GetItemDefinition()->GetQuestDef()->GetQuickplayMapName();
  720. m_strMatchmakingGroupName = m_hQuestItem->GetItemDefinition()->GetQuestDef()->GetMatchmakingGroupName();
  721. m_strMatchmakingCategoryName = m_hQuestItem->GetItemDefinition()->GetQuestDef()->GetMatchmakingCategoryName();
  722. m_strMatchmakingMapName = m_hQuestItem->GetItemDefinition()->GetQuestDef()->GetMatchmakingMapName();
  723. }
  724. // Reload res file so we get the right art
  725. LoadResFileForCurrentItem();
  726. // Capture strings after controls are created
  727. CaptureAndEncodeStrings();
  728. InvalidateLayout();
  729. }
  730. //-----------------------------------------------------------------------------
  731. // Purpose: Returns if the character is one that we don't want to re-encode
  732. // as another, or one that we don't want to encode another to in
  733. // order to maintain line breaks so that the decoding sequence is
  734. // easier for the user to follow.
  735. //-----------------------------------------------------------------------------
  736. bool IsNonEncodeCharacter( const wchar_t& wch)
  737. {
  738. switch ( wch )
  739. {
  740. case L'\x000A':
  741. case L'\x000B':
  742. case L'\x000C':
  743. case L'\x000D':
  744. case L'\x0085':
  745. case L'\x2028':
  746. case L'\x2029':
  747. case L' ':
  748. return true;
  749. }
  750. return false;
  751. }
  752. //-----------------------------------------------------------------------------
  753. // Purpose:
  754. //-----------------------------------------------------------------------------
  755. void CQuestItemPanel::CaptureAndEncodeStrings()
  756. {
  757. tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s", __FUNCTION__ );
  758. if ( !m_hQuestItem )
  759. return;
  760. // Clean up any existing values
  761. if ( m_pKVCipherStrings )
  762. {
  763. m_pKVCipherStrings->deleteThis();
  764. m_pKVCipherStrings = NULL;
  765. }
  766. m_pKVCipherStrings = new KeyValues( "cipherstrings" );
  767. KeyValues *pKVDecoded = m_pKVCipherStrings->CreateNewKey();
  768. pKVDecoded->SetName( "decoded" );
  769. {
  770. // Capture the description/flavor string
  771. const char *pszLocToken = m_hQuestItem->GetItemDefinition()->GetQuestDef()->GetRolledDescriptionForItem( m_hQuestItem->GetSOCData() );
  772. pKVDecoded->SetWString( "desc", g_pVGuiLocalize->Find( pszLocToken ) );
  773. }
  774. if ( m_pObjectiveExplanationLabel )
  775. {
  776. wchar_t wszBuff[512];
  777. m_pObjectiveExplanationLabel->GetText( wszBuff, ARRAYSIZE( wszBuff ) );
  778. pKVDecoded->SetWString( "explanation", wszBuff );
  779. }
  780. auto& vecObjectives = m_pItemTrackerPanel->GetAttributePanels();
  781. // Capture objective strings
  782. FOR_EACH_VEC( vecObjectives, i )
  783. {
  784. CItemAttributeProgressPanel *pObjective = vecObjectives[ i ];
  785. KeyValues *pKV = pObjective->GetDialogVariables();
  786. pKVDecoded->SetWString( CFmtStr( "objective%d", i ), pKV->GetWString( "attr_desc" ) );
  787. }
  788. // Create encoded strings from the decoded strings
  789. KeyValues *pKVEncoded = pKVDecoded->MakeCopy();
  790. pKVEncoded->SetName( "encoded" );
  791. m_pKVCipherStrings->AddSubKey( pKVEncoded );
  792. RandomSeed( m_hQuestItem->GetSOCData() ? m_hQuestItem->GetSOCData()->GetOriginalID() : m_hQuestItem->GetItemDefIndex() );
  793. // "encode" each string by scrambling
  794. FOR_EACH_VALUE( pKVEncoded, pKVString )
  795. {
  796. const wchar_t *pWString = pKVString->GetWString();
  797. wchar wszBuff[4096];
  798. loc_scpy_safe( wszBuff, pWString );
  799. int nStrLen = Q_wcslen( wszBuff );
  800. // Go through the entire string and swap each character
  801. // with another random character in the string
  802. int i=0;
  803. while( wszBuff[i] != 0 && i < ARRAYSIZE( wszBuff ) )
  804. {
  805. // Dont scramble spaces to maintain line breaks
  806. if ( !IsNonEncodeCharacter( wszBuff[i] ) )
  807. {
  808. // Scramble, but keep trying if we scramble to a space
  809. do
  810. {
  811. wszBuff[i] = *(pWString + RandomInt( 0, nStrLen - 1 ) );
  812. } while ( IsNonEncodeCharacter( wszBuff[i] ) );
  813. }
  814. i++;
  815. }
  816. pKVEncoded->SetWString( pKVString->GetName(), wszBuff );
  817. }
  818. const char *pszLocToken = m_hQuestItem->GetItemDefinition()->GetQuestDef()->GetRolledNameForItem( m_hQuestItem->GetSOCData() );
  819. const wchar_t* pwszName = g_pVGuiLocalize->Find( pszLocToken );
  820. // Force the encrypted version of the quest title to be "<Encrypted>".
  821. pKVEncoded->SetWString( "name", g_pVGuiLocalize->Find( m_strEncodedText ) );
  822. pKVDecoded->SetWString( "name", pwszName );
  823. }
  824. //-----------------------------------------------------------------------------
  825. // Purpose:
  826. //-----------------------------------------------------------------------------
  827. void CQuestItemPanel::OnCommand( const char *command )
  828. {
  829. tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s", __FUNCTION__ );
  830. BaseClass::OnCommand( command );
  831. if ( FStrEq( command, "discard_quest" ) )
  832. {
  833. OnDiscardQuest();
  834. }
  835. else if ( FStrEq( command, "select" ) )
  836. {
  837. m_pQuestList->SetSelected( this, false );
  838. }
  839. else if ( FStrEq( command, "turnin" ) )
  840. {
  841. if ( m_hQuestItem && m_hQuestItem->GetItemDefinition() && m_hQuestItem->GetItemDefinition()->GetQuestDef() )
  842. {
  843. if ( !tf_quest_turn_in_confirm_opt_out.GetBool() && ( GetEarnedBonusPoints( m_hQuestItem ) != m_hQuestItem->GetItemDefinition()->GetQuestDef()->GetMaxBonusPoints() ) )
  844. {
  845. CTFGenericConfirmOptOutDialog *pPanel = ShowConfirmOptOutDialog( "#TF_Quest_TurnIn_Title", "#TF_Quest_TurnIn_Text",
  846. "#TF_Quest_TurnIn_Yes", "#TF_Quest_TurnIn_No",
  847. "#TF_Quest_TurnIn_Ask_Opt_Out", "tf_quest_turn_in_confirm_opt_out",
  848. ConfirmTurnInQuest );
  849. if ( pPanel )
  850. {
  851. pPanel->SetContext( this );
  852. return;
  853. }
  854. }
  855. else
  856. {
  857. OnCompleteQuest();
  858. }
  859. }
  860. }
  861. else if ( FStrEq( command, "identify" ) )
  862. {
  863. OnIdentify();
  864. }
  865. else if ( FStrEq( command, "request_loaner_items" ) )
  866. {
  867. GCSDK::CProtoBufMsg< CMsgGCQuestObjective_RequestLoanerItems > msg( k_EMsgGCQuestObjective_RequestLoanerItems );
  868. msg.Body().set_quest_item_id( m_hQuestItem->GetItemID() );
  869. GCClientSystem()->BSendMessage( msg );
  870. }
  871. else if ( FStrEq( command, "equip_loaner_items" ) )
  872. {
  873. OnEquipLoaners();
  874. }
  875. else if( Q_strnicmp( "playsound", command, 9 ) == 0 )
  876. {
  877. vgui::surface()->PlaySound( command + 10 );
  878. }
  879. else if ( FStrEq( "mm_casual_open", command ) )
  880. {
  881. if ( GTFGCClientSystem() )
  882. {
  883. if ( ( m_strMatchmakingGroupName != 0 ) || ( m_strMatchmakingCategoryName != 0 ) || ( m_strMatchmakingMapName != 0 ) )
  884. {
  885. GTFGCClientSystem()->ClearCasualSearchCriteria();
  886. if ( m_strMatchmakingGroupName != 0 )
  887. {
  888. int iGroupType = StringFieldToInt( m_strMatchmakingGroupName.Get(), s_pszMMTypes, ARRAYSIZE( s_pszMMTypes ) );
  889. if ( iGroupType > -1 )
  890. {
  891. SelectGroup( (EMatchmakingGroupType)iGroupType, true );
  892. }
  893. }
  894. if ( m_strMatchmakingCategoryName != 0 )
  895. {
  896. int iCategoryType = StringFieldToInt( m_strMatchmakingCategoryName.Get(), s_pszGameModes, ARRAYSIZE( s_pszGameModes ) );
  897. if ( iCategoryType > -1 )
  898. {
  899. SelectCategory( (EGameCategory)iCategoryType, true );
  900. }
  901. }
  902. if ( m_strMatchmakingMapName != 0 )
  903. {
  904. if ( GetItemSchema() )
  905. {
  906. const MapDef_t *pMap = GetItemSchema()->GetMasterMapDefByName( m_strMatchmakingMapName.Get() );
  907. if ( pMap )
  908. {
  909. GTFGCClientSystem()->SelectCasualMap( pMap->m_nDefIndex, true );
  910. }
  911. }
  912. }
  913. }
  914. }
  915. // Defaulting to 12v12
  916. GTFGCClientSystem()->SetLadderType( k_nMatchGroup_Casual_12v12 );
  917. PromptOrFireCommand( "OpenMatchmakingLobby casual" );
  918. }
  919. }
  920. //-----------------------------------------------------------------------------
  921. // Purpose:
  922. //-----------------------------------------------------------------------------
  923. const wchar_t* CQuestItemPanel::GetDecodedString( const char* pszKeyName, float flPercentDecoded )
  924. {
  925. tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s", __FUNCTION__ );
  926. static wchar_t wszBuff[4096];
  927. KeyValues *pKVEncoded = m_pKVCipherStrings->FindKey( "encoded" );
  928. KeyValues *pKVDecoded = m_pKVCipherStrings->FindKey( "decoded" );
  929. // Trivial work?
  930. if ( flPercentDecoded <= 0.f )
  931. {
  932. return pKVEncoded->GetWString( pszKeyName );
  933. }
  934. else if ( flPercentDecoded >= 1.f )
  935. {
  936. return pKVDecoded->GetWString( pszKeyName );
  937. }
  938. loc_scpy_safe( wszBuff, pKVEncoded->GetWString( pszKeyName ) );
  939. const locchar_t* pwszDecoded = pKVDecoded->GetWString( pszKeyName );
  940. int nLength = loc_strlen( pwszDecoded );
  941. int nMaxCopy = nLength * flPercentDecoded;
  942. // Not using V_wcsncpy because it null terminates.
  943. wcsncpy( wszBuff, pwszDecoded, nMaxCopy );
  944. return wszBuff;
  945. }
  946. //-----------------------------------------------------------------------------
  947. // Purpose:
  948. //-----------------------------------------------------------------------------
  949. void CQuestItemPanel::OnThink()
  950. {
  951. tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s", __FUNCTION__ );
  952. switch ( m_eState )
  953. {
  954. case STATE_IDENTIFYING:
  955. {
  956. // Have we finished?
  957. if ( m_StateTimer.HasStarted() && m_StateTimer.IsElapsed() )
  958. {
  959. m_pItemTrackerPanel->InvalidateLayout();
  960. m_StateTimer.Invalidate();
  961. SetState( STATE_NORMAL );
  962. // Play a reveal sound?
  963. const GameItemDefinition_t *pItemDef = m_hQuestItem->GetItemDefinition();
  964. const CQuestThemeDefinition *pTheme = pItemDef->GetQuestDef()->GetQuestTheme();
  965. if ( pTheme )
  966. {
  967. const char *pszRevealSound = pTheme->GetRevealSound();
  968. if ( pszRevealSound && pszRevealSound[0] )
  969. {
  970. vgui::surface()->PlaySound( pszRevealSound );
  971. }
  972. }
  973. }
  974. float flPercent = m_StateTimer.GetElapsedTime() / m_StateTimer.GetCountdownDuration();
  975. switch( m_eDecodeStyle )
  976. {
  977. case DECODE_STYLE_CYPHER:
  978. {
  979. // Slowly "decode" the text in the lables
  980. m_pTitleButton->SetText( GetDecodedString( "name", flPercent ) );
  981. m_pFlavorText->SetText( GetDecodedString( "desc", flPercent ) );
  982. m_pObjectiveExplanationLabel->SetText( GetDecodedString( "explanation", flPercent ) );
  983. auto& vecObjectives = m_pItemTrackerPanel->GetAttributePanels();
  984. FOR_EACH_VEC( vecObjectives, i )
  985. {
  986. const wchar_t *pszString = GetDecodedString( CFmtStr( "objective%d", i ), flPercent );
  987. vecObjectives[ i ]->SetDialogVariable( "attr_desc", pszString );
  988. }
  989. break;
  990. }
  991. case DECODE_STYLE_PANEL_FADE:
  992. {
  993. // Slowly fade out the encode image
  994. m_pEncodedImage->SetAlpha( 255 * ( 1.f - flPercent ) );
  995. break;
  996. }
  997. default:
  998. Assert( 0 );
  999. }
  1000. break;
  1001. }
  1002. case STATE_TURNING_IN__WAITING_FOR_GC:
  1003. {
  1004. // Have we finished?
  1005. if ( m_StateTimer.HasStarted() && m_StateTimer.IsElapsed() )
  1006. {
  1007. m_pQuestList->SetCompletingPanel( NULL );
  1008. m_StateTimer.Invalidate();
  1009. // Bring up confirm dialog
  1010. CTFGenericConfirmDialog *pDialog = new CTFGenericConfirmDialog( "#TF_Trading_Timeout_Title", "#TF_Trading_Timeout_Text", "#TF_OK", NULL, NULL, NULL );
  1011. if ( pDialog )
  1012. {
  1013. pDialog->SetContext( this );
  1014. pDialog->Show();
  1015. }
  1016. SetState( STATE_COMPLETED );
  1017. }
  1018. // Intentionally fall through
  1019. }
  1020. case STATE_TURNING_IN__GC_RESPONDED:
  1021. {
  1022. // Have we finished?
  1023. if ( m_StateTimer.HasStarted() && m_StateTimer.IsElapsed() )
  1024. {
  1025. SetState( STATE_SHOW_ACCEPTED );
  1026. m_StateTimer.Start( 3.f );
  1027. m_pAcceptedImage->SetVisible( true );
  1028. vgui::surface()->PlaySound( m_strTurnInSuccessSound );
  1029. }
  1030. if ( m_pTurningInLabel )
  1031. {
  1032. int nPeriods = m_StateTimer.GetElapsedTime() / 0.3f;
  1033. nPeriods %= 4; // Only do up to 3 periods
  1034. wchar_t wszTurningInText[64];
  1035. char szTurningInLocToken[128];
  1036. m_pTurningInLabel->GetTextImage()->GetUnlocalizedText( szTurningInLocToken, ARRAYSIZE( szTurningInLocToken ) );
  1037. V_snwprintf( wszTurningInText, ARRAYSIZE( wszTurningInText ), L"%ls", g_pVGuiLocalize->Find( szTurningInLocToken ) );
  1038. while ( nPeriods > 0 )
  1039. {
  1040. V_wcsncat( wszTurningInText, L".", ARRAYSIZE( wszTurningInText ) );
  1041. --nPeriods;
  1042. }
  1043. m_pTurningInLabel->SetText( wszTurningInText );
  1044. }
  1045. break;
  1046. }
  1047. case STATE_SHOW_ACCEPTED:
  1048. {
  1049. if ( m_StateTimer.HasStarted() && m_StateTimer.IsElapsed() )
  1050. {
  1051. m_pQuestList->SetCompletingPanel( NULL );
  1052. m_StateTimer.Invalidate();
  1053. engine->ClientCmd_Unrestricted( "gameui_allowescapetoshow\n" );
  1054. InventoryManager()->ShowItemsPickedUp( true, false );
  1055. GetQuestLog()->AttachToGameUI();
  1056. GetQuestLog()->MarkQuestsDirty();
  1057. m_pQuestList->PopulateQuestLists();
  1058. engine->ClientCmd_Unrestricted( "gameui_preventescapetoshow\n" );
  1059. if ( m_pszCompleteSound )
  1060. {
  1061. vgui::surface()->PlaySound( m_pszCompleteSound );
  1062. }
  1063. }
  1064. else
  1065. {
  1066. float flPercent = Clamp( m_StateTimer.GetElapsedTime() / 0.2f, 0.f, 1.f );
  1067. m_nPaperXShakePos = sin( m_StateTimer.GetElapsedTime() * 200.f ) * ( 1.f - flPercent ) * 8.f;
  1068. m_nPaperYShakePos = sin( m_StateTimer.GetElapsedTime() * 200.f ) * ( 1.f - flPercent ) * 8.f;
  1069. m_pQuestPaperContainer->SetPos( m_nPaperXPos + m_nPaperXShakePos, m_nPaperYPos + m_nPaperYShakePos );
  1070. }
  1071. break;
  1072. }
  1073. case STATE_UNIDENTIFIED:
  1074. {
  1075. // Do nothing
  1076. break;
  1077. }
  1078. case STATE_COMPLETED:
  1079. {
  1080. // Do nothing
  1081. break;
  1082. }
  1083. default:
  1084. // Do nothing
  1085. break;
  1086. }
  1087. }
  1088. //-----------------------------------------------------------------------------
  1089. // Purpose:
  1090. //-----------------------------------------------------------------------------
  1091. void CQuestItemPanel::FireGameEvent( IGameEvent *event )
  1092. {
  1093. tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s", __FUNCTION__ );
  1094. if( FStrEq( event->GetName(), "quest_objective_completed" ) )
  1095. {
  1096. itemid_t nIDLow = 0x00000000FFFFFFFF & (itemid_t)event->GetInt( "quest_item_id_low" );
  1097. itemid_t nIDHi = 0xFFFFFFFF00000000 & (itemid_t)event->GetInt( "quest_item_id_hi" ) << 32;
  1098. itemid_t nID = nIDLow | nIDHi;
  1099. if ( m_hQuestItem && nID == m_hQuestItem->GetID() )
  1100. {
  1101. SetupObjectivesPanels( false );
  1102. if ( IsQuestItemReadyToTurnIn( m_hQuestItem ) )
  1103. {
  1104. SetState( STATE_COMPLETED );
  1105. }
  1106. PerformLayout();
  1107. }
  1108. }
  1109. else if ( FStrEq( event->GetName(), "player_spawn" )
  1110. || FStrEq( event->GetName(), "client_disconnect" ) )
  1111. {
  1112. InvalidateLayout();
  1113. }
  1114. else if ( FStrEq( "inventory_updated", event->GetName() ) )
  1115. {
  1116. // InvalidateLayout();
  1117. }
  1118. }
  1119. void CQuestItemPanel::OnMouseReleased( MouseCode code )
  1120. {
  1121. OnCommand( "select" );
  1122. }
  1123. //-----------------------------------------------------------------------------
  1124. // Purpose: Update our invalid reasons
  1125. //-----------------------------------------------------------------------------
  1126. void CQuestItemPanel::UpdateInvalidReasons()
  1127. {
  1128. tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s", __FUNCTION__ );
  1129. InvalidReasonsContainer_t invalidReasons;
  1130. bool bAllAreInvalid = false;
  1131. C_TFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer();
  1132. if ( pLocalPlayer && m_hQuestItem )
  1133. {
  1134. // Get the tracker for the items
  1135. const CQuestItemTracker* pItemTracker = QuestObjectiveManager()->GetTypedTracker< CQuestItemTracker* >( m_hQuestItem->GetItemID() );
  1136. // Get invalid reasons
  1137. if ( pItemTracker )
  1138. {
  1139. int nNumInvalid = pItemTracker->IsValidForPlayer( pLocalPlayer, invalidReasons );
  1140. bAllAreInvalid = pItemTracker->GetTrackers().Count() == nNumInvalid;
  1141. }
  1142. // Build a string describing why the current quest can't be worked on
  1143. if ( !invalidReasons.IsValid() )
  1144. {
  1145. CUtlVector< CUtlString > vecStrings;
  1146. // Get the strings that explain each reasons
  1147. GetInvalidReasonsNames( invalidReasons, vecStrings );
  1148. wchar_t wszBuff[ 1024 ];
  1149. // Start with the explanation
  1150. V_swprintf_safe( wszBuff, L"%ls", g_pVGuiLocalize->Find( "#TF_QuestInvalid_Explanation" ) );
  1151. // Add in each reason why the quest is invalid
  1152. for( int i = 0; i < vecStrings.Count(); ++ i )
  1153. {
  1154. V_wcscat_safe( wszBuff, L"\n\n" );
  1155. V_wcscat_safe( wszBuff, g_pVGuiLocalize->Find( vecStrings[i] ) );
  1156. }
  1157. // This gets snagged by CQuestTooltip
  1158. m_pInactiveStatus->SetDialogVariable( "tiptext", wszBuff );
  1159. }
  1160. }
  1161. // Visible if there's a reason why we're invalid
  1162. bool bShow = bAllAreInvalid && m_eState == STATE_NORMAL;
  1163. m_pInactiveStatus->SetShow( bShow );
  1164. m_pInactiveStatus->SetMouseInputEnabled( bShow );
  1165. m_pInactiveStatus->SetTooltip( g_spTextTooltip, NULL );
  1166. }
  1167. //-----------------------------------------------------------------------------
  1168. // Purpose: Start a glow
  1169. //-----------------------------------------------------------------------------
  1170. void CQuestItemPanel::OnCollapsedGlowStart( void )
  1171. {
  1172. g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( this, m_strHighlightOn );
  1173. }
  1174. //-----------------------------------------------------------------------------
  1175. // Purpose: Stop the glow
  1176. //-----------------------------------------------------------------------------
  1177. void CQuestItemPanel::OnCollapsedGlowEnd( void )
  1178. {
  1179. g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( this, m_strHighlightOff );
  1180. }
  1181. //-----------------------------------------------------------------------------
  1182. // Purpose: Delete the quest.
  1183. //-----------------------------------------------------------------------------
  1184. void CQuestItemPanel::OnDiscardQuest( void )
  1185. {
  1186. #if !defined(STAGING_ONLY) && !defined(DEBUG)
  1187. // Not in public!
  1188. return;
  1189. #endif
  1190. if ( m_pQuestList->GetCompletingPanel() == NULL )
  1191. {
  1192. // Bring up confirm dialog
  1193. CTFGenericConfirmDialog *pDialog = new CTFGenericConfirmDialog( "#QuestConfirmDiscard_Title", "#QuestConfirmDiscard_Body", "#X_DiscardItem", "#Cancel", &ConfirmDiscardQuest, NULL );
  1194. if ( pDialog )
  1195. {
  1196. pDialog->SetContext( this );
  1197. pDialog->Show();
  1198. }
  1199. const GameItemDefinition_t *pItemDef = m_hQuestItem->GetItemDefinition();
  1200. // Get our quest theme
  1201. const CQuestThemeDefinition *pTheme = pItemDef->GetQuestDef()->GetQuestTheme();
  1202. if ( pTheme )
  1203. {
  1204. const char *pszDiscardSound = pTheme->GetDiscardSound();
  1205. if ( pszDiscardSound && pszDiscardSound[0] )
  1206. {
  1207. vgui::surface()->PlaySound( pszDiscardSound );
  1208. }
  1209. }
  1210. }
  1211. }
  1212. //-----------------------------------------------------------------------------
  1213. // Purpose: Equip loaners for local player
  1214. //-----------------------------------------------------------------------------
  1215. void CQuestItemPanel::OnEquipLoaners( void )
  1216. {
  1217. if ( !m_hQuestItem )
  1218. return;
  1219. if ( m_pQuestList->GetCompletingPanel() == NULL )
  1220. {
  1221. // Bring up confirm dialog
  1222. CTFGenericConfirmDialog *pDialog = new CTFGenericConfirmDialog( "#QuestConfirmEquipLoaners_Title", "#QuestConfirmEquipLoaners_Body", "#Equip", "#Cancel", &ConfirmEquipLoaners, NULL );
  1223. if ( pDialog )
  1224. {
  1225. pDialog->SetContext( this );
  1226. pDialog->Show();
  1227. }
  1228. }
  1229. }
  1230. //-----------------------------------------------------------------------------
  1231. // Purpose: Send a message to the GC to evaluate completion of this quest
  1232. //-----------------------------------------------------------------------------
  1233. void CQuestItemPanel::OnCompleteQuest( void )
  1234. {
  1235. if ( !m_hQuestItem )
  1236. return;
  1237. // Double check that they're not just forcing the command
  1238. if ( IsQuestItemReadyToTurnIn( m_hQuestItem ) && m_pQuestList->GetCompletingPanel() == NULL )
  1239. {
  1240. m_pQuestList->SetCompletingPanel( this );
  1241. SetState( STATE_TURNING_IN__WAITING_FOR_GC );
  1242. // Use the timer for turning in the quest
  1243. m_StateTimer.Start( k_flQuestTurnInTime );
  1244. vgui::surface()->PlaySound( m_strTurnInSound );
  1245. GCSDK::CProtoBufMsg< CMsgGCQuestComplete_Request > msg( k_EMsgGCQuestComplete_Request );
  1246. msg.Body().set_quest_item_id( m_hQuestItem->GetItemID() );
  1247. GCClientSystem()->BSendMessage( msg );
  1248. PostActionSignal( new KeyValues("CompleteQuest") );
  1249. g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( this, m_strTurningIn );
  1250. const GameItemDefinition_t *pItemDef = m_hQuestItem->GetItemDefinition();
  1251. // Get our quest theme
  1252. const CQuestThemeDefinition *pTheme = pItemDef->GetQuestDef()->GetQuestTheme();
  1253. if ( pTheme )
  1254. {
  1255. m_pszCompleteSound = pTheme->GetRewardSound();
  1256. }
  1257. }
  1258. }
  1259. void CQuestItemPanel::OnIdentify()
  1260. {
  1261. if ( IsUnacknowledged() )
  1262. {
  1263. SetState( STATE_IDENTIFYING );
  1264. // Use the timer for identifying progress
  1265. m_StateTimer.Start( k_flQuestDecodeTime );
  1266. vgui::surface()->PlaySound( m_strDecodeSound );
  1267. // ack item
  1268. CEconItemView *pModifyItem = m_hQuestItem;
  1269. TFInventoryManager()->AcknowledgeItem( pModifyItem, false );
  1270. TFInventoryManager()->SetItemBackpackPosition( pModifyItem, (uint32)-1, false, true );
  1271. g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( m_pQuestPaperContainer, "QuestItem_StaticPhoto_Reveal" );
  1272. }
  1273. }
  1274. //-----------------------------------------------------------------------------
  1275. // Purpose:
  1276. //-----------------------------------------------------------------------------
  1277. void CQuestItemPanel::OnConfirmDelete( bool bConfirm )
  1278. {
  1279. // Delete the quest
  1280. if ( bConfirm && m_hQuestItem )
  1281. {
  1282. GCSDK::CProtoBufMsg< CMsgGCQuestDiscard_Request > msg( k_EMsgGCQuestDiscard_Request );
  1283. msg.Body().set_quest_item_id( m_hQuestItem->GetItemID() );
  1284. GCClientSystem()->BSendMessage( msg );
  1285. }
  1286. }
  1287. //-----------------------------------------------------------------------------
  1288. // Purpose:
  1289. //-----------------------------------------------------------------------------
  1290. void CQuestItemPanel::OnConfirmEquipLoaners( bool bConfirm )
  1291. {
  1292. // equip loaners
  1293. if ( bConfirm && m_hQuestItem )
  1294. {
  1295. // get all loaners required from quest
  1296. CUtlVector< item_definition_index_t > vecLoanerItems;
  1297. bool bRequiredLoaners = GetLoanerListFromQuest( m_hQuestItem, vecLoanerItems );
  1298. // get all granted loaners from this quest
  1299. CUtlVector< CEconItemView* > vecGrantedLoaners;
  1300. if ( bRequiredLoaners )
  1301. {
  1302. GetLoanersFromLocalInventory( m_hQuestItem->GetItemID(), vecLoanerItems, vecGrantedLoaners );
  1303. }
  1304. for ( int i=0; i<vecGrantedLoaners.Count(); ++i )
  1305. {
  1306. CEconItemView *pItem = vecGrantedLoaners[i];
  1307. if ( pItem )
  1308. {
  1309. // do it for first class that can equip
  1310. for ( int iClass = TF_FIRST_NORMAL_CLASS; iClass < TF_LAST_NORMAL_CLASS; ++iClass )
  1311. {
  1312. if ( pItem->GetStaticData()->CanBeUsedByClass( iClass ) )
  1313. {
  1314. int iSlot = pItem->GetStaticData()->GetLoadoutSlot( iClass );
  1315. TFInventoryManager()->EquipItemInLoadout( iClass, iSlot, pItem->GetItemID() );
  1316. // take the player to character loadout page
  1317. engine->ClientCmd_Unrestricted( CFmtStr( "open_charinfo_direct %d", iClass ) );
  1318. break;
  1319. }
  1320. }
  1321. }
  1322. }
  1323. }
  1324. }
  1325. //-----------------------------------------------------------------------------
  1326. // Purpose:
  1327. //-----------------------------------------------------------------------------
  1328. void CQuestItemPanel::QuestCompletedResponse()
  1329. {
  1330. // If we werent the one listening, dont bother
  1331. if ( m_eState != STATE_TURNING_IN__WAITING_FOR_GC )
  1332. return;
  1333. m_pQuestPaperContainer->GetPos( m_nPaperXPos, m_nPaperYPos );
  1334. m_nPaperXShakePos = m_nPaperYShakePos = 0;
  1335. SetState( STATE_TURNING_IN__GC_RESPONDED );
  1336. }
  1337. //-----------------------------------------------------------------------------
  1338. // Purpose:
  1339. //-----------------------------------------------------------------------------
  1340. void CQuestItemPanel::SetSelected( bool bSelected, bool bImmediate )
  1341. {
  1342. bool bPrevCollapsedSide = m_bCollapsed;
  1343. m_bCollapsed = ( !m_bCollapsed && bSelected ) || ( !bSelected );
  1344. if ( !bImmediate && bPrevCollapsedSide != m_bCollapsed )
  1345. {
  1346. if ( m_bCollapsed )
  1347. {
  1348. vgui::surface()->PlaySound( m_strCollapseSound );
  1349. g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( this, m_strAnimCollapse );
  1350. }
  1351. else
  1352. {
  1353. vgui::surface()->PlaySound( m_strExpandSound );
  1354. g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( this, m_strAnimExpand );
  1355. }
  1356. }
  1357. else
  1358. {
  1359. g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( this, m_strReset );
  1360. }
  1361. }
  1362. //-----------------------------------------------------------------------------
  1363. // Purpose:
  1364. //-----------------------------------------------------------------------------
  1365. bool CQuestItemPanel::IsUnacknowledged()
  1366. {
  1367. if ( !m_hQuestItem )
  1368. return false;
  1369. return IsQuestItemUnidentified( m_hQuestItem->GetSOCData() );
  1370. }
  1371. void CQuestItemPanel::SetState( EItemPanelState_t eState )
  1372. {
  1373. m_eState = eState;
  1374. InvalidateLayout();
  1375. }
  1376. //-----------------------------------------------------------------------------
  1377. // Purpose: GC Msg handler to handle a loaner item response
  1378. //-----------------------------------------------------------------------------
  1379. class CGCLoanerRequestResponse : public GCSDK::CGCClientJob
  1380. {
  1381. public:
  1382. CGCLoanerRequestResponse( GCSDK::CGCClient *pClient ) : GCSDK::CGCClientJob( pClient ) {}
  1383. virtual bool BYieldingRunGCJob( GCSDK::IMsgNetPacket *pNetPacket )
  1384. {
  1385. GCSDK::CProtoBufMsg<CMsgGCQuestObjective_RequestLoanerResponse> msg( pNetPacket );
  1386. // Show them the items they just got loaned!
  1387. InventoryManager()->ShowItemsPickedUp( true, false );
  1388. return true;
  1389. }
  1390. };
  1391. GC_REG_JOB( GCSDK::CGCClient, CGCLoanerRequestResponse, "CGCLoanerRequestResponse", k_EMsgGCQuestObjective_RequestLoanerResponse, GCSDK::k_EServerTypeGCClient );