Scripting Helpers is winding down operations and is now read-only. More info→
Ad
Log in to vote
3

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

Asked by 6 years ago
Edited 6 years ago

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
0
The errors are in the loop, saying that i'm trying to compare to a nil value (the winner value, during the print) Explosivesguy2 20 — 6y

3 answers

Log in to vote
0
Answered by
Azarth 3141 Moderation Voter Community Moderator
6 years ago
Edited 6 years ago
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()

0
Wow, this looks way more complex than the other answers. I'm going to have to try this soon! Explosivesguy2 20 — 6y
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! Explosivesguy2 20 — 6y
Ad
Log in to vote
0
Answered by 6 years ago
Edited 6 years ago
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

1
What about ties~ Azarth 3141 — 6y
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. Explosivesguy2 20 — 6y
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 Explosivesguy2 20 — 6y
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. Explosivesguy2 20 — 6y
0
Edited TheDeadlyPanther 2460 — 6y
Log in to vote
0
Answered by
Rawblocky 217 Moderation Voter
6 years ago
--[[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()

Answer this question