· 8 years ago · Feb 08, 2018, 03:46 PM
1/*
2 * shavit's Timer - Core
3 * by: shavit
4 *
5 * This file is part of shavit's Timer.
6 *
7 * This program is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License, version 3.0, as published by the
9 * Free Software Foundation.
10 *
11 * This program is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
13 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
14 * details.
15 *
16 * You should have received a copy of the GNU General Public License along with
17 * this program. If not, see <http://www.gnu.org/licenses/>.
18 *
19*/
20
21#include <sourcemod>
22#include <sdkhooks>
23#include <sdktools>
24#include <geoip>
25#include <clientprefs>
26
27#undef REQUIRE_PLUGIN
28#include <adminmenu>
29#include <bhopstats>
30
31#define USES_CHAT_COLORS
32#include <shavit>
33
34#pragma newdecls required
35#pragma semicolon 1
36
37// #define DEBUG
38
39// game type (CS:S/CS:GO/TF2)
40ServerGame gSG_Type = Game_Unknown; // deperecated and here for backwards compatibility
41EngineVersion gEV_Type = Engine_Unknown;
42
43// database handle
44Database gH_SQL = null;
45bool gB_MySQL = false;
46
47// forwards
48Handle gH_Forwards_Start = null;
49Handle gH_Forwards_Stop = null;
50Handle gH_Forwards_FinishPre = null;
51Handle gH_Forwards_Finish = null;
52Handle gH_Forwards_OnRestart = null;
53Handle gH_Forwards_OnEnd = null;
54Handle gH_Forwards_OnPause = null;
55Handle gH_Forwards_OnResume = null;
56Handle gH_Forwards_OnStyleChanged = null;
57Handle gH_Forwards_OnStyleConfigLoaded = null;
58Handle gH_Forwards_OnDatabaseLoaded = null;
59Handle gH_Forwards_OnChatConfigLoaded = null;
60Handle gH_Forwards_OnUserCmdPre = null;
61Handle gH_Forwards_OnTimerIncrement = null;
62Handle gH_Forwards_OnTimerIncrementPost = null;
63
64// timer variables
65bool gB_TimerEnabled[MAXPLAYERS+1];
66float gF_PlayerTimer[MAXPLAYERS+1];
67float gF_PausePosition[MAXPLAYERS+1][3][3];
68bool gB_ClientPaused[MAXPLAYERS+1];
69int gI_Jumps[MAXPLAYERS+1];
70int gBS_Style[MAXPLAYERS+1];
71bool gB_Auto[MAXPLAYERS+1];
72int gI_ButtonCache[MAXPLAYERS+1];
73int gI_Strafes[MAXPLAYERS+1];
74float gF_AngleCache[MAXPLAYERS+1];
75int gI_TotalMeasures[MAXPLAYERS+1];
76int gI_GoodGains[MAXPLAYERS+1];
77bool gB_DoubleSteps[MAXPLAYERS+1];
78float gF_StrafeWarning[MAXPLAYERS+1];
79bool gB_PracticeMode[MAXPLAYERS+1];
80int gI_SHSW_FirstCombination[MAXPLAYERS+1];
81int gI_Track[MAXPLAYERS+1];
82
83StringMap gSM_StyleCommands = null;
84
85// cookies
86Handle gH_StyleCookie = null;
87Handle gH_AutoBhopCookie = null;
88
89// late load
90bool gB_Late = false;
91
92// modules
93bool gB_Zones = false;
94bool gB_WR = false;
95
96// cvars
97ConVar gCV_Autobhop = null;
98ConVar gCV_LeftRight = null;
99ConVar gCV_Restart = null;
100ConVar gCV_Pause = null;
101ConVar gCV_NoStaminaReset = null;
102ConVar gCV_AllowTimerWithoutZone = null;
103ConVar gCV_BlockPreJump = null;
104ConVar gCV_NoZAxisSpeed = null;
105ConVar gCV_VelocityTeleport = null;
106
107// cached cvars
108bool gB_Autobhop = true;
109bool gB_LeftRight = true;
110bool gB_Restart = true;
111bool gB_Pause = true;
112bool gB_NoStaminaReset = true;
113bool gB_AllowTimerWithoutZone = false;
114bool gB_BlockPreJump = false;
115bool gB_NoZAxisSpeed = true;
116bool gB_VelocityTeleport = false;
117
118// table prefix
119char gS_MySQLPrefix[32];
120
121// server side
122ConVar sv_airaccelerate = null;
123ConVar sv_autobunnyhopping = null;
124ConVar sv_enablebunnyhopping = null;
125
126// timer settings
127bool gB_Registered = false;
128int gI_Styles = 0;
129char gS_StyleStrings[STYLE_LIMIT][STYLESTRINGS_SIZE][128];
130any gA_StyleSettings[STYLE_LIMIT][STYLESETTINGS_SIZE];
131
132// chat settings
133char gS_ChatStrings[CHATSETTINGS_SIZE][128];
134
135// misc cache
136bool gB_StopChatSound = false;
137bool gB_HookedJump = false;
138
139// kz support
140bool gB_KZMap = false;
141
142public Plugin myinfo =
143{
144 name = "[shavit] Core",
145 author = "shavit",
146 description = "The core for shavit's bhop timer.",
147 version = SHAVIT_VERSION,
148 url = "https://github.com/shavitush/bhoptimer"
149}
150
151public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max)
152{
153 CreateNative("Shavit_FinishMap", Native_FinishMap);
154 CreateNative("Shavit_GetBhopStyle", Native_GetBhopStyle);
155 CreateNative("Shavit_GetChatStrings", Native_GetChatStrings);
156 CreateNative("Shavit_GetClientJumps", Native_GetClientJumps);
157 CreateNative("Shavit_GetClientTime", Native_GetClientTime);
158 CreateNative("Shavit_GetClientTrack", Native_GetClientTrack);
159 CreateNative("Shavit_GetDatabase", Native_GetDatabase);
160 CreateNative("Shavit_GetDB", Native_GetDB);
161 CreateNative("Shavit_GetGameType", Native_GetGameType);
162 CreateNative("Shavit_GetStrafeCount", Native_GetStrafeCount);
163 CreateNative("Shavit_GetStyleCount", Native_GetStyleCount);
164 CreateNative("Shavit_GetStyleSettings", Native_GetStyleSettings);
165 CreateNative("Shavit_GetStyleStrings", Native_GetStyleStrings);
166 CreateNative("Shavit_GetSync", Native_GetSync);
167 CreateNative("Shavit_GetTimer", Native_GetTimer);
168 CreateNative("Shavit_GetTimerStatus", Native_GetTimerStatus);
169 CreateNative("Shavit_IsKZMap", Native_IsKZMap);
170 CreateNative("Shavit_IsPracticeMode", Native_IsPracticeMode);
171 CreateNative("Shavit_LoadSnapshot", Native_LoadSnapshot);
172 CreateNative("Shavit_MarkKZMap", Native_MarkKZMap);
173 CreateNative("Shavit_PauseTimer", Native_PauseTimer);
174 CreateNative("Shavit_PrintToChat", Native_PrintToChat);
175 CreateNative("Shavit_RestartTimer", Native_RestartTimer);
176 CreateNative("Shavit_ResumeTimer", Native_ResumeTimer);
177 CreateNative("Shavit_SaveSnapshot", Native_SaveSnapshot);
178 CreateNative("Shavit_SetPracticeMode", Native_SetPracticeMode);
179 CreateNative("Shavit_StartTimer", Native_StartTimer);
180 CreateNative("Shavit_StopChatSound", Native_StopChatSound);
181 CreateNative("Shavit_StopTimer", Native_StopTimer);
182
183 // registers library, check "bool LibraryExists(const char[] name)" in order to use with other plugins
184 RegPluginLibrary("shavit");
185
186 gB_Late = late;
187
188 return APLRes_Success;
189}
190
191public void OnPluginStart()
192{
193 // forwards
194 gH_Forwards_Start = CreateGlobalForward("Shavit_OnStart", ET_Event, Param_Cell, Param_Cell);
195 gH_Forwards_Stop = CreateGlobalForward("Shavit_OnStop", ET_Event, Param_Cell, Param_Cell);
196 gH_Forwards_FinishPre = CreateGlobalForward("Shavit_OnFinishPre", ET_Event, Param_Cell, Param_Array);
197 gH_Forwards_Finish = CreateGlobalForward("Shavit_OnFinish", ET_Event, Param_Cell, Param_Cell, Param_Cell, Param_Cell, Param_Cell, Param_Cell, Param_Cell, Param_Cell);
198 gH_Forwards_OnRestart = CreateGlobalForward("Shavit_OnRestart", ET_Event, Param_Cell, Param_Cell);
199 gH_Forwards_OnEnd = CreateGlobalForward("Shavit_OnEnd", ET_Event, Param_Cell, Param_Cell);
200 gH_Forwards_OnPause = CreateGlobalForward("Shavit_OnPause", ET_Event, Param_Cell, Param_Cell);
201 gH_Forwards_OnResume = CreateGlobalForward("Shavit_OnResume", ET_Event, Param_Cell, Param_Cell);
202 gH_Forwards_OnStyleChanged = CreateGlobalForward("Shavit_OnStyleChanged", ET_Event, Param_Cell, Param_Cell, Param_Cell, Param_Cell, Param_Cell);
203 gH_Forwards_OnStyleConfigLoaded = CreateGlobalForward("Shavit_OnStyleConfigLoaded", ET_Event, Param_Cell);
204 gH_Forwards_OnDatabaseLoaded = CreateGlobalForward("Shavit_OnDatabaseLoaded", ET_Event);
205 gH_Forwards_OnChatConfigLoaded = CreateGlobalForward("Shavit_OnChatConfigLoaded", ET_Event);
206 gH_Forwards_OnUserCmdPre = CreateGlobalForward("Shavit_OnUserCmdPre", ET_Event, Param_Cell, Param_CellByRef, Param_CellByRef, Param_Array, Param_Array, Param_Cell, Param_Cell, Param_Cell, Param_Array, Param_Array);
207 gH_Forwards_OnTimerIncrement = CreateGlobalForward("Shavit_OnTimeIncrement", ET_Event, Param_Cell, Param_Array, Param_CellByRef, Param_Array);
208 gH_Forwards_OnTimerIncrementPost = CreateGlobalForward("Shavit_OnTimeIncrementPost", ET_Event, Param_Cell, Param_Cell, Param_Array);
209
210 LoadTranslations("shavit-core.phrases");
211
212 // game types
213 gEV_Type = GetEngineVersion();
214
215 if(gEV_Type == Engine_CSS || gEV_Type == Engine_TF2)
216 {
217 gSG_Type = Game_CSS;
218 }
219
220 else if(gEV_Type == Engine_CSGO)
221 {
222 gSG_Type = Game_CSGO;
223
224 sv_autobunnyhopping = FindConVar("sv_autobunnyhopping");
225 sv_autobunnyhopping.BoolValue = false;
226 }
227
228 else
229 {
230 SetFailState("This plugin was meant to be used in CS:S, CS:GO and TF2 *only*.");
231 }
232
233 // database connections
234 SQL_SetPrefix();
235 SQL_DBConnect();
236
237 // hooks
238 gB_HookedJump = HookEventEx("player_jump", Player_Jump);
239 HookEvent("player_death", Player_Death);
240 HookEvent("player_team", Player_Death);
241 HookEvent("player_spawn", Player_Death);
242
243 // commands START
244 // style
245 RegConsoleCmd("sm_style", Command_Style, "Choose your bhop style.");
246 RegConsoleCmd("sm_styles", Command_Style, "Choose your bhop style.");
247 RegConsoleCmd("sm_diff", Command_Style, "Choose your bhop style.");
248 RegConsoleCmd("sm_difficulty", Command_Style, "Choose your bhop style.");
249 gH_StyleCookie = RegClientCookie("shavit_style", "Style cookie", CookieAccess_Protected);
250
251 // timer start
252 RegConsoleCmd("sm_s", Command_StartTimer, "Start your timer.");
253 RegConsoleCmd("sm_start", Command_StartTimer, "Start your timer.");
254 RegConsoleCmd("sm_r", Command_StartTimer, "Start your timer.");
255 RegConsoleCmd("sm_restart", Command_StartTimer, "Start your timer.");
256
257 RegConsoleCmd("sm_b", Command_StartTimer, "Start your timer on the bonus track.");
258 RegConsoleCmd("sm_bonus", Command_StartTimer, "Start your timer on the bonus track.");
259
260 // teleport to end
261 RegConsoleCmd("sm_end", Command_TeleportEnd, "Teleport to endzone.");
262
263 RegConsoleCmd("sm_bend", Command_TeleportEnd, "Teleport to endzone of the bonus track.");
264 RegConsoleCmd("sm_bonusend", Command_TeleportEnd, "Teleport to endzone of the bonus track.");
265
266 // timer stop
267 RegConsoleCmd("sm_stop", Command_StopTimer, "Stop your timer.");
268
269 // timer pause / resume
270 RegConsoleCmd("sm_pause", Command_TogglePause, "Toggle pause.");
271 RegConsoleCmd("sm_unpause", Command_TogglePause, "Toggle pause.");
272 RegConsoleCmd("sm_resume", Command_TogglePause, "Toggle pause");
273
274 // autobhop toggle
275 RegConsoleCmd("sm_auto", Command_AutoBhop, "Toggle autobhop.");
276 RegConsoleCmd("sm_autobhop", Command_AutoBhop, "Toggle autobhop.");
277 gH_AutoBhopCookie = RegClientCookie("shavit_autobhop", "Autobhop cookie", CookieAccess_Protected);
278
279 // doublestep fixer
280 AddCommandListener(Command_DoubleStep, "+ds");
281 AddCommandListener(Command_DoubleStep, "-ds");
282
283 // style commands
284 gSM_StyleCommands = new StringMap();
285 // commands END
286
287 #if defined DEBUG
288 RegConsoleCmd("sm_finishtest", Command_FinishTest);
289 #endif
290
291 CreateConVar("shavit_version", SHAVIT_VERSION, "Plugin version.", (FCVAR_NOTIFY | FCVAR_DONTRECORD));
292
293 gCV_Autobhop = CreateConVar("shavit_core_autobhop", "1", "Enable autobhop?\nWill be forced to not work if STYLE_AUTOBHOP is not defined for a style!", FCVAR_NOTIFY, true, 0.0, true, 1.0);
294 gCV_LeftRight = CreateConVar("shavit_core_blockleftright", "1", "Block +left/right?", 0, true, 0.0, true, 1.0);
295 gCV_Restart = CreateConVar("shavit_core_restart", "1", "Allow commands that restart the timer?", 0, true, 0.0, true, 1.0);
296 gCV_Pause = CreateConVar("shavit_core_pause", "1", "Allow pausing?", 0, true, 0.0, true, 1.0);
297 gCV_NoStaminaReset = CreateConVar("shavit_core_nostaminareset", "1", "Disables the built-in stamina reset.\nAlso known as 'easybhop'.\nWill be forced to not work if STYLE_EASYBHOP is not defined for a style!", 0, true, 0.0, true, 1.0);
298 gCV_AllowTimerWithoutZone = CreateConVar("shavit_core_timernozone", "0", "Allow the timer to start if there's no start zone?", 0, true, 0.0, true, 1.0);
299 gCV_BlockPreJump = CreateConVar("shavit_core_blockprejump", "0", "Prevents jumping in the start zone.", 0, true, 0.0, true, 1.0);
300 gCV_NoZAxisSpeed = CreateConVar("shavit_core_nozaxisspeed", "1", "Don't start timer if vertical speed exists (btimes style).", 0, true, 0.0, true, 1.0);
301 gCV_VelocityTeleport = CreateConVar("shavit_core_velocityteleport", "0", "Teleport the client when changing its velocity? (for special styles)", 0, true, 0.0, true, 1.0);
302
303 gCV_Autobhop.AddChangeHook(OnConVarChanged);
304 gCV_LeftRight.AddChangeHook(OnConVarChanged);
305 gCV_Restart.AddChangeHook(OnConVarChanged);
306 gCV_Pause.AddChangeHook(OnConVarChanged);
307 gCV_NoStaminaReset.AddChangeHook(OnConVarChanged);
308 gCV_AllowTimerWithoutZone.AddChangeHook(OnConVarChanged);
309 gCV_BlockPreJump.AddChangeHook(OnConVarChanged);
310 gCV_NoZAxisSpeed.AddChangeHook(OnConVarChanged);
311 gCV_VelocityTeleport.AddChangeHook(OnConVarChanged);
312
313 AutoExecConfig();
314
315 sv_airaccelerate = FindConVar("sv_airaccelerate");
316 sv_airaccelerate.Flags &= ~(FCVAR_NOTIFY | FCVAR_REPLICATED);
317
318 sv_enablebunnyhopping = FindConVar("sv_enablebunnyhopping");
319
320 if(sv_enablebunnyhopping != null)
321 {
322 sv_enablebunnyhopping.Flags &= ~(FCVAR_NOTIFY | FCVAR_REPLICATED);
323 }
324
325 // late
326 if(gB_Late)
327 {
328 OnAdminMenuReady(null);
329
330 for(int i = 1; i <= MaxClients; i++)
331 {
332 if(IsValidClient(i))
333 {
334 OnClientPutInServer(i);
335 }
336 }
337 }
338
339 gB_Zones = LibraryExists("shavit-zones");
340 gB_WR = LibraryExists("shavit-wr");
341}
342
343public void OnConVarChanged(ConVar convar, const char[] oldValue, const char[] newValue)
344{
345 gB_Autobhop = gCV_Autobhop.BoolValue;
346 gB_LeftRight = gCV_LeftRight.BoolValue;
347 gB_Restart = gCV_Restart.BoolValue;
348 gB_Pause = gCV_Pause.BoolValue;
349 gB_NoStaminaReset = gCV_NoStaminaReset.BoolValue;
350 gB_AllowTimerWithoutZone = gCV_AllowTimerWithoutZone.BoolValue;
351 gB_BlockPreJump = gCV_BlockPreJump.BoolValue;
352 gB_NoZAxisSpeed = gCV_NoZAxisSpeed.BoolValue;
353 gB_VelocityTeleport = gCV_VelocityTeleport.BoolValue;
354}
355
356public void OnLibraryAdded(const char[] name)
357{
358 if(StrEqual(name, "shavit-zones"))
359 {
360 gB_Zones = true;
361 }
362
363 else if(StrEqual(name, "shavit-wr"))
364 {
365 gB_WR = true;
366 }
367}
368
369public void OnLibraryRemoved(const char[] name)
370{
371 if(StrEqual(name, "shavit-zones"))
372 {
373 gB_Zones = false;
374 }
375
376 else if(StrEqual(name, "shavit-wr"))
377 {
378 gB_WR = false;
379 }
380}
381
382public void OnAdminMenuReady(Handle topmenu)
383{
384 Handle hTopMenu = INVALID_HANDLE;
385
386 if(LibraryExists("adminmenu") && ((hTopMenu = GetAdminTopMenu()) != INVALID_HANDLE))
387 {
388 AddToTopMenu(hTopMenu, "Timer Commands", TopMenuObject_Category, CategoryHandler, INVALID_TOPMENUOBJECT);
389 }
390}
391
392public void CategoryHandler(Handle topmenu, TopMenuAction action, TopMenuObject object_id, int param, char[] buffer, int maxlength)
393{
394 if(action == TopMenuAction_DisplayTitle)
395 {
396 strcopy(buffer, maxlength, "Timer Commands:");
397 }
398
399 else if(action == TopMenuAction_DisplayOption)
400 {
401 strcopy(buffer, maxlength, "Timer Commands");
402 }
403}
404
405public void OnMapStart()
406{
407 // styles
408 if(!LoadStyles())
409 {
410 SetFailState("Could not load the styles configuration file. Make sure it exists (addons/sourcemod/configs/shavit-styles.cfg) and follows the proper syntax!");
411 }
412
413 else
414 {
415 Call_StartForward(gH_Forwards_OnStyleConfigLoaded);
416 Call_PushCell(gI_Styles);
417 Call_Finish();
418 }
419
420 // messages
421 if(!LoadMessages())
422 {
423 SetFailState("Could not load the chat messages configuration file. Make sure it exists (addons/sourcemod/configs/shavit-messages.cfg) and follows the proper syntax!");
424 }
425
426 else
427 {
428 Call_StartForward(gH_Forwards_OnChatConfigLoaded);
429 Call_Finish();
430 }
431
432 int entity = -1;
433 while((entity = FindEntityByClassname(entity, "trigger_push")) != -1)
434 {
435 SDKHook(entity, SDKHook_Touch, OnTouch);
436 }
437}
438
439public void OnMapEnd()
440{
441 gB_KZMap = false;
442}
443
444public Action Command_StartTimer(int client, int args)
445{
446 if(!IsValidClient(client))
447 {
448 return Plugin_Handled;
449 }
450
451 char[] sCommand = new char[16];
452 GetCmdArg(0, sCommand, 16);
453
454 if(!gB_Restart)
455 {
456 if(args != -1)
457 {
458 Shavit_PrintToChat(client, "%T", "CommandDisabled", client, gS_ChatStrings[sMessageVariable], sCommand, gS_ChatStrings[sMessageText]);
459 }
460
461 return Plugin_Handled;
462 }
463
464 int track = Track_Main;
465
466 if(StrContains(sCommand, "sm_b", false) == 0)
467 {
468 track = Track_Bonus;
469 }
470
471 if(gB_AllowTimerWithoutZone || (gB_Zones && (Shavit_ZoneExists(Zone_Start, track) || gB_KZMap)))
472 {
473 Call_StartForward(gH_Forwards_OnRestart);
474 Call_PushCell(client);
475 Call_PushCell(track);
476 Call_Finish();
477
478 if(gB_AllowTimerWithoutZone)
479 {
480 StartTimer(client, track);
481 }
482 }
483
484 else
485 {
486 Shavit_PrintToChat(client, "%T", "StartZoneUndefined", client, gS_ChatStrings[sMessageWarning], gS_ChatStrings[sMessageText]);
487 }
488
489 return Plugin_Handled;
490}
491
492public Action Command_TeleportEnd(int client, int args)
493{
494 if(!IsValidClient(client))
495 {
496 return Plugin_Handled;
497 }
498
499 char[] sCommand = new char[16];
500 GetCmdArg(0, sCommand, 16);
501
502 int track = Track_Main;
503
504 if(StrContains(sCommand, "sm_b", false) == 0)
505 {
506 track = Track_Bonus;
507 }
508
509 if(gB_Zones && (Shavit_ZoneExists(Zone_End, track) || gB_KZMap))
510 {
511 Shavit_StopTimer(client);
512
513 Call_StartForward(gH_Forwards_OnEnd);
514 Call_PushCell(client);
515 Call_PushCell(track);
516 Call_Finish();
517 }
518
519 else
520 {
521 Shavit_PrintToChat(client, "%T", "EndZoneUndefined", client, gS_ChatStrings[sMessageWarning], gS_ChatStrings[sMessageText]);
522 }
523
524 return Plugin_Handled;
525}
526
527public Action Command_StopTimer(int client, int args)
528{
529 if(!IsValidClient(client))
530 {
531 return Plugin_Handled;
532 }
533
534 Shavit_StopTimer(client);
535
536 return Plugin_Handled;
537}
538
539public Action Command_TogglePause(int client, int args)
540{
541 if(!IsValidClient(client) || !gB_TimerEnabled[client])
542 {
543 return Plugin_Handled;
544 }
545
546 if(Shavit_InsideZone(client, Zone_Start, gI_Track[client]))
547 {
548 Shavit_PrintToChat(client, "%T", "PauseStartZone", client, gS_ChatStrings[sMessageText], gS_ChatStrings[sMessageWarning], gS_ChatStrings[sMessageText], gS_ChatStrings[sMessageVariable], gS_ChatStrings[sMessageText]);
549
550 return Plugin_Handled;
551 }
552
553 if(!gB_Pause)
554 {
555 char[] sCommand = new char[16];
556 GetCmdArg(0, sCommand, 16);
557
558 Shavit_PrintToChat(client, "%T", "CommandDisabled", client, gS_ChatStrings[sMessageVariable], sCommand, gS_ChatStrings[sMessageText]);
559
560 return Plugin_Handled;
561 }
562
563 if(GetEntPropEnt(client, Prop_Send, "m_hGroundEntity") == -1 && GetEntityMoveType(client) != MOVETYPE_LADDER)
564 {
565 Shavit_PrintToChat(client, "%T", "PauseNotOnGround", client, gS_ChatStrings[sMessageWarning], gS_ChatStrings[sMessageText]);
566
567 return Plugin_Handled;
568 }
569
570 if(gB_ClientPaused[client])
571 {
572 TeleportEntity(client, gF_PausePosition[client][0], gF_PausePosition[client][1], gF_PausePosition[client][2]);
573 ResumeTimer(client);
574
575 Shavit_PrintToChat(client, "%T", "MessageUnpause", client, gS_ChatStrings[sMessageText], gS_ChatStrings[sMessageWarning], gS_ChatStrings[sMessageText]);
576 }
577
578 else
579 {
580 GetClientAbsOrigin(client, gF_PausePosition[client][0]);
581 GetClientEyeAngles(client, gF_PausePosition[client][1]);
582 GetEntPropVector(client, Prop_Data, "m_vecAbsVelocity", gF_PausePosition[client][2]);
583
584 PauseTimer(client);
585
586 Shavit_PrintToChat(client, "%T", "MessagePause", client, gS_ChatStrings[sMessageText], gS_ChatStrings[sMessageWarning], gS_ChatStrings[sMessageText]);
587 }
588
589 return Plugin_Handled;
590}
591
592#if defined DEBUG
593public Action Command_FinishTest(int client, int args)
594{
595 Shavit_FinishMap(client, gI_Track[client]);
596
597 return Plugin_Handled;
598}
599#endif
600
601public Action Command_AutoBhop(int client, int args)
602{
603 if(!IsValidClient(client))
604 {
605 return Plugin_Handled;
606 }
607
608 gB_Auto[client] = !gB_Auto[client];
609
610 if(gB_Auto[client])
611 {
612 Shavit_PrintToChat(client, "%T", "AutobhopEnabled", client, gS_ChatStrings[sMessageVariable2], gS_ChatStrings[sMessageText]);
613 }
614
615 else
616 {
617 Shavit_PrintToChat(client, "%T", "AutobhopDisabled", client, gS_ChatStrings[sMessageWarning], gS_ChatStrings[sMessageText]);
618 }
619
620 char[] sAutoBhop = new char[4];
621 IntToString(view_as<int>(gB_Auto[client]), sAutoBhop, 4);
622 SetClientCookie(client, gH_AutoBhopCookie, sAutoBhop);
623
624 UpdateAutoBhop(client);
625
626 return Plugin_Handled;
627}
628
629public Action Command_DoubleStep(int client, const char[] command, int args)
630{
631 gB_DoubleSteps[client] = (command[0] == '+');
632
633 return Plugin_Handled;
634}
635
636public Action Command_Style(int client, int args)
637{
638 if(!IsValidClient(client))
639 {
640 return Plugin_Handled;
641 }
642
643 Menu menu = new Menu(StyleMenu_Handler);
644 menu.SetTitle("%T", "StyleMenuTitle", client);
645
646 for(int i = 0; i < gI_Styles; i++)
647 {
648 char[] sInfo = new char[8];
649 IntToString(i, sInfo, 8);
650
651 char[] sDisplay = new char[64];
652
653 if(gA_StyleSettings[i][bUnranked])
654 {
655 FormatEx(sDisplay, 64, "%T %s", "StyleUnranked", client, gS_StyleStrings[i][sStyleName]);
656 }
657
658 else
659 {
660 float time = 0.0;
661
662 if(gB_WR)
663 {
664 Shavit_GetWRTime(i, time, Track_Main);
665 }
666
667 if(time > 0.0)
668 {
669 char[] sTime = new char[32];
670 FormatSeconds(time, sTime, 32, false);
671
672 FormatEx(sDisplay, 64, "%s - WR: %s", gS_StyleStrings[i][sStyleName], sTime);
673 }
674
675 else
676 {
677 strcopy(sDisplay, 64, gS_StyleStrings[i][sStyleName]);
678 }
679 }
680
681 menu.AddItem(sInfo, sDisplay, (gBS_Style[client] == i)? ITEMDRAW_DISABLED:ITEMDRAW_DEFAULT);
682 }
683
684 // should NEVER happen
685 if(menu.ItemCount == 0)
686 {
687 menu.AddItem("-1", "Nothing");
688 }
689
690 else if(menu.ItemCount <= ((gEV_Type == Engine_CSS)? 9:8))
691 {
692 menu.Pagination = MENU_NO_PAGINATION;
693 }
694
695 menu.ExitButton = true;
696 menu.Display(client, 20);
697
698 return Plugin_Handled;
699}
700
701public int StyleMenu_Handler(Menu menu, MenuAction action, int param1, int param2)
702{
703 if(action == MenuAction_Select)
704 {
705 char[] info = new char[16];
706 menu.GetItem(param2, info, 16);
707
708 int style = StringToInt(info);
709
710 if(style == -1)
711 {
712 return 0;
713 }
714
715 ChangeClientStyle(param1, style, true);
716 }
717
718 else if(action == MenuAction_End)
719 {
720 delete menu;
721 }
722
723 return 0;
724}
725
726void CallOnStyleChanged(int client, int oldstyle, int newstyle, bool manual)
727{
728 Call_StartForward(gH_Forwards_OnStyleChanged);
729 Call_PushCell(client);
730 Call_PushCell(oldstyle);
731 Call_PushCell(newstyle);
732 Call_PushCell(gI_Track[client]);
733 Call_PushCell(manual);
734 Call_Finish();
735
736 gBS_Style[client] = newstyle;
737
738 UpdateAutoBhop(client);
739 UpdateAiraccelerate(client);
740 UpdateBunnyhopping(client);
741}
742
743void ChangeClientStyle(int client, int style, bool manual)
744{
745 if(!IsValidClient(client))
746 {
747 return;
748 }
749
750 if(manual)
751 {
752 Shavit_PrintToChat(client, "%T", "StyleSelection", client, gS_ChatStrings[sMessageStyle], gS_StyleStrings[style][sStyleName], gS_ChatStrings[sMessageText]);
753 }
754
755 if(gA_StyleSettings[style][bUnranked])
756 {
757 Shavit_PrintToChat(client, "%T", "UnrankedWarning", client, gS_ChatStrings[sMessageWarning], gS_ChatStrings[sMessageText]);
758 }
759
760 int aa_old = RoundToZero(gA_StyleSettings[gBS_Style[client]][fAiraccelerate]);
761 int aa_new = RoundToZero(gA_StyleSettings[style][fAiraccelerate]);
762
763 if(aa_old != aa_new)
764 {
765 Shavit_PrintToChat(client, "%T", "NewAiraccelerate", client, aa_old, gS_ChatStrings[sMessageVariable], aa_new, gS_ChatStrings[sMessageText]);
766 }
767
768 CallOnStyleChanged(client, gBS_Style[client], style, manual);
769
770 StopTimer(client);
771
772 if(gB_AllowTimerWithoutZone || (gB_Zones && (Shavit_ZoneExists(Zone_Start, gI_Track[client]) || gB_KZMap)))
773 {
774 Call_StartForward(gH_Forwards_OnRestart);
775 Call_PushCell(client);
776 Call_PushCell(gI_Track[client]);
777 Call_Finish();
778 }
779
780 char[] sStyle = new char[4];
781 IntToString(style, sStyle, 4);
782
783 SetClientCookie(client, gH_StyleCookie, sStyle);
784}
785
786// used as an alternative for games where player_jump isn't a thing, such as TF2
787public void Bunnyhop_OnLeaveGround(int client, bool jumped, bool ladder)
788{
789 if(gB_HookedJump || !jumped || ladder)
790 {
791 return;
792 }
793
794 DoJump(client);
795}
796
797public void Player_Jump(Event event, const char[] name, bool dontBroadcast)
798{
799 DoJump(GetClientOfUserId(event.GetInt("userid")));
800}
801
802void DoJump(int client)
803{
804 if(gB_TimerEnabled[client])
805 {
806 gI_Jumps[client]++;
807 }
808
809 // TF2 doesn't use stamina
810 if(gEV_Type != Engine_TF2 && (gB_NoStaminaReset && gA_StyleSettings[gBS_Style[client]][bEasybhop]) || Shavit_InsideZone(client, Zone_Easybhop, gI_Track[client]))
811 {
812 SetEntPropFloat(client, Prop_Send, "m_flStamina", 0.0);
813 }
814
815 RequestFrame(VelocityChanges, GetClientSerial(client));
816}
817
818void VelocityChanges(int data)
819{
820 int client = GetClientFromSerial(data);
821
822 if(client == 0)
823 {
824 return;
825 }
826
827 if(view_as<float>(gA_StyleSettings[gBS_Style[client]][fGravityMultiplier]) != 1.0)
828 {
829 SetEntityGravity(client, view_as<float>(gA_StyleSettings[gBS_Style[client]][fGravityMultiplier]));
830 }
831
832 if(view_as<float>(gA_StyleSettings[gBS_Style[client]][fSpeedMultiplier]) != 1.0)
833 {
834 SetEntPropFloat(client, Prop_Data, "m_flLaggedMovementValue", view_as<float>(gA_StyleSettings[gBS_Style[client]][fSpeedMultiplier]));
835 }
836
837 float fAbsVelocity[3];
838 GetEntPropVector(client, Prop_Data, "m_vecAbsVelocity", fAbsVelocity);
839
840 float fSpeed = (SquareRoot(Pow(fAbsVelocity[0], 2.0) + Pow(fAbsVelocity[1], 2.0)));
841
842 if(fSpeed == 0.0)
843 {
844 return;
845 }
846
847 float fVelocityMultiplier = view_as<float>(gA_StyleSettings[gBS_Style[client]][fVelocity]);
848 float fVelocityBonus = view_as<float>(gA_StyleSettings[gBS_Style[client]][fBonusVelocity]);
849 float fMin = view_as<float>(gA_StyleSettings[gBS_Style[client]][fMinVelocity]);
850
851 if(fVelocityMultiplier != 0.0)
852 {
853 fAbsVelocity[0] *= fVelocityMultiplier;
854 fAbsVelocity[1] *= fVelocityMultiplier;
855 }
856
857 if(fVelocityBonus != 0.0)
858 {
859 float x = fSpeed / (fSpeed + fVelocityBonus);
860 fAbsVelocity[0] /= x;
861 fAbsVelocity[1] /= x;
862 }
863
864 if(fMin != 0.0 && fSpeed < fMin)
865 {
866 float x = (fSpeed / fMin);
867 fAbsVelocity[0] /= x;
868 fAbsVelocity[1] /= x;
869 }
870
871 if(!gB_VelocityTeleport)
872 {
873 SetEntPropVector(client, Prop_Data, "m_vecAbsVelocity", fAbsVelocity);
874 }
875
876 else
877 {
878 TeleportEntity(client, NULL_VECTOR, NULL_VECTOR, fAbsVelocity);
879 }
880}
881
882public void Player_Death(Event event, const char[] name, bool dontBroadcast)
883{
884 int client = GetClientOfUserId(event.GetInt("userid"));
885
886 ResumeTimer(client);
887 StopTimer(client);
888}
889
890public int Native_GetGameType(Handle handler, int numParams)
891{
892 return view_as<int>(gSG_Type);
893}
894
895public int Native_GetDatabase(Handle handler, int numParams)
896{
897 return view_as<int>(CloneHandle(gH_SQL, handler));
898}
899
900public int Native_GetDB(Handle handler, int numParams)
901{
902 SetNativeCellRef(1, gH_SQL);
903}
904
905public int Native_GetTimer(Handle handler, int numParams)
906{
907 // 1 - client
908 int client = GetNativeCell(1);
909
910 // 2 - time
911 SetNativeCellRef(2, gF_PlayerTimer[client]);
912 SetNativeCellRef(3, gI_Jumps[client]);
913 SetNativeCellRef(4, gBS_Style[client]);
914 SetNativeCellRef(5, gB_TimerEnabled[client]);
915}
916
917public int Native_GetClientTime(Handle handler, int numParams)
918{
919 return view_as<int>(gF_PlayerTimer[GetNativeCell(1)]);
920}
921
922public int Native_GetClientTrack(Handle handler, int numParams)
923{
924 return gI_Track[GetNativeCell(1)];
925}
926
927public int Native_GetClientJumps(Handle handler, int numParams)
928{
929 return gI_Jumps[GetNativeCell(1)];
930}
931
932public int Native_GetBhopStyle(Handle handler, int numParams)
933{
934 return gBS_Style[GetNativeCell(1)];
935}
936
937public int Native_GetTimerStatus(Handle handler, int numParams)
938{
939 return GetTimerStatus(GetNativeCell(1));
940}
941
942public int Native_IsKZMap(Handle handler, int numParams)
943{
944 return view_as<bool>(gB_KZMap);
945}
946
947public int Native_StartTimer(Handle handler, int numParams)
948{
949 StartTimer(GetNativeCell(1), GetNativeCell(2));
950}
951
952public int Native_StopTimer(Handle handler, int numParams)
953{
954 int client = GetNativeCell(1);
955
956 StopTimer(client);
957
958 Call_StartForward(gH_Forwards_Stop);
959 Call_PushCell(client);
960 Call_PushCell(gI_Track[client]);
961 Call_Finish();
962}
963
964public int Native_FinishMap(Handle handler, int numParams)
965{
966 int client = GetNativeCell(1);
967
968 any[] snapshot = new any[TIMERSNAPSHOT_SIZE];
969 snapshot[bTimerEnabled] = gB_TimerEnabled[client];
970 snapshot[bClientPaused] = gB_ClientPaused[client];
971 snapshot[iJumps] = gI_Jumps[client];
972 snapshot[bsStyle] = gBS_Style[client];
973 snapshot[iStrafes] = gI_Strafes[client];
974 snapshot[iTotalMeasures] = gI_TotalMeasures[client];
975 snapshot[iGoodGains] = gI_GoodGains[client];
976 snapshot[fServerTime] = GetEngineTime();
977 snapshot[fCurrentTime] = gF_PlayerTimer[client];
978 snapshot[iSHSWCombination] = gI_SHSW_FirstCombination[client];
979 snapshot[iTimerTrack] = gI_Track[client];
980
981 Action result = Plugin_Continue;
982 Call_StartForward(gH_Forwards_FinishPre);
983 Call_PushCell(client);
984 Call_PushArrayEx(snapshot, TIMERSNAPSHOT_SIZE, SM_PARAM_COPYBACK);
985 Call_Finish(result);
986
987 if(result != Plugin_Continue && result != Plugin_Changed)
988 {
989 return;
990 }
991
992 Call_StartForward(gH_Forwards_Finish);
993 Call_PushCell(client);
994
995 int style = 0;
996 int track = Track_Main;
997
998 if(result == Plugin_Continue)
999 {
1000 Call_PushCell(style = gBS_Style[client]);
1001 Call_PushCell(gF_PlayerTimer[client]);
1002 Call_PushCell(gI_Jumps[client]);
1003 Call_PushCell(gI_Strafes[client]);
1004 Call_PushCell((gA_StyleSettings[gBS_Style[client]][bSync])? (gI_GoodGains[client] == 0)? 0.0:(gI_GoodGains[client] / float(gI_TotalMeasures[client]) * 100.0):-1.0);
1005 Call_PushCell(track = gI_Track[client]);
1006 }
1007
1008 else
1009 {
1010 Call_PushCell(style = snapshot[bsStyle]);
1011 Call_PushCell(snapshot[fCurrentTime]);
1012 Call_PushCell(snapshot[iJumps]);
1013 Call_PushCell(snapshot[iStrafes]);
1014 Call_PushCell((gA_StyleSettings[snapshot[bsStyle]][bSync])? (snapshot[iGoodGains] == 0)? 0.0:(snapshot[iGoodGains] / float(snapshot[iTotalMeasures]) * 100.0):-1.0);
1015 Call_PushCell(track = snapshot[iTimerTrack]);
1016 }
1017
1018 float oldtime = 0.0;
1019
1020 if(gB_WR)
1021 {
1022 Shavit_GetPlayerPB(client, style, oldtime, track);
1023 }
1024
1025 Call_PushCell(oldtime);
1026 Call_Finish();
1027
1028 StopTimer(client);
1029}
1030
1031public int Native_PauseTimer(Handle handler, int numParams)
1032{
1033 PauseTimer(GetNativeCell(1));
1034}
1035
1036public int Native_ResumeTimer(Handle handler, int numParams)
1037{
1038 ResumeTimer(GetNativeCell(1));
1039}
1040
1041public int Native_StopChatSound(Handle handler, int numParams)
1042{
1043 gB_StopChatSound = true;
1044}
1045
1046public int Native_PrintToChat(Handle handler, int numParams)
1047{
1048 int client = GetNativeCell(1);
1049 static int written = 0; // useless?
1050
1051 char[] buffer = new char[300];
1052 FormatNativeString(0, 2, 3, 300, written, buffer);
1053 Format(buffer, 300, "%s %s%s", gS_ChatStrings[sMessagePrefix], gS_ChatStrings[sMessageText], buffer);
1054
1055 if(IsSource2013(gEV_Type))
1056 {
1057 Handle hSayText2 = StartMessageOne("SayText2", client);
1058
1059 if(hSayText2 != null)
1060 {
1061 BfWriteByte(hSayText2, 0);
1062 BfWriteByte(hSayText2, !gB_StopChatSound);
1063 BfWriteString(hSayText2, buffer);
1064 }
1065
1066 EndMessage();
1067 }
1068
1069 else
1070 {
1071 PrintToChat(client, " %s", buffer);
1072 }
1073
1074 gB_StopChatSound = false;
1075}
1076
1077public int Native_RestartTimer(Handle handler, int numParams)
1078{
1079 int client = GetNativeCell(1);
1080 int track = GetNativeCell(2);
1081
1082 Call_StartForward(gH_Forwards_OnRestart);
1083 Call_PushCell(client);
1084 Call_PushCell(track);
1085 Call_Finish();
1086
1087 StartTimer(client, track);
1088}
1089
1090public int Native_GetStrafeCount(Handle handler, int numParams)
1091{
1092 return gI_Strafes[GetNativeCell(1)];
1093}
1094
1095public int Native_GetSync(Handle handler, int numParams)
1096{
1097 int client = GetNativeCell(1);
1098
1099 return view_as<int>((gA_StyleSettings[gBS_Style[client]][bSync])? (gI_GoodGains[client] == 0)? 0.0:(gI_GoodGains[client] / float(gI_TotalMeasures[client]) * 100.0):-1.0);
1100}
1101
1102public int Native_GetStyleCount(Handle handler, int numParams)
1103{
1104 return (gI_Styles > 0)? gI_Styles:-1;
1105}
1106
1107public int Native_GetStyleSettings(Handle handler, int numParams)
1108{
1109 return SetNativeArray(2, gA_StyleSettings[GetNativeCell(1)], STYLESETTINGS_SIZE);
1110}
1111
1112public int Native_GetStyleStrings(Handle handler, int numParams)
1113{
1114 return SetNativeString(3, gS_StyleStrings[GetNativeCell(1)][GetNativeCell(2)], GetNativeCell(4));
1115}
1116
1117public int Native_GetChatStrings(Handle handler, int numParams)
1118{
1119 return SetNativeString(2, gS_ChatStrings[GetNativeCell(1)], GetNativeCell(3));
1120}
1121
1122public int Native_SetPracticeMode(Handle handler, int numParams)
1123{
1124 int client = GetNativeCell(1);
1125 bool practice = view_as<bool>(GetNativeCell(2));
1126 bool alert = view_as<bool>(GetNativeCell(3));
1127
1128 if(alert && practice && !gB_PracticeMode[client])
1129 {
1130 Shavit_PrintToChat(client, "%T", "PracticeModeAlert", client, gS_ChatStrings[sMessageWarning], gS_ChatStrings[sMessageText]);
1131 }
1132
1133 gB_PracticeMode[client] = practice;
1134}
1135
1136public int Native_IsPracticeMode(Handle handler, int numParams)
1137{
1138 return view_as<int>(gB_PracticeMode[GetNativeCell(1)]);
1139}
1140
1141public int Native_SaveSnapshot(Handle handler, int numParams)
1142{
1143 int client = GetNativeCell(1);
1144
1145 any[] snapshot = new any[TIMERSNAPSHOT_SIZE];
1146 snapshot[bTimerEnabled] = gB_TimerEnabled[client];
1147 snapshot[bClientPaused] = gB_ClientPaused[client];
1148 snapshot[iJumps] = gI_Jumps[client];
1149 snapshot[bsStyle] = gBS_Style[client];
1150 snapshot[iStrafes] = gI_Strafes[client];
1151 snapshot[iTotalMeasures] = gI_TotalMeasures[client];
1152 snapshot[iGoodGains] = gI_GoodGains[client];
1153 snapshot[fServerTime] = GetEngineTime();
1154 snapshot[fCurrentTime] = gF_PlayerTimer[client];
1155 snapshot[iSHSWCombination] = gI_SHSW_FirstCombination[client];
1156 snapshot[iTimerTrack] = gI_Track[client];
1157
1158 return SetNativeArray(2, snapshot, TIMERSNAPSHOT_SIZE);
1159}
1160
1161public int Native_LoadSnapshot(Handle handler, int numParams)
1162{
1163 int client = GetNativeCell(1);
1164
1165 any[] snapshot = new any[TIMERSNAPSHOT_SIZE];
1166 GetNativeArray(2, snapshot, TIMERSNAPSHOT_SIZE);
1167
1168 gI_Track[client] = view_as<int>(snapshot[iTimerTrack]);
1169
1170 if(gBS_Style[client] != snapshot[bsStyle])
1171 {
1172 CallOnStyleChanged(client, gBS_Style[client], snapshot[bsStyle], false);
1173 }
1174
1175 gB_TimerEnabled[client] = view_as<bool>(snapshot[bTimerEnabled]);
1176 gB_ClientPaused[client] = view_as<bool>(snapshot[bClientPaused]);
1177 gI_Jumps[client] = view_as<int>(snapshot[iJumps]);
1178 gBS_Style[client] = snapshot[bsStyle];
1179 gI_Strafes[client] = view_as<int>(snapshot[iStrafes]);
1180 gI_TotalMeasures[client] = view_as<int>(snapshot[iTotalMeasures]);
1181 gI_GoodGains[client] = view_as<int>(snapshot[iGoodGains]);
1182 gF_PlayerTimer[client] = snapshot[fCurrentTime];
1183 gI_SHSW_FirstCombination[client] = view_as<int>(snapshot[iSHSWCombination]);
1184}
1185
1186public int Native_MarkKZMap(Handle handler, int numParams)
1187{
1188 gB_KZMap = true;
1189}
1190
1191int GetTimerStatus(int client)
1192{
1193 if(!gB_TimerEnabled[client])
1194 {
1195 return view_as<int>(Timer_Stopped);
1196 }
1197
1198 else if(gB_ClientPaused[client])
1199 {
1200 return view_as<int>(Timer_Paused);
1201 }
1202
1203 return view_as<int>(Timer_Running);
1204}
1205
1206void StartTimer(int client, int track)
1207{
1208 if(!IsValidClient(client, true) || GetClientTeam(client) < 2 || IsFakeClient(client))
1209 {
1210 return;
1211 }
1212
1213 float fSpeed[3];
1214 GetEntPropVector(client, Prop_Data, "m_vecVelocity", fSpeed);
1215
1216 if(!gB_NoZAxisSpeed || gA_StyleSettings[gBS_Style[client]][bPrespeed] || (fSpeed[2] == 0.0 && SquareRoot(Pow(fSpeed[0], 2.0) + Pow(fSpeed[1], 2.0)) <= 290.0))
1217 {
1218 Action result = Plugin_Continue;
1219 Call_StartForward(gH_Forwards_Start);
1220 Call_PushCell(client);
1221 Call_PushCell(track);
1222 Call_Finish(result);
1223
1224 if(result == Plugin_Continue)
1225 {
1226 gI_Strafes[client] = 0;
1227 gI_Jumps[client] = 0;
1228 gI_TotalMeasures[client] = 0;
1229 gI_GoodGains[client] = 0;
1230 gF_PlayerTimer[client] = 0.0;
1231 gI_Track[client] = track;
1232 gB_TimerEnabled[client] = true;
1233 gI_SHSW_FirstCombination[client] = -1;
1234 gF_PlayerTimer[client] = 0.0;
1235 gB_PracticeMode[client] = false;
1236
1237 SetEntityGravity(client, view_as<float>(gA_StyleSettings[gBS_Style[client]][fGravityMultiplier]));
1238 SetEntPropFloat(client, Prop_Data, "m_flLaggedMovementValue", view_as<float>(gA_StyleSettings[gBS_Style[client]][fSpeedMultiplier]));
1239 }
1240
1241 else if(result == Plugin_Handled || result == Plugin_Stop)
1242 {
1243 gB_TimerEnabled[client] = false;
1244 }
1245 }
1246}
1247
1248void StopTimer(int client)
1249{
1250 if(!IsValidClient(client) || IsFakeClient(client))
1251 {
1252 return;
1253 }
1254
1255 gB_TimerEnabled[client] = false;
1256 gI_Jumps[client] = 0;
1257 gF_PlayerTimer[client] = 0.0;
1258 gB_ClientPaused[client] = false;
1259 gI_Strafes[client] = 0;
1260 gI_TotalMeasures[client] = 0;
1261 gI_GoodGains[client] = 0;
1262}
1263
1264void PauseTimer(int client)
1265{
1266 if(!IsValidClient(client) || IsFakeClient(client))
1267 {
1268 return;
1269 }
1270
1271 Call_StartForward(gH_Forwards_OnPause);
1272 Call_PushCell(client);
1273 Call_PushCell(gI_Track[client]);
1274 Call_Finish();
1275
1276 gB_ClientPaused[client] = true;
1277}
1278
1279void ResumeTimer(int client)
1280{
1281 if(!IsValidClient(client) || IsFakeClient(client))
1282 {
1283 return;
1284 }
1285
1286 Call_StartForward(gH_Forwards_OnResume);
1287 Call_PushCell(client);
1288 Call_PushCell(gI_Track[client]);
1289 Call_Finish();
1290
1291 gB_ClientPaused[client] = false;
1292}
1293
1294public void OnClientDisconnect(int client)
1295{
1296 StopTimer(client);
1297}
1298
1299public void OnClientCookiesCached(int client)
1300{
1301 if(IsFakeClient(client))
1302 {
1303 return;
1304 }
1305
1306 char[] sCookie = new char[4];
1307
1308 if(gH_AutoBhopCookie != null)
1309 {
1310 GetClientCookie(client, gH_AutoBhopCookie, sCookie, 4);
1311 }
1312
1313 gB_Auto[client] = (strlen(sCookie) > 0)? view_as<bool>(StringToInt(sCookie)):true;
1314 int style = 0;
1315
1316 if(gH_StyleCookie != null)
1317 {
1318 GetClientCookie(client, gH_StyleCookie, sCookie, 4);
1319 style = StringToInt(sCookie);
1320 }
1321
1322 int newstyle = (style >= 0 && style < gI_Styles)? style:0;
1323 CallOnStyleChanged(client, gBS_Style[client], newstyle, false);
1324}
1325
1326public void OnClientPutInServer(int client)
1327{
1328 StopTimer(client);
1329
1330 if(IsClientConnected(client) && IsFakeClient(client))
1331 {
1332 return;
1333 }
1334
1335 gB_Auto[client] = true;
1336 gB_DoubleSteps[client] = false;
1337 gF_StrafeWarning[client] = 0.0;
1338 gB_PracticeMode[client] = false;
1339 gI_SHSW_FirstCombination[client] = -1;
1340 gI_Track[client] = 0;
1341
1342 if(AreClientCookiesCached(client))
1343 {
1344 OnClientCookiesCached(client);
1345 }
1346
1347 else
1348 {
1349 CallOnStyleChanged(client, 0, 0, false);
1350 }
1351
1352 if(gH_SQL == null)
1353 {
1354 return;
1355 }
1356
1357 SDKHook(client, SDKHook_PreThinkPost, PreThinkPost);
1358
1359 char[] sAuthID3 = new char[32];
1360
1361 if(!GetClientAuthId(client, AuthId_Steam3, sAuthID3, 32))
1362 {
1363 KickClient(client, "%T", "VerificationFailed", client);
1364
1365 return;
1366 }
1367
1368 char[] sName = new char[MAX_NAME_LENGTH];
1369 GetClientName(client, sName, MAX_NAME_LENGTH);
1370 ReplaceString(sName, MAX_NAME_LENGTH, "#", "?"); // to avoid this: https://user-images.githubusercontent.com/3672466/28637962-0d324952-724c-11e7-8b27-15ff021f0a59.png
1371
1372 int iLength = ((strlen(sName) * 2) + 1);
1373 char[] sEscapedName = new char[iLength];
1374 gH_SQL.Escape(sName, sEscapedName, iLength);
1375
1376 char[] sIP = new char[64];
1377 GetClientIP(client, sIP, 64);
1378
1379 char[] sCountry = new char[128];
1380
1381 if(!GeoipCountry(sIP, sCountry, 128))
1382 {
1383 strcopy(sCountry, 128, "Local Area Network");
1384 }
1385
1386 int iTime = GetTime();
1387
1388 char[] sQuery = new char[512];
1389
1390 if(gB_MySQL)
1391 {
1392 FormatEx(sQuery, 512, "INSERT INTO %susers (auth, name, country, ip, lastlogin) VALUES ('%s', '%s', '%s', '%s', %d) ON DUPLICATE KEY UPDATE name = '%s', country = '%s', ip = '%s', lastlogin = %d;", gS_MySQLPrefix, sAuthID3, sEscapedName, sCountry, sIP, iTime, sEscapedName, sCountry, sIP, iTime);
1393 }
1394
1395 else
1396 {
1397 FormatEx(sQuery, 512, "REPLACE INTO %susers (auth, name, country, ip, lastlogin) VALUES ('%s', '%s', '%s', '%s', %d);", gS_MySQLPrefix, sAuthID3, sEscapedName, sCountry, sIP, iTime);
1398 }
1399
1400 gH_SQL.Query(SQL_InsertUser_Callback, sQuery, GetClientSerial(client));
1401}
1402
1403public void SQL_InsertUser_Callback(Database db, DBResultSet results, const char[] error, any data)
1404{
1405 if(results == null)
1406 {
1407 int client = GetClientFromSerial(data);
1408
1409 if(client == 0)
1410 {
1411 LogError("Timer error! Failed to insert a disconnected player's data to the table. Reason: %s", error);
1412 }
1413
1414 else
1415 {
1416 LogError("Timer error! Failed to insert \"%N\"'s data to the table. Reason: %s", client, error);
1417 }
1418
1419 return;
1420 }
1421}
1422
1423bool LoadStyles()
1424{
1425 char[] sPath = new char[PLATFORM_MAX_PATH];
1426 BuildPath(Path_SM, sPath, PLATFORM_MAX_PATH, "configs/shavit-styles.cfg");
1427
1428 KeyValues kv = new KeyValues("shavit-styles");
1429
1430 if(!kv.ImportFromFile(sPath) || !kv.GotoFirstSubKey())
1431 {
1432 delete kv;
1433
1434 return false;
1435 }
1436
1437 int i = 0;
1438
1439 do
1440 {
1441 kv.GetString("name", gS_StyleStrings[i][sStyleName], 128, "<MISSING STYLE NAME>");
1442 kv.GetString("shortname", gS_StyleStrings[i][sShortName], 128, "<MISSING SHORT STYLE NAME>");
1443 kv.GetString("htmlcolor", gS_StyleStrings[i][sHTMLColor], 128, "<MISSING STYLE HTML COLOR>");
1444 kv.GetString("command", gS_StyleStrings[i][sChangeCommand], 128, "");
1445 kv.GetString("clantag", gS_StyleStrings[i][sClanTag], 128, "<MISSING STYLE CLAN TAG>");
1446 kv.GetString("specialstring", gS_StyleStrings[i][sSpecialString], 128, "");
1447
1448 gA_StyleSettings[i][bAutobhop] = view_as<bool>(kv.GetNum("autobhop", 1));
1449 gA_StyleSettings[i][bEasybhop] = view_as<bool>(kv.GetNum("easybhop", 1));
1450 gA_StyleSettings[i][bPrespeed] = view_as<bool>(kv.GetNum("prespeed", 0));
1451 gA_StyleSettings[i][fVelocityLimit] = kv.GetFloat("velocity_limit", 0.0);
1452 gA_StyleSettings[i][fAiraccelerate] = kv.GetFloat("airaccelerate", 1000.0);
1453 gA_StyleSettings[i][bEnableBunnyhopping] = view_as<bool>(kv.GetNum("bunnyhopping", 1));
1454 gA_StyleSettings[i][fRunspeed] = kv.GetFloat("runspeed", 260.00);
1455 gA_StyleSettings[i][fGravityMultiplier] = kv.GetFloat("gravity", 1.0);
1456 gA_StyleSettings[i][fSpeedMultiplier] = kv.GetFloat("speed", 1.0);
1457 gA_StyleSettings[i][bHalftime] = view_as<bool>(kv.GetNum("halftime", 0));
1458 gA_StyleSettings[i][fVelocity] = kv.GetFloat("velocity", 1.0);
1459 gA_StyleSettings[i][fBonusVelocity] = kv.GetFloat("bonus_velocity", 0.0);
1460 gA_StyleSettings[i][fMinVelocity] = kv.GetFloat("min_velocity", 0.0);
1461 gA_StyleSettings[i][bBlockW] = view_as<bool>(kv.GetNum("block_w", 0));
1462 gA_StyleSettings[i][bBlockA] = view_as<bool>(kv.GetNum("block_a", 0));
1463 gA_StyleSettings[i][bBlockS] = view_as<bool>(kv.GetNum("block_s", 0));
1464 gA_StyleSettings[i][bBlockD] = view_as<bool>(kv.GetNum("block_d", 0));
1465 gA_StyleSettings[i][bBlockUse] = view_as<bool>(kv.GetNum("block_use", 0));
1466 gA_StyleSettings[i][iForceHSW] = kv.GetNum("force_hsw", 0);
1467 gA_StyleSettings[i][bBlockPLeft] = view_as<bool>(kv.GetNum("block_pleft", 0));
1468 gA_StyleSettings[i][bBlockPRight] = view_as<bool>(kv.GetNum("block_pright", 0));
1469 gA_StyleSettings[i][iBlockPStrafe] = kv.GetNum("block_pstrafe", 0);
1470 gA_StyleSettings[i][bUnranked] = view_as<bool>(kv.GetNum("unranked", 0));
1471 gA_StyleSettings[i][bNoReplay] = view_as<bool>(kv.GetNum("noreplay", 0));
1472 gA_StyleSettings[i][bSync] = view_as<bool>(kv.GetNum("sync", 1));
1473 gA_StyleSettings[i][bStrafeCountW] = view_as<bool>(kv.GetNum("strafe_count_w", false));
1474 gA_StyleSettings[i][bStrafeCountA] = view_as<bool>(kv.GetNum("strafe_count_a", 1));
1475 gA_StyleSettings[i][bStrafeCountS] = view_as<bool>(kv.GetNum("strafe_count_s", false));
1476 gA_StyleSettings[i][bStrafeCountD] = view_as<bool>(kv.GetNum("strafe_count_d", 1));
1477 gA_StyleSettings[i][fRankingMultiplier] = kv.GetFloat("rankingmultiplier", 1.00);
1478 gA_StyleSettings[i][iSpecial] = kv.GetNum("special", 0);
1479
1480 if(!gB_Registered && strlen(gS_StyleStrings[i][sChangeCommand]) > 0)
1481 {
1482 char[][] sStyleCommands = new char[32][32];
1483 int iCommands = ExplodeString(gS_StyleStrings[i][sChangeCommand], ";", sStyleCommands, 32, 32, false);
1484
1485 char[] sDescription = new char[128];
1486 FormatEx(sDescription, 128, "Change style to %s.", gS_StyleStrings[i][sStyleName]);
1487
1488 for(int x = 0; x < iCommands; x++)
1489 {
1490 TrimString(sStyleCommands[x]);
1491 StripQuotes(sStyleCommands[x]);
1492
1493 char[] sCommand = new char[32];
1494 FormatEx(sCommand, 32, "sm_%s", sStyleCommands[x]);
1495
1496 gSM_StyleCommands.SetValue(sCommand, i);
1497
1498 RegConsoleCmd(sCommand, Command_StyleChange, sDescription);
1499 }
1500 }
1501
1502 i++;
1503 }
1504
1505 while(kv.GotoNextKey());
1506
1507 delete kv;
1508
1509 gI_Styles = i;
1510 gB_Registered = true;
1511
1512 return true;
1513}
1514
1515public Action Command_StyleChange(int client, int args)
1516{
1517 char[] sCommand = new char[128];
1518 GetCmdArg(0, sCommand, 128);
1519
1520 int style = 0;
1521
1522 if(gSM_StyleCommands.GetValue(sCommand, style))
1523 {
1524 ChangeClientStyle(client, style, true);
1525
1526 return Plugin_Handled;
1527 }
1528
1529 return Plugin_Continue;
1530}
1531
1532bool LoadMessages()
1533{
1534 char[] sPath = new char[PLATFORM_MAX_PATH];
1535 BuildPath(Path_SM, sPath, PLATFORM_MAX_PATH, "configs/shavit-messages.cfg");
1536
1537 KeyValues kv = new KeyValues("shavit-messages");
1538
1539 if(!kv.ImportFromFile(sPath))
1540 {
1541 delete kv;
1542
1543 return false;
1544 }
1545
1546 kv.JumpToKey((IsSource2013(gEV_Type))? "CS:S":"CS:GO");
1547
1548 kv.GetString("prefix", gS_ChatStrings[sMessagePrefix], 128, "\x075e70d0[Timer]");
1549 kv.GetString("text", gS_ChatStrings[sMessageText], 128, "\x07ffffff");
1550 kv.GetString("warning", gS_ChatStrings[sMessageWarning], 128, "\x07af2a22");
1551 kv.GetString("variable", gS_ChatStrings[sMessageVariable], 128, "\x077fd772");
1552 kv.GetString("variable2", gS_ChatStrings[sMessageVariable2], 128, "\x07276f5c");
1553 kv.GetString("style", gS_ChatStrings[sMessageStyle], 128, "\x07db88c2");
1554
1555 delete kv;
1556
1557 for(int i = 0; i < CHATSETTINGS_SIZE; i++)
1558 {
1559 for(int x = 0; x < sizeof(gS_GlobalColorNames); x++)
1560 {
1561 ReplaceString(gS_ChatStrings[i], 128, gS_GlobalColorNames[x], gS_GlobalColors[x]);
1562 }
1563
1564 for(int x = 0; x < sizeof(gS_CSGOColorNames); x++)
1565 {
1566 ReplaceString(gS_ChatStrings[i], 128, gS_CSGOColorNames[x], gS_CSGOColors[x]);
1567 }
1568
1569 ReplaceString(gS_ChatStrings[i], 128, "{RGB}", "\x07");
1570 ReplaceString(gS_ChatStrings[i], 128, "{RGBA}", "\x08");
1571 }
1572
1573 return true;
1574}
1575
1576void SQL_SetPrefix()
1577{
1578 char[] sFile = new char[PLATFORM_MAX_PATH];
1579 BuildPath(Path_SM, sFile, PLATFORM_MAX_PATH, "configs/shavit-prefix.txt");
1580
1581 File fFile = OpenFile(sFile, "r");
1582
1583 if(fFile == null)
1584 {
1585 SetFailState("Cannot open \"configs/shavit-prefix.txt\". Make sure this file exists and that the server has read permissions to it.");
1586 }
1587
1588 char[] sLine = new char[PLATFORM_MAX_PATH*2];
1589
1590 while(fFile.ReadLine(sLine, PLATFORM_MAX_PATH*2))
1591 {
1592 TrimString(sLine);
1593 strcopy(gS_MySQLPrefix, 32, sLine);
1594
1595 break;
1596 }
1597
1598 delete fFile;
1599}
1600
1601void SQL_DBConnect()
1602{
1603 if(gH_SQL != null)
1604 {
1605 delete gH_SQL;
1606 }
1607
1608 char[] sError = new char[255];
1609
1610 if(SQL_CheckConfig("shavit")) // can't be asynced as we have modules that require this database connection instantly
1611 {
1612 gH_SQL = SQL_Connect("shavit", true, sError, 255);
1613
1614 if(gH_SQL == null)
1615 {
1616 SetFailState("Timer startup failed. Reason: %s", sError);
1617 }
1618 }
1619
1620 else
1621 {
1622 gH_SQL = SQLite_UseDatabase("shavit", sError, 255);
1623 }
1624
1625 // support unicode names
1626 gH_SQL.SetCharset("utf8");
1627
1628 Call_StartForward(gH_Forwards_OnDatabaseLoaded);
1629 Call_Finish();
1630
1631 char[] sDriver = new char[8];
1632 gH_SQL.Driver.GetIdentifier(sDriver, 8);
1633 gB_MySQL = StrEqual(sDriver, "mysql", false);
1634
1635 char[] sQuery = new char[512];
1636
1637 if(gB_MySQL)
1638 {
1639 FormatEx(sQuery, 512, "CREATE TABLE IF NOT EXISTS `%susers` (`auth` CHAR(32) NOT NULL, `name` VARCHAR(32), `country` CHAR(32), `ip` CHAR(64), `lastlogin` INT NOT NULL DEFAULT -1, `points` FLOAT NOT NULL DEFAULT 0, PRIMARY KEY (`auth`), INDEX `points` (`points`)) ENGINE=INNODB;", gS_MySQLPrefix);
1640 }
1641
1642 else
1643 {
1644 FormatEx(sQuery, 512, "CREATE TABLE IF NOT EXISTS `%susers` (`auth` CHAR(32) NOT NULL PRIMARY KEY, `name` VARCHAR(32), `country` CHAR(32), `ip` CHAR(64), `lastlogin` INTEGER NOT NULL DEFAULT -1, `points` FLOAT NOT NULL DEFAULT 0);", gS_MySQLPrefix);
1645 }
1646
1647 // CREATE TABLE IF NOT EXISTS
1648 gH_SQL.Query(SQL_CreateTable_Callback, sQuery);
1649}
1650
1651public void SQL_CreateTable_Callback(Database db, DBResultSet results, const char[] error, any data)
1652{
1653 if(results == null)
1654 {
1655 LogError("Timer error! Users' data table creation failed. Reason: %s", error);
1656
1657 return;
1658 }
1659
1660 char[] sQuery = new char[192];
1661 FormatEx(sQuery, 192, "SELECT lastlogin FROM %susers LIMIT 1;", gS_MySQLPrefix);
1662 gH_SQL.Query(SQL_TableMigration1_Callback, sQuery, 0, DBPrio_High);
1663
1664 FormatEx(sQuery, 192, "SELECT points FROM %susers LIMIT 1;", gS_MySQLPrefix);
1665 gH_SQL.Query(SQL_TableMigration2_Callback, sQuery, 0, DBPrio_High);
1666
1667 char sTables[][] =
1668 {
1669 "maptiers",
1670 "mapzones",
1671 "playertimes"
1672 };
1673
1674 for(int i = 0; i < sizeof(sTables); i++)
1675 {
1676 DataPack dp = new DataPack();
1677 dp.WriteString(sTables[i]);
1678
1679 FormatEx(sQuery, 192, "SELECT map FROM %s%s WHERE map LIKE 'workshop%%' GROUP BY map;", gS_MySQLPrefix, sTables[i]);
1680 gH_SQL.Query(SQL_TableMigration3_Callback, sQuery, dp, DBPrio_Low);
1681 }
1682}
1683
1684public void SQL_TableMigration1_Callback(Database db, DBResultSet results, const char[] error, any data)
1685{
1686 if(results == null)
1687 {
1688 char[] sQuery = new char[128];
1689 FormatEx(sQuery, 128, "ALTER TABLE `%susers` ADD %s;", gS_MySQLPrefix, (gB_MySQL)? "(`lastlogin` INT NOT NULL DEFAULT -1)":"COLUMN `lastlogin` INTEGER NOT NULL DEFAULT -1");
1690 gH_SQL.Query(SQL_AlterTable1_Callback, sQuery);
1691 }
1692}
1693
1694public void SQL_AlterTable1_Callback(Database db, DBResultSet results, const char[] error, any data)
1695{
1696 if(results == null)
1697 {
1698 LogError("Timer error! Table alteration 1 (core) failed. Reason: %s", error);
1699
1700 return;
1701 }
1702}
1703
1704public void SQL_TableMigration2_Callback(Database db, DBResultSet results, const char[] error, any data)
1705{
1706 if(results == null)
1707 {
1708 char[] sQuery = new char[128];
1709 FormatEx(sQuery, 128, "ALTER TABLE `%susers` ADD %s;", gS_MySQLPrefix, (gB_MySQL)? "(`points` FLOAT NOT NULL DEFAULT 0)":"COLUMN `points` FLOAT NOT NULL DEFAULT 0");
1710 gH_SQL.Query(SQL_AlterTable2_Callback, sQuery);
1711 }
1712}
1713
1714public void SQL_AlterTable2_Callback(Database db, DBResultSet results, const char[] error, any data)
1715{
1716 if(results == null)
1717 {
1718 LogError("Timer error! Table alteration 2 (core) failed. Reason: %s", error);
1719
1720 return;
1721 }
1722}
1723
1724public void SQL_TableMigration3_Callback(Database db, DBResultSet results, const char[] error, DataPack data)
1725{
1726 char[] sTable = new char[16];
1727
1728 data.Reset();
1729 data.ReadString(sTable, 16);
1730 delete data;
1731
1732 if(results == null || results.RowCount == 0)
1733 {
1734 // no error logging here because not everyone runs the rankings/wr modules
1735 return;
1736 }
1737
1738 while(results.FetchRow())
1739 {
1740 char[] sMap = new char[160];
1741 results.FetchString(0, sMap, 160);
1742
1743 char[] sDisplayMap = new char[160];
1744 GetMapDisplayName(sMap, sDisplayMap, 160);
1745
1746 char[] sQuery = new char[256];
1747 FormatEx(sQuery, 256, "UPDATE %s%s SET map = '%s' WHERE map = '%s';", gS_MySQLPrefix, sTable, sDisplayMap, sMap);
1748 gH_SQL.Query(SQL_AlterTable3_Callback, sQuery, 0, DBPrio_High);
1749 }
1750}
1751
1752public void SQL_AlterTable3_Callback(Database db, DBResultSet results, const char[] error, any data)
1753{
1754 if(results == null)
1755 {
1756 LogError("Timer error! Table alteration 3 (core) failed. Reason: %s", error);
1757
1758 return;
1759 }
1760}
1761
1762public void PreThinkPost(int client)
1763{
1764 if(IsPlayerAlive(client))
1765 {
1766 sv_airaccelerate.FloatValue = view_as<float>(gA_StyleSettings[gBS_Style[client]][fAiraccelerate]);
1767
1768 if(sv_enablebunnyhopping != null)
1769 {
1770 sv_enablebunnyhopping.BoolValue = view_as<bool>(gA_StyleSettings[gBS_Style[client]][bEnableBunnyhopping]);
1771 }
1772 }
1773}
1774
1775public void OnGameFrame()
1776{
1777 float frametime = GetGameFrameTime();
1778
1779 for(int i = 1; i <= MaxClients; i++)
1780 {
1781 if(gB_ClientPaused[i] || !gB_TimerEnabled[i])
1782 {
1783 continue;
1784 }
1785
1786 float time = frametime;
1787
1788 if(gA_StyleSettings[gBS_Style[i]][bHalftime])
1789 {
1790 time /= 2.0;
1791 }
1792
1793 any[] snapshot = new any[TIMERSNAPSHOT_SIZE];
1794 snapshot[bTimerEnabled] = gB_TimerEnabled[i];
1795 snapshot[bClientPaused] = gB_ClientPaused[i];
1796 snapshot[iJumps] = gI_Jumps[i];
1797 snapshot[bsStyle] = gBS_Style[i];
1798 snapshot[iStrafes] = gI_Strafes[i];
1799 snapshot[iTotalMeasures] = gI_TotalMeasures[i];
1800 snapshot[iGoodGains] = gI_GoodGains[i];
1801 snapshot[fServerTime] = GetEngineTime();
1802 snapshot[fCurrentTime] = gF_PlayerTimer[i];
1803 snapshot[iSHSWCombination] = gI_SHSW_FirstCombination[i];
1804 snapshot[iTimerTrack] = gI_Track[i];
1805
1806 Call_StartForward(gH_Forwards_OnTimerIncrement);
1807 Call_PushCell(i);
1808 Call_PushArray(snapshot, TIMERSNAPSHOT_SIZE);
1809 Call_PushCellRef(time);
1810 Call_PushArray(gA_StyleSettings[gBS_Style[i]], STYLESETTINGS_SIZE);
1811 Call_Finish();
1812
1813 gF_PlayerTimer[i] += time;
1814
1815 Call_StartForward(gH_Forwards_OnTimerIncrementPost);
1816 Call_PushCell(i);
1817 Call_PushCell(time);
1818 Call_PushArray(gA_StyleSettings[gBS_Style[i]], STYLESETTINGS_SIZE);
1819 Call_Finish();
1820 }
1821}
1822
1823public Action Event_RoundStart(Event event, const char[] name, bool dontBroadcast)
1824{
1825 int entity = -1;
1826 while((entity = FindEntityByClassname(entity, "trigger_push")) != -1)
1827 {
1828 SDKHook(entity, SDKHook_Touch, OnTouch);
1829
1830 }
1831}
1832
1833stock int FindEntityByTargetname(const char[] targetname)
1834{
1835 int entity = -1;
1836 char sName[64];
1837 while ((entity = FindEntityByClassname(entity, "filter_activator_name")) != -1)
1838 {
1839 GetEntPropString(entity, Prop_Data, "m_iName", sName, 64);
1840 if (StrEqual(sName, targetname))
1841 {
1842 return entity;
1843 }
1844 }
1845
1846 return -1;
1847}
1848
1849public Action OnTouch(int client)
1850{
1851 PrintToChatAll("push by client %s STYLE: %s", client, gA_StyleSettings[gBS_Style[client]]);
1852}
1853
1854
1855
1856public Action OnPlayerRunCmd(int client, int &buttons, int &impulse, float vel[3], float angles[3], int &weapon, int &subtype, int &cmdnum, int &tickcount, int &seed, int mouse[2])
1857{
1858 if(!IsPlayerAlive(client) || IsFakeClient(client))
1859 {
1860 return Plugin_Continue;
1861 }
1862
1863 int flags = GetEntityFlags(client);
1864
1865 if(gB_ClientPaused[client])
1866 {
1867 buttons = 0;
1868 vel = view_as<float>({0.0, 0.0, 0.0});
1869
1870 SetEntityFlags(client, (flags | FL_ATCONTROLS));
1871
1872 return Plugin_Changed;
1873 }
1874
1875 SetEntityFlags(client, (flags & ~FL_ATCONTROLS));
1876
1877 Action result = Plugin_Continue;
1878 Call_StartForward(gH_Forwards_OnUserCmdPre);
1879 Call_PushCell(client);
1880 Call_PushCellRef(buttons);
1881 Call_PushCellRef(impulse);
1882 Call_PushArrayEx(vel, 3, SM_PARAM_COPYBACK);
1883 Call_PushArrayEx(angles, 3, SM_PARAM_COPYBACK);
1884 Call_PushCell(GetTimerStatus(client));
1885 Call_PushCell(gI_Track[client]);
1886 Call_PushCell(gBS_Style[client]);
1887 Call_PushArray(gA_StyleSettings[gBS_Style[client]], STYLESETTINGS_SIZE);
1888 Call_PushArrayEx(mouse, 2, SM_PARAM_COPYBACK);
1889 Call_Finish(result);
1890
1891 if(result != Plugin_Continue && result != Plugin_Changed)
1892 {
1893 return result;
1894 }
1895
1896 int iGroundEntity = GetEntPropEnt(client, Prop_Send, "m_hGroundEntity");
1897 bool bInStart = Shavit_InsideZone(client, Zone_Start, gI_Track[client]);
1898
1899 if(gB_TimerEnabled[client] && !gB_ClientPaused[client])
1900 {
1901 char[] sCheatDetected = new char[64];
1902
1903 // +left/right block
1904 if(gB_LeftRight && (!gB_Zones || !bInStart && ((gA_StyleSettings[gBS_Style[client]][bBlockPLeft] &&
1905 (buttons & IN_LEFT) > 0) || (gA_StyleSettings[gBS_Style[client]][bBlockPRight] && (buttons & IN_RIGHT) > 0))))
1906 {
1907 FormatEx(sCheatDetected, 64, "%T", "LeftRightCheat", client);
1908 StopTimer_Cheat(client, sCheatDetected);
1909 }
1910
1911 // +strafe block
1912 if(gA_StyleSettings[gBS_Style[client]][iBlockPStrafe] > 0 &&
1913 ((vel[0] > 0.0 && (buttons & IN_FORWARD) == 0) || (vel[0] < 0.0 && (buttons & IN_BACK) == 0) ||
1914 (vel[1] > 0.0 && (buttons & IN_MOVERIGHT) == 0) || (vel[1] < 0.0 && (buttons & IN_MOVELEFT) == 0)))
1915 {
1916 if(gF_StrafeWarning[client] < gF_PlayerTimer[client])
1917 {
1918 if(gA_StyleSettings[gBS_Style[client]][iBlockPStrafe] >= 2)
1919 {
1920 FormatEx(sCheatDetected, 64, "%T", "Inconsistencies", client);
1921 StopTimer_Cheat(client, sCheatDetected);
1922 }
1923
1924 vel[0] = 0.0;
1925 vel[1] = 0.0;
1926
1927 return Plugin_Changed;
1928 }
1929
1930 gF_StrafeWarning[client] = gF_PlayerTimer[client] + 0.3;
1931 }
1932 }
1933
1934 #if defined DEBUG
1935 static int cycle = 0;
1936
1937 if(++cycle % 50 == 0)
1938 {
1939 Shavit_StopChatSound();
1940 Shavit_PrintToChat(client, "vel[0]: %.01f | vel[1]: %.01f", vel[0], vel[1]);
1941 }
1942 #endif
1943
1944 MoveType mtMoveType = GetEntityMoveType(client);
1945 bool bOnLadder = (mtMoveType == MOVETYPE_LADDER);
1946
1947 //boostercheck
1948
1949
1950 // key blocking
1951 if(!bOnLadder && !Shavit_InsideZone(client, Zone_Freestyle, -1))
1952 {
1953 // block E
1954 if(gA_StyleSettings[gBS_Style[client]][bBlockUse] && (buttons & IN_USE) > 0)
1955 {
1956 buttons &= ~IN_USE;
1957 }
1958
1959 if(iGroundEntity == -1)
1960 {
1961 PrintToChatAll("push by client %s STYLE: %s", client, gA_StyleSettings[gBS_Style[client]]);
1962 if(gA_StyleSettings[gBS_Style[client]][bBlockW] && ((buttons & IN_FORWARD) > 0 || vel[0] > 0.0))
1963 {
1964 vel[0] = 0.0;
1965 buttons &= ~IN_FORWARD;
1966 }
1967
1968 if(gA_StyleSettings[gBS_Style[client]][bBlockA] && ((buttons & IN_MOVELEFT) > 0 || vel[1] < 0.0))
1969 {
1970 vel[1] = 0.0;
1971 buttons &= ~IN_MOVELEFT;
1972 }
1973
1974 if(gA_StyleSettings[gBS_Style[client]][bBlockS] && ((buttons & IN_BACK) > 0 || vel[0] < 0.0))
1975 {
1976 vel[0] = 0.0;
1977 buttons &= ~IN_BACK;
1978 }
1979
1980 if(gA_StyleSettings[gBS_Style[client]][bBlockD] && ((buttons & IN_MOVERIGHT) > 0 || vel[1] > 0.0))
1981 {
1982 vel[1] = 0.0;
1983 buttons &= ~IN_MOVERIGHT;
1984 }
1985
1986 // HSW
1987 // Theory about blocking non-HSW strafes while playing HSW:
1988 // Block S and W without A or D.
1989 // Block A and D without S or W.
1990 if(gA_StyleSettings[gBS_Style[client]][iForceHSW] > 0)
1991 {
1992 bool bSHSW = (gA_StyleSettings[gBS_Style[client]][iForceHSW] == 2) && !bInStart; // don't decide on the first valid input until out of start zone!
1993 int iCombination = -1;
1994
1995 bool bForward = ((buttons & IN_FORWARD) > 0 && vel[0] >= 200.0);
1996 bool bMoveLeft = ((buttons & IN_MOVELEFT) > 0 && vel[1] <= -200.0);
1997 bool bBack = ((buttons & IN_BACK) > 0 && vel[0] <= -200.0);
1998 bool bMoveRight = ((buttons & IN_MOVERIGHT) > 0 && vel[1] >= 200.0);
1999
2000 if(bSHSW)
2001 {
2002 if((bForward && bMoveLeft) || (bBack && bMoveRight))
2003 {
2004 iCombination = 0;
2005 }
2006
2007 else if((bForward && bMoveRight || bBack && bMoveLeft))
2008 {
2009 iCombination = 1;
2010 }
2011
2012 // int gI_SHSW_FirstCombination[MAXPLAYERS+1]; // 0 - W/A S/D | 1 - W/D S/A
2013 if(gI_SHSW_FirstCombination[client] == -1 && iCombination != -1)
2014 {
2015 Shavit_PrintToChat(client, "%T", (iCombination == 0)? "SHSWCombination0":"SHSWCombination1", client, gS_ChatStrings[sMessageVariable], gS_ChatStrings[sMessageText]);
2016 gI_SHSW_FirstCombination[client] = iCombination;
2017 }
2018
2019 bool bStop = false;
2020
2021 // W/A S/D
2022 if((gI_SHSW_FirstCombination[client] == 0 && iCombination != 0) ||
2023 // W/D S/A
2024 (gI_SHSW_FirstCombination[client] == 1 && iCombination != 1) ||
2025 // no valid combination & no valid input
2026 (gI_SHSW_FirstCombination[client] == -1 && iCombination == -1))
2027 {
2028 bStop = true;
2029 }
2030
2031 if(bStop)
2032 {
2033 vel[0] = 0.0;
2034 vel[1] = 0.0;
2035
2036 buttons &= ~IN_FORWARD;
2037 buttons &= ~IN_MOVELEFT;
2038 buttons &= ~IN_MOVERIGHT;
2039 buttons &= ~IN_BACK;
2040 }
2041 }
2042
2043 else
2044 {
2045 if((bForward || bBack) && !(bMoveLeft || bMoveRight))
2046 {
2047 vel[0] = 0.0;
2048
2049 buttons &= ~IN_FORWARD;
2050 buttons &= ~IN_BACK;
2051 }
2052
2053 if((bMoveLeft || bMoveRight) && !(bForward || bBack))
2054 {
2055 vel[1] = 0.0;
2056
2057 buttons &= ~IN_MOVELEFT;
2058 buttons &= ~IN_MOVERIGHT;
2059 }
2060 }
2061 }
2062 }
2063 }
2064
2065 if(gA_StyleSettings[gBS_Style[client]][bStrafeCountW] && !gA_StyleSettings[gBS_Style[client]][bBlockW] &&
2066 (gI_ButtonCache[client] & IN_FORWARD) == 0 && (buttons & IN_FORWARD) > 0)
2067 {
2068 gI_Strafes[client]++;
2069 }
2070
2071 if(gA_StyleSettings[gBS_Style[client]][bStrafeCountA] && !gA_StyleSettings[gBS_Style[client]][bBlockA] && (gI_ButtonCache[client] & IN_MOVELEFT) == 0 &&
2072 (buttons & IN_MOVELEFT) > 0 && (gA_StyleSettings[gBS_Style[client]][iForceHSW] > 0 || ((buttons & IN_FORWARD) == 0 && (buttons & IN_BACK) == 0)))
2073 {
2074 gI_Strafes[client]++;
2075 }
2076
2077 if(gA_StyleSettings[gBS_Style[client]][bStrafeCountS] && !gA_StyleSettings[gBS_Style[client]][bBlockS] &&
2078 (gI_ButtonCache[client] & IN_BACK) == 0 && (buttons & IN_BACK) > 0)
2079 {
2080 gI_Strafes[client]++;
2081 }
2082
2083 if(gA_StyleSettings[gBS_Style[client]][bStrafeCountD] && !gA_StyleSettings[gBS_Style[client]][bBlockD] && (gI_ButtonCache[client] & IN_MOVERIGHT) == 0 &&
2084 (buttons & IN_MOVERIGHT) > 0 && (gA_StyleSettings[gBS_Style[client]][iForceHSW] > 0 || ((buttons & IN_FORWARD) == 0 && (buttons & IN_BACK) == 0)))
2085 {
2086 gI_Strafes[client]++;
2087 }
2088
2089 bool bInWater = (GetEntProp(client, Prop_Send, "m_nWaterLevel") >= 2);
2090
2091 // enable duck-jumping/bhop in tf2
2092 if(gEV_Type == Engine_TF2 && gA_StyleSettings[gBS_Style[client]][bEnableBunnyhopping] && (buttons & IN_JUMP) > 0 && iGroundEntity != -1)
2093 {
2094 float fSpeed[3];
2095 GetEntPropVector(client, Prop_Data, "m_vecAbsVelocity", fSpeed);
2096
2097 fSpeed[2] = 271.0;
2098 SetEntPropVector(client, Prop_Data, "m_vecAbsVelocity", fSpeed);
2099 }
2100
2101 if(gA_StyleSettings[gBS_Style[client]][bAutobhop] && gB_Autobhop && gB_Auto[client] && (buttons & IN_JUMP) > 0 && mtMoveType == MOVETYPE_WALK && !bInWater)
2102 {
2103 int iOldButtons = GetEntProp(client, Prop_Data, "m_nOldButtons");
2104 SetEntProp(client, Prop_Data, "m_nOldButtons", iOldButtons & ~IN_JUMP);
2105 }
2106
2107 else if(gB_DoubleSteps[client] && (buttons & IN_JUMP) == 0)
2108 {
2109 buttons |= IN_JUMP;
2110 }
2111
2112 if(bInStart && gB_BlockPreJump && !gA_StyleSettings[gBS_Style[client]][bPrespeed] && (vel[2] > 0 || (buttons & IN_JUMP) > 0))
2113 {
2114 vel[2] = 0.0;
2115 buttons &= ~IN_JUMP;
2116 }
2117
2118 // velocity limit
2119 if(iGroundEntity != -1 && view_as<float>(gA_StyleSettings[gBS_Style[client]][fVelocityLimit] > 0.0) &&
2120 (!gB_Zones || !Shavit_InsideZone(client, Zone_NoVelLimit, -1)))
2121 {
2122 float fSpeed[3];
2123 GetEntPropVector(client, Prop_Data, "m_vecVelocity", fSpeed);
2124
2125 float fSpeed_New = (SquareRoot(Pow(fSpeed[0], 2.0) + Pow(fSpeed[1], 2.0)));
2126
2127 if(fSpeed_New > 0.0)
2128 {
2129 float fScale = view_as<float>(gA_StyleSettings[gBS_Style[client]][fVelocityLimit]) / fSpeed_New;
2130
2131 if(fScale < 1.0)
2132 {
2133 ScaleVector(fSpeed, fScale);
2134 TeleportEntity(client, NULL_VECTOR, NULL_VECTOR, fSpeed); // maybe change this to SetEntPropVector some time?
2135 }
2136 }
2137 }
2138
2139 float fAngle = (angles[1] - gF_AngleCache[client]);
2140
2141 while(fAngle > 180.0)
2142 {
2143 fAngle -= 360.0;
2144 }
2145
2146 while(fAngle < -180.0)
2147 {
2148 fAngle += 360.0;
2149 }
2150
2151 if(iGroundEntity == -1 && (GetEntityFlags(client) & FL_INWATER) == 0 && fAngle != 0.0)
2152 {
2153 float fAbsVelocity[3];
2154 GetEntPropVector(client, Prop_Data, "m_vecAbsVelocity", fAbsVelocity);
2155
2156 if(SquareRoot(Pow(fAbsVelocity[0], 2.0) + Pow(fAbsVelocity[1], 2.0)) > 0.0)
2157 {
2158 float fTempAngle = angles[1];
2159
2160 float fAngles[3];
2161 GetVectorAngles(fAbsVelocity, fAngles);
2162
2163 if(fTempAngle < 0.0)
2164 {
2165 fTempAngle += 360.0;
2166 }
2167
2168 float fDirectionAngle = (fTempAngle - fAngles[1]);
2169
2170 if(fDirectionAngle < 0.0)
2171 {
2172 fDirectionAngle = -fDirectionAngle;
2173 }
2174
2175 if(fDirectionAngle < 22.5 || fDirectionAngle > 337.5)
2176 {
2177 gI_TotalMeasures[client]++;
2178
2179 if((fAngle > 0.0 && vel[1] < 0.0) || (fAngle < 0.0 && vel[1] > 0.0))
2180 {
2181 gI_GoodGains[client]++;
2182 }
2183 }
2184
2185 else if((fDirectionAngle > 67.5 && fDirectionAngle < 112.5) || (fDirectionAngle > 247.5 && fDirectionAngle < 292.5))
2186 {
2187 gI_TotalMeasures[client]++;
2188
2189 if(vel[0] != 0.0)
2190 {
2191 gI_GoodGains[client]++;
2192 }
2193 }
2194 }
2195 }
2196
2197 gI_ButtonCache[client] = buttons;
2198 gF_AngleCache[client] = angles[1];
2199
2200 return Plugin_Continue;
2201}
2202
2203void StopTimer_Cheat(int client, const char[] message)
2204{
2205 Shavit_StopTimer(client);
2206 Shavit_PrintToChat(client, "%T", "CheatTimerStop", client, gS_ChatStrings[sMessageWarning], gS_ChatStrings[sMessageText], message);
2207}
2208
2209void UpdateAutoBhop(int client)
2210{
2211 if(sv_autobunnyhopping != null)
2212 {
2213 sv_autobunnyhopping.ReplicateToClient(client, (gA_StyleSettings[gBS_Style[client]][bAutobhop] && gB_Autobhop && gB_Auto[client])? "1":"0");
2214 }
2215}
2216
2217void UpdateAiraccelerate(int client)
2218{
2219 char[] sAiraccelerate = new char[8];
2220 FloatToString(gA_StyleSettings[gBS_Style[client]][fAiraccelerate], sAiraccelerate, 8);
2221 sv_airaccelerate.ReplicateToClient(client, sAiraccelerate);
2222}
2223
2224void UpdateBunnyhopping(int client)
2225{
2226 if(sv_enablebunnyhopping != null)
2227 {
2228 sv_enablebunnyhopping.ReplicateToClient(client, (gA_StyleSettings[gBS_Style[client]][bEnableBunnyhopping])? "1":"0");
2229 }
2230}