You can write your topic however you want, but you need to answer these questions:
What do you want to achieve? Keep it simple and clear!
I just want my round system to assign teams with no teams being empty with different priorities ( 3 teams total planned).
Inmates with the most people, security second, then aberrations last
I plan for always a guaranteed aberration no matter lobby size, even two people.
What is the issue? Include screenshots / videos if possible!
Don’t know how to describe it the best but basically it would do one of the following.
just stop working
All players are assigned to the Inmate team and the round instantly ends.
A endless respawn loop.
What solutions have you tried so far? Did you look for solutions on the Developer Hub?
It’s genuinely hard to find the root of the problem since there are many sections. I tried tweaking some small parts which may have solved future problems but I still end up having one issue or the other. The script just bugs out when there are more than two people.
my prints don’t give me any clue to what my problem is. It’s very likely to be with the team assignment for the inmates problem, the while loop for the respawn loop problem, and it not starting again.
What is very much clear is that when there are more than three players it straight up breaks.
I’m going to head to bed and will check upon this tomorrow.
-- SERVICES
local Teams = game:GetService("Teams")
local Players = game:GetService("Players")
local Workspace = game:GetService("Workspace")
-- CONFIG
local IntermissionTime = 30 -- seconds between rounds
local DecontaminationCountdown = 120 -- optional lockdown timer
local MinPlayersToStart = 2
-- TEAM REFERENCES
local TeamInmate = Teams:FindFirstChild("Inmate")
local TeamSecurity = Teams:FindFirstChild("Security Personnel")
local TeamSpectator = Teams:FindFirstChild("Spectator")
-- SPAWN POINTS
local spawnFolder = Workspace:WaitForChild("SpawnPoints")
local InmateSpawns = spawnFolder:WaitForChild("Inmates"):GetChildren()
local SecuritySpawns = spawnFolder:WaitForChild("Security"):GetChildren()
-- STATE
local RoundActive = false
-- UTILITIES
local function shuffle(tbl)
for i = #tbl, 2, -1 do
local j = math.random(1, i)
tbl[i], tbl[j] = tbl[j], tbl[i]
end
end
-- FORCE MID-ROUND JOINERS TO SPECTATE
Players.PlayerAdded:Connect(function(player)
player.Team = TeamSpectator
player.CharacterAdded:Connect(function(char)
-- if a round is live, keep them as spectator
if RoundActive and player.Team ~= TeamSpectator then
player.Team = TeamSpectator
player:LoadCharacter()
end
end)
end)
-- MAIN FUNCTIONS
local function AssignTeams()
-- gather all active players
local pool = Players:GetPlayers()
shuffle(pool)
local total = #pool
if total < MinPlayersToStart then
warn("Not enough players to start a round.")
return false
end
-- ensure at least 1 per team
local securityCount = math.clamp(math.floor(total / 2), 1, total - 1)
for idx, player in ipairs(pool) do
-- load or wait for character
player:LoadCharacter()
local char = player.Character or player.CharacterAdded:Wait()
local root = char:WaitForChild("HumanoidRootPart", 5)
if not root then
warn("No HumanoidRootPart for", player.Name)
continue
end
if idx <= securityCount then
-- SECURITY
player.Team = TeamSecurity
local spawn = SecuritySpawns[math.random(1, #SecuritySpawns)]
root.CFrame = spawn.CFrame
else
-- INMATE
player.Team = TeamInmate
local spawn = InmateSpawns[math.random(1, #InmateSpawns)]
root.CFrame = spawn.CFrame
end
end
-- (Optional) start your decontamination timer here:
-- delay(DecontaminationCountdown, function()
-- -- fire an event or handle lockdown
-- end)
return true
end
local function WinCheck()
local aliveIn, aliveSec = 0, 0
for _, player in ipairs(Players:GetPlayers()) do
if player.Team == TeamInmate or player.Team == TeamSecurity then
local hum = player.Character and player.Character:FindFirstChildWhichIsA("Humanoid")
if hum and hum.Health > 0 then
if player.Team == TeamInmate then
aliveIn += 1
else
aliveSec += 1
end
end
end
end
if aliveIn == 0 and aliveSec == 0 then
return nil -- e.g. everybody down simultaneously
elseif aliveSec == 0 then
return "Inmates"
elseif aliveIn == 0 then
return "Security"
end
return nil -- round continues
end
local function EndRound(winner)
RoundActive = false
print(("Round ended. Winner: %s"):format(winner))
-- reset everyone to spectator
for _, player in ipairs(Players:GetPlayers()) do
player.Team = TeamSpectator
player:LoadCharacter()
end
end
-- MAIN LOOP
task.spawn(function()
while true do
-- wait for minimum players
repeat
if #Players:GetPlayers() < MinPlayersToStart then
print("Waiting for players... (" .. MinPlayersToStart .. " required)")
task.wait(5)
end
until #Players:GetPlayers() >= MinPlayersToStart
-- reset all to Spectator
for _, p in ipairs(Players:GetPlayers()) do
p.Team = TeamSpectator
p:LoadCharacter()
end
-- start a new round
print("----- Starting Round -----")
if not AssignTeams() then
task.wait(IntermissionTime)
continue
end
RoundActive = true
-- monitor for win
while RoundActive do
local win = WinCheck()
if win then
EndRound(win)
break
end
task.wait(3)
end
-- intermission
print("Intermission for " .. IntermissionTime .. " seconds")
task.wait(IntermissionTime)
end
end)
Please do not ask people to write entire scripts or design entire systems for you. If you can’t answer the three questions above, you should probably pick a different category.
Here’s the revised version of your script, it should work (i think)
here are the changees:
Guaranteed at least one Aberration.
Dynamically distributes players between Aberration, Security, and Inmates based on lobby size.
Spawn Points:
Randomly assigns each player to the correct team-specific spawn point.
Win Check:
Evaluates all three teams (Inmates, Security, Aberration) to determine the winner.
Respawn Fixes:
Prevents endless respawn loops by correctly handling team assignments during active rounds.
Debugging:
Added print statements for tracking team assignments and debugging issues.
-- SERVICES
local Teams = game:GetService("Teams")
local Players = game:GetService("Players")
local Workspace = game:GetService("Workspace")
-- CONFIG
local IntermissionTime = 30 -- seconds between rounds
local DecontaminationCountdown = 120 -- optional lockdown timer
local MinPlayersToStart = 2
-- TEAM REFERENCES
local TeamInmate = Teams:FindFirstChild("Inmate")
local TeamSecurity = Teams:FindFirstChild("Security Personnel")
local TeamAberration = Teams:FindFirstChild("Aberration")
local TeamSpectator = Teams:FindFirstChild("Spectator")
-- SPAWN POINTS
local spawnFolder = Workspace:WaitForChild("SpawnPoints")
local InmateSpawns = spawnFolder:WaitForChild("Inmates"):GetChildren()
local SecuritySpawns = spawnFolder:WaitForChild("Security"):GetChildren()
local AberrationSpawns = spawnFolder:WaitForChild("Aberration"):GetChildren()
-- STATE
local RoundActive = false
-- UTILITIES
local function shuffle(tbl)
for i = #tbl, 2, -1 do
local j = math.random(1, i)
tbl[i], tbl[j] = tbl[j], tbl[i]
end
end
-- FORCE MID-ROUND JOINERS TO SPECTATE
Players.PlayerAdded:Connect(function(player)
player.Team = TeamSpectator
player.CharacterAdded:Connect(function(char)
if RoundActive and player.Team == TeamSpectator then
player:LoadCharacter() -- Reload if Spectator
elseif RoundActive and player.Team ~= TeamSpectator then
-- Prevent unnecessary respawn loops
return
end
end)
end)
-- MAIN FUNCTIONS
local function AssignTeams()
-- Gather all active players
local pool = Players:GetPlayers()
shuffle(pool)
local total = #pool
if total < MinPlayersToStart then
warn("Not enough players to start a round.")
return false
end
-- Ensure at least 1 Aberration and distribute teams based on priority
local aberrationCount = 1 -- Always guarantee at least one Aberration
local securityCount = math.clamp(math.floor((total - aberrationCount) / 3), 1, total - aberrationCount)
local inmateCount = total - securityCount - aberrationCount
local assignedAberration = 0
local assignedSecurity = 0
local assignedInmates = 0
for idx, player in ipairs(pool) do
-- Load or wait for character
player:LoadCharacter()
local char = player.Character or player.CharacterAdded:Wait()
local root = char:WaitForChild("HumanoidRootPart", 5)
if not root then
warn("No HumanoidRootPart for", player.Name)
continue
end
if assignedAberration < aberrationCount then
player.Team = TeamAberration
assignedAberration += 1
local spawn = AberrationSpawns[math.random(1, #AberrationSpawns)]
root.CFrame = spawn.CFrame
elseif assignedSecurity < securityCount then
player.Team = TeamSecurity
assignedSecurity += 1
local spawn = SecuritySpawns[math.random(1, #SecuritySpawns)]
root.CFrame = spawn.CFrame
else
player.Team = TeamInmate
assignedInmates += 1
local spawn = InmateSpawns[math.random(1, #InmateSpawns)]
root.CFrame = spawn.CFrame
end
end
print("Assigned Teams:")
print("Inmates:", assignedInmates)
print("Security:", assignedSecurity)
print("Aberrations:", assignedAberration)
return true
end
local function WinCheck()
local aliveIn, aliveSec, aliveAberr = 0, 0, 0
for _, player in ipairs(Players:GetPlayers()) do
if player.Team and player.Character then
local hum = player.Character:FindFirstChildWhichIsA("Humanoid")
if hum and hum.Health > 0 then
if player.Team == TeamInmate then
aliveIn += 1
elseif player.Team == TeamSecurity then
aliveSec += 1
elseif player.Team == TeamAberration then
aliveAberr += 1
end
end
end
end
if aliveIn == 0 and aliveSec == 0 and aliveAberr == 0 then
return nil -- Nobody left alive
elseif aliveSec == 0 and aliveAberr == 0 then
return "Inmates"
elseif aliveIn == 0 and aliveAberr == 0 then
return "Security"
elseif aliveIn == 0 and aliveSec == 0 then
return "Aberration"
end
return nil -- Round continues
end
local function EndRound(winner)
RoundActive = false
print(("Round ended. Winner: %s"):format(winner))
-- Reset players to Spectator and clean up state
for _, player in ipairs(Players:GetPlayers()) do
player.Team = TeamSpectator
player:LoadCharacter()
end
-- (Optional) Handle winner announcement or rewards here
task.wait(IntermissionTime) -- Pause before next round
end
-- MAIN LOOP
task.spawn(function()
while true do
-- Wait for minimum players
repeat
if #Players:GetPlayers() < MinPlayersToStart then
print("Waiting for players... (" .. MinPlayersToStart .. " required)")
task.wait(5)
end
until #Players:GetPlayers() >= MinPlayersToStart
-- Reset all to Spectator
for _, p in ipairs(Players:GetPlayers()) do
p.Team = TeamSpectator
p:LoadCharacter()
end
-- Start a new round
print("----- Starting Round -----")
if not AssignTeams() then
task.wait(IntermissionTime)
continue
end
RoundActive = true
-- Monitor for win
while RoundActive do
local win = WinCheck()
if win then
EndRound(win)
break
end
task.wait(3)
end
-- Intermission
print("Intermission for " .. IntermissionTime .. " seconds")
task.wait(IntermissionTime)
end
end)
it seems to be working better? Had to fix some problems, though now having a new issues where the players stay on the spectator team even though everything seems fine? I did comment some of the code that assigns players to the spectator team except for the one within round ending function. (players are on the spectator team by default anyways since it’s the default)
Haven’t gotten to trying more than 3 players since it gotten really tedious to use browser login manually for every instance so I’m unsure if it fixed now or not.