Round System Alive Player Count Bug

Hey everyone, firstly if it ends up being needed I am fine providing the entirety of the script- but for now I have omitted what I think is not important.

The round system for my game works in the following way:

ROUND SYSTEM
  1. Wait for players, then start round
  2. Monitor players who have no yet died until only the survivor and killer remain
  3. When only one survivor is left, force last survivor and killer into a duel
  4. Once either player wins the duel return everyone to lobby
  5. Repeat
    (further context can be provided for any of this if confusing)

The issue I am currently experiencing is that no matter what approach I take - my array of remaining players seems to be inaccurate most of the time, and the duel never starts (aka the array never has only a single survivor remaining - even if all other survivors have died, so I am somehow monitoring deaths incorrectly)

Simplified round code (yes some of this will eventually be moved into modules):

function StartArena(Map, Killer, Survivor) -- eventually jetpacks & sword will instead read from playerdata
	local KillerJetpack = ReplicatedStorage.Jetpacks.Default:Clone()
	local SurvivorJetpack = ReplicatedStorage.Jetpacks.Default:Clone()
	local KillerSword = ReplicatedStorage.Weapons.LinkedSword:Clone()
	local SurvivorSword = ReplicatedStorage.Weapons.LinkedSword:Clone()
	
	Survivor.Character:SetPrimaryPartCFrame(Map.ArenaSurvivorSpawn.CFrame * CFrame.new(0, 4, 0))
	Killer.Character:SetPrimaryPartCFrame(Map.ArenaKillerSpawn.CFrame * CFrame.new(0, 4, 0))
	
	KillerJetpack.Parent = Killer.Character
	SurvivorJetpack.Parent = Survivor.Character
	KillerSword.Parent = Killer.Backpack
	SurvivorSword.Parent = Survivor.Backpack
end

