Rounds ending instantaneously

I have a round system that works very well, until the rounds actually start. The initial timer counts from 10 and when it reaches 0, the problem occurs. The round instantly ends for no apparent reason even when there are at least 2 players. No errors are displayed in output and it’s really frustrating me. I’ve tried using for loops, repeat loops, and while loops to manage the actual round. Nothing works…

Round System code:

local round = require(script.Round)
local configuration = script.Parent.Parent.Others
local intermissionTime = configuration.IntermissionTime.Value
local status = workspace.Stats.Status
local gateA = workspace.Map.Gate.GateA
local gateB = workspace.Map.Gate.GateB
local tweenService = game:GetService("TweenService")

task.wait(5)

while true do
	tweenService:Create(gateA, TweenInfo.new(0.5, Enum.EasingStyle.Bounce, Enum.EasingDirection.InOut), {CFrame = gateA.Parent.GateADestination.CFrame}):Play()
	tweenService:Create(gateB, TweenInfo.new(0.5, Enum.EasingStyle.Bounce, Enum.EasingDirection.InOut), {CFrame = gateB.Parent.GateBDestination.CFrame}):Play()
	for i = intermissionTime, 1, -1 do
		status.Value = `Intermission: {i} second{i ~= 1 and "s" or ""} left!`
		task.wait(1)
	end
	tweenService:Create(gateA, TweenInfo.new(0.5, Enum.EasingStyle.Bounce, Enum.EasingDirection.InOut), {CFrame = gateA.Parent.GateAStart.CFrame}):Play()
	tweenService:Create(gateB, TweenInfo.new(0.5, Enum.EasingStyle.Bounce, Enum.EasingDirection.InOut), {CFrame = gateB.Parent.GateBStart.CFrame}):Play()
	if #round:GetPlaying() < 2 then
		status.Value = "Not enough players to start!"
		for _, v in round:GetPlaying() do
			v:LoadCharacter()
		end
		task.wait(2)
		continue
	end
	local map = game.ServerStorage.Maps:GetChildren()[math.random(#game.ServerStorage.Maps:GetChildren())]:Clone()
	status.Value = `Chosen map: {map.Name}`
	map.Parent = workspace
	task.wait(3)
	if #round:GetPlaying() < 2 then
		status.Value = "Not enough players to start!"
		for _, v in round:GetPlaying() do
			v:LoadCharacter()
		end
		task.wait(2)
		map:Destroy()
		continue
	end
	for _, v in round:GetPlaying() do
		v.Character.Parent = workspace.Characters.InGame
	end
	require(script.Round[map.Name])(map)
	local names = {}
	for _, v in round:GetInGame() do
		table.insert(names, v.Name)
	end
	status.Value = #names > 0 and `Winner{#names > 1 and "s" or ""}: {table.concat(names, ", ")}` or "No winners!"
	for _, v in round:GetInGame() do
		v:LoadCharacter()
		v.leaderstats.Wins.Value += 1
		v.PlayerData.Coins.Value += math.random(100, 200)
	end
	map:Destroy()
	task.wait(3)
end

Example scripts (bug applies to all maps):

  • The floor is lava:
local status = workspace.Stats.Status
return function(map)
	for i = 10, 1, -1 do
		status.Value = `Lava will start rising in {i} second{i == 1 and "" or "s"}!`
		task.wait(1)
	end
	local i = map:GetAttribute("Time")
	local debounce = false
	map.Lava.Touched:Connect(function(hit)
		if debounce then return end
		debounce = true
		if hit and hit.Parent:FindFirstChild("Humanoid") then
			hit.Parent.Humanoid:TakeDamage(20)
		end
		debounce = false
	end)
	game:GetService("TweenService"):Create(map.Lava, TweenInfo.new(i, Enum.EasingStyle.Bounce), {CFrame = CFrame.new(-295.974, -44.584, -193.312)}):Play()
	while #workspace.Characters.InGame:GetChildren() > 1 and i > 0 do
		status.Value = `The lava will stop rising in {i} second{i == 1 and "" or "s"}!`
		i -= 1
		task.wait(1)
	end
end
  • Spleef:
local status = workspace.Stats.Status

return function(map)
	for _, v in require(script.Parent):GetInGame() do
		v.Character:PivotTo(map.Tiles:GetChildren()[math.random(#map.Tiles:GetChildren())]:GetPivot() * CFrame.new(0, 2.5, 0))
	end
	map.Lava.Touched:Connect(function(hit)
		if hit and hit.Parent:FindFirstChild("Humanoid") then
			hit.Parent.Humanoid.Health = 0
		end
	end)
	for i = 10, 1, -1 do
		status.Value = `Tiles will start to disappear under your feet in {i} second{i == 1 and "" or "s"}!`
		task.wait(1)
	end
	for _, v: Part in map.Tiles:GetChildren() do
		local active = false
		local tween = game:GetService("TweenService"):Create(v, TweenInfo.new(0.75, Enum.EasingStyle.Exponential), {Transparency = 1})
		tween.Completed:Connect(function()
			task.delay(5, function()
				local t = game:GetService("TweenService"):Create(v, TweenInfo.new(0.75, Enum.EasingStyle.Exponential), {Transparency = 0})
				t.Completed:Once(function()
					v.CanCollide = true
					active = false
				end)
				t:Play()
			end)
			v.CanCollide = false
		end)
		v.Touched:Connect(function(hit)
			if active then return end
			if hit and hit.Parent:FindFirstChild("Humanoid") then
				active = true
				tween:Play()
			end
		end)
	end
	
	local i = map:GetAttribute("Time")
	while #workspace.Characters.InGame:GetChildren() > 1 and i > 0 do
		status.Value = `The tiles will stop disappearing in {i} second{i == 1 and "" or "s"}!`
		i -= 1
		task.wait(1)
	end
end

From what I can see, You are not using any “else statements”, meaning that it doesn’t matter whether you have 1 or 2 players. Code like this would still run.

Given, I haven’t read your code entirely and I have no idea how to use continue, but I think this is the reason.

Also, this round system seems very complicated. It should be quite simple.

1 Like

The continue keyword just restarts the closest nested loop. In this case, it prevents the rest of the while loop from running, and restarts it.

To clarify:
As soon as the for loop ends in every round from 10 to 1, the round instantaneously ends even when there is a while loop afterwards. The “Time” attribute is indeed set correctly.

local status = workspace.Stats.Status

return function(map)
	for _, v in require(script.Parent):GetInGame() do
		v.Character:PivotTo(map.Tiles:GetChildren()[math.random(#map.Tiles:GetChildren())]:GetPivot() * CFrame.new(0, 2.5, 0))
	end
	map.Lava.Touched:Connect(function(hit)
		if hit and hit.Parent:FindFirstChild("Humanoid") then
			hit.Parent.Humanoid.Health = 0
		end
	end)
	for i = 10, 1, -1 do
		status.Value = `Tiles will start to disappear under your feet in {i} second{i == 1 and "" or "s"}!`
		task.wait(1)
	end --After this loop, the round ends.
	for _, v in map.Tiles:GetChildren() do
		local active = false
		local tween = game:GetService("TweenService"):Create(v, TweenInfo.new(0.75, Enum.EasingStyle.Exponential), {Transparency = 1})
		tween.Completed:Connect(function()
			task.delay(5, function()
				local t = game:GetService("TweenService"):Create(v, TweenInfo.new(0.75, Enum.EasingStyle.Exponential), {Transparency = 0})
				t.Completed:Once(function()
					v.CanCollide = true
					active = false
				end)
				t:Play()
			end)
			v.CanCollide = false
		end)
		v.Touched:Connect(function(hit)
			if active then return end
			if hit and hit.Parent:FindFirstChild("Humanoid") then
				active = true
				tween:Play()
			end
		end)
	end
	
	local i = map:GetAttribute("Time")
	while #workspace.Characters.InGame:GetChildren() > 1 and i > 0 do
		status.Value = `The tiles will stop disappearing in {i} second{i == 1 and "" or "s"}!`
		i -= 1
		task.wait(1)
	end
end
local status = workspace.Stats.Status
return function(map)
	for i = 10, 1, -1 do
		status.Value = `Lava will start rising in {i} second{i == 1 and "" or "s"}!`
		task.wait(1)
	end --Round ends after this loop.
	local i = map:GetAttribute("Time")
	local debounce = false
	map.Lava.Touched:Connect(function(hit)
		if debounce then return end
		debounce = true
		if hit and hit.Parent:FindFirstChild("Humanoid") then
			hit.Parent.Humanoid:TakeDamage(20)
		end
		debounce = false
	end)
	game:GetService("TweenService"):Create(map.Lava, TweenInfo.new(i, Enum.EasingStyle.Bounce), {CFrame = CFrame.new(-295.974, -44.584, -193.312)}):Play()
	while #workspace.Characters.InGame:GetChildren() > 1 and i > 0 do
		status.Value = `The lava will stop rising in {i} second{i == 1 and "" or "s"}!`
		i -= 1
		task.wait(1)
	end
end
1 Like

I suspect that this isn’t yielding, but because of how require works, the returned function should yield the thread. So I’m a bit confused.

okay your code is extremely cramped so i’ll just ask

can you show the code that’s responsible for round starting and ending?

The map scripts. As in the Spleef and The Floor Is Lava and whatnot.

This loop is managing the round ending.

And this block of code manages the start:

If you want, I can link the game so you can join and see how it works.

hmmmm

sorry if this is gonna be a bad idea

do you think you could store the players in 2 folders

folder 1, called InMenu(or something like that)
and folder 2, called InGame

when a game starts, move the player from InMenu to InGame
when a player dies, move the player from InGame to InMenu

and have something like InGame.ChildRemoved to detect whether the player count goes below 2 (or reaches 1 whatever), if so, then end the game

then handle the lava timer somewhere else

and once the lava timer reaches the end and players ingame > 1, also end the game

honestly a while and for loop for stuff like this just seems extremely annoying to do
i gave you a scheme that i use, so it should work

if that’s not something you wanna do, then i don’t really have an idea

There are multiple minigames, and in the future, some minigames that don’t use lava. I code the lava in the respective minigame so that it matches correctly.

In workspace, I already have a Configuration named Characters housing three folders: InGame, Playing, and Not Playing.

hmmm

then maybe handle both the timer and amount of players in the same script?

local somefunction()
   if #ingame:GetChildren() < 2 then
     endgame
   end
end)

ingame.Removed:Connect(somefunction)

while timer > 1 do
   -- do your stuff
end

endgame

then just use Not playing instead of InMenu and Playing instead of InGame, unless your Not Playingfolder servers a differnet purpose

It could get to the point that there are minigames that end at a different way than player count.

I have a giant hole in my lobby where the rounds are set up. Those who aren’t in the hole are set to Not Playing, those who jump in are Playing, and once intermission ends, if there are at least two players in Playing, they are set to InGame.

i’m gonna assume that by this you mean you wanna handle minigames in one script, instead of having one individual script for each minigame

maybe you could have a ModuleScript that stores each minigame, let me explain

local module = require(pathtomodule)

module.SomeFunction(minigame name, minimum players, more parameters if needed)
local minigameFunctions = {
   Spleef = function(minimum players)
        -- do your thing
   end,
   
   FloorIsLava = function(minimum players)
        -- do your thing
   end
}

local module = {}

function module.SomeFunction(minigameName,minimum players)
   minigameFunctions[minigameName](minimum players)
end

return module

then try tampering around with those folders, i don’t really have any different ideas >_>

If I were to do this, the script would get so long it wouldn’t be so easy to work with. I’m more comfortable with this setup:


can’t you just replicate this inside of those module scripts then?

1 Like

I guess I could try this. I’ll follow up after testing and let you know if it works.

Spleef now functions correctly, however after implementing the same exact detection to the other maps, the ChildRemoved event does not fire until there are 0 players left…

hmmm

can i see the code? charrrrrrrrrrrrrrrrrrr

workspace.Characters.InGame.ChildRemoved:Connect(function(c) --never fires until 0 people remain
		for _, v in workspace.Characters.InGame:GetChildren() do
			print(v.Name)
		end
		print(`{c.Name} has died..`)
		if #workspace.Characters.InGame:GetChildren() == 1 then
			i = 0
		end
		print(#workspace.Characters.InGame:GetChildren(), i)
	end)
	while i > 0 do
		status.Value = `The lava will stop rising in {i} second{i == 1 and "" or "s"}!`
		i -= 1
		task.wait(1)
	end