#include <amxmodx>
#include <cstrike>
#include <fakemeta>

#define PLUGIN	"Block WallHack"
#define AUTHOR	"OT"
#define VERSION	"4.1"

#define MAX_PLAYERS							32
// Uncomment this if you want to see what the plugin actually checks, the plugin will not block any wallhack in test mode!!!
//#define point_test
//#define ignore_bots
//#define target_check

#define GENERAL_X_Y_SORROUNDING 			18.5 		// 16.0
#define CONSTANT_Z_CROUCH_UP 				31.25 		// 32.0
#define CONSTANT_Z_CROUCH_DOWN 				17.5 		// 16.0
#define CONSTANT_Z_STANDUP_UP 				34.0 		// 36.0
#define CONSTANT_Z_STANDUP_DOWN 			35.25 		// 36.0

#define GENERAL_X_Y_SORROUNDING_HALF		9.25 		// 8.0
#define GENERAL_X_Y_SORROUNDING_HALF2		12.0 		// 8.0
#define CONSTANT_Z_CROUCH_UP_HALF 			15.5 		// 16.0
#define CONSTANT_Z_CROUCH_DOWN_HALF			8.75 		// 8.0
#define CONSTANT_Z_STANDUP_UP_HALF			17.0 		// 18.0
#define CONSTANT_Z_STANDUP_DOWN_HALF		17.5 		// 18.0

#define ANGLE_COS_HEIGHT_CHECK				0.7071		// cos(45  degrees)

#define CONSTANT_MULTI_POSITION 			3.5
#define CONSTANT_MAXIMUM_VELOCITY			35.0

new const Float:weapon_edge_point[CSW_P90+1] =
{
	0.00, 35.5, 0.00, 42.0, 0.00, 35.5, 0.00, 37.0, 37.0, 0.00, 35.5, 35.5, 32.0, 41.0, 32.0, 36.0, 41.0, 35.5, 41.0, 32.0, 37.0, 35.5, 42.0, 41.0, 44.0, 0.00, 35.5, 37.0, 32.0, 0.00, 32.0
}

new const Float:vec_multi_lateral[] = 
{
	GENERAL_X_Y_SORROUNDING,
	-GENERAL_X_Y_SORROUNDING,
	GENERAL_X_Y_SORROUNDING_HALF2,
	-GENERAL_X_Y_SORROUNDING_HALF
}

new const Float:vec_add_height_crouch[] =
{
	CONSTANT_Z_CROUCH_UP,
	-CONSTANT_Z_CROUCH_DOWN,
	CONSTANT_Z_CROUCH_UP_HALF,
	-CONSTANT_Z_CROUCH_DOWN_HALF
}

new const Float:vec_add_height_standup[] =
{
	CONSTANT_Z_STANDUP_UP,
	-CONSTANT_Z_STANDUP_DOWN,
	CONSTANT_Z_STANDUP_UP_HALF,
	-CONSTANT_Z_STANDUP_DOWN_HALF
}

new bool:connect[MAX_PLAYERS + 1]
new bool:ducking[MAX_PLAYERS + 1]
new bool:alive[MAX_PLAYERS + 1]
new team[MAX_PLAYERS + 1]
new weapon[MAX_PLAYERS + 1]
new bool:bot[MAX_PLAYERS + 1] = {false, ...}
new target[MAX_PLAYERS + 1] = {0, ...}
new see_by[MAX_PLAYERS + 1] = {0, ...}
new seeable[MAX_PLAYERS + 1] = {0, ...}

new pcv_on_off
new pcv_ignoreteam
new pcv_blockents
new pcv_fov_check
new pcv_tg_check
new pcv_engine_pvs
new pcv_smooth
new pcv_texture

new pcg_on_off
new pcg_ignore
new pcg_blockents
new pcg_fov_check
new pcg_tg_check
new pcg_engine_pvs
new pcg_smooth
new pcg_texture

new thdl

new g_maxplayers

stock spr_bomb
stock beampoint

