local a=game:GetService("\087\111\114\107\115\112\097\099\101")local C=game:GetService("\082\101\112\108\105\099\097\116\101\100\083\116\111\114\097\103\101")local P=require(C:WaitForChild("\078\101\118\101\114\109\111\114\101",2))local B=(P("\083\116\100")).rquery local Z={}function Z.Raycast(C,P,B)P=P or{}local Z={}while true do Z={a:FindPartOnRayWithIgnoreList(C,P)}local L=Z[1]if not L then break else local a=B and B:CanCollideWith(L)or L.CanCollide if a then break else table.insert(P,L)end end end return unpack(Z)end function Z.GetIsInLineOfSight(a,C,P,B)if typeof(a)=="\073\110\115\116\097\110\099\101"then a=a.Position end local L,y=Z.Raycast(Ray.new(a,(a-C.HumanoidRootPart.Position).Unit*-P),B)return L and L:IsDescendantOf(C),y end function Z.IsSpaceEmpty(C)local P=Region3.new(C-Vector3.new(2,2,2),C+Vector3.new(2,2,2))return a:IsRegion3Empty(P)end function Z.GetRandomXZOffsetNear(a)local C=math.random(5,10)if math.random()>.5 then C=C*-1 end local P=math.random(5,10)if math.random()>.5 then P=P*-1 end local B=Vector3.new(a.X+C,a.Y,a.Z+P)return B end function Z.FindEmptySpaceCloseTo(a)if a==nil or not a:IsA("\086\101\099\116\111\114\051")then error("\077\105\115\115\105\110\103 \116\097\114\103\101\116 \118\101\099\116\111\114")return nil end local C=Vector3.new(0,0,0)local P=0 math.randomseed(os.time())repeat C=Z.GetRandomXZOffsetNear(a)if Z.IsSpaceEmpty(C)then return C else C=C+Vector3.new(0,4,0)end if Z.IsSpaceEmpty(C)then return C end P=P+1 until P>10 return nil end function Z.WideRayCast(C,P,B,Z)local L={}local y=B*((P-C):Cross(Vector3.FromNormalId(Enum.NormalId.Top))).unit local G=Ray.new(C+y,(P-C)+y)local R,n=a:FindPartOnRayWithIgnoreList(G,Z)if R then table.insert(L,R)end return L end function Z.FindNearestPathPoint(a,C,P,B,L)local y=a:CheckOcclusionAsync(C)if y>0 then Z.WideRayCast(P)end end function Z.ShootAzimuth(a,C,P)local B=P-a local Z=B*Vector3.new(1,0,1)local L=math.acos(Z:Dot(C)/Z.magnitude)return{ToTargetOffsets=B;AzimuthDegrees=math.deg(L)}end function Z.CanHunterSeeTarget(C,P,B,L)local y=Z.ShootAzimuth(C.Position,C.CFrame.lookVector,B.Position)if y.AzimuthDegrees<P then local P=Ray.new(C.Position,y.ToTargetOffsets)local Z,G=a:FindPartOnRayWithIgnoreList(P,L)if Z and Z.Parent==B then return true end end return false end function Z.GetClosestVisibleTarget(a,C,P,L)local y=nil local G=math.huge local R=B.PersonageTorsoOrEquivalent(a)for a,C in pairs(C)do local n=B.PersonageTorsoOrEquivalent(C)if Z.CanHunterSeeTarget(R,L,n,P)then local a=R.Position-n.Position if a.magnitude<G then y=n G=a.magnitude end end end return y end return Z
local Workspace = game:GetService("Workspace")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local require = require(ReplicatedStorage:WaitForChild("Nevermore", 2))

local rq = require("Std").rquery

local module = {}

function module.Raycast(ray, blacklist, partToCheck)
	blacklist = blacklist or {}
	local results = {}

	while true do
		results = {Workspace:FindPartOnRayWithIgnoreList(ray, blacklist)}

		local hit = results[1]

		if not hit then
			break
		else
			local canCollideWith = partToCheck and partToCheck:CanCollideWith(hit) or hit.CanCollide

			if canCollideWith then
				break
			else
				table.insert(blacklist, hit)
			end
		end
	end

	return unpack(results)
end

