diff options
| author | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
|---|---|---|
| committer | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
| commit | 3bf9df6b2785fa6d951086978a3e66f49427166a (patch) | |
| tree | 2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /game/client/tf/vgui/class_loadout_panel.cpp | |
| download | archived-source-engine-2018-hl2-src-master.tar.xz archived-source-engine-2018-hl2-src-master.zip | |
Diffstat (limited to 'game/client/tf/vgui/class_loadout_panel.cpp')
| -rw-r--r-- | game/client/tf/vgui/class_loadout_panel.cpp | 1539 |
1 files changed, 1539 insertions, 0 deletions
diff --git a/game/client/tf/vgui/class_loadout_panel.cpp b/game/client/tf/vgui/class_loadout_panel.cpp new file mode 100644 index 0000000..5db5fba --- /dev/null +++ b/game/client/tf/vgui/class_loadout_panel.cpp @@ -0,0 +1,1539 @@ +//========= 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 <tier0/memdbgon.h> + +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<CMsgSetItemEffectVerticalOffset> 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<CMsgSetHatEffectUseHeadOrigin> 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<CTFPlayerModelPanel*>( FindChildByName("classmodelpanel") ); + m_pTauntHintLabel = dynamic_cast<vgui::Label*>( FindChildByName("TauntHintLabel") ); + m_pTauntLabel = dynamic_cast<CExLabel*>( FindChildByName("TauntLabel") ); + m_pTauntCaratLabel = dynamic_cast<CExLabel*>( FindChildByName("TauntCaratLabel") ); + m_pBuildablesButton = dynamic_cast<CExButton*>( FindChildByName("BuildablesButton") ); + m_pCharacterLoadoutButton = dynamic_cast<CExImageButton*>( FindChildByName("CharacterLoadoutButton") ); + m_pTauntLoadoutButton = dynamic_cast<CExImageButton*>( FindChildByName("TauntLoadoutButton") ); + m_pPassiveAttribsLabel = dynamic_cast<CExLabel*>( FindChildByName("PassiveAttribsLabel") ); + m_pLoadoutPresetPanel = dynamic_cast<CLoadoutPresetPanel*>( FindChildByName( "loadout_preset_panel" ) ); + m_pPresetsExplanationPopup = dynamic_cast<CExplanationPopup*>( FindChildByName( "PresetsExplanation" ) ); + m_pTauntsExplanationPopup = dynamic_cast<CExplanationPopup*>( 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<CItemModelPanel *>( FindChildByName( panelName ) ); + if( !pChild ) + return; + + pChild->SetSelected( true ); + SetBorderForItem( pChild, false ); + pChild->RequestFocus(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CClassLoadoutPanel::OnNavigateFrom( const char* panelName ) +{ + CItemModelPanel *pChild = dynamic_cast<CItemModelPanel *>( 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<passive_attrib_to_print_t> *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<passive_attrib_to_print_t> *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<passive_attrib_to_print_t> 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<const CEconItemSetDefinition *> 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] ); + } +} |