local Signal = require(script.Parent.Parent.Parent.Parent.Parent.Signal)

export type AttributeBind = {
	Instance: Instance,
	Attribute: string,
	Value: any,
	Fallback: any?,
	_Connections: { RBXScriptConnection | Signal.Connection },

	Changed: Signal.Signal<any>,

	_Update: (self: AttributeBind) -> any,
	Get: (self: AttributeBind) -> any,
	Bind: (Instance: Instance, Attribute: string, Fallback: any?) -> (),
	Observe: (self: AttributeBind, Callback: (Value: any) -> ()) -> (),
}

local AttributeBind = {}

function AttributeBind.Observe(self: AttributeBind, Callback: (Value: any) -> ())
	assert(typeof(Callback) == "function", `Callback is not a function.`)

	Callback(self.Value)
	table.insert(
		self._Connections,
		self.Changed:Connect(function()
			Callback(self.Value)
		end)
	)
end

function AttributeBind.Get(self: AttributeBind): any
	return self.Value
end

function AttributeBind._Update(self: AttributeBind)
	local OldValue = self.Value
	local Value = self.Instance:GetAttribute(self.Attribute)

	if Value == nil then
		Value = self.Fallback
	end

	self.Value = Value

	if Value ~= OldValue then
		self.Changed:Fire(Value)
	end

	return Value
end

function AttributeBind.Destroy(self: AttributeBind)
	for _, Connection in ipairs(self._Connections) do
		Connection:Disconnect()
	end
end

function AttributeBind.Bind(Instance: Instance, Attribute: string, Fallback: any?): AttributeBind
	assert(typeof(Instance) == "Instance", `{tostring(Instance)} is not an Instance.`)
	assert(typeof(Attribute) == "string", `{tostring(Attribute)} is not a string.`)

	local self = table.clone(AttributeBind)
	self._Connections = {}
	self.Instance = Instance
	self.Attribute = Attribute
	self.Fallback = Fallback
	self.Changed = Signal.new()

	self:_Update()
	table.insert(
		self._Connections,
		Instance:GetAttributeChangedSignal(Attribute):Connect(function()
			self:_Update()
		end)
	)

	self.Instance.Destroying:Connect(function()
		self:Destroy()
	end)

	return self
end

return AttributeBind


local Players = game:GetService("Players")
local StarterGui = game:GetService("StarterGui")
local TextChatService = game:GetService("TextChatService")

local MouseLockController = Players.LocalPlayer.PlayerScripts
	:WaitForChild("PlayerModule")
	:WaitForChild("CameraModule")
	:WaitForChild("MouseLockController")

local CHAT_WINDOW_THEME = {
	BackgroundColor3 = Color3.fromRGB(0, 0, 0),
	BackgroundTransparency = 0.3,
	TextSize = 15,
	TextStrokeTransparency = 0.6,
}
local CHAT_WINDOW_INPUT_THEME = {
	BackgroundTransparency = 1,
	TextColor3 = Color3.fromRGB(205, 205, 205),
	TextSize = 15,
	TextStrokeTransparency = 1,
}
local BUBBLE_CHAT_THEME = {
	BackgroundColor3 = Color3.fromRGB(20, 20, 20),
	BackgroundTransparency = 0.2,
	TextColor3 = Color3.fromRGB(205, 205, 205),
	TextSize = 17,
}
local SHIFT_LOCK_KEYS = "LeftControl"
local AVATAR_CONTEXT_MENU_THEME = {
	BackgroundTransparency = 1,
	BackgroundImage = "rbxassetid://16819084929",
	BackgroundImageSliceCenter = Rect.new(12, 12, 12, 12),
	BackgroundImageTransparency = 0.3,
	NameTagColor = Color3.fromRGB(20, 20, 20),
	ButtonFrameTransparency = 1,
	ButtonColor = Color3.fromRGB(255, 255, 255),
	ButtonTransparency = 1,
	ButtonHoverTransparency = 0.9,
}
local SELECTION_INDICATOR_OPTIONS = {
	Name = "Indicator",
	Locked = true,
	CanCollide = false,
	Anchored = true,
	Color = Color3.fromRGB(170, 170, 170),
	Material = Enum.Material.Neon,
	Size = Vector3.new(0.45, 0.45, 0.45),
}

type AvatarContextMenuButton = {}

local CoreThemer = {
	_AvatarSelectionHighlight = Instance.new("Highlight"),
	_AvatarSelectionConnection = nil,
}

function CoreThemer:SetDefaults()
	self:SetChatWindowTheme()
	self:SetChatWindowInputTheme()
	self:SetBubbleChatTheme()
	self:SetShiftLockKeys()
	self:SetAvatarContextMenuTheme()
end

function CoreThemer:SetAvatarContextMenuEnabled(Enabled: boolean)
	if Enabled == nil then
		Enabled = true
	end

	self:CallCore("SetCore", "AvatarContextMenuEnabled", Enabled)

	if Enabled then
		self:_StartAvatarSelectionHandler()
	else
		self:_StopAvatarSelectionHandler()
	end
end

function CoreThemer:SetAvatarContextMenuTheme(Options: table?)
	if Options == nil then
		Options = AVATAR_CONTEXT_MENU_THEME

		local SelectionIndicator = Instance.new("Part")
		self:_SetProperties(SelectionIndicator, SELECTION_INDICATOR_OPTIONS)
		Options.SelectedCharacterIndicator = SelectionIndicator
	end

	self:CallCore("SetCore", "AvatarContextMenuTheme", Options)
end

function CoreThemer:SetShiftLockKeys(Keys: string?)
	if Keys == nil then
		Keys = SHIFT_LOCK_KEYS
	end

	local BoundKeys = MouseLockController:FindFirstChild("BoundKeys")
	if BoundKeys then
		BoundKeys.Value = Keys
	else
		BoundKeys = Instance.new("StringValue")
		BoundKeys.Name = "BoundKeys"
		BoundKeys.Value = Keys
		BoundKeys.Parent = MouseLockController
	end
end

function CoreThemer:SetChatWindowTheme(Options: table?)
	if Options == nil then
		Options = CHAT_WINDOW_THEME
	end

	self:_SetProperties(TextChatService.ChatWindowConfiguration, Options)
end

function CoreThemer:SetChatWindowInputTheme(Options: table?)
	if Options == nil then
		Options = CHAT_WINDOW_INPUT_THEME
	end

	self:_SetProperties(TextChatService.ChatInputBarConfiguration, Options)
end

function CoreThemer:SetBubbleChatTheme(Options: table?)
	if Options == nil then
		Options = BUBBLE_CHAT_THEME
	end

	self:_SetProperties(TextChatService.BubbleChatConfiguration, Options)

	if Options == nil then
		local UICorner = TextChatService.BubbleChatConfiguration:FindFirstChild("UICorner")
		if UICorner then
			UICorner.CornerRadius = UDim.new(0, 16)
		end
	end
end

function CoreThemer:CallCore(Method, ...)
	local Result = {}

	for _ = 1, 8 do
		Result = { pcall(StarterGui[Method], StarterGui, ...) }
		if Result[1] then
			break
		end
		task.wait()
	end

	return unpack(Result)
end

function CoreThemer:_StartAvatarSelectionHandler()
	self:_StopAvatarSelectionHandler()

	local Mouse = Players.LocalPlayer:GetMouse()
	self._AvatarSelectionConnection = Mouse.Move:Connect(function()
		self._AvatarSelectionHighlight.Adornee = nil

		local Target = Mouse.Target
		if Target then
			local Character = Target:FindFirstAncestorOfClass("Model")
			if Character then
				local Player = Players:GetPlayerFromCharacter(Character)
				if Player then
					self._AvatarSelectionHighlight.Adornee = Character
				end
			end
		end
	end)
end

function CoreThemer:_StopAvatarSelectionHandler()
	if self._AvatarSelectionConnection then
		self._AvatarSelectionConnection:Disconnect()
		self._AvatarSelectionConnection = nil
	end

	self._AvatarSelectionHighlight.Adornee = nil
end

function CoreThemer:_SetProperties(Instance: Instance, Properties: table)
	for Property, Value in pairs(Properties) do
		Instance[Property] = Value
	end
end

function CoreThemer:_Initialize()
	self._AvatarSelectionHighlight.Parent = Players.LocalPlayer.PlayerGui
	self._AvatarSelectionHighlight.FillTransparency = 1
	self._AvatarSelectionHighlight.OutlineTransparency = 0.1
	self._AvatarSelectionHighlight.DepthMode = Enum.HighlightDepthMode.Occluded
end

CoreThemer:_Initialize()

return CoreThemer


local function DeepCopy(Table: { [any]: any })
	local NewTable = {}

	for Key, Value in pairs(Table) do
		if typeof(Value) == "table" then
			NewTable[Key] = DeepCopy(Value)
		else
			NewTable[Key] = Value
		end
	end

	return NewTable
end

return DeepCopy


local TextService = game:GetService("TextService")

return function(String: string, Player: Player)
  local FilteredText
  local Success, Log = pcall(function()
    local FilterResult = TextService:FilterStringAsync(String, Player.UserId)
    FilteredText = FilterResult:GetNonChatStringForBroadcastAsync()
  end)
  if Success then
    return FilteredText
  else
    warn(Log)
    return "Filtering Error"
  end
end


local function GetEntriesFromArray(Array: { [number]: any }, Start: number, End: number)
	local Entries = {}

	for Index = Start, End do
		table.insert(Entries, Array[Index])
	end

	return Entries
end

return GetEntriesFromArray


local ValueClassNames = {
  int = "IntValue",
  number = "NumberValue",
  string = "StringValue",
  boolean = "BoolValue",
  Instance = "ObjectValue",
  BrickColor = "BrickColorValue",
  Color3 = "Color3Value",
  Vector3 = "Vector3Value",
}

local InternalLeaderStats = {}

local LeaderStats = {}
LeaderStats.__index = LeaderStats

function LeaderStats.New(Player: Player)
  local self = {}
  setmetatable(self, LeaderStats)

  self.Player = Player
  self.StatValues = {}

  self.LeaderStatsFolder = Instance.new("Folder")
  self.LeaderStatsFolder.Name = "leaderstats"
  self.LeaderStatsFolder.Parent = self.Player

  InternalLeaderStats[Player] = self
  return self
end

function LeaderStats:Get(Player: Player)
  return InternalLeaderStats[Player]
end

function LeaderStats:Destroy()
  InternalLeaderStats[self.Player] = nil
  self.LeaderStatsFolder:Destroy()
  self = nil
end

function LeaderStats:SetStat(StatName: string, Value: any)
  local Stat = self.StatValues[StatName]
  if Stat == nil then
      Stat = Instance.new(ValueClassNames[typeof(Value)])
      Stat.Name = StatName
      Stat.Parent = self.LeaderStatsFolder
      self.StatValues[StatName] = Stat
  end
  if Value then
      Stat.Value = Value
  else
      Stat:Destroy()
  end
end

function LeaderStats:GetStat(StatName: string)
  local Stat = self.StatValues[StatName]
  return Stat
end

return LeaderStats


local RunService = game:GetService("RunService")

local RoRooms = script.Parent.Parent.Parent.Parent.Parent
local OnyxUI = require(RoRooms.Parent.OnyxUI)
local Fusion = require(RoRooms.Parent.Fusion)

local Util = OnyxUI.Util
local Themer = OnyxUI.Themer
local Children = Fusion.Children

local TagText = require(script.Parent.TagText)
local PropertyEntry = require(script.Parent.PropertyEntry)

export type Props = {
	Character: Fusion.UsedAs<Model>?,
	Humanoid: Fusion.UsedAs<Humanoid>?,
	Head: Fusion.UsedAs<BasePart>?,
	Player: Fusion.UsedAs<Player>?,
	Properties: { { Value: any, Icon: string? } }?,
	DisplayName: string?,
	Color: Color3?,
}

return function(Scope: Fusion.Scope<any>, Props: Props)
	local Scope = Fusion.innerScope(Scope, Fusion, Util, OnyxUI.Components, {
		TagText = TagText,
		PropertyEntry = PropertyEntry,
	})
	local Theme = Themer.Theme:now()

	local Character = Scope:EnsureValue(Props.Character, "Model", nil)
	local Humanoid = Scope:EnsureValue(Props.Humanoid, "Humanoid", nil)
	local Head = Scope:EnsureValue(Props.Head, "BasePart", nil)

	local Properties = Util.Fallback(Props.Properties, {})
	local DisplayName = Util.Fallback(
		Props.DisplayName,
		Scope:Computed(function(Use)
			local HumanoidValue = Use(Humanoid)
			if HumanoidValue and HumanoidValue.DisplayName then
				return HumanoidValue and HumanoidValue.DisplayName
			else
				return "Name"
			end
		end)
	)
	local Color = Util.Fallback(Props.Color, Theme.Colors.BaseContent.Main)
	local AbsoluteSize = Scope:Value(Vector2.new())

	local Contents = Scope:Frame {
		AnchorPoint = Vector2.new(0.5, 1),
		Position = UDim2.fromScale(0.5, 1),
		ListEnabled = true,
		ListPadding = Scope:Computed(function(Use)
			return UDim.new(0, Use(Theme.Spacing["0.25"]) / 2)
		end),
		ListHorizontalAlignment = Enum.HorizontalAlignment.Center,

		[Children] = {
			Scope:TagText {
				Name = "DisplayName",
				Text = DisplayName,
				TextColor3 = Color,
				FontFace = Scope:Computed(function(Use)
					return Font.new(Use(Theme.Font.Body), Use(Theme.FontWeight.Bold))
				end),
				StrokeEnabled = true,
			},
			Scope:Frame {
				Name = "Properties",
				ListEnabled = true,
				ListFillDirection = Enum.FillDirection.Horizontal,
				ListHorizontalAlignment = Enum.HorizontalAlignment.Center,
				ListPadding = Scope:Computed(function(Use)
					return UDim.new(0, Use(Theme.Spacing["0.25"]))
				end),
				CornerRadius = Scope:Computed(function(Use)
					return UDim.new(0, 1000)
				end),
				Padding = Scope:Computed(function(Use)
					return UDim.new(0, Use(Theme.Spacing["0.25"]))
				end),
				PaddingTop = Scope:Computed(function(Use)
					return UDim.new(0, Use(Theme.Spacing["0"]))
				end),
				PaddingBottom = Scope:Computed(function(Use)
					return UDim.new(0, Use(Theme.Spacing["0"]))
				end),
				BackgroundColor3 = Util.Colors.Black,
				BackgroundTransparency = 0.8,

				[Children] = {
					Scope:ForValues(
						Properties,
						function(Use, Scope, Property: { Value: Fusion.UsedAs<any>, Image: Fusion.UsedAs<string?>? })
							return Scope:PropertyEntry {
								Value = Property.Value,
								Image = Property.Image,
							}
						end
					),
				},
			},
		},
	}

	RunService.RenderStepped:Connect(function()
		AbsoluteSize:set(Contents.AbsoluteSize)
	end)

	return Scope:New "BillboardGui" {
		Name = "Nametag",
		Parent = Character,
		Adornee = Head,
		Active = false,
		AutoLocalize = false,
		ClipsDescendants = false,
		Size = Scope:Computed(function(Use)
			local AbsoluteSizeValue = Use(AbsoluteSize)
			return UDim2.fromOffset(AbsoluteSizeValue.X, AbsoluteSizeValue.Y)
		end),
		StudsOffset = Vector3.new(0, 1.25, 0),
		SizeOffset = Vector2.new(0, 1),
		MaxDistance = 50,

		[Children] = {
			Contents,
		},
	}
