#include <amxmodx>
#include <amxmisc>
#include <colorchat>
#include <fakemeta>
#include <hamsandwich>
#include <nvault>

// cvars
new edgefriction
new mp_footsteps
new sv_cheats
new sv_gravity
new sv_airaccelerate
new sv_maxspeed
new sv_stepsize
new sv_maxvelocity

// eb
new kz_eb_leet
new kz_eb_pro
new kz_eb_good
new kz_eb_min

new kz_eb_top
new kz_eb_sounds
new kz_ebs_enabled

new kz_eb_minfps

// jb
new kz_jb_leet
new kz_jb_pro
new kz_jb_good
new kz_jb_min

new kz_jb_top
new kz_jb_sounds
new kz_jbs_enabled

new kz_bugs_prefix
new kz_bugs_viscmds
new kz_bugs_legalsettings // stupid cvar's name...

new ebCount[33]

// fps
new Float:GameTime[33]
new FramesPer[33]
new Fps[33]
new CurFps[33]

// Bools
new bool:gHasEbStats[33]
new bool:gHasJbStats[33]
new bool:gHasFPS[33]
new bool:isFalling[33]
new bool:isDucked[33]
new bool:isDuckReleased[33]
new bool:isJumped[33]
new bool:isRespawning[33]
new bool:isEdgeBug[33]

// Floats
new Float:vDuckReleased[33][3]
new Float:vJumped[33][3]
new Float:vFallingStart[33][3]

new Float:vOrigin[3]

// Strings
new const gPLUGIN[]	= "Edge Bug & Jump Bug Stats"
new const gVERSION[]	= "0.62"
new const gAUTHOR[]	= "Newbie"

new const gVAULT_E[]	= "bug_edge"
new const gVAULT_J[]	= "bug_jump"
new const gRESET[]	= "resetlist.txt"

public plugin_init()
{
	edgefriction		= get_cvar_pointer("edgefriction")
	sv_airaccelerate	= get_cvar_pointer("sv_airaccelerate")
	sv_cheats		= get_cvar_pointer("sv_cheats")
	mp_footsteps		= get_cvar_pointer("mp_footsteps")
	sv_gravity		= get_cvar_pointer("sv_gravity")
	sv_maxspeed		= get_cvar_pointer("sv_maxspeed")
	sv_maxvelocity		= get_cvar_pointer("sv_maxvelocity")
	sv_stepsize		= get_cvar_pointer("sv_stepsize")
}

public plugin_register()
{
	register_plugin(gPLUGIN, gVERSION, gAUTHOR)

	kz_eb_leet		= register_cvar("kz_eb_leet",		"6000")
	kz_eb_pro		= register_cvar("kz_eb_pro",		"3500")
	kz_eb_good		= register_cvar("kz_eb_good",		"1500")
	kz_eb_min		= register_cvar("kz_eb_min",		"150")

	kz_eb_top		= register_cvar("kz_eb_top",		"3")
	kz_eb_sounds		= register_cvar("kz_eb_sounds",		"1")
	kz_ebs_enabled		= register_cvar("kz_ebs_enabled",	"1")
	
	kz_eb_minfps		= register_cvar("kz_eb_minfps",		"85")
	
	kz_jb_leet		= register_cvar("kz_jb_leet",		"6000")
	kz_jb_pro		= register_cvar("kz_jb_pro",		"3500")
	kz_jb_good		= register_cvar("kz_jb_good",		"1500")
	kz_jb_min		= register_cvar("kz_jb_min",		"150")
	
	kz_jb_top		= register_cvar("kz_jb_top",		"3")
	kz_jb_sounds		= register_cvar("kz_jb_sounds",		"1")
	kz_jbs_enabled		= register_cvar("kz_jbs_enabled",	"1")
	
	kz_bugs_prefix		= register_cvar("kz_bugs_prefix",	"XJ")
	kz_bugs_viscmds		= register_cvar("kz_bugs_viscmds",	"0")
	kz_bugs_legalsettings	= register_cvar("kz_bugs_legalsettings","1")
	
	RegisterHam(Ham_Touch, "player", "HamTouch")
	RegisterHam(Ham_Spawn, "player", "HamSpawn")
	register_forward(FM_PlayerPreThink, "fwdPlayerPreThink")
	register_forward(FM_PlayerPostThink, "fwdPlayerPostThink")
	register_forward(FM_StartFrame, "fwdStartFrame")
	
	register_dictionary("common.txt")
	register_clcmd("say /ebstats",	"cmdEbStats", -1, "- enables/disables Edge Bug stats")
	register_clcmd("say /jbstats",	"cmdJbStats", -1, "- enables/disables Jump Bug stats")
	register_clcmd("say /fps",	"cmdFPS", -1, "- shows your fps")
	register_clcmd("say /about",	"about", -1, "- shows version")
	register_clcmd("say /resetebtop", "resetebtop", ADMIN_RCON, "- reset Edge Bug top")
	register_clcmd("say /resetjbtop", "resetjbtop", ADMIN_RCON, "- reset Jump Bug top")
	register_clcmd("say /resettop", "resettop", ADMIN_RCON, "- reset Edge Bug & Jump Bug top")
	
	register_clcmd("say /ebtop",	"show_eb", -1, "- display edge bug top")
	register_clcmd("say /ebtop10",	"show_eb", -1, "- display edge bug top")
	register_clcmd("say /ebtop15",	"show_eb", -1, "- display edge bug top")
	register_clcmd("say /eb10",	"show_eb", -1, "- display edge bug top")
	register_clcmd("say /eb15",	"show_eb", -1, "- display edge bug top")
	
	register_clcmd("say /jbtop",	"show_jb", -1, "- display jump bug top")
	register_clcmd("say /jbtop10",	"show_jb", -1, "- display jump bug top")
	register_clcmd("say /jbtop15",	"show_jb", -1, "- display jump bug top")
	register_clcmd("say /jb10",	"show_jb", -1, "- display jump bug top")
	register_clcmd("say /jb15",	"show_jb", -1, "- display jump bug top")
	
	register_resetlist()
}