// These bitsums allow 2048 entities storage. I think that it is enough :P.
new bs_array_transp[64]				// BitSum, This is equal to 64*32 bools (good for quick search)
new bs_array_solid[64]				// BitSum, This is equal to 64*32 bools (good for quick search)

#define add_transparent_ent(%1) 	bs_array_transp[((%1 - 1) / 32)] |= (1<<((%1 - 1) % 32))
#define del_transparent_ent(%1) 	bs_array_transp[((%1 - 1) / 32)] &= ~(1<<((%1 - 1) % 32))
#define  is_transparent_ent(%1)		(bs_array_transp[((%1 - 1) / 32)] & (1<<((%1 - 1) % 32)))
#define add_solid_ent(%1) 			bs_array_solid[((%1 - 1) / 32)] |= (1<<((%1 - 1) % 32))
#define del_solid_ent(%1) 			bs_array_solid[((%1 - 1) / 32)] &= ~(1<<((%1 - 1) % 32))
#define  is_solid_ent(%1)			(bs_array_solid[((%1 - 1) / 32)] & (1<<((%1 - 1) % 32)))

public plugin_precache()
{
	spr_bomb = precache_model("sprites/ledglow.spr")
	beampoint = precache_model("sprites/laserbeam.spr")
	
	register_forward(FM_Spawn, "fw_spawn", 1)
}

public fw_spawn(ent)
{
	if (!pev_valid(ent))
		return FMRES_IGNORED
	
	static rendermode, Float:renderamt
	
	rendermode = pev(ent, pev_rendermode)
	pev(ent, pev_renderamt, renderamt)
	
	if (((rendermode == kRenderTransColor || rendermode == kRenderGlow || rendermode == kRenderTransTexture) && renderamt < 255.0) || (rendermode == kRenderTransAdd))
	{
		add_transparent_ent(ent)
		return FMRES_IGNORED
	}
	
	return FMRES_IGNORED
}

public plugin_init()
{
	register_plugin(PLUGIN, VERSION, AUTHOR)
	register_cvar("wallblocker_version", VERSION, FCVAR_SPONLY | FCVAR_SERVER)
	
	pcv_on_off     = register_cvar("wallblocker_enable",       "1", FCVAR_SPONLY | FCVAR_SERVER)
	pcv_ignoreteam = register_cvar("wallblocker_ignoreteam",   "1")
	pcv_blockents  = register_cvar("wallblocker_block_ents",   "1")
	pcv_fov_check  = register_cvar("wallblocker_fov_check",    "1")
	pcv_tg_check   = register_cvar("wallblocker_target_check", "1")
	pcv_engine_pvs = register_cvar("wallblocker_engine_check", "1")
	pcv_smooth	   = register_cvar("wallblocker_smooth_check", "1")
	pcv_texture    = register_cvar("wallblocker_textureallow", "1")
	
	register_forward(FM_AddToFullPack, 	"fw_addtofullpack" , 0)
	register_forward(FM_PlayerPreThink,	"fw_prethink"	   , 0)
	register_forward(FM_TraceLine,		"fw_traceline_post", 1)
	
	register_clcmd("wallblocker", "cmd_version_show")
	
	register_event("CurWeapon", "event_active_weapon", "be")
	
	register_message(get_user_msgid("ClCorpse"), "message_clcorpse")
	
	thdl = create_tr2()
	
	g_maxplayers = get_maxplayers()
	
	for (new i=1;i<=g_maxplayers;i++)
	{
		add_solid_ent(i)
	}
}

public message_clcorpse(msg_id, msg_dest, entity)
{
	if (!connect[get_msg_arg_int(12)])
		return PLUGIN_HANDLED
	
	return PLUGIN_CONTINUE
}

public plugin_end()
{
	free_tr2(thdl)
}

public client_connect(id)
{
	connect[id] = false
	alive[id] = false
}

public client_putinserver(id)
{
#if defined ignore_bots
	bot[id] = false
#else
	if (is_user_bot(id))
		bot[id] = true
	else
		bot[id] = false
#endif
	
	connect[id] = true
	
	if (bot[id] == false)
		set_task(5.0, "task_announce", id)
}

