Inventory system

Im trying to brute force this code for an inventory system so its all over the place.

Ive come across an error.
The expected functionality relevant to the problem is;

  1. Whenever a key from 1-9 is pressed, SelectToolGui() is called and the corresponding slot is selected, if the slot isnt empty, the corresponding tool is equipped.
  2. When a tool is picked up, if a different slot is already selected, it just adds the tool to an empty slot on tool bar and leave the selected slot as is.

The problem is with (2).
When a new tool is added, the corresponding slot is selected and the tool is equipped, regardless of whatever the player already has selected.

Im on an unfamiliar setup, on a mac and i couldn’t get this to screen-record if i tried. I cant get the studio in-built screen-record to work either. Hopefully the screenshots will suffice.


in both situations the tools where auto equipped and selected upon being touched and picked up via the default pickup system.

if game:GetService("UserInputService").TouchEnabled ~= true then
	---------------------------
	-- Services & References --
	---------------------------
	local Services = {
		StarterGui = game:GetService("StarterGui"),
		TweenService = game:GetService("TweenService"),
		UserInputService = game:GetService("UserInputService"),
		Players = game:GetService("Players"),
		Lighting = game:GetService("Lighting"),
		keyToNum = function(key)
			if key == "Zero" then
				return 0
			elseif key == "One" then
				return 1
			elseif key == "Two" then
				return 2
			elseif key == "Three" then
				return 3
			elseif key == "Four" then
				return 4
			elseif key == "Five" then
				return 5
			elseif key == "Six" then
				return 6
			elseif key == "Seven" then
				return 7
			elseif key == "Eight" then
				return 8
			elseif key == "Nine" then
				return 9
			else
				return nil
			end
	
			
		end,
	}

	-- Initialize core GUI settings
	Services.StarterGui:SetCoreGuiEnabled(Enum.CoreGuiType.Backpack, false)

	-- Core References
	local Player = Services.Players.LocalPlayer
	local Character = workspace:WaitForChild(Player.Name)
	local Backpack = Player.Backpack
	local Camera = workspace.CurrentCamera
	local Blur = Services.Lighting:FindFirstChild("Blur")
	local Announce = game.ReplicatedStorage.Events:FindFirstChild("Announcement")

	-- GUI References
	local GUI = {
		Parent = script.Parent,
		ToolFrame = script.Parent:WaitForChild("ToolFrame"),
		Inventory = script.Parent:WaitForChild("Inventory"),
		Template = script.Parent:WaitForChild("Template"),
		ScrollFrame = script.Parent:WaitForChild("Inventory").MainGrid.ScrollingFrame,
		Armour = script.Parent:WaitForChild("Inventory").ViewportFrame.Armour,
		ToolFrameIcon = script.Parent:WaitForChild("ToolFrame"):FindFirstChild("1")
	}

	GUI.ToolFrame.Visible = true

	
	local State = {
		CurrentSlotEquipped = "",
		Dragging = false,
		ItemDragged = nil,
		PasteItem = nil,
		OwnedTools = require(script.Parent.Elements), -- {toolName = quantity}
		EquippedTransparency = 0.3,
		UnequippedTransparency = GUI.ToolFrame:FindFirstChild("1").Transparency,
		lastequipped = nil,
		lastcapacity = 0
	}
	
	local function setupTweens()
		local size = Blur.Size
		local tweenInfo = {
			openInfo = TweenInfo.new(0.4, Enum.EasingStyle.Quart, Enum.EasingDirection.Out),
			closeInfo = TweenInfo.new(0.2, Enum.EasingStyle.Sine, Enum.EasingDirection.In)
		}

		return {
			BlurIn = Services.TweenService:Create(Blur, tweenInfo.openInfo, {
				Size = size,
				Enabled = true
			}),
			BlurOut = Services.TweenService:Create(Blur, tweenInfo.closeInfo, {
				Size = 0,
				Enabled = false
			}),
			InventoryIn = Services.TweenService:Create(GUI.Inventory, tweenInfo.openInfo, {
				Position = GUI.Inventory.Position
			}),
			InventoryOut = Services.TweenService:Create(GUI.Inventory, tweenInfo.closeInfo, {
				Position = UDim2.new(0.5, 0, 1, 0)
			})
		}
	end

	local tweens = setupTweens()
	---------------------------
	-- Inventory Management  --
	---------------------------
	
	local TableEdit = {
		addTool = function(toolName, quantity)
			if not quantity then quantity = 1 end

			-- Search for the tool in OwnedTools
			for _, tool in ipairs(State.OwnedTools) do
				if tool.n == toolName then
					-- Tool found, update its quantity
					tool.q = tool.q + quantity
					return -- Exit early since the tool was found
				end
			end

			-- If the tool was not found, add it to OwnedTools
			table.insert(State.OwnedTools, {n = toolName, q = quantity})
		end,

		addToolAtIndex = function(toolName, quantity, index)
			if not quantity then quantity = 1 end

			-- If index is provided and valid, insert at that position
			if index and index >= 1 and index <= #State.OwnedTools + 1 then
				table.insert(State.OwnedTools, index, {n = toolName, q = quantity})
			else
				-- Otherwise, append to the end
				table.insert(State.OwnedTools, {n = toolName, q = quantity})
			end
		end,

		removeTool = function(toolName, quantity)
			if not quantity then quantity = 1 end

			-- Search for the tool in OwnedTools
			for index, tool in ipairs(State.OwnedTools) do
				if tool.n == toolName then
					-- Tool found, update its quantity
					tool.q = tool.q - quantity

					-- If quantity drops to 0 or below, remove the tool
					if tool.q <= 0 then
						table.remove(State.OwnedTools, index)
					end
					return -- Exit early since the tool was found
				end
			end
		end,

		removeToolAtIndex = function(index)
			-- Check if the index is valid
			if index >= 1 and index <= #State.OwnedTools then
				-- Remove the tool at the specified index
				table.remove(State.OwnedTools, index)
			else
				print("Error: Invalid index provided.")
			end
		end,

		switchTools = function(index1, index2)
			-- Check if both indices are valid
			if index1 >= 1 and index1 <= #State.OwnedTools and
				index2 >= 1 and index2 <= #State.OwnedTools then
				-- Swap the tools at the given indices
				State.OwnedTools[index1], State.OwnedTools[index2] =
					State.OwnedTools[index2], State.OwnedTools[index1]
			else
				print("Error: Invalid indices provided.")
			end
		end,

		combineTools = function(index1, index2)
			-- Check if both indices are valid
			if index1 >= 1 and index1 <= #State.OwnedTools and
				index2 >= 1 and index2 <= #State.OwnedTools and
				index1 ~= index2 then
				-- Check if the names of the tools match
				if State.OwnedTools[index1].n == State.OwnedTools[index2].n then
					-- Combine the quantities of the two tools
					State.OwnedTools[index1].q = State.OwnedTools[index1].q + State.OwnedTools[index2].q

					-- Remove the second tool
					table.remove(State.OwnedTools, index2)
				else
					print("Error: Tool names do not match.")
				end
			else
				print("Error: Invalid indices provided or indices are the same.")
			end
		end,

		clearIndex = function(ind)
			State.OwnedTools[ind] = nil
		end,

		clearTable = function()
			for i = #State.OwnedTools, 1, -1 do
				State.OwnedTools[i] = nil
			end
		end,

		FindIndex = function(name)
			for i = 1, #State.OwnedTools do
				if State.OwnedTools[i].n == name then
					return i
				end
			end
			return nil
		end,

		Search = function(Name)
			local found = false
			for i = 1, #State.OwnedTools do
				if State.OwnedTools[i].n == Name then
					found = true
				end
			end
			return found
		end,

		Tablecapacity = function()
			local quantity = 0
			for i = 1, #State.OwnedTools do
				quantity += State.OwnedTools[i].q
			end
			return quantity
		end,
	}
	

	local UIManager = {
		refreshGrid = function ()
			local SF = GUI.ScrollFrame
			for _, child in pairs(SF:GetChildren()) do
				if child:IsA("ImageButton") and child.Name ~= "Template" then
					local selected = child:FindFirstChild("Selected")
					if selected then
						selected.Visible = false
					end
				end
			end
		end,

		updateDragPosition = function()
			local mousePos = Services.UserInputService:GetMouseLocation()
			local viewportSize = Camera.ViewportSize
			GUI.Template.Position = UDim2.new(
				mousePos.X/viewportSize.X, 
				0, 
				mousePos.Y/viewportSize.Y, 
				0
			)
		end,
		InitializeToolButtons = function()
			local TF = GUI.ToolFrame
			
			
			for i = 2, 9 do
				local slot = GUI.ToolFrame[1]:Clone()
				slot.Name = i
				slot.Parent = TF
				slot.ToolName.Text = ""
				slot.Number.Text = i
				slot.Quantity.Text = "0"
				slot.Quantity.Visible = false
				slot.Visible = true
				slot:FindFirstChild("Selected").Visible = false
				
			end
			
		end,
		checkExistingTools = function()
			for _, tool in pairs(Character:GetChildren()) do
				if tool:IsA("Tool") then
					TableEdit.addTool(tool.Name)
				end
			end
			for _, tool in pairs(Backpack:GetChildren()) do
				if tool:IsA("Tool") then
					TableEdit.addTool(tool.Name)
				end
			end

		end
		
		
		
	}
	
	UIManager.UpdateGrids = function()
		local TF = GUI.ToolFrame
		local SF = GUI.ScrollFrame
		local template = SF.Template

		-- Update ToolFrame
		for index = 1, 9 do
			local tool = State.OwnedTools[index]
			local slot = TF[index]
			if tool then
				slot.ToolName.Text = tool.n -- Use tool name
				slot.Quantity.Text = tostring(tool.q) -- Use tool quantity
				slot.Quantity.Visible = true
				slot.Visible = true
			else
				slot.ToolName.Text = ""
				slot.Quantity.Text = "0"
				slot.Quantity.Visible = false
			end
		end

		-- Update ScrollFrame
		for index = 10, #State.OwnedTools do
			local tool = State.OwnedTools[index]
			if tool then
				local newTemplate = template:Clone()
				newTemplate.Parent = SF
				newTemplate.Name = tostring(index)
				newTemplate.Item.Text = tool.n -- Use tool name
				newTemplate.Quantity.Text = tostring(tool.q) -- Use tool quantity
				newTemplate.Quantity.Visible = true
				newTemplate.Visible = true
			end
		end

		UIManager.refreshGrid()
	end
	UIManager.Select = function(btn, drag)
		if drag then
			State.Dragging = true
			State.ItemDragged = btn.Name
		else
			if State.Dragging then
				TableEdit.switchTools(tonumber(btn.Name), tonumber(State.ItemDragged))
			end
		end
		UIManager.UpdateGrids()
	end
	
	
	local function SelectToolGui(slot, reset, NewTool)
		for i, v in pairs(GUI.ToolFrame:GetChildren()) do
			if v:IsA("ImageButton") then
				v:FindFirstChild("Selected").Visible = false
				v.Transparency = State.UnequippedTransparency
			end
		end

		for i, v in pairs(GUI.ScrollFrame:GetChildren()) do
			if v:IsA("ImageButton") then
				v:FindFirstChild("Selected").Visible = false
			end
		end

		if reset then
			State.CurrentSlotEquipped = ""
		else
			if State.CurrentSlotEquipped ~= slot then
				if tonumber(slot) <= 9 then
					State.CurrentSlotEquipped = slot
					script.Select:Play()
					local selectedPart = GUI.ToolFrame:FindFirstChild(tostring(slot))
					selectedPart:FindFirstChild("Selected").Visible = true
					selectedPart.Transparency = State.EquippedTransparency
				else
					State.CurrentSlotEquipped = slot
					script.Select:Play()
					local selectedPart = GUI.ScrollFrame:WaitForChild(tostring(slot))
					selectedPart:FindFirstChild("Selected").Visible = true
				end
			elseif State.CurrentSlotEquipped == slot then
				State.CurrentSlotEquipped = ""
			end
		end

		if NewTool == nil then
			print("newtool is nil")
			
			local tool = State.OwnedTools[tonumber(State.CurrentSlotEquipped)]
			print(State.CurrentSlotEquipped,tool, State.OwnedTools)
			
			print("tool exists")
			script.Toolbar:FireServer(State.CurrentSlotEquipped, tool) -- Use tool name
			
		end
	end
	
	
	
	local function GetTotalToolCount()


		-- Count tools in the backpack
		local backpackTools = 0
		for _, item in pairs(Backpack:GetChildren()) do
			if item:IsA("Tool") then
				backpackTools += 1
			end
		end

		-- Count tools in the character
		local characterTools = 0
		for _, item in pairs(Character:GetChildren()) do
			if item:IsA("Tool") then
				characterTools += 1
			end
		end

		-- Return the total number of tools
		return backpackTools + characterTools
	end
	
	local function handleToolAdded(child, parent,Added)
		if TableEdit.Tablecapacity() < GetTotalToolCount() then --new tool being added
			if child:IsA("Tool") then	
				Announce:Fire("Picked up: " .. child.Name)
				TableEdit.addTool(child.Name)			
				local index = TableEdit.FindIndex(child.Name)
				if index < 9 then  --and (State.CurrentSlotEquipped == nil or State.CurrentSlotEquipped =="")
					SelectToolGui(index,nil, true)
				
				end
				
			end
		end
		UIManager.UpdateGrids()
	end
	

	


	---------------------------
	-- Input Handler        --
	---------------------------
	local function toggleInventory()
		GUI.Inventory.Visible = not GUI.Inventory.Visible
		
		if GUI.Inventory.Visible then
			script.Open:Play()
			tweens.BlurIn:Play()
			tweens.InventoryIn:Play()
			UIManager.refreshGrid()
		else
			tweens.BlurOut:Play()
			tweens.InventoryOut:Play()
			State.CurrentSlotEquipped = ""
			--SelectToolGui("", true) -- Fixed: Replaced selectTool with SelectToolGui
		end
	end

	local function setupKeyBinds()
		Services.UserInputService.InputBegan:Connect(function(input, gameProcessed)
			
			if gameProcessed then return end

			
			local toolNumber = tonumber(Services.keyToNum(input.KeyCode.Name))
			
			if toolNumber and toolNumber >= 1 and toolNumber <= 9 then
				
				local btn = GUI.ToolFrame:FindFirstChild(tostring(toolNumber))
				
				if btn then
					
					SelectToolGui(btn.Name)
					
				end
			end

			-- Handle inventory toggle
			if input.KeyCode == Enum.KeyCode.Q then
				toggleInventory()
			end
		end)
	end

	---------------------------
	-- Initialization       --
	---------------------------
	local function Initialize()
		-- Check for required objects
		assert(Blur, "Blur effect not found in Lighting!")
		assert(GUI.Inventory, "Inventory GUI not found!")
		assert(GUI.ToolFrame, "ToolFrame not found!")
		assert(GUI.ScrollFrame, "ScrollingFrame not found!")
		assert(GUI.Template, "Template GUI not found!")

		-- Initial setup
		UIManager.InitializeToolButtons()
		UIManager.checkExistingTools()
		
		setupKeyBinds()
		TableEdit.removeToolAtIndex(1)

		-- Start update loop
		--task.spawn(function()
		--	while true do
			--	
			--	task.wait(0.2)
--			end
	---	end)

		-- Setup child added events
		Character.ChildAdded:Connect(function(child)
			handleToolAdded(child, Character,true)
			UIManager.refreshGrid()
		end)

		Backpack.ChildAdded:Connect(function(child)
			handleToolAdded(child, Backpack,true)
			UIManager.refreshGrid()
		
		end)
		
	end

	Initialize()
