local RbxGui local localTesting = true local screen = script.Parent local screenResizeCon = nil local friendWord = "Friend" local friendWordLowercase = "friend" local elementNames = {} local elementNameToElement = {} local privilegeOwner = 255 local privilegeAdmin = 240 local privilegeMember = 128 local privilegeVisitor = 10 local privilegeBanned = 0 local inContextMenu = false local contextMenu3d = true local bigEasingStyle = Enum.EasingStyle.Back local smallEasingStyle = Enum.EasingStyle.Quart local personalServerContextAdded = false local personalServerPlace = false local success = pcall(function() personalServerPlace = game.IsPersonalServer end) if not success then personalServerPlace = false end local friendRequestBlacklist = {} local otherPlayerBlacklist = {} local currentSortName = "" local function waitForChild(instance, name) while not instance:FindFirstChild(name) do instance.ChildAdded:wait() end end local function waitForProperty(instance, prop) while not instance[prop] do instance.Changed:wait() end end local function Color3I(r,g,b) return Color3.new(r/255,g/255,b/255) end function robloxLock(instance) instance.RobloxLocked = true children = instance:GetChildren() if children then for i, child in ipairs(children) do robloxLock(child) end end end function ArrayRemove(t, obj) for i, obj2 in ipairs(t) do if obj == obj2 then table.remove(t, i) return true end end return false end local function getPlayers() local result = {} local players = game:GetService("Players"):GetChildren() if players then for i, player in ipairs(players) do if player:IsA("Player") then table.insert(result, player) end end end return result end local brickColorTable = {} for i = 0, 63 do brickColorTable[BrickColor.palette(i).Name] = BrickColor.palette(i).Color end local function remapColor(i, j) brickColorTable[BrickColor.palette(i).Name] = BrickColor.palette(j).Color end remapColor(13, 12) remapColor(14, 12) remapColor(15, 12) remapColor(61, 29) remapColor(63, 62) remapColor(56, 50) remapColor(45, 53) remapColor(51, 20) remapColor(4, 20) remapColor(59, 35) remapColor(60, 29) local function getColor(brickColor) if brickColorTable[brickColor.Name] then return brickColorTable[brickColor.Name] else return brickColor.Color; end end local function getTeams() local result = {} local teams = game:GetService("Teams"):GetChildren() for i, team in ipairs(teams) do if team:IsA("Team") then table.insert(result, team) end end return result end local supportFriends = true local currentBoardType = "PlayerList" local currentStatCount = 0 local createBoardsFunction = nil local playerTable = {} local teamTable = {} local teamColorTable = {} local removePlayerFunction = nil local recreatePlayerFunction = nil local addPlayerFunction = function(player) if recreatePlayerFunction then recreatePlayerFunction(player) end end local sortPlayerListsFunction = nil local minimizedState = nil local bigWindowImposter = nil local smallWindowPosition = UDim2.new(0, -20, 0,5) local smallWindowSize = UDim2.new(1,0,1,0) local bigWindowSize = UDim2.new(0.6,0,0.6,0) local bigWindowPosition = UDim2.new(.2, 0, .2,0) local smallWindowHeaderYSize = 32 local debounceTeamsChanged = false local currentWindowState = "Small" local previousWindowState = nil local transitionWindowsFunction = nil local container = nil local topRightTrayContainer = nil local playerContextMenu = nil local contextMenuElements = {} local updateContextMenuItems = nil local function addContextMenuLabel(getText1, getText2, isVisible) local t = {} t.Type = "Label" t.GetText1 = getText1 t.GetText2 = getText2 t.IsVisible = isVisible table.insert(contextMenuElements, t) end local function addContextMenuButton(text, isVisible, isActive, doIt) local t = {} t.Text = text t.Type = "Button" t.IsVisible = isVisible t.IsActive = isActive t.DoIt = doIt table.insert(contextMenuElements, t) end local function getFriendStatus(player) if player == game.Players.LocalPlayer then return Enum.FriendStatus.NotFriend else local success, result = pcall(function() return game.Players.LocalPlayer:GetFriendStatus(player) end) if success then return result else return Enum.FriendStatus.NotFriend end end end local function getPrivilegeType(player) local rank = player.PersonalServerRank if rank >= privilegeOwner then return privilegeOwner elseif rank < privilegeOwner and rank >= privilegeAdmin then return privilegeAdmin elseif rank < privilegeAdmin and rank >= privilegeMember then return privilegeMember elseif rank < privilegeMember and rank >= privilegeVisitor then return privilegeVisitor else return privilegeBanned end end --Populate the ContextMenus addContextMenuLabel( --GetText1 function(player) return "Loading..." end, --GetText2 nil, --IsVisible function(player) return getFriendStatus(player) == Enum.FriendStatus.Unknown end) addContextMenuButton("Send " .. friendWord .. " Request", --IsVisible function(player) return (not otherPlayerBlacklist[player]) and (getFriendStatus(player) == Enum.FriendStatus.NotFriend) end, --IsActive function(player) return true end, --DoIt? function(player) otherPlayerBlacklist[player] = true return game.Players.LocalPlayer:RequestFriendship(player) end ) addContextMenuButton("Un" .. friendWordLowercase, --IsVisible function(player) return getFriendStatus(player) == Enum.FriendStatus.Friend end, --IsActive function(player) return true end, --DoIt function(player) return game.Players.LocalPlayer:RevokeFriendship(player) end ) addContextMenuButton("Accept " .. friendWord .. " Request", --IsVisible function(player) return (not friendRequestBlacklist[player]) and (getFriendStatus(player) == Enum.FriendStatus.FriendRequestReceived) end, --IsActive function(player) return true end, --DoIt function(player) return game.Players.LocalPlayer:RequestFriendship(player) end ) addContextMenuButton("Deny " .. friendWord .. " Request", --IsVisible function(player) return getFriendStatus(player) == Enum.FriendStatus.FriendRequestReceived end, --IsActive function(player) return true end, --DoIt function(player) friendRequestBlacklist[player] = true return game.Players.LocalPlayer:RevokeFriendship(player) end ) addContextMenuButton("Cancel " .. friendWord .. " Request", --IsVisible function(player) return false -- disable cancel request for now (can lead to griefing) --return getFriendStatus(player) == Enum.FriendStatus.FriendRequestSent end, --IsActive function(player) return true end, --DoIt function(player) otherPlayerBlacklist[player] = false return game.Players.LocalPlayer:RevokeFriendship(player) end ) function addPersonalServerContext() if personalServerContextAdded then return end personalServerContextAdded = true addContextMenuButton("Ban", --IsVisible function(player) return ( getPrivilegeType(game.Players.LocalPlayer) >= privilegeAdmin and (getPrivilegeType(player) < privilegeAdmin) ) end, --IsActive function(player) return true end, --DoIt function(player) player.PersonalServerRank = privilegeBanned return true end ) addContextMenuButton("Promote to Visitor", --IsVisible function(player) return ( getPrivilegeType(game.Players.LocalPlayer) >= privilegeAdmin ) and ( getPrivilegeType(player) == privilegeBanned ) end, --IsActive function(player) return true end, --DoIt function(player) game:GetService("PersonalServerService"):Promote(player) return true end ) addContextMenuButton("Promote to Member", --IsVisible function(player) return ( getPrivilegeType(game.Players.LocalPlayer) >= privilegeAdmin ) and ( getPrivilegeType(player) == privilegeVisitor ) end, --IsActive function(player) return true end, --DoIt function(player) game:GetService("PersonalServerService"):Promote(player) return true end ) addContextMenuButton("Promote to Admin", --IsVisible function(player) return ( getPrivilegeType(game.Players.LocalPlayer) == privilegeOwner ) and ( getPrivilegeType(player) == privilegeMember ) end, --IsActive function(player) return true end, --DoIt function(player) game:GetService("PersonalServerService"):Promote(player) return true end ) addContextMenuButton("Demote to Member", --IsVisible function(player) return ( getPrivilegeType(game.Players.LocalPlayer) == privilegeOwner ) and ( getPrivilegeType(player) == privilegeAdmin ) end, --IsActive function(player) return true end, --DoIt function(player) game:GetService("PersonalServerService"):Demote(player) return true end ) addContextMenuButton("Demote to Visitor", --IsVisible function(player) return ( getPrivilegeType(game.Players.LocalPlayer) >= privilegeAdmin ) and ( getPrivilegeType(player) == privilegeMember ) end, --IsActive function(player) return true end, --DoIt function(player) game:GetService("PersonalServerService"):Demote(player) return true end ) end local function setupBuildToolManagement() local buildToolManagerAssetId = 64164692 game:GetService("ScriptContext"):AddCoreScript(buildToolManagerAssetId,game.Players.LocalPlayer,"BuildToolManager") end local function getStatColumns(players) for i, player in ipairs(players) do local leaderstats = player:FindFirstChild("leaderstats") if leaderstats then local stats = {} local children = leaderstats:GetChildren() if children then for i, stat in ipairs(children) do if stat:IsA("IntValue") then table.insert(stats, stat) else --TODO: This should check for IntValue only but current ScoreHud does not table.insert(stats, stat) end end end return stats end end return nil end local function determineBoardType() local players = getPlayers() local foundLeaderstats = false local numStats = 0 local foundTeam = false local stats = getStatColumns(players) if stats then foundLeaderstats = true numStats = #stats end for i, player in ipairs(players) do if not foundTeam then if not player.Neutral then foundTeam = true break end end end if foundLeaderstats and foundTeam then return "TeamScore", numStats elseif foundLeaderstats then return "PlayerScore", numStats elseif foundTeam then return "TeamList", numStats else return "PlayerList", numStats end end local function toggleBigWindow() if container == nil then return end if currentWindowState == "Big" then --Hide it if previousWindowState == nil or previousWindowState == "Big" or previousWindowState == "None" then transitionWindowsFunction("None") else transitionWindowsFunction("Small") end else previousWindowState = currentWindowState transitionWindowsFunction("Big") end end local previousBigPlayerList = nil local function rebuildBoard(owner, boardType, numStats) if topRightTrayContainer == nil then topRightTrayContainer = owner:FindFirstChild("PlayerListTopRightFrame") if topRightTrayContainer == nil then topRightTrayContainer = Instance.new("Frame") topRightTrayContainer.Name = "PlayerListTopRightFrame" topRightTrayContainer.BackgroundTransparency = 1 topRightTrayContainer.Size = UDim2.new(0.2, 16, 0.42, 16) topRightTrayContainer.Position = UDim2.new(0.8, 0, 0, 0) topRightTrayContainer.Parent = container end end if minimizedState == nil then minimizedState = Instance.new("Frame") minimizedState.Name = "MinimizedPlayerlist" minimizedState.BackgroundTransparency = 1 minimizedState.Position = UDim2.new(1, -166, 0,0) minimizedState.Size = UDim2.new(0, 151, 0, 30) playerListButton = Instance.new("ImageButton") playerListButton.Name = "GoSmallButton" playerListButton.Image = "rbxasset://textures/ui/playerlist_hidden_small.png" playerListButton.BackgroundTransparency = 1 playerListButton.Size = UDim2.new(0.0, 35, 0, 30) playerListButton.Position = UDim2.new(1, -35, 0, 0) playerListButton.MouseButton1Click:connect( function() transitionWindowsFunction("Small") end) playerListButton.Parent = minimizedState minimizedState.Visible = false robloxLock(minimizedState) minimizedState.Parent = topRightTrayContainer end if bigWindowImposter == nil then bigWindowImposter = owner:FindFirstChild("BigPlayerListWindowImposter") if bigWindowImposter == nil then bigWindowImposter = Instance.new("Frame") bigWindowImposter.Name = "BigPlayerListWindowImposter" bigWindowImposter.Visible = false bigWindowImposter.BackgroundColor3 = Color3.new(0,0,0) bigWindowImposter.BackgroundTransparency = 0.7 bigWindowImposter.BorderSizePixel = 0 bigWindowImposter.Size = UDim2.new(0.4, 7, 0.4, 7) bigWindowImposter.Position = UDim2.new(0.3, 0, 0.3, 0) robloxLock(bigWindowImposter) bigWindowImposter.Parent = container end end if container == nil or container ~= owner then container = owner topRightTrayContainer.Parent = container bigWindowImposter.Parent = container end local smallVisible = true local bigVisible = false if container then if topRightTrayContainer then --Delete the old boards if topRightTrayContainer:FindFirstChild("SmallPlayerlist") then smallVisible = topRightTrayContainer.SmallPlayerlist.Visible topRightTrayContainer.SmallPlayerlist.Parent = nil end end if container:FindFirstChild("BigPlayerlist") then bigVisible = container.BigPlayerlist.Visible or (previousBigPlayerList ~= nil) container.BigPlayerlist.Parent = nil if previousBigPlayerList ~= nil then pcall(function() game.GuiService:RemoveCenterDialog(previousBigPlayerList) end) previousBigPlayerList = nil end end end local smallBoard, bigBoard = createBoardsFunction(boardType, numStats) if smallBoard then smallBoard.Visible = smallVisible smallBoard.Parent = topRightTrayContainer recalculateSmallPlayerListSize(smallBoard) end if bigBoard then if bigVisible then previousBigPlayerList = bigBoard local centerDialogSupported, msg = pcall(function() game.GuiService:AddCenterDialog(previousBigPlayerList, Enum.CenterDialogType.PlayerInitiatedDialog, function() previousBigPlayerList.Visible = true end, function() previousBigPlayerList.Visible = false end) end) bigBoard.Visible = bigVisible else bigBoard.Visible = false end bigBoard.Parent = container end return container end function recalculateSmallPlayerListSize(smallPlayerList) waitForChild(smallPlayerList,"ScrollingArea") waitForChild(smallPlayerList.ScrollingArea, "ScrollingFrame") local scrollingFrame = smallPlayerList.ScrollingArea.ScrollingFrame local playerLines = scrollingFrame:GetChildren() local totalPlayerListSize = 0 for i = 1, #playerLines do totalPlayerListSize = totalPlayerListSize + playerLines[i].AbsoluteSize.Y end if not smallPlayerList.Parent then return end local yOffset = math.max(0,(smallPlayerList.Size.Y.Scale * smallPlayerList.Parent.AbsoluteSize.Y) - totalPlayerListSize - smallWindowHeaderYSize) smallPlayerList.Size = UDim2.new(smallPlayerList.Size.X.Scale,smallPlayerList.Size.X.Offset,smallPlayerList.Size.Y.Scale,-yOffset) end local function showBigPlayerWindow() if container:FindFirstChild("BigPlayerlist") then if container.BigPlayerlist.Visible then return end end bigWindowImposter.Visible = true bigWindowImposter:TweenSizeAndPosition(bigWindowSize, bigWindowPosition, Enum.EasingDirection.Out, bigEasingStyle, 0.3, true, function(state) if state == Enum.TweenStatus.Completed then bigWindowImposter.Visible = false if container:FindFirstChild("BigPlayerlist") then container.BigPlayerlist.Visible = true end end end) end local function hideBigPlayerWindow(completed) if playerContextMenu then playerContextMenu.Visible = false end if container:FindFirstChild("BigPlayerlist") then if container.BigPlayerlist.Visible == false and bigWindowImposter.Visible == false then if completed then completed() end --Already completely hidden return end container.BigPlayerlist.Visible = false end local completedFunction = completed bigWindowImposter.Visible = true bigWindowImposter:TweenSizeAndPosition(UDim2.new(0.4, 0, 0.4, 0), UDim2.new(0.3, 0, 0.3, 0), Enum.EasingDirection.In, Enum.EasingStyle.Quart, 0.15, true, function(state) if state == Enum.TweenStatus.Completed then bigWindowImposter.Visible = false if completedFunction then completedFunction() end end end) end local function hideSmallPlayerWindow(completed) if playerContextMenu then playerContextMenu.Visible = false end if topRightTrayContainer:FindFirstChild("SmallPlayerlist") then local completedFunction = completed if topRightTrayContainer.SmallPlayerlist.Visible then topRightTrayContainer.SmallPlayerlist:TweenPosition(UDim2.new(1,0,smallWindowPosition.Y.Scale, smallWindowPosition.Y.Offset), Enum.EasingDirection.Out, smallEasingStyle, 0.3, true, function(state) if state == Enum.TweenStatus.Completed then if topRightTrayContainer:FindFirstChild("SmallPlayerlist") then topRightTrayContainer.SmallPlayerlist.Visible = false end if completedFunction then completedFunction() end end end) return end end if completed then completed() end end transitionWindowsFunction = function(desiredState) if desiredState == "Big" then minimizedState.Visible = false hideSmallPlayerWindow() if previousBigPlayerList ~= nil then if previousBigPlayerList ~= container:FindFirstChild("BigPlayerlist") then pcall(function() game.GuiService:RemoveCenterDialog(previousBigPlayerList) end) previousBigPlayerList = nil previousBigPlayerList = container:FindFirstChild("BigPlayerlist") end else previousBigPlayerList = container:FindFirstChild("BigPlayerlist") end if previousBigPlayerList then local firstShow = false local centerDialogSupported, msg = pcall(function() game.GuiService:AddCenterDialog(previousBigPlayerList, Enum.CenterDialogType.PlayerInitiatedDialog, function() if not firstShow then showBigPlayerWindow() firstShow = true else previousBigPlayerList.Visible = true end end, function() if previousBigPlayerList then previousBigPlayerList.Visible = false end end) end) if centerDialogSupported == false then print("Exception", msg) showBigPlayerWindow() end else showBigPlayerWindow() end currentWindowState = "Big" elseif desiredState == "Small" then minimizedState.Visible = false if previousBigPlayerList ~= nil then pcall(function() game.GuiService:RemoveCenterDialog(previousBigPlayerList) end) previousBigPlayerList = nil end hideBigPlayerWindow() if topRightTrayContainer:FindFirstChild("SmallPlayerlist") then if not topRightTrayContainer.SmallPlayerlist.Visible or topRightTrayContainer.SmallPlayerlist.Position ~= smallWindowPosition then topRightTrayContainer.SmallPlayerlist.Visible = true topRightTrayContainer.SmallPlayerlist:TweenPosition(smallWindowPosition, Enum.EasingDirection.Out, smallEasingStyle, 0.3, true) end end currentWindowState = "Small" elseif desiredState == "None" then if previousBigPlayerList ~= nil then pcall(function() game.GuiService:RemoveCenterDialog(previousBigPlayerList) end) previousBigPlayerList = nil end local smallDone = false local bigDone = false hideSmallPlayerWindow( function() smallDone = true if bigDone and smallDone then minimizedState.Visible = true end end) hideBigPlayerWindow( function() bigDone = true if bigDone and smallDone then minimizedState.Visible = true end end) currentWindowState = "None" end end local function getStatValuesForPlayer(player) local leaderstats = player:FindFirstChild("leaderstats") if leaderstats then local children = leaderstats:GetChildren() if children then local result = {} --Just go based on position for i, stat in ipairs(children) do if stat:IsA("IntValue") then table.insert(result, stat) else table.insert(result, 0) end end return result, leaderstats end end return nil end --ChildAdded on Player (if it's name is "leaderstats") if UserSettings and LoadLibrary then RbxGui,msg = LoadLibrary("RbxGui") local function createTeamName(name, color) local fontHeight = 20 local frame = Instance.new("Frame") frame.Name = "Team-" .. name frame.BorderSizePixel = 0 frame.BackgroundTransparency = 0.5 frame.BackgroundColor3 = Color3.new(1,1,1) frame.Size = UDim2.new(1, 0, 0, fontHeight) frame.Position = UDim2.new(0,0,0,0) local label = Instance.new("TextLabel") label.Name = "NameLabel" label.Text = " " .. name label.Font = Enum.Font.ArialBold label.FontSize = Enum.FontSize.Size18 label.Position = UDim2.new(0,0,0,0) label.Size = UDim2.new(1,0,1,0) label.TextColor3 = Color3.new(1,1,1) label.BackgroundTransparency = 0.5 label.BackgroundColor3 = getColor(color) label.BorderSizePixel = 0 label.TextXAlignment = Enum.TextXAlignment.Left local changeFunc = nil label, changeFunc = RbxGui.AutoTruncateTextObject(label) label.Parent = frame return frame, changeFunc end local function getFriendStatusIcon(friendStatus) if friendStatus == Enum.FriendStatus.Unknown or friendStatus == Enum.FriendStatus.NotFriend then return nil elseif friendStatus == Enum.FriendStatus.Friend then return "rbxasset://textures/ui/PlayerlistFriendIcon.png" elseif friendStatus == Enum.FriendStatus.FriendRequestSent then return "rbxasset://textures/ui/PlayerlistFriendRequestSentIcon.png" elseif friendStatus == Enum.FriendStatus.FriendRequestReceived then return "rbxasset://textures/ui/PlayerlistFriendRequestReceivedIcon.png" else error("Unknown FriendStatus: " .. friendStatus) end end local function getMembershipTypeIcon(membershipType) if membershipType == Enum.MembershipType.None then return "" elseif membershipType == Enum.MembershipType.BuildersClub then return "rbxasset://textures/ui/TinyBcIcon.png" elseif membershipType == Enum.MembershipType.TurboBuildersClub then return "rbxasset://textures/ui/TinyTbcIcon.png" elseif membershipType == Enum.MembershipType.OutrageousBuildersClub then return "rbxasset://textures/ui/TinyObcIcon.png" else error("Uknown membershipType" .. membershipType) end end local function updatePlayerFriendStatus(nameObject, friendStatus) local fontHeight = 20 local friendIconImage = getFriendStatusIcon(friendStatus) nameObject.MembershipTypeLabel.FriendStatusLabel.Visible = (friendIconImage ~= nil) if friendIconImage ~= nil then --Show friend icon nameObject.MembershipTypeLabel.FriendStatusLabel.Image = friendIconImage nameObject.NameLabel.Position =UDim2.new(0,2*fontHeight,0,1) nameObject.NameLabel.Size = UDim2.new(1,-2*fontHeight,1,-2) else --Hide the friend icon nameObject.NameLabel.Position = UDim2.new(0,fontHeight+1,0,1) nameObject.NameLabel.Size = UDim2.new(1,-(fontHeight+1),1,-2) end end local function updatePlayerName(nameObject, membershipStatus, teamColor) local fontHeight = 20 nameObject.Size = UDim2.new(1,0,0,fontHeight) nameObject.MembershipTypeLabel.Image = getMembershipTypeIcon(membershipStatus) end local function updatePlayerNameColor(player, teamColor) local function updatePlayerNameColorHelper(nameObject) if teamColor ~= nil then nameObject.NameLabel.TextColor3 = getColor(teamColor) nameObject.NameLabel.FullNameLabel.TextColor3 = getColor(teamColor) else nameObject.NameLabel.TextColor3 = Color3.new(1,1,1) nameObject.NameLabel.FullNameLabel.TextColor3 = Color3.new(1,1,1) end end updatePlayerNameColorHelper(playerTable[player].NameObjectSmall) updatePlayerNameColorHelper(playerTable[player].NameObjectBig) end local function createPlayerName(name, membershipStatus, teamColor, friendStatus) local frame = Instance.new("Frame") frame.Name = "Player_" .. name frame.BackgroundColor3 = Color3.new(1,1,1) frame.BackgroundTransparency = 0.5 frame.BorderSizePixel = 0 local membershipStatusLabel = Instance.new("ImageLabel") membershipStatusLabel.Name = "MembershipTypeLabel" membershipStatusLabel.BackgroundTransparency = 1 membershipStatusLabel.Size = UDim2.new(1,0,1,0) membershipStatusLabel.Position = UDim2.new(0,0,0,0) membershipStatusLabel.SizeConstraint = Enum.SizeConstraint.RelativeYY membershipStatusLabel.Parent = frame local friendStatusLabel = Instance.new("ImageLabel") friendStatusLabel.Name = "FriendStatusLabel" friendStatusLabel.Visible = false friendStatusLabel.BackgroundTransparency = 1 friendStatusLabel.Size = UDim2.new(1,0,1,0) friendStatusLabel.Position = UDim2.new(1,0,0,0) friendStatusLabel.Parent = membershipStatusLabel local changeNameFunction local nameLabel = Instance.new("TextLabel") nameLabel.Name = "NameLabel" nameLabel.Text = name nameLabel.Font = Enum.Font.ArialBold nameLabel.FontSize = Enum.FontSize.Size14 nameLabel.TextColor3 = Color3.new(1,1,1) nameLabel.BackgroundTransparency = 1 nameLabel.BackgroundColor3 = Color3.new(0,0,0) nameLabel.TextXAlignment = Enum.TextXAlignment.Left nameLabel, changeNameFunction = RbxGui.AutoTruncateTextObject(nameLabel) nameLabel.Parent = frame updatePlayerName(frame, membershipStatus, teamColor) if supportFriends and not friendRequestBlacklist[game.Players:FindFirstChild(name)] then updatePlayerFriendStatus(frame, friendStatus) else updatePlayerFriendStatus(frame, Enum.FriendStatus.NotFriend) end return frame, changeNameFunction end local function createStatColumn(i, numColumns, isTeam, color3, isHeader, stat) local textLabel = Instance.new("TextButton") textLabel.Name = "Stat" .. i textLabel.AutoButtonColor = false textLabel.TextColor3 = Color3.new(1,1,1) textLabel.TextXAlignment = Enum.TextXAlignment.Right textLabel.TextYAlignment = Enum.TextYAlignment.Center textLabel.FontSize = Enum.FontSize.Size14 if isHeader then textLabel.FontSize = Enum.FontSize.Size18 else textLabel.FontSize = Enum.FontSize.Size14 end if isHeader or isTeam then textLabel.Font = Enum.Font.ArialBold else textLabel.Font = Enum.Font.Arial end if isTeam then textLabel.BackgroundColor3 = color3 textLabel.Text = 0 else textLabel.BackgroundColor3 = Color3.new(0,0,0) textLabel.Text = "" end textLabel.BackgroundTransparency = 1 if i == numColumns then textLabel.Size = UDim2.new(1/numColumns, -6, 1, 0) else textLabel.Size = UDim2.new(1/numColumns, -4, 1, 0) end textLabel.Position = UDim2.new((i-1) * (1/numColumns), 0, 0, 0) local truncLabel, changer = RbxGui.AutoTruncateTextObject(textLabel) if isHeader then local mouseCon = {} mouseCon[1] = truncLabel.MouseEnter:connect(function() truncLabel.BackgroundTransparency = 0.2 end) mouseCon[2] = truncLabel.MouseLeave:connect(function() truncLabel.BackgroundTransparency = 1 end) mouseCon[3] = truncLabel.MouseButton1Click:connect(function() sortPlayerListsFunction(truncLabel:GetChildren()[1].Name, (currentSortName == truncLabel:GetChildren()[1].Name) ) truncLabel.BackgroundTransparency = 1 end) mouseCon[4] = truncLabel:GetChildren()[1].MouseButton1Click:connect(function() sortPlayerListsFunction(textLabel.Name, (currentSortName == truncLabel.Name) ) truncLabel.BackgroundTransparency = 1 end) mouseCon[5] = nil mouseCon[5] = truncLabel.AncestryChanged:connect(function(child,parent) if parent == nil then for i,connection in pairs(mouseCon) do connection:disconnect() end end end) end return truncLabel, changer end local function createStatHeaders(stats, numColumns, isBig) local frame = Instance.new("Frame") frame.Name = "Headers" frame.BorderSizePixel = 0 frame.BackgroundColor3 = Color3.new(0,0,0) frame.BackgroundTransparency = 1 local nameSize if isBig then nameSize = 0.5 elseif numColumns == 1 then nameSize = 0.7 elseif numColumns == 2 then nameSize = 0.6 else nameSize = 0.45 end frame.Size = UDim2.new(1-nameSize, 0, 1,0) if isBig then frame.Position = UDim2.new(nameSize,-25, 0,0) else frame.Position = UDim2.new(nameSize,0, 0,0) end local i = 1 while i <= numColumns do local headerColumn, changeText = createStatColumn(i, numColumns, false, nil, true,stats[i]) changeText(stats[i].Name) headerColumn.Parent = frame i = i + 1 end return frame, textChangers end local function createStatColumns(nameObject, numColumns, isTeam, isBig) local frame = Instance.new("Frame") frame.Name = nameObject.Name .. "_WithStats" frame.BorderSizePixel = 0 frame.BackgroundColor3 = nameObject.BackgroundColor3 frame.BackgroundTransparency = nameObject.BackgroundTransparency frame.Size = nameObject.Size frame.Position = nameObject.Position nameObject.BackgroundTransparency = 1 if numColumns == 0 then nameObject.Size = UDim2.new(1,0,1,0) nameObject.Position = UDim2.new(0,0,0,0) nameObject.Parent = frame return frame end local statFrame = Instance.new("Frame") statFrame.Name = "Stats" if isTeam then statFrame.BorderSizePixel = 0 statFrame.BackgroundColor3 = nameObject.NameLabel.BackgroundColor3 statFrame.BackgroundTransparency = nameObject.NameLabel.BackgroundTransparency else statFrame.BackgroundTransparency = 1 end local nameSize if isBig then nameSize = 0.5 elseif numColumns == 1 then nameSize = 0.7 elseif numColumns == 2 then nameSize = 0.6 else nameSize = 0.45 end nameObject.Size = UDim2.new(nameSize, 0, 1, 0) nameObject.Position = UDim2.new(0, 0, 0, 0) statFrame.Size = UDim2.new(1-nameSize,0, 1,0) statFrame.Position = UDim2.new(nameSize,0, 0,0) nameObject.Parent = frame statFrame.Parent = frame local textChangers = {} local i = 1 while i <= numColumns do local statColumn, changeText = createStatColumn(i, numColumns, isTeam, statFrame.BackgroundColor3) statColumn.Parent = statFrame table.insert(textChangers, changeText) i = i + 1 end return frame, statFrame, textChangers end local function createAlternatingRows(objects) for i, line in ipairs(objects) do if i % 2 == 0 then line.BackgroundTransparency = 1 else line.BackgroundTransparency = 0.95 end end end local removeFromTeam = nil local function clearTableEntry(obj, tableInfo) if tableInfo.MainObjectSmall then tableInfo.MainObjectSmall.Parent = nil tableInfo.MainObjectSmall = nil end if tableInfo.MainObjectBig then tableInfo.MainObjectBig.Parent = nil tableInfo.MainObjectBig = nil end if tableInfo.Connections then for i, connection in ipairs(tableInfo.Connections) do connection:disconnect() end tableInfo.Connections = nil end if tableInfo.LeaderStatConnections then for i, connection in ipairs(tableInfo.LeaderStatConnections) do connection:disconnect() end tableInfo.LeaderStatConnections = nil end if tableInfo.CurrentTeam then removeFromTeam(obj) tableInfo.CurrentTeam = nil end if tableInfo.Players then for i, player in ipairs(tableInfo.Players) do playerTable[player].CurrentTeam = nil end tableInfo.Players = {} end if tableInfo.StatValues then tableInfo.StatValues = nil end end local function resetPlayerTable() for player, info in pairs(playerTable) do clearTableEntry(player, info) playerTable[player] = nil end playerTable = {} end local function resetTeamTable() for team, info in pairs(teamTable) do clearTableEntry(team, info) teamTable[team] = nil end teamTable = {} teamColorTable = {} end local function getBoardTypeInfo() local isTeam = (currentBoardType == "TeamScore" or currentBoardType == "TeamList") local isScore = (currentBoardType == "TeamScore" or currentBoardType == "PlayerScore") return isTeam, isScore end local function recomputeTeamScore(team, column) if not team or team == "Neutral" then return end local function recomputeScoreHelper(statChangers) if statChangers and column <= #statChangers then local sum = 0 for i, p in ipairs(teamTable[team].Players) do if playerTable[p].StatValues and column <= #playerTable[p].StatValues then sum = sum + playerTable[p].StatValues[column].Value end end statChangers[column](sum) end end recomputeScoreHelper(teamTable[team].StatChangersSmall) recomputeScoreHelper(teamTable[team].StatChangersBig) end local function recomputeCompleteTeamScore(team) local col = 1 while col <= currentStatCount do recomputeTeamScore(team, col) col = col + 1 end end removeFromTeam = function(player) if playerTable[player].CurrentTeam ~= nil and teamTable[playerTable[player].CurrentTeam] ~= nil then ArrayRemove(teamTable[playerTable[player].CurrentTeam].Players, player) recomputeCompleteTeamScore(playerTable[player].CurrentTeam) playerTable[player].CurrentTeam = nil end end local function assignToTeam(player) local isTeam, isScore = getBoardTypeInfo() if isTeam then local newTeam = nil if player.Neutral or teamColorTable[player.TeamColor.Name] == nil then newTeam = "Neutral" else newTeam = teamColorTable[player.TeamColor.Name] end if playerTable[player].CurrentTeam == newTeam then return end local oldTeam = playerTable[player].LastTeam removeFromTeam(player) playerTable[player].CurrentTeam = newTeam if teamTable[oldTeam] and teamTable[oldTeam]["NameChangeFuncBig"] then if #teamTable[oldTeam].Players < 1 then teamTable[oldTeam]["NameChangeFuncBig"](" " .. oldTeam.Name) else teamTable[oldTeam]["NameChangeFuncBig"](" " .. oldTeam.Name .. " (" .. tostring(#teamTable[oldTeam].Players) ..")") end end if teamTable[newTeam] then table.insert(teamTable[newTeam].Players, player) if newTeam["Name"] then if teamTable[newTeam]["NameChangeFuncBig"] then if #teamTable[newTeam].Players < 1 then teamTable[newTeam]["NameChangeFuncBig"](" " .. newTeam.Name) else teamTable[newTeam]["NameChangeFuncBig"](" " .. newTeam.Name .. " (" .. tostring(#teamTable[newTeam].Players) ..")") end end end end if newTeam == "Neutral" then updatePlayerNameColor(player, nil) else updatePlayerNameColor(player, player.TeamColor) end playerTable[player].LastTeam = newTeam recomputeCompleteTeamScore(newTeam) --Relayout if sortPlayerListsFunction then sortPlayerListsFunction() end end end local function buildTeamObject(team, numStatColumns, suffix) local isTeam, isScore = getBoardTypeInfo() local teamObject, changeFunc = createTeamName(team.Name, team.TeamColor) teamObject.NameLabel.Text = " " .. team.Name .. " (0)" if not teamTable[team] then teamTable[team] = {} end teamTable[team]["NameObject" .. suffix] = teamObject teamTable[team]["NameChangeFunc" .. suffix] = changeFunc if isScore then local statObject local textChangers teamObject, statObject, textChangers = createStatColumns(teamObject, numStatColumns, true, suffix == "Big") teamTable[team]["StatObject" .. suffix] = statObject teamTable[team]["StatChangers" .. suffix] = textChangers end teamTable[team]["MainObject" .. suffix] = teamObject changeFunc(" " .. team.Name) if not teamTable[team].Players then teamTable[team].Players = {} else if suffix ~= "Small" and #teamTable[team].Players > 0 then changeFunc(" " .. team.Name .. " (" .. tostring(#teamTable[team].Players) ..")") end end return teamObject end local currentContextMenuPlayer = nil local function updatePlayerContextMenu(player,x,y) currentContextMenuPlayer = player local elementHeight = 18 local function highlight(button) button.TextColor3 = Color3.new(0,0,0) button.BackgroundColor3 = Color3.new(0.8,0.8,0.8) end local function clearHighlight(button) button.TextColor3 = Color3.new(1,1,1) button.BackgroundColor3 = Color3.new(0,0,0) end if playerContextMenu == nil then elementNames = {} elementNameToElement = {} for i, contextElement in ipairs(contextMenuElements) do table.insert(elementNames, contextElement.Text) elementNameToElement[tostring(contextElement.Text)] = contextElement end playerContextMenu = Instance.new("TextButton") playerContextMenu.Name = "PlayerListContextMenu" playerContextMenu.Style = Enum.ButtonStyle.RobloxButton playerContextMenu.Text = "" playerContextMenu.Visible = false playerContextMenu.ZIndex = 4 playerContextMenu.MouseLeave:connect(function() local menuChildren = playerContextMenu:GetChildren() for i = 1, #menuChildren do if menuChildren[i].Name == "ChoiceButton" then menuChildren[i].TextColor3 = Color3.new(1,1,1) menuChildren[i].BackgroundTransparency = 1 end end playerContextMenu.Visible = false inContextMenu = false end) playerContextMenu.MouseEnter:connect(function() inContextMenu = true end) for i = 1, #elementNames do local newElementButton = Instance.new("TextButton") newElementButton.Name = "ChoiceButton" newElementButton.Text = elementNames[i] newElementButton.TextColor3 = Color3.new(1,1,1) newElementButton.Font = Enum.Font.Arial newElementButton.FontSize = Enum.FontSize.Size14 newElementButton.BackgroundTransparency = 1 newElementButton.TextWrap = true newElementButton.Size = UDim2.new(1,0,0,elementHeight) newElementButton.Position = UDim2.new(0,0,0,elementHeight * (i - 1)) newElementButton.ZIndex = playerContextMenu.ZIndex + 1 newElementButton.MouseEnter:connect(function() newElementButton.TextColor3 = Color3.new(0,0,0) newElementButton.BackgroundTransparency = 0 end) newElementButton.MouseLeave:connect(function() newElementButton.TextColor3 = Color3.new(1,1,1) newElementButton.BackgroundTransparency = 1 end) newElementButton.MouseButton1Click:connect(function() local element = elementNameToElement[newElementButton.Text] pcall(function() element.DoIt(currentContextMenuPlayer) end) playerContextMenu.Visible = false newElementButton.TextColor3 = Color3.new(1,1,1) newElementButton.BackgroundTransparency = 1 end) newElementButton.Parent = playerContextMenu end robloxLock(playerContextMenu) playerContextMenu.Parent = script.Parent end local visibleElements = 0 for i, contextElement in ipairs(contextMenuElements) do local isVisible = false if contextElement.IsVisible then local success, visible = pcall(function() return contextElement.IsVisible(currentContextMenuPlayer) end) if success then isVisible = visible else print("Error in IsVisible call: " .. visible) end end if isVisible then local foundElement = false for i = 1, #elementNames do if elementNames[i] == contextElement.Text then foundElement = true break end end if not foundElement then table.insert(elementNames,contextElement.Text) end visibleElements = visibleElements + 1 else for i = 1, #elementNames do if elementNames[i] == contextElement.Text then table.remove(elementNames,i) break end end end end playerContextMenu.Size = UDim2.new(0, 150, 0, elementHeight + (elementHeight * visibleElements) ) if x and y then x = x - (playerContextMenu.AbsoluteSize.X/2) if x + playerContextMenu.AbsoluteSize.X >= script.Parent.AbsoluteSize.X then x = script.Parent.AbsoluteSize.X - playerContextMenu.AbsoluteSize.X end playerContextMenu.Position = UDim2.new(0, x, 0, y - 3) end local elementPos = 0 local contextChildren = playerContextMenu:GetChildren() for i = 1, #contextChildren do if contextChildren[i]:IsA("GuiObject") and contextChildren[i].Name == "ChoiceButton" then contextChildren[i].Visible = false for j = 1, #elementNames do if elementNames[j] == contextChildren[i].Text then contextChildren[i].Visible = true contextChildren[i].Position = UDim2.new(0,0,0,elementPos * elementHeight) elementPos = elementPos + 1 break end end end end end local function playerContextMenuHasItems() if playerContextMenu then local children = playerContextMenu:GetChildren() for i = 1, #children do if children[i]:IsA("GuiObject") and children[i].Name == "ChoiceButton" and children[i].Visible then return true end end end return false end local function showPlayerMenu(player, x, y) updatePlayerContextMenu(player,x,y) if not playerContextMenuHasItems() then return end -- don't show if we have nothing to show playerContextMenu.Visible = true end local function buildPlayerObject(player, numStatColumns, suffix) if not player then return nil end local isTeam, isScore = getBoardTypeInfo() local playerObject = nil local changePlayerNameFunction = nil local currentColor = nil if isTeam and not player.Neutral then currentColor = player.TeamColor.Color else currentColor = Color3.new(1,1,1) end playerObject, changePlayerNameFunction = createPlayerName(player.Name, player.MembershipType, currentColor, getFriendStatus(player)) if not playerTable[player] then playerTable[player] = {} end if not playerTable[player].Connections then playerTable[player].Connections = {} end if not playerTable[player].CurrentTeam then playerTable[player].CurrentTeam = nil end if not playerTable[player].LastTeam then playerTable[player].LastTeam = nil end playerTable[player]["NameObject" .. suffix] = playerObject playerTable[player]["ChangeName" .. suffix] = changePlayerNameFunction if isScore then local statObject = nil local textChangers = nil playerObject, statObject, textChangers = createStatColumns(playerObject, numStatColumns, false, suffix == "Big") playerTable[player]["StatObject" .. suffix]= statObject playerTable[player]["StatChangers" .. suffix] = textChangers local statValues, leaderstats = getStatValuesForPlayer(player) if not statValues or #statValues < numStatColumns then if not playerTable[player].LeaderStatConnections then playerTable[player].LeaderStatConnections = {} end --Setup a listener to see when this data gets filled in if not leaderstats then --We don't even have a leaderstats child, wait for one table.insert(playerTable[player].LeaderStatConnections, player.ChildAdded:connect( function(child) if child.Name == "leaderstats" then --Connections will be torn down recreatePlayerFunction(player) else table.insert(playerTable[player].LeaderStatConnections, child.Changed:connect( function(prop) if prop == "Name" and child.Name == "leaderstats" then --Connections will be torn down recreatePlayerFunction(player) end end)) end end)) else --We have a leaderstats, but not enough children, recreate if we get them table.insert(playerTable[player].LeaderStatConnections, leaderstats.ChildAdded:connect( function(child) --TODO only look for IntValue recreatePlayerFunction(player) end) ) table.insert(playerTable[player].LeaderStatConnections, leaderstats.AncestryChanged:connect( function(child) --We got deleted, try again recreatePlayerFunction(player) end) ) end end if statValues then if not playerTable[player].StatValues then playerTable[player].StatValues = {} end local pos = 1 while pos <= numStatColumns and pos <= #statValues do local currentColumn = pos local statValue = statValues[pos] local statChanger = textChangers[pos] local updateStat = function(val) statChanger(val) if playerTable[player] ~= nil then recomputeTeamScore(playerTable[player].CurrentTeam, currentColumn) end end if pos > #playerTable[player].StatValues then table.insert(playerTable[player].StatValues, statValue) end if type(statValue) ~= "number" and statValue["Changed"] then table.insert(playerTable[player].Connections, statValue.Changed:connect(updateStat) ) end table.insert(playerTable[player].Connections, statValue.AncestryChanged:connect( function() recreatePlayerFunction(player) end) ) updateStat(statValue.Value) pos = pos + 1 end end end if supportFriends and player ~= game.Players.LocalPlayer and player.userId > 0 and game.Players.LocalPlayer.userId > 0 then local button = Instance.new("TextButton") button.Name = playerObject.Name .. "Button" button.Text = "" button.Active = false button.Size = playerObject.Size button.Position = playerObject.Position button.BackgroundColor3 = playerObject.BackgroundColor3 local secondButton = Instance.new("TextButton") secondButton.Name = playerObject.Name .. "RealButton" secondButton.Text = "" secondButton.BackgroundTransparency = 1 secondButton.BackgroundColor3 = playerObject.BackgroundColor3 local theNameLabel = playerObject:findFirstChild("NameLabel",true) if theNameLabel then theNameLabel.TextColor3 = Color3.new(1,1,1) secondButton.Parent = theNameLabel end secondButton.Parent.BackgroundTransparency = 1 secondButton.Parent.Visible = true secondButton.ZIndex = 2 secondButton.Size = UDim2.new(1,0,1,0) local previousTransparency = nil table.insert(playerTable[player].Connections, secondButton.MouseEnter:connect( function(x,y) if playerContextMenu and playerContextMenu.Visible then return end -- don't update if we currently see it updatePlayerContextMenu(player,x,y) if not playerContextMenuHasItems() then return end -- don't show if we have nothing to show if previousTransparency == nil then previousTransparency = secondButton.BackgroundTransparency end secondButton.Parent.BackgroundTransparency = 0 end)) table.insert(playerTable[player].Connections, secondButton.MouseLeave:connect( function() if previousTransparency ~= nil then previousTransparency = nil end delay(0.01,function() if playerContextMenu and not inContextMenu then playerContextMenu.Visible = false end end) secondButton.Parent.BackgroundTransparency = 1 end)) local mouseDownX, mouseDownY table.insert(playerTable[player].Connections, secondButton.MouseButton1Down:connect(function(x,y) mouseDownX = x mouseDownY = y end)) table.insert(playerTable[player].Connections, secondButton.MouseButton1Click:connect(function() showPlayerMenu(player, mouseDownX, secondButton.AbsolutePosition.Y + secondButton.AbsoluteSize.Y ) end)) playerObject.BackgroundTransparency = 1 playerObject.Size = UDim2.new(1,0,1,0) playerObject.Position = UDim2.new(0,0,0,0) playerObject.Parent = button playerTable[player]["MainObject" .. suffix] = button playerObject = button else playerTable[player]["MainObject" .. suffix] = playerObject if player == game.Players.LocalPlayer and supportFriends then table.insert(playerTable[player].Connections, player.FriendStatusChanged:connect( function(otherPlayer, friendStatus) if friendRequestBlacklist[otherPlayer] then updatePlayerFriendStatus(playerTable[otherPlayer]["NameObject" .. suffix], Enum.FriendStatus.NotFriend) elseif playerTable[otherPlayer] then updatePlayerFriendStatus(playerTable[otherPlayer]["NameObject" .. suffix], friendStatus) end end) ) end end table.insert(playerTable[player].Connections, player.Changed:connect( function(prop) if prop == "MembershipType" then updatePlayerName(playerTable[player]["NameObject" .. suffix], player.MembershipType, currentColor) elseif prop == "Name" then playerTable[player]["ChangeName" .. suffix](player.Name) elseif prop == "Neutral" or prop == "TeamColor" then assignToTeam(player) end end) ) return playerObject end local function doSort(tableToSort, objectName, order, startPos, sortType, ascending) local orderedPlayerTable = {} getLocalPlayer = false for i, player in ipairs(tableToSort) do if playerTable[player] then if playerTable[player][objectName] ~= nil then if player ~= game.Players.LocalPlayer then table.insert(orderedPlayerTable,playerTable[player][objectName]) else getLocalPlayer = true end end end end if sortType == nil then -- default back to alphabetical sort table.sort(orderedPlayerTable,function(a,b) return string.lower(a:FindFirstChild("FullNameLabel",true).Text) < string.lower(b:FindFirstChild("FullNameLabel",true).Text) end) else -- we are sorting by a value table.sort(orderedPlayerTable,function(a,b) if ascending then currentSortName = "" return tonumber(a:FindFirstChild(sortType,true).Text) > tonumber(b:FindFirstChild(sortType,true).Text) else currentSortName = sortType return tonumber(a:FindFirstChild(sortType,true).Text) < tonumber(b:FindFirstChild(sortType,true).Text) end end) end if getLocalPlayer and playerTable[game.Players.LocalPlayer] and playerTable[game.Players.LocalPlayer][objectName] then table.insert(orderedPlayerTable,1,playerTable[game.Players.LocalPlayer][objectName]) end for i = 1, #orderedPlayerTable do order[orderedPlayerTable[i]] = startPos startPos = startPos + 1 end return startPos end local function orderScrollList(scrollOrder, objectName, scrollFrame, sortType, ascending) local pos = 0 local order = {} local isTeam, isScore = getBoardTypeInfo() for i, obj in ipairs(scrollOrder) do order[obj] = 0 end if isTeam then local teams = getTeams() for i, team in ipairs(teams) do if teamTable[team][objectName] then order[teamTable[team][objectName]] = pos pos = pos + 1 end pos = doSort(teamTable[team].Players, objectName, order, pos, sortType, ascending) end if #teamTable["Neutral"].Players > 0 then teamTable["Neutral"][objectName].Parent = scrollFrame order[teamTable["Neutral"][objectName]] = pos pos = pos + 1 doSort(teamTable["Neutral"].Players, objectName, order, pos, sortType, ascending) else teamTable["Neutral"][objectName].Parent = nil end else local players = getPlayers() doSort(players, objectName, order, pos, sortType, ascending) end table.sort(scrollOrder, function(a,b) return order[a] < order[b] end) end local function createPlayerListBasics(frame, isBig) local headerFrame = Instance.new("Frame") headerFrame.Name = "Header" headerFrame.BackgroundTransparency = 1 headerFrame.Size = UDim2.new(1,-13,0,26) headerFrame.Position = UDim2.new(0,0,0,0) headerFrame.Parent = frame local lowerPaneFrame = Instance.new("Frame") lowerPaneFrame.Name = "ScrollingArea" lowerPaneFrame.BackgroundTransparency = 1 lowerPaneFrame.Size = UDim2.new(1,-3,1,-26) if not isBig then lowerPaneFrame.Size = UDim2.new(1,-3,1,-30) end lowerPaneFrame.Position = UDim2.new(0,0,0,26) lowerPaneFrame.Parent = frame local scrollOrder = {} local scrollFrame, scrollUp, scrollDown, recalculateScroll, scrollBar = RbxGui.CreateScrollingFrame(scrollOrder) scrollBar.Size = UDim2.new(0, 17, 1, -36) if isBig then scrollBar.Size = UDim2.new(0, 17, 1, -61) end scrollBar.Parent = lowerPaneFrame scrollFrame.Parent = lowerPaneFrame scrollUp.Parent = lowerPaneFrame scrollDown.Parent = lowerPaneFrame if isBig then scrollFrame.Position = UDim2.new(0,0,0,0) scrollUp.Position = UDim2.new(1,-41,0,5) scrollDown.Position = UDim2.new(1,-41,1,-35) scrollBar.Position = UDim2.new(1, -41, 0, 24) scrollFrame.Size = UDim2.new(1,-48,1,-16) headerFrame.Size = UDim2.new(1,-20,0,26) else scrollBar.Position = UDim2.new(1, -15, 0, 14) scrollBar.Size = UDim2.new(0,17,1,-36) scrollFrame.Position = UDim2.new(0,1,0,0) scrollUp.Position = UDim2.new(1,-15,0,-5) scrollDown.Position = UDim2.new(1,-15,1,-20) lowerPaneFrame.Position = UDim2.new(0,0,0,30) local toggleScrollBar = function(visible) if visible then scrollFrame.Size = UDim2.new(1,-16,1,0) headerFrame.Size = UDim2.new(1,-16,0,smallWindowHeaderYSize) else scrollFrame.Size = UDim2.new(1,0,1,0) headerFrame.Size = UDim2.new(1,0,0,smallWindowHeaderYSize) end scrollUp.Visible = visible scrollDown.Visible = visible scrollBar.Visible = visible end scrollUp.Changed:connect(function(prop) if prop == "Active" then toggleScrollBar(scrollUp.Active or scrollDown.Active) end end) scrollDown.Changed:connect(function(prop) if prop == "Active" then toggleScrollBar(scrollUp.Active or scrollDown.Active) end end) toggleScrollBar(scrollUp.Active or scrollDown.Active) end return headerFrame, scrollFrame, recalculateScroll, scrollOrder end createBoardsFunction = function (boardType, numStatColumns) local updatePlayerCount = function() return #getPlayers() end local smallFrame = Instance.new("Frame") smallFrame.Name = "SmallPlayerlist" smallFrame.Position = smallWindowPosition smallFrame.Active = false smallFrame.Size = smallWindowSize smallFrame.BackgroundColor3 = Color3.new(0,0,0) smallFrame.BackgroundTransparency = 0.7 smallFrame.BorderSizePixel = 0 local bigFrame = Instance.new("Frame") bigFrame.Name = "BigPlayerlist" bigFrame.Size = bigWindowSize bigFrame.Position = bigWindowPosition bigFrame.BackgroundColor3 = Color3.new(0,0,0) bigFrame.BackgroundTransparency = 0.7 bigFrame.BorderSizePixel = 0 bigFrame.Visible = false local bigFrameWrapper = Instance.new("Frame") bigFrameWrapper.Name = "Expander" bigFrameWrapper.Size = UDim2.new(1,21,1,16) bigFrameWrapper.Position = UDim2.new(0, 0, 0,0) bigFrameWrapper.BackgroundTransparency = 1 bigFrameWrapper.Parent = bigFrame local smallHeaderFrame, scrollFrameSmall, recalculateScrollSmall, scrollOrderSmall = createPlayerListBasics(smallFrame, false) local bigHeaderFrame, scrollFrameBig, recalculateScrollBig, scrollOrderBig = createPlayerListBasics(bigFrameWrapper, true) local playerListButton = Instance.new("ImageButton") playerListButton.Name = "GoBigButton" playerListButton.BackgroundTransparency = 1 playerListButton.Image = "rbxasset://textures/ui/playerlist_small_maximize.png" playerListButton.Size = UDim2.new(0.0, 35, 0, 29) playerListButton.Position = UDim2.new(0, 0, 0, 0) playerListButton.MouseButton1Click:connect( function() toggleBigWindow() end) playerListButton.Parent = smallHeaderFrame playerListButton = Instance.new("ImageButton") playerListButton.Name = "CloseButton" playerListButton.BackgroundTransparency = 1 playerListButton.Image = "rbxasset://textures/ui/playerlist_small_hide.png" playerListButton.Size = UDim2.new(0.0, 38, 0, 29) playerListButton.Position = UDim2.new(0, 35, 0, 0) playerListButton.MouseButton1Click:connect( function() transitionWindowsFunction("None") end) playerListButton.Parent = smallHeaderFrame playerListButton = Instance.new("ImageButton") playerListButton.Name = "CloseButton" playerListButton.Image = "rbxasset://textures/ui/playerlist_big_hide.png" playerListButton.BackgroundTransparency = 1 playerListButton.Size = UDim2.new(0.0, 29, 0, 29) playerListButton.Position = UDim2.new(1, -30, 0.5, -13) playerListButton.MouseButton1Click:connect( function() toggleBigWindow() end) playerListButton.Parent = bigHeaderFrame local placeName = Instance.new("TextButton") placeName.Name = "PlaceName" placeName.Text = " Players (" .. tostring(updatePlayerCount()) .. ")" placeName.AutoButtonColor = false placeName.FontSize = Enum.FontSize.Size24 placeName.TextXAlignment = Enum.TextXAlignment.Left placeName.Font = Enum.Font.ArialBold placeName.BorderSizePixel = 0 placeName.BackgroundColor3 = Color3.new(0,0,0) placeName.BackgroundTransparency = 1 placeName.TextColor3 = Color3.new(1,1,1) placeName.Size = UDim2.new(0.4, 0, 1, 0) placeName.Position = UDim2.new(0, 0, 0.0, 0) placeName = RbxGui.AutoTruncateTextObject(placeName) placeName.Parent = bigHeaderFrame placeName.MouseEnter:connect(function() placeName.BackgroundTransparency = 0.2 end) placeName.MouseLeave:connect(function() placeName.BackgroundTransparency = 1 end) placeName.MouseButton1Click:connect(function() sortPlayerListsFunction() end) currentBoardType = boardType currentStatCount = numStatColumns local isTeam, isScore = getBoardTypeInfo() local players = getPlayers() if isScore then local statColumns = getStatColumns(players) numStatColumns = #statColumns if numStatColumns > 3 then numStatColumns = 3 end createStatHeaders(statColumns, numStatColumns, false).Parent = smallHeaderFrame createStatHeaders(statColumns, currentStatCount, true).Parent = bigHeaderFrame end --Clean up all old stuff resetPlayerTable() updatePlayerCount() for i, player in ipairs(players) do local playerObject = buildPlayerObject(player, numStatColumns, "Small") table.insert(scrollOrderSmall, playerObject) playerObject.Parent = scrollFrameSmall playerObject = buildPlayerObject(player, currentStatCount, "Big") table.insert(scrollOrderBig, playerObject) playerObject.Parent = scrollFrameBig end --Clean up old stuff resetTeamTable() local teamStatObjects = {} if isTeam then local teams = getTeams() local i = #teams while i >= 1 do --We go backwards so the "first" team color gets the team local team = teams[i] teamColorTable[team.TeamColor.Name] = team i = i - 1 end --Adding/Removing a Team causes a full invalidation of the board for i, team in ipairs(teams) do local teamObject = buildTeamObject(team, numStatColumns, "Small") table.insert(scrollOrderSmall, teamObject) teamObject.Parent = scrollFrameSmall teamObject = buildTeamObject(team, currentStatCount, "Big") table.insert(scrollOrderBig, teamObject) teamObject.Parent = scrollFrameBig end teamTable["Neutral"] = {} teamTable["Neutral"].Players = {} local neutralTeamObject = createTeamName("Neutral", BrickColor.palette(8)) teamTable["Neutral"].NameObjectSmall = neutralTeamObject teamTable["Neutral"].StatObjectSmall = nil teamTable["Neutral"].MainObjectSmall = neutralTeamObject table.insert(scrollOrderSmall, neutralTeamObject) neutralTeamObject = createTeamName("Neutral", BrickColor.palette(8)) teamTable["Neutral"].NameObjectBig = neutralTeamObject teamTable["Neutral"].StatObjectBig = nil teamTable["Neutral"].MainObjectBig = neutralTeamObject table.insert(scrollOrderBig, neutralTeamObject) local neutralPlayers = {} for i, player in ipairs(players) do assignToTeam(player) end end removePlayerFunction = function(player) if playerTable[player] then clearTableEntry(player, playerTable[player]) placeName.Text = " Players (" .. tostring(updatePlayerCount()) .. ")" ArrayRemove(scrollOrderSmall, playerTable[player].MainObjectSmall) ArrayRemove(scrollOrderBig, playerTable[player].MainObjectBig) playerTable[player] = nil recalculateSmallPlayerListSize(smallFrame) end end recreatePlayerFunction = function(player) placeName.Text = " Players (" .. tostring(updatePlayerCount()) .. ")" removePlayerFunction(player) local playerObject = buildPlayerObject(player, numStatColumns, "Small") table.insert(scrollOrderSmall, playerObject) robloxLock(playerObject) playerObject.Parent = scrollFrameSmall playerObject = buildPlayerObject(player, currentStatCount, "Big") table.insert(scrollOrderBig, playerObject) robloxLock(playerObject) playerObject.Parent = scrollFrameBig local isTeam, isScore = getBoardTypeInfo() if isTeam then assignToTeam(player) end sortPlayerListsFunction() recalculateSmallPlayerListSize(smallFrame) end if screenResizeCon then screenResizeCon:disconnect() end screenResizeCon = screen.Changed:connect( function(prop) if prop == "AbsoluteSize" then wait() recalculateSmallPlayerListSize(smallFrame) end end) sortPlayerListsFunction = function(sortType, ascending) orderScrollList(scrollOrderSmall, "MainObjectSmall", scrollFrameSmall, sortType, ascending) recalculateScrollSmall() createAlternatingRows(scrollOrderSmall) orderScrollList(scrollOrderBig, "MainObjectBig", scrollFrameBig, sortType, ascending) recalculateScrollBig() createAlternatingRows(scrollOrderBig) end sortPlayerListsFunction() robloxLock(smallFrame) robloxLock(bigFrame) return smallFrame, bigFrame end --Teams changing invalidates the whole board local function teamsChanged() if debounceTeamsChanged then return end debounceTeamsChanged = true wait() rebuildBoard(script.Parent, determineBoardType()) debounceTeamsChanged = false end local checkIfBoardChanged = function() local newBoardType, numStats = determineBoardType() if newBoardType ~= currentBoardType or numStats ~= currentStatCount then rebuildBoard(script.Parent, newBoardType, numStats) end end local function buildPlayerList() waitForChild(game, "Players") waitForProperty(game.Players, "LocalPlayer") local teams = game:GetService("Teams") if teams then local teamConnections = {} teams.ChildAdded:connect( function(child) if child:IsA("Team") then teamsChanged() teamConnections[child] = child.Changed:connect( function(prop) if prop == "TeamColor" or prop == "Name" then --Rebuild when things change teamsChanged() end end) end end) teams.ChildRemoved:connect( function(child) if child:IsA("Team") then if teamConnections[child] then teamConnections[child]:disconnect() teamConnections[child] = nil end teamsChanged() end end) end game.Players.ChildAdded:connect( function(player) if player:IsA("Player") then addPlayerFunction(player) end end) game.Players.ChildRemoved:connect( function(player) if player:IsA("Player") then if removePlayerFunction then removePlayerFunction(player) end end end) rebuildBoard(script.Parent, determineBoardType()) game.GuiService.ShowLegacyPlayerList = false game.GuiService:AddKey("\t") local lastTime = nil game.GuiService.KeyPressed:connect( function(key) if key == "\t" then local modalCheck, isModal = pcall(function() return game.GuiService.IsModalDialog end) if modalCheck == false or (modalCheck and isModal == false) then local currentTime = time() if lastTime == nil or currentTime - lastTime > 0.4 then lastTime = currentTime toggleBigWindow() end end end end) delay(0, function() while true do wait(5) checkIfBoardChanged() end end) end if game.CoreGui.Version >= 2 then buildPlayerList() end end if not personalServerPlace then -- one more backup check local theBool = game.Workspace:FindFirstChild("PSVariable") if theBool and theBool:IsA("BoolValue") then personalServerPlace = true end end if personalServerPlace then addPersonalServerContext() setupBuildToolManagement() else local psVarCon = nil psVarCon = game.Workspace.ChildAdded:connect(function(child) if child:IsA("BoolValue") and child.Name == "PSVariable" then psVarCon:disconnect() personalServerPlace = true addPersonalServerContext() setupBuildToolManagement() end end) end ---------------------------------- Start Player Hover Code ---------------------------------------- if contextMenu3d then local inMenu = false function waitForProperty(instance, name) while not instance[name] do instance.Changed:wait() end end function makeNewActionButton() local button = Instance.new("TextButton") button.Name = "ActionButton" button.Style = Enum.ButtonStyle.RobloxButtonDefault button.BackgroundColor3 = Color3.new(0,0,0) button.BorderColor3 = Color3.new(1,0,0) button.BackgroundTransparency = 0.5 button.Size = UDim2.new(1,0,0,50) button.Text = "" button.Font = Enum.Font.ArialBold button.FontSize = Enum.FontSize.Size18 button.TextColor3 = Color3.new(1,1,1) button.ZIndex = 4 return button end function getContextElements(currentContextMenuPlayer) local elements = {} for i, contextElement in ipairs(contextMenuElements) do local element = contextElement local isVisible = false if contextElement.IsVisible then local success, visible = pcall(function() return contextElement.IsVisible(currentContextMenuPlayer) end) if success then isVisible = visible else print("Error in IsVisible call: " .. visible) end end if element.Type == "Button" then local button = makeNewActionButton() button.Name = "ContextButton" .. i button.Visible = isVisible button.Text = contextElement.Text button.MouseButton1Click:connect(function() if button.Active then local success, result = pcall(function() element.DoIt(currentContextMenuPlayer) end) end end) contextElement.Button = button contextElement.Element = button table.insert(elements,contextElement) end end return elements end function findContextElement(contextElements, button) for i = 1, #contextElements do if contextElements[i].Button == button then return contextElements[i] end end end function populateActions(scrollFrame, nullFrame, recalcFunction, otherPlayer) local elements = getContextElements(otherPlayer) for i = 1, #elements do if elements[i].Button.Visible then elements[i].Button.Parent = scrollFrame else elements[i].Button.Parent = nullFrame end local actionButtonCon actionButtonCon = elements[i].Button.MouseButton1Click:connect(function() actionButtonCon:disconnect() local nullFrameChildren = nullFrame:GetChildren() for j = 1, #nullFrameChildren do local contextElement = findContextElement(elements, nullFrameChildren[j]) pcall(function() nullFrameChildren[j].Visible = contextElement.IsVisible(otherPlayer) end) if nullFrameChildren[j].Visible then nullFrameChildren[j].Parent = scrollFrame end end local scrollFrameChildren = scrollFrame:GetChildren() for j = 1, #scrollFrameChildren do local contextElement = findContextElement(elements, scrollFrameChildren[j]) pcall(function() scrollFrameChildren[j].Visible = contextElement.IsVisible(otherPlayer) end) if not scrollFrameChildren[j].Visible then scrollFrameChildren[j].Parent = nullFrame end end elements[i].Button.Parent = nullFrame recalcFunction() end) end end function createContextMenu(otherPlayer) local frame = Instance.new("Frame") frame.Name = "ContextMenuFrame" frame.Style = Enum.FrameStyle.RobloxRound frame.Size = UDim2.new(0,300,0,400) frame.Position = UDim2.new(0.5,-150,0.5,-200) frame.ZIndex = 2 local scrollFrame, scrollUpButton, scrollDownButton, recalc, scrollBar = RbxGui.CreateScrollingFrame() scrollFrame.Name = "Actions" scrollFrame.BackgroundTransparency = 1 scrollFrame.Position = UDim2.new(0,0,0,25) scrollFrame.Size = UDim2.new(1,-20,1,-80) scrollFrame.ZIndex = 3 scrollFrame.Parent = frame local nullFrame = Instance.new("Frame") nullFrame.Name = "NullFrame" nullFrame.BackgroundTransparency = 1 nullFrame.Visible = false nullFrame.Parent = frame local scrollButtons = Instance.new("Frame") scrollButtons.Name = "ScrollButtons" scrollButtons.BackgroundTransparency = 1 scrollButtons.Position = UDim2.new(1,-17,0,25) scrollButtons.Size = UDim2.new(0,17,1,-80) scrollButtons.ZIndex = 3 scrollButtons.Parent = frame scrollUpButton.ZIndex = 3 scrollUpButton.Parent = scrollButtons scrollDownButton.Position = UDim2.new(0,0,1,-17) scrollDownButton.ZIndex = 3 scrollDownButton.Parent = scrollButtons scrollBar.Size = UDim2.new(1,0,1,-34) scrollBar.Position = UDim2.new(0,0,0,17) scrollBar.Parent = scrollButtons local playerImage = Instance.new("ImageLabel") playerImage.Name = "PlayerImage" playerImage.BackgroundTransparency = 1 playerImage.Image = "http://www.roblox.com/thumbs/avatar.ashx?userId=" .. tostring(otherPlayer.userId) .. "&x=352&y=352" playerImage.Position = UDim2.new(0.5,-150,0.5,-150) playerImage.Size = UDim2.new(0,300,0,300) playerImage.Parent = frame local playerName = Instance.new("TextLabel") playerName.Name = "PlayerName" playerName.BackgroundTransparency = 1 playerName.Font = Enum.Font.ArialBold playerName.FontSize = Enum.FontSize.Size24 playerName.Position = UDim2.new(0,-8,0,-6) playerName.Size = UDim2.new(1,16,0,24) playerName.Text = otherPlayer["Name"] playerName.TextColor3 = Color3.new(1,1,1) playerName.TextWrap = true playerName.ZIndex = 3 playerName.Parent = frame local doneButtonCon local doneButton = Instance.new("TextButton") doneButton.Name = "DoneButton" doneButton.Style = Enum.ButtonStyle.RobloxButton doneButton.Font = Enum.Font.ArialBold doneButton.FontSize = Enum.FontSize.Size36 doneButton.Position = UDim2.new(0.25,0,1,-50) doneButton.Size = UDim2.new(0.5,0,0,50) doneButton.Text = "Done" doneButton.TextColor3 = Color3.new(1,1,1) doneButton.ZIndex = 3 doneButton.Parent = frame doneButton.Modal = true doneButtonCon = doneButton.MouseButton1Click:connect(function() doneButtonCon:disconnect() inMenu = false game.GuiService:RemoveCenterDialog(frame) frame:remove() end) populateActions(scrollFrame, nullFrame, recalc, otherPlayer) recalc() return frame end function makeContextInvisible(menu) menu.Visible = false end function goToContextMenu(otherPlayer) local menu = createContextMenu(otherPlayer) game.GuiService:AddCenterDialog(menu, Enum.CenterDialogType.PlayerInitiatedDialog, --ShowFunction function() menu.Visible = true menu:TweenSize(UDim2.new(0,300,0,400),Enum.EasingDirection.Out,Enum.EasingStyle.Quart,0.5,true) end, --HideFunction function() menu:TweenSize(UDim2.new(0,0,0,0),Enum.EasingDirection.Out,Enum.EasingStyle.Quart,0.5,true,function() makeContextInvisible(menu) end) end) menu.Parent = game.CoreGui.RobloxGui inMenu = true end waitForProperty(game.Players, "LocalPlayer") local currSelectedPlayer = nil if game.Players.LocalPlayer["HoverOnPlayerChanged"] then game.Players.LocalPlayer.HoverOnPlayerChanged:connect(function(otherPlayer) if not inMenu then if otherPlayer and otherPlayer.userId < 0 then return end -- we don't want this for guests end wait(0.5) currSelectedPlayer = otherPlayer end) end if game.Players.LocalPlayer["MouseDownOnPlayer"] then game.Players.LocalPlayer.MouseDownOnPlayer:connect(function(otherPlayer) if currSelectedPlayer ~= otherPlayer then return end if not inMenu and otherPlayer.userId > 0 then goToContextMenu(otherPlayer) end end) end end ---------------------------------- End Player Hover Code ----------------------------------------