MoveTool.HandleDragging=(require(script:WaitForChild("\072\097\110\100\108\101\068\114\097\103\103\105\110\103"))).new(MoveTool)MoveTool.FreeDragging=(require(script:WaitForChild("\070\114\101\101\068\114\097\103\103\105\110\103"))).new(MoveTool)MoveTool.UIController=(require(script:WaitForChild("\085\073\067\111\110\116\114\111\108\108\101\114"))).new(MoveTool)function MoveTool.Equip(N)N:SetAxes(N.Axes)N.UIController:ShowUI()N:BindShortcutKeys()N.FreeDragging:EnableDragging()end function MoveTool.Unequip(N)if N.FreeDragging.IsDragging then N.FreeDragging:FinishDragging()end ContextActionService:UnbindAction("\066\084\058 \083\116\097\114\116 \100\114\097\103\103\105\110\103")N.UIController:HideUI()N.HandleDragging:HideHandles()N.Maid:Destroy()BoundingBox.ClearBoundingBox()SnapTracking.StopTracking()end function MoveTool.SetAxes(k,N)k.Axes=N k.AxesChanged:Fire(k.Axes)BoundingBox.ClearBoundingBox()if N=="\071\108\111\098\097\108"then BoundingBox.StartBoundingBox(function(N)k.HandleDragging:AttachHandles(N)end)elseif N=="\076\111\099\097\108"then k.HandleDragging:AttachHandles(Selection.Focus,true)elseif N=="\076\097\115\116"then k.HandleDragging:AttachHandles(Selection.Focus,true)end end function MoveTool.MovePartsAlongAxesByFace(l,N,k,z,L)local K=Vector3.FromNormalId(N)*k if l.Axes=="\071\108\111\098\097\108"then for N,k in pairs(z)do N.CFrame=k.CFrame+K end elseif l.Axes=="\076\111\099\097\108"then for N,k in pairs(z)do N.CFrame=k.CFrame*CFrame.new(K)end elseif l.Axes=="\076\097\115\116"then local N=L*CFrame.new(K)for k,z in pairs(z)do local l=L:toObjectSpace(z.CFrame)k.CFrame=N*l end end end function MoveTool.BindShortcutKeys(N)N.Maid.HotkeyStart=UserInputService.InputBegan:Connect(function(k,z)if z then return end if k.UserInputType~=Enum.UserInputType.Keyboard then return end if UserInputService:GetFocusedTextBox()then return end if k.KeyCode==Enum.KeyCode.Return or k.KeyCode==Enum.KeyCode.KeypadEnter then if N.Axes=="\071\108\111\098\097\108"then N:SetAxes("\076\111\099\097\108")elseif N.Axes=="\076\111\099\097\108"then N:SetAxes("\076\097\115\116")elseif N.Axes=="\076\097\115\116"then N:SetAxes("\071\108\111\098\097\108")end elseif k.KeyCode==Enum.KeyCode.R and not Selection.Multiselecting then N:StartSnapping()elseif k.KeyCode==Enum.KeyCode.KeypadEight then N:NudgeSelectionByFace(Enum.NormalId.Top)elseif k.KeyCode==Enum.KeyCode.KeypadTwo then N:NudgeSelectionByFace(Enum.NormalId.Bottom)elseif k.KeyCode==Enum.KeyCode.KeypadNine then N:NudgeSelectionByFace(Enum.NormalId.Front)elseif k.KeyCode==Enum.KeyCode.KeypadOne then N:NudgeSelectionByFace(Enum.NormalId.Back)elseif k.KeyCode==Enum.KeyCode.KeypadFour then N:NudgeSelectionByFace(Enum.NormalId.Left)elseif k.KeyCode==Enum.KeyCode.KeypadSix then N:NudgeSelectionByFace(Enum.NormalId.Right)elseif k.KeyCode==Enum.KeyCode.T and not Selection.Multiselecting then N.FreeDragging:AlignSelectionToTarget()end end)N.Maid.HotkeyRelease=UserInputService.InputEnded:Connect(function(k,z)if z then return end if k.UserInputType~=Enum.UserInputType.Keyboard then return end if k.KeyCode==Enum.KeyCode.R then if UserInputService:GetFocusedTextBox()then return end if not N.FreeDragging.IsDragging then N:SetAxes(N.Axes)end SnapTracking.StopTracking()elseif k.KeyCode.Name=="\077\105\110\117\115"or k.KeyCode.Name=="\075\101\121\112\097\100\077\105\110\117\115"then N.UIController:FocusIncrementInput()end end)end function MoveTool.StartSnapping(N)N.HandleDragging:AttachHandles(nil,true)BoundingBox.ClearBoundingBox()if N.FreeDragging.IsDragging then SnapTracking.TargetBlacklist=Selection.Items end SnapTracking.StartTracking(function(k)if k then N.SnappedPoint=k.p N.PointSnapped:Fire(N.SnappedPoint)end end)end function MoveTool.SetAxisPosition(z,N,k)z:TrackChange()local L=z:PreparePartsForDragging()for z in pairs(L)do z.CFrame=CFrame.new(N=="\088"and k or z.Position.X,N=="\089"and k or z.Position.Y,N=="\090"and k or z.Position.Z)*(z.CFrame-z.CFrame.p)end local l=Security.GetPermissions(Security.GetSelectionAreas(Selection.Parts),Core.Player)if Core.Mode=="\084\111\111\108"and Security.ArePartsViolatingAreas(Selection.Parts,Core.Player,false,l)then for N,k in pairs(L)do N.CFrame=k.CFrame end end for N,k in pairs(L)do N:MakeJoints()Core.RestoreJoints(k.Joints)N.CanCollide=k.CanCollide N.Anchored=k.Anchored end z:RegisterChange()end function MoveTool.NudgeSelectionByFace(k,N)local z=k.Increment local L=Support.FlipTable(Support.GetListMembers(UserInputService:GetKeysPressed(),"\075\101\121\067\111\100\101"))if L[Enum.KeyCode.LeftShift]or L[Enum.KeyCode.RightShift]then z=-z end k:TrackChange()local l,K=k:PreparePartsForDragging()k:MovePartsAlongAxesByFace(N,z,l,K)k.DragChanged:Fire(z)local w=Security.GetPermissions(Security.GetSelectionAreas(Selection.Parts),Core.Player)if Core.Mode=="\084\111\111\108"and Security.ArePartsViolatingAreas(Selection.Parts,Core.Player,false,w)then for N,k in pairs(l)do N.CFrame=k.CFrame end end for N,k in pairs(l)do N:MakeJoints()Core.RestoreJoints(k.Joints)N.CanCollide=k.CanCollide N.Anchored=k.Anchored end k:RegisterChange()end function MoveTool.TrackChange(N)N.HistoryRecord={Parts=Support.CloneTable(Selection.Parts),BeforeCFrame={};AfterCFrame={};Selection=Selection.Items;Unapply=function(N)Selection.Replace(N.Selection)local k={}for z,L in pairs(N.Parts)do table.insert(k,{Part=L;CFrame=N.BeforeCFrame[L]})end Core.SyncAPI:Invoke("\083\121\110\099\077\111\118\101",k)end;Apply=function(N)Selection.Replace(N.Selection)local k={}for z,L in pairs(N.Parts)do table.insert(k,{Part=L,CFrame=N.AfterCFrame[L]})end Core.SyncAPI:Invoke("\083\121\110\099\077\111\118\101",k)end}for k,z in pairs(N.HistoryRecord.Parts)do N.HistoryRecord.BeforeCFrame[z]=z.CFrame end end function MoveTool.RegisterChange(N)if not N.HistoryRecord then return end local k={}for z,L in pairs(N.HistoryRecord.Parts)do N.HistoryRecord.AfterCFrame[L]=L.CFrame table.insert(k,{Part=L;CFrame=L.CFrame})end Core.SyncAPI:Invoke("\083\121\110\099\077\111\118\101",k)Core.History.Add(N.HistoryRecord)N.HistoryRecord=nil end function MoveTool.PreparePartsForDragging(N)local k={}local z=Support.FlipTable(Selection.Parts)for N,L in pairs(Selection.Parts)do k[L]={Anchored=L.Anchored,CanCollide=L.CanCollide;CFrame=L.CFrame}L.Anchored=true L.CanCollide=false k[L].Joints=Core.PreserveJoints(L,z)L:BreakJoints()L.Velocity=Vector3.new()L.RotVelocity=Vector3.new()end local L local l=Selection.Focus if not l then L=nil elseif l:IsA("\066\097\115\101\080\097\114\116")then L=l.CFrame elseif l:IsA("\077\111\100\101\108")then L=l:GetModelCFrame()end return k,L end

