Safe Zone removing swords bugs out

I’m trying to make it so when the player enters back into the SafeZone, their sword gets removed and they get a forcefield. What ends up happening is that the player gets multiple swords and forcefields for some reason and I can’t figure out why. When exiting the SafeZone it’s completely fine, everything works. Once they enter back in, it just completely breaks.

local event = game.ReplicatedStorage.SafeZone

script.Parent.Touched:Connect(function(part)
	if(part.Name ~= "HumanoidRootPart") then return end
	--if not game.Players:GetPlayerFromCharacter(part.Parent) then return end
	
	local character = part.Parent
	local forceField = Instance.new("ForceField")
	forceField.Name = "Safezone"
	forceField.Parent = character
	forceField.Visible = true
	local backpack = character:FindFirstChildOfClass("Backpack")
	local player = game.Players:GetPlayerFromCharacter(character)

	for _, tool in ipairs(backpack:GetChildren()) do
		if tool:IsA("Tool") then
			tool:Destroy()
		end
	end

	if player.Character:FindFirstChildOfClass("Tool") then
		player.Character:FindFirstChildOfClass("Tool"):Destroy()
	end

	if(player) then
		event:FireClient(player, "In")
	end
end)

script.Parent.TouchEnded:Connect(function(part)
	if(part.Name ~= "HumanoidRootPart") then return end
	
	local character = part.Parent
	local forceField = character:FindFirstChild("Safezone")
	local debounce = false
	
	if(forceField) then
		debounce = false
		wait(1)
		forceField:Destroy()
		local clone = game.ServerStorage.Swords.ClassicSword:Clone()
		clone.Parent = character
		debounce = true
	end
		
	local player = game.Players:GetPlayerFromCharacter(character)

	if(player) then
		event:FireClient(player, "Out")
	end
end)

This is where I have my sword at:
image

I have a small idea on why this doesn’t work, and that’s because the Touched function runs like every time the player moves. I can’t find a different way to do it though.

1 Like

Try this instead. The only change I made is that instead of character.Backpack, I did player.Backpack (after the player variable). That’s because the character doesn’t have a backpack, because the player has that.

script.Parent.Touched:Connect(function(part)
	if(part.Name ~= "HumanoidRootPart") then return end

	local character = part.Parent
	local forceField = Instance.new("ForceField")
	forceField.Name = "Safezone"
	forceField.Parent = character
	forceField.Visible = true
	local player = game.Players:GetPlayerFromCharacter(character)
	local backpack = player.Backpack

	for _, tool in ipairs(backpack:GetChildren()) do
		if tool:IsA("Tool") then
			tool:Destroy()
		end
	end

	if player.Character:FindFirstChildOfClass("Tool") then
		player.Character:FindFirstChildOfClass("Tool"):Destroy()
	end
end)

script.Parent.TouchEnded:Connect(function(part)
	if(part.Name ~= "HumanoidRootPart") then return end

	local character = part.Parent
	local forceField = character:FindFirstChild("Safezone")
	local debounce = false

	if(forceField) then
		debounce = false
		wait(1)
		forceField:Destroy()
		local clone = game.ServerStorage.Swords.ClassicSword:Clone()
		clone.Parent = character
		debounce = true
	end

	local player = game.Players:GetPlayerFromCharacter(character)
end)

This happens even after the change, and this happened before the change too and I don’t really know why it does.

See if this helps.

--> Services
local Players = game:GetService("Players")

--> Configuration
local debounce = false

script.Parent.Touched:Connect(function(hit)
	if hit.Parent and hit.Parent:FindFirstChildOfClass("Humanoid") and not debounce then
		debounce = true
		
		local Player = Players:GetPlayerFromCharacter(hit.Parent)
		local Character = Player and Player.Character
		
		if Character:FindFirstChildOfClass("ForceField") then
			return
		end
		
		local ForceField = Instance.new("ForceField", Character)
		ForceField.Name = "SafeZone"
		
		-- Runs a loop through the players backpack and deletes them
		for _, Tools in Player.Backpack:GetChildren() do
			if Tools:IsA("Tool") then
				Tools:Destroy()
			end
		end
		
		-- If the character finds a tool then destroy it
		if Character:FindFirstChildOfClass("Tool") then
			Character:FindFirstChildOfClass("Tool"):Destroy()
		end
		
		debounce = false
	end
end)

