mirror of
https://github.com/shavitush/bhoptimer.git
synced 2025-12-06 18:08:26 +00:00
3146 lines
87 KiB
SourcePawn
3146 lines
87 KiB
SourcePawn
/*
|
|
* shavit's Timer - Core
|
|
* by: shavit
|
|
*
|
|
* This file is part of shavit's Timer.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify it under
|
|
* the terms of the GNU General Public License, version 3.0, as published by the
|
|
* Free Software Foundation.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
|
* details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along with
|
|
* this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
*/
|
|
|
|
#include <sourcemod>
|
|
#include <sdkhooks>
|
|
#include <sdktools>
|
|
#include <geoip>
|
|
#include <clientprefs>
|
|
#include <convar_class>
|
|
#include <dhooks>
|
|
|
|
#define DEBUG 0
|
|
|
|
#include <shavit/core>
|
|
|
|
#undef REQUIRE_PLUGIN
|
|
#include <shavit/hud>
|
|
#include <shavit/rankings>
|
|
#include <shavit/replay-playback>
|
|
#include <shavit/wr>
|
|
#include <shavit/zones>
|
|
#include <eventqueuefix>
|
|
|
|
#include <shavit/chat-colors>
|
|
#include <shavit/anti-sv_cheats.sp>
|
|
#include <shavit/steamid-stocks>
|
|
#include <shavit/style-settings.sp>
|
|
#include <shavit/sql-create-tables-and-migrations.sp>
|
|
#include <shavit/physicsuntouch>
|
|
|
|
#pragma newdecls required
|
|
#pragma semicolon 1
|
|
|
|
// game type (CS:S/CS:GO/TF2)
|
|
EngineVersion gEV_Type = Engine_Unknown;
|
|
bool gB_Protobuf = false;
|
|
|
|
// hook stuff
|
|
DynamicHook gH_GetPlayerMaxSpeed = null;
|
|
DynamicHook gH_AcceptInput; // used for hooking player_speedmod's AcceptInput
|
|
|
|
// database handle
|
|
Database2 gH_SQL = null;
|
|
bool gB_MySQL = false;
|
|
|
|
// forwards
|
|
Handle gH_Forwards_Start = null;
|
|
Handle gH_Forwards_StartPre = null;
|
|
Handle gH_Forwards_Stop = null;
|
|
Handle gH_Forwards_StopPre = null;
|
|
Handle gH_Forwards_FinishPre = null;
|
|
Handle gH_Forwards_Finish = null;
|
|
Handle gH_Forwards_OnRestartPre = null;
|
|
Handle gH_Forwards_OnRestart = null;
|
|
Handle gH_Forwards_OnEnd = null;
|
|
Handle gH_Forwards_OnPause = null;
|
|
Handle gH_Forwards_OnResume = null;
|
|
Handle gH_Forwards_OnStyleChanged = null;
|
|
Handle gH_Forwards_OnTrackChanged = null;
|
|
Handle gH_Forwards_OnChatConfigLoaded = null;
|
|
Handle gH_Forwards_OnUserCmdPre = null;
|
|
Handle gH_Forwards_OnTimerIncrement = null;
|
|
Handle gH_Forwards_OnTimerIncrementPost = null;
|
|
Handle gH_Forwards_OnTimescaleChanged = null;
|
|
Handle gH_Forwards_OnTimeOffsetCalculated = null;
|
|
Handle gH_Forwards_OnProcessMovement = null;
|
|
Handle gH_Forwards_OnProcessMovementPost = null;
|
|
|
|
// player timer variables
|
|
timer_snapshot_t gA_Timers[MAXPLAYERS+1];
|
|
bool gB_Auto[MAXPLAYERS+1];
|
|
|
|
// these are here until the compiler bug is fixed
|
|
float gF_PauseOrigin[MAXPLAYERS+1][3];
|
|
float gF_PauseAngles[MAXPLAYERS+1][3];
|
|
float gF_PauseVelocity[MAXPLAYERS+1][3];
|
|
|
|
// used for offsets
|
|
float gF_SmallestDist[MAXPLAYERS + 1];
|
|
float gF_Origin[MAXPLAYERS + 1][2][3];
|
|
float gF_Fraction[MAXPLAYERS + 1];
|
|
|
|
// cookies
|
|
Handle gH_StyleCookie = null;
|
|
Handle gH_AutoBhopCookie = null;
|
|
Cookie gH_IHateMain = null;
|
|
|
|
// late load
|
|
bool gB_Late = false;
|
|
|
|
// modules
|
|
bool gB_Eventqueuefix = false;
|
|
bool gB_Zones = false;
|
|
bool gB_ReplayPlayback = false;
|
|
bool gB_Rankings = false;
|
|
bool gB_HUD = false;
|
|
|
|
// cvars
|
|
Convar gCV_Restart = null;
|
|
Convar gCV_Pause = null;
|
|
Convar gCV_PauseMovement = null;
|
|
Convar gCV_BlockPreJump = null;
|
|
Convar gCV_NoZAxisSpeed = null;
|
|
Convar gCV_VelocityTeleport = null;
|
|
Convar gCV_DefaultStyle = null;
|
|
Convar gCV_NoChatSound = null;
|
|
Convar gCV_SimplerLadders = null;
|
|
Convar gCV_StaticPrestrafe = null;
|
|
Convar gCV_UseOffsets = null;
|
|
Convar gCV_TimeInMessages;
|
|
Convar gCV_DebugOffsets = null;
|
|
// cached cvars
|
|
int gI_DefaultStyle = 0;
|
|
bool gB_StyleCookies = true;
|
|
|
|
// table prefix
|
|
char gS_MySQLPrefix[32];
|
|
|
|
// server side
|
|
ConVar sv_airaccelerate = null;
|
|
ConVar sv_autobunnyhopping = null;
|
|
ConVar sv_enablebunnyhopping = null;
|
|
|
|
// chat settings
|
|
chatstrings_t gS_ChatStrings;
|
|
|
|
// misc cache
|
|
bool gB_StopChatSound = false;
|
|
bool gB_HookedJump = false;
|
|
char gS_LogPath[PLATFORM_MAX_PATH];
|
|
char gS_DeleteMap[MAXPLAYERS+1][PLATFORM_MAX_PATH];
|
|
int gI_WipePlayerID[MAXPLAYERS+1];
|
|
char gS_Verification[MAXPLAYERS+1][8];
|
|
bool gB_CookiesRetrieved[MAXPLAYERS+1];
|
|
float gF_ZoneAiraccelerate[MAXPLAYERS+1];
|
|
float gF_ZoneSpeedLimit[MAXPLAYERS+1];
|
|
|
|
// kz support
|
|
bool gB_KZMap = false;
|
|
|
|
public Plugin myinfo =
|
|
{
|
|
name = "[shavit] Core",
|
|
author = "shavit",
|
|
description = "The core for shavit's bhop timer.",
|
|
version = SHAVIT_VERSION,
|
|
url = "https://github.com/shavitush/bhoptimer"
|
|
}
|
|
|
|
public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max)
|
|
{
|
|
Shavit_Style_Settings_Natives();
|
|
|
|
CreateNative("Shavit_CanPause", Native_CanPause);
|
|
CreateNative("Shavit_ChangeClientStyle", Native_ChangeClientStyle);
|
|
CreateNative("Shavit_FinishMap", Native_FinishMap);
|
|
CreateNative("Shavit_GetBhopStyle", Native_GetBhopStyle);
|
|
CreateNative("Shavit_GetChatStrings", Native_GetChatStrings);
|
|
CreateNative("Shavit_GetChatStringsStruct", Native_GetChatStringsStruct);
|
|
CreateNative("Shavit_GetClientJumps", Native_GetClientJumps);
|
|
CreateNative("Shavit_GetClientTime", Native_GetClientTime);
|
|
CreateNative("Shavit_GetClientTrack", Native_GetClientTrack);
|
|
CreateNative("Shavit_GetDatabase", Native_GetDatabase);
|
|
CreateNative("Shavit_GetPerfectJumps", Native_GetPerfectJumps);
|
|
CreateNative("Shavit_GetStrafeCount", Native_GetStrafeCount);
|
|
CreateNative("Shavit_GetSync", Native_GetSync);
|
|
CreateNative("Shavit_GetZoneOffset", Native_GetZoneOffset);
|
|
CreateNative("Shavit_GetDistanceOffset", Native_GetDistanceOffset);
|
|
CreateNative("Shavit_GetTimerStatus", Native_GetTimerStatus);
|
|
CreateNative("Shavit_IsKZMap", Native_IsKZMap);
|
|
CreateNative("Shavit_IsPaused", Native_IsPaused);
|
|
CreateNative("Shavit_IsPracticeMode", Native_IsPracticeMode);
|
|
CreateNative("Shavit_LoadSnapshot", Native_LoadSnapshot);
|
|
CreateNative("Shavit_LogMessage", Native_LogMessage);
|
|
CreateNative("Shavit_MarkKZMap", Native_MarkKZMap);
|
|
CreateNative("Shavit_PauseTimer", Native_PauseTimer);
|
|
CreateNative("Shavit_PrintToChat", Native_PrintToChat);
|
|
CreateNative("Shavit_PrintToChatAll", Native_PrintToChatAll);
|
|
CreateNative("Shavit_RestartTimer", Native_RestartTimer);
|
|
CreateNative("Shavit_ResumeTimer", Native_ResumeTimer);
|
|
CreateNative("Shavit_SaveSnapshot", Native_SaveSnapshot);
|
|
CreateNative("Shavit_SetPracticeMode", Native_SetPracticeMode);
|
|
CreateNative("Shavit_StartTimer", Native_StartTimer);
|
|
CreateNative("Shavit_StopChatSound", Native_StopChatSound);
|
|
CreateNative("Shavit_StopTimer", Native_StopTimer);
|
|
CreateNative("Shavit_GetClientTimescale", Native_GetClientTimescale);
|
|
CreateNative("Shavit_SetClientTimescale", Native_SetClientTimescale);
|
|
CreateNative("Shavit_GetAvgVelocity", Native_GetAvgVelocity);
|
|
CreateNative("Shavit_GetMaxVelocity", Native_GetMaxVelocity);
|
|
CreateNative("Shavit_SetAvgVelocity", Native_SetAvgVelocity);
|
|
CreateNative("Shavit_SetMaxVelocity", Native_SetMaxVelocity);
|
|
|
|
// registers library, check "bool LibraryExists(const char[] name)" in order to use with other plugins
|
|
RegPluginLibrary("shavit");
|
|
|
|
gB_Late = late;
|
|
|
|
return APLRes_Success;
|
|
}
|
|
|
|
public void OnPluginStart()
|
|
{
|
|
// forwards
|
|
gH_Forwards_Start = CreateGlobalForward("Shavit_OnStart", ET_Ignore, Param_Cell, Param_Cell);
|
|
gH_Forwards_StartPre = CreateGlobalForward("Shavit_OnStartPre", ET_Event, Param_Cell, Param_Cell);
|
|
gH_Forwards_Stop = CreateGlobalForward("Shavit_OnStop", ET_Event, Param_Cell, Param_Cell);
|
|
gH_Forwards_StopPre = CreateGlobalForward("Shavit_OnStopPre", ET_Event, Param_Cell, Param_Cell);
|
|
gH_Forwards_FinishPre = CreateGlobalForward("Shavit_OnFinishPre", ET_Event, Param_Cell, Param_Array);
|
|
gH_Forwards_Finish = CreateGlobalForward("Shavit_OnFinish", ET_Event, Param_Cell, Param_Cell, Param_Cell, Param_Cell, Param_Cell, Param_Cell, Param_Cell, Param_Cell, Param_Cell, Param_Cell, Param_Cell, Param_Cell);
|
|
gH_Forwards_OnRestartPre = CreateGlobalForward("Shavit_OnRestartPre", ET_Event, Param_Cell, Param_Cell);
|
|
gH_Forwards_OnRestart = CreateGlobalForward("Shavit_OnRestart", ET_Ignore, Param_Cell, Param_Cell);
|
|
gH_Forwards_OnEnd = CreateGlobalForward("Shavit_OnEnd", ET_Event, Param_Cell, Param_Cell);
|
|
gH_Forwards_OnPause = CreateGlobalForward("Shavit_OnPause", ET_Event, Param_Cell, Param_Cell);
|
|
gH_Forwards_OnResume = CreateGlobalForward("Shavit_OnResume", ET_Event, Param_Cell, Param_Cell);
|
|
gH_Forwards_OnStyleChanged = CreateGlobalForward("Shavit_OnStyleChanged", ET_Event, Param_Cell, Param_Cell, Param_Cell, Param_Cell, Param_Cell);
|
|
gH_Forwards_OnTrackChanged = CreateGlobalForward("Shavit_OnTrackChanged", ET_Event, Param_Cell, Param_Cell, Param_Cell);
|
|
gH_Forwards_OnChatConfigLoaded = CreateGlobalForward("Shavit_OnChatConfigLoaded", ET_Event);
|
|
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);
|
|
gH_Forwards_OnTimerIncrement = CreateGlobalForward("Shavit_OnTimeIncrement", ET_Event, Param_Cell, Param_Array, Param_CellByRef, Param_Array);
|
|
gH_Forwards_OnTimerIncrementPost = CreateGlobalForward("Shavit_OnTimeIncrementPost", ET_Event, Param_Cell, Param_Cell, Param_Array);
|
|
gH_Forwards_OnTimescaleChanged = CreateGlobalForward("Shavit_OnTimescaleChanged", ET_Event, Param_Cell, Param_Cell, Param_Cell);
|
|
gH_Forwards_OnTimeOffsetCalculated = CreateGlobalForward("Shavit_OnTimeOffsetCalculated", ET_Event, Param_Cell, Param_Cell, Param_Cell, Param_Cell);
|
|
gH_Forwards_OnProcessMovement = CreateGlobalForward("Shavit_OnProcessMovement", ET_Event, Param_Cell);
|
|
gH_Forwards_OnProcessMovementPost = CreateGlobalForward("Shavit_OnProcessMovementPost", ET_Event, Param_Cell);
|
|
|
|
Shavit_Style_Settings_Forwards();
|
|
|
|
LoadTranslations("shavit-core.phrases");
|
|
LoadTranslations("shavit-common.phrases");
|
|
|
|
// game types
|
|
gEV_Type = GetEngineVersion();
|
|
gB_Protobuf = (GetUserMessageType() == UM_Protobuf);
|
|
|
|
if(gEV_Type == Engine_CSGO)
|
|
{
|
|
sv_autobunnyhopping = FindConVar("sv_autobunnyhopping");
|
|
sv_autobunnyhopping.BoolValue = false;
|
|
}
|
|
|
|
else if(gEV_Type != Engine_CSS && gEV_Type != Engine_TF2)
|
|
{
|
|
SetFailState("This plugin was meant to be used in CS:S, CS:GO and TF2 *only*.");
|
|
}
|
|
|
|
LoadDHooks();
|
|
|
|
// hooks
|
|
gB_HookedJump = HookEventEx("player_jump", Player_Jump);
|
|
HookEvent("player_death", Player_Death);
|
|
HookEvent("player_team", Player_Death);
|
|
HookEvent("player_spawn", Player_Death);
|
|
|
|
// commands START
|
|
// style
|
|
RegConsoleCmd("sm_style", Command_Style, "Choose your bhop style.");
|
|
RegConsoleCmd("sm_styles", Command_Style, "Choose your bhop style.");
|
|
RegConsoleCmd("sm_diff", Command_Style, "Choose your bhop style.");
|
|
RegConsoleCmd("sm_difficulty", Command_Style, "Choose your bhop style.");
|
|
gH_StyleCookie = RegClientCookie("shavit_style", "Style cookie", CookieAccess_Protected);
|
|
|
|
// timer start
|
|
RegConsoleCmd("sm_start", Command_StartTimer, "Start your timer.");
|
|
RegConsoleCmd("sm_r", Command_StartTimer, "Start your timer.");
|
|
RegConsoleCmd("sm_restart", Command_StartTimer, "Start your timer.");
|
|
RegConsoleCmd("sm_m", Command_StartTimer, "Start your timer on the main track.");
|
|
RegConsoleCmd("sm_main", Command_StartTimer, "Start your timer on the main track.");
|
|
RegConsoleCmd("sm_ihate!main", Command_IHateMain, "If you really hate !main :(((");
|
|
gH_IHateMain = new Cookie("shavit_mainhater", "If you really hate !main :(((", CookieAccess_Protected);
|
|
|
|
RegConsoleCmd("sm_b", Command_StartTimer, "Start your timer on the bonus track.");
|
|
RegConsoleCmd("sm_bonus", Command_StartTimer, "Start your timer on the bonus track.");
|
|
|
|
for (int i = Track_Bonus; i <= Track_Bonus_Last; i++)
|
|
{
|
|
char cmd[10], helptext[50];
|
|
FormatEx(cmd, sizeof(cmd), "sm_b%d", i);
|
|
FormatEx(helptext, sizeof(helptext), "Start your timer on the bonus %d track.", i);
|
|
RegConsoleCmd(cmd, Command_StartTimer, helptext);
|
|
}
|
|
|
|
// teleport to end
|
|
RegConsoleCmd("sm_end", Command_TeleportEnd, "Teleport to endzone.");
|
|
|
|
RegConsoleCmd("sm_bend", Command_TeleportEnd, "Teleport to endzone of the bonus track.");
|
|
RegConsoleCmd("sm_bonusend", Command_TeleportEnd, "Teleport to endzone of the bonus track.");
|
|
|
|
// timer stop
|
|
RegConsoleCmd("sm_stop", Command_StopTimer, "Stop your timer.");
|
|
|
|
// timer pause / resume
|
|
RegConsoleCmd("sm_pause", Command_TogglePause, "Toggle pause.");
|
|
RegConsoleCmd("sm_unpause", Command_TogglePause, "Toggle pause.");
|
|
RegConsoleCmd("sm_resume", Command_TogglePause, "Toggle pause");
|
|
|
|
// autobhop toggle
|
|
RegConsoleCmd("sm_auto", Command_AutoBhop, "Toggle autobhop.");
|
|
RegConsoleCmd("sm_autobhop", Command_AutoBhop, "Toggle autobhop.");
|
|
gH_AutoBhopCookie = RegClientCookie("shavit_autobhop", "Autobhop cookie", CookieAccess_Protected);
|
|
|
|
#if DEBUG
|
|
RegConsoleCmd("sm_finishtest", Command_FinishTest);
|
|
RegConsoleCmd("sm_fling", Command_Fling);
|
|
#endif
|
|
|
|
// admin
|
|
RegAdminCmd("sm_deletemap", Command_DeleteMap, ADMFLAG_ROOT, "Deletes all map data. Usage: sm_deletemap <map>");
|
|
RegAdminCmd("sm_wipeplayer", Command_WipePlayer, ADMFLAG_BAN, "Wipes all bhoptimer data for specified player. Usage: sm_wipeplayer <steamid3>");
|
|
RegAdminCmd("sm_migration", Command_Migration, ADMFLAG_ROOT, "Force a database migration to run. Usage: sm_migration <migration id> or \"all\" to run all migrations.");
|
|
// commands END
|
|
|
|
// logs
|
|
BuildPath(Path_SM, gS_LogPath, PLATFORM_MAX_PATH, "logs/shavit.log");
|
|
|
|
CreateConVar("shavit_version", SHAVIT_VERSION, "Plugin version.", (FCVAR_NOTIFY | FCVAR_DONTRECORD));
|
|
|
|
gCV_Restart = new Convar("shavit_core_restart", "1", "Allow commands that restart the timer?", 0, true, 0.0, true, 1.0);
|
|
gCV_Pause = new Convar("shavit_core_pause", "1", "Allow pausing?", 0, true, 0.0, true, 1.0);
|
|
gCV_PauseMovement = new Convar("shavit_core_pause_movement", "0", "Allow movement/noclip while paused?", 0, true, 0.0, true, 1.0);
|
|
gCV_BlockPreJump = new Convar("shavit_core_blockprejump", "0", "Prevents jumping in the start zone.", 0, true, 0.0, true, 1.0);
|
|
gCV_NoZAxisSpeed = new Convar("shavit_core_nozaxisspeed", "1", "Don't start timer if vertical speed exists (btimes style).", 0, true, 0.0, true, 1.0);
|
|
gCV_VelocityTeleport = new Convar("shavit_core_velocityteleport", "0", "Teleport the client when changing its velocity? (for special styles)", 0, true, 0.0, true, 1.0);
|
|
gCV_DefaultStyle = new Convar("shavit_core_defaultstyle", "0", "Default style ID.\nAdd the '!' prefix to disable style cookies - i.e. \"!3\" to *force* scroll to be the default style.", 0, true, 0.0);
|
|
gCV_NoChatSound = new Convar("shavit_core_nochatsound", "0", "Disables click sound for chat messages.", 0, true, 0.0, true, 1.0);
|
|
gCV_SimplerLadders = new Convar("shavit_core_simplerladders", "1", "Allows using all keys on limited styles (such as sideways) after touching ladders\nTouching the ground enables the restriction again.", 0, true, 0.0, true, 1.0);
|
|
gCV_UseOffsets = new Convar("shavit_core_useoffsets", "1", "Calculates more accurate times by subtracting/adding tick offsets from the time the server uses to register that a player has left or entered a trigger", 0, true, 0.0, true, 1.0);
|
|
gCV_StaticPrestrafe = new Convar("shavit_core_staticprestrafe", "1", "Force prestrafe for every pistol.\n250 is the default value and some styles will have 260.\n0 - Disabled\n1 - Enabled", 0, true, 0.0, true, 1.0);
|
|
gCV_TimeInMessages = new Convar("shavit_core_timeinmessages", "0", "Whether to prefix SayText2 messages with the time.", 0, true, 0.0, true, 1.0);
|
|
gCV_DebugOffsets = new Convar("shavit_core_debugoffsets", "0", "Print offset upon leaving or entering a zone?", 0, true, 0.0, true, 1.0);
|
|
gCV_DefaultStyle.AddChangeHook(OnConVarChanged);
|
|
|
|
Anti_sv_cheats_cvars();
|
|
|
|
Convar.AutoExecConfig();
|
|
|
|
sv_airaccelerate = FindConVar("sv_airaccelerate");
|
|
sv_airaccelerate.Flags &= ~(FCVAR_NOTIFY | FCVAR_REPLICATED);
|
|
|
|
sv_enablebunnyhopping = FindConVar("sv_enablebunnyhopping");
|
|
|
|
if(sv_enablebunnyhopping != null)
|
|
{
|
|
sv_enablebunnyhopping.Flags &= ~(FCVAR_NOTIFY | FCVAR_REPLICATED);
|
|
}
|
|
|
|
gB_Eventqueuefix = LibraryExists("eventqueuefix");
|
|
gB_Zones = LibraryExists("shavit-zones");
|
|
gB_ReplayPlayback = LibraryExists("shavit-replay-playback");
|
|
gB_Rankings = LibraryExists("shavit-rankings");
|
|
gB_HUD = LibraryExists("shavit-hud");
|
|
|
|
// database connections
|
|
SQL_DBConnect();
|
|
|
|
// late
|
|
if(gB_Late)
|
|
{
|
|
for(int i = 1; i <= MaxClients; i++)
|
|
{
|
|
if(IsValidClient(i))
|
|
{
|
|
OnClientPutInServer(i);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void LoadDHooks()
|
|
{
|
|
Handle gamedataConf = LoadGameConfigFile("shavit.games");
|
|
|
|
if(gamedataConf == null)
|
|
{
|
|
SetFailState("Failed to load shavit gamedata");
|
|
}
|
|
|
|
StartPrepSDKCall(SDKCall_Static);
|
|
if(!PrepSDKCall_SetFromConf(gamedataConf, SDKConf_Signature, "CreateInterface"))
|
|
{
|
|
SetFailState("Failed to get CreateInterface");
|
|
}
|
|
PrepSDKCall_AddParameter(SDKType_String, SDKPass_Pointer);
|
|
PrepSDKCall_AddParameter(SDKType_PlainOldData, SDKPass_Pointer, VDECODE_FLAG_ALLOWNULL);
|
|
PrepSDKCall_SetReturnInfo(SDKType_PlainOldData, SDKPass_Plain);
|
|
Handle CreateInterface = EndPrepSDKCall();
|
|
|
|
if(CreateInterface == null)
|
|
{
|
|
SetFailState("Unable to prepare SDKCall for CreateInterface");
|
|
}
|
|
|
|
char interfaceName[64];
|
|
|
|
// ProcessMovement
|
|
if(!GameConfGetKeyValue(gamedataConf, "IGameMovement", interfaceName, sizeof(interfaceName)))
|
|
{
|
|
SetFailState("Failed to get IGameMovement interface name");
|
|
}
|
|
|
|
Address IGameMovement = SDKCall(CreateInterface, interfaceName, 0);
|
|
|
|
if(!IGameMovement)
|
|
{
|
|
SetFailState("Failed to get IGameMovement pointer");
|
|
}
|
|
|
|
int offset = GameConfGetOffset(gamedataConf, "ProcessMovement");
|
|
if(offset == -1)
|
|
{
|
|
SetFailState("Failed to get ProcessMovement offset");
|
|
}
|
|
|
|
Handle processMovement = DHookCreate(offset, HookType_Raw, ReturnType_Void, ThisPointer_Ignore, DHook_ProcessMovement);
|
|
DHookAddParam(processMovement, HookParamType_CBaseEntity);
|
|
DHookAddParam(processMovement, HookParamType_ObjectPtr);
|
|
DHookRaw(processMovement, false, IGameMovement);
|
|
|
|
Handle processMovementPost = DHookCreate(offset, HookType_Raw, ReturnType_Void, ThisPointer_Ignore, DHook_ProcessMovementPost);
|
|
DHookAddParam(processMovementPost, HookParamType_CBaseEntity);
|
|
DHookAddParam(processMovementPost, HookParamType_ObjectPtr);
|
|
DHookRaw(processMovementPost, true, IGameMovement);
|
|
|
|
LoadPhysicsUntouch(gamedataConf);
|
|
|
|
if (gEV_Type != Engine_TF2)
|
|
{
|
|
int iOffset;
|
|
|
|
if ((iOffset = GameConfGetOffset(gamedataConf, "CCSPlayer::GetPlayerMaxSpeed")) == -1)
|
|
{
|
|
SetFailState("Couldn't get the offset for \"CCSPlayer::GetPlayerMaxSpeed\" - make sure your gamedata is updated!");
|
|
}
|
|
|
|
gH_GetPlayerMaxSpeed = new DynamicHook(iOffset, HookType_Entity, ReturnType_Float, ThisPointer_CBaseEntity);
|
|
}
|
|
|
|
delete CreateInterface;
|
|
delete gamedataConf;
|
|
|
|
GameData AcceptInputGameData;
|
|
|
|
if (gEV_Type == Engine_CSS)
|
|
{
|
|
AcceptInputGameData = new GameData("sdktools.games/game.cstrike");
|
|
}
|
|
else if (gEV_Type == Engine_TF2)
|
|
{
|
|
AcceptInputGameData = new GameData("sdktools.games/game.tf");
|
|
}
|
|
else if (gEV_Type == Engine_CSGO)
|
|
{
|
|
AcceptInputGameData = new GameData("sdktools.games/engine.csgo");
|
|
}
|
|
|
|
// Stolen from dhooks-test.sp
|
|
offset = AcceptInputGameData.GetOffset("AcceptInput");
|
|
delete AcceptInputGameData;
|
|
gH_AcceptInput = new DynamicHook(offset, HookType_Entity, ReturnType_Bool, ThisPointer_CBaseEntity);
|
|
gH_AcceptInput.AddParam(HookParamType_CharPtr);
|
|
gH_AcceptInput.AddParam(HookParamType_CBaseEntity);
|
|
gH_AcceptInput.AddParam(HookParamType_CBaseEntity);
|
|
gH_AcceptInput.AddParam(HookParamType_Object, 20, DHookPass_ByVal|DHookPass_ODTOR|DHookPass_OCTOR|DHookPass_OASSIGNOP); //variant_t is a union of 12 (float[3]) plus two int type params 12 + 8 = 20
|
|
gH_AcceptInput.AddParam(HookParamType_Int);
|
|
}
|
|
|
|
public void OnConVarChanged(ConVar convar, const char[] oldValue, const char[] newValue)
|
|
{
|
|
gB_StyleCookies = (newValue[0] != '!');
|
|
gI_DefaultStyle = StringToInt(newValue[1]);
|
|
}
|
|
|
|
public void OnLibraryAdded(const char[] name)
|
|
{
|
|
if(StrEqual(name, "shavit-zones"))
|
|
{
|
|
gB_Zones = true;
|
|
}
|
|
else if(StrEqual(name, "shavit-replay-playback"))
|
|
{
|
|
gB_ReplayPlayback = true;
|
|
}
|
|
else if(StrEqual(name, "shavit-rankings"))
|
|
{
|
|
gB_Rankings = true;
|
|
}
|
|
else if(StrEqual(name, "shavit-hud"))
|
|
{
|
|
gB_HUD = true;
|
|
}
|
|
else if(StrEqual(name, "eventqueuefix"))
|
|
{
|
|
gB_Eventqueuefix = true;
|
|
}
|
|
}
|
|
|
|
public void OnLibraryRemoved(const char[] name)
|
|
{
|
|
if(StrEqual(name, "shavit-zones"))
|
|
{
|
|
gB_Zones = false;
|
|
}
|
|
else if(StrEqual(name, "shavit-replay-playback"))
|
|
{
|
|
gB_ReplayPlayback = false;
|
|
}
|
|
else if(StrEqual(name, "shavit-rankings"))
|
|
{
|
|
gB_Rankings = false;
|
|
}
|
|
else if(StrEqual(name, "shavit-hud"))
|
|
{
|
|
gB_HUD = false;
|
|
}
|
|
else if(StrEqual(name, "eventqueuefix"))
|
|
{
|
|
gB_Eventqueuefix = false;
|
|
}
|
|
}
|
|
|
|
public void OnMapStart()
|
|
{
|
|
// styles
|
|
if(!LoadStyles())
|
|
{
|
|
SetFailState("Could not load the styles configuration file. Make sure it exists (addons/sourcemod/configs/shavit-styles.cfg) and follows the proper syntax!");
|
|
}
|
|
|
|
// messages
|
|
if(!LoadMessages())
|
|
{
|
|
SetFailState("Could not load the chat messages configuration file. Make sure it exists (addons/sourcemod/configs/shavit-messages.cfg) and follows the proper syntax!");
|
|
}
|
|
}
|
|
|
|
public void OnConfigsExecuted()
|
|
{
|
|
Anti_sv_cheats_OnConfigsExecuted();
|
|
}
|
|
|
|
public void OnMapEnd()
|
|
{
|
|
gB_KZMap = false;
|
|
}
|
|
|
|
public Action Command_StartTimer(int client, int args)
|
|
{
|
|
if(!IsValidClient(client))
|
|
{
|
|
return Plugin_Handled;
|
|
}
|
|
|
|
char sCommand[16];
|
|
GetCmdArg(0, sCommand, 16);
|
|
|
|
if(!gCV_Restart.BoolValue)
|
|
{
|
|
if(args != -1)
|
|
{
|
|
Shavit_PrintToChat(client, "%T", "CommandDisabled", client, gS_ChatStrings.sVariable, sCommand, gS_ChatStrings.sText);
|
|
}
|
|
|
|
return Plugin_Handled;
|
|
}
|
|
|
|
int track = Track_Main;
|
|
|
|
if(StrContains(sCommand, "sm_b", false) == 0)
|
|
{
|
|
// Pull out bonus number for commands like sm_b1 and sm_b2.
|
|
if ('1' <= sCommand[4] <= ('0' + Track_Bonus_Last))
|
|
{
|
|
track = sCommand[4] - '0';
|
|
}
|
|
else if (args < 1)
|
|
{
|
|
track = Shavit_GetClientTrack(client);
|
|
}
|
|
else
|
|
{
|
|
char arg[6];
|
|
GetCmdArg(1, arg, sizeof(arg));
|
|
track = StringToInt(arg);
|
|
}
|
|
|
|
if (track < Track_Bonus || track > Track_Bonus_Last)
|
|
{
|
|
track = Track_Bonus;
|
|
}
|
|
}
|
|
else if(StrContains(sCommand, "sm_r", false) == 0 || StrContains(sCommand, "sm_s", false) == 0)
|
|
{
|
|
track = (DoIHateMain(client)) ? Track_Main : gA_Timers[client].iTimerTrack;
|
|
|
|
Action result = Plugin_Continue;
|
|
Call_StartForward(gH_Forwards_OnRestartPre);
|
|
Call_PushCell(client);
|
|
Call_PushCell(track);
|
|
Call_Finish(result);
|
|
|
|
if (result > Plugin_Continue)
|
|
{
|
|
return Plugin_Handled;
|
|
}
|
|
}
|
|
|
|
if (gB_Zones && (Shavit_ZoneExists(Zone_Start, track) || gB_KZMap))
|
|
{
|
|
if(!Shavit_StopTimer(client, false))
|
|
{
|
|
return Plugin_Handled;
|
|
}
|
|
|
|
Call_StartForward(gH_Forwards_OnRestart);
|
|
Call_PushCell(client);
|
|
Call_PushCell(track);
|
|
Call_Finish();
|
|
|
|
if (!gB_Zones)
|
|
{
|
|
StartTimer(client, track);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
char sTrack[32];
|
|
GetTrackName(client, track, sTrack, 32);
|
|
|
|
Shavit_PrintToChat(client, "%T", "StartZoneUndefined", client, gS_ChatStrings.sWarning, gS_ChatStrings.sText, gS_ChatStrings.sVariable2, sTrack, gS_ChatStrings.sText);
|
|
}
|
|
|
|
return Plugin_Handled;
|
|
}
|
|
|
|
bool DoIHateMain(int client)
|
|
{
|
|
char data[2];
|
|
gH_IHateMain.Get(client, data, sizeof(data));
|
|
return (data[0] == '1');
|
|
}
|
|
|
|
public Action Command_IHateMain(int client, int args)
|
|
{
|
|
if (!IsValidClient(client))
|
|
{
|
|
return Plugin_Handled;
|
|
}
|
|
|
|
bool bIHateMain = DoIHateMain(client);
|
|
gH_IHateMain.Set(client, (bIHateMain) ? "0" : "1");
|
|
Shavit_PrintToChat(client, (bIHateMain) ? ":)" : ":(");
|
|
|
|
return Plugin_Handled;
|
|
}
|
|
|
|
public Action Command_TeleportEnd(int client, int args)
|
|
{
|
|
if(!IsValidClient(client))
|
|
{
|
|
return Plugin_Handled;
|
|
}
|
|
|
|
char sCommand[16];
|
|
GetCmdArg(0, sCommand, 16);
|
|
|
|
int track = Track_Main;
|
|
|
|
if(StrContains(sCommand, "sm_b", false) == 0)
|
|
{
|
|
if (args < 1)
|
|
{
|
|
track = Shavit_GetClientTrack(client);
|
|
}
|
|
else
|
|
{
|
|
char arg[6];
|
|
GetCmdArg(1, arg, sizeof(arg));
|
|
track = StringToInt(arg);
|
|
}
|
|
|
|
if (track < Track_Bonus || track > Track_Bonus_Last)
|
|
{
|
|
track = Track_Bonus;
|
|
}
|
|
}
|
|
|
|
if(gB_Zones && (Shavit_ZoneExists(Zone_End, track) || gB_KZMap))
|
|
{
|
|
if(Shavit_StopTimer(client, false))
|
|
{
|
|
Call_StartForward(gH_Forwards_OnEnd);
|
|
Call_PushCell(client);
|
|
Call_PushCell(track);
|
|
Call_Finish();
|
|
}
|
|
}
|
|
|
|
else
|
|
{
|
|
Shavit_PrintToChat(client, "%T", "EndZoneUndefined", client, gS_ChatStrings.sWarning, gS_ChatStrings.sText);
|
|
}
|
|
|
|
return Plugin_Handled;
|
|
}
|
|
|
|
public Action Command_StopTimer(int client, int args)
|
|
{
|
|
if(!IsValidClient(client))
|
|
{
|
|
return Plugin_Handled;
|
|
}
|
|
|
|
Shavit_StopTimer(client, false);
|
|
|
|
return Plugin_Handled;
|
|
}
|
|
|
|
public Action Command_TogglePause(int client, int args)
|
|
{
|
|
if(!(1 <= client <= MaxClients) || !IsClientInGame(client))
|
|
{
|
|
return Plugin_Handled;
|
|
}
|
|
|
|
int iFlags = Shavit_CanPause(client);
|
|
|
|
if((iFlags & CPR_NoTimer) > 0)
|
|
{
|
|
return Plugin_Handled;
|
|
}
|
|
|
|
if((iFlags & CPR_InStartZone) > 0)
|
|
{
|
|
Shavit_PrintToChat(client, "%T", "PauseStartZone", client, gS_ChatStrings.sText, gS_ChatStrings.sWarning, gS_ChatStrings.sText, gS_ChatStrings.sVariable, gS_ChatStrings.sText);
|
|
|
|
return Plugin_Handled;
|
|
}
|
|
|
|
if((iFlags & CPR_InEndZone) > 0)
|
|
{
|
|
Shavit_PrintToChat(client, "%T", "PauseEndZone", client, gS_ChatStrings.sText, gS_ChatStrings.sWarning, gS_ChatStrings.sText, gS_ChatStrings.sVariable, gS_ChatStrings.sText);
|
|
|
|
return Plugin_Handled;
|
|
}
|
|
|
|
if((iFlags & CPR_ByConVar) > 0)
|
|
{
|
|
char sCommand[16];
|
|
GetCmdArg(0, sCommand, 16);
|
|
|
|
Shavit_PrintToChat(client, "%T", "CommandDisabled", client, gS_ChatStrings.sVariable, sCommand, gS_ChatStrings.sText);
|
|
|
|
return Plugin_Handled;
|
|
}
|
|
|
|
if (gA_Timers[client].bClientPaused)
|
|
{
|
|
TeleportEntity(client, gF_PauseOrigin[client], gF_PauseAngles[client], gF_PauseVelocity[client]);
|
|
ResumeTimer(client);
|
|
|
|
Shavit_PrintToChat(client, "%T", "MessageUnpause", client, gS_ChatStrings.sText, gS_ChatStrings.sWarning, gS_ChatStrings.sText);
|
|
}
|
|
|
|
else
|
|
{
|
|
if((iFlags & CPR_NotOnGround) > 0)
|
|
{
|
|
Shavit_PrintToChat(client, "%T", "PauseNotOnGround", client, gS_ChatStrings.sWarning, gS_ChatStrings.sText);
|
|
|
|
return Plugin_Handled;
|
|
}
|
|
|
|
if((iFlags & CPR_Moving) > 0)
|
|
{
|
|
Shavit_PrintToChat(client, "%T", "PauseMoving", client, gS_ChatStrings.sWarning, gS_ChatStrings.sText);
|
|
|
|
return Plugin_Handled;
|
|
}
|
|
|
|
if((iFlags & CPR_Duck) > 0)
|
|
{
|
|
Shavit_PrintToChat(client, "%T", "PauseDuck", client, gS_ChatStrings.sWarning, gS_ChatStrings.sText);
|
|
|
|
return Plugin_Handled;
|
|
}
|
|
|
|
GetClientAbsOrigin(client, gF_PauseOrigin[client]);
|
|
GetClientEyeAngles(client, gF_PauseAngles[client]);
|
|
GetEntPropVector(client, Prop_Data, "m_vecAbsVelocity", gF_PauseVelocity[client]);
|
|
|
|
PauseTimer(client);
|
|
|
|
Shavit_PrintToChat(client, "%T", "MessagePause", client, gS_ChatStrings.sText, gS_ChatStrings.sWarning, gS_ChatStrings.sText);
|
|
}
|
|
|
|
return Plugin_Handled;
|
|
}
|
|
|
|
#if DEBUG
|
|
public Action Command_FinishTest(int client, int args)
|
|
{
|
|
Shavit_FinishMap(client, gA_Timers[client].iTimerTrack);
|
|
|
|
return Plugin_Handled;
|
|
}
|
|
|
|
public Action Command_Fling(int client, int args)
|
|
{
|
|
float up[3];
|
|
up[2] = 1000.0;
|
|
SetEntPropVector(client, Prop_Data, "m_vecBaseVelocity", up);
|
|
|
|
return Plugin_Handled;
|
|
}
|
|
#endif
|
|
|
|
public Action Command_DeleteMap(int client, int args)
|
|
{
|
|
if(args == 0)
|
|
{
|
|
ReplyToCommand(client, "Usage: sm_deletemap <map>\nOnce a map is chosen, \"sm_deletemap confirm\" to run the deletion.");
|
|
|
|
return Plugin_Handled;
|
|
}
|
|
|
|
char sArgs[PLATFORM_MAX_PATH];
|
|
GetCmdArgString(sArgs, sizeof(sArgs));
|
|
LowercaseString(sArgs);
|
|
|
|
if(StrEqual(sArgs, "confirm") && strlen(gS_DeleteMap[client]) > 0)
|
|
{
|
|
Shavit_WR_DeleteMap(gS_DeleteMap[client]);
|
|
ReplyToCommand(client, "Deleted all records for %s.", gS_DeleteMap[client]);
|
|
|
|
if(gB_Zones)
|
|
{
|
|
Shavit_Zones_DeleteMap(gS_DeleteMap[client]);
|
|
ReplyToCommand(client, "Deleted all zones for %s.", gS_DeleteMap[client]);
|
|
}
|
|
|
|
if (gB_ReplayPlayback)
|
|
{
|
|
Shavit_Replay_DeleteMap(gS_DeleteMap[client]);
|
|
ReplyToCommand(client, "Deleted all replay data for %s.", gS_DeleteMap[client]);
|
|
}
|
|
|
|
if(gB_Rankings)
|
|
{
|
|
Shavit_Rankings_DeleteMap(gS_DeleteMap[client]);
|
|
ReplyToCommand(client, "Deleted all rankings for %s.", gS_DeleteMap[client]);
|
|
}
|
|
|
|
ReplyToCommand(client, "Finished deleting data for %s.", gS_DeleteMap[client]);
|
|
gS_DeleteMap[client] = "";
|
|
}
|
|
else
|
|
{
|
|
gS_DeleteMap[client] = sArgs;
|
|
ReplyToCommand(client, "Map to delete is now %s.\nRun \"sm_deletemap confirm\" to delete all data regarding the map %s.", gS_DeleteMap[client], gS_DeleteMap[client]);
|
|
}
|
|
|
|
return Plugin_Handled;
|
|
}
|
|
|
|
public Action Command_Migration(int client, int args)
|
|
{
|
|
if(args == 0)
|
|
{
|
|
ReplyToCommand(client, "Usage: sm_migration <migration id or \"all\" to run all migrationsd>.");
|
|
|
|
return Plugin_Handled;
|
|
}
|
|
|
|
char sArg[16];
|
|
GetCmdArg(1, sArg, 16);
|
|
|
|
bool bApplyMigration[MIGRATIONS_END];
|
|
|
|
if(StrEqual(sArg, "all"))
|
|
{
|
|
for(int i = 0; i < MIGRATIONS_END; i++)
|
|
{
|
|
bApplyMigration[i] = true;
|
|
}
|
|
}
|
|
|
|
else
|
|
{
|
|
int iMigration = StringToInt(sArg);
|
|
|
|
if(0 <= iMigration < MIGRATIONS_END)
|
|
{
|
|
bApplyMigration[iMigration] = true;
|
|
}
|
|
}
|
|
|
|
for(int i = 0; i < MIGRATIONS_END; i++)
|
|
{
|
|
if(bApplyMigration[i])
|
|
{
|
|
ReplyToCommand(client, "Applying database migration %d", i);
|
|
ApplyMigration(i);
|
|
}
|
|
}
|
|
|
|
return Plugin_Handled;
|
|
}
|
|
|
|
public Action Command_WipePlayer(int client, int args)
|
|
{
|
|
if(args == 0)
|
|
{
|
|
ReplyToCommand(client, "Usage: sm_wipeplayer <steamid3>\nAfter entering a SteamID, you will be prompted with a verification captcha.");
|
|
|
|
return Plugin_Handled;
|
|
}
|
|
|
|
char sArgString[32];
|
|
GetCmdArgString(sArgString, 32);
|
|
|
|
if(strlen(gS_Verification[client]) == 0 || !StrEqual(sArgString, gS_Verification[client]))
|
|
{
|
|
gI_WipePlayerID[client] = SteamIDToAccountID(sArgString);
|
|
|
|
if(gI_WipePlayerID[client] <= 0)
|
|
{
|
|
Shavit_PrintToChat(client, "Entered SteamID (%s) is invalid. The range for valid SteamIDs is [U:1:1] to [U:1:2147483647].", sArgString);
|
|
|
|
return Plugin_Handled;
|
|
}
|
|
|
|
char sAlphabet[] = "abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789!@#";
|
|
strcopy(gS_Verification[client], 8, "");
|
|
|
|
for(int i = 0; i < 5; i++)
|
|
{
|
|
gS_Verification[client][i] = sAlphabet[GetRandomInt(0, sizeof(sAlphabet) - 1)];
|
|
}
|
|
|
|
Shavit_PrintToChat(client, "Preparing to delete all user data for SteamID %s[U:1:%d]%s. To confirm, enter %s!wipeplayer %s",
|
|
gS_ChatStrings.sVariable, gI_WipePlayerID[client], gS_ChatStrings.sText, gS_ChatStrings.sVariable2, gS_Verification[client]);
|
|
}
|
|
|
|
else
|
|
{
|
|
Shavit_PrintToChat(client, "Deleting data for SteamID %s[U:1:%d]%s...",
|
|
gS_ChatStrings.sVariable, gI_WipePlayerID[client], gS_ChatStrings.sText);
|
|
|
|
DeleteUserData(client, gI_WipePlayerID[client]);
|
|
|
|
strcopy(gS_Verification[client], 8, "");
|
|
gI_WipePlayerID[client] = -1;
|
|
}
|
|
|
|
return Plugin_Handled;
|
|
}
|
|
|
|
public void Trans_DeleteRestOfUserSuccess(Database db, DataPack hPack, int numQueries, DBResultSet[] results, any[] queryData)
|
|
{
|
|
hPack.Reset();
|
|
int client = hPack.ReadCell();
|
|
int iSteamID = hPack.ReadCell();
|
|
delete hPack;
|
|
|
|
Shavit_ReloadLeaderboards();
|
|
|
|
Shavit_LogMessage("%L - wiped user data for [U:1:%d].", client, iSteamID);
|
|
Shavit_PrintToChat(client, "Finished wiping timer data for user %s[U:1:%d]%s.", gS_ChatStrings.sVariable, iSteamID, gS_ChatStrings.sText);
|
|
}
|
|
|
|
public void Trans_DeleteRestOfUserFailed(Database db, DataPack hPack, int numQueries, const char[] error, int failIndex, any[] queryData)
|
|
{
|
|
hPack.Reset();
|
|
hPack.ReadCell();
|
|
int iSteamID = hPack.ReadCell();
|
|
delete hPack;
|
|
LogError("Timer error! Failed to wipe user data (wipe | delete user data/times, id [U:1:%d]). Reason: %s", iSteamID, error);
|
|
}
|
|
|
|
void DeleteRestOfUser(int iSteamID, DataPack hPack)
|
|
{
|
|
Transaction2 hTransaction = new Transaction2();
|
|
char sQuery[256];
|
|
|
|
FormatEx(sQuery, 256, "DELETE FROM %splayertimes WHERE auth = %d;", gS_MySQLPrefix, iSteamID);
|
|
hTransaction.AddQuery(sQuery);
|
|
FormatEx(sQuery, 256, "DELETE FROM %susers WHERE auth = %d;", gS_MySQLPrefix, iSteamID);
|
|
hTransaction.AddQuery(sQuery);
|
|
|
|
gH_SQL.Execute(hTransaction, Trans_DeleteRestOfUserSuccess, Trans_DeleteRestOfUserFailed, hPack);
|
|
}
|
|
|
|
void DeleteUserData(int client, const int iSteamID)
|
|
{
|
|
DataPack hPack = new DataPack();
|
|
hPack.WriteCell(client);
|
|
hPack.WriteCell(iSteamID);
|
|
char sQuery[512];
|
|
|
|
FormatEx(sQuery, sizeof(sQuery),
|
|
"SELECT id, style, track, map FROM %swrs WHERE auth = %d;",
|
|
gS_MySQLPrefix, iSteamID);
|
|
|
|
gH_SQL.Query(SQL_DeleteUserData_GetRecords_Callback, sQuery, hPack, DBPrio_High);
|
|
}
|
|
|
|
public void SQL_DeleteUserData_GetRecords_Callback(Database db, DBResultSet results, const char[] error, DataPack hPack)
|
|
{
|
|
hPack.Reset();
|
|
hPack.ReadCell(); /*int client = */
|
|
int iSteamID = hPack.ReadCell();
|
|
|
|
if(results == null)
|
|
{
|
|
LogError("Timer error! Failed to wipe user data (wipe | get player records). Reason: %s", error);
|
|
delete hPack;
|
|
return;
|
|
}
|
|
|
|
char map[PLATFORM_MAX_PATH];
|
|
|
|
while(results.FetchRow())
|
|
{
|
|
int id = results.FetchInt(0);
|
|
int style = results.FetchInt(1);
|
|
int track = results.FetchInt(2);
|
|
results.FetchString(3, map, sizeof(map));
|
|
|
|
Shavit_DeleteWR(style, track, map, iSteamID, id, false, false);
|
|
}
|
|
|
|
DeleteRestOfUser(iSteamID, hPack);
|
|
}
|
|
|
|
public Action Command_AutoBhop(int client, int args)
|
|
{
|
|
if(!IsValidClient(client))
|
|
{
|
|
return Plugin_Handled;
|
|
}
|
|
|
|
gB_Auto[client] = !gB_Auto[client];
|
|
|
|
if (gB_Auto[client])
|
|
{
|
|
Shavit_PrintToChat(client, "%T", "AutobhopEnabled", client, gS_ChatStrings.sVariable2, gS_ChatStrings.sText);
|
|
}
|
|
else
|
|
{
|
|
Shavit_PrintToChat(client, "%T", "AutobhopDisabled", client, gS_ChatStrings.sWarning, gS_ChatStrings.sText);
|
|
}
|
|
|
|
char sAutoBhop[4];
|
|
IntToString(view_as<int>(gB_Auto[client]), sAutoBhop, 4);
|
|
SetClientCookie(client, gH_AutoBhopCookie, sAutoBhop);
|
|
|
|
UpdateStyleSettings(client);
|
|
|
|
return Plugin_Handled;
|
|
}
|
|
|
|
public Action Command_Style(int client, int args)
|
|
{
|
|
if(!IsValidClient(client))
|
|
{
|
|
return Plugin_Handled;
|
|
}
|
|
|
|
Menu menu = new Menu(StyleMenu_Handler);
|
|
menu.SetTitle("%T", "StyleMenuTitle", client);
|
|
|
|
int iStyleCount = Shavit_GetStyleCount();
|
|
int iOrderedStyles[STYLE_LIMIT];
|
|
Shavit_GetOrderedStyles(iOrderedStyles, iStyleCount);
|
|
|
|
for(int i = 0; i < iStyleCount; i++)
|
|
{
|
|
int iStyle = iOrderedStyles[i];
|
|
|
|
// this logic will prevent the style from showing in !style menu if it's specifically inaccessible
|
|
// or just completely disabled
|
|
if((GetStyleSettingBool(iStyle, "inaccessible") && GetStyleSettingInt(iStyle, "enabled") == 1) ||
|
|
GetStyleSettingInt(iStyle, "enabled") == -1)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
char sInfo[8];
|
|
IntToString(iStyle, sInfo, 8);
|
|
|
|
char sDisplay[64];
|
|
|
|
if(GetStyleSettingBool(iStyle, "unranked"))
|
|
{
|
|
char sName[64];
|
|
GetStyleSetting(iStyle, "name", sName, sizeof(sName));
|
|
FormatEx(sDisplay, 64, "%T %s", "StyleUnranked", client, sName);
|
|
}
|
|
else
|
|
{
|
|
float time = Shavit_GetWorldRecord(iStyle, gA_Timers[client].iTimerTrack);
|
|
|
|
if(time > 0.0)
|
|
{
|
|
char sTime[32];
|
|
FormatSeconds(time, sTime, 32, false);
|
|
|
|
char sWR[8];
|
|
strcopy(sWR, 8, "WR");
|
|
|
|
if (gA_Timers[client].iTimerTrack >= Track_Bonus)
|
|
{
|
|
strcopy(sWR, 8, "BWR");
|
|
}
|
|
|
|
char sName[64];
|
|
GetStyleSetting(iStyle, "name", sName, sizeof(sName));
|
|
FormatEx(sDisplay, 64, "%s - %s: %s", sName, sWR, sTime);
|
|
}
|
|
else
|
|
{
|
|
GetStyleSetting(iStyle, "name", sDisplay, sizeof(sDisplay));
|
|
}
|
|
}
|
|
|
|
menu.AddItem(sInfo, sDisplay, (gA_Timers[client].bsStyle == iStyle || !Shavit_HasStyleAccess(client, iStyle))? ITEMDRAW_DISABLED:ITEMDRAW_DEFAULT);
|
|
}
|
|
|
|
// should NEVER happen
|
|
if(menu.ItemCount == 0)
|
|
{
|
|
menu.AddItem("-1", "Nothing");
|
|
}
|
|
|
|
else if(menu.ItemCount <= ((gEV_Type == Engine_CSS)? 9:8))
|
|
{
|
|
menu.Pagination = MENU_NO_PAGINATION;
|
|
}
|
|
|
|
menu.ExitButton = true;
|
|
menu.Display(client, MENU_TIME_FOREVER);
|
|
|
|
return Plugin_Handled;
|
|
}
|
|
|
|
public int StyleMenu_Handler(Menu menu, MenuAction action, int param1, int param2)
|
|
{
|
|
if(action == MenuAction_Select)
|
|
{
|
|
char info[16];
|
|
menu.GetItem(param2, info, 16);
|
|
|
|
int style = StringToInt(info);
|
|
|
|
if(style == -1)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
ChangeClientStyle(param1, style, true);
|
|
}
|
|
|
|
else if(action == MenuAction_End)
|
|
{
|
|
delete menu;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void CallOnTrackChanged(int client, int oldtrack, int newtrack)
|
|
{
|
|
Call_StartForward(gH_Forwards_OnTrackChanged);
|
|
Call_PushCell(client);
|
|
Call_PushCell(oldtrack);
|
|
Call_PushCell(newtrack);
|
|
Call_Finish();
|
|
|
|
if (oldtrack == Track_Main && oldtrack != newtrack && !DoIHateMain(client))
|
|
{
|
|
Shavit_PrintToChat(client, "%T", "TrackChangeFromMain", client, gS_ChatStrings.sVariable, gS_ChatStrings.sText, gS_ChatStrings.sVariable, gS_ChatStrings.sText, gS_ChatStrings.sVariable, gS_ChatStrings.sText);
|
|
}
|
|
}
|
|
|
|
void CallOnStyleChanged(int client, int oldstyle, int newstyle, bool manual, bool nofoward=false)
|
|
{
|
|
if (!nofoward)
|
|
{
|
|
Call_StartForward(gH_Forwards_OnStyleChanged);
|
|
Call_PushCell(client);
|
|
Call_PushCell(oldstyle);
|
|
Call_PushCell(newstyle);
|
|
Call_PushCell(gA_Timers[client].iTimerTrack);
|
|
Call_PushCell(manual);
|
|
Call_Finish();
|
|
}
|
|
|
|
gA_Timers[client].bsStyle = newstyle;
|
|
|
|
float fNewTimescale = GetStyleSettingFloat(newstyle, "timescale");
|
|
|
|
if (gA_Timers[client].fTimescale != fNewTimescale && fNewTimescale > 0.0)
|
|
{
|
|
CallOnTimescaleChanged(client, gA_Timers[client].fTimescale, fNewTimescale);
|
|
gA_Timers[client].fTimescale = fNewTimescale;
|
|
}
|
|
|
|
UpdateStyleSettings(client);
|
|
|
|
float newLaggedMovement = fNewTimescale * GetStyleSettingFloat(newstyle, "speed");
|
|
SetEntPropFloat(client, Prop_Data, "m_flLaggedMovementValue", newLaggedMovement); // might be problematic with the shavit-kz stuff TODO
|
|
|
|
if (gB_Eventqueuefix)
|
|
{
|
|
SetEventsTimescale(client, newLaggedMovement);
|
|
}
|
|
|
|
SetEntityGravity(client, GetStyleSettingFloat(newstyle, "gravity"));
|
|
}
|
|
|
|
void CallOnTimescaleChanged(int client, float oldtimescale, float newtimescale)
|
|
{
|
|
Call_StartForward(gH_Forwards_OnTimescaleChanged);
|
|
Call_PushCell(client);
|
|
Call_PushCell(oldtimescale);
|
|
Call_PushCell(newtimescale);
|
|
Call_Finish();
|
|
}
|
|
|
|
void ChangeClientStyle(int client, int style, bool manual)
|
|
{
|
|
if(!IsValidClient(client))
|
|
{
|
|
return;
|
|
}
|
|
|
|
if(!Shavit_HasStyleAccess(client, style))
|
|
{
|
|
if(manual)
|
|
{
|
|
Shavit_PrintToChat(client, "%T", "StyleNoAccess", client, gS_ChatStrings.sWarning, gS_ChatStrings.sText);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
if(manual)
|
|
{
|
|
if(!Shavit_StopTimer(client, false))
|
|
{
|
|
return;
|
|
}
|
|
|
|
char sName[64];
|
|
GetStyleSetting(style, "name", sName, sizeof(sName));
|
|
|
|
Shavit_PrintToChat(client, "%T", "StyleSelection", client, gS_ChatStrings.sStyle, sName, gS_ChatStrings.sText);
|
|
}
|
|
|
|
if(GetStyleSettingBool(style, "unranked"))
|
|
{
|
|
Shavit_PrintToChat(client, "%T", "UnrankedWarning", client, gS_ChatStrings.sWarning, gS_ChatStrings.sText);
|
|
}
|
|
|
|
int aa_old = RoundToZero(GetStyleSettingFloat(gA_Timers[client].bsStyle, "airaccelerate"));
|
|
int aa_new = RoundToZero(GetStyleSettingFloat(style, "airaccelerate"));
|
|
|
|
if(aa_old != aa_new)
|
|
{
|
|
Shavit_PrintToChat(client, "%T", "NewAiraccelerate", client, aa_old, gS_ChatStrings.sVariable, aa_new, gS_ChatStrings.sText);
|
|
}
|
|
|
|
CallOnStyleChanged(client, gA_Timers[client].bsStyle, style, manual);
|
|
|
|
if (gB_Zones && (Shavit_ZoneExists(Zone_Start, gA_Timers[client].iTimerTrack) || gB_KZMap))
|
|
{
|
|
Shavit_StopTimer(client, true);
|
|
Call_StartForward(gH_Forwards_OnRestart);
|
|
Call_PushCell(client);
|
|
Call_PushCell(gA_Timers[client].iTimerTrack);
|
|
Call_Finish();
|
|
}
|
|
|
|
char sStyle[4];
|
|
IntToString(style, sStyle, 4);
|
|
|
|
SetClientCookie(client, gH_StyleCookie, sStyle);
|
|
}
|
|
|
|
// used as an alternative for games where player_jump isn't a thing, such as TF2
|
|
public void Bunnyhop_OnLeaveGround(int client, bool jumped, bool ladder)
|
|
{
|
|
if(gB_HookedJump || !jumped || ladder)
|
|
{
|
|
return;
|
|
}
|
|
|
|
DoJump(client);
|
|
}
|
|
|
|
public void Player_Jump(Event event, const char[] name, bool dontBroadcast)
|
|
{
|
|
int client = GetClientOfUserId(event.GetInt("userid"));
|
|
|
|
DoJump(client);
|
|
}
|
|
|
|
void DoJump(int client)
|
|
{
|
|
if (gA_Timers[client].bTimerEnabled && !gA_Timers[client].bClientPaused)
|
|
{
|
|
gA_Timers[client].iJumps++;
|
|
gA_Timers[client].bJumped = true;
|
|
}
|
|
|
|
// TF2 doesn't use stamina
|
|
if (gEV_Type != Engine_TF2 && (GetStyleSettingBool(gA_Timers[client].bsStyle, "easybhop")) || Shavit_InsideZone(client, Zone_Easybhop, gA_Timers[client].iTimerTrack))
|
|
{
|
|
SetEntPropFloat(client, Prop_Send, "m_flStamina", 0.0);
|
|
}
|
|
|
|
RequestFrame(VelocityChanges, GetClientSerial(client));
|
|
}
|
|
|
|
void VelocityChanges(int data)
|
|
{
|
|
int client = GetClientFromSerial(data);
|
|
|
|
if(client == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
int style = gA_Timers[client].bsStyle;
|
|
|
|
if(GetStyleSettingBool(style, "force_timescale"))
|
|
{
|
|
float mod = gA_Timers[client].fTimescale * GetStyleSettingFloat(gA_Timers[client].bsStyle, "speed");
|
|
SetEntPropFloat(client, Prop_Data, "m_flLaggedMovementValue", mod);
|
|
|
|
if (gB_Eventqueuefix)
|
|
{
|
|
SetEventsTimescale(client, mod);
|
|
}
|
|
}
|
|
|
|
float fAbsVelocity[3];
|
|
GetEntPropVector(client, Prop_Data, "m_vecAbsVelocity", fAbsVelocity);
|
|
|
|
float fSpeed = (SquareRoot(Pow(fAbsVelocity[0], 2.0) + Pow(fAbsVelocity[1], 2.0)));
|
|
|
|
if(fSpeed != 0.0)
|
|
{
|
|
float fVelocityMultiplier = GetStyleSettingFloat(style, "velocity");
|
|
float fVelocityBonus = GetStyleSettingFloat(style, "bonus_velocity");
|
|
float fMin = GetStyleSettingFloat(style, "min_velocity");
|
|
|
|
if(fVelocityMultiplier != 0.0)
|
|
{
|
|
fAbsVelocity[0] *= fVelocityMultiplier;
|
|
fAbsVelocity[1] *= fVelocityMultiplier;
|
|
}
|
|
|
|
if(fVelocityBonus != 0.0)
|
|
{
|
|
float x = fSpeed / (fSpeed + fVelocityBonus);
|
|
fAbsVelocity[0] /= x;
|
|
fAbsVelocity[1] /= x;
|
|
}
|
|
|
|
if(fMin != 0.0 && fSpeed < fMin)
|
|
{
|
|
float x = (fSpeed / fMin);
|
|
fAbsVelocity[0] /= x;
|
|
fAbsVelocity[1] /= x;
|
|
}
|
|
}
|
|
|
|
float fJumpMultiplier = GetStyleSettingFloat(style, "jump_multiplier");
|
|
float fJumpBonus = GetStyleSettingFloat(style, "jump_bonus");
|
|
|
|
if(fJumpMultiplier != 0.0)
|
|
{
|
|
fAbsVelocity[2] *= fJumpMultiplier;
|
|
}
|
|
|
|
if(fJumpBonus != 0.0)
|
|
{
|
|
fAbsVelocity[2] += fJumpBonus;
|
|
}
|
|
|
|
|
|
if(!gCV_VelocityTeleport.BoolValue)
|
|
{
|
|
SetEntPropVector(client, Prop_Data, "m_vecAbsVelocity", fAbsVelocity);
|
|
}
|
|
|
|
else
|
|
{
|
|
TeleportEntity(client, NULL_VECTOR, NULL_VECTOR, fAbsVelocity);
|
|
}
|
|
}
|
|
|
|
public void Player_Death(Event event, const char[] name, bool dontBroadcast)
|
|
{
|
|
int client = GetClientOfUserId(event.GetInt("userid"));
|
|
|
|
ResumeTimer(client);
|
|
StopTimer(client);
|
|
}
|
|
|
|
public int Native_GetDatabase(Handle handler, int numParams)
|
|
{
|
|
return view_as<int>(CloneHandle(gH_SQL, handler));
|
|
}
|
|
|
|
public int Native_GetClientTime(Handle handler, int numParams)
|
|
{
|
|
return view_as<int>(gA_Timers[GetNativeCell(1)].fCurrentTime);
|
|
}
|
|
|
|
public int Native_GetClientTrack(Handle handler, int numParams)
|
|
{
|
|
return gA_Timers[GetNativeCell(1)].iTimerTrack;
|
|
}
|
|
|
|
public int Native_GetClientJumps(Handle handler, int numParams)
|
|
{
|
|
return gA_Timers[GetNativeCell(1)].iJumps;
|
|
}
|
|
|
|
public int Native_GetBhopStyle(Handle handler, int numParams)
|
|
{
|
|
return gA_Timers[GetNativeCell(1)].bsStyle;
|
|
}
|
|
|
|
public int Native_GetTimerStatus(Handle handler, int numParams)
|
|
{
|
|
return view_as<int>(GetTimerStatus(GetNativeCell(1)));
|
|
}
|
|
|
|
public int Native_IsKZMap(Handle handler, int numParams)
|
|
{
|
|
return view_as<bool>(gB_KZMap);
|
|
}
|
|
|
|
public int Native_StartTimer(Handle handler, int numParams)
|
|
{
|
|
StartTimer(GetNativeCell(1), GetNativeCell(2));
|
|
}
|
|
|
|
public int Native_StopTimer(Handle handler, int numParams)
|
|
{
|
|
int client = GetNativeCell(1);
|
|
bool bBypass = (numParams < 2 || view_as<bool>(GetNativeCell(2)));
|
|
|
|
if(!bBypass)
|
|
{
|
|
bool bResult = true;
|
|
Call_StartForward(gH_Forwards_StopPre);
|
|
Call_PushCell(client);
|
|
Call_PushCell(gA_Timers[client].iTimerTrack);
|
|
Call_Finish(bResult);
|
|
|
|
if(!bResult)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
StopTimer(client);
|
|
|
|
Call_StartForward(gH_Forwards_Stop);
|
|
Call_PushCell(client);
|
|
Call_PushCell(gA_Timers[client].iTimerTrack);
|
|
Call_Finish();
|
|
|
|
return true;
|
|
}
|
|
|
|
public int Native_CanPause(Handle handler, int numParams)
|
|
{
|
|
int client = GetNativeCell(1);
|
|
int iFlags = 0;
|
|
|
|
if(!gCV_Pause.BoolValue)
|
|
{
|
|
iFlags |= CPR_ByConVar;
|
|
}
|
|
|
|
if (!gA_Timers[client].bTimerEnabled)
|
|
{
|
|
iFlags |= CPR_NoTimer;
|
|
}
|
|
|
|
if (Shavit_InsideZone(client, Zone_Start, gA_Timers[client].iTimerTrack))
|
|
{
|
|
iFlags |= CPR_InStartZone;
|
|
}
|
|
|
|
if (Shavit_InsideZone(client, Zone_End, gA_Timers[client].iTimerTrack))
|
|
{
|
|
iFlags |= CPR_InEndZone;
|
|
}
|
|
|
|
if(GetEntPropEnt(client, Prop_Send, "m_hGroundEntity") == -1 && GetEntityMoveType(client) != MOVETYPE_LADDER)
|
|
{
|
|
iFlags |= CPR_NotOnGround;
|
|
}
|
|
|
|
float vel[3];
|
|
GetEntPropVector(client, Prop_Data, "m_vecVelocity", vel);
|
|
if (vel[0] != 0.0 || vel[1] != 0.0 || vel[2] != 0.0)
|
|
{
|
|
iFlags |= CPR_Moving;
|
|
}
|
|
|
|
|
|
float CS_PLAYER_DUCK_SPEED_IDEAL = 8.0;
|
|
bool bDucked, bDucking;
|
|
float fDucktime, fDuckSpeed = CS_PLAYER_DUCK_SPEED_IDEAL;
|
|
|
|
if(gEV_Type != Engine_TF2)
|
|
{
|
|
bDucked = view_as<bool>(GetEntProp(client, Prop_Send, "m_bDucked"));
|
|
bDucking = view_as<bool>(GetEntProp(client, Prop_Send, "m_bDucking"));
|
|
|
|
if(gEV_Type == Engine_CSS)
|
|
{
|
|
fDucktime = GetEntPropFloat(client, Prop_Send, "m_flDucktime");
|
|
}
|
|
else if(gEV_Type == Engine_CSGO)
|
|
{
|
|
fDucktime = GetEntPropFloat(client, Prop_Send, "m_flDuckAmount");
|
|
fDuckSpeed = GetEntPropFloat(client, Prop_Send, "m_flDuckSpeed");
|
|
}
|
|
}
|
|
|
|
if (bDucked || bDucking || fDucktime > 0.0 || fDuckSpeed < CS_PLAYER_DUCK_SPEED_IDEAL || GetClientButtons(client) & IN_DUCK)
|
|
{
|
|
iFlags |= CPR_Duck;
|
|
}
|
|
|
|
return iFlags;
|
|
}
|
|
|
|
public int Native_ChangeClientStyle(Handle handler, int numParams)
|
|
{
|
|
int client = GetNativeCell(1);
|
|
int style = GetNativeCell(2);
|
|
bool force = view_as<bool>(GetNativeCell(3));
|
|
bool manual = view_as<bool>(GetNativeCell(4));
|
|
bool noforward = view_as<bool>(GetNativeCell(5));
|
|
|
|
if(force || Shavit_HasStyleAccess(client, style))
|
|
{
|
|
CallOnStyleChanged(client, gA_Timers[client].bsStyle, style, manual, noforward);
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
public Action Shavit_OnFinishPre(int client, timer_snapshot_t snapshot)
|
|
{
|
|
float minimum_time = GetStyleSettingFloat(snapshot.bsStyle, snapshot.iTimerTrack == Track_Main ? "minimum_time" : "minimum_time_bonus");
|
|
|
|
if (snapshot.fCurrentTime < minimum_time)
|
|
{
|
|
Shavit_PrintToChat(client, "%T", "TimeUnderMinimumTime", client, minimum_time, snapshot.fCurrentTime, snapshot.iTimerTrack == Track_Main ? "minimum_time" : "minimum_time_bonus");
|
|
return Plugin_Stop;
|
|
}
|
|
|
|
return Plugin_Continue;
|
|
}
|
|
|
|
public int Native_FinishMap(Handle handler, int numParams)
|
|
{
|
|
int client = GetNativeCell(1);
|
|
int timestamp = GetTime();
|
|
|
|
if(gCV_UseOffsets.BoolValue)
|
|
{
|
|
CalculateTickIntervalOffset(client, Zone_End);
|
|
|
|
if(gCV_DebugOffsets.BoolValue)
|
|
{
|
|
char sOffsetMessage[100];
|
|
char sOffsetDistance[8];
|
|
FormatEx(sOffsetDistance, 8, "%.1f", gA_Timers[client].fDistanceOffset[Zone_End]);
|
|
FormatEx(sOffsetMessage, sizeof(sOffsetMessage), "[END] %T %d", "DebugOffsets", client, gA_Timers[client].fZoneOffset[Zone_End], sOffsetDistance, gA_Timers[client].iZoneIncrement);
|
|
Shavit_PrintToChat(client, "%s", sOffsetMessage);
|
|
}
|
|
}
|
|
|
|
gA_Timers[client].fCurrentTime = (gA_Timers[client].fTimescaledTicks + gA_Timers[client].fZoneOffset[Zone_Start] + gA_Timers[client].fZoneOffset[Zone_End]) * GetTickInterval();
|
|
|
|
timer_snapshot_t snapshot;
|
|
BuildSnapshot(client, snapshot);
|
|
|
|
Action result = Plugin_Continue;
|
|
Call_StartForward(gH_Forwards_FinishPre);
|
|
Call_PushCell(client);
|
|
Call_PushArrayEx(snapshot, sizeof(timer_snapshot_t), SM_PARAM_COPYBACK);
|
|
Call_Finish(result);
|
|
|
|
if(result != Plugin_Continue && result != Plugin_Changed)
|
|
{
|
|
return;
|
|
}
|
|
|
|
#if DEBUG
|
|
float offset = (gA_Timers[client].fZoneOffset[Zone_Start] + gA_Timers[client].fZoneOffset[Zone_End]) * GetTickInterval();
|
|
PrintToServer("0x%X %f -- ticks*interval -- offsettime=%f ticks=%.0f", snapshot.fCurrentTime, snapshot.fCurrentTime, offset, snapshot.fTimescaledTicks);
|
|
#endif
|
|
|
|
Call_StartForward(gH_Forwards_Finish);
|
|
Call_PushCell(client);
|
|
|
|
int style = 0;
|
|
int track = Track_Main;
|
|
float perfs = 100.0;
|
|
|
|
if(result == Plugin_Continue)
|
|
{
|
|
Call_PushCell(style = gA_Timers[client].bsStyle);
|
|
Call_PushCell(gA_Timers[client].fCurrentTime);
|
|
Call_PushCell(gA_Timers[client].iJumps);
|
|
Call_PushCell(gA_Timers[client].iStrafes);
|
|
//gross
|
|
Call_PushCell((GetStyleSettingBool(gA_Timers[client].bsStyle, "sync"))? (gA_Timers[client].iGoodGains == 0)? 0.0:(gA_Timers[client].iGoodGains / float(gA_Timers[client].iTotalMeasures) * 100.0):-1.0);
|
|
Call_PushCell(track = gA_Timers[client].iTimerTrack);
|
|
perfs = (gA_Timers[client].iMeasuredJumps == 0)? 100.0:(gA_Timers[client].iPerfectJumps / float(gA_Timers[client].iMeasuredJumps) * 100.0);
|
|
}
|
|
else
|
|
{
|
|
Call_PushCell(style = snapshot.bsStyle);
|
|
Call_PushCell(snapshot.fCurrentTime);
|
|
Call_PushCell(snapshot.iJumps);
|
|
Call_PushCell(snapshot.iStrafes);
|
|
// gross
|
|
Call_PushCell((GetStyleSettingBool(snapshot.bsStyle, "sync"))? (snapshot.iGoodGains == 0)? 0.0:(snapshot.iGoodGains / float(snapshot.iTotalMeasures) * 100.0):-1.0);
|
|
Call_PushCell(track = snapshot.iTimerTrack);
|
|
perfs = (snapshot.iMeasuredJumps == 0)? 100.0:(snapshot.iPerfectJumps / float(snapshot.iMeasuredJumps) * 100.0);
|
|
}
|
|
|
|
float oldtime = Shavit_GetClientPB(client, style, track);
|
|
|
|
Call_PushCell(oldtime);
|
|
Call_PushCell(perfs);
|
|
|
|
if(result == Plugin_Continue)
|
|
{
|
|
Call_PushCell(gA_Timers[client].fAvgVelocity);
|
|
Call_PushCell(gA_Timers[client].fMaxVelocity);
|
|
}
|
|
else
|
|
{
|
|
Call_PushCell(snapshot.fAvgVelocity);
|
|
Call_PushCell(snapshot.fMaxVelocity);
|
|
}
|
|
|
|
Call_PushCell(timestamp);
|
|
Call_Finish();
|
|
|
|
StopTimer(client);
|
|
}
|
|
|
|
public int Native_PauseTimer(Handle handler, int numParams)
|
|
{
|
|
int client = GetNativeCell(1);
|
|
|
|
GetClientAbsOrigin(client, gF_PauseOrigin[client]);
|
|
GetClientEyeAngles(client, gF_PauseAngles[client]);
|
|
GetEntPropVector(client, Prop_Data, "m_vecAbsVelocity", gF_PauseVelocity[client]);
|
|
|
|
PauseTimer(client);
|
|
}
|
|
|
|
public any Native_GetZoneOffset(Handle handler, int numParams)
|
|
{
|
|
int client = GetNativeCell(1);
|
|
int zonetype = GetNativeCell(2);
|
|
|
|
if(zonetype > 1 || zonetype < 0)
|
|
{
|
|
return ThrowNativeError(32, "ZoneType is out of bounds");
|
|
}
|
|
|
|
return gA_Timers[client].fZoneOffset[zonetype];
|
|
}
|
|
|
|
public any Native_GetDistanceOffset(Handle handler, int numParams)
|
|
{
|
|
int client = GetNativeCell(1);
|
|
int zonetype = GetNativeCell(2);
|
|
|
|
if(zonetype > 1 || zonetype < 0)
|
|
{
|
|
return ThrowNativeError(32, "ZoneType is out of bounds");
|
|
}
|
|
|
|
return gA_Timers[client].fDistanceOffset[zonetype];
|
|
}
|
|
|
|
public int Native_ResumeTimer(Handle handler, int numParams)
|
|
{
|
|
int client = GetNativeCell(1);
|
|
|
|
ResumeTimer(client);
|
|
|
|
if(numParams >= 2 && view_as<bool>(GetNativeCell(2))) // teleport?
|
|
{
|
|
TeleportEntity(client, gF_PauseOrigin[client], gF_PauseAngles[client], gF_PauseVelocity[client]);
|
|
}
|
|
}
|
|
|
|
public int Native_StopChatSound(Handle handler, int numParams)
|
|
{
|
|
gB_StopChatSound = true;
|
|
}
|
|
|
|
public int Native_PrintToChatAll(Handle plugin, int numParams)
|
|
{
|
|
for (int i = 1; i <= MaxClients; i++)
|
|
{
|
|
if (IsClientInGame(i) && !IsFakeClient(i))
|
|
{
|
|
SetGlobalTransTarget(i);
|
|
|
|
bool previousStopChatSound = gB_StopChatSound;
|
|
SemiNative_PrintToChat(i, 1);
|
|
gB_StopChatSound = previousStopChatSound;
|
|
}
|
|
}
|
|
|
|
gB_StopChatSound = false;
|
|
}
|
|
|
|
public int Native_PrintToChat(Handle handler, int numParams)
|
|
{
|
|
int client = GetNativeCell(1);
|
|
return SemiNative_PrintToChat(client, 2);
|
|
}
|
|
|
|
public int SemiNative_PrintToChat(int client, int formatParam)
|
|
{
|
|
int iWritten;
|
|
char sBuffer[256];
|
|
char sInput[300];
|
|
FormatNativeString(0, formatParam, formatParam+1, sizeof(sInput), iWritten, sInput);
|
|
|
|
char sTime[50];
|
|
|
|
if (gCV_TimeInMessages.BoolValue)
|
|
{
|
|
FormatTime(sTime, sizeof(sTime), gB_Protobuf ? "%H:%M:%S " : "\x01%H:%M:%S ");
|
|
}
|
|
|
|
// space before message needed show colors in cs:go
|
|
// strlen(sBuffer)>252 is when the CSS server stops sending the messages
|
|
// css user message size limit is 255. byte for client, byte for chatsound, 252 chars + 1 null terminator = 255
|
|
FormatEx(sBuffer, (gB_Protobuf ? sizeof(sBuffer) : 253), "%s%s%s %s%s", (gB_Protobuf ? " ":""), sTime, gS_ChatStrings.sPrefix, gS_ChatStrings.sText, sInput);
|
|
|
|
if(client == 0)
|
|
{
|
|
PrintToServer("%s", sBuffer);
|
|
|
|
return false;
|
|
}
|
|
|
|
if(!IsClientInGame(client))
|
|
{
|
|
gB_StopChatSound = false;
|
|
|
|
return false;
|
|
}
|
|
|
|
Handle hSayText2 = StartMessageOne("SayText2", client, USERMSG_RELIABLE|USERMSG_BLOCKHOOKS);
|
|
|
|
if(gB_Protobuf)
|
|
{
|
|
Protobuf pbmsg = UserMessageToProtobuf(hSayText2);
|
|
pbmsg.SetInt("ent_idx", client);
|
|
pbmsg.SetBool("chat", !(gB_StopChatSound || gCV_NoChatSound.BoolValue));
|
|
pbmsg.SetString("msg_name", sBuffer);
|
|
|
|
// needed to not crash
|
|
for(int i = 1; i <= 4; i++)
|
|
{
|
|
pbmsg.AddString("params", "");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
BfWrite bfmsg = UserMessageToBfWrite(hSayText2);
|
|
bfmsg.WriteByte(client);
|
|
bfmsg.WriteByte(!(gB_StopChatSound || gCV_NoChatSound.BoolValue));
|
|
bfmsg.WriteString(sBuffer);
|
|
}
|
|
|
|
EndMessage();
|
|
|
|
gB_StopChatSound = false;
|
|
|
|
return true;
|
|
}
|
|
|
|
public int Native_RestartTimer(Handle handler, int numParams)
|
|
{
|
|
int client = GetNativeCell(1);
|
|
int track = GetNativeCell(2);
|
|
|
|
Shavit_StopTimer(client, true);
|
|
|
|
Call_StartForward(gH_Forwards_OnRestart);
|
|
Call_PushCell(client);
|
|
Call_PushCell(track);
|
|
Call_Finish();
|
|
|
|
if (!gB_Zones)
|
|
{
|
|
StartTimer(client, track);
|
|
}
|
|
}
|
|
|
|
public int Native_GetPerfectJumps(Handle handler, int numParams)
|
|
{
|
|
int client = GetNativeCell(1);
|
|
|
|
return view_as<int>((gA_Timers[client].iMeasuredJumps == 0)? 100.0:(gA_Timers[client].iPerfectJumps / float(gA_Timers[client].iMeasuredJumps) * 100.0));
|
|
}
|
|
|
|
public int Native_GetStrafeCount(Handle handler, int numParams)
|
|
{
|
|
return gA_Timers[GetNativeCell(1)].iStrafes;
|
|
}
|
|
|
|
public int Native_GetSync(Handle handler, int numParams)
|
|
{
|
|
int client = GetNativeCell(1);
|
|
|
|
return view_as<int>((GetStyleSettingBool(gA_Timers[client].bsStyle, "sync")? (gA_Timers[client].iGoodGains == 0)? 0.0:(gA_Timers[client].iGoodGains / float(gA_Timers[client].iTotalMeasures) * 100.0):-1.0));
|
|
}
|
|
|
|
public int Native_GetChatStrings(Handle handler, int numParams)
|
|
{
|
|
int type = GetNativeCell(1);
|
|
int size = GetNativeCell(3);
|
|
|
|
switch(type)
|
|
{
|
|
case sMessagePrefix: return SetNativeString(2, gS_ChatStrings.sPrefix, size);
|
|
case sMessageText: return SetNativeString(2, gS_ChatStrings.sText, size);
|
|
case sMessageWarning: return SetNativeString(2, gS_ChatStrings.sWarning, size);
|
|
case sMessageVariable: return SetNativeString(2, gS_ChatStrings.sVariable, size);
|
|
case sMessageVariable2: return SetNativeString(2, gS_ChatStrings.sVariable2, size);
|
|
case sMessageStyle: return SetNativeString(2, gS_ChatStrings.sStyle, size);
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
public int Native_GetChatStringsStruct(Handle plugin, int numParams)
|
|
{
|
|
if (GetNativeCell(2) != sizeof(chatstrings_t))
|
|
{
|
|
return ThrowNativeError(200, "chatstrings_t does not match latest(got %i expected %i). Please update your includes and recompile your plugins", GetNativeCell(2), sizeof(chatstrings_t));
|
|
}
|
|
|
|
return SetNativeArray(1, gS_ChatStrings, sizeof(gS_ChatStrings));
|
|
}
|
|
|
|
public int Native_SetPracticeMode(Handle handler, int numParams)
|
|
{
|
|
int client = GetNativeCell(1);
|
|
bool practice = view_as<bool>(GetNativeCell(2));
|
|
bool alert = view_as<bool>(GetNativeCell(3));
|
|
|
|
if(alert && practice && !gA_Timers[client].bPracticeMode && (!gB_HUD || (Shavit_GetHUDSettings(client) & HUD_NOPRACALERT) == 0))
|
|
{
|
|
Shavit_PrintToChat(client, "%T", "PracticeModeAlert", client, gS_ChatStrings.sWarning, gS_ChatStrings.sText);
|
|
}
|
|
|
|
gA_Timers[client].bPracticeMode = practice;
|
|
}
|
|
|
|
public int Native_IsPaused(Handle handler, int numParams)
|
|
{
|
|
return view_as<int>(gA_Timers[GetNativeCell(1)].bClientPaused);
|
|
}
|
|
|
|
public int Native_IsPracticeMode(Handle handler, int numParams)
|
|
{
|
|
return view_as<int>(gA_Timers[GetNativeCell(1)].bPracticeMode);
|
|
}
|
|
|
|
public int Native_SaveSnapshot(Handle handler, int numParams)
|
|
{
|
|
if(GetNativeCell(3) != sizeof(timer_snapshot_t))
|
|
{
|
|
return ThrowNativeError(200, "timer_snapshot_t does not match latest(got %i expected %i). Please update your includes and recompile your plugins",
|
|
GetNativeCell(3), sizeof(timer_snapshot_t));
|
|
}
|
|
|
|
int client = GetNativeCell(1);
|
|
|
|
timer_snapshot_t snapshot;
|
|
BuildSnapshot(client, snapshot);
|
|
return SetNativeArray(2, snapshot, sizeof(timer_snapshot_t));
|
|
}
|
|
|
|
public int Native_LoadSnapshot(Handle handler, int numParams)
|
|
{
|
|
if(GetNativeCell(3) != sizeof(timer_snapshot_t))
|
|
{
|
|
return ThrowNativeError(200, "timer_snapshot_t does not match latest(got %i expected %i). Please update your includes and recompile your plugins",
|
|
GetNativeCell(3), sizeof(timer_snapshot_t));
|
|
}
|
|
|
|
int client = GetNativeCell(1);
|
|
|
|
timer_snapshot_t snapshot;
|
|
GetNativeArray(2, snapshot, sizeof(timer_snapshot_t));
|
|
|
|
if (gA_Timers[client].iTimerTrack != snapshot.iTimerTrack)
|
|
{
|
|
CallOnTrackChanged(client, gA_Timers[client].iTimerTrack, snapshot.iTimerTrack);
|
|
}
|
|
|
|
gA_Timers[client].iTimerTrack = snapshot.iTimerTrack;
|
|
|
|
if (gA_Timers[client].bsStyle != snapshot.bsStyle && Shavit_HasStyleAccess(client, snapshot.bsStyle))
|
|
{
|
|
CallOnStyleChanged(client, gA_Timers[client].bsStyle, snapshot.bsStyle, false);
|
|
}
|
|
|
|
gA_Timers[client] = snapshot;
|
|
gA_Timers[client].bClientPaused = snapshot.bClientPaused && snapshot.bTimerEnabled;
|
|
gA_Timers[client].fTimescale = (snapshot.fTimescale > 0.0) ? snapshot.fTimescale : 1.0;
|
|
|
|
return 0;
|
|
}
|
|
|
|
public int Native_LogMessage(Handle plugin, int numParams)
|
|
{
|
|
char sPlugin[32];
|
|
|
|
if(!GetPluginInfo(plugin, PlInfo_Name, sPlugin, 32))
|
|
{
|
|
GetPluginFilename(plugin, sPlugin, 32);
|
|
}
|
|
|
|
static int iWritten = 0;
|
|
|
|
char sBuffer[300];
|
|
FormatNativeString(0, 1, 2, 300, iWritten, sBuffer);
|
|
|
|
LogToFileEx(gS_LogPath, "[%s] %s", sPlugin, sBuffer);
|
|
}
|
|
|
|
public int Native_MarkKZMap(Handle handler, int numParams)
|
|
{
|
|
gB_KZMap = true;
|
|
}
|
|
|
|
public int Native_GetClientTimescale(Handle handler, int numParams)
|
|
{
|
|
int client = GetNativeCell(1);
|
|
|
|
if (gA_Timers[client].fTimescale == GetStyleSettingFloat(gA_Timers[client].bsStyle, "timescale"))
|
|
{
|
|
return view_as<int>(-1.0);
|
|
}
|
|
|
|
return view_as<int>(gA_Timers[client].fTimescale);
|
|
}
|
|
|
|
public int Native_SetClientTimescale(Handle handler, int numParams)
|
|
{
|
|
int client = GetNativeCell(1);
|
|
float timescale = GetNativeCell(2);
|
|
|
|
if (timescale != gA_Timers[client].fTimescale && timescale > 0.0)
|
|
{
|
|
CallOnTimescaleChanged(client, gA_Timers[client].fTimescale, timescale);
|
|
gA_Timers[client].fTimescale = timescale;
|
|
}
|
|
}
|
|
|
|
public any Native_GetAvgVelocity(Handle plugin, int numParams)
|
|
{
|
|
return gA_Timers[GetNativeCell(1)].fAvgVelocity;
|
|
}
|
|
|
|
public any Native_GetMaxVelocity(Handle plugin, int numParams)
|
|
{
|
|
return gA_Timers[GetNativeCell(1)].fMaxVelocity;
|
|
}
|
|
|
|
public any Native_SetAvgVelocity(Handle plugin, int numParams)
|
|
{
|
|
gA_Timers[GetNativeCell(1)].fAvgVelocity = GetNativeCell(2);
|
|
}
|
|
|
|
public any Native_SetMaxVelocity(Handle plugin, int numParams)
|
|
{
|
|
gA_Timers[GetNativeCell(1)].fMaxVelocity = GetNativeCell(2);
|
|
}
|
|
|
|
public Action Shavit_OnStartPre(int client, int track)
|
|
{
|
|
if (GetTimerStatus(client) == Timer_Paused && gCV_PauseMovement.BoolValue)
|
|
{
|
|
return Plugin_Stop;
|
|
}
|
|
|
|
return Plugin_Continue;
|
|
}
|
|
|
|
TimerStatus GetTimerStatus(int client)
|
|
{
|
|
if (!gA_Timers[client].bTimerEnabled)
|
|
{
|
|
return Timer_Stopped;
|
|
}
|
|
else if (gA_Timers[client].bClientPaused)
|
|
{
|
|
return Timer_Paused;
|
|
}
|
|
|
|
return Timer_Running;
|
|
}
|
|
|
|
void StartTimer(int client, int track)
|
|
{
|
|
if(!IsValidClient(client, true) || GetClientTeam(client) < 2 || IsFakeClient(client) || !gB_CookiesRetrieved[client])
|
|
{
|
|
return;
|
|
}
|
|
|
|
float fSpeed[3];
|
|
GetEntPropVector(client, Prop_Data, "m_vecVelocity", fSpeed);
|
|
float curVel = SquareRoot(Pow(fSpeed[0], 2.0) + Pow(fSpeed[1], 2.0));
|
|
|
|
if (!gCV_NoZAxisSpeed.BoolValue ||
|
|
GetStyleSettingInt(gA_Timers[client].bsStyle, "prespeed") == 1 ||
|
|
(fSpeed[2] == 0.0 && (GetStyleSettingInt(gA_Timers[client].bsStyle, "prespeed") == 2 || curVel <= 290.0)))
|
|
{
|
|
Action result = Plugin_Continue;
|
|
Call_StartForward(gH_Forwards_StartPre);
|
|
Call_PushCell(client);
|
|
Call_PushCell(track);
|
|
Call_Finish(result);
|
|
|
|
if(result == Plugin_Continue)
|
|
{
|
|
Call_StartForward(gH_Forwards_Start);
|
|
Call_PushCell(client);
|
|
Call_PushCell(track);
|
|
Call_Finish(result);
|
|
|
|
if (gA_Timers[client].bClientPaused)
|
|
{
|
|
//SetEntityMoveType(client, MOVETYPE_WALK);
|
|
}
|
|
|
|
gA_Timers[client].iZoneIncrement = 0;
|
|
gA_Timers[client].fTimescaledTicks = 0.0;
|
|
gA_Timers[client].bClientPaused = false;
|
|
gA_Timers[client].iStrafes = 0;
|
|
gA_Timers[client].iJumps = 0;
|
|
gA_Timers[client].iTotalMeasures = 0;
|
|
gA_Timers[client].iGoodGains = 0;
|
|
|
|
if (gA_Timers[client].iTimerTrack != track)
|
|
{
|
|
CallOnTrackChanged(client, gA_Timers[client].iTimerTrack, track);
|
|
}
|
|
|
|
gA_Timers[client].iTimerTrack = track;
|
|
gA_Timers[client].bTimerEnabled = true;
|
|
gA_Timers[client].iSHSWCombination = -1;
|
|
gA_Timers[client].fCurrentTime = 0.0;
|
|
gA_Timers[client].bPracticeMode = false;
|
|
gA_Timers[client].iMeasuredJumps = 0;
|
|
gA_Timers[client].iPerfectJumps = 0;
|
|
gA_Timers[client].bCanUseAllKeys = false;
|
|
gA_Timers[client].fZoneOffset[Zone_Start] = 0.0;
|
|
gA_Timers[client].fZoneOffset[Zone_End] = 0.0;
|
|
gA_Timers[client].fDistanceOffset[Zone_Start] = 0.0;
|
|
gA_Timers[client].fDistanceOffset[Zone_End] = 0.0;
|
|
gA_Timers[client].fAvgVelocity = curVel;
|
|
gA_Timers[client].fMaxVelocity = curVel;
|
|
|
|
float mod = gA_Timers[client].fTimescale * GetStyleSettingFloat(gA_Timers[client].bsStyle, "speed");
|
|
SetEntPropFloat(client, Prop_Data, "m_flLaggedMovementValue", mod);
|
|
|
|
if (gB_Eventqueuefix)
|
|
{
|
|
SetEventsTimescale(client, mod);
|
|
}
|
|
|
|
SetEntityGravity(client, GetStyleSettingFloat(gA_Timers[client].bsStyle, "gravity"));
|
|
}
|
|
#if 0
|
|
else if(result == Plugin_Handled || result == Plugin_Stop)
|
|
{
|
|
gA_Timers[client].bTimerEnabled = false;
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
void StopTimer(int client)
|
|
{
|
|
if(!IsValidClient(client) || IsFakeClient(client))
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (gA_Timers[client].bClientPaused)
|
|
{
|
|
//SetEntityMoveType(client, MOVETYPE_WALK);
|
|
}
|
|
|
|
gA_Timers[client].bTimerEnabled = false;
|
|
gA_Timers[client].iJumps = 0;
|
|
gA_Timers[client].fCurrentTime = 0.0;
|
|
gA_Timers[client].bClientPaused = false;
|
|
gA_Timers[client].iStrafes = 0;
|
|
gA_Timers[client].iTotalMeasures = 0;
|
|
gA_Timers[client].iGoodGains = 0;
|
|
}
|
|
|
|
void PauseTimer(int client)
|
|
{
|
|
if(!IsValidClient(client) || IsFakeClient(client))
|
|
{
|
|
return;
|
|
}
|
|
|
|
Call_StartForward(gH_Forwards_OnPause);
|
|
Call_PushCell(client);
|
|
Call_PushCell(gA_Timers[client].iTimerTrack);
|
|
Call_Finish();
|
|
|
|
gA_Timers[client].bClientPaused = true;
|
|
}
|
|
|
|
void ResumeTimer(int client)
|
|
{
|
|
if(!IsValidClient(client) || IsFakeClient(client))
|
|
{
|
|
return;
|
|
}
|
|
|
|
Call_StartForward(gH_Forwards_OnResume);
|
|
Call_PushCell(client);
|
|
Call_PushCell(gA_Timers[client].iTimerTrack);
|
|
Call_Finish();
|
|
|
|
gA_Timers[client].bClientPaused = false;
|
|
// setting is handled in usercmd
|
|
//SetEntityMoveType(client, MOVETYPE_WALK);
|
|
}
|
|
|
|
public void OnClientDisconnect(int client)
|
|
{
|
|
RequestFrame(StopTimer, client);
|
|
}
|
|
|
|
public void OnClientCookiesCached(int client)
|
|
{
|
|
if(IsFakeClient(client) || !IsClientInGame(client))
|
|
{
|
|
return;
|
|
}
|
|
|
|
char sCookie[4];
|
|
|
|
if(gH_AutoBhopCookie != null)
|
|
{
|
|
GetClientCookie(client, gH_AutoBhopCookie, sCookie, 4);
|
|
}
|
|
|
|
gB_Auto[client] = (strlen(sCookie) > 0)? view_as<bool>(StringToInt(sCookie)):true;
|
|
|
|
int style = gI_DefaultStyle;
|
|
|
|
if(gB_StyleCookies && gH_StyleCookie != null)
|
|
{
|
|
GetClientCookie(client, gH_StyleCookie, sCookie, 4);
|
|
int newstyle = StringToInt(sCookie);
|
|
|
|
if (0 <= newstyle < Shavit_GetStyleCount())
|
|
{
|
|
style = newstyle;
|
|
}
|
|
}
|
|
|
|
if(Shavit_HasStyleAccess(client, style))
|
|
{
|
|
CallOnStyleChanged(client, gA_Timers[client].bsStyle, style, false);
|
|
}
|
|
|
|
gB_CookiesRetrieved[client] = true;
|
|
}
|
|
|
|
public void OnClientPutInServer(int client)
|
|
{
|
|
StopTimer(client);
|
|
|
|
if(!IsClientConnected(client) || IsFakeClient(client))
|
|
{
|
|
return;
|
|
}
|
|
|
|
gB_Auto[client] = true;
|
|
gA_Timers[client].fStrafeWarning = 0.0;
|
|
gA_Timers[client].bPracticeMode = false;
|
|
gA_Timers[client].iSHSWCombination = -1;
|
|
gA_Timers[client].iTimerTrack = 0;
|
|
gA_Timers[client].bsStyle = 0;
|
|
gA_Timers[client].fTimescale = 1.0;
|
|
gA_Timers[client].fTimescaledTicks = 0.0;
|
|
gA_Timers[client].iZoneIncrement = 0;
|
|
gS_DeleteMap[client][0] = 0;
|
|
|
|
gB_CookiesRetrieved[client] = false;
|
|
|
|
if(AreClientCookiesCached(client))
|
|
{
|
|
OnClientCookiesCached(client);
|
|
}
|
|
|
|
// not adding style permission check here for obvious reasons
|
|
else
|
|
{
|
|
CallOnStyleChanged(client, 0, gI_DefaultStyle, false);
|
|
}
|
|
|
|
SDKHook(client, SDKHook_PreThinkPost, PreThinkPost);
|
|
SDKHook(client, SDKHook_PostThinkPost, PostThinkPost);
|
|
|
|
if (gEV_Type != Engine_TF2)
|
|
{
|
|
gH_GetPlayerMaxSpeed.HookEntity(Hook_Post, client, CCSPlayer__GetPlayerMaxSpeed);
|
|
}
|
|
|
|
int iSteamID = GetSteamAccountID(client);
|
|
|
|
if(iSteamID == 0)
|
|
{
|
|
KickClient(client, "%T", "VerificationFailed", client);
|
|
|
|
return;
|
|
}
|
|
|
|
char sName[MAX_NAME_LENGTH];
|
|
SanerGetClientName(client, sName);
|
|
ReplaceString(sName, MAX_NAME_LENGTH, "#", "?"); // to avoid this: https://user-images.githubusercontent.com/3672466/28637962-0d324952-724c-11e7-8b27-15ff021f0a59.png
|
|
|
|
int iLength = ((strlen(sName) * 2) + 1);
|
|
char[] sEscapedName = new char[iLength];
|
|
gH_SQL.Escape(sName, sEscapedName, iLength);
|
|
|
|
char sIPAddress[64];
|
|
GetClientIP(client, sIPAddress, 64);
|
|
int iIPAddress = IPStringToAddress(sIPAddress);
|
|
|
|
int iTime = GetTime();
|
|
|
|
char sQuery[512];
|
|
|
|
if(gB_MySQL)
|
|
{
|
|
FormatEx(sQuery, 512,
|
|
"INSERT INTO %susers (auth, name, ip, lastlogin) VALUES (%d, '%s', %d, %d) ON DUPLICATE KEY UPDATE name = '%s', ip = %d, lastlogin = %d;",
|
|
gS_MySQLPrefix, iSteamID, sEscapedName, iIPAddress, iTime, sEscapedName, iIPAddress, iTime);
|
|
}
|
|
|
|
else
|
|
{
|
|
FormatEx(sQuery, 512,
|
|
"REPLACE INTO %susers (auth, name, ip, lastlogin) VALUES (%d, '%s', %d, %d);",
|
|
gS_MySQLPrefix, iSteamID, sEscapedName, iIPAddress, iTime);
|
|
}
|
|
|
|
gH_SQL.Query(SQL_InsertUser_Callback, sQuery, GetClientSerial(client));
|
|
}
|
|
|
|
public void SQL_InsertUser_Callback(Database db, DBResultSet results, const char[] error, any data)
|
|
{
|
|
if(results == null)
|
|
{
|
|
int client = GetClientFromSerial(data);
|
|
|
|
if(client == 0)
|
|
{
|
|
LogError("Timer error! Failed to insert a disconnected player's data to the table. Reason: %s", error);
|
|
}
|
|
|
|
else
|
|
{
|
|
LogError("Timer error! Failed to insert \"%N\"'s data to the table. Reason: %s", client, error);
|
|
}
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
void ReplaceColors(char[] string, int size)
|
|
{
|
|
for(int x = 0; x < sizeof(gS_GlobalColorNames); x++)
|
|
{
|
|
ReplaceString(string, size, gS_GlobalColorNames[x], gS_GlobalColors[x]);
|
|
}
|
|
|
|
for(int x = 0; x < sizeof(gS_CSGOColorNames); x++)
|
|
{
|
|
ReplaceString(string, size, gS_CSGOColorNames[x], gS_CSGOColors[x]);
|
|
}
|
|
|
|
ReplaceString(string, size, "{RGB}", "\x07");
|
|
ReplaceString(string, size, "{RGBA}", "\x08");
|
|
}
|
|
|
|
bool LoadMessages()
|
|
{
|
|
char sPath[PLATFORM_MAX_PATH];
|
|
BuildPath(Path_SM, sPath, PLATFORM_MAX_PATH, "configs/shavit-messages.cfg");
|
|
|
|
KeyValues kv = new KeyValues("shavit-messages");
|
|
|
|
if(!kv.ImportFromFile(sPath))
|
|
{
|
|
delete kv;
|
|
|
|
return false;
|
|
}
|
|
|
|
kv.JumpToKey((IsSource2013(gEV_Type))? "CS:S":"CS:GO");
|
|
|
|
kv.GetString("prefix", gS_ChatStrings.sPrefix, sizeof(chatstrings_t::sPrefix), "\x075e70d0[Timer]");
|
|
kv.GetString("text", gS_ChatStrings.sText, sizeof(chatstrings_t::sText), "\x07ffffff");
|
|
kv.GetString("warning", gS_ChatStrings.sWarning, sizeof(chatstrings_t::sWarning), "\x07af2a22");
|
|
kv.GetString("variable", gS_ChatStrings.sVariable, sizeof(chatstrings_t::sVariable), "\x077fd772");
|
|
kv.GetString("variable2", gS_ChatStrings.sVariable2, sizeof(chatstrings_t::sVariable2), "\x07276f5c");
|
|
kv.GetString("style", gS_ChatStrings.sStyle, sizeof(chatstrings_t::sStyle), "\x07db88c2");
|
|
|
|
delete kv;
|
|
|
|
ReplaceColors(gS_ChatStrings.sPrefix, sizeof(chatstrings_t::sPrefix));
|
|
ReplaceColors(gS_ChatStrings.sText, sizeof(chatstrings_t::sText));
|
|
ReplaceColors(gS_ChatStrings.sWarning, sizeof(chatstrings_t::sWarning));
|
|
ReplaceColors(gS_ChatStrings.sVariable, sizeof(chatstrings_t::sVariable));
|
|
ReplaceColors(gS_ChatStrings.sVariable2, sizeof(chatstrings_t::sVariable2));
|
|
ReplaceColors(gS_ChatStrings.sStyle, sizeof(chatstrings_t::sStyle));
|
|
|
|
Call_StartForward(gH_Forwards_OnChatConfigLoaded);
|
|
Call_Finish();
|
|
|
|
return true;
|
|
}
|
|
|
|
void SQL_DBConnect()
|
|
{
|
|
GetTimerSQLPrefix(gS_MySQLPrefix, 32);
|
|
gH_SQL = GetTimerDatabaseHandle2();
|
|
gB_MySQL = IsMySQLDatabase(gH_SQL);
|
|
|
|
SQL_CreateTables(gH_SQL, gS_MySQLPrefix, gB_MySQL);
|
|
}
|
|
|
|
public void Shavit_OnEnterZone(int client, int type, int track, int id, int entity)
|
|
{
|
|
if(type == Zone_Airaccelerate)
|
|
{
|
|
gF_ZoneAiraccelerate[client] = float(Shavit_GetZoneData(id));
|
|
|
|
UpdateAiraccelerate(client, gF_ZoneAiraccelerate[client]);
|
|
}
|
|
|
|
else if(type == Zone_CustomSpeedLimit)
|
|
{
|
|
gF_ZoneSpeedLimit[client] = float(Shavit_GetZoneData(id));
|
|
}
|
|
}
|
|
|
|
public void Shavit_OnLeaveZone(int client, int type, int track, int id, int entity)
|
|
{
|
|
if(type == Zone_Airaccelerate)
|
|
{
|
|
UpdateAiraccelerate(client, GetStyleSettingFloat(gA_Timers[client].bsStyle, "airaccelerate"));
|
|
}
|
|
}
|
|
|
|
public MRESReturn CCSPlayer__GetPlayerMaxSpeed(int pThis, DHookReturn hReturn)
|
|
{
|
|
if (!gCV_StaticPrestrafe.BoolValue || !IsValidClient(pThis, true))
|
|
{
|
|
return MRES_Ignored;
|
|
}
|
|
|
|
hReturn.Value = GetStyleSettingFloat(gA_Timers[pThis].bsStyle, "runspeed");
|
|
|
|
return MRES_Override;
|
|
}
|
|
|
|
void TF2_MaxSpeedStuff(int client)
|
|
{
|
|
float maxspeed;
|
|
|
|
if (GetEntityFlags(client) & FL_ONGROUND)
|
|
{
|
|
maxspeed = GetStyleSettingFloat(gA_Timers[client].bsStyle, "runspeed");
|
|
}
|
|
else
|
|
{
|
|
// This is used to stop CTFGameMovement::PreventBunnyJumping from destroying
|
|
// player velocity when doing uncrouch stuff. Kind of poopy.
|
|
float fSpeed[3];
|
|
GetEntPropVector(client, Prop_Data, "m_vecAbsVelocity", fSpeed);
|
|
maxspeed = GetVectorLength(fSpeed);
|
|
}
|
|
|
|
// not the best method, but only one i found for tf2
|
|
SetEntPropFloat(client, Prop_Send, "m_flMaxspeed", maxspeed);
|
|
}
|
|
|
|
public void PreThinkPost(int client)
|
|
{
|
|
if(IsPlayerAlive(client))
|
|
{
|
|
if (gEV_Type == Engine_TF2)
|
|
{
|
|
TF2_MaxSpeedStuff(client);
|
|
}
|
|
|
|
if(!gB_Zones || !Shavit_InsideZone(client, Zone_Airaccelerate, -1))
|
|
{
|
|
sv_airaccelerate.FloatValue = GetStyleSettingFloat(gA_Timers[client].bsStyle, "airaccelerate");
|
|
}
|
|
else
|
|
{
|
|
sv_airaccelerate.FloatValue = gF_ZoneAiraccelerate[client];
|
|
}
|
|
|
|
if(sv_enablebunnyhopping != null)
|
|
{
|
|
sv_enablebunnyhopping.BoolValue = GetStyleSettingBool(gA_Timers[client].bsStyle, "bunnyhopping");
|
|
}
|
|
|
|
MoveType mtMoveType = GetEntityMoveType(client);
|
|
|
|
if (GetStyleSettingFloat(gA_Timers[client].bsStyle, "gravity") != 1.0 &&
|
|
(mtMoveType == MOVETYPE_WALK || mtMoveType == MOVETYPE_ISOMETRIC) &&
|
|
(gA_Timers[client].iLastMoveType == MOVETYPE_LADDER || GetEntityGravity(client) == 1.0))
|
|
{
|
|
SetEntityGravity(client, GetStyleSettingFloat(gA_Timers[client].bsStyle, "gravity"));
|
|
}
|
|
|
|
gA_Timers[client].iLastMoveType = mtMoveType;
|
|
}
|
|
}
|
|
|
|
public void PostThinkPost(int client)
|
|
{
|
|
gF_Origin[client][1] = gF_Origin[client][0];
|
|
GetEntPropVector(client, Prop_Data, "m_vecOrigin", gF_Origin[client][0]);
|
|
|
|
if(gA_Timers[client].iZoneIncrement == 1 && gCV_UseOffsets.BoolValue)
|
|
{
|
|
float fVel[3];
|
|
GetEntPropVector(client, Prop_Data, "m_vecVelocity", fVel);
|
|
|
|
if(!gCV_NoZAxisSpeed.BoolValue)
|
|
{
|
|
if(fVel[2] == 0.0)
|
|
{
|
|
CalculateTickIntervalOffset(client, Zone_Start);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
CalculateTickIntervalOffset(client, Zone_Start);
|
|
}
|
|
|
|
if(gCV_DebugOffsets.BoolValue)
|
|
{
|
|
char sOffsetMessage[100];
|
|
char sOffsetDistance[8];
|
|
FormatEx(sOffsetDistance, 8, "%.1f", gA_Timers[client].fDistanceOffset[Zone_Start]);
|
|
FormatEx(sOffsetMessage, sizeof(sOffsetMessage), "[START] %T", "DebugOffsets", client, gA_Timers[client].fZoneOffset[Zone_Start], sOffsetDistance);
|
|
Shavit_PrintToChat(client, "%s", sOffsetMessage);
|
|
}
|
|
}
|
|
}
|
|
|
|
public void OnEntityCreated(int entity, const char[] classname)
|
|
{
|
|
if (StrEqual(classname, "player_speedmod"))
|
|
{
|
|
gH_AcceptInput.HookEntity(Hook_Pre, entity, DHook_AcceptInput_player_speedmod);
|
|
}
|
|
}
|
|
|
|
// bool CBaseEntity::AcceptInput(char const*, CBaseEntity*, CBaseEntity*, variant_t, int)
|
|
public MRESReturn DHook_AcceptInput_player_speedmod(int pThis, DHookReturn hReturn, DHookParam hParams)
|
|
{
|
|
char buf[128];
|
|
hParams.GetString(1, buf, sizeof(buf));
|
|
|
|
if (!StrEqual(buf, "ModifySpeed") || hParams.IsNull(2))
|
|
{
|
|
return MRES_Ignored;
|
|
}
|
|
|
|
int activator = hParams.Get(2);
|
|
|
|
if (!IsValidClient(activator, true))
|
|
{
|
|
return MRES_Ignored;
|
|
}
|
|
|
|
hParams.GetObjectVarString(4, 0, ObjectValueType_String, buf, sizeof(buf));
|
|
|
|
float speed = StringToFloat(buf);
|
|
int style = gA_Timers[activator].bsStyle;
|
|
|
|
speed *= gA_Timers[activator].fTimescale * GetStyleSettingFloat(style, "speed");
|
|
SetEntPropFloat(activator, Prop_Data, "m_flLaggedMovementValue", speed);
|
|
|
|
#if DEBUG
|
|
int caller = hParams.Get(3);
|
|
PrintToServer("ModifySpeed activator = %d(%N), caller = %d, old_speed = %s, new_speed = %f", activator, activator, caller, buf, speed);
|
|
#endif
|
|
|
|
hReturn.Value = true;
|
|
return MRES_Supercede;
|
|
}
|
|
|
|
public MRESReturn DHook_ProcessMovement(Handle hParams)
|
|
{
|
|
int client = DHookGetParam(hParams, 1);
|
|
|
|
// Causes client to do zone touching in movement instead of server frames.
|
|
// From https://github.com/rumourA/End-Touch-Fix
|
|
MaybeDoPhysicsUntouch(client);
|
|
|
|
Call_StartForward(gH_Forwards_OnProcessMovement);
|
|
Call_PushCell(client);
|
|
Call_Finish();
|
|
|
|
return MRES_Ignored;
|
|
}
|
|
|
|
public MRESReturn DHook_ProcessMovementPost(Handle hParams)
|
|
{
|
|
int client = DHookGetParam(hParams, 1);
|
|
|
|
Call_StartForward(gH_Forwards_OnProcessMovementPost);
|
|
Call_PushCell(client);
|
|
Call_Finish();
|
|
|
|
if (gA_Timers[client].bClientPaused || !gA_Timers[client].bTimerEnabled)
|
|
{
|
|
return MRES_Ignored;
|
|
}
|
|
|
|
float interval = GetTickInterval();
|
|
float time = interval * gA_Timers[client].fTimescale;
|
|
float timeOrig = time;
|
|
|
|
gA_Timers[client].iZoneIncrement++;
|
|
|
|
timer_snapshot_t snapshot;
|
|
BuildSnapshot(client, snapshot);
|
|
|
|
Call_StartForward(gH_Forwards_OnTimerIncrement);
|
|
Call_PushCell(client);
|
|
Call_PushArray(snapshot, sizeof(timer_snapshot_t));
|
|
Call_PushCellRef(time);
|
|
Call_Finish();
|
|
|
|
if (time == timeOrig)
|
|
{
|
|
gA_Timers[client].fTimescaledTicks += gA_Timers[client].fTimescale;
|
|
}
|
|
else
|
|
{
|
|
gA_Timers[client].fTimescaledTicks += time / interval;
|
|
}
|
|
|
|
gA_Timers[client].fCurrentTime = interval * gA_Timers[client].fTimescaledTicks;
|
|
|
|
Call_StartForward(gH_Forwards_OnTimerIncrementPost);
|
|
Call_PushCell(client);
|
|
Call_PushCell(time);
|
|
Call_Finish();
|
|
|
|
return MRES_Ignored;
|
|
}
|
|
|
|
// reference: https://github.com/momentum-mod/game/blob/5e2d1995ca7c599907980ee5b5da04d7b5474c61/mp/src/game/server/momentum/mom_timer.cpp#L388
|
|
void CalculateTickIntervalOffset(int client, int zonetype)
|
|
{
|
|
float localOrigin[3];
|
|
GetEntPropVector(client, Prop_Send, "m_vecOrigin", localOrigin);
|
|
float maxs[3];
|
|
float mins[3];
|
|
float vel[3];
|
|
GetEntPropVector(client, Prop_Send, "m_vecMins", mins);
|
|
GetEntPropVector(client, Prop_Send, "m_vecMaxs", maxs);
|
|
GetEntPropVector(client, Prop_Data, "m_vecVelocity", vel);
|
|
|
|
gF_SmallestDist[client] = 0.0;
|
|
|
|
if (zonetype == Zone_Start)
|
|
{
|
|
TR_EnumerateEntitiesHull(localOrigin, gF_Origin[client][1], mins, maxs, PARTITION_TRIGGER_EDICTS, TREnumTrigger, client);
|
|
}
|
|
else
|
|
{
|
|
TR_EnumerateEntitiesHull(gF_Origin[client][0], localOrigin, mins, maxs, PARTITION_TRIGGER_EDICTS, TREnumTrigger, client);
|
|
}
|
|
|
|
float offset = gF_Fraction[client] * GetTickInterval();
|
|
|
|
gA_Timers[client].fZoneOffset[zonetype] = gF_Fraction[client];
|
|
gA_Timers[client].fDistanceOffset[zonetype] = gF_SmallestDist[client];
|
|
|
|
Call_StartForward(gH_Forwards_OnTimeOffsetCalculated);
|
|
Call_PushCell(client);
|
|
Call_PushCell(zonetype);
|
|
Call_PushCell(offset);
|
|
Call_PushCell(gF_SmallestDist[client]);
|
|
Call_Finish();
|
|
|
|
gF_SmallestDist[client] = 0.0;
|
|
}
|
|
|
|
bool TREnumTrigger(int entity, int client) {
|
|
|
|
if (entity <= MaxClients) {
|
|
return true;
|
|
}
|
|
|
|
char classname[32];
|
|
GetEntityClassname(entity, classname, sizeof(classname));
|
|
|
|
//the entity is a zone
|
|
if(StrContains(classname, "trigger_multiple") > -1)
|
|
{
|
|
TR_ClipCurrentRayToEntity(MASK_ALL, entity);
|
|
|
|
float start[3];
|
|
TR_GetStartPosition(INVALID_HANDLE, start);
|
|
|
|
float end[3];
|
|
TR_GetEndPosition(end);
|
|
|
|
float distance = GetVectorDistance(start, end);
|
|
gF_SmallestDist[client] = distance;
|
|
gF_Fraction[client] = TR_GetFraction();
|
|
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void BuildSnapshot(int client, timer_snapshot_t snapshot)
|
|
{
|
|
snapshot = gA_Timers[client];
|
|
snapshot.fServerTime = GetEngineTime();
|
|
snapshot.fTimescale = (gA_Timers[client].fTimescale > 0.0) ? gA_Timers[client].fTimescale : 1.0;
|
|
//snapshot.iLandingTick = ?????; // TODO: Think about handling segmented scroll? /shrug
|
|
}
|
|
|
|
public 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])
|
|
{
|
|
if(IsFakeClient(client))
|
|
{
|
|
return Plugin_Continue;
|
|
}
|
|
|
|
Remove_sv_cheat_Impluses(client, impulse);
|
|
|
|
int flags = GetEntityFlags(client);
|
|
|
|
if (gA_Timers[client].bClientPaused && IsPlayerAlive(client) && !gCV_PauseMovement.BoolValue)
|
|
{
|
|
buttons = 0;
|
|
vel = view_as<float>({0.0, 0.0, 0.0});
|
|
|
|
SetEntityFlags(client, (flags | FL_ATCONTROLS));
|
|
|
|
//SetEntityMoveType(client, MOVETYPE_NONE);
|
|
|
|
return Plugin_Changed;
|
|
}
|
|
|
|
SetEntityFlags(client, (flags & ~FL_ATCONTROLS));
|
|
|
|
// Wait till now to return so spectators can free-cam while paused...
|
|
if(!IsPlayerAlive(client))
|
|
{
|
|
return Plugin_Changed;
|
|
}
|
|
|
|
Action result = Plugin_Continue;
|
|
Call_StartForward(gH_Forwards_OnUserCmdPre);
|
|
Call_PushCell(client);
|
|
Call_PushCellRef(buttons);
|
|
Call_PushCellRef(impulse);
|
|
Call_PushArrayEx(vel, 3, SM_PARAM_COPYBACK);
|
|
Call_PushArrayEx(angles, 3, SM_PARAM_COPYBACK);
|
|
Call_PushCell(GetTimerStatus(client));
|
|
Call_PushCell(gA_Timers[client].iTimerTrack);
|
|
Call_PushCell(gA_Timers[client].bsStyle);
|
|
Call_PushArrayEx(mouse, 2, SM_PARAM_COPYBACK);
|
|
Call_Finish(result);
|
|
|
|
if(result != Plugin_Continue && result != Plugin_Changed)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
int iGroundEntity = GetEntPropEnt(client, Prop_Send, "m_hGroundEntity");
|
|
bool bInStart = Shavit_InsideZone(client, Zone_Start, gA_Timers[client].iTimerTrack);
|
|
|
|
if (gA_Timers[client].bTimerEnabled && !gA_Timers[client].bClientPaused)
|
|
{
|
|
// +left/right block
|
|
if(!gB_Zones || (!bInStart && ((GetStyleSettingInt(gA_Timers[client].bsStyle, "block_pleft") > 0 &&
|
|
(buttons & IN_LEFT) > 0) || (GetStyleSettingInt(gA_Timers[client].bsStyle, "block_pright") > 0 && (buttons & IN_RIGHT) > 0))))
|
|
{
|
|
vel[0] = 0.0;
|
|
vel[1] = 0.0;
|
|
|
|
if(GetStyleSettingInt(gA_Timers[client].bsStyle, "block_pright") >= 2)
|
|
{
|
|
char sCheatDetected[64];
|
|
FormatEx(sCheatDetected, 64, "%T", "LeftRightCheat", client);
|
|
StopTimer_Cheat(client, sCheatDetected);
|
|
}
|
|
}
|
|
|
|
// +strafe block
|
|
if (GetStyleSettingInt(gA_Timers[client].bsStyle, "block_pstrafe") > 0 &&
|
|
((vel[0] > 0.0 && (buttons & IN_FORWARD) == 0) || (vel[0] < 0.0 && (buttons & IN_BACK) == 0) ||
|
|
(vel[1] > 0.0 && (buttons & IN_MOVERIGHT) == 0) || (vel[1] < 0.0 && (buttons & IN_MOVELEFT) == 0)))
|
|
{
|
|
if (gA_Timers[client].fStrafeWarning < gA_Timers[client].fCurrentTime)
|
|
{
|
|
if (GetStyleSettingInt(gA_Timers[client].bsStyle, "block_pstrafe") >= 2)
|
|
{
|
|
char sCheatDetected[64];
|
|
FormatEx(sCheatDetected, 64, "%T", "Inconsistencies", client);
|
|
StopTimer_Cheat(client, sCheatDetected);
|
|
}
|
|
|
|
vel[0] = 0.0;
|
|
vel[1] = 0.0;
|
|
|
|
return Plugin_Changed;
|
|
}
|
|
|
|
gA_Timers[client].fStrafeWarning = gA_Timers[client].fCurrentTime + 0.3;
|
|
}
|
|
}
|
|
|
|
#if DEBUG
|
|
static int cycle = 0;
|
|
|
|
if(++cycle % 50 == 0)
|
|
{
|
|
Shavit_StopChatSound();
|
|
Shavit_PrintToChat(client, "vel[0]: %.01f | vel[1]: %.01f", vel[0], vel[1]);
|
|
}
|
|
#endif
|
|
|
|
int iPButtons = buttons;
|
|
|
|
if (!gA_Timers[client].bClientPaused)
|
|
{
|
|
if (GetStyleSettingBool(gA_Timers[client].bsStyle, "strafe_count_w") && !GetStyleSettingBool(gA_Timers[client].bsStyle, "block_w") &&
|
|
(gA_Timers[client].iLastButtons & IN_FORWARD) == 0 && (buttons & IN_FORWARD) > 0)
|
|
{
|
|
gA_Timers[client].iStrafes++;
|
|
}
|
|
|
|
if (GetStyleSettingBool(gA_Timers[client].bsStyle, "strafe_count_a") && !GetStyleSettingBool(gA_Timers[client].bsStyle, "block_a") && (gA_Timers[client].iLastButtons & IN_MOVELEFT) == 0 &&
|
|
(buttons & IN_MOVELEFT) > 0 && (GetStyleSettingInt(gA_Timers[client].bsStyle, "force_hsw") > 0 || ((buttons & IN_FORWARD) == 0 && (buttons & IN_BACK) == 0)))
|
|
{
|
|
gA_Timers[client].iStrafes++;
|
|
}
|
|
|
|
if (GetStyleSettingBool(gA_Timers[client].bsStyle, "strafe_count_s") && !GetStyleSettingBool(gA_Timers[client].bsStyle, "block_s") &&
|
|
(gA_Timers[client].iLastButtons & IN_BACK) == 0 && (buttons & IN_BACK) > 0)
|
|
{
|
|
gA_Timers[client].iStrafes++;
|
|
}
|
|
|
|
if (GetStyleSettingBool(gA_Timers[client].bsStyle, "strafe_count_d") && !GetStyleSettingBool(gA_Timers[client].bsStyle, "block_d") && (gA_Timers[client].iLastButtons & IN_MOVERIGHT) == 0 &&
|
|
(buttons & IN_MOVERIGHT) > 0 && (GetStyleSettingInt(gA_Timers[client].bsStyle, "force_hsw") > 0 || ((buttons & IN_FORWARD) == 0 && (buttons & IN_BACK) == 0)))
|
|
{
|
|
gA_Timers[client].iStrafes++;
|
|
}
|
|
}
|
|
|
|
|
|
MoveType mtMoveType = GetEntityMoveType(client);
|
|
|
|
if(mtMoveType == MOVETYPE_LADDER && gCV_SimplerLadders.BoolValue)
|
|
{
|
|
gA_Timers[client].bCanUseAllKeys = true;
|
|
}
|
|
|
|
else if(iGroundEntity != -1)
|
|
{
|
|
gA_Timers[client].bCanUseAllKeys = false;
|
|
}
|
|
|
|
// key blocking
|
|
if(!gA_Timers[client].bCanUseAllKeys && mtMoveType != MOVETYPE_NOCLIP && mtMoveType != MOVETYPE_LADDER && !Shavit_InsideZone(client, Zone_Freestyle, -1))
|
|
{
|
|
// block E
|
|
if (GetStyleSettingBool(gA_Timers[client].bsStyle, "block_use") && (buttons & IN_USE) > 0)
|
|
{
|
|
buttons &= ~IN_USE;
|
|
}
|
|
|
|
if (iGroundEntity == -1 || GetStyleSettingBool(gA_Timers[client].bsStyle, "force_groundkeys"))
|
|
{
|
|
if (GetStyleSettingBool(gA_Timers[client].bsStyle, "block_w") && ((buttons & IN_FORWARD) > 0 || vel[0] > 0.0))
|
|
{
|
|
vel[0] = 0.0;
|
|
buttons &= ~IN_FORWARD;
|
|
}
|
|
|
|
if (GetStyleSettingBool(gA_Timers[client].bsStyle, "block_a") && ((buttons & IN_MOVELEFT) > 0 || vel[1] < 0.0))
|
|
{
|
|
vel[1] = 0.0;
|
|
buttons &= ~IN_MOVELEFT;
|
|
}
|
|
|
|
if (GetStyleSettingBool(gA_Timers[client].bsStyle, "block_s") && ((buttons & IN_BACK) > 0 || vel[0] < 0.0))
|
|
{
|
|
vel[0] = 0.0;
|
|
buttons &= ~IN_BACK;
|
|
}
|
|
|
|
if (GetStyleSettingBool(gA_Timers[client].bsStyle, "block_d") && ((buttons & IN_MOVERIGHT) > 0 || vel[1] > 0.0))
|
|
{
|
|
vel[1] = 0.0;
|
|
buttons &= ~IN_MOVERIGHT;
|
|
}
|
|
|
|
// HSW
|
|
// Theory about blocking non-HSW strafes while playing HSW:
|
|
// Block S and W without A or D.
|
|
// Block A and D without S or W.
|
|
if (GetStyleSettingInt(gA_Timers[client].bsStyle, "force_hsw") > 0)
|
|
{
|
|
bool bSHSW = (GetStyleSettingInt(gA_Timers[client].bsStyle, "force_hsw") == 2) && !bInStart; // don't decide on the first valid input until out of start zone!
|
|
int iCombination = -1;
|
|
|
|
bool bForward = ((buttons & IN_FORWARD) > 0 && vel[0] > 0.0);
|
|
bool bMoveLeft = ((buttons & IN_MOVELEFT) > 0 && vel[1] < 0.0);
|
|
bool bBack = ((buttons & IN_BACK) > 0 && vel[0] < 0.0);
|
|
bool bMoveRight = ((buttons & IN_MOVERIGHT) > 0 && vel[1] > 0.0);
|
|
|
|
if(bSHSW)
|
|
{
|
|
if((bForward && bMoveLeft) || (bBack && bMoveRight))
|
|
{
|
|
iCombination = 0;
|
|
}
|
|
else if((bForward && bMoveRight || bBack && bMoveLeft))
|
|
{
|
|
iCombination = 1;
|
|
}
|
|
|
|
// int gI_SHSW_FirstCombination[MAXPLAYERS+1]; // 0 - W/A S/D | 1 - W/D S/A
|
|
if(gA_Timers[client].iSHSWCombination == -1 && iCombination != -1)
|
|
{
|
|
Shavit_PrintToChat(client, "%T", (iCombination == 0)? "SHSWCombination0":"SHSWCombination1", client, gS_ChatStrings.sVariable, gS_ChatStrings.sText);
|
|
gA_Timers[client].iSHSWCombination = iCombination;
|
|
}
|
|
|
|
// W/A S/D
|
|
if((gA_Timers[client].iSHSWCombination == 0 && iCombination != 0) ||
|
|
// W/D S/A
|
|
(gA_Timers[client].iSHSWCombination == 1 && iCombination != 1) ||
|
|
// no valid combination & no valid input
|
|
(gA_Timers[client].iSHSWCombination == -1 && iCombination == -1))
|
|
{
|
|
vel[0] = 0.0;
|
|
vel[1] = 0.0;
|
|
|
|
buttons &= ~IN_FORWARD;
|
|
buttons &= ~IN_MOVELEFT;
|
|
buttons &= ~IN_MOVERIGHT;
|
|
buttons &= ~IN_BACK;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(bBack && (bMoveLeft || bMoveRight))
|
|
{
|
|
vel[0] = 0.0;
|
|
|
|
buttons &= ~IN_FORWARD;
|
|
buttons &= ~IN_BACK;
|
|
}
|
|
|
|
if(bForward && !(bMoveLeft || bMoveRight))
|
|
{
|
|
vel[0] = 0.0;
|
|
|
|
buttons &= ~IN_FORWARD;
|
|
buttons &= ~IN_BACK;
|
|
}
|
|
|
|
if((bMoveLeft || bMoveRight) && !bForward)
|
|
{
|
|
vel[1] = 0.0;
|
|
|
|
buttons &= ~IN_MOVELEFT;
|
|
buttons &= ~IN_MOVERIGHT;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool bInWater = (GetEntProp(client, Prop_Send, "m_nWaterLevel") >= 2);
|
|
|
|
// enable duck-jumping/bhop in tf2
|
|
if (gEV_Type == Engine_TF2 && GetStyleSettingBool(gA_Timers[client].bsStyle, "bunnyhopping") && (buttons & IN_JUMP) > 0 && iGroundEntity != -1)
|
|
{
|
|
float fSpeed[3];
|
|
GetEntPropVector(client, Prop_Data, "m_vecAbsVelocity", fSpeed);
|
|
|
|
fSpeed[2] = 289.0;
|
|
SetEntPropVector(client, Prop_Data, "m_vecAbsVelocity", fSpeed);
|
|
}
|
|
|
|
if (GetStyleSettingBool(gA_Timers[client].bsStyle, "autobhop") && gB_Auto[client] && (buttons & IN_JUMP) > 0 && mtMoveType == MOVETYPE_WALK && !bInWater)
|
|
{
|
|
int iOldButtons = GetEntProp(client, Prop_Data, "m_nOldButtons");
|
|
SetEntProp(client, Prop_Data, "m_nOldButtons", (iOldButtons & ~IN_JUMP));
|
|
}
|
|
|
|
// perf jump measuring
|
|
bool bOnGround = (!bInWater && mtMoveType == MOVETYPE_WALK && iGroundEntity != -1);
|
|
|
|
if(bOnGround && !gA_Timers[client].bOnGround)
|
|
{
|
|
gA_Timers[client].iLandingTick = tickcount;
|
|
|
|
if (gEV_Type != Engine_TF2 && GetStyleSettingBool(gA_Timers[client].bsStyle, "easybhop"))
|
|
{
|
|
SetEntPropFloat(client, Prop_Send, "m_flStamina", 0.0);
|
|
}
|
|
}
|
|
|
|
else if (!bOnGround && gA_Timers[client].bOnGround && gA_Timers[client].bJumped && !gA_Timers[client].bClientPaused)
|
|
{
|
|
int iDifference = (tickcount - gA_Timers[client].iLandingTick);
|
|
|
|
if(iDifference < 10)
|
|
{
|
|
gA_Timers[client].iMeasuredJumps++;
|
|
|
|
if(iDifference == 1)
|
|
{
|
|
gA_Timers[client].iPerfectJumps++;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (bInStart && gCV_BlockPreJump.BoolValue && GetStyleSettingInt(gA_Timers[client].bsStyle, "prespeed") == 0 && (vel[2] > 0 || (buttons & IN_JUMP) > 0))
|
|
{
|
|
vel[2] = 0.0;
|
|
buttons &= ~IN_JUMP;
|
|
}
|
|
|
|
// velocity limit
|
|
if (iGroundEntity != -1 && GetStyleSettingFloat(gA_Timers[client].bsStyle, "velocity_limit") > 0.0)
|
|
{
|
|
float fSpeedLimit = GetStyleSettingFloat(gA_Timers[client].bsStyle, "velocity_limit");
|
|
|
|
if(gB_Zones && Shavit_InsideZone(client, Zone_CustomSpeedLimit, -1))
|
|
{
|
|
fSpeedLimit = gF_ZoneSpeedLimit[client];
|
|
}
|
|
|
|
float fSpeed[3];
|
|
GetEntPropVector(client, Prop_Data, "m_vecVelocity", fSpeed);
|
|
|
|
float fSpeed_New = (SquareRoot(Pow(fSpeed[0], 2.0) + Pow(fSpeed[1], 2.0)));
|
|
|
|
if(fSpeedLimit != 0.0 && fSpeed_New > 0.0)
|
|
{
|
|
float fScale = fSpeedLimit / fSpeed_New;
|
|
|
|
if(fScale < 1.0)
|
|
{
|
|
ScaleVector(fSpeed, fScale);
|
|
TeleportEntity(client, NULL_VECTOR, NULL_VECTOR, fSpeed); // maybe change this to SetEntPropVector some time?
|
|
}
|
|
}
|
|
}
|
|
|
|
float fAngle = GetAngleDiff(angles[1], gA_Timers[client].fLastAngle);
|
|
|
|
if (!gA_Timers[client].bClientPaused && iGroundEntity == -1 && (GetEntityFlags(client) & FL_INWATER) == 0 && fAngle != 0.0)
|
|
{
|
|
float fAbsVelocity[3];
|
|
GetEntPropVector(client, Prop_Data, "m_vecAbsVelocity", fAbsVelocity);
|
|
|
|
if(SquareRoot(Pow(fAbsVelocity[0], 2.0) + Pow(fAbsVelocity[1], 2.0)) > 0.0)
|
|
{
|
|
float fTempAngle = angles[1];
|
|
|
|
float fAngles[3];
|
|
GetVectorAngles(fAbsVelocity, fAngles);
|
|
|
|
if(fTempAngle < 0.0)
|
|
{
|
|
fTempAngle += 360.0;
|
|
}
|
|
|
|
TestAngles(client, (fTempAngle - fAngles[1]), fAngle, vel);
|
|
}
|
|
}
|
|
|
|
if (GetTimerStatus(client) == Timer_Running && gA_Timers[client].fCurrentTime != 0.0)
|
|
{
|
|
float frameCount = float(gA_Timers[client].iZoneIncrement);
|
|
float fAbsVelocity[3];
|
|
GetEntPropVector(client, Prop_Data, "m_vecAbsVelocity", fAbsVelocity);
|
|
float curVel = SquareRoot(Pow(fAbsVelocity[0], 2.0) + Pow(fAbsVelocity[1], 2.0));
|
|
float maxVel = gA_Timers[client].fMaxVelocity;
|
|
gA_Timers[client].fMaxVelocity = (curVel > maxVel) ? curVel : maxVel;
|
|
// STOLEN from Epic/Disrevoid. Thx :)
|
|
gA_Timers[client].fAvgVelocity += (curVel - gA_Timers[client].fAvgVelocity) / frameCount;
|
|
}
|
|
|
|
gA_Timers[client].iLastButtons = iPButtons;
|
|
gA_Timers[client].fLastAngle = angles[1];
|
|
gA_Timers[client].bJumped = false;
|
|
gA_Timers[client].bOnGround = bOnGround;
|
|
|
|
return Plugin_Continue;
|
|
}
|
|
|
|
void TestAngles(int client, float dirangle, float yawdelta, float vel[3])
|
|
{
|
|
if(dirangle < 0.0)
|
|
{
|
|
dirangle = -dirangle;
|
|
}
|
|
|
|
// normal
|
|
if(dirangle < 22.5 || dirangle > 337.5)
|
|
{
|
|
gA_Timers[client].iTotalMeasures++;
|
|
|
|
if((yawdelta > 0.0 && vel[1] <= -100.0) || (yawdelta < 0.0 && vel[1] >= 100.0))
|
|
{
|
|
gA_Timers[client].iGoodGains++;
|
|
}
|
|
}
|
|
|
|
// hsw (thanks nairda!)
|
|
else if((dirangle > 22.5 && dirangle < 67.5))
|
|
{
|
|
gA_Timers[client].iTotalMeasures++;
|
|
|
|
if((yawdelta != 0.0) && (vel[0] >= 100.0 || vel[1] >= 100.0) && (vel[0] >= -100.0 || vel[1] >= -100.0))
|
|
{
|
|
gA_Timers[client].iGoodGains++;
|
|
}
|
|
}
|
|
|
|
// sw
|
|
else if((dirangle > 67.5 && dirangle < 112.5) || (dirangle > 247.5 && dirangle < 292.5))
|
|
{
|
|
gA_Timers[client].iTotalMeasures++;
|
|
|
|
if(vel[0] <= -100.0 || vel[0] >= 100.0)
|
|
{
|
|
gA_Timers[client].iGoodGains++;
|
|
}
|
|
}
|
|
}
|
|
|
|
void StopTimer_Cheat(int client, const char[] message)
|
|
{
|
|
Shavit_StopTimer(client);
|
|
Shavit_PrintToChat(client, "%T", "CheatTimerStop", client, gS_ChatStrings.sWarning, gS_ChatStrings.sText, message);
|
|
}
|
|
|
|
void UpdateAiraccelerate(int client, float airaccelerate)
|
|
{
|
|
char sAiraccelerate[8];
|
|
FloatToString(airaccelerate, sAiraccelerate, 8);
|
|
sv_airaccelerate.ReplicateToClient(client, sAiraccelerate);
|
|
}
|
|
|
|
void UpdateStyleSettings(int client)
|
|
{
|
|
if(sv_autobunnyhopping != null)
|
|
{
|
|
sv_autobunnyhopping.ReplicateToClient(client, (GetStyleSettingBool(gA_Timers[client].bsStyle, "autobhop") && gB_Auto[client])? "1":"0");
|
|
}
|
|
|
|
if(sv_enablebunnyhopping != null)
|
|
{
|
|
sv_enablebunnyhopping.ReplicateToClient(client, (GetStyleSettingBool(gA_Timers[client].bsStyle, "bunnyhopping"))? "1":"0");
|
|
}
|
|
|
|
UpdateAiraccelerate(client, GetStyleSettingFloat(gA_Timers[client].bsStyle, "airaccelerate"));
|
|
}
|