public task_announce(id)
{
	client_print(id, print_chat, "This server is using WallBlocker v%s, made by OT",VERSION)
}

public cmd_version_show(id)
{
	client_print(id, print_console, "This server is using WallBlocker v%s",VERSION)
}

public client_disconnect(id)
{
	bot[id] = false
	alive[id] = false
	connect[id] = false
}

public event_active_weapon(id)
{
	pcg_ignore 	   = get_pcvar_num(pcv_ignoreteam)
	pcg_on_off	   = get_pcvar_num(pcv_on_off)
	pcg_blockents  = get_pcvar_num(pcv_blockents)
	pcg_fov_check  = get_pcvar_num(pcv_fov_check)
	pcg_tg_check   = get_pcvar_num(pcv_tg_check)
	pcg_engine_pvs = get_pcvar_num(pcv_engine_pvs)
	pcg_smooth 	   = get_pcvar_num(pcv_smooth)
	pcg_texture	   = get_pcvar_num(pcv_texture)
	
	if (read_data(1) == 1)
	{
		weapon[id] = read_data(2)
		
		if (weapon[id] < CSW_P228 || weapon[id] > CSW_P90)
		{
			weapon[id] = CSW_KNIFE
		}
	}
	
	return PLUGIN_CONTINUE
}

public fw_prethink(id)
{
	if (!pcg_on_off)
		return FMRES_IGNORED
	
	if (!connect[id])
		return FMRES_IGNORED
	
	if (!is_user_alive(id))
	{
		alive[id] = false
	}
	else
	{
		alive[id] = true
		
		if (pev(id, pev_flags) & FL_DUCKING)
			ducking[id] = true
		else
			ducking[id] = false
	}
	
	team[id] = _:cs_get_user_team(id)
	
	return FMRES_IGNORED
}

