local F={}function SurfaceTool.Equip()ShowUI()EnableSurfaceSelection()SetSurface(SurfaceTool.Surface)end function SurfaceTool.Unequip()HideUI()ClearConnections()end function ClearConnections()for y,t in pairs(F)do t:disconnect()F[y]=nil end end function ShowUI()if SurfaceTool.UI then SurfaceTool.UI.Visible=true UIUpdater=Support.ScheduleRecurringTask(UpdateUI,.1)return end SurfaceTool.UI=Core.Tool.Interfaces.BTSurfaceToolGUI:Clone()SurfaceTool.UI.Parent=Core.UI SurfaceTool.UI.Visible=true SurfaceDropdown=(Core.Cheer(SurfaceTool.UI.SideOption.Dropdown)).Start({"\065\108\108";"\084\111\112","\066\111\116\116\111\109","\070\114\111\110\116";"\066\097\099\107","\076\101\102\116","\082\105\103\104\116"},"\065\108\108",SetSurface)local F={Studs="\083\116\117\100\115";Inlets="\073\110\108\101\116";Smooth="\083\109\111\111\116\104",Weld="\087\101\108\100";Glue="\071\108\117\101";Universal="\085\110\105\118\101\114\115\097\108";Hinge="\072\105\110\103\101",Motor="\077\111\116\111\114",["\078\111 \079\117\116\108\105\110\101"]="\083\109\111\111\116\104\078\111\079\117\116\108\105\110\101\115"}SurfaceTypeDropdown=(Core.Cheer(SurfaceTool.UI.TypeOption.Dropdown)).Start({"\083\116\117\100\115";"\073\110\108\101\116\115","\083\109\111\111\116\104";"\087\101\108\100";"\071\108\117\101";"\085\110\105\118\101\114\115\097\108";"\072\105\110\103\101";"\077\111\116\111\114","\078\111 \079\117\116\108\105\110\101"},"",function(y)SetSurfaceType(Enum.SurfaceType[F[y]])end)UIUpdater=Support.ScheduleRecurringTask(UpdateUI,.1)end function HideUI()if not SurfaceTool.UI then return end SurfaceTool.UI.Visible=false UIUpdater:Stop()end function GetSurfaceTypeDisplayName(F)if F==Enum.SurfaceType.SteppingMotor then return"\083\116\101\112\112\105\110\103 \077\111\116\111\114"elseif F==Enum.SurfaceType.SmoothNoOutlines then return"\078\111 \079\117\116\108\105\110\101"else return F.Name end end function UpdateUI()if not SurfaceTool.UI then return end if#Selection.Items==0 then SurfaceTypeDropdown.SetOption("")return end local F={}for y,t in pairs(Selection.Items)do if SurfaceTool.Surface=="\065\108\108"then table.insert(F,t.TopSurface)table.insert(F,t.BottomSurface)table.insert(F,t.FrontSurface)table.insert(F,t.BackSurface)table.insert(F,t.LeftSurface)table.insert(F,t.RightSurface)else table.insert(F,t[SurfaceTool.Surface.."\083\117\114\102\097\099\101"])end end local y=Support.IdentifyCommonItem(F)SurfaceTypeDropdown.SetOption(y and GetSurfaceTypeDisplayName(y)or"\042")end function SetSurface(F)SurfaceTool.Surface=F SurfaceDropdown.SetOption(F)end function SetSurfaceType(F)if not SurfaceTool.Surface then return end TrackChange()for y,t in pairs(Selection.Items)do if SurfaceTool.Surface=="\065\108\108"then t.TopSurface=F t.BottomSurface=F t.FrontSurface=F t.BackSurface=F t.LeftSurface=F t.RightSurface=F else t[SurfaceTool.Surface.."\083\117\114\102\097\099\101"]=F end end RegisterChange()end function EnableSurfaceSelection()F.SurfaceSelection=Selection.FocusChanged:connect(function(F)if Selection.IsSelected(Core.Mouse.Target)then SetSurface(Core.Mouse.TargetSurface.Name)end end)end function TrackChange()HistoryRecord={Parts=Support.CloneTable(Selection.Items),BeforeSurfaces={};AfterSurfaces={};Unapply=function(F)Selection.Replace(F.Parts)local y={}for t,n in pairs(F.Parts)do table.insert(y,{Part=n,Surfaces=F.BeforeSurfaces[n]})end Core.SyncAPI:Invoke("\083\121\110\099\083\117\114\102\097\099\101",y)end,Apply=function(F)Selection.Replace(F.Parts)local y={}for t,n in pairs(F.Parts)do table.insert(y,{Part=n,Surfaces=F.AfterSurfaces[n]})end Core.SyncAPI:Invoke("\083\121\110\099\083\117\114\102\097\099\101",y)end}for F,y in pairs(HistoryRecord.Parts)do HistoryRecord.BeforeSurfaces[y]={}local t=HistoryRecord.BeforeSurfaces[y]if SurfaceTool.Surface=="\065\108\108"then t.Top=y.TopSurface t.Bottom=y.BottomSurface t.Front=y.FrontSurface t.Back=y.BackSurface t.Left=y.LeftSurface t.Right=y.RightSurface else t[SurfaceTool.Surface]=y[SurfaceTool.Surface.."\083\117\114\102\097\099\101"]end end end function RegisterChange()if not HistoryRecord then return end local F={}for y,t in pairs(HistoryRecord.Parts)do HistoryRecord.AfterSurfaces[t]={}local n=HistoryRecord.AfterSurfaces[t]if SurfaceTool.Surface=="\065\108\108"then n.Top=t.TopSurface n.Bottom=t.BottomSurface n.Front=t.FrontSurface n.Back=t.BackSurface n.Left=t.LeftSurface n.Right=t.RightSurface else n[SurfaceTool.Surface]=t[SurfaceTool.Surface.."\083\117\114\102\097\099\101"]end table.insert(F,{Part=t;Surfaces=n})end Core.SyncAPI:Invoke("\083\121\110\099\083\117\114\102\097\099\101",F)Core.History.Add(HistoryRecord)HistoryRecord=nil end