MoveTool.HandleDragging = require(script:WaitForChild 'HandleDragging')
	.new(MoveTool)
MoveTool.FreeDragging = require(script:WaitForChild 'FreeDragging')
	.new(MoveTool)
MoveTool.UIController = require(script:WaitForChild 'UIController')
	.new(MoveTool)

function MoveTool:Equip()
	-- Enables the tool's equipped functionality

	-- Set our current axis mode
	self:SetAxes(self.Axes)

	-- Start up our interface
	self.UIController:ShowUI()
	self:BindShortcutKeys()
	self.FreeDragging:EnableDragging()

end

function MoveTool:Unequip()
	-- Disables the tool's equipped functionality

	-- If dragging, finish dragging
	if self.FreeDragging.IsDragging then
		self.FreeDragging:FinishDragging()
	end

	-- Disable dragging
	ContextActionService:UnbindAction 'BT: Start dragging'

	-- Clear unnecessary resources
	self.UIController:HideUI()
	self.HandleDragging:HideHandles()
	self.Maid:Destroy()
	BoundingBox.ClearBoundingBox();
	SnapTracking.StopTracking();

end

function MoveTool:SetAxes(AxisMode)
	-- Sets the given axis mode

	-- Update setting
	self.Axes = AxisMode
	self.AxesChanged:Fire(self.Axes)

	-- Disable any unnecessary bounding boxes
	BoundingBox.ClearBoundingBox();

	-- For global mode, use bounding box handles
	if AxisMode == 'Global' then
		BoundingBox.StartBoundingBox(function (BoundingBox)
			self.HandleDragging:AttachHandles(BoundingBox)
		end)

	-- For local mode, use focused part handles
	elseif AxisMode == 'Local' then
		self.HandleDragging:AttachHandles(Selection.Focus, true)

	-- For last mode, use focused part handles
	elseif AxisMode == 'Last' then
		self.HandleDragging:AttachHandles(Selection.Focus, true)
	end

