Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- --[[
- // FileName: Chat.lua
- // Written by: SolarCrane
- // Description: Code for lua side chat on ROBLOX.
- ]]
- --[[ CONSTANTS ]]
- -- NOTE: IF YOU WANT TO USE THIS CHAT SCRIPT IN YOUR OWN GAME:
- -- 1) COPY THE CONTENTS OF THIS FILE INTO A MODULE
- -- 2) CREATE A LOCALSCRIPT AND PARENT IT TO StarterGui
- -- 3) IN THE LOCALSCRIPT require() THE CHAT MODULE YOU MADE IN STEP 1
- -- 4) CONFIGURE YOUR PLACE ON THE WEBSITE TO USE BUBBLE-CHAT
- -- 5) SET THE FOLLOWING TWO VARIABLES TO TRUE
- local FORCE_CHAT_GUI = false
- local NON_CORESCRIPT_MODE = false
- -- 6) (OPTIONAL) PUT THE FOLLOWING LINE IN A SERVER SCRIPT TO MAKE CHAT PERSIST THROUGH RESPAWNING
- -- game:GetService('StarterGui').ResetPlayerGuiOnSpawn = false
- ---------------------------------
- local MESSAGES_FADE_OUT_TIME = 30
- local MAX_UDIM_SIZE = 2^15 - 1
- local CHAT_WINDOW_Y_OFFSET = 2
- local PHONE_SCREEN_WIDTH = 640
- local TABLET_SCREEN_WIDTH = 1024
- local FLOOD_CHECK_MESSAGE_COUNT = 7
- local FLOOD_CHECK_MESSAGE_INTERVAL = 15 -- This is in seconds
- local VR_CHAT_CLICK_DEBOUNCE = 0.25
- local SCROLLBAR_THICKNESS = 7
- local CHAT_COLORS =
- {
- Color3.new(253/255, 41/255, 67/255), -- BrickColor.new("Bright red").Color,
- Color3.new(1/255, 162/255, 255/255), -- BrickColor.new("Bright blue").Color,
- Color3.new(2/255, 184/255, 87/255), -- BrickColor.new("Earth green").Color,
- BrickColor.new("Bright violet").Color,
- BrickColor.new("Bright orange").Color,
- BrickColor.new("Bright yellow").Color,
- BrickColor.new("Light reddish violet").Color,
- BrickColor.new("Brick yellow").Color,
- }
- local thisModuleName = "Chat"
- local emptySelectionImage = Instance.new("ImageLabel")
- emptySelectionImage.ImageTransparency = 1
- emptySelectionImage.BackgroundTransparency = 1
- --[[ END OF CONSTANTS ]]
- --[[ SERVICES ]]
- local RunService = game:GetService('RunService')
- local CoreGuiService = game:GetService('CoreGui')
- local PlayersService = game:GetService('Players')
- local DebrisService = game:GetService('Debris')
- local GuiService = game:GetService('GuiService')
- local InputService = game:GetService('UserInputService')
- local StarterGui = game:GetService('StarterGui')
- local ContextActionService = game:GetService('ContextActionService')
- local Settings = UserSettings()
- local GameSettings = Settings.GameSettings
- --[[ END OF SERVICES ]]
- --[[ Fast Flags ]]--
- local FFlagEnableNewDevConsole = settings():GetFFlag("EnableNewDevConsole")
- --[[ SCRIPT VARIABLES ]]
- local RobloxGui = CoreGuiService:WaitForChild("RobloxGui")
- local VRHub = require(RobloxGui.Modules.VR.VRHub)
- local PlayerPermissionsModule = require(RobloxGui.Modules.PlayerPermissionsModule)
- local StatsUtils = require(RobloxGui.Modules.Stats.StatsUtils)
- -- I am not fond of waiting at the top of the script here...
- while PlayersService.LocalPlayer == nil do PlayersService.ChildAdded:wait() end
- local Player = PlayersService.LocalPlayer
- -- GuiRoot will act as the top-node for parenting GUIs
- local GuiRoot = Instance.new('Frame')
- GuiRoot.Name = 'GuiRoot';
- GuiRoot.Size = UDim2.new(1,0,1,0);
- GuiRoot.BackgroundTransparency = 1;
- local chatRepositioned = false
- local chatBarDisabled = false
- local lastSelectedPlayer = nil
- local lastSelectedButton = nil
- local blockingUtility = nil
- local topbarEnabled = true
- if not NON_CORESCRIPT_MODE and not InputService.VREnabled then
- blockingUtility = require(RobloxGui.Modules:WaitForChild("PlayerDropDown")):CreateBlockingUtility()
- end
- --[[ END OF SCRIPT VARIABLES ]]
- local function GetLuaChatFilteringFlag()
- return true
- end
- local Util = {}
- do
- -- Check if we are running on a touch device
- function Util.IsTouchDevice()
- local touchEnabled = false
- pcall(function() touchEnabled = InputService.TouchEnabled end)
- return touchEnabled
- end
- function Util.IsSmallScreenSize()
- return GuiRoot.AbsoluteSize.X <= PHONE_SCREEN_WIDTH
- end
- function Util.Create(instanceType)
- return function(data)
- local obj = Instance.new(instanceType)
- for k, v in pairs(data) do
- if type(k) == 'number' then
- v.Parent = obj
- else
- obj[k] = v
- end
- end
- return obj
- end
- end
- function Util.Clamp(low, high, input)
- return math.max(low, math.min(high, input))
- end
- function Util.Linear(t, b, c, d)
- if t >= d then return b + c end
- return c*t/d + b
- end
- function Util.EaseOutQuad(t, b, c, d)
- if t >= d then return b + c end
- t = t/d;
- return -c * t*(t-2) + b
- end
- function Util.EaseInOutQuad(t, b, c, d)
- if t >= d then
- return b + c
- end
- t = t / (d/2);
- if (t < 1) then
- return c/2*t*t + b
- end;
- t = t - 1;
- return -c/2 * (t*(t-2) - 1) + b;
- end
- function Util.PropertyTweener(instance, prop, start, final, duration, easingFunc, cbFunc)
- local this = {}
- this.StartTime = tick()
- this.EndTime = this.StartTime + duration
- this.Cancelled = false
- local finished = false
- local percentComplete = 0
- spawn(function()
- local now = tick()
- while now < this.EndTime and instance do
- if this.Cancelled then
- return
- end
- instance[prop] = easingFunc(now - this.StartTime, start, final - start, duration)
- percentComplete = Util.Clamp(0, 1, (now - this.StartTime) / duration)
- RunService.RenderStepped:wait()
- now = tick()
- end
- if this.Cancelled == false and instance then
- instance[prop] = final
- finished = true
- percentComplete = 1
- if cbFunc then
- cbFunc()
- end
- end
- end)
- function this:GetPercentComplete()
- return percentComplete
- end
- function this:IsFinished()
- return finished
- end
- function this:Cancel()
- this.Cancelled = true
- end
- return this
- end
- function Util.Signal()
- local sig = {}
- local mSignaler = Instance.new('BindableEvent')
- local mArgData = nil
- local mArgDataCount = nil
- function sig:fire(...)
- mArgData = {...}
- mArgDataCount = select('#', ...)
- mSignaler:Fire()
- end
- function sig:connect(f)
- if not f then error("connect(nil)", 2) end
- return mSignaler.Event:connect(function()
- f(unpack(mArgData, 1, mArgDataCount))
- end)
- end
- function sig:wait()
- mSignaler.Event:wait()
- assert(mArgData, "Missing arg data, likely due to :TweenSize/Position corrupting threadrefs.")
- return unpack(mArgData, 1, mArgDataCount)
- end
- return sig
- end
- function Util.DisconnectEvent(conn)
- if conn then
- conn:disconnect()
- end
- return nil
- end
- function Util.SetGUIInsetBounds(x, y)
- local success, _ = pcall(function() GuiService:SetGlobalGuiInset(0, x, 0, y) end)
- if not success then
- pcall(function() GuiService:SetGlobalSizeOffsetPixel(-x, -y) end) -- Legacy GUI-offset function
- end
- end
- local baseUrl = game:GetService("ContentProvider").BaseUrl:lower()
- baseUrl = string.gsub(baseUrl,"/m.","/www.") --mobile site does not work for this stuff!
- function Util.GetSecureApiBaseUrl()
- local secureApiUrl = baseUrl
- secureApiUrl = string.gsub(secureApiUrl,"http://","https://")
- secureApiUrl = string.gsub(secureApiUrl,"www","api")
- return secureApiUrl
- end
- function Util.GetPlayerByName(playerName)
- -- O(n), may be faster if I store a reverse hash from the players list; can't trust FindFirstChild in PlayersService because anything can be parented to there.
- local lowerName = string.lower(playerName)
- for _, player in pairs(PlayersService:GetPlayers()) do
- if string.lower(player.Name) == lowerName then
- return player
- end
- end
- return nil -- Found no player
- end
- local function MakeIsInGroup(groupId, requiredRank)
- assert(type(requiredRank) == "nil" or type(requiredRank) == "number", "requiredRank must be a number or nil")
- local inGroupCache = {}
- return function(player)
- if player and player.UserId then
- local userId = player.UserId
- if inGroupCache[userId] == nil then
- local inGroup = false
- pcall(function() -- Many things can error is the IsInGroup check
- if requiredRank then
- inGroup = player:GetRankInGroup(groupId) > requiredRank
- else
- inGroup = player:IsInGroup(groupId)
- end
- end)
- inGroupCache[userId] = inGroup
- end
- return inGroupCache[userId]
- end
- return false
- end
- end
- Util.IsPlayerAdminAsync = MakeIsInGroup(1200769)
- Util.IsPlayerInternAsync = MakeIsInGroup(2868472, 100)
- local function GetNameValue(pName)
- local value = 0
- for index = 1, #pName do
- local cValue = string.byte(string.sub(pName, index, index))
- local reverseIndex = #pName - index + 1
- if #pName%2 == 1 then
- reverseIndex = reverseIndex - 1
- end
- if reverseIndex%4 >= 2 then
- cValue = -cValue
- end
- value = value + cValue
- end
- return value
- end
- function Util.ComputeChatColor(pName)
- return CHAT_COLORS[(GetNameValue(pName) % #CHAT_COLORS) + 1]
- end
- -- This is a memo-izing function
- local testLabel = Instance.new('TextLabel')
- testLabel.TextWrapped = true;
- testLabel.Position = UDim2.new(1,0,1,0)
- testLabel.Parent = GuiRoot -- Note: We have to parent it to check TextBounds
- -- The TextSizeCache table looks like this Text->Font->sizeBounds->FontSize
- local TextSizeCache = {}
- function Util.GetStringTextBounds(text, font, fontSize, sizeBounds)
- -- If no sizeBounds are specified use some huge number
- sizeBounds = sizeBounds or false
- if not TextSizeCache[text] then
- TextSizeCache[text] = {}
- end
- if not TextSizeCache[text][font] then
- TextSizeCache[text][font] = {}
- end
- if not TextSizeCache[text][font][sizeBounds] then
- TextSizeCache[text][font][sizeBounds] = {}
- end
- if not TextSizeCache[text][font][sizeBounds][fontSize] then
- testLabel.Text = text
- testLabel.Font = font
- testLabel.FontSize = fontSize
- if sizeBounds then
- testLabel.TextWrapped = true;
- testLabel.Size = sizeBounds
- else
- testLabel.TextWrapped = false;
- end
- TextSizeCache[text][font][sizeBounds][fontSize] = testLabel.TextBounds
- end
- return TextSizeCache[text][font][sizeBounds][fontSize]
- end
- local PRINTABLE_CHARS = '[^' .. string.char(32) .. '-' .. string.char(126) .. ']'
- local WHITESPACE_CHARS = '(' .. string.rep('%s', 7) .. ')%s+'
- function Util.FilterUnprintableCharacters(str)
- if not GetLuaChatFilteringFlag() then
- return str
- end
- local result = str:gsub(PRINTABLE_CHARS, '');
- result = str:gsub(WHITESPACE_CHARS, '%1');
- return result
- end
- end
- local SelectChatModeEvent = Util.Signal()
- local SelectPlayerEvent = Util.Signal()
- local function CreateChatMessage()
- local this = {}
- this.FadeRoutines = {}
- function this:GetMessageFontSize(settings)
- return Util.IsSmallScreenSize() and settings.SmallScreenFontSize or settings.FontSize
- end
- function this:OnResize()
- -- Nothing!
- end
- function this:FadeIn()
- local gui = this:GetGui()
- if gui then
- gui.Visible = true
- end
- end
- function this:FadeOut()
- local gui = this:GetGui()
- if gui then
- gui.Visible = false
- end
- end
- function this:GetGui()
- return this.Container
- end
- function this:Destroy()
- if this.Container ~= nil then
- this.Container:Destroy()
- this.Container = nil
- end
- if this.FadeRoutines then
- for _, routine in pairs(this.FadeRoutines) do
- routine:Cancel()
- end
- this.FadeRoutines = {}
- end
- end
- return this
- end
- local function CreateSystemChatMessage(settings, chattedMessage)
- local this = CreateChatMessage()
- this.Settings = settings
- this.rawChatString = chattedMessage
- function this:OnResize(containerSize)
- if this.Container and this.ChatMessage then
- if InputService.VREnabled then
- this.ChatMessage.Position = UDim2.new(0, 4, 0, 0)
- this.ChatMessage.Size = UDim2.new(1, 0, 1, 0)
- end
- this.Container.Size = UDim2.new(1,0,0,1000)
- local textHeight = this.ChatMessage.TextBounds.Y
- local newContainerHeight = textHeight + 5
- this.Container.Size = UDim2.new(1,0,0,newContainerHeight)
- return newContainerHeight
- end
- end
- function this:FadeIn()
- local gui = this:GetGui()
- if gui then
- gui.Visible = true
- for _, routine in pairs(this.FadeRoutines) do
- routine:Cancel()
- end
- this.FadeRoutines = {}
- local tweenableObjects = {
- this.ChatMessage;
- }
- for _, object in pairs(tweenableObjects) do
- object.TextTransparency = 0;
- object.TextStrokeTransparency = this.Settings.TextStrokeTransparency;
- end
- if this.MessageBackgroundImage then
- this.MessageBackgroundImage.Visible = InputService.VREnabled
- end
- end
- end
- function this:FadeOut(instant)
- local gui = this:GetGui()
- if gui then
- if instant then
- gui.Visible = false
- else
- local tweenableObjects = {
- this.ChatMessage;
- }
- for _, object in pairs(tweenableObjects) do
- table.insert(this.FadeRoutines, Util.PropertyTweener(object, 'TextTransparency', object.TextTransparency, 1, 1, Util.Linear))
- table.insert(this.FadeRoutines, Util.PropertyTweener(object, 'TextStrokeTransparency', object.TextStrokeTransparency, 1, 0.85, Util.Linear))
- end
- end
- if this.MessageBackgroundImage then
- this.MessageBackgroundImage.Visible = false
- end
- end
- end
- local function CreateMessageGuiElement()
- local fontSize = this:GetMessageFontSize(this.Settings)
- local systemMessageDisplayText = this.rawChatString or ""
- local systemMessageSize = Util.GetStringTextBounds(systemMessageDisplayText, this.Settings.Font, fontSize, UDim2.new(0, 400, 0, 1000))
- local container = Util.Create'Frame'
- {
- Name = 'MessageContainer';
- Position = UDim2.new(0, 0, 0, 0);
- ZIndex = 1;
- BackgroundColor3 = Color3.new(0, 0, 0);
- BackgroundTransparency = 1;
- };
- this.MessageBackgroundImage = Util.Create'ImageLabel'
- {
- Name = 'TextEntryBackground';
- Size = UDim2.new(1,0,1,-2);
- Position = UDim2.new(0,0,0,1);
- Image = 'rbxasset://textures/ui/Chat/VRChatBackground.png';
- ScaleType = Enum.ScaleType.Slice;
- SliceCenter = Rect.new(8,8,56,56);
- BackgroundTransparency = 1;
- ImageTransparency = 0.3;
- BorderSizePixel = 0;
- ZIndex = 1;
- Visible = InputService.VREnabled;
- Parent = container;
- }
- local chatMessage = Util.Create'TextLabel'
- {
- Name = 'SystemChatMessage';
- Position = UDim2.new(0, 0, 0, 0);
- Size = UDim2.new(1, 0, 1, 0);
- Text = systemMessageDisplayText;
- ZIndex = 1;
- BackgroundColor3 = Color3.new(0, 0, 0);
- BackgroundTransparency = 1;
- TextXAlignment = Enum.TextXAlignment.Left;
- TextYAlignment = Enum.TextYAlignment.Top;
- TextWrapped = true;
- TextColor3 = this.Settings.DefaultMessageTextColor;
- FontSize = fontSize;
- Font = this.Settings.Font;
- TextStrokeColor3 = this.Settings.TextStrokeColor;
- TextStrokeTransparency = this.Settings.TextStrokeTransparency;
- Parent = container;
- };
- if InputService.VREnabled then
- chatMessage.Position = UDim2.new(0, 4, 0, 0)
- chatMessage.Size = UDim2.new(1, 0, 1, 0)
- end
- container.Size = UDim2.new(1, 0, 0, systemMessageSize.Y + 1);
- this.Container = container
- this.ChatMessage = chatMessage
- end
- CreateMessageGuiElement()
- return this
- end
- --[[ Popup Handling ]]--
- function createPopupFrame(selectedPlayer, selectedButton)
- if selectedPlayer and selectedPlayer.Parent == PlayersService then
- if lastSelectedButton ~= selectedButton then
- if lastSelectedButton ~= nil then
- lastSelectedButton.BackgroundTransparency = 1
- lastSelectedButton = nil
- end
- lastSelectedButton = selectedButton
- lastSelectedPlayer = selectedPlayer
- selectedButton.BackgroundTransparency = 0.5
- else
- lastSelectedPlayer = nil
- end
- end
- end
- function popupHidden()
- if lastSelectedButton then
- lastSelectedPlayer = nil
- lastSelectedButton.BackgroundTransparency = 1
- lastSelectedButton = nil
- end
- end
- InputService.InputBegan:connect(function(inputObject, isProcessed)
- if isProcessed then return end
- local inputType = inputObject.UserInputType
- if ((inputType == Enum.UserInputType.Touch and
- inputObject.UserInputState == Enum.UserInputState.Begin) or
- inputType == Enum.UserInputType.MouseButton1) then
- end
- end)
- --[[ End of popup handling ]]--
- local function CreatePlayerChatMessage(settings, playerChatType, sendingPlayer, chattedMessage, receivingPlayer)
- local this = CreateChatMessage()
- this.Settings = settings
- this.PlayerChatType = playerChatType
- this.SendingPlayer = sendingPlayer
- this.RawMessageContent = chattedMessage
- this.ReceivingPlayer = receivingPlayer
- this.ReceivedTime = tick()
- this.Neutral = this.SendingPlayer and this.SendingPlayer.Neutral or true
- this.TeamColor = this.SendingPlayer and this.SendingPlayer.TeamColor or BrickColor.new("White")
- function this:OnResize(containerSize)
- if this.Container and this.ChatMessage then
- this.Container.Size = UDim2.new(1,0,0,1000)
- local textHeight = this.ChatMessage.TextBounds.Y
- local newContainerHeight = textHeight + 5
- this.Container.Size = UDim2.new(1,0,0,newContainerHeight)
- return newContainerHeight
- end
- end
- function this:FormatMessage()
- local result = ""
- if this.RawMessageContent then
- local message = this.RawMessageContent
- result = message
- end
- return result
- end
- function this:FormatChatType()
- if this.PlayerChatType then
- if this.PlayerChatType == Enum.PlayerChatType.All then
- --return "[All]"
- elseif this.PlayerChatType == Enum.PlayerChatType.Team then
- return "[Team]"
- elseif this.PlayerChatType == Enum.PlayerChatType.Whisper then
- -- nothing!
- end
- end
- end
- function this:FormatPlayerNameText()
- local playerName = ""
- -- If we are sending a whisper to someone, then we should show their name
- if this.PlayerChatType == Enum.PlayerChatType.Whisper and this.SendingPlayer and this.SendingPlayer == Player then
- playerName = (this.ReceivingPlayer and this.ReceivingPlayer.Name or "")
- else
- playerName = (this.SendingPlayer and this.SendingPlayer.Name or "")
- end
- return "[" .. playerName .. "]:"
- end
- function this:FadeIn()
- local gui = this:GetGui()
- if gui then
- gui.Visible = true
- for _, routine in pairs(this.FadeRoutines) do
- routine:Cancel()
- end
- this.FadeRoutines = {}
- local tweenableObjects = {
- this.WhisperToText;
- this.WhisperFromText;
- this.ChatModeButton;
- this.UserNameButton;
- this.ChatMessage;
- }
- for _, object in pairs(tweenableObjects) do
- object.TextTransparency = 0;
- object.TextStrokeTransparency = this.Settings.TextStrokeTransparency;
- object.Active = true
- end
- if this.UserNameDot then
- this.UserNameDot.ImageTransparency = 0
- end
- if this.MessageBackgroundImage then
- this.MessageBackgroundImage.Visible = InputService.VREnabled
- end
- end
- end
- function this:FadeOut(instant)
- local gui = this:GetGui()
- if gui then
- if instant then
- gui.Visible = false
- else
- local tweenableObjects = {
- this.WhisperToText;
- this.WhisperFromText;
- this.ChatModeButton;
- this.UserNameButton;
- this.ChatMessage;
- }
- for _, object in pairs(tweenableObjects) do
- table.insert(this.FadeRoutines, Util.PropertyTweener(object, 'TextTransparency', object.TextTransparency, 1, 1, Util.Linear))
- table.insert(this.FadeRoutines, Util.PropertyTweener(object, 'TextStrokeTransparency', object.TextStrokeTransparency, 1, 0.85, Util.Linear))
- object.Active = false
- end
- if this.UserNameDot then
- table.insert(this.FadeRoutines, Util.PropertyTweener(this.UserNameDot, 'ImageTransparency', this.UserNameDot.ImageTransparency, 1, 1, Util.Linear))
- end
- end
- if this.MessageBackgroundImage then
- this.MessageBackgroundImage.Visible = false
- end
- end
- end
- function this:Destroy()
- if this.Container ~= nil then
- this.Container:Destroy()
- this.Container = nil
- end
- this.ClickedOnModeConn = Util.DisconnectEvent(this.ClickedOnModeConn)
- this.ClickedOnPlayerConn = Util.DisconnectEvent(this.ClickedOnPlayerConn)
- end
- local function CreateMessageGuiElement()
- local fontSize = this:GetMessageFontSize(this.Settings)
- local toMesasgeDisplayText = "To "
- local toMessageSize = Util.GetStringTextBounds(toMesasgeDisplayText, this.Settings.Font, fontSize)
- local fromMesasgeDisplayText = "From "
- local fromMessageSize = Util.GetStringTextBounds(fromMesasgeDisplayText, this.Settings.Font, fontSize)
- local chatTypeDisplayText = this:FormatChatType()
- local chatTypeSize = chatTypeDisplayText and Util.GetStringTextBounds(chatTypeDisplayText, this.Settings.Font, fontSize) or Vector2.new(0,0)
- local playerNameDisplayText = this:FormatPlayerNameText()
- local playerNameSize = Util.GetStringTextBounds(playerNameDisplayText, this.Settings.Font, fontSize)
- local singleSpaceSize = Util.GetStringTextBounds(" ", this.Settings.Font, fontSize)
- local numNeededSpaces = math.ceil(playerNameSize.X / singleSpaceSize.X) + 1
- local chatMessageDisplayText = string.rep(" ", numNeededSpaces) .. this:FormatMessage()
- local chatMessageSize = Util.GetStringTextBounds(chatMessageDisplayText, this.Settings.Font, fontSize, UDim2.new(0, 400 - 5 - playerNameSize.X, 0, 1000))
- local playerColor = this.Settings.DefaultMessageTextColor
- if this.SendingPlayer then
- if this.PlayerChatType == Enum.PlayerChatType.Whisper then
- if this.SendingPlayer == Player and this.ReceivingPlayer then
- playerColor = Util.ComputeChatColor(this.ReceivingPlayer.Name)
- else
- playerColor = Util.ComputeChatColor(this.SendingPlayer.Name)
- end
- else
- if this.SendingPlayer.Neutral then
- playerColor = Util.ComputeChatColor(this.SendingPlayer.Name)
- else
- playerColor = this.SendingPlayer.TeamColor.Color
- end
- end
- end
- local container = Util.Create'Frame'
- {
- Name = 'MessageContainer';
- Position = UDim2.new(0, 0, 0, 0);
- ZIndex = 1;
- BackgroundColor3 = Color3.new(0, 0, 0);
- BackgroundTransparency = 1;
- };
- this.MessageBackgroundImage = Util.Create'ImageLabel'
- {
- Name = 'TextEntryBackground';
- Size = UDim2.new(1,0,1,-2);
- Position = UDim2.new(0,0,0,1);
- Image = 'rbxasset://textures/ui/Chat/VRChatBackground.png';
- ScaleType = Enum.ScaleType.Slice;
- SliceCenter = Rect.new(8,8,56,56);
- BackgroundTransparency = 1;
- ImageTransparency = 0.3;
- BorderSizePixel = 0;
- ZIndex = 1;
- Visible = InputService.VREnabled;
- Parent = container;
- }
- local xOffset = InputService.VREnabled and 4 or 0
- if this.SendingPlayer and this.SendingPlayer == Player and this.PlayerChatType == Enum.PlayerChatType.Whisper then
- local whisperToText = Util.Create'TextLabel'
- {
- Name = 'WhisperTo';
- Position = UDim2.new(0, 0, 0, 0);
- Size = UDim2.new(0, toMessageSize.X, 0, toMessageSize.Y);
- Text = toMesasgeDisplayText;
- ZIndex = 1;
- BackgroundColor3 = Color3.new(0, 0, 0);
- BackgroundTransparency = 1;
- TextXAlignment = Enum.TextXAlignment.Left;
- TextYAlignment = Enum.TextYAlignment.Top;
- TextWrapped = true;
- TextColor3 = this.Settings.DefaultMessageTextColor;
- FontSize = fontSize;
- Font = this.Settings.Font;
- TextStrokeColor3 = this.Settings.TextStrokeColor;
- TextStrokeTransparency = this.Settings.TextStrokeTransparency;
- Parent = container;
- };
- xOffset = xOffset + toMessageSize.X
- this.WhisperToText = whisperToText
- elseif this.SendingPlayer and this.SendingPlayer ~= Player and this.PlayerChatType == Enum.PlayerChatType.Whisper then
- local whisperFromText = Util.Create'TextLabel'
- {
- Name = 'WhisperFromText';
- Position = UDim2.new(0, 0, 0, 0);
- Size = UDim2.new(0, fromMessageSize.X, 0, fromMessageSize.Y);
- Text = fromMesasgeDisplayText;
- ZIndex = 1;
- BackgroundColor3 = Color3.new(0, 0, 0);
- BackgroundTransparency = 1;
- TextXAlignment = Enum.TextXAlignment.Left;
- TextYAlignment = Enum.TextYAlignment.Top;
- TextWrapped = true;
- TextColor3 = this.Settings.DefaultMessageTextColor;
- FontSize = fontSize;
- Font = this.Settings.Font;
- TextStrokeColor3 = this.Settings.TextStrokeColor;
- TextStrokeTransparency = this.Settings.TextStrokeTransparency;
- Parent = container;
- };
- xOffset = xOffset + fromMessageSize.X
- this.WhisperFromText = whisperFromText
- end
- if chatTypeDisplayText then
- local chatModeButton = Util.Create(Util.IsTouchDevice() and 'TextLabel' or 'TextButton')
- {
- Name = 'ChatMode';
- BackgroundTransparency = 1;
- ZIndex = 2;
- Text = chatTypeDisplayText;
- TextColor3 = this.Settings.DefaultMessageTextColor;
- Position = UDim2.new(0, xOffset, 0, 0);
- TextXAlignment = Enum.TextXAlignment.Left;
- TextYAlignment = Enum.TextYAlignment.Top;
- FontSize = fontSize;
- Font = this.Settings.Font;
- Size = UDim2.new(0, chatTypeSize.X, 0, chatTypeSize.Y);
- TextStrokeColor3 = this.Settings.TextStrokeColor;
- TextStrokeTransparency = this.Settings.TextStrokeTransparency;
- Parent = container
- }
- if chatModeButton:IsA('TextButton') then
- this.ClickedOnModeConn = chatModeButton.MouseButton1Click:connect(function()
- SelectChatModeEvent:fire(this.PlayerChatType)
- end)
- end
- if this.PlayerChatType == Enum.PlayerChatType.Team then
- chatModeButton.TextColor3 = playerColor
- end
- xOffset = xOffset + chatTypeSize.X + 1
- this.ChatModeButton = chatModeButton
- end
- local userNameButton = Util.Create(Util.IsTouchDevice() and 'TextLabel' or 'TextButton')
- {
- Name = 'PlayerName';
- BackgroundTransparency = 1;
- BackgroundColor3 = Color3.new(0, 1, 1);
- BorderSizePixel = 0;
- ZIndex = 2;
- Text = playerNameDisplayText;
- TextColor3 = playerColor;
- Position = UDim2.new(0, xOffset, 0, 0);
- TextXAlignment = Enum.TextXAlignment.Left;
- TextYAlignment = Enum.TextYAlignment.Top;
- FontSize = fontSize;
- Font = this.Settings.Font;
- Size = UDim2.new(0, playerNameSize.X, 0, playerNameSize.Y);
- TextStrokeColor3 = this.Settings.TextStrokeColor;
- TextStrokeTransparency = this.Settings.TextStrokeTransparency;
- Parent = container
- }
- if userNameButton:IsA('TextButton') then
- this.ClickedOnPlayerConn = userNameButton.MouseButton1Click:connect(function()
- local gui = this:GetGui()
- if gui and gui.Visible then
- if this.PlayerChatType == Enum.PlayerChatType.Whisper and this.SendingPlayer == Player and this.ReceivingPlayer then
- SelectPlayerEvent:fire(this.ReceivingPlayer)
- else
- SelectPlayerEvent:fire(this.SendingPlayer)
- end
- end
- end)
- end
- local chatMessage = Util.Create'TextLabel'
- {
- Name = 'ChatMessage';
- Position = UDim2.new(0, xOffset, 0, 0);
- Size = UDim2.new(1, -xOffset, 1, 0);
- Text = chatMessageDisplayText;
- ZIndex = 1;
- BackgroundColor3 = Color3.new(0, 0, 0);
- BackgroundTransparency = 1;
- TextXAlignment = Enum.TextXAlignment.Left;
- TextYAlignment = Enum.TextYAlignment.Top;
- TextWrapped = true;
- TextColor3 = this.Settings.DefaultMessageTextColor;
- FontSize = fontSize;
- Font = this.Settings.Font;
- TextStrokeColor3 = this.Settings.TextStrokeColor;
- TextStrokeTransparency = this.Settings.TextStrokeTransparency;
- Parent = container;
- };
- if InputService.VREnabled then
- chatMessage.Size = chatMessage.Size - UDim2.new(0,4,0,0)
- end
- -- Check if they got moderated and put up a real message instead of Label
- if chatMessage.Text == 'Label' and chatMessageDisplayText ~= 'Label' then
- chatMessage.Text = string.rep(" ", numNeededSpaces) .. '[Content Deleted]'
- end
- if this.SendingPlayer then
- if PlayerPermissionsModule.IsPlayerAdminAsync(this.SendingPlayer) then
- chatMessage.TextColor3 = this.Settings.AdminTextColor
- elseif PlayerPermissionsModule.IsPlayerInternAsync(this.SendingPlayer) then
- chatMessage.TextColor3 = this.Settings.InternTextColor
- end
- end
- chatMessage.Size = chatMessage.Size + UDim2.new(0, 0, 0, chatMessage.TextBounds.Y);
- container.Size = UDim2.new(1, 0, 0, math.max(chatMessageSize.Y + 1, userNameButton.Size.Y.Offset + 1));
- this.Container = container
- this.ChatMessage = chatMessage
- this.UserNameButton = userNameButton
- end
- CreateMessageGuiElement()
- return this
- end
- local function CreateChatBarWidget(settings)
- local this = {}
- -- MessageModes: {All, Team, Whisper}
- this.MessageMode = 'All'
- this.TargetWhisperPlayer = nil
- this.Settings = settings
- this.WidgetVisible = false
- this.FadedIn = true
- this.ChatBarGainedFocusEvent = Util.Signal()
- this.ChatBarLostFocusEvent = Util.Signal()
- this.ChatCommandEvent = Util.Signal() -- Signal Signatue: success, actionType, [captures]
- this.ChatErrorEvent = Util.Signal() -- Signal Signatue: success, actionType, [captures]
- this.ChatBarFloodEvent = Util.Signal()
- this.unfocusedAt = 0
- local chatCoreGuiEnabled = true
- -- This function while lets string.find work case-insensitively without clobbering the case of the captures
- local function nocase(s)
- s = string.gsub(s, "%a", function (c)
- return string.format("[%s%s]", string.lower(c),
- string.upper(c))
- end)
- return s
- end
- this.ChatMatchingRegex =
- {
- [function(chatBarText) return string.find(chatBarText, nocase("^/w ") .. "(%w+_?%w+)") end] = "Whisper";
- [function(chatBarText) return string.find(chatBarText, nocase("^/whisper ") .. "(%w+_?%w+)") end] = "Whisper";
- [function(chatBarText) return string.find(chatBarText, "^%%") end] = "Team";
- [function(chatBarText) return string.find(chatBarText, "^%(TEAM%)") end] = "Team";
- [function(chatBarText) return string.find(chatBarText, nocase("^/t")) end] = "Team";
- [function(chatBarText) return string.find(chatBarText, nocase("^/team")) end] = "Team";
- [function(chatBarText) return string.find(chatBarText, nocase("^/a")) end] = "All";
- [function(chatBarText) return string.find(chatBarText, nocase("^/all")) end] = "All";
- [function(chatBarText) return string.find(chatBarText, nocase("^/s")) end] = "All";
- [function(chatBarText) return string.find(chatBarText, nocase("^/say")) end] = "All";
- [function(chatBarText) return string.find(chatBarText, nocase("^/e")) end] = "Emote";
- [function(chatBarText) return string.find(chatBarText, nocase("^/emote")) end] = "Emote";
- [function(chatBarText) return string.find(chatBarText, "^/%?") end] = "Help";
- [function(chatBarText) return string.find(chatBarText, nocase("^/help")) end] = "Help";
- [function(chatBarText) return string.find(chatBarText, nocase("^/block ") .. "(%w+_?%w+)") end] = "Block";
- [function(chatBarText) return string.find(chatBarText, nocase("^/unblock ") .. "(%w+_?%w+)") end] = "Unblock";
- [function(chatBarText) return string.find(chatBarText, nocase("^/mute ") .. "(%w+_?%w+)") end] = "Mute";
- [function(chatBarText) return string.find(chatBarText, nocase("^/unmute ") .. "(%w+_?%w+)") end] = "Unmute";
- }
- local ChatModesDict =
- {
- ['Whisper'] = 'Whisper';
- ['Team'] = 'Team';
- ['All'] = 'All';
- [Enum.PlayerChatType.Whisper] = 'Whisper';
- [Enum.PlayerChatType.Team] = 'Team';
- [Enum.PlayerChatType.All] = 'All';
- }
- local function TearDownEvents()
- this.ClickToChatButtonConn = Util.DisconnectEvent(this.ClickToChatButtonConn)
- this.ChatBarFocusLostConn = Util.DisconnectEvent(this.ChatBarFocusLostConn)
- this.ChatBarLostFocusConn = Util.DisconnectEvent(this.ChatBarLostFocusConn)
- this.SelectChatModeConn = Util.DisconnectEvent(this.SelectChatModeConn)
- this.SelectPlayerConn = Util.DisconnectEvent(this.SelectPlayerConn)
- this.FocusChatBarInputBeganConn = Util.DisconnectEvent(this.FocusChatBarInputBeganConn)
- this.InputBeganConn = Util.DisconnectEvent(this.InputBeganConn)
- this.ChatBarChangedConn = Util.DisconnectEvent(this.ChatBarChangedConn)
- end
- local function HookUpEvents()
- TearDownEvents() -- Cleanup old events
- if this.ClickToChatButton then this.ClickToChatButtonConn = this.ClickToChatButton.MouseButton1Click:connect(function() this:FocusChatBar() end) end
- if this.ChatBar then
- -- Use a count to check for double backspace out of a chatmode
- local count = 0
- if not Util.IsTouchDevice() then
- this.FocusChatBarInputBeganConn = Util.DisconnectEvent(this.FocusChatBarInputBeganConn)
- this.FocusChatBarInputBeganConn = InputService.InputBegan:connect(function(inputObj)
- if inputObj.KeyCode == Enum.KeyCode.Backspace and this:GetChatBarText() == "" then
- if count == 0 then
- count = count + 1
- else
- this:SetMessageMode('All')
- end
- else
- count = 0
- end
- end)
- end
- this.ChatBarFocusLostConn = this.ChatBar.FocusLost:connect(function(...)
- count = 0
- this.unfocusedAt = tick()
- this.ChatBarLostFocusEvent:fire(...)
- end)
- this.ChatBarChangedConn = this.ChatBar.Changed:connect(function(prop)
- if prop == "Text" then
- this:OnChatBarTextChanged()
- elseif prop == 'TextFits' or prop == 'TextBounds' or prop == 'Visible' then
- this:OnChatBarBoundsChanged()
- end
- end)
- end
- if this.ChatBarLostFocusEvent then this.ChatBarLostFocusConn = this.ChatBarLostFocusEvent:connect(function(...) this:OnChatBarFocusLost(...) end) end
- this.SelectChatModeConn = SelectChatModeEvent:connect(function(chatType)
- this:SetMessageMode(chatType)
- this:FocusChatBar()
- end)
- this.SelectPlayerConn = SelectPlayerEvent:connect(function(chatPlayer)
- this.TargetWhisperPlayer = chatPlayer
- this:SetMessageMode("Whisper")
- this:FocusChatBar()
- end)
- this.InputBeganConn = InputService.InputBegan:connect(function(inputObject)
- if inputObject.KeyCode == Enum.KeyCode.Escape then
- -- Clear text when they press escape
- this:SetChatBarText("")
- end
- end)
- end
- function this:CalculateVisibility()
- if this.ChatBarContainer then
- local enabled = self.WidgetVisible and chatCoreGuiEnabled and not NON_CORESCRIPT_MODE
- if enabled then
- HookUpEvents()
- else
- TearDownEvents()
- end
- this.ChatBarContainer.Visible = enabled and self.FadedIn and (not chatBarDisabled)
- end
- end
- function this:ToggleVisibility(visible)
- if visible ~= self.WidgetVisible then
- self.WidgetVisible = visible
- self:CalculateVisibility()
- end
- if NON_CORESCRIPT_MODE or chatBarDisabled then
- this.ChatBarContainer.Visible = false
- end
- end
- function this:FadeIn()
- self.FadedIn = true
- self:CalculateVisibility()
- end
- function this:FadeOut()
- self.FadedIn = false
- self:CalculateVisibility()
- end
- function this:CoreGuiChanged(coreGuiType, enabled)
- if coreGuiType == Enum.CoreGuiType.Chat or coreGuiType == Enum.CoreGuiType.All then
- chatCoreGuiEnabled = enabled
- self:CalculateVisibility()
- end
- end
- function this:IsAChatMode(mode)
- return ChatModesDict[mode] ~= nil
- end
- function this:ProcessChatBarModes(requireWhitespaceAfterChatMode)
- local matchedAChatCommand = false
- if this.ChatBar then
- local chatBarText = this:SanitizeInput(this:GetChatBarText())
- for regexFunc, actionType in pairs(this.ChatMatchingRegex) do
- local start, finish, capture = regexFunc(chatBarText)
- if start and finish then
- -- The following line is for whether or not to try setting the chatmode as-you-type
- -- versus when you press enter.
- local whitespaceAfterSlashCommand = string.find(string.sub(chatBarText, finish+1, finish+1), "%s")
- if (not requireWhitespaceAfterChatMode and finish == #chatBarText) or whitespaceAfterSlashCommand then
- if this:IsAChatMode(actionType) then
- if actionType == "Whisper" then
- local targetPlayer = capture and Util.GetPlayerByName(capture)
- if targetPlayer then --and targetPlayer ~= Player then
- this.TargetWhisperPlayer = targetPlayer
- -- start from two over to eat the space or tab character after the slash command
- this:SetChatBarText(string.sub(chatBarText, finish + 2))
- this:SetMessageMode(actionType)
- this.ChatCommandEvent:fire(true, actionType, capture)
- else
- -- This is an indirect way of detecting if they used enter to close submit this chat
- if not requireWhitespaceAfterChatMode then
- this:SetChatBarText("")
- this.ChatCommandEvent:fire(false, actionType, capture)
- end
- end
- else
- -- start from two over to eat the space or tab character after the slash command
- this:SetChatBarText(string.sub(chatBarText, finish + 2))
- this:SetMessageMode(actionType)
- this.ChatCommandEvent:fire(true, actionType, capture)
- end
- elseif actionType == "Emote" then
- -- You can only emote to everyone.
- this:SetMessageMode('All')
- elseif not requireWhitespaceAfterChatMode then -- Some non-chat related command
- if actionType == "Help" then
- this:SetChatBarText("") -- Clear the chat so we don't send /? to everyone
- end
- this.ChatCommandEvent:fire(true, actionType, capture)
- end
- -- should we break here since we already matched a slash command or keep going?
- matchedAChatCommand = true
- end
- end
- end
- end
- return matchedAChatCommand
- end
- local previousText = ""
- function this:OnChatBarTextChanged()
- if not Util.IsTouchDevice() then
- this:ProcessChatBarModes(true)
- local originalText = this:GetChatBarText()
- local newText = Util.FilterUnprintableCharacters(originalText)
- if newText ~= originalText then
- previousText = newText
- end
- local fixedText = newText
- if #newText > this.Settings.MaxCharactersInMessage or originalText ~= newText then
- -- This is a hack to deal with the bug that holding down a key for repeated input doesn't trigger the textChanged event
- if #newText == #previousText + 1 then
- fixedText = string.sub(previousText, 1, this.Settings.MaxCharactersInMessage)
- else
- fixedText = string.sub(newText, 1, this.Settings.MaxCharactersInMessage)
- end
- end
- this:SetChatBarText(fixedText)
- previousText = fixedText
- end
- end
- function this:OnChatBarBoundsChanged()
- if this.ChatBarContainer and this.ChatBar then
- local currSize = this.ChatBarContainer.Size
- if this.ChatBar.Visible and not this.ChatBar.TextFits then
- local textBounds = Util.GetStringTextBounds(this.ChatBar.Text, this.ChatBar.Font, this.ChatBar.FontSize, UDim2.new(0, this.ChatBar.AbsoluteSize.X, 0, 1000))
- if textBounds.Y <= 36 then
- this.ChatBarContainer.Size = UDim2.new(currSize.X.Scale, currSize.X.Offset, currSize.Y.Scale, 58)
- else --if currSize.Y.Offset <= 54 then
- this.ChatBarContainer.Size = UDim2.new(currSize.X.Scale, currSize.X.Offset, currSize.Y.Scale, 76)
- end
- elseif this.ChatBar.Visible == false or this.ChatBar.TextBounds.Y <= 18 then
- if currSize.Y.Offset ~= 40 then
- this.ChatBarContainer.Size = UDim2.new(currSize.X.Scale, currSize.X.Offset, currSize.Y.Scale, 40)
- end
- elseif this.ChatBar.TextBounds.Y <= 36 then
- this.ChatBarContainer.Size = UDim2.new(currSize.X.Scale, currSize.X.Offset, currSize.Y.Scale, 58)
- end
- end
- end
- function this:GetChatBarText()
- return this.ChatBar and this.ChatBar.Text or ""
- end
- function this:SetChatBarText(newText)
- if this.ChatBar and newText ~= this.ChatBar.Text then
- this.ChatBar.Text = newText
- end
- end
- function this:GetMessageMode()
- return this.MessageMode
- end
- function this:SetMessageMode(newMessageMode)
- newMessageMode = ChatModesDict[newMessageMode]
- local chatRecipientText = "[" .. (this.TargetWhisperPlayer and this.TargetWhisperPlayer.Name or "") .. "]"
- if this.MessageMode ~= newMessageMode or (newMessageMode == 'Whisper' and this.ChatModeText and chatRecipientText ~= this.ChatModeText.Text) then
- if this.ChatModeText then
- this.MessageMode = newMessageMode
- if newMessageMode == 'Whisper' then
- local chatRecipientTextBounds = Util.GetStringTextBounds(chatRecipientText, this.ChatModeText.Font, this.ChatModeText.FontSize)
- this.ChatModeText.TextColor3 = this.Settings.WhisperTextColor
- this.ChatModeText.Text = chatRecipientText
- this.ChatModeText.Size = UDim2.new(0, chatRecipientTextBounds.X, 1, 0)
- elseif newMessageMode == 'Team' then
- local chatTeamText = '[Team]'
- local chatTeamTextBounds = Util.GetStringTextBounds(chatTeamText, this.ChatModeText.Font, this.ChatModeText.FontSize)
- this.ChatModeText.TextColor3 = this.Settings.TeamTextColor
- this.ChatModeText.Text = "[Team]"
- this.ChatModeText.Size = UDim2.new(0, chatTeamTextBounds.X, 1, 0)
- else
- this.ChatModeText.Text = ""
- this.ChatModeText.Size = UDim2.new(0, 0, 1, 0)
- end
- if this.ChatBar then
- local offset = this.ChatModeText.Size.X.Offset + this.ChatModeText.Position.X.Offset
- this.ChatBar.Size = UDim2.new(1, -14 - offset, 1, 0)
- this.ChatBar.Position = UDim2.new(0, 7 + offset, 0, 0)
- end
- end
- end
- end
- function this:FocusChatBar()
- if this.ChatBar and not chatBarDisabled then
- this.ChatBar.Visible = true
- this.ChatBar:CaptureFocus()
- if self.ClickToChatButton then
- self.ClickToChatButton.Visible = false
- end
- if this.ChatModeText then
- this.ChatModeText.Visible = true
- end
- if Util.IsTouchDevice() or InputService.VREnabled then
- this:SetMessageMode('All') -- Don't remember message mode on mobile devices or VR
- end
- -- Update chatbar properties when chatbar is focused
- this:OnChatBarBoundsChanged()
- if this.ChatBarContainer then
- if self.ChatBarInnerBackground then
- self.ChatBarInnerBackground.BackgroundTransparency = 0
- end
- end
- this.ChatBarGainedFocusEvent:fire()
- end
- end
- function this:RemoveFocus()
- if self:IsFocused() then
- self.ChatBar:ReleaseFocus()
- end
- end
- function this:IsFocused()
- return self.ChatBar and self.ChatBar == InputService:GetFocusedTextBox()
- end
- function this:WasFocused()
- return (tick() - this.unfocusedAt) < VR_CHAT_CLICK_DEBOUNCE
- end
- function this:SanitizeInput(input)
- local sanitizedInput = input
- -- Chomp the whitespace at the front and end of the string
- -- TODO: maybe only chop off the front space if there are more than a few?
- local _, _, capture = string.find(sanitizedInput, "^%s*(.*)%s*$")
- sanitizedInput = capture or ""
- return sanitizedInput
- end
- local sentMessageTimeQueue = {}
- function this:FloodCheck()
- if not GetLuaChatFilteringFlag() then
- return false
- end
- while sentMessageTimeQueue[1] and tick() - sentMessageTimeQueue[1] > FLOOD_CHECK_MESSAGE_INTERVAL do
- table.remove(sentMessageTimeQueue, 1)
- end
- if #sentMessageTimeQueue > FLOOD_CHECK_MESSAGE_COUNT then
- return true
- end
- return false
- end
- function this:OnChatBarFocusLost(enterPressed)
- if self.ChatBar then
- self.ChatBar.Visible = false
- if enterPressed then
- local didMatchSlashCommand = self:ProcessChatBarModes(false)
- local cText = self:SanitizeInput(self:GetChatBarText())
- if cText ~= "" then
- if self:FloodCheck() then -- and not didMatchSlashCommand then
- self.ChatBarFloodEvent:fire()
- else
- -- For now we will let any slash command go through, NOTE: these will show up in bubble-chat
- --if not didMatchSlashCommand and string.sub(cText,1,1) == "/" then
- -- self.ChatCommandEvent:fire(false, "Unknown", cText)
- --else
- local currentMessageMode = self:GetMessageMode()
- -- {All, Team, Whisper}
- if currentMessageMode == 'Team' then
- if Player and Player.Neutral == true then
- self.ChatErrorEvent:fire("You're not on a team.")
- else
- pcall(function() PlayersService:TeamChat(cText) end)
- end
- elseif currentMessageMode == 'Whisper' then
- if self.TargetWhisperPlayer then
- if self.TargetWhisperPlayer == Player then
- self.ChatErrorEvent:fire("You cannot send a whisper to yourself.")
- else
- pcall(function() PlayersService:WhisperChat(cText, self.TargetWhisperPlayer) end)
- end
- else
- self.ChatErrorEvent:fire("Invalid whisper target.")
- end
- elseif currentMessageMode == 'All' then
- pcall(function() PlayersService:Chat(cText) end)
- else
- spawn(function() error("ChatScript: Unknown Message Mode of " .. tostring(currentMessageMode)) end)
- end
- table.insert(sentMessageTimeQueue, tick())
- --end
- self:SetChatBarText("")
- end
- end
- end
- end
- if self.ClickToChatButton then
- self.ClickToChatButton.Visible = true
- -- Fade-back in the text so it doesn't abruptly appear
- -- Normally I would like to cancel the old tween but it is so short that it doesn't matter
- self.ClickToChatButton.TextTransparency = 1
- Util.PropertyTweener(self.ClickToChatButton, 'TextTransparency', 1, 0, 0.25, Util.Linear)
- end
- if self.ChatModeText then
- self.ChatModeText.Visible = false
- end
- if this.ChatBarContainer then
- local currSize = this.ChatBarContainer.Size
- this.ChatBarContainer.Size = UDim2.new(currSize.X.Scale, currSize.X.Offset, currSize.Y.Scale, 32)
- if self.ChatBarInnerBackground then
- self.ChatBarInnerBackground.BackgroundTransparency = 0.5
- end
- end
- this.ChatBarChangedConn = Util.DisconnectEvent(this.ChatBarChangedConn)
- this.FocusChatBarInputBeganConn = Util.DisconnectEvent(this.FocusChatBarInputBeganConn)
- end
- local function CreateChatBar()
- local chatBarContainer = Util.Create'Frame'
- {
- Name = 'ChatBarContainer';
- Position = UDim2.new(0, 0, 1, 0);
- Size = UDim2.new(1, 0, 0, 20);
- ZIndex = 1;
- BackgroundColor3 = Color3.new(0, 0, 0);
- BackgroundTransparency = 0.25;
- BorderSizePixel = 0;
- };
- chatBarContainer.BackgroundColor3 = Color3.new(31/255, 31/255, 31/255);
- chatBarContainer.BackgroundTransparency = 0.5;
- local chatBarInnerBackground = Util.Create'Frame'
- {
- Name = 'InnerBackground';
- Position = UDim2.new(0, 7, 0, 5);
- Size = UDim2.new(1, -14, 1, -10);
- ZIndex = 1;
- BackgroundColor3 = Color3.new(209/255, 216/255, 221/255);
- BackgroundTransparency = 0.5;
- BorderSizePixel = 0;
- };
- local clickToChatButton = Util.Create'TextButton'
- {
- Name = 'ClickToChat';
- Position = UDim2.new(0,9,0,0);
- Size = UDim2.new(1, -9, 1, 0);
- BackgroundTransparency = 1;
- AutoButtonColor = false;
- ZIndex = 3;
- Text = 'To chat click here or press "/" key';
- TextColor3 = this.Settings.GlobalTextColor;
- TextXAlignment = Enum.TextXAlignment.Left;
- TextYAlignment = Enum.TextYAlignment.Top;
- Font = Enum.Font.SourceSansBold;
- FontSize = Enum.FontSize.Size18;
- Parent = chatBarContainer;
- }
- clickToChatButton.TextWrapped = true;
- clickToChatButton.Position = UDim2.new(0, 7, 0, 0);
- clickToChatButton.Size = UDim2.new(1, -14, 1, 0);
- clickToChatButton.TextYAlignment = Enum.TextYAlignment.Center;
- if Util.IsTouchDevice() then
- clickToChatButton.Text = "Tap here to chat"
- end
- local chatBar = Util.Create'TextBox'
- {
- Name = 'ChatBar';
- Position = UDim2.new(0, 9, 0, 0);
- Size = UDim2.new(1, -9, 1, 0);
- Text = "";
- ZIndex = 1;
- BackgroundColor3 = Color3.new(0, 0, 0);
- Active = false;
- BackgroundTransparency = 1;
- TextXAlignment = Enum.TextXAlignment.Left;
- TextYAlignment = Enum.TextYAlignment.Top;
- TextColor3 = this.Settings.GlobalTextColor;
- Font = Enum.Font.SourceSansBold;
- FontSize = Enum.FontSize.Size18;
- ClearTextOnFocus = false;
- Visible = not Util.IsTouchDevice();
- Parent = chatBarContainer;
- SelectionImageObject = emptySelectionImage;
- }
- chatBar.TextWrapped = true;
- chatBar.Position = UDim2.new(0, 7, 0, 0);
- chatBar.Size = UDim2.new(1, -14, 1, 0);
- chatBar.TextYAlignment = Enum.TextYAlignment.Center;
- chatBar.Visible = false;
- local chatModeText = Util.Create'TextButton'
- {
- Name = 'ChatModeText';
- Position = UDim2.new(0, 9, 0, 0);
- Size = UDim2.new(1, -9, 1, 0);
- AutoButtonColor = false;
- BackgroundTransparency = 1;
- ZIndex = 2;
- Text = '';
- TextColor3 = this.Settings.WhisperTextColor;
- TextXAlignment = Enum.TextXAlignment.Left;
- TextYAlignment = Enum.TextYAlignment.Top;
- Font = Enum.Font.SourceSansBold;
- FontSize = Enum.FontSize.Size18;
- Parent = chatBarContainer;
- }
- chatModeText.Position = UDim2.new(0, 7, 0, 0);
- chatModeText.Size = UDim2.new(1, -14, 1, 0);
- chatModeText.TextYAlignment = Enum.TextYAlignment.Center;
- -- Create grey background for text
- chatBarInnerBackground.Parent = chatBarContainer;
- clickToChatButton.Parent = chatBarInnerBackground;
- chatBar.Parent = chatBarInnerBackground;
- chatModeText.Parent = chatBarInnerBackground;
- this.ChatBarContainer = chatBarContainer
- this.ChatBarInnerBackground = chatBarInnerBackground
- this.ClickToChatButton = clickToChatButton
- this.ChatBar = chatBar
- this.ChatModeText = chatModeText
- this.ChatBarContainer.Parent = GuiRoot
- local function UpdateChatBarContainerLayout(newSize)
- if chatBarContainer then
- local chatbarVisible = this.ChatBar and this.ChatBar.Visible
- local bubbleChatIsOn = not PlayersService.ClassicChat and PlayersService.BubbleChat
- -- Phone
- if newSize.X <= PHONE_SCREEN_WIDTH then
- chatBarContainer.Size = UDim2.new(0.5, 0,0, chatbarVisible and 40 or 32)
- if bubbleChatIsOn then
- chatBarContainer.Position = UDim2.new(0, 0, 0, 2)
- else
- chatBarContainer.Position = UDim2.new(0, 0, 0.5, 2)
- end
- -- Tablet
- elseif newSize.X <= TABLET_SCREEN_WIDTH then
- chatBarContainer.Size = UDim2.new(0.4, 0,0, chatbarVisible and 40 or 32)
- if bubbleChatIsOn then
- chatBarContainer.Position = UDim2.new(0, 0, 0, 2)
- else
- chatBarContainer.Position = UDim2.new(0, 0, 0.3, 2)
- end
- -- Desktop
- else
- chatBarContainer.Size = UDim2.new(0.3, 0,0, chatbarVisible and 40 or 32)
- if bubbleChatIsOn then
- chatBarContainer.Position = UDim2.new(0, 0, 0, 2)
- else
- chatBarContainer.Position = UDim2.new(0,0,0.25, 2)
- end
- end
- if Util.IsTouchDevice() or InputService.VREnabled then
- -- Hide the chatbar on mobile and in VR so they can't see it.
- chatBarContainer.Position = UDim2.new(0,0,1,20);
- end
- end
- end
- GuiRoot.Changed:connect(function(prop)
- if (prop == "AbsoluteSize" and not chatRepositioned) then
- UpdateChatBarContainerLayout(GuiRoot.AbsoluteSize)
- end
- end)
- UpdateChatBarContainerLayout(GuiRoot.AbsoluteSize)
- end
- CreateChatBar()
- return this
- end
- local function CreateChatWindowWidget(settings)
- local this = {}
- this.Settings = settings
- this.Chats = {}
- this.BackgroundVisible = false
- this.ChatsVisible = false
- this.WidgetVisible = false
- this.NewUnreadMessage = false
- this.MessageCount = 0
- this.MessageCountChanged = Util.Signal()
- this.FadeInSignal = Util.Signal()
- this.FadeOutSignal = Util.Signal()
- this.ChatWindowPagingConn = nil
- local lastMoveTime = tick()
- local lastEnterTime = tick()
- local lastLeaveTime = tick()
- local lastFadeOutTime = 0
- local lastFadeInTime = 0
- local lastChatActivity = 0
- local FadeLock = false
- local chatCoreGuiEnabled = true
- local function PointInChatWindow(pt)
- local point0 = this.ChatContainer.AbsolutePosition
- local point1 = point0 + this.ChatContainer.AbsoluteSize
- -- HACK, this is so the "ChatWindow" includes the chatbar box, TODO: refactor the fadeing code to include the chatbar
- point1 = point1 + Vector2.new(0, 34)
- return (point0.X <= pt.X and
- point1.X >= pt.X and
- point0.Y <= pt.Y and
- point1.Y >= pt.Y)
- end
- function this:IsHovering()
- if this.ChatContainer and this.LastMousePosition and self:CalculateVisibility() then
- return PointInChatWindow(this.LastMousePosition)
- end
- return false
- end
- function this:SetFadeLock(lock)
- FadeLock = lock
- end
- function this:GetFadeLock()
- return FadeLock
- end
- function this:SetCanvasPosition(newCanvasPosition)
- if this.ScrollingFrame then
- local maxSize = Vector2.new(math.max(0, this.ScrollingFrame.CanvasSize.X.Offset - this.ScrollingFrame.AbsoluteWindowSize.X),
- math.max(0, this.ScrollingFrame.CanvasSize.Y.Offset - this.ScrollingFrame.AbsoluteWindowSize.Y))
- this.ScrollingFrame.CanvasPosition = Vector2.new(Util.Clamp(0, maxSize.X, newCanvasPosition.X),
- Util.Clamp(0, maxSize.Y, newCanvasPosition.Y))
- end
- end
- function this:ScrollToBottom()
- if this.ScrollingFrame then
- this:SetCanvasPosition(Vector2.new(this.ScrollingFrame.CanvasPosition.X, this.ScrollingFrame.CanvasSize.Y.Offset))
- end
- end
- function this:FadeIn(duration, lockFade)
- if not FadeLock then
- duration = duration or 0.75
- local backgroundTransparency = InputService.VREnabled and 1 or 0.5
- -- fade in
- if this.BackgroundTweener then
- this.BackgroundTweener:Cancel()
- end
- lastFadeInTime = tick()
- lastChatActivity = tick()
- this.ScrollingFrame.ScrollingEnabled = true
- this.ScrollingFrame.ScrollBarThickness = SCROLLBAR_THICKNESS
- this.BackgroundTweener = Util.PropertyTweener(this.ChatContainer, 'BackgroundTransparency', this.ChatContainer.BackgroundTransparency, backgroundTransparency, duration, Util.Linear)
- this.BackgroundVisible = true
- this:FadeInChats()
- this.ChatWindowPagingConn = Util.DisconnectEvent(this.ChatWindowPagingConn)
- this.ChatWindowPagingConn = InputService.InputBegan:connect(function(inputObject)
- local key = inputObject.KeyCode
- if key == Enum.KeyCode.PageUp then
- this:SetCanvasPosition(this.ScrollingFrame.CanvasPosition - Vector2.new(0, this.ScrollingFrame.AbsoluteWindowSize.Y))
- elseif key == Enum.KeyCode.PageDown then
- this:SetCanvasPosition(this.ScrollingFrame.CanvasPosition + Vector2.new(0, this.ScrollingFrame.AbsoluteWindowSize.Y))
- elseif key == Enum.KeyCode.Home then
- this:SetCanvasPosition(Vector2.new(0, 0))
- elseif key == Enum.KeyCode.End then
- this:ScrollToBottom()
- end
- end)
- if this.FadeInSignal then
- this.FadeInSignal:fire()
- end
- end
- end
- function this:FadeOut(duration, unlockFade)
- if not FadeLock then
- duration = duration or 0.75
- -- fade out
- if this.BackgroundTweener then
- this.BackgroundTweener:Cancel()
- end
- lastFadeOutTime = tick()
- lastChatActivity = tick()
- this.ScrollingFrame.ScrollingEnabled = false
- this.ScrollingFrame.ScrollBarThickness = 0
- this.BackgroundTweener = Util.PropertyTweener(this.ChatContainer, 'BackgroundTransparency', this.ChatContainer.BackgroundTransparency, 1, duration, Util.Linear)
- this.BackgroundVisible = false
- this.ChatWindowPagingConn = Util.DisconnectEvent(this.ChatWindowPagingConn)
- if this.FadeOutSignal then
- this.FadeOutSignal:fire()
- end
- end
- end
- function this:FadeInChats()
- if this.ChatsVisible == true then return end
- this.ChatsVisible = true
- for index, message in pairs(this.Chats) do
- message:FadeIn()
- end
- end
- function this:FadeOutChats()
- if InputService.VREnabled then return end
- if this.ChatsVisible == false then return end
- this.ChatsVisible = false
- for index, message in pairs(this.Chats) do
- local messageGui = message:GetGui()
- local instant = false
- if messageGui and this.ScrollingFrame then
- -- If the chat is not in the visible frame then don't waste cpu cycles fading it out
- if (messageGui.AbsolutePosition.Y > (this.ScrollingFrame.AbsolutePosition + this.ScrollingFrame.AbsoluteWindowSize).Y or
- messageGui.AbsolutePosition.Y + messageGui.AbsoluteSize.Y < this.ScrollingFrame.AbsolutePosition.Y) then
- instant = true
- end
- end
- message:FadeOut(instant)
- end
- end
- local ResizeCount = 0
- function this:OnResize()
- ResizeCount = ResizeCount + 1
- local currentResizeCount = ResizeCount
- local isScrolledDown = this:IsScrolledDown()
- -- Unfortunately there is a race condition so we need this wait here.
- wait()
- if this.ScrollingFrame then
- if currentResizeCount ~= ResizeCount then return end
- local scrollingFrameAbsoluteSize = this.ScrollingFrame.AbsoluteWindowSize
- if scrollingFrameAbsoluteSize ~= nil and scrollingFrameAbsoluteSize.X > 0 and scrollingFrameAbsoluteSize.Y > 0 then
- local ySize = 0
- if this.ScrollingFrame then
- for _, message in pairs(this.Chats) do
- local newHeight = message:OnResize(scrollingFrameAbsoluteSize)
- if newHeight then
- local chatMessageElement = message:GetGui()
- if chatMessageElement then
- local chatMessageElementYSize = chatMessageElement.Size.Y.Offset
- chatMessageElement.Position = UDim2.new(0, 0, 0, ySize)
- ySize = ySize + chatMessageElementYSize
- end
- end
- end
- end
- if this.MessageContainer and this.ScrollingFrame then
- this.MessageContainer.Size = UDim2.new(
- this.MessageContainer.Size.X.Scale,
- this.MessageContainer.Size.X.Offset,
- 0,
- ySize)
- this.MessageContainer.Position = UDim2.new(0, 0, 1, -this.MessageContainer.Size.Y.Offset)
- this.ScrollingFrame.CanvasSize = UDim2.new(this.ScrollingFrame.CanvasSize.X.Scale, this.ScrollingFrame.CanvasSize.X.Offset, this.ScrollingFrame.CanvasSize.Y.Scale, ySize)
- end
- end
- this:ScrollToBottom()
- end
- end
- function this:FilterMessage(playerChatType, sendingPlayer, chattedMessage, receivingPlayer)
- if chattedMessage and string.sub(chattedMessage, 1, 1) ~= '/' then
- return true
- end
- return false
- end
- function this:PushMessageIntoQueue(chatMessage, silently)
- table.insert(this.Chats, chatMessage)
- local isScrolledDown = this:IsScrolledDown()
- local chatMessageElement = chatMessage:GetGui()
- chatMessageElement.Parent = this.MessageContainer
- local chatMessageHeight = chatMessage:OnResize() or 10
- local ySize = this.MessageContainer.Size.Y.Offset
- local chatMessageElementYSize = UDim2.new(0, 0, 0, chatMessageHeight)
- if not silently then
- this.MessageCount = this.MessageCount + 1
- end
- chatMessageElement.Position = chatMessageElement.Position + UDim2.new(0, 0, 0, ySize)
- this.MessageContainer.Size = this.MessageContainer.Size + chatMessageElementYSize
- this.ScrollingFrame.CanvasSize = this.ScrollingFrame.CanvasSize + chatMessageElementYSize
- if this.Settings.MaxWindowChatMessages < #this.Chats then
- this:RemoveOldestMessage()
- end
- if isScrolledDown then
- this:ScrollToBottom()
- elseif not silently then
- -- Raise unread message alert!
- this.NewUnreadMessage = true
- end
- if silently then
- if this.ChatsVisible == false then
- chatMessage:FadeOut(true)
- end
- else
- this:FadeInChats()
- lastChatActivity = tick()
- this.MessageCountChanged:fire(this.MessageCount)
- end
- -- NOTE: Sort of hacky, but if we are approaching the max 16 bit size
- -- we need to rebase y back to 0 which can be done with the resize function
- if ySize > (MAX_UDIM_SIZE / 2) then
- self:OnResize()
- end
- end
- function this:AddSystemChatMessage(chattedMessage, silently)
- local chatMessage = CreateSystemChatMessage(this.Settings, chattedMessage)
- this:PushMessageIntoQueue(chatMessage, silently)
- end
- local function checkEnum(enumItems, value)
- for _, enum in pairs(enumItems) do
- if enum.Value == value then
- return enum
- end
- end
- return nil
- end
- -- We only need to copy the top level for the settings table
- local function shallowCopy(tableToCopy)
- local newTable = {}
- for key, value in pairs(tableToCopy) do
- newTable[key] = value
- end
- return newTable
- end
- function this:AddDeveloperSystemChatMessage(informationTable)
- local settings = shallowCopy(this.Settings)
- if informationTable["Text"] and type(informationTable["Text"]) == "string" then
- if typeof(informationTable.Color) == "Color3" then
- settings.DefaultMessageTextColor = informationTable.Color
- end
- if typeof(informationTable.Font) == "EnumItem" and informationTable.Font.EnumType == Enum.Font then
- settings.Font = informationTable.Font
- end
- if typeof(informationTable.FontSize) == "EnumItem" and informationTable.FontSize.EnumType == Enum.FontSize then
- settings.FontSize = informationTable.FontSize
- end
- local chatMessage = CreateSystemChatMessage(settings, informationTable["Text"])
- this:PushMessageIntoQueue(chatMessage, false)
- end
- end
- function this:AddChatMessage(playerChatType, sendingPlayer, chattedMessage, receivingPlayer, silently)
- local fixedChattedMessage = Util.FilterUnprintableCharacters(chattedMessage)
- if this:FilterMessage(playerChatType, sendingPlayer, fixedChattedMessage, receivingPlayer) then
- local chatMessage = CreatePlayerChatMessage(this.Settings, playerChatType, sendingPlayer, fixedChattedMessage, receivingPlayer)
- this:PushMessageIntoQueue(chatMessage, silently)
- end
- end
- function this:RemoveOldestMessage()
- local oldestChat = this.Chats[1]
- if oldestChat then
- return this:RemoveChatMessage(oldestChat)
- end
- end
- function this:RemoveChatMessage(chatMessage)
- if chatMessage then
- for index, message in pairs(this.Chats) do
- if chatMessage == message then
- local guiObj = chatMessage:GetGui()
- if guiObj then
- local ySize = guiObj.Size.Y.Offset
- this.ScrollingFrame.CanvasSize = this.ScrollingFrame.CanvasSize - UDim2.new(0,0,0,ySize)
- -- Clamp the canvasposition
- this:SetCanvasPosition(this.ScrollingFrame.CanvasPosition)
- guiObj.Parent = nil
- end
- message:Destroy()
- return table.remove(this.Chats, index)
- end
- end
- end
- end
- function this:IsScrolledDown()
- if this.ScrollingFrame then
- local yCanvasSize = this.ScrollingFrame.CanvasSize.Y.Offset
- local yContainerSize = this.ScrollingFrame.AbsoluteWindowSize.Y
- local yScrolledPosition = this.ScrollingFrame.CanvasPosition.Y
- -- Check if the messages are at the bottom
- return yCanvasSize < yContainerSize or
- yCanvasSize - yScrolledPosition <= yContainerSize + 5 -- a little wiggle room
- end
- return false
- end
- function this:GetMessageCount()
- return this.MessageCount
- end
- function this:CalculateVisibility()
- return this.WidgetVisible and ((chatCoreGuiEnabled and PlayersService.ClassicChat) or NON_CORESCRIPT_MODE)
- end
- function this:ToggleVisibility(visible)
- if visible ~= self.WidgetVisible then
- self.WidgetVisible = visible
- if this.ChatContainer then
- this.ChatContainer.Visible = self:CalculateVisibility()
- end
- end
- if NON_CORESCRIPT_MODE then
- this.ChatContainer.Visible = true
- end
- end
- function this:CoreGuiChanged(coreGuiType, enabled)
- if coreGuiType == Enum.CoreGuiType.Chat or coreGuiType == Enum.CoreGuiType.All then
- chatCoreGuiEnabled = enabled
- if this.ChatContainer then
- this.ChatContainer.Visible = self:CalculateVisibility()
- end
- end
- end
- local function CreateChatWindow()
- -- This really shouldn't be a button, but it is currently needed for VR.
- local container = Util.Create 'TextButton'
- {
- Name = 'ChatWindowContainer';
- Size = UDim2.new(0.3, 0, 0.25, 0);
- ZIndex = 1;
- BackgroundColor3 = Color3.new(0, 0, 0);
- BackgroundTransparency = 1;
- BorderSizePixel = 0;
- SelectionImageObject = emptySelectionImage;
- Active = false;
- Text = ""
- };
- container.BackgroundColor3 = Color3.new(31/255, 31/255, 31/255);
- local scrollingFrame = Util.Create'ScrollingFrame'
- {
- Name = 'ChatWindow';
- Size = UDim2.new(1, -4 - 10, 1, -20);
- CanvasSize = UDim2.new(1, -4 - 10, 0, 0);
- Position = UDim2.new(0, 10, 0, 10);
- ZIndex = 1;
- BackgroundColor3 = Color3.new(0, 0, 0);
- BackgroundTransparency = 1;
- BottomImage = "rbxasset://textures/ui/scroll-bottom.png";
- MidImage = "rbxasset://textures/ui/scroll-middle.png";
- TopImage = "rbxasset://textures/ui/scroll-top.png";
- ScrollBarThickness = 0;
- BorderSizePixel = 0;
- ScrollingEnabled = false;
- Parent = container;
- };
- local messageContainer = Util.Create'Frame'
- {
- Name = 'MessageContainer';
- Size = UDim2.new(1, -SCROLLBAR_THICKNESS - 1, 0, 0);
- Position = UDim2.new(0, 0, 1, 0);
- ZIndex = 1;
- BackgroundColor3 = Color3.new(0, 0, 0);
- BackgroundTransparency = 1;
- Parent = scrollingFrame
- };
- local function OnChatWindowResize(prop)
- if prop == 'AbsoluteSize' then
- messageContainer.Position = UDim2.new(0, 0, 1, -messageContainer.Size.Y.Offset)
- end
- if prop == 'CanvasPosition' then
- if this.ScrollingFrame then
- if this:IsScrolledDown() then
- this.NewUnreadMessage = false
- end
- end
- end
- end
- container.Changed:connect(function(prop)
- if prop == 'AbsoluteSize' then
- this:OnResize()
- end
- end)
- local function UpdateChatWindowLayout(newSize)
- -- A function to position the chat window in light of various factors
- -- (platform, container window size, presence of performance stats).
- if container == nil then
- return
- end
- -- Account for presence/absence of performance stats buttons.
- local localPlayer = PlayersService.LocalPlayer
- local isPerformanceStatsVisible = (GameSettings.PerformanceStatsVisible and localPlayer ~= nil)
- local yOffset = CHAT_WINDOW_Y_OFFSET
- if isPerformanceStatsVisible then
- yOffset = yOffset + StatsUtils.ButtonHeight
- end
- container.Position = UDim2.new(0, 0, 0, yOffset);
- -- Account for new screen size, if applicable.
- if (newSize == nil) then
- return
- end
- if InputService.VREnabled then
- container.Size = UDim2.new(1,0,1,0)
- -- Phone
- elseif newSize.X <= 640 then
- container.Size = UDim2.new(0.5,0,0.5,0) - container.Position
- -- Tablet
- elseif newSize.X <= 1024 then
- container.Size = UDim2.new(0.4,0,0.3,0) - container.Position
- -- Desktop
- else
- container.Size = UDim2.new(0.3,0,0.25,0) - container.Position
- end
- end
- -- When quick profiler button row visiblity changes, update position of chat window.
- GameSettings.PerformanceStatsVisibleChanged:connect(function()
- if not chatRepositioned then
- UpdateChatWindowLayout(nil)
- end
- end)
- GuiRoot.Changed:connect(function(prop)
- if (prop == "AbsoluteSize" and not chatRepositioned) then
- UpdateChatWindowLayout(GuiRoot.AbsoluteSize)
- end
- end)
- UpdateChatWindowLayout()
- messageContainer.Changed:connect(OnChatWindowResize)
- scrollingFrame.Changed:connect(OnChatWindowResize)
- this.ChatContainer = container
- this.ScrollingFrame = scrollingFrame
- this.MessageContainer = messageContainer
- this.ChatContainer.Parent = GuiRoot
- -- It is important to set this to true in NON_CORESCRIPT_MODE because normally the topbar sets
- -- the chat window to visible
- if NON_CORESCRIPT_MODE then
- this:ToggleVisibility(true)
- end
- --- BACKGROUND FADING CODE ---
- -- This is so we don't accidentally fade out when we are scrolling and mess with the scrollbar.
- local dontFadeOutOnMouseLeave = false
- if Util:IsTouchDevice() then
- local touchCount = 0
- this.InputBeganConn = InputService.InputBegan:connect(function(inputObject)
- if inputObject.UserInputType == Enum.UserInputType.Touch and inputObject.UserInputState == Enum.UserInputState.Begin then
- if PointInChatWindow(Vector2.new(inputObject.Position.X, inputObject.Position.Y)) then
- touchCount = touchCount + 1
- dontFadeOutOnMouseLeave = true
- end
- end
- end)
- this.InputEndedConn = InputService.InputEnded:connect(function(inputObject)
- if inputObject.UserInputType == Enum.UserInputType.Touch and inputObject.UserInputState == Enum.UserInputState.End then
- local endedCount = touchCount
- wait(2)
- if touchCount == endedCount then
- dontFadeOutOnMouseLeave = false
- end
- end
- end)
- spawn(function()
- local now = tick()
- while true do
- wait()
- now = tick()
- if this.BackgroundVisible then
- if not dontFadeOutOnMouseLeave then
- this:FadeOut(0.25)
- end
- -- If background is not visible/in-focus
- elseif this.ChatsVisible and now > lastChatActivity + MESSAGES_FADE_OUT_TIME then
- this:FadeOutChats()
- end
- end
- end)
- else
- this.LastMousePosition = Vector2.new()
- this.MouseEnterFrameConn = this.ChatContainer.MouseEnter:connect(function()
- lastEnterTime = tick()
- if this.BackgroundTweener and not this.BackgroundTweener:IsFinished() and not this.BackgroundVisible then
- this:FadeIn()
- end
- end)
- this.MouseMoveConn = InputService.InputChanged:connect(function(inputObject)
- if inputObject.UserInputType == Enum.UserInputType.MouseMovement then
- lastMoveTime = tick()
- this.LastMousePosition = Vector2.new(inputObject.Position.X, inputObject.Position.Y)
- if this.BackgroundTweener and this.BackgroundTweener:GetPercentComplete() < 0.5 and this.BackgroundVisible then
- if not dontFadeOutOnMouseLeave then
- this:FadeOut()
- end
- end
- end
- end)
- local clickCount = 0
- this.InputBeganConn = InputService.InputBegan:connect(function(inputObject)
- if inputObject.UserInputType == Enum.UserInputType.MouseButton1 and inputObject.UserInputState == Enum.UserInputState.Begin then
- if PointInChatWindow(Vector2.new(inputObject.Position.X, inputObject.Position.Y)) then
- clickCount = clickCount + 1
- dontFadeOutOnMouseLeave = true
- end
- end
- end)
- this.InputEndedConn = InputService.InputEnded:connect(function(inputObject)
- if inputObject.UserInputType == Enum.UserInputType.MouseButton1 and inputObject.UserInputState == Enum.UserInputState.End then
- local nowCount = clickCount
- wait(1.3)
- if nowCount == clickCount then
- dontFadeOutOnMouseLeave = false
- end
- end
- end)
- this.MouseLeaveFrameConn = this.ChatContainer.MouseLeave:connect(function()
- lastLeaveTime = tick()
- if this.BackgroundTweener and not this.BackgroundTweener:IsFinished() and this.BackgroundVisible then
- if not dontFadeOutOnMouseLeave then
- this:FadeOut()
- end
- end
- end)
- spawn(function()
- while true do
- wait()
- local now = tick()
- if this:IsHovering() then
- if now - lastMoveTime > 1.3 and not this.BackgroundVisible then
- this:FadeIn()
- end
- else -- not this:IsHovering()
- if this.BackgroundVisible then
- if not dontFadeOutOnMouseLeave then
- this:FadeOut(0.25)
- end
- -- If background is not visible/in-focus
- elseif this.ChatsVisible and now > lastChatActivity + MESSAGES_FADE_OUT_TIME then
- this:FadeOutChats()
- end
- end
- end
- end)
- end
- --- END OF BACKGROUND FADING CODE ---
- end
- CreateChatWindow()
- return this
- end
- local function CreateChat()
- local this = {}
- this.Settings =
- {
- GlobalTextColor = Color3.new(112/255, 110/255, 106/255);
- WhisperTextColor = Color3.new(77/255, 139/255, 255/255);
- TeamTextColor = Color3.new(230/255, 207/255, 0);
- DefaultMessageTextColor = Color3.new(255/255, 255/255, 243/255);
- AdminTextColor = Color3.new(1, 215/255, 0);
- InternTextColor = Color3.new(175/255, 221/255, 1);
- TextStrokeTransparency = 0.75;
- TextStrokeColor = Color3.new(34/255,34/255,34/255);
- Font = Enum.Font.SourceSansBold;
- SmallScreenFontSize = Enum.FontSize.Size14;
- FontSize = Enum.FontSize.Size18;
- MaxWindowChatMessages = 50;
- MaxCharactersInMessage = 140;
- }
- this.CurrentWindowMessageCountChanged = nil
- this.VisibilityStateChanged = Util.Signal()
- this.ChatBarFocusChanged = Util.Signal()
- this.Visible = false
- function this:CoreGuiChanged(coreGuiType, enabled)
- enabled = enabled and (topbarEnabled or InputService.VREnabled)
- if coreGuiType == Enum.CoreGuiType.Chat or coreGuiType == Enum.CoreGuiType.All then
- if enabled then
- pcall(function()
- self.SpecialKeyPressedConn = Util.DisconnectEvent(self.SpecialKeyPressedConn)
- GuiService:AddSpecialKey(Enum.SpecialKey.ChatHotkey)
- self.SpecialKeyPressedConn = GuiService.SpecialKeyPressed:connect(function(key)
- if key == Enum.SpecialKey.ChatHotkey then
- if self.Visible == false then
- self:ToggleVisibility()
- end
- if self.ChatBarWidget then
- self.ChatBarWidget:FocusChatBar()
- end
- end
- end)
- end)
- else
- pcall(function() GuiService:RemoveSpecialKey(Enum.SpecialKey.ChatHotkey) end)
- self.SpecialKeyPressedConn = Util.DisconnectEvent(self.SpecialKeyPressedConn)
- end
- if this.MobileChatButton then
- if enabled == true then
- this.MobileChatButton.Parent = GuiRoot
- -- we need to set it to be visible in-case we missed a lost focus event while chat was turned off.
- this.MobileChatButton.Visible = true
- else
- this.MobileChatButton.Parent = nil
- end
- end
- end
- if this.ChatWindowWidget then
- this.ChatWindowWidget:CoreGuiChanged(coreGuiType, enabled)
- end
- if this.ChatBarWidget then
- this.ChatBarWidget:CoreGuiChanged(coreGuiType, enabled)
- end
- end
- -- This event has 4 callback arguments
- -- Enum.PlayerChatType.{All|Team|Whisper}, chatPlayer, message, targetPlayer
- function this:OnPlayerChatted(playerChatType, sendingPlayer, chattedMessage, receivingPlayer)
- if this.ChatWindowWidget then
- -- Don't add messages from blocked players, don't show message if is a debug command
- local isDebugCommand = false
- pcall(function()
- if not NON_CORESCRIPT_MODE and sendingPlayer == PlayersService.LocalPlayer then
- isDebugCommand = game:GetService("GuiService"):ShowStatsBasedOnInputString(chattedMessage)
- -- allows dev console to be opened on mobile
- -- NOTE: Removed ToggleDevConsole bindable event, so engine no longer handles this
- if string.lower(chattedMessage) == "/console" then
- if FFlagEnableNewDevConsole then
- local devConsoleMaster = require(RobloxGui.Modules.DevConsoleMaster)
- if devConsoleMaster then
- devConsoleMaster.ToggleVisibility()
- end
- else
- local devConsoleModule = require(RobloxGui.Modules.DeveloperConsoleModule)
- if devConsoleModule then
- local devConsoleVisible = devConsoleModule:GetVisibility()
- devConsoleModule:SetVisibility(not devConsoleVisible)
- end
- end
- elseif string.lower(chattedMessage) == "/newconsole" then
- local devConsoleMaster = require(RobloxGui.Modules.DevConsoleMaster)
- if devConsoleMaster then
- devConsoleMaster.ToggleVisibility()
- end
- end
- end
- end)
- if not (this:IsPlayerBlocked(sendingPlayer) or this:IsPlayerMuted(sendingPlayer) or isDebugCommand) then
- this.ChatWindowWidget:AddChatMessage(playerChatType, sendingPlayer, chattedMessage, receivingPlayer)
- end
- end
- end
- function this:OnPlayerAdded(newPlayer)
- if newPlayer then
- assert(coroutine.resume(coroutine.create(function() PlayerPermissionsModule.IsPlayerAdminAsync(newPlayer) end)))
- end
- if NON_CORESCRIPT_MODE then
- newPlayer.Chatted:connect(function(msg, recipient)
- this:OnPlayerChatted(Enum.PlayerChatType.All, newPlayer, msg, recipient)
- end)
- else
- this.PlayerChattedConn = Util.DisconnectEvent(this.PlayerChattedConn)
- this.PlayerChattedConn = PlayersService.PlayerChatted:connect(function(...)
- this:OnPlayerChatted(...)
- end)
- end
- end
- function this:IsPlayerBlocked(player)
- if blockingUtility then
- return player and blockingUtility:IsPlayerBlockedByUserId(player.UserId)
- else
- return false
- end
- end
- function this:BlockPlayerAsync(playerToBlock)
- if playerToBlock and Player ~= playerToBlock then
- local blockUserId = playerToBlock.UserId
- local playerToBlockName = playerToBlock.Name
- if blockUserId > 0 then
- if not this:IsPlayerBlocked(playerToBlock) then
- if blockingUtility then
- blockingUtility:BlockPlayerAsync(playerToBlock)
- this.ChatWindowWidget:AddSystemChatMessage(playerToBlockName .. " is now blocked.")
- end
- else
- this.ChatWindowWidget:AddSystemChatMessage(playerToBlockName .. " is already blocked.")
- end
- else
- this.ChatWindowWidget:AddSystemChatMessage("You cannot block guests.")
- end
- else
- this.ChatWindowWidget:AddSystemChatMessage("You cannot block yourself.")
- end
- end
- function this:UnblockPlayerAsync(playerToUnblock)
- if playerToUnblock then
- local unblockUserId = playerToUnblock.UserId
- local playerToUnblockName = playerToUnblock.Name
- if this:IsPlayerBlocked(playerToUnblock) then
- if blockingUtility then
- this.ChatWindowWidget:AddSystemChatMessage(playerToUnblockName .. " is no longer blocked.")
- blockingUtility:UnblockPlayerAsync(playerToUnblock)
- end
- else
- this.ChatWindowWidget:AddSystemChatMessage(playerToUnblockName .. " is not blocked.")
- end
- end
- end
- function this:IsPlayerMuted(player)
- if blockingUtility then
- return player and blockingUtility:IsPlayerMutedByUserId(player.UserId)
- else
- return false
- end
- end
- function this:MutePlayer(playerToMute)
- if playerToMute and playerToMute ~= Player then
- if playerToMute.UserId > 0 then
- if not this:IsPlayerMuted(playerToMute) then
- if blockingUtility then
- blockingUtility:MutePlayer(playerToMute)
- this.ChatWindowWidget:AddSystemChatMessage(playerToMute.Name .. " is now muted.")
- end
- else
- this.ChatWindowWidget:AddSystemChatMessage(playerToMute.Name .. " is already muted.")
- end
- else
- this.ChatWindowWidget:AddSystemChatMessage("You cannot mute guests.")
- end
- else
- this.ChatWindowWidget:AddSystemChatMessage("You cannot mute yourself.")
- end
- end
- function this:UnmutePlayer(playerToUnmute)
- if playerToUnmute then
- if this:IsPlayerMuted(playerToUnmute) then
- if blockingUtility then
- blockingUtility:UnmutePlayer(playerToUnmute)
- this.ChatWindowWidget:AddSystemChatMessage(playerToUnmute.Name .. " is no longer muted.")
- end
- else
- this.ChatWindowWidget:AddSystemChatMessage(playerToUnmute.Name .. " is not muted.")
- end
- end
- end
- function this:CreateTouchDeviceChatButton()
- return Util.Create'ImageButton'
- {
- Name = 'TouchDeviceChatButton';
- Size = UDim2.new(0, 128, 0, 32);
- Position = UDim2.new(0, 88, 0, 0);
- BackgroundTransparency = 1.0;
- Image = 'https://www.roblox.com/asset/?id=97078724';
- };
- end
- function this:PrintWelcome()
- if this.ChatWindowWidget then
- if Util.IsTouchDevice() then
- this.ChatWindowWidget:AddSystemChatMessage("Please press the '...' icon to chat", true)
- end
- this.ChatWindowWidget:AddSystemChatMessage("Please chat '/?' for a list of commands", true)
- end
- end
- local doOnceVRWelcome = false
- function this:PrintVRWelcome()
- if this.ChatWindowWidget and not doOnceVRWelcome then
- if InputService.VREnabled then
- this.ChatWindowWidget:AddSystemChatMessage("Press here to chat", true)
- doOnceVRWelcome = true
- end
- end
- end
- function this:PrintHelp()
- if this.ChatWindowWidget then
- this.ChatWindowWidget:AddSystemChatMessage("Help Menu")
- this.ChatWindowWidget:AddSystemChatMessage("Chat Commands:")
- this.ChatWindowWidget:AddSystemChatMessage("/w [PlayerName] or /whisper [PlayerName] - Whisper Chat")
- this.ChatWindowWidget:AddSystemChatMessage("/t or /team - Team Chat")
- this.ChatWindowWidget:AddSystemChatMessage("/a or /all - All Chat")
- this.ChatWindowWidget:AddSystemChatMessage("/block [PlayerName] - Block communications from Target Player")
- this.ChatWindowWidget:AddSystemChatMessage("/unblock [PlayerName] - Restore communications with Target Player")
- this.ChatWindowWidget:AddSystemChatMessage("/mute [PlayerName] - Mute in-game communications from Target Player")
- this.ChatWindowWidget:AddSystemChatMessage("/unmute [PlayerName] - Restore in-game communications with Target Player")
- end
- end
- local focusCount = 0
- function this:CreateGUI()
- if (FORCE_CHAT_GUI or
- (Player.ChatMode == Enum.ChatMode.TextAndMenu or RunService:IsStudio()) and
- game:GetService("UserInputService"):GetPlatform() ~= Enum.Platform.XBoxOne) then
- if NON_CORESCRIPT_MODE then
- local chatGui = Instance.new("ScreenGui")
- chatGui.Name = "RobloxGui"
- chatGui.Parent = Player:WaitForChild('PlayerGui')
- GuiRoot.Parent = chatGui
- end
- -- NOTE: eventually we will make multiple chat window frames
- this.ChatWindowWidget = CreateChatWindowWidget(this.Settings)
- this.ChatBarWidget = CreateChatBarWidget(this.Settings)
- this.CurrentWindowMessageCountChanged = this.ChatWindowWidget.MessageCountChanged
- this.ChatWindowWidget.FadeInSignal:connect(function()
- this.ChatBarWidget:FadeIn()
- end)
- this.ChatWindowWidget.FadeOutSignal:connect(function()
- this.ChatBarWidget:FadeOut()
- end)
- this.ChatWindowWidget:FadeOut(0)
- this.ChatBarWidget.ChatBarGainedFocusEvent:connect(function()
- focusCount = focusCount + 1
- this.ChatWindowWidget:FadeIn(0.25)
- this.ChatWindowWidget:SetFadeLock(true)
- this.ChatBarFocusChanged:fire(true)
- end)
- this.ChatBarWidget.ChatBarLostFocusEvent:connect(function()
- local focusNow = focusCount
- if Util:IsTouchDevice() then
- delay(2, function()
- if focusNow == focusCount then
- this.ChatWindowWidget:SetFadeLock(false)
- end
- end)
- else
- this.ChatWindowWidget:SetFadeLock(false)
- end
- this.ChatBarFocusChanged:fire(false)
- end)
- this.ChatBarWidget.ChatBarFloodEvent:connect(function()
- if this.ChatWindowWidget then
- this.ChatWindowWidget:AddSystemChatMessage("Wait before sending another message.")
- end
- end)
- this.ChatBarWidget.ChatErrorEvent:connect(function(msg)
- if msg then
- this.ChatWindowWidget:AddSystemChatMessage(msg)
- end
- end)
- this.ChatBarWidget.ChatCommandEvent:connect(function(success, actionType, capture)
- if actionType == "Help" then
- this:PrintHelp()
- elseif actionType == "Block" then
- local blockPlayerName = capture and tostring(capture) or ""
- local playerToBlock = Util.GetPlayerByName(blockPlayerName)
- if playerToBlock then
- spawn(function() this:BlockPlayerAsync(playerToBlock) end)
- else
- this.ChatWindowWidget:AddSystemChatMessage("Cannot block " .. blockPlayerName .. " because they are not in the game.")
- end
- elseif actionType == "Unblock" then
- local unblockPlayerName = capture and tostring(capture) or ""
- local playerToBlock = Util.GetPlayerByName(unblockPlayerName)
- if playerToBlock then
- spawn(function() this:UnblockPlayerAsync(playerToBlock) end)
- else
- this.ChatWindowWidget:AddSystemChatMessage("Cannot unblock " .. unblockPlayerName .. " because they are not in the game.")
- end
- elseif actionType == "Mute" then
- local mutePlayerName = capture and tostring(capture) or ""
- local playerToMute = Util.GetPlayerByName(mutePlayerName)
- if playerToMute then
- this:MutePlayer(playerToMute)
- else
- this.ChatWindowWidget:AddSystemChatMessage("Cannot mute " .. mutePlayerName .. " because they are not in the game.")
- end
- elseif actionType == "Unmute" then
- local unmutePlayerName = capture and tostring(capture) or ""
- local playerToUnmute = Util.GetPlayerByName(unmutePlayerName)
- if playerToUnmute then
- this:UnmutePlayer(playerToUnmute)
- else
- this.ChatWindowWidget:AddSystemChatMessage("Cannot unmute " .. unmutePlayerName .. " because they are not in the game.")
- end
- elseif actionType == "Whisper" then
- if success == false then
- local playerName = capture and tostring(capture) or "Unknown"
- this.ChatWindowWidget:AddSystemChatMessage("Unable to Send a Whisper to Player: " .. playerName)
- end
- elseif actionType == "Unknown" then
- if success == false then
- local commandText = capture and tostring(capture) or "Unknown"
- this.ChatWindowWidget:AddSystemChatMessage("Invalid Slash Command: " .. commandText)
- end
- end
- end)
- if not NON_CORESCRIPT_MODE then
- local function onVREnabled()
- if InputService.VREnabled then
- self.Settings.TextStrokeTransparency = 1
- self:PrintVRWelcome()
- local Panel3D = require(RobloxGui.Modules.VR.Panel3D)
- local panel = Panel3D.Get(thisModuleName)
- panel:LinkTo("Keyboard")
- panel:SetType(Panel3D.Type.Fixed)
- panel:ResizePixels(300, 125)
- GuiRoot.Parent = panel:GetGUI()
- if this.ChatWindowWidget and this.ChatWindowWidget.ChatContainer then
- this.ChatWindowWidget.ChatContainer.MouseButton1Click:connect(function()
- if this.ChatBarWidget then
- if this.ChatBarWidget:WasFocused() then
- this.ChatBarWidget:RemoveFocus()
- else
- self:FocusChatBar()
- end
- end
- end)
- end
- function panel:CalculateTransparency()
- return 0
- end
- VRHub.ModuleOpened.Event:connect(function(moduleName)
- local module = VRHub:GetModule(moduleName)
- if moduleName ~= thisModuleName and module.VRIsExclusive then
- this:SetVisible(false)
- end
- end)
- else
- self.Settings.TextStrokeTransparency = 0.75
- GuiRoot.Parent = RobloxGui
- end
- end
- onVREnabled()
- InputService.Changed:connect(function(prop)
- if prop == 'VREnabled' then
- onVREnabled()
- end
- end)
- end
- end
- end
- local toggleCount = 0
- local function SetVisbility(newVisibility)
- this.Visible = newVisibility
- if this.ChatWindowWidget then
- this.ChatWindowWidget:ToggleVisibility(this.Visible)
- if this.Visible then
- toggleCount = toggleCount + 1
- local thisToggle = toggleCount
- local thisFocusCount = focusCount
- this.ChatWindowWidget:FadeIn()
- this.ChatWindowWidget:SetFadeLock(true)
- delay(5, function()
- if thisToggle == toggleCount and thisFocusCount == focusCount then
- this.ChatWindowWidget:SetFadeLock(false)
- end
- end)
- end
- end
- if this.ChatBarWidget then
- this.ChatBarWidget:ToggleVisibility(this.Visible)
- if this.Visible then
- this.ChatBarWidget:FadeIn()
- end
- if InputService.VREnabled and not this.Visible then
- this.ChatBarWidget:RemoveFocus()
- end
- end
- if InputService.VREnabled then
- local Panel3D = require(RobloxGui.Modules.VR.Panel3D)
- local panel = Panel3D.Get(thisModuleName)
- if this.Visible then
- local topbarPanel = Panel3D.Get("Topbar3D")
- panel.localCF = topbarPanel.localCF * CFrame.Angles(math.rad(-5), 0, 0) * CFrame.new(0, 4, 0) * CFrame.Angles(math.rad(-15), 0, 0)
- panel:SetVisible(true)
- panel:ForceShowUntilLookedAt()
- VRHub:FireModuleOpened(thisModuleName)
- else
- panel:SetVisible(this.Visible)
- VRHub:FireModuleClosed(thisModuleName)
- end
- end
- this.VisibilityStateChanged:fire(this.Visible)
- end
- function this:ToggleVisibility()
- SetVisbility(not self.Visible)
- end
- function this:SetVisible(visible)
- SetVisbility(visible)
- end
- function this:FocusChatBar()
- if self.ChatBarWidget and this.Visible then
- self.ChatBarWidget:FocusChatBar()
- end
- end
- function this:IsFocused(useWasFocused)
- if not self.ChatBarWidget then return false end
- return self.ChatBarWidget:IsFocused() or (useWasFocused and self.ChatBarWidget:WasFocused())
- end
- function this:GetCurrentWindowMessageCount()
- if this.ChatWindowWidget then
- return this.ChatWindowWidget:GetMessageCount()
- end
- return 0
- end
- function this:TopbarEnabledChanged(enabled)
- topbarEnabled = enabled
- -- Update coregui to reflect new topbar status
- self:CoreGuiChanged(Enum.CoreGuiType.Chat, StarterGui:GetCoreGuiEnabled(Enum.CoreGuiType.Chat))
- end
- function this:Initialize()
- --[[ Developer Customization API ]]--
- if not NON_CORESCRIPT_MODE then
- StarterGui:RegisterSetCore("ChatMakeSystemMessage", function(informationTable)
- if this.ChatWindowWidget then
- this.ChatWindowWidget:AddDeveloperSystemChatMessage(informationTable)
- end
- end)
- local function isUDim2Value(value)
- return typeof(value) == "UDim2" and value or nil
- end
- local function isBubbleChatOn()
- return not PlayersService.ClassicChat and PlayersService.BubbleChat
- end
- StarterGui:RegisterSetCore("ChatWindowPosition", function(value)
- if this.ChatWindowWidget and this.ChatBarWidget then
- value = isUDim2Value(value)
- if value ~= nil and not isBubbleChatOn() then
- chatRepositioned = true -- Prevent chat from moving back to the original position on screen resolution change
- this.ChatWindowWidget.ChatContainer.Position = value
- this.ChatBarWidget.ChatBarContainer.Position = value + UDim2.new(0, 0, this.ChatWindowWidget.ChatContainer.Size.Y.Scale, this.ChatWindowWidget.ChatContainer.Size.Y.Offset + 2)
- end
- end
- end)
- StarterGui:RegisterSetCore("ChatWindowSize", function(value)
- if this.ChatWindowWidget and this.ChatBarWidget then
- value = isUDim2Value(value)
- if value ~= nil and not isBubbleChatOn() then
- chatRepositioned = true
- this.ChatWindowWidget.ChatContainer.Size = value
- this.ChatBarWidget.ChatBarContainer.Size = UDim2.new(this.ChatWindowWidget.ChatContainer.Size.X.Scale, this.ChatWindowWidget.ChatContainer.Size.X.Offset, this.ChatBarWidget.ChatBarContainer.Size.Y.Scale, this.ChatBarWidget.ChatBarContainer.Size.Y.Offset)
- this.ChatBarWidget.ChatBarContainer.Position = this.ChatWindowWidget.ChatContainer.Position + UDim2.new(0, 0, this.ChatWindowWidget.ChatContainer.Size.Y.Scale, this.ChatWindowWidget.ChatContainer.Size.Y.Offset + 2)
- end
- end
- end)
- StarterGui:RegisterGetCore("ChatWindowPosition", function()
- if this.ChatWindowWidget then
- return this.ChatWindowWidget.ChatContainer.Position
- else
- return nil
- end
- end)
- StarterGui:RegisterGetCore("ChatWindowSize", function()
- if this.ChatWindowWidget then
- return this.ChatWindowWidget.ChatContainer.Size
- else
- return nil
- end
- end)
- StarterGui:RegisterSetCore("ChatBarDisabled", function(value)
- if this.ChatBarWidget then
- if type(value) == "boolean" then
- chatBarDisabled = value
- if value == true then
- this.ChatBarWidget:ToggleVisibility(false)
- end
- end
- end
- end)
- StarterGui:RegisterGetCore("ChatBarDisabled", function() return chatBarDisabled end)
- end
- this:OnPlayerAdded(Player)
- -- Upsettingly, it seems everytime a player is added, you have to redo the connection
- -- NOTE: PlayerAdded only fires on the server, hence ChildAdded is used here
- PlayersService.ChildAdded:connect(function(child)
- if child:IsA('Player') then
- this:OnPlayerAdded(child)
- end
- end)
- this:CreateGUI()
- this:CoreGuiChanged(Enum.CoreGuiType.Chat, StarterGui:GetCoreGuiEnabled(Enum.CoreGuiType.Chat))
- this.CoreGuiChangedConn = Util.DisconnectEvent(this.CoreGuiChangedConn)
- pcall(function()
- this.CoreGuiChangedConn = StarterGui.CoreGuiChangedSignal:connect(
- function(coreGuiType,enabled)
- this:CoreGuiChanged(coreGuiType, enabled)
- end)
- end)
- if not NON_CORESCRIPT_MODE then
- this:PrintWelcome()
- end
- --SetVisbility(true)
- end
- return this
- end
- local moduleApiTable = {}
- -- Main Entry Point
- do
- moduleApiTable.ModuleName = thisModuleName
- moduleApiTable.KeepVRTopbarOpen = true
- moduleApiTable.VRIsExclusive = true
- moduleApiTable.VRClosesNonExclusive = false
- VRHub:RegisterModule(moduleApiTable)
- VRHub.ModuleOpened.Event:connect(function(moduleName)
- if moduleName ~= thisModuleName then
- local module = VRHub:GetModule(moduleName)
- if module.VRIsExclusive then
- moduleApiTable:SetVisible(false)
- end
- end
- end)
- local ChatInstance = CreateChat()
- ChatInstance:Initialize()
- function moduleApiTable:ToggleVisibility()
- ChatInstance:ToggleVisibility()
- end
- function moduleApiTable:SetVisible(visible)
- ChatInstance:SetVisible(visible)
- end
- function moduleApiTable:FocusChatBar()
- ChatInstance:FocusChatBar()
- end
- function moduleApiTable:GetVisibility()
- return ChatInstance.Visible
- end
- function moduleApiTable:GetMessageCount()
- return ChatInstance:GetCurrentWindowMessageCount()
- end
- function moduleApiTable:TopbarEnabledChanged(...)
- return ChatInstance:TopbarEnabledChanged(...)
- end
- function moduleApiTable:IsFocused(useWasFocused)
- return ChatInstance:IsFocused(useWasFocused)
- end
- function moduleApiTable:ClassicChatEnabled()
- return PlayersService.ClassicChat
- end
- function moduleApiTable:IsBubbleChatOnly()
- return PlayersService.BubbleChat and not PlayersService.ClassicChat
- end
- function moduleApiTable:IsDisabled()
- return false
- end
- moduleApiTable.ChatBarFocusChanged = ChatInstance.ChatBarFocusChanged
- moduleApiTable.VisibilityStateChanged = ChatInstance.VisibilityStateChanged
- moduleApiTable.MessagesChanged = ChatInstance.CurrentWindowMessageCountChanged
- end
- return moduleApiTable
Advertisement
Add Comment
Please, Sign In to add comment