local Connections = {};

function SurfaceTool.Equip()
	-- Enables the tool's equipped functionality

	-- Start up our interface
	ShowUI();
	EnableSurfaceSelection();

	-- Set our current surface mode
	SetSurface(SurfaceTool.Surface);

end;

function SurfaceTool.Unequip()
	-- Disables the tool's equipped functionality

	-- Clear unnecessary resources
	HideUI();
	ClearConnections();

end;

function ClearConnections()
	-- Clears out temporary connections

	for ConnectionKey, Connection in pairs(Connections) do
		Connection:disconnect();
		Connections[ConnectionKey] = nil;
	end;

end;

function ShowUI()
	-- Creates and reveals the UI

	-- Reveal UI if already created
	if SurfaceTool.UI then

		-- Reveal the UI
		SurfaceTool.UI.Visible = true;

		-- Update the UI every 0.1 seconds
		UIUpdater = Support.ScheduleRecurringTask(UpdateUI, 0.1);

		-- Skip UI creation
		return;

	end;

	-- Create the UI
	SurfaceTool.UI = Core.Tool.Interfaces.BTSurfaceToolGUI:Clone();
	SurfaceTool.UI.Parent = Core.UI;
	SurfaceTool.UI.Visible = true;

	-- Create the surface selection dropdown
	SurfaceDropdown = Core.Cheer(SurfaceTool.UI.SideOption.Dropdown).Start({ 'All', 'Top', 'Bottom', 'Front', 'Back', 'Left', 'Right' }, 'All', SetSurface);

	-- Map type label names to actual type names
	local SurfaceTypes = {
		['Studs'] = 'Studs',
		['Inlets'] = 'Inlet',
		['Smooth'] = 'Smooth',
		['Weld'] = 'Weld',
		['Glue'] = 'Glue',
		['Universal'] = 'Universal',
		['Hinge'] = 'Hinge',
		['Motor'] = 'Motor',
		['No Outline'] = 'SmoothNoOutlines'
	};

	-- Create the surface type selection dropdown
	SurfaceTypeDropdown = Core.Cheer(SurfaceTool.UI.TypeOption.Dropdown).Start({ 'Studs', 'Inlets', 'Smooth', 'Weld', 'Glue', 'Universal', 'Hinge', 'Motor', 'No Outline' }, '', function (Option)
		SetSurfaceType(Enum.SurfaceType[SurfaceTypes[Option]]);
	end);

	-- Update the UI every 0.1 seconds
	UIUpdater = Support.ScheduleRecurringTask(UpdateUI, 0.1);

end;

function HideUI()
	-- Hides the tool UI

	-- Make sure there's a UI
	if not SurfaceTool.UI then
		return;
	end;

	-- Hide the UI
	SurfaceTool.UI.Visible = false;

	-- Stop updating the UI
	UIUpdater:Stop();

end;

function GetSurfaceTypeDisplayName(SurfaceType)
	-- Returns a more friendly name for the given `SurfaceType`

	-- For stepping motors, add a space
	if SurfaceType == Enum.SurfaceType.SteppingMotor then
		return 'Stepping Motor';

	-- For no outlines, simplify name
	elseif SurfaceType == Enum.SurfaceType.SmoothNoOutlines then
		return 'No Outline';

	-- For other surface types, return their normal name
	else
		return SurfaceType.Name;

	end;

end;

function UpdateUI()
	-- Updates information on the UI

	-- Make sure the UI's on
	if not SurfaceTool.UI then
		return;
	end;

	-- Only show and identify current surface type if selection is not empty
	if #Selection.Items == 0 then
		SurfaceTypeDropdown.SetOption('');
		return;
	end;

	------------------------------------
	-- Update the surface type indicator
	------------------------------------

	-- Collect all different surface types in selection
	local SurfaceTypeVariations = {};
	for _, Part in pairs(Selection.Items) do

		-- Search for variations on all surfaces if all surfaces are selected
		if SurfaceTool.Surface == 'All' then
			table.insert(SurfaceTypeVariations, Part.TopSurface);
			table.insert(SurfaceTypeVariations, Part.BottomSurface);
			table.insert(SurfaceTypeVariations, Part.FrontSurface);
			table.insert(SurfaceTypeVariations, Part.BackSurface);
			table.insert(SurfaceTypeVariations, Part.LeftSurface);
			table.insert(SurfaceTypeVariations, Part.RightSurface);

		-- Search for variations on single selected surface
		else
			table.insert(SurfaceTypeVariations, Part[SurfaceTool.Surface .. 'Surface']);
		end;

	end;

	-- Identify common surface type in selection
	local CommonSurfaceType = Support.IdentifyCommonItem(SurfaceTypeVariations);

	-- Update the current surface type in the surface type dropdown
	SurfaceTypeDropdown.SetOption(CommonSurfaceType and GetSurfaceTypeDisplayName(CommonSurfaceType) or '*');

end;

function SetSurface(Surface)
	-- Changes the surface option to `Surface`

	-- Set the surface option
	SurfaceTool.Surface = Surface;

	-- Update the current surface in the surface dropdown
	SurfaceDropdown.SetOption(Surface);

end;

function SetSurfaceType(SurfaceType)
	-- Changes the selection's surface type on the currently selected surface

	-- Make sure a surface has been selected
	if not SurfaceTool.Surface then
		return;
	end;

	-- Track changes
	TrackChange();

	-- Change the surface of the parts locally
	for _, Part in pairs(Selection.Items) do

		-- Change all surfaces if all selected
		if SurfaceTool.Surface == 'All' then
			Part.TopSurface = SurfaceType;
			Part.BottomSurface = SurfaceType;
			Part.FrontSurface = SurfaceType;
			Part.BackSurface = SurfaceType;
			Part.LeftSurface = SurfaceType;
			Part.RightSurface = SurfaceType;

		-- Change specific selected surface
		else
			Part[SurfaceTool.Surface .. 'Surface'] = SurfaceType;
		end;

	end;

	-- Register changes
	RegisterChange();

end;

function EnableSurfaceSelection()
	-- Allows the player to select surfaces by clicking on them

	-- Watch out for clicks on selected parts
	Connections.SurfaceSelection = Selection.FocusChanged:connect(function (Part)
		if Selection.IsSelected(Core.Mouse.Target) then

			-- Set the surface option to the target surface
			SetSurface(Core.Mouse.TargetSurface.Name);

		end;
	end);

end;

function TrackChange()

	-- Start the record
	HistoryRecord = {
		Parts = Support.CloneTable(Selection.Items);
		BeforeSurfaces = {};
		AfterSurfaces = {};

		Unapply = function (Record)
			-- Reverts this change

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

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

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

		end;

		Apply = function (Record)
			-- Applies this change

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

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

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

		end;

	};

	-- Collect the selection's initial state
	for _, Part in pairs(HistoryRecord.Parts) do

		-- Begin to record surfaces
		HistoryRecord.BeforeSurfaces[Part] = {};
		local Surfaces = HistoryRecord.BeforeSurfaces[Part];

		-- Record all surfaces if all selected
		if SurfaceTool.Surface == 'All' then
			Surfaces.Top = Part.TopSurface;
			Surfaces.Bottom = Part.BottomSurface;
			Surfaces.Front = Part.FrontSurface;
			Surfaces.Back = Part.BackSurface;
			Surfaces.Left = Part.LeftSurface;
			Surfaces.Right = Part.RightSurface;

		-- Record specific selected surface
		else
			Surfaces[SurfaceTool.Surface] = Part[SurfaceTool.Surface .. 'Surface'];
		end;

	end;

end;

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

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

	-- Collect the selection's final state
	local Changes = {};
	for _, Part in pairs(HistoryRecord.Parts) do

		-- Begin to record surfaces
		HistoryRecord.AfterSurfaces[Part] = {};
		local Surfaces = HistoryRecord.AfterSurfaces[Part];

		-- Record all surfaces if all selected
		if SurfaceTool.Surface == 'All' then
			Surfaces.Top = Part.TopSurface;
			Surfaces.Bottom = Part.BottomSurface;
			Surfaces.Front = Part.FrontSurface;
			Surfaces.Back = Part.BackSurface;
			Surfaces.Left = Part.LeftSurface;
			Surfaces.Right = Part.RightSurface;

		-- Record specific selected surface
		else
			Surfaces[SurfaceTool.Surface] = Part[SurfaceTool.Surface .. 'Surface'];
		end;

		-- Create the change request for this part
		table.insert(Changes, { Part = Part, Surfaces = Surfaces });

	end;

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

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

end;
