add
This commit is contained in:
@@ -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
@@ -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
@@ -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
|
||||
@@ -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)
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user