UserInputService.InputEnded:connect(function(a,f)if a.UserInputType~=Enum.UserInputType.MouseButton1 then return end if DragStart then DragStart=nil DragStartTarget=nil ClearConnection("\087\097\116\099\104\070\111\114\068\114\097\103")end if Dragging then SetAxes(MoveTool.Axes)FinishDragging()end end)function SetUpDragging(a,f)Core.Targeting.CancelSelecting()InitialState=PreparePartsForDragging()StartDragging(a,InitialState,f)end MoveTool.SetUpDragging=SetUpDragging function PreparePartsForDragging()local a={}local f=Support.FlipTable(Selection.Items)for B,l in pairs(Selection.Items)do a[l]={Anchored=l.Anchored;CanCollide=l.CanCollide;CFrame=l.CFrame}l.Anchored=true l.CanCollide=false a[l].Joints=Core.PreserveJoints(l,f)l:BreakJoints()l.Velocity=Vector3.new()l.RotVelocity=Vector3.new()end return a end function StartDragging(a,f,B)if Dragging then return end Dragging=true TrackChange()BoundingBox.ClearBoundingBox()local l if Core.Mode=="\084\111\111\108"then l=Security.GetPermissions(Security.GetSelectionAreas(Selection.Items),Core.Player)end if not a or not f[a]then return end local e=-a.CFrame:pointToObjectSpace(Core.Mouse.Hit.p)e=Vector3.new(math.clamp(GetIncrementMultiple(e.X,MoveTool.Increment),-a.Size.X/2,a.Size.X/2),math.clamp(GetIncrementMultiple(e.Y,MoveTool.Increment),-a.Size.Y/2,a.Size.Y/2),math.clamp(GetIncrementMultiple(e.Z,MoveTool.Increment),-a.Size.Z/2,a.Size.Z/2))if B then e=-a.CFrame:pointToObjectSpace(B)end SnapTracking.TargetBlacklist=Selection.Items Connections.DragSnapping=PointSnapped:connect(function(B)local Q=SurfaceAlignment or f[a].CFrame-f[a].CFrame.p a.CFrame=(CFrame.new(B)*Q)*CFrame.new(e)TranslatePartsRelativeToPart(a,f)if Core.Mode=="\084\111\111\108"and Security.ArePartsViolatingAreas(Selection.Items,Core.Player,false,l)then a.CFrame=f[a].CFrame TranslatePartsRelativeToPart(a,f)end end)CornerOffsets=GetCornerOffsets(f[a].CFrame,f)TriggerAlignment=function()DragToMouse(a,e,f,l)if SnapTracking.Enabled then PointSnapped:fire(SnappedPoint)end end Connections.Drag=Core.Mouse.Move:connect(function()DragToMouse(a,e,f,l)end)end

UserInputService.InputEnded:connect(function (InputInfo, GameProcessedEvent)

	-- Make sure this was button 1 being released
	if InputInfo.UserInputType ~= Enum.UserInputType.MouseButton1 then
		return;
	end;

	-- Clean up dragging detection listeners and data
	if DragStart then

		-- Clear dragging detection data
		DragStart = nil;
		DragStartTarget = nil;

		-- Disconnect dragging initiation listeners
		ClearConnection 'WatchForDrag';

	end;

	-- Reset from drag mode if dragging
	if Dragging then

		-- Reset normal axes option state
		SetAxes(MoveTool.Axes);

		-- Finalize the dragging operation
		FinishDragging();

	end;

end);

function SetUpDragging(BasePart, BasePoint)
	-- Sets up and initiates dragging based on the given base part

	-- Prevent selection while dragging
	Core.Targeting.CancelSelecting();

	-- Prepare parts, and start dragging
	InitialState = PreparePartsForDragging();
	StartDragging(BasePart, InitialState, BasePoint);

end;

MoveTool.SetUpDragging = SetUpDragging;