end


local RoRooms = script.Parent.Parent.Parent.Parent.Parent
local OnyxUI = require(RoRooms.Parent.OnyxUI)
local Fusion = require(RoRooms.Parent.Fusion)

local Children = Fusion.Children
local Themer = OnyxUI.Themer
local Util = OnyxUI.Util

local TagText = require(script.Parent.TagText)

export type Props = {
	Value: Fusion.UsedAs<any>,
	Image: Fusion.UsedAs<string>?,
}

return function(Scope: Fusion.Scope<any>, Props: Props)
	local Scope = Fusion.innerScope(Scope, Fusion, Util, OnyxUI.Components, {
		TagText = TagText,
	})
	local Theme = Themer.Theme:now()

	local ValueProp = Util.Fallback(Props.Value, nil)
	local Image = Util.Fallback(Props.Image, nil)

	return Scope:Frame(Util.CombineProps(Props, {
		Name = script.Name,
		ListEnabled = true,
		ListFillDirection = Enum.FillDirection.Horizontal,
		ListPadding = Scope:Computed(function(Use)
			return UDim.new(0, Use(Theme.Spacing["0.25"]) / 2)
		end),

		[Children] = {
			Scope:Computed(function(Use)
				if Use(Image) then
					return Scope:Icon {
						Image = Image,
						Size = Scope:Computed(function(Use)
							local Offset = Use(Theme.TextSize["0.875"])
							return UDim2.fromOffset(Offset, Offset)
						end),
					}
				else
					return
				end
			end),
			Scope:TagText {
				Name = "Value",
				Text = Scope:Computed(function(Use)
					return tostring(Use(ValueProp)) or ""
				end),
				TextSize = Theme.TextSize["0.875"],
				StrokeEnabled = false,
			},
		},
	}))
end


local RoRooms = script.Parent.Parent.Parent.Parent.Parent
local OnyxUI = require(RoRooms.Parent.OnyxUI)
local Fusion = require(RoRooms.Parent.Fusion)

local Util = OnyxUI.Util

export type Props = {}

return function(Scope: Fusion.Scope<any>, Props: Props)
	local Scope = Fusion.innerScope(Scope, Fusion, OnyxUI.Components)

	return Scope:Text(Util.CombineProps(Props, {
		Name = script.Name,
		TextColor3 = Util.Colors.White,
		TextWrapped = false,
		RichText = false,
		ClipsDescendants = false,
		StrokeThickness = 1,
		StrokeColor = Util.Colors.Black,
		StrokeTransparency = 0.7,
		StrokeApplyStrokeMode = Enum.ApplyStrokeMode.Contextual,
	}))
end


local Players = game:GetService("Players")

local RoRooms = script.Parent.Parent.Parent.Parent
local Nametag = require(script.Nametag)
local Fusion = require(RoRooms.Parent.Fusion)

export type Property = {
	Value: any,
	Image: string?,
}
export type Properties = { Property? }
export type Config = {
	DisplayName: string?,
	Properties: Properties?,
	Color: Color3?,
}

local Nametagger = {
	_CharacterScopes = {},
}

function Nametagger:TagCharacter(Character: Model, Config: Config?)
	local SafeConfig: Config
	if Config ~= nil then
		SafeConfig = Config
	else
		SafeConfig = {}
	end

	if self._CharacterScopes[Character] then
		self._CharacterScopes[Character]:doCleanup()
	end

	local Scope = Fusion.scoped(Fusion, {
		Nametag = Nametag,
	})

	self._CharacterScopes[Character] = Scope

	local Head = Scope:Value()
	local Humanoid = Scope:Value()
	local Player = Players:GetPlayerFromCharacter(Character)

	local function UpdateHead()
		Head:set(Character:FindFirstChild("Head"))
	end
	local function UpdateHumanoid()
		local HumanoidObject = Character:FindFirstChildOfClass("Humanoid")

		if HumanoidObject ~= nil then
			HumanoidObject.DisplayDistanceType = Enum.HumanoidDisplayDistanceType.None
		end

		Humanoid:set(HumanoidObject)

		if HumanoidObject == nil then
			Scope:doCleanup()
		end
	end

	UpdateHead()
	UpdateHumanoid()

	Character.ChildAdded:Connect(function()
		UpdateHead()
		UpdateHumanoid()
	end)

	Scope:Nametag {
		Character = Character,
		Humanoid = Humanoid,
		Head = Head,
		Player = Player,
		Properties = SafeConfig.Properties,
		DisplayName = SafeConfig.DisplayName,
		Color = SafeConfig.Color,
	}
end

return Nametagger


local function DeepCopyTable(Table)
	local Copy = {}
	for Key, Value in pairs(Table) do
		if type(Value) == "table" then
			Copy[Key] = DeepCopyTable(Value)
		else
			Copy[Key] = Value
		end
	end
	return Copy
end

local function ReconcileTable(Target, Template)
	for Key, Value in pairs(Template) do
		if type(Key) == "string" then
			if Target[Key] == nil then
				if type(Value) == "table" then
					Target[Key] = DeepCopyTable(Value)
				else
					Target[Key] = Value
				end
			elseif type(Target[Key]) == "table" and type(Value) == "table" then
				ReconcileTable(Target[Key], Value)
			end
		end
	end
end

return ReconcileTable


export type Data = {
	XPMultipliers: { [string]: number },
}

local DataTemplate: Data = {
	XPMultipliers = {},
}

return DataTemplate


local Players = game:GetService("Players")

local RoRooms = script.Parent.Parent.Parent.Parent
local Signal = require(RoRooms.Parent.Signal)
local DataTemplate = require(script.DataTemplate)

export type SessionData = DataTemplate.Data
export type MetaData = {
	CreationTime: number,
}
export type SessionProfile = {
	Data: SessionData,
	MetaData: MetaData,
	UpdateData: (SessionProfile, SessionData) -> SessionData,
	DataUpdated: Signal.Signal<SessionData, SessionData>,
}

local SessionStore = {
	Profiles = {},
	ProfileCreated = Signal.new(),
	ProfileDeleted = Signal.new(),
}

function SessionStore:GetProfileSafe(Player: Player): SessionProfile
	return self:GetProfile(Player) or self:CreateProfile(Player)
end

function SessionStore:GetProfile(Player: Player): SessionProfile?
	return self.Profiles[Player.UserId]
end

function SessionStore:DeleteProfile(UserId: number)
	if self.Profiles[UserId] ~= nil then
		self.Profiles[UserId] = nil
		self.ProfileDeleted:Fire(UserId)
	end
end

function SessionStore:CreateProfile(Player: Player): SessionProfile?
	if self.Profiles[Player.UserId] ~= nil then
		return
	end

	local Profile = {
		Data = table.clone(DataTemplate),
		MetaData = {
			CreationTime = os.time(),
		},
		DataUpdated = Signal.new(),
	}

	function Profile:UpdateData(NewData: SessionData)
		local OldData = table.clone(self.Data)
		self.Data = NewData

		self.DataUpdated:Fire(OldData, NewData)
	end

	self.Profiles[Player.UserId] = Profile

	table.freeze(Profile)

	return Profile
end

Players.PlayerRemoving:Connect(function(Player)
	SessionStore:DeleteProfile(Player.UserId)
end)

return SessionStore


return function(array)
	local n = #array
	for i = 1, n - 1 do
		local j = math.random(i, n)
		array[i], array[j] = array[j], array[i]
	end
	return array
end


local Players = game:GetService("Players")
local RunService = game:GetService("RunService")

export type Holder = {
	Character: Model | nil,
	Humanoid: Humanoid | nil,
	Animator: Animator | nil,
	Player: Player | nil,
}
export type ToolComponent = {
	Tag: string,
	Instance: Tool,

	Holder: Holder?,
	Handle: BasePart?,

	ToolActivate: (table, Holder) -> ()?,
	ToolDeactivate: (table, Holder) -> ()?,
	ToolEquip: (table, Holder) -> ()?,
	ToolUnequip: (table, Holder) -> ()?,
}

local ToolComponent = {}

function ToolComponent._HandleActivate(Component: ToolComponent)
	if RunService:IsServer() or (Component.Holder.Player == Players.LocalPlayer) then
		if Component.ToolActivate then
			Component:ToolActivate(Component.Holder)
		end
	end
end

function ToolComponent._HandleDeactivate(Component: ToolComponent)
	if RunService:IsServer() or (Component.Holder.Player == Players.LocalPlayer) then
		if Component.ToolDeactivate then
			Component:ToolDeactivate(Component.Holder)
		end
	end
end

function ToolComponent._HandleEquip(Component: ToolComponent)
	local Character = Component.Instance:FindFirstAncestorOfClass("Model")
	if Character then
		local Humanoid = Character:FindFirstChildOfClass("Humanoid")
		if Humanoid then
			Component.Holder = {
				Character = Character,
				Humanoid = Humanoid,
				Animator = Humanoid:FindFirstChildOfClass("Animator"),
				Player = Players:GetPlayerFromCharacter(Character),
			}

			if RunService:IsServer() or (Component.Holder.Player == Players.LocalPlayer) then
				if Component.ToolEquip then
					Component:ToolEquip(Component.Holder)
				end
			end
		end
	end
end

function ToolComponent._HandleUnequip(Component: ToolComponent)
	if Component.Holder then
		local OldHolder = table.clone(Component.Holder)
		Component.Holder = nil

		if Component.ToolUnequip then
			Component:ToolUnequip(OldHolder)
		end
	end
end

function ToolComponent.Constructing(Component: ToolComponent)
	Component.Handle = Component.Instance:FindFirstChild("Handle")

	Component.Instance.Activated:Connect(function()
		ToolComponent._HandleActivate(Component)
	end)
	Component.Instance.Deactivated:Connect(function()
		ToolComponent._HandleDeactivate(Component)
	end)

	Component.Instance.Equipped:Connect(function()
		ToolComponent._HandleEquip(Component)
	end)
	Component.Instance.Unequipped:Connect(function()
		ToolComponent._HandleUnequip(Component)
	end)
end

function ToolComponent.ShouldConstruct(Component: ToolComponent)
	if not Component.Instance:IsA("Tool") then
		warn(Component.Tag, "has the ToolComponent extension but is not a Tool.")
		return false
	end

	return true
end

return ToolComponent


local Rorooms = require(script.Parent.Parent.Rorooms)
local Config = require(script.Parent.Config)

Rorooms.Config:Update(Config)

Rorooms:Start()


local ConfigUtils = require(script.Parent.ConfigUtils)

local Config = {
	Systems = {
		Profiles = {
			Roles = ConfigUtils:CompileRoles(script.Roles),
			DefaultRoleId = script.Roles:GetAttribute("DefaultRoleId"),
			AvatarEditorCallback = ConfigUtils:GetAvatarEditorCallback(script),
		},
		Music = {
			SoundGroup = script.Music,
		},
		Emotes = {
			Emotes = ConfigUtils:CompileEmotes(script.Emotes),
			Categories = {
				General = {
					LayoutOrder = 1,
				},
				Unlockable = {
					LayoutOrder = 2,
				},
				Robux = {
					LayoutOrder = 3,
				},
			},
		},
		Items = {
			Items = ConfigUtils:CompileItems(script.Items),
			Categories = {
				General = {
					LayoutOrder = 1,
				},
				Unlockable = {
					LayoutOrder = 2,
				},
				Robux = {
					LayoutOrder = 3,
				},
			},
		},
		Worlds = {
			FeaturedWorlds = {},
		},
	},
}

return Config


local ConfigUtils = {}

function ConfigUtils:IfString(Value: any): string?
	if typeof(Value) == "string" then
		return Value
	else
		return nil
	end
end

function ConfigUtils:IfNumber(Value: any): number?
	if typeof(Value) == "number" then
		return Value
	else
		return nil
	end
end

function ConfigUtils:GetColor(Instance: Instance)
	local LevelRequirement = Instance:GetAttribute("LevelRequirement")
	return (LevelRequirement and Color3.fromRGB(68, 55, 68))
end

function ConfigUtils:GetCategory(Instance: Instance): string?
	local LevelRequirement = Instance:GetAttribute("LevelRequirement")
	local GamepassRequirement = Instance:GetAttribute("GamepassRequirement")
	local Category = self:IfString(Instance:GetAttribute("Category"))

	if Category then
		return Category
	elseif LevelRequirement then
		return "Unlockable"
	elseif GamepassRequirement then
		return "Robux"
	else
		return nil
	end
end

function ConfigUtils:AnimationToEmote(Animation: Animation)
	return {
		DisplayName = Animation.Name,
		Animation = Animation,
		Emoji = self:IfString(Animation:GetAttribute("Emoji")),
		LevelRequirement = self:IfNumber(Animation:GetAttribute("LevelRequirement")),
		GamepassRequirement = Animation:GetAttribute("GamepassRequirement"),
		LabelIcon = self:IfString(Animation:GetAttribute("LabelIcon")),
		LabelText = self:IfString(Animation:GetAttribute("LabelText")),
		Color = self:GetColor(Animation),
		Category = self:GetCategory(Animation),
		AllowMovement = Animation:GetAttribute("AllowMovement"),
	}
end

function ConfigUtils:ToolToItem(Tool: Tool)
	return {
		DisplayName = Tool.Name,
		Tool = Tool,
		LevelRequirement = Tool:GetAttribute("LevelRequirement"),
		GamepassRequirement = Tool:GetAttribute("GamepassRequirement"),
		LabelIcon = Tool:GetAttribute("LabelIcon"),
		LabelText = Tool:GetAttribute("LabelText"),
		Color = self:GetColor(Tool),
		Category = self:GetCategory(Tool),
	}