public register_resetlist()
{
	new file[128]
	get_configsdir(file,128)
	format(file,128,"%s/%s",file,gRESET)
	if(file_exists(file))
	{
		new temp[128], line=0, len=0
		while(read_file(file,line++,temp,128,len) != 0) register_clcmd(temp, "clear", -1)
	}
}

public plugin_precache()
{
	plugin_register()
	
	if (get_pcvar_num(kz_eb_sounds) == 1 || get_pcvar_num(kz_jb_sounds) == 1)
	{
		precache_sound("misc/mod_godlike.wav")
		precache_sound("misc/mod_wickedsick.wav")
		precache_sound("misc/perfect.wav")
		precache_sound("misc/impressive.wav")
		if (get_pcvar_num(kz_eb_sounds) == 1)
		{
			precache_sound("misc/double.wav")
			precache_sound("misc/triple.wav")
		}
	}
}

public plugin_cfg()
{
	new cfg[128]
	get_configsdir(cfg,127)
	format(cfg, 127, "%s/kz_bugs_stats.cfg", cfg)
	if (file_exists(cfg))
	{
		server_exec()
		server_cmd("exec %s", cfg)
	}
	
	if (get_pcvar_num(kz_bugs_legalsettings) && (get_pcvar_num(kz_ebs_enabled) || get_pcvar_num(kz_jbs_enabled)))
	{
		set_cvar_string("edgefriction",		"2")
		set_cvar_string("mp_footsteps",		"1")
		set_cvar_string("sv_cheats",		"0")
		set_cvar_string("sv_gravity",		"800")
		set_cvar_string("sv_airaccelerate",	"10")
		set_cvar_string("sv_maxspeed",		"320")
		set_cvar_string("sv_stepsize",		"18")
		set_cvar_string("sv_maxvelocity",	"2000")
	}
}

public clear(id)
{
	isDucked[id]		= false
	isDuckReleased[id]	= false
	isJumped[id]		= false
	isFalling[id]		= false
	ebCount[id]		= 0
}

public client_disconnect(id)
{
	gHasEbStats[id]		= false
	gHasJbStats[id]		= false
	gHasFPS[id]		= false
	isDucked[id]		= false
	isDuckReleased[id]	= false
	isJumped[id]		= false
	isFalling[id]		= false
	ebCount[id]		= 0
}

public check_eb(id, dist)
{
	new name[33], players[32], num
	get_user_name(id, name, 32)
	get_players(players, num)
	
	ebCount[id]++

	if (ebCount[id] >= 3)
	{
		ColorChat(0, BLUE,	"[%s] %s had done %d units Triple Edge Bug", getPrefix(), name, dist)
		if (get_pcvar_num(kz_eb_sounds) == 1) for (new i;i<num;i++) client_cmd(players[i], "speak misc/triple")
	}
	else if (ebCount[id] == 2)
	{
		ColorChat(0, RED,	"[%s] %s had done %d units Double Edge Bug", getPrefix(), name, dist)
		if (get_pcvar_num(kz_eb_sounds) == 1) for (new i;i<num;i++) client_cmd(players[i], "speak misc/double")
	}
	else if	(dist >= get_pcvar_num(kz_eb_leet))
	{
		ColorChat(0, RED,	"[%s] %s had done %d units Edge Bug", getPrefix(), name, dist)
		if (get_pcvar_num(kz_eb_sounds) == 1)
		{
			for (new i;i<num;i++)
			{
				if (id == players[i])	client_cmd(players[i], "speak misc/mod_wickedsick")
				else			client_cmd(players[i], "speak misc/mod_godlike")
			}
		}
	}
	else if	(dist >= get_pcvar_num(kz_eb_pro))
	{
		ColorChat(0, GREEN,	"[%s] %s had done %d units Edge Bug", getPrefix(), name, dist)
		if (get_pcvar_num(kz_eb_sounds) == 1) client_cmd(id, "speak misc/perfect")
	}
	else if	(dist >= get_pcvar_num(kz_eb_good))
	{
		ColorChat(0, GREY,	"[%s] %s had done %d units Edge Bug", getPrefix(), name, dist)
		if (get_pcvar_num(kz_eb_sounds) == 1) client_cmd(id, "speak misc/impressive")
	}
	save_eb(id, dist)
}

public check_jb(id, dist)
{
	new name[33], players[32], num
	get_user_name(id, name, 32)
	get_players(players, num)
	
	if	(dist >= get_pcvar_num(kz_jb_leet))
	{
		ColorChat(0, RED,	"[%s] %s had done %d units Jump Bug", getPrefix(), name, dist)
		if (get_pcvar_num(kz_jb_sounds) == 1)
		{
			for (new i;i<num;i++)
			{
				if (id == players[i])	client_cmd(players[i], "speak misc/mod_wickedsick")
				else			client_cmd(players[i], "speak misc/mod_godlike")
			}
		}
	}
	else if	(dist >= get_pcvar_num(kz_jb_pro))
	{
		ColorChat(0, GREEN,	"[%s] %s had done %d units Jump Bug", getPrefix(), name, dist)
		if (get_pcvar_num(kz_jb_sounds) == 1) client_cmd(id, "speak misc/perfect")
	}
	else if	(dist >= get_pcvar_num(kz_jb_good))
	{
		ColorChat(0, GREY,	"[%s] %s had done %d units Jump Bug", getPrefix(), name, dist)
		if (get_pcvar_num(kz_jb_sounds) == 1) client_cmd(id, "speak misc/impressive")
	}
	save_jb(id, dist)
}