public fw_addtofullpack(es, e, ent, host, flags, player, set)
{
	if (!pcg_on_off)
		return FMRES_IGNORED
	
	if (!connect[host])
		return FMRES_IGNORED
	
	if (!alive[host])
		return FMRES_IGNORED
	
	if (bot[host] || team[host] == _:CS_TEAM_SPECTATOR || team[host] == _:CS_TEAM_UNASSIGNED)
		return FMRES_IGNORED
	
	if (!player && !pcg_blockents)
	{
		return FMRES_IGNORED
	}
	
	if (!player && pcg_blockents)
	{
		if (!pev_valid(ent))
			return FMRES_IGNORED
		
		if (pev(ent, pev_owner) == host)
			return FMRES_IGNORED
		
		static class_name[10]
		pev(ent, pev_classname, class_name, charsmax(class_name))
		
		if (!equal(class_name, "grenade"))
		{
			if (!equal(class_name, "weaponbox"))
			{
				return FMRES_IGNORED
			}
		}
		
		if (pcg_engine_pvs)
		{
			if (!engfunc(EngFunc_CheckVisibility, ent, set))
			{
				// the ent cannot be seen so we block the send channel
				forward_return(FMV_CELL, 0)
				return FMRES_SUPERCEDE
			}
		}
		
		static Float:origin[3], Float:end[3], Float:plane_vec[3], Float:normal[3]
		
		pev(host, pev_origin, origin)
		
		if (pcg_fov_check)
		{
			pev(host, pev_v_angle, normal)
			angle_vector(normal,ANGLEVECTOR_FORWARD, normal)
			
			pev(ent, pev_origin, end)
			xs_vec_sub(end, origin, plane_vec)
			xs_vec_mul_scalar(plane_vec,  (1.0/xs_vec_len(plane_vec)), plane_vec)
			
			if (xs_vec_dot(plane_vec, normal) < 0)
			{
				forward_return(FMV_CELL, 0)
				return FMRES_SUPERCEDE
			}
			
			pev(host, pev_view_ofs, plane_vec)
			xs_vec_add(plane_vec, origin, origin)
		}
		else
		{
			pev(host, pev_view_ofs, end)
			
			xs_vec_add(end, origin, origin)
			
			pev(ent, pev_origin, end)
		}
		
		if (is_point_almost_visible(origin, end, ent))
			return FMRES_IGNORED
		
#if defined point_test
		return FMRES_IGNORED
#else
		// the player cannot be seen so we block the send channel
		forward_return(FMV_CELL, 0)
		return FMRES_SUPERCEDE
#endif
	}
	
	if (!alive[ent])
		return FMRES_IGNORED
	
	if (pcg_ignore && team[ent] == team[host])
		return FMRES_IGNORED
	
	if (host != ent)
	{
		if (pcg_tg_check)
		{
			if (see_by[host] & (1<<(ent-1)))
			{
				see_by[host] &= ~(1<<(ent-1))
				seeable[host] |= (1<<(ent-1))
				return FMRES_IGNORED
			}
			
			if (target[host] == ent)
			{
				seeable[host] |= (1<<(ent-1))
				return FMRES_IGNORED
			}
		}
		
		if (pcg_engine_pvs)
		{
			if (!engfunc(EngFunc_CheckVisibility, ent, set))
			{
				// the ent cannot be seen so we block the send channel
				seeable[host] &= ~(1<<(ent-1))
				forward_return(FMV_CELL, 0)
				return FMRES_SUPERCEDE
			}
		}
		
		static Float:origin[3], Float:start[3], Float:end[3], Float:addict[3], Float:plane_vec[3], Float:normal[3], ignore_ent
		
		ignore_ent = host
		
		pev(host, pev_origin, origin)
		
		if (pcg_fov_check)
		{
			pev(host, pev_v_angle, normal)
			angle_vector(normal, ANGLEVECTOR_FORWARD, normal)
			
			pev(ent, pev_origin, end)
			xs_vec_sub(end, origin, plane_vec)
			xs_vec_mul_scalar(plane_vec,  (1.0/xs_vec_len(plane_vec)), plane_vec)
			
			if (xs_vec_dot(plane_vec, normal) < 0)
			{
				seeable[host] &= ~(1<<(ent-1))
				forward_return(FMV_CELL, 0)
				return FMRES_SUPERCEDE
			}
			
			pev(host, pev_view_ofs, start)
			xs_vec_add(start, origin, start)
			
			if (pcg_smooth && !(seeable[host] & (1<<(ent-1))))
			{
				pev(host, pev_velocity, origin)
				
				if (!xs_vec_equal(origin, Float:{0.0, 0.0, 0.0}))
				{
					if (xs_vec_len(origin) <= CONSTANT_MAXIMUM_VELOCITY)
						xs_vec_mul_scalar(origin, 0.1, origin)
					else
						xs_vec_mul_scalar(origin, CONSTANT_MULTI_POSITION/xs_vec_len(origin), origin)
					
					xs_vec_add(start, origin, start)
				}
				
				pev(ent, pev_velocity, origin)
				
				if (!xs_vec_equal(origin, Float:{0.0, 0.0, 0.0}))
				{
					if (xs_vec_len(origin) <= CONSTANT_MAXIMUM_VELOCITY)
						xs_vec_mul_scalar(origin, 0.1, origin)
					else
						xs_vec_mul_scalar(origin, CONSTANT_MULTI_POSITION/xs_vec_len(origin), origin)
					
					xs_vec_add(origin, end, origin)
				}
				else
				{
					origin = end
				}
			}
			else
			{
				origin = end
			}
		}
		else
		{
			pev(host, pev_view_ofs, start)
			xs_vec_add(start, origin, start)
			pev(ent, pev_origin, end)
			
			if (pcg_smooth && !(seeable[host] & (1<<(ent-1))))
			{
				pev(host, pev_velocity, origin)
				
				if (!xs_vec_equal(origin, Float:{0.0, 0.0, 0.0}))
				{
					if (xs_vec_len(origin) <= CONSTANT_MAXIMUM_VELOCITY)
						xs_vec_mul_scalar(origin, 0.1, origin)
					else
						xs_vec_mul_scalar(origin, CONSTANT_MULTI_POSITION/xs_vec_len(origin), origin)
					
					xs_vec_add(start, origin, start)
				}
				
				pev(ent, pev_velocity, origin)
				
				if (!xs_vec_equal(origin, Float:{0.0, 0.0, 0.0}))
				{
					if (xs_vec_len(origin) <= CONSTANT_MAXIMUM_VELOCITY)
						xs_vec_mul_scalar(origin, 0.1, origin)
					else
						xs_vec_mul_scalar(origin, CONSTANT_MULTI_POSITION/xs_vec_len(origin), origin)
					
					xs_vec_add(origin, end, origin)
				}
				else
				{
					origin = end
				}
			}
			else
			{
				origin = end
			}
		}
		
		xs_vec_sub(start, origin, normal)
		
		// If origin is visible don't do anything
		if (pcg_texture)
		{
			if (is_point_visible_texture(start, origin, ignore_ent))
			{
				seeable[host] |= (1<<(ent-1))
				return FMRES_IGNORED
			}
		}
		else
		{
			if (is_point_visible(start, origin, ignore_ent))
			{
				seeable[host] |= (1<<(ent-1))
				return FMRES_IGNORED
			}
		}
		
		pev(ent, pev_view_ofs, end)
		xs_vec_add(end, origin, end)
		
		// If eye origin is visible don't do anything
		if (is_point_visible(start, end, ignore_ent))
		{
			seeable[host] |= (1<<(ent-1))
			return FMRES_IGNORED
		}
		
		// Check weapon point
		if (weapon_edge_point[weapon[ent]] != 0.00)
		{
			pev(ent, pev_v_angle, addict)
			angle_vector(addict, ANGLEVECTOR_FORWARD, addict)
			xs_vec_mul_scalar(addict, weapon_edge_point[weapon[ent]], addict)
			xs_vec_add(end, addict, end)
			
			// If weapon head is visible don't do anything
			if (is_point_visible(start, end, ignore_ent))
			{
				seeable[host] |= (1<<(ent-1))
				return FMRES_IGNORED
			}
		}
		
		// We use this to obtain the plain.
		xs_vec_mul_scalar(normal, 1.0/(xs_vec_len(normal)), normal)
		vector_to_angle(normal, plane_vec)
		angle_vector(plane_vec, ANGLEVECTOR_RIGHT, plane_vec)
		
		if (floatabs(normal[2]) <= ANGLE_COS_HEIGHT_CHECK)
		{
			if (ducking[ent])
			{
				for (new i=0;i<4;i++)
				{
					if (i<2)
					{
						for (new j=0;j<2;j++)
						{
							xs_vec_mul_scalar(plane_vec, vec_multi_lateral[i], addict)
							addict[2] = vec_add_height_crouch[j]
							xs_vec_add(origin, addict, end)
							
							if (is_point_visible(start, end, ignore_ent))
							{
								seeable[host] |= (1<<(ent-1))
								return FMRES_IGNORED
							}
						}
					}
					else
					{
						for (new j=2;j<4;j++)
						{
							xs_vec_mul_scalar(plane_vec, vec_multi_lateral[i], addict)
							addict[2] = vec_add_height_crouch[j]
							xs_vec_add(origin, addict, end)
							
							if (is_point_visible(start, end, ignore_ent))
							{
								seeable[host] |= (1<<(ent-1))
								return FMRES_IGNORED
							}
						}
					}
				}
			}
			else
			{
				for (new i=0;i<4;i++)
				{
					if (i<2)
					{
						for (new j=0;j<2;j++)
						{
							xs_vec_mul_scalar(plane_vec, vec_multi_lateral[i], addict)
							addict[2] = vec_add_height_standup[j]
							xs_vec_add(origin, addict, end)
							
							if (is_point_visible(start, end, ignore_ent))
							{
								seeable[host] |= (1<<(ent-1))
								return FMRES_IGNORED
							}
						}
					}
					else
					{
						for (new j=2;j<4;j++)
						{
							xs_vec_mul_scalar(plane_vec, vec_multi_lateral[i], addict)
							addict[2] = vec_add_height_standup[j]
							xs_vec_add(origin, addict, end)
							
							if (is_point_visible(start, end, ignore_ent))
							{
								seeable[host] |= (1<<(ent-1))
								return FMRES_IGNORED
							}
						}
					}
				}
			}
		}
		else
		{
			if (normal[2] > 0.0)
			{
				normal[2] = 0.0
				xs_vec_mul_scalar(normal, 1/(xs_vec_len(normal)), normal)
				
				if (ducking[ent])
				{
					for (new i=0;i<4;i++)
					{
						if (i<2)
						{
							for (new j=0;j<2;j++)
							{
								xs_vec_mul_scalar(plane_vec, vec_multi_lateral[i], addict)
								addict[2] = vec_add_height_crouch[j]
								xs_vec_add(origin, addict, end)
								xs_vec_mul_scalar(normal, (j == 0) ? (-GENERAL_X_Y_SORROUNDING) : (GENERAL_X_Y_SORROUNDING), addict)
								xs_vec_add(end, addict, end)
								
								if (is_point_visible(start, end, ignore_ent))
								{
									seeable[host] |= (1<<(ent-1))
									return FMRES_IGNORED
								}
							}
						}
						else
						{
							for (new j=2;j<4;j++)
							{
								xs_vec_mul_scalar(plane_vec, vec_multi_lateral[i], addict)
								addict[2] = vec_add_height_crouch[j]
								xs_vec_add(origin, addict, end)
								
								if (is_point_visible(start, end, ignore_ent))
								{
									seeable[host] |= (1<<(ent-1))
									return FMRES_IGNORED
								}
							}
						}
					}
				}
				else
				{
					for (new i=0;i<4;i++)
					{
						if (i<2)
						{
							for (new j=0;j<2;j++)
							{
								xs_vec_mul_scalar(plane_vec, vec_multi_lateral[i], addict)
								addict[2] = vec_add_height_standup[j]
								xs_vec_add(origin, addict, end)
								xs_vec_mul_scalar(normal, (j == 0) ? (-GENERAL_X_Y_SORROUNDING) : (GENERAL_X_Y_SORROUNDING), addict)
								xs_vec_add(end, addict, end)
								
								if (is_point_visible(start, end, ignore_ent))
								{
									seeable[host] |= (1<<(ent-1))
									return FMRES_IGNORED
								}
							}
						}
						else
						{
							for (new j=2;j<4;j++)
							{
								xs_vec_mul_scalar(plane_vec, vec_multi_lateral[i], addict)
								addict[2] = vec_add_height_standup[j]
								xs_vec_add(origin, addict, end)
								
								if (is_point_visible(start, end, ignore_ent))
								{
									seeable[host] |= (1<<(ent-1))
									return FMRES_IGNORED
								}
							}
						}
					}
				}
			}
			else
			{
				normal[2] = 0.0
				xs_vec_mul_scalar(normal, 1/(xs_vec_len(normal)), normal)
				
				if (ducking[ent])
				{
					for (new i=0;i<4;i++)
					{
						if (i<2)
						{
							for (new j=0;j<2;j++)
							{
								xs_vec_mul_scalar(plane_vec, vec_multi_lateral[i], addict)
								addict[2] = vec_add_height_crouch[j]
								xs_vec_add(origin, addict, end)
								xs_vec_mul_scalar(normal, (j == 0) ? GENERAL_X_Y_SORROUNDING : (-GENERAL_X_Y_SORROUNDING), addict)
								xs_vec_add(end, addict, end)
								
								if (is_point_visible(start, end, ignore_ent))
								{
									seeable[host] |= (1<<(ent-1))
									return FMRES_IGNORED
								}
							}
						}
						else
						{
							for (new j=2;j<4;j++)
							{
								xs_vec_mul_scalar(plane_vec, vec_multi_lateral[i], addict)
								addict[2] = vec_add_height_crouch[j]
								xs_vec_add(origin, addict, end)
								
								if (is_point_visible(start, end, ignore_ent))
								{
									seeable[host] |= (1<<(ent-1))
									return FMRES_IGNORED
								}
							}
						}
					}
				}
				else
				{
					for (new i=0;i<4;i++)
					{
						if (i<2)
						{
							for (new j=0;j<2;j++)
							{
								xs_vec_mul_scalar(plane_vec, vec_multi_lateral[i], addict)
								addict[2] = vec_add_height_standup[j]
								xs_vec_add(origin, addict, end)
								xs_vec_mul_scalar(normal, (j == 0) ? GENERAL_X_Y_SORROUNDING : (-GENERAL_X_Y_SORROUNDING), addict)
								xs_vec_add(end, addict, end)
								
								if (is_point_visible(start, end, ignore_ent))
								{
									seeable[host] |= (1<<(ent-1))
									return FMRES_IGNORED
								}
							}
						}
						else
						{
							for (new j=2;j<4;j++)
							{
								xs_vec_mul_scalar(plane_vec, vec_multi_lateral[i], addict)
								addict[2] = vec_add_height_standup[j]
								xs_vec_add(origin, addict, end)
								
								if (is_point_visible(start, end, ignore_ent))
								{
									seeable[host] |= (1<<(ent-1))
									return FMRES_IGNORED
								}
							}
						}
					}
				}
			}
		}
		
#if defined point_test
		return FMRES_IGNORED
#else
		seeable[host] &= ~(1<<(ent-1))
		// the player cannot be seen so we block the send channel
		forward_return(FMV_CELL, 0)
		return FMRES_SUPERCEDE
#endif
	}
	
	return FMRES_IGNORED
}

