666 lines
16 KiB
Lua
666 lines
16 KiB
Lua
local Players = game:GetService("Players")
|
|
local RunService = game:GetService("RunService")
|
|
local Teams = game:GetService("Teams")
|
|
|
|
spawn(function ()
|
|
local StarterGui = game:GetService("StarterGui")
|
|
StarterGui:SetCoreGuiEnabled("PlayerList", false)
|
|
|
|
local player = Players.LocalPlayer
|
|
|
|
local playerGui = player:WaitForChild("PlayerGui")
|
|
playerGui:SetTopbarTransparency(1)
|
|
end)
|
|
|
|
--------------------------------------------------------------------------------------------------------------------------------------
|
|
-- Setup
|
|
--------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
local playerStates = {}
|
|
local teamGroups = {}
|
|
|
|
local statLookup = {}
|
|
local statNames = {}
|
|
|
|
local inTeamMode = false
|
|
|
|
local playerList = script.Parent
|
|
local templates = playerList:WaitForChild("Templates")
|
|
|
|
local basePlayerLbl = templates:WaitForChild("BasePlayerLbl")
|
|
local baseGroup = templates:WaitForChild("BaseGroup")
|
|
local baseStat = templates:WaitForChild("BaseStat")
|
|
|
|
local backdrop = playerList:WaitForChild("Backdrop")
|
|
local container = playerList:WaitForChild("Container")
|
|
|
|
local coreGroup = baseGroup:Clone()
|
|
coreGroup.Name = "Default"
|
|
coreGroup.Parent = container
|
|
|
|
local coreFooter = coreGroup.Footer
|
|
local coreHeader = coreGroup.Header
|
|
|
|
local eUpdateStatLayout = Instance.new("BindableEvent")
|
|
local updateStatLayout = eUpdateStatLayout.Event
|
|
|
|
local eUpdateTeamTotal = Instance.new("BindableEvent")
|
|
local updateTeamTotal = eUpdateTeamTotal.Event
|
|
|
|
local ePlayerTeamChanged = Instance.new("BindableEvent")
|
|
local playerTeamChanged = ePlayerTeamChanged.Event
|
|
|
|
--------------------------------------------------------------------------------------------------------------------------------------
|
|
-- Player Colors
|
|
--------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
local PLAYER_COLORS =
|
|
{
|
|
[0] = Color3.fromRGB(173, 35, 35); -- red
|
|
[1] = Color3.fromRGB( 42, 75, 215); -- blue
|
|
[2] = Color3.fromRGB( 29, 105, 20); -- green
|
|
[3] = Color3.fromRGB(129, 38, 192); -- purple
|
|
[4] = Color3.fromRGB(255, 146, 51); -- orange
|
|
[5] = Color3.fromRGB(255, 238, 51); -- yellow
|
|
[6] = Color3.fromRGB(255, 205, 243); -- pink
|
|
[7] = Color3.fromRGB(233, 222, 187); -- tan
|
|
}
|
|
|
|
local function computePlayerColor(player)
|
|
local pName = player.Name
|
|
local length = #pName
|
|
|
|
local oddShift = (1 - (length % 2))
|
|
local value = 0
|
|
|
|
for i = 1,length do
|
|
local char = pName:sub(i,i):byte()
|
|
local rev = (length - i) + oddShift
|
|
|
|
if (rev % 4) >= 2 then
|
|
value = value - char
|
|
else
|
|
value = value + char
|
|
end
|
|
end
|
|
|
|
return PLAYER_COLORS[value % 8]
|
|
end
|
|
|
|
--------------------------------------------------------------------------------------------------------------------------------------
|
|
-- Backdrop Handler
|
|
--------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
local isTeamMode = false
|
|
local hasStats = false
|
|
|
|
local size1x1 = Vector2.new(1,1)
|
|
local rawGroups = {}
|
|
|
|
local function onContainerChildAdded(child)
|
|
if child:IsA("Frame") then
|
|
local listLayout = child:WaitForChild("ListLayout",2)
|
|
if listLayout then
|
|
rawGroups[child] = listLayout
|
|
end
|
|
end
|
|
end
|
|
|
|
local function onContainerChildRemoved(child)
|
|
if rawGroups[child] then
|
|
rawGroups[child] = nil
|
|
end
|
|
end
|
|
|
|
local function sortGroups(a,b)
|
|
if a == coreGroup then
|
|
return true
|
|
elseif b == coreGroup then
|
|
return false
|
|
else
|
|
local orderA,orderB = a.LayoutOrder,b.LayoutOrder
|
|
if orderA == orderB then
|
|
return a.Name < b.Name
|
|
else
|
|
return orderA < orderB
|
|
end
|
|
end
|
|
end
|
|
|
|
local function updateBackdrop()
|
|
local groups = {}
|
|
local at = 1
|
|
|
|
for group in pairs(rawGroups) do
|
|
if group.Visible then
|
|
groups[at] = group
|
|
at = at + 1
|
|
end
|
|
end
|
|
|
|
local height = 0
|
|
table.sort(groups,sortGroups)
|
|
|
|
for i = 1,#groups do
|
|
local group = groups[i]
|
|
local layout = rawGroups[group]
|
|
group.Position = UDim2.new(0,0,0,height)
|
|
height = height + layout.AbsoluteContentSize.Y
|
|
end
|
|
|
|
if #statNames > 0 and not hasStats then
|
|
hasStats = true
|
|
container.AnchorPoint = Vector2.new(1,0)
|
|
for _,group in pairs(groups) do
|
|
group.Header.TeamUnderline.Size = UDim2.new(2,-4,0,1)
|
|
end
|
|
eUpdateStatLayout:Fire()
|
|
elseif #statNames == 0 and hasStats then
|
|
hasStats = false
|
|
container.AnchorPoint = Vector2.new(0,0)
|
|
for _,group in pairs(groups) do
|
|
group.Header.TeamUnderline.Size = UDim2.new(1,-4,0,1)
|
|
end
|
|
eUpdateStatLayout:Fire()
|
|
end
|
|
|
|
if isTeamMode then
|
|
height = height + coreHeader.AbsoluteSize.Y
|
|
end
|
|
|
|
local width = container.AbsoluteSize.X * (container.AnchorPoint.X+1)
|
|
backdrop.Size = UDim2.new(0,width,0,height)
|
|
end
|
|
|
|
for _,child in pairs(container:GetChildren()) do
|
|
onContainerChildAdded(child)
|
|
end
|
|
|
|
container.ChildAdded:Connect(onContainerChildAdded)
|
|
container.ChildRemoved:Connect(onContainerChildRemoved)
|
|
RunService.Heartbeat:Connect(updateBackdrop)
|
|
|
|
--------------------------------------------------------------------------------------------------------------------------------------
|
|
-- Header Size Stuff
|
|
--------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
local function switchHeaderMode(isTeamMode)
|
|
if isTeamMode then
|
|
coreHeader.Size = UDim2.new(1,0,1/3,0)
|
|
coreHeader.Stats.Size = UDim2.new(0.75,0,1,0)
|
|
coreHeader.Title.Size = UDim2.new(1,0,1,0)
|
|
else
|
|
coreHeader.Size = UDim2.new(1,0,0.4,0)
|
|
coreHeader.Stats.Size = UDim2.new(0.75,0,0.85,0)
|
|
coreHeader.Title.Size = UDim2.new(1,0,0.85,0)
|
|
end
|
|
end
|
|
|
|
switchHeaderMode(false)
|
|
|
|
--------------------------------------------------------------------------------------------------------------------------------------
|
|
-- Player Stats
|
|
--------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
local function incrementStat(statName)
|
|
if not statLookup[statName] then
|
|
statLookup[statName] = 1
|
|
table.insert(statNames,statName)
|
|
else
|
|
statLookup[statName] = statLookup[statName] + 1
|
|
end
|
|
eUpdateStatLayout:Fire()
|
|
end
|
|
|
|
local function decrementStat(statName)
|
|
if statLookup[statName] then
|
|
statLookup[statName] = statLookup[statName] - 1
|
|
|
|
if statLookup[statName] == 0 then
|
|
statLookup[statName] = nil
|
|
for i,name in ipairs(statNames) do
|
|
if name == statName then
|
|
table.remove(statNames,i)
|
|
break
|
|
end
|
|
end
|
|
end
|
|
|
|
eUpdateStatLayout:Fire()
|
|
end
|
|
end
|
|
|
|
local function getPlayerStateFromStat(stat)
|
|
local leaderstats = stat.Parent
|
|
if leaderstats then
|
|
local player = leaderstats.Parent
|
|
if player then
|
|
return playerStates[player]
|
|
end
|
|
end
|
|
end
|
|
|
|
local function refreshTeamStats()
|
|
for _,team in pairs(Teams:GetTeams()) do
|
|
eUpdateTeamTotal:Fire(team)
|
|
end
|
|
end
|
|
|
|
local function onStatRemoved(stat,statName)
|
|
if stat.ClassName == "IntValue" then
|
|
local statName = statName or stat.Name
|
|
local playerState = getPlayerStateFromStat(stat)
|
|
|
|
if playerState and playerState.Stats[statName] then
|
|
playerState.Stats[statName]:Destroy()
|
|
playerState.Stats[statName] = nil
|
|
end
|
|
|
|
decrementStat(statName)
|
|
refreshTeamStats()
|
|
end
|
|
end
|
|
|
|
local function onStatAdded(stat)
|
|
if stat.ClassName == "IntValue" then
|
|
local statName = stat.Name
|
|
local playerState = getPlayerStateFromStat(stat)
|
|
|
|
if playerState then
|
|
local changeSignal
|
|
|
|
if not playerState.Stats[statName] then
|
|
local statLbl = baseStat:Clone()
|
|
statLbl.Name = statName
|
|
|
|
local function updateStat()
|
|
statLbl.Text = stat.Value
|
|
if isTeamMode then
|
|
local team = playerState.Player.Team
|
|
if team then
|
|
eUpdateTeamTotal:Fire(team)
|
|
end
|
|
end
|
|
end
|
|
|
|
updateStat()
|
|
changeSignal = stat.Changed:Connect(updateStat)
|
|
|
|
statLbl.Parent = playerState.Label.Stats
|
|
playerState.Stats[statName] = statLbl
|
|
end
|
|
|
|
local nameSignal do
|
|
local function onNameChanged()
|
|
if changeSignal then
|
|
changeSignal:Disconnect()
|
|
changeSignal = nil
|
|
end
|
|
|
|
nameSignal:Disconnect()
|
|
nameSignal = nil
|
|
|
|
-- Rebuild the stat
|
|
onStatRemoved(stat,statName)
|
|
onStatAdded(stat)
|
|
end
|
|
|
|
nameSignal = stat:GetPropertyChangedSignal("Name"):Connect(onNameChanged)
|
|
end
|
|
end
|
|
|
|
incrementStat(statName)
|
|
refreshTeamStats()
|
|
end
|
|
end
|
|
|
|
local function onPlayerChildAdded(leaderstats)
|
|
if leaderstats.Name == "leaderstats" then
|
|
local player = leaderstats.Parent
|
|
local playerState = playerStates[player]
|
|
|
|
if playerState and not playerState.leaderstats then
|
|
playerState.leaderstats = leaderstats
|
|
|
|
for _,stat in pairs(leaderstats:GetChildren()) do
|
|
onStatAdded(stat)
|
|
end
|
|
|
|
leaderstats.ChildAdded:Connect(onStatAdded)
|
|
leaderstats.ChildRemoved:Connect(onStatRemoved)
|
|
end
|
|
end
|
|
end
|
|
|
|
local function onPlayerChildRemoved(child)
|
|
if child.Name == "leaderstats" then
|
|
for _,stat in pairs(child:GetChildren()) do
|
|
onStatRemoved(stat)
|
|
end
|
|
|
|
for player,playerState in pairs(playerStates) do
|
|
if playerState.leaderstats == child then
|
|
playerState.leaderstats = nil
|
|
break
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
local function updateStatLbl(statLbl,index)
|
|
statLbl.Size = UDim2.new(1/#statNames,0,1,0)
|
|
statLbl.Position = UDim2.new((index-1)/#statNames)
|
|
end
|
|
|
|
local function onUpdateStatLayout()
|
|
local statBin = coreHeader.Stats
|
|
|
|
for _,statLbl in pairs(statBin:GetChildren()) do
|
|
if statLbl:IsA("TextLabel") and not statLookup[statLbl.Name] then
|
|
statLbl:Destroy()
|
|
end
|
|
end
|
|
|
|
for i,statName in pairs(statNames) do
|
|
local statLbl = statBin:FindFirstChild(statName)
|
|
|
|
if not statLbl then
|
|
statLbl = baseStat:Clone()
|
|
statLbl.Name = statName
|
|
statLbl.Text = statName
|
|
statLbl.Parent = statBin
|
|
end
|
|
|
|
updateStatLbl(statLbl,i)
|
|
end
|
|
|
|
for player,playerState in pairs(playerStates) do
|
|
for statName,statLbl in pairs(playerState.Stats) do
|
|
if not statLookup[statName] then
|
|
statLbl:Destroy()
|
|
playerState.Stats[statName] = nil
|
|
end
|
|
end
|
|
|
|
for i,statName in pairs(statNames) do
|
|
local statLbl = playerState.Stats[statName]
|
|
|
|
if statLbl then
|
|
if player.Team then
|
|
statLbl.TextColor = player.Team.TeamColor
|
|
else
|
|
statLbl.TextColor3 = Color3.new(1,1,1)
|
|
end
|
|
updateStatLbl(statLbl,i)
|
|
end
|
|
end
|
|
end
|
|
|
|
isTeamMode = (#Teams:GetTeams() > 0)
|
|
|
|
if isTeamMode then
|
|
coreHeader.Visible = hasStats
|
|
coreHeader.Title.Text = "Team"
|
|
else
|
|
coreHeader.Visible = true
|
|
if hasStats then
|
|
coreHeader.Title.Text = "Players"
|
|
else
|
|
coreHeader.Title.Text = "Player List"
|
|
end
|
|
end
|
|
|
|
switchHeaderMode(isTeamMode)
|
|
end
|
|
|
|
updateStatLayout:Connect(onUpdateStatLayout)
|
|
|
|
--------------------------------------------------------------------------------------------------------------------------------------
|
|
-- Player States
|
|
--------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
local function onPlayerAdded(player)
|
|
local playerState = {}
|
|
local name = player.Name
|
|
|
|
local lbl = basePlayerLbl:Clone()
|
|
lbl.Name = name
|
|
lbl.PlayerName.Text = name
|
|
lbl.PlayerName.TextColor3 = computePlayerColor(player)
|
|
lbl.Parent = coreGroup
|
|
|
|
playerState.Player = player
|
|
playerState.Label = lbl
|
|
playerState.Stats = {}
|
|
|
|
playerStates[player] = playerState
|
|
|
|
for _,child in pairs(player:GetChildren()) do
|
|
onPlayerChildAdded(child)
|
|
end
|
|
|
|
player.ChildAdded:Connect(onPlayerChildAdded)
|
|
player.ChildRemoved:Connect(onPlayerChildRemoved)
|
|
|
|
player.Changed:Connect(function (property)
|
|
if property == "Team" then
|
|
ePlayerTeamChanged:Fire(player)
|
|
end
|
|
end)
|
|
|
|
ePlayerTeamChanged:Fire(player)
|
|
end
|
|
|
|
local function onPlayerRemoved(player)
|
|
local state = playerStates[player]
|
|
playerStates[player] = nil
|
|
|
|
if state and state.Label then
|
|
state.Label:Destroy()
|
|
end
|
|
end
|
|
|
|
for _,player in pairs(Players:GetPlayers()) do
|
|
onPlayerAdded(player)
|
|
end
|
|
|
|
Players.PlayerAdded:Connect(onPlayerAdded)
|
|
Players.PlayerRemoving:Connect(onPlayerRemoved)
|
|
|
|
--------------------------------------------------------------------------------------------------------------------------------------
|
|
-- Teams
|
|
--------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
local function neutralizePlayer(player)
|
|
local playerState = playerStates[player]
|
|
|
|
if playerState then
|
|
local playerLbl = playerState.Label
|
|
playerLbl.PlayerName.Text = player.Name
|
|
playerLbl.PlayerName.TextColor3 = computePlayerColor(player)
|
|
playerLbl.PlayerName.Position = UDim2.new(0, 0, 0, 0)
|
|
|
|
for stat,statLbl in pairs(playerState.Stats) do
|
|
statLbl.TextColor3 = Color3.new(1,1,1)
|
|
end
|
|
|
|
playerLbl.Visible = (not isTeamMode)
|
|
playerLbl.Parent = coreGroup
|
|
end
|
|
end
|
|
|
|
local function onPlayerAddedToTeam(player)
|
|
local team = player.Team
|
|
local group = teamGroups[team]
|
|
|
|
if group then
|
|
local playerState = playerStates[player]
|
|
if playerState then
|
|
local playerLbl = playerState.Label
|
|
playerLbl.PlayerName.TextColor = team.TeamColor
|
|
playerLbl.PlayerName.Position = UDim2.new(0, 4, 0, 0)
|
|
|
|
for stat, statLbl in pairs(playerState.Stats) do
|
|
statLbl.TextColor = team.TeamColor
|
|
end
|
|
|
|
playerLbl.Parent = group
|
|
playerLbl.Visible = true
|
|
|
|
eUpdateStatLayout:Fire()
|
|
refreshTeamStats()
|
|
end
|
|
end
|
|
end
|
|
|
|
local function onPlayerRemovedFromTeam(player)
|
|
if not player.Team then
|
|
neutralizePlayer(player)
|
|
refreshTeamStats()
|
|
end
|
|
end
|
|
|
|
local function onUpdateTeamTotal(team)
|
|
local teamGroup = teamGroups[team]
|
|
|
|
if teamGroup then
|
|
local teamStats = teamGroup.Header.Stats
|
|
local totals = {}
|
|
|
|
for i,statName in ipairs(statNames) do
|
|
local total = totals[i]
|
|
|
|
if not total then
|
|
total = { Name = statName, Value = 0 }
|
|
totals[i] = total
|
|
end
|
|
|
|
for _,player in pairs(team:GetPlayers()) do
|
|
local playerState = playerStates[player]
|
|
|
|
if playerState then
|
|
local leaderstats = playerState.leaderstats
|
|
if leaderstats then
|
|
local stat = leaderstats:FindFirstChild(statName)
|
|
if stat then
|
|
total.Value = total.Value + stat.Value
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
local numStats = #statNames
|
|
|
|
for i,statRecord in ipairs(totals) do
|
|
local statName = statRecord.Name
|
|
local statLbl = teamStats:FindFirstChild(statName)
|
|
|
|
if not statLbl then
|
|
statLbl = baseStat:Clone()
|
|
statLbl.Name = statName
|
|
statLbl.TextColor = team.TeamColor
|
|
statLbl.TextStrokeTransparency = 0.5
|
|
statLbl.Parent = teamStats
|
|
end
|
|
|
|
statLbl.Text = statRecord.Value
|
|
updateStatLbl(statLbl,i)
|
|
end
|
|
|
|
for _,statLbl in pairs(teamStats:GetChildren()) do
|
|
if not statLookup[statLbl.Name] then
|
|
statLbl:Destroy()
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
local function onTeamAdded(team)
|
|
if team.ClassName == "Team" then
|
|
local teamGroup = baseGroup:Clone()
|
|
teamGroup.Name = team.Name
|
|
teamGroup.Footer.Visible = true
|
|
|
|
local teamHeader = teamGroup.Header
|
|
|
|
local teamUnderline = teamHeader.TeamUnderline
|
|
teamUnderline.Visible = true
|
|
teamUnderline.BackgroundColor = team.TeamColor
|
|
|
|
if hasStats then
|
|
teamUnderline.Size = teamUnderline.Size + UDim2.new(1,0,0,0)
|
|
end
|
|
|
|
local teamTitle = teamHeader.Title
|
|
teamTitle.Text = team.Name
|
|
teamTitle.TextColor = team.TeamColor
|
|
teamTitle.TextStrokeTransparency = 0.5
|
|
|
|
teamGroup.Parent = container
|
|
teamGroups[team] = teamGroup
|
|
|
|
for _,player in pairs(team:GetPlayers()) do
|
|
onPlayerAddedToTeam(player)
|
|
end
|
|
|
|
eUpdateTeamTotal:Fire(team)
|
|
eUpdateStatLayout:Fire()
|
|
end
|
|
if #Teams:GetTeams() > 0 and not isTeamMode then
|
|
isTeamMode = true
|
|
for _,player in pairs(Players:GetPlayers()) do
|
|
if not player.Team then
|
|
neutralizePlayer(player)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
local function onTeamRemoved(team)
|
|
if teamGroups[team] then
|
|
for _,player in pairs(Players:GetPlayers()) do
|
|
if player.TeamColor == team.TeamColor then
|
|
neutralizePlayer(player)
|
|
end
|
|
end
|
|
|
|
teamGroups[team]:Destroy()
|
|
teamGroups[team] = nil
|
|
|
|
eUpdateStatLayout:Fire()
|
|
end
|
|
|
|
if #Teams:GetTeams() == 0 then
|
|
isTeamMode = false
|
|
for _,player in pairs(Players:GetPlayers()) do
|
|
neutralizePlayer(player)
|
|
end
|
|
end
|
|
end
|
|
|
|
local function onPlayerTeamChange(player)
|
|
local team = player.Team
|
|
|
|
if team then
|
|
onPlayerAddedToTeam(player)
|
|
else
|
|
onPlayerRemovedFromTeam(player)
|
|
end
|
|
end
|
|
|
|
for _,team in pairs(Teams:GetTeams()) do
|
|
onTeamAdded(team)
|
|
end
|
|
|
|
for _,player in pairs(Players:GetPlayers()) do
|
|
onPlayerTeamChange(player)
|
|
end
|
|
|
|
Teams.ChildAdded:Connect(onTeamAdded)
|
|
Teams.ChildRemoved:Connect(onTeamRemoved)
|
|
|
|
updateTeamTotal:Connect(onUpdateTeamTotal)
|
|
playerTeamChanged:Connect(onPlayerTeamChange)
|
|
|
|
-------------------------------------------------------------------------------------------------------------------------------------- |