From 410f0e056c8668dd047ac7309152e1643184681f Mon Sep 17 00:00:00 2001 From: rabscootle Date: Fri, 24 Jul 2026 19:10:06 -0500 Subject: [PATCH 1/4] [TF2] Show active taunts in inspect panels --- src/game/client/tf/c_tf_player.cpp | 14 ++++++++++++++ src/game/client/tf/tf_hud_freezepanel.cpp | 12 +++++++++++- 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/src/game/client/tf/c_tf_player.cpp b/src/game/client/tf/c_tf_player.cpp index 941823fd1ec..e21ed8ea4da 100644 --- a/src/game/client/tf/c_tf_player.cpp +++ b/src/game/client/tf/c_tf_player.cpp @@ -10258,6 +10258,20 @@ CEconItemView *C_TFPlayer::GetInspectItem( int *pLastItem ) { int iItemsFound = 0; CEconItemView *pFirstItem = NULL; + CEconItemView *pTauntItem = GetTauntEconItemView(); + if ( m_Shared.InCond( TF_COND_TAUNTING ) && pTauntItem && + ( !pTauntItem->GetItemDefinition() || !pTauntItem->GetItemDefinition()->IsHidden() ) ) + { + pFirstItem = pTauntItem; + + iItemsFound++; + if ( iItemsFound > *pLastItem ) + { + *pLastItem = iItemsFound; + return pTauntItem; + } + } + int nCount = WeaponCount(); for ( int i = 0; i < nCount; ++i ) { diff --git a/src/game/client/tf/tf_hud_freezepanel.cpp b/src/game/client/tf/tf_hud_freezepanel.cpp index cc4f40379ba..c0868e87380 100644 --- a/src/game/client/tf/tf_hud_freezepanel.cpp +++ b/src/game/client/tf/tf_hud_freezepanel.cpp @@ -443,12 +443,22 @@ void CTFFreezePanel::FireGameEvent( IGameEvent * event ) } else { + CEconItemView *pTauntItem = pTFPlayerKiller && pTFPlayerKiller->m_Shared.InCond( TF_COND_TAUNTING ) + ? pTFPlayerKiller->GetTauntEconItemView() : NULL; + bool bShowTaunt = pTauntItem && ( !pTauntItem->GetItemDefinition() || !pTauntItem->GetItemDefinition()->IsHidden() ); + if ( bShowTaunt ) + { + m_pItemPanel->SetDialogVariable( "killername", g_PR->GetPlayerName( m_iKillerIndex ) ); + m_pItemPanel->SetItem( pTauntItem ); + m_pItemPanel->SetVisible( true ); + } + // If our killer is using an item, display its stats. CTFWeaponBase *pWeapon = pTFPlayerKiller ? pTFPlayerKiller->GetActiveTFWeapon() : NULL; bool bShowItem = false; if ( pWeapon ) { - bShowItem = pWeapon->GetAttributeContainer()->GetItem()->GetItemQuality() != AE_NORMAL; + bShowItem = !bShowTaunt && pWeapon->GetAttributeContainer()->GetItem()->GetItemQuality() != AE_NORMAL; if ( bShowItem ) { CTFStatPanel *pStatPanel = GET_HUDELEMENT( CTFStatPanel ); From 57ccebefb13feca7e2dec1a9bc72fcddc8e05954 Mon Sep 17 00:00:00 2001 From: rabscootle Date: Fri, 24 Jul 2026 20:33:17 -0500 Subject: [PATCH 2/4] [TF2] Refresh freeze panel for late taunts --- src/game/client/tf/c_tf_player.cpp | 18 +++++++++++ src/game/client/tf/tf_hud_freezepanel.cpp | 38 +++++++++++++++++++++++ src/game/client/tf/tf_hud_freezepanel.h | 2 ++ 3 files changed, 58 insertions(+) diff --git a/src/game/client/tf/c_tf_player.cpp b/src/game/client/tf/c_tf_player.cpp index e21ed8ea4da..90d4679a1d3 100644 --- a/src/game/client/tf/c_tf_player.cpp +++ b/src/game/client/tf/c_tf_player.cpp @@ -4898,6 +4898,7 @@ void C_TFPlayer::OnDataChanged( DataUpdateType_t updateType ) //----------------------------------------------------------------------------- void C_TFPlayer::UpdateTauntItem() { + bool bUsedInventoryItem = false; if ( m_nActiveTauntSlot == LOADOUT_POSITION_INVALID ) { if ( m_iTauntItemDefIndex != INVALID_ITEM_DEF_INDEX ) @@ -4917,7 +4918,24 @@ void C_TFPlayer::UpdateTauntItem() if ( pMiscItemView ) { m_TauntEconItemView = *pMiscItemView; + bUsedInventoryItem = true; } + else if ( m_iTauntItemDefIndex != INVALID_ITEM_DEF_INDEX ) + { + m_TauntEconItemView.Init( m_iTauntItemDefIndex, AE_UNIQUE, 1 ); + } + else + { + m_TauntEconItemView.Invalidate(); + } + } + + ConVarRef cl_taunt_inspect_debug( "cl_taunt_inspect_debug" ); + if ( cl_taunt_inspect_debug.IsValid() && cl_taunt_inspect_debug.GetBool() ) + { + Msg( "[taunt inspect] player=%d slot=%d defindex=%d valid=%d source=%s\n", + entindex(), m_nActiveTauntSlot, m_iTauntItemDefIndex, m_TauntEconItemView.IsValid(), + bUsedInventoryItem ? "inventory" : "network" ); } if ( m_TauntEconItemView.IsValid() ) diff --git a/src/game/client/tf/tf_hud_freezepanel.cpp b/src/game/client/tf/tf_hud_freezepanel.cpp index c0868e87380..87e3e4d3caf 100644 --- a/src/game/client/tf/tf_hud_freezepanel.cpp +++ b/src/game/client/tf/tf_hud_freezepanel.cpp @@ -77,6 +77,8 @@ DECLARE_BUILD_FACTORY( CTFFreezePanelHealth ); CTFFreezePanel *CTFFreezePanel::s_pFreezePanel = NULL; +static ConVar cl_taunt_inspect_debug( "cl_taunt_inspect_debug", "0", FCVAR_CHEAT, "Print active taunt inspect diagnostics." ); + //----------------------------------------------------------------------------- CTFFreezePanel *CTFFreezePanel::Instance() @@ -99,6 +101,8 @@ CTFFreezePanel::CTFFreezePanel( const char *pElementName ) SetScheme( "ClientScheme" ); m_iKillerIndex = 0; + m_bShowingTaunt = false; + m_bKillerWasTaunting = false; m_iShowNemesisPanel = SHOW_NO_NEMESIS; m_iYBase = -1; m_flShowCalloutsAt = 0; @@ -294,6 +298,8 @@ void CTFFreezePanel::FireGameEvent( IGameEvent * event ) { // Get the entity who killed us m_iKillerIndex = event->GetInt( "killer" ); + m_bShowingTaunt = false; + m_bKillerWasTaunting = false; C_BaseEntity *pKiller = ClientEntityList().GetBaseEntity( m_iKillerIndex ); CTFPlayer *pTFPlayerKiller = NULL; if ( pKiller ) @@ -451,6 +457,16 @@ void CTFFreezePanel::FireGameEvent( IGameEvent * event ) m_pItemPanel->SetDialogVariable( "killername", g_PR->GetPlayerName( m_iKillerIndex ) ); m_pItemPanel->SetItem( pTauntItem ); m_pItemPanel->SetVisible( true ); + m_bShowingTaunt = true; + } + + m_bKillerWasTaunting = pTFPlayerKiller && pTFPlayerKiller->m_Shared.InCond( TF_COND_TAUNTING ); + if ( cl_taunt_inspect_debug.GetBool() ) + { + Msg( "[taunt inspect] death killer=%d taunting=%d slot=%d item=%d shown=%d\n", + m_iKillerIndex, m_bKillerWasTaunting, + pTFPlayerKiller ? pTFPlayerKiller->GetActiveTauntSlot() : LOADOUT_POSITION_INVALID, + pTauntItem ? pTauntItem->GetItemDefIndex() : INVALID_ITEM_DEF_INDEX, bShowTaunt ); } // If our killer is using an item, display its stats. @@ -927,6 +943,28 @@ void CTFFreezePanel::OnThink( void ) { BaseClass::OnThink(); + C_TFPlayer *pKiller = ToTFPlayer( UTIL_PlayerByIndex( m_iKillerIndex ) ); + bool bKillerIsTaunting = pKiller && pKiller->m_Shared.InCond( TF_COND_TAUNTING ); + if ( IsVisible() && !m_bShowingTaunt && bKillerIsTaunting ) + { + CEconItemView *pTauntItem = pKiller->GetTauntEconItemView(); + if ( cl_taunt_inspect_debug.GetBool() && !m_bKillerWasTaunting ) + { + Msg( "[taunt inspect] late taunt killer=%d slot=%d item=%d valid=%d\n", + m_iKillerIndex, pKiller->GetActiveTauntSlot(), + pTauntItem ? pTauntItem->GetItemDefIndex() : INVALID_ITEM_DEF_INDEX, pTauntItem != NULL ); + } + + if ( pTauntItem && ( !pTauntItem->GetItemDefinition() || !pTauntItem->GetItemDefinition()->IsHidden() ) ) + { + m_pItemPanel->SetDialogVariable( "killername", g_PR->GetPlayerName( m_iKillerIndex ) ); + m_pItemPanel->SetItem( pTauntItem ); + m_pItemPanel->SetVisible( true ); + m_bShowingTaunt = true; + } + } + m_bKillerWasTaunting = bKillerIsTaunting; + if ( m_pItemPanel && m_pItemPanel->IsVisible() ) { CTFStatPanel *pStatPanel = GET_HUDELEMENT( CTFStatPanel ); diff --git a/src/game/client/tf/tf_hud_freezepanel.h b/src/game/client/tf/tf_hud_freezepanel.h index 5219e0191f9..b72147306fb 100644 --- a/src/game/client/tf/tf_hud_freezepanel.h +++ b/src/game/client/tf/tf_hud_freezepanel.h @@ -139,6 +139,8 @@ class CTFFreezePanel : public EditablePanel, public CHudElement int m_iKillerOriginalY; bool m_bHoldingAfterScreenshot; + bool m_bShowingTaunt; + bool m_bKillerWasTaunting; enum { From cba240913788f1be4fb5c5437122eb0ce785c5c2 Mon Sep 17 00:00:00 2001 From: rabscootle Date: Fri, 24 Jul 2026 20:50:26 -0500 Subject: [PATCH 3/4] [TF2] Preserve inspect item ordering --- src/game/client/tf/c_tf_player.cpp | 39 ++++++++++++++++-------------- 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/src/game/client/tf/c_tf_player.cpp b/src/game/client/tf/c_tf_player.cpp index 90d4679a1d3..b943d94433b 100644 --- a/src/game/client/tf/c_tf_player.cpp +++ b/src/game/client/tf/c_tf_player.cpp @@ -4452,11 +4452,6 @@ void C_TFPlayer::OnDataChanged( DataUpdateType_t updateType ) m_nOldMaxHealth = GetMaxHealth(); } - if ( m_nPrevTauntSlot != m_nActiveTauntSlot || m_iPrevTauntItemDefIndex != m_iTauntItemDefIndex ) - { - UpdateTauntItem(); - } - if ( m_iOldKartHealth != m_iKartHealth ) { UpdateKartEffects(); @@ -4468,6 +4463,11 @@ void C_TFPlayer::OnDataChanged( DataUpdateType_t updateType ) } } + if ( updateType == DATA_UPDATE_CREATED || m_nPrevTauntSlot != m_nActiveTauntSlot || m_iPrevTauntItemDefIndex != m_iTauntItemDefIndex ) + { + UpdateTauntItem(); + } + GetAttributeManager()->OnDataChanged( updateType ); // Check for full health and remove decals. @@ -10277,19 +10277,6 @@ CEconItemView *C_TFPlayer::GetInspectItem( int *pLastItem ) int iItemsFound = 0; CEconItemView *pFirstItem = NULL; CEconItemView *pTauntItem = GetTauntEconItemView(); - if ( m_Shared.InCond( TF_COND_TAUNTING ) && pTauntItem && - ( !pTauntItem->GetItemDefinition() || !pTauntItem->GetItemDefinition()->IsHidden() ) ) - { - pFirstItem = pTauntItem; - - iItemsFound++; - if ( iItemsFound > *pLastItem ) - { - *pLastItem = iItemsFound; - return pTauntItem; - } - } - int nCount = WeaponCount(); for ( int i = 0; i < nCount; ++i ) { @@ -10345,6 +10332,22 @@ CEconItemView *C_TFPlayer::GetInspectItem( int *pLastItem ) } } + if ( m_Shared.InCond( TF_COND_TAUNTING ) && pTauntItem && + ( !pTauntItem->GetItemDefinition() || !pTauntItem->GetItemDefinition()->IsHidden() ) ) + { + if ( !pFirstItem ) + { + pFirstItem = pTauntItem; + } + + iItemsFound++; + if ( iItemsFound > *pLastItem ) + { + *pLastItem = iItemsFound; + return pTauntItem; + } + } + // If we didn't find an item, go back to the first one if ( pFirstItem ) { From 5b9a102a75b83da69d69f4b310cf83a51ef2c6aa Mon Sep 17 00:00:00 2001 From: rabscootle Date: Fri, 24 Jul 2026 21:58:11 -0500 Subject: [PATCH 4/4] [TF2] Prefer taunts when freezecam starts --- src/game/client/tf/c_tf_player.cpp | 2 +- src/game/client/tf/tf_hud_freezepanel.cpp | 31 +++++++++++++++++------ src/game/client/tf/tf_hud_freezepanel.h | 1 + 3 files changed, 25 insertions(+), 9 deletions(-) diff --git a/src/game/client/tf/c_tf_player.cpp b/src/game/client/tf/c_tf_player.cpp index b943d94433b..fb5d2512bed 100644 --- a/src/game/client/tf/c_tf_player.cpp +++ b/src/game/client/tf/c_tf_player.cpp @@ -4914,7 +4914,7 @@ void C_TFPlayer::UpdateTauntItem() { int iClass = GetPlayerClass()->GetClassIndex(); - CEconItemView *pMiscItemView = Inventory() ? Inventory()->GetCacheServerItemInLoadout( iClass, m_nActiveTauntSlot ) : NULL; + CEconItemView *pMiscItemView = Inventory() ? Inventory()->GetItemInLoadout( iClass, m_nActiveTauntSlot ) : NULL; if ( pMiscItemView ) { m_TauntEconItemView = *pMiscItemView; diff --git a/src/game/client/tf/tf_hud_freezepanel.cpp b/src/game/client/tf/tf_hud_freezepanel.cpp index 87e3e4d3caf..8497034d364 100644 --- a/src/game/client/tf/tf_hud_freezepanel.cpp +++ b/src/game/client/tf/tf_hud_freezepanel.cpp @@ -256,6 +256,7 @@ void CTFFreezePanel::FireGameEvent( IGameEvent * event ) } else if ( Q_strcmp( "freezecam_started", pEventName ) == 0 ) { + ShowActiveTaunt(); ShowCalloutsIn( 1.0 ); ShowSnapshotPanelIn( 1.25 ); @@ -947,21 +948,15 @@ void CTFFreezePanel::OnThink( void ) bool bKillerIsTaunting = pKiller && pKiller->m_Shared.InCond( TF_COND_TAUNTING ); if ( IsVisible() && !m_bShowingTaunt && bKillerIsTaunting ) { - CEconItemView *pTauntItem = pKiller->GetTauntEconItemView(); if ( cl_taunt_inspect_debug.GetBool() && !m_bKillerWasTaunting ) { + CEconItemView *pTauntItem = pKiller->GetTauntEconItemView(); Msg( "[taunt inspect] late taunt killer=%d slot=%d item=%d valid=%d\n", m_iKillerIndex, pKiller->GetActiveTauntSlot(), pTauntItem ? pTauntItem->GetItemDefIndex() : INVALID_ITEM_DEF_INDEX, pTauntItem != NULL ); } - if ( pTauntItem && ( !pTauntItem->GetItemDefinition() || !pTauntItem->GetItemDefinition()->IsHidden() ) ) - { - m_pItemPanel->SetDialogVariable( "killername", g_PR->GetPlayerName( m_iKillerIndex ) ); - m_pItemPanel->SetItem( pTauntItem ); - m_pItemPanel->SetVisible( true ); - m_bShowingTaunt = true; - } + ShowActiveTaunt(); } m_bKillerWasTaunting = bKillerIsTaunting; @@ -1010,6 +1005,26 @@ void CTFFreezePanel::OnThink( void ) } } +//----------------------------------------------------------------------------- +// Purpose: Show the killer's active taunt in preference to their weapon. +//----------------------------------------------------------------------------- +bool CTFFreezePanel::ShowActiveTaunt() +{ + C_TFPlayer *pKiller = ToTFPlayer( UTIL_PlayerByIndex( m_iKillerIndex ) ); + if ( !pKiller || !pKiller->m_Shared.InCond( TF_COND_TAUNTING ) ) + return false; + + CEconItemView *pTauntItem = pKiller->GetTauntEconItemView(); + if ( !pTauntItem || ( pTauntItem->GetItemDefinition() && pTauntItem->GetItemDefinition()->IsHidden() ) ) + return false; + + m_pItemPanel->SetDialogVariable( "killername", g_PR->GetPlayerName( m_iKillerIndex ) ); + m_pItemPanel->SetItem( pTauntItem ); + m_pItemPanel->SetVisible( true ); + m_bShowingTaunt = true; + return true; +} + //----------------------------------------------------------------------------- // Purpose: diff --git a/src/game/client/tf/tf_hud_freezepanel.h b/src/game/client/tf/tf_hud_freezepanel.h index b72147306fb..2bb86601952 100644 --- a/src/game/client/tf/tf_hud_freezepanel.h +++ b/src/game/client/tf/tf_hud_freezepanel.h @@ -106,6 +106,7 @@ class CTFFreezePanel : public EditablePanel, public CHudElement void DeleteCalloutPanels(); void ShowNemesisPanel( bool bShow ); + bool ShowActiveTaunt(); const char *GetFilesafePlayerName( const char *pszOldName ); CPanelAnimationVar( bool, m_bShouldScreenshotMovePanelToCorner, "screenshot_move_panel_to_corner", "1" );