make into a sword fight bot for the local player that starts the instant the script is ran


local Funcs = {}

-- Functions from AI.Util
Funcs.VisualizePosition = function(Position)
	local Part = Instance.new("Part")
	Part.Position = Position
	Part.CanCollide = false
	Part.Parent = workspace.Filter
	Part.Size = Vector3.new(0.1,0.1,0.1)
	Part.Anchored = true
end

Funcs.GetDist = function(Model, Position)
	return (Model["Right Arm"].Position - Position).Magnitude
end

Funcs.GetClosestEnemy = function(Model)
	local ClosestEnemy = nil
	local ClosestDistance = math.huge
	for i, v in pairs(workspace.ActivePlayers:GetChildren()) do 
		if v ~= Model then
			local Dist = (v.PrimaryPart.Position - Model.PrimaryPart.Position).Magnitude
			if Dist < ClosestDistance then
				ClosestDistance = Dist
				ClosestEnemy = v
			end
		end
	end

	return ClosestEnemy
end

Funcs.GetDistance = function (Model, OtherModel)
	return (Model.PrimaryPart.Position - OtherModel.PrimaryPart.Position).Magnitude
end

Funcs.GetClosestPart = function(Position, OtherModel)
	local ClosestPart = nil
	local ClosestDistance = math.huge
	for i,v in pairs(OtherModel:GetChildren()) do 
		if v:IsA("BasePart") then 
			local Offset = Vector3.new(0,0,0)
			if v.Name == "Right Arm" then 

				Offset = OtherModel.PrimaryPart.CFrame.LookVector*3
			end

			local Dist = (Position - (v.Position+Offset)).Magnitude
			if Dist < ClosestDistance then
				ClosestDistance = Dist
				ClosestPart = v
			end
		end
	end

	return ClosestPart
end

Funcs.GetRetreatDirection = function(Model, ClosestEnemy, Dist)
	local Difference = (Model.PrimaryPart.Position - ClosestEnemy.PrimaryPart.Position)
	local Params = RaycastParams.new()
	Params.FilterType = Enum.RaycastFilterType.Blacklist
	Params.FilterDescendantsInstances = {workspace.ActivePlayers}
	local Angle = -135
	local BestDist = 0
	local BestPosition = nil
	local Raycasts = 10
	for i = 1, Raycasts do
		local RayDirection = (CFrame.lookAt(Model.PrimaryPart.Position, Model.PrimaryPart.Position + Difference) * CFrame.Angles(0, math.rad(Angle), 0)).LookVector * Dist
		local Raycast = workspace:Raycast(Model.PrimaryPart.Position, RayDirection, Params)

		if Raycast then 
			local NewDist = (Raycast.Position - ClosestEnemy.PrimaryPart.Position).Magnitude
			if NewDist > BestDist then 
				BestPosition = Raycast.Position
				BestDist = NewDist
			end
		end

		Angle += 270 / Raycasts
	end

	return BestPosition
end

Funcs.SideCast = function(ClosestEnemy)
	local Params = RaycastParams.new()
	Params.FilterType = Enum.RaycastFilterType.Blacklist
	Params.FilterDescendantsInstances = {workspace.ActivePlayers}
	local Angle = 0
	local Raycasts = 10
	for i = 1, Raycasts do 
		local RayDirection = (ClosestEnemy.PrimaryPart.CFrame * CFrame.Angles(0, math.rad(Angle), 0)).LookVector * 8
		local Raycast = workspace:Raycast(ClosestEnemy.PrimaryPart.Position, RayDirection, Params)
		Angle += 360 / Raycasts
		if Raycast then 
			return Raycast
		end
	end
	return nil
end