public save_eb(id, dist)
{
	new vault = nvault_open(gVAULT_E)
	if(vault != -1)
	{
		// vault:
		// 		szkey	= place
		//		arg1	= ip
		//		arg2	= name
		//		arg3	= distance
		//		arg4	= count
		//		arg5	= double
		//		arg6	= triple
		new szkey[8]
		new szip[32]
		new szname[32]
		new cur_place
		new new_place
		
		get_user_ip(id,szip,32,1)
		get_user_name(id,szname,32)
		
		// getting current place in top
		for(new i=1;i<=10;i++)
		{
			new szreturn[256], arg1[32], arg2[32], arg3[5], arg4[5], arg5[5], arg6[5]
			format(szkey,8,"%d",i)
			nvault_get(vault,szkey,szreturn,256)
			if(parse(szreturn,arg1,32,arg2,32,arg3,4,arg4,4,arg5,4,arg6,4) != 0 && equali(szip,arg1))
			{
				cur_place = i
				break
			}
		}
		// calculating new place in top
		for(new i=1;i<=10;i++)
		{
			new szreturn[256], arg1[32], arg2[32], arg3[5], arg4[5], arg5[5], arg6[5]
			format(szkey,8,"%d",i)
			nvault_get(vault,szkey,szreturn,256)
			if (cur_place == 1)
			{
				new_place = 1
				break
			}
			else if (cur_place != 0 && cur_place < i)
			{
				new_place = 0
				break
			}
			else if(parse(szreturn,arg1,32,arg2,32,arg3,4,arg4,4,arg5,4,arg6,4) == 0)
			{
				new_place = i
				break
			}
			else if(dist > str_to_num(arg3))
			{
				new_place = i
				break
			}
		}
		//client_print(0, print_chat, "%s: cur: %d new: %d", szname, cur_place, new_place)
	
		if (cur_place == 0 && new_place <= 10 && new_place > 0) // new record (old style)
		{
			if (new_place < 10)
			{
				new sztemp[128], sztempkey[8]
				
				for(new i=10;i>new_place;i--)
				{
					format(sztempkey,8,"%d",i-1)
					nvault_get(vault,sztempkey,sztemp,128)
					if(!equal(sztemp,""))
					{
						format(sztempkey,8,"%d",i)
						nvault_pset(vault,sztempkey,sztemp)
					}
				}
			}
			if (get_pcvar_num(kz_eb_top) == 2 || get_pcvar_num(kz_eb_top) == 3) client_print(0, print_chat, "[%s] %s now is %d in Edge Bug top with %d units Edge Bug!", getPrefix(), szname, new_place, dist)
			
			new sznew[128]
			format(szkey,8,"%d",new_place)
			format(sznew,128,"^"%s^" ^"%s^" ^"%d^" ^"1^" ^"0^" ^"0^"",szip,szname,dist)
			nvault_pset(vault,szkey,sznew)
		}
		else if (cur_place == new_place && cur_place > 0) // if same place
		{
			new sznew[128], szkey[8]
			new szreturn[256], arg1[32], arg2[32], arg3[5], arg4[5], arg5[5], arg6[5]
			format(szkey,8,"%d",cur_place)
			nvault_get(vault,szkey,szreturn,256)
			if(parse(szreturn,arg1,32,arg2,32,arg3,5,arg4,5,arg5,4,arg6,4) != 0)
			{
				format(sznew,128,"^"%s^" ^"%s^" ^"%d^" ^"%d^" ^"%d^" ^"%d^"", arg1, szname, dist > str_to_num(arg3) ? dist : str_to_num(arg3), str_to_num(arg4)+1, str_to_num(arg5) + (ebCount[id] == 2 ? 1 : 0), str_to_num(arg6) + (ebCount[id] >= 3 ? 1 : 0))
				nvault_pset(vault,szkey,sznew)
			}
			if (get_pcvar_num(kz_eb_top) == 2 || get_pcvar_num(kz_eb_top) == 3 && dist > str_to_num(arg3)) client_print(0, print_chat, "[%s] %s now is %d in Edge Bug top with %d units Edge Bug!", getPrefix(), szname, new_place, dist)
		}
		else if (new_place < cur_place && new_place > 0) // if player's place better
		{
			new sznew[128]
			new szreturn[256], arg1[32], arg2[32], arg3[5], arg4[5], arg5[5], arg6[5]
			format(szkey,8,"%d",cur_place)
			nvault_get(vault,szkey,szreturn,256)
			if(parse(szreturn,arg1,32,arg2,32,arg3,5,arg4,5,arg5,4,arg6,4) != 0)
			{
				format(sznew,128,"^"%s^" ^"%s^" ^"%d^" ^"%d^" ^"%d^" ^"%d^"", szip, szname, dist, str_to_num(arg4)+1, str_to_num(arg5) + ebCount[id] == 2 ? 1 : 0, str_to_num(arg6) + ebCount[id] >= 3 ? 1 : 0)				
				for(new i=cur_place;i>=new_place;i--)
				{
					new szreturn[256], arg1[32], arg2[32], arg3[5], arg4[5], arg5[5], arg6[5]
					format(szkey,8,"%d",i)
					nvault_get(vault,szkey,szreturn,256)
					
					if(parse(szreturn,arg1,32,arg2,32,arg3,4,arg4,4,arg5,4,arg6,4) != 0)
					{
						new sztemp [128], sztempkey[8]
						format(sztempkey,8,"%d",i-1)
						nvault_get(vault,sztempkey,sztemp,128)
						
						if(!equal(sztemp,""))
						{
							format(sztempkey,8,"%d",i)
							nvault_pset(vault,sztempkey,sztemp)
						}
					}
					else break
				}
				
				if (get_pcvar_num(kz_eb_top) == 2 || get_pcvar_num(kz_eb_top) == 3) client_print(0, print_chat, "[%s] %s now is %d in Edge Bug top with %d units Edge Bug!", getPrefix(), szname, new_place, dist)
				nvault_pset(vault,szkey,sznew)
			}
		}
		nvault_close(vault)
	}
}



