Is this code for a round system optimal?

Is the while loop necessary? If not, what else can I use?

local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local ServerStorage = game:GetService("ServerStorage")
local Teams = game:GetService("Teams")

local spleefers = Teams.Spleefers
local spectators = Teams.Spectators
local spectatorTools = ServerStorage.SpectatorTools
local spleeferTools = ServerStorage.SpleeferTools
local values = ReplicatedStorage.Values
local intermissionValue = values.IntermissionValue
local countdownValue = values.CountdownValue
local map = ServerStorage.Map

local anchoredPlayers = {}

local INTERMISSION_TIME = 10
local COUNTDOWN_TIME = 3
local LAVA_HEIGHT_LIMIT = 61

local function giveTool(player)
	if not player or not player:IsDescendantOf(game.Players) then return end -- Prevents errors if a player leaves.

	-- Clean out Backpack and Character (in case they have equipped tools).
	local function clearTools(container)
		for _, tool in container:GetChildren() do
			if tool:IsA("Tool") then
				tool:Destroy()
			end
		end
	end
	clearTools(player.Backpack)
	if player.Character then
		clearTools(player.Character)
	end

	-- Gives players their team tools.
	if player.Team == Teams.Spectators then
		for _, tools in spectatorTools:GetChildren() do
			tools:Clone().Parent = player.Backpack
		end
	elseif player.Team == Teams.Spleefers then
		for _, tools in spleeferTools:GetChildren() do
			tools:Clone().Parent = player.Backpack
		end
	end
end

-- Random position generator TeleportPlatform.
local function getRandomPositionOnPart(part)
	local size = part.Size
	local position = part.Position

	local minX = position.X - size.X / 2
	local maxX = position.X + size.X / 2
	local minZ = position.Z - size.Z / 2
	local maxZ = position.Z + size.Z / 2
	local y = position.Y

	local randomX = math.random() * (maxX - minX) + minX
	local randomZ = math.random() * (maxZ - minZ) + minZ
	return Vector3.new(randomX, y, randomZ)
end

-- Prepares the game for a new round.
local function roundStart()
	if workspace:FindFirstChild("Map") then
		workspace.Map:Destroy()
	end
	local newMap = map:Clone()
	newMap.Parent = workspace

	table.clear(anchoredPlayers)
	for _, player in Players:GetPlayers() do
		local character = player.Character or player.CharacterAdded:Wait()
		local rootPart = character:FindFirstChild("HumanoidRootPart")

		if player:GetAttribute("AFK") == false then -- If a player is not AFK, then put them in the next round.
			player.Team = spleefers
			giveTool(player)

			if rootPart then
				local randomPosition = getRandomPositionOnPart(newMap.TeleportPlatform)
				rootPart.CFrame = CFrame.new(randomPosition)
				rootPart.Anchored = true
				table.insert(anchoredPlayers, rootPart)
			end
		end
	end
	return newMap
end

-- Runs when the game ends.
local function roundEnd()
	for _, spleefer in spleefers:GetPlayers() do
		local humanoid = spleefer.Character:FindFirstChild("Humanoid")
		humanoid.Health = 0
	end
end

local function onPlayerAdded(player)
	local function onCharacterAdded(character)
		local humanoid = character:WaitForChild("Humanoid")
		giveTool(player)

		-- Runs when a character dies.
		local function onDied()
			if player.Team == spleefers then -- If spleefers die, they become spectators.
				player.Team = spectators
			end
		end
		humanoid.Died:Connect(onDied)
	end
	player.CharacterAdded:Connect(onCharacterAdded)
end
Players.PlayerAdded:Connect(onPlayerAdded)

-- Round system
while true do
	-- Wait for a minimum number of players before starting a round.
	if #Players:GetPlayers() < 2 then
		task.wait()
		continue -- Skip the rest of this iteration.
	end
	
	for i = INTERMISSION_TIME, 0, -1 do
		intermissionValue.Value = i
		task.wait(1)
	end
	
	local currentMap = roundStart()
	
	for i = COUNTDOWN_TIME, 0, -1 do
		countdownValue.Value = i
		task.wait(1)
	end
	
	for _, rootPart in ipairs(anchoredPlayers) do
		if rootPart and rootPart.Parent then
			rootPart.Anchored = false
		end
	end
	
	-- If there is 1 or less spleefers, then end the round until the lava reaches the top. 
	repeat
		if #spleefers:GetPlayers() <= 1 then
			roundEnd()
			break
		end
		task.wait()
	until currentMap.Lava.Size.Y == LAVA_HEIGHT_LIMIT
	-- If the lava reaches the top, then end the round.
	if currentMap.Lava.Size.Y >= LAVA_HEIGHT_LIMIT then
		roundEnd()
	end
end
3 Likes

Yeah the code looks fine. The only thing that really stood out to me is that you wait for players to respawn during the teleportation phase. I think this could potentially break the game if a player resets and leaves at the right time? I’m not entirely certain though. Usually round code either assumes a player that is dead either isn’t joining, or will temporarily bind to it as a connection for a few seconds for the teleport before unbinding.

As for the while loop, it is necessary. You intend to loop rounds indefinitely. The only other options are a different loop (a repeat until could work but is not standard). A for loop is bad because they are designed to end, though you can make them infinite it’s also non standard. And recursion is another option but that’s takes up more memory every round until you eventually error out.