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

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

	newModel.AlwaysOnTop = false
	newModel.Image = "rbxasset://textures/Cursors/Gamepad/Pointer@2x.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(255, 255, 100)
		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
			self.Model.CFrame = CFrame.new(position, dest) * CFrame.Angles(math.pi / 2, 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
