ObjectValues referenced break script, don't know why

Working on a one-sided CTF system for my game, where one team has to capture the flag from the other that has to defend it. There’s a lot of ObjectValues involved, them being who the player with the flag is, and, in the event the player with the flag dies, what the brick that can be picked up is.

I’ve been testing this script all night; it worked before but now it suddenly doesn’t work, and output gave me nothing at all.
Everything is defined in the script. Can someone help me?

local r = script.Parent:WaitForChild("return")
local c = script.Parent:WaitForChild("capture")
local r_Show = r:WaitForChild("Showy")
local c_Show = c:WaitForChild("Showy")
local r_Bag = r:WaitForChild("Bag")
local c_Bag = c:WaitForChild("Bag")
local score = workspace:WaitForChild("Score")
local where_cap_drops = workspace --- easy change for maps
local cap = script:WaitForChild("cap")
local holder = script:WaitForChild("holder")
local sign = c_Show:WaitForChild("Img")
local Taken = c:WaitForChild("Taken")

---there are functions for capturing and returning flag, but they aren't relevant atm

holder.Changed:Connect(function()
	if holder.Value ~= nil then
		holder.Value:WaitForChild("Humanoid").Died:Connect(function()
			print('died')
			local torso = holder.Value:WaitForChild("UpperTorso") -- we can change 'torso' to whatever
			holder.Value = nil
			print('died2')
			cap.Value = torso
			torso.Parent = where_cap_drops
			torso.Name = "Cap"
			--wait(1)
			torso.Anchored = true
			print(cap.Value.Position)
		end)
	end
end)

cap.Changed:Connect(function()
	if cap.Value ~= nil then
		cap.Value.Touched:Connect(function(part)
			local humanoid = part.Parent:FindFirstChild("Humanoid") 
			if (humanoid ~= nil) then	-- if a humanoid exists, then
				local user = game.Players:GetPlayerFromCharacter(part.Parent)
				if user.Team ~= c.team.Value then --- teammate picks up flag from dead teammate
					local mb = Instance.new("BoolValue",part.Parent)
					mb.Name = "MoneyBag"
					holder.Value = part.Parent
					if cap.Value:FindFirstChild("Img") then
						cap.Value.Img:Clone().Parent = humanoid.Parent.UpperTorso -- torso
					end
				else ---- defending team returns flag to base
					c_Bag.Transparency = 0
					Taken.Value = false
					sign.Parent = c_Show		
				end
				cap.Value:Destroy()
			end 
		end)
	end	
end)

The weird thing, is when you die in the function that detects onDied, the holder value doesn’t get cleared, and the holder value isn’t nil despite the Torso of the deceased existing nowhere in the game.

Is there a specific reason why you are using :WaitForChild("Humanoid") in the onDied event?

Assuming the holder.Value is only set to a Player’s character. That Player’s character should always have a Humanoid in it. When using :WaitForChild you are pausing that thread until said child exists. If said child already exists then you will create an infinite yield stopping your script. I would instead use :FindFirstChild("Humanoid"). You are checking to see if humanoid exists immediately. This may or may not fix your problem but I would try it and let me know.

I reccomend reading more on the differences between :WaitForChild() and :FindFirstChild() here

Yeah, that seemed to do quite better.

1 Like

What? Where did you get this information from? This is just not true. Here’s a direct quote from the DevHub page:

This function does not yield if a child with the given name exists when the call is made.

4 Likes

^

Just to explain this behaviour: WaitForChild implements FindFirstChild internally (or so I’m believing what I was told, though this does seem to be what happens). If the FFC fails, then the script yields and waits until a child of the specified name is added. Would be functionally equivalent to…

local function waitForChild(parent, childName) -- where parent == self on an object
    local ret = parent:FindFirstChild(childName)

    if ret then
        return ret
    end

    while not ret do
        local newChild = parent.ChildAdded:Wait()

        if newChild.Name == childName then
            ret = newChild
            break
        end
    end

    return ret
end

There’s definitely a better way to have written that, especially the while part, and I’m not accounting for the timeout parameter. This is roughly what it would look like internally, though. Probably.