--Checks if given a range, if origin has line of sight with character.
function module.GetIsInLineOfSight(origin, character, range, blacklist)
	if typeof(origin) == "Instance" then
		origin = origin.Position
	end
	local hit, point = module.Raycast(Ray.new(origin, (origin - character.HumanoidRootPart.Position).Unit * -range), blacklist)

	return hit and hit:IsDescendantOf(character), point
end

function module.IsSpaceEmpty(position)
	local region = Region3.new(position - Vector3.new(2,2,2), position + Vector3.new(2,2,2))
	return Workspace:IsRegion3Empty(region)
end

function module.GetRandomXZOffsetNear(targetVector3)
	local xOffset = math.random(5,10)
	if math.random() > .5 then
		xOffset = xOffset * -1
	end
	local zOffset = math.random(5, 10)
	if math.random() > .5 then
		zOffset = zOffset * -1
	end

	local targetPos = Vector3.new(targetVector3.X + xOffset,
		targetVector3.Y,
		targetVector3.Z + zOffset)

	return targetPos
end

function module.FindEmptySpaceCloseTo(targetVector3)

	if targetVector3 == nil or not targetVector3:IsA("Vector3") then
		error("Missing target vector")
		return nil
	end

	local targetPos = Vector3.new(0,0,0)
	local count = 0
	math.randomseed(os.time())
	repeat

		targetPos = module.GetRandomXZOffsetNear(targetVector3)
		if module.IsSpaceEmpty(targetPos) then
			return targetPos
		else
			targetPos = targetPos + Vector3.new(0,4,0)
		end

		if module.IsSpaceEmpty(targetPos) then
			return targetPos
		end
		count = count + 1
	until count > 10
	return nil
end

function module.WideRayCast(start, target, offset, ignoreList)
	local parts = {}

	local offsetVector = offset *
		(target - start):Cross(Vector3.FromNormalId(
			Enum.NormalId.Top)
		).unit
	local ray = Ray.new(start + offsetVector,
		target - start + offsetVector)
	local part, point = Workspace:FindPartOnRayWithIgnoreList(ray, ignoreList)

	if part then
		table.insert(parts, part)
	end

	return parts
end

function module.FindNearestPathPoint(path, point, start, target, ignoreList)
	local occludePoint = path:CheckOcclusionAsync(point)
	if occludePoint > 0 then
		module.WideRayCast(start)
	end
end

function module.ShootAzimuth(fromPosition, fromLookVector, toPosition)
	local toTarget = toPosition - fromPosition
	local toTargetWedge = toTarget * Vector3.new(1,0,1)

	local angleRads = math.acos(toTargetWedge:Dot(fromLookVector)/toTargetWedge.magnitude)
	return {
		ToTargetOffsets = toTarget,
		AzimuthDegrees = math.deg(angleRads)
	}
end

function module.CanHunterSeeTarget(hunterTorso, hunterFieldOfViewDegrees, targetTorso, ignoreList)
	local azimuthToTarget = module.ShootAzimuth(hunterTorso.Position, 
		hunterTorso.CFrame.lookVector, 
		targetTorso.Position)

	if azimuthToTarget.AzimuthDegrees < hunterFieldOfViewDegrees then
		local targetRay = Ray.new(hunterTorso.Position, azimuthToTarget.ToTargetOffsets)
		local part, position = Workspace:FindPartOnRayWithIgnoreList(targetRay, ignoreList)
		if part and part.Parent == targetTorso then
			return true
		end
	end
	return false
end

function module.GetClosestVisibleTarget(hunterPersonage, candidateTargets, ignoreList, fieldOfView)
	local closestTarget = nil
	local closestDistance = math.huge
	local hunterTorso = rq.PersonageTorsoOrEquivalent(hunterPersonage)
	for _, candidateTarget in pairs(candidateTargets) do
		local targetTorso = rq.PersonageTorsoOrEquivalent(candidateTarget)

		if module.CanHunterSeeTarget(hunterTorso, fieldOfView, targetTorso, ignoreList) then
			local toTargetOffsets = hunterTorso.Position - targetTorso.Position
			if toTargetOffsets.magnitude < closestDistance then
				closestTarget = targetTorso
				closestDistance = toTargetOffsets.magnitude
			end
		end

	end
	return closestTarget
end
return module