/*
	Asb -> A simple bot [ Inspoir ]
	
	Revision	: 1.00.0
	Description	: Provides Asb with an Aimbot
*/

local MODULE = {
	Name = "Aimbot",
	Hooks = {},
	Help = {
		"\n  -> Aimbot :",
		"    asb_bot_bone	: What bone to aim at			: 1-4",
		"    			NECK, SHOULDERS, SPINE, ABDOMIN\n",
		"    asb_bot_fov		: The bot's field of view		: 0-180",
		"    asb_bot_shoot	: Whether the bot will Autoshoot	: 0-1\n",
	}
}

// Local vars & functions {
	local self, target, aiming, nextshot = LocalPlayer, nil, false, CurTime()
	// convars {
		local _bone		= CreateClientConVar("asb_bot_bone", "2", true, false)
		local _fov		= CreateClientConVar("asb_bot_fov", "5", true, false)
		local _shoot	= CreateClientConVar("asb_bot_shoot", "1", true, false)
		local _team		= CreateClientConVar("asb_team", "1", true, false)
	// }
	local bones = {
		"ValveBiped.Bip01_Neck1",
		"ValveBiped.Bip01_Spine4",
		"ValveBiped.Bip01_Spine2",
		"ValveBiped.Bip01_Spine1"
	}
	local seizefire = {
		"weapon_phycannon",
		"weapon_physgun",
		"weapon_crowbar",
		"gmod_tool",
		nil
	}
	
	// Functions {
		local function GetBone( e )
			/* Returns the position of a bone */
			return e:GetBonePosition(e:LookupBone(bones[math.Clamp(_bone:GetInt(), 1, 4)]))
		end
		
		local function GetTrace( e )
			/* Traces from the player to the entity */
			local t = {
				start	= self():GetShootPos(),
				endpos		= GetBone(e),
				filter		= { self() }
			}
			
			return ( util.TraceLine(t).Entity == e )
		end
		
		local function InRange( v, a, z )
			/* Returns whether a Value is between a Min & Max */
			return ( v >= a && v <= z )
		end
		
		local function Normal( a )
			/* Returns a normalized Angle */
			return math.abs(math.NormalizeAngle(a))
		end
		
		local function SimulateShot()
			/* Simulates the shooting of a weapon */
			local w = self():GetActiveWeapon()
			if ( CurTime() < nextshot || w:IsWeapon() && table.HasValue(seizefire, w:GetClass()) || !w:IsWeapon() ) then return end
			
			local n = 0.1; if ( w.Primary && w.Primary.Delay ) then n = w.Primary.Delay end
			self():ConCommand("+attack; wait 2; -attack"); nextshot = CurTime() + n
		end
		
		local function ValidTarget( e )
			/* ValidTarget returns whether an Entity is considered an Enemy */
			if ( !ValidEntity(e) ) || ( e == self() ) || ( !e:IsNPC() && !e:IsPlayer() ) then return false end

			m = e:GetMoveType()	
			
			if ( m == MOVETYPE_NONE ) then return false end
			if ( e:IsPlayer() ) then
				if ( !e:Alive() ) || ( m == MOVETYPE_OBSERVER ) || ( e:Team() == self():Team() && !_team:GetBool() ) then return false end
			end
				
			return true
		end
	// }
// }

// Functions {
	function MODULE.Bot( u )
		/* This compensates to aim at the current target */
		if ( !aiming || !ValidTarget(target) ) then return end

		u:SetViewAngles((GetBone(target) - self():GetShootPos()):Angle())
		
		if ( !_shoot:GetBool() ) then return end
		local t = self():GetEyeTraceNoCursor()
		
		if ( t.Entity == target && InRange(t.HitGroup, 0, 3) ) then SimulateShot() end
	end
	
	function MODULE.Select()
		/* This makes sure the current target is valid, but only when aiming */
		if ( !aiming || ValidTarget(target) ) then return end

		local a
		for _, e in pairs( ents.GetAll() ) do
			if ( ValidTarget(e) ) then 
				a = self():GetAimVector():Angle() - (GetBone(e) - self():GetShootPos()):Angle()
				if ( Normal(a.y) < _fov:GetInt() && Normal(a.p) < _fov:GetInt() && GetTrace(e) ) then target = e end 
			end
		end
	end
	
	function MODULE.On()
		aiming = true
	end
	
	function MODULE.Off()
		aiming = false
		target = nil
	end
// }

// Register Hooks {
	concommand.Add("+asb_bot", MODULE.On)
	concommand.Add("-asb_bot", MODULE.Off)

	MODULE.Hooks["CreateMove"] = MODULE.Bot
	MODULE.Hooks["Think"] = MODULE.Select
	Asb:Register(MODULE)
// }
