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}