Josiahiscool73

ModelSaver

Aug 29th, 2026 (edited)
53
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 30.80 KB | None | 0 0
  1. local AssetService = game:GetService("AssetService")
  2. local Selection = game:GetService("Selection")
  3. local ScriptEditorService = game:GetService("ScriptEditorService")
  4. local ServerStorage = game:GetService("ServerStorage")
  5. local KeyframeSequenceProvider = game:GetService("KeyframeSequenceProvider")
  6. local CoreGui = game:GetService("CoreGui")
  7.  
  8. local prup = {
  9. "Size", "CFrame", "Color", "Material", "Transparency", "Reflectance",
  10. "CanCollide", "CanTouch", "CanQuery", "Anchored", "Massless", "CastShadow",
  11. "CollisionGroup", "TopSurface", "BottomSurface", "LeftSurface", "RightSurface",
  12. "Friction", "Elasticity", "CustomPhysicalProperties", "RootPriority"
  13. }
  14.  
  15. local x_prup = {
  16. Part = {"Shape"},
  17. MeshPart = {"MeshId", "TextureID", "RenderFidelity", "CollisionFidelity", "DoubleSided"},
  18. SpecialMesh = {"MeshType", "MeshId", "TextureId", "Scale", "Offset", "VertexColors"},
  19. Decal = {"Texture", "Transparency", "Color3", "Face", "ZIndex"},
  20. Texture = {"Texture", "Transparency", "Color3", "Face", "ZIndex", "StudsPerTileU", "StudsPerTileV", "OffsetStudsU", "OffsetStudsV"},
  21. SurfaceAppearance = {"ColorMap", "NormalMap", "MetalnessMap", "RoughnessMap", "AlphaMode"},
  22. CharacterMesh = {"BodyPart", "MeshId", "BaseTextureId", "OverlayTextureId"},
  23.  
  24. Tool = {"Grip", "GripPos", "GripForward", "GripRight", "GripUp", "CanBeDropped", "ManualActivationOnly", "RequiresHandle", "ToolTip", "Enabled"},
  25. Accessory = {"AttachmentPoint", "AccessoryType"},
  26. Accoutrement = {"AttachmentPoint"},
  27. Shirt = {"ShirtTemplate", "Color3"},
  28. Pants = {"PantsTemplate", "Color3"},
  29. ShirtGraphic = {"Graphic", "Color3"},
  30.  
  31. Humanoid = {
  32. "RigType", "HipHeight", "WalkSpeed", "JumpPower", "JumpHeight",
  33. "MaxHealth", "Health", "AutoRotate", "DisplayDistanceType",
  34. "HealthDisplayType", "HealthDisplayDistance", "NameDisplayDistance",
  35. "BreakJointsOnDeath", "RequiresNeck", "AutomaticScalingEnabled", "UseJumpPower"
  36. },
  37. Animation = {"AnimationId"},
  38. Animator = {"EvaluationType"},
  39. AnimationController = {},
  40. Bone = {"CFrame", "Transform"},
  41. BodyColors = {"HeadColor3", "TorsoColor3", "LeftArmColor3", "RightArmColor3", "LeftLegColor3", "RightLegColor3"},
  42.  
  43. KeyframeSequence = {"Loop", "Priority"},
  44. Keyframe = {"Time"},
  45. Pose = {"CFrame", "EasingDirection", "EasingStyle", "Weight"},
  46. NumberPose = {"Value", "EasingDirection", "EasingStyle", "Weight"},
  47. KeyframeMarker = {"Value"},
  48.  
  49. ProximityPrompt = {
  50. "ActionText", "ObjectText", "HoldDuration", "MaxActivationDistance",
  51. "RequiresLineOfSight", "KeyboardKeyCode", "GamepadKeyCode", "ClickablePrompt",
  52. "Enabled", "Exclusivity", "UIOffset", "AutoLocalize", "Style"
  53. },
  54. ClickDetector = {"MaxActivationDistance", "CursorIcon"},
  55. Dialog = {"InitialPrompt", "Purpose", "Tone", "GoodbyeDialog", "InUse"},
  56. DialogChoice = {"UserDialog", "ResponseDialog", "GoodbyeDialog"},
  57.  
  58. Script = {"Source", "Enabled", "RunContext"},
  59. LocalScript = {"Source", "Enabled"},
  60. ModuleScript = {"Source"},
  61.  
  62. Motor6D = {"C0", "C1", "MaxVelocity", "DesiredAngle", "CurrentAngle", "Enabled"},
  63. Weld = {"C0", "C1", "Enabled"},
  64. ManualWeld = {"C0", "C1", "Enabled"},
  65. WeldConstraint = {"Enabled"},
  66. Snap = {"C0", "C1", "Enabled"},
  67. Attachment = {"CFrame", "Visible"},
  68. NoCollisionConstraint = {"Enabled"},
  69. BallSocketConstraint = {"LimitsEnabled", "UpperAngle", "TwistLimitsEnabled", "TwistLowerAngle", "TwistUpperAngle", "Radius", "Restitution", "Enabled"},
  70. HingeConstraint = {"LimitsEnabled", "LowerAngle", "UpperAngle", "ActuatorType", "MotorMaxTorque", "AngularVelocity", "ServoTargetAngle", "Radius", "Restitution", "AngularSpeed", "Enabled"},
  71. PrismaticConstraint = {"LimitsEnabled", "LowerLimit", "UpperLimit", "Restitution", "ActuatorType", "MotorMaxAcceleration", "MotorMaxForce", "Velocity", "ServoMaxForce", "TargetPosition", "Speed", "Size", "Enabled"},
  72. CylindricalConstraint = {"LimitsEnabled", "LowerLimit", "UpperLimit", "Restitution", "ActuatorType", "MotorMaxAcceleration", "MotorMaxForce", "Velocity", "ServoMaxForce", "TargetPosition", "Speed", "AngularLimitsEnabled", "LowerAngle", "UpperAngle", "AngularRestitution", "AngularActuatorType", "MotorMaxAngularAcceleration", "MotorMaxTorque", "AngularVelocity", "AngularServoMaxTorque", "TargetAngle", "AngularSpeed", "RotationAxisVisible", "Size", "Enabled"},
  73. RodConstraint = {"Length", "Thickness", "Visible", "Enabled"},
  74. RopeConstraint = {"Length", "Thickness", "Visible", "Restitution", "Enabled"},
  75. SpringConstraint = {"Coils", "FreeLength", "Radius", "Stiffness", "Damping", "LimitsEnabled", "MaxLength", "MinLength", "Thickness", "Visible", "Enabled"},
  76. AlignPosition = {"Mode", "MaxForce", "MaxVelocity", "Responsiveness", "RigidityEnabled", "ApplyAtCenterOfMass", "Position", "ReactionForceEnabled", "Enabled"},
  77. AlignOrientation = {"Mode", "MaxTorque", "MaxAngularVelocity", "Responsiveness", "RigidityEnabled", "CFrame", "ReactionTorqueEnabled", "PrimaryAxisOnly", "AlignType", "Enabled"},
  78. LinearVelocity = {"VectorVelocity", "MaxForce", "RelativeTo", "VelocityConstraintMode", "Enabled"},
  79. AngularVelocity = {"AngularVelocity", "MaxTorque", "RelativeTo", "ReactionTorqueEnabled", "Enabled"},
  80. VectorForce = {"Force", "RelativeTo", "ApplyAtCenterOfMass", "Enabled"},
  81. Torque = {"Torque", "RelativeTo", "Enabled"},
  82.  
  83. PointLight = {"Color", "Brightness", "Range", "Shadows", "Enabled"},
  84. SpotLight = {"Color", "Brightness", "Range", "Shadows", "Angle", "Face", "Enabled"},
  85. SurfaceLight = {"Color", "Brightness", "Range", "Shadows", "Angle", "Face", "Enabled"},
  86. Sound = {"SoundId", "Volume", "PlaybackSpeed", "Looped", "RollOffMode", "RollOffMinDistance", "RollOffMaxDistance", "Playing"},
  87. ParticleEmitter = {
  88. "Texture", "Color", "Transparency", "Size", "Lifetime", "Rate", "Speed", "Rotation",
  89. "RotSpeed", "Acceleration", "SpreadAngle", "EmissionDirection", "LockedToPart",
  90. "LightInfluence", "LightEmission", "ZOffset", "Drag", "TimeScale", "VelocityInheritance", "Enabled"
  91. },
  92. Beam = {
  93. "Width0", "Width1", "Color", "Transparency", "Texture", "TextureMode", "TextureLength",
  94. "TextureSpeed", "Segments", "CurveSize0", "CurveSize1", "FaceCamera", "LightEmission",
  95. "LightInfluence", "ZOffset", "Enabled"
  96. },
  97. Trail = {
  98. "Color", "Transparency", "Lifetime", "MinLength", "WidthScale", "Texture", "TextureMode",
  99. "TextureLength", "LightEmission", "LightInfluence", "FaceCamera", "MaxLength", "Enabled"
  100. },
  101. Highlight = {"FillColor", "FillTransparency", "OutlineColor", "OutlineTransparency", "DepthMode", "Enabled"},
  102. Fire = {"Color", "SecondaryColor", "Heat", "Size", "Enabled"},
  103. Smoke = {"Color", "Opacity", "RiseVelocity", "Size", "Enabled"},
  104. Sparkles = {"SparkleColor", "Enabled"},
  105.  
  106. BillboardGui = {
  107. "AlwaysOnTop", "ExtentsOffset", "ExtentsOffsetWorldSpace", "LightInfluence",
  108. "MaxDistance", "Size", "SizeOffset", "StudsOffset", "StudsOffsetWorldSpace",
  109. "Enabled", "ResetOnSpawn", "ClipsDescendants", "Active", "DistanceLowerLimit", "DistanceUpperLimit"
  110. },
  111. SurfaceGui = {
  112. "Face", "AlwaysOnTop", "LightInfluence", "MaxDistance", "CanvasSize",
  113. "PixelsPerStud", "SizingMode", "ToolPunchThroughDistance", "Enabled", "ClipsDescendants", "Active"
  114. },
  115. ScreenGui = {"Enabled", "ResetOnSpawn", "DisplayOrder", "IgnoreGuiInset"},
  116. Frame = {"BackgroundColor3", "BackgroundTransparency", "BorderColor3", "BorderSizePixel", "Size", "Position", "AnchorPoint", "Visible", "ZIndex", "Rotation", "ClipsDescendants", "LayoutOrder"},
  117. TextLabel = {
  118. "Text", "Font", "FontFace", "TextSize", "TextColor3", "TextStrokeColor3", "TextStrokeTransparency",
  119. "TextTransparency", "TextScaled", "TextWrapped", "TextXAlignment", "TextYAlignment",
  120. "RichText", "BackgroundColor3", "BackgroundTransparency", "BorderColor3", "BorderSizePixel",
  121. "Size", "Position", "AnchorPoint", "Visible", "ZIndex", "Rotation", "LayoutOrder", "MaxVisibleGraphemes"
  122. },
  123. TextButton = {
  124. "Text", "Font", "FontFace", "TextSize", "TextColor3", "TextStrokeColor3", "TextStrokeTransparency",
  125. "TextTransparency", "TextScaled", "TextWrapped", "TextXAlignment", "TextYAlignment",
  126. "RichText", "BackgroundColor3", "BackgroundTransparency", "BorderColor3", "BorderSizePixel",
  127. "Size", "Position", "AnchorPoint", "Visible", "ZIndex", "Rotation", "AutoButtonColor", "Active", "LayoutOrder"
  128. },
  129. ImageLabel = {
  130. "Image", "ImageColor3", "ImageTransparency", "ImageRectOffset", "ImageRectSize",
  131. "ScaleType", "SliceCenter", "SliceScale", "BackgroundColor3", "BackgroundTransparency",
  132. "BorderColor3", "BorderSizePixel", "Size", "Position", "AnchorPoint", "Visible", "ZIndex", "Rotation", "LayoutOrder"
  133. },
  134. ImageButton = {
  135. "Image", "ImageColor3", "ImageTransparency", "ImageRectOffset", "ImageRectSize",
  136. "ScaleType", "SliceCenter", "SliceScale", "BackgroundColor3", "BackgroundTransparency",
  137. "BorderColor3", "BorderSizePixel", "Size", "Position", "AnchorPoint", "Visible", "ZIndex", "Rotation", "AutoButtonColor", "Active", "LayoutOrder"
  138. },
  139. UICorner = {"CornerRadius"},
  140. UIStroke = {"Color", "Thickness", "Transparency", "ApplyStrokeMode", "LineJoinMode", "Enabled"},
  141. UIGradient = {"Color", "Transparency", "Offset", "Rotation", "Enabled"},
  142. UIAspectRatioConstraint = {"AspectRatio", "AspectType", "DominantAxis"},
  143. UIScale = {"Scale"},
  144.  
  145. Model = {"PrimaryPart", "LevelOfDetail", "WorldPivot"},
  146. IntValue = {"Value"}, NumberValue = {"Value"}, StringValue = {"Value"}, BoolValue = {"Value"},
  147. Vector3Value = {"Value"}, CFrameValue = {"Value"}, Color3Value = {"Value"}, ObjectValue = {"Value"}
  148. }
  149.  
  150. local ref_prup = {
  151. PrimaryPart = true, Part0 = true, Part1 = true,
  152. Attachment0 = true, Attachment1 = true, Adornee = true, Value = true
  153. }
  154.  
  155. local CLASS_CONV = {TouchTransmitter = "ClickDetector", UnionOperation = "Part"}
  156. local SKIP_CLASSES = {Terrain = true}
  157. local PART_LIKE = {Part = true, MeshPart = true, TrussPart = true, CornerWedgePart = true, WedgePart = true, CylinderPart = true, UnionOperation = true}
  158.  
  159. local function num(v)
  160. if v ~= v then return "(0/0)" end
  161. if v == math.huge then return "math.huge" end
  162. if v == -math.huge then return "-math.huge" end
  163. return ("%.17g"):format(v)
  164. end
  165.  
  166. local function emitVal(v)
  167. local t = typeof(v)
  168. if t == "number" then return num(v) end
  169. if t == "boolean" then return tostring(v) end
  170. if t == "string" then return ("%q"):format(v) end
  171. if t == "EnumItem" then return tostring(v) end
  172. if t == "BrickColor" then return ('BrickColor.new("%s")'):format(v.Name) end
  173. if t == "Vector3" then return ("Vector3.new(%s,%s,%s)"):format(num(v.X), num(v.Y), num(v.Z)) end
  174. if t == "Vector2" then return ("Vector2.new(%s,%s)"):format(num(v.X), num(v.Y)) end
  175. if t == "Color3" then return ("Color3.new(%s,%s,%s)"):format(num(v.R), num(v.G), num(v.B)) end
  176. if t == "UDim2" then return ("UDim2.new(%s,%s,%s,%s)"):format(num(v.X.Scale), num(v.X.Offset), num(v.Y.Scale), num(v.Y.Offset)) end
  177. if t == "UDim" then return ("UDim.new(%s,%s)"):format(num(v.Scale), num(v.Offset)) end
  178. if t == "CFrame" then
  179. local x,y,z,r00,r01,r02,r10,r11,r12,r20,r21,r22 = v:GetComponents()
  180. return ("CFrame.new(%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)"):format(num(x),num(y),num(z),num(r00),num(r01),num(r02),num(r10),num(r11),num(r12),num(r20),num(r21),num(r22))
  181. end
  182. if t == "NumberRange" then return ("NumberRange.new(%s,%s)"):format(num(v.Min), num(v.Max)) end
  183. if t == "NumberSequence" then
  184. local p = {}
  185. for _, k in ipairs(v.Keypoints) do p[#p+1] = ("NumberSequenceKeypoint.new(%s,%s,%s)"):format(num(k.Time), num(k.Value), num(k.Envelope)) end
  186. return "NumberSequence.new({" .. table.concat(p, ",") .. "})"
  187. end
  188. if t == "ColorSequence" then
  189. local p = {}
  190. for _, k in ipairs(v.Keypoints) do p[#p+1] = ("ColorSequenceKeypoint.new(%s,Color3.new(%s,%s,%s))"):format(num(k.Time), num(k.Value.R), num(k.Value.G), num(k.Value.B)) end
  191. return "ColorSequence.new({" .. table.concat(p, ",") .. "})"
  192. end
  193. if t == "PhysicalProperties" then return ("PhysicalProperties.new(%s,%s,%s,%s,%s)"):format(num(v.Density), num(v.Friction), num(v.Elasticity), num(v.FrictionWeight), num(v.ElasticityWeight)) end
  194. if t == "Rect" then return ("Rect.new(%s,%s,%s,%s)"):format(num(v.Min.X), num(v.Min.Y), num(v.Max.X), num(v.Max.Y)) end
  195. if t == "Font" then return ('Font.new(%q,%s,%s)'):format(v.Family, tostring(v.Weight), tostring(v.Style)) end
  196. return nil
  197. end
  198.  
  199. local function isValidMeshId(id)
  200. if type(id) ~= "string" then return false end
  201. local t = id:match("^%s*(.-)%s*$")
  202. if t == "" or t == "0" or t == "rbxassetid://0" or t == "rbxassetid://" or t == "rbxasset://" then return false end
  203. local l = t:lower()
  204. if l == "none" or l == "null" or l:find("meshpartfallback") then return false end
  205. return true
  206. end
  207.  
  208. local function readMeshId(o)
  209. local ok, v = pcall(function() return o.MeshId end)
  210. if ok and isValidMeshId(v) then return v end
  211. local ok2, c = pcall(function() return o.MeshContent end)
  212. if ok2 and c then
  213. local ok3, uri = pcall(function() return c.Uri end)
  214. if ok3 and isValidMeshId(uri) then return uri end
  215. local s = tostring(c)
  216. if (s:match("^rbxasset") or s:match("^http")) and isValidMeshId(s) then return s end
  217. end
  218. return nil
  219. end
  220.  
  221. local function propListFor(c)
  222. local l, seen = {}, {}
  223. local function add(k) if not seen[k] then seen[k] = true; l[#l+1] = k end end
  224. if PART_LIKE[c] or c == "Part" then for _, k in ipairs(prup) do add(k) end end
  225. if x_prup[c] then for _, k in ipairs(x_prup[c]) do add(k) end end
  226. for k in pairs(ref_prup) do add(k) end
  227. return l
  228. end
  229.  
  230. local function captureActiveAnimations(root)
  231. local active = {}
  232. local hum = root:FindFirstChildOfClass("Humanoid") or root:FindFirstChildOfClass("AnimationController")
  233. if hum then
  234. local animator = hum:FindFirstChildOfClass("Animator")
  235. if animator then
  236. local ok, tracks = pcall(function() return animator:GetPlayingAnimationTracks() end)
  237. if ok and tracks and #tracks > 0 then
  238. for _, trk in ipairs(tracks) do
  239. if trk.IsPlaying and trk.Animation then
  240. table.insert(active, {
  241. id = trk.Animation.AnimationId,
  242. name = trk.Animation.Name,
  243. speed = trk.Speed,
  244. weight = trk.WeightCurrent,
  245. timePos = trk.TimePosition,
  246. looped = trk.Looped,
  247. priority = trk.Priority.Name
  248. })
  249. end
  250. end
  251. end
  252. end
  253. end
  254.  
  255. if #active == 0 then
  256. for _, d in ipairs(root:GetDescendants()) do
  257. if d:IsA("Animation") and (d.Name:lower():find("idle") or (d.Parent and d.Parent.Name:lower():find("idle"))) then
  258. table.insert(active, {
  259. id = d.AnimationId,
  260. name = d.Name,
  261. speed = 1,
  262. weight = 1,
  263. timePos = 0,
  264. looped = true,
  265. priority = "Idle"
  266. })
  267. break
  268. end
  269. end
  270. end
  271. return active
  272. end
  273.  
  274. local function serializeSelection(sel)
  275. local set, roots = {}, {}
  276. for _, o in ipairs(sel) do set[o] = true end
  277. for _, o in ipairs(sel) do
  278. local p, isRoot = o.Parent, true
  279. while p do if set[p] then isRoot = false; break end; p = p.Parent end
  280. if isRoot then roots[#roots+1] = o end
  281. end
  282. if #roots == 0 then return nil end
  283. local root, records, index = roots[1], {}, {}
  284.  
  285. local activeAnims = captureActiveAnimations(root)
  286.  
  287. local function walk(o, pIdx)
  288. local c = CLASS_CONV[o.ClassName] or o.ClassName
  289. if SKIP_CLASSES[c] then return end
  290.  
  291. local isScript = (c == "Script" or c == "LocalScript" or c == "ModuleScript")
  292. local isMP = (c == "MeshPart") or (pcall(function() return o:IsA("MeshPart") end) and o:IsA("MeshPart"))
  293. local mId = nil
  294. if isMP then
  295. c = "MeshPart"
  296. mId = readMeshId(o)
  297. end
  298.  
  299. local rec = {c = c, n = o.Name, p = pIdx, props = {}, refs = {}}
  300. records[#records+1] = rec
  301. local myIdx = #records
  302. index[o] = myIdx
  303.  
  304. if isMP and mId then rec.mesh = mId end
  305.  
  306. for _, k in ipairs(propListFor(c)) do
  307. local ok, v = pcall(function() return o[k] end)
  308. if ok and v ~= nil then
  309. if typeof(v) == "Instance" then
  310. if ref_prup[k] then rec.refs[k] = v end
  311. else
  312. local ev = emitVal(v)
  313. if ev then rec.props[k] = ev end
  314. end
  315. end
  316. end
  317.  
  318. if isScript then
  319. local okS, src = pcall(function() return o.Source end)
  320. if okS and src then rec.props["Source"] = ("%q"):format(src) end
  321. end
  322.  
  323. local okA, attrs = pcall(function() return o:GetAttributes() end)
  324. if okA and type(attrs) == "table" then
  325. for k, v in pairs(attrs) do
  326. local ev = emitVal(v)
  327. if ev then rec.attrs = rec.attrs or {}; rec.attrs[k] = ev end
  328. end
  329. end
  330.  
  331. if c == "Animation" and o.AnimationId and o.AnimationId ~= "" then
  332. pcall(function()
  333. local kfs = KeyframeSequenceProvider:GetKeyframeSequenceAsync(o.AnimationId)
  334. if kfs and kfs:IsA("KeyframeSequence") then
  335. walk(kfs, myIdx)
  336. end
  337. end)
  338. end
  339.  
  340. for _, child in ipairs(o:GetChildren()) do walk(child, myIdx) end
  341. end
  342.  
  343. walk(root, 0)
  344.  
  345. for o, i in pairs(index) do
  346. for k, target in pairs(records[i].refs) do records[i].refs[k] = index[target] end
  347. end
  348. for _, r in ipairs(records) do
  349. for k in pairs(r.refs) do if type(r.refs[k]) ~= "number" then r.refs[k] = nil end end
  350. if next(r.refs) == nil then r.refs = nil end
  351. if next(r.props) == nil then r.props = nil end
  352. end
  353. return records, root.Name, #records, activeAnims
  354. end
  355.  
  356. local RUNTIME = [===[
  357. local AssetService = game:GetService("AssetService")
  358. local Players = game:GetService("Players")
  359. local KeyframeSequenceProvider = game:GetService("KeyframeSequenceProvider")
  360.  
  361. local function setProp(o, k, v)
  362. o[k] = v
  363. end
  364.  
  365. local function setRef(o, k, target)
  366. o[k] = target
  367. end
  368.  
  369. local mCache = {}
  370. local function create(r)
  371. if r.c == "MeshPart" then
  372. if r.mesh then
  373. local t = mCache[r.mesh]
  374. if t == nil then
  375. local ok, res = pcall(function()
  376. return AssetService:CreateMeshPartAsync(Content.fromUri(r.mesh), {
  377. CollisionFidelity = (r.props and r.props.CollisionFidelity) or Enum.CollisionFidelity.Box
  378. })
  379. end)
  380. t = (ok and res) or false
  381. mCache[r.mesh] = t
  382. end
  383. if t then return t:Clone() end
  384. end
  385. local part = Instance.new("Part")
  386. if r.mesh then
  387. local sm = Instance.new("SpecialMesh")
  388. sm.MeshType = Enum.MeshType.FileMesh
  389. sm.MeshId = r.mesh
  390. if r.props and r.props.TextureID then sm.TextureId = r.props.TextureID end
  391. sm.Parent = part
  392. end
  393. return part
  394. end
  395. return Instance.new(r.c)
  396. end
  397.  
  398. local total = #DATA
  399. local insts = table.create(total)
  400. for i = 1, total do
  401. local r = DATA[i]
  402. local o = create(r)
  403. if o then o.Name = r.n end
  404. insts[i] = o
  405. if i % 250 == 0 then task.wait() end
  406. end
  407.  
  408. for i = 1, total do
  409. local o = insts[i]
  410. local r = DATA[i]
  411. if o then
  412. if r.props then
  413. if r.c == "Humanoid" and r.props.MaxHealth then
  414. pcall(setProp, o, "MaxHealth", r.props.MaxHealth)
  415. end
  416. for k, v in pairs(r.props) do
  417. if not (r.c == "MeshPart" and (k == "CollisionFidelity" or k == "MeshId")) then
  418. pcall(setProp, o, k, v)
  419. end
  420. end
  421. end
  422. if r.attrs then
  423. for k, v in pairs(r.attrs) do pcall(o.SetAttribute, o, k, v) end
  424. end
  425. end
  426. if i % 250 == 0 then task.wait() end
  427. end
  428.  
  429. for i = 1, total do
  430. local o = insts[i]
  431. local r = DATA[i]
  432. if o and r.refs then
  433. for k, idx in pairs(r.refs) do
  434. local target = insts[idx]
  435. if target then pcall(setRef, o, k, target) end
  436. end
  437. end
  438. if i % 250 == 0 then task.wait() end
  439. end
  440.  
  441. for i = 1, total do
  442. local o = insts[i]
  443. local r = DATA[i]
  444. if o and r.p ~= 0 and insts[r.p] then
  445. o.Parent = insts[r.p]
  446. end
  447. end
  448.  
  449. local root = insts[1]
  450. if not root then return end
  451.  
  452. for i = 1, total do
  453. local o = insts[i]
  454. if o and o:IsA("Animation") then
  455. local kfs = o:FindFirstChildOfClass("KeyframeSequence")
  456. if kfs then
  457. local ok, newId = pcall(function()
  458. return KeyframeSequenceProvider:RegisterKeyframeSequence(kfs)
  459. end)
  460. if ok and newId then o.AnimationId = newId end
  461. end
  462. end
  463. end
  464.  
  465. local function getBaseCF()
  466. local pl = Players.LocalPlayer or Players:FindFirstChildOfClass("Player")
  467. local c = pl and (pl.Character or pl.CharacterAdded:Wait())
  468. local hrp = c and c:WaitForChild("HumanoidRootPart", 5)
  469. if hrp then return hrp.CFrame end
  470. local sp = workspace:FindFirstChildOfClass("SpawnLocation", true)
  471. return sp and (sp.CFrame * CFrame.new(0, 4, 0)) or CFrame.new(0, 6, 0)
  472. end
  473.  
  474. if CONFIG.REPLACE then
  475. for _, e in ipairs(workspace:GetChildren()) do
  476. if e.Name == CONFIG.NAME then e:Destroy() end
  477. end
  478. end
  479.  
  480. if root:IsA("Model") then
  481. if not root.PrimaryPart then
  482. root.PrimaryPart = root:FindFirstChild("HumanoidRootPart") or root:FindFirstChildWhichIsA("BasePart")
  483. end
  484. pcall(function() root:PivotTo(getBaseCF() * CONFIG.OFFSET) end)
  485. elseif root:IsA("BasePart") then
  486. root.CFrame = getBaseCF() * CONFIG.OFFSET
  487. end
  488.  
  489. if CONFIG.ANCHOR_MODE == "RootOnly" then
  490. local hrp = root:FindFirstChild("HumanoidRootPart")
  491. if hrp then
  492. hrp.Anchored = true
  493. for _, d in ipairs(root:GetDescendants()) do
  494. if d:IsA("BasePart") and d ~= hrp then d.Anchored = false end
  495. end
  496. else
  497. for _, d in ipairs(root:GetDescendants()) do if d:IsA("BasePart") then d.Anchored = true end end
  498. end
  499. elseif CONFIG.ANCHOR_MODE == "All" then
  500. for _, d in ipairs(root:GetDescendants()) do if d:IsA("BasePart") then d.Anchored = true end end
  501. if root:IsA("BasePart") then root.Anchored = true end
  502. end
  503.  
  504. root.Parent = workspace
  505.  
  506. if CONFIG.PLAY_ANIMATIONS and #ACTIVE_ANIMS > 0 then
  507. local hum = root:FindFirstChildOfClass("Humanoid") or root:FindFirstChildOfClass("AnimationController")
  508. if hum then
  509. local animator = hum:FindFirstChildOfClass("Animator") or Instance.new("Animator", hum)
  510. for _, animData in ipairs(ACTIVE_ANIMS) do
  511. local targetAnim = nil
  512. for _, d in ipairs(root:GetDescendants()) do
  513. if d:IsA("Animation") and (d.AnimationId == animData.id or d.Name == animData.name) then
  514. targetAnim = d
  515. break
  516. end
  517. end
  518. if not targetAnim and animData.id and animData.id ~= "" then
  519. targetAnim = Instance.new("Animation")
  520. targetAnim.AnimationId = animData.id
  521. targetAnim.Name = animData.name
  522. targetAnim.Parent = hum
  523. end
  524. if targetAnim then
  525. task.spawn(function()
  526. local ok, track = pcall(function() return animator:LoadAnimation(targetAnim) end)
  527. if ok and track then
  528. pcall(function()
  529. track.Priority = Enum.AnimationPriority[animData.priority] or Enum.AnimationPriority.Idle
  530. track.Looped = (animData.looped ~= nil) and animData.looped or true
  531. track:Play(0.1, animData.weight or 1, animData.speed or 1)
  532. if animData.timePos and animData.timePos > 0 then
  533. track.TimePosition = animData.timePos
  534. end
  535. end)
  536. end
  537. end)
  538. end
  539. end
  540. end
  541. end
  542. ]===]
  543.  
  544. local function generateCode(name, records, anchorMode, playAnims, activeAnims)
  545. local animLines = {}
  546. for _, a in ipairs(activeAnims) do
  547. animLines[#animLines + 1] = ('\t{ id = %q, name = %q, speed = %s, weight = %s, timePos = %s, looped = %s, priority = %q },'):format(
  548. a.id, a.name, num(a.speed), num(a.weight), num(a.timePos), tostring(a.looped), a.priority
  549. )
  550. end
  551.  
  552. local lines = {
  553. "local CONFIG = {",
  554. ('\tNAME = %q,'):format(name),
  555. "\tOFFSET = CFrame.new(0, 0, -6),",
  556. ('\tANCHOR_MODE = %q,'):format(anchorMode),
  557. ('\tPLAY_ANIMATIONS = %s,'):format(tostring(playAnims)),
  558. "\tREPLACE = true,",
  559. "}",
  560. "",
  561. "local ACTIVE_ANIMS = {",
  562. table.concat(animLines, "\n"),
  563. "}",
  564. "",
  565. "local DATA = {"
  566. }
  567.  
  568. for _, r in ipairs(records) do
  569. local p = {('c=%q'):format(r.c), ('n=%q'):format(r.n), ('p=%d'):format(r.p)}
  570. if r.mesh then p[#p+1] = ('mesh=%q'):format(r.mesh) end
  571. if r.props then
  572. local pl = {}
  573. for k, v in pairs(r.props) do pl[#pl+1] = ("%s=%s"):format(k, v) end
  574. table.sort(pl)
  575. p[#p+1] = "props={" .. table.concat(pl, ",") .. "}"
  576. end
  577. if r.refs then
  578. local rl = {}
  579. for k, idx in pairs(r.refs) do rl[#rl+1] = ("%s=%d"):format(k, idx) end
  580. table.sort(rl)
  581. p[#p+1] = "refs={" .. table.concat(rl, ",") .. "}"
  582. end
  583. if r.attrs then
  584. local al = {}
  585. for k, v in pairs(r.attrs) do al[#al+1] = ("[%q]=%s"):format(k, v) end
  586. table.sort(al)
  587. p[#p+1] = "attrs={" .. table.concat(al, ",") .. "}"
  588. end
  589. lines[#lines+1] = "\t{ " .. table.concat(p, ", ") .. " },"
  590. end
  591. lines[#lines+1] = "}\n"
  592. return table.concat(lines, "\n") .. RUNTIME
  593. end
  594.  
  595. local function el(class, props, parent)
  596. local o = Instance.new(class)
  597. for k, v in pairs(props) do o[k] = v end
  598. if parent then o.Parent = parent end
  599. return o
  600. end
  601.  
  602. local function buildUI(parent)
  603. local pnl = el("Frame", {
  604. Name = "ModelSaverPanel", Size = UDim2.fromOffset(280, 240), Position = UDim2.new(0.5, -140, 0.5, -120),
  605. BackgroundColor3 = Color3.fromRGB(18, 18, 18), BorderSizePixel = 0, Active = true
  606. }, parent)
  607. el("UICorner", {CornerRadius = UDim.new(0, 4)}, pnl)
  608. el("UIStroke", {Color = Color3.fromRGB(60, 60, 60), Thickness = 1}, pnl)
  609.  
  610. local bar = el("Frame", {
  611. Name = "TopBar", Size = UDim2.new(1, 0, 0, 26), BackgroundColor3 = Color3.fromRGB(28, 28, 28), BorderSizePixel = 0
  612. }, pnl)
  613. el("UICorner", {CornerRadius = UDim.new(0, 4)}, bar)
  614.  
  615. el("TextLabel", {
  616. Size = UDim2.new(1, -30, 1, 0), Position = UDim2.fromOffset(8, 0), BackgroundTransparency = 1,
  617. Font = Enum.Font.RobotoMono, TextSize = 11, TextColor3 = Color3.fromRGB(240, 240, 240),
  618. TextXAlignment = Enum.TextXAlignment.Left, Text = "MODEL & NPC SAVER"
  619. }, bar)
  620.  
  621. local cls = el("TextButton", {
  622. Size = UDim2.fromOffset(20, 20), Position = UDim2.new(1, -23, 0, 3), BackgroundColor3 = Color3.fromRGB(45, 45, 45),
  623. Text = "×", Font = Enum.Font.GothamBold, TextSize = 12, TextColor3 = Color3.fromRGB(255, 255, 255), BorderSizePixel = 0
  624. }, bar)
  625. el("UICorner", {CornerRadius = UDim.new(0, 3)}, cls)
  626. cls.MouseButton1Click:Connect(function() pnl.Parent:Destroy() end)
  627.  
  628. local drag, dStart, sPos
  629. bar.InputBegan:Connect(function(inp)
  630. if inp.UserInputType == Enum.UserInputType.MouseButton1 or inp.UserInputType == Enum.UserInputType.Touch then
  631. drag = true; dStart = inp.Position; sPos = pnl.Position
  632. inp.Changed:Connect(function() if inp.UserInputState == Enum.UserInputState.End then drag = false end end)
  633. end
  634. end)
  635. bar.InputChanged:Connect(function(inp)
  636. if (inp.UserInputType == Enum.UserInputType.MouseMovement or inp.UserInputType == Enum.UserInputType.Touch) and drag then
  637. local delta = inp.Position - dStart
  638. pnl.Position = UDim2.new(sPos.X.Scale, sPos.X.Offset + delta.X, sPos.Y.Scale, sPos.Y.Offset + delta.Y)
  639. end
  640. end)
  641.  
  642. local selLbl = el("TextLabel", {
  643. Size = UDim2.new(1, -16, 0, 18), Position = UDim2.fromOffset(8, 32), BackgroundColor3 = Color3.fromRGB(24, 24, 24),
  644. Font = Enum.Font.RobotoMono, TextSize = 10, TextColor3 = Color3.fromRGB(180, 180, 180),
  645. TextXAlignment = Enum.TextXAlignment.Left, Text = " SEL: NONE"
  646. }, pnl)
  647. el("UICorner", {CornerRadius = UDim.new(0, 3)}, selLbl)
  648.  
  649. local nameInp = el("TextBox", {
  650. Size = UDim2.new(1, -16, 0, 22), Position = UDim2.fromOffset(8, 54), BackgroundColor3 = Color3.fromRGB(24, 24, 24),
  651. Font = Enum.Font.RobotoMono, TextSize = 11, TextColor3 = Color3.fromRGB(255, 255, 255),
  652. PlaceholderColor3 = Color3.fromRGB(110, 110, 110), PlaceholderText = "MODEL NAME", Text = "", ClearTextOnFocus = false
  653. }, pnl)
  654. el("UICorner", {CornerRadius = UDim.new(0, 3)}, nameInp)
  655.  
  656. local anchorMode = "RootOnly"
  657. local anchorBtn = el("TextButton", {
  658. Size = UDim2.new(1, -16, 0, 20), Position = UDim2.fromOffset(8, 80), BackgroundColor3 = Color3.fromRGB(30, 30, 30),
  659. Font = Enum.Font.RobotoMono, TextSize = 10, TextColor3 = Color3.fromRGB(200, 200, 200),
  660. Text = " ANCHOR: ROOT ONLY (NPC SAFE)"
  661. }, pnl)
  662. el("UICorner", {CornerRadius = UDim.new(0, 3)}, anchorBtn)
  663. anchorBtn.MouseButton1Click:Connect(function()
  664. if anchorMode == "RootOnly" then
  665. anchorMode = "All"
  666. anchorBtn.Text = " ANCHOR: ALL (MAP/PROP)"
  667. elseif anchorMode == "All" then
  668. anchorMode = "None"
  669. anchorBtn.Text = " ANCHOR: NONE (ORIGINAL)"
  670. else
  671. anchorMode = "RootOnly"
  672. anchorBtn.Text = " ANCHOR: ROOT ONLY (NPC SAFE)"
  673. end
  674. end)
  675.  
  676. local playAnims = true
  677. local animBtn = el("TextButton", {
  678. Size = UDim2.new(1, -16, 0, 20), Position = UDim2.fromOffset(8, 104), BackgroundColor3 = Color3.fromRGB(30, 30, 30),
  679. Font = Enum.Font.RobotoMono, TextSize = 10, TextColor3 = Color3.fromRGB(120, 255, 120),
  680. Text = " PLAY ACTIVE ANIM: ON"
  681. }, pnl)
  682. el("UICorner", {CornerRadius = UDim.new(0, 3)}, animBtn)
  683. animBtn.MouseButton1Click:Connect(function()
  684. playAnims = not playAnims
  685. animBtn.Text = " PLAY ACTIVE ANIM: " .. (playAnims and "ON" or "OFF")
  686. animBtn.TextColor3 = playAnims and Color3.fromRGB(120, 255, 120) or Color3.fromRGB(255, 120, 120)
  687. end)
  688.  
  689. local expBtn = el("TextButton", {
  690. Size = UDim2.new(1, -16, 0, 26), Position = UDim2.fromOffset(8, 130), BackgroundColor3 = Color3.fromRGB(240, 240, 240),
  691. Font = Enum.Font.GothamBold, TextSize = 11, TextColor3 = Color3.fromRGB(0, 0, 0), Text = "SAVE MODEL"
  692. }, pnl)
  693. el("UICorner", {CornerRadius = UDim.new(0, 3)}, expBtn)
  694.  
  695. local statLbl = el("TextLabel", {
  696. Size = UDim2.new(1, -16, 0, 68), Position = UDim2.fromOffset(8, 162), BackgroundColor3 = Color3.fromRGB(24, 24, 24),
  697. Font = Enum.Font.RobotoMono, TextSize = 9, TextColor3 = Color3.fromRGB(150, 150, 150),
  698. TextXAlignment = Enum.TextXAlignment.Left, TextYAlignment = Enum.TextYAlignment.Top,
  699. Text = " READY"
  700. }, pnl)
  701. el("UICorner", {CornerRadius = UDim.new(0, 3)}, statLbl)
  702.  
  703. local lastSelName = ""
  704. local function updateSel()
  705. local sel = Selection:Get()
  706. if #sel == 0 then
  707. selLbl.Text = " SEL: NONE"
  708. else
  709. local f = sel[1]
  710. selLbl.Text = (" SEL: %s [%s]"):format(f.Name, f.ClassName)
  711. if nameInp.Text == "" or nameInp.Text == lastSelName then
  712. lastSelName = f.Name
  713. nameInp.Text = f.Name
  714. end
  715. end
  716. end
  717. Selection.SelectionChanged:Connect(updateSel)
  718. updateSel()
  719.  
  720. expBtn.MouseButton1Click:Connect(function()
  721. statLbl.Text = " PROCESSING..."
  722. task.wait()
  723. local sel = Selection:Get()
  724. local recs, rootName, count, activeAnims = serializeSelection(sel)
  725. if not recs or count == 0 then statLbl.Text = " ERR: NO VALID SELECTION"; return end
  726.  
  727. local expName = nameInp.Text ~= "" and nameInp.Text or rootName
  728. expName = expName:gsub('[^%w%-_]', '')
  729. local code = generateCode(expName, recs, anchorMode, playAnims, activeAnims)
  730.  
  731. local ms = Instance.new("ModuleScript")
  732. ms.Name = "Saved_" .. expName
  733. ms.Parent = ServerStorage
  734.  
  735. local updated = false
  736. pcall(function()
  737. ScriptEditorService:UpdateSourceAsync(ms, function()
  738. return code
  739. end)
  740. updated = true
  741. end)
  742.  
  743. if not updated then
  744. pcall(function() ms.Source = code end)
  745. end
  746.  
  747. local opened = false
  748. pcall(function()
  749. ScriptEditorService:OpenScriptDocumentAsync(ms)
  750. opened = true
  751. end)
  752.  
  753. statLbl.Text = (" OK: %s\n OBJECTS: %d\n ACTIVE ANIMS: %d\n DEST: %s"):format(
  754. expName, count, #activeAnims, opened and "SCRIPT EDITOR" or "SERVERSTORAGE"
  755. )
  756. end)
  757.  
  758. return pnl
  759. end
  760.  
  761. if plugin then
  762. local bar = plugin:CreateToolbar("Model Saver")
  763. local btn = bar:CreateButton("Save", "Save Model", "")
  764. local gui = Instance.new("ScreenGui")
  765. gui.Name = "ModelSaverGui"
  766. gui.Parent = CoreGui
  767. gui.Enabled = false
  768. buildUI(gui)
  769. btn.Click:Connect(function() gui.Enabled = not gui.Enabled end)
  770. else
  771. local gui = Instance.new("ScreenGui", CoreGui)
  772. gui.Name = "ModelSaverGui"
  773. buildUI(gui)
  774. end
Advertisement
Add Comment
Please, Sign In to add comment