From 1ce6acc5f40037a97c90d6ea77966842cc151f21 Mon Sep 17 00:00:00 2001 From: XutaxKamay Date: Sun, 2 Jan 2022 11:13:10 +0000 Subject: [PATCH] xutax autostrafer stuff --- addons/sourcemod/gamedata/shavit.games.txt | 12 + .../scripting/include/shavit/tas-xutax.inc | 455 ++++++++++++++++++ addons/sourcemod/scripting/shavit-tas.sp | 249 ++++++++++ 3 files changed, 716 insertions(+) create mode 100644 addons/sourcemod/scripting/include/shavit/tas-xutax.inc create mode 100644 addons/sourcemod/scripting/shavit-tas.sp diff --git a/addons/sourcemod/gamedata/shavit.games.txt b/addons/sourcemod/gamedata/shavit.games.txt index 65e5422e..75a000a5 100644 --- a/addons/sourcemod/gamedata/shavit.games.txt +++ b/addons/sourcemod/gamedata/shavit.games.txt @@ -66,6 +66,12 @@ "windows" "409" "linux" "410" } + // TODO + "m_surfaceFriction" + { + "windows" "8" + "linux" "8" + } } "Signatures" @@ -129,6 +135,12 @@ "windows" "358" "linux" "359" } + // TODO + "m_surfaceFriction" + { + "windows" "104" + "linux" "104" + } } "Signatures" diff --git a/addons/sourcemod/scripting/include/shavit/tas-xutax.inc b/addons/sourcemod/scripting/include/shavit/tas-xutax.inc new file mode 100644 index 00000000..6d6f0cf2 --- /dev/null +++ b/addons/sourcemod/scripting/include/shavit/tas-xutax.inc @@ -0,0 +1,455 @@ +/* + * tas-xutax.inc file + * by: xutaxkamay, shavit + * + * 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 . + * + */ + +#if defined _shavit_tas_xutax_included + #endinput +#endif +#define _shavit_tas_xutax_included + + +// taken from shavit's oryx +stock bool IsSurfing(int client) +{ + float fPosition[3]; + GetClientAbsOrigin(client, fPosition); + + float fEnd[3]; + fEnd = fPosition; + fEnd[2] -= 64.0; + + float fMins[3]; + GetEntPropVector(client, Prop_Send, "m_vecMins", fMins); + + float fMaxs[3]; + GetEntPropVector(client, Prop_Send, "m_vecMaxs", fMaxs); + + Handle hTR = TR_TraceHullFilterEx(fPosition, fEnd, fMins, fMaxs, MASK_PLAYERSOLID, TRFilter_NoPlayers, client); + + if(TR_DidHit(hTR)) + { + float fNormal[3]; + TR_GetPlaneNormal(hTR, fNormal); + + delete hTR; + + // If the plane normal's Z axis is 0.7 or below (alternatively, -0.7 when upside-down) then it's a surf ramp. + // https://github.com/alliedmodders/hl2sdk/blob/92dcf04225a278b75170cc84917f04e98f5d08ec/game/server/physics_main.cpp#L1059 + // https://github.com/ValveSoftware/source-sdk-2013/blob/0d8dceea4310fde5706b3ce1c70609d72a38efdf/mp/src/game/server/physics_main.cpp#L1065 + + return (-0.7 <= fNormal[2] <= 0.7); + } + + delete hTR; + + return false; +} + +public bool TRFilter_NoPlayers(int entity, int mask, any data) +{ + return (entity != view_as(data) || (entity < 1 || entity > MaxClients)); +} + + +float AngleNormalize(float flAngle) +{ + if (flAngle > 180.0) + flAngle -= 360.0; + else if (flAngle < -180.0) + flAngle += 360.0; + + return flAngle; +} + +float Vec2DToYaw(float vec[2]) +{ + float flYaw = 0.0; + + if (vec[0] != 0.0 || vec[1] != 0.0) + { + float vecNormalized[2]; + + float flLength = SquareRoot(vec[0] * vec[0] + vec[1] * vec[1]); + + vecNormalized[0] = vec[0] / flLength; + vecNormalized[1] = vec[1] / flLength; + + // Credits to Valve. + flYaw = ArcTangent2(vecNormalized[1], vecNormalized[0]) * (180.0 / FLOAT_PI); + + flYaw = AngleNormalize(flYaw); + } + + return flYaw; +} + +/* + * So our problem here is to find a wishdir that no matter the angles we choose, it should go to the direction we want. + * So forward/right vector changing but not sidemove and forwardmove for the case where we modify our angles. (1) + * But in our case we want sidemove and forwardmove values changing and not the forward/right vectors. (2) + * So our unknown variables is fmove and smove to know the (2) case. But we know the (1) case so we can solve this into a linear equation. + * To make it more simplier, we know the wishdir values and forward/right vectors, but we do not know the fowardmove and sidemove variables + * and that's what we want to solve. + * That's what is doing this function, but only in 2D since we can only move forward or side. + * But, for noclip (3D) it's a different story that I will let you discover, same method, but 3 equations and 3 unknown variables (forwardmove, sidemove, upmove). + */ + +void Solve2DMovementsVars(float vecWishDir[2], float vecForward[2], float vecRight[2], float &flForwardMove, float &flSideMove) +{ + // wishdir[0] = foward[0] * forwardmove + right[0] * sidemove; + // wishdir[1] = foward[1] * forwardmove + right[1] * sidemove; + + // Let's translate this to letters. + // v = a * b + c * d + // w = e * b + f * d + // v = wishdir[0]; w = wishdir[1]... + + // Now let's solve it with online solver https://quickmath.com/webMathematica3/quickmath/equations/solve/advanced.jsp + // https://cdn.discordapp.com/attachments/609163806085742622/675477245178937385/c3ca4165c30b3b342e57b903a3ded367-3.png + + float v = vecWishDir[0]; + float w = vecWishDir[1]; + float a = vecForward[0]; + float c = vecRight[0]; + float e = vecForward[1]; + float f = vecRight[1]; + + float flDivide = (c * e - a * f); + if(flDivide == 0.0) + { + flForwardMove = g_fMaxMove; + flSideMove = 0.0; + } + else + { + flForwardMove = (c * w - f * v) / flDivide; + flSideMove = (e * v - a * w) / flDivide; + } +} + +float GetThetaAngleInAir(float flVelocity[2], float flAirAccelerate, float flMaxSpeed, float flSurfaceFriction, float flFrametime) +{ + // In order to solve this, we must check that accelspeed < 30 + // so it applies the correct strafing method. + // So there is basically two cases: + // if 30 - accelspeed <= 0 -> We use the perpendicular of velocity. + // but if 30 - accelspeed > 0 the dot product must be equal to = 30 - accelspeed + // in order to get the best gain. + // First case is theta == 90 + // How to solve the second case? + // here we go + // d = velocity2DLength * cos(theta) + // cos(theta) = d / velocity2D + // theta = arcos(d / velocity2D) + + float flAccelSpeed = flAirAccelerate * flMaxSpeed * flSurfaceFriction * flFrametime; + + float flWantedDotProduct = g_flAirSpeedCap - flAccelSpeed; + + if (flWantedDotProduct > 0.0) + { + float flVelLength2D = SquareRoot(flVelocity[0] * flVelocity[0] + flVelocity[1] * flVelocity[1]); + if(flVelLength2D == 0.0) + { + return 90.0; + } + float flCosTheta = flWantedDotProduct / flVelLength2D; + + if (flCosTheta > 1.0) + { + flCosTheta = 1.0; + } + else if(flCosTheta < -1.0) + { + flCosTheta = -1.0; + } + + + float flTheta = ArcCosine(flCosTheta) * (180.0 / FLOAT_PI); + + return flTheta; + } + else + { + return 90.0; + } +} + + +// Same as above, but this time we calculate max delta angle +// so we can change between normal strafer and autostrafer depending on the player's viewangles difference. +/*float GetMaxDeltaInAir(float flVelocity[2], float flAirAccelerate, float flMaxSpeed, float flSurfaceFriction, float flFrametime) +{ + float flAccelSpeed = flAirAccelerate * flMaxSpeed * flSurfaceFriction * flFrametime; + + if (flAccelSpeed >= g_flAirSpeedCap) + { + flAccelSpeed = g_flAirSpeedCap; + } + + float flVelLength2D = SquareRoot(flVelocity[0] * flVelocity[0] + flVelocity[1] * flVelocity[1]); + + float flMaxDelta = ArcTangent2(flAccelSpeed, flVelLength2D) * (180 / FLOAT_PI); + + return flMaxDelta; +}*/ + +float SimulateAirAccelerate(float flVelocity[2], float flWishDir[2], float flAirAccelerate, float flMaxSpeed, float flSurfaceFriction, float flFrametime, float flVelocityOutput[2]) +{ + float flWishSpeedCapped = flMaxSpeed; + + // Cap speed + if( flWishSpeedCapped > g_flAirSpeedCap ) + flWishSpeedCapped = g_flAirSpeedCap; + + // Determine veer amount + float flCurrentSpeed = flVelocity[0] * flWishDir[0] + flVelocity[1] * flWishDir[1]; + + // See how much to add + float flAddSpeed = flWishSpeedCapped - flCurrentSpeed; + + // If not adding any, done. + if( flAddSpeed <= 0.0 ) + { + return; + } + + // Determine acceleration speed after acceleration + float flAccelSpeed = flAirAccelerate * flMaxSpeed * flFrametime * flSurfaceFriction; + + // Cap it + if( flAccelSpeed > flAddSpeed ) + { + flAccelSpeed = flAddSpeed; + } + + flVelocityOutput[0] = flVelocity[0] + flAccelSpeed * flWishDir[0]; + flVelocityOutput[1] = flVelocity[1] + flAccelSpeed * flWishDir[1]; +} + +// The idea is to get the maximum angle +float GetMaxDeltaInAir(float flVelocity[2], float flMaxSpeed, float flSurfaceFriction, bool bLeft) +{ + float flFrametime = GetTickInterval(); + float flAirAccelerate = g_ConVar_sv_airaccelerate.FloatValue; + + float flTheta = GetThetaAngleInAir(flVelocity, flAirAccelerate, flMaxSpeed, flSurfaceFriction, flFrametime); + + // Convert velocity 2D to angle. + float flYawVelocity = Vec2DToYaw(flVelocity); + + // Get the best yaw direction on the right. + float flBestYawRight = AngleNormalize(flYawVelocity + flTheta); + + // Get the best yaw direction on the left. + float flBestYawLeft = AngleNormalize(flYawVelocity - flTheta); + + float flTemp[3], vecBestLeft3D[3], vecBestRight3D[3]; + + flTemp[0] = 0.0; + flTemp[1] = flBestYawLeft; + flTemp[2] = 0.0; + + GetAngleVectors(flTemp, vecBestLeft3D, NULL_VECTOR, NULL_VECTOR); + + flTemp[0] = 0.0; + flTemp[1] = flBestYawRight; + flTemp[2] = 0.0; + + GetAngleVectors(flTemp, vecBestRight3D, NULL_VECTOR, NULL_VECTOR); + + float vecBestRight[2], vecBestLeft[2]; + + vecBestRight[0] = vecBestRight3D[0]; + vecBestRight[1] = vecBestRight3D[1]; + + vecBestLeft[0] = vecBestLeft3D[0]; + vecBestLeft[1] = vecBestLeft3D[1]; + + float flCalcVelocityLeft[2], flCalcVelocityRight[2]; + + // Simulate air accelerate function in order to get the new max gain possible on both side. + SimulateAirAccelerate(flVelocity, vecBestLeft, flAirAccelerate, flMaxSpeed, flFrametime, flSurfaceFriction, flCalcVelocityLeft); + SimulateAirAccelerate(flVelocity, vecBestRight, flAirAccelerate, flMaxSpeed, flFrametime, flSurfaceFriction, flCalcVelocityRight); + + float flNewBestYawLeft = Vec2DToYaw(flCalcVelocityLeft); + float flNewBestYawRight = Vec2DToYaw(flCalcVelocityRight); + + // Then get the difference in order to find the maximum angle. + if (bLeft) + { + return FloatAbs(AngleNormalize(flYawVelocity - flNewBestYawLeft)); + } + else + { + return FloatAbs(AngleNormalize(flYawVelocity - flNewBestYawRight)); + } + + // Do an estimate otherwhise. + // return FloatAbs(AngleNormalize(flNewBestYawLeft - flNewBestYawRight) / 2.0); +} + +void GetIdealMovementsInAir(float flYawWantedDir, float flVelocity[2], float flMaxSpeed, float flSurfaceFriction, float &flForwardMove, float &flSideMove, bool bPreferRight = true) +{ + float flAirAccelerate = g_ConVar_sv_airaccelerate.FloatValue; + float flFrametime = GetTickInterval(); + float flYawVelocity = Vec2DToYaw(flVelocity); + + // Get theta angle + float flTheta = GetThetaAngleInAir(flVelocity, flAirAccelerate, flMaxSpeed, flSurfaceFriction, flFrametime); + + // Get the best yaw direction on the right. + float flBestYawRight = AngleNormalize(flYawVelocity + flTheta); + + // Get the best yaw direction on the left. + float flBestYawLeft = AngleNormalize(flYawVelocity - flTheta); + + float vecBestDirLeft[3], vecBestDirRight[3]; + float tempAngle[3]; + + tempAngle[0] = 0.0; + tempAngle[1] = flBestYawRight; + tempAngle[2] = 0.0; + + GetAngleVectors(tempAngle, vecBestDirRight, NULL_VECTOR, NULL_VECTOR); + + tempAngle[0] = 0.0; + tempAngle[1] = flBestYawLeft; + tempAngle[2] = 0.0; + + GetAngleVectors(tempAngle, vecBestDirLeft, NULL_VECTOR, NULL_VECTOR); + + // Our wanted direction. + float vecBestDir[2]; + + // Let's follow the most the wanted direction now with max possible gain. + float flDiffYaw = AngleNormalize(flYawWantedDir - flYawVelocity); + + if (flDiffYaw > 0.0) + { + vecBestDir[0] = vecBestDirRight[0]; + vecBestDir[1] = vecBestDirRight[1]; + } + else if(flDiffYaw < 0.0) + { + vecBestDir[0] = vecBestDirLeft[0]; + vecBestDir[1] = vecBestDirLeft[1]; + } + else + { + // Going straight. + if (bPreferRight) + { + vecBestDir[0] = vecBestDirRight[0]; + vecBestDir[1] = vecBestDirRight[1]; + } + else + { + vecBestDir[0] = vecBestDirLeft[0]; + vecBestDir[1] = vecBestDirLeft[1]; + } + } + + float vecForwardWantedDir3D[3], vecRightWantedDir3D[3]; + float vecForwardWantedDir[2], vecRightWantedDir[2]; + + tempAngle[0] = 0.0; + tempAngle[1] = flYawWantedDir; + tempAngle[2] = 0.0; + + // Convert our yaw wanted direction to vectors. + GetAngleVectors(tempAngle, vecForwardWantedDir3D, vecRightWantedDir3D, NULL_VECTOR); + + vecForwardWantedDir[0] = vecForwardWantedDir3D[0]; + vecForwardWantedDir[1] = vecForwardWantedDir3D[1]; + + vecRightWantedDir[0] = vecRightWantedDir3D[0]; + vecRightWantedDir[1] = vecRightWantedDir3D[1]; + + // Solve the movement variables from our wanted direction and the best gain direction. + Solve2DMovementsVars(vecBestDir, vecForwardWantedDir, vecRightWantedDir, flForwardMove, flSideMove); + + float flLengthMovements = SquareRoot(flForwardMove * flForwardMove + flSideMove * flSideMove); + + if(flLengthMovements != 0.0) + { + flForwardMove /= flLengthMovements; + flSideMove /= flLengthMovements; + } +} + +public Action XutaxOnPlayerRunCmd(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]) +{ + float flFowardMove, flSideMove; + float flMaxSpeed = GetEntPropFloat(client, Prop_Data, "m_flMaxspeed"); + float flSurfaceFriction = 1.0; + if (g_iSurfaceFrictionOffset > 0) + { + flSurfaceFriction = GetEntDataFloat(client, g_iSurfaceFrictionOffset); + if (g_ConVar_AutoFind_Offset.BoolValue && s_iOnGroundCount[client] == 0 && !(flSurfaceFriction == 0.25 || flSurfaceFriction == 1.0)) + { + FindNewFrictionOffset(client); + } + } + + + float flVelocity[3], flVelocity2D[2]; + + GetEntPropVector(client, Prop_Data, "m_vecVelocity", flVelocity); + + flVelocity2D[0] = flVelocity[0]; + flVelocity2D[1] = flVelocity[1]; + + // PrintToChat(client, "%f", SquareRoot(flVelocity2D[0] * flVelocity2D[0] + flVelocity2D[1] * flVelocity2D[1])); + + GetIdealMovementsInAir(angles[1], flVelocity2D, flMaxSpeed, flSurfaceFriction, flFowardMove, flSideMove); + + float flAngleDifference = AngleNormalize(angles[1] - g_flOldYawAngle[client]); + float flCurrentAngles = FloatAbs(flAngleDifference); + + + // Right + if (flAngleDifference < 0.0) + { + float flMaxDelta = GetMaxDeltaInAir(flVelocity2D, flMaxSpeed, flSurfaceFriction, true); + vel[1] = g_fMaxMove; + + if (flCurrentAngles <= flMaxDelta * g_fPower[client]) + { + vel[0] = flFowardMove * g_fMaxMove; + vel[1] = flSideMove * g_fMaxMove; + } + } + else if (flAngleDifference > 0.0) + { + float flMaxDelta = GetMaxDeltaInAir(flVelocity2D, flMaxSpeed, flSurfaceFriction, false); + vel[1] = -g_fMaxMove; + + if (flCurrentAngles <= flMaxDelta * g_fPower[client]) + { + vel[0] = flFowardMove * g_fMaxMove; + vel[1] = flSideMove * g_fMaxMove; + } + } + else + { + vel[0] = flFowardMove * g_fMaxMove; + vel[1] = flSideMove * g_fMaxMove; + } + + return Plugin_Continue; +} diff --git a/addons/sourcemod/scripting/shavit-tas.sp b/addons/sourcemod/scripting/shavit-tas.sp new file mode 100644 index 00000000..a9886111 --- /dev/null +++ b/addons/sourcemod/scripting/shavit-tas.sp @@ -0,0 +1,249 @@ + + +#include +#include +#include +#include + +#pragma newdecls required +#pragma semicolon 1 + +float g_flAirSpeedCap = 30.0; +float g_flOldYawAngle[MAXPLAYERS + 1]; +ConVar g_ConVar_sv_airaccelerate; +int g_iSurfaceFrictionOffset; +float g_fMaxMove = 400.0; +EngineVersion g_Game; +bool g_bEnabled[MAXPLAYERS + 1]; +int g_iType[MAXPLAYERS + 1]; +float g_fPower[MAXPLAYERS + 1] = {1.0, ...}; +bool g_bTASEnabled; + +Convar g_ConVar_AutoFind_Offset; + + +public Plugin myinfo = +{ + name = "Perfect autostrafe", + author = "xutaxkamay", + description = "", + version = "1.2", + url = "https://steamcommunity.com/id/xutaxkamay/" +}; + +public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max) +{ + CreateNative("SetXutaxStrafe", Native_SetAutostrafe); + CreateNative("GetXutaxStrafe", Native_GetAutostrafe); + CreateNative("SetXutaxType", Native_SetType); + CreateNative("GetXutaxType", Native_GetType); + CreateNative("SetXutaxPower", Native_SetPower); + CreateNative("GetXutaxPower", Native_GetPower); + + RegPluginLibrary("xutax-strafe"); + return APLRes_Success; +} + +public void OnPluginStart() +{ + g_Game = GetEngineVersion(); + g_ConVar_sv_airaccelerate = FindConVar("sv_airaccelerate"); + + GameData gamedata = new GameData("KiD-TAS.games"); + + g_iSurfaceFrictionOffset = gamedata.GetOffset("m_surfaceFriction"); + delete gamedata; + + if(g_iSurfaceFrictionOffset == -1) + { + LogError("[XUTAX] Invalid offset supplied, defaulting friction values"); + } + + if(g_Game == Engine_CSGO) + { + g_fMaxMove = 450.0; + ConVar sv_air_max_wishspeed = FindConVar("sv_air_max_wishspeed"); + sv_air_max_wishspeed.AddChangeHook(OnWishSpeedChanged); + g_flAirSpeedCap = sv_air_max_wishspeed.FloatValue; + + if (g_iSurfaceFrictionOffset != -1) + { + g_iSurfaceFrictionOffset = FindSendPropInfo("CBasePlayer", "m_ubEFNoInterpParity") - g_iSurfaceFrictionOffset; + } + } + else if(g_Game == Engine_CSS) + { + if (g_iSurfaceFrictionOffset != -1) + { + g_iSurfaceFrictionOffset += FindSendPropInfo("CBasePlayer", "m_szLastPlaceName"); + } + } + else + { + SetFailState("This plugin is for CSGO/CSS only."); + } + + RegAdminCmd("sm_xutax_scan", Command_ScanOffsets, ADMFLAG_CHEATS, "Scan for possible offset locations"); + + g_ConVar_AutoFind_Offset = new Convar("xutax_find_offsets", "1", "Attempt to autofind offsets", _, true, 0.0, true, 1.0); + + Convar.AutoExecConfig(); +} + +// doesn't exist in css so we have to cache the value +public void OnWishSpeedChanged(ConVar convar, const char[] oldValue, const char[] newValue) +{ + g_flAirSpeedCap = StringToFloat(newValue); +} + +public void OnClientConnected(int client) +{ + g_bEnabled[client] = false; + g_iType[client] = Type_SurfOverride; + g_fPower[client] = 1.0; +} + +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 (!g_bEnabled[client]) + { + return Plugin_Continue; + } + + if (!Shavit_ShouldProcessFrame(client)) + { + return Plugin_Continue; + } + + static int s_iOnGroundCount[MAXPLAYERS+1] = {1, ...}; + + if (GetEntPropEnt(client, Prop_Send, "m_hGroundEntity") != -1) + { + s_iOnGroundCount[client]++; + } + else + { + s_iOnGroundCount[client] = 0; + } + + if (IsPlayerAlive(client) + && s_iOnGroundCount[client] <= 1 + && !(GetEntityMoveType(client) & MOVETYPE_LADDER) + && (GetEntProp(client, Prop_Data, "m_nWaterLevel") <= 1)) + { + if (!!(buttons & (IN_FORWARD | IN_BACK))) + { + return Plugin_Continue; + } + + if (!!(buttons & (IN_MOVERIGHT | IN_MOVELEFT))) + { + if (g_iType[client] == Type_Override) + { + return Plugin_Continue; + } + else if (g_iType[client] == Type_SurfOverride && IsSurfing(client)) + { + return Plugin_Continue; + } + } + } + + g_flOldYawAngle[client] = angles[1]; + + return Plugin_Continue; +} + +stock void FindNewFrictionOffset(int client, bool logOnly = false) +{ + if(g_Game == Engine_CSGO) + { + int startingOffset = FindSendPropInfo("CBasePlayer", "m_ubEFNoInterpParity"); + for (int i = 16; i >= -128; --i) + { + float friction = GetEntDataFloat(client, startingOffset + i); + if (friction == 0.25 || friction == 1.0) + { + if (logOnly) + { + PrintToConsole(client, "Found offset canidate: %i", i * -1); + } + else + { + g_iSurfaceFrictionOffset = startingOffset - i; + LogError("[XUTAX] Current offset is out of date. Please update to new offset: %i", i * -1); + } + } + } + } + else + { + int startingOffset = FindSendPropInfo("CBasePlayer", "m_szLastPlaceName"); + for (int i = 1; i <= 128; ++i) + { + float friction = GetEntDataFloat(client, startingOffset + i); + if (friction == 0.25 || friction == 1.0) + { + if(logOnly) + { + PrintToConsole(client, "Found offset canidate: %i", i); + } + else + { + g_iSurfaceFrictionOffset = startingOffset + i; + LogError("[XUTAX] Current offset is out of date. Please update to new offset: %i", i); + } + } + } + } +} + +public Action Command_ScanOffsets(int client, int args) +{ + FindNewFrictionOffset(client, .logOnly = true); + + return Plugin_Handled; +} + +// natives +public any Native_SetAutostrafe(Handle plugin, int numParams) +{ + int client = GetNativeCell(1); + bool value = GetNativeCell(2); + g_bEnabled[client] = value; + return 0; +} + +public any Native_GetAutostrafe(Handle plugin, int numParams) +{ + int client = GetNativeCell(1); + return g_bEnabled[client]; +} + +public any Native_SetType(Handle plugin, int numParams) +{ + int client = GetNativeCell(1); + int value = GetNativeCell(2); + g_iType[client] = value; + return 0; +} + +public any Native_GetType(Handle plugin, int numParams) +{ + int client = GetNativeCell(1); + return g_iType[client]; +} + +public any Native_SetPower(Handle plugin, int numParams) +{ + int client = GetNativeCell(1); + float value = GetNativeCell(2); + g_fPower[client] = value; + return 0; +} + +public any Native_GetPower(Handle plugin, int numParams) +{ + int client = GetNativeCell(1); + return g_fPower[client]; +}