local function v(v)local X=(Create("\080\097\114\116"))({Name="\080\097\116\104\071\108\111\098\101";TopSurface="\083\109\111\111\116\104",BottomSurface="\083\109\111\111\116\104",Shape="\066\097\108\108";CanCollide=false;Size=Vector3.new(2,2,2),BrickColor=BrickColor.new("\073\110\115\116\105\116\117\116\105\111\110\097\108 \119\104\105\116\101");Transparency=0,Anchored=true;CFrame=CFrame.new(v)})return X end local function X(X,C)local p={}p.Cancelled=false p.Started=false p.Finished=Signal.Create()p.PathFailed=Signal.Create()p.PathStarted=Signal.Create()p.PathComputed=false function p.YieldUntilPointReached(h,v,X,C)C=C or 10000000 local F=findPlayerHumanoid(Player)local D=F and F.Torso local k=tick()local O=k while D and(tick()-k<C and p.Cancelled==false)do local v=X-D.CFrame.p local C=(v*Vector3.new(1,0,1)).magnitude if C<6 then if v.Y>=2.2 then F.Jump=true end end if C<2 then return true end if tick()-O>1.5 then F:MoveTo(X)O=tick()end CharacterControl:UpdateTorso(X)wait()end return false end function p.Cancel(v)p.Cancelled=true local X=findPlayerHumanoid(Player)local C=X and X.Torso if X and C then X:MoveTo(C.CFrame.p)end end function p.CheckOcclusion(h,v,X,C,p)local F=findPlayerHumanoid(Player)local D=F and F.Torso if p==nil then p=D and Vector3.new(D.Size.X/2,0,D.Size.Z/2)or Vector3.new(1,0,1)end local k=X-v local O=k.unit local d=(Vector3.new(0,1,0)):Cross(O)*p local l,g=Utility.Raycast(Ray.new(v+d,k+d),true,{C})local N,K=Utility.Raycast(Ray.new(v,k),true,{C})local Q,Z=Utility.Raycast(Ray.new(v-d,k-d),true,{C})if l or N or Q then return false end local L=(X+v)/2 local J=2 for X=1,math.floor(k.magnitude/J),1 do local p,h=Utility.Raycast(Ray.new(v+(O*X)*J,Vector3.new(0,-7,0)),true,{C})if not p then return false end end return true end function p.SmoothPoints(C,v)local h={}local F=findPlayerHumanoid(Player)local D=F and F.Torso for X=1,#v,1 do table.insert(h,v[X])end for v=#h-1,1,-1 do if v+1<=#h then local C=h[v+1]local F=h[v]local k=h[v-1]if k==nil then k=D and Vector3.new(D.CFrame.p.X,F.Y,D.CFrame.p.Z)end if k and(Utility.FuzzyEquals(F.Y,k.Y)and Utility.FuzzyEquals(F.Y,C.Y))then if p:CheckOcclusion(k,C,X)then table.remove(h,v)v=v+1 end end end end return h end function p.CheckNeighboringCells(X,v)local C={}local h=findPlayerHumanoid(Player)local F=v and(h and h.Torso)if F then local X=F.CFrame local h=X.p local D=Vector3.new(Utility.Round(h.X-2,4)+2,Utility.Round(h.Y-2,4)+2,Utility.Round(h.Z-2,4)+2)local k={}for v=-4,4,8 do for X=-4,4,8 do table.insert(k,D+Vector3.new(v,0,X))end end for X,h in pairs(k)do local F=p:CheckOcclusion(D,h,v,Vector3.new(0,0,0))if F then table.insert(C,h)end end end return C end function p.ComputeDirectPath(v)local h=findPlayerHumanoid(Player)local F=h and h.Torso if F then local v=F.CFrame.p local h=C if(h-v).magnitude<150 then h=h-(h-v).unit*2 if p:CheckOcclusion(v,h,X,Vector3.new(0,0,0))then local v={}v.Status=Enum.PathStatus.Success function v.GetPointCoordinates(v)return{h}end return v end end end end local function h(v,X,C)return math.abs(v.X-X.X)<=C and(math.abs(v.Y-X.Y)<=C and math.abs(v.Z-X.Z)<=C)end function p.ComputePath(v)local F=false local D=findPlayerHumanoid(Player)local k=D and D.Torso if k then if p.PathComputed then return end p.PathComputed=true local v=pcall(function()p.pathResult=PathfindingService:ComputeSmoothPathAsync(k.CFrame.p-Vector3.new(0,3,0),C,400)F=true end)if not v then p.pathResult=PathfindingService:ComputeRawPathAsync(k.CFrame.p-Vector3.new(0,3,0),C,400)F=false end p.pointList=p.pathResult and p.pathResult:GetPointCoordinates()local D=false if p.pathResult.Status==Enum.PathStatus.FailFinishNotEmpty then local v=C-workspace.CurrentCamera.CoordinateFrame.p if v.magnitude>2 then local X=C-v.unit*2.1 local h=pcall(function()p.pathResult=PathfindingService:ComputeSmoothPathAsync(k.CFrame.p,X,400)F=true end)if not h then p.pathResult=PathfindingService:ComputeRawPathAsync(k.CFrame.p,X,400)F=false end p.pointList=p.pathResult and p.pathResult:GetPointCoordinates()D=true end end if p.pathResult.Status==Enum.PathStatus.ClosestNoPath and(#p.pointList>=1 and D==false)then local v=p.pointList[#p.pointList]if h(C,v,4)and(k.CFrame.p-C).magnitude>(v-C).magnitude then local v={}v.Status=Enum.PathStatus.Success function v.GetPointCoordinates(v)return{p.pointList}end p.pathResult=v D=true end end if(p.pathResult.Status==Enum.PathStatus.FailStartNotEmpty or p.pathResult.Status==Enum.PathStatus.ClosestNoPath)and D==false then local v=p:CheckNeighboringCells(X)for v,X in pairs(v)do local h local D=pcall(function()h=PathfindingService:ComputeSmoothPathAsync(X,C,400)F=true end)if not D then h=PathfindingService:ComputeRawPathAsync(X,C,400)F=false end if h and h.Status==Enum.PathStatus.Success then p.pathResult=h if p.pathResult then p.pointList=p.pathResult:GetPointCoordinates()table.insert(p.pointList,1,X)end break end end end if DirectPathEnabled then if p.pathResult.Status~=Enum.PathStatus.Success then local v=p:ComputeDirectPath()if v and v.Status==Enum.PathStatus.Success then p.pathResult=v p.pointList=v:GetPointCoordinates()end end end end return F end function p.IsValidPath(v)p:ComputePath()local X=p.pathResult.Status return X==Enum.PathStatus.Success end function p.GetPathStatus(v)p:ComputePath()return p.pathResult.Status end function p.Start(C)spawn(function()local C=findPlayerHumanoid(Player)local h=C and C.Torso if h then if p.Started then return end p.Started=true local F=p:ComputePath()if p:IsValidPath()then p.PathStarted:fire()local D=F and p.pointList or p:SmoothPoints(p.pointList)for F,k in pairs(D)do if C then if p.Cancelled then return end local O=nil if SHOW_PATH then O=v(k)O.BrickColor=BrickColor.new("\078\101\119 \089\101\108\108\101\114")O.Parent=workspace print(O.CFrame.p)end C:MoveTo(k)local d=((h.CFrame.p-k)*Vector3.new(1,0,1)).magnitude local l=10 if math.abs(C.WalkSpeed)>0 then l=d/math.abs(C.WalkSpeed)end local g=true if F==1 then if CameraModule then local v=CameraModule:LookAtPreserveHeight(D[#D])local X,C=CameraModule:TweenCameraLook(v)end end if(C.Torso.CFrame.p-k).magnitude>9 then spawn(function()while g and p.Cancelled==false do if CameraModule then local v=CameraModule:GetCameraLook()local X=(v*Vector3.new(1,0,1)).unit local C=((k-workspace.CurrentCamera.CoordinateFrame.p)*Vector3.new(1,0,1)).unit local p=math.deg(math.acos(X:Dot(C)))if tick()-Utility.GetLastInput()>2 and p>workspace.CurrentCamera.FieldOfView/2 then local v=CameraModule:LookAtPreserveHeight(k)local X,C=CameraModule:TweenCameraLook(v)end end wait(.1)end end)end local N=p:YieldUntilPointReached(X,k,l*3+1)g=false if SHOW_PATH then O:Destroy()end if not N then p.PathFailed:fire()return end end end p.Finished:fire()return end end p.PathFailed:fire()end)end return p end


