This commit is contained in:
lx
2026-07-06 13:39:13 -04:00
commit 34bf440a8c
2694 changed files with 77580 additions and 0 deletions
@@ -0,0 +1,214 @@
-- This script is responsible for loading in all build tools for build mode
-- Script Globals
local buildTools = {}
local currentTools = {}
local BaseUrl = game:GetService("ContentProvider").BaseUrl:lower()
if BaseUrl:find("www.roblox.com") or BaseUrl:find("gametest1") then
DeleteToolID = 73089190
PartSelectionID = 73089166
CloneToolID = 73089204
RotateToolID = 73089214
ConfigToolID = 73089239
WiringToolID = 73089259
classicToolID = 58921588
elseif BaseUrl:find("gametest2") then
DeleteToolID = 70353317
PartSelectionID = 70353315
CloneToolID = 70353314
RotateToolID = 70353318
ConfigToolID = 70353319
WiringToolID = 70353320
classicToolID = 58921588
end
local player = nil
local backpack = nil
-- Basic Functions
local function waitForProperty(instance, name)
while not instance[name] do
instance.Changed:wait()
end
end
local function waitForChild(instance, name)
while not instance:FindFirstChild(name) do
instance.ChildAdded:wait()
end
end
waitForProperty(game:GetService("Players"),"LocalPlayer")
waitForProperty(game:GetService("Players").LocalPlayer,"userId")
-- we aren't in a true build mode session, don't give build tools and delete this script
if game:GetService("Players").LocalPlayer.userId < 1 then
script:Destroy()
return -- this is probably not necessesary, doing it just in case
end
-- Functions
function getLatestPlayer()
waitForProperty(game:GetService("Players"),"LocalPlayer")
player = game:GetService("Players").LocalPlayer
waitForChild(player,"Backpack")
backpack = player.Backpack
end
function waitForCharacterLoad()
local startTick = tick()
local playerLoaded = false
local success = pcall(function() playerLoaded = player.AppearanceDidLoad end) --TODO: remove pcall once this in client on prod
if not success then return false end
while not playerLoaded do
player.Changed:wait()
playerLoaded = player.AppearanceDidLoad
end
return true
end
function showBuildToolsTutorial()
local tutorialKey = "BuildToolsTutorial"
if UserSettings().GameSettings:GetTutorialState(tutorialKey) == true then return end --already have shown tutorial
local RbxGui = LoadLibrary("RbxGui")
local frame, showTutorial, dismissTutorial, gotoPage = RbxGui.CreateTutorial("Build", tutorialKey, false)
local firstPage = RbxGui.CreateImageTutorialPage(" ", "http://www.roblox.com/asset/?id=59162193", 359, 296, function() dismissTutorial() end, true)
RbxGui.AddTutorialPage(frame, firstPage)
frame.Parent = game:GetService("CoreGui"):FindFirstChild("RobloxGui")
game:GetService("GuiService"):AddCenterDialog(frame, Enum.CenterDialogType.UnsolicitedDialog,
--showFunction
function()
frame.Visible = true
showTutorial()
end,
--hideFunction
function()
frame.Visible = false
end
)
wait(1)
showTutorial()
end
function clearLoadout()
currentTools = {}
local backpackChildren = game:GetService("Players").LocalPlayer.Backpack:GetChildren()
for i = 1, #backpackChildren do
if backpackChildren[i]:IsA("Tool") or backpackChildren[i]:IsA("HopperBin") then
table.insert(currentTools,backpackChildren[i])
end
end
if game:GetService("Players").LocalPlayer["Character"] then
local characterChildren = game:GetService("Players").LocalPlayer.Character:GetChildren()
for i = 1, #characterChildren do
if characterChildren[i]:IsA("Tool") or characterChildren[i]:IsA("HopperBin") then
table.insert(currentTools,characterChildren[i])
end
end
end
for i = 1, #currentTools do
currentTools[i].Parent = nil
end
end
function giveToolsBack()
for i = 1, #currentTools do
currentTools[i].Parent = game:GetService("Players").LocalPlayer.Backpack
end
end
function backpackHasTool(tool)
local backpackChildren = backpack:GetChildren()
for i = 1, #backpackChildren do
if backpackChildren[i] == tool then
return true
end
end
return false
end
function getToolAssetID(assetID)
local newTool = game:GetService("InsertService"):LoadAsset(assetID)
local toolChildren = newTool:GetChildren()
for i = 1, #toolChildren do
if toolChildren[i]:IsA("Tool") then
return toolChildren[i]
end
end
return nil
end
-- remove legacy identifiers
-- todo: determine if we still need this
function removeBuildToolTag(tool)
if tool:FindFirstChild("RobloxBuildTool") then
tool.RobloxBuildTool:Destroy()
end
end
function giveAssetId(assetID,toolName)
local theTool = getToolAssetID(assetID,toolName)
if theTool and not backpackHasTool(theTool) then
removeBuildToolTag(theTool)
theTool.Parent = backpack
table.insert(buildTools,theTool)
end
end
function loadBuildTools()
giveAssetId(PartSelectionID)
giveAssetId(DeleteToolID)
giveAssetId(CloneToolID)
giveAssetId(RotateToolID)
giveAssetId(WiringToolID)
giveAssetId(ConfigToolID)
-- deprecated tools
giveAssetId(classicToolID)
end
function givePlayerBuildTools()
getLatestPlayer()
clearLoadout()
loadBuildTools()
giveToolsBack()
end
function takePlayerBuildTools()
for k,v in ipairs(buildTools) do
v.Parent = nil
end
buildTools = {}
end
-- Script start
getLatestPlayer()
waitForCharacterLoad()
givePlayerBuildTools()
-- If player dies, we make sure to give them build tools again
player.CharacterAdded:connect(function()
takePlayerBuildTools()
givePlayerBuildTools()
end)
showBuildToolsTutorial()
@@ -0,0 +1,206 @@
-- Personal Server Script
-----------------
--| Constants |--
-----------------
local CHANGES_PER_PLAYER = 100 -- Saving also occurs every time the number of edits reaches this number times the number of players
local SAVE_CHECK_INTERVAL = 1800 -- should be set in seconds, this is how long we wait to force a save, as long as at least one change has been made
local MIN_SAVE_TIME = 900 -- At least this many seconds will pass before saving again
-----------------
--| Variables |--
-----------------
local ContentProviderService = game:GetService('ContentProvider')
local PlayersService = game:GetService('Players')
local RunService = game:GetService("RunService")
local StartingPlayerRanks = {}
local RbxUtil = nil
local LastSaveTime = 0
local ChangeCount = 0
local TryingToSave = false
local NumberOfChangesBeforeSaveAbsolute = CHANGES_PER_PLAYER
local GameRunning = true
local WaitingToSave = false
local PlaceId = game.PlaceId
local Url = ContentProviderService.BaseUrl
local UrlBase = Url:match('^http://www\.(.-)/?$') -- Turns "http://www.gametest1.robloxlabs.com/" into "gametest1.robloxlabs.com"
local ApiProxyUrl = 'https://api.' .. UrlBase
local DataFarmProtocol = 'http'
local DataFarmUsesHttpsFlagExists, DataFarmUsesHttpsFlagValue = pcall(function () return settings():GetFFlag("DataFarmUsesHttps") end)
if DataFarmUsesHttpsFlagExists and DataFarmUsesHttpsFlagValue then
DataFarmProtocol = 'https'
end
local DataFarmUrl = DataFarmProtocol .. '://data.' .. UrlBase
-----------------
--| Functions |--
-----------------
function GetRbxUtil()
if not RbxUtil then
RbxUtil = LoadLibrary("RbxUtility")
end
return RbxUtil
end
-- Checks the full hierarchy of an instance for archivability
local function IsArchivable(instance)
if instance == workspace then
return true
elseif not instance.Archivable then
return false
else
return IsArchivable(instance.Parent)
end
end
local function UpdateSaveOnChangeAmount()
local players = PlayersService:GetPlayers()
NumberOfChangesBeforeSaveAbsolute = #players * CHANGES_PER_PLAYER
end
local function OnPlayerAdded(player)
if player:IsA('Player') then
local getRankUrl = ApiProxyUrl .. '/RoleSets/GetRoleSetForUser?placeId=' .. tostring(PlaceId) .. '&userId=' .. tostring(player.userId)
local serverRankTable = nil
pcall(function()
serverRankTable = GetRbxUtil().DecodeJSON(game:HttpGetAsync(getRankUrl))
end)
local playerRank = 0
if serverRankTable and type(serverRankTable) == 'table' then
for k, v in pairs(serverRankTable) do
if k == "data" then
if v["Rank"] then
playerRank = v["Rank"]
end
end
end
end
player.PersonalServerRank = playerRank
StartingPlayerRanks[player] = playerRank
UpdateSaveOnChangeAmount()
end
end
local function OnPlayerRemoved(player)
if player:IsA('Player') then
UpdateSaveOnChangeAmount()
if StartingPlayerRanks[player] then
local playerRank = player.PersonalServerRank
if StartingPlayerRanks[player] ~= playerRank then -- Don't need to make web call if rank is the same
local setRankUrl = ApiProxyUrl .. '/RoleSets/PrivilegedSetUserRoleSetRank?placeId=' .. tostring(PlaceId) .. '&userId=' .. tostring(player.userId) .. '&newRank=' .. tostring(playerRank)
ypcall(function() game:HttpPostAsync(setRankUrl, 'SetPersonalServerRank') end)
end
StartingPlayerRanks[player] = nil
end
end
end
local function DoSave()
if GameRunning then
ChangeCount = 0
LastSaveTime = tick()
game:ServerSave()
end
end
local function TrySave()
if not TryingToSave then
TryingToSave = true
local now = tick()
if now - LastSaveTime >= MIN_SAVE_TIME then
DoSave()
elseif not WaitingToSave then -- Save after cooldown
WaitingToSave = true
delay(LastSaveTime + MIN_SAVE_TIME - now, function()
DoSave()
WaitingToSave = false
end)
end
TryingToSave = false
end
end
-- Save based on number of edits to workspace
local function OnEdit(descendant)
if IsArchivable(descendant) then
ChangeCount = ChangeCount + 1
if ChangeCount >= NumberOfChangesBeforeSaveAbsolute then
TrySave()
end
end
end
-- Make sure we save every interval regardless of number of edits, so long as there is one
local function CheckForSaveOnInterval()
while true do
wait(SAVE_CHECK_INTERVAL)
if tick() - LastSaveTime >= SAVE_CHECK_INTERVAL and ChangeCount > 0 then
TrySave()
end
end
end
--------------------
--| Script Logic |--
--------------------
game:WaitForChild('Workspace')
pcall(function()
game.IsPersonalServer = true
if not workspace:FindFirstChild("PSVariable") then
local psVar = Instance.new("BoolValue")
psVar.Name = "PSVariable"
psVar.Archivable = false
psVar.Parent = workspace
end
end)
PlayersService.PlayerAdded:connect(OnPlayerAdded)
PlayersService.ChildRemoved:connect(OnPlayerRemoved)
for _, player in pairs(PlayersService:GetPlayers()) do
OnPlayerAdded(player)
end
local useSubdomainsFlagExists, useSubdomainsFlagValue = pcall(function () return settings():GetFFlag("UseNewSubdomainsInCoreScripts") end)
local saveUrlBase = Url
if(useSubdomainsFlagExists and useSubdomainsFlagValue and DataFarmUrl~=nil) then
saveUrlBase = DataFarmUrl
end
if saveUrlBase~=nil then
game:SetServerSaveUrl(saveUrlBase .. "/Data/AutoSave.ashx?assetId=" .. PlaceId)
end
if pcall(function()
game.Close:connect(
function()
GameRunning = false
game:ServerSave()
end)
end) == false then
print("!Error in Game.Close:connect")
end
RunService:Run()
game:GetService("Workspace").DescendantAdded:connect(OnEdit)
game:GetService("Workspace").DescendantRemoving:connect(OnEdit)
spawn(CheckForSaveOnInterval)
@@ -0,0 +1,268 @@
-- ContextActionTouch.lua
-- Copyright ROBLOX 2014, created by Ben Tkacheff
-- this script controls ui and firing of lua functions that are bound in ContextActionService for touch inputs
-- Essentially a user can bind a lua function to a key code, input type (mousebutton1 etc.) and this
-- Variables
local contextActionService = game:GetService("ContextActionService")
local userInputService = game:GetService("UserInputService")
local isTouchDevice = userInputService.TouchEnabled
local functionTable = {}
local buttonVector = {}
local buttonScreenGui = nil
local buttonFrame = nil
local ContextDownImage = "http://www.roblox.com/asset/?id=97166756"
local ContextUpImage = "http://www.roblox.com/asset/?id=97166444"
local oldTouches = {}
local buttonPositionTable = {
[1] = UDim2.new(0,123,0,70),
[2] = UDim2.new(0,30,0,60),
[3] = UDim2.new(0,180,0,160),
[4] = UDim2.new(0,85,0,-25),
[5] = UDim2.new(0,185,0,-25),
[6] = UDim2.new(0,185,0,260),
[7] = UDim2.new(0,216,0,65)
}
local maxButtons = #buttonPositionTable
-- Preload images
game:GetService("ContentProvider"):Preload(ContextDownImage)
game:GetService("ContentProvider"):Preload(ContextUpImage)
repeat wait() until game:GetService("Players").LocalPlayer
local localPlayer = game:GetService("Players").LocalPlayer
function createContextActionGui()
if not buttonScreenGui and isTouchDevice then
buttonScreenGui = Instance.new("ScreenGui")
buttonScreenGui.Name = "ContextActionGui"
buttonFrame = Instance.new("Frame")
buttonFrame.BackgroundTransparency = 1
buttonFrame.Size = UDim2.new(0.3,0,0.5,0)
buttonFrame.Position = UDim2.new(0.7,0,0.5,0)
buttonFrame.Name = "ContextButtonFrame"
buttonFrame.Parent = buttonScreenGui
buttonFrame.Visible = not userInputService.ModalEnabled
userInputService.Changed:connect(function(property)
if property == "ModalEnabled" then
buttonFrame.Visible = not userInputService.ModalEnabled
end
end)
end
end
-- functions
function setButtonSizeAndPosition(object)
local buttonSize = 55
local xOffset = 10
local yOffset = 95
-- todo: better way to determine mobile sized screens
local onSmallScreen = (game:GetService("CoreGui").RobloxGui.AbsoluteSize.X < 600)
if not onSmallScreen then
buttonSize = 85
xOffset = 40
end
object.Size = UDim2.new(0,buttonSize,0,buttonSize)
end
function contextButtonDown(button, inputObject, actionName)
if inputObject.UserInputType == Enum.UserInputType.Touch then
button.Image = ContextDownImage
contextActionService:CallFunction(actionName, Enum.UserInputState.Begin, inputObject)
end
end
function contextButtonMoved(button, inputObject, actionName)
if inputObject.UserInputType == Enum.UserInputType.Touch then
button.Image = ContextDownImage
contextActionService:CallFunction(actionName, Enum.UserInputState.Change, inputObject)
end
end
function contextButtonUp(button, inputObject, actionName)
button.Image = ContextUpImage
if inputObject.UserInputType == Enum.UserInputType.Touch and inputObject.UserInputState == Enum.UserInputState.End then
contextActionService:CallFunction(actionName, Enum.UserInputState.End, inputObject)
end
end
function isSmallScreenDevice()
return game:GetService("GuiService"):GetScreenResolution().y <= 320
end
function createNewButton(actionName, functionInfoTable)
local contextButton = Instance.new("ImageButton")
contextButton.Name = "ContextActionButton"
contextButton.BackgroundTransparency = 1
contextButton.Size = UDim2.new(0,90,0,90)
contextButton.Active = true
if isSmallScreenDevice() then
contextButton.Size = UDim2.new(0,70,0,70)
end
contextButton.Image = ContextUpImage
contextButton.Parent = buttonFrame
local currentButtonTouch = nil
userInputService.InputEnded:connect(function ( inputObject )
oldTouches[inputObject] = nil
end)
contextButton.InputBegan:connect(function(inputObject)
if oldTouches[inputObject] then return end
if inputObject.UserInputState == Enum.UserInputState.Begin and currentButtonTouch == nil then
currentButtonTouch = inputObject
contextButtonDown(contextButton, inputObject, actionName)
end
end)
contextButton.InputChanged:connect(function(inputObject)
if oldTouches[inputObject] then return end
if currentButtonTouch ~= inputObject then return end
contextButtonMoved(contextButton, inputObject, actionName)
end)
contextButton.InputEnded:connect(function(inputObject)
if oldTouches[inputObject] then return end
if currentButtonTouch ~= inputObject then return end
currentButtonTouch = nil
oldTouches[inputObject] = true
contextButtonUp(contextButton, inputObject, actionName)
end)
local actionIcon = Instance.new("ImageLabel")
actionIcon.Name = "ActionIcon"
actionIcon.Position = UDim2.new(0.175, 0, 0.175, 0)
actionIcon.Size = UDim2.new(0.65, 0, 0.65, 0)
actionIcon.BackgroundTransparency = 1
if functionInfoTable["image"] and type(functionInfoTable["image"]) == "string" then
actionIcon.Image = functionInfoTable["image"]
end
actionIcon.Parent = contextButton
local actionTitle = Instance.new("TextLabel")
actionTitle.Name = "ActionTitle"
actionTitle.Size = UDim2.new(1,0,1,0)
actionTitle.BackgroundTransparency = 1
actionTitle.Font = Enum.Font.SourceSansBold
actionTitle.TextColor3 = Color3.new(1,1,1)
actionTitle.TextStrokeTransparency = 0
actionTitle.FontSize = Enum.FontSize.Size18
actionTitle.TextWrapped = true
actionTitle.Text = ""
if functionInfoTable["title"] and type(functionInfoTable["title"]) == "string" then
actionTitle.Text = functionInfoTable["title"]
end
actionTitle.Parent = contextButton
return contextButton
end
function createButton( actionName, functionInfoTable )
local button = createNewButton(actionName, functionInfoTable)
local position = nil
for i = 1,#buttonVector do
if buttonVector[i] == "empty" then
position = i
break
end
end
if not position then
position = #buttonVector + 1
end
if position > maxButtons then
return -- todo: let user know we have too many buttons already?
end
buttonVector[position] = button
functionTable[actionName]["button"] = button
button.Position = buttonPositionTable[position]
button.Parent = buttonFrame
if buttonScreenGui and buttonScreenGui.Parent == nil then
buttonScreenGui.Parent = localPlayer.PlayerGui
end
end
function removeAction(actionName)
if not functionTable[actionName] then return end
local actionButton = functionTable[actionName]["button"]
if actionButton then
actionButton.Parent = nil
for i = 1,#buttonVector do
if buttonVector[i] == actionButton then
buttonVector[i] = "empty"
break
end
end
actionButton:Destroy()
end
functionTable[actionName] = nil
end
function addAction(actionName,createTouchButton,functionInfoTable)
if functionTable[actionName] then
removeAction(actionName)
end
functionTable[actionName] = {functionInfoTable}
if createTouchButton and isTouchDevice then
createContextActionGui()
createButton(actionName, functionInfoTable)
end
end
-- Connections
contextActionService.BoundActionChanged:connect( function(actionName, changeName, changeTable)
if functionTable[actionName] and changeTable then
local button = functionTable[actionName]["button"]
if button then
if changeName == "image" then
button.ActionIcon.Image = changeTable[changeName]
elseif changeName == "title" then
button.ActionTitle.Text = changeTable[changeName]
elseif changeName == "description" then
-- todo: add description to menu
elseif changeName == "position" then
button.Position = changeTable[changeName]
end
end
end
end)
contextActionService.BoundActionAdded:connect( function(actionName, createTouchButton, functionInfoTable)
addAction(actionName, createTouchButton, functionInfoTable)
end)
contextActionService.BoundActionRemoved:connect( function(actionName, functionInfoTable)
removeAction(actionName)
end)
contextActionService.GetActionButtonEvent:connect( function(actionName)
if functionTable[actionName] then
contextActionService:FireActionButtonFoundSignal(actionName, functionTable[actionName]["button"])
end
end)
-- make sure any bound data before we setup connections is handled
local boundActions = contextActionService:GetAllBoundActionInfo()
for actionName, actionData in pairs(boundActions) do
addAction(actionName,actionData["createTouchButton"],actionData)
end
@@ -0,0 +1,693 @@
--[[
Filename: GamepadMenu.lua
Written by: jeditkacheff
Version 1.1
Description: Controls the radial menu that appears when pressing menu button on gamepad
--]]
--[[ SERVICES ]]
local GuiService = game:GetService('GuiService')
local CoreGuiService = game:GetService('CoreGui')
local InputService = game:GetService('UserInputService')
local ContextActionService = game:GetService('ContextActionService')
local HttpService = game:GetService('HttpService')
local StarterGui = game:GetService('StarterGui')
local GuiRoot = CoreGuiService:WaitForChild('RobloxGui')
--[[ END OF SERVICES ]]
--[[ MODULES ]]
local tenFootInterface = require(GuiRoot.Modules.TenFootInterface)
local utility = require(GuiRoot.Modules.Settings.Utility)
local recordPage = require(GuiRoot.Modules.Settings.Pages.Record)
--[[ VARIABLES ]]
local gamepadSettingsFrame = nil
local isVisible = false
local smallScreen = utility:IsSmallTouchScreen()
local isTenFootInterface = tenFootInterface:IsEnabled()
local radialButtons = {}
local lastInputChangedCon = nil
local function getButtonForCoreGuiType(coreGuiType)
if coreGuiType == Enum.CoreGuiType.All then
return radialButtons
else
for button, table in pairs(radialButtons) do
if table["CoreGuiType"] == coreGuiType then
return button
end
end
end
return nil
end
local function getImagesForSlot(slot)
if slot == 1 then return "rbxasset://textures/ui/Settings/Radial/Top.png", "rbxasset://textures/ui/Settings/Radial/TopSelected.png",
"rbxasset://textures/ui/Settings/Radial/Menu.png",
UDim2.new(0.5,-26,0,18), UDim2.new(0,52,0,41),
UDim2.new(0,150,0,100), UDim2.new(0.5,-75,0,0)
elseif slot == 2 then return "rbxasset://textures/ui/Settings/Radial/TopRight.png", "rbxasset://textures/ui/Settings/Radial/TopRightSelected.png",
"rbxasset://textures/ui/Settings/Radial/PlayerList.png",
UDim2.new(1,-90,0,90), UDim2.new(0,52,0,52),
UDim2.new(0,108,0,150), UDim2.new(1,-110,0,50)
elseif slot == 3 then return "rbxasset://textures/ui/Settings/Radial/BottomRight.png", "rbxasset://textures/ui/Settings/Radial/BottomRightSelected.png",
"rbxasset://textures/ui/Settings/Radial/Alert.png",
UDim2.new(1,-85,1,-150), UDim2.new(0,42,0,58),
UDim2.new(0,120,0,150), UDim2.new(1,-120,1,-200)
elseif slot == 4 then return "rbxasset://textures/ui/Settings/Radial/Bottom.png", "rbxasset://textures/ui/Settings/Radial/BottomSelected.png",
"rbxasset://textures/ui/Settings/Radial/Leave.png",
UDim2.new(0.5,-20,1,-62), UDim2.new(0,55,0,46),
UDim2.new(0,150,0,100), UDim2.new(0.5,-75,1,-100)
elseif slot == 5 then return "rbxasset://textures/ui/Settings/Radial/BottomLeft.png", "rbxasset://textures/ui/Settings/Radial/BottomLeftSelected.png",
"rbxasset://textures/ui/Settings/Radial/Backpack.png",
UDim2.new(0,40,1,-150), UDim2.new(0,44,0,56),
UDim2.new(0,110,0,150), UDim2.new(0,0,0,205)
elseif slot == 6 then return "rbxasset://textures/ui/Settings/Radial/TopLeft.png", "rbxasset://textures/ui/Settings/Radial/TopLeftSelected.png",
"rbxasset://textures/ui/Settings/Radial/Chat.png",
UDim2.new(0,35,0,100), UDim2.new(0,56,0,53),
UDim2.new(0,110,0,150), UDim2.new(0,0,0,50)
end
return "", "", UDim2.new(0,0,0,0), UDim2.new(0,0,0,0)
end
local function setSelectedRadialButton(selectedObject)
for button, buttonTable in pairs(radialButtons) do
local isVisible = (button == selectedObject)
button:FindFirstChild("Selected").Visible = isVisible
button:FindFirstChild("RadialLabel").Visible = isVisible
end
end
local function activateSelectedRadialButton()
for button, buttonTable in pairs(radialButtons) do
if button:FindFirstChild("Selected").Visible then
buttonTable["Function"]()
return true
end
end
return false
end
local function setButtonEnabled(button, enabled)
if radialButtons[button]["Disabled"] == not enabled then return end
if button:FindFirstChild("Selected").Visible == true then
setSelectedRadialButton(nil)
end
if enabled then
button.Image = string.gsub(button.Image, "rbxasset://textures/ui/Settings/Radial/Empty", "rbxasset://textures/ui/Settings/Radial/")
button.ImageTransparency = 0
button.RadialIcon.ImageTransparency = 0
else
button.Image = string.gsub(button.Image, "rbxasset://textures/ui/Settings/Radial/", "rbxasset://textures/ui/Settings/Radial/Empty")
button.ImageTransparency = 0
button.RadialIcon.ImageTransparency = 1
end
radialButtons[button]["Disabled"] = not enabled
end
local function setButtonVisible(button, visible)
button.Visible = visible
if not visible then
setButtonEnabled(button, false)
end
end
local function enableVR()
local visibleButtons = {
Settings = true, LeaveGame = true,
PlayerList = false, Notifications = false,
Backpack = false, Chat = false
}
for button, _ in pairs(radialButtons) do
if visibleButtons[button.Name] ~= nil then
setButtonVisible(button, visibleButtons[button.Name])
end
end
end
local emptySelectedImageObject = utility:Create'ImageLabel'
{
BackgroundTransparency = 1,
Size = UDim2.new(1,0,1,0),
Image = ""
};
local function createRadialButton(name, text, slot, disabled, coreGuiType, activateFunc)
local slotImage, selectedSlotImage, slotIcon,
slotIconPosition, slotIconSize, mouseFrameSize, mouseFramePos = getImagesForSlot(slot)
local radialButton = utility:Create'ImageButton'
{
Name = name,
Position = UDim2.new(0,0,0,0),
Size = UDim2.new(1,0,1,0),
BackgroundTransparency = 1,
Image = slotImage,
ZIndex = 2,
SelectionImageObject = emptySelectedImageObject,
Parent = gamepadSettingsFrame
};
if disabled then
radialButton.Image = string.gsub(radialButton.Image, "rbxasset://textures/ui/Settings/Radial/", "rbxasset://textures/ui/Settings/Radial/Empty")
end
local selectedRadial = utility:Create'ImageLabel'
{
Name = "Selected",
Position = UDim2.new(0,0,0,0),
Size = UDim2.new(1,0,1,0),
BackgroundTransparency = 1,
Image = selectedSlotImage,
ZIndex = 2,
Visible = false,
Parent = radialButton
};
local radialIcon = utility:Create'ImageLabel'
{
Name = "RadialIcon",
Position = slotIconPosition,
Size = slotIconSize,
BackgroundTransparency = 1,
Image = slotIcon,
ZIndex = 3,
ImageTransparency = disabled and 1 or 0,
Parent = radialButton
};
local nameLabel = utility:Create'TextLabel'
{
Size = UDim2.new(0,220,0,50),
Position = UDim2.new(0.5, -110, 0.5, -25),
BackgroundTransparency = 1,
Text = text,
Font = Enum.Font.SourceSansBold,
FontSize = Enum.FontSize.Size14,
TextColor3 = Color3.new(1,1,1),
Name = "RadialLabel",
Visible = false,
ZIndex = 2,
Parent = radialButton
};
if not smallScreen then
nameLabel.FontSize = Enum.FontSize.Size36
nameLabel.Size = UDim2.new(nameLabel.Size.X.Scale, nameLabel.Size.X.Offset, nameLabel.Size.Y.Scale, nameLabel.Size.Y.Offset + 4)
end
local nameBackgroundImage = utility:Create'ImageLabel'
{
Name = text .. "BackgroundImage",
Size = UDim2.new(1,0,1,0),
Position = UDim2.new(0,0,0,2),
BackgroundTransparency = 1,
Image = "rbxasset://textures/ui/Settings/Radial/RadialLabel@2x.png",
ScaleType = Enum.ScaleType.Slice,
SliceCenter = Rect.new(24,4,130,42),
ZIndex = 2,
Parent = nameLabel
};
local mouseFrame = utility:Create'ImageButton'
{
Name = "MouseFrame",
Position = mouseFramePos,
Size = mouseFrameSize,
ZIndex = 3,
BackgroundTransparency = 1,
SelectionImageObject = emptySelectedImageObject,
Parent = radialButton
};
mouseFrame.MouseEnter:connect(function()
if not radialButtons[radialButton]["Disabled"] then
setSelectedRadialButton(radialButton)
end
end)
mouseFrame.MouseLeave:connect(function()
setSelectedRadialButton(nil)
end)
mouseFrame.MouseButton1Click:connect(function()
if selectedRadial.Visible then
activateFunc()
end
end)
radialButtons[radialButton] = {["Function"] = activateFunc, ["Disabled"] = disabled, ["CoreGuiType"] = coreGuiType}
return radialButton
end
local function createGamepadMenuGui()
gamepadSettingsFrame = utility:Create'Frame'
{
Name = "GamepadSettingsFrame",
Position = UDim2.new(0.5,-51,0.5,-51),
BackgroundTransparency = 1,
BorderSizePixel = 0,
Size = UDim2.new(0,102,0,102),
Visible = false,
Parent = GuiRoot
};
---------------------------------
-------- Settings Menu ----------
local settingsFunc = function()
toggleCoreGuiRadial(true)
local MenuModule = require(GuiRoot.Modules.Settings.SettingsHub)
MenuModule:SetVisibility(true, nil, nil, true)
end
local settingsRadial = createRadialButton("Settings", "Settings", 1, false, nil, settingsFunc)
settingsRadial.Parent = gamepadSettingsFrame
---------------------------------
-------- Player List ------------
local playerListFunc = function()
toggleCoreGuiRadial(true)
local PlayerListModule = require(GuiRoot.Modules.PlayerlistModule)
if not PlayerListModule:IsOpen() then
PlayerListModule:ToggleVisibility()
end
end
local playerListRadial = createRadialButton("PlayerList", "Player List", 2, not StarterGui:GetCoreGuiEnabled(Enum.CoreGuiType.PlayerList), Enum.CoreGuiType.PlayerList, playerListFunc)
playerListRadial.Parent = gamepadSettingsFrame
---------------------------------
-------- Notifications ----------
local gamepadNotifications = Instance.new("BindableEvent")
gamepadNotifications.Name = "GamepadNotifications"
gamepadNotifications.Parent = script
local notificationsFunc = function()
toggleCoreGuiRadial()
gamepadNotifications:Fire(true)
end
local notificationsRadial = createRadialButton("Notifications", "Notifications", 3, false, nil, notificationsFunc)
if isTenFootInterface then
setButtonEnabled(notificationsRadial, false)
end
notificationsRadial.Parent = gamepadSettingsFrame
---------------------------------
---------- Leave Game -----------
local leaveGameFunc = function()
toggleCoreGuiRadial(true)
local MenuModule = require(GuiRoot.Modules.Settings.SettingsHub)
MenuModule:SetVisibility(true, false, require(GuiRoot.Modules.Settings.Pages.LeaveGame), true)
end
local leaveGameRadial = createRadialButton("LeaveGame", "Leave Game", 4, false, nil, leaveGameFunc)
leaveGameRadial.Parent = gamepadSettingsFrame
---------------------------------
---------- Backpack -------------
local backpackFunc = function()
toggleCoreGuiRadial(true)
local BackpackModule = require(GuiRoot.Modules.BackpackScript)
BackpackModule:OpenClose()
end
local backpackRadial = createRadialButton("Backpack", "Backpack", 5, not StarterGui:GetCoreGuiEnabled(Enum.CoreGuiType.Backpack), Enum.CoreGuiType.Backpack, backpackFunc)
backpackRadial.Parent = gamepadSettingsFrame
---------------------------------
------------ Chat ---------------
local chatFunc = function()
toggleCoreGuiRadial()
local ChatModule = require(GuiRoot.Modules.Chat)
ChatModule:ToggleVisibility()
end
local chatRadial = createRadialButton("Chat", "Chat", 6, not StarterGui:GetCoreGuiEnabled(Enum.CoreGuiType.Chat), Enum.CoreGuiType.Chat, chatFunc)
if isTenFootInterface then
setButtonEnabled(chatRadial, false)
end
chatRadial.Parent = gamepadSettingsFrame
---------------------------------
--------- Close Button ----------
local closeHintImage = utility:Create'ImageLabel'
{
Name = "CloseHint",
Position = UDim2.new(1,10,1,10),
Size = UDim2.new(0,60,0,60),
BackgroundTransparency = 1,
Image = "rbxasset://textures/ui/Settings/Help/BButtonDark.png",
Parent = gamepadSettingsFrame
}
if isTenFootInterface then
closeHintImage.Image = "rbxasset://textures/ui/Settings/Help/BButtonDark@2x.png"
closeHintImage.Size = UDim2.new(0,90,0,90)
end
local closeHintText = utility:Create'TextLabel'
{
Name = "closeHintText",
Position = UDim2.new(1,10,0.5,-12),
Size = UDim2.new(0,43,0,24),
Font = Enum.Font.SourceSansBold,
FontSize = Enum.FontSize.Size24,
BackgroundTransparency = 1,
Text = "Back",
TextColor3 = Color3.new(1,1,1),
TextXAlignment = Enum.TextXAlignment.Left,
Parent = closeHintImage
}
if isTenFootInterface then
closeHintText.FontSize = Enum.FontSize.Size36
end
------------------------------------------
--------- Stop Recording Button ----------
--todo: enable this when recording is not a verb
--[[local stopRecordingImage = utility:Create'ImageLabel'
{
Name = "StopRecordingHint",
Position = UDim2.new(0,-100,1,10),
Size = UDim2.new(0,61,0,61),
BackgroundTransparency = 1,
Image = "rbxasset://textures/ui/Settings/Help/YButtonDark.png",
Visible = recordPage:IsRecording(),
Parent = gamepadSettingsFrame
}
local stopRecordingText = utility:Create'TextLabel'
{
Name = "stopRecordingHintText",
Position = UDim2.new(1,10,0.5,-12),
Size = UDim2.new(0,43,0,24),
Font = Enum.Font.SourceSansBold,
FontSize = Enum.FontSize.Size24,
BackgroundTransparency = 1,
Text = "Stop Recording",
TextColor3 = Color3.new(1,1,1),
TextXAlignment = Enum.TextXAlignment.Left,
Parent = stopRecordingImage
}
recordPage.RecordingChanged:connect(function(isRecording)
stopRecordingImage.Visible = isRecording
end)]]
GuiService:AddSelectionParent(HttpService:GenerateGUID(false), gamepadSettingsFrame)
gamepadSettingsFrame.Changed:connect(function(prop)
if prop == "Visible" then
if not gamepadSettingsFrame.Visible then
unbindAllRadialActions()
end
end
end)
if InputService.VREnabled then
enableVR()
end
end
local function isCoreGuiDisabled()
for _, enumItem in pairs(Enum.CoreGuiType:GetEnumItems()) do
if StarterGui:GetCoreGuiEnabled(enumItem) then
return false
end
end
return true
end
local function setupGamepadControls()
local freezeControllerActionName = "doNothingAction"
local radialSelectActionName = "RadialSelectAction"
local thumbstick2RadialActionName = "Thumbstick2RadialAction"
local radialCancelActionName = "RadialSelectCancel"
local radialAcceptActionName = "RadialSelectAccept"
local toggleMenuActionName = "RBXToggleMenuAction"
local noOpFunc = function() end
local doGamepadMenuButton = nil
function unbindAllRadialActions()
local success = pcall(function() GuiService.CoreGuiNavigationEnabled = true end)
if not success then
GuiService.GuiNavigationEnabled = true
end
ContextActionService:UnbindCoreAction(radialSelectActionName)
ContextActionService:UnbindCoreAction(radialCancelActionName)
ContextActionService:UnbindCoreAction(radialAcceptActionName)
ContextActionService:UnbindCoreAction(freezeControllerActionName)
ContextActionService:UnbindCoreAction(thumbstick2RadialActionName)
end
local radialButtonLayout = { PlayerList = {
Range = { Begin = 36,
End = 96
}
},
Notifications = {
Range = { Begin = 96,
End = 156
}
},
LeaveGame = {
Range = { Begin = 156,
End = 216
}
},
Backpack = {
Range = { Begin = 216,
End = 276
}
},
Chat = {
Range = { Begin = 276,
End = 336
}
},
Settings = {
Range = { Begin = 336,
End = 36
}
},
}
local function getSelectedObjectFromAngle(angle, depth)
local closest = nil
local closestDistance = 30 -- threshold of 30 for selecting the closest radial button
for radialKey, buttonLayout in pairs(radialButtonLayout) do
if radialButtons[gamepadSettingsFrame[radialKey]]["Disabled"] == false then
--Check for exact match
if buttonLayout.Range.Begin < buttonLayout.Range.End then
if angle > buttonLayout.Range.Begin and angle <= buttonLayout.Range.End then
return gamepadSettingsFrame[radialKey]
end
else
if angle > buttonLayout.Range.Begin or angle <= buttonLayout.Range.End then
return gamepadSettingsFrame[radialKey]
end
end
--Check if this is the closest button so far
local distanceBegin = math.min(math.abs((buttonLayout.Range.Begin + 360) - angle), math.abs(buttonLayout.Range.Begin - angle))
local distanceEnd = math.min(math.abs((buttonLayout.Range.End + 360) - angle), math.abs(buttonLayout.Range.End - angle))
local distance = math.min(distanceBegin, distanceEnd)
if distance < closestDistance then
closestDistance = distance
closest = gamepadSettingsFrame[radialKey]
end
end
end
return closest
end
local radialSelect = function(name, state, input)
local inputVector = Vector2.new(0,0)
if input.KeyCode == Enum.KeyCode.Thumbstick1 then
inputVector = Vector2.new(input.Position.x, input.Position.y)
end
local selectedObject = nil
if inputVector.magnitude > 0.8 then
local angle = math.atan2(inputVector.X, inputVector.Y) * 180 / math.pi
if angle < 0 then
angle = angle + 360
end
selectedObject = getSelectedObjectFromAngle(angle)
setSelectedRadialButton(selectedObject)
end
end
local radialSelectAccept = function(name, state, input)
if gamepadSettingsFrame.Visible and state == Enum.UserInputState.Begin then
activateSelectedRadialButton()
end
end
local radialSelectCancel = function(name, state, input)
if gamepadSettingsFrame.Visible and state == Enum.UserInputState.Begin then
toggleCoreGuiRadial()
end
end
function setVisibility()
local children = gamepadSettingsFrame:GetChildren()
for i = 1, #children do
if children[i]:FindFirstChild("RadialIcon") then
children[i].RadialIcon.Visible = isVisible
end
if children[i]:FindFirstChild("RadialLabel") and not isVisible then
children[i].RadialLabel.Visible = isVisible
end
end
end
function setOverrideMouseIconBehavior()
pcall(function()
if InputService:GetLastInputType() == Enum.UserInputType.Gamepad1 then
InputService.OverrideMouseIconBehavior = Enum.OverrideMouseIconBehavior.ForceHide
else
InputService.OverrideMouseIconBehavior = Enum.OverrideMouseIconBehavior.ForceShow
end
end)
end
function toggleCoreGuiRadial(goingToSettings)
isVisible = not gamepadSettingsFrame.Visible
setVisibility()
if isVisible then
setOverrideMouseIconBehavior()
pcall(function() lastInputChangedCon = InputService.LastInputTypeChanged:connect(setOverrideMouseIconBehavior) end)
gamepadSettingsFrame.Visible = isVisible
local settingsChildren = gamepadSettingsFrame:GetChildren()
for i = 1, #settingsChildren do
if settingsChildren[i]:IsA("GuiButton") then
utility:TweenProperty(settingsChildren[i], "ImageTransparency", 1, 0, 0.1, utility:GetEaseOutQuad(), nil)
end
end
gamepadSettingsFrame:TweenSizeAndPosition(UDim2.new(0,408,0,408), UDim2.new(0.5,-204,0.5,-204),
Enum.EasingDirection.Out, Enum.EasingStyle.Back, 0.18, true,
function()
setVisibility()
end)
else
if lastInputChangedCon ~= nil then
lastInputChangedCon:disconnect()
lastInputChangedCon = nil
end
pcall(function() InputService.OverrideMouseIconBehavior = Enum.OverrideMouseIconBehavior.None end)
local settingsChildren = gamepadSettingsFrame:GetChildren()
for i = 1, #settingsChildren do
if settingsChildren[i]:IsA("GuiButton") then
utility:TweenProperty(settingsChildren[i], "ImageTransparency", 0, 1, 0.1, utility:GetEaseOutQuad(), nil)
end
end
gamepadSettingsFrame:TweenSizeAndPosition(UDim2.new(0,102,0,102), UDim2.new(0.5,-51,0.5,-51),
Enum.EasingDirection.Out, Enum.EasingStyle.Sine, 0.1, true,
function()
if not goingToSettings and not isVisible then GuiService:SetMenuIsOpen(false) end
gamepadSettingsFrame.Visible = isVisible
end)
end
if isVisible then
setSelectedRadialButton(nil)
local success = pcall(function() GuiService.CoreGuiNavigationEnabled = false end)
if not success then
GuiService.GuiNavigationEnabled = false
end
GuiService:SetMenuIsOpen(true)
ContextActionService:BindCoreAction(freezeControllerActionName, noOpFunc, false, Enum.UserInputType.Gamepad1)
ContextActionService:BindCoreAction(radialAcceptActionName, radialSelectAccept, false, Enum.KeyCode.ButtonA)
ContextActionService:BindCoreAction(radialCancelActionName, radialSelectCancel, false, Enum.KeyCode.ButtonB)
ContextActionService:BindCoreAction(radialSelectActionName, radialSelect, false, Enum.KeyCode.Thumbstick1)
ContextActionService:BindCoreAction(thumbstick2RadialActionName, noOpFunc, false, Enum.KeyCode.Thumbstick2)
ContextActionService:BindCoreAction(toggleMenuActionName, doGamepadMenuButton, false, Enum.KeyCode.ButtonStart)
else
unbindAllRadialActions()
end
return gamepadSettingsFrame.Visible
end
doGamepadMenuButton = function(name, state, input)
if state ~= Enum.UserInputState.Begin then return end
if game.IsLoaded then
if not toggleCoreGuiRadial() then
unbindAllRadialActions()
end
end
end
if InputService:GetGamepadConnected(Enum.UserInputType.Gamepad1) then
createGamepadMenuGui()
else
InputService.GamepadConnected:connect(function(gamepadEnum)
if gamepadEnum == Enum.UserInputType.Gamepad1 then
createGamepadMenuGui()
end
end)
end
local function setRadialButtonEnabled(coreGuiType, enabled)
local returnValue = getButtonForCoreGuiType(coreGuiType)
if not returnValue then return end
local buttonsToDisable = {}
if type(returnValue) == "table" then
for button, buttonTable in pairs(returnValue) do
if buttonTable["CoreGuiType"] then
if isTenFootInterface and buttonTable["CoreGuiType"] == Enum.CoreGuiType.Chat then
else
buttonsToDisable[#buttonsToDisable + 1] = button
end
end
end
else
if isTenFootInterface and returnValue.Name == "Chat" then
else
buttonsToDisable[1] = returnValue
end
end
for i = 1, #buttonsToDisable do
local button = buttonsToDisable[i]
setButtonEnabled(button, enabled)
end
end
local loadedConnection
local function enableRadialMenu()
ContextActionService:BindCoreAction(toggleMenuActionName, doGamepadMenuButton, false, Enum.KeyCode.ButtonStart)
loadedConnection:disconnect()
end
loadedConnection = game.Players.PlayerAdded:connect(function(plr)
if game.Players.LocalPlayer and plr == game.Players.LocalPlayer then
enableRadialMenu()
end
end)
if game.Players.LocalPlayer then
enableRadialMenu()
end
StarterGui.CoreGuiChangedSignal:connect(setRadialButtonEnabled)
end
-- hook up gamepad stuff
setupGamepadControls()
@@ -0,0 +1,316 @@
--[[
This script controls the gui the player sees in regards to his or her health.
Can be turned with Game.StarterGui:SetCoreGuiEnabled(Enum.CoreGuiType.Health,false)
Copyright ROBLOX 2014. Written by Ben Tkacheff.
--]]
---------------------------------------------------------------------
-- Initialize/Variables
while not game do
wait(1/60)
end
while not game:GetService("Players") do
wait(1/60)
end
local useCoreHealthBar = false
local success = pcall(function() useCoreHealthBar = game:GetService("Players"):GetUseCoreScriptHealthBar() end)
if not success or not useCoreHealthBar then
return
end
local currentHumanoid = nil
local HealthGui = nil
local lastHealth = 100
local HealthPercentageForOverlay = 5
local maxBarTweenTime = 0.3
local greenColor = Color3.new(0.2, 1, 0.2)
local redColor = Color3.new(1, 0.2, 0.2)
local yellowColor = Color3.new(1, 1, 0.2)
local guiEnabled = false
local healthChangedConnection = nil
local humanoidDiedConnection = nil
local characterAddedConnection = nil
local greenBarImage = "rbxasset://textures/ui/Health-BKG-Center.png"
local greenBarImageLeft = "rbxasset://textures/ui/Health-BKG-Left-Cap.png"
local greenBarImageRight = "rbxasset://textures/ui/Health-BKG-Right-Cap.png"
local hurtOverlayImage = "http://www.roblox.com/asset/?id=34854607"
game:GetService("ContentProvider"):Preload(greenBarImage)
game:GetService("ContentProvider"):Preload(hurtOverlayImage)
while not game:GetService("Players").LocalPlayer do
wait(1/60)
end
---------------------------------------------------------------------
-- Functions
local capHeight = 15
local capWidth = 7
function CreateGui()
if HealthGui and #HealthGui:GetChildren() > 0 then
HealthGui.Parent = game:GetService("CoreGui").RobloxGui
return
end
local hurtOverlay = Instance.new("ImageLabel")
hurtOverlay.Name = "HurtOverlay"
hurtOverlay.BackgroundTransparency = 1
hurtOverlay.Image = hurtOverlayImage
hurtOverlay.Position = UDim2.new(-10,0,-10,0)
hurtOverlay.Size = UDim2.new(20,0,20,0)
hurtOverlay.Visible = false
hurtOverlay.Parent = HealthGui
local healthFrame = Instance.new("Frame")
healthFrame.Name = "HealthFrame"
healthFrame.BackgroundTransparency = 1
healthFrame.BackgroundColor3 = Color3.new(1,1,1)
healthFrame.BorderColor3 = Color3.new(0,0,0)
healthFrame.BorderSizePixel = 0
healthFrame.Position = UDim2.new(0.5,-85,1,-20)
healthFrame.Size = UDim2.new(0,170,0,capHeight)
healthFrame.Parent = HealthGui
local healthBarBackCenter = Instance.new("ImageLabel")
healthBarBackCenter.Name = "healthBarBackCenter"
healthBarBackCenter.BackgroundTransparency = 1
healthBarBackCenter.Image = greenBarImage
healthBarBackCenter.Size = UDim2.new(1,-capWidth*2,1,0)
healthBarBackCenter.Position = UDim2.new(0,capWidth,0,0)
healthBarBackCenter.Parent = healthFrame
healthBarBackCenter.ImageColor3 = Color3.new(1,1,1)
local healthBarBackLeft = Instance.new("ImageLabel")
healthBarBackLeft.Name = "healthBarBackLeft"
healthBarBackLeft.BackgroundTransparency = 1
healthBarBackLeft.Image = greenBarImageLeft
healthBarBackLeft.Size = UDim2.new(0,capWidth,1,0)
healthBarBackLeft.Position = UDim2.new(0,0,0,0)
healthBarBackLeft.Parent = healthFrame
healthBarBackLeft.ImageColor3 = Color3.new(1,1,1)
local healthBarBackRight = Instance.new("ImageLabel")
healthBarBackRight.Name = "healthBarBackRight"
healthBarBackRight.BackgroundTransparency = 1
healthBarBackRight.Image = greenBarImageRight
healthBarBackRight.Size = UDim2.new(0,capWidth,1,0)
healthBarBackRight.Position = UDim2.new(1,-capWidth,0,0)
healthBarBackRight.Parent = healthFrame
healthBarBackRight.ImageColor3 = Color3.new(1,1,1)
local healthBar = Instance.new("Frame")
healthBar.Name = "HealthBar"
healthBar.BackgroundTransparency = 1
healthBar.BackgroundColor3 = Color3.new(1,1,1)
healthBar.BorderColor3 = Color3.new(0,0,0)
healthBar.BorderSizePixel = 0
healthBar.ClipsDescendants = true
healthBar.Position = UDim2.new(0, 0, 0, 0)
healthBar.Size = UDim2.new(1,0,1,0)
healthBar.Parent = healthFrame
local healthBarCenter = Instance.new("ImageLabel")
healthBarCenter.Name = "healthBarCenter"
healthBarCenter.BackgroundTransparency = 1
healthBarCenter.Image = greenBarImage
healthBarCenter.Size = UDim2.new(1,-capWidth*2,1,0)
healthBarCenter.Position = UDim2.new(0,capWidth,0,0)
healthBarCenter.Parent = healthBar
healthBarCenter.ImageColor3 = greenColor
local healthBarLeft = Instance.new("ImageLabel")
healthBarLeft.Name = "healthBarLeft"
healthBarLeft.BackgroundTransparency = 1
healthBarLeft.Image = greenBarImageLeft
healthBarLeft.Size = UDim2.new(0,capWidth,1,0)
healthBarLeft.Position = UDim2.new(0,0,0,0)
healthBarLeft.Parent = healthBar
healthBarLeft.ImageColor3 = greenColor
local healthBarRight = Instance.new("ImageLabel")
healthBarRight.Name = "healthBarRight"
healthBarRight.BackgroundTransparency = 1
healthBarRight.Image = greenBarImageRight
healthBarRight.Size = UDim2.new(0,capWidth,1,0)
healthBarRight.Position = UDim2.new(1,-capWidth,0,0)
healthBarRight.Parent = healthBar
healthBarRight.ImageColor3 = greenColor
HealthGui.Parent = game:GetService("CoreGui").RobloxGui
end
function UpdateGui(health)
if not HealthGui then return end
local healthFrame = HealthGui:FindFirstChild("HealthFrame")
if not healthFrame then return end
local healthBar = healthFrame:FindFirstChild("HealthBar")
if not healthBar then return end
-- If more than 1/4 health, bar = green. Else, bar = red.
local percentHealth = (health/currentHumanoid.MaxHealth)
if percentHealth ~= percentHealth then
percentHealth = 1
healthBar.healthBarCenter.ImageColor3 = yellowColor
healthBar.healthBarRight.ImageColor3 = yellowColor
healthBar.healthBarLeft.ImageColor3 = yellowColor
elseif percentHealth > 0.25 then
healthBar.healthBarCenter.ImageColor3 = greenColor
healthBar.healthBarRight.ImageColor3 = greenColor
healthBar.healthBarLeft.ImageColor3 = greenColor
else
healthBar.healthBarCenter.ImageColor3 = redColor
healthBar.healthBarRight.ImageColor3 = redColor
healthBar.healthBarLeft.ImageColor3 = redColor
end
local width = (health / currentHumanoid.MaxHealth)
width = math.max(math.min(width,1),0) -- make sure width is between 0 and 1
if width ~= width then width = 1 end
local healthDelta = lastHealth - health
lastHealth = health
local percentOfTotalHealth = math.abs(healthDelta/currentHumanoid.MaxHealth)
percentOfTotalHealth = math.max(math.min(percentOfTotalHealth,1),0) -- make sure percentOfTotalHealth is between 0 and 1
if percentOfTotalHealth ~= percentOfTotalHealth then percentOfTotalHealth = 1 end
local newHealthSize = UDim2.new(width,0,1,0)
healthBar.Size = newHealthSize
local sizeX = healthBar.AbsoluteSize.X
if sizeX < capWidth then
healthBar.healthBarCenter.Visible = false
healthBar.healthBarRight.Visible = false
elseif sizeX < (2*capWidth + 1) then
healthBar.healthBarCenter.Visible = true
healthBar.healthBarCenter.Size = UDim2.new(0,sizeX - capWidth,1,0)
healthBar.healthBarRight.Visible = false
else
healthBar.healthBarCenter.Visible = true
healthBar.healthBarCenter.Size = UDim2.new(1,-capWidth*2,1,0)
healthBar.healthBarRight.Visible = true
end
local thresholdForHurtOverlay = currentHumanoid.MaxHealth * (HealthPercentageForOverlay/100)
if healthDelta >= thresholdForHurtOverlay and guiEnabled then
AnimateHurtOverlay()
end
end
function AnimateHurtOverlay()
if not HealthGui then return end
local overlay = HealthGui:FindFirstChild("HurtOverlay")
if not overlay then return end
local newSize = UDim2.new(20, 0, 20, 0)
local newPos = UDim2.new(-10, 0, -10, 0)
if overlay:IsDescendantOf(game) then
-- stop any tweens on overlay
overlay:TweenSizeAndPosition(newSize,newPos,Enum.EasingDirection.Out,Enum.EasingStyle.Linear,0,true,function()
-- show the gui
overlay.Size = UDim2.new(1,0,1,0)
overlay.Position = UDim2.new(0,0,0,0)
overlay.Visible = true
-- now tween the hide
if overlay:IsDescendantOf(game) then
overlay:TweenSizeAndPosition(newSize,newPos,Enum.EasingDirection.Out,Enum.EasingStyle.Quad,10,false,function()
overlay.Visible = false
end)
else
overlay.Size = newSize
overlay.Position = newPos
end
end)
else
overlay.Size = newSize
overlay.Position = newPos
end
end
function humanoidDied()
UpdateGui(0)
end
function disconnectPlayerConnections()
if characterAddedConnection then characterAddedConnection:disconnect() end
if humanoidDiedConnection then humanoidDiedConnection:disconnect() end
if healthChangedConnection then healthChangedConnection:disconnect() end
end
function newPlayerCharacter()
disconnectPlayerConnections()
startGui()
end
function startGui()
characterAddedConnection = game:GetService("Players").LocalPlayer.CharacterAdded:connect(newPlayerCharacter)
local character = game:GetService("Players").LocalPlayer.Character
if not character then
return
end
currentHumanoid = character:WaitForChild("Humanoid")
if not currentHumanoid then
return
end
if not game:GetService("StarterGui"):GetCoreGuiEnabled(Enum.CoreGuiType.Health) then
return
end
healthChangedConnection = currentHumanoid.HealthChanged:connect(UpdateGui)
humanoidDiedConnection = currentHumanoid.Died:connect(humanoidDied)
UpdateGui(currentHumanoid.Health)
CreateGui()
end
---------------------------------------------------------------------
-- Start Script
HealthGui = Instance.new("Frame")
HealthGui.Name = "HealthGui"
HealthGui.BackgroundTransparency = 1
HealthGui.Size = UDim2.new(1,0,1,0)
game:GetService("StarterGui").CoreGuiChangedSignal:connect(function(coreGuiType,enabled)
if coreGuiType == Enum.CoreGuiType.Health or coreGuiType == Enum.CoreGuiType.All then
if guiEnabled and not enabled then
if HealthGui then
HealthGui.Parent = nil
end
disconnectPlayerConnections()
elseif not guiEnabled and enabled then
startGui()
end
guiEnabled = enabled
end
end)
if game:GetService("StarterGui"):GetCoreGuiEnabled(Enum.CoreGuiType.Health) then
guiEnabled = true
startGui()
end
@@ -0,0 +1,729 @@
local PURPOSE_DATA = {
[Enum.DialogPurpose.Quest] = {"rbxasset://textures/DialogQuest.png", Vector2.new(10, 34)},
[Enum.DialogPurpose.Help] = {"rbxasset://textures/DialogHelp.png", Vector2.new(20, 35)},
[Enum.DialogPurpose.Shop] = {"rbxasset://textures/ui/DialogShop.png", Vector2.new(22, 43)},
}
local TEXT_HEIGHT = 24 -- Pixel height of one row
local FONT_SIZE = Enum.FontSize.Size24
local BAR_THICKNESS = 6
local STYLE_PADDING = 17
local CHOICE_PADDING = 6 * 2 -- (Added to vertical height)
local PROMPT_SIZE = Vector2.new(80, 90)
local FRAME_WIDTH = 350
local WIDTH_BONUS = (STYLE_PADDING * 2) - BAR_THICKNESS
local XPOS_OFFSET = -(STYLE_PADDING - BAR_THICKNESS)
local contextActionService = game:GetService("ContextActionService")
local guiService = game:GetService("GuiService")
local YPOS_OFFSET = -math.floor(STYLE_PADDING / 2)
local usingGamepad = false
function setUsingGamepad(input, processed)
if input.UserInputType == Enum.UserInputType.Gamepad1 or input.UserInputType == Enum.UserInputType.Gamepad2 or
input.UserInputType == Enum.UserInputType.Gamepad3 or input.UserInputType == Enum.UserInputType.Gamepad4 then
usingGamepad = true
else
usingGamepad = false
end
end
game:GetService("UserInputService").InputBegan:connect(setUsingGamepad)
game:GetService("UserInputService").InputChanged:connect(setUsingGamepad)
function waitForProperty(instance, name)
while not instance[name] do
instance.Changed:wait()
end
end
function waitForChild(instance, name)
while not instance:FindFirstChild(name) do
instance.ChildAdded:wait()
end
end
local filteringEnabledFixFlagSuccess, filteringEnabledFixFlagValue = pcall(function() return settings():GetFFlag("FilteringEnabledDialogFix") end)
local filterEnabledFixActive = (filteringEnabledFixFlagSuccess and filteringEnabledFixFlagValue)
local goodbyeChoiceActiveFlagSuccess, goodbyeChoiceActiveFlagValue = pcall(function() return settings():GetFFlag("GoodbyeChoiceActiveProperty") end)
local goodbyeChoiceActiveFlag = (goodbyeChoiceActiveFlagSuccess and goodbyeChoiceActiveFlagValue)
local mainFrame
local choices = {}
local lastChoice
local choiceMap = {}
local currentConversationDialog
local currentConversationPartner
local currentAbortDialogScript
local coroutineMap = {}
local currentDialogTimeoutCoroutine = nil
local tooFarAwayMessage = "You are too far away to chat!"
local tooFarAwaySize = 300
local characterWanderedOffMessage = "Chat ended because you walked away"
local characterWanderedOffSize = 350
local conversationTimedOut = "Chat ended because you didn't reply"
local conversationTimedOutSize = 350
local RobloxReplicatedStorage = game:GetService('RobloxReplicatedStorage')
local setDialogInUseEvent = RobloxReplicatedStorage:WaitForChild("SetDialogInUse")
local player
local screenGui
local chatNotificationGui
local messageDialog
local timeoutScript
local reenableDialogScript
local dialogMap = {}
local dialogConnections = {}
local touchControlGui = nil
local gui = nil
waitForChild(game,"CoreGui")
waitForChild(game:GetService("CoreGui"),"RobloxGui")
game:GetService("CoreGui").RobloxGui:WaitForChild("Modules"):WaitForChild("TenFootInterface")
local isTenFootInterface = require(game:GetService("CoreGui").RobloxGui.Modules.TenFootInterface):IsEnabled()
local utility = require(game:GetService("CoreGui").RobloxGui.Modules.Settings.Utility)
local isSmallTouchScreen = utility:IsSmallTouchScreen()
if isTenFootInterface then
FONT_SIZE = Enum.FontSize.Size36
TEXT_HEIGHT = 36
FRAME_WIDTH = 500
elseif isSmallTouchScreen then
FONT_SIZE = Enum.FontSize.Size14
TEXT_HEIGHT = 14
FRAME_WIDTH = 250
end
if game:GetService("CoreGui").RobloxGui:FindFirstChild("ControlFrame") then
gui = game:GetService("CoreGui").RobloxGui.ControlFrame
else
gui = game:GetService("CoreGui").RobloxGui
end
local touchEnabled = game:GetService("UserInputService").TouchEnabled
function currentTone()
if currentConversationDialog then
return currentConversationDialog.Tone
else
return Enum.DialogTone.Neutral
end
end
function createChatNotificationGui()
chatNotificationGui = Instance.new("BillboardGui")
chatNotificationGui.Name = "ChatNotificationGui"
chatNotificationGui.ExtentsOffset = Vector3.new(0,1,0)
chatNotificationGui.Size = UDim2.new(PROMPT_SIZE.X / 31.5, 0, PROMPT_SIZE.Y / 31.5, 0)
chatNotificationGui.SizeOffset = Vector2.new(0,0)
chatNotificationGui.StudsOffset = Vector3.new(0, 3.7, 0)
chatNotificationGui.Enabled = true
chatNotificationGui.RobloxLocked = true
chatNotificationGui.Active = true
local button = Instance.new("ImageButton")
button.Name = "Background"
button.Active = false
button.BackgroundTransparency = 1
button.Position = UDim2.new(0, 0, 0, 0)
button.Size = UDim2.new(1, 0, 1, 0)
button.Image = ""
button.RobloxLocked = true
button.Parent = chatNotificationGui
local icon = Instance.new("ImageLabel")
icon.Name = "Icon"
icon.Position = UDim2.new(0, 0, 0, 0)
icon.Size = UDim2.new(1, 0, 1, 0)
icon.Image = ""
icon.BackgroundTransparency = 1
icon.RobloxLocked = true
icon.Parent = button
local activationButton = Instance.new("ImageLabel")
activationButton.Name = "ActivationButton"
activationButton.Position = UDim2.new(-0.3, 0, -0.4, 0)
activationButton.Size = UDim2.new(.8, 0, .8*(PROMPT_SIZE.X/PROMPT_SIZE.Y), 0)
activationButton.Image = "rbxasset://textures/ui/Settings/Help/XButtonDark.png"
activationButton.BackgroundTransparency = 1
activationButton.Visible = false
activationButton.RobloxLocked = true
activationButton.Parent = button
end
function getChatColor(tone)
if tone == Enum.DialogTone.Neutral then
return Enum.ChatColor.Blue
elseif tone == Enum.DialogTone.Friendly then
return Enum.ChatColor.Green
elseif tone == Enum.DialogTone.Enemy then
return Enum.ChatColor.Red
end
end
function styleChoices()
for _, obj in pairs(choices) do
obj.BackgroundTransparency = 1
end
lastChoice.BackgroundTransparency = 1
end
function styleMainFrame(tone)
if tone == Enum.DialogTone.Neutral then
mainFrame.Style = Enum.FrameStyle.ChatBlue
elseif tone == Enum.DialogTone.Friendly then
mainFrame.Style = Enum.FrameStyle.ChatGreen
elseif tone == Enum.DialogTone.Enemy then
mainFrame.Style = Enum.FrameStyle.ChatRed
end
styleChoices()
end
function setChatNotificationTone(gui, purpose, tone)
if tone == Enum.DialogTone.Neutral then
gui.Background.Image = "rbxasset://textures/ui/chatBubble_blue_notify_bkg.png"
elseif tone == Enum.DialogTone.Friendly then
gui.Background.Image = "rbxasset://textures/ui/chatBubble_green_notify_bkg.png"
elseif tone == Enum.DialogTone.Enemy then
gui.Background.Image = "rbxasset://textures/ui/chatBubble_red_notify_bkg.png"
end
local newIcon, size = unpack(PURPOSE_DATA[purpose])
local relativeSize = size / PROMPT_SIZE
gui.Background.Icon.Size = UDim2.new(relativeSize.X, 0, relativeSize.Y, 0)
gui.Background.Icon.Position = UDim2.new(0.5 - (relativeSize.X / 2), 0, 0.4 - (relativeSize.Y / 2), 0)
gui.Background.Icon.Image = newIcon
end
function createMessageDialog()
messageDialog = Instance.new("Frame");
messageDialog.Name = "DialogScriptMessage"
messageDialog.Style = Enum.FrameStyle.Custom
messageDialog.BackgroundTransparency = 0.5
messageDialog.BackgroundColor3 = Color3.new(31/255, 31/255, 31/255)
messageDialog.Visible = false
local text = Instance.new("TextLabel")
text.Name = "Text"
text.Position = UDim2.new(0,0,0,-1)
text.Size = UDim2.new(1,0,1,0)
text.FontSize = Enum.FontSize.Size14
text.BackgroundTransparency = 1
text.TextColor3 = Color3.new(1,1,1)
text.RobloxLocked = true
text.Parent = messageDialog
end
function showMessage(msg, size)
messageDialog.Text.Text = msg
messageDialog.Size = UDim2.new(0,size,0,40)
messageDialog.Position = UDim2.new(0.5, -size/2, 0.5, -40)
messageDialog.Visible = true
wait(2)
messageDialog.Visible = false
end
function variableDelay(str)
local length = math.min(string.len(str), 100)
wait(0.75 + ((length/75) * 1.5))
end
function resetColor(frame)
frame.BackgroundTransparency = 1
end
function wanderDialog()
mainFrame.Visible = false
endDialog()
showMessage(characterWanderedOffMessage, characterWanderedOffSize)
end
function timeoutDialog()
mainFrame.Visible = false
endDialog()
showMessage(conversationTimedOut, conversationTimedOutSize)
end
function normalEndDialog()
endDialog()
end
function endDialog()
if filterEnabledFixActive then
if currentDialogTimeoutCoroutine then
coroutineMap[currentDialogTimeoutCoroutine] = false
currentDialogTimeoutCoroutine = nil
end
else
if currentAbortDialogScript then
currentAbortDialogScript:Destroy()
currentAbortDialogScript = nil
end
end
local dialog = currentConversationDialog
currentConversationDialog = nil
if dialog and dialog.InUse then
if filterEnabledFixActive then
spawn(function()
wait(5)
setDialogInUseEvent:FireServer(dialog, false)
end)
else
local reenableScript = reenableDialogScript:Clone()
reenableScript.Archivable = false
reenableScript.Disabled = false
reenableScript.Parent = dialog
end
end
for dialog, gui in pairs(dialogMap) do
if dialog and gui then
gui.Enabled = not dialog.InUse
end
end
contextActionService:UnbindCoreAction("Nothing")
currentConversationPartner = nil
if touchControlGui then
touchControlGui.Visible = true
end
end
function sanitizeMessage(msg)
if string.len(msg) == 0 then
return "..."
else
return msg
end
end
function selectChoice(choice)
renewKillswitch(currentConversationDialog)
--First hide the Gui
mainFrame.Visible = false
if choice == lastChoice then
game:GetService("Chat"):Chat(game:GetService("Players").LocalPlayer.Character, lastChoice.UserPrompt.Text, getChatColor(currentTone()))
normalEndDialog()
else
local dialogChoice = choiceMap[choice]
game:GetService("Chat"):Chat(game:GetService("Players").LocalPlayer.Character, sanitizeMessage(dialogChoice.UserDialog), getChatColor(currentTone()))
wait(1)
currentConversationDialog:SignalDialogChoiceSelected(player, dialogChoice)
game:GetService("Chat"):Chat(currentConversationPartner, sanitizeMessage(dialogChoice.ResponseDialog), getChatColor(currentTone()))
variableDelay(dialogChoice.ResponseDialog)
presentDialogChoices(currentConversationPartner, dialogChoice:GetChildren(), dialogChoice)
end
end
function newChoice()
local dummyFrame = Instance.new("Frame")
dummyFrame.Visible = false
local frame = Instance.new("TextButton")
frame.BackgroundColor3 = Color3.new(227/255, 227/255, 227/255)
frame.BackgroundTransparency = 1
frame.AutoButtonColor = false
frame.BorderSizePixel = 0
frame.Text = ""
frame.MouseEnter:connect(function() frame.BackgroundTransparency = 0 end)
frame.MouseLeave:connect(function() frame.BackgroundTransparency = 1 end)
frame.SelectionImageObject = dummyFrame
frame.MouseButton1Click:connect(function() selectChoice(frame) end)
frame.RobloxLocked = true
local prompt = Instance.new("TextLabel")
prompt.Name = "UserPrompt"
prompt.BackgroundTransparency = 1
prompt.Font = Enum.Font.SourceSans
prompt.FontSize = FONT_SIZE
prompt.Position = UDim2.new(0, 40, 0, 0)
prompt.Size = UDim2.new(1, -32-40, 1, 0)
prompt.TextXAlignment = Enum.TextXAlignment.Left
prompt.TextYAlignment = Enum.TextYAlignment.Center
prompt.TextWrap = true
prompt.RobloxLocked = true
prompt.Parent = frame
local selectionButton = Instance.new("ImageLabel")
selectionButton.Name = "RBXchatDialogSelectionButton"
selectionButton.Position = UDim2.new(0, 0, 0.5, -33/2)
selectionButton.Size = UDim2.new(0, 33, 0, 33)
selectionButton.Image = "rbxasset://textures/ui/Settings/Help/AButtonLightSmall.png"
selectionButton.BackgroundTransparency = 1
selectionButton.Visible = false
selectionButton.RobloxLocked = true
selectionButton.Parent = frame
return frame
end
function initialize(parent)
choices[1] = newChoice()
choices[2] = newChoice()
choices[3] = newChoice()
choices[4] = newChoice()
lastChoice = newChoice()
lastChoice.UserPrompt.Text = "Goodbye!"
lastChoice.Size = UDim2.new(1, WIDTH_BONUS, 0, TEXT_HEIGHT + CHOICE_PADDING)
mainFrame = Instance.new("Frame")
mainFrame.Name = "UserDialogArea"
mainFrame.Size = UDim2.new(0, FRAME_WIDTH, 0, 200)
mainFrame.Style = Enum.FrameStyle.ChatBlue
mainFrame.Visible = false
for n, obj in pairs(choices) do
obj.RobloxLocked = true
obj.Parent = mainFrame
end
lastChoice.RobloxLocked = true
lastChoice.Parent = mainFrame
mainFrame.RobloxLocked = true
mainFrame.Parent = parent
end
function presentDialogChoices(talkingPart, dialogChoices, parentDialog)
if not currentConversationDialog then
return
end
currentConversationPartner = talkingPart
sortedDialogChoices = {}
for n, obj in pairs(dialogChoices) do
if obj:IsA("DialogChoice") then
table.insert(sortedDialogChoices, obj)
end
end
table.sort(sortedDialogChoices, function(a,b) return a.Name < b.Name end)
if #sortedDialogChoices == 0 then
normalEndDialog()
return
end
local pos = 1
local yPosition = 0
choiceMap = {}
for n, obj in pairs(choices) do
obj.Visible = false
end
for n, obj in pairs(sortedDialogChoices) do
if pos <= #choices then
--3 lines is the maximum, set it to that temporarily
choices[pos].Size = UDim2.new(1, WIDTH_BONUS, 0, TEXT_HEIGHT * 3)
choices[pos].UserPrompt.Text = obj.UserDialog
local height = (math.ceil(choices[pos].UserPrompt.TextBounds.Y / TEXT_HEIGHT) * TEXT_HEIGHT) + CHOICE_PADDING
choices[pos].Position = UDim2.new(0, XPOS_OFFSET, 0, YPOS_OFFSET + yPosition)
choices[pos].Size = UDim2.new(1, WIDTH_BONUS, 0, height)
choices[pos].Visible = true
choiceMap[choices[pos]] = obj
yPosition = yPosition + height + 1 -- The +1 makes highlights not overlap
pos = pos + 1
end
end
lastChoice.Size = UDim2.new(1, WIDTH_BONUS, 0, TEXT_HEIGHT * 3)
lastChoice.UserPrompt.Text = parentDialog.GoodbyeDialog == "" and "Goodbye!" or parentDialog.GoodbyeDialog
local height = (math.ceil(lastChoice.UserPrompt.TextBounds.Y / TEXT_HEIGHT) * TEXT_HEIGHT) + CHOICE_PADDING
lastChoice.Size = UDim2.new(1, WIDTH_BONUS, 0, height)
lastChoice.Position = UDim2.new(0, XPOS_OFFSET, 0, YPOS_OFFSET + yPosition)
lastChoice.Visible = true
if goodbyeChoiceActiveFlag and not parentDialog.GoodbyeChoiceActive then
lastChoice.Visible = false
mainFrame.Size = UDim2.new(0, FRAME_WIDTH, 0, yPosition + (STYLE_PADDING * 2) + (YPOS_OFFSET * 2))
else
mainFrame.Size = UDim2.new(0, FRAME_WIDTH, 0, yPosition + lastChoice.AbsoluteSize.Y + (STYLE_PADDING * 2) + (YPOS_OFFSET * 2))
end
mainFrame.Position = UDim2.new(0,20,1.0, -mainFrame.Size.Y.Offset-20)
if isSmallTouchScreen then
local touchScreenGui = game.Players.LocalPlayer.PlayerGui:FindFirstChild("TouchGui")
if touchScreenGui then
touchControlGui = touchScreenGui:FindFirstChild("TouchControlFrame")
if touchControlGui then
touchControlGui.Visible = false
end
end
mainFrame.Position = UDim2.new(0,10,1.0, -mainFrame.Size.Y.Offset)
end
styleMainFrame(currentTone())
mainFrame.Visible = true
if usingGamepad then
Game:GetService("GuiService").SelectedCoreObject = choices[1]
end
end
function doDialog(dialog)
if dialog.InUse then
return
else
dialog.InUse = true
if filterEnabledFixActive then
setDialogInUseEvent:FireServer(dialog, true)
end
end
currentConversationDialog = dialog
game:GetService("Chat"):Chat(dialog.Parent, dialog.InitialPrompt, getChatColor(dialog.Tone))
variableDelay(dialog.InitialPrompt)
presentDialogChoices(dialog.Parent, dialog:GetChildren(), dialog)
end
function renewKillswitch(dialog)
if filterEnabledFixActive then
if currentDialogTimeoutCoroutine then
coroutineMap[currentDialogTimeoutCoroutine] = false
currentDialogTimeoutCoroutine = nil
end
else
if currentAbortDialogScript then
currentAbortDialogScript:Destroy()
currentAbortDialogScript = nil
end
end
if filterEnabledFixActive then
currentDialogTimeoutCoroutine = coroutine.create(function(thisCoroutine)
wait(15)
if thisCoroutine ~= nil then
if coroutineMap[thisCoroutine] == nil then
setDialogInUseEvent:FireServer(dialog, false)
end
coroutineMap[thisCoroutine] = nil
end
end)
coroutine.resume(currentDialogTimeoutCoroutine, currentDialogTimeoutCoroutine)
else
currentAbortDialogScript = timeoutScript:Clone()
currentAbortDialogScript.Archivable = false
currentAbortDialogScript.Disabled = false
currentAbortDialogScript.Parent = dialog
end
end
function checkForLeaveArea()
while currentConversationDialog do
if currentConversationDialog.Parent and (player:DistanceFromCharacter(currentConversationDialog.Parent.Position) >= currentConversationDialog.ConversationDistance) then
wanderDialog()
end
wait(1)
end
end
function startDialog(dialog)
if dialog.Parent and dialog.Parent:IsA("BasePart") then
if player:DistanceFromCharacter(dialog.Parent.Position) >= dialog.ConversationDistance then
showMessage(tooFarAwayMessage, tooFarAwaySize)
return
end
for dialog, gui in pairs(dialogMap) do
if dialog and gui then
gui.Enabled = false
end
end
contextActionService:BindCoreAction("Nothing", function() end, false, Enum.UserInputType.Gamepad1, Enum.UserInputType.Gamepad2, Enum.UserInputType.Gamepad3, Enum.UserInputType.Gamepad4)
renewKillswitch(dialog)
delay(1, checkForLeaveArea)
doDialog(dialog)
end
end
function removeDialog(dialog)
if dialogMap[dialog] then
dialogMap[dialog]:Destroy()
dialogMap[dialog] = nil
end
if dialogConnections[dialog] then
dialogConnections[dialog]:disconnect()
dialogConnections[dialog] = nil
end
end
function addDialog(dialog)
if dialog.Parent then
if dialog.Parent:IsA("BasePart") then
local chatGui = chatNotificationGui:clone()
chatGui.Enabled = not dialog.InUse
chatGui.Adornee = dialog.Parent
chatGui.RobloxLocked = true
chatGui.Parent = game:GetService("CoreGui")
chatGui.Background.MouseButton1Click:connect(function() startDialog(dialog) end)
setChatNotificationTone(chatGui, dialog.Purpose, dialog.Tone)
dialogMap[dialog] = chatGui
dialogConnections[dialog] = dialog.Changed:connect(function(prop)
if prop == "Parent" and dialog.Parent then
--This handles the reparenting case, seperate from removal case
removeDialog(dialog)
addDialog(dialog)
elseif prop == "InUse" then
chatGui.Enabled = not currentConversationDialog and not dialog.InUse
if dialog == currentConversationDialog then
timeoutDialog()
end
elseif prop == "Tone" or prop == "Purpose" then
setChatNotificationTone(chatGui, dialog.Purpose, dialog.Tone)
end
end)
else -- still need to listen to parent changes even if current parent is not a BasePart
dialogConnections[dialog] = dialog.Changed:connect(function(prop)
if prop == "Parent" and dialog.Parent then
--This handles the reparenting case, seperate from removal case
removeDialog(dialog)
addDialog(dialog)
end
end)
end
end
end
function fetchScripts()
local model = game:GetService("InsertService"):LoadAsset(39226062)
if type(model) == "string" then -- load failed, lets try again
wait(0.1)
model = game:GetService("InsertService"):LoadAsset(39226062)
end
if type(model) == "string" then -- not going to work, lets bail
return
end
waitForChild(model,"TimeoutScript")
timeoutScript = model.TimeoutScript
waitForChild(model,"ReenableDialogScript")
reenableDialogScript = model.ReenableDialogScript
end
function onLoad()
waitForProperty(game:GetService("Players"), "LocalPlayer")
player = game:GetService("Players").LocalPlayer
waitForProperty(player, "Character")
--print("Creating Guis")
createChatNotificationGui()
--print("Creating MessageDialog")
createMessageDialog()
messageDialog.RobloxLocked = true
messageDialog.Parent = gui
if not filterEnabledFixActive then
fetchScripts()
end
--print("Waiting for BottomLeftControl")
waitForChild(gui, "BottomLeftControl")
--print("Initializing Frame")
local frame = Instance.new("Frame")
frame.Name = "DialogFrame"
frame.Position = UDim2.new(0,0,0,0)
frame.Size = UDim2.new(0,0,0,0)
frame.BackgroundTransparency = 1
frame.RobloxLocked = true
game:GetService("GuiService"):AddSelectionParent("RBXDialogGroup", frame)
if (touchEnabled and not isSmallTouchScreen) then
frame.Position = UDim2.new(0,20,0.5,0)
frame.Size = UDim2.new(0.25,0,0.1,0)
frame.Parent = gui
elseif isSmallTouchScreen then
frame.Position = UDim2.new(0,0,.9,-10)
frame.Size = UDim2.new(0.25,0,0.1,0)
frame.Parent = gui
else
frame.Parent = gui.BottomLeftControl
end
initialize(frame)
--print("Adding Dialogs")
game:GetService("CollectionService").ItemAdded:connect(function(obj) if obj:IsA("Dialog") then addDialog(obj) end end)
game:GetService("CollectionService").ItemRemoved:connect(function(obj) if obj:IsA("Dialog") then removeDialog(obj) end end)
for i, obj in pairs(game:GetService("CollectionService"):GetCollection("Dialog")) do
if obj:IsA("Dialog") then
addDialog(obj)
end
end
end
local lastClosestDialog = nil
local getClosestDialogToPosition = guiService.GetClosestDialogToPosition
game:GetService("RunService").Heartbeat:connect(function()
local closestDistance = math.huge
local closestDialog = nil
if usingGamepad == true then
if game.Players.LocalPlayer and game.Players.LocalPlayer.Character and game.Players.LocalPlayer.Character:FindFirstChild("HumanoidRootPart") then
local characterPosition = game.Players.LocalPlayer.Character:FindFirstChild("HumanoidRootPart").Position
closestDialog = getClosestDialogToPosition(guiService, characterPosition)
end
end
if closestDialog ~= lastClosestDialog then
if dialogMap[lastClosestDialog] then
dialogMap[lastClosestDialog].Background.ActivationButton.Visible = false
end
lastClosestDialog = closestDialog
contextActionService:UnbindCoreAction("StartDialogAction")
if closestDialog ~= nil then
contextActionService:BindCoreAction("StartDialogAction",
function(actionName, userInputState, inputObject)
if userInputState == Enum.UserInputState.Begin then
if closestDialog and closestDialog.Parent then
startDialog(closestDialog)
end
end
end,
false,
Enum.KeyCode.ButtonX)
if dialogMap[closestDialog] then
dialogMap[closestDialog].Background.ActivationButton.Visible = true
end
end
end
end)
local lastSelectedChoice = nil
guiService.Changed:connect(function(property)
if property == "SelectedCoreObject" then
if lastSelectedChoice and lastSelectedChoice:FindFirstChild("RBXchatDialogSelectionButton") then
lastSelectedChoice:FindFirstChild("RBXchatDialogSelectionButton").Visible = false
lastSelectedChoice.BackgroundTransparency = 1
end
lastSelectedChoice = guiService.SelectedCoreObject
if lastSelectedChoice and lastSelectedChoice:FindFirstChild("RBXchatDialogSelectionButton") then
lastSelectedChoice:FindFirstChild("RBXchatDialogSelectionButton").Visible = true
lastSelectedChoice.BackgroundTransparency = 0
end
end
end)
onLoad()
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,160 @@
--[[
// Filename: VehicleHud.lua
// Version 1.0
// Written by: jmargh
// Description: Implementation of the VehicleSeat HUD
// TODO:
Once this is live and stable, move to PlayerScripts as module
]]
local RunService = game:GetService('RunService')
local Players = game:GetService('Players')
while not Players.LocalPlayer do
wait()
end
local LocalPlayer = Players.LocalPlayer
local RobloxGui = script.Parent
local CurrentVehicleSeat = nil
local VehicleSeatRenderCn = nil
local VehicleSeatHUDChangedCn = nil
local RobloxGui = game:GetService("CoreGui"):WaitForChild("RobloxGui")
RobloxGui:WaitForChild("Modules"):WaitForChild("TenFootInterface")
local isTenFootInterface = require(RobloxGui.Modules.TenFootInterface):IsEnabled()
--[[ Images ]]--
local VEHICLE_HUD_BG = 'rbxasset://textures/ui/Vehicle/SpeedBarBKG.png'
local SPEED_BAR_EMPTY = 'rbxasset://textures/ui/Vehicle/SpeedBarEmpty.png'
local SPEED_BAR = 'rbxasset://textures/ui/Vehicle/SpeedBar.png'
--[[ Constants ]]--
local BOTTOM_OFFSET = (isTenFootInterface and 100 or 70)
--[[ Gui Creation ]]--
local function createImageLabel(name, size, position, image, parent)
local imageLabel = Instance.new('ImageLabel')
imageLabel.Name = name
imageLabel.Size = size
imageLabel.Position = position
imageLabel.BackgroundTransparency = 1
imageLabel.Image = image
imageLabel.Parent = parent
return imageLabel
end
local function createTextLabel(name, alignment, text, parent)
local textLabel = Instance.new('TextLabel')
textLabel.Name = name
textLabel.Size = UDim2.new(1, -4, 0, (isTenFootInterface and 50 or 20))
textLabel.Position = UDim2.new(0, 2, 0, (isTenFootInterface and -50 or -20))
textLabel.BackgroundTransparency = 1
textLabel.TextXAlignment = alignment
textLabel.Font = Enum.Font.SourceSans
textLabel.FontSize = (isTenFootInterface and Enum.FontSize.Size48 or Enum.FontSize.Size18)
textLabel.TextColor3 = Color3.new(1, 1, 1)
textLabel.TextStrokeTransparency = 0.5
textLabel.TextStrokeColor3 = Color3.new(49/255, 49/255, 49/255)
textLabel.Text = text
textLabel.Parent = parent
return textLabel
end
local VehicleHudFrame = Instance.new('Frame')
VehicleHudFrame.Name = "VehicleHudFrame"
VehicleHudFrame.Size = UDim2.new(0, (isTenFootInterface and 316 or 158), 0, (isTenFootInterface and 50 or 14))
VehicleHudFrame.Position = UDim2.new(0.5, -(VehicleHudFrame.Size.X.Offset/2), 1, -BOTTOM_OFFSET - VehicleHudFrame.Size.Y.Offset)
VehicleHudFrame.BackgroundTransparency = 1
VehicleHudFrame.Visible = false
VehicleHudFrame.Parent = RobloxGui
local speedBarClippingFrame = Instance.new("Frame")
speedBarClippingFrame.Name = "SpeedBarClippingFrame"
speedBarClippingFrame.Size = UDim2.new(0, 0, 0, (isTenFootInterface and 24 or 4))
speedBarClippingFrame.Position = UDim2.new(0.5, (isTenFootInterface and -142 or -71), 0.5, (isTenFootInterface and -13 or -2))
speedBarClippingFrame.BackgroundTransparency = 1
speedBarClippingFrame.ClipsDescendants = true
speedBarClippingFrame.Parent = VehicleHudFrame
local HudBG = createImageLabel("HudBG", UDim2.new(1, 0, 1, 0), UDim2.new(0, 0, 0, 1), VEHICLE_HUD_BG, VehicleHudFrame)
local SpeedBG = createImageLabel("SpeedBG", UDim2.new(0, (isTenFootInterface and 284 or 142), 0, (isTenFootInterface and 24 or 4)), UDim2.new(0.5, (isTenFootInterface and -142 or -71), 0.5, (isTenFootInterface and -13 or -2)), SPEED_BAR_EMPTY, VehicleHudFrame)
local SpeedBarImage = createImageLabel("SpeedBarImage", UDim2.new(0, (isTenFootInterface and 284 or 142), 1, 0), UDim2.new(0, 0, 0, 0), SPEED_BAR, speedBarClippingFrame)
SpeedBarImage.ZIndex = 2
local SpeedLabel = createTextLabel("SpeedLabel", Enum.TextXAlignment.Left, "Speed", VehicleHudFrame)
local SpeedText = createTextLabel("SpeedText", Enum.TextXAlignment.Right, "0", VehicleHudFrame)
--[[ Local Functions ]]--
local function getHumanoid()
local character = LocalPlayer and LocalPlayer.Character
if character then
for _,child in pairs(character:GetChildren()) do
if child:IsA('Humanoid') then
return child
end
end
end
end
local function onRenderStepped()
if CurrentVehicleSeat then
local speed = CurrentVehicleSeat.Velocity.magnitude
SpeedText.Text = tostring(math.min(math.floor(speed), 9999))
local drawSize = math.floor((speed / CurrentVehicleSeat.MaxSpeed) * SpeedBG.Size.X.Offset)
drawSize = math.min(drawSize, SpeedBG.Size.X.Offset)
speedBarClippingFrame.Size = UDim2.new(0, drawSize, 0, (isTenFootInterface and 24 or 4))
end
end
local function onVehicleSeatChanged(property)
if property == "HeadsUpDisplay" then
VehicleHudFrame.Visible = not VehicleHudFrame.Visible
end
end
local function onSeated(active, currentSeatPart)
if active then
-- TODO: Clean up when new API is live for a while
if currentSeatPart and currentSeatPart:IsA('VehicleSeat') then
CurrentVehicleSeat = currentSeatPart
else
local camSubject = workspace.CurrentCamera.CameraSubject
if camSubject and camSubject:IsA('VehicleSeat') then
CurrentVehicleSeat = camSubject
end
end
if CurrentVehicleSeat then
VehicleHudFrame.Visible = CurrentVehicleSeat.HeadsUpDisplay
VehicleSeatRenderCn = RunService.RenderStepped:connect(onRenderStepped)
VehicleSeatHUDChangedCn = CurrentVehicleSeat.Changed:connect(onVehicleSeatChanged)
end
else
if CurrentVehicleSeat then
VehicleHudFrame.Visible = false
CurrentVehicleSeat = nil
if VehicleSeatRenderCn then
VehicleSeatRenderCn:disconnect()
VehicleSeatRenderCn = nil
end
if VehicleSeatHUDChangedCn then
VehicleSeatHUDChangedCn:disconnect()
VehicleSeatHUDChangedCn = nil
end
end
end
end
local function connectSeated()
local humanoid = getHumanoid()
while not humanoid do
wait()
humanoid = getHumanoid()
end
humanoid.Seated:connect(onSeated)
end
connectSeated()
LocalPlayer.CharacterAdded:connect(function(character)
connectSeated()
end)
File diff suppressed because it is too large Load Diff
+777
View File
@@ -0,0 +1,777 @@
-- Creates the generic "ROBLOX" loading screen on startup
-- Written by ArceusInator & Ben Tkacheff, 2014
--
-- Constants
local PLACEID = game.PlaceId
local MPS = game:GetService('MarketplaceService')
local UIS = game:GetService('UserInputService')
local guiService = game:GetService("GuiService")
local ContextActionService = game:GetService('ContextActionService')
local RobloxGui = game:GetService("CoreGui"):WaitForChild("RobloxGui")
local startTime = tick()
local COLORS = {
BLACK = Color3.new(0, 0, 0),
BACKGROUND_COLOR = Color3.new(45/255, 45/255, 45/255),
WHITE = Color3.new(1, 1, 1),
ERROR = Color3.new(253/255,68/255,72/255)
}
local function getViewportSize()
while not game.Workspace.CurrentCamera do
game.Workspace.Changed:wait()
end
while game.Workspace.CurrentCamera.ViewportSize == Vector2.new(0,0) do
game.Workspace.CurrentCamera.Changed:wait()
end
return game.Workspace.CurrentCamera.ViewportSize
end
--
-- Variables
local GameAssetInfo -- loaded by InfoProvider:LoadAssets()
local currScreenGui, renderSteppedConnection = nil, nil
local destroyingBackground, destroyedLoadingGui, hasReplicatedFirstElements = false, false, false
local backgroundImageTransparency = 0
local isMobile = (UIS.TouchEnabled == true and UIS.MouseEnabled == false and getViewportSize().Y <= 500)
local isTenFootInterface = guiService:IsTenFootInterface()
local platform = UIS:GetPlatform()
local function IsConvertMyPlaceNameInXboxAppEnabled()
if UIS:GetPlatform() == Enum.Platform.XBoxOne then
local success, flagValue = pcall(function() return settings():GetFFlag("ConvertMyPlaceNameInXboxApp") end)
return (success and flagValue == true)
end
return false
end
--
-- Utility functions
local create = function(className, defaultParent)
return function(propertyList)
local object = Instance.new(className)
for index, value in next, propertyList do
if type(index) == 'string' then
object[index] = value
else
if type(value) == 'function' then
value(object)
elseif type(value) == 'userdata' then
value.Parent = object
end
end
end
if object.Parent == nil then
object.Parent = defaultParent
end
return object
end
end
--
-- Create objects
local MainGui = {}
local InfoProvider = {}
function ExtractGeneratedUsername(gameName)
local tempUsername = string.match(gameName, "^([0-9a-fA-F]+)'s Place$")
if tempUsername and #tempUsername == 32 then
return tempUsername
end
end
-- Fix places that have been made with incorrect temporary usernames
function GetFilteredGameName(gameName, creatorName)
if gameName and type(gameName) == 'string' then
local tempUsername = ExtractGeneratedUsername(gameName)
if tempUsername then
local newGameName = string.gsub(gameName, tempUsername, creatorName, 1)
if newGameName then
return newGameName
end
end
end
return gameName
end
function InfoProvider:GetGameName()
if GameAssetInfo ~= nil then
if IsConvertMyPlaceNameInXboxAppEnabled() then
return GetFilteredGameName(GameAssetInfo.Name, self:GetCreatorName())
else
return GameAssetInfo.Name
end
else
return ''
end
end
function InfoProvider:GetCreatorName()
if GameAssetInfo ~= nil then
return GameAssetInfo.Creator.Name
else
return ''
end
end
function InfoProvider:LoadAssets()
spawn(function()
if PLACEID <= 0 then
while game.PlaceId <= 0 do
wait()
end
PLACEID = game.PlaceId
end
-- load game asset info
coroutine.resume(coroutine.create(function()
local success, result = pcall(function()
GameAssetInfo = MPS:GetProductInfo(PLACEID)
end)
if not success then
print("LoadingScript->InfoProvider:LoadAssets:", result)
end
end))
end)
end
function MainGui:tileBackgroundTexture(frameToFill)
if not frameToFill then return end
frameToFill:ClearAllChildren()
if backgroundImageTransparency < 1 then
local backgroundTextureSize = Vector2.new(512, 512)
for i = 0, math.ceil(frameToFill.AbsoluteSize.X/backgroundTextureSize.X) do
for j = 0, math.ceil(frameToFill.AbsoluteSize.Y/backgroundTextureSize.Y) do
create 'ImageLabel' {
Name = 'BackgroundTextureImage',
BackgroundTransparency = 1,
ImageTransparency = backgroundImageTransparency,
Image = 'rbxasset://textures/loading/darkLoadingTexture.png',
Position = UDim2.new(0, i*backgroundTextureSize.X, 0, j*backgroundTextureSize.Y),
Size = UDim2.new(0, backgroundTextureSize.X, 0, backgroundTextureSize.Y),
ZIndex = 1,
Parent = frameToFill
}
end
end
end
end
-- create a cancel binding for console to be able to cancel anytime while loading
local function createTenfootCancelGui()
local cancelLabel = create'ImageLabel'
{
Name = "CancelLabel";
Size = UDim2.new(0, 83, 0, 83);
Position = UDim2.new(1, -32 - 83, 0, 32);
BackgroundTransparency = 1;
Image = 'rbxasset://textures/ui/Shell/ButtonIcons/BButton.png';
}
local cancelText = create'TextLabel'
{
Name = "CancelText";
Size = UDim2.new(0, 0, 0, 0);
Position = UDim2.new(1, -131, 0, 64);
BackgroundTransparency = 1;
FontSize = Enum.FontSize.Size36;
TextXAlignment = Enum.TextXAlignment.Right;
TextColor3 = COLORS.WHITE;
Text = "Cancel";
}
-- bind cancel action
local platformService = nil
pcall(function()
platformService = game:GetService('PlatformService')
end)
if platformService then
if not game:GetService("ReplicatedFirst"):IsFinishedReplicating() then
local seenBButtonBegin = false
ContextActionService:BindCoreAction("CancelGameLoad",
function(actionName, inputState, inputObject)
if inputState == Enum.UserInputState.Begin then
seenBButtonBegin = true
elseif inputState == Enum.UserInputState.End and seenBButtonBegin then
cancelLabel:Destroy()
cancelText.Text = "Canceling..."
cancelText.Position = UDim2.new(1, -32, 0, 64)
ContextActionService:UnbindCoreAction('CancelGameLoad')
platformService:RequestGameShutdown()
end
end,
false,
Enum.KeyCode.ButtonB)
end
end
while cancelLabel.Parent == nil do
if currScreenGui then
local blackFrame = currScreenGui:FindFirstChild('BlackFrame')
if blackFrame then
cancelLabel.Parent = blackFrame
cancelText.Parent = blackFrame
break
end
end
wait()
end
end
--
-- Declare member functions
function MainGui:GenerateMain()
local screenGui = create 'ScreenGui' {
Name = 'RobloxLoadingGui'
}
--
-- create descendant frames
local mainBackgroundContainer = create 'Frame' {
Name = 'BlackFrame',
BackgroundColor3 = COLORS.BACKGROUND_COLOR,
BackgroundTransparency = 0,
Size = UDim2.new(1, 0, 1, 0),
Position = UDim2.new(0, 0, 0, 0),
Active = true,
Parent = screenGui,
}
local closeButton = create 'ImageButton' {
Name = 'CloseButton',
Image = 'rbxasset://textures/loading/cancelButton.png',
ImageTransparency = 1,
BackgroundTransparency = 1,
Position = UDim2.new(1, -37, 0, 5),
Size = UDim2.new(0, 32, 0, 32),
Active = false,
ZIndex = 10,
Parent = mainBackgroundContainer,
}
local graphicsFrame = create 'Frame' {
Name = 'GraphicsFrame',
BorderSizePixel = 0,
BackgroundTransparency = 1,
Position = UDim2.new(1, (isMobile == true and -75 or (isTenFootInterface and -245 or -225)), 1, (isMobile == true and -75 or (isTenFootInterface and -185 or -165))),
Size = UDim2.new(0, (isMobile == true and 70 or (isTenFootInterface and 140 or 120)), 0, (isMobile == true and 70 or (isTenFootInterface and 140 or 120))),
ZIndex = 2,
Parent = mainBackgroundContainer,
}
local loadingImage = create 'ImageLabel' {
Name = 'LoadingImage',
BackgroundTransparency = 1,
Image = 'rbxasset://textures/loading/loadingCircle.png',
Position = UDim2.new(0, 0, 0, 0),
Size = UDim2.new(1, 0, 1, 0),
ZIndex = 2,
Parent = graphicsFrame,
}
local loadingText = create 'TextLabel' {
Name = 'LoadingText',
BackgroundTransparency = 1,
Size = UDim2.new(1, (isMobile == true and -14 or -56), 1, 0),
Position = UDim2.new(0, (isMobile == true and 12 or 28), 0, 0),
Font = Enum.Font.SourceSans,
FontSize = (isMobile == true and Enum.FontSize.Size12 or Enum.FontSize.Size18),
TextWrapped = true,
TextColor3 = COLORS.WHITE,
TextXAlignment = Enum.TextXAlignment.Left,
Visible = not isTenFootInterface,
Text = "Loading...",
ZIndex = 2,
Parent = graphicsFrame,
}
local uiMessageFrame = create 'Frame' {
Name = 'UiMessageFrame',
BackgroundTransparency = 1,
Position = UDim2.new(0.25, 0, 1, -120),
Size = UDim2.new(0.5, 0, 0, 80),
ZIndex = 2,
Parent = mainBackgroundContainer,
}
local uiMessage = create 'TextLabel' {
Name = 'UiMessage',
BackgroundTransparency = 1,
Size = UDim2.new(1, 0, 1, 0),
Font = Enum.Font.SourceSansBold,
FontSize = Enum.FontSize.Size18,
TextWrapped = true,
TextColor3 = COLORS.WHITE,
Text = "",
ZIndex = 2,
Parent = uiMessageFrame,
}
local infoFrame = create 'Frame' {
Name = 'InfoFrame',
BackgroundTransparency = 1,
Position = UDim2.new(0, (isMobile == true and 20 or 100), 1, (isMobile == true and -120 or -150)),
Size = UDim2.new(0.4, 0, 0, 110),
ZIndex = 2,
Parent = mainBackgroundContainer,
}
local placeLabel = create 'TextLabel' {
Name = 'PlaceLabel',
BackgroundTransparency = 1,
Size = UDim2.new(1, 0, 0, 80),
Position = UDim2.new(0, 0, 0, 0),
Font = Enum.Font.SourceSans,
FontSize = (isTenFootInterface and Enum.FontSize.Size48 or Enum.FontSize.Size24),
TextWrapped = true,
TextScaled = true,
TextColor3 = COLORS.WHITE,
TextStrokeTransparency = 0,
Text = "",
TextXAlignment = Enum.TextXAlignment.Left,
TextYAlignment = Enum.TextYAlignment.Bottom,
ZIndex = 2,
Parent = infoFrame,
}
if isTenFootInterface then
local byLabel = create'TextLabel' {
Name = "ByLabel",
BackgroundTransparency = 1,
Size = UDim2.new(0, 36, 0, 30),
Position = UDim2.new(0, 0, 0, 80),
Font = Enum.Font.SourceSans,
FontSize = Enum.FontSize.Size36,
TextScaled = true,
TextColor3 = COLORS.WHITE,
TextStrokeTransparency = 0,
Text = "By",
TextXAlignment = Enum.TextXAlignment.Left,
TextYAlignment = Enum.TextYAlignment.Top,
ZIndex = 2,
Visible = false,
Parent = infoFrame,
}
local creatorIcon = create'ImageLabel' {
Name = "CreatorIcon",
BackgroundTransparency = 1,
Size = UDim2.new(0, 30, 0, 30),
Position = UDim2.new(0, 38, 0, 80),
ImageTransparency = 0,
Image = 'rbxasset://textures/ui/Shell/Icons/RobloxIcon32.png',
ZIndex = 2,
Visible = false,
Parent = infoFrame,
}
end
local creatorLabel = create 'TextLabel' {
Name = 'CreatorLabel',
BackgroundTransparency = 1,
Size = UDim2.new(1, 0, 0, 30),
Position = UDim2.new(0, isTenFootInterface and 72 or 0, 0, 80),
Font = Enum.Font.SourceSans,
FontSize = (isTenFootInterface and Enum.FontSize.Size36 or Enum.FontSize.Size18),
TextWrapped = true,
TextScaled = true,
TextColor3 = COLORS.WHITE,
TextStrokeTransparency = 0,
Text = "",
TextXAlignment = Enum.TextXAlignment.Left,
TextYAlignment = Enum.TextYAlignment.Top,
ZIndex = 2,
Parent = infoFrame,
}
local backgroundTextureFrame = create 'Frame' {
Name = 'BackgroundTextureFrame',
BorderSizePixel = 0,
Size = UDim2.new(1, 0, 1, 0),
Position = UDim2.new(0, 0, 0, 0),
ClipsDescendants = true,
ZIndex = 1,
BackgroundTransparency = 1,
Parent = mainBackgroundContainer,
}
local errorFrame = create 'Frame' {
Name = 'ErrorFrame',
BackgroundColor3 = COLORS.ERROR,
BorderSizePixel = 0,
Position = UDim2.new(0.25,0,0,0),
Size = UDim2.new(0.5, 0, 0, 80),
ZIndex = 8,
Visible = false,
Parent = screenGui,
}
local errorText = create 'TextLabel' {
Name = "ErrorText",
BackgroundTransparency = 1,
Size = UDim2.new(1, 0, 1, 0),
Font = Enum.Font.SourceSansBold,
FontSize = Enum.FontSize.Size14,
TextWrapped = true,
TextColor3 = COLORS.WHITE,
Text = "",
ZIndex = 8,
Parent = errorFrame,
}
while not game:GetService("CoreGui") do
wait()
end
screenGui.Parent = game:GetService("CoreGui")
currScreenGui = screenGui
end
function round(num, idp)
local mult = 10^(idp or 0)
return math.floor(num * mult + 0.5) / mult
end
---------------------------------------------------------
-- Main Script (show something now + setup connections)
-- start loading assets asap
InfoProvider:LoadAssets()
MainGui:GenerateMain()
if isTenFootInterface then
createTenfootCancelGui()
end
local setVerb = true
local lastRenderTime, lastDotUpdateTime, brickCountChange = nil, nil, nil
local fadeCycleTime = 1.7
local turnCycleTime = 2
local lastAbsoluteSize = Vector2.new(0, 0)
local loadingDots = "..."
local dotChangeTime = .2
local lastBrickCount = 0
renderSteppedConnection = game:GetService("RunService").RenderStepped:connect(function()
if not currScreenGui then return end
if not currScreenGui:FindFirstChild("BlackFrame") then return end
if setVerb then
currScreenGui.BlackFrame.CloseButton:SetVerb("Exit")
setVerb = false
end
if currScreenGui.BlackFrame:FindFirstChild("BackgroundTextureFrame") and currScreenGui.BlackFrame.BackgroundTextureFrame.AbsoluteSize ~= lastAbsoluteSize then
lastAbsoluteSize = currScreenGui.BlackFrame.BackgroundTextureFrame.AbsoluteSize
MainGui:tileBackgroundTexture(currScreenGui.BlackFrame.BackgroundTextureFrame)
end
local infoFrame = currScreenGui.BlackFrame:FindFirstChild('InfoFrame')
if infoFrame then
-- set place name
local placeLabel = infoFrame:FindFirstChild('PlaceLabel')
if placeLabel and placeLabel.Text == "" then
placeLabel.Text = InfoProvider:GetGameName()
end
-- set creator name
local creatorLabel = infoFrame:FindFirstChild('CreatorLabel')
if creatorLabel and creatorLabel.Text == "" then
local creatorName = InfoProvider:GetCreatorName()
if creatorName ~= "" then
if isTenFootInterface then
local showDevName = true
if platform == Enum.Platform.XBoxOne then
local success, result = pcall(function()
return settings():GetFFlag("ShowDevNameInXboxApp")
end)
if success then
showDevName = result
end
end
creatorLabel.Text = showDevName and creatorName or ""
local creatorIcon = infoFrame:FindFirstChild('CreatorIcon')
local byLabel = infoFrame:FindFirstChild('ByLabel')
if creatorIcon then creatorIcon.Visible = showDevName end
if byLabel then byLabel.Visible = showDevName end
else
creatorLabel.Text = "By "..creatorName
end
end
end
end
if not lastRenderTime then
lastRenderTime = tick()
lastDotUpdateTime = lastRenderTime
return
end
local currentTime = tick()
local fadeAmount = (currentTime - lastRenderTime) * fadeCycleTime
local turnAmount = (currentTime - lastRenderTime) * (360/turnCycleTime)
lastRenderTime = currentTime
currScreenGui.BlackFrame.GraphicsFrame.LoadingImage.Rotation = currScreenGui.BlackFrame.GraphicsFrame.LoadingImage.Rotation + turnAmount
local updateLoadingDots = function()
loadingDots = loadingDots.. "."
if loadingDots == "...." then
loadingDots = ""
end
currScreenGui.BlackFrame.GraphicsFrame.LoadingText.Text = "Loading" ..loadingDots
end
if currentTime - lastDotUpdateTime >= dotChangeTime and InfoProvider:GetCreatorName() == "" then
lastDotUpdateTime = currentTime
updateLoadingDots()
else
if guiService:GetBrickCount() > 0 then
if brickCountChange == nil then
brickCountChange = guiService:GetBrickCount()
end
if guiService:GetBrickCount() - lastBrickCount >= brickCountChange then
lastBrickCount = guiService:GetBrickCount()
updateLoadingDots()
end
end
end
if not isTenFootInterface then
if currentTime - startTime > 5 and currScreenGui.BlackFrame.CloseButton.ImageTransparency > 0 then
currScreenGui.BlackFrame.CloseButton.ImageTransparency = currScreenGui.BlackFrame.CloseButton.ImageTransparency - fadeAmount
if currScreenGui.BlackFrame.CloseButton.ImageTransparency <= 0 then
currScreenGui.BlackFrame.CloseButton.Active = true
end
end
end
end)
spawn(function()
local RobloxGui = game:GetService("CoreGui"):WaitForChild("RobloxGui")
local guiInsetChangedEvent = Instance.new("BindableEvent")
guiInsetChangedEvent.Name = "GuiInsetChanged"
guiInsetChangedEvent.Parent = RobloxGui
guiInsetChangedEvent.Event:connect(function(x1, y1, x2, y2)
if currScreenGui and currScreenGui:FindFirstChild("BlackFrame") then
currScreenGui.BlackFrame.Position = UDim2.new(0, -x1, 0, -y1)
currScreenGui.BlackFrame.Size = UDim2.new(1, x1 + x2, 1, y1 + y2)
end
end)
end)
local leaveGameButton, leaveGameTextLabel, errorImage = nil
guiService.ErrorMessageChanged:connect(function()
if guiService:GetErrorMessage() ~= '' then
if isTenFootInterface then
currScreenGui.ErrorFrame.Size = UDim2.new(1, 0, 0, 144)
currScreenGui.ErrorFrame.Position = UDim2.new(0, 0, 0, 0)
currScreenGui.ErrorFrame.BackgroundColor3 = COLORS.BLACK
currScreenGui.ErrorFrame.BackgroundTransparency = 0.5
currScreenGui.ErrorFrame.ErrorText.FontSize = Enum.FontSize.Size36
currScreenGui.ErrorFrame.ErrorText.Position = UDim2.new(.3, 0, 0, 0)
currScreenGui.ErrorFrame.ErrorText.Size = UDim2.new(.4, 0, 0, 144)
if errorImage == nil then
errorImage = Instance.new("ImageLabel")
errorImage.Image = "rbxasset://textures/ui/ErrorIconSmall.png"
errorImage.Size = UDim2.new(0, 96, 0, 79)
errorImage.Position = UDim2.new(0.228125, 0, 0, 32)
errorImage.ZIndex = 9
errorImage.BackgroundTransparency = 1
errorImage.Parent = currScreenGui.ErrorFrame
end
-- we show a B button to kill game data model on console
if not isTenFootInterface then
if leaveGameButton == nil then
local RobloxGui = game:GetService("CoreGui"):WaitForChild("RobloxGui")
local utility = require(RobloxGui.Modules.Settings.Utility)
local textLabel = nil
leaveGameButton, leaveGameTextLabel = utility:MakeStyledButton("LeaveGame", "Leave", UDim2.new(0, 288, 0, 78))
leaveGameButton:SetVerb("Exit")
leaveGameButton.NextSelectionDown = leaveGameButton
leaveGameButton.NextSelectionLeft = leaveGameButton
leaveGameButton.NextSelectionRight = leaveGameButton
leaveGameButton.NextSelectionUp = leaveGameButton
leaveGameButton.ZIndex = 9
leaveGameButton.Position = UDim2.new(0.771875, 0, 0, 37)
leaveGameButton.Parent = currScreenGui.ErrorFrame
leaveGameTextLabel.FontSize = Enum.FontSize.Size36
leaveGameTextLabel.ZIndex = 10
game:GetService("GuiService").SelectedCoreObject = leaveGameButton
else
game:GetService("GuiService").SelectedCoreObject = leaveGameButton
end
end
end
currScreenGui.ErrorFrame.ErrorText.Text = guiService:GetErrorMessage()
currScreenGui.ErrorFrame.Visible = true
local blackFrame = currScreenGui:FindFirstChild('BlackFrame')
if blackFrame then
blackFrame.CloseButton.ImageTransparency = 0
blackFrame.CloseButton.Active = true
end
else
currScreenGui.ErrorFrame.Visible = false
end
end)
guiService.UiMessageChanged:connect(function(type, newMessage)
if type == Enum.UiMessageType.UiMessageInfo then
local blackFrame = currScreenGui and currScreenGui:FindFirstChild('BlackFrame')
if blackFrame then
blackFrame.UiMessageFrame.UiMessage.Text = newMessage
if newMessage ~= '' then
blackFrame.UiMessageFrame.Visible = true
else
blackFrame.UiMessageFrame.Visible = false
end
end
end
end)
if guiService:GetErrorMessage() ~= '' then
currScreenGui.ErrorFrame.ErrorText.Text = guiService:GetErrorMessage()
currScreenGui.ErrorFrame.Visible = true
end
function stopListeningToRenderingStep()
if renderSteppedConnection then
renderSteppedConnection:disconnect()
renderSteppedConnection = nil
end
end
function fadeAndDestroyBlackFrame(blackFrame)
if destroyingBackground then return end
destroyingBackground = true
spawn(function()
local infoFrame = blackFrame:FindFirstChild("InfoFrame")
local graphicsFrame = blackFrame:FindFirstChild("GraphicsFrame")
local infoFrameChildren = infoFrame:GetChildren()
local transparency = 0
local rateChange = 1.8
local lastUpdateTime = nil
while transparency < 1 do
if not lastUpdateTime then
lastUpdateTime = tick()
else
local newTime = tick()
transparency = transparency + rateChange * (newTime - lastUpdateTime)
for i = 1, #infoFrameChildren do
local child = infoFrameChildren[i]
if child:IsA('TextLabel') then
child.TextTransparency = transparency
child.TextStrokeTransparency = transparency
elseif child:IsA('ImageLabel') then
child.ImageTransparency = transparency
end
end
graphicsFrame.LoadingImage.ImageTransparency = transparency
blackFrame.BackgroundTransparency = transparency
if backgroundImageTransparency < 1 then
backgroundImageTransparency = transparency
local backgroundImages = blackFrame.BackgroundTextureFrame:GetChildren()
for i = 1, #backgroundImages do
backgroundImages[i].ImageTransparency = backgroundImageTransparency
end
end
lastUpdateTime = newTime
end
wait()
end
if blackFrame ~= nil then
stopListeningToRenderingStep()
blackFrame:Destroy()
end
end)
end
function destroyLoadingElements(instant)
if not currScreenGui then return end
if destroyedLoadingGui then return end
destroyedLoadingGui = true
local guiChildren = currScreenGui:GetChildren()
for i=1, #guiChildren do
-- need to keep this around in case we get a connection error later
if guiChildren[i].Name ~= "ErrorFrame" then
if guiChildren[i].Name == "BlackFrame" and not instant then
fadeAndDestroyBlackFrame(guiChildren[i])
else
guiChildren[i]:Destroy()
end
end
end
end
function handleFinishedReplicating()
hasReplicatedFirstElements = (#game:GetService("ReplicatedFirst"):GetChildren() > 0)
if not hasReplicatedFirstElements then
if game:IsLoaded() then
handleRemoveDefaultLoadingGui()
else
local gameLoadedCon = nil
gameLoadedCon = game.Loaded:connect(function()
gameLoadedCon:disconnect()
gameLoadedCon = nil
handleRemoveDefaultLoadingGui()
end)
end
else
wait(5) -- make sure after 5 seconds we remove the default gui, even if the user doesn't
handleRemoveDefaultLoadingGui()
end
end
function handleRemoveDefaultLoadingGui(instant)
if isTenFootInterface then
ContextActionService:UnbindCoreAction('CancelGameLoad')
end
destroyLoadingElements(instant)
end
game:GetService("ReplicatedFirst").FinishedReplicating:connect(handleFinishedReplicating)
if game:GetService("ReplicatedFirst"):IsFinishedReplicating() then
handleFinishedReplicating()
end
game:GetService("ReplicatedFirst").RemoveDefaultLoadingGuiSignal:connect(handleRemoveDefaultLoadingGui)
if game:GetService("ReplicatedFirst"):IsDefaultLoadingGuiRemoved() then
handleRemoveDefaultLoadingGui()
end
local UserInputServiceChangedConn;
local function onUserInputServiceChanged(prop)
if prop == 'VREnabled' then
local UseVr = false
pcall(function() UseVr = UIS.VREnabled end)
if UseVr then
if UserInputServiceChangedConn then
UserInputServiceChangedConn:disconnect()
UserInputServiceChangedConn = nil
end
handleRemoveDefaultLoadingGui(true)
require(RobloxGui.Modules.LoadingScreen3D)
end
end
end
UserInputServiceChangedConn = UIS.Changed:connect(onUserInputServiceChanged)
onUserInputServiceChanged('VREnabled')
@@ -0,0 +1,502 @@
--BackpackScript3D: VR port of backpack interface using a 3D panel
--written by 0xBAADF00D
local ICON_SIZE = 48
local ICON_SPACING = 52
local PIXELS_PER_STUD = 64
local SLOT_BORDER_SIZE = 0
local SLOT_BORDER_SELECTED_SIZE = 4
local SLOT_BORDER_COLOR = Color3.new(90/255, 142/255, 233/255)
local SLOT_BACKGROUND_COLOR = Color3.new(0.2, 0.2, 0.2)
local SLOT_HOVER_BACKGROUND_COLOR = Color3.new(90/255, 90/255, 90/255)
local HOPPERBIN_ANGLE = math.rad(-45)
local HOPPERBIN_ROTATION = CFrame.Angles(HOPPERBIN_ANGLE, 0, 0)
local HOPPERBIN_OFFSET = Vector3.new(0, 0, -5)
local HEALTHBAR_SPACE = 12
local HEALTHBAR_WIDTH = 82
local HEALTHBAR_HEIGHT = 5
local NAME_SPACE = 14
local Tools = {}
local ToolsList = {}
local slotIcons = {}
local BackpackScript = {}
local topbarEnabled = false
local player = game.Players.LocalPlayer
local currentHumanoid = nil
local CoreGui = game:GetService('CoreGui')
local RobloxGui = CoreGui:WaitForChild("RobloxGui")
local Panel3D = require(RobloxGui.Modules.VR.Panel3D)
local Util = require(RobloxGui.Modules.Settings.Utility)
local ContextActionService = game:GetService("ContextActionService")
local BackpackPanel = Panel3D.Get("Backpack")
BackpackPanel:ResizeStuds(5, 2)
BackpackPanel:SetType(Panel3D.Type.Fixed, { CFrame = CFrame.new(0, 0, -5) })
BackpackPanel:SetVisible(true)
local toolsFrame = Instance.new("TextButton", BackpackPanel:GetGUI()) --prevent clicks falling through in case you have a rocket launcher and blow yourself up
toolsFrame.Text = ""
toolsFrame.Size = UDim2.new(1, 0, 0, ICON_SIZE)
toolsFrame.BackgroundTransparency = 1
toolsFrame.Selectable = false
local insetAdjustY = toolsFrame.AbsolutePosition.Y
toolsFrame.Position = UDim2.new(0, 0, 0, HEALTHBAR_SPACE + NAME_SPACE)
--Healthbar color function stolen from Topbar.lua
local HEALTH_BACKGROUND_COLOR = Color3.new(228/255, 236/255, 246/255)
local HEALTH_RED_COLOR = Color3.new(255/255, 28/255, 0/255)
local HEALTH_YELLOW_COLOR = Color3.new(250/255, 235/255, 0)
local HEALTH_GREEN_COLOR = Color3.new(27/255, 252/255, 107/255)
local healthbarBack = Instance.new("ImageLabel", BackpackPanel:GetGUI())
healthbarBack.ImageColor3 = HEALTH_BACKGROUND_COLOR
healthbarBack.BackgroundTransparency = 1
healthbarBack.ScaleType = Enum.ScaleType.Slice
healthbarBack.SliceCenter = Rect.new(10, 10, 10, 10)
healthbarBack.Name = "HealthbarContainer"
healthbarBack.Image = "rbxasset://textures/ui/Menu/rectBackgroundWhite.png"
local healthbarFront = Instance.new("ImageLabel", healthbarBack)
healthbarFront.ImageColor3 = HEALTH_GREEN_COLOR
healthbarFront.BackgroundTransparency = 1
healthbarFront.ScaleType = Enum.ScaleType.Slice
healthbarFront.SliceCenter = Rect.new(10, 10, 10, 10)
healthbarFront.Size = UDim2.new(1, 0, 1, 0)
healthbarFront.Position = UDim2.new(0, 0, 0, 0)
healthbarFront.Name = "HealthbarFill"
healthbarFront.Image = "rbxasset://textures/ui/Menu/rectBackgroundWhite.png"
local playerName = Instance.new("TextLabel", BackpackPanel:GetGUI())
playerName.Name = "PlayerName"
playerName.BackgroundTransparency = 1
playerName.TextColor3 = Color3.new(1, 1, 1)
playerName.Text = player.Name
playerName.Font = Enum.Font.SourceSansBold
playerName.FontSize = Enum.FontSize.Size12
playerName.TextXAlignment = Enum.TextXAlignment.Left
playerName.Size = UDim2.new(1, 0, 0, NAME_SPACE)
local healthColorToPosition = {
[Vector3.new(HEALTH_RED_COLOR.r, HEALTH_RED_COLOR.g, HEALTH_RED_COLOR.b)] = 0.1;
[Vector3.new(HEALTH_YELLOW_COLOR.r, HEALTH_YELLOW_COLOR.g, HEALTH_YELLOW_COLOR.b)] = 0.5;
[Vector3.new(HEALTH_GREEN_COLOR.r, HEALTH_GREEN_COLOR.g, HEALTH_GREEN_COLOR.b)] = 0.8;
}
local min = 0.1
local minColor = HEALTH_RED_COLOR
local max = 0.8
local maxColor = HEALTH_GREEN_COLOR
local function HealthbarColorTransferFunction(healthPercent)
if healthPercent < min then
return minColor
elseif healthPercent > max then
return maxColor
end
-- Shepard's Interpolation
local numeratorSum = Vector3.new(0,0,0)
local denominatorSum = 0
for colorSampleValue, samplePoint in pairs(healthColorToPosition) do
local distance = healthPercent - samplePoint
if distance == 0 then
-- If we are exactly on an existing sample value then we don't need to interpolate
return Color3.new(colorSampleValue.x, colorSampleValue.y, colorSampleValue.z)
else
local wi = 1 / (distance*distance)
numeratorSum = numeratorSum + wi * colorSampleValue
denominatorSum = denominatorSum + wi
end
end
local result = numeratorSum / denominatorSum
return Color3.new(result.x, result.y, result.z)
end
---
local backpackEnabled = true
local healthbarEnabled = true
local function UpdateLayout()
local width, height = 100, 100
local borderSize = (ICON_SPACING - ICON_SIZE) / 2
local x = borderSize
local y = 0
for _, tool in ipairs(ToolsList) do
local slot = Tools[tool]
if slot then
slot.icon.Position = UDim2.new(0, x, 0, y)
x = x + ICON_SPACING
end
end
if #ToolsList == 0 then
width = HEALTHBAR_WIDTH
height = HEALTHBAR_SPACE + NAME_SPACE
BackpackPanel.showCursor = false
else
width = #ToolsList * ICON_SPACING
height = ICON_SIZE + HEALTHBAR_SPACE + NAME_SPACE
BackpackPanel.showCursor = true
end
BackpackPanel:ResizePixels(width, height)
playerName.Position = UDim2.new(0, borderSize, 0, 0)
healthbarBack.Position = UDim2.new(0, borderSize, 0, NAME_SPACE + (HEALTHBAR_SPACE - HEALTHBAR_HEIGHT) / 2)
healthbarBack.Size = UDim2.new(0, HEALTHBAR_WIDTH, 0, HEALTHBAR_HEIGHT)
end
local function UpdateHealth(humanoid)
local percentHealth = humanoid.Health / humanoid.MaxHealth
if percentHealth ~= percentHealth then
percentHealth = 1
end
healthbarFront.BackgroundColor3 = HealthbarColorTransferFunction(percentHealth)
healthbarFront.Size = UDim2.new(percentHealth, 0, 1, 0)
end
local function SetTransparency(transparency)
for i, v in pairs(Tools) do
v.bg.ImageTransparency = transparency
v.image.ImageTransparency = transparency
v.text.TextTransparency = transparency
end
healthbarBack.ImageTransparency = transparency
healthbarFront.ImageTransparency = transparency
end
local function OnHotbarEquipPrimary(actionName, state, obj)
if state ~= Enum.UserInputState.Begin then
return
end
for tool, slot in pairs(Tools) do
if slot.hovered then
slot.OnClick()
return
end
end
end
local function EnableHotbarInput(enable)
if not backpackEnabled then
enable = false
end
if not currentHumanoid then
return
end
if enable then
ContextActionService:BindCoreAction("HotbarEquipPrimary", OnHotbarEquipPrimary, false, Enum.KeyCode.ButtonA, Enum.KeyCode.ButtonR2, Enum.UserInputType.MouseButton1)
else
ContextActionService:UnbindCoreAction("HotbarEquipPrimary")
end
end
local function AddTool(tool)
if Tools[tool] then
return
end
local slot = {}
Tools[tool] = slot
table.insert(ToolsList, tool)
slot.hovered = false
slot.tool = tool
slot.icon = Instance.new("TextButton", toolsFrame)
slot.icon.Text = ""
slot.icon.Size = UDim2.new(0, ICON_SIZE, 0, ICON_SIZE)
slot.icon.BackgroundColor3 = Color3.new(0, 0, 0)
slot.icon.Selectable = true
slot.icon.BackgroundTransparency = 1
slotIcons[tool] = slot.icon
slot.bg = Instance.new("ImageLabel", slot.icon)
slot.bg.Position = UDim2.new(0, -1, 0, -1)
slot.bg.Size = UDim2.new(1, 2, 1, 2)
slot.bg.Image = "rbxasset://textures/ui/Menu/rectBackground.png"
slot.bg.ScaleType = Enum.ScaleType.Slice
slot.bg.SliceCenter = Rect.new(10, 10, 10, 10)
slot.bg.BackgroundTransparency = 1
slot.image = Instance.new("ImageLabel", slot.icon)
slot.image.Position = UDim2.new(0, 1, 0, 1)
slot.image.Size = UDim2.new(1, -2, 1, -2)
slot.image.BackgroundTransparency = 1
slot.image.Selectable = false
slot.text = Instance.new("TextLabel", slot.icon)
slot.text.Position = UDim2.new(0, 1, 0, 1)
slot.text.Size = UDim2.new(1, -2, 1, -2)
slot.text.BackgroundTransparency = 1
slot.text.TextColor3 = Color3.new(1, 1, 1)
slot.text.Font = Enum.Font.SourceSans
slot.text.FontSize = Enum.FontSize.Size12
slot.text.ClipsDescendants = true
slot.text.Selectable = false
local selectionObject = Util:Create'ImageLabel'
{
Name = 'SelectionObject';
Size = UDim2.new(1,0,1,0);
BackgroundTransparency = 1;
Image = "rbxasset://textures/ui/Keyboard/key_selection_9slice.png";
ImageTransparency = 0;
ScaleType = Enum.ScaleType.Slice;
SliceCenter = Rect.new(12,12,52,52);
BorderSizePixel = 0;
}
slot.icon.SelectionImageObject = selectionObject
local function updateToolData()
slot.image.Image = tool.TextureId
slot.text.Text = tool.TextureId == "" and tool.Name or ""
end
updateToolData()
slot.OnClick = function()
if not player.Character then return end
local humanoid = player.Character:FindFirstChild("Humanoid")
if not humanoid then return end
local in_backpack = tool.Parent == player.Backpack
humanoid:UnequipTools()
if in_backpack then
humanoid:EquipTool(tool)
end
end
slot.icon.MouseButton1Click:connect(slot.OnClick)
slot.OnEnter = function()
slot.hovered = true
end
slot.OnLeave = function()
slot.hovered = false
end
-- slot.icon.MouseEnter:connect(slot.OnEnter)
-- slot.icon.MouseLeave:connect(slot.OnLeave)
tool.Changed:connect(function(prop)
if prop == "Parent" then
if tool.Parent == player:FindFirstChild("Backpack") then
slot.bg.Size = UDim2.new(0, ICON_SIZE, 0, ICON_SIZE) --temporary hold-over until new backpack design comes along (can't use border with this antialiased frame stand-in)
slot.bg.Position = UDim2.new(0, 0, 0, 0)
elseif tool.Parent == player.Character then
slot.bg.Size = UDim2.new(0, ICON_SIZE + 8, 0, ICON_SIZE + 8)
slot.bg.Position = UDim2.new(0, -4, 0, -4)
end
elseif prop == "TextureId" or prop == "Name" then
updateToolData()
end
end)
UpdateLayout()
end
local humanoidChangedEvent = nil
local humanoidAncestryChangedEvent = nil
local function RegisterHumanoid(humanoid)
currentHumanoid = humanoid
if humanoidChangedEvent then
humanoidChangedEvent:disconnect()
humanoidChangedEvent = nil
end
if humanoidAncestryChangedEvent then
humanoidAncestryChangedEvent:disconnect()
humanoidAncestryChangedEvent = nil
end
if humanoid then
humanoidChangedEvent = humanoid.HealthChanged:connect(function() UpdateHealth(humanoid) end)
humanoidAncestryChangedEvent = humanoid.AncestryChanged:connect(function(child, parent)
if child == humanoid and parent ~= player.Character then
RegisterHumanoid(nil)
end
end)
UpdateHealth(humanoid)
end
end
local function OnChildAdded(child)
if child:IsA("Tool") or child:IsA("HopperBin") then
AddTool(child)
end
if child:IsA("Humanoid") and child.Parent == player.Character then
RegisterHumanoid(child)
end
end
local function RemoveTool(tool)
if not Tools[tool] then
return
end
Tools[tool].icon:Destroy()
for i, v in ipairs(ToolsList) do
if v == tool then
table.remove(ToolsList, i)
break
end
end
Tools[tool] = nil
slotIcons[tool] = nil
UpdateLayout()
end
local function OnChildRemoved(child)
if child:IsA("Tool") or child:IsA("HopperBin") then
if Tools[child] then
if child.Parent ~= player:FindFirstChild("Backpack") and child.Parent ~= player.Character then
RemoveTool(child)
end
end
end
end
local function OnCharacterAdded(character)
local backpack = player:WaitForChild("Backpack")
for i, v in ipairs(character:GetChildren()) do
if v:IsA("Humanoid") then
RegisterHumanoid(v)
break
end
end
for tool, v in pairs(Tools) do
RemoveTool(tool)
end
Tools = {}
ToolsList = {}
character.ChildAdded:connect(OnChildAdded)
character.ChildRemoved:connect(OnChildRemoved)
for i, v in ipairs(backpack:GetChildren()) do
OnChildAdded(v)
end
backpack.ChildAdded:connect(OnChildAdded)
backpack.ChildRemoved:connect(OnChildRemoved)
end
player.CharacterAdded:connect(OnCharacterAdded)
if player.Character then
spawn(function() OnCharacterAdded(player.Character) end)
end
local function OnHotbarEquip(actionName, state, obj)
if not backpackEnabled then
return
end
local character = player.Character
if not character then
return
end
if not currentHumanoid then
return
end
if state ~= Enum.UserInputState.Begin then
return
end
if #ToolsList == 0 then
return
end
local current = 0
for i, v in pairs(ToolsList) do
if v.Parent == character then
current = i
end
end
currentHumanoid:UnequipTools()
if obj.KeyCode == Enum.KeyCode.ButtonR1 then
current = current + 1
if current > #ToolsList then
current = 1
end
else
current = current - 1
if current < 1 then
current = #ToolsList
end
end
currentHumanoid:EquipTool(ToolsList[current])
end
local function OnCoreGuiChanged(coreGuiType, enabled)
-- Check for enabling/disabling the whole thing
if coreGuiType == Enum.CoreGuiType.Backpack or coreGuiType == Enum.CoreGuiType.All then
backpackEnabled = enabled
UpdateLayout()
if enabled then
ContextActionService:BindCoreAction("HotbarEquip2", OnHotbarEquip, false, Enum.KeyCode.ButtonL1, Enum.KeyCode.ButtonR1)
toolsFrame.Parent = BackpackPanel:GetGUI()
else
ContextActionService:UnbindCoreAction("HotbarEquip2")
toolsFrame.Parent = nil
end
end
if coreGuiType == Enum.CoreGuiType.Health or coreGuiType == Enum.CoreGuiType.All then
healthbarEnabled = enabled
UpdateLayout()
if enabled then
healthbarBack.Parent = BackpackPanel:GetGUI()
else
healthbarBack.Parent = nil
end
end
end
local StarterGui = game:GetService("StarterGui")
StarterGui.CoreGuiChangedSignal:connect(OnCoreGuiChanged)
OnCoreGuiChanged(Enum.CoreGuiType.Backpack, StarterGui:GetCoreGuiEnabled(Enum.CoreGuiType.Backpack))
OnCoreGuiChanged(Enum.CoreGuiType.Backpack, StarterGui:GetCoreGuiEnabled(Enum.CoreGuiType.All))
OnCoreGuiChanged(Enum.CoreGuiType.Health, StarterGui:GetCoreGuiEnabled(Enum.CoreGuiType.Health))
OnCoreGuiChanged(Enum.CoreGuiType.Health, StarterGui:GetCoreGuiEnabled(Enum.CoreGuiType.All))
local panelLocalCF = CFrame.new(0, -3.5, 5) * CFrame.Angles(math.rad(20), 0, 0)
function BackpackPanel:PreUpdate(cameraCF, cameraRenderCF, userHeadCF, lookRay)
--the backpack panel needs to go in front of the user when they look at it.
--if they aren't looking, we should be updating self.localCF
if self.transparency == 1 then
local headForwardCF = Panel3D.GetHeadLookXZ()
local panelOriginCF = CFrame.new(userHeadCF.p) * headForwardCF
self.localCF = panelOriginCF * panelLocalCF
end
end
function BackpackPanel:OnUpdate()
SetTransparency(self.transparency)
local hovered, tool = BackpackPanel:FindHoveredGuiElement(slotIcons)
if hovered and tool then
local slot = Tools[tool]
if not slot.hovered then
slot.OnEnter()
end
for i, v in pairs(Tools) do
if v.hovered and v ~= slot then
v.OnLeave()
end
end
end
end
function BackpackPanel:OnMouseEnter(x, y)
EnableHotbarInput(true)
end
function BackpackPanel:OnMouseLeave(x, y)
EnableHotbarInput(false)
end
return BackpackScript
File diff suppressed because it is too large Load Diff
+491
View File
@@ -0,0 +1,491 @@
--Panel3D: 3D GUI panels for VR
--written by 0xBAADF00D
local PIXELS_PER_STUD = 64
local SETTINGS_DISTANCE = 3.5
local CURSOR_HIDE_TIME = 2
local CURSOR_FADE_TIME = 0.125
local CoreGui = game:GetService('CoreGui')
local RobloxGui = CoreGui:WaitForChild("RobloxGui")
local Panel3D = {}
Panel3D.Panels = {
Lower = 1,
Hamburger = 2,
Settings = 3,
Keyboard = 4,
Chat = 5
}
Panel3D.Orientation = {
Horizontal = 1,
Fixed = 2
}
Panel3D.Visibility = {
BelowAngleThreshold = 1,
Modal = 2,
Normal = 3
}
local panelDefaultVectors = {
[Panel3D.Panels.Lower] = CFrame.Angles(math.rad(-45), 0, 0):vectorToWorldSpace(Vector3.new(0, 0, -5)),
[Panel3D.Panels.Hamburger] = CFrame.Angles(math.rad(-55), 0, 0):vectorToWorldSpace(Vector3.new(0, 0, -5)),
[Panel3D.Panels.Settings] = Vector3.new(0, 0, -SETTINGS_DISTANCE),
[Panel3D.Panels.Keyboard] = CFrame.Angles(math.rad(-22.5), 0, 0):vectorToWorldSpace(Vector3.new(0, 0, -5)),
[Panel3D.Panels.Chat] = CFrame.Angles(math.rad(5), 0, 0):vectorToWorldSpace(Vector3.new(0, 0, -5))
}
local DEFAULT_PANEL_LOCK_THRESHOLD = math.rad(-25)
local panelTransparencyBias = { --tuned values; raise the opacity value to this power
[Panel3D.Panels.Lower] = 6.5,
[Panel3D.Panels.Hamburger] = 8,
[Panel3D.Panels.Settings] = 0,
[Panel3D.Panels.Keyboard] = 1.5,
[Panel3D.Panels.Chat] = 1.5
}
local panels = {}
local headScale = 1
local renderStepName = "Panel3D"
local cursor = Instance.new("ImageLabel")
cursor.Image = "rbxasset://textures/Cursors/Gamepad/Pointer.png"
cursor.Size = UDim2.new(0, 8, 0, 8)
cursor.BackgroundTransparency = 1
cursor.ZIndex = 10
local resetOrientationFlag = false
local currentModalPanel = nil
local GuiService = game:GetService("GuiService")
local UserInputService = game:GetService("UserInputService")
local cursorHidden = false
local hasTool = false
local lastMouseMove = tick()
local function OnCharacterAdded(character)
hasTool = false
for i, v in ipairs(character:GetChildren()) do
if v:IsA("Tool") then
hasTool = true
end
end
character.ChildAdded:connect(function(child)
if child:IsA("Tool") then
hasTool = true
lastMouseMove = tick() --kick the mouse when a tool is equipped
end
end)
character.ChildRemoved:connect(function(child)
if child:IsA("Tool") then
hasTool = false
end
end)
end
spawn(function()
while not game.Players.LocalPlayer do wait() end
game.Players.LocalPlayer.CharacterAdded:connect(OnCharacterAdded)
if game.Players.LocalPlayer.Character then OnCharacterAdded(game.Players.LocalPlayer.Character) end
end)
local function autoHideCursor(hide)
if not UserInputService.VREnabled then
cursorHidden = false
return
end
if hide then
--don't hide if there's a tool in the character
local character = game.Players.LocalPlayer.Character
if character and hasTool then
return
end
cursorHidden = true
UserInputService.OverrideMouseIconBehavior = Enum.OverrideMouseIconBehavior.ForceHide
else
cursorHidden = false
UserInputService.OverrideMouseIconBehavior = Enum.OverrideMouseIconBehavior.None
end
end
local function isCursorVisible()
--if ForceShow, the cursor is definitely visible at all times
if UserInputService.OverrideMouseIconBehavior == Enum.OverrideMouseIconBehavior.ForceShow then
return true
end
--if ForceHide, the cursor is definitely NOT visible
if UserInputService.OverrideMouseIconBehavior == Enum.OverrideMouseIconBehavior.ForceHide then
return false
end
--Otherwise, we need to check if the developer set MouseIconEnabled=false
if UserInputService.MouseIconEnabled and UserInputService.OverrideMouseIconBehavior == Enum.OverrideMouseIconBehavior.None then
return true
end
return false
end
UserInputService.InputChanged:connect(function(inputObj, processed)
if inputObj.UserInputType == Enum.UserInputType.MouseMovement then
lastMouseMove = tick()
autoHideCursor(false)
end
end)
game:GetService("RunService").Heartbeat:connect(function()
if isCursorVisible() then
cursorHidden = false
end
if lastMouseMove + CURSOR_HIDE_TIME < tick() and not GuiService.MenuIsOpen and not cursorHidden then
autoHideCursor(true)
end
end)
local function createPanel()
local panelPart = Instance.new("Part")
panelPart.Transparency = 1
panelPart.CanCollide = false
panelPart.Anchored = true
panelPart.Archivable = false
panelPart.Size = Vector3.new(1.5, 1.5, 1)
panelPart.Parent = workspace.CurrentCamera
local panelGUI = Instance.new("SurfaceGui", CoreGui)
panelGUI.Name = "GUI"
panelGUI.Adornee = panelPart
panelGUI.ToolPunchThroughDistance = 1000
panelGUI.Active = true
pcall(function() --todo: remove this when api is live
panelGUI.AlwaysOnTop = true
end)
return panelPart, panelGUI
end
function Panel3D.Get(panelId)
local panel = panels[panelId]
if not panel then
local panelName
for name, value in pairs(Panel3D.Panels) do
if value == panelId then
panelName = name
break
end
end
if not panelName then
error("Tried to request an invalid 3D panel")
end
local part, gui = createPanel()
panel = {}
panel.part = part
panel.part.Name = "GUI"--("%sPanel3D"):format(panelName)
panel.gui = gui
panel.gui.Name = ("%sPanelGUI"):format(panelName)
panel.vector = panelDefaultVectors[panelId]
panel.horizontalVector = Vector3.new(panel.vector.x, 0, panel.vector.z).unit
panel.pitchAngle = math.asin(panel.vector.unit.y)
panel.verticalRange = math.rad(5)
panel.horizontalRange = math.rad(5)
panel.transparencyCallbacks = {}
panel.OnMouseEnter = nil
panel.OnMouseLeave = nil
panel.pixelScale = 1
panel.visible = true
panel.visibilityBehavior = Panel3D.Visibility.BelowAngleThreshold
panel.orientationMode = Panel3D.Orientation.Horizontal
panel.orientation = nil
panel.cursorEnabled = true
panel.width = 1
panel.height = 1
function panel:AddTransparencyCallback(callback)
table.insert(panel.transparencyCallbacks, callback)
end
function panel:Resize(width, height, pixelsPerStud)
panel.width = width
panel.height = height
pixelsPerStud = pixelsPerStud or PIXELS_PER_STUD
panel.pixelScale = pixelsPerStud / PIXELS_PER_STUD
panel.part.Size = Vector3.new(panel.width * headScale, panel.height * headScale, 1)
panel.gui.CanvasSize = Vector2.new(pixelsPerStud * panel.width, pixelsPerStud * panel.height)
local distance = panel.vector.magnitude
panel.verticalRange = math.atan(panel.part.Size.Y / (2 * distance)) * 2
panel.horizontalRange = math.atan(panel.part.Size.X / (2 * distance)) * 2
end
function panel:ResizePixels(width, height)
local widthStuds = width / PIXELS_PER_STUD
local heightStuds = height / PIXELS_PER_STUD
panel:Resize(widthStuds, heightStuds)
end
function panel:SetModal()
panel.visible = false
panel.visibilityBehavior = Panel3D.Visibility.Modal
if panel.visible then
currentModalPanel = panel
if panel.orientationMode == Panel3D.Orientation.Fixed then
local userHeadCFrame = UserInputService:GetUserCFrame(Enum.UserCFrame.Head)
panel.orientation = userHeadCFrame * CFrame.new(0, 0, -panel.vector.magnitude) * CFrame.Angles(0, math.pi, 0)
end
end
end
function panel:SetVisible(visible)
panel.visible = visible
if visible and panel.visibilityBehavior == Panel3D.Visibility.Modal then
currentModalPanel = panel
if panel.orientationMode == Panel3D.Orientation.Fixed then
local userHeadCFrame = UserInputService:GetUserCFrame(Enum.UserCFrame.Head)
panel.orientation = userHeadCFrame * CFrame.new(0, 0, -panel.vector.magnitude) * CFrame.Angles(0, math.pi, 0)
end
else
if currentModalPanel == panel then
currentModalPanel = nil
end
end
end
panels[panelId] = panel
end
return panel
end
function Panel3D.ResetOrientation()
resetOrientationFlag = true
end
function Panel3D.GetGUI(panel)
local panelGUI = panelGUIs[panel]
if not panelGUI then
local part = Panel3D.GetPart(panel)
panelGUI = panelGUIs[panel]
end
return panelGUI
end
local zeroVector = Vector3.new(0, 0, 0)
local headXZ = CFrame.new()
local headOffset = Vector3.new()
local currentHoverPanel = nil
local savedMouseBehavior = Enum.MouseBehavior.Default
function Panel3D.OnRenderStep()
if not UserInputService.VREnabled then
return
end
local cameraCFrame = workspace.CurrentCamera.CFrame
local cameraRenderCFrame = workspace.CurrentCamera:GetRenderCFrame()
local userHeadCFrame = UserInputService:GetUserCFrame(Enum.UserCFrame.Head)
local userHeadLook = userHeadCFrame.lookVector
local userHeadHorizontalVector = Vector3.new(userHeadLook.X, 0, userHeadLook.Z).unit
local userHeadPitch = math.asin(userHeadLook.Y)
local panelLockThreshold = DEFAULT_PANEL_LOCK_THRESHOLD
for panelId, panel in pairs(panels) do
if panel.pitchLockThreshold and panel.visible then
panelLockThreshold = math.max(panelLockThreshold, panel.pitchLockThreshold)
end
end
local isAboveThreshold = false
if userHeadPitch > panelLockThreshold or resetOrientationFlag then
isAboveThreshold = true
headXZ = CFrame.new(zeroVector, userHeadHorizontalVector)
headOffset = userHeadCFrame.p
resetOrientationFlag = false
end
local panelsOrigin = cameraCFrame * CFrame.new(headOffset) * headXZ
for panelId, panel in pairs(panels) do
if panel.visibilityBehavior == Panel3D.Visibility.BelowAngleThreshold then
panel.visible = not (isAboveThreshold or currentModalPanel)
end
if not panel.visible then
panel.part.Parent = nil
panel.gui.Adornee = nil
else
panel.part.Parent = workspace.CurrentCamera --TODO: move to new 3D gui space
panel.gui.Adornee = panel.part
local panelCFrame;
if panel.orientationMode == Panel3D.Orientation.Fixed and panel.orientation then
local pos = panel.orientation.p * headScale
panel.part.CFrame = workspace.CurrentCamera.CFrame * ((panel.orientation - panel.orientation.p) + pos)
panelCFrame = panel.part.CFrame
else
local panelPosition = panelsOrigin:pointToWorldSpace(panel.vector * headScale)
panelCFrame = CFrame.new(panelPosition, panelsOrigin.p)
panel.part.CFrame = panelCFrame
end
local toPanel = (panelCFrame.p - cameraRenderCFrame.p).unit
local transparency = panel.visible and 1 - (math.max(0, cameraRenderCFrame.lookVector:Dot(toPanel)) ^ panelTransparencyBias[panelId]) or 1
for _, callback in pairs(panel.transparencyCallbacks) do
callback(transparency)
end
end
end
--Render a cursor overlaid onto the panels
local cframe = cameraRenderCFrame
local ray = Ray.new(cframe.p, cframe.lookVector * 999)
local ignoreList = { game.Players.LocalPlayer.Character }
local part, endpoint = workspace:FindPartOnRayWithIgnoreList(ray, ignoreList)
local hitPanel = nil
local hitPanelId = nil
for panelId, panel in pairs(panels) do
if part == panel.part then
hitPanel = panel
hitPanelId = panelId
end
end
if hitPanel then
if not currentHoverPanel then
savedMouseBehavior = UserInputService.MouseBehavior
UserInputService.MouseBehavior = Enum.MouseBehavior.LockCenter
else
if currentHoverPanel ~= hitPanel then
if currentHoverPanel.OnMouseLeave then
currentHoverPanel:OnMouseLeave()
end
end
end
if hitPanel.OnMouseEnter and currentHoverPanel ~= hitPanel then
hitPanel:OnMouseEnter()
end
currentHoverPanel = hitPanel
UserInputService.OverrideMouseIconBehavior = Enum.OverrideMouseIconBehavior.ForceHide
if hitPanel.cursorEnabled then
cursor.Parent = hitPanel.gui
else
cursor.Parent = nil
end
local localEndpoint = part:GetRenderCFrame():pointToObjectSpace(endpoint)
local x = (localEndpoint.X / part.Size.X) + 0.5
local y = (localEndpoint.Y / part.Size.Y) + 0.5
x = 1 - x
y = 1 - y
cursor.Size = UDim2.new(0, 8 * hitPanel.pixelScale, 0, 8 * hitPanel.pixelScale)
cursor.Position = UDim2.new(x, -cursor.AbsoluteSize.x * 0.5, y, -cursor.AbsoluteSize.y * 0.5)
if hitPanel.visible and not modalPanelIsOpen then
hitPanel.part.Parent = workspace.CurrentCamera
for _, callback in pairs(hitPanel.transparencyCallbacks) do
callback(0)
end
end
else
if currentHoverPanel then
UserInputService.MouseBehavior = savedMouseBehavior
if currentHoverPanel.OnMouseLeave then
currentHoverPanel:OnMouseLeave()
end
end
currentHoverPanel = false
cursor.Parent = nil
end
end
-- RayPlaneIntersection
-- http://www.siggraph.org/education/materials/HyperGraph/raytrace/rayplane_intersection.htm
local function RayPlaneIntersection(ray, planeNormal, pointOnPlane)
planeNormal = planeNormal.unit
ray = ray.Unit
-- compute Pn (dot) Rd = Vd and check if Vd == 0 then we know ray is parallel to plane
local Vd = planeNormal:Dot(ray.Direction)
-- could fuzzy equals this a little bit to account for imprecision or very close angles to zero
if Vd == 0 then -- parallel, no intersection
return nil
end
local V0 = planeNormal:Dot(pointOnPlane - ray.Origin)
local t = V0 / Vd
if t < 0 then --plane is behind ray origin, and thus there is no intersection
return nil
end
return ray.Origin + ray.Direction * t
end
function Panel3D.FindHoveredGuiElement(panel, elements)
local cameraRenderCFrame = workspace.CurrentCamera and workspace.CurrentCamera:GetRenderCFrame()
local panelPart = panel.part
if cameraRenderCFrame and panelPart then
local panelPartSize = panelPart.Size
local panelSurfaceCFrame = panelPart.CFrame + panelPart.CFrame.lookVector * (panelPartSize.Z * 0.5)
local intersectionPt = RayPlaneIntersection(Ray.new(cameraRenderCFrame.p, cameraRenderCFrame.lookVector), panelSurfaceCFrame.lookVector, panelSurfaceCFrame.p)
if intersectionPt then
local localPoint = panelSurfaceCFrame:pointToObjectSpace(intersectionPt) * Vector3.new(-1, 1, 1) + Vector3.new(panelPartSize.X/2, -panelPartSize.Y/2, 0)
local guiPoint = Vector2.new((localPoint.X / panelPartSize.X) * panel.gui.AbsoluteSize.X, (localPoint.Y / panelPartSize.Y) * -panel.gui.AbsoluteSize.Y)
local x = guiPoint.x
local y = guiPoint.y
for _, item in pairs(elements) do
local minPt = item.AbsolutePosition
local maxPt = item.AbsolutePosition + item.AbsoluteSize
if minPt.X <= x and maxPt.X >= x and minPt.Y <= y and maxPt.Y >= y then
return item
end
end
end
end
end
game:GetService("RunService"):BindToRenderStep(renderStepName, Enum.RenderPriority.Last.Value, Panel3D.OnRenderStep)
local function OnCameraChanged(prop)
if prop == "HeadScale" then
pcall(function()
headScale = workspace.CurrentCamera.HeadScale
end)
for i, v in pairs(panels) do
v:Resize(v.width, v.height, v.pixelScale * PIXELS_PER_STUD)
end
end
end
local cameraChangedConn = nil
workspace.Changed:connect(function(prop)
if prop == "CurrentCamera" then
OnCameraChanged("HeadScale")
if cameraChangedConn then
cameraChangedConn:disconnect()
end
cameraChangedConn = workspace.CurrentCamera.Changed:connect(OnCameraChanged)
end
end)
if workspace.CurrentCamera then
OnCameraChanged("HeadScale")
cameraChangedConn = workspace.CurrentCamera.Changed:connect(OnCameraChanged)
end
return Panel3D
@@ -0,0 +1,628 @@
--[[
// FileName: PlayerDropDown.lua
// Written by: TheGamer101
// Description: Code for the player drop down in the PlayerList and Chat
]]
local moduleApiTable = {}
--[[ Services ]]--
local CoreGui = game:GetService('CoreGui')
local HttpService = game:GetService('HttpService')
local HttpRbxApiService = game:GetService('HttpRbxApiService')
local PlayersService = game:GetService('Players')
--[[ Script Variables ]]--
local LocalPlayer = PlayersService.LocalPlayer
--[[ Constants ]]--
local POPUP_ENTRY_SIZE_Y = 24
local ENTRY_PAD = 2
local BG_TRANSPARENCY = 0.5
local BG_COLOR = Color3.new(31/255, 31/255, 31/255)
local TEXT_STROKE_TRANSPARENCY = 0.75
local TEXT_COLOR = Color3.new(1, 1, 243/255)
local TEXT_STROKE_COLOR = Color3.new(34/255, 34/255, 34/255)
local MAX_FRIEND_COUNT = 200
local FRIEND_IMAGE = 'http://www.roblox.com/thumbs/avatar.ashx?userId='
--[[ Fast Flags ]]--
local followerSuccess, isFollowersEnabled = pcall(function() return settings():GetFFlag("EnableLuaFollowers") end)
local IsFollowersEnabled = followerSuccess and isFollowersEnabled
local serverFollowersSuccess, serverFollowersEnabled = pcall(function() return settings():GetFFlag("UserServerFollowers") end)
local IsServerFollowers = serverFollowersSuccess and serverFollowersEnabled
--[[ Modules ]]--
local RobloxGui = CoreGui:WaitForChild('RobloxGui')
local reportAbuseMenu = require(RobloxGui.Modules.Settings.Pages.ReportAbuseMenu)
--[[ Bindables ]]--
local BinbableFunction_SendNotification = nil
spawn(function()
BinbableFunction_SendNotification = RobloxGui:WaitForChild("SendNotification")
end)
--[[ Remotes ]]--
local RemoteEvent_NewFollower = nil
spawn(function()
local RobloxReplicatedStorage = game:GetService('RobloxReplicatedStorage')
RemoteEvent_NewFollower = RobloxReplicatedStorage:WaitForChild('NewFollower')
end)
--[[ Utility Functions ]]--
local function createSignal()
local sig = {}
local mSignaler = Instance.new('BindableEvent')
local mArgData = nil
local mArgDataCount = nil
function sig:fire(...)
mArgData = {...}
mArgDataCount = select('#', ...)
mSignaler:Fire()
end
function sig:connect(f)
if not f then error("connect(nil)", 2) end
return mSignaler.Event:connect(function()
f(unpack(mArgData, 1, mArgDataCount))
end)
end
function sig:wait()
mSignaler.Event:wait()
assert(mArgData, "Missing arg data, likely due to :TweenSize/Position corrupting threadrefs.")
return unpack(mArgData, 1, mArgDataCount)
end
return sig
end
--[[ Events ]]--
local BlockStatusChanged = createSignal()
--[[ Personal Server Stuff ]]--
local IsPersonalServer = false
local PersonalServerService = nil
if game.Workspace:FindFirstChild('PSVariable') then
IsPersonalServer = true
PersonalServerService = game:GetService('PersonalServerService')
end
game.Workspace.ChildAdded:connect(function(child)
if child.Name == 'PSVariable' and child:IsA('BoolValue') then
IsPersonalServer = true
PersonalServerService = game:GetService('PersonalServerService')
end
end)
local PRIVILEGE_LEVEL = {
OWNER = 255,
ADMIN = 240,
MEMBER = 128,
VISITOR = 10,
BANNED = 0,
}
local function onPrivilegeLevelSelect(player, rank)
while player.PersonalServerRank < rank do
PersonalServerService:Promote(player)
end
while player.PersonalServerRank > rank do
PersonalServerService:Demote(player)
end
end
--[[ Follower Notifications ]]--
local function sendNotification(title, text, image, duration, callback)
if BinbableFunction_SendNotification then
BinbableFunction_SendNotification:Invoke(title, text, image, duration, callback)
end
end
--[[ Friend Functions ]]--
local function getFriendStatus(selectedPlayer)
if selectedPlayer == LocalPlayer then
return Enum.FriendStatus.NotFriend
else
local success, result = pcall(function()
-- NOTE: Core script only
return LocalPlayer:GetFriendStatus(selectedPlayer)
end)
if success then
return result
else
return Enum.FriendStatus.NotFriend
end
end
end
-- if userId = nil, then it will get count for local player
local function getFriendCountAsync(userId)
local friendCount = nil
local wasSuccess, result = pcall(function()
local str = 'user/get-friendship-count'
if userId then
str = str..'?userId='..tostring(userId)
end
return HttpRbxApiService:GetAsync(str, true)
end)
if not wasSuccess then
print("getFriendCountAsync() failed because", result)
return nil
end
result = HttpService:JSONDecode(result)
if result["success"] and result["count"] then
friendCount = result["count"]
end
return friendCount
end
-- checks if we can send a friend request. Right now the only way we
-- can't is if one of the players is at the max friend limit
local function canSendFriendRequestAsync(otherPlayer)
local theirFriendCount = getFriendCountAsync(otherPlayer.userId)
local myFriendCount = getFriendCountAsync()
-- assume max friends if web call fails
if not myFriendCount or not theirFriendCount then
return false
end
if myFriendCount < MAX_FRIEND_COUNT and theirFriendCount < MAX_FRIEND_COUNT then
return true
elseif myFriendCount >= MAX_FRIEND_COUNT then
sendNotification("Cannot send friend request", "You are at the max friends limit.", "", 5, function() end)
return false
elseif theirFriendCount >= MAX_FRIEND_COUNT then
sendNotification("Cannot send friend request", otherPlayer.Name.." is at the max friends limit.", "", 5, function() end)
return false
end
end
--[[ Follower Functions ]]--
-- Returns whether followerUserId is following userId
local function isFollowing(userId, followerUserId)
local apiPath = "user/following-exists?userId="
local params = userId.."&followerUserId="..followerUserId
local success, result = pcall(function()
return HttpRbxApiService:GetAsync(apiPath..params, true)
end)
if not success then
print("isFollowing() failed because", result)
return false
end
-- can now parse web response
result = HttpService:JSONDecode(result)
return result["success"] and result["isFollowing"]
end
--[[ Functions for Blocking users ]]--
local BlockedList = {}
local MutedList = {}
local function GetBlockedPlayersAsync()
local userId = LocalPlayer.userId
local apiPath = "userblock/getblockedusers" .. "?" .. "userId=" .. tostring(userId) .. "&" .. "page=" .. "1"
if userId > 0 then
local blockList = nil
local success, msg = pcall(function()
local request = HttpRbxApiService:GetAsync(apiPath)
blockList = request and game:GetService('HttpService'):JSONDecode(request)
end)
if blockList and blockList['success'] == true and blockList['userList'] then
return blockList['userList']
end
end
return {}
end
spawn(function()
BlockedList = GetBlockedPlayersAsync()
end)
local function isBlocked(userId)
if (BlockedList[userId] ~= nil and BlockedList[userId] == true) then
return true
end
return false
end
local function isMuted(userId)
if (MutedList[userId] ~= nil and MutedList[userId] == true) then
return true
end
return false
end
local function BlockPlayerAsync(playerToBlock)
if playerToBlock and LocalPlayer ~= playerToBlock then
local blockUserId = playerToBlock.UserId
if blockUserId > 0 then
if not isBlocked(blockUserId) then
BlockedList[blockUserId] = true
BlockStatusChanged:fire(blockUserId, true)
pcall(function()
local success = PlayersService:BlockUser(LocalPlayer.userId, blockUserId)
end)
end
end
end
end
local function UnblockPlayerAsync(playerToUnblock)
if playerToUnblock then
local unblockUserId = playerToUnblock.userId
if isBlocked(unblockUserId) then
BlockedList[unblockUserId] = nil
BlockStatusChanged:fire(unblockUserId, false)
pcall(function()
local success = PlayersService:UnblockUser(LocalPlayer.userId, unblockUserId)
end)
end
end
end
local function MutePlayer(playerToMute)
if playerToMute and LocalPlayer ~= playerToMute then
local muteUserId = playerToMute.UserId
if muteUserId > 0 then
if not isMuted(muteUserId) then
MutedList[muteUserId] = true
end
end
end
end
local function UnmutePlayer(playerToUnmute)
if playerToUnmute then
local unmuteUserId = playerToUnmute.UserId
MutedList[unmuteUserId] = nil
end
end
--[[ Function to create DropDown class ]]--
function createPlayerDropDown()
local playerDropDown = {}
playerDropDown.Player = nil
playerDropDown.PopupFrame = nil
playerDropDown.HidePopupImmediately = false
playerDropDown.PopupFrameOffScreenPosition = nil -- if this is set the popup frame tweens to a different offscreen position than the default
playerDropDown.HiddenSignal = createSignal()
--[[ Functions for when options in the dropdown are pressed ]]--
local function onFriendButtonPressed()
if playerDropDown.Player then
local status = getFriendStatus(playerDropDown.Player)
if status == Enum.FriendStatus.Friend then
LocalPlayer:RevokeFriendship(playerDropDown.Player)
elseif status == Enum.FriendStatus.Unknown or status == Enum.FriendStatus.NotFriend then
-- cache and spawn
local cachedLastSelectedPlayer = playerDropDown.Player
spawn(function()
-- check for max friends before letting them send the request
if canSendFriendRequestAsync(cachedLastSelectedPlayer) then -- Yields
if cachedLastSelectedPlayer and cachedLastSelectedPlayer.Parent == PlayersService then
LocalPlayer:RequestFriendship(cachedLastSelectedPlayer)
end
end
end)
elseif status == Enum.FriendStatus.FriendRequestSent then
LocalPlayer:RevokeFriendship(playerDropDown.Player)
elseif status == Enum.FriendStatus.FriendRequestReceived then
LocalPlayer:RequestFriendship(playerDropDown.Player)
end
playerDropDown:Hide()
end
end
local function onDeclineFriendButonPressed()
if playerDropDown.Player then
LocalPlayer:RevokeFriendship(playerDropDown.Player)
playerDropDown:Hide()
end
end
-- Client unfollows followedUserId
local function onUnfollowButtonPressed()
if not playerDropDown.Player then return end
--
local apiPath = "user/unfollow"
local params = "followedUserId="..tostring(playerDropDown.Player.userId)
local success, result = pcall(function()
return HttpRbxApiService:PostAsync(apiPath, params, true, Enum.ThrottlingPriority.Default, Enum.HttpContentType.ApplicationUrlEncoded)
end)
if not success then
print("unfollowPlayer() failed because", result)
playerDropDown:Hide()
return
end
result = HttpService:JSONDecode(result)
if result["success"] then
if RemoteEvent_NewFollower then
RemoteEvent_NewFollower:FireServer(playerDropDown.Player, false)
end
moduleApiTable.FollowerStatusChanged:fire()
end
playerDropDown:Hide()
-- no need to send notification when someone unfollows
end
local function onBlockButtonPressed()
if playerDropDown.Player then
local cachedPlayer = playerDropDown.Player
spawn(function()
BlockPlayerAsync(cachedPlayer)
end)
playerDropDown:Hide()
end
end
local function onUnblockButtonPressed()
if playerDropDown.Player then
local cachedPlayer = playerDropDown.Player
spawn(function()
UnblockPlayerAsync(cachedPlayer)
end)
playerDropDown:Hide()
end
end
local function onReportButtonPressed()
if playerDropDown.Player then
reportAbuseMenu:ReportPlayer(playerDropDown.Player)
playerDropDown:Hide()
end
end
-- Client follows followedUserId
local function onFollowButtonPressed()
if not playerDropDown.Player then return end
--
local followedUserId = tostring(playerDropDown.Player.userId)
local apiPath = "user/follow"
local params = "followedUserId="..followedUserId
local success, result = pcall(function()
return HttpRbxApiService:PostAsync(apiPath, params, true, Enum.ThrottlingPriority.Default, Enum.HttpContentType.ApplicationUrlEncoded)
end)
if not success then
print("followPlayer() failed because", result)
playerDropDown:Hide()
return
end
result = HttpService:JSONDecode(result)
if result["success"] then
sendNotification("You are", "now following "..playerDropDown.Player.Name, FRIEND_IMAGE..followedUserId.."&x=48&y=48", 5, function() end)
if RemoteEvent_NewFollower then
RemoteEvent_NewFollower:FireServer(playerDropDown.Player, true)
end
moduleApiTable.FollowerStatusChanged:fire()
end
playerDropDown:Hide()
end
--[[ GUI Creation Functions ]]--
local function createPersonalServerDialog(buttons, selectedPlayer)
local showPersonalServerRanks = IsPersonalServer and LocalPlayer.PersonalServerRank >= PRIVILEGE_LEVEL.ADMIN and LocalPlayer.PersonalServerRank > selectedPlayer.PersonalServerRank
if showPersonalServerRanks then
table.insert(buttons, {
Name = "BanButton",
Text = "Ban",
OnPress = function()
playerDropDown:Hide()
onPrivilegeLevelSelect(selectedPlayer, PRIVILEGE_LEVEL.BANNED)
end,
})
table.insert(buttons, {
Name = "VistorButton",
Text = "Visitor",
OnPress = function()
onPrivilegeLevelSelect(selectedPlayer, PRIVILEGE_LEVEL.VISITOR)
end,
})
table.insert(buttons, {
Name = "MemberButton",
Text = "Member",
OnPress = function()
onPrivilegeLevelSelect(selectedPlayer, PRIVILEGE_LEVEL.MEMBER)
end,
})
table.insert(buttons, {
Name = "AdminButton",
Text = "Admin",
OnPress = function()
onPrivilegeLevelSelect(selectedPlayer, PRIVILEGE_LEVEL.ADMIN)
end,
})
end
end
local function createPopupFrame(buttons)
local frame = Instance.new('Frame')
frame.Name = "PopupFrame"
frame.Size = UDim2.new(1, 0, 0, (POPUP_ENTRY_SIZE_Y * #buttons) + (#buttons - ENTRY_PAD))
frame.Position = UDim2.new(1, 1, 0, 0)
frame.BackgroundTransparency = 1
for i,button in ipairs(buttons) do
local btn = Instance.new('TextButton')
btn.Name = button.Name
btn.Size = UDim2.new(1, 0, 0, POPUP_ENTRY_SIZE_Y)
btn.Position = UDim2.new(0, 0, 0, POPUP_ENTRY_SIZE_Y * (i - 1) + ((i - 1) * ENTRY_PAD))
btn.BackgroundTransparency = BG_TRANSPARENCY
btn.BackgroundColor3 = BG_COLOR
btn.BorderSizePixel = 0
btn.Text = button.Text
btn.Font = Enum.Font.SourceSans
btn.FontSize = Enum.FontSize.Size14
btn.TextColor3 = TEXT_COLOR
btn.TextStrokeTransparency = TEXT_STROKE_TRANSPARENCY
btn.TextStrokeColor3 = TEXT_STROKE_COLOR
btn.AutoButtonColor = true
btn.Parent = frame
btn.MouseButton1Click:connect(button.OnPress)
end
return frame
end
--[[ PlayerDropDown Functions ]]--
function playerDropDown:Hide()
if playerDropDown.PopupFrame then
local offscreenPosition = (playerDropDown.PopupFrameOffScreenPosition ~= nil and playerDropDown.PopupFrameOffScreenPosition or UDim2.new(1, 1, 0, playerDropDown.PopupFrame.Position.Y.Offset))
if not playerDropDown.HidePopupImmediately then
playerDropDown.PopupFrame:TweenPosition(offscreenPosition, Enum.EasingDirection.InOut,
Enum.EasingStyle.Quad, TWEEN_TIME, true, function()
if playerDropDown.PopupFrame then
playerDropDown.PopupFrame:Destroy()
playerDropDown.PopupFrame = nil
end
end)
else
playerDropDown.PopupFrame:Destroy()
playerDropDown.PopupFrame = nil
end
end
if playerDropDown.Player then
playerDropDown.Player = nil
end
playerDropDown.HiddenSignal:fire()
end
function playerDropDown:CreatePopup(Player)
playerDropDown.Player = Player
local buttons = {}
local status = getFriendStatus(playerDropDown.Player)
local friendText = ""
local canDeclineFriend = false
if status == Enum.FriendStatus.Friend then
friendText = "Unfriend Player"
elseif status == Enum.FriendStatus.Unknown or status == Enum.FriendStatus.NotFriend then
friendText = "Send Friend Request"
elseif status == Enum.FriendStatus.FriendRequestSent then
friendText = "Revoke Friend Request"
elseif status == Enum.FriendStatus.FriendRequestReceived then
friendText = "Accept Friend Request"
canDeclineFriend = true
end
local blocked = isBlocked(playerDropDown.Player.userId)
if not blocked then
table.insert(buttons, {
Name = "FriendButton",
Text = friendText,
OnPress = onFriendButtonPressed,
})
end
if canDeclineFriend and not blocked then
table.insert(buttons, {
Name = "DeclineFriend",
Text = "Decline Friend Request",
OnPress = onDeclineFriendButonPressed,
})
end
-- following status
if IsServerFollowers or IsFollowersEnabled then
local following = isFollowing(playerDropDown.Player.userId, LocalPlayer.userId)
local followerText = following and "Unfollow Player" or "Follow Player"
if not blocked then
table.insert(buttons, {
Name = "FollowerButton",
Text = followerText,
OnPress = following and onUnfollowButtonPressed or onFollowButtonPressed,
})
end
end
local blockedText = blocked and "Unblock Player" or "Block Player"
table.insert(buttons, {
Name = "BlockButton",
Text = blockedText,
OnPress = blocked and onUnblockButtonPressed or onBlockButtonPressed,
})
table.insert(buttons, {
Name = "ReportButton",
Text = "Report Abuse",
OnPress = onReportButtonPressed,
})
createPersonalServerDialog(buttons, playerDropDown.Player)
if playerDropDown.PopupFrame then
playerDropDown.PopupFrame:Destroy()
end
playerDropDown.PopupFrame = createPopupFrame(buttons)
return playerDropDown.PopupFrame
end
--[[ PlayerRemoving Connection ]]--
PlayersService.PlayerRemoving:connect(function(leavingPlayer)
if playerDropDown.Player == leavingPlayer then
playerDropDown:Hide()
end
end)
return playerDropDown
end
do
moduleApiTable.FollowerStatusChanged = createSignal()
function moduleApiTable:CreatePlayerDropDown()
return createPlayerDropDown()
end
function moduleApiTable:CreateBlockingUtility()
local blockingUtility = {}
function blockingUtility:BlockPlayerAsync(player)
return BlockPlayerAsync(player)
end
function blockingUtility:UnblockPlayerAsync(player)
return UnblockPlayerAsync(player)
end
function blockingUtility:MutePlayer(player)
return MutePlayer(player)
end
function blockingUtility:UnmutePlayer(player)
return UnmutePlayer(player)
end
function blockingUtility:IsPlayerBlockedByUserId(userId)
return isBlocked(userId)
end
function blockingUtility:GetBlockedStatusChangedEvent()
return BlockStatusChanged
end
function blockingUtility:IsPlayerMutedByUserId(userId)
return isMuted(userId)
end
return blockingUtility
end
end
return moduleApiTable
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,790 @@
--[[
Filename: GameSettings.lua
Written by: jeditkacheff
Version 1.1
Description: Takes care of the Game Settings Tab in Settings Menu
--]]
-------------- SERVICES --------------
local CoreGui = game:GetService("CoreGui")
local RobloxGui = CoreGui:WaitForChild("RobloxGui")
local GuiService = game:GetService("GuiService")
local UserInputService = game:GetService("UserInputService")
local PlatformService = nil
pcall(function() PlatformService = game:GetService("PlatformService") end)
local ContextActionService = game:GetService("ContextActionService")
local StarterGui = game:GetService("StarterGui")
local Settings = UserSettings()
local GameSettings = Settings.GameSettings
-------------- CONSTANTS --------------
local GRAPHICS_QUALITY_LEVELS = 10
local GRAPHICS_QUALITY_TO_INT = {
["Enum.SavedQualitySetting.Automatic"] = 0,
["Enum.SavedQualitySetting.QualityLevel1"] = 1,
["Enum.SavedQualitySetting.QualityLevel2"] = 2,
["Enum.SavedQualitySetting.QualityLevel3"] = 3,
["Enum.SavedQualitySetting.QualityLevel4"] = 4,
["Enum.SavedQualitySetting.QualityLevel5"] = 5,
["Enum.SavedQualitySetting.QualityLevel6"] = 6,
["Enum.SavedQualitySetting.QualityLevel7"] = 7,
["Enum.SavedQualitySetting.QualityLevel8"] = 8,
["Enum.SavedQualitySetting.QualityLevel9"] = 9,
["Enum.SavedQualitySetting.QualityLevel10"] = 10,
}
local PC_CHANGED_PROPS = {
DevComputerMovementMode = true,
DevComputerCameraMode = true,
DevEnableMouseLock = true,
}
local TOUCH_CHANGED_PROPS = {
DevTouchMovementMode = true,
DevTouchCameraMode = true,
}
local CAMERA_MODE_DEFAULT_STRING = UserInputService.TouchEnabled and "Default (Follow)" or "Default (Classic)"
local MOVEMENT_MODE_DEFAULT_STRING = UserInputService.TouchEnabled and "Default (Thumbstick)" or "Default (Keyboard)"
local MOVEMENT_MODE_KEYBOARDMOUSE_STRING = "Keyboard + Mouse"
local MOVEMENT_MODE_CLICKTOMOVE_STRING = UserInputService.TouchEnabled and "Tap to Move" or "Click to Move"
----------- UTILITIES --------------
local utility = require(RobloxGui.Modules.Settings.Utility)
------------ Variables -------------------
RobloxGui:WaitForChild("Modules"):WaitForChild("TenFootInterface")
RobloxGui:WaitForChild("Modules"):WaitForChild("Settings"):WaitForChild("SettingsHub")
local isTenFootInterface = require(RobloxGui.Modules.TenFootInterface):IsEnabled()
local HasVRAPI = false
pcall(function() HasVRAPI = UserInputService.GetUserCFrame ~= nil end)
local PageInstance = nil
local LocalPlayer = game.Players.LocalPlayer
local platform = UserInputService:GetPlatform()
local overscanScreen = nil
----------- CLASS DECLARATION --------------
local function Initialize()
local settingsPageFactory = require(RobloxGui.Modules.Settings.SettingsPageFactory)
local this = settingsPageFactory:CreateNewPage()
----------- FUNCTIONS ---------------
local function createGraphicsOptions()
------------------ Fullscreen Selection GUI Setup ------------------
local fullScreenInit = 1
if not GameSettings:InFullScreen() then
fullScreenInit = 2
end
this.FullscreenFrame,
this.FullscreenLabel,
this.FullscreenEnabler = utility:AddNewRow(this, "Fullscreen", "Selector", {"On", "Off"}, fullScreenInit)
local fullScreenSelectionFrame = this.FullscreenEnabler.SliderFrame and this.FullscreenEnabler.SliderFrame or this.FullscreenEnabler.SelectorFrame
this.FullscreenEnabler.IndexChanged:connect(function(newIndex)
GuiService:ToggleFullscreen()
end)
------------------ Gfx Enabler Selection GUI Setup ------------------
local graphicsEnablerStart = 1
if GameSettings.SavedQualityLevel ~= Enum.SavedQualitySetting.Automatic then
graphicsEnablerStart = 2
end
this.GraphicsEnablerFrame,
this.GraphicsEnablerLabel,
this.GraphicsQualityEnabler = utility:AddNewRow(this, "Graphics Mode", "Selector", {"Automatic", "Manual"}, graphicsEnablerStart)
------------------ Gfx Slider GUI Setup ------------------
this.GraphicsQualityFrame,
this.GraphicsQualityLabel,
this.GraphicsQualitySlider = utility:AddNewRow(this, "Graphics Quality", "Slider", GRAPHICS_QUALITY_LEVELS, 1)
this.GraphicsQualitySlider:SetMinStep(1)
------------------------- Connection Setup ----------------------------
settings().Rendering.EnableFRM = true
function SetGraphicsQuality(newValue, automaticSettingAllowed)
local percentage = newValue/GRAPHICS_QUALITY_LEVELS
local newQualityLevel = math.floor((settings().Rendering:GetMaxQualityLevel() - 1) * percentage)
if newQualityLevel == 20 then
newQualityLevel = 21
elseif newValue == 1 then
newQualityLevel = 1
elseif newValue < 1 and not automaticSettingAllowed then
newValue = 1
newQualityLevel = 1
elseif newQualityLevel > settings().Rendering:GetMaxQualityLevel() then
newQualityLevel = settings().Rendering:GetMaxQualityLevel() - 1
end
GameSettings.SavedQualityLevel = newValue
settings().Rendering.QualityLevel = newQualityLevel
end
local function setGraphicsToAuto()
this.GraphicsQualitySlider:SetZIndex(1)
this.GraphicsQualityLabel.ZIndex = 1
this.GraphicsQualitySlider:SetInteractable(false)
SetGraphicsQuality(Enum.QualityLevel.Automatic.Value, true)
end
local function setGraphicsToManual(level)
this.GraphicsQualitySlider:SetZIndex(2)
this.GraphicsQualityLabel.ZIndex = 2
this.GraphicsQualitySlider:SetInteractable(true)
-- need to force the quality change if slider is already at this position
if this.GraphicsQualitySlider:GetValue() == level then
SetGraphicsQuality(level)
else
this.GraphicsQualitySlider:SetValue(level)
end
end
game.GraphicsQualityChangeRequest:connect(function(isIncrease)
if settings().Rendering.QualityLevel == Enum.QualityLevel.Automatic then return end
--
local currentGraphicsSliderValue = this.GraphicsQualitySlider:GetValue()
if isIncrease then
currentGraphicsSliderValue = currentGraphicsSliderValue + 1
else
currentGraphicsSliderValue = currentGraphicsSliderValue - 1
end
this.GraphicsQualitySlider:SetValue(currentGraphicsSliderValue)
end)
this.GraphicsQualitySlider.ValueChanged:connect(function(newValue)
SetGraphicsQuality(newValue)
end)
this.GraphicsQualityEnabler.IndexChanged:connect(function(newIndex)
if newIndex == 1 then
setGraphicsToAuto()
elseif newIndex == 2 then
setGraphicsToManual( this.GraphicsQualitySlider:GetValue() )
end
end)
-- initialize the slider position
if GameSettings.SavedQualityLevel == Enum.SavedQualitySetting.Automatic then
this.GraphicsQualitySlider:SetValue(5)
setGraphicsToAuto()
else
local graphicsLevel = tostring(GameSettings.SavedQualityLevel)
if GRAPHICS_QUALITY_TO_INT[graphicsLevel] then
graphicsLevel = GRAPHICS_QUALITY_TO_INT[graphicsLevel]
else
graphicsLevel = GRAPHICS_QUALITY_LEVELS
end
spawn(function()
this.GraphicsQualitySlider:SetValue(graphicsLevel)
end)
end
end
local function createCameraModeOptions(movementModeEnabled)
------------------------------------------------------
------------------
------------------ Shift Lock Switch -----------------
if UserInputService.MouseEnabled then
this.ShiftLockFrame,
this.ShiftLockLabel,
this.ShiftLockMode,
this.ShiftLockOverrideText = nil
if UserInputService.MouseEnabled and UserInputService.KeyboardEnabled then
local startIndex = 2
if GameSettings.ControlMode == Enum.ControlMode.MouseLockSwitch then
startIndex = 1
end
this.ShiftLockFrame,
this.ShiftLockLabel,
this.ShiftLockMode = utility:AddNewRow(this, "Shift Lock Switch", "Selector", {"On", "Off"}, startIndex)
this.ShiftLockOverrideText = utility:Create'TextLabel'
{
Name = "ShiftLockOverrideLabel",
Text = "Set by Developer",
TextColor3 = Color3.new(1,1,1),
Font = Enum.Font.SourceSans,
FontSize = Enum.FontSize.Size24,
BackgroundTransparency = 1,
Size = UDim2.new(0,200,1,0),
Position = UDim2.new(1,-350,0,0),
Visible = false,
ZIndex = 2,
Parent = this.ShiftLockFrame
};
this.ShiftLockMode.IndexChanged:connect(function(newIndex)
if newIndex == 1 then
GameSettings.ControlMode = Enum.ControlMode.MouseLockSwitch
else
GameSettings.ControlMode = Enum.ControlMode.Classic
end
end)
end
end
------------------------------------------------------
------------------
------------------ Camera Mode -----------------------
do
local enumItems = nil
local startingCameraEnumItem = 1
if UserInputService.TouchEnabled then
enumItems = Enum.TouchCameraMovementMode:GetEnumItems()
else
enumItems = Enum.ComputerCameraMovementMode:GetEnumItems()
end
local cameraEnumNames = {}
local cameraEnumNameToItem = {}
for i = 1, #enumItems do
local displayName = enumItems[i].Name
if displayName == 'Default' then
displayName = CAMERA_MODE_DEFAULT_STRING
end
if UserInputService.TouchEnabled then
if GameSettings.TouchCameraMovementMode == enumItems[i] then
startingCameraEnumItem = i
end
else
if GameSettings.ComputerCameraMovementMode == enumItems[i] then
startingCameraEnumItem = i
end
end
cameraEnumNames[i] = displayName
cameraEnumNameToItem[displayName] = enumItems[i].Value
end
this.CameraModeFrame,
this.CameraModeLabel,
this.CameraMode = utility:AddNewRow(this, "Camera Mode", "Selector", cameraEnumNames, startingCameraEnumItem)
this.CameraModeOverrideText = utility:Create'TextLabel'
{
Name = "CameraDevOverrideLabel",
Text = "Set by Developer",
TextColor3 = Color3.new(1,1,1),
Font = Enum.Font.SourceSans,
FontSize = Enum.FontSize.Size24,
BackgroundTransparency = 1,
Size = UDim2.new(0,200,1,0),
Position = UDim2.new(1,-350,0,0),
Visible = false,
ZIndex = 2,
Parent = this.CameraModeFrame
};
this.CameraMode.IndexChanged:connect(function(newIndex)
local newEnumSetting = cameraEnumNameToItem[cameraEnumNames[newIndex]]
if UserInputService.TouchEnabled then
GameSettings.TouchCameraMovementMode = newEnumSetting
else
GameSettings.ComputerCameraMovementMode = newEnumSetting
end
end)
end
------------------------------------------------------
------------------
------------------ VR Camera Mode -----------------------
if HasVRAPI and UserInputService.VREnabled then
local VR_ROTATION_INTENSITY_OPTIONS = {"Low", "High", "Smooth"}
if utility:IsSmallTouchScreen() then
this.VRRotationFrame,
this.VRRotationLabel,
this.VRRotationMode = utility:AddNewRow(this, "VR Camera Rotation", "Selector", VR_ROTATION_INTENSITY_OPTIONS, GameSettings.VRRotationIntensity)
else
this.VRRotationFrame,
this.VRRotationLabel,
this.VRRotationMode = utility:AddNewRow(this, "VR Camera Rotation", "Selector", VR_ROTATION_INTENSITY_OPTIONS, GameSettings.VRRotationIntensity, 3)
end
StarterGui:RegisterGetCore("VRRotationIntensity",
function()
return VR_ROTATION_INTENSITY_OPTIONS[GameSettings.VRRotationIntensity] or VR_ROTATION_INTENSITY_OPTIONS[1]
end)
this.VRRotationMode.IndexChanged:connect(function(newIndex)
GameSettings.VRRotationIntensity = newIndex
end)
end
------------------------------------------------------
------------------
------------------ Movement Mode ---------------------
if movementModeEnabled then
local movementEnumItems = nil
local startingMovementEnumItem = 1
if UserInputService.TouchEnabled then
movementEnumItems = Enum.TouchMovementMode:GetEnumItems()
else
movementEnumItems = Enum.ComputerMovementMode:GetEnumItems()
end
local movementEnumNames = {}
local movementEnumNameToItem = {}
for i = 1, #movementEnumItems do
local displayName = movementEnumItems[i].Name
if displayName == "Default" then
displayName = MOVEMENT_MODE_DEFAULT_STRING
elseif displayName == "KeyboardMouse" then
displayName = MOVEMENT_MODE_KEYBOARDMOUSE_STRING
elseif displayName == "ClickToMove" then
displayName = MOVEMENT_MODE_CLICKTOMOVE_STRING
end
if UserInputService.TouchEnabled then
if GameSettings.TouchMovementMode == movementEnumItems[i] then
startingMovementEnumItem = i
end
else
if GameSettings.ComputerMovementMode == movementEnumItems[i] then
startingMovementEnumItem = i
end
end
movementEnumNames[i] = displayName
movementEnumNameToItem[displayName] = movementEnumItems[i]
end
this.MovementModeFrame,
this.MovementModeLabel,
this.MovementMode = utility:AddNewRow(this, "Movement Mode", "Selector", movementEnumNames, startingMovementEnumItem)
this.MovementModeOverrideText = utility:Create'TextLabel'
{
Name = "MovementDevOverrideLabel",
Text = "Set by Developer",
TextColor3 = Color3.new(1,1,1),
Font = Enum.Font.SourceSans,
FontSize = Enum.FontSize.Size24,
BackgroundTransparency = 1,
Size = UDim2.new(0,200,1,0),
Position = UDim2.new(1,-350,0,0),
Visible = false,
ZIndex = 2,
Parent = this.MovementModeFrame
};
this.MovementMode.IndexChanged:connect(function(newIndex)
local newEnumSetting = movementEnumNameToItem[movementEnumNames[newIndex]]
if UserInputService.TouchEnabled then
GameSettings.TouchMovementMode = newEnumSetting
else
GameSettings.ComputerMovementMode = newEnumSetting
end
end)
end
------------------------------------------------------
------------------
------------------------- Connection Setup -----------
function setCameraModeVisible(visible)
if this.CameraMode then
this.CameraMode.SelectorFrame.Visible = visible
this.CameraMode:SetInteractable(visible)
end
end
function setMovementModeVisible(visible)
if this.MovementMode then
this.MovementMode.SelectorFrame.Visible = visible
this.MovementMode:SetInteractable(visible)
end
end
function setShiftLockVisible(visible)
if this.ShiftLockMode then
this.ShiftLockMode.SelectorFrame.Visible = visible
this.ShiftLockMode:SetInteractable(visible)
end
end
do -- initial set of dev vs user choice for guis
local isUserChoiceCamera = false
if UserInputService.TouchEnabled then
isUserChoiceCamera = LocalPlayer.DevTouchCameraMode == Enum.DevTouchCameraMovementMode.UserChoice
else
isUserChoiceCamera = LocalPlayer.DevComputerCameraMode == Enum.DevComputerCameraMovementMode.UserChoice
end
if not isUserChoiceCamera then
this.CameraModeOverrideText.Visible = true
setCameraModeVisible(false)
else
this.CameraModeOverrideText.Visible = false
setCameraModeVisible(true)
end
local isUserChoiceMovement = false
if UserInputService.TouchEnabled then
isUserChoiceMovement = LocalPlayer.DevTouchMovementMode == Enum.DevTouchMovementMode.UserChoice
else
isUserChoiceMovement = LocalPlayer.DevComputerMovementMode == Enum.DevComputerMovementMode.UserChoice
end
if this.MovementModeOverrideText then
if not isUserChoiceMovement then
this.MovementModeOverrideText.Visible = true
setMovementModeVisible(false)
else
this.MovementModeOverrideText.Visible = false
setMovementModeVisible(true)
end
end
if this.ShiftLockOverrideText then
this.ShiftLockOverrideText.Visible = not LocalPlayer.DevEnableMouseLock
setShiftLockVisible(LocalPlayer.DevEnableMouseLock)
end
end
local function updateUserSettingsMenu(property)
if this.ShiftLockOverrideText and property == "DevEnableMouseLock" then
this.ShiftLockOverrideText.Visible = not LocalPlayer.DevEnableMouseLock
setShiftLockVisible(LocalPlayer.DevEnableMouseLock)
elseif property == "DevComputerCameraMode" then
local isUserChoice = LocalPlayer.DevComputerCameraMode == Enum.DevComputerCameraMovementMode.UserChoice
setCameraModeVisible(isUserChoice)
this.CameraModeOverrideText.Visible = not isUserChoice
elseif property == "DevComputerMovementMode" then
local isUserChoice = LocalPlayer.DevComputerMovementMode == Enum.DevComputerMovementMode.UserChoice
setMovementModeVisible(isUserChoice)
if this.MovementModeOverrideText then
this.MovementModeOverrideText.Visible = not isUserChoice
end
-- TOUCH
elseif property == "DevTouchMovementMode" then
local isUserChoice = LocalPlayer.DevTouchMovementMode == Enum.DevTouchMovementMode.UserChoice
setMovementModeVisible(isUserChoice)
if this.MovementModeOverrideText then
this.MovementModeOverrideText.Visible = not isUserChoice
end
elseif property == "DevTouchCameraMode" then
local isUserChoice = LocalPlayer.DevTouchCameraMode == Enum.DevTouchCameraMovementMode.UserChoice
setCameraModeVisible(isUserChoice)
this.CameraModeOverrideText.Visible = not isUserChoice
end
end
LocalPlayer.Changed:connect(function(property)
if IsTouchClient then
if TOUCH_CHANGED_PROPS[property] then
updateUserSettingsMenu(property)
end
else
if PC_CHANGED_PROPS[property] then
updateUserSettingsMenu(property)
end
end
end)
end
local function createVolumeOptions()
local startVolumeLevel = math.floor(GameSettings.MasterVolume * 10)
this.VolumeFrame,
this.VolumeLabel,
this.VolumeSlider = utility:AddNewRow(this, "Volume", "Slider", 10, startVolumeLevel)
local volumeSound = Instance.new("Sound", game.CoreGui.RobloxGui.Sounds)
volumeSound.Name = "VolumeChangeSound"
volumeSound.SoundId = "rbxasset://sounds/metalstone2.mp3"
this.VolumeSlider.ValueChanged:connect(function(newValue)
local soundPercent = newValue/10
volumeSound.Volume = soundPercent
volumeSound:Play()
GameSettings.MasterVolume = soundPercent
end)
end
local function createMouseOptions()
local MouseSteps = 10
local MinMouseSensitivity = 0.2
local AdvancedSuccess, AdvancedValue = pcall(function() return settings():GetFFlag("AdvancedMouseSensitivityEnabled") end)
local AdvancedEnabled = AdvancedSuccess and AdvancedValue
-- equations below map a function to include points (0, 0.2) (5, 1) (10, 4)
-- where x is the slider position, y is the mouse sensitivity
local function translateEngineMouseSensitivityToGui(engineSensitivity)
return math.floor((2.0/3.0) * (math.sqrt(75.0 * engineSensitivity - 11.0) - 2))
end
local function translateGuiMouseSensitivityToEngine(guiSensitivity)
return 0.03 * math.pow(guiSensitivity,2) + (0.08 * guiSensitivity) + MinMouseSensitivity
end
local startMouseLevel = translateEngineMouseSensitivityToGui(GameSettings.MouseSensitivity)
------------------ Mouse Selection GUI Setup ------------------
-- switch between basic mode and advanced mode.
local MouseModeEnablerStart = 2
if GameSettings.UseBasicMouseSensitivity or not AdvancedEnabled then
MouseModeEnablerStart = 1
end
-- auto-detect mouse invert
local MouseInvertStart = 1
if GameSettings.MouseSensitivityFirstPerson.y < 0 then
MouseInvertStart = 2
end
if AdvancedEnabled then
this.MouseModeFrame,
this.MouseModeLabel,
this.MouseModeEnabler = utility:AddNewRow(this, "Mouse Sensitivity Mode", "Selector", {"Basic", "Advanced"}, MouseModeEnablerStart)
end
------------------ Basic Mouse Sensitivity Slider ------------------
-- basic quantized sensitivity with a weird number of settings.
local SliderLabel = "Basic Mouse Sensitivity"
if not AdvancedEnabled then
SliderLabel = "Mouse Sensitivity"
end
this.MouseSensitivityFrame,
this.MouseSensitivityLabel,
this.MouseSensitivitySlider = utility:AddNewRow(this, SliderLabel, "Slider", MouseSteps, startMouseLevel)
this.MouseSensitivitySlider:SetMinStep(1)
this.MouseSensitivitySlider.ValueChanged:connect(function(newValue)
GameSettings.MouseSensitivity = translateGuiMouseSensitivityToEngine(newValue)
end)
------------------ 3D Sensitivity ------------------
-- affects both first and third person.
if AdvancedEnabled then
local MouseAdvancedStart = tostring(GameSettings.MouseSensitivityFirstPerson.y)
this.MouseAdvancedFrame,
this.MouseAdvancedLabel,
this.MouseAdvancedEntry = utility:AddNewRow(this, "Advanced Mouse Sensitivity", "TextEntry", 1.0, 1.0, MouseAdvancedStart)
this.MouseAdvancedEntry.ValueChanged:connect(function(newValueText)
local currentFirstSensitivity = GameSettings.MouseSensitivityFirstPerson
local currentThirdSensitivity = GameSettings.MouseSensitivityThirdPerson
local newValue = tonumber(newValueText)
if not newValue then
this.MouseAdvancedEntry:SetValue(string.format("%.3f",currentFirstSensitivity.x))
return
end
-- inverted mouse will be handled later
if newValue < 0.0 then
newValue = -newValue
end
-- * assume a minimum that allows a 16000 dpi mouse a full 800mm travel for 360deg
-- ~0.0029: min of 0.001 seems ok.
-- * assume a max that allows a 400 dpi mouse a 360deg travel in 10mm
-- ~9.2: max of 10 seems ok, but users will want to have a bit of fun with crazy settings.
if newValue > 100.0 then
newValue = 100.0
elseif newValue < 0.001 then
newValue = 0.001
end
-- try to keep ratios the same, even though they aren't exposed to the GUI
local firstPersonX = newValue
local firstPersonY = newValue * (currentFirstSensitivity.y / currentFirstSensitivity.x)
local thirdPersonX = newValue * (currentFirstSensitivity.x / currentThirdSensitivity.x)
local thirdPersonY = thirdPersonX * (currentThirdSensitivity.y / currentThirdSensitivity.x)
-- handle invert
if currentFirstSensitivity.y < 0.0 then
firstPersonY = -firstPersonY
end
if currentThirdSensitivity.y < 0.0 then
thirdPersonY = -thirdPersonY
end
GameSettings.MouseSensitivityFirstPerson = Vector2.new(firstPersonX, firstPersonY)
GameSettings.MouseSensitivityThirdPerson = Vector2.new(thirdPersonX, thirdPersonY)
this.MouseAdvancedEntry:SetValue(string.format("%.3f",firstPersonX))
end)
end
------------------ Mouse Invert ------------------
-- This is a common setting in games, even if it is rare
if AdvancedEnabled then
this.MouseInvertFrame,
this.MouseInvertLabel,
this.MouseInvertEnabler = utility:AddNewRow(this, "Advanced Mouse Invert", "Selector", {"Normal", "Inverted"}, MouseInvertStart)
this.MouseInvertEnabler.IndexChanged:connect(function(newIndex)
local currentFirstSensitivity = GameSettings.MouseSensitivityFirstPerson
local currentThirdSensitivity = GameSettings.MouseSensitivityThirdPerson
if newIndex == 1 then
if currentFirstSensitivity.y < 0.0 then
currentFirstSensitivity = Vector2.new(currentFirstSensitivity.x, -currentFirstSensitivity.y)
currentThirdSensitivity = Vector2.new(currentThirdSensitivity.x, -currentThirdSensitivity.y)
end
elseif newIndex == 2 then
if currentFirstSensitivity.y > 0.0 then
currentFirstSensitivity = Vector2.new(currentFirstSensitivity.x, -currentFirstSensitivity.y)
currentThirdSensitivity = Vector2.new(currentThirdSensitivity.x, -currentThirdSensitivity.y)
end
end
GameSettings.MouseSensitivityFirstPerson = currentFirstSensitivity
GameSettings.MouseSensitivityThirdPerson = currentThirdSensitivity
end)
end
------------------ Init ------------------
if AdvancedEnabled then
local function setMouseModeToBasic()
this.MouseSensitivitySlider:SetZIndex(2)
this.MouseSensitivityLabel.ZIndex = 2
this.MouseSensitivitySlider:SetInteractable(true)
this.MouseSensitivitySlider:SetValue(translateEngineMouseSensitivityToGui(GameSettings.MouseSensitivity))
this.MouseAdvancedLabel.ZIndex = 1
this.MouseAdvancedEntry:SetInteractable(false)
this.MouseInvertLabel.ZIndex = 1
this.MouseInvertEnabler:SetInteractable(false)
end
local function setMouseModeToAdvanced()
this.MouseSensitivitySlider:SetZIndex(1)
this.MouseSensitivityLabel.ZIndex = 1
this.MouseSensitivitySlider:SetInteractable(false)
this.MouseAdvancedLabel.ZIndex = 2
this.MouseAdvancedEntry:SetInteractable(true)
local MouseSensitivity3d = GameSettings.MouseSensitivityFirstPerson
this.MouseAdvancedEntry:SetValue(tostring(MouseSensitivity3d.x));
this.MouseInvertLabel.ZIndex = 2
this.MouseInvertEnabler:SetInteractable(true)
end
this.MouseModeEnabler.IndexChanged:connect(function(newIndex)
if newIndex == 1 then
GameSettings.UseBasicMouseSensitivity = true
setMouseModeToBasic()
elseif newIndex == 2 then
GameSettings.UseBasicMouseSensitivity = false
setMouseModeToAdvanced()
end
end)
if GameSettings.UseBasicMouseSensitivity then
setMouseModeToBasic()
this.MouseAdvancedEntry:SetValue(tostring(MouseAdvancedStart))
else
setMouseModeToAdvanced()
end
end
end
local function createOverscanOption()
local showOverscanScreen = function()
if not overscanScreen then
local createOverscanFunc = require(RobloxGui.Modules.OverscanScreen)
overscanScreen = createOverscanFunc(RobloxGui)
overscanScreen:SetStyleForInGame()
end
local MenuModule = require(RobloxGui.Modules.Settings.SettingsHub)
MenuModule:SetVisibility(false, true)
local closedCon = nil
closedCon = overscanScreen.Closed:connect(function()
closedCon:disconnect()
pcall(function() PlatformService.BlurIntensity = 0 end)
ContextActionService:UnbindCoreAction("RbxStopOverscanMovement")
MenuModule:SetVisibility(true, true)
end)
pcall(function() PlatformService.BlurIntensity = 10 end)
local noOpFunc = function() end
ContextActionService:BindCoreAction("RbxStopOverscanMovement", noOpFunc, false,
Enum.UserInputType.Gamepad1, Enum.UserInputType.Gamepad2,
Enum.UserInputType.Gamepad3, Enum.UserInputType.Gamepad4)
local ScreenManager = require(RobloxGui.Modules.ScreenManager)
ScreenManager:OpenScreen(overscanScreen)
end
local adjustButton, adjustText, setButtonRowRef = utility:MakeStyledButton("AdjustButton", "Adjust", UDim2.new(0,300,1,-20), showOverscanScreen, this)
adjustText.Font = Enum.Font.SourceSans
adjustButton.Position = UDim2.new(1,-400,0,12)
local row = utility:AddNewRowObject(this, "Safe Zone", adjustButton)
setButtonRowRef(row)
end
createCameraModeOptions(not isTenFootInterface and
(UserInputService.TouchEnabled or UserInputService.MouseEnabled or UserInputService.KeyboardEnabled))
if UserInputService.MouseEnabled then
createMouseOptions()
end
createVolumeOptions()
if platform == Enum.Platform.Windows or platform == Enum.Platform.UWP or platform == Enum.Platform.OSX then
createGraphicsOptions()
end
if isTenFootInterface then
createOverscanOption()
end
------ TAB CUSTOMIZATION -------
this.TabHeader.Name = "GameSettingsTab"
this.TabHeader.Icon.Image = "rbxasset://textures/ui/Settings/MenuBarIcons/GameSettingsTab.png"
if utility:IsSmallTouchScreen() then
this.TabHeader.Icon.Size = UDim2.new(0,34,0,34)
this.TabHeader.Icon.Position = UDim2.new(this.TabHeader.Icon.Position.X.Scale,this.TabHeader.Icon.Position.X.Offset,0.5,-17)
this.TabHeader.Size = UDim2.new(0,125,1,0)
elseif isTenFootInterface then
this.TabHeader.Icon.Image = "rbxasset://textures/ui/Settings/MenuBarIcons/GameSettingsTab@2x.png"
this.TabHeader.Icon.Size = UDim2.new(0,90,0,90)
this.TabHeader.Icon.Position = UDim2.new(0,0,0.5,-43)
this.TabHeader.Size = UDim2.new(0,280,1,0)
else
this.TabHeader.Icon.Size = UDim2.new(0,45,0,45)
this.TabHeader.Icon.Position = UDim2.new(0,15,0.5,-22)
end
this.TabHeader.Icon.Title.Text = "Settings"
------ PAGE CUSTOMIZATION -------
this.Page.ZIndex = 5
return this
end
----------- Page Instantiation --------------
PageInstance = Initialize()
return PageInstance
@@ -0,0 +1,489 @@
--[[
Filename: Help.lua
Written by: jeditkacheff
Version 1.0
Description: Takes care of the help page in Settings Menu
--]]
-------------- CONSTANTS --------------
local KEYBOARD_MOUSE_TAG = "KeyboardMouse"
local TOUCH_TAG = "Touch"
local GAMEPAD_TAG = "Gamepad"
local PC_TABLE_SPACING = 4
-------------- SERVICES --------------
local CoreGui = game:GetService("CoreGui")
local RobloxGui = CoreGui:WaitForChild("RobloxGui")
local UserInputService = game:GetService("UserInputService")
local GuiService = game:GetService("GuiService")
----------- UTILITIES --------------
local utility = require(RobloxGui.Modules.Settings.Utility)
------------ Variables -------------------
local PageInstance = nil
RobloxGui:WaitForChild("Modules"):WaitForChild("TenFootInterface")
local isTenFootInterface = require(RobloxGui.Modules.TenFootInterface):IsEnabled()
----------- CLASS DECLARATION --------------
local function Initialize()
local settingsPageFactory = require(RobloxGui.Modules.Settings.SettingsPageFactory)
local this = settingsPageFactory:CreateNewPage()
this.HelpPages = {}
-- TODO: Change dev console script to parent this to somewhere other than an engine created gui
local ControlFrame = RobloxGui:WaitForChild('ControlFrame')
local ToggleDevConsoleBindableFunc = ControlFrame:WaitForChild('ToggleDevConsole')
local lastInputType = nil
function this:GetCurrentInputType()
if lastInputType == nil then -- we don't know what controls the user has, just use reasonable defaults
local platform = UserInputService:GetPlatform()
if platform == Enum.Platform.XBoxOne or platform == Enum.Platform.WiiU then
return GAMEPAD_TAG
elseif platform == Enum.Platform.Windows or platform == Enum.Platform.OSX then
return KEYBOARD_MOUSE_TAG
else
return TOUCH_TAG
end
end
if lastInputType == Enum.UserInputType.Keyboard or lastInputType == Enum.UserInputType.MouseMovement or
lastInputType == Enum.UserInputType.MouseButton1 or lastInputType == Enum.UserInputType.MouseButton2 or
lastInputType == Enum.UserInputType.MouseButton3 or lastInputType == Enum.UserInputType.MouseWheel then
return KEYBOARD_MOUSE_TAG
elseif lastInputType == Enum.UserInputType.Touch then
return TOUCH_TAG
elseif lastInputType == Enum.UserInputType.Gamepad1 or lastInputType == Enum.UserInputType.Gamepad2 or
inputType == Enum.UserInputType.Gamepad3 or lastInputType == Enum.UserInputType.Gamepad4 then
return GAMEPAD_TAG
end
return KEYBOARD_MOUSE_TAG
end
local function createPCHelp(parentFrame)
local function createPCGroup(title, actionInputBindings)
local textIndent = 9
local pcGroupFrame = utility:Create'Frame'
{
Size = UDim2.new(1/3,-PC_TABLE_SPACING,1,0),
BackgroundTransparency = 1,
Name = "PCGroupFrame" .. tostring(title)
};
local pcGroupTitle = utility:Create'TextLabel'
{
Position = UDim2.new(0,textIndent,0,0),
Size = UDim2.new(1,-textIndent,0,30),
BackgroundTransparency = 1,
Text = title,
Font = Enum.Font.SourceSansBold,
FontSize = Enum.FontSize.Size18,
TextColor3 = Color3.new(1,1,1),
TextXAlignment = Enum.TextXAlignment.Left,
Name = "PCGroupTitle" .. tostring(title),
ZIndex = 2,
Parent = pcGroupFrame
};
local count = 0
local frameHeight = 42
local spacing = 2
local offset = pcGroupTitle.Size.Y.Offset
for i = 1, #actionInputBindings do
for actionName, inputName in pairs(actionInputBindings[i]) do
local actionInputFrame = utility:Create'Frame'
{
Size = UDim2.new(1,0,0,frameHeight),
Position = UDim2.new(0,0,0, offset + ((frameHeight + spacing) * count)),
BackgroundTransparency = 0.65,
BorderSizePixel = 0,
ZIndex = 2,
Name = "ActionInputBinding" .. tostring(actionName),
Parent = pcGroupFrame
};
local nameLabel = utility:Create'TextLabel'
{
Size = UDim2.new(0.4,-textIndent,0,frameHeight),
Position = UDim2.new(0,textIndent,0,0),
BackgroundTransparency = 1,
Text = actionName,
Font = Enum.Font.SourceSansBold,
FontSize = Enum.FontSize.Size18,
TextColor3 = Color3.new(1,1,1),
TextXAlignment = Enum.TextXAlignment.Left,
Name = actionName .. "Label",
ZIndex = 2,
Parent = actionInputFrame
};
local inputLabel = utility:Create'TextLabel'
{
Size = UDim2.new(0.6,0,0,frameHeight),
Position = UDim2.new(0.5,-4,0,0),
BackgroundTransparency = 1,
Text = inputName,
Font = Enum.Font.SourceSans,
FontSize = Enum.FontSize.Size18,
TextColor3 = Color3.new(1,1,1),
TextXAlignment = Enum.TextXAlignment.Left,
Name = inputName .. "Label",
ZIndex = 2,
Parent = actionInputFrame
};
count = count + 1
end
end
pcGroupFrame.Size = UDim2.new(pcGroupFrame.Size.X.Scale,pcGroupFrame.Size.X.Offset,
0, offset + ((frameHeight + spacing) * count))
return pcGroupFrame
end
local rowOffset = 50
local isOSX = UserInputService:GetPlatform() == Enum.Platform.OSX
local charMoveFrame = createPCGroup( "Character Movement", {[1] = {["Move Forward"] = "W/Up Arrow"},
[2] = {["Move Backward"] = "S/Down Arrow"},
[3] = {["Move Left"] = "A/Left Arrow"},
[4] = {["Move Right"] = "D/Right Arrow"},
[5] = {["Jump"] = "Space"}} )
charMoveFrame.Parent = parentFrame
local accessoriesFrame = createPCGroup("Accessories", { [1] = {["Equip Tools"] = "1,2,3..."},
[2] = {["Unequip Tools"] = "1,2,3..."},
[3] = {["Drop Tool"] = "Backspace"},
[4] = {["Use Tool"] = "Left Mouse Button"},
[5] = {["Drop Hats"] = "+"} })
accessoriesFrame.Position = UDim2.new(1/3,PC_TABLE_SPACING,0,0)
accessoriesFrame.Parent = parentFrame
local miscFrame = nil
local hideHudSuccess, hideHudFlagValue = pcall(function() return settings():GetFFlag("AllowHideHudShortcut") end)
if (hideHudSuccess and hideHudFlagValue) then
miscFrame = createPCGroup("Misc", { [1] = {["Screenshot"] = "Print Screen"},
[2] = {["Record Video"] = isOSX and "F12/fn + F12" or "F12"},
[3] = {["Hide HUD"] = isOSX and "F7/fn + F7" or "F7"},
[4] = {["Dev Console"] = isOSX and "F9/fn + F9" or "F9"},
[5] = {["Mouselock"] = "Shift"},
[6] = {["Graphics Level"] = isOSX and "F10/fn + F10" or "F10"},
[7] = {["Fullscreen"] = isOSX and "F11/fn + F11" or "F11"} })
else
miscFrame = createPCGroup("Misc", { [1] = {["Screenshot"] = "Print Screen"},
[2] = {["Record Video"] = isOSX and "F12/fn + F12" or "F12"},
[3] = {["Dev Console"] = isOSX and "F9/fn + F9" or "F9"},
[4] = {["Mouselock"] = "Shift"},
[5] = {["Graphics Level"] = isOSX and "F10/fn + F10" or "F10"},
[6] = {["Fullscreen"] = isOSX and "F11/fn + F11" or "F11"} })
end
miscFrame.Position = UDim2.new(2/3,PC_TABLE_SPACING * 2,0,0)
miscFrame.Parent = parentFrame
local camFrame = createPCGroup("Camera Movement", { [1] = {["Rotate"] = "Right Mouse Button"},
[2] = {["Zoom In/Out"] = "Mouse Wheel"},
[3] = {["Zoom In"] = "I"},
[4] = {["Zoom Out"] = "O"} })
camFrame.Position = UDim2.new(0,0,charMoveFrame.Size.Y.Scale,charMoveFrame.Size.Y.Offset + rowOffset)
camFrame.Parent = parentFrame
local menuFrame = createPCGroup("Menu Items", { [1] = {["ROBLOX Menu"] = "ESC"},
[2] = {["Backpack"] = "~"},
[3] = {["Playerlist"] = "TAB"},
[4] = {["Chat"] = "/"} })
menuFrame.Position = UDim2.new(1/3,PC_TABLE_SPACING,charMoveFrame.Size.Y.Scale,charMoveFrame.Size.Y.Offset + rowOffset)
menuFrame.Parent = parentFrame
parentFrame.Size = UDim2.new(parentFrame.Size.X.Scale, parentFrame.Size.X.Offset, 0,
menuFrame.Size.Y.Offset + menuFrame.Position.Y.Offset)
end
local function createGamepadHelp(parentFrame)
local gamepadImage = "rbxasset://textures/ui/Settings/Help/GenericController.png"
local imageSize = UDim2.new(0,650,0,239)
local imagePosition = UDim2.new(0.5,-imageSize.X.Offset/2,0.5,-imageSize.Y.Offset/2)
if UserInputService:GetPlatform() == Enum.Platform.XBoxOne or UserInputService:GetPlatform() == Enum.Platform.XBox360 then
gamepadImage = "rbxasset://textures/ui/Settings/Help/XboxController.png"
imageSize = UDim2.new(0,1334,0,570)
imagePosition = UDim2.new(0.5, (-imageSize.X.Offset/2) - 50, 0.5, -imageSize.Y.Offset/2)
elseif UserInputService:GetPlatform() == Enum.Platform.PS4 or UserInputService:GetPlatform() == Enum.Platform.PS3 then
gamepadImage = "rbxasset://textures/ui/Settings/Help/PSController.png"
end
local gamepadImageLabel = utility:Create'ImageLabel'
{
Name = "GamepadImage",
Size = imageSize,
Position = imagePosition,
Image = gamepadImage,
BackgroundTransparency = 1,
ZIndex = 2,
Parent = parentFrame
};
parentFrame.Size = UDim2.new(parentFrame.Size.X.Scale, parentFrame.Size.X.Offset, 0, gamepadImageLabel.Size.Y.Offset + 100)
local gamepadFontSize = isTenFootInterface and Enum.FontSize.Size36 or Enum.FontSize.Size24
local function createGamepadLabel(text, position, size)
local nameLabel = utility:Create'TextLabel'
{
Position = position,
Size = size,
BackgroundTransparency = 1,
Text = text,
TextXAlignment = Enum.TextXAlignment.Left,
Font = Enum.Font.SourceSansBold,
FontSize = gamepadFontSize,
TextColor3 = Color3.new(1,1,1),
Name = text .. "Label",
ZIndex = 2,
Parent = gamepadImageLabel
};
end
local textVerticalSize = (gamepadFontSize == Enum.FontSize.Size36) and 36 or 24
if gamepadImage == "rbxasset://textures/ui/Settings/Help/XboxController.png" then
createGamepadLabel("Switch Tool", UDim2.new(0,50,0,-textVerticalSize/2), UDim2.new(0,100,0,textVerticalSize))
createGamepadLabel("Game Menu Toggle", UDim2.new(0,-38,0.15,-textVerticalSize/2), UDim2.new(0,164,0,textVerticalSize))
createGamepadLabel("Move", UDim2.new(0,-80,0.31,-textVerticalSize/2), UDim2.new(0,46,0,textVerticalSize))
createGamepadLabel("Menu Navigation", UDim2.new(0,-50,0.46,-textVerticalSize/2), UDim2.new(0,164,0,textVerticalSize))
createGamepadLabel("Use Tool", UDim2.new(0.96,0,0,-textVerticalSize/2), UDim2.new(0,73,0,textVerticalSize))
createGamepadLabel("ROBLOX Menu", UDim2.new(0.96,0,0.15,-textVerticalSize/2), UDim2.new(0,122,0,textVerticalSize))
createGamepadLabel("Back", UDim2.new(0.96,0,0.31,-textVerticalSize/2), UDim2.new(0,43,0,textVerticalSize))
createGamepadLabel("Jump", UDim2.new(0.96,0,0.46,-textVerticalSize/2), UDim2.new(0,49,0,textVerticalSize))
createGamepadLabel("Rotate Camera", UDim2.new(1,0,0.62,-textVerticalSize/2), UDim2.new(0,132,0,textVerticalSize))
createGamepadLabel("Camera Zoom", UDim2.new(1,0,0.77,-textVerticalSize/2), UDim2.new(0,122,0,textVerticalSize))
else
createGamepadLabel("Switch Tool", UDim2.new(-0.01,0,0,-textVerticalSize/2), UDim2.new(0,100,0,textVerticalSize))
createGamepadLabel("Game Menu Toggle", UDim2.new(-0.11,0,0.15,-textVerticalSize/2), UDim2.new(0,164,0,textVerticalSize))
createGamepadLabel("Move", UDim2.new(-0.08,0,0.31,-textVerticalSize/2), UDim2.new(0,46,0,textVerticalSize))
createGamepadLabel("Menu Navigation", UDim2.new(-0.125,0,0.46,-textVerticalSize/2), UDim2.new(0,164,0,textVerticalSize))
createGamepadLabel("Use Tool", UDim2.new(0.96,0,0,-textVerticalSize/2), UDim2.new(0,73,0,textVerticalSize))
createGamepadLabel("ROBLOX Menu", UDim2.new(0.9,0,0.15,-textVerticalSize/2), UDim2.new(0,122,0,textVerticalSize))
createGamepadLabel("Back", UDim2.new(1.01,0,0.31,-textVerticalSize/2), UDim2.new(0,43,0,textVerticalSize))
createGamepadLabel("Jump", UDim2.new(0.91,0,0.46,-textVerticalSize/2), UDim2.new(0,49,0,textVerticalSize))
createGamepadLabel("Rotate Camera", UDim2.new(0.91,0,0.62,-textVerticalSize/2), UDim2.new(0,132,0,textVerticalSize))
createGamepadLabel("Camera Zoom", UDim2.new(0.91,0,0.77,-textVerticalSize/2), UDim2.new(0,122,0,textVerticalSize))
end
-- todo: turn on dev console button when dev console is ready
--[[local openDevConsoleFunc = function()
this.HubRef:SetVisibility(false)
ToggleDevConsoleBindableFunc:Invoke()
end
local devConsoleButton = utility:MakeStyledButton("ConsoleButton", " Toggle Dev Console", UDim2.new(0,300,0,44), openDevConsoleFunc)
devConsoleButton.Size = UDim2.new(devConsoleButton.Size.X.Scale, devConsoleButton.Size.X.Offset, 0, 60)
devConsoleButton.Position = UDim2.new(1,-300,1,30)
if UserInputService.GamepadEnabled and not UserInputService.TouchEnabled and not UserInputService.MouseEnabled and not UserInputService.KeyboardEnabled then
devConsoleButton.ImageTransparency = 1
end
devConsoleButton.Parent = gamepadImageLabel
local aButtonImage = utility:Create'ImageLabel'
{
Name = "AButtonImage",
Size = UDim2.new(0,55,0,55),
Position = UDim2.new(0,5,0.5,-28),
Image = "rbxasset://textures/ui/Settings/Help/AButtonDark.png",
BackgroundTransparency = 1,
ZIndex = 2,
Parent = devConsoleButton
};
this:AddRow(nil, nil, devConsoleButton, 340)]]
end
local function createTouchHelp(parentFrame)
local smallScreen = utility:IsSmallTouchScreen()
local ySize = GuiService:GetScreenResolution().y - 350
if smallScreen then
ySize = GuiService:GetScreenResolution().y - 100
end
parentFrame.Size = UDim2.new(1,0,0,ySize)
local function createTouchLabel(text, position, size, parent)
local nameLabel = utility:Create'TextLabel'
{
Position = position,
Size = size,
BackgroundTransparency = 1,
Text = text,
Font = Enum.Font.SourceSansBold,
FontSize = Enum.FontSize.Size14,
TextColor3 = Color3.new(1,1,1),
Name = text .. "Label",
ZIndex = 2,
Parent = parent
};
if not smallScreen then
nameLabel.FontSize = Enum.FontSize.Size18
nameLabel.Size = UDim2.new(nameLabel.Size.X.Scale, nameLabel.Size.X.Offset, nameLabel.Size.Y.Scale, nameLabel.Size.Y.Offset + 4)
end
local nameBackgroundImage = utility:Create'ImageLabel'
{
Name = text .. "BackgroundImage",
Size = UDim2.new(1,0,1,0),
Position = UDim2.new(0,0,0,2),
BackgroundTransparency = 1,
Image = "rbxasset://textures/ui/Settings/Radial/RadialLabel.png",
ScaleType = Enum.ScaleType.Slice,
SliceCenter = Rect.new(12,2,65,21),
ZIndex = 2,
Parent = nameLabel
};
return nameLabel
end
local function createTouchGestureImage(name, image, position, size, parent)
local gestureImage = utility:Create'ImageLabel'
{
Name = name,
Size = size,
Position = position,
BackgroundTransparency = 1,
Image = image,
ZIndex = 2,
Parent = parent
};
return gestureImage
end
local xSizeOffset = 30
local ySize = 25
if smallScreen then xSizeOffset = 0 end
local moveLabel = createTouchLabel("Move", UDim2.new(0.06,0,0.58,0), UDim2.new(0,77 + xSizeOffset,0,ySize), parentFrame)
if not smallScreen then moveLabel.Position = UDim2.new(-0.03,0,0.7,0) end
local jumpLabel = createTouchLabel("Jump", UDim2.new(0.8,0,0.58,0), UDim2.new(0,77 + xSizeOffset,0,ySize), parentFrame)
if not smallScreen then jumpLabel.Position = UDim2.new(0.85,0,0.7,0) end
local equipLabel = createTouchLabel("Equip/Unequip Tools", UDim2.new(0.5,-60,0.64,0), UDim2.new(0,120 + xSizeOffset,0,ySize), parentFrame)
if not smallScreen then equipLabel.Position = UDim2.new(0.5,-60,0.95,0) end
local zoomLabel = createTouchLabel("Zoom In/Out", UDim2.new(0.15,-60,0.02,0), UDim2.new(0,120,0,ySize), parentFrame)
createTouchGestureImage("ZoomImage", "rbxasset://textures/ui/Settings/Help/ZoomGesture.png", UDim2.new(0.5,-26,1,3), UDim2.new(0,53,0,59), zoomLabel)
local rotateLabel = createTouchLabel("Rotate Camera", UDim2.new(0.5,-60,0.02,0), UDim2.new(0,120,0,ySize), parentFrame)
createTouchGestureImage("RotateImage", "rbxasset://textures/ui/Settings/Help/RotateCameraGesture.png", UDim2.new(0.5,-32,1,3), UDim2.new(0,65,0,48), rotateLabel)
local useToolLabel = createTouchLabel("Use Tool", UDim2.new(0.85,-60,0.02,0), UDim2.new(0,120,0,ySize), parentFrame)
createTouchGestureImage("ToolImage", "rbxasset://textures/ui/Settings/Help/UseToolGesture.png", UDim2.new(0.5,-19,1,3), UDim2.new(0,38,0,52), useToolLabel)
end
local function createHelpDisplay(typeOfHelp)
local helpFrame = utility:Create'Frame'
{
Size = UDim2.new(1,0,1,0),
BackgroundTransparency = 1,
Name = "HelpFrame" .. tostring(typeOfHelp)
};
if typeOfHelp == KEYBOARD_MOUSE_TAG then
createPCHelp(helpFrame)
elseif typeOfHelp == GAMEPAD_TAG then
createGamepadHelp(helpFrame)
elseif typeOfHelp == TOUCH_TAG then
createTouchHelp(helpFrame)
end
return helpFrame
end
local function displayHelp(currentPage)
for i, helpPage in pairs(this.HelpPages) do
if helpPage == currentPage then
helpPage.Parent = this.Page
this.Page.Size = helpPage.Size
else
helpPage.Parent = nil
end
end
if UserInputService:GetPlatform() == Enum.Platform.XBoxOne then
this.HubRef.PageViewClipper.ClipsDescendants = false
this.HubRef.PageView.ClipsDescendants = false
end
end
local function switchToHelp(typeOfHelp)
local helpPage = this.HelpPages[typeOfHelp]
if helpPage then
displayHelp(helpPage)
else
this.HelpPages[typeOfHelp] = createHelpDisplay(typeOfHelp)
switchToHelp(typeOfHelp)
end
end
local function showTypeOfHelp()
switchToHelp(this:GetCurrentInputType())
end
------ TAB CUSTOMIZATION -------
this.TabHeader.Name = "HelpTab"
this.TabHeader.Icon.Image = "rbxasset://textures/ui/Settings/MenuBarIcons/HelpTab.png"
if utility:IsSmallTouchScreen() then
this.TabHeader.Icon.Size = UDim2.new(0,33,0,33)
this.TabHeader.Icon.Position = UDim2.new(this.TabHeader.Icon.Position.X.Scale,this.TabHeader.Icon.Position.X.Offset,0.5,-16)
this.TabHeader.Size = UDim2.new(0,100,1,0)
elseif isTenFootInterface then
this.TabHeader.Icon.Image = "rbxasset://textures/ui/Settings/MenuBarIcons/HelpTab@2x.png"
this.TabHeader.Icon.Size = UDim2.new(0,90,0,90)
this.TabHeader.Icon.Position = UDim2.new(0,0,0.5,-43)
this.TabHeader.Size = UDim2.new(0,210,1,0)
else
this.TabHeader.Icon.Size = UDim2.new(0,44,0,44)
this.TabHeader.Icon.Position = UDim2.new(this.TabHeader.Icon.Position.X.Scale,this.TabHeader.Icon.Position.X.Offset,0.5,-22)
this.TabHeader.Size = UDim2.new(0,130,1,0)
end
this.TabHeader.Icon.Title.Text = "Help"
------ PAGE CUSTOMIZATION -------
this.Page.Name = "Help"
UserInputService.InputBegan:connect(function(inputObject)
local inputType = inputObject.UserInputType
if inputType ~= Enum.UserInputType.Focus and inputType ~= Enum.UserInputType.None then
lastInputType = inputObject.UserInputType
showTypeOfHelp()
end
end)
return this
end
----------- Public Facing API Additions --------------
do
PageInstance = Initialize()
PageInstance.Displayed.Event:connect(function()
if PageInstance:GetCurrentInputType() == TOUCH_TAG then
if PageInstance.HubRef.BottomButtonFrame and not utility:IsSmallTouchScreen() then
PageInstance.HubRef.BottomButtonFrame.Visible = false
end
end
end)
PageInstance.Hidden.Event:connect(function()
PageInstance.HubRef.PageViewClipper.ClipsDescendants = true
PageInstance.HubRef.PageView.ClipsDescendants = true
PageInstance.HubRef:ShowShield()
if PageInstance:GetCurrentInputType() == TOUCH_TAG then
PageInstance.HubRef.BottomButtonFrame.Visible = true
end
end)
end
return PageInstance
@@ -0,0 +1,124 @@
--[[
Filename: LeaveGame.lua
Written by: jeditkacheff
Version 1.0
Description: Takes care of the leave game in Settings Menu
--]]
-------------- CONSTANTS -------------
local LEAVE_GAME_ACTION = "LeaveGameCancelAction"
-------------- SERVICES --------------
local CoreGui = game:GetService("CoreGui")
local ContextActionService = game:GetService("ContextActionService")
local RobloxGui = CoreGui:WaitForChild("RobloxGui")
local GuiService = game:GetService("GuiService")
----------- UTILITIES --------------
local utility = require(RobloxGui.Modules.Settings.Utility)
------------ Variables -------------------
local PageInstance = nil
RobloxGui:WaitForChild("Modules"):WaitForChild("TenFootInterface")
local isTenFootInterface = require(RobloxGui.Modules.TenFootInterface):IsEnabled()
----------- CLASS DECLARATION --------------
local function Initialize()
local settingsPageFactory = require(RobloxGui.Modules.Settings.SettingsPageFactory)
local this = settingsPageFactory:CreateNewPage()
this.DontLeaveFunc = function(isUsingGamepad)
if this.HubRef then
this.HubRef:PopMenu(isUsingGamepad, true)
end
end
this.DontLeaveFromHotkey = function(name, state, input)
if state == Enum.UserInputState.Begin then
local isUsingGamepad = input.UserInputType == Enum.UserInputType.Gamepad1 or input.UserInputType == Enum.UserInputType.Gamepad2
or input.UserInputType == Enum.UserInputType.Gamepad3 or input.UserInputType == Enum.UserInputType.Gamepad4
this.DontLeaveFunc(isUsingGamepad)
end
end
this.DontLeaveFromButton = function(isUsingGamepad)
this.DontLeaveFunc(isUsingGamepad)
end
------ TAB CUSTOMIZATION -------
this.TabHeader = nil -- no tab for this page
------ PAGE CUSTOMIZATION -------
this.Page.Name = "LeaveGamePage"
local leaveGameText = utility:Create'TextLabel'
{
Name = "LeaveGameText",
Text = "Are you sure you want to leave the game?",
Font = Enum.Font.SourceSansBold,
FontSize = Enum.FontSize.Size36,
TextColor3 = Color3.new(1,1,1),
BackgroundTransparency = 1,
Size = UDim2.new(1,0,0,200),
TextWrapped = true,
ZIndex = 2,
Parent = this.Page
};
if utility:IsSmallTouchScreen() then
leaveGameText.FontSize = Enum.FontSize.Size24
leaveGameText.Size = UDim2.new(1,0,0,100)
elseif isTenFootInterface then
leaveGameText.FontSize = Enum.FontSize.Size48
end
local buttonSpacing = 20
local buttonSize = UDim2.new(0, 200, 0, 50)
if isTenFootInterface then
leaveGameText.Position = UDim2.new(0,0,0,100)
buttonSize = UDim2.new(0, 300, 0, 80)
end
this.LeaveGameButton = utility:MakeStyledButton("LeaveGame", "Leave", buttonSize)
this.LeaveGameButton.NextSelectionRight = nil
this.LeaveGameButton:SetVerb("Exit")
if utility:IsSmallTouchScreen() then
this.LeaveGameButton.Position = UDim2.new(0.5, -buttonSize.X.Offset - buttonSpacing, 1, 0)
else
this.LeaveGameButton.Position = UDim2.new(0.5, -buttonSize.X.Offset - buttonSpacing, 1, -30)
end
this.LeaveGameButton.Parent = leaveGameText
------------- Init ----------------------------------
local dontleaveGameButton = utility:MakeStyledButton("DontLeaveGame", "Don't Leave", buttonSize, this.DontLeaveFromButton)
dontleaveGameButton.NextSelectionLeft = nil
if utility:IsSmallTouchScreen() then
dontleaveGameButton.Position = UDim2.new(0.5, buttonSpacing, 1, 0)
else
dontleaveGameButton.Position = UDim2.new(0.5, buttonSpacing, 1, -30)
end
dontleaveGameButton.Parent = leaveGameText
this.Page.Size = UDim2.new(1,0,0,dontleaveGameButton.AbsolutePosition.Y + dontleaveGameButton.AbsoluteSize.Y)
return this
end
----------- Public Facing API Additions --------------
PageInstance = Initialize()
PageInstance.Displayed.Event:connect(function()
GuiService.SelectedCoreObject = PageInstance.LeaveGameButton
ContextActionService:BindCoreAction(LEAVE_GAME_ACTION, PageInstance.DontLeaveFromHotkey, false, Enum.KeyCode.ButtonB)
end)
PageInstance.Hidden.Event:connect(function()
ContextActionService:UnbindCoreAction(LEAVE_GAME_ACTION)
end)
return PageInstance
@@ -0,0 +1,170 @@
--[[r
Filename: Record.lua
Written by: jeditkacheff
Version 1.0
Description: Takes care of the Record Tab in Settings Menu
--]]
-------------- SERVICES --------------
local CoreGui = game:GetService("CoreGui")
local RobloxGui = CoreGui:WaitForChild("RobloxGui")
local GuiService = game:GetService("GuiService")
local Settings = UserSettings()
local GameSettings = Settings.GameSettings
----------- UTILITIES --------------
RobloxGui:WaitForChild("Modules"):WaitForChild("TenFootInterface")
local utility = require(RobloxGui.Modules.Settings.Utility)
local isTenFootInterface = require(RobloxGui.Modules.TenFootInterface):IsEnabled()
------------ Variables -------------------
local PageInstance = nil
----------- CLASS DECLARATION --------------
local function Initialize()
local settingsPageFactory = require(RobloxGui.Modules.Settings.SettingsPageFactory)
local this = settingsPageFactory:CreateNewPage()
local isRecordingVideo = false
local recordingEvent = Instance.new("BindableEvent")
recordingEvent.Name = "RecordingEvent"
this.RecordingChanged = recordingEvent.Event
function this:IsRecording()
return isRecordingVideo
end
------ TAB CUSTOMIZATION -------
this.TabHeader.Name = "RecordTab"
this.TabHeader.Icon.Image = "rbxasset://textures/ui/Settings/MenuBarIcons/RecordTab.png"
this.TabHeader.Icon.Size = UDim2.new(0,41,0,40)
this.TabHeader.Icon.Position = UDim2.new(0,5,0.5,-20)
this.TabHeader.Icon.Title.Text = "Record"
this.TabHeader.Size = UDim2.new(0,130,1,0)
------ PAGE CUSTOMIZATION -------
this.Page.Name = "Record"
local function makeTextLabel(name, text, bold, size, pos, parent)
local textLabel = utility:Create'TextLabel'
{
Name = name,
BackgroundTransparency = 1,
Text = text,
TextWrapped = true,
Font = Enum.Font.SourceSans,
FontSize = Enum.FontSize.Size24,
TextColor3 = Color3.new(1,1,1),
Size = size,
Position = pos,
TextXAlignment = Enum.TextXAlignment.Left,
TextYAlignment = Enum.TextYAlignment.Top,
ZIndex = 2,
Parent = parent
};
if bold then textLabel.Font = Enum.Font.SourceSansBold end
return textLabel
end
-- need to override this function from SettingsPageFactory
-- DropDown menus require hub to to be set when they are initialized
function this:SetHub(newHubRef)
this.HubRef = newHubRef
local recordEnumNames = {}
recordEnumNames[1] = "Save To Disk"
recordEnumNames[2] = "Upload to YouTube"
local startSetting = 2
if GameSettings.VideoUploadPromptBehavior == Enum.UploadSetting["Never"] then
startSetting = 1
end
---------------------------------- SCREENSHOT -------------------------------------
local screenshotTitle = makeTextLabel("ScreenshotTitle",
"Screenshot",
true, UDim2.new(1,0,0,36), UDim2.new(0,10,0.05,0), this.Page)
screenshotTitle.FontSize = Enum.FontSize.Size36
local screenshotBody = makeTextLabel("ScreenshotBody",
"By clicking the 'Take Screenshot' button, the menu will close and take a screenshot and save it to your computer.",
false, UDim2.new(1,-10,0,70), UDim2.new(0,0,1,0), screenshotTitle)
local closeSettingsFunc = function()
this.HubRef:SetVisibility(false, true)
end
this.ScreenshotButton = utility:MakeStyledButton("ScreenshotButton", "Take Screenshot", UDim2.new(0,300,0,44), closeSettingsFunc, this)
this.ScreenshotButton.Position = UDim2.new(0,400,1,0)
this.ScreenshotButton.Parent = screenshotBody
---------------------------------- VIDEO -------------------------------------
local videoTitle = makeTextLabel("VideoTitle",
"Video",
true, UDim2.new(1,0,0,36), UDim2.new(0,10,0.5,0), this.Page)
videoTitle.FontSize = Enum.FontSize.Size36
local videoBody = makeTextLabel("VideoBody",
"By clicking the 'Record Video' button, the menu will close and start recording your screen.",
false, UDim2.new(1,-10,0,70), UDim2.new(0,0,1,0), videoTitle)
this.VideoSettingsFrame,
this.VideoSettingsLabel,
this.VideoSettingsMode = utility:AddNewRow(this, "Video Settings", "Selector", recordEnumNames, startSetting, 270)
this.VideoSettingsMode.IndexChanged:connect(function(newIndex)
if newIndex == 1 then
GameSettings.VideoUploadPromptBehavior = Enum.UploadSetting.Never
elseif newIndex == 2 then
GameSettings.VideoUploadPromptBehavior = Enum.UploadSetting.Always
end
end)
local recordButton = utility:MakeStyledButton("RecordButton", "Record Video", UDim2.new(0,300,0,44), closeSettingsFunc, this)
recordButton.Position = UDim2.new(0,410,1,10)
recordButton.Parent = this.VideoSettingsMode.SelectorFrame.Parent
recordButton.MouseButton1Click:connect(function()
recordingEvent:Fire(not isRecordingVideo)
end)
local gameOptions = settings():FindFirstChild("Game Options")
if gameOptions then
gameOptions.VideoRecordingChangeRequest:connect(function(recording)
isRecordingVideo = recording
if recording then
recordButton.RecordButtonTextLabel.Text = "Stop Recording"
else
recordButton.RecordButtonTextLabel.Text = "Record Video"
end
end)
end
recordButton:SetVerb("RecordToggle")
this.ScreenshotButton:SetVerb("Screenshot")
this.Page.Size = UDim2.new(1,0,0,400)
end
return this
end
----------- Public Facing API Additions --------------
PageInstance = Initialize()
PageInstance.Displayed.Event:connect(function(switchedFromGamepadInput)
if switchedFromGamepadInput then
GuiService.SelectedCoreObject = PageInstance.ScreenshotButton
end
end)
return PageInstance
@@ -0,0 +1,311 @@
--[[
Filename: ReportAbuseMenu.lua
Written by: jeditkacheff
Version 1.0
Description: Takes care of the report abuse page in Settings Menu
--]]
-------------- SERVICES --------------
local CoreGui = game:GetService("CoreGui")
local RobloxGui = CoreGui:WaitForChild("RobloxGui")
local GuiService = game:GetService("GuiService")
local PlayersService = game:GetService("Players")
----------- UTILITIES --------------
local utility = require(RobloxGui.Modules.Settings.Utility)
------------ CONSTANTS -------------------
local ABUSE_TYPES_PLAYER = {
"Swearing",
"Inappropriate Username",
"Bullying",
"Scamming",
"Dating",
"Cheating/Exploiting",
"Personal Question",
"Offsite Links",
}
local ABUSE_TYPES_GAME = {
"Inappropriate Content",
"Bad Model or Script",
"Offsite Link",
}
local DEFAULT_ABUSE_DESC_TEXT = " Short Description (Optional)"
if utility:IsSmallTouchScreen() then
DEFAULT_ABUSE_DESC_TEXT = " (Optional)"
end
------------ VARIABLES -------------------
local PageInstance = nil
----------- CLASS DECLARATION --------------
local function Initialize()
local settingsPageFactory = require(RobloxGui.Modules.Settings.SettingsPageFactory)
local this = settingsPageFactory:CreateNewPage()
local playerNames = {}
local nameToRbxPlayer = {}
function this:GetPlayerFromIndex(index)
local playerName = playerNames[index]
if playerName then
return nameToRbxPlayer[nameToRbxPlayer]
end
return nil
end
function this:UpdatePlayerDropDown()
playerNames = {}
nameToRbxPlayer = {}
local players = PlayersService:GetPlayers()
local index = 1
for i = 1, #players do
local player = players[i]
if player ~= PlayersService.LocalPlayer and player.UserId > 0 then
playerNames[index] = player.Name
nameToRbxPlayer[player.Name] = player
index = index + 1
end
end
this.WhichPlayerMode:UpdateDropDownList(playerNames)
if index == 1 then
this.GameOrPlayerMode:SetSelectionIndex(1)
this.TypeOfAbuseMode:UpdateDropDownList(ABUSE_TYPES_GAME)
end
this.WhichPlayerMode:SetInteractable(index > 1 and this.GameOrPlayerMode.CurrentIndex ~= 1)
this.GameOrPlayerMode:SetInteractable(index > 1)
end
------ TAB CUSTOMIZATION -------
this.TabHeader.Name = "ReportAbuseTab"
this.TabHeader.Icon.Image = "rbxasset://textures/ui/Settings/MenuBarIcons/ReportAbuseTab.png"
if utility:IsSmallTouchScreen() then
this.TabHeader.Icon.Size = UDim2.new(0,27,0,32)
this.TabHeader.Size = UDim2.new(0,120,1,0)
else
this.TabHeader.Size = UDim2.new(0,150,1,0)
this.TabHeader.Icon.Size = UDim2.new(0,36,0,43)
end
this.TabHeader.Icon.Position = UDim2.new(this.TabHeader.Icon.Position.X.Scale, this.TabHeader.Icon.Position.X.Offset + 10, 0.5,-this.TabHeader.Icon.Size.Y.Offset/2)
this.TabHeader.Icon.Title.Text = "Report"
------ PAGE CUSTOMIZATION -------
this.Page.Name = "ReportAbusePage"
-- need to override this function from SettingsPageFactory
-- DropDown menus require hub to to be set when they are initialized
function this:SetHub(newHubRef)
this.HubRef = newHubRef
if utility:IsSmallTouchScreen() then
this.GameOrPlayerFrame,
this.GameOrPlayerLabel,
this.GameOrPlayerMode = utility:AddNewRow(this, "Game or Player?", "Selector", {"Game", "Player"}, 1)
else
this.GameOrPlayerFrame,
this.GameOrPlayerLabel,
this.GameOrPlayerMode = utility:AddNewRow(this, "Game or Player?", "Selector", {"Game", "Player"}, 1, 3)
end
this.WhichPlayerFrame,
this.WhichPlayerLabel,
this.WhichPlayerMode = utility:AddNewRow(this, "Which Player?", "DropDown", {"update me"})
this.WhichPlayerMode:SetInteractable(false)
this.WhichPlayerLabel.ZIndex = 1
this.TypeOfAbuseFrame,
this.TypeOfAbuseLabel,
this.TypeOfAbuseMode = utility:AddNewRow(this, "Type Of Abuse", "DropDown", ABUSE_TYPES_GAME)
if utility:IsSmallTouchScreen() then
this.AbuseDescriptionFrame,
this.AbuseDescriptionLabel,
this.AbuseDescription = utility:AddNewRow(this, DEFAULT_ABUSE_DESC_TEXT, "TextBox", nil, nil)
else
this.AbuseDescriptionFrame,
this.AbuseDescriptionLabel,
this.AbuseDescription = utility:AddNewRow(this, DEFAULT_ABUSE_DESC_TEXT, "TextBox", nil, nil, 5)
end
if utility:IsSmallTouchScreen() then
this.AbuseDescription.Selection.Size = UDim2.new(0, 290, 0, 30)
this.AbuseDescription.Selection.Position = UDim2.new(1,-345,this.AbuseDescription.Selection.Position.Y.Scale, this.AbuseDescription.Selection.Position.Y.Offset)
this.AbuseDescriptionLabel = this.TypeOfAbuseLabel:clone()
this.AbuseDescriptionLabel.Text = "Abuse Description"
this.AbuseDescriptionLabel.Position = UDim2.new(this.AbuseDescriptionLabel.Position.X.Scale, this.AbuseDescriptionLabel.Position.X.Offset,
0,50)
this.AbuseDescriptionLabel.Parent = this.Page
end
local SelectionOverrideObject = utility:Create'ImageLabel'
{
Image = "",
BackgroundTransparency = 1
};
local submitButton, submitText = nil, nil
local function makeSubmitButtonActive()
submitButton.ZIndex = 2
submitButton.Selectable = true
submitText.ZIndex = 2
end
local function makeSubmitButtonInactive()
submitButton.ZIndex = 1
submitButton.Selectable = false
submitText.ZIndex = 1
end
local function updateAbuseDropDown()
this.WhichPlayerMode:ResetSelectionIndex()
this.TypeOfAbuseMode:ResetSelectionIndex()
if this.GameOrPlayerMode.CurrentIndex == 1 then
this.TypeOfAbuseMode:UpdateDropDownList(ABUSE_TYPES_GAME)
this.WhichPlayerMode:SetInteractable(false)
this.WhichPlayerLabel.ZIndex = 1
this.GameOrPlayerMode.SelectorFrame.NextSelectionDown = this.TypeOfAbuseMode.DropDownFrame
else
this.TypeOfAbuseMode:UpdateDropDownList(ABUSE_TYPES_PLAYER)
this.WhichPlayerMode:SetInteractable(true)
this.WhichPlayerLabel.ZIndex = 2
this.GameOrPlayerMode.SelectorFrame.NextSelectionDown = this.WhichPlayerMode.DropDownFrame
end
makeSubmitButtonInactive()
end
local function cleanupReportAbuseMenu()
updateAbuseDropDown()
this.AbuseDescription.Selection.Text = DEFAULT_ABUSE_DESC_TEXT
this.HubRef:SetVisibility(false, true)
end
local function onReportSubmitted()
local abuseReason = nil
if this.GameOrPlayerMode.CurrentIndex == 2 then
abuseReason = ABUSE_TYPES_PLAYER[this.TypeOfAbuseMode.CurrentIndex]
local currentAbusingPlayer = this:GetPlayerFromIndex(this.WhichPlayerMode.CurrentIndex)
if currentAbusingPlayer and abuseReason then
spawn(function()
game.Players:ReportAbuse(currentAbusingPlayer, abuseReason, this.AbuseDescription.Selection.Text)
end)
end
else
abuseReason = ABUSE_TYPES_GAME[this.TypeOfAbuseMode.CurrentIndex]
if abuseReason then
spawn(function()
game.Players:ReportAbuse(nil, abuseReason, this.AbuseDescription.Selection.Text)
end)
end
end
if abuseReason then
local alertText = "Thanks for your report! Our moderators will review the chat logs and evaluate what happened."
if abuseReason == 'Cheating/Exploiting' then
alertText = "Thanks for your report! We've recorded your report for evaluation."
elseif abuseReason == 'Inappropriate Username' then
alertText = "Thanks for your report! Our moderators will evaluate the username."
elseif abuseReason == "Bad Model or Script" or abuseReason == "Inappropriate Content" or abuseReason == "Offsite Link" or abuseReason == "Offsite Links" then
alertText = "Thanks for your report! Our moderators will review the place and make a determination."
end
utility:ShowAlert(alertText, "Ok", this.HubRef, cleanupReportAbuseMenu)
this.LastSelectedObject = nil
end
end
submitButton, submitText = utility:MakeStyledButton("SubmitButton", "Submit", UDim2.new(0,198,0,50), onReportSubmitted, this)
if utility:IsSmallTouchScreen() then
submitButton.Position = UDim2.new(1,-220,1,5)
else
submitButton.Position = UDim2.new(1,-194,1,5)
end
submitButton.Selectable = false
submitButton.ZIndex = 1
submitText.ZIndex = 1
submitButton.Parent = this.AbuseDescription.Selection
local function playerSelectionChanged(newIndex)
if newIndex ~= nil and this.TypeOfAbuseMode:GetSelectedIndex() ~= nil then
makeSubmitButtonActive()
else
makeSubmitButtonInactive()
end
end
this.WhichPlayerMode.IndexChanged:connect(playerSelectionChanged)
local function typeOfAbuseChanged(newIndex)
if newIndex ~= nil then
if this.GameOrPlayerMode.CurrentIndex == 1 or this.WhichPlayerMode:GetSelectedIndex() ~= nil then
makeSubmitButtonActive()
else
makeSubmitButtonInactive()
end
else
makeSubmitButtonInactive()
end
end
this.TypeOfAbuseMode.IndexChanged:connect(typeOfAbuseChanged)
this.GameOrPlayerMode.IndexChanged:connect(updateAbuseDropDown)
this:AddRow(nil, nil, this.AbuseDescription)
this.Page.Size = UDim2.new(1,0,0,submitButton.AbsolutePosition.Y + submitButton.AbsoluteSize.Y)
end
return this
end
----------- Public Facing API Additions --------------
do
PageInstance = Initialize()
PageInstance.Displayed.Event:connect(function()
PageInstance:UpdatePlayerDropDown()
end)
function PageInstance:ReportPlayer(player)
if player then
local setReportPlayerConnection = nil
setReportPlayerConnection = PageInstance.Displayed.Event:connect(function()
-- When we change the SelectionIndex of GameOrPlayerMode it waits until the tween is done
-- before it fires the IndexChanged signal. The WhichPlayerMode dropdown listens to this signal
-- and resets when it is fired. Therefore we need to listen to this signal and set the player we want
-- to report the frame after the dropdown is reset
local indexChangedConnection = nil
indexChangedConnection = PageInstance.GameOrPlayerMode.IndexChanged:connect(function()
if indexChangedConnection then
indexChangedConnection:disconnect()
indexChangedConnection = nil
end
PageInstance.WhichPlayerMode:SetSelectionByValue(player.Name)
end)
PageInstance.GameOrPlayerMode:SetSelectionIndex(2)
if setReportPlayerConnection then
setReportPlayerConnection:disconnect()
setReportPlayerConnection = nil
end
end)
PageInstance.HubRef:SetVisibility(true, false, PageInstance)
end
end
end
return PageInstance
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,345 @@
--[[
Filename: TenFootInterface.lua
Written by: jeditkacheff
Version 1.0
Description: Setups up some special UI for ROBLOX TV gaming
--]]
-------------- CONSTANTS --------------
local HEALTH_GREEN_COLOR = Color3.new(27/255, 252/255, 107/255)
local DISPLAY_POS_INIT_INSET = 0
local DISPLAY_ITEM_OFFSET = 4
local FORCE_TEN_FOOT_INTERFACE = false
-------------- SERVICES --------------
local CoreGui = game:GetService("CoreGui")
local RobloxGui = CoreGui:WaitForChild("RobloxGui")
local UserInputService = game:GetService("UserInputService")
local GuiService = game:GetService("GuiService")
------------------ VARIABLES --------------------
local tenFootInterfaceEnabled = false
do
local platform = UserInputService:GetPlatform()
tenFootInterfaceEnabled = (platform == Enum.Platform.XBoxOne or platform == Enum.Platform.WiiU or platform == Enum.Platform.PS4 or
platform == Enum.Platform.AndroidTV or platform == Enum.Platform.XBox360 or platform == Enum.Platform.PS3 or
platform == Enum.Platform.Ouya or platform == Enum.Platform.SteamOS)
end
if FORCE_TEN_FOOT_INTERFACE then
tenFootInterfaceEnabled = true
end
local Util = {}
do
function Util.Create(instanceType)
return function(data)
local obj = Instance.new(instanceType)
for k, v in pairs(data) do
if type(k) == 'number' then
v.Parent = obj
else
obj[k] = v
end
end
return obj
end
end
end
local function CreateModule()
local this = {}
local nextObjectDisplayYPos = DISPLAY_POS_INIT_INSET
local displayStack = {}
-- setup base gui
local function createContainer()
if not this.Container then
this.Container = Util.Create'ImageButton'
{
Name = "TopRightContainer";
Size = UDim2.new(0, 350, 0, 100);
Position = UDim2.new(1,-360,0,10);
AutoButtonColor = false;
Image = "";
Active = false;
BackgroundTransparency = 1;
Parent = RobloxGui;
};
end
end
function removeFromDisplayStack(displayObject)
local moveUpFromHere = nil
for i = 1, #displayStack do
if displayStack[i] == displayObject then
moveUpFromHere = i + 1
break
end
end
local prevObject = displayObject
for i = moveUpFromHere, #displayStack do
local objectToMoveUp = displayStack[i]
objectToMoveUp.Position = UDim2.new(objectToMoveUp.Position.X.Scale, objectToMoveUp.Position.X.Offset,
objectToMoveUp.Position.Y.Scale, prevObject.AbsolutePosition.Y)
prevObject = objectToMoveUp
end
end
function addBackToDisplayStack(displayObject)
for i = 1, #displayStack do
if displayStack[i] == displayObject then
moveDownFromHere = i + 1
break
end
end
local prevObject = displayObject
for i = moveDownFromHere, #displayStack do
local objectToMoveDown = displayStack[i]
local nextDisplayPos = prevObject.AbsolutePosition.Y + prevObject.AbsoluteSize.Y + DISPLAY_ITEM_OFFSET
objectToMoveDown.Position = UDim2.new(objectToMoveDown.Position.X.Scale, objectToMoveDown.Position.X.Offset,
objectToMoveDown.Position.Y.Scale, nextDisplayPos)
prevObject = objectToMoveDown
end
end
function addToDisplayStack(displayObject)
local lastDisplayed = nil
if #displayStack > 0 then
lastDisplayed = displayStack[#displayStack]
end
displayStack[#displayStack + 1] = displayObject
local nextDisplayPos = DISPLAY_POS_INIT_INSET
if lastDisplayed then
nextDisplayPos = lastDisplayed.AbsolutePosition.Y + lastDisplayed.AbsoluteSize.Y + DISPLAY_ITEM_OFFSET
end
displayObject.Position = UDim2.new(displayObject.Position.X.Scale, displayObject.Position.X.Offset,
displayObject.Position.Y.Scale, nextDisplayPos)
createContainer()
displayObject.Parent = this.Container
displayObject.Changed:connect(function(prop)
if prop == "Visible" then
if not displayObject.Visible then
removeFromDisplayStack(displayObject)
else
addBackToDisplayStack(displayObject)
end
end
end)
end
function this:CreateHealthBar()
this.HealthContainer = Util.Create'Frame'{
Name = "HealthContainer";
Size = UDim2.new(1, -86, 0, 50);
Position = UDim2.new(0, 92, 0, 0);
BorderSizePixel = 0;
BackgroundColor3 = Color3.new(0,0,0);
BackgroundTransparency = 0.5;
};
local healthFillHolder = Util.Create'Frame'{
Name = "HealthFillHolder";
Size = UDim2.new(1, -10, 1, -10);
Position = UDim2.new(0, 5, 0, 5);
BorderSizePixel = 0;
BackgroundColor3 = Color3.new(1,1,1);
BackgroundTransparency = 1.0;
Parent = this.HealthContainer;
};
local healthFill = Util.Create'Frame'{
Name = "HealthFill";
Size = UDim2.new(1, 0, 1, 0);
Position = UDim2.new(0, 0, 0, 0);
BorderSizePixel = 0;
BackgroundTransparency = 0.0;
BackgroundColor3 = HEALTH_GREEN_COLOR;
Parent = healthFillHolder;
};
local healthText = Util.Create'TextLabel'{
Name = "HealthText";
Size = UDim2.new(0, 98, 0, 50);
Position = UDim2.new(0, -100, 0, 0);
BackgroundTransparency = 0.5;
BackgroundColor3 = Color3.new(0,0,0);
Font = Enum.Font.SourceSans;
FontSize = Enum.FontSize.Size36;
Text = "Health";
TextColor3 = Color3.new(1,1,1);
BorderSizePixel = 0;
Parent = this.HealthContainer;
};
local username = Util.Create'TextLabel'{
Visible = false
}
addToDisplayStack(this.HealthContainer)
createContainer()
return this.Container, username, this.HealthContainer, healthFill
end
function this:SetupTopStat()
local topStatEnabled = true
local displayedStat = nil
local displayedStatChangedCon = nil
local displayedStatParentedCon = nil
local leaderstatsChildAddedCon = nil
local tenFootInterfaceStat = nil
local function makeTenFootInterfaceStat()
if tenFootInterfaceStat then return end
tenFootInterfaceStat = Util.Create'Frame'{
Name = "OneStatFrame";
Size = UDim2.new(1, 0, 0, 36);
Position = UDim2.new(0, 0, 0, 0);
BorderSizePixel = 0;
BackgroundTransparency = 1;
};
local statName = Util.Create'TextLabel'{
Name = "StatName";
Size = UDim2.new(0.5,0,0,36);
BackgroundTransparency = 1;
Font = Enum.Font.SourceSans;
FontSize = Enum.FontSize.Size36;
TextStrokeColor3 = Color3.new(104/255, 104/255, 104/255);
TextStrokeTransparency = 0;
Text = " StatName:";
TextColor3 = Color3.new(1,1,1);
TextXAlignment = Enum.TextXAlignment.Left;
BorderSizePixel = 0;
ClipsDescendants = true;
Parent = tenFootInterfaceStat;
};
local statValue = statName:clone()
statValue.Position = UDim2.new(0.5,0,0,0)
statValue.Name = "StatValue"
statValue.Text = "123,643,231"
statValue.TextXAlignment = Enum.TextXAlignment.Right
statValue.Parent = tenFootInterfaceStat
addToDisplayStack(tenFootInterfaceStat)
end
local function setDisplayedStat(newStat)
if displayedStatChangedCon then displayedStatChangedCon:disconnect() displayedStatChangedCon = nil end
if displayedStatParentedCon then displayedStatParentedCon:disconnect() displayedStatParentedCon = nil end
displayedStat = newStat
if displayedStat then
makeTenFootInterfaceStat()
updateTenFootStat(displayedStat)
displayedStatParentedCon = displayedStat.AncestryChanged:connect(function() updateTenFootStat(displayedStat, "Parent") end)
displayedStatChangedCon = displayedStat.Changed:connect(function(prop) updateTenFootStat(displayedStat, prop) end)
end
end
function updateTenFootStat(statObj, property)
if property and property == "Parent" then
tenFootInterfaceStat.StatName.Text = ""
tenFootInterfaceStat.StatValue.Text = ""
setDisplayedStat(nil)
tenFootInterfaceChanged()
else
if topStatEnabled then
tenFootInterfaceStat.StatName.Text = " " .. tostring(statObj.Name) .. ":"
tenFootInterfaceStat.StatValue.Text = tostring(statObj.Value)
else
tenFootInterfaceStat.StatName.Text = ""
tenFootInterfaceStat.StatValue.Text = ""
end
end
end
local function isValidStat(obj)
return obj:IsA('StringValue') or obj:IsA('IntValue') or obj:IsA('BoolValue') or obj:IsA('NumberValue') or
obj:IsA('DoubleConstrainedValue') or obj:IsA('IntConstrainedValue')
end
local function tenFootInterfaceNewStat( newStat )
if not displayedStat and isValidStat(newStat) then
setDisplayedStat(newStat)
end
end
function tenFootInterfaceChanged()
game:WaitForChild("Players")
while not game.Players.LocalPlayer do
wait()
end
local leaderstats = game.Players.LocalPlayer:FindFirstChild('leaderstats')
if leaderstats then
local statChildren = leaderstats:GetChildren()
for i = 1, #statChildren do
tenFootInterfaceNewStat(statChildren[i])
end
if leaderstatsChildAddedCon then leaderstatsChildAddedCon:disconnect() end
leaderstatsChildAddedCon = leaderstats.ChildAdded:connect(function(newStat)
tenFootInterfaceNewStat(newStat)
end)
end
end
game:WaitForChild("Players")
while not game.Players.LocalPlayer do
wait()
end
local leaderstats = game.Players.LocalPlayer:FindFirstChild('leaderstats')
if leaderstats then
tenFootInterfaceChanged()
else
game.Players.LocalPlayer.ChildAdded:connect(tenFootInterfaceChanged)
end
--Top Stat Public API
local topStatApiTable = {}
function topStatApiTable:SetTopStatEnabled(value)
topStatEnabled = value
if displayedStat then
updateTenFootStat(displayedStat, "")
end
end
return topStatApiTable
end
return this
end
-- Public API
local moduleApiTable = {}
local TenFootInterfaceModule = CreateModule()
function moduleApiTable:IsEnabled()
return tenFootInterfaceEnabled
end
function moduleApiTable:CreateHealthBar()
return TenFootInterfaceModule:CreateHealthBar()
end
function moduleApiTable:SetupTopStat()
return TenFootInterfaceModule:SetupTopStat()
end
return moduleApiTable
+808
View File
@@ -0,0 +1,808 @@
--Panel3D: 3D GUI panels for VR
--written by 0xBAADF00D
--revised/refactored 5/11/16
local UserInputService = game:GetService("UserInputService")
local RunService = game:GetService("RunService")
local GuiService = game:GetService("GuiService")
local CoreGui = game:GetService("CoreGui")
local RobloxGui = CoreGui:WaitForChild("RobloxGui")
local PlayersService = game.Players
--Util
local Util = {}
do
function Util.Create(instanceType)
return function(data)
local obj = Instance.new(instanceType)
for k, v in pairs(data) do
if type(k) == 'number' then
v.Parent = obj
else
obj[k] = v
end
end
return obj
end
end
-- RayPlaneIntersection (shortened)
-- http://www.siggraph.org/education/materials/HyperGraph/raytrace/rayplane_intersection.htm
function Util.RayPlaneIntersection(ray, planeNormal, pointOnPlane)
planeNormal = planeNormal.unit
ray = ray.Unit
local Vd = planeNormal:Dot(ray.Direction)
if Vd == 0 then -- parallel, no intersection
return nil
end
local V0 = planeNormal:Dot(pointOnPlane - ray.Origin)
local t = V0 / Vd
if t < 0 then --plane is behind ray origin, and thus there is no intersection
return nil
end
return ray.Origin + ray.Direction * t
end
end
--End of Util
--Panel3D State variables
local renderStepName = "Panel3DRenderStep-" .. game:GetService("HttpService"):GenerateGUID()
local defaultPixelsPerStud = 64
local pointUpCF = CFrame.Angles(math.rad(-90), math.rad(180), 0)
local zeroVector = Vector3.new(0, 0, 0)
local zeroVector2 = Vector2.new(0, 0)
local turnAroundCF = CFrame.Angles(0, math.rad(180), 0)
local fullyOpaqueAtPixelsFromEdge = 10
local fullyTransparentAtPixelsFromEdge = 80
local partThickness = 0.2
local cursorHidden = false
local cursorHideTime = 2.5
local currentModal = nil
local lastModal = nil
local currentMaxDist = math.huge
local currentClosest = nil
local lastClosest = nil
local currentHeadScale = 1
local panels = {}
local floorRotation = CFrame.new()
local cursor = Util.Create "ImageLabel" {
Image = "rbxasset://textures/Cursors/Gamepad/Pointer.png",
Size = UDim2.new(0, 8, 0, 8),
BackgroundTransparency = 1,
ZIndex = 10
}
--End of Panel3D State variables
--Panel3D Declaration and enumerations
local Panel3D = {}
Panel3D.Type = {
None = 0,
Floor = 1,
Fixed = 2,
HorizontalFollow = 3,
FixedToHead = 4
}
Panel3D.OnPanelClosed = Util.Create 'BindableEvent' {
Name = 'OnPanelClosed'
}
function Panel3D.GetHeadLookXZ(withTranslation)
local userHeadCF = UserInputService:GetUserCFrame(Enum.UserCFrame.Head)
local headLook = userHeadCF.lookVector
local headYaw = math.atan2(-headLook.Z, headLook.X) + math.rad(90)
local cf = CFrame.Angles(0, headYaw, 0)
if withTranslation then
cf = cf + userHeadCF.p
end
return cf
end
function Panel3D.FindContainerOf(element)
for i, v in pairs(panels) do
if v.gui and v.gui:IsAncestorOf(element) then
return v
end
end
return nil
end
function Panel3D.SetModalPanel(panel)
if currentModal == panel then
return
end
if currentModal then
currentModal:OnModalChanged(false)
end
if panel then
panel:OnModalChanged(true)
end
lastModal = currentModal
currentModal = panel
end
--End of Panel3D Declaration and enumerations
--Cursor autohiding methods
local cursorHidden = false
local hasTool = false
local lastMouseActivity = tick()
local lastMouseBehavior = Enum.MouseBehavior.Default
local function OnCharacterAdded(character)
hasTool = false
for i, v in ipairs(character:GetChildren()) do
if v:IsA("Tool") then
hasTool = true
end
end
character.ChildAdded:connect(function(child)
if child:IsA("Tool") then
hasTool = true
lastMouseActivity = tick() --kick the mouse when a tool is equipped
end
end)
character.ChildRemoved:connect(function(child)
if child:IsA("Tool") then
hasTool = false
end
end)
end
spawn(function()
while not PlayersService.LocalPlayer do wait() end
PlayersService.LocalPlayer.CharacterAdded:connect(OnCharacterAdded)
if PlayersService.LocalPlayer.Character then OnCharacterAdded(PlayersService.LocalPlayer.Character) end
end)
local function autoHideCursor(hide)
if not UserInputService.VREnabled then
cursorHidden = false
return
end
if hide then
--don't hide if there's a tool in the character
local character = PlayersService.LocalPlayer.Character
if character and hasTool then
return
end
cursorHidden = true
UserInputService.OverrideMouseIconBehavior = Enum.OverrideMouseIconBehavior.ForceHide
else
cursorHidden = false
UserInputService.OverrideMouseIconBehavior = Enum.OverrideMouseIconBehavior.None
end
end
local function isCursorVisible()
--if ForceShow, the cursor is definitely visible at all times
if UserInputService.OverrideMouseIconBehavior == Enum.OverrideMouseIconBehavior.ForceShow then
return true
end
--if ForceHide, the cursor is definitely NOT visible
if UserInputService.OverrideMouseIconBehavior == Enum.OverrideMouseIconBehavior.ForceHide then
return false
end
--Otherwise, we need to check if the developer set MouseIconEnabled=false
if UserInputService.MouseIconEnabled and UserInputService.OverrideMouseIconBehavior == Enum.OverrideMouseIconBehavior.None then
return true
end
return false
end
--End of cursor autohiding methods
--Panel class implementation
local Panel = {}
Panel.mt = { __index = Panel }
function Panel.new(name)
local instance = {
name = name,
part = nil,
gui = nil,
width = 1,
height = 1,
isVisible = false,
isEnabled = false,
panelType = Panel3D.Type.None,
pixelScale = 1,
showCursor = true,
canFade = true,
shouldFindLookAtGuiElement = false,
ignoreModal = false,
linkedTo = nil,
subpanels = {},
transparency = 1,
forceShowUntilLookedAt = false,
isLookedAt = false,
isOffscreen = true,
lookAtPixel = Vector2.new(-1, -1),
lookAtDistance = math.huge,
lookAtGuiElement = nil,
isClosest = true
}
if panels[name] then
error("A panel by the name of " .. name .. " already exists.")
end
panels[name] = instance
return setmetatable(instance, Panel.mt)
end
--Panel accessor methods
function Panel:GetPart()
if not self.part then
self.part = Util.Create "Part" {
Name = self.name,
Parent = nil,
Transparency = 1,
CanCollide = false,
Anchored = true,
Archivable = false,
Size = Vector3.new(1, 1, partThickness)
}
end
return self.part
end
function Panel:GetGUI()
if not self.gui then
local part = self:GetPart()
self.gui = Util.Create "SurfaceGui" {
Parent = CoreGui,
Adornee = part,
Active = true,
ToolPunchThroughDistance = 1000,
CanvasSize = Vector2.new(0, 0),
Enabled = self.isEnabled,
AlwaysOnTop = true
}
end
return self.gui
end
function Panel:FindHoveredGuiElement(elements)
local x, y = self.lookAtPixel.X, self.lookAtPixel.Y
for i, v in pairs(elements) do
local minPt = v.AbsolutePosition
local maxPt = v.AbsolutePosition + v.AbsoluteSize
if minPt.X <= x and maxPt.X >= x and
minPt.Y <= y and maxPt.Y >= y then
return v, i
end
end
end
--End of panel accessor methods
--Panel update methods
function Panel:SetPartCFrame(cframe)
self:GetPart().CFrame = cframe * CFrame.new(0, 0, -0.5 * partThickness)
end
function Panel:SetEnabled(enabled)
if self.isEnabled == enabled then
return
end
self.isEnabled = enabled
if enabled then
self:GetPart().Parent = workspace.CurrentCamera --todo: Perhaps this can change soon.
self:GetGUI().Enabled = true
for i, v in pairs(self.subpanels) do
v:GetPart().Parent = workspace.CurrentCamera
v:GetGUI().Enabled = true
end
else
self:GetPart().Parent = nil
self:GetGUI().Enabled = false
for i, v in pairs(self.subpanels) do
v:GetPart().Parent = nil
v:GetGUI().Enabled = false
end
end
self:OnEnabled(enabled)
end
function Panel:EvaluatePositioning(cameraCF, cameraRenderCF, userHeadCF)
if self.panelType == Panel3D.Type.Floor then
--Floor panels simply... go on the floor.
--Panel will be in camera's local space (which is assumed to be horizontal),
--and floorRotation is derived from the user's head rotation (yaw only)
local floorCF = cameraCF * CFrame.new(0, -headHeightFromFloor * currentHeadScale, 0) * floorRotation
self:SetPartCFrame(floorCF * pointUpCF)
elseif self.panelType == Panel3D.Type.Fixed then
--Places the panel in the camera's local space, but doesn't follow the user's head.
--Useful if you know what you're doing. localCF can be updated in PreUpdate for animation.
local cf = self.localCF - self.localCF.p
cf = cf + (self.localCF.p * currentHeadScale)
self:SetPartCFrame(cameraCF * cf)
elseif self.panelType == Panel3D.Type.HorizontalFollow then
local headLook = userHeadCF.lookVector
local headYaw = math.atan2(-headLook.Z, headLook.X) + math.rad(90)
local headForwardCF = CFrame.Angles(0, headYaw, 0) + userHeadCF.p
local localCF = (headForwardCF * self.angleFromForward) * --Rotate about Y (left-right)
self.angleFromHorizon * --Rotate about X (up-down)
(currentHeadScale * self.distance) * --Move into scene
turnAroundCF --Turn around to face character
self:SetPartCFrame(cameraCF * localCF)
elseif self.panelType == Panel3D.Type.FixedToHead then
--Places the panel in the user's head local space. localCF can be updated in PreUpdate for animation.
local cf = self.localCF - self.localCF.p
cf = cf + (self.localCF.p * currentHeadScale)
self:SetPartCFrame(cameraRenderCF * cf)
end
end
function Panel:EvaluateGaze(cameraCF, cameraRenderCF, userHeadCF, lookRay)
--reset distance data
self.isClosest = false
self.lookAtPixel = zeroVector2
self.lookAtDistance = math.huge
--Evaluate the lookRay versus this panel
local planeCF = self:GetPart().CFrame
local planeNormal = planeCF.lookVector
local pointOnPlane = planeCF.p + (planeNormal * partThickness * 0.5) --Move the point out by half the thickness of the part
local worldIntersectPoint = Util.RayPlaneIntersection(lookRay, planeNormal, pointOnPlane)
if worldIntersectPoint then
self.isOffscreen = false
--transform worldIntersectPoint to gui space
local gui = self:GetGUI()
local guiWidth, guiHeight = gui.AbsoluteSize.X, gui.AbsoluteSize.Y
local localIntersectPoint = (planeCF:pointToObjectSpace(worldIntersectPoint) / currentHeadScale) * Vector3.new(-1, 1, 1) + Vector3.new(self.width / 2, -self.height / 2, 0)
self.lookAtPixel = Vector2.new((localIntersectPoint.X / self.width) * gui.AbsoluteSize.X, (localIntersectPoint.Y / self.height) * -gui.AbsoluteSize.Y)
--fire mouse enter/leave events if necessary
local guiX, guiY = self.lookAtPixel.X, self.lookAtPixel.Y
if guiX >= 0 and guiX <= guiWidth and
guiY >= 0 and guiY <= guiHeight then
if not self.isLookedAt then
self.isLookedAt = true
self:OnMouseEnter(guiX, guiY)
if self.forceShowUntilLookedAt then
self.forceShowUntilLookedAt = false
end
end
else
if self.isLookedAt then
self.isLookedAt = false
self:OnMouseLeave(guiX, guiY)
end
end
--evaluate distance
self.lookAtDistance = (worldIntersectPoint - cameraRenderCF.p).magnitude
if self.isLookedAt and self.lookAtDistance < currentMaxDist and self.showCursor then
currentMaxDist = self.lookAtDistance
currentClosest = self
end
else
self.isOffscreen = true
--Not looking at the plane at all, so fire off mouseleave if necessary.
if self.isLookedAt then
self.isLookedAt = false
self:OnMouseLeave(self.lookAtPixel.X, self.lookAtPixel.Y)
end
end
end
function Panel:EvaluateTransparency()
--Early exit if force shown
if self.forceShowUntilLookedAt then
self.transparency = 0
return
end
--Early exit if we're looking at the panel (no transparency!)
if self.isLookedAt then
self.transparency = 0
return
end
--Similarly, exit if we can't possibly see the panel.
if self.isOffscreen then
self.transparency = 1
return
end
--Otherwise, we'll want to calculate the transparency.
self.transparency = self:CalculateTransparency()
end
function Panel:Update(cameraCF, cameraRenderCF, userHeadCF, lookRay)
if self.forceShowUntilLookedAt and not self.part then
self:GetPart()
self:GetGUI()
end
if not self.part then
return
end
local isModal = (currentModal == self)
if not isModal and self.linkedTo and self.linkedTo == currentModal then
isModal = true
end
if currentModal and not isModal then
self:SetEnabled(false)
return
end
self:PreUpdate(cameraCF, cameraRenderCF, userHeadCF, lookRay)
if self.isVisible then
self:EvaluatePositioning(cameraCF, cameraRenderCF, userHeadCF)
self:EvaluateGaze(cameraCF, cameraRenderCF, userHeadCF, lookRay)
self:EvaluateTransparency(cameraCF, cameraRenderCF)
end
end
--End of Panel update methods
--Panel virtual methods
function Panel:PreUpdate() --virtual: handle positioning here
end
function Panel:OnUpdate() --virtual: handle transparency here
end
function Panel:OnMouseEnter(x, y) --virtual
end
function Panel:OnMouseLeave(x, y) --virtual
end
function Panel:OnEnabled(enabled) --virtual
end
function Panel:OnModalChanged(isModal) --virtual
end
function Panel:OnVisibilityChanged(visible) --virtual
end
function Panel:CalculateTransparency() --virtual
if not self.canFade then
return 0
end
local guiWidth, guiHeight = self.gui.AbsoluteSize.X, self.gui.AbsoluteSize.Y
local lookX, lookY = self.lookAtPixel.X, self.lookAtPixel.Y
--Determine the distance from the edge;
--if x is negative it's on the left side, meaning the distance is just absolute value
--if x is positive it's on the right side, meaning the distance is x minus the width
local xEdgeDist = lookX < 0 and -lookX or (lookX - guiWidth)
local yEdgeDist = lookY < 0 and -lookY or (lookY - guiHeight)
if lookX > 0 and lookX < guiWidth then
xEdgeDist = 0
end
if lookY > 0 and lookY < guiHeight then
yEdgeDist = 0
end
local edgeDist = math.sqrt(xEdgeDist ^ 2 + yEdgeDist ^ 2)
--since transparency is 0-1, we know how many pixels will give us 0 and how many will give us 1.
local offset = fullyOpaqueAtPixelsFromEdge
local interval = fullyTransparentAtPixelsFromEdge
--then we just clamp between 0 and 1.
return math.max(0, math.min(1, (edgeDist - offset) / interval))
end
--End of Panel virtual methods
--Panel configuration methods
function Panel:ResizeStuds(width, height, pixelsPerStud)
pixelsPerStud = pixelsPerStud or defaultPixelsPerStud
self.width = width
self.height = height
self.pixelScale = pixelsPerStud / defaultPixelsPerStud
local part = self:GetPart()
part.Size = Vector3.new(self.width * currentHeadScale, self.height * currentHeadScale, partThickness)
local gui = self:GetGUI()
gui.CanvasSize = Vector2.new(pixelsPerStud * self.width, pixelsPerStud * self.height)
end
function Panel:ResizePixels(width, height, pixelsPerStud)
pixelsPerStud = pixelsPerStud or defaultPixelsPerStud
local widthInStuds = width / pixelsPerStud
local heightInStuds = height / pixelsPerStud
self:ResizeStuds(widthInStuds, heightInStuds, pixelsPerStud)
end
function Panel:OnHeadScaleChanged(newHeadScale)
local pixelsPerStud = self.pixelScale * defaultPixelsPerStud
self:ResizeStuds(self.width, self.height, pixelsPerStud)
end
function Panel:SetType(panelType, config)
self.panelType = panelType
--clear out old type-specific members
self.floorPos = nil
self.localCF = nil
self.angleFromHorizon = nil
self.angleFromForward = nil
self.distance = nil
if not config then
config = {}
end
if panelType == Panel3D.Type.None then
--nothing to do
return
elseif panelType == Panel3D.Type.Floor then
self.floorPos = config.FloorPosition or Vector3.new(0, 0, 0)
elseif panelType == Panel3D.Type.Fixed then
self.localCF = config.CFrame or CFrame.new()
elseif panelType == Panel3D.Type.HorizontalFollow then
self.angleFromHorizon = CFrame.Angles(config.angleFromHorizon or 0, 0, 0)
self.angleFromForward = CFrame.Angles(0, config.angleFromForward or 0, 0)
self.distance = CFrame.new(0, 0, config.distance or 5)
elseif panelType == Panel3D.Type.FixedToHead then
self.localCF = config.CFrame or CFrame.new()
else
error("Invalid Panel type")
end
end
function Panel:SetVisible(visible, modal)
if visible ~= self.isVisible then
self:OnVisibilityChanged(visible)
if not visible then
Panel3D.OnPanelClosed:Fire(self.name)
end
end
self.isVisible = visible
self:SetEnabled(visible)
if visible and modal then
Panel3D.SetModalPanel(self)
end
if not visible and currentModal == self then
if modal and not doNotRestore then
--restore last modal panel
Panel3D.SetModalPanel(lastModal)
else
Panel3D.SetModalPanel(nil)
--if the coder explicitly wanted to hide this modal panel,
--it follows that they don't want it to be restored when the next
--modal panel is hidden.
if lastModal == self then
lastModal = nil
end
end
end
if not visible and self.forceShowUntilLookedAt then
self.forceShowUntilLookedAt = false
end
end
function Panel:LinkTo(panelName)
if type(panelName) == "string" then
self.linkedTo = Panel3D.Get(panelName)
else
self.linkedTo = panelName
end
end
function Panel:ForceShowUntilLookedAt(makeModal)
--ensure the part exists
self:GetPart()
self:GetGUI()
self:SetVisible(true, makeModal)
self.forceShowUntilLookedAt = true
end
function Panel:SetCanFade(canFade)
self.canFade = canFade
end
--Child class, Subpanel
local Subpanel = {}
local Subpanel_mt = {}
function Subpanel.new(guiElement)
local instance = {
guiElement = guiElement
}
return setmetatable(instance, Subpanel_mt)
end
function Subpanel:GetPart()
warn("Subpanel:GetPart() not yet implemented")
return nil
end
function Subpanel:GetGUI()
warn("Subpanel:GetGUI() not yet implemented")
return nil
end
function Panel:AddSubpanel(guiElement)
local subpanel = Subpanel.new(guiElement)
self.subpanels[guiElement] = subpanel
end
function Panel:RemoveSubpanel(guiElement)
self.subpanels[guiElement] = nil
end
--End of Panel configuration methods
--End of Panel class implementation
--Panel3D API
function Panel3D.Get(name)
local panel = panels[name]
if not panels[name] then
panels[name] = Panel.new(name)
panel = panels[name]
end
return panel
end
--End of Panel3D API
--Panel3D Setup
local function onRenderStep()
if not UserInputService.VREnabled then
return
end
--reset distance info
currentClosest = nil
currentMaxDist = math.huge
--figure out some useful stuff
local camera = workspace.CurrentCamera
local cameraCF = camera.CFrame
local cameraRenderCF = camera:GetRenderCFrame()
local userHeadCF = UserInputService:GetUserCFrame(Enum.UserCFrame.Head)
local lookRay = Ray.new(cameraRenderCF.p, cameraRenderCF.lookVector)
--allow all panels to run their own update code
for i, v in pairs(panels) do
v:Update(cameraCF, cameraRenderCF, userHeadCF, lookRay)
end
--evaluate linked panels
local processed = {}
for i, v in pairs(panels) do
if not processed[v] and v.linkedTo and v.isVisible and v.linkedTo.isVisible then
processed[v] = true
processed[v.linkedTo] = true
local minTransparency = math.min(v.transparency, v.linkedTo.transparency)
v.transparency = minTransparency
v.linkedTo.transparency = minTransparency
end
end
--run post update because the distance information hasn't been
--finalized until now.
for i, v in pairs(panels) do
--If the part is fully transparent, we don't want to keep it around in the workspace.
if v.part and v.gui then
--check if this panel is the current modal panel
local isModal = (currentModal == v)
--but also check if this panel is linked to the current modal panel
if not isModal and v.linkedTo and v.linkedTo == currentModal then
isModal = true
end
local show = v.isVisible
if not isModal and currentModal then
show = false
end
if v.transparency >= 1 then
show = false
end
if v.forceShowUntilLookedAt then
show = true
end
if not v.canFade and v.isVisible then
show = true
end
v:SetEnabled(show)
end
v:OnUpdate()
end
--place the cursor on the closest panel (for now)
if not currentClosest and lastClosest then
UserInputService.MouseBehavior = lastMouseBehavior
elseif currentClosest and not lastClosest then
lastMouseBehavior = UserInputService.MouseBehavior
end
if currentClosest then
UserInputService.MouseBehavior = Enum.MouseBehavior.LockCenter
UserInputService.OverrideMouseIconBehavior = Enum.OverrideMouseIconBehavior.ForceHide
cursor.Parent = currentClosest:GetGUI()
local x, y = currentClosest.lookAtPixel.X, currentClosest.lookAtPixel.Y
cursor.Size = UDim2.new(0, 8 * currentClosest.pixelScale, 0, 8 * currentClosest.pixelScale)
cursor.Position = UDim2.new(0, x - cursor.AbsoluteSize.x * 0.5, 0, y - cursor.AbsoluteSize.y * 0.5)
else
cursor.Parent = nil
end
lastClosest = currentClosest
end
RunService:BindToRenderStep(renderStepName, Enum.RenderPriority.Last.Value, onRenderStep)
--Implement cursor autohide functionality
UserInputService.InputChanged:connect(function(inputObj, processed)
if inputObj.UserInputType == Enum.UserInputType.MouseMovement then
lastMouseActivity = tick()
autoHideCursor(false)
end
end)
local function onHeartbeat()
if isCursorVisible() then
cursorHidden = false
end
if lastMouseActivity + cursorHideTime < tick() and not GuiService.MenuIsOpen and not cursorHidden then
autoHideCursor(true)
end
end
RunService.Heartbeat:connect(onHeartbeat)
--Handle camera changes
local cameraChangedConnection = nil
local function onCameraChanged(prop)
if prop == "HeadScale" then
pcall(function()
currentHeadScale = workspace.CurrentCamera.HeadScale
end)
for i, v in pairs(panels) do
v:OnHeadScaleChanged(currentHeadScale)
end
end
end
local function onWorkspaceChanged(prop)
if prop == "CurrentCamera" then
onCameraChanged("HeadScale")
if cameraChangedConnection then
cameraChangedConnection:disconnect()
end
cameraChangedConnection = workspace.CurrentCamera.Changed:connect(onCameraChanged)
end
end
if workspace.CurrentCamera then
onWorkspaceChanged("CurrentCamera")
end
workspace.Changed:connect(onWorkspaceChanged)
return Panel3D
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,216 @@
--[[
// FileName: PlayerlistModule.lua
// Version 1.0
// Written by: jmargh
// Description: Implements social features that need to be ran on the server
// TODO
We need to get module script working on the server. When we get that working
This should be moved to a module, and http helper functions should be moved
to a utility module.
]]
local HttpService = game:GetService('HttpService')
local HttpRbxApiService = game:GetService('HttpRbxApiService')
local Players = game:GetService('Players')
local RobloxReplicatedStorage = game:GetService('RobloxReplicatedStorage')
local RunService = game:GetService('RunService')
local GET_MULTI_FOLLOW = "user/multi-following-exists"
local PlayerToRelationshipMap = {}
--[[ Remotes ]]--
local RemoteEvent_FollowRelationshipChanged = Instance.new('RemoteEvent')
RemoteEvent_FollowRelationshipChanged.Name = "FollowRelationshipChanged"
RemoteEvent_FollowRelationshipChanged.Parent = RobloxReplicatedStorage
local RemoteEvent_NewFollower = Instance.new("RemoteEvent")
RemoteEvent_NewFollower.Name = "NewFollower"
RemoteEvent_NewFollower.Parent = RobloxReplicatedStorage
local RemoteFunc_GetFollowRelationships = Instance.new('RemoteFunction')
RemoteFunc_GetFollowRelationships.Name = "GetFollowRelationships"
RemoteFunc_GetFollowRelationships.Parent = RobloxReplicatedStorage
--[[ Helper Functions ]]--
local function decodeJSON(json)
local success, result = pcall(function()
return HttpService:JSONDecode(json)
end)
if not success then
print("decodeJSON() failed because", result, "Input:", json)
return nil
end
return result
end
local function rbxApiPostAsync(path, params, useHttps, throttlePriority, contentType)
local success, result = pcall(function()
return HttpRbxApiService:PostAsync(path, params, useHttps, throttlePriority, contentType)
end)
--
if not success then
local label = string.format("%s: - path: %s, \njson: %s", tostring(result), tostring(path), tostring(params))
return nil
end
return decodeJSON(result)
end
--[[
// Return - table
Key: FollowingDetails
Value: Arrary of details
Key: UserId1
Value: number - userId of new client
Key: UserId2
Value: number - userId of other client
Key: User1FollowsUser2
Value: boolean
Key: User2FollowsUser1
Value: boolean
]]
local function getFollowRelationshipsAsync(uid)
if RunService:IsStudio() then
return
end
local otherUserIdTable = {}
for _,player in pairs(Players:GetPlayers()) do
if player.UserId > 0 then
table.insert(otherUserIdTable, player.UserId)
end
end
if #otherUserIdTable > 0 and uid and uid > 0 then
local jsonPostBody = {
userId = uid;
otherUserIds = otherUserIdTable;
}
jsonPostBody = HttpService:JSONEncode(jsonPostBody)
if jsonPostBody then
return rbxApiPostAsync(GET_MULTI_FOLLOW, jsonPostBody, true, Enum.ThrottlingPriority.Default, Enum.HttpContentType.ApplicationJson)
end
end
end
local function createRelationshipObject(user1FollowsUser2, user2FollowsUser1)
local object = {}
object.IsFollower = user2FollowsUser1
object.IsFollowing = user1FollowsUser2
object.IsMutual = user1FollowsUser2 and user2FollowsUser1
return object
end
local function updateAndNotifyClients(resultTable, newUserIdStr, newPlayer)
local followingDetails = resultTable["FollowingDetails"]
if followingDetails then
local relationshipTable = PlayerToRelationshipMap[newUserIdStr] or {}
for i = 1, #followingDetails do
local detail = followingDetails[i]
local otherUserId = detail["UserId2"]
local otherUserIdStr = tostring(otherUserId)
local followsOther = detail["User1FollowsUser2"]
local followsNewPlayer = detail["User2FollowsUser1"]
relationshipTable[otherUserIdStr] = createRelationshipObject(followsOther, followsNewPlayer)
-- update other use
local otherRelationshipTable = PlayerToRelationshipMap[otherUserIdStr]
if otherRelationshipTable then
local newRelationship = createRelationshipObject(followsNewPlayer, followsOther)
otherRelationshipTable[newUserIdStr] = newRelationship
local otherPlayer = Players:GetPlayerByUserId(otherUserId)
if otherPlayer then
-- create single entry table (keep format same) and send to other client
local deltaTable = {}
deltaTable[newUserIdStr] = newRelationship
RemoteEvent_FollowRelationshipChanged:FireClient(otherPlayer, deltaTable)
end
end
end
PlayerToRelationshipMap[newUserIdStr] = relationshipTable
RemoteEvent_FollowRelationshipChanged:FireClient(newPlayer, relationshipTable)
end
end
--[[ Connections ]]--
function RemoteFunc_GetFollowRelationships.OnServerInvoke(player)
local uid = player.userId
local uidStr = tostring(player.userId)
if uid and uid > 0 and PlayerToRelationshipMap[uidStr] then
return PlayerToRelationshipMap[uidStr]
else
return {}
end
end
-- client fires event to server on new follow
RemoteEvent_NewFollower.OnServerEvent:connect(function(player1, player2, player1FollowsPlayer2)
if player1FollowsPlayer2 == nil then
return
end
local userId1 = tostring(player1.userId)
local userId2 = tostring(player2.userId)
local user1map = PlayerToRelationshipMap[userId1]
local user2map = PlayerToRelationshipMap[userId2]
if user1map then
local relationTable = user1map[userId2]
if relationTable then
relationTable.IsFollowing = player1FollowsPlayer2
relationTable.IsMutual = relationTable.IsFollowing and relationTable.IsFollower
local delta = {}
delta[userId2] = relationTable
RemoteEvent_FollowRelationshipChanged:FireClient(player1, delta)
-- this should be updated, but current NotificationScript listens to this
if player1FollowsPlayer2 then
RemoteEvent_NewFollower:FireClient(player2, player1)
end
end
end
if user2map then
local relationTable = user2map[userId1]
if relationTable then
relationTable.IsFollower = player1FollowsPlayer2
relationTable.IsMutual = relationTable.IsFollowing and relationTable.IsFollower
local delta = {}
delta[userId1] = relationTable
RemoteEvent_FollowRelationshipChanged:FireClient(player2, delta)
end
end
end)
local function onPlayerAdded(newPlayer)
local uid = newPlayer.UserId
if uid > 0 then
local uidStr = tostring(uid)
local result = getFollowRelationshipsAsync(uid)
if result then
updateAndNotifyClients(result, uidStr, newPlayer)
end
end
end
Players.PlayerAdded:connect(onPlayerAdded)
for _,player in pairs(Players:GetPlayers()) do
onPlayerAdded(player)
end
Players.PlayerRemoving:connect(function(prevPlayer)
local uid = tostring(prevPlayer.UserId)
if PlayerToRelationshipMap[uid] then
PlayerToRelationshipMap[uid] = nil
end
end)
@@ -0,0 +1,56 @@
--[[
// Filename: ServerStarterScript.lua
// Version: 1.0
// Description: Server core script that handles core script server side logic.
]]--
-- Prevent server script from running in Studio when not in run mode
local runService = nil
while runService == nil or not runService:IsRunning() do
wait(0.1)
runService = game:GetService('RunService')
end
--[[ Services ]]--
local RobloxReplicatedStorage = game:GetService('RobloxReplicatedStorage')
local ScriptContext = game:GetService('ScriptContext')
--[[ Fast Flags ]]--
local serverFollowersSuccess, serverFollowersEnabled = pcall(function() return settings():GetFFlag("UserServerFollowers") end)
local IsServerFollowers = serverFollowersSuccess and serverFollowersEnabled
local RemoteEvent_NewFollower = nil
--[[ Add Server CoreScript ]]--
-- TODO: FFlag check
if IsServerFollowers then
ScriptContext:AddCoreScriptLocal("ServerCoreScripts/ServerSocialScript", script.Parent)
else
-- above script will create this now
RemoteEvent_NewFollower = Instance.new('RemoteEvent')
RemoteEvent_NewFollower.Name = "NewFollower"
RemoteEvent_NewFollower.Parent = RobloxReplicatedStorage
end
--[[ Remote Events ]]--
local RemoteEvent_SetDialogInUse = Instance.new("RemoteEvent")
RemoteEvent_SetDialogInUse.Name = "SetDialogInUse"
RemoteEvent_SetDialogInUse.Parent = RobloxReplicatedStorage
--[[ Event Connections ]]--
-- Params:
-- followerRbxPlayer: player object of the new follower, this is the client who wants to follow another
-- followedRbxPlayer: player object of the person being followed
local function onNewFollower(followerRbxPlayer, followedRbxPlayer)
RemoteEvent_NewFollower:FireClient(followedRbxPlayer, followerRbxPlayer)
end
if RemoteEvent_NewFollower then
RemoteEvent_NewFollower.OnServerEvent:connect(onNewFollower)
end
local function setDialogInUse(player, dialog, value)
if dialog ~= nil then
dialog.InUse = value
end
end
RemoteEvent_SetDialogInUse.OnServerEvent:connect(setDialogInUse)
+60
View File
@@ -0,0 +1,60 @@
-- Creates all neccessary scripts for the gui on initial load, everything except build tools
-- Created by Ben T. 10/29/10
-- Please note that these are loaded in a specific order to diminish errors/perceived load time by user
local scriptContext = game:GetService("ScriptContext")
local touchEnabled = game:GetService("UserInputService").TouchEnabled
local RobloxGui = game:GetService("CoreGui"):WaitForChild("RobloxGui")
local soundFolder = Instance.new("Folder")
soundFolder.Name = "Sounds"
soundFolder.Parent = RobloxGui
-- TopBar
scriptContext:AddCoreScriptLocal("CoreScripts/Topbar", RobloxGui)
-- SettingsScript
local luaControlsSuccess, luaControlsFlagValue = pcall(function() return settings():GetFFlag("UseLuaCameraAndControl") end)
local vrKeyboardSuccess, vrKeyboardFlagValue = pcall(function() return settings():GetFFlag("UseVRKeyboardInLua") end)
local useVRKeyboard = (vrKeyboardSuccess and vrKeyboardFlagValue == true)
-- MainBotChatScript (the Lua part of Dialogs)
scriptContext:AddCoreScriptLocal("CoreScripts/MainBotChatScript2", RobloxGui)
-- Developer Console Script
scriptContext:AddCoreScriptLocal("CoreScripts/DeveloperConsole", RobloxGui)
-- In-game notifications script
scriptContext:AddCoreScriptLocal("CoreScripts/NotificationScript2", RobloxGui)
-- Chat script
spawn(function() require(RobloxGui.Modules.Chat) end)
spawn(function() require(RobloxGui.Modules.PlayerlistModule) end)
scriptContext:AddCoreScriptLocal("CoreScripts/BubbleChat", RobloxGui)
-- Purchase Prompt Script
scriptContext:AddCoreScriptLocal("CoreScripts/PurchasePromptScript2", RobloxGui)
-- Backpack!
spawn(function() require(RobloxGui.Modules.BackpackScript) end)
scriptContext:AddCoreScriptLocal("CoreScripts/VehicleHud", RobloxGui)
scriptContext:AddCoreScriptLocal("CoreScripts/GamepadMenu", RobloxGui)
if touchEnabled then -- touch devices don't use same control frame
-- only used for touch device button generation
scriptContext:AddCoreScriptLocal("CoreScripts/ContextActionTouch", RobloxGui)
RobloxGui:WaitForChild("ControlFrame")
RobloxGui.ControlFrame:WaitForChild("BottomLeftControl")
RobloxGui.ControlFrame.BottomLeftControl.Visible = false
end
if useVRKeyboard then
require(RobloxGui.Modules.VR.VirtualKeyboard)
end