Scripting HelpersARCHIVE
A little piece of Roblox history. Preserved for everyone, one question at a time.Read about the archive →
✓ ANSWER ACCEPTED

How do you find the player with the highest amount of KOs?

Right now I am attempting to make a system that gives cash (or in my game, "Explode Points") to the player with the best KO to wipeout ratio at the end of every round. The problem is, I have absolutely no idea how to do that. So far I made a weird little script that makes values in a folder that I'm going to try and take the highest value from:

local function findWinner()
	local children = game.Players:GetChildren()
	for _, child in pairs(children) do
		local stats = child:WaitForChild("leaderstats")
		if stats then --making values
			local KOs = stats.KOs
			local Deaths = stats.Wipeouts
			local newValue = (KOs.Value / Deaths.Value)
			local PnewValue = Instance.new("IntValue", game.ReplicatedStorage.WinnerValues)
			PnewValue.Name = child.Name
			PnewValue.Value = newValue
			wait(10)
			KOs.Value = 0
			Deaths.Value = 0
			PnewValue:Destroy()
		end
	end
	local winnerFolder = game.ReplicatedStorage.WinnerValues
	
end

I hope I didn't break the rules by not providing any code for finding the value, but the other questions on this site just didn't make any sense to me or work at all. So how do you go about doing this? (In case you need it, the function is fired every 120 seconds)

Edit: Be sure to tell me if there's a more efficient way to track all of the stats than putting them in a folder, because I'm sure there is but I honestly have no idea

Edit2ElectricBoogaloo: After hours of trying, I still haven't gotten it to work so far. Both answers give me a similar error where the winner is returned as a nil value, and there isn't a system for ties.

Here's my new script where I mostly used the second answer but used some aspects from the one I originally thought answered my question:

local function FindWinner()
	local children = game.Players:GetPlayers()
	local highestnumber = -10000
	local winner = nil
	local playerCountChildren = game.Players:GetPlayers();
	local playerCount = #playerCountChildren;
		
	for _, child in pairs(children) do
		if child:FindFirstChild('leaderstats') then
			local stats = child.leaderstats
			local KOs = stats.KOs
			local Deaths = stats.Wipeouts
			local newValue = (KOs.Value / Deaths.Value)
			if Deaths.Value == 0 and KOs.Value ~= 0 then
				newValue = KOs.Value
			end
			if newValue > highestnumber then
				highestnumber = newValue
				winner = child
			end
			
			if playerCount == 1 then
				winner = child
				highestnumber = newValue
			end
		end
	end
	
	return winner, highestnumber
end

Here is the loop I made at the bottom, in case I'm utilizing it wrong:

while true do
	for a=120,1,-1 do
		print(a)
		wait(1)
	end
	print ('0')
	setupMap()
	local winner, highestnumber = FindWinner()
	print (winner.Name.. " is the winner with a KDR ratio of " ..highestnumber)
	local message = Instance.new('Message',game.Workspace)
	message.Text = (winner.Name.. " is the winner with a Kill Death Ratio of " ..highestnumber .."!")
	local winPlayer = game.Players:FindFirstChild(winner.Name)
	if winPlayer then
		local winStats = winPlayer:FindFirstChild("leaderstats")
		if winStats then
			local winCash = winStats:FindFirstChild("ExplodePoints")
			winCash.Value = winCash.Value + 500
			wait(5)
		end
	end
	resetValues()
	message:Destroy()
	wait()
end

1 comment

0

The errors are in the loop, saying that i'm trying to compare to a nil value (the winner value, during the print)

3 answers

✓ Accepted answer
local players = game:GetService('Players')

local function make_table()
	-- resets the table for when we find a new high kdr
	local winners = {}
		winners.player_objects = {}
		winners.strings = {}
	
	setmetatable(winners,{
		-- calls when we try and set something new in winners
			-- used to insert strings in to winners.strings 
				-- and move player objects to player_objects
		__newindex = function(tabl, key, value)
			table.insert(winners.strings, key)
			tabl.player_objects[key] = value;
		end;
	})
	return winners
end

local function FindWinner()
	local highestnumber = -1
	local winners = make_table()
	for _, child in pairs(game.Players:GetPlayers()) do
		local stats = child:FindFirstChild('leaderstats')
		if stats then
			local KOs = stats.KOs
			local Deaths = stats.Wipeouts
			local kdr = (KOs.Value / Deaths.Value)
			-- Dividing by 0 = inf, so make it your kos value.
			if Deaths.Value == 0 then kdr = KOs.Value end
			if kdr > highestnumber then
				highestnumber = kdr
				-- reset table
				winners = make_table()
				-- calls __newindex in the metatable, so we can automatically
					-- do a couple of things
				winners[child.Name] = child;
			elseif kdr == highestnumber then 
				-- don't reset table; add player.
				winners[child.Name] = child;
			end
			KOs.Value = 0;
			Deaths.Value = 0;
		end
	end
	-- Shortens the number to four incase it's a UUGE(heh) decimal.
	return winners, tostring(highestnumber):sub(1,4)
end

local function start()
	while wait(.5) do 
		local winner_objects, kdr = FindWinner()
		local strings = winner_objects.strings		
		-- \n makes a new line
		local winner_text
		winner_text = (#strings == 0 
		-- if #strings == 0 then
		and 'No one won \n' 
		-- elseif #strings == 1 then
			or #strings == 1 
		and ('%s won with a kdr of %s'):format(strings[1], kdr)
		-- else
			or ("There was a tie between %s"):format(table.concat(strings, ", ").. ' with a kdr of '..kdr ))
		print(winner_text)
		
		if #strings > 0 then 
			for i,v in pairs(winner_objects.player_objects) do 
				local winStats = v:FindFirstChild("leaderstats")
				local winCash = winStats and winStats:FindFirstChild("ExplodePoints")
				if winCash then
					winCash.Value = winCash.Value + 500
					wait(5) -- going to cause the script to hang
				end
			end
		end
	end