end

function MoveTool:MovePartsAlongAxesByFace(Face, Distance, InitialStates, InitialFocusCFrame)
	-- Moves the given parts in `InitialStates`, along the given axis mode, in the given face direction, by the given distance

	-- Calculate the shift along the direction of the face
	local Shift = Vector3.FromNormalId(Face) * Distance

	-- Move along global axes
	if self.Axes == 'Global' then
		for Part, InitialState in pairs(InitialStates) do
			Part.CFrame = InitialState.CFrame + Shift
		end

	-- Move along individual items' axes
	elseif self.Axes == 'Local' then
		for Part, InitialState in pairs(InitialStates) do
			Part.CFrame = InitialState.CFrame * CFrame.new(Shift)
		end

	-- Move along focused item's axes
	elseif self.Axes == 'Last' then

		-- Calculate focused item's position
		local FocusCFrame = InitialFocusCFrame * CFrame.new(Shift)

		-- Move parts based on initial offset from focus
		for Part, InitialState in pairs(InitialStates) do
			local FocusOffset = InitialFocusCFrame:toObjectSpace(InitialState.CFrame)
			Part.CFrame = FocusCFrame * FocusOffset
		end

	end

end

function MoveTool:BindShortcutKeys()
	-- Enables useful shortcut keys for this tool

	-- Track user input while this tool is equipped
	self.Maid.HotkeyStart = UserInputService.InputBegan:Connect(function (InputInfo, GameProcessedEvent)
		if GameProcessedEvent then
			return
		end

		-- Make sure this input is a key press
		if InputInfo.UserInputType ~= Enum.UserInputType.Keyboard then
			return;
		end;

		-- Make sure it wasn't pressed while typing
		if UserInputService:GetFocusedTextBox() then
			return;
		end;

		-- Check if the enter key was pressed
		if InputInfo.KeyCode == Enum.KeyCode.Return or InputInfo.KeyCode == Enum.KeyCode.KeypadEnter then

			-- Toggle the current axis mode
			if self.Axes == 'Global' then
				self:SetAxes('Local')
			elseif self.Axes == 'Local' then
				self:SetAxes('Last')
			elseif self.Axes == 'Last' then
				self:SetAxes('Global')
			end

		-- Check if the R key was pressed down, and it's not the selection clearing hotkey
		elseif InputInfo.KeyCode == Enum.KeyCode.R and not Selection.Multiselecting then

			-- Start tracking snap points nearest to the mouse
			self:StartSnapping()

		-- Nudge up if the 8 button on the keypad is pressed
		elseif InputInfo.KeyCode == Enum.KeyCode.KeypadEight then
			self:NudgeSelectionByFace(Enum.NormalId.Top)

		-- Nudge down if the 2 button on the keypad is pressed
		elseif InputInfo.KeyCode == Enum.KeyCode.KeypadTwo then
			self:NudgeSelectionByFace(Enum.NormalId.Bottom)

		-- Nudge forward if the 9 button on the keypad is pressed
		elseif InputInfo.KeyCode == Enum.KeyCode.KeypadNine then
			self:NudgeSelectionByFace(Enum.NormalId.Front)

		-- Nudge backward if the 1 button on the keypad is pressed
		elseif InputInfo.KeyCode == Enum.KeyCode.KeypadOne then
			self:NudgeSelectionByFace(Enum.NormalId.Back)

		-- Nudge left if the 4 button on the keypad is pressed
		elseif InputInfo.KeyCode == Enum.KeyCode.KeypadFour then
			self:NudgeSelectionByFace(Enum.NormalId.Left)

		-- Nudge right if the 6 button on the keypad is pressed
		elseif InputInfo.KeyCode == Enum.KeyCode.KeypadSix then
			self:NudgeSelectionByFace(Enum.NormalId.Right)

		-- Align the selection to the current target surface if T is pressed
		elseif (InputInfo.KeyCode == Enum.KeyCode.T) and (not Selection.Multiselecting) then
			self.FreeDragging:AlignSelectionToTarget()
		end
	end)

	-- Track ending user input while this tool is equipped
	self.Maid.HotkeyRelease = UserInputService.InputEnded:Connect(function (InputInfo, GameProcessedEvent)
		if GameProcessedEvent then
			return
		end

		-- Make sure this is input from the keyboard
		if InputInfo.UserInputType ~= Enum.UserInputType.Keyboard then
			return;
		end;

		-- Check if the R key was let go
		if InputInfo.KeyCode == Enum.KeyCode.R then

			-- Make sure it wasn't pressed while typing
			if UserInputService:GetFocusedTextBox() then
				return;
			end;

			-- Reset handles if not dragging
			if not self.FreeDragging.IsDragging then
				self:SetAxes(self.Axes)
			end

			-- Stop snapping point tracking if it was enabled
			SnapTracking.StopTracking();

		-- If - key was released, focus on increment input
		elseif (InputInfo.KeyCode.Name == 'Minus') or (InputInfo.KeyCode.Name == 'KeypadMinus') then
			self.UIController:FocusIncrementInput()
		end
	end)