public save_jb(id, dist)
{
	new vault = nvault_open(gVAULT_J)
	if(vault != -1)
	{
		// vault:
		// 		szkey	= place
		//		arg1	= ip
		//		arg2	= name
		//		arg3	= distance
		//		arg4	= count
		new szkey[8]
		new szip[32]
		new szname[32]
		new cur_place
		new new_place
		
		get_user_ip(id,szip,32,1)
		get_user_name(id,szname,32)
		
		// getting current place in top
		for(new i=1;i<=15;i++)
		{
			new szreturn[256], arg1[32], arg2[32], arg3[5], arg4[5]
			format(szkey,8,"%d",i)
			nvault_get(vault,szkey,szreturn,256)
			if(parse(szreturn,arg1,32,arg2,32,arg3,4,arg4,4) != 0 && equali(szip,arg1))
			{
				cur_place = i
				break
			}
		}
		
		// calculating new place in top
		for(new i=1;i<=10;i++)
		{
			new szreturn[256], arg1[32], arg2[32], arg3[5], arg4[5]
			format(szkey,8,"%d",i)
			nvault_get(vault,szkey,szreturn,256)
			if (cur_place == 1)
			{
				new_place = 1
				break
			}
			else if (cur_place != 0 && cur_place < i)
			{
				new_place = 0
				break
			}
			else if(parse(szreturn,arg1,32,arg2,32,arg3,4,arg4,4) == 0)
			{
				new_place = i
				break
			}
			else if(dist > str_to_num(arg3))
			{
				new_place = i
				break
			}
		}
	
		//client_print(0, print_chat, "%s: cur: %d new: %d", szname, cur_place, new_place)
	
		if (cur_place == 0 && new_place <= 10 && new_place > 0) // new record (old style)
		{
			if (new_place < 10)
			{
				new sztemp[128], sztempkey[8]
				
				for(new i=10;i>new_place;i--)
				{
					format(sztempkey,8,"%d",i-1)
					nvault_get(vault,sztempkey,sztemp,128)
					if(!equal(sztemp,""))
					{
						format(sztempkey,8,"%d",i)
						nvault_pset(vault,sztempkey,sztemp)
					}
				}
			}
			if (get_pcvar_num(kz_jb_top) == 2 || get_pcvar_num(kz_jb_top) == 3) client_print(0, print_chat, "[%s] %s now is %d in Jump Bug top with %d units Jump Bug!", getPrefix(), szname, new_place, dist)
			
			new sznew[128]
			format(szkey,8,"%d",new_place)
			format(sznew,128,"^"%s^" ^"%s^" ^"%d^" ^"1^"",szip,szname,dist)
			nvault_pset(vault,szkey,sznew)
		}
		else if (cur_place == new_place && new_place > 0) // if same place
		{
			new sznew[128], szkey[8]
			new szreturn[256], arg1[32], arg2[32], arg3[5], arg4[5]
			format(szkey,8,"%d",cur_place)
			nvault_get(vault,szkey,szreturn,256)
			if(parse(szreturn,arg1,32,arg2,32,arg3,4,arg4,4) != 0)
			{
				format(sznew,128,"^"%s^" ^"%s^" ^"%d^" ^"%d^"",arg1,szname,dist > str_to_num(arg3) ? dist : str_to_num(arg3),str_to_num(arg4)+1)
				nvault_pset(vault,szkey,sznew)
			}
			if (get_pcvar_num(kz_jb_top) == 2 || get_pcvar_num(kz_jb_top) == 3 && dist > str_to_num(arg3)) client_print(0, print_chat, "[%s] %s now is %d in Jump Bug top with %d units Jump Bug!", getPrefix(), szname, new_place, dist)
		}
		else if (new_place < cur_place && new_place > 0) // if player's place better
		{
			new sznew[128]
			new szreturn[256], arg1[32], arg2[32], arg3[5], arg4[5]
			format(szkey,8,"%d",cur_place)
			nvault_get(vault,szkey,szreturn,256)
			if(parse(szreturn,arg1,32,arg2,32,arg3,4,arg4,4) != 0)
			{
				format(sznew,128,"^"%s^" ^"%s^" ^"%d^" ^"%d^"",szip,szname,dist,str_to_num(arg4)+1)
				
				for(new i=cur_place;i>=new_place;i--)
				{
					new szreturn[256], arg1[32], arg2[32], arg3[5], arg4[5]
					format(szkey,8,"%d",i)
					nvault_get(vault,szkey,szreturn,256)
					
					if(parse(szreturn,arg1,32,arg2,32,arg3,4,arg4,4) != 0)
					{
						new sztemp [128], sztempkey[8]
						format(sztempkey,8,"%d",i-1)
						nvault_get(vault,sztempkey,sztemp,128)
						
						if(!equal(sztemp,""))
						{
							format(sztempkey,8,"%d",i)
							nvault_pset(vault,sztempkey,sztemp)
						}
					}
					else break
				}
				
				if (get_pcvar_num(kz_jb_top) == 2 || get_pcvar_num(kz_jb_top) == 3) client_print(0, print_chat, "[%s] %s now is %d in Jump Bug top with %d units Jump Bug!", getPrefix(), szname, new_place, dist)
				nvault_pset(vault,szkey,sznew)
			}
		}
		nvault_close(vault)
	}
}