end

function ConfigUtils:ConfigurationToRole(Configuration: Configuration)
	return {
		Name = Configuration.Name,
		Color = Configuration:GetAttribute("Color"),
		LayoutOrder = Configuration:GetAttribute("LayoutOrder"),
		LevelRequirement = Configuration:GetAttribute("LevelRequirement"),
		GamepassRequirement = Configuration:GetAttribute("GamepassRequirement"),
	}
end

function ConfigUtils:CompileEmotes(Folder: Folder)
	local Emotes = {}

	for _, EmoteAnimation in ipairs(Folder:GetChildren()) do
		local EmoteId = EmoteAnimation:GetAttribute("EmoteId") or EmoteAnimation.Name
		if typeof(EmoteId) == "string" then
			assert(Emotes[EmoteId] == nil, `Duplicate EmoteId {EmoteId} detected. Please use unique Ids.`)

			Emotes[EmoteId] = self:AnimationToEmote(EmoteAnimation)
		else
			assert(false, "EmoteIds must be of type string.")
		end
	end

	return Emotes
end

function ConfigUtils:CompileItems(Folder: Folder)
	local Items = {}

	for _, ItemTool in ipairs(Folder:GetChildren()) do
		local ItemId = ItemTool:GetAttribute("ItemId") or ItemTool.Name
		if typeof(ItemId) == "string" then
			assert(Items[ItemId] == nil, `Duplicate ItemId {ItemId} detected. Please use unique Ids.`)

			Items[ItemId] = self:ToolToItem(ItemTool)
		else
			assert(false, "ItemIds must be of type string.")
		end
	end

	return Items
end

function ConfigUtils:CompileRoles(Folder: Folder)
	local Roles = {}

	for _, RoleConfiguration in ipairs(Folder:GetChildren()) do
		local RoleId = RoleConfiguration:GetAttribute("RoleId") or RoleConfiguration.Name
		if typeof(RoleId) == "string" then
			assert(Roles[RoleId] == nil, `Duplicate RoleId {RoleId} detected. Please use unique Ids.`)

			Roles[RoleId] = self:ConfigurationToRole(RoleConfiguration)
		else
			assert(false, "RoleId must be of type string.")
		end
	end

	return Roles
end

function ConfigUtils:EnsureSoundGroup(SoundGroup: Instance?): SoundGroup
	if SoundGroup == nil then
		return Instance.new("SoundGroup")
	end
	if not SoundGroup:IsA("SoundGroup") then
		return Instance.new("SoundGroup")
	end

	return SoundGroup
end

function ConfigUtils:EnsureFolder(Name: string, Parent: Instance): Folder
	local Folder = Parent:FindFirstChild(Name)

	if Folder == nil then
		Folder = Instance.new("Folder")
	end

	return Folder
end

function ConfigUtils:GetAvatarEditorCallback(Parent: Instance): (() -> ())?
	local CallbackModule = Parent:FindFirstChild("AvatarEditorCallback")
	if CallbackModule and CallbackModule:IsA("ModuleScript") then
		return require(CallbackModule)
	else
		return nil
	end
end

return ConfigUtils


local SOURCE = script.Parent.SourceCode
local PACKAGES = script.Parent.Parent
local SHARED = SOURCE.Shared
local CLIENT = SOURCE.Client
local DEFAULT_CONTROLLERS = {
	"UIController",
	"BloxstrapController",
	"ComponentsController",
	"DefaultsController",
	"UpdatesController",
	"PreloadingController",
}

local Config = require(script.Parent.Config)
local Knit = require(PACKAGES.Knit)
local Loader = require(PACKAGES.Loader)
local FindFeatureFromModule = require(SHARED.FindFeatureFromModule)
local Prompts = require(SOURCE.Client.UI.States.Prompts)

local RoroomsClient = {
	Started = false,
	Config = Config,
}

function RoroomsClient:Start()
	assert(not self.Started, "Rorooms already started.")
	self.Started = true

	Loader.LoadDescendants(CLIENT, function(Descendant)
		if Descendant:IsA("ModuleScript") and Descendant.Name:match("Controller$") ~= nil then
			local Feature = FindFeatureFromModule(Descendant)

			if
				table.find(DEFAULT_CONTROLLERS, Descendant.Name)
				or (Feature and Config.Config.Systems[Feature].Enabled == true)
			then
				return Knit.CreateController(require(Descendant))
			else
				return false
			end
		else
			return false
		end
	end)

	Knit.Start()
end

function RoroomsClient:Configure(Configuration: Config.Config)
	assert(not self.Started, "You cannot configure Rorooms after starting it.")

	Config:Update(Configuration)
end

function RoroomsClient:Prompt(Prompt)
	Prompts:PushPrompt(Prompt)
end

return RoroomsClient


export type CategoryName = string
export type Category = {
	DisplayName: string?,
	Icon: string,
	LayoutOrder: number?,
	Color: Color3?,
}
export type Categories = {
	[CategoryName]: Category,
}
export type Item = {
	Enabled: boolean?,
	Tool: Tool,
	DisplayName: string?,
	Category: string?,
	Color: Color3?,
	LayoutOrder: number?,
	LabelIcon: string?,
	LevelRequirement: number?,
	GamepassRequirement: number?,
	CallbackRequirement: () -> ()?,
}
export type Emote = {
	Enabled: boolean?,
	Animation: Animation,
	DisplayName: string?,
	Category: string?,
	Emoji: string?,
	Color: Color3?,
	LayoutOrder: number?,
	LabelIcon: string?,
	LevelRequirement: number?,
	GamepassRequirement: number?,
	CallbackRequirement: () -> ()?,
	AllowMovement: boolean?,
}
export type PlaceId = number
export type Role = {
	Name: string,
	Color: Color3?,
	LayoutOrder: number?,
	CallbackRequirement: ((Player, string, Role) -> boolean)?,
	LevelRequirement: number?,
	GamepassRequirement: number?,
}
export type Config = {
	Systems: {
		Profiles: {
			Enabled: boolean?,
			NicknameCharacterLimit: number?,
			BioCharacterLimit: number?,
			AvatarEditorCallback: () -> ()?,
			Roles: {
				[string]: Role,
			}?,
			DefaultRoleId: string?,
		}?,
		Items: {
			Enabled: boolean?,
			MaxItemsEquippable: number?,
			Categories: Categories?,
			Items: {
				[string]: Item?,
			}?,
		}?,
		Emotes: {
			Enabled: boolean?,
			Categories: Categories?,
			Emotes: {
				[string]: Emote,
			}?,
		}?,
		Music: {
			Enabled: boolean?,
			SoundGroup: SoundGroup?,
		}?,
		Worlds: {
			Enabled: boolean?,
			DiscoveryEnabled: boolean?,
			FeaturedWorlds: { [number]: PlaceId }?,
			RegistryRepository: string?,
		}?,
		Friends: {
			Enabled: boolean?,
		}?,
		Settings: {
			Enabled: boolean?,
		}?,
		VR: {
			Enabled: boolean?,
		}?,
		UI: {
			OnyxUITheme: { [string]: any }?,
		}?,
		Leveling: {
			Enabled: boolean?,
			XPPerMinute: number?,
			BaseLevelUpXP: number?,
		}?,
	}?,
	General: {
		AssetId: number?,
	}?,
}

local CONFIG_TEMPLATE: Config = {
	Systems = {
		Profiles = {
			Enabled = true,
			NicknameCharacterLimit = 20,
			BioCharacterLimit = 80,
			Roles = {},
		},
		Items = {
			Enabled = true,
			MaxItemsEquippable = 5,
			Categories = {},
			Items = {},
		},
		Emotes = {
			Enabled = true,
			Categories = {},
			Emotes = {},
		},
		Music = {
			Enabled = true,
			SoundGroup = Instance.new("SoundGroup"),
		},
		Worlds = {
			Enabled = true,
			DiscoveryEnabled = true,
			FeaturedWorlds = {},
			RegistryRepository = "Rorooms/Worlds",
		},
		Friends = {
			Enabled = true,
		},
		Settings = {
			Enabled = true,
		},
		VR = {
			Enabled = true,
		},
		Leveling = {
			Enabled = true,
			XPPerMinute = 10,
			BaseLevelUpXP = 10,
		},
		UI = {
			OnyxUITheme = {},
		},
	},
	General = {
		AssetId = 131892669922226,
	},
}

local function DeepCopyTable(Table)
	local Copy = {}
	for Key, Value in pairs(Table) do
		if type(Value) == "table" then
			Copy[Key] = DeepCopyTable(Value)
		else
			Copy[Key] = Value
		end
	end
	return Copy
end

local function ReconcileTable(Target, Template)
	for Key, Value in pairs(Template) do
		if type(Target[Key]) == "table" and type(Value) == "table" then
			ReconcileTable(Target[Key], Value)
		else
			if type(Value) == "table" then
				Target[Key] = DeepCopyTable(Value)
			else
				Target[Key] = Value
			end
		end
	end
end

local Config = {
	Config = table.clone(CONFIG_TEMPLATE),
}

function Config:Update(ConfigModifier: Config)
	ReconcileTable(Config.Config, ConfigModifier)
end

return Config


local SOURCE = script.Parent.SourceCode
local PACKAGES = script.Parent.Parent
local SERVER = SOURCE.Server
local DEFAULT_SERVICES = { "PlayerDataStoreService", "CharacterDefaultsService", "ComponentsService", "UpdatesService" }

local Config = require(script.Parent.Config)
local Knit = require(PACKAGES.Knit)
local Loader = require(PACKAGES.Loader)
local FindFeatureFromModule = require(SOURCE.Shared.FindFeatureFromModule)

local RoroomsServer = {
	Started = false,
	Config = Config,
}

function RoroomsServer:Start()
	assert(not self.Started, "Rorooms already started.")
	self.Started = true

	Loader.LoadDescendants(SERVER, function(Descendant)
		if Descendant:IsA("ModuleScript") and Descendant.Name:match("Service$") ~= nil then
			local Feature = FindFeatureFromModule(Descendant)
			if
				table.find(DEFAULT_SERVICES, Descendant.Name)
				or (Feature and Config.Config.Systems[Feature].Enabled == true)
			then
				return Knit.CreateService(require(Descendant))
			else
				return false
			end
		else
			return false
		end
	end)

	Knit.Start()
end

function RoroomsServer:Configure(Configuration: Config.Config)
	assert(not self.Started, "You cannot configure Rorooms after starting it.")

	Config:Update(Configuration)
end

return RoroomsServer


local MarketplaceService = game:GetService("MarketplaceService")
local RunService = game:GetService("RunService")

local Rorooms = script.Parent.Parent.Parent.Parent
local Future = require(script.Parent.Parent.Parent.Parent.Parent.Future)
local BloxstrapRPC = require(Rorooms.Parent.BloxstrapRPC)

local BloxstrapController = {
	Name = "BloxstrapController",
}

function BloxstrapController:UpdateRichPresence()
	Future.Try(function(PlaceId: number)
		return MarketplaceService:GetProductInfo(PlaceId, Enum.InfoType.Asset)
	end, game.PlaceId):After(function(Success, GameInfo)
		if Success == true then
			BloxstrapRPC.SetRichPresence({
				details = `In {GameInfo.Name}`,
				state = `by {GameInfo.Creator.Name} {(GameInfo.Creator.HasVerifiedBadge and "☑") or ""}`,
				startTime = os.time(),
				largeImage = {
					assetId = GameInfo.IconImageAssetId,
					hoverText = GameInfo.Name,
				},
				smallImage = {
					assetId = 15885967339,
					hoverText = "Rorooms",
				},
			})
		end
	end)
end

function BloxstrapController:KnitStart()
	if not RunService:IsStudio() then
		self:UpdateRichPresence()
	end
end

return BloxstrapController


local Rorooms = script.Parent.Parent.Parent.Parent
local Component = require(Rorooms.Parent.Component)
local ToolComponent = require(Rorooms.SourceCode.Shared.ExtPackages.ToolComponent)
local AnimatedComponent = require(Rorooms.SourceCode.Shared.ExtPackages.AnimatedComponent)

local AnimatedItemComponent = Component.new {
	Tag = `RR_{string.gsub(script.Name, "Component$", "")}`,
	Extensions = { ToolComponent, AnimatedComponent },

	Animations = {},
}

function AnimatedItemComponent:SetAnimation(AnimationNumber: number?)
	if AnimationNumber ~= nil then
		local Animation = self.Animations[AnimationNumber]
		if Animation and Animation.AnimationId then
			self:StopAnimations()
			self:PlayAnimation(Animation.AnimationId)
		else
			self:StopAnimations()
		end
	else
		self:StopAnimations()
	end

	self.Instance:SetAttribute("RR_ActiveItemAnimation", AnimationNumber)
end

function AnimatedItemComponent:CycleAnimation()
	local ActiveAnimation = self.Instance:GetAttribute("RR_ActiveItemAnimation") or 0
	if ActiveAnimation == #self.Animations then
		ActiveAnimation = 0
	end
	ActiveAnimation += 1

	self:SetAnimation(ActiveAnimation)
end

function AnimatedItemComponent:ToolActivate()
	self:CycleAnimation()
end

function AnimatedItemComponent:ToolEquip(Holder: ToolComponent.Holder)
	self:SetAnimator(Holder.Animator)
	self:SetAnimation(1)
end

function AnimatedItemComponent:ToolUnequip()
	self:StopAnimations()
end

function AnimatedItemComponent:_RegisterAnimations()
	local Animations: { Animation } = {}

	local ItemAnimationsFolder = self.Instance:FindFirstChild("RR_ItemAnimations")
	if ItemAnimationsFolder ~= nil then
		for Count = 1, #ItemAnimationsFolder:GetChildren() do
			local Animation = ItemAnimationsFolder:FindFirstChild(Count)
			if Animation and Animation:IsA("Animation") then
				table.insert(Animations, Animation)
			end
		end
	end

	table.sort(Animations, function(AnimationA: Animation, AnimationB: Animation)
		return tonumber(AnimationA.Name) < tonumber(AnimationB.Name)
	end)

	self.Animations = Animations
	return Animations
end

function AnimatedItemComponent:Construct()
	local Animations = self:_RegisterAnimations()
	if #Animations == 0 then
		warn("RR_ItemAnimations are incorrectly set up for RR_AnimatedItem", self.Instance)
	end
end

return AnimatedItemComponent