public fw_traceline_post(const Float:start[3], const Float:end[3], cond, id, tr)
{
	if (!pcg_on_off)
		return FMRES_IGNORED
	
	if (id <= 0 || id > g_maxplayers)
		return FMRES_IGNORED
	
	if (!connect[id])
		return FMRES_IGNORED
	
	target[id] = get_tr(TR_pHit)
	
	if (pcg_tg_check)
	{
		
		if (1 <= target[id] <= g_maxplayers)
			see_by[target[id]] |=  (1<<(id-1))
		
#if defined target_check
		
		new name[32], bs = see_by[id], string[400]
		new i=0
		
		if (1 <= target[id] <= g_maxplayers)
		{
			get_user_name(target[id], name, charsmax(name))
			client_print(id, print_chat, "SEE: %s", name)
		}
		
		format(string, charsmax(string), "")
		
		while (bs != 0)
		{
			if ( bs & (1<<i) )
			{
				get_user_name((i+1), name, charsmax(name))
				format(string, charsmax(string), "%s%s ", string, name)
				bs &= ~(1<<i)
			}
			
			i++
		}
		
		client_print(id, print_chat, "SEE BY: %s", string)
		
#endif
		
	}
	
	return FMRES_IGNORED
}

#if !defined point_test
bool:is_point_visible(const Float:start[3], const Float:point[3], ignore_ent)
{
	engfunc(EngFunc_TraceLine, start, point, IGNORE_GLASS | IGNORE_MONSTERS, ignore_ent, thdl)

	static Float:fraction
	get_tr2(thdl, TR_flFraction, fraction)
	
	return (fraction == 1.0)
}

