local D={Vector3.new(1,0,0);Vector3.new(.707,0,.707),Vector3.new(0,0,1);Vector3.new(-0.707,0,.707),Vector3.new(-1,0,0),Vector3.new(-0.707,0,-0.707);Vector3.new(0,0,-1);Vector3.new(.707,0,-0.707)}local n=4 local X=12 local b=20 local J=5 local function r(r,G,e)assert(typeof(r)=="\073\110\115\116\097\110\099\101"and r:IsA("\080\108\097\121\101\114"),"\077\117\115\116 \102\101\101\100 \097 \080\108\097\121\101\114 \116\111 \102\105\110\100 \112\111\115\105\116\105\111\110 \097\116")assert(G==nil or typeof(G)=="\086\101\099\116\111\114\051","\077\117\115\116 \102\101\101\100 \116\104\101 \112\114\101\118\105\111\117\115 \112\111\115\105\116\105\111\110 \116\111 \102\105\110\100 \099\108\111\115\101\115\116 \109\097\116\099\104 \116\111")assert(e==nil or typeof(e)=="\073\110\115\116\097\110\099\101","\077\117\115\116 \102\101\101\100 \097\110 \073\110\115\116\097\110\099\101 \097\115 \105\103\110\111\114\101 \105\110\115\116\097\110\099\101 \105\102 \103\105\118\101\110")if not r.Character then return end local y=r.Character:FindFirstChild("\072\117\109\097\110\111\105\100\082\111\111\116\080\097\114\116")local N=r.Character:FindFirstChildOfClass("\072\117\109\097\110\111\105\100")if not(y and N)then return end local W=y.Position-Vector3.new(0,N.HipHeight,0)local F={}for X=1,#D,1 do table.insert(F,W+D[X]*n)end if G then local D=Vector3.new(G.X,W.Y,G.Z)table.sort(F,function(n,X)return(n-D).Magnitude<(X-D).magnitude end)end for D=1,#F,1 do local n=workspace:FindPartOnRayWithIgnoreList(Ray.new(W,F[D]-W),{r.Character,e})if not n then local n=Ray.new(F[D]+Vector3.new(0,X,0),Vector3.new(0,-b,0))local r,G,y,N=workspace:FindPartOnRay(n,e)if r and math.abs(G.Y-W.Y)<=J then return G end end end end return r
--[[

	The OpenAreaChecker finds an open area around a given position.
	It checks in a grid around the supplied position.
	For any positive matches (open areas) it will perform a downward scan to ensure the open area isn't a gap.
	
--]]

local scanGrid = {
	Vector3.new(1, 0, 0),
	Vector3.new(0.707, 0, 0.707),
	Vector3.new(0, 0, 1),
	Vector3.new(-0.707, 0, 0.707),
	Vector3.new(-1, 0, 0),
	Vector3.new(-0.707, 0, -0.707),
	Vector3.new(0, 0, -1),
	Vector3.new(0.707, 0, -0.707),
}

local scanRadius = 4
local scanHeight = 12
local scanDepth = 20
local scanMaxDiff = 5

local function findPosition(owner, previousPosition, ignoreInstance)
	assert(typeof(owner) == "Instance" and owner:IsA("Player"), "Must feed a Player to find position at")
	assert(
		previousPosition == nil or typeof(previousPosition) == "Vector3",
		"Must feed the previous position to find closest match to"
	)
	assert(
		ignoreInstance == nil or typeof(ignoreInstance) == "Instance",
		"Must feed an Instance as ignore instance if given"
	)

	if not owner.Character then
		return
	end
	local root = owner.Character:FindFirstChild("HumanoidRootPart")
	local hum = owner.Character:FindFirstChildOfClass("Humanoid")
	if not (root and hum) then
		return
	end

	local origin = root.Position - Vector3.new(0, hum.HipHeight, 0)

	local finalGrid = {}
	for i = 1, #scanGrid do
		table.insert(finalGrid, origin + scanGrid[i] * scanRadius)
	end
	if previousPosition then
		local offset = Vector3.new(previousPosition.X, origin.Y, previousPosition.Z)
		table.sort(finalGrid, function(a, b)
			return (a - offset).Magnitude < (b - offset).magnitude
		end)
	end

	for i = 1, #finalGrid do
		local blocked = workspace:FindPartOnRayWithIgnoreList(
			Ray.new(origin, finalGrid[i] - origin),
			{ owner.Character, ignoreInstance }
		)

		if not blocked then
			local ray = Ray.new(finalGrid[i] + Vector3.new(0, scanHeight, 0), Vector3.new(0, -scanDepth, 0))

			local part, position, normal, material = workspace:FindPartOnRay(ray, ignoreInstance)

			if part and math.abs(position.Y - origin.Y) <= scanMaxDiff then
				return position
			end
		end
	end
end

return findPosition
