CreateClientConVar( "_box", 0 )
CreateClientConVar( "_box_ff", 0 )
CreateClientConVar( "_box_size", 5 )
CreateClientConVar( "_box_npcs", 1 )
CreateClientConVar( "_box_weapons", 1 )
CreateClientConVar( "_box_items", 1 )
CreateClientConVar( "_box_showdead", 1 )

local function GetCoordinates( ent )
	local min, max = ent:OBBMins(), ent:OBBMaxs()
	local corners = {
		Vector( min.x, min.y, min.z ),
		Vector( min.x, min.y, max.z ),
		Vector( min.x, max.y, min.z ),
		Vector( min.x, max.y, max.z ),
		Vector( max.x, min.y, min.z ),
		Vector( max.x, min.y, max.z ),
		Vector( max.x, max.y, min.z ),
		Vector( max.x, max.y, max.z )
	}
	
	local minX, minY, maxX, maxY = ScrW() * 2, ScrH() * 2, 0, 0
	for _, corner in pairs( corners ) do
		local onScreen = ent:LocalToWorld( corner ):ToScreen()
		minX, minY = math.min( minX, onScreen.x ), math.min( minY, onScreen.y )
		maxX, maxY = math.max( maxX, onScreen.x ), math.max( maxY, onScreen.y )
	end
	
	return minX, minY, maxX, maxY
end

local deathSequences = {
	[ "models/barnacle.mdl" ] = { 4, 15 },
	[ "models/antlion_guard.mdl" ] = { 44 },
	[ "models/hunter.mdl" ] = { 124, 125, 126, 127, 128 }
}
local function FilterTargets( e )	
	local ply = LocalPlayer()
	local class = e:GetClass()
	
	local showdead = GetConVarNumber( "_box_showdead" ) > 0
	
	if ( e:IsNPC() ) then
		if ( GetConVarNumber( "_box_npcs" ) <= 0 ) then return end
		if ( !showdead && e:GetMoveType() == 0 ) then return end
		
		local model = string.lower( e:GetModel() or "" )
		if ( table.HasValue( deathSequences[ model ] or {}, e:GetSequence() ) ) then return false end // They're dead.
		
		return true
	end
	
	if ( e:IsPlayer() ) then
		if ( e == ply ) then return end
		if ( !showdead && !e:Alive() ) then return end
		if ( GetConVarNumber( "_box_ff" ) <= 0 && e:Team() == ply:Team() ) then return end		
		
		return true, { e:Name(), e:Health() }, team.GetColor( e:Team() )
	end
	
	if ( e:IsWeapon() ) then
		if ( GetConVarNumber( "_box_weapons" ) <= 0 ) then return end
		if ( ValidEntity( e:GetOwner() ) ) then return end
		return true
	end
	
	if ( class == "item_healthcharger" ) then
		local bone = e:LookupBone( "roundcap" )
		if ( bone ) then
			local pos = e:GetBonePosition( bone )
			pos = e:WorldToLocal( pos )
			
			local full, empty = 5.87, 2.1
			
			local percent = math.floor( ( ( pos.x - empty ) / ( full - empty ) ) * 100 )
			percent = math.Clamp( percent, 0, 100 )
			
			return true, { class, percent .. "%" }
		end
	end
	
	if ( string.find( class, "item" ) ) then
		if ( GetConVarNumber( "_box_items" ) <= 0 ) then return end
		return true
	end

end

local targets = {}
local function FindTargets()
	if ( GetConVarNumber( "_box" ) <= 0 ) then return end
	
	targets = {}	
	for _, e in pairs( ents.GetAll() ) do
		local valid, name, colour = FilterTargets( e )
		if ( valid == true ) then
			if ( type( name ) != "table" ) then name = { name or e:GetClass() } end
			targets[ #targets + 1 ] = { Entity = e, Name = name, Colour = colour or Color( 255, 255, 255 ) }
		end
	end
end
timer.Create( "_boxy", 0.25, 0, FindTargets )
local function GetTargets()
	return targets
end

local function HUDPaint()
	if ( GetConVarNumber( "_box" ) <= 0 ) then return end
	
	local cx, cy = ScrW() / 2, ScrH() / 2
	
	local ff = GetConVarNumber( "_box_ff" ) > 0
	
	local size = GetConVarNumber( "_box_size" )
	local maxShow = GetConVarNumber( "_box_maxshow" )
	
	local ply = LocalPlayer()
	local myTeam = ply:Team()
	
	local targets = {}
	for _, info in pairs( GetTargets() ) do
		local target = info.Entity
		if ( ValidEntity( target ) ) then
			local x1, y1, x2, y2 = GetCoordinates( target )
			targets[ #targets + 1 ] = { Entity = target, Coord = { x1, y1, x2, y2 }, Length = ( Vector( ( x1 + x2 ) / 2, y1, 0 ) - Vector( cx, cy, 0 ) ):Length(), Name = info.Name, Colour = info.Colour }
		end
	end
	table.sort( targets, function( a, b ) return a.Length < b.Length end )
		
	local targNum = 0
	for _, info in pairs( targets ) do
		local ent = info.Entity
		local x1, y1, x2, y2 = unpack( info.Coord )
			
			local col = info.Colour
			surface.SetDrawColor( col.r, col.g, col.b, 255 )
			
			// Top left.
			surface.DrawLine( x1, y1, math.min( x1 + size, x2 ), y1 )
			surface.DrawLine( x1, y1, x1, math.min( y1 + size, y2 ) )
			
			// Top right.
			surface.DrawLine( x2, y1, math.max( x2 - size, x1 ), y1 ) 
			surface.DrawLine( x2, y1, x2, math.min( y1 + size, y2 ) )
			
			// Bottom left.
			surface.DrawLine( x1, y2, math.min( x1 + size, x2 ), y2 ) 
			surface.DrawLine( x1, y2, x1, math.max( y2 - size, y1 ) )
			
			// Bottom right.
			surface.DrawLine( x2, y2, math.max( x2 - size, x1 ), y2 ) 
			surface.DrawLine( x2, y2, x2, math.max( y2 - size, y1 ) )
			
			local mx, my = ( x1 + x2 ) / 2, ( y1 + y2 ) / 2
			
			
			targNum = targNum + 1
	end
end
hook.Add( "HUDPaint", "_box", HUDPaint )

local function Menu( panel )
	panel:CheckBox( "Enabled", "_box" )
	panel:Help( "" )
	panel:CheckBox( "Show teammates", "_box_ff" )
	panel:CheckBox( "Show NPCs", "_box_npcs" )
	panel:CheckBox( "Show weapons", "_box_weapons" )
	panel:CheckBox( "Show items", "_box_items" )
	panel:CheckBox( "Show dead players/NPCs", "_box_showdead" )
	panel:Help( "" )	
	panel:NumSlider( "Corner size", "_box_size", 1, 30, 0 )
end
local function PopulateToolMenu()
	spawnmenu.AddToolMenuOption( "Options", "Hacks", "Box ESP", "Box ESP", "", "", Menu )
end
hook.Add( "PopulateToolMenu", "Box ESP", PopulateToolMenu )