local Players = game:GetService("Players")

local Rorooms = script.Parent.Parent.Parent.Parent
local Component = require(Rorooms.Parent.Component)
local Fusion = require(Rorooms.Parent.Fusion)
local AttributeBind = require(Rorooms.SourceCode.Shared.ExtPackages.AttributeBind)
local Trove = require(Rorooms.Parent.Trove)

local Peek = Fusion.peek

local AnimatedSeatComponent = Component.new {
	Tag = "RR_AnimatedSeat",
}

function AnimatedSeatComponent:UpdateOccupant()
	if self.LastAnimationTrack then
		self.LastAnimationTrack:Stop()
		self.LastAnimationTrack = nil
	end

	if self.Instance.Occupant then
		local Humanoid = self.Instance.Occupant
		local Character = Humanoid:FindFirstAncestorOfClass("Model")

		if Character then
			self.Occupant:set(Players:GetPlayerFromCharacter(Character))
		end

		if Peek(self.Occupant) == Players.LocalPlayer then
			local Animator = Humanoid:FindFirstChild("Animator")
			if Animator then
				local AnimationTrack = Animator:LoadAnimation(self.Animation)
				AnimationTrack:Play()
				self.LastAnimationTrack = AnimationTrack
			end
		end
	else
		self.Occupant:set(nil)
	end
end

function AnimatedSeatComponent:OnPromptTriggered(Player: Player)
	if Player == Players.LocalPlayer then
		local Char = Players.LocalPlayer.Character
		if Char then
			local Humanoid = Char:FindFirstChildOfClass("Humanoid")
			if Humanoid then
				self.Instance:Sit(Humanoid)
			end
		end
	end
end

function AnimatedSeatComponent:GetProximityPrompt()
	local ProximityPrompt = self.Instance:FindFirstChild("RR_SitPrompt")
	if not ProximityPrompt then
		ProximityPrompt = self.Scope:New "ProximityPrompt" {
			Name = "RR_SitPrompt",
			Parent = self.Instance,
			ActionText = "",
			RequiresLineOfSight = false,
			MaxActivationDistance = 7.5,
		}
	end

	self.Scope:Hydrate(ProximityPrompt) {
		Enabled = self.Scope:Computed(function(Use)
			return Use(self.Occupant) == nil
		end),
	}

	return ProximityPrompt
end

function AnimatedSeatComponent:Start()
	self.Instance.Disabled = not Peek(self.SitOnTouch)

	if Peek(self.PromptToSit) then
		self.ProximityPrompt = self:GetProximityPrompt()

		self.ProximityPrompt.Triggered:Connect(function(Player: Player)
			self:OnPromptTriggered(Player)
		end)
	end

	self.Instance:GetPropertyChangedSignal("Occupant"):Connect(function()
		self:UpdateOccupant()
	end)
end

function AnimatedSeatComponent:Construct()
	self.Scope = Fusion.scoped(Fusion)
	self.Trove = Trove.new()

	self.PromptToSit = self.Scope:Value()
	self.Trove:Add(AttributeBind.Bind(self.Instance, "RR_PromptToSit", true)):Observe(function(Value)
		self.PromptToSit:set(Value)
	end)

	self.SitOnTouch = self.Scope:Value()
	self.Trove:Add(AttributeBind.Bind(self.Instance, "RR_PromptToSit", true)):Observe(function(Value)
		self.SitOnTouch:set(Value)
	end)

	self.Occupant = self.Scope:Value(nil)

	if not self.Instance:IsA("Seat") then
		warn("AnimatedSeat must be a Seat object", self.Instance)
		return
	end

	local Animation = self.Instance:WaitForChild("Animation")
	if Animation then
		self.Animation = Animation
	else
		warn("No Animation inside AnimatedSeat", self.Instance)
		return
	end
end

function AnimatedSeatComponent:Stop()
	self.Trove:Destroy()
end

return AnimatedSeatComponent


local ComponentsController = {
	Name = script.Name,
}

function ComponentsController:KnitStart()
	for _, Module in ipairs(script.Parent:GetChildren()) do
		if Module:IsA("ModuleScript") and Module ~= script then
			require(Module)
		end
	end
end

return ComponentsController


local Players = game:GetService("Players")

local Rorooms = script.Parent.Parent.Parent.Parent
local Component = require(Rorooms.Parent.Component)
local Fusion = require(Rorooms.Parent.Fusion)
local States = require(Rorooms.SourceCode.Client.UI.States)
local ItemsController = require(Rorooms.SourceCode.Client.Items.ItemsController)
local Config = require(Rorooms.Config).Config
local AttributeBind = require(Rorooms.SourceCode.Shared.ExtPackages.AttributeBind)
local Trove = require(Rorooms.Parent.Trove)

local Peek = Fusion.peek

local ItemGiverComponent = Component.new {
	Tag = "RR_ItemGiver",
}

function ItemGiverComponent:GiveItem(Player: Player)
	if Player == Players.LocalPlayer then
		ItemsController:ToggleEquipItem(Peek(self.ItemId), true)
	end
end

function ItemGiverComponent:GetProximityPrompt()
	local ProximityPrompt = self.Instance:FindFirstChild("RR_ItemGiverPrompt")
	if not ProximityPrompt then
		ProximityPrompt = self.Scope:New "ProximityPrompt" {
			Name = "RR_ItemGiverPrompt",
			Parent = self.Instance,
			ActionText = "",
			RequiresLineOfSight = false,
			MaxActivationDistance = 7.5,
		}
	end

	self.Scope:Hydrate(ProximityPrompt) {
		Enabled = self.Scope:Computed(function(Use)
			return Use(self.Item) ~= nil
		end),
		ActionText = self.Scope:Computed(function(Use)
			if Use(self.Item) then
				if Use(self.Equipped) then
					return "Unequip"
				else
					return "Equip"
				end
			else
				return Use(self.ItemId)
			end
		end),
		ObjectText = self.Scope:Computed(function(Use)
			local ItemValue = Use(self.Item)
			local ItemIdValue = Use(self.ItemId)

			if ItemValue and ItemValue.Name then
				return ItemValue.Name
			elseif ItemIdValue then
				return ItemIdValue
			else
				return "Invalid item"
			end
		end),
	}

	return ProximityPrompt
end

function ItemGiverComponent:Start()
	self.ProximityPrompt = self:GetProximityPrompt()

	self.ProximityPrompt.Triggered:Connect(function(Player: Player)
		self:GiveItem(Player)
	end)
end

function ItemGiverComponent:Construct()
	self.Scope = Fusion.scoped(Fusion)
	self.Trove = Trove.new()

	self.ItemId = self.Scope:Value()
	self.Trove:Add(AttributeBind.Bind(self.Instance, "RR_ItemId")):Observe(function(Value)
		self.ItemId:set(Value)
	end)

	self.Item = self.Scope:Computed(function(Use)
		return Config.Systems.Items.Items[Use(self.ItemId)]
	end)
	self.Equipped = self.Scope:Computed(function(Use)
		return table.find(Use(States.Items.Equipped), Use(self.ItemId)) ~= nil
	end)

	if not Peek(self.ItemId) then
		warn("No RR_ItemId attribute defined for ItemGiver", self.Instance)
	end
	if not Peek(self.Item) then
		warn("Could not find item from RR_ItemId", Peek(self.ItemId), self.Instance)
	end
end

function ItemGiverComponent:Stop()
	self.Trove:Destroy()
end

return ItemGiverComponent


local Players = game:GetService("Players")
local Rorooms = script.Parent.Parent.Parent.Parent
local Component = require(Rorooms.Parent.Component)
local Fusion = require(Rorooms.Parent.Fusion)
local States = require(Rorooms.SourceCode.Client.UI.States)
local AttributeBind = require(Rorooms.SourceCode.Shared.ExtPackages.AttributeBind)
local Trove = require(Rorooms.Parent.Trove)
local GamepassesHandler = require(Rorooms.SourceCode.Shared.ExtPackages.GamepassesHandler)

local Peek = Fusion.peek

local LockedDoorComponent = Component.new {
	Tag = "RR_LockedDoor",
}

function LockedDoorComponent:Start()
	self.Scope:Observer(States.Leveling.Level):onChange(function()
		self:UpdateLock()
	end)
	self.Scope:Observer(self.LevelRequirement):onChange(function()
		self:UpdateLock()
	end)
	self.Scope:Observer(self.GamepassRequirement):onChange(function()
		self:UpdateLock()
	end)

	GamepassesHandler.PlayerPurchasedGamepass:Connect(function(Player: Player, GamepassId: number)
		if (Player == Players.LocalPlayer) and (GamepassId == Peek(self.GamepassRequirement)) then
			self:UpdateLock()
		end
	end)

	self:UpdateLock()
end

function LockedDoorComponent:UpdateLock()
	self.Instance.CanCollide = not self:_IsPlayerAccepted(Players.LocalPlayer)
end

function LockedDoorComponent:_IsPlayerAccepted(Player: Player)
	local LevelRequirementValue = Peek(self.LevelRequirement)
	local GamepassRequirementValue = Peek(self.GamepassRequirement)
	local PlayerLevelValue = Peek(States.Leveling.Level)

	if PlayerLevelValue < LevelRequirementValue then
		return false
	end

	if GamepassRequirementValue ~= nil then
		local OwnsGamepass = GamepassesHandler:PlayerOwnsGamepass(Player, GamepassRequirementValue)

		if not OwnsGamepass then
			return false
		end
	end

	return true
end

function LockedDoorComponent:Construct()
	self.Scope = Fusion.scoped(Fusion)
	self.Trove = Trove.new()

	if not self.Instance:IsA("BasePart") then
		warn("LevelDoor must be a BasePart object", self.Instance)
		return
	end

	self.LevelRequirement = self.Scope:Value()
	self.Trove:Add(AttributeBind.Bind(self.Instance, "RR_LevelRequirement", 0)):Observe(function(Value)
		self.LevelRequirement:set(Value)
	end)

	self.GamepassRequirement = self.Scope:Value()
	self.Trove:Add(AttributeBind.Bind(self.Instance, "RR_GamepassRequirement", nil)):Observe(function(Value)
		self.GamepassRequirement:set(Value)
	end)
end

function LockedDoorComponent:Stop()
	self.Trove:Destroy()
	self.Scope:doCleanup()
end

return LockedDoorComponent


local Players = game:GetService("Players")

local Rorooms = script.Parent.Parent.Parent.Parent
local AttributeBind = require(Rorooms.SourceCode.Shared.ExtPackages.AttributeBind)
local Component = require(Rorooms.Parent.Component)
local States = require(Rorooms.SourceCode.Client.UI.States)
local Fusion = require(Rorooms.Parent.Fusion)
local Trove = require(Rorooms.Parent.Trove)

local Peek = Fusion.peek

local DEBOUNCE_DURATION = 1

local WorldTeleporterComponent = Component.new {
	Tag = "RR_WorldTeleporter",
}

function WorldTeleporterComponent:_PromptTeleport()
	if (os.time() - self.DebounceTimestamp) >= DEBOUNCE_DURATION then
		self.DebounceTimestamp = os.time()
	else
		return
	end

	local PlaceId = Peek(self.PlaceId)

	if PlaceId then
		States.WorldPageMenu.PlaceId:set(PlaceId)
		States.Menus.CurrentMenu:set("WorldPageMenu")
	end
end

function WorldTeleporterComponent:Start()
	self.Instance.Touched:Connect(function(TouchedPart: BasePart)
		local Character = TouchedPart:FindFirstAncestorOfClass("Model")
		if Character then
			local Player = Players:GetPlayerFromCharacter(Character)
			if Player == Players.LocalPlayer then
				if #Peek(States.Prompts) == 0 then
					self:_PromptTeleport(Peek(self.PlaceId))
				end
			end
		end
	end)
end

function WorldTeleporterComponent:Construct()
	self.Scope = Fusion.scoped(Fusion)
	self.Trove = Trove.new()

	self.PlaceId = self.Scope:Value()
	self.Trove:Add(AttributeBind.Bind(self.Instance, "RR_PlaceId")):Observe(function(Value)
		self.PlaceId:set(Value)
	end)

	self.DebounceTimestamp = 0

	if not self.Instance:IsA("BasePart") then
		warn("WorldTeleporter must be a BasePart object", self.Instance)
		return
	end
	if not Peek(self.PlaceId) then
		warn("No RR_PlaceId attribute defined for WorldTeleporter", self.Instance)
		return
	end
end

function WorldTeleporterComponent:Stop()
	self.Trove:Destroy()
end

return WorldTeleporterComponent


local Rorooms = script.Parent.Parent.Parent.Parent
local CoreThemer = require(Rorooms.SourceCode.Shared.ExtPackages.CoreThemer)
local NeoHotbar = require(Rorooms.Parent.NeoHotbar)

local DefaultsController = {
	Name = script.Name,
}

function DefaultsController:KnitStart()
	CoreThemer:SetDefaults()
end

NeoHotbar:Start()

return DefaultsController


local Rorooms = script.Parent.Parent.Parent.Parent
local Knit = require(Rorooms.Parent.Knit)
local Topbar = require(Rorooms.SourceCode.Client.UI.States.Topbar)
local Prompts = require(Rorooms.SourceCode.Client.UI.States.Prompts)
local UIController = require(Rorooms.SourceCode.Client.UI.UIController)

local EmotesService

local EmotesController = {
	Name = "EmotesController",
}

function EmotesController:PlayEmote(EmoteId: string)
	EmotesService:PlayEmote(EmoteId):andThen(function(FailureReason: string)
		if typeof(FailureReason) == "string" then
			Prompts:PushPrompt({
				Title = "Failure",
				Text = FailureReason,
				Buttons = {
					{
						Content = { "Close" },
					},
				},
			})
		end
	end)
end

function EmotesController:KnitStart()
	UIController = Knit.GetController("UIController")
	EmotesService = Knit.GetService("EmotesService")

	UIController:MountUI(require(Rorooms.SourceCode.Client.UI.ScreenGuis.EmotesMenu))

	Topbar:AddTopbarButton("Emotes", Topbar.NativeButtons.Emotes)
end

return EmotesController


local Players = game:GetService("Players")

local Rorooms = script.Parent.Parent.Parent.Parent
local Future = require(script.Parent.Parent.Parent.Parent.Parent.Future)
local UIController = require(Rorooms.SourceCode.Client.UI.UIController)
local States = require(Rorooms.SourceCode.Client.UI.States)
local Knit = require(Rorooms.Parent.Knit)
local Topbar = require(Rorooms.SourceCode.Client.UI.States.Topbar)
local Fusion = require(Rorooms.Parent.Fusion)

