diff options
Diffstat (limited to 'sourcemod/scripting/gokz-jumpstats')
| -rw-r--r-- | sourcemod/scripting/gokz-jumpstats/api.sp | 78 | ||||
| -rw-r--r-- | sourcemod/scripting/gokz-jumpstats/commands.sp | 28 | ||||
| -rw-r--r-- | sourcemod/scripting/gokz-jumpstats/distance_tiers.sp | 118 | ||||
| -rw-r--r-- | sourcemod/scripting/gokz-jumpstats/jump_reporting.sp | 508 | ||||
| -rw-r--r-- | sourcemod/scripting/gokz-jumpstats/jump_tracking.sp | 1624 | ||||
| -rw-r--r-- | sourcemod/scripting/gokz-jumpstats/jump_validating.sp | 82 | ||||
| -rw-r--r-- | sourcemod/scripting/gokz-jumpstats/options.sp | 86 | ||||
| -rw-r--r-- | sourcemod/scripting/gokz-jumpstats/options_menu.sp | 145 |
8 files changed, 0 insertions, 2669 deletions
diff --git a/sourcemod/scripting/gokz-jumpstats/api.sp b/sourcemod/scripting/gokz-jumpstats/api.sp deleted file mode 100644 index 2625fda..0000000 --- a/sourcemod/scripting/gokz-jumpstats/api.sp +++ /dev/null @@ -1,78 +0,0 @@ -static GlobalForward H_OnTakeoff; -static GlobalForward H_OnLanding; -static GlobalForward H_OnFailstat; -static GlobalForward H_OnJumpstatAlways; -static GlobalForward H_OnFailstatAlways; -static GlobalForward H_OnJumpInvalidated; - - - -// =====[ FORWARDS ]===== - -void CreateGlobalForwards() -{ - H_OnTakeoff = new GlobalForward("GOKZ_JS_OnTakeoff", ET_Ignore, Param_Cell, Param_Cell); - H_OnLanding = new GlobalForward("GOKZ_JS_OnLanding", ET_Ignore, Param_Array); - H_OnFailstat = new GlobalForward("GOKZ_JS_OnFailstat", ET_Ignore, Param_Array); - H_OnJumpstatAlways = new GlobalForward("GOKZ_JS_OnJumpstatAlways", ET_Ignore, Param_Array); - H_OnFailstatAlways = new GlobalForward("GOKZ_JS_OnFailstatAlways", ET_Ignore, Param_Array); - H_OnJumpInvalidated = new GlobalForward("GOKZ_JS_OnJumpInvalidated", ET_Ignore, Param_Cell); -} - -void Call_OnTakeoff(int client, int jumpType) -{ - Call_StartForward(H_OnTakeoff); - Call_PushCell(client); - Call_PushCell(jumpType); - Call_Finish(); -} - -void Call_OnLanding(Jump jump) -{ - Call_StartForward(H_OnLanding); - Call_PushArray(jump, sizeof(jump)); - Call_Finish(); -} - -void Call_OnJumpInvalidated(int client) -{ - Call_StartForward(H_OnJumpInvalidated); - Call_PushCell(client); - Call_Finish(); -} - -void Call_OnFailstat(Jump jump) -{ - Call_StartForward(H_OnFailstat); - Call_PushArray(jump, sizeof(jump)); - Call_Finish(); -} - -void Call_OnJumpstatAlways(Jump jump) -{ - Call_StartForward(H_OnJumpstatAlways); - Call_PushArray(jump, sizeof(jump)); - Call_Finish(); -} - -void Call_OnFailstatAlways(Jump jump) -{ - Call_StartForward(H_OnFailstatAlways); - Call_PushArray(jump, sizeof(jump)); - Call_Finish(); -} - - - -// =====[ NATIVES ]===== - -void CreateNatives() -{ - CreateNative("GOKZ_JS_InvalidateJump", Native_InvalidateJump); -} - -public int Native_InvalidateJump(Handle plugin, int numParams) -{ - InvalidateJumpstat(GetNativeCell(1)); - return 0; -} diff --git a/sourcemod/scripting/gokz-jumpstats/commands.sp b/sourcemod/scripting/gokz-jumpstats/commands.sp deleted file mode 100644 index 991f9e6..0000000 --- a/sourcemod/scripting/gokz-jumpstats/commands.sp +++ /dev/null @@ -1,28 +0,0 @@ - -void RegisterCommands() -{ - RegConsoleCmd("sm_jso", CommandJumpstatsOptions, "[KZ] Open the jumpstats options menu."); - RegConsoleCmd("sm_jsalways", CommandAlwaysJumpstats, "[KZ] Toggle the always-on jumpstats."); -} - -public Action CommandJumpstatsOptions(int client, int args) -{ - DisplayJumpstatsOptionsMenu(client); - return Plugin_Handled; -} - -public Action CommandAlwaysJumpstats(int client, int args) -{ - if (GOKZ_JS_GetOption(client, JSOption_JumpstatsAlways) == JSToggleOption_Enabled) - { - GOKZ_JS_SetOption(client, JSOption_JumpstatsAlways, JSToggleOption_Disabled); - GOKZ_PrintToChat(client, true, "%t", "Jumpstats Option - Jumpstats Always - Disable"); - } - else - { - GOKZ_JS_SetOption(client, JSOption_JumpstatsAlways, JSToggleOption_Enabled); - GOKZ_PrintToChat(client, true, "%t", "Jumpstats Option - Jumpstats Always - Enable"); - } - - return Plugin_Handled; -} diff --git a/sourcemod/scripting/gokz-jumpstats/distance_tiers.sp b/sourcemod/scripting/gokz-jumpstats/distance_tiers.sp deleted file mode 100644 index 3abe8e9..0000000 --- a/sourcemod/scripting/gokz-jumpstats/distance_tiers.sp +++ /dev/null @@ -1,118 +0,0 @@ -/* - Categorises jumps into tiers based on their distance. - Tier thresholds are loaded from a config. -*/ - - - -static float distanceTiers[JUMPTYPE_COUNT - 3][MODE_COUNT][DISTANCETIER_COUNT]; - - - -// =====[ PUBLIC ]===== - -int GetDistanceTier(int jumpType, int mode, float distance, float offset = 0.0) -{ - // No tiers given for 'Invalid' jumps. - if (jumpType == JumpType_Invalid || jumpType == JumpType_FullInvalid - || jumpType == JumpType_Fall || jumpType == JumpType_Other - || jumpType != JumpType_LadderJump && offset < -JS_OFFSET_EPSILON - || distance > JS_MAX_JUMP_DISTANCE) - { - // TODO Give a tier to "Other" jumps - // TODO Give a tier to offset jumps - return DistanceTier_None; - } - - // Get highest tier distance that the jump beats - int tier = DistanceTier_None; - while (tier + 1 < DISTANCETIER_COUNT && distance >= GetDistanceTierDistance(jumpType, mode, tier + 1)) - { - tier++; - } - - return tier; -} - -float GetDistanceTierDistance(int jumpType, int mode, int tier) -{ - return distanceTiers[jumpType][mode][tier]; -} - -bool LoadBroadcastTiers() -{ - char chatTier[16], soundTier[16]; - - KeyValues kv = new KeyValues("broadcast"); - if (!kv.ImportFromFile(JS_CFG_BROADCAST)) - { - return false; - } - - kv.GetString("chat", chatTier, sizeof(chatTier), "ownage"); - kv.GetString("sound", soundTier, sizeof(chatTier), ""); - - for (int tier = 0; tier < sizeof(gC_DistanceTierKeys); tier++) - { - if (StrEqual(chatTier, gC_DistanceTierKeys[tier])) - { - gI_JSOptionDefaults[JSOption_MinChatBroadcastTier] = tier; - } - if (StrEqual(soundTier, gC_DistanceTierKeys[tier])) - { - gI_JSOptionDefaults[JSOption_MinSoundBroadcastTier] = tier; - } - } - - delete kv; - return true; -} - - - -// =====[ EVENTS ]===== - -void OnMapStart_DistanceTiers() -{ - if (!LoadDistanceTiers()) - { - SetFailState("Failed to load file: \"%s\".", JS_CFG_TIERS); - } -} - - - -// =====[ PRIVATE ]===== - -static bool LoadDistanceTiers() -{ - KeyValues kv = new KeyValues("tiers"); - if (!kv.ImportFromFile(JS_CFG_TIERS)) - { - return false; - } - - // It's a bit of a hack to exclude non-tiered jumptypes - for (int jumpType = 0; jumpType < sizeof(gC_JumpTypeKeys) - 3; jumpType++) - { - if (!kv.JumpToKey(gC_JumpTypeKeys[jumpType])) - { - return false; - } - for (int mode = 0; mode < MODE_COUNT; mode++) - { - if (!kv.JumpToKey(gC_ModeKeys[mode])) - { - return false; - } - for (int tier = DistanceTier_Meh; tier < DISTANCETIER_COUNT; tier++) - { - distanceTiers[jumpType][mode][tier] = kv.GetFloat(gC_DistanceTierKeys[tier]); - } - kv.GoBack(); - } - kv.GoBack(); - } - delete kv; - return true; -} diff --git a/sourcemod/scripting/gokz-jumpstats/jump_reporting.sp b/sourcemod/scripting/gokz-jumpstats/jump_reporting.sp deleted file mode 100644 index 31a1bb2..0000000 --- a/sourcemod/scripting/gokz-jumpstats/jump_reporting.sp +++ /dev/null @@ -1,508 +0,0 @@ -/* - Chat and console reports for jumpstats. -*/ - -static char sounds[DISTANCETIER_COUNT][256]; - - - -// =====[ PUBLIC ]===== - -void PlayJumpstatSound(int client, int tier) -{ - int soundOption = GOKZ_JS_GetOption(client, JSOption_MinSoundTier); - if (tier <= DistanceTier_Meh || soundOption == DistanceTier_None || soundOption > tier) - { - return; - } - - GOKZ_EmitSoundToClient(client, sounds[tier], _, "Jumpstats"); -} - - - -// =====[ EVENTS ]===== - -void OnMapStart_JumpReporting() -{ - if (!LoadSounds()) - { - SetFailState("Failed to load file: \"%s\".", JS_CFG_SOUNDS); - } -} - -void OnLanding_JumpReporting(Jump jump) -{ - int minTier; - int tier = GetDistanceTier(jump.type, GOKZ_GetCoreOption(jump.jumper, Option_Mode), jump.distance, jump.offset); - if (tier == DistanceTier_None) - { - return; - } - - // Report the jumpstat to the client and their spectators - DoJumpstatsReport(jump.jumper, jump, tier); - - for (int client = 1; client <= MaxClients; client++) - { - if (IsValidClient(client) && client != jump.jumper) - { - if (GetObserverTarget(client) == jump.jumper) - { - DoJumpstatsReport(client, jump, tier); - } - else - { - minTier = GOKZ_JS_GetOption(client, JSOption_MinChatBroadcastTier); - if (minTier != 0 && tier >= minTier) - { - GOKZ_PrintToChat(client, true, "%t", "Broadcast Jumpstat Chat Report", - gC_DistanceTierChatColours[tier], - jump.jumper, - jump.distance, - gC_JumpTypes[jump.originalType]); - DoConsoleReport(client, false, jump, tier, "Console Jump Header"); - } - - minTier = GOKZ_JS_GetOption(client, JSOption_MinSoundBroadcastTier); - if (minTier != 0 && tier >= minTier) - { - PlayJumpstatSound(client, tier); - } - } - } - } -} - -void OnFailstat_FailstatReporting(Jump jump) -{ - int tier = GetDistanceTier(jump.type, GOKZ_GetCoreOption(jump.jumper, Option_Mode), jump.distance); - if (tier == DistanceTier_None) - { - return; - } - - // Report the failstat to the client and their spectators - DoFailstatReport(jump.jumper, jump, tier); - - for (int client = 1; client <= MaxClients; client++) - { - if (IsValidClient(client) && GetObserverTarget(client) == jump.jumper) - { - DoFailstatReport(client, jump, tier); - } - } -} - -void OnJumpstatAlways_JumpstatAlwaysReporting(Jump jump) -{ - DoJumpstatAlwaysReport(jump.jumper, jump); - - for (int client = 1; client <= MaxClients; client++) - { - if (IsValidClient(client) && GetObserverTarget(client) == jump.jumper) - { - DoJumpstatAlwaysReport(client, jump); - } - } -} - - -void OnFailstatAlways_FailstatAlwaysReporting(Jump jump) -{ - DoFailstatAlwaysReport(jump.jumper, jump); - - for (int client = 1; client <= MaxClients; client++) - { - if (IsValidClient(client) && GetObserverTarget(client) == jump.jumper) - { - DoFailstatAlwaysReport(client, jump); - } - } -} - - - -// =====[ PRIVATE ]===== - -static void DoJumpstatsReport(int client, Jump jump, int tier) -{ - if (GOKZ_JS_GetOption(client, JSOption_JumpstatsMaster) == JSToggleOption_Disabled) - { - return; - } - - DoChatReport(client, false, jump, tier); - DoConsoleReport(client, false, jump, tier, "Console Jump Header"); - PlayJumpstatSound(client, tier); -} - -static void DoFailstatReport(int client, Jump jump, int tier) -{ - if (GOKZ_JS_GetOption(client, JSOption_JumpstatsMaster) == JSToggleOption_Disabled) - { - return; - } - - DoChatReport(client, true, jump, tier); - DoConsoleReport(client, true, jump, tier, "Console Failstat Header"); -} - -static void DoJumpstatAlwaysReport(int client, Jump jump) -{ - if (GOKZ_JS_GetOption(client, JSOption_JumpstatsMaster) == JSToggleOption_Disabled || - GOKZ_JS_GetOption(client, JSOption_JumpstatsAlways) == JSToggleOption_Disabled) - { - return; - } - - DoChatReport(client, false, jump, 1); - DoConsoleReport(client, false, jump, 1, "Console Jump Header"); -} - -static void DoFailstatAlwaysReport(int client, Jump jump) -{ - if (GOKZ_JS_GetOption(client, JSOption_JumpstatsMaster) == JSToggleOption_Disabled || - GOKZ_JS_GetOption(client, JSOption_JumpstatsAlways) == JSToggleOption_Disabled) - { - return; - } - - DoChatReport(client, true, jump, 1); - DoConsoleReport(client, true, jump, 1, "Console Failstat Header"); -} - - - - -// CONSOLE REPORT - -static void DoConsoleReport(int client, bool isFailstat, Jump jump, int tier, char[] header) -{ - int minConsoleTier = GOKZ_JS_GetOption(client, JSOption_MinConsoleTier); - if ((minConsoleTier == 0 || minConsoleTier > tier) && GOKZ_JS_GetOption(client, JSOption_JumpstatsAlways) == JSToggleOption_Disabled - || isFailstat && GOKZ_JS_GetOption(client, JSOption_FailstatsConsole) == JSToggleOption_Disabled) - { - return; - } - - char releaseWString[32], blockString[32], edgeString[32], deviationString[32], missString[32]; - - if (jump.originalType == JumpType_LongJump || - jump.originalType == JumpType_LadderJump || - jump.originalType == JumpType_WeirdJump || - jump.originalType == JumpType_LowpreWeirdJump) - { - FormatEx(releaseWString, sizeof(releaseWString), " %s", GetIntConsoleString(client, "W Release", jump.releaseW)); - } - else if (jump.crouchRelease < 20 && jump.crouchRelease > -20) - { - FormatEx(releaseWString, sizeof(releaseWString), " %s", GetIntConsoleString(client, "Crouch Release", jump.crouchRelease)); - } - - if (jump.miss > 0.0) - { - FormatEx(missString, sizeof(missString), " %s", GetFloatConsoleString2(client, "Miss", jump.miss)); - } - - if (jump.block > 0) - { - FormatEx(blockString, sizeof(blockString), " %s", GetIntConsoleString(client, "Block", jump.block)); - FormatEx(deviationString, sizeof(deviationString), " %s", GetFloatConsoleString1(client, "Deviation", jump.deviation)); - } - - if (jump.edge > 0.0 || (jump.block > 0 && jump.edge == 0.0)) - { - FormatEx(edgeString, sizeof(edgeString), " %s", GetFloatConsoleString2(client, "Edge", jump.edge)); - } - - PrintToConsole(client, "%t", header, jump.jumper, jump.distance, gC_JumpTypes[jump.originalType]); - - PrintToConsole(client, "%s%s%s%s %s %s %s %s%s %s %s%s %s %s %s %s %s", - gC_ModeNamesShort[GOKZ_GetCoreOption(jump.jumper, Option_Mode)], - blockString, - edgeString, - missString, - GetIntConsoleString(client, jump.strafes == 1 ? "Strafe" : "Strafes", jump.strafes), - GetSyncConsoleString(client, jump.sync), - GetFloatConsoleString2(client, "Pre", jump.preSpeed), - GetFloatConsoleString2(client, "Max", jump.maxSpeed), - releaseWString, - GetIntConsoleString(client, "Overlap", jump.overlap), - GetIntConsoleString(client, "Dead Air", jump.deadair), - deviationString, - GetWidthConsoleString(client, jump.width, jump.strafes), - GetFloatConsoleString1(client, "Height", jump.height), - GetIntConsoleString(client, "Airtime", jump.duration), - GetFloatConsoleString1(client, "Offset", jump.offset), - GetIntConsoleString(client, "Crouch Ticks", jump.crouchTicks)); - - PrintToConsole(client, " #. %12t%12t%12t%12t%12t%9t%t", "Sync (Table)", "Gain (Table)", "Loss (Table)", "Airtime (Table)", "Width (Table)", "Overlap (Table)", "Dead Air (Table)"); - if (jump.strafes_ticks[0] > 0) - { - PrintToConsole(client, " 0. ---- ----- ----- %3.0f%% ----- -- --", GetStrafeAirtime(jump, 0)); - } - for (int strafe = 1; strafe <= jump.strafes && strafe < JS_MAX_TRACKED_STRAFES; strafe++) - { - PrintToConsole(client, - " %2d. %3.0f%% %5.2f %5.2f %3.0f%% %5.1f° %2d %2d", - strafe, - GetStrafeSync(jump, strafe), - jump.strafes_gain[strafe], - jump.strafes_loss[strafe], - GetStrafeAirtime(jump, strafe), - FloatAbs(jump.strafes_width[strafe]), - jump.strafes_overlap[strafe], - jump.strafes_deadair[strafe]); - } - PrintToConsole(client, ""); // New line -} - -static char[] GetSyncConsoleString(int client, float sync) -{ - char resultString[32]; - FormatEx(resultString, sizeof(resultString), "| %.0f%% %T", sync, "Sync", client); - return resultString; -} - -static char[] GetWidthConsoleString(int client, float width, int strafes) -{ - char resultString[32]; - FormatEx(resultString, sizeof(resultString), "| %.1f° %T", GetAverageStrafeWidth(strafes, width), "Width", client); - return resultString; -} - -// I couldn't really merge those together -static char[] GetFloatConsoleString1(int client, const char[] stat, float value) -{ - char resultString[32]; - FormatEx(resultString, sizeof(resultString), "| %.1f %T", value, stat, client); - return resultString; -} - -static char[] GetFloatConsoleString2(int client, const char[] stat, float value) -{ - char resultString[32]; - FormatEx(resultString, sizeof(resultString), "| %.2f %T", value, stat, client); - return resultString; -} - -static char[] GetIntConsoleString(int client, const char[] stat, int value) -{ - char resultString[32]; - FormatEx(resultString, sizeof(resultString), "| %d %T", value, stat, client); - return resultString; -} - - - -// CHAT REPORT - -static void DoChatReport(int client, bool isFailstat, Jump jump, int tier) -{ - int minChatTier = GOKZ_JS_GetOption(client, JSOption_MinChatTier); - if ((minChatTier == 0 || minChatTier > tier) // 0 means disabled - && GOKZ_JS_GetOption(client, JSOption_JumpstatsAlways) == JSToggleOption_Disabled) - { - return; - } - - char typePostfix[3], color[16], blockStats[32], extBlockStats[32]; - char releaseStats[32], edgeOffset[64], offsetEdge[32], missString[32]; - - if (isFailstat) - { - if (GOKZ_JS_GetOption(client, JSOption_FailstatsChat) == JSToggleOption_Disabled - && GOKZ_JS_GetOption(client, JSOption_JumpstatsAlways) == JSToggleOption_Disabled) - { - return; - } - strcopy(typePostfix, sizeof(typePostfix), "-F"); - strcopy(color, sizeof(color), "{grey}"); - } - else - { - strcopy(color, sizeof(color), gC_DistanceTierChatColours[tier]); - } - - if (jump.block > 0) - { - FormatEx(blockStats, sizeof(blockStats), " | %s", GetFloatChatString(client, "Edge", jump.edge)); - FormatEx(extBlockStats, sizeof(extBlockStats), " | %s", GetFloatChatString(client, "Deviation", jump.deviation)); - } - - if (jump.miss > 0.0) - { - FormatEx(missString, sizeof(missString), " | %s", GetFloatChatString(client, "Miss", jump.miss)); - } - - if (jump.edge > 0.0 || (jump.block > 0 && jump.edge == 0.0)) - { - if (jump.originalType == JumpType_LadderJump) - { - FormatEx(offsetEdge, sizeof(offsetEdge), " | %s", GetFloatChatString(client, "Edge", jump.edge)); - } - else - { - FormatEx(edgeOffset, sizeof(edgeOffset), " | %s", GetFloatChatString(client, "Edge", jump.edge)); - } - } - - if (jump.originalType == JumpType_LongJump || - jump.originalType == JumpType_LadderJump || - jump.originalType == JumpType_WeirdJump) - { - if (jump.releaseW >= 20 || jump.releaseW <= -20) - { - FormatEx(releaseStats, sizeof(releaseStats), " | {red}✗ {grey}W", GetReleaseChatString(client, "W Release", jump.releaseW)); - } - else - { - FormatEx(releaseStats, sizeof(releaseStats), " | %s", GetReleaseChatString(client, "W Release", jump.releaseW)); - } - } - else if (jump.crouchRelease < 20 && jump.crouchRelease > -20) - { - FormatEx(releaseStats, sizeof(releaseStats), " | %s", GetReleaseChatString(client, "Crouch Release", jump.crouchRelease)); - } - - if (jump.originalType == JumpType_LadderJump) - { - FormatEx(edgeOffset, sizeof(edgeOffset), " | %s", GetFloatChatString(client, "Offset Short", jump.offset)); - } - else - { - FormatEx(offsetEdge, sizeof(offsetEdge), " | %s", GetFloatChatString(client, "Offset", jump.offset)); - } - - GOKZ_PrintToChat(client, true, - "%s%s%s{grey}: %s%.1f{grey} | %s | %s%s%s", - color, - gC_JumpTypesShort[jump.originalType], - typePostfix, - color, - jump.distance, - GetStrafesSyncChatString(client, jump.strafes, jump.sync), - GetSpeedChatString(client, jump.preSpeed, jump.maxSpeed), - edgeOffset, - releaseStats); - - if (GOKZ_JS_GetOption(client, JSOption_ExtendedChatReport) == JSToggleOption_Enabled) - { - GOKZ_PrintToChat(client, false, - "%s | %s%s%s | %s | %s%s", - GetIntChatString(client, "Overlap", jump.overlap), - GetIntChatString(client, "Dead Air", jump.deadair), - offsetEdge, - extBlockStats, - GetWidthChatString(client, jump.width, jump.strafes), - GetFloatChatString(client, "Height", jump.height), - missString); - } -} - -static char[] GetStrafesSyncChatString(int client, int strafes, float sync) -{ - char resultString[64]; - FormatEx(resultString, sizeof(resultString), - "{lime}%d{grey} %T ({lime}%.0f%%%%{grey})", - strafes, "Strafes", client, sync); - return resultString; -} - -static char[] GetSpeedChatString(int client, float preSpeed, float maxSpeed) -{ - char resultString[64]; - FormatEx(resultString, sizeof(resultString), - "{lime}%.0f{grey} / {lime}%.0f{grey} %T", - preSpeed, maxSpeed, "Speed", client); - return resultString; -} - -static char[] GetReleaseChatString(int client, char[] releaseType, int release) -{ - char resultString[32]; - if (release == 0) - { - FormatEx(resultString, sizeof(resultString), - "{green}✓{grey} %T", - releaseType, client); - } - else if (release > 0) - { - FormatEx(resultString, sizeof(resultString), - "{red}+%d{grey} %T", - release, - releaseType, client); - } - else - { - FormatEx(resultString, sizeof(resultString), - "{blue}%d{grey} %T", - release, - releaseType, client); - } - return resultString; -} - -static char[] GetWidthChatString(int client, float width, int strafes) -{ - char resultString[32]; - FormatEx(resultString, sizeof(resultString), - "{lime}%.1f°{grey} %T", - GetAverageStrafeWidth(strafes, width), "Width", client); - return resultString; -} - -static float GetAverageStrafeWidth(int strafes, float totalWidth) -{ - if (strafes == 0) - { - return 0.0; - } - - return totalWidth / strafes; -} - -static char[] GetFloatChatString(int client, const char[] stat, float value) -{ - char resultString[32]; - FormatEx(resultString, sizeof(resultString), - "{lime}%.1f{grey} %T", - value, stat, client); - return resultString; -} - -static char[] GetIntChatString(int client, const char[] stat, int value) -{ - char resultString[32]; - FormatEx(resultString, sizeof(resultString), - "{lime}%d{grey} %T", - value, stat, client); - return resultString; -} - - - -// SOUNDS - -static bool LoadSounds() -{ - KeyValues kv = new KeyValues("sounds"); - if (!kv.ImportFromFile(JS_CFG_SOUNDS)) - { - return false; - } - - char downloadPath[256]; - for (int tier = DistanceTier_Impressive; tier < DISTANCETIER_COUNT; tier++) - { - kv.GetString(gC_DistanceTierKeys[tier], sounds[tier], sizeof(sounds[])); - FormatEx(downloadPath, sizeof(downloadPath), "sound/%s", sounds[tier]); - AddFileToDownloadsTable(downloadPath); - PrecacheSound(sounds[tier], true); - } - - delete kv; - return true; -} diff --git a/sourcemod/scripting/gokz-jumpstats/jump_tracking.sp b/sourcemod/scripting/gokz-jumpstats/jump_tracking.sp deleted file mode 100644 index acd9442..0000000 --- a/sourcemod/scripting/gokz-jumpstats/jump_tracking.sp +++ /dev/null @@ -1,1624 +0,0 @@ -/* - Tracking of jump type, speed, strafes and more. -*/ - - - -// =====[ STRUCTS ]============================================================ - -enum struct Pose -{ - float position[3]; - float orientation[3]; - float velocity[3]; - float speed; - int duration; - int overlap; - int deadair; - int syncTicks; -} - - - -// =====[ GLOBAL VARIABLES ]=================================================== - -static ArrayList entityTouchList[MAXPLAYERS + 1]; -static int entityTouchDuration[MAXPLAYERS + 1]; -static int lastNoclipTime[MAXPLAYERS + 1]; -static int lastDuckbugTime[MAXPLAYERS + 1]; -static int lastGroundSpeedCappedTime[MAXPLAYERS + 1]; -static int lastMovementProcessedTime[MAXPLAYERS + 1]; -static float lastJumpButtonTime[MAXPLAYERS + 1]; -static bool validCmd[MAXPLAYERS + 1]; // Whether no illegal action is detected -static const float playerMins[3] = { -16.0, -16.0, 0.0 }; -static const float playerMaxs[3] = { 16.0, 16.0, 0.0 }; -static const float playerMinsEx[3] = { -20.0, -20.0, 0.0 }; -static const float playerMaxsEx[3] = { 20.0, 20.0, 0.0 }; -static bool doFailstatAlways[MAXPLAYERS + 1]; -static bool isInAir[MAXPLAYERS + 1]; -static const Jump emptyJump; -static Handle acceptInputHook; - - -// =====[ DEFINITIONS ]======================================================== - -// We cannot return enum structs and it's annoying -// The modulo operator is broken, so we can't access this using negative numbers -// (https://github.com/alliedmodders/sourcepawn/issues/456). We use the method -// described here instead: https://stackoverflow.com/a/42131603/7421666 -#define pose(%1) (poseHistory[this.jumper][((this.poseIndex + (%1)) % JS_FAILSTATS_MAX_TRACKED_TICKS + JS_FAILSTATS_MAX_TRACKED_TICKS) % JS_FAILSTATS_MAX_TRACKED_TICKS]) - - - -// =====[ TRACKING ]=========================================================== - -// We cannot put that into the tracker struct -Pose poseHistory[MAXPLAYERS + 1][JS_FAILSTATS_MAX_TRACKED_TICKS]; - -enum struct JumpTracker -{ - Jump jump; - int jumper; - int jumpoffTick; - int poseIndex; - int strafeDirection; - int lastJumpTick; - int lastTeleportTick; - int lastType; - int lastWPressedTick; - int nextCrouchRelease; - int syncTicks; - int lastCrouchPressedTick; - int tickCount; - bool failstatBlockDetected; - bool failstatFailed; - bool failstatValid; - float failstatBlockHeight; - float takeoffOrigin[3]; - float takeoffVelocity[3]; - float position[3]; - - void Init(int jumper) - { - this.jumper = jumper; - this.jump.jumper = jumper; - this.nextCrouchRelease = 100; - this.tickCount = 0; - } - - - - // =====[ ENTRYPOINTS ]======================================================= - - void Reset(bool jumped, bool ladderJump, bool jumpbug) - { - // We need to do that before we reset the jump cause we need the - // offset and type of the previous jump - this.lastType = this.DetermineType(jumped, ladderJump, jumpbug); - - // We need this for weirdjump w-release - int releaseWTemp = this.jump.releaseW; - - // Reset all stats - this.jump = emptyJump; - this.jump.type = this.lastType; - this.jump.jumper = this.jumper; - this.syncTicks = 0; - this.strafeDirection = StrafeDirection_None; - this.jump.releaseW = 100; - - // We have to show this on the jumpbug stat, not the lj stat - this.jump.crouchRelease = this.nextCrouchRelease; - this.nextCrouchRelease = 100; - - // Handle weirdjump w-release - if (this.jump.type == JumpType_WeirdJump) - { - this.jump.releaseW = releaseWTemp; - } - - // Reset pose history - this.poseIndex = 0; - // Update the first tick if it is a jumpbug. - this.UpdateOnGround(); - } - - void Begin() - { - // Initialize stats - this.CalcTakeoff(); - this.AdjustLowpreJumptypes(); - - this.failstatBlockDetected = this.jump.type != JumpType_LadderJump; - this.failstatFailed = false; - this.failstatValid = false; - this.failstatBlockHeight = this.takeoffOrigin[2]; - - // Store the original type for the always stats - this.jump.originalType = this.jump.type; - - // Notify everyone about the takeoff - Call_OnTakeoff(this.jumper, this.jump.type); - } - - void Update() - { - this.UpdatePoseHistory(); - - float speed = pose(0).speed; - - // Fix certain props that don't give you base velocity - /* - We check for speed reduction for abuse; while prop abuses increase speed, - wall collision will very likely (if not always) result in a speed reduction. - */ - float actualSpeed = GetVectorHorizontalDistance(this.position, pose(-1).position) / GetTickInterval(); - if (FloatAbs(speed - actualSpeed) > JS_SPEED_MODIFICATION_TOLERANCE && this.jump.duration != 0) - { - if (actualSpeed <= pose(-1).speed) - { - pose(0).speed = actualSpeed; - } - // This check is needed if you land via ducking instead of moving (duckbug) - else if (FloatAbs(actualSpeed) > EPSILON) - { - this.Invalidate(); - } - } - // You shouldn't gain any vertical velocity during a jump. - // This would only happen if you get boosted back up somehow, or you edgebugged. - if (!Movement_GetOnGround(this.jumper) && pose(0).velocity[2] > pose(-1).velocity[2]) - { - this.Invalidate(); - } - - this.jump.height = FloatMax(this.jump.height, this.position[2] - this.takeoffOrigin[2]); - this.jump.maxSpeed = FloatMax(this.jump.maxSpeed, speed); - this.jump.crouchTicks += Movement_GetDucking(this.jumper) ? 1 : 0; - this.syncTicks += speed > pose(-1).speed ? 1 : 0; - this.jump.duration++; - - this.UpdateStrafes(); - this.UpdateFailstat(); - this.UpdatePoseStats(); - - this.lastType = this.jump.type; - } - - void End() - { - // The jump is so invalid we don't even have to bother. - // Also check if the player just teleported. - if (this.jump.type == JumpType_FullInvalid || - this.tickCount - this.lastTeleportTick < JS_MIN_TELEPORT_DELAY) - { - return; - } - - // Measure last tick of jumpstat - this.Update(); - - // Fix the edgebug for the current position - Movement_GetNobugLandingOrigin(this.jumper, this.position); - - // There are a couple bugs and exploits we have to check for - this.EndBugfixExploits(); - - // Calculate the last stats - this.jump.distance = this.CalcDistance(); - this.jump.sync = float(this.syncTicks) / float(this.jump.duration) * 100.0; - this.jump.offset = this.position[2] - this.takeoffOrigin[2]; - - this.EndBlockDistance(); - - // Make sure the ladder has no offset for ladder jumps - if (this.jump.type == JumpType_LadderJump) - { - this.TraceLadderOffset(this.position[2]); - } - - // Calculate always-on stats - if (GOKZ_JS_GetOption(this.jumper, JSOption_JumpstatsAlways) == JSToggleOption_Enabled) - { - this.EndAlwaysJumpstats(); - } - - // Call the appropriate functions for either regular or always stats - this.Callback(); - } - - void Invalidate() - { - if (this.jump.type != JumpType_Invalid && - this.jump.type != JumpType_FullInvalid) - { - this.jump.type = JumpType_Invalid; - Call_OnJumpInvalidated(this.jumper); - } - } - - - - // =====[ BEGIN HELPERS ]===================================================== - - void CalcTakeoff() - { - // MovementAPI now correctly calculates the takeoff origin - // and velocity for jumpbugs. What is wrong though, is how - // mode plugins set bhop prespeed. - // Jumpbug takeoff origin is correct. - Movement_GetTakeoffOrigin(this.jumper, this.takeoffOrigin); - Movement_GetTakeoffVelocity(this.jumper, this.takeoffVelocity); - if (this.jump.type == JumpType_Jumpbug || this.jump.type == JumpType_MultiBhop - || this.jump.type == JumpType_Bhop || this.jump.type == JumpType_LowpreBhop - || this.jump.type == JumpType_LowpreWeirdJump || this.jump.type == JumpType_WeirdJump) - { - // Move the origin to the ground. - // The difference can only be 2 units maximum. - float bhopOrigin[3]; - CopyVector(this.takeoffOrigin, bhopOrigin); - bhopOrigin[2] -= 2.0; - TraceHullPosition(this.takeoffOrigin, bhopOrigin, playerMins, playerMaxs, this.takeoffOrigin); - } - - this.jump.preSpeed = Movement_GetTakeoffSpeed(this.jumper); - poseHistory[this.jumper][0].speed = this.jump.preSpeed; - } - - void AdjustLowpreJumptypes() - { - // Exclude SKZ and VNL stats. - if (GOKZ_GetCoreOption(this.jumper, Option_Mode) == Mode_KZTimer) - { - if (this.jump.type == JumpType_Bhop && - this.jump.preSpeed < 360.0) - { - this.jump.type = JumpType_LowpreBhop; - } - else if (this.jump.type == JumpType_WeirdJump && - this.jump.preSpeed < 300.0) - { - this.jump.type = JumpType_LowpreWeirdJump; - } - } - } - - int DetermineType(bool jumped, bool ladderJump, bool jumpbug) - { - if (gB_SpeedJustModifiedExternally[this.jumper] || this.tickCount - this.lastTeleportTick < JS_MIN_TELEPORT_DELAY) - { - return JumpType_Invalid; - } - else if (ladderJump) - { - // Check for ladder gliding. - float curtime = GetGameTime(); - float ignoreLadderJumpTime = GetEntPropFloat(this.jumper, Prop_Data, "m_ignoreLadderJumpTime"); - // Check if the ladder glide period is still active and if the player held jump in that period. - if (ignoreLadderJumpTime > curtime && - ignoreLadderJumpTime - IGNORE_JUMP_TIME < lastJumpButtonTime[this.jumper] && lastJumpButtonTime[this.jumper] < ignoreLadderJumpTime) - { - return JumpType_Invalid; - } - if (jumped) - { - return JumpType_Ladderhop; - } - else - { - return JumpType_LadderJump; - } - } - else if (!jumped) - { - return JumpType_Fall; - } - else if (jumpbug) - { - // Check for no offset - // The origin and offset is now correct, no workaround needed - if (FloatAbs(this.jump.offset) < JS_OFFSET_EPSILON && this.lastType == JumpType_LongJump) - { - return JumpType_Jumpbug; - } - else - { - return JumpType_Invalid; - } - } - else if (this.HitBhop() && !this.HitDuckbugRecently()) - { - // Check for no offset - if (FloatAbs(this.jump.offset) < JS_OFFSET_EPSILON) - { - switch (this.lastType) - { - case JumpType_LongJump:return JumpType_Bhop; - case JumpType_Bhop:return JumpType_MultiBhop; - case JumpType_LowpreBhop:return JumpType_MultiBhop; - case JumpType_MultiBhop:return JumpType_MultiBhop; - default:return JumpType_Other; - } - } - // Check for weird jump - else if (this.lastType == JumpType_Fall && - this.ValidWeirdJumpDropDistance()) - { - return JumpType_WeirdJump; - } - else - { - return JumpType_Other; - } - } - if (this.HitDuckbugRecently() || !this.GroundSpeedCappedRecently()) - { - return JumpType_Invalid; - } - return JumpType_LongJump; - } - - bool HitBhop() - { - return Movement_GetTakeoffCmdNum(this.jumper) - Movement_GetLandingCmdNum(this.jumper) <= JS_MAX_BHOP_GROUND_TICKS; - } - - bool ValidWeirdJumpDropDistance() - { - if (this.jump.offset < -1 * JS_MAX_WEIRDJUMP_FALL_OFFSET) - { - // Don't bother telling them if they fell a very far distance - if (!GetJumpstatsDisabled(this.jumper) && this.jump.offset >= -2 * JS_MAX_WEIRDJUMP_FALL_OFFSET) - { - GOKZ_PrintToChat(this.jumper, true, "%t", "Dropped Too Far (Weird Jump)", -1 * this.jump.offset, JS_MAX_WEIRDJUMP_FALL_OFFSET); - } - return false; - } - return true; - } - - bool HitDuckbugRecently() - { - return this.tickCount - lastDuckbugTime[this.jumper] <= JS_MAX_DUCKBUG_RESET_TICKS; - } - - bool GroundSpeedCappedRecently() - { - // A valid longjump needs to have their ground speed capped the tick right before. - return lastGroundSpeedCappedTime[this.jumper] == lastMovementProcessedTime[this.jumper]; - } - - // =====[ UPDATE HELPERS ]==================================================== - - // We split that up in two functions to get a reference to the pose so we - // don't have to recalculate the pose index all the time. - void UpdatePoseHistory() - { - this.poseIndex++; - this.UpdatePose(pose(0)); - } - - void UpdatePose(Pose p) - { - Movement_GetProcessingOrigin(this.jumper, p.position); - Movement_GetProcessingVelocity(this.jumper, p.velocity); - Movement_GetEyeAngles(this.jumper, p.orientation); - p.speed = GetVectorHorizontalLength(p.velocity); - - // We use the current position in a lot of places, so we store it - // separately to avoid calling 'pose' all the time. - CopyVector(p.position, this.position); - } - - // We split that up in two functions to get a reference to the pose so we - // don't have to recalculate the pose index all the time. We seperate that - // from UpdatePose() cause those stats are not calculated yet when we call that. - void UpdatePoseStats() - { - this.UpdatePoseStats_P(pose(0)); - } - - void UpdatePoseStats_P(Pose p) - { - p.duration = this.jump.duration; - p.syncTicks = this.syncTicks; - p.overlap = this.jump.overlap; - p.deadair = this.jump.deadair; - } - - void UpdateOnGround() - { - // We want accurate values to measure the first tick - this.UpdatePose(poseHistory[this.jumper][0]); - } - - void UpdateRelease() - { - // Using UpdateOnGround doesn't work because - // takeoff tick is calculated after leaving the ground. - this.jumpoffTick = Movement_GetTakeoffTick(this.jumper); - - // We also check IN_BACK cause that happens for backwards ladderjumps - if (Movement_GetButtons(this.jumper) & IN_FORWARD || - Movement_GetButtons(this.jumper) & IN_BACK) - { - this.lastWPressedTick = this.tickCount; - } - else if (this.jump.releaseW > 99) - { - this.jump.releaseW = this.lastWPressedTick - this.jumpoffTick + 1; - } - - if (Movement_GetButtons(this.jumper) & IN_DUCK) - { - this.lastCrouchPressedTick = this.tickCount; - this.nextCrouchRelease = 100; - } - else if (this.nextCrouchRelease > 99) - { - this.nextCrouchRelease = this.lastCrouchPressedTick - this.jumpoffTick - 95; - } - } - - void UpdateStrafes() - { - // Strafe direction - if (Movement_GetTurningLeft(this.jumper) && - this.strafeDirection != StrafeDirection_Left) - { - this.strafeDirection = StrafeDirection_Left; - this.jump.strafes++; - } - else if (Movement_GetTurningRight(this.jumper) && - this.strafeDirection != StrafeDirection_Right) - { - this.strafeDirection = StrafeDirection_Right; - this.jump.strafes++; - } - - // Overlap / Deadair - int buttons = Movement_GetButtons(this.jumper); - int overlap = buttons & IN_MOVERIGHT && buttons & IN_MOVELEFT ? 1 : 0; - int deadair = !(buttons & IN_MOVERIGHT) && !(buttons & IN_MOVELEFT) ? 1 : 0; - - // Sync / Gain / Loss - float deltaSpeed = pose(0).speed - pose(-1).speed; - bool gained = deltaSpeed > EPSILON; - bool lost = deltaSpeed < -EPSILON; - - // Width - float width = FloatAbs(CalcDeltaAngle(pose(0).orientation[1], pose(-1).orientation[1])); - - // Overall stats - this.jump.overlap += overlap; - this.jump.deadair += deadair; - this.jump.width += width; - - // Individual stats - if (this.jump.strafes >= JS_MAX_TRACKED_STRAFES) - { - return; - } - - int i = this.jump.strafes; - this.jump.strafes_ticks[i]++; - - this.jump.strafes_overlap[i] += overlap; - this.jump.strafes_deadair[i] += deadair; - this.jump.strafes_loss[i] += lost ? -1 * deltaSpeed : 0.0; - this.jump.strafes_width[i] += width; - - if (gained) - { - this.jump.strafes_gainTicks[i]++; - this.jump.strafes_gain[i] += deltaSpeed; - } - } - - void UpdateFailstat() - { - int coordDist, distSign; - float failstatPosition[3], block[3], traceStart[3]; - - // There's no point in going further if we're already done - if (this.failstatValid || this.failstatFailed) - { - return; - } - - // Get the coordinate system orientation. - GetCoordOrientation(this.position, this.takeoffOrigin, coordDist, distSign); - - // For ladderjumps we have to find the landing block early so we know at which point the jump failed. - // For this, we search for the block 10 units above the takeoff origin, assuming the player already - // traveled a significant enough distance in the direction of the block at this time. - if (!this.failstatBlockDetected && - this.position[2] - this.takeoffOrigin[2] < 10.0 && - this.jump.height > 10.0) - { - this.failstatBlockDetected = true; - - // Setup a trace to search for the block - CopyVector(this.takeoffOrigin, traceStart); - traceStart[2] -= 5.0; - CopyVector(traceStart, block); - traceStart[coordDist] += JS_MIN_LAJ_BLOCK_DISTANCE * distSign; - block[coordDist] += JS_MAX_LAJ_FAILSTAT_DISTANCE * distSign; - - // Search for the block - if (!TraceHullPosition(traceStart, block, playerMins, playerMaxs, block)) - { - // Mark the calculation as failed - this.failstatFailed = true; - return; - } - - // Find the block height - block[2] += 5.0; - this.failstatBlockHeight = this.FindBlockHeight(block, float(distSign) * 17.0, coordDist, 10.0) - 0.031250; - } - - // Only do the calculation once we're below the block level - if (this.position[2] >= this.failstatBlockHeight) - { - // We need that cause we can duck after getting lower than the failstat - // height and still make the block. - this.failstatValid = false; - return; - } - - // Calculate the true origin where the player would have hit the ground. - this.GetFailOrigin(this.failstatBlockHeight, failstatPosition, -1); - - // Calculate the jump distance. - this.jump.distance = FloatAbs(GetVectorHorizontalDistance(failstatPosition, this.takeoffOrigin)); - - // Construct the maximum landing origin, assuming the player reached - // at least the middle of the gap. - CopyVector(this.takeoffOrigin, block); - block[coordDist] = 2 * failstatPosition[coordDist] - this.takeoffOrigin[coordDist]; - block[view_as<int>(!coordDist)] = failstatPosition[view_as<int>(!coordDist)]; - block[2] = this.failstatBlockHeight; - - // Calculate block stats - if ((this.lastType == JumpType_LongJump || - this.lastType == JumpType_Bhop || - this.lastType == JumpType_MultiBhop || - this.lastType == JumpType_Ladderhop || - this.lastType == JumpType_WeirdJump || - this.lastType == JumpType_Jumpbug || - this.lastType == JumpType_LowpreBhop || - this.lastType == JumpType_LowpreWeirdJump) - && this.jump.distance >= JS_MIN_BLOCK_DISTANCE) - { - // Add the player model to the distance. - this.jump.distance += 32.0; - - this.CalcBlockStats(block, true); - } - else if (this.lastType == JumpType_LadderJump && - this.jump.distance >= JS_MIN_LAJ_BLOCK_DISTANCE) - { - this.CalcLadderBlockStats(block, true); - } - else - { - this.failstatFailed = true; - return; - } - - if (this.jump.block > 0) - { - // Calculate the last stats - this.jump.sync = float(this.syncTicks) / float(this.jump.duration) * 100.0; - this.jump.offset = failstatPosition[2] - this.takeoffOrigin[2]; - - // Call the callback for the reporting. - Call_OnFailstat(this.jump); - - // Mark the calculation as successful - this.failstatValid = true; - } - else - { - this.failstatFailed = true; - } - } - - - - // =====[ END HELPERS ]===================================================== - - float CalcDistance() - { - float distance = GetVectorHorizontalDistance(this.takeoffOrigin, this.position); - - // Check whether the distance is NaN - if (distance != distance) - { - this.Invalidate(); - - // We need that for the always stats - float pos[3]; - - // For the always stats it's ok to ignore the bug - Movement_GetOrigin(this.jumper, pos); - - distance = GetVectorHorizontalDistance(this.takeoffOrigin, pos); - } - - if (this.jump.originalType != JumpType_LadderJump) - { - distance += 32.0; - } - return distance; - } - - void EndBlockDistance() - { - if ((this.jump.type == JumpType_LongJump || - this.jump.type == JumpType_Bhop || - this.jump.type == JumpType_MultiBhop || - this.jump.type == JumpType_Ladderhop || - this.jump.type == JumpType_WeirdJump || - this.jump.type == JumpType_Jumpbug || - this.jump.type == JumpType_LowpreBhop || - this.jump.type == JumpType_LowpreWeirdJump) - && this.jump.distance >= JS_MIN_BLOCK_DISTANCE) - { - this.CalcBlockStats(this.position); - } - else if (this.jump.type == JumpType_LadderJump && - this.jump.distance >= JS_MIN_LAJ_BLOCK_DISTANCE) - { - this.CalcLadderBlockStats(this.position); - } - } - - void EndAlwaysJumpstats() - { - // Only calculate that form of edge if the regular block calculations failed - if (this.jump.block == 0 && this.jump.type != JumpType_LadderJump) - { - this.CalcAlwaysEdge(); - } - - // It's possible that the offset calculation failed with the nobug origin - // functions, so we have to fix it when that happens. The offset shouldn't - // be affected by the bug anyway. - if (this.jump.offset != this.jump.offset) - { - Movement_GetOrigin(this.jumper, this.position); - this.jump.offset = this.position[2] - this.takeoffOrigin[2]; - } - } - - void EndBugfixExploits() - { - // Try to prevent a form of booster abuse - if (!this.IsValidAirtime()) - { - this.Invalidate(); - } - } - - bool IsValidAirtime() - { - // Ladderjumps can have pretty much any airtime. - if (this.jump.type == JumpType_LadderJump) - { - return true; - } - - // Ladderhops can have a maximum airtime of 102. - if (this.jump.type == JumpType_Ladderhop - && this.jump.duration <= 102) - { - return true; - } - - // Crouchjumped or perfed longjumps/bhops can have a maximum of 101 airtime - // when the lj bug occurs. Since we've fixed that the airtime is valid. - if (this.jump.duration <= 101) - { - return true; - } - - return false; - } - - void Callback() - { - if (GOKZ_JS_GetOption(this.jumper, JSOption_JumpstatsAlways) == JSToggleOption_Enabled) - { - Call_OnJumpstatAlways(this.jump); - } - else - { - Call_OnLanding(this.jump); - } - } - - - - // =====[ ALWAYS FAILSTATS ]================================================== - - void AlwaysFailstat() - { - bool foundBlock; - int coordDist, distSign; - float traceStart[3], traceEnd[3], tracePos[3], landingPos[3], orientation[3], failOrigin[3]; - - // Check whether the jump was already handled - if (this.jump.type == JumpType_FullInvalid || this.failstatValid) - { - return; - } - - // Initialize the trace boxes - float traceMins[3] = { 0.0, 0.0, 0.0 }; - float traceLongMaxs[3] = { 0.0, 0.0, 200.0 }; - float traceShortMaxs[3] = { 0.0, 0.0, 54.0 }; - - // Clear the stats - this.jump.miss = 0.0; - this.jump.distance = 0.0; - - // Calculate the edge - this.CalcAlwaysEdge(); - - // We will search for the block based on the direction the player was looking - CopyVector(pose(0).orientation, orientation); - - // Get the landing orientation - coordDist = FloatAbs(orientation[0]) < FloatAbs(orientation[1]); - distSign = orientation[coordDist] > 0 ? 1 : -1; - - // Initialize the traces - CopyVector(this.position, traceStart); - CopyVector(this.position, traceEnd); - - // Assume the miss is less than 100 units - traceEnd[coordDist] += 100.0 * distSign; - - // Search for the end block with the long trace - foundBlock = TraceHullPosition(traceStart, traceEnd, traceMins, traceLongMaxs, tracePos); - - // If not even the long trace finds the block, we're out of luck - if (foundBlock) - { - // Search for the block height - tracePos[2] = this.position[2]; - foundBlock = this.TryFindBlockHeight(tracePos, landingPos, coordDist, distSign); - - // Maybe there was a headbanger, try with the short trace instead - if (!foundBlock) - { - if (TraceHullPosition(traceStart, traceEnd, traceMins, traceShortMaxs, tracePos)) - { - // Search for the height again - tracePos[2] = this.position[2]; - foundBlock = this.TryFindBlockHeight(tracePos, landingPos, coordDist, distSign); - } - } - - if (foundBlock) - { - // Search for the last tick the player was above the landing block elevation. - for (int i = 0; i < JS_FAILSTATS_MAX_TRACKED_TICKS; i++) - { - Pose p; - - // This copies it, but it shouldn't be that much of a problem - p = pose(-i); - - if(p.position[2] >= landingPos[2]) - { - // Calculate the correct fail position - this.GetFailOrigin(landingPos[2], failOrigin, -i); - - // Calculate all missing stats - this.jump.miss = FloatAbs(failOrigin[coordDist] - landingPos[coordDist]) - 16.0; - this.jump.distance = GetVectorHorizontalDistance(failOrigin, this.takeoffOrigin); - this.jump.offset = failOrigin[2] - this.takeoffOrigin[2]; - this.jump.duration = p.duration; - this.jump.overlap = p.overlap; - this.jump.deadair = p.deadair; - this.jump.sync = float(p.syncTicks) / float(this.jump.duration) * 100.0; - break; - } - } - } - } - - // Notify everyone about the jump - Call_OnFailstatAlways(this.jump); - - // Fully invalidate the jump cause we failstatted it already - this.jump.type = JumpType_FullInvalid; - } - - void CalcAlwaysEdge() - { - int coordDist, distSign; - float traceStart[3], traceEnd[3], velocity[3]; - float ladderNormal[3], ladderMins[3], ladderMaxs[3]; - - // Ladder jumps have a different definition of edge - if (this.jump.originalType == JumpType_LadderJump) - { - // Get a vector that points outwards from the lader towards the player - GetEntPropVector(this.jumper, Prop_Send, "m_vecLadderNormal", ladderNormal); - - // Initialize box to search for the ladder - if (ladderNormal[0] > ladderNormal[1]) - { - ladderMins = view_as<float>({ 0.0, -20.0, 0.0 }); - ladderMaxs = view_as<float>({ 0.0, 20.0, 0.0 }); - coordDist = 0; - } - else - { - ladderMins = view_as<float>({ -20.0, 0.0, 0.0 }); - ladderMaxs = view_as<float>({ 20.0, 0.0, 0.0 }); - coordDist = 1; - } - - // The max the ladder will be away is the player model (16) + danvari tech (10) + a safety unit - CopyVector(this.takeoffOrigin, traceEnd); - traceEnd[coordDist] += 27.0; - - // Search for the ladder - if (TraceHullPosition(this.takeoffOrigin, traceEnd, ladderMins, ladderMaxs, traceEnd)) - { - this.jump.edge = FloatAbs(traceEnd[coordDist] - this.takeoffOrigin[coordDist]) - 16.0; - } - } - else - { - // We calculate the orientation of the takeoff block based on what - // direction the player was moving - CopyVector(this.takeoffVelocity, velocity); - this.jump.edge = -1.0; - - // Calculate the takeoff orientation - coordDist = FloatAbs(velocity[0]) < FloatAbs(velocity[1]); - distSign = velocity[coordDist] > 0 ? 1 : -1; - - // Make sure we hit the jumpoff block - CopyVector(this.takeoffOrigin, traceEnd); - traceEnd[coordDist] -= 16.0 * distSign; - traceEnd[2] -= 1.0; - - // Assume a max edge of 20 - CopyVector(traceEnd, traceStart); - traceStart[coordDist] += 20.0 * distSign; - - // Trace the takeoff block - if (TraceRayPosition(traceStart, traceEnd, traceEnd)) - { - // Check whether the trace was stuck in the block from the beginning - if (FloatAbs(traceEnd[coordDist] - traceStart[coordDist]) > EPSILON) - { - // Block trace ends 0.03125 in front of the actual block. Adjust the edge correctly. - this.jump.edge = FloatAbs(traceEnd[coordDist] - this.takeoffOrigin[coordDist] + (16.0 - 0.03125) * distSign); - } - } - } - } - - bool TryFindBlockHeight(const float position[3], float result[3], int coordDist, int distSign) - { - float traceStart[3], traceEnd[3]; - - // Setup the trace points - CopyVector(position, traceStart); - traceStart[coordDist] += distSign; - CopyVector(traceStart, traceEnd); - - // We search in 54 unit steps - traceStart[2] += 54.0; - - // We search with multiple trace starts in case the landing block has a roof - for (int i = 0; i < 3; i += 1) - { - if (TraceRayPosition(traceStart, traceEnd, result)) - { - // Make sure the trace didn't get stuck right away - if (FloatAbs(result[2] - traceStart[2]) > EPSILON) - { - result[coordDist] -= distSign; - return true; - } - } - - // Try the next are to find the block. We use two different values to have - // some overlap in case the block perfectly aligns with the trace. - traceStart[2] += 54.0; - traceEnd[2] += 53.0; - } - - return false; - } - - - - // =====[ BLOCK STATS HELPERS ]=============================================== - - void CalcBlockStats(float landingOrigin[3], bool checkOffset = false) - { - int coordDist, coordDev, distSign; - float middle[3], startBlock[3], endBlock[3], sweepBoxMin[3], sweepBoxMax[3]; - - // Get the orientation of the block. - GetCoordOrientation(landingOrigin, this.takeoffOrigin, coordDist, distSign); - coordDev = !coordDist; - - // We can't make measurements from within an entity, so we assume the - // player had a remotely reasonable edge and that the middle of the jump - // is not over a block and then start measuring things out from there. - middle[coordDist] = (this.takeoffOrigin[coordDist] + landingOrigin[coordDist]) / 2; - middle[coordDev] = (this.takeoffOrigin[coordDev] + landingOrigin[coordDev]) / 2; - middle[2] = this.takeoffOrigin[2] - 1.0; - - // Get the deviation. - this.jump.deviation = FloatAbs(landingOrigin[coordDev] - this.takeoffOrigin[coordDev]); - - // Setup a sweeping line that starts in the middle and tries to search for the smallest - // block within the deviation of the player. - sweepBoxMin[coordDist] = 0.0; - sweepBoxMin[coordDev] = -this.jump.deviation - 16.0; - sweepBoxMin[2] = 0.0; - sweepBoxMax[coordDist] = 0.0; - sweepBoxMax[coordDev] = this.jump.deviation + 16.0; - sweepBoxMax[2] = 0.0; - - // Modify the takeoff and landing origins to line up with the middle and respect - // the bounding box of the player. - startBlock[coordDist] = this.takeoffOrigin[coordDist] - distSign * 16.0; - // Sometimes you can land 0.03125 units in front of a block, so the trace needs to be extended. - endBlock[coordDist] = landingOrigin[coordDist] + distSign * (16.0 + 0.03125); - startBlock[coordDev] = middle[coordDev]; - endBlock[coordDev] = middle[coordDev]; - startBlock[2] = middle[2]; - endBlock[2] = middle[2]; - - // Search for the blocks - if (!TraceHullPosition(middle, startBlock, sweepBoxMin, sweepBoxMax, startBlock) - || !TraceHullPosition(middle, endBlock, sweepBoxMin, sweepBoxMax, endBlock)) - { - return; - } - - // Make sure the edges of the blocks are parallel. - if (!this.BlockAreEdgesParallel(startBlock, endBlock, this.jump.deviation + 32.0, coordDist, coordDev)) - { - this.jump.block = 0; - this.jump.edge = -1.0; - return; - } - - // Needed for failstats, but you need the endBlock position for that, so we do it here. - if (checkOffset) - { - endBlock[2] += 1.0; - if (FloatAbs(this.FindBlockHeight(endBlock, float(distSign) * 17.0, coordDist, 1.0) - landingOrigin[2]) > JS_OFFSET_EPSILON) - { - return; - } - } - - // Calculate distance and edge. - this.jump.block = RoundFloat(FloatAbs(endBlock[coordDist] - startBlock[coordDist])); - // Block trace ends 0.03125 in front of the actual block. Adjust the edge correctly. - this.jump.edge = FloatAbs(startBlock[coordDist] - this.takeoffOrigin[coordDist] + (16.0 - 0.03125) * distSign); - - // Make it easier to check for blocks that too short - if (this.jump.block < JS_MIN_BLOCK_DISTANCE) - { - this.jump.block = 0; - this.jump.edge = -1.0; - } - } - - void CalcLadderBlockStats(float landingOrigin[3], bool checkOffset = false) - { - int coordDist, coordDev, distSign; - float sweepBoxMin[3], sweepBoxMax[3], blockPosition[3], ladderPosition[3], normalVector[3], endBlock[3], middle[3]; - - // Get the orientation of the block. - GetCoordOrientation(landingOrigin, this.takeoffOrigin, coordDist, distSign); - coordDev = !coordDist; - - // Get the deviation. - this.jump.deviation = FloatAbs(landingOrigin[coordDev] - this.takeoffOrigin[coordDev]); - - // Make sure the ladder is aligned. - GetEntPropVector(this.jumper, Prop_Send, "m_vecLadderNormal", normalVector); - if (FloatAbs(FloatAbs(normalVector[coordDist]) - 1.0) > EPSILON) - { - return; - } - - // Make sure we'll find the block and ladder. - CopyVector(this.takeoffOrigin, ladderPosition); - CopyVector(landingOrigin, endBlock); - endBlock[2] -= 1.0; - ladderPosition[2] = endBlock[2]; - - // Setup a line to search for the ladder. - sweepBoxMin[coordDist] = 0.0; - sweepBoxMin[coordDev] = -20.0; - sweepBoxMin[2] = 0.0; - sweepBoxMax[coordDist] = 0.0; - sweepBoxMax[coordDev] = 20.0; - sweepBoxMax[2] = 0.0; - middle[coordDist] = ladderPosition[coordDist] + distSign * JS_MIN_LAJ_BLOCK_DISTANCE; - middle[coordDev] = endBlock[coordDev]; - middle[2] = ladderPosition[2]; - - // Search for the ladder. - if (!TraceHullPosition(ladderPosition, middle, sweepBoxMin, sweepBoxMax, ladderPosition)) - { - return; - } - - // Find the block and make sure it's aligned - endBlock[coordDist] += distSign * 16.0; - if (!TraceRayPositionNormal(middle, endBlock, blockPosition, normalVector) - || FloatAbs(FloatAbs(normalVector[coordDist]) - 1.0) > EPSILON) - { - return; - } - - // Needed for failstats, but you need the blockPosition for that, so we do it here. - if (checkOffset) - { - blockPosition[2] += 1.0; - if (!this.TraceLadderOffset(this.FindBlockHeight(blockPosition, float(distSign), coordDist, 1.0) - 0.031250)) - { - return; - } - } - - // Calculate distance and edge. - this.jump.block = RoundFloat(FloatAbs(blockPosition[coordDist] - ladderPosition[coordDist])); - this.jump.edge = FloatAbs(this.takeoffOrigin[coordDist] - ladderPosition[coordDist]) - 16.0; - - // Make it easier to check for blocks that too short - if (this.jump.block < JS_MIN_LAJ_BLOCK_DISTANCE) - { - this.jump.block = 0; - this.jump.edge = -1.0; - } - } - - bool TraceLadderOffset(float landingHeight) - { - float traceOrigin[3], traceEnd[3], ladderTop[3], ladderNormal[3]; - - // Get normal vector of the ladder. - GetEntPropVector(this.jumper, Prop_Send, "m_vecLadderNormal", ladderNormal); - - // 10 units is the furthest away from the ladder surface you can get while still being on the ladder. - traceOrigin[0] = this.takeoffOrigin[0] - 10.0 * ladderNormal[0]; - traceOrigin[1] = this.takeoffOrigin[1] - 10.0 * ladderNormal[1]; - traceOrigin[2] = this.takeoffOrigin[2] + 5; - - CopyVector(traceOrigin, traceEnd); - traceEnd[2] = this.takeoffOrigin[2] - 10; - - // Search for the ladder - if (!TraceHullPosition(traceOrigin, traceEnd, playerMinsEx, playerMaxsEx, ladderTop) - || FloatAbs(ladderTop[2] - landingHeight) > JS_OFFSET_EPSILON) - { - this.Invalidate(); - return false; - } - return true; - } - - bool BlockTraceAligned(const float origin[3], const float end[3], int coordDist) - { - float normalVector[3]; - if (!TraceRayNormal(origin, end, normalVector)) - { - return false; - } - return FloatAbs(FloatAbs(normalVector[coordDist]) - 1.0) <= EPSILON; - } - - bool BlockAreEdgesParallel(const float startBlock[3], const float endBlock[3], float deviation, int coordDist, int coordDev) - { - float start[3], end[3], offset; - - // We use very short rays to find the blocks where they're supposed to be and use - // their normals to determine whether they're parallel or not. - offset = startBlock[coordDist] > endBlock[coordDist] ? 0.1 : -0.1; - - // We search for the blocks on both sides of the player, on one of the sides - // there has to be a valid block. - start[coordDist] = startBlock[coordDist] - offset; - start[coordDev] = startBlock[coordDev] - deviation; - start[2] = startBlock[2]; - - end[coordDist] = startBlock[coordDist] + offset; - end[coordDev] = startBlock[coordDev] - deviation; - end[2] = startBlock[2]; - - if (this.BlockTraceAligned(start, end, coordDist)) - { - start[coordDist] = endBlock[coordDist] + offset; - end[coordDist] = endBlock[coordDist] - offset; - if (this.BlockTraceAligned(start, end, coordDist)) - { - return true; - } - start[coordDist] = startBlock[coordDist] - offset; - end[coordDist] = startBlock[coordDist] + offset; - } - - start[coordDev] = startBlock[coordDev] + deviation; - end[coordDev] = startBlock[coordDev] + deviation; - - if (this.BlockTraceAligned(start, end, coordDist)) - { - start[coordDist] = endBlock[coordDist] + offset; - end[coordDist] = endBlock[coordDist] - offset; - if (this.BlockTraceAligned(start, end, coordDist)) - { - return true; - } - } - - return false; - } - - float FindBlockHeight(const float origin[3], float offset, int coord, float searchArea) - { - float block[3], traceStart[3], traceEnd[3], normalVector[3]; - - // Setup the trace. - CopyVector(origin, traceStart); - traceStart[coord] += offset; - CopyVector(traceStart, traceEnd); - traceStart[2] += searchArea; - traceEnd[2] -= searchArea; - - // Find the block height. - if (!TraceRayPositionNormal(traceStart, traceEnd, block, normalVector) - || FloatAbs(normalVector[2] - 1.0) > EPSILON) - { - return -99999999999999999999.0; // Let's hope that's wrong enough - } - - return block[2]; - } - - void GetFailOrigin(float planeHeight, float result[3], int poseIndex) - { - float newVel[3], oldVel[3]; - - // Calculate the actual velocity. - CopyVector(pose(poseIndex).velocity, oldVel); - ScaleVector(oldVel, GetTickInterval()); - - // Calculate at which percentage of the velocity vector we hit the plane. - float scale = (planeHeight - pose(poseIndex).position[2]) / oldVel[2]; - - // Calculate the position we hit the plane. - CopyVector(oldVel, newVel); - ScaleVector(newVel, scale); - AddVectors(pose(poseIndex).position, newVel, result); - } -} - -static JumpTracker jumpTrackers[MAXPLAYERS + 1]; - - - -// =====[ HELPER FUNCTIONS ]=================================================== - -void GetCoordOrientation(const float vec1[3], const float vec2[3], int &coordDist, int &distSign) -{ - coordDist = FloatAbs(vec1[0] - vec2[0]) < FloatAbs(vec1[1] - vec2[1]); - distSign = vec1[coordDist] > vec2[coordDist] ? 1 : -1; -} - -bool TraceRayPosition(const float traceStart[3], const float traceEnd[3], float position[3]) -{ - Handle trace = TR_TraceRayFilterEx(traceStart, traceEnd, MASK_PLAYERSOLID, RayType_EndPoint, TraceEntityFilterPlayers); - if (TR_DidHit(trace)) - { - TR_GetEndPosition(position, trace); - delete trace; - return true; - } - delete trace; - return false; -} - -static bool TraceRayNormal(const float traceStart[3], const float traceEnd[3], float rayNormal[3]) -{ - Handle trace = TR_TraceRayFilterEx(traceStart, traceEnd, MASK_PLAYERSOLID, RayType_EndPoint, TraceEntityFilterPlayers); - if (TR_DidHit(trace)) - { - TR_GetPlaneNormal(trace, rayNormal); - delete trace; - return true; - } - delete trace; - return false; -} - -static bool TraceRayPositionNormal(const float traceStart[3], const float traceEnd[3], float position[3], float rayNormal[3]) -{ - Handle trace = TR_TraceRayFilterEx(traceStart, traceEnd, MASK_PLAYERSOLID, RayType_EndPoint, TraceEntityFilterPlayers); - if (TR_DidHit(trace)) - { - TR_GetEndPosition(position, trace); - TR_GetPlaneNormal(trace, rayNormal); - delete trace; - return true; - } - delete trace; - return false; -} - -static bool TraceHullPosition(const float traceStart[3], const float traceEnd[3], const float mins[3], const float maxs[3], float position[3]) -{ - Handle trace = TR_TraceHullFilterEx(traceStart, traceEnd, mins, maxs, MASK_PLAYERSOLID, TraceEntityFilterPlayers); - if (TR_DidHit(trace)) - { - TR_GetEndPosition(position, trace); - delete trace; - return true; - } - delete trace; - return false; -} - - - -// =====[ EVENTS ]============================================================= - -void OnPluginStart_JumpTracking() -{ - GameData gd = LoadGameConfigFile("sdktools.games/engine.csgo"); - int offset = gd.GetOffset("AcceptInput"); - if (offset == -1) - { - SetFailState("Failed to get AcceptInput offset"); - } - - acceptInputHook = DHookCreate(offset, HookType_Entity, ReturnType_Bool, ThisPointer_CBaseEntity, DHooks_AcceptInput); - DHookAddParam(acceptInputHook, HookParamType_CharPtr); - DHookAddParam(acceptInputHook, HookParamType_CBaseEntity); - DHookAddParam(acceptInputHook, HookParamType_CBaseEntity); - //varaint_t is a union of 12 (float[3]) plus two int type params 12 + 8 = 20 - DHookAddParam(acceptInputHook, HookParamType_Object, 20, DHookPass_ByVal|DHookPass_ODTOR|DHookPass_OCTOR|DHookPass_OASSIGNOP); - DHookAddParam(acceptInputHook, HookParamType_Int); - delete gd; -} - -void OnOptionChanged_JumpTracking(int client, const char[] option) -{ - if (StrEqual(option, gC_CoreOptionNames[Option_Mode])) - { - jumpTrackers[client].jump.type = JumpType_FullInvalid; - } -} - -void OnClientPutInServer_JumpTracking(int client) -{ - if (entityTouchList[client] != INVALID_HANDLE) - { - delete entityTouchList[client]; - } - entityTouchList[client] = new ArrayList(); - lastNoclipTime[client] = 0; - lastDuckbugTime[client] = 0; - lastJumpButtonTime[client] = 0.0; - jumpTrackers[client].Init(client); - DHookEntity(acceptInputHook, true, client); -} - - -// This was originally meant for invalidating jumpstats but was removed. -void OnJumpInvalidated_JumpTracking(int client) -{ - jumpTrackers[client].Invalidate(); -} - -void OnJumpValidated_JumpTracking(int client, bool jumped, bool ladderJump, bool jumpbug) -{ - if (!validCmd[client]) - { - return; - } - - // Update: Takeoff speed should be always correct with the new MovementAPI. - if (jumped) - { - jumpTrackers[client].lastJumpTick = jumpTrackers[client].tickCount; - } - jumpTrackers[client].Reset(jumped, ladderJump, jumpbug); - jumpTrackers[client].Begin(); -} - -void OnStartTouchGround_JumpTracking(int client) -{ - if (!doFailstatAlways[client]) - { - jumpTrackers[client].End(); - } -} - -void OnStartTouch_JumpTracking(int client, int touched) -{ - if (entityTouchList[client] != INVALID_HANDLE) - { - entityTouchList[client].Push(touched); - // Do not immediately invalidate jumps upon collision. - // Give the player a few ticks of leniency for late ducking. - } -} - -void OnTouch_JumpTracking(int client) -{ - if (entityTouchList[client] != INVALID_HANDLE && entityTouchList[client].Length > 0) - { - entityTouchDuration[client]++; - } - if (!Movement_GetOnGround(client) && entityTouchDuration[client] > JS_TOUCH_GRACE_TICKS) - { - jumpTrackers[client].Invalidate(); - } -} - -void OnEndTouch_JumpTracking(int client, int touched) -{ - if (entityTouchList[client] != INVALID_HANDLE) - { - int index = entityTouchList[client].FindValue(touched); - if (index != -1) - { - entityTouchList[client].Erase(index); - } - if (entityTouchList[client].Length == 0) - { - entityTouchDuration[client] = 0; - } - } -} - -void OnPlayerRunCmd_JumpTracking(int client, int buttons, int tickcount) -{ - if (!IsValidClient(client) || !IsPlayerAlive(client)) - { - return; - } - - jumpTrackers[client].tickCount = tickcount; - - if (GetClientButtons(client) & IN_JUMP) - { - lastJumpButtonTime[client] = GetGameTime(); - } - - if (CheckNoclip(client)) - { - lastNoclipTime[client] = tickcount; - } - - // Don't bother checking if player is already in air and jumpstat is already invalid - if (Movement_GetOnGround(client) || - jumpTrackers[client].jump.type != JumpType_FullInvalid) - { - UpdateValidCmd(client, buttons); - } -} - -public Action Movement_OnWalkMovePost(int client) -{ - lastGroundSpeedCappedTime[client] = jumpTrackers[client].tickCount; - return Plugin_Continue; -} - -public Action Movement_OnPlayerMovePost(int client) -{ - lastMovementProcessedTime[client] = jumpTrackers[client].tickCount; - return Plugin_Continue; -} - -public void OnPlayerRunCmdPost_JumpTracking(int client) -{ - if (!IsValidClient(client) || !IsPlayerAlive(client)) - { - return; - } - - // Check for always failstats - if (doFailstatAlways[client]) - { - doFailstatAlways[client] = false; - // Prevent TP shenanigans that would trigger failstats - //jumpTypeLast[client] = JumpType_Invalid; - - if (GOKZ_JS_GetOption(client, JSOption_JumpstatsAlways) == JSToggleOption_Enabled && - isInAir[client]) - { - jumpTrackers[client].AlwaysFailstat(); - } - } - - if (!Movement_GetOnGround(client)) - { - isInAir[client] = true; - jumpTrackers[client].Update(); - } - - if (Movement_GetOnGround(client) || - Movement_GetMovetype(client) == MOVETYPE_LADDER) - { - isInAir[client] = false; - jumpTrackers[client].UpdateOnGround(); - } - - // We always have to track this, no matter if in the air or not - jumpTrackers[client].UpdateRelease(); - - if (Movement_GetDuckbugged(client)) - { - lastDuckbugTime[client] = jumpTrackers[client].tickCount; - } -} - -static MRESReturn DHooks_AcceptInput(int client, DHookReturn hReturn, DHookParam hParams) -{ - if (!IsValidClient(client) || !IsPlayerAlive(client)) - { - return MRES_Ignored; - } - - // Get args - static char param[64]; - static char command[64]; - DHookGetParamString(hParams, 1, command, sizeof(command)); - if (StrEqual(command, "AddOutput")) - { - DHookGetParamObjectPtrString(hParams, 4, 0, ObjectValueType_String, param, sizeof(param)); - char kv[16]; - SplitString(param, " ", kv, sizeof(kv)); - // KVs are case insensitive. - if (StrEqual(kv[0], "origin", false)) - { - // The player technically did not get "teleported" but the origin gets changed regardless, - // which effectively is a teleport. - OnTeleport_FailstatAlways(client); - } - } - return MRES_Ignored; -} - -// =====[ CHECKS ]===== - -static void UpdateValidCmd(int client, int buttons) -{ - if (!CheckGravity(client) - || !CheckBaseVelocity(client) - || !CheckInWater(client) - || !CheckTurnButtons(buttons)) - { - InvalidateJumpstat(client); - validCmd[client] = false; - } - else - { - validCmd[client] = true; - } - - if (jumpTrackers[client].tickCount - lastNoclipTime[client] < GOKZ_JUMPSTATS_NOCLIP_RESET_TICKS) - { - jumpTrackers[client].jump.type = JumpType_FullInvalid; - } - - if (!CheckLadder(client)) - { - InvalidateJumpstat(client); - } -} - -static bool CheckGravity(int client) -{ - float gravity = Movement_GetGravity(client); - // Allow 1.0 and 0.0 gravity as both values appear during normal gameplay - if (FloatAbs(gravity - 1.0) > EPSILON && FloatAbs(gravity) > EPSILON) - { - return false; - } - return true; -} - -static bool CheckBaseVelocity(int client) -{ - float baseVelocity[3]; - Movement_GetBaseVelocity(client, baseVelocity); - if (FloatAbs(baseVelocity[0]) > EPSILON || - FloatAbs(baseVelocity[1]) > EPSILON || - FloatAbs(baseVelocity[2]) > EPSILON) - { - return false; - } - return true; -} - -static bool CheckInWater(int client) -{ - int waterLevel = GetEntProp(client, Prop_Data, "m_nWaterLevel"); - return waterLevel == 0; -} - -static bool CheckTurnButtons(int buttons) -{ - // Don't allow +left or +right turns binds - return !(buttons & (IN_LEFT | IN_RIGHT)); -} - -static bool CheckNoclip(int client) -{ - return Movement_GetMovetype(client) == MOVETYPE_NOCLIP; -} - -static bool CheckLadder(int client) -{ - return Movement_GetMovetype(client) != MOVETYPE_LADDER; -} - - - -// =====[ EXTERNAL HELPER FUNCTIONS ]========================================== - -void InvalidateJumpstat(int client) -{ - jumpTrackers[client].Invalidate(); -} - -float GetStrafeSync(Jump jump, int strafe) -{ - if (strafe < JS_MAX_TRACKED_STRAFES) - { - return float(jump.strafes_gainTicks[strafe]) - / float(jump.strafes_ticks[strafe]) - * 100.0; - } - else - { - return 0.0; - } -} - -float GetStrafeAirtime(Jump jump, int strafe) -{ - if (strafe < JS_MAX_TRACKED_STRAFES) - { - return float(jump.strafes_ticks[strafe]) - / float(jump.duration) - * 100.0; - } - else - { - return 0.0; - } -} - -void OnTeleport_FailstatAlways(int client) -{ - // We want to synchronize all of that - doFailstatAlways[client] = true; - - // gokz-core does that too, but for some reason we have to do it again - InvalidateJumpstat(client); - - jumpTrackers[client].lastTeleportTick = jumpTrackers[client].tickCount; -} diff --git a/sourcemod/scripting/gokz-jumpstats/jump_validating.sp b/sourcemod/scripting/gokz-jumpstats/jump_validating.sp deleted file mode 100644 index c6835c7..0000000 --- a/sourcemod/scripting/gokz-jumpstats/jump_validating.sp +++ /dev/null @@ -1,82 +0,0 @@ -/* - Invalidating invalid jumps, such as ones with a modified velocity. -*/ - -static Handle processMovementHookPost; - -void OnPluginStart_JumpValidating() -{ - Handle gamedataConf = LoadGameConfigFile("gokz-core.games"); - if (gamedataConf == null) - { - SetFailState("Failed to load gokz-core gamedata"); - } - - // CreateInterface - // Thanks SlidyBat and ici - 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"); - } - - processMovementHookPost = DHookCreate(offset, HookType_Raw, ReturnType_Void, ThisPointer_Ignore, DHook_ProcessMovementPost); - DHookAddParam(processMovementHookPost, HookParamType_CBaseEntity); - DHookAddParam(processMovementHookPost, HookParamType_ObjectPtr); - DHookRaw(processMovementHookPost, false, IGameMovement); -} - -static MRESReturn DHook_ProcessMovementPost(Handle hParams) -{ - int client = DHookGetParam(hParams, 1); - if (!IsValidClient(client) || IsFakeClient(client)) - { - return MRES_Ignored; - } - float pVelocity[3], velocity[3]; - Movement_GetProcessingVelocity(client, pVelocity); - Movement_GetVelocity(client, velocity); - - gB_SpeedJustModifiedExternally[client] = false; - for (int i = 0; i < 3; i++) - { - if (FloatAbs(pVelocity[i] - velocity[i]) > EPSILON) - { - // The current velocity doesn't match the velocity of the end of movement processing, - // so it must have been modified by something like a trigger. - InvalidateJumpstat(client); - gB_SpeedJustModifiedExternally[client] = true; - break; - } - } - - return MRES_Ignored; -}
\ No newline at end of file diff --git a/sourcemod/scripting/gokz-jumpstats/options.sp b/sourcemod/scripting/gokz-jumpstats/options.sp deleted file mode 100644 index 7e0e9e9..0000000 --- a/sourcemod/scripting/gokz-jumpstats/options.sp +++ /dev/null @@ -1,86 +0,0 @@ -/* - Options for jumpstats, including an option to disable it completely. -*/ - - - -// =====[ PUBLIC ]===== - -bool GetJumpstatsDisabled(int client) -{ - return GOKZ_JS_GetOption(client, JSOption_JumpstatsMaster) == JSToggleOption_Disabled - || (GOKZ_JS_GetOption(client, JSOption_MinChatTier) == DistanceTier_None - && GOKZ_JS_GetOption(client, JSOption_MinConsoleTier) == DistanceTier_None - && GOKZ_JS_GetOption(client, JSOption_MinSoundTier) == DistanceTier_None - && GOKZ_JS_GetOption(client, JSOption_FailstatsConsole) == JSToggleOption_Disabled - && GOKZ_JS_GetOption(client, JSOption_FailstatsChat) == JSToggleOption_Disabled - && GOKZ_JS_GetOption(client, JSOption_JumpstatsAlways) == JSToggleOption_Disabled); -} - - - -// =====[ EVENTS ]===== - -void OnOptionsMenuReady_Options() -{ - RegisterOptions(); -} - -void OnClientPutInServer_Options(int client) -{ - if (GOKZ_JS_GetOption(client, JSOption_MinSoundTier) == DistanceTier_Meh) - { - GOKZ_JS_SetOption(client, JSOption_MinSoundTier, DistanceTier_Impressive); - } -} - -void OnOptionChanged_Options(int client, const char[] option, any newValue) -{ - JSOption jsOption; - if (GOKZ_JS_IsJSOption(option, jsOption)) - { - if (jsOption == JSOption_MinSoundTier && newValue == DistanceTier_Meh) - { - GOKZ_JS_SetOption(client, JSOption_MinSoundTier, DistanceTier_Impressive); - } - else - { - PrintOptionChangeMessage(client, jsOption, newValue); - } - } -} - - - -// =====[ PRIVATE ]===== - -static void RegisterOptions() -{ - for (JSOption option; option < JSOPTION_COUNT; option++) - { - GOKZ_RegisterOption(gC_JSOptionNames[option], gC_JSOptionDescriptions[option], - OptionType_Int, gI_JSOptionDefaults[option], 0, gI_JSOptionCounts[option] - 1); - } -} - -static void PrintOptionChangeMessage(int client, JSOption option, any newValue) -{ - // NOTE: Not all options have a message for when they are changed. - switch (option) - { - case JSOption_JumpstatsMaster: - { - switch (newValue) - { - case JSToggleOption_Enabled: - { - GOKZ_PrintToChat(client, true, "%t", "Jumpstats Option - Master Switch - Enable"); - } - case JSToggleOption_Disabled: - { - GOKZ_PrintToChat(client, true, "%t", "Jumpstats Option - Master Switch - Disable"); - } - } - } - } -} diff --git a/sourcemod/scripting/gokz-jumpstats/options_menu.sp b/sourcemod/scripting/gokz-jumpstats/options_menu.sp deleted file mode 100644 index 903a8bb..0000000 --- a/sourcemod/scripting/gokz-jumpstats/options_menu.sp +++ /dev/null @@ -1,145 +0,0 @@ -static TopMenu optionsTopMenu; -static TopMenuObject catJumpstats; -static TopMenuObject itemsJumpstats[JSOPTION_COUNT]; - - - -// =====[ PUBLIC ]===== - -void DisplayJumpstatsOptionsMenu(int client) -{ - optionsTopMenu.DisplayCategory(catJumpstats, client); -} - - - -// =====[ EVENTS ]===== - -void OnOptionsMenuCreated_OptionsMenu(TopMenu topMenu) -{ - if (optionsTopMenu == topMenu && catJumpstats != INVALID_TOPMENUOBJECT) - { - return; - } - - catJumpstats = topMenu.AddCategory(JS_OPTION_CATEGORY, TopMenuHandler_Categories); -} - -void OnOptionsMenuReady_OptionsMenu(TopMenu topMenu) -{ - // Make sure category exists - if (catJumpstats == INVALID_TOPMENUOBJECT) - { - GOKZ_OnOptionsMenuCreated(topMenu); - } - - if (optionsTopMenu == topMenu) - { - return; - } - - optionsTopMenu = topMenu; - - // Add HUD option items - for (int option = 0; option < view_as<int>(JSOPTION_COUNT); option++) - { - itemsJumpstats[option] = optionsTopMenu.AddItem(gC_JSOptionNames[option], TopMenuHandler_HUD, catJumpstats); - } -} - -public void TopMenuHandler_Categories(TopMenu topmenu, TopMenuAction action, TopMenuObject topobj_id, int param, char[] buffer, int maxlength) -{ - if (action == TopMenuAction_DisplayOption || action == TopMenuAction_DisplayTitle) - { - if (topobj_id == catJumpstats) - { - Format(buffer, maxlength, "%T", "Options Menu - Jumpstats", param); - } - } -} - -public void TopMenuHandler_HUD(TopMenu topmenu, TopMenuAction action, TopMenuObject topobj_id, int param, char[] buffer, int maxlength) -{ - JSOption option = JSOPTION_INVALID; - for (int i = 0; i < view_as<int>(JSOPTION_COUNT); i++) - { - if (topobj_id == itemsJumpstats[i]) - { - option = view_as<JSOption>(i); - break; - } - } - - if (option == JSOPTION_INVALID) - { - return; - } - - if (action == TopMenuAction_DisplayOption) - { - if (option == JSOption_JumpstatsMaster || - option == JSOption_ExtendedChatReport || - option == JSOption_FailstatsConsole || - option == JSOption_FailstatsChat || - option == JSOption_JumpstatsAlways) - { - FormatToggleableOptionDisplay(param, option, buffer, maxlength); - } - else - { - FormatDistanceTierOptionDisplay(param, option, buffer, maxlength); - } - } - else if (action == TopMenuAction_SelectOption) - { - GOKZ_JS_CycleOption(param, option); - optionsTopMenu.Display(param, TopMenuPosition_LastCategory); - } -} - - - -// =====[ PRIVATE ]===== - -static void FormatToggleableOptionDisplay(int client, JSOption option, char[] buffer, int maxlength) -{ - if (GOKZ_JS_GetOption(client, option) == JSToggleOption_Disabled) - { - FormatEx(buffer, maxlength, "%T - %T", - gI_JSOptionPhrases[option], client, - "Options Menu - Disabled", client); - } - else - { - FormatEx(buffer, maxlength, "%T - %T", - gI_JSOptionPhrases[option], client, - "Options Menu - Enabled", client); - } -} - -static void FormatDistanceTierOptionDisplay(int client, JSOption option, char[] buffer, int maxlength) -{ - int optionValue = GOKZ_JS_GetOption(client, option); - if (optionValue == DistanceTier_None) // Disabled - { - FormatEx(buffer, maxlength, "%T - %T", - gI_JSOptionPhrases[option], client, - "Options Menu - Disabled", client); - } - else - { - // Add a plus sign to anything below the highest tier - if (optionValue < DISTANCETIER_COUNT - 1) - { - FormatEx(buffer, maxlength, "%T - %s+", - gI_JSOptionPhrases[option], client, - gC_DistanceTiers[optionValue]); - } - else - { - FormatEx(buffer, maxlength, "%T - %s", - gI_JSOptionPhrases[option], client, - gC_DistanceTiers[optionValue]); - } - } -} |
