Wrong player possibly being removed?

Section of code from a function

for i = 20, 1, -1 do
		DisplayManager:SetStatus('Time left: ' .. i, true)
		
		for i, v in pairs(players) do
			v.Character.Humanoid.Died:Connect(function()
				table.remove(players, i)
			end)
		end
		
		if #players <= 1 then
			break
		end
		
		wait(1)
	end

	if mainRound then
		if #players == 2 then
			-- Kill both players
		else
			for _, v in pairs(Mats:GetChildren()) do
				print(players[1])
				local Character = players[1].Character
				if v.Taken.Value == players[1] then
					Character:SetPrimaryPartCFrame(v.CFrame * CFrame.new(0, 3, 0))
					
					Character.PrimaryPart.Anchored = true
					
					-- Remove weapons
				end
			end
		end
	end

Basically, ‘players’ is a list of 2 players. The idea is they fight in a 1v1. If time runs out and they both still alive, I just kill them both, it’s a tie (havent added that yet) but my problem lies when one kills the other. This error occurs 50/50, and it has to do with getting the Character.

print(players[1]) SHOULD return the surviving player, however, sometimes it prints nil? Not sure if this has something to do with how I’m removing the dead player from the list. If the list looked like this

players = {Player1, Player2}

then

table.remove(players, i) -- i being the index for the table, lets say player 1 died, so we remove player1

Then the table should then be

players = {Player2}

and thus players[1] should return Player2, correct?

Can someone correct me if I’m wrong?

If a table had two values, one with the index 1 and the other at the index 2, remvoing the value at index 1 doesn’t mean that the value at index 2 is gonna become 1, it’s not retrieved back, it stays the same. A table can have a value at any index, and there doesn’t have to be values before the index, I can set a value to the index 25 even though there is not a 24 or anything before it.

To fix your problem, you can simply do, using the or logical operator

local surviver = player[1] or player[2]

This is saying, if player[1] is nil, meaning that player isn’t the one who won, it would chose player[2] instead.

If your game only uses two players, I would make sure if its absolutely necessary to use a table to store the players. What you could do is have it so you have two variables representing the state of the player.

local Alive1 = true
local Alive2 = true

local Player1 = --player here
local Player2 == --player here

Upon dying, you can change these values to false. That way, when the round ends, all you have to check is whether Player1 or Player2 is true or false, and whichever one is true, you can then apply the winner logic too.

However, if you want the ability to scale it up to as many players as you want, a table may be much better. I would suggest you use two tables, one to indicate the players and another to represent whether they’ve died or not. These two tables would be connected by the index that players are assigned in the players table

local players = {[1] = Mariofly5, [2] = NinjoOnline} -- players here, the [1]'s and [2]'s are just for your understanding
local playerStates = {}

-- When a player has died, set the index in playerStates to false

for i,v in pairs(players) do

playerStates[i] = true
v.Character.Humanoid.Died:connect(function()
playerStates[i] = false
-- indentation got me triggered >:(
end)
end
-- wait for round to end then decide winners etc

In general, I would avoid removing players from the table, as it removes the reference to all the players who have lost that game, and by keeping them, you can reward anyone who has lost with a “Better Luck Next Time” gift or similar.

Doesn’t seem to guarantee the Survivor being a player (still returns nil sometimes)

local Survivor = players[1] or players[2]
local Character = Survivor.Character

Are you sure that both players are still in the table? Perhaps they’re both removed for some reason. Try printing

print(unpack(players))

Hmm ye they both seem to be getting removed :confused:

Prints empty, why is that tho? The only time I remove a player is like so:

for i = 20, 1, -1 do
		DisplayManager:SetStatus('Time left: ' .. i, true)
		
		for i, v in pairs(players) do
			v.Character.Humanoid.Died:Connect(function()
				table.remove(players, i)
			end)
		end
		
		if #players <= 1 then
			break
		end
		
		wait(1)
	end

And only the 1 player dies, not both

Not sure, but this may have a relation with the fact that the even is being fired within the for loop.
There are two options here, to either move the Died event outside.


for i, v in pairs(players) do
	v.Character.Humanoid.Died:Connect(function()
		table.remove(players, i)
	end)
end

for i = 20, 1, -1 do
		DisplayManager:SetStatus('Time left: ' .. i, true)
		
		if #players <= 1 then
			break
		end
		
		wait(1)
	end

Or use keys instead of indices.

local players = {}
for i, v in pairs(game.Players:GetPlayers()) do
    players[v] = v
end


for i = 20, 1, -1 do
	DisplayManager:SetStatus('Time left: ' .. i, true)
	
        for i, v in pairs(players) do
         	 v.Character.Humanoid.Died:Connect(function()
		         if players[v] then
                     players[v] = nil
                 end
	         end)
        end
	if #players <= 1 then
		break
	end
	
	wait(1)
end

What we’re doing here, inserting all the players inside of the players table, where each player’s key is the player object itself. Then each time the Died event fires, we check if that player even existed doing if players[v] then, and if so removed that specific player.

I encourage you to learn about dictionarries