local function C(C)local Q=math_min(Utility.ViewSizeY()*.33,250)local A=Q return C.X<=A and C.Y>Utility.ViewSizeY()-Q end local function Q(C)local Q=math_min(Utility.ViewSizeY()*.33,250)local A=Q return C.X>=Utility.ViewSizeX()-A and C.Y>Utility.ViewSizeY()-Q end local function A(C)local Q=findPlayerHumanoid(Player)return Q~=nil and Q.Health>0 end local function m(C)if C~=nil then for C,Q in pairs(C:GetChildren())do if Q:IsA("\084\111\111\108")then return Q end end end end local L=nil local Y=nil local q=nil local N=nil local function M()DrivingTo=nil if L then L:Cancel()end if q then q:disconnect()q=nil end if N then N:disconnect()N=nil end if Y then local C=Y local Q=C:TweenOut()local A=nil A=Q.Completed:connect(function()A:disconnect()C.Model:Destroy()end)Y=nil end end local function O(C)local Q,A,m=-math.huge,-math.huge,-math.huge local L,Y,q=math.huge,math.huge,math.huge for N=1,#C,1 do Q,A,m=math_max(Q,C[N].Position.X),math_max(A,C[N].Position.Y),math_max(m,C[N].Position.Z)L,Y,q=math_min(L,C[N].Position.X),math_min(Y,C[N].Position.Y),math_min(q,C[N].Position.Z)end return Region3.new(Vector3_new(L,Y,q),Vector3_new(Q,A,m))end local function E(C,Q)if Q.X<C.CFrame.p.X-C.Size.X/2 or Q.X>C.CFrame.p.X+C.Size.X/2 then return false end if Q.Z<C.CFrame.p.Z-C.Size.Z/2 or Q.Z>C.CFrame.p.Z+C.Size.Z/2 then return false end return true end local J=0 local function D(C,Q)local P=workspace.CurrentCamera local r=Player.Character if not A(r)then return end if#C==1 or Q then if P then local A=P:ScreenPointToRay(C[1].x,C[1].y)local d=Ray.new(A.Origin,A.Direction*400)local l=getIgnoreList()local V=InvisicamModule:GetObscuredParts()local H={}for C,Q in pairs(V)do H[#H+1]=C end for C=1,#l,1 do H[#H+1]=l[C]end local B=findPlayerHumanoid(Player)local S,b,y,n=Utility.Raycast(d,true,H)local c,U=Utility.FindChacterAncestor(S)local W=GetTorso()local e=W.CFrame.p if Q then b=Q c=nil end if c and(U and(U.Torso and(U.Torso.CFrame.p-W.CFrame.p).magnitude<7))then M()if B then B:MoveTo(b)end local C=m(r)if C then C:Activate()LastFired=tick()end elseif b and(r and not CurrentSeatPart)then local C=Pather(r,b,y)if C:IsValidPath()then J=0 C:Start()if BindableEvent_OnFailStateChanged then BindableEvent_OnFailStateChanged:Fire(false)end M()local Q=createNewPopup("\068\101\115\116\105\110\097\116\105\111\110\080\111\112\117\112")Q:Place(b,Vector3_new(0,b.y,0))local A=createNewPopup("\070\097\105\108\117\114\101\080\111\112\117\112")local O=Q:TweenIn()L=C Y=Q q=C.Finished:connect(function()if Q then if Y==Q then Y=nil end local C=Q:TweenOut()local A=nil A=C.Completed:connect(function()A:disconnect()Q.Model:Destroy()Q=nil end)end if c then local C=findPlayerHumanoid(Player)local Q=m(r)if Q then Q:Activate()LastFired=tick()end if C then C:MoveTo(b)end end end)N=C.PathFailed:connect(function()if A then A:Place(b,Vector3_new(0,b.y,0))local C=A:TweenIn()C.Completed:wait()local Q=A:TweenOut()Q.Completed:wait()A.Model:Destroy()A=nil end end)else if b then local C=false if(b-e).Magnitude<25 and e.y-b.y>-3 then if B then if B.Sit then B.Jump=true end local Q B:MoveTo(b)C=true end end spawn(function()local Q=createNewPopup(C and"\068\105\114\101\099\116\087\097\108\107\080\111\112\117\112"or"\070\097\105\108\117\114\101\080\111\112\117\112")Q:Place(b,Vector3_new(0,b.y,0))local A=Q:TweenIn()A.Completed:wait()local m=Q:TweenOut()m.Completed:wait()Q.Model:Destroy()Q=nil end)end end elseif b and(r and CurrentSeatPart)then local Q=createNewPopup("\068\101\115\116\105\110\097\116\105\111\110\080\111\112\117\112")Y=Q Q:Place(b,Vector3_new(0,b.y,0))Q:TweenIn()DrivingTo=b local A=CurrentSeatPart:GetConnectedParts(true)while wait()do if CurrentSeatPart and Y==Q then local C=O(A)if E(C,b)then local C=Q spawn(function()local Q=C:TweenOut()Q.Completed:wait()C.Model:Destroy()end)Q=nil DrivingTo=nil break end else if CurrentSeatPart==nil and Q==Y then DrivingTo=nil D(C,b)end local A=Q spawn(function()local C=A:TweenOut()C.Completed:wait()A.Model:Destroy()end)Q=nil break end end end end elseif#C>=2 then if P then local Q=Utility.AveragePoints(C)local A=P:ScreenPointToRay(Q.x,Q.y)local L=m(r)if L then L:Activate()LastFired=tick()end end end end local function P()local A={}local m=0 local L=Enum.HumanoidStateType.Running local q={}local N=0 local O=tick()local E=Vector2_new()local J=tick()local P=Vector2_new()local r=tick()local d={[Enum.KeyCode.W]=true;[Enum.KeyCode.A]=true;[Enum.KeyCode.S]=true;[Enum.KeyCode.D]=true,[Enum.KeyCode.Up]=true,[Enum.KeyCode.Down]=true}local l=nil local V=nil local H=nil local B=nil local S=nil local b=nil local y=nil local n=nil local c=nil local U=nil local function W(C)if C then C:disconnect()end end local function e()W(l)W(V)W(H)W(B)W(S)W(b)W(y)W(c)W(n)pcall(function()RunService:UnbindFromRenderStep("\067\108\105\099\107\084\111\077\111\118\101\082\101\110\100\101\114\085\112\100\097\116\101")end)W(U)end local function p(C)return C==C and(C~=1/0 and C~=-1/0)end local function Z(C,Q)return math_atan2(Q.X*C.Z-Q.Z*C.X,Q.X*C.X+Q.Z*C.Z)end CameraModule=OrbitalCamModule()local function h(C,Q)if q[C]==nil and not Q then N=N+1 end q[C]=Q end local function o(C,Q)if q[C]==nil then q[C]=Q if not Q then N=N+1 end end end local function F(C,Q)if q[C]~=nil and q[C]==false then N=N-1 end q[C]=nil end local function G(A)e()V=UIS.InputBegan:connect(function(A,m)if A.UserInputType==Enum.UserInputType.Touch then h(A,m)local L=C(A.Position)local Y=Q(A.Position)if Y or L then for m,q in pairs(q)do if m~=A then local A=C(m.Position)local q=Q(m.Position)if m.UserInputState~=Enum.UserInputState.End and(L and q or Y and A)then if BindableEvent_OnFailStateChanged then BindableEvent_OnFailStateChanged:Fire(true)end return end end end end end if m==false and(A.UserInputType==Enum.UserInputType.Keyboard and d[A.KeyCode])then M()end if A.UserInputType==Enum.UserInputType.MouseButton1 then O=tick()E=A.Position end if A.UserInputType==Enum.UserInputType.MouseButton2 then J=tick()P=A.Position end end)H=UIS.InputChanged:connect(function(C,Q)if C.UserInputType==Enum.UserInputType.Touch then o(C,Q)end end)B=UIS.InputEnded:connect(function(C,Q)if C.UserInputType==Enum.UserInputType.Touch then F(C,Q)end if C.UserInputType==Enum.UserInputType.MouseButton2 then r=tick()local Q=C.Position if r-J<.25 and(Q-P).magnitude<5 then local C={Q}D(C)end end end)l=UIS.TouchTap:connect(function(C,Q)if not Q then D(C)end end)local function m(C)if C>.2 then return.5+C^2/2 else return 0 end end local function L(C,Q)local A,L=0,0 local Y=C.CFrame local q=Y:pointToObjectSpace(Q)local N=-q.z local M=q.magnitude A=m(math_min(1,M/50))*math.sign(N)local O=-math_atan2(-q.x,-q.z)L=O/(math_pi/4)return A,L end local function N()if CameraModule then CameraModule.UserPanningTheCamera=true if CameraModule.UserPanningTheCamera then CameraModule.UpdateTweenFunction=nil else if CameraModule.UpdateTweenFunction then local C=CameraModule.UpdateTweenFunction()if C then CameraModule.UpdateTweenFunction=nil end end end CameraModule:Update()end if CurrentSeatPart then if DrivingTo then local C,Q=L(CurrentSeatPart,DrivingTo)CurrentSeatPart.ThrottleFloat=C CurrentSeatPart.SteerFloat=Q else CurrentSeatPart.ThrottleFloat=0 CurrentSeatPart.SteerFloat=0 end end end RunService:BindToRenderStep("\067\108\105\099\107\084\111\077\111\118\101\082\101\110\100\101\114\085\112\100\097\116\101",Enum.RenderPriority.Camera.Value-1,N)local function y(C,Q,A)if Q then if TouchJump and UIS.TouchEnabled then TouchJump:Enable()end if A and A.ClassName=="\086\101\104\105\099\108\101\083\101\097\116"then CurrentSeatPart=A end else CurrentSeatPart=nil if TouchJump and UIS.TouchEnabled then TouchJump:Disable()end end end local function c(C)if UIS.TouchEnabled then if C:IsA("\084\111\111\108")then C.ManualActivationOnly=true end end if C:IsA("\072\117\109\097\110\111\105\100")then W(S)S=C.Died:connect(function()if Y then DebrisService:AddItem(Y.Model,1)end end)U=C.Seated:connect(function(Q,A)y(C,Q,A)end)if C.SeatPart then y(C,true,C.SeatPart)end end end b=A.ChildAdded:connect(function(C)c(C)end)n=A.ChildRemoved:connect(function(C)if UIS.TouchEnabled then if C:IsA("\084\111\111\108")then C.ManualActivationOnly=false end end end)for C,Q in pairs(A:GetChildren())do c(Q)end end local T=false function A.Stop(C)if T then e()M()if CameraModule then CameraModule.UpdateTweenFunction=nil CameraModule:SetEnabled(false)end if UIS.TouchEnabled then local C=Player.Character if C then for C,Q in pairs(C:GetChildren())do if Q:IsA("\084\111\111\108")then Q.ManualActivationOnly=false end end end end DrivingTo=nil T=false end end function A.Start(C)if not T then if Player.Character then G(Player.Character)end y=Player.CharacterAdded:connect(G)if CameraModule then CameraModule:SetEnabled(true)end T=true end end return A end return P