I get the forcefield on spawn but once I leave the SafeZone the forcefield stays and I don’t get a sword.

My best guess is that the player is repeatedly triggering the Touched and/or TouchEnded events.

This looks like it is a script inside of your SafeZone part? If it is you could create a table that tracks what players are inside the SafeZone and refer to that table as an additional check.
Though personally I’d make SafeZone stuff a localscript thing, and then the server double checks using spacial query possibly.

It is a script inside the SafeZone part.
image
I don’t know how to create tables and I want to try to keep this simple as this is my first game, but I do want to ask what a spacial query is just for future references as I don’t know what it is.

Spacial query finds all the parts in an area and returns it as a table, as far as I understand it.

This is what happens whenever I re-enter the safe zone with the item equipped. If it’s not equipped aka in the backpack it works fine, but once it’s equipped it completely breaks.

Here is my code again if anybody is wondering:

local event = game.ReplicatedStorage.SafeZone



script.Parent.Touched:Connect(function(part)
	if(part.Name ~= "HumanoidRootPart") then return end
	--if not game.Players:GetPlayerFromCharacter(part.Parent) then return end
	
	local character = part.Parent
	local forceField = Instance.new("ForceField")
	forceField.Name = "Safezone"
	forceField.Parent = character
	forceField.Visible = true
	local player = game.Players:GetPlayerFromCharacter(character)
	local backpack = player:FindFirstChild("Backpack")
	
	--if backpack then
	--	for _, child in pairs(backpack:GetChildren()) do
	--		if child:IsA("Tool") then
	--			child.Enabled = false
	--		end
	--	end
	--end
	
	for _, tool in ipairs(backpack:GetChildren()) do
		if tool:IsA("Tool") then
			tool:Destroy()
		end
	end

	if character:FindFirstChildOfClass("Tool") then
		character:FindFirstChildOfClass("Tool"):Destroy()
	end

	if(player) then
		event:FireClient(player, "In")
	end
end)

script.Parent.TouchEnded:Connect(function(part)
	if(part.Name ~= "HumanoidRootPart") then return end
	
	local character = part.Parent
	local forceField = character:FindFirstChild("Safezone")
	local debounce = false
	
	if(forceField) then
		debounce = false
		wait(1)
		forceField:Destroy()
		local clone = game.ServerStorage.Swords.ClassicSword:Clone()
		clone.Parent = character
		debounce = true
	end
		
	local player = game.Players:GetPlayerFromCharacter(character)

	if(player) then
		event:FireClient(player, "Out")
	end
end)

----> Services
--local Players = game:GetService("Players")

----> Configuration
--local debounce = false

--script.Parent.Touched:Connect(function(hit)
--	if hit.Parent and hit.Parent:FindFirstChildOfClass("Humanoid") and not debounce then
--		debounce = true

--		local Player = Players:GetPlayerFromCharacter(hit.Parent)
--		local Character = Player and Player.Character

--		if Character:FindFirstChildOfClass("ForceField") then
--			return
--		end

--		local ForceField = Instance.new("ForceField", Character)
--		ForceField.Name = "SafeZone"

--		-- Runs a loop through the players backpack and deletes them
--		for _, Tools in Player.Backpack:GetChildren() do
--			if Tools:IsA("Tool") then
--				Tools:Destroy()
--			end
--		end

--		-- If the character finds a tool then destroy it
--		if Character:FindFirstChildOfClass("Tool") then
--			Character:FindFirstChildOfClass("Tool"):Destroy()
--		end

--		debounce = false
--	end
--end)

Try using game.Workspace:GetPartsInPart() instead of script.Parent.Touched. It’s similar to what @Lleventyate said. All it does is send a table of all physically touching parts in a list, and you can detect the player from there. Here’s a simple piece of code on how it works:

while wait() do
	local touchingParts = game.Workspace:GetPartsInPart(script.Parent)
	
	for i, part in pairs(touchingParts) do
		if part.Parent:FindFirstChild("Humanoid") then
	        -- this will detect if anything is touching it
		end
	end
