local function b(b)local V=math.min(Utility.ViewSizeY()*.33,250)local M=V return b.X<=M and b.Y>Utility.ViewSizeY()-V end local function V(b)local V=math.min(Utility.ViewSizeY()*.33,250)local M=V return b.X>=Utility.ViewSizeX()-M and b.Y>Utility.ViewSizeY()-V end local function M(b)local V=findPlayerHumanoid(Player)return V~=nil and V.Health>0 end local function Q(b)if b~=nil then for b,V in pairs(b:GetChildren())do if V:IsA("\084\111\111\108")then return V end end end end local function h(b,V)local M=40 local Q={}do local b=V for V=0,15,1 do table.insert(Q,CFrame.Angles(0,(math.pi/8)*V,0)*b)end end local h={}local function E()for b,Q in pairs(h)do local h=-1*V local E=(h:Dot(Q.Vector)+1)/2 local s=Q.Distance/M Q.Value=E*s end end for V,Q in pairs(Q)do local E,s=Utility.Raycast(Ray.new(b,Q*M),true,{Player.Character})if s then table.insert(h,{Vector=Q;Distance=(s-b).magnitude})else table.insert(h,{Vector=Q,Distance=M})end end E()table.sort(h,function(b,V)return b.Value>V.Value end)return h end local E=1 local s=nil local T=nil local e=nil local N=nil local function Y()DrivingTo=nil if s then s:Cancel()end if e then e:disconnect()e=nil end if N then N:disconnect()N=nil end if T then DebrisService:AddItem(T,0)T=nil end end local function w(b)local V=b[1].Position.X local M=b[1].Position.Y local Q=b[1].Position.Z local h=b[1].Position.X local E=b[1].Position.Y local s=b[1].Position.Z for T=2,#b,1 do V=math_max(V,b[T].Position.X)M=math_max(M,b[T].Position.Y)Q=math_max(Q,b[T].Position.Z)h=math_min(h,b[T].Position.X)E=math_min(E,b[T].Position.Y)s=math_min(s,b[T].Position.Z)end return Region3.new(Vector3_new(h,E,s),Vector3_new(V,M,Q))end local function l(b,V)if V.X<b.CFrame.p.X-b.Size.X/2 or V.X>b.CFrame.p.X+b.Size.X/2 then return false end if V.Z<b.CFrame.p.Z-b.Size.Z/2 or V.Z>b.CFrame.p.Z+b.Size.Z/2 then return false end return true end local O=nil local r=0 local d=0 local function R(b,V)E=E+1 local P=E local i=workspace.CurrentCamera local H=Player.Character if not M(H)then return end if#b==1 or V then if i then local M=Utility.GetUnitRay(b[1].x,b[1].y,MyMouse.ViewSizeX,MyMouse.ViewSizeY,i)local E=Ray.new(M.Origin,M.Direction*400)local P,c=Utility.Raycast(E,true,{H})local W,y=Utility.FindChacterAncestor(P)local D=H and(H:FindFirstChild("\072\117\109\097\110\111\105\100")and(H:FindFirstChild("\072\117\109\097\110\111\105\100")).Torso)local K=D.CFrame.p if V then c=V W=nil end if W and(y and(y.Torso and(y.Torso.CFrame.p-D.CFrame.p).magnitude<7))then Y()local b=findPlayerHumanoid(Player)if b then b:MoveTo(c)end r=r+1 local V=r local M=Q(H)if M then M:Activate()LastFired=tick()end elseif c and(H and not CurrentSeatPart)then local b=Pather(H,c)if b:IsValidPath()then d=0 Player:Move(Vector3_new(1,0,0))Player:Move(ZERO_VECTOR3)b:Start()if BindableEvent_OnFailStateChanged then BindableEvent_OnFailStateChanged:Fire(false)end Y()local V=CreateDestinationIndicator(c)V.Parent=i s=b T=V if O then O:Run()end e=b.Finished:connect(function()if O then O:Stop()end if V then if T==V then T=nil end DebrisService:AddItem(V,0)V=nil end if W then local b=findPlayerHumanoid(Player)r=r+1 local V=r local M=Q(H)if M then M:Activate()LastFired=tick()end if b then b:MoveTo(c)end end local b=D and D.CFrame.p if b and(K and tick()-Utility.GetLastInput()>2)then local V=h(b,((K-b)*XZ_VECTOR3).unit)if V[1]and(V[1].Vector and(V[1].Vector.magnitude>=.5 and V[1].Distance>3))then if CameraModule then local M=CameraModule:LookAtPreserveHeight(b+V[1].Vector*V[1].Distance)local Q,h=CameraModule:TweenCameraLook(M)end end end end)N=b.PathFailed:connect(function()if O then O:Stop()end if V then FlashRed(V)DebrisService:AddItem(V,3)end end)else if c then local b=CreateDestinationIndicator(c)FlashRed(b)DebrisService:AddItem(b,1)b.Parent=i if T==nil then d=d+1 if d>=3 then if BindableEvent_OnFailStateChanged then BindableEvent_OnFailStateChanged:Fire(true)end Y()end end end end elseif c and(H and CurrentSeatPart)then local V=CreateDestinationIndicator(c)V.Parent=i T=V DrivingTo=c local M=CurrentSeatPart:GetConnectedParts(true)while task.wait()do if CurrentSeatPart and T==V then local b=w(M)if l(b,V.Position)then DebrisService:AddItem(V,0)V=nil DrivingTo=nil break end else DebrisService:AddItem(V,0)if CurrentSeatPart==nil and V==T then DrivingTo=nil R(b,c)end V=nil break end end else end end elseif#b>=2 then if i then r=r+1 local V=r local M=Utility.AveragePoints(b)local h=Utility.GetUnitRay(M.x,M.y,MyMouse.ViewSizeX,MyMouse.ViewSizeY,i)local E=Q(H)if E then E:Activate()LastFired=tick()end end end end local function P()local M={}local Q=0 local h=Enum.HumanoidStateType.Running local E={}local s=0 local e=tick()local N=Vector2.new()local w=tick()local l=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 P=nil local i=nil local H=nil local c=nil local W=nil local y=nil local D=nil local K=nil local B=nil local x=nil local function t(b)if b then b:disconnect()end end local function k()t(P)t(i)t(H)t(c)t(W)t(y)t(D)t(B)t(K)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)t(x)end local function J(b)return b==b and(b~=1/0 and b~=-1/0)end local function j(b,V)return math_atan2(V.X*b.Z-V.Z*b.X,V.X*b.X+V.Z*b.Z)end CameraModule=ClassicCameraModule()do function CameraModule.LookAtPreserveHeight(V,b)local M=workspace.CurrentCamera local Q=M.Focus.p local h=CameraModule.cframe local E=(Vector3_new(h.lookVector.x,0,h.lookVector.z)).magnitude local s=(Vector3_new(b.x,Q.y,b.z)-Q).unit*E local T=s+Vector3_new(0,h.lookVector.y,0)local e=(Q-h.p).magnitude local N=Q-T.unit*e return CFrame.new(N,N+T)end local b=(CFrame.new()).lerp function CameraModule.TweenCameraLook(Q,V,M)local h=2.718281828459 local function E(b)return 1/(1+h^(-b*1.5))end local function s(b,V,M,Q)if b>=Q then return V+M end return M*math_sin((b/Q)*(math_pi/2))+V end local T,e=CFrame_new(ZERO_VECTOR3,Q:GetCameraLook()),V-V.p local N=GetThetaBetweenCFrames(T,e)N=clamp(0,math_pi,N)local Y=.65*E(N-math_pi/4)+.15 if M then Y=N/M end local w=tick()local l=w+Y Q.UpdateTweenFunction=function()local V=tick()-w local M=clamp(0,1,s(V,0,1,Y))local h=b(T,e,M)local E=j(h.lookVector,Q:GetCameraLook())if J(E)and math_abs(E)>.0001 then Q.RotateInput=Q.RotateInput+Vector2_new(E,0)end return V>=l or M>=1 end end end local function z(b,V)if E[b]==nil and not V then s=s+1 end E[b]=V end local function m(b,V)if E[b]==nil then E[b]=V if not V then s=s+1 end end end local function A(b,V)if E[b]~=nil and E[b]==false then s=s-1 end E[b]=nil end local function n(M)k()i=UIS.InputBegan:connect(function(M,Q)if M.UserInputType==Enum.UserInputType.Touch then z(M,Q)local h=b(M.Position)local s=V(M.Position)if s or h then for Q,E in pairs(E)do if Q~=M then local M=b(Q.Position)local E=V(Q.Position)if Q.UserInputState~=Enum.UserInputState.End and(h and E or s and M)then if BindableEvent_OnFailStateChanged then BindableEvent_OnFailStateChanged:Fire(true)end return end end end end end if Q==false and(M.UserInputType==Enum.UserInputType.Keyboard and d[M.KeyCode])then Y()end if M.UserInputType==Enum.UserInputType.MouseButton1 then e=tick()N=M.Position end if M.UserInputType==Enum.UserInputType.MouseButton2 then w=tick()l=M.Position end end)H=UIS.InputChanged:connect(function(b,V)if b.UserInputType==Enum.UserInputType.Touch then m(b,V)end end)c=UIS.InputEnded:connect(function(b,V)if b.UserInputType==Enum.UserInputType.Touch then A(b,V)end if b.UserInputType==Enum.UserInputType.MouseButton2 then r=tick()local V=b.Position if r-w<.25 and(V-l).magnitude<5 then local b={V}R(b)end end end)P=UIS.TouchTap:connect(function(b,V)if not V then R(b)end end)if not UIS.TouchEnabled then if O then O:Stop()O=nil end O=AutoJumper()end local function Q(b,V)local M=V-b.Position M=(Vector3_new(M.X,0,M.Z)).unit local Q=(Vector3_new(b.CFrame.lookVector.X,0,b.CFrame.lookVector.Z)).unit local h=M-Q local E=h.magnitude local s=math_deg(math_acos(M:Dot(Q)))local T=(b.CFrame:pointToObjectSpace(V)).X>0 local e=0 if E<.25 then e=1 end if E>1.8 then e=-1 end local N=CurrentSeatPart.Position-DrivingTo local Y=CurrentSeatPart.Velocity if Y.magnitude*1.5>N.magnitude then if Y.magnitude*.5>N.magnitude then e=-e else e=0 end end local w=0 if s>5 and s<175 then if T then w=1 else w=-1 end end local l=math_deg(CurrentSeatPart.RotVelocity.magnitude)local O=math_max(math_min(s,180-s),10)if(CurrentSeatPart.RotVelocity.X<0)==(w<0)then if l*1.5>O then if l*.5>O then w=-w else w=0 end end end return e,w end local function h()if CameraModule then if CameraModule.UserPanningTheCamera then CameraModule.UpdateTweenFunction=nil else if CameraModule.UpdateTweenFunction then local b=CameraModule.UpdateTweenFunction()if b then CameraModule.UpdateTweenFunction=nil end end end CameraModule:Update()end if CurrentSeatPart then if DrivingTo then local b,V=Q(CurrentSeatPart,DrivingTo)CurrentSeatPart.Throttle=b CurrentSeatPart.Steer=V end end end local s=pcall(function()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,h)end)if not s then if B then B:disconnect()end B=RunService.RenderStepped:connect(h)end local D=false local J=false local function j(b,V,M)if V then if BindableEvent_EnableTouchJump then BindableEvent_EnableTouchJump:Fire(true)end if M and M.ClassName=="\086\101\104\105\099\108\101\083\101\097\116"then CurrentSeatPart=M if O then O:Stop()O=nil D=true else D=false end if b.AutoJumpEnabled then J=true b.AutoJumpEnabled=false end end else CurrentSeatPart=nil if BindableEvent_EnableTouchJump then BindableEvent_EnableTouchJump:Fire(false)end if D then O=AutoJumper()D=false end if J then b.AutoJumpEnabled=true J=false end end end local function n(b)if UIS.TouchEnabled then if b:IsA("\084\111\111\108")then b.ManualActivationOnly=true end end if b:IsA("\072\117\109\097\110\111\105\100")then t(W)W=b.Died:connect(function()DebrisService:AddItem(T,1)if O then O:Stop()O=nil end end)local V=false local M=false x=b.Seated:connect(function(V,M)j(b,V,M)end)if b.SeatPart then j(b,true,b.SeatPart)end end end y=M.ChildAdded:connect(function(b)n(b)end)K=M.ChildRemoved:connect(function(b)if UIS.TouchEnabled then if b:IsA("\084\111\111\108")then b.ManualActivationOnly=false end end end)for b,V in pairs(M:GetChildren())do n(V)end end local I=false function M.Stop(b)if I then k()Y()if O then O:Stop()O=nil end if CameraModule then CameraModule.UpdateTweenFunction=nil CameraModule:SetEnabled(false)end if UIS.TouchEnabled then local b=Player.Character if b then for b,V in pairs(b:GetChildren())do if V:IsA("\084\111\111\108")then V.ManualActivationOnly=false end end end end DrivingTo=nil I=false end end function M.Start(b)if not I then if Player.Character then n(Player.Character)end D=Player.CharacterAdded:connect(n)if CameraModule then CameraModule:SetEnabled(true)end I=true end end return M 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 function ExploreWithRayCast(currentPoint, originDirection)
	local TestDistance = 40
	local TestVectors = {}
	do
		local forwardVector = originDirection;
		for i = 0, 15 do
			table.insert(TestVectors, CFrame.Angles(0, math.pi / 8 * i, 0) * forwardVector)
		end
	end

	local testResults = {}
	-- Heuristic should be something along the lines of distance and closeness to the traveling direction
	local function ExploreHeuristic()
		for _, testData in pairs(testResults) do
			local walkDirection = -1 * originDirection
			local directionCoeff = (walkDirection:Dot(testData['Vector']) + 1) / 2
			local distanceCoeff = testData['Distance'] / TestDistance
			testData["Value"] = directionCoeff * distanceCoeff
		end
	end

	for i, vec in pairs(TestVectors) do
		local hitPart, hitPos = Utility.Raycast(Ray.new(currentPoint, vec * TestDistance), true, {Player.Character})
		if hitPos then
			table.insert(testResults, {Vector = vec; Distance = (hitPos - currentPoint).magnitude})
		else
			table.insert(testResults, {Vector = vec; Distance = TestDistance})
		end
	end

	ExploreHeuristic()

	table.sort(testResults, function(a,b) return a["Value"] > b["Value"] end)

	return testResults