local Peek = Fusion.peek

local WorldRegistryService

local FRIENDS_ONLINE_CACHE_PERIOD = 30

local FriendsController = {
	Name = "FriendsController",

	FriendsOnline = {},
	FriendsInRorooms = {},
	FriendsNotInGame = {},
	FriendsOnlineLastUpdated = os.time(),
}

function FriendsController:UpdateFriendsInRorooms()
	local FriendsInRorooms = {}
	local FriendsNotInRorooms = {}

	for _, Friend in ipairs(self.FriendsOnline) do
		local WorldIsRegistered = false
		if Friend.PlaceId then
			local Success, Result = WorldRegistryService:IsWorldRegistered(Friend.PlaceId):await()
			if Success then
				WorldIsRegistered = Result
			end
		end
		if WorldIsRegistered then
			table.insert(FriendsInRorooms, Friend)
		else
			table.insert(FriendsNotInRorooms, Friend)
		end
	end

	self.FriendsInRorooms = FriendsInRorooms
	States.Friends.InRorooms:set(FriendsInRorooms)

	self.FriendsNotInRorooms = FriendsNotInRorooms
	States.Friends.NotInRorooms:set(FriendsNotInRorooms)
end

function FriendsController:UpdateFriends(ForceUpdate: boolean?)
	if (not ForceUpdate) and ((os.time() - self.FriendsOnlineLastUpdated) <= FRIENDS_ONLINE_CACHE_PERIOD) then
		return
	end

	return Future.Try(function()
		return Players.LocalPlayer:GetFriendsOnline()
	end):After(function(Success, Result)
		if Success then
			self.FriendsOnline = Result
			self.FriendsOnlineLastUpdated = os.time()
			States.Friends.Online:set(Result)

			self:UpdateFriendsInRorooms()
		else
			warn(Result)
		end
	end)
end

function FriendsController:KnitStart()
	WorldRegistryService = Knit.GetService("WorldRegistryService")

	UIController:MountUI(require(Rorooms.SourceCode.Client.UI.ScreenGuis.FriendsMenu))
	Topbar:AddTopbarButton("Friends", Topbar.NativeButtons.Friends)

	self:UpdateFriends(true)

	while task.wait(1) do
		local SecondsSinceUpdated = os.time() - self.FriendsOnlineLastUpdated
		if
			(SecondsSinceUpdated > FRIENDS_ONLINE_CACHE_PERIOD) and (Peek(States.Menus.CurrentMenu) == "FriendsMenu")
		then
			self:UpdateFriends()
		end
	end
end

return FriendsController


local Players = game:GetService("Players")

local Rorooms = script.Parent.Parent.Parent.Parent
local Knit = require(Rorooms.Parent.Knit)
local Fusion = require(Rorooms.Parent.Fusion)
local Signal = require(Rorooms.Parent.Signal)
local NeoHotbar = require(Rorooms.Parent.NeoHotbar)
local States = require(Rorooms.SourceCode.Client.UI.States)
local Prompts = require(Rorooms.SourceCode.Client.UI.States.Prompts)
local UIController = require(Rorooms.SourceCode.Client.UI.UIController)
local Config = require(Rorooms.Config).Config
local Assets = require(Rorooms.SourceCode.Shared.Assets)

local Peek = Fusion.peek

local ItemsService

local ItemsController = {
	Name = "ItemsController",

	Scope = Fusion.scoped(Fusion),
	EquippedItemsUpdated = Signal.new(),
}

function ItemsController:ToggleEquipItem(ItemId: string, Hold: boolean?)
	ItemsService:ToggleEquipItem(ItemId, Hold):andThen(function(Equipped: boolean, FailureReason: string)
		if not Equipped and FailureReason then
			Prompts:PushPrompt({
				Title = "Failure",
				Text = FailureReason,
				Buttons = {
					{
						Content = { "Close" },
					},
				},
			})
		end
	end)
end

function ItemsController:_UpdateEquippedItems()
	local Backpack = Players.LocalPlayer:FindFirstChild("Backpack")
	local function ScanDirectory(Directory: Instance)
		if not Directory then
			return
		end
		for _, Child in ipairs(Directory:GetChildren()) do
			local ItemId = Child:GetAttribute("RR_ItemId")
			if Child:IsA("Tool") and Config.Systems.Items.Items[ItemId] then
				table.insert(self.EquippedItems, ItemId)
			end
		end
	end
	self.EquippedItems = {}
	ScanDirectory(Players.LocalPlayer.Character)
	ScanDirectory(Backpack)
	self.EquippedItemsUpdated:Fire(self.EquippedItems)
end

function ItemsController:_AddNeoHotbarButton()
	if NeoHotbar:FindCustomButton("ItemsMenuButton") then
		NeoHotbar:RemoveCustomButton("ItemsMenuButton")
	end

	NeoHotbar:AddCustomButton("ItemsMenuButton", Assets.Icons.General.Toolbox, function()
		if not Peek(States.Menus.ItemsMenu.Open) then
			States.Menus.ItemsMenu.Open:set(true)
		else
			States.Menus.ItemsMenu.Open:set(false)
		end
	end)
end

function ItemsController:_HandleCharacter(Character: Model)
	self:_UpdateEquippedItems()

	Character.ChildAdded:Connect(function()
		self:_UpdateEquippedItems()
	end)
	Character.ChildRemoved:Connect(function()
		self:_UpdateEquippedItems()
	end)

	local Backpack = Players.LocalPlayer:WaitForChild("Backpack")
	Backpack.ChildAdded:Connect(function()
		self:_UpdateEquippedItems()
	end)
	Backpack.ChildRemoved:Connect(function()
		self:_UpdateEquippedItems()
	end)
end

function ItemsController:KnitStart()
	ItemsService = Knit.GetService("ItemsService")
	UIController = Knit.GetController("UIController")

	Players.LocalPlayer.CharacterAdded:Connect(function(Character)
		self:_HandleCharacter(Character)
	end)
	local ExistingCharacter = Players.LocalPlayer.Character
	if ExistingCharacter ~= nil then
		self:_HandleCharacter(ExistingCharacter)
	end

	UIController:MountUI(require(Rorooms.SourceCode.Client.UI.ScreenGuis.ItemsMenu))

	self:_AddNeoHotbarButton()
end

ItemsController.EquippedItems = ItemsController.Scope:Value({})

return ItemsController


local SoundService = game:GetService("SoundService")

local Rorooms = script.Parent.Parent.Parent.Parent
local Knit = require(Rorooms.Parent.Knit)
local States = require(Rorooms.SourceCode.Client.UI.States)
local Fusion = require(Rorooms.Parent.Fusion)
local Config = require(Rorooms.Config).Config

local Peek = Fusion.peek

local MusicService

local MusicController = {
	Name = "MusicController",

	Scope = Fusion.scoped(Fusion),
	Muted = false,
}

function MusicController:SetMuted(Muted: boolean)
	self.Muted = Muted
	self.SoundGroup.Volume = (Muted and 0) or 1
end

function MusicController:StopAllSongs()
	for _, Song in ipairs(self.SoundGroup:GetChildren()) do
		Song:Stop()
	end
end

function MusicController:KnitStart()
	MusicService = Knit.GetService("MusicService")

	self.Scope:Observer(States.UserSettings.MuteMusic):onChange(function()
		self:SetMuted(Peek(States.UserSettings.MuteMusic))
	end)

	self.SoundGroup = Config.Systems.Music.SoundGroup
	self.SoundGroup.Parent = SoundService

	MusicService.CurrentSong:Observe(function(CurrentSong)
		if not CurrentSong then
			return
		end
		self:StopAllSongs()
		self.CurrentSong = CurrentSong
		CurrentSong.Parent = self.SoundGroup
		CurrentSong.SoundGroup = self.SoundGroup
		CurrentSong:Play()
	end)
end

return MusicController


local ContentProvider = game:GetService("ContentProvider")

local Rorooms = script.Parent.Parent.Parent.Parent
local Assets = require(Rorooms.SourceCode.Shared.Assets)

export type ContentIds = { string }
export type ContentIdTree = string | { [string]: ContentIdTree }

local PreloadingController = {
	Name = script.Name,
}

function PreloadingController:PreloadTree(Tree: ContentIdTree)
	ContentProvider:PreloadAsync(self:CompileContentTree(Tree))
end

function PreloadingController:CompileContentTree(Tree: ContentIdTree)
	local ContentIds: ContentIds = {}

	for _, Entry in pairs(Tree) do
		if typeof(Entry) == "table" then
			for _, SubEntry in ipairs(self:CompileContentTree(Entry)) do
				table.insert(ContentIds, SubEntry)
			end
		elseif typeof(Entry) == "string" then
			table.insert(ContentIds, Entry)
		end
	end

	return ContentIds
end

function PreloadingController:KnitStart()
	self:PreloadTree(Assets.Icons)
end

function PreloadingController:KnitInit() end

return PreloadingController


local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
local UserInputService = game:GetService("UserInputService")

local Rorooms = script.Parent.Parent.Parent.Parent
local Signal = require(Rorooms.Parent.Signal)

local AvatarSelector = {
	AvatarSelected = Signal.new(),
	AvatarDeselected = Signal.new(),
	AvatarHovered = Signal.new(),
	_AvatarSelecting = nil,
	_AvatarHovering = nil,
	_AvatarSelectionHighlight = Instance.new("Highlight"),
	_AvatarSelectionConnection = nil,
}

AvatarSelector._AvatarSelectionHighlight.Parent = Players.LocalPlayer:WaitForChild("PlayerGui")
AvatarSelector._AvatarSelectionHighlight.FillTransparency = 1
AvatarSelector._AvatarSelectionHighlight.OutlineTransparency = 0.25

function AvatarSelector:Start()
	self:Stop()

	local Mouse = Players.LocalPlayer:GetMouse()

	self._AvatarSelectionConnection = RunService.RenderStepped:Connect(function()
		self._AvatarHovering = nil
		self._AvatarSelectionHighlight.Adornee = nil

		local Target = Mouse.Target
		if Target then
			local Character = Target:FindFirstAncestorOfClass("Model")
			if Character then
				local Player = Players:GetPlayerFromCharacter(Character)
				if Player then
					self._AvatarHovering = Character
					self._AvatarSelectionHighlight.Adornee = Character
					self.AvatarHovered:Fire(Character)
				end
			end
		end
	end)

	UserInputService.InputEnded:Connect(function(Input, AlreadyProcessed)
		local AcceptableInputTypes = { Enum.UserInputType.MouseButton1, Enum.UserInputType.Touch }
		if (not AlreadyProcessed) and table.find(AcceptableInputTypes, Input.UserInputType) then
			if Mouse.Target then
				local Character = Mouse.Target:FindFirstAncestorOfClass("Model")
				if Character then
					local Player = Players:GetPlayerFromCharacter(Character)
					if (Player ~= nil) and (Player ~= Players.LocalPlayer) then
						self._AvatarSelecting = Character
						self.AvatarSelected:Fire(Character)

						return
					end
				end
			end

			if self._AvatarSelecting then
				self.AvatarDeselected:Fire(self._AvatarSelecting)
			end
			self._AvatarSelecting = nil
		end
	end)
end

function AvatarSelector:Stop()
	if self._AvatarSelectionConnection then
		self._AvatarSelectionConnection:Disconnect()
		self._AvatarSelectionConnection = nil
	end

	self._AvatarSelectionHighlight.Adornee = nil
end

return AvatarSelector


local Players = game:GetService("Players")
local Rorooms = script.Parent.Parent.Parent.Parent
local Topbar = require(Rorooms.SourceCode.Client.UI.States.Topbar)
local UIController = require(Rorooms.SourceCode.Client.UI.UIController)
local Fusion = require(Rorooms.Parent.Fusion)
local States = require(Rorooms.SourceCode.Client.UI.States)
local Knit = require(Rorooms.Parent.Knit)
local AvatarSelector = require(script.Parent.AvatarSelector)
local Future = require(Rorooms.Parent.Future)
local Signal = require(Rorooms.Parent.Signal)

local Scoped = Fusion.scoped
local Peek = Fusion.peek

local ProfilesService

local ProfilesController = {
	Name = "ProfilesController",

	Scope = Scoped(Fusion),

	ProfileUpdated = Signal.new(),
}

function ProfilesController:GetProfile(UserId: number)
	if UserId <= 0 then
		UserId = 1
	end

	local Success, Result = Future.Try(function()
		return ProfilesService:GetProfile(UserId):await()
	end):Await()
	if Success then
		return Result
	end

	return {}
end

function ProfilesController:_HandleAvatarSelection()
	AvatarSelector:Start()

	AvatarSelector.AvatarSelected:Connect(function(Character: Model?)
		local Player = Players:GetPlayerFromCharacter(Character)
		if Player then
			if
				(Player.UserId ~= Peek(States.Menus.ProfileMenu.UserId))
				or (Peek(States.Menus.CurrentMenu) ~= "ProfileMenu")
			then
				States.Menus.ProfileMenu.UserId:set(Player.UserId)
				States.Menus.ProfileMenu.Location.Online:set(true)
				States.Menus.ProfileMenu.Location.InRorooms:set(true)
				States.Menus.ProfileMenu.Location.PlaceId:set(game.PlaceId)
				States.Menus.ProfileMenu.Location.JobId:set(game.JobId)
				States.Menus.CurrentMenu:set("ProfileMenu")
			else
				States.Menus.ProfileMenu.UserId:set(nil)
				States.Menus.CurrentMenu:set(nil)
			end
		end
	end)

	AvatarSelector.AvatarDeselected:Connect(function()
		States.Menus.ProfileMenu.UserId:set(nil)

		if Peek(States.Menus.CurrentMenu) == "ProfileMenu" then
			States.Menus.CurrentMenu:set(nil)
		end
	end)
end

function ProfilesController:KnitStart()
	UIController:MountUI(require(Rorooms.SourceCode.Client.UI.ScreenGuis.ProfileMenu))

	Topbar:AddTopbarButton("Profile", Topbar.NativeButtons.Profile)

	ProfilesService = Knit.GetService("ProfilesService")

	ProfilesService.ProfileUpdated:Connect(function(UpdatedUserId: number)
		self.ProfileUpdated:Fire(UpdatedUserId)
	end)

	self:_HandleAvatarSelection()
end

return ProfilesController


local Rorooms = script.Parent.Parent.Parent.Parent
local Knit = require(Rorooms.Parent.Knit)
local Topbar = require(Rorooms.SourceCode.Client.UI.States.Topbar)
local UIController = require(Rorooms.SourceCode.Client.UI.UIController)

