if SERVER then return end local resolution = blue_prints.getScreenResolution() local run_once_at_start = false local folder_states = {} -- Track collapsed state of folders local button_height = 45 -- Function to move blueprints from a folder to root local function moveFilesToRoot(folderPath) if folderPath == "[Root Directory]" then return true end local fullFolderPath = blue_prints.normalizePath(blue_prints.save_path .. "/" .. folderPath) local rootPath = blue_prints.normalizePath(blue_prints.save_path) local files = File.GetFiles(fullFolderPath) local success = true if files then for _, filepath in ipairs(files) do if string.match(filepath, "%.txt$") then -- Read file content local content = blue_prints.readFileContents(filepath) if content then -- Get just the filename local filename = filepath:match("([^/\\]+)$") -- Create new path in root local newPath = blue_prints.normalizePath(rootPath .. "/" .. filename) -- Write to new location if not blue_prints.writeFile(newPath, content) then success = false end end end end end return success end -- Function to delete a folder and its contents local function deleteFolder(folderPath) if folderPath == "[Root Directory]" then return false end local fullFolderPath = blue_prints.normalizePath(blue_prints.save_path .. "/" .. folderPath) -- Move all files to root first if not moveFilesToRoot(folderPath) then return false end -- Try to delete the folder local success = pcall(function() File.DeleteDirectory(fullFolderPath) end) -- If first attempt fails, try alternate path if not success then local altPath = fullFolderPath:gsub("LocalMods/", "local_mods/") success = pcall(function() File.DeleteDirectory(altPath) end) end return success end local function count_blueprints_in_folder(folderPath) local files = File.GetFiles(folderPath) local count = 0 if files then for _, filepath in ipairs(files) do if string.match(filepath, "%.txt$") then count = count + 1 end end end return count end local function formatFolderHeaderText(folderName, isExpanded, blueprintCount) return string.format("%s %s (%d blueprints)", isExpanded and "▼" or "▶", folderName, blueprintCount) end local function generate_load_gui() blue_prints.current_gui_page = GUI.Frame(GUI.RectTransform(Vector2(1, 1), blue_prints.gui_button_frame.RectTransform, GUI.Anchor.Center), nil) blue_prints.current_gui_page.CanBeFocused = false blue_prints.current_gui_page.Visible = false -- Background close button local closeButton = GUI.Button( GUI.RectTransform(Vector2(1, 1), blue_prints.current_gui_page.RectTransform, GUI.Anchor.Center), "", GUI.Alignment.Center, nil) closeButton.OnClicked = function() blue_prints.current_gui_page.Visible = not blue_prints.current_gui_page.Visible end local menuContent = GUI.Frame(GUI.RectTransform(Vector2(0.4, 0.6), blue_prints.current_gui_page.RectTransform, GUI.Anchor.Center)) local mainList = GUI.ListBox(GUI.RectTransform(Vector2(1, 1), menuContent.RectTransform, GUI.Anchor.BottomCenter)) -- Title local title_text = GUI.TextBlock(GUI.RectTransform(Vector2(1, 0.08), mainList.Content.RectTransform), "LOAD BLUEPRINT", nil, nil, GUI.Alignment.Center) title_text.TextScale = 1.5 title_text.TextColor = Color(200, 200, 200) -- Instructions local instruction_text = GUI.TextBlock(GUI.RectTransform(Vector2(1, 0.25), mainList.Content.RectTransform), 'Click a button to load. Hover over the button to see its description. Click folder headers to expand/collapse.\n\n' .. 'If the base component is not available, FPGAs will be used instead. These components must be in your main inventory, not a toolbelt/backpack etc.\n\n' .. 'Click anywhere outside this box to cancel.', nil, nil, GUI.Alignment.TopLeft) instruction_text.Wrap = true instruction_text.TextColor = Color(200, 200, 200) instruction_text.Padding = Vector4(10, 5, 10, 5) local function createBlueprintButton(filename, filepath, description, componentCount, contentList) local buttonContainer = GUI.Frame(GUI.RectTransform(Vector2(1, 0.10), contentList.Content.RectTransform, GUI.Anchor.TopCenter)) buttonContainer.RectTransform.MinSize = Point(0, button_height) buttonContainer.RectTransform.MaxSize = Point(9999, button_height) local button_label = filename .. " - " .. tostring(componentCount) .. " FPGAs" local leftButton = GUI.Button( GUI.RectTransform(Vector2(0.90, 1), buttonContainer.RectTransform, GUI.Anchor.CenterLeft), button_label, GUI.Alignment.CenterLeft, "GUIButtonSmall") leftButton.TextBlock.Padding = Vector4(80, 0, 0, 0) if description then description = description:gsub(" ", "\n"):gsub(" ", "\n") leftButton.ToolTip = description else leftButton.ToolTip = "No description available" end leftButton.OnClicked = function() blue_prints.construct_blueprint(filepath) blue_prints.current_gui_page.Visible = false end local rightButton = GUI.Button( GUI.RectTransform(Vector2(0.10, 1), buttonContainer.RectTransform, GUI.Anchor.CenterRight), "Delete", GUI.Alignment.Center, "GUIButtonSmall") rightButton.ToolTip = "Delete " .. filename rightButton.Color = Color(255, 80, 80) rightButton.HoverColor = Color(255, 120, 120) rightButton.OnClicked = function() local message_box = GUI.MessageBox('Delete Blueprint?', 'Are you sure you want to delete "' .. filename .. '"?', { 'Cancel', 'Delete' }) local cancel_button = nil local delete_button = nil if message_box.Buttons[0] == nil then cancel_button = message_box.Buttons[1] delete_button = message_box.Buttons[2] else cancel_button = message_box.Buttons[0] delete_button = message_box.Buttons[1] end delete_button.Color = Color(255, 80, 80) delete_button.HoverColor = Color(255, 120, 120) cancel_button.OnClicked = function() message_box.Close() end delete_button.OnClicked = function() blue_prints.delete_blueprint(filepath) blue_prints.current_gui_page.Visible = false GUI.AddMessage('Blueprint Deleted', Color.White) message_box.Close() end end end -- Process folders local folders = blue_prints.getFolderList() for _, folderName in ipairs(folders) do local folderPath = folderName == "[Root Directory]" and "" or folderName local fullFolderPath = blue_prints.normalizePath(blue_prints.save_path .. "/" .. folderPath) local blueprintCount = count_blueprints_in_folder(fullFolderPath) folder_states[folderName] = folder_states[folderName] or false -- Create folder container local folderContainer = GUI.Frame(GUI.RectTransform(Vector2(1, 0.10), mainList.Content.RectTransform, GUI.Anchor.TopCenter)) folderContainer.RectTransform.MinSize = Point(0, button_height) folderContainer.RectTransform.MaxSize = Point(9999, button_height) -- Create folder button with 90% width local headerButton = GUI.Button( GUI.RectTransform(Vector2(0.90, 1), folderContainer.RectTransform, GUI.Anchor.CenterLeft), formatFolderHeaderText(folderName, folder_states[folderName], blueprintCount), GUI.Alignment.CenterLeft, "GUIButtonSmall") headerButton.TextColor = Color(150, 150, 255) headerButton.HoverColor = Color(180, 180, 255, 0.5) headerButton.ForceUpperCase = 1 headerButton.TextBlock.Padding = Vector4(0, 0, 0, 0) -- Add delete button for folder (except root) if folderName ~= "[Root Directory]" then local deleteButton = GUI.Button( GUI.RectTransform(Vector2(0.10, 1), folderContainer.RectTransform, GUI.Anchor.CenterRight), "Delete", GUI.Alignment.Center, "GUIButtonSmall") deleteButton.ToolTip = "Delete folder and move contents to root" deleteButton.Color = Color(255, 80, 80) deleteButton.HoverColor = Color(255, 120, 120) deleteButton.OnClicked = function() local message_box = GUI.MessageBox('Delete Folder?', 'Are you sure you want to delete "' .. folderName .. '"?\nAll blueprints will be moved to the root folder.', { 'Cancel', 'Delete' }) local cancel_button = nil local delete_button = nil if message_box.Buttons[0] == nil then cancel_button = message_box.Buttons[1] delete_button = message_box.Buttons[2] else cancel_button = message_box.Buttons[0] delete_button = message_box.Buttons[1] end delete_button.Color = Color(255, 80, 80) delete_button.HoverColor = Color(255, 120, 120) cancel_button.OnClicked = function() message_box.Close() end delete_button.OnClicked = function() if deleteFolder(folderName) then blue_prints.current_gui_page.Visible = false GUI.AddMessage('Folder Deleted', Color.White) message_box.Close() -- Regenerate GUI to show updated folder structure Timer.Wait(function() blue_prints.current_gui_page = generate_load_gui() blue_prints.current_gui_page.Visible = true end, 100) else GUI.AddMessage('Failed to delete folder', Color(255, 0, 0)) message_box.Close() end end end end local files = File.GetFiles(fullFolderPath) local size_of_listbox = #files * button_height + 20 local contentList = GUI.ListBox(GUI.RectTransform(Vector2(1, 0.1), mainList.Content.RectTransform)) contentList.RectTransform.MinSize = Point(0, size_of_listbox) contentList.RectTransform.MaxSize = Point(999999, size_of_listbox) contentList.Visible = folder_states[folderName] -- Process files if files then for _, filepath in ipairs(files) do if string.match(filepath, "%.txt$") then local filename = filepath:match("([^/\\]+)%.txt$") local xmlContent = blue_prints.readFileContents(filepath) local description = blue_prints.get_description_from_xml(xmlContent) local componentCount = blue_prints.get_component_count_from_xml(xmlContent) createBlueprintButton(filename, folderPath .. "/" .. filename, description, componentCount, contentList) end end end headerButton.OnClicked = function() folder_states[folderName] = not folder_states[folderName] contentList.Visible = folder_states[folderName] headerButton.Text = formatFolderHeaderText(folderName, folder_states[folderName], blueprintCount) end end return blue_prints.current_gui_page end local function check_and_rebuild_frame() local new_resolution = blue_prints.getScreenResolution() if new_resolution ~= resolution or run_once_at_start == false then local spacer = GUI.TextBlock(GUI.RectTransform(Vector2(1, 0.04), blue_prints.gui_button_frame_list.Content.RectTransform), "", nil, nil, GUI.Alignment.Center) local button = GUI.Button(GUI.RectTransform(Vector2(1, 0.1), blue_prints.gui_button_frame_list.Content.RectTransform), "Load Blueprint", GUI.Alignment.Center, "GUIButtonSmall") button.OnClicked = function() if blue_prints.current_gui_page then blue_prints.current_gui_page.Visible = false end blue_prints.current_gui_page = generate_load_gui() blue_prints.current_gui_page.Visible = true end resolution = new_resolution run_once_at_start = true end end Hook.Patch("Barotrauma.Items.Components.CircuitBox", "AddToGUIUpdateList", function() check_and_rebuild_frame() end, Hook.HookMethodType.After)