public show_eb(id)
{
	if(get_pcvar_num(kz_ebs_enabled) && (get_pcvar_num(kz_eb_top) == 2 || get_pcvar_num(kz_eb_top) == 3))
	{
		new motd[2048]
		add(motd,2048,"<html><head><style>")
		add(motd,2048,"body { background-color:#000000; font-family:Tahoma; font-size:10px; color:#FFFFFF; }")
		add(motd,2048,".tabel { border-style:solid; border-width:1px; border-color:#FFFFFF; font-family:Tahoma; font-size:10px; color:#FFFFFF; }")
		add(motd,2048,".header { background-color:#292929; font-family:Tahoma; font-size:10px; color:#FFFFFF; font-weight:800; }")
		add(motd,2048,"</style></head><body>")
		add(motd,2048,"<br><br><table border=0 cellspacing=0 cellpadding=1 width=90% align=center class=tabel>")
		add(motd,2048,"<tr><td class=header width=5% align=center>#</td><td class=header width=30%>Name</td><td class=header width=15%>Distance</td><td class=header width=15%>Double</td><td class=header width=15%>Triple</td><td class=header width=20%>Total</td></tr>")

		new vault = nvault_open(gVAULT_E)
		if(vault != -1)
		{
			for(new i=1;i<=10;i++)
			{
				new szkey[8]
				new szreturn[256], arg1[32], arg2[32], arg3[5], arg4[5], arg5[5], arg6[5]
				format(szkey,8,"%d",i)
				nvault_get(vault,szkey,szreturn,256)
				
				if(parse(szreturn,arg1,32,arg2,32,arg3,4,arg4,4,arg5,4,arg6,4) != 0)
				{
					while (containi(arg2, "<") != -1) replace(arg2, 63, "<", "&lt")
					while (containi(arg2, ">") != -1) replace(arg2, 63, ">", "&gt")
					add(motd,2048,"<tr><td>")
					add(motd,2048,szkey)
					add(motd,2048,"</td><td>")
					add(motd,2048,arg2)
					add(motd,2048,"</td><td>")
					add(motd,2048,arg3)
					add(motd,2048,"</td><td>")
					add(motd,2048,arg5)
					add(motd,2048,"</td><td>")
					add(motd,2048,arg6)
					add(motd,2048,"</td><td>")
					add(motd,2048,arg4)
					add(motd,2048,"</td></tr>")
				}
			}
			nvault_close(vault)
			
			add(motd,2048,"</table></body></html>")
			show_motd(id,motd,"Top 10 Edge Buggers")
		}
	}
	return get_pcvar_num(kz_bugs_viscmds) ? PLUGIN_CONTINUE : PLUGIN_HANDLED
}

public show_jb(id)
{
	if(get_pcvar_num(kz_jbs_enabled) && (get_pcvar_num(kz_jb_top) == 2 || get_pcvar_num(kz_jb_top) == 3))
	{
		new motd[2048]
		add(motd,2048,"<html><head><style>")
		add(motd,2048,"body { background-color:#000000; font-family:Tahoma; font-size:10px; color:#FFFFFF; }")
		add(motd,2048,".tabel { border-style:solid; border-width:1px; border-color:#FFFFFF; font-family:Tahoma; font-size:10px; color:#FFFFFF; }")
		add(motd,2048,".header { background-color:#292929; font-family:Tahoma; font-size:10px; color:#FFFFFF; font-weight:800; }")
		add(motd,2048,"</style></head><body>")
		add(motd,2048,"<br><br><table border=0 cellspacing=0 cellpadding=1 width=90% align=center class=tabel>")
		add(motd,2048,"<tr><td class=header width=5% align=center>#</td><td class=header width=30%>Name</td><td class=header width=25%>Distance</td><td class=header width=30%>Total Jump Bugs</td></tr>")

		new vault = nvault_open(gVAULT_J)
		if(vault != -1)
		{
			for(new i=1;i<=10;i++)
			{
				new szkey[8]
				new szreturn[256], arg1[32], arg2[32], arg3[5], arg4[5], arg5[20]
				format(szkey,8,"%d",i)
				nvault_get(vault,szkey,szreturn,256)
				
				if(parse(szreturn,arg1,32,arg2,32,arg3,5,arg4,5,arg5,20) != 0)
				{
					while (containi(arg2, "<") != -1) replace(arg2, 63, "<", "&lt")
					while (containi(arg2, ">") != -1) replace(arg2, 63, ">", "&gt")
					add(motd,2048,"<tr><td>")
					add(motd,2048,szkey)
					add(motd,2048,"</td><td>")
					add(motd,2048,arg2)
					add(motd,2048,"</td><td>")
					add(motd,2048,arg3)
					add(motd,2048,"</td><td>")
					add(motd,2048,arg4)
					add(motd,2048,"</td></tr>")
				}
			}
			nvault_close(vault)
			
			add(motd,2048,"</table></body></html>")
			show_motd(id,motd,"Top 10 Jump Buggers")
		}
	}
	return get_pcvar_num(kz_bugs_viscmds) ? PLUGIN_CONTINUE : PLUGIN_HANDLED
}