local SettingsMenu = require(Rorooms.SourceCode.Client.UI.ScreenGuis.SettingsMenu)

local SettingsController = {
	Name = "SettingsController",
}

function SettingsController:KnitStart()
	UIController = Knit.GetController("UIController")

	UIController:MountUI(SettingsMenu)

	Topbar:AddTopbarButton("Settings", Topbar.NativeButtons.Settings)
end

return SettingsController


local Rorooms = script.Parent.Parent.Parent.Parent.Parent
local OnyxUI = require(Rorooms.Parent.OnyxUI)
local Fusion = require(Rorooms.Parent.Fusion)

local Children = Fusion.Children
local Util = OnyxUI.Util
local Themer = OnyxUI.Themer

return function(Scope: Fusion.Scope<any>, Props)
	local Scope = Fusion.innerScope(Scope, Fusion, OnyxUI.Util, OnyxUI.Components)
	local Theme = Themer.Theme:now()

	local Name = Util.Fallback(Props.Name, "CategoriesSidebar")

	return Scope:Scroller {
		Name = Name,
		Size = Props.Size,
		AutomaticSize = Enum.AutomaticSize.X,
		ScrollBarThickness = 0,
		ScrollBarImageTransparency = 1,
		ListEnabled = true,
		ListPadding = Scope:Computed(function(Use)
			return UDim.new(0, Use(Theme.Spacing["0.5"]))
		end),
		Padding = Scope:Computed(function(Use)
			return UDim.new(0, Use(Theme.StrokeThickness["1"]))
		end),

		[Children] = {
			Props[Children],
		},
	}
end


local Rorooms = script.Parent.Parent.Parent.Parent.Parent
local OnyxUI = require(Rorooms.Parent.OnyxUI)
local Fusion = require(Rorooms.Parent.Fusion)
local Assets = require(Rorooms.SourceCode.Shared.Assets)

local Children = Fusion.Children
local Util = OnyxUI.Util
local Themer = OnyxUI.Themer
local Peek = Fusion.peek

local CustomButton = require(script.Parent.CustomButton)

return function(Scope: Fusion.Scope<any>, Props)
	local Scope = Fusion.innerScope(Scope, Fusion, OnyxUI.Util, OnyxUI.Components, {
		CustomButton = CustomButton,
	})
	local Theme = Themer.Theme:now()

	local Name = Util.Fallback(Props.Name, "CategoryButton")
	local CategoryName = Util.Fallback(Props.CategoryName, "General")
	local Icon = Util.Fallback(Props.Icon, nil)
	local FallbackIcon = Util.Fallback(Props.FallbackIcon, Assets.Icons.Categories.General)
	local OnActivated = Util.Fallback(Props.OnActivated, function() end)

	return Scope:CustomButton {
		Name = Name,
		LayoutOrder = Props.LayoutOrder,

		OnActivated = function()
			Peek(OnActivated)()
		end,

		[Children] = {
			Scope:Image {
				Name = "Icon",
				Image = Scope:Computed(function(Use)
					local IconValue = Use(Icon)
					local CategoryNameValue = Use(CategoryName)
					local DefaultIcon = Assets.Icons.Categories[CategoryNameValue]

					if IconValue then
						return IconValue
					elseif DefaultIcon then
						return DefaultIcon
					else
						return Assets.Icons.Categories.General
					end
				end),
				ImageColor3 = Theme.Colors.BaseContent.Main,
				FallbackImage = FallbackIcon,
				Size = Scope:Computed(function(Use)
					return UDim2.fromOffset(Use(Theme.TextSize["1.5"]), Use(Theme.TextSize["1.5"]))
				end),
				BackgroundTransparency = 1,
			},
		},
	}
end


local Rorooms = script.Parent.Parent.Parent.Parent.Parent
local OnyxUI = require(Rorooms.Parent.OnyxUI)
local Fusion = require(Rorooms.Parent.Fusion)
local ColorUtils = require(Rorooms.Parent.ColorUtils)

local Util = OnyxUI.Util
local Themer = OnyxUI.Themer

export type Props = {}

return function(Scope: Fusion.Scope<any>, Props: Props)
	local Scope = Fusion.innerScope(Scope, Fusion, OnyxUI.Util, OnyxUI.Components)
	local Theme = Themer.Theme:now()

	local Color = Util.Fallback(Props.Color, Theme.Colors.Neutral.Main)

	return Scope:Button(Util.CombineProps(Props, {
		Color = Color,
		StrokeColor = Scope:Computed(function(Use)
			return ColorUtils.Emphasize(Use(Color), Use(Theme.Emphasis.Regular))
		end),
		CornerRadius = Scope:Computed(function(Use)
			return UDim.new(0, Use(Theme.CornerRadius["1.5"]))
		end),
		PaddingTop = Scope:Computed(function(Use)
			return UDim.new(0, Use(Theme.Spacing["0.5"]))
		end),
		PaddingBottom = Scope:Computed(function(Use)
			return UDim.new(0, Use(Theme.Spacing["0.5"]))
		end),
		PaddingLeft = Scope:Computed(function(Use)
			return UDim.new(0, Use(Theme.Spacing["0.5"]))
		end),
		PaddingRight = Scope:Computed(function(Use)
			return UDim.new(0, Use(Theme.Spacing["0.5"]))
		end),
	}))
end


local Rorooms = script.Parent.Parent.Parent.Parent.Parent
local OnyxUI = require(Rorooms.Parent.OnyxUI)
local Fusion = require(Rorooms.Parent.Fusion)

local Util = OnyxUI.Util
local Themer = OnyxUI.Themer

export type Props = {}

return function(Scope, Props: Props)
	local Scope = Fusion.innerScope(Scope, Fusion, OnyxUI.Components, Util)
	local Theme = Themer.Theme:now()

	local Disabled = Util.Fallback(Props.Disabled, false)

	return Scope:Hydrate(Scope:TextInput(Util.CombineProps(Props, {
		Name = "EditableText",
		Disabled = Disabled,
		TextTransparency = 0,
		StrokeEnabled = Scope:Computed(function(Use)
			return not Use(Disabled)
		end),
		Padding = Scope:Computed(function(Use)
			if Use(Disabled) then
				return UDim.new(0, Use(Theme.Spacing["0"]))
			else
				return UDim.new(0, Use(Theme.Spacing["0.5"]))
			end
		end),
	}))) {
		Selectable = Scope:Computed(function(Use)
			return not Use(Disabled)
		end),
	}
end


local RunService = game:GetService("RunService")

local Rorooms = script.Parent.Parent.Parent.Parent.Parent
local OnyxUI = require(Rorooms.Parent.OnyxUI)
local Fusion = require(Rorooms.Parent.Fusion)
local ColorUtils = require(Rorooms.Parent.ColorUtils)
local Assets = require(Rorooms.SourceCode.Shared.Assets)
local EmotesController = RunService:IsRunning() and require(Rorooms.SourceCode.Client.Emotes.EmotesController)

local Children = Fusion.Children
local Util = OnyxUI.Util
local Themer = OnyxUI.Themer
local Peek = Fusion.peek

local CustomButton = require(script.Parent.CustomButton)

return function(Scope: Fusion.Scope<any>, Props)
	local Scope = Fusion.innerScope(Scope, Fusion, OnyxUI.Util, OnyxUI.Components, {
		CustomButton = CustomButton,
	})
	local Theme = Themer.Theme:now()

	local EmoteId = Util.Fallback(Props.EmoteId, "EmoteId")
	local Emote = Util.Fallback(Props.Emote, {})
	local Color = Util.Fallback(Props.Color, Theme.Colors.Neutral.Main)
	local LevelRequirement = Scope:Computed(function(Use)
		local EmoteValue = Use(Emote)
		if EmoteValue and EmoteValue.LevelRequirement then
			return EmoteValue.LevelRequirement
		else
			return nil
		end
	end)
	local GamepassRequirement = Scope:Computed(function(Use)
		local EmoteValue = Use(Emote)

		if EmoteValue and EmoteValue.GamepassRequirement then
			return EmoteValue.GamepassRequirement
		else
			return nil
		end
	end)
	local LabelText = Scope:Computed(function(Use)
		local EmoteValue = Use(Emote)
		local LevelRequirementValue = Use(LevelRequirement)

		if EmoteValue and EmoteValue.LabelText then
			return EmoteValue.LabelText
		elseif LevelRequirementValue then
			return LevelRequirementValue
		else
			return ""
		end
	end)
	local LabelIcon = Scope:Computed(function(Use)
		local EmoteValue = Use(Emote)
		local LevelRequirementValue = Use(LevelRequirement)
		local GamepassRequirementValue = Use(GamepassRequirement)

		if EmoteValue and EmoteValue.LabelIcon then
			return EmoteValue.LabelIcon
		elseif LevelRequirementValue then
			return Assets.Icons.Categories.Unlockable
		elseif GamepassRequirementValue then
			return Assets.Icons.Categories.Robux
		else
			return ""
		end
	end)

	return Scope:CustomButton(Util.CombineProps(Props, {
		Name = "EmoteButton",
		Color = Color,
		LayoutOrder = Scope:Computed(function(Use)
			return Use(Emote).LayoutOrder or 0
		end),
		ListEnabled = false,

		OnActivated = function()
			if Props.Callback then
				Props.Callback()
			end

			if EmotesController then
				EmotesController:PlayEmote(Peek(EmoteId))
			end
		end,

		[Children] = {
			Scope:Frame {
				Name = "Details",
				ListEnabled = true,
				ListFillDirection = Enum.FillDirection.Vertical,
				ListHorizontalAlignment = Enum.HorizontalAlignment.Center,
				ListPadding = Scope:Computed(function(Use)
					return UDim.new(0, Use(Theme.Spacing["0.5"]))
				end),
				PaddingTop = Scope:Computed(function(Use)
					return UDim.new(0, Use(Theme.Spacing["1"]))
				end),
				Padding = Scope:Computed(function(Use)
					return UDim.new(0, Use(Theme.Spacing["0"]))
				end),

				[Children] = {
					Scope:Text {
						Name = "Emoji",
						Text = Scope:Computed(function(Use)
							if Use(Props.Emote) and Use(Props.Emote).Emoji then
								return Use(Props.Emote).Emoji
							else
								return "🪩"
							end
						end),
						TextSize = Theme.TextSize["2.25"],
						RichText = false,
						ClipsDescendants = false,
						TextWrapped = false,
						AutoLocalize = false,
					},
					Scope:Text {
						Name = "Name",
						Text = Scope:Computed(function(Use)
							local EmoteValue = Use(Emote)
							local EmoteIdValue = Use(EmoteId)

							if EmoteValue and EmoteValue.DisplayName then
								return EmoteValue.DisplayName
							else
								return EmoteIdValue
							end
						end),
						TextSize = Theme.TextSize["0.875"],
						TextTruncate = Enum.TextTruncate.AtEnd,
						AutomaticSize = Enum.AutomaticSize.None,
						TextXAlignment = Enum.TextXAlignment.Center,
						Size = Scope:Computed(function(Use)
							return UDim2.fromOffset(Use(Theme.Spacing["4"]) * 1.1, Use(Theme.TextSize["0.875"]) * 2)
						end),
					},
				},
			},
			Scope:IconText {
				Name = "Label",
				Content = Scope:Computed(function(Use)
					local LabelTextValue = Use(LabelText)
					local LabelIconValue = Use(LabelIcon)
					return { LabelIconValue, LabelTextValue }
				end),
				ContentSize = Theme.TextSize["0.875"],
				ContentColor = Scope:Computed(function(Use)
					return ColorUtils.Emphasize(Use(Color), Use(Theme.Emphasis.Strong))
				end),
				ContentWrapped = false,
				Visible = Scope:Computed(function(Use)
					local LabelTextValue = Use(LabelText)
					local LabelIconValue = Use(LabelIcon)
					return (LabelTextValue ~= nil) or (LabelIconValue ~= nil)
				end),
				ListPadding = Scope:Computed(function(Use)
					return UDim.new(0, Use(Theme.Spacing["0.25"]))
				end),
				ListVerticalAlignment = Enum.VerticalAlignment.Center,
			},
		},
	}))
end


local Rorooms = script.Parent.Parent.Parent.Parent.Parent
local OnyxUI = require(Rorooms.Parent.OnyxUI)
local Fusion = require(Rorooms.Parent.Fusion)
local States = require(Rorooms.SourceCode.Client.UI.States)
local Config = require(Rorooms.Config).Config

local Children = Fusion.Children
local Util = OnyxUI.Util

local CategoriesSidebar = require(script.Parent.CategoriesSidebar)
local CategoryButton = require(script.Parent.CategoryButton)

return function(Scope: Fusion.Scope<any>, Props)
	local Scope = Fusion.innerScope(Scope, Fusion, OnyxUI.Util, OnyxUI.Components, {
		CategoriesSidebar = CategoriesSidebar,
		CategoryButton = CategoryButton,
	})

	local Name = Util.Fallback(Props.Name, "EmoteCategoriesSidebar")

	return Scope:CategoriesSidebar {
		Name = Name,
		Size = Props.Size,

		[Children] = {
			Scope:ForPairs(Config.Systems.Emotes.Categories, function(Use, Scope, CategoryName: string, Category)
				return CategoryName,
					Scope:CategoryButton {
						Name = "EmoteCategoryButton",
						CategoryName = CategoryName,
						Icon = Category.Icon,
						Color = Category.TintColor,
						LayoutOrder = Category.LayoutOrder,

						OnActivated = function()
							States.Menus.EmotesMenu.FocusedCategory:set(CategoryName, true)
						end,
					}
			end),
		},
	}
end


local Rorooms = script.Parent.Parent.Parent.Parent.Parent
local Components = require(Rorooms.SourceCode.Client.UI.Components)
local Fusion = require(Rorooms.Parent.Fusion)

return {
	story = function(Parent: GuiObject, _Props: { [string]: any })
		local Scope = Fusion.scoped(Fusion, Components)

		local Instance = Scope:EmoteCategoriesSidebar {
			Parent = Parent,
			Size = UDim2.fromOffset(0, 200),
		}

		return function()
			Instance:Destroy()
		end
	end,
}


local Rorooms = script.Parent.Parent.Parent.Parent.Parent
local OnyxUI = require(Rorooms.Parent.OnyxUI)
local Fusion = require(Rorooms.Parent.Fusion)
local States = require(Rorooms.SourceCode.Client.UI.States)
local Config = require(Rorooms.Config).Config
local Assets = require(Rorooms.SourceCode.Shared.Assets)

