Crystals appearing in random spots but not in buildings - how would i do this?

Well I thought that would be how you could find the part the player touched…

part.Hit:Connect() has automatically passes the part the object hit as the first argument.

local debounce = false
part.Hit:Connect(function(partthatithit)
    if debounce then return end
    debounce = true

    -- rest of the code
    debounce = false
end)
1 Like

Does that mean the crystal, or the character?

if the part is the crystal, then hit is an instance descended from the character.

-- Take the crystal that was touched as an argument
local function crystalTouched(crystal, hit)
	if not deb then
		if hit.Parent:FindFirstChild("Humanoid") then
			local plr = game.Players:GetPlayerFromCharacter(hit.Parent)
			local crystals = plr:WaitForChild('leaderstats').Crystals
			
			crystals.Value = crystals.Value + (1 * plr:WaitForChild("Gamepasses").CrystalsMultiplier.Value)
			
			deb = true
			
                        -- We can use the crystal that was touched since we already know which crystal it is
			crystal.Parent = crystalFolder
			--wait(math.random(10,20))
			wait(1) -- for testing purposes
			crystal.Parent = workspace
			setRandomPosition(crystal)
			deb = false
		end
	end
end

for i, crystal in pairs(workspace:GetChildren()) do
	if crystal.Name == "Crystal" then
                -- Using an anonymous function here to pass the crystal that was touched
		crystal.Touched:Connect(function(hit) crystalTouched(crystal, hit) end)
	end
end

I would use an anonymous function to pass the crystal that was touched as well as the part that touched it.

2 Likes

This is slightly unrelated but I prefer using debounces like this:

function debounce(func)
    local running = false
    return function(...)
        if not running then
            running = true
            func(...)
            running = false
        end
    end
end

This function was pulled from the bottom of roblox’s article on debounce and it makes .Touched connections look much cleaner. This is how you use it:

part.Touched:Connect(debounce(function(hitPart)
    -- now you don't need anything related to debounce in here
end)) -- don't forget the extra ) from the debounce()
1 Like

uhh…

local rs = game.ReplicatedStorage
local crystalFolder = rs.Crystals

local deb = false
local RNG = Random.new()
local bases = {
    {Object = workspace.Ground.Grass, Weight = 5};
    {Object = workspace.Path, Weight = 2};
}

local function random(choices)
	local weightSum = 0
	for i = 1, #choices do
		weightSum = weightSum + choices[i].Weight
	end

	local rand = RNG:NextInteger(0, weightSum)
	for i = 1, #choices do
		if rand <= choices[i].Weight then
			return choices[i].Object
		end
		rand = rand - choices[i].Weight
	end
end

local function setRandomPosition(item)
	local Base = random(bases)

	item.CFrame = CFrame.new(
		RNG:NextInteger((-Base.Size.X / 2), (Base.Size.X / 2)),
		Base.Position.Y + (Base.Size.Y / 2) + (item.Size.Y / 2),
		RNG:NextInteger((-Base.Size.Z / 2), (Base.Size.Z / 2))
	) * CFrame.Angles(0, math.rad(RNG:NextInteger(0, 360)), 0)
end

local function crystalTouched(crystal, hit)
	if not deb then
		if hit.Parent:FindFirstChild("Humanoid") then
			local plr = game.Players:GetPlayerFromCharacter(hit.Parent)
			local crystals = plr:WaitForChild('leaderstats').Crystals
			
			crystals.Value = crystals.Value + (1 * plr:WaitForChild("Gamepasses").CrystalsMultiplier.Value)
			
			deb = true
			
			crystal.Parent = crystalFolder
			print("Yay")
			--wait(math.random(8,20))
			wait(1)
			print("Waited")
			crystal.Parent = workspace
			setRandomPosition(crystal)
			deb = false
		end
	end
end

for i, crystal in pairs(workspace:GetChildren()) do
	if crystal.Name == "Crystal" then
		crystal.Touched:Connect(function(hit)
			crystalTouched(crystal, hit)
		end)
	end
end

I got print statements in the output but then I think the bases disappeared.

I also got this error again! (yay)

attempt to call a nil value

in between the two print statements.

local rs = game.ReplicatedStorage
local crystalFolder = rs.Crystals

local deb = false
local RNG = Random.new()
local bases = {
    {Object = workspace.Ground.Grass, Weight = 5};
    {Object = workspace.Path, Weight = 2};
}

