Advertisement
Xane_MM

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

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