function ControlModule.SelectTouchModule(F)if not UserInputService.TouchEnabled then return nil,false end local T=nil local r=Players.LocalPlayer.DevTouchMovementMode if r==Enum.DevTouchMovementMode.UserChoice then T=movementEnumToModuleMap[UserGameSettings.TouchMovementMode]elseif r==Enum.DevTouchMovementMode.Scriptable then return nil,true else T=movementEnumToModuleMap[r]end return T,true end local function F(F,T)local r=Workspace.CurrentCamera if not r then return T end if F:GetState()==Enum.HumanoidStateType.Swimming then return r.CFrame:VectorToWorldSpace(T)end local u,V local J,n,D,m,N,Z,M,b,L,c,a,S=r.CFrame:GetComponents()if L<1 and L>-1 then u=S V=Z else u=m V=-N*math.sign(L)end local P=math.sqrt(u*u+V*V)return Vector3.new((u*T.x+V*T.z)/P,0,(u*T.z-V*T.x)/P)end function ControlModule.OnRenderStepped(r,T)if r.activeController and(r.activeController.enabled and r.humanoid)then r.activeController:OnRenderStepped(T)local u=r.activeController:GetMoveVector()local V=r.activeController:IsMoveVectorCameraRelative()local J=r:GetClickToMoveController()if r.activeController~=J then if u.magnitude>0 then J:CleanupPath()else J:OnRenderStepped(T)u=J:GetMoveVector()V=J:IsMoveVectorCameraRelative()end end local n=false if r.vehicleController then u,n=r.vehicleController:Update(u,V,r.activeControlModule==Gamepad)end if FFlagUserFixMovementCameraStraightDown then if V then u=F(r.humanoid,u)end r.moveFunction(Players.LocalPlayer,u,false)else r.moveFunction(Players.LocalPlayer,u,V)end r.humanoid.Jump=r.activeController:GetIsJumping()or r.touchJumpController and r.touchJumpController:GetIsJumping()end end function ControlModule.OnHumanoidSeated(r,F,T)if F then if T and T:IsA("\086\101\104\105\099\108\101\083\101\097\116")then if not r.vehicleController then r.vehicleController=r.vehicleController.new(CONTROL_ACTION_PRIORITY)end r.vehicleController:Enable(true,T)end else if r.vehicleController then r.vehicleController:Enable(false,T)end end end function ControlModule.OnCharacterAdded(T,F)T.humanoid=F:FindFirstChildOfClass("\072\117\109\097\110\111\105\100")while not T.humanoid do F.ChildAdded:wait()T.humanoid=F:FindFirstChildOfClass("\072\117\109\097\110\111\105\100")end if T.touchGui then T.touchGui.Enabled=true end if T.humanoidSeatedConn then T.humanoidSeatedConn:Disconnect()T.humanoidSeatedConn=nil end T.humanoidSeatedConn=T.humanoid.Seated:Connect(function(F,r)T:OnHumanoidSeated(F,r)end)end function ControlModule.OnCharacterRemoving(T,F)T.humanoid=nil if T.touchGui then T.touchGui.Enabled=false end end
-- Choose current Touch control module based on settings (user, dev)
-- Returns module (possibly nil) and success code to differentiate returning nil due to error vs Scriptable
function ControlModule:SelectTouchModule()
	if not UserInputService.TouchEnabled then
		return nil, false
	end
	local touchModule = nil
	local DevMovementMode = Players.LocalPlayer.DevTouchMovementMode
	if DevMovementMode == Enum.DevTouchMovementMode.UserChoice then
		touchModule = movementEnumToModuleMap[UserGameSettings.TouchMovementMode]
	elseif DevMovementMode == Enum.DevTouchMovementMode.Scriptable then
		return nil, true
	else
		touchModule = movementEnumToModuleMap[DevMovementMode]
	end
	return touchModule, true
end

