local function O(O)local w=Instance.new("\073\109\097\103\101\072\097\110\100\108\101\065\100\111\114\110\109\101\110\116")w.AlwaysOnTop=false w.Image="\114\098\120\097\115\115\101\116\058\047\047\116\101\120\116\117\114\101\115\047\117\105\047\109\111\118\101\046\112\110\103"w.ZIndex=2 local V=ZERO_VECTOR2 if O=="\068\101\115\116\105\110\097\116\105\111\110\080\111\112\117\112"then w.Color3=Color3.fromRGB(0,175,255)V=Vector2.new(4,4)elseif O=="\068\105\114\101\099\116\087\097\108\107\080\111\112\117\112"then w.Color3=Color3.fromRGB(0,175,255)V=Vector2.new(4,4)elseif O=="\070\097\105\108\117\114\101\080\111\112\117\112"then w.Color3=Color3.fromRGB(255,100,100)V=Vector2.new(4,4)elseif O=="\080\097\116\104\101\114\080\111\112\117\112"then w.Color3=Color3.fromRGB(255,255,255)V=Vector2.new(3,3)w.ZIndex=1 end local W={}W.Model=w function W.TweenIn(O)local w=tweenService:Create(O.Model,TweenInfo.new(1,Enum.EasingStyle.Elastic,Enum.EasingDirection.Out,0,false,0),{Size=V})w:Play()return w end function W.TweenOut(O)local w=tweenService:Create(O.Model,TweenInfo.new(.25,Enum.EasingStyle.Quad,Enum.EasingDirection.In,0,false,0),{Size=ZERO_VECTOR2})w:Play()return w end function W.Place(V,O,w)if not V.Model.Parent then V.Model.Parent=workspace.Terrain V.Model.Adornee=workspace.Terrain local w=Ray.new(O+Vector3.new(0,2.5,0),Vector3.new(0,-10,0))local W,q,D=workspace:FindPartOnRayWithIgnoreList(w,{workspace.CurrentCamera,Player.Character})V.Model.CFrame=CFrame.new(q,q+D)*CFrame.Angles(0,0,0)+Vector3.new(0,.2,0)end end return W end local function w(w,V)local W={}local q=false local function D(O)for w,V in pairs(W)do if w<=O then local O=V:TweenOut()spawn(function()O.Completed:wait()V.Model:Destroy()end)W[w]=nil end end end local function c()q=true D(#w)end spawn(function()for D=1,#w,1 do if q then break end local c=D%V==0 and(D<#w and(w[#w].Position-w[D].Position).magnitude>4)if c then local V=O("\080\097\116\104\101\114\080\111\112\117\112")W[D]=V local q=w[D+1]V:Place(w[D].Position,q and q.Position or w[#w].Position)local c=V:TweenIn()wait(.2)end end end)return c,D end local function V(O,V,W)local q={}q.Cancelled=false q.Started=false q.Finished=Signal.Create()q.PathFailed=Signal.Create()q.PathStarted=Signal.Create()q.PathComputing=false q.PathComputed=false q.TargetPoint=V q.TargetSurfaceNormal=W q.MoveToConn=nil q.CurrentPoint=0 function q.Cleanup(O)if q.stopTraverseFunc then q.stopTraverseFunc()end if q.MoveToConn then q.MoveToConn:disconnect()q.MoveToConn=nil q.humanoid=nil end q.humanoid=nil end function q.Cancel(O)q.Cancelled=true q:Cleanup()end function q.ComputePath(O)local w=findPlayerHumanoid(Player)local V=w and w.Torso local W=false if V then if q.PathComputed or q.PathComputing then return end q.PathComputing=true W=pcall(function()q.pathResult=PathfindingService:FindPathAsync(V.CFrame.p,q.TargetPoint)end)q.pointList=q.pathResult and q.pathResult:GetWaypoints()q.PathComputing=false q.PathComputed=q.pathResult and q.pathResult.Status==Enum.PathStatus.Success or false end return true end function q.IsValidPath(O)if not q.pathResult then q:ComputePath()end return q.pathResult.Status==Enum.PathStatus.Success end function q.OnPointReached(w,O)if O and not q.Cancelled then q.CurrentPoint=q.CurrentPoint+1 if q.CurrentPoint>#q.pointList then if q.stopTraverseFunc then q.stopTraverseFunc()end q.Finished:fire()q:Cleanup()else if q.CurrentPoint+1<=#q.pointList then local O=q.pointList[q.CurrentPoint+1].Action if O==Enum.PathWaypointAction.Jump then local O=q.humanoid:GetState()if O==Enum.HumanoidStateType.FallingDown or O==Enum.HumanoidStateType.Freefall or O==Enum.HumanoidStateType.Jumping then q.humanoid.FreeFalling:wait()wait(.1)end end end if q.setPointFunc then q.setPointFunc(q.CurrentPoint)end local O=q.pointList[q.CurrentPoint]if O.Action==Enum.PathWaypointAction.Jump then q.humanoid.Jump=true end q.humanoid:MoveTo(O.Position)end else q.PathFailed:fire()q:Cleanup()end end function q.Start(O)if CurrentSeatPart then return end q.humanoid=findPlayerHumanoid(Player)if q.Started then return end q.Started=true if SHOW_PATH then q.stopTraverseFunc,q.setPointFunc=w(q.pointList,4)end if#q.pointList>0 then q.MoveToConn=q.humanoid.MoveToFinished:connect(function(O)q:OnPointReached(O)end)q.CurrentPoint=1 q:OnPointReached(true)else q.PathFailed:fire()if q.stopTraverseFunc then q.stopTraverseFunc()end end end q:ComputePath()if not q.PathComputed then local O=q.TargetPoint+q.TargetSurfaceNormal*1.5 local w=Ray.new(O,Vector3_new(0,-1,0)*50)local V,W=RayCastIgnoreList(workspace,w,getIgnoreList())if V then q.TargetPoint=W end q:ComputePath()end return q end
-----------------------------------PATHER--------------------------------------

local function createNewPopup(popupType)
	local newModel = Instance.new("ImageHandleAdornment")

	newModel.AlwaysOnTop = false
	newModel.Image = "rbxasset://textures/ui/move.png"
	newModel.ZIndex = 2

	local size = ZERO_VECTOR2
	if popupType == "DestinationPopup" then
		newModel.Color3 = Color3.fromRGB(0, 175, 255)
		size = Vector2.new(4, 4)
	elseif popupType == "DirectWalkPopup" then
		newModel.Color3 = Color3.fromRGB(0, 175, 255)
		size = Vector2.new(4, 4)
	elseif popupType == "FailurePopup" then
		newModel.Color3 = Color3.fromRGB(255, 100, 100)
		size = Vector2.new(4, 4)
	elseif popupType == "PatherPopup" then
		newModel.Color3 = Color3.fromRGB(255, 255, 255)
		size = Vector2.new(3, 3)
		newModel.ZIndex = 1
	end

	local dataStructure = {}
	dataStructure.Model = newModel

	function dataStructure:TweenIn()
		local tween1 = tweenService:Create(
			self.Model,
			TweenInfo.new(1, Enum.EasingStyle.Elastic, Enum.EasingDirection.Out, 0, false, 0),
			{
				Size = size,
			}
		)
		tween1:Play()
		return tween1
	end

	function dataStructure:TweenOut()
		local tween1 = tweenService:Create(
			self.Model,
			TweenInfo.new(0.25, Enum.EasingStyle.Quad, Enum.EasingDirection.In, 0, false, 0),
			{
				Size = ZERO_VECTOR2,
			}
		)
		tween1:Play()
		return tween1
	end

	function dataStructure:Place(position, dest)
		-- place the model at position
		if not self.Model.Parent then
			self.Model.Parent = workspace.Terrain
			self.Model.Adornee = workspace.Terrain

			local ray = Ray.new(position + Vector3.new(0, 2.5, 0), Vector3.new(0, -10, 0))
			local hitPart, hitPoint, hitNormal =
				workspace:FindPartOnRayWithIgnoreList(ray, { workspace.CurrentCamera, Player.Character })

			self.Model.CFrame = CFrame.new(hitPoint, hitPoint + hitNormal) * CFrame.Angles(0, 0, 0)
				+ Vector3.new(0, 0.2, 0)
		end
	end

	return dataStructure
end

local function createPopupPath(points, numCircles)
	-- creates a path with the provided points, using the path and number of circles provided
	local popups = {}
	local stopTraversing = false

	local function killPopup(i)
		-- kill all popups before and at i
		for iter, v in pairs(popups) do
			if iter <= i then
				local tween = v:TweenOut()
				spawn(function()
					tween.Completed:wait()
					v.Model:Destroy()
				end)
				popups[iter] = nil
			end
		end
	end

	local function stopFunction()
		stopTraversing = true
		killPopup(#points)
	end

	spawn(function()
		for i = 1, #points do
			if stopTraversing then
				break
			end

			local includeWaypoint = i % numCircles == 0
				and i < #points
				and (points[#points].Position - points[i].Position).magnitude > 4
			if includeWaypoint then
				local popup = createNewPopup("PatherPopup")
				popups[i] = popup
				local nextPopup = points[i + 1]
				popup:Place(points[i].Position, nextPopup and nextPopup.Position or points[#points].Position)
				local tween = popup:TweenIn()
				wait(0.2)
			end
		end
	end)

	return stopFunction, killPopup
end

local function Pather(character, endPoint, surfaceNormal)
	local this = {}

	this.Cancelled = false
	this.Started = false

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

	this.PathComputing = false
	this.PathComputed = false

	this.TargetPoint = endPoint
	this.TargetSurfaceNormal = surfaceNormal

	this.MoveToConn = nil
	this.CurrentPoint = 0

	function this:Cleanup()
		if this.stopTraverseFunc then
			this.stopTraverseFunc()
		end

		if this.MoveToConn then
			this.MoveToConn:disconnect()
			this.MoveToConn = nil
			this.humanoid = nil
		end

		this.humanoid = nil
	end

	function this:Cancel()
		this.Cancelled = true
		this:Cleanup()
	end

	function this:ComputePath()
		local humanoid = findPlayerHumanoid(Player)
		local torso = humanoid and humanoid.Torso
		local success = false
		if torso then
			if this.PathComputed or this.PathComputing then
				return
			end
			this.PathComputing = true
			success = pcall(function()
				this.pathResult = PathfindingService:FindPathAsync(torso.CFrame.p, this.TargetPoint)
			end)
			this.pointList = this.pathResult and this.pathResult:GetWaypoints()
			this.PathComputing = false
			this.PathComputed = this.pathResult and this.pathResult.Status == Enum.PathStatus.Success or false
		end
		return true
	end

	function this:IsValidPath()
		if not this.pathResult then
			this:ComputePath()
		end
		return this.pathResult.Status == Enum.PathStatus.Success
	end

	function this:OnPointReached(reached)
		if reached and not this.Cancelled then
			this.CurrentPoint = this.CurrentPoint + 1

			if this.CurrentPoint > #this.pointList then
				-- End of path reached
				if this.stopTraverseFunc then
					this.stopTraverseFunc()
				end
				this.Finished:fire()
				this:Cleanup()
			else
				-- If next action == Jump, but the humanoid
				-- is still jumping from a previous action
				-- wait until it gets to the ground
				if this.CurrentPoint + 1 <= #this.pointList then
					local nextAction = this.pointList[this.CurrentPoint + 1].Action
					if nextAction == Enum.PathWaypointAction.Jump then
						local currentState = this.humanoid:GetState()
						if
							currentState == Enum.HumanoidStateType.FallingDown
							or currentState == Enum.HumanoidStateType.Freefall
							or currentState == Enum.HumanoidStateType.Jumping
						then
							this.humanoid.FreeFalling:wait()

							-- Give time to the humanoid's state to change
							-- Otherwise, the jump flag in Humanoid
							-- will be reset by the state change
							wait(0.1)
						end
					end
				end

				-- Move to the next point
				if this.setPointFunc then
					this.setPointFunc(this.CurrentPoint)
				end

				local nextWaypoint = this.pointList[this.CurrentPoint]

				if nextWaypoint.Action == Enum.PathWaypointAction.Jump then
					this.humanoid.Jump = true
				end
				this.humanoid:MoveTo(nextWaypoint.Position)
			end
		else
			this.PathFailed:fire()
			this:Cleanup()
		end
	end

	function this:Start()
		if CurrentSeatPart then
			return
		end

		this.humanoid = findPlayerHumanoid(Player)
		if this.Started then
			return
		end
		this.Started = true

		if SHOW_PATH then
			-- choose whichever one Mike likes best
			this.stopTraverseFunc, this.setPointFunc = createPopupPath(this.pointList, 4)
		end

		if #this.pointList > 0 then
			this.MoveToConn = this.humanoid.MoveToFinished:connect(function(reached)
				this:OnPointReached(reached)
			end)
			this.CurrentPoint = 1 -- The first waypoint is always the start location. Skip it.
			this:OnPointReached(true) -- Move to first point
		else
			this.PathFailed:fire()
			if this.stopTraverseFunc then
				this.stopTraverseFunc()
			end
		end
	end

	this:ComputePath()
	if not this.PathComputed then
		-- set the end point towards the camera and raycasted towards the ground in case we hit a wall
		local offsetPoint = this.TargetPoint + this.TargetSurfaceNormal * 1.5
		local ray = Ray.new(offsetPoint, Vector3_new(0, -1, 0) * 50)
		local newHitPart, newHitPos = RayCastIgnoreList(workspace, ray, getIgnoreList())
		if newHitPart then
			this.TargetPoint = newHitPos
		end
		-- try again
		this:ComputePath()
	end

	return this
end