bool:is_point_visible_texture(const Float:start[3], const Float:point[3], ignore_ent)
{
	engfunc(EngFunc_TraceLine, start, point, IGNORE_GLASS | IGNORE_MONSTERS, ignore_ent, thdl)
	
	static ent
	ent = get_tr2(thdl, TR_pHit)

	static Float:fraction
	get_tr2(thdl, TR_flFraction, fraction)
	
	if (fraction != 1.0 && ent > 0)
	{
		if (!is_transparent_ent(ent) && !is_solid_ent(ent))
		{
			static texture_name[2]
			static Float:vec[3]
			xs_vec_sub(point, start, vec)
			xs_vec_mul_scalar(vec, (5000.0 / xs_vec_len(vec)), vec)
			xs_vec_add(start, vec, vec)
			
			engfunc(EngFunc_TraceTexture, ent, start, vec, texture_name, charsmax(texture_name))
			
			if (equal(texture_name, "{"))
			{
				add_transparent_ent(ent)
				ignore_ent = ent
				engfunc(EngFunc_TraceLine, start, point, IGNORE_GLASS | IGNORE_MONSTERS, ignore_ent, thdl)
				get_tr2(thdl, TR_flFraction, fraction)
				return (fraction == 1.0)
			}
			else
			{
				add_solid_ent(ent)
				return (fraction == 1.0)
			}
		}
		else
		{
			if (is_solid_ent(ent))
			{
				return (fraction == 1.0)
			}
			else
			{
				ignore_ent = ent
				engfunc(EngFunc_TraceLine, start, point, IGNORE_GLASS | IGNORE_MONSTERS, ignore_ent, thdl)
				get_tr2(thdl, TR_flFraction, fraction)
				return (fraction == 1.0)
			}
		}
	}
	
	return (fraction == 1.0)
}


