Team Autobalancer with Lobby Area

-- Internal custom properties
local COMPONENT_ROOT = script:GetCustomProperty("ComponentRoot"):WaitForObject()

-- User exposed properties
local TEAM_COUNT = COMPONENT_ROOT:GetCustomProperty("TeamCount")-- int
local MAX_TEAM_SIZE_DIFFERENCE = COMPONENT_ROOT:GetCustomProperty("MaxTeamSizeDifference")-- int
local ONLY_SWITCH_DEAD_PLAYERS = COMPONENT_ROOT:GetCustomProperty("OnlySwitchDeadPlayers")-- bool
local KILL_ON_TEAM_SWITCH = COMPONENT_ROOT:GetCustomProperty("KillOnTeamSwitch")-- bool
local SCRAMBLE_AT_ROUND_START = COMPONENT_ROOT:GetCustomProperty("ScrambleAtRoundStart")-- bool

-- Check user properties
if TEAM_COUNT < 2 or TEAM_COUNT > 4 then
    warn("TeamCount must be in the range [2, 4]")
    TEAM_COUNT = 2
end

if MAX_TEAM_SIZE_DIFFERENCE < 1 then
    warn("MaxTeamSizeDifference must be positive")
    MAX_TEAM_SIZE_DIFFERENCE = 1
end

local _roundStarted = 0

-- nil Tick(float)
-- Watch team sizes and enforce autobalance. We only switch one player per frame.
function Tick(deltaTime)
    local teamSizes = {}

    for i = 1, TEAM_COUNT do
        teamSizes[i] = 0
    end

    for _, player in pairs(Game.GetPlayers()) do
        if teamSizes[player.team] then
            teamSizes[player.team] = teamSizes[player.team] + 1
        end
    end

    -- Find the smallest team
    local smallestTeam = nil
    local smallestTeamSize = nil

    for team, size in pairs(teamSizes) do
        if not smallestTeamSize or size < smallestTeamSize then
            smallestTeam = team
            smallestTeamSize = size
        end
    end

    -- Find teams that are too big
    local tooBigTeams = {}

    for team, size in pairs(teamSizes) do
        if size > smallestTeamSize + MAX_TEAM_SIZE_DIFFERENCE then
            table.insert(tooBigTeams, team)
        end
    end

    if #tooBigTeams == 0 then
        return
    end

    -- Find players who can be swapped
    local switchablePlayers = {}

    for _, player in pairs(Game.GetPlayers({includeTeams = tooBigTeams})) do
        if not ONLY_SWITCH_DEAD_PLAYERS or player.isDead then
            table.insert(switchablePlayers, player)
        end
    end

    -- Swap a player at random
    local numSwitchablePlayers = #switchablePlayers

    if numSwitchablePlayers > 0 then
        local player = switchablePlayers[math.random(numSwitchablePlayers)]
        player.team = smallestTeam

        if KILL_ON_TEAM_SWITCH and not player.isDead then
            player:Die()
        end
    end
end

-- nil OnRoundStart()
-- Scrambles the teams if the creator wants
function OnRoundStart()
    local aa = 0
    _roundStarted = 1
    for _,player in ipairs(Game.GetPlayers()) do
        player.team = aa%2 + 1
        player:Respawn()
        aa = aa + 1
    end    
end

function OnRoundEnd() -- set all players to "team 0" (lobby)
    _roundStarted = 0
    for _,player in ipairs(Game.GetPlayers()) do
        player.team = 0
        player:Respawn()
    end    
end



-- player should be sent to lobby upon joining the game
Game.playerJoinedEvent:Connect(function(player) -- get our player object upon join 
    player.team = 0 
    player:Respawn()
    --print("TEAM: ",player.team)
end)
-- player will be autobalanced upon round start
Game.roundStartEvent:Connect(OnRoundStart)
-- player sent to lobby at the end of round
Game.roundEndEvent:Connect(OnRoundEnd)

Latest Posts