end

function MoveTool:StartSnapping()
	-- Starts tracking snap points nearest to the mouse

	-- Hide any handles or bounding boxes
	self.HandleDragging:AttachHandles(nil, true)
	BoundingBox.ClearBoundingBox();

	-- Avoid targeting snap points in selected parts while dragging
	if self.FreeDragging.IsDragging then
		SnapTracking.TargetBlacklist = Selection.Items;
	end;

	-- Start tracking the closest snapping point
	SnapTracking.StartTracking(function (NewPoint)

		-- Fire `SnappedPoint` and update `SnappedPoint` when there is a new snap point in focus
		if NewPoint then
			self.SnappedPoint = NewPoint.p
			self.PointSnapped:Fire(self.SnappedPoint)
		end

	end)

end

function MoveTool:SetAxisPosition(Axis, Position)
	-- Sets the selection's position on axis `Axis` to `Position`

	-- Track this change
	self:TrackChange()

	-- Prepare parts to be moved
	local InitialStates = self:PreparePartsForDragging()

	-- Update each part
	for Part in pairs(InitialStates) do

		-- Set the part's new CFrame
		Part.CFrame = CFrame.new(
			Axis == 'X' and Position or Part.Position.X,
			Axis == 'Y' and Position or Part.Position.Y,
			Axis == 'Z' and Position or Part.Position.Z
		) * (Part.CFrame - Part.CFrame.p);

	end;

	-- Cache up permissions for all private areas
	local AreaPermissions = Security.GetPermissions(Security.GetSelectionAreas(Selection.Parts), Core.Player);

	-- Revert changes if player is not authorized to move parts to target destination
	if Core.Mode == 'Tool' and Security.ArePartsViolatingAreas(Selection.Parts, Core.Player, false, AreaPermissions) then
		for Part, State in pairs(InitialStates) do
			Part.CFrame = State.CFrame;
		end;
	end;

	-- Restore the parts' original states
	for Part, State in pairs(InitialStates) do
		Part:MakeJoints();
		Core.RestoreJoints(State.Joints);
		Part.CanCollide = State.CanCollide;
		Part.Anchored = State.Anchored;
	end;

	-- Register the change
	self:RegisterChange()