end

So like:

game.Workspace:GetPartsInPart(function(part)
     if(part.Name ~= "HumanoidRootPart") then return end
	--if not game.Players:GetPlayerFromCharacter(part.Parent) then return end
	
	local character = part.Parent
	local forceField = Instance.new("ForceField")
	forceField.Name = "Safezone"
	forceField.Parent = character
	forceField.Visible = true
	local player = game.Players:GetPlayerFromCharacter(character)
	local backpack = player:FindFirstChild("Backpack")
	
	--if backpack then
	--	for _, child in pairs(backpack:GetChildren()) do
	--		if child:IsA("Tool") then
	--			child.Enabled = false
	--		end
	--	end
	--end
	
	for _, tool in ipairs(backpack:GetChildren()) do
		if tool:IsA("Tool") then
			tool:Destroy()
		end
	end

	if character:FindFirstChildOfClass("Tool") then
		character:FindFirstChildOfClass("Tool"):Destroy()
	end

	if(player) then
		event:FireClient(player, "In")
	end
end)

Yeah, but it isn’t a function. I think you would have to use a wait() loop for it to run, like my previous post code had.

I’m not the greatest at scripting (that’s why I’m here in the first palce)

Am I doing this right? I get an error for it and nothing works.

Based off the Touched implementation you’re using, you can add a guard clause in Touched to neglect players that are already safe.

After the character variable is set in Touched, do:

if character:FindFirstChildWhichIsA("ForceField") then
	return
end

It looks like you’ve done the correct opposite decision in TouchEnded, except you’re sending that RemoteEvent even if there wasn’t a forcefield in the character. I don’t know if that’s intentional.

Touched & TouchEnded aren’t very reliable. Usually, Region3 would fix the issue but since the sword unequips from the character, it triggers the Touched/TouchEnded events. I don’t exactly know why this happens, but I think it would be better to get the distance of the HumanoidRootPart and check if it is in the area of the safe zone. Probably something similar to this:

local event = game.ReplicatedStorage.SafeZone
local paart = script.Parent
local players = game:GetService("Players")
local hrps = {}
local tagged = {}

local runservice = game:GetService("RunService")

runservice.Heartbeat:Connect(function()
	
	local descendants = workspace:GetDescendants()
	
	for i = 1,#descendants do
		
		if descendants[i].Name == "HumanoidRootPart" then
			
			local find = table.find(hrps,descendants[i])
			if not find then
				
				table.insert(hrps,descendants[i])
				
			end
			
		end
		
	end
	
	for i = 1,#hrps do
		
		local magnitude = (hrps[i].Position - paart.Position).Magnitude
		local character = hrps[i].Parent :: Model
		local plr = players:GetPlayerFromCharacter(character)
		if magnitude < paart.Size.Magnitude/2.5 and not table.find(tagged,hrps[i]) then
			
			if not plr then return end
			if character:FindFirstChild("Safezone") then return end
			
			table.insert(tagged,hrps[i])
			
			local ff = Instance.new("ForceField")
			ff.Name = "Safezone"
			ff.Parent = character
			
			local sword = character:FindFirstChildOfClass("Tool")
			if sword then sword:Destroy() end
			if plr.Backpack:FindFirstChildOfClass("Tool") then plr.Backpack:FindFirstChildOfClass("Tool"):Destroy() end
			
		elseif magnitude >= paart.Size.Magnitude/2.5 and table.find(tagged,hrps[i]) then
			
			local ff = character:FindFirstChild("Safezone")
			local find = table.find(tagged,hrps[i])
			if ff then
				
				table.remove(tagged,find)
				
				delay(1,function()
					
					ff:Destroy()
					local clone = game.ServerStorage.Swords.ClassicSword:Clone()
					clone.Parent = character
					
				end)
				
			end
			
		end
		
	end
	
end)

One of my friends helped fix this last night I just forgot to post this, but all these solutions seem reasonable (sorry if you wasted time doing this, it was so late I just saved my game and got in bed).

He did use a table (I think?) by doing.

local PlayersInZone = {}

And setting stuff to true and false throughout this script.