function RoleDecision()
	local PlayerList = Players:GetChildren()
	local KillerNum = Random.new():NextInteger(1,#PlayerList)
	local Killer1 = PlayerList[KillerNum]
	table.remove(PlayerList, KillerNum)
	
	for _,v in pairs(PlayerList) do
		UpdateSpecificUI(v, "Role", "Survivor")
	end
	UpdateSpecificUI(Killer1, "Role", "Garbage Man")
	
	return Killer1, PlayerList
end

function Update(...)
	local Table, Player = ...
	
	for i,v in pairs(Table) do
		if Player == v then
			table.remove(Table, i)
			return
		end
	end
end

local PickedMap = ReplicatedStorage.Maps:GetChildren()[Random.new():NextInteger(1, #ReplicatedStorage.Maps:GetChildren())]
	local NewMap = PickedMap:Clone()
	NewMap.Parent = game.Workspace
	
	local Killer, Survivors = RoleDecision()
	TeleportFunction(NewMap, Killer, Survivors)
	
	for i,player in pairs(Survivors) do
		player.Character.Humanoid.Died:Connect(function()
			Update(Survivors, player)
			print(#Survivors .. " remain! " ..player.Name .. " has died!")
		end)
	end
	
	repeat safeWait(.003) until #Survivors <= 1

Once more, a lot is missing from this because I assume the error exists within these code portions, but more can be provided. Script is located in SSS.

Any help is appreciated, thank you all. I’ve only recently returned to Roblox and started coding again, so it is very possible the problem is quite simple. =)

4 Likes

Hey I don’t see any issues with your code, but did u try printing the table to see where the issue begin?

1 Like

Haven’t been printing the table specifically, but the print that exists tends to get stuck at a certain number of indexes remaining and is not accurate to the actual number of survivors left.

Such as,
2 players remain! Player1 has died
2 players remain! Player2 has died
etc…

Instead of continuing to remove from array.

That’s odd. I ran the code through a testing place, largely the same but commenting out some items which I don’t have:

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

function RoleDecision()
	local PlayerList = Players:GetChildren()
	local KillerNum = Random.new():NextInteger(1,#PlayerList)
	local Killer1 = PlayerList[KillerNum]
	table.remove(PlayerList, KillerNum)

	for _,v in pairs(PlayerList) do
		--UpdateSpecificUI(v, "Role", "Survivor")
	end
	--UpdateSpecificUI(Killer1, "Role", "Garbage Man")

	return Killer1, PlayerList
end

function Update(...)
	local Table, Player = ...

	for i,v in pairs(Table) do
		if Player == v then
			table.remove(Table, i)
			return
		end
	end
end

--local PickedMap = ReplicatedStorage.Maps:GetChildren()[Random.new():NextInteger(1, #ReplicatedStorage.Maps:GetChildren())]
--local NewMap = PickedMap:Clone()
--NewMap.Parent = game.Workspace
repeat wait() until #Players:GetPlayers() >= 3

local Killer, Survivors = RoleDecision()
print("Starting list: ")
print(Survivors)
--TeleportFunction(NewMap, Killer, Survivors)

for i,player in pairs(Survivors) do
	local Character = player.Character or player.CharacterAdded:Wait()
	local Humanoid = Character:FindFirstChild("Humanoid") or Character:WaitForChild("Humanoid")
	player.Character.Humanoid.Died:Connect(function()
		Update(Survivors, player)
		print(#Survivors .. " remain! " ..player.Name .. " has died!")
	end)
end

repeat wait(.003) until #Survivors <= 1 --Replaced safeWait with just wait, unknown function

print("Prepare the arena!")

And it seemed to work fine with three players - the print fired reliably after one of the players in the table died. I don’t think that it should be an issue with Characters/Humanoids, but you could try adding some WaitForChild()s just in case Roblox throws a fit with dot indexing (shouldn’t be an issue unless characters are still loading in).

I also added a simple repeat wait() until that just tracks players. That’s not necessary either and it’s mostly just so the round doesn’t start on my end prematurely. I wouldn’t recommend using repeat wait() until in general.

The RoleDescision and Update functions seem to work fine, so perhaps the culprit lies somewhere else? I would recommend printing out the table as someone else suggested - it would give you additional insight into what’s going wrong with the Survivor’s list.

1 Like

I thought your Update function looked a little unecessary and then I noticed what your issue might be caused by. You are passing Survivor table into Update function but storing it in a local variable called Table. Then removing it from that local variable and then returning out of the function. So the updating is only happening in that local Table variable and not the Survivor table which is why printing Survivor’s index keeps showing that nothing was removed. To fix this, you can do 2 things:
[A]: Remove the local table, player = .... in the Update function and instead of using … for argument, just do arg1 and arg2 and use the first arg to loop through and call table.remove.
or
[B] Make the Survivor table a global list (variable outside all functions and at the top of script) so all functions can use it. But similar to option A, use table.remove on that variable instead of a local table inside the update function.

Hey, thanks for the response- I’m not sure the update function itself is a huge issue because a majority of the time it does its job. I maybe should have clarified that this bug is an odd case and is not consistent. Yesterday I did some logic stuff to the round script to handle weird cases like players leaving and no killer etc… and was able to playtest fine. Although because I understand what you’re saying and why it is important I’d still like to change Update’s functionality. Would it also work to just return Table from inside Update and set survivors table to these new indexes?

Because if you are correct then the update function is not serving its job right now. I had asked a friend before writing it and he more or less told me that variables can link in this way and still process updates (did not sound right to me but I trust him). IE

local a = {'A', 'B', 'C'}
local b = a
local c = b

table.remove(c, 1)

print(#a) -- 2

Is this incorrect? Or is it different because the secondary table is hosted within a function. And would the solution I wrote above work if tables do not link in this way?

I have just tested this and the function serves its purpose so it should not be the issue. You can test it by using this code which is more or less the same thing.

function update(...)
	local tbl, arg = ...
	table.remove(tbl, arg)
	return
end

local tab = {'1','2','3'}

update(tab, 2)
print(#tab) -- 2

Although knowing that it functions correctly is good enough, I am still opting to return the function table and replace the survivor table with it. All this being said the general integrity of the code has not changed and so the bug should still exist somewhere (unless my updates to code logic last night were the solution to the problem).