local function calculateRawMoveVector(humanoid, cameraRelativeMoveVector)
	local camera = Workspace.CurrentCamera
	if not camera then
		return cameraRelativeMoveVector
	end

	if humanoid:GetState() == Enum.HumanoidStateType.Swimming then
		return camera.CFrame:VectorToWorldSpace(cameraRelativeMoveVector)
	end

	local c, s
	local _, _, _, R00, R01, R02, _, _, R12, _, _, R22 = camera.CFrame:GetComponents()
	if R12 < 1 and R12 > -1 then
		-- X and Z components from back vector.
		c = R22
		s = R02
	else
		-- In this case the camera is looking straight up or straight down.
		-- Use X components from right and up vectors.
		c = R00
		s = -R01 * math.sign(R12)
	end
	local norm = math.sqrt(c * c + s * s)
	return Vector3.new(
		(c * cameraRelativeMoveVector.x + s * cameraRelativeMoveVector.z) / norm,
		0,
		(c * cameraRelativeMoveVector.z - s * cameraRelativeMoveVector.x) / norm
	)
end

function ControlModule:OnRenderStepped(dt)
	if self.activeController and self.activeController.enabled and self.humanoid then
		-- Give the controller a chance to adjust its state
		self.activeController:OnRenderStepped(dt)

		-- Now retrieve info from the controller
		local moveVector = self.activeController:GetMoveVector()
		local cameraRelative = self.activeController:IsMoveVectorCameraRelative()

		local clickToMoveController = self:GetClickToMoveController()
		if self.activeController ~= clickToMoveController then
			if moveVector.magnitude > 0 then
				-- Clean up any developer started MoveTo path
				clickToMoveController:CleanupPath()
			else
				-- Get move vector for developer started MoveTo
				clickToMoveController:OnRenderStepped(dt)
				moveVector = clickToMoveController:GetMoveVector()
				cameraRelative = clickToMoveController:IsMoveVectorCameraRelative()
			end
		end

		-- Are we driving a vehicle ?
		local vehicleConsumedInput = false
		if self.vehicleController then
			moveVector, vehicleConsumedInput =
				self.vehicleController:Update(moveVector, cameraRelative, self.activeControlModule == Gamepad)
		end

		-- If not, move the player
		-- Verification of vehicleConsumedInput is commented out to preserve legacy behavior, in case some game relies on Humanoid.MoveDirection still being set while in a VehicleSeat
		--if not vehicleConsumedInput then
		if FFlagUserFixMovementCameraStraightDown then
			if cameraRelative then
				moveVector = calculateRawMoveVector(self.humanoid, moveVector)
			end
			self.moveFunction(Players.LocalPlayer, moveVector, false)
		else
			self.moveFunction(Players.LocalPlayer, moveVector, cameraRelative)
		end
		--end

		-- And make them jump if needed
		self.humanoid.Jump = self.activeController:GetIsJumping()
			or (self.touchJumpController and self.touchJumpController:GetIsJumping())
	end
end

function ControlModule:OnHumanoidSeated(active, currentSeatPart)
	if active then
		if currentSeatPart and currentSeatPart:IsA("VehicleSeat") then
			if not self.vehicleController then
				self.vehicleController = self.vehicleController.new(CONTROL_ACTION_PRIORITY)
			end
			self.vehicleController:Enable(true, currentSeatPart)
		end
	else
		if self.vehicleController then
			self.vehicleController:Enable(false, currentSeatPart)
		end
	end
end

function ControlModule:OnCharacterAdded(char)
	self.humanoid = char:FindFirstChildOfClass("Humanoid")
	while not self.humanoid do
		char.ChildAdded:wait()
		self.humanoid = char:FindFirstChildOfClass("Humanoid")
	end

	if self.touchGui then
		self.touchGui.Enabled = true
	end

	if self.humanoidSeatedConn then
		self.humanoidSeatedConn:Disconnect()
		self.humanoidSeatedConn = nil
	end
	self.humanoidSeatedConn = self.humanoid.Seated:Connect(function(active, currentSeatPart)
		self:OnHumanoidSeated(active, currentSeatPart)
	end)
end

function ControlModule:OnCharacterRemoving(char)
	self.humanoid = nil

	if self.touchGui then
		self.touchGui.Enabled = false
	end
end