public cmdEbStats(id) // huh... I really don't know is it really needed
{
	if(!gHasEbStats[id])
	{
		gHasEbStats[id] = true
		client_print(id, print_chat, "[%s] Edge Bug Stats enabled. To disable, type /ebstats.", getPrefix())
	}
	else
	{
		client_print(id, print_chat, "[%s] Edge Bug Stats disabled. To enable, type /ebstats.", getPrefix())
		gHasEbStats[id] = false
	}
	return get_pcvar_num(kz_bugs_viscmds) ? PLUGIN_CONTINUE : PLUGIN_HANDLED
}

public cmdJbStats(id)
{
	if(!gHasJbStats[id])
	{
		gHasJbStats[id] = true
		client_print(id, print_chat, "[%s] Jump Bug Stats enabled. To disable, type /jbstats.", getPrefix())
	}
	else
	{
		gHasJbStats[id] = false
		client_print(id, print_chat, "[%s] Jump Bug Stats disabled. To enable, type /jbstats.", getPrefix())
	}
	return PLUGIN_CONTINUE
}

public cmdFPS(id)
{
	if(!gHasFPS[id])
	{
		gHasFPS[id] = true
		client_print(id, print_chat, "[%s] FPS displaying enabled. To disable, type /fps.", getPrefix())
	}
	else
	{
		gHasFPS[id] = false
		client_print(id, print_chat, "[%s] FPS displaying disabled. To enable, type /fps.", getPrefix())
	}
	return get_pcvar_num(kz_bugs_viscmds) ? PLUGIN_CONTINUE : PLUGIN_HANDLED
}

public fwdStartFrame()
{
	if(get_pcvar_num(kz_bugs_legalsettings) && (get_pcvar_num(kz_ebs_enabled) || get_pcvar_num(kz_jbs_enabled)))
	{
		if (get_pcvar_num(edgefriction) != 2)		set_pcvar_num(edgefriction, 2)
		if (get_pcvar_num(mp_footsteps) != 1)		set_pcvar_num(mp_footsteps, 1)
		if (get_pcvar_num(sv_cheats) != 0)		set_pcvar_num(sv_cheats, 0)
		if (get_pcvar_num(sv_gravity) != 800)		set_pcvar_num(sv_gravity, 800)
		if (get_pcvar_num(sv_airaccelerate) != 10)	set_pcvar_num(sv_airaccelerate, 10)
		if (get_pcvar_num(sv_maxspeed) != 320)		set_pcvar_num(sv_maxspeed, 320)
		if (get_pcvar_num(sv_stepsize) != 18)		set_pcvar_num(sv_stepsize, 18)
		if (get_pcvar_num(sv_maxvelocity) != 2000)	set_pcvar_num(sv_maxvelocity, 2000)
	}
}

