import "macros" as { $ } $load $FILE -- Heliodex's basic New function (basically a simplified version of melt) New = (className, name, props) -> if not props? -- no name was provided props = name name = nil obj = Instance.new className obj.Name = name if name local parent for k, v in pairs props if type(k) == "string" if k == "Parent" parent = v else obj[k] = v elseif type(k) == "number" and type(v) == "userdata" v.Parent = obj obj.Parent = parent obj -- -- A couple of necessary functions waitForChild = (instance, name) -> assert instance assert name while not instance\FindFirstChild name print "Waiting for ...", instance, name instance.ChildAdded\wait! instance\FindFirstChild name waitForProperty = (instance, property) -> assert instance assert property while not instance[property] instance.Changed\wait! local IsTouchDevice = -> touchEnabled = false try touchEnabled = Game\GetService"UserInputService".TouchEnabled touchEnabled waitForChild game, "Players" waitForProperty game.Players, "LocalPlayer" player = game.Players.LocalPlayer local RbxGui, _ = LoadLibrary "RbxGui" if not RbxGui print "could not find RbxGui!" return --- Begin Locals StaticTabName = "gear" backpack = script.Parent backpackItems = {} buttons = {} debounce = false browsingMenu = false mouseEnterCons = {} mouseClickCons = {} local characterChildAddedCon local characterChildRemovedCon local backpackAddCon playerBackpack = waitForChild player, "Backpack" waitForChild backpack, "Tabs" waitForChild backpack, "Gear" gearPreview = waitForChild backpack.Gear, "GearPreview" scroller = waitForChild backpack.Gear, "GearGridScrollingArea" currentLoadout = waitForChild backpack.Parent, "CurrentLoadout" grid = waitForChild backpack.Gear, "GearGrid" gearButton = waitForChild grid, "GearButton" swapSlot = waitForChild script.Parent, "SwapSlot" backpackManager = waitForChild script.Parent, "CoreScripts/BackpackScripts/BackpackManager" backpackOpenEvent = waitForChild backpackManager, "BackpackOpenEvent" backpackCloseEvent = waitForChild backpackManager, "BackpackCloseEvent" tabClickedEvent = waitForChild backpackManager, "TabClickedEvent" resizeEvent = waitForChild backpackManager, "ResizeEvent" searchRequestedEvent = waitForChild backpackManager, "SearchRequestedEvent" tellBackpackReadyFunc = waitForChild backpackManager, "BackpackReady" -- creating scroll bar early as to make sure items get placed correctly local scrollFrame, scrollUp, scrollDown, recalculateScroll = RbxGui.CreateScrollingFrame nil, "grid", Vector2.new 6, 6 scrollFrame.Position = UDim2.new 0, 0, 0, 30 scrollFrame.Size = UDim2.new 1, 0, 1, -30 scrollFrame.Parent = backpack.Gear.GearGrid scrollBar = New "Frame", "ScrollBar" BackgroundTransparency: 0.9 BackgroundColor3: Color3.new 1, 1, 1 BorderSizePixel: 0 Size: UDim2.new 0, 17, 1, -36 Position: UDim2.new 0, 0, 0, 18 Parent: scroller scrollDown.Position = UDim2.new 0, 0, 1, -17 scrollUp.Parent = scroller scrollDown.Parent = scroller local scrollFrameLoadout, scrollUpLoadout, scrollDownLoadout, recalculateScrollLoadout = RbxGui.CreateScrollingFrame! scrollFrameLoadout.Position = UDim2.new 0, 0, 0, 0 scrollFrameLoadout.Size = UDim2.new 1, 0, 1, 0 scrollFrameLoadout.Parent = backpack.Gear.GearLoadouts.LoadoutsList LoadoutButton = New "TextButton", "LoadoutButton" RobloxLocked: true Font: Enum.Font.ArialBold FontSize: Enum.FontSize.Size14 Position: UDim2.new 0, 0, 0, 0 Size: UDim2.new 1, 0, 0, 32 Style: Enum.ButtonStyle.RobloxButton Text: "Loadout #1" TextColor3: Color3.new 1, 1, 1 Parent: scrollFrameLoadout with LoadoutButton\clone! .Text = "Loadout #2" .Parent = scrollFrameLoadout with LoadoutButton\clone! .Text = "Loadout #3" .Parent = scrollFrameLoadout with LoadoutButton\clone! .Text = "Loadout #4" .Parent = scrollFrameLoadout New "Frame", "ScrollBarLoadout" BackgroundTransparency: 0.9 BackgroundColor3: Color3.new 1, 1, 1 BorderSizePixel: 0 Size: UDim2.new 0, 17, 1, -36 Position: UDim2.new 0, 0, 0, 18 Parent: backpack.Gear.GearLoadouts.GearLoadoutsScrollingArea scrollDownLoadout.Position = UDim2.new 0, 0, 1, -17 scrollUpLoadout.Parent = backpack.Gear.GearLoadouts.GearLoadoutsScrollingArea scrollDownLoadout.Parent = backpack.Gear.GearLoadouts.GearLoadoutsScrollingArea -- Begin Functions removeFromMap = (map, object) -> for i = 1, #map if map[i] == object table.remove map, i break robloxLock = (instance) -> instance.RobloxLocked = true children = instance\GetChildren! if children for _, child in ipairs children robloxLock child clearPreview = -> gearPreview.GearImage.Image = "" gearPreview.GearStats.GearName.Text = "" clearHighlight = (button) -> button.TextColor3 = Color3.new 1, 1, 1 button.BackgroundColor3 = Color3.new 0, 0, 0 inLoadout = (gear) -> children = currentLoadout\GetChildren! for i = 1, #children if children[i]\IsA "Frame" button = children[i]\GetChildren! if #button > 0 and button[1].GearReference.Value and button[1].GearReference.Value == gear return true false updateGridActive = -> for _, v in pairs backpackItems if buttons[v] local gear gearRef = buttons[v]\FindFirstChild "GearReference" if gearRef gear = gearRef.Value buttons[v].Active = if (not gear) or inLoadout gear false else true swapGearSlot = (slot, gearButton) -> if not swapSlot.Value -- signal loadout to swap a gear out swapSlot.Slot.Value = slot swapSlot.GearButton.Value = gearButton swapSlot.Value = true updateGridActive! unequipGear = (physGear) -> physGear.Parent = playerBackpack updateGridActive! UnequipGearMenuClick = (element, menu) -> return if type(element.Action) ~= "number" num = element.Action if num == 1 -- remove from loadout unequipGear menu.Parent.GearReference.Value inventoryButton = menu.Parent gearToUnequip = inventoryButton.GearReference.Value loadoutChildren = currentLoadout\GetChildren! slot = -1 for i = 1, #loadoutChildren if loadoutChildren[i]\IsA "Frame" button = loadoutChildren[i]\GetChildren! if button[1] and button[1].GearReference.Value == gearToUnequip slot = button[1].SlotNumber.Text break swapGearSlot slot, nil highlight = (button) -> button.TextColor3 = Color3.new 0, 0, 0 button.BackgroundColor3 = Color3.new 0.8, 0.8, 0.8 getGearContextMenu = -> gearContextMenu = New "Frame", "UnequipContextMenu" Active: true Size: UDim2.new 0, 115, 0, 70 Position: UDim2.new 0, -16, 0, -16 BackgroundTransparency: 1 Visible: false gearContextMenuButton = New "TextButton", "UnequipContextMenuButton" Text: "" Style: Enum.ButtonStyle.RobloxButtonDefault ZIndex: 8 Size: UDim2.new 1, 0, 1, -20 Visible: true Parent: gearContextMenu elementHeight = 12 contextMenuElements = {} contextMenuElementsName = { "Remove Hotkey" } for i = 1, #contextMenuElementsName element = {} element.Type = "Button" element.Text = contextMenuElementsName[i] element.Action = i element.DoIt = UnequipGearMenuClick table.insert contextMenuElements, element for i, contextElement in ipairs contextMenuElements element = contextElement if element.Type == "Button" button = New "TextButton", "UnequipContextButton#{i}" BackgroundColor3: Color3.new 0, 0, 0 BorderSizePixel: 0 TextXAlignment: Enum.TextXAlignment.Left Text: " #{contextElement.Text}" Font: Enum.Font.Arial FontSize: Enum.FontSize.Size14 Size: UDim2.new 1, 8, 0, elementHeight Position: UDim2.new 0, 0, 0, elementHeight * i TextColor3: Color3.new 1, 1, 1 ZIndex: 9 Parent: gearContextMenuButton if not IsTouchDevice! button.MouseButton1Click\connect -> if button.Active and not gearContextMenu.Parent.Active try element.DoIt element, gearContextMenu browsingMenu = false gearContextMenu.Visible = false clearHighlight button clearPreview! button.MouseEnter\connect -> if button.Active and gearContextMenu.Parent.Active highlight button button.MouseLeave\connect -> if button.Active and gearContextMenu.Parent.Active clearHighlight button contextElement.Button = button contextElement.Element = button elseif element.Type == "Label" frame = New "Frame", "ContextLabel#{i}" BackgroundTransparency: 1 Size: UDim2.new 1, 8, 0, elementHeight * New "TextLabel", "Text1" BackgroundTransparency: 1 BackgroundColor3: Color3.new 1, 1, 1 BorderSizePixel: 0 TextXAlignment: Enum.TextXAlignment.Left Font: Enum.Font.ArialBold FontSize: Enum.FontSize.Size14 Position: UDim2.new 0, 0, 0, 0 Size: UDim2.new 0.5, 0, 1, 0 TextColor3: Color3.new 1, 1, 1 ZIndex: 9 element.Label1 = frame.Text1 if element.GetText2 element.Label2 = New "TextLabel", "Text2" BackgroundTransparency: 1 BackgroundColor3: Color3.new 1, 1, 1 BorderSizePixel: 0 TextXAlignment: Enum.TextXAlignment.Right Font: Enum.Font.Arial FontSize: Enum.FontSize.Size14 Position: UDim2.new 0.5, 0, 0, 0 Size: UDim2.new 0.5, 0, 1, 0 TextColor3: Color3.new 1, 1, 1 ZIndex: 9 Parent: frame frame.Parent = gearContextMenuButton element.Label = frame element.Element = frame gearContextMenu.ZIndex = 4 gearContextMenu.MouseLeave\connect -> browsingMenu = false gearContextMenu.Visible = false clearPreview! robloxLock gearContextMenu gearContextMenu findEmptySlot = -> local smallestNum loadout = currentLoadout\GetChildren! for i = 1, #loadout if loadout[i]\IsA"Frame" and #loadout[i]\GetChildren! <= 0 frameNum = tonumber string.sub loadout[i].Name, 5 if frameNum == 0 frameNum = 10 if not smallestNum or (smallestNum > frameNum) smallestNum = frameNum if smallestNum == 10 smallestNum = 0 smallestNum checkForSwap = (button, x, y) -> loadoutChildren = currentLoadout\GetChildren! for i = 1, #loadoutChildren if loadoutChildren[i]\IsA"Frame" and string.find loadoutChildren[i].Name, "Slot" if x >= loadoutChildren[i].AbsolutePosition.x and x <= (loadoutChildren[i].AbsolutePosition.x + loadoutChildren[i].AbsoluteSize.x) if y >= loadoutChildren[i].AbsolutePosition.y and y <= (loadoutChildren[i].AbsolutePosition.y + loadoutChildren[i].AbsoluteSize.y) slot = tonumber string.sub loadoutChildren[i].Name, 5 swapGearSlot slot, button return true false previewGear = (button) -> if not browsingMenu gearPreview.Visible = false gearPreview.GearImage.Image = button.Image gearPreview.GearStats.GearName.Text = button.GearReference.Value.Name buttonClick = (button) -> if button\FindFirstChild "UnequipContextMenu" and not button.Active button.UnequipContextMenu.Visible = true browsingMenu = true resizeGrid = -> for _, v in pairs backpackItems if not v\FindFirstChild "RobloxBuildTool" if not buttons[v] buttonClone = gearButton\clone! with buttonClone .Parent = grid.ScrollingFrame .Visible = true .Image = v.TextureId if .Image == "" .GearText.Text = v.Name .GearReference.Value = v .Draggable = true buttons[v] = buttonClone if not IsTouchDevice! unequipMenu = getGearContextMenu! unequipMenu.Visible = false unequipMenu.Parent = buttonClone local beginPos buttonClone.DragBegin\connect (value) -> waitForChild buttonClone, "Background" buttonClone["Background"].ZIndex = 10 buttonClone.ZIndex = 10 beginPos = value buttonClone.DragStopped\connect (x, y) -> waitForChild buttonClone, "Background" buttonClone["Background"].ZIndex = 1 buttonClone.ZIndex = 2 if beginPos ~= buttonClone.Position if not checkForSwap buttonClone, x, y buttonClone\TweenPosition( beginPos, Enum.EasingDirection.Out, Enum.EasingStyle.Quad, 0.5, true ) buttonClone.Draggable = false delay 0.5, -> buttonClone.Draggable = true else buttonClone.Position = beginPos clickTime = tick! mouseEnterCons[buttonClone] = buttonClone.MouseEnter\connect -> previewGear buttonClone mouseClickCons[buttonClone] = buttonClone.MouseButton1Click\connect -> newClickTime = tick! if buttonClone.Active and (newClickTime - clickTime) < 0.5 slot = findEmptySlot! if slot buttonClone.ZIndex = 1 swapGearSlot slot, buttonClone else buttonClick buttonClone clickTime = newClickTime recalculateScroll! resize = -> size = 0.75 * if gearPreview.AbsoluteSize.Y > gearPreview.AbsoluteSize.X gearPreview.AbsoluteSize.X else gearPreview.AbsoluteSize.Y waitForChild gearPreview, "GearImage" gearPreview.GearImage.Size = UDim2.new 0, size, 0, size gearPreview.GearImage.Position = UDim2.new 0, gearPreview.AbsoluteSize.X / 2 - size / 2, 0.75, -size resizeGrid! addToGrid = (child) -> return if not child\IsA"Tool" and not child\IsA "HopperBin" return if child\FindFirstChild "RobloxBuildTool" for _, v in pairs backpackItems -- check to see if we already have this gear registered return if v == child table.insert backpackItems, child changeCon = child.Changed\connect (prop) -> if prop == "Name" and buttons[child] and buttons[child].Image == "" buttons[child].GearText.Text = child.Name ancestryCon = child.AncestryChanged\connect (_, _) -> local thisObject for _, v in pairs backpackItems if v == child thisObject = v break waitForProperty player, "Character" waitForChild player, "Backpack" if child.Parent ~= player.Backpack and child.Parent ~= player.Character ancestryCon?\disconnect! changeCon?\disconnect! for _, v in pairs backpackItems if v == thisObject mouseEnterCons[buttons[v]]?\disconnect! mouseClickCons[buttons[v]]?\disconnect! buttons[v].Parent = nil buttons[v] = nil break removeFromMap backpackItems, thisObject resizeGrid! else resizeGrid! updateGridActive! resizeGrid! showPartialGrid = (subset) -> for _, v in pairs buttons v.Parent = nil if subset for _, v in pairs subset v.Parent = grid.ScrollingFrame recalculateScroll! showEntireGrid = -> for _, v in pairs buttons v.Parent = grid.ScrollingFrame recalculateScroll! centerGear = (loadoutChildren) -> gearButtons = {} local lastSlotAdd for i = 1, #loadoutChildren if loadoutChildren[i]\IsA"Frame" and #loadoutChildren[i]\GetChildren! > 0 if loadoutChildren[i].Name == "Slot0" lastSlotAdd = loadoutChildren[i] else table.insert gearButtons, loadoutChildren[i] if lastSlotAdd table.insert gearButtons, lastSlotAdd startPos = (1 - (#gearButtons * 0.1)) / 2 for i = 1, #gearButtons gearButtons[i]\TweenPosition( UDim2.new(startPos + ((i - 1) * 0.1), 0, 0, 0), Enum.EasingDirection.Out, Enum.EasingStyle.Quad, 0.25, true ) backpackOpenHandler = (currentTab) -> if currentTab and currentTab ~= StaticTabName backpack.Gear.Visible = false return backpack.Gear.Visible = true updateGridActive! resizeGrid! resize! tellBackpackReadyFunc\Invoke! backpackCloseHandler = (currentTab) -> if currentTab and currentTab ~= StaticTabName backpack.Gear.Visible = false return backpack.Gear.Visible = false resizeGrid! resize! tellBackpackReadyFunc\Invoke! tabClickHandler = (tabName) -> if tabName == StaticTabName backpackOpenHandler tabName else backpackCloseHandler tabName loadoutCheck = (child, selectState) -> return if not child\IsA "ImageButton" for _, v in pairs backpackItems if buttons[v] if child\FindFirstChild "GearReference" and buttons[v]\FindFirstChild "GearReference" if buttons[v].GearReference.Value == child.GearReference.Value buttons[v].Active = selectState break -- removeAllEquippedGear = (physGear) -> -- stuff = player.Character\GetChildren! -- for i = 1, #stuff -- if (stuff[i]\IsA"Tool" or stuff[i]\IsA"HopperBin") and stuff[i] ~= physGear -- stuff[i].Parent = playerBackpack -- equipGear = (physGear) -> -- removeAllEquippedGear physGear -- physGear.Parent = player.Character -- updateGridActive! setupCharacterConnections = -> backpackAddCon?\disconnect! backpackAddCon = game.Players.LocalPlayer.Backpack.ChildAdded\connect (child) -> addToGrid child -- make sure we get all the children backpackChildren = game.Players.LocalPlayer.Backpack\GetChildren! for i = 1, #backpackChildren addToGrid backpackChildren[i] characterChildAddedCon?\disconnect! characterChildAddedCon = game.Players.LocalPlayer.Character.ChildAdded\connect (child) -> addToGrid child updateGridActive! characterChildRemovedCon?\disconnect! characterChildRemovedCon = game.Players.LocalPlayer.Character.ChildRemoved\connect (_) -> updateGridActive! wait! centerGear currentLoadout\GetChildren! removeCharacterConnections = -> characterChildAddedCon?\disconnect! characterChildRemovedCon?\disconnect! backpackAddCon?\disconnect! trim = (s) -> s\gsub "^%s*(.-)%s*$", "%1" filterGear = (terms) -> filteredGear = {} for _, v in pairs backpackItems if buttons[v] gearString = string.lower buttons[v].GearReference.Value.Name gearString = trim gearString for i = 1, #terms if string.match gearString, terms[i] table.insert filteredGear, buttons[v] break filteredGear splitByWhitespace = (text) -> return if type(text) ~= "string" terms = {} for token in string.gmatch text, "[^%s]+" if string.len(token) > 0 table.insert terms, token terms showSearchGear = (searchTerms) -> return if not backpack.Gear.Visible -- currently not active tab searchTermTable = splitByWhitespace searchTerms local currSearchTerms currSearchTerms = if searchTermTable and (#searchTermTable > 0) searchTermTable else nil if not searchTermTable? showEntireGrid! return filteredButtons = filterGear currSearchTerms showPartialGrid filteredButtons nukeBackpack = -> while #buttons > 0 table.remove buttons buttons = {} while #backpackItems > 0 table.remove backpackItems backpackItems = {} scrollingFrameChildren = grid.ScrollingFrame\GetChildren! for i = 1, #scrollingFrameChildren scrollingFrameChildren[i]\remove! coreGuiChanged = (coreGuiType, enabled) -> if coreGuiType == Enum.CoreGuiType.Backpack or coreGuiType == Enum.CoreGuiType.All if not enabled backpack.Gear.Visible = false backpackChildren = player.Backpack\GetChildren! for i = 1, #backpackChildren addToGrid backpackChildren[i] ------------------------- Start Lifelong Connections ----------------------- resizeEvent.Event\connect (_) -> return if debounce debounce = true wait! resize! resizeGrid! debounce = false currentLoadout.ChildAdded\connect (child) -> loadoutCheck child, false currentLoadout.ChildRemoved\connect (child) -> loadoutCheck child, true currentLoadout.DescendantAdded\connect (descendant) -> if not backpack.Visible and (descendant\IsA"ImageButton" or descendant\IsA "TextButton") centerGear currentLoadout\GetChildren! currentLoadout.DescendantRemoving\connect (descendant) -> if not backpack.Visible and (descendant\IsA"ImageButton" or descendant\IsA "TextButton") wait! centerGear currentLoadout\GetChildren! grid.MouseEnter\connect -> clearPreview! grid.MouseLeave\connect -> clearPreview! player.CharacterRemoving\connect -> removeCharacterConnections! nukeBackpack! player.CharacterAdded\connect -> setupCharacterConnections! player.ChildAdded\connect (child) -> if child\IsA "Backpack" playerBackpack = child backpackAddCon?\disconnect! backpackAddCon = game.Players.LocalPlayer.Backpack.ChildAdded\connect (child) -> addToGrid child swapSlot.Changed\connect -> if not swapSlot.Value updateGridActive! loadoutChildren = currentLoadout\GetChildren! for i = 1, #loadoutChildren if loadoutChildren[i]\IsA"Frame" and string.find loadoutChildren[i].Name, "Slot" loadoutChildren[i].ChildRemoved\connect -> updateGridActive! loadoutChildren[i].ChildAdded\connect -> updateGridActive! ------------------------- End Lifelong Connections ----------------------- try coreGuiChanged Enum.CoreGuiType.Backpack, Game.StarterGui\GetCoreGuiEnabled Enum.CoreGuiType.Backpack Game.StarterGui.CoreGuiChangedSignal\connect coreGuiChanged resize! resizeGrid! -- make sure any items in the loadout are accounted for in inventory loadoutChildren = currentLoadout\GetChildren! for i = 1, #loadoutChildren loadoutCheck loadoutChildren[i], false if not backpack.Visible centerGear currentLoadout\GetChildren! -- make sure that inventory is listening to gear reparenting if not characterChildAddedCon? and game.Players.LocalPlayer["Character"] setupCharacterConnections! if not backpackAddCon backpackAddCon = game.Players.LocalPlayer.Backpack.ChildAdded\connect (child) -> addToGrid child backpackOpenEvent.Event\connect backpackOpenHandler backpackCloseEvent.Event\connect backpackCloseHandler tabClickedEvent.Event\connect tabClickHandler searchRequestedEvent.Event\connect showSearchGear recalculateScrollLoadout!