#include #include #include #include #include #include //#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 MAX_JUMP_TICKS 132 // 2 sec #define LJTOP_DIR "configs/ljstats/" #define LJTOP_FILE "ljtop.txt" #define LJTOP_NUM_ENTRIES 1024 #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 JB_HEIGHT_DELTA_MIN -1.0 #define JB_HEIGHT_DELTA_MAX 1.5 #define HUD_HINT_SIZE 256 #define STRAFE_TRAINER_TICKS 9 public Plugin:myinfo = { name = "ljstats", author = "Miu, maintained and updated by networkheaven.net", 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:bStrafeTrainer, bool:bBeam, bool:bDeadstrafe, bool:bSound, bool:bSyncStats, bool:bBlockMode, nVerbosity, bool:bShowAllJumps, bool:bShowPrestrafeHint, nSpeedometer, Float:fBlockDistance, Float:vBlockNormal[2], Float:vBlockEndPos[3], bool:bFailedBlock, bool:bDuck, bool:bLastDuckState, bool:bSecondLastDuckState, bool:bJumpBug, bool:bDuckJump, JUMP_DIRECTION:JumpDir, ILLEGAL_JUMP_FLAGS:IllegalJumpFlags, JUMP_TYPE:LastJumpType, JUMP_TYPE:JumpType, Float:fLandTime, Float:fLastJumpHeightDelta, nBhops, bool:bOnGround, bool:bOnLadder, bool:bPerf, Float:fEdge, Float:vJumpOrigin[3], Float:fJumpSpeed, 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], nMoveDir[MAX_JUMP_TICKS], nMouseDir[MAX_JUMP_TICKS], nTotalTicks, Float:fTotalAngle, Float:fSyncedAngle, Float:fStrafePercentages[STRAFE_TRAINER_TICKS], nTrainerTicks, bool:bStamina, nJumpTick, nLastAerialTick, Float:vLastOrigin[3], Float:vLastAngles[3], Float:vLastVelocity[3], Float:vTPOrigin[3], Float:vTPAngles[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_JUMPBUG, 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_SWLJ, LT_BWLJ, LT_CJ, LT_BJ, LT_LAJ, LT_WJ, LT_JB, LT_END } new String:g_strLJTopTags[LT_END][] = { "lj", "swlj", "bwlj", "cj", "bj", "laj", "wj", "jb" }; new String:g_strLJTopTableName[LT_END][] = { "Longjump", "Sideways longjump", "Backwards longjump", "Countjump", "Bhopjump", "Ladderjump", "Weirdjump", "Jumpbug" }; new String:g_strLJTopOutput[LT_END][] = { "lj", "sideways lj", "backwards lj", "countjump", "bhopjump", "ladderjump", "weirdjump", "jumpbug" }; new String:g_strJumpType[JT_END][] = { "Longjump", "Countjump", "Jumpbug", "Weirdjump", "Bhopjump", "Ladderjump", "Bhop", "Drop" }; new String:g_strJumpTypeLwr[JT_END][] = { "longjump", "countjump", "jumpbug", "weirdjump", "bhopjump", "ladderjump", "bhop", "drop" }; new String:g_strJumpTypeShort[JT_END][] = { "LJ", "CJ", "JB", "WJ", "BJ", "LAJ", "Bhop", "Drop" }; new const Float:g_fHeightDeltaMin[JT_END] = { LJ_HEIGHT_DELTA_MIN, LJ_HEIGHT_DELTA_MIN, JB_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, JB_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_hCvarColorTop = INVALID_HANDLE; new Handle:g_hCvarLJMin = INVALID_HANDLE; new Handle:g_hCvarLJMax = INVALID_HANDLE; new Handle:g_hCvarNonLJMax = 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_hCvarBJMax = 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_hCvarBJSound1 = INVALID_HANDLE; new Handle:g_hCvarBJSound2 = INVALID_HANDLE; new Handle:g_hCvarBJSound3 = INVALID_HANDLE; new Handle:g_hCvarBJSound4 = INVALID_HANDLE; new Handle:g_hCvarBJSound5 = 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:nh_warmup = 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_hCookieDeadstrafe = 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; new Handle:g_hCookieShowPrestrafeHint = INVALID_HANDLE; new Handle:g_hCookiePersonalBest = INVALID_HANDLE; new Handle:g_hCookieStrafeTrainer = INVALID_HANDLE; new Handle:g_hCookieSpeedometer = INVALID_HANDLE; new Handle:g_hCookieSyncStats = INVALID_HANDLE; new g_ColorMin[3] = {0xAD, 0xD8, 0xE6}; // Lightblue! new g_ColorMax[3] = {0x00, 0x00, 0xFF}; new g_ColorTop[3] = {0xFF, 0xFF, 0x00}; new Float:g_fLJMin = 260.0; new Float:g_fLJMax = 275.0; new Float:g_fNonLJMax = 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 = 48.0; new Float:g_fBJMin = 270.0; new Float:g_fBJMax = 265.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 Float:g_fBJSound[5] = {250.0, 255.0, 260.0, 265.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_hCvarColorTop = CreateCvar("ljstats_color_top", "FFFF00"); g_hCvarLJMin = CreateCvar("ljstats_lj_min", "260"); g_hCvarLJMax = CreateCvar("ljstats_lj_max", "275"); g_hCvarNonLJMax = CreateCvar("ljstats_nonlj_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", "48.0"); g_hCvarBJMin = CreateCvar("ljstats_bj_min", "270"); g_hCvarBJMax = CreateCvar("ljstats_bj_max", "265"); 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_hCvarBJSound1 = CreateCvar("ljstats_bj_sound1", "250"); g_hCvarBJSound2 = CreateCvar("ljstats_bj_sound2", "255"); g_hCvarBJSound3 = CreateCvar("ljstats_bj_sound3", "260"); g_hCvarBJSound4 = CreateCvar("ljstats_bj_sound4", "265"); g_hCvarBJSound5 = CreateCvar("ljstats_bj_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); HookEvent("player_death", Event_PlayerDeath); HookEvent("player_spawn", Event_PlayerSpawn); RegConsoleCmd("sm_ljhelp", Command_LJHelp); RegConsoleCmd("sm_lj", Command_LJSettings); RegConsoleCmd("sm_ljsettings", Command_LJSettings); RegConsoleCmd("sm_ljs", Command_LJSettings); RegConsoleCmd("sm_ljpanel", Command_LJPanel); RegConsoleCmd("sm_ljbeam", Command_LJBeam); RegConsoleCmd("sm_ljdeadstrafe", Command_LJDeadstrafe); 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_syncstats", Command_SyncStats); RegConsoleCmd("sm_ljtop", Command_LJTop); #if defined LJSERV RegConsoleCmd("sm_wr", Command_LJTop); #endif RegConsoleCmd("sm_tpmenu", Command_CheckpointPanel); RegAdminCmd("sm_ljtopdelete", Command_LJTopDelete, ADMFLAG_RCON); RegAdminCmd("sm_kz", Command_KZ, ADMFLAG_RCON); RegConsoleCmd("sm_gap", Command_Gap); RegConsoleCmd("sm_strafetrainer", Command_StrafeTrainer); RegConsoleCmd("sm_speed", Command_Speedometer); 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_hCookieDeadstrafe = RegClientCookie("ljstats_deadstrafe", "ljstats_deadstrafe", 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); g_hCookieShowPrestrafeHint = RegClientCookie("ljstats_showprestrafehint", "ljstats_showprestrafehint", CookieAccess_Public); g_hCookiePersonalBest = RegClientCookie("ljstats_personalbest", "ljstats_personalbest", CookieAccess_Private); g_hCookieStrafeTrainer = RegClientCookie("ljstats_strafetrainer", "ljstats_strafetrainer", CookieAccess_Private); g_hCookieSpeedometer = RegClientCookie("ljstats_speedometer", "ljstats_speedometer", CookieAccess_Private); g_hCookieSyncStats = RegClientCookie("ljstats_syncstats", "ljstats_syncstats", 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); continue; } 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; } else if(hCvar == g_hCvarColorTop) { new nColor = StringToInt(strNewValue, 16); g_ColorTop[0] = (nColor & 0xFF0000) >> 16; g_ColorTop[1] = (nColor & 0xFF00) >> 8; g_ColorTop[2] = nColor & 0xFF; } ON_CVAR_CHANGE_FLOAT(g_hCvarLJMin, g_fLJMin) ON_CVAR_CHANGE_FLOAT(g_hCvarLJMax, g_fLJMax) ON_CVAR_CHANGE_FLOAT(g_hCvarNonLJMax, g_fNonLJMax) 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_hCvarBJMax, g_fBJMax) 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_FLOAT(g_hCvarBJSound1, g_fBJSound[0]) ON_CVAR_CHANGE_FLOAT(g_hCvarBJSound2, g_fBJSound[1]) ON_CVAR_CHANGE_FLOAT(g_hCvarBJSound3, g_fBJSound[2]) ON_CVAR_CHANGE_FLOAT(g_hCvarBJSound4, g_fBJSound[3]) ON_CVAR_CHANGE_FLOAT(g_hCvarBJSound5, g_fBJSound[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() { if( nh_warmup == INVALID_HANDLE ) { nh_warmup = FindConVar( "nh_warmup" ); } 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] = true; #endif 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][bHidePanel] = true; g_PlayerStates[client][bLJEnabled] = true; 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; g_PlayerStates[client][vTPOrigin][0] = 0.0; g_PlayerStates[client][vTPOrigin][1] = 0.0; g_PlayerStates[client][vTPOrigin][2] = 0.0; /* #if defined LJSERV g_PlayerStates[client][bShowPrestrafeHint] = true; #endif */ g_PlayerStates[client][bShowAllJumps] = true; 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_SyncStats( client, args ) { if ( g_PlayerStates[client][bSyncStats] ) { g_PlayerStates[client][bSyncStats] = false; PrintToChat( client, "\x04Sync stats are now : DISABLED" ); } else { g_PlayerStates[client][bSyncStats] = true; PrintToChat( client, "\x04Sync stats are now : ENABLED" ); } SetCookie( client, g_hCookieSyncStats, g_PlayerStates[client][bSyncStats] ); 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, "!strafetrainer"); 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 SpeedometerMenuHandler( Handle:hMenu, MenuAction:ma, client, nItem ) { switch( ma ) { case MenuAction_Select: { switch( nItem ) { case 0: { g_PlayerStates[client][nSpeedometer] = nItem; PrintToChat( client, "\x04Speedometer has been disabled." ); } case 1: { g_PlayerStates[client][nSpeedometer] = nItem; PrintToChat( client, "\x04Speedometer will be displayed at the top." ); } case 2: { g_PlayerStates[client][nSpeedometer] = nItem; PrintToChat( client, "\x04Speedometer will be displayed at the bottom." ); } } SetCookie(client, g_hCookieSpeedometer, g_PlayerStates[client][nSpeedometer]); } case MenuAction_Cancel: { CloseHandle( hMenu ); } } } public Action:Command_Speedometer(client, args) { new Handle:hPanel = CreateMenu(SpeedometerMenuHandler); AddMenuItem( hPanel, "nospd", "no speedometer" ); AddMenuItem( hPanel, "top", "show at the top" ); AddMenuItem( hPanel, "bot", "show at the bottom" ); DisplayMenu( hPanel, client, MENU_TIME_FOREVER ); } public OnClientCookiesCached(client) { decl String:strCookie[64]; GetClientCookie(client, g_hCookieDefaultsSet, strCookie, sizeof(strCookie)); if(StringToInt(strCookie) == 0) { SetCookie(client, g_hCookieLJEnabled, true); SetCookie(client, g_hCookieSound, g_bLJSound); SetCookie(client, g_hCookieShowBhopStats, g_bShowBhopStats); SetCookie(client, g_hCookieHidePanel, true); SetCookie(client, g_hCookieVerbosity, g_nVerbosity); SetCookie(client, g_hCookieShowAllJumps, true); #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] = true; //g_PlayerStates[client][bLJEnabled] = bool:StringToInt(strCookie); GetClientCookie(client, g_hCookieBlockMode, strCookie, sizeof(strCookie)); g_PlayerStates[client][bBlockMode] = bool:StringToInt(strCookie); GetClientCookie(client, g_hCookieStrafeTrainer, strCookie, sizeof(strCookie)); g_PlayerStates[client][bStrafeTrainer] = bool:StringToInt(strCookie); GetClientCookie(client, g_hCookieSyncStats, strCookie, sizeof(strCookie)); g_PlayerStates[client][bSyncStats] = bool:StringToInt(strCookie); GetClientCookie(client, g_hCookieBeam, strCookie, sizeof(strCookie)); g_PlayerStates[client][bBeam] = bool:StringToInt(strCookie); GetClientCookie(client, g_hCookieSpeedometer, strCookie, sizeof(strCookie)); g_PlayerStates[client][nSpeedometer] = StringToInt(strCookie); GetClientCookie(client, g_hCookieDeadstrafe, strCookie, sizeof(strCookie)); g_PlayerStates[client][bDeadstrafe] = 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); GetClientCookie(client, g_hCookieShowPrestrafeHint, strCookie, sizeof(strCookie)); g_PlayerStates[client][bShowPrestrafeHint] = bool:StringToInt(strCookie); GetClientCookie(client, g_hCookiePersonalBest, strCookie, sizeof(strCookie)); g_PlayerStates[client][fPersonalBest] = StringToFloat(strCookie); } ShowSettingsPanel(client) { new Handle:hMenu = CreateMenu(SettingsMenuHandler); decl String:buf[64]; AddMenuItem(hMenu, "tpmenu", "Show TP menu"); Format(buf, sizeof(buf), "Show top stats"); AddMenuItem(hMenu, "ljtop", buf); Format(buf, sizeof(buf), "Beam: %s", g_PlayerStates[client][bBeam] ? "On" : "Off"); AddMenuItem(hMenu, "beam", buf); Format(buf, sizeof(buf), "Deadstrafe (beam): %s", g_PlayerStates[client][bDeadstrafe] ? "On" : "Off"); AddMenuItem(hMenu, "deadstrafe", buf); if( g_PlayerStates[client][nSpeedometer] == 0 ) { Format(buf, sizeof(buf), "Speed: off" ); } else if( g_PlayerStates[client][nSpeedometer] == 1 ) { Format(buf, sizeof(buf), "Speed: top" ); } else if( g_PlayerStates[client][nSpeedometer] == 2 ) { Format(buf, sizeof(buf), "Speed: bottom" ); } AddMenuItem(hMenu, "speed", 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); Format(buf, sizeof(buf), "Prestrafe hint: %s", g_PlayerStates[client][bShowPrestrafeHint] ? "On" : "Off"); AddMenuItem(hMenu, "prestrafehint", buf); Format(buf, sizeof(buf), "Strafe trainer: %s", g_PlayerStates[client][bStrafeTrainer] ? "On" : "Off"); AddMenuItem(hMenu, "strafetrainer", buf); Format(buf, sizeof(buf), "Sync stats: %s", g_PlayerStates[client][bSyncStats] ? "On" : "Off"); AddMenuItem(hMenu, "syncstats", buf); 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, "tpmenu")) { Command_CheckpointPanel(client, 0); } else if(!strcmp(strInfo, "ljtop")) { DisplayMenu(g_hLJTopMainMenu, client, MENU_TIME_FOREVER); 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, "deadstrafe")) { g_PlayerStates[client][bDeadstrafe] = !g_PlayerStates[client][bDeadstrafe]; SetCookie(client, g_hCookieDeadstrafe, g_PlayerStates[client][bDeadstrafe]); PrintToChat(client, "Deadstrafe beam is now %s", g_PlayerStates[client][bDeadstrafe] ? "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, "speed")) { Command_Speedometer( client, 0 ); } 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); } 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); } else if(!strcmp(strInfo, "strafetrainer")) { g_PlayerStates[client][bStrafeTrainer] = !g_PlayerStates[client][bStrafeTrainer]; SetCookie(client, g_hCookieStrafeTrainer, g_PlayerStates[client][bStrafeTrainer]); PrintToChat( client, "Strafe trainer is now %s", g_PlayerStates[client][bStrafeTrainer] ? "ENABLED" : "DISABLED" ); ShowSettingsPanel(client); } else if(!strcmp(strInfo, "syncstats")) { Command_SyncStats( client, 0 ); } } 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 bool:AreAllOnOneTeam( client ) { new team = GetClientTeam( client ); for( new i = 1; i < GetMaxClients(); ++i ) { if( !IsClientConnected(i) || !IsClientInGame(i) ) continue; new team2 = GetClientTeam( i ); if( team2 > 1 && team != team2 ) { return false; } } return true; } public CheckpointMenuHandler( Handle:hMenu, MenuAction:ma, client, nItem ) { switch( ma ) { case MenuAction_Select: { switch( nItem ) { case 0: { Command_SavePoint(client, 0); Command_CheckpointPanel(client, 0); } case 1: { Command_LoadPoint(client, 0); Command_CheckpointPanel(client, 0); } } } case MenuAction_End: { CloseHandle( hMenu ); } } } public Action:Command_SavePoint(client, args) { if( !g_PlayerStates[client][bOnGround] ) { PrintToChat( client, "\x07FF00FFCannot save a checkpoint midair!" ); return Plugin_Handled; } if( nh_warmup == INVALID_HANDLE ) { nh_warmup = FindConVar( "nh_warmup" ); } if( nh_warmup && GetConVarInt( nh_warmup ) == 0 && !AreAllOnOneTeam( client ) ) { PrintToChat( client, "\x07FF00FF Can only teleport during warmup." ); return Plugin_Handled; } new Float:vOrigin[3]; GetClientAbsOrigin(client, vOrigin); Array_Copy(vOrigin, g_PlayerStates[client][vTPOrigin], 3); Array_Copy(g_PlayerStates[client][vLastAngles], g_PlayerStates[client][vTPAngles], 3); PrintToChat( client, "\x04Checkpoint saved." ); return Plugin_Handled; } public Action:Command_LoadPoint(client, args) { if( !IsPlayerAlive(client) ) { PrintToChat( client, "\x07FF00FFCannot teleport while dead." ); return Plugin_Handled; } if( nh_warmup == INVALID_HANDLE ) { nh_warmup = FindConVar( "nh_warmup" ); } if( nh_warmup && GetConVarInt( nh_warmup ) == 0 && !AreAllOnOneTeam( client ) ) { PrintToChat( client, "\x07FF00FFCan only teleport during warmup." ); return Plugin_Handled; } new Float:vVelocity[3], Float:vOrigin[3], Float:vAngle[3], Float:vDelta[2], Float:dist; vVelocity[0] = 0.0; vVelocity[1] = 0.0; vVelocity[2] = -2.0; Array_Copy(g_PlayerStates[client][vTPOrigin], vOrigin, 3); for( new i = 1; i < GetMaxClients(); ++i ) { if( i == client || !IsClientInGame(i) || !IsPlayerAlive(i) ) continue; new Float:vOrigin2[3]; GetClientAbsOrigin( i, vOrigin2 ); vDelta[0] = vOrigin[0] - vOrigin2[0]; vDelta[1] = vOrigin[1] - vOrigin2[1]; dist = SquareRoot( vDelta[0] * vDelta[0] + vDelta[1] * vDelta[1] ); if( FloatAbs( vOrigin[2] - vOrigin2[2] ) < 64 && FloatAbs(dist) < 64 ) { PrintToChat( client, "\x07FF00FFCannot load checkpoint because a player is standing there." ); return Plugin_Handled; } } Array_Copy(g_PlayerStates[client][vTPAngles], vAngle, 3); if( vOrigin[0] == 0.0 && vOrigin[1] == 0.0 && vOrigin[2] == 0.0 ) { PrintToChat( client, "\x07FF00FFYou do not have a valid checkpoint." ); return Plugin_Handled; } g_PlayerStates[client][IllegalJumpFlags] |= IJF_TELEPORT; TeleportEntity( client, vOrigin, vAngle, vVelocity ); PrintToChat( client, "\x01Checkpoint loaded." ); return Plugin_Handled; } public Action:Command_CheckpointPanel(client, args) { if( !IsPlayerAlive(client) ) { PrintToChat( client, "\x07FF00FFCannot teleport while dead." ); return Plugin_Handled; } if( nh_warmup == INVALID_HANDLE ) { nh_warmup = FindConVar( "nh_warmup" ); } if( nh_warmup && GetConVarInt( nh_warmup ) == 0 && !AreAllOnOneTeam( client ) ) { PrintToChat( client, "\x07FF00FFCan only teleport during warmup." ); return Plugin_Handled; } new Handle:hPanel = CreateMenu(CheckpointMenuHandler); AddMenuItem( hPanel, "savetp", "save checkpoint" ); AddMenuItem( hPanel, "loadtp", "load checkpoint" ); DisplayMenu(hPanel, client, MENU_TIME_FOREVER); return Plugin_Handled; } public Action:Command_StrafeTrainer( client, args ) { g_PlayerStates[client][bStrafeTrainer] = !g_PlayerStates[client][bStrafeTrainer]; SetCookie(client, g_hCookieStrafeTrainer, g_PlayerStates[client][bStrafeTrainer]); PrintToChat( client, "Strafe trainer is now %s", g_PlayerStates[client][bStrafeTrainer] ? "ENABLED" : "DISABLED" ); } public Action:Command_KZ(client, args) { if( nh_warmup == INVALID_HANDLE ) { nh_warmup = FindConVar( "nh_warmup" ); } if( nh_warmup ) { SetConVarInt( nh_warmup, -1 ); } else { ServerCommand( "mp_ignore_round_win_conditions 1" ); } ServerCommand( "bot_kick" ); return Plugin_Handled; } public Action:Command_LJDeadstrafe(client, args) { g_PlayerStates[client][bDeadstrafe] = !g_PlayerStates[client][bDeadstrafe]; SetCookie(client, g_hCookieDeadstrafe, g_PlayerStates[client][bDeadstrafe]); PrintToChat(client, "Deadstrafe beam %s", g_PlayerStates[client][bDeadstrafe] ? "ENABLED" : "DISABLED"); return Plugin_Handled; } 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) { 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-, updated by networkheaven team.", 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; } 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); DrawPanelTextF(hPanel, " key gain loss time sync"); 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, "total sync: %.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][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_JUMPBUG && g_PlayerStates[client][fJumpDistance] > g_LJTop[LT_JB][LJTOP_NUM_ENTRIES - 1][m_fDistance] ) { LJTopUpdateTable(client, LT_JB); } else if( g_PlayerStates[client][JumpType] == JT_WEIRDJUMP && g_PlayerStates[client][fJumpDistance] > g_LJTop[LT_WJ][LJTOP_NUM_ENTRIES - 1][m_fDistance] ) { LJTopUpdateTable(client, LT_WJ); } } GetStrafeString( String:buffer[], maxlength, Float:percentage ) { if( percentage > 0.5 && percentage <= 1.5 ){ new spaces = RoundFloat( (percentage - 0.5) / 0.05 ); for( new i = 0; i <= spaces + 1; i++ ) { FormatEx(buffer, maxlength, "%s ", buffer); } FormatEx(buffer, maxlength, "%s|", buffer); for( new i = 0; i <= (21 - spaces); i++ ) { FormatEx(buffer, maxlength, "%s ", buffer); } } else Format(buffer, maxlength, "%s", percentage < 1.0 ? "| " : " |"); } StrafeTrainer( client, bool: onGround, Float:angles[3], Float:velocity[3] ) { if( !g_PlayerStates[client][bStrafeTrainer] || !IsPlayerAlive( client ) ) return; if( onGround ) return; if( g_PlayerStates[client][bOnLadder] ) return; new Float:velocity2d[3]; Array_Copy( velocity, velocity2d, 2 ); new Float:speed = GetVectorLength( velocity2d ); new Float:surf_friction = 1.0; if( velocity[2] > 0 && velocity[2] < 140.0 ) surf_friction = 0.25; new Float:perfAngle = RadToDeg( ArcTangent( 30.0 * surf_friction / speed ) ); new Float:curAngle = angles[1]; new Float:diff = g_PlayerStates[client][vLastAngles][1] - curAngle; while( diff > 180.0 ) diff -= 360.0; while( diff < -180.0 ) diff += 360.0; new Float:percentage = FloatAbs(diff / perfAngle) * 100; if( g_PlayerStates[client][nTrainerTicks] >= STRAFE_TRAINER_TICKS ) { new Float:AvgPercentage = 0.0; for( new i = 0; i < STRAFE_TRAINER_TICKS; ++i ) { AvgPercentage += g_PlayerStates[client][fStrafePercentages][i]; g_PlayerStates[client][fStrafePercentages][i] = 0.0; } AvgPercentage /= STRAFE_TRAINER_TICKS; new String:msg[256], String:strafe[32]; GetStrafeString( strafe, sizeof(strafe), AvgPercentage * 0.01 ); Format(msg, sizeof(msg), "%d\%", RoundFloat(AvgPercentage)); Format(msg, sizeof(msg), "%s\n══════^══════", msg); Format(msg, sizeof(msg), "%s\n %s ", msg, strafe); Format(msg, sizeof(msg), "%s\n══════^══════", msg); new Float:offset = FloatAbs( 1.0 - AvgPercentage * 0.01 ); new r, g, b; if( offset < 0.05 ) { r = 0; g = 255; b = 0; } else if( 0.05 <= offset < 0.1 ) { r = 128; g = 255; b = 0; } else if( 0.1 <= offset < 0.25 ) { r = 255; g = 255; b = 0; } else if( 0.25 <= offset < 0.5 ) { r = 255; g = 128; b = 0; } else { r = 255; g = 0; b = 0; } SetHudTextParams(-1.0, 0.2, GetTickInterval() * (STRAFE_TRAINER_TICKS+1), r, g, b, 255, 0, 0.0, 0.0, 0.1); ShowHudText(client, 0, msg); g_PlayerStates[client][nTrainerTicks] = 0; } new tick = g_PlayerStates[client][nTrainerTicks]; g_PlayerStates[client][fStrafePercentages][tick] = percentage; g_PlayerStates[client][nTrainerTicks]++; } Speedometer( client, bool: bJump, bool: bGround, bool: bIsDucking, Float:velocity[3] ) { new Float:speed = GetVectorLength( velocity ); new String:sBuffer[64]; if( (g_PlayerStates[client][bOnGround] || g_PlayerStates[client][bOnLadder]) && !bJump ) { g_PlayerStates[client][fJumpSpeed] = speed; } if( (!g_PlayerStates[client][bOnGround]) && bGround && bJump ) { g_PlayerStates[client][bPerf] = true; g_PlayerStates[client][fJumpSpeed] = speed; } else if( bGround || g_PlayerStates[client][bOnLadder] ) { g_PlayerStates[client][bPerf] = false; g_PlayerStates[client][fJumpSpeed] = speed; g_PlayerStates[client][bDuckJump] = false; g_PlayerStates[client][bJumpBug] = false; } if( bJump && bGround ) g_PlayerStates[client][bDuckJump] = bIsDucking; if( !g_PlayerStates[client][nSpeedometer] ) return; Format(sBuffer, sizeof(sBuffer), "%.0f", speed ); if( !bGround && !g_PlayerStates[client][bOnLadder] ) { Format(sBuffer, sizeof(sBuffer), "%s\n(%.0f)", sBuffer, g_PlayerStates[client][fJumpSpeed] ); if( g_PlayerStates[client][bDuckJump] ) Format(sBuffer, sizeof(sBuffer), "%sC", sBuffer ); } new r, g, b; if( g_PlayerStates[client][bJumpBug] ) { r = 255; g = 255; b = 0; } else if( g_PlayerStates[client][bPerf] ) { r = b = 0; g = 255; } else { r = 255; g = 255; b = 255; } switch( g_PlayerStates[client][nSpeedometer] ) { case 1: { SetHudTextParams(-1.0, 0.325, 0.1, r, g, b, 255, 0, 0.0, 0.0, 0.0); ShowHudText(client, 1, sBuffer); } case 2: { SetHudTextParams(-1.0, 0.85, 0.1, r, g, b, 255, 0, 0.0, 0.0, 0.0); ShowHudText(client, 1, sBuffer); } } } 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 && 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_PlayerStates[client][fJumpDistance] < g_fLJMin) { 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; } 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); } public Action:Event_PlayerDeath(Handle:event, const String:name[], bool:dontBroadcast) { new id = GetEventInt(event, "userid"); new client = GetClientOfUserId( id ); g_PlayerStates[client][IllegalJumpFlags] = IJF_TELEPORT; } public Action:Event_PlayerSpawn(Handle:event, const String:name[], bool:dontBroadcast) { new id = GetEventInt(event, "userid"); new client = GetClientOfUserId( id ); g_PlayerStates[client][IllegalJumpFlags] = IJF_TELEPORT; } // cba with another enum so JT_LONGJUMP = jump, JT_DROP = slide off edge, JT_LADDERJUMP = ladder PlayerJump(client, JUMP_TYPE:JumpType2 = JT_LONGJUMP) { if( !g_PlayerStates[client][bOnGround] && JumpType2 == JT_LONGJUMP ) { g_PlayerStates[client][bJumpBug] = true; g_PlayerStates[client][fJumpSpeed] = GetSpeed(client); g_PlayerStates[client][bDuckJump] = false; PlayerLand(client); } else g_PlayerStates[client][bJumpBug] = false; 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; } for( new i = 0; i < MAX_JUMP_TICKS; ++i ) { g_PlayerStates[client][nMouseDir][i] = 0; g_PlayerStates[client][nMoveDir][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][bJumpBug]) { g_PlayerStates[client][JumpType] = JumpType2; } else { if( g_PlayerStates[client][bJumpBug] ) { if( g_PlayerStates[client][fPrestrafe] > 325.0 ) { g_PlayerStates[client][IllegalJumpFlags] |= IJF_PRESTRAFE; CPrintToChat(client, "Your jumpbug prestrafe was too high! {red}({default}%0.2f {red}> {default}325{red})", g_PlayerStates[client][fPrestrafe]); } g_PlayerStates[client][JumpType] = JT_JUMPBUG; } 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; new Float:height = FloatAbs( g_PlayerStates[client][fLastJumpHeightDelta] ); if( height > 64.0 ) { g_PlayerStates[client][IllegalJumpFlags] |= IJF_PRESTRAFE; CPrintToChat( client, "Your drop was too high for a weirdjump! {red}({default}%0.2f {red}> {default}64{red})", height ); } } 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); if( g_PlayerStates[client][bJumpBug] ) { new Float:vVel[3]; GetEntPropVector(client, Prop_Data, "m_vecVelocity", vVel); // ducking lowers u by 8.5 units if( GetEntProp(client, Prop_Send, "m_bDucking", 1) ) vOrigin[2] -= 8.5; } Array_Copy(vOrigin, g_PlayerStates[client][vJumpOrigin], 3); // Prestrafe g_PlayerStates[client][fPrestrafe] = GetSpeed(client); if(g_PlayerStates[client][JumpType] == JT_LONGJUMP || g_PlayerStates[client][JumpType] == JT_COUNTJUMP) { 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][JumpType] == JT_COUNTJUMP) { 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]--; } if( nTarget > 0 && nTarget < MaxClients ) { 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; } 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); new bool:teleport = !!(g_PlayerStates[client][IllegalJumpFlags] & IJF_TELEPORT); // BEAMU if(g_PlayerStates[client][bBeam] && !bGround && !teleport && (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(bDucked) { color[1] = 0; color[2] = 0; } else if( vVelocity[2] > 0 && vVelocity[2] < 140 && g_PlayerStates[client][bDeadstrafe] ) { color[1] = (g_PlayerStates[client][CurStrafeDir] % STRAFE_DIRECTION:2) ? 80 : 120; color[2] = 0; } else 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(g_PlayerStates[client][bLJEnabled] && g_PlayerStates[client][bShowPrestrafeHint]) { PrintPrestrafeHint(client); } 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(g_PlayerStates[client][bLJEnabled] && g_PlayerStates[client][bShowPrestrafeHint]) { PrintPrestrafeHint(client); } 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; new tick = g_PlayerStates[client][nTotalTicks]; 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; new Float:delta = vAngles[1] - v2[1]; while(delta < -180.0) delta += 360.0; while(delta > 180.0) delta -= 360.0; if( tick < MAX_JUMP_TICKS ) g_PlayerStates[client][nMouseDir][tick] = delta > 0.0 ? -1 : 1; } else { g_PlayerStates[client][fStrafeLoss][g_PlayerStates[client][nStrafes] - 1] -= fVelDelta; g_PlayerStates[client][fLoss] -= fVelDelta; if( tick < MAX_JUMP_TICKS ) g_PlayerStates[client][nMouseDir][tick] = 0; } if( tick < MAX_JUMP_TICKS ) { if( g_PlayerStates[client][JumpDir] == JD_SIDEWAYS ) { if( !nButtonCount ) g_PlayerStates[client][nMoveDir][tick] = 0; else if( g_PlayerStates[client][CurStrafeDir] == SD_W ) g_PlayerStates[client][nMoveDir][tick] = -1; else if( g_PlayerStates[client][CurStrafeDir] == SD_S ) g_PlayerStates[client][nMoveDir][tick] = 1; else g_PlayerStates[client][nMoveDir][tick] = 0; } else { if( !nButtonCount ) g_PlayerStates[client][nMoveDir][tick] = 0; else if( g_PlayerStates[client][CurStrafeDir] == SD_A ) g_PlayerStates[client][nMoveDir][tick] = -1; else if( g_PlayerStates[client][CurStrafeDir] == SD_D ) g_PlayerStates[client][nMoveDir][tick] = 1; else g_PlayerStates[client][nMoveDir][tick] = 0; } } } g_PlayerStates[client][nTotalTicks]++; g_PlayerStates[client][fTrajectory] += GetSpeed(client) * GetTickInterval(); if(bGround && !g_PlayerStates[client][bOnGround]) { PlayerLand(client); } if(g_PlayerStates[client][bLJEnabled] && g_PlayerStates[client][bShowPrestrafeHint]) { PrintPrestrafeHint(client); } } 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); new bool:jump = !!(buttons & IN_JUMP); new bool:duck = !!(buttons & IN_DUCK); new Float:vVelocitycopy[3]; Array_Copy( vVelocity, vVelocitycopy, 2 ); Speedometer( client, jump, bGround, duck, vVelocitycopy ); _OnPlayerRunCmd(client, buttons, vOrigin, vAngles, vVelocity, bDucked, bGround); StrafeTrainer( client, bGround, vAngles, vVelocity ); 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; } 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])); } if( nh_warmup != INVALID_HANDLE ) { new wm = GetConVarInt( nh_warmup ); if( wm > 0 ) { new secs = wm % 60; new mins = (wm - secs) / 60; Append( strHint, sizeof( strHint ), "\n---[ WARMUP %d:%02d ] ---", mins, secs ); } else if( wm < 0 ) { Append( strHint, sizeof( strHint ), "\n---[ WARMUP ] ---" ); } } PrintHintText(client, strHint); } public PrintSyncStats(client) { new String:strLeft[256]; new String:strRight[256]; new String:strMouseLeft[256]; new String:strMouseRight[256]; new String:strFull[1024]; if( g_PlayerStates[client][nStrafes] == 0 ) return; if( g_PlayerStates[client][nTotalTicks] < 10 ) return; Format( strLeft, sizeof( strLeft ), "[ " ); Format( strRight, sizeof( strRight ), "[ " ); Format( strMouseLeft, sizeof( strMouseLeft ), "[ " ); Format( strMouseRight, sizeof( strMouseRight ), "[ " ); for( new i = 0; i < g_PlayerStates[client][nTotalTicks]; ++i ) { if( g_PlayerStates[client][nMouseDir][i] == -1 ) { Append( strMouseLeft, sizeof( strMouseLeft ), "|" ); Append( strMouseRight, sizeof( strMouseRight ), " " ); } else if( g_PlayerStates[client][nMouseDir][i] == 1 ) { Append( strMouseLeft, sizeof( strMouseLeft ), " " ); Append( strMouseRight, sizeof( strMouseRight ), "|" ); } else { Append( strMouseLeft, sizeof( strMouseLeft ), " " ); Append( strMouseRight, sizeof( strMouseRight ), " " ); } if( g_PlayerStates[client][nMoveDir][i] == -1 ) { Append( strLeft, sizeof( strLeft ), "|" ); Append( strRight, sizeof( strRight ), " " ); } else if( g_PlayerStates[client][nMoveDir][i] == 1 ) { Append( strLeft, sizeof( strLeft ), " " ); Append( strRight, sizeof( strRight ), "|" ); } else { Append( strLeft, sizeof( strLeft ), " " ); Append( strRight, sizeof( strRight ), " " ); } } Format( strLeft, sizeof( strLeft ), "%s ]", strLeft ); Format( strRight, sizeof( strRight ), "%s ]", strRight ); Format( strMouseLeft, sizeof( strMouseLeft ), "%s ]", strMouseLeft ); Format( strMouseRight, sizeof( strMouseRight ), "%s ]", strMouseRight ); if( g_PlayerStates[client][JumpDir] == JD_SIDEWAYS ) { Format( strFull, sizeof( strFull ), "W: %s\nS: %s\nL: %s\nR: %s", strLeft, strRight, strMouseLeft, strMouseRight ); } else { Format( strFull, sizeof( strFull ), "A: %s\nD: %s\nL: %s\nR: %s", strLeft, strRight, strMouseLeft, strMouseRight ); } PrintToConsole( client, strFull ); if( g_PlayerStates[client][bSyncStats] ) { Format( strLeft, sizeof( strLeft ), "[ " ); Format( strRight, sizeof( strRight ), "[ " ); Format( strMouseLeft, sizeof( strMouseLeft ), "[ " ); Format( strMouseRight, sizeof( strMouseRight ), "[ " ); new String:char1[] = "|"; new String:char2[] = "_"; new String:strFull2[1024]; for( new i = 0; i < g_PlayerStates[client][nTotalTicks]; ++i ) { if( g_PlayerStates[client][nMouseDir][i] == -1 ) { Append( strMouseLeft, sizeof( strMouseLeft ), char1 ); Append( strMouseRight, sizeof( strMouseRight ), char2 ); } else if( g_PlayerStates[client][nMouseDir][i] == 1 ) { Append( strMouseLeft, sizeof( strMouseLeft ), char2 ); Append( strMouseRight, sizeof( strMouseRight ), char1 ); } else { Append( strMouseLeft, sizeof( strMouseLeft ), char2 ); Append( strMouseRight, sizeof( strMouseRight ), char2 ); } if( g_PlayerStates[client][nMoveDir][i] == -1 ) { Append( strLeft, sizeof( strLeft ), char1 ); Append( strRight, sizeof( strRight ), char2 ); } else if( g_PlayerStates[client][nMoveDir][i] == 1 ) { Append( strLeft, sizeof( strLeft ), char2 ); Append( strRight, sizeof( strRight ), char1 ); } else { Append( strLeft, sizeof( strLeft ), char2 ); Append( strRight, sizeof( strRight ), char2 ); } } Format( strLeft, sizeof( strLeft ), "%s ]", strLeft ); Format( strRight, sizeof( strRight ), "%s ]", strRight ); Format( strMouseLeft, sizeof( strMouseLeft ), "%s ]", strMouseLeft ); Format( strMouseRight, sizeof( strMouseRight ), "%s ]", strMouseRight ); if( g_PlayerStates[client][JumpDir] == JD_SIDEWAYS ) { Format( strFull, sizeof( strFull ), "W: %s\nS: %s\n", strLeft, strRight ); } else { Format( strFull, sizeof( strFull ), "A: %s\nD: %s\n", strLeft, strRight ); } Format( strFull2, sizeof( strFull2 ), "L: %s\nR: %s", strMouseLeft, strMouseRight ); new Handle:hText = CreateHudSynchronizer(); if(hText != INVALID_HANDLE) { SetHudTextParams(-1.0, 0.06, 3.0, 255, 255, 255, 255, 0, 0.0, 0.15, 0.5); ShowHudText(client, 2, strFull); SetHudTextParams(-1.0, 0.14, 3.0, 180, 180, 255, 255, 0, 0.0, 0.15, 0.5); ShowHudText(client, 3, strFull2); CloseHandle(hText); } } } 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; PrintSyncStats( client ); // 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; } } // 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; 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), "strafes: %d (%.0f); max: %.2f", g_PlayerStates[client][nStrafes], g_PlayerStates[client][fSync], g_PlayerStates[client][fMaxSpeed]); 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"); 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] && IsClientInGame(j)) { 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, JT_JUMPBUG, JT_WEIRDJUMP: { if(g_fBJMin != 0.0 && g_PlayerStates[client][fJumpDistance] >= g_fBJMin) { OutputJump(client, buf); } if(g_bLJSound && g_PlayerStates[client][bSound]) { for(new i = 0; i < LJSOUND_NUM; i++) { if(g_PlayerStates[client][fJumpDistance] >= g_fBJSound[i]) { if(i == (LJSOUND_NUM - 1) || g_PlayerStates[client][fJumpDistance] < g_fBJSound[i + 1] || g_fBJSound[i + 1] == 0.0) { if(g_bLJSoundToAll[i]) { for(new j = 1; j < MaxClients; j++) { if(IsClientInGame(client) && !IsFakeClient(client) && g_PlayerStates[j][bSound] && IsClientInGame(j)) { EmitSoundToClient(j, g_strLJSoundFile[i]); } } } else { EmitSoundToClient(client, g_strLJSoundFile[i]); } break; } } else { break; } } } } 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 Float:fMax = g_fLJMax; 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(!g_bOutput16Style) { decl String:strOutput[512]; decl String:strName[64]; GetClientName(client, strName, sizeof(strName)); Format(strOutput, sizeof(strOutput), "%s {green}%s%s%s ", strName, g_PlayerStates[client][JumpType] == JT_BHOPJUMP && g_PlayerStates[client][bStamina] ? "easy" : "", g_strJumpTypeLwr[g_PlayerStates[client][JumpType]], g_PlayerStates[client][JumpType] == JT_JUMPBUG ? "ged" : "ed"); if(g_PlayerStates[client][JumpType] != JT_LADDERJUMP) { if(g_PlayerStates[client][JumpType] != JT_LONGJUMP && g_PlayerStates[client][JumpType] != JT_COUNTJUMP ) { fMax = g_fNonLJMax; } if( g_PlayerStates[client][JumpType] == JT_BHOPJUMP || g_PlayerStates[client][JumpType] == JT_JUMPBUG || g_PlayerStates[client][JumpType] == JT_WEIRDJUMP ) { fMin = g_fBJMin; fMax = g_fBJMax; } new nColor[3]; if( g_PlayerStates[client][fJumpDistance] > fMax ) { for( new i = 0; i < 3; ++i ) { nColor[i] = g_ColorTop[i]; } } else { for(new i; i < 3; i++) { nColor[i] = RoundFloat((MIN(MAX(g_PlayerStates[client][fJumpDistance], fMinClamp), fMax) - fMinClamp) / (fMax - 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