//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: // //=============================================================================// #include "cbase.h" #include "charinfo_loadout_subpanel.h" #include "vgui/ISurface.h" #include "vgui/IInput.h" #include "vgui/ILocalize.h" #include "c_tf_freeaccount.h" #include "c_tf_player.h" #include "confirm_dialog.h" #include "gamestringpool.h" #include "c_tf_objective_resource.h" #include "tf_gamerules.h" #include "tf_item_inventory.h" #include "trading_start_dialog.h" #include "gc_clientsystem.h" // memdbgon must be the last include file in a .cpp file!!! #include DECLARE_BUILD_FACTORY( CImageButton ); ConVar tf_explanations_charinfopanel( "tf_explanations_charinfopanel", "0", FCVAR_ARCHIVE, "Whether the user has seen explanations for this panel." ); //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- CImageButton::CImageButton( vgui::Panel *parent, const char *panelName ) : BaseClass( parent, panelName, "" ) { m_pszActiveImageName = NULL; m_pszInactiveImageName = NULL; m_pActiveImage = NULL; m_pInactiveImage = NULL; m_ActiveDrawColor = Color(255,255,255,255); m_InactiveDrawColor = Color(255,255,255,255); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CImageButton::ApplySettings( KeyValues *inResourceData ) { m_bScaleImage = inResourceData->GetInt( "scaleImage", 0 ); // Active Image delete [] m_pszActiveImageName; m_pszActiveImageName = NULL; const char *activeImageName = inResourceData->GetString( "activeimage", "" ); if ( *activeImageName ) { SetActiveImage( activeImageName ); } // Inactive Image delete [] m_pszInactiveImageName; m_pszInactiveImageName = NULL; const char *inactiveImageName = inResourceData->GetString( "inactiveimage", "" ); if ( *inactiveImageName ) { SetInactiveImage( inactiveImageName ); } const char *pszDrawColor = inResourceData->GetString("activedrawcolor", ""); if (*pszDrawColor) { int r = 0, g = 0, b = 0, a = 255; if (sscanf(pszDrawColor, "%d %d %d %d", &r, &g, &b, &a) >= 3) { m_ActiveDrawColor = Color(r, g, b, a); } else { vgui::IScheme *pScheme = vgui::scheme()->GetIScheme( GetScheme() ); m_ActiveDrawColor = pScheme->GetColor(pszDrawColor, Color(0, 0, 0, 0)); } } pszDrawColor = inResourceData->GetString("inactivedrawcolor", ""); if (*pszDrawColor) { int r = 0, g = 0, b = 0, a = 255; if (sscanf(pszDrawColor, "%d %d %d %d", &r, &g, &b, &a) >= 3) { m_InactiveDrawColor = Color(r, g, b, a); } else { vgui::IScheme *pScheme = vgui::scheme()->GetIScheme( GetScheme() ); m_InactiveDrawColor = pScheme->GetColor(pszDrawColor, Color(0, 0, 0, 0)); } } BaseClass::ApplySettings( inResourceData ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CImageButton::ApplySchemeSettings( vgui::IScheme *pScheme ) { BaseClass::ApplySchemeSettings( pScheme ); if ( m_pszActiveImageName && strlen( m_pszActiveImageName ) > 0 ) { SetActiveImage(vgui::scheme()->GetImage( m_pszActiveImageName, m_bScaleImage ) ); } if ( m_pszInactiveImageName && strlen( m_pszInactiveImageName ) > 0 ) { SetInactiveImage(vgui::scheme()->GetImage( m_pszInactiveImageName, m_bScaleImage ) ); } vgui::IBorder *pBorder = pScheme->GetBorder( "NoBorder" ); SetDefaultBorder( pBorder); SetDepressedBorder( pBorder ); SetKeyFocusBorder( pBorder ); Color defaultFgColor = GetSchemeColor( "Button.TextColor", Color(255, 255, 255, 255), pScheme ); Color armedFgColor = GetSchemeColor( "Button.ArmedTextColor", Color(255, 255, 255, 255), pScheme ); Color depressedFgColor = GetSchemeColor( "Button.DepressedTextColor", Color(255, 255, 255, 255), pScheme ); Color blank(0,0,0,0); SetDefaultColor( defaultFgColor, blank ); SetArmedColor( armedFgColor, blank ); SetDepressedColor( depressedFgColor, blank ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CImageButton::SetActiveImage( const char *imagename ) { int len = Q_strlen( imagename ) + 1; m_pszActiveImageName = new char[ len ]; Q_strncpy( m_pszActiveImageName, imagename, len ); SetActiveImage(vgui::scheme()->GetImage( m_pszActiveImageName, m_bScaleImage ) ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CImageButton::SetInactiveImage( const char *imagename ) { int len = Q_strlen( imagename ) + 1; m_pszInactiveImageName = new char[ len ]; Q_strncpy( m_pszInactiveImageName, imagename, len ); SetInactiveImage(vgui::scheme()->GetImage( m_pszInactiveImageName, m_bScaleImage ) ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CImageButton::SetActiveImage( vgui::IImage *image ) { m_pActiveImage = image; if ( m_pActiveImage ) { int wide, tall; if ( m_bScaleImage ) { // scaling, force the image size to be our size GetSize( wide, tall ); m_pActiveImage->SetSize( wide, tall ); } else { // not scaling, so set our size to the image size m_pActiveImage->GetSize( wide, tall ); SetSize( wide, tall ); } } Repaint(); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CImageButton::SetInactiveImage( vgui::IImage *image ) { m_pInactiveImage = image; if ( m_pInactiveImage ) { int wide, tall; if ( m_bScaleImage) { // scaling, force the image size to be our size GetSize( wide, tall ); m_pInactiveImage->SetSize( wide, tall ); } else { // not scaling, so set our size to the image size m_pInactiveImage->GetSize( wide, tall ); SetSize( wide, tall ); } } Repaint(); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CImageButton::OnSizeChanged( int newWide, int newTall ) { if ( m_bScaleImage ) { // scaling, force the image size to be our size if ( m_pActiveImage ) m_pActiveImage->SetSize( newWide, newTall ); if ( m_pInactiveImage ) m_pInactiveImage->SetSize( newWide, newTall ); } BaseClass::OnSizeChanged( newWide, newTall ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CImageButton::Paint() { if ( IsArmed() || _buttonFlags.IsFlagSet( FORCE_DEPRESSED ) ) { // draw the active image if ( m_pActiveImage ) { m_pActiveImage->SetColor( m_ActiveDrawColor ); m_pActiveImage->SetPos( 0, 0 ); m_pActiveImage->Paint(); } } else { // draw the inactive image if ( m_pInactiveImage ) { m_pActiveImage->SetColor( m_InactiveDrawColor ); m_pInactiveImage->SetPos( 0, 0 ); m_pInactiveImage->Paint(); } } BaseClass::Paint(); } const char *g_pszSubButtonNames[CHSB_NUM_BUTTONS] = { "ShowBackpackButton", // CHSB_BACKPACK, "ShowCraftingButton", // CHSB_CRAFTING, "ShowArmoryButton", // CHSB_ARMORY, "ShowTradeButton", // CHSB_TRADING, }; const char *g_pszSubButtonLabelNames[CHSB_NUM_BUTTONS] = { "ShowBackpackLabel", // CHSB_BACKPACK, "ShowCraftingLabel", // CHSB_CRAFTING, "ShowArmoryLabel", // CHSB_ARMORY, "ShowTradeLabel", // CHSB_TRADING, }; int g_nLoadoutClassOrder[] = { TF_CLASS_SCOUT, TF_CLASS_SOLDIER, TF_CLASS_PYRO, TF_CLASS_DEMOMAN, TF_CLASS_HEAVYWEAPONS, TF_CLASS_ENGINEER, TF_CLASS_MEDIC, TF_CLASS_SNIPER, TF_CLASS_SPY }; //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- CCharInfoLoadoutSubPanel::CCharInfoLoadoutSubPanel(Panel *parent) : vgui::PropertyPage(parent, "CharInfoLoadoutSubPanel") { m_iCurrentClassIndex = TF_CLASS_UNDEFINED; m_iCurrentTeamIndex = TF_TEAM_RED; m_iShowingPanel = CHAP_LOADOUT; m_iPrevShowingPanel = CHAP_LOADOUT; memset( m_pClassButtons, 0, sizeof( m_pClassButtons ) ); m_pClassButtons[ TF_CLASS_SCOUT ] = new CImageButton( this, "scout" ); m_pClassButtons[ TF_CLASS_SOLDIER ] = new CImageButton( this, "soldier" ); m_pClassButtons[ TF_CLASS_PYRO ] = new CImageButton( this, "pyro" ); m_pClassButtons[ TF_CLASS_DEMOMAN ] = new CImageButton( this, "demoman" ); m_pClassButtons[ TF_CLASS_HEAVYWEAPONS ] = new CImageButton( this, "heavyweapons" ); m_pClassButtons[ TF_CLASS_ENGINEER ] = new CImageButton( this, "engineer" ); m_pClassButtons[ TF_CLASS_MEDIC ] = new CImageButton( this, "medic" ); m_pClassButtons[ TF_CLASS_SNIPER ] = new CImageButton( this, "sniper" ); m_pClassButtons[ TF_CLASS_SPY ] = new CImageButton( this, "spy" ); for( int i = 0; i < Q_ARRAYSIZE( m_pClassButtons ); i++ ) { if( m_pClassButtons[ i ] ) m_pClassButtons[ i ]->SetParentNeedsCursorMoveEvents( true ); } for ( int i = 0; i < CHSB_NUM_BUTTONS; i++ ) { m_pSubButtons[i] = new CImageButton( this, g_pszSubButtonNames[i] ); m_pButtonLabels[i] = new CExLabel( this, g_pszSubButtonLabelNames[i], "" ); } m_iOverSubButton = -1; m_pClassLoadoutPanel = new CClassLoadoutPanel( this ); m_pBackpackPanel = new CBackpackPanel( this, "backpack_panel" ); m_pCraftingPanel = new CCraftingPanel( this, "crafting_panel" ); m_pArmoryPanel = new CArmoryPanel( this, "armory_panel" ); m_pArmoryPanel->AllowGotoStore(); m_pSelectLabel = NULL; m_pLoadoutChangesLabel = NULL; m_pNoSteamLabel = NULL; m_pNoGCLabel = NULL; m_pClassLabel = NULL; m_pItemsLabel = NULL; m_bSnapClassLayout = false; m_bClassLayoutDirty = false; m_bRequestingInventoryRefresh = false; m_flStartExplanationsAt = 0; vgui::ivgui()->AddTickSignal( GetVPanel() ); REGISTER_COLOR_AS_OVERRIDABLE( m_ItemColorNone, "itemcountcolor_noitems" ); REGISTER_COLOR_AS_OVERRIDABLE( m_ItemColor, "itemcountcolor" ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- CCharInfoLoadoutSubPanel::~CCharInfoLoadoutSubPanel() { } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CCharInfoLoadoutSubPanel::ApplySchemeSettings( vgui::IScheme *pScheme ) { BaseClass::ApplySchemeSettings( pScheme ); LoadControlSettings( "Resource/UI/CharInfoLoadoutSubPanel.res" ); m_pSelectLabel = dynamic_cast( FindChildByName("SelectLabel") ); m_pLoadoutChangesLabel = dynamic_cast( FindChildByName("LoadoutChangesLabel") ); m_pNoSteamLabel = dynamic_cast( FindChildByName("NoSteamLabel") ); m_pNoGCLabel = dynamic_cast( FindChildByName("NoGCLabel") ); m_pClassLabel = dynamic_cast( FindChildByName("ClassLabel") ); int ignored; if ( m_pClassLabel ) { m_pClassLabel->GetPos( ignored, m_iClassLabelYPos ); } m_pItemsLabel = dynamic_cast( FindChildByName("ItemsLabel") ); if ( m_pItemsLabel ) { m_pItemsLabel->GetPos( ignored, m_iItemLabelYPos ); } // Start classes sized as if the mouse is in the middle of the screen m_iMouseXPos = -1; m_bSnapClassLayout = true; RecalculateTargetClassLayout(); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CCharInfoLoadoutSubPanel::OnPageShow( void ) { SetVisible( true ); BaseClass::OnPageShow(); if( m_iCurrentClassIndex != TF_CLASS_UNDEFINED ) { m_pClassButtons[ m_iCurrentClassIndex ]->GetPos( m_iMouseXPos, m_iMouseYPos ); m_bClassLayoutDirty = true; InvalidateLayout(); } // If this is the first time we've opened the loadout, start the loadout explanations if ( !tf_explanations_charinfopanel.GetBool() && ShouldShowExplanations() ) { m_flStartExplanationsAt = engine->Time() + 0.5; } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CCharInfoLoadoutSubPanel::OnSelectionStarted( void ) { PostActionSignal( new KeyValues("SelectionUpdate", "open", 1 ) ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CCharInfoLoadoutSubPanel::OnSelectionEnded( void ) { PostActionSignal( new KeyValues("SelectionUpdate", "open", 0 ) ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CCharInfoLoadoutSubPanel::OnCancelSelection( void ) { PostMessage( m_pClassLoadoutPanel, new KeyValues("CancelSelection") ); PostMessage( m_pBackpackPanel, new KeyValues("CancelSelection") ); PostMessage( m_pCraftingPanel, new KeyValues("CancelSelection") ); PostMessage( m_pArmoryPanel, new KeyValues("CancelSelection") ); RequestFocus(); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CCharInfoLoadoutSubPanel::OnCharInfoClosing( void ) { switch ( m_iShowingPanel ) { case CHAP_CRAFTING: PostMessage( m_pCraftingPanel, new KeyValues("Closing") ); break; case CHAP_BACKPACK: break; case CHAP_ARMORY: PostMessage( m_pArmoryPanel, new KeyValues("Closing") ); break; case CHAP_LOADOUT: PostMessage( m_pClassLoadoutPanel, new KeyValues("Closing") ); break; default: // Class loadout. break; } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CCharInfoLoadoutSubPanel::OnOpenCrafting( void ) { OpenToCrafting(); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CCharInfoLoadoutSubPanel::OnCraftingClosed( void ) { PostMessage( m_pCraftingPanel, new KeyValues("Closing") ); m_iShowingPanel = CHAP_LOADOUT; m_iPrevShowingPanel = CHAP_CRAFTING; m_flStartExplanationsAt = 0; m_iCurrentClassIndex = TF_CLASS_UNDEFINED; UpdateModelPanels(); RequestFocus(); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CCharInfoLoadoutSubPanel::OnArmoryClosed( void ) { // Return to whatever we were on before opening the armory PostMessage( m_pArmoryPanel, new KeyValues("Closing") ); m_iShowingPanel = m_iPrevShowingPanel; m_iPrevShowingPanel = CHAP_ARMORY; m_flStartExplanationsAt = 0; m_iCurrentClassIndex = TF_CLASS_UNDEFINED; UpdateModelPanels(); RequestFocus(); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CCharInfoLoadoutSubPanel::OnCommand( const char *command ) { if ( !Q_strnicmp( command, "loadout ", 8 ) ) { // Ignore selection while we don't have a steam connection if ( !TFInventoryManager()->GetLocalTFInventory()->RetrievedInventoryFromSteam() ) return; m_flStartExplanationsAt = 0; const char *pszClass = command+8; if ( pszClass[0] != '\0' ) { int nClassIndex = GetClassIndexFromString( pszClass, NUM_CLASSES_IN_LOADOUT_PANEL ); if ( nClassIndex != TF_CLASS_UNDEFINED && m_iCurrentClassIndex != nClassIndex ) { SetClassIndex( nClassIndex, true ); return; } } } else if ( !Q_strnicmp( command, "backpack", 8 ) ) { OpenToBackpack(); } else if ( !Q_strnicmp( command, "crafting", 8 ) ) { OpenToCrafting(); } else if ( !Q_strnicmp( command, "armory", 6 ) ) { OpenToArmory(); } else if ( !Q_strnicmp( command, "trading", 7 ) ) { OpenTradingStartDialog( this ); } else if ( !Q_stricmp( command, "show_explanations" ) ) { if ( !m_flStartExplanationsAt ) { m_flStartExplanationsAt = engine->Time(); } RequestFocus(); } else { engine->ClientCmd( const_cast( command ) ); } BaseClass::OnCommand( command ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CCharInfoLoadoutSubPanel::RequestInventoryRefresh() { m_bRequestingInventoryRefresh = false; // Don't respond to the mouse if we don't have items if ( !TFInventoryManager()->GetLocalTFInventory()->RetrievedInventoryFromSteam() ) { ShowWaitingDialog( new CGenericWaitingDialog(this), "#NoSteamNoItems_Refresh", true, true, 30.0f ); if ( !m_bRequestingInventoryRefresh ) { // make sure the local inventory is added as a listener TFInventoryManager()->UpdateLocalInventory(); m_bRequestingInventoryRefresh = true; // ask GC for refresh GCSDK::CProtoBufMsg< CMsgRequestInventoryRefresh > msg( k_EMsgGCRequestInventoryRefresh ); GCClientSystem()->BSendMessage( msg ); } } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CCharInfoLoadoutSubPanel::SetClassIndex( int iClassIndex, bool bOpenClassLoadout ) { Assert(iClassIndex >= TF_CLASS_UNDEFINED && iClassIndex <= NUM_CLASSES_IN_LOADOUT_PANEL); m_iCurrentClassIndex = iClassIndex; m_iShowingPanel = CHAP_LOADOUT; UpdateModelPanels( bOpenClassLoadout ); RequestInventoryRefresh(); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CCharInfoLoadoutSubPanel::SetTeamIndex( int iTeam ) { Assert( IsValidTFTeam( iTeam ) ); m_iCurrentTeamIndex = iTeam; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CCharInfoLoadoutSubPanel::OpenSubPanel( charinfo_activepanels_t iPanel ) { m_flStartExplanationsAt = 0; m_iCurrentClassIndex = TF_CLASS_UNDEFINED; m_iPrevShowingPanel = m_iShowingPanel; m_iShowingPanel = iPanel; UpdateModelPanels(); RequestInventoryRefresh(); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CCharInfoLoadoutSubPanel::UpdateModelPanels( bool bOpenClassLoadout ) { int iLabelClassToSet = -1; int iClassIndexToSet = 0; if ( m_iShowingPanel == CHAP_CRAFTING ) { m_pClassLoadoutPanel->SetVisible( false ); m_pBackpackPanel->SetVisible( false ); m_pArmoryPanel->SetVisible( false ); m_pCraftingPanel->ShowPanel( m_iCurrentClassIndex, true, (m_iPrevShowingPanel == CHAP_ARMORY) ); } else if ( m_iShowingPanel == CHAP_BACKPACK ) { m_pClassLoadoutPanel->SetVisible( false ); m_pCraftingPanel->SetVisible( false ); m_pArmoryPanel->SetVisible( false ); m_pBackpackPanel->ShowPanel( m_iCurrentClassIndex, true, (m_iPrevShowingPanel == CHAP_ARMORY) ); } else if ( m_iShowingPanel == CHAP_ARMORY ) { m_pClassLoadoutPanel->SetVisible( false ); m_pCraftingPanel->SetVisible( false ); m_pBackpackPanel->SetVisible( false ); m_pArmoryPanel->ShowPanel( m_iArmoryItemDef ); } else { iClassIndexToSet = bOpenClassLoadout ? m_iCurrentClassIndex : TF_CLASS_UNDEFINED; m_pArmoryPanel->SetVisible( false ); m_pBackpackPanel->SetVisible( false ); m_pCraftingPanel->SetVisible( false ); m_pClassLoadoutPanel->SetTeam( m_iCurrentTeamIndex ); m_pClassLoadoutPanel->SetClass( iClassIndexToSet ); m_pClassLoadoutPanel->ShowPanel( iClassIndexToSet, false, (m_iPrevShowingPanel == CHAP_ARMORY) ); iLabelClassToSet = m_iCurrentClassIndex; } m_iCurrentClassIndex = iClassIndexToSet; if( bOpenClassLoadout ) { PostActionSignal( new KeyValues("ClassSelected", "class", m_iCurrentClassIndex ) ); } else { m_iLabelSetToClass = iLabelClassToSet; m_bClassLayoutDirty = true; InvalidateLayout(); } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CCharInfoLoadoutSubPanel::PerformLayout( void ) { BaseClass::PerformLayout(); // Show our changes label if we're alive, and hence won't get the changes immediately bool bChangesLabel = false; if ( engine->IsInGame() ) { C_TFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer(); if ( pLocalPlayer && pLocalPlayer->IsAlive() && pLocalPlayer->GetObserverMode() == OBS_MODE_NONE ) { bChangesLabel = true; } } if ( !TFInventoryManager()->GetLocalTFInventory()->RetrievedInventoryFromSteam() ) { bool bLoggedIntoSteam = steamapicontext && steamapicontext->SteamUser() && steamapicontext->SteamUser()->BLoggedOn(); if ( m_pItemsLabel ) m_pNoGCLabel->SetVisible( bLoggedIntoSteam ); if ( m_pNoSteamLabel ) m_pNoSteamLabel->SetVisible( !bLoggedIntoSteam ); if ( m_pSelectLabel ) m_pSelectLabel->SetVisible( false ); if ( m_pLoadoutChangesLabel) m_pLoadoutChangesLabel->SetVisible( false ); for ( int i = 0; i < CHSB_NUM_BUTTONS; i++ ) { m_pSubButtons[i]->SetVisible( false ); m_pButtonLabels[i]->SetVisible( false ); } } else { if ( m_pNoSteamLabel ) m_pNoSteamLabel->SetVisible( false ); if ( m_pNoGCLabel ) m_pNoGCLabel->SetVisible( false ); if ( m_pSelectLabel ) m_pSelectLabel->SetVisible( true ); for ( int i = 0; i < CHSB_NUM_BUTTONS; i++ ) { m_pSubButtons[i]->SetVisible( true ); m_pButtonLabels[i]->SetVisible( true ); } if ( !bChangesLabel ) { if ( m_pSelectLabel ) m_pSelectLabel->SetPos( 0, m_iSelectLabelY ); if ( m_pLoadoutChangesLabel ) m_pLoadoutChangesLabel->SetVisible( false ); } else { if ( m_pSelectLabel ) m_pSelectLabel->SetPos( 0, m_iSelectLabelOnChangesY ); if ( m_pLoadoutChangesLabel ) m_pLoadoutChangesLabel->SetVisible( true ); } } m_iOverSubButton = -1; if ( m_pSelectLabel ) m_pClassLabel->SetVisible( false ); if ( m_pItemsLabel ) m_pItemsLabel->SetVisible( false ); m_bClassLayoutDirty = false; // Now Layout the class images. for ( int iPanel = 0; iPanel < ARRAYSIZE( g_nLoadoutClassOrder ); iPanel++ ) { int i = g_nLoadoutClassOrder[iPanel]; int iX = m_iClassLayout[i][0]; int iY = m_iClassLayout[i][1]; int iWide = m_iClassLayout[i][2]; int iTall = m_iClassLayout[i][3]; if ( m_bSnapClassLayout ) { m_pClassButtons[i]->SetBounds( iX, iY, iWide, iTall ); } else { // Lerp towards the target int iCurX, iCurY, iCurWide, iCurTall; m_pClassButtons[i]->GetBounds( iCurX, iCurY, iCurWide, iCurTall ); int iNewX = Lerp( 0.2, iCurX, iX ); int iNewY = Lerp( 0.2, iCurY, iY ); int iNewWide = Lerp( 0.2, iCurWide, iWide ); int iNewTall = Lerp( 0.2, iCurTall, iTall ); m_pClassButtons[i]->SetBounds( iNewX, iNewY, iNewWide, iNewTall ); if ( abs(iNewX-iX) > 5 || abs(iNewY-iY) > 5 || abs(iNewWide-iWide) > 5 || abs(iNewTall-iTall) > 5 ) { m_bClassLayoutDirty = true; } } } // We need to do our own management of cursor arming in the buttons, because the curserentered/exited code can't // deal with the way we resize the buttons without the cursor moving. int iBestButton = -1; int iBestZ = 0; int x = m_iMouseXPos, y = m_iMouseYPos; // only get the actual cursor pos if we don't have a cached cursor pos. THe // cached pos might have come from the keyboard. if( x < 0 ) vgui::input()->GetCursorPos(x, y); for ( int iPanel = 0; iPanel < ARRAYSIZE( g_nLoadoutClassOrder ); iPanel++ ) { int i = g_nLoadoutClassOrder[iPanel]; m_pClassButtons[i]->SetArmed( false ); m_pClassButtons[i]->SetEnabled( TFInventoryManager()->GetLocalTFInventory()->RetrievedInventoryFromSteam() ); if ( m_pClassButtons[i]->IsWithin( x,y ) && iBestZ < m_pClassButtons[i]->GetZPos() ) { iBestButton = i; iBestZ = m_pClassButtons[i]->GetZPos(); } } if ( iBestButton >= 0 && iBestButton < ARRAYSIZE( m_pClassButtons ) ) { m_pClassButtons[iBestButton]->SetArmed( true ); if ( m_iLabelSetToClass != iBestButton ) { m_iLabelSetToClass = iBestButton; } UpdateLabelFromClass( m_iLabelSetToClass ); } m_bSnapClassLayout = false; } void CCharInfoLoadoutSubPanel::UpdateLabelFromClass( int nClass ) { if ( nClass < 0 ) return; const wchar_t *wszClassName = g_pVGuiLocalize->Find( g_aPlayerClassNames[nClass] ); if ( m_pClassLabel ) { m_pClassLabel->SetText( wszClassName ); m_pClassLabel->SetVisible( true ); } if ( m_pItemsLabel ) { m_pItemsLabel->SetVisible( true ); } CUtlVector pList; int iNumItems = TFInventoryManager()->GetAllUsableItemsForSlot( nClass, -1, &pList ); if ( !iNumItems ) { const wchar_t *wszItemsName = g_pVGuiLocalize->Find( "#NoItemsFoundShort" ); m_pItemsLabel->SetText( wszItemsName ); m_pItemsLabel->SetColorStr( m_ItemColorNone ); } else if ( iNumItems == 1 ) { const wchar_t *wszItemsName = g_pVGuiLocalize->Find( "#ItemsFoundShortOne" ); m_pItemsLabel->SetText( wszItemsName ); m_pItemsLabel->SetColorStr( m_ItemColor ); } else { wchar_t wzCount[10]; _snwprintf( wzCount, ARRAYSIZE( wzCount ), L"%d", iNumItems ); wchar_t wTemp[32]; g_pVGuiLocalize->ConstructString_safe( wTemp, g_pVGuiLocalize->Find("ItemsFoundShort"), 1, wzCount ); m_pItemsLabel->SetText( wTemp ); m_pItemsLabel->SetColorStr( m_ItemColor ); } int iPos = 0; for ( int i = TF_FIRST_NORMAL_CLASS; i <= NUM_CLASSES_IN_LOADOUT_PANEL; i++ ) { if ( iRemapIndexToClass[i] == nClass ) { iPos = i; break; } } Assert(iPos != 0 ); int iXLeft = (GetWide() - ((m_iClassWideMin * NUM_CLASSES_IN_LOADOUT_PANEL) + (m_iClassXDelta * (NUM_CLASSES_IN_LOADOUT_PANEL-1)))) * 0.5; int iBaseX = iXLeft + ((m_iClassWideMin + m_iClassXDelta) * (iPos-1)); int iCenterX = iBaseX + (m_iClassWideMin * 0.5); m_pClassLabel->SetVisible( true ); m_pClassLabel->SetPos( iCenterX - (m_pClassLabel->GetWide() * 0.5), m_iClassLabelYPos ); m_pItemsLabel->SetVisible( true ); m_pItemsLabel->SetPos( iCenterX - (m_pItemsLabel->GetWide() * 0.5), m_iItemLabelYPos ); } void CCharInfoLoadoutSubPanel::UpdateLabelFromSubButton( int nButton ) { if( nButton < 0 ) nButton = CHSB_NUM_BUTTONS - 1; else if( nButton >= CHSB_NUM_BUTTONS ) nButton = 0; if ( m_iOverSubButton == nButton ) return; m_iOverSubButton = nButton; switch ( nButton ) { default: case CHSB_BACKPACK: { int iNumItems = TFInventoryManager()->GetLocalTFInventory()->GetItemCount(); if ( iNumItems == 1 ) { const wchar_t *wszItemsName = g_pVGuiLocalize->Find( "#Loadout_OpenBackpackDesc1" ); m_pItemsLabel->SetText( wszItemsName ); } else { wchar_t wzCount[10]; _snwprintf( wzCount, ARRAYSIZE( wzCount ), L"%d", iNumItems ); wchar_t wTemp[32]; g_pVGuiLocalize->ConstructString_safe( wTemp, g_pVGuiLocalize->Find("Loadout_OpenBackpackDesc"), 1, wzCount ); m_pItemsLabel->SetText( wTemp ); } } break; case CHSB_CRAFTING: m_pItemsLabel->SetText( g_pVGuiLocalize->Find( "Loadout_OpenCraftingDesc" ) ); break; case CHSB_ARMORY: m_pItemsLabel->SetText( g_pVGuiLocalize->Find( "Loadout_OpenArmoryDesc" ) ); break; case CHSB_TRADING: m_pItemsLabel->SetText( g_pVGuiLocalize->Find( "Loadout_OpenTradingDesc" ) ); break; } int iX, iY; m_pSubButtons[nButton]->GetPos( iX, iY ); iX += (m_pSubButtons[nButton]->GetWide() * 0.5); iY += m_pSubButtons[nButton]->GetTall() + YRES(5); m_pItemsLabel->SetVisible( true ); m_pItemsLabel->SetPos( iX - (m_pItemsLabel->GetWide() * 0.5), iY + (m_iItemLabelYPos - m_iClassLabelYPos) ); m_pItemsLabel->SetColorStr( m_ItemColor ); for ( int i = 0; i < CHSB_NUM_BUTTONS; i++ ) { m_pSubButtons[i]->SetArmed( false ); } m_pSubButtons[nButton]->SetArmed( true ); m_pSubButtons[nButton]->RequestFocus(); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CCharInfoLoadoutSubPanel::OnTick( void ) { if ( m_iCurrentClassIndex != TF_CLASS_UNDEFINED ) return; if ( !IsVisible() ) return; if ( m_bRequestingInventoryRefresh && TFInventoryManager()->GetLocalTFInventory()->RetrievedInventoryFromSteam() ) { m_bRequestingInventoryRefresh = false; CloseWaitingDialog(); return; } // if the class layout is dirty, invalidate our layout so that // we'll animate the class buttons. if ( m_bClassLayoutDirty ) { InvalidateLayout(); } if ( !HasFocus() ) return; // Don't respond to the mouse if we don't have items if ( !TFInventoryManager()->GetLocalTFInventory()->RetrievedInventoryFromSteam() ) return; if ( m_flStartExplanationsAt && m_flStartExplanationsAt < engine->Time() ) { m_flStartExplanationsAt = 0; if ( ShouldShowExplanations() ) { tf_explanations_charinfopanel.SetValue( 1 ); CExplanationPopup *pPopup = dynamic_cast( FindChildByName("StartExplanation") ); if ( pPopup ) { pPopup->Popup(); } } } } //----------------------------------------------------------------------------- // Purpose: Handles mousing over classes //----------------------------------------------------------------------------- void CCharInfoLoadoutSubPanel::OnCursorMoved( int x, int y ) { RecalculateTargetClassLayoutAtPos( x, y ); } //----------------------------------------------------------------------------- // Purpose: Handles setting the highlighted class for both mouse and keyboard //----------------------------------------------------------------------------- void CCharInfoLoadoutSubPanel::RecalculateTargetClassLayoutAtPos( int x, int y ) { // Ignore mouse movement outside the buttons bool bWithin = false; for ( int i = TF_FIRST_NORMAL_CLASS; i <= NUM_CLASSES_IN_LOADOUT_PANEL; i++ ) { if ( m_pClassButtons[i]->IsWithin(x,y) ) { bWithin = true; break; } } if ( bWithin ) { m_iMouseXPos = x; m_iMouseYPos = y; RecalculateTargetClassLayout(); m_bClassLayoutDirty = true; } else { // See if we're over a sub button bool bOverSubButton = false; for ( int i = 0; i < CHSB_NUM_BUTTONS; i++ ) { if ( m_pSubButtons[i]->IsWithin(x,y) ) { bOverSubButton = true; UpdateLabelFromSubButton( i ); } } if ( !bOverSubButton && m_pClassLabel->IsVisible() ) { // Hide the class label if ( m_iMouseXPos != -1 ) { m_iMouseXPos = -1; RecalculateTargetClassLayout(); m_bClassLayoutDirty = true; } m_iOverSubButton = -1; m_iLabelSetToClass = -1; m_pClassLabel->SetVisible( false ); m_pItemsLabel->SetVisible( false ); } } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CCharInfoLoadoutSubPanel::RecalculateTargetClassLayout( void ) { // Now Layout the class images. for ( int i = TF_FIRST_NORMAL_CLASS; i <= NUM_CLASSES_IN_LOADOUT_PANEL; i++ ) { int iIndex = GetRemappedMenuIndexForClass(i); // Figure out where we'd be unscaled int iXLeft = (GetWide() - ((m_iClassWideMin * NUM_CLASSES_IN_LOADOUT_PANEL) + (m_iClassXDelta * (NUM_CLASSES_IN_LOADOUT_PANEL-1)))) * 0.5; int iBaseX = iXLeft + ((m_iClassWideMin + m_iClassXDelta) * (iIndex-1)); // Scale based on distance from the mouse cursor. int iCenterX = iBaseX + (m_iClassWideMin * 0.5); float flScale = 0.0; if ( m_iMouseXPos >= 0 ) { flScale = RemapValClamped( abs(m_iMouseXPos - iCenterX), m_iClassDistanceMin, m_iClassDistanceMax, 1.0, 0.0 ); } float iWide = RemapValClamped( flScale, 0.0, 1.0, m_iClassWideMin, m_iClassWideMax ); float iTall = RemapValClamped( flScale, 0.0, 1.0, m_iClassTallMin, m_iClassTallMax ); int iY = m_iClassYPos - ((iTall - m_iClassTallMin) * 0.5); int iX = iBaseX - ((iWide - m_iClassWideMin) * 0.5); m_pClassButtons[i]->SetZPos( flScale * 100 ); // Cache off the target bounds for this class button m_iClassLayout[i][0] = iX; m_iClassLayout[i][1] = iY; m_iClassLayout[i][2] = iWide; m_iClassLayout[i][3] = iTall; } } void CCharInfoLoadoutSubPanel::MoveCharacterSelection( int nDirection ) { int nCurrent = 0; if ( m_iLabelSetToClass != -1 ) { for ( int i = 0; i < ARRAYSIZE( g_nLoadoutClassOrder ); i++ ) { if ( m_iLabelSetToClass == g_nLoadoutClassOrder[ i ] ) { nCurrent = i; break; } } nCurrent += nDirection; if ( nCurrent < 0 ) { nCurrent = ARRAYSIZE( g_nLoadoutClassOrder ) - 1; } else if ( nCurrent >= ARRAYSIZE( g_nLoadoutClassOrder ) ) { nCurrent = 0; } } for ( int i = 0; i < ARRAYSIZE( g_nLoadoutClassOrder ); i++ ) { m_pClassButtons[ g_nLoadoutClassOrder[ i ] ]->SetArmed( false ); } // animate the class buttons CImageButton *pButton = m_pClassButtons[ g_nLoadoutClassOrder[ nCurrent ] ]; int x, y, wide, tall; pButton->GetBounds( x, y, wide, tall ); RecalculateTargetClassLayoutAtPos( x + wide/2, y + tall/2 ); pButton->RequestFocus(); } void CCharInfoLoadoutSubPanel::OnKeyCodeTyped(vgui::KeyCode code) { // turn off key handling in this panel when we're showing a loadout // for one class if ( m_iCurrentClassIndex != TF_CLASS_UNDEFINED ) { // let escape and B (aka "go back") through so we // can actually get out of the loadout screen if ( code == KEY_ESCAPE ) { BaseClass::OnKeyCodePressed( code ); } return; } BaseClass::OnKeyCodeTyped( code ); } void CCharInfoLoadoutSubPanel::OnKeyCodePressed(vgui::KeyCode code) { ButtonCode_t nButtonCode = GetBaseButtonCode( code ); // turn off key handling in this panel when we're showing a loadout // for one class if( m_iCurrentClassIndex != TF_CLASS_UNDEFINED ) { // let escape and B (aka "go back") through so we // can actually get out of the loadout screen if ( nButtonCode == KEY_XBUTTON_B ) { BaseClass::OnKeyCodePressed( code ); } return; } if ( nButtonCode == KEY_XBUTTON_LEFT || nButtonCode == KEY_XSTICK1_LEFT || nButtonCode == KEY_XSTICK2_LEFT || nButtonCode == STEAMCONTROLLER_DPAD_LEFT || code == KEY_LEFT ) { if ( m_iLabelSetToClass != -1 ) { MoveCharacterSelection( -1 ); } else { UpdateLabelFromSubButton( m_iOverSubButton - 1 ); } return; } else if ( nButtonCode == KEY_XBUTTON_RIGHT || nButtonCode == KEY_XSTICK1_RIGHT || nButtonCode == KEY_XSTICK2_RIGHT || nButtonCode == STEAMCONTROLLER_DPAD_RIGHT || code == KEY_RIGHT ) { if ( m_iLabelSetToClass != -1 ) { MoveCharacterSelection( 1 ); } else { UpdateLabelFromSubButton( m_iOverSubButton + 1 ); } return; } else if ( nButtonCode == KEY_XBUTTON_UP || nButtonCode == KEY_XSTICK1_UP || nButtonCode == KEY_XSTICK2_UP || nButtonCode == STEAMCONTROLLER_DPAD_UP || code == KEY_UP ) { if ( m_iLabelSetToClass == -1 ) { m_iLabelSetToClass = g_nLoadoutClassOrder[ 0 ]; CImageButton *pButton = m_pClassButtons[ m_iLabelSetToClass ]; UpdateLabelFromClass( m_iLabelSetToClass ); int x, y, wide, tall; pButton->GetBounds( x, y, wide, tall ); RecalculateTargetClassLayoutAtPos( x + wide/2, y + tall/2 ); pButton->RequestFocus(); } return; } else if ( nButtonCode == KEY_XBUTTON_DOWN || nButtonCode == KEY_XSTICK1_DOWN || nButtonCode == KEY_XSTICK2_DOWN || nButtonCode == STEAMCONTROLLER_DPAD_DOWN || code == KEY_DOWN ) { if ( m_iLabelSetToClass != -1 ) { m_iLabelSetToClass = -1; m_pClassLabel->SetVisible( false ); m_pItemsLabel->SetVisible( false ); for ( int iPanel = 0; iPanel < ARRAYSIZE( g_nLoadoutClassOrder ); iPanel++ ) { int i = g_nLoadoutClassOrder[iPanel]; m_pClassButtons[i]->SetArmed( false ); } UpdateLabelFromSubButton( 0 ); } return; } BaseClass::OnKeyCodePressed( code ); }