local function IsInBottomLeft(pt)
	local joystickHeight = math_min(Utility.ViewSizeY() * 0.33, 250)
	local joystickWidth = joystickHeight
	return pt.X <= joystickWidth and pt.Y > Utility.ViewSizeY() - joystickHeight
end

local function IsInBottomRight(pt)
	local joystickHeight = math_min(Utility.ViewSizeY() * 0.33, 250)
	local joystickWidth = joystickHeight
	return pt.X >= Utility.ViewSizeX() - joystickWidth and pt.Y > Utility.ViewSizeY() - joystickHeight
end

local function CheckAlive(character)
	local humanoid = findPlayerHumanoid(Player)
	return humanoid ~= nil and humanoid.Health > 0
end

local function GetEquippedTool(character)
	if character ~= nil then
		for _, child in pairs(character:GetChildren()) do
			if child:IsA('Tool') then
				return child
			end
		end
	end
end

local ExistingPather = nil
local ExistingIndicator = nil
local PathCompleteListener = nil
local PathFailedListener = nil

local function CleanupPath()
	DrivingTo = nil
	if ExistingPather then
		ExistingPather:Cancel()
	end
	if PathCompleteListener then
		PathCompleteListener:disconnect()
		PathCompleteListener = nil
	end
	if PathFailedListener then
		PathFailedListener:disconnect()
		PathFailedListener = nil
	end
	if ExistingIndicator then
		local obj = ExistingIndicator
		local tween = obj:TweenOut()
		local tweenCompleteEvent = nil
		tweenCompleteEvent = tween.Completed:connect(function()
			tweenCompleteEvent:disconnect()
			obj.Model:Destroy()
		end)
		ExistingIndicator = nil
	end
