From 502d42a51ebb02a78efc9fb872dab2b42d34de21 Mon Sep 17 00:00:00 2001 From: Thomas Kain Date: Mon, 24 Nov 2025 12:17:16 -0500 Subject: [PATCH 1/4] Implement unused kill icon for burning arrow headshots An attack in TF2 can only have one special damage type at a time. Headshots and burning arrows are two different special damage types, so burning arrow headshots cannot be fully tracked with the current set of special damage types. This commit adds another special damage type for burning arrow headshots and configures it to use the (currently unused) kill icon for such an event. (Unrelatedly, this also replaces two symbols in a comment that Rider marks as UTF-8 errors. These would get changed whenever I edited this file so I may as well fix them now.) --- src/game/client/tf/tf_hud_deathnotice.cpp | 5 +++++ src/game/server/tf/tf_eventlog.cpp | 1 + src/game/server/tf/tf_projectile_arrow.cpp | 2 +- src/game/server/vscript_server.cpp | 1 + src/game/shared/tf/tf_player_shared.cpp | 1 + src/game/shared/tf/tf_shareddefs.cpp | 3 ++- src/game/shared/tf/tf_shareddefs.h | 3 ++- 7 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/game/client/tf/tf_hud_deathnotice.cpp b/src/game/client/tf/tf_hud_deathnotice.cpp index 9f94ed957a0..ff64604712d 100644 --- a/src/game/client/tf/tf_hud_deathnotice.cpp +++ b/src/game/client/tf/tf_hud_deathnotice.cpp @@ -1161,6 +1161,11 @@ void CTFHudDeathNotice::OnGameEvent( IGameEvent *event, int iDeathNoticeMsg ) msg.wzInfoText[ 0 ] = 0; break; } + case TF_DMG_CUSTOM_HEADSHOT_FLYINGBURN: + // special-case if the player is headshot by a burning arrow as the killing blow + Q_strncpy( msg.szIcon, "d_huntsman_flyingburn_headshot", ARRAYSIZE( msg.szIcon ) ); + msg.wzInfoText[0] = 0; + break; default: break; } diff --git a/src/game/server/tf/tf_eventlog.cpp b/src/game/server/tf/tf_eventlog.cpp index 04e7a858127..f31340cc8a9 100644 --- a/src/game/server/tf/tf_eventlog.cpp +++ b/src/game/server/tf/tf_eventlog.cpp @@ -105,6 +105,7 @@ class CTFEventLog : public CEventLog switch( iCustomDamage ) { + case TF_DMG_CUSTOM_HEADSHOT_FLYINGBURN: case TF_DMG_CUSTOM_HEADSHOT_DECAPITATION: case TF_DMG_CUSTOM_HEADSHOT: pszCustom = "headshot"; diff --git a/src/game/server/tf/tf_projectile_arrow.cpp b/src/game/server/tf/tf_projectile_arrow.cpp index b6c14eb8b11..e38f2c2fb66 100644 --- a/src/game/server/tf/tf_projectile_arrow.cpp +++ b/src/game/server/tf/tf_projectile_arrow.cpp @@ -492,7 +492,7 @@ bool CTFProjectile_Arrow::StrikeTarget( mstudiobbox_t *pBox, CBaseEntity *pOther if ( bHeadshot ) { nDamageType |= DMG_CRITICAL; - nDamageCustom = TF_DMG_CUSTOM_HEADSHOT; + nDamageCustom = m_bArrowAlight ? TF_DMG_CUSTOM_HEADSHOT_FLYINGBURN : TF_DMG_CUSTOM_HEADSHOT; } if ( m_bCritical ) diff --git a/src/game/server/vscript_server.cpp b/src/game/server/vscript_server.cpp index c1b2eae8c13..9cdd98a4938 100644 --- a/src/game/server/vscript_server.cpp +++ b/src/game/server/vscript_server.cpp @@ -3371,6 +3371,7 @@ DECLARE_SCRIPT_CONST( ETFDmgCustom, TF_DMG_CUSTOM_CROC ) DECLARE_SCRIPT_CONST( ETFDmgCustom, TF_DMG_CUSTOM_TAUNTATK_GASBLAST ) DECLARE_SCRIPT_CONST( ETFDmgCustom, TF_DMG_CUSTOM_AXTINGUISHER_BOOSTED ) DECLARE_SCRIPT_CONST( ETFDmgCustom, TF_DMG_CUSTOM_TAUNTATK_TRICKSHOT ) +DECLARE_SCRIPT_CONST( ETFDmgCustom, TF_DMG_CUSTOM_HEADSHOT_FLYINGBURN ) DECLARE_SCRIPT_CONST( ETFDmgCustom, TF_DMG_CUSTOM_END ) REGISTER_SCRIPT_CONST_TABLE( ETFDmgCustom ) diff --git a/src/game/shared/tf/tf_player_shared.cpp b/src/game/shared/tf/tf_player_shared.cpp index 65297449744..d1764290262 100644 --- a/src/game/shared/tf/tf_player_shared.cpp +++ b/src/game/shared/tf/tf_player_shared.cpp @@ -13344,6 +13344,7 @@ int CTFPlayerShared::GetSequenceForDeath( CBaseAnimating* pRagdoll, bool bBurnin switch ( nCustomDeath ) { + case TF_DMG_CUSTOM_HEADSHOT_FLYINGBURN: case TF_DMG_CUSTOM_HEADSHOT_DECAPITATION: case TF_DMG_CUSTOM_TAUNTATK_BARBARIAN_SWING: case TF_DMG_CUSTOM_DECAPITATION: diff --git a/src/game/shared/tf/tf_shareddefs.cpp b/src/game/shared/tf/tf_shareddefs.cpp index bd54279bc5c..5f512a6780a 100644 --- a/src/game/shared/tf/tf_shareddefs.cpp +++ b/src/game/shared/tf/tf_shareddefs.cpp @@ -916,6 +916,7 @@ const char *g_szSpecialDamageNames[] = "TF_DMG_CUSTOM_KRAMPUS_MELEE", "TF_DMG_CUSTOM_KRAMPUS_RANGED", "TF_DMG_CUSTOM_TAUNTATK_TRICKSHOT", + "TF_DMG_CUSTOM_HEADSHOT_FLYINGBURN", }; COMPILE_TIME_ASSERT( ARRAYSIZE( g_szSpecialDamageNames ) == TF_DMG_CUSTOM_END ); @@ -1462,7 +1463,7 @@ void LoadObjectInfos( IBaseFileSystem *pFileSystem ) // Does it make sense to call the below Steam API so it'll force a validation next startup time? // Need to verify it's real corruption and not someone dorking around with their objects.txt file... // - // From Martin Otten: If you have a file on disc and you’re 100% sure it’s + // From Martin Otten: If you have a file on disc and you're 100% sure it's // corrupt, call ISteamApps::MarkContentCorrupt( false ), before you shutdown // the game. This will cause a content validation in Steam. diff --git a/src/game/shared/tf/tf_shareddefs.h b/src/game/shared/tf/tf_shareddefs.h index f3b5a91a61f..d5f57b75193 100644 --- a/src/game/shared/tf/tf_shareddefs.h +++ b/src/game/shared/tf/tf_shareddefs.h @@ -1265,6 +1265,7 @@ enum ETFDmgCustom TF_DMG_CUSTOM_KRAMPUS_MELEE, TF_DMG_CUSTOM_KRAMPUS_RANGED, TF_DMG_CUSTOM_TAUNTATK_TRICKSHOT, + TF_DMG_CUSTOM_HEADSHOT_FLYINGBURN, // // INSERT NEW ITEMS HERE TO AVOID BREAKING DEMOS // @@ -1309,7 +1310,7 @@ inline bool IsDOTDmg( int iType ) inline bool IsHeadshot( int iType ) { - return (iType == TF_DMG_CUSTOM_HEADSHOT || iType == TF_DMG_CUSTOM_HEADSHOT_DECAPITATION); + return (iType == TF_DMG_CUSTOM_HEADSHOT || iType == TF_DMG_CUSTOM_HEADSHOT_DECAPITATION || iType == TF_DMG_CUSTOM_HEADSHOT_FLYINGBURN); } enum From cf1b5ba74b191c9e2e1abd7502d7b99db58ceb26 Mon Sep 17 00:00:00 2001 From: Thomas Kain Date: Mon, 24 Nov 2025 13:34:48 -0500 Subject: [PATCH 2/4] Implement unused kill icons for reflected arrows This includes the icons for reflected arrow headshots, burning reflected arrows, and burning reflected arrow headshots. --- src/game/client/tf/tf_hud_deathnotice.cpp | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/src/game/client/tf/tf_hud_deathnotice.cpp b/src/game/client/tf/tf_hud_deathnotice.cpp index ff64604712d..e8a529b8b0e 100644 --- a/src/game/client/tf/tf_hud_deathnotice.cpp +++ b/src/game/client/tf/tf_hud_deathnotice.cpp @@ -995,6 +995,10 @@ void CTFHudDeathNotice::OnGameEvent( IGameEvent *event, int iDeathNoticeMsg ) { Q_strncpy( msg.szIcon, "d_huntsman_headshot", ARRAYSIZE( msg.szIcon ) ); } + else if ( FStrEq( event->GetString( "weapon" ), "deflect_arrow" ) ) + { + Q_strncpy( msg.szIcon, "d_deflect_huntsman_headshot", ARRAYSIZE( msg.szIcon ) ); + } else { // Did this headshot penetrate something before the kill? If so, show a fancy icon @@ -1028,7 +1032,14 @@ void CTFHudDeathNotice::OnGameEvent( IGameEvent *event, int iDeathNoticeMsg ) case TF_DMG_CUSTOM_FLYINGBURN: // special-case if the player is killed from a burning arrow as the killing blow - Q_strncpy( msg.szIcon, "d_huntsman_flyingburn", ARRAYSIZE( msg.szIcon ) ); + if ( FStrEq( event->GetString( "weapon" ), "deflect_huntsman_flyingburn" ) ) + { + Q_strncpy( msg.szIcon, "d_deflect_huntsman_flyingburn", ARRAYSIZE( msg.szIcon ) ); + } + else + { + Q_strncpy( msg.szIcon, "d_huntsman_flyingburn", ARRAYSIZE( msg.szIcon ) ); + } msg.wzInfoText[0] = 0; break; @@ -1163,7 +1174,14 @@ void CTFHudDeathNotice::OnGameEvent( IGameEvent *event, int iDeathNoticeMsg ) } case TF_DMG_CUSTOM_HEADSHOT_FLYINGBURN: // special-case if the player is headshot by a burning arrow as the killing blow - Q_strncpy( msg.szIcon, "d_huntsman_flyingburn_headshot", ARRAYSIZE( msg.szIcon ) ); + if ( FStrEq( event->GetString( "weapon" ), "deflect_huntsman_flyingburn" ) ) + { + Q_strncpy( msg.szIcon, "d_deflect_huntsman_flyingburn_headshot", ARRAYSIZE( msg.szIcon ) ); + } + else + { + Q_strncpy( msg.szIcon, "d_huntsman_flyingburn_headshot", ARRAYSIZE( msg.szIcon ) ); + } msg.wzInfoText[0] = 0; break; default: From f8088685749ae6b87cf8f314306b9752d6647bb4 Mon Sep 17 00:00:00 2001 From: Thomas Kain Date: Mon, 24 Nov 2025 13:37:05 -0500 Subject: [PATCH 3/4] Remove unnecessary special casing for deflected burning arrows Since we now handle burning arrows entirely through special damage types, giving deflected burning arrows their own weapon name is unnecessary. --- src/game/client/tf/tf_hud_deathnotice.cpp | 4 ++-- src/game/shared/tf/tf_gamerules.cpp | 10 +--------- 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/src/game/client/tf/tf_hud_deathnotice.cpp b/src/game/client/tf/tf_hud_deathnotice.cpp index e8a529b8b0e..8a611faff2e 100644 --- a/src/game/client/tf/tf_hud_deathnotice.cpp +++ b/src/game/client/tf/tf_hud_deathnotice.cpp @@ -1032,7 +1032,7 @@ void CTFHudDeathNotice::OnGameEvent( IGameEvent *event, int iDeathNoticeMsg ) case TF_DMG_CUSTOM_FLYINGBURN: // special-case if the player is killed from a burning arrow as the killing blow - if ( FStrEq( event->GetString( "weapon" ), "deflect_huntsman_flyingburn" ) ) + if ( FStrEq( event->GetString( "weapon" ), "deflect_arrow" ) ) { Q_strncpy( msg.szIcon, "d_deflect_huntsman_flyingburn", ARRAYSIZE( msg.szIcon ) ); } @@ -1174,7 +1174,7 @@ void CTFHudDeathNotice::OnGameEvent( IGameEvent *event, int iDeathNoticeMsg ) } case TF_DMG_CUSTOM_HEADSHOT_FLYINGBURN: // special-case if the player is headshot by a burning arrow as the killing blow - if ( FStrEq( event->GetString( "weapon" ), "deflect_huntsman_flyingburn" ) ) + if ( FStrEq( event->GetString( "weapon" ), "deflect_arrow" ) ) { Q_strncpy( msg.szIcon, "d_deflect_huntsman_flyingburn_headshot", ARRAYSIZE( msg.szIcon ) ); } diff --git a/src/game/shared/tf/tf_gamerules.cpp b/src/game/shared/tf/tf_gamerules.cpp index c3ec6c0d998..f69214bb62c 100644 --- a/src/game/shared/tf/tf_gamerules.cpp +++ b/src/game/shared/tf/tf_gamerules.cpp @@ -12481,15 +12481,7 @@ const char *CTFGameRules::GetKillingWeaponName( const CTakeDamageInfo &info, CTF } else if ( *iWeaponID == TF_WEAPON_COMPOUND_BOW ) { - CTFProjectile_Arrow* pArrow = dynamic_cast( pBaseRocket ); - if ( pArrow && pArrow->IsAlight() ) - { - killer_weapon_name = "deflect_huntsman_flyingburn"; - } - else - { - killer_weapon_name = "deflect_arrow"; - } + killer_weapon_name = "deflect_arrow"; } else if ( *iWeaponID == TF_WEAPON_SHOTGUN_BUILDING_RESCUE ) { From d006cd3ea5dd4643e1c15d15ea01eeca6830add3 Mon Sep 17 00:00:00 2001 From: Thomas Kain Date: Tue, 25 Nov 2025 11:21:24 -0500 Subject: [PATCH 4/4] Fix reflected burning arrow headshot kill icon not displaying What a mouthful. Fittingly enough, the icon for such a kill has a name that's too long to fit in DeathNoticeItem's szIcon buffer. This commit increases the buffer size to account for that. --- src/game/client/tf/hud_basedeathnotice.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/game/client/tf/hud_basedeathnotice.h b/src/game/client/tf/hud_basedeathnotice.h index c2fc5c80d77..6c3c783db14 100644 --- a/src/game/client/tf/hud_basedeathnotice.h +++ b/src/game/client/tf/hud_basedeathnotice.h @@ -54,7 +54,7 @@ struct DeathNoticeItem DeathNoticePlayer Killer; DeathNoticePlayer Victim; - char szIcon[32]; // name of icon to display + char szIcon[64]; // name of icon to display wchar_t wzInfoText[32]; // any additional text to display next to icon wchar_t wzInfoTextEnd[32]; // any additional text to display next to victim name CHudTexture *iconDeath;