Advertisement
Xane_MM

Universal Roblox Script - Xane's Model Recreator (Public)

Jan 11th, 2024 (edited)
15,451
8
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 56.58 KB | Gaming | 9 1
  1. --[[
  2.     XANE'S MODEL RECREATOR (SCRIPT/DECONSTRUCTOR PHASE) V3, WRITTEN IN 2024
  3.     LAST UPDATED ON AUGUST 3RD, 2024
  4.    
  5.     A personal script of mine, released to the public to enable anyone to save models (and other supported instances)
  6.     from any Roblox experience (as long as your Roblox app doesn't crash in the process of loading it)! Execute this
  7.     script and a small button will appear at the top of your screen. Click/tap on it to open the GUI, then click on
  8.     one of the four buttons at the top of the window to scan through that container, making a list of all of the
  9.     supported Instances!
  10.    
  11.     Click on an instance's icon to include it in this save (and do that again to deselect it), and use the page
  12.     buttons at the bottom of the window to switch between pages of 100 Instances. If you're unsure of which
  13.     Instance you're selecting, a box will be drawn around them, and you can use the camera button to focus on that
  14.     object, if it supports changing the camera's subject to it. To return to your character, right-click or long
  15.     tap that button.
  16.    
  17.     Instances can be selected from one or all of the containers. Switch between them using the top row of buttons,
  18.     select anything that you want, give a name to this export using the text box at the bottom-right corner, then
  19.     with one click/tap of the save button, your selection will be transformed into JSON files in your executor's
  20.     "workspace" folder.
  21.    
  22.     GLOBAL ARRAY (_G) SETTINGS
  23.     For those in the know, you can change a couple settings that aren't accessible from the GUI by setting certain
  24.     values in the _G dictionary! Here's the complete list of changes you can make:
  25.    
  26.     _G.PageLength (number) - Adjusts the length of each set of Instances which are shown in the GUI. By default,
  27.     this is 24, but changing this will make each page longer or shorter. If it's small enough, the whole page fits!
  28.    
  29.     _G.AntiLagInterval (number) - To ensure the progress UI updates instead of the script freezing the client while
  30.     it's indexing or saving instances, the recursive iteration function intentionally waits a frame after it checks
  31.     every 25th Instance. If you have a weaker device, increase this to improve performance, though it'll come at
  32.     the cost of longer indexing/saving times. If on a stronger device, I recommend decreasing this or using 0.
  33. ]]--
  34.  
  35. if not game:IsLoaded() then game.Loaded:Wait() end
  36. task.wait(0.975)
  37. print("Xane's Model Recreator GUI is initializing... (Please work!)")
  38.  
  39. -- SERVICES
  40. local Players           = game:GetService("Players")    -- These are the three locations that can be accessed using this script.
  41. local Lighting          = game:GetService("Lighting")
  42. local ReplicatedStorage = game:GetService("ReplicatedStorage")
  43. local ServerStorage     = game:GetService("ServerStorage")  -- This container stores the four "container imposter" Models for redirection.
  44. local MaterialService   = game:GetService("MaterialService")
  45.  
  46. -- REFERENCES (PARTIAL, NOT INCLUDING GUI)
  47. local PlayerGui         = Players.LocalPlayer:WaitForChild("PlayerGui")
  48. local CoreGui           = game:GetService("CoreGui")
  49.  
  50. -- STATIC DATA
  51. local DefaultMessage    = "Welcome to Xane's Model Recreator v3, the ultimate instance capturer! Please choose a container above, select which instances you want, name it, then save!"
  52.  
  53. --- This script uses Xane's Recreator API! The boilerplate code below will load it from one of its locations online, trying each in order.
  54. local RecreatorAPIMirrors = {
  55.     'https://pastebin.com/raw/DL77J2LY',
  56.     'https://raw.githubusercontent.com/Xane123/Roblox-Scripts/main/API_Recreation.luau'
  57. }
  58. -- Prepare our environment; If this executor supports it, use its getgenv() function, falling back on the detectable _G if it isn't available.
  59. local env               = getgenv and getgenv() or _G
  60.  
  61. if env then
  62.     if not env.XRecreator then
  63.         for i,mirror in RecreatorAPIMirrors do
  64.             -- Attempt to load Xane's Recreator API from Pastebin,
  65.             local success, errormsg = pcall(function() loadstring(game:HttpGet(mirror))() end)
  66.             if success then
  67.                 print("Loaded Recreator API from source", i, "of", #RecreatorAPIMirrors)
  68.                 break
  69.             else
  70.                 warn("Recreator API couldn't load from " .. mirror .. ". It's either removed, or isn't available in your region.")
  71.             end
  72.         end
  73.        
  74.         if env.XRecreator and env.XRecreator.Save then
  75.             print("Recreator API has been imported and setup successfully!")
  76.         else
  77.             game:GetService("Debris"):AddItem(script, 5)
  78.             local VisibleError = Instance.new("Hint", game:GetService("CoreGui"))
  79.             VisibleError.Text = "Error: Xane's Recreator API couldn't load/initialize! Please load a local copy of that script " ..
  80.                 "then execute this one again, or try again."
  81.             game:GetService("Debris"):AddItem(VisibleError,7)
  82.             error("The Recreator API couldn't load or initialize. This script depends on it, so execution cannot continue.")
  83.         end
  84.     else print("The recreator API is already set up! Its functions will be used by this script.")
  85.     end
  86. else
  87.     game:GetService("Debris"):AddItem(script, 1)
  88.     error("Couldn't find a destination for the recreation APIs! (Your executor is really bad if you see this!)")
  89. end
  90.  
  91. -- XANE'S RECREATOR API TYPES
  92. -- The format used by the API's internal Instance list. This is used by both the API and this GUI script.
  93. type InternalListEntry      = {
  94.     Instance            : Instance, -- Instance which this entry represents. Its class and name are used when generating the visible list.
  95.     Level               : number    -- Represents the depth this instance was placed at in the hierarchy during the scan. 0 is the container itself.
  96. }
  97.  
  98. -- List item definition for the currently shown subset of the full list, generated as needed.
  99. type VisibleListEntry       = {
  100.     CheckboxClickEvent  : RBXScriptConnection,  -- Connection which lets the user (de)select this item. (This also updates its icon.)
  101.     CameraFocusEvent    : RBXScriptConnection,  -- Click event for a button which makes the user's camera focus on this instance.
  102.     CamRevertEvent1     : RBXScriptConnection,  -- Right-click event, which brings the camera back to the player.
  103.     CamRevertEvent2     : RBXScriptConnection,  -- Alternative camera reverting event, for mobile devices (long tap).
  104.     Instance            : Instance,             -- Reference to this instance, used to access its properties if needed.
  105.     RowBase             : Frame,                -- A reference to this row's container Frame.
  106.     Checkbox            : TextButton,           -- The toggle-box found at the left side of this row/entry.
  107.     SelectBox           : SelectionBox,         -- This Instance's SelectionBox, which is created and destroyed as needed upon its selection.
  108.     IsSelectAllRow      : boolean               -- If TRUE, selecting this row will deselect all instances within the selected container.
  109. }
  110.  
  111. type ClassDefinition        = {
  112.     ListView                        : {     -- Properties that customize how this instance is displayed in the list (only used in the script).
  113.         Icon                        : string,
  114.         CreateTest                  : boolean,
  115.         CanView                     : string,
  116.     },
  117.     Props                           : {string}      -- List of properties that should be saved or loaded to/from JSON for this instance.
  118. }
  119.  
  120. type ModelRecreatorStruct   = {
  121.     -- FUNCTIONS
  122.     Select      : (_mode:"set"|"add"|"remove", _list:{Instance}) -> (), -- Updates the API's selection.
  123.     MakeList    : (_baseInst : Instance) -> (nil),                      -- Generates a series of list entries, for GUI's that display one.
  124.     Save        : (_name:string,_rescan:boolean) -> (boolean),          -- Saves selection to files. Will fail on invalid filenames.
  125.     CustomizeProgressBox:(_message : string, _total : number) -> (),    -- Affects the text shown in "saving"/"indexing" GUI (may be unusable).
  126.     ActivateAPI : (_use : boolean) -> boolean,                          -- Requests to use API functions. If in use, this returns FALSE.
  127.     SetStatusGui: (_gui:TextLabel) -> (nil),                            -- Changes GUI that save status is shown in. Set to nil to use default.
  128.     OnSaveDone  : (_success : boolean) -> (),                           -- External code ran upon finishing. Used for other scripts' cleanup.
  129.     IsInstanceAllowed : (_instance : Instance) -> (boolean),            -- Checks if an Instance's type is safe to save. (Model Recreator v3)
  130.  
  131.     -- VARIABLES
  132.     Reserved    : boolean,                                              -- Set to TRUE after a script calls ActivateAPI().
  133.     IgnoreAttrib: string,                                               -- Instances with this attribute won't be scanned.
  134.     Selection   : {Instance},                                           -- List of instances marked to be saved.
  135.     FullList    : {InternalListEntry},                                  -- List generated by MakeList(), which can be used by scripts' GUIs.
  136.     StatusGui   : TextLabel,                                            -- Reference to current label that save/index statuses are shown in.
  137.     PageLength  : number,                                               -- How many instances SHOULD be shown on list pages. (Not used by API.)
  138.     AntiLagInterval : number,                                           -- How many instances to parse before waiting, to reduce lag.
  139.     ClassData   : {ClassDefinition}                                     -- This script's "Roblox class API" (read-onlu).
  140. }
  141. local Recreator : ModelRecreatorStruct = env.XRecreator -- Let's simplify references to the API by just defining a new "shortcut" variable.
  142. if not Recreator.ActivateAPI(true) then -- Attempt to claim the API; If it's in use, the script will stop here.
  143.     warn("Another script is using Xane's recreator API! Please close that script or rejoin (if it doesn't have a 'close' button).")
  144.     script:Destroy()       
  145.     return false
  146. end
  147.  
  148. Recreator.IgnoreAttrib = { "XaneProtectedDoNotShowInRecreatorGui" } -- This script's GUI and SelectionBox instances can't be saved.
  149. if not Recreator.AntiLagInterval or type(Recreator.AntiLagInterval) ~= "number" or
  150.     (type(Recreator.AntiLagInterval) == "number" and Recreator.AntiLagInterval <= 0)
  151. then
  152.     Recreator.AntiLagInterval = 25
  153. end
  154.  
  155. --[[
  156.     Roblox2Lua
  157.     ----------
  158.    
  159.     This code was generated using
  160.     Deluct's Roblox2Lua plugin.
  161. ]]--
  162.  
  163. --// Instances
  164.  
  165. local xane_mdlrecreator_gui = Instance.new("ScreenGui")
  166. xane_mdlrecreator_gui.IgnoreGuiInset = false
  167. xane_mdlrecreator_gui.ResetOnSpawn = true
  168. xane_mdlrecreator_gui.ZIndexBehavior = Enum.ZIndexBehavior.Sibling
  169. xane_mdlrecreator_gui.Name = "XaneMDLRecreatorGUI"
  170. xane_mdlrecreator_gui:SetAttribute("XaneProtectedDoNotShowInRecreatorGui",true) -- Hide this instance from the Recreator script itself.
  171. xane_mdlrecreator_gui.Parent = CoreGui
  172.  
  173. -- TOP BUTTONS (HIDE & CANCEL BUTTONS)
  174. local toggle_frame = Instance.new("Frame")
  175. toggle_frame.AnchorPoint = Vector2.new(0.5, 0)
  176. toggle_frame.BackgroundTransparency = 1
  177. toggle_frame.BorderSizePixel = 0
  178. toggle_frame.Position = UDim2.new(0.5, 0, 0.0625, 0)
  179. toggle_frame.Size = UDim2.new(0.25, 0, 0.09375, 0)
  180. toggle_frame.SizeConstraint = Enum.SizeConstraint.RelativeYY
  181. toggle_frame.Visible = true
  182. toggle_frame.Name = "ToggleFrame"
  183. toggle_frame.Parent = xane_mdlrecreator_gui
  184.  
  185. local toggle_list_layout = Instance.new("UIListLayout")
  186. toggle_list_layout.Name = "ToggleBtnList"
  187. toggle_list_layout.FillDirection = Enum.FillDirection.Horizontal
  188. toggle_list_layout.Padding = UDim.new(0.001,0)
  189. toggle_list_layout.Parent = toggle_frame
  190.  
  191. local toggle_button = Instance.new("TextButton")
  192. toggle_button.Font = Enum.Font.RobotoCondensed
  193. toggle_button.Text = "Save Instances!"
  194. toggle_button.TextColor3 = Color3.new(1, 1, 1)
  195. toggle_button.TextScaled = true
  196. toggle_button.TextSize = 14
  197. toggle_button.TextStrokeTransparency = 0
  198. toggle_button.TextWrapped = true
  199. -- toggle_button.AnchorPoint = Vector2.new(0.5, 0.5)
  200. toggle_button.BackgroundColor3 = Color3.new(0.882353, 0.756863, 0.615686)
  201. toggle_button.BorderSizePixel = 0
  202. -- toggle_button.Position = UDim2.new(0.5, 0, 0.5, 0)
  203. toggle_button.Size = UDim2.fromScale(0.5, 1)
  204. toggle_button.LayoutOrder = 1
  205. toggle_button.Visible = true
  206. toggle_button.Name = "ToggleButton"
  207. toggle_button.Parent = toggle_frame
  208.  
  209. local clear_button = Instance.new("TextButton")
  210. clear_button.Font = Enum.Font.RobotoCondensed
  211. clear_button.Text = "Clear Selection"
  212. clear_button.TextColor3 = Color3.new(1, 1, 1)
  213. clear_button.TextScaled = true
  214. clear_button.TextSize = 14
  215. clear_button.TextStrokeTransparency = 0
  216. clear_button.TextWrapped = true
  217. -- clear_button.AnchorPoint = Vector2.new(0.5, 0.5)
  218. clear_button.BackgroundColor3 = Color3.new(0.882353, 0.623529, 0.686275)
  219. clear_button.BorderSizePixel = 0
  220. -- clear_button.Position = UDim2.new(0.5, 0, 0.5, 0)
  221. clear_button.Size = UDim2.fromScale(0.5, 1)
  222. clear_button.LayoutOrder = 2
  223. clear_button.Visible = false
  224. clear_button.Name = "ClearButton"
  225. clear_button.Parent = toggle_frame
  226.  
  227. local close_button = Instance.new("TextButton")
  228. close_button.Font = Enum.Font.RobotoCondensed
  229. close_button.Text = "End Script..."
  230. close_button.TextColor3 = Color3.new(1, 1, 1)
  231. close_button.TextScaled = true
  232. close_button.TextSize = 14
  233. close_button.TextStrokeTransparency = 0
  234. close_button.TextWrapped = true
  235. close_button.BackgroundColor3 = Color3.new(0.75, 0.4, 0.625)
  236. close_button.BorderSizePixel = 0
  237. close_button.Size = UDim2.fromScale(0.5, 1)
  238. close_button.LayoutOrder = 3
  239. close_button.Visible = true -- The close button is only visible when the main UI is hidden.
  240. close_button.Name = "CloseButton"
  241. close_button.Parent = toggle_frame
  242.  
  243. local uicorner = Instance.new("UICorner")
  244. uicorner.CornerRadius = UDim.new(0.125, 0)
  245. uicorner.Parent = toggle_button
  246.  
  247. -- THE MAIN WINDOW
  248. local main_frame = Instance.new("Frame")
  249. main_frame.AnchorPoint = Vector2.one / 2
  250. main_frame.BackgroundColor3 = Color3.new(0.219608, 0.321569, 0.627451)
  251. main_frame.BackgroundTransparency = 0.25
  252. main_frame.BorderColor3 = Color3.new(0, 0, 0)
  253. main_frame.BorderSizePixel = 0
  254. main_frame.Position = UDim2.fromScale(0.5, 1.75)    -- This Frame starts off-screen, revealed by clicking the toggle button at the top of the screen.
  255. main_frame.Size = UDim2.fromScale(0.75, 0.725)
  256. main_frame.Visible = true
  257. main_frame.ZIndex = 2
  258. main_frame.Name = "MainFrame"
  259. main_frame.Parent = xane_mdlrecreator_gui
  260.  
  261. local uicorner_2 = Instance.new("UICorner")
  262. uicorner_2.CornerRadius = UDim.new(0.025, 0)
  263. uicorner_2.Parent = main_frame
  264.  
  265. --- STATUS MESSAGE
  266. local message = Instance.new("TextLabel")
  267. message.Font = Enum.Font.Gotham
  268. message.Text = DefaultMessage
  269. message.TextColor3 = Color3.new(1, 1, 1)
  270. message.TextScaled = true
  271. message.TextSize = 14
  272. message.TextStrokeTransparency = 0
  273. message.TextWrapped = true
  274. message.AutomaticSize = Enum.AutomaticSize.Y
  275. message.BackgroundColor3 = Color3.new(1, 1, 1)
  276. message.BackgroundTransparency = 1
  277. message.BorderColor3 = Color3.new(0, 0, 0)
  278. message.BorderSizePixel = 0
  279. message.LayoutOrder = 2
  280. message.Size = UDim2.new(1, 0, 0.0625, 0)
  281. message.Visible = true
  282. message.Name = "Message"
  283. message.Parent = main_frame
  284.  
  285. local uitext_size_constraint = Instance.new("UITextSizeConstraint")
  286. uitext_size_constraint.MaxTextSize = 24
  287. uitext_size_constraint.MinTextSize = 8
  288. uitext_size_constraint.Parent = message
  289.  
  290. local top_bar = Instance.new("Frame")
  291. top_bar.AnchorPoint = Vector2.xAxis / 2
  292. top_bar.BackgroundTransparency = 1
  293. top_bar.BorderSizePixel = 0
  294. top_bar.LayoutOrder = 1
  295. top_bar.Position = UDim2.new(0.5, 0, 0, 4)
  296. top_bar.Size = UDim2.new(1,-8, 0.094,0)
  297. top_bar.Visible = true
  298. top_bar.Name = "TopBar"
  299. top_bar.Parent = main_frame
  300.  
  301. local topbar_layout = Instance.new("UIListLayout")
  302. topbar_layout.HorizontalFlex = Enum.UIFlexAlignment.None
  303. topbar_layout.Padding = UDim.new(0, 4)
  304. topbar_layout.FillDirection = Enum.FillDirection.Horizontal
  305. topbar_layout.HorizontalAlignment = Enum.HorizontalAlignment.Right
  306. topbar_layout.SortOrder = Enum.SortOrder.LayoutOrder
  307. topbar_layout.Parent = top_bar
  308.  
  309. -- CONTAINER BUTTONS
  310. local container1 = Instance.new("TextButton")
  311. container1.Font = Enum.Font.RobotoCondensed
  312. container1.Text = "🌎Workspace"
  313. container1.TextColor3 = Color3.new(1, 1, 1)
  314. container1.TextScaled = true
  315. container1.TextSize = 14
  316. container1.TextStrokeTransparency = 0
  317. container1.TextWrapped = true
  318. container1.Modal = true
  319. container1.AutomaticSize = Enum.AutomaticSize.X
  320. container1.BackgroundColor3 = Color3.new(0.596078, 0.921569, 0.921569)
  321. container1.BorderColor3 = Color3.new(0, 0, 0)
  322. container1.BorderSizePixel = 0
  323. container1.LayoutOrder = 1
  324. container1.Size = UDim2.fromScale(0.125,1)
  325. container1.SizeConstraint = Enum.SizeConstraint.RelativeXY
  326. container1.Visible = true
  327. container1.Name = "Container1"
  328. container1.Parent = top_bar
  329.  
  330. local uitext_size_constraint_2 = Instance.new("UITextSizeConstraint")
  331. uitext_size_constraint_2.MaxTextSize = 24
  332. uitext_size_constraint_2.MinTextSize = 6
  333. uitext_size_constraint_2.Parent = container1
  334.  
  335. local container4 = Instance.new("TextButton")
  336. container4.Font = Enum.Font.RobotoCondensed
  337. container4.Text = "📦ReplicatedStorage"
  338. container4.TextColor3 = Color3.new(1, 1, 1)
  339. container4.TextScaled = true
  340. container4.TextSize = 14
  341. container4.TextStrokeTransparency = 0
  342. container4.TextWrapped = true
  343. container4.Modal = true
  344. container4.AutomaticSize = Enum.AutomaticSize.X
  345. container4.BackgroundColor3 = Color3.new(0.596078, 0.921569, 0.921569)
  346. container4.BorderColor3 = Color3.new(0, 0, 0)
  347. container4.BorderSizePixel = 0
  348. container4.LayoutOrder = 4
  349. container4.Size = UDim2.fromScale(0.125,1)
  350. container4.SizeConstraint = Enum.SizeConstraint.RelativeXY
  351. container4.Visible = true
  352. container4.Name = "Container4"
  353. container4.Parent = top_bar
  354.  
  355. local uitext_size_constraint_3 = Instance.new("UITextSizeConstraint")
  356. uitext_size_constraint_3.MaxTextSize = 24
  357. uitext_size_constraint_3.MinTextSize = 6
  358. uitext_size_constraint_3.Parent = container4
  359.  
  360. local container3 = Instance.new("TextButton")
  361. container3.Font = Enum.Font.RobotoCondensed
  362. container3.Text = "🌟Lighting"
  363. container3.TextColor3 = Color3.new(1, 1, 1)
  364. container3.TextScaled = true
  365. container3.TextSize = 14
  366. container3.TextStrokeTransparency = 0
  367. container3.TextWrapped = true
  368. container3.Modal = true
  369. container3.AutomaticSize = Enum.AutomaticSize.X
  370. container3.BackgroundColor3 = Color3.new(0.596078, 0.921569, 0.921569)
  371. container3.BorderColor3 = Color3.new(0, 0, 0)
  372. container3.BorderSizePixel = 0
  373. container3.LayoutOrder = 3
  374. container3.Size = UDim2.fromScale(0.125,1)
  375. container3.SizeConstraint = Enum.SizeConstraint.RelativeXY
  376. container3.Visible = true
  377. container3.Name = "Container3"
  378. container3.Parent = top_bar
  379.  
  380. local uitext_size_constraint_4 = Instance.new("UITextSizeConstraint")
  381. uitext_size_constraint_4.MaxTextSize = 24
  382. uitext_size_constraint_4.MinTextSize = 6
  383. uitext_size_constraint_4.Parent = container3
  384.  
  385. local container2 = Instance.new("TextButton")
  386. container2.Font = Enum.Font.RobotoCondensed
  387. container2.Text = "👥Players"
  388. container2.TextColor3 = Color3.new(1, 1, 1)
  389. container2.TextScaled = true
  390. container2.TextSize = 14
  391. container2.TextStrokeTransparency = 0
  392. container2.TextWrapped = true
  393. container2.Modal = true
  394. container2.AutomaticSize = Enum.AutomaticSize.X
  395. container2.BackgroundColor3 = Color3.new(0.596078, 0.921569, 0.921569)
  396. container2.BorderColor3 = Color3.new(0, 0, 0)
  397. container2.BorderSizePixel = 0
  398. container2.LayoutOrder = 2
  399. container2.Size = UDim2.fromScale(0.125,1)
  400. container2.SizeConstraint = Enum.SizeConstraint.RelativeXY
  401. container2.Visible = true
  402. container2.Name = "Container2"
  403. container2.Parent = top_bar
  404.  
  405. local container5 = Instance.new("TextButton")
  406. container5.Font = Enum.Font.RobotoCondensed
  407. container5.Text = "🕺🏼Characters"
  408. container5.TextColor3 = Color3.new(1, 1, 1)
  409. container5.TextScaled = true
  410. container5.TextSize = 14
  411. container5.TextStrokeTransparency = 0
  412. container5.TextWrapped = true
  413. container5.Modal = true
  414. container5.AutomaticSize = Enum.AutomaticSize.X
  415. container5.BackgroundColor3 = Color3.new(0.596078, 0.921569, 0.921569)
  416. container5.BorderColor3 = Color3.new(0, 0, 0)
  417. container5.BorderSizePixel = 0
  418. container5.LayoutOrder = 0
  419. container5.Size = UDim2.fromScale(0.1,1)
  420. container5.SizeConstraint = Enum.SizeConstraint.RelativeXY
  421. container5.Visible = true
  422. container5.Name = "Container5"
  423. container5.Parent = top_bar
  424.  
  425. local uitext_size_constraint_5 = Instance.new("UITextSizeConstraint")
  426. uitext_size_constraint_5.MaxTextSize = 24
  427. uitext_size_constraint_5.MinTextSize = 6
  428. uitext_size_constraint_5.Parent = container2
  429.  
  430. local uilist_layout_2 = Instance.new("UIListLayout")
  431. uilist_layout_2.Padding = UDim.new(0, 4)
  432. uilist_layout_2.VerticalFlex = Enum.UIFlexAlignment.Fill
  433. uilist_layout_2.HorizontalAlignment = Enum.HorizontalAlignment.Center
  434. uilist_layout_2.SortOrder = Enum.SortOrder.LayoutOrder
  435. uilist_layout_2.Parent = main_frame
  436.  
  437. local instance_list = Instance.new("ScrollingFrame")
  438. instance_list.AutomaticCanvasSize = Enum.AutomaticSize.Y
  439. instance_list.CanvasSize = UDim2.new(0, 0, 0, 0)
  440. instance_list.BackgroundColor3 = Color3.new(1, 1, 1)
  441. instance_list.BackgroundTransparency = 1
  442. instance_list.BorderColor3 = Color3.new(0, 0, 0)
  443. instance_list.BorderSizePixel = 0
  444. instance_list.LayoutOrder = 3
  445. instance_list.Size = UDim2.new(1, 0, 0.675, 0)
  446. instance_list.Visible = true
  447. instance_list.Name = "InstanceList"
  448. instance_list.Parent = main_frame
  449.  
  450. local uilist_layout_3 = Instance.new("UIListLayout")
  451. uilist_layout_3.Padding = UDim.new(0, 8)
  452. uilist_layout_3.HorizontalAlignment = Enum.HorizontalAlignment.Center
  453. uilist_layout_3.SortOrder = Enum.SortOrder.LayoutOrder
  454. uilist_layout_3.Parent = instance_list
  455.  
  456. -- "EXPLORER" ROW TEMPLATE
  457. local template_entry = Instance.new("Frame")
  458. template_entry.BackgroundColor3 = Color3.new(1, 1, 1)
  459. template_entry.BackgroundTransparency = 0.875
  460. template_entry.BorderColor3 = Color3.new(0, 0, 0)
  461. template_entry.BorderSizePixel = 0
  462. template_entry.Size = UDim2.new(1, 0, 0.0925, 0)
  463. template_entry.Visible = false
  464. template_entry.Name = "TemplateEntry"
  465. template_entry.Parent = instance_list
  466.  
  467. local checkbox_button = Instance.new("TextButton")
  468. checkbox_button.Font = Enum.Font.SourceSans
  469. checkbox_button.Text = "❗"
  470. checkbox_button.TextColor3 = Color3.new(0, 0, 0)
  471. checkbox_button.TextScaled = true
  472. checkbox_button.TextSize = 14
  473. checkbox_button.TextWrapped = true
  474. checkbox_button.Modal = true
  475. checkbox_button.BackgroundColor3 = Color3.new(1, 1, 1)
  476. checkbox_button.BackgroundTransparency = 1
  477. checkbox_button.BorderColor3 = Color3.new(0, 0, 0)
  478. checkbox_button.BorderSizePixel = 0
  479. checkbox_button.LayoutOrder = 2
  480. checkbox_button.Size = UDim2.new(1, 0, 1, 0)
  481. checkbox_button.SizeConstraint = Enum.SizeConstraint.RelativeYY
  482. checkbox_button.Visible = true
  483. checkbox_button.Name = "SelectButton"
  484. checkbox_button.Parent = template_entry
  485.  
  486. local uilist_layout_4 = Instance.new("UIListLayout")
  487. uilist_layout_4.HorizontalFlex = Enum.UIFlexAlignment.None
  488. uilist_layout_4.FillDirection = Enum.FillDirection.Horizontal
  489. uilist_layout_4.SortOrder = Enum.SortOrder.LayoutOrder
  490. uilist_layout_4.Parent = template_entry
  491.  
  492. local indent = Instance.new("Frame")
  493. indent.BackgroundColor3 = Color3.new(1, 1, 1)
  494. indent.BackgroundTransparency = 1
  495. indent.BorderSizePixel = 0
  496. indent.LayoutOrder = 1
  497. indent.Size = UDim2.fromScale(0.01825, 1)
  498. indent.Visible = false
  499. indent.Name = "Indent"
  500. indent.Parent = template_entry
  501.  
  502. local indent_outline = Instance.new("UIStroke")
  503. indent_outline.Color = Color3.new(0.825,0.933,1)
  504. indent_outline.Transparency = 0.725
  505. indent_outline.Thickness = 1
  506. indent_outline.Parent = indent
  507.  
  508. local inst_name = Instance.new("TextLabel")
  509. inst_name.Font = Enum.Font.RobotoCondensed
  510. inst_name.Text = "Template Row"
  511. inst_name.TextColor3 = Color3.new(1, 1, 1)
  512. inst_name.TextScaled = true
  513. inst_name.TextSize = 14
  514. inst_name.TextStrokeTransparency = 0
  515. inst_name.TextWrapped = true
  516. inst_name.TextXAlignment = Enum.TextXAlignment.Left
  517. inst_name.BackgroundColor3 = Color3.new(1, 1, 1)
  518. inst_name.BackgroundTransparency = 1
  519. inst_name.BorderColor3 = Color3.new(0, 0, 0)
  520. inst_name.BorderSizePixel = 0
  521. inst_name.LayoutOrder = 3
  522. inst_name.Size = UDim2.fromScale(0.75, 1)
  523. inst_name.Visible = true
  524. inst_name.Name = "InstName"
  525. inst_name.Parent = template_entry
  526.  
  527. local uitext_size_constraint_6 = Instance.new("UITextSizeConstraint")
  528. uitext_size_constraint_6.MaxTextSize = 28
  529. uitext_size_constraint_6.MinTextSize = 6
  530. uitext_size_constraint_6.Parent = inst_name
  531.  
  532. local cam_action_btn = Instance.new("TextButton")
  533. cam_action_btn.Font = Enum.Font.SourceSans
  534. cam_action_btn.Text = "📸"
  535. cam_action_btn.TextColor3 = Color3.new(0, 0, 0)
  536. cam_action_btn.TextScaled = true
  537. cam_action_btn.TextSize = 14
  538. cam_action_btn.TextWrapped = true
  539. cam_action_btn.Modal = true
  540. cam_action_btn.BackgroundColor3 = Color3.new(1, 1, 1)
  541. cam_action_btn.BackgroundTransparency = 1
  542. cam_action_btn.BorderColor3 = Color3.new(0, 0, 0)
  543. cam_action_btn.BorderSizePixel = 0
  544. cam_action_btn.LayoutOrder = 4
  545. cam_action_btn.Size = UDim2.new(1, 0, 1, 0)
  546. cam_action_btn.SizeConstraint = Enum.SizeConstraint.RelativeYY
  547. cam_action_btn.Visible = true
  548. cam_action_btn.Name = "CamActionButton"
  549. cam_action_btn.Parent = template_entry
  550.  
  551. local cam_revert_btn = Instance.new("TextButton")
  552. cam_revert_btn.Font = Enum.Font.SourceSans
  553. cam_revert_btn.Text = "🔁"
  554. cam_revert_btn.TextColor3 = Color3.new(0, 0, 0)
  555. cam_revert_btn.TextTransparency = 1 -- This button was going to be used, but now its functions are handled by the "camera" button to its left.
  556. cam_revert_btn.TextScaled = true
  557. cam_revert_btn.TextSize = 14
  558. cam_revert_btn.TextWrapped = true
  559. cam_revert_btn.Modal = true
  560. cam_revert_btn.BackgroundColor3 = Color3.new(1, 1, 1)
  561. cam_revert_btn.BackgroundTransparency = 1
  562. cam_revert_btn.BorderColor3 = Color3.new(0, 0, 0)
  563. cam_revert_btn.BorderSizePixel = 0
  564. cam_revert_btn.LayoutOrder = 5
  565. cam_revert_btn.Size = UDim2.new(1, 0, 1, 0)
  566. cam_revert_btn.SizeConstraint = Enum.SizeConstraint.RelativeYY
  567. cam_revert_btn.Visible = true
  568. cam_revert_btn.Name = "CamRevertButton"
  569. cam_revert_btn.Parent = template_entry
  570.  
  571. -- BOTTOM BAR (PAGINATION BUTTONS, NAME ENTRY, AND SAVE BUTTON)
  572. local bot_bar = Instance.new("Frame")
  573. bot_bar.AnchorPoint = Vector2.new(0.5,1)
  574. bot_bar.BackgroundTransparency = 1
  575. bot_bar.BorderSizePixel = 0
  576. bot_bar.LayoutOrder = 4
  577. bot_bar.Position = UDim2.new(0.5, 0, 0, -4)
  578. bot_bar.Size = UDim2.new(1,-8, 0.094,0)
  579. bot_bar.Visible = true
  580. bot_bar.Name = "BottomBar"
  581. bot_bar.Parent = main_frame
  582.  
  583. local botbar_layout = Instance.new("UIListLayout")
  584. botbar_layout.HorizontalFlex = Enum.UIFlexAlignment.None
  585. botbar_layout.Padding = UDim.new(0, 4)
  586. botbar_layout.FillDirection = Enum.FillDirection.Horizontal
  587. botbar_layout.HorizontalAlignment = Enum.HorizontalAlignment.Right
  588. botbar_layout.SortOrder = Enum.SortOrder.LayoutOrder
  589. botbar_layout.Parent = bot_bar
  590.  
  591. local prevPageButton = Instance.new("TextButton")
  592. prevPageButton.Font = Enum.Font.RobotoCondensed
  593. prevPageButton.Text = "◀ Prev"
  594. prevPageButton.TextColor3 = Color3.new(1, 1, 1)
  595. prevPageButton.TextScaled = true
  596. prevPageButton.TextSize = 14
  597. prevPageButton.TextStrokeTransparency = 0
  598. prevPageButton.TextWrapped = true
  599. prevPageButton.Modal = true
  600. prevPageButton.AutomaticSize = Enum.AutomaticSize.X
  601. prevPageButton.BackgroundColor3 = Color3.new(0.596078, 0.921569, 0.921569)
  602. prevPageButton.BorderColor3 = Color3.new(0, 0, 0)
  603. prevPageButton.BorderSizePixel = 0
  604. prevPageButton.LayoutOrder = 1
  605. prevPageButton.Size = UDim2.fromScale(0.1125,1)
  606. prevPageButton.SizeConstraint = Enum.SizeConstraint.RelativeXY
  607. prevPageButton.Visible = true
  608. prevPageButton.Name = "PrevPage"
  609. prevPageButton.Parent = bot_bar
  610.  
  611. local prevPageBtnSizeConstraint = Instance.new("UITextSizeConstraint")
  612. prevPageBtnSizeConstraint.MaxTextSize = 24
  613. prevPageBtnSizeConstraint.MinTextSize = 6
  614. prevPageBtnSizeConstraint.Parent = prevPageButton
  615.  
  616. local nextPageButton = Instance.new("TextButton")
  617. nextPageButton.Font = Enum.Font.RobotoCondensed
  618. nextPageButton.Text = "Next ▶"
  619. nextPageButton.TextColor3 = Color3.new(1, 1, 1)
  620. nextPageButton.TextScaled = true
  621. nextPageButton.TextSize = 14
  622. nextPageButton.TextStrokeTransparency = 0
  623. nextPageButton.TextWrapped = true
  624. nextPageButton.Modal = true
  625. nextPageButton.AutomaticSize = Enum.AutomaticSize.X
  626. nextPageButton.BackgroundColor3 = Color3.new(0.596078, 0.921569, 0.921569)
  627. nextPageButton.BorderColor3 = Color3.new(0, 0, 0)
  628. nextPageButton.BorderSizePixel = 0
  629. nextPageButton.LayoutOrder = 2
  630. nextPageButton.Size = UDim2.fromScale(0.1125,1)
  631. nextPageButton.SizeConstraint = Enum.SizeConstraint.RelativeXY
  632. nextPageButton.Visible = true
  633. nextPageButton.Name = "NextPage"
  634. nextPageButton.Parent = bot_bar
  635.  
  636. local nextPageBtnSizeConstraint = Instance.new("UITextSizeConstraint")
  637. nextPageBtnSizeConstraint.MaxTextSize = 24
  638. nextPageBtnSizeConstraint.MinTextSize = 6
  639. nextPageBtnSizeConstraint.Parent = nextPageButton
  640.  
  641. local PoseCharCheckbox = Instance.new("TextButton")
  642. PoseCharCheckbox.Font = Enum.Font.RobotoCondensed
  643. PoseCharCheckbox.Text = "💃🏼Ignore characters"
  644. PoseCharCheckbox.TextColor3 = Color3.new(1, 1, 1)
  645. PoseCharCheckbox.TextScaled = true
  646. PoseCharCheckbox.TextSize = 14
  647. PoseCharCheckbox.TextStrokeTransparency = 0
  648. PoseCharCheckbox.TextWrapped = true
  649. PoseCharCheckbox.Modal = true
  650. PoseCharCheckbox.AutomaticSize = Enum.AutomaticSize.X
  651. PoseCharCheckbox.BackgroundColor3 = Color3.new(0.596078, 0.921569, 0.921569)
  652. PoseCharCheckbox.BorderColor3 = Color3.new(0, 0, 0)
  653. PoseCharCheckbox.BorderSizePixel = 0
  654. PoseCharCheckbox.LayoutOrder = 3
  655. PoseCharCheckbox.Size = UDim2.fromScale(0.2,1)
  656. PoseCharCheckbox.SizeConstraint = Enum.SizeConstraint.RelativeXY
  657. PoseCharCheckbox.Visible = true
  658. PoseCharCheckbox.Name = "CBox_PoseChars"
  659. PoseCharCheckbox.Parent = bot_bar
  660.  
  661. local pose_chars_size_constraint = Instance.new("UITextSizeConstraint")
  662. pose_chars_size_constraint.MaxTextSize = 24
  663. pose_chars_size_constraint.MinTextSize = 6
  664. pose_chars_size_constraint.Parent = PoseCharCheckbox
  665.  
  666. -- JSON MODEL NAME ENTRY FIELD/TEXTBOX
  667. local filename_box = Instance.new("TextBox")
  668. filename_box.Font = Enum.Font.Ubuntu
  669. filename_box.PlaceholderColor3 = Color3.new(0.239216, 0.392157, 0.290196)
  670. filename_box.PlaceholderText = "What should this be called?"
  671. filename_box.Text = ""
  672. filename_box.TextColor3 = Color3.new(0.317647, 0.231373, 0.490196)
  673. filename_box.TextScaled = true
  674. filename_box.TextSize = 14
  675. filename_box.TextStrokeColor3 = Color3.new(0.317647, 0.231373, 0.490196)
  676. filename_box.TextStrokeTransparency = 0.5
  677. filename_box.TextWrapped = true
  678. filename_box.BackgroundColor3 = Color3.new(0.654902, 0.788235, 0.980392)
  679. filename_box.BorderColor3 = Color3.new(0, 0, 0)
  680. filename_box.BorderSizePixel = 0
  681. filename_box.LayoutOrder = 6
  682. filename_box.Position = UDim2.new(0, 4, 0, 4)
  683. filename_box.Size = UDim2.fromScale(0.175,1)
  684. filename_box.SizeConstraint = Enum.SizeConstraint.RelativeXY
  685. filename_box.Visible = true
  686. filename_box.ZIndex = 2
  687. filename_box.Name = "FilenameBox"
  688. filename_box.Parent = bot_bar
  689.  
  690. local uicorner_3 = Instance.new("UICorner")
  691. uicorner_3.CornerRadius = UDim.new(0.22499999403953552, 0)
  692. uicorner_3.Parent = filename_box
  693.  
  694. local label = Instance.new("TextLabel")
  695. label.Font = Enum.Font.FredokaOne
  696. label.Text = "JSON filename:"
  697. label.TextColor3 = Color3.new(0.811765, 1, 0.431373)
  698. label.TextScaled = true
  699. label.TextSize = 14
  700. label.TextStrokeColor3 = Color3.new(0.266667, 0.364706, 0.411765)
  701. label.TextStrokeTransparency = 0
  702. label.TextWrapped = true
  703. label.TextXAlignment = Enum.TextXAlignment.Left
  704. label.AnchorPoint = Vector2.new(0.5, 0)
  705. label.BackgroundColor3 = Color3.new(1, 1, 1)
  706. label.BackgroundTransparency = 1
  707. label.BorderColor3 = Color3.new(0, 0, 0)
  708. label.BorderSizePixel = 0
  709. label.Position = UDim2.new(0.5, 4, -0.300000012, 0)
  710. label.Size = UDim2.new(1, 0, 0.532999992, 0)
  711. label.Visible = true
  712. label.ZIndex = 2
  713. label.Name = "Label"
  714. label.Parent = filename_box
  715.  
  716. -- THE RH-ESQUE "3D" SAVE BUTTON
  717. local save_button = Instance.new("TextButton")
  718. save_button.Font = Enum.Font.Cartoon
  719. save_button.Text = ""
  720. save_button.TextColor3 = Color3.new(1, 1, 1)
  721. save_button.TextScaled = true
  722. save_button.TextSize = 14
  723. save_button.TextWrapped = true
  724. save_button.AnchorPoint = Vector2.new(1, 0)
  725. save_button.BorderSizePixel = 0
  726. save_button.LayoutOrder = 7
  727. save_button.Position = UDim2.fromScale(0.125,1)
  728. save_button.Size = UDim2.fromScale(0.125,1)
  729. save_button.SizeConstraint = Enum.SizeConstraint.RelativeXY
  730. save_button.Visible = true
  731. save_button.Name = "SaveButton"
  732. save_button.Parent = bot_bar
  733.  
  734. local uicorner_4 = Instance.new("UICorner")
  735. uicorner_4.CornerRadius = UDim.new(0.125, 0)
  736. uicorner_4.Parent = save_button
  737.  
  738. local button_top = Instance.new("TextLabel")
  739. button_top.Font = Enum.Font.Cartoon
  740. button_top.Text = "Save"
  741. button_top.TextColor3 = Color3.new(1, 1, 1)
  742. button_top.TextScaled = true
  743. button_top.TextSize = 14
  744. button_top.TextStrokeTransparency = 0
  745. button_top.TextWrapped = true
  746. button_top.AnchorPoint = Vector2.new(0.5, 1)
  747. button_top.BackgroundColor3 = Color3.new(0.647059, 1, 0.678431)
  748. button_top.BorderColor3 = Color3.new(0, 0, 0)
  749. button_top.BorderSizePixel = 0
  750. button_top.Position = UDim2.new(0.5, 0, 0.875, 0)
  751. button_top.Selectable = true
  752. button_top.Size = UDim2.new(1, 0, 1, 0)
  753. button_top.Visible = true
  754. button_top.ZIndex = 2
  755. button_top.Name = "FakeTop"
  756. button_top.Parent = save_button
  757.  
  758. local uicorner_5 = Instance.new("UICorner")
  759. uicorner_5.CornerRadius = UDim.new(0.125, 0)
  760. uicorner_5.Parent = button_top
  761.  
  762. local button_outline = Instance.new("UIStroke")
  763. button_outline.ApplyStrokeMode = Enum.ApplyStrokeMode.Border
  764. button_outline.Color = Color3.new(0.388235, 0.705882, 0.627451)
  765. button_outline.Thickness = 2
  766. button_outline.Parent = button_top
  767.  
  768. local uigradient = Instance.new("UIGradient")
  769. uigradient.Color = ColorSequence.new({ColorSequenceKeypoint.new(0, Color3.new(0.752941, 0.752941, 0.752941)), ColorSequenceKeypoint.new(0.125, Color3.new(1, 1, 1)), ColorSequenceKeypoint.new(0.875, Color3.new(1, 1, 1)), ColorSequenceKeypoint.new(1, Color3.new(0.752941, 0.752941, 0.752941))})
  770. uigradient.Parent = save_button
  771.  
  772. -- PROGRESS BOX (USED FOR INDEXING AND SAVING)
  773. local progress_ui = Instance.new("TextLabel")
  774. progress_ui.Font = Enum.Font.RobotoCondensed
  775. progress_ui.Text = "Indexing instance\u{000D}\u{000A}56 / 203"
  776. progress_ui.TextColor3 = Color3.new(1, 1, 1)
  777. progress_ui.TextScaled = true
  778. progress_ui.TextStrokeTransparency = 0
  779. progress_ui.TextWrapped = true
  780. progress_ui.AnchorPoint = Vector2.one / 2
  781. progress_ui.BackgroundColor3 = Color3.new(0.161, 0.353, 0.224)
  782. progress_ui.BackgroundTransparency = 0.25
  783. progress_ui.BorderColor3 = Color3.new(0, 0, 0)
  784. progress_ui.BorderSizePixel = 0
  785. progress_ui.Position = UDim2.fromScale(0.5, 0.5)
  786. progress_ui.Size = UDim2.fromScale(0.425, 0.225)
  787. progress_ui.Visible = false
  788. progress_ui.ZIndex = 3
  789. progress_ui.Name = "ProgressUI"
  790. progress_ui.Parent = xane_mdlrecreator_gui
  791.  
  792. local ui_aspectratio_progress = Instance.new("UIAspectRatioConstraint")
  793. ui_aspectratio_progress.AspectRatio = 3.5
  794. ui_aspectratio_progress.Parent = progress_ui
  795.  
  796. local uitextsize_progress = Instance.new("UITextSizeConstraint")
  797. uitextsize_progress.MaxTextSize = 50
  798. uitextsize_progress.MinTextSize = 10
  799. uitextsize_progress.Parent = progress_ui
  800.  
  801. local uicorner_progress = Instance.new("UICorner")
  802. uicorner_progress.CornerRadius = UDim.new(0.125, 0)
  803. uicorner_progress.Parent = progress_ui
  804.  
  805. local template_selbox = Instance.new("SelectionBox")    -- A box that appears around any selected instances that support rendering it around them.
  806. template_selbox.Name = "XaneSelBoxTemplate"
  807. template_selbox.LineThickness = 0.03125
  808. template_selbox.Transparency = 0.25
  809. template_selbox.SurfaceTransparency = 0.925
  810. template_selbox:SetAttribute("XaneProtectedDoNotShowInRecreatorGui",true)   -- Hide this instance from the Recreator script itself.
  811. template_selbox.Parent = xane_mdlrecreator_gui
  812.  
  813. print("UI created!")
  814.  
  815. -- DATA FOR THE ANIMATED SAVE BUTTON
  816. local Position_Raised       = UDim2.fromScale(0.5,0.875)
  817. local Position_Pressed      = UDim2.fromScale(0.5,1)
  818. local BtnColor_Ready        = Color3.fromRGB(160,255,224)   -- Save button colors.
  819. local BtnColor_Caution      = Color3.fromRGB(212,255,204)   -- Used if the selected file already exists.
  820. local BtnColor_Disabled     = Color3.fromRGB(126,128,130)   -- Used until a valid model and filename are entered.
  821.  
  822. local RowOpacityNormal      = 0.875
  823. local RowOpacitySelected    = 0.75
  824.  
  825. -- An array of dictionary entries which keeps all of the events and references needed by each instance shown in the main UI list.
  826. -- As creating too many rows will lag the client when the list updates (scrolling, generating, etc), this array should be limited to
  827. -- 100 or so entries at any time.
  828.  
  829. -- The full list (which has less data in each entry) is InternalList below.
  830. local ListData : {VisibleListEntry} = {}
  831.  
  832. -- VARIABLES (SECOND SET)
  833. local GUIShown              = true  -- This is set to TRUE when the main window is visible, used by the toggle button at the top of the screen.
  834. local IsBusy                = false -- Marks the main window as busy. This disables all buttons, so the script can execute code in peace.
  835. local Offset                = 0     -- The current "page" of instances that are being shown to the user.
  836. local CurrentContainer      = nil
  837.  
  838. local function ChangeButtonState(_release : boolean, _color : Color3, _text : string)
  839.     if _text then button_top.Text = _text end
  840.     if _color then
  841.         button_top.BackgroundColor3 = _color
  842.         local btnH, btnS, btnV = _color:ToHSV()
  843.  
  844.         button_outline.Color = Color3.fromHSV(btnH, btnS+(btnS/4), btnV-(btnV/6))
  845.         save_button.BackgroundColor3 = Color3.fromHSV(btnH, btnS+(btnS/3), btnV-(btnV/4))
  846.     end
  847.  
  848.     -- If the button is already in the same state as this command would change it to, stop the function here.
  849.     if _release == save_button.Active then return nil
  850.     else
  851.         save_button.Active = _release   -- Immediately update whether the user can click on this button before playing the animation.
  852.         button_top:TweenPosition(
  853.             _release and Position_Raised or Position_Pressed,
  854.             Enum.EasingDirection.InOut,
  855.             Enum.EasingStyle.Quad,
  856.             0.125,
  857.             true
  858.         )
  859.         return nil
  860.     end
  861. end
  862. -- Initially disable the save button.
  863. ChangeButtonState(false, BtnColor_Disabled, "Can't save")
  864.  
  865. local function CheckSavePrerequisites()
  866.     if filename_box.Text:len() > 0 and      -- Make sure the TextBox isn't empty and the filename doesn't contain invalid characters.
  867.         not filename_box.Text:find("/") and
  868.         not filename_box.Text:find("\"") and
  869.         not filename_box.Text:find("\\") and
  870.         not filename_box.Text:find(":") and
  871.         not filename_box.Text:find("*") and
  872.         not filename_box.Text:find("?") and
  873.         #Recreator.Selection > 0    -- Ensure the player has at least one Instance selected before letting them export.
  874.     then
  875.         local FileStatus = nil
  876.         pcall(function()
  877.             FileStatus = readfile(filename_box.Text .. "_header.json")
  878.         end)
  879.         if FileStatus then ChangeButtonState(true, BtnColor_Caution, "Overwrite")
  880.         else ChangeButtonState(true, BtnColor_Ready, "Save!")
  881.         end
  882.     else ChangeButtonState(false, BtnColor_Disabled, "Can't save")
  883.     end
  884.  
  885.     clear_button.Visible = #Recreator.Selection > 0 -- A second button will appear at the top of the screen to deselect everything if anything's marked.
  886.     message.Text = DefaultMessage
  887. end
  888.  
  889. -- Disconnects all events within the list shown in the main window, then removes all of its rows.
  890. local function ClearVisibleList()
  891.     if #ListData > 0 then
  892.         for i,entry in ListData do
  893.             -- Disconnect all of the button events' connections before destroying this row.
  894.             if entry.CheckboxClickEvent then
  895.                 entry.CheckboxClickEvent:Disconnect()
  896.                 ListData[i].CheckboxClickEvent = nil
  897.             end
  898.             if entry.CameraFocusEvent then
  899.                 entry.CameraFocusEvent:Disconnect()
  900.                 ListData[i].CameraFocusEvent = nil
  901.             end
  902.             if entry.CamRevertEvent1 then
  903.                 entry.CamRevertEvent1:Disconnect()
  904.                 ListData[i].CamRevertEvent1 = nil
  905.             end
  906.             if entry.CamRevertEvent2 then
  907.                 entry.CamRevertEvent2:Disconnect()
  908.                 ListData[i].CamRevertEvent2 = nil
  909.             end
  910.  
  911.             if entry.SelectBox then
  912.                 entry.SelectBox:Destroy()
  913.                 entry.SelectBox = nil
  914.             end
  915.  
  916.             entry.Checkbox:Destroy()
  917.             entry.Checkbox = nil
  918.  
  919.             entry.RowBase:Destroy()
  920.             entry.RowBase = nil
  921.         end
  922.  
  923.         table.clear(ListData)   -- Remove all of the now-useless entries from the array.
  924.     end
  925. end
  926.  
  927. -- Visually updates a given row to make it apppear to be (de)selected.
  928. local function UpdateRowVisualState(_entry : InstanceListEntry, _select : boolean)
  929.     if _select then
  930.         _entry.RowBase.BackgroundTransparency = RowOpacitySelected
  931.         _entry.Checkbox.Text = "✅"
  932.     else
  933.         _entry.RowBase.BackgroundTransparency = RowOpacityNormal
  934.         _entry.Checkbox.Text = Recreator.ClassData[_entry.Instance.ClassName].ListView.Icon -- Revert this checkbox's icon to the class icon.
  935.     end
  936. end
  937.  
  938. -- An iffy function that does what ApplyChildAction()'s "list" mode used to do, only now in a for loop. It destroys the
  939. -- current "visible list" then rebuilds it using data from the full, pre-generated internal instance list.
  940. local function RedrawVisibleList()
  941.     if #ListData > 0 then
  942.         ClearVisibleList()
  943.     end
  944.  
  945.     for i = (Recreator.PageLength*Offset)+1, (Recreator.PageLength*Offset)+Recreator.PageLength-1 do
  946.         -- Make sure we haven't reached the end of the list yet. If we have, stop creating entries now.
  947.         if Recreator.FullList[i] then
  948.             -- Make sure this Instance isn't a "bad actor" which could cause a softlock or wouldn't assist this export.
  949.             if not Recreator.IsInstanceAllowed(Recreator.FullList[i].Instance) then continue end
  950.            
  951.             -- If execution reaches this point, this Instance is supported, so let's add it to our temporary list!
  952.             local NewEntry : InstanceListEntry = {
  953.                 Instance                = Recreator.FullList[i].Instance,
  954.                 RowBase                 = template_entry:Clone(),
  955.                 Checkbox                = nil,
  956.                 CheckboxClickEvent      = nil,
  957.                 CameraFocusEvent        = nil,
  958.                 CamRevertEvent1         = nil,
  959.                 CamRevertEvent2         = nil
  960.             }
  961.             NewEntry.RowBase.Name       = "Listing_" .. i   -- Name each row's Frame after its ordering, just to make it easier for those sifting through UI instances.
  962.             NewEntry.Checkbox           = NewEntry.RowBase:WaitForChild("SelectButton")
  963.            
  964.             local temp_instName         = NewEntry.RowBase:WaitForChild("InstName")
  965.            
  966.             -- Before continuing, determine how big a row should be in pixels rather than scale. Since executors are behind the times,
  967.             -- they unfortyunately don't support Roblox's new CSS-esque flexible UI containers, so there isn't an easy way to make the
  968.             -- "instance name" label fill the space the icon on the left side and camera button on the right side exactly. It's dumb!
  969.            
  970.             -- TODO: I just cannot get rows to be just long enough to squeeze the camera button onto the right edge of each row, regardless
  971.             -- of indentation. For some reason, with or without a delay, AbsoluteSize is always 0, 0! I'm sorry for this awful alt. sizing...
  972.             temp_instName.Size = UDim2.fromScale(1 - (indent.Size.X.Scale * Recreator.FullList[i].Level) - 0.0825,1)
  973.             NewEntry.RowBase:WaitForChild("CamActionButton").AnchorPoint = Vector2.xAxis
  974.             --[[
  975.                 task.wait()
  976.                
  977.                 print("Row X scale was calculated as", 1 - (indent.Size.X.Scale * InternalList[i].Level))
  978.                 print("Just the subtracted value is", indent.Size.X.Scale * InternalList[i].Level)
  979.                 print("Absolute size is", temp_instName.AbsoluteSize)
  980.                 print("Final X size is somehow", temp_instName.AbsoluteSize.X-temp_instName.AbsoluteSize.Y)
  981.                
  982.                 temp_instName.Size = UDim2.fromOffset(  -- Take the size determined above and subtract one square icon's size from it.
  983.                     temp_instName.AbsoluteSize.X-temp_instName.AbsoluteSize.Y,
  984.                     temp_instName.AbsoluteSize.Y
  985.                 )
  986.             ]]--
  987.            
  988.             temp_instName.Text = Recreator.FullList[i].Instance.Name
  989.             local temp_newIndent = NewEntry.RowBase:WaitForChild("Indent")  -- Hold a reference to the original indent, which will be duplicated based on this instance's "level".
  990.             temp_newIndent.Visible = Recreator.FullList[i].Level > 0
  991.             if Recreator.FullList[i].Level > 1 then -- If this Instance is at the second level or deeper, duplicate the indent as needed, forming a grid for clarity left of the list.
  992.                 for i = 2, Recreator.FullList[i].Level do
  993.                     local temp_additionalIndent = temp_newIndent:Clone()
  994.                     temp_additionalIndent.Parent = temp_newIndent.Parent
  995.                 end
  996.             end
  997.            
  998.             NewEntry.Checkbox.Text = Recreator.ClassData[Recreator.FullList[i].Instance.ClassName].ListView.Icon
  999.            
  1000.             -- If this object has been previously selected, highlight it now.
  1001.             if table.find(Recreator.Selection, Recreator.FullList[i].Instance) then
  1002.                 UpdateRowVisualState(NewEntry, true)
  1003.             end
  1004.            
  1005.             NewEntry.RowBase.Visible = true
  1006.            
  1007.             -- Add functionality to the the two buttons found on the ends of this row.
  1008.             local temp_camButton : TextButton = NewEntry.RowBase:WaitForChild("CamActionButton")
  1009.             temp_camButton.Visible = Recreator.ClassData[Recreator.FullList[i].Instance.ClassName].ListView.CanView ~= "no"
  1010.             if temp_camButton.Visible then
  1011.                 NewEntry.CameraFocusEvent = temp_camButton.MouseButton1Click:Connect(function()
  1012.                     if Recreator.ClassData[Recreator.FullList[i].Instance.ClassName].ListView.CanView == "parent" then
  1013.                         local temp_ancestor = Recreator.FullList[i].Instance:FindFirstAncestorWhichIsA("BasePart")
  1014.                         if temp_ancestor then
  1015.                             workspace.CurrentCamera.CameraSubject = temp_ancestor
  1016.                         end
  1017.                     elseif Recreator.ClassData[Recreator.FullList[i].Instance.ClassName].ListView.CanView == "child" then
  1018.                         local temp_child = Recreator.FullList[i].Instance:FindFirstChildWhichIsA("BasePart")
  1019.                         if temp_child then
  1020.                             workspace.CurrentCamera.CameraSubject = Recreator.FullList[i].Instance.Parent
  1021.                         else
  1022.                             -- Hide the camera button if nothing camera-compatible is found within this instance.
  1023.                             NewEntry.RowBase:FindFirstChild("CamActionButton").Visible = false
  1024.                         end
  1025.                     else
  1026.                         workspace.CurrentCamera.CameraSubject = Recreator.FullList[i].Instance
  1027.                     end
  1028.                 end)
  1029.                 NewEntry.CamRevertEvent1 = temp_camButton.MouseButton2Click:Connect(function()
  1030.                     workspace.CurrentCamera.CameraSubject = Players.LocalPlayer.Character:WaitForChild("Humanoid")
  1031.                 end)
  1032.                 NewEntry.CamRevertEvent2 = temp_camButton.TouchLongPress:Connect(function()
  1033.                     workspace.CurrentCamera.CameraSubject = Players.LocalPlayer.Character:WaitForChild("Humanoid")
  1034.                 end)
  1035.             end
  1036.            
  1037.             NewEntry.CheckboxClickEvent = NewEntry.Checkbox.MouseButton1Click:Connect(function()
  1038.                 -- If this Instance is currently selected, remove it from the list and undo changes to this row on the list.
  1039.                 local SelectionIndex    = table.find(Recreator.Selection, Recreator.FullList[i].Instance)
  1040.                 if SelectionIndex then  -- TODO: This code block relies on the selection list using debug IDs instead of proper references!
  1041.                     Recreator.Select("remove", {Recreator.FullList[i].Instance})
  1042.                     -- table.remove(Recreator.Selection, SelectionIndex)
  1043.                     UpdateRowVisualState(NewEntry, false)
  1044.                    
  1045.                     -- If this Instance is visually selected in-world, remove the box that's around it.
  1046.                     if NewEntry.SelectBox then
  1047.                         NewEntry.SelectBox:Destroy()
  1048.                         NewEntry.SelectBox = nil    -- Make sure the other part of this mini-function knows that the box no longer exists.
  1049.                     end
  1050.                 else
  1051.                     -- table.insert(Recreator.Selection, Recreator.FullList[i].Instance)
  1052.                     Recreator.Select("add", {Recreator.FullList[i].Instance})
  1053.                    
  1054.                     UpdateRowVisualState(NewEntry, true)
  1055.                     if Recreator.FullList[i].Level <= 0 then    -- If the container itself was just selected, deselect anything within it.
  1056.                         local SelectionDelQueue = {}        -- Keep track of the array indices that'll be removed after this for loop.
  1057.                         local Cleared = false               -- The selection array will be endlessly checked until this flag is set to TRUE.
  1058.                         while not Cleared do
  1059.                             for i1,_inst1 : Instance in Recreator.Selection do  -- For each selected Instance...
  1060.                                 if _inst1 and _inst1:IsDescendantOf(Recreator.FullList[i].Instance) then
  1061.                                     table.remove(Recreator.Selection, i1)   -- The table.remove() function messes up arrays' orders, so let's start over...
  1062.                                     break
  1063.                                 elseif i1 >= #Recreator.Selection and not Cleared then
  1064.                                     Cleared = true  -- Without this action, the while loop would force this for loop to completely restart.
  1065.                                 end
  1066.                             end
  1067.                         end
  1068.                         Cleared = false
  1069.                         RedrawVisibleList() -- Update the list, in case the loop above deselected any Instances.
  1070.                     end
  1071.                    
  1072.                     -- Also, create a SelectionBox for this instance, to make it easier to tell what has been selected in messy games (like Royale High).
  1073.                     -- Only do this for child instances within the workspace, and obviously, don't draw a box around the workspace itself.
  1074.                     if Recreator.FullList[i].Level > 0 and Recreator.FullList[i].Instance:IsDescendantOf(workspace) and not NewEntry.SelectBox then
  1075.                         NewEntry.SelectBox                  = template_selbox:Clone()
  1076.                         NewEntry.SelectBox.Color3           = Color3.fromHSV(math.random(), 0.125+(math.random()/5), 1-(math.random()/16))  -- Use a random color.
  1077.                         NewEntry.SelectBox.SurfaceColor3    = NewEntry.SelectBox.Color3
  1078.                         NewEntry.SelectBox.Adornee          = Recreator.FullList[i].Instance
  1079.                         NewEntry.SelectBox.Parent           = Recreator.FullList[i].Instance
  1080.                     end
  1081.                 end
  1082.                
  1083.                 CheckSavePrerequisites()    -- If the user's entered a filename and has at least one instance selected, allow them to start exporting things!
  1084.             end)
  1085.            
  1086.             NewEntry.RowBase.Parent = instance_list
  1087.             table.insert(ListData, NewEntry)
  1088.         else break
  1089.         end
  1090.     end
  1091. end
  1092.  
  1093. -- Slides the main window on/off the screen. Normally, this is prevented when anything important is happening, but _force will bypass restrictions.
  1094. local function ToggleUI(_force : boolean)
  1095.     if not IsBusy or _force then
  1096.         close_button.Visible = GUIShown -- Show the close button when the user closes the main UI.
  1097.         if GUIShown then
  1098.             IsBusy = true
  1099.             if #Recreator.Selection <= 0 then toggle_button.Text = "Open Save GUI..."
  1100.             else toggle_button.Text = "Select Instances... (" .. tostring(#Recreator.Selection) .. ")"
  1101.             end
  1102.             toggle_button.BackgroundColor3 = BtnColor_Ready
  1103.             main_frame:TweenPosition(UDim2.fromScale(0.5, 2.0), Enum.EasingDirection.InOut, Enum.EasingStyle.Quad, 0.333, true)
  1104.             task.wait(0.125)
  1105.             IsBusy = false
  1106.         else
  1107.             IsBusy = true
  1108.             toggle_button.Text = "Hide GUI"
  1109.             toggle_button.BackgroundColor3 = BtnColor_Caution
  1110.             main_frame:TweenPosition(UDim2.fromScale(0.5, 0.55), Enum.EasingDirection.Out, Enum.EasingStyle.Back, 0.35, true)
  1111.             task.wait(0.35)
  1112.             IsBusy = false
  1113.         end
  1114.  
  1115.         GUIShown = not GUIShown
  1116.     end
  1117. end
  1118. toggle_button.MouseButton1Click:Connect(function() ToggleUI(false) end)
  1119. ToggleUI(false)
  1120.  
  1121. local function ChangeListSource(_base : Instance)
  1122.     if not IsBusy then
  1123.         IsBusy = true   -- Disable all interface buttons until the list has been updated. (Hopefully, no errors occur!)
  1124.         ToggleUI(false)
  1125.  
  1126.         if #ListData > 0 then ClearVisibleList() end
  1127.         if #Recreator.FullList > 0 then table.clear(Recreator.FullList) end
  1128.  
  1129.         -- Set up and show the "indexing progress" UI, which will be updated by ApplyChildAction(), so the user isn't left in the dark.
  1130.         progress_ui.Visible = true  -- For now, make the UI pop up rather than sliding in like the other frames would.
  1131.        
  1132.         Recreator.MakeList(_base)   -- Generate the full list of Instances within the new container.
  1133.         table.insert(Recreator.FullList, 1, {   -- Slip a fake entry for the container itself into the start of the list.
  1134.             Instance = _base,
  1135.             Level = 0
  1136.         })
  1137.  
  1138.         progress_ui.Visible = false -- Hide the progress UI; We're about to display the list that was just generated.
  1139.         Offset = 0                  -- Always start from the beginning of a container's Instance list.
  1140.         RedrawVisibleList()
  1141.         task.wait(0.425)
  1142.  
  1143.         ToggleUI(false)
  1144.         IsBusy = false
  1145.     end
  1146. end
  1147. container1.MouseButton1Click:Connect(function() ChangeListSource(workspace) end)
  1148. container2.MouseButton1Click:Connect(function() ChangeListSource(Players) end)
  1149. container3.MouseButton1Click:Connect(function() ChangeListSource(Lighting) end)
  1150. container4.MouseButton1Click:Connect(function() ChangeListSource(ReplicatedStorage) end)
  1151.  
  1152. -- "Container 5" is a special case; Rather than indexing every instance within a container, it specifically tries to find each player's
  1153. -- character model and indexes ITS contents. If playing Royale High, its EquippedStorage folder is checked for and also indexed.
  1154. local function IndexCharacters()
  1155.     if not IsBusy then
  1156.         IsBusy = true   -- Disable all interface buttons until the list has been updated. (Hopefully, no errors occur!)
  1157.         ToggleUI(false)
  1158.        
  1159.         -- Clear the full and visible lists if they're already occupied by another container button's instances.
  1160.         if #ListData > 0 then ClearVisibleList() end
  1161.         if #Recreator.FullList > 0 then table.clear(Recreator.FullList) end
  1162.        
  1163.         for _,player : Player in pairs(Players:GetPlayers()) do
  1164.             local PlayerChar            = player.Character  -- Try to access this player's character model.
  1165.             if not PlayerChar then                          -- If it hasn't been created yet, retry a couple of times, then give up.
  1166.                 local CharTries         = 10
  1167.                 local success           = false
  1168.                 while CharTries > 0 do
  1169.                     success, _ = pcall(function()
  1170.                         PlayerChar = player.Character
  1171.                     end)
  1172.                     if success then break end               -- Did we find it? Let's move on!
  1173.                     task.wait(0.25)
  1174.                     CharTries -= 1
  1175.                 end
  1176.                
  1177.                 if not PlayerChar then continue end         -- If their character is still inaccessible, just move on to the next player.
  1178.             end
  1179.            
  1180.             -- If execution reaches this point, we have a player character to index!
  1181.            
  1182.             -- Unlike every other "mode", we won't be listing every instance within character models to save time; A player could leave the
  1183.             -- server within a minute, after all! Just index the character models directly.
  1184.             table.insert(Recreator.FullList, {
  1185.                 Instance = PlayerChar,
  1186.                 Level = 1
  1187.             })
  1188.            
  1189.             -- progress_ui.Visible = false  -- Hide the progress UI; We're about to display the list that was just generated.
  1190.         end
  1191.        
  1192.         -- Check for a folder named "EquippedStorage", which Royale High places clothes that players are wearing inside. This folder should be
  1193.         -- listed first, as the user probably doesn't want to save characters with missing body parts or anything weird-looking.
  1194.         local RH_EquippedStorage = workspace:FindFirstChild("EquippedStorage")
  1195.         if RH_EquippedStorage then
  1196.             table.insert(Recreator.FullList, 1, {
  1197.                 Instance = RH_EquippedStorage,
  1198.                 Level = 1
  1199.             })
  1200.         end
  1201.        
  1202.         Offset = 0                          -- Always start from the beginning of a container's Instance list.
  1203.         RedrawVisibleList()
  1204.         task.wait(0.425)
  1205.        
  1206.         ToggleUI(false)
  1207.         IsBusy = false
  1208.     end
  1209. end
  1210. container5.MouseButton1Click:Connect(function() IndexCharacters(false) end)
  1211.  
  1212. prevPageButton.MouseButton1Click:Connect(function()
  1213.     -- If advancing to the next page would go beyond the full Instance list's bounds, ignore this action. Otherwise, update the visible list.
  1214.     if  not IsBusy and Offset > 0 then
  1215.         IsBusy = true   -- Prevent other actions from occurring until the list has been finalized.
  1216.         Offset -= 1
  1217.         RedrawVisibleList()
  1218.         instance_list.CanvasPosition = Vector2.yAxis * instance_list.AbsoluteCanvasSize.Y   -- Jump to the end of the visible list.
  1219.         IsBusy = false  -- It is now safe to close the main window and export.
  1220.     end
  1221. end)
  1222. nextPageButton.MouseButton1Click:Connect(function()
  1223.     -- If advancing to the next page would go beyond the full Instance list's bounds, ignore this action. Otherwise, update the visible list.
  1224.     if not IsBusy and ((Offset*Recreator.PageLength)+1)+Recreator.PageLength <= #Recreator.FullList then
  1225.         IsBusy = true   -- Prevent other actions from occurring until the list has been finalized.
  1226.         Offset += 1
  1227.         RedrawVisibleList()
  1228.         instance_list.CanvasPosition = Vector2.zero -- Jump to the top of the visible list.
  1229.         IsBusy = false  -- It is now safe to close the main window and export.
  1230.     end
  1231. end)
  1232.  
  1233. -- If the clear button is visible, tappping it will deselect everything then redraw whatever page the window was showing.
  1234. clear_button.MouseButton1Click:Connect(function()
  1235.     Recreator.Select("set", {}) -- Clear the current selection.
  1236.     ClearVisibleList()
  1237.     RedrawVisibleList()
  1238.     CheckSavePrerequisites()
  1239. end)
  1240.  
  1241. -- If the user just finished using the model name text box, validate their selection and name to determine if they're allowed to save it now.
  1242. local LastSafeName  = ""
  1243. filename_box.FocusLost:Connect(function()
  1244.     if not IsBusy then  -- As long as saving or something important isn't happening now, save the current file name to a variable. It's used below.
  1245.         LastSafeName = filename_box.Text
  1246.         CheckSavePrerequisites()
  1247.     else    -- If something is happening, don't let the user change the save's name!
  1248.         filename_box.Text = LastSafeName
  1249.     end
  1250. end)
  1251.  
  1252. -- Unfreezes frozen char. parts, shows the GUI, and unsets the "busy" flag, restoring the ability to select instances again.
  1253. -- If _success is set, this function also deselects everything and redraws the list.
  1254. local function PostSaveCleanup(_success)
  1255.     if _success then
  1256.         RedrawVisibleList()
  1257.         Recreator.Select("set", {})
  1258.     end
  1259.    
  1260.     if not GUIShown then
  1261.         ToggleUI(false)
  1262.         task.wait(0.25)
  1263.         ChangeButtonState(true)
  1264.        
  1265.         message.Text = "Done! Transfer the new JSON files in your workspace folder to your PC, " ..
  1266.             "then paste each of them into the Studio plugin's text fields, adding more as needed." ..
  1267.             "Make sure the 'header' goes first, followed by each piece IN ORDER!"
  1268.        
  1269.         IsBusy = false
  1270.     end
  1271. end
  1272. Recreator.OnSaveDone = function(_success : boolean)
  1273.     PostSaveCleanup(_success)
  1274. end
  1275.  
  1276. save_button.MouseButton1Click:Connect(function()
  1277.     if not IsBusy and save_button.Active then
  1278.         if GUIShown then ToggleUI(false) end
  1279.         IsBusy = true
  1280.         progress_ui.Text    = "Preparing to save..."
  1281.         progress_ui.Visible = true
  1282.         Recreator.Save(filename_box.Text)
  1283.         progress_ui.Visible = false
  1284.         IsBusy = false
  1285.     end
  1286. end)
  1287.  
  1288. close_button.MouseButton1Click:Connect(function()
  1289.     if not IsBusy then
  1290.         Recreator.ActivateAPI(false)
  1291.         table.clear(ListData)
  1292.         xane_mdlrecreator_gui:Destroy()
  1293.         print("Xane's Model Recreator has been closed. Thank you for using this awesome script!")
  1294.         script:Destroy()
  1295.     end
  1296. end)
  1297.  
  1298. Recreator.SetStatusGui(progress_ui)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement