Clients/Client2018/content/internal/AppShell/Modules/Shell/GameDetailScreen.lua

1240 lines
41 KiB
Lua

--[[
// GameDetailScreen.lua
]]
local CoreGui = game:GetService("CoreGui")
local GuiRoot = CoreGui:FindFirstChild("RobloxGui")
local Modules = GuiRoot:FindFirstChild("Modules")
local ShellModules = Modules:FindFirstChild("Shell")
local GuiService = game:GetService('GuiService')
local PlatformService = nil
pcall(function() PlatformService = game:GetService('PlatformService') end)
local AssetManager = require(ShellModules:FindFirstChild('AssetManager'))
local GameData = require(ShellModules:FindFirstChild('GameData'))
local GlobalSettings = require(ShellModules:FindFirstChild('GlobalSettings'))
local Strings = require(ShellModules:FindFirstChild('LocalizedStrings'))
local ScreenManager = require(ShellModules:FindFirstChild('ScreenManager'))
local Utility = require(ShellModules:FindFirstChild('Utility'))
local EventHub = require(ShellModules:FindFirstChild('EventHub'))
local ImageOverlayModule = require(ShellModules:FindFirstChild('ImageOverlay'))
local ReportOverlayModule = require(ShellModules:FindFirstChild('ReportOverlay'))
local BadgeSortModule = require(ShellModules:FindFirstChild('BadgeSort'))
local ScrollingTextBox = require(ShellModules:FindFirstChild('ScrollingTextBox'))
local PopupText = require(ShellModules:FindFirstChild('PopupText'))
local LoadingWidget = require(ShellModules:FindFirstChild('LoadingWidget'))
local Errors = require(ShellModules:FindFirstChild('Errors'))
local ErrorOverlayModule = require(ShellModules:FindFirstChild('ErrorOverlay'))
local GameJoinModule = require(ShellModules:FindFirstChild('GameJoin'))
local ThumbnailLoader = require(ShellModules:FindFirstChild('ThumbnailLoader'))
local SoundManager = require(ShellModules:FindFirstChild('SoundManager'))
local VoteViewModule = require(ShellModules:FindFirstChild('VoteView'))
local BaseScreen = require(ShellModules:FindFirstChild('BaseScreen'))
local Analytics = require(ShellModules:FindFirstChild('Analytics'))
local WidgetModules = ShellModules:FindFirstChild("Widgets")
local MoreButtonModule = require(WidgetModules:FindFirstChild("MoreButton"))
local AchievementManager = require(ShellModules:FindFirstChild('AchievementManager'))
local function CreateGameDetail(placeId)
local this = BaseScreen()
local PLAY_BUTTON_SIZE = Vector2.new(228,72)
local FAVORITE_BUTTON_SIZE = Vector2.new(228,72)
local LEFT_MARGIN = 8
local TWEEN_TIME = 0.5
local BASE_ZINDEX = 2
local baseButtonTextColor = GlobalSettings.WhiteTextColor
local selectedButtonTextColor = GlobalSettings.TextSelectedColor
local selectionChangedCn = nil
local dataModelViewChangedCn = nil
local playButtonDebounce = false
local canJoinGame = true
local returnedFromGame = true
local gameData = nil
--[[ Cache Game Data ]]--
local getGameDataDebounce = false
--This func will be async if placeId not stored in gamecacheddata
local function getDataAsync()
while getGameDataDebounce do
wait()
end
getGameDataDebounce = true
if not gameData then
gameData = GameData:GetGameData(placeId, true)
--We get details(refresh) only once to make sure the game detail pieces are synced
gameData:GetGameDetailsAsync()
end
getGameDataDebounce = false
return gameData
end
-- override 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;
}
--[[ Top Level Elements ]]--
--This Scrolling Frame ensures that we can select sth off screen, so we won't lose selection
local GameDetailContainer = Utility.Create'ScrollingFrame'
{
Name = "ScrollingFrame";
ClipsDescendants = false;
Size = UDim2.new(1, 0, 1, 0);
CanvasSize = UDim2.new(1, 0, 1, 0);
Position = UDim2.new(0, 0, 0, 0);
BackgroundTransparency = 1;
Parent = this.Container;
ScrollingEnabled = false;
Selectable = false;
BorderSizePixel = 0;
ScrollBarThickness = 0;
}
this:SetTitle('')
spawn(function()
local data = getDataAsync()
this:SetTitle(data.Name or '')
end)
--[[ Inner Class - ContentManager - Handles Positioning/Visiblity of content on this screen ]]--
local function createContentManager(size, position, parent)
local contentManager = {}
-- items are an array from left to right
local contentItems = {}
local isTweeningContentLeft = false
local isTweeningContentRight = false
local contentContainer = Utility.Create'Frame'
{
Name = "ContentContainer";
Size = size;
Position = position;
BackgroundTransparency = 1;
Parent = parent;
}
local function recalcLayout()
local offset = 0
for i = 1, #contentItems do
local currentItem = contentItems[i]
currentItem.Item.Position = UDim2.new(0, offset + currentItem.Padding.x, 0, currentItem.Padding.y)
offset = offset + currentItem.Item.Size.X.Offset + currentItem.Padding.x
end
end
function contentManager:AddItem(newItem, padding)
local contentItem = {}
contentItem.Item = newItem
contentItem.Padding = padding or Vector2.new(0, 0)
newItem.Parent = contentContainer
table.insert(contentItems, contentItem)
recalcLayout()
end
function contentManager:RemoveItem(itemToRemove)
for i = 1, #contentItems do
local contentItem = contentItems[i]
if contentItem.Item == itemToRemove then
contentItem.Item.Parent = nil
table.remove(contentItems, i)
recalcLayout()
return
end
end
end
function contentManager:TweenContentLeft()
if not isTweeningContentLeft then
isTweeningContentLeft = true
contentContainer:TweenPosition(UDim2.new(0, LEFT_MARGIN, 0, contentContainer.Position.Y.Offset), Enum.EasingDirection.InOut,
Enum.EasingStyle.Quad, TWEEN_TIME, true, function(tweenStatus)
isTweeningContentLeft = false
end)
end
end
function contentManager:TweenContentRight()
if not isTweeningContentRight then
isTweeningContentRight = true
contentContainer:TweenPosition(UDim2.new(-1, LEFT_MARGIN, 0, contentContainer.Position.Y.Offset), Enum.EasingDirection.InOut,
Enum.EasingStyle.Quad, TWEEN_TIME, true, function(tweenStatus)
isTweeningContentRight = false
end)
end
end
function contentManager:TweenContent(selectedObject)
if selectedObject and selectedObject:IsDescendantOf(contentContainer) then
-- find parent container of selection
local parentContainer = selectedObject
while parentContainer.Parent ~= contentContainer do
parentContainer = parentContainer.Parent
end
if parentContainer.Position.X.Offset + parentContainer.Size.X.Offset > contentContainer.AbsoluteSize.x then
self:TweenContentRight()
elseif parentContainer.Position.X.Offset < contentContainer.AbsoluteSize.x then
self:TweenContentLeft()
end
end
end
function contentManager:ResetTween()
isTweeningContentLeft = false
isTweeningContentRight = false
end
return contentManager
end
local MyContentManager = createContentManager(UDim2.new(1, 0, 0, 622), UDim2.new(0, LEFT_MARGIN, 0, 210), GameDetailContainer)
local PlayButton = Utility.Create'ImageButton'
{
Name = "PlayButton";
Size = UDim2.new(0, PLAY_BUTTON_SIZE.X, 0, PLAY_BUTTON_SIZE.Y);
Position = UDim2.new(0, 0, 1, -77);
BackgroundTransparency = 1;
ImageColor3 = GlobalSettings.GreenButtonColor;
Image = GlobalSettings.RoundCornerButtonImage;
ScaleType = Enum.ScaleType.Slice;
SliceCenter = Rect.new(Vector2.new(4, 4), Vector2.new(28, 28));
Parent = GameDetailContainer;
ZIndex = BASE_ZINDEX;
SoundManager:CreateSound('MoveSelection');
AssetManager.CreateShadow(1)
}
local PlayText = Utility.Create'TextLabel'
{
Name = "PlayText";
Size = UDim2.new(1, 0, 1, 0);
BackgroundTransparency = 1;
Text = Strings:LocalizedString("PlayWord");
Font = GlobalSettings.RegularFont;
FontSize = GlobalSettings.ButtonSize;
TextColor3 = baseButtonTextColor;
ZIndex = BASE_ZINDEX;
Parent = PlayButton;
}
Utility.ResizeButtonWithText(PlayButton, PlayText, GlobalSettings.TextHorizontalPadding)
local FavoriteButton = Utility.Create'ImageButton'
{
Name = "FavoriteButton";
Size = UDim2.new(0, FAVORITE_BUTTON_SIZE.X, 0, FAVORITE_BUTTON_SIZE.Y);
BackgroundTransparency = 1;
ImageColor3 = GlobalSettings.GreyButtonColor;
ScaleType = Enum.ScaleType.Slice;
Parent = GameDetailContainer;
Position = UDim2.new(0, PlayButton.Size.X.Offset + 10, 1, -77);
Image = GlobalSettings.RoundCornerButtonImage;
SliceCenter = Rect.new(Vector2.new(4, 4), Vector2.new(28, 28));
ZIndex = BASE_ZINDEX;
SoundManager:CreateSound('MoveSelection');
AssetManager.CreateShadow(1)
}
FavoriteButton.NextSelectionLeft = PlayButton;
FavoriteButton.NextSelectionDown = FavoriteButton;
FavoriteButton.NextSelectionUp = FavoriteButton;
FavoriteButton.NextSelectionRight = FavoriteButton;
local FavoriteText = Utility.Create'TextLabel'
{
Name = "FavoriteText";
Size = UDim2.new(1, 0, 1, 0);
BackgroundTransparency = 1;
Text = Strings:LocalizedString("FavoriteWord");
Font = GlobalSettings.RegularFont;
FontSize = GlobalSettings.ButtonSize;
TextColor3 = baseButtonTextColor;
ZIndex = BASE_ZINDEX;
Parent = FavoriteButton;
}
local FavoriteStarImage = Utility.Create'ImageLabel'
{
Name = "FavoriteStarImage";
BackgroundTransparency = 1;
Visible = false;
ZIndex = BASE_ZINDEX;
Parent = FavoriteButton;
Image = "rbxasset://textures/ui/Shell/Icons/FavoriteStar@1080.png";
Size = UDim2.new(0,32,0,31);
}
FavoriteStarImage.Position = UDim2.new(0, 16, 0.5, -FavoriteStarImage.Size.Y.Offset / 2)
--Make it big enough to hold the star and text
Utility.ResizeButtonWithDynamicText(FavoriteButton, FavoriteText, {Strings:LocalizedString("FavoritedWord")},
GlobalSettings.TextHorizontalPadding + (FavoriteStarImage.Position.X.Offset + FavoriteStarImage.Size.X.Offset + 12) / 2)
local FavoriteRedirectFrame = Utility.Create'Frame'
{
Name = "FavoriteRedirectFrame";
Size = UDim2.new(1, 0, 0, FavoriteButton.Size.Y.Offset);
Position = UDim2.new(0, FavoriteButton.Position.X.Offset + FavoriteButton.Size.X.Offset, 1, FavoriteButton.Position.Y.Offset);
BackgroundTransparency = 1;
Selectable = true;
Parent = GameDetailContainer;
}
--[[ Scrolling Content Items ]]--
--[[ Icon Image ]]--
local GameIconContainer = Utility.Create'Frame'
{
Name = "GameIconContainer";
Size = UDim2.new(0, 566, 1, 0);
BackgroundTransparency = 1;
}
MyContentManager:AddItem(GameIconContainer, Vector2.new(0, 0))
local GameIconImage = Utility.Create'ImageLabel'
{
Name = "GameIconImage";
Size = UDim2.new(1, 0, 0, 566);
Position = UDim2.new(0, 0, 0.5, -566/2);
BackgroundTransparency = 0;
BorderSizePixel = 0;
BackgroundColor3 = Color3.new();
ZIndex = BASE_ZINDEX;
Parent = GameIconContainer;
AssetManager.CreateShadow(1);
}
local function loadGameIcon(assetId)
local gameIconLoader = ThumbnailLoader:Create(GameIconImage, assetId,
ThumbnailLoader.Sizes.Medium, ThumbnailLoader.AssetType.SquareIcon)
spawn(function()
gameIconLoader:LoadAsync(true, true, { ZIndex = GameIconImage.ZIndex } )
end)
end
spawn(function()
local data = getDataAsync()
loadGameIcon(data.IconId)
GameData:AddRelatedGuiObject(placeId, GameIconImage)
end)
--[[ Rating and Description ]]--
local RatingDescriptionContainer = Utility.Create'Frame'
{
Name = "RatingDescriptionContainer";
Size = UDim2.new(0, 394, 1, 0);
BackgroundTransparency = 1;
}
MyContentManager:AddItem(RatingDescriptionContainer, Vector2.new(38, 0))
local RatingDescriptionTitle = Utility.Create'TextLabel'
{
Name = "RatingDescriptionTitle";
Size = UDim2.new(0, 0, 0, 33);
Position = UDim2.new(0, 0, 0, 0);
BackgroundTransparency = 1;
Text = Strings:LocalizedString("RatingDescriptionTitle");
TextXAlignment = Enum.TextXAlignment.Left;
Font = GlobalSettings.RegularFont;
FontSize = GlobalSettings.SubHeaderSize;
TextColor3 = GlobalSettings.WhiteTextColor;
Parent = RatingDescriptionContainer;
}
local VoteView = VoteViewModule()
VoteView:SetPosition(UDim2.new(0, 0, 0, RatingDescriptionTitle.Size.Y.Offset))
local VoteViewContainer = VoteView.Container
local RatingDescriptionLineBreak = Utility.Create'Frame'
{
Name = "RatingDescriptionLineBreak";
Size = UDim2.new(0, 362, 0, 2);
Position = UDim2.new(0, 0, 0, VoteViewContainer.Position.Y.Offset + VoteViewContainer.Size.Y.Offset);
BorderSizePixel = 0;
BackgroundColor3 = GlobalSettings.LineBreakColor;
Parent = RatingDescriptionContainer;
}
--[[ Description Container ]]--
local DescriptionContainer = Utility.Create'Frame'
{
Name = "DescriptionContainer";
Size = UDim2.new(1, 0, 0, 430);
Position = UDim2.new(0, 0, 0, RatingDescriptionLineBreak.Position.Y.Offset + RatingDescriptionLineBreak.Size.Y.Offset + 20);
BackgroundTransparency = 1;
Parent = RatingDescriptionContainer;
}
local DescriptionScrollingTextBox = ScrollingTextBox(UDim2.new(1, 0, 1, 0), UDim2.new(0, 0, 0, 0), DescriptionContainer)
--Used to overwrite the selection when the voteview/descriptionScrollingTextBox selectable changes
local function updateRatingDescriptionSelection()
local defaultVoteSelection = VoteView:GetDefaultSelection()
if defaultVoteSelection then --Make sure the vote view is loaded when overwrite the selections
defaultVoteSelection.NextSelectionLeft = FavoriteButton
if DescriptionScrollingTextBox:IsSelectable() then
local descriptionSelectionObject = DescriptionScrollingTextBox:GetSelectableObject()
descriptionSelectionObject.NextSelectionUp = defaultVoteSelection
defaultVoteSelection.NextSelectionDown = descriptionSelectionObject
FavoriteButton.NextSelectionRight = descriptionSelectionObject
FavoriteButton.NextSelectionUp = descriptionSelectionObject
descriptionSelectionObject.NextSelectionLeft = FavoriteButton
descriptionSelectionObject.NextSelectionDown = FavoriteButton
else
defaultVoteSelection.NextSelectionDown = FavoriteButton
FavoriteButton.NextSelectionRight = defaultVoteSelection
FavoriteButton.NextSelectionUp = defaultVoteSelection
end
end
end
--[[ Line Break ]]--
local RatingThumbsLineBreak = Utility.Create'Frame'
{
Name = "LineBreak";
Size = UDim2.new(0, 2, 0, 566);
BackgroundTransparency = 0;
BorderSizePixel = 0;
BackgroundColor3 = Color3.new(78/255, 78/255, 78/255);
BorderSizePixel = 0;
}
MyContentManager:AddItem(RatingThumbsLineBreak, Vector2.new(32, 33))
--[[ Additional Thumbs ]]--
local ThumbsContainer = Utility.Create'Frame'
{
Name = "ThumbsContainer";
Size = UDim2.new(0, 630, 1, 0);
BackgroundTransparency = 1;
}
MyContentManager:AddItem(ThumbsContainer, Vector2.new(52, 0))
local ThumbsTitle = RatingDescriptionTitle:Clone()
ThumbsTitle.Name = "ThumbsTitle"
ThumbsTitle.Text = Strings:LocalizedString("GameImagesTitle")
ThumbsTitle.Parent = ThumbsContainer
local ThumbsContent = Utility.Create'Frame'
{
Name = "ThumbsContent";
Size = UDim2.new(1, 0, 1, 0);
Position = UDim2.new(0, 0, 0, 0);
BackgroundTransparency = 1;
Parent = ThumbsContainer;
}
local MainThumbImage = Utility.Create'ImageButton'
{
Name = "MainThumbImage";
Size = UDim2.new(1, 0, 0, 374);
Position = UDim2.new(0, 0, 0, ThumbsTitle.Size.Y.Offset);
BackgroundTransparency = 0;
BorderSizePixel = 0;
BackgroundColor3 = Color3.new();
ZIndex = BASE_ZINDEX;
Parent = ThumbsContent;
SoundManager:CreateSound('MoveSelection');
AssetManager.CreateShadow(1);
}
local SmThumbLeft = Utility.Create'ImageButton'
{
Name = "SmThumbLeft";
Size = UDim2.new(0, 310, 0, 184);
Position = UDim2.new(0, 0, 0, MainThumbImage.Position.Y.Offset + MainThumbImage.Size.Y.Offset + 8);
BackgroundTransparency = 0;
BackgroundColor3 = Color3.new();
BorderSizePixel = 0;
ZIndex = BASE_ZINDEX;
Parent = ThumbsContent;
SoundManager:CreateSound('MoveSelection');
AssetManager.CreateShadow(1);
}
SmThumbRight = SmThumbLeft:Clone()
SmThumbRight.Name = "SmThumbRight"
SmThumbRight.Position = UDim2.new(0, SmThumbLeft.Size.X.Offset + 10, 0, SmThumbRight.Position.Y.Offset)
SmThumbRight.Parent = ThumbsContent
local MoreThumbsButton = MoreButtonModule()
MoreThumbsButton.Size = UDim2.new(0, 108, 0, 50)
MoreThumbsButton.Image = "rbxasset://textures/ui/Shell/Buttons/MoreButton@1080.png"
MoreThumbsButton.Position = UDim2.new(1, -MoreThumbsButton.Size.X.Offset + 3, 0,
SmThumbRight.Position.Y.Offset + SmThumbRight.Size.Y.Offset + 12)
MoreThumbsButton.Visible = false
MoreThumbsButton.ZIndex = BASE_ZINDEX
MoreThumbsButton.SelectionImageObject = nil
MoreThumbsButton.Parent = ThumbsContent
--[[ Badges ]]--
local BadgeContainer = Utility.Create'Frame'
{
Name = "BadgeContainer";
Size = UDim2.new(0, 568, 1, 0);
BackgroundTransparency = 1;
}
MyContentManager:AddItem(BadgeContainer, Vector2.new(52, 0))
local BadgeTitle = RatingDescriptionTitle:Clone()
BadgeTitle.Name = "BadgeTitle"
BadgeTitle.Text = Strings:LocalizedString("GameBadgesTitle")
BadgeTitle.Parent = BadgeContainer
--[[ Related Games ]]--
local RelatedGamesContainer = Utility.Create'Frame'
{
Name = "RelatedGamesContainer";
Size = BadgeContainer.Size;
BackgroundTransparency = 1;
}
local HideRelatedGames = not AchievementManager:AllGamesUnlocked()
if not HideRelatedGames then
MyContentManager:AddItem(RelatedGamesContainer, Vector2.new(52, 0))
end
local RelatedGamesTitle = BadgeTitle:Clone()
RelatedGamesTitle.Name = "RelatedGamesTitle"
RelatedGamesTitle.Text = Strings:LocalizedString("RelatedGamesTitle")
RelatedGamesTitle.Parent = RelatedGamesContainer
local RelatedGamesImageFrame = Utility.Create'Frame'
{
Name = "RelatedGamesImageFrame";
Size = UDim2.new(1, 0, 566, 0);
Position = UDim2.new(0, 0, 0, RelatedGamesTitle.Size.Y.Offset);
BackgroundTransparency = 1;
Parent = RelatedGamesContainer;
}
-- 2x2 grid of related games
local RelatedGameImages = {}
local relatedIndex = 1
local relatedMargin = 14
local relatedSize = (566 - relatedMargin) / 2
for i = 1, 2 do
for j = 1, 2 do
local image = Utility.Create'ImageButton'
{
Name = tostring(relatedIndex);
Size = UDim2.new(0, relatedSize, 0, relatedSize);
Position = UDim2.new(0, (i - 1) * relatedSize + (i - 1) * relatedMargin, 0, (j - 1) * relatedSize + (j - 1) * relatedMargin);
BackgroundTransparency = 0;
BackgroundColor3 = GlobalSettings.GreyButtonColor;
BorderSizePixel = 0;
ZIndex = BASE_ZINDEX;
Parent = RelatedGamesImageFrame;
SoundManager:CreateSound('MoveSelection');
AssetManager.CreateShadow(1);
}
RelatedGameImages[relatedIndex] = image
relatedIndex = relatedIndex + 1
end
end
local RelatedGamesMoreDetailsLineBreak = RatingThumbsLineBreak:Clone()
RelatedGamesMoreDetailsLineBreak.Name = "RelatedGamesMoreDetailsLineBreak"
MyContentManager:AddItem(RelatedGamesMoreDetailsLineBreak, Vector2.new(52, RelatedGamesMoreDetailsLineBreak.Position.Y.Offset))
--[[ More Details ]]--
local MoreDetailsContainer = Utility.Create'Frame'
{
Name = "MoreDetailsContainer";
Size = UDim2.new(0, 386, 1, 0);
BackgroundTransparency = 1;
Selectable = true;
}
MyContentManager:AddItem(MoreDetailsContainer, Vector2.new(52, 0))
local MoreDetailsTitle = RelatedGamesTitle:Clone()
MoreDetailsTitle.Name = "MoreDetailsTitle"
MoreDetailsTitle.Text = Strings:LocalizedString("MoreDetailsTitle")
MoreDetailsTitle.Parent = MoreDetailsContainer
local MoreDetailsContent = Utility.Create'Frame'
{
Name = "MoreDetailsContent";
Size = UDim2.new(1, 0, 1, 0);
Position = UDim2.new(0, 0, 0, 0);
BackgroundTransparency = 1;
Parent = MoreDetailsContainer;
}
local UpdatedText = Utility.Create'TextLabel'
{
Name = "UpdatedText";
Size = UDim2.new(0, 0, 0, 0);
Position = UDim2.new(0, 0, 0, MoreDetailsTitle.Size.Y.Offset + 48);
BackgroundTransparency = 1;
Text = Strings:LocalizedString("LastUpdatedWord");
Font = GlobalSettings.RegularFont;
FontSize = GlobalSettings.SmallTitleSize;
TextColor3 = GlobalSettings.WhiteTextColor;
TextXAlignment = Enum.TextXAlignment.Left;
Parent = MoreDetailsContent;
}
local LastUpdatedText = Utility.Create'TextLabel'
{
Name = "LastUpdatedText";
Size = UDim2.new(0, 0, 0, 0);
Position = UDim2.new(0, 0, 0, UpdatedText.Position.Y.Offset + 34);
BackgroundTransparency = 1;
Text = "";
Font = GlobalSettings.LightFont;
FontSize = GlobalSettings.TitleSize;
TextColor3 = GlobalSettings.WhiteTextColor;
TextXAlignment = Enum.TextXAlignment.Left;
Parent = MoreDetailsContent;
}
local CreationText = UpdatedText:Clone()
CreationText.Name = "CreationText"
CreationText.Position = UDim2.new(0, 0, 0, LastUpdatedText.Position.Y.Offset + 70)
CreationText.Text = Strings:LocalizedString("CreationDateWord")
CreationText.Parent = MoreDetailsContent
local CreationDateText = LastUpdatedText:Clone()
CreationDateText.Name = "CreationDateText"
CreationDateText.Position = UDim2.new(0, 0, 0, CreationText.Position.Y.Offset + 34)
CreationDateText.Parent = MoreDetailsContent
local MaxPlayersText = UpdatedText:Clone()
MaxPlayersText.Name = "MaxPlayersText"
MaxPlayersText.Position = UDim2.new(0, 0, 0, CreationDateText.Position.Y.Offset + 70)
MaxPlayersText.Text = Strings:LocalizedString("MaxPlayersWord")
MaxPlayersText.Parent = MoreDetailsContent
local MaxPlayersCountText = LastUpdatedText:Clone()
MaxPlayersCountText.Name = "MaxPlayersCountText"
MaxPlayersCountText.Position = UDim2.new(0, 0, 0, MaxPlayersText.Position.Y.Offset + 34)
MaxPlayersCountText.Parent = MoreDetailsContent
local CreatorText = UpdatedText:Clone()
CreatorText.Name = "CreatorText"
CreatorText.Position = UDim2.new(0, 0, 0, MaxPlayersCountText.Position.Y.Offset + 70)
CreatorText.Text = Strings:LocalizedString("CreatedByWord")
CreatorText.Parent = MoreDetailsContent
local CreatorIcon = Utility.Create'ImageLabel'
{
Name = "CreatorIcon";
Size = UDim2.new(0, 32, 0, 32);
Position = UDim2.new(0, 0, 0, CreatorText.Position.Y.Offset + 34 - 14);
BackgroundTransparency = 1;
Image = 'rbxasset://textures/ui/Shell/Icons/RobloxIcon32.png';
Parent = MoreDetailsContent;
}
local CreatorNameText = LastUpdatedText:Clone()
CreatorNameText.Name = "CreatorNameText"
CreatorNameText.Position = UDim2.new(0, CreatorIcon.Size.X.Offset + 8, 0, CreatorText.Position.Y.Offset + 34)
CreatorNameText.Parent = MoreDetailsContent
local DetailsBottomLineBreak = Utility.Create'Frame'
{
Name = "DetailsBottomLineBreak";
Size = UDim2.new(0, 308, 0, 2);
Position = UDim2.new(0, 107, 0, MoreDetailsTitle.Size.Y.Offset + 566 - 2);
BorderSizePixel = 0;
BackgroundColor3 = GlobalSettings.LineBreakColor;
Parent = MoreDetailsContainer;
}
local ReportFrame = Utility.Create'TextButton'
{
Name = "ReportFrame";
Size = UDim2.new(0, 91, 0, 91);
Position = UDim2.new(0, 0, 0, DetailsBottomLineBreak.Position.Y.Offset - 95);
BackgroundColor3 = GlobalSettings.GreyButtonColor;
BackgroundTransparency = 0;
BorderSizePixel = 0;
Text = "";
Parent = MoreDetailsContainer;
ZIndex = BASE_ZINDEX;
SoundManager:CreateSound('MoveSelection');
AssetManager.CreateShadow(1);
}
local ReportIcon = Utility.Create'ImageLabel'
{
Name = "ReportIcon";
BackgroundTransparency = 1;
ZIndex = BASE_ZINDEX;
Parent = ReportFrame;
Image = "rbxasset://textures/ui/Shell/Icons/ReportIcon@1080.png";
Size = UDim2.new(0,52,0,43);
}
ReportIcon.Position = UDim2.new(0.5, -ReportIcon.Size.X.Offset / 2, 0.5, -ReportIcon.Size.Y.Offset / 2)
local ReportText = Utility.Create'TextLabel'
{
Name = "ReportText";
Size = UDim2.new(0, 0, 0, 0);
Position = UDim2.new(1, 16, 0.5, 0);
BackgroundTransparency = 1;
Font = GlobalSettings.LightFont;
FontSize = GlobalSettings.TitleSize;
TextColor3 = GlobalSettings.WhiteTextColor;
TextXAlignment = Enum.TextXAlignment.Left;
Text = Strings:LocalizedString("ReportGameWord");
Parent = ReportFrame;
}
ReportFrame.MouseButton1Click:connect(function()
ScreenManager:OpenScreen(ReportOverlayModule:CreateReportOverlay(ReportOverlayModule.ReportType.REPORT_GAME, placeId), false)
end)
--[[ Custom Selection Logic ]]--
PlayButton.NextSelectionRight = FavoriteButton
SmThumbLeft.NextSelectionDown = MoreThumbsButton
SmThumbLeft.NextSelectionRight = SmThumbRight
local function JoinGame()
if canJoinGame and returnedFromGame then
canJoinGame = false
local data = getDataAsync()
local creatorUserId = nil
if data then
creatorUserId = data.CreatorUserId
end
GameJoinModule:StartGame(GameJoinModule.JoinType.Normal, placeId, creatorUserId)
canJoinGame = true
end
end
local function toggleFavoriteButton(value)
if value == true then
FavoriteStarImage.Visible = true
FavoriteText.Position = UDim2.new(0, FavoriteStarImage.Position.X.Offset + FavoriteStarImage.Size.X.Offset + 12, 0, 0)
FavoriteText.Text = Strings:LocalizedString("FavoritedWord")
FavoriteText.TextXAlignment = Enum.TextXAlignment.Left
elseif value == false then
FavoriteStarImage.Visible = false
FavoriteText.Position = UDim2.new(0, 0, 0, 0)
FavoriteText.Text = Strings:LocalizedString("FavoriteWord")
FavoriteText.TextXAlignment = Enum.TextXAlignment.Center
end
end
--[[ SelectionGained/Lost Connections ]]--
PlayButton.SelectionGained:connect(function()
PlayButton.ImageColor3 = GlobalSettings.GreenSelectedButtonColor
PlayText.TextColor3 = selectedButtonTextColor
end)
PlayButton.SelectionLost:connect(function()
PlayButton.ImageColor3 = GlobalSettings.GreenButtonColor
PlayText.TextColor3 = baseButtonTextColor
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)
MoreDetailsContainer.SelectionGained:connect(function()
-- redirect
Utility.SetSelectedCoreObject(ReportFrame)
end)
FavoriteRedirectFrame.SelectionGained:connect(function()
Utility.SetSelectedCoreObject(FavoriteButton)
end)
--[[ Input Events ]]--
PlayButton.MouseButton1Click:connect(function()
SoundManager:Play('ButtonPress')
if playButtonDebounce then return end
playButtonDebounce = true
JoinGame()
playButtonDebounce = false
end)
local favoriteDebounce = false
FavoriteButton.MouseButton1Click:connect(function()
SoundManager:Play('ButtonPress')
if favoriteDebounce then return end
favoriteDebounce = true
local data = getDataAsync()
if data then
local success, reason = data:PostFavoriteAsync()
if success == true then
toggleFavoriteButton(data.IsFavorited)
elseif reason then
local err = Errors.Favorite[reason]
ScreenManager:OpenScreen(ErrorOverlayModule(err), false)
end
end
favoriteDebounce = false
end)
--[[ Content Set Functions ]]--
local function connectThumbImage(image, index, thumbIds)
local loader = ThumbnailLoader:Create(image, thumbIds[index], ThumbnailLoader.Sizes.Large,
ThumbnailLoader.AssetType.Icon, false)
image.MouseButton1Click:connect(function()
SoundManager:Play('OverlayOpen')
ScreenManager:OpenScreen(ImageOverlayModule(thumbIds, index), false)
end)
spawn(function()
loader:LoadAsync(true, true, { ZIndex = image.ZIndex } )
end)
end
local function setAdditionalThumbs(thumbIds)
if #thumbIds >= 3 then
connectThumbImage(MainThumbImage, 1, thumbIds)
connectThumbImage(SmThumbLeft, 2, thumbIds)
connectThumbImage(SmThumbRight, 3, thumbIds)
if #thumbIds > 3 then
MoreThumbsButton.Visible = true
MoreThumbsButton.MouseButton1Click:connect(function()
SoundManager:Play('OverlayOpen')
ScreenManager:OpenScreen(ImageOverlayModule(thumbIds, 1), false)
end)
end
else
MyContentManager:RemoveItem(ThumbsContainer)
end
end
local function setRelatedGames(placeIds)
if #placeIds > 0 then
for i = 1, #RelatedGameImages do
local image = RelatedGameImages[i]
if placeIds[i] then
local thisData = GameData:GetGameData(placeIds[i])
local thumbLoader = ThumbnailLoader:Create(image, thisData.IconId,
ThumbnailLoader.Sizes.Medium, ThumbnailLoader.AssetType.SquareIcon)
spawn(function()
thumbLoader:LoadAsync(true, true, { ZIndex = image.ZIndex } )
end)
-- connect open detail event
image.MouseButton1Click:connect(function()
EventHub:dispatchEvent(EventHub.Notifications["OpenGameDetail"], thisData.PlaceId, thisData.Name, thisData.IconId)
end)
PopupText(image, thisData["Name"])
GameData:AddRelatedGuiObject(placeId, image)
end
end
else
MyContentManager:RemoveItem(RelatedGamesContainer)
end
end
local function setNoteDisplay(OverrideAvatar, IsExperimental)
local noteText = ""
if OverrideAvatar then
noteText = Strings:LocalizedString("CustomAvatarPhrase")
end
if IsExperimental then
local experimentalGamePhrase = Strings:LocalizedString("ExperimentalGamePhrase")
noteText = noteText ~= "" and (noteText.."\n") or noteText
noteText = noteText..experimentalGamePhrase
end
if noteText ~= "" then
local noteFrame = Utility.Create'Frame'
{
Name = "NoteFrame";
Size = UDim2.new(1, 0, 0.1, 0);
BackgroundTransparency = 1;
Parent = DescriptionContainer;
}
local noteText = Utility.Create'TextLabel'
{
Name = "NoteText";
Size = UDim2.new(0, 0, 0, 0);
Position = UDim2.new(0, 190, 0.5, 0);
BackgroundTransparency = 1;
Font = GlobalSettings.BoldFont;
FontSize = GlobalSettings.SubHeaderSize;
TextColor3 = GlobalSettings.GreyTextColor;
Text = noteText;
Parent = noteFrame;
}
DescriptionScrollingTextBox:SetPosition(UDim2.new(0, 0, 0.15, 0))
DescriptionScrollingTextBox:SetSize(UDim2.new(1, 0, 0.85, 0))
else
DescriptionScrollingTextBox:SetPosition(UDim2.new(0, 0, 0, 0))
DescriptionScrollingTextBox:SetSize(UDim2.new(1, 0, 1, 0))
end
end
--[[ Initialize Content - Don't Block ]]--
local scrollingTextBoxSelectableChangedCn = nil
local function waitForTweensToFinish()
while this and this.TransitionTweens and this.TransitionTweens[1] and not this.TransitionTweens[1]:IsFinished() do
wait()
end
end
local function concatTables(t1, t2)
for _, value in pairs(t2) do
table.insert(t1, value)
end
end
--This is a utility class to update the selection between each game detail page "Chunk"
local function CreateChunk(container, titleFrame)
local this = {}
this.Container = container
this.SelectionHolder = Utility.Create'Frame'
{
Name = "SelectionHolder";
Size = UDim2.new(1, 0, 1, -titleFrame.Size.Y.Offset);
Position = UDim2.new(0, 0, 0, titleFrame.Size.Y.Offset);
BackgroundTransparency = 1;
Parent = this.Container;
Selectable = true;
}
this.DefaultSelection = this.SelectionHolder
this.Container.SelectionGained:connect(function()
Utility.SetSelectedCoreObject(this.DefaultSelection)
end)
--Should be called when the container's internal data get loaded
function this:PostLoaded()
--If we are selecting the selectionHolder, we switch to the new defaultSelection
self.SelectionHolder.Selectable = false
if GuiService.SelectedCoreObject == self.SelectionHolder and self.DefaultSelection then
Utility.SetSelectedCoreObject(self.DefaultSelection)
end
self.SelectionHolder:Destroy()
end
return this
end
--[[Create the detail Chunk]]
local GameDetailChunks = {}
table.insert(GameDetailChunks, CreateChunk(RatingDescriptionContainer, RatingDescriptionTitle))
table.insert(GameDetailChunks, CreateChunk(ThumbsContainer, ThumbsTitle))
table.insert(GameDetailChunks, CreateChunk(BadgeContainer, BadgeTitle))
if not HideRelatedGames then
table.insert(GameDetailChunks, CreateChunk(RelatedGamesContainer, RelatedGamesTitle))
end
local function GetPrevDefaultSelectionForChunk(index)
local defaultSelection = FavoriteButton
index = index - 1
while index > 0 and GameDetailChunks[index] do
if GameDetailChunks[index].DefaultSelection then
defaultSelection = GameDetailChunks[index].DefaultSelection
break
else
index = index - 1
end
end
return defaultSelection
end
local function GetNextDefaultSelectionForChunk(index)
local defaultSelection = ReportFrame
index = index + 1
while index > 0 and GameDetailChunks[index] do
if GameDetailChunks[index].DefaultSelection then
defaultSelection = GameDetailChunks[index].DefaultSelection
break
else
index = index + 1
end
end
return defaultSelection
end
do
RatingDescriptionLineBreak.Visible = false
DescriptionContainer.Visible = false
local function loadFavoritedDataAsync()
local data = getDataAsync()
if not data then
Utility.DebugLog("GameDetail:loadFavoritedDataAsync() could not fetch data.")
return
end
-- set favorite
toggleFavoriteButton(data.IsFavorited)
end
local function loadDescriptionDataAsync()
local data = getDataAsync()
if not data then
Utility.DebugLog("GameDetail:loadDescriptionDataAsync() could not fetch data.")
return
end
local descriptionData = data.Description
local overridesDefaultAvatar = data.OverridesDefaultAvatar
--Only show Experimental text when the flag is on
setNoteDisplay(overridesDefaultAvatar, data.IsExperimental)
DescriptionScrollingTextBox:SetText(descriptionData)
end
local function loadVoteDataAsync()
local data = getDataAsync()
if not data then
Utility.DebugLog("GameDetail:loadVoteDataAsync() could not fetch data.")
return
end
VoteView:InitializeAsync(data)
end
FavoriteButton.NextSelectionRight = GameDetailChunks[1].SelectionHolder
FavoriteButton.NextSelectionUp = GameDetailChunks[1].SelectionHolder
GameDetailChunks[1].SelectionHolder.NextSelectionLeft = FavoriteButton
GameDetailChunks[1].SelectionHolder.NextSelectionDown = FavoriteButton
local loader = LoadingWidget({Parent = GameDetailChunks[1].SelectionHolder}, {loadFavoritedDataAsync, loadDescriptionDataAsync, loadVoteDataAsync, waitForTweensToFinish})
spawn(function()
loader:AwaitFinished()
loader:Cleanup()
loader = nil
RatingDescriptionLineBreak.Visible = true
DescriptionContainer.Visible = true
VoteView:SetParent(RatingDescriptionContainer)
VoteView:SetVisible(true)
--Hook up Selections after loading
updateRatingDescriptionSelection()
GameDetailChunks[1].DefaultSelection = VoteView:GetDefaultSelection() or GetNextDefaultSelectionForChunk(1)
GameDetailChunks[1]:PostLoaded()
if this then
this.TransitionTweens = this.TransitionTweens or {}
concatTables(this.TransitionTweens, ScreenManager:FadeInSitu(RatingDescriptionLineBreak))
concatTables(this.TransitionTweens, ScreenManager:FadeInSitu(DescriptionContainer))
--Recalc position
MyContentManager:TweenContent(GuiService.SelectedCoreObject)
end
end)
end
do
local gameThumbIds = {}
local function loadThumbsAsync()
local data = getDataAsync()
if not data then
Utility.DebugLog("GameDetail:loadThumbsAsync() could not fetch data.")
return
end
gameThumbIds = data:GetThumbnailIdsAsync()
end
ThumbsContent.Visible = false
local loader = LoadingWidget({Parent = GameDetailChunks[2].SelectionHolder}, {loadThumbsAsync, waitForTweensToFinish})
spawn(function()
loader:AwaitFinished()
loader:Cleanup()
loader = nil
ThumbsContent.Visible = true
-- set additional thumbnails
local hasThumbs = #gameThumbIds >= 3
setAdditionalThumbs(gameThumbIds)
GameDetailChunks[2].DefaultSelection = hasThumbs and MainThumbImage or GetNextDefaultSelectionForChunk(2)
GameDetailChunks[2]:PostLoaded()
if this then
this.TransitionTweens = this.TransitionTweens or {}
concatTables(this.TransitionTweens, ScreenManager:FadeInSitu(ThumbsContent))
--Recalc position
MyContentManager:TweenContent(GuiService.SelectedCoreObject)
end
end)
end
do
local badgeData = {}
local placeName = ""
local function loadBadgeDataAsync()
local data = getDataAsync()
if not data then
Utility.DebugLog("GameDetail:loadBadgeDataAsync() could not fetch data.")
return
end
-- set badge data
badgeData = data:GetBadgeDataAsync()
placeName = data.Name or ""
end
local loader = LoadingWidget({Parent = GameDetailChunks[3].SelectionHolder}, {loadBadgeDataAsync, waitForTweensToFinish})
spawn(function()
loader:AwaitFinished()
loader:Cleanup()
loader = nil
-- set badges
local hasBadges = #badgeData >= 4
local BadgeSort
if hasBadges then
BadgeSort = BadgeSortModule(placeName, UDim2.new(1, 0, 0, 566), UDim2.new(0, 0, 0, BadgeTitle.Size.Y.Offset), BadgeContainer)
BadgeSort:Initialize(badgeData)
GameDetailChunks[3].DefaultSelection = BadgeSort:GetDefaultSelection()
else
MyContentManager:RemoveItem(BadgeContainer)
GameDetailChunks[3].DefaultSelection = GetNextDefaultSelectionForChunk(3)
end
GameDetailChunks[3]:PostLoaded()
if this then
this.TransitionTweens = this.TransitionTweens or {}
if BadgeSort then
concatTables(this.TransitionTweens, ScreenManager:FadeInSitu(BadgeSort:GetContainer()))
end
--Recalc position
MyContentManager:TweenContent(GuiService.SelectedCoreObject)
end
end)
end
if not HideRelatedGames then
do
local relatedGames = {}
local function loadRelatedGamesAsync()
local data = getDataAsync()
if not data then
Utility.DebugLog("GameDetail:loadRelatedGamesAsync() could not fetch data.")
return
end
-- set related games sort data
relatedGames = data:GetRecommendedGamesAsync()
end
RelatedGamesImageFrame.Visible = false
local loader = LoadingWidget({Parent = GameDetailChunks[4].SelectionHolder}, {loadRelatedGamesAsync, waitForTweensToFinish})
spawn(function()
loader:AwaitFinished()
loader:Cleanup()
loader = nil
RelatedGamesImageFrame.Visible = true
setRelatedGames(relatedGames)
GameDetailChunks[4].DefaultSelection = (#relatedGames > 0) and RelatedGameImages[1] or GetNextDefaultSelectionForChunk(4)
GameDetailChunks[4]:PostLoaded()
if this then
this.TransitionTweens = this.TransitionTweens or {}
concatTables(this.TransitionTweens, ScreenManager:FadeInSitu(RelatedGamesImageFrame))
--Recalc position
MyContentManager:TweenContent(GuiService.SelectedCoreObject)
end
end)
end
end
do
local function loadMoreGameDetailsAsync()
local data = getDataAsync()
if not data then
Utility.DebugLog("GameDetail:loadMoreGameDetailsAsync() could not fetch data.")
return
end
-- set more details
LastUpdatedText.Text = data.LastUpdated or LastUpdatedText.Text
CreationDateText.Text = data.CreationDate or CreationDateText.Text
CreatorNameText.Text = data.CreatorName or CreatorNameText.Text
MaxPlayersCountText.Text = data.MaxPlayers or MaxPlayersCountText.Text
end
MoreDetailsContent.Visible = false
local loader = LoadingWidget({Parent = MoreDetailsContent}, {loadMoreGameDetailsAsync, waitForTweensToFinish})
spawn(function()
loader:AwaitFinished()
loader:Cleanup()
loader = nil
MoreDetailsContent.Visible = true
if this then
this.TransitionTweens = this.TransitionTweens or {}
concatTables(this.TransitionTweens, ScreenManager:FadeInSitu(MoreDetailsContent))
--Recalc position
MyContentManager:TweenContent(GuiService.SelectedCoreObject)
end
end)
end
--[[ Public API ]]--
--Override
function this:GetAnalyticsInfo()
return
{
[Analytics.WidgetNames('WidgetId')] = Analytics.WidgetNames('GameDetailId');
PlaceId = placeId;
}
end
function this:GetDefaultSelectionObject()
return PlayButton
end
-- Override
local baseShow = this.Show
function this:Show()
baseShow(self)
end
-- Override
local baseFocus = this.Focus
function this:Focus()
baseFocus(self)
--This ensures that we can still navigate even if the callback of TweenPosition fails to be called
MyContentManager:ResetTween()
MyContentManager:TweenContent(GuiService.SelectedCoreObject)
selectionChangedCn = GuiService:GetPropertyChangedSignal('SelectedCoreObject'):connect(function()
local newSelectedObject = GuiService.SelectedCoreObject
if newSelectedObject then
MyContentManager:TweenContent(newSelectedObject)
end
end)
if PlatformService then
dataModelViewChangedCn = PlatformService.ViewChanged:connect(function(viewType)
if viewType == 0 then
returnedFromGame = false
wait(1)
returnedFromGame = true
end
end)
end
EventHub:addEventListener(EventHub.Notifications["GameJoin"], "gameJoin",
function(success)
if not VoteView:GetCanVote() then
if success then
VoteView:SetCanVote(true)
updateRatingDescriptionSelection()
end
end
end)
scrollingTextBoxSelectableChangedCn = DescriptionScrollingTextBox.OnSelectableChanged:connect(updateRatingDescriptionSelection)
end
-- Override
local baseRemoveFocus = this.RemoveFocus
function this:RemoveFocus()
baseRemoveFocus(self)
selectionChangedCn = Utility.DisconnectEvent(selectionChangedCn)
dataModelViewChangedCn = Utility.DisconnectEvent(dataModelViewChangedCn)
EventHub:removeEventListener(EventHub.Notifications["GameJoin"], "gameJoin")
scrollingTextBoxSelectableChangedCn = Utility.DisconnectEvent(scrollingTextBoxSelectableChangedCn)
end
return this
end
return CreateGameDetail