Help with inventory system

ok so i have a sword in the ‘swords’ tab and a staff in the ‘staffs’ tab. if i equip the sword to the hotbar then send it back to the inventory it duplicates. now if i equip the staff to the hotbar then send it back to the inventory it just dissapears. im gonna leave the place file here if anybody has the time to look at it that would be amazing. also if anyone wants to use this as their own feel free to download and edit it however you like.

Any help is appreciated!

Inventory system (Bugged).rbxl (119.7 KB)

Post your actual code here, most people aren’t going to download your file

1 Like
local ServerStorage = game:GetService("ServerStorage")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local remotes = ReplicatedStorage:WaitForChild("InventoryReplicatedStorage"):WaitForChild("RemoteEvents")

local hotbarSlots = 1  -- Define the number of hotbar slots

local function givePlayerItem(player, category, itemName)
	local categoryFolder = ServerStorage.Pickups:FindFirstChild(category)
	if categoryFolder then
		local item = categoryFolder:FindFirstChild(itemName)
		if item then
			local inventory = player:FindFirstChild("Inventory")
			if inventory then
				local categoryFolderInInventory = inventory:FindFirstChild(category)
				if categoryFolderInInventory then
					local clonedItem = item:Clone()
					clonedItem.Parent = categoryFolderInInventory
					print("Successfully gave player item:", itemName)
				end
			end
		end
	end
end

local function setupPlayerInventory(plr)
	local inventory = Instance.new("Folder")
	inventory.Name = "Inventory"
	inventory.Parent = plr

	local categories = {"Swords", "Staffs", "Bows", "Resources"}  -- Define your categories here

	for _, category in ipairs(categories) do
		local categoryFolder = Instance.new("Folder")
		categoryFolder.Name = category
		categoryFolder.Parent = inventory
	end

	local hotbar = Instance.new("Folder")
	hotbar.Name = "Hotbar"
	hotbar.Parent = plr

	for slotNum = 1, hotbarSlots do
		local newSlot = Instance.new("ObjectValue")
		newSlot.Name = tostring(slotNum)
		newSlot.Parent = hotbar
	end

	local equipped = Instance.new("ObjectValue")
	equipped.Name = "Equipped"
	equipped.Parent = plr
end

