How do i stop a player from collecting tools?

So, I’ve looked around everywhere and can’t seem to find a tutorial for this small system I’m trying to create.

Basically, I’m making a system where a player can only pick up two tools in game so other players can have a chance to get some tools too. Like a full inventory system. But, I’m not sure how to approach this. Can someone help me?

Script (Local)

local rs = game:GetService("ReplicatedStorage")
local pickedupevent = rs:WaitForChild("Bindable").pickedup
local Stoppickupevent = rs:WaitForChild("Remotes").StopPickup


--local player = game.Players.LocalPlayer
local delayTime = 1

--This script detects when the player inventory is FULL.

pickedupevent.Event:Connect(function(plr)
	task.wait(1)
	--print(plr.Name)
	local Tools = plr.Backpack:GetChildren()
	
	for _, tool in Tools do
		print(tool.Name)
	end
	
	if #Tools >= 2 then
		print("Maximum tools equipped")
		Stoppickupevent:FireServer(plr)
	end
end)

Script (server)

--This sample is a typical tool script for functioning.

local debounce = false
local db = false -- debounce for firing events
local canDamage = false

local waitTime = 2

local equipped = false

local Fired = false
local function onTouch(hit)
	tool.Handle.Anchored = false
	task.wait(1)
	if not Fired then
		--task.wait(2)
		SpawnWeaponsEvent:Fire(tool.Name)
		Fired = true
	end

	local humanoid = hit.Parent:FindFirstChild("Humanoid")
	if not humanoid then 
		return 
	end

	if humanoid.Parent ~= tool.Parent and canDamage then

		humanoid:TakeDamage(10)
	else
		return
	end
	canDamage = false

end

tool.Activated:Connect(function()
	local character = tool.Parent
	local humanoid = character:FindFirstChild("Humanoid")
	if debounce == false then
		debounce = true
		canDamage = true
		tool.Parent:FindFirstChild("Humanoid").Animator:LoadAnimation(animation):Play()
		sfx:Play()

		task.wait(waitTime)

		sfx:Stop()

		debounce = false

	end
	
end)

tool.Handle.Touched:Connect(onTouch)
2 Likes

You must do checks before giving the tool, what I do is I make a tool giving module.
Heres an example

Module:

local ToolGiver = {}

function ToolGiver.getPlayerTools(plr : Player)
     local tools = {}
     for _, tool in plr.Backpack:GetChildren() do
         if tool:IsA("Tool") then table.insert(tools, tool) end
     end
     for _, tool in plr.Character:GetChildren() do
         if tool:IsA("Tool") then table.insert(tools, tool) end
     end
     return tools
end

function ToolGiver.giveTool(toolName : string, plr : Player)
    local playerTools = ToolGiver.getPlayerTools(plr)
    if #playerTools >= 2 then
       print("Player already have 2 tools in the backpack")
       return
    end 
    toolFolder[toolName]:Clone().Parent = plr.Backpack
end

return ToolGiver

Server script:

local ToolGiver = require(modulePath)

local toolName = "TEST"
local toolName2 = "ANOTHER_TEST"
local toolName3 = "LAST_TOOL"

ToolGiver.giveTool(plr, toolName)
ToolGiver.giveTool(plr, toolName2)
ToolGiver.giveTool(plr, toolName3) -- will print "Player already have 2 tools in the backpack"

I believe the best solution to still use the classic Roblox system would be to leave the model of the tools around and create a “custom” pickup script.

Here you create the blacklist throught the event in your Local script.

local blockedPlayers = {}

Stoppickupevent.OnServerEvent:Connect(function(player) -- taking the event from your Local script
	table.insert(blockedPlayers, player.Name)
end)

and then (basing that every tool is in a folder inside workspace and a tools folder in ReplicatedStorage) update their .Touched event

local function checkGivable(tool)
	tool.Touched:Connect(function(part)
		local character = part:FindFirstAncestorOfClass("Model")
		if not character then return end

		local player = game.Players:GetPlayerFromCharacter(character)
		if not player then return end
		if table.find(blockedPlayers, player.Name) then return end
		if not game.ReplicatedStorage.Tools[tool.Name] then return end

		local newTool = game.ReplicatedStorage.Tools[tool.Name]:Clone()
		newTool.Parent = player.Backpack
		tool:Destroy()
	end)
end

for i, tool in pairs(workspace.Tools:GetChildren()) do
	checkGivable(tool)
end

You should also want to create a function in a way so if new tools are added you can connect it with a :ChildAdded() function.

workspace.Tools.ChildAdded:Connect(checkGivable)

Thanks for your response. But there is a problem with the ModuleScript.

When I run it, I receive this error with the plr.backpack and I can’t find a fix to it.
image

It doesn’t really work for some reason. I think it’s because the table adds a player’s name more than once. I forgot to mention that each tool is parented to workspace, so workspace.Tools.ChildAdded:Connect(checkGivable) wouldn’t really be a good approach. Does it change anything else?

Also it still allows players to collect more than the maximum amount.

Oh yes, to remove the fact that it adds the name twice (I should have added it before) you can just add a check like this:

