11/* *
2- * Version 2.2 by A1m`
2+ * Version 2.3 by A1m`
33 *
44 * Changes:
55 * 1. Removed duplicate plugins:
1010 * - Replaced with safer, hook-based implementation using OnTakeDamage and netprops.
1111 * - Ensures more stable and reliable drag damage behavior without unnecessary timers.
1212 *
13- * 3. Supports late loading.
13+ * Notes:
14+ * The timing of damage is not perfect, as 'CTerrorPlayer::PostThink' is not called every frame,
15+ * but after a certain period of time depending on the number of players and the tick rate (see function 'CTerrorPlayer::ShouldPostThink').
16+ * For this reason, it is difficult to calculate using game netprops whether this was the first damage dealt or not,
17+ * the code becomes too complicated, so we introduce our own variables to determine this.
1418**/
1519
1620#pragma semicolon 1
3943 g_fDebugDamageInterval = 0.0 ;
4044#endif
4145
46+ enum
47+ {
48+ eUserId = 0 ,
49+ eHitCount ,
50+
51+ eDamageInfo_Size
52+ };
53+
4254int
55+ g_iTongueHitCount [MAXPLAYERS + 1 ][eDamageInfo_Size ],
4356 g_iTongueDragDamageTimerDurationOffset = - 1 ,
4457 g_iTongueDragDamageTimerTimeStampOffset = - 1 ;
4558
@@ -53,7 +66,7 @@ public Plugin myinfo =
5366 name = " L4D2 smoker drag damage interval" ,
5467 author = " Visor, Sir, A1m`" ,
5568 description = " Implements a native-like cvar that should've been there out of the box" ,
56- version = " 2.2 " ,
69+ version = " 2.3 " ,
5770 url = " https://github.com/SirPlease/L4D2-Competitive-Rework"
5871};
5972
@@ -83,9 +96,9 @@ void InitGameData()
8396 SetFailState (" Gamedata '%s .txt' missing or corrupt." , GAMEDATA );
8497 }
8598
86- int iTongueDragDamageTimer = GameConfGetOffset (hGamedata , " CTerrorPlayer:: m_tongueDragDamageTimer" );
99+ int iTongueDragDamageTimer = GameConfGetOffset (hGamedata , " CTerrorPlayer-> m_tongueDragDamageTimer" );
87100 if (iTongueDragDamageTimer == - 1 ) {
88- SetFailState (" Failed to get offset 'CTerrorPlayer:: m_tongueDragDamageTimer'." );
101+ SetFailState (" Failed to get offset 'CTerrorPlayer-> m_tongueDragDamageTimer'." );
89102 }
90103
91104 g_iTongueDragDamageTimerDurationOffset = iTongueDragDamageTimer + CT_DURATION_OFFSET ;
@@ -123,6 +136,9 @@ void Event_OnTongueGrab(Event hEvent, const char[] eName, bool bDontBroadcast)
123136 SetDragDamageTimer (iVictim , GetFirstDamageInterval ());
124137 }
125138
139+ g_iTongueHitCount [iVictim ][eUserId ] = hEvent .GetInt (" victim" );
140+ g_iTongueHitCount [iVictim ][eHitCount ] = 0 ;
141+
126142#if DEBUG
127143 g_fDebugDamageInterval = GetGameTime ();
128144#endif
@@ -131,6 +147,7 @@ void Event_OnTongueGrab(Event hEvent, const char[] eName, bool bDontBroadcast)
131147Action Hook_OnTakeDamage (int iVictim , int &iAttacker , int &iInflictor , float &fDamage , int &iDamageType )
132148{
133149 // Replacing the function patch 'CTerrorPlayer::UpdateHangingFromTongue'.
150+ // This dmg function is called after variable 'CTerrorPlayer::m_tongueDragDamageTimer' is set, we can't get it here.
134151 if (! (iDamageType & DMG_CHOKE )) {
135152 return Plugin_Continue ;
136153 }
@@ -141,23 +158,20 @@ Action Hook_OnTakeDamage(int iVictim, int &iAttacker, int &iInflictor, float &fD
141158 }
142159
143160 // Stop dragging.
144- bool bIsHangingFromTongue = (GetEntProp (iVictim , Prop_Send , " m_isHangingFromTongue" , 1 ) > 0 );
145- if (bIsHangingFromTongue ) {
161+ if (GetEntProp (iVictim , Prop_Send , " m_isHangingFromTongue" , 1 ) > 0 ) {
146162 return Plugin_Continue ;
147163 }
148164
149165 // Fix damage interval.
150166 SetDragDamageTimer (iVictim , g_hTongueDragDamageInterval .FloatValue );
151167
152168 // First damage if cvar enabled.
169+ g_iTongueHitCount [iVictim ][eHitCount ]++ ;
153170 bool bFirstDamage = false ;
154- float fTongueDragFirstDamage = g_hTongueDragFirstDamage .FloatValue ;
155171
156- if (fTongueDragFirstDamage > 0.0 ) {
157- float fTongueVictimTimer = GetEntPropFloat (iVictim , Prop_Send , " m_tongueVictimTimer" , IT_TIMESTAMP_INDEX );
158-
159- if (FloatCompareEps (fTongueVictimTimer , GetFirstDamageInterval ()) == 0 ) {
160- fDamage = fTongueDragFirstDamage ;
172+ if (g_hTongueDragFirstDamage .FloatValue > 0.0 ) {
173+ if (g_iTongueHitCount [iVictim ][eHitCount ] == 1 && g_iTongueHitCount [iVictim ][eUserId ] == GetClientUserId (iVictim )) {
174+ fDamage = g_hTongueDragFirstDamage .FloatValue ;
161175 bFirstDamage = true ;
162176 }
163177 }
@@ -188,18 +202,6 @@ void SetDragDamageTimer(int iClient, float fDuration)
188202 SetEntDataFloat (iClient , g_iTongueDragDamageTimerTimeStampOffset , fTimeStamp , false ); // 'CountdownTimer::timestamp'
189203}
190204
191- // For small differences in tick interval.
192- int FloatCompareEps (float fOne , float fTwo , float fEps = EPSILON )
193- {
194- if (FloatAbs (fOne - fTwo ) < fEps ) {
195- return 0 ;
196- } else if (fOne > fTwo ) {
197- return 1 ;
198- }
199-
200- return - 1 ;
201- }
202-
203205#if DEBUG
204206void DebugPrint (int iVictim , float fDamage , bool bFirstDamage )
205207{
0 commit comments