This commit is contained in:
lx
2026-07-06 14:01:11 -04:00
commit c4f97d729d
16468 changed files with 935321 additions and 0 deletions
@@ -0,0 +1,9 @@
local CorePackages = game:GetService("CorePackages")
local Action = require(CorePackages.AppTempCommon.Common.Action)
return Action(script.Name, function(open)
return {
open = open,
}
end)
@@ -0,0 +1,9 @@
local CorePackages = game:GetService("CorePackages")
local Action = require(CorePackages.AppTempCommon.Common.Action)
return Action(script.Name, function(canChat)
return {
canChat = canChat,
}
end)
@@ -0,0 +1,9 @@
local CorePackages = game:GetService("CorePackages")
local Action = require(CorePackages.AppTempCommon.Common.Action)
return Action(script.Name, function(enabled)
return {
enabled = enabled,
}
end)
@@ -0,0 +1,9 @@
local CorePackages = game:GetService("CorePackages")
local Action = require(CorePackages.AppTempCommon.Common.Action)
return Action(script.Name, function(open)
return {
open = open,
}
end)
@@ -0,0 +1,9 @@
local CorePackages = game:GetService("CorePackages")
local Action = require(CorePackages.AppTempCommon.Common.Action)
return Action(script.Name, function(gameName)
return {
gameName = gameName,
}
end)
@@ -0,0 +1,9 @@
local CorePackages = game:GetService("CorePackages")
local Action = require(CorePackages.AppTempCommon.Common.Action)
return Action(script.Name, function(inputType)
return {
inputType = inputType,
}
end)
@@ -0,0 +1,9 @@
local CorePackages = game:GetService("CorePackages")
local Action = require(CorePackages.AppTempCommon.Common.Action)
return Action(script.Name, function(inspectMenuOpen)
return {
inspectMenuOpen = inspectMenuOpen,
}
end)
@@ -0,0 +1,9 @@
local CorePackages = game:GetService("CorePackages")
local Action = require(CorePackages.AppTempCommon.Common.Action)
return Action(script.Name, function(isDead)
return {
isDead = isDead,
}
end)
@@ -0,0 +1,9 @@
local CorePackages = game:GetService("CorePackages")
local Action = require(CorePackages.AppTempCommon.Common.Action)
return Action(script.Name, function(open)
return {
open = open,
}
end)
@@ -0,0 +1,9 @@
local CorePackages = game:GetService("CorePackages")
local Action = require(CorePackages.AppTempCommon.Common.Action)
return Action(script.Name, function(menuOpen)
return {
menuOpen = menuOpen,
}
end)
@@ -0,0 +1,9 @@
local CorePackages = game:GetService("CorePackages")
local Action = require(CorePackages.AppTempCommon.Common.Action)
return Action(script.Name, function(open)
return {
open = open,
}
end)
@@ -0,0 +1,10 @@
local CorePackages = game:GetService("CorePackages")
local Action = require(CorePackages.AppTempCommon.Common.Action)
return Action(script.Name, function(respawnEnabled, respawnCallback)
return {
respawnEnabled = respawnEnabled,
customCallback = respawnCallback,
}
end)
@@ -0,0 +1,9 @@
local CorePackages = game:GetService("CorePackages")
local Action = require(CorePackages.AppTempCommon.Common.Action)
return Action(script.Name, function(screenSize)
return {
screenSize = screenSize,
}
end)
@@ -0,0 +1,9 @@
local CorePackages = game:GetService("CorePackages")
local Action = require(CorePackages.AppTempCommon.Common.Action)
return Action(script.Name, function(isSmallTouchDevice)
return {
isSmallTouchDevice = isSmallTouchDevice,
}
end)
@@ -0,0 +1,9 @@
local CorePackages = game:GetService("CorePackages")
local Action = require(CorePackages.AppTempCommon.Common.Action)
return Action(script.Name, function(enabled)
return {
enabled = enabled,
}
end)
@@ -0,0 +1,9 @@
local CorePackages = game:GetService("CorePackages")
local Action = require(CorePackages.AppTempCommon.Common.Action)
return Action(script.Name, function(messages)
return {
messages = messages,
}
end)
@@ -0,0 +1,9 @@
local CorePackages = game:GetService("CorePackages")
local Action = require(CorePackages.AppTempCommon.Common.Action)
return Action(script.Name, function(visible)
return {
visible = visible,
}
end)
@@ -0,0 +1,10 @@
local CorePackages = game:GetService("CorePackages")
local Action = require(CorePackages.AppTempCommon.Common.Action)
return Action(script.Name, function(coreGuiType, enabled)
return {
coreGuiType = coreGuiType,
enabled = enabled,
}
end)
@@ -0,0 +1,10 @@
local CorePackages = game:GetService("CorePackages")
local Action = require(CorePackages.AppTempCommon.Common.Action)
return Action(script.Name, function(health, maxHealth)
return {
health = health,
maxHealth = maxHealth,
}
end)
@@ -0,0 +1,74 @@
local CorePackages = game:GetService("CorePackages")
local CoreGui = game:GetService("CoreGui")
local Roact = require(CorePackages.Roact)
local RoactRodux = require(CorePackages.RoactRodux)
local t = require(CorePackages.Packages.t)
local Components = script.Parent.Parent
local TopBar = Components.Parent
local UpdateChatVisible = require(TopBar.Actions.UpdateChatVisible)
local UpdateChatMessages = require(TopBar.Actions.UpdateChatMessages)
local RobloxGui = CoreGui:WaitForChild("RobloxGui")
local ChatSelector = require(RobloxGui.Modules.ChatSelector)
local GameSettings = UserSettings().GameSettings
local ChatConnector = Roact.PureComponent:extend("ChatConnector")
ChatConnector.validateProps = t.strictInterface({
isSmallTouchScreen = t.boolean,
updateChatVisible = t.callback,
updateChatMessages = t.callback,
})
function ChatConnector:didMount()
local willEnableChat = GameSettings.ChatVisible
if self.props.isSmallTouchScreen then
willEnableChat = false
end
ChatSelector:SetVisible(willEnableChat)
self.props.updateChatMessages(ChatSelector:GetMessageCount())
self.props.updateChatVisible(ChatSelector:GetVisibility())
self.chatVisibleConnection = ChatSelector.VisibilityStateChanged:connect(function(visible)
self.props.updateChatVisible(visible)
end)
self.chatMessagesConnection = ChatSelector.MessagesChanged:connect(function(messages)
self.props.updateChatMessages(messages)
end)
end
function ChatConnector:render()
return nil
end
function ChatConnector:willUnmount()
self.chatVisibleConnection:disconnect()
self.chatMessagesConnection:disconnect()
end
local function mapStateToProps(state)
return {
isSmallTouchScreen = state.displayOptions.isSmallTouchDevice,
}
end
local function mapDispatchToProps(dispatch)
return {
updateChatVisible = function(visible)
return dispatch(UpdateChatVisible(visible))
end,
updateChatMessages = function(messages)
return dispatch(UpdateChatMessages(messages))
end,
}
end
return RoactRodux.UNSTABLE_connect2(mapStateToProps, mapDispatchToProps)(ChatConnector)
@@ -0,0 +1,49 @@
local CorePackages = game:GetService("CorePackages")
local StarterGui = game:GetService("StarterGui")
local Roact = require(CorePackages.Roact)
local RoactRodux = require(CorePackages.RoactRodux)
local t = require(CorePackages.Packages.t)
local Components = script.Parent.Parent
local TopBar = Components.Parent
local UpdateCoreGuiEnabled = require(TopBar.Actions.UpdateCoreGuiEnabled)
local EventConnection = require(TopBar.Parent.Common.EventConnection)
local CoreGuiConnector = Roact.PureComponent:extend("CoreGuiConnector")
CoreGuiConnector.validateProps = t.strictInterface({
updateCoreGuiEnabled = t.callback,
})
function CoreGuiConnector:didMount()
local initalCoreGuiTypes = Enum.CoreGuiType:GetEnumItems()
for _, coreGuiType in ipairs(initalCoreGuiTypes) do
if coreGuiType ~= Enum.CoreGuiType.All then
self.props.updateCoreGuiEnabled(coreGuiType, StarterGui:GetCoreGuiEnabled(coreGuiType))
end
end
end
function CoreGuiConnector:render()
return Roact.createFragment({
CoreGuiChangedConnection = Roact.createElement(EventConnection, {
event = StarterGui.CoreGuiChangedSignal,
callback = function(coreGuiType, enabled)
self.props.updateCoreGuiEnabled(coreGuiType, enabled)
end,
}),
})
end
local function mapDispatchToProps(dispatch)
return {
updateCoreGuiEnabled = function(coreGuiType, enabled)
return dispatch(UpdateCoreGuiEnabled(coreGuiType, enabled))
end,
}
end
return RoactRodux.UNSTABLE_connect2(nil, mapDispatchToProps)(CoreGuiConnector)
@@ -0,0 +1,47 @@
local CorePackages = game:GetService("CorePackages")
local CoreGui = game:GetService("CoreGui")
local Roact = require(CorePackages.Roact)
local RoactRodux = require(CorePackages.RoactRodux)
local t = require(CorePackages.Packages.t)
local RobloxGui = CoreGui:WaitForChild("RobloxGui")
local ChatSelector = require(RobloxGui.Modules.ChatSelector)
local BackpackModule = require(RobloxGui.Modules.BackpackScript)
local EmotesModule = require(RobloxGui.Modules.EmotesMenu.EmotesMenuMaster)
local PlayerListMaster = require(RobloxGui.Modules.PlayerList.PlayerListManager)
local EnabledNotifier = Roact.PureComponent:extend("CoreGuiCEnabledNotifieronnector")
EnabledNotifier.validateProps = t.strictInterface({
topBarEnabled = t.boolean,
})
function EnabledNotifier:notifyEnabled()
PlayerListMaster:SetTopBarEnabled(self.props.topBarEnabled)
ChatSelector:TopbarEnabledChanged(self.props.topBarEnabled)
BackpackModule:TopbarEnabledChanged(self.props.topBarEnabled)
EmotesModule:setTopBarEnabled(self.props.topBarEnabled)
end
function EnabledNotifier:didMount()
self:notifyEnabled()
end
function EnabledNotifier:render()
return nil
end
function EnabledNotifier:didUpdate(prevProps, prevState)
if self.props.topBarEnabled ~= prevProps.topBarEnabled then
self:notifyEnabled()
end
end
local function mapStateToProps(state)
return {
topBarEnabled = state.displayOptions.topbarEnabled,
}
end
return RoactRodux.UNSTABLE_connect2(mapStateToProps, nil)(EnabledNotifier)
@@ -0,0 +1,132 @@
local CorePackages = game:GetService("CorePackages")
local Players = game:GetService("Players")
local Roact = require(CorePackages.Roact)
local RoactRodux = require(CorePackages.RoactRodux)
local t = require(CorePackages.Packages.t)
local Components = script.Parent.Parent
local TopBar = Components.Parent
local SetIsDead = require(TopBar.Actions.SetIsDead)
local UpdateHealth = require(TopBar.Actions.UpdateHealth)
local EventConnection = require(TopBar.Parent.Common.EventConnection)
local LocalPlayer = Players.LocalPlayer
while not LocalPlayer do
Players:GetPropertyChangedSignal("LocalPlayer"):Wait()
LocalPlayer = Players.LocalPlayer
end
local HealthConnector = Roact.PureComponent:extend("HealthConnector")
HealthConnector.validateProps = t.strictInterface({
setIsDead = t.callback,
updateHealth = t.callback,
})
function HealthConnector:init()
local character = LocalPlayer.Character
local humanoid = nil
if character then
humanoid = character:FindFirstChildOfClass("Humanoid")
end
self:setState({
character = character,
humanoid = humanoid,
})
end
function HealthConnector:didMount()
if self.state.humanoid then
self.props.updateHealth(self.state.humanoid.Health, self.state.humanoid.MaxHealth)
end
end
function HealthConnector:render()
local eventConnections = {}
if self.state.humanoid then
eventConnections["HealthChangedConnection"] = Roact.createElement(EventConnection, {
event = self.state.humanoid.HealthChanged,
callback = function(health)
self.props.updateHealth(self.state.humanoid.Health, self.state.humanoid.MaxHealth)
end,
})
eventConnections["HumanoidDiedConnection"] = Roact.createElement(EventConnection, {
event = self.state.humanoid.Died,
callback = function()
self.props.setIsDead(true)
end,
})
elseif self.state.character then
eventConnections["ChildAddedConnection"] = Roact.createElement(EventConnection, {
event = self.state.character.ChildAdded,
callback = function(child)
if child:IsA("Humanoid") then
self:setState({
humanoid = child,
})
end
end,
})
end
eventConnections["CharacterAddedConnection"] = Roact.createElement(EventConnection, {
event = LocalPlayer.CharacterAdded,
callback = function(character)
self:setState({
humanoid = character:FindFirstChildOfClass("Humanoid"),
character = character,
})
self.props.setIsDead(false)
end,
})
eventConnections["CharacterRemovingConnection"] = Roact.createElement(EventConnection, {
event = LocalPlayer.CharacterRemoving,
callback = function()
self:setState({
humanoid = Roact.None,
character = Roact.None,
})
end,
})
return Roact.createFragment(eventConnections)
end
function HealthConnector:didUpdate(prevProps, prevState)
if self.state.humanoid == nil then
self.props.updateHealth(0, 0)
elseif self.state.humanoid ~= prevState.humanoid then
self.props.setIsDead(false)
self.props.updateHealth(self.state.humanoid.Health, self.state.humanoid.MaxHealth)
end
if self.state.character and self.state.character ~= prevState.character then
local humanoid = self.state.character:FindFirstChildWhichIsA("Humanoid")
if humanoid then
self:setState({
humanoid = Roact.None,
character = Roact.None,
})
end
end
end
local function mapDispatchToProps(dispatch)
return {
setIsDead = function(isDead)
return dispatch(SetIsDead(isDead))
end,
updateHealth = function(health, maxHealth)
return dispatch(UpdateHealth(health, maxHealth))
end,
}
end
return RoactRodux.UNSTABLE_connect2(nil, mapDispatchToProps)(HealthConnector)
@@ -0,0 +1,64 @@
local CorePackages = game:GetService("CorePackages")
local UserInputService = game:GetService("UserInputService")
local Roact = require(CorePackages.Roact)
local RoactRodux = require(CorePackages.RoactRodux)
local Components = script.Parent.Parent
local TopBar = Components.Parent
local EventConnection = require(TopBar.Parent.Common.EventConnection)
local SetInputType = require(TopBar.Actions.SetInputType)
local Constants = require(TopBar.Constants)
local InputType = Constants.InputType
local LastInputTypeConnector = Roact.PureComponent:extend("LastInputTypeConnector")
local inputTypeMap = {
[Enum.UserInputType.MouseButton2] = InputType.MouseAndKeyBoard,
[Enum.UserInputType.MouseButton3] = InputType.MouseAndKeyBoard,
[Enum.UserInputType.MouseWheel] = InputType.MouseAndKeyBoard,
[Enum.UserInputType.MouseMovement] = InputType.MouseAndKeyBoard,
[Enum.UserInputType.Keyboard] = InputType.MouseAndKeyBoard,
[Enum.UserInputType.Gamepad1] = InputType.Gamepad,
[Enum.UserInputType.Gamepad2] = InputType.Gamepad,
[Enum.UserInputType.Gamepad3] = InputType.Gamepad,
[Enum.UserInputType.Gamepad4] = InputType.Gamepad,
[Enum.UserInputType.Gamepad5] = InputType.Gamepad,
[Enum.UserInputType.Gamepad6] = InputType.Gamepad,
[Enum.UserInputType.Gamepad7] = InputType.Gamepad,
[Enum.UserInputType.Gamepad8] = InputType.Gamepad,
[Enum.UserInputType.Touch] = InputType.Touch,
}
function LastInputTypeConnector:didMount()
local initalInputType = inputTypeMap[UserInputService:GetLastInputType()]
if initalInputType then
self.props.setInputType(initalInputType)
end
end
function LastInputTypeConnector:render()
return Roact.createElement(EventConnection, {
event = UserInputService.LastInputTypeChanged,
callback = function(lastInputType)
local inputType = inputTypeMap[lastInputType]
if inputType then
self.props.setInputType(inputType)
end
end,
})
end
return RoactRodux.UNSTABLE_connect2(nil, function(dispatch)
return {
setInputType = function(inputType)
return dispatch(SetInputType(inputType))
end,
}
end)(LastInputTypeConnector)
@@ -0,0 +1,114 @@
local CorePackages = game:GetService("CorePackages")
local CoreGui = game:GetService("CoreGui")
local Roact = require(CorePackages.Roact)
local RoactRodux = require(CorePackages.RoactRodux)
local t = require(CorePackages.Packages.t)
local Components = script.Parent.Parent
local TopBar = Components.Parent
local SetMenuOpen = require(TopBar.Actions.SetMenuOpen)
local SetRespawnBehaviour = require(TopBar.Actions.SetRespawnBehaviour)
local EventConnection = require(TopBar.Parent.Common.EventConnection)
local RobloxGui = CoreGui:WaitForChild("RobloxGui")
local isNewInGameMenuEnabled = require(RobloxGui.Modules.isNewInGameMenuEnabled)
local isNewGamepadMenuEnabled = require(RobloxGui.Modules.Flags.isNewGamepadMenuEnabled)
local MenuConnector = Roact.PureComponent:extend("MenuConnector")
MenuConnector.validateProps = t.strictInterface({
setMenuOpen = t.callback,
setRespawnBehaviour = isNewGamepadMenuEnabled() and t.callback or nil,
})
function MenuConnector:didMount()
if isNewInGameMenuEnabled() then
local InGameMenu = require(RobloxGui.Modules.InGameMenu)
self.props.setMenuOpen(InGameMenu.getOpen())
if isNewGamepadMenuEnabled() then
local isEnabled, customCallback = InGameMenu.getRespawnBehaviour()
self.props.setRespawnBehaviour(isEnabled, customCallback)
end
else
local SettingsHub = require(RobloxGui.Modules.Settings.SettingsHub)
self.props.setMenuOpen(SettingsHub:GetVisibility())
if isNewGamepadMenuEnabled() then
local isEnabled, customCallback = SettingsHub:GetRespawnBehaviour()
self.props.setRespawnBehaviour(isEnabled, customCallback)
end
end
end
function MenuConnector:render()
if isNewInGameMenuEnabled() then
--TODO: Move require the top of the script when removing isNewInGameMenuEnabled
local InGameMenu = require(RobloxGui.Modules.InGameMenu)
local inGameMenuOpenChangedEvent = InGameMenu.getOpenChangedEvent()
local respawnBehaviourChangedEvent
if isNewGamepadMenuEnabled() then
respawnBehaviourChangedEvent = InGameMenu.getRespawnBehaviourChangedEvent()
end
return Roact.createFragment({
MenuOpenChangedConnection = Roact.createElement(EventConnection, {
event = inGameMenuOpenChangedEvent.Event,
callback = function(open)
self.props.setMenuOpen(open)
end,
}),
RespawnBehaviourConnection = isNewGamepadMenuEnabled() and Roact.createElement(EventConnection, {
event = respawnBehaviourChangedEvent.Event,
callback = function(isEnabled, customCallback)
self.props.setRespawnBehaviour(isEnabled, customCallback)
end,
}),
})
else
local SettingsHub = require(RobloxGui.Modules.Settings.SettingsHub)
local settingsHubOpenedEvent = SettingsHub.SettingsShowEvent
local respawnBehaviourChangedEvent
if isNewGamepadMenuEnabled() then
respawnBehaviourChangedEvent = SettingsHub.RespawnBehaviourChangedEvent
end
return Roact.createFragment({
MenuOpenChangedConnection = Roact.createElement(EventConnection, {
event = settingsHubOpenedEvent.Event,
callback = function(open)
self.props.setMenuOpen(open)
end,
}),
RespawnBehaviourConnection = isNewGamepadMenuEnabled() and Roact.createElement(EventConnection, {
event = respawnBehaviourChangedEvent.Event,
callback = function(isEnabled, customCallback)
self.props.setRespawnBehaviour(isEnabled, customCallback)
end,
}),
})
end
end
local function mapDispatchToProps(dispatch)
return {
setMenuOpen = function(open)
return dispatch(SetMenuOpen(open))
end,
setRespawnBehaviour = isNewGamepadMenuEnabled() and function(isEnabled, customCallback)
return dispatch(SetRespawnBehaviour(isEnabled, customCallback))
end or nil,
}
end
return RoactRodux.UNSTABLE_connect2(nil, mapDispatchToProps)(MenuConnector)
@@ -0,0 +1,95 @@
local CorePackages = game:GetService("CorePackages")
local CoreGui = game:GetService("CoreGui")
local Roact = require(CorePackages.Roact)
local RoactRodux = require(CorePackages.RoactRodux)
local t = require(CorePackages.Packages.t)
local Components = script.Parent.Parent
local TopBar = Components.Parent
local SetBackpackOpen = require(TopBar.Actions.SetBackpackOpen)
local SetEmotesOpen = require(TopBar.Actions.SetEmotesOpen)
local SetLeaderboardOpen = require(TopBar.Actions.SetLeaderboardOpen)
local SetEmotesEnabled = require(TopBar.Actions.SetEmotesEnabled)
local EventConnection = require(TopBar.Parent.Common.EventConnection)
local RobloxGui = CoreGui:WaitForChild("RobloxGui")
local BackpackModule = require(RobloxGui.Modules.BackpackScript)
local EmotesMenuMaster = require(RobloxGui.Modules.EmotesMenu.EmotesMenuMaster)
local PlayerListMaster = require(RobloxGui.Modules.PlayerList.PlayerListManager)
local OpenUIConnector = Roact.PureComponent:extend("OpenUIConnector")
OpenUIConnector.validateProps = t.strictInterface({
setBackpackOpen = t.callback,
setEmotesOpen = t.callback,
setLeaderboardOpen = t.callback,
setEmotesEnabled = t.callback,
})
function OpenUIConnector:didMount()
self.props.setLeaderboardOpen(PlayerListMaster:GetSetVisible())
self.props.setBackpackOpen(BackpackModule.IsOpen)
self.props.setEmotesOpen(EmotesMenuMaster:isOpen())
self.props.setEmotesEnabled(EmotesMenuMaster.MenuIsVisible)
end
function OpenUIConnector:render()
local leaderboardEvent = PlayerListMaster:GetSetVisibleChangedEvent()
return Roact.createFragment({
LeaderboardOpenChangedConnection = Roact.createElement(EventConnection, {
event = leaderboardEvent.Event,
callback = function(open)
self.props.setLeaderboardOpen(open)
end,
}),
BackpackOpenChangedConnection = Roact.createElement(EventConnection, {
event = BackpackModule.StateChanged.Event,
callback = function(open)
self.props.setBackpackOpen(open)
end,
}),
EmotesOpenChangedConnection = Roact.createElement(EventConnection, {
event = EmotesMenuMaster.EmotesMenuToggled.Event,
callback = function(open)
self.props.setEmotesOpen(open)
end,
}),
EmotesEnabledChangedConnection = Roact.createElement(EventConnection, {
event = EmotesMenuMaster.MenuVisibilityChanged.Event,
callback = function(enabled)
self.props.setEmotesEnabled(enabled)
end,
}),
})
end
local function mapDispatchToProps(dispatch)
return {
setBackpackOpen = function(open)
return dispatch(SetBackpackOpen(open))
end,
setEmotesOpen = function(open)
return dispatch(SetEmotesOpen(open))
end,
setLeaderboardOpen = function(open)
return dispatch(SetLeaderboardOpen(open))
end,
setEmotesEnabled = function(enabled)
return dispatch(SetEmotesEnabled(enabled))
end,
}
end
return RoactRodux.UNSTABLE_connect2(nil, mapDispatchToProps)(OpenUIConnector)
@@ -0,0 +1,27 @@
local CorePackages = game:GetService("CorePackages")
local Roact = require(CorePackages.Roact)
local CoreGuiConnector = require(script.CoreGuiConnector)
local MenuConnector = require(script.MenuConnector)
local ChatConnector = require(script.ChatConnector)
local HealthConnector = require(script.HealthConnector)
local EnabledNotifier = require(script.EnabledNotifier)
local OpenUIConnector = require(script.OpenUIConnector)
local LastInputTypeConnector = require(script.LastInputTypeConnector)
local Connection = Roact.PureComponent:extend("Connection")
function Connection:render()
return Roact.createFragment({
CoreGuiConnector = Roact.createElement(CoreGuiConnector),
MenuConnector = Roact.createElement(MenuConnector),
ChatConnector = Roact.createElement(ChatConnector),
HealthConnector = Roact.createElement(HealthConnector),
EnabledNotifier = Roact.createElement(EnabledNotifier),
OpenUIConnector = Roact.createElement(OpenUIConnector),
LastInputTypeConnector = Roact.createElement(LastInputTypeConnector),
})
end
return Connection
@@ -0,0 +1,95 @@
local CorePackages = game:GetService("CorePackages")
local CoreGui = game:GetService("CoreGui")
local Roact = require(CorePackages.Roact)
local RoactRodux = require(CorePackages.RoactRodux)
local t = require(CorePackages.Packages.t)
local UIBlox = require(CorePackages.UIBlox)
local withStyle = UIBlox.Core.Style.withStyle
local Badge = UIBlox.App.Indicator.Badge
local RobloxGui = CoreGui:WaitForChild("RobloxGui")
local ChatSelector = require(RobloxGui.Modules.ChatSelector)
local TenFootInterface = require(RobloxGui.Modules.TenFootInterface)
local IconButton = require(script.Parent.IconButton)
local GameSettings = UserSettings().GameSettings
local ChatIcon = Roact.PureComponent:extend("ChatIcon")
local CHAT_ICON_AREA_WIDTH = 44
local ICON_SIZE = 20
local BADGE_OFFSET_X = 18
local BADGE_OFFSET_Y = 2
ChatIcon.validateProps = t.strictInterface({
layoutOrder = t.integer,
chatVisible = t.boolean,
unreadMessages = t.integer,
topBarEnabled = t.boolean,
chatEnabled = t.boolean,
})
function ChatIcon:init()
self.chatIconActivated = function()
ChatSelector:ToggleVisibility()
GameSettings.ChatVisible = ChatSelector:GetVisibility()
end
end
function ChatIcon:render()
return withStyle(function(style)
local chatEnabled = self.props.topBarEnabled and self.props.chatEnabled and not TenFootInterface:IsEnabled()
local chatIcon = "rbxasset://textures/ui/TopBar/chatOn.png"
if not self.props.chatVisible then
chatIcon = "rbxasset://textures/ui/TopBar/chatOff.png"
end
return Roact.createElement("TextButton", {
Text = "",
Visible = chatEnabled,
BackgroundTransparency = 1,
Size = UDim2.new(0, CHAT_ICON_AREA_WIDTH, 1, 0),
LayoutOrder = self.props.layoutOrder,
}, {
Background = Roact.createElement(IconButton, {
icon = chatIcon,
iconSize = ICON_SIZE,
onActivated = self.chatIconActivated,
}),
BadgeContainer = Roact.createElement("Frame", {
BackgroundTransparency = 1,
Size = UDim2.fromScale(1, 1),
ZIndex = 2,
}, {
Badge = self.props.unreadMessages > 0 and Roact.createElement(Badge, {
position = UDim2.fromOffset(BADGE_OFFSET_X, BADGE_OFFSET_Y),
anchorPoint = Vector2.new(0, 0),
hasShadow = false,
value = self.props.unreadMessages,
})
})
})
end)
end
local function mapStateToProps(state)
return {
chatVisible = state.chat.visible,
unreadMessages = state.chat.unreadMessages,
topBarEnabled = state.displayOptions.topbarEnabled,
chatEnabled = state.coreGuiEnabled[Enum.CoreGuiType.Chat],
}
end
return RoactRodux.UNSTABLE_connect2(mapStateToProps, nil)(ChatIcon)
@@ -0,0 +1,93 @@
local CorePackages = game:GetService("CorePackages")
local Roact = require(CorePackages.Roact)
local RoactRodux = require(CorePackages.RoactRodux)
local t = require(CorePackages.Packages.t)
local UIBlox = require(CorePackages.UIBlox)
local withStyle = UIBlox.Core.Style.withStyle
local ButtonHint = require(script.Parent.ButtonHint)
local BOTTOMBAR_HEIGHT = 72
local SCREENSIDE_PADDING = 24
local PADDING = 40
local BottomBar = Roact.PureComponent:extend("BottomBar")
BottomBar.validateProps = t.strictInterface({
respawnEnabled = t.boolean,
})
function BottomBar:render()
return withStyle(function(style)
local theme = style.Theme
return Roact.createElement("Frame", {
BorderSizePixel = 0,
BackgroundTransparency = theme.BackgroundUIContrast.Transparency,
BackgroundColor3 = theme.BackgroundUIContrast.Color,
Size = UDim2.new(1, 0, 0, BOTTOMBAR_HEIGHT),
Position = UDim2.fromScale(0, 1),
AnchorPoint = Vector2.new(0, 1),
}, {
Padding = Roact.createElement("UIPadding", {
PaddingLeft = UDim.new(0, SCREENSIDE_PADDING),
PaddingRight = UDim.new(0, SCREENSIDE_PADDING),
}),
LeftFrame = Roact.createElement("Frame", {
BackgroundTransparency = 1,
Size = UDim2.fromScale(0.5, 1),
}, {
Layout = Roact.createElement("UIListLayout", {
SortOrder = Enum.SortOrder.LayoutOrder,
FillDirection = Enum.FillDirection.Horizontal,
HorizontalAlignment = Enum.HorizontalAlignment.Left,
VerticalAlignment = Enum.VerticalAlignment.Center,
Padding = UDim.new(0, PADDING),
}),
LeaveHint = Roact.createElement(ButtonHint, {
localizationKey = "CoreScripts.TopBar.Back",
keyLabel = "B",
}),
}),
RightFrame = Roact.createElement("Frame", {
BackgroundTransparency = 1,
Size = UDim2.fromScale(0.5, 1),
Position = UDim2.fromScale(1, 0),
AnchorPoint = Vector2.new(1, 0),
}, {
Layout = Roact.createElement("UIListLayout", {
SortOrder = Enum.SortOrder.LayoutOrder,
FillDirection = Enum.FillDirection.Horizontal,
HorizontalAlignment = Enum.HorizontalAlignment.Right,
VerticalAlignment = Enum.VerticalAlignment.Center,
Padding = UDim.new(0, PADDING),
}),
LeaveHint = Roact.createElement(ButtonHint, {
layoutOrder = 2,
localizationKey = "CoreScripts.TopBar.Leave",
keyLabel = "X",
}),
RespawnHint = self.props.respawnEnabled and Roact.createElement(ButtonHint, {
layoutOrder = 1,
localizationKey = "CoreScripts.TopBar.Respawn",
keyLabel = "Y",
}),
}),
})
end)
end
local function mapStateToProps(state)
return {
respawnEnabled = state.respawn.enabled,
}
end
return RoactRodux.UNSTABLE_connect2(mapStateToProps, nil)(BottomBar)
@@ -0,0 +1,91 @@
local CorePackages = game:GetService("CorePackages")
local CoreGui = game:GetService("CoreGui")
local TextService = game:GetService("TextService")
local Roact = require(CorePackages.Roact)
local t = require(CorePackages.Packages.t)
local UIBlox = require(CorePackages.UIBlox)
local withStyle = UIBlox.Core.Style.withStyle
local Images = UIBlox.App.ImageSet.Images
local ImageSetLabel = UIBlox.Core.ImageSet.Label
local RobloxGui = CoreGui:WaitForChild("RobloxGui")
local RobloxTranslator = require(RobloxGui.Modules.RobloxTranslator)
local ICON_SIZE = 28
local PADDING = 12
local ICON_RING_IMAGE = Images["component_assets/circle_29_stroke_1"]
local ButtonHint = Roact.PureComponent:extend("ButtonHint")
ButtonHint.validateProps = t.strictInterface({
layoutOrder = t.optional(t.integer),
localizationKey = t.string,
keyLabel = t.string,
})
function ButtonHint:render()
return withStyle(function(style)
local theme = style.Theme
local font = style.Font
local text = RobloxTranslator:FormatByKey(self.props.localizationKey)
local fontSize = font.BaseSize * font.Header1.RelativeSize
local textWidth = TextService:GetTextSize(text, fontSize, font.Header1.Font, Vector2.new(1000, 1000)).X
local buttonHintWidth = ICON_SIZE + PADDING + textWidth
return Roact.createElement("Frame", {
LayoutOrder = self.props.layoutOrder,
BackgroundTransparency = 1,
Size = UDim2.new(0, buttonHintWidth, 1, 0),
}, {
Layout = Roact.createElement("UIListLayout", {
SortOrder = Enum.SortOrder.LayoutOrder,
FillDirection = Enum.FillDirection.Horizontal,
HorizontalAlignment = Enum.HorizontalAlignment.Left,
VerticalAlignment = Enum.VerticalAlignment.Center,
Padding = UDim.new(0, PADDING),
}),
Icon = Roact.createElement(ImageSetLabel, {
BackgroundTransparency = 1,
LayoutOrder = 1,
Size = UDim2.fromOffset(ICON_SIZE, ICON_SIZE),
Image = ICON_RING_IMAGE,
ImageTransparency = theme.UIDefault.Transparency,
ImageColor3 = theme.UIDefault.Color,
}, {
Text = Roact.createElement("TextLabel", {
BackgroundTransparency = 1,
Size = UDim2.fromScale(1, 1),
Text = self.props.keyLabel,
Font = font.Header2.Font,
TextSize = font.BaseSize * font.Header2.RelativeSize,
TextColor3 = theme.UIDefault.Color,
TextTransparency = theme.UIDefault.TextTransparency,
TextXAlignment = Enum.TextXAlignment.Center,
TextYAlignment = Enum.TextYAlignment.Center,
}),
}),
Text = Roact.createElement("TextLabel", {
BackgroundTransparency = 1,
Size = UDim2.new(0, textWidth, 0, 1),
LayoutOrder = 2,
Font = font.Header1.Font,
Text = text,
TextSize = fontSize,
TextColor3 = theme.TextEmphasis.Color,
TextTransparency = theme.TextEmphasis.TextTransparency,
TextXAlignment = Enum.TextXAlignment.Left,
TextYAlignment = Enum.TextYAlignment.Center,
})
})
end)
end
return ButtonHint
@@ -0,0 +1,58 @@
local CorePackages = game:GetService("CorePackages")
local Roact = require(CorePackages.Roact)
local t = require(CorePackages.Packages.t)
local UIBlox = require(CorePackages.UIBlox)
local RoactRodux = require(CorePackages.RoactRodux)
local withStyle = UIBlox.Core.Style.withStyle
local Images = UIBlox.App.ImageSet.Images
local ImageSetLabel = UIBlox.Core.ImageSet.Label
local Badge = UIBlox.App.Indicator.Badge
local CHAT_ON = Images["icons/menu/chat_on"]
local CHAT_OFF = Images["icons/menu/chat_off"]
local ChatIcon = Roact.PureComponent:extend("ChatIcon")
ChatIcon.validateProps = t.strictInterface({
chatVisible = t.boolean,
unreadMessages = t.integer,
})
function ChatIcon:render()
return withStyle(function(style)
local theme = style.Theme
local chatIcon = CHAT_ON
if not self.props.chatVisible then
chatIcon = CHAT_OFF
end
return Roact.createElement(ImageSetLabel, {
Size = UDim2.fromScale(1, 1),
BackgroundTransparency = 1,
Image = chatIcon,
ImageColor3 = theme.IconEmphasis.Color,
ImageTransparency = theme.IconEmphasis.Transparency,
ZIndex = 2,
}, {
Badge = self.props.unreadMessages > 0 and Roact.createElement(Badge, {
position = UDim2.new(0, 24, 0.5, 0),
anchorPoint = Vector2.new(0, 0.5),
hasShadow = false,
value = self.props.unreadMessages,
})
})
end)
end
local function mapStateToProps(state)
return {
chatVisible = state.chat.visible,
unreadMessages = state.chat.unreadMessages,
}
end
return RoactRodux.UNSTABLE_connect2(mapStateToProps, nil)(ChatIcon)
@@ -0,0 +1,145 @@
local CorePackages = game:GetService("CorePackages")
local CoreGui = game:GetService("CoreGui")
local Roact = require(CorePackages.Roact)
local t = require(CorePackages.Packages.t)
local UIBlox = require(CorePackages.UIBlox)
local withStyle = UIBlox.Core.Style.withStyle
local ImageSetLabel = UIBlox.Core.ImageSet.Label
local RobloxGui = CoreGui:WaitForChild("RobloxGui")
local RobloxTranslator = require(RobloxGui.Modules.RobloxTranslator)
local ICON_SIZE = 36
local BORDER_SIZE = 2
local PADDING = 12
local DIVIDER_SIZE = 1
local MenuCell = Roact.PureComponent:extend("MenuCell")
MenuCell.validateProps = t.strictInterface({
layoutOrder = t.integer,
icon = t.optional(t.union(t.table, t.string)),
iconComponent = t.optional(t.table),
localizationKey = t.string,
height = t.numberPositive,
hasDivider = t.boolean,
isSelected = t.boolean,
})
function MenuCell:render()
return withStyle(function(style)
local theme = style.Theme
local font = style.Font
local text = RobloxTranslator:FormatByKey(self.props.localizationKey)
return Roact.createElement("Frame", {
LayoutOrder = self.props.layoutOrder,
BackgroundTransparency = 1,
Size = UDim2.new(1, 0, 0, self.props.height),
}, {
SelectedFrame = self.props.isSelected and Roact.createElement("Frame", {
BackgroundTransparency = 1,
Size = UDim2.new(1, BORDER_SIZE * 2, 1, BORDER_SIZE * 2),
Position = UDim2.fromScale(0.5, 0.5),
AnchorPoint = Vector2.new(0.5, 0.5),
}, {
TopBorder = Roact.createElement("Frame", {
BorderSizePixel = 0,
Size = UDim2.new(1, 0, 0, BORDER_SIZE),
BackgroundTransparency = theme.SystemPrimaryOnHover.Transparency,
BackgroundColor3 = theme.SystemPrimaryOnHover.Color,
}),
LeftBorder = Roact.createElement("Frame", {
BorderSizePixel = 0,
Size = UDim2.new(0, BORDER_SIZE, 1, 0),
BackgroundTransparency = theme.SystemPrimaryOnHover.Transparency,
BackgroundColor3 = theme.SystemPrimaryOnHover.Color,
}),
RightBorder = Roact.createElement("Frame", {
BorderSizePixel = 0,
Size = UDim2.new(0, BORDER_SIZE, 1, 0),
Position = UDim2.fromScale(1, 0),
AnchorPoint = Vector2.new(1, 0),
BackgroundTransparency = theme.SystemPrimaryOnHover.Transparency,
BackgroundColor3 = theme.SystemPrimaryOnHover.Color,
}),
BottomBorder = Roact.createElement("Frame", {
BorderSizePixel = 0,
Size = UDim2.new(1, 0, 0, BORDER_SIZE),
Position = UDim2.fromScale(0, 1),
AnchorPoint = Vector2.new(0, 1),
BackgroundTransparency = theme.SystemPrimaryOnHover.Transparency,
BackgroundColor3 = theme.SystemPrimaryOnHover.Color,
}),
}),
CellContents = Roact.createElement("Frame", {
BackgroundTransparency = 1,
Size = UDim2.fromScale(1, 1),
}, {
Layout = Roact.createElement("UIListLayout", {
SortOrder = Enum.SortOrder.LayoutOrder,
FillDirection = Enum.FillDirection.Horizontal,
HorizontalAlignment = Enum.HorizontalAlignment.Left,
VerticalAlignment = Enum.VerticalAlignment.Center,
Padding = UDim.new(0, PADDING),
}),
Padding = Roact.createElement("UIPadding", {
PaddingLeft = UDim.new(0, PADDING),
PaddingRight = UDim.new(0, PADDING),
}),
IconFrame = Roact.createElement("Frame", {
BackgroundTransparency = 1,
LayoutOrder = 1,
Size = UDim2.fromOffset(ICON_SIZE, ICON_SIZE),
}, {
IconLabel = self.props.icon ~= nil and Roact.createElement(ImageSetLabel, {
BackgroundTransparency = 1,
Image = self.props.icon,
Size = UDim2.fromScale(1, 1),
ImageTransparency = theme.IconEmphasis.Transparency,
ImageColor3 = theme.IconEmphasis.Color,
}),
IconComponent = self.props.iconComponent ~= nil and Roact.createElement(self.props.iconComponent),
}),
Text = Roact.createElement("TextLabel", {
BackgroundTransparency = 1,
LayoutOrder = 2,
Size = UDim2.new(1, -(PADDING + ICON_SIZE), 1, 0),
Text = text,
Font = font.Header2.Font,
TextSize = font.BaseSize * font.Header2.RelativeSize,
TextColor3 = theme.TextEmphasis.Color,
TextTransparency = theme.TextEmphasis.TextTransparency,
TextXAlignment = Enum.TextXAlignment.Left,
TextYAlignment = Enum.TextYAlignment.Center,
TextTruncate = Enum.TextTruncate.AtEnd,
})
}),
Divider = self.props.hasDivider and Roact.createElement("Frame", {
BorderSizePixel = 0,
Size = UDim2.new(1, 0, 0, DIVIDER_SIZE),
Position = UDim2.fromScale(0, 1),
AnchorPoint = Vector2.new(0, 1),
BackgroundTransparency = theme.Divider.Transparency,
BackgroundColor3 = theme.Divider.Color,
}),
})
end)
end
return MenuCell
@@ -0,0 +1,142 @@
local CorePackages = game:GetService("CorePackages")
local Roact = require(CorePackages.Roact)
local t = require(CorePackages.Packages.t)
local UIBlox = require(CorePackages.UIBlox)
local RoactRodux = require(CorePackages.RoactRodux)
local withStyle = UIBlox.Core.Style.withStyle
local Images = UIBlox.App.ImageSet.Images
local ImageSetLabel = UIBlox.Core.ImageSet.Label
local ICON_SIZE = 80
local PLACEHOLDER_ICON_SIZE = 44
local PADDING = 20
local TEXT_PADDING_TOP = 25
local TEXT_PADDING_BOTTOM = 20
local DIVIDER_SIZE = 1
local GAME_ICON_REQUEST_SIZE = 128
local IMAGE_UNAVAILABLE = Images["icons/imageUnavailable"]
local ROUNDED_BACKGROUND_ASSET = Images["component_assets/circle_17"]
local ROUNDED_SLICE_CENTER = Rect.new(8, 8, 9, 9)
local MenuHeader = Roact.PureComponent:extend("MenuHeader")
MenuHeader.validateProps = t.strictInterface({
gameName = t.string,
layoutOrder = t.integer,
height = t.numberPositive,
})
function MenuHeader:render()
return withStyle(function(style)
local theme = style.Theme
local font = style.Font
local gameIconComponent
if game.GameId == 0 then
gameIconComponent = Roact.createElement(ImageSetLabel, {
BackgroundTransparency = 1,
Image = ROUNDED_BACKGROUND_ASSET,
SliceCenter = ROUNDED_SLICE_CENTER,
ScaleType = Enum.ScaleType.Slice,
Size = UDim2.fromOffset(ICON_SIZE, ICON_SIZE),
ImageColor3 = style.Theme.PlaceHolder.Color,
ImageTransparency = style.Theme.PlaceHolder.Transparency,
}, {
PlaceholderIcon = Roact.createElement(ImageSetLabel, {
BackgroundTransparency = 1,
Size = UDim2.fromOffset(PLACEHOLDER_ICON_SIZE, PLACEHOLDER_ICON_SIZE),
Position = UDim2.fromScale(0.5, 0.5),
Image = IMAGE_UNAVAILABLE,
ImageColor3 = style.Theme.UIDefault.Color,
ImageTransparency = style.Theme.UIDefault.Transparency,
AnchorPoint = Vector2.new(0.5, 0.5),
}),
})
else
local iconUrl = ("rbxthumb://type=GameIcon&id=%d&w=%d&h=%d"):format(
game.GameId,
GAME_ICON_REQUEST_SIZE,
GAME_ICON_REQUEST_SIZE
)
gameIconComponent = Roact.createElement("ImageLabel", {
LayoutOrder = 1,
BackgroundTransparency = 1,
Size = UDim2.fromOffset(ICON_SIZE, ICON_SIZE),
Image = iconUrl,
})
end
return Roact.createElement("Frame", {
LayoutOrder = self.props.layoutOrder,
BackgroundTransparency = 1,
Size = UDim2.new(1, 0, 0, self.props.height),
}, {
ContentsFrame = Roact.createElement("Frame", {
BackgroundTransparency = 1,
Size = UDim2.fromScale(1, 1),
}, {
Layout = Roact.createElement("UIListLayout", {
SortOrder = Enum.SortOrder.LayoutOrder,
FillDirection = Enum.FillDirection.Horizontal,
HorizontalAlignment = Enum.HorizontalAlignment.Left,
VerticalAlignment = Enum.VerticalAlignment.Center,
Padding = UDim.new(0, PADDING),
}),
Padding = Roact.createElement("UIPadding", {
PaddingLeft = UDim.new(0, PADDING),
PaddingRight = UDim.new(0, PADDING),
}),
GameIcon = gameIconComponent,
TextFrame = Roact.createElement("Frame", {
LayoutOrder = 2,
BackgroundTransparency = 1,
Size = UDim2.new(1, -(ICON_SIZE + PADDING), 1, 0),
}, {
TextLabel = Roact.createElement("TextLabel", {
BackgroundTransparency = 1,
Size = UDim2.new(1, 0, 1, -(TEXT_PADDING_TOP + TEXT_PADDING_BOTTOM)),
Position = UDim2.fromOffset(0, TEXT_PADDING_TOP),
Font = font.Header1.Font,
Text = self.props.gameName,
TextSize = font.BaseSize * font.Header1.RelativeSize,
TextColor3 = theme.TextEmphasis.Color,
TextTransparency = theme.TextEmphasis.TextTransparency,
TextXAlignment = Enum.TextXAlignment.Left,
TextYAlignment = Enum.TextYAlignment.Top,
TextTruncate = Enum.TextTruncate.AtEnd,
TextWrapped = true,
}),
})
}),
Divider = Roact.createElement("Frame", {
BorderSizePixel = 0,
Size = UDim2.new(1, 0, 0, DIVIDER_SIZE),
Position = UDim2.fromScale(0, 1),
AnchorPoint = Vector2.new(0, 1),
BackgroundTransparency = theme.Divider.Transparency,
BackgroundColor3 = theme.Divider.Color,
}),
})
end)
end
local function mapStateToProps(state)
return {
gameName = state.gameInfo.name,
}
end
return RoactRodux.UNSTABLE_connect2(mapStateToProps, nil)(MenuHeader)
@@ -0,0 +1,549 @@
local CorePackages = game:GetService("CorePackages")
local ContextActionService = game:GetService("ContextActionService")
local CoreGui = game:GetService("CoreGui")
local GuiService = game:GetService("GuiService")
local Roact = require(CorePackages.Roact)
local RoactRodux = require(CorePackages.RoactRodux)
local Cryo = require(CorePackages.Cryo)
local t = require(CorePackages.Packages.t)
local UIBlox = require(CorePackages.UIBlox)
local withStyle = UIBlox.Core.Style.withStyle
local Images = UIBlox.App.ImageSet.Images
local ImageSetLabel = UIBlox.Core.ImageSet.Label
local MenuHeader = require(script.MenuHeader)
local ChatIcon = require(script.ChatIcon)
local MenuCell = require(script.MenuCell)
local BottomBar = require(script.BottomBar)
local RobloxGui = CoreGui:WaitForChild("RobloxGui")
local TenFootInterface = require(RobloxGui.Modules.TenFootInterface)
local BackpackModule = require(RobloxGui.Modules.BackpackScript)
local EmotesModule = require(RobloxGui.Modules.EmotesMenu.EmotesMenuMaster)
local ChatModule = require(RobloxGui.Modules.ChatSelector)
local PlayerListMaster = require(RobloxGui.Modules.PlayerList.PlayerListManager)
local isNewInGameMenuEnabled = require(RobloxGui.Modules.isNewInGameMenuEnabled)
local InGameMenuConstants = require(RobloxGui.Modules.InGameMenu.Resources.Constants)
local TOGGLE_GAMEPAD_MENU_ACTION = "TopBarGamepadToggleGamepadMenu"
local FREEZE_CONTROLLER_ACTION_NAME = "TopBarGamepadFreezeController"
local CLOSEMENU_ACTION_NAME = "TopBarGamepadCloseMenu"
local LEAVE_GAME_ACTION_NAME = "TopBarGamepadLeaveGame"
local RESPAWN_ACTION_NAME = "TopBarGamepadRespawnAction"
local MOVE_SLECTION_ACTION_NAME = "TopBarGamepadMoveSelection"
local ACTIVATE_SELECTION_ACTION_NAME = "TopBarGamepadActivateSelection"
local GO_TO_TOP_ACTION_NAME = "TopBarGamepadMoveSelectionTop"
local GO_TO_BOTTOM_ACTION_NAME = "TopBarGamepadMoveSelectionBottom"
local THUMBSTICK_MOVE_COOLDOWN = 0.15
local MENU_ICON = Images["icons/logo/block"]
local LEADERBOARD_ICON_ON = "rbxasset://textures/ui/TopBar/leaderboardOn.png"
local LEADERBOARD_ICON_OFF = "rbxasset://textures/ui/TopBar/leaderboardOff.png"
local EMOTES_ICON_ON = "rbxasset://textures/ui/TopBar/emotesOn.png"
local EMOTES_ICON_OFF = "rbxasset://textures/ui/TopBar/emotesOff.png"
local INVENTORY_ICON_ON = "rbxasset://textures/ui/TopBar/inventoryOn.png"
local INVENTORY_ICON_OFF = "rbxasset://textures/ui/TopBar/inventoryOff.png"
local RESPAWN_ICON = Images["icons/actions/respawn"]
local LEAVE_ICON = Images["icons/navigation/close"]
local MENU_BACKGROUND_ASSET = Images["component_assets/circle_17"]
local MENU_SLICE_CENTER = Rect.new(8, 8, 9, 9)
local MENU_SIZE_X = 336
local HEADER_HEIGHT = 120
local CELL_HEIGHT = 56
local GAMEPAD_MENU_MOUNT_TAG = "gamepadMenuMount"
local GAMEPAD_MENU_UPDATE_TAG = "gamepadMenuUpdate"
local GAMEPAD_MENU_KEY = "GamepadMenu"
local GamepadMenu = Roact.PureComponent:extend("GamepadMenu")
GamepadMenu.validateProps = t.strictInterface({
chatEnabled = t.boolean,
leaderboardEnabled = t.boolean,
emotesEnabled = t.boolean,
backpackEnabled = t.boolean,
respawnEnabled = t.boolean,
leaderboardOpen = t.boolean,
backpackOpen = t.boolean,
emotesOpen = t.boolean,
})
function GamepadMenu:init()
debug.profilebegin(GAMEPAD_MENU_MOUNT_TAG)
self:setState({
visible = false,
selectedIndex = 1,
menuActions = {}
})
self.boundMenuOpenActions = false
self.toggleMenuVisibleAction = function(actionName, userInputState, input)
if userInputState ~= Enum.UserInputState.Begin then
return Enum.ContextActionResult.Pass
end
self:setState({
visible = not self.state.visible,
})
return Enum.ContextActionResult.Sink
end
self.closeMenuAction = function(actionName, userInputState, input)
if userInputState ~= Enum.UserInputState.Begin then
return Enum.ContextActionResult.Pass
end
self:setState({
visible = false,
})
return Enum.ContextActionResult.Sink
end
self.leaveGameMenuAction = function(actionName, userInputState, input)
if userInputState ~= Enum.UserInputState.Begin then
return Enum.ContextActionResult.Pass
end
GamepadMenu.leaveGame()
self:setState({
visible = false,
})
return Enum.ContextActionResult.Sink
end
self.respawnMenuAction = function(actionName, userInputState, input)
if userInputState ~= Enum.UserInputState.Begin then
return Enum.ContextActionResult.Pass
end
GamepadMenu.respawnCharacter()
self:setState({
visible = false,
})
return Enum.ContextActionResult.Sink
end
self.lastThumbStickMove = 0
self.moveSelectionAction = function(actionName, userInputState, input)
if userInputState == Enum.UserInputState.Begin then
if input.KeyCode == Enum.KeyCode.DPadUp then
if self.state.selectedIndex > 1 then
self:setState({
selectedIndex = self.state.selectedIndex - 1,
})
end
return Enum.ContextActionResult.Sink
elseif input.KeyCode == Enum.KeyCode.DPadDown then
if self.state.selectedIndex < #self.state.menuActions then
self:setState({
selectedIndex = self.state.selectedIndex + 1,
})
end
return Enum.ContextActionResult.Sink
end
elseif input.KeyCode == Enum.KeyCode.Thumbstick1 then
if tick() - self.lastThumbStickMove < THUMBSTICK_MOVE_COOLDOWN then
return Enum.ContextActionResult.Pass
end
if input.Position.Y > 0.8 then
if self.state.selectedIndex > 1 then
self:setState({
selectedIndex = self.state.selectedIndex - 1,
})
self.lastThumbStickMove = tick()
end
return Enum.ContextActionResult.Pass
elseif input.Position.Y < -0.8 then
if self.state.selectedIndex < #self.state.menuActions then
self:setState({
selectedIndex = self.state.selectedIndex + 1,
})
self.lastThumbStickMove = tick()
end
return Enum.ContextActionResult.Sink
end
end
return Enum.ContextActionResult.Pass
end
self.goToTopAction = function(actionName, userInputState, input)
if userInputState ~= Enum.UserInputState.Begin then
return Enum.ContextActionResult.Pass
end
self:setState({
selectedIndex = 1,
})
end
self.goToBottomAction = function(actionName, userInputState, input)
if userInputState ~= Enum.UserInputState.Begin then
return Enum.ContextActionResult.Pass
end
self:setState({
selectedIndex = #self.state.menuActions,
})
end
self.activateSelectionAction = function(actionName, userInputState, input)
if userInputState ~= Enum.UserInputState.Begin then
return Enum.ContextActionResult.Pass
end
local action = self.state.menuActions[self.state.selectedIndex]
action.onActivated()
self:setState({
visible = false,
})
return Enum.ContextActionResult.Sink
end
self.overlayDismiss = function()
self:setState({
visible = false,
})
end
end
function GamepadMenu.openSettingsMenu()
if isNewInGameMenuEnabled() then
-- todo: move InGameMenu to a script global when removing isNewInGameMenuEnabled
local InGameMenu = require(RobloxGui.Modules.InGameMenu)
InGameMenu.openGameSettingsPage()
else
local MenuModule = require(RobloxGui.Modules.Settings.SettingsHub)
MenuModule:SetVisibility(true, nil, MenuModule.Instance.GameSettingsPage, true,
InGameMenuConstants.AnalyticsMenuOpenTypes.SettingsTriggered)
end
end
function GamepadMenu.toggleChatVisible()
ChatModule:ToggleVisibility()
end
function GamepadMenu.toggleLeaderboard()
PlayerListMaster:SetVisibility(not PlayerListMaster:GetSetVisible())
end
function GamepadMenu.toggleEmotesMenu()
if EmotesModule:isOpen() then
EmotesModule:close()
else
EmotesModule:open()
end
end
function GamepadMenu.toggleBackpack()
BackpackModule:OpenClose()
end
function GamepadMenu.leaveGame()
local MenuModule = require(RobloxGui.Modules.Settings.SettingsHub)
MenuModule:SetVisibility(true, false, MenuModule.Instance.LeaveGamePage, true,
InGameMenuConstants.AnalyticsMenuOpenTypes.GamepadLeaveGame)
end
function GamepadMenu.respawnCharacter()
local MenuModule = require(RobloxGui.Modules.Settings.SettingsHub)
MenuModule:SetVisibility(true, false, MenuModule.Instance.ResetCharacterPage, true,
InGameMenuConstants.AnalyticsMenuOpenTypes.GamepadResetCharacter)
end
function GamepadMenu.getMenuActionsFromProps(props)
local menuActions = {}
table.insert(menuActions, {
name = "Menu",
icon = MENU_ICON,
iconComponent = nil,
localizationKey = "CoreScripts.TopBar.Menu",
onActivated = GamepadMenu.openSettingsMenu,
})
if props.chatEnabled and not TenFootInterface:IsEnabled() then
table.insert(menuActions, {
name = "Chat",
icon = nil,
iconComponent = ChatIcon,
localizationKey = "CoreScripts.TopBar.Chat",
onActivated = GamepadMenu.toggleChatVisible,
})
end
if props.leaderboardEnabled or TenFootInterface:IsEnabled() then
local icon
if props.leaderboardOpen then
icon = LEADERBOARD_ICON_ON
else
icon = LEADERBOARD_ICON_OFF
end
table.insert(menuActions, {
name = "Leaderboard",
icon = icon,
iconComponent = nil,
localizationKey = "CoreScripts.TopBar.Leaderboard",
onActivated = GamepadMenu.toggleLeaderboard,
})
end
if props.emotesEnabled then
local icon
if props.emotesOpen then
icon = EMOTES_ICON_ON
else
icon = EMOTES_ICON_OFF
end
table.insert(menuActions, {
name = "Emotes",
icon = icon,
iconComponent = nil,
localizationKey = "CoreScripts.TopBar.Emotes",
onActivated = GamepadMenu.toggleEmotesMenu,
})
end
if props.backpackEnabled then
local icon
if props.backpackOpen then
icon = INVENTORY_ICON_ON
else
icon = INVENTORY_ICON_OFF
end
table.insert(menuActions, {
name = "Inventory",
icon = icon,
iconComponent = nil,
localizationKey = "CoreScripts.TopBar.Inventory",
onActivated = GamepadMenu.toggleBackpack,
})
end
if props.respawnEnabled then
table.insert(menuActions, {
name = "Respawn",
icon = RESPAWN_ICON,
iconComponent = nil,
localizationKey = "CoreScripts.TopBar.Respawn",
onActivated = GamepadMenu.respawnCharacter,
})
end
table.insert(menuActions, {
name = "Leave",
icon = LEAVE_ICON,
iconComponent = nil,
localizationKey = "CoreScripts.TopBar.Leave",
onActivated = GamepadMenu.leaveGame,
})
return menuActions
end
function GamepadMenu.getDerivedStateFromProps(nextProps, prevState)
local menuActions = GamepadMenu.getMenuActionsFromProps(nextProps)
local selectedIndex = prevState.selectedIndex or 1
if selectedIndex > #menuActions then
selectedIndex = #menuActions
end
return Cryo.Dictionary.join(prevState, {
selectedIndex = selectedIndex,
menuActions = menuActions
})
end
function GamepadMenu:render()
return withStyle(function(style)
local theme = style.Theme
local menuChildren = {}
menuChildren.Layout = Roact.createElement("UIListLayout", {
FillDirection = Enum.FillDirection.Vertical,
HorizontalAlignment = Enum.HorizontalAlignment.Center,
VerticalAlignment = Enum.VerticalAlignment.Top,
SortOrder = Enum.SortOrder.LayoutOrder,
})
menuChildren.MenuHeader = Roact.createElement(MenuHeader, {
layoutOrder = 1,
height = HEADER_HEIGHT,
})
for index, action in ipairs(self.state.menuActions) do
menuChildren[action.name] = Roact.createElement(MenuCell, {
layoutOrder = index + 1,
icon = action.icon,
iconComponent = action.iconComponent,
localizationKey = action.localizationKey,
height = CELL_HEIGHT,
hasDivider = index ~= #self.state.menuActions,
isSelected = index == self.state.selectedIndex,
})
end
local menuHeight = HEADER_HEIGHT + (#self.state.menuActions * CELL_HEIGHT)
return Roact.createElement("TextButton", {
Visible = self.state.visible,
Text = "",
BackgroundTransparency = theme.Overlay.Transparency,
BackgroundColor3 = theme.Overlay.Color,
Size = UDim2.fromScale(1, 1),
BorderSizePixel = 0,
ZIndex = 10,
[Roact.Event.Activated] = self.overlayDismiss,
}, {
Menu = Roact.createElement(ImageSetLabel, {
BackgroundTransparency = 1,
Image = MENU_BACKGROUND_ASSET,
SliceCenter = MENU_SLICE_CENTER,
ScaleType = Enum.ScaleType.Slice,
ImageTransparency = theme.BackgroundUIContrast.Transparency,
ImageColor3 = theme.BackgroundUIContrast.Color,
Size = UDim2.fromOffset(MENU_SIZE_X, menuHeight),
Position = UDim2.fromScale(0.5, 0.5),
AnchorPoint = Vector2.new(0.5, 0.5),
}, menuChildren),
BottomBar = Roact.createElement(BottomBar),
})
end)
end
function GamepadMenu:didMount()
ContextActionService:BindCoreAction(
TOGGLE_GAMEPAD_MENU_ACTION, self.toggleMenuVisibleAction, false, Enum.KeyCode.ButtonStart)
debug.profileend(GAMEPAD_MENU_MOUNT_TAG)
end
function GamepadMenu:bindMenuOpenActions()
self.boundMenuOpenActions = true
ContextActionService:BindCoreAction(FREEZE_CONTROLLER_ACTION_NAME, function() end, false, Enum.UserInputType.Gamepad1)
ContextActionService:BindCoreAction(CLOSEMENU_ACTION_NAME, self.closeMenuAction, false, Enum.KeyCode.ButtonB)
ContextActionService:BindCoreAction(LEAVE_GAME_ACTION_NAME, self.leaveGameMenuAction, false, Enum.KeyCode.ButtonX)
ContextActionService:BindCoreAction(RESPAWN_ACTION_NAME, self.respawnMenuAction, false, Enum.KeyCode.ButtonY)
ContextActionService:BindCoreAction(
ACTIVATE_SELECTION_ACTION_NAME, self.activateSelectionAction, false, Enum.KeyCode.ButtonA)
ContextActionService:BindCoreAction(
MOVE_SLECTION_ACTION_NAME,
self.moveSelectionAction,
false,
Enum.KeyCode.Thumbstick1,
Enum.KeyCode.DPadUp,
Enum.KeyCode.DPadDown
)
ContextActionService:BindCoreAction(GO_TO_TOP_ACTION_NAME, self.goToTopAction, false, Enum.KeyCode.ButtonL2)
ContextActionService:BindCoreAction(GO_TO_BOTTOM_ACTION_NAME, self.goToBottomAction, false, Enum.KeyCode.ButtonR2)
ContextActionService:BindCoreAction(
TOGGLE_GAMEPAD_MENU_ACTION, self.toggleMenuVisibleAction, false, Enum.KeyCode.ButtonStart)
end
function GamepadMenu:unbindMenuOpenActions()
self.boundMenuOpenActions = false
ContextActionService:UnbindCoreAction(FREEZE_CONTROLLER_ACTION_NAME)
ContextActionService:UnbindCoreAction(CLOSEMENU_ACTION_NAME)
ContextActionService:UnbindCoreAction(ACTIVATE_SELECTION_ACTION_NAME)
ContextActionService:UnbindCoreAction(LEAVE_GAME_ACTION_NAME)
ContextActionService:UnbindCoreAction(RESPAWN_ACTION_NAME)
ContextActionService:UnbindCoreAction(MOVE_SLECTION_ACTION_NAME)
ContextActionService:UnbindCoreAction(GO_TO_TOP_ACTION_NAME)
ContextActionService:UnbindCoreAction(GO_TO_BOTTOM_ACTION_NAME)
end
function GamepadMenu:unbindAllActions()
if self.boundMenuOpenActions then
self:unbindMenuOpenActions()
end
ContextActionService:UnbindCoreAction(TOGGLE_GAMEPAD_MENU_ACTION)
end
function GamepadMenu:willUpdate()
debug.profilebegin(GAMEPAD_MENU_UPDATE_TAG)
end
function GamepadMenu:didUpdate(prevProps, prevState)
if self.state.visible ~= prevState.visible then
if self.state.visible then
self:bindMenuOpenActions()
if self.state.selectedIndex ~= 1 then
self:setState({
selectedIndex = 1,
})
end
self.savedSelectedCoreObject = GuiService.SelectedCoreObject
GuiService.SelectedCoreObject = nil
self.savedSelectedObject = GuiService.SelectedObject
GuiService.SelectedObject = nil
GuiService:SetMenuIsOpen(true, GAMEPAD_MENU_KEY)
else
self:unbindMenuOpenActions()
if GuiService.SelectedCoreObject == nil then
GuiService.SelectedCoreObject = self.savedSelectedCoreObject
end
if GuiService.SelectedObject == nil then
GuiService.SelectedObject = self.savedSelectedObject
end
GuiService:SetMenuIsOpen(false, GAMEPAD_MENU_KEY)
end
end
debug.profileend(GAMEPAD_MENU_UPDATE_TAG)
end
function GamepadMenu:willUnmount()
self:unbindAllActions()
end
local function mapStateToProps(state)
local topBarEnabled = state.displayOptions.topbarEnabled
return {
chatEnabled = state.coreGuiEnabled[Enum.CoreGuiType.Chat] and topBarEnabled,
leaderboardEnabled = state.coreGuiEnabled[Enum.CoreGuiType.PlayerList] and topBarEnabled,
emotesEnabled = state.moreMenu.emotesEnabled and state.coreGuiEnabled[Enum.CoreGuiType.EmotesMenu] and topBarEnabled,
backpackEnabled = state.coreGuiEnabled[Enum.CoreGuiType.Backpack] and topBarEnabled,
respawnEnabled = state.respawn.enabled,
leaderboardOpen = state.moreMenu.leaderboardOpen,
backpackOpen = state.moreMenu.backpackOpen,
emotesOpen = state.moreMenu.emotesOpen,
}
end
return RoactRodux.UNSTABLE_connect2(mapStateToProps, nil)(GamepadMenu)
@@ -0,0 +1,140 @@
local CorePackages = game:GetService("CorePackages")
local CoreGui = game:GetService("CoreGui")
local Roact = require(CorePackages.Roact)
local RoactRodux = require(CorePackages.RoactRodux)
local t = require(CorePackages.Packages.t)
local Components = script.Parent.Parent
local TopBar = Components.Parent
local Constants = require(TopBar.Constants)
local RobloxGui = CoreGui:WaitForChild("RobloxGui")
local TenFootInterface = require(RobloxGui.Modules.TenFootInterface)
local HEALTHBAR_SIZE = UDim2.new(0, 80, 0, 6)
local HEALTHBAR_SIZE_TENFOOT = UDim2.new(0, 220, 0, 16)
local HEALTHBAR_OFFSET = 4
local HEALTHBAR_OFFSET_TENFOOT = 0
local HealthBar = Roact.PureComponent:extend("HealthBar")
HealthBar.validateProps = t.strictInterface({
layoutOrder = t.integer,
healthEnabled = t.boolean,
health = t.number,
maxHealth = t.number,
})
local function color3ToVector3(color3)
return Vector3.new(color3.r, color3.g, color3.b)
end
local healthColorToPosition = {
[color3ToVector3(Constants.HealthRedColor)] = 0.1,
[color3ToVector3(Constants.HealthYellowColor)] = 0.5,
[color3ToVector3(Constants.HealthGreenColor)] = 0.8,
}
local redHealthFraction = 0.1
local redHealthColor = Constants.HealthRedColor
local greenHealthFraction = 0.8
local greenHealthColor = Constants.HealthGreenColor
local function getHealthBarColor(healthPercent)
if healthPercent <= redHealthFraction then
return redHealthColor
elseif healthPercent >= greenHealthFraction then
return greenHealthColor
end
-- Shepard's Interpolation
local numeratorSum = Vector3.new(0,0,0)
local denominatorSum = 0
for colorSampleValue, samplePoint in pairs(healthColorToPosition) do
local distance = healthPercent - samplePoint
if distance == 0 then
-- If we are exactly on an existing sample value then we don't need to interpolate
return Color3.new(colorSampleValue.x, colorSampleValue.y, colorSampleValue.z)
else
local wi = 1 / (distance*distance)
numeratorSum = numeratorSum + wi * colorSampleValue
denominatorSum = denominatorSum + wi
end
end
local result = numeratorSum / denominatorSum
return Color3.new(result.x, result.y, result.z)
end
function HealthBar:render()
local healthVisible = self.props.healthEnabled
and self.props.health < self.props.maxHealth
local healthPercent = 1
if self.props.isDead then
healthPercent = 0
elseif self.props.maxHealth > 0 then
healthPercent = self.props.health / self.props.maxHealth
end
local healthBarSize = HEALTHBAR_SIZE
if TenFootInterface:IsEnabled() then
healthBarSize = HEALTHBAR_SIZE_TENFOOT
end
local healthBarOffset = HEALTHBAR_OFFSET
if TenFootInterface:IsEnabled() then
healthBarOffset = HEALTHBAR_OFFSET_TENFOOT
end
local healthBarBase = "rbxasset://textures/ui/TopBar/HealthBarBase.png"
local healthBar = "rbxasset://textures/ui/TopBar/HealthBar.png"
local sliceCenter = Rect.new(3, 3, 4, 4)
if TenFootInterface:IsEnabled() then
healthBarBase = "rbxasset://textures/ui/TopBar/HealthBarBaseTV.png"
healthBar = "rbxasset://textures/ui/TopBar/HealthBarTV.png"
sliceCenter = Rect.new(8, 8, 9, 9)
end
local healthBarColor = getHealthBarColor(healthPercent)
return Roact.createElement("Frame", {
Visible = healthVisible,
BackgroundTransparency = 1,
Size = UDim2.new(healthBarSize.X, UDim.new(1, 0)),
}, {
Padding = Roact.createElement("UIPadding", {
PaddingTop = UDim.new(0, healthBarOffset),
}),
HealthBar = Roact.createElement("ImageLabel", {
BackgroundTransparency = 1,
Image = healthBarBase,
ScaleType = Enum.ScaleType.Slice,
SliceCenter = sliceCenter,
Size = healthBarSize,
Position = UDim2.fromScale(0, 0.5),
AnchorPoint = Vector2.new(0, 0.5),
LayoutOrder = self.props.layoutOrder,
}, {
Fill = Roact.createElement("ImageLabel", {
BackgroundTransparency = 1,
Image = healthBar,
ImageColor3 = healthBarColor,
ScaleType = Enum.ScaleType.Slice,
SliceCenter = sliceCenter,
Size = UDim2.fromScale(healthPercent, 1),
}),
})
})
end
local function mapStateToProps(state)
return {
health = state.health.currentHealth,
maxHealth = state.health.maxHealth,
healthEnabled = state.coreGuiEnabled[Enum.CoreGuiType.Health],
}
end
return RoactRodux.UNSTABLE_connect2(mapStateToProps, nil)(HealthBar)
@@ -0,0 +1,103 @@
local CorePackages = game:GetService("CorePackages")
local CoreGui = game:GetService("CoreGui")
local Roact = require(CorePackages.Roact)
local RoactRodux = require(CorePackages.RoactRodux)
local t = require(CorePackages.Packages.t)
local Otter = require(CorePackages.Otter)
local RobloxGui = CoreGui:WaitForChild("RobloxGui")
local PolicyService = require(RobloxGui.Modules.Common.PolicyService)
local Components = script.Parent.Parent
local TopBar = Components.Parent
local Constants = require(TopBar.Constants)
local MOTOR_OPTIONS = {
frequency = 0.75,
dampingRatio = 1,
}
local RED_OVERLAY_COLOR = Color3.fromRGB(187, 0, 4)
local WHITE_OVERLAY_COLOR = Color3.new(1, 1, 1)
local HurtOverlay = Roact.PureComponent:extend("HurtOverlay")
HurtOverlay.validateProps = t.strictInterface({
healthEnabled = t.boolean,
health = t.number,
maxHealth = t.number,
isDead = t.boolean,
})
function HurtOverlay:init()
self.state = {
isAnimating = false,
}
self.animationBinding, self.animationBindingUpdate = Roact.createBinding(0)
self.positionBinding = self.animationBinding:map(function(animation)
return UDim2.new(-10 * animation, 0, -10 * animation, 0)
end)
self.sizeBinding = self.animationBinding:map(function(animation)
return UDim2.new(1 + 19 * animation, 0, 1 + 19 * animation, 0)
end)
self.motor = Otter.createSingleMotor(0)
self.motor:onStep(function(value)
self.animationBindingUpdate(value)
end)
self.motor:onComplete(function()
self:setState({
isAnimating = false,
})
end)
end
function HurtOverlay:render()
local overlayVisible = self.props.healthEnabled and self.state.isAnimating
local hurtOverlayImage = "rbxasset://textures/ui/TopBar/WhiteOverlayAsset.png"
local hurtOverlayColor = RED_OVERLAY_COLOR
if PolicyService:IsSubjectToChinaPolicies() then
hurtOverlayColor = WHITE_OVERLAY_COLOR
end
return Roact.createElement("ImageLabel", {
Visible = overlayVisible,
BackgroundTransparency = 1,
Image = hurtOverlayImage,
ImageColor3 = hurtOverlayColor,
Size = self.sizeBinding,
Position = self.positionBinding,
})
end
function HurtOverlay:didUpdate(prevProps, prevState)
if self.props.health < prevProps.health then
if not (self.props.isDead and prevProps.isDead) then
local healthChange = prevProps.health - self.props.health
if healthChange / self.props.maxHealth >= Constants.HealthPercentForOverlay then
self.motor:setGoal(Otter.instant(0))
self.motor:step(0)
self.motor:setGoal(Otter.spring(1, MOTOR_OPTIONS))
self.motor:start()
self:setState({
isAnimating = true,
})
end
end
end
end
local function mapStateToProps(state)
return {
health = state.health.currentHealth,
maxHealth = state.health.maxHealth,
isDead = state.health.isDead,
healthEnabled = state.coreGuiEnabled[Enum.CoreGuiType.Health],
}
end
return RoactRodux.UNSTABLE_connect2(mapStateToProps, nil)(HurtOverlay)
@@ -0,0 +1,94 @@
local CorePackages = game:GetService("CorePackages")
local Roact = require(CorePackages.Roact)
local t = require(CorePackages.Packages.t)
local UIBlox = require(CorePackages.UIBlox)
local ImageSetLabel = UIBlox.Core.ImageSet.Label
local withStyle = UIBlox.Core.Style.withStyle
local Interactable = UIBlox.Core.Control.Interactable
local ControlState = UIBlox.Core.Control.Enum.ControlState
local Images = UIBlox.App.ImageSet.Images
local IconButton = Roact.PureComponent:extend("IconButton")
local BACKGROUND_SIZE = 32
local OVERLAY_ASSET = Images["component_assets/circle_17"]
IconButton.validateProps = t.strictInterface({
icon = t.union(t.string, t.table),
iconSize = t.integer,
onActivated = t.callback,
onHover = t.optional(t.callback),
})
function IconButton:init()
self:setState({
controlState = ControlState.Default,
})
self.controlStateUpdated = function(oldControlState, newControlState)
if self.props.onHover and newControlState == ControlState.Hover then
self.props.onHover()
end
self:setState({
controlState = newControlState,
})
end
end
function IconButton:render()
return withStyle(function(style)
local overlayTheme = {
Color = Color3.new(1, 1, 1),
Transparency = 1,
}
if self.state.controlState == ControlState.Pressed then
overlayTheme = style.Theme.BackgroundOnPress
elseif self.state.controlState == ControlState.Hover then
overlayTheme = style.Theme.BackgroundOnHover
end
return Roact.createElement(Interactable, {
onStateChanged = self.controlStateUpdated,
ZIndex = 1,
BackgroundTransparency = 1,
Position = UDim2.fromScale(0, 1),
AnchorPoint = Vector2.new(0, 1),
Size = UDim2.fromOffset(BACKGROUND_SIZE, BACKGROUND_SIZE),
Image = "rbxasset://textures/ui/TopBar/iconBase.png",
[Roact.Event.Activated] = self.props.onActivated,
}, {
Icon = Roact.createElement(ImageSetLabel, {
Size = UDim2.fromOffset(self.props.iconSize, self.props.iconSize),
Position = UDim2.fromScale(0.5, 0.5),
AnchorPoint = Vector2.new(0.5, 0.5),
BackgroundTransparency = 1,
Image = self.props.icon,
ImageColor3 = style.Theme.IconEmphasis.Color,
ImageTransparency = style.Theme.IconEmphasis.Transparency,
ZIndex = 1,
}),
StateOverlay = Roact.createElement(ImageSetLabel, {
BackgroundTransparency = 1,
Image = OVERLAY_ASSET,
ScaleType = Enum.ScaleType.Slice,
SliceCenter = Rect.new(8, 8, 8, 8),
ImageColor3 = overlayTheme.Color,
ImageTransparency = overlayTheme.Transparency,
Size = UDim2.fromScale(1, 1),
ZIndex = 2,
}),
})
end)
end
return IconButton
@@ -0,0 +1,67 @@
local CorePackages = game:GetService("CorePackages")
local CoreGui = game:GetService("CoreGui")
local Roact = require(CorePackages.Roact)
local t = require(CorePackages.Packages.t)
local RobloxGui = CoreGui:WaitForChild("RobloxGui")
local TenFootInterface = require(RobloxGui.Modules.TenFootInterface)
local isNewInGameMenuEnabled = require(RobloxGui.Modules.isNewInGameMenuEnabled)
local InGameMenuConstants = require(RobloxGui.Modules.InGameMenu.Resources.Constants)
local InGameMenu
if isNewInGameMenuEnabled() then
InGameMenu = require(RobloxGui.Modules.InGameMenu)
end
local IconButton = require(script.Parent.IconButton)
local GetFFlagInGameMenuOpenOnHover = require(RobloxGui.Modules.Flags.GetFFlagInGameMenuOpenOnHover)
local GetFFlagInGameMenuIconTooltip = require(RobloxGui.Modules.Flags.GetFFlagInGameMenuIconTooltip)
local MenuIcon = Roact.PureComponent:extend("MenuIcon")
local BACKGROUND_SIZE = 32
local ICON_SIZE = 24
MenuIcon.validateProps = t.strictInterface({
layoutOrder = t.integer,
})
function MenuIcon:init()
self.menuIconActivated = function()
if isNewInGameMenuEnabled() then
InGameMenu.openInGameMenu()
else
local SettingsHub = require(RobloxGui.Modules.Settings.SettingsHub)
SettingsHub:ToggleVisibility(InGameMenuConstants.AnalyticsMenuOpenTypes.TopbarButton)
end
end
self.menuIconOnHover = function()
if isNewInGameMenuEnabled() then
InGameMenu.openInGameMenu()
if GetFFlagInGameMenuIconTooltip() then
InGameMenu.setMenuIconTooltipOpen(false)
end
end
end
end
function MenuIcon:render()
return Roact.createElement("Frame", {
Visible = not TenFootInterface:IsEnabled(),
BackgroundTransparency = 1,
Size = UDim2.new(0, BACKGROUND_SIZE, 1, 0),
LayoutOrder = self.props.layoutOrder
}, {
Background = Roact.createElement(IconButton, {
icon = "rbxasset://textures/ui/TopBar/coloredlogo.png",
iconSize = ICON_SIZE,
onActivated = self.menuIconActivated,
onHover = GetFFlagInGameMenuOpenOnHover() and self.menuIconOnHover or nil,
})
})
end
return MenuIcon
@@ -0,0 +1,267 @@
local CorePackages = game:GetService("CorePackages")
local CoreGui = game:GetService("CoreGui")
local ContextActionService = game:GetService("ContextActionService")
local Roact = require(CorePackages.Roact)
local RoactRodux = require(CorePackages.RoactRodux)
local t = require(CorePackages.Packages.t)
local UIBlox = require(CorePackages.UIBlox)
local OverlayContextualMenu = UIBlox.App.Menu.OverlayContextualMenu
local MenuDirection = UIBlox.App.Menu.MenuDirection
local Components = script.Parent.Parent
local TopBar = Components.Parent
local Actions = TopBar.Actions
local SetMoreMenuOpen = require(Actions.SetMoreMenuOpen)
local Constants = require(TopBar.Constants)
local InputType = Constants.InputType
local IconButton = require(script.Parent.IconButton)
local RobloxGui = CoreGui:WaitForChild("RobloxGui")
local TenFootInterface = require(RobloxGui.Modules.TenFootInterface)
local EmotesMenuMaster = require(RobloxGui.Modules.EmotesMenu.EmotesMenuMaster)
local BackpackModule = require(RobloxGui.Modules.BackpackScript)
local ChatSelector = require(RobloxGui.Modules.ChatSelector)
local PlayerListMaster = require(RobloxGui.Modules.PlayerList.PlayerListManager)
local EmotesConstants = require(RobloxGui.Modules.EmotesMenu.Constants)
local RobloxTranslator = require(RobloxGui.Modules.RobloxTranslator)
local MORE_BUTTON_SIZE = 32
local ICON_SIZE = 24
local MENU_GAP = 12
local MENU_DEFAULT_SIZE = 300
local CONTEXT_MENU_DEFAULT_PADDING = 12
local MENU_EXTRA_PADDING = 12
local MENU_FULLSCREEN_THRESHOLD = 450
local CHAT_HIDE_THRESHOLD = 600
local ESCAPE_CLOSE_MENU_ACTION = "CloseMoreMenuAction"
local MoreMenu = Roact.PureComponent:extend("MoreMenu")
MoreMenu.validateProps = t.strictInterface({
layoutOrder = t.integer,
moreMenuOpen = t.boolean,
setMoreMenuOpen = t.callback,
screenSize = t.Vector2,
isSmallTouchDevice = t.boolean,
topBarEnabled = t.boolean,
leaderboardEnabled = t.boolean,
emotesEnabled = t.boolean,
backpackEnabled = t.boolean,
leaderboardOpen = t.boolean,
backpackOpen = t.boolean,
emotesOpen = t.boolean,
inputType = t.string,
})
function MoreMenu:init()
self.chatWasHidden = false
self.boundAction = false
self.moreButtonActivated = function()
self.props.setMoreMenuOpen(not self.props.moreMenuOpen)
end
end
function MoreMenu:render()
local menuOptions = {}
local hasOptions = false
local isUsingKeyBoard = self.props.inputType == InputType.MouseAndKeyBoard
if self.props.leaderboardEnabled and not self.props.isSmallTouchDevice then
local leaderboardIcon = "rbxasset://textures/ui/TopBar/leaderboardOn.png"
if not self.props.leaderboardOpen then
leaderboardIcon = "rbxasset://textures/ui/TopBar/leaderboardOff.png"
end
table.insert(menuOptions, {
icon = leaderboardIcon,
text = RobloxTranslator:FormatByKey("CoreScripts.TopBar.Leaderboard"),
keyCodeLabel = isUsingKeyBoard and Enum.KeyCode.Tab or nil,
onActivated = function()
PlayerListMaster:SetVisibility(not PlayerListMaster:GetSetVisible())
self.props.setMoreMenuOpen(false)
end,
})
hasOptions = true
end
if self.props.emotesEnabled then
local emotesIcon = "rbxasset://textures/ui/TopBar/emotesOn.png"
if not self.props.emotesOpen then
emotesIcon = "rbxasset://textures/ui/TopBar/emotesOff.png"
end
local emotesKeybind = EmotesConstants.EmoteMenuOpenKey
table.insert(menuOptions, {
icon = emotesIcon,
text = RobloxTranslator:FormatByKey("CoreScripts.TopBar.Emotes"),
keyCodeLabel = isUsingKeyBoard and emotesKeybind or nil,
onActivated = function()
if EmotesMenuMaster:isOpen() then
EmotesMenuMaster:close()
else
EmotesMenuMaster:open()
end
self.props.setMoreMenuOpen(false)
end,
})
hasOptions = true
end
if self.props.backpackEnabled then
local backpackIcon = "rbxasset://textures/ui/TopBar/inventoryOn.png"
if not self.props.backpackOpen then
backpackIcon = "rbxasset://textures/ui/TopBar/inventoryOff.png"
end
table.insert(menuOptions, {
icon = backpackIcon,
text = RobloxTranslator:FormatByKey("CoreScripts.TopBar.Inventory"),
keyCodeLabel = isUsingKeyBoard and Enum.KeyCode.Backquote or nil,
onActivated = function()
BackpackModule:OpenClose()
self.props.setMoreMenuOpen(false)
end,
})
hasOptions = true
end
local moreMenuSize = UDim2.new(0, MENU_DEFAULT_SIZE + CONTEXT_MENU_DEFAULT_PADDING * 2, 0, self.props.screenSize.Y)
if self.props.screenSize.X < MENU_FULLSCREEN_THRESHOLD then
moreMenuSize = UDim2.new(0, self.props.screenSize.X - (MENU_EXTRA_PADDING * 2), 0, self.props.screenSize.Y)
end
local moreIcon = "rbxasset://textures/ui/TopBar/moreOn.png"
if not self.props.moreMenuOpen then
moreIcon = "rbxasset://textures/ui/TopBar/moreOff.png"
end
local moreButtonVisible = not TenFootInterface:IsEnabled() and self.props.topBarEnabled and hasOptions
return Roact.createElement("Frame", {
Visible = moreButtonVisible,
BackgroundTransparency = 1,
Size = UDim2.new(0, MORE_BUTTON_SIZE, 1, 0),
LayoutOrder = self.props.layoutOrder,
}, {
OpenButton = Roact.createElement(IconButton, {
icon = moreIcon,
iconSize = ICON_SIZE,
onActivated = self.moreButtonActivated,
}),
MoreMenuContainer = Roact.createElement("Frame", {
ZIndex = 3,
BackgroundTransparency = 1,
Position = UDim2.new(1, CONTEXT_MENU_DEFAULT_PADDING, 0, 0),
Size = moreMenuSize,
AnchorPoint = Vector2.new(1, 0),
}, {
OverlayContextualMenu = Roact.createElement(OverlayContextualMenu, {
buttonProps = menuOptions,
open = self.props.moreMenuOpen,
menuDirection = MenuDirection.Down,
openPositionY = UDim.new(0, Constants.TopBarHeight + MENU_GAP),
closeBackgroundVisible = false,
screenSize = self.props.screenSize,
onDismiss = function()
self.props.setMoreMenuOpen(false)
PlayerListMaster:SetMinimized(false)
end,
}),
}),
})
end
function MoreMenu:updateActionBound()
if self.props.moreMenuOpen then
ContextActionService:BindCoreAction(
ESCAPE_CLOSE_MENU_ACTION,
function(actionName, inputState, inputObj)
if inputState == Enum.UserInputState.Begin then
self.props.setMoreMenuOpen(false)
return Enum.ContextActionResult.Sink
end
return Enum.ContextActionResult.Pass
end,
false, Enum.KeyCode.Escape
)
self.boundAction = true
elseif self.boundAction then
ContextActionService:UnbindCoreAction(ESCAPE_CLOSE_MENU_ACTION)
self.boundAction = false
end
end
function MoreMenu:didUpdate(prevProps, prevState)
if self.props.moreMenuOpen ~= prevProps.moreMenuOpen then
PlayerListMaster:SetMinimized(self.props.moreMenuOpen)
self:updateActionBound()
if self.props.screenSize.X < CHAT_HIDE_THRESHOLD then
if self.props.moreMenuOpen and ChatSelector:GetVisibility() then
self.chatWasHidden = true
ChatSelector:SetVisible(false)
elseif not self.props.moreMenuOpen and self.chatWasHidden then
ChatSelector:SetVisible(true)
self.chatWasHidden = false
end
end
end
end
local function mapStateToProps(state)
return {
screenSize = state.displayOptions.screenSize,
moreMenuOpen = state.moreMenu.open,
isSmallTouchDevice = state.displayOptions.isSmallTouchDevice,
topBarEnabled = state.displayOptions.topbarEnabled,
leaderboardEnabled = state.coreGuiEnabled[Enum.CoreGuiType.PlayerList],
emotesEnabled = state.moreMenu.emotesEnabled and state.coreGuiEnabled[Enum.CoreGuiType.EmotesMenu],
backpackEnabled = state.coreGuiEnabled[Enum.CoreGuiType.Backpack],
leaderboardOpen = state.moreMenu.leaderboardOpen,
backpackOpen = state.moreMenu.backpackOpen,
emotesOpen = state.moreMenu.emotesOpen,
inputType = state.displayOptions.inputType,
}
end
local function mapDispatchToProps(dispatch)
return {
setMoreMenuOpen = function(open)
return dispatch(SetMoreMenuOpen(open))
end,
}
end
return RoactRodux.UNSTABLE_connect2(mapStateToProps, mapDispatchToProps)(MoreMenu)
@@ -0,0 +1,167 @@
local CorePackages = game:GetService("CorePackages")
local CoreGui = game:GetService("CoreGui")
local Roact = require(CorePackages.Roact)
local RoactRodux = require(CorePackages.RoactRodux)
local t = require(CorePackages.Packages.t)
local UIBlox = require(CorePackages.UIBlox)
local ImageSetButton = UIBlox.Core.ImageSet.Button
local Images = UIBlox.App.ImageSet.Images
local Presentation = script.Parent.Presentation
local MenuIcon = require(Presentation.MenuIcon)
local ChatIcon = require(Presentation.ChatIcon)
local MoreMenu = require(Presentation.MoreMenu)
local HealthBar = require(Presentation.HealthBar)
local HurtOverlay = require(Presentation.HurtOverlay)
local GamepadMenu = require(Presentation.GamepadMenu)
local Connection = require(script.Parent.Connection)
local TopBar = Presentation.Parent.Parent
local Constants = require(TopBar.Constants)
local SetScreenSize = require(TopBar.Actions.SetScreenSize)
local RobloxGui = CoreGui:WaitForChild("RobloxGui")
local TenFootInterface = require(RobloxGui.Modules.TenFootInterface)
local isNewInGameMenuEnabled = require(RobloxGui.Modules.isNewInGameMenuEnabled)
local isNewGamepadMenuEnabled = require(RobloxGui.Modules.Flags.isNewGamepadMenuEnabled)
local CLOSE_MENU_ICON_SIZE = 30
local TopBarApp = Roact.PureComponent:extend("TopBarApp")
TopBarApp.validateProps = t.strictInterface({
menuOpen = t.boolean,
inspectMenuOpen = t.boolean,
setScreenSize = t.callback,
})
function TopBarApp:render()
local screenSideOffset = Constants.ScreenSideOffset
local topBarHeight = Constants.TopBarHeight
if TenFootInterface:IsEnabled() then
screenSideOffset = Constants.ScreenSideOffsetTenFoot
topBarHeight = Constants.TopBarHeightTenFoot
end
local isTopBarVisible = not (self.props.menuOpen or self.props.inspectMenuOpen)
return Roact.createElement("ScreenGui", {
IgnoreGuiInset = true,
ZIndexBehavior = Enum.ZIndexBehavior.Sibling,
AutoLocalize = false,
DisplayOrder = 6,
[Roact.Change.AbsoluteSize] = function(rbx)
self.props.setScreenSize(rbx.AbsoluteSize)
end,
}, {
Connection = Roact.createElement(Connection),
GamepadMenu = isNewGamepadMenuEnabled() and Roact.createElement(GamepadMenu) or nil,
FullScreenFrame = Roact.createElement("Frame", {
BackgroundTransparency = 1,
Size = UDim2.new(1, 0, 1, 0),
Visible = isTopBarVisible,
}, {
HurtOverlay = Roact.createElement(HurtOverlay),
}),
--Remove with isNewInGameMenuEnabled
LegacyCloseMenu = not isNewInGameMenuEnabled() and Roact.createElement("Frame", {
BackgroundTransparency = 1,
Position = UDim2.new(0, screenSideOffset, 0, 0),
Size = UDim2.new(1, 0, 0, topBarHeight),
Visible = self.props.menuOpen,
}, {
CloseMenuButton = Roact.createElement(ImageSetButton, {
Visible = not TenFootInterface:IsEnabled(),
BackgroundTransparency = 1,
Position = UDim2.new(0, 0, 0.5, 0),
AnchorPoint = Vector2.new(0, 0.5),
Size = UDim2.new(0, CLOSE_MENU_ICON_SIZE, 0, CLOSE_MENU_ICON_SIZE),
Image = Images["icons/controls/close-ingame"],
[Roact.Event.Activated] = function()
local SettingsHub = require(RobloxGui.Modules.Settings.SettingsHub)
SettingsHub:ToggleVisibility()
end,
})
}),
TopBarFrame = Roact.createElement("Frame", {
BackgroundTransparency = 1,
Size = UDim2.new(1, 0, 0, topBarHeight),
Visible = isTopBarVisible,
}, {
LeftFrame = not TenFootInterface:IsEnabled() and Roact.createElement("Frame", {
BackgroundTransparency = 1,
Size = UDim2.new(0.5, -screenSideOffset, 1, 0),
Position = UDim2.new(0, screenSideOffset, 0, 0),
}, {
Layout = Roact.createElement("UIListLayout", {
Padding = UDim.new(0, Constants.Padding),
FillDirection = Enum.FillDirection.Horizontal,
HorizontalAlignment = Enum.HorizontalAlignment.Left,
VerticalAlignment = Enum.VerticalAlignment.Center,
SortOrder = Enum.SortOrder.LayoutOrder,
}),
MenuIcon = Roact.createElement(MenuIcon, {
layoutOrder = 1,
}),
ChatIcon = Roact.createElement(ChatIcon, {
layoutOrder = 2,
}),
}),
RightFrame = Roact.createElement("Frame", {
BackgroundTransparency = 1,
Size = UDim2.new(0.5, -screenSideOffset, 1, 0),
Position = UDim2.new(1, -screenSideOffset, 0, 0),
AnchorPoint = Vector2.new(1, 0),
}, {
Layout = Roact.createElement("UIListLayout", {
Padding = UDim.new(0, Constants.Padding),
FillDirection = Enum.FillDirection.Horizontal,
HorizontalAlignment = Enum.HorizontalAlignment.Right,
VerticalAlignment = Enum.VerticalAlignment.Center,
SortOrder = Enum.SortOrder.LayoutOrder,
}),
HealthBar = Roact.createElement(HealthBar, {
layoutOrder = 1,
}),
MoreMenu = not TenFootInterface:IsEnabled() and Roact.createElement(MoreMenu, {
layoutOrder = 2,
}),
}),
}),
})
end
local function mapStateToProps(state)
local inspectMenuOpen = state.displayOptions.inspectMenuOpen
return {
menuOpen = state.displayOptions.menuOpen,
inspectMenuOpen = inspectMenuOpen,
}
end
local function mapDispatchToProps(dispatch)
return {
setScreenSize = function(screenSize)
return dispatch(SetScreenSize(screenSize))
end,
}
end
return RoactRodux.UNSTABLE_connect2(mapStateToProps, mapDispatchToProps)(TopBarApp)
@@ -0,0 +1,86 @@
return function()
local CorePackages = game:GetService("CorePackages")
local RobloxReplicatedStorage = game:GetService("RobloxReplicatedStorage")
local CoreGui = game:GetService("CoreGui")
local AppDarkTheme = require(CorePackages.AppTempCommon.LuaApp.Style.Themes.DarkTheme)
local AppFont = require(CorePackages.AppTempCommon.LuaApp.Style.Fonts.Gotham)
local Roact = require(CorePackages.Roact)
local Rodux = require(CorePackages.Rodux)
local RoactRodux = require(CorePackages.RoactRodux)
local UIBlox = require(CorePackages.UIBlox)
local Reducer = require(script.Parent.Parent.Reducer)
local appStyle = {
Theme = AppDarkTheme,
Font = AppFont,
}
--Create dummy events in RobloxReplicatedStorage:
local UpdatePlayerBlockList = Instance.new("RemoteEvent")
UpdatePlayerBlockList.Name = "UpdatePlayerBlockList"
UpdatePlayerBlockList.Parent = RobloxReplicatedStorage
local NewPlayerGroupDetails = Instance.new("RemoteEvent")
NewPlayerGroupDetails.Name = "NewPlayerGroupDetails"
NewPlayerGroupDetails.Parent = RobloxReplicatedStorage
local NewPlayerCanManageDetails = Instance.new("RemoteEvent")
NewPlayerCanManageDetails.Name = "NewPlayerCanManageDetails"
NewPlayerCanManageDetails.Parent = RobloxReplicatedStorage
local FollowRelationshipChanged
local GetFollowRelationships
local NewFollower
local Flags = script.Parent.Parent.Parent.Flags
local GetFFlagRemoveInGameFollowingEvents = require(Flags.GetFFlagRemoveInGameFollowingEvents)
if not GetFFlagRemoveInGameFollowingEvents() then
FollowRelationshipChanged = Instance.new("RemoteEvent")
FollowRelationshipChanged.Name = "FollowRelationshipChanged"
FollowRelationshipChanged.Parent = RobloxReplicatedStorage
GetFollowRelationships = Instance.new("RemoteFunction")
GetFollowRelationships.Name = "GetFollowRelationships"
GetFollowRelationships.Parent = RobloxReplicatedStorage
NewFollower = Instance.new("RemoteEvent")
NewFollower.Name = "NewFollower"
NewFollower.Parent = RobloxReplicatedStorage
end
local RobloxGui = CoreGui:WaitForChild("RobloxGui")
local SendNotificationInfo = Instance.new("BindableEvent")
SendNotificationInfo.Name = "SendNotificationInfo"
SendNotificationInfo.Parent = RobloxGui
local Sounds = Instance.new("Folder")
Sounds.Name = "Sounds"
Sounds.Parent = CoreGui.RobloxGui
describe("TopBarApp", function()
it("should create and destroy without errors", function()
local TopBarApp = require(script.Parent.TopBarApp)
local store = Rodux.Store.new(Reducer, nil, {
Rodux.thunkMiddleware,
})
local element = Roact.createElement(RoactRodux.StoreProvider, {
store = store,
}, {
ThemeProvider = Roact.createElement(UIBlox.Style.Provider, {
style = appStyle,
}, {
TopBarApp = Roact.createElement(TopBarApp)
})
})
local instance = Roact.mount(element)
Roact.unmount(instance)
end)
end)
end
@@ -0,0 +1,20 @@
return {
TopBarHeight = 36,
TopBarHeightTenFoot = 72,
ScreenSideOffset = 16,
ScreenSideOffsetTenFoot = 48,
Padding = 12,
HealthPercentForOverlay = 5 / 100,
HealthRedColor = Color3.fromRGB(255, 28, 0),
HealthYellowColor = Color3.fromRGB(250, 235, 0),
HealthGreenColor = Color3.fromRGB(27, 252, 107),
InputType = {
MouseAndKeyBoard = "MouseAndKeyboard",
Touch = "Touch",
Gamepad = "Gamepad",
},
}
@@ -0,0 +1,4 @@
return {
propValidation = false,
elementTracing = false,
}
@@ -0,0 +1,64 @@
local CorePackages = game:GetService("CorePackages")
local Rodux = require(CorePackages.Rodux)
local Actions = script.Parent.Parent.Actions
local UpdateChatMessages = require(Actions.UpdateChatMessages)
local UpdateChatVisible = require(Actions.UpdateChatVisible)
local SetCanChat = require(Actions.SetCanChat)
local initialChatState = {
canChat = false,
visible = true,
lastReadMessages = 0,
unreadMessages = 0,
}
local Chat = Rodux.createReducer(initialChatState, {
[UpdateChatMessages.name] = function(state, action)
if state.visible then
return {
canChat = state.canChat,
visible = true,
lastReadMessages = action.messages,
unreadMessages = 0,
}
else
return {
canChat = state.canChat,
visible = false,
lastReadMessages = state.lastReadMessages,
unreadMessages = action.messages - state.lastReadMessages,
}
end
end,
[UpdateChatVisible.name] = function(state, action)
if action.visible then
return {
canChat = state.canChat,
visible = true,
lastReadMessages = state.lastReadMessages + state.unreadMessages,
unreadMessages = 0,
}
else
return {
canChat = state.canChat,
visible = false,
lastReadMessages = state.lastReadMessages,
unreadMessages = state.unreadMessages,
}
end
end,
[SetCanChat.name] = function(state, action)
return {
canChat = action.canChat,
visible = state.visible,
lastReadMessages = state.lastReadMessages,
unreadMessages = state.unreadMessages,
}
end,
})
return Chat
@@ -0,0 +1,80 @@
return function()
local TopBar = script.Parent.Parent
local Actions = TopBar.Actions
local UpdateChatMessages = require(Actions.UpdateChatMessages)
local UpdateChatVisible = require(Actions.UpdateChatVisible)
local SetCanChat = require(Actions.SetCanChat)
local Chat = require(script.Parent.Chat)
local function countValues(t)
local c = 0
for _, _ in pairs(t) do
c = c + 1
end
return c
end
it("should have the correct default values", function()
local defaultState = Chat(nil, {})
expect(type(defaultState)).to.equal("table")
expect(defaultState.canChat).to.equal(false)
expect(defaultState.visible).to.equal(true)
expect(defaultState.lastReadMessages).to.equal(0)
expect(defaultState.unreadMessages).to.equal(0)
end)
describe("SetCanChat", function()
it("should change the value of canChat", function()
local oldState = Chat(nil, {})
local newState = Chat(oldState, SetCanChat(true))
expect(oldState).to.never.equal(newState)
expect(newState.canChat).to.equal(true)
end)
it("should not change any other values", function()
local oldState = Chat(nil, {})
local newState = Chat(oldState, SetCanChat(true))
expect(countValues(newState)).to.equal(countValues(oldState))
for key, value in pairs(newState) do
if key ~= "canChat" then
expect(value).to.equal(oldState[key])
end
end
end)
end)
describe("UpdateChatMessages", function()
it("should change the value of lastReadMessages when visible", function()
local oldState = Chat(nil, {})
oldState = Chat(oldState, UpdateChatVisible(true))
local newState = Chat(oldState, UpdateChatMessages(10))
expect(oldState).to.never.equal(newState)
expect(newState.lastReadMessages).to.equal(10)
expect(newState.unreadMessages).to.equal(0)
end)
it("should change the value of unreadMessages when not visible", function()
local oldState = Chat(nil, {})
oldState = Chat(oldState, UpdateChatMessages(5))
oldState = Chat(oldState, UpdateChatVisible(false))
local newState = Chat(oldState, UpdateChatMessages(15))
expect(oldState).to.never.equal(newState)
expect(newState.lastReadMessages).to.equal(5)
expect(newState.unreadMessages).to.equal(10)
end)
end)
describe("UpdateChatVisible", function()
it("should reset unreadMessages", function()
local oldState = Chat(nil, {})
oldState = Chat(oldState, UpdateChatVisible(false))
expect(oldState.visible).to.equal(false)
oldState = Chat(oldState, UpdateChatMessages(10))
local newState = Chat(oldState, UpdateChatVisible(true))
expect(oldState).to.never.equal(newState)
expect(newState.visible).to.equal(true)
expect(newState.lastReadMessages).to.equal(10)
expect(newState.unreadMessages).to.equal(0)
end)
end)
end
@@ -0,0 +1,33 @@
local CorePackages = game:GetService("CorePackages")
local Rodux = require(CorePackages.Rodux)
local Cryo = require(CorePackages.Cryo)
local Actions = script.Parent.Parent.Actions
local UpdateCoreGuiEnabled = require(Actions.UpdateCoreGuiEnabled)
local initialCoreGuiEnabled = {
[Enum.CoreGuiType.PlayerList] = true,
[Enum.CoreGuiType.Health] = true,
[Enum.CoreGuiType.Backpack] = true,
[Enum.CoreGuiType.Chat] = true,
[Enum.CoreGuiType.EmotesMenu] = true,
}
local CoreGuiEanbled = Rodux.createReducer(initialCoreGuiEnabled, {
[UpdateCoreGuiEnabled.name] = function(state, action)
if action.coreGuiType == Enum.CoreGuiType.All then
local newState = {}
for coreGuiType, _ in pairs(state) do
newState[coreGuiType] = action.enabled
end
return newState
else
return Cryo.Dictionary.join(state, {
[action.coreGuiType] = action.enabled,
})
end
end,
})
return CoreGuiEanbled
@@ -0,0 +1,37 @@
return function()
local TopBar = script.Parent.Parent
local Actions = TopBar.Actions
local UpdateCoreGuiEnabled = require(Actions.UpdateCoreGuiEnabled)
local CoreGuiEnabled = require(script.Parent.CoreGuiEnabled)
it("everything should be enabled by default", function()
local defaultState = CoreGuiEnabled(nil, {})
expect(type(defaultState)).to.equal("table")
expect(defaultState[Enum.CoreGuiType.PlayerList]).to.equal(true)
expect(defaultState[Enum.CoreGuiType.Health]).to.equal(true)
expect(defaultState[Enum.CoreGuiType.Backpack]).to.equal(true)
expect(defaultState[Enum.CoreGuiType.Chat]).to.equal(true)
expect(defaultState[Enum.CoreGuiType.EmotesMenu]).to.equal(true)
end)
describe("UpdateCoreGuiEnabled", function()
it("should change the value of of the given coregui type", function()
local oldState = CoreGuiEnabled(nil, {})
local newState = CoreGuiEnabled(oldState, UpdateCoreGuiEnabled(Enum.CoreGuiType.PlayerList, false))
expect(oldState).to.never.equal(newState)
expect(newState[Enum.CoreGuiType.PlayerList]).to.equal(false)
end)
it("should update all values when passed Enum.CoreGuiType.All", function()
local oldState = CoreGuiEnabled(nil, {})
local newState = CoreGuiEnabled(oldState, UpdateCoreGuiEnabled(Enum.CoreGuiType.All, false))
expect(newState[Enum.CoreGuiType.PlayerList]).to.equal(false)
expect(newState[Enum.CoreGuiType.Health]).to.equal(false)
expect(newState[Enum.CoreGuiType.Backpack]).to.equal(false)
expect(newState[Enum.CoreGuiType.Chat]).to.equal(false)
expect(newState[Enum.CoreGuiType.EmotesMenu]).to.equal(false)
end)
end)
end
@@ -0,0 +1,65 @@
local CorePackages = game:GetService("CorePackages")
local Rodux = require(CorePackages.Rodux)
local Cryo = require(CorePackages.Cryo)
local TopBar = script.Parent.Parent
local Actions = TopBar.Actions
local SetMenuOpen = require(Actions.SetMenuOpen)
local SetTopBarEnabled = require(Actions.SetTopBarEnabled)
local SetSmallTouchDevice = require(Actions.SetSmallTouchDevice)
local SetScreenSize = require(Actions.SetScreenSize)
local SetInputType = require(Actions.SetInputType)
local SetInspectMenuOpen = require(Actions.SetInspectMenuOpen)
local Constants = require(TopBar.Constants)
local InputType = Constants.InputType
local initialDisplayOptions = {
menuOpen = false,
inspectMenuOpen = false,
topbarEnabled = true, --If the top bar is enabled from the SetCore API
isSmallTouchDevice = false,
screenSize = Vector2.new(0, 0),
inputType = InputType.MouseAndKeyBoard,
}
local DisplayOptions = Rodux.createReducer(initialDisplayOptions, {
[SetMenuOpen.name] = function(state, action)
return Cryo.Dictionary.join(state, {
menuOpen = action.menuOpen,
})
end,
[SetTopBarEnabled.name] = function(state, action)
return Cryo.Dictionary.join(state, {
topbarEnabled = action.enabled,
})
end,
[SetSmallTouchDevice.name] = function(state, action)
return Cryo.Dictionary.join(state, {
isSmallTouchDevice = action.isSmallTouchDevice,
})
end,
[SetScreenSize.name] = function(state, action)
return Cryo.Dictionary.join(state, {
screenSize = action.screenSize,
})
end,
[SetInputType.name] = function(state, action)
return Cryo.Dictionary.join(state, {
inputType = action.inputType,
})
end,
[SetInspectMenuOpen.name] = function(state, action)
return Cryo.Dictionary.join(state, {
inspectMenuOpen = action.inspectMenuOpen
})
end,
})
return DisplayOptions
@@ -0,0 +1,155 @@
return function()
local TopBar = script.Parent.Parent
local Actions = TopBar.Actions
local SetMenuOpen = require(Actions.SetMenuOpen)
local SetTopBarEnabled = require(Actions.SetTopBarEnabled)
local SetSmallTouchDevice = require(Actions.SetSmallTouchDevice)
local SetScreenSize = require(Actions.SetScreenSize)
local DisplayOptions = require(script.Parent.DisplayOptions)
local SetInputType = require(Actions.SetInputType)
local SetInspectMenuOpen = require(Actions.SetInspectMenuOpen)
local Constants = require(TopBar.Constants)
local InputType = Constants.InputType
local function countValues(t)
local c = 0
for _, _ in pairs(t) do
c = c + 1
end
return c
end
it("should have the correct default values", function()
local defaultState = DisplayOptions(nil, {})
expect(type(defaultState)).to.equal("table")
expect(defaultState.menuOpen).to.equal(false)
expect(defaultState.topbarEnabled).to.equal(true)
expect(defaultState.isSmallTouchDevice).to.equal(false)
expect(defaultState.inputType).to.equal(InputType.MouseAndKeyBoard)
expect(defaultState.inspectMenuOpen).to.equal(false)
end)
describe("SetMenuOpen", function()
it("should change the value of menuOpen", function()
local oldState = DisplayOptions(nil, {})
local newState = DisplayOptions(oldState, SetMenuOpen(true))
expect(oldState).to.never.equal(newState)
expect(newState.menuOpen).to.equal(true)
end)
it("should not change any other values", function()
local oldState = DisplayOptions(nil, {})
local newState = DisplayOptions(oldState, SetMenuOpen(true))
expect(countValues(newState)).to.equal(countValues(oldState))
for key, value in pairs(newState) do
if key ~= "menuOpen" then
expect(value).to.equal(oldState[key])
end
end
end)
end)
describe("SetTopBarEnabled", function()
it("should change the value of topbarEnabled", function()
local oldState = DisplayOptions(nil, {})
local newState = DisplayOptions(oldState, SetTopBarEnabled(false))
expect(oldState).to.never.equal(newState)
expect(newState.topbarEnabled).to.equal(false)
end)
it("should not change any other values", function()
local oldState = DisplayOptions(nil, {})
local newState = DisplayOptions(oldState, SetTopBarEnabled(false))
expect(countValues(newState)).to.equal(countValues(oldState))
for key, value in pairs(newState) do
if key ~= "topbarEnabled" then
expect(value).to.equal(oldState[key])
end
end
end)
end)
describe("SetSmallTouchDevice", function()
it("should change the value of topbarEnabled", function()
local oldState = DisplayOptions(nil, {})
local newState = DisplayOptions(oldState, SetSmallTouchDevice(true))
expect(oldState).to.never.equal(newState)
expect(newState.isSmallTouchDevice).to.equal(true)
end)
it("should not change any other values", function()
local oldState = DisplayOptions(nil, {})
local newState = DisplayOptions(oldState, SetSmallTouchDevice(true))
expect(countValues(newState)).to.equal(countValues(oldState))
for key, value in pairs(newState) do
if key ~= "isSmallTouchDevice" then
expect(value).to.equal(oldState[key])
end
end
end)
end)
describe("SetScreenSize", function()
it("should change the value of topbarEnabled", function()
local oldState = DisplayOptions(nil, {})
local newState = DisplayOptions(oldState, SetScreenSize(Vector2.new(100, 100)))
expect(oldState).to.never.equal(newState)
expect(newState.screenSize).to.equal(Vector2.new(100, 100))
end)
it("should not change any other values", function()
local oldState = DisplayOptions(nil, {})
local newState = DisplayOptions(oldState, SetScreenSize(Vector2.new(100, 100)))
expect(countValues(newState)).to.equal(countValues(oldState))
for key, value in pairs(newState) do
if key ~= "screenSize" then
expect(value).to.equal(oldState[key])
end
end
end)
end)
describe("SetInputType", function()
it("should change the value of inputType", function()
local oldState = DisplayOptions(nil, {})
local newState = DisplayOptions(oldState, SetInputType(InputType.Touch))
expect(oldState).to.never.equal(newState)
expect(newState.inputType).to.equal(InputType.Touch)
end)
it("should not change any other values", function()
local oldState = DisplayOptions(nil, {})
local newState = DisplayOptions(oldState, SetInputType(InputType.Touch))
expect(countValues(newState)).to.equal(countValues(oldState))
for key, value in pairs(newState) do
if key ~= "inputType" then
expect(value).to.equal(oldState[key])
end
end
end)
end)
describe("SetInspectMenuOpen", function()
it("should change the value of inspectMenuOpen", function()
local oldState = DisplayOptions(nil, {})
local newState = DisplayOptions(oldState, SetInspectMenuOpen(true))
expect(oldState).to.never.equal(newState)
expect(newState.inspectMenuOpen).to.equal(true)
newState = DisplayOptions(newState, SetInspectMenuOpen(false))
expect(newState.inspectMenuOpen).to.equal(false)
end)
it("should not change any other values", function()
local oldState = DisplayOptions(nil, {})
local newState = DisplayOptions(oldState, SetInspectMenuOpen(true))
expect(countValues(newState)).to.equal(countValues(oldState))
for key, value in pairs(newState) do
if key ~= "inspectMenuOpen" then
expect(value).to.equal(oldState[key])
end
end
end)
end)
end
@@ -0,0 +1,21 @@
local CorePackages = game:GetService("CorePackages")
local CoreGui = game:GetService("CoreGui")
local Rodux = require(CorePackages.Rodux)
local RobloxGui = CoreGui:WaitForChild("RobloxGui")
local RobloxTranslator = require(RobloxGui.Modules.RobloxTranslator)
local Actions = script.Parent.Parent.Actions
local SetGameName = require(Actions.SetGameName)
return Rodux.createReducer({
name = RobloxTranslator:FormatByKey("CoreScripts.TopBar.GameNamePlaceHolder"),
}, {
[SetGameName.name] = function(state, action)
return {
name = action.gameName,
}
end,
})
@@ -0,0 +1,22 @@
return function()
local TopBar = script.Parent.Parent
local Actions = TopBar.Actions
local SetGameName = require(Actions.SetGameName)
local GameInfo = require(script.Parent.GameInfo)
it("should have the correct default values", function()
local defaultState = GameInfo(nil, {})
expect(type(defaultState)).to.equal("table")
expect(defaultState.name).to.equal("Game")
end)
describe("SetGameName", function()
it("should change the value of name", function()
local oldState = GameInfo(nil, {})
local newState = GameInfo(oldState, SetGameName("Test"))
expect(oldState).to.never.equal(newState)
expect(newState.name).to.equal("Test")
end)
end)
end
@@ -0,0 +1,33 @@
local CorePackages = game:GetService("CorePackages")
local Rodux = require(CorePackages.Rodux)
local Actions = script.Parent.Parent.Actions
local UpdateHealth = require(Actions.UpdateHealth)
local SetIsDead = require(Actions.SetIsDead)
local initialHealth = {
isDead = false,
currentHealth = 100,
maxHealth = 100,
}
local Health = Rodux.createReducer(initialHealth, {
[UpdateHealth.name] = function(state, action)
return {
isDead = state.isDead,
currentHealth = action.health,
maxHealth = action.maxHealth,
}
end,
[SetIsDead.name] = function(state, action)
return {
isDead = action.isDead,
currentHealth = state.currentHealth,
maxHealth = state.maxHealth,
}
end,
})
return Health
@@ -0,0 +1,65 @@
return function()
local TopBar = script.Parent.Parent
local Actions = TopBar.Actions
local UpdateHealth = require(Actions.UpdateHealth)
local SetIsDead = require(Actions.SetIsDead)
local Health = require(script.Parent.Health)
local function countValues(t)
local c = 0
for _, _ in pairs(t) do
c = c + 1
end
return c
end
it("should have the correct default values", function()
local defaultState = Health(nil, {})
expect(type(defaultState)).to.equal("table")
expect(defaultState.isDead).to.equal(false)
expect(defaultState.currentHealth).to.equal(100)
expect(defaultState.maxHealth).to.equal(100)
end)
describe("UpdateHealth", function()
it("should change the value of health and maxHealth", function()
local oldState = Health(nil, {})
local newState = Health(oldState, UpdateHealth(15, 30))
expect(oldState).to.never.equal(newState)
expect(newState.currentHealth).to.equal(15)
expect(newState.maxHealth).to.equal(30)
end)
it("should not change any other values", function()
local oldState = Health(nil, {})
local newState = Health(oldState, UpdateHealth(15, 30))
expect(countValues(newState)).to.equal(countValues(oldState))
for key, value in pairs(newState) do
if key ~= "currentHealth" and key ~= "maxHealth" then
expect(value).to.equal(oldState[key])
end
end
end)
end)
describe("SetIsDead", function()
it("should change the value of isDead", function()
local oldState = Health(nil, {})
local newState = Health(oldState, SetIsDead(true))
expect(oldState).to.never.equal(newState)
expect(newState.isDead).to.equal(true)
end)
it("should not change any other values", function()
local oldState = Health(nil, {})
local newState = Health(oldState, SetIsDead(true))
expect(countValues(newState)).to.equal(countValues(oldState))
for key, value in pairs(newState) do
if key ~= "isDead" then
expect(value).to.equal(oldState[key])
end
end
end)
end)
end
@@ -0,0 +1,78 @@
local CorePackages = game:GetService("CorePackages")
local Rodux = require(CorePackages.Rodux)
local Cryo = require(CorePackages.Cryo)
local Actions = script.Parent.Parent.Actions
local SetMoreMenuOpen = require(Actions.SetMoreMenuOpen)
local SetBackpackOpen = require(Actions.SetBackpackOpen)
local SetEmotesOpen = require(Actions.SetEmotesOpen)
local SetLeaderboardOpen = require(Actions.SetLeaderboardOpen)
local SetEmotesEnabled = require(Actions.SetEmotesEnabled)
local FFlagTopBarCloseContextMenuWhenHotkeysUsed = game:DefineFastFlag(
"TopBarCloseContextMenuWhenHotkeysUsed", false
)
local initialState = {
open = false,
backpackOpen = false,
leaderboardOpen = false,
emotesOpen = false,
emotesEnabled = true,
}
local MoreMenu = Rodux.createReducer(initialState, {
[SetMoreMenuOpen.name] = function(state, action)
return Cryo.Dictionary.join(state, {
open = action.open,
})
end,
[SetBackpackOpen.name] = function(state, action)
if FFlagTopBarCloseContextMenuWhenHotkeysUsed then
return Cryo.Dictionary.join(state, {
open = false,
backpackOpen = action.open,
})
else
return Cryo.Dictionary.join(state, {
backpackOpen = action.open,
})
end
end,
[SetEmotesOpen.name] = function(state, action)
if FFlagTopBarCloseContextMenuWhenHotkeysUsed then
return Cryo.Dictionary.join(state, {
open = false,
emotesOpen = action.open,
})
else
return Cryo.Dictionary.join(state, {
emotesOpen = action.open,
})
end
end,
[SetEmotesEnabled.name] = function(state, action)
return Cryo.Dictionary.join(state, {
emotesEnabled = action.enabled,
})
end,
[SetLeaderboardOpen.name] = function(state, action)
if FFlagTopBarCloseContextMenuWhenHotkeysUsed then
return Cryo.Dictionary.join(state, {
open = false,
leaderboardOpen = action.open,
})
else
return Cryo.Dictionary.join(state, {
leaderboardOpen = action.open,
})
end
end,
})
return MoreMenu
@@ -0,0 +1,195 @@
return function()
local TopBar = script.Parent.Parent
local Actions = TopBar.Actions
local SetMoreMenuOpen = require(Actions.SetMoreMenuOpen)
local SetBackpackOpen = require(Actions.SetBackpackOpen)
local SetEmotesOpen = require(Actions.SetEmotesOpen)
local SetLeaderboardOpen = require(Actions.SetLeaderboardOpen)
local SetEmotesEnabled = require(Actions.SetEmotesEnabled)
local MoreMenu = require(script.Parent.MoreMenu)
local FFlagTopBarCloseContextMenuWhenHotkeysUsed = game:GetFastFlag(
"TopBarCloseContextMenuWhenHotkeysUsed"
)
local function countValues(t)
local c = 0
for _, _ in pairs(t) do
c = c + 1
end
return c
end
it("should have the correct default values", function()
local defaultState = MoreMenu(nil, {})
expect(type(defaultState)).to.equal("table")
expect(defaultState.open).to.equal(false)
expect(defaultState.backpackOpen).to.equal(false)
expect(defaultState.leaderboardOpen).to.equal(false)
expect(defaultState.emotesOpen).to.equal(false)
end)
describe("SetMoreMenuOpen", function()
it("should change the value of open", function()
local oldState = MoreMenu(nil, {})
local newState = MoreMenu(oldState, SetMoreMenuOpen(true))
expect(oldState).to.never.equal(newState)
expect(newState.open).to.equal(true)
end)
it("should not change any other values", function()
local oldState = MoreMenu(nil, {})
local newState = MoreMenu(oldState, SetMoreMenuOpen(true))
expect(countValues(newState)).to.equal(countValues(oldState))
for key, value in pairs(newState) do
if key ~= "open" then
expect(value).to.equal(oldState[key])
end
end
end)
end)
describe("SetBackpackOpen", function()
it("should change the value of backpackOpen", function()
local oldState = MoreMenu(nil, {})
local newState = MoreMenu(oldState, SetBackpackOpen(true))
expect(oldState).to.never.equal(newState)
expect(newState.backpackOpen).to.equal(true)
end)
if FFlagTopBarCloseContextMenuWhenHotkeysUsed then
it("should not change any other values except open", function()
local oldState = MoreMenu(nil, {})
local newState = MoreMenu(oldState, SetBackpackOpen(true))
expect(countValues(newState)).to.equal(countValues(oldState))
for key, value in pairs(newState) do
if key ~= "backpackOpen" and key ~= "open" then
expect(value).to.equal(oldState[key])
end
end
end)
it("should set open to false when changed", function()
local oldState = MoreMenu(nil, {})
oldState = MoreMenu(oldState, SetMoreMenuOpen(true))
local newState = MoreMenu(oldState, SetBackpackOpen(true))
expect(oldState).to.never.equal(newState)
expect(newState.open).to.equal(false)
end)
else
it("should not change any other values", function()
local oldState = MoreMenu(nil, {})
local newState = MoreMenu(oldState, SetBackpackOpen(true))
expect(countValues(newState)).to.equal(countValues(oldState))
for key, value in pairs(newState) do
if key ~= "backpackOpen" then
expect(value).to.equal(oldState[key])
end
end
end)
end
end)
describe("SetEmotesOpen", function()
it("should change the value of emotesOpen", function()
local oldState = MoreMenu(nil, {})
local newState = MoreMenu(oldState, SetEmotesOpen(true))
expect(oldState).to.never.equal(newState)
expect(newState.emotesOpen).to.equal(true)
end)
if FFlagTopBarCloseContextMenuWhenHotkeysUsed then
it("should not change any other values except open", function()
local oldState = MoreMenu(nil, {})
local newState = MoreMenu(oldState, SetEmotesOpen(true))
expect(countValues(newState)).to.equal(countValues(oldState))
for key, value in pairs(newState) do
if key ~= "emotesOpen" and key ~= "open" then
expect(value).to.equal(oldState[key])
end
end
end)
it("should set open to false when changed", function()
local oldState = MoreMenu(nil, {})
oldState = MoreMenu(oldState, SetMoreMenuOpen(true))
local newState = MoreMenu(oldState, SetEmotesOpen(true))
expect(oldState).to.never.equal(newState)
expect(newState.open).to.equal(false)
end)
else
it("should not change any other values", function()
local oldState = MoreMenu(nil, {})
local newState = MoreMenu(oldState, SetEmotesOpen(true))
expect(countValues(newState)).to.equal(countValues(oldState))
for key, value in pairs(newState) do
if key ~= "emotesOpen" then
expect(value).to.equal(oldState[key])
end
end
end)
end
end)
describe("SetEmotesEnabled", function()
it("should change the value of emotesEnabled", function()
local oldState = MoreMenu(nil, {})
local newState = MoreMenu(oldState, SetEmotesEnabled(false))
expect(oldState).to.never.equal(newState)
expect(newState.emotesEnabled).to.equal(false)
end)
it("should not change any other values", function()
local oldState = MoreMenu(nil, {})
local newState = MoreMenu(oldState, SetEmotesEnabled(false))
expect(countValues(newState)).to.equal(countValues(oldState))
for key, value in pairs(newState) do
if key ~= "emotesEnabled" then
expect(value).to.equal(oldState[key])
end
end
end)
end)
describe("SetLeaderboardOpen", function()
it("should change the value of open", function()
local oldState = MoreMenu(nil, {})
local newState = MoreMenu(oldState, SetLeaderboardOpen(true))
expect(oldState).to.never.equal(newState)
expect(newState.leaderboardOpen).to.equal(true)
end)
if FFlagTopBarCloseContextMenuWhenHotkeysUsed then
it("should not change any other values except open", function()
local oldState = MoreMenu(nil, {})
local newState = MoreMenu(oldState, SetLeaderboardOpen(true))
expect(countValues(newState)).to.equal(countValues(oldState))
for key, value in pairs(newState) do
if key ~= "leaderboardOpen" and key ~= "open" then
expect(value).to.equal(oldState[key])
end
end
end)
it("should set open to false when changed", function()
local oldState = MoreMenu(nil, {})
oldState = MoreMenu(oldState, SetMoreMenuOpen(true))
local newState = MoreMenu(oldState, SetLeaderboardOpen(true))
expect(oldState).to.never.equal(newState)
expect(newState.open).to.equal(false)
end)
else
it("should not change any other values", function()
local oldState = MoreMenu(nil, {})
local newState = MoreMenu(oldState, SetLeaderboardOpen(true))
expect(countValues(newState)).to.equal(countValues(oldState))
for key, value in pairs(newState) do
if key ~= "leaderboardOpen" then
expect(value).to.equal(oldState[key])
end
end
end)
end
end)
end
@@ -0,0 +1,19 @@
local CorePackages = game:GetService("CorePackages")
local Rodux = require(CorePackages.Rodux)
local Actions = script.Parent.Parent.Actions
local SetRespawnBehaviour = require(Actions.SetRespawnBehaviour)
return Rodux.createReducer({
enabled = true,
customCallback = nil,
}, {
[SetRespawnBehaviour.name] = function(state, action)
return {
enabled = action.respawnEnabled,
customCallback = action.customCallback,
}
end,
})
@@ -0,0 +1,32 @@
return function()
local TopBar = script.Parent.Parent
local Actions = TopBar.Actions
local SetRespawnBehaviour = require(Actions.SetRespawnBehaviour)
local Respawn = require(script.Parent.Respawn)
it("should have the correct default values", function()
local defaultState = Respawn(nil, {})
expect(type(defaultState)).to.equal("table")
expect(defaultState.enabled).to.equal(true)
expect(defaultState.customCallback).to.equal(nil)
end)
describe("SetRespawnBehavior", function()
it("should change the value of enabled", function()
local oldState = Respawn(nil, {})
local newState = Respawn(oldState, SetRespawnBehaviour(false))
expect(oldState).to.never.equal(newState)
expect(newState.enabled).to.equal(false)
end)
it("should change the value of customCallback", function()
local Callback = Instance.new("BindableEvent")
local oldState = Respawn(nil, {})
local newState = Respawn(oldState, SetRespawnBehaviour(true, Callback))
expect(oldState).to.never.equal(newState)
expect(newState.customCallback).to.equal(Callback)
end)
end)
end
@@ -0,0 +1,26 @@
local CorePackages = game:GetService("CorePackages")
local CoreGui = game:GetService("CoreGui")
local Rodux = require(CorePackages.Rodux)
local RobloxGui = CoreGui:WaitForChild("RobloxGui")
local isNewGamepadMenuEnabled = require(RobloxGui.Modules.Flags.isNewGamepadMenuEnabled)
local DisplayOptions = require(script.DisplayOptions)
local CoreGuiEnabled = require(script.CoreGuiEnabled)
local Health = require(script.Health)
local MoreMenu = require(script.MoreMenu)
local Chat = require(script.Chat)
local Respawn = require(script.Respawn)
local GameInfo = require(script.GameInfo)
local Reducer = Rodux.combineReducers({
displayOptions = DisplayOptions,
coreGuiEnabled = CoreGuiEnabled,
health = Health,
moreMenu = MoreMenu,
chat = Chat,
respawn = isNewGamepadMenuEnabled() and Respawn or nil,
gameInfo = isNewGamepadMenuEnabled() and GameInfo or nil,
})
return Reducer
@@ -0,0 +1,33 @@
local Chat = game:GetService("Chat")
local RunService = game:GetService("RunService")
local Players = game:GetService("Players")
local CoreGui = game:GetService("CoreGui")
local TopBar = script.Parent.Parent
local SetCanChat = require(TopBar.Actions.SetCanChat)
local RobloxGui = CoreGui:WaitForChild("RobloxGui")
local ChatSelector = require(RobloxGui.Modules.ChatSelector)
local LocalPlayer = Players.LocalPlayer
while not LocalPlayer do
Players:GetPropertyChangedSignal("LocalPlayer"):Wait()
LocalPlayer = Players.LocalPlayer
end
return function(store)
coroutine.wrap(function()
local canChat = true
if not RunService:IsStudio() then
local success, localUserCanChat = pcall(function()
return Chat:CanUserChatAsync(LocalPlayer.UserId)
end)
canChat = success and localUserCanChat
end
store:dispatch(SetCanChat(canChat))
if not canChat then
ChatSelector:SetVisible(false)
end
end)()
end
@@ -0,0 +1,40 @@
local CoreGui = game:GetService("CoreGui")
local CorePackages = game:GetService("CorePackages")
local HttpRbxApiService = game:GetService("HttpRbxApiService")
local LocalizationService = game:GetService("LocalizationService")
local RobloxGui = CoreGui:WaitForChild("RobloxGui")
local httpRequest = require(CorePackages.AppTempCommon.Temp.httpRequest)
local httpImpl = httpRequest(HttpRbxApiService)
local Thunks = script.Parent
local TopBar = Thunks.Parent
local SetGameName = require(TopBar.Actions.SetGameName)
local GetGameNameAndDescription = require(RobloxGui.Modules.Common.GetGameNameAndDescription)
return function(store)
coroutine.wrap(function()
if game.GameId == 0 then
return
end
GetGameNameAndDescription(httpImpl, game.GameId):andThen(function(
gameNameLocaleMap, gameDescriptionsLocaleMap, sourceLocale)
local localeGameName = gameNameLocaleMap[LocalizationService.RobloxLocaleId]
if localeGameName then
return store:dispatch(SetGameName(localeGameName))
end
local sourceGameName = gameNameLocaleMap[sourceLocale]
if sourceGameName then
return store:dispatch(SetGameName(sourceGameName))
end
end):catch(function()
warn("Unable to get game name for Gamepad Menu")
end)
end)()
end
@@ -0,0 +1,19 @@
local Players = game:GetService("Players")
return function(store)
local state = store:getState()
if state.respawn.customCallback then
state.respawn.customCallback:Fire()
else
local character = Players.LocalPlayer.Character
if character ~= nil then
-- For backwards compatibility with games that disable respawning
-- by renaming the humanoid instance, we don't use FindFirstChildOfClass.
local humanoid = character:FindFirstChild("Humanoid")
if humanoid ~= nil and humanoid:IsA("Humanoid") then
humanoid.Health = 0
end
end
end
end
@@ -0,0 +1,12 @@
local CorePackages = game:GetService("CorePackages")
local PolicyProvider = require(CorePackages.PolicyProvider)
local implementation = PolicyProvider.GetPolicyImplementations.MemStorageService("app-policy")
local TopBarAppPolicy = PolicyProvider.withGetPolicyImplementation(implementation)
TopBarAppPolicy.Mapper = function(policy)
return {
}
end
return TopBarAppPolicy
@@ -0,0 +1,44 @@
return function()
local CorePackages = game:GetService("CorePackages")
local Roact = require(CorePackages.Roact)
it("should create and destroy without errors", function()
local TopBarAppPolicy = require(script.Parent.TopBarAppPolicy)
local testPolicyValue = "TestPolicy"
local testPolicyMapper = function(policy)
return {
UnitTestPolicy = function()
return testPolicyValue
end
}
end
local RoactTestComponent = Roact.PureComponent:extend("RoactTestComponent")
function RoactTestComponent:render()
end
function RoactTestComponent:didMount()
self.props.callback(self.props.testValue)
end
RoactTestComponent = TopBarAppPolicy.connect(function(appPolicy, props)
return {
testValue = appPolicy.UnitTestPolicy(),
}
end)(RoactTestComponent)
local testValue
local function testCallback(actualTestValue)
testValue = actualTestValue
end
local element = Roact.createElement(TopBarAppPolicy.Provider, {
policy = { testPolicyMapper },
}, {
SomeComponent = Roact.createElement(RoactTestComponent, {
callback = testCallback,
}),
})
local instance = Roact.mount(element)
expect(testValue).to.equal(testPolicyValue)
Roact.unmount(instance)
end)
end
@@ -0,0 +1,120 @@
local CorePackages = game:GetService("CorePackages")
local CoreGui = game:GetService("CoreGui")
local GuiService = game:GetService("GuiService")
local RobloxGui = CoreGui:WaitForChild("RobloxGui")
local AppDarkTheme = require(CorePackages.AppTempCommon.LuaApp.Style.Themes.DarkTheme)
local AppFont = require(CorePackages.AppTempCommon.LuaApp.Style.Fonts.Gotham)
local Roact = require(CorePackages.Roact)
local Rodux = require(CorePackages.Rodux)
local RoactRodux = require(CorePackages.RoactRodux)
local UIBlox = require(CorePackages.UIBlox)
local SettingsUtil = require(RobloxGui.Modules.Settings.Utility)
local TenFootInterface = require(RobloxGui.Modules.TenFootInterface)
local isNewInGameMenuEnabled = require(RobloxGui.Modules.isNewInGameMenuEnabled)
local isNewGamepadMenuEnabled = require(RobloxGui.Modules.Flags.isNewGamepadMenuEnabled)
local GetFFlagUseRoactPolicyProvider = require(RobloxGui.Modules.Flags.GetFFlagUseRoactPolicyProvider)
local TopBarApp = require(script.Components.TopBarApp)
local Reducer = require(script.Reducer)
local Constants = require(script.Constants)
local TopBarAppPolicy = require(script.TopBarAppPolicy)
local SetSmallTouchDevice = require(script.Actions.SetSmallTouchDevice)
local SetInspectMenuOpen = require(script.Actions.SetInspectMenuOpen)
local GetCanChat = require(script.Thunks.GetCanChat)
local GetGameName = require(script.Thunks.GetGameName)
local registerSetCores = require(script.registerSetCores)
local GlobalConfig = require(script.GlobalConfig)
local TopBar = {}
TopBar.__index = TopBar
function TopBar.new()
local self = setmetatable({}, TopBar)
if GlobalConfig.propValidation then
Roact.setGlobalConfig({
propValidation = true,
})
end
if GlobalConfig.elementTracing then
Roact.setGlobalConfig({
elementTracing = true,
})
end
if not TenFootInterface:IsEnabled() then
GuiService:SetGlobalGuiInset(0, Constants.TopBarHeight, 0, 0)
end
self.store = Rodux.Store.new(Reducer, nil, {
Rodux.thunkMiddleware,
})
registerSetCores(self.store)
self.store:dispatch(GetCanChat)
if isNewGamepadMenuEnabled() then
self.store:dispatch(GetGameName)
end
if isNewInGameMenuEnabled() then
-- Move to top of script when removing isNewInGameMenuEnabled
local InGameMenu = require(RobloxGui.Modules.InGameMenu)
InGameMenu.mountInGameMenu()
end
coroutine.wrap(function()
self.store:dispatch(SetSmallTouchDevice(SettingsUtil.IsSmallTouchScreen()))
end)()
local appStyle = {
Theme = AppDarkTheme,
Font = AppFont,
}
if GetFFlagUseRoactPolicyProvider() then
self.root = Roact.createElement(RoactRodux.StoreProvider, {
store = self.store,
}, {
PolicyProvider = Roact.createElement(TopBarAppPolicy.Provider, {
policy = { TopBarAppPolicy.Mapper },
}, {
ThemeProvider = Roact.createElement(UIBlox.Style.Provider, {
style = appStyle,
}, {
TopBarApp = Roact.createElement(TopBarApp)
})
})
})
else
self.root = Roact.createElement(RoactRodux.StoreProvider, {
store = self.store,
}, {
ThemeProvider = Roact.createElement(UIBlox.Style.Provider, {
style = appStyle,
}, {
TopBarApp = Roact.createElement(TopBarApp)
})
})
end
self.element = Roact.mount(self.root, CoreGui, "TopBar")
return self
end
function TopBar:setInspectMenuOpen(open)
self.store:dispatch(SetInspectMenuOpen(open))
end
return TopBar.new()
@@ -0,0 +1,6 @@
return function()
it("should require without errors", function()
local TopBarApp = require(script.Parent)
expect(TopBarApp).to.be.ok()
end)
end
@@ -0,0 +1,21 @@
local StarterGui = game:GetService("StarterGui")
local TopBar = script.Parent
local SetTopBarEnabled = require(TopBar.Actions.SetTopBarEnabled)
local function registerSetCores(store)
StarterGui:RegisterSetCore("TopbarEnabled", function(enabled)
if type(enabled) == "boolean" then
store:dispatch(SetTopBarEnabled(enabled))
else
error("SetCore TopbarEnabled argument must be a boolean", 2)
end
end)
StarterGui:RegisterGetCore("TopbarEnabled", function()
return store:getState().displayOptions.topbarEnabled
end)
end
return registerSetCores