end

local function getExtentsSize(Parts)
	local maxX,maxY,maxZ = -math.huge,-math.huge,-math.huge
	local minX,minY,minZ = math.huge,math.huge,math.huge
	for i = 1, #Parts do
		maxX,maxY,maxZ = math_max(maxX, Parts[i].Position.X), math_max(maxY, Parts[i].Position.Y), math_max(maxZ, Parts[i].Position.Z)
		minX,minY,minZ = math_min(minX, Parts[i].Position.X), math_min(minY, Parts[i].Position.Y), math_min(minZ, Parts[i].Position.Z)
	end
	return Region3.new(Vector3_new(minX, minY, minZ), Vector3_new(maxX, maxY, maxZ))
end

local function inExtents(Extents, Position)
	if Position.X < (Extents.CFrame.p.X - Extents.Size.X/2) or Position.X > (Extents.CFrame.p.X + Extents.Size.X/2) then
		return false
	end
	if Position.Z < (Extents.CFrame.p.Z - Extents.Size.Z/2) or Position.Z > (Extents.CFrame.p.Z + Extents.Size.Z/2) then
		return false
	end
	--ignoring Y for now
	return true
end

local FailCount = 0
local function OnTap(tapPositions, goToPoint)
	-- Good to remember if this is the latest tap event
	local camera = workspace.CurrentCamera
	local character = Player.Character
	
	if not CheckAlive(character) then return end
	
	-- This is a path tap position
	if #tapPositions == 1 or goToPoint then
		if camera then
			local unitRay = camera:ScreenPointToRay(tapPositions[1].x, tapPositions[1].y)
			local ray = Ray.new(unitRay.Origin, unitRay.Direction*400)
			
			-- inivisicam stuff
			local initIgnore = getIgnoreList()
			local invisicamParts = InvisicamModule:GetObscuredParts()
			local ignoreTab = {}
			
			-- add to the ignore list
			for i, v in pairs(invisicamParts) do
				ignoreTab[#ignoreTab+1] = i
			end
			for i = 1, #initIgnore do
				ignoreTab[#ignoreTab+1] = initIgnore[i]
			end
			--			
			local myHumanoid = findPlayerHumanoid(Player)
			local hitPart, hitPt, hitNormal, hitMat = Utility.Raycast(ray, true, ignoreTab)

			local hitChar, hitHumanoid = Utility.FindChacterAncestor(hitPart)
			local torso = GetTorso()
			local startPos = torso.CFrame.p
			if goToPoint then
				hitPt = goToPoint
				hitChar = nil
			end
			if hitChar and hitHumanoid and hitHumanoid.Torso and (hitHumanoid.Torso.CFrame.p - torso.CFrame.p).magnitude < 7 then
				CleanupPath()
				
				if myHumanoid then
					myHumanoid:MoveTo(hitPt)
				end
				-- Do shoot
				local currentWeapon = GetEquippedTool(character)
				if currentWeapon then
					currentWeapon:Activate()
					LastFired = tick()
				end
			elseif hitPt and character and not CurrentSeatPart then
				local thisPather = Pather(character, hitPt, hitNormal)
				if thisPather:IsValidPath() then
					FailCount = 0
					
					thisPather:Start()
					if BindableEvent_OnFailStateChanged then
						BindableEvent_OnFailStateChanged:Fire(false)
					end
					CleanupPath()
					
					local destinationPopup = createNewPopup("DestinationPopup")	
					destinationPopup:Place(hitPt, Vector3_new(0,hitPt.y,0))
					local failurePopup = createNewPopup("FailurePopup")
					local currentTween = destinationPopup:TweenIn()
					
					
					ExistingPather = thisPather
					ExistingIndicator = destinationPopup

					PathCompleteListener = thisPather.Finished:connect(function()
						if destinationPopup then
							if ExistingIndicator == destinationPopup then
								ExistingIndicator = nil
							end
							local tween = destinationPopup:TweenOut()
							local tweenCompleteEvent = nil
							tweenCompleteEvent = tween.Completed:connect(function()
								tweenCompleteEvent:disconnect()
								destinationPopup.Model:Destroy()
								destinationPopup = nil
							end)
						end
						if hitChar then
							local humanoid = findPlayerHumanoid(Player)
							local currentWeapon = GetEquippedTool(character)
							if currentWeapon then
								currentWeapon:Activate()
								LastFired = tick()
							end
							if humanoid then
	
								humanoid:MoveTo(hitPt)
							end
						end
					end)
					PathFailedListener = thisPather.PathFailed:connect(function()
						if failurePopup then
							failurePopup:Place(hitPt, Vector3_new(0,hitPt.y,0))
							local failTweenIn = failurePopup:TweenIn()
							failTweenIn.Completed:wait()
							local failTweenOut = failurePopup:TweenOut()
							failTweenOut.Completed:wait()
							failurePopup.Model:Destroy()
							failurePopup = nil
						end
					end)
				else
					if hitPt then
						-- Feedback here for when we don't have a good path
						local foundDirectPath = false
						if (hitPt-startPos).Magnitude < 25 and (startPos.y-hitPt.y > -3) then
							-- move directly here
							if myHumanoid then
								if myHumanoid.Sit then
									myHumanoid.Jump = true
								end
								local currentPosition
								myHumanoid:MoveTo(hitPt)
								foundDirectPath = true
							end
						end						
						
						spawn(function()
							local directPopup = createNewPopup(foundDirectPath and "DirectWalkPopup" or "FailurePopup")
							directPopup:Place(hitPt, Vector3_new(0,hitPt.y,0))
							local directTweenIn = directPopup:TweenIn()
							directTweenIn.Completed:wait()
							local directTweenOut = directPopup:TweenOut()
							directTweenOut.Completed:wait()
							directPopup.Model:Destroy()
							directPopup = nil
						end)
					end
				end
			elseif hitPt and character and CurrentSeatPart then 
				local destinationPopup = createNewPopup("DestinationPopup")	
				ExistingIndicator = destinationPopup
				destinationPopup:Place(hitPt, Vector3_new(0,hitPt.y,0))
				destinationPopup:TweenIn()
				
				DrivingTo = hitPt
				local ConnectedParts = CurrentSeatPart:GetConnectedParts(true)
				
				while wait() do
					if CurrentSeatPart and ExistingIndicator == destinationPopup then
						local ExtentsSize = getExtentsSize(ConnectedParts)
						if inExtents(ExtentsSize, hitPt) then
							local popup = destinationPopup
							spawn(function()
								local tweenOut = popup:TweenOut()
								tweenOut.Completed:wait()
								popup.Model:Destroy()
							end)
							destinationPopup = nil
							DrivingTo = nil
							break
						end
					else
						if CurrentSeatPart == nil and destinationPopup == ExistingIndicator then
							DrivingTo = nil
							OnTap(tapPositions, hitPt)
						end
						local popup = destinationPopup
						spawn(function()
							local tweenOut = popup:TweenOut()
							tweenOut.Completed:wait()
							popup.Model:Destroy()
						end)
						destinationPopup = nil
						break
					end
				end
			end
		end
	elseif #tapPositions >= 2 then
		if camera then
			-- Do shoot
			local avgPoint = Utility.AveragePoints(tapPositions)
			local unitRay = camera:ScreenPointToRay(avgPoint.x, avgPoint.y)
			local currentWeapon = GetEquippedTool(character)
			if currentWeapon then
				currentWeapon:Activate()
				LastFired = tick()
			end
		end
	end
end


local function CreateClickToMoveModule()
	local this = {}

	local LastStateChange = 0
	local LastState = Enum.HumanoidStateType.Running
	local FingerTouches = {}
	local NumUnsunkTouches = 0
	-- PC simulation
	local mouse1Down = tick()
	local mouse1DownPos = Vector2_new()
	local mouse2Down = tick()
	local mouse2DownPos = Vector2_new()
	local mouse2Up = tick()

	local movementKeys = {
		[Enum.KeyCode.W] = true;
		[Enum.KeyCode.A] = true;
		[Enum.KeyCode.S] = true;
		[Enum.KeyCode.D] = true;
		[Enum.KeyCode.Up] = true;
		[Enum.KeyCode.Down] = true;
	}

	local TapConn = nil
	local InputBeganConn = nil
	local InputChangedConn = nil
	local InputEndedConn = nil
	local HumanoidDiedConn = nil
	local CharacterChildAddedConn = nil
	local OnCharacterAddedConn = nil
	local CharacterChildRemovedConn = nil
	local RenderSteppedConn = nil
	local HumanoidSeatedConn = nil

	local function disconnectEvent(event)
		if event then
			event:disconnect()
		end
	end

	local function DisconnectEvents()
		disconnectEvent(TapConn)
		disconnectEvent(InputBeganConn)
		disconnectEvent(InputChangedConn)
		disconnectEvent(InputEndedConn)
		disconnectEvent(HumanoidDiedConn)
		disconnectEvent(CharacterChildAddedConn)
		disconnectEvent(OnCharacterAddedConn)
		disconnectEvent(RenderSteppedConn)
		disconnectEvent(CharacterChildRemovedConn)
		pcall(function() RunService:UnbindFromRenderStep("ClickToMoveRenderUpdate") end)
		disconnectEvent(HumanoidSeatedConn)
	end



	local function IsFinite(num)
		return num == num and num ~= 1/0 and num ~= -1/0
	end
	
	local function findAngleBetweenXZVectors(vec2, vec1)
		return math_atan2(vec1.X*vec2.Z-vec1.Z*vec2.X, vec1.X*vec2.X + vec1.Z*vec2.Z)
	end
	
	-- Setup the camera
	CameraModule = OrbitalCamModule()

	local function OnTouchBegan(input, processed)
		if FingerTouches[input] == nil and not processed then
			NumUnsunkTouches = NumUnsunkTouches + 1
		end
		FingerTouches[input] = processed
	end

	local function OnTouchChanged(input, processed)
		if FingerTouches[input] == nil then
			FingerTouches[input] = processed
			if not processed then
				NumUnsunkTouches = NumUnsunkTouches + 1
			end
		end
	end

	local function OnTouchEnded(input, processed)
		if FingerTouches[input] ~= nil and FingerTouches[input] == false then
			NumUnsunkTouches = NumUnsunkTouches - 1
		end
		FingerTouches[input] = nil
	end


	local function OnCharacterAdded(character)
		DisconnectEvents()

		InputBeganConn = UIS.InputBegan:connect(function(input, processed)
			if input.UserInputType == Enum.UserInputType.Touch then
				OnTouchBegan(input, processed)

				-- Give back controls when they tap both sticks
				local wasInBottomLeft = IsInBottomLeft(input.Position)
				local wasInBottomRight = IsInBottomRight(input.Position)
				if wasInBottomRight or wasInBottomLeft then
					for otherInput, _ in pairs(FingerTouches) do
						if otherInput ~= input then
							local otherInputInLeft = IsInBottomLeft(otherInput.Position)
							local otherInputInRight = IsInBottomRight(otherInput.Position)
							if otherInput.UserInputState ~= Enum.UserInputState.End and ((wasInBottomLeft and otherInputInRight) or (wasInBottomRight and otherInputInLeft)) then
								if BindableEvent_OnFailStateChanged then
									BindableEvent_OnFailStateChanged:Fire(true)
								end
								return
							end
						end
					end
				end
			end

			 -- Cancel path when you use the keyboard controls.
			if processed == false and input.UserInputType == Enum.UserInputType.Keyboard and movementKeys[input.KeyCode] then
				CleanupPath()
			end
			if input.UserInputType == Enum.UserInputType.MouseButton1 then
				mouse1Down = tick()
				mouse1DownPos = input.Position
			end
			if input.UserInputType == Enum.UserInputType.MouseButton2 then
				mouse2Down = tick()
				mouse2DownPos = input.Position
			end
		end)

		InputChangedConn = UIS.InputChanged:connect(function(input, processed)
			if input.UserInputType == Enum.UserInputType.Touch then
				OnTouchChanged(input, processed)
			end
		end)

		InputEndedConn = UIS.InputEnded:connect(function(input, processed)
			if input.UserInputType == Enum.UserInputType.Touch then
				OnTouchEnded(input, processed)
			end

			if input.UserInputType == Enum.UserInputType.MouseButton2 then
				mouse2Up = tick()
				local currPos = input.Position
				if mouse2Up - mouse2Down < 0.25 and (currPos - mouse2DownPos).magnitude < 5 then
					local positions = {currPos}
					OnTap(positions)
				end
			end
		end)

		TapConn = UIS.TouchTap:connect(function(touchPositions, processed)
			if not processed then
				OnTap(touchPositions)
			end
		end)
		
		local function computeThrottle(dist)
			if dist > .2 then
				return 0.5+(dist^2)/2
			else
				return 0
			end
		end		

		local function getThrottleAndSteer(object, point)
			local throttle, steer = 0, 0
			local oCF = object.CFrame
			
			local relativePosition = oCF:pointToObjectSpace(point)
			local relativeZDirection = -relativePosition.z
			local relativeDistance = relativePosition.magnitude
			
			-- throttle quadratically increases from 0-1 as distance from the selected point goes from 0-50, after 50, throttle is 1.
			-- this allows shorter distance travel to have more fine-tuned control.
			throttle = computeThrottle(math_min(1,relativeDistance/50))*math.sign(relativeZDirection)
			
			local steerAngle = -math_atan2(-relativePosition.x, -relativePosition.z)
			steer = steerAngle/(math_pi/4)
			return throttle, steer
		end
		
		local function Update()
			if CameraModule then
				CameraModule.UserPanningTheCamera = true
				if CameraModule.UserPanningTheCamera then
					CameraModule.UpdateTweenFunction = nil
				else
					if CameraModule.UpdateTweenFunction then
						local done = CameraModule.UpdateTweenFunction()
						if done then
							CameraModule.UpdateTweenFunction = nil
						end
					end
				end
				CameraModule:Update()
			end

			if CurrentSeatPart then
				if DrivingTo then
					local throttle, steer = getThrottleAndSteer(CurrentSeatPart, DrivingTo)
					CurrentSeatPart.ThrottleFloat = throttle
					CurrentSeatPart.SteerFloat = steer
				else
					CurrentSeatPart.ThrottleFloat = 0
					CurrentSeatPart.SteerFloat = 0
				end
			end
		end
		
		RunService:BindToRenderStep("ClickToMoveRenderUpdate",Enum.RenderPriority.Camera.Value - 1,Update)
	
		local function onSeated(child, active, currentSeatPart)
			if active then
				if TouchJump and UIS.TouchEnabled then
					TouchJump:Enable()
				end
				if currentSeatPart and currentSeatPart.ClassName == "VehicleSeat" then
					CurrentSeatPart = currentSeatPart
				end
			else
				CurrentSeatPart = nil
				if TouchJump and UIS.TouchEnabled then
					TouchJump:Disable()
				end
			end
		end

		local function OnCharacterChildAdded(child)
			if UIS.TouchEnabled then
				if child:IsA('Tool') then
					child.ManualActivationOnly = true
				end
			end
			if child:IsA('Humanoid') then
				disconnectEvent(HumanoidDiedConn)
				HumanoidDiedConn = child.Died:connect(function()
					if ExistingIndicator then
						DebrisService:AddItem(ExistingIndicator.Model, 1)
					end
				end)
				HumanoidSeatedConn = child.Seated:connect(function(active, seat) onSeated(child, active, seat) end)
				if child.SeatPart then
					onSeated(child, true, child.SeatPart)
				end
			end
		end

		CharacterChildAddedConn = character.ChildAdded:connect(function(child)
			OnCharacterChildAdded(child)
		end)
		CharacterChildRemovedConn = character.ChildRemoved:connect(function(child)
			if UIS.TouchEnabled then
				if child:IsA('Tool') then
					child.ManualActivationOnly = false
				end
			end
		end)
		for _, child in pairs(character:GetChildren()) do
			OnCharacterChildAdded(child)
		end
	end

	local Running = false

	function this:Stop()
		if Running then
			DisconnectEvents()
			CleanupPath()
			if CameraModule then
				CameraModule.UpdateTweenFunction = nil
				CameraModule:SetEnabled(false)
			end
			-- Restore tool activation on shutdown
			if UIS.TouchEnabled then
				local character = Player.Character
				if character then
					for _, child in pairs(character:GetChildren()) do
						if child:IsA('Tool') then
							child.ManualActivationOnly = false
						end
					end
				end
			end
			DrivingTo = nil
			Running = false
		end
	end

	function this:Start()
		if not Running then
			if Player.Character then -- retro-listen
				OnCharacterAdded(Player.Character)
			end
			OnCharacterAddedConn = Player.CharacterAdded:connect(OnCharacterAdded)
			if CameraModule then
				CameraModule:SetEnabled(true)
			end
			Running = true
		end
	end

	return this
end

return CreateClickToMoveModule