function PreparePartsForDragging()
	-- Prepares parts for dragging and returns the initial state of the parts

	local InitialState = {};

	-- Get index of parts
	local PartIndex = Support.FlipTable(Selection.Items);

	-- Stop parts from moving, and capture the initial state of the parts
	for _, Part in pairs(Selection.Items) do
		InitialState[Part] = { Anchored = Part.Anchored, CanCollide = Part.CanCollide, CFrame = Part.CFrame };
		Part.Anchored = true;
		Part.CanCollide = false;
		InitialState[Part].Joints = Core.PreserveJoints(Part, PartIndex);
		Part:BreakJoints();
		Part.Velocity = Vector3.new();
		Part.RotVelocity = Vector3.new();
	end;

	return InitialState;
end;

function StartDragging(BasePart, InitialState, BasePoint)
	-- Begins dragging the selection

	-- Ensure dragging is not already ongoing
	if Dragging then
		return;
	end;

	-- Indicate that we're dragging
	Dragging = true;

	-- Track changes
	TrackChange();

	-- Disable bounding box calculation
	BoundingBox.ClearBoundingBox();

	-- Cache area permissions information
	local AreaPermissions;
	if Core.Mode == 'Tool' then
		AreaPermissions = Security.GetPermissions(Security.GetSelectionAreas(Selection.Items), Core.Player);
	end;

	-- Ensure a base part is provided
	if not BasePart or not InitialState[BasePart] then
		return;
	end;

	-- Determine the base point for dragging
	local BasePartOffset = -BasePart.CFrame:pointToObjectSpace(Core.Mouse.Hit.p);

	-- Improve base point alignment for the given increment
	BasePartOffset = Vector3.new(
		math.clamp(GetIncrementMultiple(BasePartOffset.X, MoveTool.Increment), -BasePart.Size.X / 2, BasePart.Size.X / 2),
		math.clamp(GetIncrementMultiple(BasePartOffset.Y, MoveTool.Increment), -BasePart.Size.Y / 2, BasePart.Size.Y / 2),
		math.clamp(GetIncrementMultiple(BasePartOffset.Z, MoveTool.Increment), -BasePart.Size.Z / 2, BasePart.Size.Z / 2)
	);

	-- Use the given base point instead if any
	if BasePoint then
		BasePartOffset = -BasePart.CFrame:pointToObjectSpace(BasePoint);
	end;

	-- Prepare snapping in case it is enabled, and make sure to override its default target selection
	SnapTracking.TargetBlacklist = Selection.Items;
	Connections.DragSnapping = PointSnapped:connect(function (SnappedPoint)

		-- Align the selection's base point to the snapped point
		local Rotation = SurfaceAlignment or (InitialState[BasePart].CFrame - InitialState[BasePart].CFrame.p);
		BasePart.CFrame = CFrame.new(SnappedPoint) * Rotation * CFrame.new(BasePartOffset);
		TranslatePartsRelativeToPart(BasePart, InitialState);

		-- Make sure we're not entering any unauthorized private areas
		if Core.Mode == 'Tool' and Security.ArePartsViolatingAreas(Selection.Items, Core.Player, false, AreaPermissions) then
			BasePart.CFrame = InitialState[BasePart].CFrame;
			TranslatePartsRelativeToPart(BasePart, InitialState);
		end;

	end);

	-- Update cache of corner offsets for later crossthrough calculations
	CornerOffsets = GetCornerOffsets(InitialState[BasePart].CFrame, InitialState);

	-- Provide a callback to trigger alignment
	TriggerAlignment = function ()

		-- Trigger drag recalculation
		DragToMouse(BasePart, BasePartOffset, InitialState, AreaPermissions);

		-- Trigger snapping recalculation
		if SnapTracking.Enabled then
			PointSnapped:fire(SnappedPoint);
		end;

	end;

	-- Start up the dragging action
	Connections.Drag = Core.Mouse.Move:connect(function ()
		DragToMouse(BasePart, BasePartOffset, InitialState, AreaPermissions);
	end);

end;
