mirror of
https://github.com/copyrighttxt/watrbx-game-engine.git
synced 2026-09-05 05:07:48 +00:00
GEEKING
This commit is contained in:
@@ -0,0 +1,197 @@
|
||||
--[[
|
||||
// AccountManager.lua
|
||||
|
||||
// Handles all account related functions
|
||||
]]
|
||||
local CoreGui = Game:GetService("CoreGui")
|
||||
local GuiRoot = CoreGui:FindFirstChild("RobloxGui")
|
||||
local Modules = GuiRoot:FindFirstChild("Modules")
|
||||
|
||||
local PlatformService = nil
|
||||
pcall(function() PlatformService = game:GetService('PlatformService') end)
|
||||
local UserInputService = game:GetService('UserInputService')
|
||||
|
||||
local Http = require(Modules:FindFirstChild('Http'))
|
||||
|
||||
local AccountManager = {}
|
||||
|
||||
AccountManager.AuthResults = {
|
||||
Error = -1;
|
||||
Success = 0;
|
||||
InProgress = 1;
|
||||
AccountUnlinked = 2;
|
||||
MissingGamePad = 3;
|
||||
NoUserDetected = 4;
|
||||
HttpErrorDetected = 5;
|
||||
SignUpDisabled = 6;
|
||||
Flooded = 7;
|
||||
LeaseLocked = 8;
|
||||
AccountLinkingDisabled = 9;
|
||||
InvalidRobloxUser = 10;
|
||||
RobloxUserAlreadyLinked = 11;
|
||||
XboxUserAlreadyLinked = 12;
|
||||
IllgealChildAccountLinking = 13;
|
||||
InvalidPassword = 14;
|
||||
UsernamePasswordNotSet = 15;
|
||||
UsernameAlreadyTaken = 16;
|
||||
}
|
||||
|
||||
AccountManager.InvalidUsernameReasons = {
|
||||
Valid = "Valid";
|
||||
InvalidUsername = "Invalid Username";
|
||||
AlreadyTaken = "Already Taken";
|
||||
InvalidCharactersUsed = "Invalid Characters Used";
|
||||
UsernameCannotContainSpaces = "Username Cannot Contain Spaces";
|
||||
}
|
||||
|
||||
--[[ Authentication ]]--
|
||||
local function authenticateStudio()
|
||||
return AccountManager.AuthResults.Success
|
||||
end
|
||||
|
||||
-- TODO: Auth will now return an int that maps to a success or failure
|
||||
-- Getting the GamerTag and rbxUid will be a seperate call after auth
|
||||
local function authenticateXBox(inputObject)
|
||||
local authResult = nil
|
||||
local success, result = pcall(function()
|
||||
-- PlatformService may not exist on studio platform
|
||||
authResult = PlatformService:BeginAuthUnlinkCheck(inputObject)
|
||||
end)
|
||||
|
||||
return authResult
|
||||
end
|
||||
|
||||
function AccountManager:SignInAsync(inputObject)
|
||||
local success, result = pcall(function()
|
||||
-- PlatformService may not exist on studio platform
|
||||
return PlatformService:BeginAuthorization(inputObject)
|
||||
end)
|
||||
|
||||
if success then
|
||||
return result
|
||||
else
|
||||
return AccountManager.AuthResults.Error
|
||||
end
|
||||
end
|
||||
|
||||
-- Returns
|
||||
-- Success of authentication, with authentication state
|
||||
function AccountManager:BeginAuthenticationAsync(inputObject)
|
||||
local result = nil
|
||||
if UserSettings().GameSettings:InStudioMode() then
|
||||
result = authenticateStudio()
|
||||
elseif PlatformService and UserInputService:GetPlatform() == Enum.Platform.XBoxOne then
|
||||
result = authenticateXBox(inputObject)
|
||||
end
|
||||
|
||||
return result
|
||||
end
|
||||
|
||||
--[[ Account Linking ]]--
|
||||
-- called at sign in
|
||||
function AccountManager:LinkAccountAsync(accountName, password)
|
||||
local success, result = pcall(function()
|
||||
-- PlatformService may not exist on studio platform
|
||||
return PlatformService:BeginAccountLink(accountName, password)
|
||||
end)
|
||||
if not success then
|
||||
print("AccountManager:LinkAccountAsync() failed because", result)
|
||||
result = AccountManager.AuthResults.Error
|
||||
end
|
||||
|
||||
return result
|
||||
end
|
||||
|
||||
-- used when setting credentials for a generated account
|
||||
function AccountManager:SetRobloxCredentialsAsync(accountName, password)
|
||||
local success, result = pcall(function()
|
||||
-- PlatformService may not exist on studio platform
|
||||
return PlatformService:BeginSetRobloxCredentials(accountName, password)
|
||||
end)
|
||||
if not success then
|
||||
print("AccountManager:SetRobloxCredentialsAsync() failed because", result)
|
||||
result = AccountManager.AuthResults.Error
|
||||
end
|
||||
|
||||
return result
|
||||
end
|
||||
|
||||
-- used when creating a new roblox account that is linked to the users xbox account
|
||||
function AccountManager:GenerateAccountAsync(accountName, password)
|
||||
local result = AccountManager:SignInAsync(Enum.UserInputType.Gamepad1)
|
||||
if result == AccountManager.AuthResults.Success then
|
||||
result = AccountManager:SetRobloxCredentialsAsync(accountName, password)
|
||||
end
|
||||
|
||||
return result
|
||||
end
|
||||
|
||||
-- called when user has roblox credentials
|
||||
function AccountManager:UnlinkAccountAsync()
|
||||
local success, result = pcall(function()
|
||||
-- PlatformService may not exist on studio platform
|
||||
return PlatformService:BeginUnlinkAccount()
|
||||
end)
|
||||
if not success then
|
||||
print("AccountManager:UnlinkAccountAsync() failed because", result)
|
||||
result = AccountManager.AuthResults.Error
|
||||
end
|
||||
|
||||
return result
|
||||
end
|
||||
|
||||
function AccountManager:HasLinkedAccountAsync()
|
||||
if UserSettings().GameSettings:InStudioMode() then
|
||||
return AccountManager.AuthResults.Success
|
||||
end
|
||||
|
||||
local success, result = pcall(function()
|
||||
-- PlatformService may not exist on studio platform
|
||||
return PlatformService:BeginHasLinkedAccount()
|
||||
end)
|
||||
if not success then
|
||||
print("AccountManager:HasLinkedAccountAsync() failed because", result)
|
||||
result = AccountManager.AuthResults.Error
|
||||
end
|
||||
|
||||
return result
|
||||
end
|
||||
|
||||
function AccountManager:HasRobloxCredentialsAsync()
|
||||
if UserSettings().GameSettings:InStudioMode() then
|
||||
return AccountManager.AuthResults.Success
|
||||
end
|
||||
|
||||
local success, result = pcall(function()
|
||||
-- PlatformService may not exist on studio platform
|
||||
return PlatformService:BeginHasRobloxCredentials()
|
||||
end)
|
||||
if not success then
|
||||
print("AccountManager:HasRobloxCredentialsAsync() failed because", result)
|
||||
result = AccountManager.AuthResults.Error
|
||||
end
|
||||
|
||||
return result
|
||||
end
|
||||
|
||||
function AccountManager:IsValidUsernameAsync(username)
|
||||
local result = Http.IsValidUsername(username)
|
||||
if not result then
|
||||
-- return false
|
||||
return nil
|
||||
end
|
||||
|
||||
return result["IsValid"], result["ErrorMessage"]
|
||||
end
|
||||
|
||||
function AccountManager:IsValidPasswordAsync(username, password)
|
||||
local result = Http.IsValidPassword(username, password)
|
||||
if not result then
|
||||
-- return false
|
||||
return nil
|
||||
end
|
||||
|
||||
return result["IsValid"], result["ErrorMessage"]
|
||||
end
|
||||
|
||||
return AccountManager
|
||||
@@ -0,0 +1,51 @@
|
||||
--[[
|
||||
// AccountPage.lua
|
||||
]]
|
||||
local CoreGui = Game:GetService("CoreGui")
|
||||
local GuiRoot = CoreGui:FindFirstChild("RobloxGui")
|
||||
local Modules = GuiRoot:FindFirstChild("Modules")
|
||||
|
||||
local ContextActionService = game:GetService('ContextActionService')
|
||||
|
||||
local GuiService = game:GetService('GuiService')
|
||||
local AccountManager = require(Modules:FindFirstChild('AccountManager'))
|
||||
local AssetManager = require(Modules:FindFirstChild('AssetManager'))
|
||||
local BaseScreen = require(Modules:FindFirstChild('BaseScreen'))
|
||||
local GlobalSettings = require(Modules:FindFirstChild('GlobalSettings'))
|
||||
local LoadingWidget = require(Modules:FindFirstChild('LoadingWidget'))
|
||||
local ScreenManager = require(Modules:FindFirstChild('ScreenManager'))
|
||||
local Utility = require(Modules:FindFirstChild('Utility'))
|
||||
local UserData = require(Modules:FindFirstChild('UserData'))
|
||||
local Strings = require(Modules:FindFirstChild('LocalizedStrings'))
|
||||
|
||||
local SetAccountCredentialsScreen = require(Modules:FindFirstChild('SetAccountCredentialsScreen'))
|
||||
local UnlinkAccountScreen = require(Modules:FindFirstChild('UnlinkAccountScreen'))
|
||||
local LinkAccountScreen = require(Modules:FindFirstChild('LinkAccountScreen'))
|
||||
|
||||
-- This is an empty page that is a place holder. Account page changes depending on cases
|
||||
local function createAccountScreen()
|
||||
local hasLinkedAccount = UserData:HasLinkedAccount()
|
||||
local hasRobloxCredentials = UserData:HasRobloxCredentials()
|
||||
|
||||
-- Cases
|
||||
-- 1. Has roblox credentials, which implies they have a linked account
|
||||
-- 2. No credentials but a linked account
|
||||
-- 3. No Credentials/No Linked account - this should never happen, but cover it
|
||||
-- 4. One of these calls has a web error, result will be nil in that case
|
||||
|
||||
local this = nil
|
||||
|
||||
|
||||
if hasRobloxCredentials ~= nil and hasLinkedAccount ~= nil then
|
||||
if hasRobloxCredentials == true then
|
||||
this = UnlinkAccountScreen()
|
||||
elseif hasLinkedAccount == true and hasRobloxCredentials == false then
|
||||
this = SetAccountCredentialsScreen(Strings:LocalizedString("SignUpTitle"),
|
||||
Strings:LocalizedString("SignUpPhrase"), Strings:LocalizedString("SignUpWord"))
|
||||
end
|
||||
end
|
||||
|
||||
return this
|
||||
end
|
||||
|
||||
return createAccountScreen
|
||||
@@ -0,0 +1,262 @@
|
||||
-- Written by Kip Turner, Copyright ROBLOX 2015
|
||||
|
||||
-- Achievement Manager
|
||||
|
||||
local PlatformService = nil
|
||||
pcall(function() PlatformService = game:GetService('PlatformService') end)
|
||||
|
||||
local CoreGui = Game:GetService("CoreGui")
|
||||
local GuiRoot = CoreGui:FindFirstChild("RobloxGui")
|
||||
local Modules = GuiRoot:FindFirstChild("Modules")
|
||||
|
||||
local EventHub = require(Modules:FindFirstChild('EventHub'))
|
||||
local Http = require(Modules:FindFirstChild('Http'))
|
||||
local UserData = require(Modules:FindFirstChild('UserData'))
|
||||
local PlatformInterface = require(Modules:FindFirstChild('PlatformInterface'))
|
||||
local GameCollection = require(Modules:FindFirstChild('GameCollection'))
|
||||
|
||||
--[[ ACHIEVEMENT NAMES --]]
|
||||
-- "Award10DayRoll"
|
||||
-- "Award20DayRoll"
|
||||
-- "Award3DayRoll"
|
||||
-- "AwardDeepDiver"
|
||||
-- "AwardFoursCompany"
|
||||
-- "AwardOneNameManyFaces"
|
||||
-- "AwardPollster"
|
||||
-- "AwardSampler"
|
||||
-- "AwardStrengthInNumbers"
|
||||
-- "AwardWorldTraveler"
|
||||
-- "AwardYouDidIt"
|
||||
-- "GameProgress"
|
||||
-- "MultiplayerRoundEnd"
|
||||
-- "MultiplayerRoundStart"
|
||||
-- "PlayerSessionEnd"
|
||||
-- "PlayerSessionPause"
|
||||
-- "PlayerSessionResume"
|
||||
-- "PlayerSessionStart"
|
||||
-- "Test_XPresses"
|
||||
--[[ END OF ACHIEVEMENT NAMES --]]
|
||||
|
||||
|
||||
|
||||
local VIEW_GAMETYPE_ENUM =
|
||||
{
|
||||
AppShell = 0;
|
||||
Game = 1;
|
||||
}
|
||||
|
||||
|
||||
local GAMES_FOR_YOU_DID_IT = 1
|
||||
local GAMES_FOR_AWARD_SAMPLER = 5
|
||||
|
||||
local DAYS_FOR_3DAYROLL = 3
|
||||
local DAYS_FOR_10DAYROLL = 10
|
||||
local DAYS_FOR_20DAYROLL = 20
|
||||
|
||||
local GAMES_RATED_FOR_POLLSTER = 5
|
||||
|
||||
local PLAY_SECONDS_FOR_DEEP_DIVER = 60 * 60
|
||||
|
||||
local NUMBER_OF_FRIENDS_REQUIRED_FOR_FOURS_COMPANY = 3
|
||||
|
||||
|
||||
local SECONDS_BETWEEN_FOURS_COMPANY_CHECKS = 30
|
||||
|
||||
|
||||
local AchievementManager = {}
|
||||
|
||||
local CurrentView = VIEW_GAMETYPE_ENUM['AppShell']
|
||||
|
||||
|
||||
local function GetTotalNumberOfGamesOnXbox()
|
||||
-- TODO: is there a programmatic way of figuring this out?
|
||||
-- local SortData = require(SortDataModule)
|
||||
-- local recentlyPlayedSortData = SortData.GetSort(1, false)
|
||||
return 15
|
||||
end
|
||||
|
||||
local function FilterInGameFriends(onlineFriends, playersInGame)
|
||||
local result = {}
|
||||
|
||||
if onlineFriends and playersInGame then
|
||||
-- Create reverse lookup for speed
|
||||
local playersInGameReverseLookup = {}
|
||||
for _, playerInGame in pairs(playersInGame) do
|
||||
-- TODO: Figure out what the actual lookup
|
||||
if playerInGame['robloxuid'] then
|
||||
playersInGameReverseLookup[playerInGame['robloxuid']] = true
|
||||
end
|
||||
end
|
||||
|
||||
for _, friend in pairs(onlineFriends) do
|
||||
if playersInGameReverseLookup[friend['robloxuid']] then
|
||||
table.insert(result, friend)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return result
|
||||
end
|
||||
|
||||
|
||||
local function OnPlayedGamesChanged()
|
||||
spawn(function()
|
||||
local myUserId = UserData:GetRbxUserId()
|
||||
if myUserId then
|
||||
local recentCollection = GameCollection:GetUserRecent()
|
||||
-- TODO: is this the right way of getting num of played games?
|
||||
local recentlyPage1 = recentCollection and recentCollection:GetSortAsync(0, GetTotalNumberOfGamesOnXbox())
|
||||
local gamesPlayed = recentlyPage1 and #recentlyPage1:GetPagePlaceIds() or 0
|
||||
print("You have played:" , gamesPlayed , "games" )
|
||||
if gamesPlayed >= GAMES_FOR_YOU_DID_IT then
|
||||
AchievementManager:SendAchievementEventAsync("AwardYouDidIt")
|
||||
end
|
||||
if gamesPlayed >= GAMES_FOR_AWARD_SAMPLER then
|
||||
AchievementManager:SendAchievementEventAsync("AwardSampler")
|
||||
end
|
||||
if gamesPlayed >= GetTotalNumberOfGamesOnXbox() then
|
||||
AchievementManager:SendAchievementEventAsync("AwardWorldTraveler")
|
||||
end
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
local function OnJoinedGame()
|
||||
spawn(function()
|
||||
print("OnJoinGame: AwardStrengthInNumbers check")
|
||||
local partyMembers = PlatformInterface:GetPartyMembersAsync()
|
||||
if partyMembers then
|
||||
if PlatformInterface:IsInAParty(partyMembers) then
|
||||
AchievementManager:SendAchievementEventAsync("AwardStrengthInNumbers")
|
||||
end
|
||||
end
|
||||
end)
|
||||
spawn(function()
|
||||
print("OnJoinGame: Fours Company check")
|
||||
|
||||
if PlatformService then
|
||||
local lastCheck = 0
|
||||
while CurrentView == VIEW_GAMETYPE_ENUM['Game'] do
|
||||
local now = tick()
|
||||
if now - lastCheck > SECONDS_BETWEEN_FOURS_COMPANY_CHECKS then
|
||||
|
||||
local friendsData = require(Modules:FindFirstChild('FriendsData'))
|
||||
local onlineFriends = friendsData.GetOnlineFriendsAsync()
|
||||
|
||||
-- TODO: add actually API
|
||||
local inGamePlayers = PlatformService:GetInGamePlayers()
|
||||
if inGamePlayers and onlineFriends then
|
||||
local inGameFriends = FilterInGameFriends(onlineFriends, inGamePlayers)
|
||||
|
||||
if #inGameFriends >= NUMBER_OF_FRIENDS_REQUIRED_FOR_FOURS_COMPANY then
|
||||
AchievementManager:SendAchievementEventAsync("AwardFoursCompany")
|
||||
return
|
||||
end
|
||||
|
||||
end
|
||||
lastCheck = now
|
||||
end
|
||||
wait(1)
|
||||
end
|
||||
end
|
||||
end)
|
||||
spawn(function()
|
||||
local startTime = tick()
|
||||
while tick() - startTime < PLAY_SECONDS_FOR_DEEP_DIVER do
|
||||
if CurrentView ~= VIEW_GAMETYPE_ENUM['Game'] then
|
||||
return
|
||||
end
|
||||
wait(1)
|
||||
end
|
||||
if CurrentView == VIEW_GAMETYPE_ENUM['Game'] then
|
||||
AchievementManager:SendAchievementEventAsync("AwardDeepDiver")
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
function AchievementManager:SendAchievementEventAsync(achievementName)
|
||||
print("Achievement Manager - Awarding achievement:" , achievementName)
|
||||
local achievementStatus = nil
|
||||
local success, msg = pcall(function()
|
||||
-- NOTE: Yielding function
|
||||
if not UserSettings().GameSettings:InStudioMode() then
|
||||
achievementStatus = PlatformService:BeginAwardAchievement(achievementName)
|
||||
end
|
||||
end)
|
||||
if not success then
|
||||
-- NOTE: very likely this function ever throws an error but returns error codes
|
||||
print("Achievement Manager - Unable to award achievement:" , achievementName , "for reason:" , msg)
|
||||
end
|
||||
|
||||
print("Achievement Manager - Achievement:" , achievementName , "event status:" , achievementStatus)
|
||||
end
|
||||
|
||||
|
||||
EventHub:addEventListener(EventHub.Notifications["TestXButtonPressed"], "AchievementManager",
|
||||
function()
|
||||
-- AchievementManager:SendAchievementEventAsync("Test_XPresses")
|
||||
end)
|
||||
|
||||
EventHub:addEventListener(EventHub.Notifications["AuthenticationSuccess"], "AchievementManager",
|
||||
function()
|
||||
spawn(function()
|
||||
local myUserId = UserData:GetRbxUserId()
|
||||
local function stillLoggedIn()
|
||||
local newUserId = UserData:GetRbxUserId()
|
||||
return newUserId ~= nil and myUserId == newUserId
|
||||
end
|
||||
|
||||
if myUserId ~= nil then
|
||||
local loggedInResult = Http.GetConsecutiveDaysLoggedInAsync()
|
||||
local daysLoggedIn = loggedInResult and loggedInResult['count']
|
||||
-- local Utility = require(Modules:FindFirstChild('Utility'))
|
||||
-- print("AchievementManager - Checking number of days Logged in:" , Utility.PrettyPrint(loggedInResult))
|
||||
if daysLoggedIn then
|
||||
if daysLoggedIn >= DAYS_FOR_3DAYROLL and stillLoggedIn() then
|
||||
AchievementManager:SendAchievementEventAsync("Award3DayRoll")
|
||||
end
|
||||
if daysLoggedIn >= DAYS_FOR_10DAYROLL and stillLoggedIn() then
|
||||
AchievementManager:SendAchievementEventAsync("Award10DayRoll")
|
||||
end
|
||||
if daysLoggedIn >= DAYS_FOR_20DAYROLL and stillLoggedIn() then
|
||||
AchievementManager:SendAchievementEventAsync("Award20DayRoll")
|
||||
end
|
||||
end
|
||||
OnPlayedGamesChanged()
|
||||
end
|
||||
end)
|
||||
end)
|
||||
|
||||
EventHub:addEventListener(EventHub.Notifications["DonnedDifferentPackage"], "AchievementManager",
|
||||
function(assetId)
|
||||
AchievementManager:SendAchievementEventAsync("AwardOneNameManyFaces")
|
||||
end)
|
||||
|
||||
EventHub:addEventListener(EventHub.Notifications["VotedOnPlace"], "AchievementManager",
|
||||
function()
|
||||
spawn(function()
|
||||
local voteCount = UserData:GetVoteCount()
|
||||
print("Vote Check: with vote count" , voteCount)
|
||||
if voteCount >= GAMES_RATED_FOR_POLLSTER then
|
||||
AchievementManager:SendAchievementEventAsync("AwardPollster")
|
||||
end
|
||||
end)
|
||||
end)
|
||||
|
||||
if PlatformService then
|
||||
PlatformService.ViewChanged:connect(function(newView)
|
||||
print("ViewChanged:" , newView)
|
||||
CurrentView = newView
|
||||
if newView == VIEW_GAMETYPE_ENUM['AppShell'] then
|
||||
print("New view is appshell")
|
||||
OnPlayedGamesChanged()
|
||||
elseif newView == VIEW_GAMETYPE_ENUM['Game'] then
|
||||
print("New view is game")
|
||||
OnJoinedGame()
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
|
||||
return AchievementManager
|
||||
|
||||
@@ -0,0 +1,352 @@
|
||||
-- Written by Kip Turner, Copyright ROBLOX 2015
|
||||
|
||||
local CoreGui = game:GetService("CoreGui")
|
||||
local ContextActionService = game:GetService("ContextActionService")
|
||||
local PlatformService = nil
|
||||
pcall(function() PlatformService = game:GetService('PlatformService') end)
|
||||
local UserInputService = game:GetService("UserInputService")
|
||||
local GuiService = game:GetService('GuiService')
|
||||
local TextService = game:GetService('TextService')
|
||||
|
||||
local GuiRoot = CoreGui:FindFirstChild("RobloxGui")
|
||||
local Modules = GuiRoot:FindFirstChild("Modules")
|
||||
|
||||
local Utility = require(Modules:FindFirstChild('Utility'))
|
||||
local GlobalSettings = require(Modules:FindFirstChild('GlobalSettings'))
|
||||
local AppTabDockModule = require(Modules:FindFirstChild('TabDock'))
|
||||
local AppTabDockItemModule = require(Modules:FindFirstChild('TabDockItem'))
|
||||
local HomePaneModule = require(Modules:FindFirstChild('HomePane'))
|
||||
local GamePaneModule = require(Modules:FindFirstChild('GamePane'))
|
||||
local AvatarPaneModule = require(Modules:FindFirstChild('AvatarPane'))
|
||||
local Errors = require(Modules:FindFirstChild('Errors'))
|
||||
local ErrorOverlay = require(Modules:FindFirstChild('ErrorOverlay'))
|
||||
local EventHub = require(Modules:FindFirstChild('EventHub'))
|
||||
local ScreenManager = require(Modules:FindFirstChild('ScreenManager'))
|
||||
local SocialPaneModule = require(Modules:FindFirstChild('SocialPane'))
|
||||
local StorePaneModule = require(Modules:FindFirstChild('StorePane'))
|
||||
local Strings = require(Modules:FindFirstChild('LocalizedStrings'))
|
||||
local AssetManager = require(Modules:FindFirstChild('AssetManager'))
|
||||
local SoundManager = require(Modules:FindFirstChild('SoundManager'))
|
||||
local SettingsScreen = require(Modules:FindFirstChild('SettingsScreen'))
|
||||
|
||||
local function CreateAppHub()
|
||||
local this = {}
|
||||
-- Game.CoreGui.SelectionImageObject = Instance.new('ImageLabel')
|
||||
|
||||
local AppTabDock = AppTabDockModule()
|
||||
local appHubCns = {}
|
||||
local selectionChangedConn = nil
|
||||
|
||||
local lastSelectedContentPane = nil
|
||||
local lastParent = nil
|
||||
|
||||
local HubContainer = Utility.Create'Frame'
|
||||
{
|
||||
Name = 'HubContainer';
|
||||
Size = UDim2.new(1, 0, 1, 0);
|
||||
BackgroundTransparency = 1;
|
||||
Visible = false;
|
||||
}
|
||||
|
||||
local PaneContainer = Utility.Create'Frame'
|
||||
{
|
||||
Name = 'PaneContainer';
|
||||
Size = UDim2.new(1, 0, 0.786, 0);
|
||||
Position = UDim2.new(0,0,0.214,0);
|
||||
BackgroundTransparency = 1;
|
||||
Parent = HubContainer;
|
||||
}
|
||||
|
||||
AppTabDock:SetParent(HubContainer)
|
||||
AppTabDock:SetPosition(UDim2.new(0,0,0.132,0))
|
||||
local HomeTab = AppTabDock:AddTab(AppTabDockItemModule(Strings:LocalizedString('HomeWord'):upper(), HomePaneModule(PaneContainer)))
|
||||
local AvatarTab = AppTabDock:AddTab(AppTabDockItemModule(Strings:LocalizedString('AvatarWord'):upper(), AvatarPaneModule(PaneContainer)))
|
||||
local GameTab = AppTabDock:AddTab(AppTabDockItemModule(Strings:LocalizedString('GameWord'):upper(), GamePaneModule(PaneContainer)))
|
||||
local SocialTab = AppTabDock:AddTab(AppTabDockItemModule(Strings:LocalizedString('FriendsWord'):upper(), SocialPaneModule(PaneContainer)))
|
||||
local StoreTab = AppTabDock:AddTab(AppTabDockItemModule(Strings:LocalizedString('CatalogWord'):upper(), StorePaneModule(PaneContainer)))
|
||||
|
||||
|
||||
local RobloxLogo = Utility.Create'ImageLabel'
|
||||
{
|
||||
Name = 'RobloxLogo';
|
||||
Size = UDim2.new(0, 232, 0, 56);
|
||||
Position = UDim2.new(0,0,0,0);
|
||||
BackgroundTransparency = 1;
|
||||
Image = 'rbxasset://textures/ui/Shell/Icons/ROBLOXLogoSmall@1080.png';
|
||||
Parent = HubContainer;
|
||||
}
|
||||
|
||||
-- Positin/Size set after text bounds set
|
||||
local BoundHintContainer = Utility.Create'Frame'
|
||||
{
|
||||
Name = "BoundHintContainer";
|
||||
BackgroundTransparency = 1;
|
||||
Visible = false;
|
||||
Parent = HubContainer;
|
||||
}
|
||||
local BoundHintText = Utility.Create'TextLabel'
|
||||
{
|
||||
Name = "BoundHintText";
|
||||
BackgroundTransparency = 1;
|
||||
Font = GlobalSettings.RegularFont;
|
||||
FontSize = GlobalSettings.TitleSize;
|
||||
TextColor3 = GlobalSettings.WhiteTextColor;
|
||||
TextXAlignment = GlobalSettings.Right;
|
||||
Text = "";
|
||||
Parent = BoundHintContainer;
|
||||
}
|
||||
local BoundHintImage = Utility.Create'ImageLabel'
|
||||
{
|
||||
Name = "BoundHintImage";
|
||||
Size = UDim2.new(0, 83, 0, 83);
|
||||
BackgroundTransparency = 1;
|
||||
Image = 'rbxasset://textures/ui/Shell/ButtonIcons/XButton.png';
|
||||
Parent = BoundHintContainer;
|
||||
}
|
||||
|
||||
local function setHintText(newText)
|
||||
local textSize = TextService:GetTextSize(newText, 42, BoundHintText.Font, Vector2.new(0, 0))
|
||||
BoundHintText.Size = UDim2.new(0, textSize.x, 0, 83)
|
||||
BoundHintText.Position = UDim2.new(1, -textSize.x, 0, 0)
|
||||
BoundHintText.Text = newText
|
||||
BoundHintContainer.Size = UDim2.new(0, textSize.x + BoundHintImage.Size.X.Offset, 0, 83)
|
||||
BoundHintContainer.Position = UDim2.new(1, -BoundHintContainer.Size.X.Offset, 1, -BoundHintContainer.Size.Y.Offset)
|
||||
BoundHintContainer.Visible = true
|
||||
end
|
||||
|
||||
local seenXButtonPressed = false
|
||||
local function onOpenSettings(actionName, inputState, inputObject)
|
||||
if inputState == Enum.UserInputState.Begin then
|
||||
seenXButtonPressed = true
|
||||
elseif inputState == Enum.UserInputState.End and seenXButtonPressed then
|
||||
local settingsScreen = SettingsScreen()
|
||||
EventHub:dispatchEvent(EventHub.Notifications["OpenSettingsScreen"], settingsScreen);
|
||||
end
|
||||
end
|
||||
|
||||
local function onOpenPartyUI(actionName, inputState, inputObject)
|
||||
if inputState == Enum.UserInputState.Begin then
|
||||
seenXButtonPressed = true
|
||||
elseif inputState == Enum.UserInputState.End and seenXButtonPressed then
|
||||
if UserSettings().GameSettings:InStudioMode() then
|
||||
ScreenManager:OpenScreen(ErrorOverlay(Errors.Test.FeatureNotAvailableInStudio), false)
|
||||
else
|
||||
local success, result = pcall(function()
|
||||
-- PlatformService may not exist in studio
|
||||
return PlatformService:PopupPartyUI(inputObject.UserInputType)
|
||||
end)
|
||||
if not success then
|
||||
ScreenManager:OpenScreen(ErrorOverlay(Errors.PlatformError.PopupPartyUI), false)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function setHintAction(selectedTab)
|
||||
ContextActionService:UnbindCoreAction("OpenHintAction")
|
||||
BoundHintContainer.Visible = false
|
||||
if selectedTab == HomeTab then
|
||||
setHintText(Strings:LocalizedString("SettingsWord"))
|
||||
ContextActionService:BindCoreAction("OpenHintAction", onOpenSettings, false, Enum.KeyCode.ButtonX)
|
||||
elseif selectedTab == SocialTab then
|
||||
setHintText(Strings:LocalizedString("StartPartyPhrase"))
|
||||
ContextActionService:BindCoreAction("OpenHintAction", onOpenPartyUI, false, Enum.KeyCode.ButtonX)
|
||||
end
|
||||
end
|
||||
|
||||
function this:GetName()
|
||||
-- print("Apphub get name called - lastSelectedContentPane:" , lastSelectedContentPane, "name:" , lastSelectedContentPane and lastSelectedContentPane:GetName())
|
||||
return lastSelectedContentPane and lastSelectedContentPane:GetName() or Strings:LocalizedString('HomeWord')
|
||||
end
|
||||
|
||||
|
||||
function this:Show()
|
||||
HubContainer.Visible = true
|
||||
HubContainer.Parent = lastParent
|
||||
|
||||
EventHub:removeEventListener(EventHub.Notifications["NavigateToRobuxScreen"], 'AppHubListenToRobuxScreenSwitch')
|
||||
EventHub:addEventListener(EventHub.Notifications["NavigateToRobuxScreen"], 'AppHubListenToRobuxScreenSwitch', function()
|
||||
if ScreenManager:ContainsScreen(this) then
|
||||
while ScreenManager:GetTopScreen() ~= this and ScreenManager:ContainsScreen(this) do
|
||||
ScreenManager:CloseCurrent()
|
||||
end
|
||||
if ScreenManager:GetTopScreen() == this then
|
||||
if AppTabDock:GetSelectedTab() ~= StoreTab then
|
||||
AppTabDock:SetSelectedTab(StoreTab)
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
local openEquippedDebounce = false
|
||||
EventHub:removeEventListener(EventHub.Notifications["NavigateToEquippedAvatar"], 'AppHubListenToAvatarScreenSwitch')
|
||||
EventHub:addEventListener(EventHub.Notifications["NavigateToEquippedAvatar"], 'AppHubListenToAvatarScreenSwitch', function()
|
||||
if openEquippedDebounce then return end
|
||||
openEquippedDebounce = true
|
||||
if ScreenManager:ContainsScreen(this) then
|
||||
while ScreenManager:GetTopScreen() ~= this and ScreenManager:ContainsScreen(this) do
|
||||
ScreenManager:CloseCurrent()
|
||||
end
|
||||
if ScreenManager:GetTopScreen() == this then
|
||||
if AppTabDock:GetSelectedTab() ~= AvatarTab then
|
||||
AppTabDock:SetSelectedTab(AvatarTab)
|
||||
-- local avatarPane = AvatarTab:GetContentItem()
|
||||
-- if avatarPane then
|
||||
-- avatarPane:OpenEquippedPackage()
|
||||
-- end
|
||||
end
|
||||
end
|
||||
end
|
||||
openEquippedDebounce = false
|
||||
end)
|
||||
|
||||
local currentlySelectedTab = AppTabDock:GetSelectedTab()
|
||||
AppTabDock:SetSelectedTab(currentlySelectedTab)
|
||||
if lastSelectedContentPane then
|
||||
lastSelectedContentPane:Show()
|
||||
end
|
||||
end
|
||||
|
||||
function this:Hide()
|
||||
if not ScreenManager:ContainsScreen(self) then
|
||||
EventHub:removeEventListener(EventHub.Notifications["NavigateToRobuxScreen"], 'AppHubListenToRobuxScreenSwitch')
|
||||
EventHub:removeEventListener(EventHub.Notifications["NavigateToEquippedAvatar"], 'AppHubListenToAvatarScreenSwitch')
|
||||
end
|
||||
HubContainer.Visible = false
|
||||
HubContainer.Parent = nil
|
||||
if lastSelectedContentPane then
|
||||
lastSelectedContentPane:Hide()
|
||||
end
|
||||
end
|
||||
|
||||
function this:Focus()
|
||||
AppTabDock:ConnectEvents()
|
||||
ContextActionService:BindCoreAction("CycleTabDock",
|
||||
function(actionName, inputState, inputObject)
|
||||
if inputState == Enum.UserInputState.End then
|
||||
if inputObject.KeyCode == Enum.KeyCode.ButtonL1 then
|
||||
local prevTab = AppTabDock:GetPreviousTab()
|
||||
if prevTab then
|
||||
AppTabDock:SetSelectedTab(prevTab)
|
||||
end
|
||||
elseif inputObject.KeyCode == Enum.KeyCode.ButtonR1 then
|
||||
local nextTab = AppTabDock:GetNextTab()
|
||||
if nextTab then
|
||||
AppTabDock:SetSelectedTab(nextTab)
|
||||
end
|
||||
end
|
||||
end
|
||||
end,
|
||||
false,
|
||||
Enum.KeyCode.ButtonL1, Enum.KeyCode.ButtonR1)
|
||||
|
||||
local seenBButtonBegin = false
|
||||
ContextActionService:BindCoreAction("CloseAppHub",
|
||||
function(actionName, inputState, inputObject)
|
||||
if inputState == Enum.UserInputState.Begin then
|
||||
seenBButtonBegin = true
|
||||
elseif inputState == Enum.UserInputState.End then
|
||||
if seenBButtonBegin then
|
||||
local currentlySelectedTab = AppTabDock:GetSelectedTab()
|
||||
if currentlySelectedTab ~= HomeTab then
|
||||
AppTabDock:SetSelectedTab(HomeTab)
|
||||
end
|
||||
end
|
||||
end
|
||||
end,
|
||||
false,
|
||||
Enum.KeyCode.ButtonB)
|
||||
|
||||
local function onSelectedTabChanged(selectedTab)
|
||||
if selectedTab then
|
||||
if lastSelectedContentPane then
|
||||
lastSelectedContentPane:Hide()
|
||||
lastSelectedContentPane:RemoveFocus()
|
||||
end
|
||||
local selectedContentPane = selectedTab:GetContentItem()
|
||||
if selectedContentPane then
|
||||
selectedContentPane:Show()
|
||||
if not AppTabDock:IsFocused() then
|
||||
AppTabDock:Focus()
|
||||
selectedContentPane:Focus()
|
||||
end
|
||||
end
|
||||
lastSelectedContentPane = selectedContentPane
|
||||
|
||||
-- set X action
|
||||
setHintAction(selectedTab)
|
||||
end
|
||||
end
|
||||
table.insert(appHubCns, AppTabDock.SelectedTabChanged:connect(onSelectedTabChanged))
|
||||
|
||||
local function onSelectedTabClicked(selectedTab)
|
||||
local selectedContentPane = selectedTab and selectedTab:GetContentItem()
|
||||
if selectedContentPane and selectedContentPane == lastSelectedContentPane then
|
||||
selectedContentPane:Focus()
|
||||
end
|
||||
end
|
||||
table.insert(appHubCns, AppTabDock.SelectedTabClicked:connect(onSelectedTabClicked))
|
||||
|
||||
selectionChangedConn = Utility.DisconnectEvent(selectionChangedConn)
|
||||
selectionChangedConn = GuiService.Changed:connect(function(prop)
|
||||
if prop == "SelectedCoreObject" then
|
||||
local currentSelection = GuiService.SelectedCoreObject
|
||||
if currentSelection and lastSelectedContentPane then
|
||||
-- first condition checks if function exist
|
||||
if lastSelectedContentPane.IsFocused and not lastSelectedContentPane:IsFocused() and lastSelectedContentPane.IsAncestorOf then
|
||||
if lastSelectedContentPane:IsAncestorOf(currentSelection) then
|
||||
-- print("Doing our focus")
|
||||
lastSelectedContentPane:Focus()
|
||||
else
|
||||
-- lastSelectedContentPane:RemoveFocus()
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
--set the Home tab to be the starting tab
|
||||
if AppTabDock:GetSelectedTab() == nil then
|
||||
AppTabDock:SetSelectedTab(HomeTab)
|
||||
end
|
||||
|
||||
if lastSelectedContentPane then
|
||||
lastSelectedContentPane:Focus()
|
||||
end
|
||||
|
||||
setHintAction(AppTabDock:GetSelectedTab())
|
||||
end
|
||||
|
||||
function this:RemoveFocus()
|
||||
AppTabDock:DisconnectEvents()
|
||||
ContextActionService:UnbindCoreAction("CycleTabDock")
|
||||
ContextActionService:UnbindCoreAction("CloseAppHub")
|
||||
|
||||
if lastSelectedContentPane then
|
||||
lastSelectedContentPane:RemoveFocus()
|
||||
end
|
||||
for k,v in pairs(appHubCns) do
|
||||
v:disconnect()
|
||||
v = nil
|
||||
appHubCns[k] = nil
|
||||
end
|
||||
|
||||
selectionChangedConn = Utility.DisconnectEvent(selectionChangedConn)
|
||||
|
||||
ContextActionService:UnbindCoreAction("OpenHintAction")
|
||||
end
|
||||
|
||||
function this:SetParent(newParent)
|
||||
HubContainer.Parent = newParent
|
||||
lastParent = newParent
|
||||
end
|
||||
|
||||
local hubID = "AppHub"
|
||||
--EventHub:addEventListener(EventHub.Notifications["OpenGameDetail"], hubID, function(data) AppTabDock:SetSelectedTab(GameTab); end);
|
||||
--EventHub:addEventListener(EventHub.Notifications["OpenGameGenre"], hubID, function(data) AppTabDock:SetSelectedTab(GameTab); end);
|
||||
|
||||
|
||||
return this
|
||||
end
|
||||
|
||||
return CreateAppHub
|
||||
@@ -0,0 +1,134 @@
|
||||
-- Written by Kip Turner, Copyright ROBLOX 2015
|
||||
|
||||
local GuiService = game:GetService('GuiService')
|
||||
local CoreGui = Game:GetService("CoreGui")
|
||||
|
||||
local GuiRoot = CoreGui:FindFirstChild("RobloxGui")
|
||||
local Modules = GuiRoot:FindFirstChild("Modules")
|
||||
|
||||
local Utility = require(Modules:FindFirstChild('Utility'))
|
||||
|
||||
local AssetManager = {}
|
||||
|
||||
|
||||
local TrackedAssets = {}
|
||||
local WeakTrackedAssets = {}
|
||||
|
||||
-- Set weak-keys table
|
||||
setmetatable(WeakTrackedAssets, {__mode = 'k' })
|
||||
|
||||
local function GetScreenSize()
|
||||
-- return GuiService:GetScreenResolution()
|
||||
return GuiRoot.AbsoluteSize
|
||||
end
|
||||
|
||||
local function GetImageSuffixByScreenSize(screenSize)
|
||||
if screenSize.Y <= 720 then
|
||||
return '@720'
|
||||
else
|
||||
return '@1080'
|
||||
end
|
||||
end
|
||||
|
||||
local function UpdateAsset(asset, metadata)
|
||||
if asset then
|
||||
local newSize = GetScreenSize()
|
||||
local newScreenSuffix = GetImageSuffixByScreenSize(newSize)
|
||||
local imagePath = metadata['path'] .. newScreenSuffix .. metadata['extension']
|
||||
asset.Image = imagePath
|
||||
if metadata['sizes'] then
|
||||
if newScreenSuffix == '@720' and metadata['sizes']['720'] then
|
||||
asset.Size = metadata['sizes']['720']
|
||||
elseif newScreenSuffix == '@1080' and metadata['sizes']['1080'] then
|
||||
asset.Size = metadata['sizes']['1080']
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function OnAncestryChanged(descendant, descendantMetatable)
|
||||
descendantMetatable = descendantMetatable or WeakTrackedAssets[descendant] or TrackedAssets[descendant]
|
||||
if descendantMetatable then
|
||||
if descendant.Parent == nil then
|
||||
WeakTrackedAssets[descendant] = descendantMetatable
|
||||
TrackedAssets[descendant] = nil
|
||||
else
|
||||
TrackedAssets[descendant] = descendantMetatable
|
||||
WeakTrackedAssets[descendant] = nil
|
||||
UpdateAsset(descendant, descendantMetatable)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- rbxImageInstance is a gui object such as ImageButton or ImageLabel
|
||||
-- Imagepath is the local path to the image
|
||||
-- fileFormat is the file extension i.e. .png
|
||||
function AssetManager.LocalImage(rbxImageInstance, imagepath, sizes, fileFormat)
|
||||
fileFormat = fileFormat or '.png'
|
||||
|
||||
if imagepath then
|
||||
local metadataTable = {
|
||||
['path'] = imagepath;
|
||||
['extension'] = fileFormat;
|
||||
['sizes'] = sizes;
|
||||
}
|
||||
UpdateAsset(rbxImageInstance, metadataTable)
|
||||
OnAncestryChanged(rbxImageInstance, metadataTable)
|
||||
end
|
||||
|
||||
return rbxImageInstance
|
||||
end
|
||||
|
||||
function AssetManager.CreateShadow(zIndex)
|
||||
return Utility.Create'ImageLabel'
|
||||
{
|
||||
Name = 'Shadow';
|
||||
Image = 'rbxasset://textures/ui/Shell/Buttons/Generic9ScaleShadow.png';
|
||||
Size = UDim2.new(1,3,1,3);
|
||||
Position = UDim2.new(0,0,0,0);
|
||||
ScaleType = Enum.ScaleType.Slice;
|
||||
SliceCenter = Rect.new(10,10,28,28);
|
||||
BackgroundTransparency = 1;
|
||||
ZIndex = zIndex or 1;
|
||||
}
|
||||
end
|
||||
|
||||
local LastScreenSuffix = GetImageSuffixByScreenSize(GetScreenSize())
|
||||
GuiRoot.Changed:connect(function(prop)
|
||||
if prop == 'AbsoluteSize' then
|
||||
local newSize = GetScreenSize()
|
||||
local newScreenSuffix = GetImageSuffixByScreenSize(newSize)
|
||||
if newScreenSuffix ~= LastScreenSuffix then
|
||||
for asset, metadata in pairs(TrackedAssets) do
|
||||
UpdateAsset(asset, metadata)
|
||||
end
|
||||
LastScreenSuffix = newScreenSuffix
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
|
||||
GuiRoot.DescendantAdded:connect(function(descendant)
|
||||
if WeakTrackedAssets[descendant] or TrackedAssets[descendant] then
|
||||
-- need to spawn because we haven't got the new parent yet
|
||||
spawn(function()
|
||||
if descendant then
|
||||
OnAncestryChanged(descendant)
|
||||
end
|
||||
end)
|
||||
end
|
||||
end)
|
||||
|
||||
GuiRoot.DescendantRemoving:connect(function(descendant)
|
||||
if WeakTrackedAssets[descendant] or TrackedAssets[descendant] then
|
||||
-- need to spawn because we haven't got the new parent yet
|
||||
spawn(function()
|
||||
if descendant then
|
||||
OnAncestryChanged(descendant)
|
||||
end
|
||||
end)
|
||||
end
|
||||
end)
|
||||
|
||||
|
||||
return AssetManager
|
||||
@@ -0,0 +1,691 @@
|
||||
-- Written by Kip Turner, Copyright ROBLOX 2015
|
||||
|
||||
local TextService = game:GetService('TextService')
|
||||
|
||||
local CoreGui = game:GetService("CoreGui")
|
||||
local GuiService = game:GetService('GuiService')
|
||||
local RunService = game:GetService('RunService')
|
||||
local ContextActionService = game:GetService("ContextActionService")
|
||||
|
||||
local GuiRoot = CoreGui:FindFirstChild("RobloxGui")
|
||||
local Modules = GuiRoot:FindFirstChild("Modules")
|
||||
|
||||
local PackageData = require(Modules:FindFirstChild('PackageData'))
|
||||
local OutfitData = require(Modules:FindFirstChild('OutfitData'))
|
||||
local ScrollingGridModule = require(Modules:FindFirstChild('ScrollingGrid'))
|
||||
local AvatarTile = require(Modules:FindFirstChild('AvatarTile'))
|
||||
local OutfitTile = require(Modules:FindFirstChild('OutfitTile'))
|
||||
local Utility = require(Modules:FindFirstChild('Utility'))
|
||||
local Http = require(Modules:FindFirstChild('Http'))
|
||||
local ScreenManager = require(Modules:FindFirstChild('ScreenManager'))
|
||||
local GlobalSettings = require(Modules:FindFirstChild('GlobalSettings'))
|
||||
local LoadingWidget = require(Modules:FindFirstChild('LoadingWidget'))
|
||||
local ThumbnailLoader = require(Modules:FindFirstChild('ThumbnailLoader'))
|
||||
local UserData = require(Modules:FindFirstChild('UserData'))
|
||||
local EventHub = require(Modules:FindFirstChild('EventHub'))
|
||||
|
||||
local Strings = require(Modules:FindFirstChild('LocalizedStrings'))
|
||||
local AssetManager = require(Modules:FindFirstChild('AssetManager'))
|
||||
local SoundManager = require(Modules:FindFirstChild('SoundManager'))
|
||||
|
||||
|
||||
local GLOW_BASE_RPM = 2
|
||||
local GLOW_TOP_RPM = -0.5
|
||||
local GLOW_TRANSPARENCY = 0.2
|
||||
local CATALOG_BELOW_POSITION = 320
|
||||
|
||||
|
||||
local function CreateAvatarPane(parent)
|
||||
local this = {}
|
||||
|
||||
local inFocus = false
|
||||
local isShown = false
|
||||
|
||||
local AvatarObjects = {}
|
||||
|
||||
local OnGuiServiceChangedConn = nil
|
||||
|
||||
local lastParent = parent
|
||||
|
||||
local LastWearingPackageAssetId = nil
|
||||
local LastWearingOutfitId = nil
|
||||
|
||||
|
||||
local MainContainer = Utility.Create'Frame'
|
||||
{
|
||||
Name = 'AvatarPane';
|
||||
Size = UDim2.new(1, 0, 1, 0);
|
||||
BackgroundTransparency = 1;
|
||||
Visible = false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
local MyAvatarContainer = Utility.Create'Frame'
|
||||
{
|
||||
Name = 'MyAvatarContainer';
|
||||
Size = UDim2.new(0.38,0,1,0);
|
||||
Position = UDim2.new(0,0,0,0);
|
||||
BackgroundTransparency = 1;
|
||||
ClipsDescendants = true;
|
||||
Parent = MainContainer;
|
||||
}
|
||||
local MyNameLabel = Utility.Create'TextLabel'
|
||||
{
|
||||
Name = 'MyNameLabel';
|
||||
Text = '';
|
||||
Size = UDim2.new(1,0,0,25);
|
||||
Position = UDim2.new(0,12,0,0);
|
||||
TextXAlignment = 'Left';
|
||||
TextColor3 = GlobalSettings.WhiteTextColor;
|
||||
Font = GlobalSettings.RegularFont;
|
||||
FontSize = GlobalSettings.SubHeaderSize;
|
||||
BackgroundTransparency = 1;
|
||||
Parent = MyAvatarContainer;
|
||||
};
|
||||
local ProfileImageContainer = Utility.Create'Frame'
|
||||
{
|
||||
Name = 'ProfileImageContainer';
|
||||
Size = UDim2.new(0.68,0,0.9,-MyNameLabel.Size.Y.Offset);
|
||||
Position = UDim2.new(0.16, 0, 0.05, MyNameLabel.Size.Y.Offset);
|
||||
BackgroundTransparency = 1;
|
||||
Parent = MyAvatarContainer;
|
||||
}
|
||||
local ProfileImage = Utility.Create'ImageLabel'
|
||||
{
|
||||
Name = 'ProfileImage';
|
||||
Size = UDim2.new(0,780,0,780);
|
||||
Position = UDim2.new(0.5, 0, 0.5, 0);
|
||||
BackgroundTransparency = 1;
|
||||
ZIndex = 3;
|
||||
Parent = ProfileImageContainer;
|
||||
};
|
||||
local CrossfadeProfileImage = Utility.Create'ImageLabel'
|
||||
{
|
||||
Name = 'CrossfadeProfileImage';
|
||||
Size = UDim2.new(1,0,1,0);
|
||||
Position = UDim2.new(0, 0, 0, 0);
|
||||
BackgroundTransparency = 1;
|
||||
ImageTransparency = 1;
|
||||
ZIndex = 3;
|
||||
Parent = ProfileImage;
|
||||
};
|
||||
local CharacterGlowBase = Utility.Create'ImageLabel'
|
||||
{
|
||||
Name = 'CharacterGlowBase';
|
||||
Size = UDim2.new(0,1015,0,1002);
|
||||
Position = UDim2.new(0, 0, 0, 0);
|
||||
BackgroundTransparency = 1;
|
||||
Image = 'rbxasset://textures/ui/Shell/Images/CharacterGlow/CharacterGlowBase.png';
|
||||
ImageTransparency = GLOW_TRANSPARENCY;
|
||||
Parent = CrossfadeProfileImage;
|
||||
};
|
||||
Utility.CalculateAnchor(CharacterGlowBase, UDim2.new(0.5, 0, 0.5, 0), Utility.Enum.Anchor.Center)
|
||||
local CharacterGlowTop = Utility.Create'ImageLabel'
|
||||
{
|
||||
Name = 'CharacterGlowTop';
|
||||
Size = UDim2.new(0,1026,0,1009);
|
||||
Position = UDim2.new(0, 0, 0, 0);
|
||||
BackgroundTransparency = 1;
|
||||
Image = 'rbxasset://textures/ui/Shell/Images/CharacterGlow/CharacterGlowTop.png';
|
||||
ImageTransparency = GLOW_TRANSPARENCY;
|
||||
ZIndex = 2;
|
||||
Parent = CrossfadeProfileImage;
|
||||
};
|
||||
Utility.CalculateAnchor(CharacterGlowTop, UDim2.new(0.5, 0, 0.5, 0), Utility.Enum.Anchor.Center)
|
||||
local function onSizeChanged()
|
||||
ProfileImage.Size = Utility.CalculateFill(ProfileImage, Vector2.new(576, 324))
|
||||
Utility.CalculateAnchor(ProfileImage, UDim2.new(0.5, 0, 0.5, 0), Utility.Enum.Anchor.Center)
|
||||
end
|
||||
|
||||
local EquipButtonImage = Utility.Create'ImageLabel'
|
||||
{
|
||||
Name = 'EquipButtonImage';
|
||||
Size = UDim2.new(0,70,0,70);
|
||||
Position = UDim2.new(0.5, 25, 0.75, 0);
|
||||
Image = 'rbxasset://textures/ui/Shell/ButtonIcons/XButton.png';
|
||||
ImageTransparency = 1;
|
||||
BackgroundTransparency = 1;
|
||||
Parent = MainContainer;
|
||||
};
|
||||
local EquipHint = Utility.Create'TextLabel'
|
||||
{
|
||||
Name = 'EquipHint';
|
||||
Text = Strings:LocalizedString('EquipWord'):upper();
|
||||
Size = UDim2.new(0,0,1,0);
|
||||
Position = UDim2.new(1, 5, 0, -3);
|
||||
TextXAlignment = 'Left';
|
||||
TextColor3 = GlobalSettings.WhiteTextColor;
|
||||
TextTransparency = 1;
|
||||
Font = GlobalSettings.RegularFont;
|
||||
FontSize = GlobalSettings.TitleSize;
|
||||
BackgroundTransparency = 1;
|
||||
Parent = EquipButtonImage;
|
||||
};
|
||||
do
|
||||
local equipHintTextSize = TextService:GetTextSize(EquipHint.Text, Utility.ConvertFontSizeEnumToInt(EquipHint.FontSize), EquipHint.Font, Vector2.new())
|
||||
Utility.CalculateAnchor(EquipButtonImage, UDim2.new(0.95, -equipHintTextSize.X,1,0), Utility.Enum.Anchor.BottomRight)
|
||||
end
|
||||
|
||||
local function TweenEquipButton(newValue, duration)
|
||||
duration = duration or 0.25
|
||||
Utility.PropertyTweener(EquipButtonImage, 'ImageTransparency', EquipButtonImage.ImageTransparency, newValue, duration, Utility.EaseOutQuad, true)
|
||||
Utility.PropertyTweener(EquipHint, 'TextTransparency', EquipHint.TextTransparency, newValue, duration, Utility.EaseOutQuad, true)
|
||||
end
|
||||
|
||||
local function UpdateEquipButton(IsOwned, IsWearing)
|
||||
TweenEquipButton((IsOwned and not IsWearing) and 0 or 1)
|
||||
end
|
||||
|
||||
local SelectableAvatarsContainer = Utility.Create'Frame'
|
||||
{
|
||||
Name = 'SelectableAvatarsContainer';
|
||||
Size = UDim2.new(0.6,0,1,0);
|
||||
Position = UDim2.new(0.4,0,0,0);
|
||||
BackgroundTransparency = 1;
|
||||
Parent = MainContainer;
|
||||
}
|
||||
|
||||
local NoCatalogStatusMessage = Utility.Create'TextLabel'
|
||||
{
|
||||
Name = 'NoCatalogStatusMessage';
|
||||
Text = Strings:LocalizedString('DefaultErrorPhrase');
|
||||
Size = UDim2.new(0.9,0,1,-125);
|
||||
Position = UDim2.new(0.05, 0, 0, 0);
|
||||
TextColor3 = GlobalSettings.GreyTextColor;
|
||||
TextWrapped = true;
|
||||
TextTransparency = GlobalSettings.FriendStatusTextTransparency;
|
||||
Font = GlobalSettings.BoldFont;
|
||||
FontSize = GlobalSettings.DescriptionSize;
|
||||
BackgroundTransparency = 1;
|
||||
Visible = false;
|
||||
Parent = SelectableAvatarsContainer;
|
||||
};
|
||||
|
||||
local OutfitsTitle = Utility.Create'TextLabel'
|
||||
{
|
||||
Name = 'OutfitsTitle';
|
||||
Text = Strings:LocalizedString('AvatarOutfitsTitle'):upper();
|
||||
Size = UDim2.new(1,0,0,40);
|
||||
TextXAlignment = 'Left';
|
||||
TextColor3 = GlobalSettings.WhiteTextColor;
|
||||
Font = GlobalSettings.RegularFont;
|
||||
FontSize = GlobalSettings.SubHeaderSize;
|
||||
BackgroundTransparency = 1;
|
||||
Visible = false;
|
||||
Parent = SelectableAvatarsContainer;
|
||||
};
|
||||
|
||||
local OutfitsScroller = ScrollingGridModule()
|
||||
OutfitsScroller:SetSize(UDim2.new(1,0,1,-OutfitsTitle.Size.Y.Offset - 40))
|
||||
OutfitsScroller:SetScrollDirection(OutfitsScroller.Enum.ScrollDirection.Horizontal)
|
||||
OutfitsScroller:SetCellSize(Vector2.new(220, 220))
|
||||
OutfitsScroller:SetSpacing(Vector2.new(25,25))
|
||||
OutfitsScroller:SetPosition(UDim2.new(0,0,0,OutfitsTitle.Size.Y.Offset))
|
||||
OutfitsScroller:SetRowColumnConstraint(1)
|
||||
-- OutfitsScroller:SetParent(SelectableAvatarsContainer)
|
||||
|
||||
local CatalogTitle = Utility.Create'TextLabel'
|
||||
{
|
||||
Name = 'CatalogTitle';
|
||||
Text = Strings:LocalizedString('AvatarCatalogTitle'):upper();
|
||||
Size = UDim2.new(1,0,0,40);
|
||||
Position = UDim2.new(0,0,0,0);
|
||||
TextXAlignment = 'Left';
|
||||
TextColor3 = GlobalSettings.WhiteTextColor;
|
||||
Font = GlobalSettings.RegularFont;
|
||||
FontSize = GlobalSettings.SubHeaderSize;
|
||||
BackgroundTransparency = 1;
|
||||
Parent = SelectableAvatarsContainer;
|
||||
};
|
||||
|
||||
local AvatarScroller = ScrollingGridModule()
|
||||
AvatarScroller:SetSize(UDim2.new(1,0,1,-CatalogTitle.Size.Y.Offset - 40))
|
||||
AvatarScroller:SetScrollDirection(AvatarScroller.Enum.ScrollDirection.Horizontal)
|
||||
AvatarScroller:SetCellSize(Vector2.new(220, 220))
|
||||
AvatarScroller:SetPosition(UDim2.new(0,0,0,40))
|
||||
AvatarScroller:SetSpacing(Vector2.new(25,25))
|
||||
AvatarScroller:SetRowColumnConstraint(2)
|
||||
AvatarScroller:SetParent(SelectableAvatarsContainer)
|
||||
|
||||
local function SortOutfitsScroller()
|
||||
OutfitsScroller:SortItems(
|
||||
function(a, b)
|
||||
local aObject = a and AvatarObjects[a] and AvatarObjects[a]:GetPackageInfo()
|
||||
local bObject = b and AvatarObjects[b] and AvatarObjects[b]:GetPackageInfo()
|
||||
local aIsEquipped = aObject and aObject:IsWearing()
|
||||
local bIsEquipped = bObject and bObject:IsWearing()
|
||||
local aName = aObject and aObject:GetName()
|
||||
local bName = bObject and bObject:GetName()
|
||||
|
||||
if aIsEquipped then return true elseif bIsEquipped then return false end
|
||||
if aName and bName then
|
||||
return aName < bName
|
||||
end
|
||||
return aObject ~= nil
|
||||
end)
|
||||
end
|
||||
|
||||
local function OnAddToOutfitsScroller()
|
||||
CatalogTitle.Position = UDim2.new(0,0,0, CATALOG_BELOW_POSITION)
|
||||
AvatarScroller:SetPosition(UDim2.new(0,0,0,CATALOG_BELOW_POSITION + 40))
|
||||
AvatarScroller:SetRowColumnConstraint(1)
|
||||
OutfitsTitle.Visible = true
|
||||
OutfitsScroller:SetParent(SelectableAvatarsContainer)
|
||||
SortOutfitsScroller()
|
||||
end
|
||||
|
||||
|
||||
|
||||
local LoaderSpinner = nil
|
||||
local ProfileImageThumbnailLoader = nil
|
||||
local GlobalFadeCount = 1
|
||||
local function CrossfadeAvatarImage(frontImage, fadeImage, newImageUrl, duration)
|
||||
duration = duration or 0.75
|
||||
|
||||
GlobalFadeCount = GlobalFadeCount + 1
|
||||
local thisFadeCount = GlobalFadeCount
|
||||
|
||||
local fadeoutDuration = duration
|
||||
|
||||
spawn(function()
|
||||
local dummyImage = {}
|
||||
local function waitForWearReady()
|
||||
PackageData:AwaitWearAssetRequest()
|
||||
if thisFadeCount == GlobalFadeCount then
|
||||
if ProfileImageThumbnailLoader then ProfileImageThumbnailLoader:Cancel() end
|
||||
ProfileImageThumbnailLoader = ThumbnailLoader:Create(dummyImage, newImageUrl, ThumbnailLoader.Sizes.Large, ThumbnailLoader.AssetType.Avatar, true)
|
||||
local loadResult = ProfileImageThumbnailLoader:LoadAsync(false, false)
|
||||
ProfileImageThumbnailLoader = nil
|
||||
end
|
||||
end
|
||||
|
||||
if LoaderSpinner then LoaderSpinner:Cleanup() end
|
||||
local newLoaderSpinner = LoadingWidget({Parent = frontImage, ZIndex = 3}, {waitForWearReady})
|
||||
LoaderSpinner = newLoaderSpinner
|
||||
newLoaderSpinner:AwaitFinished()
|
||||
newLoaderSpinner:Cleanup()
|
||||
|
||||
if thisFadeCount == GlobalFadeCount then
|
||||
local noPreviousImage = (frontImage.Image == "" and fadeImage.Image == "")
|
||||
if noPreviousImage then
|
||||
fadeoutDuration = 0
|
||||
else
|
||||
fadeImage.Image = frontImage.Image
|
||||
end
|
||||
|
||||
|
||||
Utility.PropertyTweener(fadeImage, 'ImageTransparency', noPreviousImage and 0 or frontImage.ImageTransparency, 1, fadeoutDuration, Utility.EaseInOutQuad, true)
|
||||
Utility.PropertyTweener(CharacterGlowBase, 'ImageTransparency', CharacterGlowBase.ImageTransparency, 1, fadeoutDuration, Utility.EaseInOutQuad, true)
|
||||
Utility.PropertyTweener(CharacterGlowTop, 'ImageTransparency', CharacterGlowTop.ImageTransparency, 1, fadeoutDuration, Utility.EaseInOutQuad, true)
|
||||
frontImage.ImageTransparency = 1
|
||||
|
||||
frontImage.Image = dummyImage.Image
|
||||
|
||||
Utility.PropertyTweener(frontImage, 'ImageTransparency', frontImage.ImageTransparency, 0, duration, Utility.EaseInOutQuad, true)
|
||||
Utility.PropertyTweener(CharacterGlowBase, 'ImageTransparency', CharacterGlowBase.ImageTransparency, GLOW_TRANSPARENCY, duration, Utility.EaseInOutQuad, true)
|
||||
Utility.PropertyTweener(CharacterGlowTop, 'ImageTransparency', CharacterGlowTop.ImageTransparency, GLOW_TRANSPARENCY, duration, Utility.EaseInOutQuad, true)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
local function UpdateProfileImage(forceRefresh)
|
||||
MyNameLabel.Text = UserData:GetDisplayName()
|
||||
if forceRefresh or
|
||||
LastWearingPackageAssetId ~= PackageData:GetCachedWearingPackage() or
|
||||
LastWearingOutfitId ~= OutfitData:GetCachedWearingOutfitId() then
|
||||
CrossfadeAvatarImage(ProfileImage, CrossfadeProfileImage, UserData.GetLocalUserIdAsync())
|
||||
|
||||
LastWearingPackageAssetId = PackageData:GetCachedWearingPackage()
|
||||
LastWearingOutfitId = OutfitData:GetCachedWearingOutfitId()
|
||||
end
|
||||
end
|
||||
|
||||
UpdateProfileImage(true)
|
||||
|
||||
local function onOwnershipChanged(tile, nowOwns)
|
||||
-- Move purchased packages into my outfits
|
||||
if nowOwns then
|
||||
local guiObject = tile and tile:GetGuiObject()
|
||||
if guiObject and AvatarScroller:ContainsItem(guiObject) then
|
||||
AvatarScroller:RemoveItem(guiObject)
|
||||
OutfitsScroller:AddItem(guiObject)
|
||||
OnAddToOutfitsScroller()
|
||||
if inFocus then
|
||||
GuiService.SelectedCoreObject = guiObject
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local ownershipChangedCns = {}
|
||||
|
||||
local function listenToOwnershipChanged(tile)
|
||||
local packageInfo = tile and tile:GetPackageInfo()
|
||||
if packageInfo and not packageInfo:IsOwned() and (packageInfo.OwnershipChanged ~= nil) then
|
||||
if not ownershipChangedCns[tile] then
|
||||
ownershipChangedCns[tile] = packageInfo.OwnershipChanged:connect(function(nowOwns)
|
||||
onOwnershipChanged(tile, nowOwns)
|
||||
end)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function removeListenToOwnershipChanged(tile)
|
||||
if ownershipChangedCns[tile] then
|
||||
ownershipChangedCns[tile]:disconnect()
|
||||
ownershipChangedCns[tile] = nil
|
||||
end
|
||||
end
|
||||
|
||||
local packagesLoaded = false
|
||||
local outfitsLoaded = true -- NOTE: we don't load outfits atm
|
||||
local LoadAvatarWebDataLoader = nil
|
||||
local function LoadAvatarWebData()
|
||||
local function loadCatalogPackages()
|
||||
local packages = PackageData:GetXboxCatalogPackagesAsync()
|
||||
|
||||
if packages and not packagesLoaded then
|
||||
packagesLoaded = true
|
||||
for _, packageInfo in pairs(packages) do
|
||||
local avatarItemContainer = AvatarTile(packageInfo)
|
||||
|
||||
AvatarObjects[avatarItemContainer:GetGuiObject()] = avatarItemContainer
|
||||
|
||||
if packageInfo:IsOwned() then
|
||||
OutfitsScroller:AddItem(avatarItemContainer:GetGuiObject())
|
||||
OnAddToOutfitsScroller()
|
||||
else
|
||||
AvatarScroller:AddItem(avatarItemContainer:GetGuiObject())
|
||||
listenToOwnershipChanged(avatarItemContainer)
|
||||
end
|
||||
|
||||
if isShown then
|
||||
avatarItemContainer:Show()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
local function loadOutfits()
|
||||
local outfits = OutfitData:GetMyOutfitsAsync()
|
||||
|
||||
if outfits and not outfitsLoaded then
|
||||
outfitsLoaded = true
|
||||
for _, outfitInfo in pairs(outfits) do
|
||||
local outfitItemContainer = OutfitTile(outfitInfo)
|
||||
|
||||
AvatarObjects[outfitItemContainer:GetGuiObject()] = outfitItemContainer
|
||||
OutfitsScroller:AddItem(outfitItemContainer:GetGuiObject())
|
||||
OnAddToOutfitsScroller()
|
||||
if isShown then
|
||||
outfitItemContainer:Show()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
if LoadAvatarWebDataLoader then return end
|
||||
|
||||
SelectableAvatarsContainer.Visible = false
|
||||
local containerSize = SelectableAvatarsContainer.Size
|
||||
LoadAvatarWebDataLoader = LoadingWidget(
|
||||
{Parent = MainContainer, Position = SelectableAvatarsContainer.Position + UDim2.new(containerSize.X.Scale / 2, containerSize.X.Offset / 2, containerSize.Y.Scale / 2, containerSize.Y.Offset / 2)},
|
||||
-- {loadCatalogPackages, loadOutfits})
|
||||
{loadCatalogPackages})
|
||||
spawn(function()
|
||||
NoCatalogStatusMessage.Visible = false
|
||||
|
||||
LoadAvatarWebDataLoader:AwaitFinished()
|
||||
LoadAvatarWebDataLoader:Cleanup()
|
||||
LoadAvatarWebDataLoader = nil
|
||||
SelectableAvatarsContainer.Visible = true
|
||||
-- SortOutfitsScroller()
|
||||
|
||||
if not (packagesLoaded and outfitsLoaded) then
|
||||
NoCatalogStatusMessage.Visible = true
|
||||
end
|
||||
|
||||
if inFocus and isShown and GuiService.SelectedCoreObject == nil then
|
||||
GuiService.SelectedCoreObject = this:GetDefaultSelectableObject()
|
||||
end
|
||||
|
||||
if this.TransitionTweens == nil or #this.TransitionTweens == 0 then
|
||||
this.TransitionTweens = ScreenManager:FadeInSitu(SelectableAvatarsContainer)
|
||||
end
|
||||
end)
|
||||
end
|
||||
LoadAvatarWebData()
|
||||
|
||||
local function onEquipChanged()
|
||||
local selectedObject = GuiService.SelectedCoreObject
|
||||
|
||||
if AvatarObjects[selectedObject] then
|
||||
local packageInfo = AvatarObjects[selectedObject]:GetPackageInfo()
|
||||
if packageInfo then
|
||||
UpdateEquipButton(packageInfo:IsOwned(), packageInfo:IsWearing())
|
||||
end
|
||||
end
|
||||
SortOutfitsScroller()
|
||||
end
|
||||
|
||||
local lastSelectedObject = GuiService.SelectedCoreObject
|
||||
local function OnSelectedCoreObjectChanged()
|
||||
local selectedObject = GuiService.SelectedCoreObject
|
||||
|
||||
if AvatarObjects[lastSelectedObject] then
|
||||
AvatarObjects[lastSelectedObject]:RemoveFocus()
|
||||
end
|
||||
if AvatarObjects[selectedObject] then
|
||||
AvatarObjects[selectedObject]:Focus()
|
||||
|
||||
onEquipChanged()
|
||||
end
|
||||
|
||||
lastSelectedObject = selectedObject
|
||||
end
|
||||
|
||||
|
||||
function this:GetDefaultSelectableObject()
|
||||
if OutfitsScroller.GridItems[1] then
|
||||
return OutfitsScroller.GridItems[1]
|
||||
end
|
||||
if AvatarScroller.GridItems[1] then
|
||||
return AvatarScroller.GridItems[1]
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
--[[ Public API ]]--
|
||||
function this:GetName()
|
||||
return Strings:LocalizedString('AvatarWord')
|
||||
end
|
||||
|
||||
function this:IsFocused()
|
||||
return inFocus
|
||||
end
|
||||
|
||||
local debounceSelect = false
|
||||
function this:OnSelectAction()
|
||||
if debounceSelect then return end
|
||||
debounceSelect = true
|
||||
|
||||
local selectedObject = GuiService.SelectedCoreObject
|
||||
if selectedObject and AvatarObjects[selectedObject] then
|
||||
if AvatarObjects[selectedObject]:Select() then
|
||||
SoundManager:Play('ButtonPress')
|
||||
end
|
||||
end
|
||||
|
||||
debounceSelect = false
|
||||
end
|
||||
|
||||
function this:OpenEquippedPackage()
|
||||
if isShown and inFocus then
|
||||
for _, avatarItemContainer in pairs(AvatarObjects) do
|
||||
local packageInfo = avatarItemContainer:GetPackageInfo()
|
||||
if packageInfo then
|
||||
if packageInfo:IsWearing() then
|
||||
avatarItemContainer:OnClick()
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
--[[ End Public API ]]--
|
||||
|
||||
local profileImageChangeCn = nil
|
||||
function this:Show()
|
||||
isShown = true
|
||||
|
||||
Utility.DisconnectEvent(profileImageChangeCn)
|
||||
profileImageChangeCn = ProfileImageContainer.Changed:connect(function(prop)
|
||||
if prop == 'AbsoluteSize' then
|
||||
onSizeChanged()
|
||||
end
|
||||
end)
|
||||
onSizeChanged()
|
||||
|
||||
local lastUpdate = tick()
|
||||
RunService:BindToRenderStep("UpdateAvatarGlow", Enum.RenderPriority.Camera.Value,
|
||||
function()
|
||||
local now = tick()
|
||||
local delta = now - lastUpdate
|
||||
|
||||
CharacterGlowBase.Rotation = CharacterGlowBase.Rotation + delta * GLOW_BASE_RPM * 6 -- 6 = 360 / 60
|
||||
CharacterGlowTop.Rotation = CharacterGlowTop.Rotation + delta * GLOW_TOP_RPM * 6
|
||||
|
||||
lastUpdate = now
|
||||
end)
|
||||
|
||||
if not (packagesLoaded and outfitsLoaded) then
|
||||
LoadAvatarWebData()
|
||||
end
|
||||
|
||||
for _, avatarItemContainer in pairs(AvatarObjects) do
|
||||
avatarItemContainer:Show()
|
||||
listenToOwnershipChanged(avatarItemContainer)
|
||||
local packageInfo = tile and tile:GetPackageInfo()
|
||||
if packageInfo then
|
||||
onOwnershipChanged(avatarItemContainer, packageInfo:IsOwned())
|
||||
end
|
||||
end
|
||||
|
||||
UpdateProfileImage()
|
||||
|
||||
EventHub:addEventListener(EventHub.Notifications["AvatarEquipBegin"], "AvatarPane",
|
||||
function(assetId)
|
||||
UpdateProfileImage(true)
|
||||
spawn(function()
|
||||
PackageData:AwaitWearAssetRequest()
|
||||
LastWearingPackageAssetId = PackageData:GetCachedWearingPackage()
|
||||
end)
|
||||
end)
|
||||
|
||||
EventHub:addEventListener(EventHub.Notifications["DonnedDifferentPackage"], "AvatarPane",
|
||||
function()
|
||||
onEquipChanged()
|
||||
end)
|
||||
EventHub:addEventListener(EventHub.Notifications["DonnedDifferentOutfit"], "AvatarPane",
|
||||
function()
|
||||
onEquipChanged()
|
||||
UpdateProfileImage()
|
||||
end)
|
||||
|
||||
|
||||
local seenXButtonPressed = false
|
||||
local function onSelectAvatar(actionName, inputState, inputObject)
|
||||
if inputState == Enum.UserInputState.Begin then
|
||||
seenXButtonPressed = true
|
||||
elseif inputState == Enum.UserInputState.End and seenXButtonPressed then
|
||||
self:OnSelectAction()
|
||||
end
|
||||
end
|
||||
|
||||
ContextActionService:BindCoreAction("AvatarPaneSelectAction", onSelectAvatar, false, Enum.KeyCode.ButtonX)
|
||||
|
||||
|
||||
self.TransitionTweens = ScreenManager:DefaultFadeIn(MainContainer)
|
||||
|
||||
SortOutfitsScroller()
|
||||
|
||||
MainContainer.Parent = lastParent
|
||||
MainContainer.Visible = true
|
||||
end
|
||||
|
||||
|
||||
function this:Hide()
|
||||
isShown = false
|
||||
MainContainer.Visible = false
|
||||
|
||||
profileImageChangeCn = Utility.DisconnectEvent(profileImageChangeCn)
|
||||
|
||||
RunService:UnbindFromRenderStep("UpdateAvatarGlow")
|
||||
ContextActionService:UnbindCoreAction("AvatarPaneSelectAction")
|
||||
|
||||
|
||||
for _, avatarItemContainer in pairs(AvatarObjects) do
|
||||
avatarItemContainer:Hide()
|
||||
removeListenToOwnershipChanged(avatarItemContainer)
|
||||
end
|
||||
|
||||
EventHub:removeEventListener(EventHub.Notifications["AvatarEquipBegin"], "AvatarPane")
|
||||
EventHub:removeEventListener(EventHub.Notifications["DonnedDifferentPackage"], "AvatarPane")
|
||||
EventHub:removeEventListener(EventHub.Notifications["DonnedDifferentOutfit"], "AvatarPane")
|
||||
|
||||
|
||||
ScreenManager:DefaultCancelFade(self.TransitionTweens)
|
||||
self.TransitionTweens = nil
|
||||
-- Clean out saved selected object so when we tab back
|
||||
-- we will start with the default selection
|
||||
self.SavedSelectObject = nil
|
||||
end
|
||||
|
||||
function this:Focus()
|
||||
inFocus = true
|
||||
|
||||
Utility.DisconnectEvent(OnGuiServiceChangedConn)
|
||||
OnGuiServiceChangedConn = GuiService.Changed:connect(function(prop)
|
||||
if prop == 'SelectedCoreObject' then
|
||||
OnSelectedCoreObjectChanged()
|
||||
end
|
||||
end)
|
||||
OnSelectedCoreObjectChanged()
|
||||
|
||||
if self.SavedSelectObject and self.SavedSelectObject:IsDescendantOf(MainContainer) then
|
||||
GuiService.SelectedCoreObject = self.SavedSelectObject
|
||||
else
|
||||
local defaultSelection = self:GetDefaultSelectableObject()
|
||||
if defaultSelection then
|
||||
GuiService.SelectedCoreObject = defaultSelection
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function this:RemoveFocus()
|
||||
inFocus = false
|
||||
|
||||
OnGuiServiceChangedConn = Utility.DisconnectEvent(OnGuiServiceChangedConn)
|
||||
|
||||
local selectedObject = GuiService.SelectedCoreObject
|
||||
if isShown and selectedObject and selectedObject:IsDescendantOf(MainContainer) then
|
||||
self.SavedSelectObject = GuiService.SelectedCoreObject
|
||||
GuiService.SelectedCoreObject = nil
|
||||
end
|
||||
end
|
||||
|
||||
function this:SetPosition(newPosition)
|
||||
MainContainer.Position = newPosition
|
||||
end
|
||||
|
||||
function this:SetParent(newParent)
|
||||
lastParent = newParent
|
||||
MainContainer.Parent = newParent
|
||||
end
|
||||
|
||||
return this
|
||||
end
|
||||
|
||||
return CreateAvatarPane
|
||||
@@ -0,0 +1,215 @@
|
||||
--[[
|
||||
// AvatarTile.lua
|
||||
|
||||
// Created by Kip Turner
|
||||
// Copyright Roblox 2015
|
||||
]]
|
||||
|
||||
|
||||
|
||||
local TextService = game:GetService('TextService')
|
||||
|
||||
local ContextActionService = game:GetService("ContextActionService")
|
||||
local CoreGui = game:GetService("CoreGui")
|
||||
local ContentProvider = game:GetService("ContentProvider")
|
||||
local GuiRoot = CoreGui:FindFirstChild("RobloxGui")
|
||||
local Modules = GuiRoot:FindFirstChild("Modules")
|
||||
|
||||
|
||||
local Utility = require(Modules:FindFirstChild('Utility'))
|
||||
local Http = require(Modules:FindFirstChild('Http'))
|
||||
local GlobalSettings = require(Modules:FindFirstChild('GlobalSettings'))
|
||||
|
||||
local Errors = require(Modules:FindFirstChild('Errors'))
|
||||
local SoundManager = require(Modules:FindFirstChild('SoundManager'))
|
||||
local ScreenManager = require(Modules:FindFirstChild('ScreenManager'))
|
||||
local AssetManager = require(Modules:FindFirstChild('AssetManager'))
|
||||
local ErrorOverlayModule = require(Modules:FindFirstChild('ErrorOverlay'))
|
||||
local PopupText = require(Modules:FindFirstChild('PopupText'))
|
||||
local PurchasePackagePrompt = require(Modules:FindFirstChild('PurchasePackagePrompt'))
|
||||
|
||||
local BaseTile = require(Modules:FindFirstChild('BaseTile'))
|
||||
local Strings = require(Modules:FindFirstChild('LocalizedStrings'))
|
||||
|
||||
local ACTIVE_AVATAR_BACKGROUND_COLOR = Color3.new(45/255, 96/255, 128/255)
|
||||
local INACTIVE_AVATAR_BACKGROUND_COLOR = Color3.new(106/255, 120/255, 129/255)
|
||||
|
||||
local function createAvatarInfoContainer(packageInfo)
|
||||
local this = BaseTile()
|
||||
local focused = false
|
||||
|
||||
local packageName = packageInfo:GetFullName()
|
||||
|
||||
local function wearPackageAsync()
|
||||
if packageInfo:IsOwned() and not packageInfo:IsWearing() then
|
||||
|
||||
local result = packageInfo:WearAsync()
|
||||
|
||||
if result and result['success'] == true then
|
||||
this:UpdateEquipButton()
|
||||
else
|
||||
local err = Errors.PackageEquip['Default']
|
||||
ScreenManager:OpenScreen(ErrorOverlayModule(err), false)
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
local function buyPackageAsync()
|
||||
local newPurchasePrompt = PurchasePackagePrompt(packageInfo)
|
||||
newPurchasePrompt:SetParent(GuiRoot)
|
||||
newPurchasePrompt:FadeInBackground()
|
||||
ScreenManager:OpenScreen(newPurchasePrompt, false)
|
||||
spawn(function()
|
||||
local didPurchase = newPurchasePrompt:ResultAsync()
|
||||
-- print("Buy Package Result:" , didPurchase , "ownsAsset:" , packageInfo:IsOwned())
|
||||
if didPurchase then
|
||||
SoundManager:Play('PurchaseSuccess')
|
||||
wearPackageAsync()
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
local PriceText = Utility.Create'TextLabel'
|
||||
{
|
||||
Name = 'PriceText';
|
||||
Text = '';
|
||||
Size = UDim2.new(1,0,0,36);
|
||||
Position = UDim2.new(0,0,0,0);
|
||||
TextColor3 = GlobalSettings.BlackTextColor;
|
||||
Font = GlobalSettings.RegularFont;
|
||||
FontSize = GlobalSettings.DescriptionSize;
|
||||
BorderSizePixel = 0;
|
||||
BackgroundColor3 = GlobalSettings.PriceLabelColor;
|
||||
ZIndex = 2;
|
||||
Visible = false;
|
||||
Parent = this.AvatarItemContainer;
|
||||
};
|
||||
|
||||
function this:UpdatePriceText()
|
||||
local newText = ""
|
||||
local price = packageInfo:GetRobuxPrice()
|
||||
if price == 0 then
|
||||
newText = Strings:LocalizedString('FreeWord'):upper()
|
||||
elseif price then
|
||||
newText = "R$ " .. Utility.FormatNumberString(price)
|
||||
end
|
||||
|
||||
PriceText.Text = newText
|
||||
local priceTextSize = TextService:GetTextSize(PriceText.Text, Utility.ConvertFontSizeEnumToInt(PriceText.FontSize), PriceText.Font, Vector2.new())
|
||||
PriceText.Size = UDim2.new(0,priceTextSize.X + 28,0,36)
|
||||
Utility.CalculateAnchor(PriceText, UDim2.new(1,-6, 0, 6), Utility.Enum.Anchor.TopRight)
|
||||
PriceText.Visible = price ~= nil and not packageInfo:IsOwned()
|
||||
end
|
||||
|
||||
if packageInfo:GetAssetId() then
|
||||
this:SetImage(Http.GetThumbnailUrlForAsset(packageInfo:GetAssetId()))
|
||||
else
|
||||
--TODO: show a no package image?
|
||||
end
|
||||
|
||||
|
||||
this:ColorizeImage(packageInfo:IsOwned() and 1 or 0, 0)
|
||||
this:SetPopupText(packageInfo:GetName())
|
||||
|
||||
function this:GetAssetId()
|
||||
return packageInfo:GetAssetId()
|
||||
end
|
||||
|
||||
function this:GetPackageInfo()
|
||||
return packageInfo
|
||||
end
|
||||
|
||||
function this:UpdateOwnership()
|
||||
local ownsAsset = packageInfo:IsOwned()
|
||||
self:SetActive(ownsAsset)
|
||||
self:ColorizeImage(ownsAsset and 1 or 0, 0)
|
||||
self:UpdateEquipButton()
|
||||
self:UpdatePriceText()
|
||||
end
|
||||
|
||||
function this:UpdateEquipButton()
|
||||
self.EquippedCheckmark.Visible = packageInfo:IsWearing()
|
||||
end
|
||||
|
||||
local selectDebounce = false
|
||||
function this:Select()
|
||||
if selectDebounce then return false end
|
||||
local result = false
|
||||
if packageInfo:IsOwned() and not packageInfo:IsWearing() then
|
||||
selectDebounce = true
|
||||
spawn(function()
|
||||
wearPackageAsync()
|
||||
selectDebounce = false
|
||||
end)
|
||||
result = true
|
||||
end
|
||||
return result
|
||||
end
|
||||
|
||||
function this:OnClick()
|
||||
buyPackageAsync()
|
||||
end
|
||||
|
||||
local isWearingConn = nil
|
||||
local ownershipChangedCn = nil
|
||||
local baseShow = this.Show
|
||||
function this:Show()
|
||||
baseShow(self)
|
||||
Utility.DisconnectEvent(isWearingConn)
|
||||
packageInfo.IsWearingChanged:connect(function() self:UpdateEquipButton() end)
|
||||
Utility.DisconnectEvent(ownershipChangedCn)
|
||||
ownershipChangedCn = packageInfo.OwnershipChanged:connect(function()
|
||||
self:UpdateOwnership()
|
||||
end)
|
||||
self:UpdateEquipButton()
|
||||
|
||||
self:UpdateOwnership()
|
||||
end
|
||||
|
||||
local baseHide = this.Hide
|
||||
function this:Hide()
|
||||
baseHide(self)
|
||||
isWearingConn = Utility.DisconnectEvent(isWearingConn)
|
||||
ownershipChangedCn = Utility.DisconnectEvent(ownershipChangedCn)
|
||||
end
|
||||
|
||||
local baseFocus = this.Focus
|
||||
local avatarItemClickConn = nil
|
||||
function this:Focus()
|
||||
baseFocus(self)
|
||||
focused = true
|
||||
|
||||
Utility.DisconnectEvent(avatarItemClickConn)
|
||||
avatarItemClickConn = self.AvatarItemContainer.MouseButton1Click:connect(function()
|
||||
self:OnClick()
|
||||
end)
|
||||
|
||||
self:UpdateEquipButton()
|
||||
|
||||
spawn(function()
|
||||
wait(0.17)
|
||||
if focused then
|
||||
if not packageInfo:IsOwned() then
|
||||
self:ColorizeImage(1)
|
||||
end
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
local baseRemoveFocus = this.RemoveFocus
|
||||
function this:RemoveFocus()
|
||||
baseRemoveFocus(self)
|
||||
focused = false
|
||||
avatarItemClickConn = Utility.DisconnectEvent(avatarItemClickConn)
|
||||
|
||||
-- Decolorize unowned packages
|
||||
if not packageInfo:IsOwned() then
|
||||
self:ColorizeImage(0)
|
||||
end
|
||||
end
|
||||
|
||||
return this
|
||||
end
|
||||
|
||||
return createAvatarInfoContainer
|
||||
@@ -0,0 +1,136 @@
|
||||
--[[
|
||||
// BadgeOverlay.lua
|
||||
|
||||
// Displays information for a single badge
|
||||
// Used by GameDetail and BadgeScreen
|
||||
]]
|
||||
local CoreGui = Game:GetService("CoreGui")
|
||||
local GuiRoot = CoreGui:FindFirstChild("RobloxGui")
|
||||
local Modules = GuiRoot:FindFirstChild("Modules")
|
||||
local GuiService = game:GetService('GuiService')
|
||||
local ContextActionService = game:GetService("ContextActionService")
|
||||
|
||||
local AssetManager = require(Modules:FindFirstChild('AssetManager'))
|
||||
local GlobalSettings = require(Modules:FindFirstChild('GlobalSettings'))
|
||||
local ScrollingTextBox = require(Modules:FindFirstChild('ScrollingTextBox'))
|
||||
local Strings = require(Modules:FindFirstChild('LocalizedStrings'))
|
||||
local Utility = require(Modules:FindFirstChild('Utility'))
|
||||
local BaseOverlay = require(Modules:FindFirstChild('BaseOverlay'))
|
||||
local ScreenManager = require(Modules:FindFirstChild('ScreenManager'))
|
||||
local SoundManager = require(Modules:FindFirstChild('SoundManager'))
|
||||
|
||||
local createBadgeOverlay = function(badgeData)
|
||||
local this = BaseOverlay()
|
||||
|
||||
local hasBadge = badgeData["IsOwned"]
|
||||
this:SetImageBackgroundTransparency(0)
|
||||
this:SetImageBackgroundColor(hasBadge and GlobalSettings.BadgeOwnedColor or GlobalSettings.BadgeOverlayColor)
|
||||
this:SetDropShadow()
|
||||
|
||||
local badgeImage = Utility.Create'ImageLabel'
|
||||
{
|
||||
Name = "BadgeImage";
|
||||
Size = UDim2.new(0, 394, 0, 394);
|
||||
BackgroundTransparency = 1;
|
||||
Image = 'http://www.watrbx.wtf/Thumbs/Asset.ashx?width='..
|
||||
tostring(250)..'&height='..tostring(250)..'&assetId='..tostring(badgeData.AssetId);
|
||||
ZIndex = this.BaseZIndex + 1;
|
||||
Parent = badgeImageContainer;
|
||||
}
|
||||
badgeImage.Position = UDim2.new(0.5, -197, 0.5, -197)
|
||||
this:SetImage(badgeImage)
|
||||
|
||||
local titleText = Utility.Create'TextLabel'
|
||||
{
|
||||
Name = "TitleText";
|
||||
Size = UDim2.new(0, 0, 0, 0);
|
||||
Position = UDim2.new(0, this.RightAlign, 0, 88);
|
||||
BackgroundTransparency = 1;
|
||||
Font = GlobalSettings.RegularFont;
|
||||
FontSize = GlobalSettings.HeaderSize;
|
||||
TextColor3 = GlobalSettings.WhiteTextColor;
|
||||
Text = badgeData.Name;
|
||||
TextXAlignment = Enum.TextXAlignment.Left;
|
||||
ZIndex = this.BaseZIndex;
|
||||
Parent = this.Container;
|
||||
}
|
||||
|
||||
--[[ Has Badge ]]--
|
||||
local hasBadgeContainer = nil
|
||||
if hasBadge then
|
||||
hasBadgeContainer = Utility.Create'Frame'
|
||||
{
|
||||
Name = "HasBadgeContainer";
|
||||
Position = UDim2.new(0, titleText.Position.X.Offset, 0, titleText.Position.Y.Offset + 34);
|
||||
BackgroundTransparency = 1;
|
||||
Parent = this.Container;
|
||||
}
|
||||
local hasBadgeImage = Utility.Create'ImageLabel'
|
||||
{
|
||||
Name = "HasBadgeImage";
|
||||
BackgroundTransparency = 1;
|
||||
ZIndex = this.BaseZIndex;
|
||||
Parent = hasBadgeContainer;
|
||||
}
|
||||
AssetManager.LocalImage(hasBadgeImage,
|
||||
'rbxasset://textures/ui/Shell/Icons/Checkmark', {['720'] = UDim2.new(0,23,0,23); ['1080'] = UDim2.new(0,35,0,35);})
|
||||
hasBadgeContainer.Size = UDim2.new(0, 200, 0, hasBadgeImage.Size.Y.Offset)
|
||||
local hasBadgeText = Utility.Create'TextLabel'
|
||||
{
|
||||
Name = "HasBadgeText";
|
||||
Size = UDim2.new(0, 0, 0, 0);
|
||||
Position = UDim2.new(0, hasBadgeImage.Size.X.Offset + 12, 0.5, 0);
|
||||
BackgroundTransparency = 1;
|
||||
Font = GlobalSettings.ItalicFont;
|
||||
FontSize = GlobalSettings.DescriptionSize;
|
||||
TextColor3 = GlobalSettings.GreenTextColor;
|
||||
TextXAlignment = Enum.TextXAlignment.Left;
|
||||
Text = Strings:LocalizedString("HaveBadgeWord");
|
||||
ZIndex = this.BaseZIndex;
|
||||
Parent = hasBadgeContainer;
|
||||
}
|
||||
end
|
||||
|
||||
--[[ Description ]]--
|
||||
local descriptionYOffset = hasBadgeContainer and hasBadgeContainer.Position.Y.Offset + hasBadgeContainer.Size.Y.Offset + 10 or
|
||||
titleText.Position.Y.Offset + 40
|
||||
|
||||
local descriptionScrollingTextBox = ScrollingTextBox(UDim2.new(0, 762, 0, 304),
|
||||
UDim2.new(0, titleText.Position.X.Offset, 0, descriptionYOffset),
|
||||
this.Container)
|
||||
descriptionScrollingTextBox:SetText(badgeData.Description)
|
||||
descriptionScrollingTextBox:SetFontSize(GlobalSettings.TitleSize)
|
||||
descriptionScrollingTextBox:SetZIndex(this.BaseZIndex)
|
||||
local descriptionFrame = descriptionScrollingTextBox:GetContainer()
|
||||
|
||||
local okButton = Utility.Create'TextButton'
|
||||
{
|
||||
Name = "OkButton";
|
||||
Size = UDim2.new(0, 320, 0, 66);
|
||||
Position = UDim2.new(0, titleText.Position.X.Offset, 1, -66 - 55);
|
||||
BorderSizePixel = 0;
|
||||
BackgroundColor3 = GlobalSettings.BlueButtonColor;
|
||||
Font = GlobalSettings.RegularFont;
|
||||
FontSize = GlobalSettings.ButtonSize;
|
||||
TextColor3 = GlobalSettings.TextSelectedColor;
|
||||
Text = string.upper(Strings:LocalizedString("OkWord"));
|
||||
ZIndex = this.BaseZIndex;
|
||||
Parent = this.Container;
|
||||
|
||||
SoundManager:CreateSound('MoveSelection');
|
||||
}
|
||||
|
||||
--[[ Input Events ]]--
|
||||
okButton.MouseButton1Click:connect(function()
|
||||
this:Close()
|
||||
end)
|
||||
local baseFocus = this.Focus
|
||||
function this:Focus()
|
||||
baseFocus(this)
|
||||
GuiService.SelectedCoreObject = okButton
|
||||
end
|
||||
|
||||
return this
|
||||
end
|
||||
|
||||
return createBadgeOverlay
|
||||
@@ -0,0 +1,123 @@
|
||||
--[[
|
||||
// BadgeScreen.lua
|
||||
|
||||
// Displays a 2xN grid of badges for a game
|
||||
]]
|
||||
local CoreGui = Game:GetService("CoreGui")
|
||||
local GuiRoot = CoreGui:FindFirstChild("RobloxGui")
|
||||
local Modules = GuiRoot:FindFirstChild("Modules")
|
||||
local ContextActionService = game:GetService("ContextActionService")
|
||||
local GuiService = game:GetService('GuiService')
|
||||
|
||||
local AssetManager = require(Modules:FindFirstChild('AssetManager'))
|
||||
local BadgeOverlayModule = require(Modules:FindFirstChild('BadgeOverlay'))
|
||||
local GlobalSettings = require(Modules:FindFirstChild('GlobalSettings'))
|
||||
local ScreenManager = require(Modules:FindFirstChild('ScreenManager'))
|
||||
local ScrollingGrid = require(Modules:FindFirstChild('ScrollingGrid'))
|
||||
local Strings = require(Modules:FindFirstChild('LocalizedStrings'))
|
||||
local Utility = require(Modules:FindFirstChild('Utility'))
|
||||
local PopupText = require(Modules:FindFirstChild('PopupText'))
|
||||
local ThumbnailLoader = require(Modules:FindFirstChild('ThumbnailLoader'))
|
||||
local SoundManager = require(Modules:FindFirstChild('SoundManager'))
|
||||
|
||||
local BaseScreen = require(Modules:FindFirstChild('BaseScreen'))
|
||||
|
||||
local createBadgeScreen = function(badgeData)
|
||||
local this = BaseScreen()
|
||||
|
||||
local ROWS = 2
|
||||
-- columns is dynamic
|
||||
|
||||
local BadgeContainer = this.Container
|
||||
this:SetTitle(string.upper(Strings:LocalizedString("GameBadgesTitle")))
|
||||
|
||||
local defaultSelection = nil
|
||||
|
||||
-- create grid
|
||||
local BadgeScrollGrid = ScrollingGrid()
|
||||
BadgeScrollGrid:SetPosition(UDim2.new(0, 0, 0.5 - (0.57 / 2), 0))
|
||||
BadgeScrollGrid:SetSize(UDim2.new(1, 0, 0, 570))
|
||||
BadgeScrollGrid:SetScrollDirection(BadgeScrollGrid.Enum.ScrollDirection.Horizontal)
|
||||
BadgeScrollGrid:SetParent(BadgeContainer)
|
||||
BadgeScrollGrid:SetClipping(false)
|
||||
BadgeScrollGrid:SetCellSize(Vector2.new(276, 276))
|
||||
BadgeScrollGrid:SetSpacing(Vector2.new(18, 18))
|
||||
|
||||
local checkmarkImage = Utility.Create'ImageLabel'
|
||||
{
|
||||
Name = "CheckMarkImage";
|
||||
BackgroundTransparency = 1;
|
||||
ZIndex = 2;
|
||||
}
|
||||
AssetManager.LocalImage(checkmarkImage, 'rbxasset://textures/ui/Shell/Icons/Checkmark',
|
||||
{['720'] = UDim2.new(0,23,0,23); ['1080'] = UDim2.new(0,35,0,35);})
|
||||
|
||||
local function connectImageInput(image, data)
|
||||
image.MouseButton1Click:connect(function()
|
||||
-- Do not play sound because we are opening a screen here
|
||||
ScreenManager:OpenScreen(BadgeOverlayModule(data), false)
|
||||
end)
|
||||
end
|
||||
|
||||
local baseItem = Utility.Create'TextButton'
|
||||
{
|
||||
Name = "BadgeImage";
|
||||
BorderSizePixel = 0;
|
||||
BackgroundTransparency = 0.2;
|
||||
BackgroundColor3 = Color3.new(64/255, 81/255, 93/255);
|
||||
Text = "";
|
||||
|
||||
SoundManager:CreateSound('MoveSelection');
|
||||
ZIndex = 2;
|
||||
AssetManager.CreateShadow(1);
|
||||
}
|
||||
local badgeIcon = Utility.Create'ImageLabel'
|
||||
{
|
||||
Name = "Thumb";
|
||||
Size = UDim2.new(0, 228, 0, 228);
|
||||
Position = UDim2.new(0.5, -228/2, 0.5, -228/2);
|
||||
BackgroundTransparency = 1;
|
||||
Image = "";
|
||||
ZIndex = 2;
|
||||
Parent = baseItem;
|
||||
}
|
||||
|
||||
for i = 1, #badgeData do
|
||||
local data = badgeData[i]
|
||||
local item = baseItem:Clone()
|
||||
local thumb = item:FindFirstChild("Thumb")
|
||||
if thumb then
|
||||
local thumbLoader = ThumbnailLoader:Create(thumb, data.AssetId,
|
||||
ThumbnailLoader.Sizes.Medium, ThumbnailLoader.AssetType.Icon)
|
||||
spawn(function()
|
||||
thumbLoader:LoadAsync()
|
||||
end)
|
||||
end
|
||||
local hasBadge = data["IsOwned"]
|
||||
if hasBadge then
|
||||
item.BackgroundColor3 = GlobalSettings.BadgeOwnedColor;
|
||||
item.BackgroundTransparency = 0;
|
||||
--
|
||||
local check = checkmarkImage:Clone()
|
||||
check.Position = UDim2.new(1, -check.Size.X.Offset - 8, 0, 8)
|
||||
check.Parent = item
|
||||
end
|
||||
item.Name = tostring(i)
|
||||
BadgeScrollGrid:AddItem(item)
|
||||
connectImageInput(item, data)
|
||||
PopupText(item, data["Name"])
|
||||
if not defaultSelection then
|
||||
defaultSelection = item
|
||||
end
|
||||
end
|
||||
|
||||
--[[ Public API ]]--
|
||||
--Override
|
||||
function this:GetDefaultSelectionObject()
|
||||
return defaultSelection
|
||||
end
|
||||
|
||||
return this
|
||||
end
|
||||
|
||||
return createBadgeScreen
|
||||
@@ -0,0 +1,180 @@
|
||||
--[[
|
||||
// BadgeSort.lua
|
||||
// Creates a badge sort for a game
|
||||
|
||||
// Handles the following for badges
|
||||
// Displays 2x2 of badges on game details page
|
||||
// Displays individual information about each badge (overlay)
|
||||
]]
|
||||
local CoreGui = Game:GetService("CoreGui")
|
||||
local GuiRoot = CoreGui:FindFirstChild("RobloxGui")
|
||||
local Modules = GuiRoot:FindFirstChild("Modules")
|
||||
local GuiService = game:GetService('GuiService')
|
||||
|
||||
local AssetManager = require(Modules:FindFirstChild('AssetManager'))
|
||||
local BadgeScreeenModule = require(Modules:FindFirstChild('BadgeScreen'))
|
||||
local BadgeOverlayModule = require(Modules:FindFirstChild('BadgeOverlay'))
|
||||
local EventHub = require(Modules:FindFirstChild('EventHub'))
|
||||
local ScreenManager = require(Modules:FindFirstChild('ScreenManager'))
|
||||
local GlobalSettings = require(Modules:FindFirstChild('GlobalSettings'))
|
||||
local Utility = require(Modules:FindFirstChild('Utility'))
|
||||
local PopupText = require(Modules:FindFirstChild('PopupText'))
|
||||
local SoundManager = require(Modules:FindFirstChild('SoundManager'))
|
||||
local ThumbnailLoader = require(Modules:FindFirstChild('ThumbnailLoader'))
|
||||
|
||||
local CreateBadgeSort = function(placeName, size, position, parent)
|
||||
local this = {}
|
||||
|
||||
local badgeData = nil
|
||||
local margin = 14
|
||||
local imageSize = (size.Y.Offset - margin) / 2
|
||||
|
||||
local gridImages = {}
|
||||
|
||||
local GRID_SIZE = 4
|
||||
|
||||
--[[ Game Details Grid ]]--
|
||||
local container = Utility.Create'Frame'
|
||||
{
|
||||
Name = "ImageContainer";
|
||||
Size = size;
|
||||
Position = position;
|
||||
BackgroundTransparency = 1;
|
||||
Parent = parent;
|
||||
}
|
||||
-- create 2x2 preview grid
|
||||
local index = 1
|
||||
for i = 1, GRID_SIZE/2 do
|
||||
for j = 1, GRID_SIZE/2 do
|
||||
local image = Utility.Create'TextButton'
|
||||
{
|
||||
Name = tostring(index);
|
||||
Size = UDim2.new(0, imageSize, 0, imageSize);
|
||||
Position = UDim2.new(0, (i - 1) * imageSize + (i - 1) * margin, 0, (j - 1) * imageSize + (j - 1) * margin);
|
||||
BackgroundTransparency = GlobalSettings.FriendStatusTextTransparency;
|
||||
BackgroundColor3 = GlobalSettings.BadgeFrameColor;
|
||||
BorderSizePixel = 0;
|
||||
ZIndex = 2;
|
||||
Text = "";
|
||||
ClipsDescendants = true;
|
||||
Parent = container;
|
||||
|
||||
SoundManager:CreateSound('MoveSelection');
|
||||
AssetManager.CreateShadow(1);
|
||||
}
|
||||
local thumb = Utility.Create'ImageLabel'
|
||||
{
|
||||
Name = "Thumb";
|
||||
Size = UDim2.new(0, 228, 0, 228);
|
||||
Position = UDim2.new(0.5, -228/2, 0.5, -228/2);
|
||||
BackgroundTransparency = 1;
|
||||
Image = "";
|
||||
ZIndex = 2;
|
||||
Parent = image;
|
||||
}
|
||||
gridImages[index] = image
|
||||
index = index + 1
|
||||
end
|
||||
end
|
||||
-- more button visible when #badges > 4
|
||||
local moreBadgesButton = Utility.Create'ImageButton'
|
||||
{
|
||||
Name = "MoreBadgesButton";
|
||||
BackgroundTransparency = 1;
|
||||
Visible = false;
|
||||
Parent = container;
|
||||
|
||||
SoundManager:CreateSound('MoveSelection');
|
||||
ZIndex = 2;
|
||||
}
|
||||
AssetManager.LocalImage(moreBadgesButton, 'rbxasset://textures/ui/Shell/Buttons/MoreButton',
|
||||
{['720'] = UDim2.new(0,72,0,33); ['1080'] = UDim2.new(0,108,0,50);})
|
||||
moreBadgesButton.Position = UDim2.new(1, - moreBadgesButton.AbsoluteSize.x, 1, 12)
|
||||
|
||||
local function updateMoreButton(isSelected)
|
||||
local uri = isSelected and 'rbxasset://textures/ui/Shell/Buttons/MoreButtonSelected'
|
||||
or 'rbxasset://textures/ui/Shell/Buttons/MoreButton'
|
||||
AssetManager.LocalImage(moreBadgesButton, uri, {['720'] = UDim2.new(0,72,0,33); ['1080'] = UDim2.new(0,108,0,50);})
|
||||
end
|
||||
|
||||
moreBadgesButton.SelectionGained:connect(function()
|
||||
updateMoreButton(true)
|
||||
end)
|
||||
moreBadgesButton.SelectionLost:connect(function()
|
||||
updateMoreButton(false)
|
||||
end)
|
||||
|
||||
local checkmarkImage = Utility.Create'ImageLabel'
|
||||
{
|
||||
Name = "CheckMarkImage";
|
||||
BackgroundTransparency = 1;
|
||||
ZIndex = 2;
|
||||
}
|
||||
AssetManager.LocalImage(checkmarkImage, 'rbxasset://textures/ui/Shell/Icons/Checkmark',
|
||||
{['720'] = UDim2.new(0,23,0,23); ['1080'] = UDim2.new(0,35,0,35);})
|
||||
|
||||
local function setBadgeData()
|
||||
if not badgeData then
|
||||
print("BadgeSort: failed to set badge data because data is nil.")
|
||||
return
|
||||
end
|
||||
--
|
||||
for i = 1, #gridImages do
|
||||
if badgeData[i] then
|
||||
local data = badgeData[i]
|
||||
local thumb = gridImages[i]:FindFirstChild("Thumb")
|
||||
if thumb then
|
||||
local thumbLoader = ThumbnailLoader:Create(thumb, data.AssetId,
|
||||
ThumbnailLoader.Sizes.Medium, ThumbnailLoader.AssetType.Icon)
|
||||
spawn(function()
|
||||
thumbLoader:LoadAsync()
|
||||
end)
|
||||
end
|
||||
local hasBadge = data["IsOwned"]
|
||||
if hasBadge then
|
||||
gridImages[i].BackgroundColor3 = GlobalSettings.BadgeOwnedColor
|
||||
gridImages[i].BackgroundTransparency = 0
|
||||
--
|
||||
local check = checkmarkImage:Clone()
|
||||
check.Position = UDim2.new(1, -check.Size.X.Offset - 8, 0, 8)
|
||||
check.Parent = gridImages[i]
|
||||
end
|
||||
--
|
||||
gridImages[i].MouseButton1Click:connect(function()
|
||||
ScreenManager:OpenScreen(BadgeOverlayModule(data), false)
|
||||
end)
|
||||
-- connect popup text
|
||||
PopupText(gridImages[i], data["Name"])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--[[ Input Events ]]--
|
||||
moreBadgesButton.MouseButton1Click:connect(function()
|
||||
EventHub:dispatchEvent(EventHub.Notifications["OpenBadgeScreen"], badgeData, placeName)
|
||||
end)
|
||||
|
||||
--[[ Public API ]]--
|
||||
function this:GetContainer()
|
||||
return container
|
||||
end
|
||||
|
||||
function this:Initialize(data)
|
||||
if not badgeData then
|
||||
badgeData = data
|
||||
setBadgeData()
|
||||
if #data > GRID_SIZE then
|
||||
moreBadgesButton.Visible = true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function this:Destroy()
|
||||
container:Destroy()
|
||||
gridImages = nil
|
||||
end
|
||||
|
||||
return this
|
||||
end
|
||||
|
||||
return CreateBadgeSort
|
||||
@@ -0,0 +1,145 @@
|
||||
--[[
|
||||
// BaseOverlay.lua
|
||||
|
||||
// Implements a base overlay for overlay screens.
|
||||
// Any other overlay classes should require this module
|
||||
// first, then implement its own logic
|
||||
]]
|
||||
local CoreGui = Game:GetService("CoreGui")
|
||||
local GuiRoot = CoreGui:FindFirstChild("RobloxGui")
|
||||
local Modules = GuiRoot:FindFirstChild("Modules")
|
||||
local GuiService = game:GetService('GuiService')
|
||||
local ContextActionService = game:GetService("ContextActionService")
|
||||
|
||||
local AssetManager = require(Modules:FindFirstChild('AssetManager'))
|
||||
local GlobalSettings = require(Modules:FindFirstChild('GlobalSettings'))
|
||||
local ScrollingTextBox = require(Modules:FindFirstChild('ScrollingTextBox'))
|
||||
local Utility = require(Modules:FindFirstChild('Utility'))
|
||||
local ScreenManager = require(Modules:FindFirstChild('ScreenManager'))
|
||||
local SoundManager = require(Modules:FindFirstChild('SoundManager'))
|
||||
|
||||
local FADE_TIME = 0.25
|
||||
|
||||
local createBaseOverlay = function()
|
||||
local this = {}
|
||||
|
||||
local OVERLAY_TRANSPARENCY = GlobalSettings.ModalBackgroundTransparency
|
||||
|
||||
this.RightAlign = 776
|
||||
this.BaseZIndex = 8
|
||||
|
||||
local modalOverlay = Utility.Create'Frame'
|
||||
{
|
||||
Name = "ModalOverlay";
|
||||
Size = UDim2.new(1, 0, 1, 0);
|
||||
BackgroundTransparency = 1;
|
||||
BackgroundColor3 = Color3.new();
|
||||
BorderSizePixel = 0;
|
||||
ZIndex = this.BaseZIndex;
|
||||
}
|
||||
local container = Utility.Create'Frame'
|
||||
{
|
||||
Name = "Container";
|
||||
Size = UDim2.new(1, 0, 0, 668);
|
||||
Position = UDim2.new(0, 0, 0, 227);
|
||||
BorderSizePixel = 0;
|
||||
BackgroundColor3 = GlobalSettings.OverlayColor;
|
||||
ZIndex = this.BaseZIndex;
|
||||
Parent = modalOverlay;
|
||||
}
|
||||
local imageContainer = Utility.Create'Frame'
|
||||
{
|
||||
Name = "ImageContainer";
|
||||
Size = UDim2.new(0, 576, 0, 642);
|
||||
Position = UDim2.new(0, 100, 0.5, -321);
|
||||
BorderSizePixel = 0;
|
||||
BackgroundTransparency = 1;
|
||||
BackgroundColor3 = Color3.new();
|
||||
ZIndex = this.BaseZIndex + 1;
|
||||
Parent = container;
|
||||
}
|
||||
|
||||
this.Container = container
|
||||
|
||||
--[[ Public API ]]--
|
||||
function this:SetImageBackgroundTransparency(value)
|
||||
imageContainer.BackgroundTransparency = value
|
||||
end
|
||||
|
||||
function this:SetImageBackgroundColor(value)
|
||||
imageContainer.BackgroundColor3 = value
|
||||
end
|
||||
|
||||
function this:SetImage(guiImage)
|
||||
guiImage.Position = UDim2.new(0.5, -guiImage.Size.X.Offset/2, 0.5, -guiImage.Size.Y.Offset/2)
|
||||
guiImage.Parent = imageContainer
|
||||
end
|
||||
|
||||
function this:GetOverlaySound()
|
||||
return 'OverlayOpen'
|
||||
end
|
||||
|
||||
function this:SetDropShadow()
|
||||
local dropShadow = AssetManager.CreateShadow(3)
|
||||
dropShadow.Parent = imageContainer
|
||||
end
|
||||
|
||||
function this:GetPriority()
|
||||
return GlobalSettings.DefaultPriority
|
||||
end
|
||||
|
||||
function this:Show()
|
||||
modalOverlay.Parent = ScreenManager:GetScreenGuiByPriority(self:GetPriority())
|
||||
local overlayTweenIn = Utility.PropertyTweener(modalOverlay, "BackgroundTransparency",
|
||||
1, OVERLAY_TRANSPARENCY, FADE_TIME, Utility.EaseInOutQuad, nil)
|
||||
SoundManager:Play(self:GetOverlaySound())
|
||||
|
||||
-- Show the modalOverlay when we are shown
|
||||
modalOverlay.Visible = true
|
||||
end
|
||||
|
||||
function this:Hide()
|
||||
local overlayTweenOut = Utility.PropertyTweener(modalOverlay, "BackgroundTransparency",
|
||||
OVERLAY_TRANSPARENCY, 1, FADE_TIME, Utility.EaseInOutQuad, true,
|
||||
function()
|
||||
modalOverlay:Destroy()
|
||||
end)
|
||||
container.Parent = nil
|
||||
container:Destroy()
|
||||
end
|
||||
|
||||
function this:Focus()
|
||||
ContextActionService:BindCoreAction("CloseOverlay",
|
||||
function(actionName, inputState, inputObject)
|
||||
if inputState == Enum.UserInputState.End then
|
||||
ScreenManager:CloseCurrent()
|
||||
end
|
||||
end,
|
||||
false, Enum.KeyCode.ButtonB)
|
||||
GuiService:AddSelectionParent("Overlay", container)
|
||||
|
||||
-- Don't show overlays when not focused
|
||||
modalOverlay.Visible = true
|
||||
end
|
||||
|
||||
function this:RemoveFocus()
|
||||
ContextActionService:UnbindCoreAction("CloseOverlay")
|
||||
GuiService:RemoveSelectionGroup("Overlay")
|
||||
|
||||
-- Don't show overlays when not focused
|
||||
modalOverlay.Visible = false
|
||||
end
|
||||
|
||||
function this:Close()
|
||||
if ScreenManager:GetTopScreen() == self then
|
||||
SoundManager:Play('ButtonPress')
|
||||
ScreenManager:CloseCurrent()
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
return this
|
||||
end
|
||||
|
||||
return createBaseOverlay
|
||||
@@ -0,0 +1,143 @@
|
||||
--[[
|
||||
// BaseScreen.lua
|
||||
|
||||
// Creates a base screen with breadcrumbs and title. Do not use for a pane/tab
|
||||
]]
|
||||
local CoreGui = Game:GetService("CoreGui")
|
||||
local GuiRoot = CoreGui:FindFirstChild("RobloxGui")
|
||||
local Modules = GuiRoot:FindFirstChild("Modules")
|
||||
|
||||
local ContextActionService = game:GetService("ContextActionService")
|
||||
local GuiService = game:GetService('GuiService')
|
||||
|
||||
local AssetManager = require(Modules:FindFirstChild('AssetManager'))
|
||||
local GlobalSettings = require(Modules:FindFirstChild('GlobalSettings'))
|
||||
local ScreenManager = require(Modules:FindFirstChild('ScreenManager'))
|
||||
local Strings = require(Modules:FindFirstChild('LocalizedStrings'))
|
||||
local Utility = require(Modules:FindFirstChild('Utility'))
|
||||
|
||||
local function createBaseScreen()
|
||||
local this = {}
|
||||
|
||||
local defaultSelectionObject = nil
|
||||
local lastParent = nil
|
||||
|
||||
local container = Utility.Create'Frame'
|
||||
{
|
||||
Name = "Container";
|
||||
Size = UDim2.new(1, 0, 1, 0);
|
||||
BackgroundTransparency = 1;
|
||||
}
|
||||
local backImage = Utility.Create'ImageLabel'
|
||||
{
|
||||
Name = "BackImage";
|
||||
BackgroundTransparency = 1;
|
||||
Parent = container;
|
||||
}
|
||||
AssetManager.LocalImage(backImage,
|
||||
'rbxasset://textures/ui/Shell/Icons/BackIcon', {['720'] = UDim2.new(0,32,0,32); ['1080'] = UDim2.new(0,48,0,48);})
|
||||
local backText = Utility.Create'TextLabel'
|
||||
{
|
||||
Name = "BackText";
|
||||
Size = UDim2.new(0, 0, 0, backImage.Size.Y.Offset);
|
||||
Position = UDim2.new(0, backImage.Size.X.Offset + 8, 0, 0);
|
||||
BackgroundTransparency = 1;
|
||||
Font = GlobalSettings.RegularFont;
|
||||
FontSize = GlobalSettings.ButtonSize;
|
||||
TextXAlignment = Enum.TextXAlignment.Left;
|
||||
TextColor3 = GlobalSettings.WhiteTextColor;
|
||||
Text = "";
|
||||
Parent = container
|
||||
}
|
||||
local titleText = Utility.Create'TextLabel'
|
||||
{
|
||||
Name = "TitleText";
|
||||
Size = UDim2.new(0, 0, 0, 35);
|
||||
Position = UDim2.new(0, 16, 0, backImage.Size.Y.Offset + 74);
|
||||
BackgroundTransparency = 1;
|
||||
Font = GlobalSettings.LightFont;
|
||||
FontSize = GlobalSettings.HeaderSize;
|
||||
TextXAlignment = Enum.TextXAlignment.Left;
|
||||
TextColor3 = GlobalSettings.WhiteTextColor;
|
||||
Text = "";
|
||||
Parent = container;
|
||||
}
|
||||
|
||||
--[[ Public API ]]--
|
||||
this.Container = container
|
||||
|
||||
function this:SetTitle(newTitle)
|
||||
titleText.Text = newTitle
|
||||
end
|
||||
function this:SetBackText(newText)
|
||||
backText.Text = newText
|
||||
end
|
||||
|
||||
function this:GetDefaultSelectionObject()
|
||||
return defaultSelectionObject
|
||||
end
|
||||
|
||||
function this:Destroy()
|
||||
self.Container:Destroy()
|
||||
self = nil
|
||||
end
|
||||
|
||||
--[[ Public API - Screen Management ]]--
|
||||
function this:SetPosition(newPosition)
|
||||
self.Container.Position = newPosition
|
||||
end
|
||||
function this:SetParent(newParent)
|
||||
lastParent = newParent
|
||||
self.Container.Parent = newParent
|
||||
end
|
||||
|
||||
function this:GetName()
|
||||
return titleText.Text
|
||||
end
|
||||
|
||||
function this:Show()
|
||||
local prevScreen = ScreenManager:GetScreenBelow(self)
|
||||
if prevScreen and prevScreen.GetName then
|
||||
self:SetBackText(prevScreen:GetName())
|
||||
else
|
||||
self:SetBackText(string.upper(Strings:LocalizedString("BackWord")))
|
||||
end
|
||||
|
||||
self.Container.Parent = lastParent
|
||||
self.TransisitionTweens = ScreenManager:DefaultFadeIn(self.Container)
|
||||
ScreenManager:PlayDefaultOpenSound()
|
||||
end
|
||||
function this:Hide()
|
||||
self.Container.Parent = nil
|
||||
ScreenManager:DefaultCancelFade(self.TransisitionTweens)
|
||||
self.TransisitionTweens = nil
|
||||
end
|
||||
function this:Focus()
|
||||
if self.SavedSelectedObject and self.SavedSelectedObject:IsDescendantOf(self.Container) then
|
||||
GuiService.SelectedCoreObject = self.SavedSelectedObject
|
||||
else
|
||||
GuiService.SelectedCoreObject = self:GetDefaultSelectionObject()
|
||||
end
|
||||
|
||||
ContextActionService:BindCoreAction("ReturnFromScreen",
|
||||
function(actionName, inputState, inputObject)
|
||||
if inputState == Enum.UserInputState.End then
|
||||
ScreenManager:CloseCurrent()
|
||||
self:Destroy()
|
||||
end
|
||||
end,
|
||||
false, Enum.KeyCode.ButtonB)
|
||||
end
|
||||
function this:RemoveFocus()
|
||||
local selectedObject = GuiService.SelectedCoreObject
|
||||
if selectedObject and selectedObject:IsDescendantOf(self.Container) then
|
||||
self.SavedSelectedObject = selectedObject
|
||||
GuiService.SelectedCoreObject = nil
|
||||
end
|
||||
ContextActionService:UnbindCoreAction("ReturnFromScreen")
|
||||
end
|
||||
|
||||
return this
|
||||
end
|
||||
|
||||
return createBaseScreen
|
||||
@@ -0,0 +1,200 @@
|
||||
--[[
|
||||
// BaseSignInScreen.lua
|
||||
|
||||
// Creates a base screen to be used for account linking and sign in
|
||||
]]
|
||||
local CoreGui = game:GetService("CoreGui")
|
||||
local GuiRoot = CoreGui:FindFirstChild("RobloxGui")
|
||||
local Modules = GuiRoot:FindFirstChild("Modules")
|
||||
|
||||
local AssetManager = require(Modules:FindFirstChild('AssetManager'))
|
||||
local BaseScreen = require(Modules:FindFirstChild('BaseScreen'))
|
||||
local GlobalSettings = require(Modules:FindFirstChild('GlobalSettings'))
|
||||
local SoundManager = require(Modules:FindFirstChild('SoundManager'))
|
||||
local Strings = require(Modules:FindFirstChild('LocalizedStrings'))
|
||||
local TextBox = require(Modules:FindFirstChild('TextBox'))
|
||||
local Utility = require(Modules:FindFirstChild('Utility'))
|
||||
|
||||
|
||||
local PlatformService = nil
|
||||
pcall(function() PlatformService = game:GetService('PlatformService') end)
|
||||
|
||||
local TERMS_OF_SERVICE_URI = "https://en.help.watrbx.wtf/hc/en-us/articles/205358110"
|
||||
|
||||
local function createBaseAccountScreen()
|
||||
local this = BaseScreen()
|
||||
|
||||
local DefaultButtonColor = GlobalSettings.GreyButtonColor
|
||||
local SelectedButtonColor = GlobalSettings.GreySelectedButtonColor
|
||||
local DefaultButtonTextColor = GlobalSettings.WhiteTextColor
|
||||
local SelectedButtonTextColor = GlobalSettings.TextSelectedColor
|
||||
|
||||
local ScreenDivide = Utility.Create'Frame'
|
||||
{
|
||||
Name = "ScreenDivide";
|
||||
Size = UDim2.new(0, 2, 0, 610);
|
||||
Position = UDim2.new(0, 822, 0.5, -305);
|
||||
BorderSizePixel = 0;
|
||||
BackgroundColor3 = GlobalSettings.PageDivideColor;
|
||||
Parent = this.Container;
|
||||
}
|
||||
local DescriptionText = Utility.Create'TextLabel'
|
||||
{
|
||||
Name = "DescriptionText";
|
||||
Size = UDim2.new(0, 740, 0, 500);
|
||||
Position = UDim2.new(0, 16, 0, 334);
|
||||
BackgroundTransparency = 1;
|
||||
Font = GlobalSettings.RegularFont;
|
||||
FontSize = GlobalSettings.TitleSize;
|
||||
TextColor3 = GlobalSettings.WhiteTextColor;
|
||||
TextXAlignment = Enum.TextXAlignment.Left;
|
||||
TextYAlignment = Enum.TextYAlignment.Top;
|
||||
TextWrapped = true;
|
||||
Text = "";
|
||||
Parent = this.Container;
|
||||
}
|
||||
|
||||
local TermsOfServiceText = Utility.Create'TextLabel'
|
||||
{
|
||||
Name = "TermsOfServiceText";
|
||||
Size = UDim2.new(0, 740, 0, 128);
|
||||
Position = UDim2.new(0, 16, 0, 835);
|
||||
BackgroundTransparency = 1;
|
||||
Font = GlobalSettings.LightFont;
|
||||
FontSize = GlobalSettings.ButtonSize;
|
||||
TextColor3 = GlobalSettings.WhiteTextColor;
|
||||
TextXAlignment = Enum.TextXAlignment.Left;
|
||||
TextYAlignment = Enum.TextYAlignment.Top;
|
||||
TextWrapped = true;
|
||||
Text = Strings:LocalizedString("ToSInfoLinkPhrase");
|
||||
Parent = this.Container;
|
||||
}
|
||||
|
||||
local UsernameObject = TextBox(UDim2.new(0, 600, 0, 84))
|
||||
UsernameObject:SetPosition(UDim2.new(0, ScreenDivide.Position.X.Offset + ScreenDivide.Size.X.Offset + 86, 0, 334))
|
||||
UsernameObject:SetSpacing(Vector2.new(20, 0))
|
||||
UsernameObject:SetParent(this.Container)
|
||||
local UsernameTextBox = UsernameObject:GetTextBox()
|
||||
local UsernameSelection = UsernameObject:GetContainer()
|
||||
this.UsernameObject = UsernameObject
|
||||
this.UsernameTextBox = UsernameTextBox
|
||||
this.UsernameSelection = UsernameSelection
|
||||
|
||||
local PasswordObject = TextBox(UDim2.new(0, 600, 0, 84))
|
||||
PasswordObject:SetPosition(UDim2.new(0, ScreenDivide.Position.X.Offset + ScreenDivide.Size.X.Offset + 86, 0, 484))
|
||||
PasswordObject:SetSpacing(Vector2.new(20, 0))
|
||||
PasswordObject:SetParent(this.Container)
|
||||
local PasswordTextBox = PasswordObject:GetTextBox()
|
||||
local PasswordSelection = PasswordObject:GetContainer()
|
||||
this.PasswordObject = PasswordObject
|
||||
this.PasswordTextBox = PasswordTextBox
|
||||
this.PasswordSelection = PasswordSelection
|
||||
|
||||
local function CreateSlicedTextButton(name, text, position)
|
||||
name = name or ""
|
||||
|
||||
local newButton = Utility.Create'ImageButton'
|
||||
{
|
||||
Name = name .. "Button";
|
||||
Size = UDim2.new(0, 320, 0, 64);
|
||||
Position = position or UDim2.new();
|
||||
BackgroundTransparency = 1;
|
||||
ImageColor3 = DefaultButtonColor;
|
||||
Image = 'rbxasset://textures/ui/Shell/Buttons/Generic9ScaleButton@720.png';
|
||||
ScaleType = Enum.ScaleType.Slice;
|
||||
SliceCenter = Rect.new(Vector2.new(4, 4), Vector2.new(28, 28));
|
||||
ZIndex = 2;
|
||||
Parent = this.Container;
|
||||
|
||||
SoundManager:CreateSound('MoveSelection');
|
||||
AssetManager.CreateShadow(1)
|
||||
}
|
||||
local newText = Utility.Create'TextLabel'
|
||||
{
|
||||
Name = name .. "Text";
|
||||
Size = UDim2.new(1, 0, 1, 0);
|
||||
BackgroundTransparency = 1;
|
||||
Font = GlobalSettings.RegularFont;
|
||||
FontSize = GlobalSettings.ButtonSize;
|
||||
TextColor3 = DefaultButtonTextColor;
|
||||
Text = text;
|
||||
ZIndex = 2;
|
||||
Parent = newButton;
|
||||
}
|
||||
|
||||
newButton.SelectionGained:connect(function()
|
||||
newButton.ImageColor3 = SelectedButtonColor
|
||||
newText.TextColor3 = SelectedButtonTextColor
|
||||
end)
|
||||
newButton.SelectionLost:connect(function()
|
||||
newButton.ImageColor3 = DefaultButtonColor
|
||||
newText.TextColor3 = DefaultButtonTextColor
|
||||
end)
|
||||
|
||||
return newButton, newText
|
||||
end
|
||||
|
||||
local function TryLaunchUri(uri)
|
||||
local success, msg = pcall(function()
|
||||
assert(not UserSettings().GameSettings:InStudioMode(), "Can't use in studio")
|
||||
PlatformService:LaunchPlatformUri(uri)
|
||||
end)
|
||||
if not success then
|
||||
print(string.format("PlatformService:LaunchPlatformUri failed to launch uri: %s, for reason: %s", uri, msg))
|
||||
end
|
||||
end
|
||||
|
||||
local SignInButton, SignInText = CreateSlicedTextButton("SignIn",
|
||||
string.upper(Strings:LocalizedString("SignInPhrase")),
|
||||
UDim2.new(0, PasswordSelection.Position.X.Offset, 0,
|
||||
PasswordSelection.Position.Y.Offset + PasswordSelection.Size.Y.Offset + 66))
|
||||
this.SignInButton = SignInButton
|
||||
|
||||
local tosButtonY = SignInButton.Position.Y.Offset -- + SignInButton.Size.Y.Offset + 30
|
||||
local ToSButton, ToSText = CreateSlicedTextButton("ToS",
|
||||
string.upper(Strings:LocalizedString("ToSPhrase")),
|
||||
UDim2.new(0, SignInButton.Position.X.Offset + SignInButton.Size.X.Offset + 10,
|
||||
0, tosButtonY))
|
||||
ToSButton.Size = UDim2.new(0, 270, 0, ToSButton.Size.Y.Offset)
|
||||
|
||||
local tosButtonLastPress = tick() - 1
|
||||
ToSButton.MouseButton1Click:connect(function()
|
||||
if tick() - tosButtonLastPress < 1 then return end
|
||||
tosButtonLastPress = tick()
|
||||
TryLaunchUri(TERMS_OF_SERVICE_URI)
|
||||
end)
|
||||
--[[
|
||||
local PrivacyButton, PrivacyText = CreateSlicedTextButton("Privacy",
|
||||
string.upper(Strings:LocalizedString("PrivacyPhrase")),
|
||||
UDim2.new(0, ToSButton.Position.X.Offset + ToSButton.Size.X.Offset + 5,
|
||||
0, tosButtonY))
|
||||
PrivacyButton.MouseButton1Click:connect(function()
|
||||
TryLaunchUri("http://www.watrbx.wtf/info/Privacy.aspx")
|
||||
end)
|
||||
--]]
|
||||
|
||||
|
||||
-- Override selection - issue with selections remembering their last selection, so in some cases
|
||||
-- the password selection become unselectable. I've talk to Ben about this and we're going to fix it
|
||||
-- TODO: Remove this when selection memory is fixed
|
||||
UsernameSelection.NextSelectionDown = PasswordSelection
|
||||
SignInButton.NextSelectionUp = PasswordSelection
|
||||
|
||||
--[[ Public API ]]--
|
||||
--Override
|
||||
function this:GetDefaultSelectionObject()
|
||||
return UsernameSelection
|
||||
end
|
||||
|
||||
function this:SetDescriptionText(newText)
|
||||
DescriptionText.Text = newText
|
||||
end
|
||||
|
||||
function this:SetButtonText(newText)
|
||||
SignInText.Text = string.upper(newText)
|
||||
end
|
||||
|
||||
return this
|
||||
end
|
||||
|
||||
return createBaseAccountScreen
|
||||
@@ -0,0 +1,131 @@
|
||||
--[[
|
||||
// BaseTile.lua
|
||||
|
||||
// Created by Kip Turner
|
||||
// Copyright Roblox 2015
|
||||
]]
|
||||
|
||||
|
||||
local CoreGui = game:GetService("CoreGui")
|
||||
local GuiRoot = CoreGui:FindFirstChild("RobloxGui")
|
||||
local Modules = GuiRoot:FindFirstChild("Modules")
|
||||
|
||||
|
||||
local Utility = require(Modules:FindFirstChild('Utility'))
|
||||
|
||||
local SoundManager = require(Modules:FindFirstChild('SoundManager'))
|
||||
local AssetManager = require(Modules:FindFirstChild('AssetManager'))
|
||||
local PopupText = require(Modules:FindFirstChild('PopupText'))
|
||||
|
||||
|
||||
local ACTIVE_AVATAR_BACKGROUND_COLOR = Color3.new(45/255, 96/255, 128/255)
|
||||
local INACTIVE_AVATAR_BACKGROUND_COLOR = Color3.new(39/255, 69/255, 82/255) --Color3.new(106/255, 120/255, 129/255)
|
||||
|
||||
local function createBaseTileContainer()
|
||||
local this = {}
|
||||
this.focused = false
|
||||
this.active = false
|
||||
|
||||
|
||||
local avatarItemContainer = Utility.Create'ImageButton'
|
||||
{
|
||||
Name = 'AvatarItemContainer';
|
||||
Size = UDim2.new(0,220,0,220);
|
||||
BorderSizePixel = 0;
|
||||
BackgroundTransparency = 0;
|
||||
BackgroundColor3 = this.active and ACTIVE_AVATAR_BACKGROUND_COLOR or INACTIVE_AVATAR_BACKGROUND_COLOR;
|
||||
AutoButtonColor = false;
|
||||
ClipsDescendants = true;
|
||||
ZIndex = 2;
|
||||
AssetManager.CreateShadow(1);
|
||||
|
||||
SoundManager:CreateSound('MoveSelection');
|
||||
}
|
||||
|
||||
local myPopText = PopupText(avatarItemContainer, '')
|
||||
myPopText:SetZIndex(3)
|
||||
|
||||
|
||||
local avatarImage = Utility.Create'ImageLabel'
|
||||
{
|
||||
Name = "AvatarImage";
|
||||
Size = UDim2.new(1, 0, 1, 0);
|
||||
Position = UDim2.new(0, 0, 0, 0);
|
||||
BackgroundTransparency = 1;
|
||||
ZIndex = 2;
|
||||
Parent = avatarItemContainer;
|
||||
}
|
||||
|
||||
local equippedCheckmark = Utility.Create'ImageLabel'
|
||||
{
|
||||
Name = "EquippedCheckmark";
|
||||
Size = UDim2.new(1, 0, 1, 0);
|
||||
Position = UDim2.new(0, 0, 0, 0);
|
||||
BackgroundTransparency = 1;
|
||||
ZIndex = 3;
|
||||
Visible = false;
|
||||
Image = 'rbxasset://textures/ui/Shell/Icons/EquippedOverlay.png';
|
||||
Parent = avatarItemContainer;
|
||||
}
|
||||
|
||||
this.AvatarItemContainer = avatarItemContainer
|
||||
this.AvatarImage = avatarImage
|
||||
this.EquippedCheckmark = equippedCheckmark
|
||||
|
||||
local function colorizeImage(newColor, duration)
|
||||
duration = duration or 0.2
|
||||
Utility.PropertyTweener(avatarImage, 'ImageColor3', avatarImage.ImageColor3.r, newColor, duration,
|
||||
function(...) local scalar = Utility.EaseOutQuad(...) return Color3.new(scalar, scalar, scalar) end, true)
|
||||
end
|
||||
|
||||
function this:UpdateEquipButton()
|
||||
end
|
||||
|
||||
function this:ColorizeImage(...)
|
||||
colorizeImage(...)
|
||||
end
|
||||
|
||||
function this:SetPopupText(newText)
|
||||
myPopText:SetText(newText)
|
||||
end
|
||||
|
||||
function this:SetImage(imgUrl)
|
||||
avatarImage.Image = imgUrl
|
||||
end
|
||||
|
||||
function this:GetGuiObject()
|
||||
return avatarItemContainer
|
||||
end
|
||||
|
||||
function this:GetPackageInfo()
|
||||
end
|
||||
|
||||
function this:OnClick()
|
||||
end
|
||||
|
||||
function this:SetActive(isActive)
|
||||
self.active = isActive
|
||||
avatarItemContainer.BackgroundColor3 = self.active and ACTIVE_AVATAR_BACKGROUND_COLOR or INACTIVE_AVATAR_BACKGROUND_COLOR;
|
||||
end
|
||||
|
||||
function this:Select()
|
||||
end
|
||||
|
||||
function this:Show()
|
||||
end
|
||||
|
||||
function this:Hide()
|
||||
end
|
||||
|
||||
function this:Focus()
|
||||
self.focused = true
|
||||
end
|
||||
|
||||
function this:RemoveFocus()
|
||||
self.focused = false
|
||||
end
|
||||
|
||||
return this
|
||||
end
|
||||
|
||||
return createBaseTileContainer
|
||||
@@ -0,0 +1,261 @@
|
||||
-- Written by Kip Turner, Copyright ROBLOX 2015
|
||||
-- CFrame animations by Tomarty :)
|
||||
|
||||
|
||||
local CoreGui = game:GetService("CoreGui")
|
||||
local GuiRoot = CoreGui:FindFirstChild("RobloxGui")
|
||||
local Modules = GuiRoot:FindFirstChild("Modules")
|
||||
local Utility = require(Modules:FindFirstChild('Utility'))
|
||||
local GlobalSettings = require(Modules:FindFirstChild('GlobalSettings'))
|
||||
local RunService = game:GetService("RunService")
|
||||
|
||||
local PlatformService;
|
||||
pcall(function() PlatformService = game:GetService('PlatformService') end)
|
||||
local UserInputService = game:GetService('UserInputService')
|
||||
|
||||
local function clerp(x)
|
||||
return (math.sin(math.pi * (x - 0.5)) + 1) * 0.5
|
||||
end
|
||||
|
||||
local function clerp0(x)
|
||||
return clerp(x * 0.5)
|
||||
end
|
||||
local function clerp1(x)
|
||||
return clerp(x * 0.5 + 0.5)
|
||||
end
|
||||
|
||||
|
||||
local CameraManager = {}
|
||||
|
||||
function CameraManager:StartTransitionScreenEffect()
|
||||
if PlatformService then
|
||||
PlatformService.Brightness = GlobalSettings.SceneBrightness
|
||||
PlatformService.Contrast = GlobalSettings.SceneContrast
|
||||
PlatformService.GrayscaleLevel = GlobalSettings.SceneGrayscaleLevel
|
||||
PlatformService.TintColor = GlobalSettings.SceneTintColor
|
||||
PlatformService.BlurIntensity = GlobalSettings.SceneMotionBlurIntensity
|
||||
end
|
||||
end
|
||||
|
||||
function CameraManager:EndTransitionScreenEffect( ... )
|
||||
-- if PlatformService then
|
||||
-- PlatformService.Brightness = GlobalSettings.SceneBrightness
|
||||
-- PlatformService.Contrast = GlobalSettings.SceneContrast
|
||||
-- PlatformService.GrayscaleLevel = GlobalSettings.SceneGrayscaleLevel
|
||||
-- PlatformService.TintColor = GlobalSettings.SceneTintColor
|
||||
-- PlatformService.BlurIntensity = GlobalSettings.SceneBlurIntensity
|
||||
-- end
|
||||
end
|
||||
|
||||
local cameraConnection;
|
||||
|
||||
|
||||
local camera = workspace.CurrentCamera
|
||||
local function onCameraChanged()
|
||||
if workspace.CurrentCamera then
|
||||
camera = workspace.CurrentCamera
|
||||
camera.CameraType = 'Scriptable'
|
||||
end
|
||||
end
|
||||
workspace.Changed:connect(function()
|
||||
if prop == 'CurrentCamera' then
|
||||
onCameraChanged()
|
||||
end
|
||||
end)
|
||||
onCameraChanged()
|
||||
|
||||
local function CFrameBezierLerp(cframes, t)
|
||||
local cframes2 = {}
|
||||
for i = 1, #cframes - 1 do
|
||||
cframes2[i] = cframes[i]:lerp(cframes[i + 1], t)
|
||||
end
|
||||
if #cframes2 == 1 then
|
||||
return cframes2[1]
|
||||
end
|
||||
return CFrameBezierLerp(cframes2, t)
|
||||
end
|
||||
|
||||
local cameraMoveCn = nil
|
||||
local gamepadInput = Vector2.new(0, 0)
|
||||
function CameraManager:EnableCameraControl()
|
||||
cameraMoveCn = Utility.DisconnectEvent(cameraMoveCn)
|
||||
cameraMoveCn = UserInputService.InputChanged:connect(function(input)
|
||||
if input.KeyCode == Enum.KeyCode.Thumbstick2 then
|
||||
gamepadInput = input.Position or gamepadInput
|
||||
gamepadInput = Vector2.new(gamepadInput.X, gamepadInput.Y)
|
||||
end
|
||||
end)
|
||||
end
|
||||
function CameraManager:DisableCameraControl()
|
||||
cameraMoveCn = Utility.DisconnectEvent(cameraMoveCn)
|
||||
gamepadInput = Vector2.new(0, 0)
|
||||
end
|
||||
|
||||
local getGamepadInputCFrame; do
|
||||
local gamepadInputLerping = Vector2.new(0, 0)
|
||||
local timestamp0 = tick()
|
||||
function getGamepadInputCFrame()
|
||||
local timestamp1 = tick()
|
||||
local deltaTime = timestamp1 - timestamp0
|
||||
timestamp0 = timestamp1
|
||||
local unit = 0.125 ^ deltaTime
|
||||
gamepadInputLerping = gamepadInputLerping * unit + gamepadInput * (1 - unit)
|
||||
return CFrame.new(gamepadInputLerping.X/8, gamepadInputLerping.Y/8, 0) * CFrame.Angles(0, -gamepadInputLerping.X / 12, 0) * CFrame.Angles(gamepadInputLerping.Y / 12, 0, 0)
|
||||
end
|
||||
end
|
||||
|
||||
local function lerpToCFrame(cframes, length, useVelocity, yieldEarlyLength)
|
||||
-- print("Lerpin")
|
||||
|
||||
local name = "CameraScriptCutsceneLerp"
|
||||
|
||||
yieldEarlyLength = yieldEarlyLength or 0
|
||||
yieldEarlyLength = 1 - yieldEarlyLength / length
|
||||
|
||||
if useVelocity then
|
||||
-- TODO: estimate the total distance traveled
|
||||
--length = (cf0.p - cf1.p).magnitude / length
|
||||
end
|
||||
|
||||
if cameraConnection then
|
||||
cameraConnection:disconnect()
|
||||
cameraConnection = nil
|
||||
end
|
||||
|
||||
local timestamp0 = tick()
|
||||
|
||||
local event = Instance.new("BindableEvent")
|
||||
|
||||
cameraConnection = {
|
||||
disconnect = function()
|
||||
if event then
|
||||
event:Fire()
|
||||
event = nil
|
||||
end
|
||||
RunService:UnbindFromRenderStep(name)
|
||||
end
|
||||
}
|
||||
|
||||
RunService:BindToRenderStep(name, Enum.RenderPriority.Camera.Value, function()
|
||||
|
||||
local timestamp1 = tick()
|
||||
local t = (timestamp1 - timestamp0) / length
|
||||
|
||||
if t >= 1 then
|
||||
t = 1
|
||||
end
|
||||
|
||||
--print(t)
|
||||
|
||||
local cam = Workspace.CurrentCamera
|
||||
cam.CoordinateFrame = CFrameBezierLerp(cframes, t) * getGamepadInputCFrame()
|
||||
if t >= 1 then
|
||||
if event then
|
||||
event:Fire()
|
||||
event = nil
|
||||
end
|
||||
cameraConnection:disconnect()
|
||||
elseif t >= yieldEarlyLength then
|
||||
--local test = cf0:lerp(cf1, (t))
|
||||
--print(test)
|
||||
if event then
|
||||
event:Fire()
|
||||
event = nil
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
event.Event:wait()
|
||||
|
||||
end
|
||||
|
||||
|
||||
local function GetCameraParts(model)
|
||||
local parts = {}
|
||||
for i, part in pairs(model:GetChildren()) do
|
||||
parts[tonumber(part.Name:sub(4, -1))] = part
|
||||
part.Transparency = 1
|
||||
end
|
||||
return parts
|
||||
end
|
||||
|
||||
local function PlayAnimationAsync(cameras, doTransition, isPan)
|
||||
|
||||
if #cameras <= 1 then
|
||||
return
|
||||
end
|
||||
|
||||
local cam = Workspace.CurrentCamera
|
||||
local time = 1.7
|
||||
|
||||
cam.CoordinateFrame = cameras[1].CFrame
|
||||
|
||||
if (doTransition) then
|
||||
delay(time, function()
|
||||
if PlatformService then
|
||||
Utility.PropertyTweener(PlatformService, 'Contrast', PlatformService.Contrast, GlobalSettings.SceneContrast, time, Utility.EaseInOutQuad, true)
|
||||
Utility.PropertyTweener(PlatformService, 'BlurIntensity', PlatformService.BlurIntensity, GlobalSettings.SceneMotionBlurIntensity, time, Utility.EaseInOutQuad, true)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
|
||||
local cframes = {}
|
||||
for i = 1, #cameras do
|
||||
cframes[i] = cameras[i].CFrame * CFrame.new(0, 0, -cameras[i].Size.Z / 2)
|
||||
end
|
||||
lerpToCFrame(cframes, isPan and 60 or 120, false, time + 0.1)
|
||||
-- lerpToCFrame(cframes, 15, false, time + 0.1)
|
||||
|
||||
if PlatformService then
|
||||
Utility.PropertyTweener(PlatformService, 'Contrast', GlobalSettings.SceneContrast, -1, time, Utility.EaseInOutQuad, true)
|
||||
Utility.PropertyTweener(PlatformService, 'BlurIntensity', GlobalSettings.SceneMotionBlurIntensity, 50, time, Utility.EaseInOutQuad, true)
|
||||
end
|
||||
|
||||
wait(time)
|
||||
|
||||
end
|
||||
|
||||
pcall(function()
|
||||
local function recurse(model)
|
||||
local children = model:GetChildren()
|
||||
for i = 1, #children do
|
||||
local child = children[i]
|
||||
if child:IsA("BasePart") then
|
||||
child.Locked = true
|
||||
child.Anchored = true
|
||||
child.CanCollide = false
|
||||
end
|
||||
recurse(child)
|
||||
end
|
||||
end
|
||||
recurse(workspace)
|
||||
end)
|
||||
|
||||
|
||||
|
||||
local ZoneManager = require(script.Parent:WaitForChild("CameraManagerModules"):WaitForChild("CameraManager_ZoneManager"))
|
||||
|
||||
function CameraManager:CameraMoveToAsync( ... )
|
||||
local cameraSets = {}
|
||||
for k, f in pairs(workspace:WaitForChild("Cameras"):GetChildren()) do
|
||||
cameraSets[f.Name] = GetCameraParts(f)
|
||||
end
|
||||
|
||||
local first = true
|
||||
while true do
|
||||
ZoneManager:SetZone("City")
|
||||
PlayAnimationAsync(cameraSets.City, not first, false)
|
||||
ZoneManager:SetZone("Space")
|
||||
PlayAnimationAsync(cameraSets.Space, true, true)
|
||||
ZoneManager:SetZone("Volcano")
|
||||
PlayAnimationAsync(cameraSets.Volcano, true, true)
|
||||
|
||||
first = false
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
return CameraManager
|
||||
|
||||
|
||||
+121
@@ -0,0 +1,121 @@
|
||||
-- Written by Tomarty, Copyright ROBLOX 2015
|
||||
local runService = game:GetService("RunService")
|
||||
|
||||
|
||||
|
||||
|
||||
local ZoneAnimator;
|
||||
|
||||
do
|
||||
|
||||
local animators = {}
|
||||
|
||||
local function GetAnimator(zone)
|
||||
if animators[zone] then
|
||||
return animators[zone]
|
||||
end
|
||||
local moduleScript = script.Parent:WaitForChild("CameraManager_Zones"):FindFirstChild("CameraManagerZone_" .. tostring(zone))
|
||||
if not moduleScript then
|
||||
return
|
||||
end
|
||||
-- print('require', moduleScript:GetFullName())
|
||||
local animator = require(moduleScript)
|
||||
animators[zone] = animator
|
||||
return animator
|
||||
end
|
||||
|
||||
|
||||
ZoneAnimator = {
|
||||
CurrentZone = nil;
|
||||
}
|
||||
|
||||
local connection;
|
||||
|
||||
function ZoneAnimator:SetZone(zone)
|
||||
if ZoneAnimator.CurrentZone == zone then
|
||||
return
|
||||
end
|
||||
ZoneAnimator.CurrentZone = zone
|
||||
if connection then
|
||||
connection:disconnect()
|
||||
connection = nil
|
||||
end
|
||||
local animator = GetAnimator(zone)
|
||||
if not animator then
|
||||
return
|
||||
end
|
||||
animator:SetEnabled(true)
|
||||
connection = {
|
||||
disconnect = function()
|
||||
animator:SetEnabled(false)
|
||||
end
|
||||
}
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
local SkyboxManager;
|
||||
|
||||
do
|
||||
SkyboxManager = {}
|
||||
|
||||
local activeSkybox = nil
|
||||
function SkyboxManager:SetZone(id)
|
||||
if activeSkybox then
|
||||
activeSkybox.Parent = nil
|
||||
activeSkybox = nil
|
||||
end
|
||||
local Skyboxes = game:GetService("ReplicatedStorage"):WaitForChild("Skyboxes")
|
||||
local skybox = Skyboxes:FindFirstChild(id or "default")
|
||||
if skybox then
|
||||
activeSkybox = skybox:Clone()
|
||||
local lighting = game:GetService("Lighting")
|
||||
skybox.Parent = lighting
|
||||
pcall(function()
|
||||
for k, v in pairs(skybox:GetChildren()) do
|
||||
lighting[v.Name] = v.Value
|
||||
end
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
local ZoneManager = {
|
||||
Zone = nil;
|
||||
}
|
||||
|
||||
--local ZoneAnimator = require(script.ZoneAnimator)
|
||||
--local Skybox = require(script.Skybox)
|
||||
|
||||
local function setZoneInternal(zone)
|
||||
ZoneManager.Zone = zone
|
||||
SkyboxManager:SetZone(zone)
|
||||
ZoneAnimator:SetZone(zone)
|
||||
end
|
||||
|
||||
function ZoneManager:SetZone(zone)
|
||||
if zone == ZoneManager.Zone then
|
||||
return
|
||||
end
|
||||
if runService:IsRunning() then
|
||||
setZoneInternal(zone)
|
||||
else
|
||||
spawn(function()
|
||||
while not runService:IsRunning() do wait() end
|
||||
setZoneInternal(zone)
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
return ZoneManager
|
||||
|
||||
|
||||
|
||||
+218
@@ -0,0 +1,218 @@
|
||||
local runService = game:GetService("RunService")
|
||||
|
||||
|
||||
local Zones = workspace:FindFirstChild("Zones")
|
||||
local City = Zones and Zones:FindFirstChild("City")
|
||||
|
||||
local CFramer = {}; do
|
||||
function CFramer.GetAllParts(model, list)
|
||||
list = list or {}
|
||||
|
||||
if model:IsA("BasePart") then
|
||||
list[#list + 1] = model
|
||||
end
|
||||
|
||||
local children = model:GetChildren()
|
||||
for i = 1, #children do
|
||||
CFramer.GetAllParts(children[i], list)
|
||||
end
|
||||
|
||||
return list
|
||||
end
|
||||
|
||||
function CFramer.GetAllPartOffsets(parts, cframeBase)
|
||||
-- The offset is a part's cframe relative to cframeBase
|
||||
|
||||
local offsets = {}
|
||||
local cframeBase_inv = cframeBase:inverse()
|
||||
for i = 1, #parts do
|
||||
offsets[i] = cframeBase_inv * parts[i].CFrame
|
||||
end
|
||||
|
||||
return offsets
|
||||
end
|
||||
|
||||
function CFramer.newCFramer(parts, offsets)
|
||||
return function(cf)
|
||||
for i = 1, #parts do
|
||||
parts[i].CFrame = cf * offsets[i]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function CFramer.newCFramerFromModel(model, cframeBase)
|
||||
local parts = CFramer.GetAllParts(model)
|
||||
local offsets = CFramer.GetAllPartOffsets(parts, cframeBase)
|
||||
return CFramer.newCFramer(parts, offsets)
|
||||
end
|
||||
end
|
||||
|
||||
local renderTrains;
|
||||
spawn(function()
|
||||
while not runService:IsRunning() do wait(0.1) end
|
||||
|
||||
if not City then
|
||||
City = workspace:WaitForChild("Zones"):WaitForChild("City")
|
||||
end
|
||||
|
||||
local exampleTrain = City.Train:Clone()
|
||||
local function newTrain()
|
||||
local train = exampleTrain:Clone()
|
||||
train.Parent = workspace
|
||||
return CFramer.newCFramerFromModel(train.Parts, train.Track.CFrame)
|
||||
end
|
||||
|
||||
|
||||
local function newTrack(part, direction)
|
||||
if not direction then
|
||||
direction = math.random(0, 1) * 2 - 1
|
||||
end
|
||||
local length = part.Size.Y
|
||||
local cf = part.CFrame * CFrame.Angles(math.pi / 2 * (direction + 1), 0, 0)
|
||||
local cf0 = cf * CFrame.new(0, -length/2, 0)
|
||||
return function(train, time)
|
||||
local distance = time * 48
|
||||
if distance >= 0 and distance <= length then
|
||||
train(cf0 * CFrame.new(0, distance, 0))
|
||||
end
|
||||
end, part.Size.Y
|
||||
end
|
||||
|
||||
local train0 = newTrain()
|
||||
local train1 = newTrain()
|
||||
local train2 = newTrain()
|
||||
local train3 = newTrain()
|
||||
local train4 = newTrain()
|
||||
local train5 = newTrain()
|
||||
local train6 = newTrain()
|
||||
|
||||
local tracks = City.Tracks
|
||||
|
||||
local track0 = newTrack(tracks.TrackA1)--, 1)
|
||||
local track1 = newTrack(tracks.TrackA2)--, -1)
|
||||
local track2 = newTrack(tracks.TrackA3)--, 1)
|
||||
local track3 = newTrack(tracks.TrackB1)--, 1)
|
||||
local track4 = newTrack(tracks.TrackB2)--, -1)
|
||||
local track5 = newTrack(tracks.TrackC1)--, -1)
|
||||
local track6 = newTrack(tracks.TrackC2)--, 1)
|
||||
local track7 = newTrack(tracks.TrackC3)--, 1)
|
||||
|
||||
function renderTrains(timestamp)
|
||||
|
||||
track0(train0, timestamp % 15)
|
||||
track1(train1, (timestamp - 3) % 20)
|
||||
track2(train2, (timestamp - 5.5) % 18)
|
||||
|
||||
track3(train3, (timestamp - 5.5) % 25)
|
||||
|
||||
track6(train4, (timestamp - 2.5) % 30)
|
||||
end
|
||||
end)
|
||||
|
||||
|
||||
local renderFlows;
|
||||
|
||||
spawn(function()
|
||||
|
||||
if not City then
|
||||
City = workspace:WaitForChild("Zones"):WaitForChild("City")
|
||||
end
|
||||
|
||||
|
||||
local function newFlow(model, flowLength)
|
||||
|
||||
local parts = {}
|
||||
local partParts = {}
|
||||
local partPartOffsets = {}
|
||||
local cframes = {}
|
||||
|
||||
for j, part in pairs(model:GetChildren()) do
|
||||
local i = tonumber(part.Name)
|
||||
parts[i] = part
|
||||
cframes[i] = part.CFrame
|
||||
local pParts = {}
|
||||
local pPartOffsets = {}
|
||||
for i, pPart in pairs(part:GetChildren()) do
|
||||
if pPart:IsA("BasePart") then
|
||||
pParts[#pParts + 1] = pPart
|
||||
pPartOffsets[#pPartOffsets + 1] = part.CFrame:inverse() * pPart.CFrame
|
||||
end
|
||||
end
|
||||
partParts[i] = pParts
|
||||
partPartOffsets[i] = pPartOffsets
|
||||
end
|
||||
|
||||
|
||||
local partCycle = 0
|
||||
local unitOffset = 0
|
||||
local timestamp0 = 0
|
||||
return function(timestamp1)
|
||||
if timestamp1 < timestamp0 then
|
||||
timestamp0 = timestamp1
|
||||
end
|
||||
local unit = (timestamp1 - timestamp0) / flowLength + unitOffset
|
||||
|
||||
if unit >= 1 then
|
||||
partCycle = (partCycle + math.floor(unit)) % #parts
|
||||
unit = unit % 1
|
||||
unitOffset = unit
|
||||
timestamp0 = timestamp1 -- - deltaTime * flowLength
|
||||
end
|
||||
|
||||
for x = 0, #parts - 2 do
|
||||
local i = (x - partCycle) % #cframes + 1
|
||||
local cf = cframes[x + 1]:lerp(cframes[x + 2], unit)
|
||||
parts[i].CFrame = cf
|
||||
local pParts = partParts[i]
|
||||
local pPartOffsets = partPartOffsets[i]
|
||||
for n = 1, #pParts do
|
||||
pParts[n].CFrame = cf * pPartOffsets[n]
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
local flow1 = newFlow(City.Pipe1.Flow, 0.5)
|
||||
local flow2 = newFlow(City.Pipe2.Flow, 0.65)
|
||||
|
||||
function renderFlows(t)
|
||||
flow1(t)
|
||||
flow2(t)
|
||||
end
|
||||
|
||||
end)
|
||||
|
||||
|
||||
|
||||
local connection;
|
||||
|
||||
local self = {}
|
||||
|
||||
function self:SetEnabled(enabled)
|
||||
if enabled and connection then
|
||||
return
|
||||
end
|
||||
if connection then
|
||||
connection:disconnect()
|
||||
connection = nil
|
||||
end
|
||||
if not enabled then
|
||||
return
|
||||
end
|
||||
|
||||
local timestamp0 = tick()
|
||||
connection = game:GetService("RunService").RenderStepped:connect(function()
|
||||
local t = tick() - timestamp0
|
||||
if renderFlows then
|
||||
renderFlows(t)
|
||||
end
|
||||
if renderTrains then
|
||||
renderTrains(t)
|
||||
end
|
||||
end)
|
||||
|
||||
end
|
||||
|
||||
return self
|
||||
+145
@@ -0,0 +1,145 @@
|
||||
local runService = game:GetService("RunService")
|
||||
|
||||
local CFrameOffsetter = {};
|
||||
|
||||
do
|
||||
function CFrameOffsetter.GetAllParts(model, list)
|
||||
list = list or {}
|
||||
|
||||
if model:IsA("BasePart") then
|
||||
list[#list + 1] = model
|
||||
end
|
||||
|
||||
local children = model:GetChildren()
|
||||
for i = 1, #children do
|
||||
CFrameOffsetter.GetAllParts(children[i], list)
|
||||
end
|
||||
|
||||
return list
|
||||
end
|
||||
function CFrameOffsetter.GetAllPartOffsets(parts, cframeBase)
|
||||
-- The offset is a part's cframe relative to cframeBase
|
||||
|
||||
local offsets = {}
|
||||
local cframeBase_inv = cframeBase:inverse()
|
||||
for i = 1, #parts do
|
||||
offsets[i] = cframeBase_inv * parts[i].CFrame
|
||||
end
|
||||
|
||||
return offsets
|
||||
end
|
||||
function CFrameOffsetter.newOffsetter(parts, offsets, cframeBase)
|
||||
return function(offset)
|
||||
local cf = cframeBase * offset
|
||||
for i = 1, #parts do
|
||||
parts[i].CFrame = cf * offsets[i]
|
||||
end
|
||||
end
|
||||
end
|
||||
function CFrameOffsetter.newOffsetterFromModel(model, cframeBase)
|
||||
local parts = CFrameOffsetter.GetAllParts(model)
|
||||
local offsets = CFrameOffsetter.GetAllPartOffsets(parts, cframeBase)
|
||||
return CFrameOffsetter.newOffsetter(parts, offsets, cframeBase)
|
||||
end
|
||||
end
|
||||
|
||||
local newOffsetterFromModel = CFrameOffsetter.newOffsetterFromModel
|
||||
|
||||
local renderStation;
|
||||
spawn(function()
|
||||
while not runService:IsRunning() do wait(0.1) end
|
||||
|
||||
local Space = workspace:WaitForChild("Zones"):WaitForChild("Space")
|
||||
local model = Space.Station
|
||||
local cframeBase = model.rings.center.CFrame
|
||||
|
||||
local ring1 = newOffsetterFromModel(model.rings.Ring1, cframeBase)
|
||||
local ring2 = newOffsetterFromModel(model.rings.Ring2, cframeBase)
|
||||
local ring3 = newOffsetterFromModel(model.rings.Ring3, cframeBase)
|
||||
local station = newOffsetterFromModel(model.station, cframeBase)
|
||||
|
||||
|
||||
function renderStation(t)
|
||||
ring1(
|
||||
CFrame.Angles(0, (t / 6 % (math.pi * 2)), 0)
|
||||
* CFrame.new(0, math.sin(t / 4 % (math.pi * 2)) * 1, 0)
|
||||
)
|
||||
ring2(
|
||||
CFrame.Angles(0, -(t / 4 % (math.pi * 2)), 0)
|
||||
* CFrame.new(0, math.sin(2 + t / 2 % (math.pi * 2)) * 1, 0)
|
||||
)
|
||||
ring3(
|
||||
CFrame.Angles(0, (t / 3.5 % (math.pi * 2)), 0)
|
||||
* CFrame.new(0, math.sin(1 + t / 2 % (math.pi * 2)) * 1, 0)
|
||||
)
|
||||
|
||||
station(
|
||||
CFrame.Angles(0, (t / -32 % (math.pi * 2)), 0)
|
||||
* CFrame.new(0, math.sin(t / 4 % (math.pi * 2)) * 3 - 2, 0)
|
||||
)
|
||||
end
|
||||
end)
|
||||
|
||||
local renderOrbit;
|
||||
spawn(function()
|
||||
while not runService:IsRunning() do wait(0.1) end
|
||||
|
||||
local Space = workspace:WaitForChild("Zones"):WaitForChild("Space")
|
||||
|
||||
local model = Space.Orbit
|
||||
local cframeBase = model.center.CFrame
|
||||
|
||||
local cuteMoonThings = newOffsetterFromModel(model, cframeBase)
|
||||
|
||||
local ringParts = {Space.Disk, Space.Disk2, Space.Disk3}
|
||||
|
||||
local rings = {}
|
||||
for i = 1, #ringParts do
|
||||
rings[i] = newOffsetterFromModel(ringParts[i], ringParts[i].CFrame)
|
||||
end
|
||||
|
||||
function renderOrbit(t)
|
||||
cuteMoonThings(
|
||||
CFrame.Angles((t / 80 % (math.pi * 2)), (t / 60 % (math.pi * 2)), (t / 40 % (math.pi * 2)))
|
||||
)
|
||||
for i = 1, #rings do
|
||||
rings[i](
|
||||
CFrame.Angles(0, 1 / 4 + (t / 60 % (math.pi * 2)), 0)
|
||||
)
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
|
||||
|
||||
|
||||
local connection;
|
||||
|
||||
local self = {}
|
||||
|
||||
function self:SetEnabled(enabled)
|
||||
if enabled and connection then
|
||||
return
|
||||
end
|
||||
if connection then
|
||||
connection:disconnect()
|
||||
connection = nil
|
||||
end
|
||||
if not enabled then
|
||||
return
|
||||
end
|
||||
|
||||
local timestamp0 = tick()
|
||||
connection = game:GetService("RunService").RenderStepped:connect(function()
|
||||
local t = (tick() - timestamp0)
|
||||
if renderStation then
|
||||
renderStation(t)
|
||||
end
|
||||
if renderOrbit then
|
||||
renderOrbit(t)
|
||||
end
|
||||
end)
|
||||
|
||||
end
|
||||
|
||||
return self
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
local runService = game:GetService("RunService")
|
||||
|
||||
|
||||
local Volcano;
|
||||
local lightning;
|
||||
|
||||
spawn(function()
|
||||
while not runService:IsRunning() do wait(0.1) end
|
||||
|
||||
Volcano = workspace:WaitForChild("Zones"):WaitForChild("Volcano")
|
||||
lightning = Volcano.Lightning:GetChildren()
|
||||
for i = 1, #lightning do
|
||||
lightning[i].Parent = nil
|
||||
end
|
||||
end)
|
||||
|
||||
local function playLighting(model)
|
||||
if not (model and Volcano) then
|
||||
return
|
||||
end
|
||||
model.Parent = Volcano.Lightning
|
||||
wait(0.125)
|
||||
model.Parent = nil
|
||||
end
|
||||
|
||||
|
||||
|
||||
local connection;
|
||||
local handle;
|
||||
|
||||
local self = {}
|
||||
|
||||
function self:SetEnabled(enabled)
|
||||
if enabled and connection then
|
||||
return
|
||||
end
|
||||
if connection then
|
||||
connection:disconnect()
|
||||
connection = nil
|
||||
end
|
||||
handle = nil
|
||||
if not enabled then
|
||||
return
|
||||
end
|
||||
|
||||
local h = {} -- our handle
|
||||
handle = h -- Stops a previous loop
|
||||
coroutine.wrap(function()
|
||||
while handle == h do
|
||||
wait(math.random() * 16 + 1)
|
||||
|
||||
if lightning then
|
||||
playLighting(lightning[math.random(1, #lightning)])
|
||||
end
|
||||
|
||||
end
|
||||
end)()
|
||||
|
||||
--[[
|
||||
local timestamp0 = tick()
|
||||
connection = game:GetService("RunService").RenderStepped:connect(function()
|
||||
local t = (tick() - timestamp0)
|
||||
--renderStation(t)
|
||||
--renderOrbit(t)
|
||||
end)
|
||||
--]]
|
||||
|
||||
end
|
||||
|
||||
return self
|
||||
@@ -0,0 +1,285 @@
|
||||
--[[
|
||||
// CarouselController.lua
|
||||
|
||||
// Controls how the data is updated for a carousel view
|
||||
]]
|
||||
local CoreGui = Game:GetService("CoreGui")
|
||||
local GuiRoot = CoreGui:FindFirstChild("RobloxGui")
|
||||
local Modules = GuiRoot:FindFirstChild("Modules")
|
||||
|
||||
local ContextActionService = game:GetService('ContextActionService')
|
||||
local GuiService = game:GetService('GuiService')
|
||||
|
||||
local EventHub = require(Modules:FindFirstChild('EventHub'))
|
||||
local GlobalSettings = require(Modules:FindFirstChild('GlobalSettings'))
|
||||
local SoundManager = require(Modules:FindFirstChild('SoundManager'))
|
||||
local ThumbnailLoader = require(Modules:FindFirstChild('ThumbnailLoader'))
|
||||
local Utility = require(Modules:FindFirstChild('Utility'))
|
||||
|
||||
local function createCarouselController(view)
|
||||
local this = {}
|
||||
|
||||
local PAGE_SIZE = 25
|
||||
local MAX_PAGES = 2
|
||||
local LOAD_BUFFER = 10
|
||||
local LOAD_AMOUNT = 100
|
||||
|
||||
local sortCollection = nil
|
||||
local sortDataObject = nil -- TODO: Remove when caching is finished
|
||||
local sortData = nil
|
||||
local currentFocusData = nil
|
||||
local sortDataIndex = 1
|
||||
local guiServiceChangedCn = nil
|
||||
|
||||
local pages = {}
|
||||
local firstIndex = 0
|
||||
local lastIndex = 0
|
||||
local isLoading = false
|
||||
|
||||
-- Events
|
||||
this.NewItemSelected = Utility.Signal()
|
||||
|
||||
local function getNewItem(data)
|
||||
local item = Utility.Create'ImageButton'
|
||||
{
|
||||
Name = "CarouselViewImage";
|
||||
BackgroundColor3 = GlobalSettings.ModalBackgroundColor;
|
||||
SoundManager:CreateSound('MoveSelection');
|
||||
}
|
||||
spawn(function()
|
||||
local loader = ThumbnailLoader:Create(item, data.IconId, ThumbnailLoader.Sizes.Medium, ThumbnailLoader.AssetType.Icon)
|
||||
loader:LoadAsync(true, false, nil)
|
||||
end)
|
||||
|
||||
return item
|
||||
end
|
||||
|
||||
-- TODO: Remove this when caching is finished. Doing it this way is going to leave the old
|
||||
-- data issues in place with the carousel
|
||||
local function setInternalData(page)
|
||||
if not page then
|
||||
return {}
|
||||
end
|
||||
|
||||
local newData = {}
|
||||
|
||||
local placeIds = page:GetPagePlaceIds()
|
||||
local names = page:GetPagePlaceNames()
|
||||
local voteData = page:GetPageVoteData()
|
||||
local iconIds = page:GetPageIconIds()
|
||||
local creatorNames = page:GetCreatorNames()
|
||||
local creatorUserIds = page:GetPageCreatorUserIds()
|
||||
|
||||
for i = 1, #page.Data do
|
||||
local gameEntry = {
|
||||
Title = names[i];
|
||||
PlaceId = placeIds[i];
|
||||
IconId = iconIds[i];
|
||||
VoteData = voteData[i];
|
||||
CreatorName = creatorNames[i];
|
||||
CreatorUserId = creatorUserIds[i];
|
||||
-- Description and IsFavorites needs to be queried for each game when needed
|
||||
Description = nil;
|
||||
IsFavorited = nil;
|
||||
GameData = nil;
|
||||
}
|
||||
table.insert(newData, gameEntry)
|
||||
end
|
||||
|
||||
return newData
|
||||
end
|
||||
|
||||
local function createPage(startIndex)
|
||||
local page = {}
|
||||
local items = {}
|
||||
|
||||
for i = 1, PAGE_SIZE do
|
||||
local dataIndex = startIndex + (i - 1)
|
||||
if sortData[dataIndex] then
|
||||
local item = getNewItem(sortData[dataIndex])
|
||||
item.MouseButton1Click:connect(function()
|
||||
local currentData = sortData[dataIndex]
|
||||
if currentData then
|
||||
EventHub:dispatchEvent(EventHub.Notifications["OpenGameDetail"], currentData.PlaceId, currentData.Title, currentData.IconId, currentData.GameData)
|
||||
end
|
||||
end)
|
||||
table.insert(items, item)
|
||||
end
|
||||
end
|
||||
|
||||
function page:GetCount()
|
||||
return #items
|
||||
end
|
||||
|
||||
function page:GetItems()
|
||||
return items
|
||||
end
|
||||
|
||||
function page:Destroy()
|
||||
for i,item in pairs(items) do
|
||||
items[i] = nil
|
||||
item:Destroy()
|
||||
end
|
||||
end
|
||||
|
||||
return page
|
||||
end
|
||||
|
||||
local previousFocusItem = nil
|
||||
local function onViewFocusChanged(newFocusItem)
|
||||
local offset = 0
|
||||
if previousFocusItem then
|
||||
offset = view:GetItemIndex(newFocusItem) - view:GetItemIndex(previousFocusItem)
|
||||
end
|
||||
|
||||
local visibleItemCount = view:GetVisibleCount()
|
||||
local itemCount = view:GetCount()
|
||||
|
||||
if offset > 0 then
|
||||
-- scrolled right
|
||||
local firstVisibleItemIndex = view:GetFirstVisibleItemIndex()
|
||||
if not isLoading and firstVisibleItemIndex + visibleItemCount + LOAD_BUFFER >= itemCount then
|
||||
isLoading = true
|
||||
local page = createPage(lastIndex + 1)
|
||||
if page:GetCount() > 0 then
|
||||
local items = page:GetItems()
|
||||
view:InsertCollectionBack(items)
|
||||
table.insert(pages, page)
|
||||
lastIndex = lastIndex + page:GetCount()
|
||||
-- remove front page
|
||||
if view:GetCount() > PAGE_SIZE * MAX_PAGES then
|
||||
local firstPage = table.remove(pages, 1)
|
||||
firstIndex = firstIndex + firstPage:GetCount()
|
||||
view:RemoveAmountFromFront(firstPage:GetCount())
|
||||
firstPage:Destroy()
|
||||
end
|
||||
end
|
||||
isLoading = false
|
||||
end
|
||||
elseif offset < 0 then
|
||||
-- scrolled left
|
||||
local lastVisibleItemIndex = view:GetLastVisibleItemIndex()
|
||||
if not isLoading and lastVisibleItemIndex - visibleItemCount - LOAD_BUFFER < 0 then
|
||||
isLoading = true
|
||||
local page = createPage(firstIndex - PAGE_SIZE)
|
||||
if page:GetCount() > 0 then
|
||||
local items = page:GetItems()
|
||||
view:InsertCollectionFront(items)
|
||||
table.insert(pages, 1, page)
|
||||
firstIndex = firstIndex - page:GetCount()
|
||||
if view:GetCount() > PAGE_SIZE * MAX_PAGES then
|
||||
local lastPage = table.remove(pages)
|
||||
lastIndex = lastIndex - lastPage:GetCount()
|
||||
view:RemoveAmountFromBack(lastPage:GetCount())
|
||||
lastPage:Destroy()
|
||||
end
|
||||
end
|
||||
isLoading = false
|
||||
end
|
||||
end
|
||||
|
||||
sortDataIndex = sortDataIndex + offset
|
||||
previousFocusItem = newFocusItem
|
||||
view:ChangeFocus(newFocusItem)
|
||||
currentFocusData = sortData[sortDataIndex]
|
||||
this.NewItemSelected:fire(currentFocusData)
|
||||
end
|
||||
|
||||
--[[ Public API ]]--
|
||||
function this:GetCurrentFocusGameData()
|
||||
return currentFocusData
|
||||
end
|
||||
|
||||
function this:InitializeAsync(gameCollection)
|
||||
view:RemoveAllItems()
|
||||
firstIndex = 1
|
||||
lastIndex = 1
|
||||
currentFocusData = nil
|
||||
pages = {}
|
||||
sortData = {}
|
||||
|
||||
sortCollection = gameCollection
|
||||
|
||||
local page = gameCollection:GetSortAsync(0, LOAD_AMOUNT)
|
||||
sortData = setInternalData(page)
|
||||
|
||||
local firstPage = createPage(firstIndex)
|
||||
if firstPage:GetCount() > 0 then
|
||||
local items = firstPage:GetItems()
|
||||
view:InsertCollectionBack(items)
|
||||
table.insert(pages, firstPage)
|
||||
lastIndex = firstPage:GetCount()
|
||||
|
||||
local frontViewItem = view:GetFront()
|
||||
if frontViewItem then
|
||||
previousFocusItem = frontViewItem
|
||||
sortDataIndex = 1
|
||||
currentFocusData = sortData[sortDataIndex]
|
||||
view:SetFocus(frontViewItem)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function this:Connect()
|
||||
guiServiceChangedCn = Utility.DisconnectEvent(guiServiceChangedCn)
|
||||
guiServiceChangedCn = GuiService.Changed:connect(function(property)
|
||||
if property ~= 'SelectedCoreObject' then
|
||||
return
|
||||
end
|
||||
local newSelection = GuiService.SelectedCoreObject
|
||||
if newSelection and view:ContainsItem(newSelection) then
|
||||
onViewFocusChanged(newSelection)
|
||||
end
|
||||
end)
|
||||
|
||||
local seenRightBumper = false
|
||||
local function onBumperRight(actionName, inputState, inputObject)
|
||||
if inputState == Enum.UserInputState.Begin then
|
||||
seenRightBumper = true
|
||||
elseif seenRightBumper and inputState == Enum.UserInputState.End then
|
||||
local currentSelection = GuiService.SelectedCoreObject
|
||||
if currentSelection and view:ContainsItem(currentSelection) then
|
||||
local currentFocusIndex = view:GetItemIndex(previousFocusItem)
|
||||
local shiftAmount = view:GetFullVisibleItemCount()
|
||||
local nextItem = view:GetItemAt(currentFocusIndex + shiftAmount) or view:GetBack()
|
||||
if nextItem then
|
||||
GuiService.SelectedCoreObject = nextItem
|
||||
end
|
||||
end
|
||||
seenRightBumper = false
|
||||
end
|
||||
end
|
||||
|
||||
local seenLeftBumper = false
|
||||
local function onBumperLeft(actionName, inputState, inputObject)
|
||||
if inputState == Enum.UserInputState.Begin then
|
||||
seenLeftBumper = true
|
||||
elseif seenLeftBumper and inputState == Enum.UserInputState.End then
|
||||
local currentSelection = GuiService.SelectedCoreObject
|
||||
if currentSelection and view:ContainsItem(currentSelection) then
|
||||
local currentFocusIndex = view:GetItemIndex(previousFocusItem)
|
||||
local shiftAmount = view:GetFullVisibleItemCount()
|
||||
local nextItem = view:GetItemAt(currentFocusIndex - shiftAmount) or view:GetFront()
|
||||
if nextItem then
|
||||
GuiService.SelectedCoreObject = nextItem
|
||||
end
|
||||
end
|
||||
seenRightBumper = false
|
||||
end
|
||||
end
|
||||
|
||||
-- Bumper Binds
|
||||
ContextActionService:BindCoreAction("BumperRight", onBumperRight, false, Enum.KeyCode.ButtonR1)
|
||||
ContextActionService:BindCoreAction("BumperLeft", onBumperLeft, false, Enum.KeyCode.ButtonL1)
|
||||
end
|
||||
|
||||
function this:Disconnect()
|
||||
guiServiceChangedCn = Utility.DisconnectEvent(guiServiceChangedCn)
|
||||
ContextActionService:UnbindCoreAction("BumperRight")
|
||||
ContextActionService:UnbindCoreAction("BumperLeft")
|
||||
end
|
||||
|
||||
return this
|
||||
end
|
||||
|
||||
return createCarouselController
|
||||
@@ -0,0 +1,292 @@
|
||||
--[[
|
||||
// CarouselView.lua
|
||||
|
||||
// View for a carousel. Used for GameGenre screen
|
||||
// TODO: Support Vertical?
|
||||
//
|
||||
// Current this supports a focus that is aligned to the left (0, 0), in the future we
|
||||
// could do other alignments if we need them
|
||||
]]
|
||||
local CoreGui = Game:GetService("CoreGui")
|
||||
local GuiRoot = CoreGui:FindFirstChild("RobloxGui")
|
||||
local Modules = GuiRoot:FindFirstChild("Modules")
|
||||
|
||||
local Utility = require(Modules:FindFirstChild('Utility'))
|
||||
|
||||
local function createCarouselView()
|
||||
local this = {}
|
||||
|
||||
local items = {}
|
||||
local padding = 0
|
||||
local itemSizePercentOfContainer = 1
|
||||
local focusItem = nil
|
||||
|
||||
local BASE_TWEEN_TIME = 0.2
|
||||
|
||||
local container = Utility.Create'ScrollingFrame'
|
||||
{
|
||||
Name = "CarouselContainer";
|
||||
BackgroundTransparency = 1;
|
||||
ClipsDescendants = false;
|
||||
ScrollingEnabled = false;
|
||||
Selectable = false;
|
||||
ScrollBarThickness = 0;
|
||||
}
|
||||
|
||||
local function isVisible(item)
|
||||
return item.AbsolutePosition.x + item.AbsoluteSize.x >= 0 and
|
||||
item.AbsolutePosition.x < GuiRoot.AbsoluteSize.x
|
||||
end
|
||||
local function getFocusSize()
|
||||
local size = container.Size.Y.Offset
|
||||
return UDim2.new(0, size, 0, size)
|
||||
end
|
||||
local function getNonFocusSize()
|
||||
local size = container.Size.Y.Offset * itemSizePercentOfContainer
|
||||
return UDim2.new(0, size, 0, size)
|
||||
end
|
||||
local function getItemSize(item)
|
||||
if item == focusItem then
|
||||
return getFocusSize()
|
||||
else
|
||||
return getNonFocusSize()
|
||||
end
|
||||
end
|
||||
|
||||
local function getItemLayoutPosition(index)
|
||||
local focusIndex = this:GetItemIndex(focusItem)
|
||||
local offsetFromFocus = index - focusIndex
|
||||
local x, y = 0, 0
|
||||
|
||||
if index > focusIndex then
|
||||
-- items to the right of focus need additional buffer due to focus size being larger
|
||||
x = getFocusSize().X.Offset + offsetFromFocus * padding + (offsetFromFocus - 1) * getNonFocusSize().X.Offset
|
||||
else
|
||||
x = offsetFromFocus * padding + offsetFromFocus * getNonFocusSize().X.Offset
|
||||
end
|
||||
y = (index == focusIndex and 0) or (container.Size.Y.Offset - getNonFocusSize().Y.Offset) / 2
|
||||
|
||||
return UDim2.new(0, x, 0, y)
|
||||
end
|
||||
|
||||
local function recalcLayout()
|
||||
for i = 1, #items do
|
||||
local item = items[i]
|
||||
local size = getItemSize(item)
|
||||
local position = getItemLayoutPosition(i)
|
||||
if item:IsDescendantOf(game.Workspace) then
|
||||
item:TweenSizeAndPosition(size, position, Enum.EasingDirection.Out, Enum.EasingStyle.Quad, 0, true)
|
||||
else
|
||||
item.Size = size
|
||||
item.Position = position
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--[[ Public API ]]--
|
||||
function this:ChangeFocus(newFocus)
|
||||
-- We don't use SetFocus() as that function will do a recalc. We want to get the next position
|
||||
-- from current position, not recalcd postion for each item
|
||||
if self:ContainsItem(newFocus) then
|
||||
focusItem = newFocus
|
||||
for i = 1, #items do
|
||||
local item = items[i]
|
||||
item:TweenSizeAndPosition(getItemSize(item), getItemLayoutPosition(i), Enum.EasingDirection.Out, Enum.EasingStyle.Quad, BASE_TWEEN_TIME, true)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function this:SetSize(newSize)
|
||||
if newSize ~= container.Size then
|
||||
container.Size = newSize
|
||||
container.CanvasSize = UDim2.new(0, container.Size.X.Offset * 2, 1, 0)
|
||||
recalcLayout()
|
||||
end
|
||||
end
|
||||
|
||||
function this:SetPosition(newPosition)
|
||||
container.Position = newPosition
|
||||
end
|
||||
|
||||
function this:SetPadding(newPadding)
|
||||
if newPadding ~= padding then
|
||||
padding = newPadding
|
||||
recalcLayout()
|
||||
end
|
||||
end
|
||||
|
||||
function this:SetItemSizePercentOfContainer(value)
|
||||
if value ~= itemSizePercentOfContainer then
|
||||
itemSizePercentOfContainer = value
|
||||
recalcLayout()
|
||||
end
|
||||
end
|
||||
|
||||
function this:SetParent(newParent)
|
||||
container.Parent = newParent
|
||||
end
|
||||
|
||||
function this:SetFocus(newFocusItem)
|
||||
if self:ContainsItem(newFocusItem) and newFocusItem ~= focusItem then
|
||||
focusItem = newFocusItem
|
||||
recalcLayout()
|
||||
end
|
||||
end
|
||||
|
||||
function this:GetFocusItem()
|
||||
return focusItem
|
||||
end
|
||||
|
||||
function this:GetItemAt(index)
|
||||
return items[index]
|
||||
end
|
||||
|
||||
function this:GetFront()
|
||||
return items[1]
|
||||
end
|
||||
|
||||
function this:GetBack()
|
||||
return items[#items]
|
||||
end
|
||||
|
||||
function this:GetItemIndex(item)
|
||||
for i = 1, #items do
|
||||
if items[i] == item then
|
||||
return i
|
||||
end
|
||||
end
|
||||
|
||||
return 0
|
||||
end
|
||||
|
||||
function this:GetCount()
|
||||
return #items
|
||||
end
|
||||
|
||||
function this:GetVisibleCount()
|
||||
local visibleItemCount = 0
|
||||
for i = 1, #items do
|
||||
if isVisible(items[i]) then
|
||||
visibleItemCount = visibleItemCount + 1
|
||||
end
|
||||
end
|
||||
|
||||
return visibleItemCount
|
||||
end
|
||||
|
||||
function this:GetFirstVisibleItemIndex()
|
||||
for i = 1, #items do
|
||||
if isVisible(items[i]) then
|
||||
return i
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function this:GetLastVisibleItemIndex()
|
||||
for i = #items, 1, -1 do
|
||||
if isVisible(items[i]) then
|
||||
return i
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function this:GetFullVisibleItemCount()
|
||||
local containerSizeX = container.AbsoluteSize.x
|
||||
-- remove focus from the size, and figure out how many other items can fit
|
||||
local fittingSize = containerSizeX - getFocusSize().X.Offset
|
||||
if fittingSize <= 0 then
|
||||
return 0
|
||||
end
|
||||
|
||||
local itemSize = getNonFocusSize().X.Offset + padding
|
||||
local count = math.floor(fittingSize/itemSize) + 1
|
||||
return count
|
||||
end
|
||||
|
||||
function this:InsertCollectionFront(collection)
|
||||
for i = #collection, 1, -1 do
|
||||
local item = collection[i]
|
||||
table.insert(items, 1, item)
|
||||
item.Parent = container
|
||||
end
|
||||
recalcLayout()
|
||||
end
|
||||
|
||||
function this:InsertCollectionBack(collection)
|
||||
for i = 1, #collection do
|
||||
local item = collection[i]
|
||||
table.insert(items, item)
|
||||
item.Parent = container
|
||||
end
|
||||
recalcLayout()
|
||||
end
|
||||
|
||||
function this:RemoveAmountFromFront(amount)
|
||||
for i = 1, amount do
|
||||
local item = table.remove(items, 1)
|
||||
item.Parent = nil
|
||||
end
|
||||
recalcLayout()
|
||||
end
|
||||
|
||||
function this:RemoveAmountFromBack(amount)
|
||||
for i = 1, amount do
|
||||
local item = table.remove(items)
|
||||
item.Parent = nil
|
||||
end
|
||||
recalcLayout()
|
||||
end
|
||||
|
||||
function this:InsertFront(newItem)
|
||||
table.insert(items, 1, newItem)
|
||||
newItem.Parent = container
|
||||
recalcLayout()
|
||||
end
|
||||
|
||||
function this:InsertBack(newItem)
|
||||
table.insert(items, newItem)
|
||||
newItem.Parent = container
|
||||
recalcLayout()
|
||||
end
|
||||
|
||||
function this:RemoveFront()
|
||||
local item = table.remove(items, 1)
|
||||
item.Parent = nil
|
||||
recalcLayout()
|
||||
end
|
||||
|
||||
function this:RemoveBack()
|
||||
local item = table.remove(items, #items)
|
||||
item.Parent = nil
|
||||
recalcLayout()
|
||||
end
|
||||
|
||||
function this:RemoveItem(item)
|
||||
for i = 1, #items do
|
||||
if items[i] == item then
|
||||
local removedItem = table.remove(items, i)
|
||||
removedItem.Parent = nil
|
||||
break
|
||||
end
|
||||
end
|
||||
recalcLayout()
|
||||
end
|
||||
|
||||
function this:RemoveAllItems()
|
||||
for i = #items, 1, -1 do
|
||||
local item = table.remove(items, #items)
|
||||
item.Parent = nil
|
||||
end
|
||||
end
|
||||
|
||||
function this:ContainsItem(item)
|
||||
if not item then
|
||||
return false
|
||||
end
|
||||
return item.Parent == container
|
||||
end
|
||||
|
||||
return this
|
||||
end
|
||||
|
||||
return createCarouselView
|
||||
@@ -0,0 +1,449 @@
|
||||
--[[
|
||||
// ConfirmPrompt.lua
|
||||
// Kip Turner
|
||||
// Copyright Roblox 2015
|
||||
]]
|
||||
local CoreGui = game:GetService("CoreGui")
|
||||
local GuiRoot = CoreGui:FindFirstChild("RobloxGui")
|
||||
local Modules = GuiRoot:FindFirstChild("Modules")
|
||||
local ContextActionService = game:GetService("ContextActionService")
|
||||
local GuiService = game:GetService('GuiService')
|
||||
local MarketplaceService = game:GetService('MarketplaceService')
|
||||
|
||||
local UserDataModule = require(Modules:FindFirstChild('UserData'))
|
||||
local Http = require(Modules:FindFirstChild('Http'))
|
||||
local ScrollingTextBox = require(Modules:FindFirstChild('ScrollingTextBox'))
|
||||
|
||||
local AssetManager = require(Modules:FindFirstChild('AssetManager'))
|
||||
local GlobalSettings = require(Modules:FindFirstChild('GlobalSettings'))
|
||||
local Utility = require(Modules:FindFirstChild('Utility'))
|
||||
local Strings = require(Modules:FindFirstChild('LocalizedStrings'))
|
||||
|
||||
local ScreenManager = require(Modules:FindFirstChild('ScreenManager'))
|
||||
local SoundManager = require(Modules:FindFirstChild('SoundManager'))
|
||||
local CurrencyWidgetModule = require(Modules:FindFirstChild('CurrencyWidget'))
|
||||
|
||||
local MOCKUP_WIDTH = 1920
|
||||
local MOCKUP_HEIGHT = 1080
|
||||
local CONTENT_WIDTH = 1920
|
||||
local CONTENT_HEIGHT = 690
|
||||
local PACKAGE_CONTAINER_WIDTH = 780
|
||||
local PACKAGE_CONTAINER_HEIGHT = 690
|
||||
local PACKAGE_BACKGROUND_WIDTH = 580
|
||||
local PACKAGE_BACKGROUND_HEIGHT = 640
|
||||
|
||||
local CONTENT_POSITION = Vector2.new(0, 225)
|
||||
|
||||
local DETAILS_CONTAINER_WIDTH = CONTENT_WIDTH - PACKAGE_CONTAINER_WIDTH
|
||||
local DETAILS_CONTAINER_HEIGHT = 690
|
||||
|
||||
local DESCRIPTION_WIDTH = 800
|
||||
local DESCRIPTION_HEIGHT = 265
|
||||
|
||||
local BUY_BUTTON_WIDTH = 320
|
||||
local BUY_BUTTON_HEIGHT = 64
|
||||
|
||||
local TEXT_START_OFFSET = Vector2.new(0, 135)
|
||||
local TEXT_SPACING = Vector2.new(0, 20)
|
||||
|
||||
local BUY_BUTTON_OFFSET = Vector2.new(0, -50)
|
||||
|
||||
local ROBUX_BALANCE_OFFSET = Vector2.new(100, -130)
|
||||
|
||||
local DELAY_BEFORE_PURCHASE = 1
|
||||
|
||||
local function CreateConfirmPrompt(confirmDetails, properties)
|
||||
local this = {}
|
||||
|
||||
properties = properties or {}
|
||||
|
||||
local MyParent = nil
|
||||
|
||||
local InFocus = false
|
||||
local Result = nil
|
||||
local ResultEvent = Utility.Signal()
|
||||
|
||||
local OnResultCallbacks = {}
|
||||
|
||||
local productName = confirmDetails.ProductName
|
||||
|
||||
local ConfirmPrompt = Utility.Create'Frame'
|
||||
{
|
||||
Name = "ConfirmPrompt";
|
||||
Size = UDim2.new(1, 0, 1, 0);
|
||||
BackgroundTransparency = 1;
|
||||
BackgroundColor3 = GlobalSettings.ModalBackgroundColor;
|
||||
BorderSizePixel = 0;
|
||||
ZIndex = 4;
|
||||
}
|
||||
|
||||
local ContentContainer = Utility.Create'Frame'
|
||||
{
|
||||
Name = "ContentContainer";
|
||||
Size = UDim2.new(CONTENT_WIDTH/MOCKUP_WIDTH, 0, CONTENT_HEIGHT/MOCKUP_HEIGHT, 0);
|
||||
Position = UDim2.new(CONTENT_POSITION.x/MOCKUP_WIDTH, 0, CONTENT_POSITION.y/MOCKUP_HEIGHT, 0);
|
||||
BackgroundTransparency = 0;
|
||||
BackgroundColor3 = GlobalSettings.OverlayColor;
|
||||
BorderSizePixel = 0;
|
||||
ZIndex = 4;
|
||||
Parent = ConfirmPrompt;
|
||||
}
|
||||
|
||||
local PackageContainer = Utility.Create'Frame'
|
||||
{
|
||||
Name = "PackageContainer";
|
||||
Size = UDim2.new(PACKAGE_CONTAINER_WIDTH/CONTENT_WIDTH, 0, PACKAGE_CONTAINER_HEIGHT/CONTENT_HEIGHT, 0);
|
||||
Position = UDim2.new(0,0,0,0);
|
||||
BackgroundTransparency = 1;
|
||||
BorderSizePixel = 0;
|
||||
ZIndex = 4;
|
||||
Parent = ContentContainer;
|
||||
}
|
||||
local PackageBackground = Utility.Create'Frame'
|
||||
{
|
||||
Name = "PackageBackground";
|
||||
Size = UDim2.new(PACKAGE_BACKGROUND_WIDTH/PACKAGE_CONTAINER_WIDTH, 0, PACKAGE_BACKGROUND_HEIGHT/CONTENT_HEIGHT, 0);
|
||||
BackgroundTransparency = 0;
|
||||
BackgroundColor3 = GlobalSettings.ForegroundGreyColor;
|
||||
BorderSizePixel = 0;
|
||||
ZIndex = 4;
|
||||
Parent = PackageContainer;
|
||||
AssetManager.CreateShadow(3);
|
||||
}
|
||||
Utility.CalculateAnchor(PackageBackground, UDim2.new(0.5,0,0.5,0), Utility.Enum.Anchor.Center)
|
||||
local PackageClipper = Utility.Create'Frame'
|
||||
{
|
||||
Name = "PackageClipper";
|
||||
Size = UDim2.new(1,0,1,0);
|
||||
BackgroundTransparency = 1;
|
||||
ClipsDescendants = true;
|
||||
Parent = PackageBackground;
|
||||
}
|
||||
local PackageImage = Utility.Create'ImageLabel'
|
||||
{
|
||||
Name = 'PackageImage';
|
||||
Size = UDim2.new(1,0,1,0);
|
||||
Image = ''; --Http.GetThumbnailUrlForAsset(assetId);
|
||||
BackgroundTransparency = 1;
|
||||
ZIndex = 5;
|
||||
Parent = PackageClipper;
|
||||
};
|
||||
if confirmDetails and confirmDetails.ProductImage then
|
||||
PackageImage.Image = confirmDetails.ProductImage
|
||||
else
|
||||
PackageBackground.Visible = false
|
||||
end
|
||||
|
||||
|
||||
local DetailsContainer = Utility.Create'Frame'
|
||||
{
|
||||
Name = "DetailsContainer";
|
||||
Size = UDim2.new(DETAILS_CONTAINER_WIDTH/CONTENT_WIDTH, 0, DETAILS_CONTAINER_HEIGHT/CONTENT_HEIGHT, 0);
|
||||
Position = UDim2.new(PACKAGE_CONTAINER_WIDTH/CONTENT_WIDTH,0,0,0);
|
||||
BackgroundTransparency = 1;
|
||||
BorderSizePixel = 0;
|
||||
ZIndex = 4;
|
||||
Parent = ContentContainer;
|
||||
}
|
||||
local ConfirmTitle = Utility.Create'TextLabel'
|
||||
{
|
||||
Name = 'ConfirmTitle';
|
||||
Text = Strings:LocalizedString('ConfirmPurchaseTitle');
|
||||
Position = UDim2.new(0, 0, 0, 66);
|
||||
Size = UDim2.new(1,0,0,25);
|
||||
TextXAlignment = 'Left';
|
||||
TextColor3 = GlobalSettings.WhiteTextColor;
|
||||
Font = GlobalSettings.HeadingFont;
|
||||
FontSize = GlobalSettings.HeaderSize;
|
||||
BackgroundTransparency = 1;
|
||||
ZIndex = 4;
|
||||
Parent = DetailsContainer;
|
||||
};
|
||||
|
||||
local formattedPackageCost = "";
|
||||
if confirmDetails.Cost then
|
||||
if type(confirmDetails.Cost) == 'string' then
|
||||
formattedPackageCost = confirmDetails.Cost
|
||||
else
|
||||
formattedPackageCost = (confirmDetails.CurrencySymbol or '') .. Utility.FormatNumberString(confirmDetails.Cost)
|
||||
end
|
||||
end
|
||||
|
||||
local RobuxIcon = Utility.Create'ImageLabel'
|
||||
{
|
||||
Name = 'RobuxIcon';
|
||||
Position = UDim2.new(0,0,0,125);
|
||||
Size = UDim2.new(0,(properties.ShowRobuxIcon == true) and 50 or 0,0,50);
|
||||
BackgroundTransparency = 1;
|
||||
ZIndex = 4;
|
||||
Parent = DetailsContainer;
|
||||
};
|
||||
if confirmDetails.Currency == "ROBUX" then
|
||||
AssetManager.LocalImage(RobuxIcon, 'rbxasset://textures/ui/Shell/Icons/ROBUXIcon', {['720'] = UDim2.new(0,28,0,28); ['1080'] = UDim2.new(0,42,0,42);})
|
||||
end
|
||||
local PackageCost = Utility.Create'TextLabel'
|
||||
{
|
||||
Name = 'PackageCost';
|
||||
Text = formattedPackageCost;
|
||||
Size = UDim2.new(0,0,1,0);
|
||||
Position = UDim2.new(1.3,0,0,0);
|
||||
TextXAlignment = 'Left';
|
||||
TextColor3 = GlobalSettings.GreenTextColor;
|
||||
Font = GlobalSettings.RegularFont;
|
||||
FontSize = GlobalSettings.HeaderSize;
|
||||
BackgroundTransparency = 1;
|
||||
ZIndex = 4;
|
||||
Parent = RobuxIcon;
|
||||
};
|
||||
|
||||
if confirmDetails and confirmDetails.Cost and confirmDetails.Cost == 0 then
|
||||
PackageCost.Text = Strings:LocalizedString('FreeWord'):upper()
|
||||
end
|
||||
|
||||
|
||||
local areYouSurePhrase;
|
||||
if confirmDetails.Cost and confirmDetails.Cost == 0 then
|
||||
areYouSurePhrase = string.format(Strings:LocalizedString('AreYouSureTakePhrase'), tostring(productName))
|
||||
elseif properties.ConfirmWithPrice then
|
||||
areYouSurePhrase = string.format(Strings:LocalizedString('AreYouSureWithPricePhrase'), tostring(productName), tostring(formattedPackageCost))
|
||||
else
|
||||
areYouSurePhrase = string.format(Strings:LocalizedString('AreYouSurePhrase'), tostring(productName))
|
||||
end
|
||||
|
||||
local ConfirmItemDetail = Utility.Create'TextLabel'
|
||||
{
|
||||
Name = 'ConfirmItemDetail';
|
||||
Text = areYouSurePhrase;
|
||||
Position = UDim2.new(0, 0, 0, 205);
|
||||
Size = UDim2.new(1,0,0,25);
|
||||
TextXAlignment = 'Left';
|
||||
TextColor3 = GlobalSettings.WhiteTextColor;
|
||||
Font = GlobalSettings.LightFont;
|
||||
FontSize = GlobalSettings.TitleSize;
|
||||
BackgroundTransparency = 1;
|
||||
ZIndex = 4;
|
||||
Parent = DetailsContainer;
|
||||
};
|
||||
|
||||
local RemaningBalance = Utility.Create'TextLabel'
|
||||
{
|
||||
Name = 'RemaningBalance';
|
||||
Text = '';
|
||||
Position = UDim2.new(0, 0, 0, 285);
|
||||
Size = UDim2.new(1,0,0,25);
|
||||
TextXAlignment = 'Left';
|
||||
TextColor3 = GlobalSettings.WhiteTextColor;
|
||||
Font = GlobalSettings.LightFont;
|
||||
FontSize = GlobalSettings.TitleSize;
|
||||
BackgroundTransparency = 1;
|
||||
ZIndex = 4;
|
||||
Visible = properties.ShowRemainingBalance == true;
|
||||
Parent = DetailsContainer;
|
||||
};
|
||||
|
||||
|
||||
|
||||
local ConfirmButton = Utility.Create'ImageButton'
|
||||
{
|
||||
Name = "ConfirmButton";
|
||||
Size = UDim2.new(BUY_BUTTON_WIDTH/DETAILS_CONTAINER_WIDTH, 0, BUY_BUTTON_HEIGHT/DETAILS_CONTAINER_HEIGHT, 0);
|
||||
BorderSizePixel = 0;
|
||||
BackgroundColor3 = GlobalSettings.BlueButtonColor;
|
||||
BackgroundTransparency = 0;
|
||||
ZIndex = 4;
|
||||
Parent = DetailsContainer;
|
||||
}
|
||||
Utility.CalculateAnchor(ConfirmButton, UDim2.new(0, 0, 1 + BUY_BUTTON_OFFSET.Y/DETAILS_CONTAINER_HEIGHT, 0), Utility.Enum.Anchor.BottomLeft)
|
||||
local ConfirmText = Utility.Create'TextLabel'
|
||||
{
|
||||
Name = 'ConfirmText';
|
||||
Text = Strings:LocalizedString('ConfirmWord'):upper();
|
||||
Size = UDim2.new(1,0,1,0);
|
||||
TextColor3 = GlobalSettings.TextSelectedColor;
|
||||
Font = GlobalSettings.HeadingFont;
|
||||
FontSize = GlobalSettings.ButtonSize;
|
||||
BackgroundTransparency = 1;
|
||||
ZIndex = 4;
|
||||
Parent = ConfirmButton;
|
||||
};
|
||||
|
||||
|
||||
local function SetResult(value)
|
||||
Result = value
|
||||
while #OnResultCallbacks > 0 do
|
||||
local callback = table.remove(OnResultCallbacks, #OnResultCallbacks)
|
||||
callback(Result)
|
||||
end
|
||||
end
|
||||
|
||||
local function Decline()
|
||||
if this == ScreenManager:GetTopScreen() then
|
||||
SetResult(false)
|
||||
ScreenManager:CloseCurrent()
|
||||
end
|
||||
end
|
||||
|
||||
local function Confirm()
|
||||
if this == ScreenManager:GetTopScreen() then
|
||||
SetResult(true)
|
||||
ScreenManager:CloseCurrent()
|
||||
end
|
||||
end
|
||||
|
||||
function this:ResultAsync()
|
||||
if Result then
|
||||
return Result
|
||||
end
|
||||
ResultEvent:wait()
|
||||
return Result
|
||||
end
|
||||
|
||||
function this:AddResultCallback(callback)
|
||||
if Result ~= nil then
|
||||
callback(Result)
|
||||
else
|
||||
table.insert(OnResultCallbacks, callback)
|
||||
end
|
||||
end
|
||||
|
||||
function this:FadeInBackground()
|
||||
Utility.PropertyTweener(ConfirmPrompt, "BackgroundTransparency", 1, GlobalSettings.ModalBackgroundTransparency, 0.25, Utility.EaseInOutQuad, true)
|
||||
end
|
||||
|
||||
function this:GetDefaultSelectableObject()
|
||||
return ConfirmButton
|
||||
end
|
||||
|
||||
local currencyWidget = nil
|
||||
local RobuxChangedConn = nil
|
||||
function this:Show()
|
||||
ConfirmPrompt.Visible = true
|
||||
ConfirmPrompt.Parent = MyParent
|
||||
|
||||
if self.BackgroundTween then
|
||||
self.BackgroundTween:Cancel()
|
||||
end
|
||||
self.BackgroundTween = Utility.PropertyTweener(ConfirmPrompt, "BackgroundTransparency", 1, GlobalSettings.ModalBackgroundTransparency, 0, Utility.EaseInOutQuad, nil)
|
||||
SoundManager:Play('OverlayOpen')
|
||||
|
||||
local function onPackageBackgroundResize()
|
||||
local rawImageSize = Vector2.new(420, 420)
|
||||
if confirmDetails.ProductImageSize then
|
||||
rawImageSize = confirmDetails.ProductImageSize
|
||||
end
|
||||
PackageImage.Size = Utility.CalculateFill(PackageBackground, rawImageSize)
|
||||
Utility.CalculateAnchor(PackageImage, UDim2.new(0.5,0,0.5,0), Utility.Enum.Anchor.Center)
|
||||
end
|
||||
|
||||
self.PackageBackgroundChangedConn = Utility.DisconnectEvent(self.PackageBackgroundChangedConn)
|
||||
self.PackageBackgroundChangedConn = PackageBackground.Changed:connect(function(prop)
|
||||
if prop == 'AbsoluteSize' then
|
||||
onPackageBackgroundResize()
|
||||
end
|
||||
end)
|
||||
onPackageBackgroundResize()
|
||||
|
||||
local function onBalanceLoaded(newBalance)
|
||||
local balance = newBalance
|
||||
if properties.ShowRemainingBalance then
|
||||
if balance and confirmDetails and confirmDetails.Cost then
|
||||
if confirmDetails.Cost and confirmDetails.Cost > 0 then
|
||||
local newBalance = balance and confirmDetails.Cost and balance - confirmDetails.Cost
|
||||
RemaningBalance.Text = string.format(Strings:LocalizedString('RemainingBalancePhrase'), Utility.FormatNumberString(tostring(newBalance)));
|
||||
else
|
||||
RemaningBalance.Text = ''
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if confirmDetails.Balance then
|
||||
onBalanceLoaded(confirmDetails.Balance)
|
||||
else
|
||||
spawn(function()
|
||||
local balance = UserDataModule.GetPlatformUserBalanceAsync()
|
||||
onBalanceLoaded(balance)
|
||||
end)
|
||||
end
|
||||
|
||||
if not currencyWidget then
|
||||
currencyWidget = CurrencyWidgetModule({Parent = ConfirmPrompt; Position = UDim2.new(0.052, 0, 0.88, 0); ZIndex = 4;})
|
||||
end
|
||||
Utility.DisconnectEvent(RobuxChangedConn)
|
||||
RobuxChangedConn = currencyWidget.RobuxChanged:connect(onBalanceLoaded)
|
||||
|
||||
end
|
||||
|
||||
function this:Hide()
|
||||
ConfirmPrompt.Visible = false
|
||||
ConfirmPrompt.Parent = nil
|
||||
|
||||
if self.BackgroundTween then
|
||||
self.BackgroundTween:Cancel()
|
||||
end
|
||||
self.BackgroundTween = nil
|
||||
|
||||
self.PackageBackgroundChangedConn = Utility.DisconnectEvent(self.PackageBackgroundChangedConn)
|
||||
RobuxChangedConn = Utility.DisconnectEvent(RobuxChangedConn)
|
||||
end
|
||||
|
||||
function this:ScreenRemoved()
|
||||
if currencyWidget then
|
||||
currencyWidget:Destroy()
|
||||
currencyWidget = nil
|
||||
end
|
||||
if Result == nil then
|
||||
SetResult(false)
|
||||
end
|
||||
ResultEvent:fire(Result)
|
||||
end
|
||||
|
||||
function this:Focus()
|
||||
InFocus = true
|
||||
ContextActionService:BindCoreAction("ReturnFromCurrentConfirmScreen",
|
||||
function(actionName, inputState, inputObject)
|
||||
if inputState == Enum.UserInputState.End then
|
||||
Decline()
|
||||
end
|
||||
end,
|
||||
false,
|
||||
Enum.KeyCode.ButtonB)
|
||||
|
||||
local isConfirmingPurchase = false
|
||||
self.ConfirmButtonConn = Utility.DisconnectEvent(self.ConfirmButtonConn)
|
||||
self.ConfirmButtonConn = ConfirmButton.MouseButton1Click:connect(function()
|
||||
if isConfirmingPurchase then return end
|
||||
isConfirmingPurchase = true
|
||||
SoundManager:Play('ButtonPress')
|
||||
Confirm()
|
||||
isConfirmingPurchase = false
|
||||
end)
|
||||
|
||||
GuiService:AddSelectionParent("ConfirmOptionsSelectionGroup", ContentContainer)
|
||||
-- spawn(function()
|
||||
-- wait(DELAY_BEFORE_PURCHASE) -- Stop quick a-button smashing from purchasing items
|
||||
if InFocus and GuiService.SelectedCoreObject == nil then
|
||||
GuiService.SelectedCoreObject = self:GetDefaultSelectableObject()
|
||||
end
|
||||
-- end)
|
||||
end
|
||||
|
||||
function this:RemoveFocus()
|
||||
ContextActionService:UnbindCoreAction("ReturnFromCurrentConfirmScreen")
|
||||
GuiService:RemoveSelectionGroup("ConfirmOptionsSelectionGroup")
|
||||
self.ConfirmButtonConn = Utility.DisconnectEvent(self.ConfirmButtonConn)
|
||||
GuiService.SelectedCoreObject = nil
|
||||
InFocus = false
|
||||
end
|
||||
|
||||
|
||||
function this:SetParent(parent)
|
||||
MyParent = parent
|
||||
ConfirmPrompt.Parent = MyParent
|
||||
end
|
||||
|
||||
|
||||
return this
|
||||
end
|
||||
|
||||
return CreateConfirmPrompt
|
||||
@@ -0,0 +1,131 @@
|
||||
--[[
|
||||
// ControllerStateManager.lua
|
||||
|
||||
// Handles controller state changes
|
||||
]]
|
||||
local CoreGui = Game:GetService("CoreGui")
|
||||
local GuiRoot = CoreGui:FindFirstChild("RobloxGui")
|
||||
local Modules = GuiRoot:FindFirstChild("Modules")
|
||||
local PlatformService = nil
|
||||
pcall(function() PlatformService = game:GetService('PlatformService') end)
|
||||
local UserInputService = game:GetService('UserInputService')
|
||||
|
||||
local EventHub = require(Modules:FindFirstChild('EventHub'))
|
||||
local NoActionOverlay = require(Modules:FindFirstChild('NoActionOverlay'))
|
||||
local ScreenManager = require(Modules:FindFirstChild('ScreenManager'))
|
||||
local Strings = require(Modules:FindFirstChild('LocalizedStrings'))
|
||||
local UserData = require(Modules:FindFirstChild('UserData'))
|
||||
local Utility = require(Modules:FindFirstChild('Utility'))
|
||||
|
||||
local ControllerStateManager = {}
|
||||
|
||||
local LostUserGamepadCn = nil
|
||||
local LostActiveUserCn = nil
|
||||
local GainedUserGamepadCn = nil
|
||||
local GainedActiveUserCn = nil
|
||||
local DisconnectCn = nil
|
||||
|
||||
local currentOverlay = nil
|
||||
|
||||
local DATAMODEL_TYPE = {
|
||||
APP_SHELL = 0;
|
||||
GAME = 1;
|
||||
}
|
||||
|
||||
local function closeOverlay(dataModelType)
|
||||
if dataModelType == DATAMODEL_TYPE.GAME then
|
||||
UserInputService.OverrideMouseIconBehavior = Enum.OverrideMouseIconBehavior.None
|
||||
|
||||
currentOverlay:Hide()
|
||||
currentOverlay = nil
|
||||
else
|
||||
ScreenManager:CloseCurrent()
|
||||
end
|
||||
end
|
||||
|
||||
local function showErrorOverlay(titleName, bodyName, userDisplayName, dataModelType)
|
||||
-- create error
|
||||
local err = { Title = Strings:LocalizedString(titleName),
|
||||
Msg = string.format(Strings:LocalizedString(bodyName), userDisplayName) }
|
||||
local noActionOverlay = NoActionOverlay(err)
|
||||
if dataModelType == DATAMODEL_TYPE.GAME then
|
||||
UserInputService.OverrideMouseIconBehavior = Enum.OverrideMouseIconBehavior.ForceHide
|
||||
|
||||
currentOverlay = noActionOverlay
|
||||
noActionOverlay:Show()
|
||||
else
|
||||
ScreenManager:OpenScreen(noActionOverlay, false)
|
||||
end
|
||||
end
|
||||
|
||||
local function onLostActiveUser(userDisplayName, dataModelType)
|
||||
showErrorOverlay("ActiveUserLostConnectionTitle", "ActiveUserLostConnectionPhrase", userDisplayName, dataModelType)
|
||||
end
|
||||
|
||||
local function onGainedActiveUser(dataModelType)
|
||||
closeOverlay(dataModelType)
|
||||
end
|
||||
|
||||
local function onLostUserGamepad(userDisplayName, dataModelType)
|
||||
showErrorOverlay("ControllerLostConnectionTitle", "ControllerLostConnectionPhrase", userDisplayName, dataModelType)
|
||||
end
|
||||
|
||||
local function onGainedUserGamepad(dataModelType)
|
||||
closeOverlay(dataModelType)
|
||||
end
|
||||
|
||||
local function disconnectEvents()
|
||||
LostActiveUserCn = Utility.DisconnectEvent(LostActiveUserCn)
|
||||
GainedActiveUserCn = Utility.DisconnectEvent(GainedActiveUserCn)
|
||||
|
||||
LostUserGamepadCn = Utility.DisconnectEvent(LostUserGamepadCn)
|
||||
GainedUserGamepadCn = Utility.DisconnectEvent(GainedUserGamepadCn)
|
||||
end
|
||||
|
||||
function ControllerStateManager:Initialize()
|
||||
if not PlatformService then return end
|
||||
|
||||
local dataModelType = PlatformService.DatamodelType
|
||||
|
||||
disconnectEvents()
|
||||
LostUserGamepadCn = PlatformService.LostUserGamepad:connect(function(userDisplayName)
|
||||
onLostUserGamepad(userDisplayName, dataModelType)
|
||||
end)
|
||||
GainedUserGamepadCn = PlatformService.GainedUserGamepad:connect(function(userDisplayName)
|
||||
onGainedUserGamepad(dataModelType)
|
||||
end)
|
||||
|
||||
LostActiveUserCn = PlatformService.LostActiveUser:connect(function(userDisplayName)
|
||||
onLostActiveUser(userDisplayName, dataModelType)
|
||||
end)
|
||||
GainedActiveUserCn = PlatformService.GainedActiveUser:connect(function(userDisplayName)
|
||||
onGainedActiveUser(dataModelType)
|
||||
end)
|
||||
|
||||
-- disconnect based on DataModel type
|
||||
if dataModelType == DATAMODEL_TYPE.GAME then
|
||||
DisconnectCn = PlatformService.ViewChanged:connect(function(viewType)
|
||||
if viewType == 0 then
|
||||
disconnectEvents()
|
||||
end
|
||||
end)
|
||||
else
|
||||
DisconnectCn = PlatformService.UserAccountChanged:connect(function(reauthenticationReason)
|
||||
disconnectEvents()
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
function ControllerStateManager:CheckUserConnected()
|
||||
if not PlatformService then return end
|
||||
|
||||
local isGamepadConnected = UserInputService:GetGamepadConnected(Enum.UserInputType.Gamepad1)
|
||||
local dataModelType = PlatformService.DatamodelType
|
||||
if not isGamepadConnected then
|
||||
local userInfo = PlatformService:GetPlatformUserInfo()
|
||||
local userDisplayName = userInfo["Gamertag"] or ""
|
||||
onLostUserGamepad(userDisplayName, dataModelType)
|
||||
end
|
||||
end
|
||||
|
||||
return ControllerStateManager
|
||||
@@ -0,0 +1,257 @@
|
||||
--[[
|
||||
// CurrencyWidget.lua by Kip Turner
|
||||
--]]
|
||||
|
||||
local TextService = game:GetService('TextService')
|
||||
local PlatformService;
|
||||
pcall(function() PlatformService = game:GetService('PlatformService') end)
|
||||
|
||||
|
||||
local CoreGui = game:GetService("CoreGui")
|
||||
local GuiRoot = CoreGui:FindFirstChild("RobloxGui")
|
||||
local Modules = GuiRoot:FindFirstChild("Modules")
|
||||
|
||||
local UserDataModule = require(Modules:FindFirstChild('UserData'))
|
||||
local GlobalSettings = require(Modules:FindFirstChild('GlobalSettings'))
|
||||
local Utility = require(Modules:FindFirstChild('Utility'))
|
||||
local Strings = require(Modules:FindFirstChild('LocalizedStrings'))
|
||||
local LoadingWidget = require(Modules:FindFirstChild('LoadingWidget'))
|
||||
local EventHub = require(Modules:FindFirstChild('EventHub'))
|
||||
|
||||
local EventHubConnectCount = 0
|
||||
|
||||
|
||||
local InternalPlatformRobuxAmountChangedSignal = Utility.Signal()
|
||||
local InternalTotalRobuxAmountChangedSignal = Utility.Signal()
|
||||
|
||||
|
||||
local function CreateCurrencyWidget(properties)
|
||||
properties = properties or {}
|
||||
|
||||
local this = {}
|
||||
|
||||
this.RobuxChanged = Utility.Signal()
|
||||
|
||||
local internalPlatformRobuxChangedConn = nil
|
||||
local internalTotalRobuxChangedConn = nil
|
||||
|
||||
local CachedTotalRobuxValue = nil
|
||||
local CachedRobuxValue = nil
|
||||
local destroyed = false
|
||||
|
||||
EventHubConnectCount = EventHubConnectCount + 1
|
||||
local myEventId = "CurrencyWidget" .. tostring(EventHubConnectCount)
|
||||
|
||||
local RobuxBalanceTitle = Utility.Create'TextLabel'
|
||||
{
|
||||
Name = 'RobuxBalanceTitle';
|
||||
Size = UDim2.new(0,0,0,0);
|
||||
Position = properties.Position or UDim2.new(0, 0, 0, 0);
|
||||
TextXAlignment = 'Left';
|
||||
TextYAlignment = 'Top';
|
||||
TextColor3 = GlobalSettings.WhiteTextColor;
|
||||
Text = Strings:LocalizedString('RobuxBalanceTitle') .. ':';
|
||||
Font = GlobalSettings.RegularFont;
|
||||
FontSize = GlobalSettings.HeaderSize;
|
||||
BackgroundTransparency = 1;
|
||||
ZIndex = properties.ZIndex or 2;
|
||||
Parent = properties.Parent or nil;
|
||||
};
|
||||
local robuxTitleSize = TextService:GetTextSize(RobuxBalanceTitle.Text, Utility.ConvertFontSizeEnumToInt(RobuxBalanceTitle.FontSize), RobuxBalanceTitle.Font, Vector2.new())
|
||||
RobuxBalanceTitle.Size = UDim2.new(0, robuxTitleSize.X, 0, robuxTitleSize.Y)
|
||||
|
||||
local RobuxBalanceIcon = Utility.Create'ImageLabel'
|
||||
{
|
||||
Name = 'RobuxIcon';
|
||||
Position = UDim2.new(1,10,0,0);
|
||||
Size = UDim2.new(0,46,0,46);
|
||||
BackgroundTransparency = 1;
|
||||
Image = 'rbxasset://textures/ui/Shell/Icons/ROBUXIconOutlined@1080.png';
|
||||
ZIndex = properties.ZIndex or 2;
|
||||
Parent = RobuxBalanceTitle;
|
||||
};
|
||||
Utility.CalculateAnchor(RobuxBalanceIcon, UDim2.new(1,17,0.5,0), Utility.Enum.Anchor.CenterLeft)
|
||||
local RobuxBalanceValue = Utility.Create'TextLabel'
|
||||
{
|
||||
Name = 'RobuxBalanceValue';
|
||||
Size = UDim2.new(0,0,1,0);
|
||||
Position = UDim2.new(1,5,0,-2);
|
||||
Text = '';
|
||||
TextXAlignment = 'Left';
|
||||
TextYAlignment = 'Center';
|
||||
TextColor3 = GlobalSettings.GreenTextColor;
|
||||
Font = GlobalSettings.RegularFont;
|
||||
FontSize = GlobalSettings.HeaderSize;
|
||||
BackgroundTransparency = 1;
|
||||
ZIndex = properties.ZIndex or 2;
|
||||
TextTransparency = 1;
|
||||
Parent = RobuxBalanceIcon;
|
||||
};
|
||||
|
||||
|
||||
local function UpdateBalanceText()
|
||||
local balanceValueString = CachedRobuxValue and Utility.FormatNumberString(tostring(CachedRobuxValue)) or '-'
|
||||
local balanceStringWidth = TextService:GetTextSize(balanceValueString, Utility.ConvertFontSizeEnumToInt(RobuxBalanceValue.FontSize), RobuxBalanceValue.Font, Vector2.new())
|
||||
RobuxBalanceValue.Size = UDim2.new(0, balanceStringWidth.X, 1, 0)
|
||||
RobuxBalanceValue.Text = balanceValueString
|
||||
if RobuxBalanceValue.TextTransparency == 1 and CachedRobuxValue ~= nil then
|
||||
Utility.PropertyTweener(RobuxBalanceValue, 'TextTransparency', 1, 0, 0.5, Utility.EaseOutQuad, true)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local robuxAmountChangedLoader = nil
|
||||
local RobuxChangedConn = nil
|
||||
if PlatformService then
|
||||
local robuxChangedEventCount = 0
|
||||
|
||||
local function fetchNewRobuxAsync(thisEventCount)
|
||||
if not (CachedRobuxValue and CachedTotalRobuxValue) then
|
||||
this:RefreshRobuxAmountAsync()
|
||||
end
|
||||
local prepurchaseRobux = CachedRobuxValue
|
||||
local prepurchaseTotalRobux = CachedTotalRobuxValue
|
||||
Utility.ExponentialRepeat(
|
||||
function() return thisEventCount == robuxChangedEventCount and not destroyed end,
|
||||
function()
|
||||
local balance = UserDataModule.GetPlatformUserBalanceAsync()
|
||||
local totalBalance = UserDataModule.GetTotalUserBalanceAsync()
|
||||
if balance and totalBalance and not destroyed then
|
||||
if balance ~= prepurchaseRobux and totalBalance ~= prepurchaseTotalRobux then
|
||||
CachedRobuxValue = balance
|
||||
CachedTotalRobuxValue = totalBalance
|
||||
UpdateBalanceText()
|
||||
if this.RobuxChanged ~= nil then
|
||||
this.RobuxChanged:fire(balance)
|
||||
end
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
)
|
||||
end
|
||||
|
||||
local function OnRobuxAmountChanged()
|
||||
robuxChangedEventCount = robuxChangedEventCount + 1
|
||||
local thisEventCount = robuxChangedEventCount
|
||||
|
||||
if robuxAmountChangedLoader then
|
||||
robuxAmountChangedLoader:Cleanup()
|
||||
robuxAmountChangedLoader = nil
|
||||
end
|
||||
local loader = LoadingWidget({Parent = RobuxBalanceValue, Size = UDim2.new(0,50,0,50), Position = UDim2.new(1,75,0,25)}, {function() fetchNewRobuxAsync(thisEventCount) end})
|
||||
robuxAmountChangedLoader = loader
|
||||
robuxAmountChangedLoader:AwaitFinished()
|
||||
if robuxAmountChangedLoader and loader == robuxAmountChangedLoader then
|
||||
robuxAmountChangedLoader:Cleanup()
|
||||
robuxAmountChangedLoader = nil
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
Utility.DisconnectEvent(RobuxChangedConn)
|
||||
RobuxChangedConn = PlatformService.RobuxAmountChanged:connect(function(platformPurchaseResult)
|
||||
if platformPurchaseResult == 3 then
|
||||
OnRobuxAmountChanged()
|
||||
else
|
||||
if robuxAmountChangedLoader then
|
||||
robuxAmountChangedLoader:Cleanup()
|
||||
robuxAmountChangedLoader = nil
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
EventHub:addEventListener(EventHub.Notifications["RobuxCatalogPurchaseInitiated"], myEventId, function()
|
||||
-- print("CurrencyWidget: on robux amoutn changed")
|
||||
OnRobuxAmountChanged()
|
||||
end)
|
||||
end
|
||||
|
||||
function this:GetAbsoluteSize()
|
||||
return (RobuxBalanceValue.AbsolutePosition + RobuxBalanceValue.AbsoluteSize) - (RobuxBalanceTitle.AbsolutePosition)
|
||||
end
|
||||
|
||||
function this:GetRobuxAmount()
|
||||
return CachedRobuxValue
|
||||
end
|
||||
|
||||
function this:RefreshRobuxAmountAsync()
|
||||
local beforeTotalRobuxValue = CachedTotalRobuxValue
|
||||
local beforeRobuxValue = CachedRobuxValue
|
||||
|
||||
UserDataModule.GetLocalUserIdAsync()
|
||||
CachedTotalRobuxValue = UserDataModule.GetTotalUserBalanceAsync()
|
||||
CachedRobuxValue = UserDataModule.GetPlatformUserBalanceAsync()
|
||||
UpdateBalanceText()
|
||||
|
||||
if beforeTotalRobuxValue ~= CachedTotalRobuxValue then
|
||||
InternalTotalRobuxAmountChangedSignal:fire(CachedTotalRobuxValue)
|
||||
end
|
||||
if beforeRobuxValue ~= CachedRobuxValue then
|
||||
InternalPlatformRobuxAmountChangedSignal:fire(CachedRobuxValue)
|
||||
end
|
||||
end
|
||||
|
||||
local GetRobuxAmountAsyncTempWidget = nil
|
||||
function this:GetRobuxAmountAsync()
|
||||
if CachedRobuxValue then
|
||||
return CachedRobuxValue
|
||||
end
|
||||
UserDataModule.GetLocalUserIdAsync()
|
||||
spawn(function()
|
||||
wait(1)
|
||||
if CachedRobuxValue == nil and GetRobuxAmountAsyncTempWidget == nil then
|
||||
GetRobuxAmountAsyncTempWidget = LoadingWidget({Parent = RobuxBalanceValue, Size = UDim2.new(0,50,0,50), Position = UDim2.new(1,75,0,25)}, {function() while CachedRobuxValue == nil do wait() end end})
|
||||
GetRobuxAmountAsyncTempWidget:AwaitFinished()
|
||||
if GetRobuxAmountAsyncTempWidget then
|
||||
GetRobuxAmountAsyncTempWidget:Cleanup()
|
||||
end
|
||||
GetRobuxAmountAsyncTempWidget = nil
|
||||
end
|
||||
end)
|
||||
self:RefreshRobuxAmountAsync()
|
||||
return CachedRobuxValue
|
||||
end
|
||||
|
||||
function this:Destroy()
|
||||
destroyed = true
|
||||
RobuxBalanceTitle.Parent = nil
|
||||
self.RobuxChanged = nil
|
||||
if robuxAmountChangedLoader then
|
||||
robuxAmountChangedLoader:Cleanup()
|
||||
robuxAmountChangedLoader = nil
|
||||
end
|
||||
RobuxChangedConn = Utility.DisconnectEvent(RobuxChangedConn)
|
||||
internalPlatformRobuxChangedConn = Utility.DisconnectEvent(internalPlatformRobuxChangedConn)
|
||||
internalTotalRobuxChangedConn = Utility.DisconnectEvent(internalTotalRobuxChangedConn)
|
||||
EventHub:removeEventListener(EventHub.Notifications["RobuxCatalogPurchaseInitiated"], myEventId)
|
||||
end
|
||||
|
||||
function this:GetGuiObject()
|
||||
return RobuxBalanceTitle
|
||||
end
|
||||
|
||||
internalPlatformRobuxChangedConn = InternalPlatformRobuxAmountChangedSignal:connect(
|
||||
function(newPlatformRobux)
|
||||
if newPlatformRobux ~= CachedRobuxValue then
|
||||
CachedRobuxValue = newPlatformRobux
|
||||
UpdateBalanceText()
|
||||
this.RobuxChanged:fire(CachedRobuxValue)
|
||||
end
|
||||
end)
|
||||
internalTotalRobuxChangedConn = InternalTotalRobuxAmountChangedSignal:connect(
|
||||
function(newTotalRobux)
|
||||
if newTotalRobux ~= CachedTotalRobuxValue then
|
||||
CachedTotalRobuxValue = newTotalRobux
|
||||
this.RobuxChanged:fire(CachedRobuxValue)
|
||||
end
|
||||
end)
|
||||
|
||||
spawn(function()
|
||||
this:GetRobuxAmountAsync()
|
||||
end)
|
||||
|
||||
return this
|
||||
end
|
||||
|
||||
return CreateCurrencyWidget
|
||||
@@ -0,0 +1,187 @@
|
||||
-- Written by Kip Turner, Copyright ROBLOX 2015
|
||||
|
||||
local CoreGui = Game:GetService("CoreGui")
|
||||
local GuiService = game:GetService('GuiService')
|
||||
local PlayersService = game:GetService("Players")
|
||||
local ContextActionService = game:GetService("ContextActionService")
|
||||
local UserInputService = game:GetService("UserInputService")
|
||||
|
||||
local GuiRoot = CoreGui:FindFirstChild("RobloxGui")
|
||||
local Modules = GuiRoot:FindFirstChild("Modules")
|
||||
|
||||
local AccountManager = require(Modules:FindFirstChild('AccountManager'))
|
||||
local Utility = require(Modules:FindFirstChild('Utility'))
|
||||
local GlobalSettings = require(Modules:FindFirstChild('GlobalSettings'))
|
||||
local Errors = require(Modules:FindFirstChild('Errors'))
|
||||
local ErrorOverlay = require(Modules:FindFirstChild('ErrorOverlay'))
|
||||
local Strings = require(Modules:FindFirstChild('LocalizedStrings'))
|
||||
local AssetManager = require(Modules:FindFirstChild('AssetManager'))
|
||||
local ScreenManager = require(Modules:FindFirstChild('ScreenManager'))
|
||||
local SetAccountCredentialsScreen = require(Modules:FindFirstChild('SetAccountCredentialsScreen'))
|
||||
local SignInScreen = require(Modules:FindFirstChild('SignInScreen'))
|
||||
local LoadingWidget = require(Modules:FindFirstChild('LoadingWidget'))
|
||||
|
||||
local SoundManager = require(Modules:FindFirstChild('SoundManager'))
|
||||
local EventHub = require(Modules:FindFirstChild('EventHub'))
|
||||
|
||||
local ANY_KEY_CODES =
|
||||
{
|
||||
[Enum.KeyCode.ButtonA] = true;
|
||||
-- [Enum.KeyCode.ButtonB] = true;
|
||||
[Enum.KeyCode.ButtonX] = true;
|
||||
[Enum.KeyCode.ButtonY] = true;
|
||||
[Enum.KeyCode.ButtonStart] = true;
|
||||
[Enum.KeyCode.ButtonSelect] = true;
|
||||
[Enum.KeyCode.ButtonL1] = true;
|
||||
[Enum.KeyCode.ButtonR1] = true;
|
||||
}
|
||||
|
||||
local GAMEPAD_INPUT_TYPES =
|
||||
{
|
||||
[Enum.UserInputType.Gamepad1] = true;
|
||||
[Enum.UserInputType.Gamepad2] = true;
|
||||
[Enum.UserInputType.Gamepad3] = true;
|
||||
[Enum.UserInputType.Gamepad4] = true;
|
||||
}
|
||||
|
||||
local function CreateHomePane(parent)
|
||||
local this = {}
|
||||
|
||||
local AnyButtonBeganConnection, AnyButtonEndedConnection = nil
|
||||
|
||||
local EngagementScreenContainer = Utility.Create'Frame'
|
||||
{
|
||||
Name = 'EngagementScreen';
|
||||
Size = UDim2.new(1, 0, 1, 0);
|
||||
BackgroundTransparency = 1;
|
||||
Parent = parent;
|
||||
}
|
||||
|
||||
local RobloxLogo = Utility.Create'ImageLabel'
|
||||
{
|
||||
Name = 'RobloxLogo';
|
||||
BackgroundTransparency = 1;
|
||||
Size = UDim2.new(0, 594, 0, 199);
|
||||
Image = 'rbxasset://textures/ui/Shell/Icons/ROBLOXSplashLogo.png';
|
||||
Parent = EngagementScreenContainer;
|
||||
}
|
||||
Utility.CalculateAnchor(RobloxLogo, UDim2.new(0.5,0,0.5,0), Utility.Enum.Anchor.Center)
|
||||
|
||||
local AnyButtonHint = Utility.Create'TextLabel'
|
||||
{
|
||||
Name = 'AnyButtonHint';
|
||||
Text = Strings:LocalizedString('EngagementScreenHint');
|
||||
TextColor3 = GlobalSettings.WhiteTextColor;
|
||||
Font = GlobalSettings.RegularFont;
|
||||
FontSize = GlobalSettings.ButtonSize;
|
||||
Size = UDim2.new(0,0,0,0);
|
||||
BackgroundTransparency = 1;
|
||||
Parent = RobloxLogo;
|
||||
};
|
||||
Utility.CalculateAnchor(AnyButtonHint, UDim2.new(0.5,0,0,415), Utility.Enum.Anchor.Center)
|
||||
|
||||
local function beginAuthenticationAsync(gamePad)
|
||||
local authResult = nil
|
||||
local hasRobloxCredentialsResult = nil
|
||||
local function auth()
|
||||
authResult = AccountManager:BeginAuthenticationAsync(gamePad)
|
||||
if not authResult then
|
||||
print("beginAuthenticationAsync() failed because", result)
|
||||
authResult = AccountManager.AuthResults.Error
|
||||
end
|
||||
|
||||
if authResult == AccountManager.AuthResults.Success then
|
||||
hasRobloxCredentialsResult = AccountManager:HasRobloxCredentialsAsync()
|
||||
end
|
||||
end
|
||||
|
||||
local loader = LoadingWidget(
|
||||
{ Parent = RobloxLogo, Position = UDim2.new(0.5, 0, 0, 415) }, { auth })
|
||||
loader:AwaitFinished()
|
||||
loader:Cleanup()
|
||||
loader = nil
|
||||
|
||||
print("beginAuthenticationAsync has completed with code:", authResult)
|
||||
if authResult == AccountManager.AuthResults.Success then
|
||||
if hasRobloxCredentialsResult == AccountManager.AuthResults.Success then
|
||||
EventHub:dispatchEvent(EventHub.Notifications["AuthenticationSuccess"])
|
||||
elseif hasRobloxCredentialsResult == AccountManager.AuthResults.UsernamePasswordNotSet then
|
||||
local setAccountCredentialsScreen = SetAccountCredentialsScreen(Strings:LocalizedString("SetCredentialsTitle"),
|
||||
Strings:LocalizedString("SetCredentialsPhrase"), Strings:LocalizedString("SetCredentialsWord"))
|
||||
setAccountCredentialsScreen:SetParent(EngagementScreenContainer.Parent)
|
||||
ScreenManager:OpenScreen(setAccountCredentialsScreen, true)
|
||||
else
|
||||
local err = Errors.Authentication[hasRobloxCredentialsResult] or Errors.Default
|
||||
ScreenManager:OpenScreen(ErrorOverlay(err), false)
|
||||
end
|
||||
elseif authResult == AccountManager.AuthResults.AccountUnlinked then
|
||||
local signInScreen = SignInScreen()
|
||||
signInScreen:SetParent(EngagementScreenContainer.Parent)
|
||||
ScreenManager:OpenScreen(signInScreen, true)
|
||||
else
|
||||
local err = Errors.Authentication[authResult] or Errors.Default
|
||||
ScreenManager:OpenScreen(ErrorOverlay(err), false)
|
||||
end
|
||||
end
|
||||
|
||||
local function onAnyButtonPressed(gamePad)
|
||||
AnyButtonBeganConnection = Utility.DisconnectEvent(AnyButtonBeganConnection)
|
||||
AnyButtonEndedConnection = Utility.DisconnectEvent(AnyButtonEndedConnection)
|
||||
AnyButtonHint.TextColor3 = GlobalSettings.WhiteTextColor
|
||||
Utility.PropertyTweener(AnyButtonHint, 'TextTransparency', 0, 1, 0.25, Utility.EaseOutQuad, true,
|
||||
function()
|
||||
beginAuthenticationAsync(gamePad)
|
||||
end)
|
||||
end
|
||||
|
||||
function this:Show()
|
||||
EngagementScreenContainer.Visible = true
|
||||
end
|
||||
|
||||
function this:Hide()
|
||||
EngagementScreenContainer.Visible = false
|
||||
end
|
||||
|
||||
function this:Focus()
|
||||
AnyButtonHint.TextColor3 = GlobalSettings.WhiteTextColor
|
||||
AnyButtonHint.TextTransparency = 0
|
||||
|
||||
Utility.DisconnectEvent(AnyButtonBeganConnection)
|
||||
local anyButtonDown = {}
|
||||
AnyButtonBeganConnection = UserInputService.InputBegan:connect(function(inputObject)
|
||||
if GAMEPAD_INPUT_TYPES[inputObject.UserInputType] then
|
||||
if ANY_KEY_CODES[inputObject.KeyCode] then
|
||||
AnyButtonHint.TextColor3 = GlobalSettings.GreyTextColor
|
||||
anyButtonDown[inputObject.KeyCode] = true
|
||||
end
|
||||
end
|
||||
end)
|
||||
Utility.DisconnectEvent(AnyButtonEndedConnection)
|
||||
local isAuthenticating = false
|
||||
AnyButtonEndedConnection = UserInputService.InputEnded:connect(function(inputObject)
|
||||
if isAuthenticating then return end
|
||||
isAuthenticating = true
|
||||
if GAMEPAD_INPUT_TYPES[inputObject.UserInputType] then
|
||||
if ANY_KEY_CODES[inputObject.KeyCode] and anyButtonDown[inputObject.KeyCode] == true then
|
||||
SoundManager:Play('ButtonPress')
|
||||
onAnyButtonPressed(inputObject.UserInputType)
|
||||
end
|
||||
end
|
||||
isAuthenticating = false
|
||||
anyButtonDown[inputObject.KeyCode] = false
|
||||
end)
|
||||
end
|
||||
|
||||
function this:RemoveFocus()
|
||||
AnyButtonBeganConnection = Utility.DisconnectEvent(AnyButtonBeganConnection)
|
||||
AnyButtonEndedConnection = Utility.DisconnectEvent(AnyButtonEndedConnection)
|
||||
end
|
||||
|
||||
function this:SetParent(newParent)
|
||||
EngagementScreenContainer.Parent = newParent
|
||||
end
|
||||
|
||||
return this
|
||||
end
|
||||
|
||||
return CreateHomePane
|
||||
@@ -0,0 +1,119 @@
|
||||
--[[
|
||||
// ErrorOverlay.lau
|
||||
|
||||
// Creates and error overlay
|
||||
|
||||
// NOTE: Right now error and alerts look the same, so we're
|
||||
// using the same module to make both. If in the future this
|
||||
// changes, we'll need to move alert to it's own module.
|
||||
]]
|
||||
local CoreGui = Game:GetService("CoreGui")
|
||||
local GuiRoot = CoreGui:FindFirstChild("RobloxGui")
|
||||
local Modules = GuiRoot:FindFirstChild("Modules")
|
||||
local GuiService = game:GetService('GuiService')
|
||||
|
||||
local AssetManager = require(Modules:FindFirstChild('AssetManager'))
|
||||
local GlobalSettings = require(Modules:FindFirstChild('GlobalSettings'))
|
||||
local Utility = require(Modules:FindFirstChild('Utility'))
|
||||
local Strings = require(Modules:FindFirstChild('LocalizedStrings'))
|
||||
local BaseOverlay = require(Modules:FindFirstChild('BaseOverlay'))
|
||||
local ScreenManager = require(Modules:FindFirstChild('ScreenManager'))
|
||||
local SoundManager = require(Modules:FindFirstChild('SoundManager'))
|
||||
|
||||
local createErrorOverlay = function(errorType, isAlert)
|
||||
if not errorType then
|
||||
return
|
||||
end
|
||||
|
||||
local this = BaseOverlay()
|
||||
|
||||
local title = errorType.Title
|
||||
local message = errorType.Msg
|
||||
local errorCode = errorType.Code
|
||||
|
||||
local iconImage = Utility.Create'ImageLabel'
|
||||
{
|
||||
Name = "IconImage";
|
||||
BackgroundTransparency = 1;
|
||||
ZIndex = this.BaseZIndex;
|
||||
}
|
||||
iconImage.Image = isAlert and 'rbxasset://textures/ui/Shell/Icons/AlertIcon.png' or
|
||||
'rbxasset://textures/ui/Shell/Icons/ErrorIconLargeCopy@1080.png'
|
||||
iconImage.Size = isAlert and UDim2.new(0, 416, 0, 416) or UDim2.new(0, 321, 0, 264)
|
||||
Utility.CalculateAnchor(iconImage, UDim2.new(0.5, 0, 0.5, 0), Utility.Enum.Anchor.Center)
|
||||
this:SetImage(iconImage)
|
||||
|
||||
local titleText = Utility.Create'TextLabel'
|
||||
{
|
||||
Name = "TitleText";
|
||||
Size = UDim2.new(0, 0, 0, 0);
|
||||
Position = UDim2.new(0, this.RightAlign, 0, 136);
|
||||
BackgroundTransparency = 1;
|
||||
Font = GlobalSettings.RegularFont;
|
||||
FontSize = GlobalSettings.HeaderSize;
|
||||
TextColor3 = GlobalSettings.WhiteTextColor;
|
||||
Text = title;
|
||||
TextXAlignment = Enum.TextXAlignment.Left;
|
||||
ZIndex = this.BaseZIndex;
|
||||
Parent = this.Container;
|
||||
}
|
||||
|
||||
local descriptionText = Utility.Create'TextLabel'
|
||||
{
|
||||
Name = "DescriptionText";
|
||||
Size = UDim2.new(0, 762, 0, 304);
|
||||
Position = UDim2.new(0, this.RightAlign, 0, titleText.Position.Y.Offset + 62);
|
||||
BackgroundTransparency = 1;
|
||||
TextXAlignment = Enum.TextXAlignment.Left;
|
||||
TextYAlignment = Enum.TextYAlignment.Top;
|
||||
Font = GlobalSettings.LightFont;
|
||||
FontSize = GlobalSettings.TitleSize;
|
||||
TextColor3 = GlobalSettings.WhiteTextColor;
|
||||
TextWrapped = true;
|
||||
Text = message;
|
||||
ZIndex = this.BaseZIndex;
|
||||
Parent = this.Container;
|
||||
}
|
||||
if errorCode and not isAlert then
|
||||
descriptionText.Text = string.format(Strings:LocalizedString('ErrorMessageAndCodePrase'), message, errorCode)
|
||||
end
|
||||
|
||||
local okButton = Utility.Create'TextButton'
|
||||
{
|
||||
Name = "OkButton";
|
||||
Size = UDim2.new(0, 320, 0, 66);
|
||||
Position = UDim2.new(0, this.RightAlign, 1, -100 - 66);
|
||||
BorderSizePixel = 0;
|
||||
BackgroundColor3 = GlobalSettings.BlueButtonColor;
|
||||
Font = GlobalSettings.RegularFont;
|
||||
FontSize = GlobalSettings.ButtonSize;
|
||||
TextColor3 = GlobalSettings.TextSelectedColor;
|
||||
Text = string.upper(Strings:LocalizedString("OkWord"));
|
||||
ZIndex = this.BaseZIndex;
|
||||
Parent = this.Container;
|
||||
|
||||
SoundManager:CreateSound('MoveSelection');
|
||||
}
|
||||
|
||||
function this:GetPriority()
|
||||
return GlobalSettings.ElevatedPriority
|
||||
end
|
||||
|
||||
--[[ Input Events ]]--
|
||||
okButton.MouseButton1Click:connect(function()
|
||||
this:Close()
|
||||
end)
|
||||
local baseFocus = this.Focus
|
||||
function this:Focus()
|
||||
baseFocus(this)
|
||||
GuiService.SelectedCoreObject = okButton
|
||||
end
|
||||
|
||||
function this:GetOverlaySound()
|
||||
return 'Error'
|
||||
end
|
||||
|
||||
return this
|
||||
end
|
||||
|
||||
return createErrorOverlay
|
||||
@@ -0,0 +1,122 @@
|
||||
--[[
|
||||
// Errors.lua
|
||||
|
||||
// Global error codes
|
||||
]]
|
||||
local CoreGui = Game:GetService("CoreGui")
|
||||
local GuiRoot = CoreGui:FindFirstChild("RobloxGui")
|
||||
local Modules = GuiRoot:FindFirstChild("Modules")
|
||||
|
||||
local Strings = require(Modules:FindFirstChild('LocalizedStrings'))
|
||||
|
||||
local Errors =
|
||||
{
|
||||
Default = { Title = Strings:LocalizedString("ErrorOccurredTitle"), Msg = Strings:LocalizedString("DefaultErrorPhrase"), Code = 0 };
|
||||
|
||||
GameJoin =
|
||||
{
|
||||
-- index mapped to error code returned from c++
|
||||
{ Title = Strings:LocalizedString("UnableToJoinTitle"), Msg = Strings:LocalizedString("AlreadyRunningPhrase"), Code = 101 };
|
||||
{ Title = Strings:LocalizedString("UnableToJoinTitle"), Msg = Strings:LocalizedString("WebServerConnectFailPhrase"), Code = 102 };
|
||||
{ Title = Strings:LocalizedString("UnableToJoinTitle"), Msg = Strings:LocalizedString("AccessDeniedByWeb"), Code = 103 };
|
||||
{ Title = Strings:LocalizedString("UnableToJoinTitle"), Msg = Strings:LocalizedString("InstanceNotFound"), Code = 104 };
|
||||
{ Title = Strings:LocalizedString("UnableToJoinTitle"), Msg = Strings:LocalizedString("GameFullPhrase"), Code = 105 };
|
||||
{ Title = Strings:LocalizedString("UnableToJoinTitle"), Msg = Strings:LocalizedString("FollowUserFailed"), Code = 106 };
|
||||
{ Title = Strings:LocalizedString("UnableToJoinTitle"), Msg = Strings:LocalizedString("DefaultJoinFailPhrase"), Code = 107 };
|
||||
};
|
||||
|
||||
Vote =
|
||||
{
|
||||
FloodCheckThresholdMet = { Title = Strings:LocalizedString("CannotVoteTitle"), Msg = Strings:LocalizedString("VoteFloodPhrase"), Code = 201 };
|
||||
PlayGame = { Title = Strings:LocalizedString("CannotVoteTitle"), Msg = Strings:LocalizedString("VotePlayGamePhrase"), Code = 202 };
|
||||
};
|
||||
|
||||
Favorite =
|
||||
{
|
||||
Failed = { Title = Strings:LocalizedString("CannotFavoriteTitle"), Msg = Strings:LocalizedString("DefaultErrorPhrase"), Code = 301 };
|
||||
FloodCheck = { Title = Strings:LocalizedString("CannotFavoriteTitle"), Msg = Strings:LocalizedString("FavoriteFloodPhrase"), Code = 302 };
|
||||
};
|
||||
|
||||
Test =
|
||||
{
|
||||
CannotJoinGame = { Title = "An Error Occured", Msg = "Cannot join games from studio.", Code = 401 };
|
||||
StillInDev = { Title = "An Error Occured", Msg = "This feature is still in development.", Code = 402 };
|
||||
FeatureNotAvailableInStudio = { Title = "An Error Occured", Msg = "This feature is not available in Roblox Studio.", Code = 403 };
|
||||
};
|
||||
|
||||
PackageEquip =
|
||||
{
|
||||
Default = { Title = Strings:LocalizedString("UnableToEquipTitle"), Msg = Strings:LocalizedString("UnableToEquipPhrase"), Code = 501 };
|
||||
};
|
||||
|
||||
OutfitEquip =
|
||||
{
|
||||
Default = { Title = Strings:LocalizedString("UnableToWearOufitTitle"), Msg = Strings:LocalizedString("UnableToWearOufitPhrase"), Code = 601 };
|
||||
};
|
||||
|
||||
PackagePurchase =
|
||||
{
|
||||
{ Title = Strings:LocalizedString("UnableToDoPurchaseTitle"), Msg = Strings:LocalizedString("UnableToDoPurchasePhrase"), Code = 701 };
|
||||
};
|
||||
|
||||
RobuxPurchase =
|
||||
{
|
||||
{ Title = Strings:LocalizedString("UnableToDoRobuxPurchaseTitle"), Msg = Strings:LocalizedString("UnableToDoRobuxPurchasePhrase"), Code = 801 };
|
||||
};
|
||||
|
||||
Authentication =
|
||||
{
|
||||
-- index mapped to int error code from c++
|
||||
[-1] = { Title = Strings:LocalizedString("AuthenticationErrorTitle"), Msg = Strings:LocalizedString("AuthErrorPhrase"), Code = 901 };
|
||||
-- ["0"]; This is success
|
||||
[1] = { Title = Strings:LocalizedString("AuthenticationErrorTitle"), Msg = Strings:LocalizedString("AuthInProgressPhrase"), Code = 902 };
|
||||
[2] = { Title = Strings:LocalizedString("AuthenticationErrorTitle"), Msg = Strings:LocalizedString("AuthAccountUnlinkedPhrase"), Code = 903 };
|
||||
[3] = { Title = Strings:LocalizedString("AuthenticationErrorTitle"), Msg = Strings:LocalizedString("AuthMissingGamePadPhrase"), Code = 904 };
|
||||
[4] = { Title = Strings:LocalizedString("AuthenticationErrorTitle"), Msg = Strings:LocalizedString("AuthNoUserDetectedPhrase"), Code = 905 };
|
||||
[5] = { Title = Strings:LocalizedString("AuthenticationErrorTitle"), Msg = Strings:LocalizedString("AuthHttpErrorDetected"), Code = 906 };
|
||||
[6] = { Title = Strings:LocalizedString("ErrorOccurredTitle"), Msg = Strings:LocalizedString("LinkSignUpDisabled"), Code = 907 };
|
||||
[7] = { Title = Strings:LocalizedString("ErrorOccurredTitle"), Msg = Strings:LocalizedString("LinkFlooded"), Code = 908 };
|
||||
[8] = { Title = Strings:LocalizedString("ErrorOccurredTitle"), Msg = Strings:LocalizedString("LinkLeaseLocked"), Code = 909 };
|
||||
[9] = { Title = Strings:LocalizedString("ErrorOccurredTitle"), Msg = Strings:LocalizedString("LinkAccountLinkingDisabled"), Code = 910 };
|
||||
[10] = { Title = Strings:LocalizedString("ErrorOccurredTitle"), Msg = Strings:LocalizedString("LinkInvalidRobloxUser"), Code = 911 };
|
||||
[11] = { Title = Strings:LocalizedString("ErrorOccurredTitle"), Msg = Strings:LocalizedString("LinkRobloxUserAlreadyLinked"), Code = 912 };
|
||||
[12] = { Title = Strings:LocalizedString("ErrorOccurredTitle"), Msg = Strings:LocalizedString("LinkXboxUserAlreadyLinked"), Code = 913 };
|
||||
[13] = { Title = Strings:LocalizedString("ErrorOccurredTitle"), Msg = Strings:LocalizedString("LinkIllegalChildAccountLinking"), Code = 914 };
|
||||
[14] = { Title = Strings:LocalizedString("ErrorOccurredTitle"), Msg = Strings:LocalizedString("LinkInvalidPassword"), Code = 915 };
|
||||
[15] = { Title = Strings:LocalizedString("ErrorOccurredTitle"), Msg = Strings:LocalizedString("LinkUsernamePasswordNotSet"), Code = 916 };
|
||||
[16] = { Title = Strings:LocalizedString("ErrorOccurredTitle"), Msg = Strings:LocalizedString("LinkUsernameAlreadyTaken"), Code = 917 };
|
||||
[17] = { Title = Strings:LocalizedString("ErrorOccurredTitle"), Msg = Strings:LocalizedString("LinkInvalidCredentials"), Code = 918 };
|
||||
};
|
||||
|
||||
Reauthentication =
|
||||
{
|
||||
-- index mapped to int error code from c++, you must index into this with a string
|
||||
[0] = { Title = Strings:LocalizedString("ErrorOccurredTitle"), Msg = Strings:LocalizedString("ReauthUnknownPhrase"), Code = 1001 };
|
||||
[1] = { Title = Strings:LocalizedString("ReauthSignedOutTitle"), Msg = Strings:LocalizedString("ReauthSignedOutPhrase"), Code = 1002 };
|
||||
[2] = { Title = Strings:LocalizedString("ReauthRemovedTitle"), Msg = Strings:LocalizedString("ReauthRemovedPhrase"), Code = 1003 };
|
||||
[3] = { Title = Strings:LocalizedString("ReauthSignedOutTitle"), Msg = Strings:LocalizedString("ReauthInvalidSessionPhrase"), Code = 1004 };
|
||||
[4] = { Title = Strings:LocalizedString("ReauthUnlinkTitle"), Msg = Strings:LocalizedString("ReauthUnlinkPhrase"), Code = 1005 };
|
||||
[5] = { Title = Strings:LocalizedString("ReauthRemovedTitle"), Msg = Strings:LocalizedString("ReauthRemovedPhrase"), Code = 1006 };
|
||||
[6] = { Title = Strings:LocalizedString("ReauthRemovedTitle"), Msg = Strings:LocalizedString("ReauthRemovedPhrase"), Code = 1007 };
|
||||
[7] = { Title = Strings:LocalizedString("ReauthRemovedTitle"), Msg = Strings:LocalizedString("ReauthRemovedPhrase"), Code = 1008 };
|
||||
};
|
||||
|
||||
SignIn =
|
||||
{
|
||||
["Invalid Username"] = { Title = Strings:LocalizedString("InvalidUsernameTitle"), Msg = Strings:LocalizedString("InvalidUsernamePhrase"), Code = 1101 };
|
||||
InvalidPassword = { Title = Strings:LocalizedString("InvalidPasswordTitle"), Msg = Strings:LocalizedString("InvalidPasswordPhrase"), Code = 1102 };
|
||||
["Already Taken"] = { Title = Strings:LocalizedString("AlreadyTakenTitle"), Msg = Strings:LocalizedString("AlreadyTakenPhrase"), Code = 1103 };
|
||||
["Invalid Characters Used"] = { Title = Strings:LocalizedString("InvalidUsernameTitle"), Msg = Strings:LocalizedString("InvalidCharactersUsedPhrase"), Code = 1104 };
|
||||
["Username Cannot Contain Spaces"] = { Title = Strings:LocalizedString("InvalidUsernameTitle"), Msg = Strings:LocalizedString("UsernameCannotContainSpacesPhrase"), Code = 1105 };
|
||||
NoUsernameEntered = { Title = Strings:LocalizedString("ErrorOccurredTitle"), Msg = Strings:LocalizedString("NoUsernameEnteredPhrase"), Code = 1106 };
|
||||
NoUsernameOrPasswordEntered = { Title = Strings:LocalizedString("ErrorOccurredTitle"), Msg = Strings:LocalizedString("NoUsernameOrPasswordEnteredPhrase"), Code = 1107 };
|
||||
["ConnectionFailed"] = { Title = Strings:LocalizedString("AuthenticationErrorTitle"), Msg = Strings:LocalizedString("WebServerConnectFailPhrase"), Code = 1108 };
|
||||
};
|
||||
|
||||
PlatformError =
|
||||
{
|
||||
PopupPartyUI = { Title = Strings:LocalizedString("ErrorOccurredTitle"), Msg = Strings:LocalizedString("PopupPartyUIErrorPhrase"), Code = 1201 };
|
||||
};
|
||||
}
|
||||
|
||||
return Errors
|
||||
@@ -0,0 +1,174 @@
|
||||
-- Written by Kyler Mulherin, Copyright ROBLOX 2015
|
||||
local listeners = {}
|
||||
--listeners is a table that holds arrays of listener objects
|
||||
--Ex - listeners["login"] = { Listener , Listener, Listener }
|
||||
|
||||
local function createListener(idString, callbackFunction)
|
||||
local Listener = { id = idString , callback = callbackFunction };
|
||||
return Listener;
|
||||
end
|
||||
|
||||
--Initialize all the functions for the EventHub
|
||||
local EventHub = {}
|
||||
do
|
||||
function EventHub:addEventListener(eventString, objectIDString, callbackFunction)
|
||||
--print ('Adding Listener with ID : ' .. objectIDString)
|
||||
if (listeners[eventString] == nil) then
|
||||
listeners[eventString] = {}
|
||||
end
|
||||
|
||||
table.insert(listeners[eventString], createListener(objectIDString, callbackFunction))
|
||||
end
|
||||
function EventHub:removeEventListener(eventString, objectIDString)
|
||||
--print ('Removing Listener with ID : ' .. objectIDString)
|
||||
if (listeners[eventString] == nil) then return end
|
||||
|
||||
--iterate through the listeners for an event string, remove all of the listeners with the provided objectIDString
|
||||
for key, value in ipairs(listeners[eventString]) do
|
||||
local listener = value
|
||||
if (listener ~= nil) then
|
||||
if (listener.id == objectIDString) then
|
||||
--print ('-Removing listener with id : ' .. listener.id)
|
||||
table.remove(listeners[eventString], key)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
function EventHub:removeCallbackFromEvent(eventString, objectIDString, callbackFunction)
|
||||
--NOTE- Will not work with anonymous functions
|
||||
--print ('Removing Listener with ID : ' .. objectIDString)
|
||||
if (listeners[eventString] == nil) then return end
|
||||
|
||||
--iterate through the listeners for an event string, remove the one with the provided objectIDString and callback function
|
||||
for key, value in ipairs(listeners[eventString]) do
|
||||
local listener = value
|
||||
if (listener ~= nil) then
|
||||
if (listener.id == objectIDString) and (listener.callback == callbackFunction) then
|
||||
table.remove(listeners[eventString], key)
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
function EventHub:dispatchEvent(eventString, ...)
|
||||
--print ('Dispatching Event : ' .. eventString .. ' with data : ' .. tostring(data))
|
||||
if (listeners[eventString] == nil) then
|
||||
return
|
||||
end
|
||||
|
||||
--loop through all the listeners and call the callback function
|
||||
for key, value in ipairs(listeners[eventString]) do
|
||||
value.callback(...)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
--A comprehensive list of notification strings to read from
|
||||
EventHub.Notifications = {
|
||||
-- Authentication
|
||||
AuthenticationSuccess = "rbxNotificationAuthenticationSuccess";
|
||||
GameJoin = "rbxNotificationGameJoin";
|
||||
|
||||
-- Game Notifications
|
||||
OpenGames = "rbxNotificationOpenGames";
|
||||
OpenGameDetail = "rbxNotificationOpenGameDetail";
|
||||
OpenGameGenre = "rbxNotificationOpenGameGenre";
|
||||
OpenBadgeScreen = "rbxNotificationOpenBadgeScreen";
|
||||
|
||||
-- Unlink Account Notification
|
||||
UnlinkAccountConfirmation = "rbxNotificationUnlinkAccountConfirmation";
|
||||
|
||||
-- Overscan Notifications
|
||||
OpenOverscanScreen = "rbxNotificationOpenOverscanScreen";
|
||||
|
||||
-- Engagement Screen Notifcations
|
||||
OpenEngagementScreen = "rbxNotificationOpenEngagementScreen";
|
||||
|
||||
--Social Notifications
|
||||
OpenSocialScreen = "rbxNotificationOpenSocialScreen";
|
||||
|
||||
--Settings Notifications
|
||||
OpenSettingsScreen = "rbxNotificationOpenSettingsScreen";
|
||||
|
||||
--Avatar Screen Notifications
|
||||
NavigateToEquippedAvatar = "rbxNotificationNavigateToEquippedAvatar";
|
||||
|
||||
-- Robux Screen Notification
|
||||
NavigateToRobuxScreen = "rbxNotificationNavigateToRobuxScreen";
|
||||
RobuxCatalogPurchaseInitiated = "rbxRobuxCatalogPurchaseInitiated";
|
||||
--
|
||||
|
||||
-- Achievement Related Events
|
||||
TestXButtonPressed = "rbxTestXButtonPressed";
|
||||
DonnedDifferentPackage = "rbxDonnedDifferentPackage";
|
||||
VotedOnPlace = "rbxVotedOnPlace";
|
||||
|
||||
-- Hero Stats Related Events
|
||||
AvatarEquipped = "rbxAvatarEquipped";
|
||||
-- JoinedParty = "rbxJoinedParty";
|
||||
|
||||
AvatarEquipBegin = "rbxAvatarEquipBegin";
|
||||
|
||||
DonnedDifferentOutfit = "rbxDonnedDifferentOutfit";
|
||||
};
|
||||
|
||||
end
|
||||
|
||||
|
||||
return EventHub;
|
||||
--print 'Event Hub initialized'
|
||||
|
||||
|
||||
-- [[ TESTING STUFF - include somewhere the EventHub has been initialized ]]--
|
||||
--[[
|
||||
-- TEST #1 -- removing entire listeners from events
|
||||
print '\nAdding Event Listeners...';
|
||||
EventHub:addEventListener( "test0", "testID1", function (data) print('Test 1 Received test0 : ' .. data) end);
|
||||
EventHub:addEventListener( "test0", "testID2", function (data) print('Test 2 Received test0 : ' .. data) end);
|
||||
EventHub:addEventListener( "test0", "testID3", function (data) print('Test 3 Received test0 : ' .. data) end);
|
||||
EventHub:addEventListener( "test0", "testID4", function (data) print('Test 4 Received test0 : ' .. data) end);
|
||||
|
||||
print '\nDispatching Events...';
|
||||
EventHub:dispatchEvent("test0", "var");
|
||||
--SHOULD SEE ALL 4 LISTENERS REPORT
|
||||
|
||||
print '\nDispatching Events...';
|
||||
EventHub:dispatchEvent("test0", "var2");
|
||||
|
||||
print '\nRemoving TestID2 and 3s Listeners...';
|
||||
EventHub:removeEventListener("test0", "testID2");
|
||||
EventHub:removeEventListener("test0", "testID3");
|
||||
|
||||
print '\nDispatching Events...';
|
||||
EventHub:dispatchEvent("test0", "var3");
|
||||
--SHOULD ONLY SEE testID1 AND testID4 REPORT
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
-- TEST #2 -- selective removal of listeners
|
||||
print '\nAdding Generic Function Event Listeners...';
|
||||
local function test1(data) print('Generic function 1. Received test1 : ' .. data) end;
|
||||
local function test2(data) print('Generic function 2. Received test1 : ' .. data) end;
|
||||
local testFunc1 = test1;
|
||||
local testFunc2 = test2;
|
||||
|
||||
EventHub:addEventListener( "test1", "testID1", testFunc1);
|
||||
EventHub:addEventListener( "test1", "testID1", testFunc2);
|
||||
EventHub:addEventListener( "test1", "testID2", testFunc1);
|
||||
|
||||
print '\nDispatching Events...';
|
||||
EventHub:dispatchEvent("test1", "foo");
|
||||
--SHOULD SEE 3 REPORTS
|
||||
|
||||
print '\nRemoving Generic Function 1 Callback of testID1';
|
||||
EventHub:removeCallbackFromEvent("test1", "testID1", testFunc1);
|
||||
|
||||
print '\nDispatching Events...';
|
||||
EventHub:dispatchEvent("test1", "foo3");
|
||||
--SHOULD SEE THAT testID1 NO LONGER REPORTS FROM GENERIC FUNCTION 1
|
||||
|
||||
|
||||
]]--
|
||||
@@ -0,0 +1,157 @@
|
||||
--[[
|
||||
// FriendPresenceItem.lua
|
||||
// Creates a friend activity gui item to be used with a ScrollingGrid
|
||||
// for friends social status
|
||||
]]
|
||||
local CoreGui = Game:GetService("CoreGui")
|
||||
local GuiRoot = CoreGui:FindFirstChild("RobloxGui")
|
||||
local Modules = GuiRoot:FindFirstChild("Modules")
|
||||
|
||||
local AssetManager = require(Modules:FindFirstChild('AssetManager'))
|
||||
local GlobalSettings = require(Modules:FindFirstChild('GlobalSettings'))
|
||||
local Utility = require(Modules:FindFirstChild('Utility'))
|
||||
local ThumbnailLoader = require(Modules:FindFirstChild('ThumbnailLoader'))
|
||||
local SoundManager = require(Modules:FindFirstChild('SoundManager'))
|
||||
|
||||
local FAIL_IMG = 'rbxasset://textures/ui/Shell/Icons/DefaultProfileIcon.png'
|
||||
|
||||
local function FriendPresenceItem(size, idStr)
|
||||
local this = {}
|
||||
|
||||
local TEXT_OFFSET = 12
|
||||
|
||||
local container = Utility.Create'ImageButton'
|
||||
{
|
||||
Name = idStr;
|
||||
Size = size;
|
||||
Position = UDim2.new(0, 0, 0, 0);
|
||||
BackgroundColor3 = GlobalSettings.WhiteTextColor;
|
||||
BorderSizePixel = 0;
|
||||
BackgroundTransparency = 1;
|
||||
AutoButtonColor = false;
|
||||
|
||||
SoundManager:CreateSound('MoveSelection');
|
||||
}
|
||||
container.SelectionGained:connect(function()
|
||||
local startTween = Utility.PropertyTweener(container, "BackgroundTransparency", 1, GlobalSettings.AvatarBoxBackgroundSelectedTransparency, 0,
|
||||
Utility.EaseInOutQuad, true, nil)
|
||||
end)
|
||||
container.SelectionLost:connect(function()
|
||||
container.BackgroundTransparency = 1
|
||||
end)
|
||||
local avatarImage = Utility.Create'ImageLabel'
|
||||
{
|
||||
Name = "AvatarImage";
|
||||
Image = '';
|
||||
Size = UDim2.new(0, 104, 0, 104);
|
||||
Position = UDim2.new(0, 0, 0, 0);
|
||||
BackgroundTransparency = 0;
|
||||
BorderSizePixel = 0;
|
||||
BackgroundColor3 = GlobalSettings.CharacterBackgroundColor;
|
||||
ZIndex = 2;
|
||||
Parent = container;
|
||||
AssetManager.CreateShadow(1);
|
||||
}
|
||||
local nameLabel = Utility.Create'TextLabel'
|
||||
{
|
||||
Name = "NameLabel";
|
||||
Text = "";
|
||||
Size = UDim2.new(0, 0, 0, 0);
|
||||
Position = UDim2.new(0, avatarImage.Size.X.Offset + TEXT_OFFSET, 0, 32);
|
||||
TextXAlignment = Enum.TextXAlignment.Left;
|
||||
TextColor3 = GlobalSettings.WhiteTextColor;
|
||||
Font = GlobalSettings.RegularFont;
|
||||
FontSize = GlobalSettings.TitleSize;
|
||||
BackgroundTransparency = 1;
|
||||
Parent = container;
|
||||
}
|
||||
local presenceStatusImage = Utility.Create'ImageLabel'
|
||||
{
|
||||
Name = "PresenceStatusImage";
|
||||
BackgroundTransparency = 1;
|
||||
Parent = container;
|
||||
}
|
||||
AssetManager.LocalImage(presenceStatusImage,
|
||||
'rbxasset://textures/ui/Shell/Icons/OnlineStatusIcon', {['720'] = UDim2.new(0,13,0,13); ['1080'] = UDim2.new(0,19,0,20);})
|
||||
presenceStatusImage.Position = UDim2.new(0, nameLabel.Position.X.Offset, 0,
|
||||
nameLabel.Position.Y.Offset + 36 - (presenceStatusImage.Size.Y.Offset/2) + 1)
|
||||
|
||||
local presenceLabel = Utility.Create'TextLabel'
|
||||
{
|
||||
Name = "PresenceLabel";
|
||||
Text = "";
|
||||
Size = UDim2.new(0,
|
||||
container.Size.X.Offset - presenceStatusImage.Position.X.Offset - presenceStatusImage.Size.X.Offset - 12, 0, 32);
|
||||
Position = UDim2.new(0, presenceStatusImage.Position.X.Offset + presenceStatusImage.Size.X.Offset + 12, 0,
|
||||
presenceStatusImage.Position.Y.Offset + presenceStatusImage.Size.Y.Offset / 2 - 16);
|
||||
TextXAlignment = Enum.TextXAlignment.Left;
|
||||
TextColor3 = GlobalSettings.LightGreyTextColor;
|
||||
Font = GlobalSettings.RegularFont;
|
||||
FontSize = GlobalSettings.DescriptionSize;
|
||||
BackgroundTransparency = 1;
|
||||
ClipsDescendants = true;
|
||||
Parent = container;
|
||||
}
|
||||
local lineBreak = Utility.Create'Frame'
|
||||
{
|
||||
Name = "Break";
|
||||
Size = UDim2.new(1, -avatarImage.Size.X.Offset - TEXT_OFFSET, 0, 2);
|
||||
Position = UDim2.new(0, avatarImage.Size.X.Offset + TEXT_OFFSET, 1, -2);
|
||||
BorderSizePixel = 0;
|
||||
BackgroundColor3 = GlobalSettings.LineBreakColor;
|
||||
Parent = container;
|
||||
}
|
||||
|
||||
local failImage = Utility.Create'ImageLabel'
|
||||
{
|
||||
Name = "FailImage";
|
||||
Size = UDim2.new(0.5, 0, 0.5, 0);
|
||||
Position = UDim2.new(0.25, 0, 0.25, 0);
|
||||
BackgroundTransparency = 1;
|
||||
Image = FAIL_IMG;
|
||||
ZIndex = 2;
|
||||
}
|
||||
|
||||
function this:GetContainer()
|
||||
return container
|
||||
end
|
||||
|
||||
function this:SetAvatarImage(userId)
|
||||
local loader = ThumbnailLoader:Create(avatarImage, userId,
|
||||
ThumbnailLoader.Sizes.Small, ThumbnailLoader.AssetType.Avatar)
|
||||
spawn(function()
|
||||
if not loader:LoadAsync(true, false) then
|
||||
failImage.Parent = avatarImage
|
||||
else
|
||||
failImage.Parent = nil
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
function this:GetNameText()
|
||||
return nameLabel.Text
|
||||
end
|
||||
|
||||
function this:SetNameText(name)
|
||||
nameLabel.Text = name
|
||||
end
|
||||
|
||||
function this:SetPresence(str, isInRobloxGame)
|
||||
presenceLabel.Text = str
|
||||
presenceStatusImage.ImageColor3 = isInRobloxGame and GlobalSettings.GreenTextColor
|
||||
or GlobalSettings.GreySelectedButtonColor
|
||||
end
|
||||
|
||||
function this:SetLastActivityText(str)
|
||||
lastActivityLabel.Text = str
|
||||
end
|
||||
|
||||
function this:Destroy()
|
||||
container:Destroy()
|
||||
this = nil
|
||||
end
|
||||
|
||||
return this
|
||||
end
|
||||
|
||||
return FriendPresenceItem
|
||||
@@ -0,0 +1,286 @@
|
||||
--[[
|
||||
// FriendsData.lua
|
||||
|
||||
// Caches the current friends pagination to used by anyone in the app
|
||||
// polls every POLL_DELAY and gets the latest pagination
|
||||
|
||||
// TODO:
|
||||
Need polling to update friends. How are we going to handle all the cases
|
||||
like the person you're selecting going offline, etc..
|
||||
]]
|
||||
local CoreGui = game:GetService("CoreGui")
|
||||
local GuiRoot = CoreGui:FindFirstChild("RobloxGui")
|
||||
local Modules = GuiRoot:FindFirstChild("Modules")
|
||||
local Players = game:GetService('Players')
|
||||
local HttpService = game:GetService('HttpService')
|
||||
local PlatformService = nil
|
||||
pcall(function() PlatformService = game:GetService('PlatformService') end)
|
||||
|
||||
local Http = require(Modules:FindFirstChild('Http'))
|
||||
local Utility = require(Modules:FindFirstChild('Utility'))
|
||||
local EventHub = require(Modules:FindFirstChild('EventHub'))
|
||||
local UserData = require(Modules:FindFirstChild('UserData'))
|
||||
local SortData = require(Modules:FindFirstChild('SortData'))
|
||||
local Strings = require(Modules:FindFirstChild('LocalizedStrings'))
|
||||
|
||||
-- NOTE: This is just required for fixing Usernames in auto-generatd games
|
||||
local GameData = require(Modules:FindFirstChild('GameData'))
|
||||
local ConvertMyPlaceNameInXboxAppFlag = Utility.IsFastFlagEnabled("ConvertMyPlaceNameInXboxApp")
|
||||
|
||||
local FriendsData = {}
|
||||
|
||||
|
||||
local pollDelay = Utility.GetFastVariable("XboxFriendsPolling")
|
||||
if pollDelay then
|
||||
pollDelay = tonumber(pollDelay)
|
||||
end
|
||||
|
||||
local POLL_DELAY = pollDelay or 30
|
||||
local STATUS = {
|
||||
UNKNOWN = "Unknown";
|
||||
ONLINE = "Online";
|
||||
OFFLINE = "Offline";
|
||||
AWAY = "Away";
|
||||
}
|
||||
|
||||
local isOnlineFriendsPolling = false
|
||||
local myCurrentFriendsData = nil
|
||||
local updateEventCns = {}
|
||||
|
||||
local function filterXboxFriends(friendsData)
|
||||
local titleId = PlatformService and tostring(PlatformService:GetTitleId()) or ''
|
||||
local onlineRobloxFriendsData = {}
|
||||
local onlineRobloxFriendsUserIds = {}
|
||||
local onlineFriendsData = {}
|
||||
-- filter into two list, those online in roblox and those online
|
||||
-- for those online, also get their roblox userId
|
||||
for i = 1, #friendsData do
|
||||
local data = friendsData[i]
|
||||
if data["status"] and data["status"] == STATUS.ONLINE then
|
||||
if data["rich"] then
|
||||
-- get rich presence table, last entry is most recent activity from user
|
||||
local richTbl = data["rich"]
|
||||
richTbl = richTbl[#richTbl]
|
||||
if richTbl["titleId"] == titleId then
|
||||
table.insert(onlineRobloxFriendsData, data)
|
||||
table.insert(onlineRobloxFriendsUserIds, data["robloxuid"])
|
||||
else
|
||||
table.insert(onlineFriendsData, data)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- now get roblox friends presence in roblox
|
||||
local robloxPresence = {}
|
||||
if #onlineRobloxFriendsUserIds > 0 then
|
||||
local jsonTable = {}
|
||||
jsonTable["userIds"] = onlineRobloxFriendsUserIds
|
||||
local jsonPostBody = HttpService:JSONEncode(jsonTable)
|
||||
robloxPresence = Http.GetUsersOnlinePresenceAsync(jsonPostBody)
|
||||
if robloxPresence and robloxPresence["UserPresences"] then
|
||||
robloxPresence = robloxPresence["UserPresences"]
|
||||
end
|
||||
end
|
||||
|
||||
-- now append roblox presence data to each users data
|
||||
for i = 1, #onlineRobloxFriendsData do
|
||||
if robloxPresence[i] then
|
||||
local data = onlineRobloxFriendsData[i]
|
||||
-- make sure we have the right person
|
||||
for j = 1, #robloxPresence do
|
||||
local rbxPresenceData = robloxPresence[j]
|
||||
local rbxUserId = rbxPresenceData["VisitorId"]
|
||||
if rbxUserId == data["robloxuid"] then
|
||||
if rbxPresenceData["IsOnline"] == true then
|
||||
local placeId = rbxPresenceData["PlaceId"]
|
||||
local lastLocation = rbxPresenceData["LastLocation"]
|
||||
|
||||
-- If the lastLocation for a user is some user place with a GeneratedUsername in it
|
||||
-- then replace it with the actual creator name!
|
||||
if ConvertMyPlaceNameInXboxAppFlag and placeId and lastLocation and GameData:ExtractGeneratedUsername(lastLocation) then
|
||||
local gameCreator = GameData:GetGameCreatorAsync(placeId)
|
||||
if gameCreator then
|
||||
lastLocation = GameData:GetFilteredGameName(lastLocation, gameCreator)
|
||||
end
|
||||
end
|
||||
|
||||
-- If the user is not in a featured game, then hide their presence
|
||||
if placeId and not SortData:IsFeaturedGameAsync(placeId) then
|
||||
data["IsPrivateSession"] = true
|
||||
lastLocation = Strings:LocalizedString("PrivateSessionPhrase")
|
||||
end
|
||||
|
||||
data["PlaceId"] = placeId
|
||||
data["LastLocation"] = lastLocation
|
||||
end
|
||||
-- remove from list and gtfo
|
||||
table.remove(robloxPresence, j)
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- now sort those in roblox
|
||||
table.sort(onlineRobloxFriendsData, function(a, b)
|
||||
if a["PlaceId"] and b["PlaceId"] then
|
||||
return a["display"] < b["display"]
|
||||
end
|
||||
if a["PlaceId"] then
|
||||
return true
|
||||
end
|
||||
if b["PlaceId"] then
|
||||
return false
|
||||
end
|
||||
|
||||
return a["display"] < b["display"]
|
||||
end)
|
||||
|
||||
-- now sort all other friends
|
||||
table.sort(onlineFriendsData, function(a, b)
|
||||
return a["display"] < b["display"]
|
||||
end)
|
||||
|
||||
-- now concat tables
|
||||
for i = 1, #onlineFriendsData do
|
||||
onlineRobloxFriendsData[#onlineRobloxFriendsData + 1] = onlineFriendsData[i]
|
||||
end
|
||||
|
||||
return onlineRobloxFriendsData
|
||||
end
|
||||
|
||||
--[[
|
||||
// Returns table with an array with the key friends
|
||||
// Keys
|
||||
// xuid - number, xbox user id
|
||||
// gamertage - string, users gamertag
|
||||
// display - string, users display name (depends on user settings, could be same as gamertag)
|
||||
// robloxuid - number, users roblox userId
|
||||
// status - string (Online, Away, Offline, Unknown)
|
||||
// rich - array of rich presence, might currently be empty?
|
||||
// timestamp - number, UTC timestamp of record
|
||||
// device - string, XboxOne, Windows8, etc.
|
||||
// title - string, of game
|
||||
// playing - boolean, are they playing the game
|
||||
// presence - string, rich presence string from that title
|
||||
]]
|
||||
local function fetchXboxFriendsAsync()
|
||||
local success, result = pcall(function()
|
||||
if PlatformService then
|
||||
return PlatformService:BeginFetchFriends(Enum.UserInputType.Gamepad1)
|
||||
end
|
||||
end)
|
||||
if success then
|
||||
return HttpService:JSONDecode(result)
|
||||
else
|
||||
print("fetchXboxFriends failed because", result)
|
||||
end
|
||||
end
|
||||
|
||||
local function getOnlineFriends()
|
||||
if UserSettings().GameSettings:InStudioMode() then
|
||||
-- Roblox Friends - leaving this in for testing purposes in studio
|
||||
local result = Http.GetOnlineFriendsAsync()
|
||||
if not result then
|
||||
-- TODO: Error code
|
||||
return nil
|
||||
end
|
||||
--
|
||||
local myOnlineFriends = {}
|
||||
|
||||
for i = 1, #result do
|
||||
local data = result[i]
|
||||
local friend = {
|
||||
Name = Players:GetNameFromUserIdAsync(data["VisitorId"]);
|
||||
UserId = data["VisitorId"];
|
||||
LastLocation = data["LastLocation"];
|
||||
PlaceId = data["PlaceId"];
|
||||
LocationType = data["LocationType"];
|
||||
GameId = data["GameId"];
|
||||
}
|
||||
table.insert(myOnlineFriends, friend)
|
||||
end
|
||||
|
||||
local function sortFunc(a, b)
|
||||
if a.LocationType == b.LocationType then
|
||||
return a.Name:lower() < b.Name:lower()
|
||||
end
|
||||
return a.LocationType > b.LocationType
|
||||
end
|
||||
|
||||
table.sort(myOnlineFriends, sortFunc)
|
||||
|
||||
return myOnlineFriends
|
||||
elseif game:GetService('UserInputService'):GetPlatform() == Enum.Platform.XBoxOne then
|
||||
-- Xbox Friends
|
||||
local myXboxFriends = fetchXboxFriendsAsync()
|
||||
local myOnlineFriends = {}
|
||||
if myXboxFriends then
|
||||
myXboxFriends = myXboxFriends["friends"]
|
||||
myOnlineFriends = filterXboxFriends(myXboxFriends)
|
||||
end
|
||||
|
||||
return myOnlineFriends
|
||||
end
|
||||
end
|
||||
|
||||
--[[ Public API ]]--
|
||||
FriendsData.OnFriendsDataUpdated = Utility.Signal()
|
||||
|
||||
local isFetchingFriends = false
|
||||
function FriendsData.GetOnlineFriendsAsync()
|
||||
-- can only make one call into PlatformService:BeginFetchFriends() at a time
|
||||
while isFetchingFriends do
|
||||
wait()
|
||||
end
|
||||
-- we have current data, this will be updated when polling
|
||||
if myCurrentFriendsData then
|
||||
return myCurrentFriendsData
|
||||
end
|
||||
isFetchingFriends = true
|
||||
|
||||
myCurrentFriendsData = getOnlineFriends()
|
||||
-- spawn polling on first request
|
||||
if not isOnlineFriendsPolling then
|
||||
FriendsData.BeginPolling()
|
||||
end
|
||||
|
||||
isFetchingFriends = false
|
||||
|
||||
return myCurrentFriendsData
|
||||
end
|
||||
|
||||
function FriendsData.BeginPolling()
|
||||
if not isOnlineFriendsPolling then
|
||||
isOnlineFriendsPolling = true
|
||||
local requesterId = UserData:GetRbxUserId()
|
||||
spawn(function()
|
||||
wait(POLL_DELAY)
|
||||
while requesterId == UserData:GetRbxUserId() do
|
||||
myCurrentFriendsData = getOnlineFriends()
|
||||
FriendsData.OnFriendsDataUpdated:fire(myCurrentFriendsData)
|
||||
wait(POLL_DELAY)
|
||||
end
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
-- we make connections through this function so we can clean them all up upon
|
||||
-- clearing the friends data
|
||||
function FriendsData.ConnectUpdateEvent(cbFunc)
|
||||
local cn = FriendsData.OnFriendsDataUpdated:connect(cbFunc)
|
||||
table.insert(updateEventCns, cn)
|
||||
end
|
||||
|
||||
function FriendsData.Reset()
|
||||
isOnlineFriendsPolling = false
|
||||
myCurrentFriendsData = nil
|
||||
for index,cn in pairs(updateEventCns) do
|
||||
cn = Utility.DisconnectEvent(cn)
|
||||
updateEventCns[index] = nil
|
||||
end
|
||||
print('FriendsData: Cleared last users FriendsData')
|
||||
end
|
||||
|
||||
return FriendsData
|
||||
@@ -0,0 +1,204 @@
|
||||
--[[
|
||||
// FriendsView.lua
|
||||
|
||||
// Creates a view for the users friends.
|
||||
// Handles user input, updating view
|
||||
|
||||
TODO:
|
||||
Connect selected/deselected to change color
|
||||
]]
|
||||
local CoreGui = Game:GetService("CoreGui")
|
||||
local GuiRoot = CoreGui:FindFirstChild("RobloxGui")
|
||||
local Modules = GuiRoot:FindFirstChild("Modules")
|
||||
local PlatformService = nil
|
||||
pcall(function() PlatformService = game:GetService('PlatformService') end)
|
||||
local UserInputService = game:GetService('UserInputService')
|
||||
local GuiService = game:GetService('GuiService')
|
||||
|
||||
local FriendsData = require(Modules:FindFirstChild('FriendsData'))
|
||||
local FriendPresenceItem = require(Modules:FindFirstChild('FriendPresenceItem'))
|
||||
local SideBarModule = require(Modules:FindFirstChild('SideBar'))
|
||||
local Strings = require(Modules:FindFirstChild('LocalizedStrings'))
|
||||
local Utility = require(Modules:FindFirstChild('Utility'))
|
||||
local GameJoinModule = require(Modules:FindFirstChild('GameJoin'))
|
||||
local Errors = require(Modules:FindFirstChild('Errors'))
|
||||
local ErrorOverlayModule = require(Modules:FindFirstChild('ErrorOverlay'))
|
||||
local EventHub = require(Modules:FindFirstChild('EventHub'))
|
||||
local ScreenManager = require(Modules:FindFirstChild('ScreenManager'))
|
||||
|
||||
-- Array of tables
|
||||
-- see FriendsData - fetchXboxFriends() for full documentation
|
||||
local myFriendsData = nil
|
||||
|
||||
local FOLLOW_MODE = 2
|
||||
|
||||
local SIDE_BAR_ITEMS = {
|
||||
JoinGame = string.upper(Strings:LocalizedString("JoinGameWord"));
|
||||
ViewDetails = string.upper(Strings:LocalizedString("ViewGameDetailsWord"));
|
||||
ViewProfile = string.upper(Strings:LocalizedString("ViewGamerCardWord"));
|
||||
}
|
||||
|
||||
-- side bar is shared between all views
|
||||
local SideBar = SideBarModule()
|
||||
|
||||
local function setPresenceData(item, data)
|
||||
if UserSettings().GameSettings:InStudioMode() then
|
||||
item:SetAvatarImage(data.UserId)
|
||||
item:SetNameText(data.Name)
|
||||
item:SetPresence(data.LastLocation, data.PlaceId ~= nil)
|
||||
elseif UserInputService:GetPlatform() == Enum.Platform.XBoxOne then
|
||||
local rbxuid = data["robloxuid"]
|
||||
item:SetAvatarImage(rbxuid)
|
||||
item:SetNameText(data["display"])
|
||||
if data["PlaceId"] and data["LastLocation"] then
|
||||
item:SetPresence(data["LastLocation"], true)
|
||||
elseif data["rich"] then
|
||||
local richTbl = data["rich"]
|
||||
local presence = richTbl[#richTbl]
|
||||
item:SetPresence(presence["title"], false)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- viewGridContainer - ScrollingGrid
|
||||
-- friendData - FriendsData
|
||||
-- sizeConstraint - limit view to this many items
|
||||
-- updateFunc - function that will be called when FriendData update
|
||||
local createFriendsView = function(viewGridContainer, friendsData, sizeConstraint, updateFunc)
|
||||
local this = {}
|
||||
|
||||
-- map of userId to presenceItem
|
||||
local presenceItems = {}
|
||||
local selectedItemOnFocus = nil
|
||||
local guiObjectToSortIndex = {}
|
||||
local presenceItemToData = {}
|
||||
|
||||
local count = #friendsData
|
||||
if sizeConstraint then
|
||||
count = math.min(count, sizeConstraint)
|
||||
end
|
||||
|
||||
local function connectSideBar(item)
|
||||
local container = item:GetContainer()
|
||||
container.MouseButton1Click:connect(function()
|
||||
local data = presenceItemToData[item]
|
||||
if data then
|
||||
-- rebuild side bar based on current data
|
||||
SideBar:RemoveAllItems()
|
||||
local inGame = data["PlaceId"] ~= nil
|
||||
if inGame and not data["IsPrivateSession"] then
|
||||
SideBar:AddItem(SIDE_BAR_ITEMS.JoinGame, function()
|
||||
GameJoinModule:StartGame(GameJoinModule.JoinType.Follow, data["robloxuid"])
|
||||
end)
|
||||
SideBar:AddItem(SIDE_BAR_ITEMS.ViewDetails, function()
|
||||
-- pass nil for iconId, gameDetail will fetch
|
||||
EventHub:dispatchEvent(EventHub.Notifications["OpenGameDetail"], data["PlaceId"], data["LastLocation"], nil)
|
||||
end)
|
||||
end
|
||||
SideBar:AddItem(SIDE_BAR_ITEMS.ViewProfile, function()
|
||||
if PlatformService and data["xuid"] then
|
||||
local success, result = pcall(function()
|
||||
PlatformService:PopupProfileUI(Enum.UserInputType.Gamepad1, data["xuid"])
|
||||
end)
|
||||
-- NOTE: This will try to pop up the xbox system gamer card, failure will be handled
|
||||
-- by the xbox.
|
||||
if not success then
|
||||
print("PlatformService:PopupProfileUI failed because,", result)
|
||||
end
|
||||
end
|
||||
end)
|
||||
ScreenManager:OpenScreen(SideBar, false)
|
||||
else
|
||||
ScreenManager:OpenScreen(ErrorOverlayModule(Errors.Default), false)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
-- initialize view
|
||||
for i = 1, count do
|
||||
local data = friendsData[i]
|
||||
if data then
|
||||
local idStr = tostring(data.UserId or data["robloxuid"])
|
||||
local presenceItem = FriendPresenceItem(UDim2.new(0, 446, 0, 114), idStr)
|
||||
presenceItems[idStr] = presenceItem
|
||||
presenceItemToData[presenceItem] = data
|
||||
viewGridContainer:AddItem(presenceItem:GetContainer())
|
||||
setPresenceData(presenceItem, data)
|
||||
connectSideBar(presenceItem)
|
||||
if not selectedItemOnFocus then
|
||||
selectedItemOnFocus = presenceItem:GetContainer()
|
||||
end
|
||||
guiObjectToSortIndex[presenceItem:GetContainer()] = i
|
||||
end
|
||||
end
|
||||
|
||||
local function onFriendsUpdated(newFriendsData)
|
||||
local size = #newFriendsData
|
||||
if sizeConstraint then
|
||||
size = math.min(size, sizeConstraint)
|
||||
end
|
||||
|
||||
-- map of valid userIds to bool
|
||||
local validEntries = {}
|
||||
|
||||
-- a,b are guiObjects
|
||||
local function sortGridItems(a, b)
|
||||
if guiObjectToSortIndex[a] and guiObjectToSortIndex[b] then
|
||||
return guiObjectToSortIndex[a] < guiObjectToSortIndex[b]
|
||||
end
|
||||
if guiObjectToSortIndex[a] then return true end
|
||||
if guiObjectToSortIndex[b] then return false end
|
||||
return a.Name < b.Name
|
||||
end
|
||||
|
||||
-- refresh view
|
||||
for i = 1, size do
|
||||
local data = newFriendsData[i]
|
||||
if data then
|
||||
local idStr = tostring(data.UserId or data["robloxuid"])
|
||||
local presenceItem = presenceItems[idStr]
|
||||
if not presenceItem then
|
||||
presenceItem = FriendPresenceItem(UDim2.new(0, 446, 0, 114), idStr)
|
||||
presenceItems[idStr] = presenceItem
|
||||
viewGridContainer:AddItem(presenceItem:GetContainer())
|
||||
connectSideBar(presenceItem)
|
||||
end
|
||||
presenceItemToData[presenceItem] = data
|
||||
setPresenceData(presenceItem, data)
|
||||
validEntries[idStr] = true
|
||||
guiObjectToSortIndex[presenceItem:GetContainer()] = i
|
||||
if i == 1 then
|
||||
selectedItemOnFocus = presenceItem:GetContainer()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- remove items if needed
|
||||
for userId,presenceItem in pairs(presenceItems) do
|
||||
if not validEntries[userId] then
|
||||
local container = presenceItem:GetContainer()
|
||||
guiObjectToSortIndex[container] = nil
|
||||
viewGridContainer:RemoveItem(container)
|
||||
presenceItems[userId] = nil
|
||||
presenceItem:Destroy()
|
||||
presenceItem = nil
|
||||
end
|
||||
end
|
||||
|
||||
viewGridContainer:SortItems(sortGridItems)
|
||||
|
||||
if updateFunc then
|
||||
updateFunc(#newFriendsData)
|
||||
end
|
||||
end
|
||||
|
||||
FriendsData.ConnectUpdateEvent(onFriendsUpdated)
|
||||
|
||||
function this:GetDefaultFocusItem()
|
||||
return selectedItemOnFocus
|
||||
end
|
||||
|
||||
return this
|
||||
end
|
||||
|
||||
return createFriendsView
|
||||
@@ -0,0 +1,125 @@
|
||||
--[[
|
||||
// GameCollection.lua
|
||||
|
||||
// Used to get a collection of games
|
||||
]]
|
||||
local CoreGui = Game:GetService("CoreGui")
|
||||
local GuiRoot = CoreGui:FindFirstChild("RobloxGui")
|
||||
local Modules = GuiRoot:FindFirstChild("Modules")
|
||||
|
||||
local PlatformService = nil
|
||||
pcall(function() PlatformService = game:GetService('PlatformService') end)
|
||||
|
||||
local SortData = require(Modules:FindFirstChild('SortData'))
|
||||
local UserData = require(Modules:FindFirstChild('UserData'))
|
||||
local Utility = require(Modules:FindFirstChild('Utility'))
|
||||
|
||||
local GameCollection = {}
|
||||
|
||||
GameCollection.DefaultSortId = {
|
||||
Popular = 1;
|
||||
Featured = 3;
|
||||
TopEarning = 8;
|
||||
TopRated = 11;
|
||||
}
|
||||
|
||||
local function createBaseCollection()
|
||||
local this = {}
|
||||
|
||||
function this:GetSortAsync(startIndex, pageSize)
|
||||
print("GameCollection GetSortAsync() must be implemented by sub class")
|
||||
end
|
||||
|
||||
return this
|
||||
end
|
||||
|
||||
local SortCollections = {}
|
||||
function GameCollection:GetSort(sortId)
|
||||
if SortCollections[sortId] then
|
||||
return SortCollections[sortId]
|
||||
end
|
||||
|
||||
local collection = createBaseCollection()
|
||||
|
||||
-- Override
|
||||
function collection:GetSortAsync(startIndex, pageSize)
|
||||
local sort = SortData.GetSort(sortId)
|
||||
local timeFilter = nil
|
||||
-- top rated is time filtered to show most recent top rated
|
||||
if sortId == GameCollection.DefaultSortId.TopRated then
|
||||
timeFilter = 2
|
||||
end
|
||||
return sort:GetPageAsync(startIndex, pageSize, timeFilter)
|
||||
end
|
||||
|
||||
SortCollections[sortId] = collection
|
||||
|
||||
return collection
|
||||
end
|
||||
|
||||
local UserFavoriteCollection = nil
|
||||
function GameCollection:GetUserFavorites()
|
||||
if UserFavoriteCollection then
|
||||
return UserFavoriteCollection
|
||||
end
|
||||
|
||||
UserFavoriteCollection = createBaseCollection()
|
||||
|
||||
-- Override
|
||||
function UserFavoriteCollection:GetSortAsync(startIndex, pageSize)
|
||||
local sort = SortData.GetUserFavorites()
|
||||
return sort:GetPageAsync(startIndex, pageSize)
|
||||
end
|
||||
|
||||
return UserFavoriteCollection
|
||||
end
|
||||
|
||||
local UserRecentCollection = nil
|
||||
function GameCollection:GetUserRecent()
|
||||
if UserRecentCollection then
|
||||
return UserRecentCollection
|
||||
end
|
||||
|
||||
UserRecentCollection = createBaseCollection()
|
||||
|
||||
-- Override
|
||||
function UserRecentCollection:GetSortAsync(startIndex, pageSize)
|
||||
local sort = SortData.GetUserRecent()
|
||||
return sort:GetPageAsync(startIndex, pageSize)
|
||||
end
|
||||
|
||||
return UserRecentCollection
|
||||
end
|
||||
|
||||
local UserPlacesCollection = nil
|
||||
function GameCollection:GetUserPlaces()
|
||||
if UserPlacesCollection then
|
||||
return UserPlacesCollection
|
||||
end
|
||||
|
||||
UserPlacesCollection = createBaseCollection()
|
||||
|
||||
-- Override
|
||||
function UserPlacesCollection:GetSortAsync(startIndex, pageSize)
|
||||
if Utility.IsFastFlagEnabled("XboxPlayMyPlace") then
|
||||
local userId = UserData:GetRbxUserId()
|
||||
if userId then
|
||||
local sort = SortData.GetUserPlaces(userId)
|
||||
return sort:GetPageAsync(startIndex, pageSize)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return UserPlacesCollection
|
||||
end
|
||||
|
||||
if PlatformService then
|
||||
PlatformService.UserAccountChanged:connect(function()
|
||||
SortCollections = {}
|
||||
UserFavoritCollection = nil
|
||||
UserRecentCollection = nil
|
||||
UserPlacesCollection = nil
|
||||
end)
|
||||
end
|
||||
|
||||
return GameCollection
|
||||
@@ -0,0 +1,255 @@
|
||||
--[[
|
||||
// GameData.lua
|
||||
|
||||
// Fetches data for a game to be used to fill out
|
||||
// the details of that game
|
||||
]]
|
||||
local CoreGui = Game:GetService("CoreGui")
|
||||
local GuiRoot = CoreGui:FindFirstChild("RobloxGui")
|
||||
local Modules = GuiRoot:FindFirstChild("Modules")
|
||||
|
||||
local Http = require(Modules:FindFirstChild('Http'))
|
||||
local Utility = require(Modules:FindFirstChild('Utility'))
|
||||
|
||||
local UserData = require(Modules:FindFirstChild('UserData'))
|
||||
local ConvertMyPlaceNameInXboxAppFlag = Utility.IsFastFlagEnabled("ConvertMyPlaceNameInXboxApp")
|
||||
|
||||
local GameData = {}
|
||||
|
||||
local gameCreatorCache = {}
|
||||
function GameData:GetGameCreatorAsync(placeId)
|
||||
if placeId then
|
||||
if not gameCreatorCache[placeId] then
|
||||
local gameDataByPlaceId = self:GetGameDataAsync(placeId)
|
||||
gameCreatorCache[placeId] = gameDataByPlaceId:GetCreatorName()
|
||||
end
|
||||
return gameCreatorCache[placeId]
|
||||
end
|
||||
end
|
||||
|
||||
function GameData: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
|
||||
-- creatorName is optional and must be used when querying a game that is
|
||||
-- not the current user's creation
|
||||
function GameData:GetFilteredGameName(gameName, creatorName)
|
||||
if ConvertMyPlaceNameInXboxAppFlag and gameName and type(gameName) == 'string' then
|
||||
local tempUsername = self:ExtractGeneratedUsername(gameName)
|
||||
if tempUsername then
|
||||
local realUsername = creatorName or UserData:GetRobloxName()
|
||||
if realUsername then
|
||||
local newGameName = string.gsub(gameName, tempUsername, realUsername, 1)
|
||||
if newGameName then
|
||||
return newGameName
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return gameName
|
||||
end
|
||||
|
||||
function GameData:GetGameDataAsync(placeId)
|
||||
local this = {}
|
||||
|
||||
local result = Http.GetGameDetailsAsync(placeId)
|
||||
if not result then
|
||||
print("GameData:GetGameDataAsync() failed to get web response for placeId "..tostring(placeId))
|
||||
result = {}
|
||||
end
|
||||
|
||||
this.Data = result
|
||||
|
||||
--[[ Public API ]]--
|
||||
function this:GetCreatorName()
|
||||
return self.Data["Builder"] or ""
|
||||
end
|
||||
function this:GetDescription()
|
||||
return self.Data["Description"] or ""
|
||||
end
|
||||
function this:GetIsFavoritedByUser()
|
||||
return self.Data["IsFavoritedByUser"] or false
|
||||
end
|
||||
function this:GetLastUpdated()
|
||||
return self.Data["Updated"] or ""
|
||||
end
|
||||
function this:GetCreationDate()
|
||||
return self.Data["Created"] or ""
|
||||
end
|
||||
function this:GetMaxPlayers()
|
||||
return self.Data["MaxPlayers"] or 0
|
||||
end
|
||||
function this:GetOverridesDefaultAvatar()
|
||||
return self.Data["OverridesDefaultAvatar"] or false
|
||||
end
|
||||
function this:GetCreatorUserId()
|
||||
return self.Data["BuilderId"]
|
||||
end
|
||||
|
||||
--[[ Async Public API ]]--
|
||||
function this:GetVoteDataAsync()
|
||||
local result = Http.GetGameVotesAsync(placeId)
|
||||
if not result then
|
||||
print("GameData:GetVoteDataAsync() failed to get web response for placeId "..tostring(placeId))
|
||||
end
|
||||
|
||||
local voteData = {}
|
||||
local voteTable = result and result["VotingModel"] or nil
|
||||
|
||||
if voteTable then
|
||||
voteData.UpVotes = voteTable["UpVotes"] or 0
|
||||
voteData.DownVotes = voteTable["DownVotes"] or 0
|
||||
voteData.UserVote = voteTable["UserVote"] or nil
|
||||
voteData.CanVote = voteTable["CanVote"] or false
|
||||
voteData.CantVoteReason = voteTable["ReasonForNotVoteable"] or "PlayGame"
|
||||
end
|
||||
|
||||
return voteData
|
||||
end
|
||||
|
||||
function this:GetGameIconIdAsync()
|
||||
local iconId = nil
|
||||
local result = Http.GetGameIconIdAsync(placeId)
|
||||
if result then
|
||||
iconId = result["ImageId"]
|
||||
-- use placeId as backup
|
||||
if not iconId then
|
||||
iconId = placeId
|
||||
end
|
||||
end
|
||||
|
||||
return iconId
|
||||
end
|
||||
|
||||
function this:GetRecommendedGamesAsync()
|
||||
local result = Http.GetRecommendedGamesAsync(placeId)
|
||||
if not result then
|
||||
print("GameData:GetRecommendedGamesAsync() failed to get web response for placeId "..tostring(placeId))
|
||||
return {}
|
||||
end
|
||||
|
||||
local recommendedGames = {}
|
||||
for i = 1, #result do
|
||||
local data = result[i]
|
||||
if data then
|
||||
local game = {}
|
||||
-- Temp fix for fixing game names
|
||||
game.Name = GameData:GetFilteredGameName(data["GameName"], data["Creator"] and data["Creator"]["CreatorName"])
|
||||
game.PlaceId = data["PlaceId"]
|
||||
game.IconId = data["ImageId"]
|
||||
table.insert(recommendedGames, game)
|
||||
end
|
||||
end
|
||||
|
||||
return recommendedGames
|
||||
end
|
||||
|
||||
function this:GetThumbnailIdsAsync()
|
||||
local result = Http.GetGameThumbnailsAsync(placeId)
|
||||
if not result then
|
||||
print("GameData:GetThumbnailIdsAsync() failed to get web response for placeId "..tostring(placeId))
|
||||
return {}
|
||||
end
|
||||
|
||||
local thumbIds = {}
|
||||
local thumbIdTable = result["thumbnails"]
|
||||
if thumbIdTable then
|
||||
for i = 1, #thumbIdTable do
|
||||
local data = thumbIdTable[i]
|
||||
-- AssetTypeId of 1 is a Image (33 is a video if can ever play videos)
|
||||
if data and data["AssetTypeId"] == 1 then
|
||||
local assetId = data["AssetId"]
|
||||
if assetId then
|
||||
table.insert(thumbIds, assetId)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return thumbIds
|
||||
end
|
||||
|
||||
function this:GetBadgeDataAsync()
|
||||
local result = Http.GetGameBadgeDataAsync(placeId)
|
||||
if not result then
|
||||
print("GameData:GetBadgeDataAsync() failed to get web response for placeId "..tostring(placeId))
|
||||
return {}
|
||||
end
|
||||
|
||||
local badgeData = {}
|
||||
local badgeTable = result["GameBadges"]
|
||||
if badgeTable then
|
||||
for i = 1, #badgeTable do
|
||||
local data = badgeTable[i]
|
||||
if data then
|
||||
local badge = {}
|
||||
badge.Name = data["Name"]
|
||||
badge.Description = data["Description"]
|
||||
badge.AssetId = data["BadgeAssetId"]
|
||||
badge.IsOwned = data["IsOwned"]
|
||||
badge.Order = i
|
||||
table.insert(badgeData, badge)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
table.sort(badgeData, function(a, b)
|
||||
if a["IsOwned"] == true and b["IsOwned"] == true then
|
||||
return a.Order < b.Order
|
||||
elseif a["IsOwned"] then
|
||||
return true
|
||||
elseif b["IsOwned"] then
|
||||
return false
|
||||
end
|
||||
return a.Order < b.Order
|
||||
end)
|
||||
|
||||
return badgeData
|
||||
end
|
||||
|
||||
--[[ Post Public API ]]--
|
||||
function this:PostFavoriteAsync()
|
||||
local result = Http.PostFavoriteToggleAsync(placeId)
|
||||
local success = result and result["success"] == true
|
||||
if not success then
|
||||
local reason = "Failed"
|
||||
-- the floodcheck message is "Whoa. Slow Down.". So if there is a message,
|
||||
-- let's just say flood check?
|
||||
if result and result["message"] then
|
||||
reason = "FloodCheck"
|
||||
end
|
||||
return success, reason
|
||||
else
|
||||
self.Data["IsFavoritedByUser"] = not self:GetIsFavoritedByUser()
|
||||
end
|
||||
|
||||
return success
|
||||
end
|
||||
|
||||
function this:PostVoteAsync(status)
|
||||
local result = Http.PostGameVoteAsync(placeId, status)
|
||||
if not result then
|
||||
return nil
|
||||
end
|
||||
|
||||
local success = result["Success"] == true
|
||||
if not success then
|
||||
return success, result["ModalType"]
|
||||
end
|
||||
|
||||
return success
|
||||
end
|
||||
|
||||
-- Temp fix for fixing game names
|
||||
if this.Data then
|
||||
this.Data.Name = GameData:GetFilteredGameName(this.Data.Name, this:GetCreatorName())
|
||||
end
|
||||
|
||||
return this
|
||||
end
|
||||
|
||||
return GameData
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,712 @@
|
||||
--[[
|
||||
// GameGenre.lua
|
||||
// Displays a game genre page for a certain sort
|
||||
|
||||
TODO:
|
||||
Clean up code
|
||||
]]
|
||||
local CoreGui = Game:GetService("CoreGui")
|
||||
local GuiRoot = CoreGui:FindFirstChild("RobloxGui")
|
||||
local Modules = GuiRoot:FindFirstChild("Modules")
|
||||
local ContextActionService = game:GetService("ContextActionService")
|
||||
local GuiService = game:GetService('GuiService')
|
||||
local PlatformService = nil
|
||||
pcall(function() PlatformService = game:GetService('PlatformService') end)
|
||||
|
||||
local Utility = require(Modules:FindFirstChild('Utility'))
|
||||
local GlobalSettings = require(Modules:FindFirstChild('GlobalSettings'))
|
||||
local GameDataModule = require(Modules:FindFirstChild('GameData'))
|
||||
local SideBarModule = require(Modules:FindFirstChild('SideBar'))
|
||||
local VoteFrameModule = require(Modules:FindFirstChild('VoteFrame'))
|
||||
local Strings = require(Modules:FindFirstChild('LocalizedStrings'))
|
||||
local SortCarousel = require(Modules:FindFirstChild('SortCarousel'))
|
||||
local ScreenManager = require(Modules:FindFirstChild('ScreenManager'))
|
||||
local AssetManager = require(Modules:FindFirstChild('AssetManager'))
|
||||
local GameJoinModule = require(Modules:FindFirstChild('GameJoin'))
|
||||
local SoundManager = require(Modules:FindFirstChild('SoundManager'))
|
||||
local Errors = require(Modules:FindFirstChild('Errors'))
|
||||
local ErrorOverlayModule = require(Modules:FindFirstChild('ErrorOverlay'))
|
||||
local LoadingWidget = require(Modules:FindFirstChild('LoadingWidget'))
|
||||
local GameCollection = require(Modules:FindFirstChild('GameCollection'))
|
||||
|
||||
local isUseNewCarouselInXboxAppEnabled = Utility.IsFastFlagEnabled("UseNewCarouselInXboxApp")
|
||||
local CarouselView = require(Modules:FindFirstChild('CarouselView'))
|
||||
local CarouselController = require(Modules:FindFirstChild('CarouselController'))
|
||||
|
||||
local function CreateGameGenre(sortName, gameCollection)
|
||||
local this = {}
|
||||
|
||||
local inFocus = false
|
||||
|
||||
local gameLoadCount = 20
|
||||
local sideBarSorts = {} -- array or sort names and ids for the side bar
|
||||
local baseButtonTextColor = GlobalSettings.WhiteTextColor
|
||||
local selectedButtonTextColor = GlobalSettings.TextSelectedColor
|
||||
|
||||
local newGameSelectedCn = nil
|
||||
local sideBarSelectedCn = nil
|
||||
local dataModelViewChangedCn = nil
|
||||
local canJoinGame = true
|
||||
local returnedFromGame = true
|
||||
|
||||
local currentShownCollection = gameCollection
|
||||
--[[ Top Level Elements ]]--
|
||||
local GameGenreContainer = Utility.Create'Frame'
|
||||
{
|
||||
Name = "GameGenreContainer";
|
||||
Size = UDim2.new(1, 0, 1, 0);
|
||||
Position = UDim2.new(0, 0, 0, 0);
|
||||
BackgroundTransparency = 1;
|
||||
Visible = false;
|
||||
Parent = parent;
|
||||
}
|
||||
local BackLabel = Utility.Create'ImageLabel'
|
||||
{
|
||||
Name = "BackLabel";
|
||||
Position = UDim2.new(0, 0, 0, 0);
|
||||
BackgroundTransparency = 1;
|
||||
ZIndex = 2;
|
||||
Parent = GameGenreContainer;
|
||||
}
|
||||
AssetManager.LocalImage(BackLabel,
|
||||
'rbxasset://textures/ui/Shell/Icons/BackIcon', {['720'] = UDim2.new(0,32,0,32); ['1080'] = UDim2.new(0,48,0,48);})
|
||||
|
||||
local BackText = Utility.Create'TextLabel'
|
||||
{
|
||||
Name = "BackText";
|
||||
Size = UDim2.new(0, 0, 0, BackLabel.Size.Y.Offset);
|
||||
Position = UDim2.new(0, BackLabel.Size.X.Offset + 8, 0, 0);
|
||||
BackgroundTransparency = 1;
|
||||
Font = GlobalSettings.RegularFont;
|
||||
FontSize = GlobalSettings.ButtonSize;
|
||||
TextXAlignment = Enum.TextXAlignment.Left;
|
||||
TextColor3 = GlobalSettings.WhiteTextColor;
|
||||
Text = '';
|
||||
Parent = GameGenreContainer;
|
||||
}
|
||||
local SideBarButton = Utility.Create'ImageButton'
|
||||
{
|
||||
Name = "SideBarButton";
|
||||
Size = UDim2.new(0, 450, 0, 75);
|
||||
Position = UDim2.new(0, 0, 0, BackLabel.Size.Y.Offset + 60);
|
||||
BackgroundTransparency = 1;
|
||||
BorderSizePixel = 0;
|
||||
BackgroundColor3 = GlobalSettings.GreySelectionColor;
|
||||
Parent = GameGenreContainer;
|
||||
|
||||
SoundManager:CreateSound('MoveSelection');
|
||||
}
|
||||
SideBarButton.NextSelectionRight = SideBarButton
|
||||
SideBarButton.NextSelectionLeft = SideBarButton
|
||||
SideBarButton.SelectionGained:connect(function()
|
||||
Utility.PropertyTweener(SideBarButton, 'BackgroundTransparency', 0, 0, 0, nil, true)
|
||||
end)
|
||||
SideBarButton.SelectionLost:connect(function()
|
||||
Utility.PropertyTweener(SideBarButton, 'BackgroundTransparency', 1, 1, 0, nil, true)
|
||||
end)
|
||||
|
||||
local DropDownImage = Utility.Create'ImageLabel'
|
||||
{
|
||||
Name = "DropDownImage";
|
||||
BackgroundTransparency = 1;
|
||||
Parent = SideBarButton;
|
||||
}
|
||||
AssetManager.LocalImage(DropDownImage,
|
||||
'rbxasset://textures/ui/Shell/Icons/Dropdown02', {['720'] = UDim2.new(0,29,0,29); ['1080'] = UDim2.new(0,44,0,44);})
|
||||
DropDownImage.Position = UDim2.new(1, -DropDownImage.Size.X.Offset - 12, 0.5, -DropDownImage.Size.Y.Offset / 2 + 4)
|
||||
|
||||
local TitleLabel = Utility.Create'TextLabel'
|
||||
{
|
||||
Name = "TitleLabel";
|
||||
Size = UDim2.new(0, 0, 0, 0);
|
||||
Position = UDim2.new(0, 18, 0.5, 0);
|
||||
BackgroundTransparency = 1;
|
||||
Text = string.upper(sortName);
|
||||
TextColor3 = GlobalSettings.WhiteTextColor;
|
||||
TextXAlignment = Enum.TextXAlignment.Left;
|
||||
Font = GlobalSettings.LightFont;
|
||||
FontSize = GlobalSettings.HeaderSize;
|
||||
ZIndex = 2;
|
||||
Parent = SideBarButton;
|
||||
}
|
||||
local GameTitleLabel = Utility.Create'TextLabel'
|
||||
{
|
||||
Name = "GameTitleLabel";
|
||||
Size = UDim2.new(0, 0, 0, 0);
|
||||
Position = UDim2.new(0, 18, 0, SideBarButton.Position.Y.Offset + SideBarButton.Size.Y.Offset + 550);
|
||||
BackgroundTransparency = 1;
|
||||
Text = "";
|
||||
TextColor3 = GlobalSettings.WhiteTextColor;
|
||||
TextXAlignment = Enum.TextXAlignment.Left;
|
||||
Font = GlobalSettings.LightFont;
|
||||
FontSize = GlobalSettings.HeaderSize;
|
||||
Parent = GameGenreContainer;
|
||||
}
|
||||
local ThumbsUpImage = Utility.Create'ImageLabel'
|
||||
{
|
||||
Name = "ThumbsUpImage";
|
||||
BackgroundTransparency = 1;
|
||||
ZIndex = 2;
|
||||
Visible = false;
|
||||
Parent = GameGenreContainer;
|
||||
}
|
||||
local ThumbsDownImage = Utility.Create'ImageLabel'
|
||||
{
|
||||
Name = "ThumbsDownImage";
|
||||
BackgroundTransparency = 1;
|
||||
ZIndex = 2;
|
||||
Visible = false;
|
||||
Parent = GameGenreContainer;
|
||||
}
|
||||
AssetManager.LocalImage(ThumbsUpImage,
|
||||
'rbxasset://textures/ui/Shell/Icons/ThumbsUpIcon', {['720'] = UDim2.new(0,19,0,19); ['1080'] = UDim2.new(0,28,0,28);})
|
||||
AssetManager.LocalImage(ThumbsDownImage,
|
||||
'rbxasset://textures/ui/Shell/Icons/ThumbsDownIcon', {['720'] = UDim2.new(0,19,0,19); ['1080'] = UDim2.new(0,28,0,28);})
|
||||
|
||||
local VoteWidget = VoteFrameModule(GameGenreContainer,
|
||||
UDim2.new(0, 60, 0, GameTitleLabel.Position.Y.Offset + 46))
|
||||
local VoteContainer = VoteWidget:GetContainer()
|
||||
ThumbsUpImage.Position = UDim2.new(0, VoteContainer.Position.X.Offset - ThumbsUpImage.Size.X.Offset - 10, 0,
|
||||
VoteContainer.Position.Y.Offset + VoteContainer.Size.Y.Offset - ThumbsUpImage.Size.Y.Offset)
|
||||
ThumbsDownImage.Position = UDim2.new(0, VoteContainer.Position.X.Offset + VoteContainer.Size.X.Offset + 10, 0,
|
||||
VoteContainer.Position.Y.Offset)
|
||||
|
||||
local SeparatorDot = Utility.Create'ImageLabel'
|
||||
{
|
||||
Name = "SeparatorDot";
|
||||
BackgroundTransparency = 1;
|
||||
Visible = false;
|
||||
Parent = GameGenreContainer;
|
||||
}
|
||||
AssetManager.LocalImage(SeparatorDot,
|
||||
'rbxasset://textures/ui/Shell/Icons/SeparatorDot', {['720'] = UDim2.new(0,7,0,7); ['1080'] = UDim2.new(0,10,0,10);})
|
||||
SeparatorDot.Position = UDim2.new(0, ThumbsDownImage.Position.X.Offset + ThumbsDownImage.Size.X.Offset + 32, 0,
|
||||
VoteContainer.Position.Y.Offset + (VoteContainer.Size.Y.Offset/2) - (SeparatorDot.Size.Y.Offset/2))
|
||||
|
||||
local CreatorIcon = Utility.Create'ImageLabel'
|
||||
{
|
||||
Name = "CreatorIcon";
|
||||
Size = UDim2.new(0, 24, 0, 24);
|
||||
Position = UDim2.new(0, SeparatorDot.Position.X.Offset + SeparatorDot.Size.X.Offset + 32, 0,
|
||||
SeparatorDot.Position.Y.Offset + SeparatorDot.Size.Y.Offset/2 - 2 - 10);
|
||||
BackgroundTransparency = 1;
|
||||
Image = 'rbxasset://textures/ui/Shell/Icons/RobloxIcon24.png';
|
||||
Visible = false;
|
||||
Parent = GameGenreContainer;
|
||||
}
|
||||
|
||||
local CreatorNameLabel = Utility. Create'TextLabel'
|
||||
{
|
||||
Name = "CreatorNameLabel";
|
||||
Size = UDim2.new(0, 0, 0, 0);
|
||||
Position = UDim2.new(0, CreatorIcon.Position.X.Offset + CreatorIcon.Size.X.Offset + 8, 0,
|
||||
SeparatorDot.Position.Y.Offset + SeparatorDot.Size.Y.Offset/2 - 2);
|
||||
BackgroundTransparency = 1;
|
||||
Font = GlobalSettings.RegularFont;
|
||||
FontSize = GlobalSettings.DescriptionSize;
|
||||
TextColor3 = GlobalSettings.LightGreyTextColor;
|
||||
TextXAlignment = Enum.TextXAlignment.Left;
|
||||
Text = "";
|
||||
Parent = GameGenreContainer;
|
||||
}
|
||||
|
||||
local DescriptionTextLabel = Utility.Create'TextLabel'
|
||||
{
|
||||
Name = "DescriptionTextLabel";
|
||||
Size = UDim2.new(0, 850, 0, 56);
|
||||
Position = UDim2.new(0, GameTitleLabel.Position.X.Offset, 0, VoteContainer.Position.Y.Offset + VoteContainer.Size.Y.Offset + 20);
|
||||
BackgroundTransparency = 1;
|
||||
Text = "";
|
||||
TextColor3 = GlobalSettings.LightGreyTextColor;
|
||||
TextXAlignment = Enum.TextXAlignment.Left;
|
||||
TextYAlignment = Enum.TextYAlignment.Top;
|
||||
Font = GlobalSettings.LightFont;
|
||||
TextWrapped = true;
|
||||
FontSize = GlobalSettings.DescriptionSize;
|
||||
Parent = GameGenreContainer;
|
||||
}
|
||||
local PlayButton = Utility.Create'ImageButton'
|
||||
{
|
||||
Name = "PlayButton";
|
||||
Position = UDim2.new(0, 0, 1, -77);
|
||||
Size = UDim2.new(0, 228, 0, 72);
|
||||
BackgroundTransparency = 1;
|
||||
ImageColor3 = GlobalSettings.GreenButtonColor;
|
||||
Image = 'rbxasset://textures/ui/Shell/Buttons/Generic9ScaleButton@720.png';
|
||||
ScaleType = Enum.ScaleType.Slice;
|
||||
SliceCenter = Rect.new(Vector2.new(4, 4), Vector2.new(28, 28));
|
||||
Parent = GameGenreContainer;
|
||||
|
||||
SoundManager:CreateSound('MoveSelection');
|
||||
ZIndex = 2;
|
||||
AssetManager.CreateShadow(1);
|
||||
}
|
||||
|
||||
local PlayText = Utility.Create'TextLabel'
|
||||
{
|
||||
Name = "PlayText";
|
||||
Size = UDim2.new(1, 0, 1, 0);
|
||||
BackgroundTransparency = 1;
|
||||
Text = string.upper(Strings:LocalizedString("PlayWord"));
|
||||
Font = GlobalSettings.RegularFont;
|
||||
FontSize = GlobalSettings.ButtonSize;
|
||||
TextColor3 = baseButtonTextColor;
|
||||
ZIndex = 2;
|
||||
Parent = PlayButton;
|
||||
}
|
||||
local FavoriteButton = Utility.Create'ImageButton'
|
||||
{
|
||||
Name = "FavoriteButton";
|
||||
Position = UDim2.new(0, PlayButton.Size.X.Offset + 10, 1, -77);
|
||||
Size = UDim2.new(0, 228, 0, 72);
|
||||
BackgroundTransparency = 1;
|
||||
ImageColor3 = GlobalSettings.GreyButtonColor;
|
||||
Image = 'rbxasset://textures/ui/Shell/Buttons/Generic9ScaleButton@720.png';
|
||||
ScaleType = Enum.ScaleType.Slice;
|
||||
SliceCenter = Rect.new(Vector2.new(4, 4), Vector2.new(28, 28));
|
||||
ZIndex = 2;
|
||||
Parent = GameGenreContainer;
|
||||
|
||||
SoundManager:CreateSound('MoveSelection');
|
||||
AssetManager.CreateShadow(1);
|
||||
}
|
||||
|
||||
FavoriteButton.NextSelectionRight = FavoriteButton
|
||||
local FavoriteText = Utility.Create'TextLabel'
|
||||
{
|
||||
Name = "FavoriteText";
|
||||
Size = UDim2.new(1, 0, 1, 0);
|
||||
BackgroundTransparency = 1;
|
||||
Text = string.upper(Strings:LocalizedString("FavoriteWord"));
|
||||
Font = GlobalSettings.RegularFont;
|
||||
FontSize = GlobalSettings.ButtonSize;
|
||||
TextColor3 = baseButtonTextColor;
|
||||
ZIndex = 2;
|
||||
Parent = FavoriteButton;
|
||||
}
|
||||
local FavoriteStarImage = Utility.Create'ImageLabel'
|
||||
{
|
||||
Name = "FavoriteStarImage";
|
||||
BackgroundTransparency = 1;
|
||||
Visible = false;
|
||||
ZIndex = 2;
|
||||
Parent = FavoriteButton;
|
||||
}
|
||||
AssetManager.LocalImage(FavoriteStarImage,
|
||||
'rbxasset://textures/ui/Shell/Icons/FavoriteStar', {['720'] = UDim2.new(0,21,0,21); ['1080'] = UDim2.new(0,32,0,31);})
|
||||
FavoriteStarImage.Position = UDim2.new(0, 16, 0.5, -FavoriteStarImage.Size.Y.Offset / 2)
|
||||
|
||||
-- Selection Overrides
|
||||
-- NOTE: This is a fix to prevent unintended selection in the carousel do to the nature of
|
||||
-- how selection works for a ScrollingFrame.
|
||||
PlayButton.NextSelectionLeft = PlayButton
|
||||
FavoriteButton.NextSelectionRight = FavoriteButton
|
||||
SideBarButton.NextSelectionLeft = SideBarButton
|
||||
SideBarButton.NextSelectionRight = SideBarButton
|
||||
|
||||
--[[ Page Events ]]--
|
||||
local function toggleFavoriteButton(isFavorited)
|
||||
if isFavorited == true then
|
||||
FavoriteStarImage.Visible = true
|
||||
FavoriteText.Position = UDim2.new(0, FavoriteStarImage.Position.X.Offset + FavoriteStarImage.Size.X.Offset + 12, 0, 0)
|
||||
FavoriteText.Text = string.upper(Strings:LocalizedString("FavoritedWord"))
|
||||
FavoriteText.TextXAlignment = Enum.TextXAlignment.Left
|
||||
elseif isFavorited == false then
|
||||
FavoriteStarImage.Visible = false
|
||||
FavoriteText.Position = UDim2.new(0, 0, 0, 0)
|
||||
FavoriteText.Text = string.upper(Strings:LocalizedString("FavoriteWord"))
|
||||
FavoriteText.TextXAlignment = Enum.TextXAlignment.Center
|
||||
end
|
||||
end
|
||||
FavoriteButton.SelectionGained:connect(function()
|
||||
FavoriteButton.ImageColor3 = GlobalSettings.GreySelectedButtonColor
|
||||
FavoriteText.TextColor3 = selectedButtonTextColor
|
||||
end)
|
||||
FavoriteButton.SelectionLost:connect(function()
|
||||
FavoriteButton.ImageColor3 = GlobalSettings.GreyButtonColor
|
||||
FavoriteText.TextColor3 = baseButtonTextColor
|
||||
end)
|
||||
PlayButton.SelectionGained:connect(function()
|
||||
PlayButton.ImageColor3 = GlobalSettings.GreenSelectedButtonColor
|
||||
PlayText.TextColor3 = selectedButtonTextColor
|
||||
end)
|
||||
PlayButton.SelectionLost:connect(function()
|
||||
PlayButton.ImageColor3 = GlobalSettings.GreenButtonColor
|
||||
PlayText.TextColor3 = baseButtonTextColor
|
||||
end)
|
||||
|
||||
--[[ Content Initialization ]]--
|
||||
local function setItemsVisible(value)
|
||||
ThumbsUpImage.Visible = value
|
||||
ThumbsDownImage.Visible = value
|
||||
VoteWidget:SetVisible(value)
|
||||
GameTitleLabel.Visible = value
|
||||
DescriptionTextLabel.Visible = value
|
||||
PlayButton.Visible = value
|
||||
FavoriteButton.Visible = value
|
||||
SideBarButton.Visible = value
|
||||
--
|
||||
SeparatorDot.Visible = value
|
||||
CreatorIcon.Visible = value
|
||||
CreatorNameLabel.Visible = value
|
||||
end
|
||||
|
||||
local currentSortCarousel = nil
|
||||
local currentItemData = nil
|
||||
local onNewGameSelectedCn = nil
|
||||
local onNewGameSelectedLateCn = nil
|
||||
local function setCurrentSortCarousel()
|
||||
setItemsVisible(false)
|
||||
toggleFavoriteButton(false)
|
||||
currentSortCarousel = SortCarousel(UDim2.new(1, 0, 0, 450),
|
||||
UDim2.new(0, 0, 0, SideBarButton.Position.Y.Offset + SideBarButton.Size.Y.Offset + 56), currentShownCollection, GameGenreContainer)
|
||||
onNewGameSelectedCn = currentSortCarousel.OnNewGameSelected:connect(function(itemData)
|
||||
if itemData then
|
||||
currentItemData = itemData
|
||||
GameTitleLabel.Text = itemData.Name
|
||||
DescriptionTextLabel.Text = itemData.Description or ""
|
||||
CreatorNameLabel.Text = itemData.CreatorName
|
||||
toggleFavoriteButton(itemData.IsFavorited)
|
||||
local voteData = itemData.VoteData
|
||||
if voteData then
|
||||
local upvotes = voteData.UpVotes
|
||||
local downvotes = voteData.DownVotes
|
||||
if upvotes == 0 and downvotes == 0 then
|
||||
VoteWidget:SetPercentFilled(nil)
|
||||
else
|
||||
VoteWidget:SetPercentFilled(upvotes / (upvotes + downvotes))
|
||||
end
|
||||
end
|
||||
setItemsVisible(true)
|
||||
end
|
||||
end)
|
||||
onNewGameSelectedLateCn = currentSortCarousel.OnNewGameSelectedLate:connect(function(description, isFavorited)
|
||||
toggleFavoriteButton(isFavorited)
|
||||
DescriptionTextLabel.Text = description or ""
|
||||
end)
|
||||
|
||||
spawn(function()
|
||||
currentSortCarousel:LoadSortAsync()
|
||||
if inFocus then
|
||||
local focusItem = currentSortCarousel:GetItemAt(1)
|
||||
if focusItem and focusItem:IsDescendantOf(GameGenreContainer) then
|
||||
GuiService.SelectedCoreObject = focusItem
|
||||
end
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
local function onNewGameSelected(data)
|
||||
if not data then return end
|
||||
-- TODO: Update this function when caching is finished
|
||||
GameTitleLabel.Text = data.Title
|
||||
CreatorNameLabel.Text = data.CreatorName
|
||||
|
||||
local voteData = data.VoteData
|
||||
if voteData then
|
||||
local upVotes = voteData.UpVotes
|
||||
local downVotes = voteData.DownVotes
|
||||
if upVotes == 0 and downVotes == 0 then
|
||||
VoteWidget:SetPercentFilled(nil)
|
||||
else
|
||||
VoteWidget:SetPercentFilled(upVotes / (upVotes + downVotes))
|
||||
end
|
||||
end
|
||||
|
||||
DescriptionTextLabel.Text = data.Description or ""
|
||||
toggleFavoriteButton(data.IsFavorited)
|
||||
|
||||
if not data.Description or data.IsFavorited == nil then
|
||||
spawn(function()
|
||||
local gameData = GameDataModule:GetGameDataAsync(data.PlaceId)
|
||||
if gameData then
|
||||
data.GameData = gameData
|
||||
data.Description = gameData:GetDescription()
|
||||
data.IsFavorited = gameData:GetIsFavoritedByUser()
|
||||
|
||||
DescriptionTextLabel.Text = data.Description
|
||||
toggleFavoriteButton(data.IsFavorited)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
setItemsVisible(true)
|
||||
end
|
||||
|
||||
local myCarouselView = nil
|
||||
local myCarouselController = nil
|
||||
if isUseNewCarouselInXboxAppEnabled then
|
||||
myCarouselView = CarouselView()
|
||||
myCarouselView:SetSize(UDim2.new(0, 1724, 0, 450))
|
||||
myCarouselView:SetPosition(UDim2.new(0, 0, 0, SideBarButton.Position.Y.Offset + SideBarButton.Size.Y.Offset + 56))
|
||||
myCarouselView:SetPadding(18)
|
||||
myCarouselView:SetItemSizePercentOfContainer(2/3)
|
||||
myCarouselView:SetParent(GameGenreContainer)
|
||||
|
||||
myCarouselController = CarouselController(myCarouselView)
|
||||
end
|
||||
|
||||
local function initializeCarouselView()
|
||||
setItemsVisible(false)
|
||||
toggleFavoriteButton(false)
|
||||
myCarouselView:SetParent(nil)
|
||||
|
||||
spawn(function()
|
||||
local loader = LoadingWidget(
|
||||
{ Parent = GameGenreContainer }, {
|
||||
function()
|
||||
if myCarouselController then
|
||||
myCarouselController:InitializeAsync(currentShownCollection)
|
||||
end
|
||||
end
|
||||
})
|
||||
loader:AwaitFinished()
|
||||
loader:Cleanup()
|
||||
loader = nil
|
||||
|
||||
myCarouselView:SetParent(GameGenreContainer)
|
||||
if this:IsFocused() and myCarouselView then
|
||||
GuiService.SelectedCoreObject = myCarouselView:GetFocusItem()
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
local function canShowFavoritesAsync(collection)
|
||||
local favoritesPage = collection:GetSortAsync(0, 1)
|
||||
return favoritesPage and favoritesPage.Count > 0
|
||||
end
|
||||
local function canShowRecentAsync(collection)
|
||||
local recentPage = collection:GetSortAsync(0, 1)
|
||||
return recentPage and recentPage.Count > 0
|
||||
end
|
||||
local function canShowMyPlacesAsync(collection)
|
||||
local userPlacesPage = collection:GetSortAsync(0, 1)
|
||||
return userPlacesPage and userPlacesPage.Count > 0
|
||||
end
|
||||
local function getSideBarList()
|
||||
local sideBarList = {}
|
||||
local favoriteCollection = GameCollection:GetUserFavorites()
|
||||
if canShowFavoritesAsync(favoriteCollection) then
|
||||
table.insert(sideBarList,
|
||||
{ Name = string.upper(Strings:LocalizedString("FavoritesSortTitle")), Collection = favoriteCollection })
|
||||
end
|
||||
local recentCollection = GameCollection:GetUserRecent()
|
||||
if canShowRecentAsync(recentCollection) then
|
||||
table.insert(sideBarList,
|
||||
{ Name = string.upper(Strings:LocalizedString("RecentlyPlayedSortTitle")), Collection = recentCollection })
|
||||
end
|
||||
table.insert(sideBarList, { Name = string.upper(Strings:LocalizedString("FeaturedTitle")), Collection = GameCollection:GetSort(GameCollection.DefaultSortId.Featured) })
|
||||
table.insert(sideBarList, { Name = string.upper(Strings:LocalizedString("PopularTitle")), Collection = GameCollection:GetSort(GameCollection.DefaultSortId.Popular) })
|
||||
table.insert(sideBarList, { Name = string.upper(Strings:LocalizedString("TopRatedTitle")), Collection = GameCollection:GetSort(GameCollection.DefaultSortId.TopRated) })
|
||||
table.insert(sideBarList, { Name = string.upper(Strings:LocalizedString("TopEarningTitle")), Collection = GameCollection:GetSort(GameCollection.DefaultSortId.TopEarning) })
|
||||
|
||||
local userPlacesCollection = GameCollection:GetUserPlaces()
|
||||
if canShowMyPlacesAsync(userPlacesCollection) then
|
||||
table.insert(sideBarList,
|
||||
{ Name = string.upper(Strings:LocalizedString("PlayMyPlaceMoreGamesTitle")), Collection = userPlacesCollection })
|
||||
end
|
||||
|
||||
return sideBarList
|
||||
end
|
||||
|
||||
local function createSideBarAsync()
|
||||
local sideBar = SideBarModule()
|
||||
local sideBarList = getSideBarList()
|
||||
local collectionToIndex = {}
|
||||
|
||||
for i = 1, #sideBarList do
|
||||
local sort = sideBarList[i]
|
||||
collectionToIndex[sort.Collection] = i
|
||||
sideBar:AddItem(sort.Name, function()
|
||||
if sort.Collection ~= currentShownCollection then
|
||||
currentShownCollection = sort.Collection
|
||||
TitleLabel.Text = sort.Name
|
||||
|
||||
if isUseNewCarouselInXboxAppEnabled then
|
||||
initializeCarouselView()
|
||||
else
|
||||
currentSortCarousel:Destroy()
|
||||
setCurrentSortCarousel()
|
||||
end
|
||||
if this.TransitionTweens then
|
||||
ScreenManager:DefaultCancelFade(this.TransitionTweens)
|
||||
this.TransitionTweens = ScreenManager:DefaultFadeIn(GameGenreContainer)
|
||||
ScreenManager:PlayDefaultOpenSound()
|
||||
end
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
sideBarSelectedCn = Utility.DisconnectEvent(sideBarSelectedCn)
|
||||
sideBarSelectedCn = SideBarButton.MouseButton1Click:connect(function()
|
||||
sideBar:SetSelectedObject(collectionToIndex[currentShownCollection])
|
||||
ScreenManager:OpenScreen(sideBar, false)
|
||||
end)
|
||||
end
|
||||
|
||||
--[[ Input Events ]]--
|
||||
PlayButton.MouseButton1Click:connect(function()
|
||||
SoundManager:Play('ButtonPress')
|
||||
local gameData = nil
|
||||
if isUseNewCarouselInXboxAppEnabled then
|
||||
if myCarouselController then
|
||||
gameData = myCarouselController:GetCurrentFocusGameData()
|
||||
end
|
||||
else
|
||||
gameData = currentSortCarousel:GetCurrentSelectedGameData()
|
||||
end
|
||||
|
||||
if gameData then
|
||||
local placeId = gameData.PlaceId
|
||||
local creatorUserId = gameData.CreatorUserId
|
||||
if canJoinGame and returnedFromGame then
|
||||
canJoinGame = false
|
||||
GameJoinModule:StartGame(GameJoinModule.JoinType.Normal, placeId, creatorUserId)
|
||||
canJoinGame = true
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
FavoriteButton.MouseButton1Click:connect(function()
|
||||
SoundManager:Play('ButtonPress')
|
||||
local gameData = nil
|
||||
if isUseNewCarouselInXboxAppEnabled then
|
||||
if myCarouselController then
|
||||
gameData = myCarouselController:GetCurrentFocusGameData()
|
||||
end
|
||||
else
|
||||
gameData = currentItemData
|
||||
end
|
||||
|
||||
if gameData and gameData.GameData then
|
||||
local success, reason = gameData.GameData:PostFavoriteAsync()
|
||||
if success then
|
||||
gameData.IsFavorited = gameData.GameData:GetIsFavoritedByUser()
|
||||
toggleFavoriteButton(gameData.IsFavorited)
|
||||
elseif reason then
|
||||
local err = Errors.Favorite[reason]
|
||||
ScreenManager:OpenScreen(ErrorOverlayModule(err), false)
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
--[[ Public API ]]--
|
||||
function this:SetPosition(newPosition)
|
||||
GameGenreContainer.Position = newPosition
|
||||
end
|
||||
|
||||
function this:SetParent(newParent)
|
||||
GameGenreContainer.Parent = newParent
|
||||
end
|
||||
|
||||
function this:GetName()
|
||||
return TitleLabel.Text
|
||||
end
|
||||
|
||||
function this:Show()
|
||||
GameGenreContainer.Visible = true
|
||||
|
||||
local prevScreen = ScreenManager:GetScreenBelow(self)
|
||||
if prevScreen and prevScreen.GetName then
|
||||
BackText.Text = prevScreen:GetName()
|
||||
else
|
||||
BackText.Text = ''
|
||||
end
|
||||
|
||||
ScreenManager:DefaultCancelFade(this.TransitionTweens)
|
||||
self.TransitionTweens = ScreenManager:DefaultFadeIn(GameGenreContainer)
|
||||
ScreenManager:PlayDefaultOpenSound()
|
||||
end
|
||||
|
||||
function this:Hide()
|
||||
GameGenreContainer.Visible = false
|
||||
ScreenManager:DefaultCancelFade(self.TransitionTweens)
|
||||
self.TransitionTweens = nil
|
||||
end
|
||||
|
||||
function this:Destroy()
|
||||
if onNewGameSelectedCn then
|
||||
onNewGameSelectedCn:disconnect()
|
||||
onNewGameSelectedCn = nil
|
||||
end
|
||||
if not isUseNewCarouselInXboxAppEnabled then
|
||||
currentSortCarousel:Destroy()
|
||||
end
|
||||
VoteWidget:Destroy()
|
||||
GameGenreContainer:Destroy()
|
||||
end
|
||||
|
||||
function this:IsFocused()
|
||||
return inFocus
|
||||
end
|
||||
|
||||
function this:Focus()
|
||||
inFocus = true
|
||||
if isUseNewCarouselInXboxAppEnabled then
|
||||
local selection = myCarouselView:GetFocusItem() or SideBarButton
|
||||
GuiService.SelectedCoreObject = selection
|
||||
else
|
||||
if self.SavedSelectedObject and self.SavedSelectedObject:IsDescendantOf(GameGenreContainer) then
|
||||
GuiService.SelectedCoreObject = self.SavedSelectedObject
|
||||
end
|
||||
end
|
||||
|
||||
ContextActionService:BindCoreAction("ReturnFromGameGenreScreen",
|
||||
function(actionName, inputState, inputObject)
|
||||
if inputState == Enum.UserInputState.End then
|
||||
ScreenManager:CloseCurrent()
|
||||
self:Destroy()
|
||||
end
|
||||
end,
|
||||
false,
|
||||
Enum.KeyCode.ButtonB)
|
||||
|
||||
if PlatformService then
|
||||
dataModelViewChangedCn = PlatformService.ViewChanged:connect(function(viewType)
|
||||
if viewType == 0 then
|
||||
returnedFromGame = false
|
||||
wait(1)
|
||||
returnedFromGame = true
|
||||
end
|
||||
end)
|
||||
end
|
||||
if isUseNewCarouselInXboxAppEnabled then
|
||||
if myCarouselController then
|
||||
newGameSelectedCn = myCarouselController.NewItemSelected:connect(onNewGameSelected)
|
||||
onNewGameSelected(myCarouselController:GetCurrentFocusGameData())
|
||||
myCarouselController:Connect()
|
||||
end
|
||||
end
|
||||
spawn(function()
|
||||
createSideBarAsync()
|
||||
end)
|
||||
end
|
||||
|
||||
function this:RemoveFocus()
|
||||
inFocus = false
|
||||
local selectedObject = GuiService.SelectedCoreObject
|
||||
if selectedObject and selectedObject:IsDescendantOf(GameGenreContainer) then
|
||||
self.SavedSelectedObject = selectedObject
|
||||
GuiService.SelectedCoreObject = nil
|
||||
end
|
||||
ContextActionService:UnbindCoreAction("ReturnFromGameGenreScreen")
|
||||
dataModelViewChangedCn = Utility.DisconnectEvent(dataModelViewChangedCn)
|
||||
newGameSelectedCn = Utility.DisconnectEvent(newGameSelectedCn)
|
||||
sideBarSelectedCn = Utility.DisconnectEvent(sideBarSelectedCn)
|
||||
if isUseNewCarouselInXboxAppEnabled then
|
||||
if myCarouselController then
|
||||
myCarouselController:Disconnect()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if isUseNewCarouselInXboxAppEnabled then
|
||||
initializeCarouselView()
|
||||
else
|
||||
setCurrentSortCarousel()
|
||||
end
|
||||
|
||||
return this
|
||||
end
|
||||
|
||||
return CreateGameGenre
|
||||
@@ -0,0 +1,48 @@
|
||||
--[[
|
||||
// GameJoin.lua
|
||||
|
||||
// Handles game join logic
|
||||
]]
|
||||
local CoreGui = Game:GetService("CoreGui")
|
||||
local GuiRoot = CoreGui:FindFirstChild("RobloxGui")
|
||||
local Modules = GuiRoot:FindFirstChild("Modules")
|
||||
local PlatformService = nil
|
||||
pcall(function() PlatformService = game:GetService('PlatformService') end)
|
||||
|
||||
local Errors = require(Modules:FindFirstChild('Errors'))
|
||||
local ErrorOverlayModule = require(Modules:FindFirstChild('ErrorOverlay'))
|
||||
local ScreenManager = require(Modules:FindFirstChild('ScreenManager'))
|
||||
local UserData = require(Modules:FindFirstChild('UserData'))
|
||||
|
||||
local GameJoin = {}
|
||||
|
||||
GameJoin.JoinType = {
|
||||
Normal = 0; -- use placeId
|
||||
GameInstance = 1; -- use game instance id
|
||||
Follow = 2; -- use userId or user you are following
|
||||
PMPCreator = 3; -- use placeId, used when a player joins their own place
|
||||
}
|
||||
|
||||
-- joinType - GameJoin.JoinType
|
||||
-- joinId - can be a userId or placeId, see JoinType for which one to use
|
||||
function GameJoin:StartGame(joinType, joinId, creatorUserId)
|
||||
if UserSettings().GameSettings:InStudioMode() then
|
||||
ScreenManager:OpenScreen(ErrorOverlayModule(Errors.Test.CannotJoinGame), false)
|
||||
else
|
||||
local success, result = pcall(function()
|
||||
-- check if we are the creator for normal joins
|
||||
if joinType == self.JoinType.Normal and creatorUserId == UserData:GetRbxUserId() then
|
||||
joinType = self.JoinType.PMPCreator
|
||||
end
|
||||
|
||||
return PlatformService:BeginStartGame3(joinType, joinId)
|
||||
end)
|
||||
-- catch pcall error, something went wrong with call into API
|
||||
-- all other game join errors are caught in AppHome.lua
|
||||
if not success then
|
||||
ScreenManager:OpenScreen(ErrorOverlayModule(Errors.GameJoin[#Errors.GameJoin]), false)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return GameJoin
|
||||
@@ -0,0 +1,233 @@
|
||||
-- Copyright ROBLOX 2015
|
||||
--[[
|
||||
Filename: GamePane.lua
|
||||
Written By: Kyler Mulherin, Jason Roth
|
||||
|
||||
TODO:
|
||||
Make responsive
|
||||
]]
|
||||
|
||||
local CoreGui = Game:GetService("CoreGui")
|
||||
local GuiRoot = CoreGui:FindFirstChild("RobloxGui")
|
||||
local Modules = GuiRoot:FindFirstChild("Modules")
|
||||
|
||||
local Utility = require(Modules:FindFirstChild('Utility'))
|
||||
local GlobalSettings = require(Modules:FindFirstChild('GlobalSettings'))
|
||||
local GuiService = game:GetService('GuiService')
|
||||
|
||||
local GameSort = require(Modules:FindFirstChild('GameSort'))
|
||||
local SortData = require(Modules:FindFirstChild('SortData'))
|
||||
local ScrollingGrid = require(Modules:FindFirstChild('ScrollingGrid'))
|
||||
local EventHub = require(Modules:FindFirstChild('EventHub'))
|
||||
local ScreenManager = require(Modules:FindFirstChild('ScreenManager'))
|
||||
local Strings = require(Modules:FindFirstChild('LocalizedStrings'))
|
||||
local LoadingWidget = require(Modules:FindFirstChild('LoadingWidget'))
|
||||
local GameCollection = require(Modules:FindFirstChild('GameCollection'))
|
||||
|
||||
--CONSTANTS
|
||||
local SPACING = 33
|
||||
|
||||
local function CreateGamePane(parent)
|
||||
local this = {}
|
||||
|
||||
local sortCatagories = nil
|
||||
local imageSize = UDim2.new(0, 298, 0, 298)
|
||||
local spacing = Vector2.new(14, 14)
|
||||
local rows, columns = 2, 2
|
||||
local views = {}
|
||||
|
||||
|
||||
local inFocus = false
|
||||
|
||||
local noSelectionObject = Utility.Create'ImageLabel'
|
||||
{
|
||||
BackgroundTransparency = 1;
|
||||
}
|
||||
|
||||
-- UI Elements
|
||||
local GamePaneContainer = Utility.Create'Frame'
|
||||
{
|
||||
Name = 'GamePane';
|
||||
Size = UDim2.new(1, 0, 1, 0);
|
||||
BackgroundTransparency = 1;
|
||||
Parent = parent;
|
||||
SelectionImageObject = noSelectionObject;
|
||||
Visible = false;
|
||||
}
|
||||
|
||||
local gameSortsPane = ScrollingGrid()
|
||||
gameSortsPane:SetPosition(UDim2.new(0, 0, 0, 0))
|
||||
gameSortsPane:SetCellSize(Vector2.new(610, 646))
|
||||
gameSortsPane:SetSize(UDim2.new(1, GlobalSettings.TitleSafeInset.X.Offset, 1, 0))
|
||||
gameSortsPane:SetScrollDirection(gameSortsPane.Enum.ScrollDirection.Horizontal)
|
||||
gameSortsPane:SetParent(GamePaneContainer)
|
||||
gameSortsPane:SetClipping(false)
|
||||
gameSortsPane:SetSpacing(Vector2.new(SPACING, SPACING))
|
||||
gameSortsPane.Container.Visible = false
|
||||
|
||||
local function setSelectionOnLoad()
|
||||
if this:IsFocused() then
|
||||
GuiService.SelectedCoreObject = this:GetDefaultSelection()
|
||||
end
|
||||
end
|
||||
|
||||
local setSortsDebounce = false
|
||||
local function setSortView()
|
||||
if setSortsDebounce then return end
|
||||
setSortsDebounce = true
|
||||
if not sortCatagories then
|
||||
spawn(function()
|
||||
sortCatagories = SortData.GetSortCategoriesAsync()
|
||||
end)
|
||||
end
|
||||
|
||||
local function createView(gameCollection, name)
|
||||
local sortPage = gameCollection:GetSortAsync(0, 5)
|
||||
|
||||
if sortPage and sortPage.Count > 0 then
|
||||
local view = GameSort:CreateGridView(UDim2.new(), imageSize, spacing, rows, columns)
|
||||
local iconIds = sortPage:GetPageIconIds()
|
||||
local names = sortPage:GetPagePlaceNames()
|
||||
local placeIds = sortPage:GetPagePlaceIds()
|
||||
view:SetImages(iconIds)
|
||||
view:ConnectInput(placeIds, names, iconIds, name, gameCollection)
|
||||
view:SetTitle(name)
|
||||
return view
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
local newViews = {}
|
||||
local function loadSortsAsync()
|
||||
while not sortCatagories do
|
||||
wait()
|
||||
end
|
||||
|
||||
-- create all the views
|
||||
for i = 1, #sortCatagories do
|
||||
local sort = sortCatagories[i]
|
||||
local sortCollection = GameCollection:GetSort(sort["Id"])
|
||||
table.insert(newViews, createView(sortCollection, sort["Name"]))
|
||||
end
|
||||
|
||||
local favoriteGamesCollection = GameCollection:GetUserFavorites()
|
||||
table.insert(newViews, createView(favoriteGamesCollection, Strings:LocalizedString("FavoritesSortTitle")))
|
||||
|
||||
local recentGamesCollection = GameCollection:GetUserRecent()
|
||||
table.insert(newViews, createView(recentGamesCollection, Strings:LocalizedString("RecentlyPlayedSortTitle")))
|
||||
|
||||
local userPlacesCollection = GameCollection:GetUserPlaces()
|
||||
table.insert(newViews, createView(userPlacesCollection, Strings:LocalizedString("PlayMyPlaceMoreGamesTitle")))
|
||||
end
|
||||
local loader = LoadingWidget(
|
||||
{ Parent = GamePaneContainer }, { loadSortsAsync })
|
||||
spawn(function()
|
||||
loader:AwaitFinished()
|
||||
loader:Cleanup()
|
||||
loader = nil
|
||||
|
||||
-- Remove old items
|
||||
gameSortsPane:RemoveAllItems()
|
||||
views = newViews
|
||||
for i = 1, #views do
|
||||
gameSortsPane:AddItem(views[i]:GetContainer())
|
||||
end
|
||||
gameSortsPane.Container.Visible = true
|
||||
setSelectionOnLoad()
|
||||
if GamePaneContainer.Visible then
|
||||
this.TransitionTweens = ScreenManager:DefaultFadeIn(gameSortsPane.Container)
|
||||
ScreenManager:PlayDefaultOpenSound()
|
||||
end
|
||||
|
||||
setSortsDebounce = false
|
||||
end)
|
||||
end
|
||||
|
||||
-- SCREEN FUNCTIONS
|
||||
function this:SetPosition(newPosition)
|
||||
GamePaneContainer.Position = newPosition
|
||||
end
|
||||
function this:SetParent(newParent)
|
||||
GamePaneContainer.Parent = newParent
|
||||
end
|
||||
|
||||
function this:IsAncestorOf(object)
|
||||
return GamePaneContainer and GamePaneContainer:IsAncestorOf(object)
|
||||
end
|
||||
|
||||
function this:GetName()
|
||||
return Strings:LocalizedString('GameWord')
|
||||
end
|
||||
|
||||
function this:GetDefaultSelection()
|
||||
for i = 1, #views do
|
||||
if views[i]:GetDefaultSelection() then
|
||||
return views[i]:GetDefaultSelection()
|
||||
end
|
||||
end
|
||||
return GamePaneContainer
|
||||
end
|
||||
|
||||
function this:ViewsContainObject(guiObject)
|
||||
if guiObject then
|
||||
for i = 1, #views do
|
||||
if views[i]:Contains(guiObject) then
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function this:IsFocused()
|
||||
return inFocus
|
||||
end
|
||||
|
||||
function this:Show()
|
||||
ScreenManager:DefaultCancelFade(self.TransitionTweens)
|
||||
setSortView()
|
||||
GamePaneContainer.Visible = true
|
||||
end
|
||||
function this:Hide()
|
||||
GamePaneContainer.Visible = false
|
||||
gameSortsPane.Container.Visible = false
|
||||
|
||||
ScreenManager:DefaultCancelFade(self.TransitionTweens)
|
||||
self.TransitionTweens = nil
|
||||
end
|
||||
function this:Focus()
|
||||
inFocus = true
|
||||
|
||||
if self.SavedSelectObject and self:ViewsContainObject(self.SavedSelectObject) then
|
||||
GuiService.SelectedCoreObject = self.SavedSelectObject
|
||||
else
|
||||
GuiService.SelectedCoreObject = self:GetDefaultSelection()
|
||||
end
|
||||
|
||||
-- Clear out the saved selected object, it has done its work
|
||||
self.SavedSelectObject = nil
|
||||
end
|
||||
function this:RemoveFocus()
|
||||
inFocus = false
|
||||
|
||||
local selectedObject = GuiService.SelectedCoreObject
|
||||
if selectedObject and self:ViewsContainObject(selectedObject) then
|
||||
self.SavedSelectObject = selectedObject
|
||||
else
|
||||
self.SavedSelectObject = nil
|
||||
end
|
||||
|
||||
if selectedObject and (selectedObject == GamePaneContainer or self:IsAncestorOf(selectedObject)) then
|
||||
GuiService.SelectedCoreObject = nil
|
||||
end
|
||||
end
|
||||
|
||||
--[[ Initialize - Don't Block ]]--
|
||||
spawn(function()
|
||||
sortCatagories = SortData.GetSortCategoriesAsync()
|
||||
end)
|
||||
|
||||
return this
|
||||
end
|
||||
|
||||
return CreateGamePane
|
||||
@@ -0,0 +1,283 @@
|
||||
--[[
|
||||
// GameSort.lua
|
||||
// Creates a grid layout for a game sort
|
||||
]]
|
||||
|
||||
local CoreGui = Game:GetService("CoreGui")
|
||||
local GuiRoot = CoreGui:FindFirstChild("RobloxGui")
|
||||
local Modules = GuiRoot:FindFirstChild("Modules")
|
||||
local GuiService = game:GetService('GuiService')
|
||||
|
||||
local Utility = require(Modules:FindFirstChild('Utility'))
|
||||
local GlobalSettings = require(Modules:FindFirstChild('GlobalSettings'))
|
||||
local EventHub = require(Modules:FindFirstChild('EventHub'))
|
||||
local ScreenManager = require(Modules:FindFirstChild('ScreenManager'))
|
||||
local SoundManager = require(Modules:FindFirstChild('SoundManager'))
|
||||
local AssetManager = require(Modules:FindFirstChild('AssetManager'))
|
||||
local PopupText = require(Modules:FindFirstChild('PopupText'))
|
||||
local ThumbnailLoader = require(Modules:FindFirstChild('ThumbnailLoader'))
|
||||
local Strings = require(Modules:FindFirstChild('LocalizedStrings'))
|
||||
local ErrorOverlay = require(Modules:FindFirstChild('ErrorOverlay'))
|
||||
local GameCollection = require(Modules:FindFirstChild('GameCollection'))
|
||||
|
||||
local GameSort = {}
|
||||
|
||||
local function createSortContainer(size)
|
||||
local sortContainer = Utility.Create'Frame'
|
||||
{
|
||||
Name = "SortContainer";
|
||||
Size = size;
|
||||
BackgroundTransparency = 1;
|
||||
}
|
||||
|
||||
return sortContainer
|
||||
end
|
||||
|
||||
local function createSortTitle(name)
|
||||
local sortTitleLabel = Utility.Create'TextLabel'
|
||||
{
|
||||
Name = "SortTitle";
|
||||
Size = UDim2.new(1, 0, 0, 36);
|
||||
Position = UDim2.new(0, 0, 0, 0);
|
||||
BackgroundTransparency = 1;
|
||||
Font = GlobalSettings.RegularFont;
|
||||
FontSize = GlobalSettings.SubHeaderSize;
|
||||
TextXAlignment = Enum.TextXAlignment.Left;
|
||||
TextYAlignment = Enum.TextYAlignment.Top;
|
||||
TextColor3 = GlobalSettings.WhiteTextColor;
|
||||
Text = string.upper(name);
|
||||
Parent = sortContainer;
|
||||
}
|
||||
|
||||
return sortTitleLabel
|
||||
end
|
||||
|
||||
local function createMoreButton(margin, parent)
|
||||
-- we override the selection on moreButton to fit around the moreImage
|
||||
local overrideSelection = Utility.Create'ImageLabel'
|
||||
{
|
||||
Name = "OverrideSelection";
|
||||
Image = 'rbxasset://textures/ui/SelectionBox.png';
|
||||
ScaleType = Enum.ScaleType.Slice;
|
||||
SliceCenter = Rect.new(19,19,43,43);
|
||||
BackgroundTransparency = 1;
|
||||
}
|
||||
|
||||
local moreButton = Utility.Create'ImageButton'
|
||||
{
|
||||
Name = "MoreButton";
|
||||
Size = UDim2.new(1, 0, 0, 50);
|
||||
Position = UDim2.new(0, 0, 1, margin);
|
||||
BackgroundTransparency = 1;
|
||||
ZIndex = 2;
|
||||
Visible = false;
|
||||
SelectionImageObject = overrideSelection;
|
||||
Parent = parent;
|
||||
SoundManager:CreateSound('MoveSelection');
|
||||
}
|
||||
local moreImage = Utility.Create'ImageLabel'
|
||||
{
|
||||
Name = "MoreImage";
|
||||
BackgroundTransparency = 1;
|
||||
BorderSizePixel = 0;
|
||||
ZIndex = 2;
|
||||
Parent = moreButton;
|
||||
}
|
||||
|
||||
local function updateMoreImage(isSelected)
|
||||
local uri = isSelected and 'rbxasset://textures/ui/Shell/Buttons/MoreButtonSelected' or 'rbxasset://textures/ui/Shell/Buttons/MoreButton'
|
||||
AssetManager.LocalImage(moreImage, uri, {['720'] = UDim2.new(0,72,0,33); ['1080'] = UDim2.new(0,108,0,50);})
|
||||
moreImage.Position = UDim2.new(1, -moreImage.Size.X.Offset, 0, 0)
|
||||
|
||||
overrideSelection.Size = UDim2.new(0, moreImage.Size.X.Offset + 14, 0, moreImage.Size.Y.Offset + 14)
|
||||
overrideSelection.Position = UDim2.new(1, -overrideSelection.Size.X.Offset + 7, 0, -7)
|
||||
end
|
||||
|
||||
moreButton.SelectionGained:connect(function()
|
||||
updateMoreImage(true)
|
||||
end)
|
||||
moreButton.SelectionLost:connect(function()
|
||||
updateMoreImage(false)
|
||||
end)
|
||||
|
||||
updateMoreImage(GuiService.SelectedCoreObject == moreButton)
|
||||
|
||||
return moreButton
|
||||
end
|
||||
|
||||
local function createImageButton(size, position)
|
||||
return Utility.Create'ImageButton'
|
||||
{
|
||||
Name = "GameThumbButton";
|
||||
Size = size;
|
||||
Position = position;
|
||||
BackgroundTransparency = 0;
|
||||
BorderSizePixel = 0;
|
||||
BackgroundColor3 = GlobalSettings.ModalBackgroundColor;
|
||||
ZIndex = 2;
|
||||
AssetManager.CreateShadow(1);
|
||||
SoundManager:CreateSound('MoveSelection');
|
||||
}
|
||||
end
|
||||
|
||||
local function createImageGrid(rows, columns, size, spacing, offset, images)
|
||||
for i = 1, rows do
|
||||
for j = 1, columns do
|
||||
local image = createImageButton(size,
|
||||
UDim2.new(0, (j - 1) * size.X.Offset + (j - 1) * spacing.x, 0, offset + (i - 1) * size.Y.Offset + (i - 1) * spacing.y))
|
||||
table.insert(images, image)
|
||||
image.Name = tostring(#images)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function createPopupText(images)
|
||||
local popupText = {}
|
||||
for i = 1, #images do
|
||||
local popup = PopupText(images[i], "")
|
||||
table.insert(popupText, popup)
|
||||
end
|
||||
|
||||
return popupText
|
||||
end
|
||||
|
||||
local function setImages(imageIds, images)
|
||||
local size = ThumbnailLoader.Sizes.Medium
|
||||
local assetType = ThumbnailLoader.AssetType.Icon
|
||||
for i = 1, #images do
|
||||
if imageIds[i] then
|
||||
local thumbLoader = ThumbnailLoader:Create(images[i], imageIds[i], size, assetType)
|
||||
spawn(function()
|
||||
if not thumbLoader:LoadAsync(true, false) then
|
||||
-- TODO
|
||||
end
|
||||
end)
|
||||
else
|
||||
images[i].BackgroundColor3 = GlobalSettings.GreyButtonColor
|
||||
images[i].Image = ""
|
||||
local image = Utility.Create'ImageLabel'
|
||||
{
|
||||
Name = "NoGameImage";
|
||||
Size = UDim2.new(0, 102, 0, 102);
|
||||
BackgroundTransparency = 1;
|
||||
Image = 'rbxasset://textures/ui/Shell/Icons/GamePlusIcon.png';
|
||||
ZIndex = images[i].ZIndex;
|
||||
Parent = images[i];
|
||||
}
|
||||
Utility.CalculateAnchor(image, UDim2.new(0.5, 0, 0.5, 0), Utility.Enum.Anchor.Center)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function createBaseGrid(size, spacing, images)
|
||||
local this = {}
|
||||
|
||||
this.Container = createSortContainer(size)
|
||||
this.Title = createSortTitle("")
|
||||
this.Title.Parent = this.Container
|
||||
this.MoreButton = createMoreButton(spacing, this.Container)
|
||||
|
||||
for i = 1, #images do
|
||||
images[i].Parent = this.Container
|
||||
end
|
||||
local popupText = createPopupText(images)
|
||||
|
||||
function this:SetParent(newParent)
|
||||
self.Container.Parent = newParent
|
||||
end
|
||||
function this:GetContainer()
|
||||
return self.Container
|
||||
end
|
||||
function this:SetPosition(newPosition)
|
||||
self.Container.Position = newPosition
|
||||
end
|
||||
function this:SetTitle(newTitle)
|
||||
self.Title.Text = string.upper(newTitle)
|
||||
end
|
||||
function this:SetVisible(value)
|
||||
self.Container.Visible = value
|
||||
end
|
||||
function this:SetImages(imageIds)
|
||||
setImages(imageIds, images)
|
||||
end
|
||||
function this:ConnectInput(ids, names, iconIds, sortName, gameCollection)
|
||||
for i = 1, #images do
|
||||
if ids[i] then
|
||||
images[i].MouseButton1Click:connect(function()
|
||||
EventHub:dispatchEvent(EventHub.Notifications["OpenGameDetail"], ids[i], names[i], iconIds[i])
|
||||
end)
|
||||
else
|
||||
images[i].MouseButton1Click:connect(function()
|
||||
-- If there are not enough games in my games section then let them know how to make more!
|
||||
if gameCollection == GameCollection:GetUserPlaces() then
|
||||
local titleAndMsg = {
|
||||
Title = Strings:LocalizedString('PlayMyPlaceMoreGamesTitle');
|
||||
Msg = Strings:LocalizedString('PlayMyPlaceMoreGamesPhrase');
|
||||
}
|
||||
ScreenManager:OpenScreen(ErrorOverlay(titleAndMsg, true), false)
|
||||
else
|
||||
-- if there is no game to load in this slot, we're going to redirect to the featured sort if the user presses this button
|
||||
EventHub:dispatchEvent(EventHub.Notifications["OpenGameGenre"], Strings:LocalizedString('FeaturedTitle'), GameCollection:GetSort(3))
|
||||
end
|
||||
end)
|
||||
end
|
||||
if popupText[i] then
|
||||
popupText[i]:SetText(names[i] or Strings:LocalizedString("MoreGamesPhrase"))
|
||||
end
|
||||
end
|
||||
self.MoreButton.Visible = #ids > #images
|
||||
self.MoreButton.MouseButton1Click:connect(function()
|
||||
EventHub:dispatchEvent(EventHub.Notifications["OpenGameGenre"], sortName, gameCollection)
|
||||
end)
|
||||
end
|
||||
function this:GetDefaultSelection()
|
||||
local default = nil
|
||||
if #images > 0 then
|
||||
default = images[1]
|
||||
end
|
||||
return default
|
||||
end
|
||||
function this:Contains(guiObject)
|
||||
for i = 1, #images do
|
||||
if images[i] == guiObject then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
function this:Destroy()
|
||||
self.Container:Destroy()
|
||||
end
|
||||
|
||||
return this
|
||||
end
|
||||
|
||||
function GameSort:CreateMainGridView(size, spacing, lgImageSize, smImageSize)
|
||||
local images = {}
|
||||
local mainImage = createImageButton(lgImageSize, UDim2.new(0, 0, 0, 36))
|
||||
table.insert(images, mainImage)
|
||||
mainImage.Name = tostring(#images)
|
||||
|
||||
for i = 1, 2 do
|
||||
local smImage = createImageButton(smImageSize,
|
||||
UDim2.new(0, (i - 1) * smImageSize.X.Offset + (i - 1) * spacing.x, 1, -smImageSize.Y.Offset))
|
||||
table.insert(images, smImage)
|
||||
smImage.Name = tostring(#images)
|
||||
end
|
||||
|
||||
local this = createBaseGrid(size, 12, images)
|
||||
|
||||
return this
|
||||
end
|
||||
|
||||
-- 2x2 grid for the games page
|
||||
function GameSort:CreateGridView(size, imageSize, spacing, rows, columns)
|
||||
local images = {}
|
||||
createImageGrid(rows, columns, imageSize, spacing, 36, images)
|
||||
|
||||
local this = createBaseGrid(size, 12, images)
|
||||
|
||||
return this
|
||||
end
|
||||
|
||||
return GameSort
|
||||
@@ -0,0 +1,96 @@
|
||||
-- Written by Kip Turner, Copyright ROBLOX 2015
|
||||
|
||||
local Settings =
|
||||
{
|
||||
-- As per microsoft reccomendations
|
||||
ActionSafeInset = UDim2.new((128 / 1920) * 0.5, 0, (64 / 1080) * 0.5, 0);
|
||||
TitleSafeInset = UDim2.new((72 / 1792) * 0.5, 0, (16 / 1080) * 0.5, 0);
|
||||
AbsoluteTitleSafeInset = UDim2.new((200 / 1920) * 0.5, 0, (80 / 1080) * 0.5, 0);
|
||||
|
||||
WhiteTextColor = Color3.new(1,1,1);
|
||||
GreyTextColor = Color3.new(0.5,0.5,0.5);
|
||||
LightGreyTextColor = Color3.new(184/255, 184/255, 184/255);
|
||||
BlueTextColor = Color3.new(0,116/255,189/255);
|
||||
BlackTextColor = Color3.new(0,0,0);
|
||||
GreenTextColor = Color3.new(2/255, 183/255, 87/255);
|
||||
RedTextColor = Color3.new(216/255, 104/255, 104/255);
|
||||
TextSelectedColor = Color3.new(19/255, 19/255, 19/255);
|
||||
|
||||
LineBreakColor = Color3.new(78/255, 78/255, 78/255);
|
||||
PageDivideColor = Color3.new(151/255, 151/255, 151/255);
|
||||
BadgeOwnedColor = Color3.new(45/255, 96/255, 128/255);
|
||||
BadgeOverlayColor = Color3.new(13/255, 28/255, 38/255);
|
||||
|
||||
OverlayColor = Color3.new(26/255, 57/255, 76/255);
|
||||
BadgeFrameColor = Color3.new(106/255, 120/255, 129/255);
|
||||
RobuxOverlayImageColor = Color3.new(42/255, 51/255, 57/255);
|
||||
|
||||
BlueButtonColor = Color3.new(50/255, 181/255, 1);
|
||||
GreySelectionColor = Color3.new(84/255, 99/255, 109/255);
|
||||
GreenButtonColor = Color3.new(2/255, 163/255, 77/255);
|
||||
GreenSelectedButtonColor = Color3.new(63/255, 198/255, 121/255);
|
||||
GreyButtonColor = Color3.new(78/255, 84/255, 96/255);
|
||||
GreySelectedButtonColor = Color3.new(50/255, 181/255, 1);
|
||||
|
||||
CharacterBackgroundColor = Color3.new(39/255, 69/255, 82/255);
|
||||
ForegroundGreyColor = Color3.new(58/255, 60/255, 64/255);
|
||||
BackgroundGreyColor = Color3.new(78/255, 84/255, 96/255);
|
||||
ModalBackgroundColor = Color3.new(0,0,0);
|
||||
AvatarBoxBackgroundColor = Color3.new(255/255,255/255,255/255);
|
||||
TabUnderlineColor = Color3.new(50/255,181/255,255/255);
|
||||
PriceLabelColor = Color3.new(241/255, 116/255, 10/255);
|
||||
|
||||
TextBoxColor = Color3.new(1, 1, 1);
|
||||
TextBoxSelectedTransparency = 0.5;
|
||||
TextBoxDefaultTransparency = 0.75;
|
||||
|
||||
AvatarBoxBackgroundSelectedTransparency = 0.75;
|
||||
AvatarBoxBackgroundDeselectedTransparency = 0.875;
|
||||
AvatarBoxTextSelectedTransparency = 0;
|
||||
AvatarBoxTextDeselectedTransparency = 0.5;
|
||||
ModalBackgroundTransparency = 0.3;
|
||||
FriendStatusTextTransparency = 0.5;
|
||||
|
||||
LargeHeadingSize = Enum.FontSize.Size48;
|
||||
MediumLargeHeadingSize = Enum.FontSize.Size36;
|
||||
MediumHeadingSize = Enum.FontSize.Size24;
|
||||
SmallHeadingSize = Enum.FontSize.Size18;
|
||||
ParagraphSize = Enum.FontSize.Size14;
|
||||
|
||||
-- Font Sizes
|
||||
-- RobloxSize -> Mockup Sizes
|
||||
LargeFontSize = Enum.FontSize.Size96; -- 72pt
|
||||
HeaderSize = Enum.FontSize.Size60; -- 48pt
|
||||
MediumFontSize = Enum.FontSize.Size48; -- 36pt
|
||||
TitleSize = Enum.FontSize.Size42; -- 34pt
|
||||
ButtonSize = Enum.FontSize.Size36; -- 30pt
|
||||
DescriptionSize = Enum.FontSize.Size32; -- 26pt
|
||||
SubHeaderSize = Enum.FontSize.Size28; -- 24pt
|
||||
SmallTitleSize = Enum.FontSize.Size24; -- 20pt
|
||||
|
||||
HeadingFont = Enum.Font.SourceSans;
|
||||
|
||||
-- Font Types
|
||||
RegularFont = Enum.Font.SourceSans;
|
||||
LightFont = Enum.Font.SourceSansLight;
|
||||
BoldFont = Enum.Font.SourceSansBold;
|
||||
ItalicFont = Enum.Font.SourceSansItalic;
|
||||
|
||||
TabItemSpacing = 30;
|
||||
|
||||
-- Values for tinting the background scenery
|
||||
SceneBrightness = 0.3;
|
||||
SceneContrast = 0.5;
|
||||
SceneGrayscaleLevel = 1;
|
||||
SceneTintColor = Color3.new(20.0 / 255.0, 43.0 / 255.0, 60.0 / 255.0);
|
||||
SceneBlurIntensity = 24;
|
||||
SceneMotionBlurIntensity = 3;
|
||||
|
||||
-- Screen priority
|
||||
DefaultPriority = 1;
|
||||
ElevatedPriority = 2;
|
||||
ImmediatePriority = 3;
|
||||
|
||||
}
|
||||
|
||||
return Settings
|
||||
@@ -0,0 +1,124 @@
|
||||
-- Written by Kip Turner, Copyright ROBLOX 2015
|
||||
|
||||
-- Herostats Manager
|
||||
|
||||
local PlatformService = nil
|
||||
pcall(function() PlatformService = game:GetService('PlatformService') end)
|
||||
|
||||
local CoreGui = Game:GetService("CoreGui")
|
||||
local GuiRoot = CoreGui:FindFirstChild("RobloxGui")
|
||||
local Modules = GuiRoot:FindFirstChild("Modules")
|
||||
|
||||
local SortDataModule = Modules:FindFirstChild('SortData')
|
||||
|
||||
local EventHub = require(Modules:FindFirstChild('EventHub'))
|
||||
local Http = require(Modules:FindFirstChild('Http'))
|
||||
local UserData = require(Modules:FindFirstChild('UserData'))
|
||||
local PlatformInterface = require(Modules:FindFirstChild('PlatformInterface'))
|
||||
|
||||
|
||||
local VIEW_GAMETYPE_ENUM =
|
||||
{
|
||||
AppShell = 0;
|
||||
Game = 1;
|
||||
}
|
||||
|
||||
|
||||
local HeroStatsManager = {}
|
||||
|
||||
|
||||
|
||||
function HeroStatsManager:SendHeroStatsEventAsync(heroStatName, setValue)
|
||||
print("HeroStatsManager - event name:" , heroStatName , "event value:" , setValue)
|
||||
local heroStatStatus = nil
|
||||
local success, msg = pcall(function()
|
||||
-- NOTE: Yielding function
|
||||
if PlatformService and not UserSettings().GameSettings:InStudioMode() then
|
||||
heroStatStatus = PlatformService:BeginHeroStat(heroStatName, setValue)
|
||||
end
|
||||
end)
|
||||
if not success then
|
||||
-- NOTE: very likely this function ever throws an error but returns error codes
|
||||
print("HeroStatsManager - event name:" , heroStatName , "value" , setValue , "for reason:" , msg)
|
||||
end
|
||||
|
||||
print("HeroStatsManager - event name:" , heroStatName , "event status:" , heroStatStatus)
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
local function UpdateEquippedPackagesAsync()
|
||||
-- print("Update Equipped Packages")
|
||||
local myUserId = UserData:GetRbxUserId()
|
||||
local packages = myUserId and Http.GetUserOwnedPackagesAsync(myUserId)
|
||||
local data = packages and packages['IsValid'] and packages['Data']
|
||||
local items = data and data['Items']
|
||||
|
||||
-- local Utility = require(Modules:FindFirstChild('Utility'))
|
||||
-- print("Update Equipped Packages DATA:" , Utility.PrettyPrint(myUserId), Utility.PrettyPrint(packages), Utility.PrettyPrint(data), Utility.PrettyPrint(items))
|
||||
if items then
|
||||
local numberOwnedPackages = #items
|
||||
HeroStatsManager:SendHeroStatsEventAsync("AvatarsEquipped", numberOwnedPackages)
|
||||
end
|
||||
end
|
||||
|
||||
local joinDebounce = false
|
||||
local function OnJoinedGameAsync()
|
||||
if joinDebounce then return end
|
||||
joinDebounce = true
|
||||
HeroStatsManager:SendHeroStatsEventAsync("GamesCount")
|
||||
joinDebounce = false
|
||||
end
|
||||
|
||||
|
||||
EventHub:addEventListener(EventHub.Notifications["DonnedDifferentPackage"], "HeroStatsManager",
|
||||
function(packageId)
|
||||
spawn(UpdateEquippedPackagesAsync)
|
||||
end)
|
||||
|
||||
EventHub:addEventListener(EventHub.Notifications["AuthenticationSuccess"], "HeroStatsManager",
|
||||
function()
|
||||
spawn(UpdateEquippedPackagesAsync)
|
||||
end)
|
||||
|
||||
if PlatformService then
|
||||
PlatformService.ViewChanged:connect(function(newView)
|
||||
if newView == VIEW_GAMETYPE_ENUM['Game'] then
|
||||
spawn(OnJoinedGameAsync)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
spawn(function()
|
||||
if UserSettings().GameSettings:InStudioMode() then return end
|
||||
|
||||
local last = nil
|
||||
|
||||
while true do
|
||||
local partyMembers = PlatformInterface:GetPartyMembersAsync()
|
||||
local inParty = PlatformInterface:IsInAParty(partyMembers)
|
||||
|
||||
local current = tick()
|
||||
if inParty then
|
||||
if last then
|
||||
if current - last > 60 then
|
||||
HeroStatsManager:SendHeroStatsEventAsync("PartyCount")
|
||||
last = last + 60
|
||||
end
|
||||
else
|
||||
last = current
|
||||
end
|
||||
else
|
||||
last = nil
|
||||
end
|
||||
|
||||
wait(60)
|
||||
end
|
||||
end)
|
||||
|
||||
|
||||
|
||||
return HeroStatsManager
|
||||
@@ -0,0 +1,483 @@
|
||||
-- Written by Kip Turner, Copyright ROBLOX 2015
|
||||
|
||||
local CoreGui = Game:GetService("CoreGui")
|
||||
local GuiService = game:GetService('GuiService')
|
||||
local PlayersService = game:GetService("Players")
|
||||
local RunService = game:GetService("RunService")
|
||||
local ContextActionService = game:GetService('ContextActionService')
|
||||
|
||||
local GuiRoot = CoreGui:FindFirstChild("RobloxGui")
|
||||
local Modules = GuiRoot:FindFirstChild("Modules")
|
||||
|
||||
local Utility = require(Modules:FindFirstChild('Utility'))
|
||||
local GlobalSettings = require(Modules:FindFirstChild('GlobalSettings'))
|
||||
local UserData = require(Modules:FindFirstChild('UserData'))
|
||||
local EventHub = require(Modules:FindFirstChild('EventHub'))
|
||||
local FriendsData = require(Modules:FindFirstChild('FriendsData'))
|
||||
local FriendsView = require(Modules:FindFirstChild('FriendsView'))
|
||||
local ScrollingGridModule = require(Modules:FindFirstChild('ScrollingGrid'))
|
||||
local GameSort = require(Modules:FindFirstChild('GameSort'))
|
||||
local ScreenManager = require(Modules:FindFirstChild('ScreenManager'))
|
||||
local SoundManager = require(Modules:FindFirstChild('SoundManager'))
|
||||
local AssetManager = require(Modules:FindFirstChild('AssetManager'))
|
||||
local LoadingWidget = require(Modules:FindFirstChild('LoadingWidget'))
|
||||
local Strings = require(Modules:FindFirstChild('LocalizedStrings'))
|
||||
local ThumbnailLoader = require(Modules:FindFirstChild('ThumbnailLoader'))
|
||||
local GameCollection = require(Modules:FindFirstChild("GameCollection"))
|
||||
|
||||
local MOCKUP_SIZE = Vector2.new(1920, 1080)
|
||||
local PROFILE_SIZE = Vector2.new(450, 343)
|
||||
local PROFILE_NAME_SIZE = Vector2.new(450, 38)
|
||||
local PROFILE_BUTTON_SIZE = Vector2.new(450, 300)
|
||||
local PROFILE_IMAGE_SIZE = Vector2.new(450, 300)
|
||||
local PROFILE_AVATAR_BRUSH_SIZE = Vector2.new(301, 149)
|
||||
local SORTS_SIZE = Vector2.new(1234, 608)
|
||||
|
||||
|
||||
local function CreateHomePane(parent)
|
||||
local this = {}
|
||||
|
||||
local inFocus = false
|
||||
|
||||
local sortsObjects = {}
|
||||
|
||||
|
||||
local HomePaneContainer = Utility.Create'Frame'
|
||||
{
|
||||
Name = 'HomePane';
|
||||
Size = UDim2.new(1, 0, 1, 0);
|
||||
BackgroundTransparency = 1;
|
||||
Parent = parent;
|
||||
}
|
||||
|
||||
local ProfileContainer = Utility.Create'Frame'
|
||||
{
|
||||
Name = 'ProfileContainer';
|
||||
Position = UDim2.new(0,0,0,0);
|
||||
BackgroundTransparency = 1;
|
||||
Parent = HomePaneContainer;
|
||||
}
|
||||
|
||||
local NameLabel = Utility.Create'TextLabel'
|
||||
{
|
||||
Name = 'NameLabel';
|
||||
Text = '';
|
||||
TextXAlignment = 'Left';
|
||||
TextYAlignment = 'Top';
|
||||
-- TextScaled = true;
|
||||
TextColor3 = GlobalSettings.WhiteTextColor;
|
||||
Font = GlobalSettings.RegularFont;
|
||||
FontSize = GlobalSettings.SubHeaderSize;
|
||||
BackgroundTransparency = 1;
|
||||
Parent = ProfileContainer;
|
||||
};
|
||||
local ProfileButton = Utility.Create'ImageButton'
|
||||
{
|
||||
Name = 'ProfileButton';
|
||||
AutoButtonColor = false;
|
||||
BackgroundTransparency = 1;
|
||||
Parent = ProfileContainer;
|
||||
|
||||
SoundManager:CreateSound('MoveSelection');
|
||||
}
|
||||
local ProfileImage = Utility.Create'ImageLabel'
|
||||
{
|
||||
Name = 'ProfileImage';
|
||||
Position = UDim2.new(0,0,0,0);
|
||||
BackgroundTransparency = 1;
|
||||
BorderSizePixel = 0;
|
||||
ZIndex = 3;
|
||||
Parent = ProfileButton;
|
||||
};
|
||||
local ProfileImageBackground = Utility.Create'Frame'
|
||||
{
|
||||
Name = 'ProfileImageBackground';
|
||||
Size = UDim2.new(1,0,1,0);
|
||||
BorderSizePixel = 0;
|
||||
BackgroundTransparency = 0;
|
||||
BackgroundColor3 = GlobalSettings.CharacterBackgroundColor;
|
||||
ZIndex = 2;
|
||||
Parent = ProfileImage;
|
||||
AssetManager.CreateShadow(1);
|
||||
}
|
||||
local AvatarBrushBackground = Utility.Create'Frame'
|
||||
{
|
||||
Name = 'AvatarBrushImage';
|
||||
BorderSizePixel = 0;
|
||||
BackgroundColor3 = GlobalSettings.AvatarBoxBackgroundColor;
|
||||
BackgroundTransparency = GlobalSettings.AvatarBoxBackgroundDeselectedTransparency;
|
||||
ZIndex = 2;
|
||||
-- Parent = ProfileButton;
|
||||
}
|
||||
local AvatarBrushImage = Utility.Create'ImageLabel'
|
||||
{
|
||||
Name = 'AvatarBrushImage';
|
||||
BorderSizePixel = 0;
|
||||
BackgroundTransparency = 1;
|
||||
ZIndex = 2;
|
||||
Parent = AvatarBrushBackground;
|
||||
};
|
||||
AssetManager.LocalImage(AvatarBrushImage,
|
||||
'rbxasset://textures/ui/Shell/Icons/CustomizeIcon', {['720'] = UDim2.new(0,31,0,32); ['1080'] = UDim2.new(0,47,0,48);})
|
||||
local AvatarTextLabel = Utility.Create'TextLabel'
|
||||
{
|
||||
Name = 'AvatarTextLabel';
|
||||
Text = Strings:LocalizedString('EditAvatarPhrase');
|
||||
TextColor3 = GlobalSettings.WhiteTextColor;
|
||||
Font = GlobalSettings.RegularFont;
|
||||
FontSize = GlobalSettings.ButtonSize;
|
||||
BackgroundTransparency = 1;
|
||||
ZIndex = 2;
|
||||
Parent = AvatarBrushBackground;
|
||||
};
|
||||
|
||||
ProfileButton.MouseButton1Click:connect(function()
|
||||
SoundManager:Play('ButtonPress')
|
||||
EventHub:dispatchEvent(EventHub.Notifications["NavigateToEquippedAvatar"])
|
||||
-- EventHub:dispatchEvent(EventHub.Notifications["OpenProfileDetail"], "AppHub");
|
||||
end)
|
||||
|
||||
local function OnProfileButtonSelectionChanged()
|
||||
local selectedObject = GuiService.SelectedCoreObject
|
||||
local newBackgroundTransparency = selectedObject == ProfileButton and
|
||||
GlobalSettings.AvatarBoxBackgroundSelectedTransparency or
|
||||
GlobalSettings.AvatarBoxBackgroundDeselectedTransparency
|
||||
local newTextTransparency = selectedObject == ProfileButton and
|
||||
GlobalSettings.AvatarBoxTextSelectedTransparency or
|
||||
GlobalSettings.AvatarBoxTextDeselectedTransparency
|
||||
|
||||
Utility.PropertyTweener(AvatarBrushBackground, 'BackgroundTransparency', newBackgroundTransparency, newBackgroundTransparency, 0, nil, true)
|
||||
Utility.PropertyTweener(AvatarTextLabel, 'TextTransparency', newTextTransparency, newTextTransparency, 0, nil, true)
|
||||
Utility.PropertyTweener(AvatarBrushImage, 'ImageTransparency', newTextTransparency, newTextTransparency, 0, nil, true)
|
||||
end
|
||||
|
||||
local existingThumbnailLoader = nil
|
||||
local function UpdateProfileInfo()
|
||||
local playerName = UserData:GetDisplayName()
|
||||
NameLabel.Text = playerName and playerName:upper() or ''
|
||||
-- ProfileImage.Image = UserData:GetAvatarUrl(420, 420)..'&cb='..tostring(tick())
|
||||
|
||||
local rbxuid = UserData:GetRbxUserId()
|
||||
|
||||
if rbxuid then
|
||||
if existingThumbnailLoader then
|
||||
existingThumbnailLoader:Cancel()
|
||||
end
|
||||
local thumbnailSize = ThumbnailLoader.Sizes.Medium
|
||||
local thumbLoader = ThumbnailLoader:Create(ProfileImage, rbxuid,
|
||||
thumbnailSize, ThumbnailLoader.AssetType.Avatar, true)
|
||||
existingThumbnailLoader = thumbLoader
|
||||
spawn(function()
|
||||
thumbLoader:LoadAsync()
|
||||
-- ProfileImage.ImageRectOffset = Vector2.new(thumbnailSize.X, 0)
|
||||
-- ProfileImage.ImageRectSize = Vector2.new(-thumbnailSize.X, (PROFILE_IMAGE_SIZE.Y/PROFILE_IMAGE_SIZE.X) * thumbnailSize.X)
|
||||
ProfileImage.ImageRectSize = Vector2.new(thumbnailSize.X, (PROFILE_IMAGE_SIZE.Y/PROFILE_IMAGE_SIZE.X) * thumbnailSize.X)
|
||||
end)
|
||||
end
|
||||
|
||||
-- local thumbLoader = ThumbnailLoader:Create(ProfileImage, data.AssetId, ThumbnailLoader.Sizes.Medium)
|
||||
-- spawn(function()
|
||||
-- thumbLoader:LoadAsync()
|
||||
-- end)
|
||||
end
|
||||
UpdateProfileInfo()
|
||||
|
||||
local FriendActivityContainer = Utility.Create'Frame'
|
||||
{
|
||||
Name = 'FriendActivityContainer';
|
||||
BackgroundTransparency = 1;
|
||||
Parent = HomePaneContainer;
|
||||
}
|
||||
local FriendActivityTitle = Utility.Create'TextLabel'
|
||||
{
|
||||
Name = 'FriendActivityTitle';
|
||||
Text = Strings:LocalizedString('FriendActivityWord'):upper();
|
||||
Size = UDim2.new(1,0,0,50);
|
||||
Position = UDim2.new(0,0,0,10);
|
||||
TextXAlignment = 'Left';
|
||||
TextColor3 = GlobalSettings.WhiteTextColor;
|
||||
Font = GlobalSettings.RegularFont;
|
||||
FontSize = GlobalSettings.SubHeaderSize;
|
||||
BackgroundTransparency = 1;
|
||||
Parent = FriendActivityContainer;
|
||||
};
|
||||
|
||||
local FriendsStatusMessage = Utility.Create'TextLabel'
|
||||
{
|
||||
Name = 'FriendsStatusMessage';
|
||||
Text = Strings:LocalizedString('NoFriendsOnlinePhrase');
|
||||
Size = UDim2.new(0.9,0,1,-125);
|
||||
Position = UDim2.new(0.05, 0, 0, 125);
|
||||
TextYAlignment = 'Top';
|
||||
TextColor3 = GlobalSettings.GreyTextColor;
|
||||
TextWrapped = true;
|
||||
TextTransparency = GlobalSettings.FriendStatusTextTransparency;
|
||||
Font = GlobalSettings.BoldFont;
|
||||
FontSize = GlobalSettings.DescriptionSize;
|
||||
BackgroundTransparency = 1;
|
||||
Visible = false;
|
||||
Parent = FriendActivityContainer;
|
||||
};
|
||||
|
||||
local friendsScroller = ScrollingGridModule()
|
||||
friendsScroller:SetSize(UDim2.new(1,0,1,-60))
|
||||
friendsScroller:SetRowColumnConstraint(1)
|
||||
friendsScroller:SetScrollDirection(friendsScroller.Enum.ScrollDirection.Vertical)
|
||||
friendsScroller:SetCellSize(Vector2.new(446,114))
|
||||
friendsScroller:SetSpacing(Vector2.new(0, 26))
|
||||
friendsScroller:SetPosition(UDim2.new(0,0,0,FriendActivityTitle.Position.Y.Offset + FriendActivityTitle.Size.Y.Offset))
|
||||
local friendScrollerContainer = friendsScroller:GetGuiObject()
|
||||
friendScrollerContainer.Visible = false
|
||||
friendsScroller:SetParent(FriendActivityContainer)
|
||||
|
||||
local function setFriendItems()
|
||||
local function onFriendsUpdated(friendCount)
|
||||
FriendsStatusMessage.Visible = friendCount < 1
|
||||
end
|
||||
|
||||
local friendsData = FriendsData.GetOnlineFriendsAsync()
|
||||
local myFriendsView = FriendsView(friendsScroller, friendsData, nil, onFriendsUpdated)
|
||||
onFriendsUpdated(#friendsData)
|
||||
end
|
||||
|
||||
local friendsLoader = LoadingWidget(
|
||||
{ Parent = FriendActivityContainer }, { setFriendItems } )
|
||||
|
||||
-- Don't Block
|
||||
spawn(function()
|
||||
friendsLoader:AwaitFinished()
|
||||
friendsLoader:Cleanup()
|
||||
friendScrollerContainer.Visible = true
|
||||
end)
|
||||
|
||||
local SortsContainer = Utility.Create'Frame'
|
||||
{
|
||||
Name = 'SortsContainer';
|
||||
BackgroundTransparency = 1;
|
||||
Parent = HomePaneContainer;
|
||||
}
|
||||
|
||||
local populateSortsDebounce = false
|
||||
local function PopulateSorts()
|
||||
if populateSortsDebounce then return end
|
||||
populateSortsDebounce = true
|
||||
local function loadGameSorts()
|
||||
while #sortsObjects > 0 do
|
||||
local sortObject = table.remove(sortsObjects)
|
||||
if sortObject then
|
||||
if this.SavedSelectedObject and this.SavedSelectedObject:IsDescendantOf(sortObject:GetContainer()) then
|
||||
this.SavedSelectedObject = nil
|
||||
end
|
||||
if GuiService.SelectedCoreObject and GuiService.SelectedCoreObject:IsDescendantOf(sortObject:GetContainer()) then
|
||||
if inFocus then
|
||||
GuiService.SelectedCoreObject = this:GetDefaultSelectionObject()
|
||||
end
|
||||
end
|
||||
sortObject:Destroy()
|
||||
end
|
||||
end
|
||||
|
||||
local favoriteGamesCollection = GameCollection:GetUserFavorites()
|
||||
local favoritesPage1 = favoriteGamesCollection:GetSortAsync(0, 4)
|
||||
|
||||
local recentGamesCollection = GameCollection:GetUserRecent()
|
||||
local recentlyPage1 = recentGamesCollection:GetSortAsync(0, 4)
|
||||
|
||||
local showRecent = recentlyPage1 and #recentlyPage1:GetPagePlaceIds() > 2
|
||||
local showFavorites = favoritesPage1 and #favoritesPage1:GetPagePlaceIds() > 2
|
||||
|
||||
local function setGridView(view, page, title, collection)
|
||||
if page then
|
||||
local names = page:GetPagePlaceNames()
|
||||
local placeIds = page:GetPagePlaceIds()
|
||||
local iconIds = page:GetPageIconIds()
|
||||
view:SetImages(iconIds)
|
||||
view:ConnectInput(placeIds, names, iconIds, title, collection)
|
||||
end
|
||||
view:SetTitle(title)
|
||||
view:SetVisible(false)
|
||||
view:SetParent(SortsContainer)
|
||||
table.insert(sortsObjects, view)
|
||||
end
|
||||
|
||||
local function createMainSortGrid(title, page, collection)
|
||||
local view = GameSort:CreateMainGridView(UDim2.new(0, 378, 1, 0), Vector2.new(10, 10),
|
||||
UDim2.new(1, 0, 0, 378), UDim2.new(0, 184, 0, 184))
|
||||
setGridView(view, page, title, collection)
|
||||
|
||||
return view
|
||||
end
|
||||
local function createGridSortView(size, imageSize, spacing, rows, columns, page, title, collection)
|
||||
local view = GameSort:CreateGridView(size, imageSize, spacing, rows, columns)
|
||||
setGridView(view, page, title, collection)
|
||||
|
||||
return view
|
||||
end
|
||||
|
||||
local currentPosition = UDim2.new(0, 0, 0, 0)
|
||||
local margin = (showFavorites and showRecent) and 50 or 28
|
||||
|
||||
if showFavorites then
|
||||
local view = createMainSortGrid(Strings:LocalizedString('FavoritesSortTitle'), favoritesPage1, favoriteGamesCollection)
|
||||
view:SetPosition(currentPosition)
|
||||
currentPosition = currentPosition + UDim2.new(0, view:GetContainer().Size.X.Offset + margin, 0, 0)
|
||||
end
|
||||
if showRecent then
|
||||
local view = createMainSortGrid(Strings:LocalizedString('RecentlyPlayedSortTitle'), recentlyPage1, recentGamesCollection)
|
||||
view:SetPosition(currentPosition)
|
||||
currentPosition = currentPosition + UDim2.new(0, view:GetContainer().Size.X.Offset + margin, 0, 0)
|
||||
end
|
||||
|
||||
local featuredCollection = GameCollection:GetSort(GameCollection.DefaultSortId.Featured)
|
||||
local featuredTitle = Strings:LocalizedString('FeaturedTitle')
|
||||
local recommendedView = nil
|
||||
if showRecent and showFavorites then
|
||||
local page = featuredCollection:GetSortAsync(0, 4)
|
||||
recommendedView = createMainSortGrid(featuredTitle, page, featuredCollection)
|
||||
elseif showRecent or showFavorites then
|
||||
local page = featuredCollection:GetSortAsync(0, 7)
|
||||
recommendedView = createGridSortView(UDim2.new(0, 858, 1, 0), UDim2.new(0, 276, 0, 276), Vector2.new(15, 20),
|
||||
2, 3, page, featuredTitle, featuredCollection)
|
||||
else
|
||||
local page = featuredCollection:GetSortAsync(0, 9)
|
||||
recommendedView = createGridSortView(UDim2.new(0, 1236, 0, 648), UDim2.new(0, 300, 0, 300), Vector2.new(12, 12),
|
||||
2, 4, page, featuredTitle, featuredCollection)
|
||||
end
|
||||
recommendedView:SetPosition(currentPosition)
|
||||
end
|
||||
local loader = LoadingWidget(
|
||||
{Parent = SortsContainer}, {loadGameSorts})
|
||||
spawn(function()
|
||||
loader:AwaitFinished()
|
||||
loader:Cleanup()
|
||||
loader = nil
|
||||
populateSortsDebounce = false
|
||||
if this.TransitionTweens == nil or #this.TransitionTweens == 0 then
|
||||
this.TransitionTweens = ScreenManager:FadeInSitu(SelectableAvatarsContainer)
|
||||
end
|
||||
for i = 1, #sortsObjects do
|
||||
sortsObjects[i]:SetVisible(true)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
local function UpdateLayout()
|
||||
ProfileContainer.Size = Utility.CalculateRelativeDimensions(ProfileContainer, PROFILE_SIZE, MOCKUP_SIZE)
|
||||
|
||||
NameLabel.Size = Utility.CalculateRelativeDimensions(NameLabel, PROFILE_NAME_SIZE, MOCKUP_SIZE)
|
||||
|
||||
ProfileButton.Size = Utility.CalculateRelativeDimensions(ProfileButton, PROFILE_BUTTON_SIZE, MOCKUP_SIZE)
|
||||
Utility.CalculateAnchor(ProfileButton, UDim2.new(0, 0, 1, -6), Utility.Enum.Anchor.BottomLeft)
|
||||
|
||||
|
||||
ProfileImage.Size = Utility.CalculateRelativeDimensions(ProfileImage, PROFILE_IMAGE_SIZE, MOCKUP_SIZE)
|
||||
AvatarBrushBackground.Size = UDim2.new(1 - ProfileImage.Size.X.Scale, -ProfileImage.Size.X.Offset, 1, 0)
|
||||
-- AvatarBrushBackground.Size = Utility.CalculateRelativeDimensions(AvatarBrushBackground, PROFILE_AVATAR_BRUSH_SIZE, MOCKUP_SIZE)
|
||||
AvatarBrushBackground.Position = UDim2.new(1 - AvatarBrushBackground.Size.X.Scale, AvatarBrushBackground.Size.X.Offset, 0, 0)
|
||||
Utility.CalculateAnchor(AvatarBrushImage, UDim2.new(0.5, 0, 0.42, 0), Utility.Enum.Anchor.Center)
|
||||
Utility.CalculateAnchor(AvatarTextLabel, UDim2.new(0.5, 0, 0.7, 0), Utility.Enum.Anchor.Center)
|
||||
|
||||
|
||||
FriendActivityContainer.Size = UDim2.new(ProfileContainer.Size.X.Scale, 0, 0, 300);
|
||||
FriendActivityContainer.Position = UDim2.new(0,0,ProfileContainer.Size.Y.Scale,0);
|
||||
|
||||
SortsContainer.Size = Utility.CalculateRelativeDimensions(SortsContainer, SORTS_SIZE, MOCKUP_SIZE)
|
||||
SortsContainer.Position = UDim2.new(1 - SortsContainer.Size.X.Scale, 0, 0, 0)
|
||||
end
|
||||
|
||||
UpdateLayout()
|
||||
|
||||
local screenResolutionChangedConn = nil
|
||||
local profileButtonSelectedConn = nil
|
||||
local profileButtonDeselectedConn = nil
|
||||
|
||||
function this:GetName()
|
||||
return Strings:LocalizedString('HomeWord')
|
||||
end
|
||||
|
||||
function this:IsFocused()
|
||||
return inFocus
|
||||
end
|
||||
|
||||
function this:GetDefaultSelectionObject()
|
||||
return ProfileButton
|
||||
end
|
||||
|
||||
function this:SetSelectionObject()
|
||||
GuiService.SelectedCoreObject = self:GetDefaultSelectionObject()
|
||||
-- TODO: Remember selection while staying in Pane. Pressing A on the Home tab should remember
|
||||
-- last selection only while in pane.
|
||||
end
|
||||
|
||||
function this:Show()
|
||||
HomePaneContainer.Visible = true
|
||||
PopulateSorts()
|
||||
UpdateLayout()
|
||||
|
||||
Utility.DisconnectEvent(screenResolutionChangedConn)
|
||||
screenResolutionChangedConn = GuiRoot.Changed:connect(function(prop)
|
||||
if prop == 'AbsoluteSize' then
|
||||
RunService.RenderStepped:wait()
|
||||
UpdateLayout()
|
||||
end
|
||||
end)
|
||||
|
||||
Utility.DisconnectEvent(profileButtonSelectedConn)
|
||||
Utility.DisconnectEvent(profileButtonDeselectedConn)
|
||||
profileButtonSelectedConn = ProfileButton.SelectionGained:connect(OnProfileButtonSelectionChanged)
|
||||
profileButtonDeselectedConn = ProfileButton.SelectionLost:connect(OnProfileButtonSelectionChanged)
|
||||
OnProfileButtonSelectionChanged()
|
||||
UpdateProfileInfo()
|
||||
|
||||
ScreenManager:DefaultCancelFade(self.TransitionTweens)
|
||||
self.TransitionTweens = ScreenManager:DefaultFadeIn(HomePaneContainer)
|
||||
delay(0.5, function()
|
||||
if inFocus and GuiService.SelectedCoreObject == nil then
|
||||
self:SetSelectionObject()
|
||||
end
|
||||
end)
|
||||
ScreenManager:PlayDefaultOpenSound()
|
||||
end
|
||||
|
||||
function this:Hide()
|
||||
HomePaneContainer.Visible = false
|
||||
|
||||
profileButtonSelectedConn = Utility.DisconnectEvent(profileButtonSelectedConn)
|
||||
profileButtonDeselectedConn = Utility.DisconnectEvent(profileButtonDeselectedConn)
|
||||
screenResolutionChangedConn = Utility.DisconnectEvent(screenResolutionChangedConn)
|
||||
|
||||
ScreenManager:DefaultCancelFade(self.TransitionTweens)
|
||||
self.TransitionTweens = nil
|
||||
end
|
||||
|
||||
function this:Focus()
|
||||
inFocus = true
|
||||
UpdateLayout()
|
||||
self:SetSelectionObject()
|
||||
OnProfileButtonSelectionChanged()
|
||||
end
|
||||
|
||||
function this:RemoveFocus()
|
||||
inFocus = false
|
||||
local selectedObject = GuiService.SelectedCoreObject
|
||||
if selectedObject and selectedObject:IsDescendantOf(HomePaneContainer) then
|
||||
GuiService.SelectedCoreObject = nil
|
||||
end
|
||||
end
|
||||
|
||||
function this:SetPosition(newPosition)
|
||||
HomePaneContainer.Position = newPosition
|
||||
end
|
||||
|
||||
function this:SetParent(newParent)
|
||||
HomePaneContainer.Parent = newParent
|
||||
end
|
||||
|
||||
function this:IsAncestorOf(object)
|
||||
return HomePaneContainer and HomePaneContainer:IsAncestorOf(object)
|
||||
end
|
||||
|
||||
return this
|
||||
end
|
||||
|
||||
return CreateHomePane
|
||||
@@ -0,0 +1,764 @@
|
||||
--[[
|
||||
// Http.lua
|
||||
// API for all web endpoints
|
||||
// Calls are async
|
||||
|
||||
// Any calls to watrbx.wtf need to use game:HttpGetAynsc() and game:HttpPostAynsc()
|
||||
// use rbxGetAsync() and rbxPostAsync()
|
||||
// Any calls to api.watrbx.wtf should use HttpRbxApiService
|
||||
// use rbxApiGetAsync() and rbxApiPostAsync()
|
||||
|
||||
// NOTE: You cannot currently get thumbnails with this API (please see the Thumbnail module), because
|
||||
// Roblox GUIs cannot accept rbxcnd.com for the Image property.
|
||||
]]
|
||||
|
||||
--[[ Services ]]--
|
||||
local HttpService = game:GetService('HttpService')
|
||||
local HttpRbxApiService = game:GetService('HttpRbxApiService')
|
||||
|
||||
local Http = {}
|
||||
|
||||
local BaseUrl = game:GetService('ContentProvider').BaseUrl:lower()
|
||||
BaseUrl = string.gsub(BaseUrl, "/m.", "/www.")
|
||||
-- TODO: There are some calls that fail when using https. Wait for web to fix.
|
||||
BaseUrl = string.gsub(BaseUrl, "http://", "https://")
|
||||
|
||||
AssetGameBaseUrl = string.gsub(BaseUrl, "https://www.", "https://assetgame.")
|
||||
|
||||
Http.BaseUrl = BaseUrl
|
||||
Http.AssetGameBaseUrl = AssetGameBaseUrl
|
||||
|
||||
--[[ 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 rbxGetAsync(path, returnRaw)
|
||||
local success, result = pcall(function()
|
||||
return game:HttpGetAsync(path)
|
||||
end)
|
||||
--
|
||||
if not success then
|
||||
print(path, "rbxGetAsync() failed because", result)
|
||||
return nil
|
||||
end
|
||||
|
||||
if returnRaw then
|
||||
return result
|
||||
end
|
||||
return decodeJSON(result)
|
||||
end
|
||||
|
||||
local function rbxPostAsync(path, params, contentType)
|
||||
local success, result = pcall(function()
|
||||
return game:HttpPostAsync(path, params, contentType)
|
||||
end)
|
||||
--
|
||||
if not success then
|
||||
print(path, "rbxPostAsync() failed because", result)
|
||||
return nil
|
||||
end
|
||||
|
||||
return decodeJSON(result)
|
||||
end
|
||||
|
||||
local function rbxApiGetAsync(path, useHttps)
|
||||
local success, result = pcall(function()
|
||||
return HttpRbxApiService:GetAsync(path, useHttps)
|
||||
end)
|
||||
--
|
||||
if not success then
|
||||
print(path, "rbxApiGetAsync() failed because", result)
|
||||
return nil
|
||||
end
|
||||
|
||||
return decodeJSON(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
|
||||
print(path..params, "rbxApiPostAsync() failed because", result)
|
||||
return nil
|
||||
end
|
||||
|
||||
return decodeJSON(result)
|
||||
end
|
||||
|
||||
--[[ Public API ]]--
|
||||
|
||||
--[[ Helper Functions ]]--
|
||||
function Http.DecodeJSON(json)
|
||||
return decodeJSON(json)
|
||||
end
|
||||
|
||||
--[[ Games Endpoints ]]--
|
||||
|
||||
--[[
|
||||
// Return Array of tables
|
||||
// Table Keys
|
||||
// Id - number
|
||||
// Name - string
|
||||
// TimeOptionsAvailable - boolean
|
||||
// DefaultTimeOption - number
|
||||
// GenresOptionsAvailable - boolean
|
||||
]]
|
||||
function Http.GetGameSortsAsync()
|
||||
return rbxGetAsync(BaseUrl..'games/default-sorts')
|
||||
end
|
||||
|
||||
--[[
|
||||
All Sorts return the following json
|
||||
|
||||
// Returns Array of Tables
|
||||
// Table Keys
|
||||
// CreatorID - number
|
||||
// CreatorName- string
|
||||
// CreatorUrl - string
|
||||
// Plays - number
|
||||
// Price - number
|
||||
// ProductID - number
|
||||
// IsOwned - boolean
|
||||
// IsVotingEnabled - boolean
|
||||
// TotalUpVotes - number
|
||||
// TotalDownVotes - number
|
||||
// TotalBought - number
|
||||
// UniverseID - number
|
||||
// HasErrorOcurred - boolean
|
||||
// Name - string
|
||||
// PlaceID - number
|
||||
// PlayerCount - number
|
||||
// ImageId - number
|
||||
]]
|
||||
function Http.GetSortAsync(startRows, maxRows, sortId, timeFilter)
|
||||
local path = string.format("%sgames/list-json?sortFilter=%d&StartRows=%d&MaxRows=%d&searchAllGames=false&filterByDeviceType=true",
|
||||
BaseUrl, sortId, startRows, maxRows)
|
||||
|
||||
if timeFilter then
|
||||
path = string.format("%s&timeFilter=%d", path, timeFilter)
|
||||
end
|
||||
|
||||
return rbxGetAsync(path)
|
||||
end
|
||||
|
||||
function Http.GetUserFavoritesAsync(startRows, maxRows)
|
||||
local path = string.format("%sgames/moreresultsuncached-json?sortFilter=MyFavorite&StartRows=%d&MaxRows=%d&searchAllGames=true&filterByDeviceType=true",
|
||||
BaseUrl, startRows, maxRows)
|
||||
|
||||
return rbxGetAsync(path)
|
||||
end
|
||||
|
||||
function Http.GetUserRecentAsync(startRows, maxRows)
|
||||
local path = string.format("%sgames/moreresultsuncached-json?sortFilter=MyRecent&StartRows=%d&MaxRows=%d&searchAllGames=true&filterByDeviceType=true",
|
||||
BaseUrl, startRows, maxRows)
|
||||
|
||||
return rbxGetAsync(path)
|
||||
end
|
||||
|
||||
function Http.GetUserPlacesAsync(startIndex, pageSize, userid)
|
||||
local path = string.format("%sgames/list-users-games-json?userid=%d&startIndex=%d&pageSize=%d", BaseUrl, userid, startIndex, pageSize)
|
||||
|
||||
return rbxGetAsync(path)
|
||||
end
|
||||
|
||||
-- Here for future use
|
||||
function Http.SearchGamesAsync(startRows, maxRows, keyword)
|
||||
local path = string.format("%sgames/list-json?keyword=%s&StartRows=%d&MaxRows=%d", BaseUrl, startRows, maxRows)
|
||||
|
||||
return rbxGetAsync(path)
|
||||
end
|
||||
|
||||
--[[ Asset Endpoints ]]--
|
||||
|
||||
--[[
|
||||
// Returns Table
|
||||
// Table Keys
|
||||
// Url - string
|
||||
// Final - boolean
|
||||
]]
|
||||
function Http.GetAssetThumbnailFinalAsync(assetId, width, height, format)
|
||||
return rbxGetAsync(AssetGameBaseUrl..'asset-thumbnail/json?assetId='..tostring(assetId)..'&width='..tostring(width)..
|
||||
'&height='..tostring(height)..'&format='..format)
|
||||
end
|
||||
|
||||
--[[
|
||||
// See Above
|
||||
]]
|
||||
function Http.GetAssetAvatarFinalAsync(userId, width, height, format)
|
||||
local result = rbxGetAsync(BaseUrl..'avatar-thumbnail/json?userId='..tostring(userId)..'&width='..tostring(width)..
|
||||
'&height='..tostring(height)..'&format='..format)
|
||||
return result
|
||||
end
|
||||
|
||||
function Http.GetOutfitThumbnailFinalAsync(outfitId, width, height, format)
|
||||
local requestData = {['userOutfitId'] = outfitId}
|
||||
local requestDataJson = HttpService:JSONEncode(requestData)
|
||||
local encodedRequestData = requestDataJson and HttpService:UrlEncode(requestDataJson) or ""
|
||||
|
||||
local url = string.format('%savatar-thumbnails?params=%s', BaseUrl, encodedRequestData)
|
||||
print("Get outfit thumbnail url:", url)
|
||||
|
||||
local result = rbxGetAsync(url)
|
||||
return result
|
||||
end
|
||||
|
||||
--[[ Game Endpoints ]]--
|
||||
|
||||
--[[
|
||||
// Return Table
|
||||
// Table Keys
|
||||
// AssetId - number
|
||||
// Name - string
|
||||
// Description - string
|
||||
// Created - string date (m/dd/yyyy)
|
||||
// Updated - string date (m/dd/yyyy)
|
||||
// FavoritedCount - number
|
||||
// Url - string url ex : "/games/192800/Work-at-a-Pizza-Place"
|
||||
// ReportAbuseUrl - string url ex : "/abusereport/asset?id=192800&RedirectUrl=%2fgames%2f192800%2fWork-at-a-Pizza-Place",
|
||||
// IsFavoritedByUser - boolean
|
||||
// IsCreatedByUser - boolean
|
||||
// VisitedCount - number
|
||||
// MaxPlayers - number
|
||||
// Builder - string
|
||||
// BuilderId - number
|
||||
// BuilderUrl - string url ex: "/User.aspx?ID=82471"
|
||||
// IsPlayable - boolean
|
||||
// ReasonProhibited - string
|
||||
// ReasonProhibitedMessage - string
|
||||
// IsBuildersClubOnly - boolean
|
||||
// IsCopyLocked - boolean
|
||||
// IsPersonalServer - boolean
|
||||
// IsPersonalServerOverlay - boolean
|
||||
// BuildersClubOverlay - string
|
||||
// PlayButtonType - string (Enum?)
|
||||
// AssetGenre - string
|
||||
// AssetGenreViewModel - table
|
||||
// DisplayName - string
|
||||
// Id - number
|
||||
// OnlineCount - number
|
||||
// UniverseId - number
|
||||
// UniverseRootPlaceId - number
|
||||
// TotalUpVotes - number
|
||||
// TotalDownVotes - number
|
||||
// UserVote - null (?)
|
||||
// OverridesDefaultAvatar - boolean
|
||||
|
||||
]]
|
||||
function Http.GetGameDetailsAsync(placeId)
|
||||
return rbxGetAsync(BaseUrl..'places/api-get-details?assetId='..tostring(placeId))
|
||||
end
|
||||
|
||||
--[[
|
||||
// Returns Table
|
||||
// Table Keys
|
||||
// Id - number
|
||||
// PlaceId - number
|
||||
// ImageId - number
|
||||
// IconUrl - string
|
||||
// IconFinal - boolean
|
||||
// WikiUrl - string
|
||||
// ReleaseDate - string
|
||||
// IconUpdateSuccess - boolean
|
||||
// IconUpdateMessage - string
|
||||
]]
|
||||
function Http.GetGameIconIdAsync(placeId)
|
||||
return rbxGetAsync(BaseUrl..'places/icons/json?placeId='..tostring(placeId))
|
||||
end
|
||||
|
||||
--[[
|
||||
// Return Table
|
||||
// Table Keys
|
||||
// ShowVotes - boolean
|
||||
// VotingModel - table
|
||||
// ShowVotes - boolean
|
||||
// UpVotes - number
|
||||
// DownVotes - number
|
||||
// CanVote - boolean
|
||||
// UserVote - boolean / null if user has not voted on this game
|
||||
// ReasonForNotVoteable - string
|
||||
// InvalidAssetOrUser, EmailIsVerified, PlayGame
|
||||
]]
|
||||
function Http.GetGameVotesAsync(placeId)
|
||||
return rbxGetAsync(BaseUrl..'PlaceItem/GameDetailsVotingPanelJson?placeId='..tostring(placeId))
|
||||
end
|
||||
|
||||
--[[
|
||||
// Returns Array of tables - Length should always be 6
|
||||
// Table Keys
|
||||
// PlaceId - number
|
||||
// GameName - string
|
||||
// GameSeoUrl - string
|
||||
// Creator - table
|
||||
// CreatorName - string
|
||||
// CreatorTargetId - number
|
||||
// CreatorType - number
|
||||
// GameThumbnail - table
|
||||
// Url - string
|
||||
// IsFinal - boolean
|
||||
// AssetId - number
|
||||
// AssetType - number
|
||||
// AssetHash - can be null
|
||||
// ImageId - number, NEW
|
||||
]]
|
||||
function Http.GetRecommendedGamesAsync(currentPlaceId)
|
||||
return rbxGetAsync(BaseUrl..'Games/GetRecommendedGamesJson?currentPlaceId='..tostring(currentPlaceId))
|
||||
end
|
||||
|
||||
--[[
|
||||
// Returns Table
|
||||
// Table Keys
|
||||
// IsMobile - boolean
|
||||
// IsVideoAutoplayedOnReady - boolean
|
||||
// thumbnailCount - number
|
||||
// thumbnails - array of tables
|
||||
// Tabke Keys
|
||||
// AssetId - number
|
||||
// AssetTypeId - number
|
||||
// Url - string
|
||||
// IsFinal - boolean
|
||||
// AssetHash - can be null
|
||||
]]
|
||||
function Http.GetGameThumbnailsAsync(placeId)
|
||||
return rbxGetAsync(BaseUrl..'thumbnail/place-thumbnails?placeId='..tostring(placeId))
|
||||
end
|
||||
|
||||
--[[
|
||||
// Returns Table
|
||||
// Table Keys
|
||||
// PlaceId - number
|
||||
// GameBadges - array of tables
|
||||
// BadgeAssetId - number
|
||||
// IsOwned - boolean
|
||||
// Rarity - number
|
||||
// RarityName - string
|
||||
// TotalAwarded - number
|
||||
// TotalAwardedYesterday - number
|
||||
// Created - string
|
||||
// Updated - string
|
||||
// BadgeSeoUrl - string
|
||||
// CreatorId - number
|
||||
// ImageUrl - string (DO NOT USE)
|
||||
// IsImageUrlFinal - boolean
|
||||
// Name - string
|
||||
// Description - string
|
||||
]]
|
||||
function Http.GetGameBadgeDataAsync(placeId)
|
||||
return rbxGetAsync(BaseUrl..'badges/list-badges-for-place/json?placeId='..tostring(placeId))
|
||||
end
|
||||
|
||||
--[[
|
||||
// Returns Table
|
||||
// Table Keys
|
||||
// PlaceId - number
|
||||
// totalItems - number
|
||||
// IsViewerPlaceOwner - boolean
|
||||
// data - array of tables
|
||||
// PassID - number
|
||||
// PassName - string
|
||||
// TotalSales - number
|
||||
// PriceInRobux - number
|
||||
// PriceInTickets - number
|
||||
// Description - string
|
||||
// UserOwns - boolean
|
||||
// PassItemURL - string
|
||||
// ProductID - number
|
||||
// TotalUpVotes - number
|
||||
// TotalDownVotes - number
|
||||
// UserVote - null if user has not voted
|
||||
// TotalFavorites - number
|
||||
// IsFavoritedByUser - boolean
|
||||
// PlaceOwnerId - number
|
||||
// PlaceOwnerName - string
|
||||
]]
|
||||
function Http.GetGamePassesAsync(placeId, startIndex, maxRows)
|
||||
return rbxGetAsync(BaseUrl..'Games/GetGamePassesPaged?placeId='..tostring(placeId)..
|
||||
'&startIndex='..tostring(startIndex)..'&maxRows='..tostring(maxRows))
|
||||
end
|
||||
|
||||
--[[
|
||||
// Returns table
|
||||
// Table Keys
|
||||
// PlaceId - number
|
||||
// totalItems - number
|
||||
// IsViewerPlaceOwner - boolean
|
||||
// data - array of tables
|
||||
// Name - string
|
||||
// Description - string
|
||||
// PriceInRobux - number
|
||||
// PriceInTickets - number
|
||||
// ProductID - number
|
||||
// AssetID - number
|
||||
// TotalSales - number
|
||||
// UserOwns - boolean
|
||||
// SellerID - number
|
||||
// SellerName - string
|
||||
// ItemUrl - string
|
||||
// IsRentable - boolean
|
||||
// PromotionID - number
|
||||
// BCRequirement - number
|
||||
// IsForSale - boolean
|
||||
// TotalUpVotes - number
|
||||
// TotalDownVotes - number
|
||||
// UserVote - boolean, can be null if user has not voted
|
||||
// TotalFavorites - number
|
||||
// IsFavoritedByUser - boolean
|
||||
// AffiliateSalePlaceId - number
|
||||
]]
|
||||
function Http.GetPlaceProductsAsync(placeId, startIndex, maxRows)
|
||||
return rbxGetAsync(BaseUrl..'Games/GetPlaceProductPromotions?placeId='..tostring(placeId)..
|
||||
'&startIndex='..tostring(startIndex)..'&maxRows='..tostring(maxRows))
|
||||
end
|
||||
|
||||
--[[
|
||||
// Returns table
|
||||
// Table Keys
|
||||
// PlaceId - number
|
||||
// TotalCollectionSize - number
|
||||
// ShowShutdownAllButton - boolean
|
||||
// Collection - Array of tables
|
||||
// Table Keys
|
||||
// PlaceId - number
|
||||
// Capacity - number
|
||||
// UserCanJoin - boolean
|
||||
// ServerIpAddress - string
|
||||
// Fps - number
|
||||
// Guid - string
|
||||
// JoinScript - string
|
||||
// ShowSlowGameMessage - boolean
|
||||
// Ping - number
|
||||
// CurrentPlayers - array of tables
|
||||
// Table Keys
|
||||
// Id - number
|
||||
// Username - string
|
||||
// Thumbnail - table
|
||||
// Table Keys
|
||||
// AssetId - number
|
||||
// AssetTypeId - number
|
||||
// Url - string
|
||||
// IsFinal - boolean
|
||||
// AssetHash - can be null
|
||||
]]
|
||||
function Http.GetGameInstancesAsync(placeId, startIndex)
|
||||
return rbxGetAsync(BaseUrl..'Games/GetGameInstancesJson?placeId='..tostring(placeId)..'&startIndex='..tostring(startIndex))
|
||||
end
|
||||
|
||||
--[[
|
||||
// Returns table
|
||||
// Table Keys
|
||||
// success - boolean
|
||||
// message - string (is Whoa. Slow Down.)
|
||||
]]
|
||||
function Http.PostFavoriteToggleAsync(assetID)
|
||||
return rbxPostAsync(BaseUrl..'favorite/toggle?assetID='..tostring(assetID), 'favoriteToggle')
|
||||
end
|
||||
|
||||
-- TODO: Need to test flood check
|
||||
--[[
|
||||
// Returns Table
|
||||
// Table Keys
|
||||
// Success - boolean
|
||||
// Model - table
|
||||
// ModalType - string | Will be FloodCheckThresholdMet, PlayGame, EmailIsVerified
|
||||
// Table Keys
|
||||
// UserVote - boolean
|
||||
// ShowVotes - boolean
|
||||
// CanVote - boolean
|
||||
// DownVotes - number
|
||||
// UpVotes - number
|
||||
]]
|
||||
-- status can be true, false or null (null is a neutral vote)
|
||||
function Http.PostGameVoteAsync(assetId, status)
|
||||
return rbxPostAsync(BaseUrl..'voting/vote?assetId='..tostring(assetId)..'&vote='..tostring(status), 'vote')
|
||||
end
|
||||
|
||||
--[[ Social ]]--
|
||||
|
||||
--[[
|
||||
// Returns Table
|
||||
// Table Keys
|
||||
// GameId - number (can be null)
|
||||
// IsOnline - boolean
|
||||
// LastOnline - string
|
||||
// LastLocation - string
|
||||
// LocationType - number
|
||||
// PlaceId - number (can be null)
|
||||
]]
|
||||
function Http.GetUserPresenceAsync(userId)
|
||||
return rbxApiGetAsync('users/'..tostring(userId)..'/onlinestatus', true)
|
||||
end
|
||||
|
||||
--[[
|
||||
// Returns table
|
||||
// Table Keys
|
||||
// UserPresences - array of tables
|
||||
// Table Keys
|
||||
// VisitorId - number
|
||||
// GameId - number or null
|
||||
// IsOnline - boolean
|
||||
// LastOnline - string
|
||||
// LastLocation - string
|
||||
// LocationType - number/enum
|
||||
// PlaceId - number
|
||||
]]
|
||||
function Http.GetUsersOnlinePresenceAsync(listOfUsers)
|
||||
--rbxApiPostAsync(path, params, useHttps, throttlePriority, contentType)
|
||||
return rbxApiPostAsync('users/online-status', listOfUsers, true,
|
||||
Enum.ThrottlingPriority.Default, Enum.HttpContentType.ApplicationJson)
|
||||
end
|
||||
|
||||
--[[
|
||||
// Returns Table
|
||||
// Table Keys
|
||||
// UserId - number
|
||||
// TotalFriends - number
|
||||
// CurrentPage - number
|
||||
// PageSize - number
|
||||
// TotalPages - number
|
||||
// Friends - Array of Tables
|
||||
// Table Keys
|
||||
// UserId - number
|
||||
// Username - string
|
||||
// AvatarUri - string
|
||||
// AvatarFinal - boolean
|
||||
// InvitationId - number
|
||||
// FriendshipStatus - number
|
||||
// OnlineStatus - table
|
||||
// Table Keys
|
||||
// LocationOrLastSeen - string
|
||||
// ImageUrl - string
|
||||
// AlternateText - string
|
||||
]]
|
||||
function Http.GetFriendsAsync(userId, currentPage, pageSize)
|
||||
return rbxGetAsync(BaseUrl..'friends/json?userId='..tostring(userId)..'¤tPage='..tostring(currentPage)..'&pageSize='..tostring(pageSize)..
|
||||
'&friendsType=1')
|
||||
end
|
||||
|
||||
--[[
|
||||
// See Http.GetFriendsAsync
|
||||
]]
|
||||
function Http.GetRequestedFriendsAsync(userId, currentPage, pageSize)
|
||||
return rbxGetAsync(BaseUrl..'friends/json?userId='..tostring(userId)..'¤tPage='..tostring(currentPage)..'&pageSize='..tostring(pageSize)..
|
||||
'&friendsType=3')
|
||||
end
|
||||
|
||||
--[[
|
||||
// Returns array of tables
|
||||
// Table Keys
|
||||
// VisitorId - number
|
||||
// GameId - string (hash)
|
||||
// IsOnline - boolean
|
||||
// LastOnline - string
|
||||
// LastLocation - string
|
||||
// LocationType - number (enum)
|
||||
// PlaceId - number
|
||||
|
||||
]]
|
||||
function Http.GetOnlineFriendsAsync()
|
||||
return rbxApiGetAsync('my/friendsonline', true)
|
||||
end
|
||||
|
||||
--[[
|
||||
// Returns Table
|
||||
]]
|
||||
function Http.GetPlatformUserBalanceAsync()
|
||||
return rbxApiGetAsync('my/platform-currency-budget', true)
|
||||
end
|
||||
function Http.GetTotalUserBalanceAsync()
|
||||
return rbxApiGetAsync('currency/balance', true)
|
||||
end
|
||||
|
||||
--[[
|
||||
// Returns true if the user owns the asset, otherwise, returns false
|
||||
]]
|
||||
function Http.GetUserOwnsAssetAsync(userId, assetId)
|
||||
-- local BaseUrl = 'http://api.sitetest2.pizzaboxer.fun/'
|
||||
return rbxApiGetAsync(BaseUrl..'ownership/hasasset?userId='..tostring(userId)..'&assetId='..tostring(assetId), false)
|
||||
end
|
||||
|
||||
--[[
|
||||
// Returns Table
|
||||
// Table Keys
|
||||
// IsValid - boolean
|
||||
// Data - Table of keys
|
||||
// Table Keys
|
||||
// TotalItems - number
|
||||
// Start - number
|
||||
// End - number
|
||||
// Page - number
|
||||
// Items - Array of tables
|
||||
// Table Keys
|
||||
// Item - Table
|
||||
// Table Keys
|
||||
// AssetId - number
|
||||
// Name - string
|
||||
// Creator - Table
|
||||
// Table Keys
|
||||
// Id - number
|
||||
// Name - string
|
||||
// Type - number
|
||||
// Product - Table
|
||||
// Table Keys
|
||||
// PriceInRobux - number (nullable)
|
||||
// PriceInTickets - - number (nullable)
|
||||
// IsForSale - boolean
|
||||
// IsPublicDomain - boolean
|
||||
// IsResellable - boolean
|
||||
// IsLimitedEdition - boolean
|
||||
// IsUnique - boolean
|
||||
// ExpireTime - time (nullable)
|
||||
// IsExpired - boolean
|
||||
|
||||
// OTHER JUNK
|
||||
"PrivateServer":null,
|
||||
"Thumbnail": {"Final":true,"Url":"http://t4.watrbx.wtf/213527c418b9ea4fc7bbcfb79afd770f","RetryUrl":null}
|
||||
},
|
||||
]]
|
||||
function Http.GetUserOwnedPackagesAsync(userId, currentPage)
|
||||
currentPage = currentPage or 1
|
||||
local packageAssetIdType = 32
|
||||
-- return rbxGetAsync('http://www.sitetest2.pizzaboxer.fun/' ..'users/inventory/list-json?userId='..tostring(userId)..
|
||||
-- '&assetTypeId='..tostring(packageAssetIdType)..'&pageNumber='..tostring(currentPage))
|
||||
return rbxGetAsync(BaseUrl..'users/inventory/list-json?userId='..tostring(userId)..
|
||||
'&assetTypeId='..tostring(packageAssetIdType)..'&pageNumber='..tostring(currentPage))
|
||||
end
|
||||
|
||||
function Http.GetMyUserOutfitsAsync(startIndex, count)
|
||||
startIndex = startIndex or 0
|
||||
count = count or 20
|
||||
local url = string.format('appearance/get-my-user-outfits?startIndex=%d&count=%d', startIndex, count)
|
||||
return rbxApiGetAsync(url, true)
|
||||
end
|
||||
|
||||
function Http.GetCharactersAssetsAsync(userId)
|
||||
local url = string.format(AssetGameBaseUrl..'asset/characterfetch.ashx?userId=%d', userId)
|
||||
return rbxGetAsync(url, true)
|
||||
end
|
||||
|
||||
function Http.PostWearUserOutfitAsync(id)
|
||||
local url = string.format('appearance/wear-user-outfit?id=%d', id)
|
||||
return rbxApiPostAsync(url, '', false)
|
||||
end
|
||||
|
||||
function Http.PostWearAssetAsync(assetId)
|
||||
--local BaseUrl = 'http://api.gametest5.pizzaboxer.fun/'
|
||||
return rbxApiPostAsync('appearance/set-clothing?assetIds='..tostring(assetId), '', false)
|
||||
end
|
||||
|
||||
-- @ Params
|
||||
-- productId - integer number of the id of the product (different from assetId)
|
||||
-- expectedPrice - integer for how much of the currency is required to purchase
|
||||
-- expectedSellerId - UserId integer for the seller of the product
|
||||
-- expectedCurrency - integer denoting whether the currency is robux or tickets
|
||||
-- 1 = robux
|
||||
|
||||
-- @ Example result is a table:
|
||||
-- balanceAfterSale 197620
|
||||
-- sl_translate title, errorMsg
|
||||
-- AssetID 86500185
|
||||
-- shortfallPrice -197620
|
||||
-- errorMsg You already own this item.
|
||||
-- statusCode 500
|
||||
-- title Item Owned
|
||||
-- currentPrice 25
|
||||
-- expectedPrice 25
|
||||
-- showDivID TransactionFailureView
|
||||
-- expectedCurrency 1
|
||||
-- currentCurrency 1
|
||||
function Http.PurchaseProductAsync(productId, expectedPrice, expectedSellerId, expectedCurrency)
|
||||
-- local formattedUrl = BaseUrl .. 'API/Item.ashx?rqtype=purchase' ..
|
||||
-- '&productID=' .. tostring(productId) ..
|
||||
-- '&expectedCurrency=' .. tostring(expectedCurrency) ..
|
||||
-- '&expectedPrice=' .. tostring(expectedPrice) ..
|
||||
-- '&expectedSellerID=' .. tostring(expectedSellerId)
|
||||
local formattedUrl = string.format('%sAPI/Item.ashx?rqtype=purchase&productID=%d&expectedCurrency=%d&expectedPrice=%d&expectedSellerID=%d', BaseUrl, productId, expectedCurrency, expectedPrice, expectedSellerId)
|
||||
print('PurchaseProductAsync:' , formattedUrl)
|
||||
return rbxPostAsync(formattedUrl, '')
|
||||
-- path, params, contentType)
|
||||
-- return http://www.watrbx.wtf/API/Item.ashx?rqtype=purchase&productID=24805065&expectedCurrency=1&expectedPrice=88&expectedSellerID=1
|
||||
end
|
||||
|
||||
|
||||
|
||||
-- Sample response:
|
||||
--[=[
|
||||
{Products = {{AssetTypeId = 32, IconImageAssetId = 0, IsNew = false, Updated = '2015-08-03T22:26:25.447Z', IsLimitedUnique = false, ProductId = 9187002, MinimumMembershipLevel = 1, Created = '2011-08-05T21:50:20.67Z', Creator = {Name = 'ROBLOX', Id = 1}, IsLimited = false, ContentRatingTypeId = 0, AssetId = 58537634, IsPublicDomain = false, Name = 'Knight of the Splintered Sky', IsForSale = true, Description = 'He fights bravely along side his brothers and the Knights of Redcliff to defeat the evil Korblox and their zombies.', PriceInRobux = 1000, Sales = 19}}}
|
||||
--]=]
|
||||
function Http.GetXboxProductsAsync(startIndex, count)
|
||||
startIndex = startIndex or 0
|
||||
count = count or 20
|
||||
local url = string.format('xbox/catalog/contents?startIndex=%d&count=%d', startIndex, count)
|
||||
return rbxApiGetAsync(url, true)
|
||||
end
|
||||
|
||||
function Http.GetXboxCurrentlyWearingPackageAsync()
|
||||
local url = string.format('xbox/currently-wearing')
|
||||
return rbxApiGetAsync(url, true)
|
||||
end
|
||||
|
||||
-----
|
||||
|
||||
function Http.GetThumbnailUrlForAsset(assetId, width, height)
|
||||
width = width or 420
|
||||
height = height or 420
|
||||
return AssetGameBaseUrl .. 'Thumbs/Asset.ashx?width='..tostring(width)..'&height='..tostring(height)..'&assetId='..tostring(assetId)
|
||||
end
|
||||
|
||||
-- Report Abuse
|
||||
function Http.ReportAbuseAsync(reportingItemTypeName, reportingItemId, reportCategoryId, comment)
|
||||
local jsonPostBody = {
|
||||
reportingItemTypeName = reportingItemTypeName;
|
||||
reportingItemId = tostring(reportingItemId);
|
||||
reportCategoryId = tostring(reportCategoryId);
|
||||
comment = comment;
|
||||
}
|
||||
local params = HttpService:JSONEncode(jsonPostBody)
|
||||
if params then
|
||||
return rbxApiPostAsync('moderation/reportabuse', params, true,
|
||||
Enum.ThrottlingPriority.Default, Enum.HttpContentType.ApplicationJson)
|
||||
end
|
||||
end
|
||||
|
||||
--- Achievements
|
||||
function Http.GetConsecutiveDaysLoggedInAsync()
|
||||
local url = string.format('/xbox/get-login-consecutive-days')
|
||||
return rbxApiGetAsync(url, true)
|
||||
end
|
||||
|
||||
function Http.GetVoteCountAsync()
|
||||
local url = string.format('/user/get-vote-count?targetType=Place')
|
||||
return rbxApiGetAsync(url, true)
|
||||
end
|
||||
-- Account Linking
|
||||
--[[
|
||||
// Returns table
|
||||
// Table Keys
|
||||
// IsValid - boolean
|
||||
// ErrorMessage - string
|
||||
]]
|
||||
function Http.IsValidUsername(username)
|
||||
return rbxApiGetAsync('signup/is-username-valid?username='..username, true)
|
||||
end
|
||||
|
||||
--[[
|
||||
// Returns table
|
||||
// Table Keys
|
||||
// IsValid - boolean
|
||||
// ErrorMessage - string
|
||||
]]
|
||||
function Http.IsValidPassword(username, password)
|
||||
return rbxApiGetAsync('signup/is-password-valid?username='..username..'&password='..password, true)
|
||||
end
|
||||
|
||||
|
||||
return Http
|
||||
@@ -0,0 +1,211 @@
|
||||
--[[
|
||||
// ImageOverlay.lua
|
||||
// Creates an image overlay. Used with the game details page to see more thumbnails
|
||||
]]
|
||||
local CoreGui = Game:GetService("CoreGui")
|
||||
local GuiRoot = CoreGui:FindFirstChild("RobloxGui")
|
||||
local Modules = GuiRoot:FindFirstChild("Modules")
|
||||
local GuiService = game:GetService('GuiService')
|
||||
local ContextActionService = game:GetService("ContextActionService")
|
||||
|
||||
local GlobalSettings = require(Modules:FindFirstChild('GlobalSettings'))
|
||||
local Utility = require(Modules:FindFirstChild('Utility'))
|
||||
local SoundManager = require(Modules:FindFirstChild('SoundManager'))
|
||||
local ScreenManager = require(Modules:FindFirstChild('ScreenManager'))
|
||||
local ThumbnailLoader = require(Modules:FindFirstChild('ThumbnailLoader'))
|
||||
|
||||
local createImageOverlay = function(thumbIds, selectedThumbIndex)
|
||||
local this = {}
|
||||
|
||||
local thumbCount = #thumbIds
|
||||
if selectedThumbIndex < 1 or selectedThumbIndex > thumbCount then
|
||||
print("ImageOverlay: Invalid index to selectedThumbIndex")
|
||||
return
|
||||
end
|
||||
|
||||
local thumbnailImages = nil
|
||||
local currentSelectedIndex = selectedThumbIndex
|
||||
local baseZIndex = 3
|
||||
|
||||
local shield = Utility.Create'Frame'
|
||||
{
|
||||
Name = "Shield";
|
||||
Size = UDim2.new(1, 0, 1, 0);
|
||||
BackgroundTransparency = 1;
|
||||
BackgroundColor3 = Color3.new(0, 0, 0);
|
||||
BorderSizePixel = 0;
|
||||
ZIndex = baseZIndex;
|
||||
}
|
||||
local container = Utility.Create'Frame'
|
||||
{
|
||||
Name = "ImageOverlayContainer";
|
||||
Size = UDim2.new(1, 0, 0, 668);
|
||||
Position = UDim2.new(0, 0, 0, 226);
|
||||
BorderSizePixel = 0;
|
||||
BackgroundColor3 = GlobalSettings.OverlayColor;
|
||||
ZIndex = baseZIndex;
|
||||
}
|
||||
local dummySelectionImage = Utility.Create'TextButton'
|
||||
{
|
||||
Name = "DummySelectionImage";
|
||||
Size = UDim2.new(0, 0, 0, 0);
|
||||
Visible = false;
|
||||
}
|
||||
local imageSelection = Utility.Create'Frame'
|
||||
{
|
||||
Name = "imageSelection";
|
||||
Size = UDim2.new(0, 1030, 0, 580);
|
||||
Position = UDim2.new(0.5, -1030/2, 0.5, -580/2);
|
||||
BackgroundTransparency = 1;
|
||||
Selectable = true;
|
||||
SelectionImageObject = dummySelectionImage;
|
||||
Parent = container;
|
||||
SoundManager:CreateSound('MoveSelection');
|
||||
}
|
||||
local leftArrowImage = Utility.Create'ImageButton'
|
||||
{
|
||||
Name = "LeftArrowImage";
|
||||
Size = UDim2.new(0, 26, 0, 45);
|
||||
Position = UDim2.new(0.5, imageSelection.Position.X.Offset - 18 - 75, 0.5, -45/2);
|
||||
BackgroundTransparency = 1;
|
||||
Image = 'rbxasset://textures/ui/Settings/Slider/Left.png';
|
||||
SelectionImageObject = dummySelectionImage;
|
||||
ZIndex = baseZIndex + 1;
|
||||
Parent = container;
|
||||
SoundManager:CreateSound('MoveSelection');
|
||||
}
|
||||
local rightArrowImage = leftArrowImage:Clone()
|
||||
rightArrowImage.Name = "RightArrowImage"
|
||||
rightArrowImage.Position = UDim2.new(0.5, imageSelection.Position.X.Offset + imageSelection.Size.X.Offset + 75, 0.5, -45/2)
|
||||
rightArrowImage.Image = 'rbxasset://textures/ui/Settings/Slider/Right.png';
|
||||
rightArrowImage.Parent = container
|
||||
|
||||
local selectedText = Utility.Create'TextLabel'
|
||||
{
|
||||
Name = "SelectedText";
|
||||
Size = UDim2.new();
|
||||
Position = UDim2.new(0.5, 0, 1, -24);
|
||||
BackgroundTransparency = 1;
|
||||
Font = GlobalSettings.RegularFont;
|
||||
FontSize = GlobalSettings.ButtonSize;
|
||||
TextColor3 = GlobalSettings.WhiteTextColor;
|
||||
Text = "";
|
||||
ZIndex = baseZIndex;
|
||||
Parent = container;
|
||||
}
|
||||
|
||||
local function createThumbImages()
|
||||
if not thumbnailImages then
|
||||
thumbnailImages = {}
|
||||
for i = 1, #thumbIds do
|
||||
local image = Utility.Create'ImageLabel'
|
||||
{
|
||||
Name = tostring(i);
|
||||
Size = imageSelection.Size;
|
||||
Position = imageSelection.Position;
|
||||
BackgroundTransparency = 1;
|
||||
ZIndex = baseZIndex;
|
||||
Parent = container;
|
||||
SoundManager:CreateSound('MoveSelection');
|
||||
}
|
||||
local loader = ThumbnailLoader:Create(image, thumbIds[i],
|
||||
ThumbnailLoader.Sizes.Large, ThumbnailLoader.AssetType.Icon, false)
|
||||
spawn(function()
|
||||
loader:LoadAsync(true, true, { ZIndex = image.ZIndex } )
|
||||
end)
|
||||
thumbnailImages[i] = image
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function setImagePositions(startIndex)
|
||||
for i = 1, #thumbnailImages do
|
||||
local image = thumbnailImages[i]
|
||||
local xScale = i == startIndex and 0.5 or 1.5
|
||||
image.Position = UDim2.new(xScale, -image.Size.X.Offset / 2, 0.5, image.Position.Y.Offset)
|
||||
end
|
||||
end
|
||||
|
||||
local function tweenImagePositions(currentIndex, nextIndex, direction)
|
||||
if currentIndex == nextIndex then return end
|
||||
local nextStartPosition = direction * 1.5
|
||||
local currentEndPosition = -nextStartPosition
|
||||
--
|
||||
local currentImage = thumbnailImages[currentIndex]
|
||||
local nextImage = thumbnailImages[nextIndex]
|
||||
nextImage.Position = UDim2.new(nextStartPosition, -nextImage.Size.X.Offset / 2, 0.5, nextImage.Position.Y.Offset)
|
||||
--
|
||||
GuiService.SelectedCoreObject = imageSelection
|
||||
Utility.TweenPositionOrSet(currentImage, UDim2.new(currentEndPosition, -currentImage.Size.X.Offset / 2, 0.5, currentImage.Position.Y.Offset),
|
||||
Enum.EasingDirection.InOut, Enum.EasingStyle.Quad, 0.25, true)
|
||||
Utility.TweenPositionOrSet(nextImage, UDim2.new(0.5, -nextImage.Size.X.Offset / 2, 0.5, nextImage.Position.Y.Offset),
|
||||
Enum.EasingDirection.InOut, Enum.EasingStyle.Quad, 0.25, true)
|
||||
selectedText.Text = tostring(nextIndex)..'/'..tostring(#thumbnailImages)
|
||||
end
|
||||
|
||||
local function onArrowSelected(direction)
|
||||
local nextIndex = nil
|
||||
nextIndex = currentSelectedIndex + direction
|
||||
if nextIndex < 1 then
|
||||
nextIndex = thumbCount
|
||||
elseif nextIndex > thumbCount then
|
||||
nextIndex = 1
|
||||
end
|
||||
if nextIndex then
|
||||
tweenImagePositions(currentSelectedIndex, nextIndex, direction)
|
||||
currentSelectedIndex = nextIndex
|
||||
end
|
||||
end
|
||||
|
||||
--[[ Public API ]]--
|
||||
function this:Show()
|
||||
createThumbImages()
|
||||
setImagePositions(currentSelectedIndex)
|
||||
selectedText.Text = tostring(currentSelectedIndex).."/"..tostring(thumbCount)
|
||||
if thumbCount == 1 then
|
||||
leftArrowImage.Visible = false
|
||||
rightArrowImage.Visible = false
|
||||
end
|
||||
|
||||
shield.Parent = GuiRoot
|
||||
container.Parent = shield.Parent
|
||||
local shieldTweenIn = Utility.PropertyTweener(shield, "BackgroundTransparency", 1, 0.3, 0.25, Utility.EaseInOutQuad, nil)
|
||||
end
|
||||
|
||||
function this:Hide()
|
||||
local shieldTweenOut = Utility.PropertyTweener(shield, "BackgroundTransparency", 0.3, 1, 0.25, Utility.EaseInOutQuad, true, function()
|
||||
shield:Destroy()
|
||||
end)
|
||||
container:Destroy()
|
||||
SoundManager:Play('ScreenChange');
|
||||
end
|
||||
|
||||
function this:Focus()
|
||||
ContextActionService:BindCoreAction("CloseImageOverlay",
|
||||
function(actionName, inputState, inputObject)
|
||||
if inputState == Enum.UserInputState.End then
|
||||
ScreenManager:CloseCurrent()
|
||||
end
|
||||
end,
|
||||
false, Enum.KeyCode.ButtonB)
|
||||
|
||||
leftArrowImage.SelectionGained:connect(function()
|
||||
onArrowSelected(-1)
|
||||
end)
|
||||
rightArrowImage.SelectionGained:connect(function()
|
||||
onArrowSelected(1)
|
||||
end)
|
||||
|
||||
GuiService:AddSelectionParent("ImageOverlay", container)
|
||||
GuiService.SelectedCoreObject = imageSelection
|
||||
end
|
||||
|
||||
function this:RemoveFocus()
|
||||
ContextActionService:UnbindCoreAction("CloseImageOverlay")
|
||||
GuiService:RemoveSelectionGroup("ImageOverlay")
|
||||
end
|
||||
|
||||
return this
|
||||
end
|
||||
|
||||
return createImageOverlay
|
||||
@@ -0,0 +1,240 @@
|
||||
--[[
|
||||
]]
|
||||
local CoreGui = Game:GetService("CoreGui")
|
||||
local GuiRoot = CoreGui:FindFirstChild("RobloxGui")
|
||||
local Modules = GuiRoot:FindFirstChild("Modules")
|
||||
local GuiService = game:GetService("GuiService")
|
||||
local UserInputService = game:GetService("UserInputService")
|
||||
|
||||
local ThumbnailLoader = require(Modules:FindFirstChild('ThumbnailLoader'))
|
||||
local Utility = require(Modules:FindFirstChild('Utility'))
|
||||
|
||||
local function CreateImageSlider(size, position)
|
||||
local this = {}
|
||||
|
||||
local items = {}
|
||||
local maxItems = 0
|
||||
local currentItemIndex = 1
|
||||
local padding = 0
|
||||
local focusSize = 450
|
||||
local itemSize = 300
|
||||
local dataTable = nil
|
||||
|
||||
local lastSelectedObject = nil
|
||||
local newSelectedObjectCn = nil
|
||||
this.OnNewFocusItem = Utility.Signal()
|
||||
|
||||
local imageObjectToLoaderMap = {}
|
||||
|
||||
this.Container = Utility.Create'ScrollingFrame'
|
||||
{
|
||||
Name = "ImageSliderContainer";
|
||||
Size = size;
|
||||
Position = position;
|
||||
BackgroundTransparency = 1;
|
||||
ClipsDescendants = false;
|
||||
ScrollingEnabled = false;
|
||||
Selectable = false;
|
||||
ScrollBarThickness = 0;
|
||||
}
|
||||
|
||||
local function loadNewImage(item, dataIndex)
|
||||
if imageObjectToLoaderMap[item] then
|
||||
imageObjectToLoaderMap[item]:Cancel()
|
||||
imageObjectToLoaderMap[item] = nil
|
||||
end
|
||||
local iconId = dataTable[dataIndex].IconId
|
||||
local thumbLoader = ThumbnailLoader:Create(item, iconId,
|
||||
ThumbnailLoader.Sizes.Medium, ThumbnailLoader.AssetType.Icon)
|
||||
imageObjectToLoaderMap[item] = thumbLoader
|
||||
spawn(function()
|
||||
thumbLoader:LoadAsync()
|
||||
imageObjectToLoaderMap[item] = nil
|
||||
end)
|
||||
end
|
||||
|
||||
local MAX_LEFT = 2
|
||||
local MAX_RIGHT = 6
|
||||
-- TODO: Figure out left/right better
|
||||
local function recalcPositionAndSize(tweenTime, isRight, newSelectedObject)
|
||||
local smIconYPos = (focusSize - itemSize) / 2
|
||||
local startPosition = 0
|
||||
if currentItemIndex == 1 then
|
||||
startPosition = 0
|
||||
elseif currentItemIndex == 2 then
|
||||
startPosition = -1
|
||||
elseif maxItems == #items then
|
||||
startPosition = 1 - currentItemIndex
|
||||
elseif maxItems - currentItemIndex < MAX_RIGHT then
|
||||
startPosition = maxItems - currentItemIndex - MAX_RIGHT - MAX_LEFT
|
||||
else
|
||||
startPosition = -MAX_LEFT
|
||||
end
|
||||
|
||||
-- put front at back
|
||||
if maxItems > #items then
|
||||
if isRight == true and startPosition == -MAX_LEFT and currentItemIndex > 3 then
|
||||
local front = items[1]
|
||||
local back = items[#items]
|
||||
front.Position = UDim2.new(0, back.Position.X.Offset + back.Size.X.Offset + padding, 0, smIconYPos)
|
||||
this:RemoveFromFront()
|
||||
this:AddItemToBack(front)
|
||||
if dataTable and dataTable[currentItemIndex + MAX_RIGHT] then
|
||||
loadNewImage(front, currentItemIndex + MAX_RIGHT)
|
||||
end
|
||||
-- put back in front
|
||||
elseif isRight == false and currentItemIndex < maxItems - MAX_RIGHT and startPosition == -MAX_LEFT then
|
||||
local back = items[#items]
|
||||
local front = items[1]
|
||||
back.Position = UDim2.new(0, front.Position.X.Offset - padding - itemSize, 0, smIconYPos)
|
||||
this:RemoveFromBack()
|
||||
this:AddToFront(back)
|
||||
if dataTable and dataTable[currentItemIndex - MAX_LEFT] then
|
||||
loadNewImage(back, currentItemIndex - MAX_LEFT)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local currentXPosition = itemSize * startPosition + padding * startPosition
|
||||
for i = 1, #items do
|
||||
if items[i] == newSelectedObject then
|
||||
if tweenTime == 0 then
|
||||
items[i].Size = UDim2.new(0, focusSize, 0, focusSize)
|
||||
items[i].Position = UDim2.new(0, currentXPosition, 0, 0)
|
||||
else
|
||||
items[i]:TweenSizeAndPosition(UDim2.new(0, focusSize, 0, focusSize), UDim2.new(0, currentXPosition, 0, 0),
|
||||
Enum.EasingDirection.InOut, Enum.EasingStyle.Sine, tweenTime, true)
|
||||
end
|
||||
currentXPosition = currentXPosition + focusSize + padding
|
||||
else
|
||||
if tweenTime == 0 then
|
||||
items[i].Size = UDim2.new(0, itemSize, 0, itemSize)
|
||||
items[i].Position = UDim2.new(0, currentXPosition, 0, smIconYPos)
|
||||
else
|
||||
items[i]:TweenSizeAndPosition(UDim2.new(0, itemSize, 0, itemSize), UDim2.new(0, currentXPosition, 0, smIconYPos),
|
||||
Enum.EasingDirection.InOut, Enum.EasingStyle.Sine, tweenTime, true)
|
||||
end
|
||||
currentXPosition = currentXPosition + itemSize + padding
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--[[ Events ]]--
|
||||
newSelectedObjectCn = GuiService.Changed:connect(function(property)
|
||||
if property == 'SelectedCoreObject' then
|
||||
local selectedObject = GuiService.SelectedCoreObject
|
||||
if selectedObject and selectedObject ~= lastSelectedObject and selectedObject:IsDescendantOf(this.Container) then
|
||||
local isRight = nil
|
||||
if lastSelectedObject then
|
||||
-- slide left
|
||||
if lastSelectedObject.AbsolutePosition.x < selectedObject.AbsolutePosition.x then
|
||||
currentItemIndex = currentItemIndex + 1
|
||||
isRight = true
|
||||
-- slide right
|
||||
else
|
||||
currentItemIndex = currentItemIndex - 1
|
||||
isRight = false
|
||||
end
|
||||
end
|
||||
recalcPositionAndSize(lastSelectedObject and 0.25 or 0, isRight, selectedObject)
|
||||
lastSelectedObject = selectedObject
|
||||
this.OnNewFocusItem:fire(currentItemIndex, isRight, selectedObject)
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
--[[ Public API ]]--
|
||||
function this:SetPosition(position)
|
||||
self.Container.Position = position
|
||||
end
|
||||
|
||||
function this:SetSize(size)
|
||||
self.Container.Size = size
|
||||
end
|
||||
|
||||
function this:SetParent(parent)
|
||||
self.Container.Parent = parent
|
||||
end
|
||||
|
||||
function this:SetFocusPosition(index)
|
||||
if index > 0 and index < #items + 1 then
|
||||
currentItemIndex = index
|
||||
recalcPositionAndSize(0, nil, items[currentItemIndex])
|
||||
end
|
||||
end
|
||||
|
||||
function this:SetDataTable(tbl)
|
||||
dataTable = tbl
|
||||
end
|
||||
|
||||
function this:SetMaxItems(value)
|
||||
local index = nil
|
||||
-- we're near the end, so we need to update image content
|
||||
if currentItemIndex > maxItems - MAX_RIGHT then
|
||||
-- find the current selected index in relation to image pool
|
||||
for i = 1, #items do
|
||||
if lastSelectedObject and items[i] == GuiService.SelectedCoreObject then
|
||||
index = i
|
||||
break
|
||||
end
|
||||
end
|
||||
-- TODO: Still might be a visual bug in some cases
|
||||
if index then
|
||||
-- replace images from front of pool
|
||||
for i = 1, index - 3 do
|
||||
if dataTable[currentItemIndex + i] then
|
||||
local item = items[1]
|
||||
loadNewImage(item, currentItemIndex + (maxItems - currentItemIndex) + i)
|
||||
print("NEW IMAGE:", currentItemIndex, ":", maxItems, ":", i, ":", currentItemIndex + (maxItems - currentItemIndex) + i)
|
||||
self:RemoveFromFront()
|
||||
self:AddItemToBack(item)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
maxItems = value
|
||||
recalcPositionAndSize(0)
|
||||
end
|
||||
|
||||
function this:SetPadding(newPadding)
|
||||
padding = newPadding
|
||||
end
|
||||
|
||||
function this:AddToFront(newItem)
|
||||
table.insert(items, 1, newItem)
|
||||
newItem.Parent = this.Container
|
||||
end
|
||||
|
||||
function this:AddItemToBack(newItem)
|
||||
if this and this.Container then
|
||||
items[#items + 1] = newItem
|
||||
newItem.Parent = this.Container
|
||||
end
|
||||
end
|
||||
|
||||
function this:AddItem(newItem)
|
||||
self:AddItemToBack(newItem)
|
||||
end
|
||||
|
||||
function this:RemoveFromFront()
|
||||
table.remove(items, 1)
|
||||
end
|
||||
|
||||
function this:RemoveFromBack()
|
||||
table.remove(items)
|
||||
end
|
||||
|
||||
function this:Destroy()
|
||||
if newSelectedObjectCn then
|
||||
newSelectedObjectCn:disconnect()
|
||||
newSelectedObjectCn = nil
|
||||
end
|
||||
this.Container:Destroy()
|
||||
this.OnNewFocus = nil
|
||||
this = nil
|
||||
end
|
||||
|
||||
return this
|
||||
end
|
||||
|
||||
return CreateImageSlider
|
||||
@@ -0,0 +1,147 @@
|
||||
--[[
|
||||
// LinkAccountScreen.lua
|
||||
]]
|
||||
local CoreGui = Game:GetService("CoreGui")
|
||||
local GuiRoot = CoreGui:FindFirstChild("RobloxGui")
|
||||
local Modules = GuiRoot:FindFirstChild("Modules")
|
||||
|
||||
local ContextActionService = game:GetService('ContextActionService')
|
||||
local GuiService = game:GetService('GuiService')
|
||||
|
||||
local AccountManager = require(Modules:FindFirstChild('AccountManager'))
|
||||
local AssetManager = require(Modules:FindFirstChild('AssetManager'))
|
||||
local BaseSignInScreen = require(Modules:FindFirstChild('BaseSignInScreen'))
|
||||
local Errors = require(Modules:FindFirstChild('Errors'))
|
||||
local ErrorOverlay = require(Modules:FindFirstChild('ErrorOverlay'))
|
||||
local EventHub = require(Modules:FindFirstChild('EventHub'))
|
||||
local GlobalSettings = require(Modules:FindFirstChild('GlobalSettings'))
|
||||
local LoadingWidget = require(Modules:FindFirstChild('LoadingWidget'))
|
||||
local ScreenManager = require(Modules:FindFirstChild('ScreenManager'))
|
||||
local SoundManager = require(Modules:FindFirstChild('SoundManager'))
|
||||
local Strings = require(Modules:FindFirstChild('LocalizedStrings'))
|
||||
local TextBox = require(Modules:FindFirstChild('TextBox'))
|
||||
local Utility = require(Modules:FindFirstChild('Utility'))
|
||||
|
||||
local function createLinkAccountScreen()
|
||||
local this = BaseSignInScreen()
|
||||
|
||||
this:SetTitle(string.upper(Strings:LocalizedString("LinkAccountTitle")))
|
||||
this:SetDescriptionText(Strings:LocalizedString("LinkAccountPhrase"))
|
||||
|
||||
local ModalOverlay = Utility.Create'Frame'
|
||||
{
|
||||
Name = "ModalOverlay";
|
||||
Size = UDim2.new(1, 0, 1, 0);
|
||||
BackgroundTransparency = GlobalSettings.ModalBackgroundTransparency;
|
||||
BackgroundColor3 = GlobalSettings.ModalBackgroundColor;
|
||||
BorderSizePixel = 0;
|
||||
ZIndex = 4;
|
||||
}
|
||||
|
||||
local myUsername = nil
|
||||
local myPassword = nil
|
||||
|
||||
this.UsernameObject:SetDefaultText(Strings:LocalizedString("UsernameWord"))
|
||||
this.UsernameObject:SetKeyboardTitle(Strings:LocalizedString("UsernameWord"))
|
||||
local usernameChangedCn = nil
|
||||
|
||||
this.PasswordObject:SetDefaultText(Strings:LocalizedString("PasswordWord"))
|
||||
this.PasswordObject:SetKeyboardTitle(Strings:LocalizedString("PasswordWord"))
|
||||
this.PasswordObject:SetKeyboardType(Enum.XboxKeyBoardType.Password)
|
||||
local passwordChangedCn = nil
|
||||
|
||||
local function linkAccountAsync()
|
||||
local linkResult = nil
|
||||
local signInResult = nil
|
||||
local loader = LoadingWidget(
|
||||
{ Parent = this.Container }, {
|
||||
-- try link account
|
||||
function()
|
||||
linkResult = AccountManager:LinkAccountAsync(myUsername, myPassword)
|
||||
|
||||
-- sign in here on success
|
||||
if linkResult == AccountManager.AuthResults.Success then
|
||||
signInResult = AccountManager:SignInAsync(Enum.UserInputType.Gamepad1)
|
||||
end
|
||||
end
|
||||
})
|
||||
|
||||
-- set up full screen loader
|
||||
ModalOverlay.Parent = GuiRoot
|
||||
ContextActionService:BindCoreAction("BlockB", function() end, false, Enum.KeyCode.ButtonB)
|
||||
local selectedObject = GuiService.SelectedCoreObject
|
||||
GuiService.SelectedCoreObject = nil
|
||||
|
||||
-- call loader
|
||||
loader:AwaitFinished()
|
||||
|
||||
-- clean up
|
||||
loader:Cleanup()
|
||||
loader = nil
|
||||
GuiService.SelectedCoreObject = selectedObject
|
||||
ContextActionService:UnbindCoreAction("BlockB")
|
||||
ModalOverlay.Parent = nil
|
||||
|
||||
if linkResult ~= AccountManager.AuthResults.Success then
|
||||
local err = linkResult and Errors.Authentication[linkResult] or Errors.Default
|
||||
ScreenManager:OpenScreen(ErrorOverlay(err), false)
|
||||
else
|
||||
if signInResult == AccountManager.AuthResults.Success then
|
||||
ScreenManager:CloseCurrent()
|
||||
EventHub:dispatchEvent(EventHub.Notifications["AuthenticationSuccess"])
|
||||
else
|
||||
local err = signInResult and Errors.Authentication[signInResult] or Errors.Default
|
||||
ScreenManager:OpenScreen(ErrorOverlay(err), false)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local isSigningIn = false
|
||||
this.SignInButton.MouseButton1Click:connect(function()
|
||||
if isSigningIn then return end
|
||||
isSigningIn = true
|
||||
SoundManager:Play('ButtonPress')
|
||||
if (myUsername and #myUsername > 0) and (myPassword and #myPassword > 0) then
|
||||
linkAccountAsync()
|
||||
else
|
||||
local err = Errors.SignIn.NoUsernameOrPasswordEntered
|
||||
ScreenManager:OpenScreen(ErrorOverlay(err), false)
|
||||
end
|
||||
isSigningIn = false
|
||||
end)
|
||||
|
||||
--[[ Public API ]]--
|
||||
--override
|
||||
local baseFocus = this.Focus
|
||||
function this:Focus()
|
||||
baseFocus(self)
|
||||
usernameChangedCn = this.UsernameObject.OnTextChanged:connect(function(text)
|
||||
myUsername = text
|
||||
if #myUsername > 0 then
|
||||
GuiService.SelectedCoreObject = this.PasswordSelection
|
||||
else
|
||||
GuiService.SelectedCoreObject = this.UsernameSelection
|
||||
end
|
||||
end)
|
||||
passwordChangedCn = this.PasswordObject.OnTextChanged:connect(function(text)
|
||||
myPassword = text
|
||||
if #myPassword > 0 then
|
||||
GuiService.SelectedCoreObject = this.SignInButton
|
||||
else
|
||||
GuiService.SelectedCoreObject = this.PasswordSelection
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
--override
|
||||
local baseRemoveFocus = this.RemoveFocus
|
||||
function this:RemoveFocus()
|
||||
baseRemoveFocus(self)
|
||||
Utility.DisconnectEvent(usernameChangedCn)
|
||||
Utility.DisconnectEvent(passwordChangedCn)
|
||||
end
|
||||
|
||||
return this
|
||||
end
|
||||
|
||||
return createLinkAccountScreen
|
||||
@@ -0,0 +1,89 @@
|
||||
--[[
|
||||
// LoadingWidget.lua
|
||||
|
||||
// Created by Kip Turner
|
||||
// Copyright Roblox 2015
|
||||
]]
|
||||
|
||||
|
||||
local CoreGui = Game:GetService("CoreGui")
|
||||
local GuiRoot = CoreGui:FindFirstChild("RobloxGui")
|
||||
local Modules = GuiRoot:FindFirstChild("Modules")
|
||||
local GuiService = game:GetService('GuiService')
|
||||
|
||||
local Utility = require(Modules:FindFirstChild('Utility'))
|
||||
|
||||
|
||||
local function CreateLoadingWidget(properties, loadingFunctions)
|
||||
properties = properties or {}
|
||||
loadingFunctions = loadingFunctions or {}
|
||||
|
||||
local this = {}
|
||||
|
||||
local completedFunctions = {}
|
||||
local cancelled = false
|
||||
local finishedConn = Utility.Signal()
|
||||
|
||||
local loadIcon = Utility.Create'ImageLabel'
|
||||
{
|
||||
Name = "LoadIcon";
|
||||
BackgroundTransparency = 1;
|
||||
Image = 'rbxasset://textures/ui/Shell/Icons/LoadingSpinner@1080.png';
|
||||
Size = properties.Size or UDim2.new(0,99,0,100);
|
||||
ZIndex = properties.ZIndex or 7;
|
||||
Parent = properties.Parent;
|
||||
}
|
||||
Utility.CalculateAnchor(loadIcon, properties.Position or UDim2.new(0.5,0,0.5,0), Utility.Enum.Anchor.Center)
|
||||
|
||||
if properties.Visible == false then
|
||||
loadIcon.Visible = false
|
||||
end
|
||||
|
||||
local function isLoadingComplete()
|
||||
return #completedFunctions == #loadingFunctions
|
||||
end
|
||||
|
||||
function this:AwaitFinished()
|
||||
if isLoadingComplete() then
|
||||
return true
|
||||
end
|
||||
finishedConn:wait()
|
||||
if cancelled then
|
||||
return false
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
function this:Cleanup()
|
||||
loadIcon.Parent = nil
|
||||
loadIcon:Destroy()
|
||||
cancelled = true
|
||||
end
|
||||
|
||||
|
||||
-- Run it!
|
||||
for _, loadingFunction in pairs(loadingFunctions) do
|
||||
spawn(function()
|
||||
loadingFunction()
|
||||
table.insert(completedFunctions, loadingFunction)
|
||||
end)
|
||||
end
|
||||
|
||||
spawn(function()
|
||||
local t = tick()
|
||||
while not (cancelled or isLoadingComplete()) do
|
||||
local now = tick()
|
||||
local rotation = (now - t) * 360
|
||||
if loadIcon.Parent then
|
||||
loadIcon.Rotation = loadIcon.Rotation + rotation
|
||||
end
|
||||
t = now
|
||||
wait()
|
||||
end
|
||||
finishedConn.fire()
|
||||
end)
|
||||
|
||||
return this
|
||||
end
|
||||
|
||||
return CreateLoadingWidget
|
||||
@@ -0,0 +1,275 @@
|
||||
-- Written by Kip Turner, Copyright ROBLOX 2015
|
||||
|
||||
|
||||
local enUS =
|
||||
{
|
||||
["HomeWord"] = "Home";
|
||||
["GameWord"] = "Games";
|
||||
["FriendsWord"] = "Friends";
|
||||
["CatalogWord"] = "ROBUX";
|
||||
["AvatarWord"] = "Avatar";
|
||||
["PlayWord"] = "Play";
|
||||
["FavoriteWord"] = "Favorite";
|
||||
["FavoritedWord"] = "Favorited";
|
||||
["LikedWord"] = "Liked";
|
||||
["DislikedWord"] = "Disliked";
|
||||
["BackWord"] = "Back";
|
||||
["SettingsWord"] = "Settings";
|
||||
["AccountWord"] = "Account";
|
||||
["OverscanWord"] = "Adjust Screen";
|
||||
["HelpWord"] = "Help";
|
||||
["SwitchProfileWord"] = "Switch Profile";
|
||||
|
||||
["SearchWord"] = "Search";
|
||||
|
||||
["EditAvatarPhrase"] = "Edit Avatar";
|
||||
["FriendActivityWord"] = "Friend Activity";
|
||||
["OnlineFriendsWords"] = "Online Friends";
|
||||
["RecentlyPlayedWithSortTitle"] = "Recently Played With";
|
||||
["StartPartyPhrase"] = "Snap Party App";
|
||||
|
||||
["RecommendedSortTitle"] = "Recommended";
|
||||
["FavoritesSortTitle"] = "Favorites";
|
||||
["RecentlyPlayedSortTitle"] = "Recently Played";
|
||||
["MyRecentTitle"] = "My Recent";
|
||||
["TopEarningTitle"] = "Top Earning";
|
||||
["TopRatedTitle"] = "Top Rated";
|
||||
["PopularTitle"] = "Popular";
|
||||
["FeaturedTitle"] = "Featured";
|
||||
["MoreGamesPhrase"] = "More Games";
|
||||
|
||||
["NoFriendsOnlinePhrase"] = "Your friends are not online";
|
||||
["JoinGameWord"] = "Join Game";
|
||||
["ViewGameDetailsWord"] = "View Game Details";
|
||||
["InviteToPartyWord"] = "Invite To Party";
|
||||
["ViewGamerCardWord"] = "View Gamer Card";
|
||||
|
||||
["RatingDescriptionTitle"] = "Rating and Description";
|
||||
["GameImagesTitle"] = "Game Images";
|
||||
["GameBadgesTitle"] = "Game Badges";
|
||||
["FriendActivityTitle"] = "Friend Activity";
|
||||
["RelatedGamesTitle"] = "Related Games";
|
||||
["MoreDetailsTitle"] = "More Details";
|
||||
["GameBadgesTitle"] = "Game Badges";
|
||||
|
||||
["SelectYourAvatarTitle"] = "Select Your Avatar";
|
||||
|
||||
["LastUpdatedWord"] = "Last Updated";
|
||||
["CreationDateWord"] = "Creation Date";
|
||||
["CreatedByWord"] = "Created By";
|
||||
["ReportGameWord"] = "Report Game";
|
||||
["HaveBadgeWord"] = "You Have This Badge";
|
||||
["MaxPlayersWord"] = "Max Players";
|
||||
|
||||
["ScreenSizeWord"] = "Screen Size";
|
||||
["ResizeScreenPrompt"] = "Use the right stick to adjust the edges of the white box until it is just off the screen";
|
||||
["ResizeScreenInputHint"] = "Resize Screen";
|
||||
["AcceptWord"] = "Accept";
|
||||
["ResetWord"] = "Reset";
|
||||
["CannotVoteWord"] = "You must play this game before rating";
|
||||
["FirstToRateWord"] = "Be the first to rate this game";
|
||||
["CustomAvatarPhrase"] = "This game uses custom Avatars";
|
||||
|
||||
["EngagementScreenHint"] = "Press Any Button To Begin";
|
||||
|
||||
["EquipWord"] = "Swap Avatar";
|
||||
["BuyWord"] = "Buy";
|
||||
["OkWord"] = "Ok";
|
||||
["FreeWord"] = "Free";
|
||||
["TakeWord"] = "Take";
|
||||
['GetRobuxPhrase'] = 'Get ROBUX';
|
||||
["AlreadyOwnedPhrase"] = "You already own this";
|
||||
["PurchasedThisPhrase"] = 'You purchased this for %s ROBUX';
|
||||
["RobuxBalanceTitle"] = 'My Balance';
|
||||
|
||||
["RobuxBalanceOverlayTitle"] = "ROBUX Balance";
|
||||
["RobuxBalanceOverlayPhrase"] = "Only ROBUX purchased from the Xbox Store may be used on Xbox.";
|
||||
["PlatformBalanceTitle"] = "Available on Xbox:";
|
||||
["TotalBalanceTitle"] = "Total Balance:";
|
||||
|
||||
["AvatarCatalogTitle"] = 'Catalog';
|
||||
["AvatarOutfitsTitle"] = 'My Collection';
|
||||
|
||||
|
||||
["PurchasingTitle"] = 'Purchasing...';
|
||||
["ConfirmPurchaseTitle"] = 'Confirm Purchase';
|
||||
["AreYouSurePhrase"] = 'Are you sure you want to buy "%s"?';
|
||||
["AreYouSureTakePhrase"] = 'Are you sure you want to take "%s"?';
|
||||
["AreYouSureWithPricePhrase"] = 'Are you sure you want to buy "%s" for %s?';
|
||||
["RemainingBalancePhrase"] = 'Your remaining balance will be %s ROBUX';
|
||||
["ConfirmWord"] = 'Confirm';
|
||||
["DeclineWord"] = 'Decline';
|
||||
|
||||
["OnlineWord"] = "Online";
|
||||
["OfflineWord"] = "Offline";
|
||||
|
||||
["SubmitWord"] = "Submit";
|
||||
["ReportPhrase"] = "You can send a report to our moderation team. We will review the game and take appropriate action.";
|
||||
|
||||
|
||||
["CurrencySymbol"] = "$";
|
||||
["RobuxStoreDescription"] = "Get ROBUX to buy great new looks for your Avatar, plus perks and abilities in games.";
|
||||
["RobuxStoreNoItemsPhrase"] = "There are no items for purchase right now, please try again later";
|
||||
["PercentMoreRobuxPhrase"] = "%s%% More";
|
||||
--["RobuxStoreError"] = "ROBUX items are inaccessable at this time.";
|
||||
|
||||
["NoFriendsPhrase"] = "Your friends are not online. Play some games and make new friends!";
|
||||
|
||||
-- Platform Service Errors
|
||||
["PopupPartyUIErrorPhrase"] = "There was an error trying to start a party. Please try again.";
|
||||
|
||||
-- Auth Errors
|
||||
["AuthenticationErrorTitle"] = "Authentication Error";
|
||||
["AuthInProgressPhrase"] = "Authentication already in progress";
|
||||
["AuthAccountUnlinkedPhrase"] = ""; -- This is a special case handled by EngagementScreen
|
||||
["AuthMissingGamePadPhrase"] = "No gamepad detected. Please turn on a gamepad.";
|
||||
["AuthNoUserDetectedPhrase"] = "No Xbox Live user detected. Please sign in to an Xbox Live account.";
|
||||
["AuthHttpErrorDetected"] = "Trouble communicating with ROBLOX servers. Please check www.watrbx.wtf/help/xbox for more info.";
|
||||
["AuthErrorPhrase"] = "Trouble communicating with ROBLOX servers. Please try again.";
|
||||
|
||||
-- Reauth (Booted to engagement screen)
|
||||
["ReauthSignedOutTitle"] = "Signed Out";
|
||||
["ReauthSignedOutPhrase"] = "You have signed out of your Xbox Live account. Please sign in to continue.";
|
||||
["ReauthRemovedTitle"] = "User Changed";
|
||||
["ReauthRemovedPhrase"] = "We have detected a change in the active user. Please sign in again.";
|
||||
["ReauthInvalidSessionPhrase"] = "You have been signed out of all current ROBLOX sessons.";
|
||||
["ReauthUnlinkTitle"] = "Account Unlinked";
|
||||
["ReauthUnlinkPhrase"] = "You have successfully unlinked from your ROBLOX account. You can now choose to sign in again, or sign up for a new ROBLOX account.";
|
||||
["ReauthUnknownPhrase"] = "An error occurred and you have been signed out. Please sign in again to continue playing.";
|
||||
|
||||
-- Sign In
|
||||
["NewUserPhrase"] = "Community Created Gaming. Limitless Possibilities.";
|
||||
["SignInPhrase"] = "Sign In";
|
||||
["PlayAsPhrase"] = "Sign Up Using %s";
|
||||
["UsernameWord"] = "Username";
|
||||
["PasswordWord"] = "Password";
|
||||
["UsernameRulePhrase"] = "3-20 characters, no spaces";
|
||||
["PasswordRulePhrase"] = "6 letters and 2 numbers minimum";
|
||||
["AccountSettingsTitle"] = "Account Settings";
|
||||
["PlatformLinkInfoTitle"] = "Welcome to ROBLOX!";
|
||||
["PlatformLinkInfoMessage"] = "We have created a new ROBLOX account for you. All game progress and purchases will be saved to this ROBLOX account. Now you can sign in on any platform and continue where you left off!";
|
||||
|
||||
-- Sign In/Up Errors
|
||||
["InvalidUsernameTitle"] = "Invalid Username";
|
||||
["InvalidPasswordTitle"] = "Invalid Password";
|
||||
["InvalidUsernamePhrase"] = "Usernames must have 3-20 characters.";
|
||||
["InvalidPasswordPhrase"] = "Passwords must have at least 6 letters and 2 numbers";
|
||||
["AlreadyTakenTitle"] = "Username Taken";
|
||||
["AlreadyTakenPhrase"] = "That username is already taken, please try another.";
|
||||
["InvalidCharactersUsedPhrase"] = "The username you entered contains invalid characters. Only letters and numbers are allowed.";
|
||||
["UsernameCannotContainSpacesPhrase"] = "The username you entered contains spaces. Only letters and numbers are allowed.";
|
||||
["NoUsernameEnteredPhrase"] = "Username is required.";
|
||||
["NoUsernameOrPasswordEnteredPhrase"] = "You must enter a valid username and password to set up your ROBLOX account.";
|
||||
["LinkedAsPhrase"] = "%s is currently linked to your ROBLOX account, %s.";
|
||||
|
||||
-- Linking Errors
|
||||
["LinkSignUpDisabled"] = "Sign up is currently disabled. Please try again later.";
|
||||
["LinkFlooded"] = "You have signed up too many times today. Please sign in with an existing ROBLOX account or try again later.";
|
||||
["LinkLeaseLocked"] = "Transaction in progress. Please wait.";
|
||||
["LinkAccountLinkingDisabled"] = "Account linking is currently disabled. Please try again later.";
|
||||
["LinkInvalidRobloxUser"] = "The ROBLOX username you entered is invalid. Please enter a valid ROBLOX username.";
|
||||
["LinkRobloxUserAlreadyLinked"] = "Your Xbox Live account is already linked to a ROBLOX account";
|
||||
["LinkXboxUserAlreadyLinked"] = "Your Xbox Live account is already linked to a ROBLOX account.";
|
||||
["LinkIllegalChildAccountLinking"] = "The accounts could not be linked. Please review your Xbox age settings.";
|
||||
["LinkInvalidPassword"] = "The password you entered is invalid. Please enter the correct password.";
|
||||
["LinkUsernamePasswordNotSet"] = "Username or password is empty. Please enter a username and password and try again.";
|
||||
["LinkUsernameAlreadyTaken"] = "That username is already taken. Please choose another username and try again.";
|
||||
["LinkInvalidCredentials"] = "The username or password is invalid. Please enter a valid username and password and try again.";
|
||||
["LinkUnknownError"] = "An unknown error has occurred. Please try again.";
|
||||
|
||||
-- Linking
|
||||
["LinkAccountTitle"] = "Sign in to ROBLOX";
|
||||
["LinkAccountPhrase"] = "Sign in with an existing ROBLOX account to access your Avatar appearance and save game progress.\n\nYour ROBLOX account will be linked to your Xbox Live account, and the next time you play you will sign in automatically!\n\nYou can unlink from the Settings screen at any time.";
|
||||
|
||||
-- Sign Up
|
||||
["SignUpWord"] = "Sign up";
|
||||
["SignUpTitle"] = "Create a ROBLOX Account";
|
||||
["SignUpPhrase"] = "A ROBLOX account is your access to ROBLOX on every platform. Simply set a username and password and you can sign in anywhere and continue where you left off!\n\nYour new ROBLOX account will be linked to your Xbox Live account, and the next time you play you will sign in automatically!\n\nYou can unlink from the Settings screen at any time.";
|
||||
|
||||
-- Edge Case - have linked account but no credentials
|
||||
["SetCredentialsTitle"] = "Assign Username and Password";
|
||||
["SetCredentialsPhrase"] = "Looks like you were interrupted before you could finish signing up! Don't worry, you can finish setting up your ROBLOX account by choosing a username and password here.\n\nYour ROBLOX account will be linked to your Xbox Live account, and the next time you play you will sign in automatically!\n\nYou can unlink from the Settings screen at any time.";
|
||||
["SetCredentialsWord"] = "Assign";
|
||||
|
||||
-- Unlink
|
||||
["UnlinkTitle"] = "Unlink Account";
|
||||
["UnlinkGamerTagPhrase"] = "Unlink %s";
|
||||
["UnlinkPhrase"] = "Are you sure you want to unlink this ROBLOX account from your Xbox account? Your save data and purchases are associated with this ROBLOX account and will be inaccessible until you sign back in!";
|
||||
|
||||
-- Error Strings
|
||||
["UnableToJoinTitle"] = "Unable to Join";
|
||||
["ErrorOccurredTitle"] = "An Error Occurred";
|
||||
|
||||
["DefaultErrorPhrase"] = "Could not connect to ROBLOX. Please try again later.";
|
||||
|
||||
["DefaultJoinFailPhrase"] = "Could not connect to ROBLOX. Please try again later.";
|
||||
["AlreadyRunningPhrase"] = "You are already in this ROBLOX game.";
|
||||
["WebServerConnectFailPhrase"] = "Could not connect to ROBLOX servers. Please try again later.";
|
||||
["AccessDeniedByWeb"] = "The ROBLOX game you are trying to join is currently not available.";
|
||||
["InstanceNotFound"] = "The ROBLOX game you are trying to join is currently not available.";
|
||||
["GameFullPhrase"] = "The ROBLOX game you are trying to join is currently full.";
|
||||
["FollowUserFailed"] = "The ROBLOX game you are trying to join is currently not available.";
|
||||
|
||||
["UnableToEquipTitle"] = "Unable to Select";
|
||||
["UnableToEquipPhrase"] = "We were unable to select that Avatar. Please try again later.";
|
||||
|
||||
["UnableToWearOufitTitle"] = "Unable to Select";
|
||||
["UnableToWearOufitPhrase"] = "We were unable to select that Outfit. Please try again later.";
|
||||
|
||||
["UnableToDoPurchaseTitle"] = "Unable to Complete Purchase";
|
||||
["UnableToDoPurchasePhrase"] = "We were unable to complete your purchase. Please try again later.";
|
||||
|
||||
["UnableToDoRobuxPurchaseTitle"] = "Unable to Complete Purchase";
|
||||
["UnableToDoRobuxPurchasePhrase"] = "We were unable to complete your purchase. Please try again later.";
|
||||
|
||||
["CannotVoteTitle"] = "Cannot Vote";
|
||||
["VoteFloodPhrase"] = "You're voting too often. Come back later and try again.";
|
||||
["VotePlayGamePhrase"] = "You must play this game before voting.";
|
||||
|
||||
["CannotFavoriteTitle"] = "Cannot Favorite Game";
|
||||
["FavoriteFloodPhrase"] = "You're favoriting games too often. Come back later and try again.";
|
||||
|
||||
-- Controller connection errors
|
||||
["ControllerLostConnectionTitle"] = "Missing Controller";
|
||||
["ControllerLostConnectionPhrase"] = "Controller for user '%s' has been disconnected, please press 'A' on the controller you would like to continue with.";
|
||||
|
||||
["ActiveUserLostConnectionTitle"] = "Active User Removed";
|
||||
["ActiveUserLostConnectionPhrase"] = "User '%s' has been logged out, please press 'A' on the controller you would like to continue with.";
|
||||
|
||||
-- Terms of Service & Privacy
|
||||
["ToSPhrase"] = "View Terms...";
|
||||
["ToSInfoLinkPhrase"] = "Terms & Privacy Policy:\nwww.watrbx.wtf/info/terms-of-service\nwww.watrbx.wtf/info/Privacy.aspx";
|
||||
["PrivacyPhrase"] = "Privacy";
|
||||
|
||||
-- Codes
|
||||
["VersionIdString"] = "Version: %s.%s.%s.%s";
|
||||
["ErrorMessageAndCodePrase"] = "%s\nError Code: %d";
|
||||
|
||||
-- Play My Place
|
||||
["PlayMyPlaceMoreGamesTitle"] = "My Games";
|
||||
["PlayMyPlaceMoreGamesPhrase"] = "Sign in on watrbx.wtf to create more games and edit your existing creations!";
|
||||
["PrivateSessionPhrase"] = "In Private Game";
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
local this = {}
|
||||
|
||||
|
||||
function this:GetLocale()
|
||||
return enUS
|
||||
end
|
||||
|
||||
function this:LocalizedString(stringKey)
|
||||
local locale = self:GetLocale()
|
||||
local result = locale and locale[stringKey]
|
||||
if not result then
|
||||
print("LocalizedString: Could not find string for:" , stringKey , "using locale:" , locale)
|
||||
result = stringKey
|
||||
end
|
||||
return result
|
||||
end
|
||||
|
||||
|
||||
return this
|
||||
@@ -0,0 +1,120 @@
|
||||
--[[
|
||||
// NoActionOverlay.lua
|
||||
|
||||
// Creates an overlay where the user cannot take an action
|
||||
// to remove.
|
||||
|
||||
// This is used when we detect something wrong with input or the active user
|
||||
// being lost
|
||||
]]
|
||||
|
||||
local DATAMODEL_TYPE = {
|
||||
APP_SHELL = 0;
|
||||
GAME = 1;
|
||||
}
|
||||
|
||||
local CoreGui = Game:GetService("CoreGui")
|
||||
local GuiRoot = CoreGui:FindFirstChild("RobloxGui")
|
||||
local Modules = GuiRoot:FindFirstChild("Modules")
|
||||
local GuiService = game:GetService('GuiService')
|
||||
local ContextActionService = game:GetService("ContextActionService")
|
||||
local PlatformService = nil
|
||||
pcall(function() PlatformService = game:GetService('PlatformService') end)
|
||||
|
||||
local BaseOverlay = require(Modules:FindFirstChild('BaseOverlay'))
|
||||
local GlobalSettings = require(Modules:FindFirstChild('GlobalSettings'))
|
||||
local Utility = require(Modules:FindFirstChild('Utility'))
|
||||
|
||||
local createNoActionOverlay = function(errorType)
|
||||
local this = BaseOverlay()
|
||||
|
||||
local title = errorType.Title
|
||||
local message = errorType.Msg
|
||||
local errorCode = errorType.Code
|
||||
|
||||
local iconImage = Utility.Create'ImageLabel'
|
||||
{
|
||||
Name = "IconImage";
|
||||
Size = UDim2.new(0, 416, 0, 416);
|
||||
BackgroundTransparency = 1;
|
||||
ZIndex = this.BaseZIndex;
|
||||
Image = 'rbxasset://textures/ui/Shell/Icons/AlertIcon.png';
|
||||
}
|
||||
Utility.CalculateAnchor(iconImage, UDim2.new(0.5, 0, 0.5, 0), Utility.Enum.Anchor.Center)
|
||||
this:SetImage(iconImage)
|
||||
|
||||
local titleText = Utility.Create'TextLabel'
|
||||
{
|
||||
Name = "TitleText";
|
||||
Size = UDim2.new(0, 0, 0, 0);
|
||||
Position = UDim2.new(0, this.RightAlign, 0, 136);
|
||||
BackgroundTransparency = 1;
|
||||
Font = GlobalSettings.RegularFont;
|
||||
FontSize = GlobalSettings.HeaderSize;
|
||||
TextColor3 = GlobalSettings.WhiteTextColor;
|
||||
Text = title;
|
||||
TextXAlignment = Enum.TextXAlignment.Left;
|
||||
ZIndex = this.BaseZIndex;
|
||||
Parent = this.Container;
|
||||
}
|
||||
|
||||
local descriptionText = Utility.Create'TextLabel'
|
||||
{
|
||||
Name = "DescriptionText";
|
||||
Size = UDim2.new(0, 762, 0, 304);
|
||||
Position = UDim2.new(0, this.RightAlign, 0, titleText.Position.Y.Offset + 62);
|
||||
BackgroundTransparency = 1;
|
||||
TextXAlignment = Enum.TextXAlignment.Left;
|
||||
TextYAlignment = Enum.TextYAlignment.Top;
|
||||
Font = GlobalSettings.LightFont;
|
||||
FontSize = GlobalSettings.TitleSize;
|
||||
TextColor3 = GlobalSettings.WhiteTextColor;
|
||||
TextWrapped = true;
|
||||
Text = message;
|
||||
ZIndex = this.BaseZIndex;
|
||||
Parent = this.Container;
|
||||
}
|
||||
if errorCode then
|
||||
descriptionText.Text = string.format(Strings:LocalizedString('ErrorMessageAndCodePrase'), message, errorCode)
|
||||
end
|
||||
|
||||
function this:GetPriority()
|
||||
return GlobalSettings.ImmediatePriority
|
||||
end
|
||||
|
||||
--[[ Public API ]]--
|
||||
-- override
|
||||
function this:Focus()
|
||||
-- DO NOTHING
|
||||
end
|
||||
function this:RemoveFocus()
|
||||
-- DO NOTHING
|
||||
end
|
||||
|
||||
|
||||
local baseShow = this.Show
|
||||
function this:Show()
|
||||
ContextActionService:BindCoreAction("StopControllerInput", function() end, false, Enum.UserInputType.Gamepad1)
|
||||
baseShow(self)
|
||||
end
|
||||
|
||||
local baseHide = this.Hide
|
||||
function this:Hide()
|
||||
baseHide(self)
|
||||
ContextActionService:UnbindCoreAction("StopControllerInput")
|
||||
end
|
||||
|
||||
local baseFocus = this.Focus
|
||||
function this:Focus()
|
||||
baseFocus()
|
||||
-- NOTE: This might want to be:
|
||||
-- `not PlatformService or`
|
||||
if PlatformService and PlatformService.DatamodelType == DATAMODEL_TYPE.APP_SHELL then
|
||||
GuiService.SelectedCoreObject = nil
|
||||
end
|
||||
end
|
||||
|
||||
return this
|
||||
end
|
||||
|
||||
return createNoActionOverlay
|
||||
@@ -0,0 +1,148 @@
|
||||
|
||||
--[[
|
||||
// OutfitData.lua
|
||||
|
||||
// Created by Kip Turner
|
||||
// Copyright Roblox 2015
|
||||
]]
|
||||
|
||||
local CoreGui = Game:GetService("CoreGui")
|
||||
local GuiRoot = CoreGui:FindFirstChild("RobloxGui")
|
||||
local Modules = GuiRoot:FindFirstChild("Modules")
|
||||
|
||||
local Utility = require(Modules:FindFirstChild('Utility'))
|
||||
local UserData = require(Modules:FindFirstChild('UserData'))
|
||||
|
||||
|
||||
local Http = require(Modules:FindFirstChild('Http'))
|
||||
local EventHub = require(Modules:FindFirstChild('EventHub'))
|
||||
|
||||
local OutfitData = {}
|
||||
|
||||
local wearingOutfit = nil
|
||||
|
||||
local function CreateOutfitItem(outfitInfo)
|
||||
local this = {}
|
||||
local isWearing = false
|
||||
this.IsWearingChanged = Utility.Signal()
|
||||
|
||||
function this:GetUserId()
|
||||
return outfitInfo['UserId']
|
||||
end
|
||||
|
||||
function this:GetOutfitId()
|
||||
return outfitInfo['OutfitId']
|
||||
end
|
||||
|
||||
function this:GetId()
|
||||
return outfitInfo['Id']
|
||||
end
|
||||
|
||||
function this:GetName()
|
||||
return outfitInfo['Name']
|
||||
end
|
||||
|
||||
function this:IsOwned()
|
||||
return true
|
||||
end
|
||||
|
||||
function this:IsWearing()
|
||||
local wasWearing = isWearing
|
||||
isWearing = (self:GetId() == wearingOutfit)
|
||||
if isWearing ~= wasWearing then
|
||||
self.IsWearingChanged:fire(isWearing)
|
||||
end
|
||||
return isWearing
|
||||
end
|
||||
|
||||
function this:WearAsync()
|
||||
local result = Http.PostWearUserOutfitAsync(self:GetId())
|
||||
-- if result and result['success'] == true then
|
||||
wearingOutfit = self:GetId()
|
||||
EventHub:dispatchEvent(EventHub.Notifications["DonnedDifferentOutfit"], self:GetId())
|
||||
-- end
|
||||
return
|
||||
end
|
||||
|
||||
return this
|
||||
end
|
||||
|
||||
|
||||
local OutfitCache = nil
|
||||
local RbxUid = nil
|
||||
|
||||
local debounceGetGetMyOutfitsAsync = false
|
||||
function OutfitData:GetMyOutfitsAsync()
|
||||
while debounceGetGetMyOutfitsAsync do wait() end
|
||||
|
||||
debounceGetGetMyOutfitsAsync = true
|
||||
UserData.GetLocalPlayerAsync()
|
||||
|
||||
if RbxUid ~= UserData:GetRbxUserId() then
|
||||
OutfitCache = nil
|
||||
end
|
||||
|
||||
while not OutfitCache do
|
||||
local startRbxUid = UserData:GetRbxUserId()
|
||||
local outfits = {}
|
||||
local index = 0
|
||||
local count = 20
|
||||
|
||||
repeat
|
||||
local result = nil
|
||||
|
||||
Utility.ExponentialRepeat(
|
||||
function() return result == nil end,
|
||||
function() result = Http.GetMyUserOutfitsAsync(index, count) end,
|
||||
5)
|
||||
|
||||
if result then
|
||||
local userOutfits = result['UserOutfits']
|
||||
if userOutfits then
|
||||
for _, outfitInfo in pairs(userOutfits) do
|
||||
table.insert(outfits, CreateOutfitItem(outfitInfo))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
index = index + count
|
||||
until result == nil or result['FinalPage']
|
||||
|
||||
local nowRbxUid = UserData:GetRbxUserId()
|
||||
if startRbxUid == nowRbxUid then
|
||||
OutfitCache = outfits
|
||||
end
|
||||
print("Getting info cache again" , "now" , nowRbxUid , "startRbxUid" , startRbxUid)
|
||||
end
|
||||
|
||||
debounceGetGetMyOutfitsAsync = false
|
||||
return OutfitCache
|
||||
end
|
||||
|
||||
function OutfitData:GetCachedWearingOutfitId()
|
||||
return wearingOutfit
|
||||
end
|
||||
|
||||
EventHub:addEventListener(EventHub.Notifications["DonnedDifferentPackage"], "OutfitData",
|
||||
function()
|
||||
wearingOutfit = nil
|
||||
if OutfitCache then
|
||||
for _, outfit in pairs(OutfitCache) do
|
||||
outfit:IsWearing()
|
||||
end
|
||||
end
|
||||
end)
|
||||
EventHub:addEventListener(EventHub.Notifications["DonnedDifferentOutfit"], "OutfitData",
|
||||
function()
|
||||
if OutfitCache then
|
||||
for _, outfit in pairs(OutfitCache) do
|
||||
outfit:IsWearing()
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
|
||||
return OutfitData
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
--[[
|
||||
// OutfitTile.lua
|
||||
|
||||
// Created by Kip Turner
|
||||
// Copyright Roblox 2015
|
||||
]]
|
||||
|
||||
|
||||
local ContextActionService = game:GetService("ContextActionService")
|
||||
local CoreGui = game:GetService("CoreGui")
|
||||
local GuiRoot = CoreGui:FindFirstChild("RobloxGui")
|
||||
local Modules = GuiRoot:FindFirstChild("Modules")
|
||||
|
||||
|
||||
local Utility = require(Modules:FindFirstChild('Utility'))
|
||||
local SoundManager = require(Modules:FindFirstChild('SoundManager'))
|
||||
local ScreenManager = require(Modules:FindFirstChild('ScreenManager'))
|
||||
local Errors = require(Modules:FindFirstChild('Errors'))
|
||||
local ErrorOverlayModule = require(Modules:FindFirstChild('ErrorOverlay'))
|
||||
local ThumbnailLoader = require(Modules:FindFirstChild('ThumbnailLoader'))
|
||||
|
||||
|
||||
local BaseTile = require(Modules:FindFirstChild('BaseTile'))
|
||||
|
||||
|
||||
local function createOutfitTileContainer(outfitData)
|
||||
local this = BaseTile()
|
||||
|
||||
local function wearOutfitAsync()
|
||||
local result = outfitData:WearAsync()
|
||||
|
||||
-- print("Wear Outfit Result:" , Utility.PrettyPrint(result))
|
||||
|
||||
-- if result and result['success'] == true then
|
||||
-- else
|
||||
-- local err = Errors.OutfitEquip['Default']
|
||||
-- ScreenManager:OpenScreen(ErrorOverlayModule(err), false)
|
||||
-- end
|
||||
end
|
||||
|
||||
local thumbnailLoader = ThumbnailLoader:Create(this.AvatarImage, outfitData:GetOutfitId(), ThumbnailLoader.Sizes.Medium, ThumbnailLoader.AssetType.Outfit, true)
|
||||
spawn(function()
|
||||
thumbnailLoader:LoadAsync(false, true)
|
||||
end)
|
||||
|
||||
this:SetPopupText(outfitData:GetName())
|
||||
|
||||
function this:UpdateEquipButton()
|
||||
self.EquippedCheckmark.Visible = outfitData:IsWearing()
|
||||
end
|
||||
|
||||
function this:GetPackageInfo()
|
||||
return outfitData
|
||||
end
|
||||
|
||||
local selectDebounce = false
|
||||
function this:Select()
|
||||
if selectDebounce then return false end
|
||||
selectDebounce = true
|
||||
spawn(function()
|
||||
wearOutfitAsync()
|
||||
selectDebounce = false
|
||||
end)
|
||||
return true
|
||||
end
|
||||
|
||||
|
||||
local isWearingConn = nil
|
||||
local baseShow = this.Show
|
||||
function this:Show()
|
||||
baseShow(self)
|
||||
self:SetActive(true)
|
||||
Utility.DisconnectEvent(isWearingConn)
|
||||
isWearingConn = outfitData.IsWearingChanged:connect(function() self:UpdateEquipButton() end)
|
||||
end
|
||||
|
||||
local baseHide = this.Hide
|
||||
function this:Hide()
|
||||
baseHide(self)
|
||||
isWearingConn = Utility.DisconnectEvent(isWearingConn)
|
||||
end
|
||||
|
||||
local baseFocus = this.Focus
|
||||
function this:Focus()
|
||||
baseFocus(self)
|
||||
|
||||
-- ContextActionService:BindCoreAction("WearSelectedOutfitItem",
|
||||
-- function(actionName, inputState, inputObject)
|
||||
-- if inputState == Enum.UserInputState.End then
|
||||
-- SoundManager:Play('ButtonPress')
|
||||
-- wearOutfitAsync()
|
||||
-- end
|
||||
-- end,
|
||||
-- false,
|
||||
-- Enum.KeyCode.ButtonX)
|
||||
end
|
||||
|
||||
local baseRemoveFocus = this.RemoveFocus
|
||||
function this:RemoveFocus()
|
||||
baseRemoveFocus(self)
|
||||
-- ContextActionService:UnbindCoreAction("WearSelectedOutfitItem")
|
||||
end
|
||||
|
||||
return this
|
||||
end
|
||||
|
||||
return createOutfitTileContainer
|
||||
@@ -0,0 +1,450 @@
|
||||
-- Written by Kip Turner, Copyright ROBLOX 2015
|
||||
|
||||
local TextService = game:GetService('TextService')
|
||||
|
||||
local CoreGui = game:GetService("CoreGui")
|
||||
local GuiService = game:GetService('GuiService')
|
||||
local RunService = game:GetService('RunService')
|
||||
local ContextActionService = game:GetService("ContextActionService")
|
||||
local PlatformService = nil
|
||||
pcall(function() PlatformService = game:GetService('PlatformService') end)
|
||||
|
||||
local GameOptionsSettings = settings():FindFirstChild("Game Options")
|
||||
|
||||
local GuiRoot = CoreGui:FindFirstChild("RobloxGui")
|
||||
local Modules = GuiRoot:FindFirstChild("Modules")
|
||||
|
||||
local Utility = require(Modules:FindFirstChild('Utility'))
|
||||
local GlobalSettings = require(Modules:FindFirstChild('GlobalSettings'))
|
||||
local ScreenManager = require(Modules:FindFirstChild('ScreenManager'))
|
||||
|
||||
local AssetManager = require(Modules:FindFirstChild('AssetManager'))
|
||||
local Strings = require(Modules:FindFirstChild('LocalizedStrings'))
|
||||
|
||||
local BACKGROUND_COLOR = Color3.new(3/255,3/255,3/255)
|
||||
local START_EDGE_SIZE = UDim2.new(0.9,0,0.9,0)
|
||||
local MIN_EDGE_SIZE = UDim2.new(0.85,0,0.85,0)
|
||||
local MAX_EDGE_SIZE = UDim2.new(1,0,1,0)
|
||||
local ZERO_VEC2 = Vector2.new(0,0)
|
||||
local THUMBSTICK_BORDER_SENSITIVITY = 0.05
|
||||
local MAX_STICK_ACCELERATION = 3
|
||||
local ACCELERATION_RATE = 1
|
||||
local DPAD_STEP_AMOUNT = 2
|
||||
local DPAD_CODE_TO_EDGE_PUSH =
|
||||
{
|
||||
[Enum.KeyCode.DPadDown] = Vector2.new(0.0, DPAD_STEP_AMOUNT);
|
||||
[Enum.KeyCode.DPadUp] = Vector2.new(0.0, -DPAD_STEP_AMOUNT);
|
||||
[Enum.KeyCode.DPadLeft] = Vector2.new(-DPAD_STEP_AMOUNT, 0.0);
|
||||
[Enum.KeyCode.DPadRight] = Vector2.new(DPAD_STEP_AMOUNT, 0.0);
|
||||
}
|
||||
|
||||
|
||||
local function CreateOverscanAdjustmentScreen(parent)
|
||||
local this = {}
|
||||
|
||||
this.StickPosition = ZERO_VEC2
|
||||
this.StickAcceleration = 1
|
||||
local lastUpdate = nil
|
||||
local lastParent = parent
|
||||
|
||||
local lastSavedOverscanPX = START_EDGE_SIZE.X.Scale
|
||||
local lastSavedOverscanPY = START_EDGE_SIZE.Y.Scale
|
||||
|
||||
local function StorePreviousOverscanValues()
|
||||
local success, errormsg = pcall(function()
|
||||
lastSavedOverscanPX = GameOptionsSettings.OverscanPX
|
||||
lastSavedOverscanPY = GameOptionsSettings.OverscanPY
|
||||
end)
|
||||
if not success then
|
||||
print("Error, StorePreviousOverscanValues: OverscanPX and OverscanPY" , errormsg)
|
||||
end
|
||||
end
|
||||
StorePreviousOverscanValues()
|
||||
|
||||
local MainContainer = Utility.Create'Frame'
|
||||
{
|
||||
Name = 'OverscanAdjustmentScreen';
|
||||
Size = UDim2.new(1, 0, 1, 0);
|
||||
BorderSizePixel = 1;
|
||||
BackgroundTransparency = 0;
|
||||
BackgroundColor3 = BACKGROUND_COLOR;
|
||||
Visible = false;
|
||||
}
|
||||
|
||||
local BackgroundImage = Utility.Create'ImageLabel'
|
||||
{
|
||||
Name = 'BackgroundImage';
|
||||
Image = 'rbxasset://textures/ui/Shell/ScreenAdjustment/Background.png';
|
||||
Size = UDim2.new(1,0,1,0);
|
||||
BackgroundTransparency = 1;
|
||||
Parent = MainContainer;
|
||||
};
|
||||
|
||||
local Title = Utility.Create'TextLabel'
|
||||
{
|
||||
Name = 'Title';
|
||||
Text = Strings:LocalizedString('ScreenSizeWord');
|
||||
Position = UDim2.new(0, 230, 0, 175);
|
||||
TextXAlignment = 'Left';
|
||||
TextColor3 = GlobalSettings.WhiteTextColor;
|
||||
Font = GlobalSettings.LightFont;
|
||||
FontSize = GlobalSettings.HeaderSize;
|
||||
BackgroundTransparency = 1;
|
||||
Parent = MainContainer;
|
||||
};
|
||||
do
|
||||
local titleTextSize = TextService:GetTextSize(Title.Text, Utility.ConvertFontSizeEnumToInt(Title.FontSize), Title.Font, Vector2.new())
|
||||
Title.Size = UDim2.new(0, titleTextSize.X, 0, titleTextSize.Y)
|
||||
end
|
||||
|
||||
local Prompt = Utility.Create'TextLabel'
|
||||
{
|
||||
Name = 'Prompt';
|
||||
Text = Strings:LocalizedString('ResizeScreenPrompt');
|
||||
Position = UDim2.new(0,230,0,225);
|
||||
TextXAlignment = 'Left';
|
||||
TextColor3 = GlobalSettings.WhiteTextColor;
|
||||
Font = GlobalSettings.RegularFont;
|
||||
FontSize = GlobalSettings.ButtonSize;
|
||||
BackgroundTransparency = 1;
|
||||
Parent = MainContainer;
|
||||
};
|
||||
|
||||
|
||||
do
|
||||
local ControllerImage = Utility.Create'ImageLabel'
|
||||
{
|
||||
Name = 'ControllerImage';
|
||||
BackgroundTransparency = 1;
|
||||
Image = 'rbxasset://textures/ui/Shell/ScreenAdjustment/Controller@1080.png';
|
||||
Size = UDim2.new(0,599,0,404);
|
||||
Parent = MainContainer;
|
||||
};
|
||||
Utility.CalculateAnchor(ControllerImage, UDim2.new(0.5,0,0.5,0), Utility.Enum.Anchor.Center)
|
||||
|
||||
local Line = Utility.Create'Frame'
|
||||
{
|
||||
Name = 'Line';
|
||||
Size = UDim2.new(0.4,0,0,1);
|
||||
Position = UDim2.new(0.73, 0, 0.545, 0);
|
||||
BackgroundColor3 = Color3.new(1,1,1);
|
||||
BorderSizePixel = 0;
|
||||
BackgroundTransparency = 0;
|
||||
Parent = ControllerImage;
|
||||
};
|
||||
local InputHint = Utility.Create'TextLabel'
|
||||
{
|
||||
Name = 'InputHint';
|
||||
Text = Strings:LocalizedString('ResizeScreenInputHint');
|
||||
Size = UDim2.new(0,0,0,0);
|
||||
Position = UDim2.new(1, 3, 0, -1);
|
||||
TextXAlignment = 'Left';
|
||||
TextColor3 = GlobalSettings.WhiteTextColor;
|
||||
Font = GlobalSettings.RegularFont;
|
||||
FontSize = GlobalSettings.ButtonSize;
|
||||
BackgroundTransparency = 1;
|
||||
Parent = Line;
|
||||
};
|
||||
end
|
||||
|
||||
local AcceptButtonImage = Utility.Create'ImageLabel'
|
||||
{
|
||||
Name = 'AcceptButtonImage';
|
||||
Size = UDim2.new(0,65,0,65);
|
||||
Position = UDim2.new(0.5, 25, 0.75, 0);
|
||||
Image = 'rbxasset://textures/ui/Shell/ButtonIcons/AButton.png';
|
||||
BackgroundTransparency = 1;
|
||||
Parent = MainContainer;
|
||||
};
|
||||
local AcceptHint = Utility.Create'TextLabel'
|
||||
{
|
||||
Name = 'AcceptHint';
|
||||
Text = Strings:LocalizedString('AcceptWord'):upper();
|
||||
Size = UDim2.new(0,0,1,0);
|
||||
Position = UDim2.new(1, 5, 0, -3);
|
||||
TextXAlignment = 'Left';
|
||||
TextColor3 = GlobalSettings.WhiteTextColor;
|
||||
Font = GlobalSettings.RegularFont;
|
||||
FontSize = GlobalSettings.ButtonSize;
|
||||
BackgroundTransparency = 1;
|
||||
Parent = AcceptButtonImage;
|
||||
};
|
||||
|
||||
local ResetButtonImage = Utility.Create'ImageLabel'
|
||||
{
|
||||
Name = 'ResetButtonImage';
|
||||
Size = UDim2.new(0,65,0,65);
|
||||
Image = 'rbxasset://textures/ui/Shell/ButtonIcons/XButton.png';
|
||||
BackgroundTransparency = 1;
|
||||
Parent = MainContainer;
|
||||
};
|
||||
local ResetHint = Utility.Create'TextLabel'
|
||||
{
|
||||
Name = 'ResetHint';
|
||||
Text = Strings:LocalizedString('ResetWord'):upper();
|
||||
Size = UDim2.new(0,0,1,0);
|
||||
Position = UDim2.new(1, 5, 0, -3);
|
||||
TextXAlignment = 'Left';
|
||||
TextColor3 = GlobalSettings.WhiteTextColor;
|
||||
Font = GlobalSettings.RegularFont;
|
||||
FontSize = GlobalSettings.ButtonSize;
|
||||
BackgroundTransparency = 1;
|
||||
Parent = ResetButtonImage;
|
||||
};
|
||||
|
||||
local Edges = Utility.Create'Frame'
|
||||
{
|
||||
Name = 'Edges';
|
||||
Size = START_EDGE_SIZE;
|
||||
BackgroundTransparency = 1;
|
||||
Parent = MainContainer;
|
||||
};
|
||||
do
|
||||
local edgesSelectionImage = Utility.Create'ImageLabel'
|
||||
{
|
||||
Name = 'EdgesSelectionImage';
|
||||
Size = UDim2.new(1,2,1,2);
|
||||
Position = UDim2.new(0,-1,0,-1);
|
||||
Image = 'rbxasset://textures/ui/Shell/ScreenAdjustment/ScreenRangeOverlay.png';
|
||||
ScaleType = Enum.ScaleType.Slice;
|
||||
SliceCenter = Rect.new(21,21,41,41);
|
||||
BackgroundTransparency = 1;
|
||||
};
|
||||
edgesSelectionImage.Parent = Edges
|
||||
end
|
||||
for i = 0, 3 do
|
||||
local CornerImage = Utility.Create'ImageLabel'
|
||||
{
|
||||
Name = 'CornerImage';
|
||||
Size = UDim2.new(0, 95, 0, 95);
|
||||
BackgroundTransparency = 1;
|
||||
Rotation = 90 * i;
|
||||
Image = "rbxasset://textures/ui/Shell/ScreenAdjustment/ScreenAdjustmentArrow.png";
|
||||
Parent = Edges;
|
||||
};
|
||||
|
||||
if i == 0 then
|
||||
Utility.CalculateAnchor(CornerImage, UDim2.new(0,0,0,0), Utility.Enum.Anchor.TopLeft)
|
||||
elseif i == 1 then
|
||||
Utility.CalculateAnchor(CornerImage, UDim2.new(1, 0, 0, 0), Utility.Enum.Anchor.TopRight)
|
||||
elseif i == 2 then
|
||||
Utility.CalculateAnchor(CornerImage, UDim2.new(1, 0, 1, 0), Utility.Enum.Anchor.BottomRight)
|
||||
elseif i == 3 then
|
||||
Utility.CalculateAnchor(CornerImage, UDim2.new(0, 0, 1, 0), Utility.Enum.Anchor.BottomLeft)
|
||||
end
|
||||
end
|
||||
|
||||
local function RefreshLayout()
|
||||
ResetButtonImage.Position = UDim2.new(0.5 - ResetButtonImage.Size.X.Scale, -ResetHint.TextBounds.X - 25 - ResetButtonImage.Size.X.Offset, 0.75, 0);
|
||||
Prompt.Size = UDim2.new(0, Prompt.TextBounds.X, 0, Prompt.TextBounds.Y)
|
||||
end
|
||||
local closedEvent = Instance.new("BindableEvent")
|
||||
closedEvent.Name = "ClosedEvent"
|
||||
this.Closed = closedEvent.Event
|
||||
|
||||
local EdgePercent = Vector2.new(START_EDGE_SIZE.X, START_EDGE_SIZE.Y)
|
||||
function this:GetAdjustmentEdgesPercent()
|
||||
return EdgePercent
|
||||
end
|
||||
|
||||
function this:SetAdjustmentEdges(newEdgeSize)
|
||||
EdgePercent = Utility.ClampVector2(Vector2.new(MIN_EDGE_SIZE.X.Scale, MIN_EDGE_SIZE.Y.Scale), Vector2.new(1,1), newEdgeSize)
|
||||
|
||||
local guiSize = GuiRoot.AbsoluteSize
|
||||
local absoluteEdgeSize = EdgePercent * guiSize
|
||||
|
||||
-- Round to nearest 2 so that we can evenly space above and below
|
||||
local roundedAbsoluteEdgeSize = Vector2.new(Utility.Round(absoluteEdgeSize.X/2), Utility.Round(absoluteEdgeSize.Y/2)) * 2
|
||||
roundedAbsoluteEdgeSize = Utility.ClampVector2(Vector2.new(), guiSize, roundedAbsoluteEdgeSize)
|
||||
|
||||
Edges.Size = UDim2.new(0, roundedAbsoluteEdgeSize.X, 0, roundedAbsoluteEdgeSize.Y)
|
||||
Edges.Position = UDim2.new(0, (guiSize.X - roundedAbsoluteEdgeSize.X) / 2, 0, (guiSize.Y - roundedAbsoluteEdgeSize.Y) / 2)
|
||||
end
|
||||
|
||||
function this:PushAdjustmentEdges(pushAmount)
|
||||
self:SetAdjustmentEdges(self:GetAdjustmentEdgesPercent() + pushAmount)
|
||||
end
|
||||
|
||||
function this:PushAdjustmentEdgesByPixels(pixelPushAmount)
|
||||
local guiSize = GuiRoot.AbsoluteSize
|
||||
local pushAmount = pixelPushAmount / guiSize
|
||||
|
||||
if Utility.IsFinite(pushAmount.X) and Utility.IsFinite(pushAmount.Y) then
|
||||
self:SetAdjustmentEdges(self:GetAdjustmentEdgesPercent() + pushAmount)
|
||||
end
|
||||
end
|
||||
|
||||
function this:Update()
|
||||
local now = tick()
|
||||
if lastUpdate and self.StickPosition ~= ZERO_VEC2 then
|
||||
local delta = now - lastUpdate
|
||||
local transformedStick = Utility.GamepadLinearToCurve(self.StickPosition, 0.2)
|
||||
self:PushAdjustmentEdges(Vector2.new(transformedStick.X, -transformedStick.Y) * self.StickAcceleration * delta * THUMBSTICK_BORDER_SENSITIVITY)
|
||||
if transformedStick ~= ZERO_VEC2 then
|
||||
self.StickAcceleration = math.min(self.StickAcceleration + delta * ACCELERATION_RATE, MAX_STICK_ACCELERATION)
|
||||
else
|
||||
self.StickAcceleration = 1
|
||||
end
|
||||
end
|
||||
lastUpdate = now
|
||||
end
|
||||
|
||||
function this:ResetEdges()
|
||||
self:SetAdjustmentEdges(Vector2.new(START_EDGE_SIZE.X, START_EDGE_SIZE.Y))
|
||||
end
|
||||
|
||||
local GuiRootChangedConn = nil
|
||||
function this:Show()
|
||||
MainContainer.Visible = true
|
||||
MainContainer.Parent = lastParent
|
||||
RefreshLayout()
|
||||
self.StickAcceleration = 1
|
||||
|
||||
if not UserSettings().GameSettings:InStudioMode() and GameOptionsSettings.OverscanPX > 0 and GameOptionsSettings.OverscanPY > 0 then
|
||||
this:SetAdjustmentEdges(Vector2.new(GameOptionsSettings.OverscanPX, GameOptionsSettings.OverscanPY))
|
||||
else
|
||||
self:ResetEdges()
|
||||
end
|
||||
|
||||
local success, errormsg = pcall(function()
|
||||
StorePreviousOverscanValues()
|
||||
-- Need to do this step so that the player
|
||||
-- can accurately estimate their TV's overscan
|
||||
GameOptionsSettings.OverscanPX = 1
|
||||
GameOptionsSettings.OverscanPY = 1
|
||||
end)
|
||||
if not success then
|
||||
print("Error resetting Overscan Screen Resolution:" , errormsg)
|
||||
end
|
||||
|
||||
Utility.DisconnectEvent(GuiRootChangedConn)
|
||||
GuiRootChangedConn = GuiRoot.Changed:connect(function(prop)
|
||||
if prop == 'AbsoluteSize' then
|
||||
self:PushAdjustmentEdgesByPixels(ZERO_VEC2)
|
||||
end
|
||||
end)
|
||||
self:PushAdjustmentEdgesByPixels(ZERO_VEC2)
|
||||
|
||||
end
|
||||
|
||||
function this:Hide()
|
||||
MainContainer.Visible = false
|
||||
MainContainer.Parent = nil
|
||||
GuiRootChangedConn = Utility.DisconnectEvent(GuiRootChangedConn)
|
||||
end
|
||||
|
||||
function this:Focus()
|
||||
GuiService.SelectedCoreObject = nil
|
||||
RefreshLayout()
|
||||
|
||||
self.StickPosition = ZERO_VEC2
|
||||
self.StickAcceleration = 1
|
||||
|
||||
local beginSeen = false
|
||||
|
||||
ContextActionService:BindCoreAction("ResetAdjustmentScreen",
|
||||
function(actionName, inputState, inputObject)
|
||||
if inputState == Enum.UserInputState.End then
|
||||
self:ResetEdges()
|
||||
end
|
||||
end,
|
||||
false,
|
||||
Enum.KeyCode.ButtonX)
|
||||
|
||||
ContextActionService:BindCoreAction("AcceptAdjustmentScreen",
|
||||
function(actionName, inputState, inputObject)
|
||||
if inputState == Enum.UserInputState.Begin then
|
||||
beginSeen = true
|
||||
elseif inputState == Enum.UserInputState.End and beginSeen then
|
||||
local success, errormsg = pcall(function()
|
||||
GameOptionsSettings.OverscanPX = math.min(1, self:GetAdjustmentEdgesPercent().X)
|
||||
GameOptionsSettings.OverscanPY = math.min(1, self:GetAdjustmentEdgesPercent().Y)
|
||||
StorePreviousOverscanValues()
|
||||
end)
|
||||
if not success then
|
||||
print("Error setting Overscan Screen Resolution:" , errormsg)
|
||||
end
|
||||
if self == ScreenManager:GetTopScreen() then
|
||||
ScreenManager:CloseCurrent()
|
||||
closedEvent:Fire()
|
||||
end
|
||||
end
|
||||
end,
|
||||
false,
|
||||
Enum.KeyCode.ButtonA)
|
||||
|
||||
ContextActionService:BindCoreAction("ThumbstickAdjustmentScreen",
|
||||
function(actionName, inputState, inputObject)
|
||||
self.StickPosition = Vector2.new(inputObject.Position.X, inputObject.Position.Y)
|
||||
end,
|
||||
false,
|
||||
Enum.KeyCode.Thumbstick2)
|
||||
|
||||
ContextActionService:BindCoreAction("DPadAdjustmentScreen",
|
||||
function(actionName, inputState, inputObject)
|
||||
if inputState == Enum.UserInputState.Begin then
|
||||
local pushAmount = DPAD_CODE_TO_EDGE_PUSH[inputObject.KeyCode]
|
||||
if pushAmount then
|
||||
self:PushAdjustmentEdgesByPixels(pushAmount)
|
||||
end
|
||||
end
|
||||
end,
|
||||
false,
|
||||
Enum.KeyCode.DPadDown, Enum.KeyCode.DPadUp, Enum.KeyCode.DPadLeft, Enum.KeyCode.DPadRight)
|
||||
|
||||
RunService:BindToRenderStep("UpdateAdjustmentScreen", Enum.RenderPriority.Input.Value, function() self:Update() end)
|
||||
end
|
||||
|
||||
function this:RemoveFocus()
|
||||
self.StickPosition = ZERO_VEC2
|
||||
lastUpdate = nil
|
||||
|
||||
ContextActionService:UnbindCoreAction("ResetAdjustmentScreen")
|
||||
ContextActionService:UnbindCoreAction("AcceptAdjustmentScreen")
|
||||
ContextActionService:UnbindCoreAction("ThumbstickAdjustmentScreen")
|
||||
ContextActionService:UnbindCoreAction("DPadAdjustmentScreen")
|
||||
|
||||
RunService:UnbindFromRenderStep("UpdateAdjustmentScreen")
|
||||
end
|
||||
|
||||
function this:SetPosition(newPosition)
|
||||
MainContainer.Position = newPosition
|
||||
end
|
||||
|
||||
function this:SetParent(newParent)
|
||||
MainContainer.Parent = newParent
|
||||
lastParent = newParent
|
||||
end
|
||||
|
||||
function this:SetStyleForInGame()
|
||||
BackgroundImage.Visible = false
|
||||
MainContainer.BackgroundTransparency = 0.2
|
||||
|
||||
local function setZIndex(guiObject, newZIndex)
|
||||
if not guiObject:IsA("GuiObject") then return end
|
||||
|
||||
guiObject.ZIndex = newZIndex
|
||||
|
||||
local children = guiObject:GetChildren()
|
||||
for i = 1, #children do
|
||||
setZIndex(children[i], newZIndex)
|
||||
end
|
||||
end
|
||||
|
||||
setZIndex(MainContainer, 9)
|
||||
end
|
||||
|
||||
|
||||
if not UserSettings().GameSettings:InStudioMode() then
|
||||
pcall(function() PlatformService.Suspended:connect(function()
|
||||
GameOptionsSettings.OverscanPX = lastSavedOverscanPX
|
||||
GameOptionsSettings.OverscanPY = lastSavedOverscanPY
|
||||
end)
|
||||
end)
|
||||
end
|
||||
|
||||
return this
|
||||
end
|
||||
|
||||
|
||||
return CreateOverscanAdjustmentScreen
|
||||
@@ -0,0 +1,380 @@
|
||||
|
||||
--[[
|
||||
// PackageData.lua
|
||||
|
||||
// Created by Kip Turner
|
||||
// Copyright Roblox 2015
|
||||
]]
|
||||
|
||||
local CoreGui = Game:GetService("CoreGui")
|
||||
local GuiRoot = CoreGui:FindFirstChild("RobloxGui")
|
||||
local Modules = GuiRoot:FindFirstChild("Modules")
|
||||
|
||||
|
||||
local Utility = require(Modules:FindFirstChild('Utility'))
|
||||
|
||||
local Http = require(Modules:FindFirstChild('Http'))
|
||||
local UserData = require(Modules:FindFirstChild('UserData'))
|
||||
local EventHub = require(Modules:FindFirstChild('EventHub'))
|
||||
|
||||
|
||||
local ContentProvider = game:GetService("ContentProvider")
|
||||
local MarketplaceService = Game:GetService('MarketplaceService')
|
||||
local PlatformService = nil
|
||||
pcall(function() PlatformService = game:GetService('PlatformService') end)
|
||||
|
||||
|
||||
local CurrentlyWearingAssetId = nil
|
||||
|
||||
local RequestingWearAsset = false
|
||||
|
||||
local function AwaitWearAssetRequest()
|
||||
while RequestingWearAsset do wait(0.1) end
|
||||
end
|
||||
|
||||
-- local function PreloadCharacterAppearanceAsync()
|
||||
-- -- Preload content so that when we join a game our appearance is cached
|
||||
-- local myAsset = Http.GetCharactersAssetsAsync(UserData:GetLocalUserIdAsync())
|
||||
-- print("MyAsset:" , myAsset)
|
||||
-- if myAsset then
|
||||
-- local assetList = Utility.SplitString(myAsset, ";")
|
||||
-- print("Parsed:" , Utility.PrettyPrint(assetList))
|
||||
-- ContentProvider:PreloadAsync(assetList)
|
||||
-- end
|
||||
-- end
|
||||
|
||||
local function PreloadCharacterAppearanceAsync()
|
||||
local character = nil
|
||||
local success, msg = pcall(function()
|
||||
character = game.Players:GetCharacterAppearanceAsync(UserData:GetLocalUserIdAsync())
|
||||
end)
|
||||
if character then
|
||||
local assetUrl = Http.BaseUrl .. 'asset/?id='
|
||||
local assetList = Utility.FindAssetsInModel(character, assetUrl)
|
||||
-- print("Preloading:" , Utility.PrettyPrint(assetList))
|
||||
ContentProvider:PreloadAsync(assetList)
|
||||
end
|
||||
|
||||
|
||||
return success
|
||||
end
|
||||
|
||||
local function SetOwnedInternal(packageData, newValue)
|
||||
if packageData.Owned ~= newValue then
|
||||
packageData.Owned = newValue
|
||||
packageData.OwnershipChanged:fire(packageData.Owned)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local function CreatePackageItem(data)
|
||||
local this = {}
|
||||
|
||||
this.Owned = false
|
||||
this.OwnershipChanged = Utility.Signal()
|
||||
this.IsWearingChanged = Utility.Signal()
|
||||
|
||||
local productInfo = nil
|
||||
|
||||
-- print("PackageData:" , Utility.PrettyPrint(data))
|
||||
|
||||
|
||||
function this:GetAssetId()
|
||||
local assetId = data and data['AssetId']
|
||||
if not assetId then
|
||||
assetId = data and data['Item'] and data['Item']['AssetId']
|
||||
end
|
||||
return assetId
|
||||
end
|
||||
|
||||
function this:GetProductIdAsync()
|
||||
while not productInfo do wait() end
|
||||
return productInfo and productInfo['ProductId']
|
||||
end
|
||||
|
||||
function this:BuyAsync()
|
||||
print("Do buy" , 'productId' , self:GetProductIdAsync() , 'robuxPrice' , self:GetRobuxPrice())
|
||||
local purchaseResult = Http.PurchaseProductAsync(self:GetProductIdAsync(), self:GetRobuxPrice(), self:GetCreatorId(), 1)
|
||||
local nowOwns = purchaseResult and purchaseResult['TransactionVerb'] == 'bought'
|
||||
if nowOwns then
|
||||
SetOwnedInternal(self, nowOwns)
|
||||
end
|
||||
return purchaseResult
|
||||
end
|
||||
|
||||
function this:IsOwned()
|
||||
return self.Owned
|
||||
end
|
||||
|
||||
local lastIsWearing = nil
|
||||
function this:IsWearing()
|
||||
local wasWearing = lastIsWearing
|
||||
lastIsWearing = CurrentlyWearingAssetId and CurrentlyWearingAssetId == self:GetAssetId() and self:IsOwned()
|
||||
if lastIsWearing ~= wasWearing then
|
||||
self.IsWearingChanged:fire(lastIsWearing)
|
||||
end
|
||||
return lastIsWearing
|
||||
end
|
||||
|
||||
function this:GetRobuxPrice()
|
||||
local robuxPrice = data and data['PriceInRobux']
|
||||
if not robuxPrice then
|
||||
robuxPrice = data and data['Product'] and data['Product']['PriceInRobux']
|
||||
end
|
||||
local isPublicDomain = data and data['IsPublicDomain'] == true
|
||||
if isPublicDomain == nil then
|
||||
isPublicDomain = data and data['Product'] and data['Product']['IsPublicDomain'] == true
|
||||
end
|
||||
if not robuxPrice and isPublicDomain then
|
||||
robuxPrice = 0
|
||||
end
|
||||
|
||||
return robuxPrice
|
||||
end
|
||||
|
||||
function this:GetCreatorId()
|
||||
return data and data['Creator'] and data['Creator']['Id']
|
||||
end
|
||||
|
||||
function this:WearAsync()
|
||||
local assetId = self:GetAssetId()
|
||||
if assetId then
|
||||
AwaitWearAssetRequest()
|
||||
|
||||
EventHub:dispatchEvent(EventHub.Notifications["AvatarEquipBegin"], assetId)
|
||||
|
||||
RequestingWearAsset = true
|
||||
local result = Http.PostWearAssetAsync(assetId)
|
||||
RequestingWearAsset = false
|
||||
|
||||
if result and result['success'] == true then
|
||||
EventHub:dispatchEvent(EventHub.Notifications["DonnedDifferentPackage"], assetId)
|
||||
end
|
||||
|
||||
|
||||
-- print("Http result from post wear asset:")
|
||||
-- print(Utility.PrettyPrint(result))
|
||||
return result
|
||||
end
|
||||
end
|
||||
|
||||
function this:GetName()
|
||||
local resultPackageName = self:GetFullName()
|
||||
|
||||
if resultPackageName then
|
||||
local colonPosition = string.find(resultPackageName, ":")
|
||||
if colonPosition then
|
||||
resultPackageName = string.sub(resultPackageName, 1, colonPosition - 1)
|
||||
end
|
||||
else
|
||||
resultPackageName = "Unknown"
|
||||
end
|
||||
|
||||
return resultPackageName
|
||||
end
|
||||
|
||||
function this:GetFullName()
|
||||
local name = data and data['Name']
|
||||
if not name then
|
||||
name = data and data['Item'] and data["Item"]['Name']
|
||||
end
|
||||
return name or "Unknown"
|
||||
end
|
||||
|
||||
function this:GetDescriptionAsync()
|
||||
while not productInfo do wait() end
|
||||
return productInfo and productInfo['Description']
|
||||
end
|
||||
|
||||
spawn(function()
|
||||
productInfo = MarketplaceService:GetProductInfo(this:GetAssetId())
|
||||
if productInfo == nil then productInfo = {} end
|
||||
end)
|
||||
|
||||
return this
|
||||
end
|
||||
|
||||
|
||||
|
||||
local PackageData = {}
|
||||
local PackageCache = nil
|
||||
|
||||
|
||||
local function GetAvailableXboxCatalogPackagesAsync()
|
||||
local isFinalPage = false
|
||||
|
||||
local packages = {}
|
||||
local index = 0
|
||||
local count = 20
|
||||
|
||||
repeat
|
||||
local result = nil
|
||||
|
||||
Utility.ExponentialRepeat(
|
||||
function() return result == nil end,
|
||||
function() result = Http.GetXboxProductsAsync(index, count) end,
|
||||
2)
|
||||
|
||||
if result then
|
||||
local items = result['Products']
|
||||
if items then
|
||||
if #items < count then
|
||||
isFinalPage = true
|
||||
end
|
||||
for _, itemInfo in pairs(items) do
|
||||
table.insert(packages, CreatePackageItem(itemInfo))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
index = index + count
|
||||
until result == nil or isFinalPage
|
||||
-- print("Xbox products:" , packages , Utility.PrettyPrint(packages))
|
||||
|
||||
if isFinalPage then
|
||||
return packages
|
||||
end
|
||||
end
|
||||
|
||||
local function GetOwnedCatalogPackageIdsByUserAsync(userId)
|
||||
local packages = Http.GetUserOwnedPackagesAsync(userId)
|
||||
if packages then
|
||||
local data = packages['IsValid'] and packages['Data']
|
||||
local items = data and data['Items']
|
||||
local result = {}
|
||||
-- print("Items" , Utility.PrettyPrint(items))
|
||||
if items then
|
||||
for _, itemInfo in pairs(items) do
|
||||
local assetId = itemInfo and itemInfo['Item'] and itemInfo['Item']['AssetId']
|
||||
result[assetId] = itemInfo
|
||||
end
|
||||
end
|
||||
return result
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local function getCatalogPackagesAsync()
|
||||
local xboxCatalogPackages = GetAvailableXboxCatalogPackagesAsync()
|
||||
local myPackages = GetOwnedCatalogPackageIdsByUserAsync(UserData:GetRbxUserId())
|
||||
|
||||
if xboxCatalogPackages and myPackages then
|
||||
local result = {}
|
||||
for _, xboxPackage in pairs(xboxCatalogPackages) do
|
||||
local owned = (myPackages[xboxPackage:GetAssetId()] ~= nil)
|
||||
SetOwnedInternal(xboxPackage, owned)
|
||||
table.insert(result, xboxPackage)
|
||||
end
|
||||
|
||||
|
||||
-- NOTE: Temporary changed to get items that you bought outside xbox
|
||||
-- Create a map of what we already have
|
||||
local haveAssets = {}
|
||||
for _, package in pairs(result) do
|
||||
haveAssets[package:GetAssetId()] = true
|
||||
end
|
||||
|
||||
for assetId, packageInfo in pairs(myPackages) do
|
||||
if not haveAssets[assetId] then
|
||||
local myPackage = CreatePackageItem(packageInfo)
|
||||
SetOwnedInternal(myPackage, true)
|
||||
table.insert(result, myPackage)
|
||||
end
|
||||
end
|
||||
return result
|
||||
end
|
||||
end
|
||||
|
||||
local function SetCurrentlyWearingAssetId(newValue)
|
||||
if CurrentlyWearingAssetId ~= newValue then
|
||||
CurrentlyWearingAssetId = newValue
|
||||
if PackageCache then
|
||||
for _, package in pairs(PackageCache) do
|
||||
package:IsWearing()
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local UserChangedCount = 0
|
||||
local function OnUserAccountChanged()
|
||||
UserChangedCount = UserChangedCount + 1
|
||||
|
||||
PackageCache = nil
|
||||
SetCurrentlyWearingAssetId(nil)
|
||||
|
||||
local wearingAsset = PackageData:GetCurrentlyWearingPackageAssetIdAsync()
|
||||
if not CurrentlyWearingAssetId then
|
||||
SetCurrentlyWearingAssetId(wearingAsset)
|
||||
end
|
||||
end
|
||||
|
||||
spawn(function()
|
||||
local function queryWearingAsset()
|
||||
local wearingAsset = PackageData:GetCurrentlyWearingPackageAssetIdAsync()
|
||||
SetCurrentlyWearingAssetId(wearingAsset)
|
||||
end
|
||||
|
||||
EventHub:addEventListener(EventHub.Notifications["DonnedDifferentPackage"], "PackageData",
|
||||
function(assetId)
|
||||
SetCurrentlyWearingAssetId(assetId)
|
||||
spawn(PreloadCharacterAppearanceAsync)
|
||||
end)
|
||||
EventHub:addEventListener(EventHub.Notifications["DonnedDifferentOutfit"], "PackageData",
|
||||
function(outfitId)
|
||||
queryWearingAsset()
|
||||
spawn(PreloadCharacterAppearanceAsync)
|
||||
end)
|
||||
EventHub:addEventListener(EventHub.Notifications["AuthenticationSuccess"], "PackageData", OnUserAccountChanged)
|
||||
-- Wait until we are ready to make the call be being signed in
|
||||
if UserData:GetRbxUserId() then
|
||||
queryWearingAsset()
|
||||
spawn(PreloadCharacterAppearanceAsync)
|
||||
end
|
||||
end)
|
||||
|
||||
local debounceGetXboxCatalogPackages = false
|
||||
function PackageData:GetXboxCatalogPackagesAsync()
|
||||
if debounceGetXboxCatalogPackages then
|
||||
while debounceGetXboxCatalogPackages do wait() end
|
||||
end
|
||||
debounceGetXboxCatalogPackages = true
|
||||
|
||||
-- Ensure that the catalog data we are getting is applicable to the
|
||||
-- currently logged in user
|
||||
-- while not PackageCache do
|
||||
local startCount = UserChangedCount
|
||||
local packageData = nil
|
||||
|
||||
Utility.ExponentialRepeat(
|
||||
function() return packageData == nil and startCount == UserChangedCount end,
|
||||
function() packageData = getCatalogPackagesAsync() end,
|
||||
3)
|
||||
|
||||
if startCount == UserChangedCount then
|
||||
PackageCache = packageData
|
||||
end
|
||||
-- end
|
||||
|
||||
debounceGetXboxCatalogPackages = false
|
||||
|
||||
return PackageCache
|
||||
end
|
||||
|
||||
|
||||
function PackageData:GetCurrentlyWearingPackageAssetIdAsync()
|
||||
local currentWearingData = Http.GetXboxCurrentlyWearingPackageAsync()
|
||||
return currentWearingData and currentWearingData['AssetId']
|
||||
end
|
||||
|
||||
function PackageData:GetCachedWearingPackage()
|
||||
return CurrentlyWearingAssetId
|
||||
end
|
||||
|
||||
function PackageData:AwaitWearAssetRequest()
|
||||
AwaitWearAssetRequest()
|
||||
end
|
||||
|
||||
|
||||
return PackageData
|
||||
@@ -0,0 +1,65 @@
|
||||
local CoreGui = Game:GetService("CoreGui")
|
||||
local GuiRoot = CoreGui:FindFirstChild("RobloxGui")
|
||||
local Modules = GuiRoot:FindFirstChild("Modules")
|
||||
|
||||
local PlatformService;
|
||||
pcall(function() PlatformService = Game:GetService('PlatformService') end)
|
||||
|
||||
|
||||
local PlatformCatalogData = {}
|
||||
|
||||
local function getStudioDummyData()
|
||||
return {{ReducedName = 'Default Short Title', Description = 'Default Description', DisplayListPrice = '$199.99', IsPartOfAnyBundle = false, DisplayPrice = '$0.80', ProductId = '210d1d69-5189-40f4-a59b-ecfb4f849847', Name = '22,500 ROBUX', TitleId = 0, IsBundle = false}, {ReducedName = 'Default Short Title', Description = 'Default Description', DisplayListPrice = '$3.00', IsPartOfAnyBundle = false, DisplayPrice = '$3.00', ProductId = '70c2075d-5e2f-4ffd-8de5-8a6d2f5e65ad', Name = '400 ROBUX', TitleId = 0, IsBundle = false}, {ReducedName = 'Default Short Title', Description = 'Default Description', DisplayListPrice = '$2.20', IsPartOfAnyBundle = false, DisplayPrice = '$2.20', ProductId = '878c642b-cb27-4d5e-a150-a408ea40c41c', Name = '240 ROBUX', TitleId = 0, IsBundle = false}}
|
||||
end
|
||||
|
||||
|
||||
function PlatformCatalogData:GetCatalogInfoAsync()
|
||||
if UserSettings().GameSettings:InStudioMode() then
|
||||
return getStudioDummyData(),
|
||||
true,
|
||||
''
|
||||
end
|
||||
|
||||
local numRetries = 5
|
||||
local catalogInfo, success, errormsg;
|
||||
for i = 1, numRetries do
|
||||
success, errormsg = pcall(function()
|
||||
catalogInfo = PlatformService:BeginGetCatalogInfo()
|
||||
end)
|
||||
if success and catalogInfo then
|
||||
return catalogInfo, success, errormsg
|
||||
end
|
||||
wait(10)
|
||||
end
|
||||
|
||||
return catalogInfo, success, errormsg
|
||||
end
|
||||
|
||||
function PlatformCatalogData:ParseDisplayPrice(productInfo)
|
||||
local rawText = productInfo and productInfo.DisplayListPrice
|
||||
local noCurrency = rawText and string.gsub(rawText, "%$", "") or nil
|
||||
|
||||
noCurrency = noCurrency and string.gsub(noCurrency, ",", "") or nil
|
||||
|
||||
return noCurrency and tonumber(noCurrency) or 0.99
|
||||
end
|
||||
|
||||
function PlatformCatalogData:ParseRobuxValue(productInfo)
|
||||
local rawText = productInfo and productInfo.Name
|
||||
local noJunk = string.gsub(rawText, ",", "")
|
||||
noJunk = noJunk and string.match(noJunk, "[0-9]+") or nil
|
||||
return noJunk and tonumber(noJunk) or 1000
|
||||
end
|
||||
|
||||
function PlatformCatalogData:CalculateRobuxRatio(productInfo)
|
||||
local robuxValue = self:ParseRobuxValue(productInfo)
|
||||
local displayPrice = self:ParseDisplayPrice(productInfo)
|
||||
|
||||
if displayPrice == 0 or robuxValue == 0 then
|
||||
return 0
|
||||
end
|
||||
|
||||
return robuxValue / displayPrice
|
||||
end
|
||||
|
||||
return PlatformCatalogData
|
||||
@@ -0,0 +1,38 @@
|
||||
-- Written by Kip Turner, Copyright ROBLOX 2015
|
||||
|
||||
-- Platform Interface
|
||||
|
||||
local PlatformService = nil
|
||||
pcall(function() PlatformService = game:GetService('PlatformService') end)
|
||||
|
||||
|
||||
local PlatformInterface = {}
|
||||
|
||||
|
||||
local gettingFriends = false
|
||||
function PlatformInterface:GetPartyMembersAsync()
|
||||
while gettingFriends do wait() end
|
||||
gettingFriends = true
|
||||
|
||||
local partyMembers;
|
||||
local success, msg = pcall(function()
|
||||
partyMembers = PlatformService:GetPlatformPartyMembers()
|
||||
end)
|
||||
if not success then
|
||||
print("HeroStatsManager - Error getting party members:" , msg)
|
||||
end
|
||||
|
||||
gettingFriends = false
|
||||
return partyMembers
|
||||
end
|
||||
|
||||
|
||||
|
||||
function PlatformInterface:IsInAParty(partyMembers)
|
||||
return (partyMembers and #partyMembers > 1)
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
return PlatformInterface
|
||||
@@ -0,0 +1,123 @@
|
||||
--[[
|
||||
// PopupText.lua
|
||||
|
||||
// Creates a transparent text label that pops up when
|
||||
// its parent it selected
|
||||
]]
|
||||
local CoreGui = Game:GetService("CoreGui")
|
||||
local GuiRoot = CoreGui:FindFirstChild("RobloxGui")
|
||||
local Modules = GuiRoot:FindFirstChild("Modules")
|
||||
local GuiService = game:GetService('GuiService')
|
||||
|
||||
local GlobalSettings = require(Modules:FindFirstChild('GlobalSettings'))
|
||||
local Utility = require(Modules:FindFirstChild('Utility'))
|
||||
|
||||
local TextService = game:GetService('TextService')
|
||||
|
||||
local VERTICAL_PADDING = 12
|
||||
local HORIZONTAL_PADDING = 18
|
||||
local SELECTION_BORDER = 7
|
||||
|
||||
local createPopupText = function(parent, text)
|
||||
local this = {}
|
||||
|
||||
local tweenTime = 0.3
|
||||
local easingStyle = Enum.EasingStyle.Quad
|
||||
local easingDirection = Enum.EasingDirection.Out
|
||||
|
||||
local currentZIndex = 2
|
||||
|
||||
local clipFrame = Utility.Create'Frame'
|
||||
{
|
||||
Name = "ClipFrame";
|
||||
Size = UDim2.new(1, 0, 1, 0);
|
||||
BackgroundTransparency = 1;
|
||||
ZIndex = currentZIndex;
|
||||
ClipsDescendants = true;
|
||||
Parent = parent
|
||||
}
|
||||
|
||||
local bg = Utility.Create'Frame'
|
||||
{
|
||||
Name = "PopupBG";
|
||||
Size = UDim2.new(1, 0, 1, 0);
|
||||
Position = UDim2.new(0, 0, 1, 5);
|
||||
BackgroundTransparency = GlobalSettings.ModalBackgroundTransparency;
|
||||
BackgroundColor3 = GlobalSettings.ModalBackgroundColor;
|
||||
BorderSizePixel = 0;
|
||||
ZIndex = currentZIndex;
|
||||
Parent = clipFrame;
|
||||
}
|
||||
local nameLabel = Utility.Create'TextLabel'
|
||||
{
|
||||
Name = "NameLabel";
|
||||
Size = UDim2.new(1, -HORIZONTAL_PADDING, 1, -VERTICAL_PADDING);
|
||||
Position = UDim2.new(0, HORIZONTAL_PADDING/2 + SELECTION_BORDER, 0, VERTICAL_PADDING/2);
|
||||
BackgroundTransparency = 1;
|
||||
TextColor3 = GlobalSettings.WhiteTextColor;
|
||||
TextWrapped = true;
|
||||
TextXAlignment = Enum.TextXAlignment.Left;
|
||||
TextYAlignment = Enum.TextYAlignment.Top;
|
||||
Font = GlobalSettings.LightFont;
|
||||
FontSize = GlobalSettings.TitleSize;
|
||||
ZIndex = currentZIndex;
|
||||
Text = text;
|
||||
Parent = bg;
|
||||
}
|
||||
|
||||
-- resize based on text bounds
|
||||
local function resizeBounds()
|
||||
local nameLabelTextSize = TextService:GetTextSize(
|
||||
nameLabel.Text,
|
||||
Utility.ConvertFontSizeEnumToInt(nameLabel.FontSize),
|
||||
nameLabel.Font,
|
||||
Vector2.new(clipFrame.AbsoluteSize.x - SELECTION_BORDER - HORIZONTAL_PADDING, clipFrame.AbsoluteSize.y - SELECTION_BORDER -VERTICAL_PADDING))
|
||||
|
||||
local newSizeX = nameLabelTextSize.x + HORIZONTAL_PADDING
|
||||
local newSizeY = math.min(nameLabelTextSize.y + VERTICAL_PADDING, parent.AbsoluteSize.y * 0.75)
|
||||
bg.Size = UDim2.new(0, newSizeX + SELECTION_BORDER, 0, newSizeY + SELECTION_BORDER)
|
||||
end
|
||||
spawn(function()
|
||||
resizeBounds()
|
||||
end)
|
||||
|
||||
parent.SelectionGained:connect(function()
|
||||
if #nameLabel.Text > 0 then
|
||||
resizeBounds()
|
||||
Utility.TweenPositionOrSet(bg, UDim2.new(0, 0, 1, -bg.Size.Y.Offset), easingDirection, easingStyle, tweenTime, true)
|
||||
end
|
||||
end)
|
||||
parent.SelectionLost:connect(function()
|
||||
Utility.TweenPositionOrSet(bg, UDim2.new(0, 0, 1, 5), easingDirection, easingStyle, tweenTime, true)
|
||||
end)
|
||||
|
||||
function this:SetTweenTime(value)
|
||||
tweenTime = value
|
||||
end
|
||||
function this:SetEasingStyle(style)
|
||||
easingStyle = style
|
||||
end
|
||||
function this:SetEasingDirection(direction)
|
||||
easingDirection = direction
|
||||
end
|
||||
function this:SetText(text)
|
||||
nameLabel.Text = text
|
||||
resizeBounds()
|
||||
if #text == 0 then
|
||||
bg.Position = UDim2.new(0, 0, 1, 5)
|
||||
end
|
||||
end
|
||||
function this:SetZIndex(zindex)
|
||||
if zindex ~= currentZIndex then
|
||||
currentZIndex = zindex
|
||||
|
||||
clipFrame.ZIndex = currentZIndex
|
||||
bg.ZIndex = currentZIndex
|
||||
nameLabel.ZIndex = currentZIndex
|
||||
end
|
||||
end
|
||||
|
||||
return this
|
||||
end
|
||||
|
||||
return createPopupText
|
||||
@@ -0,0 +1,501 @@
|
||||
--[[
|
||||
// PurchasePackagePrompt.lua
|
||||
// Kip Turner
|
||||
// Copyright Roblox 2015
|
||||
]]
|
||||
|
||||
local CoreGui = game:GetService("CoreGui")
|
||||
local GuiRoot = CoreGui:FindFirstChild("RobloxGui")
|
||||
local Modules = GuiRoot:FindFirstChild("Modules")
|
||||
local ContextActionService = game:GetService("ContextActionService")
|
||||
local GuiService = game:GetService('GuiService')
|
||||
local MarketplaceService = game:GetService('MarketplaceService')
|
||||
local PlatformService;
|
||||
pcall(function() PlatformService = game:GetService('PlatformService') end)
|
||||
|
||||
local UserDataModule = require(Modules:FindFirstChild('UserData'))
|
||||
local Http = require(Modules:FindFirstChild('Http'))
|
||||
local ScrollingTextBox = require(Modules:FindFirstChild('ScrollingTextBox'))
|
||||
local CreateConfirmPrompt = require(Modules:FindFirstChild('ConfirmPrompt'))
|
||||
local LoadingWidget = require(Modules:FindFirstChild('LoadingWidget'))
|
||||
|
||||
local AssetManager = require(Modules:FindFirstChild('AssetManager'))
|
||||
local GlobalSettings = require(Modules:FindFirstChild('GlobalSettings'))
|
||||
local Utility = require(Modules:FindFirstChild('Utility'))
|
||||
local Strings = require(Modules:FindFirstChild('LocalizedStrings'))
|
||||
|
||||
local ScreenManager = require(Modules:FindFirstChild('ScreenManager'))
|
||||
local EventHub = require(Modules:FindFirstChild('EventHub'))
|
||||
local ErrorOverlayModule = require(Modules:FindFirstChild('ErrorOverlay'))
|
||||
local Errors = require(Modules:FindFirstChild('Errors'))
|
||||
local SoundManager = require(Modules:FindFirstChild('SoundManager'))
|
||||
|
||||
local CurrencyWidgetModule = require(Modules:FindFirstChild('CurrencyWidget'))
|
||||
|
||||
local MOCKUP_WIDTH = 1920
|
||||
local MOCKUP_HEIGHT = 1080
|
||||
local CONTENT_WIDTH = 1920
|
||||
local CONTENT_HEIGHT = 690
|
||||
local PACKAGE_CONTAINER_WIDTH = 780
|
||||
local PACKAGE_CONTAINER_HEIGHT = 690
|
||||
local PACKAGE_BACKGROUND_WIDTH = 580
|
||||
local PACKAGE_BACKGROUND_HEIGHT = 640
|
||||
|
||||
local CONTENT_POSITION = Vector2.new(0, 225)
|
||||
|
||||
local DETAILS_CONTAINER_WIDTH = CONTENT_WIDTH - PACKAGE_CONTAINER_WIDTH
|
||||
local DETAILS_CONTAINER_HEIGHT = 690
|
||||
|
||||
local DESCRIPTION_WIDTH = 800
|
||||
local DESCRIPTION_HEIGHT = 320
|
||||
|
||||
local BUY_BUTTON_WIDTH = 320
|
||||
local BUY_BUTTON_HEIGHT = 64
|
||||
|
||||
local TEXT_START_OFFSET = Vector2.new(0, 70)
|
||||
local TEXT_SPACING = Vector2.new(0, 20)
|
||||
local ROBUX_TEXT_OFFSET = Vector2.new(0, 25)
|
||||
local DETAIL_TEXT_OFFSET = Vector2.new(0, 30)
|
||||
|
||||
local BUY_BUTTON_OFFSET = Vector2.new(0, -50)
|
||||
|
||||
local ROBUX_BALANCE_OFFSET = Vector2.new(100, -130)
|
||||
|
||||
local function CreatePurchasePackagePrompt(packageInfo)
|
||||
local this = {}
|
||||
|
||||
local MyParent = nil
|
||||
local Result = nil
|
||||
local purchasing = false
|
||||
local finishedLoading = false
|
||||
local balance = nil
|
||||
local inFocus = false
|
||||
local ResultEvent = Utility.Signal()
|
||||
|
||||
local packageName = packageInfo:GetFullName()
|
||||
local robuxPrice = packageInfo:GetRobuxPrice()
|
||||
local creatorId = packageInfo:GetCreatorId()
|
||||
|
||||
|
||||
local ModalBackground = Utility.Create'Frame'
|
||||
{
|
||||
Name = "PurchasePackagePrompt";
|
||||
Size = UDim2.new(1, 0, 1, 0);
|
||||
BackgroundTransparency = 1;
|
||||
BackgroundColor3 = GlobalSettings.ModalBackgroundColor;
|
||||
BorderSizePixel = 0;
|
||||
ZIndex = 4;
|
||||
}
|
||||
|
||||
local ContentContainer = Utility.Create'Frame'
|
||||
{
|
||||
Name = "ContentContainer";
|
||||
Size = UDim2.new(CONTENT_WIDTH/MOCKUP_WIDTH, 0, CONTENT_HEIGHT/MOCKUP_HEIGHT, 0);
|
||||
Position = UDim2.new(CONTENT_POSITION.x/MOCKUP_WIDTH, 0, CONTENT_POSITION.y/MOCKUP_HEIGHT, 0);
|
||||
BackgroundTransparency = 0;
|
||||
BackgroundColor3 = GlobalSettings.OverlayColor;
|
||||
BorderSizePixel = 0;
|
||||
ZIndex = 4;
|
||||
Parent = ModalBackground;
|
||||
}
|
||||
-- Utility.CalculateAnchor(ContentContainer, UDim2.new(0.5,0,0.5,0), Utility.Enum.Anchor.Center)
|
||||
|
||||
local PackageContainer = Utility.Create'Frame'
|
||||
{
|
||||
Name = "PackageContainer";
|
||||
Size = UDim2.new(PACKAGE_CONTAINER_WIDTH/CONTENT_WIDTH, 0, PACKAGE_CONTAINER_HEIGHT/CONTENT_HEIGHT, 0);
|
||||
Position = UDim2.new(0,0,0,0);
|
||||
BackgroundTransparency = 1;
|
||||
BorderSizePixel = 0;
|
||||
ZIndex = 4;
|
||||
Parent = ContentContainer;
|
||||
}
|
||||
local PackageBackground = Utility.Create'Frame'
|
||||
{
|
||||
Name = "PackageBackground";
|
||||
Size = UDim2.new(PACKAGE_BACKGROUND_WIDTH/PACKAGE_CONTAINER_WIDTH, 0, PACKAGE_BACKGROUND_HEIGHT/CONTENT_HEIGHT, 0);
|
||||
BackgroundTransparency = 0;
|
||||
BackgroundColor3 = GlobalSettings.ForegroundGreyColor;
|
||||
BorderSizePixel = 0;
|
||||
ClipsDescendants = true;
|
||||
ZIndex = 4;
|
||||
Parent = PackageContainer;
|
||||
}
|
||||
Utility.CalculateAnchor(PackageBackground, UDim2.new(0.5,0,0.5,0), Utility.Enum.Anchor.Center)
|
||||
local PackageImage = Utility.Create'ImageLabel'
|
||||
{
|
||||
Name = 'PackageImage';
|
||||
Size = UDim2.new(1,0,1,0);
|
||||
Image = Http.GetThumbnailUrlForAsset(packageInfo:GetAssetId());
|
||||
BackgroundTransparency = 1;
|
||||
ZIndex = 4;
|
||||
Parent = PackageBackground;
|
||||
};
|
||||
local DetailsContainer = Utility.Create'Frame'
|
||||
{
|
||||
Name = "DetailsContainer";
|
||||
Size = UDim2.new(DETAILS_CONTAINER_WIDTH/CONTENT_WIDTH, 0, DETAILS_CONTAINER_HEIGHT/CONTENT_HEIGHT, 0);
|
||||
Position = UDim2.new(PACKAGE_CONTAINER_WIDTH/CONTENT_WIDTH,0,0,0);
|
||||
BackgroundTransparency = 1;
|
||||
BorderSizePixel = 0;
|
||||
ZIndex = 4;
|
||||
Parent = ContentContainer;
|
||||
}
|
||||
local PurchasingTitle = Utility.Create'TextLabel'
|
||||
{
|
||||
Name = 'PurchasingTitle';
|
||||
Text = Strings:LocalizedString('PurchasingTitle');
|
||||
Position = UDim2.new(0, 0, 0, 66);
|
||||
Size = UDim2.new(1,0,0,25);
|
||||
TextXAlignment = 'Left';
|
||||
TextColor3 = GlobalSettings.WhiteTextColor;
|
||||
Font = GlobalSettings.HeadingFont;
|
||||
FontSize = GlobalSettings.HeaderSize;
|
||||
BackgroundTransparency = 1;
|
||||
ZIndex = 4;
|
||||
Visible = false;
|
||||
Parent = DetailsContainer;
|
||||
};
|
||||
local DetailsContent = Utility.Create'Frame'
|
||||
{
|
||||
Name = "DetailsContent";
|
||||
Size = UDim2.new(1, 0, 1, 0);
|
||||
Position = UDim2.new(0,0,0,0);
|
||||
BackgroundTransparency = 1;
|
||||
BorderSizePixel = 0;
|
||||
ZIndex = 4;
|
||||
Parent = DetailsContainer;
|
||||
}
|
||||
|
||||
local PackageName = Utility.Create'TextLabel'
|
||||
{
|
||||
Name = 'PackageName';
|
||||
Text = packageName or "Unknown Package";
|
||||
-- Position = UDim2.new(TEXT_START_OFFSET.X/DETAILS_CONTAINER_WIDTH, 0, TEXT_START_OFFSET.Y/DETAILS_CONTAINER_HEIGHT, 0);
|
||||
Position = UDim2.new(0, 0, 0, 66);
|
||||
Size = UDim2.new(1,0,0,25);
|
||||
TextXAlignment = 'Left';
|
||||
TextColor3 = GlobalSettings.WhiteTextColor;
|
||||
Font = GlobalSettings.HeadingFont;
|
||||
FontSize = GlobalSettings.HeaderSize;
|
||||
BackgroundTransparency = 1;
|
||||
ZIndex = 4;
|
||||
Parent = DetailsContent;
|
||||
};
|
||||
local RobuxIcon = Utility.Create'ImageLabel'
|
||||
{
|
||||
Name = 'RobuxIcon';
|
||||
Position = UDim2.new(0,0,0,125);
|
||||
-- Position = PackageName.Position + UDim2.new(ROBUX_TEXT_OFFSET.X/DETAILS_CONTAINER_WIDTH,0,ROBUX_TEXT_OFFSET.Y/DETAILS_CONTAINER_HEIGHT + PackageName.Size.Y.Scale, PackageName.Size.Y.Offset);
|
||||
Size = UDim2.new(0,50,0,50);
|
||||
BackgroundTransparency = 1;
|
||||
ZIndex = 4;
|
||||
Parent = DetailsContent;
|
||||
};
|
||||
AssetManager.LocalImage(RobuxIcon, 'rbxasset://textures/ui/Shell/Icons/ROBUXIcon', {['720'] = UDim2.new(0,28,0,28); ['1080'] = UDim2.new(0,42,0,42);})
|
||||
local PackageCost = Utility.Create'TextLabel'
|
||||
{
|
||||
Name = 'PackageCost';
|
||||
Text = '';
|
||||
Size = UDim2.new(0,0,1,0);
|
||||
Position = UDim2.new(1.3,0,0,0);
|
||||
TextXAlignment = 'Left';
|
||||
TextColor3 = GlobalSettings.GreenTextColor;
|
||||
Font = GlobalSettings.RegularFont;
|
||||
FontSize = GlobalSettings.HeaderSize;
|
||||
BackgroundTransparency = 1;
|
||||
ZIndex = 4;
|
||||
Parent = RobuxIcon;
|
||||
};
|
||||
|
||||
local AlreadyOwnTextLabel = Utility.Create'TextLabel'
|
||||
{
|
||||
Name = 'AlreadyOwnTextLabel';
|
||||
Text = '';
|
||||
Size = UDim2.new(0,0,0,50);
|
||||
Position = UDim2.new(0,0,0,125);
|
||||
-- Position = PackageName.Position + UDim2.new(ROBUX_TEXT_OFFSET.X/DETAILS_CONTAINER_WIDTH,0,ROBUX_TEXT_OFFSET.Y/DETAILS_CONTAINER_HEIGHT + PackageName.Size.Y.Scale, PackageName.Size.Y.Offset);
|
||||
TextXAlignment = 'Left';
|
||||
TextColor3 = GlobalSettings.GreenTextColor;
|
||||
Font = GlobalSettings.ItalicFont;
|
||||
FontSize = GlobalSettings.DescriptionSize;
|
||||
BackgroundTransparency = 1;
|
||||
ZIndex = 4;
|
||||
Visible = false;
|
||||
Parent = DetailsContent;
|
||||
};
|
||||
|
||||
local descriptionScrollingTextBox = ScrollingTextBox(UDim2.new(DESCRIPTION_WIDTH/DETAILS_CONTAINER_WIDTH, 0, DESCRIPTION_HEIGHT/DETAILS_CONTAINER_HEIGHT, 0),
|
||||
-- RobuxIcon.Position + UDim2.new(DETAIL_TEXT_OFFSET.X/DETAILS_CONTAINER_WIDTH,0,DETAIL_TEXT_OFFSET.Y/DETAILS_CONTAINER_HEIGHT + RobuxIcon.Size.Y.Scale, RobuxIcon.Size.Y.Offset),
|
||||
UDim2.new(0, 0, 0, 200),
|
||||
DetailsContent)
|
||||
descriptionScrollingTextBox:SetZIndex(5)
|
||||
descriptionScrollingTextBox:SetFontSize(GlobalSettings.TitleSize)
|
||||
descriptionScrollingTextBox:SetFont(GlobalSettings.LightFont)
|
||||
|
||||
local BuyButton = Utility.Create'ImageButton'
|
||||
{
|
||||
Name = "BuyButton";
|
||||
Size = UDim2.new(BUY_BUTTON_WIDTH/DETAILS_CONTAINER_WIDTH, 0, BUY_BUTTON_HEIGHT/DETAILS_CONTAINER_HEIGHT, 0);
|
||||
BorderSizePixel = 0;
|
||||
BackgroundColor3 = GlobalSettings.BlueButtonColor;
|
||||
BackgroundTransparency = 0;
|
||||
ZIndex = 4;
|
||||
Parent = DetailsContent;
|
||||
}
|
||||
Utility.CalculateAnchor(BuyButton, UDim2.new(0, 0, 1 + BUY_BUTTON_OFFSET.Y/DETAILS_CONTAINER_HEIGHT, 0), Utility.Enum.Anchor.BottomLeft)
|
||||
local BuyText = Utility.Create'TextLabel'
|
||||
{
|
||||
Name = 'BuyText';
|
||||
Text = '';
|
||||
Size = UDim2.new(1,0,1,0);
|
||||
TextColor3 = GlobalSettings.TextSelectedColor;
|
||||
Font = GlobalSettings.HeadingFont;
|
||||
FontSize = GlobalSettings.MediumLargeHeadingSize;
|
||||
BackgroundTransparency = 1;
|
||||
ZIndex = 4;
|
||||
Parent = BuyButton;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
local function OnOwnedUpdate()
|
||||
if packageInfo:IsOwned() or robuxPrice == nil then
|
||||
if packageInfo:IsOwned() then
|
||||
if robuxPrice and robuxPrice > 0 then
|
||||
AlreadyOwnTextLabel.Text = string.format(Strings:LocalizedString('PurchasedThisPhrase'), Utility.FormatNumberString(tostring(robuxPrice)))
|
||||
else
|
||||
AlreadyOwnTextLabel.Text = Strings:LocalizedString('AlreadyOwnedPhrase')
|
||||
end
|
||||
end
|
||||
AlreadyOwnTextLabel.Visible = (packageInfo:IsOwned() == true)
|
||||
RobuxIcon.Visible = false
|
||||
BuyText.Text = Strings:LocalizedString("OkWord")
|
||||
else
|
||||
if robuxPrice and robuxPrice == 0 then
|
||||
PackageCost.Text = Strings:LocalizedString('FreeWord'):upper();
|
||||
else
|
||||
PackageCost.Text = robuxPrice and Utility.FormatNumberString(tostring(robuxPrice)) or '-';
|
||||
end
|
||||
RobuxIcon.Visible = true
|
||||
AlreadyOwnTextLabel.Visible = false
|
||||
if robuxPrice == 0 then
|
||||
BuyText.Text = Strings:LocalizedString('TakeWord'):upper();
|
||||
elseif balance and robuxPrice > balance then
|
||||
BuyText.Text = Strings:LocalizedString('GetRobuxPhrase'):upper();
|
||||
else
|
||||
BuyText.Text = Strings:LocalizedString('BuyWord'):upper();
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function SetBalance(newBalance)
|
||||
balance = newBalance
|
||||
OnOwnedUpdate()
|
||||
end
|
||||
|
||||
function this:UpdateOwned()
|
||||
OnOwnedUpdate()
|
||||
end
|
||||
|
||||
function this:ResultAsync()
|
||||
if Result then
|
||||
return Result
|
||||
end
|
||||
ResultEvent:wait()
|
||||
return Result
|
||||
end
|
||||
|
||||
|
||||
do
|
||||
local function loadBalanceAsync()
|
||||
local balance = UserDataModule.GetPlatformUserBalanceAsync()
|
||||
SetBalance(balance)
|
||||
end
|
||||
local function loadDescription()
|
||||
local descriptionText = packageInfo:GetDescriptionAsync()
|
||||
descriptionScrollingTextBox:SetText(descriptionText or "")
|
||||
end
|
||||
|
||||
DetailsContent.Visible = false
|
||||
|
||||
local loader = LoadingWidget({Parent = DetailsContainer}, {loadBalanceAsync, loadDescription})
|
||||
spawn(function()
|
||||
loader:AwaitFinished()
|
||||
loader:Cleanup()
|
||||
loader = nil
|
||||
ScreenManager:DefaultFadeIn(DetailsContent)
|
||||
DetailsContent.Visible = true
|
||||
if inFocus then
|
||||
GuiService.SelectedCoreObject = this:GetDefaultSelectableObject()
|
||||
end
|
||||
finishedLoading = true
|
||||
end)
|
||||
end
|
||||
|
||||
local function DoPurchase()
|
||||
purchasing = true
|
||||
local wasOwned = packageInfo:IsOwned()
|
||||
local purchaseResult = packageInfo:BuyAsync()
|
||||
local newBalance = purchaseResult and purchaseResult['balanceAfterSale'] or UserDataModule.GetPlatformUserBalanceAsync()
|
||||
-- print('purchaseResult')
|
||||
-- print(Utility.PrettyPrint(purchaseResult))
|
||||
SetBalance(newBalance)
|
||||
local nowOwns = packageInfo:IsOwned() and not wasOwned
|
||||
Result = nowOwns
|
||||
if nowOwns then
|
||||
this:UpdateOwned()
|
||||
end
|
||||
purchasing = false
|
||||
if not wasOwned and not nowOwns then
|
||||
if ScreenManager:GetTopScreen() == this then
|
||||
ScreenManager:CloseCurrent()
|
||||
end
|
||||
ScreenManager:OpenScreen(ErrorOverlayModule(Errors.PackagePurchase[1]), false)
|
||||
end
|
||||
print("Done with purchase")
|
||||
end
|
||||
|
||||
|
||||
OnOwnedUpdate()
|
||||
|
||||
function this:GetDefaultSelectableObject()
|
||||
return BuyButton
|
||||
end
|
||||
|
||||
function this:FadeInBackground()
|
||||
Utility.PropertyTweener(ModalBackground, "BackgroundTransparency", 1, GlobalSettings.ModalBackgroundTransparency, 0.25, Utility.EaseInOutQuad, true)
|
||||
end
|
||||
|
||||
local currencyWidget = nil
|
||||
local RobuxChangedConn = nil
|
||||
function this:Show()
|
||||
ModalBackground.Visible = true
|
||||
ModalBackground.Parent = MyParent
|
||||
|
||||
local function onPackageBackgroundResize()
|
||||
PackageImage.Size = Utility.CalculateFill(PackageBackground, Vector2.new(420, 420))
|
||||
Utility.CalculateAnchor(PackageImage, UDim2.new(0.5,0,0.5,0), Utility.Enum.Anchor.Center)
|
||||
end
|
||||
|
||||
self.PackageBackgroundChangedConn = Utility.DisconnectEvent(self.PackageBackgroundChangedConn)
|
||||
self.PackageBackgroundChangedConn = PackageBackground.Changed:connect(function(prop)
|
||||
if prop == 'AbsoluteSize' then
|
||||
onPackageBackgroundResize()
|
||||
end
|
||||
end)
|
||||
onPackageBackgroundResize()
|
||||
|
||||
if not currencyWidget then
|
||||
currencyWidget = CurrencyWidgetModule({Parent = ModalBackground; Position = UDim2.new(0.052, 0, 0.88, 0); ZIndex = 4;})
|
||||
end
|
||||
Utility.DisconnectEvent(RobuxChangedConn)
|
||||
RobuxChangedConn = currencyWidget.RobuxChanged:connect(SetBalance)
|
||||
|
||||
SoundManager:Play('OverlayOpen')
|
||||
end
|
||||
|
||||
function this:Hide()
|
||||
ModalBackground.Visible = false
|
||||
ModalBackground.Parent = nil
|
||||
|
||||
self.PackageBackgroundChangedConn = Utility.DisconnectEvent(self.PackageBackgroundChangedConn)
|
||||
RobuxChangedConn = Utility.DisconnectEvent(RobuxChangedConn)
|
||||
end
|
||||
|
||||
function this:ScreenRemoved()
|
||||
if currencyWidget then
|
||||
currencyWidget:Destroy()
|
||||
currencyWidget = nil
|
||||
end
|
||||
ResultEvent:fire()
|
||||
end
|
||||
|
||||
function this:Focus()
|
||||
inFocus = true
|
||||
ContextActionService:BindCoreAction("ReturnFromPurchasePackageScreen",
|
||||
function(actionName, inputState, inputObject)
|
||||
if not purchasing then
|
||||
if inputState == Enum.UserInputState.End then
|
||||
ScreenManager:CloseCurrent()
|
||||
end
|
||||
end
|
||||
end,
|
||||
false,
|
||||
Enum.KeyCode.ButtonB)
|
||||
|
||||
local buyButtonDebounce = false
|
||||
self.BuyButtonConn = Utility.DisconnectEvent(self.BuyButtonConn)
|
||||
self.BuyButtonConn = BuyButton.MouseButton1Click:connect(function()
|
||||
if buyButtonDebounce or purchasing or not finishedLoading then
|
||||
return
|
||||
end
|
||||
buyButtonDebounce = true
|
||||
|
||||
if packageInfo:IsOwned() or robuxPrice == nil then
|
||||
SoundManager:Play('ButtonPress')
|
||||
ScreenManager:CloseCurrent()
|
||||
else
|
||||
if balance then
|
||||
if robuxPrice > 0 and balance < robuxPrice then
|
||||
print("Goto robux screen")
|
||||
EventHub:dispatchEvent(EventHub.Notifications["NavigateToRobuxScreen"])
|
||||
else
|
||||
local confirmPrompt = CreateConfirmPrompt({ProductName = packageName; Cost = robuxPrice; Balance = balance; ProductImage = Http.GetThumbnailUrlForAsset(packageInfo:GetAssetId()); Currency = "ROBUX"; CurrencySymbol = ""},
|
||||
{ShowRemainingBalance = true; ShowRobuxIcon = true;})
|
||||
confirmPrompt:SetParent(MyParent)
|
||||
ScreenManager:OpenScreen(confirmPrompt)
|
||||
local result = confirmPrompt:ResultAsync()
|
||||
if result == true then
|
||||
local loader = LoadingWidget({Parent = DetailsContainer}, {DoPurchase})
|
||||
DetailsContent.Visible = false
|
||||
PurchasingTitle.Visible = true
|
||||
spawn(function()
|
||||
loader:AwaitFinished()
|
||||
loader:Cleanup()
|
||||
loader = nil
|
||||
ScreenManager:DefaultFadeIn(DetailsContent)
|
||||
DetailsContent.Visible = true
|
||||
PurchasingTitle.Visible = false
|
||||
|
||||
if ScreenManager:GetTopScreen() == this then
|
||||
ScreenManager:CloseCurrent()
|
||||
end
|
||||
end)
|
||||
|
||||
else
|
||||
print("Declined to buy")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
buyButtonDebounce = false
|
||||
end)
|
||||
|
||||
GuiService:AddSelectionParent("PurchasePackagePromptSelectionGroup", ContentContainer)
|
||||
GuiService.SelectedCoreObject = self:GetDefaultSelectableObject()
|
||||
end
|
||||
|
||||
function this:RemoveFocus()
|
||||
inFocus = false
|
||||
ContextActionService:UnbindCoreAction("ReturnFromPurchasePackageScreen")
|
||||
GuiService:RemoveSelectionGroup("PurchasePackagePromptSelectionGroup")
|
||||
self.BuyButtonConn = Utility.DisconnectEvent(self.BuyButtonConn)
|
||||
GuiService.SelectedCoreObject = nil
|
||||
end
|
||||
|
||||
|
||||
function this:SetParent(parent)
|
||||
MyParent = parent
|
||||
ModalBackground.Parent = MyParent
|
||||
end
|
||||
|
||||
|
||||
return this
|
||||
end
|
||||
|
||||
return CreatePurchasePackagePrompt
|
||||
@@ -0,0 +1,111 @@
|
||||
--[[
|
||||
// ReportOverlay.lua
|
||||
]]
|
||||
local CoreGui = Game:GetService("CoreGui")
|
||||
local GuiRoot = CoreGui:FindFirstChild("RobloxGui")
|
||||
local Modules = GuiRoot:FindFirstChild("Modules")
|
||||
local GuiService = game:GetService('GuiService')
|
||||
local ContextActionService = game:GetService("ContextActionService")
|
||||
|
||||
local AssetManager = require(Modules:FindFirstChild('AssetManager'))
|
||||
local GlobalSettings = require(Modules:FindFirstChild('GlobalSettings'))
|
||||
local Strings = require(Modules:FindFirstChild('LocalizedStrings'))
|
||||
local Utility = require(Modules:FindFirstChild('Utility'))
|
||||
local BaseOverlay = require(Modules:FindFirstChild('BaseOverlay'))
|
||||
local ScreenManager = require(Modules:FindFirstChild('ScreenManager'))
|
||||
local SoundManager = require(Modules:FindFirstChild('SoundManager'))
|
||||
local Http = require(Modules:FindFirstChild('Http'))
|
||||
|
||||
local ReportOverlay = {}
|
||||
|
||||
ReportOverlay.ReportType = {
|
||||
REPORT_GAME = 0;
|
||||
}
|
||||
|
||||
local REPORT_COMMENT = "Game reported from the Xbox App.";
|
||||
|
||||
function ReportOverlay:CreateReportOverlay(reportType, assetId)
|
||||
local this = BaseOverlay()
|
||||
|
||||
local submitButton = Utility.Create'TextButton'
|
||||
{
|
||||
Name = "SubmitButton";
|
||||
Size = UDim2.new(0, 320, 0, 66);
|
||||
Position = UDim2.new(0, 776, 1, -100 - 66);
|
||||
BorderSizePixel = 0;
|
||||
BackgroundColor3 = GlobalSettings.BlueButtonColor;
|
||||
Font = GlobalSettings.RegularFont;
|
||||
FontSize = GlobalSettings.ButtonSize;
|
||||
TextColor3 = GlobalSettings.TextSelectedColor;
|
||||
Text = string.upper(Strings:LocalizedString("SubmitWord"));
|
||||
ZIndex = this.BaseZIndex;
|
||||
Parent = this.Container;
|
||||
|
||||
SoundManager:CreateSound('MoveSelection');
|
||||
}
|
||||
local titleText = Utility.Create'TextLabel'
|
||||
{
|
||||
Name = "TitleText";
|
||||
Size = UDim2.new(0, 0, 0, 0);
|
||||
Position = UDim2.new(0, submitButton.Position.X.Offset, 0, 136);
|
||||
BackgroundTransparency = 1;
|
||||
Font = GlobalSettings.RegularFont;
|
||||
FontSize = GlobalSettings.HeaderSize;
|
||||
TextColor3 = GlobalSettings.WhiteTextColor;
|
||||
Text = Strings:LocalizedString("ReportGameWord");
|
||||
TextXAlignment = Enum.TextXAlignment.Left;
|
||||
ZIndex = this.BaseZIndex;
|
||||
Parent = this.Container;
|
||||
}
|
||||
local descriptionText = Utility.Create'TextLabel'
|
||||
{
|
||||
Name = "DescriptionText";
|
||||
Size = UDim2.new(0, 762, 0, 304);
|
||||
Position = UDim2.new(0, titleText.Position.X.Offset, 0, titleText.Position.Y.Offset + 62);
|
||||
BackgroundTransparency = 1;
|
||||
TextXAlignment = Enum.TextXAlignment.Left;
|
||||
TextYAlignment = Enum.TextYAlignment.Top;
|
||||
Font = GlobalSettings.LightFont;
|
||||
FontSize = GlobalSettings.TitleSize;
|
||||
TextColor3 = GlobalSettings.WhiteTextColor;
|
||||
TextWrapped = true;
|
||||
Text = Strings:LocalizedString("ReportPhrase");
|
||||
ZIndex = this.BaseZIndex;
|
||||
Parent = this.Container;
|
||||
}
|
||||
|
||||
local reportIcon = Utility.Create'ImageLabel'
|
||||
{
|
||||
Name = "ReportIcon";
|
||||
Position = UDim2.new(0, 226, 0, 204);
|
||||
BackgroundTransparency = 1;
|
||||
ZIndex = this.BaseZIndex;
|
||||
}
|
||||
AssetManager.LocalImage(reportIcon, 'rbxasset://textures/ui/Shell/Icons/ErrorIconLargeCopy',
|
||||
{['720'] = UDim2.new(0,214,0,176); ['1080'] = UDim2.new(0,321,0,264);})
|
||||
this:SetImage(reportIcon)
|
||||
|
||||
submitButton.MouseButton1Click:connect(function()
|
||||
if this:Close() then
|
||||
if assetId then
|
||||
spawn(function()
|
||||
local result = Http.ReportAbuseAsync("Asset", assetId, 7, REPORT_COMMENT)
|
||||
end)
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
function this:GetPriority()
|
||||
return GlobalSettings.ElevatedPriority
|
||||
end
|
||||
|
||||
local baseFocus = this.Focus
|
||||
function this:Focus()
|
||||
baseFocus(this)
|
||||
GuiService.SelectedCoreObject = submitButton
|
||||
end
|
||||
|
||||
return this
|
||||
end
|
||||
|
||||
return ReportOverlay
|
||||
@@ -0,0 +1,173 @@
|
||||
--[[
|
||||
// RobuxBalanceOverlay.lua
|
||||
]]
|
||||
local CoreGui = Game:GetService("CoreGui")
|
||||
local GuiRoot = CoreGui:FindFirstChild("RobloxGui")
|
||||
local Modules = GuiRoot:FindFirstChild("Modules")
|
||||
local GuiService = game:GetService('GuiService')
|
||||
local TextService = game:GetService('TextService')
|
||||
|
||||
local AssetManager = require(Modules:FindFirstChild('AssetManager'))
|
||||
local GlobalSettings = require(Modules:FindFirstChild('GlobalSettings'))
|
||||
local Strings = require(Modules:FindFirstChild('LocalizedStrings'))
|
||||
local Utility = require(Modules:FindFirstChild('Utility'))
|
||||
local BaseOverlay = require(Modules:FindFirstChild('BaseOverlay'))
|
||||
local ScreenManager = require(Modules:FindFirstChild('ScreenManager'))
|
||||
local SoundManager = require(Modules:FindFirstChild('SoundManager'))
|
||||
local CurrencyWidgetModule = require(Modules:FindFirstChild('CurrencyWidget'))
|
||||
local UserData = require(Modules:FindFirstChild('UserData'))
|
||||
|
||||
local function createRobuxBalanceOverlay(platformBalance, totalBalance)
|
||||
local this = BaseOverlay()
|
||||
|
||||
local currencyWidget = nil
|
||||
local onRobuxChangedCn = nil
|
||||
|
||||
local overlayImage = Utility.Create'ImageLabel'
|
||||
{
|
||||
Name = "OverlayImage";
|
||||
Size = UDim2.new(0, 416, 0, 416);
|
||||
BackgroundTransparency = 1;
|
||||
ZIndex = this.BaseZIndex + 1;
|
||||
Image = 'rbxasset://textures/ui/Shell/Icons/AlertIcon.png';
|
||||
}
|
||||
Utility.CalculateAnchor(overlayImage, UDim2.new(0.5, 0, 0.5, 0), Utility.Enum.Anchor.Center)
|
||||
this:SetImage(overlayImage)
|
||||
|
||||
local titleText = Utility.Create'TextLabel'
|
||||
{
|
||||
Name = "TitleText";
|
||||
Size = UDim2.new(0, 0, 0, 0);
|
||||
Position = UDim2.new(0, this.RightAlign, 0, 88);
|
||||
BackgroundTransparency = 1;
|
||||
Font = GlobalSettings.RegularFont;
|
||||
FontSize = GlobalSettings.HeaderSize;
|
||||
TextColor3 = GlobalSettings.WhiteTextColor;
|
||||
Text = Strings:LocalizedString("RobuxBalanceOverlayTitle");
|
||||
TextXAlignment = Enum.TextXAlignment.Left;
|
||||
ZIndex = this.BaseZIndex;
|
||||
Parent = this.Container;
|
||||
}
|
||||
local descriptionText = Utility.Create'TextLabel'
|
||||
{
|
||||
Name = "DescriptionText";
|
||||
Size = UDim2.new(0, 762, 0, 126);
|
||||
Position = UDim2.new(0, this.RightAlign, 0, titleText.Position.Y.Offset + 62);
|
||||
BackgroundTransparency = 1;
|
||||
TextXAlignment = Enum.TextXAlignment.Left;
|
||||
TextYAlignment = Enum.TextYAlignment.Top;
|
||||
Font = GlobalSettings.LightFont;
|
||||
FontSize = GlobalSettings.TitleSize;
|
||||
TextColor3 = GlobalSettings.WhiteTextColor;
|
||||
TextWrapped = true;
|
||||
Text = Strings:LocalizedString("RobuxBalanceOverlayPhrase");
|
||||
ZIndex = this.BaseZIndex;
|
||||
Parent = this.Container;
|
||||
}
|
||||
local platformBalanceTitle = Utility.Create'TextLabel'
|
||||
{
|
||||
Name = "platformBalanceTitle";
|
||||
BackgroundTransparency = 1;
|
||||
Font = GlobalSettings.RegularFont;
|
||||
FontSize = GlobalSettings.TitleSize;
|
||||
TextXAlignment = Enum.TextXAlignment.Left;
|
||||
TextColor3 = GlobalSettings.WhiteTextColor;
|
||||
Text = Strings:LocalizedString("PlatformBalanceTitle");
|
||||
ZIndex = this.BaseZIndex;
|
||||
Parent = this.Container;
|
||||
}
|
||||
local offsetSize = TextService:GetTextSize(platformBalanceTitle.Text, 42, GlobalSettings.RegularFont, Vector2.new())
|
||||
platformBalanceTitle.Size = UDim2.new(0, offsetSize.x, 0, offsetSize.y)
|
||||
Utility.CalculateAnchor(platformBalanceTitle,
|
||||
UDim2.new(0, this.RightAlign, 0, descriptionText.Position.Y.Offset + descriptionText.Size.Y.Offset + 60),
|
||||
Utility.Enum.Anchor.CenterLeft)
|
||||
|
||||
local totalBalanceTitle = platformBalanceTitle:Clone()
|
||||
totalBalanceTitle.Name = "TotalBalanceTitle"
|
||||
totalBalanceTitle.Text = Strings:LocalizedString("TotalBalanceTitle");
|
||||
totalBalanceTitle.Parent = this.Container
|
||||
offsetSize = TextService:GetTextSize(totalBalanceTitle.Text, 42, GlobalSettings.RegularFont, Vector2.new())
|
||||
totalBalanceTitle.Size = UDim2.new(0, offsetSize.x, 0, offsetSize.y)
|
||||
Utility.CalculateAnchor(totalBalanceTitle,
|
||||
UDim2.new(0, this.RightAlign, 0, platformBalanceTitle.Position.Y.Offset + platformBalanceTitle.Size.Y.Offset + 16),
|
||||
Utility.Enum.Anchor.CenterLeft)
|
||||
|
||||
local platformBalanceText = Utility.Create'TextLabel'
|
||||
{
|
||||
Name = "XboxBalanceText";
|
||||
BackgroundTransparency = 1;
|
||||
Font = GlobalSettings.RegularFont;
|
||||
FontSize = GlobalSettings.TitleSize;
|
||||
TextXAlignment = Enum.TextXAlignment.Left;
|
||||
TextColor3 = GlobalSettings.GreenTextColor;
|
||||
ZIndex = this.BaseZIndex;
|
||||
Parent = this.Container;
|
||||
}
|
||||
|
||||
local function setPlatformBalance(newBalance)
|
||||
platformBalanceText.Text = Utility.FormatNumberString(newBalance)
|
||||
offsetSize = TextService:GetTextSize(platformBalanceText.Text, 42, GlobalSettings.RegularFont, Vector2.new())
|
||||
platformBalanceText.Size = UDim2.new(0, offsetSize.x, 0, offsetSize.y)
|
||||
platformBalanceText.Position = UDim2.new(0, this.RightAlign + platformBalanceTitle.Size.X.Offset + 8, 0, platformBalanceTitle.Position.Y.Offset)
|
||||
end
|
||||
setPlatformBalance(platformBalance)
|
||||
|
||||
local totalBalanceText = platformBalanceText:Clone()
|
||||
totalBalanceText.Name = "TotalBalanceText"
|
||||
totalBalanceText.Parent = this.Container
|
||||
|
||||
local function setTotalBalance(newBalance)
|
||||
totalBalanceText.Text = Utility.FormatNumberString(newBalance)
|
||||
offsetSize = TextService:GetTextSize(totalBalanceText.Text, 42, GlobalSettings.RegularFont, Vector2.new())
|
||||
totalBalanceText.Size = UDim2.new(0, offsetSize.x, 0, offsetSize.y)
|
||||
totalBalanceText.Position = UDim2.new(0, this.RightAlign + totalBalanceTitle.Size.X.Offset + 8, 0, totalBalanceTitle.Position.Y.Offset)
|
||||
end
|
||||
setTotalBalance(totalBalance)
|
||||
|
||||
local okButton = Utility.Create'TextButton'
|
||||
{
|
||||
Name = "OkButton";
|
||||
Size = UDim2.new(0, 320, 0, 66);
|
||||
Position = UDim2.new(0, titleText.Position.X.Offset, 1, -66 - 55);
|
||||
BorderSizePixel = 0;
|
||||
BackgroundColor3 = GlobalSettings.BlueButtonColor;
|
||||
Font = GlobalSettings.RegularFont;
|
||||
FontSize = GlobalSettings.ButtonSize;
|
||||
TextColor3 = GlobalSettings.TextSelectedColor;
|
||||
Text = string.upper(Strings:LocalizedString("OkWord"));
|
||||
ZIndex = this.BaseZIndex;
|
||||
Parent = this.Container;
|
||||
|
||||
SoundManager:CreateSound('MoveSelection');
|
||||
}
|
||||
|
||||
--[[ Input Events ]]--
|
||||
okButton.MouseButton1Click:connect(function()
|
||||
this:Close()
|
||||
end)
|
||||
local baseFocus = this.Focus
|
||||
function this:Focus()
|
||||
baseFocus(this)
|
||||
GuiService.SelectedCoreObject = okButton
|
||||
|
||||
-- listen to robux changing
|
||||
if not currencyWidget then
|
||||
currencyWidget = CurrencyWidgetModule()
|
||||
end
|
||||
onRobuxChangedCn = Utility.DisconnectEvent(onRobuxChangedCn)
|
||||
onRobuxChangedCn = currencyWidget.RobuxChanged:connect(function(newPlatformBalance)
|
||||
setPlatformBalance(newPlatformBalance)
|
||||
setTotalBalance(UserData.GetTotalUserBalanceAsync())
|
||||
end)
|
||||
end
|
||||
|
||||
function this:ScreenRemoved()
|
||||
onRobuxChangedCn = Utility.DisconnectEvent(onRobuxChangedCn)
|
||||
currencyWidget:Destroy()
|
||||
currencyWidget = nil
|
||||
end
|
||||
|
||||
return this
|
||||
end
|
||||
|
||||
return createRobuxBalanceOverlay
|
||||
@@ -0,0 +1,262 @@
|
||||
-- Written by Kip Turner, Copyright ROBLOX 2015
|
||||
|
||||
local CoreGui = Game:GetService("CoreGui")
|
||||
local GuiRoot = CoreGui:FindFirstChild("RobloxGui")
|
||||
local Modules = GuiRoot:FindFirstChild("Modules")
|
||||
local Utility = require(Modules:FindFirstChild('Utility'))
|
||||
|
||||
local PlatformService = nil
|
||||
pcall(function() PlatformService = game:GetService('PlatformService') end)
|
||||
|
||||
|
||||
local SoundManager = require(Modules:FindFirstChild('SoundManager'))
|
||||
local GlobalSettings = require(Modules:FindFirstChild('GlobalSettings'))
|
||||
|
||||
local ScreenManager = {}
|
||||
|
||||
local ScreenStack = {}
|
||||
local ScreenToHideMap = {}
|
||||
|
||||
local ScreenGuis = {[1] = GuiRoot}
|
||||
|
||||
local function ContainsScreenInternal(screen)
|
||||
local foundScreenIndex = nil
|
||||
for i, otherScreen in pairs(ScreenStack) do
|
||||
if otherScreen == screen then
|
||||
foundScreenIndex = i
|
||||
end
|
||||
end
|
||||
|
||||
return foundScreenIndex
|
||||
end
|
||||
|
||||
local function GetScreenPriorityInternal(screen)
|
||||
local priority = GlobalSettings.DefaultPriority
|
||||
if screen.GetPriority ~= nil then
|
||||
priority = screen:GetPriority()
|
||||
end
|
||||
return priority
|
||||
end
|
||||
|
||||
function ScreenManager:GetInsertIndexForScreen(screen)
|
||||
local screenPriority = GetScreenPriorityInternal(screen)
|
||||
local currentScreen = self:GetTopScreen()
|
||||
while currentScreen and GetScreenPriorityInternal(currentScreen) > screenPriority do
|
||||
currentScreen = self:GetScreenBelow(currentScreen)
|
||||
end
|
||||
if currentScreen then
|
||||
local currentScreenIndex = ContainsScreenInternal(currentScreen)
|
||||
if currentScreenIndex then
|
||||
return currentScreenIndex + 1
|
||||
end
|
||||
end
|
||||
return 1
|
||||
end
|
||||
|
||||
function ScreenManager:GetScreenGuiByPriority(priority)
|
||||
priority = math.max(1, priority)
|
||||
if not ScreenGuis[priority] then
|
||||
for i = 1, priority do
|
||||
if not ScreenGuis[i] then
|
||||
ScreenGuis[i] = Utility.Create'ScreenGui'
|
||||
{
|
||||
Name = 'AppShell' .. tostring(i);
|
||||
Parent = CoreGui;
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return ScreenGuis[priority]
|
||||
end
|
||||
|
||||
-- TODO: handle race conditions for opening multiple screens
|
||||
local openScreenEntryCount = 0
|
||||
local openScreenExitCount = 0
|
||||
function ScreenManager:OpenScreen(screen, hideCurrent)
|
||||
if openScreenEntryCount ~= openScreenExitCount then
|
||||
print("ScreenManager: OpenScreen Re-entry detected" , openScreenEntryCount, openScreenExitCount)
|
||||
end
|
||||
openScreenEntryCount = openScreenEntryCount + 1
|
||||
|
||||
if hideCurrent == nil then
|
||||
hideCurrent = true
|
||||
end
|
||||
|
||||
local currentScreen = self:GetTopScreen()
|
||||
local insertIndex = self:GetInsertIndexForScreen(screen)
|
||||
local isNewTop = insertIndex > #ScreenStack
|
||||
|
||||
if not isNewTop then
|
||||
hideCurrent = false
|
||||
end
|
||||
|
||||
if currentScreen ~= screen then
|
||||
local foundScreenIndex = ContainsScreenInternal(screen)
|
||||
|
||||
if foundScreenIndex then
|
||||
table.remove(ScreenStack, foundScreenIndex)
|
||||
end
|
||||
|
||||
if currentScreen then
|
||||
currentScreen:RemoveFocus()
|
||||
if hideCurrent then
|
||||
currentScreen:Hide()
|
||||
end
|
||||
ScreenToHideMap[currentScreen] = hideCurrent
|
||||
end
|
||||
|
||||
table.insert(ScreenStack, insertIndex, screen)
|
||||
-- spawn(function()
|
||||
if isNewTop then
|
||||
if ScreenToHideMap[screen] ~= false then
|
||||
screen:Show()
|
||||
end
|
||||
|
||||
if screen == self:GetTopScreen() then
|
||||
screen:Focus()
|
||||
end
|
||||
else
|
||||
ScreenToHideMap[screen] = true
|
||||
end
|
||||
-- end)
|
||||
end
|
||||
|
||||
openScreenExitCount = openScreenExitCount + 1
|
||||
end
|
||||
|
||||
local closeCurrentEntryCount = 0
|
||||
local closeCurrentExitCount = 0
|
||||
function ScreenManager:CloseCurrent()
|
||||
if closeCurrentEntryCount ~= closeCurrentExitCount then
|
||||
print("ScreenManager: CloseScreen Re-entry detected" , closeCurrentEntryCount, closeCurrentExitCount)
|
||||
end
|
||||
closeCurrentEntryCount = closeCurrentEntryCount + 1
|
||||
|
||||
local currentScreen = ScreenStack[#ScreenStack]
|
||||
local belowScreen = currentScreen and self:GetScreenBelow(currentScreen)
|
||||
if currentScreen then
|
||||
-- spawn(function()
|
||||
currentScreen:Hide()
|
||||
currentScreen:RemoveFocus()
|
||||
if currentScreen.ScreenRemoved then
|
||||
currentScreen:ScreenRemoved()
|
||||
end
|
||||
-- end)
|
||||
table.remove(ScreenStack, #ScreenStack)
|
||||
ScreenToHideMap[currentScreen] = nil
|
||||
end
|
||||
|
||||
|
||||
-- if belowScreen and belowScreen ~= self:GetTopScreen() then return end
|
||||
local newTop = belowScreen
|
||||
if newTop and newTop == self:GetTopScreen() then
|
||||
local showNewTop = (ScreenToHideMap[newTop] == true)
|
||||
|
||||
-- spawn(function()
|
||||
if showNewTop then
|
||||
newTop:Show()
|
||||
end
|
||||
if newTop == self:GetTopScreen() then
|
||||
newTop:Focus()
|
||||
end
|
||||
-- end)
|
||||
end
|
||||
closeCurrentExitCount = closeCurrentExitCount + 1
|
||||
end
|
||||
|
||||
function ScreenManager:ContainsScreen(screen)
|
||||
local index = ContainsScreenInternal(screen)
|
||||
if index then
|
||||
return ScreenStack[index]
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
function ScreenManager:GetScreenBelow(screen)
|
||||
local thisScreenIndex = ContainsScreenInternal(screen)
|
||||
if thisScreenIndex then
|
||||
return ScreenStack[thisScreenIndex - 1]
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
function ScreenManager:GetTopScreen()
|
||||
return ScreenStack[#ScreenStack]
|
||||
end
|
||||
|
||||
|
||||
----- TWEENS -----
|
||||
|
||||
local function FadeInElement(element, tweeners)
|
||||
if element == nil then return end
|
||||
if element:IsA('ImageLabel') or element:IsA('ImageButton') then
|
||||
table.insert(tweeners, Utility.PropertyTweener(element, 'ImageTransparency', 1, element.ImageTransparency, 0.5, Utility.EaseOutQuad))
|
||||
end
|
||||
if element:IsA('GuiObject') then
|
||||
table.insert(tweeners, Utility.PropertyTweener(element, 'BackgroundTransparency', 1, element.BackgroundTransparency, 0.5, Utility.EaseOutQuad))
|
||||
end
|
||||
if element:IsA('TextLabel') or element:IsA('TextBox') or element:IsA('TextButton') then
|
||||
table.insert(tweeners, Utility.PropertyTweener(element, 'TextTransparency', 1, element.TextTransparency, 0.5, Utility.EaseOutQuad))
|
||||
end
|
||||
for _, child in pairs(element:GetChildren()) do
|
||||
FadeInElement(child, tweeners)
|
||||
end
|
||||
end
|
||||
|
||||
function ScreenManager:FadeInSitu(guiObject)
|
||||
local tweeners = {}
|
||||
if guiObject then
|
||||
FadeInElement(guiObject, tweeners)
|
||||
end
|
||||
return tweeners
|
||||
end
|
||||
|
||||
function ScreenManager:DefaultFadeIn(guiObject)
|
||||
local tweeners = {}
|
||||
|
||||
if guiObject then
|
||||
table.insert(tweeners, Utility.PropertyTweener(guiObject, 'Position', guiObject.Position + UDim2.new(0.15, 0, 0, 0), guiObject.Position, 0.5,
|
||||
function(t,b,c,d)
|
||||
if t >= d then return b + c end
|
||||
t = t / d;
|
||||
local tComputed = t*(t-2)
|
||||
return -UDim2.new(c.X.Scale * tComputed, c.X.Offset * tComputed, c.Y.Scale * tComputed, c.Y.Offset * tComputed) + b
|
||||
end))
|
||||
|
||||
FadeInElement(guiObject, tweeners)
|
||||
end
|
||||
|
||||
pcall(function()
|
||||
if UserSettings().GameSettings:InStudioMode() or (PlatformService and PlatformService.DatamodelType == 0) then
|
||||
local CameraManager = require(Modules:FindFirstChild('CameraManager'))
|
||||
CameraManager:StartTransitionScreenEffect()
|
||||
end
|
||||
end)
|
||||
|
||||
return tweeners
|
||||
end
|
||||
|
||||
function ScreenManager:DefaultCancelFade(tweens)
|
||||
if tweens then
|
||||
for _, tween in pairs(tweens) do
|
||||
tween:Finish()
|
||||
end
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
---------------
|
||||
|
||||
--------- SOUNDS ---------
|
||||
|
||||
function ScreenManager:PlayDefaultOpenSound()
|
||||
SoundManager:Play('ScreenChange')
|
||||
end
|
||||
|
||||
----------------------------
|
||||
|
||||
|
||||
|
||||
return ScreenManager
|
||||
|
||||
@@ -0,0 +1,480 @@
|
||||
-- Written by Kip Turner, Copyright ROBLOX 2015
|
||||
|
||||
local CoreGui = Game:GetService("CoreGui")
|
||||
local GuiRoot = CoreGui:FindFirstChild("RobloxGui")
|
||||
local Modules = GuiRoot:FindFirstChild("Modules")
|
||||
|
||||
local Utility = require(Modules:FindFirstChild('Utility'))
|
||||
local GlobalSettings = require(Modules:FindFirstChild('GlobalSettings'))
|
||||
|
||||
local GuiService = game:GetService('GuiService')
|
||||
|
||||
local DEFAULT_WINDOW_SIZE = UDim2.new(1,0,1,0)
|
||||
|
||||
|
||||
local function ScrollingGrid()
|
||||
|
||||
local this = {}
|
||||
|
||||
this.Enum =
|
||||
{
|
||||
ScrollDirection = {["Vertical"] = 1; ["Horizontal"] = 2;};
|
||||
StartCorner = {["UpperLeft"] = 1; ["UpperRight"] = 2; ["BottomLeft"] = 3; ["BottomRight"] = 4;};
|
||||
--ChildAlignment = {["UpperLeft"] = 1; ["UpperRight"] = 2; ["BottomLeft"] = 3; ["BottomRight"] = 4;};
|
||||
}
|
||||
|
||||
|
||||
this.GridItems = {}
|
||||
this.ItemSet = {}
|
||||
|
||||
this.ScrollDirection = this.Enum.ScrollDirection.Vertical
|
||||
|
||||
this.StartCorner = this.Enum.StartCorner.UpperLeft
|
||||
|
||||
this.FixedRowColumnCount = nil
|
||||
|
||||
--this.ChildAlignment = nil
|
||||
|
||||
this.CellSize = Vector2.new(100,100)
|
||||
this.Padding = Vector2.new(0,0)
|
||||
this.Spacing = Vector2.new(0,0)
|
||||
|
||||
|
||||
|
||||
|
||||
function this:GetPadding()
|
||||
return self.Padding
|
||||
end
|
||||
|
||||
function this:SetPadding(newPadding)
|
||||
if newPadding ~= self.Padding then
|
||||
self.Padding = newPadding
|
||||
self:RecalcLayout()
|
||||
end
|
||||
end
|
||||
|
||||
function this:GetSpacing()
|
||||
return self.Spacing
|
||||
end
|
||||
|
||||
function this:SetSpacing(newSpacing)
|
||||
if newSpacing ~= self.Spacing then
|
||||
self.Spacing = newSpacing
|
||||
self:RecalcLayout()
|
||||
end
|
||||
end
|
||||
|
||||
function this:GetCellSize()
|
||||
return self.CellSize
|
||||
end
|
||||
|
||||
function this:SetCellSize(cellSize)
|
||||
if cellSize ~= self.CellSize then
|
||||
self.CellSize = cellSize
|
||||
self:RecalcLayout()
|
||||
end
|
||||
end
|
||||
|
||||
function this:GetScrollDirection()
|
||||
return self.ScrollDirection
|
||||
end
|
||||
|
||||
function this:SetScrollDirection(newDirection)
|
||||
if newDirection ~= self.ScrollDirection then
|
||||
self.ScrollDirection = newDirection
|
||||
self:RecalcLayout()
|
||||
end
|
||||
end
|
||||
|
||||
function this:GetStartCorner()
|
||||
return self.StartCorner
|
||||
end
|
||||
|
||||
function this:SetStartCorner(newStartCorner)
|
||||
if newStartCorner ~= self.StartCorner then
|
||||
self.StartCorner = newStartCorner
|
||||
self:RecalcLayout()
|
||||
end
|
||||
end
|
||||
|
||||
function this:GetRowColumnConstraint()
|
||||
return self.FixedRowColumnCount
|
||||
end
|
||||
|
||||
function this:SetRowColumnConstraint(fixedRowColumnCount)
|
||||
if fixedRowColumnCount < 1 then
|
||||
fixedRowColumnCount = nil
|
||||
end
|
||||
if fixedRowColumnCount ~= self.FixedRowColumnCount then
|
||||
self.FixedRowColumnCount = fixedRowColumnCount
|
||||
self:RecalcLayout()
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function this:GetClipping()
|
||||
return self.Container.ClipsDescendants
|
||||
end
|
||||
|
||||
function this:SetClipping(clippingEnabled)
|
||||
self.Container.ClipsDescendants = clippingEnabled;
|
||||
end
|
||||
|
||||
function this:GetVisible()
|
||||
return self.Container.Visible
|
||||
end
|
||||
|
||||
function this:SetVisible(isVisible)
|
||||
self.Container.Visible = isVisible;
|
||||
end
|
||||
|
||||
function this:GetSize()
|
||||
return self.Container.Size
|
||||
end
|
||||
|
||||
function this:SetSize(size)
|
||||
self.Container.Size = size
|
||||
end
|
||||
|
||||
function this:GetPosition()
|
||||
return self.Container.Position
|
||||
end
|
||||
|
||||
function this:SetPosition(position)
|
||||
self.Container.Position = position
|
||||
end
|
||||
|
||||
function this:GetParent()
|
||||
return self.Container.Parent
|
||||
end
|
||||
|
||||
function this:SetParent(parent)
|
||||
self.Container.Parent = parent
|
||||
end
|
||||
|
||||
function this:GetGuiObject()
|
||||
return self.Container
|
||||
end
|
||||
|
||||
-- Default selection handles the case of removing the last item in the grid while it is selected
|
||||
-- Set to nil if do not want a default selection
|
||||
function this:SetDefaultSelection(selectionObject)
|
||||
self.DefaultSelection = selectionObject
|
||||
end
|
||||
|
||||
function this:ResetDefaultSelection()
|
||||
self.DefaultSelection = self.Container
|
||||
end
|
||||
|
||||
----
|
||||
|
||||
|
||||
function this:ContainsItem(gridItem)
|
||||
return self.ItemSet[gridItem] ~= nil
|
||||
end
|
||||
|
||||
function this:SortItems(sortFunc)
|
||||
table.sort(self.GridItems, sortFunc)
|
||||
self:RecalcLayout()
|
||||
|
||||
local selectedObject = self:FindAncestorGridItem(GuiService.SelectedCoreObject)
|
||||
if selectedObject and self:ContainsItem(selectedObject) then
|
||||
local thisPos = self:GetCanvasPositionForOffscreenItem(selectedObject)
|
||||
if thisPos then
|
||||
Utility.PropertyTweener(self.Container, 'CanvasPosition', thisPos, thisPos, 0, Utility.EaseOutQuad, true)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function this:AddItem(gridItem)
|
||||
if not self:ContainsItem(gridItem) then
|
||||
table.insert(self.GridItems, gridItem)
|
||||
self.ItemSet[gridItem] = true
|
||||
gridItem.Parent = self.Container
|
||||
if GuiService.SelectedCoreObject == self.DefaultSelection then
|
||||
GuiService.SelectedCoreObject = gridItem
|
||||
end
|
||||
self:RecalcLayout()
|
||||
end
|
||||
end
|
||||
|
||||
function this:RemoveItem(gridItem)
|
||||
if self:ContainsItem(gridItem) then
|
||||
for i, otherItem in pairs(self.GridItems) do
|
||||
if otherItem == gridItem then
|
||||
table.remove(self.GridItems, i)
|
||||
-- Assign a new selection
|
||||
if GuiService.SelectedCoreObject == gridItem then
|
||||
GuiService.SelectedCoreObject = self.GridItems[i] or self.GridItems[i-1] or self.GridItems[1] or self.DefaultSelection
|
||||
end
|
||||
-- Clean-up
|
||||
self.ItemSet[gridItem] = nil
|
||||
gridItem.Parent = nil
|
||||
self:RecalcLayout()
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function this:RemoveAllItems()
|
||||
local wasSelected = false
|
||||
do
|
||||
local currentSelection = GuiService.SelectedCoreObject
|
||||
while currentSelection ~= nil and wasSelected == false do
|
||||
wasSelected = wasSelected or self:ContainsItem(currentSelection)
|
||||
currentSelection = currentSelection.Parent
|
||||
end
|
||||
end
|
||||
for i = #self.GridItems, 1, -1 do
|
||||
local removed = table.remove(self.GridItems, i)
|
||||
self.ItemSet[removed] = nil
|
||||
removed.Parent = nil
|
||||
end
|
||||
|
||||
if wasSelected then
|
||||
GuiService.SelectedCoreObject = self.Container
|
||||
end
|
||||
|
||||
self:RecalcLayout()
|
||||
self.Container.CanvasPosition = Vector2.new(0, 0)
|
||||
end
|
||||
|
||||
function this:Get2DGridIndex(index)
|
||||
-- 0 base index
|
||||
local zerobasedIndex = index - 1
|
||||
local rows, columns = self:GetNumRowsColumns()
|
||||
local row, column;
|
||||
|
||||
-- TODO: implement StartCorner here
|
||||
if self.ScrollDirection == self.Enum.ScrollDirection.Vertical then
|
||||
row = math.floor(zerobasedIndex / columns)
|
||||
column = zerobasedIndex % columns
|
||||
else
|
||||
column = math.floor(zerobasedIndex / rows)
|
||||
row = zerobasedIndex % rows
|
||||
end
|
||||
|
||||
return row, column
|
||||
end
|
||||
|
||||
function this:GetNumRowsColumns()
|
||||
local rows, columns = 0, 0
|
||||
|
||||
local windowSize = self.Container.AbsoluteWindowSize
|
||||
local padding = self:GetPadding()
|
||||
local cellSize = self:GetCellSize()
|
||||
local cellSpacing = self:GetSpacing()
|
||||
local adjustedWindowSize = Utility.ClampVector2(Vector2.new(0, 0), windowSize - padding, windowSize - padding)
|
||||
local absoluteCellSize = Utility.ClampVector2(Vector2.new(1,1), cellSize + cellSpacing, cellSize + cellSpacing)
|
||||
local windowSizeCalc = (adjustedWindowSize + cellSpacing) / absoluteCellSize
|
||||
|
||||
if self.ScrollDirection == self.Enum.ScrollDirection.Vertical then
|
||||
columns = math.max(1, self:GetRowColumnConstraint() or math.floor(windowSizeCalc.x))
|
||||
rows = math.ceil(math.max(1, #self.GridItems) / columns)
|
||||
else
|
||||
rows = math.max(1, self:GetRowColumnConstraint() or math.floor(windowSizeCalc.y))
|
||||
columns = math.ceil(math.max(1, #self.GridItems) / rows)
|
||||
end
|
||||
|
||||
return rows, columns
|
||||
end
|
||||
|
||||
function this:GetGridPosition(row, column, gridItemSize)
|
||||
local cellSize = self:GetCellSize()
|
||||
local spacing = self:GetSpacing()
|
||||
local padding = self:GetPadding()
|
||||
return UDim2.new(0, padding.X + column * cellSize.X + column * spacing.X,
|
||||
0, padding.Y + row * cellSize.Y + row * spacing.Y)
|
||||
end
|
||||
|
||||
function this:GetGridItemSize()
|
||||
return self.CellSize
|
||||
--[[
|
||||
if self.CellSize then
|
||||
return self.CellSize
|
||||
end
|
||||
return UDim2.new(0, (self.Container.AbsoluteSize.X - ((self.Columns + 1) * self.CellPadding.X)) / self.Columns,
|
||||
0, (self.Container.AbsoluteSize.Y - ((self.Rows + 1) * self.CellPadding.Y)) / self.Rows)
|
||||
--]]
|
||||
-- if self.ScrollDirection == EnumScrollDirection.Vertical then
|
||||
-- return UDim2.new(0,(self.Container.AbsoluteSize.X - ((self.Columns + 1) * self.CellPadding.X)) / self.Columns, 0, self.Container.AbsoluteSize.Y / self.Rows)
|
||||
-- else
|
||||
-- return UDim2.new(0,self.Container.AbsoluteSize.Y / self.Rows, 0, (self.Container.AbsoluteSize.Y - ((self.Rows + 1) * self.CellPadding.Y)) / self.Rows)
|
||||
-- end
|
||||
end
|
||||
|
||||
function this:GetCanvasPositionForOffscreenItem(selectedObject)
|
||||
-- NOTE: using <= and >= instead of < and > because scrollingframe
|
||||
-- code may automatically bump it while we are observing the change
|
||||
if selectedObject and self.Container and self:ContainsItem(selectedObject) then
|
||||
if self.ScrollDirection == self.Enum.ScrollDirection.Vertical then
|
||||
if selectedObject.AbsolutePosition.Y <= self.Container.AbsolutePosition.Y then
|
||||
return Utility.ClampCanvasPosition(self.Container, Vector2.new(0, selectedObject.Position.Y.Offset)) -- - selectedObject.AbsoluteSize.Y/2))
|
||||
elseif selectedObject.AbsolutePosition.Y + selectedObject.AbsoluteSize.Y >= self.Container.AbsolutePosition.Y + self.Container.AbsoluteWindowSize.Y then
|
||||
return Utility.ClampCanvasPosition(self.Container, Vector2.new(0, -(self.Container.AbsoluteWindowSize.Y - selectedObject.Position.Y.Offset - selectedObject.AbsoluteSize.Y) )) --+ selectedObject.AbsoluteSize.Y/2))
|
||||
end
|
||||
else -- Horizontal
|
||||
if selectedObject.AbsolutePosition.X <= self.Container.AbsolutePosition.X then
|
||||
return Utility.ClampCanvasPosition(self.Container, Vector2.new(selectedObject.Position.X.Offset, 0))
|
||||
elseif selectedObject.AbsolutePosition.X + selectedObject.AbsoluteSize.X >= self.Container.AbsolutePosition.X + self.Container.AbsoluteWindowSize.X then
|
||||
return Utility.ClampCanvasPosition(self.Container, Vector2.new(-(self.Container.AbsoluteWindowSize.X - selectedObject.Position.X.Offset - selectedObject.AbsoluteSize.X), 0))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function this:RecalcLayout()
|
||||
local padding = self:GetPadding()
|
||||
local cellSpacing = self:GetSpacing()
|
||||
local gridItemSize = self:GetGridItemSize()
|
||||
local rows, columns = self:GetNumRowsColumns()
|
||||
|
||||
if self.ScrollDirection == self.Enum.ScrollDirection.Vertical then
|
||||
self.Container.CanvasSize = UDim2.new(self.Container.Size.X.Scale, self.Container.Size.X.Offset, 0, padding.Y * 2 + rows * gridItemSize.Y + (math.max(0, rows - 1)) * cellSpacing.Y)
|
||||
else
|
||||
self.Container.CanvasSize = UDim2.new(0, padding.X * 2 + columns * gridItemSize.X + (math.max(0, columns - 1)) * cellSpacing.X, self.Container.Size.Y.Scale, self.Container.Size.Y.Offset)
|
||||
end
|
||||
|
||||
local grid2DtoIndex = {}
|
||||
for i = 1, #self.GridItems do
|
||||
local row, column = self:Get2DGridIndex(i)
|
||||
local gridItem = self.GridItems[i]
|
||||
|
||||
gridItem.Size = UDim2.new(0, gridItemSize.X, 0, gridItemSize.Y)
|
||||
gridItem.Position = self:GetGridPosition(row, column, gridItemSize)
|
||||
|
||||
grid2DtoIndex[row] = grid2DtoIndex[row] or {}
|
||||
grid2DtoIndex[row][column] = gridItem
|
||||
end
|
||||
|
||||
for rowNum, row in pairs(grid2DtoIndex) do
|
||||
for columnNum, column in pairs(row) do
|
||||
local gridItem = grid2DtoIndex[rowNum][columnNum]
|
||||
if gridItem then
|
||||
if self.ScrollDirection == self.Enum.ScrollDirection.Vertical then
|
||||
gridItem.NextSelectionUp = grid2DtoIndex[rowNum - 1] and grid2DtoIndex[rowNum - 1][columnNum] or nil
|
||||
gridItem.NextSelectionDown = grid2DtoIndex[rowNum + 1] and grid2DtoIndex[rowNum + 1][columnNum] or nil
|
||||
if gridItem.NextSelectionDown == nil and grid2DtoIndex[rowNum + 1] ~= nil then
|
||||
gridItem.NextSelectionDown = self.GridItems[#self.GridItems]
|
||||
end
|
||||
gridItem.NextSelectionLeft = nil
|
||||
gridItem.NextSelectionRight = nil
|
||||
else
|
||||
gridItem.NextSelectionLeft = grid2DtoIndex[rowNum] and grid2DtoIndex[rowNum][columnNum - 1] or nil
|
||||
gridItem.NextSelectionRight = grid2DtoIndex[rowNum] and grid2DtoIndex[rowNum][columnNum + 1] or nil
|
||||
if gridItem.NextSelectionRight == nil and grid2DtoIndex[0] and grid2DtoIndex[0][columnNum + 1] then
|
||||
gridItem.NextSelectionRight = self.GridItems[#self.GridItems]
|
||||
end
|
||||
gridItem.NextSelectionUp = nil
|
||||
gridItem.NextSelectionDown = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function this:Destroy()
|
||||
if self.Container then
|
||||
self.Container:Destroy()
|
||||
end
|
||||
end
|
||||
|
||||
do
|
||||
local container = Utility.Create'ScrollingFrame'
|
||||
{
|
||||
Size = DEFAULT_WINDOW_SIZE;
|
||||
Name = "Container";
|
||||
BackgroundTransparency = 1;
|
||||
ClipsDescendants = true;
|
||||
ScrollingEnabled = false;
|
||||
ScrollBarThickness = 0;
|
||||
Selectable = false;
|
||||
}
|
||||
this.Container = container
|
||||
this.DefaultSelection = this.Container
|
||||
|
||||
this.Container.Changed:connect(function(prop)
|
||||
if prop == 'AbsoluteSize' then
|
||||
this:RecalcLayout()
|
||||
end
|
||||
end)
|
||||
|
||||
this:RecalcLayout()
|
||||
|
||||
|
||||
function this:FindAncestorGridItem(object)
|
||||
if object ~= nil then
|
||||
if self:ContainsItem(object) then
|
||||
return object
|
||||
end
|
||||
return self:FindAncestorGridItem(object.Parent)
|
||||
end
|
||||
end
|
||||
|
||||
local lastSelectedObject = nil
|
||||
GuiService.Changed:connect(function(prop)
|
||||
if prop == 'SelectedCoreObject' then
|
||||
local selectedObject = this:FindAncestorGridItem(GuiService.SelectedCoreObject)
|
||||
if selectedObject and this:ContainsItem(selectedObject) then
|
||||
-- print(selectedObject.NextSelectionUp, selectedObject.NextSelectionDown, selectedObject.NextSelectionLeft, selectedObject.NextSelectionRight)
|
||||
|
||||
local upDirection = (this.ScrollDirection == this.Enum.ScrollDirection.Vertical) and 'NextSelectionUp' or 'NextSelectionLeft'
|
||||
local downDirection = (this.ScrollDirection == this.Enum.ScrollDirection.Vertical) and 'NextSelectionDown' or 'NextSelectionRight'
|
||||
local upObject = selectedObject[upDirection]
|
||||
local downObject = selectedObject[downDirection]
|
||||
|
||||
local nextPos, upPos, downPos;
|
||||
|
||||
|
||||
local gridItemSize = this:GetGridItemSize()
|
||||
local thisPos = this:GetCanvasPositionForOffscreenItem(selectedObject)
|
||||
|
||||
if lastSelectedObject then
|
||||
local lastUpObject = lastSelectedObject[upDirection]
|
||||
local lastDownObject = lastSelectedObject[downDirection]
|
||||
|
||||
if upObject and lastUpObject == selectedObject then
|
||||
upPos = this:GetCanvasPositionForOffscreenItem(upObject)
|
||||
upPos = upPos and upPos + gridItemSize / 2
|
||||
elseif downObject and lastDownObject == selectedObject then
|
||||
downPos = this:GetCanvasPositionForOffscreenItem(downObject)
|
||||
downPos = downPos and downPos - gridItemSize / 2
|
||||
end
|
||||
end
|
||||
|
||||
if upPos and (upPos.Y < this.Container.CanvasPosition.Y or upPos.X < this.Container.CanvasPosition.X) then
|
||||
nextPos = upPos
|
||||
-- print('up' , nextPos , selectedObject.Position, lastSelectedObject and lastSelectedObject.Position)
|
||||
elseif downPos and (downPos.Y > this.Container.CanvasPosition.Y or downPos.X > this.Container.CanvasPosition.X) then
|
||||
nextPos = downPos
|
||||
-- print('down' , nextPos , selectedObject.Position, lastSelectedObject and lastSelectedObject.Position)
|
||||
else
|
||||
nextPos = thisPos
|
||||
-- print('this' , selectedObject.Name , nextPos , selectedObject.Position, lastSelectedObject and lastSelectedObject.Position, selectedObject.AbsolutePosition, this.Container.AbsolutePosition)
|
||||
end
|
||||
|
||||
if nextPos then
|
||||
-- print("nextPos" , selectedObject.Name , nextPos)
|
||||
nextPos = Utility.ClampCanvasPosition(this.Container, nextPos)
|
||||
if thisPos then --and thisPos ~= nextPos then
|
||||
-- Sort of a hack to not snap on the last one
|
||||
if (upObject and downObject) then
|
||||
Utility.PropertyTweener(this.Container, 'CanvasPosition', thisPos, thisPos, 0, Utility.EaseOutQuad, true)
|
||||
end
|
||||
end
|
||||
Utility.PropertyTweener(this.Container, 'CanvasPosition', this.Container.CanvasPosition, nextPos, 0.2, Utility.EaseOutQuad, true)
|
||||
end
|
||||
lastSelectedObject = selectedObject
|
||||
else
|
||||
lastSelectedObject = nil
|
||||
end
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
return this
|
||||
end
|
||||
|
||||
return ScrollingGrid
|
||||
@@ -0,0 +1,209 @@
|
||||
--[[
|
||||
// ScrollingTextBox.lua
|
||||
|
||||
// Creates a scrolling text box to be used with controlers and selectable
|
||||
// guis
|
||||
|
||||
// NOTE: Add any api needed to further expand this module
|
||||
]]
|
||||
local CoreGui = Game:GetService("CoreGui")
|
||||
local GuiRoot = CoreGui:FindFirstChild("RobloxGui")
|
||||
local Modules = GuiRoot:FindFirstChild("Modules")
|
||||
local GuiService = game:GetService('GuiService')
|
||||
|
||||
local AssetManager = require(Modules:FindFirstChild('AssetManager'))
|
||||
local GlobalSettings = require(Modules:FindFirstChild('GlobalSettings'))
|
||||
local Utility = require(Modules:FindFirstChild('Utility'))
|
||||
|
||||
local SoundManager = require(Modules:FindFirstChild('SoundManager'))
|
||||
|
||||
local createScrollingTextBox = function(size, position, parent)
|
||||
local this = {}
|
||||
|
||||
local SCROLL_BUFFER = 2
|
||||
|
||||
this.OnSelectableChaged = Utility.Signal()
|
||||
|
||||
-- adjust selection image
|
||||
local edgeSelectionImage = Utility.Create'ImageLabel'
|
||||
{
|
||||
Name = "EdgeSelectionImage";
|
||||
Size = UDim2.new(1, 32, 1, 32);
|
||||
Position = UDim2.new(0, -16, 0, -16);
|
||||
Image = 'rbxasset://textures/ui/SelectionBox.png';
|
||||
ScaleType = Enum.ScaleType.Slice;
|
||||
SliceCenter = Rect.new(21,21,41,41);
|
||||
BackgroundTransparency = 1;
|
||||
}
|
||||
|
||||
local container = Utility.Create'Frame'
|
||||
{
|
||||
Name = "ScrollingTextBox";
|
||||
Size = size or UDim2.new();
|
||||
Position = position or UDim2.new();
|
||||
BackgroundTransparency = 1;
|
||||
Parent = parent;
|
||||
}
|
||||
local scrollingFrame = Utility.Create'ScrollingFrame'
|
||||
{
|
||||
Name = "ScrollingBox";
|
||||
Size = UDim2.new(1, 0, 1, 0);
|
||||
Position = UDim2.new(0, 0, 0, 0);
|
||||
BackgroundTransparency = 1;
|
||||
ScrollBarThickness = 0;
|
||||
SelectionImageObject = edgeSelectionImage;
|
||||
Parent = container;
|
||||
|
||||
SoundManager:CreateSound('MoveSelection');
|
||||
}
|
||||
local textLabel = Utility.Create'TextLabel'
|
||||
{
|
||||
Name = "TextLabel";
|
||||
Size = UDim2.new(1, 0, 4, 0);
|
||||
Position = UDim2.new(0, 0, 0, 0);
|
||||
BackgroundTransparency = 1;
|
||||
TextXAlignment = Enum.TextXAlignment.Left;
|
||||
TextYAlignment = Enum.TextYAlignment.Top;
|
||||
Font = GlobalSettings.LightFont;
|
||||
FontSize = GlobalSettings.DescriptionSize;
|
||||
TextColor3 = GlobalSettings.WhiteTextColor;
|
||||
TextWrapped = true;
|
||||
Text = "";
|
||||
Parent = scrollingFrame;
|
||||
}
|
||||
local upArrow = Utility.Create'ImageLabel'
|
||||
{
|
||||
Name = "UpArrow";
|
||||
BackgroundTransparency = 1;
|
||||
ImageColor3 = GlobalSettings.WhiteTextColor;
|
||||
Visible = false;
|
||||
Parent = container;
|
||||
}
|
||||
local downArrow = Utility.Create'ImageLabel'
|
||||
{
|
||||
Name = "DownArrow";
|
||||
BackgroundTransparency = 1;
|
||||
ImageColor3 = GlobalSettings.WhiteTextColor;
|
||||
Visible = false;
|
||||
Parent = container;
|
||||
}
|
||||
AssetManager.LocalImage(upArrow,
|
||||
'rbxasset://textures/ui/Shell/Icons/UpIndicatorIcon', {['720'] = UDim2.new(0,14,0,13); ['1080'] = UDim2.new(0,21,0,19);})
|
||||
upArrow.Position = UDim2.new(0, 0, 1, 16)
|
||||
AssetManager.LocalImage(downArrow,
|
||||
'rbxasset://textures/ui/Shell/Icons/DownIndicatorIcon', {['720'] = UDim2.new(0,14,0,13); ['1080'] = UDim2.new(0,21,0,19);})
|
||||
downArrow.Position = UDim2.new(0, upArrow.Size.X.Offset, 1, 16)
|
||||
|
||||
--[[ Private Functions ]]--
|
||||
local function setArrowState()
|
||||
local canvasPosition = scrollingFrame.CanvasPosition
|
||||
local maxSizeY = textLabel.AbsoluteSize.y - scrollingFrame.AbsoluteWindowSize.y
|
||||
if canvasPosition.y >= maxSizeY - SCROLL_BUFFER then
|
||||
downArrow.ImageColor3 = GlobalSettings.GreyTextColor
|
||||
else
|
||||
downArrow.ImageColor3 = GlobalSettings.WhiteTextColor
|
||||
end
|
||||
if canvasPosition.y <= SCROLL_BUFFER then
|
||||
upArrow.ImageColor3 = GlobalSettings.GreyTextColor
|
||||
else
|
||||
upArrow.ImageColor3 = GlobalSettings.WhiteTextColor
|
||||
end
|
||||
end
|
||||
|
||||
local function setScrollSize()
|
||||
-- NOTE: this is a hack to get the actual height on textbounds
|
||||
textLabel.Size = UDim2.new(1, 0, 0, 100000)
|
||||
|
||||
local ySize = textLabel.TextBounds.y
|
||||
textLabel.Size = UDim2.new(1, 0, 0, ySize)
|
||||
scrollingFrame.CanvasSize = UDim2.new(0, 0, 0, ySize)
|
||||
local areArrowsVisible = ySize > scrollingFrame.AbsoluteSize.y
|
||||
this:SetArrowsVisible(areArrowsVisible)
|
||||
this:SetSelectable(areArrowsVisible)
|
||||
setArrowState();
|
||||
end
|
||||
|
||||
--[[ Events ]]--
|
||||
scrollingFrame.Changed:connect(function(property)
|
||||
if property == 'CanvasPosition' or property == 'AbsoluteWindowSize' then
|
||||
setArrowState()
|
||||
end
|
||||
end)
|
||||
|
||||
--[[ Public API ]]--
|
||||
function this:SetParent(newParent)
|
||||
scrollingFrame.Parent = newParent
|
||||
spawn(function()
|
||||
setScrollSize()
|
||||
end)
|
||||
end
|
||||
|
||||
function this:SetPosition(newPosition)
|
||||
scrollingFrame.Position = newPosition
|
||||
end
|
||||
|
||||
function this:SetSize(newSize)
|
||||
scrollingFrame.Size = newSize
|
||||
spawn(function()
|
||||
setScrollSize()
|
||||
end)
|
||||
end
|
||||
|
||||
function this:SetFontSize(newFontSize)
|
||||
textLabel.FontSize = newFontSize
|
||||
spawn(function()
|
||||
setScrollSize()
|
||||
end)
|
||||
end
|
||||
|
||||
function this:SetFont(newFont)
|
||||
textLabel.Font = newFont
|
||||
spawn(function()
|
||||
setScrollSize()
|
||||
end)
|
||||
end
|
||||
|
||||
function this:SetText(text)
|
||||
textLabel.Text = tostring(text)
|
||||
spawn(function()
|
||||
setScrollSize()
|
||||
end)
|
||||
end
|
||||
|
||||
function this:SetSelectable(value)
|
||||
scrollingFrame.Selectable = value
|
||||
this.OnSelectableChaged:fire(value)
|
||||
end
|
||||
|
||||
function this:SetZIndex(value)
|
||||
container.ZIndex = value
|
||||
textLabel.ZIndex = value
|
||||
upArrow.ZIndex = value
|
||||
downArrow.ZIndex = value
|
||||
end
|
||||
|
||||
function this:SetArrowsVisible(value)
|
||||
upArrow.Visible = value
|
||||
downArrow.Visible = value
|
||||
end
|
||||
|
||||
function this:GetContainer()
|
||||
return container
|
||||
end
|
||||
|
||||
function this:GetSelectableObject()
|
||||
return scrollingFrame
|
||||
end
|
||||
|
||||
function this:GetArrowsVisible()
|
||||
return upArrow.Visible
|
||||
end
|
||||
|
||||
function this:IsSelectable()
|
||||
return scrollingFrame.Selectable
|
||||
end
|
||||
|
||||
return this
|
||||
end
|
||||
|
||||
return createScrollingTextBox
|
||||
@@ -0,0 +1,277 @@
|
||||
--[[
|
||||
// SetAccountCredentialsScreen.lua
|
||||
]]
|
||||
local CoreGui = game:GetService("CoreGui")
|
||||
local GuiRoot = CoreGui:FindFirstChild("RobloxGui")
|
||||
local Modules = GuiRoot:FindFirstChild("Modules")
|
||||
|
||||
local ContextActionService = game:GetService('ContextActionService')
|
||||
local GuiService = game:GetService('GuiService')
|
||||
|
||||
local AccountManager = require(Modules:FindFirstChild('AccountManager'))
|
||||
local AssetManager = require(Modules:FindFirstChild('AssetManager'))
|
||||
local BaseSignInScreen = require(Modules:FindFirstChild('BaseSignInScreen'))
|
||||
local Errors = require(Modules:FindFirstChild('Errors'))
|
||||
local ErrorOverlay = require(Modules:FindFirstChild('ErrorOverlay'))
|
||||
local EventHub = require(Modules:FindFirstChild('EventHub'))
|
||||
local GlobalSettings = require(Modules:FindFirstChild('GlobalSettings'))
|
||||
local LoadingWidget = require(Modules:FindFirstChild('LoadingWidget'))
|
||||
local ScreenManager = require(Modules:FindFirstChild('ScreenManager'))
|
||||
local SoundManager = require(Modules:FindFirstChild('SoundManager'))
|
||||
local Strings = require(Modules:FindFirstChild('LocalizedStrings'))
|
||||
local TextBox = require(Modules:FindFirstChild('TextBox'))
|
||||
local Utility = require(Modules:FindFirstChild('Utility'))
|
||||
local UserData = require(Modules:FindFirstChild('UserData'))
|
||||
|
||||
local function createSetAccountCredentialsScreen(title, description, buttonText)
|
||||
local this = BaseSignInScreen()
|
||||
|
||||
this:SetTitle(string.upper(title or ""))
|
||||
this:SetDescriptionText(description or "")
|
||||
this:SetButtonText(buttonText or "")
|
||||
|
||||
local ModalOverlay = Utility.Create'Frame'
|
||||
{
|
||||
Name = "ModalOverlay";
|
||||
Size = UDim2.new(1, 0, 1, 0);
|
||||
BackgroundTransparency = GlobalSettings.ModalBackgroundTransparency;
|
||||
BackgroundColor3 = GlobalSettings.ModalBackgroundColor;
|
||||
BorderSizePixel = 0;
|
||||
ZIndex = 4;
|
||||
}
|
||||
|
||||
local myUsername = nil
|
||||
local myPassword = nil
|
||||
|
||||
local isUsernameValid = false
|
||||
local isPasswordValid = false
|
||||
|
||||
local UserNameProcessingCount = 0
|
||||
local PasswordProcessingCount = 0
|
||||
|
||||
this.UsernameObject:SetDefaultText(Strings:LocalizedString("UsernameWord").." ("..
|
||||
Strings:LocalizedString("UsernameRulePhrase")..")")
|
||||
this.UsernameObject:SetKeyboardTitle(Strings:LocalizedString("UsernameWord"))
|
||||
this.UsernameObject:SetKeyboardDescription(Strings:LocalizedString("UsernameRulePhrase"))
|
||||
local usernameChangedCn = nil
|
||||
|
||||
this.PasswordObject:SetDefaultText(Strings:LocalizedString("PasswordWord").." ("..
|
||||
Strings:LocalizedString("PasswordRulePhrase")..")")
|
||||
this.PasswordObject:SetKeyboardTitle(Strings:LocalizedString("PasswordWord"))
|
||||
this.PasswordObject:SetKeyboardDescription(Strings:LocalizedString("PasswordRulePhrase"))
|
||||
this.PasswordObject:SetKeyboardType(Enum.XboxKeyBoardType.Password)
|
||||
local passwordChangedCn = nil
|
||||
|
||||
local function createAndSetCredentialsAsync()
|
||||
local result = nil
|
||||
|
||||
local function signInAsync()
|
||||
-- check linked account status
|
||||
result = AccountManager:HasLinkedAccountAsync()
|
||||
|
||||
-- linked, set credentials
|
||||
if result == AccountManager.AuthResults.Success then
|
||||
result = AccountManager:SetRobloxCredentialsAsync(myUsername, myPassword)
|
||||
-- unlinked, create new account and set credentials
|
||||
elseif result == AccountManager.AuthResults.AccountUnlinked then
|
||||
result = AccountManager:GenerateAccountAsync(myUsername, myPassword)
|
||||
end
|
||||
end
|
||||
|
||||
local loader = LoadingWidget(
|
||||
{ Parent = ModalOverlay }, { signInAsync } )
|
||||
|
||||
-- set up full screen loader
|
||||
ModalOverlay.Parent = GuiRoot
|
||||
ContextActionService:BindCoreAction("BlockB", function() end, false, Enum.KeyCode.ButtonB)
|
||||
local selectedObject = GuiService.SelectedCoreObject
|
||||
GuiService.SelectedCoreObject = nil
|
||||
|
||||
-- call loader
|
||||
loader:AwaitFinished()
|
||||
|
||||
-- clean up
|
||||
loader:Cleanup()
|
||||
loader = nil
|
||||
GuiService.SelectedCoreObject = selectedObject
|
||||
ContextActionService:UnbindCoreAction("BlockB")
|
||||
ModalOverlay.Parent = nil
|
||||
|
||||
if result == AccountManager.AuthResults.Success then
|
||||
EventHub:dispatchEvent(EventHub.Notifications["AuthenticationSuccess"])
|
||||
else
|
||||
local err = result and Errors.Authentication[result] or Errors.Default
|
||||
ScreenManager:OpenScreen(ErrorOverlay(err), false)
|
||||
end
|
||||
end
|
||||
|
||||
local function validatePassword(silent)
|
||||
PasswordProcessingCount = PasswordProcessingCount + 1
|
||||
|
||||
local reason = nil
|
||||
isPasswordValid, reason = AccountManager:IsValidPasswordAsync(myUsername or "", myPassword)
|
||||
if isPasswordValid then
|
||||
if myUsername and #myUsername > 0 then
|
||||
GuiService.SelectedCoreObject = this.SignInButton
|
||||
else
|
||||
GuiService.SelectedCoreObject = this.UsernameSelection
|
||||
end
|
||||
elseif isPasswordValid == false then
|
||||
if not silent then
|
||||
-- web returns long strings on password error. Lets create our own error type
|
||||
GuiService.SelectedCoreObject = this.PasswordSelection
|
||||
local err = Errors.Default;
|
||||
if reason then
|
||||
err = Errors.SignIn.InvalidPassword
|
||||
err.Msg = reason
|
||||
end
|
||||
ScreenManager:OpenScreen(ErrorOverlay(err), false)
|
||||
end
|
||||
else -- Http failure
|
||||
|
||||
end
|
||||
|
||||
PasswordProcessingCount = PasswordProcessingCount - 1
|
||||
end
|
||||
|
||||
local function validateUsername(silent)
|
||||
UserNameProcessingCount = UserNameProcessingCount + 1
|
||||
|
||||
-- 1. Check if valid user name
|
||||
local reason = nil
|
||||
isUsernameValid, reason = AccountManager:IsValidUsernameAsync(myUsername)
|
||||
if isUsernameValid then
|
||||
-- 2. if password set, need to recheck password rules
|
||||
if myPassword and #myPassword > 0 then
|
||||
validatePassword()
|
||||
else
|
||||
GuiService.SelectedCoreObject = this.PasswordSelection
|
||||
end
|
||||
elseif isUsernameValid == false then
|
||||
if not silent then
|
||||
GuiService.SelectedCoreObject = this.UsernameSelection
|
||||
-- NOTE: Web has changed username rules and the result of the ErrorMessage in the endpoint call.
|
||||
-- We key check reason vs. our current error table, if we don't find a key, we create an error out
|
||||
-- of the reason returned. If there is no reason, use default. This covers both old and new behavior
|
||||
local err = nil
|
||||
if Errors.SignIn[reason] then
|
||||
err = Errors.SignIn[reason]
|
||||
elseif reason then
|
||||
err = { Title = Strings:LocalizedString("InvalidUsernameTitle"), Msg = reason }
|
||||
else
|
||||
err = Errors.Default
|
||||
end
|
||||
ScreenManager:OpenScreen(ErrorOverlay(err), false)
|
||||
end
|
||||
else -- Http Request failed
|
||||
|
||||
end
|
||||
|
||||
UserNameProcessingCount = UserNameProcessingCount - 1
|
||||
end
|
||||
|
||||
local function onUsernameChanged(text)
|
||||
myUsername = text
|
||||
if #myUsername > 0 then
|
||||
validateUsername()
|
||||
else
|
||||
GuiService.SelectedCoreObject = this.UsernameSelection
|
||||
isUsernameValid = false
|
||||
end
|
||||
end
|
||||
|
||||
local function onPasswordChanged(text)
|
||||
myPassword = text
|
||||
if #myPassword > 0 then
|
||||
validatePassword()
|
||||
else
|
||||
GuiService.SelectedCoreObject = this.PasswordSelection
|
||||
isPasswordValid = false
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local isSettingCredentials = false
|
||||
this.SignInButton.MouseButton1Click:connect(function()
|
||||
if isSettingCredentials then return end
|
||||
isSettingCredentials = true
|
||||
|
||||
local function stillValidatingUserInfo()
|
||||
return UserNameProcessingCount > 0 or PasswordProcessingCount > 0
|
||||
end
|
||||
local function awaitValidatingUserInfo()
|
||||
while stillValidatingUserInfo() do wait() end
|
||||
end
|
||||
|
||||
SoundManager:Play('ButtonPress')
|
||||
|
||||
|
||||
local processingFunctions = nil
|
||||
-- Wait for current validation to finish
|
||||
if stillValidatingUserInfo() then
|
||||
|
||||
processingFunctions = { awaitValidatingUserInfo }
|
||||
|
||||
-- Retry our username and password validation
|
||||
elseif isUsernameValid == nil or isPasswordValid == nil then
|
||||
|
||||
processingFunctions = {}
|
||||
if isUsernameValid == nil then
|
||||
table.insert(processingFunctions, function() validateUsername(true) awaitValidatingUserInfo() end)
|
||||
end
|
||||
if isPasswordValid == nil then
|
||||
table.insert(processingFunctions, function() validatePassword(true) awaitValidatingUserInfo() end)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
if processingFunctions then
|
||||
local processingLoader = LoadingWidget(
|
||||
{ Parent = ModalOverlay },
|
||||
processingFunctions)
|
||||
|
||||
-- NOTE: may need to get a separate overlay for this spinner
|
||||
-- Also should we disable input while overlay is active?
|
||||
ModalOverlay.Parent = GuiRoot
|
||||
|
||||
processingLoader:AwaitFinished()
|
||||
processingLoader:Cleanup()
|
||||
processingLoader = nil
|
||||
|
||||
ModalOverlay.Parent = nil
|
||||
end
|
||||
|
||||
if isUsernameValid and isPasswordValid then
|
||||
createAndSetCredentialsAsync()
|
||||
elseif isUsernameValid == false or isPasswordValid == false then
|
||||
local err = Errors.SignIn.NoUsernameOrPasswordEntered
|
||||
ScreenManager:OpenScreen(ErrorOverlay(err), false)
|
||||
else -- Http failed to validate your password or username
|
||||
local err = Errors.SignIn.ConnectionFailed
|
||||
ScreenManager:OpenScreen(ErrorOverlay(err), false)
|
||||
end
|
||||
isSettingCredentials = false
|
||||
end)
|
||||
|
||||
|
||||
--[[ Public API ]]--
|
||||
-- override
|
||||
local baseFocus = this.Focus
|
||||
function this:Focus()
|
||||
baseFocus(self)
|
||||
usernameChangedCn = this.UsernameObject.OnTextChanged:connect(onUsernameChanged)
|
||||
passwordChangedCn = this.PasswordObject.OnTextChanged:connect(onPasswordChanged)
|
||||
end
|
||||
|
||||
-- override
|
||||
local baseRemoveFocus = this.RemoveFocus
|
||||
function this:RemoveFocus()
|
||||
baseRemoveFocus(self)
|
||||
Utility.DisconnectEvent(usernameChangedCn)
|
||||
Utility.DisconnectEvent(passwordChangedCn)
|
||||
end
|
||||
|
||||
return this
|
||||
end
|
||||
|
||||
return createSetAccountCredentialsScreen
|
||||
@@ -0,0 +1,242 @@
|
||||
--[[
|
||||
]]
|
||||
local CoreGui = Game:GetService("CoreGui")
|
||||
local GuiRoot = CoreGui:FindFirstChild("RobloxGui")
|
||||
local Modules = GuiRoot:FindFirstChild("Modules")
|
||||
|
||||
local PlatformService = nil
|
||||
pcall(function() PlatformService = game:GetService('PlatformService') end)
|
||||
|
||||
local AssetManager = require(Modules:FindFirstChild('AssetManager'))
|
||||
local BaseScreen = require(Modules:FindFirstChild('BaseScreen'))
|
||||
local Errors = require(Modules:FindFirstChild('Errors'))
|
||||
local ErrorOverlay = require(Modules:FindFirstChild('ErrorOverlay'))
|
||||
local EventHub = require(Modules:FindFirstChild('EventHub'))
|
||||
local GlobalSettings = require(Modules:FindFirstChild('GlobalSettings'))
|
||||
local ScreenManager = require(Modules:FindFirstChild('ScreenManager'))
|
||||
local SoundManager = require(Modules:FindFirstChild('SoundManager'))
|
||||
local Strings = require(Modules:FindFirstChild('LocalizedStrings'))
|
||||
local Utility = require(Modules:FindFirstChild('Utility'))
|
||||
local AccountScreen = require(Modules:FindFirstChild('AccountScreen'))
|
||||
|
||||
local function createSettingsScreen()
|
||||
local this = BaseScreen()
|
||||
|
||||
this:SetTitle(string.upper(Strings:LocalizedString("SettingsWord")))
|
||||
|
||||
|
||||
local VersionBuildIdText = Utility.Create'TextLabel'
|
||||
{
|
||||
Name = "VersionBuildIdText";
|
||||
Size = UDim2.new(0, 0, 0, 0);
|
||||
Position = UDim2.new(1, 0, 1, 0);
|
||||
BackgroundTransparency = 1;
|
||||
Font = GlobalSettings.RegularFont;
|
||||
FontSize = GlobalSettings.TitleSize;
|
||||
TextColor3 = GlobalSettings.WhiteTextColor;
|
||||
TextXAlignment = Enum.TextXAlignment.Right;
|
||||
TextYAlignment = Enum.TextYAlignment.Bottom;
|
||||
Text = '';
|
||||
Parent = this.Container;
|
||||
}
|
||||
do
|
||||
local versionInfo;
|
||||
if UserSettings().GameSettings:InStudioMode() then
|
||||
versionInfo = {Major = 1, Minor = 0, Build = 0, Revision = 0}
|
||||
elseif PlatformService then
|
||||
versionInfo = PlatformService:GetVersionIdInfo();
|
||||
else
|
||||
versionInfo = {Major = 1, Minor = 1, Build = 1, Revision = 1}
|
||||
end
|
||||
|
||||
local versionStr = string.format(Strings:LocalizedString('VersionIdString'), tostring(versionInfo['Major']) , tostring(versionInfo['Minor']), tostring(versionInfo['Build']), tostring(versionInfo['Revision']))
|
||||
VersionBuildIdText.Text = versionStr
|
||||
end
|
||||
|
||||
|
||||
local spacing = 40
|
||||
local DefaultTransparency = GlobalSettings.TextBoxDefaultTransparency
|
||||
local SelectedTransparency = GlobalSettings.TextBoxSelectedTransparency
|
||||
|
||||
local AccountButton = Utility.Create'TextButton'
|
||||
{
|
||||
Name = "AccountButton";
|
||||
Size = UDim2.new(0, 394, 0, 612);
|
||||
Position = UDim2.new(0, 0, 0, 238);
|
||||
BackgroundTransparency = DefaultTransparency;
|
||||
BackgroundColor3 = GlobalSettings.TextBoxColor;
|
||||
BorderSizePixel = 0;
|
||||
Text = "";
|
||||
Parent = this.Container;
|
||||
SoundManager:CreateSound('MoveSelection');
|
||||
}
|
||||
|
||||
local SwitchProfileButton = AccountButton:Clone()
|
||||
SwitchProfileButton.Name = "SwitchProfileButton"
|
||||
SwitchProfileButton.Position = UDim2.new(0, AccountButton.Size.X.Offset + spacing, 0, 238)
|
||||
SwitchProfileButton.Parent = this.Container
|
||||
|
||||
local OverscanButton = SwitchProfileButton:Clone()
|
||||
OverscanButton.Name = "OverscanButton"
|
||||
OverscanButton.Position = UDim2.new(0, SwitchProfileButton.Position.X.Offset + SwitchProfileButton.Size.X.Offset + spacing, 0, 238)
|
||||
OverscanButton.Parent = this.Container
|
||||
|
||||
local HelpButton = OverscanButton:Clone()
|
||||
HelpButton.Name = "HelpButton"
|
||||
HelpButton.Position = UDim2.new(0, OverscanButton.Position.X.Offset + OverscanButton.Size.X.Offset + spacing, 0, 238)
|
||||
HelpButton.Parent = this.Container
|
||||
|
||||
local AccountIcon = Utility.Create'ImageLabel'
|
||||
{
|
||||
Name = "AccountIcon";
|
||||
Size = UDim2.new(0, 256, 0, 256);
|
||||
BackgroundTransparency = 1;
|
||||
Image = 'rbxasset://textures/ui/Shell/Icons/AccountIcon.png';
|
||||
Parent = AccountButton;
|
||||
}
|
||||
Utility.CalculateAnchor(AccountIcon, UDim2.new(0.5, 0, 0.5, 0), Utility.Enum.Anchor.Center)
|
||||
local AccountText = Utility.Create'TextLabel'
|
||||
{
|
||||
Name = "AccountText";
|
||||
Size = UDim2.new(0, 0, 0, 0);
|
||||
Position = UDim2.new(0.5, 0, 1, -96);
|
||||
BackgroundTransparency = 1;
|
||||
Font = GlobalSettings.RegularFont;
|
||||
FontSize = GlobalSettings.TitleSize;
|
||||
TextColor3 = GlobalSettings.WhiteTextColor;
|
||||
Text = Strings:LocalizedString("AccountWord");
|
||||
Parent = AccountButton;
|
||||
}
|
||||
|
||||
local SwitchProfileIcon = Utility.Create'ImageLabel'
|
||||
{
|
||||
Name = "SwitchProfileIcon";
|
||||
Size = UDim2.new(0, 224, 0, 255);
|
||||
BackgroundTransparency = 1;
|
||||
Image = 'rbxasset://textures/ui/Shell/Icons/ProfileIcon.png';
|
||||
Parent = SwitchProfileButton;
|
||||
}
|
||||
Utility.CalculateAnchor(SwitchProfileIcon, UDim2.new(0.5, 0, 0.5, 0), Utility.Enum.Anchor.Center)
|
||||
SwitchProfileText = AccountText:Clone()
|
||||
SwitchProfileText.Name = "SwitchProfileText"
|
||||
SwitchProfileText.Text = Strings:LocalizedString("SwitchProfileWord");
|
||||
SwitchProfileText.Parent = SwitchProfileButton
|
||||
|
||||
local OverscanIcon = Utility.Create'ImageLabel'
|
||||
{
|
||||
Name = "OverscanIcon";
|
||||
Size = UDim2.new(0, 256, 0, 181);
|
||||
BackgroundTransparency = 1;
|
||||
Image = 'rbxasset://textures/ui/Shell/Icons/TVIcon.png';
|
||||
Parent = OverscanButton;
|
||||
}
|
||||
Utility.CalculateAnchor(OverscanIcon, UDim2.new(0.5, 0, 0.5, 0), Utility.Enum.Anchor.Center)
|
||||
OverscanText = AccountText:Clone()
|
||||
OverscanText.Name = "OverscanText";
|
||||
OverscanText.Text = Strings:LocalizedString("OverscanWord");
|
||||
OverscanText.Parent = OverscanButton
|
||||
|
||||
local HelpIcon = Utility.Create'ImageLabel'
|
||||
{
|
||||
Name = "HelpIcon";
|
||||
Size = UDim2.new(0, 256, 0, 256);
|
||||
BackgroundTransparency = 1;
|
||||
Image = 'rbxasset://textures/ui/Shell/Icons/HelpIcon.png';
|
||||
Parent = HelpButton;
|
||||
}
|
||||
Utility.CalculateAnchor(HelpIcon, UDim2.new(0.5, 0, 0.5, 0), Utility.Enum.Anchor.Center)
|
||||
HelpText = AccountText:Clone()
|
||||
HelpText.Name = "HelpText";
|
||||
HelpText.Text = Strings:LocalizedString("HelpWord");
|
||||
HelpText.Parent = HelpButton
|
||||
|
||||
AccountButton.SelectionGained:connect(function()
|
||||
Utility.PropertyTweener(AccountButton, "BackgroundTransparency", SelectedTransparency,
|
||||
SelectedTransparency, 0, Utility.EaseInOutQuad, true)
|
||||
end)
|
||||
AccountButton.SelectionLost:connect(function()
|
||||
AccountButton.BackgroundTransparency = DefaultTransparency
|
||||
end)
|
||||
SwitchProfileButton.SelectionGained:connect(function()
|
||||
Utility.PropertyTweener(SwitchProfileButton, "BackgroundTransparency", SelectedTransparency,
|
||||
SelectedTransparency, 0, Utility.EaseInOutQuad, true)
|
||||
end)
|
||||
SwitchProfileButton.SelectionLost:connect(function()
|
||||
SwitchProfileButton.BackgroundTransparency = DefaultTransparency
|
||||
end)
|
||||
OverscanButton.SelectionGained:connect(function()
|
||||
Utility.PropertyTweener(OverscanButton, "BackgroundTransparency", SelectedTransparency,
|
||||
SelectedTransparency, 0, Utility.EaseInOutQuad, true)
|
||||
end)
|
||||
OverscanButton.SelectionLost:connect(function()
|
||||
OverscanButton.BackgroundTransparency = DefaultTransparency
|
||||
end)
|
||||
HelpButton.SelectionGained:connect(function()
|
||||
Utility.PropertyTweener(HelpButton, "BackgroundTransparency", SelectedTransparency,
|
||||
SelectedTransparency, 0, Utility.EaseInOutQuad, true)
|
||||
end)
|
||||
HelpButton.SelectionLost:connect(function()
|
||||
HelpButton.BackgroundTransparency = DefaultTransparency
|
||||
end)
|
||||
|
||||
--[[ Input ]]--
|
||||
AccountButton.MouseButton1Click:connect(function()
|
||||
SoundManager:Play('ButtonPress')
|
||||
local accountScreen = AccountScreen()
|
||||
if accountScreen then
|
||||
accountScreen:SetParent(this.Container.Parent)
|
||||
ScreenManager:OpenScreen(accountScreen, true)
|
||||
else
|
||||
ScreenManager:OpenScreen(ErrorOverlay(Errors.Default), false)
|
||||
end
|
||||
end)
|
||||
|
||||
local switchProfileDebounce = false
|
||||
SwitchProfileButton.MouseButton1Click:connect(function()
|
||||
if switchProfileDebounce then return end
|
||||
switchProfileDebounce = true
|
||||
SoundManager:Play('ButtonPress')
|
||||
if UserSettings().GameSettings:InStudioMode() then
|
||||
ScreenManager:OpenScreen(ErrorOverlay(Errors.Test.FeatureNotAvailableInStudio), false)
|
||||
elseif PlatformService then
|
||||
PlatformService:PopupAccountPickerUI(Enum.UserInputType.Gamepad1)
|
||||
end
|
||||
switchProfileDebounce = false
|
||||
end)
|
||||
|
||||
local overscanDebounce = false
|
||||
OverscanButton.MouseButton1Click:connect(function()
|
||||
if overscanDebounce then return end
|
||||
overscanDebounce = true
|
||||
SoundManager:Play('ButtonPress')
|
||||
EventHub:dispatchEvent(EventHub.Notifications["OpenOverscanScreen"], "")
|
||||
overscanDebounce = false
|
||||
end)
|
||||
|
||||
local helpDebounce = false
|
||||
HelpButton.MouseButton1Click:connect(function()
|
||||
if helpDebounce then return end
|
||||
helpDebounce = true
|
||||
SoundManager:Play('ButtonPress')
|
||||
if UserSettings().GameSettings:InStudioMode() then
|
||||
ScreenManager:OpenScreen(ErrorOverlay(Errors.Test.FeatureNotAvailableInStudio), false)
|
||||
helpDebounce = false
|
||||
else
|
||||
local success, result = pcall(function()
|
||||
-- errors will be handled by xbox
|
||||
return PlatformService:PopupHelpUI()
|
||||
end)
|
||||
helpDebounce = false
|
||||
end
|
||||
end)
|
||||
|
||||
--[[ Public API ]]--
|
||||
--Override
|
||||
function this:GetDefaultSelectionObject()
|
||||
return AccountButton
|
||||
end
|
||||
|
||||
return this
|
||||
end
|
||||
|
||||
return createSettingsScreen
|
||||
@@ -0,0 +1,172 @@
|
||||
--[[
|
||||
// SideBar.lua
|
||||
// Creates a side bar to be used for certain pages
|
||||
// Currently used by:
|
||||
// GameGenre
|
||||
// Friends
|
||||
]]
|
||||
local CoreGui = Game:GetService("CoreGui")
|
||||
local GuiRoot = CoreGui:FindFirstChild("RobloxGui")
|
||||
local Modules = GuiRoot:FindFirstChild("Modules")
|
||||
local ContextActionService = game:GetService("ContextActionService")
|
||||
local GuiService = game:GetService('GuiService')
|
||||
|
||||
local GlobalSettings = require(Modules:FindFirstChild('GlobalSettings'))
|
||||
local Utility = require(Modules:FindFirstChild('Utility'))
|
||||
local ScreenManager = require(Modules:FindFirstChild('ScreenManager'))
|
||||
local SoundManager = require(Modules:FindFirstChild('SoundManager'))
|
||||
|
||||
local CreateSideBar = function()
|
||||
local this = {}
|
||||
|
||||
local buttons = {}
|
||||
local selectedObject = nil
|
||||
|
||||
local INSET_Y = 156
|
||||
local INSET_X = 65
|
||||
local BUTTON_SIZE_Y = 75
|
||||
|
||||
local modalOverlay = Utility.Create'Frame'
|
||||
{
|
||||
Name = "ModalOverlay";
|
||||
Size = UDim2.new(1, 0, 1, 0);
|
||||
BackgroundTransparency = 1;
|
||||
BackgroundColor3 = GlobalSettings.ModalBackgroundColor;
|
||||
BorderSizePixel = 0;
|
||||
ZIndex = 4;
|
||||
}
|
||||
|
||||
local container = Utility.Create'Frame'
|
||||
{
|
||||
Name = "SideBarContainer";
|
||||
Size = UDim2.new(0.3, 0, 1, 0);
|
||||
Position = UDim2.new(1, 0, 0, 0);
|
||||
BorderSizePixel = 0;
|
||||
BackgroundColor3 = GlobalSettings.OverlayColor;
|
||||
ZIndex = 5;
|
||||
Parent = modalOverlay;
|
||||
}
|
||||
local dummySelectionImage = Utility.Create'TextButton'
|
||||
{
|
||||
Name = "DummySelectionImage";
|
||||
Size = UDim2.new(0, 0, 0, 0);
|
||||
Visible = false;
|
||||
|
||||
SoundManager:CreateSound('MoveSelection');
|
||||
}
|
||||
|
||||
local function recalcPositions()
|
||||
for i = 1, #buttons do
|
||||
buttons[i].Position = UDim2.new(0, 0, 0, INSET_Y + (BUTTON_SIZE_Y * (i - 1)))
|
||||
end
|
||||
end
|
||||
|
||||
--[[ Public API ]]--
|
||||
function this:AddItem(newItemName, callback)
|
||||
local button = Utility.Create'TextButton'
|
||||
{
|
||||
Name = "SortButton";
|
||||
Size = UDim2.new(1, 0, 0, BUTTON_SIZE_Y);
|
||||
BorderSizePixel = 0;
|
||||
BackgroundColor3 = GlobalSettings.BlueButtonColor;
|
||||
BackgroundTransparency = 1;
|
||||
ZIndex = 6;
|
||||
Text = "";
|
||||
SelectionImageObject = dummySelectionImage;
|
||||
Parent = container;
|
||||
|
||||
SoundManager:CreateSound('MoveSelection');
|
||||
}
|
||||
local text = Utility.Create'TextLabel'
|
||||
{
|
||||
Name = "SortName";
|
||||
Size = UDim2.new(1, -INSET_X, 1, 0);
|
||||
Position = UDim2.new(0, INSET_X, 0, 0);
|
||||
BackgroundTransparency = 1;
|
||||
Text = newItemName;
|
||||
TextXAlignment = Enum.TextXAlignment.Left;
|
||||
TextColor3 = GlobalSettings.WhiteTextColor;
|
||||
Font = GlobalSettings.RegularFont;
|
||||
FontSize = GlobalSettings.MediumFontSize;
|
||||
ZIndex = 7;
|
||||
Parent = button;
|
||||
}
|
||||
button.MouseButton1Click:connect(function()
|
||||
SoundManager:Play('ButtonPress')
|
||||
ScreenManager:CloseCurrent()
|
||||
callback()
|
||||
end)
|
||||
button.SelectionGained:connect(function()
|
||||
button.BackgroundTransparency = 0
|
||||
text.TextColor3 = GlobalSettings.TextSelectedColor
|
||||
end)
|
||||
button.SelectionLost:connect(function()
|
||||
button.BackgroundTransparency = 1
|
||||
text.TextColor3 = GlobalSettings.WhiteTextColor
|
||||
end)
|
||||
|
||||
buttons[#buttons + 1] = button
|
||||
recalcPositions()
|
||||
end
|
||||
|
||||
function this:RemoveAllItems()
|
||||
for i,button in pairs(buttons) do
|
||||
button:Destroy()
|
||||
buttons[i] = nil
|
||||
end
|
||||
end
|
||||
|
||||
function this:SetSelectedObject(indexToOpenTo)
|
||||
if #buttons > 0 then
|
||||
selectedObject = indexToOpenTo and buttons[indexToOpenTo] or buttons[1]
|
||||
else
|
||||
selectedObject = nil
|
||||
end
|
||||
end
|
||||
|
||||
function this:Show()
|
||||
modalOverlay.Parent = GuiRoot
|
||||
local tweenIn = Utility.PropertyTweener(modalOverlay, "BackgroundTransparency", 1,
|
||||
GlobalSettings.ModalBackgroundTransparency, 0.25, Utility.EaseInOutQuad, true, nil)
|
||||
container:TweenPosition(UDim2.new(1 - container.Size.X.Scale, 0, 0, 0),
|
||||
Enum.EasingDirection.InOut, Enum.EasingStyle.Quad, 0.25, true)
|
||||
SoundManager:Play('SideMenuSlideIn')
|
||||
end
|
||||
|
||||
function this:Hide()
|
||||
local tweenOut = Utility.PropertyTweener(modalOverlay, "BackgroundTransparency", 0.3, 1, 0.25, Utility.EaseInOutQuad, true,
|
||||
function()
|
||||
modalOverlay.Parent = nil
|
||||
end)
|
||||
container:TweenPosition(UDim2.new(1, 0, 0, 0), Enum.EasingDirection.InOut, Enum.EasingStyle.Sine, 0.25, true)
|
||||
end
|
||||
|
||||
function this:Focus()
|
||||
GuiService:AddSelectionParent("SideBar", container)
|
||||
if selectedObject then
|
||||
GuiService.SelectedCoreObject = selectedObject
|
||||
else
|
||||
self:SetSelectedObject(1)
|
||||
GuiService.SelectedCoreObject = selectedObject
|
||||
end
|
||||
|
||||
-- connect back button
|
||||
ContextActionService:BindCoreAction("CloseSideBar",
|
||||
function(actionName, inputState, inputObject)
|
||||
if inputState == Enum.UserInputState.End then
|
||||
ScreenManager:CloseCurrent()
|
||||
end
|
||||
end,
|
||||
false, Enum.KeyCode.ButtonB)
|
||||
end
|
||||
|
||||
function this:RemoveFocus()
|
||||
GuiService:RemoveSelectionGroup("SideBar")
|
||||
ContextActionService:UnbindCoreAction("CloseSideBar")
|
||||
selectedObject = nil
|
||||
end
|
||||
|
||||
return this
|
||||
end
|
||||
|
||||
return CreateSideBar
|
||||
@@ -0,0 +1,197 @@
|
||||
--[[
|
||||
// SignInScreen.lua
|
||||
]]
|
||||
local CoreGui = Game:GetService("CoreGui")
|
||||
local GuiRoot = CoreGui:FindFirstChild("RobloxGui")
|
||||
local Modules = GuiRoot:FindFirstChild("Modules")
|
||||
|
||||
local GuiService = game:GetService('GuiService')
|
||||
local PlatformService = nil
|
||||
pcall(function() PlatformService = game:GetService('PlatformService') end)
|
||||
local TextService = game:GetService('TextService')
|
||||
local UserInputService = game:GetService('UserInputService')
|
||||
|
||||
local AccountManager = require(Modules:FindFirstChild('AccountManager'))
|
||||
local AssetManager = require(Modules:FindFirstChild('AssetManager'))
|
||||
local Errors = require(Modules:FindFirstChild('Errors'))
|
||||
local ErrorOverlay = require(Modules:FindFirstChild('ErrorOverlay'))
|
||||
local EventHub = require(Modules:FindFirstChild('EventHub'))
|
||||
local GlobalSettings = require(Modules:FindFirstChild('GlobalSettings'))
|
||||
local LoadingWidget = require(Modules:FindFirstChild('LoadingWidget'))
|
||||
local ScreenManager = require(Modules:FindFirstChild('ScreenManager'))
|
||||
local SoundManager = require(Modules:FindFirstChild('SoundManager'))
|
||||
local Strings = require(Modules:FindFirstChild('LocalizedStrings'))
|
||||
local Utility = require(Modules:FindFirstChild('Utility'))
|
||||
|
||||
local LinkAccountScreen = require(Modules:FindFirstChild('LinkAccountScreen'))
|
||||
local SetAccountCredentialsScreen = require(Modules:FindFirstChild('SetAccountCredentialsScreen'))
|
||||
|
||||
local function createSignInScreen()
|
||||
local this = {}
|
||||
|
||||
local isFocused = false
|
||||
|
||||
local DefaultButtonColor = GlobalSettings.GreyButtonColor
|
||||
local SelectedButtonColor = GlobalSettings.GreySelectedButtonColor
|
||||
local DefaultButtonTextColor = GlobalSettings.WhiteTextColor
|
||||
local SelectedButtonTextColor = GlobalSettings.TextSelectedColor
|
||||
|
||||
-- get gamertag and set display text
|
||||
local createAccountText = Strings:LocalizedString("PlayAsPhrase")
|
||||
if PlatformService then
|
||||
local userInfo = PlatformService:GetPlatformUserInfo()
|
||||
local gamertag = userInfo["Gamertag"]
|
||||
if gamertag then
|
||||
createAccountText = string.format(createAccountText, gamertag)
|
||||
end
|
||||
end
|
||||
|
||||
local ModalOverlay = Utility.Create'Frame'
|
||||
{
|
||||
Name = "ModalOverlay";
|
||||
Size = UDim2.new(1, 0, 1, 0);
|
||||
BackgroundTransparency = GlobalSettings.ModalBackgroundTransparency;
|
||||
BackgroundColor3 = GlobalSettings.ModalBackgroundColor;
|
||||
BorderSizePixel = 0;
|
||||
ZIndex = 4;
|
||||
}
|
||||
|
||||
local Container = Utility.Create'Frame'
|
||||
{
|
||||
Name = "SignInScreen";
|
||||
Size = UDim2.new(1, 0, 1, 0);
|
||||
BackgroundTransparency = 1;
|
||||
Visible = false;
|
||||
}
|
||||
local RobloxLogo = Utility.Create'ImageLabel'
|
||||
{
|
||||
Name = "RobloxLogo";
|
||||
Size = UDim2.new(0, 594, 0, 199);
|
||||
BackgroundTransparency = 1;
|
||||
Image = 'rbxasset://textures/ui/Shell/Icons/ROBLOXSplashLogo.png';
|
||||
Parent = Container;
|
||||
}
|
||||
Utility.CalculateAnchor(RobloxLogo, UDim2.new(0.5,0,0.5,0), Utility.Enum.Anchor.Center)
|
||||
|
||||
local FadeInFrame = Utility.Create'Frame'
|
||||
{
|
||||
Name = "FadeInFrame";
|
||||
Size = UDim2.new(1, 0, 1, 0);
|
||||
BackgroundTransparency = 1;
|
||||
Visible = false;
|
||||
Parent = Container;
|
||||
}
|
||||
|
||||
local LinkAccountButton = Utility.Create'ImageButton'
|
||||
{
|
||||
Name = "LinkAccountButton";
|
||||
Size = UDim2.new(0, 200, 0, 64);
|
||||
Position = UDim2.new(0.5, -100, 1, -64 - 140);
|
||||
BackgroundTransparency = 1;
|
||||
ImageColor3 = DefaultButtonColor;
|
||||
Image = 'rbxasset://textures/ui/Shell/Buttons/Generic9ScaleButton@720.png';
|
||||
ScaleType = Enum.ScaleType.Slice;
|
||||
SliceCenter = Rect.new(Vector2.new(4, 4), Vector2.new(28, 28));
|
||||
ZIndex = 2;
|
||||
Parent = FadeInFrame;
|
||||
|
||||
SoundManager:CreateSound('MoveSelection');
|
||||
AssetManager.CreateShadow(1)
|
||||
}
|
||||
|
||||
local CreateAccountButton = LinkAccountButton:Clone()
|
||||
CreateAccountButton.Name = "CreateAccountButton"
|
||||
CreateAccountButton.Size = UDim2.new(0, 360, 0, 64);
|
||||
CreateAccountButton.Position = UDim2.new(0.5, -180, 1, LinkAccountButton.Position.Y.Offset - 64 - 44)
|
||||
CreateAccountButton.Parent = FadeInFrame
|
||||
|
||||
local LinkAccountText = Utility.Create'TextLabel'
|
||||
{
|
||||
Name = "LinkAccountText";
|
||||
Size = UDim2.new(1, 0, 1, 0);
|
||||
BackgroundTransparency = 1;
|
||||
Font = GlobalSettings.RegularFont;
|
||||
FontSize = GlobalSettings.ButtonSize;
|
||||
TextColor3 = DefaultButtonTextColor;
|
||||
Text = string.upper(Strings:LocalizedString("SignInPhrase"));
|
||||
ZIndex = 2;
|
||||
Parent = LinkAccountButton;
|
||||
}
|
||||
local CreateAccountText = LinkAccountText:Clone()
|
||||
CreateAccountText.Text = string.upper(createAccountText)
|
||||
CreateAccountText.Parent = CreateAccountButton
|
||||
local createAccountTextSize = TextService:GetTextSize(createAccountText, Utility.ConvertFontSizeEnumToInt(CreateAccountText.FontSize),
|
||||
CreateAccountText.Font, Vector2.new(0, 0))
|
||||
CreateAccountButton.Size = UDim2.new(0, createAccountTextSize.X + 64, 0, CreateAccountButton.Size.Y.Offset)
|
||||
CreateAccountButton.Position = UDim2.new(0.5, -CreateAccountButton.Size.X.Offset/2, 1, CreateAccountButton.Position.Y.Offset)
|
||||
|
||||
CreateAccountButton.SelectionGained:connect(function()
|
||||
CreateAccountButton.ImageColor3 = SelectedButtonColor
|
||||
CreateAccountText.TextColor3 = SelectedButtonTextColor
|
||||
end)
|
||||
CreateAccountButton.SelectionLost:connect(function()
|
||||
CreateAccountButton.ImageColor3 = DefaultButtonColor
|
||||
CreateAccountText.TextColor3 = DefaultButtonTextColor
|
||||
end)
|
||||
LinkAccountButton.SelectionGained:connect(function()
|
||||
LinkAccountButton.ImageColor3 = SelectedButtonColor
|
||||
LinkAccountText.TextColor3 = SelectedButtonTextColor
|
||||
end)
|
||||
LinkAccountButton.SelectionLost:connect(function()
|
||||
LinkAccountButton.ImageColor3 = DefaultButtonColor
|
||||
LinkAccountText.TextColor3 = DefaultButtonTextColor
|
||||
end)
|
||||
|
||||
local function animateOnShow()
|
||||
ScreenManager:DefaultCancelFade(this.TransitionTweens)
|
||||
FadeInFrame.Visible = true
|
||||
this.TransitionTweens = ScreenManager:FadeInSitu(FadeInFrame)
|
||||
if isFocused then
|
||||
GuiService.SelectedCoreObject = CreateAccountButton
|
||||
end
|
||||
end
|
||||
|
||||
--[[ Input ]]--
|
||||
CreateAccountButton.MouseButton1Click:connect(function()
|
||||
SoundManager:Play('ButtonPress')
|
||||
local setAccountCredentialsScreen = SetAccountCredentialsScreen(Strings:LocalizedString("SignUpTitle"),
|
||||
Strings:LocalizedString("SignUpPhrase"), Strings:LocalizedString("SignUpWord"))
|
||||
setAccountCredentialsScreen:SetParent(Container.Parent)
|
||||
ScreenManager:OpenScreen(setAccountCredentialsScreen, true)
|
||||
end)
|
||||
|
||||
LinkAccountButton.MouseButton1Click:connect(function()
|
||||
SoundManager:Play('ButtonPress')
|
||||
local linkAccountScreen = LinkAccountScreen()
|
||||
linkAccountScreen:SetParent(Container.Parent)
|
||||
ScreenManager:OpenScreen(linkAccountScreen, true)
|
||||
end)
|
||||
|
||||
--[[ Public API ]]--
|
||||
function this:SetParent(newParent)
|
||||
Container.Parent = newParent
|
||||
end
|
||||
function this:Show()
|
||||
Utility.CalculateAnchor(RobloxLogo, UDim2.new(0.5,0,0.5,0), Utility.Enum.Anchor.Center)
|
||||
Container.Visible = true
|
||||
animateOnShow()
|
||||
end
|
||||
function this:Hide()
|
||||
Container.Visible = false
|
||||
FadeInFrame.Visible = false
|
||||
end
|
||||
function this:Focus()
|
||||
isFocused = true
|
||||
GuiService:AddSelectionParent("SignInScreen", FadeInFrame)
|
||||
GuiService.SelectedCoreObject = CreateAccountButton
|
||||
end
|
||||
function this:RemoveFocus()
|
||||
isFocused = false
|
||||
GuiService:RemoveSelectionGroup("SignInScreen")
|
||||
GuiService.SelectedCoreObject = nil
|
||||
end
|
||||
|
||||
return this
|
||||
end
|
||||
|
||||
return createSignInScreen
|
||||
@@ -0,0 +1,244 @@
|
||||
--[[
|
||||
// SocialPane.lua
|
||||
]]
|
||||
local CoreGui = Game:GetService("CoreGui")
|
||||
local GuiRoot = CoreGui:FindFirstChild("RobloxGui")
|
||||
local Modules = GuiRoot:FindFirstChild("Modules")
|
||||
local GuiService = game:GetService('GuiService')
|
||||
|
||||
local AssetManager = require(Modules:FindFirstChild('AssetManager'))
|
||||
local EventHub = require(Modules:FindFirstChild('EventHub'))
|
||||
local Utility = require(Modules:FindFirstChild('Utility'))
|
||||
local FriendsData = require(Modules:FindFirstChild('FriendsData'))
|
||||
local FriendsView = require(Modules:FindFirstChild('FriendsView'))
|
||||
local GlobalSettings = require(Modules:FindFirstChild('GlobalSettings'))
|
||||
local ScrollingGridModule = require(Modules:FindFirstChild('ScrollingGrid'))
|
||||
local SocialScreenModule = require(Modules:FindFirstChild('SocialScreen'))
|
||||
local ScreenManager = require(Modules:FindFirstChild('ScreenManager'))
|
||||
local Strings = require(Modules:FindFirstChild('LocalizedStrings'))
|
||||
local LoadingWidget = require(Modules:FindFirstChild('LoadingWidget'))
|
||||
local SoundManager = require(Modules:FindFirstChild('SoundManager'))
|
||||
|
||||
--[[ Constants ]]--
|
||||
local GRID_SIZE = UDim2.new(1, 0, 1, 0)
|
||||
local GRID_ROWS = 5
|
||||
local GRID_COLUMNS = 4
|
||||
local GRID_DIRECTION = 'Horizontal'
|
||||
|
||||
local function CreateSocialPane(parent)
|
||||
local this = {}
|
||||
|
||||
local BREAK_COLOR = Color3.new(78/255, 78/255, 78/255)
|
||||
local DISPLAY_FRIEND_COUNT = 15
|
||||
local SIDE_BAR_ITEMS = {
|
||||
string.upper(Strings:LocalizedString("JoinGameWord"));
|
||||
string.upper(Strings:LocalizedString("ViewGameDetailsWord"));
|
||||
string.upper(Strings:LocalizedString("InviteToPartyWord"));
|
||||
string.upper(Strings:LocalizedString("ViewGamerCardWord"));
|
||||
}
|
||||
|
||||
local moreFriendsScreen = nil
|
||||
local myFriendsView = nil
|
||||
local isPaneFocused = false
|
||||
local defaultSelectionObject = nil
|
||||
|
||||
local noSelectionObject = Utility.Create'ImageLabel'
|
||||
{
|
||||
BackgroundTransparency = 1;
|
||||
}
|
||||
|
||||
local SocialPaneContainer = Utility.Create'Frame'
|
||||
{
|
||||
Name = 'SocialPane';
|
||||
Size = UDim2.new(1, 0, 1, 0);
|
||||
BackgroundTransparency = 1;
|
||||
Visible = false;
|
||||
SelectionImageObject = noSelectionObject;
|
||||
Parent = parent;
|
||||
}
|
||||
--[[ Online Friends ]]--
|
||||
local onlineFriendsTitle = Utility.Create'TextLabel'
|
||||
{
|
||||
Name = "OnlineFriendsTitle";
|
||||
Size = UDim2.new(0, 0, 0, 33);
|
||||
Position = UDim2.new();
|
||||
BackgroundTransparency = 1;
|
||||
Font = GlobalSettings.RegularFont;
|
||||
FontSize = GlobalSettings.SubHeaderSize;
|
||||
TextColor3 = GlobalSettings.WhiteTextColor;
|
||||
TextXAlignment = Enum.TextXAlignment.Left;
|
||||
Text = string.upper(Strings:LocalizedString("OnlineFriendsWords"));
|
||||
Visible = false;
|
||||
Parent = SocialPaneContainer;
|
||||
}
|
||||
local onlineFriendsContainer = Utility.Create'Frame'
|
||||
{
|
||||
Name = "OnlineFriendsContainer";
|
||||
Size = UDim2.new(0, 1438, 0, 610);
|
||||
Position = UDim2.new(0, 0, 0, onlineFriendsTitle.Size.Y.Offset);
|
||||
BackgroundTransparency = 1;
|
||||
Parent = SocialPaneContainer;
|
||||
}
|
||||
local moreFriendsButton = Utility.Create'ImageButton'
|
||||
{
|
||||
Name = "MoreButton";
|
||||
BackgroundTransparency = 1;
|
||||
|
||||
SoundManager:CreateSound('MoveSelection');
|
||||
}
|
||||
AssetManager.LocalImage(moreFriendsButton,
|
||||
'rbxasset://textures/ui/Shell/Buttons/MoreButton', {['720'] = UDim2.new(0,67,0,28); ['1080'] = UDim2.new(0,100,0,42);})
|
||||
moreFriendsButton.Position = UDim2.new(1, -moreFriendsButton.Size.X.Offset, 1, 12)
|
||||
|
||||
local function updateMoreImage(isSelected)
|
||||
local uri = isSelected and 'rbxasset://textures/ui/Shell/Buttons/MoreButtonSelected'
|
||||
or 'rbxasset://textures/ui/Shell/Buttons/MoreButton'
|
||||
AssetManager.LocalImage(moreFriendsButton, uri, {['720'] = UDim2.new(0,72,0,33); ['1080'] = UDim2.new(0,108,0,50);})
|
||||
end
|
||||
moreFriendsButton.SelectionGained:connect(function()
|
||||
updateMoreImage(true)
|
||||
end)
|
||||
moreFriendsButton.SelectionLost:connect(function()
|
||||
updateMoreImage(false)
|
||||
end)
|
||||
|
||||
local friendsScrollingGrid = ScrollingGridModule()
|
||||
friendsScrollingGrid:SetSize(UDim2.new(1, 0, 1, 0))
|
||||
friendsScrollingGrid:SetCellSize(Vector2.new(446, 114))
|
||||
friendsScrollingGrid:SetSpacing(Vector2.new(50, 10))
|
||||
friendsScrollingGrid:SetScrollDirection(friendsScrollingGrid.Enum.ScrollDirection.Horizontal)
|
||||
friendsScrollingGrid:SetPosition(UDim2.new(0, 0, 0, 0))
|
||||
local friendsScrollingGridContainer = friendsScrollingGrid:GetGuiObject()
|
||||
friendsScrollingGridContainer.Visible = false
|
||||
friendsScrollingGrid:SetParent(onlineFriendsContainer)
|
||||
|
||||
--[[ No Friends Online ]]--
|
||||
local noFriendsIcon = Utility.Create'ImageLabel'
|
||||
{
|
||||
Name = "noFriendsIcon";
|
||||
Size = UDim2.new(0, 296, 0, 259);
|
||||
Position = UDim2.new(0.5, -296/2, 0, 100);
|
||||
BackgroundTransparency = 1;
|
||||
Image = 'rbxasset://textures/ui/Shell/Icons/FriendsIcon@1080.png';
|
||||
Visible = false;
|
||||
Parent = SocialPaneContainer;
|
||||
}
|
||||
local noFriendsText = Utility.Create'TextLabel'
|
||||
{
|
||||
Name = "NoFriendsText";
|
||||
Size = UDim2.new(0, 500, 0, 72);
|
||||
BackgroundTransparency = 1;
|
||||
Font = GlobalSettings.RegularFont;
|
||||
FontSize = GlobalSettings.ButtonSize;
|
||||
TextColor3 = GlobalSettings.WhiteTextColor;
|
||||
Text = Strings:LocalizedString("NoFriendsPhrase");
|
||||
TextYAlignment = Enum.TextYAlignment.Top;
|
||||
TextWrapped = true;
|
||||
Visible = false;
|
||||
Parent = SocialPaneContainer;
|
||||
}
|
||||
noFriendsText.Position = UDim2.new(0.5, -noFriendsText.Size.X.Offset/2, 0,
|
||||
noFriendsIcon.Position.Y.Offset + noFriendsIcon.Size.Y.Offset + 32)
|
||||
|
||||
--[[ Content Functions ]]--
|
||||
local function setPaneContentVisible(hasOnlineFriends)
|
||||
noFriendsIcon.Visible = not hasOnlineFriends
|
||||
noFriendsText.Visible = not hasOnlineFriends
|
||||
--
|
||||
onlineFriendsTitle.Visible = hasOnlineFriends
|
||||
--
|
||||
defaultSelectionObject = hasOnlineFriends and myFriendsView:GetDefaultFocusItem() or nil
|
||||
end
|
||||
|
||||
local function onFriendsUpdated(friendCount)
|
||||
local hasOnlineFriends = friendCount > 0
|
||||
setPaneContentVisible(hasOnlineFriends)
|
||||
if hasOnlineFriends then
|
||||
if friendCount > DISPLAY_FRIEND_COUNT and not moreFriendsButton.Parent then
|
||||
moreFriendsButton.Parent = onlineFriendsContainer
|
||||
elseif friendCount < DISPLAY_FRIEND_COUNT and moreFriendsButton.Parent then
|
||||
moreFriendsButton.Parent = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function loadFriendsView()
|
||||
local friendsData = FriendsData.GetOnlineFriendsAsync()
|
||||
local displayCount = math.min(#friendsData, DISPLAY_FRIEND_COUNT)
|
||||
myFriendsView = FriendsView(friendsScrollingGrid, friendsData, DISPLAY_FRIEND_COUNT, onFriendsUpdated)
|
||||
onFriendsUpdated(#friendsData)
|
||||
|
||||
if isPaneFocused then
|
||||
GuiService.SelectedCoreObject = defaultSelectionObject
|
||||
end
|
||||
|
||||
moreFriendsScreen = SocialScreenModule(Strings:LocalizedString("OnlineFriendsWords"), Strings:LocalizedString("FriendsWord"))
|
||||
moreFriendsButton.MouseButton1Click:connect(function()
|
||||
EventHub:dispatchEvent(EventHub.Notifications["OpenSocialScreen"], moreFriendsScreen);
|
||||
end)
|
||||
end
|
||||
|
||||
local loader = LoadingWidget(
|
||||
{ Parent = SocialPaneContainer }, { loadFriendsView }
|
||||
)
|
||||
|
||||
spawn(function()
|
||||
loader:AwaitFinished()
|
||||
loader:Cleanup()
|
||||
friendsScrollingGridContainer.Visible = true
|
||||
end)
|
||||
|
||||
function this:GetName()
|
||||
return Strings:LocalizedString('FriendsWord')
|
||||
end
|
||||
|
||||
function this:IsFocused()
|
||||
return isPaneFocused
|
||||
end
|
||||
|
||||
--[[ Public API ]]--
|
||||
function this:Show()
|
||||
SocialPaneContainer.Visible = true
|
||||
self.TransitionTweens = ScreenManager:DefaultFadeIn(SocialPaneContainer)
|
||||
ScreenManager:PlayDefaultOpenSound()
|
||||
end
|
||||
|
||||
function this:Hide()
|
||||
SocialPaneContainer.Visible = false
|
||||
ScreenManager:DefaultCancelFade(self.TransitionTweens)
|
||||
self.TransitionTweens = nil
|
||||
end
|
||||
|
||||
function this:Focus()
|
||||
-- TODO: Hook in the hidden selection after figuring how how to know how
|
||||
-- panes take focus (ie, bumper, tab, etc)
|
||||
isPaneFocused = true
|
||||
if defaultSelectionObject then
|
||||
GuiService.SelectedCoreObject = defaultSelectionObject
|
||||
end
|
||||
end
|
||||
|
||||
function this:RemoveFocus()
|
||||
isPaneFocused = false
|
||||
local selectedObject = GuiService.SelectedCoreObject
|
||||
if selectedObject and selectedObject:IsDescendantOf(SocialPaneContainer) then
|
||||
GuiService.SelectedCoreObject = nil
|
||||
end
|
||||
end
|
||||
|
||||
function this:SetPosition(newPosition)
|
||||
SocialPaneContainer.Position = newPosition
|
||||
end
|
||||
|
||||
function this:SetParent(newParent)
|
||||
SocialPaneContainer.Parent = newParent
|
||||
end
|
||||
|
||||
function this:IsAncestorOf(object)
|
||||
return SocialPaneContainer and SocialPaneContainer:IsAncestorOf(object)
|
||||
end
|
||||
|
||||
return this
|
||||
end
|
||||
|
||||
return CreateSocialPane
|
||||
@@ -0,0 +1,152 @@
|
||||
--[[
|
||||
// SocialMorePane.lua
|
||||
|
||||
// Shows a full grid view of a social view. Only shown if social items for a given
|
||||
// view are above a threshold set by SocialPane.lua
|
||||
// User by SocialPane.lua
|
||||
|
||||
// TODO:
|
||||
Bug with TabDock where its removing focus of the last tab when re-entering the tab dock
|
||||
]]
|
||||
local CoreGui = Game:GetService("CoreGui")
|
||||
local GuiRoot = CoreGui:FindFirstChild("RobloxGui")
|
||||
local Modules = GuiRoot:FindFirstChild("Modules")
|
||||
local ContextActionService = game:GetService("ContextActionService")
|
||||
local GuiService = game:GetService("GuiService")
|
||||
|
||||
local AssetManager = require(Modules:FindFirstChild('AssetManager'))
|
||||
local EventHub = require(Modules:FindFirstChild('EventHub'))
|
||||
local Utility = require(Modules:FindFirstChild('Utility'))
|
||||
local GlobalSettings = require(Modules:FindFirstChild('GlobalSettings'))
|
||||
local ScrollingGridModule = require(Modules:FindFirstChild('ScrollingGrid'))
|
||||
local ScreenManager = require(Modules:FindFirstChild('ScreenManager'))
|
||||
local Strings = require(Modules:FindFirstChild('LocalizedStrings'))
|
||||
local FriendsData = require(Modules:FindFirstChild('FriendsData'))
|
||||
local FriendsView = require(Modules:FindFirstChild('FriendsView'))
|
||||
|
||||
local createSocialScreen = function(currentScreenTitle, previousScreenName)
|
||||
local this = {}
|
||||
|
||||
local mySocialView = nil
|
||||
|
||||
local container = Utility.Create'Frame'
|
||||
{
|
||||
Name = "SocialContainer";
|
||||
Size = UDim2.new(1, 0, 1, 0);
|
||||
BackgroundTransparency = 1;
|
||||
Visible = false;
|
||||
}
|
||||
local backLabel = Utility.Create'ImageLabel'
|
||||
{
|
||||
-- PLACE HOLDER
|
||||
Name = "BackLabel";
|
||||
Position = UDim2.new(0, 0, 0, 0);
|
||||
BackgroundTransparency = 1;
|
||||
ZIndex = 2;
|
||||
Parent = container;
|
||||
}
|
||||
AssetManager.LocalImage(backLabel,
|
||||
'rbxasset://textures/ui/Shell/Icons/BackIcon', {['720'] = UDim2.new(0,32,0,32); ['1080'] = UDim2.new(0,48,0,48);})
|
||||
|
||||
-- Right now previous screen is always Friends, but we're still using previousScreenName in case
|
||||
-- recently played with ever makes it in
|
||||
local backText = Utility.Create'TextLabel'
|
||||
{
|
||||
Name = "BackText";
|
||||
Size = UDim2.new(0, 0, 0, backLabel.Size.Y.Offset);
|
||||
Position = UDim2.new(0, backLabel.Size.X.Offset + 8, 0, 0);
|
||||
BackgroundTransparency = 1;
|
||||
Font = GlobalSettings.RegularFont;
|
||||
FontSize = GlobalSettings.ButtonSize;
|
||||
TextXAlignment = Enum.TextXAlignment.Left;
|
||||
TextColor3 = GlobalSettings.WhiteTextColor;
|
||||
Text = string.upper(previousScreenName);
|
||||
Parent = container;
|
||||
}
|
||||
local titleLabel = Utility.Create'TextLabel'
|
||||
{
|
||||
Name = "TitleLabel";
|
||||
Size = UDim2.new(0, 0, 0, 35);
|
||||
Position = UDim2.new(0, 16, 0, backLabel.Size.Y.Offset + 74);
|
||||
BackgroundTransparency = 1;
|
||||
Text = string.upper(currentScreenTitle);
|
||||
TextColor3 = GlobalSettings.WhiteTextColor;
|
||||
TextXAlignment = Enum.TextXAlignment.Left;
|
||||
Font = GlobalSettings.LightFont;
|
||||
FontSize = GlobalSettings.HeaderSize;
|
||||
Parent = container;
|
||||
}
|
||||
|
||||
local socialScrollingGrid = ScrollingGridModule()
|
||||
socialScrollingGrid:SetSize(UDim2.new(0, 1438, 0, 610))
|
||||
socialScrollingGrid:SetCellSize(Vector2.new(446, 114))
|
||||
socialScrollingGrid:SetSpacing(Vector2.new(50, 10))
|
||||
socialScrollingGrid:SetScrollDirection(socialScrollingGrid.Enum.ScrollDirection.Horizontal)
|
||||
socialScrollingGrid:SetPosition(UDim2.new(0, 0, 0, titleLabel.Position.Y.Offset + titleLabel.Size.Y.Offset + 90))
|
||||
socialScrollingGrid:SetClipping(false)
|
||||
socialScrollingGrid:SetParent(container)
|
||||
|
||||
--[[ Set Images ]]--
|
||||
local function setSocialView()
|
||||
print('set the social screen data')
|
||||
local friendsData = FriendsData.GetOnlineFriendsAsync()
|
||||
mySocialView = FriendsView(socialScrollingGrid, friendsData, nil, nil)
|
||||
end
|
||||
|
||||
--[[ Public API ]]--
|
||||
function this:Show()
|
||||
container.Visible = true
|
||||
self.TransitionTweens = ScreenManager:DefaultFadeIn(container)
|
||||
ScreenManager:PlayDefaultOpenSound()
|
||||
end
|
||||
|
||||
function this:Hide()
|
||||
container.Visible = false
|
||||
container.Parent = nil
|
||||
ScreenManager:DefaultCancelFade(self.TransitionTweens)
|
||||
self.TransitionTweens = nil
|
||||
end
|
||||
|
||||
function this:Focus()
|
||||
if self.SavedSelectedObject and self.SavedSelectedObject:IsDescendantOf(container) then
|
||||
GuiService.SelectedCoreObject = self.SavedSelectedObject
|
||||
else
|
||||
if mySocialView then
|
||||
GuiService.SelectedCoreObject = mySocialView:GetDefaultFocusItem()
|
||||
end
|
||||
end
|
||||
--
|
||||
ContextActionService:BindCoreAction("ReturnFromSocialScreen",
|
||||
function(actionName, inputState, inputObject)
|
||||
if inputState == Enum.UserInputState.End then
|
||||
ScreenManager:CloseCurrent()
|
||||
end
|
||||
end,
|
||||
false, Enum.KeyCode.ButtonB)
|
||||
end
|
||||
|
||||
function this:RemoveFocus()
|
||||
local selectedObject = GuiService.SelectedCoreObject
|
||||
if selectedObject and selectedObject:IsDescendantOf(container) then
|
||||
self.SavedSelectedObject = selectedObject
|
||||
GuiService.SelectedCoreObject = nil
|
||||
else
|
||||
self.SavedSelectedObject = nil
|
||||
end
|
||||
ContextActionService:UnbindCoreAction("ReturnFromSocialScreen")
|
||||
end
|
||||
|
||||
function this:SetPosition(newPosition)
|
||||
container.Position = newPosition
|
||||
end
|
||||
|
||||
function this:SetParent(newParent)
|
||||
container.Parent = newParent
|
||||
end
|
||||
|
||||
setSocialView()
|
||||
|
||||
return this
|
||||
end
|
||||
|
||||
return createSocialScreen
|
||||
@@ -0,0 +1,202 @@
|
||||
--[[
|
||||
// SortCarousel.lua
|
||||
// Creates a sort carousel with data for the sort
|
||||
]]
|
||||
local CoreGui = Game:GetService("CoreGui")
|
||||
local GuiRoot = CoreGui:FindFirstChild("RobloxGui")
|
||||
local Modules = GuiRoot:FindFirstChild("Modules")
|
||||
|
||||
local EventHub = require(Modules:FindFirstChild('EventHub'))
|
||||
local GameDataModule = require(Modules:FindFirstChild('GameData'))
|
||||
local ImageSlider = require(Modules:FindFirstChild('ImageSlider'))
|
||||
local Utility = require(Modules:FindFirstChild('Utility'))
|
||||
local ThumbnailLoader = require(Modules:FindFirstChild('ThumbnailLoader'))
|
||||
local GlobalSettings = require(Modules:FindFirstChild('GlobalSettings'))
|
||||
local LoadingWidget = require(Modules:FindFirstChild('LoadingWidget'))
|
||||
local SoundManager = require(Modules:FindFirstChild('SoundManager'))
|
||||
local AssetManager = require(Modules:FindFirstChild('AssetManager'))
|
||||
|
||||
local createSortCarousel = function(size, position, gameCollection, parent)
|
||||
local this = {}
|
||||
|
||||
--[[ Constants ]]--
|
||||
local LOAD_COUNT = 20
|
||||
local IMAGE_COUNT = 9
|
||||
|
||||
local carousel = ImageSlider(size, position)
|
||||
carousel:SetPadding(18)
|
||||
carousel:SetParent(parent)
|
||||
|
||||
--[[ Variables ]]--
|
||||
local images = {}
|
||||
local gameData = {}
|
||||
local lastSelectedIndex = nil
|
||||
local lastLoadedCount = 0
|
||||
|
||||
--[[ Event ]]--
|
||||
this.OnNewGameSelected = Utility.Signal()
|
||||
-- we need to fetch for description/isFavorited, so we send a late signal to update those
|
||||
this.OnNewGameSelectedLate = Utility.Signal()
|
||||
|
||||
--[[ Private Functions ]]--
|
||||
local function insertSortData(page)
|
||||
if not page then
|
||||
print("Error: SortCarousel failed to get a valid page for insertSortData.")
|
||||
return
|
||||
end
|
||||
-- TODO: Get new square icons
|
||||
local ids = page:GetPagePlaceIds()
|
||||
local names = page:GetPagePlaceNames()
|
||||
local voteData = page:GetPageVoteData()
|
||||
local iconIds = page:GetPageIconIds()
|
||||
local creatorNames = page:GetCreatorNames()
|
||||
--
|
||||
for i = 1, #page.Data do
|
||||
local entry = {
|
||||
Name = names[i];
|
||||
PlaceId = ids[i];
|
||||
IconId = iconIds[i];
|
||||
VoteData = voteData[i];
|
||||
CreatorName = creatorNames[i];
|
||||
-- Description/IsFavorited needs to be queried for each game, do it only when needed
|
||||
-- we'll also cache gameData
|
||||
Description = nil;
|
||||
IsFavorited = nil;
|
||||
GameData = nil;
|
||||
}
|
||||
gameData[#gameData + 1] = entry
|
||||
end
|
||||
lastLoadedCount = #gameData
|
||||
end
|
||||
|
||||
local function createCarouselImages()
|
||||
local thumbSize = ThumbnailLoader.Sizes.Medium
|
||||
local assetType = ThumbnailLoader.AssetType.Icon
|
||||
for i = 1, IMAGE_COUNT do
|
||||
local data = gameData[i]
|
||||
if data then
|
||||
local image = Utility.Create'ImageButton'
|
||||
{
|
||||
Name = "GameImage";
|
||||
BorderSizePixel = 0;
|
||||
BackgroundColor3 = GlobalSettings.GreyButtonColor;
|
||||
ZIndex = 2;
|
||||
|
||||
SoundManager:CreateSound('MoveSelection');
|
||||
AssetManager.CreateShadow(1);
|
||||
}
|
||||
local thumbLoader = ThumbnailLoader:Create(image, data.IconId, thumbSize, assetType)
|
||||
spawn(function()
|
||||
if not thumbLoader:LoadAsync() then
|
||||
-- TODO
|
||||
end
|
||||
end)
|
||||
table.insert(images, image)
|
||||
carousel:AddItem(image)
|
||||
|
||||
-- connect button press based on current selected index mapped to gameData
|
||||
image.MouseButton1Click:connect(function()
|
||||
if lastSelectedIndex and gameData[lastSelectedIndex] then
|
||||
local currentData = gameData[lastSelectedIndex]
|
||||
EventHub:dispatchEvent(EventHub.Notifications["OpenGameDetail"], currentData.PlaceId,
|
||||
currentData.Name, currentData.IconId, currentData.GameData)
|
||||
end
|
||||
end)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--[[ Event Connections ]]--
|
||||
local nextLoadIndex = 0
|
||||
local lastSelectedObject = nil
|
||||
local onNewFocusCn = nil
|
||||
|
||||
local function onNewFocusItem(newIndex, isRight, selectedObject)
|
||||
lastSelectedObject = selectedObject
|
||||
lastSelectedIndex = newIndex
|
||||
--
|
||||
if gameData[newIndex] then
|
||||
local currentData = gameData[newIndex]
|
||||
if this then
|
||||
this.OnNewGameSelected:fire(currentData)
|
||||
end
|
||||
|
||||
-- description/isFavorited needs to fetch, so send a late update
|
||||
spawn(function()
|
||||
if not currentData.GameData then
|
||||
local gameData = GameDataModule:GetGameDataAsync(currentData.PlaceId)
|
||||
if gameData then
|
||||
currentData.GameData = gameData
|
||||
currentData.Description = gameData:GetDescription()
|
||||
currentData.IsFavorited = gameData:GetIsFavoritedByUser()
|
||||
end
|
||||
end
|
||||
if this and selectedObject == lastSelectedObject then
|
||||
this.OnNewGameSelectedLate:fire(currentData.Description, currentData.IsFavorited)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
-- load more data
|
||||
if isRight and #gameData > IMAGE_COUNT and newIndex == nextLoadIndex then
|
||||
nextLoadIndex = nextLoadIndex + LOAD_COUNT
|
||||
local page = gameCollection:GetSortAsync(lastLoadedCount, LOAD_COUNT)
|
||||
insertSortData(page)
|
||||
carousel:SetMaxItems(#gameData)
|
||||
end
|
||||
end
|
||||
|
||||
onNewFocusCn = carousel.OnNewFocusItem:connect(onNewFocusItem)
|
||||
--[[ Public API ]]--
|
||||
function this:SetParent(newParent)
|
||||
carousel:SetParent(newParent)
|
||||
end
|
||||
|
||||
function this:GetCurrentSelectedGameData()
|
||||
if gameData[lastSelectedIndex] then
|
||||
return gameData[lastSelectedIndex]
|
||||
end
|
||||
end
|
||||
|
||||
function this:GetItemAt(index)
|
||||
return images[index]
|
||||
end
|
||||
|
||||
function this:Destroy()
|
||||
carousel:Destroy()
|
||||
if onNewFocusCn then
|
||||
onNewFocusCn:disconnect()
|
||||
onNewFocusCn = nil
|
||||
end
|
||||
spawn(function()
|
||||
gameData = nil
|
||||
this = nil
|
||||
end)
|
||||
end
|
||||
|
||||
function this:LoadSortAsync()
|
||||
local loader = LoadingWidget(
|
||||
{ Parent = parent },{
|
||||
function()
|
||||
local page = gameCollection:GetSortAsync(0, LOAD_COUNT)
|
||||
insertSortData(page)
|
||||
createCarouselImages()
|
||||
if carousel then
|
||||
carousel:SetDataTable(gameData)
|
||||
carousel:SetMaxItems(#gameData)
|
||||
carousel:SetFocusPosition(1)
|
||||
onNewFocusItem(1, nil, images[1])
|
||||
nextLoadIndex = LOAD_COUNT / 2
|
||||
end
|
||||
end
|
||||
})
|
||||
|
||||
loader:AwaitFinished()
|
||||
loader:Cleanup()
|
||||
loader = nil
|
||||
end
|
||||
|
||||
return this
|
||||
end
|
||||
|
||||
return createSortCarousel
|
||||
@@ -0,0 +1,230 @@
|
||||
--[[
|
||||
// SortData.lua
|
||||
// API for Game Sorts
|
||||
]]
|
||||
local CoreGui = Game:GetService("CoreGui")
|
||||
local GuiRoot = CoreGui:FindFirstChild("RobloxGui")
|
||||
local Modules = GuiRoot:FindFirstChild("Modules")
|
||||
|
||||
local GameData = require(Modules:FindFirstChild('GameData'))
|
||||
|
||||
local Http = require(Modules:FindFirstChild('Http'))
|
||||
local Strings = require(Modules:FindFirstChild('LocalizedStrings'))
|
||||
local Utility = require(Modules:FindFirstChild('Utility'))
|
||||
|
||||
local SortData = {}
|
||||
|
||||
-- this is a temp cache for play my place. We need to be able to check if a user is joining
|
||||
-- a featured game or not in order to set their session privacy.
|
||||
local featuredSortCache = nil
|
||||
local FEATURED_SORT_ID = 3
|
||||
local REFRESH_TIME = 300
|
||||
local lastTimeUpdated = 0
|
||||
local isFetching = false
|
||||
local function getFeaturedCacheAsync()
|
||||
while isFetching do
|
||||
wait()
|
||||
end
|
||||
|
||||
if featuredSortCache and (tick() - lastTimeUpdated < REFRESH_TIME) then
|
||||
return featuredSortCache
|
||||
end
|
||||
|
||||
isFetching = true
|
||||
local newFeaturedSortCache = {}
|
||||
local lastIndexLoaded = 0
|
||||
local PAGE_SIZE = 100
|
||||
local sort = SortData.GetSort(FEATURED_SORT_ID)
|
||||
local getFeaturedSortSuccess = true
|
||||
|
||||
local isLastPage = false
|
||||
repeat
|
||||
local currentPage = nil
|
||||
local function tryGetSortAsync()
|
||||
currentPage = sort:GetPageAsync(lastIndexLoaded, PAGE_SIZE)
|
||||
if currentPage then
|
||||
return true
|
||||
end
|
||||
end
|
||||
Utility.ExponentialRepeat(
|
||||
function() return currentPage == nil end, tryGetSortAsync, 3)
|
||||
|
||||
if currentPage then
|
||||
if currentPage.Count > 0 then
|
||||
local placeIds = currentPage:GetPagePlaceIds()
|
||||
for i = 1, #placeIds do
|
||||
newFeaturedSortCache[placeIds[i]] = true
|
||||
end
|
||||
lastIndexLoaded = lastIndexLoaded + currentPage.Count
|
||||
end
|
||||
isLastPage = currentPage.Count < PAGE_SIZE
|
||||
else
|
||||
getFeaturedSortSuccess = false
|
||||
end
|
||||
until not getFeaturedSortSuccess == false or isLastPage == true
|
||||
|
||||
if getFeaturedSortSuccess == true then
|
||||
featuredSortCache = newFeaturedSortCache
|
||||
end
|
||||
|
||||
lastTimeUpdated = tick()
|
||||
isFetching = false
|
||||
|
||||
return featuredSortCache
|
||||
end
|
||||
|
||||
function SortData:IsFeaturedGameAsync(placeId)
|
||||
local featuredSort = getFeaturedCacheAsync()
|
||||
if featuredSort then
|
||||
return featuredSort[placeId]
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
local function initPage(data)
|
||||
local page = {}
|
||||
|
||||
page.Data = data
|
||||
page.Count = #data
|
||||
|
||||
function page:GetPagePlaceIds()
|
||||
local ids = {}
|
||||
|
||||
for i = 1, #data do
|
||||
ids[i] = data[i]["PlaceID"]
|
||||
end
|
||||
|
||||
return ids
|
||||
end
|
||||
|
||||
function page:GetPagePlaceNames()
|
||||
local names = {}
|
||||
|
||||
for i = 1, #data do
|
||||
names[i] = GameData:GetFilteredGameName(data[i]["Name"], data[i]["CreatorName"])
|
||||
end
|
||||
|
||||
return names
|
||||
end
|
||||
|
||||
function page:GetCreatorNames()
|
||||
local names = {}
|
||||
|
||||
for i = 1, #data do
|
||||
names[i] = data[i]["CreatorName"]
|
||||
end
|
||||
|
||||
return names
|
||||
end
|
||||
|
||||
function page:GetPageIconIds()
|
||||
local iconIds = {}
|
||||
|
||||
for i = 1, #data do
|
||||
local id = data[i]["ImageId"]
|
||||
iconIds[i] = id
|
||||
end
|
||||
|
||||
return iconIds
|
||||
end
|
||||
|
||||
function page:GetPageVoteData()
|
||||
local voteData = {}
|
||||
|
||||
for i = 1, #data do
|
||||
local vote = {
|
||||
UpVotes = data[i]["TotalUpVotes"];
|
||||
DownVotes = data[i]["TotalDownVotes"];
|
||||
}
|
||||
voteData[i] = vote
|
||||
end
|
||||
|
||||
return voteData
|
||||
end
|
||||
|
||||
function page:GetPageCreatorUserIds()
|
||||
local creatorIds = {}
|
||||
|
||||
for i = 1, #data do
|
||||
local id = data[i]["CreatorID"]
|
||||
table.insert(creatorIds, id)
|
||||
end
|
||||
|
||||
return creatorIds
|
||||
end
|
||||
|
||||
return page
|
||||
end
|
||||
|
||||
-- returns class style table for sorts
|
||||
function SortData.GetSortCategoriesAsync()
|
||||
local result = Http.GetGameSortsAsync()
|
||||
if not result then
|
||||
-- TODO: Error codes
|
||||
return
|
||||
end
|
||||
--
|
||||
local sorts = {}
|
||||
|
||||
for i = 1, #result do
|
||||
local sort = {
|
||||
Id = result[i]["Id"];
|
||||
Name = result[i]["Name"];
|
||||
}
|
||||
table.insert(sorts, sort)
|
||||
end
|
||||
|
||||
return sorts
|
||||
end
|
||||
|
||||
local function createSort(sort, sortId, httpFunc)
|
||||
function sort:GetPageAsync(startIndex, pageSize, timeFilter)
|
||||
local result = httpFunc(startIndex, pageSize, sortId, timeFilter)
|
||||
if not result then
|
||||
return nil
|
||||
end
|
||||
|
||||
return initPage(result)
|
||||
end
|
||||
end
|
||||
|
||||
function SortData.GetSort(sortId)
|
||||
local sort = {}
|
||||
|
||||
local httpFunc = Http.GetSortAsync
|
||||
createSort(sort, sortId, httpFunc)
|
||||
|
||||
return sort
|
||||
end
|
||||
|
||||
function SortData.GetUserFavorites()
|
||||
local sort = {}
|
||||
local sortId = "MyFavorite"
|
||||
|
||||
local httpFunc = Http.GetUserFavoritesAsync
|
||||
createSort(sort, sortId, httpFunc)
|
||||
|
||||
return sort
|
||||
end
|
||||
|
||||
function SortData.GetUserRecent()
|
||||
local sort = {}
|
||||
local sortId = "MyRecent"
|
||||
|
||||
local httpFunc = Http.GetUserRecentAsync
|
||||
createSort(sort, sortId, httpFunc)
|
||||
|
||||
return sort
|
||||
end
|
||||
|
||||
function SortData.GetUserPlaces(userId)
|
||||
local sort = {}
|
||||
|
||||
local httpFunc = Http.GetUserPlacesAsync
|
||||
createSort(sort, userId, httpFunc)
|
||||
|
||||
return sort
|
||||
end
|
||||
|
||||
return SortData
|
||||
@@ -0,0 +1,206 @@
|
||||
-- local CoreGui = Game:GetService("CoreGui")
|
||||
|
||||
-- local GuiRoot = CoreGui:FindFirstChild("RobloxGui")
|
||||
|
||||
local GuiService = game:GetService('GuiService')
|
||||
local SoundService = Game:GetService("SoundService")
|
||||
local runService = game:GetService("RunService")
|
||||
|
||||
local CoreGui = Game:GetService("CoreGui")
|
||||
local GuiRoot = CoreGui:FindFirstChild("RobloxGui")
|
||||
local Modules = GuiRoot:FindFirstChild("Modules")
|
||||
|
||||
local Utility = require(Modules:FindFirstChild('Utility'))
|
||||
|
||||
local BASE_SOUND_URL = 'rbxasset://sounds/ui/Shell/'
|
||||
|
||||
local SOUNDS =
|
||||
{
|
||||
--[[ BGM ]]--
|
||||
['BackgroundLoop'] = 'RobloxMusic.ogg';
|
||||
|
||||
|
||||
--[[ UI Sounds ]]--
|
||||
['Error'] = 'Error.mp3';
|
||||
['ButtonPress'] = 'ButtonPress.mp3';
|
||||
['MoveSelection'] = 'MoveSelection.mp3';
|
||||
['OverlayOpen'] = 'OverlayOpen.mp3';
|
||||
['PopUp'] = 'PopUp.mp3';
|
||||
['PurchaseSuccess'] = 'PurchaseSuccess.mp3';
|
||||
['ScreenChange'] = 'ScreenChange.mp3';
|
||||
['SideMenuSlideIn'] = 'SideMenuSlideIn.mp3';
|
||||
}
|
||||
|
||||
local SoundQueue = {}
|
||||
|
||||
local function EaseOutCirc(currentTime, startValue, deltaValue, duration)
|
||||
currentTime = currentTime / duration;
|
||||
currentTime = currentTime - 1;
|
||||
return deltaValue * math.sqrt(1 - currentTime*currentTime) + startValue;
|
||||
end
|
||||
|
||||
local function IsGameRunning()
|
||||
if not UserSettings().GameSettings:InStudioMode() then
|
||||
return true
|
||||
end
|
||||
return runService:IsRunning()
|
||||
end
|
||||
|
||||
GetSoundManager = function()
|
||||
local this = {}
|
||||
|
||||
local rawVolumes = {}
|
||||
|
||||
local function FindSoundObjectForName(soundName)
|
||||
-- local soundsFolder = this.SoundHolder and this.SoundHolder:FindFirstChild(soundName)
|
||||
-- local soundObj = nil
|
||||
-- for _, otherSoundObj in pairs(soundsFolder:GetChildren()) do
|
||||
-- print("Other:" , otherSoundObj , " is time" , otherSoundObj.TimePosition, "tl:" , otherSoundObj.TimeLength)
|
||||
-- if not otherSoundObj.IsPlaying then
|
||||
-- if soundObj then
|
||||
-- otherSoundObj:Destroy()
|
||||
-- else
|
||||
-- soundObj = otherSoundObj
|
||||
-- end
|
||||
-- end
|
||||
-- end
|
||||
-- return soundObj
|
||||
if SoundQueue[soundName] then
|
||||
local soundObj = table.remove(SoundQueue[soundName], 1)
|
||||
if soundObj then
|
||||
table.insert(SoundQueue[soundName], #SoundQueue[soundName], soundObj)
|
||||
return soundObj
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function this:CreateSound(soundName)
|
||||
local fileName = SOUNDS[soundName]
|
||||
local soundsUrl = BASE_SOUND_URL .. fileName
|
||||
|
||||
local soundObj = Instance.new('Sound')
|
||||
soundObj.Name = soundName
|
||||
soundObj.SoundId = soundsUrl
|
||||
|
||||
return soundObj
|
||||
end
|
||||
|
||||
function this:Play(soundName, vol, isLoop, pitch, ...)
|
||||
local result = nil
|
||||
|
||||
if SOUNDS[soundName] then
|
||||
local soundObj = FindSoundObjectForName(soundName)
|
||||
if soundObj then
|
||||
soundObj.Volume = vol or 1
|
||||
|
||||
soundObj.Looped = isLoop or false
|
||||
soundObj.Pitch = pitch or 1
|
||||
|
||||
soundObj:Play(...)
|
||||
|
||||
rawVolumes[soundObj] = soundObj.Volume
|
||||
if not IsGameRunning() then
|
||||
soundObj.Volume = 0
|
||||
end
|
||||
|
||||
result = soundObj
|
||||
else
|
||||
print("No sound:" , soundName , "in the queue.")
|
||||
end
|
||||
else
|
||||
spawn(function()
|
||||
error("Unable to find sound: " .. tostring(soundName))
|
||||
end)
|
||||
end
|
||||
|
||||
return result
|
||||
end
|
||||
|
||||
function this:IsPlaying(soundName)
|
||||
local sound = this.SoundHolder:FindFirstChild(soundName)
|
||||
if sound then
|
||||
return sound.IsPlaying
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function this:Stop(soundName, ...)
|
||||
if not Player then return end
|
||||
if this.SoundHolder and SOUNDS[soundName] then
|
||||
local soundObj = this.SoundHolder:FindFirstChild(soundName)
|
||||
if soundObj then
|
||||
soundObj:Stop()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function this:TweenSound(soundObj, newVolume, duration)
|
||||
rawVolumes[soundObj] = nil
|
||||
Utility.PropertyTweener(soundObj, 'Volume', soundObj.Volume, newVolume, duration,
|
||||
function(...)
|
||||
return IsGameRunning() and Utility.EaseInOutQuad(...) or 0
|
||||
end,
|
||||
true,
|
||||
function()
|
||||
rawVolumes[soundObj] = newVolume
|
||||
end)
|
||||
end
|
||||
|
||||
-- function this:PlayOnEvent(event, soundName)
|
||||
-- event:connect(function()
|
||||
|
||||
-- end)
|
||||
-- end
|
||||
|
||||
local function Initialize()
|
||||
local appshellSounds = Instance.new('Folder')
|
||||
appshellSounds.Name = 'AppShellSounds'
|
||||
appshellSounds.Parent = SoundService
|
||||
|
||||
this.SoundHolder = appshellSounds
|
||||
|
||||
for name, fileName in pairs(SOUNDS) do
|
||||
local soundsForFile = Instance.new('Folder')
|
||||
soundsForFile.Name = name
|
||||
soundsForFile.Parent = this.SoundHolder
|
||||
|
||||
SoundQueue[name] = {}
|
||||
for i = 1, 3 do
|
||||
local soundObj = this:CreateSound(name)
|
||||
soundObj.Parent = soundsForFile
|
||||
table.insert(SoundQueue[name], soundObj)
|
||||
end
|
||||
end
|
||||
|
||||
local lastSelection = nil
|
||||
GuiService.Changed:connect(function(property)
|
||||
if property == 'SelectedCoreObject' then
|
||||
local currentSelection = GuiService.SelectedCoreObject
|
||||
if currentSelection and lastSelection then
|
||||
local moveSelectionSound = currentSelection:FindFirstChild('MoveSelection')
|
||||
if moveSelectionSound and moveSelectionSound:IsA('Sound') then
|
||||
moveSelectionSound:Play()
|
||||
end
|
||||
end
|
||||
lastSelection = currentSelection
|
||||
end
|
||||
end)
|
||||
|
||||
if not IsGameRunning() then
|
||||
spawn(function()
|
||||
while not IsGameRunning() do
|
||||
wait(0.1)
|
||||
end
|
||||
for soundObj, rawVolume in pairs(rawVolumes) do
|
||||
soundObj.Volume = rawVolume
|
||||
end
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
Initialize()
|
||||
|
||||
return this
|
||||
end
|
||||
|
||||
return GetSoundManager()
|
||||
@@ -0,0 +1,576 @@
|
||||
--[[
|
||||
// StorePane.lua by Kip Turner
|
||||
]]
|
||||
local CoreGui = game:GetService("CoreGui")
|
||||
local GuiRoot = CoreGui:FindFirstChild("RobloxGui")
|
||||
local Modules = GuiRoot:FindFirstChild("Modules")
|
||||
|
||||
local Utility = require(Modules:FindFirstChild('Utility'))
|
||||
local GlobalSettings = require(Modules:FindFirstChild('GlobalSettings'))
|
||||
local ScrollingGridModule = require(Modules:FindFirstChild('ScrollingGrid'))
|
||||
local UserDataModule = require(Modules:FindFirstChild('UserData'))
|
||||
local ScreenManager = require(Modules:FindFirstChild('ScreenManager'))
|
||||
local Strings = require(Modules:FindFirstChild('LocalizedStrings'))
|
||||
local AssetManager = require(Modules:FindFirstChild('AssetManager'))
|
||||
local CreateConfirmPrompt = require(Modules:FindFirstChild('ConfirmPrompt'))
|
||||
local LoadingWidget = require(Modules:FindFirstChild('LoadingWidget'))
|
||||
local SoundManager = require(Modules:FindFirstChild('SoundManager'))
|
||||
local PlatformCatalogData = require(Modules:FindFirstChild('PlatformCatalogData'))
|
||||
local CurrencyWidgetModule = require(Modules:FindFirstChild('CurrencyWidget'))
|
||||
local EventHub = require(Modules:FindFirstChild('EventHub'))
|
||||
|
||||
local RobuxBalanceOverlay = require(Modules:FindFirstChild('RobuxBalanceOverlay'))
|
||||
|
||||
local ErrorOverlayModule = require(Modules:FindFirstChild('ErrorOverlay'))
|
||||
local Errors = require(Modules:FindFirstChild('Errors'))
|
||||
|
||||
|
||||
local TextService = game:GetService('TextService')
|
||||
local GuiService = game:GetService('GuiService')
|
||||
local PlatformService;
|
||||
pcall(function() PlatformService = game:GetService('PlatformService') end)
|
||||
|
||||
--[[ Constants ]]--
|
||||
local GRID_ROWS = 2
|
||||
local GRID_COLUMNS = 3
|
||||
local GRID_SPACING = Vector2.new(20,20)
|
||||
local GRID_SIZE = UDim2.new(0, 1620, 0, 610)
|
||||
local GRID_CELL_SIZE = Vector2.new(520,285)
|
||||
local GRID_POSITION = UDim2.new(0, 40, 0, 65)
|
||||
local DESCRIPTION_SIZE = UDim2.new(1,0,0,50)
|
||||
local PRICE_CORNER_OFFSET = Vector2.new(-15,-12)
|
||||
local NO_ITEMS_MSG_POSITION = UDim2.new(0.1, 0, 0, 275)
|
||||
local NO_ITEMS_MSG_SIZE = UDim2.new(0.8, 0, 0, 150)
|
||||
|
||||
local ROBUX_ASSETS =
|
||||
{
|
||||
{
|
||||
Wide = 'Robux01.png';
|
||||
Square = 'RobuxSquare01.png';
|
||||
};
|
||||
{
|
||||
Wide = 'Robux02.png';
|
||||
Square = 'RobuxSquare02.png';
|
||||
};
|
||||
{
|
||||
Wide = 'Robux03.png';
|
||||
Square = 'RobuxSquare03.png';
|
||||
};
|
||||
{
|
||||
Wide = 'Robux04.png';
|
||||
Square = 'RobuxSquare04.png';
|
||||
};
|
||||
{
|
||||
Wide = 'Robux05.png';
|
||||
Square = 'RobuxSquare05.png';
|
||||
};
|
||||
{
|
||||
Wide = 'Robux06.png';
|
||||
Square = 'RobuxSquare06.png';
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
local function createGridItem(productInfo)
|
||||
local this = {}
|
||||
|
||||
local container = Utility.Create'ImageButton'
|
||||
{
|
||||
Name = "StoreItemContainer",
|
||||
BackgroundTransparency = 1,
|
||||
BackgroundColor3 = Color3.new(220/255, 220/255, 220/255),
|
||||
-- Image = '',
|
||||
AutoButtonColor = false,
|
||||
BorderSizePixel = 0,
|
||||
ZIndex = 2;
|
||||
AssetManager.CreateShadow(1);
|
||||
|
||||
SoundManager:CreateSound('MoveSelection');
|
||||
}
|
||||
local priceText = Utility.Create'TextLabel'
|
||||
{
|
||||
Name = "PriceText",
|
||||
Size = UDim2.new(0, 0, 0, 0),
|
||||
Position = UDim2.new(1, -15, 1, -12),
|
||||
BackgroundTransparency = 1,
|
||||
TextXAlignment = Enum.TextXAlignment.Right,
|
||||
TextYAlignment = 'Bottom';
|
||||
TextColor3 = GlobalSettings.WhiteTextColor,
|
||||
Font = GlobalSettings.HeadingFont,
|
||||
FontSize = GlobalSettings.LargeFontSize,
|
||||
Text = '',
|
||||
ZIndex = 2;
|
||||
Parent = container,
|
||||
}
|
||||
local dollarSign = Utility.Create'TextLabel'
|
||||
{
|
||||
Name = "DollarSign",
|
||||
Size = UDim2.new(0, 0, 1, -15),
|
||||
Position = UDim2.new(0, 0, 0, 15),
|
||||
BackgroundTransparency = 1,
|
||||
TextXAlignment = Enum.TextXAlignment.Right,
|
||||
TextYAlignment = Enum.TextYAlignment.Top,
|
||||
TextColor3 = GlobalSettings.WhiteTextColor,
|
||||
Font = GlobalSettings.BoldFont,
|
||||
FontSize = GlobalSettings.MediumFontSize,
|
||||
Text = Strings:LocalizedString('CurrencySymbol'),
|
||||
ZIndex = 2;
|
||||
Parent = priceText,
|
||||
}
|
||||
|
||||
local robuxIcon = Utility.Create'ImageLabel'
|
||||
{
|
||||
Name = 'RobuxIcon';
|
||||
Position = UDim2.new(0,5,0,5);
|
||||
Size = UDim2.new(0,80,0,80);
|
||||
Image = 'rbxasset://textures/ui/Shell/Icons/ROBUXIcon@1080.png';
|
||||
BackgroundTransparency = 1;
|
||||
ZIndex = 2;
|
||||
Parent = container;
|
||||
};
|
||||
local robuxAmount = Utility.Create'TextLabel'
|
||||
{
|
||||
Name = 'RobuxAmount';
|
||||
Text = '';
|
||||
Size = UDim2.new(0,0,1,0);
|
||||
Position = UDim2.new(1,10,0,0);
|
||||
TextXAlignment = 'Left';
|
||||
TextColor3 = GlobalSettings.WhiteTextColor;
|
||||
Font = GlobalSettings.BoldFont;
|
||||
FontSize = GlobalSettings.LargeFontSize;
|
||||
BackgroundTransparency = 1;
|
||||
ZIndex = 2;
|
||||
Parent = robuxIcon;
|
||||
};
|
||||
local percentMoreText = Utility.Create'TextLabel'
|
||||
{
|
||||
Name = "PercentMoreText",
|
||||
Size = UDim2.new(0, 0, 0, 0),
|
||||
Position = UDim2.new(0, 5, 1, 10),
|
||||
BackgroundTransparency = 1,
|
||||
TextXAlignment = Enum.TextXAlignment.Left,
|
||||
TextYAlignment = Enum.TextYAlignment.Top,
|
||||
TextColor3 = GlobalSettings.GreenTextColor,
|
||||
Font = GlobalSettings.BoldFont,
|
||||
FontSize = GlobalSettings.ButtonSize,
|
||||
-- Visible = false,
|
||||
Text = '',
|
||||
ZIndex = 2;
|
||||
Parent = robuxIcon,
|
||||
}
|
||||
|
||||
|
||||
local function UpdateInfo()
|
||||
local priceTextSize = TextService:GetTextSize(priceText.Text, Utility.ConvertFontSizeEnumToInt(priceText.FontSize), priceText.Font, Vector2.new())
|
||||
priceText.Size = UDim2.new(0, priceTextSize.x , 0, priceTextSize.y)
|
||||
priceText.Position = UDim2.new(1, PRICE_CORNER_OFFSET.x - priceTextSize.x, 1, PRICE_CORNER_OFFSET.y - priceTextSize.y)
|
||||
end
|
||||
|
||||
UpdateInfo()
|
||||
|
||||
function this:GetContainer()
|
||||
return container
|
||||
end
|
||||
|
||||
function this:SetDollarPrice(value)
|
||||
priceText.Text = value
|
||||
UpdateInfo()
|
||||
end
|
||||
|
||||
function this:SetRobuxValue(value)
|
||||
robuxAmount.Text = Utility.FormatNumberString(tostring(value))
|
||||
end
|
||||
|
||||
function this:SetPercentMore(value)
|
||||
percentMoreText.Visible = value > 0
|
||||
percentMoreText.Text = string.format(Strings:LocalizedString('PercentMoreRobuxPhrase'), tostring(value))
|
||||
end
|
||||
|
||||
function this:SetImage(image)
|
||||
container.Image = image
|
||||
end
|
||||
|
||||
return this
|
||||
end
|
||||
|
||||
local function CreateStorePane(parent)
|
||||
local this = {}
|
||||
|
||||
local storeItemClickConns = {}
|
||||
|
||||
local cachedBalance = nil
|
||||
local cachedTotalBalance = nil
|
||||
local inFocus = false
|
||||
|
||||
local currencyWidget = nil
|
||||
|
||||
local StorePaneContainer = Utility.Create'Frame'
|
||||
{
|
||||
Name = 'StorePane',
|
||||
Size = UDim2.new(1, 0, 1, 0),
|
||||
BackgroundTransparency = 1,
|
||||
Visible = false,
|
||||
Parent = parent,
|
||||
}
|
||||
local StoreDescriptionText = Utility.Create'TextLabel'
|
||||
{
|
||||
Name = "StoreDescriptionText",
|
||||
Size = DESCRIPTION_SIZE,
|
||||
Position = UDim2.new(0, 0, 0, 0),
|
||||
BackgroundTransparency = 1,
|
||||
TextXAlignment = Enum.TextXAlignment.Left,
|
||||
TextColor3 = GlobalSettings.WhiteTextColor,
|
||||
Font = GlobalSettings.LightFont,
|
||||
FontSize = GlobalSettings.TitleSize,
|
||||
TextWrapped = true,
|
||||
Visible = false;
|
||||
Text = Strings:LocalizedString('RobuxStoreDescription'),
|
||||
Parent = StorePaneContainer,
|
||||
}
|
||||
local StoreNoItemsText = Utility.Create'TextLabel'
|
||||
{
|
||||
Name = "StoreNoItemsText",
|
||||
Size = NO_ITEMS_MSG_SIZE,
|
||||
Position = NO_ITEMS_MSG_POSITION,
|
||||
BackgroundTransparency = 1,
|
||||
TextXAlignment = Enum.TextXAlignment.Center,
|
||||
FontSize = GlobalSettings.TitleSize,
|
||||
TextWrapped = true,
|
||||
|
||||
TextColor3 = GlobalSettings.GreyTextColor;
|
||||
TextTransparency = GlobalSettings.FriendStatusTextTransparency;
|
||||
Font = GlobalSettings.BoldFont;
|
||||
|
||||
Text = Strings:LocalizedString('RobuxStoreNoItemsPhrase'),
|
||||
Visible = false;
|
||||
Parent = StorePaneContainer,
|
||||
}
|
||||
|
||||
local RobuxBalanceButton = Utility.Create'ImageButton'
|
||||
{
|
||||
Name = "RobuxBalanceButton";
|
||||
-- size will change based on text bounds of balance
|
||||
Size = UDim2.new(0, 436, 0, 75);
|
||||
Position = UDim2.new(0, 0, 1, -100);
|
||||
BackgroundTransparency = 1;
|
||||
BorderSizePixel = 0;
|
||||
BackgroundColor3 = GlobalSettings.GreySelectionColor;
|
||||
Selectable = false;
|
||||
Parent = StorePaneContainer;
|
||||
|
||||
SoundManager:CreateSound('MoveSelection')
|
||||
};
|
||||
RobuxBalanceButton.SelectionGained:connect(function()
|
||||
RobuxBalanceButton.BackgroundTransparency = 0;
|
||||
end)
|
||||
RobuxBalanceButton.SelectionLost:connect(function()
|
||||
RobuxBalanceButton.BackgroundTransparency = 1;
|
||||
end)
|
||||
|
||||
local RobuxHelpIcon = Utility.Create'ImageLabel'
|
||||
{
|
||||
Name = "RobuxHelpIcon";
|
||||
Size = UDim2.new(0, 42, 0, 42);
|
||||
Position = UDim2.new(0, 10, 0, 0);
|
||||
BackgroundTransparency = 1;
|
||||
Image = 'rbxasset://textures/ui/Shell/Icons/HelpIconSmall.png';
|
||||
Visible = false;
|
||||
Parent = RobuxBalanceButton;
|
||||
};
|
||||
Utility.CalculateAnchor(RobuxHelpIcon, UDim2.new(0, 10, 0.5, 4), Utility.Enum.Anchor.CenterLeft)
|
||||
|
||||
local function setBalanceButtonSize(balanceObjectSize)
|
||||
local sizeX = 56 + RobuxHelpIcon.Size.X.Offset + (balanceObjectSize and balanceObjectSize.X or 0) + 10
|
||||
RobuxBalanceButton.Size = UDim2.new(0, sizeX, 0, 75)
|
||||
end
|
||||
|
||||
|
||||
local showBalanceHelp = true
|
||||
local showBalanceOverlayDebounce = false
|
||||
RobuxBalanceButton.MouseButton1Click:connect(function()
|
||||
if showBalanceOverlayDebounce then return end
|
||||
--
|
||||
showBalanceOverlayDebounce = true
|
||||
if showBalanceHelp then
|
||||
local robuxBalanceOverlay = RobuxBalanceOverlay(cachedBalance, cachedTotalBalance)
|
||||
ScreenManager:OpenScreen(robuxBalanceOverlay, false)
|
||||
end
|
||||
showBalanceOverlayDebounce = false
|
||||
end)
|
||||
|
||||
local function setBalanceHelpOption(platformBalance)
|
||||
local totalBalance = UserDataModule.GetTotalUserBalanceAsync()
|
||||
if totalBalance then
|
||||
cachedTotalBalance = totalBalance
|
||||
showBalanceHelp = platformBalance ~= totalBalance
|
||||
RobuxHelpIcon.Visible = showBalanceHelp
|
||||
RobuxBalanceButton.Selectable = showBalanceHelp
|
||||
end
|
||||
end
|
||||
|
||||
local function PopulateBalance()
|
||||
spawn(function()
|
||||
local platformBalance = currencyWidget and currencyWidget:GetRobuxAmountAsync() or UserDataModule.GetPlatformUserBalanceAsync()
|
||||
if platformBalance then
|
||||
cachedBalance = platformBalance
|
||||
setBalanceHelpOption(platformBalance)
|
||||
if currencyWidget then
|
||||
setBalanceButtonSize(currencyWidget:GetAbsoluteSize())
|
||||
end
|
||||
else
|
||||
print("Unable to update user's balance because web call failed.")
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
|
||||
local storeScrollingGrid = ScrollingGridModule()
|
||||
storeScrollingGrid:SetSize(GRID_SIZE)
|
||||
storeScrollingGrid:SetScrollDirection(storeScrollingGrid.Enum.ScrollDirection.Vertical)
|
||||
storeScrollingGrid:SetCellSize(GRID_CELL_SIZE)
|
||||
storeScrollingGrid:SetPosition(GRID_POSITION)
|
||||
storeScrollingGrid:SetRowColumnConstraint(GRID_COLUMNS)
|
||||
storeScrollingGrid:SetSpacing(GRID_SPACING)
|
||||
storeScrollingGrid:SetParent(StorePaneContainer)
|
||||
--
|
||||
|
||||
local SuccessfullyLoadedCatalog = false
|
||||
local catalogLoading = false
|
||||
local function OnLoad()
|
||||
if catalogLoading or SuccessfullyLoadedCatalog then return end
|
||||
catalogLoading = true
|
||||
|
||||
if PlatformService then
|
||||
local catalogInfo, success, errormsg;
|
||||
|
||||
-- Hide these text labels while we are loading
|
||||
StoreDescriptionText.Visible = false
|
||||
StoreNoItemsText.Visible = false
|
||||
|
||||
local loader = LoadingWidget({Parent = StorePaneContainer}, {function() catalogInfo, success, errormsg = PlatformCatalogData:GetCatalogInfoAsync() end})
|
||||
loader:AwaitFinished()
|
||||
loader:Cleanup()
|
||||
loader = nil
|
||||
ScreenManager:DefaultFadeIn(storeScrollingGrid:GetGuiObject())
|
||||
if inFocus and GuiService.SelectedCoreObject == nil then
|
||||
if storeScrollingGrid.GridItems[1] then
|
||||
GuiService.SelectedCoreObject = storeScrollingGrid.GridItems[1]
|
||||
end
|
||||
end
|
||||
|
||||
if success and catalogInfo then
|
||||
while #storeItemClickConns > 0 do
|
||||
Utility.DisconnectEvent(table.remove(storeItemClickConns, 1))
|
||||
end
|
||||
|
||||
table.sort(catalogInfo, function(a, b)
|
||||
local aPrice = PlatformCatalogData:ParseRobuxValue(a)
|
||||
local bPrice = PlatformCatalogData:ParseRobuxValue(b)
|
||||
if aPrice and bPrice then
|
||||
return aPrice < bPrice
|
||||
end
|
||||
return a < b
|
||||
end)
|
||||
|
||||
local worstRatio = nil
|
||||
for _, productInfo in pairs(catalogInfo) do
|
||||
local ratio = PlatformCatalogData:CalculateRobuxRatio(productInfo)
|
||||
if Utility.IsFinite(ratio) and ratio ~= 0 then
|
||||
if worstRatio == nil or ratio < worstRatio then
|
||||
worstRatio = ratio
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if #catalogInfo == 0 then
|
||||
StoreDescriptionText.Visible = false
|
||||
StoreNoItemsText.Visible = true
|
||||
elseif not SuccessfullyLoadedCatalog then
|
||||
SuccessfullyLoadedCatalog = true
|
||||
|
||||
StoreDescriptionText.Visible = true
|
||||
StoreNoItemsText.Visible = false
|
||||
|
||||
local i = 1
|
||||
for _, productInfo in pairs(catalogInfo) do
|
||||
local productImageData = ROBUX_ASSETS[math.min(i, #ROBUX_ASSETS)]
|
||||
local catalogItemImage = 'rbxasset://textures/ui/Shell/Images/Robux/' .. productImageData['Wide']
|
||||
local confirmItemImage = 'rbxasset://textures/ui/Shell/Images/Robux/' .. productImageData['Square']
|
||||
|
||||
local debounce = false
|
||||
local function onClick()
|
||||
if debounce then return end
|
||||
debounce = true
|
||||
|
||||
local confirmPrompt = CreateConfirmPrompt({ProductName = productInfo and productInfo.Name or 'Unknown'; Balance = cachedBalance; Cost = productInfo and productInfo.DisplayListPrice or "Unknown"; ProductImage = confirmItemImage; ProductImageSize = Vector2.new(484, 540); CurrencySymbol = '';},
|
||||
{ShowRemainingBalance = false; ShowRobuxIcon = false; ConfirmWithPrice = true;})
|
||||
|
||||
do
|
||||
local selectedObject = GuiService.SelectedCoreObject
|
||||
if selectedObject and selectedObject:IsDescendantOf(StorePaneContainer) then
|
||||
this.SavedSelection = selectedObject
|
||||
end
|
||||
end
|
||||
|
||||
confirmPrompt:SetParent(GuiRoot)
|
||||
ScreenManager:OpenScreen(confirmPrompt)
|
||||
confirmPrompt:FadeInBackground()
|
||||
local function onConfirmFinished(result)
|
||||
if result == true then
|
||||
print("Do buy")
|
||||
local purchaseResult = nil
|
||||
if not UserSettings().GameSettings:InStudioMode() then
|
||||
local purchaseCallSuccess, purchaseErrorMsg = pcall(function()
|
||||
purchaseResult = PlatformService:BeginPlatformStorePurchase(productInfo.ProductId)
|
||||
end)
|
||||
if purchaseCallSuccess then
|
||||
-- 0 means we bought it
|
||||
-- print("purchaseResult" , purchaseResult)
|
||||
if purchaseResult == 0 then
|
||||
-- print("dispatchEvent RobuxCatalogPurchaseInitiated")
|
||||
EventHub:dispatchEvent(EventHub.Notifications["RobuxCatalogPurchaseInitiated"], purchaseResult);
|
||||
end
|
||||
else
|
||||
print("Purchase Robux failed with pcall status:" , purchaseCallSuccess , "and purchaseResult:" , purchaseResult , "because of:" , purchaseErrorMsg)
|
||||
end
|
||||
else
|
||||
spawn(function()
|
||||
ScreenManager:OpenScreen(ErrorOverlayModule(Errors.RobuxPurchase[1]), false)
|
||||
end)
|
||||
end
|
||||
PopulateBalance()
|
||||
print("Done with purchase; result was:" , purchaseResult)
|
||||
else
|
||||
print("Declined to buy")
|
||||
end
|
||||
end
|
||||
-- confirmPrompt:AddResultCallback(onConfirmFinished)
|
||||
local result = confirmPrompt:ResultAsync()
|
||||
onConfirmFinished(result)
|
||||
|
||||
debounce = false
|
||||
end
|
||||
|
||||
local extractedPrice = productInfo and productInfo.DisplayPrice and string.gsub(productInfo.DisplayPrice, "%$", "") or ""
|
||||
-- local extractedRobuxValue = productInfo and productInfo.Name and string.match(productInfo.Name, "[0-9,]+") or "1000"
|
||||
local thisRatio = PlatformCatalogData:CalculateRobuxRatio(productInfo)
|
||||
|
||||
local item = createGridItem()
|
||||
item:SetDollarPrice(extractedPrice)
|
||||
item:SetRobuxValue(PlatformCatalogData:ParseRobuxValue(productInfo))
|
||||
if thisRatio and worstRatio then
|
||||
item:SetPercentMore(math.floor(((thisRatio / worstRatio) - 1) * 100))
|
||||
else
|
||||
item:SetPercentMore(0)
|
||||
end
|
||||
item:SetImage(catalogItemImage)
|
||||
storeScrollingGrid:AddItem(item:GetContainer())
|
||||
table.insert(storeItemClickConns, item:GetContainer().MouseButton1Click:connect(onClick))
|
||||
|
||||
i = math.min(#ROBUX_ASSETS, i + 1)
|
||||
end
|
||||
end
|
||||
else
|
||||
StoreNoItemsText.Visible = true
|
||||
print("StorePane - BeginGetCatalogInfo failed because:" , errormsg)
|
||||
end
|
||||
end
|
||||
catalogLoading = false
|
||||
end
|
||||
|
||||
if not SuccessfullyLoadedCatalog then
|
||||
spawn(OnLoad)
|
||||
end
|
||||
|
||||
--[[ Public API ]]--
|
||||
function this:GetName()
|
||||
return Strings:LocalizedString('CatalogWord')
|
||||
end
|
||||
|
||||
function this:IsFocused()
|
||||
return inFocus
|
||||
end
|
||||
|
||||
|
||||
local RobuxChangedConn = nil
|
||||
local robuxChangedEventCount = 0
|
||||
local robuxAmountChangedLoader = nil
|
||||
|
||||
function this:Show()
|
||||
StorePaneContainer.Visible = true
|
||||
|
||||
if not currencyWidget then
|
||||
currencyWidget = CurrencyWidgetModule({Parent = RobuxBalanceButton; Position = UDim2.new(0, RobuxHelpIcon.Position.X.Offset + RobuxHelpIcon.Size.X.Offset + 10, 0.5, -30);})
|
||||
else
|
||||
spawn(function()
|
||||
currencyWidget:RefreshRobuxAmountAsync()
|
||||
end)
|
||||
end
|
||||
setBalanceButtonSize(currencyWidget:GetAbsoluteSize())
|
||||
Utility.DisconnectEvent(RobuxChangedConn)
|
||||
RobuxChangedConn = currencyWidget.RobuxChanged:connect(function()
|
||||
PopulateBalance()
|
||||
currencyWidget:GetRobuxAmountAsync()
|
||||
setBalanceButtonSize(currencyWidget:GetAbsoluteSize())
|
||||
SoundManager:Play('PurchaseSuccess')
|
||||
end)
|
||||
PopulateBalance()
|
||||
|
||||
self.TransitionTweens = ScreenManager:DefaultFadeIn(StorePaneContainer)
|
||||
ScreenManager:PlayDefaultOpenSound()
|
||||
|
||||
if not SuccessfullyLoadedCatalog then
|
||||
spawn(OnLoad)
|
||||
end
|
||||
end
|
||||
|
||||
function this:Hide()
|
||||
StorePaneContainer.Visible = false
|
||||
|
||||
RobuxChangedConn = Utility.DisconnectEvent(RobuxChangedConn)
|
||||
|
||||
ScreenManager:DefaultCancelFade(self.TransitionTweens)
|
||||
self.TransitionTweens = nil
|
||||
|
||||
-- Let's not do this it creates weird race conditions
|
||||
-- if currencyWidget then
|
||||
-- currencyWidget:Destroy()
|
||||
-- currencyWidget = nil
|
||||
-- end
|
||||
end
|
||||
|
||||
function this:Focus()
|
||||
-- TODO: What is the selection if the packages fail to load?
|
||||
inFocus = true
|
||||
if self.SavedSelection and self.SavedSelection:IsDescendantOf(StorePaneContainer) then
|
||||
GuiService.SelectedCoreObject = self.SavedSelection
|
||||
elseif storeScrollingGrid.GridItems[1] then
|
||||
GuiService.SelectedCoreObject = storeScrollingGrid.GridItems[1]
|
||||
end
|
||||
self.SavedSelection = nil
|
||||
end
|
||||
|
||||
function this:RemoveFocus()
|
||||
inFocus = false
|
||||
local selectedObject = GuiService.SelectedCoreObject
|
||||
if selectedObject and selectedObject:IsDescendantOf(StorePaneContainer) then
|
||||
GuiService.SelectedCoreObject = nil
|
||||
end
|
||||
end
|
||||
|
||||
function this:SetPosition(newPosition)
|
||||
StorePaneContainer.Position = newPosition
|
||||
end
|
||||
|
||||
function this:SetParent(newParent)
|
||||
StorePaneContainer.Parent = newParent
|
||||
end
|
||||
|
||||
function this:IsAncestorOf(object)
|
||||
return StorePaneContainer and StorePaneContainer:IsAncestorOf(object)
|
||||
end
|
||||
|
||||
return this
|
||||
end
|
||||
|
||||
return CreateStorePane
|
||||
@@ -0,0 +1,282 @@
|
||||
-- Written by Kip Turner, Copyright ROBLOX 2015
|
||||
|
||||
local GuiService = game:GetService('GuiService')
|
||||
local ContextActionService = game:GetService("ContextActionService")
|
||||
local UserInputService = game:GetService("UserInputService")
|
||||
|
||||
local CoreGui = Game:GetService("CoreGui")
|
||||
local GuiRoot = CoreGui:FindFirstChild("RobloxGui")
|
||||
local Modules = GuiRoot:FindFirstChild("Modules")
|
||||
|
||||
local Utility = require(Modules:FindFirstChild('Utility'))
|
||||
local GlobalSettings = require(Modules:FindFirstChild('GlobalSettings'))
|
||||
|
||||
local function CreateTabDock()
|
||||
local this = {}
|
||||
|
||||
local Tabs = {}
|
||||
local SelectedTab = nil
|
||||
local SizeChangedConns = {}
|
||||
this.SelectedTabChanged = Utility.Signal()
|
||||
this.SelectedTabClicked = Utility.Signal()
|
||||
local guiServiceChangedCn = nil
|
||||
|
||||
local TabContainer = Utility.Create'ImageButton'
|
||||
{
|
||||
Size = UDim2.new(1, 0, 0, 36);
|
||||
BackgroundTransparency = 1;
|
||||
Name = 'TabContainer';
|
||||
}
|
||||
|
||||
local SelectionBorderObject = Utility.Create'ImageLabel'
|
||||
{
|
||||
Name = 'SelectionBorderObject';
|
||||
Size = UDim2.new(1,0,0,4);
|
||||
Position = UDim2.new(0,0,1,5);
|
||||
BorderSizePixel = 0;
|
||||
BackgroundColor3 = GlobalSettings.TabUnderlineColor;
|
||||
-- Image = 'rbxasset://textures/ui/SelectionBox.png';
|
||||
-- ScaleType = Enum.ScaleType.Slice;
|
||||
-- SliceCenter = Rect.new(19,19,43,43);
|
||||
BackgroundTransparency = 0;
|
||||
};
|
||||
|
||||
local DownSelector = Utility.Create'ImageButton'
|
||||
{
|
||||
Size = UDim2.new(1, 0, 0, 36);
|
||||
BackgroundTransparency = 1;
|
||||
Name = 'DownSelector';
|
||||
Selectable = false;
|
||||
Parent = TabContainer;
|
||||
}
|
||||
|
||||
DownSelector.SelectionGained:connect(function()
|
||||
if SelectedTab then
|
||||
GuiService.SelectedCoreObject = SelectedTab:GetGuiObject()
|
||||
this.SelectedTabClicked:fire(SelectedTab)
|
||||
end
|
||||
end)
|
||||
|
||||
local function onGuiServiceChanged(prop)
|
||||
if prop == 'SelectedCoreObject' then
|
||||
if GuiService.SelectedCoreObject == TabContainer then
|
||||
local currentTab = this:GetSelectedTab()
|
||||
local currentTabItem = currentTab and currentTab:GetGuiObject()
|
||||
if currentTabItem then
|
||||
GuiService.SelectedCoreObject = currentTabItem
|
||||
end
|
||||
end
|
||||
|
||||
local selectedObject = GuiService.SelectedCoreObject
|
||||
local focusedTab = this:FindFocusedTabByGuiObject(selectedObject)
|
||||
|
||||
if SelectedTab and selectedObject ~= SelectedTab:GetGuiObject() then
|
||||
SelectedTab:OnClickRelease()
|
||||
end
|
||||
|
||||
if focusedTab then
|
||||
this:SetSelectedTab(focusedTab)
|
||||
|
||||
for _, inputObject in pairs(UserInputService:GetGamepadState(Enum.UserInputType.Gamepad1)) do
|
||||
if inputObject.KeyCode == Enum.KeyCode.ButtonA and inputObject.UserInputState == Enum.UserInputState.Begin then
|
||||
if SelectedTab then
|
||||
SelectedTab:OnClick()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
ContextActionService:UnbindCoreAction("OnClickSelectedTab")
|
||||
ContextActionService:BindCoreAction("OnClickSelectedTab",
|
||||
function(actionName, inputState, inputObject)
|
||||
if inputState == Enum.UserInputState.Begin then
|
||||
if SelectedTab then
|
||||
SelectedTab:OnClick()
|
||||
end
|
||||
elseif inputState == Enum.UserInputState.End then
|
||||
if SelectedTab then
|
||||
SelectedTab:OnClickRelease()
|
||||
end
|
||||
this.SelectedTabClicked:fire(SelectedTab)
|
||||
end
|
||||
end,
|
||||
false,
|
||||
Enum.KeyCode.ButtonA)
|
||||
else
|
||||
ContextActionService:UnbindCoreAction("OnClickSelectedTab")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function this:FindFocusedTabByGuiObject(selectedObject)
|
||||
-- NOTE: This is a sort of cheater way of culling look-up checks
|
||||
if selectedObject and selectedObject:IsDescendantOf(TabContainer) then
|
||||
for _, currTab in pairs(Tabs) do
|
||||
local guiObject = currTab and currTab:GetGuiObject()
|
||||
if guiObject and guiObject == selectedObject then
|
||||
return currTab
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function this:IsFocused()
|
||||
local selectedObject = GuiService.SelectedCoreObject
|
||||
return self:FindFocusedTabByGuiObject(selectedObject) ~= nil
|
||||
end
|
||||
|
||||
function this:SetSelectedTab(newSelectedTab)
|
||||
if newSelectedTab ~= SelectedTab then
|
||||
if SelectedTab then
|
||||
SelectedTab:SetSelected(false)
|
||||
SelectedTab:OnClickRelease()
|
||||
end
|
||||
|
||||
SelectedTab = newSelectedTab
|
||||
|
||||
if SelectedTab then
|
||||
SelectedTab:SetSelected(true)
|
||||
if self:IsFocused() then
|
||||
local currentTabItem = SelectedTab and SelectedTab:GetGuiObject()
|
||||
if currentTabItem then
|
||||
GuiService.SelectedCoreObject = currentTabItem
|
||||
end
|
||||
end
|
||||
end
|
||||
-- Fire tab selection event with the name of the new tab selection
|
||||
this.SelectedTabChanged:fire(SelectedTab)
|
||||
end
|
||||
end
|
||||
|
||||
function this:Focus()
|
||||
if SelectedTab then
|
||||
SelectedTab:SetSelected(true)
|
||||
local currentTabItem = SelectedTab and SelectedTab:GetGuiObject()
|
||||
if currentTabItem then
|
||||
GuiService.SelectedCoreObject = currentTabItem
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function this:GetSelectedTab()
|
||||
return SelectedTab
|
||||
end
|
||||
|
||||
local arrangeCount = 0
|
||||
function this:ArrangeTabs()
|
||||
arrangeCount = arrangeCount + 1
|
||||
local currentCount = arrangeCount
|
||||
|
||||
local x = 0
|
||||
for i, tabItem in pairs(Tabs) do
|
||||
local tabItemSize = tabItem:GetSize()
|
||||
local xSize = tabItemSize.X.Offset
|
||||
|
||||
local spacing = GlobalSettings.TabItemSpacing
|
||||
if i == 1 then
|
||||
spacing = 0
|
||||
end
|
||||
-- Stop recursion in its tracks
|
||||
if currentCount == arrangeCount then
|
||||
tabItem:SetPosition(UDim2.new(0, x + spacing, 0, 0))
|
||||
|
||||
local tabItemGuiObject = tabItem:GetGuiObject()
|
||||
if tabItemGuiObject then
|
||||
local prevItemGuiObject = Tabs[i-1] and Tabs[i-1]:GetGuiObject()
|
||||
local nextItemGuiObject = Tabs[i+1] and Tabs[i+1]:GetGuiObject()
|
||||
tabItemGuiObject.NextSelectionLeft = prevItemGuiObject
|
||||
tabItemGuiObject.NextSelectionRight = nextItemGuiObject
|
||||
end
|
||||
|
||||
else
|
||||
return
|
||||
end
|
||||
x = x + spacing + xSize
|
||||
end
|
||||
end
|
||||
|
||||
function this:FindTabIndex(tab)
|
||||
for i, currTab in pairs(Tabs) do
|
||||
if tab == currTab then
|
||||
return i
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function this:GetNextTab()
|
||||
if SelectedTab then
|
||||
local index = this:FindTabIndex(SelectedTab)
|
||||
return index and Tabs[index + 1]
|
||||
end
|
||||
end
|
||||
|
||||
function this:GetPreviousTab()
|
||||
if SelectedTab then
|
||||
local index = this:FindTabIndex(SelectedTab)
|
||||
return index and Tabs[index - 1]
|
||||
end
|
||||
end
|
||||
|
||||
function this:AddTab(newTab)
|
||||
local existingIndex = self:FindTabIndex(newTab)
|
||||
if existingIndex then
|
||||
print("Not adding tab:" , newTab:GetName() , "because that tab already exists.")
|
||||
return
|
||||
end
|
||||
|
||||
local guiObject = newTab and newTab:GetGuiObject()
|
||||
if guiObject then
|
||||
guiObject.SelectionImageObject = SelectionBorderObject
|
||||
guiObject.NextSelectionDown = DownSelector
|
||||
end
|
||||
|
||||
table.insert(Tabs, newTab)
|
||||
newTab:SetParent(TabContainer)
|
||||
|
||||
Utility.DisconnectEvent(SizeChangedConns[newTab])
|
||||
SizeChangedConns[newTab] = newTab.SizeChanged:connect(function()
|
||||
self:ArrangeTabs()
|
||||
end)
|
||||
|
||||
this:ArrangeTabs()
|
||||
|
||||
return newTab
|
||||
end
|
||||
|
||||
function this:RemoveTab(tab)
|
||||
local removeIndex = self:FindTabIndex(tab)
|
||||
|
||||
if removeIndex then
|
||||
table.remove(Tabs, removeIndex)
|
||||
if tab == SelectedTab then
|
||||
this:SetSelectedTab(nil)
|
||||
end
|
||||
Utility.DisconnectEvent(SizeChangedConns[tab])
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function this:SetPosition(newPosition)
|
||||
TabContainer.Position = newPosition
|
||||
end
|
||||
|
||||
function this:SetParent(newParent)
|
||||
TabContainer.Parent = newParent
|
||||
end
|
||||
|
||||
function this:ConnectEvents()
|
||||
onGuiServiceChanged('SelectedCoreObject')
|
||||
guiServiceChangedCn = GuiService.Changed:connect(onGuiServiceChanged)
|
||||
end
|
||||
|
||||
function this:DisconnectEvents()
|
||||
if guiServiceChangedCn then
|
||||
guiServiceChangedCn:disconnect()
|
||||
guiServiceChangedCn = nil
|
||||
end
|
||||
end
|
||||
|
||||
return this
|
||||
end
|
||||
|
||||
return CreateTabDock
|
||||
@@ -0,0 +1,111 @@
|
||||
-- Written by Kip Turner, Copyright ROBLOX 2015
|
||||
|
||||
local TextService = game:GetService('TextService')
|
||||
|
||||
local CoreGui = Game:GetService("CoreGui")
|
||||
local GuiRoot = CoreGui:FindFirstChild("RobloxGui")
|
||||
local Modules = GuiRoot:FindFirstChild("Modules")
|
||||
|
||||
local Utility = require(Modules:FindFirstChild('Utility'))
|
||||
local GlobalSettings = require(Modules:FindFirstChild('GlobalSettings'))
|
||||
local SoundManager = require(Modules:FindFirstChild('SoundManager'))
|
||||
|
||||
local function CreateTabDockItem(tabName, contentItem)
|
||||
local this = {}
|
||||
local name = tabName
|
||||
local selected = false
|
||||
local content = contentItem
|
||||
|
||||
this.SizeChanged = Utility.Signal()
|
||||
|
||||
local tabItem = Utility.Create'TextLabel'
|
||||
{
|
||||
Text = name;
|
||||
Size = UDim2.new(0, 100, 1, 0);
|
||||
BackgroundTransparency = 1;
|
||||
Name = 'TabItem';
|
||||
FontSize = GlobalSettings.HeaderSize;
|
||||
Font = GlobalSettings.LightFont;
|
||||
BackgroundTransparency = 1;
|
||||
}
|
||||
do
|
||||
local tabItemTextSize = TextService:GetTextSize(tabItem.Text, Utility.ConvertFontSizeEnumToInt(tabItem.FontSize), tabItem.Font, Vector2.new())
|
||||
tabItem.Size = UDim2.new(0,tabItemTextSize.X,1,0)
|
||||
this.SizeChanged:fire(tabItem.Size)
|
||||
end
|
||||
local smallText = Utility.Create'TextLabel'
|
||||
{
|
||||
Text = name;
|
||||
Size = UDim2.new(0, 0, 0, 0);
|
||||
Position = UDim2.new(0.5, 0, 0.5, 0);
|
||||
BackgroundTransparency = 1;
|
||||
Name = 'SmallText';
|
||||
FontSize = GlobalSettings.MediumFontSize;
|
||||
Font = GlobalSettings.LightFont;
|
||||
TextColor3 = GlobalSettings.WhiteTextColor;
|
||||
BackgroundTransparency = 1;
|
||||
Visible = false;
|
||||
Parent = tabItem;
|
||||
}
|
||||
|
||||
local function OnSelectionChanged()
|
||||
if selected then
|
||||
tabItem.TextColor3 = GlobalSettings.WhiteTextColor;
|
||||
else
|
||||
tabItem.TextColor3 = GlobalSettings.BlueTextColor;
|
||||
end
|
||||
end
|
||||
|
||||
function this:GetContentItem()
|
||||
return content
|
||||
end
|
||||
|
||||
function this:GetGuiObject()
|
||||
return tabItem
|
||||
end
|
||||
|
||||
function this:SetSelected(isSelected)
|
||||
if selected ~= isSelected then
|
||||
selected = isSelected
|
||||
OnSelectionChanged()
|
||||
end
|
||||
end
|
||||
|
||||
function this:GetSelected()
|
||||
return selected
|
||||
end
|
||||
|
||||
function this:GetName()
|
||||
return name
|
||||
end
|
||||
|
||||
function this:GetSize()
|
||||
return tabItem.Size
|
||||
end
|
||||
|
||||
function this:OnClick()
|
||||
smallText.Visible = true;
|
||||
tabItem.TextTransparency = 1;
|
||||
SoundManager:Play('ButtonPress')
|
||||
end
|
||||
|
||||
function this:OnClickRelease()
|
||||
smallText.Visible = false;
|
||||
tabItem.TextTransparency = 0;
|
||||
end
|
||||
|
||||
function this:SetPosition(newPosition)
|
||||
tabItem.Position = newPosition
|
||||
end
|
||||
|
||||
function this:SetParent(newParent)
|
||||
tabItem.Parent = newParent
|
||||
end
|
||||
-- Initialize
|
||||
OnSelectionChanged()
|
||||
|
||||
return this
|
||||
end
|
||||
|
||||
|
||||
return CreateTabDockItem
|
||||
@@ -0,0 +1,165 @@
|
||||
--[[
|
||||
// TextBox.lua
|
||||
|
||||
// Creates a custom TextBox object that uses the platform virtual keyboard.
|
||||
]]
|
||||
local CoreGui = Game:GetService("CoreGui")
|
||||
local GuiRoot = CoreGui:FindFirstChild("RobloxGui")
|
||||
local Modules = GuiRoot:FindFirstChild("Modules")
|
||||
|
||||
local PlatformService = nil
|
||||
pcall(function() PlatformService = game:GetService('PlatformService') end)
|
||||
|
||||
local AssetManager = require(Modules:FindFirstChild('AssetManager'))
|
||||
local GlobalSettings = require(Modules:FindFirstChild('GlobalSettings'))
|
||||
local SoundManager = require(Modules:FindFirstChild('SoundManager'))
|
||||
local Utility = require(Modules:FindFirstChild('Utility'))
|
||||
|
||||
local function createTextBox(size)
|
||||
local this = {}
|
||||
|
||||
local spacing = Vector2.new()
|
||||
local defaultText = ""
|
||||
|
||||
local keyboardTitle = ""
|
||||
local keyboardDescription = ""
|
||||
local keyboardType = Enum.XboxKeyBoardType.Default
|
||||
local currentInputText = ""
|
||||
|
||||
local keyboardClosedCn = nil
|
||||
local isEnabled = true
|
||||
|
||||
this.OnTextChanged = Utility.Signal()
|
||||
|
||||
-- need custom selection box to fit with the spacing
|
||||
local SelectionBox = Utility.Create'ImageLabel'
|
||||
{
|
||||
Name = "SelectionBox";
|
||||
Image = 'rbxasset://textures/ui/SelectionBox.png';
|
||||
ScaleType = Enum.ScaleType.Slice;
|
||||
SliceCenter = Rect.new(21,21,41,41);
|
||||
BackgroundTransparency = 1;
|
||||
}
|
||||
|
||||
local TextBoxFrame = Utility.Create'ImageButton'
|
||||
{
|
||||
Name = "TextBoxFrame";
|
||||
Size = size;
|
||||
BackgroundTransparency = 1;
|
||||
ImageColor3 = GlobalSettings.TextBoxColor;
|
||||
ImageTransparency = GlobalSettings.TextBoxDefaultTransparency;
|
||||
Image = 'rbxasset://textures/ui/Shell/Buttons/Generic9ScaleButton@720.png';
|
||||
ScaleType = Enum.ScaleType.Slice;
|
||||
SliceCenter = Rect.new(Vector2.new(4, 4), Vector2.new(28, 28));
|
||||
|
||||
SoundManager:CreateSound('MoveSelection');
|
||||
}
|
||||
local DefaultTextLabel = Utility.Create'TextLabel'
|
||||
{
|
||||
Name = "DefaultTextLabel";
|
||||
Size = UDim2.new(1, -spacing.x * 2, 1, -spacing.y * 2);
|
||||
Position = UDim2.new(0, spacing.x, 0, spacing.y);
|
||||
BackgroundTransparency = 1;
|
||||
Font = GlobalSettings.RegularFont;
|
||||
FontSize = GlobalSettings.ButtonSize;
|
||||
TextColor3 = GlobalSettings.TextSelectedColor;
|
||||
TextXAlignment = Enum.TextXAlignment.Left;
|
||||
ZIndex = 2;
|
||||
Parent = TextBoxFrame;
|
||||
}
|
||||
|
||||
local function setTextBoxSizeAndPosition()
|
||||
DefaultTextLabel.Size = UDim2.new(1, -spacing.x * 2, 1, -spacing.y * 2)
|
||||
DefaultTextLabel.Position = UDim2.new(0, spacing.x, 0, spacing.y)
|
||||
SelectionBox.Size = UDim2.new(1, spacing.x * 2 + 24, 1, spacing.y * 2 + 24)
|
||||
SelectionBox.Position = UDim2.new(0, -spacing.x - 12, 0, -spacing.y - 12)
|
||||
end
|
||||
setTextBoxSizeAndPosition()
|
||||
|
||||
--[[ Input ]]--
|
||||
local function onKeyboardClosed(inputText)
|
||||
currentInputText = inputText
|
||||
if #currentInputText == 0 then
|
||||
DefaultTextLabel.Text = this:GetDefaultText()
|
||||
elseif keyboardType == Enum.XboxKeyBoardType.Password then
|
||||
DefaultTextLabel.Text = string.rep("*", #currentInputText)
|
||||
else
|
||||
DefaultTextLabel.Text = currentInputText
|
||||
end
|
||||
DefaultTextLabel.Visible = true
|
||||
Utility.DisconnectEvent(keyboardClosedCn)
|
||||
this.OnTextChanged:fire(currentInputText)
|
||||
end
|
||||
|
||||
TextBoxFrame.MouseButton1Click:connect(function()
|
||||
if isEnabled then
|
||||
if UserSettings().GameSettings:InStudioMode() then
|
||||
print('Warning: virtual keyboard not accessable in studio')
|
||||
elseif PlatformService then
|
||||
DefaultTextLabel.Visible = false
|
||||
PlatformService:ShowKeyboard(keyboardTitle, keyboardDescription,
|
||||
currentInputText or "", keyboardType)
|
||||
end
|
||||
Utility.DisconnectEvent(keyboardClosedCn)
|
||||
if PlatformService then
|
||||
keyboardClosedCn = PlatformService.KeyboardClosed:connect(onKeyboardClosed)
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
TextBoxFrame.SelectionGained:connect(function()
|
||||
Utility.PropertyTweener(TextBoxFrame, "ImageTransparency", GlobalSettings.TextBoxSelectedTransparency,
|
||||
GlobalSettings.TextBoxSelectedTransparency, 0, Utility.EaseInOutQuad, true)
|
||||
end)
|
||||
TextBoxFrame.SelectionLost:connect(function()
|
||||
Utility.PropertyTweener(TextBoxFrame, "ImageTransparency", GlobalSettings.TextBoxDefaultTransparency,
|
||||
GlobalSettings.TextBoxDefaultTransparency, 0, Utility.EaseInOutQuad, true)
|
||||
end)
|
||||
|
||||
function this:SetKeyboardTitle(newTitle)
|
||||
keyboardTitle = newTitle
|
||||
end
|
||||
function this:SetKeyboardDescription(newDescription)
|
||||
keyboardDescription = newDescription
|
||||
end
|
||||
function this:SetKeyboardType(newType)
|
||||
keyboardType = newType
|
||||
end
|
||||
function this:SetEnabled(value)
|
||||
isEnabled = value
|
||||
end
|
||||
function this:SetParent(newParent)
|
||||
TextBoxFrame.Parent = newParent
|
||||
end
|
||||
function this:SetPosition(newPosition)
|
||||
TextBoxFrame.Position = newPosition
|
||||
end
|
||||
function this:SetSpacing(newSpacing)
|
||||
spacing = newSpacing
|
||||
setTextBoxSizeAndPosition()
|
||||
end
|
||||
function this:SetDefaultText(newText)
|
||||
defaultText = newText
|
||||
DefaultTextLabel.Text = defaultText
|
||||
end
|
||||
function this:SetFont(newFont)
|
||||
TextBox.Font = newFont
|
||||
end
|
||||
function this:SetFontSize(newFontSize)
|
||||
TextBox.FontSize = newFontSize
|
||||
end
|
||||
|
||||
function this:GetContainer()
|
||||
return TextBoxFrame
|
||||
end
|
||||
function this:GetTextBox()
|
||||
return TextBox
|
||||
end
|
||||
function this:GetDefaultText()
|
||||
return defaultText
|
||||
end
|
||||
|
||||
return this
|
||||
end
|
||||
|
||||
return createTextBox
|
||||
@@ -0,0 +1,155 @@
|
||||
--[[
|
||||
// ThumbnailLoader.lua
|
||||
|
||||
// Creates a thumbnail loader object that handles the loading
|
||||
// of thumb nails.
|
||||
|
||||
// Thumbnails may not yet be generated, so this will retry generation and
|
||||
// assign the final thumbnail
|
||||
]]
|
||||
local CoreGui = Game:GetService("CoreGui")
|
||||
local GuiRoot = CoreGui:FindFirstChild("RobloxGui")
|
||||
local Modules = GuiRoot:FindFirstChild("Modules")
|
||||
local ContentProvider = game:GetService('ContentProvider')
|
||||
|
||||
local Http = require(Modules:FindFirstChild('Http'))
|
||||
local LoadingWidget = require(Modules:FindFirstChild('LoadingWidget'))
|
||||
local Utility = require(Modules:FindFirstChild('Utility'))
|
||||
|
||||
local ThumbnailLoader = {}
|
||||
|
||||
local BaseUrl = Http.BaseUrl
|
||||
local AssetGameBaseUrl = Http.AssetGameBaseUrl
|
||||
|
||||
local RETRIES = 6
|
||||
local FORMAT = "png"
|
||||
local FADE_IN_TIME = 0.25
|
||||
|
||||
--[[ Sizes ]]--
|
||||
ThumbnailLoader.Sizes = {
|
||||
Small = Vector2.new(100, 100);
|
||||
Medium = Vector2.new(250, 250);
|
||||
Large = Vector2.new(576, 324);
|
||||
}
|
||||
|
||||
ThumbnailLoader.SubstitutionTypeResult = {
|
||||
None = 0;
|
||||
Unapproved = 1;
|
||||
PendingReview = 2;
|
||||
Broken = 3;
|
||||
Unavailable = 4;
|
||||
Unknown = 5;
|
||||
}
|
||||
|
||||
ThumbnailLoader.AssetType = {
|
||||
Icon = { IsFinal = Http.GetAssetThumbnailFinalAsync;
|
||||
SetImageUrl = 'Thumbs/Asset.ashx?width=%d&height=%d&assetId=%d&ignorePlaceMediaItems=true'; };
|
||||
Avatar = { IsFinal = Http.GetAssetAvatarFinalAsync;
|
||||
SetImageUrl = 'Thumbs/Avatar.ashx?width=%d&height=%d&userId=%d&ignorePlaceMediaItems=true'; };
|
||||
Outfit = { IsFinal = Http.GetOutfitThumbnailFinalAsync;
|
||||
SetImageUrl = 'Thumbs/Avatar.ashx?width=%d&height=%d&userId=%d&ignorePlaceMediaItems=true'; };
|
||||
}
|
||||
|
||||
--[[
|
||||
imageObject - a roblox gui image object (ImageLabel, ImageButton)
|
||||
assetId - the id of the asset you want an image for
|
||||
size - a ThumbnailLoader.Sizes
|
||||
assetType - a ThumbnailLoader.AssetType
|
||||
]]
|
||||
function ThumbnailLoader:Create(imageObject, assetId, size, assetType, cachebust)
|
||||
local this = {}
|
||||
|
||||
local isLoading = false
|
||||
local cancelled = false
|
||||
local isFinalSuccess = false
|
||||
local uri = AssetGameBaseUrl..string.format(assetType.SetImageUrl, size.x, size.y, assetId or -1)
|
||||
if cachebust then
|
||||
uri = uri .. '&cb=' .. tostring(tick())
|
||||
end
|
||||
local getIsFinalFunc = assetType.IsFinal
|
||||
|
||||
local function preloadThumbnailAsync()
|
||||
local preloadTable = { uri }
|
||||
ContentProvider:PreloadAsync(preloadTable)
|
||||
end
|
||||
|
||||
local function tryGetFinalAsync()
|
||||
local result = getIsFinalFunc(assetId, size.x, size.y, FORMAT)
|
||||
if result then
|
||||
local isFinal = result["Final"] or result["thumbnailFinal"]
|
||||
local substitutionType = result["substitutionType"] or result["SubstitutionType"]
|
||||
if isFinal == true and
|
||||
(substitutionType == nil or substitutionType == ThumbnailLoader.SubstitutionTypeResult.None) then
|
||||
isFinalSuccess = true
|
||||
preloadThumbnailAsync()
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
local function loadThumbInternalAsync()
|
||||
local tryCount = 0
|
||||
isLoading = true
|
||||
isFinalSuccess = false
|
||||
while tryCount <= RETRIES and isLoading and not cancelled do
|
||||
if tryGetFinalAsync() then
|
||||
break
|
||||
end
|
||||
tryCount = tryCount + 1
|
||||
wait(tryCount ^ 2)
|
||||
end
|
||||
isLoading = false
|
||||
end
|
||||
|
||||
function this:LoadAsync(showSpinner, fadeImage, spinnerProperties)
|
||||
spinnerProperties = spinnerProperties or {}
|
||||
|
||||
if not assetId then return end
|
||||
if showSpinner == nil then
|
||||
showSpinner = true
|
||||
end
|
||||
if fadeImage == nil then
|
||||
fadeImage = true
|
||||
end
|
||||
-- reset image
|
||||
imageObject.Image = ""
|
||||
if fadeImage then
|
||||
local tween = Utility.PropertyTweener(imageObject, "ImageTransparency", 1, 1, 0,
|
||||
Utility.EaseInOutQuad, true, nil)
|
||||
end
|
||||
|
||||
-- try first time before starting loading widget
|
||||
if not tryGetFinalAsync() then
|
||||
if showSpinner then
|
||||
spinnerProperties['Parent'] = spinnerProperties['Parent'] or imageObject
|
||||
local loader = LoadingWidget(
|
||||
spinnerProperties,
|
||||
{ loadThumbInternalAsync } )
|
||||
loader:AwaitFinished()
|
||||
loader:Cleanup()
|
||||
else
|
||||
loadThumbInternalAsync()
|
||||
end
|
||||
end
|
||||
|
||||
if not cancelled then
|
||||
imageObject.Image = isFinalSuccess and uri or ""
|
||||
if fadeImage then
|
||||
local tween = Utility.PropertyTweener(imageObject, "ImageTransparency", 1, 0, FADE_IN_TIME,
|
||||
Utility.EaseInOutQuad, true, nil)
|
||||
end
|
||||
end
|
||||
|
||||
return isFinalSuccess
|
||||
end
|
||||
|
||||
function this:Cancel()
|
||||
isLoading = false
|
||||
cancelled = true
|
||||
end
|
||||
|
||||
return this
|
||||
end
|
||||
|
||||
return ThumbnailLoader
|
||||
@@ -0,0 +1,108 @@
|
||||
--[[
|
||||
// UnlinkAccountOverlay.lua
|
||||
|
||||
// Confirmation overlay for when you unlink your account
|
||||
]]
|
||||
local CoreGui = Game:GetService("CoreGui")
|
||||
local GuiRoot = CoreGui:FindFirstChild("RobloxGui")
|
||||
local Modules = GuiRoot:FindFirstChild("Modules")
|
||||
local GuiService = game:GetService('GuiService')
|
||||
local ContextActionService = game:GetService("ContextActionService")
|
||||
|
||||
local AssetManager = require(Modules:FindFirstChild('AssetManager'))
|
||||
local BaseOverlay = require(Modules:FindFirstChild('BaseOverlay'))
|
||||
local EventHub = require(Modules:FindFirstChild('EventHub'))
|
||||
local GlobalSettings = require(Modules:FindFirstChild('GlobalSettings'))
|
||||
local SoundManager = require(Modules:FindFirstChild('SoundManager'))
|
||||
local ScreenManager = require(Modules:FindFirstChild('ScreenManager'))
|
||||
local Strings = require(Modules:FindFirstChild('LocalizedStrings'))
|
||||
local Utility = require(Modules:FindFirstChild('Utility'))
|
||||
|
||||
local function createUnlinkAccountOverlay(titleAndMsg)
|
||||
local this = BaseOverlay()
|
||||
|
||||
local title = titleAndMsg.Title
|
||||
local message = titleAndMsg.Msg
|
||||
|
||||
local errorIcon = Utility.Create'ImageLabel'
|
||||
{
|
||||
Name = "ReportIcon";
|
||||
Position = UDim2.new(0, 226, 0, 204);
|
||||
BackgroundTransparency = 1;
|
||||
ZIndex = this.BaseZIndex;
|
||||
}
|
||||
AssetManager.LocalImage(errorIcon, 'rbxasset://textures/ui/Shell/Icons/ErrorIconLargeCopy',
|
||||
{['720'] = UDim2.new(0,214,0,176); ['1080'] = UDim2.new(0,321,0,264);})
|
||||
this:SetImage(errorIcon)
|
||||
|
||||
local titleText = Utility.Create'TextLabel'
|
||||
{
|
||||
Name = "TitleText";
|
||||
Size = UDim2.new(0, 0, 0, 0);
|
||||
Position = UDim2.new(0, this.RightAlign, 0, 136);
|
||||
BackgroundTransparency = 1;
|
||||
Font = GlobalSettings.RegularFont;
|
||||
FontSize = GlobalSettings.HeaderSize;
|
||||
TextColor3 = GlobalSettings.WhiteTextColor;
|
||||
Text = title;
|
||||
TextXAlignment = Enum.TextXAlignment.Left;
|
||||
ZIndex = this.BaseZIndex;
|
||||
Parent = this.Container;
|
||||
}
|
||||
|
||||
local descriptionText = Utility.Create'TextLabel'
|
||||
{
|
||||
Name = "DescriptionText";
|
||||
Size = UDim2.new(0, 762, 0, 304);
|
||||
Position = UDim2.new(0, this.RightAlign, 0, titleText.Position.Y.Offset + 62);
|
||||
BackgroundTransparency = 1;
|
||||
TextXAlignment = Enum.TextXAlignment.Left;
|
||||
TextYAlignment = Enum.TextYAlignment.Top;
|
||||
Font = GlobalSettings.LightFont;
|
||||
FontSize = GlobalSettings.TitleSize;
|
||||
TextColor3 = GlobalSettings.WhiteTextColor;
|
||||
TextWrapped = true;
|
||||
Text = message;
|
||||
ZIndex = this.BaseZIndex;
|
||||
Parent = this.Container;
|
||||
}
|
||||
|
||||
local okButton = Utility.Create'TextButton'
|
||||
{
|
||||
Name = "OkButton";
|
||||
Size = UDim2.new(0, 320, 0, 66);
|
||||
Position = UDim2.new(0, this.RightAlign, 1, -100 - 66);
|
||||
BorderSizePixel = 0;
|
||||
BackgroundColor3 = GlobalSettings.BlueButtonColor;
|
||||
Font = GlobalSettings.RegularFont;
|
||||
FontSize = GlobalSettings.ButtonSize;
|
||||
TextColor3 = GlobalSettings.TextSelectedColor;
|
||||
Text = string.upper(Strings:LocalizedString("ConfirmWord"));
|
||||
ZIndex = this.BaseZIndex;
|
||||
Parent = this.Container;
|
||||
|
||||
SoundManager:CreateSound('MoveSelection');
|
||||
}
|
||||
|
||||
--[[ Input Events ]]--
|
||||
local okButtonDebounce
|
||||
okButton.MouseButton1Click:connect(function()
|
||||
if this:Close() then
|
||||
EventHub:dispatchEvent(EventHub.Notifications["UnlinkAccountConfirmation"])
|
||||
end
|
||||
end)
|
||||
|
||||
local baseFocus = this.Focus
|
||||
function this:Focus()
|
||||
baseFocus(self)
|
||||
GuiService.SelectedCoreObject = okButton
|
||||
end
|
||||
|
||||
function this:GetOverlaySound()
|
||||
return 'Error'
|
||||
end
|
||||
|
||||
return this
|
||||
end
|
||||
|
||||
return createUnlinkAccountOverlay
|
||||
@@ -0,0 +1,178 @@
|
||||
--[[
|
||||
// UnlinkAccountScreen.lua
|
||||
]]
|
||||
local CoreGui = Game:GetService("CoreGui")
|
||||
local GuiRoot = CoreGui:FindFirstChild("RobloxGui")
|
||||
local Modules = GuiRoot:FindFirstChild("Modules")
|
||||
|
||||
local ContextActionService = game:GetService('ContextActionService')
|
||||
local GuiService = game:GetService('GuiService')
|
||||
|
||||
local AccountManager = require(Modules:FindFirstChild('AccountManager'))
|
||||
local AssetManager = require(Modules:FindFirstChild('AssetManager'))
|
||||
local BaseScreen = require(Modules:FindFirstChild('BaseScreen'))
|
||||
local Errors = require(Modules:FindFirstChild('Errors'))
|
||||
local ErrorOverlay = require(Modules:FindFirstChild('ErrorOverlay'))
|
||||
local EventHub = require(Modules:FindFirstChild('EventHub'))
|
||||
local GlobalSettings = require(Modules:FindFirstChild('GlobalSettings'))
|
||||
local LoadingWidget = require(Modules:FindFirstChild('LoadingWidget'))
|
||||
local ScreenManager = require(Modules:FindFirstChild('ScreenManager'))
|
||||
local SoundManager = require(Modules:FindFirstChild('SoundManager'))
|
||||
local Strings = require(Modules:FindFirstChild('LocalizedStrings'))
|
||||
local TextBox = require(Modules:FindFirstChild('TextBox'))
|
||||
local UnlinkAccountOverlay = require(Modules:FindFirstChild('UnlinkAccountOverlay'))
|
||||
local Utility = require(Modules:FindFirstChild('Utility'))
|
||||
local UserData = require(Modules:FindFirstChild('UserData'))
|
||||
|
||||
local function createUnlinkAccountScreen()
|
||||
local this = BaseScreen()
|
||||
|
||||
this:SetTitle(string.upper(Strings:LocalizedString("AccountSettingsTitle")))
|
||||
local gamerTag = UserData:GetDisplayName() or ""
|
||||
local robloxName = UserData:GetRobloxName() or ""
|
||||
local linkedAsPhrase = string.format(Strings:LocalizedString('LinkedAsPhrase'), gamerTag, robloxName)
|
||||
local unlinkButtonText = string.format(Strings:LocalizedString("UnlinkGamerTagPhrase"), gamerTag)
|
||||
|
||||
local dummySelection = Utility.Create'Frame'
|
||||
{
|
||||
BackgroundTransparency = 1;
|
||||
}
|
||||
|
||||
local ModalOverlay = Utility.Create'Frame'
|
||||
{
|
||||
Name = "ModalOverlay";
|
||||
Size = UDim2.new(1, 0, 1, 0);
|
||||
BackgroundTransparency = GlobalSettings.ModalBackgroundTransparency;
|
||||
BackgroundColor3 = GlobalSettings.ModalBackgroundColor;
|
||||
BorderSizePixel = 0;
|
||||
ZIndex = 4;
|
||||
}
|
||||
|
||||
local LinkedAsText = Utility.Create'TextLabel'
|
||||
{
|
||||
Name = "LinkedAsText";
|
||||
Size = UDim2.new(0, 0, 0, 0);
|
||||
Position = UDim2.new(0.5, 0, 0, 264);
|
||||
BackgroundTransparency = 1;
|
||||
Font = GlobalSettings.RegularFont;
|
||||
FontSize = GlobalSettings.TitleSize;
|
||||
TextColor3 = GlobalSettings.WhiteTextColor;
|
||||
Text = linkedAsPhrase;
|
||||
Parent = this.Container;
|
||||
}
|
||||
local GamerPic = Utility.Create'ImageLabel'
|
||||
{
|
||||
Name = "GamerPic";
|
||||
Size = UDim2.new(0, 300, 0, 300);
|
||||
BackgroundTransparency = 0;
|
||||
BorderSizePixel = 0;
|
||||
Image = 'rbxapp://xbox/localgamerpic';
|
||||
Parent = this.Container;
|
||||
}
|
||||
Utility.CalculateAnchor(GamerPic, UDim2.new(0.5, 0, 0, LinkedAsText.Position.Y.Offset + 52), Utility.Enum.Anchor.TopMiddle)
|
||||
|
||||
local UnlinkButton = Utility.Create'ImageButton'
|
||||
{
|
||||
Name = "UnlinkButton";
|
||||
Size = UDim2.new(0, 360, 0, 64);
|
||||
BackgroundTransparency = 1;
|
||||
ImageColor3 = GlobalSettings.GreySelectedButtonColor;
|
||||
Image = 'rbxasset://textures/ui/Shell/Buttons/Generic9ScaleButton@720.png';
|
||||
ScaleType = Enum.ScaleType.Slice;
|
||||
SliceCenter = Rect.new(Vector2.new(4, 4), Vector2.new(28, 28));
|
||||
ZIndex = 2;
|
||||
Parent = this.Container;
|
||||
|
||||
SoundManager:CreateSound('MoveSelection');
|
||||
AssetManager.CreateShadow(1)
|
||||
}
|
||||
Utility.CalculateAnchor(UnlinkButton, UDim2.new(0.5, 0, 0, GamerPic.Position.Y.Offset + GamerPic.Size.Y.Offset + 35), Utility.Enum.Anchor.TopMiddle)
|
||||
|
||||
local UnlinkText = Utility.Create'TextLabel'
|
||||
{
|
||||
Name = "UnlinkText";
|
||||
Size = UDim2.new(1, 0, 1, 0);
|
||||
BackgroundTransparency = 1;
|
||||
Font = GlobalSettings.RegularFont;
|
||||
FontSize = GlobalSettings.ButtonSize;
|
||||
TextColor3 = GlobalSettings.TextSelectedColor;
|
||||
Text = string.upper(unlinkButtonText);
|
||||
ZIndex = 2;
|
||||
Parent = UnlinkButton;
|
||||
}
|
||||
|
||||
local isUnlinking = false
|
||||
local function unlinkAccountAsync()
|
||||
if isUnlinking then return end
|
||||
isUnlinking = true
|
||||
local unlinkResult = nil
|
||||
local loader = LoadingWidget(
|
||||
{ Parent = this.Container }, {
|
||||
function()
|
||||
unlinkResult = AccountManager:UnlinkAccountAsync()
|
||||
end
|
||||
})
|
||||
|
||||
-- set up full screen loader
|
||||
ModalOverlay.Parent = GuiRoot
|
||||
ContextActionService:BindCoreAction("BlockB", function() end, false, Enum.KeyCode.ButtonB)
|
||||
UnlinkButton.SelectionImageObject = dummySelection
|
||||
UnlinkButton.ImageColor3 = GlobalSettings.GreyButtonColor
|
||||
UnlinkText.TextColor3 = GlobalSettings.WhiteTextColor
|
||||
|
||||
-- call loader
|
||||
loader:AwaitFinished()
|
||||
|
||||
-- clean up
|
||||
-- NOTE: Unlink success will fire the UserAccountChanged event. This event will fire and listeners will
|
||||
-- run before the loader is finished. The below code needs to run in case of errors, but on success
|
||||
-- will not interfere with the reauth logic in AppHome.lua
|
||||
loader:Cleanup()
|
||||
loader = nil
|
||||
UnlinkButton.SelectionImageObject = nil
|
||||
UnlinkButton.ImageColor3 = GlobalSettings.GreySelectedButtonColor
|
||||
UnlinkText.TextColor3 = GlobalSettings.TextSelectedColor
|
||||
ContextActionService:UnbindCoreAction("BlockB")
|
||||
ModalOverlay.Parent = nil
|
||||
|
||||
if unlinkResult ~= AccountManager.AuthResults.Success then
|
||||
local err = unlinkResult and Errors.Authentication[unlinkResult] or Errors.Default
|
||||
ScreenManager:OpenScreen(ErrorOverlay(err), false)
|
||||
end
|
||||
isUnlinking = false
|
||||
end
|
||||
|
||||
UnlinkButton.MouseButton1Click:connect(function()
|
||||
if isUnlinking then return end
|
||||
SoundManager:Play('ButtonPress')
|
||||
local confirmTitleAndMsg = { Title = Strings:LocalizedString("UnlinkTitle"), Msg = Strings:LocalizedString("UnlinkPhrase") }
|
||||
ScreenManager:OpenScreen(UnlinkAccountOverlay(confirmTitleAndMsg), false)
|
||||
end)
|
||||
|
||||
--[[ Public API ]]--
|
||||
-- Override
|
||||
function this:GetDefaultSelectionObject()
|
||||
return UnlinkButton
|
||||
end
|
||||
|
||||
-- Override
|
||||
local baseFocus = this.Focus
|
||||
function this:Focus()
|
||||
baseFocus(self)
|
||||
EventHub:addEventListener(EventHub.Notifications["UnlinkAccountConfirmation"], "unlinkAccount",
|
||||
function()
|
||||
unlinkAccountAsync()
|
||||
end)
|
||||
end
|
||||
|
||||
-- Override
|
||||
local baseRemoveFocus = this.RemoveFocus
|
||||
function this:RemoveFocus()
|
||||
baseRemoveFocus(self)
|
||||
EventHub:removeEventListener(EventHub.Notifications["UnlinkAccountConfirmation"], "unlinkAccount")
|
||||
end
|
||||
|
||||
return this
|
||||
end
|
||||
|
||||
return createUnlinkAccountScreen
|
||||
@@ -0,0 +1,225 @@
|
||||
--[[
|
||||
// UserData.lua
|
||||
// API for all user related data
|
||||
|
||||
// TODO:
|
||||
Update all local create calls to use GetLocalUserData() and update
|
||||
.Create()
|
||||
Remove all friends stuff and move to new module
|
||||
]]
|
||||
local CoreGui = game:GetService("CoreGui")
|
||||
local GuiRoot = CoreGui:FindFirstChild("RobloxGui")
|
||||
local Modules = GuiRoot:FindFirstChild("Modules")
|
||||
local Players = game:GetService('Players')
|
||||
local HttpService = game:GetService('HttpService')
|
||||
local PlatformService = nil
|
||||
pcall(function() PlatformService = game:GetService('PlatformService') end)
|
||||
|
||||
local AccountManager = require(Modules:FindFirstChild('AccountManager'))
|
||||
local Http = require(Modules:FindFirstChild('Http'))
|
||||
|
||||
local BaseUrl = Http.BaseUrl
|
||||
|
||||
local UserData = {}
|
||||
|
||||
local currentUserData = nil
|
||||
|
||||
local CONSTANT_RETRY_TIME = 30
|
||||
|
||||
local function getLocalPlayer()
|
||||
local plr = Players.LocalPlayer
|
||||
if not plr then
|
||||
while not plr do
|
||||
wait()
|
||||
plr = Players.LocalPlayer
|
||||
end
|
||||
end
|
||||
return plr
|
||||
end
|
||||
|
||||
local function setVoteCountAsync()
|
||||
local voteResult = Http.GetVoteCountAsync()
|
||||
currentUserData["VoteCount"] = voteResult and voteResult['VoteCount'] or 0
|
||||
end
|
||||
|
||||
local function verifyHasLinkedAccountAsync()
|
||||
local result = AccountManager:HasLinkedAccountAsync()
|
||||
|
||||
while result ~= AccountManager.AuthResults.Success and result ~= AccountManager.AuthResults.AccountUnlinked do
|
||||
result = AccountManager:HasLinkedAccountAsync()
|
||||
wait(CONSTANT_RETRY_TIME)
|
||||
end
|
||||
|
||||
currentUserData["LinkedAccountResult"] = result
|
||||
end
|
||||
|
||||
local function verifyHasRobloxCredentialsAsync()
|
||||
local result = AccountManager:HasRobloxCredentialsAsync()
|
||||
|
||||
while result ~= AccountManager.AuthResults.Success and result ~= AccountManager.AuthResults.UsernamePasswordNotSet do
|
||||
result = AccountManager:HasRobloxCredentialsAsync()
|
||||
wait(CONSTANT_RETRY_TIME)
|
||||
end
|
||||
|
||||
currentUserData["RobloxCredentialsResult"] = result
|
||||
end
|
||||
|
||||
function UserData:Initialize()
|
||||
if currentUserData then
|
||||
print("Trying to initialize UserData when we already have valid data.")
|
||||
end
|
||||
|
||||
currentUserData = {}
|
||||
|
||||
if UserSettings().GameSettings:InStudioMode() then
|
||||
local localPlayer = getLocalPlayer()
|
||||
currentUserData["Gamertag"] = "InStudioNoGamertag"
|
||||
currentUserData["RbxUid"] = localPlayer.userId
|
||||
currentUserData["RobloxName"] = localPlayer.Name
|
||||
spawn(function()
|
||||
setVoteCountAsync()
|
||||
end)
|
||||
elseif PlatformService then
|
||||
local userInfo = PlatformService:GetPlatformUserInfo()
|
||||
if userInfo then
|
||||
currentUserData["Gamertag"] = userInfo["Gamertag"]
|
||||
currentUserData["RbxUid"] = userInfo["RobloxUserId"]
|
||||
else
|
||||
currentUserData["Gamertag"] = Players.LocalPlayer.Name
|
||||
end
|
||||
|
||||
spawn(setVoteCountAsync)
|
||||
spawn(verifyHasLinkedAccountAsync)
|
||||
spawn(verifyHasRobloxCredentialsAsync)
|
||||
spawn(function()
|
||||
currentUserData["RobloxName"] = Players:GetNameFromUserIdAsync(currentUserData["RbxUid"])
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
function UserData:GetRbxUserId()
|
||||
if not currentUserData then
|
||||
print("Error: UserData:GetRbxUserId() - UserData has not been initialized. Don't do that!")
|
||||
return nil
|
||||
end
|
||||
return currentUserData["RbxUid"]
|
||||
end
|
||||
|
||||
function UserData:GetDisplayName()
|
||||
if not currentUserData then
|
||||
print("Error: UserData:GetDisplayName() - UserData has not been initialized. Don't do that!")
|
||||
return nil
|
||||
end
|
||||
return currentUserData["Gamertag"]
|
||||
end
|
||||
|
||||
function UserData:GetRobloxName()
|
||||
if not currentUserData then
|
||||
print("Error: UserData:GetRobloxName() - UserData has not been initialized. Don't do that!")
|
||||
return nil
|
||||
end
|
||||
return currentUserData["RobloxName"]
|
||||
end
|
||||
|
||||
function UserData:SetRobloxName(name)
|
||||
if currentUserData then
|
||||
currentUserData["RobloxName"] = name
|
||||
spawn(function()
|
||||
currentUserData["RobloxName"] = Players:GetNameFromUserIdAsync(currentUserData["RbxUid"])
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
function UserData:GetAvatarUrl(width, height)
|
||||
-- TODO: Update when Thumbnail manager gets fixed
|
||||
if not currentUserData then
|
||||
print("Error: UserData:GetAvatarUrl() - UserData has not been initialized. Don't do that!")
|
||||
return nil
|
||||
end
|
||||
return Http.AssetGameBaseUrl..'Thumbs/Avatar.ashx?userid='..tostring(currentUserData.RbxUid)..
|
||||
'&width='..tostring(width)..'&height='..tostring(height)
|
||||
end
|
||||
|
||||
function UserData:GetVoteCount()
|
||||
if not currentUserData then
|
||||
print("Error: UserData:GetVoteCount() - UserData has not been initialized. Don't do that!")
|
||||
return nil
|
||||
end
|
||||
return currentUserData["VoteCount"]
|
||||
end
|
||||
|
||||
function UserData:IncrementVote()
|
||||
currentUserData["VoteCount"] = (currentUserData["VoteCount"] or 0) + 1
|
||||
end
|
||||
|
||||
function UserData:DecrementVote()
|
||||
currentUserData["VoteCount"] = math.max((currentUserData["VoteCount"] or 0) - 1, 0)
|
||||
end
|
||||
|
||||
-- returns true, false or nil in the case of error
|
||||
function UserData:HasLinkedAccount()
|
||||
local result = currentUserData["LinkedAccountResult"]
|
||||
if result == AccountManager.AuthResults.Success then
|
||||
return true
|
||||
elseif result == AccountManager.AuthResults.AccountUnlinked then
|
||||
return false
|
||||
else
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
-- returns true, false or nil in the case of error
|
||||
function UserData:HasRobloxCredentials()
|
||||
local result = currentUserData["RobloxCredentialsResult"]
|
||||
if result == AccountManager.AuthResults.Success then
|
||||
return true
|
||||
elseif result == AccountManager.AuthResults.UsernamePasswordNotSet then
|
||||
return false
|
||||
else
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
function UserData:SetHasRobloxCredentials(value)
|
||||
currentUserData["RobloxCredentialsResult"] = value
|
||||
end
|
||||
|
||||
function UserData:Reset()
|
||||
currentUserData = nil
|
||||
end
|
||||
|
||||
--[[ This should no longer be used ]]--
|
||||
function UserData.GetLocalUserIdAsync()
|
||||
return UserData.GetLocalPlayerAsync().userId
|
||||
end
|
||||
|
||||
function UserData.GetLocalPlayerAsync()
|
||||
local localPlayer = Players.LocalPlayer
|
||||
while not localPlayer do
|
||||
wait()
|
||||
localPlayer = Players.LocalPlayer
|
||||
end
|
||||
return localPlayer
|
||||
end
|
||||
|
||||
function UserData.GetPlatformUserBalanceAsync()
|
||||
local result = Http.GetPlatformUserBalanceAsync()
|
||||
if not result then
|
||||
-- TODO: Error Code
|
||||
return nil
|
||||
end
|
||||
--
|
||||
|
||||
return result["Robux"]
|
||||
end
|
||||
|
||||
function UserData.GetTotalUserBalanceAsync()
|
||||
local result = Http.GetTotalUserBalanceAsync()
|
||||
if not result then
|
||||
return nil
|
||||
end
|
||||
|
||||
return result["robux"]
|
||||
end
|
||||
|
||||
return UserData
|
||||
@@ -0,0 +1,470 @@
|
||||
-- Written by Kip Turner, Copyright ROBLOX 2015
|
||||
local RunService = game:GetService('RunService')
|
||||
local GuiService = game:GetService('GuiService')
|
||||
|
||||
local Util = {}
|
||||
do
|
||||
|
||||
function Util.IsFinite(num)
|
||||
return num == num and num ~= 1/0 and num ~= -1/0
|
||||
end
|
||||
|
||||
function Util.CalculateRelativeDimensions(guiObject, guiDims, mockup_dims)
|
||||
local guiResolution = GuiService:GetScreenResolution()
|
||||
local absolutePercentSize = (guiDims / mockup_dims)
|
||||
if mockup_dims.y > 0 and guiResolution.y > 0 then
|
||||
local mockupAspectRatio = mockup_dims.x / mockup_dims.y
|
||||
local globalAspectRatio = guiResolution.x / guiResolution.y
|
||||
absolutePercentSize = absolutePercentSize * (mockupAspectRatio / globalAspectRatio)
|
||||
local parentObject = guiObject.Parent
|
||||
if parentObject then
|
||||
local parentPercentScreen = parentObject.AbsoluteSize / guiResolution
|
||||
local parentSizeInverse = 1 / parentPercentScreen
|
||||
if Util.IsFinite(parentSizeInverse.x) and Util.IsFinite(parentSizeInverse.y) then
|
||||
return UDim2.new(parentSizeInverse.x * absolutePercentSize.x, 0, parentSizeInverse.y * absolutePercentSize.y, 0)
|
||||
end
|
||||
end
|
||||
end
|
||||
return UDim2.new(absolutePercentSize.x, 0, absolutePercentSize.y, 0)
|
||||
end
|
||||
|
||||
|
||||
-- Anchor Graph
|
||||
-- 1 2 3
|
||||
-- 4 5 6
|
||||
-- 7 8 9
|
||||
|
||||
Util.Enum =
|
||||
{
|
||||
Anchor =
|
||||
{
|
||||
TopLeft = 1;
|
||||
TopMiddle = 2;
|
||||
TopRight = 3;
|
||||
CenterLeft = 4;
|
||||
Center = 5;
|
||||
CenterRight = 6;
|
||||
BottomLeft = 7;
|
||||
BottomMiddle = 8;
|
||||
BottomRight = 9;
|
||||
};
|
||||
};
|
||||
|
||||
function Util.CalculateAnchor(imageObject, position, anchorType)
|
||||
-- TODO: make this work with relativeXX, relativeYY
|
||||
if anchorType == Util.Enum.Anchor.TopLeft then
|
||||
imageObject.Position = position
|
||||
elseif anchorType == Util.Enum.Anchor.TopMiddle then
|
||||
imageObject.Position = position + UDim2.new(-imageObject.Size.X.Scale / 2, -imageObject.Size.X.Offset / 2,
|
||||
0, 0);
|
||||
elseif anchorType == Util.Enum.Anchor.TopRight then
|
||||
imageObject.Position = position + UDim2.new(imageObject.Size.X.Scale, -imageObject.Size.X.Offset,
|
||||
0, 0);
|
||||
elseif anchorType == Util.Enum.Anchor.CenterLeft then
|
||||
imageObject.Position = position + UDim2.new(0, 0,
|
||||
-imageObject.Size.Y.Scale / 2, -imageObject.Size.Y.Offset / 2);
|
||||
elseif anchorType == Util.Enum.Anchor.Center then
|
||||
imageObject.Position = position + UDim2.new(-imageObject.Size.X.Scale / 2, -imageObject.Size.X.Offset / 2,
|
||||
-imageObject.Size.Y.Scale / 2, -imageObject.Size.Y.Offset / 2);
|
||||
elseif anchorType == Util.Enum.Anchor.CenterRight then
|
||||
imageObject.Position = position + UDim2.new(-imageObject.Size.X.Scale, -imageObject.Size.X.Offset,
|
||||
-imageObject.Size.Y.Scale / 2, -imageObject.Size.Y.Offset / 2);
|
||||
elseif anchorType == Util.Enum.Anchor.BottomLeft then
|
||||
imageObject.Position = position + UDim2.new(0, 0,
|
||||
-imageObject.Size.Y.Scale, -imageObject.Size.Y.Offset);
|
||||
elseif anchorType == Util.Enum.Anchor.BottomMiddle then
|
||||
imageObject.Position = position + UDim2.new(-imageObject.Size.X.Scale / 2, -imageObject.Size.X.Offset / 2,
|
||||
-imageObject.Size.Y.Scale, -imageObject.Size.Y.Offset);
|
||||
elseif anchorType == Util.Enum.Anchor.BottomRight then
|
||||
imageObject.Position = position + UDim2.new(-imageObject.Size.X.Scale, -imageObject.Size.X.Offset,
|
||||
-imageObject.Size.Y.Scale, -imageObject.Size.Y.Offset);
|
||||
end
|
||||
end
|
||||
|
||||
function Util.CalculateFit(containerObject, rawImageSize)
|
||||
local absSize = containerObject.AbsoluteSize
|
||||
local scalar = absSize / rawImageSize
|
||||
local fixedSize = rawImageSize * math.min(scalar.X, scalar.Y)
|
||||
|
||||
return UDim2.new(0, fixedSize.X , 0, fixedSize.Y)
|
||||
end
|
||||
|
||||
function Util.CalculateFill(containerObject, rawImageSize)
|
||||
local absSize = containerObject.AbsoluteSize
|
||||
local scalar = absSize / rawImageSize
|
||||
local fixedSize = rawImageSize * math.max(scalar.X, scalar.Y)
|
||||
|
||||
return UDim2.new(0, fixedSize.X , 0, fixedSize.Y)
|
||||
end
|
||||
|
||||
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
|
||||
|
||||
---- TWEENING ----
|
||||
local ActiveTweens = {}
|
||||
|
||||
local function getActiveTween(prop, instance)
|
||||
return ActiveTweens[prop] and ActiveTweens[prop][instance]
|
||||
end
|
||||
|
||||
local function setActiveTween(prop, instance, newTween)
|
||||
if not ActiveTweens[prop] then
|
||||
ActiveTweens[prop] = {}
|
||||
end
|
||||
ActiveTweens[prop][instance] = newTween
|
||||
end
|
||||
|
||||
function Util.Linear(t, b, c, d)
|
||||
if t >= d then return b + c end
|
||||
|
||||
return c*t/d + b
|
||||
end
|
||||
|
||||
function Util.EaseOutQuad(t, b, c, d)
|
||||
if t >= d then return b + c end
|
||||
|
||||
t = t/d;
|
||||
return -c * t*(t-2) + b
|
||||
end
|
||||
|
||||
function Util.EaseInOutQuad(t, b, c, d)
|
||||
if t >= d then return b + c end
|
||||
|
||||
t = t / (d/2);
|
||||
if (t < 1) then return c/2*t*t + b end;
|
||||
t = t - 1;
|
||||
return -c/2 * (t*(t-2) - 1) + b;
|
||||
end
|
||||
|
||||
function Util.PropertyTweener(instance, prop, start, final, duration, easingFunc, override, callbackFunction)
|
||||
easingFunc = easingFunc or Util.Linear
|
||||
override = override or false
|
||||
|
||||
local this = {}
|
||||
this.StartTime = tick()
|
||||
this.EndTime = this.StartTime + duration
|
||||
this.Cancelled = false
|
||||
|
||||
local finished = false
|
||||
local percentComplete = 0
|
||||
|
||||
|
||||
local function setValue(newValue)
|
||||
if instance then
|
||||
instance[prop] = newValue
|
||||
end
|
||||
end
|
||||
|
||||
local function finalize()
|
||||
setValue(easingFunc(1, start, final - start, 1))
|
||||
finished = true
|
||||
percentComplete = 1
|
||||
|
||||
if getActiveTween(prop, instance) == this then
|
||||
setActiveTween(prop, instance, nil)
|
||||
end
|
||||
|
||||
if callbackFunction then
|
||||
callbackFunction()
|
||||
end
|
||||
end
|
||||
|
||||
if override or not getActiveTween(prop, instance) then
|
||||
if getActiveTween(prop, instance) then
|
||||
getActiveTween(prop, instance):Cancel()
|
||||
end
|
||||
setActiveTween(prop, instance, this)
|
||||
|
||||
-- Initial set
|
||||
setValue(easingFunc(0, start, final - start, duration))
|
||||
spawn(function()
|
||||
local now = tick()
|
||||
while now < this.EndTime and instance and not this.Cancelled do
|
||||
setValue(easingFunc(now - this.StartTime, start, final - start, duration))
|
||||
percentComplete = Util.Clamp(0, 1, (now - this.StartTime) / duration)
|
||||
RunService.RenderStepped:wait()
|
||||
now = tick()
|
||||
end
|
||||
if this.Cancelled == false and instance then
|
||||
finalize()
|
||||
end
|
||||
|
||||
if getActiveTween(prop, instance) == this then
|
||||
setActiveTween(prop, instance, nil)
|
||||
end
|
||||
end)
|
||||
else
|
||||
finished = true
|
||||
end
|
||||
|
||||
function this:GetFinal()
|
||||
return final
|
||||
end
|
||||
|
||||
function this:GetPercentComplete()
|
||||
return percentComplete
|
||||
end
|
||||
|
||||
function this:IsFinished()
|
||||
return finished
|
||||
end
|
||||
|
||||
function this:Finish()
|
||||
if not finished then
|
||||
self:Cancel()
|
||||
finalize()
|
||||
end
|
||||
end
|
||||
|
||||
function this:Cancel()
|
||||
this.Cancelled = true
|
||||
finished = true
|
||||
if getActiveTween(prop, instance) == this then
|
||||
setActiveTween(prop, instance, nil)
|
||||
end
|
||||
end
|
||||
|
||||
return this
|
||||
end
|
||||
---------------
|
||||
|
||||
--- EVENTS ----
|
||||
function Util.Signal()
|
||||
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
|
||||
|
||||
function Util.DisconnectEvent(conn)
|
||||
if conn then
|
||||
conn:disconnect()
|
||||
end
|
||||
return nil
|
||||
end
|
||||
--------------
|
||||
|
||||
-- MATH --
|
||||
function Util.Clamp(low, high, input)
|
||||
return math.max(low, math.min(high, input))
|
||||
end
|
||||
|
||||
function Util.ClampVector2(low, high, input)
|
||||
return Vector2.new(Util.Clamp(low.x, high.x, input.x), Util.Clamp(low.y, high.y, input.y))
|
||||
end
|
||||
|
||||
function Util.TweenPositionOrSet(guiObject, ...)
|
||||
if guiObject:IsDescendantOf(game) then
|
||||
guiObject:TweenPosition(...)
|
||||
else
|
||||
guiObject.Position = select(1, ...)
|
||||
end
|
||||
end
|
||||
----
|
||||
|
||||
function Util.ClampCanvasPosition(scrollingContainer, position)
|
||||
local container = scrollingContainer
|
||||
local parentSize = container.Parent and container.Parent.AbsoluteSize or Vector2.new(0,0)
|
||||
local absoluteCanvasSize = Vector2.new(container.CanvasSize.X.Scale * parentSize.X + container.CanvasSize.X.Offset,
|
||||
container.CanvasSize.Y.Scale * parentSize.Y + container.CanvasSize.Y.Offset)
|
||||
local nextX = Util.Clamp(0, absoluteCanvasSize.X - container.AbsoluteWindowSize.X, position.X)
|
||||
local nextY = Util.Clamp(0, absoluteCanvasSize.Y - container.AbsoluteWindowSize.Y, position.Y)
|
||||
|
||||
return Vector2.new(nextX, nextY)
|
||||
end
|
||||
|
||||
function Util.Round(num, roundToNearest)
|
||||
roundToNearest = roundToNearest or 1
|
||||
return math.floor((num + roundToNearest/2) / roundToNearest) * roundToNearest
|
||||
end
|
||||
--------------
|
||||
|
||||
-- FORMATING --
|
||||
-- Removed whitespace from the beginning and end of the string
|
||||
function Util.ChompString(str)
|
||||
return tostring(str):gsub("^%s+" , ""):gsub("%s+$" , "")
|
||||
end
|
||||
|
||||
function Util.FormatNumberString(value)
|
||||
-- Make sure beginning and end of the string is clipped
|
||||
local stringValue = Util.ChompString(value)
|
||||
return stringValue:reverse():gsub("%d%d%d", "%1,"):reverse():gsub("^,", "")
|
||||
end
|
||||
|
||||
-- PrettyPrint function for formatting data structures into flat strings, usefull for debugging
|
||||
|
||||
local function PrettyPrint(tb)
|
||||
if type(tb) == 'table' then
|
||||
local str = "{"
|
||||
for k, v in pairs(tb) do
|
||||
str = ((str == "{") and str or str..", ")
|
||||
if type(k) == 'string' then
|
||||
str = str..k.." = "
|
||||
elseif type(k) == 'number' then
|
||||
-- nothing
|
||||
else
|
||||
str = str.."["..k.."] = "
|
||||
end
|
||||
str = str..PrettyPrint(v)
|
||||
end
|
||||
return str.."}"
|
||||
elseif type(tb) == 'string' then
|
||||
return "'"..tb.."'"
|
||||
else
|
||||
return tostring(tb)
|
||||
end
|
||||
end
|
||||
|
||||
Util.PrettyPrint = PrettyPrint
|
||||
|
||||
|
||||
-- K is a tunable parameter that changes the shape of the S-curve
|
||||
-- the larger K is the more straight/linear the curve gets
|
||||
local function SCurveTranform(t, k, lowerK)
|
||||
k = k or 0.35
|
||||
lowerK = lowerK or 0.8
|
||||
t = Util.Clamp(-1,1,t)
|
||||
if t >= 0 then
|
||||
return (k*t) / (k - t + 1)
|
||||
end
|
||||
return -((lowerK*-t) / (lowerK + t + 1))
|
||||
end
|
||||
|
||||
local function toSCurveSpace(t, deadzone)
|
||||
deadzone = deadzone or 0.1
|
||||
return (1 + deadzone) * (2*math.abs(t) - 1) - deadzone
|
||||
end
|
||||
|
||||
local function fromSCurveSpace(t)
|
||||
return t/2 + 0.5
|
||||
end
|
||||
|
||||
function Util.GamepadLinearToCurve(thumbstickPosition, deadzone, k, lowerK)
|
||||
local function onAxis(axisValue)
|
||||
local sign = axisValue < 0 and -1 or 1
|
||||
|
||||
local point = fromSCurveSpace(SCurveTranform(toSCurveSpace(math.abs(axisValue), deadzone)), k, lowerK)
|
||||
return Util.Clamp(-1,1, point * sign)
|
||||
end
|
||||
return Vector2.new(onAxis(thumbstickPosition.x), onAxis(thumbstickPosition.y))
|
||||
end
|
||||
|
||||
function Util.IsFastFlagEnabled(flagName)
|
||||
local success, isFlagEnabled = pcall(function()
|
||||
return settings():GetFFlag(flagName)
|
||||
end)
|
||||
|
||||
if success and not isFlagEnabled then
|
||||
print("Fast Flag:", flagName, "is currently not enabled.")
|
||||
elseif not success then
|
||||
print("GetFFlag failed for flag:", flagName)
|
||||
end
|
||||
|
||||
return success and isFlagEnabled
|
||||
end
|
||||
function Util.GetFastVariable(variableName)
|
||||
local success, value = pcall(function()
|
||||
return settings():GetFVariable(variableName)
|
||||
end)
|
||||
|
||||
return success and value
|
||||
end
|
||||
|
||||
function Util.ExponentialRepeat(loopPredicate, loopBody, repeatCount)
|
||||
repeatCount = repeatCount or 6
|
||||
local retryCount = 1
|
||||
local numRetries = repeatCount
|
||||
|
||||
while retryCount <= numRetries and loopPredicate() do
|
||||
local done = loopBody()
|
||||
if done then return end
|
||||
wait(retryCount ^ 2)
|
||||
retryCount = retryCount + 1
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
function Util.SplitString(str, sep)
|
||||
local result = {}
|
||||
if str and sep then
|
||||
for word in string.gmatch(str, '([^' .. sep .. ']+)') do
|
||||
table.insert(result, word)
|
||||
end
|
||||
end
|
||||
return result
|
||||
end
|
||||
|
||||
|
||||
local function findAssetsHelper(object, result, baseUrl)
|
||||
if not object then return end
|
||||
|
||||
if object:IsA('CharacterMesh') then
|
||||
table.insert(result, baseUrl .. tostring(object.MeshId))
|
||||
table.insert(result, baseUrl .. tostring(object.BaseTextureId))
|
||||
table.insert(result, baseUrl .. tostring(object.OverlayTextureId))
|
||||
elseif object:IsA('FileMesh') then
|
||||
table.insert(result, object.MeshId)
|
||||
table.insert(result, object.TextureId)
|
||||
elseif object:IsA('Decal') then
|
||||
table.insert(result, object.Texture)
|
||||
elseif object:IsA('Pants') then
|
||||
table.insert(result, object.PantsTemplate)
|
||||
elseif object:IsA('Shirt') then
|
||||
table.insert(result, object.ShirtTemplate)
|
||||
end
|
||||
|
||||
for _, child in pairs(object:GetChildren()) do
|
||||
findAssetsHelper(child, result, baseUrl)
|
||||
end
|
||||
end
|
||||
function Util.FindAssetsInModel(object, baseUrl)
|
||||
baseUrl = baseUrl or 'http://www.watrbx.wtf/asset/?id='
|
||||
local result = {}
|
||||
findAssetsHelper(object, result, baseUrl)
|
||||
return result
|
||||
end
|
||||
|
||||
function Util.ConvertFontSizeEnumToInt(fontSizeEnum)
|
||||
local name = fontSizeEnum.Name
|
||||
-- TODO: this is sort of gross?
|
||||
local result = string.match(name, '%d+')
|
||||
return result or 10
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
return Util
|
||||
@@ -0,0 +1,74 @@
|
||||
--[[
|
||||
// VoteFrame.lua
|
||||
// Creates a vote frame for a game
|
||||
]]
|
||||
local CoreGui = Game:GetService("CoreGui")
|
||||
local GuiRoot = CoreGui:FindFirstChild("RobloxGui")
|
||||
local Modules = GuiRoot:FindFirstChild("Modules")
|
||||
|
||||
local AssetManager = require(Modules:FindFirstChild('AssetManager'))
|
||||
local GlobalSettings = require(Modules:FindFirstChild('GlobalSettings'))
|
||||
local Utility = require(Modules:FindFirstChild('Utility'))
|
||||
|
||||
local CreateVoteFrame = function(parent, position)
|
||||
local this = {}
|
||||
|
||||
-- Assume 1080p
|
||||
local MAX_SIZE = 203
|
||||
|
||||
local voteContainer = Utility.Create'Frame'
|
||||
{
|
||||
Name = "VoteContainer";
|
||||
Size = UDim2.new(0, MAX_SIZE, 0, 16);
|
||||
Position = position;
|
||||
BackgroundTransparency = 1;
|
||||
Visible = false;
|
||||
Parent = parent;
|
||||
}
|
||||
local batteryImageRed = Utility.Create'ImageLabel'
|
||||
{
|
||||
Name = "BatteryImageRed";
|
||||
BackgroundTransparency = 1;
|
||||
ImageColor3 = GlobalSettings.RedTextColor;
|
||||
Parent = voteContainer;
|
||||
}
|
||||
|
||||
AssetManager.LocalImage(batteryImageRed,
|
||||
'rbxasset://textures/ui/Shell/Icons/RatingBar', {['720'] = UDim2.new(0,134,0,11); ['1080'] = UDim2.new(0,203,0,16);})
|
||||
local batteryImageGreen = batteryImageRed:Clone()
|
||||
batteryImageGreen.ImageColor3 = GlobalSettings.GreenTextColor
|
||||
batteryImageGreen.ZIndex = 2
|
||||
batteryImageGreen.Parent = batteryImageRed
|
||||
|
||||
--[[ Public API ]]--
|
||||
function this:SetPercentFilled(percent)
|
||||
if not percent then
|
||||
batteryImageGreen.Size = batteryImageRed.Size
|
||||
batteryImageGreen.ImageRectSize = Vector2.new(0, 0)
|
||||
batteryImageGreen.ImageColor3 = GlobalSettings.GreyTextColor
|
||||
else
|
||||
percent = Utility.Round(percent, 0.1)
|
||||
local drawSize = math.floor(percent * MAX_SIZE)
|
||||
batteryImageGreen.ImageColor3 = GlobalSettings.GreenTextColor
|
||||
batteryImageGreen.Size = UDim2.new(0, drawSize, 0, batteryImageGreen.Size.Y.Offset)
|
||||
batteryImageGreen.ImageRectSize = Vector2.new(drawSize, 0)
|
||||
end
|
||||
end
|
||||
|
||||
function this:SetVisible(value)
|
||||
voteContainer.Visible = value
|
||||
end
|
||||
|
||||
function this:GetContainer()
|
||||
return voteContainer
|
||||
end
|
||||
|
||||
function this:Destroy()
|
||||
voteContainer:Destroy()
|
||||
this = nil
|
||||
end
|
||||
|
||||
return this
|
||||
end
|
||||
|
||||
return CreateVoteFrame
|
||||
@@ -0,0 +1,278 @@
|
||||
--[[
|
||||
// VoteView.lua
|
||||
|
||||
// Manages the vote view for the game details page
|
||||
]]
|
||||
local CoreGui = Game:GetService("CoreGui")
|
||||
local GuiRoot = CoreGui:FindFirstChild("RobloxGui")
|
||||
local Modules = GuiRoot:FindFirstChild("Modules")
|
||||
|
||||
local EventHub = require(Modules:FindFirstChild('EventHub'))
|
||||
local GlobalSettings = require(Modules:FindFirstChild('GlobalSettings'))
|
||||
local Strings = require(Modules:FindFirstChild('LocalizedStrings'))
|
||||
local Utility = require(Modules:FindFirstChild('Utility'))
|
||||
local VoteFrame = require(Modules:FindFirstChild('VoteFrame'))
|
||||
local AssetManager = require(Modules:FindFirstChild('AssetManager'))
|
||||
local ScreenManager = require(Modules:FindFirstChild('ScreenManager'))
|
||||
local ErrorOverlayModule = require(Modules:FindFirstChild('ErrorOverlay'))
|
||||
local Errors = require(Modules:FindFirstChild('Errors'))
|
||||
local SoundManager = require(Modules:FindFirstChild('SoundManager'))
|
||||
local UserData = require(Modules:FindFirstChild('UserData'))
|
||||
|
||||
local function createVoteView()
|
||||
local this = {}
|
||||
|
||||
local canVote = false
|
||||
local myVote = nil
|
||||
local upVotes = 0
|
||||
local downVotes = 0
|
||||
local thisGameData = nil
|
||||
local defaultSelection = nil
|
||||
|
||||
this.Container = Utility.Create'Frame'
|
||||
{
|
||||
Name = "VoteContainer";
|
||||
Size = UDim2.new(1, 0, 0, 114);
|
||||
BackgroundTransparency = 1;
|
||||
Visible = false;
|
||||
}
|
||||
|
||||
-- Can't Vote Objects
|
||||
-- override selection image
|
||||
local SelectionImage = Utility.Create'ImageLabel'
|
||||
{
|
||||
Name = "SelectionImage";
|
||||
Size = UDim2.new(1, 32, 1, 32);
|
||||
Position = UDim2.new(0, -16, 0, -16);
|
||||
Image = 'rbxasset://textures/ui/SelectionBox.png';
|
||||
ScaleType = Enum.ScaleType.Slice;
|
||||
SliceCenter = Rect.new(21,21,41,41);
|
||||
BackgroundTransparency = 1;
|
||||
}
|
||||
local CannotVoteSelection = Utility.Create'Frame'
|
||||
{
|
||||
Name = "CannotVoteSelection";
|
||||
Size = UDim2.new(1, 0, 0, 94);
|
||||
Position = UDim2.new(0, 0, 0, 5);
|
||||
BackgroundTransparency = 1;
|
||||
Selectable = true;
|
||||
SelectionImageObject = SelectionImage;
|
||||
SoundManager:CreateSound('MoveSelection');
|
||||
}
|
||||
local CannotVoteText = Utility.Create'TextLabel'
|
||||
{
|
||||
Name = "CannotVoteText";
|
||||
Size = UDim2.new(0.8, 0, 1, 0);
|
||||
Position = UDim2.new(0.1, 0, 0, 0);
|
||||
BackgroundTransparency = 1;
|
||||
Font = GlobalSettings.BoldFont;
|
||||
FontSize = GlobalSettings.SubHeaderSize;
|
||||
TextColor3 = GlobalSettings.WhiteTextColor;
|
||||
Visible = false;
|
||||
TextWrapped = true;
|
||||
Text = Strings:LocalizedString("CannotVoteWord");
|
||||
Parent = CannotVoteSelection;
|
||||
}
|
||||
|
||||
-- Vote Objects
|
||||
local VoteWidget = VoteFrame(this.Container, UDim2.new(0, 88, 0, 34))
|
||||
local VoteContainer = VoteWidget:GetContainer()
|
||||
local ThumbsUpImage = Utility.Create'ImageLabel'
|
||||
{
|
||||
Name = "ThumbsUpImage";
|
||||
Size = UDim2.new(0, 48, 0, 48);
|
||||
BackgroundTransparency = 1;
|
||||
}
|
||||
local ThumbsDownImage = ThumbsUpImage:Clone()
|
||||
ThumbsDownImage.Name = "ThumbsDownImage"
|
||||
-- Buttons act as buffers for selection gui
|
||||
local ThumbsUpButton = Utility.Create'ImageButton'
|
||||
{
|
||||
Name = "ThumbsUpButton";
|
||||
Size = UDim2.new(0, ThumbsUpImage.Size.X.Offset + 18, 0, ThumbsUpImage.Size.Y.Offset + 18);
|
||||
BackgroundTransparency = 1;
|
||||
Image = "";
|
||||
Parent = VoteContainer;
|
||||
SoundManager:CreateSound('MoveSelection');
|
||||
}
|
||||
ThumbsUpButton.Position = UDim2.new(0, -ThumbsUpButton.Size.X.Offset - 12, 0,
|
||||
-ThumbsUpButton.Size.Y.Offset / 2 + VoteContainer.Size.Y.Offset / 2)
|
||||
local ThumbsDownButton = ThumbsUpButton:Clone()
|
||||
ThumbsDownButton.Name = "ThumbsDownButton"
|
||||
ThumbsDownButton.Position = UDim2.new(1, 12, 0, ThumbsDownButton.Position.Y.Offset)
|
||||
ThumbsDownButton.Parent = VoteContainer
|
||||
|
||||
ThumbsUpImage.Parent = ThumbsUpButton
|
||||
ThumbsDownImage.Parent = ThumbsDownButton
|
||||
ThumbsUpImage.Position = UDim2.new(0.5, -ThumbsUpImage.Size.X.Offset / 2, 0.5, -ThumbsUpImage.Size.Y.Offset / 2)
|
||||
ThumbsDownImage.Position = UDim2.new(0.5, -ThumbsDownImage.Size.X.Offset / 2, 0.5, -ThumbsDownImage.Size.Y.Offset / 2)
|
||||
|
||||
local RatingText = Utility.Create'TextLabel'
|
||||
{
|
||||
Name = "RatingText";
|
||||
Size = UDim2.new(0, 0, 0, 0);
|
||||
Position = UDim2.new(0, VoteContainer.Position.X.Offset + VoteContainer.Size.X.Offset / 2, 1, -20);
|
||||
BackgroundTransparency = 1;
|
||||
Font = GlobalSettings.BoldFont;
|
||||
FontSize = GlobalSettings.SubHeaderSize;
|
||||
Text = "";
|
||||
Parent = this.Container;
|
||||
}
|
||||
local UpCountText = Utility.Create'TextLabel'
|
||||
{
|
||||
Name = "UpCountText";
|
||||
Size = UDim2.new(0, 0, 0, 0);
|
||||
Position = UDim2.new(0, 43, 1, RatingText.Position.Y.Offset);
|
||||
BackgroundTransparency = 1;
|
||||
Font = GlobalSettings.RegularFont;
|
||||
FontSize = GlobalSettings.SubHeaderSize;
|
||||
TextColor3 = GlobalSettings.GreenTextColor;
|
||||
Text = "";
|
||||
Parent = this.Container;
|
||||
}
|
||||
local DownCountText = UpCountText:Clone()
|
||||
DownCountText.Name = "DownCountText"
|
||||
DownCountText.Position = UDim2.new(0, 336, 1, RatingText.Position.Y.Offset)
|
||||
DownCountText.TextColor3 = GlobalSettings.RedTextColor;
|
||||
DownCountText.Parent = this.Container;
|
||||
|
||||
local function setImagesAndText(vote, noVote)
|
||||
AssetManager.LocalImage(ThumbsUpImage,
|
||||
(vote == true and 'rbxasset://textures/ui/Shell/Icons/ThumbsUpFilled') or
|
||||
'rbxasset://textures/ui/Shell/Icons/ThumbsUp', {['720'] = UDim2.new(0,32,0,32); ['1080'] = UDim2.new(0,48,0,48);})
|
||||
AssetManager.LocalImage(ThumbsDownImage,
|
||||
(vote == false and 'rbxasset://textures/ui/Shell/Icons/ThumbsDownFilled') or
|
||||
'rbxasset://textures/ui/Shell/Icons/ThumbsDown', {['720'] = UDim2.new(0,32,0,32); ['1080'] = UDim2.new(0,48,0,48);})
|
||||
RatingText.TextColor3 =
|
||||
(vote == true and GlobalSettings.GreenTextColor) or
|
||||
(vote == false and GlobalSettings.RedTextColor) or GlobalSettings.GreyTextColor
|
||||
local textWord = (noVote and "FirstToRateWord") or
|
||||
(vote == true and "LikedWord") or (vote == false and "DislikedWord") or nil
|
||||
RatingText.Text = textWord and Strings:LocalizedString(textWord) or ""
|
||||
end
|
||||
|
||||
local function updateView(newVote)
|
||||
local noVote = upVotes == 0 and downVotes == 0
|
||||
|
||||
VoteWidget:SetPercentFilled(not noVote and (upVotes / (upVotes + downVotes)) or nil)
|
||||
setImagesAndText(newVote, noVote)
|
||||
UpCountText.Text = Utility.FormatNumberString(noVote and "" or upVotes)
|
||||
DownCountText.Text = Utility.FormatNumberString(noVote and "" or downVotes)
|
||||
|
||||
ThumbsUpButton.Selectable = canVote
|
||||
ThumbsDownButton.Selectable = canVote
|
||||
|
||||
CannotVoteSelection.Parent = not canVote and this.Container.Parent or nil
|
||||
defaultSelection = not canVote and CannotVoteSelection or ThumbsUpButton
|
||||
end
|
||||
|
||||
local function updateVoteCount(prevVote, newVote)
|
||||
-- we must check for a unique vote before sending off the achivement event
|
||||
-- unique votes are when prevVote is null.
|
||||
if prevVote == "null" then
|
||||
UserData:IncrementVote()
|
||||
elseif newVote == "null" then
|
||||
UserData:DecrementVote()
|
||||
end
|
||||
EventHub:dispatchEvent(EventHub.Notifications["VotedOnPlace"])
|
||||
end
|
||||
|
||||
local isVotingDebouce = false
|
||||
local function postVoteAsync(newVote)
|
||||
if isVotingDebouce then return end
|
||||
isVotingDebouce = true
|
||||
local success, reason = nil, nil
|
||||
if thisGameData then
|
||||
success, reason = thisGameData:PostVoteAsync(newVote)
|
||||
else
|
||||
success = false
|
||||
end
|
||||
if success then
|
||||
local prevVote = myVote
|
||||
if newVote == true then
|
||||
upVotes = upVotes + 1
|
||||
if prevVote == false then
|
||||
downVotes = downVotes - 1
|
||||
end
|
||||
elseif newVote == false then
|
||||
downVotes = downVotes + 1
|
||||
if prevVote == true then
|
||||
upVotes = upVotes - 1
|
||||
end
|
||||
elseif newVote == "null" then
|
||||
if prevVote == true then
|
||||
upVotes = upVotes - 1
|
||||
elseif prevVote == false then
|
||||
downVotes = downVotes - 1
|
||||
end
|
||||
end
|
||||
myVote = newVote
|
||||
updateView(newVote)
|
||||
updateVoteCount(prevVote, newVote)
|
||||
else
|
||||
if reason and Errors.Vote[reason] then
|
||||
ScreenManager:OpenScreen(ErrorOverlayModule(Errors.Vote[reason]), false)
|
||||
else
|
||||
ScreenManager:OpenScreen(ErrorOverlayModule(Errors.Default), false)
|
||||
end
|
||||
end
|
||||
isVotingDebouce = false
|
||||
end
|
||||
|
||||
local function toggleCannotVoteDisplay()
|
||||
CannotVoteText.Visible = not CannotVoteText.Visible
|
||||
this.Container.Visible = not this.Container.Visible
|
||||
end
|
||||
CannotVoteSelection.SelectionGained:connect(toggleCannotVoteDisplay)
|
||||
CannotVoteSelection.SelectionLost:connect(toggleCannotVoteDisplay)
|
||||
|
||||
ThumbsUpButton.MouseButton1Click:connect(function()
|
||||
postVoteAsync(myVote == true and "null" or true)
|
||||
end)
|
||||
ThumbsDownButton.MouseButton1Click:connect(function()
|
||||
postVoteAsync(myVote == false and "null" or false)
|
||||
end)
|
||||
|
||||
--[[ Public API ]]--
|
||||
function this:SetParent(newParent)
|
||||
self.Container.Parent = newParent
|
||||
end
|
||||
|
||||
function this:SetPosition(newPosition)
|
||||
self.Container.Position = newPosition
|
||||
CannotVoteSelection.Position = UDim2.new(0, 0, 0, self.Container.Position.Y.Offset + 5)
|
||||
end
|
||||
|
||||
function this:SetVisible(value)
|
||||
self.Container.Visible = value
|
||||
end
|
||||
|
||||
function this:GetDefaultSelection()
|
||||
return defaultSelection
|
||||
end
|
||||
|
||||
function this:SetCanVote(value)
|
||||
canVote = value
|
||||
updateView(myVote)
|
||||
end
|
||||
|
||||
function this:InitializeAsync(gameData)
|
||||
thisGameData = gameData
|
||||
local voteData = gameData:GetVoteDataAsync()
|
||||
if voteData then
|
||||
upVotes = voteData.UpVotes
|
||||
downVotes = voteData.DownVotes
|
||||
myVote = voteData.UserVote or "null"
|
||||
canVote = voteData.CantVoteReason ~= "PlayGame"
|
||||
else
|
||||
myVote = "null"
|
||||
end
|
||||
|
||||
updateView(myVote)
|
||||
VoteWidget:SetVisible(true)
|
||||
end
|
||||
|
||||
return this
|
||||
end
|
||||
|
||||
return createVoteView
|
||||
Reference in New Issue
Block a user