local function handleCharacterAdded(plr, char)
	local backpack = plr:WaitForChild("Backpack")
	local hotbar = plr:WaitForChild("Hotbar")
	local equippedVal = plr:WaitForChild("Equipped")
	local inventory = plr:WaitForChild("Inventory")

	-- Transfer tools from backpack to hotbar
	for slotNum, tool in ipairs(backpack:GetChildren()) do
		if slotNum <= hotbarSlots then
			hotbar[slotNum].Value = tool
			print("Moved tool to hotbar:", tool.Name)
		else
			break
		end
	end

	-- Handle tool adding to backpack
	backpack.ChildAdded:Connect(function(child)
		if child:IsA("Tool") or child:IsA("Model") then
			for _, slot in ipairs(hotbar:GetChildren()) do
				if slot.Value == child then return end
			end

			if #backpack:GetChildren() <= hotbarSlots then
				hotbar[#backpack:GetChildren()].Value = child
				print("Added tool to hotbar:", child.Name)
			else
				local category = "Uncategorized"
				-- Determine the category of the item (you can customize this logic)
				if child.Name:match("Sword") then
					category = "Swords"
				elseif child.Name:match("Staff") then
					category = "Staffs"
				elseif child.Name:match("Bow") then
					category = "Bows"
				elseif child.Name:match("Resource") then
					category = "Resources"
				end

				local newItemValue = Instance.new("ObjectValue")
				newItemValue.Value = child
				newItemValue.Name = child.Name
				newItemValue.Parent = inventory:FindFirstChild(category) or inventory:FindFirstChild("Uncategorized")
				print("Added tool to inventory:", child.Name)
			end
		end
	end)
end

game.Players.PlayerAdded:Connect(function(plr)
	setupPlayerInventory(plr)
	plr.CharacterAdded:Connect(function(char)
		handleCharacterAdded(plr, char)
	end)
	-- Give player a sword and a staff
	givePlayerItem(plr, "Swords", "Sword")
	givePlayerItem(plr, "Staffs", "Leaf")
	givePlayerItem(plr, "Resources", "Model")
end)

local function handleEquip(plr, itemToEquip)
	local character = plr.Character
	local humanoid = character and character:FindFirstChildOfClass("Humanoid")
	if not character or not humanoid or humanoid.Health == 0 then return end

	local backpack = plr:WaitForChild("Backpack")
	local hotbar = plr:WaitForChild("Hotbar")
	local equippedVal = plr:WaitForChild("Equipped")

	if itemToEquip and (itemToEquip:IsA("Tool") or itemToEquip:IsA("Model")) then
		if itemToEquip.Parent == backpack then
			if equippedVal.Value then
				equippedVal.Value.Parent = backpack
			end
			equippedVal.Value = itemToEquip
			itemToEquip.Parent = character
			print("Equipped item:", itemToEquip.Name)
		elseif itemToEquip.Parent == character then
			if equippedVal.Value then
				equippedVal.Value.Parent = backpack
				equippedVal.Value = nil
			end
			itemToEquip.Parent = backpack
			print("Unequipped item:", itemToEquip.Name)
		end

		if itemToEquip:IsA("Tool") then
			itemToEquip.Unequipped:Wait()
			if humanoid and not character:FindFirstChildOfClass("Tool") then
				equippedVal.Value = nil
				itemToEquip.Parent = backpack
			end
		end
	end
end

local function handleToHotbar(plr, itemToHotbar)
	local backpack = plr:WaitForChild("Backpack")
	local hotbar = plr:WaitForChild("Hotbar")
	local inventory = plr:WaitForChild("Inventory")

	if itemToHotbar and (itemToHotbar:IsA("Tool") or itemToHotbar:IsA("Model")) then
		-- Ensure the item is removed from the inventory before adding it to the hotbar
		local category = itemToHotbar.Parent.Name
		if inventory:FindFirstChild(category) then
			for _, invItem in pairs(inventory[category]:GetChildren()) do
				if invItem:IsA("ObjectValue") and invItem.Value == itemToHotbar then
					invItem:Destroy()
					break
				end
			end
		end

		local firstFreeSlot = nil
		for slotNum, slot in ipairs(hotbar:GetChildren()) do
			if not slot.Value then
				firstFreeSlot = slot
				break
			end
		end

		if firstFreeSlot then
			firstFreeSlot.Value = itemToHotbar
			itemToHotbar.Parent = backpack
			print("Moved item to hotbar:", itemToHotbar.Name)
		end
	end
end

local function handleToInventory(plr, itemToInventory)
	local backpack = plr:WaitForChild("Backpack")
	local hotbar = plr:WaitForChild("Hotbar")
	local inventory = plr:WaitForChild("Inventory")

	if itemToInventory and (itemToInventory:IsA("Tool") or itemToInventory:IsA("Model")) then
		-- Ensure the item is removed from the hotbar before adding it to the inventory
		for slotNum, slot in ipairs(hotbar:GetChildren()) do
			if slot.Value == itemToInventory then
				slot.Value = nil
				break
			end
		end

		local category = "Uncategorized"
		-- Determine the category of the item (you can customize this logic)
		if itemToInventory.Name:match("Sword") then
			category = "Swords"
		elseif itemToInventory.Name:match("Staff") then
			category = "Staffs"
		elseif itemToInventory.Name:match("Bow") then
			category = "Bows"
		elseif itemToInventory.Name:match("Resource") then
			category = "Resources"
		end
		local newItemValue = Instance.new("ObjectValue")
		newItemValue.Value = itemToInventory
		newItemValue.Name = itemToInventory.Name
		newItemValue.Parent = inventory:FindFirstChild(category) or inventory:FindFirstChild("Uncategorized")
		print("Moved item to inventory:", itemToInventory.Name)
		itemToInventory.Parent = newItemValue.Parent
	end
end

remotes.Equip.OnServerEvent:Connect(handleEquip)
remotes.ToHotbar.OnServerEvent:Connect(handleToHotbar)
remotes.ToInventory.OnServerEvent:Connect(handleToInventory)

game:GetService("StarterGui"):SetCoreGuiEnabled(Enum.CoreGuiType.Backpack, false)

local keys = {
	[1] = Enum.KeyCode.One,
}

local uis = game:GetService("UserInputService")

local rs = game:GetService("ReplicatedStorage"):WaitForChild("InventoryReplicatedStorage")
local remotes = rs:WaitForChild("RemoteEvents")

local client = game.Players.LocalPlayer

local hotbar = client:WaitForChild("Hotbar")
local inventory = client:WaitForChild("Inventory")
local equipped = client:WaitForChild("Equipped")

local hotbarGui = script.Parent:WaitForChild("Hotbar"):WaitForChild("SlotsContainer")
local inventoryGui = script.Parent
local invGui = script.Parent:WaitForChild("Inventory")
invGui.Visible = false
local openInvButton = script.Parent:WaitForChild("OpenInventory")

local currentCategory = "Swords" -- Default category

local inventoryCategories = {
	Swords = script.Parent:WaitForChild("InventorySwords"),
	Staffs = script.Parent:WaitForChild("InventoryStaffs"),
	Bows = script.Parent:WaitForChild("InventoryBows"),
	Resources = script.Parent:WaitForChild("InventoryResources"),
}

-- Initial visibility setup
for _, frame in pairs(inventoryCategories) do
	frame.Visible = false
end
script.Parent.Background.Visible = false
script.Parent.SwordButton.Visible = false
script.Parent.StaffButton.Visible = false
script.Parent.BowButton.Visible = false
script.Parent.ResourceButton.Visible = false

local function updateHotbar()
	print("Updating Hotbar")
	for slotNum, slotVal in pairs(hotbar:GetChildren()) do
		local guiSlot = hotbarGui:FindFirstChild(tostring(slotNum))
		if guiSlot and slotVal.Value then
			guiSlot.ItemName.Visible = true
			guiSlot.ItemName.Text = slotVal.Value.Name
			if slotVal.Value:IsA("Tool") and string.len(slotVal.Value.TextureId or "") > 0 then
				guiSlot.ItemViewer.Visible = false
				guiSlot.ItemImage.Visible = true
				guiSlot.ItemImage.Image = slotVal.Value.TextureId
			else
				guiSlot.ItemViewer.Visible = true
				guiSlot.ItemImage.Visible = false
				guiSlot.ItemViewer:ClearAllChildren()
				local vpfModel = Instance.new("Model")
				local copiedItem = slotVal.Value:Clone()
				for __, desc in pairs(copiedItem:GetDescendants()) do
					if desc:IsA("LocalScript") or desc:IsA("Script") then
						desc:Destroy()
					elseif desc:IsA("BasePart") then
						desc.Parent = vpfModel
					end
				end
				copiedItem:Destroy()
				local cf, size = vpfModel:GetBoundingBox()
				local primaryPart = Instance.new("Part")
				primaryPart.Transparency = 1
				primaryPart.CFrame = cf
				primaryPart.Parent = vpfModel
				vpfModel.PrimaryPart = primaryPart
				vpfModel:PivotTo(CFrame.new(Vector3.new(0, 0, 0), Vector3.new(0, math.rad(90), math.rad(90))))
				local itemPivot = vpfModel:GetPivot()
				local itemSize = vpfModel:GetExtentsSize()
				local vpfCam = Instance.new("Camera")
				vpfCam.CFrame = CFrame.new(itemPivot.Position + (Vector3.one * itemSize * 0.5), itemPivot.Position)
				guiSlot.ItemViewer.CurrentCamera = vpfCam
				vpfCam.Parent = guiSlot.ItemViewer
				vpfModel.Parent = guiSlot.ItemViewer
			end
		else
			if guiSlot then
				guiSlot.ItemName.Visible = false
				guiSlot.ItemImage.Visible = false
				guiSlot.ItemViewer.Visible = false
			end
		end
	end
end

local function updateInventory(category)
	print("Updating Inventory for category:", category)
	local categoryFolder = inventory:FindFirstChild(category)
	if not categoryFolder then 
		print("Category folder not found for category:", category)
		return 
	end

	for _, invItem in pairs(inventoryCategories[category].InventoryScroller:GetChildren()) do
		if invItem:IsA(script.InventoryItem.ClassName) then
			invItem:Destroy()
		end
	end

	for _, itemVal in pairs(categoryFolder:GetChildren()) do
		local item = itemVal
		local newItem = script.InventoryItem:Clone()
		newItem.ItemName.Text = item.Name
		if item:IsA("Tool") and string.len(item.TextureId or "") > 0 then
			newItem.ItemViewer.Visible = false
			newItem.ItemImage.Image = item.TextureId
		else
			newItem.ItemImage.Visible = false
			newItem.ItemViewer:ClearAllChildren()
			local vpfModel = Instance.new("Model")
			local copiedItem = item:Clone()
			for __, desc in pairs(copiedItem:GetDescendants()) do
				if desc:IsA("LocalScript") or desc:IsA("Script") then
					desc:Destroy()
				elseif desc:IsA("BasePart") then
					desc.Parent = vpfModel
				end
			end
			copiedItem:Destroy()
			local cf, size = vpfModel:GetBoundingBox()
			local primaryPart = Instance.new("Part")
			primaryPart.Transparency = 1
			primaryPart.CFrame = cf
			primaryPart.Parent = vpfModel
			vpfModel.PrimaryPart = primaryPart
			vpfModel:PivotTo(CFrame.new(Vector3.new(0, 0, 0), Vector3.new(0, math.rad(90), math.rad(90))))
			local itemPivot = vpfModel:GetPivot()
			local itemSize = vpfModel:GetExtentsSize()
			local vpfCam = Instance.new("Camera")
			vpfCam.CFrame = CFrame.new(itemPivot.Position + (Vector3.one * itemSize * 0.5), itemPivot.Position)
			newItem.ItemViewer.CurrentCamera = vpfCam
			vpfCam.Parent = newItem.ItemViewer
			vpfModel.Parent = newItem.ItemViewer
		end
		newItem.MouseButton1Click:Connect(function()
			remotes.ToHotbar:FireServer(item)
		end)
		newItem.MouseButton2Click:Connect(function()
			if equipped.Value ~= item then
				remotes.ToInventory:FireServer(item)
			end
		end)
		newItem.Parent = inventoryCategories[category].InventoryScroller
	end
end

local function findSelectedSlot()
	print("Finding selected slot")
	for slotNum, slotVal in pairs(hotbar:GetChildren()) do
		local guiSlot = hotbarGui:FindFirstChild(tostring(slotNum))
		if slotVal.Value and slotVal.Value == equipped.Value then
			guiSlot.ImageColor3 = Color3.fromRGB(255, 255, 255)
		else
			guiSlot.ImageColor3 = Color3.fromRGB(218, 218, 218)
		end
	end
end

for slotNum, slotVal in pairs(hotbar:GetChildren()) do
	local newSlot = script:WaitForChild("HotbarItem"):Clone()
	newSlot.Name = slotNum
	if keys[slotNum].Value >= 48 and keys[slotNum].Value <= 57 then
		newSlot.NumberKey.Text = keys[slotNum].Value - 48
	else
		newSlot.NumberKey.Text = string.split(tostring(keys[slotNum]), ".")[3]
	end
	if slotVal.Value then
		newSlot.ItemName.Text = slotVal.Value.Name
		if slotVal.Value:IsA("Tool") and string.len(slotVal.Value.TextureId or "") > 0 then
			newSlot.ItemViewer.Visible = false
			newSlot.ItemImage.Image = slotVal.Value.TextureId
		else
			newSlot.ItemImage.Visible = false
			newSlot.ItemViewer:ClearAllChildren()
			local vpfModel = Instance.new("Model")
			local copiedItem = slotVal.Value:Clone()
			for __, desc in pairs(copiedItem:GetDescendants()) do
				if desc:IsA("LocalScript") or desc:IsA("Script") then
					desc:Destroy()
				elseif desc:IsA("BasePart") then
					desc.Parent = vpfModel
				end
			end
			copiedItem:Destroy()
			local cf, size = vpfModel:GetBoundingBox()
			local primaryPart = Instance.new("Part")
			primaryPart.Transparency = 1
			primaryPart.CFrame = cf
			primaryPart.Parent = vpfModel
			vpfModel.PrimaryPart = primaryPart
			vpfModel:PivotTo(CFrame.new(Vector3.new(0, 0, 0), Vector3.new(0, math.rad(90), math.rad(90))))
			local itemPivot = vpfModel:GetPivot()
			local itemSize = vpfModel:GetExtentsSize()
			local vpfCam = Instance.new("Camera")
			vpfCam.CFrame = CFrame.new(itemPivot.Position + (Vector3.one * itemSize * 0.5), itemPivot.Position)
			newSlot.ItemViewer.CurrentCamera = vpfCam
			vpfCam.Parent = newSlot.ItemViewer
			vpfModel.Parent = newSlot.ItemViewer
		end
	else
		newSlot.ItemName.Visible = false
		newSlot.ItemImage.Visible = false
		newSlot.ItemViewer.Visible = false
	end
	newSlot.MouseButton1Click:Connect(function()
		local item = slotVal.Value
		remotes.Equip:FireServer(item)
	end)
	newSlot.MouseButton2Click:Connect(function()
		local item = slotVal.Value
		if equipped.Value ~= item then
			remotes.ToInventory:FireServer(item)
		end
	end)
	newSlot.Parent = hotbarGui
	slotVal:GetPropertyChangedSignal("Value"):Connect(updateHotbar)
end

updateInventory(currentCategory)
inventory.ChildAdded:Connect(function() updateInventory(currentCategory) end)
inventory.ChildRemoved:Connect(function() updateInventory(currentCategory) end)

findSelectedSlot()
equipped:GetPropertyChangedSignal("Value"):Connect(findSelectedSlot)

uis.InputBegan:Connect(function(inp, p)
	if p then return end
	if table.find(keys, inp.KeyCode) then
		local slotVal = hotbar:FindFirstChild(tostring(table.find(keys, inp.KeyCode)))
		local item = slotVal.Value
		remotes.Equip:FireServer(item)
	end
end)

local function toggleInventoryVisibility()
	local isVisible = not script.Parent.Background.Visible
	for _, frame in pairs(inventoryCategories) do
		frame.Visible = isVisible
	end
	script.Parent.Background.Visible = isVisible
	script.Parent.SwordButton.Visible = isVisible
	script.Parent.StaffButton.Visible = isVisible
	script.Parent.BowButton.Visible = isVisible
	script.Parent.ResourceButton.Visible = isVisible
end

uis.InputBegan:Connect(function(input, gameProcessedEvent)
	if gameProcessedEvent then return end
	if input.KeyCode == Enum.KeyCode.K then
		toggleInventoryVisibility()
	end
end)

openInvButton.MouseButton1Click:Connect(function()
	toggleInventoryVisibility()
end)

-- Category buttons handling
local swordButton = script.Parent:WaitForChild("SwordButton")
local staffButton = script.Parent:WaitForChild("StaffButton")
local bowButton = script.Parent:WaitForChild("BowButton")
local resourceButton = script.Parent:WaitForChild("ResourceButton")

swordButton.MouseButton1Click:Connect(function() 
	currentCategory = "Swords"
	updateInventory(currentCategory)
end)
staffButton.MouseButton1Click:Connect(function() 
	currentCategory = "Staffs"
	updateInventory(currentCategory)
end)
bowButton.MouseButton1Click:Connect(function() 
	currentCategory = "Bows"
	updateInventory(currentCategory)
end)
resourceButton.MouseButton1Click:Connect(function() 
	currentCategory = "Resources"
	updateInventory(currentCategory)
end)

i posted the codes but they are pretty long thats why i just added the place file