local function random(choices)
	local weightSum = 0
	for i = 1, #choices do
		weightSum = weightSum + choices[i].Weight
	end

	local rand = RNG:NextInteger(0, weightSum)
	for i = 1, #choices do
		if rand <= choices[i].Weight then
			return choices[i].Object
		end
		rand = rand - choices[i].Weight
	end
end

local function setRandomPosition(item)
	local Base = random(bases)

	item.CFrame = CFrame.new(
		RNG:NextInteger((-Base.Size.X / 2), (Base.Size.X / 2)),
		Base.Position.Y + (Base.Size.Y / 2) + (item.Size.Y / 2),
		RNG:NextInteger((-Base.Size.Z / 2), (Base.Size.Z / 2))
	) * CFrame.Angles(0, math.rad(RNG:NextInteger(0, 360)), 0)
end

local function crystalTouched(crystal, hit)
	if not deb then
                deb = true
		if hit.Parent:FindFirstChild("Humanoid") then
			local plr = game.Players:GetPlayerFromCharacter(hit.Parent)
			local crystals = plr:WaitForChild('leaderstats').Crystals
			
			crystals.Value = crystals.Value + (1 * plr:WaitForChild("Gamepasses").CrystalsMultiplier.Value)
			
			crystal.Parent = crystalFolder
			print("Yay")
			--wait(math.random(8,20))
			wait(1)
			print("Waited")
			crystal.Parent = workspace
			setRandomPosition(crystal)
			deb = false
		end
	end
end

for i, crystal in pairs(workspace:GetChildren()) do
	if crystal.Name == "Crystal" then
		crystal.Touched:Connect(function(hit)
			crystalTouched(crystal, hit)
		end)
	end
end

Try this.
I moved the deb = true to the top of the if statement. There will be a fraction of a second of lag while assigning the variables because you used :WaitForChild() and :GetPlayerFromCharacter(). During this time, a new .Touched even could have fired on the same crystal, and since deb is not set to true yet, the function executes.

1 Like

Ok it worked but the bases are still disappearing along with the other crystals.

The bases should just be transparent CanCollide = false rectangles that mark the areas where the crystals can spawn. You’re not supposed to be able to see them.

1 Like

ohhhh… that’s why.

If the problem is fixed you can mark the answer that fixed it for you as the solution. It will help others when they view the thread by allowing them to jump straight to the answer rather than read through every reply.

1 Like

One problem still:
The grass keeps moving as well as crystals that were not touched:

local crystalFolder = game.ServerStorage.Crystals

local deb = false
local RNG = Random.new()
local bases = {
    {Object = workspace.Base, Weight = 3};
    {Object = workspace.Base2, Weight = 4};
}

local function random(choices)
	local weightSum = 0
	for i = 1, #choices do
		weightSum = weightSum + choices[i].Weight
	end

	local rand = RNG:NextInteger(0, weightSum)
	for i = 1, #choices do
		if rand <= choices[i].Weight then
			return choices[i].Object
		end
		rand = rand - choices[i].Weight
	end
end

local function setRandomPosition(item)
	local Base = random(bases)

	item.CFrame = CFrame.new(
		RNG:NextInteger((-Base.Size.X / 2), (Base.Size.X / 2)),
		Base.Position.Y + (Base.Size.Y / 2) + (item.Size.Y / 2),
		RNG:NextInteger((-Base.Size.Z / 2), (Base.Size.Z / 2))
	) * CFrame.Angles(0, math.rad(RNG:NextInteger(0, 360)), 0)
end

local function crystalTouched(crystal, hit)
	if not deb then
		deb = true
		if hit.Parent:FindFirstChild("Humanoid") then
			local plr = game.Players:GetPlayerFromCharacter(hit.Parent)
			local crystals = plr:WaitForChild('leaderstats').Crystals
			
			crystals.Value = crystals.Value + (1 * plr:WaitForChild("Gamepasses").CrystalsMultiplier.Value)
			
			crystal.Parent = crystalFolder
			print("Yay")
			--wait(math.random(8,20))
			wait(1)
			print("Waited")
			crystal.Parent = workspace
			setRandomPosition(crystal)
			deb = false
		else
			deb = false
		end
	end
end


script.Parent.Touched:Connect(function(hit)
	crystalTouched(script.Parent, hit)
end)

Note: I moved the script into every crystal.

It works again after I deleted some welds. (Idk how that does anything but whatever maybe I’m dumb)

One thing though: some crystals appear in the tycoons which could be bad.

That probably means that a part of one of the bases is in the tycoon.


Nope.

local bases = {
    {Object = workspace.Base, Weight = 3};
    {Object = workspace.Base2, Weight = 4};
}