//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: // //=============================================================================// #include "cbase.h" #include "class_loadout_panel.h" #include "c_tf_player.h" #include "vgui_controls/CheckButton.h" #include "econ_gcmessages.h" #include "gc_clientsystem.h" #include "tf_item_system.h" #include "loadout_preset_panel.h" #include "econ_item_description.h" #include "item_style_select_dialog.h" #include "vgui/IInput.h" #include "vgui_controls/PanelListPanel.h" // memdbgon must be the last include file in a .cpp file!!! #include extern ConVar tf_respawn_on_loadoutchanges; ConVar tf_show_preset_explanation_in_class_loadout( "tf_show_preset_explanation_in_class_loadout", "1", FCVAR_HIDDEN | FCVAR_CLIENTDLL | FCVAR_ARCHIVE ); ConVar tf_show_taunt_explanation_in_class_loadout( "tf_show_taunt_explanation_in_class_loadout", "1", FCVAR_HIDDEN | FCVAR_CLIENTDLL | FCVAR_ARCHIVE ); void ParticleSlider_UpdateRequest( int iLoadoutPosition, float value ) { CClassLoadoutPanel *pPanel = g_pClassLoadoutPanel; if ( !pPanel ) return; CEconItemView *pHat = pPanel->GetItemInSlot( iLoadoutPosition ); if ( !pHat ) return; // does this hat even have a particle effect static CSchemaAttributeDefHandle pAttrDef_AttachParticleEffect( "attach particle effect" ); uint32 iHasEffect = 0; if ( !pHat->FindAttribute( pAttrDef_AttachParticleEffect, &iHasEffect ) ) return; // Check for use head toggle static CSchemaAttributeDefHandle pAttrDef_UseHeadOrigin( "particle effect use head origin" ); uint32 iUseHead = 0; if ( !pHat->FindAttribute( pAttrDef_UseHeadOrigin, &iUseHead ) || iUseHead == 0 ) return; // Look for the attribute and request to change it static CSchemaAttributeDefHandle pAttrDef_VerticalOffset( "particle effect vertical offset" ); uint32 iOffSet = 0; if ( !pHat->FindAttribute( pAttrDef_VerticalOffset, &iOffSet ) && value == 0 ) { return; } else { const float& flAttrValue = (float&)iOffSet; if ( value == flAttrValue ) return; // no change do nothing } // Send a message to the GC to request a change GCSDK::CProtoBufMsg msg( k_EMsgGCSetItemEffectVerticalOffset ); msg.Body().set_item_id( pHat->GetItemID() ); msg.Body().set_offset( value ); GCClientSystem()->BSendMessage( msg ); } void HatOffset_Callback( IConVar *pConVar, char const *pOldString, float flOldValue ) { ConVarRef cVarRef( pConVar ); ParticleSlider_UpdateRequest( LOADOUT_POSITION_HEAD, cVarRef.GetFloat() ); } ConVar tf_hat_effect_offset( "tf_hat_effect_offset", "0", FCVAR_DEVELOPMENTONLY, "Adjust the position of the unusual effect for your hat.", HatOffset_Callback ); void Misc1Offset_Callback( IConVar *pConVar, char const *pOldString, float flOldValue ) { ConVarRef cVarRef( pConVar ); ParticleSlider_UpdateRequest( LOADOUT_POSITION_MISC, cVarRef.GetFloat() ); } ConVar tf_misc1_effect_offset( "tf_misc1_effect_offset", "0", FCVAR_DEVELOPMENTONLY, "Adjust the position of the unusual effect for your hat.", Misc1Offset_Callback ); void Misc2Offset_Callback( IConVar *pConVar, char const *pOldString, float flOldValue ) { ConVarRef cVarRef( pConVar ); ParticleSlider_UpdateRequest( LOADOUT_POSITION_MISC2, cVarRef.GetFloat() ); } ConVar tf_misc2_effect_offset( "tf_misc2_effect_offset", "0", FCVAR_DEVELOPMENTONLY, "Adjust the position of the unusual effect for your hat.", Misc2Offset_Callback ); // Hacky solution to different classes wanting different slots visible in their loadouts, and in different positions struct LoadoutPanelPositioningInstance { int m_iPos[NUM_ITEM_PANELS_IN_LOADOUT]; }; bool IsTauntPanelPosition( int iButtonPos ) { return iButtonPos >= 9 && iButtonPos <= 16; } const LoadoutPanelPositioningInstance g_DefaultLoadoutPanelPositioning = { { 1, // LOADOUT_POSITION_PRIMARY = 0, 2, // LOADOUT_POSITION_SECONDARY, 3, // LOADOUT_POSITION_MELEE, 0, // LOADOUT_POSITION_UTILITY, // STAGING ONLY 0, // LOADOUT_POSITION_BUILDING, 0, // LOADOUT_POSITION_PDA, 0, // LOADOUT_POSITION_PDA2, 5, // LOADOUT_POSITION_HEAD, 6, // LOADOUT_POSITION_MISC, 8, // LOADOUT_POSITION_ACTION, 7, // LOADOUT_POSITION_MISC2, 9, // LOADOUT_POSITION_TAUNT, 10, // LOADOUT_POSITION_TAUNT2, 11, // LOADOUT_POSITION_TAUNT3, 12, // LOADOUT_POSITION_TAUNT4, 13, // LOADOUT_POSITION_TAUNT5, 14, // LOADOUT_POSITION_TAUNT6, 15, // LOADOUT_POSITION_TAUNT7, 16, // LOADOUT_POSITION_TAUNT8, #ifdef STAGING_ONLY 0, // LOADOUT_POSITION_PDA_ADDON1, 0, // LOADOUT_POSITION_PDA_ADDON2, 0, // LOADOUT_POSITION_PDA3, //9, // LOADOUT_POSITION_MISC3, //10, // LOADOUT_POSITION_MISC4, //11, // LOADOUT_POSITION_MISC5, //12, // LOADOUT_POSITION_MISC6, //13, // LOADOUT_POSITION_MISC7, //14, // LOADOUT_POSITION_MISC8, //15, // LOADOUT_POSITION_MISC9, //16, // LOADOUT_POSITION_MISC10, 0, // LOADOUT_POSITION_BUILDING2, #endif // STAGING_ONLY } }; const LoadoutPanelPositioningInstance g_LoadoutPanelPositioning_Spy = { { 0, // LOADOUT_POSITION_PRIMARY = 0, 1, // LOADOUT_POSITION_SECONDARY, 2, // LOADOUT_POSITION_MELEE, 0, // LOADOUT_POSITION_UTILITY, // STAGING ONLY 4, // LOADOUT_POSITION_BUILDING, // sapper 0, // LOADOUT_POSITION_PDA, // disguise kit (Hidden) 3, // LOADOUT_POSITION_PDA2, // Watch 5, // LOADOUT_POSITION_HEAD, 6, // LOADOUT_POSITION_MISC, 8, // LOADOUT_POSITION_ACTION, 7, // LOADOUT_POSITION_MISC2, 9, // LOADOUT_POSITION_TAUNT, 10, // LOADOUT_POSITION_TAUNT2, 11, // LOADOUT_POSITION_TAUNT3, 12, // LOADOUT_POSITION_TAUNT4, 13, // LOADOUT_POSITION_TAUNT5, 14, // LOADOUT_POSITION_TAUNT6, 15, // LOADOUT_POSITION_TAUNT7, 16, // LOADOUT_POSITION_TAUNT8, #ifdef STAGING_ONLY 0, // LOADOUT_POSITION_PDA_ADDON1, 0, // LOADOUT_POSITION_PDA_ADDON2, 0, // LOADOUT_POSITION_PDA3, //9, // LOADOUT_POSITION_MISC3, //10, // LOADOUT_POSITION_MISC4, //11, // LOADOUT_POSITION_MISC5, //12, // LOADOUT_POSITION_MISC6, //13, // LOADOUT_POSITION_MISC7, //14, // LOADOUT_POSITION_MISC8, //15, // LOADOUT_POSITION_MISC9, //16, // LOADOUT_POSITION_MISC10, 0, // LOADOUT_POSITION_BUILDING2, #endif // STAGING_ONLY } }; const LoadoutPanelPositioningInstance g_LoadoutPanelPositioning_Engineer = { { 1, // LOADOUT_POSITION_PRIMARY = 0, 2, // LOADOUT_POSITION_SECONDARY, 3, // LOADOUT_POSITION_MELEE, 0, // LOADOUT_POSITION_UTILITY, // STAGING ONLY 0, // LOADOUT_POSITION_BUILDING, 4, // LOADOUT_POSITION_PDA, 0, // LOADOUT_POSITION_PDA2, 5, // LOADOUT_POSITION_HEAD, 6, // LOADOUT_POSITION_MISC, 8, // LOADOUT_POSITION_ACTION, 7, // LOADOUT_POSITION_MISC2, 9, // LOADOUT_POSITION_TAUNT, 10, // LOADOUT_POSITION_TAUNT2, 11, // LOADOUT_POSITION_TAUNT3, 12, // LOADOUT_POSITION_TAUNT4, 13, // LOADOUT_POSITION_TAUNT5, 14, // LOADOUT_POSITION_TAUNT6, 15, // LOADOUT_POSITION_TAUNT7, 16, // LOADOUT_POSITION_TAUNT8, #ifdef STAGING_ONLY 17, // LOADOUT_POSITION_PDA_ADDON1, 18, // LOADOUT_POSITION_PDA_ADDON2, 0, // LOADOUT_POSITION_PDA3, //9, // LOADOUT_POSITION_MISC3, //10, // LOADOUT_POSITION_MISC4, //11, // LOADOUT_POSITION_MISC5, //12, // LOADOUT_POSITION_MISC6, //13, // LOADOUT_POSITION_MISC7, //14, // LOADOUT_POSITION_MISC8, //15, // LOADOUT_POSITION_MISC9, //16, // LOADOUT_POSITION_MISC10, 0, // LOADOUT_POSITION_BUILDING2, #endif // STAGING_ONLY } }; const LoadoutPanelPositioningInstance *g_VisibleLoadoutSlotsPerClass[] = { &g_DefaultLoadoutPanelPositioning, // TF_CLASS_UNDEFINED &g_DefaultLoadoutPanelPositioning, // TF_CLASS_SCOUT &g_DefaultLoadoutPanelPositioning, // TF_CLASS_SNIPER &g_DefaultLoadoutPanelPositioning, // TF_CLASS_SOLDIER &g_DefaultLoadoutPanelPositioning, // TF_CLASS_DEMOMAN &g_DefaultLoadoutPanelPositioning, // TF_CLASS_MEDIC &g_DefaultLoadoutPanelPositioning, // TF_CLASS_HEAVYWEAPONS &g_DefaultLoadoutPanelPositioning, // TF_CLASS_PYRO &g_LoadoutPanelPositioning_Spy, // TF_CLASS_SPY &g_LoadoutPanelPositioning_Engineer, // TF_CLASS_ENGINEER }; COMPILE_TIME_ASSERT( ARRAYSIZE( g_VisibleLoadoutSlotsPerClass ) == TF_LAST_NORMAL_CLASS ); //----------------------------------------------------------------------------- // Particle Effect Slider //----------------------------------------------------------------------------- CLoadoutItemOptionsPanel::CLoadoutItemOptionsPanel( Panel *parent, const char *pName ) : vgui::EditablePanel( parent, pName ) { m_pHatParticleSlider = NULL; m_pHatParticleUseHeadButton = NULL; m_iCurrentClassIndex = -1; m_eItemSlot = LOADOUT_POSITION_INVALID; m_pListPanel = new vgui::PanelListPanel( this, "PanelListPanel" ); m_pListPanel->SetFirstColumnWidth( 0 ); m_pHatParticleSlider = new CCvarSlider( m_pListPanel, "HatParticleSlider" ); m_pHatParticleSlider->AddActionSignalTarget( this ); m_pHatParticleUseHeadButton = new vgui::CheckButton( m_pListPanel, "HatUseHeadCheckButton", "#GameUI_ParticleHatUseHead" ); m_pHatParticleUseHeadButton->AddActionSignalTarget( this ); m_pSetStyleButton = new CExButton( m_pListPanel, "SetStyleButton", "#TF_Item_SelectStyle" ); m_pSetStyleButton->AddActionSignalTarget( this ); } //----------------------------------------------------------------------------- void CLoadoutItemOptionsPanel::ApplySchemeSettings( vgui::IScheme *pScheme ) { BaseClass::ApplySchemeSettings( pScheme ); LoadControlSettings( "resource/UI/ItemOptionsPanel.res" ); } //----------------------------------------------------------------------------- void CLoadoutItemOptionsPanel::PerformLayout( void ) { BaseClass::PerformLayout(); m_pHatParticleSlider->SetTickColor( Color( 235, 226, 202, 255 ) ); // tanlight } //----------------------------------------------------------------------------- void CLoadoutItemOptionsPanel::OnCommand( const char *command ) { if ( FStrEq( command, "particle_button_clicked" ) ) { UpdateItemOptionsUI(); return; } else if ( FStrEq( command, "particle_use_head_clicked" ) ) { // Grab current hat CEconItemView *pHat = TFInventoryManager()->GetItemInLoadoutForClass( m_iCurrentClassIndex, m_eItemSlot ); if ( !pHat ) return; // does this hat even have a particle effect static CSchemaAttributeDefHandle pAttrDef_AttachParticleEffect( "attach particle effect" ); uint32 iHasEffect = 0; if ( !pHat->FindAttribute( pAttrDef_AttachParticleEffect, &iHasEffect ) ) return; // Send a message to the GC to request a change GCSDK::CProtoBufMsg msg( k_EMsgGCSetHatEffectUseHeadOrigin ); msg.Body().set_item_id( pHat->GetItemID() ); msg.Body().set_use_head( m_pHatParticleUseHeadButton->IsSelected() ); GCClientSystem()->BSendMessage( msg ); return; } else if ( FStrEq( command, "set_style" ) ) { CEconItemView *pHat = TFInventoryManager()->GetItemInLoadoutForClass( m_iCurrentClassIndex, m_eItemSlot ); CStyleSelectDialog *pStyle = vgui::SETUP_PANEL( new CStyleSelectDialog( GetParent(), pHat ) ); if ( pStyle ) { pStyle->Show(); } } } //----------------------------------------------------------------------------- void CLoadoutItemOptionsPanel::OnMessage( const KeyValues* pParams, vgui::VPANEL hFromPanel ) { if ( FStrEq( pParams->GetName(), "SliderDragEnd" ) ) { m_pHatParticleSlider->ApplyChanges(); } BaseClass::OnMessage( pParams, hFromPanel ); } //----------------------------------------------------------------------------- void CLoadoutItemOptionsPanel::SetItemSlot( loadout_positions_t eItemSlot, int iClassIndex ) { m_eItemSlot = eItemSlot; m_iCurrentClassIndex = iClassIndex; // Init the Slider based on the slot const char * pszConVarName = NULL; switch ( eItemSlot ) { case LOADOUT_POSITION_HEAD : pszConVarName = "tf_hat_effect_offset"; break; case LOADOUT_POSITION_MISC : pszConVarName = "tf_misc1_effect_offset"; break; case LOADOUT_POSITION_MISC2 : pszConVarName = "tf_misc2_effect_offset"; break; default: break; } if ( pszConVarName ) { m_pHatParticleSlider->SetupSlider( -8, 8, pszConVarName, false ); } m_pHatParticleSlider->SetTickColor( Color( 235, 226, 202, 255 ) ); // tanlight m_pHatParticleSlider->SetTickCaptions( "", "" ); UpdateItemOptionsUI(); } //----------------------------------------------------------------------------- void CLoadoutItemOptionsPanel::UpdateItemOptionsUI() { if ( m_eItemSlot == LOADOUT_POSITION_INVALID ) return; m_pListPanel->RemoveAll(); // Add controls for various item options AddControlsParticleEffect(); AddControlsSetStyle(); // Bail if no controls added if ( m_pListPanel->GetItemCount() == 0 ) { // We should have some controls if we get to this point. Assert( 0 ); SetVisible( false ); return; } // Resize the background and list panel to contain all the controls int nVertPixels = m_pListPanel->ComputeVPixelsNeeded(); int nNewTall = Min( 200, nVertPixels ); m_pListPanel->SetTall( nNewTall ); SetTall( nNewTall ); InvalidateLayout( true, false ); } //----------------------------------------------------------------------------- void CLoadoutItemOptionsPanel::AddControlsParticleEffect( void ) const { m_pHatParticleUseHeadButton->SetVisible( false ); m_pHatParticleSlider->SetVisible( false ); CEconItemView *pItem = TFInventoryManager()->GetItemInLoadoutForClass( m_iCurrentClassIndex, m_eItemSlot ); if ( pItem ) { // does this hat even have a particle effect static CSchemaAttributeDefHandle pAttrDef_AttachParticleEffect( "attach particle effect" ); uint32 iValue = 0; if ( pItem->FindAttribute( pAttrDef_AttachParticleEffect, &iValue ) ) { m_pHatParticleUseHeadButton->SetVisible( true ); m_pListPanel->AddItem( NULL, m_pHatParticleUseHeadButton ); m_pListPanel->AddItem( NULL, m_pHatParticleSlider ); // Check for use head toggle static CSchemaAttributeDefHandle pAttrDef_UseHeadOrigin( "particle effect use head origin" ); uint32 iUseHead = 0; if ( pItem->FindAttribute( pAttrDef_UseHeadOrigin, &iUseHead ) && iUseHead > 0 ) { m_pHatParticleSlider->SetVisible( true ); m_pHatParticleUseHeadButton->SetSelected( true ); m_pHatParticleSlider->SetTickColor( Color( 235, 226, 202, 255 ) ); // tanlight m_pHatParticleSlider->Repaint(); // Get offset if it exists static CSchemaAttributeDefHandle pAttrDef_VerticalOffset( "particle effect vertical offset" ); uint32 iOffset = 0; if ( pItem->FindAttribute( pAttrDef_VerticalOffset, &iOffset ) ) { m_pHatParticleSlider->SetSliderValue( (float&)iOffset ); } } else { m_pHatParticleUseHeadButton->SetSelected( false ); } } } } //----------------------------------------------------------------------------- void CLoadoutItemOptionsPanel::AddControlsSetStyle( void ) const { m_pSetStyleButton->SetVisible( false ); CEconItemView *pItem = GetItem(); if ( pItem && pItem->GetStaticData()->GetNumStyles() ) { m_pSetStyleButton->SetVisible( true ); m_pListPanel->AddItem( NULL, m_pSetStyleButton ); } } //----------------------------------------------------------------------------- CEconItemView* CLoadoutItemOptionsPanel::GetItem( void ) const { return TFInventoryManager()->GetItemInLoadoutForClass( m_iCurrentClassIndex, m_eItemSlot ); } CClassLoadoutPanel *g_pClassLoadoutPanel = NULL; //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- CClassLoadoutPanel::CClassLoadoutPanel( vgui::Panel *parent ) : CBaseLoadoutPanel( parent, "class_loadout_panel" ) , m_pItemOptionPanelKVs( NULL ) { m_iCurrentClassIndex = TF_CLASS_UNDEFINED; m_iCurrentTeamIndex = TF_TEAM_RED; m_iCurrentSlotIndex = -1; m_pPlayerModelPanel = NULL; m_pSelectionPanel = NULL; m_pTauntHintLabel = NULL; m_pTauntLabel = NULL; m_pTauntCaratLabel = NULL; m_pPassiveAttribsLabel = NULL; m_pLoadoutPresetPanel = NULL; m_pPresetsExplanationPopup = NULL; m_pTauntsExplanationPopup = NULL; m_pBuildablesButton = NULL; m_pCharacterLoadoutButton = NULL; m_pTauntLoadoutButton = NULL; m_bInTauntLoadoutMode = false; g_pClassLoadoutPanel = this; m_pItemOptionPanel = new CLoadoutItemOptionsPanel( this, "ItemOptionsPanel" ); } CClassLoadoutPanel::~CClassLoadoutPanel() { if ( m_pItemOptionPanelKVs ) { m_pItemOptionPanelKVs->deleteThis(); m_pItemOptionPanelKVs = NULL; } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CClassLoadoutPanel::ApplySchemeSettings( vgui::IScheme *pScheme ) { LoadControlSettings( "Resource/UI/ClassLoadoutPanel.res" ); BaseClass::ApplySchemeSettings( pScheme ); m_pPlayerModelPanel = dynamic_cast( FindChildByName("classmodelpanel") ); m_pTauntHintLabel = dynamic_cast( FindChildByName("TauntHintLabel") ); m_pTauntLabel = dynamic_cast( FindChildByName("TauntLabel") ); m_pTauntCaratLabel = dynamic_cast( FindChildByName("TauntCaratLabel") ); m_pBuildablesButton = dynamic_cast( FindChildByName("BuildablesButton") ); m_pCharacterLoadoutButton = dynamic_cast( FindChildByName("CharacterLoadoutButton") ); m_pTauntLoadoutButton = dynamic_cast( FindChildByName("TauntLoadoutButton") ); m_pPassiveAttribsLabel = dynamic_cast( FindChildByName("PassiveAttribsLabel") ); m_pLoadoutPresetPanel = dynamic_cast( FindChildByName( "loadout_preset_panel" ) ); m_pPresetsExplanationPopup = dynamic_cast( FindChildByName( "PresetsExplanation" ) ); m_pTauntsExplanationPopup = dynamic_cast( FindChildByName( "TauntsExplanation" ) ); m_pTopLinePanel = FindChildByName( "TopLine" ); if ( m_pPassiveAttribsLabel ) { m_pPassiveAttribsLabel->SetMouseInputEnabled( false ); } m_pMouseOverTooltip->SetPositioningStrategy( IPTTP_BOTTOM_SIDE ); m_aDefaultColors[LOADED][FG][DEFAULT] = vgui::scheme()->GetIScheme( GetScheme() )->GetColor( "Econ.Button.PresetDefaultColorFg", Color( 255, 255, 255, 255 ) ); m_aDefaultColors[LOADED][FG][ARMED] = vgui::scheme()->GetIScheme( GetScheme() )->GetColor( "Econ.Button.PresetArmedColorFg", Color( 255, 255, 255, 255 ) ); m_aDefaultColors[LOADED][FG][DEPRESSED] = vgui::scheme()->GetIScheme( GetScheme() )->GetColor( "Econ.Button.PresetDepressedColorFg", Color( 255, 255, 255, 255 ) ); m_aDefaultColors[LOADED][BG][DEFAULT] = vgui::scheme()->GetIScheme( GetScheme() )->GetColor( "Econ.Button.PresetDefaultColorBg", Color( 255, 255, 255, 255 ) ); m_aDefaultColors[LOADED][BG][ARMED] = vgui::scheme()->GetIScheme( GetScheme() )->GetColor( "Econ.Button.PresetArmedColorBg", Color( 255, 255, 255, 255 ) ); m_aDefaultColors[LOADED][BG][DEPRESSED] = vgui::scheme()->GetIScheme( GetScheme() )->GetColor( "Econ.Button.PresetDepressedColorBg", Color( 255, 255, 255, 255 ) ); m_aDefaultColors[NOTLOADED][FG][DEFAULT] = vgui::scheme()->GetIScheme( GetScheme() )->GetColor( "Button.TextColor", Color( 255, 255, 255, 255 ) ); m_aDefaultColors[NOTLOADED][FG][ARMED] = vgui::scheme()->GetIScheme( GetScheme() )->GetColor( "Button.ArmedTextColor", Color( 255, 255, 255, 255 ) ); m_aDefaultColors[NOTLOADED][FG][DEPRESSED] = vgui::scheme()->GetIScheme( GetScheme() )->GetColor( "Button.DepressedTextColor", Color( 255, 255, 255, 255 ) ); m_aDefaultColors[NOTLOADED][BG][DEFAULT] = vgui::scheme()->GetIScheme( GetScheme() )->GetColor( "Button.BgColor", Color( 255, 255, 255, 255 ) ); m_aDefaultColors[NOTLOADED][BG][ARMED] = vgui::scheme()->GetIScheme( GetScheme() )->GetColor( "Button.ArmedBgColor", Color( 255, 255, 255, 255 ) ); m_aDefaultColors[NOTLOADED][BG][DEPRESSED] = vgui::scheme()->GetIScheme( GetScheme() )->GetColor( "Button.DepressedBgColor", Color( 255, 255, 255, 255 ) ); } void CClassLoadoutPanel::ApplySettings( KeyValues *inResourceData ) { BaseClass::ApplySettings( inResourceData ); KeyValues *pItemKV = inResourceData->FindKey( "itemoptionpanels_kv" ); if ( pItemKV ) { if ( m_pItemOptionPanelKVs ) { m_pItemOptionPanelKVs->deleteThis(); } m_pItemOptionPanelKVs = new KeyValues("itemoptionpanels_kv"); pItemKV->CopySubkeys( m_pItemOptionPanelKVs ); } #ifdef STAGING_ONLY // PDA Panels if ( m_pItemModelPanels.Count() > LOADOUT_POSITION_PDA_ADDON2 ) { for ( int i = LOADOUT_POSITION_PDA_ADDON1; i <= LOADOUT_POSITION_PDA_ADDON2; i++ ) { int wide = m_pItemModelPanels[i]->GetWide(); int tall = m_pItemModelPanels[i]->GetTall(); m_pItemModelPanels[i]->SetSize( wide / 2, tall / 2 ); m_pItemModelPanels[i]->InvalidateLayout(); } } #endif } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CClassLoadoutPanel::PerformLayout( void ) { BaseClass::PerformLayout(); // This is disabled by default in res file. IF we turn it on again, uncomment this. /*if ( m_pPassiveAttribsLabel ) { m_pPassiveAttribsLabel->SetVisible( !m_bInTauntLoadoutMode ); }*/ if ( m_pTauntHintLabel ) { m_pTauntHintLabel->SetVisible( m_bInTauntLoadoutMode ); const char *key = engine->Key_LookupBinding( "taunt" ); if ( !key ) { key = "< not bound >"; } SetDialogVariable( "taunt", key ); } if ( m_pTauntLabel ) { m_pTauntLabel->SetVisible( m_bInTauntLoadoutMode ); } if ( m_pTauntCaratLabel ) { m_pTauntCaratLabel->SetVisible( m_bInTauntLoadoutMode ); } if ( m_pCharacterLoadoutButton ) { UpdatePageButtonColor( m_pCharacterLoadoutButton, !m_bInTauntLoadoutMode ); } if ( m_pTauntLoadoutButton ) { UpdatePageButtonColor( m_pTauntLoadoutButton, m_bInTauntLoadoutMode ); } FOR_EACH_VEC( m_vecItemOptionButtons, i ) { m_vecItemOptionButtons[i]->SetVisible( false ); } for ( int i = 0; i < m_pItemModelPanels.Count(); i++ ) { // Viewing a class loadout. Layout the buttons & the class image. if ( i >= NUM_ITEM_PANELS_IN_LOADOUT ) { m_pItemModelPanels[i]->SetVisible( false ); continue; } int iButtonPos = 0; if ( m_iCurrentClassIndex != TF_CLASS_UNDEFINED ) { iButtonPos = g_VisibleLoadoutSlotsPerClass[m_iCurrentClassIndex]->m_iPos[i]; } bool bIsVisible = false; if ( iButtonPos > 0 ) { bIsVisible = m_bInTauntLoadoutMode ? IsTauntPanelPosition( iButtonPos ) : !IsTauntPanelPosition( iButtonPos ); } m_pItemModelPanels[i]->SetVisible( bIsVisible ); if ( bIsVisible ) { if ( m_bInTauntLoadoutMode ) { iButtonPos -= g_VisibleLoadoutSlotsPerClass[m_iCurrentClassIndex]->m_iPos[LOADOUT_POSITION_TAUNT]; } else { iButtonPos--; } #ifdef STAGING_ONLY // Override for the PDA AddOnSlots if ( i == LOADOUT_POSITION_PDA_ADDON1 ) { int iYPos, iXPos; m_pItemModelPanels[ LOADOUT_POSITION_PDA ]->GetPos( iXPos, iYPos ); int iWide, iTall; m_pItemModelPanels[ LOADOUT_POSITION_PDA ]->GetSize( iWide, iTall ); m_pItemModelPanels[i]->SetPos( iXPos + iWide + XRES(1), iYPos ); m_pItemModelPanels[i]->SetSize( iWide / 2.1, iTall / 2.1 ); continue; } else if ( i == LOADOUT_POSITION_PDA_ADDON2 ) { int iYPos, iXPos; m_pItemModelPanels[LOADOUT_POSITION_PDA]->GetPos( iXPos, iYPos ); int iWide, iTall; m_pItemModelPanels[LOADOUT_POSITION_PDA]->GetSize( iWide, iTall ); m_pItemModelPanels[i]->SetPos( iXPos + iWide + XRES(1), iYPos + iTall - (iTall / 2.1 ) ); m_pItemModelPanels[i]->SetSize( iWide / 2.1, iTall / 2.1 ); continue; } #endif m_pItemModelPanels[i]->SetNoItemText( ItemSystem()->GetItemSchema()->GetLoadoutStringsForDisplay( EEquipType_t::EQUIP_TYPE_CLASS )[i] ); int iCenter = GetWide() * 0.5; int iColumnHeight = 4; int iColumn = iButtonPos / iColumnHeight; int iYButtonPos = iButtonPos % iColumnHeight; int iOffset = iColumn == 0 ? m_iItemXPosOffcenterA : m_iItemXPosOffcenterB + ((iColumn - 1) * 200); int iXPos = iCenter + iOffset; int iYPos = m_iItemYPos + (m_iItemYDelta * iYButtonPos); m_pItemModelPanels[i]->SetPos( iXPos, iYPos ); // Update position and visibility of the item option buttons if ( i < m_vecItemOptionButtons.Count() ) { // Place the button just inside the item model panel CExButton* pItemOptionsPanel = m_vecItemOptionButtons[iButtonPos]; int iButtonWide = m_pItemModelPanels[i]->GetWide(); int iMyWide = pItemOptionsPanel->GetWide(); int iOptionsXPos = iColumn == 0 ? iXPos + iButtonWide - iMyWide : iXPos; pItemOptionsPanel->SetPos( iOptionsXPos, iYPos ); CEconItemView *pItemData = TFInventoryManager()->GetItemInLoadoutForClass( m_iCurrentClassIndex, i ); // Enable or disable the item options button for this item model panel pItemOptionsPanel->SetVisible( !m_bInTauntLoadoutMode && AnyOptionsAvailableForItem( pItemData ) ); pItemOptionsPanel->SetCommand( CFmtStr( "options%d", i ) ); } } m_pItemModelPanels[ i ]->SetSelected( false ); } if ( m_pLoadoutPresetPanel ) { m_pLoadoutPresetPanel->SetPos( ( ScreenWidth() - m_pLoadoutPresetPanel->GetWide() ) / 2, m_iItemYPos ); } LinkModelPanelControllerNavigation( true ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CClassLoadoutPanel::OnKeyCodePressed( vgui::KeyCode code ) { // See if the preset control uses this key if( m_pLoadoutPresetPanel->HandlePresetKeyPressed( code ) ) { return; } ButtonCode_t nButtonCode = GetBaseButtonCode( code ); if (nButtonCode == KEY_XBUTTON_LEFT || nButtonCode == KEY_XSTICK1_LEFT || nButtonCode == KEY_XSTICK2_LEFT || nButtonCode == STEAMCONTROLLER_DPAD_LEFT || code == KEY_LEFT || nButtonCode == KEY_XBUTTON_RIGHT || nButtonCode == KEY_XSTICK1_RIGHT || nButtonCode == KEY_XSTICK2_RIGHT || nButtonCode == STEAMCONTROLLER_DPAD_RIGHT || code == KEY_RIGHT || nButtonCode == KEY_XBUTTON_UP || nButtonCode == KEY_XSTICK1_UP || nButtonCode == KEY_XSTICK2_UP || nButtonCode == STEAMCONTROLLER_DPAD_UP || code == KEY_UP || nButtonCode == KEY_XBUTTON_DOWN || nButtonCode == KEY_XSTICK1_DOWN || nButtonCode == KEY_XSTICK2_DOWN || nButtonCode == STEAMCONTROLLER_DPAD_DOWN || code == KEY_DOWN ) { // just eat all navigation keys so we don't // end up with undesirable navigation behavior bubbling from // one item model panel to another } else if( nButtonCode == KEY_XBUTTON_A || code == KEY_ENTER || nButtonCode == STEAMCONTROLLER_A ) { // show the current loadout slot int nSelected = GetFirstSelectedItemIndex( true ); if( nSelected != -1 ) { OnCommand( VarArgs("change%d", nSelected ) ); } } else { BaseClass::OnKeyCodePressed( code ); } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CClassLoadoutPanel::OnNavigateTo( const char* panelName ) { CItemModelPanel *pChild = dynamic_cast( FindChildByName( panelName ) ); if( !pChild ) return; pChild->SetSelected( true ); SetBorderForItem( pChild, false ); pChild->RequestFocus(); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CClassLoadoutPanel::OnNavigateFrom( const char* panelName ) { CItemModelPanel *pChild = dynamic_cast( FindChildByName( panelName ) ); if( !pChild ) return; pChild->SetSelected( false ); SetBorderForItem( pChild, false ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CClassLoadoutPanel::OnShowPanel( bool bVisible, bool bReturningFromArmory ) { if ( bVisible ) { // always start in character loadout page SetLoadoutPage( CHARACTER_LOADOUT_PAGE ); if ( m_pSelectionPanel ) { m_pSelectionPanel->SetVisible( false ); m_pSelectionPanel->MarkForDeletion(); m_pSelectionPanel = NULL; } m_iCurrentSlotIndex = TF_WPN_TYPE_PRIMARY; if( m_pItemModelPanels.Count() && m_pItemModelPanels[0] ) { m_pItemModelPanels[0]->SetSelected( true ); SetBorderForItem( m_pItemModelPanels[0], false ); } m_bLoadoutHasChanged = false; if ( tf_show_preset_explanation_in_class_loadout.GetBool() && m_pPresetsExplanationPopup ) { m_pPresetsExplanationPopup->Popup(); tf_show_preset_explanation_in_class_loadout.SetValue( 0 ); } else if ( tf_show_taunt_explanation_in_class_loadout.GetBool() && m_pTauntsExplanationPopup ) { m_pTauntsExplanationPopup->Popup(); tf_show_taunt_explanation_in_class_loadout.SetValue( 0 ); } ClearItemOptionsMenu(); } else { if ( m_pPlayerModelPanel ) { m_pPlayerModelPanel->ClearCarriedItems(); } } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CClassLoadoutPanel::PostShowPanel( bool bVisible ) { if ( bVisible ) { if ( m_pPlayerModelPanel ) { m_pPlayerModelPanel->SetVisible( true ); } if ( m_pBuildablesButton ) { m_pBuildablesButton->SetVisible( m_iCurrentClassIndex == TF_CLASS_ENGINEER ); } } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CClassLoadoutPanel::SetClass( int iClass ) { m_iCurrentClassIndex = iClass; if ( m_pLoadoutPresetPanel ) { m_pLoadoutPresetPanel->SetClass( m_iCurrentClassIndex ); } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CClassLoadoutPanel::SetTeam( int iTeam ) { Assert( IsValidTFTeam( iTeam ) ); m_iCurrentTeamIndex = iTeam; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- int CClassLoadoutPanel::GetNumRelevantSlots() const { return m_pItemModelPanels.Count(); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- CEconItemView *CClassLoadoutPanel::GetItemInSlot( int iSlot ) { if( iSlot >= 0 && iSlot < m_pItemModelPanels.Count() ) { return m_pItemModelPanels[iSlot]->GetItem(); } return NULL; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CClassLoadoutPanel::FireGameEvent( IGameEvent *event ) { // If we're not visible, ignore all events if ( !IsVisible() ) return; BaseClass::FireGameEvent( event ); // We need to update ourselves after the base has done it, so our item models have been updated const char *type = event->GetName(); if ( Q_strcmp( "inventory_updated", type ) == 0 ) { if ( m_pPlayerModelPanel ) { m_pPlayerModelPanel->HoldItemInSlot( m_iCurrentSlotIndex ); } } } void CClassLoadoutPanel::AddNewItemPanel( int iPanelIndex ) { BaseClass::AddNewItemPanel( iPanelIndex ); m_vecItemOptionButtons[ m_vecItemOptionButtons.AddToTail() ] = new CExButton( this, CFmtStr( "item_options_button%d", iPanelIndex ), "+", this, CFmtStr( "options%d", iPanelIndex ) ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CClassLoadoutPanel::UpdateModelPanels( void ) { // Search for a Robot Costume bool bIsRobot = false; static CSchemaAttributeDefHandle pAttrDef_PlayerRobot( "appear as mvm robot" ); // For now, fill them out with the local player's currently wielded items for ( int i = 0; i < m_pItemModelPanels.Count(); i++ ) { CEconItemView *pItemData = TFInventoryManager()->GetItemInLoadoutForClass( m_iCurrentClassIndex, i ); if ( !pItemData ) continue; if ( FindAttribute( pItemData, pAttrDef_PlayerRobot ) ) { bIsRobot = true; break; } } // We're showing the loadout for a specific class. TFPlayerClassData_t *pData = GetPlayerClassData( m_iCurrentClassIndex ); if ( m_pPlayerModelPanel ) { m_pPlayerModelPanel->ClearCarriedItems(); m_pPlayerModelPanel->SetToPlayerClass( m_iCurrentClassIndex, bIsRobot ); m_pPlayerModelPanel->SetTeam( m_iCurrentTeamIndex ); } // For now, fill them out with the local player's currently wielded items for ( int i = 0; i < m_pItemModelPanels.Count(); i++ ) { CEconItemView *pItemData = TFInventoryManager()->GetItemInLoadoutForClass( m_iCurrentClassIndex, i ); m_pItemModelPanels[i]->SetItem( pItemData ); m_pItemModelPanels[i]->SetShowQuantity( true ); m_pItemModelPanels[i]->SetSelected( false ); SetBorderForItem( m_pItemModelPanels[i], false ); if ( m_pPlayerModelPanel && pItemData && pItemData->IsValid() ) { m_pPlayerModelPanel->AddCarriedItem( pItemData ); } } if ( m_pPlayerModelPanel ) { m_pPlayerModelPanel->HoldItemInSlot( m_iCurrentSlotIndex ); } SetDialogVariable( "loadoutclass", g_pVGuiLocalize->Find( pData->m_szLocalizableName ) ); UpdatePassiveAttributes(); // Now layout again to position our item buttons InvalidateLayout(); if ( m_pItemOptionPanel->IsVisible() ) { m_pItemOptionPanel->UpdateItemOptionsUI(); } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CClassLoadoutPanel::OnItemPanelMouseReleased( vgui::Panel *panel ) { CItemModelPanel *pItemPanel = dynamic_cast < CItemModelPanel * > ( panel ); if ( pItemPanel && IsVisible() ) { for ( int i = 0; i < m_pItemModelPanels.Count(); i++ ) { if ( m_pItemModelPanels[i] == pItemPanel ) { OnCommand( VarArgs("change%d", i) ); return; } } } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CClassLoadoutPanel::OnSelectionReturned( KeyValues *data ) { if ( data ) { uint64 ulIndex = data->GetUint64( "itemindex", INVALID_ITEM_ID ); // ulIndex implies do nothing (escape key) if ( ulIndex != 0 ) { TFInventoryManager()->EquipItemInLoadout( m_iCurrentClassIndex, m_iCurrentSlotIndex, ulIndex ); m_bLoadoutHasChanged = true; UpdateModelPanels(); // Send the preset panel a msg so it can save the change KeyValues *pLoadoutChangedMsg = new KeyValues( "LoadoutChanged" ); pLoadoutChangedMsg->SetInt( "slot", m_iCurrentSlotIndex ); pLoadoutChangedMsg->SetUint64( "itemid", ulIndex ); PostMessage( m_pLoadoutPresetPanel, pLoadoutChangedMsg ); } } PostMessage( GetParent(), new KeyValues("SelectionEnded") ); // It'll have deleted itself, so we don't need to clean it up m_pSelectionPanel = NULL; OnCancelSelection(); // find the selected item and give it the focus CItemModelPanel *pSelection = GetFirstSelectedItemModelPanel( true ); if( !pSelection ) { m_pItemModelPanels[0]->SetSelected( true ); pSelection = m_pItemModelPanels[0]; } pSelection->RequestFocus(); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CClassLoadoutPanel::OnCancelSelection( void ) { if ( m_pSelectionPanel ) { m_pSelectionPanel->SetVisible( false ); m_pSelectionPanel->MarkForDeletion(); m_pSelectionPanel = NULL; } if ( m_pPlayerModelPanel ) { m_pPlayerModelPanel->SetVisible( true ); } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CClassLoadoutPanel::RespawnPlayer() { if ( tf_respawn_on_loadoutchanges.GetBool() ) { // Tell the GC to tell server that we should respawn if we're in a respawn room GCSDK::CGCMsg< MsgGCEmpty_t > msg( k_EMsgGCRespawnPostLoadoutChange ); GCClientSystem()->BSendMessage( msg ); } } //----------------------------------------------------------------------------- // Purpose: Apply KVs to the item option buttons //----------------------------------------------------------------------------- void CClassLoadoutPanel::ApplyKVsToItemPanels( void ) { BaseClass::ApplyKVsToItemPanels(); if ( m_pItemOptionPanelKVs ) { for ( int i = 0; i < m_vecItemOptionButtons.Count(); i++ ) { m_vecItemOptionButtons[i]->ApplySettings( m_pItemOptionPanelKVs ); m_vecItemOptionButtons[i]->InvalidateLayout(); } } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CClassLoadoutPanel::OnClosing( void ) { if ( m_pPlayerModelPanel ) { m_pPlayerModelPanel->ClearCarriedItems(); } if ( m_bLoadoutHasChanged ) { RespawnPlayer(); m_bLoadoutHasChanged = false; } } extern const char *g_szItemBorders[AE_MAX_TYPES][5]; extern ConVar cl_showbackpackrarities; //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CClassLoadoutPanel::SetBorderForItem( CItemModelPanel *pItemPanel, bool bMouseOver ) { if ( !pItemPanel ) return; const char *pszBorder = NULL; if ( pItemPanel->IsGreyedOut() ) { pszBorder = "EconItemBorder"; } else { int iRarity = 0; if ( pItemPanel->HasItem() && cl_showbackpackrarities.GetBool() ) { iRarity = pItemPanel->GetItem()->GetItemQuality(); uint8 nRarity = pItemPanel->GetItem()->GetItemDefinition()->GetRarity(); if ( ( nRarity != k_unItemRarity_Any ) && ( iRarity != AE_SELFMADE ) ) { // translate this quality to rarity iRarity = nRarity + AE_RARITY_DEFAULT; } if ( iRarity > 0 ) { if ( bMouseOver || pItemPanel->IsSelected() ) { pszBorder = g_szItemBorders[iRarity][1]; } else { pszBorder = g_szItemBorders[iRarity][0]; } } } if ( iRarity == 0 ) { if ( bMouseOver || pItemPanel->IsSelected() ) { pszBorder = "LoadoutItemMouseOverBorder"; } else { pszBorder = "EconItemBorder"; } } } vgui::IScheme *pScheme = vgui::scheme()->GetIScheme( GetScheme() ); pItemPanel->SetBorder( pScheme->GetBorder( pszBorder ) ); } //----------------------------------------------------------------------------- // Purpose: Clear the item options menu and reset the button that summoned it //----------------------------------------------------------------------------- void CClassLoadoutPanel::ClearItemOptionsMenu( void ) { SetOptionsButtonText( m_pItemOptionPanel->GetItemSlot(), "+" ); m_pItemOptionPanel->SetItemSlot( LOADOUT_POSITION_INVALID, m_iCurrentClassIndex ); m_pItemOptionPanel->SetVisible( false ); } //----------------------------------------------------------------------------- // Purpose: Safely set the text for a button //----------------------------------------------------------------------------- void CClassLoadoutPanel::SetOptionsButtonText( int nIndex, const char* pszText ) { if ( nIndex >= 0 && nIndex < m_vecItemOptionButtons.Count() ) { m_vecItemOptionButtons[ m_pItemOptionPanel->GetItemSlot() ]->SetText( pszText ); } } //----------------------------------------------------------------------------- // Purpose: Return if the passed in item has any options //----------------------------------------------------------------------------- bool CClassLoadoutPanel::AnyOptionsAvailableForItem( const CEconItemView *pItem ) { if ( !pItem ) return false; // Styles! if ( pItem->GetStaticData()->GetNumSelectableStyles() > 1 ) return true; // Unusual particle effect! For Cosmetics only static CSchemaAttributeDefHandle pAttrDef_AttachParticleEffect( "attach particle effect" ); if ( pItem->FindAttribute( pAttrDef_AttachParticleEffect ) && pItem->GetItemDefinition()->GetLoadoutSlot( 0 ) >= LOADOUT_POSITION_HEAD ) return true; return false; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CClassLoadoutPanel::SetLoadoutPage( classloadoutpage_t loadoutPage ) { ClearItemOptionsMenu(); switch ( loadoutPage ) { case CHARACTER_LOADOUT_PAGE: { m_bInTauntLoadoutMode = false; } break; case TAUNT_LOADOUT_PAGE: { m_bInTauntLoadoutMode = true; } break; default: { // Unhandled loadout page Assert( 0 ); } } InvalidateLayout(); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CClassLoadoutPanel::OnCommand( const char *command ) { if ( FStrEq( command, "characterloadout" ) ) { SetLoadoutPage( CHARACTER_LOADOUT_PAGE ); return; } else if ( FStrEq( command, "tauntloadout" ) ) { SetLoadoutPage( TAUNT_LOADOUT_PAGE ); return; } else if ( !V_strnicmp( command, "change", 6 ) ) { const char *pszNum = command+6; if ( pszNum && pszNum[0] ) { int iSlot = atoi(pszNum); if ( iSlot >= 0 && iSlot < CLASS_LOADOUT_POSITION_COUNT && m_iCurrentClassIndex != TF_CLASS_UNDEFINED ) { if ( m_iCurrentSlotIndex != iSlot ) { m_iCurrentSlotIndex = iSlot; } // Create the selection screen. It removes itself on close. m_pSelectionPanel = new CEquipSlotItemSelectionPanel( this, m_iCurrentClassIndex, iSlot ); m_pSelectionPanel->ShowPanel( 0, true ); if ( m_pPlayerModelPanel ) { m_pPlayerModelPanel->SetVisible( false ); } ClearItemOptionsMenu(); PostMessage( GetParent(), new KeyValues("SelectionStarted") ); } } return; } else if ( !V_strnicmp( command, "options", 7 ) ) { const char *pszNum = command + 7; if( pszNum && pszNum[0] ) { int iSlot = atoi( pszNum ); //iSlot = g_VisibleLoadoutSlotsPerClass[m_iCurrentClassIndex]->m_iPos[iSlot - 1]; if ( iSlot >= 0 && iSlot < m_vecItemOptionButtons.Count() && m_iCurrentClassIndex != TF_CLASS_UNDEFINED ) { // Change the button we're coming from to be a "+" SetOptionsButtonText( m_pItemOptionPanel->GetItemSlot(), "+" ); // Update the current slot index for callback from the setstyle button. // It will send us a message to change the item the player model is holding // and we need this to be updated for that. m_iCurrentSlotIndex = iSlot; // Did they just toggle? if ( m_pItemOptionPanel->GetItemSlot() == iSlot ) { m_pItemOptionPanel->SetVisible( !m_pItemOptionPanel->IsVisible() ); } else { // Set the options panel to have the data for this slot m_pItemOptionPanel->SetItemSlot( (loadout_positions_t)iSlot, m_iCurrentClassIndex ); m_pItemOptionPanel->SetVisible( true ); // Figure out if this is on the left or right int iColumnHeight = 4; int iColumn = iSlot / iColumnHeight; PinCorner_e myCornerToPin = iColumn == 0 ? PIN_TOPLEFT : PIN_TOPRIGHT; PinCorner_e siblingCornerPinTo = iColumn == 0 ? PIN_TOPRIGHT : PIN_TOPLEFT; // Pin to the appropriate side int iButtonPos = g_VisibleLoadoutSlotsPerClass[m_iCurrentClassIndex]->m_iPos[ iSlot ] - 1; m_pItemOptionPanel->PinToSibling( m_vecItemOptionButtons[ iButtonPos ]->GetName(), myCornerToPin, siblingCornerPinTo ); m_pItemOptionPanel->UpdateItemOptionsUI(); } // Change the button we're going to to be "-" if we're visible, "+" if we're not SetOptionsButtonText( m_pItemOptionPanel->GetItemSlot(), m_pItemOptionPanel->IsVisible() ? "-" : "+" ); } return; } } BaseClass::OnCommand( command ); } //----------------------------------------------------------------------------- void CClassLoadoutPanel::OnMessage( const KeyValues* pParams, vgui::VPANEL hFromPanel ) { BaseClass::OnMessage( pParams, hFromPanel ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- struct passive_attrib_to_print_t { const CEconItemAttributeDefinition *m_pAttrDef; attrib_value_t m_value; }; //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- class CAttributeIterator_AddPassiveAttribsToPassiveList : public CEconItemSpecificAttributeIterator { public: CAttributeIterator_AddPassiveAttribsToPassiveList( CUtlVector *pList, bool bForceAdd ) : m_pList( pList ) , m_bForceAdd( bForceAdd ) { Assert( m_pList ); } virtual bool OnIterateAttributeValue( const CEconItemAttributeDefinition *pAttrDef, attrib_value_t value ) { Assert( pAttrDef ); if ( pAttrDef->IsHidden() ) return true; if ( !m_bForceAdd ) { const char *pDesc = pAttrDef->GetArmoryDescString(); if ( !pDesc || !pDesc[0] ) return true; // If we have the "on_wearer" key, we're a passive attribute if ( !Q_stristr(pDesc, "on_wearer") ) return true; } // Now see if we're already in the list FOR_EACH_VEC( (*m_pList), i ) { passive_attrib_to_print_t& passiveAttr = (*m_pList)[i]; Assert( passiveAttr.m_pAttrDef ); // We match if our class is the same -- this is a case-sensitive compare! if ( Q_strcmp( passiveAttr.m_pAttrDef->GetAttributeClass(), pAttrDef->GetAttributeClass() ) ) continue; // We've found a matching attribute. Collate our values and stomp over the earlier value. passiveAttr.m_value = CollateAttributeValues( passiveAttr.m_pAttrDef, passiveAttr.m_value, pAttrDef, value ); return true; } // We didn't find it. Add it to the list. passive_attrib_to_print_t newPassiveAttr = { pAttrDef, value }; m_pList->AddToTail( newPassiveAttr ); return true; } // Other types are ignored. private: CUtlVector *m_pList; bool m_bForceAdd; }; //----------------------------------------------------------------------------- void CClassLoadoutPanel::UpdatePassiveAttributes( void ) { if ( !m_pPassiveAttribsLabel ) return; // We build a list of attributes & associated values by looping through all equipped items. // This way we can identify & collate attributes based on the same definition index. CUtlVector vecAttribsToPrint; // Loop through all equipped items for ( int i = 0; i < m_pItemModelPanels.Count(); i++ ) { CEconItemView *pItemData = TFInventoryManager()->GetItemInLoadoutForClass( m_iCurrentClassIndex, i ); if ( pItemData && pItemData->IsValid() ) { CAttributeIterator_AddPassiveAttribsToPassiveList attrItPassives( &vecAttribsToPrint, false ); pItemData->IterateAttributes( &attrItPassives ); } } // Then add any set bonuses if ( steamapicontext && steamapicontext->SteamUser() ) { CSteamID localSteamID = steamapicontext->SteamUser()->GetSteamID(); CUtlVector pActiveSets; TFInventoryManager()->GetActiveSets( &pActiveSets, localSteamID, m_iCurrentClassIndex ); FOR_EACH_VEC( pActiveSets, set ) { CAttributeIterator_AddPassiveAttribsToPassiveList attrItSetPassives( &vecAttribsToPrint, true ); pActiveSets[set]->IterateAttributes( &attrItSetPassives ); } } // Now build the text wchar_t wszPassiveDesc[4096]; wszPassiveDesc[0] = '\0'; m_pPassiveAttribsLabel->GetTextImage()->ClearColorChangeStream(); wchar_t *pHeader = g_pVGuiLocalize->Find( "#TF_PassiveAttribs" ); if ( pHeader ) { V_wcscpy_safe( wszPassiveDesc, pHeader ); V_wcscat_safe( wszPassiveDesc, L"\n" ); } if ( vecAttribsToPrint.Count() ) { FOR_EACH_VEC( vecAttribsToPrint, i ) { CEconAttributeDescription AttrDesc( GLocalizationProvider(), vecAttribsToPrint[i].m_pAttrDef, vecAttribsToPrint[i].m_value ); AddAttribPassiveText( AttrDesc, wszPassiveDesc, ARRAYSIZE(wszPassiveDesc) ); } } else { vgui::IScheme *pScheme = vgui::scheme()->GetIScheme( GetScheme() ); Color col = pScheme->GetColor( GetColorNameForAttribColor( ATTRIB_COL_NEUTRAL ), Color(255,255,255,255) ); m_pPassiveAttribsLabel->GetTextImage()->AddColorChange( col, Q_wcslen( wszPassiveDesc ) ); wchar_t *pNone = g_pVGuiLocalize->Find( "#TF_PassiveAttribs_None" ); if ( pNone ) { V_wcscat_safe( wszPassiveDesc, pNone ); } } m_pPassiveAttribsLabel->SetText( wszPassiveDesc ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CClassLoadoutPanel::AddAttribPassiveText( const CEconAttributeDescription& AttrDesc, INOUT_Z_CAP(iNumPassiveChars) wchar_t *out_wszPassiveDesc, int iNumPassiveChars ) { vgui::IScheme *pScheme = vgui::scheme()->GetIScheme( GetScheme() ); Assert( pScheme ); if ( !AttrDesc.GetDescription().IsEmpty() ) { // Insert the color change at the current position Color col = pScheme->GetColor( GetColorNameForAttribColor( AttrDesc.GetDefaultColor() ), Color(255,255,255,255) ); m_pPassiveAttribsLabel->GetTextImage()->AddColorChange( col, Q_wcslen( out_wszPassiveDesc ) ); // Now append the text of the attribute V_wcsncat( out_wszPassiveDesc, AttrDesc.GetDescription().Get(), iNumPassiveChars ); V_wcsncat( out_wszPassiveDesc, L"\n", iNumPassiveChars ); } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CClassLoadoutPanel::UpdatePageButtonColor( CExImageButton *pPageButton, bool bIsActive ) { if ( pPageButton ) { int iLoaded = bIsActive ? LOADED : NOTLOADED; pPageButton->SetDefaultColor( m_aDefaultColors[iLoaded][FG][DEFAULT], m_aDefaultColors[iLoaded][BG][DEFAULT] ); pPageButton->SetArmedColor( m_aDefaultColors[iLoaded][FG][ARMED], m_aDefaultColors[iLoaded][BG][ARMED] ); pPageButton->SetDepressedColor( m_aDefaultColors[iLoaded][FG][DEPRESSED], m_aDefaultColors[iLoaded][BG][DEPRESSED] ); } }