Last active
January 18, 2023 00:15
-
-
Save rtldg/a4e91c93d5cb94ba6b98a87fac6cde4f to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <sourcemod> | |
#include <sdktools> | |
#include <clientprefs> | |
#include <sdkhooks> | |
#include <smlib> | |
#include <shavit> | |
#include <morecolors> | |
//#define DEBUG | |
//#define LJSERV | |
#pragma semicolon 1 | |
#define MIN(%0,%1) (%0 > %1 ? %1 : %0) | |
#define MAX(%0,%1) (%0 < %1 ? %1 : %0) | |
#define LJSTATS_VERSION "2.0.1" | |
#define LJTOP_DIR "configs/ljstats/" | |
#define LJTOP_FILE "ljtop.txt" | |
#define LJTOP_NUM_ENTRIES 50 | |
#define LJSOUND_NUM 5 | |
#define MAX_STRAFES 50 | |
#define BHOP_TIME 0.3 | |
#define STAMINA_RECHARGE_TIME 0.58579 | |
#define SW_ANGLE_THRESHOLD 20.0 | |
#define LJ_HEIGHT_DELTA_MIN -0.01 // Dropjump limit | |
#define LJ_HEIGHT_DELTA_MAX 1.5 // Upjump limit | |
#define CJ_HEIGHT_DELTA_MIN -0.01 | |
#define CJ_HEIGHT_DELTA_MAX 1.5 | |
#define WJ_HEIGHT_DELTA_MIN -0.01 | |
#define WJ_HEIGHT_DELTA_MAX 1.5 | |
#define BJ_HEIGHT_DELTA_MIN -2.0 // dynamic pls | |
#define BJ_HEIGHT_DELTA_MAX 2.0 | |
#define LAJ_HEIGHT_DELTA_MIN -6.0 | |
#define LAJ_HEIGHT_DELTA_MAX 0.0 | |
#define HUD_HINT_SIZE 256 | |
public Plugin:myinfo = | |
{ | |
name = "ljstats", | |
author = "Miu", | |
description = "longjump stats", | |
version = LJSTATS_VERSION, | |
url = "https://forums.alliedmods.net/showthread.php?p=2060983" | |
} | |
enum PlayerState | |
{ | |
bool:bLJEnabled, | |
bool:bHidePanel, | |
bool:bHideBhopPanel, | |
bool:bShowBhopStats, | |
bool:bBeam, | |
bool:bSound, | |
bool:bBlockMode, | |
nVerbosity, | |
bool:bShowAllJumps, | |
#if defined LJSERV | |
bool:bShowPrestrafeHint, | |
#endif | |
Float:fBlockDistance, | |
Float:vBlockNormal[2], | |
Float:vBlockEndPos[3], | |
bool:bFailedBlock, | |
bool:bDuck, | |
bool:bLastDuckState, | |
bool:bSecondLastDuckState, | |
JUMP_DIRECTION:JumpDir, | |
ILLEGAL_JUMP_FLAGS:IllegalJumpFlags, | |
JUMP_TYPE:LastJumpType, | |
JUMP_TYPE:JumpType, | |
Float:fLandTime, | |
Float:fLastJumpHeightDelta, | |
nBhops, | |
bool:bOnGround, | |
bool:bOnLadder, | |
Float:fEdge, | |
Float:vJumpOrigin[3], | |
Float:fWJDropPre, | |
Float:fPrestrafe, | |
Float:fJumpDistance, | |
Float:fHeightDelta, | |
Float:fJumpHeight, | |
Float:fSync, | |
Float:fMaxSpeed, | |
Float:fFinalSpeed, | |
Float:fTrajectory, | |
Float:fGain, | |
Float:fLoss, | |
STRAFE_DIRECTION:CurStrafeDir, | |
nStrafes, | |
STRAFE_DIRECTION:StrafeDir[MAX_STRAFES], | |
Float:fStrafeGain[MAX_STRAFES], | |
Float:fStrafeLoss[MAX_STRAFES], | |
Float:fStrafeSync[MAX_STRAFES], | |
nStrafeTicks[MAX_STRAFES], | |
nStrafeTicksSynced[MAX_STRAFES], | |
nTotalTicks, | |
Float:fTotalAngle, | |
Float:fSyncedAngle, | |
bool:bStamina, | |
nJumpTick, | |
nLastAerialTick, | |
Float:vLastOrigin[3], | |
Float:vLastAngles[3], | |
Float:vLastVelocity[3], | |
String:strHUDHint[HUD_HINT_SIZE / 4], // string characters are stored as cells | |
Float:fPersonalBest, | |
nSpectators, | |
nSpectatorTarget, | |
GAP_SELECTION_MODE:GapSelectionMode, | |
Float:vGapPoint1[3], | |
LastButtons, | |
} | |
#define LJTOP_MIN_NUM_STATS_0 7 | |
#define LJTOP_MIN_NUM_STATS_1 14 | |
#define LJTOP_MAX_NUM_STATS 14 + 16 * 5 | |
#define LJTOP_MAX_STRAFES 16 | |
enum TopStats | |
{ | |
String:m_strName[64 / 4], | |
String:m_strSteamID[32 / 4], | |
Float:m_fDistance, | |
Float:m_fPrestrafe, | |
m_nStrafes, // | |
Float:m_fSync, | |
Float:m_fMaxSpeed, | |
m_nTotalTicks, | |
Float:m_fSyncedAngle, | |
Float:m_fTotalAngle, // | |
Float:m_fHeightDelta, | |
Float:m_fBlockDistance, | |
Float:m_fTrajectory, | |
m_nTimestamp, | |
STRAFE_DIRECTION:m_StrafeDir[LJTOP_MAX_STRAFES], | |
Float:m_fStrafeGain[LJTOP_MAX_STRAFES], | |
Float:m_fStrafeLoss[LJTOP_MAX_STRAFES], | |
m_nStrafeTicks[LJTOP_MAX_STRAFES], | |
Float:m_fStrafeSync[LJTOP_MAX_STRAFES], | |
} | |
enum ILLEGAL_JUMP_FLAGS | |
{ | |
IJF_NONE = 0, | |
IJF_WORLD = 1 << 0, | |
IJF_BOOSTER = 1 << 1, | |
IJF_GRAVITY = 1 << 2, | |
IJF_TELEPORT = 1 << 3, | |
IJF_LAGGEDMOVEMENTVALUE = 1 << 4, | |
IJF_PRESTRAFE = 1 << 5, | |
IJF_SCOUT = 1 << 6, | |
IJF_NOCLIP = 1 << 7, | |
} | |
enum JUMP_TYPE | |
{ | |
JT_LONGJUMP, | |
JT_COUNTJUMP, | |
JT_WEIRDJUMP, | |
JT_BHOPJUMP, | |
JT_LADDERJUMP, | |
JT_BHOP, | |
JT_DROP, | |
JT_END, | |
} | |
enum JUMP_DIRECTION | |
{ | |
JD_NONE, // Indeterminate | |
JD_NORMAL, | |
JD_FORWARDS = JD_NORMAL, | |
JD_SIDEWAYS, | |
JD_BACKWARDS, | |
JD_END, | |
} | |
enum STRAFE_DIRECTION | |
{ | |
SD_NONE, | |
SD_W, | |
SD_D, | |
SD_A, | |
SD_S, | |
SD_WA, | |
SD_WD, | |
SD_SA, | |
SD_SD, | |
SD_END, | |
} | |
enum GAP_SELECTION_MODE | |
{ | |
GSM_NONE, | |
GSM_GAP, | |
GSM_GAPSECOND, | |
GSM_BLOCKGAP, | |
} | |
enum LJTOP_TABLE | |
{ | |
LT_LJ, | |
LT_BLOCKLJ, | |
LT_SWLJ, | |
LT_BWLJ, | |
LT_CJ, | |
LT_BJ, | |
LT_LAJ, | |
LT_STRAFEBHOP, | |
LT_END, | |
} | |
new String:g_strLJTopTags[LT_END][] = | |
{ | |
"lj", | |
"blj", | |
"swlj", | |
"bwlj", | |
"cj", | |
"bj", | |
"laj", | |
"strafebhop" | |
}; | |
new String:g_strLJTopTableName[LT_END][] = | |
{ | |
"Longjump", | |
"Block longjump", | |
"Sideways longjump", | |
"Backwards longjump", | |
"Countjump", | |
"Bhopjump", | |
"Ladderjump", | |
"_strafe bhop" | |
}; | |
new String:g_strLJTopOutput[LT_END][] = | |
{ | |
"lj", | |
"block lj", | |
"sideways lj", | |
"backwards lj", | |
"countjump", | |
"bhopjump", | |
"ladderjump", | |
"_strafe bhop" | |
}; | |
new String:g_strJumpType[JT_END][] = | |
{ | |
"Longjump", | |
"Countjump", | |
"Weirdjump", | |
"Bhopjump", | |
"Ladderjump", | |
"Bhop", | |
"Drop" | |
}; | |
new String:g_strJumpTypeLwr[JT_END][] = | |
{ | |
"longjump", | |
"countjump", | |
"weirdjump", | |
"bhopjump", | |
"ladderjump", | |
"bhop", | |
"drop" | |
}; | |
new String:g_strJumpTypeShort[JT_END][] = | |
{ | |
"LJ", | |
"CJ", | |
"WJ", | |
"BJ", | |
"LAJ", | |
"Bhop", | |
"Drop" | |
}; | |
new const Float:g_fHeightDeltaMin[JT_END] = | |
{ | |
LJ_HEIGHT_DELTA_MIN, | |
LJ_HEIGHT_DELTA_MIN, | |
WJ_HEIGHT_DELTA_MIN, | |
BJ_HEIGHT_DELTA_MIN, | |
LAJ_HEIGHT_DELTA_MIN, | |
-3.402823466e38, | |
-3.402823466e38 | |
}; | |
new const Float:g_fHeightDeltaMax[JT_END] = | |
{ | |
LJ_HEIGHT_DELTA_MAX, | |
LJ_HEIGHT_DELTA_MAX, | |
WJ_HEIGHT_DELTA_MAX, | |
BJ_HEIGHT_DELTA_MAX, | |
LAJ_HEIGHT_DELTA_MAX, | |
3.402823466e38, | |
3.402823466e38 | |
}; | |
// SourcePawn is silly | |
#define HEIGHT_DELTA_MIN(%0) (Float:g_fHeightDeltaMin[Float:%0]) | |
#define HEIGHT_DELTA_MAX(%0) (Float:g_fHeightDeltaMax[Float:%0]) | |
new g_PlayerStates[MAXPLAYERS + 1][PlayerState]; | |
new g_LJTop[LT_END][LJTOP_NUM_ENTRIES][TopStats]; | |
new Handle:g_hLJTopMainMenu = INVALID_HANDLE; | |
new Handle:g_hLJTopMenus[LT_END] = INVALID_HANDLE; | |
new g_BeamModel; | |
new Handle:g_hCvarColorMin = INVALID_HANDLE; | |
new Handle:g_hCvarColorMax = INVALID_HANDLE; | |
new Handle:g_hCvarLJMin = INVALID_HANDLE; | |
new Handle:g_hCvarLJMax = INVALID_HANDLE; | |
new Handle:g_hCvarLJMaxPrestrafe = INVALID_HANDLE; | |
new Handle:g_hCvarLJScoutStats = INVALID_HANDLE; | |
new Handle:g_hCvarLJNoDuckMin = INVALID_HANDLE; | |
new Handle:g_hCvarLJClientMin = INVALID_HANDLE; | |
new Handle:g_hCvarWJMin = INVALID_HANDLE; | |
new Handle:g_hCvarWJDropMax = INVALID_HANDLE; | |
new Handle:g_hCvarBJMin = INVALID_HANDLE; | |
new Handle:g_hCvarLAJMin = INVALID_HANDLE; | |
new Handle:g_hCvarPrintFailedBlockStats = INVALID_HANDLE; | |
new Handle:g_hCvarShowBhopStats = INVALID_HANDLE; | |
new Handle:g_hCvarOutput16Style = INVALID_HANDLE; | |
new Handle:g_hCvarVerbosity = INVALID_HANDLE; | |
new Handle:g_hCvarLJTopAllowEasyBJ = INVALID_HANDLE; | |
new Handle:g_hCvarLJSound = INVALID_HANDLE; | |
new Handle:g_hCvarLJSound1 = INVALID_HANDLE; | |
new Handle:g_hCvarLJSound2 = INVALID_HANDLE; | |
new Handle:g_hCvarLJSound3 = INVALID_HANDLE; | |
new Handle:g_hCvarLJSound4 = INVALID_HANDLE; | |
new Handle:g_hCvarLJSound5 = INVALID_HANDLE; | |
new Handle:g_hCvarLJSound1File = INVALID_HANDLE; | |
new Handle:g_hCvarLJSound2File = INVALID_HANDLE; | |
new Handle:g_hCvarLJSound3File = INVALID_HANDLE; | |
new Handle:g_hCvarLJSound4File = INVALID_HANDLE; | |
new Handle:g_hCvarLJSound5File = INVALID_HANDLE; | |
new Handle:g_hCvarLJSoundToAll[5] = {INVALID_HANDLE, ...}; | |
new Handle:g_hCvarMaxspeed = INVALID_HANDLE; | |
new Handle:g_hCvarEnableBunnyHopping = INVALID_HANDLE; | |
new Handle:g_hCookieDefaultsSet = INVALID_HANDLE; | |
new Handle:g_hCookieLJEnabled = INVALID_HANDLE; | |
new Handle:g_hCookieBlockMode = INVALID_HANDLE; | |
new Handle:g_hCookieBeam = INVALID_HANDLE; | |
new Handle:g_hCookieSound = INVALID_HANDLE; | |
new Handle:g_hCookieHidePanel = INVALID_HANDLE; | |
new Handle:g_hCookieHideBhopPanel = INVALID_HANDLE; | |
new Handle:g_hCookieShowBhopStats = INVALID_HANDLE; | |
new Handle:g_hCookieVerbosity = INVALID_HANDLE; | |
new Handle:g_hCookieShowAllJumps = INVALID_HANDLE; | |
#if defined LJSERV | |
new Handle:g_hCookieShowPrestrafeHint = INVALID_HANDLE; | |
#endif | |
new Handle:g_hCookiePersonalBest = INVALID_HANDLE; | |
new g_ColorMin[3] = {0xAD, 0xD8, 0xE6}; // Lightblue! | |
new g_ColorMax[3] = {0x00, 0x00, 0xFF}; | |
new Float:g_fLJMin = 260.0; | |
new Float:g_fLJMax = 275.0; | |
new Float:g_fLJMaxPrestrafe = 280.0; | |
new bool:g_bLJScoutStats = false; | |
new Float:g_fLJNoDuckMin = 256.0; | |
new Float:g_fLJClientMin = 0.0; | |
new Float:g_fWJMin = 270.0; | |
new Float:g_fWJDropMax = 30.0; | |
new Float:g_fBJMin = 270.0; | |
new Float:g_fLAJMin = 140.0; | |
new g_nVerbosity = 2; | |
new bool:g_bPrintFailedBlockStats = true; | |
new bool:g_bShowBhopStats = true; | |
new bool:g_bOutput16Style = false; | |
new bool:g_bLJTopAllowEasyBJ = true; | |
new bool:g_bLJSound = true; | |
new Float:g_fLJSound[5] = {260.0, 265.0, 268.0, 270.0, 0.0}; | |
new String:g_strLJSoundFile[5][64] = {"misc/perfect.wav", "misc/mod_wickedsick.wav", "misc/mod_godlike.wav", "misc/holyshit.wav", ""}; | |
new bool:g_bLJSoundToAll[5] = false; | |
new Float:g_fMaxspeed = 320.0; // sv_maxspeed | |
new bool:g_bEnableBunnyHopping = true; // sv_enablebunnyhopping | |
Handle:CreateCvar(String:strName[], String:strValue[]) | |
{ | |
new Handle:hCvar = CreateConVar(strName, strValue); | |
HookConVarChange(hCvar, OnCvarChange); | |
return hCvar; | |
} | |
public OnPluginStart() | |
{ | |
DB_Connect(); | |
DB_CreateTables(); | |
DB_LoadLJTop(); | |
CreateConVar("mljstats_version", LJSTATS_VERSION, "ljstats version", FCVAR_PLUGIN|FCVAR_SPONLY|FCVAR_REPLICATED|FCVAR_NOTIFY); | |
g_hCvarColorMin = CreateCvar("ljstats_color_min", "ADD8E6"); | |
g_hCvarColorMax = CreateCvar("ljstats_color_max", "0000FF"); | |
g_hCvarLJMin = CreateCvar("ljstats_lj_min", "260"); | |
g_hCvarLJMax = CreateCvar("ljstats_lj_max", "275"); | |
g_hCvarLJMaxPrestrafe = CreateCvar("ljstats_lj_max_prestrafe", "280"); | |
g_hCvarLJScoutStats = CreateCvar("ljstats_lj_scout_stats", "0"); | |
g_hCvarLJNoDuckMin = CreateCvar("ljstats_lj_noduck_min", "256"); | |
g_hCvarLJClientMin = CreateCvar("ljstats_lj_client_min", "0.0"); | |
g_hCvarWJMin = CreateCvar("ljstats_wj_min", "270"); | |
g_hCvarWJDropMax = CreateCvar("ljstats_wj_drop_max", "30.0"); | |
g_hCvarBJMin = CreateCvar("ljstats_bj_min", "270"); | |
g_hCvarLAJMin = CreateCvar("ljstats_laj_min", "140"); | |
g_hCvarVerbosity = CreateCvar("ljstats_verbosity", "2"); | |
g_hCvarPrintFailedBlockStats = CreateCvar("ljstats_print_failed_block_stats", "1"); | |
g_hCvarShowBhopStats = CreateCvar("ljstats_show_bhop_stats", "0"); | |
g_hCvarOutput16Style = CreateCvar("ljstats_output_1.6_style", "0"); | |
g_hCvarLJTopAllowEasyBJ = CreateCvar("ljstats_ljtop_allow_easybhopjump", "1"); | |
g_hCvarLJSound = CreateCvar("ljstats_lj_sound", "1"); | |
g_hCvarLJSound1 = CreateCvar("ljstats_lj_sound1", "260"); | |
g_hCvarLJSound2 = CreateCvar("ljstats_lj_sound2", "265"); | |
g_hCvarLJSound3 = CreateCvar("ljstats_lj_sound3", "268"); | |
g_hCvarLJSound4 = CreateCvar("ljstats_lj_sound4", "270"); | |
g_hCvarLJSound5 = CreateCvar("ljstats_lj_sound5", "0"); | |
g_hCvarLJSound1File = CreateCvar("ljstats_lj_sound1_file", g_strLJSoundFile[0]); | |
g_hCvarLJSound2File = CreateCvar("ljstats_lj_sound2_file", g_strLJSoundFile[1]); | |
g_hCvarLJSound3File = CreateCvar("ljstats_lj_sound3_file", g_strLJSoundFile[2]); | |
g_hCvarLJSound4File = CreateCvar("ljstats_lj_sound4_file", g_strLJSoundFile[3]); | |
g_hCvarLJSound5File = CreateCvar("ljstats_lj_sound5_file", g_strLJSoundFile[4]); | |
g_hCvarLJSoundToAll[0] = CreateCvar("ljstats_lj_sound1_to_all", "0"); | |
g_hCvarLJSoundToAll[1] = CreateCvar("ljstats_lj_sound2_to_all", "0"); | |
g_hCvarLJSoundToAll[2] = CreateCvar("ljstats_lj_sound3_to_all", "0"); | |
g_hCvarLJSoundToAll[3] = CreateCvar("ljstats_lj_sound4_to_all", "0"); | |
g_hCvarLJSoundToAll[4] = CreateCvar("ljstats_lj_sound5_to_all", "0"); | |
g_hCvarMaxspeed = FindConVar("sv_maxspeed"); | |
if(g_hCvarMaxspeed) | |
{ | |
g_fMaxspeed = GetConVarFloat(g_hCvarMaxspeed); | |
} | |
HookConVarChange(g_hCvarMaxspeed, OnCvarChange); | |
g_hCvarEnableBunnyHopping = FindConVar("sv_enablebunnyhopping"); | |
if(g_hCvarEnableBunnyHopping) | |
{ | |
g_bEnableBunnyHopping = GetConVarBool(g_hCvarEnableBunnyHopping); | |
} | |
HookConVarChange(g_hCvarEnableBunnyHopping, OnCvarChange); | |
CreateNative("LJStats_CancelJump", Native_CancelJump); | |
HookEvent("player_jump", Event_PlayerJump); | |
RegConsoleCmd("sm_ljhelp", Command_LJHelp); | |
#if !defined LJSERV | |
RegConsoleCmd("sm_lj", Command_LJ); | |
#else | |
RegConsoleCmd("sm_lj", Command_LJSettings); | |
#endif | |
RegConsoleCmd("sm_ljsettings", Command_LJSettings); | |
RegConsoleCmd("sm_ljs", Command_LJSettings); | |
RegConsoleCmd("sm_ljpanel", Command_LJPanel); | |
RegConsoleCmd("sm_ljbeam", Command_LJBeam); | |
RegConsoleCmd("sm_ljblock", Command_LJBlock); | |
RegConsoleCmd("sm_ljb", Command_LJBlock); | |
RegConsoleCmd("sm_ljsound", Command_LJSound); | |
RegConsoleCmd("sm_ljver", Command_LJVersion); | |
RegConsoleCmd("sm_ljversion", Command_LJVersion); | |
RegConsoleCmd("sm_ljtop", Command_LJTop); | |
#if defined LJSERV | |
RegConsoleCmd("sm_wr", Command_LJTop); | |
#endif | |
RegAdminCmd("sm_ljtopdelete", Command_LJTopDelete, ADMFLAG_RCON); | |
RegConsoleCmd("sm_gap", Command_Gap); | |
RegConsoleCmd("sm_blockgap", Command_BlockGap); | |
RegConsoleCmd("sm_tele", Command_Tele); | |
RegAdminCmd("sm_ljtopdeleteall", Command_Delete, ADMFLAG_RCON); | |
RegConsoleCmd("sm_ljpb", Command_PersonalBest); | |
RegConsoleCmd("sm_pb", Command_PersonalBest); | |
RegConsoleCmd("sm_personalbest", Command_PersonalBest); | |
RegConsoleCmd("sm_pr", Command_PersonalBest); | |
RegConsoleCmd("sm_resetpersonalbest", Command_ResetPersonalBest); | |
RegAdminCmd("sm_ljtoploadfromfile", Command_LJTopLoadFromFile, ADMFLAG_RCON); | |
g_hCookieDefaultsSet = RegClientCookie("ljstats_defaultsset", "ljstats_defaultsset", CookieAccess_Public); | |
g_hCookieLJEnabled = RegClientCookie("ljstats_ljenabled", "ljstats_ljenabled", CookieAccess_Public); | |
g_hCookieBlockMode = RegClientCookie("ljstats_blockmode", "ljstats_blockmode", CookieAccess_Public); | |
g_hCookieBeam = RegClientCookie("ljstats_beam", "ljstats_beam", CookieAccess_Public); | |
g_hCookieSound = RegClientCookie("ljstats_sound", "ljstats_sound", CookieAccess_Public); | |
g_hCookieHidePanel = RegClientCookie("ljstats_hidepanel", "ljstats_hidepanel", CookieAccess_Public); | |
g_hCookieHideBhopPanel = RegClientCookie("ljstats_hidebhoppanel", "ljstats_hidebhoppanel", CookieAccess_Public); | |
g_hCookieShowBhopStats = RegClientCookie("ljstats_showbhopstats", "ljstats_showbhopstats", CookieAccess_Public); | |
g_hCookieVerbosity = RegClientCookie("ljstats_verbosity", "ljstats_verbosity", CookieAccess_Public); | |
g_hCookieShowAllJumps = RegClientCookie("ljstats_showalljumps", "ljstats_showalljumps", CookieAccess_Public); | |
#if defined LJSERV | |
g_hCookieShowPrestrafeHint = RegClientCookie("ljstats_showprestrafehint", "ljstats_showprestrafehint", CookieAccess_Public); | |
#endif | |
g_hCookiePersonalBest = RegClientCookie("ljstats_personalbest", "ljstats_personalbest", CookieAccess_Private); | |
for(new i = 1; i < MaxClients; i++) | |
{ | |
if(IsClientInGame(i)) | |
{ | |
OnClientPutInServer(i); | |
OnClientCookiesCached(i); | |
} | |
} | |
} | |
/* | |
enum TopStats | |
{ | |
String:m_strName[64 / 4], | |
String:m_strSteamID[32 / 4], | |
Float:m_fDistance, | |
Float:m_fPrestrafe, | |
m_nStrafes, // | |
Float:m_fSync, | |
Float:m_fMaxSpeed, | |
m_nTotalTicks, | |
Float:m_fSyncedAngle, | |
Float:m_fTotalAngle, // | |
Float:m_fHeightDelta, | |
Float:m_fBlockDistance, | |
Float:m_fTrajectory, | |
m_nTimestamp, | |
STRAFE_DIRECTION:m_StrafeDir[LJTOP_MAX_STRAFES], | |
Float:m_fStrafeGain[LJTOP_MAX_STRAFES], | |
Float:m_fStrafeLoss[LJTOP_MAX_STRAFES], | |
m_nStrafeTicks[LJTOP_MAX_STRAFES], | |
Float:m_fStrafeSync[LJTOP_MAX_STRAFES], | |
} | |
*/ | |
new const String:SQL_CreateLJTopTable[] = "CREATE TABLE IF NOT EXISTS ljtop (ljtable VARCHAR(16), name VARCHAR(64), steamid VARCHAR(32), distance FLOAT, prestrafe FLOAT, strafes INT, sync FLOAT, maxspeed FLOAT, totalticks FLOAT, syncedangle FLOAT, totalangle FLOAT, heightdelta FLOAT, blockdistance FLOAT, trajectory FLOAT, timestamp INT)"; | |
new const String:SQL_CreateLJTopStrafesTable[] = "CREATE TABLE IF NOT EXISTS ljtopstrafes (ljtable VARCHAR(16), rank INTEGER, strafenum INTEGER, dir VARCHAR(3), gain FLOAT, loss FLOAT, ticks INT, sync FLOAT)"; | |
new const String:SQL_LoadLJTop[] = "SELECT * FROM ljtop ORDER BY distance DESC"; | |
new const String:SQL_LoadLJTopStrafes[] = "SELECT * FROM ljtopstrafes"; | |
new const String:SQL_DeleteLJTop[] = "DELETE FROM ljtop"; | |
new const String:SQL_DeleteLJTopStrafes[] = "DELETE FROM ljtopstrafes"; | |
new Handle:g_DB = INVALID_HANDLE; | |
DB_Connect() | |
{ | |
if (g_DB != INVALID_HANDLE) | |
{ | |
CloseHandle(g_DB); | |
} | |
decl String:error[255]; | |
g_DB = SQL_Connect("ljstats", true, error, sizeof(error)); | |
if (g_DB == INVALID_HANDLE) | |
{ | |
LogError(error); | |
CloseHandle(g_DB); | |
} | |
} | |
DB_CreateTables() | |
{ | |
new Handle:hQ = SQL_Query(g_DB, SQL_CreateLJTopTable); | |
if(hQ == INVALID_HANDLE) | |
{ | |
decl String:error[255]; | |
SQL_GetError(g_DB, error, sizeof(error)); | |
LogError("DB_CreateTables failed: %s", error); | |
return; | |
} | |
SQL_Query(g_DB, SQL_CreateLJTopStrafesTable); | |
if(hQ == INVALID_HANDLE) | |
{ | |
decl String:error[255]; | |
SQL_GetError(g_DB, error, sizeof(error)); | |
LogError("DB_CreateTables failed: %s", error); | |
return; | |
} | |
} | |
DB_LoadLJTop() | |
{ | |
SQL_TQuery(g_DB, DB_LoadLJTop_Callback, SQL_LoadLJTop); | |
SQL_TQuery(g_DB, DB_LoadLJTopStrafes_Callback, SQL_LoadLJTopStrafes); | |
} | |
public DB_LoadLJTop_Callback(Handle:owner, Handle:hndl, String:error[], any:pack) | |
{ | |
if(hndl == INVALID_HANDLE) | |
{ | |
LogError("DB_LoadLJTop failed: %s", error); | |
return; | |
} | |
new rows = SQL_GetRowCount(hndl); | |
new it[LT_END] = {0, ...}; | |
for(new i = 0; i < rows; i++) | |
{ | |
SQL_FetchRow(hndl); | |
decl String:table[16], String:name[64], String:steamid[32]; | |
SQL_FetchStringByName(hndl, "ljtable", table, sizeof(table)); | |
new iTable = _:GetLJTopTable(table); | |
if(it[iTable] > LJTOP_NUM_ENTRIES - 1) | |
{ | |
LogError("Too many rows for table %s", table); | |
return; | |
} | |
if (iTable == -1) | |
{ | |
LogError("DB_LoadLJTop: Invalid table %s", table); | |
return; | |
} | |
SQL_FetchStringByName(hndl, "name", name, sizeof(name)); | |
SQL_FetchStringByName(hndl, "steamid", steamid, sizeof(steamid)); | |
strcopy(g_LJTop[iTable][it[iTable]][m_strName], 64, name); | |
strcopy(g_LJTop[iTable][it[iTable]][m_strSteamID], 32, steamid); | |
g_LJTop[iTable][it[iTable]][m_fDistance] = SQL_FetchFloatByName(hndl, "distance"); | |
g_LJTop[iTable][it[iTable]][m_fPrestrafe] = SQL_FetchFloatByName(hndl, "prestrafe"); | |
g_LJTop[iTable][it[iTable]][m_nStrafes] = SQL_FetchIntByName(hndl, "strafes"); | |
g_LJTop[iTable][it[iTable]][m_fSync] = SQL_FetchFloatByName(hndl, "sync"); | |
g_LJTop[iTable][it[iTable]][m_fMaxSpeed] = SQL_FetchFloatByName(hndl, "maxspeed"); | |
g_LJTop[iTable][it[iTable]][m_nTotalTicks] = SQL_FetchIntByName(hndl, "totalticks"); | |
g_LJTop[iTable][it[iTable]][m_fSyncedAngle] = SQL_FetchFloatByName(hndl, "syncedangle"); | |
g_LJTop[iTable][it[iTable]][m_fTotalAngle] = SQL_FetchFloatByName(hndl, "totalangle"); | |
g_LJTop[iTable][it[iTable]][m_fBlockDistance] = SQL_FetchFloatByName(hndl, "blockdistance"); | |
g_LJTop[iTable][it[iTable]][m_fTrajectory] = SQL_FetchFloatByName(hndl, "trajectory"); | |
g_LJTop[iTable][it[iTable]][m_nTimestamp] = SQL_FetchIntByName(hndl, "timestamp"); | |
it[iTable]++; | |
} | |
LJTopCreateMainMenu(); | |
for(new LJTOP_TABLE:i; i < LT_END; i++) | |
{ | |
LJTopCreateMenu(i); | |
} | |
} | |
public DB_LoadLJTopStrafes_Callback(Handle:owner, Handle:hndl, String:error[], any:pack) | |
{ | |
if(hndl == INVALID_HANDLE) | |
{ | |
LogError("DB_LoadLJTopStrafes failed: %s", error); | |
return; | |
} | |
new rows = SQL_GetRowCount(hndl); | |
for(new i = 0; i < rows; i++) | |
{ | |
SQL_FetchRow(hndl); | |
decl String:table[16]; | |
SQL_FetchStringByName(hndl, "ljtable", table, sizeof(table)); | |
new iTable = -1; | |
for(new LJTOP_TABLE:j; j < LT_END; j++) | |
{ | |
if(!strcmp(g_strLJTopTags[j], table)) | |
{ | |
iTable = _:j; | |
break; | |
} | |
} | |
if (iTable == -1) | |
{ | |
LogError("DB_LoadLJTop: Invalid table %s", table); | |
return; | |
} | |
new iEntry = SQL_FetchIntByName(hndl, "rank"); | |
new iStrafe = SQL_FetchIntByName(hndl, "strafenum"); | |
decl String:key[3]; | |
SQL_FetchStringByName(hndl, "dir", key, sizeof(key)); | |
g_LJTop[iTable][iEntry][m_StrafeDir][iStrafe] = GetStrafeDir(key); | |
g_LJTop[iTable][iEntry][m_fStrafeGain][iStrafe] = SQL_FetchFloatByName(hndl, "gain"); | |
g_LJTop[iTable][iEntry][m_fStrafeLoss][iStrafe] = SQL_FetchFloatByName(hndl, "loss"); | |
g_LJTop[iTable][iEntry][m_nStrafeTicks][iStrafe] = SQL_FetchIntByName(hndl, "ticks"); | |
g_LJTop[iTable][iEntry][m_fStrafeSync][iStrafe] = SQL_FetchFloatByName(hndl, "sync"); | |
} | |
} | |
DB_SaveLJTop() | |
{ | |
SQL_TQuery(g_DB, DB_EmptyCallback, SQL_DeleteLJTop); | |
SQL_TQuery(g_DB, DB_EmptyCallback, SQL_DeleteLJTopStrafes); | |
new Handle:hTxn = SQL_CreateTransaction(); | |
decl String:sQuery[1024]; | |
for(new LJTOP_TABLE:i; i < LT_END; i++) | |
{ | |
for (new j = 0; j < LJTOP_NUM_ENTRIES; j++) | |
{ | |
if (g_LJTop[i][j][m_strSteamID][0] == 0) | |
continue; | |
decl String:EscapedName[512]; | |
if (!SQL_EscapeString(g_DB, g_LJTop[i][j][m_strName], EscapedName, sizeof(EscapedName))) | |
{ | |
LogError("Failed to escape %s's name when writing ljs to database! Writing name without quotes instead", g_LJTop[i][j][m_strName]); | |
strcopy(EscapedName, sizeof(EscapedName), g_LJTop[i][j][m_strName]); | |
new index = 0; | |
while ((index = StrContains(EscapedName, "'")) != -1) | |
{ | |
strcopy(EscapedName[index], sizeof(EscapedName) - index, EscapedName[index + 1]); | |
} | |
} | |
FormatEx(sQuery, sizeof(sQuery), "INSERT INTO ljtop (ljtable, name, steamid, distance, prestrafe, strafes, sync, maxspeed, totalticks, syncedangle, totalangle, heightdelta, blockdistance, trajectory, timestamp) VALUES ('%s', '%s', '%s', %f, %f, %d, %f, %f, %d, %f, %f, %f, %f, %f, %d)", | |
g_strLJTopTags[i], | |
EscapedName, | |
g_LJTop[i][j][m_strSteamID], | |
g_LJTop[i][j][m_fDistance], | |
g_LJTop[i][j][m_fPrestrafe], | |
g_LJTop[i][j][m_nStrafes], | |
g_LJTop[i][j][m_fSync], | |
g_LJTop[i][j][m_fMaxSpeed], | |
g_LJTop[i][j][m_nTotalTicks], | |
g_LJTop[i][j][m_fSyncedAngle], | |
g_LJTop[i][j][m_fTotalAngle], | |
g_LJTop[i][j][m_fHeightDelta], | |
g_LJTop[i][j][m_fBlockDistance], | |
g_LJTop[i][j][m_fTrajectory], | |
g_LJTop[i][j][m_nTimestamp]); | |
SQL_AddQuery(hTxn, sQuery); | |
for (new k = 0; k < g_LJTop[i][j][m_nStrafes]; k++) | |
{ | |
decl String:key[3]; | |
GetStrafeKey(key, g_LJTop[i][j][m_StrafeDir][k]); | |
FormatEx(sQuery, sizeof(sQuery), "INSERT INTO ljtopstrafes (ljtable, rank, strafenum, dir, gain, loss, ticks, sync) VALUES ('%s', %d, %d, '%s', %f, %f, %d, %f)", | |
g_strLJTopTags[i], | |
j, | |
k, | |
key, | |
g_LJTop[i][j][m_fStrafeGain][k], | |
g_LJTop[i][j][m_fStrafeLoss][k], | |
g_LJTop[i][j][m_nStrafeTicks][k], | |
g_LJTop[i][j][m_fStrafeSync][k]); | |
SQL_AddQuery(hTxn, sQuery); | |
} | |
} | |
} | |
SQL_ExecuteTransaction(g_DB, hTxn, SQLTxnSuccess:-1, DB_TxnFailure); | |
} | |
public DB_EmptyCallback(Handle:owner, Handle:hndl, String:error[], any:pack) | |
{ | |
if(hndl == INVALID_HANDLE) | |
{ | |
LogError(error); | |
} | |
} | |
public DB_TxnFailure(Handle:db, any:data, numQueries, const String:error[], failIndex, any:queryData[]) | |
{ | |
LogError("DB_SaveLJTop: Transaction failed: %s", error); | |
} | |
LJTOP_TABLE:GetLJTopTable(const String:table[]) | |
{ | |
for(new LJTOP_TABLE:j; j < LT_END; j++) | |
{ | |
if(!strcmp(g_strLJTopTags[j], table)) | |
{ | |
return j; | |
} | |
} | |
return LJTOP_TABLE:-1; | |
} | |
#define ON_CVAR_CHANGE_BOOL(%0,%1) else if(hCvar == %0) { %1 = bool:StringToInt(strNewValue); } | |
#define ON_CVAR_CHANGE_INT(%0,%1) else if(hCvar == %0) { %1 = StringToInt(strNewValue); } | |
#define ON_CVAR_CHANGE_FLOAT(%0,%1) else if(hCvar == %0) { %1 = StringToFloat(strNewValue); } | |
public OnCvarChange(Handle:hCvar, const String:strOldValue[], const String:strNewValue[]) | |
{ | |
if(hCvar == g_hCvarColorMin) | |
{ | |
new nColor = StringToInt(strNewValue, 16); | |
g_ColorMin[0] = (nColor & 0xFF0000) >> 16; | |
g_ColorMin[1] = (nColor & 0xFF00) >> 8; | |
g_ColorMin[2] = nColor & 0xFF; | |
} | |
else if(hCvar == g_hCvarColorMax) | |
{ | |
new nColor = StringToInt(strNewValue, 16); | |
g_ColorMax[0] = (nColor & 0xFF0000) >> 16; | |
g_ColorMax[1] = (nColor & 0xFF00) >> 8; | |
g_ColorMax[2] = nColor & 0xFF; | |
} | |
ON_CVAR_CHANGE_FLOAT(g_hCvarLJMin, g_fLJMin) | |
ON_CVAR_CHANGE_FLOAT(g_hCvarLJMax, g_fLJMax) | |
ON_CVAR_CHANGE_FLOAT(g_hCvarLJMaxPrestrafe, g_fLJMaxPrestrafe) | |
ON_CVAR_CHANGE_BOOL(g_hCvarLJScoutStats, g_bLJScoutStats) | |
ON_CVAR_CHANGE_FLOAT(g_hCvarLJNoDuckMin, g_fLJNoDuckMin) | |
ON_CVAR_CHANGE_FLOAT(g_hCvarLJClientMin, g_fLJClientMin) | |
ON_CVAR_CHANGE_FLOAT(g_hCvarWJMin, g_fWJMin) | |
ON_CVAR_CHANGE_FLOAT(g_hCvarWJDropMax, g_fWJDropMax) | |
ON_CVAR_CHANGE_FLOAT(g_hCvarBJMin, g_fBJMin) | |
ON_CVAR_CHANGE_FLOAT(g_hCvarLAJMin, g_fLAJMin) | |
ON_CVAR_CHANGE_INT(g_hCvarVerbosity, g_nVerbosity) | |
ON_CVAR_CHANGE_BOOL(g_hCvarPrintFailedBlockStats, g_bPrintFailedBlockStats) | |
ON_CVAR_CHANGE_BOOL(g_hCvarShowBhopStats, g_bShowBhopStats) | |
ON_CVAR_CHANGE_BOOL(g_hCvarOutput16Style, g_bOutput16Style) | |
ON_CVAR_CHANGE_BOOL(g_hCvarLJTopAllowEasyBJ, g_bLJTopAllowEasyBJ) | |
ON_CVAR_CHANGE_BOOL(g_hCvarLJSound, g_bLJSound) | |
ON_CVAR_CHANGE_FLOAT(g_hCvarLJSound1, g_fLJSound[0]) | |
ON_CVAR_CHANGE_FLOAT(g_hCvarLJSound2, g_fLJSound[1]) | |
ON_CVAR_CHANGE_FLOAT(g_hCvarLJSound3, g_fLJSound[2]) | |
ON_CVAR_CHANGE_FLOAT(g_hCvarLJSound4, g_fLJSound[3]) | |
ON_CVAR_CHANGE_FLOAT(g_hCvarLJSound5, g_fLJSound[4]) | |
ON_CVAR_CHANGE_BOOL(g_hCvarLJSoundToAll[0], g_bLJSoundToAll[0]) | |
ON_CVAR_CHANGE_BOOL(g_hCvarLJSoundToAll[1], g_bLJSoundToAll[1]) | |
ON_CVAR_CHANGE_BOOL(g_hCvarLJSoundToAll[2], g_bLJSoundToAll[2]) | |
ON_CVAR_CHANGE_BOOL(g_hCvarLJSoundToAll[3], g_bLJSoundToAll[3]) | |
ON_CVAR_CHANGE_BOOL(g_hCvarLJSoundToAll[4], g_bLJSoundToAll[4]) | |
else if(hCvar == g_hCvarLJSound1File) | |
{ | |
strcopy(g_strLJSoundFile[0], sizeof(g_strLJSoundFile[]), strNewValue); | |
PrecacheSound(g_strLJSoundFile[0]); | |
} | |
else if(hCvar == g_hCvarLJSound2File) | |
{ | |
strcopy(g_strLJSoundFile[1], sizeof(g_strLJSoundFile[]), strNewValue); | |
PrecacheSound(g_strLJSoundFile[1]); | |
} | |
else if(hCvar == g_hCvarLJSound3File) | |
{ | |
strcopy(g_strLJSoundFile[2], sizeof(g_strLJSoundFile[]), strNewValue); | |
PrecacheSound(g_strLJSoundFile[2]); | |
} | |
else if(hCvar == g_hCvarLJSound4File) | |
{ | |
strcopy(g_strLJSoundFile[3], sizeof(g_strLJSoundFile[]), strNewValue); | |
PrecacheSound(g_strLJSoundFile[3]); | |
} | |
else if(hCvar == g_hCvarLJSound5File) | |
{ | |
strcopy(g_strLJSoundFile[4], sizeof(g_strLJSoundFile[]), strNewValue); | |
PrecacheSound(g_strLJSoundFile[4]); | |
} | |
ON_CVAR_CHANGE_FLOAT(g_hCvarMaxspeed, g_fMaxspeed) | |
ON_CVAR_CHANGE_BOOL(g_hCvarEnableBunnyHopping, g_bEnableBunnyHopping) | |
} | |
#undef ON_CVAR_CHANGE_BOOL | |
#undef ON_CVAR_CHANGE_INT | |
#undef ON_CVAR_CHANGE_FLOAT | |
public OnMapStart() | |
{ | |
g_BeamModel = PrecacheModel("materials/sprites/bluelaser1.vmt"); | |
for(new i; i < LJSOUND_NUM; i++) | |
{ | |
if(g_strLJSoundFile[i][0] != 0) | |
{ | |
PrecacheSound(g_strLJSoundFile[i]); | |
} | |
} | |
} | |
public OnClientPutInServer(client) | |
{ | |
/* | |
#if defined LJSERV | |
g_PlayerStates[client][bLJEnabled] = true; | |
#else | |
g_PlayerStates[client][bLJEnabled] = false; | |
#endif | |
g_PlayerStates[client][bHidePanel] = false; | |
g_PlayerStates[client][bBeam] = false; | |
g_PlayerStates[client][bSound] = true; | |
//#if defined LJSERV | |
//g_PlayerStates[client][bBlockMode] = true; | |
//#else | |
g_PlayerStates[client][bBlockMode] = false; | |
//#endif | |
g_PlayerStates[client][nVerbosity] = g_nVerbosity; | |
*/ | |
g_PlayerStates[client][bOnGround] = true; | |
g_PlayerStates[client][fBlockDistance] = -1.0; | |
g_PlayerStates[client][IllegalJumpFlags] = IJF_NONE; | |
g_PlayerStates[client][nSpectators] = 0; | |
g_PlayerStates[client][nSpectatorTarget] = -1; | |
/* | |
#if defined LJSERV | |
g_PlayerStates[client][bShowPrestrafeHint] = true; | |
#endif | |
*/ | |
SDKHook(client, SDKHook_Touch, hkTouch); | |
} | |
public Action:hkTouch(client, other) | |
{ | |
new Float:vOrigin[3]; | |
GetClientAbsOrigin(client, vOrigin); | |
if(other == 0 && !(GetEntityFlags(client) & FL_ONGROUND) && | |
!(g_PlayerStates[client][bBlockMode] && g_PlayerStates[client][bFailedBlock] && | |
vOrigin[2] - g_PlayerStates[client][vJumpOrigin][2] < HEIGHT_DELTA_MIN(JT_LONGJUMP))) | |
{ | |
g_PlayerStates[client][IllegalJumpFlags] |= IJF_WORLD; | |
#if defined DEBUG | |
PrintToChat(client, "%d, %d, %f, %d, %d", g_PlayerStates[client][bBlockMode], g_PlayerStates[client][bFailedBlock], vOrigin[2] - g_PlayerStates[client][vJumpOrigin][2], | |
vOrigin[2] - g_PlayerStates[client][vJumpOrigin][2] < HEIGHT_DELTA_MIN(JT_LONGJUMP), GetGameTickCount()); | |
#endif | |
} | |
else | |
{ | |
decl String:strClassname[64]; | |
GetEdictClassname(other, strClassname, sizeof(strClassname)); | |
if(!strcmp(strClassname, "trigger_push")) | |
{ | |
g_PlayerStates[client][IllegalJumpFlags] |= IJF_BOOSTER; | |
#if defined DEBUG | |
PrintToChat(client, "booster"); | |
#endif | |
} | |
} | |
} | |
public Action:Command_Delete(client, args) | |
{ | |
SQL_Query(g_DB, "drop table ljtop"); | |
SQL_Query(g_DB, "drop table ljtopstrafes"); | |
return Plugin_Handled; | |
} | |
public Action:Command_LJHelp(client, args) | |
{ | |
new Handle:hHelpPanel = CreatePanel(); | |
SetPanelTitle(hHelpPanel, "!lj"); | |
DrawPanelText(hHelpPanel, "!ljsettings, !ljs"); | |
DrawPanelText(hHelpPanel, "!ljpanel"); | |
DrawPanelText(hHelpPanel, "!ljbeam"); | |
DrawPanelText(hHelpPanel, "!ljblock"); | |
DrawPanelText(hHelpPanel, "!ljsound"); | |
DrawPanelText(hHelpPanel, "!gap"); | |
DrawPanelText(hHelpPanel, "!blockgap"); | |
DrawPanelText(hHelpPanel, "!ljtop"); | |
DrawPanelText(hHelpPanel, "!ljtopdelete (requires ADMFLAG_RCON)"); | |
DrawPanelText(hHelpPanel, "!ljversion, !ljver"); | |
DrawPanelText(hHelpPanel, " "); | |
DrawPanelText(hHelpPanel, "ex. !lj, /lj, or sm_lj"); | |
SendPanelToClient(hHelpPanel, client, EmptyPanelHandler, 10); | |
CloseHandle(hHelpPanel); | |
return Plugin_Handled; | |
} | |
public Action:Command_LJ(client, args) | |
{ | |
g_PlayerStates[client][bLJEnabled] = !g_PlayerStates[client][bLJEnabled]; | |
SetCookie(client, g_hCookieLJEnabled, g_PlayerStates[client][bLJEnabled]); | |
PrintToChat(client, "Longjump stats %s", g_PlayerStates[client][bLJEnabled] ? "ENABLED" : "DISABLED"); | |
return Plugin_Handled; | |
} | |
public Action:Command_LJSettings(client, args) | |
{ | |
ShowSettingsPanel(client); | |
return Plugin_Handled; | |
} | |
public OnClientCookiesCached(client) | |
{ | |
decl String:strCookie[64]; | |
GetClientCookie(client, g_hCookieDefaultsSet, strCookie, sizeof(strCookie)); | |
if(StringToInt(strCookie) == 0) | |
{ | |
#if defined LJSERV | |
SetCookie(client, g_hCookieLJEnabled, true); | |
SetCookie(client, g_hCookieBlockMode, true); | |
#endif | |
SetCookie(client, g_hCookieSound, g_bLJSound); | |
SetCookie(client, g_hCookieShowBhopStats, g_bShowBhopStats); | |
SetCookie(client, g_hCookieVerbosity, g_nVerbosity); | |
SetCookie(client, g_hCookieShowAllJumps, false); | |
#if defined LJSERV | |
SetCookie(client, g_hCookieShowPrestrafeHint, true); | |
#endif | |
SetCookie(client, g_hCookieDefaultsSet, true); | |
} | |
GetClientCookie(client, g_hCookieLJEnabled, strCookie, sizeof(strCookie)); | |
g_PlayerStates[client][bLJEnabled] = bool:StringToInt(strCookie); | |
GetClientCookie(client, g_hCookieBlockMode, strCookie, sizeof(strCookie)); | |
g_PlayerStates[client][bBlockMode] = bool:StringToInt(strCookie); | |
GetClientCookie(client, g_hCookieBeam, strCookie, sizeof(strCookie)); | |
g_PlayerStates[client][bBeam] = bool:StringToInt(strCookie); | |
GetClientCookie(client, g_hCookieSound, strCookie, sizeof(strCookie)); | |
g_PlayerStates[client][bSound] = bool:StringToInt(strCookie); | |
GetClientCookie(client, g_hCookieHidePanel, strCookie, sizeof(strCookie)); | |
g_PlayerStates[client][bHidePanel] = bool:StringToInt(strCookie); | |
GetClientCookie(client, g_hCookieHideBhopPanel, strCookie, sizeof(strCookie)); | |
g_PlayerStates[client][bHideBhopPanel] = bool:StringToInt(strCookie); | |
GetClientCookie(client, g_hCookieShowBhopStats, strCookie, sizeof(strCookie)); | |
g_PlayerStates[client][bShowBhopStats] = bool:StringToInt(strCookie); | |
GetClientCookie(client, g_hCookieVerbosity, strCookie, sizeof(strCookie)); | |
g_PlayerStates[client][nVerbosity] = StringToInt(strCookie); | |
GetClientCookie(client, g_hCookieShowAllJumps, strCookie, sizeof(strCookie)); | |
g_PlayerStates[client][bShowAllJumps] = bool:StringToInt(strCookie); | |
#if defined LJSERV | |
GetClientCookie(client, g_hCookieShowPrestrafeHint, strCookie, sizeof(strCookie)); | |
g_PlayerStates[client][bShowPrestrafeHint] = bool:StringToInt(strCookie); | |
#endif | |
GetClientCookie(client, g_hCookiePersonalBest, strCookie, sizeof(strCookie)); | |
g_PlayerStates[client][fPersonalBest] = StringToFloat(strCookie); | |
} | |
ShowSettingsPanel(client) | |
{ | |
new Handle:hMenu = CreateMenu(SettingsMenuHandler); | |
decl String:buf[64]; | |
Format(buf, sizeof(buf), "LJ stats: %s", g_PlayerStates[client][bLJEnabled] ? "On" : "Off"); | |
AddMenuItem(hMenu, "ljenabled", buf); | |
Format(buf, sizeof(buf), "Block mode: %s", g_PlayerStates[client][bBlockMode] ? "On" : "Off"); | |
AddMenuItem(hMenu, "block", buf); | |
Format(buf, sizeof(buf), "Beam: %s", g_PlayerStates[client][bBeam] ? "On" : "Off"); | |
AddMenuItem(hMenu, "beam", buf); | |
Format(buf, sizeof(buf), "Sounds: %s", g_PlayerStates[client][bSound] ? "On" : "Off"); | |
AddMenuItem(hMenu, "sound", buf); | |
Format(buf, sizeof(buf), "Panel: %s", !g_PlayerStates[client][bHidePanel] ? "On" : "Off"); | |
AddMenuItem(hMenu, "panel", buf); | |
Format(buf, sizeof(buf), "Bhop panel: %s", !g_PlayerStates[client][bHideBhopPanel] ? "On" : "Off"); | |
AddMenuItem(hMenu, "bhoppanel", buf); | |
Format(buf, sizeof(buf), "Bhop stats: %s", g_PlayerStates[client][bShowBhopStats] ? "On" : "Off"); | |
AddMenuItem(hMenu, "bhopstats", buf); | |
Format(buf, sizeof(buf), "Verbosity: %d", g_PlayerStates[client][nVerbosity]); | |
AddMenuItem(hMenu, "verbosity", buf); | |
Format(buf, sizeof(buf), "Show all jumps: %s", g_PlayerStates[client][bShowAllJumps] ? "On" : "Off"); | |
AddMenuItem(hMenu, "showalljumps", buf); | |
#if defined LJSERV | |
Format(buf, sizeof(buf), "Prestrafe hint: %s", g_PlayerStates[client][bShowPrestrafeHint] ? "On" : "Off"); | |
AddMenuItem(hMenu, "prestrafehint", buf); | |
#endif | |
DisplayMenu(hMenu, client, 0); | |
} | |
public SettingsMenuHandler(Handle:hMenu, MenuAction:ma, client, nItem) | |
{ | |
switch(ma) | |
{ | |
case MenuAction_Select: | |
{ | |
decl String:strInfo[16]; | |
if(!GetMenuItem(hMenu, nItem, strInfo, sizeof(strInfo))) | |
{ | |
LogError("rip menu..."); | |
return; | |
} | |
if(!strcmp(strInfo, "ljenabled")) | |
{ | |
g_PlayerStates[client][bLJEnabled] = !g_PlayerStates[client][bLJEnabled]; | |
SetCookie(client, g_hCookieLJEnabled, g_PlayerStates[client][bLJEnabled]); | |
PrintToChat(client, "LJ stats are now %s", g_PlayerStates[client][bLJEnabled] ? "on" : "off"); | |
ShowSettingsPanel(client); | |
} | |
else if(!strcmp(strInfo, "block")) | |
{ | |
g_PlayerStates[client][bBlockMode] = !g_PlayerStates[client][bBlockMode]; | |
SetCookie(client, g_hCookieBlockMode, g_PlayerStates[client][bBlockMode]); | |
PrintToChat(client, "Block mode is now %s", g_PlayerStates[client][bBlockMode] ? "on" : "off"); | |
ShowSettingsPanel(client); | |
} | |
else if(!strcmp(strInfo, "beam")) | |
{ | |
g_PlayerStates[client][bBeam] = !g_PlayerStates[client][bBeam]; | |
SetCookie(client, g_hCookieBeam, g_PlayerStates[client][bBeam]); | |
PrintToChat(client, "Beam is now %s", g_PlayerStates[client][bBeam] ? "on" : "off"); | |
ShowSettingsPanel(client); | |
} | |
else if(!strcmp(strInfo, "sound")) | |
{ | |
g_PlayerStates[client][bSound] = !g_PlayerStates[client][bSound]; | |
SetCookie(client, g_hCookieSound, g_PlayerStates[client][bSound]); | |
PrintToChat(client, "Sound is now %s", g_PlayerStates[client][bSound] ? "on" : "off"); | |
ShowSettingsPanel(client); | |
} | |
else if(!strcmp(strInfo, "panel")) | |
{ | |
g_PlayerStates[client][bHidePanel] = !g_PlayerStates[client][bHidePanel]; | |
SetCookie(client, g_hCookieHidePanel, g_PlayerStates[client][bHidePanel]); | |
PrintToChat(client, "Panel is now %s", g_PlayerStates[client][bHidePanel] ? "hidden" : "visible"); | |
ShowSettingsPanel(client); | |
} | |
else if(!strcmp(strInfo, "bhoppanel")) | |
{ | |
g_PlayerStates[client][bHideBhopPanel] = !g_PlayerStates[client][bHideBhopPanel]; | |
SetCookie(client, g_hCookieHideBhopPanel, g_PlayerStates[client][bHideBhopPanel]); | |
PrintToChat(client, "Bhop panel is now %s", g_PlayerStates[client][bHideBhopPanel] ? "hidden" : "visible"); | |
ShowSettingsPanel(client); | |
} | |
else if(!strcmp(strInfo, "bhopstats")) | |
{ | |
g_PlayerStates[client][bShowBhopStats] = !g_PlayerStates[client][bShowBhopStats]; | |
SetCookie(client, g_hCookieShowBhopStats, g_PlayerStates[client][bShowBhopStats]); | |
PrintToChat(client, "Bhop stats are now %s", g_PlayerStates[client][bShowBhopStats] ? "on" : "off"); | |
ShowSettingsPanel(client); | |
} | |
else if(!strcmp(strInfo, "verbosity")) | |
{ | |
hMenu = CreateMenu(VerbosityMenuHandler); | |
AddMenuItem(hMenu, "0", "0"); | |
AddMenuItem(hMenu, "1", "1"); | |
AddMenuItem(hMenu, "2", "2"); | |
AddMenuItem(hMenu, "3", "3"); | |
DisplayMenu(hMenu, client, 0); | |
} | |
else if(!strcmp(strInfo, "showalljumps")) | |
{ | |
g_PlayerStates[client][bShowAllJumps] = !g_PlayerStates[client][bShowAllJumps]; | |
SetCookie(client, g_hCookieShowAllJumps, g_PlayerStates[client][bShowAllJumps]); | |
PrintToChat(client, "Showing all jumps is now %s", g_PlayerStates[client][bShowAllJumps] ? "on" : "off"); | |
ShowSettingsPanel(client); | |
} | |
#if defined LJSERV | |
else if(!strcmp(strInfo, "prestrafehint")) | |
{ | |
g_PlayerStates[client][bShowPrestrafeHint] = !g_PlayerStates[client][bShowPrestrafeHint]; | |
SetCookie(client, g_hCookieShowPrestrafeHint, g_PlayerStates[client][bShowPrestrafeHint]); | |
PrintToChat(client, "Prestrafe hint is now %s", g_PlayerStates[client][bShowPrestrafeHint] ? "on" : "off"); | |
ShowSettingsPanel(client); | |
} | |
#endif | |
} | |
case MenuAction_End: | |
{ | |
CloseHandle(hMenu); | |
} | |
} | |
} | |
SetCookie(client, Handle:hCookie, n) | |
{ | |
decl String:strCookie[64]; | |
IntToString(n, strCookie, sizeof(strCookie)); | |
SetClientCookie(client, hCookie, strCookie); | |
} | |
SetCookieFloat(client, Handle:hCookie, Float:n) | |
{ | |
decl String:strCookie[64]; | |
FloatToString(n, strCookie, sizeof(strCookie)); | |
SetClientCookie(client, hCookie, strCookie); | |
} | |
public VerbosityMenuHandler(Handle:hMenu, MenuAction:ma, client, nItem) | |
{ | |
switch(ma) | |
{ | |
case MenuAction_Select: | |
{ | |
g_PlayerStates[client][nVerbosity] = nItem; | |
SetCookie(client, g_hCookieVerbosity, g_PlayerStates[client][nVerbosity]); | |
PrintToChat(client, "Verbosity level is now %d", g_PlayerStates[client][nVerbosity]); | |
ShowSettingsPanel(client); | |
} | |
case MenuAction_End: | |
{ | |
CloseHandle(hMenu); | |
} | |
} | |
} | |
public Action:Command_LJPanel(client, args) | |
{ | |
g_PlayerStates[client][bHidePanel] = !g_PlayerStates[client][bHidePanel]; | |
SetCookie(client, g_hCookieHidePanel, g_PlayerStates[client][bHidePanel]); | |
PrintToChat(client, "Longjump panel %s", g_PlayerStates[client][bHidePanel] ? "ENABLED" : "DISABLED"); | |
return Plugin_Handled; | |
} | |
public Action:Command_LJBeam(client, args) | |
{ | |
g_PlayerStates[client][bBeam] = !g_PlayerStates[client][bBeam]; | |
SetCookie(client, g_hCookieBeam, g_PlayerStates[client][bBeam]); | |
PrintToChat(client, "Longjump beam %s", g_PlayerStates[client][bBeam] ? "ENABLED" : "DISABLED"); | |
return Plugin_Handled; | |
} | |
public Action:Command_LJBlock(client, args) | |
{ | |
g_PlayerStates[client][bBlockMode] = !g_PlayerStates[client][bBlockMode]; | |
SetCookie(client, g_hCookieBlockMode, g_PlayerStates[client][bBlockMode]); | |
PrintToChat(client, "Longjump block mode %s", g_PlayerStates[client][bBlockMode] ? "ENABLED" : "DISABLED"); | |
return Plugin_Handled; | |
} | |
public Action:Command_LJSound(client, args) | |
{ | |
g_PlayerStates[client][bSound] = !g_PlayerStates[client][bSound]; | |
SetCookie(client, g_hCookieSound, g_PlayerStates[client][bSound]); | |
PrintToChat(client, "Longjump sounds %s", g_PlayerStates[client][bSound] ? "enabled" : "disabled"); | |
return Plugin_Handled; | |
} | |
public Action:Command_LJVersion(client, args) | |
{ | |
CPrintToChat(client, "{green}ljstats %s by Miu -w-", LJSTATS_VERSION); | |
return Plugin_Handled; | |
} | |
public Action:Command_LJTop(client, args) | |
{ | |
//SendPanelToClient(g_hLJTopLJPanel, client, EmptyPanelHandler, 10); | |
DisplayMenu(g_hLJTopMainMenu, client, MENU_TIME_FOREVER); | |
return Plugin_Handled; | |
} | |
public Action:Command_LJTopDelete(client, args) | |
{ | |
decl String:buf[32]; | |
GetCmdArg(1, buf, sizeof(buf)); | |
new LJTOP_TABLE:nLJTopTable = LJTOP_TABLE:-1; | |
for(new LJTOP_TABLE:i; i < LT_END; i++) | |
{ | |
if(!strcmp(g_strLJTopTags[i], buf)) | |
{ | |
nLJTopTable = i; | |
break; | |
} | |
} | |
if(nLJTopTable == LJTOP_TABLE:-1) | |
{ | |
PrintToChat(client, "Unrecognized table %s", buf); | |
return Plugin_Handled; | |
} | |
decl String:str[4]; | |
GetCmdArg(2, str, sizeof(str)); | |
new n = StringToInt(str) - 1; | |
if(n < 0 || n > LJTOP_NUM_ENTRIES - 1) | |
{ | |
PrintToChat(client, "Invalid entry"); | |
return Plugin_Handled; | |
} | |
PrintToChat(client, "Removing %s's %.2f in table %d (%s)", g_LJTop[nLJTopTable][n][m_strName], g_LJTop[nLJTopTable][n][m_fDistance], nLJTopTable, buf); | |
LJTopMoveUp(nLJTopTable, n); | |
LJTopSave(); | |
LJTopCreateMenu(nLJTopTable); | |
return Plugin_Handled; | |
} | |
public Action:Command_Gap(client, args) | |
{ | |
new Handle:hGapPanel = CreatePanel(); | |
SetPanelTitle(hGapPanel, "Select point 1"); | |
SendPanelToClient(hGapPanel, client, EmptyPanelHandler, 10); | |
CloseHandle(hGapPanel); | |
g_PlayerStates[client][GapSelectionMode] = GSM_GAP; | |
return Plugin_Handled; | |
} | |
public Action:Command_BlockGap(client, args) | |
{ | |
new Handle:hGapPanel = CreatePanel(); | |
SetPanelTitle(hGapPanel, "Select block"); | |
SendPanelToClient(hGapPanel, client, EmptyPanelHandler, 10); | |
CloseHandle(hGapPanel); | |
g_PlayerStates[client][GapSelectionMode] = GSM_BLOCKGAP; | |
return Plugin_Handled; | |
} | |
GapSelect(client, buttons) | |
{ | |
if(!(buttons & IN_ATTACK || buttons & IN_ATTACK2 || buttons & IN_USE) || | |
g_PlayerStates[client][LastButtons] & IN_ATTACK || g_PlayerStates[client][LastButtons] & IN_ATTACK2 || g_PlayerStates[client][LastButtons] & IN_USE) | |
{ | |
return; | |
} | |
new Float:vPoint[3], Float:vNormal[3]; | |
GetGapPoint(vPoint, vNormal, client); | |
switch(g_PlayerStates[client][GapSelectionMode]) | |
{ | |
case GSM_GAP: | |
{ | |
Array_Copy(vPoint, g_PlayerStates[client][vGapPoint1], 3); | |
SendPanelMsg(client, "Select point 2"); | |
g_PlayerStates[client][GapSelectionMode] = GSM_GAPSECOND; | |
} | |
case GSM_GAPSECOND: | |
{ | |
new Float:vPoint1[3]; | |
Array_Copy(g_PlayerStates[client][vGapPoint1], vPoint1, 3); | |
new Float:xy = Pow(Pow(vPoint[0] - vPoint1[0], 2.0) + Pow(vPoint[1] - vPoint1[1], 2.0), 0.5); | |
SendPanelMsg(client, "distance: %.2f, xy: %.2f, z: %.2f", GetVectorDistance(vPoint, vPoint1), xy, vPoint1[2] - vPoint[2]); | |
CreateBeamClient(client, vPoint, vPoint1, 0, 0, 128, 5.0); | |
g_PlayerStates[client][GapSelectionMode] = GSM_NONE; | |
} | |
case GSM_BLOCKGAP: | |
{ | |
new Float:vBlockEnd[3], Float:vOrigin[3]; | |
GetClientAbsOrigin(client, vOrigin); | |
GetOppositePoint(vBlockEnd, vPoint, vNormal); | |
SendPanelMsg(client, "block: %.2f", GetVectorDistance(vPoint, vBlockEnd)); | |
CreateBeamClient(client, vPoint, vBlockEnd, 255, 0, 0, 5.0); | |
g_PlayerStates[client][GapSelectionMode] = GSM_NONE; | |
} | |
} | |
} | |
public Action:Command_Tele(client, args) | |
{ | |
g_PlayerStates[client][IllegalJumpFlags] |= IJF_TELEPORT; | |
return Plugin_Continue; | |
} | |
public Action:Command_PersonalBest(client, args) | |
{ | |
CPrintToChat(client, "{green}Your longjump record is {default}%.2f{green} units", g_PlayerStates[client][fPersonalBest]); | |
return Plugin_Handled; | |
} | |
UpdatePersonalBest(client) | |
{ | |
if(g_PlayerStates[client][JumpType] != JT_LONGJUMP) | |
{ | |
return; | |
} | |
if(g_PlayerStates[client][fJumpDistance] > g_PlayerStates[client][fPersonalBest]) | |
{ | |
g_PlayerStates[client][fPersonalBest] = g_PlayerStates[client][fJumpDistance]; | |
CPrintToChat(client, "{green}Congratulations, you have a new longjump record with {default}%.2f{green} units!", g_PlayerStates[client][fPersonalBest]); | |
SetCookieFloat(client, g_hCookiePersonalBest, g_PlayerStates[client][fPersonalBest]); | |
} | |
} | |
public Action:Command_ResetPersonalBest(client, args) | |
{ | |
SetCookieFloat(client, g_hCookiePersonalBest, 0.0); | |
g_PlayerStates[client][fPersonalBest] = 0.0; | |
return Plugin_Continue; | |
} | |
public Action:Command_LJTopLoadFromFile(client, args) | |
{ | |
decl String:arg[PLATFORM_MAX_PATH]; | |
GetCmdArgString(arg, sizeof(arg)); | |
LJTopLoad(arg); | |
return Plugin_Handled; | |
} | |
LJTopCreateMainMenu() | |
{ | |
if(g_hLJTopMainMenu != INVALID_HANDLE) | |
{ | |
CloseHandle(g_hLJTopMainMenu); | |
} | |
g_hLJTopMainMenu = CreateMenu(LJTopMainMenuHandler); | |
for(new LJTOP_TABLE:i; i < LT_END; i++) | |
{ | |
AddMenuItem(g_hLJTopMainMenu, g_strLJTopTags[i], g_strLJTopTableName[i]); | |
} | |
} | |
public LJTopMainMenuHandler(Handle:hMenu, MenuAction:ma, client, nItem) | |
{ | |
switch(ma) | |
{ | |
case MenuAction_Select: | |
{ | |
decl String:strInfo[16]; | |
if(!GetMenuItem(hMenu, nItem, strInfo, sizeof(strInfo))) | |
{ | |
PrintToChat(client, "rip menu..."); | |
return; | |
} | |
for(new LJTOP_TABLE:i = LJTOP_TABLE:0; i < LT_END; i++) | |
{ | |
if(!strcmp(g_strLJTopTags[i], strInfo)) | |
{ | |
DisplayMenu(g_hLJTopMenus[i], client, 0); | |
break; | |
} | |
} | |
} | |
case MenuAction_End: | |
{ | |
} | |
} | |
} | |
LJTopCreateMenu(LJTOP_TABLE:nTable) | |
{ | |
if(g_hLJTopMenus[nTable] != INVALID_HANDLE) | |
{ | |
CloseHandle(g_hLJTopMenus[nTable]); | |
} | |
g_hLJTopMenus[nTable] = CreateMenu(LJTopRecordMenuHandler); | |
decl String:buf[128], String:info[32]; | |
Format(buf, sizeof(buf), "%s top", g_strLJTopTableName[nTable]); | |
SetMenuTitle(g_hLJTopMenus[nTable], buf); | |
for(new i; i < LJTOP_NUM_ENTRIES; i++) | |
{ | |
if(g_LJTop[nTable][i][m_strName][0] == 0) | |
{ | |
break; | |
} | |
if(nTable == LT_BLOCKLJ) | |
{ | |
FormatEx(buf, sizeof(buf), "%s - %.2f (%.2f: %.2f, %d @ %d%%, %.2f)", | |
g_LJTop[LT_BLOCKLJ][i][m_strName], g_LJTop[LT_BLOCKLJ][i][m_fBlockDistance], g_LJTop[LT_BLOCKLJ][i][m_fDistance], | |
g_LJTop[LT_BLOCKLJ][i][m_fPrestrafe], g_LJTop[LT_BLOCKLJ][i][m_nStrafes], RoundFloat(g_LJTop[LT_BLOCKLJ][i][m_fSync]), g_LJTop[LT_BLOCKLJ][i][m_fMaxSpeed]); | |
} | |
else | |
{ | |
FormatEx(buf, sizeof(buf), "%s - %.2f (%.2f, %d @ %d%%, %.2f)", | |
g_LJTop[nTable][i][m_strName], g_LJTop[nTable][i][m_fDistance], | |
g_LJTop[nTable][i][m_fPrestrafe], g_LJTop[nTable][i][m_nStrafes], RoundFloat(g_LJTop[nTable][i][m_fSync]), g_LJTop[nTable][i][m_fMaxSpeed]); | |
} | |
FormatEx(info, sizeof(info), "%s;%d", g_strLJTopTags[nTable], i); | |
AddMenuItem(g_hLJTopMenus[nTable], info, buf); | |
} | |
//SetMenuExitBackButton(g_hLJTopMenus[nTable], true); | |
} | |
public LJTopRecordMenuHandler(Handle:hMenu, MenuAction:ma, client, nItem) | |
{ | |
switch(ma) | |
{ | |
case MenuAction_Select: | |
{ | |
decl String:info[16]; | |
if(!GetMenuItem(hMenu, nItem, info, sizeof(info))) | |
{ | |
LogError("rip menu..."); | |
return; | |
} | |
decl String:split[2][16], String:sTime[128], String:buf[128]; | |
ExplodeString(info, ";", split, sizeof(split), sizeof(split[])); | |
new iTable = _:GetLJTopTable(split[0]); | |
new iEntry = StringToInt(split[1]); | |
if(iTable == -1) | |
{ | |
LogError("Unrecognized table %s"); | |
return; | |
} | |
new Handle:hPanel = CreatePanel(); | |
FormatTime(sTime, sizeof(sTime), NULL_STRING, g_LJTop[iTable][iEntry][m_nTimestamp]); // "%B %d %Y %T" | |
FormatEx(buf, sizeof(buf), "%s's %.2f -- %s\n ", g_LJTop[iTable][iEntry][m_strName], g_LJTop[iTable][iEntry][m_fDistance], sTime); | |
SetPanelTitle(hPanel, buf); | |
for(new i = 0; i < g_LJTop[iTable][iEntry][m_nStrafes] && i < 16; i++) | |
{ | |
decl String:strStrafeKey[3]; | |
GetStrafeKey(strStrafeKey, g_LJTop[iTable][iEntry][m_StrafeDir][i]); | |
DrawPanelTextF(hPanel, "%d %s %.2f %.2f %.2f %.2f", | |
i + 1, | |
strStrafeKey, | |
g_LJTop[iTable][iEntry][m_fStrafeGain][i], g_LJTop[iTable][iEntry][m_fStrafeLoss][i], | |
float(g_LJTop[iTable][iEntry][m_nStrafeTicks][i]) / g_LJTop[iTable][iEntry][m_nTotalTicks] * 100, | |
g_LJTop[iTable][iEntry][m_fStrafeSync][i]); | |
} | |
DrawPanelTextF(hPanel, " %.2f%%", g_LJTop[iTable][iEntry][m_fSync]); | |
SendPanelToClient(hPanel, client, RecordPanelHandler, 0); | |
CloseHandle(hPanel); | |
} | |
case MenuAction_Cancel: | |
{ | |
if(nItem == -3) | |
{ | |
DisplayMenu(g_hLJTopMainMenu, client, 0); | |
} | |
} | |
} | |
} | |
public RecordPanelHandler(Handle:hMenu, MenuAction:ma, client, nItem) | |
{ | |
switch(ma) | |
{ | |
case MenuAction_Select: | |
{ | |
DisplayMenu(g_hLJTopMainMenu, client, 0); | |
} | |
} | |
} | |
LJTopLoad(const String:strPath[]) | |
{ | |
// Load top stats into memory from file | |
/*decl String:strPath[PLATFORM_MAX_PATH]; | |
BuildPath(Path_SM, strPath, PLATFORM_MAX_PATH, LJTOP_DIR); | |
if(!DirExists(strPath)) | |
{ | |
PrintToServer("[LJTop] Dir %s nonexistent", strPath); | |
return; | |
} | |
StrCat(strPath, sizeof(strPath), LJTOP_FILE);*/ | |
new Handle:hFile = OpenFile(strPath, "r"); | |
if(hFile == INVALID_HANDLE) | |
{ | |
LogError("[LJTop] Error opening %s", strPath); | |
return; | |
} | |
decl String:strLine[1024]; // omg, so big it is??? | |
decl String:strBuffers[LJTOP_MAX_NUM_STATS][64]; | |
ReadFileLine(hFile, strLine, sizeof(strLine)); | |
new /*nVersion, */MinStats; | |
if(!strcmp(strLine, "1\n")) | |
{ | |
//nVersion = 1; | |
MinStats = LJTOP_MIN_NUM_STATS_1; | |
if(IsEndOfFile(hFile)) | |
{ | |
PrintToServer("[LJTop] EOF"); | |
return; | |
} | |
ReadFileLine(hFile, strLine, sizeof(strLine)); | |
} | |
else | |
{ | |
//nVersion = 0; | |
MinStats = LJTOP_MIN_NUM_STATS_0; | |
} | |
do | |
{ | |
#if defined DEBUG | |
PrintToServer("[LJTop] read %s", strLine); | |
#endif | |
static LJTOP_TABLE:nTable = LJTOP_TABLE:-1; | |
static nEntry = 0; | |
new bool:bTable; | |
for(new LJTOP_TABLE:j; j < LT_END; j++) | |
{ | |
decl String:strTag[16]; | |
Format(strTag, sizeof(strTag), "%s:\n", g_strLJTopTags[j]); | |
if(!strcmp(strLine, strTag)) | |
{ | |
PrintToServer("[LJTop] read tag: %s", g_strLJTopTags[j]); | |
nTable = j; | |
nEntry = 0; | |
bTable = true; | |
} | |
} | |
if(bTable) | |
{ | |
continue; | |
} | |
if(nTable == LJTOP_TABLE:-1) | |
{ | |
PrintToServer("[LJTop] no table for line %s", strLine); | |
continue; | |
} | |
if(nEntry >= LJTOP_NUM_ENTRIES) | |
{ | |
PrintToServer("[LJTop] Too many entries for table %d; ignoring line", nTable); | |
continue; | |
} | |
new nLength = ExplodeString(strLine, ";", strBuffers, LJTOP_MAX_NUM_STATS, sizeof(strBuffers[]), false); | |
if(nLength < MinStats) | |
{ | |
PrintToServer("[LJTop] Unexpected entry %d length (expected at least %d, got %d); ignoring line", nEntry + 1, MinStats, nLength); | |
continue; | |
} | |
new k; | |
strcopy(g_LJTop[nTable][nEntry][m_strName], 64, strBuffers[k++]); | |
strcopy(g_LJTop[nTable][nEntry][m_strSteamID], 32, strBuffers[k++]); | |
g_LJTop[nTable][nEntry][m_fDistance] = StringToFloat(strBuffers[k++]); | |
g_LJTop[nTable][nEntry][m_fPrestrafe] = StringToFloat(strBuffers[k++]); | |
g_LJTop[nTable][nEntry][m_nStrafes] = StringToInt(strBuffers[k++]); | |
g_LJTop[nTable][nEntry][m_fSync] = StringToFloat(strBuffers[k++]); | |
g_LJTop[nTable][nEntry][m_fMaxSpeed] = StringToFloat(strBuffers[k++]); | |
g_LJTop[nTable][nEntry][m_nTotalTicks] = StringToInt(strBuffers[k++]); | |
g_LJTop[nTable][nEntry][m_fHeightDelta] = StringToFloat(strBuffers[k++]); | |
g_LJTop[nTable][nEntry][m_fSyncedAngle] = StringToFloat(strBuffers[k++]); | |
g_LJTop[nTable][nEntry][m_fTotalAngle] = StringToFloat(strBuffers[k++]); | |
g_LJTop[nTable][nEntry][m_fHeightDelta] = StringToFloat(strBuffers[k++]); | |
g_LJTop[nTable][nEntry][m_fBlockDistance] = StringToFloat(strBuffers[k++]); | |
g_LJTop[nTable][nEntry][m_fTrajectory] = StringToFloat(strBuffers[k++]); | |
g_LJTop[nTable][nEntry][m_nTimestamp] = StringToInt(strBuffers[k++]); | |
for(new l; l < (nLength - MinStats) / 5 && l < LJTOP_MAX_STRAFES && k < 64 - 5; l++) | |
{ | |
g_LJTop[nTable][nEntry][m_StrafeDir][l] = GetStrafeDir(strBuffers[k++]); | |
g_LJTop[nTable][nEntry][m_fStrafeGain][l] = StringToFloat(strBuffers[k++]); | |
g_LJTop[nTable][nEntry][m_fStrafeLoss][l] = StringToFloat(strBuffers[k++]); | |
g_LJTop[nTable][nEntry][m_nStrafeTicks][l] = StringToInt(strBuffers[k++]); | |
g_LJTop[nTable][nEntry][m_fStrafeSync][l] = StringToFloat(strBuffers[k++]); | |
} | |
nEntry++; | |
#if defined DEBUG | |
PrintToServer("read %s %.2f in table %d", g_LJTop[nTable][nEntry][m_strName], g_LJTop[nTable][nEntry][m_fDistance], nTable); | |
#endif | |
} | |
while(!IsEndOfFile(hFile) && ReadFileLine(hFile, strLine, sizeof(strLine))); | |
CloseHandle(hFile); | |
} | |
LJTopSave() | |
{ | |
DB_SaveLJTop(); | |
} | |
/*LJTopSave() | |
{ | |
// Delete old file and write entirely new one | |
decl String:strPath[PLATFORM_MAX_PATH]; | |
BuildPath(Path_SM, strPath, PLATFORM_MAX_PATH, LJTOP_DIR); | |
if(!DirExists(strPath)) | |
{ | |
CreateDirectory(strPath, 0x3FF); // 0777 | |
} | |
StrCat(strPath, sizeof(strPath), LJTOP_FILE); | |
new Handle:hFile = OpenFile(strPath, "w"); // This will overwrite the old file | |
if(hFile == INVALID_HANDLE) | |
{ | |
PrintToServer("[LJTop] Error opening %s", strPath); | |
return; | |
} | |
WriteFileLine(hFile, "1", false); | |
decl String:buf[512], String:buf2[128]; | |
for(new nIndex; nIndex < _:LT_END; nIndex++) | |
{ | |
Format(buf, sizeof(buf), "%s:", g_strLJTopTags[nIndex]); | |
if(!WriteFileLine(hFile, buf, false)) | |
{ | |
PrintToServer("[LJTop] Error writing to %s", strPath); | |
PrintToChatAll("[LJTop] Error saving ljtop"); | |
} | |
for(new i; i < LJTOP_NUM_ENTRIES; i++) | |
{ | |
if(g_LJTop[nIndex][i][m_strSteamID][0] == 0) | |
{ | |
break; | |
} | |
Format(buf, sizeof(buf), "%s;%s;%f;%f;%d;%f;%f;%d;%f;%f;%f;%f;%f;%f;%d;", | |
g_LJTop[nIndex][i][m_strName], | |
g_LJTop[nIndex][i][m_strSteamID], | |
g_LJTop[nIndex][i][m_fDistance], | |
g_LJTop[nIndex][i][m_fPrestrafe], | |
g_LJTop[nIndex][i][m_nStrafes], | |
g_LJTop[nIndex][i][m_fSync], | |
g_LJTop[nIndex][i][m_fMaxSpeed], | |
g_LJTop[nIndex][i][m_nTotalTicks], | |
g_LJTop[nIndex][i][m_fHeightDelta], | |
g_LJTop[nIndex][i][m_fSyncedAngle], | |
g_LJTop[nIndex][i][m_fTotalAngle], | |
g_LJTop[nIndex][i][m_fHeightDelta], | |
g_LJTop[nIndex][i][m_fBlockDistance], | |
g_LJTop[nIndex][i][m_fTrajectory], | |
g_LJTop[nIndex][i][m_nTimestamp]); | |
for(new j; j < g_LJTop[nIndex][i][m_nStrafes] && j < LJTOP_MAX_STRAFES; j++) | |
{ | |
decl String:strStrafeKey[4]; | |
GetStrafeKey(strStrafeKey, g_LJTop[nIndex][i][m_StrafeDir][j]); | |
Format(buf2, sizeof(buf2), "%s;%f;%f;%d;%f;", | |
strStrafeKey, | |
g_LJTop[nIndex][i][m_fStrafeGain][j], | |
g_LJTop[nIndex][i][m_fStrafeLoss][j], | |
g_LJTop[nIndex][i][m_nStrafeTicks][j], | |
g_LJTop[nIndex][i][m_fStrafeSync][j]); | |
StrCat(buf, sizeof(buf), buf2); | |
} | |
if(!WriteFileLine(hFile, buf)) | |
{ | |
PrintToServer("[LJTop] Error writing to %s", strPath); | |
PrintToChatAll("[LJTop] Error saving ljtop"); | |
} | |
} | |
} | |
CloseHandle(hFile); | |
}*/ | |
GetStrafeKey(String:str[], STRAFE_DIRECTION:Dir) | |
{ | |
if(Dir == SD_W) | |
{ | |
strcopy(str, 3, "W"); | |
} | |
else if(Dir == SD_A) | |
{ | |
strcopy(str, 3, "A"); | |
} | |
else if(Dir == SD_S) | |
{ | |
strcopy(str, 3, "S"); | |
} | |
else if(Dir == SD_D) | |
{ | |
strcopy(str, 3, "D"); | |
} | |
else if(Dir == SD_WA) | |
{ | |
strcopy(str, 3, "WA"); | |
} | |
else if(Dir == SD_WD) | |
{ | |
strcopy(str, 3, "WD"); | |
} | |
else if(Dir == SD_SA) | |
{ | |
strcopy(str, 3, "SA"); | |
} | |
else if(Dir == SD_SD) | |
{ | |
strcopy(str, 3, "SD"); | |
} | |
} | |
STRAFE_DIRECTION:GetStrafeDir(String:str[]) | |
{ | |
if(!strcmp(str, "W")) | |
{ | |
return SD_W; | |
} | |
else if(!strcmp(str, "A")) | |
{ | |
return SD_A; | |
} | |
else if(!strcmp(str, "S")) | |
{ | |
return SD_S; | |
} | |
else if(!strcmp(str, "D")) | |
{ | |
return SD_D; | |
} | |
else if(!strcmp(str, "WA")) | |
{ | |
return SD_WA; | |
} | |
else if(!strcmp(str, "WD")) | |
{ | |
return SD_WD; | |
} | |
else if(!strcmp(str, "SA")) | |
{ | |
return SD_SA; | |
} | |
else if(!strcmp(str, "SD")) | |
{ | |
return SD_SD; | |
} | |
return SD_NONE; | |
} | |
LJTopMoveDown(LJTOP_TABLE:nIndex, nOldPos, nPos) | |
{ | |
// move entries down for insertion | |
for(new i = nOldPos - 1; i >= nPos; i--) | |
{ | |
strcopy(g_LJTop[nIndex][i + 1][m_strName], 64, g_LJTop[nIndex][i][m_strName]); | |
strcopy(g_LJTop[nIndex][i + 1][m_strSteamID], 32, g_LJTop[nIndex][i][m_strSteamID]); | |
g_LJTop[nIndex][i + 1][m_fDistance] = g_LJTop[nIndex][i][m_fDistance]; | |
g_LJTop[nIndex][i + 1][m_fPrestrafe] = g_LJTop[nIndex][i][m_fPrestrafe]; | |
g_LJTop[nIndex][i + 1][m_nStrafes] = g_LJTop[nIndex][i][m_nStrafes]; | |
g_LJTop[nIndex][i + 1][m_fSync] = g_LJTop[nIndex][i][m_fSync]; | |
g_LJTop[nIndex][i + 1][m_fMaxSpeed] = g_LJTop[nIndex][i][m_fMaxSpeed]; | |
g_LJTop[nIndex][i + 1][m_nTotalTicks] = g_LJTop[nIndex][i][m_nTotalTicks]; | |
g_LJTop[nIndex][i + 1][m_fSyncedAngle] = g_LJTop[nIndex][i][m_fSyncedAngle]; | |
g_LJTop[nIndex][i + 1][m_fTotalAngle] = g_LJTop[nIndex][i][m_fTotalAngle]; | |
g_LJTop[nIndex][i + 1][m_fHeightDelta] = g_LJTop[nIndex][i][m_fHeightDelta]; | |
g_LJTop[nIndex][i + 1][m_fBlockDistance] = g_LJTop[nIndex][i][m_fBlockDistance]; | |
g_LJTop[nIndex][i + 1][m_fTrajectory] = g_LJTop[nIndex][i][m_fTrajectory]; | |
g_LJTop[nIndex][i + 1][m_nTimestamp] = g_LJTop[nIndex][i][m_nTimestamp]; | |
for(new j; j < g_LJTop[nIndex][i][m_nStrafes]; j++) | |
{ | |
g_LJTop[nIndex][i + 1][m_StrafeDir][j] = g_LJTop[nIndex][i][m_StrafeDir][j]; | |
g_LJTop[nIndex][i + 1][m_fStrafeGain][j] = g_LJTop[nIndex][i][m_fStrafeGain][j]; | |
g_LJTop[nIndex][i + 1][m_fStrafeLoss][j] = g_LJTop[nIndex][i][m_fStrafeLoss][j]; | |
g_LJTop[nIndex][i + 1][m_nStrafeTicks][j] = g_LJTop[nIndex][i][m_nStrafeTicks][j]; | |
g_LJTop[nIndex][i + 1][m_fStrafeSync][j] = g_LJTop[nIndex][i][m_fStrafeSync][j]; | |
} | |
} | |
} | |
LJTopMoveUp(LJTOP_TABLE:nIndex, nPos) | |
{ | |
for(new i = nPos; i < 9; i++) | |
{ | |
strcopy(g_LJTop[nIndex][i][m_strName], 64, g_LJTop[nIndex][i + 1][m_strName]); | |
strcopy(g_LJTop[nIndex][i][m_strSteamID], 32, g_LJTop[nIndex][i + 1][m_strSteamID]); | |
g_LJTop[nIndex][i][m_fDistance] = g_LJTop[nIndex][i + 1][m_fDistance]; | |
g_LJTop[nIndex][i][m_fPrestrafe] = g_LJTop[nIndex][i + 1][m_fPrestrafe]; | |
g_LJTop[nIndex][i][m_nStrafes] = g_LJTop[nIndex][i + 1][m_nStrafes]; | |
g_LJTop[nIndex][i][m_fSync] = g_LJTop[nIndex][i + 1][m_fSync]; | |
g_LJTop[nIndex][i][m_fMaxSpeed] = g_LJTop[nIndex][i + 1][m_fMaxSpeed]; | |
g_LJTop[nIndex][i][m_nTotalTicks] = g_LJTop[nIndex][i + 1][m_nTotalTicks]; | |
g_LJTop[nIndex][i][m_fSyncedAngle] = g_LJTop[nIndex][i + 1][m_fSyncedAngle]; | |
g_LJTop[nIndex][i][m_fTotalAngle] = g_LJTop[nIndex][i + 1][m_fTotalAngle]; | |
g_LJTop[nIndex][i][m_fHeightDelta] = g_LJTop[nIndex][i + 1][m_fHeightDelta]; | |
g_LJTop[nIndex][i][m_fBlockDistance] = g_LJTop[nIndex][i + 1][m_fBlockDistance]; | |
g_LJTop[nIndex][i][m_fTrajectory] = g_LJTop[nIndex][i + 1][m_fTrajectory]; | |
g_LJTop[nIndex][i][m_nTimestamp] = g_LJTop[nIndex][i + 1][m_nTimestamp]; | |
for(new j; j < g_LJTop[nIndex][i + 1][m_nStrafes]; j++) | |
{ | |
g_LJTop[nIndex][i][m_StrafeDir][j] = g_LJTop[nIndex][i + 1][m_StrafeDir][j]; | |
g_LJTop[nIndex][i][m_fStrafeGain][j] = g_LJTop[nIndex][i + 1][m_fStrafeGain][j]; | |
g_LJTop[nIndex][i][m_fStrafeLoss][j] = g_LJTop[nIndex][i + 1][m_fStrafeLoss][j]; | |
g_LJTop[nIndex][i][m_nStrafeTicks][j] = g_LJTop[nIndex][i + 1][m_nStrafeTicks][j]; | |
g_LJTop[nIndex][i][m_fStrafeSync][j] = g_LJTop[nIndex][i + 1][m_fStrafeSync][j]; | |
} | |
} | |
// Clear last entry to prevent duplicates | |
strcopy(g_LJTop[nIndex][9][m_strName], 64, ""); | |
strcopy(g_LJTop[nIndex][9][m_strSteamID], 32, ""); | |
g_LJTop[nIndex][9][m_fDistance] = 0.0; | |
g_LJTop[nIndex][9][m_fPrestrafe] = 0.0; | |
g_LJTop[nIndex][9][m_nStrafes] = 0; | |
g_LJTop[nIndex][9][m_fSync] = 0.0; | |
g_LJTop[nIndex][9][m_fMaxSpeed] = 0.0; | |
g_LJTop[nIndex][9][m_nTotalTicks] = 0; | |
g_LJTop[nIndex][9][m_fSyncedAngle] = 0.0; | |
g_LJTop[nIndex][9][m_fTotalAngle] = 0.0; | |
g_LJTop[nIndex][9][m_fHeightDelta] = 0.0; | |
g_LJTop[nIndex][9][m_fBlockDistance] = 0.0; | |
g_LJTop[nIndex][9][m_nTimestamp] = 0; | |
for(new j; j < g_LJTop[nIndex][9][m_nStrafes]; j++) | |
{ | |
g_LJTop[nIndex][9][m_StrafeDir][j] = SD_NONE; | |
g_LJTop[nIndex][9][m_fStrafeGain][j] = 0.0; | |
g_LJTop[nIndex][9][m_fStrafeLoss][j] = 0.0; | |
g_LJTop[nIndex][9][m_nStrafeTicks][j] = 0; | |
g_LJTop[nIndex][9][m_fStrafeSync][j] = 0.0; | |
} | |
} | |
LJTopUpdate(client) | |
{ | |
if(g_PlayerStates[client][JumpType] == JT_LONGJUMP) | |
{ | |
if(g_PlayerStates[client][fJumpDistance] > g_LJTop[LT_LJ][LJTOP_NUM_ENTRIES - 1][m_fDistance]) | |
{ | |
LJTopUpdateTable(client, LT_LJ); | |
} | |
if(g_PlayerStates[client][bBlockMode] && !g_PlayerStates[client][bFailedBlock] && g_PlayerStates[client][fBlockDistance] > g_LJTop[LT_BLOCKLJ][LJTOP_NUM_ENTRIES - 1][m_fBlockDistance]) | |
{ | |
LJTopUpdateTable(client, LT_BLOCKLJ); | |
} | |
if(g_PlayerStates[client][JumpDir] == JD_SIDEWAYS && g_PlayerStates[client][fJumpDistance] > g_LJTop[LT_SWLJ][LJTOP_NUM_ENTRIES - 1][m_fDistance]) | |
{ | |
LJTopUpdateTable(client, LT_SWLJ); | |
} | |
if(g_PlayerStates[client][JumpDir] == JD_BACKWARDS && g_PlayerStates[client][fJumpDistance] > g_LJTop[LT_BWLJ][LJTOP_NUM_ENTRIES - 1][m_fDistance]) | |
{ | |
LJTopUpdateTable(client, LT_BWLJ); | |
} | |
} | |
else if(g_PlayerStates[client][JumpType] == JT_COUNTJUMP && g_PlayerStates[client][fJumpDistance] > g_LJTop[LT_CJ][LJTOP_NUM_ENTRIES - 1][m_fDistance]) | |
{ | |
LJTopUpdateTable(client, LT_CJ); | |
} | |
else if(g_PlayerStates[client][JumpType] == JT_BHOPJUMP && g_PlayerStates[client][fJumpDistance] > g_LJTop[LT_BJ][LJTOP_NUM_ENTRIES - 1][m_fDistance] && (!g_PlayerStates[client][bStamina] || g_bLJTopAllowEasyBJ)) // There's no such thing as an easy bj, only sore jaws, pubes down your throat and the taste of acidic ejaculate | |
{ | |
LJTopUpdateTable(client, LT_BJ); | |
} | |
else if(g_PlayerStates[client][JumpType] == JT_LADDERJUMP && g_PlayerStates[client][fJumpDistance] > g_LJTop[LT_LAJ][LJTOP_NUM_ENTRIES - 1][m_fDistance]) | |
{ | |
LJTopUpdateTable(client, LT_LAJ); | |
} | |
else if(g_PlayerStates[client][JumpType] == JT_BHOP && !g_bEnableBunnyHopping && g_PlayerStates[client][fJumpDistance] > g_LJTop[LT_STRAFEBHOP][LJTOP_NUM_ENTRIES - 1][m_fDistance]) | |
{ | |
LJTopUpdateTable(client, LT_STRAFEBHOP); | |
} | |
} | |
LJTopUpdateTable(client, LJTOP_TABLE:nLJTopTable) | |
{ | |
decl String:strName[64], String:strSteamID[32]; | |
GetClientName(client, strName, sizeof(strName)); | |
GetClientAuthString(client, strSteamID, sizeof(strSteamID)); | |
decl nIndex; | |
while((nIndex = FindCharInString(strName, ';')) != -1) | |
{ | |
strName[nIndex] = '-'; | |
} | |
new nPos = 0; | |
while(nPos < 9 && | |
(nLJTopTable == LT_BLOCKLJ ? g_PlayerStates[client][fBlockDistance] < g_LJTop[nLJTopTable][nPos][m_fBlockDistance] || | |
(g_PlayerStates[client][fBlockDistance] == g_LJTop[nLJTopTable][nPos][m_fBlockDistance] && | |
g_PlayerStates[client][fJumpDistance] < g_LJTop[nLJTopTable][nPos][m_fDistance]) : | |
g_PlayerStates[client][fJumpDistance] < g_LJTop[nLJTopTable][nPos][m_fDistance])) // longest statement in history | |
{ | |
if(!strcmp(g_LJTop[nLJTopTable][nPos][m_strSteamID], strSteamID)) | |
{ | |
// player already has better record | |
g_LJTop[nLJTopTable][nPos][m_strName] = strName; // update name | |
return; | |
} | |
nPos++; | |
} | |
new nOldPos = -1; | |
for(new i = 0; i < 10; i++) | |
{ | |
if(!strcmp(g_LJTop[nLJTopTable][i][m_strSteamID], strSteamID)) | |
{ | |
nOldPos = i; | |
break; | |
} | |
} | |
new bool:bSilent; | |
if(/*g_LJTop[nLJTopTable][nPos][m_strSteamID][0] == 0 || */g_PlayerStates[client][fJumpDistance] < g_fLJMin || | |
(nLJTopTable == LT_BLOCKLJ && nOldPos == nPos && g_PlayerStates[client][fBlockDistance] == g_LJTop[nLJTopTable][nPos][m_fBlockDistance])) | |
{ | |
bSilent = true; | |
} | |
if (Shavit_GetStyleSettingBool(Shavit_GetBhopStyle(client), "autobhop")) | |
{ | |
bSilent = true; | |
} | |
/* | |
enum TopStats | |
{ | |
String:m_strName[64 / 4], | |
String:m_strSteamID[32 / 4], | |
Float:m_fDistance, | |
Float:m_fPrestrafe, | |
m_nStrafes, | |
Float:m_fSync, | |
Float:m_fMaxSpeed, | |
m_nTotalTicks, | |
Float:m_fSyncedAngle, | |
Float:m_fTotalAngle, | |
Float:m_fHeightDelta, | |
Float:m_fBlockDistance, | |
Float:m_fTrajectory, | |
m_nTimestamp, | |
m_StrafeDir[LJTOP_MAX_STRAFES], | |
Float:m_fStrafeGain[LJTOP_MAX_STRAFES], | |
Float:m_fStrafeLoss[LJTOP_MAX_STRAFES], | |
m_nStrafeTicks[LJTOP_MAX_STRAFES], | |
Float:m_fStrafeSync[LJTOP_MAX_STRAFES], | |
} | |
*/ | |
LJTopMoveDown(nLJTopTable, nOldPos == -1 ? 9 : nOldPos, nPos); | |
// overwrite entry | |
strcopy(g_LJTop[nLJTopTable][nPos][m_strName], 64, strName); | |
strcopy(g_LJTop[nLJTopTable][nPos][m_strSteamID], 32, strSteamID); | |
g_LJTop[nLJTopTable][nPos][m_fDistance] = g_PlayerStates[client][fJumpDistance]; | |
g_LJTop[nLJTopTable][nPos][m_fPrestrafe] = g_PlayerStates[client][fPrestrafe]; | |
g_LJTop[nLJTopTable][nPos][m_nStrafes] = g_PlayerStates[client][nStrafes]; | |
g_LJTop[nLJTopTable][nPos][m_fSync] = g_PlayerStates[client][fSync]; | |
g_LJTop[nLJTopTable][nPos][m_fMaxSpeed] = g_PlayerStates[client][fMaxSpeed]; | |
g_LJTop[nLJTopTable][nPos][m_nTotalTicks] = g_PlayerStates[client][nTotalTicks]; | |
g_LJTop[nLJTopTable][nPos][m_fSyncedAngle] = g_PlayerStates[client][fSyncedAngle]; | |
g_LJTop[nLJTopTable][nPos][m_fTotalAngle] = g_PlayerStates[client][fTotalAngle]; | |
g_LJTop[nLJTopTable][nPos][m_fHeightDelta] = g_PlayerStates[client][fHeightDelta]; | |
g_LJTop[nLJTopTable][nPos][m_fBlockDistance] = g_PlayerStates[client][fBlockDistance]; | |
g_LJTop[nLJTopTable][nPos][m_fTrajectory] = g_PlayerStates[client][fTrajectory]; | |
g_LJTop[nLJTopTable][nPos][m_nTimestamp] = GetTime(); | |
for(new j; j < g_PlayerStates[client][nStrafes]; j++) | |
{ | |
g_LJTop[nLJTopTable][nPos][m_StrafeDir][j] = g_PlayerStates[client][StrafeDir][j]; | |
g_LJTop[nLJTopTable][nPos][m_fStrafeGain][j] = g_PlayerStates[client][fStrafeGain][j]; | |
g_LJTop[nLJTopTable][nPos][m_fStrafeLoss][j] = g_PlayerStates[client][fStrafeLoss][j]; | |
g_LJTop[nLJTopTable][nPos][m_nStrafeTicks][j] = g_PlayerStates[client][nStrafeTicks][j]; | |
g_LJTop[nLJTopTable][nPos][m_fStrafeSync][j] = g_PlayerStates[client][fStrafeSync][j]; | |
} | |
LJTopSave(); | |
LJTopCreateMenu(nLJTopTable); | |
if(bSilent) | |
{ | |
return; | |
} | |
if(nLJTopTable == LT_BLOCKLJ) | |
{ | |
CPrintToChatAll("%s {green}%s top {default}%d{green} in block lj top with {default}%.2f{green} longjump at {default}%.1f{green} block!", | |
strName, nPos == nOldPos ? "has improved their" : "is now", nPos + 1, g_PlayerStates[client][fJumpDistance], g_PlayerStates[client][fBlockDistance]); | |
} | |
else | |
{ | |
CPrintToChatAll("%s {green}%s top {default}%d{green} in %s top with {default}%.2f{green} %s!", | |
strName, nPos == nOldPos ? "has improved their" : "is now", nPos + 1, g_strLJTopOutput[nLJTopTable], g_PlayerStates[client][fJumpDistance], g_strJumpTypeLwr[g_PlayerStates[client][JumpType]]); | |
} | |
} | |
public Native_CancelJump(Handle:hPlugin, nParams) | |
{ | |
CancelJump(GetNativeCell(1)); | |
} | |
CancelJump(client) | |
{ | |
g_PlayerStates[client][bOnGround] = true; | |
} | |
public Action:Event_PlayerJump(Handle:event, const String:name[], bool:dontBroadcast) | |
{ | |
new client = GetClientOfUserId(GetEventInt(event, "userid")); | |
PlayerJump(client); | |
} | |
// cba with another enum so JT_LONGJUMP = jump, JT_DROP = slide off edge, JT_LADDERJUMP = ladder | |
PlayerJump(client, JUMP_TYPE:JumpType2 = JT_LONGJUMP) | |
{ | |
g_PlayerStates[client][bOnGround] = false; | |
new Float:fTime = GetGameTime(); | |
if(fTime - g_PlayerStates[client][fLandTime] < BHOP_TIME)//if((g_PlayerStates[client][nLastAerialTick] - GetGameTickCount()) * GetTickInterval() < BHOP_TIME) | |
{ | |
g_PlayerStates[client][nBhops]++; | |
} | |
else | |
{ | |
g_PlayerStates[client][nBhops] = 0; | |
// Only reset flags when jump chain stops so that players can't e.g. boost in the first jump and get a high distance on the next in a bhopjump | |
g_PlayerStates[client][IllegalJumpFlags] = IJF_NONE; | |
} | |
g_PlayerStates[client][fLastJumpHeightDelta] = g_PlayerStates[client][fHeightDelta]; | |
for(new i = 0; i < g_PlayerStates[client][nStrafes] && i < MAX_STRAFES; i++) | |
{ | |
g_PlayerStates[client][fStrafeGain][i] = 0.0; | |
g_PlayerStates[client][fStrafeLoss][i] = 0.0; | |
g_PlayerStates[client][fStrafeSync][i] = 0.0; | |
g_PlayerStates[client][nStrafeTicks][i] = 0; | |
g_PlayerStates[client][nStrafeTicksSynced][i] = 0; | |
} | |
// Reset stuff | |
g_PlayerStates[client][JumpDir] = JD_NONE; | |
g_PlayerStates[client][CurStrafeDir] = SD_NONE; | |
g_PlayerStates[client][nStrafes] = 0; | |
g_PlayerStates[client][fSync] = 0.0; | |
g_PlayerStates[client][fMaxSpeed] = 0.0; | |
g_PlayerStates[client][fJumpHeight] = 0.0; | |
g_PlayerStates[client][nTotalTicks] = 0; | |
g_PlayerStates[client][fTotalAngle] = 0.0; | |
g_PlayerStates[client][fSyncedAngle] = 0.0; | |
g_PlayerStates[client][fEdge] = -1.0; | |
g_PlayerStates[client][fBlockDistance] = -1.0; | |
g_PlayerStates[client][bStamina] = !GetEntPropFloat(client, Prop_Send, "m_flStamina"); | |
g_PlayerStates[client][bFailedBlock] = false; | |
g_PlayerStates[client][fTrajectory] = 0.0; | |
g_PlayerStates[client][fGain] = 0.0; | |
g_PlayerStates[client][fLoss] = 0.0; | |
g_PlayerStates[client][nJumpTick] = GetGameTickCount(); | |
if(JumpType2 == JT_LONGJUMP && g_PlayerStates[client][bBlockMode]) | |
{ | |
g_PlayerStates[client][fBlockDistance] = GetBlockDistance(client); | |
} | |
g_PlayerStates[client][LastJumpType] = g_PlayerStates[client][JumpType]; | |
// Determine jump type | |
if(JumpType2 == JT_DROP || JumpType2 == JT_LADDERJUMP) | |
{ | |
g_PlayerStates[client][JumpType] = JumpType2; | |
} | |
else | |
{ | |
if(g_PlayerStates[client][nBhops] > 1) | |
{ | |
g_PlayerStates[client][JumpType] = JT_BHOP; | |
} | |
else if(g_PlayerStates[client][nBhops] == 1) | |
{ | |
if(g_PlayerStates[client][LastJumpType] == JT_DROP) | |
{ | |
g_PlayerStates[client][fWJDropPre] = g_PlayerStates[client][fPrestrafe]; | |
g_PlayerStates[client][JumpType] = JT_WEIRDJUMP; | |
} | |
else if(g_PlayerStates[client][fLastJumpHeightDelta] > HEIGHT_DELTA_MIN(JT_LONGJUMP)) | |
{ | |
g_PlayerStates[client][JumpType] = JT_BHOPJUMP; | |
} | |
else | |
{ | |
g_PlayerStates[client][JumpType] = JT_BHOP; | |
} | |
} | |
else | |
{ | |
if(GetEntProp(client, Prop_Send, "m_bDucking", 1)) | |
{ | |
g_PlayerStates[client][JumpType] = JT_COUNTJUMP; | |
} | |
else | |
{ | |
g_PlayerStates[client][JumpType] = JT_LONGJUMP; | |
} | |
} | |
} | |
// Jumpoff origin | |
new Float:vOrigin[3]; | |
GetClientAbsOrigin(client, vOrigin); | |
Array_Copy(vOrigin, g_PlayerStates[client][vJumpOrigin], 3); | |
// Prestrafe | |
g_PlayerStates[client][fPrestrafe] = GetSpeed(client); | |
if(g_PlayerStates[client][JumpType] == JT_LONGJUMP) | |
{ | |
if(g_PlayerStates[client][fPrestrafe] > g_fLJMaxPrestrafe) | |
{ | |
g_PlayerStates[client][IllegalJumpFlags] |= IJF_PRESTRAFE; | |
} | |
if(!g_bLJScoutStats && (g_fMaxspeed > 250.0 && GetEntPropFloat(client, Prop_Data, "m_flMaxspeed") > 250.0)) | |
{ | |
new String:strPlayerWeapon[32]; | |
GetClientWeapon(client, strPlayerWeapon, sizeof(strPlayerWeapon)); | |
if(!strcmp(strPlayerWeapon, "weapon_scout") || strPlayerWeapon[0] == 0) | |
{ | |
g_PlayerStates[client][IllegalJumpFlags] |= IJF_SCOUT; | |
} | |
} | |
} | |
if(JumpType2 == JT_LONGJUMP) | |
{ | |
g_PlayerStates[client][fEdge] = GetEdge(client); | |
} | |
if(g_PlayerStates[client][bLJEnabled] && g_PlayerStates[client][bBeam]) | |
{ | |
StopBeam(client); | |
g_PlayerStates[client][bBeam] = true; | |
} | |
} | |
StopBeam(client) | |
{ | |
g_PlayerStates[client][bBeam] = false; | |
} | |
GetJumpDistance(client) | |
{ | |
new Float:vCurOrigin[3]; | |
GetClientAbsOrigin(client, vCurOrigin); | |
g_PlayerStates[client][fHeightDelta] = vCurOrigin[2] - g_PlayerStates[client][vJumpOrigin][2]; | |
vCurOrigin[2] = 0.0; | |
new Float:v[3]; | |
Array_Copy(g_PlayerStates[client][vJumpOrigin], v, 3); | |
v[2] = 0.0; | |
if(g_PlayerStates[client][JumpType] == JT_LADDERJUMP) | |
{ | |
g_PlayerStates[client][fJumpDistance] = GetVectorDistance(v, vCurOrigin); | |
} | |
else | |
{ | |
g_PlayerStates[client][fJumpDistance] = GetVectorDistance(v, vCurOrigin) + 32; | |
} | |
g_PlayerStates[client][bDuck] = bool:GetEntProp(client, Prop_Send, "m_bDucked", 1); | |
//g_PlayerStates[client][nTotalTicks] = GetGameTickCount() - g_PlayerStates[client][nJumpTick]; | |
} | |
GetJumpDistanceLastTick(client) | |
{ | |
new Float:vCurOrigin[3]; | |
Array_Copy(g_PlayerStates[client][vLastOrigin], vCurOrigin, 3); | |
g_PlayerStates[client][fHeightDelta] = vCurOrigin[2] - g_PlayerStates[client][vJumpOrigin][2]; | |
vCurOrigin[2] = 0.0; | |
new Float:v[3]; | |
Array_Copy(g_PlayerStates[client][vJumpOrigin], v, 3); | |
v[2] = 0.0; | |
g_PlayerStates[client][fJumpDistance] = GetVectorDistance(v, vCurOrigin) + 32.0; | |
g_PlayerStates[client][bDuck] = g_PlayerStates[client][bSecondLastDuckState]; | |
//g_PlayerStates[client][nTotalTicks] = GetGameTickCount() - g_PlayerStates[client][nJumpTick]; | |
//g_PlayerStates[client][nTotalTicks] -= 1; | |
} | |
CheckValidJump(client) | |
{ | |
new Float:vOrigin[3]; | |
GetClientAbsOrigin(client, vOrigin); | |
// Check gravity | |
new Float:fGravity = GetEntPropFloat(client, Prop_Data, "m_flGravity"); | |
if(fGravity != 1.0 && fGravity != 0.0) | |
{ | |
g_PlayerStates[client][IllegalJumpFlags] |= IJF_GRAVITY; | |
} | |
// Check speed | |
if(GetEntPropFloat(client, Prop_Data, "m_flLaggedMovementValue") != 1.0) | |
{ | |
g_PlayerStates[client][IllegalJumpFlags] |= IJF_LAGGEDMOVEMENTVALUE; | |
} | |
if(GetEntityMoveType(client) & MOVETYPE_NOCLIP) | |
{ | |
g_PlayerStates[client][IllegalJumpFlags] |= IJF_NOCLIP; | |
} | |
// Teleport check | |
new Float:vLastOrig[3], Float:vLastVel[3], Float:vVel[3]; | |
Array_Copy(g_PlayerStates[client][vLastOrigin], vLastOrig, 3); | |
Array_Copy(g_PlayerStates[client][vLastVelocity], vLastVel, 3); | |
GetEntPropVector(client, Prop_Data, "m_vecVelocity", vVel); | |
vLastOrig[2] = 0.0; | |
vOrigin[2] = 0.0; | |
vLastVel[2] = 0.0; | |
vVel[2] = 0.0; | |
// If the player moved further than their last velocity, they teleported | |
// It's slightly off, so adjust velocity | |
// pretty suk // less suk | |
/* | |
teleported 2.461413, 2.461400 | |
teleported 2.468606, 2.468604 | |
teleported 2.488778, 2.488739 | |
teleported 2.517628, 2.517453 | |
teleported 2.534332, 2.534170 | |
teleported 2.550610, 2.550508 | |
teleported 2.567417, 2.567395 | |
teleported 2.598604, 2.598514 | |
teleported 2.612708, 2.612616 | |
teleported 2.633581, 2.633533 | |
teleported 2.634170, 2.634044 | |
teleported 2.646703, 2.646473 | |
teleported 2.657407, 2.657327 | |
teleported 2.669471, 2.669248 | |
teleported 2.710047, 2.709968 | |
teleported 2.723108, 2.722937 | |
teleported 2.742104, 2.742006 | |
teleported 2.744069, 2.743859 | |
teleported 2.751010, 2.750807 | |
teleported 2.759773, 2.759721 | |
teleported 2.771660, 2.771600 | |
teleported 2.822698, 2.822640 | |
teleported 2.839976, 2.839771 | |
teleported 2.839976, 2.839771 | |
teleported 2.850264, 2.850194 | |
teleported 2.882310, 2.882229 | |
teleported 2.894205, 2.894115 | |
teleported 2.905041, 2.905009 | |
teleported 2.920642, 2.920416 | |
*/ | |
if(GetVectorDistance(vLastOrig, vOrigin) > GetVectorLength(vVel) / (1.0 / GetTickInterval()) + 0.001) | |
{ | |
#if defined DEBUG | |
PrintToChat(client, "teleported %f, %f (%f)", GetVectorDistance(vLastOrig, vOrigin), GetVectorLength(vVel) / (1.0 / GetTickInterval()) + 0.001, GetVectorLength(vLastVel) / (1.0 / GetTickInterval())); | |
#endif | |
if(g_PlayerStates[client][bBlockMode]) | |
{ | |
if(g_PlayerStates[client][bFailedBlock]) | |
{ | |
#if defined DEBUG | |
PrintToChat(client, "failedblock; returning"); | |
#endif | |
return; | |
} | |
else | |
{ | |
#if defined DEBUG | |
PrintToChat(client, "failedblocklongjump 3 tp %s %f %f %d", | |
g_PlayerStates[client][vLastOrigin][2] >= g_PlayerStates[client][vJumpOrigin][2] + HEIGHT_DELTA_MIN(JT_LONGJUMP) ? "saved" : "not saved", | |
g_PlayerStates[client][vLastOrigin][2], g_PlayerStates[client][vJumpOrigin][2] + HEIGHT_DELTA_MIN(JT_LONGJUMP), GetGameTickCount()); | |
#endif | |
if(g_PlayerStates[client][vLastOrigin][2] >= g_PlayerStates[client][vJumpOrigin][2] + HEIGHT_DELTA_MIN(JT_LONGJUMP)) | |
{ | |
GetJumpDistanceLastTick(client); | |
g_PlayerStates[client][bFailedBlock] = true; | |
return; | |
} | |
} | |
} | |
g_PlayerStates[client][IllegalJumpFlags] |= IJF_TELEPORT; | |
} | |
} | |
TBAnglesToUV(Float:vOut[3], const Float:vAngles[3]) | |
{ | |
vOut[0] = Cosine(vAngles[1] * FLOAT_PI / 180.0) * Cosine(vAngles[0] * FLOAT_PI / 180.0); | |
vOut[1] = Sine(vAngles[1] * FLOAT_PI / 180.0) * Cosine(vAngles[0] * FLOAT_PI / 180.0); | |
vOut[2] = -Sine(vAngles[0] * FLOAT_PI / 180.0); | |
} | |
_OnPlayerRunCmd(client, buttons, const Float:vOrigin[3], const Float:vAngles[3], const Float:vVelocity[3], bool:bDucked, bool:bGround) | |
{ | |
if(g_PlayerStates[client][GapSelectionMode] != GSM_NONE) | |
{ | |
GapSelect(client, buttons); | |
} | |
// Manage spectators | |
if(IsClientObserver(client)) | |
{ | |
if(g_PlayerStates[client][bLJEnabled]) | |
{ | |
new nObserverMode = GetEntProp(client, Prop_Send, "m_iObserverMode"); | |
if(nObserverMode == 4 || nObserverMode == 3) | |
{ | |
new nTarget = GetEntPropEnt(client, Prop_Send, "m_hObserverTarget"); | |
if(g_PlayerStates[client][nSpectatorTarget] != nTarget) | |
{ | |
if(g_PlayerStates[client][nSpectatorTarget] != -1 && g_PlayerStates[client][nSpectatorTarget] > 0 && g_PlayerStates[client][nSpectatorTarget] < MaxClients) | |
{ | |
g_PlayerStates[g_PlayerStates[client][nSpectatorTarget]][nSpectators]--; | |
} | |
g_PlayerStates[nTarget][nSpectators]++; | |
g_PlayerStates[client][nSpectatorTarget] = nTarget; | |
} | |
} | |
} | |
else | |
{ | |
if(g_PlayerStates[client][nSpectatorTarget] != -1) | |
{ | |
if(g_PlayerStates[client][nSpectatorTarget] > 0 && g_PlayerStates[client][nSpectatorTarget] < MaxClients) | |
{ | |
g_PlayerStates[g_PlayerStates[client][nSpectatorTarget]][nSpectators]--; | |
} | |
g_PlayerStates[client][nSpectatorTarget] = -1; | |
} | |
} | |
return; | |
} | |
else | |
{ | |
if(g_PlayerStates[client][nSpectatorTarget] != -1) | |
{ | |
g_PlayerStates[g_PlayerStates[client][nSpectatorTarget]][nSpectators]--; | |
g_PlayerStates[client][nSpectatorTarget] = -1; | |
} | |
} | |
if(!g_PlayerStates[client][bOnGround]) | |
CheckValidJump(client); | |
// BEAMU | |
if(g_PlayerStates[client][bBeam] && !bGround && (g_PlayerStates[client][bShowBhopStats] || g_PlayerStates[client][nBhops] < 2)) | |
{ | |
new Float:v1[3], Float:v2[3]; | |
v1[0] = vOrigin[0]; | |
v1[1] = vOrigin[1]; | |
v1[2] = g_PlayerStates[client][vJumpOrigin][2]; | |
v2[0] = g_PlayerStates[client][vLastOrigin][0]; | |
v2[1] = g_PlayerStates[client][vLastOrigin][1]; | |
v2[2] = g_PlayerStates[client][vJumpOrigin][2]; | |
new color[4] = {255, 255, 255, 100}; | |
if(g_PlayerStates[client][CurStrafeDir] % STRAFE_DIRECTION:2) | |
{ | |
color[0] = 128; | |
color[1] = 128; | |
} | |
TE_SetupBeamPoints(v1, v2, g_BeamModel, 0, 0, 0, 10.0, 3.0, 3.0, 10, 0.0, color, 0); | |
TE_SendToClient(client); | |
} | |
// Call PlayerJump for ladder jumps or walking off the edge | |
if(GetEntityMoveType(client) == MOVETYPE_LADDER) | |
{ | |
g_PlayerStates[client][bOnLadder] = true; | |
} | |
else | |
{ | |
if(g_PlayerStates[client][bOnLadder]) | |
{ | |
PlayerJump(client, JT_LADDERJUMP); | |
} | |
g_PlayerStates[client][bOnLadder] = false; | |
} | |
if(!bGround) | |
{ | |
if(g_PlayerStates[client][bOnGround]) | |
{ | |
PlayerJump(client, JT_DROP); | |
} | |
} | |
if(g_PlayerStates[client][bOnGround] || g_PlayerStates[client][nStrafes] >= MAX_STRAFES || (!g_PlayerStates[client][bLJEnabled] && !g_PlayerStates[client][nSpectators]) || g_PlayerStates[client][bFailedBlock]) | |
{ | |
// dumb language | |
if((bGround || g_PlayerStates[client][bOnLadder]) && !g_PlayerStates[client][bOnGround]) | |
{ | |
PlayerLand(client); | |
} | |
#if defined LJSERV | |
if(g_PlayerStates[client][bLJEnabled] && g_PlayerStates[client][bShowPrestrafeHint]) | |
{ | |
PrintPrestrafeHint(client); | |
} | |
#endif | |
return; | |
} | |
if(!bGround) | |
{ | |
g_PlayerStates[client][nLastAerialTick] = GetGameTickCount(); | |
if(GetVSpeed(vVelocity) > g_PlayerStates[client][fMaxSpeed]) | |
g_PlayerStates[client][fMaxSpeed] = GetVSpeed(vVelocity); | |
if(vOrigin[2] - g_PlayerStates[client][vJumpOrigin][2] > g_PlayerStates[client][fJumpHeight]) | |
g_PlayerStates[client][fJumpHeight] = vOrigin[2] - g_PlayerStates[client][vJumpOrigin][2]; | |
// Record the failed distance, but since it will trigger if you duck late, only save it if it's certain that the player will not land | |
if(g_PlayerStates[client][bBlockMode] && | |
!g_PlayerStates[client][bFailedBlock] && | |
(bDucked && vOrigin[2] <= g_PlayerStates[client][vJumpOrigin][2] + 1.0 || | |
!bDucked && vOrigin[2] <= g_PlayerStates[client][vJumpOrigin][2] + 1.5) && | |
vOrigin[2] >= g_PlayerStates[client][vJumpOrigin][2] + HEIGHT_DELTA_MIN(JT_LONGJUMP)) | |
{ | |
GetJumpDistance(client); | |
#if defined DEBUG | |
PrintToChat(client, "getting failed dist, %d, %f, %f", | |
vOrigin[2] >= g_PlayerStates[client][vJumpOrigin][2] + HEIGHT_DELTA_MIN(JT_LONGJUMP), vOrigin[2], g_PlayerStates[client][vJumpOrigin][2] + HEIGHT_DELTA_MIN(JT_LONGJUMP)); | |
#endif | |
} | |
#if defined DEBUG | |
if(vOrigin[2] <= g_PlayerStates[client][vJumpOrigin][2]) | |
{ | |
PrintToChat(client, "%d, %d, %d", bDucked, vOrigin[2] <= g_PlayerStates[client][vJumpOrigin][2] + HEIGHT_DELTA_MIN(JT_LONGJUMP), !bDucked && vOrigin[2] <= g_PlayerStates[client][vJumpOrigin][2] - 10.5); | |
} | |
#endif | |
// Check if the player is still capable of landing | |
if(g_PlayerStates[client][bBlockMode] && !g_PlayerStates[client][bFailedBlock] && | |
(bDucked && vOrigin[2] <= g_PlayerStates[client][vJumpOrigin][2] + HEIGHT_DELTA_MIN(JT_LONGJUMP)/* + 1.0*/ || // You land at 0.79 elevation when ducking | |
!bDucked && vOrigin[2] <= g_PlayerStates[client][vJumpOrigin][2] - 10.5)) | |
// Ducking increases your origin by 8.5; you land at 1.47 units elevation when ducking, so around 10.0; 10.5 for good measure | |
{ | |
StopBeam(client); | |
g_PlayerStates[client][bDuck] = bDucked; | |
g_PlayerStates[client][bFailedBlock] = true; | |
#if defined DEBUG | |
PrintToChat(client, "failedblocklongjump 1 %.2f, %d", vOrigin[2] - g_PlayerStates[client][vJumpOrigin][2], GetGameTickCount()); | |
#endif | |
if(bGround && !g_PlayerStates[client][bOnGround]) | |
{ | |
PlayerLand(client); | |
} | |
#if defined LJSERV | |
if(g_PlayerStates[client][bLJEnabled] && g_PlayerStates[client][bShowPrestrafeHint]) | |
{ | |
PrintPrestrafeHint(client); | |
} | |
#endif | |
return; | |
} | |
} | |
if(g_PlayerStates[client][JumpDir] == JD_BACKWARDS) | |
{ | |
new Float:vAnglesUV[3]; | |
TBAnglesToUV(vAnglesUV, vAngles); | |
new Float:vVelocityDir[3]; | |
vVelocityDir = vVelocity; | |
vVelocityDir[2] = 0.0; | |
NormalizeVector(vVelocityDir, vVelocityDir); | |
if(ArcCosine(GetVectorDotProduct(vAnglesUV, vVelocityDir)) < FLOAT_PI / 2) | |
{ | |
g_PlayerStates[client][JumpDir] = JD_NORMAL; | |
} | |
} | |
// check for multiple keys -- it will spam strafes when multiple are held without this | |
new nButtonCount; | |
if(buttons & IN_MOVELEFT) | |
nButtonCount++; | |
if(buttons & IN_MOVERIGHT) | |
nButtonCount++; | |
if(buttons & IN_FORWARD) | |
nButtonCount++; | |
if(buttons & IN_BACK) | |
nButtonCount++; | |
if(nButtonCount == 1) | |
{ | |
if(g_PlayerStates[client][CurStrafeDir] != SD_A && buttons & IN_MOVELEFT) | |
{ | |
if(g_PlayerStates[client][JumpDir] == JD_NONE) | |
{ | |
new Float:vAnglesUV[3]; | |
TBAnglesToUV(vAnglesUV, vAngles); | |
new Float:vVelocityDir[3]; | |
vVelocityDir = vVelocity; | |
vVelocityDir[2] = 0.0; | |
NormalizeVector(vVelocityDir, vVelocityDir); | |
if(ArcCosine(GetVectorDotProduct(vAnglesUV, vVelocityDir)) > FLOAT_PI / 2) | |
{ | |
g_PlayerStates[client][JumpDir] = JD_BACKWARDS; | |
} | |
else | |
{ | |
g_PlayerStates[client][JumpDir] = JD_NORMAL; | |
} | |
} | |
if(g_PlayerStates[client][JumpDir] == JD_SIDEWAYS) | |
{ | |
g_PlayerStates[client][JumpDir] = JD_NORMAL; | |
} | |
g_PlayerStates[client][StrafeDir][g_PlayerStates[client][nStrafes]] = SD_A; | |
g_PlayerStates[client][CurStrafeDir] = SD_A; | |
g_PlayerStates[client][nStrafes]++; | |
} | |
else if(g_PlayerStates[client][CurStrafeDir] != SD_D && buttons & IN_MOVERIGHT) | |
{ | |
if(g_PlayerStates[client][JumpDir] == JD_NONE) | |
{ | |
new Float:vAnglesUV[3]; | |
TBAnglesToUV(vAnglesUV, vAngles); | |
new Float:vVelocityDir[3]; | |
vVelocityDir = vVelocity; | |
vVelocityDir[2] = 0.0; | |
NormalizeVector(vVelocityDir, vVelocityDir); | |
if(ArcCosine(GetVectorDotProduct(vAnglesUV, vVelocityDir)) > FLOAT_PI / 2) | |
{ | |
g_PlayerStates[client][JumpDir] = JD_BACKWARDS; | |
} | |
else | |
{ | |
g_PlayerStates[client][JumpDir] = JD_NORMAL; | |
} | |
} | |
else if(g_PlayerStates[client][JumpDir] == JD_SIDEWAYS) | |
{ | |
g_PlayerStates[client][JumpDir] = JD_NORMAL; | |
} | |
g_PlayerStates[client][StrafeDir][g_PlayerStates[client][nStrafes]] = SD_D; | |
g_PlayerStates[client][CurStrafeDir] = SD_D; | |
g_PlayerStates[client][nStrafes]++; | |
} | |
else if(g_PlayerStates[client][CurStrafeDir] != SD_W && buttons & IN_FORWARD) | |
{ | |
if(g_PlayerStates[client][JumpDir] == JD_NONE && (vVelocity[0] || vVelocity[1])) | |
{ | |
new Float:vAnglesUV[3]; | |
TBAnglesToUV(vAnglesUV, vAngles); | |
new Float:vVelocityDir[3]; | |
vVelocityDir = vVelocity; | |
vVelocityDir[2] = 0.0; | |
NormalizeVector(vVelocityDir, vVelocityDir); | |
if(DegToRad(90.0 - SW_ANGLE_THRESHOLD) < ArcCosine(GetVectorDotProduct(vAnglesUV, vVelocityDir)) < DegToRad(90.0 + SW_ANGLE_THRESHOLD)) | |
{ | |
g_PlayerStates[client][JumpDir] = JD_SIDEWAYS; | |
} | |
} | |
g_PlayerStates[client][StrafeDir][g_PlayerStates[client][nStrafes]] = SD_W; | |
g_PlayerStates[client][CurStrafeDir] = SD_W; | |
g_PlayerStates[client][nStrafes]++; | |
} | |
else if(g_PlayerStates[client][CurStrafeDir] != SD_S && buttons & IN_BACK) | |
{ | |
if(g_PlayerStates[client][JumpDir] == JD_NONE && (vVelocity[0] || vVelocity[1])) | |
{ | |
new Float:vAnglesUV[3]; | |
TBAnglesToUV(vAnglesUV, vAngles); | |
new Float:vVelocityDir[3]; | |
vVelocityDir = vVelocity; | |
vVelocityDir[2] = 0.0; | |
NormalizeVector(vVelocityDir, vVelocityDir); | |
if(DegToRad(90.0 - SW_ANGLE_THRESHOLD) < ArcCosine(GetVectorDotProduct(vAnglesUV, vVelocityDir)) < DegToRad(90.0 + SW_ANGLE_THRESHOLD)) | |
{ | |
g_PlayerStates[client][JumpDir] = JD_SIDEWAYS; | |
} | |
} | |
g_PlayerStates[client][StrafeDir][g_PlayerStates[client][nStrafes]] = SD_S; | |
g_PlayerStates[client][CurStrafeDir] = SD_S; | |
g_PlayerStates[client][nStrafes]++; | |
} | |
} | |
if(g_PlayerStates[client][nStrafes] > 0) | |
{ | |
new Float:v[3], Float:v2[3]; | |
Array_Copy(g_PlayerStates[client][vLastVelocity], v, 3); | |
Array_Copy(g_PlayerStates[client][vLastAngles], v2, 3); | |
new Float:fVelDelta = GetSpeed(client) - GetVSpeed(v); | |
new Float:fAngleDelta = fmod((FloatAbs(vAngles[1] - v2[1]) + 180.0), 360.0) - 180.0; | |
g_PlayerStates[client][nStrafeTicks][g_PlayerStates[client][nStrafes] - 1]++; | |
g_PlayerStates[client][fTotalAngle] += fAngleDelta; | |
if(fVelDelta > 0.0) | |
{ | |
g_PlayerStates[client][fStrafeGain][g_PlayerStates[client][nStrafes] - 1] += fVelDelta; | |
g_PlayerStates[client][fGain] += fVelDelta; | |
g_PlayerStates[client][nStrafeTicksSynced][g_PlayerStates[client][nStrafes] - 1]++; | |
g_PlayerStates[client][fSyncedAngle] += fAngleDelta; | |
} | |
else | |
{ | |
g_PlayerStates[client][fStrafeLoss][g_PlayerStates[client][nStrafes] - 1] -= fVelDelta; | |
g_PlayerStates[client][fLoss] -= fVelDelta; | |
} | |
} | |
g_PlayerStates[client][nTotalTicks]++; | |
g_PlayerStates[client][fTrajectory] += GetSpeed(client) * GetTickInterval(); | |
if(bGround && !g_PlayerStates[client][bOnGround]) | |
{ | |
PlayerLand(client); | |
} | |
#if defined LJSERV | |
if(g_PlayerStates[client][bLJEnabled] && g_PlayerStates[client][bShowPrestrafeHint]) | |
{ | |
PrintPrestrafeHint(client); | |
} | |
#endif | |
} | |
public Action:OnPlayerRunCmd(client, &buttons, &impulse, Float:vel[3], Float:vAngles[3], &weapon) | |
{ | |
new Float:vOrigin[3], Float:vVelocity[3]; | |
new bool:bDucked = bool:GetEntProp(client, Prop_Send, "m_bDucked", 1), bool:bGround = bool:(GetEntityFlags(client) & FL_ONGROUND); | |
GetClientAbsOrigin(client, vOrigin); | |
GetEntPropVector(client, Prop_Data, "m_vecVelocity", vVelocity); | |
_OnPlayerRunCmd(client, buttons, vOrigin, vAngles, vVelocity, bDucked, bGround); | |
Array_Copy(vOrigin, g_PlayerStates[client][vLastOrigin], 3); | |
Array_Copy(vAngles, g_PlayerStates[client][vLastAngles], 3); | |
Array_Copy(vVelocity, g_PlayerStates[client][vLastVelocity], 3); | |
g_PlayerStates[client][bSecondLastDuckState] = g_PlayerStates[client][bLastDuckState]; | |
g_PlayerStates[client][bLastDuckState] = bDucked; | |
g_PlayerStates[client][LastButtons] = buttons; | |
return Plugin_Continue; | |
} | |
#if defined LJSERV | |
PrintPrestrafeHint(client) | |
{ | |
new bool:bGround = bool:(GetEntityFlags(client) & FL_ONGROUND); | |
decl String:strHint[128]; | |
Format(strHint, sizeof(strHint), "Pre: %.2f", bGround && !GetEntPropFloat(client, Prop_Send, "m_flStamina") ? GetSpeed(client) : g_PlayerStates[client][fPrestrafe]); | |
if(g_PlayerStates[client][fEdge] != -1.0 && !bGround) | |
{ | |
Append(strHint, sizeof(strHint), " | e: %.2f", g_PlayerStates[client][fEdge]); | |
} | |
if(!bGround) | |
{ | |
Append(strHint, sizeof(strHint), "\nG: %d | L: %d\nMaxspeed: %d", RoundFloat(g_PlayerStates[client][fGain]), RoundFloat(g_PlayerStates[client][fLoss]), RoundFloat(g_PlayerStates[client][fMaxSpeed])); | |
} | |
PrintHintText(client, strHint); | |
} | |
#endif | |
PlayerLand(client) | |
{ | |
g_PlayerStates[client][bOnGround] = true; | |
g_PlayerStates[client][fLandTime] = GetGameTime(); | |
if(!g_PlayerStates[client][bLJEnabled] && !g_PlayerStates[client][nSpectators] || !g_PlayerStates[client][bShowBhopStats] && g_PlayerStates[client][nBhops] > 1) | |
return; | |
// Final CheckValidJump | |
//CheckValidJump(client); | |
new Float:vCurOrigin[3]; | |
GetClientAbsOrigin(client, vCurOrigin); | |
g_PlayerStates[client][fFinalSpeed] = GetSpeed(client); | |
#if defined DEBUG | |
if(g_PlayerStates[client][bFailedBlock] && vCurOrigin[2] - g_PlayerStates[client][vJumpOrigin][2] > -2.0) | |
PrintToChat(client, "failed block && height delta = %f", vCurOrigin[2] - g_PlayerStates[client][vJumpOrigin][2]); | |
PrintToChat(client, "%d", g_PlayerStates[client][bFailedBlock]); | |
#endif | |
// Calculate distances | |
if(!g_PlayerStates[client][bFailedBlock])// || // if block longjump failed, distances have already been written in mid-air. | |
//vCurOrigin[2] - g_PlayerStates[client][vJumpOrigin][2] >= HEIGHT_DELTA_MIN(JT_LONGJUMP)) // bugs sometimes if you land on last tick (I think) idk how else 2 fix | |
{ | |
GetJumpDistance(client); | |
g_PlayerStates[client][bFailedBlock] = false; | |
} | |
// don't show drop stats | |
if(g_PlayerStates[client][JumpType] == JT_DROP) | |
return; | |
if(!g_PlayerStates[client][bShowAllJumps]) | |
{ | |
if(g_PlayerStates[client][JumpType] == JT_LONGJUMP) | |
{ | |
if(g_PlayerStates[client][fHeightDelta] > HEIGHT_DELTA_MIN(g_PlayerStates[client][JumpType]) && g_PlayerStates[client][fHeightDelta] < HEIGHT_DELTA_MAX(g_PlayerStates[client][JumpType])) | |
{ | |
if(g_PlayerStates[client][fJumpDistance] < 240.0) | |
{ | |
return; | |
} | |
} | |
else // Dropjump/upjump | |
{ | |
if(g_PlayerStates[client][fJumpDistance] < 240.0 - g_PlayerStates[client][fHeightDelta]) | |
{ | |
return; | |
} | |
} | |
} | |
else if(g_PlayerStates[client][fJumpDistance] < 240.0) | |
{ | |
return; | |
} | |
} | |
// Check whether the player actually moved past the block edge | |
if(g_PlayerStates[client][bBlockMode] && !g_PlayerStates[client][bFailedBlock]) | |
{ | |
if(!g_PlayerStates[client][vBlockNormal][0] || !g_PlayerStates[client][vBlockNormal][1]) | |
{ | |
// bools are not actually handled as 1 bit bools but 32 bit cells so n = normal.y gives out of bounds exception | |
// !!normal.y or !normal.x rather | |
// pawn good | |
new bool:n = !g_PlayerStates[client][vBlockNormal][0]; | |
if(g_PlayerStates[client][vBlockNormal][n] > 0.0) | |
{ | |
if(vCurOrigin[n] + 16.0 * g_PlayerStates[client][vBlockNormal][n] < g_PlayerStates[client][vBlockEndPos][n]) | |
{ | |
g_PlayerStates[client][bFailedBlock] = true; | |
} | |
} | |
else | |
{ | |
if(vCurOrigin[n] + 16.0 * g_PlayerStates[client][vBlockNormal][n] > g_PlayerStates[client][vBlockEndPos][n]) | |
{ | |
g_PlayerStates[client][bFailedBlock] = true; | |
} | |
} | |
} | |
else | |
{ | |
new Float:vAdjCurOrigin[3], Float:vInvNormal[3]; | |
vAdjCurOrigin = vCurOrigin; | |
Array_Copy(g_PlayerStates[client][vBlockNormal], vInvNormal, 2); | |
ScaleVector(vInvNormal, -1.0); | |
Adjust(vAdjCurOrigin, vInvNormal); | |
// f(endpos.x) + (origin.x - endpos.x) * b = (f(endpos.x) - endpos.x * b) + origin.x * b = f(0) + origin.x * b | |
// block normal is perpendicular to the edge direction, so b = 1 / (normal rot 90).x | |
// dx and dy should have same sign so ccw rot if facing down, cw rot if up | |
new Float:b = 1 / (g_PlayerStates[client][vBlockNormal][0] < 0 ? g_PlayerStates[client][vBlockNormal][1] : -g_PlayerStates[client][vBlockNormal][1]); | |
new Float:fPos = g_PlayerStates[client][vBlockEndPos][1] + (vAdjCurOrigin[0] - g_PlayerStates[client][vBlockEndPos][0]) * b; | |
if(g_PlayerStates[client][vBlockNormal][1] > 0.0 ? vAdjCurOrigin[1] < fPos : vAdjCurOrigin[1] > fPos) | |
{ | |
g_PlayerStates[client][bFailedBlock] = true; | |
} | |
#if defined DEBUG | |
PrintToChat(client, "block normal: %.2f, %.2f\n%.2f, %.2f, %.2f", | |
g_PlayerStates[client][vBlockNormal][0], | |
g_PlayerStates[client][vBlockNormal][1], | |
vAdjCurOrigin[1], | |
fPos, | |
g_PlayerStates[client][vBlockEndPos][1]); | |
new Float:v1[3], Float:v2[3]; | |
v1[2] = g_PlayerStates[client][vBlockEndPos][2] + 0.1; | |
v2[2] = g_PlayerStates[client][vBlockEndPos][2] + 0.1; | |
v1[0] = g_PlayerStates[client][vBlockEndPos][0] - 50; | |
v1[1] = g_PlayerStates[client][vBlockEndPos][1] + (v1[0] - g_PlayerStates[client][vBlockEndPos][0]) * b; | |
v2[0] = g_PlayerStates[client][vBlockEndPos][0] + 50; | |
v2[1] = g_PlayerStates[client][vBlockEndPos][1] + (v2[0] - g_PlayerStates[client][vBlockEndPos][0]) * b; | |
CreateBeam2(v1, v2, 0, 0, 255); | |
if(g_PlayerStates[client][bFailedBlock]) | |
{ | |
PrintToChat(client, "failedblocklongjump 2"); | |
} | |
#endif | |
} | |
} | |
// sum sync | |
g_PlayerStates[client][fSync] = 0.0; | |
for(new i = 0; i < g_PlayerStates[client][nStrafes] && i < MAX_STRAFES; i++) | |
{ | |
g_PlayerStates[client][fSync] += g_PlayerStates[client][nStrafeTicksSynced][i]; | |
g_PlayerStates[client][fStrafeSync][i] = float(g_PlayerStates[client][nStrafeTicksSynced][i]) / g_PlayerStates[client][nStrafeTicks][i] * 100; | |
} | |
g_PlayerStates[client][fSync] /= g_PlayerStates[client][nTotalTicks]; | |
g_PlayerStates[client][fSync] *= 100; | |
//// | |
// Write HUD hint | |
//// | |
decl String:buf[1024]; | |
g_PlayerStates[client][strHUDHint][0] = 0; | |
if(g_PlayerStates[client][bBlockMode]) | |
{ | |
if(g_PlayerStates[client][fBlockDistance] != -1.0) | |
{ | |
Format(buf, sizeof(buf), "%.1f block %s\n", | |
g_PlayerStates[client][fBlockDistance], | |
g_PlayerStates[client][bFailedBlock] ? "(failed)" : ""); | |
StrCat(g_PlayerStates[client][strHUDHint], HUD_HINT_SIZE, buf); | |
} | |
else | |
{ | |
Format(buf, sizeof(buf), "??? block %s\n", | |
g_PlayerStates[client][bFailedBlock] ? "(failed) " : ""); | |
StrCat(g_PlayerStates[client][strHUDHint], HUD_HINT_SIZE, buf); | |
} | |
if(g_PlayerStates[client][fBlockDistance] != -1.0 && g_PlayerStates[client][vBlockNormal][0] != 0.0 && g_PlayerStates[client][vBlockNormal][1] != 0.0 && g_PlayerStates[client][nVerbosity] > 2) | |
{ | |
new Float:f = 32.0 * (FloatAbs(g_PlayerStates[client][vBlockNormal][0]) + FloatAbs(g_PlayerStates[client][vBlockNormal][1]) - 1.0); | |
new Float:fAngle = FloatAbs(RadToDeg(ArcSine(g_PlayerStates[client][vBlockNormal][0]))); | |
fAngle = fAngle <= 45.0 ? fAngle : 90 - fAngle; | |
Format(buf, sizeof(buf), "(%.1f rotated by %.1fº)\n", | |
g_PlayerStates[client][fBlockDistance] + f, | |
fAngle); | |
StrCat(g_PlayerStates[client][strHUDHint], HUD_HINT_SIZE, buf); | |
} | |
if(g_PlayerStates[client][fBlockDistance] != -1.0 && g_PlayerStates[client][nVerbosity] > 1) | |
{ | |
new Float:vJumpAngle[3], Float:vJumpOrig[3], Float:vBlockN[3]; | |
vJumpAngle = vCurOrigin; | |
Array_Copy(g_PlayerStates[client][vJumpOrigin], vJumpOrig, 3); | |
vBlockN[0] = g_PlayerStates[client][vBlockNormal][0]; | |
vBlockN[1] = g_PlayerStates[client][vBlockNormal][1]; | |
vJumpAngle[2] = 0.0; | |
vJumpOrig[2] = 0.0; | |
SubtractVectors(vJumpAngle, vJumpOrig, vJumpAngle); | |
NormalizeVector(vJumpAngle, vJumpAngle); | |
Format(buf, sizeof(buf), "%.2f degrees off block\n", | |
RadToDeg(ArcCosine(GetVectorDotProduct(vJumpAngle, vBlockN)))); | |
StrCat(g_PlayerStates[client][strHUDHint], HUD_HINT_SIZE, buf); | |
} | |
} | |
decl String:strJump[32]; | |
if(g_PlayerStates[client][fHeightDelta] > HEIGHT_DELTA_MAX(g_PlayerStates[client][JumpType])) | |
{ | |
if(g_PlayerStates[client][JumpType] == JT_LONGJUMP) | |
{ | |
strJump = "Upjump"; | |
} | |
else | |
{ | |
Format(strJump, sizeof(strJump), "Up%s", g_strJumpTypeLwr[g_PlayerStates[client][JumpType]]); | |
} | |
} | |
else if(g_PlayerStates[client][fHeightDelta] < HEIGHT_DELTA_MIN(g_PlayerStates[client][JumpType])) | |
{ | |
if(g_PlayerStates[client][JumpType] == JT_LONGJUMP) | |
{ | |
strJump = "Dropjump"; | |
} | |
else | |
{ | |
Format(strJump, sizeof(strJump), "Drop%s", g_strJumpTypeLwr[g_PlayerStates[client][JumpType]]); | |
} | |
} | |
else | |
{ | |
strcopy(strJump, sizeof(strJump), g_strJumpType[g_PlayerStates[client][JumpType]]); | |
} | |
decl String:strJumpDir[16]; | |
strJumpDir = g_PlayerStates[client][JumpDir] == JD_SIDEWAYS ? " sideways" : g_PlayerStates[client][JumpDir] == JD_BACKWARDS ? " backwards" : ""; | |
Format(buf, sizeof(buf), "%s%s%s\npre: %.2f", | |
strJump, strJumpDir, | |
g_PlayerStates[client][JumpType] == JT_LONGJUMP && | |
g_PlayerStates[client][fHeightDelta] >= HEIGHT_DELTA_MIN(g_PlayerStates[client][JumpType]) && | |
g_PlayerStates[client][IllegalJumpFlags] == IJF_NONE && | |
g_PlayerStates[client][nTotalTicks] > 77 ? " (extended)" : "", | |
g_PlayerStates[client][fPrestrafe]); | |
StrCat(g_PlayerStates[client][strHUDHint], HUD_HINT_SIZE, buf); | |
if(g_PlayerStates[client][JumpType] == JT_WEIRDJUMP && g_PlayerStates[client][nVerbosity] > 1) | |
{ | |
Format(buf, sizeof(buf), " (%.2f)", | |
g_PlayerStates[client][fWJDropPre]); | |
StrCat(g_PlayerStates[client][strHUDHint], HUD_HINT_SIZE, buf); | |
} | |
Format(buf, sizeof(buf), "; dist: %.2f", | |
g_PlayerStates[client][fJumpDistance]); | |
StrCat(g_PlayerStates[client][strHUDHint], HUD_HINT_SIZE, buf); | |
if(g_PlayerStates[client][fEdge] != -1.0) | |
{ | |
Format(buf, sizeof(buf), "; edge: %.2f", | |
g_PlayerStates[client][fEdge]); | |
StrCat(g_PlayerStates[client][strHUDHint], HUD_HINT_SIZE, buf); | |
} | |
StrCat(g_PlayerStates[client][strHUDHint], HUD_HINT_SIZE, "\n"); | |
Format(buf, sizeof(buf), "%d; %.2f%; %.2f (%.2f)", | |
g_PlayerStates[client][nStrafes], | |
g_PlayerStates[client][fSync], | |
g_PlayerStates[client][fMaxSpeed], | |
g_PlayerStates[client][fMaxSpeed] - g_PlayerStates[client][fPrestrafe]); | |
StrCat(g_PlayerStates[client][strHUDHint], HUD_HINT_SIZE, buf); | |
if(g_PlayerStates[client][nVerbosity] > 2) | |
{ | |
Format(buf, sizeof(buf), "\n%s%.2f; %.2f; %.4f%; %d", | |
g_PlayerStates[client][fHeightDelta] >= 0.0 ? "+" : "", | |
g_PlayerStates[client][fHeightDelta], | |
g_PlayerStates[client][fJumpHeight], | |
(g_PlayerStates[client][fJumpDistance] - 32.0) / g_PlayerStates[client][fTrajectory], | |
g_PlayerStates[client][nTotalTicks]); | |
StrCat(g_PlayerStates[client][strHUDHint], HUD_HINT_SIZE, buf); | |
} | |
if(g_PlayerStates[client][bLJEnabled]) | |
{ | |
buf[0] = 0; | |
Append(buf, sizeof(buf), "\n"); | |
if(g_PlayerStates[client][bBlockMode]) | |
{ | |
if(g_PlayerStates[client][fBlockDistance] != -1.0) | |
{ | |
Append(buf, sizeof(buf), "%.01f block%s", | |
g_PlayerStates[client][fBlockDistance], | |
g_PlayerStates[client][bFailedBlock] ? " (failed)" : ""); | |
} | |
else | |
{ | |
Append(buf, sizeof(buf), "??? block%s", | |
g_PlayerStates[client][bFailedBlock] ? " (failed)" : ""); | |
} | |
if(g_PlayerStates[client][fBlockDistance] != -1.0 && g_PlayerStates[client][vBlockNormal][0] != 0.0 && g_PlayerStates[client][vBlockNormal][1] != 0.0) | |
{ | |
new Float:f = 32.0 * (FloatAbs(g_PlayerStates[client][vBlockNormal][0]) + FloatAbs(g_PlayerStates[client][vBlockNormal][1]) - 1.0); | |
new Float:fAngle = FloatAbs(RadToDeg(ArcSine(g_PlayerStates[client][vBlockNormal][0]))); | |
fAngle = fAngle <= 45.0 ? fAngle : 90 - fAngle; | |
Append(buf, sizeof(buf), " (%.1f rotated by %.1f)", | |
g_PlayerStates[client][fBlockDistance] + f, | |
fAngle); | |
} | |
if(g_PlayerStates[client][fBlockDistance] != -1.0) | |
{ | |
new Float:vJumpAngle[3], Float:vJumpOrig[3], Float:vBlockN[3]; | |
vJumpAngle = vCurOrigin; | |
Array_Copy(g_PlayerStates[client][vJumpOrigin], vJumpOrig, 3); | |
vBlockN[0] = g_PlayerStates[client][vBlockNormal][0]; | |
vBlockN[1] = g_PlayerStates[client][vBlockNormal][1]; | |
vJumpAngle[2] = 0.0; | |
vJumpOrig[2] = 0.0; | |
SubtractVectors(vJumpAngle, vJumpOrig, vJumpAngle); | |
NormalizeVector(vJumpAngle, vJumpAngle); | |
Append(buf, sizeof(buf), " - %.2f degrees off block", | |
RadToDeg(ArcCosine(GetVectorDotProduct(vJumpAngle, vBlockN)))); | |
} | |
Append(buf, sizeof(buf), "\n"); | |
} | |
Append(buf, sizeof(buf), "%s%s%s\nDistance: %.2f", | |
strJump, strJumpDir, | |
g_PlayerStates[client][JumpType] == JT_LONGJUMP && | |
g_PlayerStates[client][fHeightDelta] > HEIGHT_DELTA_MIN(g_PlayerStates[client][JumpType]) | |
&& g_PlayerStates[client][nTotalTicks] > 77 ? " (extended)" : "", | |
g_PlayerStates[client][fJumpDistance]); | |
Append(buf, sizeof(buf), "; prestrafe: %.2f", | |
g_PlayerStates[client][fPrestrafe]); | |
if(g_PlayerStates[client][JumpType] == JT_WEIRDJUMP) | |
{ | |
Append(buf, sizeof(buf), "; drop prestrafe: %.2f", | |
g_PlayerStates[client][fWJDropPre]); | |
} | |
if(g_PlayerStates[client][fEdge] != -1.0) | |
{ | |
Append(buf, sizeof(buf), "; edge: %.2f", | |
g_PlayerStates[client][fEdge]); | |
} | |
if(g_PlayerStates[client][nTotalTicks] == 78) | |
{ | |
new Float:vCurOrigin2[3]; | |
Array_Copy(g_PlayerStates[client][vLastOrigin], vCurOrigin2, 3); | |
vCurOrigin2[2] = 0.0; | |
new Float:v[3]; | |
Array_Copy(g_PlayerStates[client][vJumpOrigin], v, 3); | |
v[2] = 0.0; | |
new Float:ProjDist = GetVectorDistance(v, vCurOrigin2) + 32.0; | |
Append(buf, sizeof(buf), "; projected real distance: %.2f", ProjDist); | |
} | |
Append(buf, sizeof(buf), "\nStrafes: %d; sync: %.2f%%; maxspeed (gain): %.2f (%.2f)", | |
g_PlayerStates[client][nStrafes], | |
g_PlayerStates[client][fSync], | |
g_PlayerStates[client][fMaxSpeed], | |
g_PlayerStates[client][fMaxSpeed] - g_PlayerStates[client][fPrestrafe]); | |
Append(buf, sizeof(buf), "\nHeight diff: %s%.2f; jump height: %.2f; efficiency: %.4f; ticks: %d; degrees synced/degrees turned: %.2f/%.2f", | |
g_PlayerStates[client][fHeightDelta] >= 0.0 ? "+" : "", | |
g_PlayerStates[client][fHeightDelta], | |
g_PlayerStates[client][fJumpHeight], | |
(g_PlayerStates[client][fJumpDistance] - 32.0) / g_PlayerStates[client][fTrajectory], | |
g_PlayerStates[client][nTotalTicks], | |
g_PlayerStates[client][fSyncedAngle], g_PlayerStates[client][fTotalAngle]); | |
PrintToConsole(client, buf); | |
new Handle:hBuffer = StartMessageOne("KeyHintText", client); | |
BfWriteByte(hBuffer, 1); | |
BfWriteString(hBuffer, g_PlayerStates[client][strHUDHint]); | |
EndMessage(); | |
} | |
//// | |
// Panel | |
//// | |
new Handle:hStatsPanel = CreatePanel(); | |
Format(buf, 128, "%s %.2f %s%.2f", | |
g_strJumpTypeShort[g_PlayerStates[client][JumpType]], | |
g_PlayerStates[client][fJumpDistance], | |
g_PlayerStates[client][fHeightDelta] > 0.01 ? "+" : "", | |
g_PlayerStates[client][fHeightDelta]); | |
SetPanelTitle(hStatsPanel, buf); | |
if(g_PlayerStates[client][bLJEnabled]) | |
{ | |
PrintToConsole(client, "--------------------------------"); | |
} | |
// Print first 16 strafes to panel | |
for(new i = 0; i < g_PlayerStates[client][nStrafes] && i < 16; i++) | |
{ | |
decl String:strStrafeKey[3]; | |
GetStrafeKey(strStrafeKey, g_PlayerStates[client][StrafeDir][i]); | |
DrawPanelTextF(hStatsPanel, "%d %s %.2f %.2f %.2f %.2f", | |
i + 1, | |
strStrafeKey, | |
g_PlayerStates[client][fStrafeGain][i], g_PlayerStates[client][fStrafeLoss][i], | |
float(g_PlayerStates[client][nStrafeTicks][i]) / g_PlayerStates[client][nTotalTicks] * 100, | |
float(g_PlayerStates[client][nStrafeTicksSynced][i]) / g_PlayerStates[client][nStrafeTicks][i] * 100); | |
} | |
// Print strafes to console | |
if(g_PlayerStates[client][bLJEnabled]) | |
{ | |
PrintToConsole(client, "# Key Gain Loss Time Sync"); | |
for(new i = 0; i < g_PlayerStates[client][nStrafes] && i < MAX_STRAFES; i++) | |
{ | |
decl String:strStrafeKey[3]; | |
GetStrafeKey(strStrafeKey, g_PlayerStates[client][StrafeDir][i]); | |
Format(buf, sizeof(buf), "%d %s %6.2f %6.2f %6.2f %6.2f", i + 1, | |
strStrafeKey, | |
g_PlayerStates[client][fStrafeGain][i], g_PlayerStates[client][fStrafeLoss][i], | |
float(g_PlayerStates[client][nStrafeTicks][i]) / g_PlayerStates[client][nTotalTicks] * 100, | |
float(g_PlayerStates[client][nStrafeTicksSynced][i]) / g_PlayerStates[client][nStrafeTicks][i] * 100); | |
PrintToConsole(client, buf); | |
} | |
} | |
DrawPanelTextF(hStatsPanel, " %.2f%%", g_PlayerStates[client][fSync]); | |
if(g_PlayerStates[client][nVerbosity] > 2) | |
{ | |
DrawPanelTextF(hStatsPanel, " %.2f/%.2f", g_PlayerStates[client][fSyncedAngle], g_PlayerStates[client][fTotalAngle]); | |
} | |
DrawPanelTextF(hStatsPanel, " %s", g_PlayerStates[client][bDuck] ? "Duck" : g_PlayerStates[client][bLastDuckState] ? "Partial Duck" : "No Duck"); | |
if(g_PlayerStates[client][bLJEnabled]) | |
{ | |
/*PrintToConsole(client, " %.2f%%", g_PlayerStates[client][fSync]); | |
if(g_nVerbosity > 1) | |
{ | |
PrintToConsole(client, " %.2f/%.2f", g_PlayerStates[client][fSyncedAngle], g_PlayerStates[client][fTotalAngle]); | |
}*/ | |
PrintToConsole(client, " %s", g_PlayerStates[client][bDuck] ? "Duck" : g_PlayerStates[client][bLastDuckState] ? "Partial Duck" : "No Duck"); | |
PrintToConsole(client, ""); // Newline | |
} | |
if(g_PlayerStates[client][bLJEnabled] && g_PlayerStates[client][JumpType] != JT_BHOP && g_PlayerStates[client][IllegalJumpFlags]) | |
{ | |
PrintToConsole(client, "Illegal jump: "); | |
if(g_PlayerStates[client][IllegalJumpFlags] & IJF_WORLD) | |
{ | |
PrintToConsole(client, "Lateral world collision (hit wall/surf)"); | |
} | |
if(g_PlayerStates[client][IllegalJumpFlags] & IJF_BOOSTER) | |
{ | |
PrintToConsole(client, "Booster"); | |
} | |
if(g_PlayerStates[client][IllegalJumpFlags] & IJF_GRAVITY) | |
{ | |
PrintToConsole(client, "Gravity"); | |
} | |
if(g_PlayerStates[client][IllegalJumpFlags] & IJF_TELEPORT) | |
{ | |
PrintToConsole(client, "Teleport"); | |
} | |
if(g_PlayerStates[client][IllegalJumpFlags] & IJF_LAGGEDMOVEMENTVALUE) | |
{ | |
PrintToConsole(client, "Lagged movement value"); | |
} | |
if(g_PlayerStates[client][IllegalJumpFlags] & IJF_PRESTRAFE) | |
{ | |
PrintToConsole(client, "Prestrafe > %.2f", g_fLJMaxPrestrafe); | |
} | |
if(g_PlayerStates[client][IllegalJumpFlags] & IJF_SCOUT) | |
{ | |
PrintToConsole(client, "Scout"); | |
} | |
if(g_PlayerStates[client][IllegalJumpFlags] & IJF_NOCLIP) | |
{ | |
PrintToConsole(client, "noclip"); | |
} | |
PrintToConsole(client, ""); // Newline | |
} | |
if(g_PlayerStates[client][bLJEnabled] && !g_PlayerStates[client][bHidePanel] && g_PlayerStates[client][nVerbosity] > 0 && !(g_PlayerStates[client][bHideBhopPanel] && g_PlayerStates[client][nBhops] > 1)) | |
{ | |
SendPanelToClient(hStatsPanel, client, EmptyPanelHandler, 5); | |
} | |
// Send to spectators of this player | |
for(new i = 1; i <= MaxClients; i++) | |
{ | |
if(IsClientInGame(i) && !IsClientSourceTV(i) && !IsClientReplay(i) && !IsFakeClient(i)) | |
{ | |
if(g_PlayerStates[i][nSpectatorTarget] == client) | |
{ | |
if(g_PlayerStates[i][nVerbosity] > 0 && !g_PlayerStates[i][bHidePanel]) | |
{ | |
SendPanelToClient(hStatsPanel, i, EmptyPanelHandler, 5); | |
} | |
new Handle:hBuffer2 = StartMessageOne("KeyHintText", i); | |
BfWriteByte(hBuffer2, 1); | |
BfWriteString(hBuffer2, g_PlayerStates[client][strHUDHint]); | |
EndMessage(); | |
} | |
} | |
} | |
CloseHandle(hStatsPanel); | |
//// | |
// Print chat message | |
//// | |
if(!g_PlayerStates[client][bLJEnabled] || | |
g_PlayerStates[client][IllegalJumpFlags] != IJF_NONE || | |
g_PlayerStates[client][fHeightDelta] < HEIGHT_DELTA_MIN(JUMP_TYPE:(g_PlayerStates[client][JumpType] == JT_BHOP ? JT_BHOPJUMP : g_PlayerStates[client][JumpType])) || | |
g_PlayerStates[client][bFailedBlock] && !g_bPrintFailedBlockStats) | |
{ | |
return; | |
} | |
if(g_PlayerStates[client][JumpType] == JT_BHOPJUMP && g_PlayerStates[client][fLastJumpHeightDelta] < HEIGHT_DELTA_MIN(JT_BHOPJUMP)) | |
{ | |
return; | |
} | |
switch(g_PlayerStates[client][JumpType]) | |
{ | |
case JT_LONGJUMP, JT_COUNTJUMP: | |
{ | |
new Float:fMin = (g_fLJNoDuckMin != 0.0 && !g_PlayerStates[client][bDuck] && !g_PlayerStates[client][bLastDuckState]) ? g_fLJNoDuckMin : g_fLJMin; | |
if(fMin != 0.0 && g_PlayerStates[client][fJumpDistance] >= fMin) | |
{ | |
OutputJump(client, buf); | |
} | |
if(g_bLJSound && g_PlayerStates[client][bSound]) | |
{ | |
for(new i = 0; i < LJSOUND_NUM; i++) | |
{ | |
if(g_PlayerStates[client][fJumpDistance] >= g_fLJSound[i]) | |
{ | |
if(i == LJSOUND_NUM || g_PlayerStates[client][fJumpDistance] < g_fLJSound[i + 1] || g_fLJSound[i + 1] == 0.0) | |
{ | |
if(g_bLJSoundToAll[i]) | |
{ | |
for(new j = 1; j <= MaxClients; j++) | |
{ | |
if(IsClientInGame(client) && !IsFakeClient(client) && g_PlayerStates[j][bSound]) | |
{ | |
EmitSoundToClient(j, g_strLJSoundFile[i]); | |
} | |
} | |
} | |
else | |
{ | |
EmitSoundToClient(client, g_strLJSoundFile[i]); | |
} | |
break; | |
} | |
} | |
else | |
{ | |
break; | |
} | |
} | |
} | |
} | |
case JT_WEIRDJUMP: | |
{ | |
if(g_fWJMin != 0.0 && g_PlayerStates[client][fJumpDistance] > g_fWJMin && (g_fWJDropMax == 0.0 || g_fWJDropMax >= FloatAbs(g_PlayerStates[client][fLastJumpHeightDelta]))) | |
{ | |
OutputJump(client, buf); | |
} | |
} | |
case JT_BHOPJUMP: | |
{ | |
if(g_fBJMin != 0.0 && g_PlayerStates[client][fJumpDistance] >= g_fBJMin) | |
{ | |
OutputJump(client, buf); | |
} | |
} | |
case JT_LADDERJUMP: | |
{ | |
if(g_fLAJMin != 0.0 && g_PlayerStates[client][fJumpDistance] >= g_fLAJMin) | |
{ | |
OutputJump(client, buf); | |
} | |
} | |
} | |
UpdatePersonalBest(client); | |
LJTopUpdate(client); | |
} | |
public EmptyPanelHandler(Handle:hPanel, MenuAction:ma, Param1, Param2) | |
{ | |
} | |
OutputJump(client, String:buf[1024]) | |
{ | |
new Float:fMin = (g_fLJNoDuckMin != 0.0 && !g_PlayerStates[client][bDuck] && !g_PlayerStates[client][bLastDuckState]) ? g_fLJNoDuckMin : g_fLJMin; | |
new Float:fMinClamp = g_fLJMin ? g_fLJMin : g_fLJNoDuckMin ? g_fLJNoDuckMin : g_fLJClientMin; | |
new bool:bPrintToAll = true; | |
if(g_PlayerStates[client][JumpType] == JT_LONGJUMP && g_fLJClientMin != 0 && g_PlayerStates[client][fJumpDistance] < fMin) | |
{ | |
fMin = g_fLJClientMin; | |
bPrintToAll = false; | |
} | |
if (Shavit_GetStyleSettingBool(Shavit_GetBhopStyle(client), "autobhop")) | |
{ | |
bPrintToAll = false; | |
} | |
if(!g_bOutput16Style) | |
{ | |
decl String:strOutput[512]; | |
decl String:strName[64]; | |
GetClientName(client, strName, sizeof(strName)); | |
Format(strOutput, sizeof(strOutput), "%s {green}%s%sed ", | |
strName, | |
g_PlayerStates[client][JumpType] == JT_BHOPJUMP && g_PlayerStates[client][bStamina] ? "easy" : "", | |
g_strJumpTypeLwr[g_PlayerStates[client][JumpType]]); | |
if(g_PlayerStates[client][JumpType] == JT_LONGJUMP) | |
{ | |
new nColor[3]; | |
for(new i; i < 3; i++) | |
nColor[i] = RoundFloat((MIN(MAX(g_PlayerStates[client][fJumpDistance], fMinClamp), g_fLJMax) - fMinClamp) / | |
(g_fLJMax - fMinClamp) * (g_ColorMax[i] - g_ColorMin[i]) + g_ColorMin[i]); | |
Format(buf, sizeof(buf), "\x07%02X%02X%02X", | |
nColor[0], nColor[1], nColor[2]); | |
StrCat(strOutput, sizeof(strOutput), buf); | |
} | |
Format(buf, sizeof(buf), "%.2f{green} units", g_PlayerStates[client][fJumpDistance]); | |
StrCat(strOutput, sizeof(strOutput), buf); | |
if(g_PlayerStates[client][JumpDir] != JD_FORWARDS) | |
{ | |
if(g_PlayerStates[client][JumpDir] == JD_SIDEWAYS) | |
{ | |
StrCat(strOutput, sizeof(strOutput), " sideways"); | |
} | |
else if(g_PlayerStates[client][JumpDir] == JD_BACKWARDS) | |
{ | |
StrCat(strOutput, sizeof(strOutput), " backwards"); | |
} | |
} | |
if(!g_PlayerStates[client][bDuck] && !g_PlayerStates[client][bLastDuckState]) | |
{ | |
StrCat(strOutput, sizeof(strOutput), " no duck"); | |
} | |
if(g_PlayerStates[client][bBlockMode]) | |
{ | |
if(g_PlayerStates[client][fBlockDistance] != -1.0) | |
{ | |
Format(buf, sizeof(buf), " @ %.1f block%s", g_PlayerStates[client][fBlockDistance], g_PlayerStates[client][bFailedBlock] ? " (failed)" : ""); | |
StrCat(strOutput, sizeof(strOutput), buf); | |
} | |
else if(g_PlayerStates[client][bFailedBlock]) | |
{ | |
StrCat(strOutput, sizeof(strOutput), " @ ? block (failed)"); | |
} | |
} | |
StrCat(strOutput, sizeof(strOutput), "!"); | |
if(g_nVerbosity > 1) | |
{ | |
Format(buf, sizeof(buf), " ({lightblue}%.2f{green}, {lightblue}%d{green} @ {lightblue}%d%%{green}, {lightblue}%d{green}", | |
g_PlayerStates[client][fPrestrafe], g_PlayerStates[client][nStrafes], RoundFloat(g_PlayerStates[client][fSync]), RoundFloat(g_PlayerStates[client][fMaxSpeed])); | |
StrCat(strOutput, sizeof(strOutput), buf); | |
} | |
else if(g_nVerbosity > 0) | |
{ | |
Format(buf, sizeof(buf), " ({lightblue}%.2f{green}, {lightblue}%d{green} @ {lightblue}%d%%{green}", | |
g_PlayerStates[client][fPrestrafe], g_PlayerStates[client][nStrafes], RoundFloat(g_PlayerStates[client][fSync])); | |
StrCat(strOutput, sizeof(strOutput), buf); | |
} | |
if(g_PlayerStates[client][bBlockMode] && g_PlayerStates[client][fBlockDistance] != -1.0 && g_PlayerStates[client][fEdge] != -1.0) | |
{ | |
Format(buf, sizeof(buf), ", edge: {lightblue}%.2f{default}", g_PlayerStates[client][fEdge]); | |
StrCat(strOutput, sizeof(strOutput), buf); | |
} | |
StrCat(strOutput, sizeof(strOutput), ")"); | |
if(bPrintToAll) | |
{ | |
CPrintToChatAll("%s", strOutput); | |
} | |
else | |
{ | |
CPrintToChat(client, "%s", strOutput); | |
} | |
} | |
else | |
{ | |
decl String:strOutput[512]; | |
if(g_PlayerStates[client][fJumpDistance] < (g_PlayerStates[client][JumpType] == JT_LONGJUMP ? 265.0 : g_PlayerStates[client][JumpType] == JT_LADDERJUMP ? 155.0 : 285.0)) | |
{ | |
strcopy(strOutput, sizeof(strOutput), "{white}"); | |
} | |
else if(g_PlayerStates[client][fJumpDistance] < (g_PlayerStates[client][JumpType] == JT_LONGJUMP ? 268.0 : g_PlayerStates[client][JumpType] == JT_LADDERJUMP ? 165.0 : 295.0)) | |
{ | |
strcopy(strOutput, sizeof(strOutput), "{green}"); | |
} | |
else | |
{ | |
strcopy(strOutput, sizeof(strOutput), "{red}"); | |
} | |
decl String:strName[64]; | |
GetClientName(client, strName, sizeof(strName)); | |
Format(buf, sizeof(buf), "%s jumped %.2f units", | |
strName, g_PlayerStates[client][fJumpDistance]); | |
StrCat(strOutput, sizeof(strOutput), buf); | |
if(g_PlayerStates[client][JumpType]) | |
{ | |
Format(buf, sizeof(buf), " with %s%s", | |
g_PlayerStates[client][JumpType] == JT_BHOPJUMP && g_PlayerStates[client][bStamina] ? "easy" : "", | |
g_strJumpTypeLwr[g_PlayerStates[client][JumpType]]); | |
StrCat(strOutput, sizeof(strOutput), buf); | |
} | |
if(g_PlayerStates[client][JumpDir] != JD_FORWARDS) | |
{ | |
if(g_PlayerStates[client][JumpDir] == JD_SIDEWAYS) | |
{ | |
StrCat(strOutput, sizeof(strOutput), " sideways"); | |
} | |
else if(g_PlayerStates[client][JumpDir] == JD_BACKWARDS) | |
{ | |
StrCat(strOutput, sizeof(strOutput), " backwards"); | |
} | |
} | |
if(!g_PlayerStates[client][bDuck] && !g_PlayerStates[client][bLastDuckState]) | |
{ | |
StrCat(strOutput, sizeof(strOutput), " no duck"); | |
} | |
if(g_PlayerStates[client][bBlockMode]) | |
{ | |
if(g_PlayerStates[client][fBlockDistance] != -1.0) | |
{ | |
Format(buf, sizeof(buf), " @ %.1f block%s", g_PlayerStates[client][fBlockDistance], g_PlayerStates[client][bFailedBlock] ? " (failed)" : ""); | |
StrCat(strOutput, sizeof(strOutput), buf); | |
} | |
else if(g_PlayerStates[client][bFailedBlock]) | |
{ | |
StrCat(strOutput, sizeof(strOutput), " @ ? block (failed)"); | |
} | |
} | |
StrCat(strOutput, sizeof(strOutput), "!"); | |
if(g_nVerbosity > 2) | |
{ | |
Format(buf, sizeof(buf), " (%.2f, %d @ %d%%, %d", | |
g_PlayerStates[client][fPrestrafe], g_PlayerStates[client][nStrafes], RoundFloat(g_PlayerStates[client][fSync]), RoundFloat(g_PlayerStates[client][fMaxSpeed])); | |
StrCat(strOutput, sizeof(strOutput), buf); | |
} | |
else if(g_nVerbosity > 1) | |
{ | |
Format(buf, sizeof(buf), " (%.2f, %d @ %d%%", | |
g_PlayerStates[client][fPrestrafe], g_PlayerStates[client][nStrafes], RoundFloat(g_PlayerStates[client][fSync])); | |
StrCat(strOutput, sizeof(strOutput), buf); | |
} | |
if(g_PlayerStates[client][bBlockMode] && g_PlayerStates[client][fBlockDistance] != -1.0 && g_PlayerStates[client][fEdge] != -1.0) | |
{ | |
Format(buf, sizeof(buf), ", edge: %.2f", g_PlayerStates[client][fEdge]); | |
StrCat(strOutput, sizeof(strOutput), buf); | |
} | |
StrCat(strOutput, sizeof(strOutput), ")"); | |
if(bPrintToAll) | |
{ | |
CPrintToChatAll("%s", strOutput); | |
} | |
else | |
{ | |
CPrintToChat(client, "%s", strOutput); | |
} | |
} | |
} | |
/////////////////////////////////// | |
/////////////////////////////////// | |
//////// //////// | |
//////// Trace functions //////// | |
//////// //////// | |
/////////////////////////////////// | |
/////////////////////////////////// | |
#define RAYTRACE_Z_DELTA -0.1 | |
#define GAP_TRACE_LENGTH 10000.0 | |
public bool:WorldFilter(entity, mask) | |
{ | |
if (entity >= 1 && entity <= MaxClients) | |
return false; | |
return true; | |
} | |
bool:TracePlayer(Float:vEndPos[3], Float:vNormal[3], const Float:vTraceOrigin[3], const Float:vEndPoint[3], bool:bCorrectError = true) | |
{ | |
new Float:vMins[3] = {-16.0, -16.0, 0.0}, Float:vMaxs[3] = {16.0, 16.0, 0.0}; | |
TR_TraceHullFilter(vTraceOrigin, vEndPoint, vMins, vMaxs, MASK_PLAYERSOLID, WorldFilter); | |
if(!TR_DidHit()) // although tracehull does not ever seem to not hit (merely returning a hit at the end of the line), I'm keeping this here just in case, I guess | |
{ | |
return false; | |
} | |
TR_GetEndPosition(vEndPos); | |
TR_GetPlaneNormal(INVALID_HANDLE, vNormal); | |
// correct slopes | |
if(vNormal[2]) | |
{ | |
vNormal[2] = 0.0; | |
NormalizeVector(vNormal, vNormal); | |
} | |
#if defined DEBUG | |
new Float:v1[3], Float:v2[3]; | |
v1 = vEndPos; | |
v2 = vEndPos; | |
v1[0] += 16.0; | |
v1[1] += 16.0; | |
v2[0] += 16.0; | |
v2[1] -= 16.0; | |
CreateBeam2(v1, v2, 0, 255, 0); | |
v1[0] -= 32.0; | |
v1[1] -= 32.0; | |
CreateBeam2(v1, v2, 0, 255, 0); | |
v2[0] -= 32.0; | |
v2[1] += 32.0; | |
CreateBeam2(v1, v2, 0, 255, 0); | |
v1[0] += 32.0; | |
v1[1] += 32.0; | |
CreateBeam2(v1, v2, 0, 255, 0); | |
#endif | |
Adjust(vEndPos, vNormal); | |
// dunno where this error comes from | |
if(bCorrectError) | |
{ | |
vEndPos[0] -= vNormal[0] * 0.03125; | |
vEndPos[1] -= vNormal[1] * 0.03125; | |
} | |
new Float:fDist = GetVectorDistance(vTraceOrigin, vEndPos); | |
return fDist != 0.0 && fDist < GetVectorDistance(vTraceOrigin, vEndPoint); | |
} | |
// no function overloading... @__@ | |
bool:TracePlayer2(Float:vEndPos[3], const Float:vTraceOrigin[3], const Float:vEndPoint[3], bool:bCorrectError = true) | |
{ | |
new Float:vNormal[3]; | |
return TracePlayer(vEndPos, vNormal, vTraceOrigin, vEndPoint, bCorrectError); | |
} | |
bool:TraceRay(Float:vEndPos[3], Float:vNormal[3], const Float:vTraceOrigin[3], const Float:vEndPoint[3], bool:bCorrectError = true) | |
{ | |
TR_TraceRayFilter(vTraceOrigin, vEndPoint, MASK_PLAYERSOLID, RayType_EndPoint, WorldFilter); | |
if(!TR_DidHit()) | |
{ | |
return false; | |
} | |
TR_GetEndPosition(vEndPos); | |
TR_GetPlaneNormal(INVALID_HANDLE, vNormal); | |
// correct slopes | |
if(vNormal[2]) | |
{ | |
vNormal[2] = 0.0; | |
NormalizeVector(vNormal, vNormal); | |
} | |
if(bCorrectError) | |
{ | |
vEndPos[0] -= vNormal[0] * 0.03125; | |
vEndPos[1] -= vNormal[1] * 0.03125; | |
} | |
new Float:fDist = GetVectorDistance(vTraceOrigin, vEndPos); | |
return fDist != 0.0 && fDist < GetVectorDistance(vTraceOrigin, vEndPoint); | |
} | |
bool:TraceRay2(Float:vEndPos[3], const Float:vTraceOrigin[3], const Float:vEndPoint[3], bool:bCorrectError = true) | |
{ | |
new Float:vNormal[3]; | |
return TraceRay(vEndPos, vNormal, vTraceOrigin, vEndPoint, bCorrectError); | |
} | |
bool:IsLeft(const Float:vDir[3], const Float:vNormal[3]) | |
{ | |
if(vNormal[1] > 0) | |
{ | |
if(vDir[0] > vNormal[0]) | |
{ | |
return true; | |
} | |
else | |
{ | |
return false; | |
} | |
} | |
else | |
{ | |
if(vDir[0] > vNormal[0]) | |
{ | |
return false; | |
} | |
else | |
{ | |
return true; | |
} | |
} | |
} | |
// align with normal | |
Align(Float:vOut[3], const Float:v1[3], const Float:v2[3], const Float:vNormal[3]) | |
{ | |
// cardinal | |
if(!vNormal[0] || !vNormal[1]) | |
{ | |
if(vNormal[0]) | |
{ | |
vOut[0] = v2[0]; | |
vOut[1] = v1[1]; | |
} | |
else | |
{ | |
vOut[0] = v1[0]; | |
vOut[1] = v2[1]; | |
} | |
return; | |
} | |
// noncardinal | |
// rotate to cardinal, perform the same operation, rotate the result back | |
// [ cos(t) -sin(t) 0 ] | |
// Rz = [ sin(t) cos(t) 0 ] | |
// [ 0 0 1 ] | |
new Float:vTo[3] = {1.0, 0.0}, Float:fAngle = ArcCosine(GetVectorDotProduct(vNormal, vTo)), Float:fRotatedOriginY, Float:vRotatedEndPos[2]; | |
if(IsLeft(vTo, vNormal)) | |
{ | |
fAngle = -fAngle; | |
} | |
fRotatedOriginY = v1[0] * Sine(fAngle) + v1[1] * Cosine(fAngle); | |
vRotatedEndPos[0] = v2[0] * Cosine(fAngle) - v2[1] * Sine(fAngle); | |
vRotatedEndPos[1] = fRotatedOriginY; | |
fAngle = -fAngle; | |
vOut[0] = vRotatedEndPos[0] * Cosine(fAngle) - vRotatedEndPos[1] * Sine(fAngle); | |
vOut[1] = vRotatedEndPos[0] * Sine(fAngle) + vRotatedEndPos[1] * Cosine(fAngle); | |
} | |
// Adjust collision hitbox center to periphery (the furthest point you could be from the edge as inferred by the normal) | |
Adjust(Float:vOrigin[3], const Float:vNormal[3]) | |
{ | |
// cardinal | |
if(!vNormal[0] || !vNormal[1]) | |
{ | |
vOrigin[0] -= vNormal[0] * 16.0; | |
vOrigin[1] -= vNormal[1] * 16.0; | |
return; | |
} | |
// noncardinal | |
// since the corner will always be the furthest point, set it to the corner of the normal's quadrant | |
if(vNormal[0] > 0.0) | |
{ | |
vOrigin[0] -= 16.0; | |
} | |
else | |
{ | |
vOrigin[0] += 16.0; | |
} | |
if(vNormal[1] > 0.0) | |
{ | |
vOrigin[1] -= 16.0; | |
} | |
else | |
{ | |
vOrigin[1] += 16.0; | |
} | |
} | |
Float:GetEdge(client) | |
{ | |
new Float:vOrigin[3], Float:vTraceOrigin[3], Float:vDir[3]; | |
GetClientAbsOrigin(client, vOrigin); | |
GetEntPropVector(client, Prop_Data, "m_vecVelocity", vDir); | |
NormalizeVector(vDir, vDir); | |
vTraceOrigin = vOrigin; | |
vTraceOrigin[0] += vDir[0] * 64.0; | |
vTraceOrigin[1] += vDir[1] * 64.0; | |
vTraceOrigin[2] += RAYTRACE_Z_DELTA; | |
new Float:vEndPoint[3]; | |
vEndPoint = vOrigin; | |
vEndPoint[0] -= vDir[0] * 16.0 * 1.414214; | |
vEndPoint[1] -= vDir[1] * 16.0 * 1.414214; | |
vEndPoint[2] += RAYTRACE_Z_DELTA; | |
new Float:vEndPos[3], Float:vNormal[3]; | |
if(!TracePlayer(vEndPos, vNormal, vTraceOrigin, vEndPoint)) | |
{ | |
return -1.0; | |
} | |
#if defined DEBUG | |
CreateLightglow("0 255 0", vOrigin); | |
#endif | |
Adjust(vOrigin, vNormal); | |
#if defined DEBUG | |
CreateLightglow("255 255 255", vEndPos); | |
CreateLightglow("255 0 0", vOrigin); | |
#endif | |
Align(vEndPos, vOrigin, vEndPos, vNormal); | |
#if defined DEBUG | |
CreateLightglow("0 0 255", vEndPos); | |
#endif | |
// Correct Z -- the trace ray is a bit lower | |
vEndPos[2] = vOrigin[2]; | |
return GetVectorDistance(vEndPos, vOrigin); | |
} | |
Float:GetBlockDistance(client) | |
{ | |
decl Float:vOrigin[3], Float:vTraceOrigin[3], Float:vDir[3], Float:vEndPoint[3]; | |
GetClientAbsOrigin(client, vOrigin); | |
GetEntPropVector(client, Prop_Data, "m_vecVelocity", vDir); | |
NormalizeVector(vDir, vDir); | |
vTraceOrigin = vOrigin; | |
vTraceOrigin[0] += vDir[0] * 64.0; | |
vTraceOrigin[1] += vDir[1] * 64.0; | |
vTraceOrigin[2] += RAYTRACE_Z_DELTA; | |
vEndPoint = vOrigin; | |
vEndPoint[0] -= vDir[0] * 16.0 * 1.414214; | |
vEndPoint[1] -= vDir[1] * 16.0 * 1.414214; | |
vEndPoint[2] += RAYTRACE_Z_DELTA; | |
new Float:vBlockStart[3], Float:vNormal[3]; | |
if(!TracePlayer(vBlockStart, vNormal, vTraceOrigin, vEndPoint)) | |
{ | |
return -1.0; | |
} | |
new Float:vBlockEnd[3]; | |
Array_Copy(vNormal, g_PlayerStates[client][vBlockNormal], 2); | |
vEndPoint = vBlockStart; | |
vEndPoint[0] += vNormal[0] * 300.0; | |
vEndPoint[1] += vNormal[1] * 300.0; | |
if(TracePlayer2(vBlockEnd, vBlockStart, vEndPoint)) | |
{ | |
Array_Copy(vBlockEnd, g_PlayerStates[client][vBlockEndPos], 3); | |
Align(vBlockEnd, vBlockStart, vBlockEnd, vNormal); | |
if(vNormal[0] == 0.0 || vNormal[1] == 0.0) | |
{ | |
return GetVectorDistance(vBlockStart, vBlockEnd); | |
} | |
else | |
{ | |
return GetVectorDistance(vBlockStart, vBlockEnd) - 32.0 * (FloatAbs(vNormal[0]) + FloatAbs(vNormal[1]) - 1.0); | |
} | |
} | |
else | |
{ | |
// Trace the other direction | |
// rotate normal da way opposite da direction | |
new bool:bLeft = IsLeft(vDir, vNormal); | |
vDir = vNormal; | |
new Float:fTempSwap = vDir[0]; | |
vDir[0] = vDir[1]; | |
vDir[1] = fTempSwap; | |
if(bLeft) | |
{ | |
vDir[0] = -vDir[0]; | |
} | |
else | |
{ | |
vDir[1] = -vDir[1]; | |
} | |
vTraceOrigin = vOrigin; | |
vTraceOrigin[0] += vDir[0] * 48.0; | |
vTraceOrigin[1] += vDir[1] * 48.0; | |
vTraceOrigin[2] += RAYTRACE_Z_DELTA; | |
vEndPoint = vTraceOrigin; | |
vEndPoint[0] += vNormal[0] * 300.0; | |
vEndPoint[1] += vNormal[1] * 300.0; | |
if(!TracePlayer2(vBlockEnd, vTraceOrigin, vEndPoint)) | |
{ | |
return -1.0; | |
} | |
Array_Copy(vBlockEnd, g_PlayerStates[client][vBlockEndPos], 3); | |
// adjust vBlockStart -- the second trace was on a different axis | |
Align(vBlockStart, vBlockStart, vBlockEnd, vNormal); | |
if(vNormal[0] == 0.0 || vNormal[1] == 0.0) | |
{ | |
return GetVectorDistance(vBlockStart, vBlockEnd); | |
} | |
else | |
{ | |
return GetVectorDistance(vBlockStart, vBlockEnd) - 32.0 * (FloatAbs(vNormal[0]) + FloatAbs(vNormal[1]) - 1.0); | |
} | |
} | |
} | |
bool:GetGapPoint(Float:vOut[3], Float:vNormal[3], client) | |
{ | |
decl Float:vAngles[3], Float:vTraceOrigin[3], Float:vDir[3], Float:vEndPoint[3]; | |
GetClientEyePosition(client, vTraceOrigin); | |
GetClientEyeAngles(client, vAngles); | |
TBAnglesToUV(vDir, vAngles); | |
vEndPoint = vTraceOrigin; | |
vEndPoint[0] += vDir[0] * GAP_TRACE_LENGTH; | |
vEndPoint[1] += vDir[1] * GAP_TRACE_LENGTH; | |
vEndPoint[2] += vDir[2] * GAP_TRACE_LENGTH; | |
if(!TraceRay(vOut, vNormal, vTraceOrigin, vEndPoint)) | |
{ | |
return false; | |
} | |
#if defined DEBUG | |
CreateBeam(vTraceOrigin, vEndPoint); | |
#endif | |
return true; | |
} | |
bool:GetOppositePoint(Float:vOut[3], const Float:vTraceOrigin[3], const Float:vNormal[3]) | |
{ | |
decl Float:vDir[3], Float:vEndPoint[3]; | |
vDir = vNormal; | |
if(vDir[2]) | |
{ | |
vDir[2] = 0.0; | |
NormalizeVector(vDir, vDir); | |
} | |
vEndPoint = vTraceOrigin; | |
vEndPoint[0] += vDir[0] * 10000.0; | |
vEndPoint[1] += vDir[1] * 10000.0; | |
if(!TraceRay2(vOut, vTraceOrigin, vEndPoint)) | |
{ | |
return false; | |
} | |
return true; | |
} | |
// generic utility functions | |
Float:GetSpeed(client) | |
{ | |
new Float:vVelocity[3]; | |
GetEntPropVector(client, Prop_Data, "m_vecVelocity", vVelocity); | |
vVelocity[2] = 0.0; | |
return GetVectorLength(vVelocity); | |
} | |
Float:GetVSpeed(const Float:v[3]) | |
{ | |
new Float:vVelocity[3]; | |
vVelocity = v; | |
vVelocity[2] = 0.0; | |
return GetVectorLength(vVelocity); | |
} | |
SendPanelMsg(client, const String:strFormat[], any:...) | |
{ | |
new Handle:hPanel = CreatePanel(); | |
decl String:buf[512]; | |
VFormat(buf, sizeof(buf), strFormat, 3); | |
SetPanelTitle(hPanel, buf); | |
SendPanelToClient(hPanel, client, EmptyPanelHandler, 10); | |
CloseHandle(hPanel); | |
} | |
DrawPanelTextF(Handle:hPanel, const String:strFormat[], any:...) | |
{ | |
decl String:buf[512]; | |
VFormat(buf, sizeof(buf), strFormat, 3); | |
DrawPanelText(hPanel, buf); | |
} | |
Append(String:sOutput[], maxlen, const String:sFormat[], any:...) | |
{ | |
decl String:buf[1024]; | |
VFormat(buf, sizeof(buf), sFormat, 4); | |
StrCat(sOutput, maxlen, buf); | |
} | |
// undefined for negative numbers | |
Float:fmod(Float:a, Float:b) | |
{ | |
while(a > b) | |
a -= b; | |
return a; | |
} | |
stock Float:round(Float:a, b, Float:Base = 10.0) | |
{ | |
new Float:f = Pow(Base, float(b)); | |
return RoundFloat(a * f) / f; | |
} | |
CreateBeamClient(client, const Float:v1[3], const Float:v2[3], r = 255, g = 255, b = 255, Float:fLifetime = 10.0) | |
{ | |
new color[4]; | |
color[0] = r; | |
color[1] = g; | |
color[2] = b; | |
color[3] = 100; | |
TE_SetupBeamPoints(v1, v2, g_BeamModel, 0, 0, 0, fLifetime, 10.0, 10.0, 10, 0.0, color, 0); | |
TE_SendToClient(client); | |
} | |
#if defined DEBUG | |
CreateLightglow(const String:sColor[], const Float:vOrigin[3]) | |
{ | |
new Lightglow = CreateEntityByName("env_lightglow"); | |
SetEntPropVector(Lightglow, Prop_Data, "m_vecOrigin", vOrigin); | |
DispatchKeyValue(Lightglow,"rendercolor", sColor); | |
DispatchKeyValue(Lightglow,"GlowProxySize", "5"); | |
DispatchKeyValue(Lightglow,"VerticalGlowSize", "5"); | |
DispatchKeyValue(Lightglow,"HorizontalGlowSize", "5"); | |
DispatchSpawn(Lightglow); | |
CreateTimer(10.0, KillEntity, Lightglow); | |
} | |
CreateBeam(const Float:v1[3], const Float:v2[3]) | |
{ | |
new color[4] = {255, 255, 255, 100}; | |
TE_SetupBeamPoints(v1, v2, g_BeamModel, 0, 0, 0, 10.0, 3.0, 3.0, 10, 0.0, color, 0); | |
TE_SendToAll(); | |
} | |
CreateBeam2(const Float:v1[3], const Float:v2[3], r, g, b) | |
{ | |
new color[4]; | |
color[0] = r; | |
color[1] = g; | |
color[2] = b; | |
color[3] = 255; | |
TE_SetupBeamPoints(v1, v2, g_BeamModel, 0, 0, 0, 10.0, 10.0, 10.0, 10, 0.0, color, 0); | |
TE_SendToAll(); | |
} | |
public Action:KillEntity(Handle:timer, any:entity) | |
{ | |
AcceptEntityInput(entity, "Kill"); | |
} | |
#endif |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment