roblonium-web/Game/FinobeTools/PBSTools/WiringTool.rbxm

1421 lines
48 KiB
Plaintext

<roblox xmlns:xmime="http://www.w3.org/2005/05/xmlmime" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://www.roblox.com/roblox.xsd" version="4">
<External>null</External>
<External>nil</External>
<Item class="Tool" referent="RBX0">
<Properties>
<bool name="CanBeDropped">true</bool>
<bool name="Enabled">true</bool>
<CoordinateFrame name="Grip">
<X>0</X>
<Y>0</Y>
<Z>-0.699999988</Z>
<R00>0</R00>
<R01>0</R01>
<R02>1</R02>
<R10>1</R10>
<R11>0</R11>
<R12>-0</R12>
<R20>-0</R20>
<R21>1</R21>
<R22>-0</R22>
</CoordinateFrame>
<string name="Name">WiringTool</string>
<Content name="TextureId"><url>http://www.roblox.com/asset?id=56954008</url></Content>
<string name="ToolTip">Wiring Tool</string>
</Properties>
<Item class="Part" referent="RBX1">
<Properties>
<bool name="Anchored">false</bool>
<float name="BackParamA">-0.5</float>
<float name="BackParamB">0.5</float>
<token name="BackSurface">0</token>
<token name="BackSurfaceInput">0</token>
<float name="BottomParamA">-0.5</float>
<float name="BottomParamB">0.5</float>
<token name="BottomSurface">0</token>
<token name="BottomSurfaceInput">0</token>
<int name="BrickColor">199</int>
<CoordinateFrame name="CFrame">
<X>0</X>
<Y>1.60000002</Y>
<Z>-0.5</Z>
<R00>0</R00>
<R01>0</R01>
<R02>-1</R02>
<R10>0</R10>
<R11>1</R11>
<R12>-0</R12>
<R20>1</R20>
<R21>0</R21>
<R22>-0</R22>
</CoordinateFrame>
<bool name="CanCollide">true</bool>
<float name="Elasticity">0.5</float>
<float name="Friction">0.300000012</float>
<float name="FrontParamA">-0.5</float>
<float name="FrontParamB">0.5</float>
<token name="FrontSurface">0</token>
<token name="FrontSurfaceInput">0</token>
<float name="LeftParamA">-0.5</float>
<float name="LeftParamB">0.5</float>
<token name="LeftSurface">0</token>
<token name="LeftSurfaceInput">0</token>
<bool name="Locked">true</bool>
<token name="Material">256</token>
<string name="Name">Handle</string>
<float name="Reflectance">0</float>
<float name="RightParamA">-0.5</float>
<float name="RightParamB">0.5</float>
<token name="RightSurface">0</token>
<token name="RightSurfaceInput">0</token>
<Vector3 name="RotVelocity">
<X>0</X>
<Y>0</Y>
<Z>0</Z>
</Vector3>
<float name="TopParamA">-0.5</float>
<float name="TopParamB">0.5</float>
<token name="TopSurface">0</token>
<token name="TopSurfaceInput">0</token>
<float name="Transparency">0</float>
<Vector3 name="Velocity">
<X>0</X>
<Y>0</Y>
<Z>0</Z>
</Vector3>
<token name="formFactorRaw">2</token>
<token name="shape">1</token>
<Vector3 name="size">
<X>1</X>
<Y>0.800000012</Y>
<Z>2</Z>
</Vector3>
</Properties>
<Item class="SpecialMesh" referent="RBX2">
<Properties>
<token name="LODX">2</token>
<token name="LODY">2</token>
<Content name="MeshId"><url>http://www.roblox.com/asset/?id=16884681</url></Content>
<token name="MeshType">5</token>
<string name="Name">Mesh</string>
<Vector3 name="Offset">
<X>0</X>
<Y>0</Y>
<Z>0</Z>
</Vector3>
<Vector3 name="Scale">
<X>0.600000024</X>
<Y>0.600000024</Y>
<Z>0.600000024</Z>
</Vector3>
<Content name="TextureId"><url>http://www.roblox.com/asset/?id=16884673</url></Content>
<Vector3 name="VertexColor">
<X>1</X>
<Y>1</Y>
<Z>1</Z>
</Vector3>
</Properties>
</Item>
</Item>
<Item class="LocalScript" referent="RBX3">
<Properties>
<bool name="Disabled">false</bool>
<Content name="LinkedSource"><null></null></Content>
<string name="Name">WiringScript</string>
<ProtectedString name="Source">function waitForChild(parent, child)
&#9;while not parent:FindFirstChild(child) do
&#9;&#9;parent.ChildAdded:wait()
&#9;end
end
local Tool = script.Parent
local mouseMoveCon, mouseButtonDownCon = nil
local eventTable = {}
local receiverTable = {}
local ServiceConnections = {}
local adornmentTable = {}
local eventBadgeCount = {}
local receiverBadgeCount = {}
local root = nil
local isRestricted = (game.PlaceId == 41324860 or game.PlaceId == 129686177)
waitForChild(Tool, &quot;PlayerOwner&quot;)
local playerOwner = Tool.PlayerOwner
local CONNECT_BILLBOARD_GUI = &quot;ConnectBillboardGui&quot;
local WIRE_LASSO = &quot;WireLasso&quot;
local WIRE_TEXTURE = &quot;http://www.roblox.com/asset?id=56954045&quot;
local KILL_WIRE_TEXTURE = &quot;rbxasset://Textures/ui/CloseButton_dn.png&quot;
local STATIC_PLAYER_GUI = nil
local STATIC_BASE_PLATE = nil
local SELECTED_SOURCE = nil
local SELECTED_SINK = nil
local WIRE_LASSO_MAP = {}
local WIRING_PANEL_MAP = {}
local LAST_HOVERED_PART = nil
local SCREEN_MESSAGE = nil
local ANNOTATIONS = {}
local KNOWN_SOURCE_PARTS = {}
local KNOWN_SINK_PARTS = {}
local BASE_ANNOTATION_TRANSPARENCY = 0.5
local BASE_WIRE_TRANSPARENCY = 0.5
local BASE_WIRE_RADIUS = .06
local ENHANCED_WIRE_RADIUS = .12
local SOURCE_BUTTON_TEXT_COLOR = Color3.new(1, .5, 0)
local SOURCE_BUTTON_ICON_TEXTURE = &quot;http://www.roblox.com/asset?id=61334830&quot;
local SOURCE_BUTTON_ICON_HOVER_TEXTURE = &quot;http://www.roblox.com/asset?id=61335012&quot;
local SINK_BUTTON_TEXT_COLOR = Color3.new(0, 1, 0)
local SINK_BUTTON_ICON_TEXTURE = &quot;http://www.roblox.com/asset?id=60730993&quot;
local SINK_BUTTON_ICON_HOVER_TEXTURE = &quot;http://www.roblox.com/asset?id=61335025&quot;
local BUTTON_HOVER_TEXT_COLOR = Color3.new(1, 1, 1)
local BUTTON_ICON_WIDTH = 50
local DISCONNECT_ICON_HOVER_TEXTURE = &quot;http://www.roblox.com/asset?id=55130256&quot;
local DISCONNECT_SOURCE_ICON_TEXTURE = &quot;http://www.roblox.com/asset?id=55130237&quot;
local DISCONNECT_SINK_ICON_TEXTURE = &quot;http://www.roblox.com/asset?id=55130219&quot;
local TAIL_TEXTURE = &quot;http://www.roblox.com/asset?id=55134078&quot;
local USE_BILLBOARD_GUI = true
local LAST_CLICK_TIME = 0
local CLICK_HELP_TIME_DELTA = .8
local SOURCE_BADGE_TEXTURE = &quot;http://www.roblox.com/asset?id=60730993&quot;
local SINK_BADGE_TEXTURE = &quot;http://www.roblox.com/asset?id=61334830&quot;
local ALL_TEXTURES = { SOURCE_BUTTON_ICON_TEXTURE, SOURCE_BUTTON_ICON_HOVER_TEXTURE,
&#9;&#9;SINK_BUTTON_ICON_TEXTURE, SINK_BUTTON_ICON_HOVER_TEXTURE,
&#9;&#9;DISCONNECT_ICON_HOVER_TEXTURE, DISCONNECT_SOURCE_ICON_TEXTURE,
&#9;&#9;DISCONNECT_SINK_ICON_TEXTURE, TAIL_TEXTURE, WIRE_TEXTURE, SOURCE_BADGE_TEXTURE, SINK_BADGE_TEXTURE}
for idx, asset in ipairs(ALL_TEXTURES) do
&#9;game:GetService(&quot;ContentProvider&quot;):Preload(asset)
end
function clearSelection()
&#9;SELECTED_SOURCE = nil
&#9;SELECTED_SINK = nil
&#9;getLocalLasso().From = nil
&#9;getLocalLasso().To = nil
end
function clearScreenMessage()
&#9;if SCREEN_MESSAGE ~= nil then
&#9;&#9;SCREEN_MESSAGE:Remove()
&#9;&#9;SCREEN_MESSAGE = nil
&#9;end
end
function clearHover()
&#9;if LAST_HOVERED_PART == nil then return end
&#9;local lastHover = LAST_HOVERED_PART
&#9;if not ANNOTATIONS[lastHover] then
&#9;&#9;lastHover = findModel(lastHover)
&#9;end
&#9;if not lastHover or not ANNOTATIONS[lastHover] then return end
&#9;if ANNOTATIONS[lastHover].Transparency ~= 1 then
&#9;&#9;ANNOTATIONS[lastHover].Transparency = BASE_ANNOTATION_TRANSPARENCY
&#9;end
&#9;local hoverGui = WIRING_PANEL_MAP[lastHover]
&#9;if hoverGui then hoverGui.Enabled = false end
&#9;-- hack: destroy object on server
&#9;local destroyObj = Instance.new(&quot;ObjectValue&quot;)
&#9;destroyObj.Value = hoverGui
&#9;destroyObj.Name = &quot;ObjectToDestroy&quot;
&#9;destroyObj.Parent = script.Parent.DestroyScript
&#9;WIRING_PANEL_MAP[lastHover] = nil
&#9;setPartWireTransparency(LAST_HOVERED_PART, BASE_WIRE_TRANSPARENCY, BASE_WIRE_RADIUS, &quot;&quot;)
end
function getPlayerGui()
&#9;if STATIC_PLAYER_GUI == nil then
&#9;&#9;STATIC_PLAYER_GUI = game.Players:GetPlayerFromCharacter(Tool.Parent).PlayerGui
&#9;end
&#9;return STATIC_PLAYER_GUI
end
function findMyBasePlate()
&#9;if isRestricted then
&#9;&#9;if STATIC_BASE_PLATE == nil then
&#9;&#9;&#9;local buildingAreas = game.Workspace.BuildingAreas:GetChildren()
&#9;&#9;&#9;for i = 1, #buildingAreas do
&#9;&#9;&#9;&#9;if buildingAreas[i]:FindFirstChild(&quot;Player&quot;) then
&#9;&#9;&#9;&#9;&#9;if buildingAreas[i].Player.Value == game.Players.LocalPlayer.Name then
&#9;&#9;&#9;&#9;&#9;&#9;waitForChild(buildingAreas[i],&quot;PlayerArea&quot;)
&#9;&#9;&#9;&#9;&#9;&#9;STATIC_BASE_PLATE = buildingAreas[i].PlayerArea
&#9;&#9;&#9;&#9;&#9;end
&#9;&#9;&#9;&#9;end
&#9;&#9;&#9;end
&#9;&#9;end
&#9;&#9;return STATIC_BASE_PLATE
&#9;end
&#9;return nil
end
function getLocalLasso()
&#9;if not game.Players.LocalPlayer.PlayerGui:FindFirstChild(&quot;lasso&quot;) then
&#9;&#9;local lasso = Instance.new(&quot;FloorWire&quot;)
&#9;&#9;lasso.Name = &quot;lasso&quot;
&#9;&#9;lasso.Parent = game.Players.LocalPlayer.PlayerGui
&#9;&#9;lasso.Color = BrickColor.new(&quot;Really black&quot;)
&#9;end
&#9;return game.Players.LocalPlayer.PlayerGui.lasso
end
function findModel(part)
&#9;if isRestricted then
&#9;&#9;local basePlate = findMyBasePlate()
&#9;&#9;while part ~= nil do
&#9;&#9;&#9;if part.className == &quot;Model&quot; and part.Name ~= basePlate.Name and part.Name ~= &quot;GarbageParts&quot; then
&#9;&#9;&#9;&#9;return part
&#9;&#9;&#9;elseif part.Name == basePlate.Name or part.Name == &quot;GarbageParts&quot; then
&#9;&#9;&#9;&#9;return nil
&#9;&#9;&#9;end
&#9;&#9;&#9;part = part.Parent
&#9;&#9;end
&#9;else
&#9;&#9;local origPart = part
&#9;&#9;while part ~= nil do
&#9;&#9;&#9;if part.className == &quot;Model&quot; then
&#9;&#9;&#9;&#9;return part
&#9;&#9;&#9;elseif part.Name == &quot;Workspace&quot; or part.Name == &quot;game&quot; then
&#9;&#9;&#9;&#9;return origPart
&#9;&#9;&#9;end
&#9;&#9;&#9;part = part.Parent
&#9;&#9;end
&#9;end
&#9;return nil
end
function createVisualAnnotation(part, guiMain)
&#9;local selection = Instance.new(&quot;SelectionBox&quot;, guiMain)
&#9;selection.Name = &quot;Annotation&quot;
&#9;selection.Color = BrickColor.new(&quot;Lime green&quot;)
&#9;selection.Transparency = BASE_ANNOTATION_TRANSPARENCY
&#9;selection.Adornee = part
&#9;return selection
end
function isInteractivePart(obj)
&#9;if obj == nil then return false end
&#9;if obj:IsA(&quot;Part&quot;) then
&#9;&#9;for idx, child in ipairs(obj:GetChildren()) do
&#9;&#9;&#9;if child:IsA(&quot;CustomEvent&quot;) or child:IsA(&quot;CustomEventReceiver&quot;) then
&#9;&#9;&#9;&#9;return true
&#9;&#9;&#9;end
&#9;&#9;end
&#9;end
&#9;return false
end
function applyToConnectorsWires(sourceOrSink, fn)
&#9;if sourceOrSink:IsA(&quot;CustomEvent&quot;) then
&#9;&#9;for idx, recv in ipairs(sourceOrSink:GetAttachedReceivers()) do
&#9;&#9;&#9;fn(WIRE_LASSO_MAP[sourceOrSink][recv])
&#9;&#9;end
&#9;else
&#9;&#9;local source = sourceOrSink.Source
&#9;&#9;if source ~= nil then
&#9;&#9;&#9;fn(WIRE_LASSO_MAP[source][sourceOrSink])
&#9;&#9;end
&#9;end
end
function warnNoWireableParts()
&#9;local topHint = nil
&#9;pcall(function() topHint = getPlayerGui().Gui.Hints.CenterHint end)
&#9;
&#9;if topHint then
&#9;&#9;topHint.Add.Label.Value = &quot;No Wiring Parts! Add Wiring Parts using the Stamper Tool.&quot;
&#9;&#9;topHint.Add.Width.Value = 580
&#9;&#9;topHint.Add.Time.Value = 10
&#9;&#9;topHint.Add.Disabled = true -- flip it off then on, in case it&apos;s currently running.
&#9;&#9;topHint.Add.Disabled = false
&#9;end
end
function warnNotClickingWireablePart()
&#9;if getPlayerGui():FindFirstChild(&quot;CenterHint&quot;, true) then
&#9;&#9;local topHint = getPlayerGui().Gui.Hints.CenterHint
&#9;&#9;topHint.Add.Label.Value = &quot;This part isn&apos;t wireable :(&quot;
&#9;&#9;topHint.Add.Width.Value = 580
&#9;&#9;topHint.Add.Time.Value = 2
&#9;&#9;topHint.Add.Disabled = true -- flip it off then on, in case it&apos;s currently running.
&#9;&#9;topHint.Add.Disabled = false
&#9;end
end
--------------------------------------------------------------------------------
-- Screen messages (when source/sink is selected)
function stylizeScreenMessageLabel(label, text)
&#9;label.Text = text
&#9;label.FontSize = Enum.FontSize.Size24
&#9;label.Font = Enum.Font.ArialBold
&#9;label.BackgroundTransparency = 1
&#9;label.BorderSizePixel = 0
&#9;label.Size = UDim2.new(0, label.TextBounds.x, 0, label.TextBounds.y)
&#9;label.TextColor3 = Color3.new(1, 1, 1)
end
function createSourceIcon(parent, precedingText)
&#9;local sourceIcon = Instance.new(&quot;ImageLabel&quot;, frame)
&#9;sourceIcon.Archivable = false
&#9;sourceIcon.Image = SOURCE_BUTTON_ICON_TEXTURE
&#9;sourceIcon.Size = UDim2.new(0, 30, 0, 30)
&#9;sourceIcon.BackgroundTransparency = 1
&#9;sourceIcon.BorderSizePixel = 0
&#9;sourceIcon.Position = UDim2.new(0,
&#9;&#9;&#9;precedingText.Position.X.Offset + precedingText.TextBounds.x + 5,
&#9;&#9;&#9;0, precedingText.Position.Y.Offset +
&#9;&#9;&#9;&#9;((precedingText.Size.Y.Offset - sourceIcon.Size.Y.Offset) / 2))
&#9;return sourceIcon
end
function createSinkIcon(parent, precedingText)
&#9;local sinkIcon = Instance.new(&quot;ImageLabel&quot;, frame)
&#9;sinkIcon.Archivable = false
&#9;sinkIcon.Image = SINK_BUTTON_ICON_TEXTURE
&#9;sinkIcon.Size = UDim2.new(0, 30, 0, 30)
&#9;sinkIcon.BackgroundTransparency = 1
&#9;sinkIcon.BorderSizePixel = 0
&#9;sinkIcon.Position = UDim2.new(0,
&#9;&#9;&#9;precedingText.Position.X.Offset + precedingText.TextBounds.x + 5,
&#9;&#9;&#9;0, precedingText.Position.Y.Offset +
&#9;&#9;&#9;&#9;((precedingText.Size.Y.Offset - sinkIcon.Size.Y.Offset) / 2))
&#9;return sinkIcon
end
function addToAllXPositions(objs, offset)
&#9;for idx, obj in ipairs(objs) do
&#9;&#9;pos = obj.Position
&#9;&#9;obj.Position = UDim2.new(0, pos.X.Offset + offset, 0, pos.Y.Offset)
&#9;end
end
function showSourceScreenMessage(source)
&#9;gui = Instance.new(&quot;ScreenGui&quot;, getPlayerGui())
&#9;gui.Archivable = false
&#9;frame = Instance.new(&quot;Frame&quot;, gui)
&#9;frame.Archivable = false
&#9;frame.Style = Enum.FrameStyle.RobloxRound
&#9;local line1part1 = Instance.new(&quot;TextLabel&quot;, frame)
&#9;line1part1.Archivable = false
&#9;stylizeScreenMessageLabel(line1part1, &quot;Choose a&quot;)
&#9;sinkIcon = createSinkIcon(frame, line1part1)
&#9;
&#9;line1part2 = Instance.new(&quot;TextLabel&quot;, frame)
&#9;line1part2.Archivable = false
&#9;stylizeScreenMessageLabel(line1part2, &quot;receiver to trigger when&quot;)
&#9;line1part2.Position = UDim2.new(0,
&#9;&#9;sinkIcon.Position.X.Offset + sinkIcon.Size.X.Offset + 5, 0, 0)
&#9;line1height = math.max(sinkIcon.Size.Y.Offset, line1part1.Size.Y.Offset)
&#9;line2part1 = Instance.new(&quot;TextLabel&quot;, frame)
&#9;line2part1.Archivable = false
&#9;stylizeScreenMessageLabel(line2part1, source.Parent.Name)
&#9;line2part1.Position = UDim2.new(0, 0, 0, line1height)
&#9;
&#9;sourceIcon = createSourceIcon(frame, line2part1)
&#9;line2part2 = Instance.new(&quot;TextLabel&quot;, frame)
&#9;stylizeScreenMessageLabel(line2part2, &quot;signals &quot;)
&#9;line2part2.Position = UDim2.new(0,
&#9;&#9;sourceIcon.Position.X.Offset + sourceIcon.Size.X.Offset + 5,
&#9;&#9;0, line2part1.Position.Y.Offset)
&#9;line2part3 = Instance.new(&quot;TextLabel&quot;, frame)
&#9;stylizeScreenMessageLabel(line2part3, source.Name)
&#9;line2part3.TextColor3 = SOURCE_BUTTON_TEXT_COLOR
&#9;line2part3.Position = UDim2.new(0,
&#9;&#9;line2part2.Position.X.Offset + line2part2.Size.X.Offset,
&#9;&#9;0, line2part1.Position.Y.Offset)
&#9;-- re-center
&#9;line1width = line1part2.Position.X.Offset + line1part2.Size.X.Offset
&#9;line2width = line2part3.Position.X.Offset + line2part3.Size.X.Offset
&#9;if line1width &gt; line2width then
&#9;&#9;local halfDelta = (line1width - line2width) / 2
&#9;&#9;addToAllXPositions({line2part1, sourceIcon, line2part2, line2part3}, halfDelta)
&#9;else
&#9;&#9;local halfDelta = (line2width - line1width) / 2
&#9;&#9;addToAllXPositions({line1part1, sinkIcon, line1part2}, halfDelta)
&#9;end
&#9;frame.Size = UDim2.new(0, math.max(line1width, line2width) + 15,
&#9;&#9;&#9;0, 2 * line1height + 5)
&#9;frame.Position = UDim2.new(.5, -frame.Size.X.Offset/2, 0, 0)
&#9;clearScreenMessage()
&#9;SCREEN_MESSAGE = gui
end
function showSinkScreenMessage(sink)
&#9;gui = Instance.new(&quot;ScreenGui&quot;, getPlayerGui())
&#9;frame = Instance.new(&quot;Frame&quot;, gui)
&#9;frame.Style = Enum.FrameStyle.RobloxRound
&#9;local line1part1 = Instance.new(&quot;TextLabel&quot;, frame)
&#9;stylizeScreenMessageLabel(line1part1, &quot;Choose which&quot;)
&#9;local sourceIcon = createSourceIcon(frame, line1part1)
&#9;
&#9;line1part2 = Instance.new(&quot;TextLabel&quot;, frame)
&#9;stylizeScreenMessageLabel(line1part2, &quot;signal will cause&quot;)
&#9;line1part2.Position = UDim2.new(0,
&#9;&#9;sourceIcon.Position.X.Offset + sourceIcon.Size.X.Offset + 5, 0, 0)
&#9;local line1height = math.max(sourceIcon.Size.Y.Offset, line1part1.Size.Y.Offset)
&#9;line2part1 = Instance.new(&quot;TextLabel&quot;, frame)
&#9;stylizeScreenMessageLabel(line2part1, sink.Parent.Name .. &quot; to&quot;)
&#9;line2part1.Position = UDim2.new(0, 0, 0, line1height)
&#9;
&#9;local sinkIcon = createSinkIcon(frame, line2part1)
&#9;line2part2 = Instance.new(&quot;TextLabel&quot;, frame)
&#9;stylizeScreenMessageLabel(line2part2, sink.Name)
&#9;line2part2.TextColor3 = SINK_BUTTON_TEXT_COLOR
&#9;line2part2.Position = UDim2.new(0,
&#9;&#9;sinkIcon.Position.X.Offset + sinkIcon.Size.X.Offset,
&#9;&#9;0, line2part1.Position.Y.Offset)
&#9;-- re-center
&#9;line1width = line1part2.Position.X.Offset + line1part2.Size.X.Offset
&#9;line2width = line2part2.Position.X.Offset + line2part2.Size.X.Offset
&#9;if line1width &gt; line2width then
&#9;&#9;local halfDelta = (line1width - line2width) / 2
&#9;&#9;addToAllXPositions({line2part1, sinkIcon, line2part2}, halfDelta)
&#9;else
&#9;&#9;local halfDelta = (line2width - line1width) / 2
&#9;&#9;addToAllXPositions({line1part1, sourceIcon, line1part2}, halfDelta)
&#9;end
&#9;frame.Size = UDim2.new(0, math.max(line1width, line2width) + 15,
&#9;&#9;&#9;0, 2 * line1height + 5)
&#9;frame.Position = UDim2.new(.5, -frame.Size.X.Offset/2, 0, 0)
&#9;clearScreenMessage()
&#9;SCREEN_MESSAGE = gui
end
--------------------------------------------------------------------------------
-- Hover
function setPartWireTransparency(part, transparency, wireRadius, texture)
&#9;if not part then return end
&#9;for idx, child in ipairs(part:GetChildren()) do
&#9;&#9;if child:IsA(&quot;CustomEvent&quot;) then
&#9;&#9;&#9;for idx2, recv in ipairs(child:GetAttachedReceivers()) do
&#9;&#9;&#9;&#9;addWireUiIfNotAlreadyThere(child, recv)
&#9;&#9;&#9;&#9;WIRE_LASSO_MAP[child][recv].Transparency = transparency
&#9;&#9;&#9;&#9;WIRE_LASSO_MAP[child][recv].WireRadius = wireRadius
&#9;&#9;&#9;&#9;WIRE_LASSO_MAP[child][recv].Texture = texture
&#9;&#9;&#9;&#9;WIRE_LASSO_MAP[child][recv].Color = BrickColor.new(&quot;Really black&quot;)
&#9;&#9;&#9;end
&#9;&#9;elseif child:IsA(&quot;CustomEventReceiver&quot;) then
&#9;&#9;&#9;local source = child.Source
&#9;&#9;&#9;if source ~= nil then
&#9;&#9;&#9;&#9;addWireUiIfNotAlreadyThere(source, child)
&#9;&#9;&#9;&#9;WIRE_LASSO_MAP[source][child].Transparency = transparency
&#9;&#9;&#9;&#9;WIRE_LASSO_MAP[source][child].WireRadius = wireRadius
&#9;&#9;&#9;&#9;WIRE_LASSO_MAP[source][child].Texture = texture
&#9;&#9;&#9;&#9;WIRE_LASSO_MAP[source][child].Color = BrickColor.new(&quot;Really black&quot;)
&#9;&#9;&#9;end
&#9;&#9;end
&#9;end
end
function canHighlight(part,model)
&#9;if (KNOWN_SOURCE_PARTS[part] and SELECTED_SOURCE == nil) or (KNOWN_SINK_PARTS[part] and SELECTED_SINK == nil) then
&#9;&#9;return true, part
&#9;elseif (KNOWN_SOURCE_PARTS[model] and SELECTED_SOURCE == nil) or (KNOWN_SINK_PARTS[model] and SELECTED_SINK == nil) then
&#9;&#9;return true, model
&#9;end
&#9;return false, nil
end
function hoverListener(mouse)
&#9;if mouse.Target == nil then
&#9;&#9;clearHover()
&#9;&#9;LAST_HOVERED_PART = nil
&#9;&#9;return
&#9;end
&#9;local part = mouse.Target
&#9;local model = findModel(part)
&#9;if LAST_HOVERED_PART ~= part and findModel(LAST_HOVERED_PART) ~= model then
&#9;&#9;clearHover()
&#9;&#9;LAST_HOVERED_PART = part
&#9;&#9;local highlight, instance = canHighlight(part,model)
&#9;&#9;if highlight then
&#9;&#9;&#9;ANNOTATIONS[model].Transparency = 0
&#9;&#9;&#9;buildScreenPanel(model, mouse.X, mouse.Y)
&#9;&#9;&#9;setPartWireTransparency(part, 0, ENHANCED_WIRE_RADIUS, &quot;&quot;)
&#9;&#9;end
&#9;&#9;-- Point the temporary wire to the LAST_HOVERED_PART if not nil,
&#9;&#9;-- otherwise point it at the character
&#9;&#9;local otherEndOfWire = game.Players.LocalPlayer.Character.Humanoid.Torso
&#9;&#9;if canHighlight(LAST_HOVERED_PART, model) then
&#9;&#9;&#9;otherEndOfWire = LAST_HOVERED_PART
&#9;&#9;end
&#9;&#9;if SELECTED_SOURCE ~= nil then
&#9;&#9;&#9;getLocalLasso().To = otherEndOfWire
&#9;&#9;end
&#9;&#9;if SELECTED_SINK ~= nil then
&#9;&#9;&#9;getLocalLasso().From = otherEndOfWire
&#9;&#9;end
&#9;end
end
--------------------------------------------------------------------------------
-- Connect / Disconnect dialog
function addWireUiIfNotAlreadyThere(source, sink)
&#9;if WIRE_LASSO_MAP[source] == nil then
&#9;&#9;WIRE_LASSO_MAP[source] = {}
&#9;end
&#9;if WIRE_LASSO_MAP[source][sink] ~= nil then
&#9;&#9;return
&#9;end
&#9;pairLasso = Instance.new(&quot;FloorWire&quot;, getPlayerGui())
&#9;pairLasso.From = source.Parent
&#9;pairLasso.To = sink.Parent
&#9;pairLasso.Transparency = BASE_WIRE_TRANSPARENCY
&#9;pairLasso.Texture = &quot;&quot;
&#9;pairLasso.Name = WIRE_LASSO
&#9;pairLasso.Color = BrickColor.new(&quot;Really black&quot;)
&#9;WIRE_LASSO_MAP[source][sink] = pairLasso
end
function connectHelper(source, sink)
&#9;-- clear wires coming to sink
&#9;local old_source = sink.Source
&#9;if old_source ~= nil then
&#9;&#9;wire = WIRE_LASSO_MAP[old_source][sink]
&#9;&#9;if wire ~= nil then
&#9;&#9;&#9;wire:Remove()
&#9;&#9;&#9;WIRE_LASSO_MAP[old_source][sink] = nil
&#9;&#9;end
&#9;&#9;sink.Source = nil
&#9;end
&#9;sink.Source = source
&#9;addWireUiIfNotAlreadyThere(source, sink)
end
function makeSourceConnectCallback(source)
&#9;return function()
&#9;&#9;clearHover()
&#9;&#9;if SELECTED_SINK ~= nil then
&#9;&#9;&#9;connectHelper(source, SELECTED_SINK)
&#9;&#9;&#9;clearSelection()
&#9;&#9;&#9;clearScreenMessage()
&#9;&#9;&#9;for part, val in pairs(KNOWN_SINK_PARTS) do
&#9;&#9;&#9;&#9;local model = findModel(part)
&#9;&#9;&#9;&#9;ANNOTATIONS[model].Transparency = BASE_ANNOTATION_TRANSPARENCY
&#9;&#9;&#9;end
&#9;&#9;else
&#9;&#9;&#9;SELECTED_SOURCE = source
&#9;&#9;&#9;getLocalLasso().From = source.Parent
&#9;&#9;&#9;getLocalLasso().To = game.Players.LocalPlayer.Character.Humanoid.Torso
&#9;&#9;&#9;showSourceScreenMessage(SELECTED_SOURCE)
&#9;&#9;&#9;for part, val in pairs(KNOWN_SOURCE_PARTS) do
&#9;&#9;&#9;&#9;if not KNOWN_SINK_PARTS[part] then
&#9;&#9;&#9;&#9;&#9;local model = findModel(part)
&#9;&#9;&#9;&#9;&#9;ANNOTATIONS[model].Transparency = 1
&#9;&#9;&#9;&#9;end
&#9;&#9;&#9;end
&#9;&#9;end
&#9;end
end
function makeSinkConnectCallback(sink)
&#9;return function()
&#9;&#9;clearHover()
&#9;&#9;if SELECTED_SOURCE ~= nil then
&#9;&#9;&#9;connectHelper(SELECTED_SOURCE, sink)
&#9;&#9;&#9;clearSelection()
&#9;&#9;&#9;clearScreenMessage()
&#9;&#9;&#9;for part, val in pairs(KNOWN_SOURCE_PARTS) do
&#9;&#9;&#9;&#9;local model = findModel(part)
&#9;&#9;&#9;&#9;ANNOTATIONS[model].Transparency = BASE_ANNOTATION_TRANSPARENCY
&#9;&#9;&#9;end
&#9;&#9;else
&#9;&#9;&#9;SELECTED_SINK = sink
&#9;&#9;&#9;getLocalLasso().From = game.Players.LocalPlayer.Character.Humanoid.Torso
&#9;&#9;&#9;getLocalLasso().To = sink.Parent
&#9;&#9;&#9;showSinkScreenMessage(SELECTED_SINK)
&#9;&#9;&#9;for part, val in pairs(KNOWN_SINK_PARTS) do
&#9;&#9;&#9;&#9;if not KNOWN_SOURCE_PARTS[part] then
&#9;&#9;&#9;&#9;&#9;local model = findModel(part)
&#9;&#9;&#9;&#9;&#9;ANNOTATIONS[model].Transparency = 1
&#9;&#9;&#9;&#9;end
&#9;&#9;&#9;end
&#9;&#9;end
&#9;end
end
function makeControlButton(y_position, frame, sourceOrSink, textColor, iconImage, iconHoverImage, callbackBuilder)
&#9;local button = Instance.new(&quot;TextButton&quot;, frame)
&#9;button.Position = UDim2.new(.025, 0, 0, y_position)
&#9;button.Text = sourceOrSink.Name
&#9;button.TextXAlignment = Enum.TextXAlignment.Left
&#9;button.Style = Enum.ButtonStyle.Custom
&#9;button.BorderSizePixel = 0
&#9;button.BackgroundTransparency = 1
&#9;button.BackgroundColor3 = Color3.new(0, 0 ,0)
&#9;button.TextColor3 = textColor
&#9;button.Font = Enum.Font.ArialBold
&#9;button.FontSize = Enum.FontSize.Size18
&#9;button.ZIndex = 2
&#9;button.Size = UDim2.new(.95, 0, 0, button.TextBounds.y)
&#9;
&#9;local icon = Instance.new(&quot;ImageLabel&quot;, button)
&#9;icon.Image = iconImage
&#9;icon.ZIndex = 2
&#9;icon.Position = UDim2.new(0, button.TextBounds.x + 10, 0, -8)
&#9;icon.Size = UDim2.new(0, 30, 0 , 30)
&#9;icon.BackgroundTransparency = 1
&#9;button.MouseEnter:connect(function()
&#9;&#9;applyToConnectorsWires(sourceOrSink, function(wire)
&#9;&#9;&#9;wire.Texture = WIRE_TEXTURE
&#9;&#9;&#9;wire.Velocity = 2
&#9;&#9;end)
&#9;&#9;button.BackgroundTransparency = 0
&#9;&#9;button.TextColor3 = BUTTON_HOVER_TEXT_COLOR
&#9;&#9;icon.Image = iconHoverImage
&#9;end)
&#9;local leaveCallback = function()
&#9;&#9;applyToConnectorsWires(sourceOrSink, function(wire)
&#9;&#9;&#9;wire.Texture = &quot;&quot;
&#9;&#9;end)
&#9;&#9;button.BackgroundTransparency = 1
&#9;&#9;button.TextColor3 = textColor
&#9;&#9;icon.Image = iconImage
&#9;end
&#9;button.MouseLeave:connect(leaveCallback)
&#9;button.MouseButton1Click:connect(function()
&#9;&#9;callbackBuilder(sourceOrSink)()
&#9;&#9;leaveCallback()
&#9;end)
&#9;return button
end
function makeDisconnectCallback(source, sink)
&#9;clearHover()
&#9;sink.Source = nil
&#9;if WIRE_LASSO_MAP[source] ~= nil then
&#9;&#9;lassoUi = WIRE_LASSO_MAP[source][sink]
&#9;&#9;if lassoUi ~= nil then
&#9;&#9;&#9;lassoUi:Remove()
&#9;&#9;&#9;WIRE_LASSO_MAP[source][sink] = nil
&#9;&#9;end
&#9;end
end
function makeDisconnectButton(y_position, frame, localConnector, foreignConnector, textColor, iconImage)
&#9;local source = nil
&#9;local sink = nil
&#9;if localConnector:IsA(&quot;CustomEvent&quot;) then
&#9;&#9;source = localConnector
&#9;&#9;sink = foreignConnector
&#9;else
&#9;&#9;source = foreignConnector
&#9;&#9;sink = localConnector
&#9;end
&#9;local button = Instance.new(&quot;TextButton&quot;, frame)
&#9;button.Position = UDim2.new(0, 17, 0, y_position)
&#9;button.Text = foreignConnector.Name .. &quot; (&quot; .. foreignConnector.Parent.Name .. &quot;)&quot;
&#9;button.TextXAlignment = Enum.TextXAlignment.Left
&#9;button.Style = Enum.ButtonStyle.Custom
&#9;button.BackgroundTransparency = 1
&#9;button.BackgroundColor3 = Color3.new(0, 0 ,0)
&#9;button.TextColor3 = textColor
&#9;button.BorderSizePixel = 0
&#9;button.Font = Enum.Font.Arial
&#9;button.FontSize = Enum.FontSize.Size18
&#9;button.ZIndex = 3
&#9;button.Size = UDim2.new(.95, -10, 0, button.TextBounds.y + 2)
&#9;
&#9;local icon = Instance.new(&quot;ImageLabel&quot;, button)
&#9;icon.Image = iconImage
&#9;icon.Parent = button
&#9;icon.Position = UDim2.new(0, button.TextBounds.x + 10, 0, 2)
&#9;icon.Size = UDim2.new(0, 15, 0 , 15)
&#9;icon.BackgroundTransparency = 1
&#9;local buttonCons = {}
&#9;table.insert(buttonCons,
&#9;&#9;button.MouseButton1Click:connect(function()
&#9;&#9;&#9;makeDisconnectCallback(source, sink)
&#9;&#9;end)
&#9;)
&#9;table.insert(buttonCons,
&#9;&#9;button.MouseEnter:connect(function()
&#9;&#9;&#9;button.BackgroundTransparency = 0
&#9;&#9;&#9;button.TextColor3 = BUTTON_HOVER_TEXT_COLOR
&#9;&#9;&#9;icon.Image = DISCONNECT_ICON_HOVER_TEXTURE
&#9;&#9;&#9;WIRE_LASSO_MAP[source][sink].Color = BrickColor.new(&quot;Really red&quot;)
&#9;&#9;end)
&#9;)
&#9;table.insert(buttonCons,
&#9;&#9;button.MouseLeave:connect(function()
&#9;&#9;button.BackgroundTransparency = 1
&#9;&#9;button.TextColor3 = textColor
&#9;&#9;icon.Image = iconImage
&#9;&#9;WIRE_LASSO_MAP[source][sink].Color = BrickColor.new(&quot;Really black&quot;)
&#9;end)
&#9;)
&#9;table.insert(buttonCons,
&#9;&#9;button.AncestryChanged:connect(function(child,parent)
&#9;&#9;&#9;if parent == nil then
&#9;&#9;&#9;&#9;for i = 1, #buttonCons do
&#9;&#9;&#9;&#9;&#9;buttonCons[i]:disconnect()
&#9;&#9;&#9;&#9;end
&#9;&#9;&#9;end
&#9;&#9;end)
&#9;)
&#9;return button
end
function getPartSourcesAndSinks(part, sources,sinks)
&#9;for idx, child in ipairs(part:GetChildren()) do
&#9;&#9;if child:IsA(&quot;CustomEvent&quot;) then
&#9;&#9;&#9;table.insert(sources, child)
&#9;&#9;elseif child:IsA(&quot;CustomEventReceiver&quot;) then
&#9;&#9;&#9;table.insert(sinks, child)
&#9;&#9;end
&#9;end
end
function getSourcesAndSinks(instance,sources,sinks)
&#9;if instance:IsA(&quot;BasePart&quot;) then
&#9;&#9;getPartSourcesAndSinks(instance, sources,sinks)
&#9;elseif instance:IsA(&quot;Model&quot;) then
&#9;&#9;local modelChildren = instance:GetChildren()
&#9;&#9;for i = 1, #modelChildren do
&#9;&#9;&#9;if modelChildren[i]:IsA(&quot;BasePart&quot;) then
&#9;&#9;&#9;&#9;getPartSourcesAndSinks(modelChildren[i],sources,sinks)
&#9;&#9;&#9;elseif modelChildren[i]:IsA(&quot;Model&quot;) then
&#9;&#9;&#9;&#9;getSourcesAndSinks(modelChildren[i],sources,sinks)
&#9;&#9;&#9;end
&#9;&#9;end
&#9;end
end
local function findFirstConnector(node)
&#9;if node:IsA(&quot;BasePart&quot;) then
&#9;&#9;for idx, child in ipairs(node:GetChildren()) do
&#9;&#9;&#9;if connector == nil and (child:IsA(&quot;CustomEvent&quot;) or child:IsA(&quot;CustomEventReceiver&quot;)) then
&#9;&#9;&#9;&#9;return child
&#9;&#9;&#9;end
&#9;&#9;end
&#9;else
&#9;&#9;local children = node:GetChildren()
&#9;&#9;if #children == 0 then
&#9;&#9;&#9;return nil
&#9;&#9;end
&#9;&#9;for i = 1, #children do
&#9;&#9;&#9;local subConnector = findFirstConnector(children[i])
&#9;&#9;&#9;if subConnector then return subConnector end
&#9;&#9;end
&#9;end
end
function buildScreenPanel(part, x, y)
&#9;sources = {}
&#9;sinks = {}
&#9;getSourcesAndSinks(part,sources,sinks)
&#9;local gui = nil
&#9;if not gui then
&#9;&#9;local mouseFrame = nil
&#9;&#9;if USE_BILLBOARD_GUI then
&#9;&#9;&#9;gui = Instance.new(&quot;BillboardGui&quot;, getPlayerGui())
&#9;&#9;&#9;gui.Name = &quot;WiringGui&quot;
&#9;&#9;&#9;gui.StudsOffset = Vector3.new(0, 1.5, 0)
&#9;&#9;&#9;gui.ExtentsOffset = Vector3.new(0,0, 0)
&#9;&#9;&#9;gui.Adornee = part
&#9;&#9;&#9;gui.Active = true
&#9;&#9;&#9;gui.AlwaysOnTop = true
&#9;&#9;else
&#9;&#9;&#9;gui = Instance.new(&quot;ScreenGui&quot;, getPlayerGui())
&#9;&#9;end
&#9;end
&#9;local frame = Instance.new(&quot;Frame&quot;, gui)
&#9;frame.Style = Enum.FrameStyle.RobloxRound
&#9;frame.ZIndex = 1
&#9;frame.Active = true
&#9;local maxWidth = 0
&#9;local y_position = 5
&#9;if SELECTED_SOURCE == nil then
&#9;&#9;for idx, source in ipairs(sources) do
&#9;&#9;&#9;local button = makeControlButton(y_position, frame, source, SOURCE_BUTTON_TEXT_COLOR,
&#9;&#9;&#9;&#9;&#9;SOURCE_BUTTON_ICON_TEXTURE, SOURCE_BUTTON_ICON_HOVER_TEXTURE,
&#9;&#9;&#9;&#9;&#9;makeSourceConnectCallback)
&#9;&#9;&#9;maxWidth = math.max(1.25 * (button.TextBounds.x + BUTTON_ICON_WIDTH), maxWidth)
&#9;&#9;&#9;y_position = y_position + button.TextBounds.y
&#9;&#9;&#9;receivers = source:GetAttachedReceivers()
&#9;&#9;&#9;for sub_idx, receiver in ipairs(receivers) do
&#9;&#9;&#9;&#9;y_position = y_position + 2
&#9;&#9;&#9;&#9;addWireUiIfNotAlreadyThere(source, receiver)
&#9;&#9;&#9;&#9;local button = makeDisconnectButton(y_position, frame, source, receiver,
&#9;&#9;&#9;&#9;&#9;SINK_BUTTON_TEXT_COLOR, DISCONNECT_SINK_ICON_TEXTURE)
&#9;&#9;&#9;&#9;y_position = y_position + button.TextBounds.y
&#9;&#9;&#9;&#9;maxWidth = math.max(1.15 * (button.TextBounds.x + 17 + 25), maxWidth)
&#9;&#9;&#9;end
&#9;&#9;&#9;y_position = y_position + 5
&#9;&#9;end
&#9;end
&#9;if SELECTED_SINK == nil then
&#9;&#9;for idx, sink in ipairs(sinks) do
&#9;&#9;&#9;local button = makeControlButton(y_position, frame, sink, SINK_BUTTON_TEXT_COLOR,
&#9;&#9;&#9;&#9;&#9;SINK_BUTTON_ICON_TEXTURE, SINK_BUTTON_ICON_HOVER_TEXTURE,
&#9;&#9;&#9;&#9;&#9;makeSinkConnectCallback)
&#9;&#9;&#9;maxWidth = math.max(1.25 * (button.TextBounds.x + BUTTON_ICON_WIDTH), maxWidth)
&#9;&#9;&#9;y_position = y_position + button.TextBounds.y
&#9;&#9;&#9;local sender = sink.Source
&#9;&#9;&#9;if sender ~= nil then
&#9;&#9;&#9;&#9;y_position = y_position + 2
&#9;&#9;&#9;&#9;-- addWire takes source first
&#9;&#9;&#9;&#9;addWireUiIfNotAlreadyThere(sender, sink)
&#9;&#9;&#9;&#9;local button = makeDisconnectButton(y_position, frame, sink, sender,
&#9;&#9;&#9;&#9;&#9;SOURCE_BUTTON_TEXT_COLOR, DISCONNECT_SOURCE_ICON_TEXTURE)
&#9;&#9;&#9;&#9;y_position = y_position + button.TextBounds.y
&#9;&#9;&#9;&#9;maxWidth = math.max(1.15 * (button.TextBounds.x + 17 + 25), maxWidth)
&#9;&#9;&#9;end
&#9;&#9;&#9;y_position = y_position + 5
&#9;&#9;end
&#9;end
&#9;-- set size and position
&#9;if not getPlayerGui():FindFirstChild(&quot;ScreenGui&quot;) then
&#9;&#9;local screenGui = Instance.new(&quot;ScreenGui&quot;)
&#9;&#9;screenGui.Parent = getPlayerGui()
&#9;end
&#9;local screenSize = getPlayerGui().ScreenGui.AbsoluteSize
&#9;local menuWidth = maxWidth
&#9;local menuHeight = y_position + 17.5
&#9;if USE_BILLBOARD_GUI then
&#9;&#9;
&#9;&#9;local size = Vector3.new(0,0,0)
&#9;&#9;if gui.Adornee:IsA(&quot;BasePart&quot;) then
&#9;&#9;&#9;size = gui.Adornee.Size
&#9;&#9;elseif gui.Adornee:IsA(&quot;Model&quot;) then
&#9;&#9;&#9;size = gui.Adornee:GetModelSize()
&#9;&#9;end
&#9;&#9;local xSize= size.X
&#9;&#9;if size.Y &gt; xSize then
&#9;&#9;&#9;xSize = size.Y
&#9;&#9;end
&#9;&#9;gui.Size = UDim2.new(0, menuWidth,0,menuHeight + 150)
&#9;&#9;gui.SizeOffset = Vector2.new(0, -50.0 / (menuHeight + 150));
&#9;&#9;
&#9;&#9;local tail = Instance.new(&quot;ImageLabel&quot;, frame)
&#9;&#9;tail.Size = UDim2.new(0, 32, 0, 32)
&#9;&#9;tail.Position = UDim2.new(.5, -16, 1, 8)
&#9;&#9;tail.Image = TAIL_TEXTURE
&#9;&#9;tail.BackgroundTransparency = 1
&#9;&#9;tail.Visible = true
&#9;&#9;f = Instance.new(&quot;Frame&quot;, gui)
&#9;&#9;f.Size = UDim2.new(1, 0, 1, 0)
&#9;&#9;f.BackgroundTransparency = 1
&#9;&#9;f.ZIndex = 1
&#9;&#9;f.Active = true
&#9;&#9;b = Instance.new(&quot;TextButton&quot;, f)
&#9;&#9;b.ZIndex = 1
&#9;&#9;b.BackgroundTransparency = 1
&#9;&#9;b.Text = &quot;&quot;
&#9;&#9;b.BorderSizePixel = 0
&#9;&#9;b.Size = UDim2.new(1, 0, 1, 0)
&#9;&#9;b.MouseButton1Click:connect(function()
&#9;&#9;&#9;local foundConnector = findFirstConnector(findModel(LAST_HOVERED_PART))
&#9;&#9;&#9;if foundConnector ~= nil and foundConnector:IsA(&quot;CustomEvent&quot;) then
&#9;&#9;&#9;&#9;makeSourceConnectCallback(foundConnector)()
&#9;&#9;&#9;elseif foundConnector ~= nil and foundConnector:IsA(&quot;CustomEventReceiver&quot;) then
&#9;&#9;&#9;&#9;makeSinkConnectCallback(foundConnector)()
&#9;&#9;&#9;end
&#9;&#9;end)
&#9;else
&#9;&#9;x = math.min(x - 9, screenSize.x - menuWidth)
&#9;&#9;y = math.min(y - 9, screenSize.y - menuHeight)
&#9;&#9;frame.Position = UDim2.new(0, x, 0, y)
&#9;end
&#9;frame.Size = UDim2.new(0, menuWidth, 0, menuHeight)
&#9;frame.Position = UDim2.new(0.5,-menuWidth/2,0.05,0)
&#9;WIRING_PANEL_MAP[part] = gui
end
function inBaseplate(instance)
&#9;if instance == STATIC_BASE_PLATE then return true end
&#9;local instanceCopy = instance
&#9;while instanceCopy and (instanceCopy.Parent ~= nil or instanceCopy.Parent ~= game.Workspace) do
&#9;&#9;if instanceCopy.Parent == STATIC_BASE_PLATE then
&#9;&#9;&#9;return true
&#9;&#9;end
&#9;&#9;instanceCopy = instanceCopy.Parent
&#9;end
&#9;return false
end
--------------------------------------------------------------------------------
-- Tool.Equipped/Unequipped
Tool.Equipped:connect(function(mouse)
&#9;local player = game.Players:getPlayerFromCharacter(Tool.Parent)
&#9;if not player then return end
&#9;if playerOwner.Value and playerOwner.Value ~= player then return end
&#9;playerOwner.Value = player
&#9;playerGui = getPlayerGui()
&#9;LAST_HOVERED_PART = nil
&#9;if isRestricted then
&#9;&#9;local theBumps = game.Workspace:FindFirstChild(&quot;BaseplateBumpers&quot;, true)
&#9;&#9;mouse.TargetFilter = game.Workspace:FindFirstChild(&quot;BaseplateBumpers&quot;, true)
&#9;&#9;root = findMyBasePlate()
&#9;else
&#9;&#9;root = game.Workspace
&#9;end
&#9;local interactiveCount = setUpConfigurationService()
&#9;if not interactiveCount or interactiveCount == 0 then
&#9;&#9;warnNoWireableParts()
&#9;end
&#9;getLocalLasso().Texture = WIRE_TEXTURE
&#9;getLocalLasso().WireRadius = ENHANCED_WIRE_RADIUS
&#9;clearSelection()
&#9;mouse.Icon = &quot;http://www.roblox.com/asset?id=66887773&quot;
&#9;mouseMoveCon = mouse.Move:connect(function() hoverListener(mouse) end)
&#9;mouseButtonDownCon = mouse.Button1Down:connect(function()
&#9;&#9;if LAST_HOVERED_PART ~= nil then return end
&#9;&#9;clearSelection()
&#9;&#9;clearScreenMessage()
&#9;&#9;clearHover()
&#9;&#9;local annotationCount = 0
&#9;&#9;for part, annotation in pairs(ANNOTATIONS) do
&#9;&#9;&#9;annotation.Transparency = BASE_ANNOTATION_TRANSPARENCY
&#9;&#9;&#9;annotationCount = annotationCount + 1
&#9;&#9;end
&#9;&#9;if annotationCount == 0 then
&#9;&#9;&#9;warnNoWireableParts()
&#9;&#9;elseif time() - LAST_CLICK_TIME &lt; CLICK_HELP_TIME_DELTA then
&#9;&#9;&#9;warnNotClickingWireablePart()
&#9;&#9;end
&#9;&#9;LAST_CLICK_TIME = time()
&#9;end)
&#9;-- TODO: onkeydown/onmouse2down, prevent hover from triggering
&#9;-- until the up event comes
end)
Tool.Unequipped:connect(function()
&#9;playerGui = getPlayerGui()
&#9;destroyConfigurationService()
&#9;if mouseMoveCon then mouseMoveCon:disconnect() end
&#9;if mouseButtonDownCon then mouseButtonDownCon:disconnect() end
&#9;if playerGui:FindFirstChild(&quot;CenterHint&quot;, true) then
&#9;&#9;local centerHint = getPlayerGui().Gui.Hints.CenterHint
&#9;&#9;centerHint.Delete.Disabled = false
&#9;end
&#9;-- TODO: simplify these side effects
&#9;-- call clearHover before removing annotations, because
&#9;-- clear hover resets annotation boxes. Also before clearing
&#9;-- lassos because this may create lassos
&#9;clearHover()
&#9;for part, gui in pairs(WIRING_PANEL_MAP) do
&#9;&#9;if gui then gui:Destroy() end
&#9;end
&#9;WIRING_PANEL_MAP = {}
&#9;for source, submap in pairs(WIRE_LASSO_MAP) do
&#9;&#9;for sink, wire in pairs(submap) do
&#9;&#9;&#9;wire:Destroy()
&#9;&#9;end
&#9;end
&#9;WIRE_LASSO_MAP = {}
&#9;for k,adorneeTable in pairs(adornmentTable) do
&#9;&#9;for i = 1, #adorneeTable do
&#9;&#9;&#9;adorneeTable[i]:Destroy()
&#9;&#9;end
&#9;&#9;adorneeTable = nil
&#9;end
&#9;adornmentTable = {}
&#9;ANNOTATIONS = {}
&#9;KNOWN_SOURCE_PARTS = {}
&#9;KNOWN_SINK_PARTS = {}
&#9;clearSelection()
&#9;clearScreenMessage()
&#9;LAST_HOVERED_PART = nil
end)
function findBillboard(guiTable)
&#9;if not guiTable then return end
&#9;for i = 1, #guiTable do
&#9;&#9;if guiTable[i]:IsA(&quot;BillboardGui&quot;) then
&#9;&#9;&#9;return guiTable[i]
&#9;&#9;end
&#9;end
end
function getBillboard(adornee, parent)
&#9;local billboard = findBillboard(adornmentTable[adornee])
&#9;if not billboard and parent then
&#9;&#9;local screen = Instance.new(&quot;BillboardGui&quot;)
&#9;&#9;screen.Name = adornee.Name .. &quot;BadgeGUI&quot;
&#9;&#9;screen.Size = UDim2.new(1.5,0,1.5,0)
&#9;&#9;screen.Enabled = true
&#9;&#9;screen.Active = true
&#9;&#9;screen.AlwaysOnTop = true
&#9;&#9;screen.ExtentsOffset = Vector3.new(0,0,0)
&#9;&#9;screen.Adornee = adornee
&#9;&#9;screen.Parent = parent
&#9;&#9;local badgeFrame = Instance.new(&quot;Frame&quot;)
&#9;&#9;badgeFrame.Name = &quot;BadgeFrame&quot;
&#9;&#9;badgeFrame.Size = UDim2.new(2,0,1,0)
&#9;&#9;badgeFrame.Position = UDim2.new(-0.5,0,0,0)
&#9;&#9;badgeFrame.BackgroundTransparency = 1
&#9;&#9;badgeFrame.Parent = screen
&#9;&#9;table.insert(adornmentTable[adornee],screen)
&#9;&#9;return screen
&#9;end
&#9;return billboard
end
function repositionBadges(badgeFrame)
&#9;local badges = badgeFrame:GetChildren()
&#9;if #badges == 1 then
&#9;&#9;badges[1].Position = UDim2.new(0.25,0,0,0)
&#9;elseif #badges == 2 then
&#9;&#9;badges[1].Position = UDim2.new(0,0,0,0)
&#9;&#9;badges[2].Position = UDim2.new(0.5,0,0)
&#9;end
end
function hasBadge(adornee, type)
&#9;local screen = getBillboard(adornee)
&#9;return screen and screen:FindFirstChild(type .. &quot;Badge&quot;,true)
end
function removeBadge(adornee, type)
&#9;local screen = getBillboard(adornee)
&#9;local badge = screen:FindFirstChild(type .. &quot;Badge&quot;,true)
&#9;if badge then badge:Destroy() end
&#9;if screen then screen:remove() end
end
function createBadge(adornee,type,parent)
&#9;local screen = getBillboard(adornee, parent)
&#9;local wiringBadge = Instance.new(&quot;ImageLabel&quot;)
&#9;wiringBadge.Name = type .. &quot;Badge&quot;
&#9;wiringBadge.BackgroundTransparency = 1
&#9;if type == &quot;Receiver&quot; then
&#9;&#9;wiringBadge.Image = SOURCE_BADGE_TEXTURE
&#9;else
&#9;&#9;wiringBadge.Image = SINK_BADGE_TEXTURE
&#9;end
&#9;wiringBadge.Position = UDim2.new(0.25,0,0,0)
&#9;wiringBadge.Size = UDim2.new(0.5,0,1,0)
&#9;wiringBadge.Parent = screen.BadgeFrame
&#9;wiringBadge.Changed:connect(function(prop)
&#9;&#9;if prop == &quot;AbsoluteSize&quot; then
&#9;&#9;&#9;if wiringBadge.AbsoluteSize.X &lt; 10 then
&#9;&#9;&#9;&#9;wiringBadge.Visible = false
&#9;&#9;&#9;else
&#9;&#9;&#9;&#9;wiringBadge.Visible = true
&#9;&#9;&#9;end
&#9;&#9;end
&#9;end)
&#9;repositionBadges(screen.BadgeFrame)
end
function upAdorneeCount(adornee,type)
&#9;local typeLower = string.lower(type)
&#9;if typeLower == &quot;receiver&quot; then
&#9;&#9;if not receiverBadgeCount[adornee] then
&#9;&#9;&#9;receiverBadgeCount[adornee] = 1
&#9;&#9;else
&#9;&#9;&#9;receiverBadgeCount[adornee] = receiverBadgeCount[adornee] + 1
&#9;&#9;end
&#9;elseif typeLower == &quot;event&quot; then
&#9;&#9;if not eventBadgeCount[adornee] then
&#9;&#9;&#9;eventBadgeCount[adornee] = 1
&#9;&#9;else
&#9;&#9;&#9;eventBadgeCount[adornee] = eventBadgeCount[adornee] + 1
&#9;&#9;end
&#9;end
end
function downAdorneeCount(adornee,type)
&#9;local typeLower = string.lower(type)
&#9;if typeLower == &quot;receiver&quot; then
&#9;&#9;if receiverBadgeCount[adornee] then
&#9;&#9;&#9;receiverBadgeCount[adornee] = receiverBadgeCount[adornee] - 1
&#9;&#9;&#9;if receiverBadgeCount[adornee] &lt; 1 then
&#9;&#9;&#9;&#9;receiverBadgeCount[adornee] = nil
&#9;&#9;&#9;end
&#9;&#9;end
&#9;elseif typeLower == &quot;event&quot; then
&#9;&#9;if eventBadgeCount[adornee] then
&#9;&#9;&#9;eventBadgeCount[adornee] = eventBadgeCount[adornee] - 1
&#9;&#9;&#9;if eventBadgeCount[adornee] &lt; 1 then
&#9;&#9;&#9;&#9;eventBadgeCount[adornee] = nil
&#9;&#9;&#9;end
&#9;&#9;end
&#9;end
end
function createAdornment(adornee,adornColor,type)
&#9;upAdorneeCount(adornee,type)
&#9;if receiverBadgeCount[adornee] == 1 or eventBadgeCount[adornee] == 1 then
&#9;&#9;local box = Instance.new(&quot;SelectionBox&quot;)
&#9;&#9;box.Color = adornColor
&#9;&#9;box.Name = adornee.Name .. &quot;Selection&quot; .. tostring(type)
&#9;&#9;box.Adornee = adornee
&#9;&#9;box.Transparency = 0.5
&#9;&#9;box.Parent = game.Players.LocalPlayer.PlayerGui
&#9;&#9;ANNOTATIONS[adornee] = box
&#9;&#9;if not adornmentTable[adornee] then
&#9;&#9;&#9;adornmentTable[adornee] = {}
&#9;&#9;end
&#9;&#9;table.insert(adornmentTable[adornee],box)
&#9;&#9;if not hasBadge(adornee,type) then
&#9;&#9;&#9;createBadge(adornee, type, box)
&#9;&#9;end
&#9;end
end
function doRemoveAdornment(adornee)
&#9;if not adornmentTable[adornee] then return end
&#9;local adorneeTable = adornmentTable[adornee]
&#9;for i = 1, #adorneeTable do
&#9;&#9;adorneeTable[i]:Destroy()
&#9;end
end
function removeAdornment(adornee, type)
&#9;downAdorneeCount(adornee,type)
&#9;if type == &quot;Receiver&quot; then
&#9;&#9;if not receiverBadgeCount[adornee] then
&#9;&#9;&#9;doRemoveAdornment(adornee)
&#9;&#9;end
&#9;elseif type == &quot;Event&quot; then
&#9;&#9;if not eventBadgeCount[adornee] then
&#9;&#9;&#9;doRemoveAdornment(adornee)
&#9;&#9;end
&#9;end
end
function eventReceiverAdded(receiver,wirePartCount)
&#9;if isRestricted then
&#9;&#9;if not inBaseplate(receiver) then return wirePartCount end
&#9;end
&#9;receiverTable[receiver] = findModel(receiver.Parent)
&#9;createAdornment(receiverTable[receiver], BrickColor.new(&quot;Lime green&quot;), &quot;Receiver&quot;)
&#9;setPartWireTransparency(receiver.Parent, BASE_WIRE_TRANSPARENCY, BASE_WIRE_RADIUS, &quot;&quot;)
&#9;KNOWN_SINK_PARTS[receiver.Parent] = true
&#9;KNOWN_SINK_PARTS[receiverTable[receiver]]= true
&#9;if wirePartCount then
&#9;&#9;return wirePartCount + 1
&#9;else
&#9;&#9;return 0
&#9;end
end
function eventAdded(event,wirePartCount)
&#9;if isRestricted then
&#9;&#9;if not inBaseplate(event) then return wirePartCount end
&#9;end
&#9;eventTable[event] = findModel(event.Parent)
&#9;createAdornment(eventTable[event], BrickColor.new(&quot;Bright orange&quot;), &quot;Event&quot;)
&#9;setPartWireTransparency(event.Parent, BASE_WIRE_TRANSPARENCY, BASE_WIRE_RADIUS, &quot;&quot;)
&#9;KNOWN_SOURCE_PARTS[event.Parent] = true
&#9;KNOWN_SOURCE_PARTS[eventTable[event]]= true
&#9;if wirePartCount then
&#9;&#9;return wirePartCount + 1
&#9;else
&#9;&#9;return 0
&#9;end
end
function eventReceiverRemoved(receiver)
&#9;if not receiverTable[receiver] then return end
&#9;KNOWN_SINK_PARTS[receiver.Parent] = false
&#9;KNOWN_SINK_PARTS[receiverTable[receiver]]= false
&#9;removeAdornment(receiverTable[receiver],&quot;Receiver&quot;)
&#9;receiverTable[receiver] = nil
end
function eventRemoved(event)
&#9;if not eventTable[event] then return end
&#9;KNOWN_SOURCE_PARTS[event.Parent] = false
&#9;KNOWN_SOURCE_PARTS[eventTable[event]]= false
&#9;removeAdornment(eventTable[event], &quot;Event&quot;)
&#9;eventTable[event] = nil
end
function setUpConfigurationService()
&#9;local wirePartCount = 0
&#9;ServiceConnections = {}
&#9;local collectionService = game:GetService(&quot;CollectionService&quot;)
&#9;-- first lets check if anything already exists
&#9;local receivers = collectionService:GetCollection(&quot;CustomEventReceiver&quot;)
&#9;if receivers then
&#9;&#9;for pos, receiver in pairs(receivers) do
&#9;&#9;&#9;wirePartCount = eventReceiverAdded(receiver, wirePartCount)
&#9;&#9;end
&#9;end
&#9;local events = collectionService:GetCollection(&quot;CustomEvent&quot;)
&#9;if events then
&#9;&#9;for pos, event in pairs(events) do
&#9;&#9;&#9;wirePartCount = eventAdded(event, wirePartCount)
&#9;&#9;end
&#9;end
&#9;-- Now lets listen for any future additions/removals
&#9;table.insert(ServiceConnections, collectionService.ItemAdded:connect(function(instance)
&#9;&#9;if instance:IsA(&quot;CustomEventReceiver&quot;) then
&#9;&#9;&#9;eventReceiverAdded(instance)
&#9;&#9;elseif instance:IsA(&quot;CustomEvent&quot;) then
&#9;&#9;&#9;eventAdded(instance)
&#9;&#9;end
&#9;end))
&#9;table.insert(ServiceConnections, collectionService.ItemRemoved:connect(function(instance)
&#9;&#9;if instance:IsA(&quot;CustomEventReceiver&quot;) then
&#9;&#9;&#9;eventReceiverRemoved(instance)
&#9;&#9;elseif instance:IsA(&quot;CustomEvent&quot;) then
&#9;&#9;&#9;eventRemoved(instance)
&#9;&#9;end
&#9;end))
&#9;return wirePartCount
end
function destroyConfigurationService()
&#9;-- first lets destroy the collection service
&#9;for index, connection in pairs(ServiceConnections) do
&#9;&#9;connection:disconnect()
&#9;end
&#9;ServiceConnections = {}
&#9;-- now lets remove all of our collection service objects that were generated
&#9;for event, object in pairs(eventTable) do
&#9;&#9;eventRemoved(event)
&#9;end
&#9;eventTable = {}
&#9;for eventReceiver, object in pairs(receiverTable) do
&#9;&#9;eventReceiverRemoved(eventReceiver)
&#9;end
&#9;receiverTable = {}
end
</ProtectedString>
</Properties>
</Item>
<Item class="ObjectValue" referent="RBX4">
<Properties>
<string name="Name">PlayerOwner</string>
<Ref name="Value">null</Ref>
</Properties>
</Item>
<Item class="Script" referent="RBX5">
<Properties>
<bool name="Disabled">false</bool>
<Content name="LinkedSource"><null></null></Content>
<string name="Name">DestroyScript</string>
<ProtectedString name="Source">local destroyObject = function(object)
&#9;if object and object.Value then
&#9;&#9;object.Value:Destroy()
&#9;&#9;object:Destroy()
&#9;end
end
script.ChildAdded:connect(function(child)
&#9;if child:IsA(&quot;ObjectValue&quot;) and child.Name == &quot;ObjectToDestroy&quot; then
&#9;&#9;destroyObject(child)
&#9;end
end)</ProtectedString>
</Properties>
<Item class="ObjectValue" referent="RBX6">
<Properties>
<string name="Name">ObjectToDestroy</string>
<Ref name="Value">null</Ref>
</Properties>
</Item>
</Item>
</Item>
</roblox>