local Children = Fusion.Children
local Util = OnyxUI.Util
local Themer = OnyxUI.Themer

local EmoteButton = require(Rorooms.SourceCode.Client.UI.Components.EmoteButton)

return function(Scope: Fusion.Scope<any>, Props)
	local Scope = Fusion.innerScope(Scope, Fusion, OnyxUI.Util, OnyxUI.Components, {
		EmoteButton = EmoteButton,
	})
	local Theme = Themer.Theme:now()

	local CategoryName = Util.Fallback(Props.CategoryName, "General")
	local Name = Util.Fallback(Props.Name, script.Name)
	local LayoutOrder = Util.Fallback(Props.LayoutOrder, 0)

	local Category = Scope:Computed(function(Use)
		return Config.Systems.Emotes.Categories[Use(CategoryName)]
	end)

	return Scope:Frame {
		Name = Name,
		LayoutOrder = LayoutOrder,
		ListEnabled = true,
		ListHorizontalFlex = Enum.UIFlexAlignment.Fill,

		[Children] = {
			Scope:Frame {
				Name = "Title",
				ListEnabled = true,
				ListFillDirection = Enum.FillDirection.Horizontal,
				ListPadding = Scope:Computed(function(Use)
					return UDim.new(0, Use(Theme.Spacing["0.25"]))
				end),

				[Children] = {
					Scope:Icon {
						Image = Scope:Computed(function(Use)
							local CategoryValue = Use(Category)
							local CategoryNameValue = Use(CategoryName)
							local DefaultIcon = Assets.Icons.Categories[CategoryNameValue]

							if CategoryValue and Use(Category).Icon then
								return Use(Category).Icon
							elseif DefaultIcon then
								return DefaultIcon
							else
								return Assets.Icons.Categories.General
							end
						end),
						ImageColor3 = Theme.Colors.BaseContent.Main,
						Size = Scope:Computed(function(Use)
							return UDim2.fromOffset(Use(Theme.TextSize["1"]), Use(Theme.TextSize["1"]))
						end),
					},
					Scope:Text {
						Text = CategoryName,
						TextWrapped = false,
					},
				},
			},
			Scope:Frame {
				Name = "Emotes",
				AutomaticSize = Enum.AutomaticSize.Y,
				ListEnabled = true,
				ListPadding = Scope:Computed(function(Use)
					return UDim.new(0, Use(Theme.Spacing["0.75"]))
				end),
				ListFillDirection = Enum.FillDirection.Horizontal,
				ListWraps = true,

				[Children] = {
					Scope:ForPairs(Config.Systems.Emotes.Emotes, function(Use, Scope, EmoteId, Emote)
						local EmoteCategory = Emote.Category
						if EmoteCategory == nil then
							EmoteCategory = "General"
						end

						if EmoteCategory == Use(CategoryName) then
							return EmoteId,
								Scope:EmoteButton {
									EmoteId = EmoteId,
									Emote = Emote,
									Color = Emote.TintColor,
									LayoutOrder = Scope:Computed(function(Use)
										if Emote then
											if Emote.LevelRequirement then
												return Emote.LevelRequirement
											elseif Emote.DisplayName then
												return string.byte(Emote.DisplayName)
											end
										end

										return 0
									end),

									Callback = function()
										if Use(States.CoreGui.ScreenSize).Y <= 500 then
											States.Menus.CurrentMenu:set()
										end
									end,
								}
						else
							return EmoteId, nil
						end
					end),
				},
			},
		},
	}
end


local MarketplaceService = game:GetService("MarketplaceService")

local Rorooms = script.Parent.Parent.Parent.Parent.Parent
local Future = require(script.Parent.Parent.Parent.Parent.Parent.Parent.Future)
local OnyxUI = require(Rorooms.Parent.OnyxUI)
local Fusion = require(Rorooms.Parent.Fusion)
local States = require(Rorooms.SourceCode.Client.UI.States)

local Children = Fusion.Children
local Util = OnyxUI.Util
local Themer = OnyxUI.Themer
local Peek = Fusion.peek

local CustomButton = require(Rorooms.SourceCode.Client.UI.Components.CustomButton)

return function(Scope: Fusion.Scope<any>, Props)
	local Scope = Fusion.innerScope(Scope, Fusion, OnyxUI.Util, OnyxUI.Components, {
		CustomButton = CustomButton,
	})
	local Theme = Themer.Theme:now()

	local PlaceId = Scope:EnsureValue(Util.Fallback(Props.PlaceId, nil))
	local UserId = Util.Fallback(Props.UserId, 1)
	local DisplayName = Util.Fallback(Props.DisplayName, "DisplayName")
	local Online = Util.Fallback(Props.Online, false)
	local JobId = Util.Fallback(Props.JobId, nil)
	local InRorooms = Util.Fallback(Props.InRorooms, false)

	local PlaceInfo = Scope:Value({})

	local function UpdatePlaceInfo()
		if Peek(PlaceId) == nil then
			return
		end

		Future.Try(function()
			return MarketplaceService:GetProductInfo(Peek(PlaceId))
		end):After(function(Success, Result)
			if Success then
				PlaceInfo:set(Result)
			else
				warn(Result)
			end
		end)
	end

	Scope:Observer(PlaceId):onChange(UpdatePlaceInfo)
	UpdatePlaceInfo()

	local Status = Scope:Computed(function(Use)
		if Use(InRorooms) then
			return "Rorooms"
		elseif Use(Online) then
			return "Online"
		else
			return "Offline"
		end
	end)
	local StatusColor = Scope:Computed(function(Use)
		local StatusValue = Use(Status)
		if StatusValue == "Rorooms" then
			return OnyxUI.Util.Colors.Green["500"]
		elseif StatusValue == "Online" then
			return OnyxUI.Util.Colors.Sky["500"]
		else
			return Use(Theme.Colors.Base.Main)
		end
	end)

	return Scope:CustomButton {
		Name = "FriendButton",
		Parent = Props.Parent,
		ClipsDescendants = true,
		LayoutOrder = Props.LayoutOrder,
		ListEnabled = true,
		ListFillDirection = Enum.FillDirection.Vertical,
		ListHorizontalAlignment = Enum.HorizontalAlignment.Center,
		ListPadding = Scope:Computed(function(Use)
			return UDim.new(0, Use(Theme.Spacing["0.75"]))
		end),

		OnActivated = function()
			States.Menus.ProfileMenu.UserId:set(Peek(UserId))
			States.Menus.ProfileMenu.IsFriend:set(true)
			States.Menus.ProfileMenu.Location.Online:set(Peek(Online))
			States.Menus.ProfileMenu.Location.InRorooms:set(Peek(InRorooms))
			States.Menus.ProfileMenu.Location.JobId:set(Peek(JobId))
			States.Menus.ProfileMenu.Location.PlaceId:set(Peek(PlaceId))
			States.Menus.CurrentMenu:set("ProfileMenu")
		end,

		[Children] = {
			Scope:PlayerAvatar {
				Status = Status,
				Image = Scope:Computed(function(Use)
					return `rbxthumb://type=AvatarHeadShot&id={Use(UserId)}&w=150&h=150`
				end),
			},
			Scope:Frame {
				Name = "Details",
				Size = UDim2.fromOffset(80, 0),
				AutomaticSize = Enum.AutomaticSize.Y,
				ListEnabled = true,
				ListPadding = UDim.new(0, 0),
				ListHorizontalFlex = Enum.UIFlexAlignment.Fill,

				[Children] = {
					Scope:Text {
						Name = "DisplayName",
						Text = DisplayName,
						TextTruncate = Enum.TextTruncate.AtEnd,
						AutomaticSize = Enum.AutomaticSize.Y,
						TextXAlignment = Enum.TextXAlignment.Center,
						TextWrapped = false,
						AutoLocalize = false,
					},
					Scope:Text {
						Name = "Status",
						Text = Scope:Computed(function(Use)
							return (Use(InRorooms) and Use(PlaceInfo).Name) or "Online"
						end),
						TextColor3 = StatusColor,
						TextSize = Theme.TextSize["0.875"],
						TextTruncate = Enum.TextTruncate.AtEnd,
						AutomaticSize = Enum.AutomaticSize.Y,
						TextXAlignment = Enum.TextXAlignment.Center,
						TextWrapped = false,
						AutoLocalize = Scope:Computed(function(Use)
							return next((Use(PlaceInfo) or {})) ~= nil
						end),
					},
				},
			},
		},
	}
end


local Component = require(script.Parent[string.split(script.Name, ".")[1]])

return function(Target)
	local Gui = Component {
		Parent = Target,
	}

	return function()
		Gui:Destroy()
	end
end


local RunService = game:GetService("RunService")

local Rorooms = script.Parent.Parent.Parent.Parent.Parent
local OnyxUI = require(Rorooms.Parent.OnyxUI)
local Fusion = require(Rorooms.Parent.Fusion)
local States = require(Rorooms.SourceCode.Client.UI.States)
local ColorUtils = require(Rorooms.Parent.ColorUtils)
local ItemsController = RunService:IsRunning() and require(Rorooms.SourceCode.Client.Items.ItemsController)
local Assets = require(Rorooms.SourceCode.Shared.Assets)

local Children = Fusion.Children
local Util = OnyxUI.Util
local Themer = OnyxUI.Themer
local Peek = Fusion.peek

local CustomButton = require(script.Parent.CustomButton)

return function(Scope: Fusion.Scope<any>, Props)
	local Scope = Fusion.innerScope(Scope, Fusion, OnyxUI.Util, OnyxUI.Components, {
		CustomButton = CustomButton,
	})
	local Theme = Themer.Theme:now()

	local ItemId = Util.Fallback(Props.ItemId, "ItemId")
	local Item = Util.Fallback(Props.Item, {})
	local Color = Util.Fallback(Props.Color, Theme.Colors.Neutral.Main)
	local Callback = Util.Fallback(Props.Callback, function() end)
	local LevelRequirement = Scope:Computed(function(Use)
		local ItemValue = Use(Item)
		if ItemValue and ItemValue.LevelRequirement then
			return ItemValue.LevelRequirement
		else
			return nil
		end
	end)
	local GamepassRequirement = Scope:Computed(function(Use)
		local ItemValue = Use(Item)

		if ItemValue and ItemValue.GamepassRequirement then
			return ItemValue.GamepassRequirement
		else
			return nil
		end
	end)
	local LabelText = Scope:Computed(function(Use)
		local ItemValue = Use(Item)
		local LevelRequirementValue = Use(LevelRequirement)

		if ItemValue and ItemValue.LabelText then
			return ItemValue.LabelText
		elseif LevelRequirementValue then
			return LevelRequirementValue
		else
			return ""
		end
	end)
	local LabelIcon = Scope:Computed(function(Use)
		local ItemValue = Use(Item)
		local LevelRequirementValue = Use(LevelRequirement)
		local GamepassRequirementValue = Use(GamepassRequirement)

		if ItemValue and ItemValue.LabelIcon then
			return ItemValue.LabelIcon
		elseif LevelRequirementValue then
			return Assets.Icons.Categories.Unlockable
		elseif GamepassRequirementValue then
			return Assets.Icons.Categories.Robux
		else
			return ""
		end
	end)

	local IsEquipped = Scope:Computed(function(Use)
		return table.find(Use(States.Items.Equipped), Use(ItemId)) ~= nil
	end)

	return Scope:CustomButton(Util.CombineProps(Props, {
		Name = "ItemButton",
		Color = Color,
		Size = UDim2.fromOffset(70, 70),
		AutomaticSize = Enum.AutomaticSize.None,
		LayoutOrder = Scope:Computed(function(Use)
			return Use(Item).LayoutOrder or 0
		end),
		StrokeColor = Scope:Spring(
			Scope:Computed(function(Use)
				if Use(IsEquipped) then
					return ColorUtils.Emphasize(Use(Color), Use(Theme.Emphasis.Strong))
				else
					return ColorUtils.Emphasize(Use(Color), Use(Theme.Emphasis.Regular))
				end
			end),
			Theme.SpringSpeed["1"],
			Theme.SpringDampening["1"]
		),
		ListEnabled = false,

		OnActivated = function()
			if Callback then
				Callback()
			end

			if ItemsController then
				ItemsController:ToggleEquipItem(Peek(ItemId))
			end
		end,

		[Children] = {
			Scope:Computed(function(Use)
				local Tool = Use(Item).Tool
				if not Tool then
					return
				end

				local Size = UDim2.fromOffset(60, 60)
				local AnchorPoint = Vector2.new(0.5, 0.5)
				local Position = UDim2.fromScale(0.5, 0.5)

				if string.len(Tool.TextureId) >= 1 then
					return Scope:Image {
						Name = "Icon",
						Image = Tool.TextureId,
						BackgroundTransparency = 1,
						AnchorPoint = AnchorPoint,
						Position = Position,
						Size = Size,
					}
				else
					return Scope:Text {
						Name = "ItemName",
						Text = Scope:Computed(function(Use)
							local ItemValue = Use(Item)
							local ItemIdValue = Use(ItemId)

							if ItemValue and ItemValue.DisplayName then
								return ItemValue.DisplayName
							else
								return ItemIdValue
							end
						end),
						TextSize = Theme.TextSize["1"],
						AnchorPoint = AnchorPoint,
						Position = Position,
						Size = Size,
						TextTruncate = Enum.TextTruncate.AtEnd,
						AutomaticSize = Enum.AutomaticSize.None,
						TextXAlignment = Enum.TextXAlignment.Center,
						TextYAlignment = Enum.TextYAlignment.Center,
						AutoLocalize = false,
						TextWrapped = true,
					}
				end
			end),
			Scope:IconText {
				Name = "Label",
				Content = Scope:Computed(function(Use)
					local LabelTextValue = Use(LabelText)
					local LabelIconValue = Use(LabelIcon)
					return { LabelIconValue, LabelTextValue }
				end),
				ContentSize = Theme.TextSize["0.875"],
				ContentColor = Scope:Computed(function(Use)
					return ColorUtils.Emphasize(Use(Color), Use(Theme.Emphasis.Strong))
				end),
				ContentWrapped = false,
				Visible = Scope:Computed(function(Use)
					local LabelTextValue = Use(LabelText)
					local LabelIconValue = Use(LabelIcon)
					return (LabelTextValue ~= nil) or (LabelIconValue ~= nil)
				end),
				ListPadding = Scope:Computed(function(Use)
					return UDim.new(0, Use(Theme.Spacing["0.25"]))
				end),
				ListVerticalAlignment = Enum.VerticalAlignment.Center,
			},
		},
	}))