end

local TapId = 1
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
		DebrisService:AddItem(ExistingIndicator, 0)
		ExistingIndicator = nil
	end
end

local function getExtentsSize(Parts)
	local maxX = Parts[1].Position.X
	local maxY = Parts[1].Position.Y
	local maxZ = Parts[1].Position.Z
	local minX = Parts[1].Position.X
	local minY = Parts[1].Position.Y
	local minZ = Parts[1].Position.Z
	for i = 2, #Parts do
		maxX = math_max(maxX, Parts[i].Position.X)
		maxY = math_max(maxY, Parts[i].Position.Y)
		maxZ = math_max(maxZ, Parts[i].Position.Z)
		minX = math_min(minX, Parts[i].Position.X)
		minY = math_min(minY, Parts[i].Position.Y)
		minZ = 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 AutoJumperInstance = nil
local ShootCount = 0
local FailCount = 0
local function OnTap(tapPositions, goToPoint)
	-- Good to remember if this is the latest tap event
	TapId = TapId + 1
	local thisTapId = TapId


	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 = Utility.GetUnitRay(tapPositions[1].x, tapPositions[1].y, MyMouse.ViewSizeX, MyMouse.ViewSizeY, camera)
			local ray = Ray.new(unitRay.Origin, unitRay.Direction*400)
			local hitPart, hitPt = Utility.Raycast(ray, true, {character})

			local hitChar, hitHumanoid = Utility.FindChacterAncestor(hitPart)
			local torso = character and character:FindFirstChild("Humanoid") and character:FindFirstChild("Humanoid").Torso
			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()

				local myHumanoid = findPlayerHumanoid(Player)
				if myHumanoid then
					myHumanoid:MoveTo(hitPt)
				end

				ShootCount = ShootCount + 1
				local thisShoot = ShootCount
				-- Do shooot
				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)
				if thisPather:IsValidPath() then
					FailCount = 0
					-- TODO: Remove when bug in engine is fixed
					Player:Move(Vector3_new(1, 0, 0))
					Player:Move(ZERO_VECTOR3)
					thisPather:Start()
					if BindableEvent_OnFailStateChanged then
						BindableEvent_OnFailStateChanged:Fire(false)
					end
					CleanupPath()

					local destinationGlobe = CreateDestinationIndicator(hitPt)
					destinationGlobe.Parent = camera

					ExistingPather = thisPather
					ExistingIndicator = destinationGlobe

					if AutoJumperInstance then
						AutoJumperInstance:Run()
					end

					PathCompleteListener = thisPather.Finished:connect(function()
						if AutoJumperInstance then
							AutoJumperInstance:Stop()
						end
						if destinationGlobe then
							if ExistingIndicator == destinationGlobe then
								ExistingIndicator = nil
							end
							DebrisService:AddItem(destinationGlobe, 0)
							destinationGlobe = nil
						end
						if hitChar then
							local humanoid = findPlayerHumanoid(Player)
							ShootCount = ShootCount + 1
							local thisShoot = ShootCount
							-- Do shoot
							local currentWeapon = GetEquippedTool(character)
							if currentWeapon then
								currentWeapon:Activate()
								LastFired = tick()
							end
							if humanoid then
								humanoid:MoveTo(hitPt)
							end
						end
						local finishPos = torso and torso.CFrame.p --hitPt
						if finishPos and startPos and tick() - Utility.GetLastInput() > 2 then
							local exploreResults = ExploreWithRayCast(finishPos, ((startPos - finishPos) * XZ_VECTOR3).unit)
							-- Check for Nans etc..
							if exploreResults[1] and exploreResults[1]["Vector"] and exploreResults[1]["Vector"].magnitude >= 0.5 and exploreResults[1]["Distance"] > 3 then
								if CameraModule then
									local rotatedCFrame = CameraModule:LookAtPreserveHeight(finishPos + exploreResults[1]["Vector"] * exploreResults[1]["Distance"])
									local finishedSignal, duration = CameraModule:TweenCameraLook(rotatedCFrame)
								end
							end
						end
					end)
					PathFailedListener = thisPather.PathFailed:connect(function()
						if AutoJumperInstance then
							AutoJumperInstance:Stop()
						end
						if destinationGlobe then
							FlashRed(destinationGlobe)
							DebrisService:AddItem(destinationGlobe, 3)
						end
					end)
				else
					if hitPt then
						-- Feedback here for when we don't have a good path
						local failedGlobe = CreateDestinationIndicator(hitPt)
						FlashRed(failedGlobe)
						DebrisService:AddItem(failedGlobe, 1)
						failedGlobe.Parent = camera
						if ExistingIndicator == nil then
							FailCount = FailCount + 1
							if FailCount >= 3 then
								if BindableEvent_OnFailStateChanged then
									BindableEvent_OnFailStateChanged:Fire(true)
								end
								CleanupPath()
							end
						end
					end
				end
			elseif hitPt and character and CurrentSeatPart then 
				local destinationGlobe = CreateDestinationIndicator(hitPt)
				destinationGlobe.Parent = camera
				ExistingIndicator = destinationGlobe
				DrivingTo = hitPt		
				local ConnectedParts = CurrentSeatPart:GetConnectedParts(true)
				
				while task.wait() do
					if CurrentSeatPart and ExistingIndicator == destinationGlobe then
						local ExtentsSize = getExtentsSize(ConnectedParts)
						if inExtents(ExtentsSize, destinationGlobe.Position) then
							DebrisService:AddItem(destinationGlobe, 0)
							destinationGlobe = nil
							DrivingTo = nil
							break
						end
					else
						DebrisService:AddItem(destinationGlobe, 0)
						if CurrentSeatPart == nil and destinationGlobe == ExistingIndicator then
							DrivingTo = nil
							OnTap(tapPositions, hitPt)
						end
						destinationGlobe = nil
						break
					end
				end		
				
			else
				-- no hit pt
			end
		end
	elseif #tapPositions >= 2 then
		if camera then
			ShootCount = ShootCount + 1
			local thisShoot = ShootCount
			-- Do shoot
			local avgPoint = Utility.AveragePoints(tapPositions)
			local unitRay = Utility.GetUnitRay(avgPoint.x, avgPoint.y, MyMouse.ViewSizeX, MyMouse.ViewSizeY, camera)
			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 = ClassicCameraModule()

	do
		-- Extend The Camera Module Class
		function CameraModule:LookAtPreserveHeight(newLookAtPt)
			local camera = 	workspace.CurrentCamera

			local focus = camera.Focus.p

			local cameraCFrame = CameraModule.cframe
			local mag = Vector3_new(cameraCFrame.lookVector.x, 0, cameraCFrame.lookVector.z).magnitude
			local newLook = (Vector3_new(newLookAtPt.x, focus.y, newLookAtPt.z) - focus).unit * mag
			local flippedLook = newLook + Vector3_new(0, cameraCFrame.lookVector.y, 0)

			local distance = (focus - cameraCFrame.p).magnitude

			local newCamPos = focus - flippedLook.unit * distance
			return CFrame.new(newCamPos, newCamPos + flippedLook)
		end

		local lerp = CFrame.new().lerp
		function CameraModule:TweenCameraLook(desiredCFrame, speed)
			local e = 2.718281828459
			
			local function SCurve(t)
				return 1/(1 + e^(-t*1.5))
			end

			local function easeOutSine(t, b, c, d)
				if t >= d then return b + c end
				return c * math_sin(t/d * (math_pi/2)) + b;
			end

			local c0, c1 = CFrame_new(ZERO_VECTOR3, self:GetCameraLook()), desiredCFrame - desiredCFrame.p
			local theta = GetThetaBetweenCFrames(c0, c1)
			theta = clamp(0, math_pi, theta)
			local duration = 0.65 * SCurve(theta - math_pi/4) + 0.15
			if speed then
				duration = theta / speed
			end
			local start = tick()
			local finish = start + duration

			self.UpdateTweenFunction = function()
				local currTime = tick() - start
				local alpha = clamp(0, 1, easeOutSine(currTime, 0, 1, duration))
				local newCFrame = lerp(c0, c1, alpha)
				local y = findAngleBetweenXZVectors(newCFrame.lookVector, self:GetCameraLook())
				if IsFinite(y) and math_abs(y) > 0.0001 then
					self.RotateInput = self.RotateInput + Vector2_new(y, 0)
				end
				return (currTime >= finish or alpha >= 1)
			end
		end
	end
	--- Done Extending


	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)
			--print("Touch tap fake:" , processed)
			--if not processed then
			--	OnTap({input.Position})
			--end
		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)

		if not UIS.TouchEnabled then -- PC
			if AutoJumperInstance then
				AutoJumperInstance:Stop()
				AutoJumperInstance = nil
			end
			AutoJumperInstance = AutoJumper()
		end
		
		local function getThrottleAndSteer(object, point)
			local lookVector = (point - object.Position)
			lookVector = Vector3_new(lookVector.X, 0, lookVector.Z).unit
			local objectVector = Vector3_new(object.CFrame.lookVector.X, 0, object.CFrame.lookVector.Z).unit
			local dirVector = lookVector - objectVector
			local mag = dirVector.magnitude
			local degrees = math_deg(math_acos(lookVector:Dot(objectVector)))
			local side = (object.CFrame:pointToObjectSpace(point).X > 0)
			local throttle = 0
			if mag < 0.25 then
				throttle = 1
			end
			if mag > 1.8 then
				throttle = -1
			end
			local distance = CurrentSeatPart.Position - DrivingTo
			local velocity = CurrentSeatPart.Velocity
			if velocity.magnitude*1.5 > distance.magnitude then
				if velocity.magnitude*0.5 > distance.magnitude then
					throttle = -throttle
				else
					throttle = 0
				end
			end
			local steer = 0
			if degrees > 5 and degrees < 175 then
				if side then
					steer = 1
				else
					steer = -1
				end
			end
			local rotatingAt = math_deg(CurrentSeatPart.RotVelocity.magnitude)
			local degreesAway = math_max(math_min(degrees, 180 - degrees), 10)
			if (CurrentSeatPart.RotVelocity.X < 0)== (steer < 0) then
				if rotatingAt*1.5 > degreesAway then
					if rotatingAt*0.5 > degreesAway then
						steer = -steer
					else
						steer = 0
					end
				end
			end
			return throttle, steer
		end
		
		local function Update()
			if CameraModule then
				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.Throttle = throttle
					CurrentSeatPart.Steer = steer
				end
			end
		end
		
		local success = pcall(function() RunService:BindToRenderStep("ClickToMoveRenderUpdate",Enum.RenderPriority.Camera.Value - 1,Update) end)
		if not success then
			if RenderSteppedConn then
				RenderSteppedConn:disconnect()
			end
			RenderSteppedConn = RunService.RenderStepped:connect(Update)
		end
		
		local WasAutoJumper = false
		local WasAutoJumpMobile = false		
		local function onSeated(child, active, currentSeatPart)
			if active then
				if BindableEvent_EnableTouchJump then
					BindableEvent_EnableTouchJump:Fire(true)
				end
				if currentSeatPart and currentSeatPart.ClassName == "VehicleSeat" then
					CurrentSeatPart = currentSeatPart
					if AutoJumperInstance then
						AutoJumperInstance:Stop()
						AutoJumperInstance = nil
						WasAutoJumper = true
					else
						WasAutoJumper = false
					end
					if child.AutoJumpEnabled then
						WasAutoJumpMobile = true
						child.AutoJumpEnabled = false
					end
				end
			else
				CurrentSeatPart = nil
				if BindableEvent_EnableTouchJump then
					BindableEvent_EnableTouchJump:Fire(false)
				end
				if WasAutoJumper then
					AutoJumperInstance = AutoJumper()
					WasAutoJumper = false
				end
				if WasAutoJumpMobile then
					child.AutoJumpEnabled = true
					WasAutoJumpMobile = false
				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()
					DebrisService:AddItem(ExistingIndicator, 1)
					if AutoJumperInstance then
						AutoJumperInstance:Stop()
						AutoJumperInstance = nil
					end
				end)
				local WasAutoJumper = false
				local WasAutoJumpMobile = false
				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 AutoJumperInstance then
				AutoJumperInstance:Stop()
				AutoJumperInstance = nil
			end
			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