bool:is_point_almost_visible(const Float:start[3], const Float:point[3], ignore_ent)
{
	engfunc(EngFunc_TraceLine, start, point, IGNORE_GLASS | IGNORE_MONSTERS, ignore_ent, thdl)

	static Float:fraction
	get_tr2(thdl, TR_flFraction, fraction)
	
	return (fraction >= 0.9)
}
#else

bool:is_point_visible(const Float:start[3], const Float:point[3], ignore_ent)
{
	bomb_led(point)
	return false
}


bool:is_point_almost_visible(const Float:start[3], const Float:point[3], ignore_ent)
{
	bomb_led(point)
	return false
}
#endif

stock xs_vec_add(const Float:in1[], const Float:in2[], Float:out[])
{
	out[0] = in1[0] + in2[0];
	out[1] = in1[1] + in2[1];
	out[2] = in1[2] + in2[2];
}

stock xs_vec_sub(const Float:in1[], const Float:in2[], Float:out[])
{
	out[0] = in1[0] - in2[0];
	out[1] = in1[1] - in2[1];
	out[2] = in1[2] - in2[2];
}

stock xs_vec_mul_scalar(const Float:vec[], Float:scalar, Float:out[])
{
	out[0] = vec[0] * scalar;
	out[1] = vec[1] * scalar;
	out[2] = vec[2] * scalar;
}