end


local Rorooms = script.Parent.Parent.Parent.Parent.Parent
local OnyxUI = require(Rorooms.Parent.OnyxUI)
local Fusion = require(Rorooms.Parent.Fusion)
local States = require(Rorooms.SourceCode.Client.UI.States)
local Config = require(Rorooms.Config).Config

local Children = Fusion.Children
local Util = OnyxUI.Util

local CategoriesSidebar = require(script.Parent.CategoriesSidebar)
local CategoryButton = require(script.Parent.CategoryButton)

return function(Scope: Fusion.Scope<any>, Props)
	local Scope = Fusion.innerScope(Scope, Fusion, OnyxUI.Util, OnyxUI.Components, {
		CategoriesSidebar = CategoriesSidebar,
		CategoryButton = CategoryButton,
	})

	local Name = Util.Fallback(Props.Name, "ItemCategoriesSidebar")

	return Scope:CategoriesSidebar {
		Name = Name,
		Size = Props.Size,

		[Children] = {
			Scope:ForPairs(Config.Systems.Items.Categories, function(Use, Scope, CategoryName: string, Category)
				return CategoryName,
					Scope:CategoryButton {
						Name = "ItemCategoryButton",
						CategoryName = CategoryName,
						Icon = Category.Icon,
						Color = Category.TintColor,
						LayoutOrder = Category.LayoutOrder,

						OnActivated = function()
							States.Menus.ItemsMenu.FocusedCategory:set(CategoryName, true)
						end,
					}
			end),
		},
	}
end


local Rorooms = script.Parent.Parent.Parent.Parent.Parent
local OnyxUI = require(Rorooms.Parent.OnyxUI)
local Fusion = require(Rorooms.Parent.Fusion)
local Config = require(Rorooms.Config).Config
local Assets = require(Rorooms.SourceCode.Shared.Assets)

local Children = Fusion.Children
local Util = OnyxUI.Util
local Themer = OnyxUI.Themer
local Peek = Fusion.peek

local ItemButton = require(Rorooms.SourceCode.Client.UI.Components.ItemButton)

return function(Scope: Fusion.Scope<any>, Props)
	local Scope = Fusion.innerScope(Scope, Fusion, OnyxUI.Util, OnyxUI.Components, {
		ItemButton = ItemButton,
	})
	local Theme = Themer.Theme:now()

	local Name = Util.Fallback(Props.Name, script.Name)
	local CategoryName = Util.Fallback(Props.CategoryName, "Category")

	local Category = Scope:Computed(function(Use)
		return Config.Systems.Items.Categories[Use(CategoryName)]
	end)

	return Scope:Frame {
		Name = Name,
		LayoutOrder = Scope:Computed(function(Use)
			if Use(Category) then
				return Use(Category).LayoutOrder
			else
				return 0
			end
		end),
		ListEnabled = true,
		ListPadding = Scope:Computed(function(Use)
			return UDim.new(0, Use(Theme.Spacing["0.75"]))
		end),
		ListHorizontalFlex = Enum.UIFlexAlignment.Fill,

		[Children] = {
			Scope:Frame {
				Name = "Title",
				ListEnabled = true,
				ListFillDirection = Enum.FillDirection.Horizontal,
				ListPadding = Scope:Computed(function(Use)
					return UDim.new(0, Use(Theme.Spacing["0.25"]))
				end),

				[Children] = {
					Scope:Icon {
						Image = Scope:Computed(function(Use)
							local CategoryValue = Use(Category)
							local CategoryNameValue = Use(CategoryName)
							local DefaultIcon = Assets.Icons.Categories[CategoryNameValue]

							if CategoryValue and Use(Category).Icon then
								return Use(Category).Icon
							elseif DefaultIcon then
								return DefaultIcon
							else
								return Assets.Icons.Categories.General
							end
						end),
						ImageColor3 = Theme.Colors.BaseContent.Main,
						Size = Scope:Computed(function(Use)
							return UDim2.fromOffset(Use(Theme.TextSize["1"]), Use(Theme.TextSize["1"]))
						end),
					},
					Scope:Text {
						Text = CategoryName,
						TextWrapped = false,
					},
				},
			},
			Scope:Frame {
				Name = "Items",
				ListEnabled = true,
				ListWraps = true,
				ListFillDirection = Enum.FillDirection.Horizontal,
				ListPadding = Scope:Computed(function(Use)
					return UDim.new(0, Use(Theme.Spacing["0.75"]))
				end),
				Padding = Scope:Computed(function(Use)
					return UDim.new(0, Use(Theme.StrokeThickness["1"]))
				end),

				[Children] = {
					Scope:ForPairs(Config.Systems.Items.Items, function(Use, Scope, ItemId, Item)
						local ItemCategory = Item.Category
						if ItemCategory == nil then
							ItemCategory = "General"
						end

						if ItemCategory == Peek(CategoryName) then
							return ItemId,
								Scope:ItemButton {
									ItemId = ItemId,
									Item = Item,
									Color = Item.TintColor,
									LayoutOrder = Scope:Computed(function(Use)
										if Item then
											if Item.LevelRequirement then
												return Item.LevelRequirement
											elseif Item.DisplayName then
												return string.byte(Item.DisplayName)
											end
										end

										return 0
									end),
								}
						else
							return ItemId, nil
						end
					end),
				},
			},
		},
	}
end


local Rorooms = script.Parent.Parent.Parent.Parent.Parent
local OnyxUI = require(Rorooms.Parent.OnyxUI)
local Fusion = require(Rorooms.Parent.Fusion)
local Assets = require(Rorooms.SourceCode.Shared.Assets)

local Children = Fusion.Children

export type Props = {
	Level: Fusion.UsedAs<number>?,
}

return function(Scope: Fusion.Scope<any>, Props: Props)
	local Scope = Fusion.innerScope(Scope, Fusion, OnyxUI.Components, OnyxUI.Util)
	local Theme = OnyxUI.Themer.Theme:now()

	local Level = OnyxUI.Util.Fallback(Props.Level, 0)

	return Scope:Frame(OnyxUI.Util.CombineProps(Props, {
		Name = "LevelBadge",
		ListEnabled = true,
		ListFillDirection = Enum.FillDirection.Horizontal,
		ListPadding = Scope:Computed(function(Use)
			return UDim.new(0, Use(Theme.Spacing["0"]))
		end),
		ListVerticalAlignment = Enum.VerticalAlignment.Center,

		[Children] = {
			Scope:IconText {
				Content = Scope:Computed(function(Use)
					return { Assets.Icons.UserBadges.Level, Use(Level) }
				end),
				ContentSize = Theme.TextSize["1.25"],
				ListPadding = Scope:Computed(function(Use)
					return UDim.new(0, Use(Theme.Spacing["0.25"]))
				end),
			},
		},
	}))
end


local Rorooms = script.Parent.Parent.Parent.Parent.Parent
local Fusion = require(Rorooms.Parent.Fusion)
local OnyxUI = require(Rorooms.Parent.OnyxUI)
local States = require(Rorooms.SourceCode.Client.UI.States)

local Children = Fusion.Children
local Themer = OnyxUI.Themer
local Util = OnyxUI.Util

export type Props = {
	Name: Fusion.UsedAs<string>?,
	Open: Fusion.UsedAs<boolean>?,
	Size: Fusion.UsedAs<UDim2>?,
	AutomaticSize: Fusion.UsedAs<Enum.AutomaticSize>?,
	ListFillDirection: Fusion.UsedAs<Enum.FillDirection>?,
	ListHorizontalFlex: Fusion.UsedAs<Enum.UIFlexAlignment>?,
	ListPadding: Fusion.UsedAs<UDim>?,
	DisplayOrder: Fusion.UsedAs<number>?,

	[typeof(Children)]: { any },
}

return function(Scope: Fusion.Scope<any>, Props)
	local Scope = Fusion.innerScope(Scope, Fusion, OnyxUI.Util, OnyxUI.Components)
	local Theme = Themer.Theme:now()

	local Name = Util.Fallback(Props.Name, "Menu")
	local Open = Util.Fallback(Props.Open, false)
	local AnchorPoint = Util.Fallback(Props.AnchorPoint, Vector2.new(0.5, 0))
	local Position = Util.Fallback(
		Props.Position,
		Scope:Computed(function(Use)
			local YPos = Use(States.Topbar.YPosition)
			if not Use(Open) then
				YPos = YPos + 15
			end
			return UDim2.new(UDim.new(0.5, 0), UDim.new(0, YPos))
		end)
	)
	local Size = Util.Fallback(Props.Size, UDim2.fromOffset(0, 0))
	local AutomaticSize = Util.Fallback(Props.AutomaticSize, Enum.AutomaticSize.Y)
	local ListFillDirection = Util.Fallback(Props.ListFillDirection, Enum.FillDirection.Vertical)
	local ListHorizontalFlex = Util.Fallback(Props.ListHorizontalFlex, Enum.UIFlexAlignment.None)
	local ListVerticalFlex = Util.Fallback(Props.ListVerticalFlex, Enum.UIFlexAlignment.None)
	local Padding = Util.Fallback(
		Props.Padding,
		Scope:Computed(function(Use)
			return UDim.new(0, Use(Theme.Spacing["1.5"]))
		end)
	)
	local ListPadding = Util.Fallback(
		Props.ListPadding,
		Scope:Computed(function(Use)
			return UDim.new(0, Use(Theme.Spacing["1"]))
		end)
	)
	local DisplayOrder = Util.Fallback(Props.DisplayOrder, 0)

	return Scope:New "ScreenGui" {
		Name = Name,
		Parent = Props.Parent,
		Enabled = Open,
		ResetOnSpawn = false,
		DisplayOrder = DisplayOrder,

		[Children] = {
			Scope:MenuFrame {
				AnchorPoint = AnchorPoint,
				Position = Scope:Spring(Position, Theme.SpringSpeed["1"], Theme.SpringDampening["1.5"]),
				Size = Size,
				AutomaticSize = AutomaticSize,
				GroupTransparency = Scope:Spring(
					Scope:Computed(function(Use)
						if Use(Open) then
							return 0
						else
							return 1
						end
					end),
					Theme.SpringSpeed["1"],
					Theme.SpringDampening["1.5"]
				),
				BackgroundTransparency = States.CoreGui.PreferredTransparency,
				ListEnabled = true,
				ListFillDirection = ListFillDirection,
				ListHorizontalFlex = ListHorizontalFlex,
				ListVerticalFlex = ListVerticalFlex,
				ListPadding = ListPadding,
				Padding = Padding,

				[Children] = Props[Children],
			},
		},
	}
end


local Rorooms = script.Parent.Parent.Parent.Parent.Parent
local OnyxUI = require(Rorooms.Parent.OnyxUI)
local Fusion = require(Rorooms.Parent.Fusion)
local Assets = require(Rorooms.SourceCode.Shared.Assets)

local Children = Fusion.Children

export type Props = {
	Editable: Fusion.UsedAs<boolean>?,
	Image: Fusion.UsedAs<string>?,
	Status: Fusion.UsedAs<string>?,
	RingThickness: Fusion.UsedAs<number>?,
}

return function(Scope: Fusion.Scope<any>, Props: Props)
	local Scope = Fusion.innerScope(Scope, Fusion, OnyxUI.Components, OnyxUI.Util)
	local Theme = OnyxUI.Themer.Theme:now()

	local Editable = OnyxUI.Util.Fallback(Props.Editable, false)
	local Image = OnyxUI.Util.Fallback(Props.Image, "")
	local Status = OnyxUI.Util.Fallback(Props.Status, false)
	local RingThickness = OnyxUI.Util.Fallback(Props.RingThickness, Theme.StrokeThickness["3"])

	local StatusColor = Scope:Computed(function(Use)
		local StatusValue = Use(Status)
		if StatusValue == "Rorooms" then
			return OnyxUI.Util.Colors.Green["500"]
		elseif StatusValue == "Online" then
			return OnyxUI.Util.Colors.Sky["500"]
		else
			return Use(Theme.Colors.Base.Main)
		end
	end)

	local IsHovering = Scope:Value(false)
	local IsHolding = Scope:Value(false)

	return Scope:BaseButton(OnyxUI.Util.CombineProps(Props, {
		Name = script.Name,
		Disabled = Scope:Computed(function(Use)
			return not Use(Editable)
		end),
		Active = Editable,
		Interactable = Editable,
		CornerRadius = Scope:Computed(function(Use)
			return UDim.new(0, Use(Theme.CornerRadius.Full))
		end),

		IsHolding = IsHolding,
		IsHovering = IsHovering,

		[Children] = {
			Scope:Avatar {
				Image = Image,
				ImageTransparency = Scope:Spring(
					Scope:Computed(function(Use)
						if not Use(Editable) then
							return 0
						elseif Use(IsHolding) then
							return Use(Theme.Emphasis.Strong) * 1.25
						elseif Use(IsHovering) then
							return Use(Theme.Emphasis.Strong)
						else
							return 0
						end
					end),
					Theme.SpringSpeed["0.5"],
					Theme.SpringDampening["1"]
				),
				Size = Scope:Computed(function(Use)
					local Offset = Use(Theme.TextSize["4.5"])
					return UDim2.fromOffset(Offset, Offset)
				end),
				CornerRadius = Scope:Computed(function(Use)
					return UDim.new(0, Use(Theme.CornerRadius.Full))
				end),
				RingEnabled = true,
				RingColor = StatusColor,
				RingThickness = RingThickness,
				IndicatorEnabled = Scope:Computed(function(Use)
					return Use(Status) == "Rorooms"
				end),
				IndicatorColor = StatusColor,

				[Children] = {
					Scope:Icon {
						AnchorPoint = Vector2.new(0.5, 0.5),
						Position = UDim2.fromScale(0.5, 0.5),
						Image = Assets.Icons.General.EditPerson,
						Size = Scope:Computed(function(Use)
							local Offset = Use(Theme.TextSize["3"])
							return UDim2.fromOffset(Offset, Offset)
						end),
						ImageTransparency = Scope:Spring(
							Scope:Computed(function(Use)
								if not Use(Editable) then
									return 1
								elseif Use(IsHolding) then
									return Use(Theme.Emphasis.Regular)
								elseif Use(IsHovering) then
									return 0
								else
									return 1
								end
							end),
							Theme.SpringSpeed["0.5"],
							Theme.SpringDampening["1"]
						),
					},
				},
			},
		},
	}, { "Image" }))
end