Stoppickupevent.OnServerEvent:Connect(function(player) -- taking the event from your Local 
	if table.find(blockedPlayers, player.Name) then return end
	table.insert(blockedPlayers, player.Name)
end)

As for the tools in workspace, I personally reccomend that you add a folder where tools are inside however if you wish to keep them in the workspace you can just add an attribute to the tool main part and set it as a BoolValue like “Tool = true” then run a check in the for i loop.

As for it still allows player to collect more than the maximum is also kinda my fault as I haven’t seen the previous remote. You can simply fix it by running the event you made so here’s the fix:

local function checkGivable(tool)
	tool.Touched:Connect(function(part)
		local character = part:FindFirstAncestorOfClass("Model")
		if not character then return end

		local player = game.Players:GetPlayerFromCharacter(character)
		if not player then return end
		if table.find(blockedPlayers, player.Name) then return end
		if not game.ReplicatedStorage.Tools[tool.Name] then return end
		
		local newTool = game.ReplicatedStorage.Tools[tool.Name]:Clone()
		newTool.Parent = player.Backpack
		tool:Destroy()
		
		SpawnWeaponsEvent:Fire()
	end)
end

for i, tool in pairs(workspace:GetChildren()) do
	if tool:GetAttribute("Tool") then
		checkGivable(tool)
	end
end

workspace.Tools.ChildAdded:Connect(checkGivable)

Lads, I think we’re all forgetting a core thing that doesn’t require scripting. Why not check out CanTouch?

Sadly the CanTouch property can be modified only server-side disabling the pick up for EVERY user. And as from what I understood the request is to block it for the group of users who have more than 2 items in inventory.

2 Likes

Why not simply have a pseudo-tool that’s visible in the Workspace, and upon trying to pick it up it runs the checks, and when verified the player can pick it up, it simply copies the actual tool from ServerStorage to their Backpack?

On pickup (or alternatively when a tool is added to the backpack), loop through the backpack and character and increase a counter for every tool detected. If it’s over 2, don’t let them pick up (or drop) the tool.

The way i prevented from auto pick up is i renamed the tool handle to something else. Because with how roblox auto picks up every tool, my suggestion is that you should make your own custom script where it handles the pick up. This way you can fully control how many tools you want the player to pick up.

(Using roblox default pick up isnt worth it, you will suffer and have less control)

Thanks. I’ve tweaked the code a bit, unfortunately using chatgpt because it didnt really work how i wanted it to. Good news is that the script works well. Bad news is that it’s a bit laggy and the weapons can still be collected even when it is not cloned from ReplicatedStorage.

Do I change the tools to models instead and grant the tools thru that code then? because the tools are still tools in workspace.

(Tweaked server code) -- local script is still the same/very similar
Stoppickupevent.OnServerEvent:Connect(function(player) -- taking the event from your Local 
	--print("Fired stopPickup!")
	if not blockedPlayers[player.UserId] then
		blockedPlayers[player.UserId] = true
		print("Blocked player:", player.Name)
	else
		print("Player already blocked:", player.Name)
	end
end)

local function handlePickup(tool, player)
	if not player or blockedPlayers[player.UserId] then return end

	local weaponTemplate = WeaponsStorage:FindFirstChild(tool.Name)
	if not weaponTemplate then return end

	local clonedTool = weaponTemplate:Clone()
	clonedTool.Parent = player.Backpack
	print("Tool granted to Player!")

	if clonedTool:FindFirstChild("Model") then
		clonedTool.Model.Anchored = false
	end
	if clonedTool:FindFirstChild("Handle") then
		clonedTool.Handle.Anchored = false
	end

	tool:Destroy()
	task.wait(delayTime)

	PickupEvent:FireClient(player)
	SpawnWeaponsEvent:Fire(clonedTool.Name)
end

-- Connect touch for any valid tool
local function connectTool(tool)
	if not tool:IsA("Tool") then return end

	local touchPart = tool:FindFirstChild("Model") or tool:FindFirstChild("Handle")
	if not touchPart then return end
	touchPart.Touched:Connect(function(hit)
		local character = hit:FindFirstAncestorOfClass("Model")
		local player = Players:GetPlayerFromCharacter(character)
		handlePickup(tool, player)
	end)
end


-- Initial tools in workspace folder
for i, tool in pairs(WeaponSpawner:GetChildren()) do
	if tool:IsA("Tool") then
		connectTool(tool)
	end
end

--New spawned tools
WeaponSpawner.ChildAdded:Connect(connectTool)

Actually, updating from this. Could you try removing TouchInterest on the client? You can prevent exploiting (i.e. picking up tools you’re not supposed to) by checking the backpack/character server side since they’re replicated.

Hi,

Thanks for replying. Actually, forget what I said earlier. It worked when testing the game in Client. but how do i get it to not touchinterest thru server sided? (a.k.a actually playing the game thru roblox)

Uh, I’m not too sure what you mean.
If you destroy the touch interest on the client, the server won’t respond to it. It will be local to your client. That said, exploiters can simply disable the script. This is why I suggested to add sanity checks in my original reply (what’s the point in exploiting to pick up the tool if it does nothing?)

If I’m wrong, the same issue that @azureknight1704 raised still applies and… rip.