end

function MoveTool:NudgeSelectionByFace(Face)
	-- Nudges the selection along the current axes mode in the direction of the focused part's face

	-- Get amount to nudge by
	local NudgeAmount = self.Increment

	-- Reverse nudge amount if shift key is held while nudging
	local PressedKeys = Support.FlipTable(Support.GetListMembers(UserInputService:GetKeysPressed(), 'KeyCode'));
	if PressedKeys[Enum.KeyCode.LeftShift] or PressedKeys[Enum.KeyCode.RightShift] then
		NudgeAmount = -NudgeAmount;
	end;

	-- Track this change
	self:TrackChange()

	-- Prepare parts to be moved
	local InitialState, InitialFocusCFrame = self:PreparePartsForDragging()

	-- Perform the movement
	self:MovePartsAlongAxesByFace(Face, NudgeAmount, InitialState, InitialFocusCFrame)

	-- Indicate updated drag distance
	self.DragChanged:Fire(NudgeAmount)

	-- Cache up permissions for all private areas
	local AreaPermissions = Security.GetPermissions(Security.GetSelectionAreas(Selection.Parts), Core.Player);

	-- Revert changes if player is not authorized to move parts to target destination
	if Core.Mode == 'Tool' and Security.ArePartsViolatingAreas(Selection.Parts, Core.Player, false, AreaPermissions) then
		for Part, State in pairs(InitialState) do
			Part.CFrame = State.CFrame;
		end;
	end;

	-- Restore the parts' original states
	for Part, State in pairs(InitialState) do
		Part:MakeJoints();
		Core.RestoreJoints(State.Joints);
		Part.CanCollide = State.CanCollide;
		Part.Anchored = State.Anchored;
	end;

	-- Register the change
	self:RegisterChange()

end

function MoveTool:TrackChange()

	-- Start the record
	self.HistoryRecord = {
		Parts = Support.CloneTable(Selection.Parts);
		BeforeCFrame = {};
		AfterCFrame = {};
		Selection = Selection.Items;

		Unapply = function (Record)
			-- Reverts this change

			-- Select the changed parts
			Selection.Replace(Record.Selection)

			-- Put together the change request
			local Changes = {};
			for _, Part in pairs(Record.Parts) do
				table.insert(Changes, { Part = Part, CFrame = Record.BeforeCFrame[Part] });
			end;

			-- Send the change request
			Core.SyncAPI:Invoke('SyncMove', Changes);

		end;

		Apply = function (Record)
			-- Applies this change

			-- Select the changed parts
			Selection.Replace(Record.Selection)

			-- Put together the change request
			local Changes = {};
			for _, Part in pairs(Record.Parts) do
				table.insert(Changes, { Part = Part, CFrame = Record.AfterCFrame[Part] });
			end;

			-- Send the change request
			Core.SyncAPI:Invoke('SyncMove', Changes);

		end;

	};

	-- Collect the selection's initial state
	for _, Part in pairs(self.HistoryRecord.Parts) do
		self.HistoryRecord.BeforeCFrame[Part] = Part.CFrame
	end

end

function MoveTool:RegisterChange()
	-- Finishes creating the history record and registers it

	-- Make sure there's an in-progress history record
	if not self.HistoryRecord then
		return
	end

	-- Collect the selection's final state
	local Changes = {};
	for _, Part in pairs(self.HistoryRecord.Parts) do
		self.HistoryRecord.AfterCFrame[Part] = Part.CFrame
		table.insert(Changes, { Part = Part, CFrame = Part.CFrame });
	end;

	-- Send the change to the server
	Core.SyncAPI:Invoke('SyncMove', Changes);

	-- Register the record and clear the staging
	Core.History.Add(self.HistoryRecord)
	self.HistoryRecord = nil

end

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

	local InitialState = {};

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

	-- Stop parts from moving, and capture the initial state of the parts
	for _, Part in pairs(Selection.Parts) 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;

	-- Get initial state of focused item
	local InitialFocusCFrame
	local Focus = Selection.Focus
	if not Focus then
		InitialFocusCFrame = nil
	elseif Focus:IsA 'BasePart' then
		InitialFocusCFrame = Focus.CFrame
	elseif Focus:IsA 'Model' then
		InitialFocusCFrame = Focus:GetModelCFrame()
	end

	return InitialState, InitialFocusCFrame
end;
