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 ----------------------------------------