Do for loops "rules" carry on?

Yeah, I know. I realized my mistake in my code but there’s this weird bug that only tweens the color of a specific cloud for some reason and I don’t know why

2 Likes
local TweenService = game:GetService("TweenService")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local SoundService = game:GetService("SoundService")
local Debris = game:GetService("Debris")

local LIGHTNING_STRIKE_SOUND = ReplicatedStorage:WaitForChild("Sounds"):WaitForChild("Lightning Strike 3")
local LIGHTNING_MODEL = ReplicatedStorage:WaitForChild("Models"):WaitForChild("Lightning")

local function CLOUD_COLOR_TOGGLE(CLOUD_MESH)
	TweenService:Create(CLOUD_MESH, TweenInfo.new(3, Enum.EasingStyle.Linear), {Color = Color3.fromRGB(0, 0, 0)}):Play()
	task.wait(math.random(5, 10))
	
	
	TweenService:Create(CLOUD_MESH, TweenInfo.new(3, Enum.EasingStyle.Linear), {Color = Color3.fromRGB(255, 255, 255)}):Play()
	task.wait(1)
end

local function STRIKE_PLAYER(Humanoid, Head, Cloud)
	local Lightning_Model_Clone = LIGHTNING_MODEL:Clone()
	local Lightning_Sound_Clone = LIGHTNING_STRIKE_SOUND:Clone()

	Lightning_Model_Clone.Parent = game.Workspace
	Lightning_Model_Clone:PivotTo(CFrame.new(Head.Position + Vector3.new(0, 60, 0)))

	Lightning_Sound_Clone.Parent = Head
	Lightning_Sound_Clone:Play()

	Humanoid.Sit = true
	Humanoid:TakeDamage(20)

	Debris:AddItem(Lightning_Model_Clone, 2)
	Debris:AddItem(Lightning_Sound_Clone, 2)
	task.wait(5)
	Cloud:SetAttribute("Lightning", false)
end

for i, v in script.Parent:GetDescendants() do
	if v:IsA("Folder") or v:IsA("Model") or v:IsA("SpecialMesh") or v:IsA("WeldConstraint") or v:IsA("TouchTransmitter") or v:IsA("Script") or v.Name == "Cloud" then continue end
	for ii, CLOUD_MESH in script.Parent:GetDescendants() do
		if CLOUD_MESH:IsA("Folder") or CLOUD_MESH:IsA("Model") or CLOUD_MESH:IsA("SpecialMesh") or CLOUD_MESH:IsA("WeldConstraint") or CLOUD_MESH:IsA("TouchTransmitter") or CLOUD_MESH:IsA("Script") or CLOUD_MESH.Name == "CloudPlatform" then continue end
		v.Touched:Connect(function(hit)
			if not hit.Parent:FindFirstChild("Humanoid") or v:GetAttribute("Touched", true) or v:GetAttribute("Lightning", false) then return end
			v:SetAttribute("Touched", true)
			
			if math.random(1, 100) <= 100 then
				v:SetAttribute("Lightning", true)
				task.spawn(CLOUD_COLOR_TOGGLE, CLOUD_MESH)
				task.spawn(STRIKE_PLAYER, hit.Parent:FindFirstChild("Humanoid"), hit.Parent:FindFirstChild("Head"), v)
				task.wait(1)
				v:SetAttribute("Touched", false)
			else
				task.wait(1)
				v:SetAttribute("Touched", false)
			end
		end)
	end
end

This is the code I’m using for my obby script thing and Idk why but it’s bugging out

I notice that you only refer to Lightning Strike 3 and nothing else.

Pardon the constant message changes. I’m still reading the situation.

1 Like

There’s Cloud1, Cloud2, and Cloud3. Whatever cloud I touch it will always be that same one.

For one thing, task.spawn probably doesn’t forward arguments. I know that spawn doesn’t.

local val = 6
function f(val)
   print(val)
end
spawn(f, 12)

It’ll print 6.

It does forward arguments you pass into it

task.spawn does pass varargs through to the function as parameters (this is different behavior than the deprecated spawn function)

The intention of the logic with the nested loop and the mountain of conditions for the loops is unclear to me, so it’s hard to help here.

Also, :GetAttribute() only takes 1 parameter

Is Touched attribute supposed to be a local debounce for that cloud?
What is Lightning attribute used for, and why is it never set back to true?
Is each cloud mesh a single MeshPart named “cloud”?

1 Like


Why the heck did it print Clouds 121 times when there’s way less than that?

True.

Whats the difference between cloud and cloudplatform?

The cloud is just the mesh and the cloud platform is just what the player stands on.

If I’ve understood correctly, this might be a better approach:

local TweenService = game:GetService("TweenService")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Workspace = game:GetService("Workspace")