end


local stats = {
	
	['KOs'] = 10;
	['Wipeouts'] = 0;
	['ExplodePoints'] = 0;

}

players.PlayerAdded:connect(function(player)
	local ls = Instance.new("NumberValue")
	ls.Name = 'leaderstats';
	
	for i,v in pairs(stats) do 
		local val = Instance.new("NumberValue", ls)
		val.Name = i;
		val.Value = v;
	end
	
	ls.Parent = player
	
end)

start()

2 comments

0

Wow, this looks way more complex than the other answers. I'm going to have to try this soon!

0

So I tried it out, and it works! At first I thought "why is it keep giving me explode points" when I realized that you designed the script to work without an outside timer (the while wait do you made). Thank you for this, my game is nearly complete!

local Winners_Cache = {} -->> Store info as a table in your script, no need for external values

local function FindWinner()
	local Highest = {}

	for _,Player in pairs (game.Players:GetPlayers()) do
		local Stats = Player:FindFirstChild("leaderstats") -->> Having an if statement is pointless if it is WaitForChild
		if Stats then
			local KOs,WOs = Stats:WaitForChild("KOs"),Stats:WaitForChild("WOs") -->> Name your statistics consistently, 'KOs' and 'Wipeouts' doesn't really work
			local KDR = KOs.Value/WOs.Value

			if WOs.Value == 0 then -->> To stop it from being infinite
				KDR = KOs.Value
			elseif KOs.Value == 0 and WOs.Value > 0 then
				KDR = -WOs.Value
			end
			
			if not Highest[1] or Highest[1].KDR < KDR then -->> If highest hasn't been assigned to yet, or if this KDR is bigger
				Highest = {{Player = Player,KDR = KDR}} -->> Set it
				-->> Highest is now a holder for a number of players, not just one, to allow for ties
			end

			delay(10,function() -->> Waits 10 seconds without slowing down the code
				KOs.Value,WOs.Value = 0,0
			end)
		end
	end

	table.insert(Winners_Cache,Highest)
	return Highest
end

local Winners = FindWinners()
for _,Winner in pairs (Winners) do -->> Loop through every winner
	print(Winner.Player.Name.. " is the winner with a KDR of " ..Winner.KDR.."!")
	-->> After this, just change the Explode Points value inside Winner.Player's leaderstats
end

EDIT:

My bat about the errors. It errored on line 18 because the first time the code runs, Winners.KDR did not exist so it was trying to compare nil with KDR. I've also edited it to work for tying.


Some more tips:

Use ServerStorage instead of ReplicatedStorage when you want to store things that can't be accessed or changed by players. This reduces the potential for the winners to be hacked if you were using an object system.

Use game.Players:GetPlayers() to get players, not :GetChildren().


Hope I helped!

~TDP

5 comments

1

What about ties~

1

Wow! THank you! About the Waitforchild if statement thing, I had a feeling that it was pointless but I did it with a lot of other stuff and it worked fine, lol. Thanks for explaining it too, I hate it whenever someone (like a tutorial on youtube) just shows you how to do something but doesn't actually say how it works.

0

Yeah actually, how are ties handled? Btw, I keep getting an error on your 18th line which says: attempt to compare nil with number

0

I had to unaccept this answer, it didn't work after spending hours trying to figure this out. Both solutions so far hasn't worked.

0

Edited

--[[Edits:
	You can use "local winner = findWinner()" to know who's the winner, so you can
	basically do this: winner.leaderstats.Points.Value = winner.leaderstats.Points.Value+10
	There was also a bug so that
	it does this: find winner, waits 10 seconds, delete the data, and it restarts
	it doesnt like do all of it at the same time, like it will perform one by one
	so I basically made it so the WinnerValues clear all children, and
	made another function that clears the player's score.
	
]]
function resetScores()
    local children = game.Players:GetChildren()
	local highestnumber = -10000
	local winner = nil
    for _, child in pairs(children) do
        if child:FindFirstChild('leaderstats') then
        local stats = child.leaderstats
            local KOs = stats.KOs
            local Deaths = stats.Wipeouts
			KOs.Value = 0
			Deaths.Value = 0
        end
    end
end


function findWinner()
    local children = game.Players:GetChildren()
	local highestnumber = -10000
	local winner = nil
    for _, child in pairs(children) do
        if child:FindFirstChild('leaderstats') then
        local stats = child.leaderstats
            local KOs = stats.KOs
            local Deaths = stats.Wipeouts
            local newValue = (KOs.Value / Deaths.Value)
			if Deaths.Value == 0 and KOs.Value ~= 0 then --prevent it from saying "inf" for the ratio
				newValue = KOs.Value
			end
            local PnewValue = Instance.new("IntValue", game.ReplicatedStorage.WinnerValues)
            PnewValue.Name = child.Name
            PnewValue.Value = newValue
			if newValue > highestnumber then
				highestnumber = PnewValue
				winner = child
			end
           -- KOs.Value = 0
            --Deaths.Value = 0
           -- PnewValue:Destroy()
        end
    end
    local winnerFolder = game.ReplicatedStorage.WinnerValues
	return winner
end

local winner = findWinner()
local message = Instance.new('Message',workspace)
message.Text = 'The winner is '..winner.Name
wait(10)
game.ReplicatedStorage.WinnerValues:ClearAllChildren()
resetScores()
message:remove()

This discussion is preserved as it was. New answers, comments, and votes are closed.