public fwdPlayerPreThink(id)
{
	if(is_user_alive(id))
	{
		// FPS displaying
		GameTime[id] = get_gametime()
		
		if(FramesPer[id] >= GameTime[id]) Fps[id] += 1
		else
		{
			FramesPer[id] += 1
			
			if (gHasFPS[id])
			{
				set_hudmessage(255, 0, 0, -1.0, 0.9, 0, 1.0, 1.0, 0.1, 0.1, 1337)
				show_hudmessage(id, "FPS: %d", Fps[id])
			}
			CurFps[id] = Fps[id]
			
			Fps[id] = 0
		}
		// FPS displaying
		
		if ((pev(id, pev_flags)&FL_INWATER && pev(id, pev_waterlevel) >= 2) || pev(id, pev_movetype) == MOVETYPE_FLY)
		{
			// Water Bug + water level check
			// ladder (fakemeta doesn't hook func_ladder)
			clear(id)
			return FMRES_IGNORED
		}
		
		// Incorrect cvars
		static Float:fGravity
 
		pev(id, pev_gravity, fGravity)
		if (fGravity != 1.0
		|| get_pcvar_num(edgefriction) != 2
		|| get_pcvar_num(mp_footsteps) != 1
		|| get_pcvar_num(sv_cheats) != 0
		|| get_pcvar_num(sv_gravity) != 800
		|| get_pcvar_num(sv_airaccelerate) != 10
		|| get_pcvar_num(sv_maxspeed) != 320
		|| get_pcvar_num(sv_stepsize) != 18
		|| get_pcvar_num(sv_maxvelocity) != 2000)
		{
			clear(id)
			return FMRES_IGNORED
		}
		// Incorrect cvars
		
		// Buttons logger
		if (isFalling[id])
		{
			if(pev(id, pev_button)&IN_DUCK && !(pev(id, pev_flags)&FL_ONGROUND))
			{
				isDucked[id] = true
				isDuckReleased[id] = false
			}
			else if(isDucked[id] && pev(id, pev_oldbuttons)&IN_DUCK && !(pev(id, pev_button)&IN_DUCK) && !isDuckReleased[id] && !(pev(id, pev_flags)&FL_ONGROUND))
			{
				isJumped[id] = false
				
				vJumped[id][0] = 0.0
				vJumped[id][1] = 0.0
				vJumped[id][2] = 0.0
				
				pev(id, pev_origin, vOrigin)
				
				if(is_in_duck(id))	vOrigin[2]-=18
				else			vOrigin[2]-=36

				if (vFallingStart[id][2] > vOrigin[2]+62) // +62 - max jump height
				{
					isDuckReleased[id] = true

					vDuckReleased[id][0] = vOrigin[0]
					vDuckReleased[id][1] = vOrigin[1]
					vDuckReleased[id][2] = vOrigin[2]
					
				}
			}
			else if(isDucked[id] && isDuckReleased[id] && pev(id, pev_button)&IN_JUMP && !isJumped[id])
			{
				isJumped[id] = true

				pev(id, pev_origin, vOrigin)
				
				vJumped[id][0] = vOrigin[0]
				vJumped[id][1] = vOrigin[1]
				vJumped[id][2] = vOrigin[2]-18 // only 18 units (Bug fixing)
			}
		}
		// Buttons logger
		
		// Detecting jump bug
		if(pev(id, pev_flags)&FL_ONGROUND && isFalling[id])
		{
			pev(id, pev_origin, vOrigin)
			
			if(is_in_duck(id))	vOrigin[2]-=18
			else			vOrigin[2]-=36
			
			new Distance = floatround((vFallingStart[id][2]-vOrigin[2]), floatround_floor)

			if (vFallingStart[id][2] > vOrigin[2]+62 && Distance >= get_pcvar_num(kz_jb_min)) // +62 - max jump height
			{

				if (isDuckReleased[id])
				{
					vDuckReleased[id][0] = vDuckReleased[id][0]-vOrigin[0]
					vDuckReleased[id][1] = vDuckReleased[id][1]-vOrigin[1]
					vDuckReleased[id][2] = vDuckReleased[id][2]-vOrigin[2]
				}

				if (isJumped[id])
				{
					vJumped[id][0] = vJumped[id][0]-vOrigin[0]
					vJumped[id][1] = vJumped[id][1]-vOrigin[1]
					vJumped[id][2] = vJumped[id][2]-vOrigin[2]
				}

				if (vJumped[id][2] > vDuckReleased[id][2] && vDuckReleased[id][2] >= 0 && isDuckReleased[id] && isJumped[id])
				{
					check_jb(id, Distance)
				}
				
				if (gHasJbStats[id] && isDucked[id])
				{
					if (isDuckReleased[id])
					{
						if (vDuckReleased[id][2] >= 0)
						{
							if (vJumped[id][2] > vDuckReleased[id][2] && isJumped[id])
							{
								set_hudmessage(0, 255, 0, -1.0, 0.75, 0, 0.0, 2.0, 0.1, 0.1, 3)
								show_hudmessage(id, "Fall Distance: %d^nDuck Released at: %f^nWell done!", Distance, vDuckReleased[id][2])
							}
							else
							{
								set_hudmessage(0, 255, 159, -1.0, 0.8, 0, 0.0, 2.0, 0.1, 0.1, 3)
								show_hudmessage(id, "Fall Distance: %d^nDuck Released at: %f", Distance, vDuckReleased[id][2])
								client_print(id, print_console, "Fall Distance: %d Duck Released at: %f", Distance, vDuckReleased[id][2])
							}
						}
						else
						{
							set_hudmessage(255, 0, 0, -1.0, 0.8, 0, 0.0, 2.0, 0.1, 0.1, 3)
							show_hudmessage(id, "Fall Distance: %d^nDuck not released!", Distance)
						}
					}
					else
					{
						set_hudmessage(255, 0, 0, -1.0, 0.8, 0, 0.0, 2.0, 0.1, 0.1, 3)
						show_hudmessage(id, "Fall Distance: %d^nDuck not released!", Distance)
					}
				}
			}
			clear(id)
		}
		if(!(pev(id, pev_flags)&FL_ONGROUND) && !(pev(id, pev_flags)&FL_INWATER) && !isFalling[id])
		{
			isFalling[id] = true

			pev(id, pev_origin, vOrigin)
			
			if(is_in_duck(id))	vOrigin[2]-=18
			else			vOrigin[2]-=36

			vFallingStart[id][0]	= vOrigin[0]
			vFallingStart[id][1]	= vOrigin[1]
			vFallingStart[id][2]	= vOrigin[2]
			if(pev(id, pev_oldbuttons)&IN_JUMP) vFallingStart[id][2] -= 2.0

			vDuckReleased[id][0]	= 0.0
			vDuckReleased[id][1]	= 0.0
			vDuckReleased[id][2]	= 0.0

			vJumped[id][0]		= 0.0
			vJumped[id][1]		= 0.0
			vJumped[id][2]		= 0.0
		}
		// Detecting jump bug
		
		// Detecting edge bug
		static Float:velocity[3]
		pev(id, pev_velocity, velocity)
		
		if (isFalling[id] && isEdgeBug[id] && CurFps[id] > get_pcvar_num(kz_eb_minfps) && -velocity[2] <= 400/CurFps[id]) // 400/CurFps - velocity :)
		{
			//client_print(id, print_chat, "FPS: %d Velocity: %f", CurFps[id], -velocity[2])
			pev(id, pev_origin, vOrigin)
			
			if(is_in_duck(id))	vOrigin[2]-=18
			else			vOrigin[2]-=36
			
			new Distance = floatround((vFallingStart[id][2]-vOrigin[2]), floatround_floor)
				
			if (vFallingStart[id][2] > vOrigin[2]+62 && Distance >= get_pcvar_num(kz_eb_min))
			{
				if (gHasEbStats[id])
				{
					set_hudmessage(0, 255, 0, -1.0, 0.75, 0, 0.0, 2.0, 0.1, 0.1, 3)
					show_hudmessage(id, "Fall Distance: %d^nWell done!", Distance)
				}
				check_eb(id, Distance)
			}
			isEdgeBug[id] = false
		}
		// Detecting edge bug

		// Falling end...
		if(pev(id, pev_flags)&FL_ONGROUND && isFalling[id])
		{
			clear(id)
			isFalling[id] = false
		}
		// Falling end...

		// Falling start...
		if(!(pev(id, pev_flags)&FL_ONGROUND) && !(pev(id, pev_flags)&FL_INWATER) && !isFalling[id])
		{
			isFalling[id] = true

			pev(id, pev_origin, vOrigin)
			
			if(is_in_duck(id))	vOrigin[2]-=18
			else			vOrigin[2]-=36

			vFallingStart[id][0]	= vOrigin[0]
			vFallingStart[id][1]	= vOrigin[1]
			vFallingStart[id][2]	= vOrigin[2]
		}
		// Falling start...
	}
	return FMRES_IGNORED
}

