· 8 years ago · Feb 08, 2018, 02:40 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
433public void OnMapEnd()
434{
435 gB_KZMap = false;
436}
437
438public Action Command_StartTimer(int client, int args)
439{
440 if(!IsValidClient(client))
441 {
442 return Plugin_Handled;
443 }
444
445 char[] sCommand = new char[16];
446 GetCmdArg(0, sCommand, 16);
447
448 if(!gB_Restart)
449 {
450 if(args != -1)
451 {
452 Shavit_PrintToChat(client, "%T", "CommandDisabled", client, gS_ChatStrings[sMessageVariable], sCommand, gS_ChatStrings[sMessageText]);
453 }
454
455 return Plugin_Handled;
456 }
457
458 int track = Track_Main;
459
460 if(StrContains(sCommand, "sm_b", false) == 0)
461 {
462 track = Track_Bonus;
463 }
464
465 if(gB_AllowTimerWithoutZone || (gB_Zones && (Shavit_ZoneExists(Zone_Start, track) || gB_KZMap)))
466 {
467 Call_StartForward(gH_Forwards_OnRestart);
468 Call_PushCell(client);
469 Call_PushCell(track);
470 Call_Finish();
471
472 if(gB_AllowTimerWithoutZone)
473 {
474 StartTimer(client, track);
475 }
476 }
477
478 else
479 {
480 Shavit_PrintToChat(client, "%T", "StartZoneUndefined", client, gS_ChatStrings[sMessageWarning], gS_ChatStrings[sMessageText]);
481 }
482
483 return Plugin_Handled;
484}
485
486public Action Command_TeleportEnd(int client, int args)
487{
488 if(!IsValidClient(client))
489 {
490 return Plugin_Handled;
491 }
492
493 char[] sCommand = new char[16];
494 GetCmdArg(0, sCommand, 16);
495
496 int track = Track_Main;
497
498 if(StrContains(sCommand, "sm_b", false) == 0)
499 {
500 track = Track_Bonus;
501 }
502
503 if(gB_Zones && (Shavit_ZoneExists(Zone_End, track) || gB_KZMap))
504 {
505 Shavit_StopTimer(client);
506
507 Call_StartForward(gH_Forwards_OnEnd);
508 Call_PushCell(client);
509 Call_PushCell(track);
510 Call_Finish();
511 }
512
513 else
514 {
515 Shavit_PrintToChat(client, "%T", "EndZoneUndefined", client, gS_ChatStrings[sMessageWarning], gS_ChatStrings[sMessageText]);
516 }
517
518 return Plugin_Handled;
519}
520
521public Action Command_StopTimer(int client, int args)
522{
523 if(!IsValidClient(client))
524 {
525 return Plugin_Handled;
526 }
527
528 Shavit_StopTimer(client);
529
530 return Plugin_Handled;
531}
532
533public Action Command_TogglePause(int client, int args)
534{
535 if(!IsValidClient(client) || !gB_TimerEnabled[client])
536 {
537 return Plugin_Handled;
538 }
539
540 if(Shavit_InsideZone(client, Zone_Start, gI_Track[client]))
541 {
542 Shavit_PrintToChat(client, "%T", "PauseStartZone", client, gS_ChatStrings[sMessageText], gS_ChatStrings[sMessageWarning], gS_ChatStrings[sMessageText], gS_ChatStrings[sMessageVariable], gS_ChatStrings[sMessageText]);
543
544 return Plugin_Handled;
545 }
546
547 if(!gB_Pause)
548 {
549 char[] sCommand = new char[16];
550 GetCmdArg(0, sCommand, 16);
551
552 Shavit_PrintToChat(client, "%T", "CommandDisabled", client, gS_ChatStrings[sMessageVariable], sCommand, gS_ChatStrings[sMessageText]);
553
554 return Plugin_Handled;
555 }
556
557 if(GetEntPropEnt(client, Prop_Send, "m_hGroundEntity") == -1 && GetEntityMoveType(client) != MOVETYPE_LADDER)
558 {
559 Shavit_PrintToChat(client, "%T", "PauseNotOnGround", client, gS_ChatStrings[sMessageWarning], gS_ChatStrings[sMessageText]);
560
561 return Plugin_Handled;
562 }
563
564 if(gB_ClientPaused[client])
565 {
566 TeleportEntity(client, gF_PausePosition[client][0], gF_PausePosition[client][1], gF_PausePosition[client][2]);
567 ResumeTimer(client);
568
569 Shavit_PrintToChat(client, "%T", "MessageUnpause", client, gS_ChatStrings[sMessageText], gS_ChatStrings[sMessageWarning], gS_ChatStrings[sMessageText]);
570 }
571
572 else
573 {
574 GetClientAbsOrigin(client, gF_PausePosition[client][0]);
575 GetClientEyeAngles(client, gF_PausePosition[client][1]);
576 GetEntPropVector(client, Prop_Data, "m_vecAbsVelocity", gF_PausePosition[client][2]);
577
578 PauseTimer(client);
579
580 Shavit_PrintToChat(client, "%T", "MessagePause", client, gS_ChatStrings[sMessageText], gS_ChatStrings[sMessageWarning], gS_ChatStrings[sMessageText]);
581 }
582
583 return Plugin_Handled;
584}
585
586#if defined DEBUG
587public Action Command_FinishTest(int client, int args)
588{
589 Shavit_FinishMap(client, gI_Track[client]);
590
591 return Plugin_Handled;
592}
593#endif
594
595public Action Command_AutoBhop(int client, int args)
596{
597 if(!IsValidClient(client))
598 {
599 return Plugin_Handled;
600 }
601
602 gB_Auto[client] = !gB_Auto[client];
603
604 if(gB_Auto[client])
605 {
606 Shavit_PrintToChat(client, "%T", "AutobhopEnabled", client, gS_ChatStrings[sMessageVariable2], gS_ChatStrings[sMessageText]);
607 }
608
609 else
610 {
611 Shavit_PrintToChat(client, "%T", "AutobhopDisabled", client, gS_ChatStrings[sMessageWarning], gS_ChatStrings[sMessageText]);
612 }
613
614 char[] sAutoBhop = new char[4];
615 IntToString(view_as<int>(gB_Auto[client]), sAutoBhop, 4);
616 SetClientCookie(client, gH_AutoBhopCookie, sAutoBhop);
617
618 UpdateAutoBhop(client);
619
620 return Plugin_Handled;
621}
622
623public Action Command_DoubleStep(int client, const char[] command, int args)
624{
625 gB_DoubleSteps[client] = (command[0] == '+');
626
627 return Plugin_Handled;
628}
629
630public Action Command_Style(int client, int args)
631{
632 if(!IsValidClient(client))
633 {
634 return Plugin_Handled;
635 }
636
637 Menu menu = new Menu(StyleMenu_Handler);
638 menu.SetTitle("%T", "StyleMenuTitle", client);
639
640 for(int i = 0; i < gI_Styles; i++)
641 {
642 char[] sInfo = new char[8];
643 IntToString(i, sInfo, 8);
644
645 char[] sDisplay = new char[64];
646
647 if(gA_StyleSettings[i][bUnranked])
648 {
649 FormatEx(sDisplay, 64, "%T %s", "StyleUnranked", client, gS_StyleStrings[i][sStyleName]);
650 }
651
652 else
653 {
654 float time = 0.0;
655
656 if(gB_WR)
657 {
658 Shavit_GetWRTime(i, time, Track_Main);
659 }
660
661 if(time > 0.0)
662 {
663 char[] sTime = new char[32];
664 FormatSeconds(time, sTime, 32, false);
665
666 FormatEx(sDisplay, 64, "%s - WR: %s", gS_StyleStrings[i][sStyleName], sTime);
667 }
668
669 else
670 {
671 strcopy(sDisplay, 64, gS_StyleStrings[i][sStyleName]);
672 }
673 }
674
675 menu.AddItem(sInfo, sDisplay, (gBS_Style[client] == i)? ITEMDRAW_DISABLED:ITEMDRAW_DEFAULT);
676 }
677
678 // should NEVER happen
679 if(menu.ItemCount == 0)
680 {
681 menu.AddItem("-1", "Nothing");
682 }
683
684 else if(menu.ItemCount <= ((gEV_Type == Engine_CSS)? 9:8))
685 {
686 menu.Pagination = MENU_NO_PAGINATION;
687 }
688
689 menu.ExitButton = true;
690 menu.Display(client, 20);
691
692 return Plugin_Handled;
693}
694
695public int StyleMenu_Handler(Menu menu, MenuAction action, int param1, int param2)
696{
697 if(action == MenuAction_Select)
698 {
699 char[] info = new char[16];
700 menu.GetItem(param2, info, 16);
701
702 int style = StringToInt(info);
703
704 if(style == -1)
705 {
706 return 0;
707 }
708
709 ChangeClientStyle(param1, style, true);
710 }
711
712 else if(action == MenuAction_End)
713 {
714 delete menu;
715 }
716
717 return 0;
718}
719
720void CallOnStyleChanged(int client, int oldstyle, int newstyle, bool manual)
721{
722 Call_StartForward(gH_Forwards_OnStyleChanged);
723 Call_PushCell(client);
724 Call_PushCell(oldstyle);
725 Call_PushCell(newstyle);
726 Call_PushCell(gI_Track[client]);
727 Call_PushCell(manual);
728 Call_Finish();
729
730 gBS_Style[client] = newstyle;
731
732 UpdateAutoBhop(client);
733 UpdateAiraccelerate(client);
734 UpdateBunnyhopping(client);
735}
736
737void ChangeClientStyle(int client, int style, bool manual)
738{
739 if(!IsValidClient(client))
740 {
741 return;
742 }
743
744 if(manual)
745 {
746 Shavit_PrintToChat(client, "%T", "StyleSelection", client, gS_ChatStrings[sMessageStyle], gS_StyleStrings[style][sStyleName], gS_ChatStrings[sMessageText]);
747 }
748
749 if(gA_StyleSettings[style][bUnranked])
750 {
751 Shavit_PrintToChat(client, "%T", "UnrankedWarning", client, gS_ChatStrings[sMessageWarning], gS_ChatStrings[sMessageText]);
752 }
753
754 int aa_old = RoundToZero(gA_StyleSettings[gBS_Style[client]][fAiraccelerate]);
755 int aa_new = RoundToZero(gA_StyleSettings[style][fAiraccelerate]);
756
757 if(aa_old != aa_new)
758 {
759 Shavit_PrintToChat(client, "%T", "NewAiraccelerate", client, aa_old, gS_ChatStrings[sMessageVariable], aa_new, gS_ChatStrings[sMessageText]);
760 }
761
762 CallOnStyleChanged(client, gBS_Style[client], style, manual);
763
764 StopTimer(client);
765
766 if(gB_AllowTimerWithoutZone || (gB_Zones && (Shavit_ZoneExists(Zone_Start, gI_Track[client]) || gB_KZMap)))
767 {
768 Call_StartForward(gH_Forwards_OnRestart);
769 Call_PushCell(client);
770 Call_PushCell(gI_Track[client]);
771 Call_Finish();
772 }
773
774 char[] sStyle = new char[4];
775 IntToString(style, sStyle, 4);
776
777 SetClientCookie(client, gH_StyleCookie, sStyle);
778}
779
780// used as an alternative for games where player_jump isn't a thing, such as TF2
781public void Bunnyhop_OnLeaveGround(int client, bool jumped, bool ladder)
782{
783 if(gB_HookedJump || !jumped || ladder)
784 {
785 return;
786 }
787
788 DoJump(client);
789}
790
791public void Player_Jump(Event event, const char[] name, bool dontBroadcast)
792{
793 DoJump(GetClientOfUserId(event.GetInt("userid")));
794}
795
796void DoJump(int client)
797{
798 if(gB_TimerEnabled[client])
799 {
800 gI_Jumps[client]++;
801 }
802
803 // TF2 doesn't use stamina
804 if(gEV_Type != Engine_TF2 && (gB_NoStaminaReset && gA_StyleSettings[gBS_Style[client]][bEasybhop]) || Shavit_InsideZone(client, Zone_Easybhop, gI_Track[client]))
805 {
806 SetEntPropFloat(client, Prop_Send, "m_flStamina", 0.0);
807 }
808
809 RequestFrame(VelocityChanges, GetClientSerial(client));
810}
811
812void VelocityChanges(int data)
813{
814 int client = GetClientFromSerial(data);
815
816 if(client == 0)
817 {
818 return;
819 }
820
821 if(view_as<float>(gA_StyleSettings[gBS_Style[client]][fGravityMultiplier]) != 1.0)
822 {
823 SetEntityGravity(client, view_as<float>(gA_StyleSettings[gBS_Style[client]][fGravityMultiplier]));
824 }
825
826 if(view_as<float>(gA_StyleSettings[gBS_Style[client]][fSpeedMultiplier]) != 1.0)
827 {
828 SetEntPropFloat(client, Prop_Data, "m_flLaggedMovementValue", view_as<float>(gA_StyleSettings[gBS_Style[client]][fSpeedMultiplier]));
829 }
830
831 float fAbsVelocity[3];
832 GetEntPropVector(client, Prop_Data, "m_vecAbsVelocity", fAbsVelocity);
833
834 float fSpeed = (SquareRoot(Pow(fAbsVelocity[0], 2.0) + Pow(fAbsVelocity[1], 2.0)));
835
836 if(fSpeed == 0.0)
837 {
838 return;
839 }
840
841 float fVelocityMultiplier = view_as<float>(gA_StyleSettings[gBS_Style[client]][fVelocity]);
842 float fVelocityBonus = view_as<float>(gA_StyleSettings[gBS_Style[client]][fBonusVelocity]);
843 float fMin = view_as<float>(gA_StyleSettings[gBS_Style[client]][fMinVelocity]);
844
845 if(fVelocityMultiplier != 0.0)
846 {
847 fAbsVelocity[0] *= fVelocityMultiplier;
848 fAbsVelocity[1] *= fVelocityMultiplier;
849 }
850
851 if(fVelocityBonus != 0.0)
852 {
853 float x = fSpeed / (fSpeed + fVelocityBonus);
854 fAbsVelocity[0] /= x;
855 fAbsVelocity[1] /= x;
856 }
857
858 if(fMin != 0.0 && fSpeed < fMin)
859 {
860 float x = (fSpeed / fMin);
861 fAbsVelocity[0] /= x;
862 fAbsVelocity[1] /= x;
863 }
864
865 if(!gB_VelocityTeleport)
866 {
867 SetEntPropVector(client, Prop_Data, "m_vecAbsVelocity", fAbsVelocity);
868 }
869
870 else
871 {
872 TeleportEntity(client, NULL_VECTOR, NULL_VECTOR, fAbsVelocity);
873 }
874}
875
876public void Player_Death(Event event, const char[] name, bool dontBroadcast)
877{
878 int client = GetClientOfUserId(event.GetInt("userid"));
879
880 ResumeTimer(client);
881 StopTimer(client);
882}
883
884public int Native_GetGameType(Handle handler, int numParams)
885{
886 return view_as<int>(gSG_Type);
887}
888
889public int Native_GetDatabase(Handle handler, int numParams)
890{
891 return view_as<int>(CloneHandle(gH_SQL, handler));
892}
893
894public int Native_GetDB(Handle handler, int numParams)
895{
896 SetNativeCellRef(1, gH_SQL);
897}
898
899public int Native_GetTimer(Handle handler, int numParams)
900{
901 // 1 - client
902 int client = GetNativeCell(1);
903
904 // 2 - time
905 SetNativeCellRef(2, gF_PlayerTimer[client]);
906 SetNativeCellRef(3, gI_Jumps[client]);
907 SetNativeCellRef(4, gBS_Style[client]);
908 SetNativeCellRef(5, gB_TimerEnabled[client]);
909}
910
911public int Native_GetClientTime(Handle handler, int numParams)
912{
913 return view_as<int>(gF_PlayerTimer[GetNativeCell(1)]);
914}
915
916public int Native_GetClientTrack(Handle handler, int numParams)
917{
918 return gI_Track[GetNativeCell(1)];
919}
920
921public int Native_GetClientJumps(Handle handler, int numParams)
922{
923 return gI_Jumps[GetNativeCell(1)];
924}
925
926public int Native_GetBhopStyle(Handle handler, int numParams)
927{
928 return gBS_Style[GetNativeCell(1)];
929}
930
931public int Native_GetTimerStatus(Handle handler, int numParams)
932{
933 return GetTimerStatus(GetNativeCell(1));
934}
935
936public int Native_IsKZMap(Handle handler, int numParams)
937{
938 return view_as<bool>(gB_KZMap);
939}
940
941public int Native_StartTimer(Handle handler, int numParams)
942{
943 StartTimer(GetNativeCell(1), GetNativeCell(2));
944}
945
946public int Native_StopTimer(Handle handler, int numParams)
947{
948 int client = GetNativeCell(1);
949
950 StopTimer(client);
951
952 Call_StartForward(gH_Forwards_Stop);
953 Call_PushCell(client);
954 Call_PushCell(gI_Track[client]);
955 Call_Finish();
956}
957
958public int Native_FinishMap(Handle handler, int numParams)
959{
960 int client = GetNativeCell(1);
961
962 any[] snapshot = new any[TIMERSNAPSHOT_SIZE];
963 snapshot[bTimerEnabled] = gB_TimerEnabled[client];
964 snapshot[bClientPaused] = gB_ClientPaused[client];
965 snapshot[iJumps] = gI_Jumps[client];
966 snapshot[bsStyle] = gBS_Style[client];
967 snapshot[iStrafes] = gI_Strafes[client];
968 snapshot[iTotalMeasures] = gI_TotalMeasures[client];
969 snapshot[iGoodGains] = gI_GoodGains[client];
970 snapshot[fServerTime] = GetEngineTime();
971 snapshot[fCurrentTime] = gF_PlayerTimer[client];
972 snapshot[iSHSWCombination] = gI_SHSW_FirstCombination[client];
973 snapshot[iTimerTrack] = gI_Track[client];
974
975 Action result = Plugin_Continue;
976 Call_StartForward(gH_Forwards_FinishPre);
977 Call_PushCell(client);
978 Call_PushArrayEx(snapshot, TIMERSNAPSHOT_SIZE, SM_PARAM_COPYBACK);
979 Call_Finish(result);
980
981 if(result != Plugin_Continue && result != Plugin_Changed)
982 {
983 return;
984 }
985
986 Call_StartForward(gH_Forwards_Finish);
987 Call_PushCell(client);
988
989 int style = 0;
990 int track = Track_Main;
991
992 if(result == Plugin_Continue)
993 {
994 Call_PushCell(style = gBS_Style[client]);
995 Call_PushCell(gF_PlayerTimer[client]);
996 Call_PushCell(gI_Jumps[client]);
997 Call_PushCell(gI_Strafes[client]);
998 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);
999 Call_PushCell(track = gI_Track[client]);
1000 }
1001
1002 else
1003 {
1004 Call_PushCell(style = snapshot[bsStyle]);
1005 Call_PushCell(snapshot[fCurrentTime]);
1006 Call_PushCell(snapshot[iJumps]);
1007 Call_PushCell(snapshot[iStrafes]);
1008 Call_PushCell((gA_StyleSettings[snapshot[bsStyle]][bSync])? (snapshot[iGoodGains] == 0)? 0.0:(snapshot[iGoodGains] / float(snapshot[iTotalMeasures]) * 100.0):-1.0);
1009 Call_PushCell(track = snapshot[iTimerTrack]);
1010 }
1011
1012 float oldtime = 0.0;
1013
1014 if(gB_WR)
1015 {
1016 Shavit_GetPlayerPB(client, style, oldtime, track);
1017 }
1018
1019 Call_PushCell(oldtime);
1020 Call_Finish();
1021
1022 StopTimer(client);
1023}
1024
1025public int Native_PauseTimer(Handle handler, int numParams)
1026{
1027 PauseTimer(GetNativeCell(1));
1028}
1029
1030public int Native_ResumeTimer(Handle handler, int numParams)
1031{
1032 ResumeTimer(GetNativeCell(1));
1033}
1034
1035public int Native_StopChatSound(Handle handler, int numParams)
1036{
1037 gB_StopChatSound = true;
1038}
1039
1040public int Native_PrintToChat(Handle handler, int numParams)
1041{
1042 int client = GetNativeCell(1);
1043 static int written = 0; // useless?
1044
1045 char[] buffer = new char[300];
1046 FormatNativeString(0, 2, 3, 300, written, buffer);
1047 Format(buffer, 300, "%s %s%s", gS_ChatStrings[sMessagePrefix], gS_ChatStrings[sMessageText], buffer);
1048
1049 if(IsSource2013(gEV_Type))
1050 {
1051 Handle hSayText2 = StartMessageOne("SayText2", client);
1052
1053 if(hSayText2 != null)
1054 {
1055 BfWriteByte(hSayText2, 0);
1056 BfWriteByte(hSayText2, !gB_StopChatSound);
1057 BfWriteString(hSayText2, buffer);
1058 }
1059
1060 EndMessage();
1061 }
1062
1063 else
1064 {
1065 PrintToChat(client, " %s", buffer);
1066 }
1067
1068 gB_StopChatSound = false;
1069}
1070
1071public int Native_RestartTimer(Handle handler, int numParams)
1072{
1073 int client = GetNativeCell(1);
1074 int track = GetNativeCell(2);
1075
1076 Call_StartForward(gH_Forwards_OnRestart);
1077 Call_PushCell(client);
1078 Call_PushCell(track);
1079 Call_Finish();
1080
1081 StartTimer(client, track);
1082}
1083
1084public int Native_GetStrafeCount(Handle handler, int numParams)
1085{
1086 return gI_Strafes[GetNativeCell(1)];
1087}
1088
1089public int Native_GetSync(Handle handler, int numParams)
1090{
1091 int client = GetNativeCell(1);
1092
1093 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);
1094}
1095
1096public int Native_GetStyleCount(Handle handler, int numParams)
1097{
1098 return (gI_Styles > 0)? gI_Styles:-1;
1099}
1100
1101public int Native_GetStyleSettings(Handle handler, int numParams)
1102{
1103 return SetNativeArray(2, gA_StyleSettings[GetNativeCell(1)], STYLESETTINGS_SIZE);
1104}
1105
1106public int Native_GetStyleStrings(Handle handler, int numParams)
1107{
1108 return SetNativeString(3, gS_StyleStrings[GetNativeCell(1)][GetNativeCell(2)], GetNativeCell(4));
1109}
1110
1111public int Native_GetChatStrings(Handle handler, int numParams)
1112{
1113 return SetNativeString(2, gS_ChatStrings[GetNativeCell(1)], GetNativeCell(3));
1114}
1115
1116public int Native_SetPracticeMode(Handle handler, int numParams)
1117{
1118 int client = GetNativeCell(1);
1119 bool practice = view_as<bool>(GetNativeCell(2));
1120 bool alert = view_as<bool>(GetNativeCell(3));
1121
1122 if(alert && practice && !gB_PracticeMode[client])
1123 {
1124 Shavit_PrintToChat(client, "%T", "PracticeModeAlert", client, gS_ChatStrings[sMessageWarning], gS_ChatStrings[sMessageText]);
1125 }
1126
1127 gB_PracticeMode[client] = practice;
1128}
1129
1130public int Native_IsPracticeMode(Handle handler, int numParams)
1131{
1132 return view_as<int>(gB_PracticeMode[GetNativeCell(1)]);
1133}
1134
1135public int Native_SaveSnapshot(Handle handler, int numParams)
1136{
1137 int client = GetNativeCell(1);
1138
1139 any[] snapshot = new any[TIMERSNAPSHOT_SIZE];
1140 snapshot[bTimerEnabled] = gB_TimerEnabled[client];
1141 snapshot[bClientPaused] = gB_ClientPaused[client];
1142 snapshot[iJumps] = gI_Jumps[client];
1143 snapshot[bsStyle] = gBS_Style[client];
1144 snapshot[iStrafes] = gI_Strafes[client];
1145 snapshot[iTotalMeasures] = gI_TotalMeasures[client];
1146 snapshot[iGoodGains] = gI_GoodGains[client];
1147 snapshot[fServerTime] = GetEngineTime();
1148 snapshot[fCurrentTime] = gF_PlayerTimer[client];
1149 snapshot[iSHSWCombination] = gI_SHSW_FirstCombination[client];
1150 snapshot[iTimerTrack] = gI_Track[client];
1151
1152 return SetNativeArray(2, snapshot, TIMERSNAPSHOT_SIZE);
1153}
1154
1155public int Native_LoadSnapshot(Handle handler, int numParams)
1156{
1157 int client = GetNativeCell(1);
1158
1159 any[] snapshot = new any[TIMERSNAPSHOT_SIZE];
1160 GetNativeArray(2, snapshot, TIMERSNAPSHOT_SIZE);
1161
1162 gI_Track[client] = view_as<int>(snapshot[iTimerTrack]);
1163
1164 if(gBS_Style[client] != snapshot[bsStyle])
1165 {
1166 CallOnStyleChanged(client, gBS_Style[client], snapshot[bsStyle], false);
1167 }
1168
1169 gB_TimerEnabled[client] = view_as<bool>(snapshot[bTimerEnabled]);
1170 gB_ClientPaused[client] = view_as<bool>(snapshot[bClientPaused]);
1171 gI_Jumps[client] = view_as<int>(snapshot[iJumps]);
1172 gBS_Style[client] = snapshot[bsStyle];
1173 gI_Strafes[client] = view_as<int>(snapshot[iStrafes]);
1174 gI_TotalMeasures[client] = view_as<int>(snapshot[iTotalMeasures]);
1175 gI_GoodGains[client] = view_as<int>(snapshot[iGoodGains]);
1176 gF_PlayerTimer[client] = snapshot[fCurrentTime];
1177 gI_SHSW_FirstCombination[client] = view_as<int>(snapshot[iSHSWCombination]);
1178}
1179
1180public int Native_MarkKZMap(Handle handler, int numParams)
1181{
1182 gB_KZMap = true;
1183}
1184
1185int GetTimerStatus(int client)
1186{
1187 if(!gB_TimerEnabled[client])
1188 {
1189 return view_as<int>(Timer_Stopped);
1190 }
1191
1192 else if(gB_ClientPaused[client])
1193 {
1194 return view_as<int>(Timer_Paused);
1195 }
1196
1197 return view_as<int>(Timer_Running);
1198}
1199
1200void StartTimer(int client, int track)
1201{
1202 if(!IsValidClient(client, true) || GetClientTeam(client) < 2 || IsFakeClient(client))
1203 {
1204 return;
1205 }
1206
1207 float fSpeed[3];
1208 GetEntPropVector(client, Prop_Data, "m_vecVelocity", fSpeed);
1209
1210 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))
1211 {
1212 Action result = Plugin_Continue;
1213 Call_StartForward(gH_Forwards_Start);
1214 Call_PushCell(client);
1215 Call_PushCell(track);
1216 Call_Finish(result);
1217
1218 if(result == Plugin_Continue)
1219 {
1220 gI_Strafes[client] = 0;
1221 gI_Jumps[client] = 0;
1222 gI_TotalMeasures[client] = 0;
1223 gI_GoodGains[client] = 0;
1224 gF_PlayerTimer[client] = 0.0;
1225 gI_Track[client] = track;
1226 gB_TimerEnabled[client] = true;
1227 gI_SHSW_FirstCombination[client] = -1;
1228 gF_PlayerTimer[client] = 0.0;
1229 gB_PracticeMode[client] = false;
1230
1231 SetEntityGravity(client, view_as<float>(gA_StyleSettings[gBS_Style[client]][fGravityMultiplier]));
1232 SetEntPropFloat(client, Prop_Data, "m_flLaggedMovementValue", view_as<float>(gA_StyleSettings[gBS_Style[client]][fSpeedMultiplier]));
1233 }
1234
1235 else if(result == Plugin_Handled || result == Plugin_Stop)
1236 {
1237 gB_TimerEnabled[client] = false;
1238 }
1239 }
1240}
1241
1242void StopTimer(int client)
1243{
1244 if(!IsValidClient(client) || IsFakeClient(client))
1245 {
1246 return;
1247 }
1248
1249 gB_TimerEnabled[client] = false;
1250 gI_Jumps[client] = 0;
1251 gF_PlayerTimer[client] = 0.0;
1252 gB_ClientPaused[client] = false;
1253 gI_Strafes[client] = 0;
1254 gI_TotalMeasures[client] = 0;
1255 gI_GoodGains[client] = 0;
1256}
1257
1258void PauseTimer(int client)
1259{
1260 if(!IsValidClient(client) || IsFakeClient(client))
1261 {
1262 return;
1263 }
1264
1265 Call_StartForward(gH_Forwards_OnPause);
1266 Call_PushCell(client);
1267 Call_PushCell(gI_Track[client]);
1268 Call_Finish();
1269
1270 gB_ClientPaused[client] = true;
1271}
1272
1273void ResumeTimer(int client)
1274{
1275 if(!IsValidClient(client) || IsFakeClient(client))
1276 {
1277 return;
1278 }
1279
1280 Call_StartForward(gH_Forwards_OnResume);
1281 Call_PushCell(client);
1282 Call_PushCell(gI_Track[client]);
1283 Call_Finish();
1284
1285 gB_ClientPaused[client] = false;
1286}
1287
1288public void OnClientDisconnect(int client)
1289{
1290 StopTimer(client);
1291}
1292
1293public void OnClientCookiesCached(int client)
1294{
1295 if(IsFakeClient(client))
1296 {
1297 return;
1298 }
1299
1300 char[] sCookie = new char[4];
1301
1302 if(gH_AutoBhopCookie != null)
1303 {
1304 GetClientCookie(client, gH_AutoBhopCookie, sCookie, 4);
1305 }
1306
1307 gB_Auto[client] = (strlen(sCookie) > 0)? view_as<bool>(StringToInt(sCookie)):true;
1308 int style = 0;
1309
1310 if(gH_StyleCookie != null)
1311 {
1312 GetClientCookie(client, gH_StyleCookie, sCookie, 4);
1313 style = StringToInt(sCookie);
1314 }
1315
1316 int newstyle = (style >= 0 && style < gI_Styles)? style:0;
1317 CallOnStyleChanged(client, gBS_Style[client], newstyle, false);
1318}
1319
1320public void OnClientPutInServer(int client)
1321{
1322 StopTimer(client);
1323
1324 if(IsClientConnected(client) && IsFakeClient(client))
1325 {
1326 return;
1327 }
1328
1329 gB_Auto[client] = true;
1330 gB_DoubleSteps[client] = false;
1331 gF_StrafeWarning[client] = 0.0;
1332 gB_PracticeMode[client] = false;
1333 gI_SHSW_FirstCombination[client] = -1;
1334 gI_Track[client] = 0;
1335
1336 if(AreClientCookiesCached(client))
1337 {
1338 OnClientCookiesCached(client);
1339 }
1340
1341 else
1342 {
1343 CallOnStyleChanged(client, 0, 0, false);
1344 }
1345
1346 if(gH_SQL == null)
1347 {
1348 return;
1349 }
1350
1351 SDKHook(client, SDKHook_PreThinkPost, PreThinkPost);
1352
1353 char[] sAuthID3 = new char[32];
1354
1355 if(!GetClientAuthId(client, AuthId_Steam3, sAuthID3, 32))
1356 {
1357 KickClient(client, "%T", "VerificationFailed", client);
1358
1359 return;
1360 }
1361
1362 char[] sName = new char[MAX_NAME_LENGTH];
1363 GetClientName(client, sName, MAX_NAME_LENGTH);
1364 ReplaceString(sName, MAX_NAME_LENGTH, "#", "?"); // to avoid this: https://user-images.githubusercontent.com/3672466/28637962-0d324952-724c-11e7-8b27-15ff021f0a59.png
1365
1366 int iLength = ((strlen(sName) * 2) + 1);
1367 char[] sEscapedName = new char[iLength];
1368 gH_SQL.Escape(sName, sEscapedName, iLength);
1369
1370 char[] sIP = new char[64];
1371 GetClientIP(client, sIP, 64);
1372
1373 char[] sCountry = new char[128];
1374
1375 if(!GeoipCountry(sIP, sCountry, 128))
1376 {
1377 strcopy(sCountry, 128, "Local Area Network");
1378 }
1379
1380 int iTime = GetTime();
1381
1382 char[] sQuery = new char[512];
1383
1384 if(gB_MySQL)
1385 {
1386 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);
1387 }
1388
1389 else
1390 {
1391 FormatEx(sQuery, 512, "REPLACE INTO %susers (auth, name, country, ip, lastlogin) VALUES ('%s', '%s', '%s', '%s', %d);", gS_MySQLPrefix, sAuthID3, sEscapedName, sCountry, sIP, iTime);
1392 }
1393
1394 gH_SQL.Query(SQL_InsertUser_Callback, sQuery, GetClientSerial(client));
1395}
1396
1397public void SQL_InsertUser_Callback(Database db, DBResultSet results, const char[] error, any data)
1398{
1399 if(results == null)
1400 {
1401 int client = GetClientFromSerial(data);
1402
1403 if(client == 0)
1404 {
1405 LogError("Timer error! Failed to insert a disconnected player's data to the table. Reason: %s", error);
1406 }
1407
1408 else
1409 {
1410 LogError("Timer error! Failed to insert \"%N\"'s data to the table. Reason: %s", client, error);
1411 }
1412
1413 return;
1414 }
1415}
1416
1417bool LoadStyles()
1418{
1419 char[] sPath = new char[PLATFORM_MAX_PATH];
1420 BuildPath(Path_SM, sPath, PLATFORM_MAX_PATH, "configs/shavit-styles.cfg");
1421
1422 KeyValues kv = new KeyValues("shavit-styles");
1423
1424 if(!kv.ImportFromFile(sPath) || !kv.GotoFirstSubKey())
1425 {
1426 delete kv;
1427
1428 return false;
1429 }
1430
1431 int i = 0;
1432
1433 do
1434 {
1435 kv.GetString("name", gS_StyleStrings[i][sStyleName], 128, "<MISSING STYLE NAME>");
1436 kv.GetString("shortname", gS_StyleStrings[i][sShortName], 128, "<MISSING SHORT STYLE NAME>");
1437 kv.GetString("htmlcolor", gS_StyleStrings[i][sHTMLColor], 128, "<MISSING STYLE HTML COLOR>");
1438 kv.GetString("command", gS_StyleStrings[i][sChangeCommand], 128, "");
1439 kv.GetString("clantag", gS_StyleStrings[i][sClanTag], 128, "<MISSING STYLE CLAN TAG>");
1440 kv.GetString("specialstring", gS_StyleStrings[i][sSpecialString], 128, "");
1441
1442 gA_StyleSettings[i][bAutobhop] = view_as<bool>(kv.GetNum("autobhop", 1));
1443 gA_StyleSettings[i][bEasybhop] = view_as<bool>(kv.GetNum("easybhop", 1));
1444 gA_StyleSettings[i][bPrespeed] = view_as<bool>(kv.GetNum("prespeed", 0));
1445 gA_StyleSettings[i][fVelocityLimit] = kv.GetFloat("velocity_limit", 0.0);
1446 gA_StyleSettings[i][fAiraccelerate] = kv.GetFloat("airaccelerate", 1000.0);
1447 gA_StyleSettings[i][bEnableBunnyhopping] = view_as<bool>(kv.GetNum("bunnyhopping", 1));
1448 gA_StyleSettings[i][fRunspeed] = kv.GetFloat("runspeed", 260.00);
1449 gA_StyleSettings[i][fGravityMultiplier] = kv.GetFloat("gravity", 1.0);
1450 gA_StyleSettings[i][fSpeedMultiplier] = kv.GetFloat("speed", 1.0);
1451 gA_StyleSettings[i][bHalftime] = view_as<bool>(kv.GetNum("halftime", 0));
1452 gA_StyleSettings[i][fVelocity] = kv.GetFloat("velocity", 1.0);
1453 gA_StyleSettings[i][fBonusVelocity] = kv.GetFloat("bonus_velocity", 0.0);
1454 gA_StyleSettings[i][fMinVelocity] = kv.GetFloat("min_velocity", 0.0);
1455 gA_StyleSettings[i][bBlockW] = view_as<bool>(kv.GetNum("block_w", 0));
1456 gA_StyleSettings[i][bBlockA] = view_as<bool>(kv.GetNum("block_a", 0));
1457 gA_StyleSettings[i][bBlockS] = view_as<bool>(kv.GetNum("block_s", 0));
1458 gA_StyleSettings[i][bBlockD] = view_as<bool>(kv.GetNum("block_d", 0));
1459 gA_StyleSettings[i][bBlockUse] = view_as<bool>(kv.GetNum("block_use", 0));
1460 gA_StyleSettings[i][iForceHSW] = kv.GetNum("force_hsw", 0);
1461 gA_StyleSettings[i][bBlockPLeft] = view_as<bool>(kv.GetNum("block_pleft", 0));
1462 gA_StyleSettings[i][bBlockPRight] = view_as<bool>(kv.GetNum("block_pright", 0));
1463 gA_StyleSettings[i][iBlockPStrafe] = kv.GetNum("block_pstrafe", 0);
1464 gA_StyleSettings[i][bUnranked] = view_as<bool>(kv.GetNum("unranked", 0));
1465 gA_StyleSettings[i][bNoReplay] = view_as<bool>(kv.GetNum("noreplay", 0));
1466 gA_StyleSettings[i][bSync] = view_as<bool>(kv.GetNum("sync", 1));
1467 gA_StyleSettings[i][bStrafeCountW] = view_as<bool>(kv.GetNum("strafe_count_w", false));
1468 gA_StyleSettings[i][bStrafeCountA] = view_as<bool>(kv.GetNum("strafe_count_a", 1));
1469 gA_StyleSettings[i][bStrafeCountS] = view_as<bool>(kv.GetNum("strafe_count_s", false));
1470 gA_StyleSettings[i][bStrafeCountD] = view_as<bool>(kv.GetNum("strafe_count_d", 1));
1471 gA_StyleSettings[i][fRankingMultiplier] = kv.GetFloat("rankingmultiplier", 1.00);
1472 gA_StyleSettings[i][iSpecial] = kv.GetNum("special", 0);
1473
1474 if(!gB_Registered && strlen(gS_StyleStrings[i][sChangeCommand]) > 0)
1475 {
1476 char[][] sStyleCommands = new char[32][32];
1477 int iCommands = ExplodeString(gS_StyleStrings[i][sChangeCommand], ";", sStyleCommands, 32, 32, false);
1478
1479 char[] sDescription = new char[128];
1480 FormatEx(sDescription, 128, "Change style to %s.", gS_StyleStrings[i][sStyleName]);
1481
1482 for(int x = 0; x < iCommands; x++)
1483 {
1484 TrimString(sStyleCommands[x]);
1485 StripQuotes(sStyleCommands[x]);
1486
1487 char[] sCommand = new char[32];
1488 FormatEx(sCommand, 32, "sm_%s", sStyleCommands[x]);
1489
1490 gSM_StyleCommands.SetValue(sCommand, i);
1491
1492 RegConsoleCmd(sCommand, Command_StyleChange, sDescription);
1493 }
1494 }
1495
1496 i++;
1497 }
1498
1499 while(kv.GotoNextKey());
1500
1501 delete kv;
1502
1503 gI_Styles = i;
1504 gB_Registered = true;
1505
1506 return true;
1507}
1508
1509public Action Command_StyleChange(int client, int args)
1510{
1511 char[] sCommand = new char[128];
1512 GetCmdArg(0, sCommand, 128);
1513
1514 int style = 0;
1515
1516 if(gSM_StyleCommands.GetValue(sCommand, style))
1517 {
1518 ChangeClientStyle(client, style, true);
1519
1520 return Plugin_Handled;
1521 }
1522
1523 return Plugin_Continue;
1524}
1525
1526bool LoadMessages()
1527{
1528 char[] sPath = new char[PLATFORM_MAX_PATH];
1529 BuildPath(Path_SM, sPath, PLATFORM_MAX_PATH, "configs/shavit-messages.cfg");
1530
1531 KeyValues kv = new KeyValues("shavit-messages");
1532
1533 if(!kv.ImportFromFile(sPath))
1534 {
1535 delete kv;
1536
1537 return false;
1538 }
1539
1540 kv.JumpToKey((IsSource2013(gEV_Type))? "CS:S":"CS:GO");
1541
1542 kv.GetString("prefix", gS_ChatStrings[sMessagePrefix], 128, "\x075e70d0[Timer]");
1543 kv.GetString("text", gS_ChatStrings[sMessageText], 128, "\x07ffffff");
1544 kv.GetString("warning", gS_ChatStrings[sMessageWarning], 128, "\x07af2a22");
1545 kv.GetString("variable", gS_ChatStrings[sMessageVariable], 128, "\x077fd772");
1546 kv.GetString("variable2", gS_ChatStrings[sMessageVariable2], 128, "\x07276f5c");
1547 kv.GetString("style", gS_ChatStrings[sMessageStyle], 128, "\x07db88c2");
1548
1549 delete kv;
1550
1551 for(int i = 0; i < CHATSETTINGS_SIZE; i++)
1552 {
1553 for(int x = 0; x < sizeof(gS_GlobalColorNames); x++)
1554 {
1555 ReplaceString(gS_ChatStrings[i], 128, gS_GlobalColorNames[x], gS_GlobalColors[x]);
1556 }
1557
1558 for(int x = 0; x < sizeof(gS_CSGOColorNames); x++)
1559 {
1560 ReplaceString(gS_ChatStrings[i], 128, gS_CSGOColorNames[x], gS_CSGOColors[x]);
1561 }
1562
1563 ReplaceString(gS_ChatStrings[i], 128, "{RGB}", "\x07");
1564 ReplaceString(gS_ChatStrings[i], 128, "{RGBA}", "\x08");
1565 }
1566
1567 return true;
1568}
1569
1570void SQL_SetPrefix()
1571{
1572 char[] sFile = new char[PLATFORM_MAX_PATH];
1573 BuildPath(Path_SM, sFile, PLATFORM_MAX_PATH, "configs/shavit-prefix.txt");
1574
1575 File fFile = OpenFile(sFile, "r");
1576
1577 if(fFile == null)
1578 {
1579 SetFailState("Cannot open \"configs/shavit-prefix.txt\". Make sure this file exists and that the server has read permissions to it.");
1580 }
1581
1582 char[] sLine = new char[PLATFORM_MAX_PATH*2];
1583
1584 while(fFile.ReadLine(sLine, PLATFORM_MAX_PATH*2))
1585 {
1586 TrimString(sLine);
1587 strcopy(gS_MySQLPrefix, 32, sLine);
1588
1589 break;
1590 }
1591
1592 delete fFile;
1593}
1594
1595void SQL_DBConnect()
1596{
1597 if(gH_SQL != null)
1598 {
1599 delete gH_SQL;
1600 }
1601
1602 char[] sError = new char[255];
1603
1604 if(SQL_CheckConfig("shavit")) // can't be asynced as we have modules that require this database connection instantly
1605 {
1606 gH_SQL = SQL_Connect("shavit", true, sError, 255);
1607
1608 if(gH_SQL == null)
1609 {
1610 SetFailState("Timer startup failed. Reason: %s", sError);
1611 }
1612 }
1613
1614 else
1615 {
1616 gH_SQL = SQLite_UseDatabase("shavit", sError, 255);
1617 }
1618
1619 // support unicode names
1620 gH_SQL.SetCharset("utf8");
1621
1622 Call_StartForward(gH_Forwards_OnDatabaseLoaded);
1623 Call_Finish();
1624
1625 char[] sDriver = new char[8];
1626 gH_SQL.Driver.GetIdentifier(sDriver, 8);
1627 gB_MySQL = StrEqual(sDriver, "mysql", false);
1628
1629 char[] sQuery = new char[512];
1630
1631 if(gB_MySQL)
1632 {
1633 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);
1634 }
1635
1636 else
1637 {
1638 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);
1639 }
1640
1641 // CREATE TABLE IF NOT EXISTS
1642 gH_SQL.Query(SQL_CreateTable_Callback, sQuery);
1643}
1644
1645public void SQL_CreateTable_Callback(Database db, DBResultSet results, const char[] error, any data)
1646{
1647 if(results == null)
1648 {
1649 LogError("Timer error! Users' data table creation failed. Reason: %s", error);
1650
1651 return;
1652 }
1653
1654 char[] sQuery = new char[192];
1655 FormatEx(sQuery, 192, "SELECT lastlogin FROM %susers LIMIT 1;", gS_MySQLPrefix);
1656 gH_SQL.Query(SQL_TableMigration1_Callback, sQuery, 0, DBPrio_High);
1657
1658 FormatEx(sQuery, 192, "SELECT points FROM %susers LIMIT 1;", gS_MySQLPrefix);
1659 gH_SQL.Query(SQL_TableMigration2_Callback, sQuery, 0, DBPrio_High);
1660
1661 char sTables[][] =
1662 {
1663 "maptiers",
1664 "mapzones",
1665 "playertimes"
1666 };
1667
1668 for(int i = 0; i < sizeof(sTables); i++)
1669 {
1670 DataPack dp = new DataPack();
1671 dp.WriteString(sTables[i]);
1672
1673 FormatEx(sQuery, 192, "SELECT map FROM %s%s WHERE map LIKE 'workshop%%' GROUP BY map;", gS_MySQLPrefix, sTables[i]);
1674 gH_SQL.Query(SQL_TableMigration3_Callback, sQuery, dp, DBPrio_Low);
1675 }
1676}
1677
1678public void SQL_TableMigration1_Callback(Database db, DBResultSet results, const char[] error, any data)
1679{
1680 if(results == null)
1681 {
1682 char[] sQuery = new char[128];
1683 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");
1684 gH_SQL.Query(SQL_AlterTable1_Callback, sQuery);
1685 }
1686}
1687
1688public void SQL_AlterTable1_Callback(Database db, DBResultSet results, const char[] error, any data)
1689{
1690 if(results == null)
1691 {
1692 LogError("Timer error! Table alteration 1 (core) failed. Reason: %s", error);
1693
1694 return;
1695 }
1696}
1697
1698public void SQL_TableMigration2_Callback(Database db, DBResultSet results, const char[] error, any data)
1699{
1700 if(results == null)
1701 {
1702 char[] sQuery = new char[128];
1703 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");
1704 gH_SQL.Query(SQL_AlterTable2_Callback, sQuery);
1705 }
1706}
1707
1708public void SQL_AlterTable2_Callback(Database db, DBResultSet results, const char[] error, any data)
1709{
1710 if(results == null)
1711 {
1712 LogError("Timer error! Table alteration 2 (core) failed. Reason: %s", error);
1713
1714 return;
1715 }
1716}
1717
1718public void SQL_TableMigration3_Callback(Database db, DBResultSet results, const char[] error, DataPack data)
1719{
1720 char[] sTable = new char[16];
1721
1722 data.Reset();
1723 data.ReadString(sTable, 16);
1724 delete data;
1725
1726 if(results == null || results.RowCount == 0)
1727 {
1728 // no error logging here because not everyone runs the rankings/wr modules
1729 return;
1730 }
1731
1732 while(results.FetchRow())
1733 {
1734 char[] sMap = new char[160];
1735 results.FetchString(0, sMap, 160);
1736
1737 char[] sDisplayMap = new char[160];
1738 GetMapDisplayName(sMap, sDisplayMap, 160);
1739
1740 char[] sQuery = new char[256];
1741 FormatEx(sQuery, 256, "UPDATE %s%s SET map = '%s' WHERE map = '%s';", gS_MySQLPrefix, sTable, sDisplayMap, sMap);
1742 gH_SQL.Query(SQL_AlterTable3_Callback, sQuery, 0, DBPrio_High);
1743 }
1744}
1745
1746public void SQL_AlterTable3_Callback(Database db, DBResultSet results, const char[] error, any data)
1747{
1748 if(results == null)
1749 {
1750 LogError("Timer error! Table alteration 3 (core) failed. Reason: %s", error);
1751
1752 return;
1753 }
1754}
1755
1756public void PreThinkPost(int client)
1757{
1758 if(IsPlayerAlive(client))
1759 {
1760 sv_airaccelerate.FloatValue = view_as<float>(gA_StyleSettings[gBS_Style[client]][fAiraccelerate]);
1761
1762 if(sv_enablebunnyhopping != null)
1763 {
1764 sv_enablebunnyhopping.BoolValue = view_as<bool>(gA_StyleSettings[gBS_Style[client]][bEnableBunnyhopping]);
1765 }
1766 }
1767}
1768
1769public void OnGameFrame()
1770{
1771 float frametime = GetGameFrameTime();
1772
1773 for(int i = 1; i <= MaxClients; i++)
1774 {
1775 if(gB_ClientPaused[i] || !gB_TimerEnabled[i])
1776 {
1777 continue;
1778 }
1779
1780 float time = frametime;
1781
1782 if(gA_StyleSettings[gBS_Style[i]][bHalftime])
1783 {
1784 time /= 2.0;
1785 }
1786
1787 any[] snapshot = new any[TIMERSNAPSHOT_SIZE];
1788 snapshot[bTimerEnabled] = gB_TimerEnabled[i];
1789 snapshot[bClientPaused] = gB_ClientPaused[i];
1790 snapshot[iJumps] = gI_Jumps[i];
1791 snapshot[bsStyle] = gBS_Style[i];
1792 snapshot[iStrafes] = gI_Strafes[i];
1793 snapshot[iTotalMeasures] = gI_TotalMeasures[i];
1794 snapshot[iGoodGains] = gI_GoodGains[i];
1795 snapshot[fServerTime] = GetEngineTime();
1796 snapshot[fCurrentTime] = gF_PlayerTimer[i];
1797 snapshot[iSHSWCombination] = gI_SHSW_FirstCombination[i];
1798 snapshot[iTimerTrack] = gI_Track[i];
1799
1800 Call_StartForward(gH_Forwards_OnTimerIncrement);
1801 Call_PushCell(i);
1802 Call_PushArray(snapshot, TIMERSNAPSHOT_SIZE);
1803 Call_PushCellRef(time);
1804 Call_PushArray(gA_StyleSettings[gBS_Style[i]], STYLESETTINGS_SIZE);
1805 Call_Finish();
1806
1807 gF_PlayerTimer[i] += time;
1808
1809 Call_StartForward(gH_Forwards_OnTimerIncrementPost);
1810 Call_PushCell(i);
1811 Call_PushCell(time);
1812 Call_PushArray(gA_StyleSettings[gBS_Style[i]], STYLESETTINGS_SIZE);
1813 Call_Finish();
1814 }
1815}
1816
1817
1818public 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])
1819{
1820 if(!IsPlayerAlive(client) || IsFakeClient(client))
1821 {
1822 PrintToChatAll("Client: %s STYLE: %s", GetClientName(client), gA_StyleSettings[gBS_Style[client]]);
1823 return Plugin_Continue;
1824 }
1825
1826 int flags = GetEntityFlags(client);
1827
1828 if(gB_ClientPaused[client])
1829 {
1830 buttons = 0;
1831 vel = view_as<float>({0.0, 0.0, 0.0});
1832
1833 SetEntityFlags(client, (flags | FL_ATCONTROLS));
1834
1835 return Plugin_Changed;
1836 }
1837
1838 SetEntityFlags(client, (flags & ~FL_ATCONTROLS));
1839
1840 Action result = Plugin_Continue;
1841 Call_StartForward(gH_Forwards_OnUserCmdPre);
1842 Call_PushCell(client);
1843 Call_PushCellRef(buttons);
1844 Call_PushCellRef(impulse);
1845 Call_PushArrayEx(vel, 3, SM_PARAM_COPYBACK);
1846 Call_PushArrayEx(angles, 3, SM_PARAM_COPYBACK);
1847 Call_PushCell(GetTimerStatus(client));
1848 Call_PushCell(gI_Track[client]);
1849 Call_PushCell(gBS_Style[client]);
1850 Call_PushArray(gA_StyleSettings[gBS_Style[client]], STYLESETTINGS_SIZE);
1851 Call_PushArrayEx(mouse, 2, SM_PARAM_COPYBACK);
1852 Call_Finish(result);
1853
1854 if(result != Plugin_Continue && result != Plugin_Changed)
1855 {
1856 return result;
1857 }
1858
1859 int iGroundEntity = GetEntPropEnt(client, Prop_Send, "m_hGroundEntity");
1860 bool bInStart = Shavit_InsideZone(client, Zone_Start, gI_Track[client]);
1861
1862 if(gB_TimerEnabled[client] && !gB_ClientPaused[client])
1863 {
1864 char[] sCheatDetected = new char[64];
1865
1866 // +left/right block
1867 if(gB_LeftRight && (!gB_Zones || !bInStart && ((gA_StyleSettings[gBS_Style[client]][bBlockPLeft] &&
1868 (buttons & IN_LEFT) > 0) || (gA_StyleSettings[gBS_Style[client]][bBlockPRight] && (buttons & IN_RIGHT) > 0))))
1869 {
1870 FormatEx(sCheatDetected, 64, "%T", "LeftRightCheat", client);
1871 StopTimer_Cheat(client, sCheatDetected);
1872 }
1873
1874 // +strafe block
1875 if(gA_StyleSettings[gBS_Style[client]][iBlockPStrafe] > 0 &&
1876 ((vel[0] > 0.0 && (buttons & IN_FORWARD) == 0) || (vel[0] < 0.0 && (buttons & IN_BACK) == 0) ||
1877 (vel[1] > 0.0 && (buttons & IN_MOVERIGHT) == 0) || (vel[1] < 0.0 && (buttons & IN_MOVELEFT) == 0)))
1878 {
1879 if(gF_StrafeWarning[client] < gF_PlayerTimer[client])
1880 {
1881 if(gA_StyleSettings[gBS_Style[client]][iBlockPStrafe] >= 2)
1882 {
1883 FormatEx(sCheatDetected, 64, "%T", "Inconsistencies", client);
1884 StopTimer_Cheat(client, sCheatDetected);
1885 }
1886
1887 vel[0] = 0.0;
1888 vel[1] = 0.0;
1889
1890 return Plugin_Changed;
1891 }
1892
1893 gF_StrafeWarning[client] = gF_PlayerTimer[client] + 0.3;
1894 }
1895 }
1896
1897 #if defined DEBUG
1898 static int cycle = 0;
1899
1900 if(++cycle % 50 == 0)
1901 {
1902 Shavit_StopChatSound();
1903 Shavit_PrintToChat(client, "vel[0]: %.01f | vel[1]: %.01f", vel[0], vel[1]);
1904 }
1905 #endif
1906
1907 MoveType mtMoveType = GetEntityMoveType(client);
1908 bool bOnLadder = (mtMoveType == MOVETYPE_LADDER);
1909
1910 //boostercheck
1911
1912
1913 // key blocking
1914 if(!bOnLadder && !Shavit_InsideZone(client, Zone_Freestyle, -1))
1915 {
1916 // block E
1917 if(gA_StyleSettings[gBS_Style[client]][bBlockUse] && (buttons & IN_USE) > 0)
1918 {
1919 buttons &= ~IN_USE;
1920 }
1921
1922 if(iGroundEntity == -1)
1923 {
1924 if(gA_StyleSettings[gBS_Style[client]][bBlockW] && ((buttons & IN_FORWARD) > 0 || vel[0] > 0.0))
1925 {
1926 vel[0] = 0.0;
1927 buttons &= ~IN_FORWARD;
1928 }
1929
1930 if(gA_StyleSettings[gBS_Style[client]][bBlockA] && ((buttons & IN_MOVELEFT) > 0 || vel[1] < 0.0))
1931 {
1932 vel[1] = 0.0;
1933 buttons &= ~IN_MOVELEFT;
1934 }
1935
1936 if(gA_StyleSettings[gBS_Style[client]][bBlockS] && ((buttons & IN_BACK) > 0 || vel[0] < 0.0))
1937 {
1938 vel[0] = 0.0;
1939 buttons &= ~IN_BACK;
1940 }
1941
1942 if(gA_StyleSettings[gBS_Style[client]][bBlockD] && ((buttons & IN_MOVERIGHT) > 0 || vel[1] > 0.0))
1943 {
1944 vel[1] = 0.0;
1945 buttons &= ~IN_MOVERIGHT;
1946 }
1947
1948 // HSW
1949 // Theory about blocking non-HSW strafes while playing HSW:
1950 // Block S and W without A or D.
1951 // Block A and D without S or W.
1952 if(gA_StyleSettings[gBS_Style[client]][iForceHSW] > 0)
1953 {
1954 bool bSHSW = (gA_StyleSettings[gBS_Style[client]][iForceHSW] == 2) && !bInStart; // don't decide on the first valid input until out of start zone!
1955 int iCombination = -1;
1956
1957 bool bForward = ((buttons & IN_FORWARD) > 0 && vel[0] >= 200.0);
1958 bool bMoveLeft = ((buttons & IN_MOVELEFT) > 0 && vel[1] <= -200.0);
1959 bool bBack = ((buttons & IN_BACK) > 0 && vel[0] <= -200.0);
1960 bool bMoveRight = ((buttons & IN_MOVERIGHT) > 0 && vel[1] >= 200.0);
1961
1962 if(bSHSW)
1963 {
1964 if((bForward && bMoveLeft) || (bBack && bMoveRight))
1965 {
1966 iCombination = 0;
1967 }
1968
1969 else if((bForward && bMoveRight || bBack && bMoveLeft))
1970 {
1971 iCombination = 1;
1972 }
1973
1974 // int gI_SHSW_FirstCombination[MAXPLAYERS+1]; // 0 - W/A S/D | 1 - W/D S/A
1975 if(gI_SHSW_FirstCombination[client] == -1 && iCombination != -1)
1976 {
1977 Shavit_PrintToChat(client, "%T", (iCombination == 0)? "SHSWCombination0":"SHSWCombination1", client, gS_ChatStrings[sMessageVariable], gS_ChatStrings[sMessageText]);
1978 gI_SHSW_FirstCombination[client] = iCombination;
1979 }
1980
1981 bool bStop = false;
1982
1983 // W/A S/D
1984 if((gI_SHSW_FirstCombination[client] == 0 && iCombination != 0) ||
1985 // W/D S/A
1986 (gI_SHSW_FirstCombination[client] == 1 && iCombination != 1) ||
1987 // no valid combination & no valid input
1988 (gI_SHSW_FirstCombination[client] == -1 && iCombination == -1))
1989 {
1990 bStop = true;
1991 }
1992
1993 if(bStop)
1994 {
1995 vel[0] = 0.0;
1996 vel[1] = 0.0;
1997
1998 buttons &= ~IN_FORWARD;
1999 buttons &= ~IN_MOVELEFT;
2000 buttons &= ~IN_MOVERIGHT;
2001 buttons &= ~IN_BACK;
2002 }
2003 }
2004
2005 else
2006 {
2007 if((bForward || bBack) && !(bMoveLeft || bMoveRight))
2008 {
2009 vel[0] = 0.0;
2010
2011 buttons &= ~IN_FORWARD;
2012 buttons &= ~IN_BACK;
2013 }
2014
2015 if((bMoveLeft || bMoveRight) && !(bForward || bBack))
2016 {
2017 vel[1] = 0.0;
2018
2019 buttons &= ~IN_MOVELEFT;
2020 buttons &= ~IN_MOVERIGHT;
2021 }
2022 }
2023 }
2024 }
2025 }
2026
2027 if(gA_StyleSettings[gBS_Style[client]][bStrafeCountW] && !gA_StyleSettings[gBS_Style[client]][bBlockW] &&
2028 (gI_ButtonCache[client] & IN_FORWARD) == 0 && (buttons & IN_FORWARD) > 0)
2029 {
2030 gI_Strafes[client]++;
2031 }
2032
2033 if(gA_StyleSettings[gBS_Style[client]][bStrafeCountA] && !gA_StyleSettings[gBS_Style[client]][bBlockA] && (gI_ButtonCache[client] & IN_MOVELEFT) == 0 &&
2034 (buttons & IN_MOVELEFT) > 0 && (gA_StyleSettings[gBS_Style[client]][iForceHSW] > 0 || ((buttons & IN_FORWARD) == 0 && (buttons & IN_BACK) == 0)))
2035 {
2036 gI_Strafes[client]++;
2037 }
2038
2039 if(gA_StyleSettings[gBS_Style[client]][bStrafeCountS] && !gA_StyleSettings[gBS_Style[client]][bBlockS] &&
2040 (gI_ButtonCache[client] & IN_BACK) == 0 && (buttons & IN_BACK) > 0)
2041 {
2042 gI_Strafes[client]++;
2043 }
2044
2045 if(gA_StyleSettings[gBS_Style[client]][bStrafeCountD] && !gA_StyleSettings[gBS_Style[client]][bBlockD] && (gI_ButtonCache[client] & IN_MOVERIGHT) == 0 &&
2046 (buttons & IN_MOVERIGHT) > 0 && (gA_StyleSettings[gBS_Style[client]][iForceHSW] > 0 || ((buttons & IN_FORWARD) == 0 && (buttons & IN_BACK) == 0)))
2047 {
2048 gI_Strafes[client]++;
2049 }
2050
2051 bool bInWater = (GetEntProp(client, Prop_Send, "m_nWaterLevel") >= 2);
2052
2053 // enable duck-jumping/bhop in tf2
2054 if(gEV_Type == Engine_TF2 && gA_StyleSettings[gBS_Style[client]][bEnableBunnyhopping] && (buttons & IN_JUMP) > 0 && iGroundEntity != -1)
2055 {
2056 float fSpeed[3];
2057 GetEntPropVector(client, Prop_Data, "m_vecAbsVelocity", fSpeed);
2058
2059 fSpeed[2] = 271.0;
2060 SetEntPropVector(client, Prop_Data, "m_vecAbsVelocity", fSpeed);
2061 }
2062
2063 if(gA_StyleSettings[gBS_Style[client]][bAutobhop] && gB_Autobhop && gB_Auto[client] && (buttons & IN_JUMP) > 0 && mtMoveType == MOVETYPE_WALK && !bInWater)
2064 {
2065 int iOldButtons = GetEntProp(client, Prop_Data, "m_nOldButtons");
2066 SetEntProp(client, Prop_Data, "m_nOldButtons", iOldButtons & ~IN_JUMP);
2067 }
2068
2069 else if(gB_DoubleSteps[client] && (buttons & IN_JUMP) == 0)
2070 {
2071 buttons |= IN_JUMP;
2072 }
2073
2074 if(bInStart && gB_BlockPreJump && !gA_StyleSettings[gBS_Style[client]][bPrespeed] && (vel[2] > 0 || (buttons & IN_JUMP) > 0))
2075 {
2076 vel[2] = 0.0;
2077 buttons &= ~IN_JUMP;
2078 }
2079
2080 // velocity limit
2081 if(iGroundEntity != -1 && view_as<float>(gA_StyleSettings[gBS_Style[client]][fVelocityLimit] > 0.0) &&
2082 (!gB_Zones || !Shavit_InsideZone(client, Zone_NoVelLimit, -1)))
2083 {
2084 float fSpeed[3];
2085 GetEntPropVector(client, Prop_Data, "m_vecVelocity", fSpeed);
2086
2087 float fSpeed_New = (SquareRoot(Pow(fSpeed[0], 2.0) + Pow(fSpeed[1], 2.0)));
2088
2089 if(fSpeed_New > 0.0)
2090 {
2091 float fScale = view_as<float>(gA_StyleSettings[gBS_Style[client]][fVelocityLimit]) / fSpeed_New;
2092
2093 if(fScale < 1.0)
2094 {
2095 ScaleVector(fSpeed, fScale);
2096 TeleportEntity(client, NULL_VECTOR, NULL_VECTOR, fSpeed); // maybe change this to SetEntPropVector some time?
2097 }
2098 }
2099 }
2100
2101 float fAngle = (angles[1] - gF_AngleCache[client]);
2102
2103 while(fAngle > 180.0)
2104 {
2105 fAngle -= 360.0;
2106 }
2107
2108 while(fAngle < -180.0)
2109 {
2110 fAngle += 360.0;
2111 }
2112
2113 if(iGroundEntity == -1 && (GetEntityFlags(client) & FL_INWATER) == 0 && fAngle != 0.0)
2114 {
2115 float fAbsVelocity[3];
2116 GetEntPropVector(client, Prop_Data, "m_vecAbsVelocity", fAbsVelocity);
2117
2118 if(SquareRoot(Pow(fAbsVelocity[0], 2.0) + Pow(fAbsVelocity[1], 2.0)) > 0.0)
2119 {
2120 float fTempAngle = angles[1];
2121
2122 float fAngles[3];
2123 GetVectorAngles(fAbsVelocity, fAngles);
2124
2125 if(fTempAngle < 0.0)
2126 {
2127 fTempAngle += 360.0;
2128 }
2129
2130 float fDirectionAngle = (fTempAngle - fAngles[1]);
2131
2132 if(fDirectionAngle < 0.0)
2133 {
2134 fDirectionAngle = -fDirectionAngle;
2135 }
2136
2137 if(fDirectionAngle < 22.5 || fDirectionAngle > 337.5)
2138 {
2139 gI_TotalMeasures[client]++;
2140
2141 if((fAngle > 0.0 && vel[1] < 0.0) || (fAngle < 0.0 && vel[1] > 0.0))
2142 {
2143 gI_GoodGains[client]++;
2144 }
2145 }
2146
2147 else if((fDirectionAngle > 67.5 && fDirectionAngle < 112.5) || (fDirectionAngle > 247.5 && fDirectionAngle < 292.5))
2148 {
2149 gI_TotalMeasures[client]++;
2150
2151 if(vel[0] != 0.0)
2152 {
2153 gI_GoodGains[client]++;
2154 }
2155 }
2156 }
2157 }
2158
2159 gI_ButtonCache[client] = buttons;
2160 gF_AngleCache[client] = angles[1];
2161
2162 return Plugin_Continue;
2163}
2164
2165void StopTimer_Cheat(int client, const char[] message)
2166{
2167 Shavit_StopTimer(client);
2168 Shavit_PrintToChat(client, "%T", "CheatTimerStop", client, gS_ChatStrings[sMessageWarning], gS_ChatStrings[sMessageText], message);
2169}
2170
2171void UpdateAutoBhop(int client)
2172{
2173 if(sv_autobunnyhopping != null)
2174 {
2175 sv_autobunnyhopping.ReplicateToClient(client, (gA_StyleSettings[gBS_Style[client]][bAutobhop] && gB_Autobhop && gB_Auto[client])? "1":"0");
2176 }
2177}
2178
2179void UpdateAiraccelerate(int client)
2180{
2181 char[] sAiraccelerate = new char[8];
2182 FloatToString(gA_StyleSettings[gBS_Style[client]][fAiraccelerate], sAiraccelerate, 8);
2183 sv_airaccelerate.ReplicateToClient(client, sAiraccelerate);
2184}
2185
2186void UpdateBunnyhopping(int client)
2187{
2188 if(sv_enablebunnyhopping != null)
2189 {
2190 sv_enablebunnyhopping.ReplicateToClient(client, (gA_StyleSettings[gBS_Style[client]][bEnableBunnyhopping])? "1":"0");
2191 }
2192}