-- 41458171 require(game.ReplicatedFirst.ReadyModule)("SaveModule") local Commands = require(_G.Modules.CommandsModule) Placement = require(_G.Modules.PlacementModule) Debris = _G.Debris SaveFrame = nil Stroke = nil SaveData = nil SaveTabs = nil SaveLists = nil Templates = nil PlayerSaveList = nil SelectionBox = nil parts = _G.Storage:WaitForChild("Parts") SubassemblyData = "" SubassemblyModel = nil CurrentSaveData = "" CurrentPlacementConfig = nil CurrentSaveRoot = nil LastInteractedPart = nil SelectionBoxes = {} local function OffloadSelection() if CurrentPlacementConfig then CurrentPlacementConfig:stop() end CurrentSaveRoot = nil CurrentSaveData = nil SelectionBox.Adornee = nil for _,object in pairs(SelectionBoxes) do object:Destroy() end SelectionBoxes = {} end local function LoadModel(key,group) wait() local raw = _G.Remotes.Load:InvokeServer(group,key) local success,result = pcall(function() return _G.Methods.saves.dataToModel(raw,false) end) local model if success then model = result else _G.Error(string.format("Load Model Error (%s:%s): %s",key,group,result)) end if not model then return _G.Storage.Missing:Clone() end local primary = model:FindFirstChildWhichIsA("VehicleSeat",true) if primary then model.PrimaryPart = primary end return model end local _tab = {} _tab.__index = _tab local _slot = {} _slot.__index = _slot _slot.__newindex_operations = { name = function(this,name) this.frame.NameBox.Text = name end, order = function(this,index) this.frame.LayoutOrder = tonumber(index) or 0 this.frame.IdLabel.Text = tostring(index):upper() if index == "autosave" then this.frame.IdLabel.BackgroundColor3 = Color3.new(1, 0.768627, 0.223529) elseif index == "subassembly" then this.frame.IdLabel.BackgroundColor3 = Color3.new(0.623529, 0.0941176, 1) end end, on_mirror_clicked = function(this,callback) _G.Error("Mirror function unimplemented") end, on_name_change = function(this,callback) this.frame.NameBox.TextEditable = true this.frame.NameBox.FocusLost:Connect(function(enter) if enter then callback(this.frame.NameBox.Text) end end) end, on_save_clicked = function(this,callback) this.frame.SaveButton.Visible = true this.frame.SaveButton.Activated:Connect(function() callback(CurrentSaveData) end) end, on_load_clicked = function(this,callback) this.frame.LoadButton.Activated:Connect(function() callback(this) end) end, model = function(this,model) local old = this.viewport:FindFirstChildWhichIsA("Model") if old then old:Destroy() end if not model then this.frame.OldLabel.Visible = true this.frame.OldLabel.Text = "CORRUPT" return else if not (model.Name == "Model") then this.frame.NameBox.Text = model.Name end end model.Parent = this.viewport this.frame.OldLabel.Visible = model:HasTag("Old") this.frame.OldLabel.Text = model:GetAttribute("Version") or "Err" local cframe,size = model:GetBoundingBox() this.camera.CFrame = cframe * CFrame.fromEulerAnglesYXZ(-0.5,-2.5,0) this.camera.CFrame = this.camera.CFrame + this.camera.CFrame.LookVector * -(size.Magnitude/2) end } _slot.__newindex = function(this,index,value) if not _slot.__newindex_operations[index] then error("No index to set: "..index,2) end _slot.__newindex_operations[index](this,value) end _slot.show = function(this) this.frame.Visible = true _G.Tween:Create(this.frame.UIPadding,TweenInfo.new(0.5,Enum.EasingStyle.Quad,Enum.EasingDirection.Out),{PaddingLeft = UDim.new(0,0)}):Play() end _tab.CreateSlot = function(this) local new = {} new.frame = Templates.Frame:Clone() new.frame.Parent = this.list new.raw = "" _G.Union(new.frame,{ Parent = this.list, Visible = true, }) new.frame.NameBox.TextEditable = false new.frame.SaveButton.Visible = false new.angle = 45 new.viewport = new.frame.ViewportFrame new.undo = new.frame.UndoButton new.refresh = new.frame.RefreshButton new.camera = Instance.new("Camera") new.camera.Parent = new.viewport new.camera.CFrame = CFrame.Angles(0,math.rad(new.angle),0) new.viewport.CurrentCamera = new.camera local hover = false new.viewport.MouseEnter:Connect(function() hover = true new.viewport.BackgroundColor3 = Color3.new(0.75,0.75,0.75) while wait() and hover do new.camera.CFrame = CFrame.Angles(0,math.rad(2),0) * new.camera.CFrame end end) new.viewport.MouseLeave:Connect(function() hover = false new.viewport.BackgroundColor3 = Color3.new(1,1,1) end) new.frame.Visible = false new.frame.OldLabel.Visible = false return setmetatable(new,_slot) end _tab.FocusSection = function(this) for _,list in pairs(SaveLists:GetChildren()) do if list:IsA("ScrollingFrame") then list.Visible = false end end this.list.Visible = true Stroke.Parent = this.tab end local function CreateSection(title) local new = setmetatable({},_tab) new.list = Templates.List:Clone() _G.Union(new.list,{ Parent = SaveLists, Visible = false }) new.tab = Templates.Tab:Clone() _G.Union(new.tab,{ Parent = SaveTabs, Visible = true, Text = title }) new.slots = {} new.tab.CloseButton.Activated:Connect(function() Stroke.Parent = PlayerSaveList new.tab:Destroy() new.list:Destroy() PlayerSaveList:FocusSection() end) new.tab.Activated:Connect(function() new:FocusSection() end) return new end _G.SaveIndices = {} local function CreateUserSection(userid,modify,close) userid = tostring(userid or _G.UserId) local title = (game.Players:GetPlayerByUserId(tonumber(userid) or 0) or {DisplayName = userid}).DisplayName local section = CreateSection(title) if userid == tostring(_G.UserId) then section:FocusSection() end local save_info = {} local save_info_locked = false local saves = {} local slots = {} if not userid:match(":") and not close then section.tab.CloseButton.Visible = false end local function update_save_info(callback) repeat wait() until not save_info_locked save_info_locked = true if callback then callback(save_info) end _G.Remotes.SetSaveInfo:InvokeServer(save_info,userid) save_info = _G.Remotes.GetSaveInfo:InvokeServer(userid) for index,name in pairs(save_info) do local slot = slots[tostring(index)] or slots[index] if slot then slot.name = name end end save_info_locked = false end spawn(function() save_info = _G.Remotes.GetSaveInfo:InvokeServer(userid) local indices = {} if not save_info then return end for index,_ in pairs(save_info) do table.insert(indices,index) end _G.SaveIndices[userid] = indices if not userid:match(":") then table.sort(indices,function(before,after) before = tonumber(before) after = tonumber(after) if not before then return true end if not after then return false end return before < after end) end for _,index in pairs(indices) do spawn(function() local name = save_info[index] index = tostring(index) local slot = section:CreateSlot() slots[index] = slot local model = LoadModel(index,userid) saves[index] = model _G.Union(slot,{ name = name, order = index, on_name_change = function(text) update_save_info(function() save_info[tonumber(index) or index] = text end) end, on_load_clicked = function() if not saves[index] or not saves[index].PrimaryPart then _G.Events.Notification:Fire("Vehicle Load Failed","There is no vehicle.") return end OffloadSelection() CurrentPlacementConfig = Placement.begin(saves[index]:Clone(),{ confirm = function(config) _G.Remotes.Place:InvokeServer(config.model.PrimaryPart.CFrame,index,userid) config:stop() end, stopped = function(config) config.model:Destroy() CurrentPlacementConfig = nil end, disabled = { resize = true } }) end, model = model }) if modify then slot.on_save_clicked = function() if (not CurrentSaveData) or (#CurrentSaveData == 0) then _G.Events.Notification:Fire("Vehicle Save Failed","Please select a vehicle to save") return end _G.Remotes.Save:InvokeServer(CurrentSaveData,index,userid) model = LoadModel(index,userid) saves[index] = model slot.model = model OffloadSelection() end slot.viewport.MouseEnter:Connect(function() slot.undo.Visible = true slot.refresh.Visible = true end) slot.viewport.MouseLeave:Connect(function() slot.undo.Visible = false slot.refresh.Visible = false end) end slot.undo.Activated:Connect(function() CreateUserSection(userid..":"..index,false) end) slot.refresh.Activated:Connect(function() model = LoadModel(index,userid) saves[index] = model slot.model = model OffloadSelection() end) slot:show() end) wait(0.2) end if tostring(_G.UserId) == userid then -- autosave spawn(function() wait(5) repeat if not LastInteractedPart then continue end local part = LastInteractedPart.part if part and part.Parent and _G.Methods.playerOwnsPart(game.Players.LocalPlayer,part) then _G.Events.Notification:Fire("Auto Saving","Attempting Autosave") CurrentSaveData = _G.Methods.saves.modelToData(part) _G.Remotes.Save:InvokeServer(CurrentSaveData,"autosave",userid) _G.Events.Save:Fire(CurrentSaveData) local selection_template = Instance.new("SelectionBox") for index,item in pairs({ SurfaceTransparency = 0.75, SurfaceColor3 = Color3.fromRGB(231, 72, 183), LineThickness = 0, }) do selection_template[index] = item end _G.Methods.forEachConnectedObject(part,function(model) wait() local selection = selection_template:Clone() selection.Adornee = model selection.Parent = model Debris:AddItem(selection,0.1) end) update_save_info(function() save_info["autosave"] = tostring(os.date("%c",os.time())) end) local model = LoadModel("autosave",userid) slots["autosave"].model = model saves["autosave"] = model end until not wait(60*2.5) end) end end) return section,saves end _G.Events.Create.Event:Connect(function(part) LastInteractedPart = part end) local function RenderStepped(delta) -- General procedure for allowing us to pick a part from a vehicle to later select (per frame) if CurrentPlacementConfig then return end local result = _G.Methods.getMouseHit() if result and result.Instance then local part if _G.Methods.playerOwnsPart(_G.Player,result.Instance) then part = result.Instance elseif _G.Methods.playerOwnsPart(_G.Player,result.Instance.Parent) then part = result.Instance.Parent end SelectionBox.Adornee = part CurrentSaveRoot = part end end function InitTool(script) SelectionBox = script:WaitForChild("SelectionBox") local renderSteppedEvent = nil script.Parent.Activated:Connect(function() if CurrentSaveRoot then local root = CurrentSaveRoot OffloadSelection() CurrentSaveData = _G.Methods.saves.modelToData(root) local selection_template = Instance.new("SelectionBox") for index,item in pairs({ SurfaceTransparency = 0.75, SurfaceColor3 = Color3.fromRGB(154, 46, 231), LineThickness = 0 }) do selection_template[index] = item end _G.Methods.forEachConnectedObject(root,function(model) local selection = selection_template:Clone() selection.Adornee = model selection.Parent = model table.insert(SelectionBoxes,selection) end) end end) script.Parent.Unequipped:Connect(function() OffloadSelection() SaveFrame.Visible = false SelectionBox.Adornee = nil if renderSteppedEvent then renderSteppedEvent:Disconnect() end end) script.Parent.Equipped:Connect(function() SaveFrame.Visible = true if renderSteppedEvent then renderSteppedEvent:Disconnect() end renderSteppedEvent = game:GetService("RunService").RenderStepped:Connect(RenderStepped) end) end local player_saves function InitUI(script) SaveFrame = script.Parent Stroke = script:WaitForChild("UIStroke") SaveData = script.Parent:WaitForChild("SaveData") SaveTabs = script.Parent:WaitForChild("SaveTabs") SaveLists = script.Parent:WaitForChild("SaveLists") Templates = script.Parent:WaitForChild("Template") SaveFrame.Visible = false Templates.Frame.Visible = false PlayerSaveList,player_saves = CreateUserSection(_G.UserId,true) for i = 1,2 do local SubSlot = PlayerSaveList:CreateSlot() SubSlot.order = string.format("SUB %i",i) SubSlot.name = string.format("Subassembly %i",i) SubSlot.on_save_clicked = function() if (not CurrentSaveData) or (#CurrentSaveData == 0) then _G.Events.Notification:Fire("Vehicle Save Failed","Please select a vehicle to save") return end SubassemblyData = CurrentSaveData SubassemblyModel = _G.Methods.saves.dataToModel(CurrentSaveData,false) SubSlot.model = SubassemblyModel OffloadSelection() end SubSlot.on_mirror_clicked = function() OffloadSelection() if not SubassemblyData then return end if SubassemblyModel then SubassemblyModel:Destroy() end SubassemblyModel = _G.Methods.saves.dataToModel(SubassemblyData,false,true) SubSlot.model = SubassemblyModel end SubSlot.on_load_clicked = function() OffloadSelection() if not SubassemblyModel then return end CurrentPlacementConfig = Placement.begin(SubassemblyModel:Clone(),{ confirm = function(config) _G.Events.Notification:Fire("Subassembly Placing!") _G.Remotes.PlaceSubassembly:InvokeServer(config.model.PrimaryPart.CFrame,SubassemblyData,config.touching) config:stop() end, stopped = function(config) config.model:Destroy() CurrentPlacementConfig = nil end, }) end SubSlot:show() end CreateUserSection("public",_G.Remotes.Admin:InvokeServer()) end Commands.new("open save (.+)","open save : Opens saves in your tool.",function(userid) CreateUserSection(userid,false) _G.Events.Notification:Fire("Opened Read-Only Save",userid) end) Commands.new("open write save (.+)","open write save : Opens write saves in your tool.",function(userid) CreateUserSection(userid,true) _G.Events.Notification:Fire("Opened Write Save",userid) end) Commands.new("export save (.+) (.+)","export save : Prints out your save",function(userid,key) _G.Events.Notification:Fire("Save Export",tostring(_G.Remotes.Load:InvokeServer(userid,key))) end) Commands.new("show indices","show indices: Prints out save indices",function() local result = "" local indices = _G.SaveIndices for userid,tab in pairs(indices) do result = result .. "Userid: "..tostring(userid).."\n" for index,item in pairs(tab) do result = result .. " "..tostring(index)..": "..tostring(item).."\n" end end print(result) _G.Events.Notification:Fire("Save Indicies",result) end) return getfenv()