-- Functions from AI.FindBestAngle
Funcs.FindBestAngle = function(Model, Enemy, EnemyTool)
	local Params = RaycastParams.new()
	Params.FilterType = Enum.RaycastFilterType.Blacklist
	Params.FilterDescendantsInstances = {Model, workspace.Filter, EnemyTool, table.unpack(Enemy.Humanoid:GetAccessories()), workspace.Map}
	local Raycasts = {}
	table.insert(Raycasts, {workspace:Raycast(Model["Right Arm"].Position, Model.PrimaryPart.CFrame.LookVector * 10, Params), 0})
	table.insert(Raycasts, {workspace:Raycast(Model["Right Arm"].Position, (Model.PrimaryPart.CFrame * CFrame.Angles(0, -math.rad(1), 0)).LookVector.Unit * 10, Params), -1})
	table.insert(Raycasts, {workspace:Raycast(Model["Right Arm"].Position, (Model.PrimaryPart.CFrame * CFrame.Angles(0, math.rad(1), 0)).LookVector.Unit * 10, Params), 1})
	table.insert(Raycasts, {workspace:Raycast(Model["Right Arm"].Position, (Model.PrimaryPart.CFrame * CFrame.Angles(0, math.rad(2), 0)).LookVector.Unit * 10, Params), 2})
	table.insert(Raycasts, {workspace:Raycast(Model["Right Arm"].Position, (Model.PrimaryPart.CFrame * CFrame.Angles(0, -math.rad(2), 0)).LookVector.Unit * 10, Params), -2})
	workspace.Filter:ClearAllChildren()
	local ClosestDistance = math.huge
	local ClosestAngle = 0
	local ClosestPos = nil
	for i, v in pairs(Raycasts) do 
		local RaycastResult = v[1]
		local Angle = v[2]
		if RaycastResult then
			local Dist = Funcs.GetDist(Model, RaycastResult.Position)
			if Dist < ClosestDistance then
				ClosestDistance = Dist
				ClosestAngle = Angle
				ClosestPos = RaycastResult.Position
			end
		end
	end
	return ClosestAngle
end

-- Other functions defined in 'AI' script
local GetXDist = function(Pos1, Pos2)
	return math.sqrt((Pos1.X - Pos2.X) ^ 2 + (Pos1.Z - Pos2.Z) ^ 2)
end

local FairLookAt = function(Pos1, Pos2)
	return CFrame.lookAt(Pos1, Pos2 - Vector3.new(0, Pos2.Y - Pos1.Y, 0)) * CFrame.Angles(0, math.rad(17.5), 0)
end

local Grips = {
	Up = CFrame.new(0, 0, -1.70000005, 0, 0, 1, 1, 0, 0, 0, 1, 0),
	Out = CFrame.new(0, 0, -1.70000005, 0, 1, 0, 1, -0, 0, 0, 0, -1)
}

local LungingGui = game.Players.LocalPlayer.PlayerGui:WaitForChild("Game").Lunging