stock Float:xs_vec_len(const Float:vec[3])
{
	return floatsqroot(vec[0]*vec[0] + vec[1]*vec[1] + vec[2]*vec[2]);
}

stock Float:xs_vec_dot(const Float:vec[], const Float:vec2[])
{
	return (vec[0]*vec2[0] + vec[1]*vec2[1] + vec[2]*vec2[2])
}

bool:xs_vec_equal(const Float:vec1[], const Float:vec2[])
{
	return (vec1[0] == vec2[0]) && (vec1[1] == vec2[1]) && (vec1[2] == vec2[2]);
}

stock bomb_led(const Float:point[3])
{
	message_begin(MSG_BROADCAST, SVC_TEMPENTITY)
	write_byte(TE_GLOWSPRITE)
	engfunc(EngFunc_WriteCoord, point[0])
	engfunc(EngFunc_WriteCoord, point[1])
	engfunc(EngFunc_WriteCoord, point[2])
	write_short(spr_bomb)
	write_byte(1)
	write_byte(3)
	write_byte(255)
	message_end()
}


stock draw_line(const Float:start[3], const Float:end[3])
{
	engfunc(EngFunc_MessageBegin, MSG_ALL, SVC_TEMPENTITY, Float:{0.0,0.0,0.0}, 0)
	write_byte(TE_BEAMPOINTS)
	engfunc(EngFunc_WriteCoord, start[0])
	engfunc(EngFunc_WriteCoord, start[1])
	engfunc(EngFunc_WriteCoord, start[2])
	engfunc(EngFunc_WriteCoord, end[0])
	engfunc(EngFunc_WriteCoord, end[1])
	engfunc(EngFunc_WriteCoord, end[2])
	write_short(beampoint)
	write_byte(0)
	write_byte(0)
	write_byte(25)
	write_byte(10)
	write_byte(0)
	write_byte(255)
	write_byte(255)
	write_byte(255)
	write_byte(127)
	write_byte(1)
	message_end()	
}