Why is my custom inventory dropping more than 1 of the item?

Hello I have been working on an inventory system for about 40 hours and I have been working on this specific bug I have for 10 hours with no signs of progress. I have tried a lot of things but I cannot figure out why my inventory is dropping more than 1 of the item. I notice that it’s only doing it after picking up items in-game, where it then drops 6, or 8, or 10 of the item I drop but I don’t know why it does that.

https://medal.tv/games/roblox-studio/clips/fa8G1Onv3ehps/d13377ksNuDI?invite=cr-MSx4R0csNTgzMDQ2NzAs

Inventory Client Local Script:

local Player = game.Players.LocalPlayer
local UserInputService = game:GetService('UserInputService')
local HTTPService = game:GetService('HttpService')
local RunService = game:GetService('RunService')

local GetDataRemote = game.ReplicatedStorage:WaitForChild('GetData')
local EquipItemRemote = game.ReplicatedStorage:WaitForChild('EquipItem')
local DropItemRemote = game.ReplicatedStorage:WaitForChild('DropItem')
local RemoveItemRemote = game.ReplicatedStorage:WaitForChild('RemoveItem')
local RemoveSlottedItemRemote = game.ReplicatedStorage:WaitForChild('RemoveSlottedItem');

local ItemsModule = require(game.ReplicatedStorage:WaitForChild('Items'))
local PlayerInventory = script.Parent:WaitForChild('InventoryWindow')

local count = 1;
local PlayerData
local CurrentItemID
local ItemSelected = false
local CurrentItemType = PlayerInventory.RightFrame.Type
local CurrentItemTitle = PlayerInventory.RightFrame.Title

local inventoryIDCache = {}
local spinningObjects = {}

PlayerInventory.Visible = false

wait(5)
--Open/Close Inventory
UserInputService.InputBegan:Connect(function(input)
	if input.UserInputType == Enum.UserInputType.Keyboard then
		if input.KeyCode == Enum.KeyCode.G then
			if PlayerInventory.Visible == false then
				if Player.PlayerGui.ScreenGui.InteractionInsignia.Visible == true then
					Player.PlayerGui.ScreenGui.InteractionInsignia.Visible = false;
				end
				local openSound = game.ReplicatedStorage.OtherAssets:FindFirstChild('OpenBackpack');
				openSound:Play()
				PlayerInventory.Visible = true
				
			elseif PlayerInventory.Visible == true then
				local closeSound = game.ReplicatedStorage.OtherAssets:FindFirstChild('CloseBackpack');
				closeSound:Play()
				PlayerInventory.Visible = false
				
			end
		end
	end
end)

function fade(object)
	--this aint working
	for i=0, 5 do
		print(i)
		object.TextTransparency = object.TextTransparency - 0.35; --todolater: need to evaluate if it's a text object or image object.
		i += 1;
	end
end

function EquipMessage(itemID)
	warn('EquipMessage')
	local textMessage = script.Parent.Frame.TextLabel;
	textMessage.Text = itemID .. " equipped.";
	textMessage.Visible = true;
	textMessage:TweenPosition(UDim2.new(0.5, 0, 0, 0), "Out", "Quad", 1, false);
	wait(0.2);
	fade(textMessage);
	wait(1)
	textMessage.Visible = false;
	warn('Jo');
	textMessage:TweenPosition(UDim2.new(0.5, 0, 0.906, 0), "Out", "Quad", 0.001, false);
end

function DisplayItemInfo(FrameName)
	local infoFrame = PlayerInventory.RightFrame
	local itemType = infoFrame.Type
	local viewport = infoFrame.ViewportFrame
			
	viewport.Visible = true
	CurrentItemTitle.Text = FrameName;
	local itemConfiguration = ItemsModule:GetItemFromID(FrameName)
	if itemConfiguration then
		
		for i,v in pairs(itemConfiguration) do
			if i == 'Type' then
				CurrentItemType.Text = v
			end
		end
	end
end


local function UpdateCanvasSize(Canvas, Constraint)
	Canvas.CanvasSize = UDim2.new(0, Constraint.AbsoluteContentSize.X, 0, Constraint.AbsoluteContentSize.Y)
