From a234dbfbb061b4da814aa978b9522a78f2454cf2 Mon Sep 17 00:00:00 2001 From: A1mDev <33463136+A1mDev@users.noreply.github.com> Date: Fri, 28 Nov 2025 10:18:43 +0700 Subject: [PATCH 1/8] [l4d2_smoker_drag_damage_interval] Upload --- .../l4d2_smoker_drag_damage_interval.sp | 211 ++++++++++++++++++ 1 file changed, 211 insertions(+) create mode 100644 addons/sourcemod/plugins/optional/l4d2_smoker_drag_damage_interval.sp diff --git a/addons/sourcemod/plugins/optional/l4d2_smoker_drag_damage_interval.sp b/addons/sourcemod/plugins/optional/l4d2_smoker_drag_damage_interval.sp new file mode 100644 index 000000000..a6c63e082 --- /dev/null +++ b/addons/sourcemod/plugins/optional/l4d2_smoker_drag_damage_interval.sp @@ -0,0 +1,211 @@ +/** + * Version 2.2 by A1m` + * + * Changes: + * 1. Removed duplicate plugins: + * - l4d2_smoker_drag_damage_interval_zone + * - l4d2_smoker_drag_damage_interval. + * + * 2. Removed untrusted timer-based code: + * - Replaced with safer, hook-based implementation using OnTakeDamage and netprops. + * - Ensures more stable and reliable drag damage behavior without unnecessary timers. + * + * 3. Supports late loading. +**/ + +#pragma semicolon 1 +#pragma newdecls required + +#include +#include + +#define DEBUG 0 +#define GAMEDATA "l4d2_si_ability" + +// DMG_CHOKE = 1048576 = 0x100000 = (1 << 20) +#define DMG_CHOKE (1 << 20) + +#define IT_TIMESTAMP_INDEX 0 +#define CT_DURATION_OFFSET 4 +#define CT_TIMESTAMP_OFFSET 8 + +#define TEAM_SURVIVOR 2 +#define TEAM_INFECTED 3 + +#define EPSILON 0.001 + +#if DEBUG +float + g_fDebugDamageInterval = 0.0; +#endif + +int + g_iTongueDragDamageTimerDurationOffset = -1, + g_iTongueDragDamageTimerTimeStampOffset = -1; + +ConVar + g_hTongueDragDamageInterval = null, + g_hTongueDragFirstDamageInterval = null, + g_hTongueDragFirstDamage = null; + +public Plugin myinfo = +{ + name = "L4D2 smoker drag damage interval", + author = "Visor, Sir, A1m`", + description = "Implements a native-like cvar that should've been there out of the box", + version = "2.2", + url = "https://github.com/SirPlease/L4D2-Competitive-Rework" +}; + +public void OnPluginStart() +{ + InitGameData(); + + HookEvent("tongue_grab", Event_OnTongueGrab); + + // Get the default value of cvar 'tongue_choke_damage_interval' + char sCvarVal[32]; + ConVar hTongueChokeDamageInterval = FindConVar("tongue_choke_damage_interval"); + hTongueChokeDamageInterval.GetDefault(sCvarVal, sizeof(sCvarVal)); + + g_hTongueDragDamageInterval = CreateConVar("tongue_drag_damage_interval", sCvarVal, "How often the drag does damage. Allowed values: 0.01 - 15.0.", _, true, 0.01, true, 15.0); + g_hTongueDragFirstDamageInterval = CreateConVar("tongue_drag_first_damage_interval", "-1.0", "After how many seconds do we apply our first tick of damage? 0.0 - disable, max value - 15.0.", _, false, 0.0, true, 15.0); + g_hTongueDragFirstDamage = CreateConVar("tongue_drag_first_damage", "-1.0", "How much damage do we apply on the first tongue hit? 0.0 - disable", _, false, 0.0, true, 100.0); + + LateLoad(); +} + +void InitGameData() +{ + Handle hGamedata = LoadGameConfigFile(GAMEDATA); + + if (!hGamedata) { + SetFailState("Gamedata '%s.txt' missing or corrupt.", GAMEDATA); + } + + int iTongueDragDamageTimer = GameConfGetOffset(hGamedata, "CTerrorPlayer::m_tongueDragDamageTimer"); + if (iTongueDragDamageTimer == -1) { + SetFailState("Failed to get offset 'CTerrorPlayer::m_tongueDragDamageTimer'."); + } + + g_iTongueDragDamageTimerDurationOffset = iTongueDragDamageTimer + CT_DURATION_OFFSET; + g_iTongueDragDamageTimerTimeStampOffset = iTongueDragDamageTimer + CT_TIMESTAMP_OFFSET; + + delete hGamedata; +} + +void LateLoad() +{ + for (int i = 1; i <= MaxClients; i++) { + if (!IsClientInGame(i)) { + continue; + } + + OnClientPutInServer(i); + } +} + +public void OnClientPutInServer(int iClient) +{ + SDKHook(iClient, SDKHook_OnTakeDamage, Hook_OnTakeDamage); +} + +void Event_OnTongueGrab(Event hEvent, const char[] eName, bool bDontBroadcast) +{ + // Replacing variable value 'CTerrorPlayer::m_tongueDragDamageTimer', + // ​​after calling a function 'CTerrorPlayer::OnGrabbedByTongue'. + // Fix damage interval. + + int iVictim = GetClientOfUserId(hEvent.GetInt("victim")); + bool bIsHangingFromTongue = (GetEntProp(iVictim, Prop_Send, "m_isHangingFromTongue", 1) > 0); + + if (!bIsHangingFromTongue) { // Dragging? + SetDragDamageTimer(iVictim, GetFirstDamageInterval()); + } + +#if DEBUG + g_fDebugDamageInterval = GetGameTime(); +#endif +} + +Action Hook_OnTakeDamage(int iVictim, int &iAttacker, int &iInflictor, float &fDamage, int &iDamageType) +{ + // Replacing the function patch 'CTerrorPlayer::UpdateHangingFromTongue'. + if (!(iDamageType & DMG_CHOKE)) { + return Plugin_Continue; + } + + int iTongueOwner = GetEntPropEnt(iVictim, Prop_Send, "m_tongueOwner"); + if (iTongueOwner < 1 || iTongueOwner > MaxClients || iTongueOwner != iAttacker) { + return Plugin_Continue; + } + + // Stop dragging. + bool bIsHangingFromTongue = (GetEntProp(iVictim, Prop_Send, "m_isHangingFromTongue", 1) > 0); + if (bIsHangingFromTongue) { + return Plugin_Continue; + } + + // Fix damage interval. + SetDragDamageTimer(iVictim, g_hTongueDragDamageInterval.FloatValue); + + // First damage if cvar enabled. + bool bFirstDamage = false; + float fTongueDragFirstDamage = g_hTongueDragFirstDamage.FloatValue; + + if (fTongueDragFirstDamage > 0.0) { + float fTongueVictimTimer = GetEntPropFloat(iVictim, Prop_Send, "m_tongueVictimTimer", IT_TIMESTAMP_INDEX); + + if (FloatCompareEps(fTongueVictimTimer, GetFirstDamageInterval()) == 0) { + fDamage = fTongueDragFirstDamage; + bFirstDamage = true; + } + } + +#if DEBUG + DebugPrint(iVictim, fDamage, bFirstDamage); +#endif + + return (bFirstDamage) ? Plugin_Changed : Plugin_Continue; +} + +float GetFirstDamageInterval() +{ + float fTongueFirstDamageInterval = g_hTongueDragFirstDamageInterval.FloatValue; + if (fTongueFirstDamageInterval > 0.0) { + return fTongueFirstDamageInterval; + } + + return g_hTongueDragDamageInterval.FloatValue; +} + +void SetDragDamageTimer(int iClient, float fDuration) +{ + // 'CTerrorPlayer::m_tongueDragDamageTimer', this is not netprop + float fTimeStamp = GetGameTime() + fDuration; + + SetEntDataFloat(iClient, g_iTongueDragDamageTimerDurationOffset, fDuration, false); // 'CountdownTimer::duration' + SetEntDataFloat(iClient, g_iTongueDragDamageTimerTimeStampOffset, fTimeStamp, false); // 'CountdownTimer::timestamp' +} + +// For small differences in tick interval. +int FloatCompareEps(float fOne, float fTwo, float fEps = EPSILON) +{ + if (FloatAbs(fOne - fTwo) < fEps) { + return 0; + } else if (fOne > fTwo) { + return 1; + } + + return -1; +} + +#if DEBUG +void DebugPrint(int iVictim, float fDamage, bool bFirstDamage) +{ + PrintToChatAll("[DEBUG] Victim: %N, %sdamage: %f, time: %f, game time: %f", \ + iVictim, (bFirstDamage) ? "first " : "", fDamage, GetGameTime() - g_fDebugDamageInterval, GetGameTime()); + + g_fDebugDamageInterval = GetGameTime(); +} +#endif From 9c92515e93686965e7e458f13c7ac4d07ff4f9b9 Mon Sep 17 00:00:00 2001 From: A1mDev <33463136+A1mDev@users.noreply.github.com> Date: Fri, 28 Nov 2025 10:23:23 +0700 Subject: [PATCH 2/8] [l4d2_smoker_drag_damage_interval] Change configs for new version --- cfg/cfgogl/apex/shared_plugins.cfg | 2 +- cfg/cfgogl/neomod/shared_plugins.cfg | 2 +- cfg/cfgogl/nextmod/shared_plugins.cfg | 2 +- cfg/cfgogl/pmelite/confogl_plugins.cfg | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/cfg/cfgogl/apex/shared_plugins.cfg b/cfg/cfgogl/apex/shared_plugins.cfg index a1ab438c5..2d95d7f0c 100644 --- a/cfg/cfgogl/apex/shared_plugins.cfg +++ b/cfg/cfgogl/apex/shared_plugins.cfg @@ -73,7 +73,7 @@ sm plugins load optional/l4d_texture_manager_block.smx //---------------------- // EQ3 / Acemod / ZoneMod / Apex //---------------------- -sm plugins load optional/l4d2_smoker_drag_damage_interval_zone.smx +sm plugins load optional/l4d2_smoker_drag_damage_interval.smx sm plugins load optional/l4d_tankpunchstuckfix.smx sm plugins load optional/despawn_health.smx sm plugins load optional/checkpoint-rage-control.smx diff --git a/cfg/cfgogl/neomod/shared_plugins.cfg b/cfg/cfgogl/neomod/shared_plugins.cfg index 789ba7ac6..c7835c9ba 100644 --- a/cfg/cfgogl/neomod/shared_plugins.cfg +++ b/cfg/cfgogl/neomod/shared_plugins.cfg @@ -77,7 +77,7 @@ sm plugins load optional/l4d_texture_manager_block.smx //---------------------- // EQ3 / Acemod / ZoneMod //---------------------- -sm plugins load optional/l4d2_smoker_drag_damage_interval_zone.smx +sm plugins load optional/l4d2_smoker_drag_damage_interval.smx sm plugins load optional/l4d_tankpunchstuckfix.smx sm plugins load optional/despawn_health.smx sm plugins load optional/checkpoint-rage-control.smx diff --git a/cfg/cfgogl/nextmod/shared_plugins.cfg b/cfg/cfgogl/nextmod/shared_plugins.cfg index c2f9e3e52..a79eba6c6 100644 --- a/cfg/cfgogl/nextmod/shared_plugins.cfg +++ b/cfg/cfgogl/nextmod/shared_plugins.cfg @@ -54,7 +54,7 @@ sm plugins load optional/l4d2_spitblock.smx sm plugins load optional/l4d2_uniform_spit.smx sm plugins load optional/l4d_tank_painfade.smx sm plugins load optional/l4d_texture_manager_block.smx -sm plugins load optional/l4d2_smoker_drag_damage_interval_zone.smx +sm plugins load optional/l4d2_smoker_drag_damage_interval.smx sm plugins load optional/l4d_tankpunchstuckfix.smx sm plugins load optional/despawn_health.smx sm plugins load optional/checkpoint-rage-control.smx diff --git a/cfg/cfgogl/pmelite/confogl_plugins.cfg b/cfg/cfgogl/pmelite/confogl_plugins.cfg index 42ca010ee..735c62f7c 100644 --- a/cfg/cfgogl/pmelite/confogl_plugins.cfg +++ b/cfg/cfgogl/pmelite/confogl_plugins.cfg @@ -67,7 +67,7 @@ sm plugins load optional/l4d2_saferoom_detect.smx sm plugins load optional/l4d2_saferoom_item_remove.smx sm plugins load optional/l4d2_scoremod.smx sm plugins load optional/l4d2_setscores.smx -sm plugins load optional/l4d2_smoker_drag_damage_interval_zone.smx +sm plugins load optional/l4d2_smoker_drag_damage_interval.smx sm plugins load optional/l4d2_si_ffblock.smx sm plugins load optional/l4d2_si_staggers.smx sm plugins load optional/l4d2_skill_detect.smx From 04f523a3926c4eda04d02981728234ceefb5d2cd Mon Sep 17 00:00:00 2001 From: A1mDev <33463136+A1mDev@users.noreply.github.com> Date: Fri, 28 Nov 2025 10:24:46 +0700 Subject: [PATCH 3/8] [l4d2_smoker_drag_damage_interval] Upload --- .../l4d2_smoker_drag_damage_interval.sp | 211 ----------------- .../l4d2_smoker_drag_damage_interval.sp | 215 +++++++++++++----- 2 files changed, 160 insertions(+), 266 deletions(-) delete mode 100644 addons/sourcemod/plugins/optional/l4d2_smoker_drag_damage_interval.sp diff --git a/addons/sourcemod/plugins/optional/l4d2_smoker_drag_damage_interval.sp b/addons/sourcemod/plugins/optional/l4d2_smoker_drag_damage_interval.sp deleted file mode 100644 index a6c63e082..000000000 --- a/addons/sourcemod/plugins/optional/l4d2_smoker_drag_damage_interval.sp +++ /dev/null @@ -1,211 +0,0 @@ -/** - * Version 2.2 by A1m` - * - * Changes: - * 1. Removed duplicate plugins: - * - l4d2_smoker_drag_damage_interval_zone - * - l4d2_smoker_drag_damage_interval. - * - * 2. Removed untrusted timer-based code: - * - Replaced with safer, hook-based implementation using OnTakeDamage and netprops. - * - Ensures more stable and reliable drag damage behavior without unnecessary timers. - * - * 3. Supports late loading. -**/ - -#pragma semicolon 1 -#pragma newdecls required - -#include -#include - -#define DEBUG 0 -#define GAMEDATA "l4d2_si_ability" - -// DMG_CHOKE = 1048576 = 0x100000 = (1 << 20) -#define DMG_CHOKE (1 << 20) - -#define IT_TIMESTAMP_INDEX 0 -#define CT_DURATION_OFFSET 4 -#define CT_TIMESTAMP_OFFSET 8 - -#define TEAM_SURVIVOR 2 -#define TEAM_INFECTED 3 - -#define EPSILON 0.001 - -#if DEBUG -float - g_fDebugDamageInterval = 0.0; -#endif - -int - g_iTongueDragDamageTimerDurationOffset = -1, - g_iTongueDragDamageTimerTimeStampOffset = -1; - -ConVar - g_hTongueDragDamageInterval = null, - g_hTongueDragFirstDamageInterval = null, - g_hTongueDragFirstDamage = null; - -public Plugin myinfo = -{ - name = "L4D2 smoker drag damage interval", - author = "Visor, Sir, A1m`", - description = "Implements a native-like cvar that should've been there out of the box", - version = "2.2", - url = "https://github.com/SirPlease/L4D2-Competitive-Rework" -}; - -public void OnPluginStart() -{ - InitGameData(); - - HookEvent("tongue_grab", Event_OnTongueGrab); - - // Get the default value of cvar 'tongue_choke_damage_interval' - char sCvarVal[32]; - ConVar hTongueChokeDamageInterval = FindConVar("tongue_choke_damage_interval"); - hTongueChokeDamageInterval.GetDefault(sCvarVal, sizeof(sCvarVal)); - - g_hTongueDragDamageInterval = CreateConVar("tongue_drag_damage_interval", sCvarVal, "How often the drag does damage. Allowed values: 0.01 - 15.0.", _, true, 0.01, true, 15.0); - g_hTongueDragFirstDamageInterval = CreateConVar("tongue_drag_first_damage_interval", "-1.0", "After how many seconds do we apply our first tick of damage? 0.0 - disable, max value - 15.0.", _, false, 0.0, true, 15.0); - g_hTongueDragFirstDamage = CreateConVar("tongue_drag_first_damage", "-1.0", "How much damage do we apply on the first tongue hit? 0.0 - disable", _, false, 0.0, true, 100.0); - - LateLoad(); -} - -void InitGameData() -{ - Handle hGamedata = LoadGameConfigFile(GAMEDATA); - - if (!hGamedata) { - SetFailState("Gamedata '%s.txt' missing or corrupt.", GAMEDATA); - } - - int iTongueDragDamageTimer = GameConfGetOffset(hGamedata, "CTerrorPlayer::m_tongueDragDamageTimer"); - if (iTongueDragDamageTimer == -1) { - SetFailState("Failed to get offset 'CTerrorPlayer::m_tongueDragDamageTimer'."); - } - - g_iTongueDragDamageTimerDurationOffset = iTongueDragDamageTimer + CT_DURATION_OFFSET; - g_iTongueDragDamageTimerTimeStampOffset = iTongueDragDamageTimer + CT_TIMESTAMP_OFFSET; - - delete hGamedata; -} - -void LateLoad() -{ - for (int i = 1; i <= MaxClients; i++) { - if (!IsClientInGame(i)) { - continue; - } - - OnClientPutInServer(i); - } -} - -public void OnClientPutInServer(int iClient) -{ - SDKHook(iClient, SDKHook_OnTakeDamage, Hook_OnTakeDamage); -} - -void Event_OnTongueGrab(Event hEvent, const char[] eName, bool bDontBroadcast) -{ - // Replacing variable value 'CTerrorPlayer::m_tongueDragDamageTimer', - // ​​after calling a function 'CTerrorPlayer::OnGrabbedByTongue'. - // Fix damage interval. - - int iVictim = GetClientOfUserId(hEvent.GetInt("victim")); - bool bIsHangingFromTongue = (GetEntProp(iVictim, Prop_Send, "m_isHangingFromTongue", 1) > 0); - - if (!bIsHangingFromTongue) { // Dragging? - SetDragDamageTimer(iVictim, GetFirstDamageInterval()); - } - -#if DEBUG - g_fDebugDamageInterval = GetGameTime(); -#endif -} - -Action Hook_OnTakeDamage(int iVictim, int &iAttacker, int &iInflictor, float &fDamage, int &iDamageType) -{ - // Replacing the function patch 'CTerrorPlayer::UpdateHangingFromTongue'. - if (!(iDamageType & DMG_CHOKE)) { - return Plugin_Continue; - } - - int iTongueOwner = GetEntPropEnt(iVictim, Prop_Send, "m_tongueOwner"); - if (iTongueOwner < 1 || iTongueOwner > MaxClients || iTongueOwner != iAttacker) { - return Plugin_Continue; - } - - // Stop dragging. - bool bIsHangingFromTongue = (GetEntProp(iVictim, Prop_Send, "m_isHangingFromTongue", 1) > 0); - if (bIsHangingFromTongue) { - return Plugin_Continue; - } - - // Fix damage interval. - SetDragDamageTimer(iVictim, g_hTongueDragDamageInterval.FloatValue); - - // First damage if cvar enabled. - bool bFirstDamage = false; - float fTongueDragFirstDamage = g_hTongueDragFirstDamage.FloatValue; - - if (fTongueDragFirstDamage > 0.0) { - float fTongueVictimTimer = GetEntPropFloat(iVictim, Prop_Send, "m_tongueVictimTimer", IT_TIMESTAMP_INDEX); - - if (FloatCompareEps(fTongueVictimTimer, GetFirstDamageInterval()) == 0) { - fDamage = fTongueDragFirstDamage; - bFirstDamage = true; - } - } - -#if DEBUG - DebugPrint(iVictim, fDamage, bFirstDamage); -#endif - - return (bFirstDamage) ? Plugin_Changed : Plugin_Continue; -} - -float GetFirstDamageInterval() -{ - float fTongueFirstDamageInterval = g_hTongueDragFirstDamageInterval.FloatValue; - if (fTongueFirstDamageInterval > 0.0) { - return fTongueFirstDamageInterval; - } - - return g_hTongueDragDamageInterval.FloatValue; -} - -void SetDragDamageTimer(int iClient, float fDuration) -{ - // 'CTerrorPlayer::m_tongueDragDamageTimer', this is not netprop - float fTimeStamp = GetGameTime() + fDuration; - - SetEntDataFloat(iClient, g_iTongueDragDamageTimerDurationOffset, fDuration, false); // 'CountdownTimer::duration' - SetEntDataFloat(iClient, g_iTongueDragDamageTimerTimeStampOffset, fTimeStamp, false); // 'CountdownTimer::timestamp' -} - -// For small differences in tick interval. -int FloatCompareEps(float fOne, float fTwo, float fEps = EPSILON) -{ - if (FloatAbs(fOne - fTwo) < fEps) { - return 0; - } else if (fOne > fTwo) { - return 1; - } - - return -1; -} - -#if DEBUG -void DebugPrint(int iVictim, float fDamage, bool bFirstDamage) -{ - PrintToChatAll("[DEBUG] Victim: %N, %sdamage: %f, time: %f, game time: %f", \ - iVictim, (bFirstDamage) ? "first " : "", fDamage, GetGameTime() - g_fDebugDamageInterval, GetGameTime()); - - g_fDebugDamageInterval = GetGameTime(); -} -#endif diff --git a/addons/sourcemod/scripting/l4d2_smoker_drag_damage_interval.sp b/addons/sourcemod/scripting/l4d2_smoker_drag_damage_interval.sp index af60931d4..a6c63e082 100644 --- a/addons/sourcemod/scripting/l4d2_smoker_drag_damage_interval.sp +++ b/addons/sourcemod/scripting/l4d2_smoker_drag_damage_interval.sp @@ -1,44 +1,78 @@ +/** + * Version 2.2 by A1m` + * + * Changes: + * 1. Removed duplicate plugins: + * - l4d2_smoker_drag_damage_interval_zone + * - l4d2_smoker_drag_damage_interval. + * + * 2. Removed untrusted timer-based code: + * - Replaced with safer, hook-based implementation using OnTakeDamage and netprops. + * - Ensures more stable and reliable drag damage behavior without unnecessary timers. + * + * 3. Supports late loading. +**/ + #pragma semicolon 1 #pragma newdecls required #include +#include + +#define DEBUG 0 +#define GAMEDATA "l4d2_si_ability" + +// DMG_CHOKE = 1048576 = 0x100000 = (1 << 20) +#define DMG_CHOKE (1 << 20) -#define GAMEDATA "l4d2_si_ability" +#define IT_TIMESTAMP_INDEX 0 +#define CT_DURATION_OFFSET 4 +#define CT_TIMESTAMP_OFFSET 8 -#define DURATION_OFFSET 4 -#define TIMESTAMP_OFFSET 8 +#define TEAM_SURVIVOR 2 +#define TEAM_INFECTED 3 -#define TEAM_SURVIVOR 2 +#define EPSILON 0.001 -int - m_tongueDragDamageTimerDuration, - m_tongueDragDamageTimerTimeStamp; +#if DEBUG +float + g_fDebugDamageInterval = 0.0; +#endif -ConVar - tongue_drag_damage_interval; +int + g_iTongueDragDamageTimerDurationOffset = -1, + g_iTongueDragDamageTimerTimeStampOffset = -1; + +ConVar + g_hTongueDragDamageInterval = null, + g_hTongueDragFirstDamageInterval = null, + g_hTongueDragFirstDamage = null; public Plugin myinfo = { - name = "L4D2 Smoker Drag Damage Interval", - author = "Visor, A1m`", + name = "L4D2 smoker drag damage interval", + author = "Visor, Sir, A1m`", description = "Implements a native-like cvar that should've been there out of the box", - version = "0.7", + version = "2.2", url = "https://github.com/SirPlease/L4D2-Competitive-Rework" }; public void OnPluginStart() { InitGameData(); - HookEvent("tongue_grab", OnTongueGrab); - - char value[32]; - ConVar tongue_choke_damage_interval = FindConVar("tongue_choke_damage_interval"); - tongue_choke_damage_interval.GetString(value, sizeof(value)); - - tongue_drag_damage_interval = CreateConVar("tongue_drag_damage_interval", value, "How often the drag does damage."); - - ConVar tongue_choke_damage_amount = FindConVar("tongue_choke_damage_amount"); - tongue_choke_damage_amount.AddChangeHook(tongue_choke_damage_amount_ValueChanged); + + HookEvent("tongue_grab", Event_OnTongueGrab); + + // Get the default value of cvar 'tongue_choke_damage_interval' + char sCvarVal[32]; + ConVar hTongueChokeDamageInterval = FindConVar("tongue_choke_damage_interval"); + hTongueChokeDamageInterval.GetDefault(sCvarVal, sizeof(sCvarVal)); + + g_hTongueDragDamageInterval = CreateConVar("tongue_drag_damage_interval", sCvarVal, "How often the drag does damage. Allowed values: 0.01 - 15.0.", _, true, 0.01, true, 15.0); + g_hTongueDragFirstDamageInterval = CreateConVar("tongue_drag_first_damage_interval", "-1.0", "After how many seconds do we apply our first tick of damage? 0.0 - disable, max value - 15.0.", _, false, 0.0, true, 15.0); + g_hTongueDragFirstDamage = CreateConVar("tongue_drag_first_damage", "-1.0", "How much damage do we apply on the first tongue hit? 0.0 - disable", _, false, 0.0, true, 100.0); + + LateLoad(); } void InitGameData() @@ -48,59 +82,130 @@ void InitGameData() if (!hGamedata) { SetFailState("Gamedata '%s.txt' missing or corrupt.", GAMEDATA); } - - int m_tongueDragDamageTimer = GameConfGetOffset(hGamedata, "CTerrorPlayer->m_tongueDragDamageTimer"); - if (m_tongueDragDamageTimer == -1) { - SetFailState("Failed to get offset 'CTerrorPlayer->m_tongueDragDamageTimer'."); + + int iTongueDragDamageTimer = GameConfGetOffset(hGamedata, "CTerrorPlayer::m_tongueDragDamageTimer"); + if (iTongueDragDamageTimer == -1) { + SetFailState("Failed to get offset 'CTerrorPlayer::m_tongueDragDamageTimer'."); } - - m_tongueDragDamageTimerDuration = m_tongueDragDamageTimer + DURATION_OFFSET; - m_tongueDragDamageTimerTimeStamp = m_tongueDragDamageTimer + TIMESTAMP_OFFSET; - + + g_iTongueDragDamageTimerDurationOffset = iTongueDragDamageTimer + CT_DURATION_OFFSET; + g_iTongueDragDamageTimerTimeStampOffset = iTongueDragDamageTimer + CT_TIMESTAMP_OFFSET; + delete hGamedata; } -void tongue_choke_damage_amount_ValueChanged(ConVar convar, const char[] oldValue, const char[] newValue) +void LateLoad() { - convar.SetInt(1); // hack-hack: game tries to change this cvar for some reason, can't be arsed so HARDCODETHATSHIT + for (int i = 1; i <= MaxClients; i++) { + if (!IsClientInGame(i)) { + continue; + } + + OnClientPutInServer(i); + } +} + +public void OnClientPutInServer(int iClient) +{ + SDKHook(iClient, SDKHook_OnTakeDamage, Hook_OnTakeDamage); } -void OnTongueGrab(Event hEvent, const char[] eName, bool dontBroadcast) +void Event_OnTongueGrab(Event hEvent, const char[] eName, bool bDontBroadcast) { - int userid = hEvent.GetInt("victim"); - int client = GetClientOfUserId(userid); - - SetDragDamageInterval(client); - - float fTimerUpdate = tongue_drag_damage_interval.FloatValue + 0.1; - CreateTimer(fTimerUpdate, FixDragInterval, userid, TIMER_REPEAT | TIMER_FLAG_NO_MAPCHANGE); + // Replacing variable value 'CTerrorPlayer::m_tongueDragDamageTimer', + // ​​after calling a function 'CTerrorPlayer::OnGrabbedByTongue'. + // Fix damage interval. + + int iVictim = GetClientOfUserId(hEvent.GetInt("victim")); + bool bIsHangingFromTongue = (GetEntProp(iVictim, Prop_Send, "m_isHangingFromTongue", 1) > 0); + + if (!bIsHangingFromTongue) { // Dragging? + SetDragDamageTimer(iVictim, GetFirstDamageInterval()); + } + +#if DEBUG + g_fDebugDamageInterval = GetGameTime(); +#endif } -Action FixDragInterval(Handle hTimer, any userid) +Action Hook_OnTakeDamage(int iVictim, int &iAttacker, int &iInflictor, float &fDamage, int &iDamageType) { - int client = GetClientOfUserId(userid); - if (client > 0 && GetClientTeam(client) == TEAM_SURVIVOR && IsSurvivorBeingDragged(client)) { - SetDragDamageInterval(client); + // Replacing the function patch 'CTerrorPlayer::UpdateHangingFromTongue'. + if (!(iDamageType & DMG_CHOKE)) { + return Plugin_Continue; + } + + int iTongueOwner = GetEntPropEnt(iVictim, Prop_Send, "m_tongueOwner"); + if (iTongueOwner < 1 || iTongueOwner > MaxClients || iTongueOwner != iAttacker) { + return Plugin_Continue; + } + + // Stop dragging. + bool bIsHangingFromTongue = (GetEntProp(iVictim, Prop_Send, "m_isHangingFromTongue", 1) > 0); + if (bIsHangingFromTongue) { return Plugin_Continue; } - return Plugin_Stop; + + // Fix damage interval. + SetDragDamageTimer(iVictim, g_hTongueDragDamageInterval.FloatValue); + + // First damage if cvar enabled. + bool bFirstDamage = false; + float fTongueDragFirstDamage = g_hTongueDragFirstDamage.FloatValue; + + if (fTongueDragFirstDamage > 0.0) { + float fTongueVictimTimer = GetEntPropFloat(iVictim, Prop_Send, "m_tongueVictimTimer", IT_TIMESTAMP_INDEX); + + if (FloatCompareEps(fTongueVictimTimer, GetFirstDamageInterval()) == 0) { + fDamage = fTongueDragFirstDamage; + bFirstDamage = true; + } + } + +#if DEBUG + DebugPrint(iVictim, fDamage, bFirstDamage); +#endif + + return (bFirstDamage) ? Plugin_Changed : Plugin_Continue; +} + +float GetFirstDamageInterval() +{ + float fTongueFirstDamageInterval = g_hTongueDragFirstDamageInterval.FloatValue; + if (fTongueFirstDamageInterval > 0.0) { + return fTongueFirstDamageInterval; + } + + return g_hTongueDragDamageInterval.FloatValue; } -void SetDragDamageInterval(int client) +void SetDragDamageTimer(int iClient, float fDuration) { - float fCvarValue = tongue_drag_damage_interval.FloatValue; - float fTimeStamp = GetGameTime() + fCvarValue; - - SetEntDataFloat(client, m_tongueDragDamageTimerDuration, fCvarValue); //duration - SetEntDataFloat(client, m_tongueDragDamageTimerTimeStamp, fTimeStamp); //timestamp + // 'CTerrorPlayer::m_tongueDragDamageTimer', this is not netprop + float fTimeStamp = GetGameTime() + fDuration; + + SetEntDataFloat(iClient, g_iTongueDragDamageTimerDurationOffset, fDuration, false); // 'CountdownTimer::duration' + SetEntDataFloat(iClient, g_iTongueDragDamageTimerTimeStampOffset, fTimeStamp, false); // 'CountdownTimer::timestamp' } -bool IsSurvivorBeingDragged(int client) +// For small differences in tick interval. +int FloatCompareEps(float fOne, float fTwo, float fEps = EPSILON) { - return ((GetEntPropEnt(client, Prop_Send, "m_tongueOwner") > 0) && !IsSurvivorBeingChoked(client)); + if (FloatAbs(fOne - fTwo) < fEps) { + return 0; + } else if (fOne > fTwo) { + return 1; + } + + return -1; } -bool IsSurvivorBeingChoked(int client) +#if DEBUG +void DebugPrint(int iVictim, float fDamage, bool bFirstDamage) { - return (GetEntProp(client, Prop_Send, "m_isHangingFromTongue") > 0); + PrintToChatAll("[DEBUG] Victim: %N, %sdamage: %f, time: %f, game time: %f", \ + iVictim, (bFirstDamage) ? "first " : "", fDamage, GetGameTime() - g_fDebugDamageInterval, GetGameTime()); + + g_fDebugDamageInterval = GetGameTime(); } +#endif From 314b5e33b46145512661cdcc5c24b03ead93d295 Mon Sep 17 00:00:00 2001 From: A1mDev <33463136+A1mDev@users.noreply.github.com> Date: Fri, 28 Nov 2025 10:25:03 +0700 Subject: [PATCH 4/8] [l4d2_smoker_drag_damage_interval_zone] Delete old version --- .../l4d2_smoker_drag_damage_interval_zone.sp | 143 ------------------ 1 file changed, 143 deletions(-) delete mode 100644 addons/sourcemod/scripting/l4d2_smoker_drag_damage_interval_zone.sp diff --git a/addons/sourcemod/scripting/l4d2_smoker_drag_damage_interval_zone.sp b/addons/sourcemod/scripting/l4d2_smoker_drag_damage_interval_zone.sp deleted file mode 100644 index e3122c2fc..000000000 --- a/addons/sourcemod/scripting/l4d2_smoker_drag_damage_interval_zone.sp +++ /dev/null @@ -1,143 +0,0 @@ -#pragma semicolon 1 -#pragma newdecls required - -#include -#include -#include - -#define GAMEDATA "l4d2_si_ability" - -#define DURATION_OFFSET 4 -#define TIMESTAMP_OFFSET 8 - -#define TEAM_SURVIVOR 2 -#define TEAM_INFECTED 3 - -int - m_tongueDragDamageTimerDuration, - m_tongueDragDamageTimerTimeStamp; - -ConVar - tongue_drag_damage_interval, - tongue_drag_first_damage_interval, - tongue_drag_first_damage; - -public Plugin myinfo = -{ - name = "L4D2 Smoker Drag Damage Interval", - author = "Visor, Sir, A1m`", - description = "Implements a native-like cvar that should've been there out of the box", - version = "1.0", - url = "https://github.com/SirPlease/L4D2-Competitive-Rework" -}; - -public void OnPluginStart() -{ - InitGameData(); - - HookEvent("tongue_grab", OnTongueGrab); - - char value[32]; - ConVar tongue_choke_damage_interval = FindConVar("tongue_choke_damage_interval"); - tongue_choke_damage_interval.GetString(value, sizeof(value)); - - tongue_drag_damage_interval = CreateConVar("tongue_drag_damage_interval", value, "How often the drag does damage."); - tongue_drag_first_damage_interval = CreateConVar("tongue_drag_first_damage_interval", "-1.0", "After how many seconds do we apply our first tick of damage? | 0.0 to Disable."); - tongue_drag_first_damage = CreateConVar("tongue_drag_first_damage", "3.0", "How much damage do we apply on the first tongue hit? | Only applies when first_damage_interval is used"); - - ConVar tongue_choke_damage_amount = FindConVar("tongue_choke_damage_amount"); - tongue_choke_damage_amount.AddChangeHook(tongue_choke_damage_amount_ValueChanged); -} - -void InitGameData() -{ - Handle hGamedata = LoadGameConfigFile(GAMEDATA); - - if (!hGamedata) { - SetFailState("Gamedata '%s.txt' missing or corrupt.", GAMEDATA); - } - - int m_tongueDragDamageTimer = GameConfGetOffset(hGamedata, "CTerrorPlayer->m_tongueDragDamageTimer"); - if (m_tongueDragDamageTimer == -1) { - SetFailState("Failed to get offset 'CTerrorPlayer->m_tongueDragDamageTimer'."); - } - - m_tongueDragDamageTimerDuration = m_tongueDragDamageTimer + DURATION_OFFSET; - m_tongueDragDamageTimerTimeStamp = m_tongueDragDamageTimer + TIMESTAMP_OFFSET; - - delete hGamedata; -} - -void tongue_choke_damage_amount_ValueChanged(ConVar convar, const char[] oldValue, const char[] newValue) -{ - SetConVarInt(convar, 1); // hack-hack: game tries to change this cvar for some reason, can't be arsed so HARDCODETHATSHIT -} - -void OnTongueGrab(Event hEvent, const char[] name, bool dontBroadcast) -{ - int userid = hEvent.GetInt("victim"); - int client = GetClientOfUserId(userid); - float fFirst = tongue_drag_first_damage_interval.FloatValue; - - if (fFirst < 0.0) { - FixDragInterval(client, userid); - return; - } - - SetDragDamageInterval(client, tongue_drag_first_damage_interval); - CreateTimer(fFirst, FirstDamage, userid, TIMER_FLAG_NO_MAPCHANGE); -} - -void FixDragInterval(int client, int userid) -{ - SetDragDamageInterval(client, tongue_drag_damage_interval); - - float fTimerUpdate = tongue_drag_damage_interval.FloatValue + 0.1; - CreateTimer(fTimerUpdate, FixDragIntervalTimer, userid, TIMER_REPEAT | TIMER_FLAG_NO_MAPCHANGE); -} - -Action FirstDamage(Handle hTimer, any userid) -{ - int client = GetClientOfUserId(userid); - if (client > 0 && GetClientTeam(client) == TEAM_SURVIVOR && IsSurvivorBeingDragged(client)) { - int iAttacker = GetEntPropEnt(client, Prop_Send, "m_tongueOwner"); - if (IsClientInGame(iAttacker) && GetClientTeam(iAttacker) == TEAM_INFECTED) { - float fDamage = tongue_drag_first_damage.FloatValue - 1.0; - SDKHooks_TakeDamage(client, iAttacker, iAttacker, fDamage); - } - - FixDragInterval(client, userid); - return Plugin_Continue; - } - - return Plugin_Continue; -} - -Action FixDragIntervalTimer(Handle hTimer, any userid) -{ - int client = GetClientOfUserId(userid); - if (client > 0 && GetClientTeam(client) == TEAM_SURVIVOR && IsSurvivorBeingDragged(client)) { - SetDragDamageInterval(client, tongue_drag_damage_interval); - return Plugin_Continue; - } - return Plugin_Stop; -} - -void SetDragDamageInterval(int client, ConVar hConvar) -{ - float fCvarValue = hConvar.FloatValue; - float fTimeStamp = GetGameTime() + fCvarValue; - - SetEntDataFloat(client, m_tongueDragDamageTimerDuration, fCvarValue); //duration - SetEntDataFloat(client, m_tongueDragDamageTimerTimeStamp, fTimeStamp); //timestamp -} - -bool IsSurvivorBeingDragged(int client) -{ - return ((GetEntPropEnt(client, Prop_Send, "m_tongueOwner") > 0) && !IsSurvivorBeingChoked(client)); -} - -bool IsSurvivorBeingChoked(int client) -{ - return (GetEntProp(client, Prop_Send, "m_isHangingFromTongue") > 0); -} From 01b56cadc46dec708ce26409c83aaa494be012ba Mon Sep 17 00:00:00 2001 From: A1mDev <33463136+A1mDev@users.noreply.github.com> Date: Fri, 28 Nov 2025 10:26:10 +0700 Subject: [PATCH 5/8] [l4d2_smoker_drag_damage_interval] Upload smx --- .../l4d2_smoker_drag_damage_interval.smx | Bin 4639 -> 5402 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/addons/sourcemod/plugins/optional/l4d2_smoker_drag_damage_interval.smx b/addons/sourcemod/plugins/optional/l4d2_smoker_drag_damage_interval.smx index ad99fe0ec521e043650648ac57407aeae8581e88..83e2bea8476ac2fdb142cf94b8474693a219fac7 100644 GIT binary patch delta 5251 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 BnOy(? From ca0a12df60ad1376ea172ab4984997b5df161fe9 Mon Sep 17 00:00:00 2001 From: A1mDev <33463136+A1mDev@users.noreply.github.com> Date: Fri, 28 Nov 2025 10:26:19 +0700 Subject: [PATCH 6/8] [l4d2_smoker_drag_damage_interval_zone] Remove old smx version --- .../l4d2_smoker_drag_damage_interval_zone.smx | Bin 5706 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 addons/sourcemod/plugins/optional/l4d2_smoker_drag_damage_interval_zone.smx diff --git a/addons/sourcemod/plugins/optional/l4d2_smoker_drag_damage_interval_zone.smx b/addons/sourcemod/plugins/optional/l4d2_smoker_drag_damage_interval_zone.smx deleted file mode 100644 index a4426cf07c93ff8a406e20709f95a8c7eae94c6e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5706 zcmYk21yodB`?m)W$pJ+qW(XAtNu^6r5RmTf7+}buyQCCQK)Q3JQ=}0_1eBDa1cn%5 zkZy+NKR(a%uJ1c*{nmBe``qW=dmnju4Q+fpJS7?c0R9pHpeDux0G4q1`~2k|9RP3} z=SUC$@QM-uAjdfkCjel8<9%FQaGsAN6D|~70DwJ??6`R2`~deMFV5}x0e}k}MRBnf z007!?l*5Ht5CA~osD;Y~&I55Y#l=SmcMu#Maf!t_1CD;Ul;GR~$G5l;2?GEha7@M} z1?P8g%){{CB+p=i4|p!4ZLr;&T9?2Y0t&oKr{x0BQKRb6UIFKmfcp zmY$XXUYM7a6V%${AMv(y$BB!jC)6AAk2vGt?&%5rxAz}&hIrb$+Wfctzt(@=KwP|> z|6BL;gF$Q{w*Oz{0rC8=vUalc@c4f(ZJ`h++&EqvD?46WsMFtl|Ed$z<$nrwv331- z@qc^&(_!c2YGvv4zjjyazwSG;Z5#qK=`&sMVK=wgd_2&|El~!o{*HiJ=c)J0Y07+@ z5jSiswIWfjONwJ#OWINNS#AqR!_NP7U#6gc`uK%?cU%UEU=B%mi7Fz*CTegzxq%LZ zY|uo!um@UqIjcH$y_f;*g(3pKUd&;agR5ktr2~p4?64QpP12oh7jr=uyiPQQMYDH~ zHp62PJY?6b=-|86WW1D3_je3?=pVWmc0Vscn;~H}(B{t|7rE}7DOj2iN*AaQ=510F zrEK4v95o9^e-cDl0@oax?}2s*f!L2x0dn0PpU}e&&G&&mX5FTfuqixK)Wq<}sK-7TP0AltdMJ*uJ?*5G4WHDU1rx?|!Gd-7*An zsfbdBHWLZqW{MP{DIA+WMoCnobH`xpLZ|`IPD7ML5qka?Y)ud)2lO%R?y=4co`OMv zP3ql44$b9J5(Q|paaf5E$`FYC5Y<+QUTU(RWB{fiVMU;wXFz=djV;bPOoSr6|F>hw$1@oxOIhkyCm(pY9abrrJ|<($fCq?O5qR z@e5<)WW<1Lc)9U%_TqjJC$rMcQLn9($-I4Odx^;Hphl!E`HzElc2a>p;lr*@qdsUm zr1PbSjCGkNd-tl#QS@w)`MPKGNcYw3Z=&s9BLi2h!iWI^oJu4y!Ax$&=6^{sjh zUGDkDwt9cDCol~HugQOO*7DZ0?&xksiVhpEw-ijm^*qioQFU{r%uz<4Jx8veZtA$k zy3wo6g44{T(}YDv;>{L#!ogZ@?RD;y+k_$ufmNHtXZJ*&j8#1hm@=-}2{k5PeHJIU zq_3e5{MGg&S7}Qnoy|62W6PM*q%yX$q_X)czPu@>hlamYRNZTuvc|l0z-(7)l+lLq zSG7opGy~Gk+?(T1Q*jM>0qk{CxF`PA3FILo7V_{pWk2AXrl%F@(Sw%; zqudYQ8$}*u3Y+rcU+8ivbt0kb`f>@u!?{=GtHtIJ<^%0XV(ZDn!_b8hf?#RN(<{jp z?y`gbs)d?^MZ<>b7$OgN=K7Z&mQTwoG^(A>?dg_cNb)yU{f#D$)r|?{t!0YB$u0?g zUStF9;!zMY({BEnVap7CZ0(?9xGjBCB)jX}DqcD3ap$=i}XtO8@ zAQprg^YeJ9&(*?wZ)Id!!AXGcDz*`-C&iY_d!Lr9nlu5myr!Y=o7ZlOHBcKj zl9*dPx#jS4Wi8?RGx~*LT^hp^-h)n3AK=cpgH-W)=w?K0_wl!5gmctvebgo`NzQYB zmlSbun_Q&C1QXY#fXt3UXVy*J?LfXx@b3Q9&e^-8KNeQ4PK?jCznA71tHfdY=?F*$ z2|smwcicx5T2Ig38fDH!&XU1`WRCQnNcr(h488vCXn#qrJ0j)<`r{I@p)8J_A$d_ zFh{40v39+JtrFP38tOM*AFn|_Y#BcGPPJEeks5Y;_zP#OkU*RL8pdVDn{y`+@5=}V z(($=RqXTQGpsANOko!Y>bA@M25jI|omAtDOzuW8KT^LGTOD~oR8}zIA*jJ>tRGRTa zw*qc$PbyKT(ZDEzrdf;EsDM3UR%1{v{#X{_$SNPJ=29I|`JKX{G=Gp^{R|fSM!RL; z%h?;H8Y%7SxPE=MR1Rv>evf+mT&w$W}fp;KR5_-k_D zp21HysXw>0SiP(K(YrTqGCjD4GO?P?QA3(~H?j81^&zN|YPW6)fAFr_K+cN@SkE*P zLVhv|Y%@}(ea?PT#_xDQj(J_lW8Gr)b5J|$oBb>)fP*=PVM!H?vCGcIT=oZ#~ zJJIOEK&HKcuC%tuQyHD1ru5{J$y(kK@BNm8!>-x&pqI3HEQ6KkE3X9ysTygr>`xwe zKkU{SS*RZ6OcVO(V|g*-c~&?3N!Ub>?3#1y02er(7%Nkh$=m zGGy+o%!Tn&bYD@MknC`vJYb!Z2%i^!r20hA!2llu-Vc$hdd+QU@9u5$9v^TFOaU!L zfql}f-W|lWquN~44LRYqiZ7_kBj*c6+nux$~PmbszD9-x|}uoYLOH z%|E6Pqbn{Fs<%qB+8LK30$&R~OTPKT(y~f^n*<|OcQg9#-PX}y*#1?(bO=n{*7hLu zhwWVfE=tkgo6~oQc{3Wd8#oK!H@=^AQYqQ?I~Uo;^y_L$=~Z$Y%s@-G_u9`SKdO{O zMJFw%6e?T0QCn+{6V3pI>_eE?%n)G2YmVnnlbxjLZQ#J@94@EicHf$Pe!bNPC$0An zIkG}i?Z~VOd#GNu%uAuCmPKi*XZ!jt&HF06M?c5~BAJM!Q3%vEP) z^%-%KqSAi`L?hRG$uk_DsLV@9X0v5U|GD33Pz5Iolj5=Y1w*}!jC1HI2t(+&qcW;w;Z=6--;swADCp^op( zAmS&d(6dto3+QfIYQIY#Ez_%bK8B)uKf@gikmTPwcy6?x?md?|L>Sb4)ZgfP0e+u)8Pdzf~@7eQh1+^ZSa`1kzl%cvNF5+QH}TbF|jzk-(c{r4?1X zdES00+Llb?E|;;aNJnqQEyJipcG-qU8swFrmiF4SUt;k#uO%KUkC2Gfn{qyjksI0= z{AQKI_yyyLWXqj1_QXzLY-j0eKDS6>={orMPcPV;DjBZ!+R7G9{9o&9i$@lMZsU9&g?&fJ)z z?-Q|X{s>EulS!hb9Jr*0gC&-n`^;VRTQGk^>ja_8K@viak2OSr-rUA_bRc1ui)E{o zbm1;Un%=6Zf^1-gJv=V1aD-!d>kVnhOM0jdn=C!lrZRCbAWQ*0dkd^^gp4MC?2mpW zk9=z(yUz0FJv@)b>FMYIJ%6Ud#=(}G7yXGHAz0&x>TO&p?JcktJ0Vy*kJ?EihssGO zNWRw6%c$1vSkBAuNEIPZ)59XWS@GsQPx)90wYoeleUOc>DV0-TkU}koo7A_WIhNY__`asKr2}{m8SZ= zIVjS196S2iU4d6c+N=jF)~`^tH>6t`IZzIfD)HaMj!%3VsnB=piqH2;`1YyDQ9r|J(@`t^sm~GU-i$|Lqe6qJ=G0av zTz;czf*5|+jr0^dF3%{*&kHDHuJ+nn(+c_%9-=YTeUs=A^G3*n9C}85>~FJMpsuOVyWure@?rX+j9qFRzd6s3i0F#w z>3m}m`m%~5sbLji>|u`NU+1R@P4KKT(-g{&CIY@{ z^vF;(fiwUW2gmed-!|d4KjGrx3;>w+Q8}f zE<|bo9{^d`j= zcxhARjx3I6{`jgKo~t%wds0Y`Na$j1>-lL?@md!Vw4Ec6@HCzY<^*gYo;IQU-lo7{x^v!4FgWZhfqakB7K6{aEMVFlyh>jh5Va**~Q7 zFEiCcqwTZtn2Y>?sfc@mYQlSNilEcSL(Je)(%}rCq<%GEy=xzJ`+Dn;G~q*#?FAhr z0d48k`Kdz>BvL<=5Y+cnE@mU7oAOEqxW!SH_w_k%B0a{(Gne5cL8*9s1 zDZ$qb$<>#EI(tE2aqKSOntZ6D%dV7JxNa5ZevcWOO_ZB|00;@gM5|uXZ@7tj7oOl< z#~3MgqO2xN$6#@cnl33umK}~yo+~xJ?2!7SKl#m>GY4BXMQp4y15S|`lRH$4qkZcB zPD(1^<7Xz4AJ~DtH0`&5wHK4WMS}{K)?EN?q03<(Z>=q%ZE9& zigVbAr6!7jWf3Tw|2H!)VRp*3%XIUN6oEt?sJX8~UPz`sR+Mojrv21*$)^0l2$?s%zU!$sR(oFyv$-Ar%%oTr~{$~*!qj{d3+{XQH%Zl!iF|Glu2J|B7%1U9!a|S%4A$bP<<>v;Z%Nd{>tFb zYA%rEp0RG`h^#0%q5>@Hb0!LTCGZpMFqsf9je3wAd1Do^7NrfAjSLa^c8^4rG#I_^ zmgyyaVfe>xg#;?uA2%EsG5+IvS+LB+F%Wfc^7#(Qx|?C~#rl}S;QcjWH!( Date: Fri, 28 Nov 2025 13:12:28 +0700 Subject: [PATCH 7/8] [l4d2_smoker_drag_damage_interval] Fix the incorrect code for the first damage. --- .../l4d2_smoker_drag_damage_interval.smx | Bin 5402 -> 5377 bytes .../l4d2_smoker_drag_damage_interval.sp | 52 +++++++++--------- 2 files changed, 27 insertions(+), 25 deletions(-) diff --git a/addons/sourcemod/plugins/optional/l4d2_smoker_drag_damage_interval.smx b/addons/sourcemod/plugins/optional/l4d2_smoker_drag_damage_interval.smx index 83e2bea8476ac2fdb142cf94b8474693a219fac7..3657964b999f1a4cd4f9c9d5da2947f158577f8e 100644 GIT binary patch delta 5221 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 delta 5246 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"); if (iTongueDragDamageTimer == -1) { - SetFailState("Failed to get offset 'CTerrorPlayer::m_tongueDragDamageTimer'."); + SetFailState("Failed to get offset 'CTerrorPlayer->m_tongueDragDamageTimer'."); } g_iTongueDragDamageTimerDurationOffset = iTongueDragDamageTimer + CT_DURATION_OFFSET; @@ -123,6 +136,9 @@ void Event_OnTongueGrab(Event hEvent, const char[] eName, bool bDontBroadcast) SetDragDamageTimer(iVictim, GetFirstDamageInterval()); } + g_iTongueHitCount[iVictim][eUserId] = hEvent.GetInt("victim"); + g_iTongueHitCount[iVictim][eHitCount] = 0; + #if DEBUG g_fDebugDamageInterval = GetGameTime(); #endif @@ -131,6 +147,7 @@ void Event_OnTongueGrab(Event hEvent, const char[] eName, bool bDontBroadcast) Action Hook_OnTakeDamage(int iVictim, int &iAttacker, int &iInflictor, float &fDamage, int &iDamageType) { // Replacing the function patch 'CTerrorPlayer::UpdateHangingFromTongue'. + // This dmg function is called after variable 'CTerrorPlayer::m_tongueDragDamageTimer' is set, we can't get it here. if (!(iDamageType & DMG_CHOKE)) { return Plugin_Continue; } @@ -141,8 +158,7 @@ Action Hook_OnTakeDamage(int iVictim, int &iAttacker, int &iInflictor, float &fD } // Stop dragging. - bool bIsHangingFromTongue = (GetEntProp(iVictim, Prop_Send, "m_isHangingFromTongue", 1) > 0); - if (bIsHangingFromTongue) { + if (GetEntProp(iVictim, Prop_Send, "m_isHangingFromTongue", 1) > 0) { return Plugin_Continue; } @@ -150,14 +166,12 @@ Action Hook_OnTakeDamage(int iVictim, int &iAttacker, int &iInflictor, float &fD SetDragDamageTimer(iVictim, g_hTongueDragDamageInterval.FloatValue); // First damage if cvar enabled. + g_iTongueHitCount[iVictim][eHitCount]++; bool bFirstDamage = false; - float fTongueDragFirstDamage = g_hTongueDragFirstDamage.FloatValue; - if (fTongueDragFirstDamage > 0.0) { - float fTongueVictimTimer = GetEntPropFloat(iVictim, Prop_Send, "m_tongueVictimTimer", IT_TIMESTAMP_INDEX); - - if (FloatCompareEps(fTongueVictimTimer, GetFirstDamageInterval()) == 0) { - fDamage = fTongueDragFirstDamage; + if (g_hTongueDragFirstDamage.FloatValue > 0.0) { + if (g_iTongueHitCount[iVictim][eHitCount] == 1 && g_iTongueHitCount[iVictim][eUserId] == GetClientUserId(iVictim)) { + fDamage = g_hTongueDragFirstDamage.FloatValue; bFirstDamage = true; } } @@ -188,18 +202,6 @@ void SetDragDamageTimer(int iClient, float fDuration) SetEntDataFloat(iClient, g_iTongueDragDamageTimerTimeStampOffset, fTimeStamp, false); // 'CountdownTimer::timestamp' } -// For small differences in tick interval. -int FloatCompareEps(float fOne, float fTwo, float fEps = EPSILON) -{ - if (FloatAbs(fOne - fTwo) < fEps) { - return 0; - } else if (fOne > fTwo) { - return 1; - } - - return -1; -} - #if DEBUG void DebugPrint(int iVictim, float fDamage, bool bFirstDamage) { From fc1b04c37eac284dfceb78b35ec91d043dbbbf88 Mon Sep 17 00:00:00 2001 From: A1mDev <33463136+A1mDev@users.noreply.github.com> Date: Fri, 28 Nov 2025 13:15:19 +0700 Subject: [PATCH 8/8] [l4d2_smoker_drag_damage_interval] Edit comment --- addons/sourcemod/scripting/l4d2_smoker_drag_damage_interval.sp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/sourcemod/scripting/l4d2_smoker_drag_damage_interval.sp b/addons/sourcemod/scripting/l4d2_smoker_drag_damage_interval.sp index 075fab510..3d29db1f6 100644 --- a/addons/sourcemod/scripting/l4d2_smoker_drag_damage_interval.sp +++ b/addons/sourcemod/scripting/l4d2_smoker_drag_damage_interval.sp @@ -7,7 +7,7 @@ * - l4d2_smoker_drag_damage_interval. * * 2. Removed untrusted timer-based code: - * - Replaced with safer, hook-based implementation using OnTakeDamage and netprops. + * - Replaced with safer, hook-based implementation using OnTakeDamage. * - Ensures more stable and reliable drag damage behavior without unnecessary timers. * * Notes: