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.

1539 lines
50 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================//
  6. #include "cbase.h"
  7. #include "class_loadout_panel.h"
  8. #include "c_tf_player.h"
  9. #include "vgui_controls/CheckButton.h"
  10. #include "econ_gcmessages.h"
  11. #include "gc_clientsystem.h"
  12. #include "tf_item_system.h"
  13. #include "loadout_preset_panel.h"
  14. #include "econ_item_description.h"
  15. #include "item_style_select_dialog.h"
  16. #include "vgui/IInput.h"
  17. #include "vgui_controls/PanelListPanel.h"
  18. // memdbgon must be the last include file in a .cpp file!!!
  19. #include <tier0/memdbgon.h>
  20. extern ConVar tf_respawn_on_loadoutchanges;
  21. ConVar tf_show_preset_explanation_in_class_loadout( "tf_show_preset_explanation_in_class_loadout", "1", FCVAR_HIDDEN | FCVAR_CLIENTDLL | FCVAR_ARCHIVE );
  22. ConVar tf_show_taunt_explanation_in_class_loadout( "tf_show_taunt_explanation_in_class_loadout", "1", FCVAR_HIDDEN | FCVAR_CLIENTDLL | FCVAR_ARCHIVE );
  23. void ParticleSlider_UpdateRequest( int iLoadoutPosition, float value )
  24. {
  25. CClassLoadoutPanel *pPanel = g_pClassLoadoutPanel;
  26. if ( !pPanel )
  27. return;
  28. CEconItemView *pHat = pPanel->GetItemInSlot( iLoadoutPosition );
  29. if ( !pHat )
  30. return;
  31. // does this hat even have a particle effect
  32. static CSchemaAttributeDefHandle pAttrDef_AttachParticleEffect( "attach particle effect" );
  33. uint32 iHasEffect = 0;
  34. if ( !pHat->FindAttribute( pAttrDef_AttachParticleEffect, &iHasEffect ) )
  35. return;
  36. // Check for use head toggle
  37. static CSchemaAttributeDefHandle pAttrDef_UseHeadOrigin( "particle effect use head origin" );
  38. uint32 iUseHead = 0;
  39. if ( !pHat->FindAttribute( pAttrDef_UseHeadOrigin, &iUseHead ) || iUseHead == 0 )
  40. return;
  41. // Look for the attribute and request to change it
  42. static CSchemaAttributeDefHandle pAttrDef_VerticalOffset( "particle effect vertical offset" );
  43. uint32 iOffSet = 0;
  44. if ( !pHat->FindAttribute( pAttrDef_VerticalOffset, &iOffSet ) && value == 0 )
  45. {
  46. return;
  47. }
  48. else
  49. {
  50. const float& flAttrValue = (float&)iOffSet;
  51. if ( value == flAttrValue )
  52. return; // no change do nothing
  53. }
  54. // Send a message to the GC to request a change
  55. GCSDK::CProtoBufMsg<CMsgSetItemEffectVerticalOffset> msg( k_EMsgGCSetItemEffectVerticalOffset );
  56. msg.Body().set_item_id( pHat->GetItemID() );
  57. msg.Body().set_offset( value );
  58. GCClientSystem()->BSendMessage( msg );
  59. }
  60. void HatOffset_Callback( IConVar *pConVar, char const *pOldString, float flOldValue )
  61. {
  62. ConVarRef cVarRef( pConVar );
  63. ParticleSlider_UpdateRequest( LOADOUT_POSITION_HEAD, cVarRef.GetFloat() );
  64. }
  65. ConVar tf_hat_effect_offset( "tf_hat_effect_offset", "0", FCVAR_DEVELOPMENTONLY, "Adjust the position of the unusual effect for your hat.", HatOffset_Callback );
  66. void Misc1Offset_Callback( IConVar *pConVar, char const *pOldString, float flOldValue )
  67. {
  68. ConVarRef cVarRef( pConVar );
  69. ParticleSlider_UpdateRequest( LOADOUT_POSITION_MISC, cVarRef.GetFloat() );
  70. }
  71. ConVar tf_misc1_effect_offset( "tf_misc1_effect_offset", "0", FCVAR_DEVELOPMENTONLY, "Adjust the position of the unusual effect for your hat.", Misc1Offset_Callback );
  72. void Misc2Offset_Callback( IConVar *pConVar, char const *pOldString, float flOldValue )
  73. {
  74. ConVarRef cVarRef( pConVar );
  75. ParticleSlider_UpdateRequest( LOADOUT_POSITION_MISC2, cVarRef.GetFloat() );
  76. }
  77. ConVar tf_misc2_effect_offset( "tf_misc2_effect_offset", "0", FCVAR_DEVELOPMENTONLY, "Adjust the position of the unusual effect for your hat.", Misc2Offset_Callback );
  78. // Hacky solution to different classes wanting different slots visible in their loadouts, and in different positions
  79. struct LoadoutPanelPositioningInstance
  80. {
  81. int m_iPos[NUM_ITEM_PANELS_IN_LOADOUT];
  82. };
  83. bool IsTauntPanelPosition( int iButtonPos )
  84. {
  85. return iButtonPos >= 9 && iButtonPos <= 16;
  86. }
  87. const LoadoutPanelPositioningInstance g_DefaultLoadoutPanelPositioning =
  88. {
  89. {
  90. 1, // LOADOUT_POSITION_PRIMARY = 0,
  91. 2, // LOADOUT_POSITION_SECONDARY,
  92. 3, // LOADOUT_POSITION_MELEE,
  93. 0, // LOADOUT_POSITION_UTILITY, // STAGING ONLY
  94. 0, // LOADOUT_POSITION_BUILDING,
  95. 0, // LOADOUT_POSITION_PDA,
  96. 0, // LOADOUT_POSITION_PDA2,
  97. 5, // LOADOUT_POSITION_HEAD,
  98. 6, // LOADOUT_POSITION_MISC,
  99. 8, // LOADOUT_POSITION_ACTION,
  100. 7, // LOADOUT_POSITION_MISC2,
  101. 9, // LOADOUT_POSITION_TAUNT,
  102. 10, // LOADOUT_POSITION_TAUNT2,
  103. 11, // LOADOUT_POSITION_TAUNT3,
  104. 12, // LOADOUT_POSITION_TAUNT4,
  105. 13, // LOADOUT_POSITION_TAUNT5,
  106. 14, // LOADOUT_POSITION_TAUNT6,
  107. 15, // LOADOUT_POSITION_TAUNT7,
  108. 16, // LOADOUT_POSITION_TAUNT8,
  109. #ifdef STAGING_ONLY
  110. 0, // LOADOUT_POSITION_PDA_ADDON1,
  111. 0, // LOADOUT_POSITION_PDA_ADDON2,
  112. 0, // LOADOUT_POSITION_PDA3,
  113. //9, // LOADOUT_POSITION_MISC3,
  114. //10, // LOADOUT_POSITION_MISC4,
  115. //11, // LOADOUT_POSITION_MISC5,
  116. //12, // LOADOUT_POSITION_MISC6,
  117. //13, // LOADOUT_POSITION_MISC7,
  118. //14, // LOADOUT_POSITION_MISC8,
  119. //15, // LOADOUT_POSITION_MISC9,
  120. //16, // LOADOUT_POSITION_MISC10,
  121. 0, // LOADOUT_POSITION_BUILDING2,
  122. #endif // STAGING_ONLY
  123. }
  124. };
  125. const LoadoutPanelPositioningInstance g_LoadoutPanelPositioning_Spy =
  126. {
  127. {
  128. 0, // LOADOUT_POSITION_PRIMARY = 0,
  129. 1, // LOADOUT_POSITION_SECONDARY,
  130. 2, // LOADOUT_POSITION_MELEE,
  131. 0, // LOADOUT_POSITION_UTILITY, // STAGING ONLY
  132. 4, // LOADOUT_POSITION_BUILDING, // sapper
  133. 0, // LOADOUT_POSITION_PDA, // disguise kit (Hidden)
  134. 3, // LOADOUT_POSITION_PDA2, // Watch
  135. 5, // LOADOUT_POSITION_HEAD,
  136. 6, // LOADOUT_POSITION_MISC,
  137. 8, // LOADOUT_POSITION_ACTION,
  138. 7, // LOADOUT_POSITION_MISC2,
  139. 9, // LOADOUT_POSITION_TAUNT,
  140. 10, // LOADOUT_POSITION_TAUNT2,
  141. 11, // LOADOUT_POSITION_TAUNT3,
  142. 12, // LOADOUT_POSITION_TAUNT4,
  143. 13, // LOADOUT_POSITION_TAUNT5,
  144. 14, // LOADOUT_POSITION_TAUNT6,
  145. 15, // LOADOUT_POSITION_TAUNT7,
  146. 16, // LOADOUT_POSITION_TAUNT8,
  147. #ifdef STAGING_ONLY
  148. 0, // LOADOUT_POSITION_PDA_ADDON1,
  149. 0, // LOADOUT_POSITION_PDA_ADDON2,
  150. 0, // LOADOUT_POSITION_PDA3,
  151. //9, // LOADOUT_POSITION_MISC3,
  152. //10, // LOADOUT_POSITION_MISC4,
  153. //11, // LOADOUT_POSITION_MISC5,
  154. //12, // LOADOUT_POSITION_MISC6,
  155. //13, // LOADOUT_POSITION_MISC7,
  156. //14, // LOADOUT_POSITION_MISC8,
  157. //15, // LOADOUT_POSITION_MISC9,
  158. //16, // LOADOUT_POSITION_MISC10,
  159. 0, // LOADOUT_POSITION_BUILDING2,
  160. #endif // STAGING_ONLY
  161. }
  162. };
  163. const LoadoutPanelPositioningInstance g_LoadoutPanelPositioning_Engineer =
  164. {
  165. {
  166. 1, // LOADOUT_POSITION_PRIMARY = 0,
  167. 2, // LOADOUT_POSITION_SECONDARY,
  168. 3, // LOADOUT_POSITION_MELEE,
  169. 0, // LOADOUT_POSITION_UTILITY, // STAGING ONLY
  170. 0, // LOADOUT_POSITION_BUILDING,
  171. 4, // LOADOUT_POSITION_PDA,
  172. 0, // LOADOUT_POSITION_PDA2,
  173. 5, // LOADOUT_POSITION_HEAD,
  174. 6, // LOADOUT_POSITION_MISC,
  175. 8, // LOADOUT_POSITION_ACTION,
  176. 7, // LOADOUT_POSITION_MISC2,
  177. 9, // LOADOUT_POSITION_TAUNT,
  178. 10, // LOADOUT_POSITION_TAUNT2,
  179. 11, // LOADOUT_POSITION_TAUNT3,
  180. 12, // LOADOUT_POSITION_TAUNT4,
  181. 13, // LOADOUT_POSITION_TAUNT5,
  182. 14, // LOADOUT_POSITION_TAUNT6,
  183. 15, // LOADOUT_POSITION_TAUNT7,
  184. 16, // LOADOUT_POSITION_TAUNT8,
  185. #ifdef STAGING_ONLY
  186. 17, // LOADOUT_POSITION_PDA_ADDON1,
  187. 18, // LOADOUT_POSITION_PDA_ADDON2,
  188. 0, // LOADOUT_POSITION_PDA3,
  189. //9, // LOADOUT_POSITION_MISC3,
  190. //10, // LOADOUT_POSITION_MISC4,
  191. //11, // LOADOUT_POSITION_MISC5,
  192. //12, // LOADOUT_POSITION_MISC6,
  193. //13, // LOADOUT_POSITION_MISC7,
  194. //14, // LOADOUT_POSITION_MISC8,
  195. //15, // LOADOUT_POSITION_MISC9,
  196. //16, // LOADOUT_POSITION_MISC10,
  197. 0, // LOADOUT_POSITION_BUILDING2,
  198. #endif // STAGING_ONLY
  199. }
  200. };
  201. const LoadoutPanelPositioningInstance *g_VisibleLoadoutSlotsPerClass[] =
  202. {
  203. &g_DefaultLoadoutPanelPositioning, // TF_CLASS_UNDEFINED
  204. &g_DefaultLoadoutPanelPositioning, // TF_CLASS_SCOUT
  205. &g_DefaultLoadoutPanelPositioning, // TF_CLASS_SNIPER
  206. &g_DefaultLoadoutPanelPositioning, // TF_CLASS_SOLDIER
  207. &g_DefaultLoadoutPanelPositioning, // TF_CLASS_DEMOMAN
  208. &g_DefaultLoadoutPanelPositioning, // TF_CLASS_MEDIC
  209. &g_DefaultLoadoutPanelPositioning, // TF_CLASS_HEAVYWEAPONS
  210. &g_DefaultLoadoutPanelPositioning, // TF_CLASS_PYRO
  211. &g_LoadoutPanelPositioning_Spy, // TF_CLASS_SPY
  212. &g_LoadoutPanelPositioning_Engineer, // TF_CLASS_ENGINEER
  213. };
  214. COMPILE_TIME_ASSERT( ARRAYSIZE( g_VisibleLoadoutSlotsPerClass ) == TF_LAST_NORMAL_CLASS );
  215. //-----------------------------------------------------------------------------
  216. // Particle Effect Slider
  217. //-----------------------------------------------------------------------------
  218. CLoadoutItemOptionsPanel::CLoadoutItemOptionsPanel( Panel *parent, const char *pName ) : vgui::EditablePanel( parent, pName )
  219. {
  220. m_pHatParticleSlider = NULL;
  221. m_pHatParticleUseHeadButton = NULL;
  222. m_iCurrentClassIndex = -1;
  223. m_eItemSlot = LOADOUT_POSITION_INVALID;
  224. m_pListPanel = new vgui::PanelListPanel( this, "PanelListPanel" );
  225. m_pListPanel->SetFirstColumnWidth( 0 );
  226. m_pHatParticleSlider = new CCvarSlider( m_pListPanel, "HatParticleSlider" );
  227. m_pHatParticleSlider->AddActionSignalTarget( this );
  228. m_pHatParticleUseHeadButton = new vgui::CheckButton( m_pListPanel, "HatUseHeadCheckButton", "#GameUI_ParticleHatUseHead" );
  229. m_pHatParticleUseHeadButton->AddActionSignalTarget( this );
  230. m_pSetStyleButton = new CExButton( m_pListPanel, "SetStyleButton", "#TF_Item_SelectStyle" );
  231. m_pSetStyleButton->AddActionSignalTarget( this );
  232. }
  233. //-----------------------------------------------------------------------------
  234. void CLoadoutItemOptionsPanel::ApplySchemeSettings( vgui::IScheme *pScheme )
  235. {
  236. BaseClass::ApplySchemeSettings( pScheme );
  237. LoadControlSettings( "resource/UI/ItemOptionsPanel.res" );
  238. }
  239. //-----------------------------------------------------------------------------
  240. void CLoadoutItemOptionsPanel::PerformLayout( void )
  241. {
  242. BaseClass::PerformLayout();
  243. m_pHatParticleSlider->SetTickColor( Color( 235, 226, 202, 255 ) ); // tanlight
  244. }
  245. //-----------------------------------------------------------------------------
  246. void CLoadoutItemOptionsPanel::OnCommand( const char *command )
  247. {
  248. if ( FStrEq( command, "particle_button_clicked" ) )
  249. {
  250. UpdateItemOptionsUI();
  251. return;
  252. }
  253. else if ( FStrEq( command, "particle_use_head_clicked" ) )
  254. {
  255. // Grab current hat
  256. CEconItemView *pHat = TFInventoryManager()->GetItemInLoadoutForClass( m_iCurrentClassIndex, m_eItemSlot );
  257. if ( !pHat )
  258. return;
  259. // does this hat even have a particle effect
  260. static CSchemaAttributeDefHandle pAttrDef_AttachParticleEffect( "attach particle effect" );
  261. uint32 iHasEffect = 0;
  262. if ( !pHat->FindAttribute( pAttrDef_AttachParticleEffect, &iHasEffect ) )
  263. return;
  264. // Send a message to the GC to request a change
  265. GCSDK::CProtoBufMsg<CMsgSetHatEffectUseHeadOrigin> msg( k_EMsgGCSetHatEffectUseHeadOrigin );
  266. msg.Body().set_item_id( pHat->GetItemID() );
  267. msg.Body().set_use_head( m_pHatParticleUseHeadButton->IsSelected() );
  268. GCClientSystem()->BSendMessage( msg );
  269. return;
  270. }
  271. else if ( FStrEq( command, "set_style" ) )
  272. {
  273. CEconItemView *pHat = TFInventoryManager()->GetItemInLoadoutForClass( m_iCurrentClassIndex, m_eItemSlot );
  274. CStyleSelectDialog *pStyle = vgui::SETUP_PANEL( new CStyleSelectDialog( GetParent(), pHat ) );
  275. if ( pStyle )
  276. {
  277. pStyle->Show();
  278. }
  279. }
  280. }
  281. //-----------------------------------------------------------------------------
  282. void CLoadoutItemOptionsPanel::OnMessage( const KeyValues* pParams, vgui::VPANEL hFromPanel )
  283. {
  284. if ( FStrEq( pParams->GetName(), "SliderDragEnd" ) )
  285. {
  286. m_pHatParticleSlider->ApplyChanges();
  287. }
  288. BaseClass::OnMessage( pParams, hFromPanel );
  289. }
  290. //-----------------------------------------------------------------------------
  291. void CLoadoutItemOptionsPanel::SetItemSlot( loadout_positions_t eItemSlot, int iClassIndex )
  292. {
  293. m_eItemSlot = eItemSlot;
  294. m_iCurrentClassIndex = iClassIndex;
  295. // Init the Slider based on the slot
  296. const char * pszConVarName = NULL;
  297. switch ( eItemSlot )
  298. {
  299. case LOADOUT_POSITION_HEAD :
  300. pszConVarName = "tf_hat_effect_offset";
  301. break;
  302. case LOADOUT_POSITION_MISC :
  303. pszConVarName = "tf_misc1_effect_offset";
  304. break;
  305. case LOADOUT_POSITION_MISC2 :
  306. pszConVarName = "tf_misc2_effect_offset";
  307. break;
  308. default:
  309. break;
  310. }
  311. if ( pszConVarName )
  312. {
  313. m_pHatParticleSlider->SetupSlider( -8, 8, pszConVarName, false );
  314. }
  315. m_pHatParticleSlider->SetTickColor( Color( 235, 226, 202, 255 ) ); // tanlight
  316. m_pHatParticleSlider->SetTickCaptions( "", "" );
  317. UpdateItemOptionsUI();
  318. }
  319. //-----------------------------------------------------------------------------
  320. void CLoadoutItemOptionsPanel::UpdateItemOptionsUI()
  321. {
  322. if ( m_eItemSlot == LOADOUT_POSITION_INVALID )
  323. return;
  324. m_pListPanel->RemoveAll();
  325. // Add controls for various item options
  326. AddControlsParticleEffect();
  327. AddControlsSetStyle();
  328. // Bail if no controls added
  329. if ( m_pListPanel->GetItemCount() == 0 )
  330. {
  331. // We should have some controls if we get to this point.
  332. Assert( 0 );
  333. SetVisible( false );
  334. return;
  335. }
  336. // Resize the background and list panel to contain all the controls
  337. int nVertPixels = m_pListPanel->ComputeVPixelsNeeded();
  338. int nNewTall = Min( 200, nVertPixels );
  339. m_pListPanel->SetTall( nNewTall );
  340. SetTall( nNewTall );
  341. InvalidateLayout( true, false );
  342. }
  343. //-----------------------------------------------------------------------------
  344. void CLoadoutItemOptionsPanel::AddControlsParticleEffect( void ) const
  345. {
  346. m_pHatParticleUseHeadButton->SetVisible( false );
  347. m_pHatParticleSlider->SetVisible( false );
  348. CEconItemView *pItem = TFInventoryManager()->GetItemInLoadoutForClass( m_iCurrentClassIndex, m_eItemSlot );
  349. if ( pItem )
  350. {
  351. // does this hat even have a particle effect
  352. static CSchemaAttributeDefHandle pAttrDef_AttachParticleEffect( "attach particle effect" );
  353. uint32 iValue = 0;
  354. if ( pItem->FindAttribute( pAttrDef_AttachParticleEffect, &iValue ) )
  355. {
  356. m_pHatParticleUseHeadButton->SetVisible( true );
  357. m_pListPanel->AddItem( NULL, m_pHatParticleUseHeadButton );
  358. m_pListPanel->AddItem( NULL, m_pHatParticleSlider );
  359. // Check for use head toggle
  360. static CSchemaAttributeDefHandle pAttrDef_UseHeadOrigin( "particle effect use head origin" );
  361. uint32 iUseHead = 0;
  362. if ( pItem->FindAttribute( pAttrDef_UseHeadOrigin, &iUseHead ) && iUseHead > 0 )
  363. {
  364. m_pHatParticleSlider->SetVisible( true );
  365. m_pHatParticleUseHeadButton->SetSelected( true );
  366. m_pHatParticleSlider->SetTickColor( Color( 235, 226, 202, 255 ) ); // tanlight
  367. m_pHatParticleSlider->Repaint();
  368. // Get offset if it exists
  369. static CSchemaAttributeDefHandle pAttrDef_VerticalOffset( "particle effect vertical offset" );
  370. uint32 iOffset = 0;
  371. if ( pItem->FindAttribute( pAttrDef_VerticalOffset, &iOffset ) )
  372. {
  373. m_pHatParticleSlider->SetSliderValue( (float&)iOffset );
  374. }
  375. }
  376. else
  377. {
  378. m_pHatParticleUseHeadButton->SetSelected( false );
  379. }
  380. }
  381. }
  382. }
  383. //-----------------------------------------------------------------------------
  384. void CLoadoutItemOptionsPanel::AddControlsSetStyle( void ) const
  385. {
  386. m_pSetStyleButton->SetVisible( false );
  387. CEconItemView *pItem = GetItem();
  388. if ( pItem && pItem->GetStaticData()->GetNumStyles() )
  389. {
  390. m_pSetStyleButton->SetVisible( true );
  391. m_pListPanel->AddItem( NULL, m_pSetStyleButton );
  392. }
  393. }
  394. //-----------------------------------------------------------------------------
  395. CEconItemView* CLoadoutItemOptionsPanel::GetItem( void ) const
  396. {
  397. return TFInventoryManager()->GetItemInLoadoutForClass( m_iCurrentClassIndex, m_eItemSlot );
  398. }
  399. CClassLoadoutPanel *g_pClassLoadoutPanel = NULL;
  400. //-----------------------------------------------------------------------------
  401. // Purpose:
  402. //-----------------------------------------------------------------------------
  403. CClassLoadoutPanel::CClassLoadoutPanel( vgui::Panel *parent )
  404. : CBaseLoadoutPanel( parent, "class_loadout_panel" )
  405. , m_pItemOptionPanelKVs( NULL )
  406. {
  407. m_iCurrentClassIndex = TF_CLASS_UNDEFINED;
  408. m_iCurrentTeamIndex = TF_TEAM_RED;
  409. m_iCurrentSlotIndex = -1;
  410. m_pPlayerModelPanel = NULL;
  411. m_pSelectionPanel = NULL;
  412. m_pTauntHintLabel = NULL;
  413. m_pTauntLabel = NULL;
  414. m_pTauntCaratLabel = NULL;
  415. m_pPassiveAttribsLabel = NULL;
  416. m_pLoadoutPresetPanel = NULL;
  417. m_pPresetsExplanationPopup = NULL;
  418. m_pTauntsExplanationPopup = NULL;
  419. m_pBuildablesButton = NULL;
  420. m_pCharacterLoadoutButton = NULL;
  421. m_pTauntLoadoutButton = NULL;
  422. m_bInTauntLoadoutMode = false;
  423. g_pClassLoadoutPanel = this;
  424. m_pItemOptionPanel = new CLoadoutItemOptionsPanel( this, "ItemOptionsPanel" );
  425. }
  426. CClassLoadoutPanel::~CClassLoadoutPanel()
  427. {
  428. if ( m_pItemOptionPanelKVs )
  429. {
  430. m_pItemOptionPanelKVs->deleteThis();
  431. m_pItemOptionPanelKVs = NULL;
  432. }
  433. }
  434. //-----------------------------------------------------------------------------
  435. // Purpose:
  436. //-----------------------------------------------------------------------------
  437. void CClassLoadoutPanel::ApplySchemeSettings( vgui::IScheme *pScheme )
  438. {
  439. LoadControlSettings( "Resource/UI/ClassLoadoutPanel.res" );
  440. BaseClass::ApplySchemeSettings( pScheme );
  441. m_pPlayerModelPanel = dynamic_cast<CTFPlayerModelPanel*>( FindChildByName("classmodelpanel") );
  442. m_pTauntHintLabel = dynamic_cast<vgui::Label*>( FindChildByName("TauntHintLabel") );
  443. m_pTauntLabel = dynamic_cast<CExLabel*>( FindChildByName("TauntLabel") );
  444. m_pTauntCaratLabel = dynamic_cast<CExLabel*>( FindChildByName("TauntCaratLabel") );
  445. m_pBuildablesButton = dynamic_cast<CExButton*>( FindChildByName("BuildablesButton") );
  446. m_pCharacterLoadoutButton = dynamic_cast<CExImageButton*>( FindChildByName("CharacterLoadoutButton") );
  447. m_pTauntLoadoutButton = dynamic_cast<CExImageButton*>( FindChildByName("TauntLoadoutButton") );
  448. m_pPassiveAttribsLabel = dynamic_cast<CExLabel*>( FindChildByName("PassiveAttribsLabel") );
  449. m_pLoadoutPresetPanel = dynamic_cast<CLoadoutPresetPanel*>( FindChildByName( "loadout_preset_panel" ) );
  450. m_pPresetsExplanationPopup = dynamic_cast<CExplanationPopup*>( FindChildByName( "PresetsExplanation" ) );
  451. m_pTauntsExplanationPopup = dynamic_cast<CExplanationPopup*>( FindChildByName( "TauntsExplanation" ) );
  452. m_pTopLinePanel = FindChildByName( "TopLine" );
  453. if ( m_pPassiveAttribsLabel )
  454. {
  455. m_pPassiveAttribsLabel->SetMouseInputEnabled( false );
  456. }
  457. m_pMouseOverTooltip->SetPositioningStrategy( IPTTP_BOTTOM_SIDE );
  458. m_aDefaultColors[LOADED][FG][DEFAULT] = vgui::scheme()->GetIScheme( GetScheme() )->GetColor( "Econ.Button.PresetDefaultColorFg", Color( 255, 255, 255, 255 ) );
  459. m_aDefaultColors[LOADED][FG][ARMED] = vgui::scheme()->GetIScheme( GetScheme() )->GetColor( "Econ.Button.PresetArmedColorFg", Color( 255, 255, 255, 255 ) );
  460. m_aDefaultColors[LOADED][FG][DEPRESSED] = vgui::scheme()->GetIScheme( GetScheme() )->GetColor( "Econ.Button.PresetDepressedColorFg", Color( 255, 255, 255, 255 ) );
  461. m_aDefaultColors[LOADED][BG][DEFAULT] = vgui::scheme()->GetIScheme( GetScheme() )->GetColor( "Econ.Button.PresetDefaultColorBg", Color( 255, 255, 255, 255 ) );
  462. m_aDefaultColors[LOADED][BG][ARMED] = vgui::scheme()->GetIScheme( GetScheme() )->GetColor( "Econ.Button.PresetArmedColorBg", Color( 255, 255, 255, 255 ) );
  463. m_aDefaultColors[LOADED][BG][DEPRESSED] = vgui::scheme()->GetIScheme( GetScheme() )->GetColor( "Econ.Button.PresetDepressedColorBg", Color( 255, 255, 255, 255 ) );
  464. m_aDefaultColors[NOTLOADED][FG][DEFAULT] = vgui::scheme()->GetIScheme( GetScheme() )->GetColor( "Button.TextColor", Color( 255, 255, 255, 255 ) );
  465. m_aDefaultColors[NOTLOADED][FG][ARMED] = vgui::scheme()->GetIScheme( GetScheme() )->GetColor( "Button.ArmedTextColor", Color( 255, 255, 255, 255 ) );
  466. m_aDefaultColors[NOTLOADED][FG][DEPRESSED] = vgui::scheme()->GetIScheme( GetScheme() )->GetColor( "Button.DepressedTextColor", Color( 255, 255, 255, 255 ) );
  467. m_aDefaultColors[NOTLOADED][BG][DEFAULT] = vgui::scheme()->GetIScheme( GetScheme() )->GetColor( "Button.BgColor", Color( 255, 255, 255, 255 ) );
  468. m_aDefaultColors[NOTLOADED][BG][ARMED] = vgui::scheme()->GetIScheme( GetScheme() )->GetColor( "Button.ArmedBgColor", Color( 255, 255, 255, 255 ) );
  469. m_aDefaultColors[NOTLOADED][BG][DEPRESSED] = vgui::scheme()->GetIScheme( GetScheme() )->GetColor( "Button.DepressedBgColor", Color( 255, 255, 255, 255 ) );
  470. }
  471. void CClassLoadoutPanel::ApplySettings( KeyValues *inResourceData )
  472. {
  473. BaseClass::ApplySettings( inResourceData );
  474. KeyValues *pItemKV = inResourceData->FindKey( "itemoptionpanels_kv" );
  475. if ( pItemKV )
  476. {
  477. if ( m_pItemOptionPanelKVs )
  478. {
  479. m_pItemOptionPanelKVs->deleteThis();
  480. }
  481. m_pItemOptionPanelKVs = new KeyValues("itemoptionpanels_kv");
  482. pItemKV->CopySubkeys( m_pItemOptionPanelKVs );
  483. }
  484. #ifdef STAGING_ONLY
  485. // PDA Panels
  486. if ( m_pItemModelPanels.Count() > LOADOUT_POSITION_PDA_ADDON2 )
  487. {
  488. for ( int i = LOADOUT_POSITION_PDA_ADDON1; i <= LOADOUT_POSITION_PDA_ADDON2; i++ )
  489. {
  490. int wide = m_pItemModelPanels[i]->GetWide();
  491. int tall = m_pItemModelPanels[i]->GetTall();
  492. m_pItemModelPanels[i]->SetSize( wide / 2, tall / 2 );
  493. m_pItemModelPanels[i]->InvalidateLayout();
  494. }
  495. }
  496. #endif
  497. }
  498. //-----------------------------------------------------------------------------
  499. // Purpose:
  500. //-----------------------------------------------------------------------------
  501. void CClassLoadoutPanel::PerformLayout( void )
  502. {
  503. BaseClass::PerformLayout();
  504. // This is disabled by default in res file. IF we turn it on again, uncomment this.
  505. /*if ( m_pPassiveAttribsLabel )
  506. {
  507. m_pPassiveAttribsLabel->SetVisible( !m_bInTauntLoadoutMode );
  508. }*/
  509. if ( m_pTauntHintLabel )
  510. {
  511. m_pTauntHintLabel->SetVisible( m_bInTauntLoadoutMode );
  512. const char *key = engine->Key_LookupBinding( "taunt" );
  513. if ( !key )
  514. {
  515. key = "< not bound >";
  516. }
  517. SetDialogVariable( "taunt", key );
  518. }
  519. if ( m_pTauntLabel )
  520. {
  521. m_pTauntLabel->SetVisible( m_bInTauntLoadoutMode );
  522. }
  523. if ( m_pTauntCaratLabel )
  524. {
  525. m_pTauntCaratLabel->SetVisible( m_bInTauntLoadoutMode );
  526. }
  527. if ( m_pCharacterLoadoutButton )
  528. {
  529. UpdatePageButtonColor( m_pCharacterLoadoutButton, !m_bInTauntLoadoutMode );
  530. }
  531. if ( m_pTauntLoadoutButton )
  532. {
  533. UpdatePageButtonColor( m_pTauntLoadoutButton, m_bInTauntLoadoutMode );
  534. }
  535. FOR_EACH_VEC( m_vecItemOptionButtons, i )
  536. {
  537. m_vecItemOptionButtons[i]->SetVisible( false );
  538. }
  539. for ( int i = 0; i < m_pItemModelPanels.Count(); i++ )
  540. {
  541. // Viewing a class loadout. Layout the buttons & the class image.
  542. if ( i >= NUM_ITEM_PANELS_IN_LOADOUT )
  543. {
  544. m_pItemModelPanels[i]->SetVisible( false );
  545. continue;
  546. }
  547. int iButtonPos = 0;
  548. if ( m_iCurrentClassIndex != TF_CLASS_UNDEFINED )
  549. {
  550. iButtonPos = g_VisibleLoadoutSlotsPerClass[m_iCurrentClassIndex]->m_iPos[i];
  551. }
  552. bool bIsVisible = false;
  553. if ( iButtonPos > 0 )
  554. {
  555. bIsVisible = m_bInTauntLoadoutMode ? IsTauntPanelPosition( iButtonPos ) : !IsTauntPanelPosition( iButtonPos );
  556. }
  557. m_pItemModelPanels[i]->SetVisible( bIsVisible );
  558. if ( bIsVisible )
  559. {
  560. if ( m_bInTauntLoadoutMode )
  561. {
  562. iButtonPos -= g_VisibleLoadoutSlotsPerClass[m_iCurrentClassIndex]->m_iPos[LOADOUT_POSITION_TAUNT];
  563. }
  564. else
  565. {
  566. iButtonPos--;
  567. }
  568. #ifdef STAGING_ONLY
  569. // Override for the PDA AddOnSlots
  570. if ( i == LOADOUT_POSITION_PDA_ADDON1 )
  571. {
  572. int iYPos, iXPos;
  573. m_pItemModelPanels[ LOADOUT_POSITION_PDA ]->GetPos( iXPos, iYPos );
  574. int iWide, iTall;
  575. m_pItemModelPanels[ LOADOUT_POSITION_PDA ]->GetSize( iWide, iTall );
  576. m_pItemModelPanels[i]->SetPos( iXPos + iWide + XRES(1), iYPos );
  577. m_pItemModelPanels[i]->SetSize( iWide / 2.1, iTall / 2.1 );
  578. continue;
  579. }
  580. else if ( i == LOADOUT_POSITION_PDA_ADDON2 )
  581. {
  582. int iYPos, iXPos;
  583. m_pItemModelPanels[LOADOUT_POSITION_PDA]->GetPos( iXPos, iYPos );
  584. int iWide, iTall;
  585. m_pItemModelPanels[LOADOUT_POSITION_PDA]->GetSize( iWide, iTall );
  586. m_pItemModelPanels[i]->SetPos( iXPos + iWide + XRES(1), iYPos + iTall - (iTall / 2.1 ) );
  587. m_pItemModelPanels[i]->SetSize( iWide / 2.1, iTall / 2.1 );
  588. continue;
  589. }
  590. #endif
  591. m_pItemModelPanels[i]->SetNoItemText( ItemSystem()->GetItemSchema()->GetLoadoutStringsForDisplay( EEquipType_t::EQUIP_TYPE_CLASS )[i] );
  592. int iCenter = GetWide() * 0.5;
  593. int iColumnHeight = 4;
  594. int iColumn = iButtonPos / iColumnHeight;
  595. int iYButtonPos = iButtonPos % iColumnHeight;
  596. int iOffset = iColumn == 0 ? m_iItemXPosOffcenterA : m_iItemXPosOffcenterB + ((iColumn - 1) * 200);
  597. int iXPos = iCenter + iOffset;
  598. int iYPos = m_iItemYPos + (m_iItemYDelta * iYButtonPos);
  599. m_pItemModelPanels[i]->SetPos( iXPos, iYPos );
  600. // Update position and visibility of the item option buttons
  601. if ( i < m_vecItemOptionButtons.Count() )
  602. {
  603. // Place the button just inside the item model panel
  604. CExButton* pItemOptionsPanel = m_vecItemOptionButtons[iButtonPos];
  605. int iButtonWide = m_pItemModelPanels[i]->GetWide();
  606. int iMyWide = pItemOptionsPanel->GetWide();
  607. int iOptionsXPos = iColumn == 0
  608. ? iXPos + iButtonWide - iMyWide
  609. : iXPos;
  610. pItemOptionsPanel->SetPos( iOptionsXPos, iYPos );
  611. CEconItemView *pItemData = TFInventoryManager()->GetItemInLoadoutForClass( m_iCurrentClassIndex, i );
  612. // Enable or disable the item options button for this item model panel
  613. pItemOptionsPanel->SetVisible( !m_bInTauntLoadoutMode && AnyOptionsAvailableForItem( pItemData ) );
  614. pItemOptionsPanel->SetCommand( CFmtStr( "options%d", i ) );
  615. }
  616. }
  617. m_pItemModelPanels[ i ]->SetSelected( false );
  618. }
  619. if ( m_pLoadoutPresetPanel )
  620. {
  621. m_pLoadoutPresetPanel->SetPos( ( ScreenWidth() - m_pLoadoutPresetPanel->GetWide() ) / 2, m_iItemYPos );
  622. }
  623. LinkModelPanelControllerNavigation( true );
  624. }
  625. //-----------------------------------------------------------------------------
  626. // Purpose:
  627. //-----------------------------------------------------------------------------
  628. void CClassLoadoutPanel::OnKeyCodePressed( vgui::KeyCode code )
  629. {
  630. // See if the preset control uses this key
  631. if( m_pLoadoutPresetPanel->HandlePresetKeyPressed( code ) )
  632. {
  633. return;
  634. }
  635. ButtonCode_t nButtonCode = GetBaseButtonCode( code );
  636. if (nButtonCode == KEY_XBUTTON_LEFT ||
  637. nButtonCode == KEY_XSTICK1_LEFT ||
  638. nButtonCode == KEY_XSTICK2_LEFT ||
  639. nButtonCode == STEAMCONTROLLER_DPAD_LEFT ||
  640. code == KEY_LEFT ||
  641. nButtonCode == KEY_XBUTTON_RIGHT ||
  642. nButtonCode == KEY_XSTICK1_RIGHT ||
  643. nButtonCode == KEY_XSTICK2_RIGHT ||
  644. nButtonCode == STEAMCONTROLLER_DPAD_RIGHT ||
  645. code == KEY_RIGHT ||
  646. nButtonCode == KEY_XBUTTON_UP ||
  647. nButtonCode == KEY_XSTICK1_UP ||
  648. nButtonCode == KEY_XSTICK2_UP ||
  649. nButtonCode == STEAMCONTROLLER_DPAD_UP ||
  650. code == KEY_UP ||
  651. nButtonCode == KEY_XBUTTON_DOWN ||
  652. nButtonCode == KEY_XSTICK1_DOWN ||
  653. nButtonCode == KEY_XSTICK2_DOWN ||
  654. nButtonCode == STEAMCONTROLLER_DPAD_DOWN ||
  655. code == KEY_DOWN )
  656. {
  657. // just eat all navigation keys so we don't
  658. // end up with undesirable navigation behavior bubbling from
  659. // one item model panel to another
  660. }
  661. else if( nButtonCode == KEY_XBUTTON_A || code == KEY_ENTER || nButtonCode == STEAMCONTROLLER_A )
  662. {
  663. // show the current loadout slot
  664. int nSelected = GetFirstSelectedItemIndex( true );
  665. if( nSelected != -1 )
  666. {
  667. OnCommand( VarArgs("change%d", nSelected ) );
  668. }
  669. }
  670. else
  671. {
  672. BaseClass::OnKeyCodePressed( code );
  673. }
  674. }
  675. //-----------------------------------------------------------------------------
  676. // Purpose:
  677. //-----------------------------------------------------------------------------
  678. void CClassLoadoutPanel::OnNavigateTo( const char* panelName )
  679. {
  680. CItemModelPanel *pChild = dynamic_cast<CItemModelPanel *>( FindChildByName( panelName ) );
  681. if( !pChild )
  682. return;
  683. pChild->SetSelected( true );
  684. SetBorderForItem( pChild, false );
  685. pChild->RequestFocus();
  686. }
  687. //-----------------------------------------------------------------------------
  688. // Purpose:
  689. //-----------------------------------------------------------------------------
  690. void CClassLoadoutPanel::OnNavigateFrom( const char* panelName )
  691. {
  692. CItemModelPanel *pChild = dynamic_cast<CItemModelPanel *>( FindChildByName( panelName ) );
  693. if( !pChild )
  694. return;
  695. pChild->SetSelected( false );
  696. SetBorderForItem( pChild, false );
  697. }
  698. //-----------------------------------------------------------------------------
  699. // Purpose:
  700. //-----------------------------------------------------------------------------
  701. void CClassLoadoutPanel::OnShowPanel( bool bVisible, bool bReturningFromArmory )
  702. {
  703. if ( bVisible )
  704. {
  705. // always start in character loadout page
  706. SetLoadoutPage( CHARACTER_LOADOUT_PAGE );
  707. if ( m_pSelectionPanel )
  708. {
  709. m_pSelectionPanel->SetVisible( false );
  710. m_pSelectionPanel->MarkForDeletion();
  711. m_pSelectionPanel = NULL;
  712. }
  713. m_iCurrentSlotIndex = TF_WPN_TYPE_PRIMARY;
  714. if( m_pItemModelPanels.Count() && m_pItemModelPanels[0] )
  715. {
  716. m_pItemModelPanels[0]->SetSelected( true );
  717. SetBorderForItem( m_pItemModelPanels[0], false );
  718. }
  719. m_bLoadoutHasChanged = false;
  720. if ( tf_show_preset_explanation_in_class_loadout.GetBool() && m_pPresetsExplanationPopup )
  721. {
  722. m_pPresetsExplanationPopup->Popup();
  723. tf_show_preset_explanation_in_class_loadout.SetValue( 0 );
  724. }
  725. else if ( tf_show_taunt_explanation_in_class_loadout.GetBool() && m_pTauntsExplanationPopup )
  726. {
  727. m_pTauntsExplanationPopup->Popup();
  728. tf_show_taunt_explanation_in_class_loadout.SetValue( 0 );
  729. }
  730. ClearItemOptionsMenu();
  731. }
  732. else
  733. {
  734. if ( m_pPlayerModelPanel )
  735. {
  736. m_pPlayerModelPanel->ClearCarriedItems();
  737. }
  738. }
  739. }
  740. //-----------------------------------------------------------------------------
  741. // Purpose:
  742. //-----------------------------------------------------------------------------
  743. void CClassLoadoutPanel::PostShowPanel( bool bVisible )
  744. {
  745. if ( bVisible )
  746. {
  747. if ( m_pPlayerModelPanel )
  748. {
  749. m_pPlayerModelPanel->SetVisible( true );
  750. }
  751. if ( m_pBuildablesButton )
  752. {
  753. m_pBuildablesButton->SetVisible( m_iCurrentClassIndex == TF_CLASS_ENGINEER );
  754. }
  755. }
  756. }
  757. //-----------------------------------------------------------------------------
  758. // Purpose:
  759. //-----------------------------------------------------------------------------
  760. void CClassLoadoutPanel::SetClass( int iClass )
  761. {
  762. m_iCurrentClassIndex = iClass;
  763. if ( m_pLoadoutPresetPanel )
  764. {
  765. m_pLoadoutPresetPanel->SetClass( m_iCurrentClassIndex );
  766. }
  767. }
  768. //-----------------------------------------------------------------------------
  769. // Purpose:
  770. //-----------------------------------------------------------------------------
  771. void CClassLoadoutPanel::SetTeam( int iTeam )
  772. {
  773. Assert( IsValidTFTeam( iTeam ) );
  774. m_iCurrentTeamIndex = iTeam;
  775. }
  776. //-----------------------------------------------------------------------------
  777. // Purpose:
  778. //-----------------------------------------------------------------------------
  779. int CClassLoadoutPanel::GetNumRelevantSlots() const
  780. {
  781. return m_pItemModelPanels.Count();
  782. }
  783. //-----------------------------------------------------------------------------
  784. // Purpose:
  785. //-----------------------------------------------------------------------------
  786. CEconItemView *CClassLoadoutPanel::GetItemInSlot( int iSlot )
  787. {
  788. if( iSlot >= 0 && iSlot < m_pItemModelPanels.Count() )
  789. {
  790. return m_pItemModelPanels[iSlot]->GetItem();
  791. }
  792. return NULL;
  793. }
  794. //-----------------------------------------------------------------------------
  795. // Purpose:
  796. //-----------------------------------------------------------------------------
  797. void CClassLoadoutPanel::FireGameEvent( IGameEvent *event )
  798. {
  799. // If we're not visible, ignore all events
  800. if ( !IsVisible() )
  801. return;
  802. BaseClass::FireGameEvent( event );
  803. // We need to update ourselves after the base has done it, so our item models have been updated
  804. const char *type = event->GetName();
  805. if ( Q_strcmp( "inventory_updated", type ) == 0 )
  806. {
  807. if ( m_pPlayerModelPanel )
  808. {
  809. m_pPlayerModelPanel->HoldItemInSlot( m_iCurrentSlotIndex );
  810. }
  811. }
  812. }
  813. void CClassLoadoutPanel::AddNewItemPanel( int iPanelIndex )
  814. {
  815. BaseClass::AddNewItemPanel( iPanelIndex );
  816. m_vecItemOptionButtons[ m_vecItemOptionButtons.AddToTail() ] = new CExButton( this,
  817. CFmtStr( "item_options_button%d", iPanelIndex ),
  818. "+",
  819. this,
  820. CFmtStr( "options%d", iPanelIndex ) );
  821. }
  822. //-----------------------------------------------------------------------------
  823. // Purpose:
  824. //-----------------------------------------------------------------------------
  825. void CClassLoadoutPanel::UpdateModelPanels( void )
  826. {
  827. // Search for a Robot Costume
  828. bool bIsRobot = false;
  829. static CSchemaAttributeDefHandle pAttrDef_PlayerRobot( "appear as mvm robot" );
  830. // For now, fill them out with the local player's currently wielded items
  831. for ( int i = 0; i < m_pItemModelPanels.Count(); i++ )
  832. {
  833. CEconItemView *pItemData = TFInventoryManager()->GetItemInLoadoutForClass( m_iCurrentClassIndex, i );
  834. if ( !pItemData )
  835. continue;
  836. if ( FindAttribute( pItemData, pAttrDef_PlayerRobot ) )
  837. {
  838. bIsRobot = true;
  839. break;
  840. }
  841. }
  842. // We're showing the loadout for a specific class.
  843. TFPlayerClassData_t *pData = GetPlayerClassData( m_iCurrentClassIndex );
  844. if ( m_pPlayerModelPanel )
  845. {
  846. m_pPlayerModelPanel->ClearCarriedItems();
  847. m_pPlayerModelPanel->SetToPlayerClass( m_iCurrentClassIndex, bIsRobot );
  848. m_pPlayerModelPanel->SetTeam( m_iCurrentTeamIndex );
  849. }
  850. // For now, fill them out with the local player's currently wielded items
  851. for ( int i = 0; i < m_pItemModelPanels.Count(); i++ )
  852. {
  853. CEconItemView *pItemData = TFInventoryManager()->GetItemInLoadoutForClass( m_iCurrentClassIndex, i );
  854. m_pItemModelPanels[i]->SetItem( pItemData );
  855. m_pItemModelPanels[i]->SetShowQuantity( true );
  856. m_pItemModelPanels[i]->SetSelected( false );
  857. SetBorderForItem( m_pItemModelPanels[i], false );
  858. if ( m_pPlayerModelPanel && pItemData && pItemData->IsValid() )
  859. {
  860. m_pPlayerModelPanel->AddCarriedItem( pItemData );
  861. }
  862. }
  863. if ( m_pPlayerModelPanel )
  864. {
  865. m_pPlayerModelPanel->HoldItemInSlot( m_iCurrentSlotIndex );
  866. }
  867. SetDialogVariable( "loadoutclass", g_pVGuiLocalize->Find( pData->m_szLocalizableName ) );
  868. UpdatePassiveAttributes();
  869. // Now layout again to position our item buttons
  870. InvalidateLayout();
  871. if ( m_pItemOptionPanel->IsVisible() )
  872. {
  873. m_pItemOptionPanel->UpdateItemOptionsUI();
  874. }
  875. }
  876. //-----------------------------------------------------------------------------
  877. // Purpose:
  878. //-----------------------------------------------------------------------------
  879. void CClassLoadoutPanel::OnItemPanelMouseReleased( vgui::Panel *panel )
  880. {
  881. CItemModelPanel *pItemPanel = dynamic_cast < CItemModelPanel * > ( panel );
  882. if ( pItemPanel && IsVisible() )
  883. {
  884. for ( int i = 0; i < m_pItemModelPanels.Count(); i++ )
  885. {
  886. if ( m_pItemModelPanels[i] == pItemPanel )
  887. {
  888. OnCommand( VarArgs("change%d", i) );
  889. return;
  890. }
  891. }
  892. }
  893. }
  894. //-----------------------------------------------------------------------------
  895. // Purpose:
  896. //-----------------------------------------------------------------------------
  897. void CClassLoadoutPanel::OnSelectionReturned( KeyValues *data )
  898. {
  899. if ( data )
  900. {
  901. uint64 ulIndex = data->GetUint64( "itemindex", INVALID_ITEM_ID );
  902. // ulIndex implies do nothing (escape key)
  903. if ( ulIndex != 0 )
  904. {
  905. TFInventoryManager()->EquipItemInLoadout( m_iCurrentClassIndex, m_iCurrentSlotIndex, ulIndex );
  906. m_bLoadoutHasChanged = true;
  907. UpdateModelPanels();
  908. // Send the preset panel a msg so it can save the change
  909. KeyValues *pLoadoutChangedMsg = new KeyValues( "LoadoutChanged" );
  910. pLoadoutChangedMsg->SetInt( "slot", m_iCurrentSlotIndex );
  911. pLoadoutChangedMsg->SetUint64( "itemid", ulIndex );
  912. PostMessage( m_pLoadoutPresetPanel, pLoadoutChangedMsg );
  913. }
  914. }
  915. PostMessage( GetParent(), new KeyValues("SelectionEnded") );
  916. // It'll have deleted itself, so we don't need to clean it up
  917. m_pSelectionPanel = NULL;
  918. OnCancelSelection();
  919. // find the selected item and give it the focus
  920. CItemModelPanel *pSelection = GetFirstSelectedItemModelPanel( true );
  921. if( !pSelection )
  922. {
  923. m_pItemModelPanels[0]->SetSelected( true );
  924. pSelection = m_pItemModelPanels[0];
  925. }
  926. pSelection->RequestFocus();
  927. }
  928. //-----------------------------------------------------------------------------
  929. // Purpose:
  930. //-----------------------------------------------------------------------------
  931. void CClassLoadoutPanel::OnCancelSelection( void )
  932. {
  933. if ( m_pSelectionPanel )
  934. {
  935. m_pSelectionPanel->SetVisible( false );
  936. m_pSelectionPanel->MarkForDeletion();
  937. m_pSelectionPanel = NULL;
  938. }
  939. if ( m_pPlayerModelPanel )
  940. {
  941. m_pPlayerModelPanel->SetVisible( true );
  942. }
  943. }
  944. //-----------------------------------------------------------------------------
  945. // Purpose:
  946. //-----------------------------------------------------------------------------
  947. void CClassLoadoutPanel::RespawnPlayer()
  948. {
  949. if ( tf_respawn_on_loadoutchanges.GetBool() )
  950. {
  951. // Tell the GC to tell server that we should respawn if we're in a respawn room
  952. GCSDK::CGCMsg< MsgGCEmpty_t > msg( k_EMsgGCRespawnPostLoadoutChange );
  953. GCClientSystem()->BSendMessage( msg );
  954. }
  955. }
  956. //-----------------------------------------------------------------------------
  957. // Purpose: Apply KVs to the item option buttons
  958. //-----------------------------------------------------------------------------
  959. void CClassLoadoutPanel::ApplyKVsToItemPanels( void )
  960. {
  961. BaseClass::ApplyKVsToItemPanels();
  962. if ( m_pItemOptionPanelKVs )
  963. {
  964. for ( int i = 0; i < m_vecItemOptionButtons.Count(); i++ )
  965. {
  966. m_vecItemOptionButtons[i]->ApplySettings( m_pItemOptionPanelKVs );
  967. m_vecItemOptionButtons[i]->InvalidateLayout();
  968. }
  969. }
  970. }
  971. //-----------------------------------------------------------------------------
  972. // Purpose:
  973. //-----------------------------------------------------------------------------
  974. void CClassLoadoutPanel::OnClosing( void )
  975. {
  976. if ( m_pPlayerModelPanel )
  977. {
  978. m_pPlayerModelPanel->ClearCarriedItems();
  979. }
  980. if ( m_bLoadoutHasChanged )
  981. {
  982. RespawnPlayer();
  983. m_bLoadoutHasChanged = false;
  984. }
  985. }
  986. extern const char *g_szItemBorders[AE_MAX_TYPES][5];
  987. extern ConVar cl_showbackpackrarities;
  988. //-----------------------------------------------------------------------------
  989. // Purpose:
  990. //-----------------------------------------------------------------------------
  991. void CClassLoadoutPanel::SetBorderForItem( CItemModelPanel *pItemPanel, bool bMouseOver )
  992. {
  993. if ( !pItemPanel )
  994. return;
  995. const char *pszBorder = NULL;
  996. if ( pItemPanel->IsGreyedOut() )
  997. {
  998. pszBorder = "EconItemBorder";
  999. }
  1000. else
  1001. {
  1002. int iRarity = 0;
  1003. if ( pItemPanel->HasItem() && cl_showbackpackrarities.GetBool() )
  1004. {
  1005. iRarity = pItemPanel->GetItem()->GetItemQuality();
  1006. uint8 nRarity = pItemPanel->GetItem()->GetItemDefinition()->GetRarity();
  1007. if ( ( nRarity != k_unItemRarity_Any ) && ( iRarity != AE_SELFMADE ) )
  1008. {
  1009. // translate this quality to rarity
  1010. iRarity = nRarity + AE_RARITY_DEFAULT;
  1011. }
  1012. if ( iRarity > 0 )
  1013. {
  1014. if ( bMouseOver || pItemPanel->IsSelected() )
  1015. {
  1016. pszBorder = g_szItemBorders[iRarity][1];
  1017. }
  1018. else
  1019. {
  1020. pszBorder = g_szItemBorders[iRarity][0];
  1021. }
  1022. }
  1023. }
  1024. if ( iRarity == 0 )
  1025. {
  1026. if ( bMouseOver || pItemPanel->IsSelected() )
  1027. {
  1028. pszBorder = "LoadoutItemMouseOverBorder";
  1029. }
  1030. else
  1031. {
  1032. pszBorder = "EconItemBorder";
  1033. }
  1034. }
  1035. }
  1036. vgui::IScheme *pScheme = vgui::scheme()->GetIScheme( GetScheme() );
  1037. pItemPanel->SetBorder( pScheme->GetBorder( pszBorder ) );
  1038. }
  1039. //-----------------------------------------------------------------------------
  1040. // Purpose: Clear the item options menu and reset the button that summoned it
  1041. //-----------------------------------------------------------------------------
  1042. void CClassLoadoutPanel::ClearItemOptionsMenu( void )
  1043. {
  1044. SetOptionsButtonText( m_pItemOptionPanel->GetItemSlot(), "+" );
  1045. m_pItemOptionPanel->SetItemSlot( LOADOUT_POSITION_INVALID, m_iCurrentClassIndex );
  1046. m_pItemOptionPanel->SetVisible( false );
  1047. }
  1048. //-----------------------------------------------------------------------------
  1049. // Purpose: Safely set the text for a button
  1050. //-----------------------------------------------------------------------------
  1051. void CClassLoadoutPanel::SetOptionsButtonText( int nIndex, const char* pszText )
  1052. {
  1053. if ( nIndex >= 0 && nIndex < m_vecItemOptionButtons.Count() )
  1054. {
  1055. m_vecItemOptionButtons[ m_pItemOptionPanel->GetItemSlot() ]->SetText( pszText );
  1056. }
  1057. }
  1058. //-----------------------------------------------------------------------------
  1059. // Purpose: Return if the passed in item has any options
  1060. //-----------------------------------------------------------------------------
  1061. bool CClassLoadoutPanel::AnyOptionsAvailableForItem( const CEconItemView *pItem )
  1062. {
  1063. if ( !pItem )
  1064. return false;
  1065. // Styles!
  1066. if ( pItem->GetStaticData()->GetNumSelectableStyles() > 1 )
  1067. return true;
  1068. // Unusual particle effect! For Cosmetics only
  1069. static CSchemaAttributeDefHandle pAttrDef_AttachParticleEffect( "attach particle effect" );
  1070. if ( pItem->FindAttribute( pAttrDef_AttachParticleEffect ) && pItem->GetItemDefinition()->GetLoadoutSlot( 0 ) >= LOADOUT_POSITION_HEAD )
  1071. return true;
  1072. return false;
  1073. }
  1074. //-----------------------------------------------------------------------------
  1075. // Purpose:
  1076. //-----------------------------------------------------------------------------
  1077. void CClassLoadoutPanel::SetLoadoutPage( classloadoutpage_t loadoutPage )
  1078. {
  1079. ClearItemOptionsMenu();
  1080. switch ( loadoutPage )
  1081. {
  1082. case CHARACTER_LOADOUT_PAGE:
  1083. {
  1084. m_bInTauntLoadoutMode = false;
  1085. }
  1086. break;
  1087. case TAUNT_LOADOUT_PAGE:
  1088. {
  1089. m_bInTauntLoadoutMode = true;
  1090. }
  1091. break;
  1092. default:
  1093. {
  1094. // Unhandled loadout page
  1095. Assert( 0 );
  1096. }
  1097. }
  1098. InvalidateLayout();
  1099. }
  1100. //-----------------------------------------------------------------------------
  1101. // Purpose:
  1102. //-----------------------------------------------------------------------------
  1103. void CClassLoadoutPanel::OnCommand( const char *command )
  1104. {
  1105. if ( FStrEq( command, "characterloadout" ) )
  1106. {
  1107. SetLoadoutPage( CHARACTER_LOADOUT_PAGE );
  1108. return;
  1109. }
  1110. else if ( FStrEq( command, "tauntloadout" ) )
  1111. {
  1112. SetLoadoutPage( TAUNT_LOADOUT_PAGE );
  1113. return;
  1114. }
  1115. else if ( !V_strnicmp( command, "change", 6 ) )
  1116. {
  1117. const char *pszNum = command+6;
  1118. if ( pszNum && pszNum[0] )
  1119. {
  1120. int iSlot = atoi(pszNum);
  1121. if ( iSlot >= 0 && iSlot < CLASS_LOADOUT_POSITION_COUNT && m_iCurrentClassIndex != TF_CLASS_UNDEFINED )
  1122. {
  1123. if ( m_iCurrentSlotIndex != iSlot )
  1124. {
  1125. m_iCurrentSlotIndex = iSlot;
  1126. }
  1127. // Create the selection screen. It removes itself on close.
  1128. m_pSelectionPanel = new CEquipSlotItemSelectionPanel( this, m_iCurrentClassIndex, iSlot );
  1129. m_pSelectionPanel->ShowPanel( 0, true );
  1130. if ( m_pPlayerModelPanel )
  1131. {
  1132. m_pPlayerModelPanel->SetVisible( false );
  1133. }
  1134. ClearItemOptionsMenu();
  1135. PostMessage( GetParent(), new KeyValues("SelectionStarted") );
  1136. }
  1137. }
  1138. return;
  1139. }
  1140. else if ( !V_strnicmp( command, "options", 7 ) )
  1141. {
  1142. const char *pszNum = command + 7;
  1143. if( pszNum && pszNum[0] )
  1144. {
  1145. int iSlot = atoi( pszNum );
  1146. //iSlot = g_VisibleLoadoutSlotsPerClass[m_iCurrentClassIndex]->m_iPos[iSlot - 1];
  1147. if ( iSlot >= 0 && iSlot < m_vecItemOptionButtons.Count() && m_iCurrentClassIndex != TF_CLASS_UNDEFINED )
  1148. {
  1149. // Change the button we're coming from to be a "+"
  1150. SetOptionsButtonText( m_pItemOptionPanel->GetItemSlot(), "+" );
  1151. // Update the current slot index for callback from the setstyle button.
  1152. // It will send us a message to change the item the player model is holding
  1153. // and we need this to be updated for that.
  1154. m_iCurrentSlotIndex = iSlot;
  1155. // Did they just toggle?
  1156. if ( m_pItemOptionPanel->GetItemSlot() == iSlot )
  1157. {
  1158. m_pItemOptionPanel->SetVisible( !m_pItemOptionPanel->IsVisible() );
  1159. }
  1160. else
  1161. {
  1162. // Set the options panel to have the data for this slot
  1163. m_pItemOptionPanel->SetItemSlot( (loadout_positions_t)iSlot, m_iCurrentClassIndex );
  1164. m_pItemOptionPanel->SetVisible( true );
  1165. // Figure out if this is on the left or right
  1166. int iColumnHeight = 4;
  1167. int iColumn = iSlot / iColumnHeight;
  1168. PinCorner_e myCornerToPin = iColumn == 0 ? PIN_TOPLEFT : PIN_TOPRIGHT;
  1169. PinCorner_e siblingCornerPinTo = iColumn == 0 ? PIN_TOPRIGHT : PIN_TOPLEFT;
  1170. // Pin to the appropriate side
  1171. int iButtonPos = g_VisibleLoadoutSlotsPerClass[m_iCurrentClassIndex]->m_iPos[ iSlot ] - 1;
  1172. m_pItemOptionPanel->PinToSibling( m_vecItemOptionButtons[ iButtonPos ]->GetName(), myCornerToPin, siblingCornerPinTo );
  1173. m_pItemOptionPanel->UpdateItemOptionsUI();
  1174. }
  1175. // Change the button we're going to to be "-" if we're visible, "+" if we're not
  1176. SetOptionsButtonText( m_pItemOptionPanel->GetItemSlot(), m_pItemOptionPanel->IsVisible() ? "-" : "+" );
  1177. }
  1178. return;
  1179. }
  1180. }
  1181. BaseClass::OnCommand( command );
  1182. }
  1183. //-----------------------------------------------------------------------------
  1184. void CClassLoadoutPanel::OnMessage( const KeyValues* pParams, vgui::VPANEL hFromPanel )
  1185. {
  1186. BaseClass::OnMessage( pParams, hFromPanel );
  1187. }
  1188. //-----------------------------------------------------------------------------
  1189. // Purpose:
  1190. //-----------------------------------------------------------------------------
  1191. struct passive_attrib_to_print_t
  1192. {
  1193. const CEconItemAttributeDefinition *m_pAttrDef;
  1194. attrib_value_t m_value;
  1195. };
  1196. //-----------------------------------------------------------------------------
  1197. // Purpose:
  1198. //-----------------------------------------------------------------------------
  1199. class CAttributeIterator_AddPassiveAttribsToPassiveList : public CEconItemSpecificAttributeIterator
  1200. {
  1201. public:
  1202. CAttributeIterator_AddPassiveAttribsToPassiveList( CUtlVector<passive_attrib_to_print_t> *pList, bool bForceAdd )
  1203. : m_pList( pList )
  1204. , m_bForceAdd( bForceAdd )
  1205. {
  1206. Assert( m_pList );
  1207. }
  1208. virtual bool OnIterateAttributeValue( const CEconItemAttributeDefinition *pAttrDef, attrib_value_t value )
  1209. {
  1210. Assert( pAttrDef );
  1211. if ( pAttrDef->IsHidden() )
  1212. return true;
  1213. if ( !m_bForceAdd )
  1214. {
  1215. const char *pDesc = pAttrDef->GetArmoryDescString();
  1216. if ( !pDesc || !pDesc[0] )
  1217. return true;
  1218. // If we have the "on_wearer" key, we're a passive attribute
  1219. if ( !Q_stristr(pDesc, "on_wearer") )
  1220. return true;
  1221. }
  1222. // Now see if we're already in the list
  1223. FOR_EACH_VEC( (*m_pList), i )
  1224. {
  1225. passive_attrib_to_print_t& passiveAttr = (*m_pList)[i];
  1226. Assert( passiveAttr.m_pAttrDef );
  1227. // We match if our class is the same -- this is a case-sensitive compare!
  1228. if ( Q_strcmp( passiveAttr.m_pAttrDef->GetAttributeClass(), pAttrDef->GetAttributeClass() ) )
  1229. continue;
  1230. // We've found a matching attribute. Collate our values and stomp over the earlier value.
  1231. passiveAttr.m_value = CollateAttributeValues( passiveAttr.m_pAttrDef, passiveAttr.m_value, pAttrDef, value );
  1232. return true;
  1233. }
  1234. // We didn't find it. Add it to the list.
  1235. passive_attrib_to_print_t newPassiveAttr = { pAttrDef, value };
  1236. m_pList->AddToTail( newPassiveAttr );
  1237. return true;
  1238. }
  1239. // Other types are ignored.
  1240. private:
  1241. CUtlVector<passive_attrib_to_print_t> *m_pList;
  1242. bool m_bForceAdd;
  1243. };
  1244. //-----------------------------------------------------------------------------
  1245. void CClassLoadoutPanel::UpdatePassiveAttributes( void )
  1246. {
  1247. if ( !m_pPassiveAttribsLabel )
  1248. return;
  1249. // We build a list of attributes & associated values by looping through all equipped items.
  1250. // This way we can identify & collate attributes based on the same definition index.
  1251. CUtlVector<passive_attrib_to_print_t> vecAttribsToPrint;
  1252. // Loop through all equipped items
  1253. for ( int i = 0; i < m_pItemModelPanels.Count(); i++ )
  1254. {
  1255. CEconItemView *pItemData = TFInventoryManager()->GetItemInLoadoutForClass( m_iCurrentClassIndex, i );
  1256. if ( pItemData && pItemData->IsValid() )
  1257. {
  1258. CAttributeIterator_AddPassiveAttribsToPassiveList attrItPassives( &vecAttribsToPrint, false );
  1259. pItemData->IterateAttributes( &attrItPassives );
  1260. }
  1261. }
  1262. // Then add any set bonuses
  1263. if ( steamapicontext && steamapicontext->SteamUser() )
  1264. {
  1265. CSteamID localSteamID = steamapicontext->SteamUser()->GetSteamID();
  1266. CUtlVector<const CEconItemSetDefinition *> pActiveSets;
  1267. TFInventoryManager()->GetActiveSets( &pActiveSets, localSteamID, m_iCurrentClassIndex );
  1268. FOR_EACH_VEC( pActiveSets, set )
  1269. {
  1270. CAttributeIterator_AddPassiveAttribsToPassiveList attrItSetPassives( &vecAttribsToPrint, true );
  1271. pActiveSets[set]->IterateAttributes( &attrItSetPassives );
  1272. }
  1273. }
  1274. // Now build the text
  1275. wchar_t wszPassiveDesc[4096];
  1276. wszPassiveDesc[0] = '\0';
  1277. m_pPassiveAttribsLabel->GetTextImage()->ClearColorChangeStream();
  1278. wchar_t *pHeader = g_pVGuiLocalize->Find( "#TF_PassiveAttribs" );
  1279. if ( pHeader )
  1280. {
  1281. V_wcscpy_safe( wszPassiveDesc, pHeader );
  1282. V_wcscat_safe( wszPassiveDesc, L"\n" );
  1283. }
  1284. if ( vecAttribsToPrint.Count() )
  1285. {
  1286. FOR_EACH_VEC( vecAttribsToPrint, i )
  1287. {
  1288. CEconAttributeDescription AttrDesc( GLocalizationProvider(), vecAttribsToPrint[i].m_pAttrDef, vecAttribsToPrint[i].m_value );
  1289. AddAttribPassiveText( AttrDesc, wszPassiveDesc, ARRAYSIZE(wszPassiveDesc) );
  1290. }
  1291. }
  1292. else
  1293. {
  1294. vgui::IScheme *pScheme = vgui::scheme()->GetIScheme( GetScheme() );
  1295. Color col = pScheme->GetColor( GetColorNameForAttribColor( ATTRIB_COL_NEUTRAL ), Color(255,255,255,255) );
  1296. m_pPassiveAttribsLabel->GetTextImage()->AddColorChange( col, Q_wcslen( wszPassiveDesc ) );
  1297. wchar_t *pNone = g_pVGuiLocalize->Find( "#TF_PassiveAttribs_None" );
  1298. if ( pNone )
  1299. {
  1300. V_wcscat_safe( wszPassiveDesc, pNone );
  1301. }
  1302. }
  1303. m_pPassiveAttribsLabel->SetText( wszPassiveDesc );
  1304. }
  1305. //-----------------------------------------------------------------------------
  1306. // Purpose:
  1307. //-----------------------------------------------------------------------------
  1308. void CClassLoadoutPanel::AddAttribPassiveText( const CEconAttributeDescription& AttrDesc, INOUT_Z_CAP(iNumPassiveChars) wchar_t *out_wszPassiveDesc, int iNumPassiveChars )
  1309. {
  1310. vgui::IScheme *pScheme = vgui::scheme()->GetIScheme( GetScheme() );
  1311. Assert( pScheme );
  1312. if ( !AttrDesc.GetDescription().IsEmpty() )
  1313. {
  1314. // Insert the color change at the current position
  1315. Color col = pScheme->GetColor( GetColorNameForAttribColor( AttrDesc.GetDefaultColor() ), Color(255,255,255,255) );
  1316. m_pPassiveAttribsLabel->GetTextImage()->AddColorChange( col, Q_wcslen( out_wszPassiveDesc ) );
  1317. // Now append the text of the attribute
  1318. V_wcsncat( out_wszPassiveDesc, AttrDesc.GetDescription().Get(), iNumPassiveChars );
  1319. V_wcsncat( out_wszPassiveDesc, L"\n", iNumPassiveChars );
  1320. }
  1321. }
  1322. //-----------------------------------------------------------------------------
  1323. // Purpose:
  1324. //-----------------------------------------------------------------------------
  1325. void CClassLoadoutPanel::UpdatePageButtonColor( CExImageButton *pPageButton, bool bIsActive )
  1326. {
  1327. if ( pPageButton )
  1328. {
  1329. int iLoaded = bIsActive ? LOADED : NOTLOADED;
  1330. pPageButton->SetDefaultColor( m_aDefaultColors[iLoaded][FG][DEFAULT], m_aDefaultColors[iLoaded][BG][DEFAULT] );
  1331. pPageButton->SetArmedColor( m_aDefaultColors[iLoaded][FG][ARMED], m_aDefaultColors[iLoaded][BG][ARMED] );
  1332. pPageButton->SetDepressedColor( m_aDefaultColors[iLoaded][FG][DEPRESSED], m_aDefaultColors[iLoaded][BG][DEPRESSED] );
  1333. }
  1334. }