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)
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.
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)
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.
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.
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.