-- Things in ReplicatedStorage are guaranteed to exist at runtime (assuming StreamingEnabled is false), no need to WaitForChild
local lightningStrikeSoundTemplate = ReplicatedStorage.Sounds:FindFirstChild("Lightning Strike 3")
local lightningModelTemplate = ReplicatedStorage.Models.Lightning

local LIGHTNING_CHANCE_PERCENT = 100 -- 0% to 100%
local MIN_SECONDS_BETWEEN_STRIKES = 5
local CLOUD_STRIKE_COLOR_TIME = {
	Min = 5,
	Max = 10
}

local random = Random.new()
local tweenInfo = TweenInfo.new(3, Enum.EasingStyle.Linear)

local CLOUD_STRIKE_COLOR = Color3.fromRGB(0, 0, 0)
local CLOUD_NORMAL_COLOR = Color3.fromRGB(255, 255, 255)
local LIGHTNING_OFFSET = Vector3.new(0, 60, 0)

local strikeColorTween = nil
local function tweenCloudColor(cloudMesh: MeshPart)
	strikeColorTween = TweenService:Create(cloudMesh, tweenInfo, { Color = CLOUD_STRIKE_COLOR })
	strikeColorTween.Completed:Once(function(tweenStatus: Enum.TweenStatus)
		if tweenStatus ~= Enum.TweenStatus.Completed then
			return
		end
		local thisStrikeColorTween = strikeColorTween
		task.wait(random:NextInteger(CLOUD_STRIKE_COLOR_TIME.Min, CLOUD_STRIKE_COLOR_TIME.Max))
		if thisStrikeColorTween ~= strikeColorTween then
			return
		end
		TweenService:Create(cloudMesh, tweenInfo, { Color = CLOUD_NORMAL_COLOR }):Play()
	end)
	strikeColorTween:Play()
end

local function strikePlayer(humanoid, head)
	local lightningModel = lightningModelTemplate:Clone()
	local lightningSound = lightningStrikeSoundTemplate:Clone()
	
	local lightningCFrame = CFrame.new(head.Position + LIGHTNING_OFFSET)
	lightningModel:PivotTo(lightningCFrame)
	lightningModel.Parent = Workspace

	lightningSound.Parent = head
	lightningSound:Play()
	lightningSound.Ended:Once(function()
		lightningSound:Destroy()
		lightningModel:Destroy()
	end)

	humanoid.Sit = true
	humanoid:TakeDamage(20)
end

local function createLightningStrike(cloudPlatform: BasePart, humanoid: Humanoid)
	local head = humanoid.Parent:FindFirstChild("Head")
	if not head then
		return
	end
	
	local cloudMesh = cloudPlatform:FindFirstChild("cloud")
	tweenCloudColor(cloudMesh)
	strikePlayer(humanoid, head)
end

local function isCloudPlatform(instance: Instance)
	-- A better way to do this is tag your cloud platforms with a "CloudPlatform" tag and check instance:HasTag("CloudPlatform") here; or, iterate over CollectionService:GetTagged("CloudPlatform") instead of filtering descendants like this
	return instance.Name == "CloudPlatform"
end

for _, descendant in script.Parent:GetDescendants() do
	if not isCloudPlatform(descendant) then
		continue
	end
	
	local cloudPlatform = descendant :: BasePart
	local debounce = false
	
	cloudPlatform.Touched:Connect(function(hit: BasePart)
		if debounce then
			return
		end

		local humanoid = hit.Parent:FindFirstChildOfClass("Humanoid")
		if not humanoid then
			return
		end
		
		debounce = true
		
		local shouldStrike = random:NextInteger(1, 100) <= LIGHTNING_CHANCE_PERCENT
		
		if shouldStrike then
			createLightningStrike(cloudPlatform, humanoid)
			task.wait(MIN_SECONDS_BETWEEN_STRIKES)
		else
			task.wait(1)
		end

		debounce = false
	end)
end

I think I understand most of it but why is there a variable for strikeColorTween? Why not just tween it in the function and I don’t know what StreamingEnabled is.

It looked like your code wanted some delay between when the cloud turns dark and when it turns back to white. Because tweenCloudColor could be called during that delay, it needs to check after the delay if it was called again at some point during the delay. Put another way, it needs to know if it’s still the most recent call. Because if it isn’t, then it shouldn’t set the color back to white. A second cloud strike during the delay should basically cancel the first strike’s delayed set-to-white operation.

The way that it knows if it’s the most recent call is by storing the created tween in a global variable that changes each time the function is called. So it is then able to compare “thisStrikeColorTween” with “strikeColorTween” (the global one) to see if the global one still matches “this” one. If it does, then it knows it’s still the latest call, and so it does not need to cancel turning it to white.