end

Heres the SelectToolGui() function independently

local function SelectToolGui(slot, reset, NewTool)
		for i, v in pairs(GUI.ToolFrame:GetChildren()) do
			if v:IsA("ImageButton") then
				v:FindFirstChild("Selected").Visible = false
				v.Transparency = State.UnequippedTransparency
			end
		end

		for i, v in pairs(GUI.ScrollFrame:GetChildren()) do
			if v:IsA("ImageButton") then
				v:FindFirstChild("Selected").Visible = false
			end
		end

		if reset then
			State.CurrentSlotEquipped = ""
		else
			if State.CurrentSlotEquipped ~= slot then
				if tonumber(slot) <= 9 then
					State.CurrentSlotEquipped = slot
					script.Select:Play()
					local selectedPart = GUI.ToolFrame:FindFirstChild(tostring(slot))
					selectedPart:FindFirstChild("Selected").Visible = true
					selectedPart.Transparency = State.EquippedTransparency
				else
					State.CurrentSlotEquipped = slot
					script.Select:Play()
					local selectedPart = GUI.ScrollFrame:WaitForChild(tostring(slot))
					selectedPart:FindFirstChild("Selected").Visible = true
				end
			elseif State.CurrentSlotEquipped == slot then
				State.CurrentSlotEquipped = ""
			end
		end

		if NewTool == nil then
			print("newtool is nil")
			
			local tool = State.OwnedTools[tonumber(State.CurrentSlotEquipped)]
			print(State.CurrentSlotEquipped,tool, State.OwnedTools)
			
			print("tool exists")
			script.Toolbar:FireServer(State.CurrentSlotEquipped, tool) -- Use tool name
			
		end
	end

Heres the tool added function independently

local function handleToolAdded(child, parent,Added)
		if TableEdit.Tablecapacity() < GetTotalToolCount() then --new tool being added
			if child:IsA("Tool") then	
				Announce:Fire("Picked up: " .. child.Name)
				TableEdit.addTool(child.Name)			
				local index = TableEdit.FindIndex(child.Name)
				if index < 9 then  --and (State.CurrentSlotEquipped == nil or State.CurrentSlotEquipped =="")
					SelectToolGui(index,nil, true)
				
				end
				
			end
		end
		UIManager.UpdateGrids()
	end

I tried changing the logic for calling selectToolGui in the commented section

	if index < 9 then  --and (State.CurrentSlotEquipped == nil or State.CurrentSlotEquipped =="")

It didn’t work, the tools were still being equipped but now the corresponding slots weren’t being selected so i commented that out to find the root of the problem.

I hadn’t used Roblox or Roblox studio in over 6 years before this project and have grown unfamiliar with the syntax and default functionality

Im sure theres an easy fix to this somewhere. Deepseek is just as confused as I am.
Appreciate the help.