null nil CutscenifyCore false Cutscenify {DF21BC6C-6E5D-4259-AD65-A5D04EEDAE06} 1 then cutsceneSelect.CutsceneAlreadyExistsPrompt.Content.Prompt.Buttons.ChooseOrEdit.Text.Text = CutscenifyTranslator:GetTranslationForId("CutsceneSelect.Choose") end cutsceneSelect.CutsceneAlreadyExistsPrompt.Content.Prompt.Content.Amount.CutsceneSaveCount.Text = string.format("%s%s", CutscenifyTranslator:GetTranslationForId("CutsceneSelect.CutsceneSaveCount"), #workspace:WaitForChild("Cutscenify_Saves"):GetChildren()) cutsceneSelect.CutsceneAlreadyExistsPrompt.Content.Prompt.Buttons.ChooseOrEdit.MouseButton1Click:Connect(function() if #workspace:WaitForChild("Cutscenify_Saves"):GetChildren() == 1 then for _, cutsceneSave in ipairs(workspace:WaitForChild("Cutscenify_Saves"):GetChildren()) do CutsceneName = cutsceneSave.Name pluginGui.Title = string.format("%s - Cutscenify", CutsceneName) if workspace:FindFirstChild(CutsceneName) then workspace[CutsceneName]:Destroy() end local cutsceneSaveClone = workspace:WaitForChild("Cutscenify_Saves")[CutsceneName].Model:Clone() cutsceneSaveClone.Parent = workspace cutsceneSaveClone.Name = CutsceneName cutsceneParts = cutsceneSaveClone cutsceneSelect.CutsceneAlreadyExistsPrompt.Visible = false NamePromptFinished = true pluginGui.Enabled = true WindowOpen = true Button:SetActive(true) CutscenePoints = #workspace:WaitForChild("Cutscenify_Saves")[CutsceneName].Model:GetChildren() for _, pointPart in ipairs(workspace:WaitForChild("Cutscenify_Saves")[CutsceneName].Model:GetChildren()) do local pointPartClone = pointPart:Clone() local otherClone = pointPart:Clone() pointPartClone.Parent = cutsceneSaves otherClone.Parent = EditModeParts EditModeParts.Parent = workspace otherClone.Transparency = 0.5 otherClone.numberBillboard.Enabled = true end NamePromptWindowOpen = false end elseif #workspace:WaitForChild("Cutscenify_Saves"):GetChildren() > 1 then cutscenePicker.CutsceneSelectionPrompt.Visible = true cutsceneSelect.CutsceneAlreadyExistsPrompt.Visible = false for _, labelToRemove in ipairs(cutscenePicker.CutsceneSelectionPrompt.Content.Prompt.Content.CutsceneSelection:GetChildren()) do if labelToRemove.Name == "CutsceneName" or labelToRemove:IsA("UIListLayout") then Selection:Set{} else labelToRemove:Destroy() end end RefreshCutscenePickerList() end end) else NameSelect.Parent = game.CoreGui NameSelect.NamePrompt.Visible = true end end end Button.Click:Connect(OpenGui) local function getCameraLookAt(maxRange) if camera then local ray = Ray.new(camera.CFrame.p, camera.CFrame.lookVector * maxRange) local _, pos = workspace:FindPartOnRay(ray) camera.Focus = CFrame.new(pos) return pos else print("Unable to find default camera.") return Vector3.new(0, 5.2, 0) end end ImporterButton.Click:Connect(function() local FBXModel = plugin:ImportFbxRig(true) if FBXModel then local modelSelect = {} table.insert(modelSelect, FBXModel) Selection:Set(modelSelect) FBXModel:MoveTo(getCameraLookAt(10)) print("Rig Imported Successfully!") elseif not FBXModel then print("Rig Import Failed. Reason: FBX File selection was cancelled.") end end) local function BindToCloseFunc() pluginGui.Enabled = false Button:SetActive(false) WindowOpen = false for _, child in ipairs(EditModeParts:GetChildren()) do child.Transparency = 1 child.numberBillboard.Enabled = false print("child disabled") end plugin:Deactivate() end pluginGui:BindToClose(function() BindToCloseFunc() end) -- GUI Setup -- local cameraPositionTabOpen = false local insertCutsceneTabOpen = false local previewTabOpen = false local propertiesCamPosition local propertiesCutsceneInserting optionsButton.MouseEnter:Connect(function() Mouse.Icon = ClickMouse end) optionsButton.MouseLeave:Connect(function() Mouse.Icon = DefaultMouse end) cutscenePicker.CutsceneSelectionPrompt.Content.Prompt.Buttons.CreateNew.Activated:Connect(function() EnterNewNamePromptIsFromCutscenePrompt = true cutscenePicker.CutsceneSelectionPrompt.Visible = false NameSelect.Parent = game.CoreGui NameSelect.NamePrompt.Visible = true end) local function RemoveCutsceneFromList(name) if not cutscenePicker.CutsceneSelectionPrompt.Content.Prompt.Content.CutsceneSelection:FindFirstChild(name) then warn("Child " .. name .. " does not exist!") return end cutscenePicker.CutsceneSelectionPrompt.Content.Prompt.Content.CutsceneSelection:WaitForChild(name):Destroy() end local function AddCutsceneToList(name) local LabelClone = cutscenePicker.CutsceneSelectionPrompt.Content.Prompt.Content.CutsceneSelection.CutsceneName:Clone() LabelClone.Parent = cutscenePicker.CutsceneSelectionPrompt.Content.Prompt.Content.CutsceneSelection LabelClone.Name = name LabelClone.TitleLabel.Text = name LabelClone.Visible = true LabelClone.Button.Activated:Connect(function() cutscenePicker.CutsceneSelectionPrompt.Visible = false local didPressYes = WindowPromptMain:PromptWindowCutscenePicker(string.format("%s%s?", CutscenifyTranslator:GetTranslationForId("CutsceneConfirmation.Text"), name), pluginGui, cutscenePicker, name, cutsceneParts) if didPressYes then CutsceneName = name local newClone = workspace:WaitForChild("Cutscenify_Saves"):WaitForChild(name).Model:Clone() newClone.Parent = workspace newClone.Name = name cutsceneParts = newClone CutscenePoints = #workspace:WaitForChild(name):GetChildren() NamePromptFinished = true WindowOpen = true if roActivity:UsingRoActivity(UserId) then roActivity:SetUserRichPresence(UserId, "Editing " .. CutsceneName .. " | Cutscenify") end cutscenePartsIntentionalParentChange = true cutscenePartsIntentionalNameChange = true wait(0.5) cutscenePartsIntentionalParentChange = false cutscenePartsIntentionalNameChange = false else Selection:Set{} end end) end cutsceneSelect.CutsceneAlreadyExistsPrompt.Content.Prompt.Buttons.New.Activated:Connect(function() cutsceneSelect.CutsceneAlreadyExistsPrompt.Visible = false if NamePromptFinished == false then NameSelect.Parent = game.CoreGui NameSelect.NamePrompt.Visible = true EnterNewNamePromptIsFromCutscenePrompt = true end end) cutsceneSelect.CutsceneAlreadyExistsPrompt.Header.Close.Activated:Connect(function() cutsceneSelect.CutsceneAlreadyExistsPrompt.Visible = false NamePromptWindowOpen = false end) cutscenePicker.CutsceneSelectionPrompt.Header.Close.Activated:Connect(function() cutscenePicker.CutsceneSelectionPrompt.Visible = false NamePromptWindowOpen = false end) cutsceneSelect.CutsceneAlreadyExistsPrompt.Header.WindowTitle.Text = CutscenifyTranslator:GetTranslationForId("CutsceneSelect.Title") cutsceneSelect.CutsceneAlreadyExistsPrompt.Content.Prompt.Content.PromptText.Text = CutscenifyTranslator:GetTranslationForId("CutsceneSelect.Text") cutsceneSelect.CutsceneAlreadyExistsPrompt.Content.Prompt.Buttons.New.Text.Text = CutscenifyTranslator:GetTranslationForId("CutsceneSelect.New") cutscenePicker.CutsceneSelectionPrompt.Header.WindowTitle.Text = CutscenifyTranslator:GetTranslationForId("CutsceneSelect.Title") cutscenePicker.CutsceneSelectionPrompt.Content.Prompt.Content.PromptText.Text = CutscenifyTranslator:GetTranslationForId("CutscenePicker.Title") cutscenePicker.CutsceneSelectionPrompt.Content.Prompt.Buttons.CreateNew.Text.Text = CutscenifyTranslator:GetTranslationForId("CutsceneSelect.New") optionsButton.Button.Activated:Connect(function() local Menu = plugin:CreatePluginMenu("OptionsMenu") Menu:AddNewAction("RenameButton", CutscenifyTranslator:GetTranslationForId("More.Rename")).Triggered:Connect(function() RenameFrame.Visible = true Background.Visible = true end) -- Menu:AddNewAction("InsertButton", "Insert from File").Triggered:Connect(function() -- local File = StudioService:PromptImportFile({"rbxm", "rbxmx"}) -- if File then -- local FileContents = File:GetBinaryContents() -- File.Parent = workspace -- File.Name = FileContents.Name -- end -- end) Menu:AddSeparator() -- Menu:AddNewAction("SaveButton", "Save to File").Triggered:Connect(function() -- SelectionChangeIntended = true -- local newSelection = {} -- table.insert(newSelection, cutsceneParts) -- Selection:Set(newSelection) -- local didSave = plugin:PromptSaveSelection(string.format("%s - Cutscenify", CutsceneName)) -- if not didSave then -- SelectionChangeIntended = false -- Selection:Set{} -- end -- end) if #Selection:Get() > 0 then Menu:AddNewAction("LookAtSelectionButton", "Look At Selected Object").Triggered:Connect(function() for _, obj in pairs(Selection:Get()) do if obj:IsA("BasePart") then camera.CameraType = Enum.CameraType.Scriptable camera.CFrame = CFrame.new(camera.CFrame.Position, obj.Position) -- camera.CameraType = Enum.CameraType.Fixed end end end) Menu:AddSeparator() elseif isACutscenePointSelected then SubMenu = plugin:CreatePluginMenu("CutscenePointSubMenu", "Cutscene Point") SubMenu:AddNewAction("RepositionButton", "Reposition").Triggered:Connect(function() -- repeat -- selectedCutscenePoint.CFrame = camera.CFrame -- wait(0.1) -- until end) SubMenu:AddNewAction("ReconfigureButton", "Reconfigure").Triggered:Connect(function() -- Action logic goes here... end) SubMenu:AddNewAction("DeleteButton", "Delete Point").Triggered:Connect(function() -- Action logic goes here... end) Menu:AddMenu(SubMenu) Menu:AddSeparator() end Menu:AddNewAction("SettingsButton", "Settings").Triggered:Connect(function() local isToggled = true local SettingsMenu = Roact.createElement(BaseMenu, { text = "Settings", theme = studioTheme, withBackButton = true }) local ShowChangeLogToggle = Roact.createElement(KnobSelect, { theme = studioTheme, text = "Show Change Log", enabled = true, layoutOrder = 2, onActivated = function() print("I was activated!") end }) local tree = Roact.mount(SettingsMenu, pluginGui, "SettingsMenu") local changeLogTree = Roact.mount(ShowChangeLogToggle, pluginGui:WaitForChild("SettingsMenu").Page.PageContainer, "ChangeLogToggle") pluginGui:WaitForChild("SettingsMenu").PageTitle.BackButton.Activated:Connect(function() Roact.unmount(tree) end) Studio.ThemeChanged:Connect(function() local newMenu = Roact.createElement(BaseMenu, { text = "Settings", theme = studioTheme, withBackButton = true }) local ChangeLogToggleUpdate = Roact.createElement(KnobSelect, { theme = studioTheme, text = "Show Change Log", enabled = true, layoutOrder = 2, onActivated = function() print("I was activated!") end }) local newTree = Roact.update(tree, newMenu) local newChangeLogTree = Roact.update(changeLogTree, ChangeLogToggleUpdate) end) end) Menu:AddNewAction("ExitButton", CutscenifyTranslator:GetTranslationForId("More.Exit")).Triggered:Connect(function() -- for _, child in ipairs(EditModeParts:GetChildren()) do -- print(child) -- child:Destroy() -- end workspace:WaitForChild(EditModeParts.Name):ClearAllChildren() for _, child in ipairs(workspace:WaitForChild(CutsceneName):GetChildren()) do child.Transparency = 1 child.numberBillboard.Enabled = false end EditModeParts.Parent = script CutsceneName = "" pluginGui.Enabled = false WindowOpen = false Button:SetActive(false) NamePromptFinished = false end) Menu:ShowAsync() if SubMenu then SubMenu:ShowAsync() SubMenu:Destroy() end Menu:Destroy() end) NameSelect.NamePrompt.Done.Activated:Connect(function() if NamePromptFinished == false then if NameText.Text == "" then warn("Name cannot be blank.") else previousCutsceneName = CutsceneName CutsceneName = NameSelect.NamePrompt.NameFrame.TextBox.Text if workspace:FindFirstChild("Cutscenify_Saves") then if not workspace:WaitForChild("Cutscenify_Saves"):FindFirstChild(CutsceneName) then Selection:Set{} else warn("The name you have chosen (" .. CutsceneName .. "), has already been used. Please choose another.") CutsceneName = previousCutsceneName return end end if workspace:FindFirstChild(CutsceneName) then workspace[CutsceneName]:Destroy() end EditModeParts.Parent = workspace NameSelect.NamePrompt.NameFrame.TextBox.Text = "" pluginGui.Title = string.format("%s - Cutscenify", CutsceneName) cutscenePartsIntentionalNameChange = true cutsceneParts.Name = CutsceneName cutscenePartsIntentionalParentChange = true cutsceneParts.Parent = workspace if EnterNewNamePromptIsFromCutscenePrompt then CutscenePoints = 0 end cutscenePartsIntentionalParentChange = false cutscenePartsIntentionalNameChange = false NameSelect.NamePrompt.Visible = false pluginGui.Enabled = true WindowOpen = true NamePromptFinished = true if not EnterNewNamePromptIsFromCutscenePrompt or not workspace:FindFirstChild("Cutscenify_Saves") then newCloneSaves = cutsceneSaves:Clone() newCloneSaves.Parent = workspace else newCloneSaves = workspace:WaitForChild("Cutscenify_Saves") end Selection.SelectionChanged:Connect(function() for _, object in pairs(Selection:Get()) do if object == newCloneSaves then Selection:Set{} end end end) local newValue = Instance.new("StringValue", newCloneSaves) newValue.Name = CutsceneName Selection.SelectionChanged:Connect(function() for _, object in pairs(Selection:Get()) do if object == newValue then Selection:Set{} end end end) local model = Instance.new("Model", newValue) Selection.SelectionChanged:Connect(function() for _, object in pairs(Selection:Get()) do if object == model then Selection:Set{} end end end) end EnterNewNamePromptIsFromCutscenePrompt = false NamePromptFinished = true NamePromptWindowOpen = false else warn("Cutscene Prompt has already been completed.") end end) RenameFrame.Done.Activated:Connect(function() if RenameFrame.NameFrame.TextBox.Text == CutsceneName then warn("That is the current name of the cutscene!") return elseif workspace:WaitForChild("Cutscenify_Saves"):FindFirstChild(RenameFrame.NameFrame.TextBox.Text) then warn("You've already used that name! Please choose another.") end local cutsceneSaveToEdit = workspace:WaitForChild("Cutscenify_Saves")[CutsceneName] cutsceneSaveToEdit.Name = RenameFrame.NameFrame.TextBox.Text CutsceneName = RenameFrame.NameFrame.TextBox.Text cutsceneParts.Name = CutsceneName RenameFrame.NameFrame.TextBox.Text = "" pluginGui.Title = string.format("%s - Cutscenify", CutsceneName) RenameFrame.Visible = false Background.Visible = false end) Selection.SelectionChanged:Connect(function() for _, object in pairs(Selection:Get()) do if object == cutsceneParts then if SelectionChangeIntended == false then Selection:Set{} end end end end) cutsceneParts:GetPropertyChangedSignal("Parent"):Connect(function() if cutscenePartsIntentionalParentChange == false then cutsceneParts.Parent = workspace end end) cutsceneParts:GetPropertyChangedSignal("Name"):Connect(function() if not cutscenePartsIntentionalNameChange then local success, errors = pcall(function() cutsceneParts.Name = CutsceneName end) else Selection:Set{} end end) local cameraCapturePositionButton = ImageButtonWithText.new( "CaptureCamPosition", -- name of the gui object 1, -- sets the sorting order for use with a UIGridStyleLayout object "rbxassetid://3944675151", -- the asset id of the image CutscenifyTranslator:GetTranslationForId("MainUi.AddPoint"), -- button text UDim2.new(0, 100, 0, 100), -- button size UDim2.new(0, 70, 0, 70), -- image size UDim2.new(0, 15, 0, 15), -- image position UDim2.new(0, 60, 0, 20), -- text size UDim2.new(0, 20, 0, 80) -- text position ) -- use the :getButton() method to return an ImageButton gui object local capturePositionButtonInstance = cameraCapturePositionButton:getButton() capturePositionButtonInstance.Parent = cutscenifyUI.Actions if studioTheme == "Light" then capturePositionButtonInstance.ImageLabel.ImageColor3 = Color3.new(0, 0, 0) end local playCutsceneButton = ImageButtonWithText.new( "CutscenePlay", -- name of the gui object 1, -- sets the sorting order for use with a UIGridStyleLayout object "rbxassetid://4335484343", -- the asset id of the image CutscenifyTranslator:GetTranslationForId("MainUi.Preview"), -- button text UDim2.new(0, 100, 0, 100), -- button size UDim2.new(0, 70, 0, 70), -- image size UDim2.new(0, 15, 0, 15), -- image position UDim2.new(0, 60, 0, 20), -- text size UDim2.new(0, 20, 0, 80) -- text position ) -- use the :getButton() method to return an ImageButton gui object local playCutsceneButtonObj = playCutsceneButton:getButton() playCutsceneButtonObj.Parent = cutscenifyUI.Actions if studioTheme == "Light" then playCutsceneButtonObj.ImageLabel.ImageColor3 = Color3.new(0, 0, 0) end local saveCutsceneButton = ImageButtonWithText.new( "CutsceneInsert", -- name of the gui object 1, -- sets the sorting order for use with a UIGridStyleLayout object "rbxassetid://4400701828", -- the asset id of the image CutscenifyTranslator:GetTranslationForId("MainUi.Insert"), -- button text UDim2.new(0, 100, 0, 100), -- button size UDim2.new(0, 70, 0, 70), -- image size UDim2.new(0, 15, 0, 15), -- image position UDim2.new(0, 60, 0, 20), -- text size UDim2.new(0, 20, 0, 80) -- text position ) -- use the :getButton() method to return an ImageButton gui object local saveCutsceneButtonObj = saveCutsceneButton:getButton() saveCutsceneButtonObj.Parent = cutscenifyUI.Actions if studioTheme == "Light" then saveCutsceneButtonObj.ImageLabel.ImageColor3 = Color3.new(0, 0, 0) end local function AddProperties(name, text, tabType) local collapse = CollapsibleTitledSection.new( name, -- name suffix of the gui object text, -- the text displayed beside the collapsible arrow true, -- show the title text? false, -- minimizable? false -- minimized by default? ) collapse:GetSectionFrame().Parent = cutscenifyUI.Properties if tabType == "AddPoint" then addPointConfigurationTab = collapse:GetSectionFrame() elseif tabType == "InsertCutscene" then addCutsceneScriptTab = collapse:GetSectionFrame() end return collapse end ------------STUDIO THEME------------- local function ChangeCutscenifyPluginGuiColors(withTheme) if withTheme == "Dark" then cutscenifyUI.BackgroundColor3 = Color3.fromRGB(46, 46, 46) optionsButton.Button.BackgroundColor3 = Color3.fromRGB(46, 46, 46) optionsButton.Button.BorderColor3 = Color3.fromRGB(34, 34, 34) optionsButton.Button.image.ImageColor3 = Color3.fromRGB(204, 204, 204) capturePositionButtonInstance.ImageLabel.ImageColor3 = Color3.new(1, 1, 1) playCutsceneButtonObj.ImageLabel.ImageColor3 = Color3.new(1, 1, 1) saveCutsceneButtonObj.ImageLabel.ImageColor3 = Color3.new(1, 1, 1) PluginDetails.TextColor3 = Color3.new(1, 1, 1) elseif withTheme == "Light" then cutscenifyUI.BackgroundColor3 = Color3.new(1, 1, 1) optionsButton.Button.BackgroundColor3 = Color3.new(1, 1, 1) optionsButton.Button.BorderColor3 = Color3.fromRGB(221, 221, 221) optionsButton.Button.image.ImageColor3 = Color3.new(0, 0, 0) capturePositionButtonInstance.ImageLabel.ImageColor3 = Color3.new(0, 0, 0) playCutsceneButtonObj.ImageLabel.ImageColor3 = Color3.new(0, 0, 0) saveCutsceneButtonObj.ImageLabel.ImageColor3 = Color3.new(0, 0, 0) PluginDetails.TextColor3 = Color3.new(0, 0, 0) end end Studio.ThemeChanged:Connect(function() studioTheme = Studio.Theme.Name ChangeCutscenifyPluginGuiColors(studioTheme) end) ChangeCutscenifyPluginGuiColors(studioTheme) ------------REST OF CODE------------- cameraEffect.Parent = game.CoreGui capturePositionButtonInstance.Activated:Connect(function() if cameraPositionTabOpen == false then cameraPositionTabOpen = true insertCutsceneTabOpen = false propertiesCamPosition = AddProperties("CameraPositionCaptureTab", CutscenifyTranslator:GetTranslationForId("Properties.Point.Title"), "AddPoint") if cutscenifyUI.Properties:FindFirstChild("CTSectionSaveCutsceneTab") then addCutsceneScriptTab:Destroy() addCutsceneScriptTab = nil end local layout = Instance.new("UIListLayout", propertiesCamPosition:GetContentsFrame()) layout.SortOrder = Enum.SortOrder.LayoutOrder layout.HorizontalAlignment = Enum.HorizontalAlignment.Center local ImmediateSwitchCheckbox = LabeledCheckbox.new( "ImmediateSwitch", -- name suffix of gui object CutscenifyTranslator:GetTranslationForId("Properties.Point.ImmediateSwitch"), -- text beside the checkbox false, -- initial value false -- initially disabled? ) ImmediateSwitchCheckbox:GetFrame().Parent = propertiesCamPosition:GetContentsFrame() if CutscenifyTranslator:GetTranslationForId("Properties.Point.ImmediateSwitch") == "Switch to Next Point when done?" then ImmediateSwitchCheckbox:GetFrame().FullBackground.Button.Position = UDim2.new(0, 215, 0.5, 0) else ImmediateSwitchCheckbox:GetFrame().FullBackground.Button.Position = UDim2.new(0, 160, 0.5, 0) end if #workspace:WaitForChild(CutsceneName):GetChildren() == 0 then ImmediateSwitchCheckbox:GetFrame():Destroy() end local sliderFOV = LabeledSlider.new( "FieldOfViewSlider", -- name suffix of gui object string.format("%s (%s)", CutscenifyTranslator:GetTranslationForId("Properties.Point.FOV"), camera.FieldOfView), -- text beside the checkbox 120, -- how many intervals to split the slider into camera.FieldOfView -- the starting value of the slider ) sliderFOV:GetFrame().Parent = propertiesCamPosition:GetContentsFrame() sliderFOV:SetValueChangedFunction(function() camera.FieldOfView = sliderFOV:GetValue() sliderFOV:GetFrame().Label.Text = string.format("%s (%s)", CutscenifyTranslator:GetTranslationForId("Properties.Point.FOV"), sliderFOV:GetValue()) end) local sliderTweenTime = LabeledSlider.new( "TransitionSlider", -- name suffix of gui object string.format("%s (5)", CutscenifyTranslator:GetTranslationForId("Properties.Point.Transition")), -- text beside the checkbox 20, -- how many intervals to split the slider into 5 -- the starting value of the slider ) sliderTweenTime:GetFrame().Parent = propertiesCamPosition:GetContentsFrame() sliderTweenTime:SetValueChangedFunction(function() sliderTweenTime:GetFrame().Label.Text = string.format("%s (%s)", CutscenifyTranslator:GetTranslationForId("Properties.Point.Transition"), sliderTweenTime:GetValue()) end) local EnableDelayCheckbox = LabeledCheckbox.new( "EnableDelayCheckbox", -- name suffix of gui object "Enable Delay?", -- text beside the checkbox true, -- initial value false -- initially disabled? ) EnableDelayCheckbox:GetFrame().Parent = propertiesCamPosition:GetContentsFrame() local sliderDelayTime = LabeledSlider.new( "DelaySlider", -- name suffix of gui object string.format("%s (1)", "Delay"), -- text beside the checkbox 20, -- how many intervals to split the slider into 1 -- the starting value of the slider ) sliderDelayTime:GetFrame().Parent = propertiesCamPosition:GetContentsFrame() sliderDelayTime:SetValueChangedFunction(function() sliderDelayTime:GetFrame().Label.Text = string.format("%s (%s)", "Delay", sliderDelayTime:GetValue()) end) EnableDelayCheckbox:SetValueChangedFunction(function() if not EnableDelayCheckbox:GetValue() then sliderDelayTime:GetFrame().Visible = false else sliderDelayTime:GetFrame().Visible = true end end) -- local sliderCameraRoll = LabeledSlider.new( -- "CameraRollSlider", -- name suffix of gui object -- "Rotation (1)", -- text beside the checkbox -- 360, -- how many intervals to split the slider into -- 1 -- the starting value of the slider -- ) -- sliderCameraRoll:GetFrame().Parent = propertiesCamPosition:GetContentsFrame() -- sliderCameraRoll:SetValueChangedFunction(function() -- camera.CameraType = Enum.CameraType.Scriptable -- camera:SetRoll((sliderCameraRoll:GetValue()*.01)-6.28) -- sliderCameraRoll:GetFrame().Label.Text = string.format("Rotation (%s)", sliderCameraRoll:GetValue()) -- end) local choices = { {Id = "in", Text = CutscenifyTranslator:GetTranslationForId("In")}, {Id = "out", Text = CutscenifyTranslator:GetTranslationForId("EDir.Out")}, {Id = "inout", Text = CutscenifyTranslator:GetTranslationForId("EDir.InOut")} } local easingDirectionMultiChoice = LabeledMultiChoice.new( "EasingDirectionMultiChoice", -- name suffix of gui object CutscenifyTranslator:GetTranslationForId("Properties.Point.EasingDirection"), -- title text of the multi choice choices, -- choices array 1 -- the starting index of the selection (in this case choice 1) ) easingDirectionMultiChoice:GetFrame().Parent = propertiesCamPosition:GetContentsFrame() local easingStyleChoices = { {Id = "linear", Text = CutscenifyTranslator:GetTranslationForId("ESty.Linear")}, {Id = "quad", Text = CutscenifyTranslator:GetTranslationForId("ESty.Quad")}, {Id = "quart", Text = CutscenifyTranslator:GetTranslationForId("ESty.Quart")}, {Id = "quint", Text = CutscenifyTranslator:GetTranslationForId("ESty.Quint")}, {Id = "sine", Text = CutscenifyTranslator:GetTranslationForId("ESty.Sine")}, {Id = "back", Text = CutscenifyTranslator:GetTranslationForId("ESty.Back")}, {Id = "bounce", Text = CutscenifyTranslator:GetTranslationForId("ESty.Bounce")}, {Id = "circular", Text = CutscenifyTranslator:GetTranslationForId("ESty.Circular")}, {Id = "cubic", Text = CutscenifyTranslator:GetTranslationForId("ESty.Cubic")}, {Id = "elastic", Text = CutscenifyTranslator:GetTranslationForId("ESty.Elastic")}, {Id = "exponential", Text = CutscenifyTranslator:GetTranslationForId("ESty.Exponential")} } local easingStyleMultiChoice = LabeledMultiChoice.new( "EasingStyleMultiChoice", -- name suffix of gui object CutscenifyTranslator:GetTranslationForId("Properties.Point.EasingStyle"), -- title text of the multi choice easingStyleChoices, -- choices array 1 -- the starting index of the selection (in this case choice 1) ) easingStyleMultiChoice:GetFrame().Parent = propertiesCamPosition:GetContentsFrame() local divider = Instance.new("Frame", propertiesCamPosition:GetContentsFrame()) divider.BackgroundTransparency = 1 divider.Name = "divider" divider.Size = UDim2.new(1, 0, 0, 10) divider.LayoutOrder = 2 local button = CustomTextButton.new( "AddPointButton", -- name of the gui object CutscenifyTranslator:GetTranslationForId("Properties.Point.Add") -- the text displayed on the button ) -- use the :getButton() method to return the ImageButton gui object local buttonObject = button:GetButton() buttonObject.Size = UDim2.new(0, 150, 0, 25) buttonObject.LayoutOrder = 2 buttonObject.MouseEnter:Connect(function() Mouse.Icon = ClickMouse end) buttonObject.MouseLeave:Connect(function() Mouse.Icon = DefaultMouse end) buttonObject.Activated:Connect(function() Mouse.Icon = DefaultMouse camera.CameraType = Enum.CameraType.Scriptable CutscenePoints = CutscenePoints + 1 local NewCutscenePointPart = Instance.new("Part", cutsceneParts) NewCutscenePointPart.Name = CutscenePoints NewCutscenePointPart.Anchored = true NewCutscenePointPart.Color = Color3.new(0, 0, 0) NewCutscenePointPart.Transparency = 1 NewCutscenePointPart.CFrame = camera.CFrame NewCutscenePointPart.Locked = true local FOVNumberValue = Instance.new("NumberValue", NewCutscenePointPart) FOVNumberValue.Name = "CameraFOV" FOVNumberValue.Value = sliderFOV:GetValue() local TransitionValue = Instance.new("NumberValue", NewCutscenePointPart) TransitionValue.Name = "TransitionTime" TransitionValue.Value = sliderTweenTime:GetValue() local DelayValue = Instance.new("NumberValue", NewCutscenePointPart) DelayValue.Name = "DelayTime" if EnableDelayCheckbox:GetValue() == true then DelayValue.Value = 0 else DelayValue.Value = sliderDelayTime end local EasingDirectionValue = Instance.new("NumberValue", NewCutscenePointPart) EasingDirectionValue.Name = "EasingDirection" EasingDirectionValue.Value = easingDirectionMultiChoice:GetSelectedIndex() local EasingStyleValue = Instance.new("NumberValue", NewCutscenePointPart) EasingStyleValue.Name = "EasingStyle" EasingStyleValue.Value = easingStyleMultiChoice:GetSelectedIndex() local ImmediateSwitchValue = Instance.new("BoolValue", NewCutscenePointPart) ImmediateSwitchValue.Name = "ImmediateSwitch" if #workspace:WaitForChild(CutsceneName):GetChildren() == 0 then ImmediateSwitchValue.Value = false end ImmediateSwitchValue.Value = ImmediateSwitchCheckbox:GetValue() local Billboard = Instance.new("BillboardGui", NewCutscenePointPart) Billboard.AlwaysOnTop = true Billboard.StudsOffset = Vector3.new(0, 3, 0) Billboard.Name = "numberBillboard" Billboard.Active = true Billboard.Enabled = false Billboard.Size = UDim2.new(0, 200, 0, 50) local TextLabel = Instance.new("TextLabel", Billboard) TextLabel.BackgroundTransparency = 1 TextLabel.Size = UDim2.new(1, 0, 1, 0) TextLabel.Name = "Number" TextLabel.Font = Enum.Font.SourceSansBold TextLabel.TextColor3 = Color3.new(0, 0, 0) TextLabel.TextStrokeColor3 = Color3.new(1, 1, 1) TextLabel.TextStrokeTransparency = 0 TextLabel.TextScaled = true TextLabel.Text = CutscenePoints propertiesCamPosition:GetSectionFrame():Destroy() cameraPositionTabOpen = false camera.FieldOfView = 70 local pointPartClone = NewCutscenePointPart:Clone() pointPartClone.Parent = workspace.Cutscenify_Saves[CutsceneName].Model pointPartClone.numberBillboard.Enabled = false pointPartClone.Transparency = 1 local cameraFlashPropChange1 = { BackgroundTransparency = 0 } local pointPartVisible = NewCutscenePointPart:Clone() pointPartVisible.Parent = EditModeParts pointPartVisible.Transparency = 0.5 pointPartVisible.numberBillboard.Enabled = true camera.CameraType = Enum.CameraType.Fixed local cameraFlashTweenInfo1 = TweenInfo.new(0.5, Enum.EasingStyle.Linear, Enum.EasingDirection.InOut, 0, true, 0) local cameraFlashTween1 = TweenService:Create(cameraEffect.White, cameraFlashTweenInfo1, cameraFlashPropChange1) cameraFlashTween1:Play() end) buttonObject.Parent = propertiesCamPosition:GetContentsFrame() else cameraPositionTabOpen = false propertiesCamPosition:GetSectionFrame():Destroy() end end) playCutsceneButtonObj.MouseButton1Click:Connect(function() if #cutsceneParts:GetChildren() <= 1 then ErrorPromptMain:AddError(CutscenifyTranslator:GetTranslationForId("Warning.CutscenePoints"), pluginGui) return end if cameraPositionTabOpen == true then addPointConfigurationTab:Destroy() cameraPositionTabOpen = false elseif insertCutsceneTabOpen == true then addCutsceneScriptTab:Destroy() insertCutsceneTabOpen = false end for _, pointPartInstance in ipairs(EditModeParts:GetChildren()) do pointPartInstance.Transparency = 1 pointPartInstance.numberBillboard.Enabled = false end camera.CameraType = Enum.CameraType.Scriptable camera.CFrame = cutsceneParts["1"].CFrame camera.FieldOfView = cutsceneParts["1"].CameraFOV.Value -- local firstPartOldTransitionTime = cutsceneParts["1"].TransitionTime.Value -- cutsceneParts["1"].TransitionTime.Value = 3 -- local CutscenePart = Instance.new("Part", workspace) -- CutscenePart.Name = "CutscenePart" -- CutscenePart.Transparency = 1 -- CutscenePart.Locked = true pluginGui.Enabled = false Button:SetActive(false) WindowOpen = false Button.Enabled = false local ScreenGuiClickEater = Instance.new("ScreenGui", game.CoreGui) ScreenGuiClickEater.Name = "ClickEater" local ClickEater = Instance.new("TextButton", ScreenGuiClickEater) ClickEater.Name = "Click" ClickEater.Size = UDim2.new(1, 0, 1, 0) ClickEater.Text = "" ClickEater.BackgroundTransparency = 1 local ScreenGui = Instance.new("ScreenGui", game.CoreGui) ScreenGui.Name = "CutsceneTimerGui" ScreenGui.ResetOnSpawn = false local message = Instance.new("TextLabel", ScreenGui) message.Name = "CutsceneTimerText" message.AnchorPoint = Vector2.new(0.5, 0.5) message.Position = UDim2.new(0.5, 0, 0.5, 0) message.Size = UDim2.new(0, 71, 0, 50) message.BackgroundTransparency = 1 message.Text = "3" message.Font = Enum.Font.SourceSansBold message.TextSize = 100 message.TextColor3 = Color3.new(1, 1, 1) message.TextStrokeTransparency = 0 wait(1) message.Text = "2" wait(1) message.Text = "1" wait(1) ScreenGui:Destroy() camera.CameraType = Enum.CameraType.Scriptable camera.FieldOfView = cutsceneParts["1"].CameraFOV.Value camera.CFrame = cutsceneParts["1"].CFrame -- Cutscene.run(cutsceneParts) for _, pointPartInstance in ipairs(cutsceneParts:GetChildren()) do if pointPartInstance.EasingDirection.Value == 1 then easingDirection = Enum.EasingDirection.In elseif pointPartInstance.EasingDirection.Value == 2 then easingDirection = Enum.EasingDirection.Out elseif pointPartInstance.EasingDirection.Value == 3 then easingDirection = Enum.EasingDirection.InOut end if pointPartInstance.EasingStyle.Value == 1 then easingStyle = Enum.EasingStyle.Linear elseif pointPartInstance.EasingStyle.Value == 2 then easingStyle = Enum.EasingStyle.Quad elseif pointPartInstance.EasingStyle.Value == 3 then easingStyle = Enum.EasingStyle.Quart elseif pointPartInstance.EasingStyle.Value == 4 then easingStyle = Enum.EasingStyle.Quint elseif pointPartInstance.EasingStyle.Value == 5 then easingStyle = Enum.EasingStyle.Sine elseif pointPartInstance.EasingStyle.Value == 6 then easingStyle = Enum.EasingStyle.Back elseif pointPartInstance.EasingStyle.Value == 7 then easingStyle = Enum.EasingStyle.Bounce elseif pointPartInstance.EasingStyle.Value == 8 then easingStyle = Enum.EasingStyle.Circular elseif pointPartInstance.EasingStyle.Value == 9 then easingStyle = Enum.EasingStyle.Cubic elseif pointPartInstance.EasingStyle.Value == 10 then easingStyle = Enum.EasingStyle.Elastic elseif pointPartInstance.EasingStyle.Value == 11 then easingStyle = Enum.EasingStyle.Exponential end if pointPartInstance.Name == "1" then lastCutscenePart = #cutsceneParts:GetChildren() -- camera.CFrame = cutsceneParts[lastCutscenePart].CFrame camera.CFrame = pointPartInstance.CFrame camera.FieldOfView = pointPartInstance.CameraFOV.Value else local delayTime = tonumber(pointPartInstance:WaitForChild("DelayTime").Value) local tweenInfo = TweenInfo.new(pointPartInstance.TransitionTime.Value, easingStyle, easingDirection, 0, false, tonumber(pointPartInstance:WaitForChild("DelayTime").Value)) fieldOfView = pointPartInstance.CameraFOV.Value CutsceneManager:PlayPartOfCutscene(camera, pointPartInstance, fieldOfView, tweenInfo) end end wait(0.1) camera.CameraType = Enum.CameraType.Fixed camera.FieldOfView = 70 -- CutscenePart:Destroy() ScreenGuiClickEater:Destroy() pluginGui.Enabled = true Button.Enabled = true for _, pointPartInstance in ipairs(EditModeParts:GetChildren()) do pointPartInstance.Transparency = 0.5 pointPartInstance.numberBillboard.Enabled = true end ScreenGuiClickEater:Destroy() pluginGui.Enabled = true Button.Enabled = true camera.CameraType = Enum.CameraType.Fixed camera.FieldOfView = 70 for _, pointPartInstance in ipairs(EditModeParts:GetChildren()) do pointPartInstance.Transparency = 0.5 pointPartInstance.numberBillboard.Enabled = true end -- cutsceneParts["1"].TransitionTime.Value = firstPartOldTransitionTime end) saveCutsceneButtonObj.Activated:Connect(function() if #cutsceneParts:GetChildren() <= 1 then ErrorPromptMain:AddError(CutscenifyTranslator:GetTranslationForId("Warning.CutscenePoints"), pluginGui) return end if insertCutsceneTabOpen == false then insertCutsceneTabOpen = true cameraPositionTabOpen = false propertiesCutsceneInserting = AddProperties("SaveCutsceneTab", CutscenifyTranslator:GetTranslationForId("Properties.Insert.Title"), "InsertCutscene") if cutscenifyUI.Properties:FindFirstChild("CTSectionCameraPositionCaptureTab") then addPointConfigurationTab:Destroy() addPointConfigurationTab = nil end local choicesInsertType = { {Id = "modulescript", Text = CutscenifyTranslator:GetTranslationForId("Properties.Insert.Executable")}, {Id = "part", Text = CutscenifyTranslator:GetTranslationForId("Properties.Insert.Part")}, {Id = "playerentered", Text = CutscenifyTranslator:GetTranslationForId("Properties.Insert.PlayerAdded")}, {Id = "playerdied", Text = CutscenifyTranslator:GetTranslationForId("Properties.Insert.PlayerDied")} } local scriptTypeMultiChoice = LabeledMultiChoice.new( "ScriptTypeMultiChoice", -- name suffix of gui object CutscenifyTranslator:GetTranslationForId("Properties.Insert.ScriptType"), -- title text of the multi choice choicesInsertType, -- choices array 1 -- the starting index of the selection (in this case choice 1) ) scriptTypeMultiChoice:GetFrame().Parent = propertiesCutsceneInserting:GetContentsFrame() local StartAndEndPlayerCheckbox = LabeledCheckbox.new( "StartAndEndAtPlayer", -- name suffix of gui object CutscenifyTranslator:GetTranslationForId("Properties.Insert.DoesStartAndEndPlayer"), -- text beside the checkbox false, -- initial value false -- initially disabled? ) if StartAndEndPlayerCheckbox:GetFrame().FullBackground.Label.Text == "플레이어의 시작 및 끝 컷씬?" then StartAndEndPlayerCheckbox:GetFrame().FullBackground.Button.Position = UDim2.new(0, 170, 0.5, 0) elseif StartAndEndPlayerCheckbox:GetFrame().FullBackground.Label.Text == "开始和结束的过场动画的播放器?" then StartAndEndPlayerCheckbox:GetFrame().FullBackground.Button.Position = UDim2.new(0, 205, 0.5, 0) else StartAndEndPlayerCheckbox:GetFrame().FullBackground.Button.Position = UDim2.new(0, 210, 0.5, 0) end StartAndEndPlayerCheckbox:GetFrame().Parent = propertiesCutsceneInserting:GetContentsFrame() -- local skipButtonCheckbox = LabeledCheckbox.new( -- "CutsceneHasSkipButton", -- name suffix of gui object -- "Skip Button", -- text beside the checkbox -- true, -- initial value -- false -- initially disabled? -- ) -- skipButtonCheckbox:GetFrame().Parent = propertiesCutsceneInserting:GetContentsFrame() -- local choicesSkipBtnType = { -- {Id = "imgbutton", Text = "Rounded Button"}, -- {Id = "bluerounded", Text = "Blue"}, -- {Id = "classicrounded", Text = "Classic"}, -- {Id = "custom", Text = "Custom"} -- } -- local skipButtonMultiChoice = LabeledMultiChoice.new( -- "SkipButtonStyleMultiChoice", -- name suffix of gui object -- "Skip Button Style", -- title text of the multi choice -- choicesSkipBtnType, -- choices array -- 1 -- the starting index of the selection (in this case choice 1) -- ) -- skipButtonMultiChoice:GetFrame().Parent = propertiesCutsceneInserting:GetContentsFrame() -- if studioTheme == "Light" then -- buttonPreviewBgColor = Color3.new(1, 1, 1) -- end -- local buttonPreview = Roact.createElement("Frame", {}, { -- BackgroundColor3 = buttonPreviewBgColor, -- BorderSizePixel = 0, -- Size = UDim2.new(1, 0, 0, 95), -- Layout = Roact.createElement("UIListLayout", {}, { -- SortOrder = Enum.SortOrder.LayoutOrder -- }), -- Title = Roact.createElement("Frame", {}, { -- BackgroundTransparency = 1, -- LayoutOrder = 0, -- Size = UDim2.new(1, 0, 0, 30), -- Label = Roact.createElement("TextLabel", {}, { -- AnchorPoint = Vector2.new(0, 0.5), -- BackgroundTransparency = 1, -- Position = UDim2.new(0, 27, 0.5, -3), -- Size = UDim2.new(0, 90, 1, 0), -- Font = Enum.Font.SourceSans, -- Text = CutscenifyTranslator:GetTranslationForId("MainUi.Preview"), -- TextColor3 = Color3.fromRGB(204, 204, 204) -- }) -- }), -- RadioButtons = Roact.createElement("Frame", {}, { -- BackgroundTransparency = 1, -- LayoutOrder = 1, -- Size = UDim2.new(1, 0, 0, 55), -- previewImage = Roact.createElement("ImageButton", {}, { -- AnchorPoint = Vector2.new(0.5, 0.5), -- Position = UDim2.new(0.5, 0, 0.5, 0), -- BackgroundTransparency = 1, -- Size = UDim2.new(0, 200, 0, 50), -- Image = "rbxasset://textures/TerrainTools/button_default.png", -- ScaleType = Enum.ScaleType.Slice, -- SliceCenter = Rect.new(7, 7, 156, 36), -- Text = Roact.createElement("TextLabel", { -- Size = UDim2.new(1, 0, 1, -5), -- Text = "Skip Cutscene" -- }) -- }) -- }), -- BottomPadding = Roact.createElement("Frame", {}, { -- BackgroundTransparency = 1, -- LayoutOrder = 1000, -- Size = UDim2.new(1, 0, 0, 10) -- }) -- }) -- local skipButtonRoactTree = Roact.mount(buttonPreview, propertiesCutsceneInserting:GetContentsFrame(), "SkipButtonPreview") local addButton = CustomTextButton.new( "InsertButton", -- name of the gui object CutscenifyTranslator:GetTranslationForId("Properties.Insert.Button") -- the text displayed on the button ) -- use the :getButton() method to return the ImageButton gui object local buttonObjectInsert = addButton:GetButton() buttonObjectInsert.Size = UDim2.new(0, 150, 0, 25) buttonObjectInsert.LayoutOrder = 2 buttonObjectInsert.MouseEnter:Connect(function() Mouse.Icon = ClickMouse end) buttonObjectInsert.MouseLeave:Connect(function() Mouse.Icon = DefaultMouse end) buttonObjectInsert.Parent = propertiesCutsceneInserting:GetContentsFrame() buttonObjectInsert.Activated:Connect(function() Mouse.Icon = DefaultMouse if scriptTypeMultiChoice:GetSelectedIndex() == 1 then local newModuleClone = script.CutsceneExecutionModule:Clone() newModuleClone.Parent = workspace local cutsceneNameValue = Instance.new("StringValue", newModuleClone) cutsceneNameValue.Name = "CutsceneName" cutsceneNameValue.Value = CutsceneName local cutscenePointValue = Instance.new("NumberValue", newModuleClone) cutscenePointValue.Name = "CutscenePartCount" cutscenePointValue.Value = CutscenePoints local scriptSelect = {} table.insert(scriptSelect, newModuleClone) Selection:Set(scriptSelect) plugin:OpenScript(newModuleClone) local DoesStartAndEndValue = Instance.new("BoolValue", newModuleClone) DoesStartAndEndValue.Name = "DoesStartAndEndAtPlayer" DoesStartAndEndValue.Value = StartAndEndPlayerCheckbox:GetValue() elseif scriptTypeMultiChoice:GetSelectedIndex() == 2 then local noResetGui = Instance.new("ScreenGui", game.StarterGui) noResetGui.Name = "PartTouchCutscene" noResetGui.ResetOnSpawn = false local newScriptClone = script.PartTouchCutsceneScript:Clone() newScriptClone.Parent = noResetGui newScriptClone.Disabled = false local cutsceneTouchPart = script.CutscenePartTouch:Clone() local cutsceneNameValue = Instance.new("StringValue", newScriptClone) cutsceneNameValue.Name = "CutsceneName" cutsceneNameValue.Value = CutsceneName local cutscenePointValue = Instance.new("NumberValue", newScriptClone) cutscenePointValue.Name = "CutscenePartCount" cutscenePointValue.Value = CutscenePoints cutsceneTouchPart.Parent = workspace cutsceneTouchPart.Name = string.format("CutscenePartTouch_%s", CutsceneName) local moveToModel = Instance.new("Model", workspace) cutsceneTouchPart.Parent = moveToModel moveToModel:MoveTo(getCameraLookAt(10)) cutsceneTouchPart.Parent = workspace moveToModel:Destroy() local scriptSelect = {} table.insert(scriptSelect, newScriptClone) Selection:Set(scriptSelect) plugin:OpenScript(newScriptClone) local DoesStartAndEndValue = Instance.new("BoolValue", newScriptClone) DoesStartAndEndValue.Name = "DoesStartAndEndAtPlayer" DoesStartAndEndValue.Value = StartAndEndPlayerCheckbox:GetValue() elseif scriptTypeMultiChoice:GetSelectedIndex() == 3 then local playerAddedGui = Instance.new("ScreenGui", game.StarterGui) playerAddedGui.ResetOnSpawn = false playerAddedGui.Name = "PlayerAddedCutscene" local playerAddedScript = Instance.new("LocalScript", playerAddedGui) playerAddedScript.Name = "PlayerAddedCutsceneScript" playerAddedScript.Source = script.PlayerAddedCutsceneScript.Source local playerAddedValue = Instance.new("StringValue", playerAddedScript) playerAddedValue.Name = "CutsceneName" playerAddedValue.Value = CutsceneName local cutscenePointValue = Instance.new("NumberValue", playerAddedScript) cutscenePointValue.Name = "CutscenePartCount" cutscenePointValue.Value = CutscenePoints local guiSelect = {} table.insert(guiSelect, playerAddedGui) Selection:Set(guiSelect) plugin:OpenScript(playerAddedScript) local DoesStartAndEndValue = Instance.new("BoolValue", playerAddedScript) DoesStartAndEndValue.Name = "DoesStartAndEndAtPlayer" DoesStartAndEndValue.Value = StartAndEndPlayerCheckbox:GetValue() elseif scriptTypeMultiChoice:GetSelectedIndex() == 4 then local newScriptClone = script.PlayerDiedCutsceneScript:Clone() newScriptClone.Disabled = false newScriptClone.Parent = game.StarterPlayer.StarterCharacterScripts local cutsceneNameValue = Instance.new("StringValue", newScriptClone) cutsceneNameValue.Name = "CutsceneName" cutsceneNameValue.Value = CutsceneName local scriptSelect = {} table.insert(scriptSelect, newScriptClone) Selection:Set(scriptSelect) plugin:OpenScript(newScriptClone) local DoesStartAndEndValue = Instance.new("BoolValue", newScriptClone) DoesStartAndEndValue.Name = "DoesStartAndEndAtPlayer" DoesStartAndEndValue.Value = StartAndEndPlayerCheckbox:GetValue() local cutscenePointValue = Instance.new("NumberValue", newScriptClone) cutscenePointValue.Name = "CutscenePartCount" cutscenePointValue.Value = CutscenePoints end insertCutsceneTabOpen = false propertiesCutsceneInserting:GetSectionFrame():Destroy() BindToCloseFunc() end) ChangeHistoryService:SetWaypoint("Cutscene Script Added") local layout = Instance.new("UIListLayout", propertiesCutsceneInserting:GetContentsFrame()) layout.SortOrder = Enum.SortOrder.LayoutOrder layout.HorizontalAlignment = Enum.HorizontalAlignment.Center else insertCutsceneTabOpen = false propertiesCutsceneInserting:GetSectionFrame():Destroy() end end) plugin.Unloading:Connect(function() print("Shutting down Cutscenify...") if not cutsceneParts.Parent == script then for _, child in ipairs(EditModeParts:GetChildren()) do child.Transparency = 1 child.numberBillboard.Enabled = false end end cameraEffect:Destroy() pluginGui.Enabled = false end) workspace.ChildAdded:Connect(function(child) if child:IsA("Folder") and child.Name == "Cutscenify" and not Username == "Inventfvl" then warn("You have been locked out of Cutscenify.") pluginGui.Enabled = false pluginGui:Destroy() Button:SetActive(false) Button.Enabled = false ImporterButton.Enabled = false workspace.CutscenifyCore:Destroy() script:Destroy() end end) Mouse.Button1Down:Connect(function() if not Mouse.Target then return end if Mouse.Target.Parent == EditModeParts then isACutscenePointSelected = true print(Mouse.Target) selectedCutscenePoint = Mouse.Target local selectionBox = Instance.new("SelectionBox", game.CoreGui) selectionBox.Adornee = Mouse.Target selectionBox.Color = BrickColor.new("Dark green") selectionBox.LineThickness = 0.1 else if isACutscenePointSelected then isACutscenePointSelected = false selectedCutscenePoint:FindFirstChild("SelectionBox"):Destroy() selectedCutscenePoint = nil end end end) if not RunService:IsEdit() then repeat camera.CameraType = Enum.CameraType.Fixed wait(0.1) until camera.CameraType == Enum.CameraType.Fixed end print("Cutscenify has loaded successfully.") plugin:Activate(true)]]> StudioWidgets CollapsibleTitledSection {49460D29-7BC5-4771-9CBB-62D1169DC53E} CustomTextButton {24F9E167-72E9-4258-A6A6-BA884ED5AB3A} GuiUtilities {295173B3-5117-4B14-A021-11DD0B5A3CB0} ImageButtonWithText {0991BF60-BA2D-4279-8657-36FBE41E442B} LabeledCheckbox {6C85D90C-DFF5-44E4-9BE3-7F7A1EF35D5C} LabeledMultiChoice {6AA2895A-D42F-4E1D-944D-56BF7B576D22} #choices) then initChoiceIndex = #choices end local vsl = VerticallyScalingListFrame.new("MCC_" .. nameSuffix) vsl:AddBottomPadding() local titleLabel = GuiUtilities.MakeFrameWithSubSectionLabel("Title", labelText) vsl:AddChild(titleLabel) -- Container for cells. local cellFrame = self:_MakeRadioButtons(choices) vsl:AddChild(cellFrame) self._vsl = vsl self:SetSelectedIndex(initChoiceIndex) return self end function LabeledMultiChoiceClass:SetSelectedIndex(selectedIndex) self._selectedIndex = selectedIndex for i = 1, #self._buttonObjsByIndex do self._buttonObjsByIndex[i]:SetValue(i == selectedIndex) end if (self._valueChangedFunction) then self._valueChangedFunction(self._selectedIndex) end end function LabeledMultiChoiceClass:GetSelectedIndex() return self._selectedIndex end function LabeledMultiChoiceClass:SetValueChangedFunction(vcf) self._valueChangedFunction = vcf end function LabeledMultiChoiceClass:GetFrame() return self._vsl:GetFrame() end -- Small checkboxes are a different entity. -- All the bits are smaller. -- Fixed width instead of flood-fill. -- Box comes first, then label. function LabeledMultiChoiceClass:_MakeRadioButtons(choices) local frame = GuiUtilities.MakeFrame("RadioButtons") frame.BackgroundTransparency = 1 local padding = Instance.new("UIPadding") padding.PaddingLeft = UDim.new(0, GuiUtilities.StandardLineLabelLeftMargin) padding.PaddingRight = UDim.new(0, GuiUtilities.StandardLineLabelLeftMargin) padding.Parent = frame -- Make a grid to put checkboxes in. local uiGridLayout = Instance.new("UIGridLayout") uiGridLayout.CellSize = LabeledCheckbox.kMinFrameSize uiGridLayout.CellPadding = UDim2.new(0, kRadioButtonsHPadding, 0, GuiUtilities.kStandardVMargin) uiGridLayout.HorizontalAlignment = Enum.HorizontalAlignment.Left uiGridLayout.VerticalAlignment = Enum.VerticalAlignment.Top uiGridLayout.Parent = frame uiGridLayout.SortOrder = Enum.SortOrder.LayoutOrder for i, choiceData in ipairs(choices) do self:_AddRadioButton(frame, i, choiceData) end -- Sync size with content size. GuiUtilities.AdjustHeightDynamicallyToLayout(frame, uiGridLayout) return frame end function LabeledMultiChoiceClass:_AddRadioButton(parentFrame, index, choiceData) local radioButtonObj = LabeledRadioButton.new(choiceData.Id, choiceData.Text) self._buttonObjsByIndex[index] = radioButtonObj radioButtonObj:SetValueChangedFunction(function(value) -- If we notice the button going from off to on, and it disagrees with -- our current notion of selection, update selection. if (value and self._selectedIndex ~= index) then self:SetSelectedIndex(index) end end) radioButtonObj:GetFrame().LayoutOrder = index radioButtonObj:GetFrame().Parent = parentFrame end return LabeledMultiChoiceClass]]> LabeledRadioButton {236EBD18-B368-4F89-97D7-88A41A517F80} LabeledSlider {9C49C73E-653E-47CA-90F5-9F9311D0CAAF} LabeledTextInput {9E448F4A-E256-4288-9447-2C8EBF2E9E49} am I rendering?". -- We are using the self._MaxGraphemes = 10 self._valueChangedFunction = nil local defaultValue = defaultValue or "" local frame = GuiUtilities.MakeStandardFixedHeightFrame('TextInput ' .. nameSuffix) self._frame = frame local label = GuiUtilities.MakeStandardPropertyLabel(labelText) label.Parent = frame self._label = label self._value = defaultValue -- Dumb hack to add padding to text box, local textBoxWrapperFrame = Instance.new("Frame") textBoxWrapperFrame.Name = "Wrapper" textBoxWrapperFrame.Size = UDim2.new(0, kTextInputWidth, 0.6, 0) textBoxWrapperFrame.Position = UDim2.new(0, GuiUtilities.StandardLineElementLeftMargin, .5, 0) textBoxWrapperFrame.AnchorPoint = Vector2.new(0, .5) textBoxWrapperFrame.Parent = frame GuiUtilities.syncGuiElementInputFieldColor(textBoxWrapperFrame) GuiUtilities.syncGuiElementBorderColor(textBoxWrapperFrame) local textBox = Instance.new("TextBox") textBox.Parent = textBoxWrapperFrame textBox.Name = "TextBox" textBox.Text = defaultValue textBox.Font = Enum.Font.SourceSans textBox.TextSize = 15 textBox.BackgroundTransparency = 1 textBox.TextXAlignment = Enum.TextXAlignment.Left textBox.Size = UDim2.new(1, -kTextBoxInternalPadding, 1, GuiUtilities.kTextVerticalFudge) textBox.Position = UDim2.new(0, kTextBoxInternalPadding, 0, 0) textBox.ClipsDescendants = true GuiUtilities.syncGuiElementFontColor(textBox) textBox:GetPropertyChangedSignal("Text"):connect(function() -- Never let the text be too long. -- Careful here: we want to measure number of graphemes, not characters, -- in the text, and we want to clamp on graphemes as well. if (utf8.len(self._textBox.Text) > self._MaxGraphemes) then local count = 0 for start, stop in utf8.graphemes(self._textBox.Text) do count = count + 1 if (count > self._MaxGraphemes) then -- We have gone one too far. -- clamp just before the beginning of this grapheme. self._textBox.Text = string.sub(self._textBox.Text, 1, start-1) break end end -- Don't continue with rest of function: the resetting of "Text" field -- above will trigger re-entry. We don't need to trigger value -- changed function twice. return end self._value = self._textBox.Text if (self._valueChangedFunction) then self._valueChangedFunction(self._value) end end) self._textBox = textBox return self end function LabeledTextInputClass:SetValueChangedFunction(vcf) self._valueChangedFunction = vcf end function LabeledTextInputClass:GetFrame() return self._frame end function LabeledTextInputClass:GetValue() return self._value end function LabeledTextInputClass:GetMaxGraphemes() return self._MaxGraphemes end function LabeledTextInputClass:SetMaxGraphemes(newValue) self._MaxGraphemes = newValue end function LabeledTextInputClass:SetValue(newValue) if self._value ~= newValue then self._textBox.Text = newValue end end return LabeledTextInputClass ]]> RbxGui {E65E4305-CBC9-413F-ACCA-3923FC1567DD} = 3 then local spacing = .1 / numButtons local buttonSize = .9 / numButtons buttonNum = 1 while buttonNum <= numButtons do buttonObjs[buttonNum].Position = UDim2.new(spacing*buttonNum + (buttonNum-1) * buttonSize, 0, yPos.Scale, yPos.Offset) buttonObjs[buttonNum].Size = UDim2.new(buttonSize, 0, ySize.Scale, ySize.Offset) buttonNum = buttonNum + 1 end end end local function setSliderPos(newAbsPosX,slider,sliderPosition,bar,steps) local newStep = steps - 1 --otherwise we really get one more step than we want local relativePosX = math.min(1, math.max(0, (newAbsPosX - bar.AbsolutePosition.X) / bar.AbsoluteSize.X )) local wholeNum, remainder = math.modf(relativePosX * newStep) if remainder > 0.5 then wholeNum = wholeNum + 1 end relativePosX = wholeNum/newStep local result = math.ceil(relativePosX * newStep) if sliderPosition.Value ~= (result + 1) then --only update if we moved a step sliderPosition.Value = result + 1 slider.Position = UDim2.new(relativePosX,-slider.AbsoluteSize.X/2,slider.Position.Y.Scale,slider.Position.Y.Offset) end end local function cancelSlide(areaSoak) areaSoak.Visible = false end t.CreateStyledMessageDialog = function(title, message, style, buttons) local frame = Instance.new("Frame") frame.Size = UDim2.new(0.5, 0, 0, 165) frame.Position = UDim2.new(0.25, 0, 0.5, -72.5) frame.Name = "MessageDialog" frame.Active = true frame.Style = Enum.FrameStyle.RobloxRound local styleImage = Instance.new("ImageLabel") styleImage.Name = "StyleImage" styleImage.BackgroundTransparency = 1 styleImage.Position = UDim2.new(0,5,0,15) if style == "error" or style == "Error" then styleImage.Size = UDim2.new(0, 71, 0, 71) styleImage.Image = "https://www.roblox.com/asset/?id=42565285" elseif style == "notify" or style == "Notify" then styleImage.Size = UDim2.new(0, 71, 0, 71) styleImage.Image = "https://www.roblox.com/asset/?id=42604978" elseif style == "confirm" or style == "Confirm" then styleImage.Size = UDim2.new(0, 74, 0, 76) styleImage.Image = "https://www.roblox.com/asset/?id=42557901" else return t.CreateMessageDialog(title,message,buttons) end styleImage.Parent = frame local titleLabel = Instance.new("TextLabel") titleLabel.Name = "Title" titleLabel.Text = title titleLabel.TextStrokeTransparency = 0 titleLabel.BackgroundTransparency = 1 titleLabel.TextColor3 = Color3.new(221/255,221/255,221/255) titleLabel.Position = UDim2.new(0, 80, 0, 0) titleLabel.Size = UDim2.new(1, -80, 0, 40) titleLabel.Font = Enum.Font.ArialBold titleLabel.FontSize = Enum.FontSize.Size36 titleLabel.TextXAlignment = Enum.TextXAlignment.Center titleLabel.TextYAlignment = Enum.TextYAlignment.Center titleLabel.Parent = frame local messageLabel = Instance.new("TextLabel") messageLabel.Name = "Message" messageLabel.Text = message messageLabel.TextStrokeTransparency = 0 messageLabel.TextColor3 = Color3.new(221/255,221/255,221/255) messageLabel.Position = UDim2.new(0.025, 80, 0, 45) messageLabel.Size = UDim2.new(0.95, -80, 0, 55) messageLabel.BackgroundTransparency = 1 messageLabel.Font = Enum.Font.Arial messageLabel.FontSize = Enum.FontSize.Size18 messageLabel.TextWrap = true messageLabel.TextXAlignment = Enum.TextXAlignment.Left messageLabel.TextYAlignment = Enum.TextYAlignment.Top messageLabel.Parent = frame CreateButtons(frame, buttons, UDim.new(0, 105), UDim.new(0, 40) ) return frame end t.CreateMessageDialog = function(title, message, buttons) local frame = Instance.new("Frame") frame.Size = UDim2.new(0.5, 0, 0.5, 0) frame.Position = UDim2.new(0.25, 0, 0.25, 0) frame.Name = "MessageDialog" frame.Active = true frame.Style = Enum.FrameStyle.RobloxRound local titleLabel = Instance.new("TextLabel") titleLabel.Name = "Title" titleLabel.Text = title titleLabel.BackgroundTransparency = 1 titleLabel.TextColor3 = Color3.new(221/255,221/255,221/255) titleLabel.Position = UDim2.new(0, 0, 0, 0) titleLabel.Size = UDim2.new(1, 0, 0.15, 0) titleLabel.Font = Enum.Font.ArialBold titleLabel.FontSize = Enum.FontSize.Size36 titleLabel.TextXAlignment = Enum.TextXAlignment.Center titleLabel.TextYAlignment = Enum.TextYAlignment.Center titleLabel.Parent = frame local messageLabel = Instance.new("TextLabel") messageLabel.Name = "Message" messageLabel.Text = message messageLabel.TextColor3 = Color3.new(221/255,221/255,221/255) messageLabel.Position = UDim2.new(0.025, 0, 0.175, 0) messageLabel.Size = UDim2.new(0.95, 0, .55, 0) messageLabel.BackgroundTransparency = 1 messageLabel.Font = Enum.Font.Arial messageLabel.FontSize = Enum.FontSize.Size18 messageLabel.TextWrap = true messageLabel.TextXAlignment = Enum.TextXAlignment.Left messageLabel.TextYAlignment = Enum.TextYAlignment.Top messageLabel.Parent = frame CreateButtons(frame, buttons, UDim.new(0.8,0), UDim.new(0.15, 0)) return frame end -- written by jmargh -- to be used for the new settings menu t.CreateScrollingDropDownMenu = function(onSelectedCallback, size, position, baseZ) local maxVisibleList = 6 local baseZIndex = 0 if type(baseZ) == 'number' then baseZIndex = baseZ end local dropDownMenu = {} local currentList = nil local updateFunc = nil local frame = Instance.new('Frame') frame.Name = "DropDownMenuFrame" frame.Size = size frame.Position = position frame.BackgroundTransparency = 1 dropDownMenu.Frame = frame local currentSelectionName = Instance.new('TextButton') currentSelectionName.Name = "CurrentSelectionName" currentSelectionName.Size = UDim2.new(1, 0, 1, 0) currentSelectionName.BackgroundTransparency = 1 currentSelectionName.Font = Enum.Font.SourceSansBold currentSelectionName.FontSize = Enum.FontSize.Size18 currentSelectionName.TextXAlignment = Enum.TextXAlignment.Left currentSelectionName.TextYAlignment = Enum.TextYAlignment.Center currentSelectionName.TextColor3 = Color3.new(0.5, 0.5, 0.5) currentSelectionName.TextWrap = true currentSelectionName.ZIndex = baseZIndex currentSelectionName.Style = Enum.ButtonStyle.RobloxRoundDropdownButton currentSelectionName.Text = "Choose One" currentSelectionName.Parent = frame dropDownMenu.CurrentSelectionButton = currentSelectionName local icon = Instance.new('ImageLabel') icon.Name = "DropDownIcon" icon.Size = UDim2.new(0, 16, 0, 12) icon.Position = UDim2.new(1, -17, 0.5, -6) icon.Image = 'rbxasset://textures/ui/dropdown_arrow.png' icon.BackgroundTransparency = 1 icon.ZIndex = baseZIndex icon.Parent = currentSelectionName local listMenu = nil local scrollingBackground = nil local visibleCount = 0 local isOpen = false local function onEntrySelected() icon.Rotation = 0 scrollingBackground:TweenSize(UDim2.new(1, 0, 0, currentSelectionName.AbsoluteSize.y), Enum.EasingDirection.InOut, Enum.EasingStyle.Sine, 0.15, true) -- listMenu.ScrollBarThickness = 0 listMenu:TweenSize(UDim2.new(1, -16, 0, 24), Enum.EasingDirection.InOut, Enum.EasingStyle.Sine, 0.15, true, function() if not isOpen then listMenu.Visible = false scrollingBackground.Visible = false end end) isOpen = false end currentSelectionName.MouseButton1Click:connect(function() if not currentSelectionName.Active or #currentList == 0 then return end if isOpen then onEntrySelected() return end -- isOpen = true icon.Rotation = 180 if listMenu then listMenu.Visible = true end if scrollingBackground then scrollingBackground.Visible = true end -- if scrollingBackground then scrollingBackground:TweenSize(UDim2.new(1, 0, 0, visibleCount * 24 + 8), Enum.EasingDirection.InOut, Enum.EasingStyle.Sine, 0.15, true) end if listMenu then listMenu:TweenSize(UDim2.new(1, -16, 0, visibleCount * 24), Enum.EasingDirection.InOut, Enum.EasingStyle.Sine, 0.15, true, function() listMenu.ScrollBarThickness = 6 end) end end) --[[ Public API ]]-- dropDownMenu.IsOpen = function() return isOpen end dropDownMenu.Close = function() onEntrySelected() end dropDownMenu.Reset = function() isOpen = false icon.Rotation = 0 listMenu.ScrollBarThickness = 0 listMenu.Size = UDim2.new(1, -16, 0, 24) listMenu.Visible = false scrollingBackground.Visible = false end dropDownMenu.SetVisible = function(isVisible) if frame then frame.Visible = isVisible end end dropDownMenu.UpdateZIndex = function(newZIndexBase) currentSelectionName.ZIndex = newZIndexBase icon.ZIndex = newZIndexBase if scrollingBackground then scrollingBackground.ZIndex = newZIndexBase + 1 end if listMenu then listMenu.ZIndex = newZIndexBase + 2 for _,child in pairs(listMenu:GetChildren()) do child.ZIndex = newZIndexBase + 4 end end end dropDownMenu.SetActive = function(isActive) currentSelectionName.Active = isActive end dropDownMenu.SetSelectionText = function(text) currentSelectionName.Text = text end dropDownMenu.CreateList = function(list) currentSelectionName.Text = "Choose One" if listMenu then listMenu:Destroy() end if scrollingBackground then scrollingBackground:Destroy() end -- currentList = list local length = #list visibleCount = math.min(maxVisibleList, length) local listMenuOffset = visibleCount * 24 listMenu = Instance.new('ScrollingFrame') listMenu.Name = "ListMenu" listMenu.Size = UDim2.new(1, -16, 0, 24) listMenu.Position = UDim2.new(0, 12, 0, 32) listMenu.CanvasSize = UDim2.new(0, 0, 0, length * 24) listMenu.BackgroundTransparency = 1 listMenu.BorderSizePixel = 0 listMenu.ZIndex = baseZIndex + 2 listMenu.Visible = false listMenu.Active = true listMenu.BottomImage = 'rbxasset://textures/ui/scroll-bottom.png' listMenu.MidImage = 'rbxasset://textures/ui/scroll-middle.png' listMenu.TopImage = 'rbxasset://textures/ui/scroll-top.png' listMenu.ScrollBarThickness = 0 listMenu.Parent = frame scrollingBackground = Instance.new('TextButton') scrollingBackground.Name = "ScrollingBackground" scrollingBackground.Size = UDim2.new(1, 0, 0, currentSelectionName.AbsoluteSize.y) scrollingBackground.Position = UDim2.new(0, 0, 0, 28) scrollingBackground.BackgroundColor3 = Color3.new(1, 1, 1) scrollingBackground.Style = Enum.ButtonStyle.RobloxRoundDropdownButton scrollingBackground.ZIndex = baseZIndex + 1 scrollingBackground.Text = "" scrollingBackground.Visible = false scrollingBackground.AutoButtonColor = false scrollingBackground.Parent = frame for i = 1, length do local entry = list[i] local btn = Instance.new('TextButton') btn.Name = entry btn.Size = UDim2.new(1, 0, 0, 24) btn.Position = UDim2.new(0, 0, 0, (i - 1) * 24) btn.BackgroundTransparency = 0 btn.BackgroundColor3 = Color3.new(1, 1, 1) btn.BorderSizePixel = 0 btn.Font = Enum.Font.SourceSans btn.FontSize = Enum.FontSize.Size18 btn.TextColor3 = Color3.new(0.5, 0.5, 0.5) btn.TextXAlignment = Enum.TextXAlignment.Left btn.TextYAlignment = Enum.TextYAlignment.Center btn.Text = entry btn.ZIndex = baseZIndex + 4 btn.AutoButtonColor = false btn.Parent = listMenu btn.MouseButton1Click:connect(function() currentSelectionName.Text = btn.Text onEntrySelected() btn.Font = Enum.Font.SourceSans btn.TextColor3 = Color3.new(0.5, 0.5, 0.5) btn.BackgroundColor3 = Color3.new(1, 1, 1) onSelectedCallback(btn.Text) end) btn.MouseEnter:connect(function() btn.TextColor3 = Color3.new(1, 1, 1) btn.BackgroundColor3 = Color3.new(0.75, 0.75, 0.75) end) btn.MouseLeave:connect(function() btn.TextColor3 = Color3.new(0.5, 0.5, 0.5) btn.BackgroundColor3 = Color3.new(1, 1, 1) end) end end return dropDownMenu end t.CreateDropDownMenu = function(items, onSelect, forRoblox, whiteSkin, baseZ) local baseZIndex = 0 if (type(baseZ) == "number") then baseZIndex = baseZ end local width = UDim.new(0, 100) local height = UDim.new(0, 32) local xPos = 0.055 local frame = Instance.new("Frame") local textColor = Color3.new(1,1,1) if (whiteSkin) then textColor = Color3.new(0.5, 0.5, 0.5) end frame.Name = "DropDownMenu" frame.BackgroundTransparency = 1 frame.Size = UDim2.new(width, height) local dropDownMenu = Instance.new("TextButton") dropDownMenu.Name = "DropDownMenuButton" dropDownMenu.TextWrap = true dropDownMenu.TextColor3 = textColor dropDownMenu.Text = "Choose One" dropDownMenu.Font = Enum.Font.ArialBold dropDownMenu.FontSize = Enum.FontSize.Size18 dropDownMenu.TextXAlignment = Enum.TextXAlignment.Left dropDownMenu.TextYAlignment = Enum.TextYAlignment.Center dropDownMenu.BackgroundTransparency = 1 dropDownMenu.AutoButtonColor = true if (whiteSkin) then dropDownMenu.Style = Enum.ButtonStyle.RobloxRoundDropdownButton else dropDownMenu.Style = Enum.ButtonStyle.RobloxButton end dropDownMenu.Size = UDim2.new(1,0,1,0) dropDownMenu.Parent = frame dropDownMenu.ZIndex = 2 + baseZIndex local dropDownIcon = Instance.new("ImageLabel") dropDownIcon.Name = "Icon" dropDownIcon.Active = false if (whiteSkin) then dropDownIcon.Image = "rbxasset://textures/ui/dropdown_arrow.png" dropDownIcon.Size = UDim2.new(0,16,0,12) dropDownIcon.Position = UDim2.new(1,-17,0.5, -6) else dropDownIcon.Image = "https://www.roblox.com/asset/?id=45732894" dropDownIcon.Size = UDim2.new(0,11,0,6) dropDownIcon.Position = UDim2.new(1,-11,0.5, -2) end dropDownIcon.BackgroundTransparency = 1 dropDownIcon.Parent = dropDownMenu dropDownIcon.ZIndex = 2 + baseZIndex local itemCount = #items local dropDownItemCount = #items local useScrollButtons = false if dropDownItemCount > 6 then useScrollButtons = true dropDownItemCount = 6 end local droppedDownMenu = Instance.new("TextButton") droppedDownMenu.Name = "List" droppedDownMenu.Text = "" droppedDownMenu.BackgroundTransparency = 1 --droppedDownMenu.AutoButtonColor = true if (whiteSkin) then droppedDownMenu.Style = Enum.ButtonStyle.RobloxRoundDropdownButton else droppedDownMenu.Style = Enum.ButtonStyle.RobloxButton end droppedDownMenu.Visible = false droppedDownMenu.Active = true --Blocks clicks droppedDownMenu.Position = UDim2.new(0,0,0,0) droppedDownMenu.Size = UDim2.new(1,0, (1 + dropDownItemCount)*.8, 0) droppedDownMenu.Parent = frame droppedDownMenu.ZIndex = 2 + baseZIndex local choiceButton = Instance.new("TextButton") choiceButton.Name = "ChoiceButton" choiceButton.BackgroundTransparency = 1 choiceButton.BorderSizePixel = 0 choiceButton.Text = "ReplaceMe" choiceButton.TextColor3 = textColor choiceButton.TextXAlignment = Enum.TextXAlignment.Left choiceButton.TextYAlignment = Enum.TextYAlignment.Center choiceButton.BackgroundColor3 = Color3.new(1, 1, 1) choiceButton.Font = Enum.Font.Arial choiceButton.FontSize = Enum.FontSize.Size18 if useScrollButtons then choiceButton.Size = UDim2.new(1,-13, .8/((dropDownItemCount + 1)*.8),0) else choiceButton.Size = UDim2.new(1, 0, .8/((dropDownItemCount + 1)*.8),0) end choiceButton.TextWrap = true choiceButton.ZIndex = 2 + baseZIndex local areaSoak = Instance.new("TextButton") areaSoak.Name = "AreaSoak" areaSoak.Text = "" areaSoak.BackgroundTransparency = 1 areaSoak.Active = true areaSoak.Size = UDim2.new(1,0,1,0) areaSoak.Visible = false areaSoak.ZIndex = 3 + baseZIndex local dropDownSelected = false local scrollUpButton local scrollDownButton local scrollMouseCount = 0 local setZIndex = function(baseZIndex) droppedDownMenu.ZIndex = baseZIndex +1 if scrollUpButton then scrollUpButton.ZIndex = baseZIndex + 3 end if scrollDownButton then scrollDownButton.ZIndex = baseZIndex + 3 end local children = droppedDownMenu:GetChildren() if children then for i, child in ipairs(children) do if child.Name == "ChoiceButton" then child.ZIndex = baseZIndex + 2 elseif child.Name == "ClickCaptureButton" then child.ZIndex = baseZIndex end end end end local scrollBarPosition = 1 local updateScroll = function() if scrollUpButton then scrollUpButton.Active = scrollBarPosition > 1 end if scrollDownButton then scrollDownButton.Active = scrollBarPosition + dropDownItemCount <= itemCount end local children = droppedDownMenu:GetChildren() if not children then return end local childNum = 1 for i, obj in ipairs(children) do if obj.Name == "ChoiceButton" then if childNum < scrollBarPosition or childNum >= scrollBarPosition + dropDownItemCount then obj.Visible = false else obj.Position = UDim2.new(0,0,((childNum-scrollBarPosition+1)*.8)/((dropDownItemCount+1)*.8),0) obj.Visible = true end obj.TextColor3 = textColor obj.BackgroundTransparency = 1 childNum = childNum + 1 end end end local toggleVisibility = function() dropDownSelected = not dropDownSelected areaSoak.Visible = not areaSoak.Visible dropDownMenu.Visible = not dropDownSelected droppedDownMenu.Visible = dropDownSelected if dropDownSelected then setZIndex(4 + baseZIndex) else setZIndex(2 + baseZIndex) end if useScrollButtons then updateScroll() end end droppedDownMenu.MouseButton1Click:connect(toggleVisibility) local updateSelection = function(text) local foundItem = false local children = droppedDownMenu:GetChildren() local childNum = 1 if children then for i, obj in ipairs(children) do if obj.Name == "ChoiceButton" then if obj.Text == text then obj.Font = Enum.Font.ArialBold foundItem = true scrollBarPosition = childNum if (whiteSkin) then obj.TextColor3 = Color3.new(90/255,142/255,233/255) end else obj.Font = Enum.Font.Arial if (whiteSkin) then obj.TextColor3 = textColor end end childNum = childNum + 1 end end end if not text then dropDownMenu.Text = "Choose One" scrollBarPosition = 1 else if not foundItem then error("Invalid Selection Update -- " .. text) end if scrollBarPosition + dropDownItemCount > itemCount + 1 then scrollBarPosition = itemCount - dropDownItemCount + 1 end dropDownMenu.Text = text end end local function scrollDown() if scrollBarPosition + dropDownItemCount <= itemCount then scrollBarPosition = scrollBarPosition + 1 updateScroll() return true end return false end local function scrollUp() if scrollBarPosition > 1 then scrollBarPosition = scrollBarPosition - 1 updateScroll() return true end return false end if useScrollButtons then --Make some scroll buttons scrollUpButton = Instance.new("ImageButton") scrollUpButton.Name = "ScrollUpButton" scrollUpButton.BackgroundTransparency = 1 scrollUpButton.Image = "rbxasset://textures/ui/scrollbuttonUp.png" scrollUpButton.Size = UDim2.new(0,17,0,17) scrollUpButton.Position = UDim2.new(1,-11,(1*.8)/((dropDownItemCount+1)*.8),0) scrollUpButton.MouseButton1Click:connect( function() scrollMouseCount = scrollMouseCount + 1 end) scrollUpButton.MouseLeave:connect( function() scrollMouseCount = scrollMouseCount + 1 end) scrollUpButton.MouseButton1Down:connect( function() scrollMouseCount = scrollMouseCount + 1 scrollUp() local val = scrollMouseCount wait(0.5) while val == scrollMouseCount do if scrollUp() == false then break end wait(0.1) end end) scrollUpButton.Parent = droppedDownMenu scrollDownButton = Instance.new("ImageButton") scrollDownButton.Name = "ScrollDownButton" scrollDownButton.BackgroundTransparency = 1 scrollDownButton.Image = "rbxasset://textures/ui/scrollbuttonDown.png" scrollDownButton.Size = UDim2.new(0,17,0,17) scrollDownButton.Position = UDim2.new(1,-11,1,-11) scrollDownButton.Parent = droppedDownMenu scrollDownButton.MouseButton1Click:connect( function() scrollMouseCount = scrollMouseCount + 1 end) scrollDownButton.MouseLeave:connect( function() scrollMouseCount = scrollMouseCount + 1 end) scrollDownButton.MouseButton1Down:connect( function() scrollMouseCount = scrollMouseCount + 1 scrollDown() local val = scrollMouseCount wait(0.5) while val == scrollMouseCount do if scrollDown() == false then break end wait(0.1) end end) local scrollbar = Instance.new("ImageLabel") scrollbar.Name = "ScrollBar" scrollbar.Image = "rbxasset://textures/ui/scrollbar.png" scrollbar.BackgroundTransparency = 1 scrollbar.Size = UDim2.new(0, 18, (dropDownItemCount*.8)/((dropDownItemCount+1)*.8), -(17) - 11 - 4) scrollbar.Position = UDim2.new(1,-11,(1*.8)/((dropDownItemCount+1)*.8),17+2) scrollbar.Parent = droppedDownMenu end for i,item in ipairs(items) do -- needed to maintain local scope for items in event listeners below local button = choiceButton:clone() if forRoblox then button.RobloxLocked = true end button.Text = item button.Parent = droppedDownMenu if (whiteSkin) then button.TextColor3 = textColor end button.MouseButton1Click:connect(function() --Remove Highlight if (not whiteSkin) then button.TextColor3 = Color3.new(1,1,1) end button.BackgroundTransparency = 1 updateSelection(item) onSelect(item) toggleVisibility() end) button.MouseEnter:connect(function() --Add Highlight if (not whiteSkin) then button.TextColor3 = Color3.new(0,0,0) end button.BackgroundTransparency = 0 end) button.MouseLeave:connect(function() --Remove Highlight if (not whiteSkin) then button.TextColor3 = Color3.new(1,1,1) end button.BackgroundTransparency = 1 end) end --This does the initial layout of the buttons updateScroll() frame.AncestryChanged:connect(function(child,parent) if parent == nil then areaSoak.Parent = nil else areaSoak.Parent = getLayerCollectorAncestor(frame) end end) dropDownMenu.MouseButton1Click:connect(toggleVisibility) areaSoak.MouseButton1Click:connect(toggleVisibility) return frame, updateSelection end t.CreatePropertyDropDownMenu = function(instance, property, enum) local items = enum:GetEnumItems() local names = {} local nameToItem = {} for i,obj in ipairs(items) do names[i] = obj.Name nameToItem[obj.Name] = obj end local frame local updateSelection frame, updateSelection = t.CreateDropDownMenu(names, function(text) instance[property] = nameToItem[text] end) ScopedConnect(frame, instance, "Changed", function(prop) if prop == property then updateSelection(instance[property].Name) end end, function() updateSelection(instance[property].Name) end) return frame end t.GetFontHeight = function(font, fontSize) if font == nil or fontSize == nil then error("Font and FontSize must be non-nil") end local fontSizeInt = tonumber(fontSize.Name:match("%d+")) -- Clever hack to extract the size from the enum itself. if font == Enum.Font.Legacy then -- Legacy has a 50% bigger size. return math.ceil(fontSizeInt*1.5) else -- Size is literally just the fontSizeInt return fontSizeInt end end local function layoutGuiObjectsHelper(frame, guiObjects, settingsTable) local totalPixels = frame.AbsoluteSize.Y local pixelsRemaining = frame.AbsoluteSize.Y for i, child in ipairs(guiObjects) do if child:IsA("TextLabel") or child:IsA("TextButton") then local isLabel = child:IsA("TextLabel") if isLabel then pixelsRemaining = pixelsRemaining - settingsTable["TextLabelPositionPadY"] else pixelsRemaining = pixelsRemaining - settingsTable["TextButtonPositionPadY"] end child.Position = UDim2.new(child.Position.X.Scale, child.Position.X.Offset, 0, totalPixels - pixelsRemaining) child.Size = UDim2.new(child.Size.X.Scale, child.Size.X.Offset, 0, pixelsRemaining) if child.TextFits and child.TextBounds.Y < pixelsRemaining then child.Visible = true if isLabel then child.Size = UDim2.new(child.Size.X.Scale, child.Size.X.Offset, 0, child.TextBounds.Y + settingsTable["TextLabelSizePadY"]) else child.Size = UDim2.new(child.Size.X.Scale, child.Size.X.Offset, 0, child.TextBounds.Y + settingsTable["TextButtonSizePadY"]) end while not child.TextFits do child.Size = UDim2.new(child.Size.X.Scale, child.Size.X.Offset, 0, child.AbsoluteSize.Y + 1) end pixelsRemaining = pixelsRemaining - child.AbsoluteSize.Y if isLabel then pixelsRemaining = pixelsRemaining - settingsTable["TextLabelPositionPadY"] else pixelsRemaining = pixelsRemaining - settingsTable["TextButtonPositionPadY"] end else child.Visible = false pixelsRemaining = -1 end else --GuiObject child.Position = UDim2.new(child.Position.X.Scale, child.Position.X.Offset, 0, totalPixels - pixelsRemaining) pixelsRemaining = pixelsRemaining - child.AbsoluteSize.Y child.Visible = (pixelsRemaining >= 0) end end end t.LayoutGuiObjects = function(frame, guiObjects, settingsTable) if not frame:IsA("GuiObject") then error("Frame must be a GuiObject") end for i, child in ipairs(guiObjects) do if not child:IsA("GuiObject") then error("All elements that are layed out must be of type GuiObject") end end if not settingsTable then settingsTable = {} end if not settingsTable["TextLabelSizePadY"] then settingsTable["TextLabelSizePadY"] = 0 end if not settingsTable["TextLabelPositionPadY"] then settingsTable["TextLabelPositionPadY"] = 0 end if not settingsTable["TextButtonSizePadY"] then settingsTable["TextButtonSizePadY"] = 12 end if not settingsTable["TextButtonPositionPadY"] then settingsTable["TextButtonPositionPadY"] = 2 end --Wrapper frame takes care of styled objects local wrapperFrame = Instance.new("Frame") wrapperFrame.Name = "WrapperFrame" wrapperFrame.BackgroundTransparency = 1 wrapperFrame.Size = UDim2.new(1,0,1,0) wrapperFrame.Parent = frame for i, child in ipairs(guiObjects) do child.Parent = wrapperFrame end local recalculate = function() wait() layoutGuiObjectsHelper(wrapperFrame, guiObjects, settingsTable) end frame.Changed:connect( function(prop) if prop == "AbsoluteSize" then --Wait a heartbeat for it to sync in recalculate(nil) end end) frame.AncestryChanged:connect(recalculate) layoutGuiObjectsHelper(wrapperFrame, guiObjects, settingsTable) end t.CreateSlider = function(steps,width,position) local sliderGui = Instance.new("Frame") sliderGui.Size = UDim2.new(1,0,1,0) sliderGui.BackgroundTransparency = 1 sliderGui.Name = "SliderGui" local sliderSteps = Instance.new("IntValue") sliderSteps.Name = "SliderSteps" sliderSteps.Value = steps sliderSteps.Parent = sliderGui local areaSoak = Instance.new("TextButton") areaSoak.Name = "AreaSoak" areaSoak.Text = "" areaSoak.BackgroundTransparency = 1 areaSoak.Active = false areaSoak.Size = UDim2.new(1,0,1,0) areaSoak.Visible = false areaSoak.ZIndex = 4 sliderGui.AncestryChanged:connect(function(child,parent) if parent == nil then areaSoak.Parent = nil else areaSoak.Parent = getLayerCollectorAncestor(sliderGui) end end) local sliderPosition = Instance.new("IntValue") sliderPosition.Name = "SliderPosition" sliderPosition.Value = 0 sliderPosition.Parent = sliderGui local id = math.random(1,100) local bar = Instance.new("TextButton") bar.Text = "" bar.AutoButtonColor = false bar.Name = "Bar" bar.BackgroundColor3 = Color3.new(0,0,0) if type(width) == "number" then bar.Size = UDim2.new(0,width,0,5) else bar.Size = UDim2.new(0,200,0,5) end bar.BorderColor3 = Color3.new(95/255,95/255,95/255) bar.ZIndex = 2 bar.Parent = sliderGui if position["X"] and position["X"]["Scale"] and position["X"]["Offset"] and position["Y"] and position["Y"]["Scale"] and position["Y"]["Offset"] then bar.Position = position end local slider = Instance.new("ImageButton") slider.Name = "Slider" slider.BackgroundTransparency = 1 slider.Image = "rbxasset://textures/ui/Slider.png" slider.Position = UDim2.new(0,0,0.5,-10) slider.Size = UDim2.new(0,20,0,20) slider.ZIndex = 3 slider.Parent = bar local areaSoakMouseMoveCon = nil areaSoak.MouseLeave:connect(function() if areaSoak.Visible then cancelSlide(areaSoak) end end) areaSoak.MouseButton1Up:connect(function() if areaSoak.Visible then cancelSlide(areaSoak) end end) slider.MouseButton1Down:connect(function() areaSoak.Visible = true if areaSoakMouseMoveCon then areaSoakMouseMoveCon:disconnect() end areaSoakMouseMoveCon = areaSoak.MouseMoved:connect(function(x,y) setSliderPos(x,slider,sliderPosition,bar,steps) end) end) slider.MouseButton1Up:connect(function() cancelSlide(areaSoak) end) sliderPosition.Changed:connect(function(prop) sliderPosition.Value = math.min(steps, math.max(1,sliderPosition.Value)) local relativePosX = (sliderPosition.Value - 1) / (steps - 1) slider.Position = UDim2.new(relativePosX,-slider.AbsoluteSize.X/2,slider.Position.Y.Scale,slider.Position.Y.Offset) end) bar.MouseButton1Down:connect(function(x,y) setSliderPos(x,slider,sliderPosition,bar,steps) end) return sliderGui, sliderPosition, sliderSteps end t.CreateSliderNew = function(steps,width,position) local sliderGui = Instance.new("Frame") sliderGui.Size = UDim2.new(1,0,1,0) sliderGui.BackgroundTransparency = 1 sliderGui.Name = "SliderGui" local sliderSteps = Instance.new("IntValue") sliderSteps.Name = "SliderSteps" sliderSteps.Value = steps sliderSteps.Parent = sliderGui local areaSoak = Instance.new("TextButton") areaSoak.Name = "AreaSoak" areaSoak.Text = "" areaSoak.BackgroundTransparency = 1 areaSoak.Active = false areaSoak.Size = UDim2.new(1,0,1,0) areaSoak.Visible = false areaSoak.ZIndex = 6 sliderGui.AncestryChanged:connect(function(child,parent) if parent == nil then areaSoak.Parent = nil else areaSoak.Parent = getLayerCollectorAncestor(sliderGui) end end) local sliderPosition = Instance.new("IntValue") sliderPosition.Name = "SliderPosition" sliderPosition.Value = 0 sliderPosition.Parent = sliderGui local id = math.random(1,100) local sliderBarImgHeight = 7 local sliderBarCapImgWidth = 4 local bar = Instance.new("ImageButton") bar.BackgroundTransparency = 1 bar.Image = "rbxasset://textures/ui/Slider-BKG-Center.png" bar.Name = "Bar" local displayWidth = 200 if type(width) == "number" then bar.Size = UDim2.new(0,width - (sliderBarCapImgWidth * 2),0,sliderBarImgHeight) displayWidth = width - (sliderBarCapImgWidth * 2) else bar.Size = UDim2.new(0,200,0,sliderBarImgHeight) end bar.ZIndex = 3 bar.Parent = sliderGui if position["X"] and position["X"]["Scale"] and position["X"]["Offset"] and position["Y"] and position["Y"]["Scale"] and position["Y"]["Offset"] then bar.Position = position end local barLeft = bar:clone() barLeft.Name = "BarLeft" barLeft.Image = "rbxasset://textures/ui/Slider-BKG-Left-Cap.png" barLeft.Size = UDim2.new(0, sliderBarCapImgWidth, 0, sliderBarImgHeight) barLeft.Position = UDim2.new(position.X.Scale, position.X.Offset - sliderBarCapImgWidth, position.Y.Scale, position.Y.Offset) barLeft.Parent = sliderGui barLeft.ZIndex = 3 local barRight = barLeft:clone() barRight.Name = "BarRight" barRight.Image = "rbxasset://textures/ui/Slider-BKG-Right-Cap.png" barRight.Position = UDim2.new(position.X.Scale, position.X.Offset + displayWidth, position.Y.Scale, position.Y.Offset) barRight.Parent = sliderGui local fillLeft = barLeft:clone() fillLeft.Name = "FillLeft" fillLeft.Image = "rbxasset://textures/ui/Slider-Fill-Left-Cap.png" fillLeft.Parent = sliderGui fillLeft.ZIndex = 4 local fill = fillLeft:clone() fill.Name = "Fill" fill.Image = "rbxasset://textures/ui/Slider-Fill-Center.png" fill.Parent = bar fill.ZIndex = 4 fill.Position = UDim2.new(0, 0, 0, 0) fill.Size = UDim2.new(0.5, 0, 1, 0) -- bar.Visible = false local slider = Instance.new("ImageButton") slider.Name = "Slider" slider.BackgroundTransparency = 1 slider.Image = "rbxasset://textures/ui/slider_new_tab.png" slider.Position = UDim2.new(0,0,0.5,-14) slider.Size = UDim2.new(0,28,0,28) slider.ZIndex = 5 slider.Parent = bar local areaSoakMouseMoveCon = nil areaSoak.MouseLeave:connect(function() if areaSoak.Visible then cancelSlide(areaSoak) end end) areaSoak.MouseButton1Up:connect(function() if areaSoak.Visible then cancelSlide(areaSoak) end end) slider.MouseButton1Down:connect(function() areaSoak.Visible = true if areaSoakMouseMoveCon then areaSoakMouseMoveCon:disconnect() end areaSoakMouseMoveCon = areaSoak.MouseMoved:connect(function(x,y) setSliderPos(x,slider,sliderPosition,bar,steps) end) end) slider.MouseButton1Up:connect(function() cancelSlide(areaSoak) end) sliderPosition.Changed:connect(function(prop) sliderPosition.Value = math.min(steps, math.max(1,sliderPosition.Value)) local relativePosX = (sliderPosition.Value - 1) / (steps - 1) slider.Position = UDim2.new(relativePosX,-slider.AbsoluteSize.X/2,slider.Position.Y.Scale,slider.Position.Y.Offset) fill.Size = UDim2.new(relativePosX, 0, 1, 0) end) bar.MouseButton1Down:connect(function(x,y) setSliderPos(x,slider,sliderPosition,bar,steps) end) fill.MouseButton1Down:connect(function(x,y) setSliderPos(x,slider,sliderPosition,bar,steps) end) fillLeft.MouseButton1Down:connect(function(x,y) setSliderPos(x,slider,sliderPosition,bar,steps) end) return sliderGui, sliderPosition, sliderSteps end t.CreateTrueScrollingFrame = function() local lowY = nil local highY = nil local dragCon = nil local upCon = nil local internalChange = false local descendantsChangeConMap = {} local scrollingFrame = Instance.new("Frame") scrollingFrame.Name = "ScrollingFrame" scrollingFrame.Active = true scrollingFrame.Size = UDim2.new(1,0,1,0) scrollingFrame.ClipsDescendants = true local controlFrame = Instance.new("Frame") controlFrame.Name = "ControlFrame" controlFrame.BackgroundTransparency = 1 controlFrame.Size = UDim2.new(0,18,1,0) controlFrame.Position = UDim2.new(1,-20,0,0) controlFrame.Parent = scrollingFrame local scrollBottom = Instance.new("BoolValue") scrollBottom.Value = false scrollBottom.Name = "ScrollBottom" scrollBottom.Parent = controlFrame local scrollUp = Instance.new("BoolValue") scrollUp.Value = false scrollUp.Name = "scrollUp" scrollUp.Parent = controlFrame local scrollUpButton = Instance.new("TextButton") scrollUpButton.Name = "ScrollUpButton" scrollUpButton.Text = "" scrollUpButton.AutoButtonColor = false scrollUpButton.BackgroundColor3 = Color3.new(0,0,0) scrollUpButton.BorderColor3 = Color3.new(1,1,1) scrollUpButton.BackgroundTransparency = 0.5 scrollUpButton.Size = UDim2.new(0,18,0,18) scrollUpButton.ZIndex = 2 scrollUpButton.Parent = controlFrame for i = 1, 6 do local triFrame = Instance.new("Frame") triFrame.BorderColor3 = Color3.new(1,1,1) triFrame.Name = "tri" .. tostring(i) triFrame.ZIndex = 3 triFrame.BackgroundTransparency = 0.5 triFrame.Size = UDim2.new(0,12 - ((i -1) * 2),0,0) triFrame.Position = UDim2.new(0,3 + (i -1),0.5,2 - (i -1)) triFrame.Parent = scrollUpButton end scrollUpButton.MouseEnter:connect(function() scrollUpButton.BackgroundTransparency = 0.1 local upChildren = scrollUpButton:GetChildren() for i = 1, #upChildren do upChildren[i].BackgroundTransparency = 0.1 end end) scrollUpButton.MouseLeave:connect(function() scrollUpButton.BackgroundTransparency = 0.5 local upChildren = scrollUpButton:GetChildren() for i = 1, #upChildren do upChildren[i].BackgroundTransparency = 0.5 end end) local scrollDownButton = scrollUpButton:clone() scrollDownButton.Name = "ScrollDownButton" scrollDownButton.Position = UDim2.new(0,0,1,-18) local downChildren = scrollDownButton:GetChildren() for i = 1, #downChildren do downChildren[i].Position = UDim2.new(0,3 + (i -1),0.5,-2 + (i - 1)) end scrollDownButton.MouseEnter:connect(function() scrollDownButton.BackgroundTransparency = 0.1 local downChildren = scrollDownButton:GetChildren() for i = 1, #downChildren do downChildren[i].BackgroundTransparency = 0.1 end end) scrollDownButton.MouseLeave:connect(function() scrollDownButton.BackgroundTransparency = 0.5 local downChildren = scrollDownButton:GetChildren() for i = 1, #downChildren do downChildren[i].BackgroundTransparency = 0.5 end end) scrollDownButton.Parent = controlFrame local scrollTrack = Instance.new("Frame") scrollTrack.Name = "ScrollTrack" scrollTrack.BackgroundTransparency = 1 scrollTrack.Size = UDim2.new(0,18,1,-38) scrollTrack.Position = UDim2.new(0,0,0,19) scrollTrack.Parent = controlFrame local scrollbar = Instance.new("TextButton") scrollbar.BackgroundColor3 = Color3.new(0,0,0) scrollbar.BorderColor3 = Color3.new(1,1,1) scrollbar.BackgroundTransparency = 0.5 scrollbar.AutoButtonColor = false scrollbar.Text = "" scrollbar.Active = true scrollbar.Name = "ScrollBar" scrollbar.ZIndex = 2 scrollbar.BackgroundTransparency = 0.5 scrollbar.Size = UDim2.new(0, 18, 0.1, 0) scrollbar.Position = UDim2.new(0,0,0,0) scrollbar.Parent = scrollTrack local scrollNub = Instance.new("Frame") scrollNub.Name = "ScrollNub" scrollNub.BorderColor3 = Color3.new(1,1,1) scrollNub.Size = UDim2.new(0,10,0,0) scrollNub.Position = UDim2.new(0.5,-5,0.5,0) scrollNub.ZIndex = 2 scrollNub.BackgroundTransparency = 0.5 scrollNub.Parent = scrollbar local newNub = scrollNub:clone() newNub.Position = UDim2.new(0.5,-5,0.5,-2) newNub.Parent = scrollbar local lastNub = scrollNub:clone() lastNub.Position = UDim2.new(0.5,-5,0.5,2) lastNub.Parent = scrollbar scrollbar.MouseEnter:connect(function() scrollbar.BackgroundTransparency = 0.1 scrollNub.BackgroundTransparency = 0.1 newNub.BackgroundTransparency = 0.1 lastNub.BackgroundTransparency = 0.1 end) scrollbar.MouseLeave:connect(function() scrollbar.BackgroundTransparency = 0.5 scrollNub.BackgroundTransparency = 0.5 newNub.BackgroundTransparency = 0.5 lastNub.BackgroundTransparency = 0.5 end) local mouseDrag = Instance.new("ImageButton") mouseDrag.Active = false mouseDrag.Size = UDim2.new(1.5, 0, 1.5, 0) mouseDrag.AutoButtonColor = false mouseDrag.BackgroundTransparency = 1 mouseDrag.Name = "mouseDrag" mouseDrag.Position = UDim2.new(-0.25, 0, -0.25, 0) mouseDrag.ZIndex = 10 local function positionScrollBar(x,y,offset) local oldPos = scrollbar.Position if y < scrollTrack.AbsolutePosition.y then scrollbar.Position = UDim2.new(scrollbar.Position.X.Scale,scrollbar.Position.X.Offset,0,0) return (oldPos ~= scrollbar.Position) end local relativeSize = scrollbar.AbsoluteSize.Y/scrollTrack.AbsoluteSize.Y if y > (scrollTrack.AbsolutePosition.y + scrollTrack.AbsoluteSize.y) then scrollbar.Position = UDim2.new(scrollbar.Position.X.Scale,scrollbar.Position.X.Offset,1 - relativeSize,0) return (oldPos ~= scrollbar.Position) end local newScaleYPos = (y - scrollTrack.AbsolutePosition.y - offset)/scrollTrack.AbsoluteSize.y if newScaleYPos + relativeSize > 1 then newScaleYPos = 1 - relativeSize scrollBottom.Value = true scrollUp.Value = false elseif newScaleYPos <= 0 then newScaleYPos = 0 scrollUp.Value = true scrollBottom.Value = false else scrollUp.Value = false scrollBottom.Value = false end scrollbar.Position = UDim2.new(scrollbar.Position.X.Scale,scrollbar.Position.X.Offset,newScaleYPos,0) return (oldPos ~= scrollbar.Position) end local function drillDownSetHighLow(instance) if not instance or not instance:IsA("GuiObject") then return end if instance == controlFrame then return end if instance:IsDescendantOf(controlFrame) then return end if not instance.Visible then return end if lowY and lowY > instance.AbsolutePosition.Y then lowY = instance.AbsolutePosition.Y elseif not lowY then lowY = instance.AbsolutePosition.Y end if highY and highY < (instance.AbsolutePosition.Y + instance.AbsoluteSize.Y) then highY = instance.AbsolutePosition.Y + instance.AbsoluteSize.Y elseif not highY then highY = instance.AbsolutePosition.Y + instance.AbsoluteSize.Y end local children = instance:GetChildren() for i = 1, #children do drillDownSetHighLow(children[i]) end end local function resetHighLow() local firstChildren = scrollingFrame:GetChildren() for i = 1, #firstChildren do drillDownSetHighLow(firstChildren[i]) end end local function recalculate() internalChange = true local percentFrame = 0 if scrollbar.Position.Y.Scale > 0 then if scrollbar.Visible then percentFrame = scrollbar.Position.Y.Scale/((scrollTrack.AbsoluteSize.Y - scrollbar.AbsoluteSize.Y)/scrollTrack.AbsoluteSize.Y) else percentFrame = 0 end end if percentFrame > 0.99 then percentFrame = 1 end local hiddenYAmount = (scrollingFrame.AbsoluteSize.Y - (highY - lowY)) * percentFrame local guiChildren = scrollingFrame:GetChildren() for i = 1, #guiChildren do if guiChildren[i] ~= controlFrame then guiChildren[i].Position = UDim2.new(guiChildren[i].Position.X.Scale,guiChildren[i].Position.X.Offset, 0, math.ceil(guiChildren[i].AbsolutePosition.Y) - math.ceil(lowY) + hiddenYAmount) end end lowY = nil highY = nil resetHighLow() internalChange = false end local function setSliderSizeAndPosition() if not highY or not lowY then return end local totalYSpan = math.abs(highY - lowY) if totalYSpan == 0 then scrollbar.Visible = false scrollDownButton.Visible = false scrollUpButton.Visible = false if dragCon then dragCon:disconnect() dragCon = nil end if upCon then upCon:disconnect() upCon = nil end return end local percentShown = scrollingFrame.AbsoluteSize.Y/totalYSpan if percentShown >= 1 then scrollbar.Visible = false scrollDownButton.Visible = false scrollUpButton.Visible = false recalculate() else scrollbar.Visible = true scrollDownButton.Visible = true scrollUpButton.Visible = true scrollbar.Size = UDim2.new(scrollbar.Size.X.Scale,scrollbar.Size.X.Offset,percentShown,0) end local percentPosition = (scrollingFrame.AbsolutePosition.Y - lowY)/totalYSpan scrollbar.Position = UDim2.new(scrollbar.Position.X.Scale,scrollbar.Position.X.Offset,percentPosition,-scrollbar.AbsoluteSize.X/2) if scrollbar.AbsolutePosition.y < scrollTrack.AbsolutePosition.y then scrollbar.Position = UDim2.new(scrollbar.Position.X.Scale,scrollbar.Position.X.Offset,0,0) end if (scrollbar.AbsolutePosition.y + scrollbar.AbsoluteSize.Y) > (scrollTrack.AbsolutePosition.y + scrollTrack.AbsoluteSize.y) then local relativeSize = scrollbar.AbsoluteSize.Y/scrollTrack.AbsoluteSize.Y scrollbar.Position = UDim2.new(scrollbar.Position.X.Scale,scrollbar.Position.X.Offset,1 - relativeSize,0) end end local buttonScrollAmountPixels = 7 local reentrancyGuardScrollUp = false local function doScrollUp() if reentrancyGuardScrollUp then return end reentrancyGuardScrollUp = true if positionScrollBar(0,scrollbar.AbsolutePosition.Y - buttonScrollAmountPixels,0) then recalculate() end reentrancyGuardScrollUp = false end local reentrancyGuardScrollDown = false local function doScrollDown() if reentrancyGuardScrollDown then return end reentrancyGuardScrollDown = true if positionScrollBar(0,scrollbar.AbsolutePosition.Y + buttonScrollAmountPixels,0) then recalculate() end reentrancyGuardScrollDown = false end local function scrollUp(mouseYPos) if scrollUpButton.Active then scrollStamp = tick() local current = scrollStamp local upCon upCon = mouseDrag.MouseButton1Up:connect(function() scrollStamp = tick() mouseDrag.Parent = nil upCon:disconnect() end) mouseDrag.Parent = getLayerCollectorAncestor(scrollbar) doScrollUp() wait(0.2) local t = tick() local w = 0.1 while scrollStamp == current do doScrollUp() if mouseYPos and mouseYPos > scrollbar.AbsolutePosition.y then break end if not scrollUpButton.Active then break end if tick()-t > 5 then w = 0 elseif tick()-t > 2 then w = 0.06 end wait(w) end end end local function scrollDown(mouseYPos) if scrollDownButton.Active then scrollStamp = tick() local current = scrollStamp local downCon downCon = mouseDrag.MouseButton1Up:connect(function() scrollStamp = tick() mouseDrag.Parent = nil downCon:disconnect() end) mouseDrag.Parent = getLayerCollectorAncestor(scrollbar) doScrollDown() wait(0.2) local t = tick() local w = 0.1 while scrollStamp == current do doScrollDown() if mouseYPos and mouseYPos < (scrollbar.AbsolutePosition.y + scrollbar.AbsoluteSize.x) then break end if not scrollDownButton.Active then break end if tick()-t > 5 then w = 0 elseif tick()-t > 2 then w = 0.06 end wait(w) end end end scrollbar.MouseButton1Down:connect(function(x,y) if scrollbar.Active then scrollStamp = tick() local mouseOffset = y - scrollbar.AbsolutePosition.y if dragCon then dragCon:disconnect() dragCon = nil end if upCon then upCon:disconnect() upCon = nil end local prevY = y local reentrancyGuardMouseScroll = false dragCon = mouseDrag.MouseMoved:connect(function(x,y) if reentrancyGuardMouseScroll then return end reentrancyGuardMouseScroll = true if positionScrollBar(x,y,mouseOffset) then recalculate() end reentrancyGuardMouseScroll = false end) upCon = mouseDrag.MouseButton1Up:connect(function() scrollStamp = tick() mouseDrag.Parent = nil dragCon:disconnect(); dragCon = nil upCon:disconnect(); drag = nil end) mouseDrag.Parent = getLayerCollectorAncestor(scrollbar) end end) local scrollMouseCount = 0 scrollUpButton.MouseButton1Down:connect(function() scrollUp() end) scrollUpButton.MouseButton1Up:connect(function() scrollStamp = tick() end) scrollDownButton.MouseButton1Up:connect(function() scrollStamp = tick() end) scrollDownButton.MouseButton1Down:connect(function() scrollDown() end) scrollbar.MouseButton1Up:connect(function() scrollStamp = tick() end) local function heightCheck(instance) if highY and (instance.AbsolutePosition.Y + instance.AbsoluteSize.Y) > highY then highY = instance.AbsolutePosition.Y + instance.AbsoluteSize.Y elseif not highY then highY = instance.AbsolutePosition.Y + instance.AbsoluteSize.Y end setSliderSizeAndPosition() end local function highLowRecheck() local oldLowY = lowY local oldHighY = highY lowY = nil highY = nil resetHighLow() if (lowY ~= oldLowY) or (highY ~= oldHighY) then setSliderSizeAndPosition() end end local function descendantChanged(this, prop) if internalChange then return end if not this.Visible then return end if prop == "Size" or prop == "Position" then wait() highLowRecheck() end end scrollingFrame.DescendantAdded:connect(function(instance) if not instance:IsA("GuiObject") then return end if instance.Visible then wait() -- wait a heartbeat for sizes to reconfig highLowRecheck() end descendantsChangeConMap[instance] = instance.Changed:connect(function(prop) descendantChanged(instance, prop) end) end) scrollingFrame.DescendantRemoving:connect(function(instance) if not instance:IsA("GuiObject") then return end if descendantsChangeConMap[instance] then descendantsChangeConMap[instance]:disconnect() descendantsChangeConMap[instance] = nil end wait() -- wait a heartbeat for sizes to reconfig highLowRecheck() end) scrollingFrame.Changed:connect(function(prop) if prop == "AbsoluteSize" then if not highY or not lowY then return end highLowRecheck() setSliderSizeAndPosition() end end) return scrollingFrame, controlFrame end t.CreateScrollingFrame = function(orderList,scrollStyle) local frame = Instance.new("Frame") frame.Name = "ScrollingFrame" frame.BackgroundTransparency = 1 frame.Size = UDim2.new(1,0,1,0) local scrollUpButton = Instance.new("ImageButton") scrollUpButton.Name = "ScrollUpButton" scrollUpButton.BackgroundTransparency = 1 scrollUpButton.Image = "rbxasset://textures/ui/scrollbuttonUp.png" scrollUpButton.Size = UDim2.new(0,17,0,17) local scrollDownButton = Instance.new("ImageButton") scrollDownButton.Name = "ScrollDownButton" scrollDownButton.BackgroundTransparency = 1 scrollDownButton.Image = "rbxasset://textures/ui/scrollbuttonDown.png" scrollDownButton.Size = UDim2.new(0,17,0,17) local scrollbar = Instance.new("ImageButton") scrollbar.Name = "ScrollBar" scrollbar.Image = "rbxasset://textures/ui/scrollbar.png" scrollbar.BackgroundTransparency = 1 scrollbar.Size = UDim2.new(0, 18, 0, 150) local scrollStamp = 0 local scrollDrag = Instance.new("ImageButton") scrollDrag.Image = "https://www.roblox.com/asset/?id=61367186" scrollDrag.Size = UDim2.new(1, 0, 0, 16) scrollDrag.BackgroundTransparency = 1 scrollDrag.Name = "ScrollDrag" scrollDrag.Active = true scrollDrag.Parent = scrollbar local mouseDrag = Instance.new("ImageButton") mouseDrag.Active = false mouseDrag.Size = UDim2.new(1.5, 0, 1.5, 0) mouseDrag.AutoButtonColor = false mouseDrag.BackgroundTransparency = 1 mouseDrag.Name = "mouseDrag" mouseDrag.Position = UDim2.new(-0.25, 0, -0.25, 0) mouseDrag.ZIndex = 10 local style = "simple" if scrollStyle and tostring(scrollStyle) then style = scrollStyle end local scrollPosition = 1 local rowSize = 0 local howManyDisplayed = 0 local layoutGridScrollBar = function() howManyDisplayed = 0 local guiObjects = {} if orderList then for i, child in ipairs(orderList) do if child.Parent == frame then table.insert(guiObjects, child) end end else local children = frame:GetChildren() if children then for i, child in ipairs(children) do if child:IsA("GuiObject") then table.insert(guiObjects, child) end end end end if #guiObjects == 0 then scrollUpButton.Active = false scrollDownButton.Active = false scrollDrag.Active = false scrollPosition = 1 return end if scrollPosition > #guiObjects then scrollPosition = #guiObjects end if scrollPosition < 1 then scrollPosition = 1 end local totalPixelsY = frame.AbsoluteSize.Y local pixelsRemainingY = frame.AbsoluteSize.Y local totalPixelsX = frame.AbsoluteSize.X local xCounter = 0 local rowSizeCounter = 0 local setRowSize = true local pixelsBelowScrollbar = 0 local pos = #guiObjects local currentRowY = 0 pos = scrollPosition --count up from current scroll position to fill out grid while pos <= #guiObjects and pixelsBelowScrollbar < totalPixelsY do xCounter = xCounter + guiObjects[pos].AbsoluteSize.X --previous pos was the end of a row if xCounter >= totalPixelsX then pixelsBelowScrollbar = pixelsBelowScrollbar + currentRowY currentRowY = 0 xCounter = guiObjects[pos].AbsoluteSize.X end if guiObjects[pos].AbsoluteSize.Y > currentRowY then currentRowY = guiObjects[pos].AbsoluteSize.Y end pos = pos + 1 end --Count wherever current row left off pixelsBelowScrollbar = pixelsBelowScrollbar + currentRowY currentRowY = 0 pos = scrollPosition - 1 xCounter = 0 --objects with varying X,Y dimensions can rarely cause minor errors --rechecking every new scrollPosition is necessary to avoid 100% of errors --count backwards from current scrollPosition to see if we can add more rows while pixelsBelowScrollbar + currentRowY < totalPixelsY and pos >= 1 do xCounter = xCounter + guiObjects[pos].AbsoluteSize.X rowSizeCounter = rowSizeCounter + 1 if xCounter >= totalPixelsX then rowSize = rowSizeCounter - 1 rowSizeCounter = 0 xCounter = guiObjects[pos].AbsoluteSize.X if pixelsBelowScrollbar + currentRowY <= totalPixelsY then --It fits, so back up our scroll position pixelsBelowScrollbar = pixelsBelowScrollbar + currentRowY if scrollPosition <= rowSize then scrollPosition = 1 break else scrollPosition = scrollPosition - rowSize end currentRowY = 0 else break end end if guiObjects[pos].AbsoluteSize.Y > currentRowY then currentRowY = guiObjects[pos].AbsoluteSize.Y end pos = pos - 1 end --Do check last time if pos = 0 if (pos == 0) and (pixelsBelowScrollbar + currentRowY <= totalPixelsY) then scrollPosition = 1 end xCounter = 0 --pos = scrollPosition rowSizeCounter = 0 setRowSize = true local lastChildSize = 0 local xOffset,yOffset = 0 if guiObjects[1] then yOffset = math.ceil(math.floor(math.fmod(totalPixelsY,guiObjects[1].AbsoluteSize.X))/2) xOffset = math.ceil(math.floor(math.fmod(totalPixelsX,guiObjects[1].AbsoluteSize.Y))/2) end for i, child in ipairs(guiObjects) do if i < scrollPosition then child.Visible = false else if pixelsRemainingY < 0 then child.Visible = false else --GuiObject if setRowSize then rowSizeCounter = rowSizeCounter + 1 end if xCounter + child.AbsoluteSize.X >= totalPixelsX then if setRowSize then rowSize = rowSizeCounter - 1 setRowSize = false end xCounter = 0 pixelsRemainingY = pixelsRemainingY - child.AbsoluteSize.Y end child.Position = UDim2.new(child.Position.X.Scale,xCounter + xOffset, 0, totalPixelsY - pixelsRemainingY + yOffset) xCounter = xCounter + child.AbsoluteSize.X child.Visible = ((pixelsRemainingY - child.AbsoluteSize.Y) >= 0) if child.Visible then howManyDisplayed = howManyDisplayed + 1 end lastChildSize = child.AbsoluteSize end end end scrollUpButton.Active = (scrollPosition > 1) if lastChildSize == 0 then scrollDownButton.Active = false else scrollDownButton.Active = ((pixelsRemainingY - lastChildSize.Y) < 0) end scrollDrag.Active = #guiObjects > howManyDisplayed scrollDrag.Visible = scrollDrag.Active end local layoutSimpleScrollBar = function() local guiObjects = {} howManyDisplayed = 0 if orderList then for i, child in ipairs(orderList) do if child.Parent == frame then table.insert(guiObjects, child) end end else local children = frame:GetChildren() if children then for i, child in ipairs(children) do if child:IsA("GuiObject") then table.insert(guiObjects, child) end end end end if #guiObjects == 0 then scrollUpButton.Active = false scrollDownButton.Active = false scrollDrag.Active = false scrollPosition = 1 return end if scrollPosition > #guiObjects then scrollPosition = #guiObjects end local totalPixels = frame.AbsoluteSize.Y local pixelsRemaining = frame.AbsoluteSize.Y local pixelsBelowScrollbar = 0 local pos = #guiObjects while pixelsBelowScrollbar < totalPixels and pos >= 1 do if pos >= scrollPosition then pixelsBelowScrollbar = pixelsBelowScrollbar + guiObjects[pos].AbsoluteSize.Y else if pixelsBelowScrollbar + guiObjects[pos].AbsoluteSize.Y <= totalPixels then --It fits, so back up our scroll position pixelsBelowScrollbar = pixelsBelowScrollbar + guiObjects[pos].AbsoluteSize.Y if scrollPosition <= 1 then scrollPosition = 1 break else --local ("Backing up ScrollPosition from -- " ..scrollPosition) scrollPosition = scrollPosition - 1 end else break end end pos = pos - 1 end pos = scrollPosition for i, child in ipairs(guiObjects) do if i < scrollPosition then child.Visible = false else if pixelsRemaining < 0 then child.Visible = false else --GuiObject child.Position = UDim2.new(child.Position.X.Scale, child.Position.X.Offset, 0, totalPixels - pixelsRemaining) pixelsRemaining = pixelsRemaining - child.AbsoluteSize.Y if (pixelsRemaining >= 0) then child.Visible = true howManyDisplayed = howManyDisplayed + 1 else child.Visible = false end end end end scrollUpButton.Active = (scrollPosition > 1) scrollDownButton.Active = (pixelsRemaining < 0) scrollDrag.Active = #guiObjects > howManyDisplayed scrollDrag.Visible = scrollDrag.Active end local moveDragger = function() local guiObjects = 0 local children = frame:GetChildren() if children then for i, child in ipairs(children) do if child:IsA("GuiObject") then guiObjects = guiObjects + 1 end end end if not scrollDrag.Parent then return end local dragSizeY = scrollDrag.Parent.AbsoluteSize.y * (1/(guiObjects - howManyDisplayed + 1)) if dragSizeY < 16 then dragSizeY = 16 end scrollDrag.Size = UDim2.new(scrollDrag.Size.X.Scale,scrollDrag.Size.X.Offset,scrollDrag.Size.Y.Scale,dragSizeY) local relativeYPos = (scrollPosition - 1)/(guiObjects - (howManyDisplayed)) if relativeYPos > 1 then relativeYPos = 1 elseif relativeYPos < 0 then relativeYPos = 0 end local absYPos = 0 if relativeYPos ~= 0 then absYPos = (relativeYPos * scrollbar.AbsoluteSize.y) - (relativeYPos * scrollDrag.AbsoluteSize.y) end scrollDrag.Position = UDim2.new(scrollDrag.Position.X.Scale,scrollDrag.Position.X.Offset,scrollDrag.Position.Y.Scale,absYPos) end local reentrancyGuard = false local recalculate = function() if reentrancyGuard then return end reentrancyGuard = true wait() local success, err = nil if style == "grid" then success, err = pcall(function() layoutGridScrollBar() end) elseif style == "simple" then success, err = pcall(function() layoutSimpleScrollBar() end) end if not success then print(err) end moveDragger() reentrancyGuard = false end local doScrollUp = function() scrollPosition = (scrollPosition) - rowSize if scrollPosition < 1 then scrollPosition = 1 end recalculate(nil) end local doScrollDown = function() scrollPosition = (scrollPosition) + rowSize recalculate(nil) end local scrollUp = function(mouseYPos) if scrollUpButton.Active then scrollStamp = tick() local current = scrollStamp local upCon upCon = mouseDrag.MouseButton1Up:connect(function() scrollStamp = tick() mouseDrag.Parent = nil upCon:disconnect() end) mouseDrag.Parent = getLayerCollectorAncestor(scrollbar) doScrollUp() wait(0.2) local t = tick() local w = 0.1 while scrollStamp == current do doScrollUp() if mouseYPos and mouseYPos > scrollDrag.AbsolutePosition.y then break end if not scrollUpButton.Active then break end if tick()-t > 5 then w = 0 elseif tick()-t > 2 then w = 0.06 end wait(w) end end end local scrollDown = function(mouseYPos) if scrollDownButton.Active then scrollStamp = tick() local current = scrollStamp local downCon downCon = mouseDrag.MouseButton1Up:connect(function() scrollStamp = tick() mouseDrag.Parent = nil downCon:disconnect() end) mouseDrag.Parent = getLayerCollectorAncestor(scrollbar) doScrollDown() wait(0.2) local t = tick() local w = 0.1 while scrollStamp == current do doScrollDown() if mouseYPos and mouseYPos < (scrollDrag.AbsolutePosition.y + scrollDrag.AbsoluteSize.x) then break end if not scrollDownButton.Active then break end if tick()-t > 5 then w = 0 elseif tick()-t > 2 then w = 0.06 end wait(w) end end end local y = 0 scrollDrag.MouseButton1Down:connect(function(x,y) if scrollDrag.Active then scrollStamp = tick() local mouseOffset = y - scrollDrag.AbsolutePosition.y local dragCon local upCon dragCon = mouseDrag.MouseMoved:connect(function(x,y) local barAbsPos = scrollbar.AbsolutePosition.y local barAbsSize = scrollbar.AbsoluteSize.y local dragAbsSize = scrollDrag.AbsoluteSize.y local barAbsOne = barAbsPos + barAbsSize - dragAbsSize y = y - mouseOffset y = y < barAbsPos and barAbsPos or y > barAbsOne and barAbsOne or y y = y - barAbsPos local guiObjects = 0 local children = frame:GetChildren() if children then for i, child in ipairs(children) do if child:IsA("GuiObject") then guiObjects = guiObjects + 1 end end end local doublePercent = y/(barAbsSize-dragAbsSize) local rowDiff = rowSize local totalScrollCount = guiObjects - (howManyDisplayed - 1) local newScrollPosition = math.floor((doublePercent * totalScrollCount) + 0.5) + rowDiff if newScrollPosition < scrollPosition then rowDiff = -rowDiff end if newScrollPosition < 1 then newScrollPosition = 1 end scrollPosition = newScrollPosition recalculate(nil) end) upCon = mouseDrag.MouseButton1Up:connect(function() scrollStamp = tick() mouseDrag.Parent = nil dragCon:disconnect(); dragCon = nil upCon:disconnect(); drag = nil end) mouseDrag.Parent = getLayerCollectorAncestor(scrollbar) end end) local scrollMouseCount = 0 scrollUpButton.MouseButton1Down:connect( function() scrollUp() end) scrollUpButton.MouseButton1Up:connect(function() scrollStamp = tick() end) scrollDownButton.MouseButton1Up:connect(function() scrollStamp = tick() end) scrollDownButton.MouseButton1Down:connect( function() scrollDown() end) scrollbar.MouseButton1Up:connect(function() scrollStamp = tick() end) scrollbar.MouseButton1Down:connect( function(x,y) if y > (scrollDrag.AbsoluteSize.y + scrollDrag.AbsolutePosition.y) then scrollDown(y) elseif y < (scrollDrag.AbsolutePosition.y) then scrollUp(y) end end) frame.ChildAdded:connect(function() recalculate(nil) end) frame.ChildRemoved:connect(function() recalculate(nil) end) frame.Changed:connect( function(prop) if prop == "AbsoluteSize" then --Wait a heartbeat for it to sync in recalculate(nil) end end) frame.AncestryChanged:connect(function() recalculate(nil) end) return frame, scrollUpButton, scrollDownButton, recalculate, scrollbar end local function binaryGrow(min, max, fits) if min > max then return min end local biggestLegal = min while min <= max do local mid = min + math.floor((max - min) / 2) if fits(mid) and (biggestLegal == nil or biggestLegal < mid) then biggestLegal = mid --Try growing min = mid + 1 else --Doesn't fit, shrink max = mid - 1 end end return biggestLegal end local function binaryShrink(min, max, fits) if min > max then return min end local smallestLegal = max while min <= max do local mid = min + math.floor((max - min) / 2) if fits(mid) and (smallestLegal == nil or smallestLegal > mid) then smallestLegal = mid --It fits, shrink max = mid - 1 else --Doesn't fit, grow min = mid + 1 end end return smallestLegal end local function getGuiOwner(instance) while instance ~= nil do if instance:IsA("ScreenGui") or instance:IsA("BillboardGui") then return instance end instance = instance.Parent end return nil end t.AutoTruncateTextObject = function(textLabel) local text = textLabel.Text local fullLabel = textLabel:Clone() fullLabel.Name = "Full" .. textLabel.Name fullLabel.BorderSizePixel = 0 fullLabel.BackgroundTransparency = 0 fullLabel.Text = text fullLabel.TextXAlignment = Enum.TextXAlignment.Center fullLabel.Position = UDim2.new(0,-3,0,0) fullLabel.Size = UDim2.new(0,100,1,0) fullLabel.Visible = false fullLabel.Parent = textLabel local shortText = nil local mouseEnterConnection = nil local mouseLeaveConnection= nil local checkForResize = function() if getGuiOwner(textLabel) == nil then return end textLabel.Text = text if textLabel.TextFits then --Tear down the rollover if it is active if mouseEnterConnection then mouseEnterConnection:disconnect() mouseEnterConnection = nil end if mouseLeaveConnection then mouseLeaveConnection:disconnect() mouseLeaveConnection = nil end else local len = string.len(text) textLabel.Text = text .. "~" --Shrink the text local textSize = binaryGrow(0, len, function(pos) if pos == 0 then textLabel.Text = "~" else textLabel.Text = string.sub(text, 1, pos) .. "~" end return textLabel.TextFits end) shortText = string.sub(text, 1, textSize) .. "~" textLabel.Text = shortText --Make sure the fullLabel fits if not fullLabel.TextFits then --Already too small, grow it really bit to start fullLabel.Size = UDim2.new(0, 10000, 1, 0) end --Okay, now try to binary shrink it back down local fullLabelSize = binaryShrink(textLabel.AbsoluteSize.X,fullLabel.AbsoluteSize.X, function(size) fullLabel.Size = UDim2.new(0, size, 1, 0) return fullLabel.TextFits end) fullLabel.Size = UDim2.new(0,fullLabelSize+6,1,0) --Now setup the rollover effects, if they are currently off if mouseEnterConnection == nil then mouseEnterConnection = textLabel.MouseEnter:connect( function() fullLabel.ZIndex = textLabel.ZIndex + 1 fullLabel.Visible = true --textLabel.Text = "" end) end if mouseLeaveConnection == nil then mouseLeaveConnection = textLabel.MouseLeave:connect( function() fullLabel.Visible = false --textLabel.Text = shortText end) end end end textLabel.AncestryChanged:connect(checkForResize) textLabel.Changed:connect( function(prop) if prop == "AbsoluteSize" then checkForResize() end end) checkForResize() local function changeText(newText) text = newText fullLabel.Text = text checkForResize() end return textLabel, changeText end local function TransitionTutorialPages(fromPage, toPage, transitionFrame, currentPageValue) if fromPage then fromPage.Visible = false if transitionFrame.Visible == false then transitionFrame.Size = fromPage.Size transitionFrame.Position = fromPage.Position end else if transitionFrame.Visible == false then transitionFrame.Size = UDim2.new(0.0,50,0.0,50) transitionFrame.Position = UDim2.new(0.5,-25,0.5,-25) end end transitionFrame.Visible = true currentPageValue.Value = nil local newSize, newPosition if toPage then --Make it visible so it resizes toPage.Visible = true newSize = toPage.Size newPosition = toPage.Position toPage.Visible = false else newSize = UDim2.new(0.0,50,0.0,50) newPosition = UDim2.new(0.5,-25,0.5,-25) end transitionFrame:TweenSizeAndPosition(newSize, newPosition, Enum.EasingDirection.InOut, Enum.EasingStyle.Quad, 0.3, true, function(state) if state == Enum.TweenStatus.Completed then transitionFrame.Visible = false if toPage then toPage.Visible = true currentPageValue.Value = toPage end end end) end t.CreateTutorial = function(name, tutorialKey, createButtons) local frame = Instance.new("Frame") frame.Name = "Tutorial-" .. name frame.BackgroundTransparency = 1 frame.Size = UDim2.new(0.6, 0, 0.6, 0) frame.Position = UDim2.new(0.2, 0, 0.2, 0) local transitionFrame = Instance.new("Frame") transitionFrame.Name = "TransitionFrame" transitionFrame.Style = Enum.FrameStyle.RobloxRound transitionFrame.Size = UDim2.new(0.6, 0, 0.6, 0) transitionFrame.Position = UDim2.new(0.2, 0, 0.2, 0) transitionFrame.Visible = false transitionFrame.Parent = frame local currentPageValue = Instance.new("ObjectValue") currentPageValue.Name = "CurrentTutorialPage" currentPageValue.Value = nil currentPageValue.Parent = frame local boolValue = Instance.new("BoolValue") boolValue.Name = "Buttons" boolValue.Value = createButtons boolValue.Parent = frame local pages = Instance.new("Frame") pages.Name = "Pages" pages.BackgroundTransparency = 1 pages.Size = UDim2.new(1,0,1,0) pages.Parent = frame local function getVisiblePageAndHideOthers() local visiblePage = nil local children = pages:GetChildren() if children then for i,child in ipairs(children) do if child.Visible then if visiblePage then child.Visible = false else visiblePage = child end end end end return visiblePage end local showTutorial = function(alwaysShow) if alwaysShow or UserSettings().GameSettings:GetTutorialState(tutorialKey) == false then print("Showing tutorial-",tutorialKey) local currentTutorialPage = getVisiblePageAndHideOthers() local firstPage = pages:FindFirstChild("TutorialPage1") if firstPage then TransitionTutorialPages(currentTutorialPage, firstPage, transitionFrame, currentPageValue) else error("Could not find TutorialPage1") end end end local dismissTutorial = function() local currentTutorialPage = getVisiblePageAndHideOthers() if currentTutorialPage then TransitionTutorialPages(currentTutorialPage, nil, transitionFrame, currentPageValue) end UserSettings().GameSettings:SetTutorialState(tutorialKey, true) end local gotoPage = function(pageNum) local page = pages:FindFirstChild("TutorialPage" .. pageNum) local currentTutorialPage = getVisiblePageAndHideOthers() TransitionTutorialPages(currentTutorialPage, page, transitionFrame, currentPageValue) end return frame, showTutorial, dismissTutorial, gotoPage end local function CreateBasicTutorialPage(name, handleResize, skipTutorial, giveDoneButton) local frame = Instance.new("Frame") frame.Name = "TutorialPage" frame.Style = Enum.FrameStyle.RobloxRound frame.Size = UDim2.new(0.6, 0, 0.6, 0) frame.Position = UDim2.new(0.2, 0, 0.2, 0) frame.Visible = false local frameHeader = Instance.new("TextLabel") frameHeader.Name = "Header" frameHeader.Text = name frameHeader.BackgroundTransparency = 1 frameHeader.FontSize = Enum.FontSize.Size24 frameHeader.Font = Enum.Font.ArialBold frameHeader.TextColor3 = Color3.new(1,1,1) frameHeader.TextXAlignment = Enum.TextXAlignment.Center frameHeader.TextWrap = true frameHeader.Size = UDim2.new(1,-55, 0, 22) frameHeader.Position = UDim2.new(0,0,0,0) frameHeader.Parent = frame local skipButton = Instance.new("ImageButton") skipButton.Name = "SkipButton" skipButton.AutoButtonColor = false skipButton.BackgroundTransparency = 1 skipButton.Image = "rbxasset://textures/ui/closeButton.png" skipButton.MouseButton1Click:connect(function() skipTutorial() end) skipButton.MouseEnter:connect(function() skipButton.Image = "rbxasset://textures/ui/closeButton_dn.png" end) skipButton.MouseLeave:connect(function() skipButton.Image = "rbxasset://textures/ui/closeButton.png" end) skipButton.Size = UDim2.new(0, 25, 0, 25) skipButton.Position = UDim2.new(1, -25, 0, 0) skipButton.Parent = frame if giveDoneButton then local doneButton = Instance.new("TextButton") doneButton.Name = "DoneButton" doneButton.Style = Enum.ButtonStyle.RobloxButtonDefault doneButton.Text = "Done" doneButton.TextColor3 = Color3.new(1,1,1) doneButton.Font = Enum.Font.ArialBold doneButton.FontSize = Enum.FontSize.Size18 doneButton.Size = UDim2.new(0,100,0,50) doneButton.Position = UDim2.new(0.5,-50,1,-50) if skipTutorial then doneButton.MouseButton1Click:connect(function() skipTutorial() end) end doneButton.Parent = frame end local innerFrame = Instance.new("Frame") innerFrame.Name = "ContentFrame" innerFrame.BackgroundTransparency = 1 innerFrame.Position = UDim2.new(0,0,0,25) innerFrame.Parent = frame local nextButton = Instance.new("TextButton") nextButton.Name = "NextButton" nextButton.Text = "Next" nextButton.TextColor3 = Color3.new(1,1,1) nextButton.Font = Enum.Font.Arial nextButton.FontSize = Enum.FontSize.Size18 nextButton.Style = Enum.ButtonStyle.RobloxButtonDefault nextButton.Size = UDim2.new(0,80, 0, 32) nextButton.Position = UDim2.new(0.5, 5, 1, -32) nextButton.Active = false nextButton.Visible = false nextButton.Parent = frame local prevButton = Instance.new("TextButton") prevButton.Name = "PrevButton" prevButton.Text = "Previous" prevButton.TextColor3 = Color3.new(1,1,1) prevButton.Font = Enum.Font.Arial prevButton.FontSize = Enum.FontSize.Size18 prevButton.Style = Enum.ButtonStyle.RobloxButton prevButton.Size = UDim2.new(0,80, 0, 32) prevButton.Position = UDim2.new(0.5, -85, 1, -32) prevButton.Active = false prevButton.Visible = false prevButton.Parent = frame if giveDoneButton then innerFrame.Size = UDim2.new(1,0,1,-75) else innerFrame.Size = UDim2.new(1,0,1,-22) end local parentConnection = nil local function basicHandleResize() if frame.Visible and frame.Parent then local maxSize = math.min(frame.Parent.AbsoluteSize.X, frame.Parent.AbsoluteSize.Y) handleResize(200,maxSize) end end frame.Changed:connect( function(prop) if prop == "Parent" then if parentConnection ~= nil then parentConnection:disconnect() parentConnection = nil end if frame.Parent and frame.Parent:IsA("GuiObject") then parentConnection = frame.Parent.Changed:connect( function(parentProp) if parentProp == "AbsoluteSize" then wait() basicHandleResize() end end) basicHandleResize() end end if prop == "Visible" then basicHandleResize() end end) return frame, innerFrame end t.CreateTextTutorialPage = function(name, text, skipTutorialFunc) local frame = nil local contentFrame = nil local textLabel = Instance.new("TextLabel") textLabel.BackgroundTransparency = 1 textLabel.TextColor3 = Color3.new(1,1,1) textLabel.Text = text textLabel.TextWrap = true textLabel.TextXAlignment = Enum.TextXAlignment.Left textLabel.TextYAlignment = Enum.TextYAlignment.Center textLabel.Font = Enum.Font.Arial textLabel.FontSize = Enum.FontSize.Size14 textLabel.Size = UDim2.new(1,0,1,0) local function handleResize(minSize, maxSize) size = binaryShrink(minSize, maxSize, function(size) frame.Size = UDim2.new(0, size, 0, size) return textLabel.TextFits end) frame.Size = UDim2.new(0, size, 0, size) frame.Position = UDim2.new(0.5, -size/2, 0.5, -size/2) end frame, contentFrame = CreateBasicTutorialPage(name, handleResize, skipTutorialFunc) textLabel.Parent = contentFrame return frame end t.CreateImageTutorialPage = function(name, imageAsset, x, y, skipTutorialFunc, giveDoneButton) local frame = nil local contentFrame = nil local imageLabel = Instance.new("ImageLabel") imageLabel.BackgroundTransparency = 1 imageLabel.Image = imageAsset imageLabel.Size = UDim2.new(0,x,0,y) imageLabel.Position = UDim2.new(0.5,-x/2,0.5,-y/2) local function handleResize(minSize, maxSize) size = binaryShrink(minSize, maxSize, function(size) return size >= x and size >= y end) if size >= x and size >= y then imageLabel.Size = UDim2.new(0,x, 0,y) imageLabel.Position = UDim2.new(0.5,-x/2, 0.5, -y/2) else if x > y then --X is limiter, so imageLabel.Size = UDim2.new(1,0,y/x,0) imageLabel.Position = UDim2.new(0,0, 0.5 - (y/x)/2, 0) else --Y is limiter imageLabel.Size = UDim2.new(x/y,0,1, 0) imageLabel.Position = UDim2.new(0.5-(x/y)/2, 0, 0, 0) end end size = size + 50 frame.Size = UDim2.new(0, size, 0, size) frame.Position = UDim2.new(0.5, -size/2, 0.5, -size/2) end frame, contentFrame = CreateBasicTutorialPage(name, handleResize, skipTutorialFunc, giveDoneButton) imageLabel.Parent = contentFrame return frame end t.AddTutorialPage = function(tutorial, tutorialPage) local transitionFrame = tutorial.TransitionFrame local currentPageValue = tutorial.CurrentTutorialPage if not tutorial.Buttons.Value then tutorialPage.NextButton.Parent = nil tutorialPage.PrevButton.Parent = nil end local children = tutorial.Pages:GetChildren() if children and #children > 0 then tutorialPage.Name = "TutorialPage" .. (#children+1) local previousPage = children[#children] if not previousPage:IsA("GuiObject") then error("All elements under Pages must be GuiObjects") end if tutorial.Buttons.Value then if previousPage.NextButton.Active then error("NextButton already Active on previousPage, please only add pages with RbxGui.AddTutorialPage function") end previousPage.NextButton.MouseButton1Click:connect( function() TransitionTutorialPages(previousPage, tutorialPage, transitionFrame, currentPageValue) end) previousPage.NextButton.Active = true previousPage.NextButton.Visible = true if tutorialPage.PrevButton.Active then error("PrevButton already Active on tutorialPage, please only add pages with RbxGui.AddTutorialPage function") end tutorialPage.PrevButton.MouseButton1Click:connect( function() TransitionTutorialPages(tutorialPage, previousPage, transitionFrame, currentPageValue) end) tutorialPage.PrevButton.Active = true tutorialPage.PrevButton.Visible = true end tutorialPage.Parent = tutorial.Pages else --First child tutorialPage.Name = "TutorialPage1" tutorialPage.Parent = tutorial.Pages end end t.CreateSetPanel = function(userIdsForSets, objectSelected, dialogClosed, size, position, showAdminCategories, useAssetVersionId) if not userIdsForSets then error("CreateSetPanel: userIdsForSets (first arg) is nil, should be a table of number ids") end if type(userIdsForSets) ~= "table" and type(userIdsForSets) ~= "userdata" then error("CreateSetPanel: userIdsForSets (first arg) is of type " ..type(userIdsForSets) .. ", should be of type table or userdata") end if not objectSelected then error("CreateSetPanel: objectSelected (second arg) is nil, should be a callback function!") end if type(objectSelected) ~= "function" then error("CreateSetPanel: objectSelected (second arg) is of type " .. type(objectSelected) .. ", should be of type function!") end if dialogClosed and type(dialogClosed) ~= "function" then error("CreateSetPanel: dialogClosed (third arg) is of type " .. type(dialogClosed) .. ", should be of type function!") end if showAdminCategories == nil then -- by default, don't show beta sets showAdminCategories = false end local arrayPosition = 1 local insertButtons = {} local insertButtonCons = {} local contents = nil local setGui = nil -- used for water selections local waterForceDirection = "NegX" local waterForce = "None" local waterGui, waterTypeChangedEvent = nil local Data = {} Data.CurrentCategory = nil Data.Category = {} local SetCache = {} local userCategoryButtons = nil local buttonWidth = 64 local buttonHeight = buttonWidth local SmallThumbnailUrl = nil local LargeThumbnailUrl = nil local BaseUrl = game:GetService("ContentProvider").BaseUrl:lower() local AssetGameUrl = string.gsub(BaseUrl, "www", "assetgame") if useAssetVersionId then LargeThumbnailUrl = AssetGameUrl .. "Game/Tools/ThumbnailAsset.ashx?fmt=png&wd=420&ht=420&assetversionid=" SmallThumbnailUrl = AssetGameUrl .. "Game/Tools/ThumbnailAsset.ashx?fmt=png&wd=75&ht=75&assetversionid=" else LargeThumbnailUrl = AssetGameUrl .. "Game/Tools/ThumbnailAsset.ashx?fmt=png&wd=420&ht=420&aid=" SmallThumbnailUrl = AssetGameUrl .. "Game/Tools/ThumbnailAsset.ashx?fmt=png&wd=75&ht=75&aid=" end local function drillDownSetZIndex(parent, index) local children = parent:GetChildren() for i = 1, #children do if children[i]:IsA("GuiObject") then children[i].ZIndex = index end drillDownSetZIndex(children[i], index) end end -- for terrain stamping local currTerrainDropDownFrame = nil local terrainShapes = {"Block","Vertical Ramp","Corner Wedge","Inverse Corner Wedge","Horizontal Ramp","Auto-Wedge"} local terrainShapeMap = {} for i = 1, #terrainShapes do terrainShapeMap[terrainShapes[i]] = i - 1 end terrainShapeMap[terrainShapes[#terrainShapes]] = 6 local function createWaterGui() local waterForceDirections = {"NegX","X","NegY","Y","NegZ","Z"} local waterForces = {"None", "Small", "Medium", "Strong", "Max"} local waterFrame = Instance.new("Frame") waterFrame.Name = "WaterFrame" waterFrame.Style = Enum.FrameStyle.RobloxSquare waterFrame.Size = UDim2.new(0,150,0,110) waterFrame.Visible = false local waterForceLabel = Instance.new("TextLabel") waterForceLabel.Name = "WaterForceLabel" waterForceLabel.BackgroundTransparency = 1 waterForceLabel.Size = UDim2.new(1,0,0,12) waterForceLabel.Font = Enum.Font.ArialBold waterForceLabel.FontSize = Enum.FontSize.Size12 waterForceLabel.TextColor3 = Color3.new(1,1,1) waterForceLabel.TextXAlignment = Enum.TextXAlignment.Left waterForceLabel.Text = "Water Force" waterForceLabel.Parent = waterFrame local waterForceDirLabel = waterForceLabel:Clone() waterForceDirLabel.Name = "WaterForceDirectionLabel" waterForceDirLabel.Text = "Water Force Direction" waterForceDirLabel.Position = UDim2.new(0,0,0,50) waterForceDirLabel.Parent = waterFrame local waterTypeChangedEvent = Instance.new("BindableEvent") waterTypeChangedEvent.Name = "WaterTypeChangedEvent" waterTypeChangedEvent.Parent = waterFrame local waterForceDirectionSelectedFunc = function(newForceDirection) waterForceDirection = newForceDirection waterTypeChangedEvent:Fire({waterForce, waterForceDirection}) end local waterForceSelectedFunc = function(newForce) waterForce = newForce waterTypeChangedEvent:Fire({waterForce, waterForceDirection}) end local waterForceDirectionDropDown, forceWaterDirectionSelection = t.CreateDropDownMenu(waterForceDirections, waterForceDirectionSelectedFunc) waterForceDirectionDropDown.Size = UDim2.new(1,0,0,25) waterForceDirectionDropDown.Position = UDim2.new(0,0,1,3) forceWaterDirectionSelection("NegX") waterForceDirectionDropDown.Parent = waterForceDirLabel local waterForceDropDown, forceWaterForceSelection = t.CreateDropDownMenu(waterForces, waterForceSelectedFunc) forceWaterForceSelection("None") waterForceDropDown.Size = UDim2.new(1,0,0,25) waterForceDropDown.Position = UDim2.new(0,0,1,3) waterForceDropDown.Parent = waterForceLabel return waterFrame, waterTypeChangedEvent end -- Helper Function that contructs gui elements local function createSetGui() local setGui = Instance.new("ScreenGui") setGui.Name = "SetGui" local setPanel = Instance.new("Frame") setPanel.Name = "SetPanel" setPanel.Active = true setPanel.BackgroundTransparency = 1 if position then setPanel.Position = position else setPanel.Position = UDim2.new(0.2, 29, 0.1, 24) end if size then setPanel.Size = size else setPanel.Size = UDim2.new(0.6, -58, 0.64, 0) end setPanel.Style = Enum.FrameStyle.RobloxRound setPanel.ZIndex = 6 setPanel.Parent = setGui -- Children of SetPanel local itemPreview = Instance.new("Frame") itemPreview.Name = "ItemPreview" itemPreview.BackgroundTransparency = 1 itemPreview.Position = UDim2.new(0.8,5,0.085,0) itemPreview.Size = UDim2.new(0.21,0,0.9,0) itemPreview.ZIndex = 6 itemPreview.Parent = setPanel -- Children of ItemPreview local textPanel = Instance.new("Frame") textPanel.Name = "TextPanel" textPanel.BackgroundTransparency = 1 textPanel.Position = UDim2.new(0,0,0.45,0) textPanel.Size = UDim2.new(1,0,0.55,0) textPanel.ZIndex = 6 textPanel.Parent = itemPreview -- Children of TextPanel local rolloverText = Instance.new("TextLabel") rolloverText.Name = "RolloverText" rolloverText.BackgroundTransparency = 1 rolloverText.Size = UDim2.new(1,0,0,48) rolloverText.ZIndex = 6 rolloverText.Font = Enum.Font.ArialBold rolloverText.FontSize = Enum.FontSize.Size24 rolloverText.Text = "" rolloverText.TextColor3 = Color3.new(1,1,1) rolloverText.TextWrap = true rolloverText.TextXAlignment = Enum.TextXAlignment.Left rolloverText.TextYAlignment = Enum.TextYAlignment.Top rolloverText.Parent = textPanel local largePreview = Instance.new("ImageLabel") largePreview.Name = "LargePreview" largePreview.BackgroundTransparency = 1 largePreview.Image = "" largePreview.Size = UDim2.new(1,0,0,170) largePreview.ZIndex = 6 largePreview.Parent = itemPreview local sets = Instance.new("Frame") sets.Name = "Sets" sets.BackgroundTransparency = 1 sets.Position = UDim2.new(0,0,0,5) sets.Size = UDim2.new(0.23,0,1,-5) sets.ZIndex = 6 sets.Parent = setPanel -- Children of Sets local line = Instance.new("Frame") line.Name = "Line" line.BackgroundColor3 = Color3.new(1,1,1) line.BackgroundTransparency = 0.7 line.BorderSizePixel = 0 line.Position = UDim2.new(1,-3,0.06,0) line.Size = UDim2.new(0,3,0.9,0) line.ZIndex = 6 line.Parent = sets local setsLists, controlFrame = t.CreateTrueScrollingFrame() setsLists.Size = UDim2.new(1,-6,0.94,0) setsLists.Position = UDim2.new(0,0,0.06,0) setsLists.BackgroundTransparency = 1 setsLists.Name = "SetsLists" setsLists.ZIndex = 6 setsLists.Parent = sets drillDownSetZIndex(controlFrame, 7) local setsHeader = Instance.new("TextLabel") setsHeader.Name = "SetsHeader" setsHeader.BackgroundTransparency = 1 setsHeader.Size = UDim2.new(0,47,0,24) setsHeader.ZIndex = 6 setsHeader.Font = Enum.Font.ArialBold setsHeader.FontSize = Enum.FontSize.Size24 setsHeader.Text = "Sets" setsHeader.TextColor3 = Color3.new(1,1,1) setsHeader.TextXAlignment = Enum.TextXAlignment.Left setsHeader.TextYAlignment = Enum.TextYAlignment.Top setsHeader.Parent = sets local cancelButton = Instance.new("TextButton") cancelButton.Name = "CancelButton" cancelButton.Position = UDim2.new(1,-32,0,-2) cancelButton.Size = UDim2.new(0,34,0,34) cancelButton.Style = Enum.ButtonStyle.RobloxButtonDefault cancelButton.ZIndex = 6 cancelButton.Text = "" cancelButton.Modal = true cancelButton.Parent = setPanel -- Children of Cancel Button local cancelImage = Instance.new("ImageLabel") cancelImage.Name = "CancelImage" cancelImage.BackgroundTransparency = 1 cancelImage.Image = "https://www.roblox.com/asset/?id=54135717" cancelImage.Position = UDim2.new(0,-2,0,-2) cancelImage.Size = UDim2.new(0,16,0,16) cancelImage.ZIndex = 6 cancelImage.Parent = cancelButton return setGui end local function createSetButton(text) local setButton = Instance.new("TextButton") if text then setButton.Text = text else setButton.Text = "" end setButton.AutoButtonColor = false setButton.BackgroundTransparency = 1 setButton.BackgroundColor3 = Color3.new(1,1,1) setButton.BorderSizePixel = 0 setButton.Size = UDim2.new(1,-5,0,18) setButton.ZIndex = 6 setButton.Visible = false setButton.Font = Enum.Font.Arial setButton.FontSize = Enum.FontSize.Size18 setButton.TextColor3 = Color3.new(1,1,1) setButton.TextXAlignment = Enum.TextXAlignment.Left return setButton end local function buildSetButton(name, setId, setImageId, i, count) local button = createSetButton(name) button.Text = name button.Name = "SetButton" button.Visible = true local setValue = Instance.new("IntValue") setValue.Name = "SetId" setValue.Value = setId setValue.Parent = button local setName = Instance.new("StringValue") setName.Name = "SetName" setName.Value = name setName.Parent = button return button end local function processCategory(sets) local setButtons = {} local numSkipped = 0 for i = 1, #sets do if not showAdminCategories and sets[i].Name == "Beta" then numSkipped = numSkipped + 1 else setButtons[i - numSkipped] = buildSetButton(sets[i].Name, sets[i].CategoryId, sets[i].ImageAssetId, i - numSkipped, #sets) end end return setButtons end local function handleResize() wait() -- neccessary to insure heartbeat happened local itemPreview = setGui.SetPanel.ItemPreview itemPreview.LargePreview.Size = UDim2.new(1,0,0,itemPreview.AbsoluteSize.X) itemPreview.LargePreview.Position = UDim2.new(0.5,-itemPreview.LargePreview.AbsoluteSize.X/2,0,0) itemPreview.TextPanel.Position = UDim2.new(0,0,0,itemPreview.LargePreview.AbsoluteSize.Y) itemPreview.TextPanel.Size = UDim2.new(1,0,0,itemPreview.AbsoluteSize.Y - itemPreview.LargePreview.AbsoluteSize.Y) end local function makeInsertAssetButton() local insertAssetButtonExample = Instance.new("Frame") insertAssetButtonExample.Name = "InsertAssetButtonExample" insertAssetButtonExample.Position = UDim2.new(0,128,0,64) insertAssetButtonExample.Size = UDim2.new(0,64,0,64) insertAssetButtonExample.BackgroundTransparency = 1 insertAssetButtonExample.ZIndex = 6 insertAssetButtonExample.Visible = false local assetId = Instance.new("IntValue") assetId.Name = "AssetId" assetId.Value = 0 assetId.Parent = insertAssetButtonExample local assetName = Instance.new("StringValue") assetName.Name = "AssetName" assetName.Value = "" assetName.Parent = insertAssetButtonExample local button = Instance.new("TextButton") button.Name = "Button" button.Text = "" button.Style = Enum.ButtonStyle.RobloxButton button.Position = UDim2.new(0.025,0,0.025,0) button.Size = UDim2.new(0.95,0,0.95,0) button.ZIndex = 6 button.Parent = insertAssetButtonExample local buttonImage = Instance.new("ImageLabel") buttonImage.Name = "ButtonImage" buttonImage.Image = "" buttonImage.Position = UDim2.new(0,-7,0,-7) buttonImage.Size = UDim2.new(1,14,1,14) buttonImage.BackgroundTransparency = 1 buttonImage.ZIndex = 7 buttonImage.Parent = button local configIcon = buttonImage:clone() configIcon.Name = "ConfigIcon" configIcon.Visible = false configIcon.Position = UDim2.new(1,-23,1,-24) configIcon.Size = UDim2.new(0,16,0,16) configIcon.Image = "" configIcon.ZIndex = 6 configIcon.Parent = insertAssetButtonExample return insertAssetButtonExample end local function showLargePreview(insertButton) if insertButton:FindFirstChild("AssetId") then delay(0,function() game:GetService("ContentProvider"):Preload(LargeThumbnailUrl .. tostring(insertButton.AssetId.Value)) setGui.SetPanel.ItemPreview.LargePreview.Image = LargeThumbnailUrl .. tostring(insertButton.AssetId.Value) end) end if insertButton:FindFirstChild("AssetName") then setGui.SetPanel.ItemPreview.TextPanel.RolloverText.Text = insertButton.AssetName.Value end end local function selectTerrainShape(shape) if currTerrainDropDownFrame then objectSelected(tostring(currTerrainDropDownFrame.AssetName.Value), tonumber(currTerrainDropDownFrame.AssetId.Value), shape) end end local function createTerrainTypeButton(name, parent) local dropDownTextButton = Instance.new("TextButton") dropDownTextButton.Name = name .. "Button" dropDownTextButton.Font = Enum.Font.ArialBold dropDownTextButton.FontSize = Enum.FontSize.Size14 dropDownTextButton.BorderSizePixel = 0 dropDownTextButton.TextColor3 = Color3.new(1,1,1) dropDownTextButton.Text = name dropDownTextButton.TextXAlignment = Enum.TextXAlignment.Left dropDownTextButton.BackgroundTransparency = 1 dropDownTextButton.ZIndex = parent.ZIndex + 1 dropDownTextButton.Size = UDim2.new(0,parent.Size.X.Offset - 2,0,16) dropDownTextButton.Position = UDim2.new(0,1,0,0) dropDownTextButton.MouseEnter:connect(function() dropDownTextButton.BackgroundTransparency = 0 dropDownTextButton.TextColor3 = Color3.new(0,0,0) end) dropDownTextButton.MouseLeave:connect(function() dropDownTextButton.BackgroundTransparency = 1 dropDownTextButton.TextColor3 = Color3.new(1,1,1) end) dropDownTextButton.MouseButton1Click:connect(function() dropDownTextButton.BackgroundTransparency = 1 dropDownTextButton.TextColor3 = Color3.new(1,1,1) if dropDownTextButton.Parent and dropDownTextButton.Parent:IsA("GuiObject") then dropDownTextButton.Parent.Visible = false end selectTerrainShape(terrainShapeMap[dropDownTextButton.Text]) end) return dropDownTextButton end local function createTerrainDropDownMenu(zIndex) local dropDown = Instance.new("Frame") dropDown.Name = "TerrainDropDown" dropDown.BackgroundColor3 = Color3.new(0,0,0) dropDown.BorderColor3 = Color3.new(1,0,0) dropDown.Size = UDim2.new(0,200,0,0) dropDown.Visible = false dropDown.ZIndex = zIndex dropDown.Parent = setGui for i = 1, #terrainShapes do local shapeButton = createTerrainTypeButton(terrainShapes[i],dropDown) shapeButton.Position = UDim2.new(0,1,0,(i - 1) * (shapeButton.Size.Y.Offset)) shapeButton.Parent = dropDown dropDown.Size = UDim2.new(0,200,0,dropDown.Size.Y.Offset + (shapeButton.Size.Y.Offset)) end dropDown.MouseLeave:connect(function() dropDown.Visible = false end) end local function createDropDownMenuButton(parent) local dropDownButton = Instance.new("ImageButton") dropDownButton.Name = "DropDownButton" dropDownButton.Image = "https://www.roblox.com/asset/?id=67581509" dropDownButton.BackgroundTransparency = 1 dropDownButton.Size = UDim2.new(0,16,0,16) dropDownButton.Position = UDim2.new(1,-24,0,6) dropDownButton.ZIndex = parent.ZIndex + 2 dropDownButton.Parent = parent if not setGui:FindFirstChild("TerrainDropDown") then createTerrainDropDownMenu(8) end dropDownButton.MouseButton1Click:connect(function() setGui.TerrainDropDown.Visible = true setGui.TerrainDropDown.Position = UDim2.new(0,parent.AbsolutePosition.X,0,parent.AbsolutePosition.Y) currTerrainDropDownFrame = parent end) end local function buildInsertButton() local insertButton = makeInsertAssetButton() insertButton.Name = "InsertAssetButton" insertButton.Visible = true if Data.Category[Data.CurrentCategory].SetName == "High Scalability" then createDropDownMenuButton(insertButton) end local lastEnter = nil local mouseEnterCon = insertButton.MouseEnter:connect(function() lastEnter = insertButton delay(0.1,function() if lastEnter == insertButton then showLargePreview(insertButton) end end) end) return insertButton, mouseEnterCon end local function realignButtonGrid(columns) local x = 0 local y = 0 for i = 1, #insertButtons do insertButtons[i].Position = UDim2.new(0, buttonWidth * x, 0, buttonHeight * y) x = x + 1 if x >= columns then x = 0 y = y + 1 end end end local function setInsertButtonImageBehavior(insertFrame, visible, name, assetId) if visible then insertFrame.AssetName.Value = name insertFrame.AssetId.Value = assetId local newImageUrl = SmallThumbnailUrl .. assetId if newImageUrl ~= insertFrame.Button.ButtonImage.Image then delay(0,function() game:GetService("ContentProvider"):Preload(SmallThumbnailUrl .. assetId) if insertFrame:findFirstChild("Button") then insertFrame.Button.ButtonImage.Image = SmallThumbnailUrl .. assetId end end) end table.insert(insertButtonCons, insertFrame.Button.MouseButton1Click:connect(function() -- special case for water, show water selection gui local isWaterSelected = (name == "Water") and (Data.Category[Data.CurrentCategory].SetName == "High Scalability") waterGui.Visible = isWaterSelected if isWaterSelected then objectSelected(name, tonumber(assetId), nil) else objectSelected(name, tonumber(assetId)) end end) ) insertFrame.Visible = true else insertFrame.Visible = false end end local function loadSectionOfItems(setGui, rows, columns) local pageSize = rows * columns if arrayPosition > #contents then return end local origArrayPos = arrayPosition local yCopy = 0 for i = 1, pageSize + 1 do if arrayPosition >= #contents + 1 then break end local buttonCon insertButtons[arrayPosition], buttonCon = buildInsertButton() table.insert(insertButtonCons,buttonCon) insertButtons[arrayPosition].Parent = setGui.SetPanel.ItemsFrame arrayPosition = arrayPosition + 1 end realignButtonGrid(columns) local indexCopy = origArrayPos for index = origArrayPos, arrayPosition do if insertButtons[index] then if contents[index] then -- we don't want water to have a drop down button if contents[index].Name == "Water" then if Data.Category[Data.CurrentCategory].SetName == "High Scalability" then insertButtons[index]:FindFirstChild("DropDownButton",true):Destroy() end end local assetId if useAssetVersionId then assetId = contents[index].AssetVersionId else assetId = contents[index].AssetId end setInsertButtonImageBehavior(insertButtons[index], true, contents[index].Name, assetId) else break end else break end indexCopy = index end end local function setSetIndex() Data.Category[Data.CurrentCategory].Index = 0 rows = 7 columns = math.floor(setGui.SetPanel.ItemsFrame.AbsoluteSize.X/buttonWidth) contents = Data.Category[Data.CurrentCategory].Contents if contents then -- destroy our buttons and their connections for i = 1, #insertButtons do insertButtons[i]:Destroy() end for i = 1, #insertButtonCons do if insertButtonCons[i] then insertButtonCons[i]:disconnect() end end insertButtonCons = {} insertButtons = {} arrayPosition = 1 loadSectionOfItems(setGui, rows, columns) end end local function selectSet(button, setName, setId, setIndex) if button and Data.Category[Data.CurrentCategory] ~= nil then if button ~= Data.Category[Data.CurrentCategory].Button then Data.Category[Data.CurrentCategory].Button = button if SetCache[setId] == nil then SetCache[setId] = game:GetService("InsertService"):GetCollection(setId) end Data.Category[Data.CurrentCategory].Contents = SetCache[setId] Data.Category[Data.CurrentCategory].SetName = setName Data.Category[Data.CurrentCategory].SetId = setId end setSetIndex() end end local function selectCategoryPage(buttons, page) if buttons ~= Data.CurrentCategory then if Data.CurrentCategory then for key, button in pairs(Data.CurrentCategory) do button.Visible = false end end Data.CurrentCategory = buttons if Data.Category[Data.CurrentCategory] == nil then Data.Category[Data.CurrentCategory] = {} if #buttons > 0 then selectSet(buttons[1], buttons[1].SetName.Value, buttons[1].SetId.Value, 0) end else Data.Category[Data.CurrentCategory].Button = nil selectSet(Data.Category[Data.CurrentCategory].ButtonFrame, Data.Category[Data.CurrentCategory].SetName, Data.Category[Data.CurrentCategory].SetId, Data.Category[Data.CurrentCategory].Index) end end end local function selectCategory(category) selectCategoryPage(category, 0) end local function resetAllSetButtonSelection() local setButtons = setGui.SetPanel.Sets.SetsLists:GetChildren() for i = 1, #setButtons do if setButtons[i]:IsA("TextButton") then setButtons[i].Selected = false setButtons[i].BackgroundTransparency = 1 setButtons[i].TextColor3 = Color3.new(1,1,1) setButtons[i].BackgroundColor3 = Color3.new(1,1,1) end end end local function populateSetsFrame() local currRow = 0 for i = 1, #userCategoryButtons do local button = userCategoryButtons[i] button.Visible = true button.Position = UDim2.new(0,5,0,currRow * button.Size.Y.Offset) button.Parent = setGui.SetPanel.Sets.SetsLists if i == 1 then -- we will have this selected by default, so show it button.Selected = true button.BackgroundColor3 = Color3.new(0,204/255,0) button.TextColor3 = Color3.new(0,0,0) button.BackgroundTransparency = 0 end button.MouseEnter:connect(function() if not button.Selected then button.BackgroundTransparency = 0 button.TextColor3 = Color3.new(0,0,0) end end) button.MouseLeave:connect(function() if not button.Selected then button.BackgroundTransparency = 1 button.TextColor3 = Color3.new(1,1,1) end end) button.MouseButton1Click:connect(function() resetAllSetButtonSelection() button.Selected = not button.Selected button.BackgroundColor3 = Color3.new(0,204/255,0) button.TextColor3 = Color3.new(0,0,0) button.BackgroundTransparency = 0 selectSet(button, button.Text, userCategoryButtons[i].SetId.Value, 0) end) currRow = currRow + 1 end local buttons = setGui.SetPanel.Sets.SetsLists:GetChildren() -- set first category as loaded for default if buttons then for i = 1, #buttons do if buttons[i]:IsA("TextButton") then selectSet(buttons[i], buttons[i].Text, userCategoryButtons[i].SetId.Value, 0) selectCategory(userCategoryButtons) break end end end end setGui = createSetGui() waterGui, waterTypeChangedEvent = createWaterGui() waterGui.Position = UDim2.new(0,55,0,0) waterGui.Parent = setGui setGui.Changed:connect(function(prop) -- this resizes the preview image to always be the right size if prop == "AbsoluteSize" then handleResize() setSetIndex() end end) local scrollFrame, controlFrame = t.CreateTrueScrollingFrame() scrollFrame.Size = UDim2.new(0.54,0,0.85,0) scrollFrame.Position = UDim2.new(0.24,0,0.085,0) scrollFrame.Name = "ItemsFrame" scrollFrame.ZIndex = 6 scrollFrame.Parent = setGui.SetPanel scrollFrame.BackgroundTransparency = 1 drillDownSetZIndex(controlFrame,7) controlFrame.Parent = setGui.SetPanel controlFrame.Position = UDim2.new(0.76, 5, 0, 0) local debounce = false controlFrame.ScrollBottom.Changed:connect(function(prop) if controlFrame.ScrollBottom.Value == true then if debounce then return end debounce = true loadSectionOfItems(setGui, rows, columns) debounce = false end end) local userData = {} for id = 1, #userIdsForSets do local newUserData = game:GetService("InsertService"):GetUserSets(userIdsForSets[id]) if newUserData and #newUserData > 2 then -- start at #3 to skip over My Decals and My Models for each account for category = 3, #newUserData do if newUserData[category].Name == "High Scalability" then -- we want high scalability parts to show first table.insert(userData,1,newUserData[category]) else table.insert(userData, newUserData[category]) end end end end if userData then userCategoryButtons = processCategory(userData) end rows = math.floor(setGui.SetPanel.ItemsFrame.AbsoluteSize.Y/buttonHeight) columns = math.floor(setGui.SetPanel.ItemsFrame.AbsoluteSize.X/buttonWidth) populateSetsFrame() setGui.SetPanel.CancelButton.MouseButton1Click:connect(function() setGui.SetPanel.Visible = false if dialogClosed then dialogClosed() end end) local setVisibilityFunction = function(visible) if visible then setGui.SetPanel.Visible = true else setGui.SetPanel.Visible = false end end local getVisibilityFunction = function() if setGui then if setGui:FindFirstChild("SetPanel") then return setGui.SetPanel.Visible end end return false end return setGui, setVisibilityFunction, getVisibilityFunction, waterTypeChangedEvent end t.CreateTerrainMaterialSelector = function(size,position) local terrainMaterialSelectionChanged = Instance.new("BindableEvent") terrainMaterialSelectionChanged.Name = "TerrainMaterialSelectionChanged" local selectedButton = nil local frame = Instance.new("Frame") frame.Name = "TerrainMaterialSelector" if size then frame.Size = size else frame.Size = UDim2.new(0, 245, 0, 230) end if position then frame.Position = position end frame.BorderSizePixel = 0 frame.BackgroundColor3 = Color3.new(0,0,0) frame.Active = true terrainMaterialSelectionChanged.Parent = frame local waterEnabled = true -- todo: turn this on when water is ready local materialToImageMap = {} local materialNames = {"Grass", "Sand", "Brick", "Granite", "Asphalt", "Iron", "Aluminum", "Gold", "Plank", "Log", "Gravel", "Cinder Block", "Stone Wall", "Concrete", "Plastic (red)", "Plastic (blue)"} if waterEnabled then table.insert(materialNames,"Water") end local currentMaterial = 1 function getEnumFromName(choice) if choice == "Grass" then return 1 end if choice == "Sand" then return 2 end if choice == "Erase" then return 0 end if choice == "Brick" then return 3 end if choice == "Granite" then return 4 end if choice == "Asphalt" then return 5 end if choice == "Iron" then return 6 end if choice == "Aluminum" then return 7 end if choice == "Gold" then return 8 end if choice == "Plank" then return 9 end if choice == "Log" then return 10 end if choice == "Gravel" then return 11 end if choice == "Cinder Block" then return 12 end if choice == "Stone Wall" then return 13 end if choice == "Concrete" then return 14 end if choice == "Plastic (red)" then return 15 end if choice == "Plastic (blue)" then return 16 end if choice == "Water" then return 17 end end function getNameFromEnum(choice) if choice == Enum.CellMaterial.Grass or choice == 1 then return "Grass"end if choice == Enum.CellMaterial.Sand or choice == 2 then return "Sand" end if choice == Enum.CellMaterial.Empty or choice == 0 then return "Erase" end if choice == Enum.CellMaterial.Brick or choice == 3 then return "Brick" end if choice == Enum.CellMaterial.Granite or choice == 4 then return "Granite" end if choice == Enum.CellMaterial.Asphalt or choice == 5 then return "Asphalt" end if choice == Enum.CellMaterial.Iron or choice == 6 then return "Iron" end if choice == Enum.CellMaterial.Aluminum or choice == 7 then return "Aluminum" end if choice == Enum.CellMaterial.Gold or choice == 8 then return "Gold" end if choice == Enum.CellMaterial.WoodPlank or choice == 9 then return "Plank" end if choice == Enum.CellMaterial.WoodLog or choice == 10 then return "Log" end if choice == Enum.CellMaterial.Gravel or choice == 11 then return "Gravel" end if choice == Enum.CellMaterial.CinderBlock or choice == 12 then return "Cinder Block" end if choice == Enum.CellMaterial.MossyStone or choice == 13 then return "Stone Wall" end if choice == Enum.CellMaterial.Cement or choice == 14 then return "Concrete" end if choice == Enum.CellMaterial.RedPlastic or choice == 15 then return "Plastic (red)" end if choice == Enum.CellMaterial.BluePlastic or choice == 16 then return "Plastic (blue)" end if waterEnabled then if choice == Enum.CellMaterial.Water or choice == 17 then return "Water" end end end local function updateMaterialChoice(choice) currentMaterial = getEnumFromName(choice) terrainMaterialSelectionChanged:Fire(currentMaterial) end -- we so need a better way to do this for i,v in pairs(materialNames) do materialToImageMap[v] = {} if v == "Grass" then materialToImageMap[v].Regular = "https://www.roblox.com/asset/?id=56563112" elseif v == "Sand" then materialToImageMap[v].Regular = "https://www.roblox.com/asset/?id=62356652" elseif v == "Brick" then materialToImageMap[v].Regular = "https://www.roblox.com/asset/?id=65961537" elseif v == "Granite" then materialToImageMap[v].Regular = "https://www.roblox.com/asset/?id=67532153" elseif v == "Asphalt" then materialToImageMap[v].Regular = "https://www.roblox.com/asset/?id=67532038" elseif v == "Iron" then materialToImageMap[v].Regular = "https://www.roblox.com/asset/?id=67532093" elseif v == "Aluminum" then materialToImageMap[v].Regular = "https://www.roblox.com/asset/?id=67531995" elseif v == "Gold" then materialToImageMap[v].Regular = "https://www.roblox.com/asset/?id=67532118" elseif v == "Plastic (red)" then materialToImageMap[v].Regular = "https://www.roblox.com/asset/?id=67531848" elseif v == "Plastic (blue)" then materialToImageMap[v].Regular = "https://www.roblox.com/asset/?id=67531924" elseif v == "Plank" then materialToImageMap[v].Regular = "https://www.roblox.com/asset/?id=67532015" elseif v == "Log" then materialToImageMap[v].Regular = "https://www.roblox.com/asset/?id=67532051" elseif v == "Gravel" then materialToImageMap[v].Regular = "https://www.roblox.com/asset/?id=67532206" elseif v == "Cinder Block" then materialToImageMap[v].Regular = "https://www.roblox.com/asset/?id=67532103" elseif v == "Stone Wall" then materialToImageMap[v].Regular = "https://www.roblox.com/asset/?id=67531804" elseif v == "Concrete" then materialToImageMap[v].Regular = "https://www.roblox.com/asset/?id=67532059" elseif v == "Water" then materialToImageMap[v].Regular = "https://www.roblox.com/asset/?id=81407474" else materialToImageMap[v].Regular = "https://www.roblox.com/asset/?id=66887593" -- fill in the rest here!! end end local scrollFrame, scrollUp, scrollDown, recalculateScroll = t.CreateScrollingFrame(nil,"grid") scrollFrame.Size = UDim2.new(0.85,0,1,0) scrollFrame.Position = UDim2.new(0,0,0,0) scrollFrame.Parent = frame scrollUp.Parent = frame scrollUp.Visible = true scrollUp.Position = UDim2.new(1,-19,0,0) scrollDown.Parent = frame scrollDown.Visible = true scrollDown.Position = UDim2.new(1,-19,1,-17) local function goToNewMaterial(buttonWrap, materialName) updateMaterialChoice(materialName) buttonWrap.BackgroundTransparency = 0 selectedButton.BackgroundTransparency = 1 selectedButton = buttonWrap end local function createMaterialButton(name) local buttonWrap = Instance.new("TextButton") buttonWrap.Text = "" buttonWrap.Size = UDim2.new(0,32,0,32) buttonWrap.BackgroundColor3 = Color3.new(1,1,1) buttonWrap.BorderSizePixel = 0 buttonWrap.BackgroundTransparency = 1 buttonWrap.AutoButtonColor = false buttonWrap.Name = tostring(name) local imageButton = Instance.new("ImageButton") imageButton.AutoButtonColor = false imageButton.BackgroundTransparency = 1 imageButton.Size = UDim2.new(0,30,0,30) imageButton.Position = UDim2.new(0,1,0,1) imageButton.Name = tostring(name) imageButton.Parent = buttonWrap imageButton.Image = materialToImageMap[name].Regular local enumType = Instance.new("NumberValue") enumType.Name = "EnumType" enumType.Parent = buttonWrap enumType.Value = 0 imageButton.MouseEnter:connect(function() buttonWrap.BackgroundTransparency = 0 end) imageButton.MouseLeave:connect(function() if selectedButton ~= buttonWrap then buttonWrap.BackgroundTransparency = 1 end end) imageButton.MouseButton1Click:connect(function() if selectedButton ~= buttonWrap then goToNewMaterial(buttonWrap, tostring(name)) end end) return buttonWrap end for i = 1, #materialNames do local imageButton = createMaterialButton(materialNames[i]) if materialNames[i] == "Grass" then -- always start with grass as the default selectedButton = imageButton imageButton.BackgroundTransparency = 0 end imageButton.Parent = scrollFrame end local forceTerrainMaterialSelection = function(newMaterialType) if not newMaterialType then return end if currentMaterial == newMaterialType then return end local matName = getNameFromEnum(newMaterialType) local buttons = scrollFrame:GetChildren() for i = 1, #buttons do if buttons[i].Name == "Plastic (blue)" and matName == "Plastic (blue)" then goToNewMaterial(buttons[i],matName) return end if buttons[i].Name == "Plastic (red)" and matName == "Plastic (red)" then goToNewMaterial(buttons[i],matName) return end if string.find(buttons[i].Name, matName) then goToNewMaterial(buttons[i],matName) return end end end frame.Changed:connect(function ( prop ) if prop == "AbsoluteSize" then recalculateScroll() end end) recalculateScroll() return frame, terrainMaterialSelectionChanged, forceTerrainMaterialSelection end t.CreateLoadingFrame = function(name,size,position) game:GetService("ContentProvider"):Preload("https://www.roblox.com/asset/?id=35238053") local loadingFrame = Instance.new("Frame") loadingFrame.Name = "LoadingFrame" loadingFrame.Style = Enum.FrameStyle.RobloxRound if size then loadingFrame.Size = size else loadingFrame.Size = UDim2.new(0,300,0,160) end if position then loadingFrame.Position = position else loadingFrame.Position = UDim2.new(0.5, -150, 0.5,-80) end local loadingBar = Instance.new("Frame") loadingBar.Name = "LoadingBar" loadingBar.BackgroundColor3 = Color3.new(0,0,0) loadingBar.BorderColor3 = Color3.new(79/255,79/255,79/255) loadingBar.Position = UDim2.new(0,0,0,41) loadingBar.Size = UDim2.new(1,0,0,30) loadingBar.Parent = loadingFrame local loadingGreenBar = Instance.new("ImageLabel") loadingGreenBar.Name = "LoadingGreenBar" loadingGreenBar.Image = "https://www.roblox.com/asset/?id=35238053" loadingGreenBar.Position = UDim2.new(0,0,0,0) loadingGreenBar.Size = UDim2.new(0,0,1,0) loadingGreenBar.Visible = false loadingGreenBar.Parent = loadingBar local loadingPercent = Instance.new("TextLabel") loadingPercent.Name = "LoadingPercent" loadingPercent.BackgroundTransparency = 1 loadingPercent.Position = UDim2.new(0,0,1,0) loadingPercent.Size = UDim2.new(1,0,0,14) loadingPercent.Font = Enum.Font.Arial loadingPercent.Text = "0%" loadingPercent.FontSize = Enum.FontSize.Size14 loadingPercent.TextColor3 = Color3.new(1,1,1) loadingPercent.Parent = loadingBar local cancelButton = Instance.new("TextButton") cancelButton.Name = "CancelButton" cancelButton.Position = UDim2.new(0.5,-60,1,-40) cancelButton.Size = UDim2.new(0,120,0,40) cancelButton.Font = Enum.Font.Arial cancelButton.FontSize = Enum.FontSize.Size18 cancelButton.TextColor3 = Color3.new(1,1,1) cancelButton.Text = "Cancel" cancelButton.Style = Enum.ButtonStyle.RobloxButton cancelButton.Parent = loadingFrame local loadingName = Instance.new("TextLabel") loadingName.Name = "loadingName" loadingName.BackgroundTransparency = 1 loadingName.Size = UDim2.new(1,0,0,18) loadingName.Position = UDim2.new(0,0,0,2) loadingName.Font = Enum.Font.Arial loadingName.Text = name loadingName.TextColor3 = Color3.new(1,1,1) loadingName.TextStrokeTransparency = 1 loadingName.FontSize = Enum.FontSize.Size18 loadingName.Parent = loadingFrame local cancelButtonClicked = Instance.new("BindableEvent") cancelButtonClicked.Name = "CancelButtonClicked" cancelButtonClicked.Parent = cancelButton cancelButton.MouseButton1Click:connect(function() cancelButtonClicked:Fire() end) local updateLoadingGuiPercent = function(percent, tweenAction, tweenLength) if percent and type(percent) ~= "number" then error("updateLoadingGuiPercent expects number as argument, got",type(percent),"instead") end local newSize = nil if percent < 0 then newSize = UDim2.new(0,0,1,0) elseif percent > 1 then newSize = UDim2.new(1,0,1,0) else newSize = UDim2.new(percent,0,1,0) end if tweenAction then if not tweenLength then error("updateLoadingGuiPercent is set to tween new percentage, but got no tween time length! Please pass this in as third argument") end if (newSize.X.Scale > 0) then loadingGreenBar.Visible = true loadingGreenBar:TweenSize( newSize, Enum.EasingDirection.Out, Enum.EasingStyle.Quad, tweenLength, true) else loadingGreenBar:TweenSize( newSize, Enum.EasingDirection.Out, Enum.EasingStyle.Quad, tweenLength, true, function() if (newSize.X.Scale < 0) then loadingGreenBar.Visible = false end end) end else loadingGreenBar.Size = newSize loadingGreenBar.Visible = (newSize.X.Scale > 0) end end loadingGreenBar.Changed:connect(function(prop) if prop == "Size" then loadingPercent.Text = tostring( math.ceil(loadingGreenBar.Size.X.Scale * 100) ) .. "%" end end) return loadingFrame, updateLoadingGuiPercent, cancelButtonClicked end t.CreatePluginFrame = function (name,size,position,scrollable,parent) local function createMenuButton(size,position,text,fontsize,name,parent) local button = Instance.new("TextButton") button.AutoButtonColor = false button.Name = name button.BackgroundTransparency = 1 button.Position = position button.Size = size button.Font = Enum.Font.ArialBold button.FontSize = fontsize button.Text = text button.TextColor3 = Color3.new(1,1,1) button.BorderSizePixel = 0 button.BackgroundColor3 = Color3.new(20/255,20/255,20/255) button.Parent = parent button.MouseEnter:connect(function ( ) if button.Selected then return end button.BackgroundTransparency = 0 end) button.MouseLeave:connect(function ( ) if button.Selected then return end button.BackgroundTransparency = 1 end) return button end local dragBar = Instance.new("Frame") dragBar.Name = tostring(name) .. "DragBar" dragBar.BackgroundColor3 = Color3.new(39/255,39/255,39/255) dragBar.BorderColor3 = Color3.new(0,0,0) if size then dragBar.Size = UDim2.new(size.X.Scale,size.X.Offset,0,20) + UDim2.new(0,20,0,0) else dragBar.Size = UDim2.new(0,183,0,20) end if position then dragBar.Position = position end dragBar.Active = true dragBar.Draggable = true dragBar.Parent = parent dragBar.MouseEnter:connect(function ( ) dragBar.BackgroundColor3 = Color3.new(49/255,49/255,49/255) end) dragBar.MouseLeave:connect(function ( ) dragBar.BackgroundColor3 = Color3.new(39/255,39/255,39/255) end) -- plugin name label local pluginNameLabel = Instance.new("TextLabel") pluginNameLabel.Name = "BarNameLabel" pluginNameLabel.Text = " " .. tostring(name) pluginNameLabel.TextColor3 = Color3.new(1,1,1) pluginNameLabel.TextStrokeTransparency = 0 pluginNameLabel.Size = UDim2.new(1,0,1,0) pluginNameLabel.Font = Enum.Font.ArialBold pluginNameLabel.FontSize = Enum.FontSize.Size18 pluginNameLabel.TextXAlignment = Enum.TextXAlignment.Left pluginNameLabel.BackgroundTransparency = 1 pluginNameLabel.Parent = dragBar -- close button local closeButton = createMenuButton(UDim2.new(0,15,0,17),UDim2.new(1,-16,0.5,-8),"X",Enum.FontSize.Size14,"CloseButton",dragBar) local closeEvent = Instance.new("BindableEvent") closeEvent.Name = "CloseEvent" closeEvent.Parent = closeButton closeButton.MouseButton1Click:connect(function () closeEvent:Fire() closeButton.BackgroundTransparency = 1 end) -- help button local helpButton = createMenuButton(UDim2.new(0,15,0,17),UDim2.new(1,-51,0.5,-8),"?",Enum.FontSize.Size14,"HelpButton",dragBar) local helpFrame = Instance.new("Frame") helpFrame.Name = "HelpFrame" helpFrame.BackgroundColor3 = Color3.new(0,0,0) helpFrame.Size = UDim2.new(0,300,0,552) helpFrame.Position = UDim2.new(1,5,0,0) helpFrame.Active = true helpFrame.BorderSizePixel = 0 helpFrame.Visible = false helpFrame.Parent = dragBar helpButton.MouseButton1Click:connect(function( ) helpFrame.Visible = not helpFrame.Visible if helpFrame.Visible then helpButton.Selected = true helpButton.BackgroundTransparency = 0 local screenGui = getLayerCollectorAncestor(helpFrame) if screenGui then if helpFrame.AbsolutePosition.X + helpFrame.AbsoluteSize.X > screenGui.AbsoluteSize.X then --position on left hand side helpFrame.Position = UDim2.new(0,-5 - helpFrame.AbsoluteSize.X,0,0) else -- position on right hand side helpFrame.Position = UDim2.new(1,5,0,0) end else helpFrame.Position = UDim2.new(1,5,0,0) end else helpButton.Selected = false helpButton.BackgroundTransparency = 1 end end) local minimizeButton = createMenuButton(UDim2.new(0,16,0,17),UDim2.new(1,-34,0.5,-8),"-",Enum.FontSize.Size14,"MinimizeButton",dragBar) minimizeButton.TextYAlignment = Enum.TextYAlignment.Top local minimizeFrame = Instance.new("Frame") minimizeFrame.Name = "MinimizeFrame" minimizeFrame.BackgroundColor3 = Color3.new(73/255,73/255,73/255) minimizeFrame.BorderColor3 = Color3.new(0,0,0) minimizeFrame.Position = UDim2.new(0,0,1,0) if size then minimizeFrame.Size = UDim2.new(size.X.Scale,size.X.Offset,0,50) + UDim2.new(0,20,0,0) else minimizeFrame.Size = UDim2.new(0,183,0,50) end minimizeFrame.Visible = false minimizeFrame.Parent = dragBar local minimizeBigButton = Instance.new("TextButton") minimizeBigButton.Position = UDim2.new(0.5,-50,0.5,-20) minimizeBigButton.Name = "MinimizeButton" minimizeBigButton.Size = UDim2.new(0,100,0,40) minimizeBigButton.Style = Enum.ButtonStyle.RobloxButton minimizeBigButton.Font = Enum.Font.ArialBold minimizeBigButton.FontSize = Enum.FontSize.Size18 minimizeBigButton.TextColor3 = Color3.new(1,1,1) minimizeBigButton.Text = "Show" minimizeBigButton.Parent = minimizeFrame local separatingLine = Instance.new("Frame") separatingLine.Name = "SeparatingLine" separatingLine.BackgroundColor3 = Color3.new(115/255,115/255,115/255) separatingLine.BorderSizePixel = 0 separatingLine.Position = UDim2.new(1,-18,0.5,-7) separatingLine.Size = UDim2.new(0,1,0,14) separatingLine.Parent = dragBar local otherSeparatingLine = separatingLine:clone() otherSeparatingLine.Position = UDim2.new(1,-35,0.5,-7) otherSeparatingLine.Parent = dragBar local widgetContainer = Instance.new("Frame") widgetContainer.Name = "WidgetContainer" widgetContainer.BackgroundTransparency = 1 widgetContainer.Position = UDim2.new(0,0,1,0) widgetContainer.BorderColor3 = Color3.new(0,0,0) if not scrollable then widgetContainer.BackgroundTransparency = 0 widgetContainer.BackgroundColor3 = Color3.new(72/255,72/255,72/255) end widgetContainer.Parent = dragBar if size then if scrollable then widgetContainer.Size = size else widgetContainer.Size = UDim2.new(0,dragBar.AbsoluteSize.X,size.Y.Scale,size.Y.Offset) end else if scrollable then widgetContainer.Size = UDim2.new(0,163,0,400) else widgetContainer.Size = UDim2.new(0,dragBar.AbsoluteSize.X,0,400) end end if position then widgetContainer.Position = position + UDim2.new(0,0,0,20) end local frame,control,verticalDragger = nil if scrollable then --frame for widgets frame,control = t.CreateTrueScrollingFrame() frame.Size = UDim2.new(1, 0, 1, 0) frame.BackgroundColor3 = Color3.new(72/255,72/255,72/255) frame.BorderColor3 = Color3.new(0,0,0) frame.Active = true frame.Parent = widgetContainer control.Parent = dragBar control.BackgroundColor3 = Color3.new(72/255,72/255,72/255) control.BorderSizePixel = 0 control.BackgroundTransparency = 0 control.Position = UDim2.new(1,-21,1,1) if size then control.Size = UDim2.new(0,21,size.Y.Scale,size.Y.Offset) else control.Size = UDim2.new(0,21,0,400) end control:FindFirstChild("ScrollDownButton").Position = UDim2.new(0,0,1,-20) local fakeLine = Instance.new("Frame") fakeLine.Name = "FakeLine" fakeLine.BorderSizePixel = 0 fakeLine.BackgroundColor3 = Color3.new(0,0,0) fakeLine.Size = UDim2.new(0,1,1,1) fakeLine.Position = UDim2.new(1,0,0,0) fakeLine.Parent = control verticalDragger = Instance.new("TextButton") verticalDragger.ZIndex = 2 verticalDragger.AutoButtonColor = false verticalDragger.Name = "VerticalDragger" verticalDragger.BackgroundColor3 = Color3.new(50/255,50/255,50/255) verticalDragger.BorderColor3 = Color3.new(0,0,0) verticalDragger.Size = UDim2.new(1,20,0,20) verticalDragger.Position = UDim2.new(0,0,1,0) verticalDragger.Active = true verticalDragger.Text = "" verticalDragger.Parent = widgetContainer local scrubFrame = Instance.new("Frame") scrubFrame.Name = "ScrubFrame" scrubFrame.BackgroundColor3 = Color3.new(1,1,1) scrubFrame.BorderSizePixel = 0 scrubFrame.Position = UDim2.new(0.5,-5,0.5,0) scrubFrame.Size = UDim2.new(0,10,0,1) scrubFrame.ZIndex = 5 scrubFrame.Parent = verticalDragger local scrubTwo = scrubFrame:clone() scrubTwo.Position = UDim2.new(0.5,-5,0.5,-2) scrubTwo.Parent = verticalDragger local scrubThree = scrubFrame:clone() scrubThree.Position = UDim2.new(0.5,-5,0.5,2) scrubThree.Parent = verticalDragger local areaSoak = Instance.new("TextButton",getLayerCollectorAncestor(parent)) areaSoak.Name = "AreaSoak" areaSoak.Size = UDim2.new(1,0,1,0) areaSoak.BackgroundTransparency = 1 areaSoak.BorderSizePixel = 0 areaSoak.Text = "" areaSoak.ZIndex = 10 areaSoak.Visible = false areaSoak.Active = true local draggingVertical = false local startYPos = nil verticalDragger.MouseEnter:connect(function () verticalDragger.BackgroundColor3 = Color3.new(60/255,60/255,60/255) end) verticalDragger.MouseLeave:connect(function () verticalDragger.BackgroundColor3 = Color3.new(50/255,50/255,50/255) end) verticalDragger.MouseButton1Down:connect(function(x,y) draggingVertical = true areaSoak.Visible = true startYPos = y end) areaSoak.MouseButton1Up:connect(function ( ) draggingVertical = false areaSoak.Visible = false end) areaSoak.MouseMoved:connect(function(x,y) if not draggingVertical then return end local yDelta = y - startYPos if not control.ScrollDownButton.Visible and yDelta > 0 then return end if (widgetContainer.Size.Y.Offset + yDelta) < 150 then widgetContainer.Size = UDim2.new(widgetContainer.Size.X.Scale, widgetContainer.Size.X.Offset,widgetContainer.Size.Y.Scale,150) control.Size = UDim2.new (0,21,0,150) return end startYPos = y if widgetContainer.Size.Y.Offset + yDelta >= 0 then widgetContainer.Size = UDim2.new(widgetContainer.Size.X.Scale, widgetContainer.Size.X.Offset,widgetContainer.Size.Y.Scale,widgetContainer.Size.Y.Offset + yDelta) control.Size = UDim2.new(0,21,0,control.Size.Y.Offset + yDelta ) end end) end local function switchMinimize() minimizeFrame.Visible = not minimizeFrame.Visible if scrollable then frame.Visible = not frame.Visible verticalDragger.Visible = not verticalDragger.Visible control.Visible = not control.Visible else widgetContainer.Visible = not widgetContainer.Visible end if minimizeFrame.Visible then minimizeButton.Text = "+" else minimizeButton.Text = "-" end end minimizeBigButton.MouseButton1Click:connect(function ( ) switchMinimize() end) minimizeButton.MouseButton1Click:connect(function( ) switchMinimize() end) if scrollable then return dragBar, frame, helpFrame, closeEvent else return dragBar, widgetContainer, helpFrame, closeEvent end end t.Help = function(funcNameOrFunc) --input argument can be a string or a function. Should return a description (of arguments and expected side effects) if funcNameOrFunc == "CreatePropertyDropDownMenu" or funcNameOrFunc == t.CreatePropertyDropDownMenu then return "Function CreatePropertyDropDownMenu. " .. "Arguments: (instance, propertyName, enumType). " .. "Side effect: returns a container with a drop-down-box that is linked to the 'property' field of 'instance' which is of type 'enumType'" end if funcNameOrFunc == "CreateDropDownMenu" or funcNameOrFunc == t.CreateDropDownMenu then return "Function CreateDropDownMenu. " .. "Arguments: (items, onItemSelected). " .. "Side effect: Returns 2 results, a container to the gui object and a 'updateSelection' function for external updating. The container is a drop-down-box created around a list of items" end if funcNameOrFunc == "CreateMessageDialog" or funcNameOrFunc == t.CreateMessageDialog then return "Function CreateMessageDialog. " .. "Arguments: (title, message, buttons). " .. "Side effect: Returns a gui object of a message box with 'title' and 'message' as passed in. 'buttons' input is an array of Tables contains a 'Text' and 'Function' field for the text/callback of each button" end if funcNameOrFunc == "CreateStyledMessageDialog" or funcNameOrFunc == t.CreateStyledMessageDialog then return "Function CreateStyledMessageDialog. " .. "Arguments: (title, message, style, buttons). " .. "Side effect: Returns a gui object of a message box with 'title' and 'message' as passed in. 'buttons' input is an array of Tables contains a 'Text' and 'Function' field for the text/callback of each button, 'style' is a string, either Error, Notify or Confirm" end if funcNameOrFunc == "GetFontHeight" or funcNameOrFunc == t.GetFontHeight then return "Function GetFontHeight. " .. "Arguments: (font, fontSize). " .. "Side effect: returns the size in pixels of the given font + fontSize" end if funcNameOrFunc == "LayoutGuiObjects" or funcNameOrFunc == t.LayoutGuiObjects then end if funcNameOrFunc == "CreateScrollingFrame" or funcNameOrFunc == t.CreateScrollingFrame then return "Function CreateScrollingFrame. " .. "Arguments: (orderList, style) " .. "Side effect: returns 4 objects, (scrollFrame, scrollUpButton, scrollDownButton, recalculateFunction). 'scrollFrame' can be filled with GuiObjects. It will lay them out and allow scrollUpButton/scrollDownButton to interact with them. Orderlist is optional (and specifies the order to layout the children. Without orderlist, it uses the children order. style is also optional, and allows for a 'grid' styling if style is passed 'grid' as a string. recalculateFunction can be called when a relayout is needed (when orderList changes)" end if funcNameOrFunc == "CreateTrueScrollingFrame" or funcNameOrFunc == t.CreateTrueScrollingFrame then return "Function CreateTrueScrollingFrame. " .. "Arguments: (nil) " .. "Side effect: returns 2 objects, (scrollFrame, controlFrame). 'scrollFrame' can be filled with GuiObjects, and they will be clipped if not inside the frame's bounds. controlFrame has children scrollup and scrolldown, as well as a slider. controlFrame can be parented to any guiobject and it will readjust itself to fit." end if funcNameOrFunc == "AutoTruncateTextObject" or funcNameOrFunc == t.AutoTruncateTextObject then return "Function AutoTruncateTextObject. " .. "Arguments: (textLabel) " .. "Side effect: returns 2 objects, (textLabel, changeText). The 'textLabel' input is modified to automatically truncate text (with ellipsis), if it gets too small to fit. 'changeText' is a function that can be used to change the text, it takes 1 string as an argument" end if funcNameOrFunc == "CreateSlider" or funcNameOrFunc == t.CreateSlider then return "Function CreateSlider. " .. "Arguments: (steps, width, position) " .. "Side effect: returns 2 objects, (sliderGui, sliderPosition). The 'steps' argument specifies how many different positions the slider can hold along the bar. 'width' specifies in pixels how wide the bar should be (modifiable afterwards if desired). 'position' argument should be a UDim2 for slider positioning. 'sliderPosition' is an IntValue whose current .Value specifies the specific step the slider is currently on." end if funcNameOrFunc == "CreateSliderNew" or funcNameOrFunc == t.CreateSliderNew then return "Function CreateSliderNew. " .. "Arguments: (steps, width, position) " .. "Side effect: returns 2 objects, (sliderGui, sliderPosition). The 'steps' argument specifies how many different positions the slider can hold along the bar. 'width' specifies in pixels how wide the bar should be (modifiable afterwards if desired). 'position' argument should be a UDim2 for slider positioning. 'sliderPosition' is an IntValue whose current .Value specifies the specific step the slider is currently on." end if funcNameOrFunc == "CreateLoadingFrame" or funcNameOrFunc == t.CreateLoadingFrame then return "Function CreateLoadingFrame. " .. "Arguments: (name, size, position) " .. "Side effect: Creates a gui that can be manipulated to show progress for a particular action. Name appears above the loading bar, and size and position are udim2 values (both size and position are optional arguments). Returns 3 arguments, the first being the gui created. The second being updateLoadingGuiPercent, which is a bindable function. This function takes one argument (two optionally), which should be a number between 0 and 1, representing the percentage the loading gui should be at. The second argument to this function is a boolean value that if set to true will tween the current percentage value to the new percentage value, therefore our third argument is how long this tween should take. Our third returned argument is a BindableEvent, that when fired means that someone clicked the cancel button on the dialog." end if funcNameOrFunc == "CreateTerrainMaterialSelector" or funcNameOrFunc == t.CreateTerrainMaterialSelector then return "Function CreateTerrainMaterialSelector. " .. "Arguments: (size, position) " .. "Side effect: Size and position are UDim2 values that specifies the selector's size and position. Both size and position are optional arguments. This method returns 3 objects (terrainSelectorGui, terrainSelected, forceTerrainSelection). terrainSelectorGui is just the gui object that we generate with this function, parent it as you like. TerrainSelected is a BindableEvent that is fired whenever a new terrain type is selected in the gui. ForceTerrainSelection is a function that takes an argument of Enum.CellMaterial and will force the gui to show that material as currently selected." end end return t ]]> StatefulImageButton {9F789D2F-FFF3-4DC4-919E-185AC8F8B235} VerticallyScalingListFrame {2B74BDC9-0989-42F0-80FC-1F01C310FA2F} true 0 true false CutscenifyGui false null 1 false 0 0 true 0.180392161 0.180392161 0.180392161 0 0.105882362 0.164705887 0.207843155 0 1 false false 0 UI null null null null 0 0 0 0 null 0 false null 1 0 1 0 0 0 true 1 1 1 UILayout 0 14 2 1 false 0 0 true 0.639215708 0.635294139 0.647058845 1 0.105882362 0.164705887 0.207843155 0 0 rbxasset://textures/StudioToolbox/ScrollBarBottom.png 0 0 1 0 0 0 true false 0 0 3 rbxasset://textures/StudioToolbox/ScrollBarMiddle.png Properties null null null null 0 0 0 0 null 0 1 1 1 0 12 4 true true null 1 0 1 -172 0 rbxasset://textures/StudioToolbox/ScrollBarTop.png 0 0 true 1 1 1 UIListLayout 0 0 2 1 false 0 0 true 0.639215708 0.635294139 0.647058845 1 0.105882362 0.164705887 0.207843155 0 1 false false 0 Actions null null null null 0 0 0 0 null 0 false null 1 0 0 158 0 0 true 1 0 1 UILayout 0 14 2 1 false 0 0 true 1 1 1 1 0.105882362 0.164705887 0.207843155 0 1 false false 0 divider null null null null 0 0 0 0 null 0 false null 0 1 0 100 0 0 true 1 false 1 1 true 1 1 1 1 0.105882362 0.164705887 0.207843155 0 1 false false 3 0 1 PluginDetails null null null null 1 0 1 0 false null 0 false null 0 200 0 50 0 1 1 1 false 14 0 0 0 1 0 0 false 1 2 true 1 0 0 0 1 0 0 0 1 0 0 0 1 Inventfvl_CutsceneParts null true 0 true false CutsceneSkipGui true null 1 true 1 1 true true 1 1 1 0 0.105882362 0.164705887 0.207843155 0 1 false false 3 0 1 false SkipButton null null null null 1 0 1 0 false null 0 true false null 0 200 0 50 0 4 Skip 1 1 1 false 20 0 0 0 1 0 0 true 2 1 true 1 true 0 true false CameraEffect true null 1 false 0 0 true 1 1 1 1 0.105882362 0.164705887 0.207843155 0 0 false false 0 White null null null null 0 0 0 0 null 0 false null 1 0 1 0 0 0 true 1 CutsceneManager {2F5CA641-5733-4824-9FFE-0E7479F46DF9} true 0 true false NameSelect true null 1 false 0.5 0.5 true 0.180392161 0.180392161 0.180392161 0 0.105882362 0.164705887 0.207843155 0 0 false false 0 NamePrompt null null null null 0.5 0 0.5 0 null 0 false null 0 217 0 188 0 0 false 1 false 0 0 true 1 1 1 1 0.105882362 0.164705887 0.207843155 0 1 false false 3 0 1 Header null null null null 0.0875576064 0 0 0 false null 0 false null 0.824884772 0 0.0957446843 50 0 Enter a name for your cutscene. 0.800000012 0.800000012 0.800000012 false 20 0 0 0 1 0 0 true 2 1 true 1 false 0 0.5 true 1 1 1 1 0.105882362 0.164705887 0.207843155 0 0 false false 0 NameFrame null null null null 0.0875576064 0 0.5 0 null 0 false null 0.824884772 0 0 30 0 0 true 111 true 0 0 true 1 1 1 1 0.105882362 0.164705887 0.207843155 0 0 false false false 3 0 1 false TextBox null null null null 0.699999988 0.699999988 0.699999988 Type here... 0 8 0 0 false null 0 true null true 0.989304245 0 0 22 0 1 1 1 true false 16 0 0 0 1 0 0 false 0 1 true 111 BackgroundColorKey BackgroundColor TextColorKey TextColor BackgroundColorKey BackgroundColor false 0 0 true 1 1 1 1 0.105882362 0.164705887 0.207843155 0 0 false false rbxasset://textures/AnimationEditor/RoundedBorder.png 0.721568644 0.721568644 0.721568644 0 0 0 0 0 0 Border null null null null 0 0 0 0 null 0 1 false null 1 0 0 22 0 8 8 8 8 1 1 0 1 0 true 111 BackgroundColorKey BackgroundColor ImageKey BackgroundColor true 0 0.5 true true 0.639215708 0.635294139 0.647058845 1 0.105882362 0.164705887 0.207843155 0 1 false false rbxasset://textures/AvatarImporter/button_avatarType.png 0 0.635294139 1 0 0 0 0 0 2 false Done null null null null 0.0879999995 0 0.800000012 0 null 0 1 true false null 0.824999988 0 0 30 0 4 4 5 5 1 0 1 0 1 0 true 111 false 0 0 true 0.639215708 0.635294139 0.647058845 1 0.105882362 0.164705887 0.207843155 0 1 false false rbxasset://textures/AvatarImporter/button_avatarType_border.png 0 0.635294139 1 0 0 0 0 0 0 Border null null null null 0 0 0 0 null 0 1 false null 1 0 1 0 0 4 4 5 5 1 1 0 1 0 true 111 false 0 0 true 0.639215708 0.635294139 0.647058845 1 0.105882362 0.164705887 0.207843155 0 1 false false 3 0 1 TextLabel null null null null 0 0 0 0 false null 0 false null 1 0 1 0 0 OK 1 1 1 false 20 0 0 0 1 0 0 false 2 1 true 111 true 0 true false CutsceneSelect true null 1 false 0.5 0.5 true 1 1 1 0 0.745098054 0 0 0 1 false false 0 CutsceneAlreadyExistsPrompt null null null null 0.5 0 0.5 0 null 0 false null 0 400 0 217 0 0 false 1 false 0 0 true 0.745098054 0 0 0 0.105882362 0.164705887 0.207843155 0 0 false false 0 Header null null null null 0 0 0 0 null 0 false null 1 0 0.0104712043 29 0 0 true 1 false 0 0 true 1 1 1 1 0.105882362 0.164705887 0.207843155 0 1 false false rbxassetid://4681952923 1 1 1 0 0 0 0 0 0 WindowIcon null null null null 0.0206422023 0 0.0967741907 0 null 0 0 false null 0 24 0 24 0 0 0 0 0 1 1 0 1 0 true 1 false 0 0 true 1 1 1 1 0.105882362 0.164705887 0.207843155 0 1 false false 3 0 1 WindowTitle null null null null 0.0940366983 0 0 0 false null 0 false null 0 200 1 0 0 Cutscene Selection 1 1 1 false 14 0 0 0 1 0 0 false 0 1 true 1 true 0 0 true true 1 0 0 1 0.105882362 0.164705887 0.207843155 0 0 false false 17 0 1 false Close null null null null 0.883027494 0 0 0 false null 0 true false null 0 51 0 31 0 0 1 1 1 false 17 0 0 0 1 0 0 false 2 1 true 1 false 0.5 0.5 true 1 1 1 1 0.105882362 0.164705887 0.207843155 0 1 false false rbxassetid://3091824332 1 1 1 0 0 0 0 0 0 Image_Close null null null null 0.5 0 0.5 0 null 0 0 false null 0 13 0 13 0 0 0 0 0 1 1 0 1 0 true 1 false 0 0 true 1 1 1 0 0.105882362 0.164705887 0.207843155 0 0 false false 0 Content null null null null 0 0 0.142289385 0 null 0 false null 1 0 0.85771054 0 0 0 true 1 false 0 0 true 0.180392161 0.180392161 0.180392161 0 0.105882362 0.164705887 0.207843155 0 0 false false 0 Prompt null null null null 0 0 0 0 null 0 false null 0 400 0 186 0 0 true 1 UIPadding 0 24 0 24 0 24 0 24 false 0.5 0 true 1 1 1 1 0.105882362 0.164705887 0.207843155 0 1 false false 1 Content null null null null 0.5 0 0 0 null 0 false null 1 0 1 -56 0 0 true 1 false 0 0 true 1 1 1 1 0.105882362 0.164705887 0.207843155 0 0 false false rbxassetid://3944675151 1 1 1 0 0 0 0 0 0 Thumbnail null null null null 0 0 0 0 null 0 0 false null 0 80 0 80 0 0 0 0 0 1 1 0 1 0 true 1 false 1 0 true 1 1 1 1 0.105882362 0.164705887 0.207843155 0 1 false false 3 0 1 PromptText null null null null 1 0 0 0 false null 0 false null 0 260 0 60 0 You already have a saved cutscene for this place. Would you like to edit it? 0.800000012 0.800000012 0.800000012 false 18 0 0 0 1 0 0 true 0 0 true 1 false 0.5 0 true 1 1 1 1 0.105882362 0.164705887 0.207843155 0 1 false false 0 Amount null null null null 0.629999995 0 0 56 null 0 false null 0 260 0 18 0 0 true 1 0 1 Layout 0 3 2 0 false 0 0 true 1 1 1 1 0.105882362 0.164705887 0.207843155 0 1 false false 3 1 1 CutsceneSaveCount null null null null 0 0 0 0 false null 0 false null 0.215384617 50 1 0 0 Saved Cutscenes: 1 0.400000006 0.400000006 0.400000006 false 16 0 0 0 1 0 0 false 0 1 true 1 false 0.5 1 true 1 1 1 1 0.105882362 0.164705887 0.207843155 0 1 false false 2 Buttons null null null null 0.5 0 1 0 null 0 false null 1 0 0 32 0 0 true 1 true 0 0.5 true true 0.639215708 0.635294139 0.647058845 1 0.105882362 0.164705887 0.207843155 0 1 false false rbxasset://textures/StudioToolbox/RoundedBackground.png 0.219607845 0.219607845 0.219607845 0 0 0 0 0 1 false New null null null null 0 0 0 0 null 0 1 true false null 0 120 0 32 0 3 3 13 13 1 0 1 0 1 0 true 1 false 0 0 true 0.639215708 0.635294139 0.647058845 1 0.105882362 0.164705887 0.207843155 0 1 false false rbxasset://textures/StudioToolbox/RoundedBorder.png 0.13333334 0.13333334 0.13333334 0 0 0 0 0 0 Border null null null null 0 0 0 0 null 0 1 false null 1 0 1 0 0 3 3 13 13 1 1 0 1 0 true 1 false 0 0 true 1 1 1 1 0.105882362 0.164705887 0.207843155 0 1 false false 3 0 1 Text null null null null 0 0 0 0 false null 0 false null 1 0 1 0 0 Create New 0.800000012 0.800000012 0.800000012 false 18 0 0 0 1 0 0 false 2 1 true 1 0 1 UIListLayout 0 0 2 1 false 0 0 true 1 1 1 1 0.105882362 0.164705887 0.207843155 0 1 false false 0 Seperator1 null null null null 0 0 0 0 null 0 false null 0.0482954532 22 1 0 0 0 true 1 false 0 0 true 1 1 1 1 0.105882362 0.164705887 0.207843155 0 1 false false 2 Seperator2 null null null null 0.451704532 0 0 0 null 0 false null 0 22 1 0 0 0 true 1 true 0 0.5 true true 0.639215708 0.635294139 0.647058845 1 0.105882362 0.164705887 0.207843155 0 1 false false rbxasset://textures/StudioToolbox/RoundedBackground.png 0 0.635294139 1 0 0 0 0 0 3 false ChooseOrEdit null null null null 0 0 0 0 null 0 1 true false null 0 120 0 32 0 3 3 13 13 1 0 1 0 1 0 true 1 false 0 0 true 0.639215708 0.635294139 0.647058845 1 0.105882362 0.164705887 0.207843155 0 1 false false rbxasset://textures/StudioToolbox/RoundedBorder.png 0 0.635294139 1 0 0 0 0 0 0 Border null null null null 0 0 0 0 null 0 1 false null 1 0 1 0 0 3 3 13 13 1 1 0 1 0 true 1 false 0 0 true 1 1 1 1 0.105882362 0.164705887 0.207843155 0 1 false false 3 0 1 Text null null null null 0 0 0 0 false null 0 false null 1 0 1 0 0 Choose Cutscene 1 1 1 false 18 0 0 0 1 0 0 false 2 1 true 1 ErrorPromptMain {E28A88FC-3BF1-43A9-BE94-CEF97BCE14FE} Cutscenify_Saves true PlayerDiedCutsceneScript {B0D904E4-A8AF-4BDF-A8B4-C9F4058AF78E} true 0 true false BottomGui true null 1 true 0.5 1 true true 0.639215708 0.635294139 0.647058845 1 0.105882362 0.164705887 0.207843155 0 1 false false rbxasset://textures/StudioToolbox/RoundedBackground.png 0.121568628 0.121568628 0.121568628 0 0 0 0 0 2 false Error null null null null 0.5 0 0.980000019 0 null 0 1 true false null 0 300 0 50 0 3 3 13 13 1 0 1 0 1 0 false 1 false 0 0.5 true 1 1 1 1 0.105882362 0.164705887 0.207843155 0 1 false false rbxasset://textures/ui/LuaChat/icons/ic-alert@2x.png 1 1 1 0 0 0 0 0 0 ImageLabel null null null null 0.0500000007 0 0.5 0 null 0 0 false null 0 40 0 40 0 0 0 0 0 1 1 0 1 0 true 1 false 1 0.5 true 0.639215708 0.635294139 0.647058845 1 0.105882362 0.164705887 0.207843155 0 0 false false 3 2 1 Text null null null null 1 0 0.5 0 false null 0 false null 0 235 0 50 0 Error 0.800000072 0.800000072 0.800000072 false 16 0 0 0 1 0 0 true 0 1 true 1 true 0.5 1 true true 0.639215708 0.635294139 0.647058845 1 0.105882362 0.164705887 0.207843155 0 1 false false rbxasset://textures/StudioToolbox/RoundedBackground.png 1 0.501960814 0 0 0 0 0 0 2 false Update null null null null 0.5 0 0.980000019 0 null 0 1 true false null 0 300 0 50 0 3 3 13 13 1 0 1 0 1 0 false 1 false 0.5 0.5 true 0.639215708 0.635294139 0.647058845 1 0.105882362 0.164705887 0.207843155 0 0 false false 3 2 1 Text null null null null 0.5 0 0.5 0 false null 0 false null 0 300 0 30 0 A new update is avaliable! 1 1 1 false 16 0 0 0 1 0 0 true 2 1 true 1 true PartTouchCutsceneScript {40F2893B-BD9E-4041-88E6-28337621A78D} true 0 true false Options true null 1 false 1 0 true 0.639215708 0.635294139 0.647058845 1 0.105882362 0.164705887 0.207843155 0 1 false false 2 Options null null null null 1 0 0 0 null 0 false null 0 24 0 24 0 0 false 1 true 0 0 false true 0.180392161 0.180392161 0.180392161 0 0.13333334 0.13333334 0.13333334 0 1 false false 1 1 1 0 0 0 0 0 0 false Button null null null null 0 0 0 0 null 0 0 true false null 1 0 1 0 0 0 0 0 0 1 0 1 0 1 0 true 1 false 0.5 0.5 true 0.639215708 0.635294139 0.647058845 1 0.105882362 0.164705887 0.207843155 0 1 false false rbxasset://textures/AnimationEditor/icon_showmore.png 0.800000072 0.800000072 0.800000072 0 0 0 0 0 0 image null null null null 0.5 0 0.5 0 null 0 3 false null 0.800000012 0 0.800000012 0 0 0 0 0 0 1 1 0 1 0 true 1 false 0 0 true 0.639215708 0.635294139 0.647058845 1 0.105882362 0.164705887 0.207843155 0 1 false false 0 Tooltip null null null null 0 0 0 0 null 0 false null 1 0 1 0 0 0 true 1 true PlayerAddedCutsceneScript {DA136452-CAC5-4179-A389-175B40579DDB} CutsceneExecutionModule {7113C19F-22A6-445D-9162-4E5B0F370C99} true -0.5 0.5 0 0 -0.5 0.5 0 0 -49.5 30.5000191 -134 1 0 0 0 1 0 0 0 1 true true 0 4288914085 false -0.5 0.5 0 0 -0.5 0.5 0 0 false false 256 CutscenePartTouch 0 -0.5 0.5 0 0 0 0 0 0 -0.5 0.5 0 0 0 0 0 0 1 1 4 1 4 0 0 0 1 Decal rbxassetid://4335484343 0 true 0 true false RenameGui true null 1 false 0.5 0.5 true 0.180392161 0.180392161 0.180392161 0 0.105882362 0.164705887 0.207843155 0 0 false false 0 NamePrompt null null null null 0.5 0 0.5 0 null 0 false null 0 217 0 188 0 0 false 2 false 0 0 true 1 1 1 1 0.105882362 0.164705887 0.207843155 0 1 false false 3 0 1 Header null null null null 0.0875576064 0 0 0 false null 0 false null 0.824884772 0 0.0957446843 50 0 Enter a new name. 0.800000012 0.800000012 0.800000012 false 20 0 0 0 1 0 0 true 2 1 true 2 false 0 0.5 true 1 1 1 1 0.105882362 0.164705887 0.207843155 0 0 false false 0 NameFrame null null null null 0.0875576064 0 0.5 0 null 0 false null 0.824884772 0 0 30 0 0 true 2 true 0 0 true 1 1 1 1 0.105882362 0.164705887 0.207843155 0 0 false false false 3 0 1 false TextBox null null null null 0.699999988 0.699999988 0.699999988 Type here... 0 8 0 0 false null 0 true null true 0.989304245 0 0 22 0 1 1 1 true false 16 0 0 0 1 0 0 false 0 1 true 111 BackgroundColorKey BackgroundColor TextColorKey TextColor BackgroundColorKey BackgroundColor false 0 0 true 1 1 1 1 0.105882362 0.164705887 0.207843155 0 0 false false rbxasset://textures/AnimationEditor/RoundedBorder.png 0.721568644 0.721568644 0.721568644 0 0 0 0 0 0 Border null null null null 0 0 0 0 null 0 1 false null 1 0 0 22 0 8 8 8 8 1 1 0 1 0 true 111 BackgroundColorKey BackgroundColor ImageKey BackgroundColor true 0 0.5 true true 0.639215708 0.635294139 0.647058845 1 0.105882362 0.164705887 0.207843155 0 1 false false rbxasset://textures/AvatarImporter/button_avatarType.png 0 0.635294139 1 0 0 0 0 0 2 false Done null null null null 0.0879999995 0 0.800000012 0 null 0 1 true false null 0.824999988 0 0 30 0 4 4 5 5 1 0 1 0 1 0 true 2 false 0 0 true 0.639215708 0.635294139 0.647058845 1 0.105882362 0.164705887 0.207843155 0 1 false false rbxasset://textures/AvatarImporter/button_avatarType_border.png 0 0.635294139 1 0 0 0 0 0 0 Border null null null null 0 0 0 0 null 0 1 false null 1 0 1 0 0 4 4 5 5 1 1 0 1 0 true 111 false 0 0 true 0.639215708 0.635294139 0.647058845 1 0.105882362 0.164705887 0.207843155 0 1 false false 3 0 1 TextLabel null null null null 0 0 0 0 false null 0 false null 1 0 1 0 0 OK 1 1 1 false 20 0 0 0 1 0 0 false 2 1 true 111 true 0 true false Background true null 1 false 0 0 true 0 0 0 0.5 0.105882362 0.164705887 0.207843155 0 0 false false 0 BGFrame null null null null 0 0 0 0 null 0 false null 1 0 1 0 0 0 false 1 true 0 0 true true 1 1 1 1 0.105882362 0.164705887 0.207843155 0 0 false false 3 0 1 false ClickEater null null null null 0 0 0 0 false null 0 true false null 1 0 1 0 0 0 0 0 0 false 14 0 0 0 1 0 0 false 2 1 true 1 true 0 true false PopupGui true null 1 false 0.5 0.5 true 1 1 1 0 0.745098054 0 0 0 1 false false 0 PopupFrame null null null null 0.5 0 0.5 0 null 0 false null 0 400 0 217 0 0 false 1 false 0 0 true 0.745098054 0 0 0 0.105882362 0.164705887 0.207843155 0 0 false false 0 Header null null null null 0 0 0 0 null 0 false null 1 0 0.0104712043 29 0 0 true 1 false 0 0 true 1 1 1 1 0.105882362 0.164705887 0.207843155 0 1 false false rbxassetid://4681952923 1 1 1 0 0 0 0 0 0 WindowIcon null null null null 0.0206422023 0 0.0967741907 0 null 0 0 false null 0 24 0 24 0 0 0 0 0 1 1 0 1 0 true 1 false 0 0 true 1 1 1 1 0.105882362 0.164705887 0.207843155 0 1 false false 3 0 1 WindowTitle null null null null 0.0940366983 0 0 0 false null 0 false null 0 200 1 0 0 Cutscenify 1 1 1 false 14 0 0 0 1 0 0 false 0 1 true 1 true 0 0 true true 1 0 0 1 0.105882362 0.164705887 0.207843155 0 0 false false 17 0 1 false Close null null null null 0.883027494 0 0 0 false null 0 true false null 0 46 0 31 0 0 1 1 1 false 17 0 0 0 1 0 0 false 2 1 true 1 false 0.5 0.5 true 1 1 1 1 0.105882362 0.164705887 0.207843155 0 1 false false rbxassetid://3091824332 1 1 1 0 0 0 0 0 0 Image_Close null null null null 0.5 0 0.5 0 null 0 0 false null 0 13 0 13 0 0 0 0 0 1 1 0 1 0 true 1 false 0 0 true 1 1 1 0 0.105882362 0.164705887 0.207843155 0 0 false false 0 Content null null null null 0 0 0.142289385 0 null 0 false null 1 0 0.85771054 0 0 0 true 1 false 0 0 true 0.180392161 0.180392161 0.180392161 0 0.105882362 0.164705887 0.207843155 0 0 false false 0 PromptDetails null null null null 0 0 0 0 null 0 false null 0 400 1 0 0 0 true 1 UIPadding 0 24 0 24 0 24 0 24 false 0.5 0 true 1 1 1 1 0.105882362 0.164705887 0.207843155 0 1 false false 1 Content null null null null 0.5 0 0 0 null 0 false null 1 0 1 -56 0 0 true 1 false 0.5 0 true 1 1 1 1 0.105882362 0.164705887 0.207843155 0 1 false false 3 0 1 PromptText null null null null 0.400000006 0 0 0 false null 0 false null 0 260 0 60 0 PopupDetails 0.800000012 0.800000012 0.800000012 false 18 0 0 0 1 0 0 true 0 0 true 1 false 0.5 1 true 1 1 1 1 0.105882362 0.164705887 0.207843155 0 1 false false 2 Buttons null null null null 0.5 0 1 0 null 0 false null 1 0 0 32 0 0 true 1 true 0 0.5 true true 0.639215708 0.635294139 0.647058845 1 0.105882362 0.164705887 0.207843155 0 1 false false rbxasset://textures/StudioToolbox/RoundedBackground.png 0.219607845 0.219607845 0.219607845 0 0 0 0 0 1 false Secondary null null null null 0 0 0 0 null 0 1 true false null 0 120 0 32 0 3 3 13 13 1 0 1 0 1 0 true 1 false 0 0 true 0.639215708 0.635294139 0.647058845 1 0.105882362 0.164705887 0.207843155 0 1 false false rbxasset://textures/StudioToolbox/RoundedBorder.png 0.13333334 0.13333334 0.13333334 0 0 0 0 0 0 Border null null null null 0 0 0 0 null 0 1 false null 1 0 1 0 0 3 3 13 13 1 1 0 1 0 true 1 false 0 0 true 1 1 1 1 0.105882362 0.164705887 0.207843155 0 1 false false 3 0 1 Text null null null null 0 0 0 0 false null 0 false null 1 0 1 0 0 No 0.800000012 0.800000012 0.800000012 false 18 0 0 0 1 0 0 false 2 1 true 1 0 1 UIListLayout 0 0 2 1 false 0 0 true 1 1 1 1 0.105882362 0.164705887 0.207843155 0 1 false false 0 Seperator1 null null null null 0 0 0 0 null 0 false null 0.0482954532 22 1 0 0 0 true 1 false 0 0 true 1 1 1 1 0.105882362 0.164705887 0.207843155 0 1 false false 2 Seperator2 null null null null 0.451704532 0 0 0 null 0 false null 0 22 1 0 0 0 true 1 true 0 0.5 true true 0.639215708 0.635294139 0.647058845 1 0.105882362 0.164705887 0.207843155 0 1 false false rbxasset://textures/StudioToolbox/RoundedBackground.png 0 0.635294139 1 0 0 0 0 0 3 false Primary null null null null 0 0 0 0 null 0 1 true false null 0 120 0 32 0 3 3 13 13 1 0 1 0 1 0 true 1 false 0 0 true 0.639215708 0.635294139 0.647058845 1 0.105882362 0.164705887 0.207843155 0 1 false false rbxasset://textures/StudioToolbox/RoundedBorder.png 0 0.635294139 1 0 0 0 0 0 0 Border null null null null 0 0 0 0 null 0 1 false null 1 0 1 0 0 3 3 13 13 1 1 0 1 0 true 1 false 0 0 true 1 1 1 1 0.105882362 0.164705887 0.207843155 0 1 false false 3 0 1 Text null null null null 0 0 0 0 false null 0 false null 1 0 1 0 0 Yes 1 1 1 false 18 0 0 0 1 0 0 false 2 1 true 1 true 0 true false OLD_CutscenePicker true null 1 false 0.5 0.5 true 1 1 1 0 0.745098054 0 0 0 1 false false 0 CutsceneSelectionPrompt null null null null 0.5 0 0.5 0 null 0 false null 0 400 0 500 0 0 false 1 false 0 0 true 0.745098054 0 0 0 0.105882362 0.164705887 0.207843155 0 0 false false 0 Header null null null null 0 0 0 0 null 0 false null 1 0 0.0104712043 29 0 0 true 1 false 0 0 true 1 1 1 1 0.105882362 0.164705887 0.207843155 0 1 false false rbxassetid://4681952923 1 1 1 0 0 0 0 0 0 WindowIcon null null null null 0.0206422023 0 0.0967741907 0 null 0 0 false null 0 24 0 24 0 0 0 0 0 1 1 0 1 0 true 1 false 0 0 true 1 1 1 1 0.105882362 0.164705887 0.207843155 0 1 false false 3 0 1 WindowTitle null null null null 0.0940366983 0 0 0 false null 0 false null 0 200 1 0 0 Cutscene Selection 1 1 1 false 14 0 0 0 1 0 0 false 0 1 true 1 false 0 0 true 1 1 1 0 0.105882362 0.164705887 0.207843155 0 0 false false 0 Content null null null null 0 0 0.0684711933 0 null 0 false null 1 0 0.931528807 0 0 0 true 1 false 0 0 true 0.180392161 0.180392161 0.180392161 0 0.105882362 0.164705887 0.207843155 0 0 false false 0 Prompt null null null null 0 0 0 0 null 0 false null 0 400 1 0 0 0 true 1 UIPadding 0 24 0 24 0 24 0 24 false 0.5 0 true 1 1 1 1 0.105882362 0.164705887 0.207843155 0 1 false false 1 Content null null null null 0.5 0 0 0 null 0 false null 1 0 1 -56 0 0 true 1 false 0.5 0 true 1 1 1 1 0.105882362 0.164705887 0.207843155 0 1 false false 3 0 1 PromptText null null null null 0.400000006 0 0 0 false null 0 false null 0 260 0 60 0 Please select a cutscene. 0.800000012 0.800000012 0.800000012 false 18 0 0 0 1 0 0 true 0 0 true 1 false 0 1 true 1 1 1 1 0.105882362 0.164705887 0.207843155 0 0 rbxasset://textures/ui/Scroll/scroll-bottom.png 0 0 0 0 2 0 true false 0 0 0 rbxasset://textures/ui/Scroll/scroll-middle.png CutsceneSelection null null null null 0 0 1 0 null 0 1 1 1 0 0 4 true true null 1 0 0.765691698 55 0 rbxasset://textures/ui/Scroll/scroll-top.png 0 0 true 1 true 0 0 false true 0.207843155 0.207843155 0.207843155 0 0.105882362 0.164705887 0.207843155 0 0 false false 1 1 1 0 0 0 0 0 1 false CutsceneName null null null null 0 0 0 0 null 0 0 true false null 1 0 0 27 0 0 0 0 0 1 0 1 0 1 0 false 1 false 0 0 true 0.639215708 0.635294139 0.647058845 1 0.105882362 0.164705887 0.207843155 0 1 false false 4 0 1 TitleLabel null null null null 0 15 0 0 false null 0 false null 1 -27 1 -3 0 CutsceneName 0.800000072 0.800000072 0.800000072 false 15 0 0 0 1 0 0 false 0 1 true 1 true 0 0 false true 1 1 1 1 0.105882362 0.164705887 0.207843155 0 0 false false 3 0 1 false Button null null null null 0 0 0 0 false null 0 true false null 1 0 1 0 0 0 0 0 0 false 14 0 0 0 1 0 0 false 2 1 true 1 1 1 UILayout 0 3 2 1 false 0.5 1 true 1 1 1 1 0.105882362 0.164705887 0.207843155 0 1 false false 2 Buttons null null null null 0.5 0 1 0 null 0 false null 1 0 0 32 0 0 true 1 true 0 0.5 true true 0.639215708 0.635294139 0.647058845 1 0.105882362 0.164705887 0.207843155 0 1 false false rbxasset://textures/StudioToolbox/RoundedBackground.png 0.219607845 0.219607845 0.219607845 0 0 0 0 0 1 false New null null null null 0 0 0 0 null 0 1 true false null 0 120 0 32 0 3 3 13 13 1 0 1 0 1 0 true 1 false 0 0 true 0.639215708 0.635294139 0.647058845 1 0.105882362 0.164705887 0.207843155 0 1 false false rbxasset://textures/StudioToolbox/RoundedBorder.png 0.13333334 0.13333334 0.13333334 0 0 0 0 0 0 Border null null null null 0 0 0 0 null 0 1 false null 1 0 1 0 0 3 3 13 13 1 1 0 1 0 true 1 false 0 0 true 1 1 1 1 0.105882362 0.164705887 0.207843155 0 1 false false 3 0 1 Text null null null null 0 0 0 0 false null 0 false null 1 0 1 0 0 Create New 0.800000012 0.800000012 0.800000012 false 18 0 0 0 1 0 0 false 2 1 true 1 0 1 UIListLayout 0 0 2 1 false 0 0 true 1 1 1 1 0.105882362 0.164705887 0.207843155 0 1 false false 0 Seperator1 null null null null 0 0 0 0 null 0 false null 0.0482954532 22 1 0 0 0 true 1 false 0 0 true 1 1 1 1 0.105882362 0.164705887 0.207843155 0 1 false false 2 Seperator2 null null null null 0.451704532 0 0 0 null 0 false null 0 22 1 0 0 0 true 1 true 0 0.5 true true 0.639215708 0.635294139 0.647058845 1 0.105882362 0.164705887 0.207843155 0 1 false false rbxasset://textures/StudioToolbox/RoundedBackground.png 0 0.635294139 1 0 0 0 0 0 3 false Done null null null null 0 0 0 0 null 0 1 true false null 0 120 0 32 0 3 3 13 13 1 0 1 0 1 0 true 1 false 0 0 true 0.639215708 0.635294139 0.647058845 1 0.105882362 0.164705887 0.207843155 0 1 false false rbxasset://textures/StudioToolbox/RoundedBorder.png 0 0.635294139 1 0 0 0 0 0 0 Border null null null null 0 0 0 0 null 0 1 false null 1 0 1 0 0 3 3 13 13 1 1 0 1 0 true 1 false 0 0 true 1 1 1 1 0.105882362 0.164705887 0.207843155 0 1 false false 3 0 1 Text null null null null 0 0 0 0 false null 0 false null 1 0 1 0 0 Done 1 1 1 false 18 0 0 0 1 0 0 false 2 1 true 1 true 0 true false CutscenePicker true null 1 false 0.5 0.5 true 1 1 1 0 0.745098054 0 0 0 1 false false 0 CutsceneSelectionPrompt null null null null 0.5 0 0.5 0 null 0 false null 0 400 0 500 0 0 false 1 false 0 0 true 0.745098054 0 0 0 0.105882362 0.164705887 0.207843155 0 0 false false 0 Header null null null null 0 0 0 0 null 0 false null 1 0 0.0104712043 29 0 0 true 1 false 0 0 true 1 1 1 1 0.105882362 0.164705887 0.207843155 0 1 false false rbxassetid://4681952923 1 1 1 0 0 0 0 0 0 WindowIcon null null null null 0.0206422023 0 0.0967741907 0 null 0 0 false null 0 24 0 24 0 0 0 0 0 1 1 0 1 0 true 1 false 0 0 true 1 1 1 1 0.105882362 0.164705887 0.207843155 0 1 false false 3 0 1 WindowTitle null null null null 0.0940366983 0 0 0 false null 0 false null 0 200 1 0 0 Cutscene Selection 1 1 1 false 14 0 0 0 1 0 0 false 0 1 true 1 true 1 0 true true 1 0 0 1 0.105882362 0.164705887 0.207843155 0 0 false false 17 0 1 false Close null null null null 1 0 0 0 false null 0 true false null 0 51 1 0 0 0 1 1 1 false 17 0 0 0 1 0 0 false 2 1 true 1 false 0.5 0.5 true 1 1 1 1 0.105882362 0.164705887 0.207843155 0 1 false false rbxassetid://3091824332 1 1 1 0 0 0 0 0 0 Image_Close null null null null 0.5 0 0.5 0 null 0 0 false null 0 13 0 13 0 0 0 0 0 1 1 0 1 0 true 1 false 0 0 true 1 1 1 0 0.105882362 0.164705887 0.207843155 0 0 false false 0 Content null null null null 0 0 0.0684711933 0 null 0 false null 1 0 0.931528807 0 0 0 true 1 false 0 0 true 0.180392161 0.180392161 0.180392161 0 0.105882362 0.164705887 0.207843155 0 0 false false 0 Prompt null null null null 0 0 0 0 null 0 false null 0 400 1 0 0 0 true 1 UIPadding 0 24 0 24 0 24 0 24 false 0.5 0 true 1 1 1 1 0.105882362 0.164705887 0.207843155 0 1 false false 1 Content null null null null 0.5 0 0 0 null 0 false null 1 0 1 -56 0 0 true 1 false 0.5 0 true 1 1 1 1 0.105882362 0.164705887 0.207843155 0 1 false false 3 0 1 PromptText null null null null 0.400000006 0 0 0 false null 0 false null 0 260 0 60 0 Please select a cutscene. 0.800000012 0.800000012 0.800000012 false 18 0 0 0 1 0 0 true 0 0 true 1 false 0 1 true 1 1 1 1 0.105882362 0.164705887 0.207843155 0 0 rbxasset://textures/ui/Scroll/scroll-bottom.png 0 0 0 0 2 0 true false 0 0 0 rbxasset://textures/ui/Scroll/scroll-middle.png CutsceneSelection null null null null 0 0 1 0 null 0 1 1 1 0 0 4 true true null 1 0 0.765691698 55 0 rbxasset://textures/ui/Scroll/scroll-top.png 0 0 true 1 true 0 0 false true 0.207843155 0.207843155 0.207843155 0 0.105882362 0.164705887 0.207843155 0 0 false false 1 1 1 0 0 0 0 0 1 false CutsceneName null null null null 0 0 0 0 null 0 0 true false null 1 0 0 27 0 0 0 0 0 1 0 1 0 1 0 false 1 false 0 0 true 0.639215708 0.635294139 0.647058845 1 0.105882362 0.164705887 0.207843155 0 1 false false 4 0 1 TitleLabel null null null null 0 15 0 0 false null 0 false null 1 -27 1 -3 0 CutsceneName 0.800000072 0.800000072 0.800000072 false 15 0 0 0 1 0 0 false 0 1 true 1 true 0 0 false true 1 1 1 1 0.105882362 0.164705887 0.207843155 0 0 false false 3 0 1 false Button null null null null 0 0 0 0 false null 0 true false null 1 0 1 0 0 0 0 0 0 false 14 0 0 0 1 0 0 false 2 1 true 1 1 1 UILayout 0 3 2 1 false 0.5 1 true 1 1 1 1 0.105882362 0.164705887 0.207843155 0 1 false false 2 Buttons null null null null 0.5 0 1 0 null 0 false null 1 0 0 32 0 0 true 1 0 0 UIListLayout 0 0 2 1 true 0 0.5 true true 0.639215708 0.635294139 0.647058845 1 0.105882362 0.164705887 0.207843155 0 1 false false rbxasset://textures/StudioToolbox/RoundedBackground.png 0 0.635294139 1 0 0 0 0 0 3 false CreateNew null null null null 0 0 0 0 null 0 1 true false null 0 120 0 32 0 3 3 13 13 1 0 1 0 1 0 true 1 false 0 0 true 0.639215708 0.635294139 0.647058845 1 0.105882362 0.164705887 0.207843155 0 1 false false rbxasset://textures/StudioToolbox/RoundedBorder.png 0 0.635294139 1 0 0 0 0 0 0 Border null null null null 0 0 0 0 null 0 1 false null 1 0 1 0 0 3 3 13 13 1 1 0 1 0 true 1 false 0 0 true 1 1 1 1 0.105882362 0.164705887 0.207843155 0 1 false false 3 0 1 Text null null null null 0 0 0 0 false null 0 false null 1 0 1 0 0 Create New 1 1 1 false 18 0 0 0 1 0 0 false 2 1 true 1 WindowPromptMain {0EE0DE88-11EE-4F92-85D9-6720AE553AC9} CutscenifyTranslator {C105B658-A5A0-44EF-A72E-08E9D652D923} [{"key":"CutsceneConfirmation.Text","values":{"en-us":"Would you like to edit ","zh-cn":"你想编辑","ko-kr":"당신은 편집 싶습니다 "}},{"key":"CutsceneConfirmation.Title","values":{"en-us":"Cutscenify","zh-cn":"Cutscenify","ko-kr":"Cutscenify"}},{"key":"CutsceneConfirmation.Yes","values":{"en-us":"Yes","zh-cn":"是","ko-kr":"예"}},{"key":"CutsceneConfirmation_No","values":{"en-us":"No","zh-cn":"没有","ko-kr":"아니"}},{"key":"CutscenePicker.Title","values":{"en-us":"Please select a cutscene.","zh-cn":"请选择一个过场动画。","ko-kr":"컷씬을 선택하십시오."}},{"key":"CutsceneSelect.Choose","values":{"en-us":"Choose Cutscene","zh-cn":"选择过场","ko-kr":"컷씬을 선택"}},{"key":"CutsceneSelect.CutsceneSaveCount","values":{"en-us":"Saved Cutscenes: ","zh-cn":"保存过场:","ko-kr":"저장 컷씬 :"}},{"key":"CutsceneSelect.Edit","values":{"en-us":"Edit","zh-cn":"编辑","ko-kr":"편집하다"}},{"key":"CutsceneSelect.New","values":{"en-us":"Create New","zh-cn":"创建新的","ko-kr":"새로 만들기"}},{"key":"CutsceneSelect.Text","values":{"en-us":"You already have a saved cutscene for this place. Would you like to edit it?","zh-cn":"您已经有这个地方保存的过场动画。你想编辑呢?","ko-kr":"이미이 장소에 대한 저장된 컷씬이있다. 당신은 편집 그것에 하시겠습니까?"}},{"key":"CutsceneSelect.Title","values":{"en-us":"Cutscene Selection","zh-cn":"过场的选择","ko-kr":"컷씬 선택"}},{"key":"EDir.InOut","values":{"en-us":"InOut","zh-cn":"进出","ko-kr":"입출력"}},{"key":"EDir.Out","values":{"en-us":"Out","zh-cn":"退房","ko-kr":"ë°–"}},{"key":"ESty.Back","values":{"en-us":"Back","zh-cn":"背部","ko-kr":"ë’¤"}},{"key":"ESty.Bounce","values":{"en-us":"Bounce","zh-cn":"弹跳","ko-kr":"되튐"}},{"key":"ESty.Circular","values":{"en-us":"Circular","zh-cn":"圆","ko-kr":"회보"}},{"key":"ESty.Cubic","values":{"en-us":"Cubic","zh-cn":"立方体","ko-kr":"입방의"}},{"key":"ESty.Elastic","values":{"en-us":"Elastic","zh-cn":"å¼¹","ko-kr":"탄력있는"}},{"key":"ESty.Exponential","values":{"en-us":"Exponential","zh-cn":"指数","ko-kr":"지수의"}},{"key":"ESty.Linear","values":{"en-us":"Linear","zh-cn":"线性","ko-kr":"선의"}},{"key":"ESty.Quad","values":{"en-us":"Quad","zh-cn":"å››","ko-kr":"쿼드"}},{"key":"ESty.Quart","values":{"en-us":"Quart","zh-cn":"夸脱","ko-kr":"쿼트"}},{"key":"ESty.Quint","values":{"en-us":"Quint","zh-cn":"昆特","ko-kr":"다섯 쌍둥이 중의 í•œ 명"}},{"key":"ESty.Sine","values":{"en-us":"Sine","zh-cn":"正弦","ko-kr":"사인"}},{"key":"ImportFBXRigPlugin.Name","values":{"en-us":"Import Rig","zh-cn":"进口钻机","ko-kr":"가져 오기 ì¡°ìž‘"}},{"key":"In","values":{"en-us":"In","zh-cn":"在","ko-kr":"에"}},{"key":"MainUi.AddPoint","values":{"en-us":"Add Point","zh-cn":"添加点","ko-kr":"포인트 추가"}},{"key":"MainUi.Insert","values":{"en-us":"Insert","zh-cn":"插","ko-kr":"끼워 넣다"}},{"key":"MainUi.Preview","values":{"en-us":"Preview","zh-cn":"预习","ko-kr":"시사"}},{"key":"More.Exit","values":{"en-us":"Exit","zh-cn":"出口","ko-kr":"출구"}},{"key":"More.Rename","values":{"en-us":"Rename","zh-cn":"改名","ko-kr":"이름 바꾸기"}},{"key":"NamePrompt.Done","values":{"en-us":"OK","zh-cn":"好","ko-kr":"확인"}},{"key":"NamePrompt.Placeholder","values":{"en-us":"Type here...","zh-cn":"在此输入...","ko-kr":"여기에 ìž…ë ¥ ..."}},{"key":"NamePrompt.Title","values":{"en-us":"Enter a name for your cutscene.","zh-cn":"为您的镜头切换输入一个名称。","ko-kr":"당신의 컷씬의 이름을 입력합니다."}},{"key":"Plugin.Button","values":{"en-us":"Cutscenify","zh-cn":"Cutscenify","ko-kr":"Cutscenify"}},{"key":"Plugin.RoactAPI.ChangeLog.Feature1Text","values":{"en-us":"Added a new Change Log window.","zh-cn":"增加了一个新的更改日志窗口。","ko-kr":"새로운 변경 로그 창을 추가했습니다."}},{"key":"Plugin.RoactAPI.ChangeLog.Feature2Text","values":{"en-us":"Added a delay slider.","zh-cn":"增加了一个延迟滑块。","ko-kr":"지연 슬라이더를 추가했습니다."}},{"key":"Plugin.RoactAPI.ChangeLog.Feature3Text","values":{}},{"key":"Plugin.RoactAPI.ChangeLog.Feature4Text","values":{}},{"key":"Plugin.RoactAPI.ChangeLog.Feature5Text","values":{}},{"key":"Plugin.RoactAPI.ChangeLog.Fix1Text","values":{}},{"key":"Plugin.RoactAPI.ChangeLog.Fix2Text","values":{}},{"key":"Plugin.RoactAPI.ChangeLog.Fix3Text","values":{}},{"key":"Plugin.RoactAPI.ChangeLog.Fix4Text","values":{}},{"key":"Plugin.RoactAPI.ChangeLog.Fix5Text","values":{}},{"key":"Plugin.RoactAPI.ChangeLog.PluginGuiTitle","values":{"en-us":"Change Log - Cutscenify","zh-cn":"更改日志 - Cutscenify","ko-kr":"로그인 변경 - Cutscenify"}},{"key":"Plugin.RoactAPI.ChangeLog.WhatsNew","values":{"en-us":"What's new?","zh-cn":"什么是新的?","ko-kr":"새로운 기능?"}},{"key":"Plugin.Toolbar","values":{"en-us":"Cutscene Creator","zh-cn":"过场动画创作者","ko-kr":"컷씬 창조주"}},{"key":"Plugin_Info","values":{"en-us":"Create awesome, impressive & amazing cutscenes with Cutscenify!","zh-cn":"创建Cutscenify真棒,令人印象深刻的和惊人的过场动画!","ko-kr":"Cutscenify와, 멋진 인상 및 놀라운 컷씬을 만들기!"}},{"key":"Properties.Insert.Button","values":{"en-us":"Insert Script","zh-cn":"插入脚本","ko-kr":"삽입 스크립트"}},{"key":"Properties.Insert.DoesStartAndEndPlayer","values":{"en-us":"Start & End Cutscene at Player?","zh-cn":"开始和结束的过场动画的播放器?","ko-kr":"플레이어의 시작 및 끝 컷씬?"}},{"key":"Properties.Insert.Executable","values":{"en-us":"Executable","zh-cn":"可执行文件","ko-kr":"실행"}},{"key":"Properties.Insert.Part","values":{"en-us":"Part Touched","zh-cn":"部分感动","ko-kr":"파트 감동"}},{"key":"Properties.Insert.PlayerAdded","values":{"en-us":"Player Entered","zh-cn":"玩家输入","ko-kr":"플레이어 ì²´ê²°"}},{"key":"Properties.Insert.PlayerDied","values":{"en-us":"Player Died","zh-cn":"球员死","ko-kr":"플레이어는 사망"}},{"key":"Properties.Insert.ScriptType","values":{"en-us":"Cutscene Script Type","zh-cn":"过场动画脚本类型","ko-kr":"컷씬 스크립트 유형"}},{"key":"Properties.Insert.Title","values":{"en-us":"Cutscene Saving","zh-cn":"过场动画保存","ko-kr":"컷씬 저장"}},{"key":"Properties.Point.Add","values":{"en-us":"Add Cutscene Point","zh-cn":"添加过场点","ko-kr":"컷씬 포인트 추가"}},{"key":"Properties.Point.EasingDirection","values":{"en-us":"Easing Direction","zh-cn":"宽松方向","ko-kr":"완화 ë°©í–¥"}},{"key":"Properties.Point.EasingStyle","values":{"en-us":"Easing Style","zh-cn":"宽松款式","ko-kr":"완화 스타일"}},{"key":"Properties.Point.FOV","values":{"en-us":"FOV","zh-cn":"FOV","ko-kr":"FOV"}},{"key":"Properties.Point.ImmediateSwitch","values":{"en-us":"Switch to Next Point when done?","zh-cn":"完成后切换到下一个点?","ko-kr":"완료되면 다음 포인트로 전환?"}},{"key":"Properties.Point.Title","values":{"en-us":"Configuration","zh-cn":"组态","ko-kr":"구성"}},{"key":"Properties.Point.Transition","values":{"en-us":"Transition","zh-cn":"过渡","ko-kr":"전이"}},{"key":"Rename.Done","values":{"en-us":"OK","zh-cn":"好","ko-kr":"확인"}},{"key":"Rename.Placeholder","values":{"en-us":"Type here...","zh-cn":"在此输入...","ko-kr":"여기에 ìž…ë ¥ ..."}},{"key":"Rename.Title","values":{"en-us":"Enter a new name.","zh-cn":"输入新的名称。","ko-kr":"새 이름을 입력합니다."}},{"key":"Warning.CutscenePoints","values":{"en-us":"You must at least have more than 1 cutscene points.","zh-cn":"您必须至少有1个多过场点。","ko-kr":"당신은 적어도 1 ê°œ 이상의 컷씬 포인트가 있어야합니다."}}] CutscenifyLocalization en-us 0 0 0 1 0 0 0 1 0 0 0 1 CutscenePartsEditMode null BezierCutsceneManager {7ABDF739-1E6B-47C4-90EF-17F517261C7A} true 0 true false Warning true null 1 false 0 0 true 0.180392161 0.180392161 0.180392161 0 0.105882362 0.164705887 0.207843155 0 0 false false 0 Main null null null null 0 0 0 0 null 0 false null 1 0 1 0 0 0 false 1 false 0 0 true 0.639215708 0.635294139 0.647058845 1 0.105882362 0.164705887 0.207843155 0 1 false false 0 Content null null null null 0 0 0 0 null 0 false null 1 0 1 0 0 0 true 1 false 0 0 true 0.639215708 0.635294139 0.647058845 1 0.105882362 0.164705887 0.207843155 0 1 false false rbxasset://textures/AvatarImporter/icon_error.png 1 1 1 0 0 0 0 0 0 ErrorIcon null null null null 0 30 0 20 null 0 0 false null 0 22 0 22 0 0 0 0 0 1 1 0 1 0 true 1 false 0 0 true 0.639215708 0.635294139 0.647058845 1 0.105882362 0.164705887 0.207843155 0 1 true false 3 0 1 ErrorTitle null null null null 0 62 0 20 false null 0 false null 1 -62 0 20 0 You may be a victim of software counterfeiting. 0.800000072 0.800000072 0.800000072 false 20 0 0 0 1 0 0 false 0 1 true 1 false 0 0 true 0.639215708 0.635294139 0.647058845 1 0.105882362 0.164705887 0.207843155 0 1 false false 4 0 1 Header null null null null 0 62 0 60 false null 0 false null 0 302 0 16 0 Wondering why you're seeing this message? 0.800000072 0.800000072 0.800000072 false 16 0 0 0 1 0 0 false 0 1 true 1 false 0 0 true 0.639215708 0.635294139 0.647058845 1 0.105882362 0.164705887 0.207843155 0 1 false false 3 0 1 Note null null null null 0 70 0 86 false null 0 false null 0 337 0 34 0 If you installed a version of Cutscenify NOT created by Inventfvl, you are using a stolen version of Cutscenify. 0.800000072 0.800000072 0.800000072 false 16 0 0 0 1 0 0 true 0 0 true 1 false 0 0 true 0.639215708 0.635294139 0.647058845 1 0.105882362 0.164705887 0.207843155 0 1 false false 3 0 1 Warning null null null null 0 62 1 -149 false null 0 false null 0 210 0 16 0 For your safety, please uninstall the plugin. 0.800000072 0.800000072 0.800000072 false 16 0 0 0 1 0 0 false 0 0 true 1 false 0 0 true 0.639215708 0.635294139 0.647058845 1 0.105882362 0.164705887 0.207843155 0 1 false false 0 Buttons null null null null 0 0 1 -64 null 0 false null 1 0 0 34 0 0 true 1 0 0 UIListLayout 0 21 2 1 true 0 0 true true 0.639215708 0.635294139 0.647058845 1 0.105882362 0.164705887 0.207843155 0 1 false false rbxasset://textures/AvatarImporter/button_avatarType.png 0 0.635294139 1 0 0 0 0 0 1 false OkButton null null null null 0 0 0 0 null 0 1 true false null 0 102 0 34 0 4 4 5 5 1 0 1 0 1 0 true 1 false 0 0 true 0.639215708 0.635294139 0.647058845 1 0.105882362 0.164705887 0.207843155 0 1 false false rbxasset://textures/AvatarImporter/button_avatarType_border.png 0 0.635294139 1 0 0 0 0 0 0 Border null null null null 0 0 0 0 null 0 1 false null 1 0 1 0 0 4 4 5 5 1 1 0 1 0 true 1 false 0 0 true 0.639215708 0.635294139 0.647058845 1 0.105882362 0.164705887 0.207843155 0 1 false false 3 0 1 TextLabel null null null null 0 0 0 0 false null 0 false null 1 0 1 0 0 OK 1 1 1 false 20 0 0 0 1 0 0 false 2 1 true 1 true 0 0 true true 0.639215708 0.635294139 0.647058845 1 0.105882362 0.164705887 0.207843155 0 1 false false 3 0 1 false Info null null null null 0 62 1 -123 false null 0 true false null 0 373 0 32 0 0 To use Cutscenify, download the official version here: https://www.roblox.com/library/4694596625/Cutscenify 1 1 1 false 16 0 0 0 1 0 0 true 0 0 true 1 false 1 1 true 1 1 1 1 0.105882362 0.164705887 0.207843155 0 1 false false 3 0 1 WarningText null null null null 0.930000007 0 0.949999988 0 false null 0 false null 0 200 0 50 0 Your copy of Cutscenify is not genuine. 1 1 1 false 20 0 0 0 1 0.300000012 0 false 2 1 false 1 true 0 true false RestartStudioWarning true null 1 false 0 0 true 0.180392161 0.180392161 0.180392161 0 0.105882362 0.164705887 0.207843155 0 1 false false 0 Warning null null null null 0 0 0 0 null 0 false null 0 245 0 87 0 0 false 1 1 0 UIListLayout 0 12 2 1 false 0 0 true 0.639215708 0.635294139 0.647058845 1 0.105882362 0.164705887 0.207843155 0 1 false false 1 Information null null null null 0 0 0 0 null 0 false null 1 0 0 32 0 0 true 1 0 1 UIListLayout 0 20 2 1 false 0 0 true 0.639215708 0.635294139 0.647058845 1 0.105882362 0.164705887 0.207843155 0 1 false false rbxasset://textures/DevConsole/Info.png 1 1 1 0 0 0 0 0 0 Icon null null null null 0 0 0 0 null 0 0 false null 0 32 0 32 0 0 0 0 0 1 1 0 1 0 true 1 false 0 0 true 0.639215708 0.635294139 0.647058845 1 0.105882362 0.164705887 0.207843155 0 1 false false 1 Texts null null null null 0 0 0 0 null 0 false null 1 -52 1 0 0 0 true 1 1 1 UIListLayout 0 8 2 1 false 0 0 true 0.639215708 0.635294139 0.647058845 1 0.105882362 0.164705887 0.207843155 0 1 false false 3 0 1 TextLabel null null null null 0 0 0 0 false null 0 false null 1 0 0.5625 14 0 Please restart studio for Cutscenify to work. 0.800000072 0.800000072 0.800000072 false 14 0 0 0 1 0 0 true 0 0 true 1 UIPadding 0 12 0 12 0 12 0 8 false 0 0 true 0.639215708 0.635294139 0.647058845 1 0.105882362 0.164705887 0.207843155 0 1 false false 2 Buttons null null null null 0 0 0 0 null 0 false null 1 0 0 23 0 0 true 1 true 0 0 true true 0.639215708 0.635294139 0.647058845 1 0.105882362 0.164705887 0.207843155 0 1 false false rbxasset://textures/StudioToolbox/RoundedBackground.png 0.180392161 0.180392161 0.180392161 0 0 0 0 0 2 false ok null null null null 0 0 0 0 null 0 1 true false null 0 72 1 0 0 3 3 13 13 1 0 1 0 1 0 true 1 false 0 0 true 0.639215708 0.635294139 0.647058845 1 0.105882362 0.164705887 0.207843155 0 1 false false rbxasset://textures/StudioToolbox/RoundedBorder.png 0.13333334 0.13333334 0.13333334 0 0 0 0 0 0 Border null null null null 0 0 0 0 null 0 1 false null 1 0 1 0 0 3 3 13 13 1 1 0 1 0 true 1 false 0 0 true 0.639215708 0.635294139 0.647058845 1 0.105882362 0.164705887 0.207843155 0 1 false false 3 0 1 TextLabel null null null null 0 0 0 -1 false null 0 false null 1 0 1 0 0 Ok 0.800000072 0.800000072 0.800000072 false 14 0 0 0 1 0 0 false 2 1 true 1 0 2 UIListLayout 0 8 2 1 roActivity_Wrapper {8C5D160D-5272-4347-8CD8-0985227963B1} Cutscene_DONOTUSE {F5731C51-A4E9-48F3-98C6-D7658E42C53A} NumberToEnum {51CEE989-6C89-424A-8B5E-EDD0435DBC7D} true LICENSE {76AFA0DA-6A0A-4191-BD10-D8AAFE4960E9} Libraries Roact {AE3C863C-9B63-47A4-B838-E3489AD59BC3} Config.spec {6680298A-2679-49A8-87EF-0AFDD4BF735C} SingleEventManager.spec {32687741-8659-4F8A-A150-92A3A479F043} RobloxRenderer.spec {FE245853-7C11-4502-A5DD-CCAA48D6CF6F} Binding {EC962514-19C4-410F-A0BA-182863ADD557} Symbol {7029828B-06CD-4CEE-A58E-C0A65ECEB5E4} createSignal.spec {7540925D-F6C8-42F8-9663-45BB4EECCD6C} invalidSetStateMessages {390A7195-B33C-4E86-A3FE-C20D08C3BA46} Type.spec {76EFD045-D30B-4470-9C3E-56B36DA02DAB} ElementUtils {EBFF765F-375F-4F0E-ABDB-35ABDF24DB26} getDefaultInstanceProperty {F61494E9-FB3A-47CB-A552-5B60824000D4} internalAssert {8F973906-467B-4BA9-88C4-496DD3CCC24E} ElementUtils.spec {CE94B21A-BB5F-4FCC-A73A-3C67686CB44B} createRef.spec {BB95F318-3464-4C0F-BE92-3DBA13172C00} GlobalConfig {5555DE40-D08F-4895-9A12-767823AEA517} oneChild {60E79BD1-2F88-4B2D-A687-58A97B4C4D77} None {ECD19360-CC11-4349-AF9E-D470E0D3876B} GlobalConfig.spec {2B4AEEB7-ED02-4ECB-A2B9-E6FD4A4256E7} SingleEventManager {06F063B9-A54D-4C8C-99A8-34866DF11FA8} assertDeepEqual.spec {15286D7B-1E31-4F91-B00C-7F0135AABA64} Type {20A29016-7796-4C15-911C-3B10A94E790F} ComponentLifecyclePhase {88E1B603-7905-447A-AA77-9911A3FF2510} Component {C9E4305E-F1EE-4AB3-B966-0FB4F602981A} " error(("Property validation failed: %s\n\n%s"):format( tostring(failureReason), self:getElementTraceback() or ""), 0) end end --[[ An internal method used by the reconciler to construct a new component instance and attach it to the given virtualNode. ]] function Component:__mount(reconciler, virtualNode) if config.internalTypeChecks then internalAssert(Type.of(self) == Type.StatefulComponentClass, "Invalid use of `__mount`") internalAssert(Type.of(virtualNode) == Type.VirtualNode, "Expected arg #2 to be of type VirtualNode") end local currentElement = virtualNode.currentElement local hostParent = virtualNode.hostParent -- Contains all the information that we want to keep from consumers of -- Roact, or even other parts of the codebase like the reconciler. local internalData = { reconciler = reconciler, virtualNode = virtualNode, componentClass = self, lifecyclePhase = ComponentLifecyclePhase.Init, } local instance = { [Type] = Type.StatefulComponentInstance, [InternalData] = internalData, } setmetatable(instance, self) virtualNode.instance = instance local props = currentElement.props if self.defaultProps ~= nil then props = assign({}, self.defaultProps, props) end instance:__validateProps(props) instance.props = props local newContext = assign({}, virtualNode.context) instance._context = newContext instance.state = assign({}, instance:__getDerivedState(instance.props, {})) if instance.init ~= nil then instance:init(instance.props) assign(instance.state, instance:__getDerivedState(instance.props, instance.state)) end -- It's possible for init() to redefine _context! virtualNode.context = instance._context internalData.lifecyclePhase = ComponentLifecyclePhase.Render local renderResult = instance:render() internalData.lifecyclePhase = ComponentLifecyclePhase.ReconcileChildren reconciler.updateVirtualNodeWithRenderResult(virtualNode, hostParent, renderResult) if instance.didMount ~= nil then internalData.lifecyclePhase = ComponentLifecyclePhase.DidMount instance:didMount() end if internalData.pendingState ~= nil then -- __update will handle pendingState, so we don't pass any new element or state instance:__update(nil, nil) end internalData.lifecyclePhase = ComponentLifecyclePhase.Idle end --[[ Internal method used by the reconciler to clean up any resources held by this component instance. ]] function Component:__unmount() if config.internalTypeChecks then internalAssert(Type.of(self) == Type.StatefulComponentInstance, "Invalid use of `__unmount`") end local internalData = self[InternalData] local virtualNode = internalData.virtualNode local reconciler = internalData.reconciler if self.willUnmount ~= nil then internalData.lifecyclePhase = ComponentLifecyclePhase.WillUnmount self:willUnmount() end for _, childNode in pairs(virtualNode.children) do reconciler.unmountVirtualNode(childNode) end end --[[ Internal method used by setState (to trigger updates based on state) and by the reconciler (to trigger updates based on props) Returns true if the update was completed, false if it was cancelled by shouldUpdate ]] function Component:__update(updatedElement, updatedState) if config.internalTypeChecks then internalAssert(Type.of(self) == Type.StatefulComponentInstance, "Invalid use of `__update`") internalAssert( Type.of(updatedElement) == Type.Element or updatedElement == nil, "Expected arg #1 to be of type Element or nil" ) internalAssert( typeof(updatedState) == "table" or updatedState == nil, "Expected arg #2 to be of type table or nil" ) end local internalData = self[InternalData] local componentClass = internalData.componentClass local newProps = self.props if updatedElement ~= nil then newProps = updatedElement.props if componentClass.defaultProps ~= nil then newProps = assign({}, componentClass.defaultProps, newProps) end self:__validateProps(newProps) end local updateCount = 0 repeat local finalState local pendingState = nil -- Consume any pending state we might have if internalData.pendingState ~= nil then pendingState = internalData.pendingState internalData.pendingState = nil end -- Consume a standard update to state or props if updatedState ~= nil or newProps ~= self.props then if pendingState == nil then finalState = updatedState or self.state else finalState = assign(pendingState, updatedState) end local derivedState = self:__getDerivedState(newProps, finalState) if derivedState ~= nil then finalState = assign({}, finalState, derivedState) end updatedState = nil else finalState = pendingState end if not self:__resolveUpdate(newProps, finalState) then -- If the update was short-circuited, bubble the result up to the caller return false end updateCount = updateCount + 1 if updateCount > MAX_PENDING_UPDATES then error(tooManyUpdatesMessage:format(tostring(internalData.componentClass)), 3) end until internalData.pendingState == nil return true end --[[ Internal method used by __update to apply new props and state Returns true if the update was completed, false if it was cancelled by shouldUpdate ]] function Component:__resolveUpdate(incomingProps, incomingState) if config.internalTypeChecks then internalAssert(Type.of(self) == Type.StatefulComponentInstance, "Invalid use of `__resolveUpdate`") end local internalData = self[InternalData] local virtualNode = internalData.virtualNode local reconciler = internalData.reconciler local oldProps = self.props local oldState = self.state if incomingProps == nil then incomingProps = oldProps end if incomingState == nil then incomingState = oldState end if self.shouldUpdate ~= nil then internalData.lifecyclePhase = ComponentLifecyclePhase.ShouldUpdate local continueWithUpdate = self:shouldUpdate(incomingProps, incomingState) if not continueWithUpdate then internalData.lifecyclePhase = ComponentLifecyclePhase.Idle return false end end if self.willUpdate ~= nil then internalData.lifecyclePhase = ComponentLifecyclePhase.WillUpdate self:willUpdate(incomingProps, incomingState) end internalData.lifecyclePhase = ComponentLifecyclePhase.Render self.props = incomingProps self.state = incomingState local renderResult = virtualNode.instance:render() internalData.lifecyclePhase = ComponentLifecyclePhase.ReconcileChildren reconciler.updateVirtualNodeWithRenderResult(virtualNode, virtualNode.hostParent, renderResult) if self.didUpdate ~= nil then internalData.lifecyclePhase = ComponentLifecyclePhase.DidUpdate self:didUpdate(oldProps, oldState) end internalData.lifecyclePhase = ComponentLifecyclePhase.Idle return true end return Component]]> createFragment {F9DCBF9B-DC7C-433E-B9B1-E29142A30166} assertDeepEqual {268CE9F7-FC87-4976-9C54-8CEBFD0E4CB5} Component.spec didUpdate.spec {9D930F50-C46C-4F55-A132-923D8501D9F7} context.spec {D8E85031-8F7F-4A44-855C-1ABF43D71D61} getDerivedStateFromProps.spec {122C2D73-4318-4B4B-8C95-50018412E4D8} defaultProps.spec {2C57AC81-7B3D-4532-9F01-93FE53AD2874} extend.spec {C0C112EE-12A6-4EB0-9634-E6E6681DC598} render.spec {D1BCF697-1BC8-4FD8-BBB1-959D220DC4B9} shouldUpdate.spec {F7C6C601-1561-48D3-B1C9-DBB80117E4D0} init.spec {8154E545-B6F2-48CD-BBAD-2FE29875D484} willUnmount.spec {EAE248EE-E30F-4A7F-9D85-B3007B6B11D3} getElementTraceback.spec {943FEBAD-ECE5-403B-852A-B19F5D8AC323} didMount.spec {598FDBCC-BB8C-43E0-8B2C-178AD92D8BEB} validateProps.spec {0CA38E36-534A-4B22-9B37-B6701581F676} willUpdate.spec {C0FBFD21-65B5-4A82-8C8B-E319AA9B6B05} setState.spec {C22A5EF1-8C18-4097-A58F-79142B0CF6F2} getDefaultInstanceProperty.spec {3BB15728-065B-4669-BA0E-852AF42DA5F3} createElement.spec {8644A317-242D-4DAF-955B-198B6B28105E} createReconciler.spec {D6C58FEB-63AA-4A11-AE03-F01EE3198337} Symbol.spec {55956CD6-9149-4860-870E-4A4533773F66} Config {97B7798C-7921-4910-9532-1530F9AC79B2} createReconciler {F58E336B-93D0-486F-92B0-0CA9477C574A} " ), 0) end end --[[ Unmounts the given virtual node and releases any held resources. ]] function unmountVirtualNode(virtualNode) if config.internalTypeChecks then internalAssert(Type.of(virtualNode) == Type.VirtualNode, "Expected arg #1 to be of type VirtualNode") end local kind = ElementKind.of(virtualNode.currentElement) if kind == ElementKind.Host then renderer.unmountHostNode(reconciler, virtualNode) elseif kind == ElementKind.Function then for _, childNode in pairs(virtualNode.children) do unmountVirtualNode(childNode) end elseif kind == ElementKind.Stateful then virtualNode.instance:__unmount() elseif kind == ElementKind.Portal then for _, childNode in pairs(virtualNode.children) do unmountVirtualNode(childNode) end elseif kind == ElementKind.Fragment then for _, childNode in pairs(virtualNode.children) do unmountVirtualNode(childNode) end else error(("Unknown ElementKind %q"):format(tostring(kind), 2)) end end local function updateFunctionVirtualNode(virtualNode, newElement) local children = newElement.component(newElement.props) updateVirtualNodeWithRenderResult(virtualNode, virtualNode.hostParent, children) return virtualNode end local function updatePortalVirtualNode(virtualNode, newElement) local oldElement = virtualNode.currentElement local oldTargetHostParent = oldElement.props.target local targetHostParent = newElement.props.target assert(renderer.isHostObject(targetHostParent), "Expected target to be host object") if targetHostParent ~= oldTargetHostParent then return replaceVirtualNode(virtualNode, newElement) end local children = newElement.props[Children] updateVirtualNodeWithChildren(virtualNode, targetHostParent, children) return virtualNode end local function updateFragmentVirtualNode(virtualNode, newElement) updateVirtualNodeWithChildren(virtualNode, virtualNode.hostParent, newElement.elements) return virtualNode end --[[ Update the given virtual node using a new element describing what it should transform into. `updateVirtualNode` will return a new virtual node that should replace the passed in virtual node. This is because a virtual node can be updated with an element referencing a different component! In that case, `updateVirtualNode` will unmount the input virtual node, mount a new virtual node, and return it in this case, while also issuing a warning to the user. ]] function updateVirtualNode(virtualNode, newElement, newState) if config.internalTypeChecks then internalAssert(Type.of(virtualNode) == Type.VirtualNode, "Expected arg #1 to be of type VirtualNode") end if config.typeChecks then assert( Type.of(newElement) == Type.Element or typeof(newElement) == "boolean" or newElement == nil, "Expected arg #2 to be of type Element, boolean, or nil" ) end -- If nothing changed, we can skip this update if virtualNode.currentElement == newElement and newState == nil then return virtualNode end if typeof(newElement) == "boolean" or newElement == nil then unmountVirtualNode(virtualNode) return nil end if virtualNode.currentElement.component ~= newElement.component then return replaceVirtualNode(virtualNode, newElement) end local kind = ElementKind.of(newElement) local shouldContinueUpdate = true if kind == ElementKind.Host then virtualNode = renderer.updateHostNode(reconciler, virtualNode, newElement) elseif kind == ElementKind.Function then virtualNode = updateFunctionVirtualNode(virtualNode, newElement) elseif kind == ElementKind.Stateful then shouldContinueUpdate = virtualNode.instance:__update(newElement, newState) elseif kind == ElementKind.Portal then virtualNode = updatePortalVirtualNode(virtualNode, newElement) elseif kind == ElementKind.Fragment then virtualNode = updateFragmentVirtualNode(virtualNode, newElement) else error(("Unknown ElementKind %q"):format(tostring(kind), 2)) end -- Stateful components can abort updates via shouldUpdate. If that -- happens, we should stop doing stuff at this point. if not shouldContinueUpdate then return virtualNode end virtualNode.currentElement = newElement return virtualNode end --[[ Constructs a new virtual node but not does mount it. ]] local function createVirtualNode(element, hostParent, hostKey, context) if config.internalTypeChecks then internalAssert(renderer.isHostObject(hostParent) or hostParent == nil, "Expected arg #2 to be a host object") internalAssert(typeof(context) == "table" or context == nil, "Expected arg #4 to be of type table or nil") end if config.typeChecks then assert(hostKey ~= nil, "Expected arg #3 to be non-nil") assert( Type.of(element) == Type.Element or typeof(element) == "boolean", "Expected arg #1 to be of type Element or boolean" ) end return { [Type] = Type.VirtualNode, currentElement = element, depth = 1, children = {}, hostParent = hostParent, hostKey = hostKey, context = context, -- This copy of context is useful if the element gets replaced -- with an element of a different component type parentContext = context, } end local function mountFunctionVirtualNode(virtualNode) local element = virtualNode.currentElement local children = element.component(element.props) updateVirtualNodeWithRenderResult(virtualNode, virtualNode.hostParent, children) end local function mountPortalVirtualNode(virtualNode) local element = virtualNode.currentElement local targetHostParent = element.props.target local children = element.props[Children] assert(renderer.isHostObject(targetHostParent), "Expected target to be host object") updateVirtualNodeWithChildren(virtualNode, targetHostParent, children) end local function mountFragmentVirtualNode(virtualNode) local element = virtualNode.currentElement local children = element.elements updateVirtualNodeWithChildren(virtualNode, virtualNode.hostParent, children) end --[[ Constructs a new virtual node and mounts it, but does not place it into the tree. ]] function mountVirtualNode(element, hostParent, hostKey, context) if config.internalTypeChecks then internalAssert(renderer.isHostObject(hostParent) or hostParent == nil, "Expected arg #2 to be a host object") internalAssert(typeof(context) == "table" or context == nil, "Expected arg #4 to be of type table or nil") end if config.typeChecks then assert(hostKey ~= nil, "Expected arg #3 to be non-nil") assert( Type.of(element) == Type.Element or typeof(element) == "boolean", "Expected arg #1 to be of type Element or boolean" ) end -- Boolean values render as nil to enable terse conditional rendering. if typeof(element) == "boolean" then return nil end local kind = ElementKind.of(element) local virtualNode = createVirtualNode(element, hostParent, hostKey, context) if kind == ElementKind.Host then renderer.mountHostNode(reconciler, virtualNode) elseif kind == ElementKind.Function then mountFunctionVirtualNode(virtualNode) elseif kind == ElementKind.Stateful then element.component:__mount(reconciler, virtualNode) elseif kind == ElementKind.Portal then mountPortalVirtualNode(virtualNode) elseif kind == ElementKind.Fragment then mountFragmentVirtualNode(virtualNode) else error(("Unknown ElementKind %q"):format(tostring(kind), 2)) end return virtualNode end --[[ Constructs a new Roact virtual tree, constructs a root node for it, and mounts it. ]] local function mountVirtualTree(element, hostParent, hostKey) if config.typeChecks then assert(Type.of(element) == Type.Element, "Expected arg #1 to be of type Element") assert(renderer.isHostObject(hostParent) or hostParent == nil, "Expected arg #2 to be a host object") end if hostKey == nil then hostKey = "RoactTree" end local tree = { [Type] = Type.VirtualTree, [InternalData] = { -- The root node of the tree, which starts into the hierarchy of -- Roact component instances. rootNode = nil, mounted = true, }, } tree[InternalData].rootNode = mountVirtualNode(element, hostParent, hostKey) return tree end --[[ Unmounts the virtual tree, freeing all of its resources. No further operations should be done on the tree after it's been unmounted, as indicated by its the `mounted` field. ]] local function unmountVirtualTree(tree) local internalData = tree[InternalData] if config.typeChecks then assert(Type.of(tree) == Type.VirtualTree, "Expected arg #1 to be a Roact handle") assert(internalData.mounted, "Cannot unmounted a Roact tree that has already been unmounted") end internalData.mounted = false if internalData.rootNode ~= nil then unmountVirtualNode(internalData.rootNode) end end --[[ Utility method for updating the root node of a virtual tree given a new element. ]] local function updateVirtualTree(tree, newElement) local internalData = tree[InternalData] if config.typeChecks then assert(Type.of(tree) == Type.VirtualTree, "Expected arg #1 to be a Roact handle") assert(Type.of(newElement) == Type.Element, "Expected arg #2 to be a Roact Element") end internalData.rootNode = updateVirtualNode(internalData.rootNode, newElement) return tree end reconciler = { mountVirtualTree = mountVirtualTree, unmountVirtualTree = unmountVirtualTree, updateVirtualTree = updateVirtualTree, createVirtualNode = createVirtualNode, mountVirtualNode = mountVirtualNode, unmountVirtualNode = unmountVirtualNode, updateVirtualNode = updateVirtualNode, updateVirtualNodeWithChildren = updateVirtualNodeWithChildren, updateVirtualNodeWithRenderResult = updateVirtualNodeWithRenderResult, } return reconciler end return createReconciler]]> Binding.spec {25A4759E-6052-4B46-BA63-76DA2B1DADF3} base binding local length = word:map(string.len) local lengthSpy = createSpy() local disconnectLength = Binding.subscribe(length, lengthSpy.value) -- binding -> binding -> base binding local isEvenLength = length:map(function(value) return value % 2 == 0 end) local isEvenLengthSpy = createSpy() local disconnectIsEvenLength = Binding.subscribe(isEvenLength, isEvenLengthSpy.value) expect(wordSpy.callCount).to.equal(0) expect(lengthSpy.callCount).to.equal(0) expect(isEvenLengthSpy.callCount).to.equal(0) updateWord("nice") expect(wordSpy.callCount).to.equal(1) wordSpy:assertCalledWith("nice") expect(lengthSpy.callCount).to.equal(1) lengthSpy:assertCalledWith(4) expect(isEvenLengthSpy.callCount).to.equal(1) isEvenLengthSpy:assertCalledWith(true) disconnectWord() disconnectLength() disconnectIsEvenLength() updateWord("goodbye") expect(wordSpy.callCount).to.equal(1) expect(isEvenLengthSpy.callCount).to.equal(1) expect(lengthSpy.callCount).to.equal(1) end) it("should throw when updated directly", function() local source = Binding.create(1) local mapped = source:map(function(v) return v end) expect(function() Binding.update(mapped, 5) end).to.throw() end) end) describe("Binding.join", function() it("should have getValue", function() local binding1 = Binding.create(1) local binding2 = Binding.create(2) local binding3 = Binding.create(3) local joinedBinding = Binding.join({ binding1, binding2, foo = binding3, }) local bindingValue = joinedBinding:getValue() expect(bindingValue).to.be.a("table") expect(bindingValue[1]).to.equal(1) expect(bindingValue[2]).to.equal(2) expect(bindingValue.foo).to.equal(3) end) it("should update when any one of the subscribed bindings updates", function() local binding1, update1 = Binding.create(1) local binding2, update2 = Binding.create(2) local binding3, update3 = Binding.create(3) local joinedBinding = Binding.join({ binding1, binding2, foo = binding3, }) local spy = createSpy() Binding.subscribe(joinedBinding, spy.value) expect(spy.callCount).to.equal(0) update1(3) expect(spy.callCount).to.equal(1) local args = spy:captureValues("value") expect(args.value).to.be.a("table") expect(args.value[1]).to.equal(3) expect(args.value[2]).to.equal(2) expect(args.value["foo"]).to.equal(3) update2(4) expect(spy.callCount).to.equal(2) args = spy:captureValues("value") expect(args.value).to.be.a("table") expect(args.value[1]).to.equal(3) expect(args.value[2]).to.equal(4) expect(args.value["foo"]).to.equal(3) update3(8) expect(spy.callCount).to.equal(3) args = spy:captureValues("value") expect(args.value).to.be.a("table") expect(args.value[1]).to.equal(3) expect(args.value[2]).to.equal(4) expect(args.value["foo"]).to.equal(8) end) it("should disconnect from all upstream bindings", function() local binding1, update1 = Binding.create(1) local binding2, update2 = Binding.create(2) local joined = Binding.join({binding1, binding2}) local spy = createSpy() local disconnect = Binding.subscribe(joined, spy.value) expect(spy.callCount).to.equal(0) update1(3) expect(spy.callCount).to.equal(1) update2(3) expect(spy.callCount).to.equal(2) disconnect() update1(4) expect(spy.callCount).to.equal(2) update2(2) expect(spy.callCount).to.equal(2) local value = joined:getValue() expect(value[1]).to.equal(4) expect(value[2]).to.equal(2) end) it("should be okay with calling disconnect multiple times", function() local joined = Binding.join({}) local disconnect = Binding.subscribe(joined, function() end) disconnect() disconnect() end) it("should throw if updated directly", function() local joined = Binding.join({}) expect(function() Binding.update(joined, 0) end) end) it("should throw when a non-table value is passed", function() GlobalConfig.scoped({ typeChecks = true, }, function() expect(function() Binding.join("hi") end).to.throw() end) end) it("should throw when a non-binding value is passed via table", function() GlobalConfig.scoped({ typeChecks = true, }, function() expect(function() local binding = Binding.create(123) Binding.join({ binding, "abcde", }) end).to.throw() end) end) end) end]]> oneChild.spec {0F451FF5-B400-4B12-A1E2-64A00BD6B46F} createReconcilerCompat.spec {74D3BA37-8A75-46E3-8D1A-B565061AABA3} init.spec {1170AD9E-A5E9-4656-9AF8-4A1C5F30E59F} PropMarkers Change {3015E1AD-A091-4CC9-8691-789EFB05212F} Children {73D66EE7-7BD7-43DD-AE55-37D7B71148CF} Event.spec {6B01B4E4-C504-490B-9098-2FAD122CA47E} Ref {5A4114B0-DA29-4844-BB04-81EEB8175D98} Event {1535B6D4-50A4-45C8-8875-D70D95D6A92A} Change.spec {8DF1F4DF-D91E-4293-9AEB-151157B5E5FE} assign.spec {867C2B59-376A-4350-861F-419E6B3AC85F} createRef {4FF513F6-45D3-4164-B57A-E71CE505D5A2} createFragment.spec {8EF34727-E010-4267-BEF0-FB4E7294386B} strict.spec {1752EE74-A4F0-4F5B-ABFF-AFC70D8D4812} Logging {F532A272-3294-4167-BB21-C8356863572C} 0 then table.insert(outputBuffer, ("\tErrors (%d) {"):format(errorCount)) table.insert(outputBuffer, indentLines(self.errors, 2)) table.insert(outputBuffer, "\t}") end if warningCount > 0 then table.insert(outputBuffer, ("\tWarnings (%d) {"):format(warningCount)) table.insert(outputBuffer, indentLines(self.warnings, 2)) table.insert(outputBuffer, "\t}") end if infosCount > 0 then table.insert(outputBuffer, ("\tInfos (%d) {"):format(infosCount)) table.insert(outputBuffer, indentLines(self.infos, 2)) table.insert(outputBuffer, "\t}") end table.insert(outputBuffer, "}") return table.concat(outputBuffer, "\n") end local function createLogInfo() local logInfo = { errors = {}, warnings = {}, infos = {}, } setmetatable(logInfo, logInfoMetatable) return logInfo end local Logging = {} --[[ Invokes `callback`, capturing all output that happens during its execution. Output will not go to stdout or stderr and will instead be put into a LogInfo object that is returned. If `callback` throws, the error will be bubbled up to the caller of `Logging.capture`. ]] function Logging.capture(callback) local collector = createLogInfo() local wasOutputEnabled = outputEnabled outputEnabled = false collectors[collector] = true local success, result = pcall(callback) collectors[collector] = nil outputEnabled = wasOutputEnabled assert(success, result) return collector end --[[ Issues a warning with an automatically attached stack trace. ]] function Logging.warn(messageTemplate, ...) local message = messageTemplate:format(...) for collector in pairs(collectors) do table.insert(collector.warnings, message) end -- debug.traceback inserts a leading newline, so we trim it here local trace = debug.traceback("", 2):sub(2) local fullMessage = ("%s\n%s"):format(message, indent(trace, 1)) if outputEnabled then warn(fullMessage) end end --[[ Issues a warning like `Logging.warn`, but only outputs once per call site. This is useful for marking deprecated functions that might be called a lot; using `warnOnce` instead of `warn` will reduce output noise while still correctly marking all call sites. ]] function Logging.warnOnce(messageTemplate, ...) local trace = debug.traceback() if onceUsedLocations[trace] then return end onceUsedLocations[trace] = true Logging.warn(messageTemplate, ...) end return Logging]]> createSignal {DDE24AA0-BBE8-4CD0-B3D3-C66D4759619A} NoopRenderer {460C29C0-2E98-4186-BD0E-C20FACEE3780} createReconcilerCompat {FD696713-29E7-457A-9DA9-B938EA013848} PureComponent {C32DC4D2-7CF2-4819-B93F-5544C2DEE82A} ElementKind.spec {7168031A-6E89-47B3-BC45-990DC7E18391} assign {198BC719-780A-4D49-8A8E-0B92A243ED5D} ElementKind {BED460A8-4045-4337-A537-A4DB7151CFAA} RobloxRenderer {5CDB3E28-01B6-4EE6-A281-82DE672B93C0} " end local fullMessage = updatePropsError:format(errorMessage, source) error(fullMessage, 0) end end if virtualNode.bindings == nil then virtualNode.bindings = {} end virtualNode.bindings[key] = Binding.subscribe(newBinding, updateBoundProperty) updateBoundProperty(newBinding:getValue()) end local function detachAllBindings(virtualNode) if virtualNode.bindings ~= nil then for _, disconnect in pairs(virtualNode.bindings) do disconnect() end end end local function applyProp(virtualNode, key, newValue, oldValue) if newValue == oldValue then return end if key == Ref or key == Children then -- Refs and children are handled in a separate pass return end local internalKeyType = Type.of(key) if internalKeyType == Type.HostEvent or internalKeyType == Type.HostChangeEvent then if virtualNode.eventManager == nil then virtualNode.eventManager = SingleEventManager.new(virtualNode.hostObject) end local eventName = key.name if internalKeyType == Type.HostChangeEvent then virtualNode.eventManager:connectPropertyChange(eventName, newValue) else virtualNode.eventManager:connectEvent(eventName, newValue) end return end local newIsBinding = Type.of(newValue) == Type.Binding local oldIsBinding = Type.of(oldValue) == Type.Binding if oldIsBinding then removeBinding(virtualNode, key) end if newIsBinding then attachBinding(virtualNode, key, newValue) else setRobloxInstanceProperty(virtualNode.hostObject, key, newValue) end end local function applyProps(virtualNode, props) for propKey, value in pairs(props) do applyProp(virtualNode, propKey, value, nil) end end local function updateProps(virtualNode, oldProps, newProps) -- Apply props that were added or updated for propKey, newValue in pairs(newProps) do local oldValue = oldProps[propKey] applyProp(virtualNode, propKey, newValue, oldValue) end -- Clean up props that were removed for propKey, oldValue in pairs(oldProps) do local newValue = newProps[propKey] if newValue == nil then applyProp(virtualNode, propKey, nil, oldValue) end end end local RobloxRenderer = {} function RobloxRenderer.isHostObject(target) return typeof(target) == "Instance" end function RobloxRenderer.mountHostNode(reconciler, virtualNode) local element = virtualNode.currentElement local hostParent = virtualNode.hostParent local hostKey = virtualNode.hostKey if config.internalTypeChecks then internalAssert(ElementKind.of(element) == ElementKind.Host, "Element at given node is not a host Element") end if config.typeChecks then assert(element.props.Name == nil, "Name can not be specified as a prop to a host component in Roact.") assert(element.props.Parent == nil, "Parent can not be specified as a prop to a host component in Roact.") end local instance = Instance.new(element.component) virtualNode.hostObject = instance local success, errorMessage = xpcall(function() applyProps(virtualNode, element.props) end, identity) if not success then local source = element.source if source == nil then source = "" end local fullMessage = applyPropsError:format(errorMessage, source) error(fullMessage, 0) end instance.Name = tostring(hostKey) local children = element.props[Children] if children ~= nil then reconciler.updateVirtualNodeWithChildren(virtualNode, virtualNode.hostObject, children) end instance.Parent = hostParent virtualNode.hostObject = instance applyRef(element.props[Ref], instance) if virtualNode.eventManager ~= nil then virtualNode.eventManager:resume() end end function RobloxRenderer.unmountHostNode(reconciler, virtualNode) local element = virtualNode.currentElement applyRef(element.props[Ref], nil) for _, childNode in pairs(virtualNode.children) do reconciler.unmountVirtualNode(childNode) end detachAllBindings(virtualNode) virtualNode.hostObject:Destroy() end function RobloxRenderer.updateHostNode(reconciler, virtualNode, newElement) local oldProps = virtualNode.currentElement.props local newProps = newElement.props if virtualNode.eventManager ~= nil then virtualNode.eventManager:suspend() end -- If refs changed, detach the old ref and attach the new one if oldProps[Ref] ~= newProps[Ref] then applyRef(oldProps[Ref], nil) applyRef(newProps[Ref], virtualNode.hostObject) end local success, errorMessage = xpcall(function() updateProps(virtualNode, oldProps, newProps) end, identity) if not success then local source = newElement.source if source == nil then source = "" end local fullMessage = updatePropsError:format(errorMessage, source) error(fullMessage, 0) end local children = newElement.props[Children] if children ~= nil or oldProps[Children] ~= nil then reconciler.updateVirtualNodeWithChildren(virtualNode, virtualNode.hostObject, children) end if virtualNode.eventManager ~= nil then virtualNode.eventManager:resume() end return virtualNode end return RobloxRenderer ]]> createElement {2F1868C8-02AA-4307-A5CB-8A540C2B8761} strict {E8516619-16F9-4F5C-B9DE-882ADCB7A1C5} Portal {CD0D5112-8B1C-4560-BE76-870E0906A892} createSpy {4E24A439-6F58-43B4-B7C9-A1ACC2FCEAFB} PureComponent.spec {AFE410A5-9BC1-47E0-81AC-3992D4BF7E82} createSpy.spec {4C0DF357-99CE-4B93-808B-4A33B0778B63} RoactRodux {66341D68-8F04-48E6-B065-F5C0709DE822} connect {C7534E44-36D8-4707-9248-E64AF021070A} partialProps OR () -> (storeState, props) -> partialProps mapDispatchToProps: (dispatch) -> partialProps ]] local function connect(mapStateToPropsOrThunk, mapDispatchToProps) local connectTrace = debug.traceback() if mapStateToPropsOrThunk ~= nil then assert(typeof(mapStateToPropsOrThunk) == "function", "mapStateToProps must be a function or nil!") else mapStateToPropsOrThunk = noop end if mapDispatchToProps ~= nil then assert(typeof(mapDispatchToProps) == "function", "mapDispatchToProps must be a function or nil!") else mapDispatchToProps = noop end return function(innerComponent) if innerComponent == nil then local message = formatMessage({ "connect returns a function that must be passed a component.", "Check the connection at:", "%s", }, { connectTrace, }) error(message, 2) end local componentName = ("RoduxConnection(%s)"):format(tostring(innerComponent)) local Connection = Roact.Component:extend(componentName) function Connection.getDerivedStateFromProps(nextProps, prevState) if prevState.stateUpdater ~= nil then return prevState.stateUpdater(nextProps, prevState) end end function Connection:init() self.store = getStore(self) if self.store == nil then local message = formatMessage({ "Cannot initialize Roact-Rodux connection without being a descendent of StoreProvider!", "Tried to wrap component %q", "Make sure there is a StoreProvider above this component in the tree.", }, { tostring(innerComponent), }) error(message) end local storeState = self.store:getState() local mapStateToProps = mapStateToPropsOrThunk local mappedStoreState = mapStateToProps(storeState, self.props) -- mapStateToPropsOrThunk can return a function instead of a state -- value. In this variant, we keep that value as mapStateToProps -- instead of the original mapStateToProps. This matches react-redux -- and enables connectors to keep instance-level state. if typeof(mappedStoreState) == "function" then mapStateToProps = mappedStoreState mappedStoreState = mapStateToProps(storeState, self.props) end if mappedStoreState ~= nil and typeof(mappedStoreState) ~= "table" then local message = formatMessage({ "mapStateToProps must either return a table, or return another function that returns a table.", "Instead, it returned %q, which is of type %s.", }, { tostring(mappedStoreState), typeof(mappedStoreState), }) error(message) end local mappedStoreDispatch = mapDispatchToProps(function(...) return self.store:dispatch(...) end) local stateUpdater = makeStateUpdater(self.store) self.state = { -- Combines props, mappedStoreDispatch, and the result of -- mapStateToProps into propsForChild. Stored in state so that -- getDerivedStateFromProps can access it. stateUpdater = stateUpdater, -- Used by the store changed connection and stateUpdater to -- construct propsForChild. mapStateToProps = mapStateToProps, -- Used by stateUpdater to construct propsForChild. mappedStoreDispatch = mappedStoreDispatch, -- Passed directly into the component that Connection is -- wrapping. propsForChild = nil, } local extraState = stateUpdater(self.props, self.state, mappedStoreState) for key, value in pairs(extraState) do self.state[key] = value end end function Connection:didMount() self.storeChangedConnection = self.store.changed:connect(function(storeState) self:setState(function(prevState, props) local mappedStoreState = prevState.mapStateToProps(storeState, props) -- We run this check here so that we only check shallow -- equality with the result of mapStateToProps, and not the -- other props that could be passed through the connector. if shallowEqual(mappedStoreState, prevState.mappedStoreState) then return nil end return prevState.stateUpdater(props, prevState, mappedStoreState) end) end) end function Connection:willUnmount() self.storeChangedConnection:disconnect() end function Connection:render() return Roact.createElement(innerComponent, self.state.propsForChild) end return Connection end end return connect]]> connect.spec {9C8023FE-F138-463F-AC72-B7CE7FC91BC0} Symbol {F63684AE-5C0C-4E07-A462-5E9BC7CA5597} getStore.spec {3924E78A-66E4-4772-82DB-BEAF6BC15D77} Symbol.spec {15B3E378-5764-4609-9D7D-1AA0FE08E38B} shallowEqual.spec {6CDB6AE4-321B-48C6-921C-AE38299AEE56} getStore {60F88FB9-5988-4CEE-9BAC-A32FC7B46A13} shallowEqual {1E58BF2D-3B37-4058-98FF-F43EA9E1B673} StoreProvider.spec {202BCBE9-A575-4858-9FBE-D7327251F891} join {22DFE5CB-B548-47FC-9DF6-C781C60FAB00} storeKey {F574A667-B6BA-4002-987D-42D311146517} StoreProvider {161B15AB-D119-437F-B26E-2F6C3EB0E608} Rodux {BFEE71AF-BB9B-441F-BAF6-084B3720D0E9} NoYield {97102C40-7C6D-42D1-8739-23749F7D9313} Signal {BFDB7229-E690-46C5-B52A-A45470B37D01} Store {6ADD3E82-25AC-4A3D-B4C6-7AEF4A013B6F} combineReducers {04BD0951-0E7D-4A1C-8078-D7983F8DCED6} createReducer {FF51F9DA-7322-42F3-BCA7-49DCEB2A5D81} loggerMiddleware {A0309063-C94D-44C6-AB12-456A8D83E231} thunkMiddleware {A40B0873-54BD-4A68-BEAA-2D939EBAF76C} Cryo {AF4C468D-52ED-45FB-AE20-FC779073E8AC} Dictionary {C8CC18B8-E258-4858-B217-F42356E2413B} fold {0C84A04E-1025-4D56-9E3D-650B7A123B7A} join {F1F4C3E7-EDE5-4776-ADF1-A16AD83B9DF6} keys {2185942D-DEE1-4A14-BD88-6B3B3193B153} values {214CF1F1-2628-4A80-B16D-AC9EECFACE02} List {32DA42EB-E83C-488C-B401-7A034493DCB2} filter {2741BE23-D1DC-4397-B1CE-E465F1B85217} filterMap {33FB701E-AD7E-4F3F-8AF5-6711C6A1F0A3} find {075EEA00-CFE4-4E42-B256-C988D01562E5} foldLeft {14722C2C-668B-49DC-8B08-1BBFA7571F4D} foldRight {7B8E6918-1E4D-469E-99FA-04BD4A02609A} getRange {DF57536E-8F76-4BDD-AD36-2E8ABBD34707} join {DB195D2D-5868-4283-B66F-DA64E1BF0F5A} map {BF21CE28-25AA-423D-A3A8-4C7396A7C8F5} removeIndex {CFDBDD4F-D02D-42E9-90E8-74BD9836F764} removeRange {C0A25188-492D-4861-8505-8A4560C4AC9E} removeValue {1102FBB2-CA5A-45FC-A742-261112AB096F} reverse {EA39D41B-6213-449E-9DC5-B841F2BDF5BE} None {7B588F95-63D0-4ECC-B519-D64F1D6CE959} FakePluginGui {546D406E-26C7-4F5A-B107-9AAC4CD21405} mount {7B782405-E1AE-464B-9C1C-2B257410F279} unmount {C135DCD3-66D2-450E-B0CE-DE095FCD1931} FFlag {8A9A82A5-8A65-41A9-A33D-A447C0CF5866} get {4A1EFB15-E8B5-4FAF-BBEC-63C15D32142A} list {C4CF0367-B328-410D-A6C9-7A7EB629C502} define {1D602ACA-0225-4807-97E7-1EC60D62CFCA} Romoji {63915304-8AAA-4CA6-8993-23B0308FBD7C} new {80C77F67-88CF-426A-8A51-64924D667D2C}