local function CreateDestinationIndicator(pos)
	local destinationGlobe = Create'Part'
	{
		Name = 'PathGlobe';
		TopSurface = 'Smooth';
		BottomSurface = 'Smooth';
		Shape = 'Ball';
		CanCollide = false;
		Size = Vector3.new(2,2,2);
		BrickColor = BrickColor.new('Institutional white');
		Transparency = 0;
		Anchored = true;
		CFrame = CFrame.new(pos);
	}
	return destinationGlobe
end

local function Pather(character, point)
	local this = {}

	this.Cancelled = false
	this.Started = false

	this.Finished = Signal.Create()
	this.PathFailed = Signal.Create()
	this.PathStarted = Signal.Create()

	this.PathComputed = false

	function this:YieldUntilPointReached(character, point, timeout)
		timeout = timeout or 10000000

		local humanoid = findPlayerHumanoid(Player)
		local torso = humanoid and humanoid.Torso
		local start = tick()
		local lastMoveTo = start
		while torso and tick() - start < timeout and this.Cancelled == false do
			local diffVector = (point - torso.CFrame.p)
			local xzMagnitude = (diffVector * Vector3.new(1,0,1)).magnitude
			if xzMagnitude < 6 then
				-- Jump if the path is telling is to go upwards
				if diffVector.Y >= 2.2 then
					humanoid.Jump = true
				end
			end
			-- The hard-coded number 2 here is from the engine's MoveTo implementation
			if xzMagnitude < 2 then
				return true
			end
			-- Keep on issuing the move command because it will automatically quit every so often.
			if tick() - lastMoveTo > 1.5 then
				humanoid:MoveTo(point)
				lastMoveTo = tick()
			end
			CharacterControl:UpdateTorso(point)
			wait()
		end
		return false
	end

	function this:Cancel()
		this.Cancelled = true
		local humanoid = findPlayerHumanoid(Player)
		local torso = humanoid and humanoid.Torso
		if humanoid and torso then
			humanoid:MoveTo(torso.CFrame.p)
		end
	end

	function this:CheckOcclusion(point1, point2, character, torsoRadius)
		local humanoid = findPlayerHumanoid(Player)
		local torso = humanoid and humanoid.Torso
		if torsoRadius == nil then
			torsoRadius = torso and Vector3.new(torso.Size.X/2,0,torso.Size.Z/2) or Vector3.new(1,0,1)
		end

		local diffVector = point2 - point1
		local directionVector = diffVector.unit

		local rightVector = Vector3.new(0,1,0):Cross(directionVector) * torsoRadius

		local rightPart, _ = Utility.Raycast(Ray.new(point1 + rightVector, diffVector + rightVector), true, {character})
		local hitPart, _ = Utility.Raycast(Ray.new(point1, diffVector), true, {character})
		local leftPart, _ = Utility.Raycast(Ray.new(point1 - rightVector, diffVector - rightVector), true, {character})

		if rightPart or hitPart or leftPart then
			return false
		end

		-- Make sure we have somewhere to stand on
		local midPt = (point2 + point1) / 2
		local studsBetweenSamples = 2
		for i = 1, math.floor(diffVector.magnitude/studsBetweenSamples) do
			local downPart, _ = Utility.Raycast(Ray.new(point1 + directionVector * i * studsBetweenSamples, Vector3.new(0,-7,0)), true, {character})
			if not downPart then
				return false
			end
		end

		return true
	end

	function this:SmoothPoints(pathToSmooth)
		local result = {}

		local humanoid = findPlayerHumanoid(Player)
		local torso = humanoid and humanoid.Torso
		for i = 1, #pathToSmooth do
			table.insert(result, pathToSmooth[i])
		end

		-- Backwards for safe-deletion
		for i = #result - 1, 1, -1 do
			if i + 1 <= #result then

				local nextPoint = result[i+1]
				local thisPoint = result[i]

				local lastPoint = result[i-1]
				if lastPoint == nil then
					lastPoint = torso and Vector3.new(torso.CFrame.p.X, thisPoint.Y, torso.CFrame.p.Z)
				end

				if lastPoint and Utility.FuzzyEquals(thisPoint.Y, lastPoint.Y) and Utility.FuzzyEquals(thisPoint.Y, nextPoint.Y) then
					if this:CheckOcclusion(lastPoint, nextPoint, character) then
						table.remove(result, i)
						-- Move i back one to recursively-smooth
						i = i + 1
					end
				end
			end
		end

		return result
	end

	function this:CheckNeighboringCells(character)
		local pathablePoints = {}
		local humanoid = findPlayerHumanoid(Player)
		local torso = character and humanoid and humanoid.Torso
		if torso then
			local torsoCFrame = torso.CFrame
			local torsoPos = torsoCFrame.p
			-- Minus and plus 2 is so we can get it into the cell-corner space and then translate it back into cell-center space
			local roundedPos = Vector3.new(Utility.Round(torsoPos.X-2,4)+2, Utility.Round(torsoPos.Y-2,4)+2, Utility.Round(torsoPos.Z-2,4)+2)
			local neighboringCells = {}
			for x = -4, 4, 8 do
				for z = -4, 4, 8 do
					table.insert(neighboringCells, roundedPos + Vector3.new(x,0,z))
				end
			end
			for _, testPoint in pairs(neighboringCells) do
				local pathable = this:CheckOcclusion(roundedPos, testPoint, character, Vector3.new(0,0,0))
				if pathable then
					table.insert(pathablePoints, testPoint)
				end
			end
		end
		return pathablePoints
	end

	function this:ComputeDirectPath()
		local humanoid = findPlayerHumanoid(Player)
		local torso = humanoid and humanoid.Torso
		if torso then
			local startPt = torso.CFrame.p
			local finishPt = point
			if (finishPt - startPt).magnitude < 150 then
				-- move back the destination by 2 studs or otherwise the pather will collide with the object we are trying to reach
				finishPt = finishPt - (finishPt - startPt).unit * 2
				if this:CheckOcclusion(startPt, finishPt, character, Vector3.new(0,0,0)) then
					local pathResult = {}
					pathResult.Status = Enum.PathStatus.Success
					function pathResult:GetPointCoordinates()
						return {finishPt}
					end
					return pathResult
				end
			end
		end
	end

	local function AllAxisInThreshhold(targetPt, otherPt, threshold)
		return math.abs(targetPt.X - otherPt.X) <= threshold and
			math.abs(targetPt.Y - otherPt.Y) <= threshold and
			math.abs(targetPt.Z - otherPt.Z) <= threshold
	end

	function this:ComputePath()
		local smoothed = false
		local humanoid = findPlayerHumanoid(Player)
		local torso = humanoid and humanoid.Torso
		if torso then
			if this.PathComputed then return end
			this.PathComputed = true
			-- Will yield the script since it is an Async script (start, finish, maxDistance)
			-- Try to use the smooth function, but it may not exist yet :(
			local success = pcall(function()
				-- 3 is height from torso cframe to ground
				this.pathResult = PathfindingService:ComputeSmoothPathAsync(torso.CFrame.p - Vector3.new(0,3,0), point, 400)
				smoothed = true
			end)
			if not success then
				-- 3 is height from torso cframe to ground
				this.pathResult = PathfindingService:ComputeRawPathAsync(torso.CFrame.p - Vector3.new(0,3,0), point, 400)
				smoothed = false
			end
			this.pointList = this.pathResult and this.pathResult:GetPointCoordinates()
			local pathFound = false
			if this.pathResult.Status == Enum.PathStatus.FailFinishNotEmpty then
				-- Lets try again with a slightly set back start point; it is ok to do this again so the FailFinishNotEmpty uses little computation
				local diffVector = point - workspace.CurrentCamera.CoordinateFrame.p
				if diffVector.magnitude > 2 then
					local setBackPoint = point - (diffVector).unit * 2.1
					local success = pcall(function()
						this.pathResult = PathfindingService:ComputeSmoothPathAsync(torso.CFrame.p, setBackPoint, 400)
						smoothed = true
					end)
					if not success then
						this.pathResult = PathfindingService:ComputeRawPathAsync(torso.CFrame.p, setBackPoint, 400)
						smoothed = false
					end
					this.pointList = this.pathResult and this.pathResult:GetPointCoordinates()
					pathFound = true
				end
			end
			if this.pathResult.Status == Enum.PathStatus.ClosestNoPath and #this.pointList >= 1 and pathFound == false then
				local otherPt = this.pointList[#this.pointList]
				if AllAxisInThreshhold(point, otherPt, 4) and (torso.CFrame.p - point).magnitude > (otherPt - point).magnitude then
					local pathResult = {}
					pathResult.Status = Enum.PathStatus.Success
					function pathResult:GetPointCoordinates()
						return {this.pointList}
					end
					this.pathResult = pathResult
					pathFound = true
				end
			end
			if (this.pathResult.Status == Enum.PathStatus.FailStartNotEmpty or this.pathResult.Status == Enum.PathStatus.ClosestNoPath) and pathFound == false then
				local pathablePoints = this:CheckNeighboringCells(character)
				for _, otherStart in pairs(pathablePoints) do
					local pathResult;
					local success = pcall(function()
						pathResult = PathfindingService:ComputeSmoothPathAsync(otherStart, point, 400)
						smoothed = true
					end)
					if not success then
						pathResult = PathfindingService:ComputeRawPathAsync(otherStart, point, 400)
						smoothed = false
					end
					if pathResult and pathResult.Status == Enum.PathStatus.Success then
						this.pathResult = pathResult
						if this.pathResult then
							this.pointList = this.pathResult:GetPointCoordinates()
							table.insert(this.pointList, 1, otherStart)
						end
						break
					end
				end
			end
			if DirectPathEnabled then
				if this.pathResult.Status ~= Enum.PathStatus.Success then
					local directPathResult = this:ComputeDirectPath()
					if directPathResult and directPathResult.Status == Enum.PathStatus.Success then
						this.pathResult = directPathResult
						this.pointList = directPathResult:GetPointCoordinates()
					end
				end
			end
		end
		return smoothed
	end

	function this:IsValidPath()
		this:ComputePath()
		local pathStatus = this.pathResult.Status
		return pathStatus == Enum.PathStatus.Success
	end

	function this:GetPathStatus()
		this:ComputePath()
		return this.pathResult.Status
	end

	function this:Start()
		spawn(function()
			local humanoid = findPlayerHumanoid(Player)
			--humanoid.AutoRotate = false
			local torso = humanoid and humanoid.Torso
			if torso then
				if this.Started then return end
				this.Started = true
				-- Will yield the script since it is an Async function script (start, finish, maxDistance)
				local smoothed = this:ComputePath()
				if this:IsValidPath() then
					this.PathStarted:fire()
					-- smooth out zig-zaggy paths
					local smoothPath = smoothed and this.pointList or this:SmoothPoints(this.pointList)
					for i, point in pairs(smoothPath) do
						if humanoid then
							if this.Cancelled then
								return
							end

							local wayPoint = nil
							if SHOW_PATH then
								wayPoint = CreateDestinationIndicator(point)
								wayPoint.BrickColor = BrickColor.new("New Yeller")
								wayPoint.Parent = workspace
								print(wayPoint.CFrame.p)
							end

							humanoid:MoveTo(point)

							local distance = ((torso.CFrame.p - point) * Vector3.new(1,0,1)).magnitude
							local approxTime = 10
							if math.abs(humanoid.WalkSpeed) > 0 then
								approxTime = distance / math.abs(humanoid.WalkSpeed)
							end

							local yielding = true

							if i == 1 then
								--local rotatedCFrame = CameraModule:LookAtPreserveHeight(point)
								if CameraModule then
									local rotatedCFrame = CameraModule:LookAtPreserveHeight(smoothPath[#smoothPath])
									local finishedSignal, duration = CameraModule:TweenCameraLook(rotatedCFrame)
								end
								--CharacterControl:SetTorsoLookPoint(point)
							end
							---[[
							if (humanoid.Torso.CFrame.p - point).magnitude > 9 then
								spawn(function()
									while yielding and this.Cancelled == false do
										if CameraModule then
											local look = CameraModule:GetCameraLook()
											local squashedLook = (look * Vector3.new(1,0,1)).unit
											local direction = ((point - workspace.CurrentCamera.CoordinateFrame.p) * Vector3.new(1,0,1)).unit

											local theta = math.deg(math.acos(squashedLook:Dot(direction)))

											if tick() - Utility.GetLastInput() > 2 and theta > (workspace.CurrentCamera.FieldOfView / 2) then
												local rotatedCFrame = CameraModule:LookAtPreserveHeight(point)
												local finishedSignal, duration = CameraModule:TweenCameraLook(rotatedCFrame)
												--return
											end
										end
										wait(0.1)
									end
								end)
							end
							--]]
							local didReach = this:YieldUntilPointReached(character, point, approxTime * 3 + 1)

							yielding = false

							if SHOW_PATH then
								wayPoint:Destroy()
							end

							if not didReach then
								this.PathFailed:fire()
								return
							end
						end
					end

					this.Finished:fire()
					return
				end
			end
			this.PathFailed:fire()
		end)
	end

	return this
end
