1+ #pragma semicolon 1
2+ #pragma newdecls required
3+
14#include <sourcemod>
25#include <sdktools>
6+ #include <l4d2util_constants>
7+ //#define LEFT4FRAMEWORK_GAMEDATA_ONLY 1
8+ //#include <left4framework>
39
410#define DEBUG 0
511
6- #define L4DBUILD 1
12+ #define NEW_METHOD_GET_MAX_SPEED 1 // 0 - old
713
814#define LEFT4FRAMEWORK_GAMEDATA " left4dhooks.l4d2" // left4dhooks
9- #define SECTION_NAME " CTerrorPlayer::GetRunTopSpeed" // left4dhooks
15+ #define SECTION_NAME " CTerrorPlayer::GetRunTopSpeed"
16+
17+ int
18+ g_iCvarSIExcept = 0 ;
19+
20+ bool
21+ g_bCvarEnable = false ,
22+ g_bCvarSurvivorExcept = false ;
23+
24+ float
25+ g_fLeftGroundMaxSpeed [MAXPLAYERS + 1 ] = {0.0 , ...};
26+
27+ ConVar
28+ g_hCvarEnable = null ,
29+ g_hCvarSIExcept = null ,
30+ g_hCvarSurvivorExcept = null ;
1031
11- //#define LEFT4FRAMEWORK_GAMEDATA "left4downtown.l4d2" // left4downtown
12- //#define SECTION_NAME "CTerrorPlayer_GetRunTopSpeed" // left4downtown
32+ #if NEW_METHOD_GET_MAX_SPEED
33+ Handle
34+ g_hGetRunTopSpeed = null ;
35+ #endif
1336
14- public Plugin : myinfo =
37+ public Plugin myinfo =
1538{
1639 name = " Simple Anti-Bunnyhop" ,
17- author = " CanadaRox, ProdigySim, blodia, CircleSquared, robex" ,
40+ author = " CanadaRox, ProdigySim, blodia, CircleSquared, robex, A1m` " ,
1841 description = " Stops bunnyhops by restricting speed when a player lands on the ground to their MaxSpeed" ,
19- version = " 0.3 " ,
42+ version = " 0.5.1 " ,
2043 url = " https://github.com/SirPlease/L4D2-Competitive-Rework"
2144};
2245
23- new Handle :hCvarEnable ;
24- #if defined (L4DBUILD )
25- new Handle :hCvarSIExcept ;
26- new Handle :hCvarSurvivorExcept ;
27- new Handle :g_hGetRunTopSpeed ;
28- #endif
29-
30- public OnPluginStart ()
46+ public void OnPluginStart ()
3147{
48+ #if NEW_METHOD_GET_MAX_SPEED
3249 LoadSDK ();
33- hCvarEnable = CreateConVar (" simple_antibhop_enable" , " 1" , " Enable or disable the Simple Anti-Bhop plugin" );
34- #if defined (L4DBUILD )
35- hCvarSIExcept = CreateConVar (" bhop_except_si_flags" , " 0" , " Bitfield for exempting SI in anti-bhop functionality. From least significant: Smoker, Boomer, Hunter, Spitter, Jockey, Charger, Tank" );
36- hCvarSurvivorExcept = CreateConVar (" bhop_allow_survivor" , " 0" , " Allow Survivors to bhop while plugin is enabled" );
3750#endif
51+
52+ g_hCvarEnable = CreateConVar ( \
53+ " simple_antibhop_enable" , \
54+ " 1" , \
55+ " Enable or disable the Simple Anti-Bhop plugin (0 - disable, 1 - enable)." , \
56+ _ , true , 0.0 , true , 1.0 \
57+ );
58+
59+ // 1 - smoker, 2 - boomer, 4 - hunter, 8 - spitter, 16 - jockey, 32 - charger, 64 - tank
60+ // 1 + 2 + 4 + 8 + 16 + 32 + 64 = 127 full flag
61+ // Exmaple 1: 1 + 4 = 5 - allow bhop for smoker and hunter.
62+ // Exmaple 2: 1 + 2 + 4 + 8 + 16 + 32 = 63 - allow bhop for all classes, except for the tank.
63+ g_hCvarSIExcept = CreateConVar ( \
64+ " bhop_except_si_flags" , \
65+ " 0" , \
66+ " Bitfield for exempting SI in anti-bhop functionality. \
67+ 1 - smoker, 2 - boomer, 4 - hunter, 8 - spitter, 16 - jockey, 32 - charger, 64 - tank." , \
68+ _ , true , 0.0 , true , 127.0 \
69+ );
70+
71+ g_hCvarSurvivorExcept = CreateConVar ( \
72+ " bhop_allow_survivor" , \
73+ " 0" , \
74+ " Allow Survivors to bhop while plugin is enabled (1 - allow, 0 - block)." , \
75+ _ , true , 0.0 , true , 1.0 \
76+ );
77+
78+ CvarsToType ();
79+
80+ g_hCvarEnable .AddChangeHook (Cvars_Changed );
81+ g_hCvarSIExcept .AddChangeHook (Cvars_Changed );
82+ g_hCvarSurvivorExcept .AddChangeHook (Cvars_Changed );
83+
84+ RegConsoleCmd (" sm_check_bhop" , Cmd_CheckBhop );
3885}
3986
87+ #if NEW_METHOD_GET_MAX_SPEED
4088void LoadSDK ()
4189{
4290 Handle hGameData = LoadGameConfigFile (LEFT4FRAMEWORK_GAMEDATA );
@@ -48,78 +96,116 @@ void LoadSDK()
4896 if (! PrepSDKCall_SetFromConf (hGameData , SDKConf_Signature , SECTION_NAME )) {
4997 SetFailState (" Function '%s ' not found" , SECTION_NAME );
5098 }
51-
99+
52100 PrepSDKCall_SetReturnInfo (SDKType_Float , SDKPass_Plain );
53-
101+
54102 g_hGetRunTopSpeed = EndPrepSDKCall ();
55103 if (g_hGetRunTopSpeed == null ) {
56104 SetFailState (" Function '%s ' found, but something went wrong" , SECTION_NAME );
57105 }
58-
106+
59107 delete hGameData ;
60108}
109+ #endif
61110
62- public Action : OnPlayerRunCmd ( client , & buttons , & impulse , Float : vel [ 3 ], Float : angles [ 3 ], & weapon )
111+ void CvarsToType ( )
63112{
64- if (! IsValidClient (client ) || IsFakeClient (client ))
65- return Plugin_Continue ;
113+ g_bCvarEnable = g_hCvarEnable .BoolValue ;
114+ g_iCvarSIExcept = g_hCvarSIExcept .IntValue ;
115+ g_bCvarSurvivorExcept = g_hCvarSurvivorExcept .BoolValue ;
116+ }
117+
118+ void Cvars_Changed (ConVar hConVar , const char [] sOldValue , const char [] sNewValue )
119+ {
120+ CvarsToType ();
121+ }
122+
123+ Action Cmd_CheckBhop (int iClient , int iArgs )
124+ {
125+ if (! g_bCvarEnable ) {
126+ ReplyToCommand (iClient , " Bunnyhop is allowed for everyone!" );
127+ return Plugin_Handled ;
128+ }
129+
130+ ReplyToCommand (iClient , " Bunnyhop is %s for survivors!" , (g_bCvarSurvivorExcept ) ? " allowed" : " blocked" );
131+
132+ for (int i = view_as <int >(L4D2Infected_Smoker ); i <= view_as <int >(L4D2Infected_Tank ); i ++ ) {
133+ if (i == view_as <int >(L4D2Infected_Witch )) {
134+ continue ;
135+ }
66136
67- static Float :LeftGroundMaxSpeed [MAXPLAYERS + 1 ];
137+ ReplyToCommand (iClient , " [Infected] Bunnyhop is %s for %s zombie class!" , (IsAllowBhopZClass (i )) ? " allowed" : " blocked" , L4D2_InfectedNames [i ]);
138+ }
139+
140+ return Plugin_Handled ;
141+ }
68142
69- if (! GetConVarBool (hCvarEnable ))
143+ public Action OnPlayerRunCmd (int iClient , int & iButtons , int & iImpulse , float fVel [3 ], float fAngles [3 ], \
144+ int & iWeapon , int & iSubtype , int & iCmdNum , int & iTickcount , int & iSeed , int iMouse [2 ])
145+ {
146+ if (! g_bCvarEnable || IsFakeClient (iClient ) || ! IsPlayerAlive (iClient )) {
70147 return Plugin_Continue ;
148+ }
71149
72- if (IsPlayerAlive (client )) {
73- #if defined (L4DBUILD )
74- if (GetClientTeam (client ) == 3 ) {
75- new class = GetEntProp (client , Prop_Send , " m_zombieClass" );
76- // tank
77- if (class == 8 ) {
78- -- class ;
79- }
80- class -- ;
81- new except = GetConVarInt (hCvarSIExcept );
82- if (class >= 0 && class <= 6 && ((1 << class ) & except )) {
83- // Skipping calculation for This SI based on exception rules
150+ switch (GetClientTeam (iClient )) {
151+ case L4D2Team_Survivor : {
152+ if (g_bCvarSurvivorExcept ) {
84153 return Plugin_Continue ;
85154 }
86155 }
87- if (GetClientTeam (client ) == 2 ) {
88- if (GetConVarBool (hCvarSurvivorExcept )) {
156+ case L4D2Team_Infected : {
157+ int iZClass = GetEntProp (iClient , Prop_Send , " m_zombieClass" );
158+ if (IsAllowBhopZClass (iZClass )) {
89159 return Plugin_Continue ;
90160 }
91161 }
92- #endif
162+ default : {
163+ return Plugin_Continue ;
164+ }
165+ }
93166
94- new ClientFlags = GetEntityFlags (client );
95- if (ClientFlags & FL_ONGROUND ) {
96- if (LeftGroundMaxSpeed [client ] != - 1.0 ) {
167+ if (GetEntityFlags (iClient ) & FL_ONGROUND ) {
168+ if (g_fLeftGroundMaxSpeed [iClient ] != - 1.0 ) {
169+ float CurVelVec [3 ];
170+ GetEntPropVector (iClient , Prop_Data , " m_vecAbsVelocity" , CurVelVec );
97171
98- new Float :CurVelVec [3 ];
99- GetEntPropVector (client , Prop_Data , " m_vecAbsVelocity" , CurVelVec );
172+ if (GetVectorLength (CurVelVec ) > g_fLeftGroundMaxSpeed [iClient ]) {
173+ #if DEBUG
174+ PrintToChat (iClient , " Speed: %f {%.02f , %.02f , %.02f }, MaxSpeed: %f " , \
175+ GetVectorLength (CurVelVec ), CurVelVec [0 ], CurVelVec [1 ], CurVelVec [2 ], g_fLeftGroundMaxSpeed [iClient ]);
176+ #endif
100177
101- if (GetVectorLength (CurVelVec ) > LeftGroundMaxSpeed [client ]) {
102- #if DEBUG
103- PrintToChat (client , " Speed: %f {%.02f , %.02f , %.02f }, MaxSpeed: %f " , GetVectorLength (CurVelVec ), CurVelVec [0 ], CurVelVec [1 ], CurVelVec [2 ], LeftGroundMaxSpeed [client ]);
104- #endif
105- NormalizeVector (CurVelVec , CurVelVec );
106- ScaleVector (CurVelVec , LeftGroundMaxSpeed [client ]);
107- TeleportEntity (client , NULL_VECTOR , NULL_VECTOR , CurVelVec );
108- }
109- LeftGroundMaxSpeed [client ] = - 1.0 ;
178+ NormalizeVector (CurVelVec , CurVelVec );
179+ ScaleVector (CurVelVec , g_fLeftGroundMaxSpeed [iClient ]);
180+ TeleportEntity (iClient , NULL_VECTOR , NULL_VECTOR , CurVelVec );
110181 }
111- } else if (LeftGroundMaxSpeed [client ] == - 1.0 ) {
112- LeftGroundMaxSpeed [client ] = SDKCall (g_hGetRunTopSpeed , client );
182+ g_fLeftGroundMaxSpeed [iClient ] = - 1.0 ;
113183 }
184+ } else if (g_fLeftGroundMaxSpeed [iClient ] == - 1.0 ) {
185+ #if NEW_METHOD_GET_MAX_SPEED
186+ g_fLeftGroundMaxSpeed [iClient ] = SDKCall (g_hGetRunTopSpeed , iClient );
187+ #else
188+ g_fLeftGroundMaxSpeed [iClient ] = GetEntPropFloat (iClient , Prop_Data , " m_flMaxspeed" );
189+ #endif
114190 }
115191
116192 return Plugin_Continue ;
117- }
193+ }
118194
119- stock bool : IsValidClient ( client )
120- {
121- if (client <= 0 || client > MaxClients || ! IsClientConnected ( client )) {
122- return false ;
195+ bool IsAllowBhopZClass ( int iZClass )
196+ {
197+ if (iZClass == view_as < int >( L4D2Infected_Tank )) {
198+ iZClass -- ;
123199 }
124- return IsClientInGame (client );
200+ iZClass -- ;
201+
202+ if (iZClass >= view_as <int >(L4D2Infected_Common )
203+ && iZClass <= view_as <int >(L4D2Infected_Charger )
204+ && ((1 << iZClass ) & g_iCvarSIExcept )
205+ ) {
206+ // Skipping calculation for This SI based on exception rules
207+ return true ;
208+ }
209+
210+ return false ;
125211}
0 commit comments