return function(Model)
	local Humanoid = Model.Humanoid
	local PrimaryPart = Model.PrimaryPart
	local Tool = Model:FindFirstChildOfClass("Tool")
	if not Tool then 
		repeat Tool = Model:FindFirstChildOfClass("Tool"); wait() until Tool
	end
	local Tick = 0
	local LastAttack = 0
	local LastOpponentAttack = 0
	local EnemyExtended = false
	while Model.PrimaryPart and Humanoid.Health > 0 and Model.Parent == workspace.ActivePlayers do  
		Tick += game:GetService("RunService").RenderStepped:Wait()
		local ClosestEnemy = Funcs.GetClosestEnemy(Model)

		if ClosestEnemy then 
			local ClosestPart = Funcs.GetClosestPart(Tool.Handle.Position, ClosestEnemy)
			local EnemyTool = ClosestEnemy:FindFirstChildOfClass("Tool")

			local LungeCooldown = false
			if EnemyTool then 
				if EnemyTool.Grip == Grips.Out then
					if EnemyExtended == false then 
						LastOpponentAttack = Tick
					end
					EnemyExtended = true
				end
			end
			if Tool.Grip ~= Grips.Out then
				LungeCooldown = true
			end

			local HigherHp = Model.Humanoid.Health > ClosestEnemy.Humanoid.Health
			local LowerHp = Model.Humanoid.Health < ClosestEnemy.Humanoid.Health
			local XDist = GetXDist(Model.PrimaryPart.Position, ClosestEnemy.PrimaryPart.Position)

			local Distance = Funcs.GetDistance(Model, ClosestEnemy)
			local HeightDifference = math.abs(ClosestEnemy.PrimaryPart.Position.Y - Model.PrimaryPart.Position.Y)
			local SameHeight = HeightDifference < 3

			local ClosestPos = ClosestEnemy.PrimaryPart.Position

			if EnemyExtended and LungeCooldown and SameHeight and Distance > 6 and EnemyTool then 
				Model:SetPrimaryPartCFrame(FairLookAt(PrimaryPart.Position, EnemyTool.Handle.Position + ClosestEnemy.PrimaryPart.CFrame.LookVector * EnemyTool.Handle.Size.Y * 2.2))
			else 
				if Tool.Parent ~= Model then
					Tool.Equip:FireServer()
				end
				Tool.Parent = Model

				Model:SetPrimaryPartCFrame(CFrame.lookAt(PrimaryPart.Position, (ClosestPart.Position - Vector3.new(0, ClosestPart.Position.Y - PrimaryPart.Position.Y, 0))) * CFrame.Angles(0, math.rad(17.5), 0))
				local Angle = 0
				local LastAngle = Angle
				-- Points sword towards closest position on part
				for i = 1, 10 do
					Angle = Angle + Funcs.FindBestAngle(Model, ClosestEnemy, EnemyTool)
					if LastAngle == Angle then 
						-- The wiggle to trigger touched
						if HigherHp then
							Angle += math.random(-10, 10)
						else 
							Angle += math.random(-2, 2)
						end
						Model:SetPrimaryPartCFrame(CFrame.lookAt(PrimaryPart.Position, (ClosestPart.Position - Vector3.new(0, ClosestPart.Position.Y - PrimaryPart.Position.Y, 0))) * CFrame.Angles(0, math.rad(17.5 + Angle), 0))
						break
					end
					LastAngle = Angle
					Model:SetPrimaryPartCFrame(CFrame.lookAt(PrimaryPart.Position, (ClosestPart.Position - Vector3.new(0, ClosestPart.Position.Y - PrimaryPart.Position.Y, 0))) * CFrame.Angles(0, math.rad(17.5 + Angle), 0))
				end
			end 

			local EnemyToModelVector = (Model.PrimaryPart.Position - ClosestEnemy.PrimaryPart.Position).Unit
			local MoveDirection = ClosestEnemy.Humanoid.MoveDirection
			local EnemyIsMovingTowardsModel = (EnemyToModelVector - MoveDirection).Magnitude < 1
			-- Goes really aggressive when enemy is on cooldown
			local IdealDistance = EnemyExtended and 8 or 7
			-- Backs off
			IdealDistance += EnemyIsMovingTowardsModel and 2 or 0

			-- Detects when last lunged
			if Tool.Enabled and Tool.Grip == Grips.Up then
				LastAttack = Tick
			end

			-- Pushes when enemy is about to run out of lunge
			if EnemyTool and not EnemyTool.Enabled and (Tick - LastOpponentAttack > 0.1) and (Tick - LastOpponentAttack > 0.2) and  (Tick - LastAttack > 0.05) then 
				IdealDistance -= 2
			end

			LungingGui.BackgroundColor3 = Color3.new(1, 1, 1)
			-- Backs off when about to stop lunging
			if (Tick - LastAttack > 0.5) then
				LungingGui.BackgroundColor3 = Color3.new(0.0470588, 1, 0) 
				IdealDistance += 2
			end

			if XDist < 15 then
				Tool.Use:FireServer()
			end

			if Distance > 8 then
				if LowerHp then 
					IdealDistance += 10
				end
			end

			local Offset = -ClosestEnemy.PrimaryPart.CFrame.RightVector
			local SideCast = Funcs.SideCast(ClosestEnemy)

			if HigherHp and SameHeight then 
				Offset *= 3
				IdealDistance = 2
			end

			if SideCast or Tool.Grip == Grips.Up then 
				Offset = Vector3.new(0, 0, 0)
			end

			-- Favors the side without the sword
			local SpacedPosition = (ClosestPos - (ClosestPos - PrimaryPart.Position).Unit * IdealDistance) + Offset
			if HigherHp then 
				Offset *= 3
			end

			if Distance < IdealDistance then 
				local RetreatPos = Funcs.GetRetreatDirection(Model, ClosestEnemy, 10)
				if RetreatPos and EnemyIsMovingTowardsModel then 
					RetreatPos = Funcs.GetRetreatDirection(Model, ClosestEnemy, 100)
					Humanoid:MoveTo(RetreatPos)
				else
					Humanoid:MoveTo(SpacedPosition)
				end
			else 
				Humanoid:MoveTo(SpacedPosition)
			end
		end
	end
end