public HamSpawn(id)
{
	isRespawning[id] = true
	set_task(2.0, "check_respawn", id+1337)
}

public check_respawn(id)
{
	isRespawning[id-1337] = false
}

public HamTouch(id, ent)
{
	if (!isRespawning[id])
	{
		new classname[32]
		if (ent > 0 && pev_valid(ent)) pev(ent, pev_classname, classname, 32)
		
		static Float:velocity[3]
		pev(id, pev_velocity, velocity)
		
		if (equal(classname, "trigger_teleport") || equal(classname, "trigger_push")) clear(id)
		else if (isFalling[id] && !(pev(id, pev_flags)&FL_ONGROUND) && floatround(velocity[2], floatround_floor) < 0)
		{
			isEdgeBug[id] = true
			static Float:trace[3]
			pev(id, pev_origin, vOrigin)
			for (new i=-14;i<=14;i+=14)
			{
				if (isEdgeBug[id] == false) break
				for (new j=-14;j<=14;j+=14)
				{
					trace[0] = vOrigin[0]+i
					trace[1] = vOrigin[1]+j
					trace[2] = vOrigin[2]
					if (is_wallbug(id, trace))
					{
						isEdgeBug[id] = false
						break
					}
				}
			}
			//client_print(id, print_chat, "Edgebug: %s", isEdgeBug[id] ? "true" : "false")
		}
	}
}

public about(id)
{
	ColorChat(id, GREY, "^x04[%s] Plugin: ^x01%s^x04 by: ^x03%s", getPrefix(), gPLUGIN, gAUTHOR)
	if (get_pcvar_num(kz_ebs_enabled) || get_pcvar_num(kz_jbs_enabled))	ColorChat(id, BLUE, "^x04[%s] Version: ^x01%s^x04, EB Status:^x03 %s^x04, JB Status:^x03 %s", getPrefix(), gVERSION, get_pcvar_num(kz_ebs_enabled) ? "enabled" : "disabled", get_pcvar_num(kz_jbs_enabled) ? "enabled" : "disabled")
	else									ColorChat(id, RED, "^x04[%s] Version: ^x01%s^x04, EB Status:^x03 disabled^x04, JB Status:^x03 disabled", getPrefix(), gVERSION)
	return get_pcvar_num(kz_bugs_viscmds) ? PLUGIN_CONTINUE : PLUGIN_HANDLED
}

public resetebtop(id)
{
	reset(true, false)
	ColorChat(id, RED, "[%s] Edge Bug top has been cleared", getPrefix())
	return get_pcvar_num(kz_bugs_viscmds) ? PLUGIN_CONTINUE : PLUGIN_HANDLED
}

public resetjbtop(id)
{
	reset(false, true)
	ColorChat(id, RED, "[%s] Jump Bug top has been cleared", getPrefix())
	return get_pcvar_num(kz_bugs_viscmds) ? PLUGIN_CONTINUE : PLUGIN_HANDLED
}

public resettop(id)
{
	reset(true, true)
	ColorChat(id, RED, "[%s] Edge Bug and Jump Bug top has been cleared", getPrefix())
	return get_pcvar_num(kz_bugs_viscmds) ? PLUGIN_CONTINUE : PLUGIN_HANDLED
}

public reset(bool:eb, bool:jb)
{
	new dir[64], file[128]
	get_datadir(dir, 63)
	if (eb)
	{
		format(file, 127, "%s/vault/%s.vault", dir, gVAULT_E)
		if (file_exists(file)) delete_file(file)
	}
	if (jb)
	{
		format(file, 127, "%s/vault/%s.vault", dir, gVAULT_J)
		if (file_exists(file)) delete_file(file)
	}
}

public getPrefix()
{
	new prefix[33]
	get_pcvar_string(kz_bugs_prefix, prefix, 32)
	return prefix
}

stock bool:is_wallbug(id, Float:origin[3])
{
	static tr
	engfunc(EngFunc_TraceHull, origin, origin, IGNORE_MONSTERS, pev(id, pev_flags) & FL_DUCKING ? HULL_HEAD : HULL_HUMAN, id, tr)
	if (get_tr2(tr, TR_StartSolid)) return true
	return false
}

stock bool:is_in_duck(entity)
{
	if(!pev_valid(entity)) return false

	static Float:absmin[3], Float:absmax[3]

	pev(entity, pev_absmin, absmin)
	pev(entity, pev_absmax, absmax)

	absmin[2]+=64.0
	
	if(absmin[2] < absmax[2]) return false
	return true
}
