local module = {} _G.Methods = module local tags = _G.Tags local saveToPartTranslator = { Barrel = "$B", Sheet = "$S", Stick = "$s" } local partToSaveTranslator = { } local ids = require(_G.Modules.IdModule) module.Ids = ids local mouse = require(_G.Modules.MouseModule) module.Mouse = mouse local buildParts = {} local idParts = {} local idNodes = {} local polyParts = {} module.canLoad = false module.polyParts = polyParts module.buildParts = buildParts module.idParts = idParts task.spawn(function() require(game.ReplicatedFirst.ReadyModule)("Parts") _G.Storage:WaitForChild("Parts") for _,object in pairs(_G.Storage.Parts:GetDescendants()) do if object.Parent:IsA("Folder") then local id = object:GetAttribute("PartId") if not id then continue end if not object:HasTag("ConnectorObject") then buildParts[object.Name] = object if saveToPartTranslator[object.Name] then buildParts[saveToPartTranslator[object.Name]] = object end idParts[id] = object else if object:GetAttribute("Nodes") == 3 then if not object:FindFirstChild("A") then warn(tostring(object).." missing sub-wedge 'A'") end if not object:FindFirstChild("B") then warn(tostring(object).." missing sub-wedge 'B'") end end idParts[id] = object polyParts[id] = object polyParts[object.Name] = object --[[if object:FindFirstChild("A") and object:FindFirstChild("B") then polyParts[object.Name] = object polyParts[id] = object elseif object:FindFirstChild("1") and object:FindFirstChild("2") then idParts[id] = object else polyParts[object.Name] = object polyParts[id] = object idParts[id] = object end]] end end end -- To fix old compatibility issues local polyCompat = { Pane = 17, Wedge = 6, Stick = 5, Paper = 20, HeavySheet = 10, Bar = 13, LightSheet = 14, Rod = 13, Rope = 22 } for index,item in pairs(polyCompat) do --[[local part = idParts[item]:Clone() part.Parent = idParts[item].Parent if part:FindFirstChild("1") then part["1"].Name = "A" end if part:FindFirstChild("2") then part["2"].Name = "B" end]] polyParts[index] = polyParts[item] end module.canLoad = true end) for id,part in pairs(idParts) do local nodeId = part:GetAttribute("NodeId") if nodeId then idNodes[id] = idParts[nodeId] end end function module.getPartByName(name) return buildParts[name] end function module.getPolyByName(name) return polyParts[name] end function module.getPartById(id) return idParts[id] end function module.getNodeById(id) return idNodes[id] end function module.setItemCFrame(item,cframe) if item:IsA("BasePart") then item.CFrame = cframe elseif item:IsA("Model") then if item.PrimaryPart then local mcframe,size = item:GetBoundingBox() item:SetPrimaryPartCFrame(cframe-cframe.Position+cframe:PointToWorldSpace(mcframe:PointToObjectSpace(item.PrimaryPart.Position))) end end return cframe end function module.getItemSize(item) if item:IsA("BasePart") then return item.Size elseif item:IsA("Model") then if item.PrimaryPart then return item:GetExtentsSize() end end end function module.getItemCFrame(item) if item:IsA("BasePart") then return item.CFrame elseif item:IsA("Model") then return item:GetBoundingBox() end end function module.colorPart(part,color) if part:IsA("Model") then for _,subpart in pairs(part:GetDescendants()) do if subpart:IsA("BasePart") then subpart.Color = color end end else part.Color = color end part:SetAttribute("Color",color) end function module.shapeTriangle(p1,p2,p3,r1,r2) -- simple function, might work? local s = 0 --0.01 local bottom = { b1 = nil, b2 = nil, dist = 0, other = nil } local function check(point1,point2,other) local dist = (point1 - point2).Magnitude if dist > bottom.dist then bottom.b1 = point1 bottom.b2 = point2 bottom.other = other bottom.dist = dist end end check(p1,p2,p3) -- check all of the different options for bottom and get the right one? check(p1,p3,p2) check(p2,p3,p1) local view = CFrame.lookAt(bottom.b1,bottom.b2,(bottom.other-bottom.b1).Unit) local object = view:PointToObjectSpace(bottom.other) local length = object.Z local height = object.Y r1.Size = Vector3.new(r1.Size.X,height-s,-length-s) r1.CFrame = (view * CFrame.Angles(0,math.rad(180),0)) * CFrame.new(0,height/2+s/2,-length/2+s/2) r2.Size = Vector3.new(r2.Size.X,height-s,-(-bottom.dist-length)-s) r2.CFrame = view * CFrame.new(0,height/2+s/2,-(bottom.dist-length)/2+s/2) return r1,r2 end function module.positionPart(part,nodes,s) local d = (nodes[1].Position - nodes[2].Position).Magnitude -- distance local a = nodes[1].Position -- first node local b = nodes[2].Position -- second node if #nodes == 2 then -- stick on Z (XY thick) part.CFrame = CFrame.lookAt(a,b) * CFrame.Angles(0,math.pi*0.5,0) part.CFrame = part.CFrame + part.CFrame.RightVector * d * 0.5 part.Size = Vector3.new(d,part.Size.Y,part.Size.Z) end if #nodes == 3 then -- triangle on YZ (X thick) local c = nodes[3].Position -- third node local p2 = part["B"] local p1 = part["A"] module.shapeTriangle(a,b,c,p1,p2) end end function module.shiftPart(part,shift) shift = shift - (part:GetAttribute("Shift") or 0) for _,part in pairs(part:GetChildren()) do if part:IsA("BasePart") then part.Position = part.Position + part.CFrame.RightVector * shift end end end function module.getNodePositions(part) end function module.form_rod(pa,p1,p2) local d = (p1 - p2).Magnitude -- distance pa.CFrame = CFrame.lookAt(p1,p2) * CFrame.Angles(0,math.pi*0.5,0) pa.CFrame = pa.CFrame + pa.CFrame.RightVector * d * 0.5 pa.Size = Vector3.new(d,pa.Size.Y,pa.Size.Z) end module.saves = require(script.Parent:WaitForChild("SerialisationModule")) spawn(function() require(game.ReplicatedFirst.ReadyModule)("MethodCommands") if _G.IsClient then local commands = require(_G.Modules.CommandsModule) commands.new("save format (%d)","set save format : Sets current save version. Use '4'",function(v) _G.Events.Notification:Fire("Set Save Format","Set save format to: "..v) _G.SaveFormat = v end) commands.new("save comp[^%s]- (.+)","set save compress : Enables or disables save compression for debugging. A bad idea.",function(v) v = not not v:match("t") _G.Events.Notification:Fire("Set Save Compress","Set save compress to: "..tostring(v)) _G.SaveCompression = v end) end end) function module.saves.modelToData(root) if root then if module.saves[_G.SaveFormat] then local success,result = xpcall(module.saves[_G.SaveFormat].encode,function(err) print("before") _G.Error(string.format("Save V%s Error: %s",_G.SaveFormat,err)) end,root,{ compress = _G.SaveCompression }) if not success then return nil end if #result.errors > 0 then print(result.errors) end return result.data else return _G.Methods.dataToString(_G.Methods.modelToTable(root)) end else error("Root passed is nil!",2) end end function module.saves.dataToModel(raw,physics,mirror) if _G.IsString(raw) then local v,r = raw:match("v(%d)save(.+)") if v then local success,result = xpcall(module.saves[v].decode,function(err) warn(debug.traceback(err)) end,r,{ physics = physics, mirror = mirror, }) if not success then return nil end if #result.errors > 0 then print(result.errors) end if result.model then result.model:SetAttribute("Version","V"..v) end return result.model else local tabular = _G.Protected(module.universalConvertToTable,raw) if not tabular or not tabular[1] then return nil end local model = _G.Protected(module.convertToModel,tabular,physics) if not model then return nil end if not tabular[1]:match("V%dSAVE") then tabular.Name = tabular[1] --model:AddTag("Old") end model:AddTag("Old") return model end else return nil end end function module.tableToModel(data,physics) end function module.stringToData(str) if type(str) ~= "string" then error("Not a string",2) end local insert = table.insert if str:sub(1,1) ~= "{" then return end local function parse(start) local data = {} local function register(str) if #str < 1 then return end local index,part = str:match("(.*)=(.*)") if not index then insert(data,str) else data[index] = str end end while start do local first,last = str:find("[{},]",start+1,false) local c = str:sub(last,last) if c == "{" then local sub_data local index = str:sub(start+1,last-2) sub_data,start = parse(last) if #index > 0 then data[index] = sub_data else insert(data,sub_data) end elseif c == "}" then register(str:sub(start+1,last-1)) return data,last else if c == "," then register(str:sub(start+1,last-1)) end start = last end end wait() end return parse(1) end function module.can_destroy(part,player) return part:HasTag("Combat") or part.Parent:HasTag("Combat") end local built = {BuiltWeldConstraint = true} function module.break_part(part,player) if part:IsA("Model") then for _,item in pairs(part:GetDescendants()) do if built[item.Name] then item:Destroy() end end elseif part:IsA("BasePart") then part:BreakJoints() end end function module.wash_part(part,colour,material) if part:IsA("Model") then for _,item in pairs(part:GetDescendants()) do if part:IsA("BasePart") then part.Color = colour part.Material = material end end elseif part:IsA("BasePart") then part.Color = colour part.Material = material end end function module.fire(root,player) if module.can_destroy(root,player) then if root:HasTag("Engine") then local fire = _G.Storage.Fire:Clone() fire.Parent = root.PrimaryPart end module.wash_part(root,Color3.new(1, 0.831617, 0.298161),Enum.Material.Neon) wait(math.random(1,10)*0.333) module.break_part(root,player) for _,part in pairs(root:GetTouchingParts()) do module.fire(part,player) end wait(math.random(1,20)*0.333) module.wash_part(root,Color3.new(0,0,0),Enum.Material.Sand) end end function module.explode(root,player,power) print("explode") if root:HasTag("Exploded") then return end root:AddTag("Exploded") local debris = game:GetService("Debris") if not power then power = 10 end local explosion = _G.Sounds.Explosion:Clone() explosion.Parent = root explosion:Destroy() local burn = _G.Storage.Burn:Clone() burn.Parent = root debris:AddItem(burn,1) local params = OverlapParams.new() params.MaxParts = 40 params.RespectCanCollide = true params.FilterType = Enum.RaycastFilterType.Include params.FilterDescendantsInstances = {game.Workspace.Objects} local result = game.Workspace:GetPartBoundsInRadius(root.Position,10,params) wait() for _,hit in pairs(result) do if module.can_destroy(hit,player) then module.break_part(hit,player) if math.random(1,3) == 1 then module.fire(hit,player) end end end wait(0.1) for _,hit in pairs(result) do hit:ApplyImpulse((hit.Position-root.Position)*-100) end end function module.playerOwnsPart(player,part) return part:HasTag("P_"..player.UserId) or part:HasTag("P_ublic") end function module.dataToString(data) local insert = table.insert local isString = {["string"] = true} local isNumber = {["number"] = true} local isTable = {["table"] = true} local function converts(t) local result = {"{"} local first = true for index,item in pairs(t) do if not first then insert(result,",") else first = false end local value if isTable[type(item)] then value = converts(item) else value = tostring(item) end if isNumber[type(index)] then insert(result,value) elseif isString[type(index)] then insert(result,index.."="..value) end end insert(result,"}") return table.concat(result) end return converts(data) end function module.convertToData(data) if not data:match("{.*}") then return end data = data:gsub("%s","") local index = 2 local function parseTable(t) local this = {} -- The table we're working with now local idx -- Special named table index local _,last local char local value while true do _,last = data:find("[{,=}]",index) if not last then return end char = data:sub(last,last) -- The next significant character if index ~= last then value = data:sub(index,last-1) end -- Current value in table index = last + 1 --print(t,value,char) local function terminate() if idx then this[idx] = value -- If there is an index, add it by index else table.insert(this,value) end -- Otherwise just plainly insert it (by order) end if char == "=" then -- When finding an '=', take the existing piece and make it an index idx = value elseif char == "," then -- When finding a ',', the entry has terminated so add it terminate() elseif char == "{" then -- When finding a '{', there is a new table whose value needs to be computed value = parseTable(t+1) else-- char == "}" then -- When finding a '}', this table has terminated so return terminate() return this end end end return parseTable(1) end function module.universalConvertToTable(data) if not (data:match("{V%dSAVE,objects=")) then return module.stringToData(data) else return module.convertToData(data) end end function module.removeSpaces(data) local new = "" for index = 1, #data do local char = string.sub(data,index,index) if char ~= " " then new = new .. char end end return new end function module.round(number,decimals) local pow = math.pow(10,decimals) return math.round(number*pow)/pow end function module.makeSaveString(data) local saveDataString = "{"..data.saveName.."," local saveObjectsString = "{" for index,item in ipairs(data.saveObjects) do local editString = "" local cframeString = module.removeSpaces(tostring(item.CFrame)) if item.Edit then for _,edit in pairs(item.Edit) do editString = editString .. "{"..edit[1]..","..edit[2].."}," end end if #editString ~= 0 then editString = "{"..editString.."}," end saveObjectsString = saveObjectsString .. "{{" .. cframeString .. "}" .. "," .. item.Name .. ","..editString.."}," end saveObjectsString = saveObjectsString .. "}" saveDataString = saveDataString .. saveObjectsString .. "," local saveWeldsString = "{" for index,item in pairs(data.saveWelds) do saveWeldsString = saveWeldsString .. "{{" .. tostring(item.Part0[1]) if item.Part0[2] then saveWeldsString = saveWeldsString .. "," .. item.Part0[2] end saveWeldsString = saveWeldsString .. "},{" .. tostring(item.Part1[1]) if item.Part1[2] then saveWeldsString = saveWeldsString .. "," .. item.Part1[2] end saveWeldsString = saveWeldsString .. "}}," end saveWeldsString = saveWeldsString .. "}" saveDataString = saveDataString .. saveWeldsString saveDataString = saveDataString .. "}" --print(saveDataString) return saveDataString end function module.convertValue(value) -- BRUH if value == "true" then return true elseif value == "false" then return false elseif tonumber(value) then return tonumber(value) else return value end end function module.tableToCFrame(c) local n = tonumber return CFrame.new( n(c[1]),n(c[2]),n(c[3]), n(c[4]),n(c[5]),n(c[6]), n(c[7]),n(c[8]),n(c[9]), n(c[10]),n(c[11]),n(c[12]) ) end local function weld(part1,part2) local weld = Instance.new("WeldConstraint") weld.Name = "BuiltWeldConstraint" weld.Parent = part1 weld.Part0 = part1 weld.Part1 = part2 return weld end local function weldNoPersist(part1,part2) weld(part1,part2).Name = "WeldConstraint" end local CFrameNew = CFrame.new local CFrameFromAxisAngle = CFrame.fromAxisAngle local tableUnpack = table.unpack local Vector3New = Vector3.new local n = tonumber function module.cframeToData(cframe) local p = cframe.Position local r,w = cframe:ToAxisAngle() return {{cframe.Position},{r},w} end local tp = table.pack function module.cframeToDataNoAxis(cframe) return tp(cframe:GetComponents()) end local nc = CFrame.new local tu = table.unpack function module.dataToCFrameNoAxis(data) return nc(tu(data)) end function module.dataToCFrame(data) if not (data[1] and data[2] and data[3]) then return CFrame.new() end local p1,p2,p3 = tableUnpack(data[1]) local r1,r2,r3 = tableUnpack(data[2]) local w = data[3] return CFrameNew(n(p1),n(p2),n(p3))*CFrameFromAxisAngle(Vector3New(n(r1),n(r2),n(r3)),n(w)) end local editCompress = {Color="C",HingeCFrame="HC",Size="S"} local editDepress = {HC="HingeCFrame",C="Color",S="Size"} function module.modelToTable(root) local getItemCFrame = function(object) if object:IsA("BasePart") then return object.CFrame elseif object:IsA("Model") then return object.PrimaryPart.CFrame end end local origin = getItemCFrame(root) local insert = table.insert local saveData = {"V3SAVE"} saveData.objects = {} saveData.welds = {} local objects = {} --[[Agreed object structure: { 1: name 2: {node1,node2,node3} cframes (nodes included) 3: {colour={a,b,c},size={a,b,c},speed=120} edits (size included) } ]] local isBuiltWeld = {["BuiltWeldConstraint"] = true} local index = 1 ids.forEachConnectedObject(root,function(object) insert(objects,object) objects[object] = index index = index + 1 local cframes = {} local edits = {} local nodes = object:GetAttribute("Nodes") if nodes then for i=1,nodes do insert(cframes,module.cframeToDataNoAxis(origin:ToObjectSpace(object[i].CFrame))) end else insert(cframes,module.cframeToDataNoAxis(origin:ToObjectSpace(getItemCFrame(object)))) end -- edits for index,edit in pairs(object:GetChildren()) do if tags:HasTag(edit,"Edit") and (edit:GetAttribute("Default") ~= edit.Value) then edits[editCompress[edit.Name] or edit.Name] = tostring(edit.Value) end end local id = object:GetAttribute("PartId") or object.Name --local original = module.getPartById(id) or module.getPartByName(id) -- psuedo edits local color = object:GetAttribute("Color") if color then edits[editCompress["Color"]] = {color.R,color.G,color.B} end local hinge = object:FindFirstChild("Hinge") if hinge then edits[editCompress["HingeCFrame"]] = module.cframeToDataNoAxis(origin:ToObjectSpace(hinge.CFrame)) end local size = object:GetAttribute("Size") if size then size = size * math.sqrt(0.333); edits[editCompress["Size"]] = {size.X,size.Y,size.Z} end -- final object local final = {id,cframes} if next(edits) then insert(final,edits) end insert(saveData.objects,final) end) for index,object in ipairs(objects) do for _,weld in pairs(object:GetDescendants()) do if isBuiltWeld[weld.Name] then if not weld.Part0 or not weld.Part1 then continue end local first = objects[weld.Part0] if not first then first = {objects[weld.Part0.Parent],weld.Part0.Name} else first = {first} end local second = objects[weld.Part1] if not second then second = {objects[weld.Part1.Parent],weld.Part1.Name} else second = {second} end insert(saveData.welds,{first,second}) end end end return saveData end function module.convertToModel(data,physics) if not data then return end local v = data[1]:match("V(%d)SAVE") if not v or not data.objects then return module.oldConvertToModel(data,physics) end local insert = table.insert local objects = {} local model = Instance.new("Model") local toCFrame = module.dataToCFrameNoAxis local setCFrame = function(part,cframe) if part:IsA("BasePart") then part.CFrame = cframe else part:SetPrimaryPartCFrame(cframe) end end model:SetAttribute("Version","V3") if v == "2" then setCFrame = module.setItemCFrame toCFrame = module.dataToCFrame model:SetAttribute("Version","V2") end local editActions = { Color = function(edit,part) module.colorPart(part,Color3.new(n(edit[1]),n(edit[2]),n(edit[3]))) end, HingeCFrame = function(edit,part) local hinge = part:FindFirstChild("Hinge") if hinge then hinge.CFrame = toCFrame(edit) end end, MaterialAttribute = function() end, Size = function(edit,part) local size = Vector3.new(n(edit[1]),n(edit[2]),n(edit[3])) if part:IsA("Model") then part:ScaleTo(size.Magnitude) size = (size/math.sqrt(0.333)) -- For parity with the new stronger system else part.Size = size end part:SetAttribute("Size",size) end, } repeat wait() until module.canLoad for index,object in ipairs(data.objects) do local name = object[1] name = n(name) or name local isPoly = #object[2] > 1 local part = idParts[name] local node if not part then if isPoly then part = module.getPolyByName(name) if not part then print(name) end node = part.Parent:FindFirstChild("Node") or idParts[-1] else part = module.getPartByName(name) end else if isPoly then node = idNodes[name] or part.Parent:FindFirstChild("Node") or idParts[-1] end end if not part then warn(name,isPoly,idParts,buildParts,polyParts) end part = part:Clone() local isModel = not part:IsA("BasePart") local primary = part if isModel then primary = part.PrimaryPart end if isPoly then local nodes = {} for index,cframe in pairs(object[2]) do local newNode = node:Clone() newNode.CFrame = toCFrame(cframe) newNode.Parent = part newNode.Name = tostring(index) newNode.Transparency = 1 nodes[index] = newNode end module.positionPart(part,nodes) for _,subpart in pairs(part:GetChildren()) do if subpart ~= primary and subpart:IsA("BasePart") then weldNoPersist(subpart,primary) end end else setCFrame(part,toCFrame(object[2][1])) end local cnv = module.convertValue if object[3] then for index,edit in pairs(object[3]) do if editDepress[index] then index = editDepress[index] end local value = part:FindFirstChild(index) if not value then if editActions[index] then editActions[index](edit,part) end else value.Value = cnv(edit) end end end if not physics then if isModel then for _,object in pairs(part:GetChildren()) do if object:IsA("BasePart") then object.Anchored = true object.CanCollide = false end end else part.CanCollide = false part.Anchored = true end end if index == 1 then if part:IsA("Model") then model.PrimaryPart = part.PrimaryPart else model.PrimaryPart = part end end if part.Name == "VehicleSeat" then model.PrimaryPart = part.PrimaryPart end part.Parent = model insert(objects,part) end if physics then for index,weld in pairs(data.welds) do local weld11 = n(weld[1][1]) local weld12 = weld[1][2] local weld21 = n(weld[2][1]) local weld22 = weld[2][2] local part1 = objects[weld11] local part2 = objects[weld21] if weld12 then part1 = part1:FindFirstChild(weld12) or part1 end if weld22 then part2 = part2:FindFirstChild(weld22) or part2 end if not (part1 and part2) then print("Weld Missing",objects[weld11],objects[weld21],weld21,weld22) continue end local new = Instance.new("WeldConstraint") if part1:IsA("Model") then part1 = part1.PrimaryPart end if part2:IsA("Model") then part2 = part2.PrimaryPart end new.Part0 = part1 new.Part1 = part2 new.Parent = part1 new.Name = "BuiltWeldConstraint" end end return model end function module.oldConvertToModel(data,physics) if not data[1] then return end local parts = {} local n = tonumber local currentModel = Instance.new("Model") currentModel:SetAttribute("Version","V1") local internalSuccess = true local internalValue = {} local success,value = xpcall(function() for index,part in pairs(data[2]) do local success,value = pcall(function() local isPoly = #part[1] == 0 local new = module.getPartByName(part[2]) if new then new = new:Clone() end if isPoly then new = module.getPolyByName(part[2]) if not new then warn("Missing Poly: "..part[2]) end local old = new new = new:Clone() local realNodes = {} local attach = new if new:IsA("Model") then attach = new.PrimaryPart end for _,edit in pairs(part[3]) do if #edit[1] == 1 and tonumber(edit[1]) then local realNode = (old.Parent:FindFirstChild("Node") or _G.Storage.Parts.DefaultNode):Clone() realNode.CFrame = module.tableToCFrame(edit[2]) realNode.Name = edit[1] realNode.Parent = new realNode.Transparency = 1 table.insert(realNodes,realNode) end end module.positionPart(new,realNodes) for _,node in pairs(realNodes) do weldNoPersist(node,attach) end for _,part in pairs(new:GetChildren()) do if part:IsA("BasePart") and part ~= attach then weldNoPersist(part,attach) end end end if new then table.insert(parts,new) new.Parent = currentModel if index == 1 then if new:IsA("Model") then currentModel.PrimaryPart = new.PrimaryPart else currentModel.PrimaryPart = new end end if part[3] then local cnv = module.convertValue if typeof(part[3]) == "string" then print(part[3]) end for _,edit in pairs(part[3]) do if #edit ~= 1 and not tonumber(edit[1]) and not (edit[1] == "C") and new:FindFirstChild(edit[1]) then new[edit[1]].Value = module.convertValue(edit[2]) elseif (edit[1] == "C") then module.colorPart(new,Color3.new(cnv(edit[2][1]),cnv(edit[2][2]),cnv(edit[2][3]))) end end end if not physics then if new:IsA("Model") then new.PrimaryPart.Anchored = true for _,part in pairs(new:GetDescendants()) do if part:IsA("BasePart") then part.CanCollide = false part.CanTouch = false end end else new.Anchored = true new.CanCollide = false new.CanTouch = false end end if not isPoly then module.setItemCFrame(new,module.tableToCFrame(part[1])) end end end) if not success then internalSuccess = false table.insert(internalValue,value) end end if not physics then return currentModel,data end for index,weld in pairs(data[3]) do local new = Instance.new("WeldConstraint") local part0 = parts[n(weld[1][1])] if not part0 then warn("Weld missing.") continue end if weld[1][2] and part0:FindFirstChild(weld[1][2]) then part0 = part0[weld[1][2]] end local part1 = parts[n(weld[2][1])] if not part1 then continue end if weld[2][2] and part1:FindFirstChild(weld[2][2]) then part1 = part1[weld[2][2]] end if part0:IsA("Model") then part0 = part0.PrimaryPart end if part1:IsA("Model") then part1 = part1.PrimaryPart end new.Part0 = part0 new.Part1 = part1 new.Parent = part0 new.Name = "BuiltWeldConstraint" end end,print) if not internalSuccess then data[1] = "CORRUPTED" for _,item in pairs(internalValue) do print(data) print(item) debug.traceback(item,2) end end --print(data) return currentModel,data end local function getConnectedParts(part) local connected = part:GetConnectedParts(false) for _,joint in pairs(part:GetJoints()) do if joint:IsA("Constraint") then if joint.Attachment0 and joint.Attachment1 then if joint.Attachment0.Parent == part then table.insert(connected,joint.Attachment1.Parent) else table.insert(connected,joint.Attachment0.Parent) end end end end return connected end function module.forEachConnectedObject(start,callback,show,parent) if not start then return end if start:IsA("Model") then start = start.PrimaryPart end if not parent then parent = game.Workspace.Objects end local parts = {} local objects = {} local function checkPart(part) local object if part.Parent:IsA("Model") and part.Parent.Parent == parent then object = part.Parent elseif part.Parent == parent then object = part end if not object or objects[object] then return end objects[object] = object if callback then callback(object) end end local function tryConnected(part) if parts[part] then return else parts[part] = part checkPart(part) end local connected = getConnectedParts(part) for _,part in pairs(connected) do tryConnected(part) end end tryConnected(start) return objects end function module.setNetworkOwner(part,player) for _,part in pairs(part:GetConnectedParts(true)) do if part.Anchored then return end end part:SetNetworkOwner(player) end function module.getOwnedOrNil(player,part) if module.playerOwnsPart(player,part) then return part elseif module.playerOwnsPart(player,part.Parent) then return part end return nil end function module.getObjectFromPart(part) if part:GetAttribute("PartId") then return part elseif part.Parent and part.Parent:GetAttribute("PartId") then return part.Parent end end module.getMouseHit = mouse.getMouseHit return module