end

function Update()
	if not PlayerData then
		return
	end
	
	for itemID,v in pairs(PlayerData.Inventory) do
		local itemConfiguration = ItemsModule:GetItemFromID(itemID)
	
		if not itemConfiguration then
			continue
		end
		
		--**[[ THIS IS FOR CREATING INVENTORY ITEM IN SCROLLING FRAME FROM PLAYER INVENTORY DATA
		local InventoryItem = script.Parent.InventoryWindow.LeftFrame.ScrollingFrame:FindFirstChild(itemID)
		if not InventoryItem then
			InventoryItem = script.TemplateItem:Clone()
			InventoryItem.Name = itemID
			
			print(itemConfiguration)
			print(itemConfiguration.Title)
			for i,v in pairs(itemConfiguration.Title) do
				print('title meant to be here')
				InventoryItem.Title[i] = v
			end
			
			local model = game.ReplicatedStorage.GameItems:FindFirstChild(itemConfiguration.Model):Clone()
			if model then
				model:Clone()
				if model:IsA("Model") then
					model:SetPrimaryPartCFrame(itemConfiguration.Viewport.Model)
				end
				model.Parent = InventoryItem.ItemSquare.ViewportFrame
				table.insert(spinningObjects, model)
				
				local ViewportCamera = Instance.new('Camera')
				ViewportCamera.CFrame = itemConfiguration.Viewport.Camera
				InventoryItem.ItemSquare.ViewportFrame.CurrentCamera = ViewportCamera
			else
				warn("there is no model.");
			end
			
			InventoryItem.Parent = PlayerInventory.LeftFrame.ScrollingFrame
		end
		
		--**[[ THIS IS FOR RIGHT CLICKING INVENTORY ITEM TO BRING UP ITEM-SPECIFIC ACTIONS.
		InventoryItem.MouseButton2Click:Connect(function()
			if ItemSelected == false then
				local itemType = ItemsModule:GetTypeFromID(InventoryItem.Name);
				print(itemType);
				if itemType == 'Shield' then
					InventoryItem.ItemSquare.Equip1.Visible = true;
					InventoryItem.ItemSquare.Drop.Visible = true;
					InventoryItem.ItemSquare.Drop:TweenPosition(UDim2.new(0.5, 0, 0.5, 0), "Out", "Quad", 0.001, false);
					InventoryItem.ItemSquare.Equip1.Text = 'Equip';
					
				elseif itemType == 'Weapon' then
					
				elseif itemType == 'Resource' then
					
				end
			end
		end)
		
		--**[[ THIS IS FOR HIGHLIGHTING AN ITEM WHEN PLAYER HOVERS MOUSE ON INVENTORY ITEM
		--InventoryItem.MouseEnter:Connect(function()
		--	if ItemSelected == false then
		--		InventoryItem.Equip1Button.Visible = true
		--		InventoryItem.Equip2Button.Visible = true
		--		InventoryItem.DropButton.Visible = true
		--	end
		--end)
		
		--**[[ ABOVE VICE VERSA
		InventoryItem.MouseLeave:Connect(function()
			if ItemSelected == false then
				InventoryItem.ItemSquare.Equip1.Visible = false
				InventoryItem.ItemSquare.Equip2.Visible = false
				InventoryItem.ItemSquare.Drop.Visible = false
			end
		end)
		
		InventoryItem.Quantity.Text = v
		table.insert(inventoryIDCache, itemID)
	end
	local countHelper = 0;
	for _, Frame in pairs(script.Parent.InventoryWindow.LeftFrame.ScrollingFrame:GetChildren()) do
		print('At 182 ' .. os.time() .. count);
		if Frame:IsA('ImageButton') then
			print('yo');
			if not table.find(inventoryIDCache, Frame.Name) then
				warn('UH-OH');
				Frame:Destroy()
			end
			
			local inventorySlot = Frame
			local equip1Button = inventorySlot.ItemSquare:FindFirstChild('Equip1');
			local equip2Button = inventorySlot.ItemSquare:FindFirstChild('Equip2');
			local dropButton = inventorySlot.ItemSquare:FindFirstChild('Drop');
			local shieldSlot = Player.PlayerGui.ScreenGui.InventoryWindow.RightFrame:WaitForChild('ShieldSlot');
			
			--**[[  EQUIPPING ITEMS
			equip1Button.MouseButton1Click:Connect(function()
				local quantity = equip1Button.Parent.Parent.Quantity.Value.Value;
				print(quantity);
				if not Player.Character:FindFirstChild(equip1Button.Parent.Parent.Name) then
					if not Player.PlayerGui.ScreenGui.InventoryWindow.RightFrame.ShieldSlot.ViewportFrame:FindFirstChildWhichIsA('Model') then
						--player can equip more than 1 shield. need to add another check to see if there's a child in the character.
						CurrentItemID = equip1Button.Parent.Parent.Name;
						ItemSelected = false
						PlayerInventory.Visible = false
						if quantity <= 0 then
							print('quanquan')
							equip1Button.Parent.Parent:Destroy();--problem is we're destroying it without any consideration of how many of that item the player has.
						end
						equip1Button.Parent.Parent:Destroy();
						EquipItemRemote:FireServer(CurrentItemID)
						local itemType = ItemsModule:GetTypeFromID(CurrentItemID);
						local Item = ItemsModule:GetItemFromID(CurrentItemID)
						print(Item);
						print(itemType);

						local viewModel = Player.Character:WaitForChild(Item.ID):Clone()
						if itemType == 'Shield' then
							viewModel.Parent = Player.PlayerGui.ScreenGui.InventoryWindow.RightFrame.ShieldSlot.ViewportFrame;
							viewModel:SetPrimaryPartCFrame(Item.Viewport.Model);
							table.insert(spinningObjects, viewModel);
							local viewportCam = Instance.new('Camera');
							viewportCam.CFrame = Item.Viewport.Camera;
							shieldSlot.ViewportFrame.CurrentCamera = viewportCam;
						elseif itemType == 'Weapon' then

						elseif itemType == 'Resource' then

						end

						EquipMessage(Item.ID);
					end
				end
			end)

			equip2Button.MouseButton2Click:Connect(function()
				--this gets coded for different item functionalities, will come back to this 
			end)
			
			--local item = ItemsModule:GetItemFromID(dropButton.Parent.Parent.Name);
			--local quantity = dropButton.Parent.Parent.Quantity.Value.Value;

			print('At 237 ' .. os.time() .. count);
			dropButton.MouseButton1Click:Connect(function(item, quantity)
				countHelper += 1
				print(countHelper)
				item = ItemsModule:GetItemFromID(dropButton.Parent.Parent.Name);
				quantity = dropButton.Parent.Parent.Quantity.Value.Value;
				--print('o');
				
				print(quantity);
				if quantity <= 1 then
					ItemSelected = false;
					DropItemRemote:FireServer(item.ID, quantity)
				end
				
				--print(item.Parent);
				--PlayerInventory.Visible = false
				--ItemSelected = false
				--print(item.ID.. " is the current item ID");
				--DropItemRemote:FireServer(item.ID, quantity) --PROBLEM IS THIS: FIRING LIKE 8+ TIMES FOR NO REASON.
			end)
		end
	end
	
	local Constraint = PlayerInventory.LeftFrame.ScrollingFrame.UIListLayout
	PlayerInventory.LeftFrame.ScrollingFrame.CanvasSize = UDim2.new(0,Constraint.AbsoluteContentSize.X,0,Constraint.AbsoluteContentSize.Y)
end

--** [[REMOVING/DROPPING ITEMS FROM EQUIP SLOTS
for i,v in pairs(script.Parent.InventoryWindow.RightFrame:GetChildren()) do
	if v:IsA('ImageButton') then
		if v.Name == 'ClothingSlot' or v.Name == 'HelmetSlot' or v.Name == 'ShieldSlot' then
			local dropbButton = v:WaitForChild('Drop');
			local removeButton = v:WaitForChild('Remove');
			local item

			local reset = 1;
			local dude = false;

			removeButton.Parent.ViewportFrame.ChildAdded:Connect(function(model1)
				print(model1);
				item = model1;
			end)
			--local item = removeButton.Parent.ViewportFrame.
			v.MouseButton2Click:Connect(function()
				if item then
					dropbButton.Visible = true;
					removeButton.Visible = true;
				end
			end)
			v.MouseLeave:Connect(function()
				dropbButton.Visible = false;
				removeButton.Visible = false;
			end)
			dropbButton.MouseButton1Click:Connect(function()
				local viewItem;
				for i,v in pairs(dropbButton.Parent.ViewportFrame:GetChildren()) do
					if v:IsA('Model') then
						viewItem = v;
					end
				end
				print(viewItem);
				DropItemRemote:FireServer(viewItem.Name);
				local equipitem = Player.Character:WaitForChild(viewItem.Name);
				equipitem:Destroy();
				viewItem:Destroy();
			end)

			removeButton.MouseButton1Click:Connect(function()
				if not dude then
					dude = true;
					print(item)
					local equippedItem = Player.Character:WaitForChild(item.Name);
					print(equippedItem);
					item:Destroy();
					RemoveSlottedItemRemote:FireServer(equippedItem); --removing it from the player's character
					Update()
					removeButton.Visible = false;
					dropbButton.Visible = false;
					wait(reset);
					dude = false;
				end
				--need to destroy it from player character
				--need to see if when we equip an item, is it still inside the player data. if not we need to make it so that it is so that when they leave the game the item will be equipped when they come back.
			end)
		end
	end
end

local rotationCFrame = CFrame.Angles( 0, math.rad(0.8), 0 )
RunService.Heartbeat:Connect(function()
	for i, object in ipairs( spinningObjects ) do
		if (not object.Parent) then
			table.remove(spinningObjects, i)
			break
		end
		if object:IsA('Model') then
			object:SetPrimaryPartCFrame( object:GetPrimaryPartCFrame() * rotationCFrame )
		elseif object:IsA('BasePart') then
			object.CFrame *= rotationCFrame
		end
	end
end)

--function that checks the area of the player around the player compared to the position of the dropped item that the
--player drops. but basically this function will check if the player is within this area or not, and we're gonna use
--this function to return a bool value because we want to evaluate directly below our "dropbutton function" whether or
--not the player is in proximity to the dropped item. if the player is not in proximity of the dropped item then
--it will keep running. we'll just word it like "if not ... then " 

task.defer(function()
	local oldDataJSON = ''
	while task.wait(0.25) do		
		PlayerData = GetDataRemote:InvokeServer()
		local newPlayerData = PlayerData and HTTPService:JSONEncode(PlayerData)
		if newPlayerData ~= oldDataJSON then --If player picks up an item for example.
			oldDataJSON  = newPlayerData
			task.defer(Update)
		end	
	end
end)

RunService.RenderStepped:Connect(function()
	if Player.Character then
		local playerData = GetDataRemote:InvokeServer()
		if playerData then
			for i,v in pairs(script.Parent.InventoryWindow.LeftFrame.ScrollingFrame:GetChildren()) do
				if v:IsA('ImageButton') then
					local item = v.Name
					if playerData.Inventory[item] <= 0 then
						v:Destroy()
					end
					--v.MouseButton1Click:Connect(function()
					--	v.ImageColor3 = Color3.new(0.784314, 1, 0.580392)
					--	v.ItemSquare.ImageColor3 = Color3.new(0.443137, 0.635294, 0.411765)
					--	local FrameName = v.Name
					--	ItemSelected = true
					--	DisplayItemInfo(FrameName)
					--end)
				end
			end
		end
	end
end)

count = count + 1;

Server Handler:

local DataService = require(game.ServerStorage:WaitForChild('DataService'))
local GetDataRemote = game.ReplicatedStorage:WaitForChild('GetData')
local EquipItemRemote = game.ReplicatedStorage:WaitForChild('EquipItem')
local DropItemRemote = game.ReplicatedStorage:WaitForChild('DropItem')
local RemoveItemRemote = game.ReplicatedStorage:WaitForChild('RemoveItem')
local RemoveSlottedItemRemote = game.ReplicatedStorage:WaitForChild('RemoveSlottedItem')

GetDataRemote.OnServerInvoke = function(Player, Yield)
	local profile = DataService:GetPlayerProfile(Player, false)
	if profile then
		return profile.Data
	end
	return nil
end

RemoveSlottedItemRemote.OnServerEvent:Connect(function(Player, itemID)
	local playerProfile = DataService:GetPlayerProfile(Player)
	if playerProfile then
		for i,v in pairs(Player.Character:GetChildren()) do
			if v == itemID then
				v:Destroy();
			end
		end
	end
end)

RemoveItemRemote.OnServerEvent:Connect(function(Player, itemID)
	local playerProfile = DataService:GetPlayerProfile(Player)
	if playerProfile then
		--make a condition for when we're just wanting to remove the item from the slot, to go back to the inventory frame.
		playerProfile.Data.Inventory[itemID] = nil
	end
end)

--local gameItemsArray = game.ReplicatedStorage:WaitForChild('GameItems'):GetChildren()
	
DropItemRemote.OnServerEvent:Connect(function(Player, CurrentItemID, quantity)
	local playerProfile = DataService:GetPlayerProfile(Player)
	for i,v in pairs(game.ReplicatedStorage:WaitForChild('GameItems'):GetChildren()) do
		if v.Name == CurrentItemID then
			if quantity > 1 then
				local droppedItem = v:Clone();
				droppedItem.Parent = game.Workspace.EnvironmentAssets
				droppedItem.PrimaryPart.CFrame = Player.Character.HumanoidRootPart.CFrame + Vector3.new(0, 0, -2)
				droppedItem.PrimaryPart.Anchored = false
				droppedItem.PrimaryPart.CanCollide = true;
			elseif quantity <= 1 then
				local droppedItem = v:Clone();
				droppedItem.Parent = game.Workspace.EnvironmentAssets
				droppedItem.PrimaryPart.CFrame = Player.Character.HumanoidRootPart.CFrame + Vector3.new(0, 0, -2)
				droppedItem.PrimaryPart.Anchored = false
				droppedItem.PrimaryPart.CanCollide = true;
			end
			--playerProfile.Data.Inventory[v] = playerProfile.Data.Inventory[v] - 1
		end
	end
	
	for i,v in pairs(playerProfile.Data.Inventory) do
		if i == CurrentItemID then
			--if Player.PlayerGui.ScreenGui.InventoryWindow.
			playerProfile.Data.Inventory[CurrentItemID] = playerProfile.Data.Inventory[CurrentItemID] - 1
		end
	end
end)

EquipItemRemote.OnServerEvent:Connect(function(Player, CurrentItemID)
	local playerProfile = DataService:GetPlayerProfile(Player)
	local gameItemsArray = game.ReplicatedFirst:WaitForChild('GameItems'):GetChildren()
	for i,v in pairs(gameItemsArray) do
		if v.ItemType.Value == 'Shield' and v.Name == CurrentItemID then
			local clonedItem = v:Clone()
			clonedItem.Parent = Player.Character
			
			local newWeld = Instance.new('Weld')
			newWeld.Parent = clonedItem.ArmPart
			newWeld.Part0 = Player.Character.LeftLowerArm
			newWeld.Part1 = clonedItem.ArmPart
			--playerProfile.Data.Inventory[CurrentItemID] = playerProfile.Data.Inventory[CurrentItemID] - 1;
		end
		if v.ItemType.Value == 'Weapon' and v.Name == CurrentItemID then

		end
		if v.ItemType.Value == 'Resource' and v.Name == CurrentItemID then
			print('you cant do that bro.')
		end
	end
end)

2 Likes

ngl the script is way too big, show me part of the script which drops items

First you should tell what is countHelper so that others can understand why you kept that there

Second, you need to show your module clearly, no one can find why many tools are dropping if they don’t know how the module functions

Add a debounce cool down
Like this:

local candrop = true

if candrop == true then
  candrop = false -- script don't work now
  DropItemRemote:FireServer() -- fires event
  task.wait(1) -- cooldown for drop button
  candrop = true -- script can again start working now
end
2 Likes