oh dear, my bad.
I forgot text embed overrides any properties!
but I haven’t been sleeping or doing nothing about it!
as soon as i realized, i searched for a solution!
Roblox does not allow for modifying text of a bubble, so we can’t really remove the text embed.
So what is the solution?
Reroute all chat messages to our very own custom chat system!
for the past night I have been cooking up a new chat system,
very simple and easy to remake!
it requires one module script with a data structure we use for passing our data.
one localscript for client handling,
one server script for text filtering.
and then boom you get this:
(ill share my hierarchy, my scripts, and a video of how it looks)
scripts:
module:
local ChatMessage = {}
ChatMessage.__index = ChatMessage
function ChatMessage.new(msg : string, userId : number, font : Enum.Font, textColor : Color3, textSize : number, isWhisper : boolean, whisperId : number, bold : boolean, italic : boolean, israinbow : boolean)
local newMsg = {}
setmetatable(newMsg, ChatMessage)
newMsg.Message = msg
newMsg.Source = userId
newMsg.Font = font
newMsg.TextColor = textColor
newMsg.TextSize = textSize
newMsg.IsWhisper = isWhisper
newMsg.Bold = bold
newMsg.Italic = italic
newMsg.WhisperId = whisperId
newMsg.IsRainBow = israinbow
return newMsg
end
function ChatMessage.default()
local newMsg = {}
setmetatable(newMsg, ChatMessage)
newMsg.Message = ""
newMsg.Source = -1
newMsg.Font = Enum.Font.Gotham
newMsg.TextColor = Color3.fromRGB(255, 255, 255)
newMsg.TextSize = 14
newMsg.IsWhisper = false
newMsg.WhisperId = -1
newMsg.Bold = false
newMsg.Italic = false
newMsg.IsRainBow = false
return newMsg
end
function ChatMessage:With(property : string, value : any)
local newCopy = self:Clone()
newCopy[property] = value
return newCopy
end
function ChatMessage:Clone()
local newCopy = {}
setmetatable(newCopy, ChatMessage)
newCopy.Message = self.Message
newCopy.Source = self.Source
newCopy.Font = self.Font
newCopy.TextColor = self.TextColor
newCopy.TextSize = self.TextSize
newCopy.IsWhisper = self.IsWhisper
newCopy.WhisperId = self.WhisperId
newCopy.Bold = self.Bold
newCopy.Italic = self.Italic
newCopy.IsRainBow = self.IsRainBow
return newCopy
end
function ChatMessage.CopyFrom(arr)
local newCopy = {}
local defaultArr = ChatMessage.default()
setmetatable(newCopy, ChatMessage)
newCopy.Message = arr["Message"] or defaultArr["Message"]
newCopy.Source = arr["Source"] or defaultArr["Source"]
newCopy.Font = arr["Font"] or defaultArr["Font"]
newCopy.TextColor = arr["TextColor"] or defaultArr["TextColor"]
newCopy.TextSize = arr["TextSize"] or defaultArr["TextSize"]
newCopy.IsWhisper = arr["IsWhisper"] or defaultArr["IsWhisper"]
newCopy.WhisperId = arr["WhisperId"] or defaultArr["WhisperId"]
newCopy.Bold = arr["Bold"] or defaultArr["Bold"]
newCopy.Italic = arr["Italic"] or defaultArr["Italic"]
newCopy.IsRainBow = arr["IsRainBow"] or defaultArr["IsRainBow"]
return newCopy
end
return ChatMessage
this defines a new class called ChatMessage! with some instance and some static methods.
With method- returns a new copy of the chatmessage but with the given property changed.
new- creates a new chatmessage with the given properties
default- creates a new chatmessage with default properties
clone- clones a chatmessage
copyfrom- clones a chatmessage from a given array, if values are nil, it puts in the default value!
localscript, handles rendering and sending to server, also decorating messages based on chatmessage settings:
local Rep = game.ReplicatedStorage
local core = game.StarterGui
local tweenss = game:GetService("TweenService")
local col = game:GetService("CollectionService")
local contextActions = game:GetService("ContextActionService")
local SendChat = Rep:WaitForChild("SendChat")
local ChatMessage = require(Rep:WaitForChild("ChatMessage"))
local ChatUI = script.Parent
local Input = ChatUI.InputFrame.input
local Output = ChatUI.Output
local tweenInfo = TweenInfo.new(0.3, Enum.EasingStyle.Linear, Enum.EasingDirection.In, 0, false, 0)
local rainBows = {}
local hue = 0
local debounce = false
local function onSendRequest()
if debounce then return end
print("ran")
debounce = true
local msg = ChatMessage.default()
:With("Message", Input.Text)
:With("Source", game.Players.LocalPlayer.UserId)
SendChat:FireServer(msg)
Input.Text = ""
task.wait(1)
debounce = false
end
core:SetCoreGuiEnabled(Enum.CoreGuiType.Chat, false)
contextActions:BindAction("ChatSend", onSendRequest, false, Enum.KeyCode.Return)
SendChat.OnClientEvent:Connect(function(usermsg)
local sender = "System"
local posplr = game.Players:GetPlayerByUserId(usermsg["Source"])
if posplr then sender = posplr.Name end
local newDisplay = script.Message:Clone()
newDisplay.Internal.Text = sender..":".." \n"..usermsg["Message"]
newDisplay.Internal.Font = usermsg["Font"]
newDisplay.Internal.TextSize = usermsg["TextSize"]
newDisplay.Internal.TextColor3 = usermsg["TextColor"]
if usermsg["Source"] == game.Players.LocalPlayer.UserId then newDisplay.BackgroundColor3 = Color3.fromRGB(0, 170, 255) end
if usermsg["IsRainBow"] == true then newDisplay:AddTag("Rainbow") end
newDisplay.Parent = Output
end)
spawn(function()
while task.wait(0.1) do
hue += 0.01
local color = Color3.fromHSV(hue, 1, 1)
for i,v in pairs(col:GetTagged("Rainbow")) do--for every rainbow, set its color
local int : TextLabel = v["Internal"]
if not int then return end
int.TextColor3 = color
end
if hue >= 1 then
hue = 0
end
end
end)
and the server, which handles text filtering! because it is important to protect our users! the server also handles broadcasting the message to every player, or only the whispered player.
local SendChat = game.ReplicatedStorage.SendChat
local Chat = game:GetService("Chat")
local ChatMessage = require(game.ReplicatedStorage.ChatMessage)
SendChat.OnServerEvent:Connect(function(plr, umsg)
print("ran")
local succes, errorMsg = pcall(function()
if umsg["Source"] == -1 then return umsg["Message"] end--message came from server
return Chat:FilterStringForBroadcast(umsg["Message"], game.Players:GetPlayerByUserId(umsg["Source"]))
end)
if not succes then print(errorMsg) return end
print("sending...")
local new = ChatMessage.CopyFrom(umsg)
:With("Message", errorMsg)
for i,v in pairs(game.Players:GetPlayers()) do
if new["IsWhisper"] then
if v.UserId ~= new["WhisperId"] and v.UserId ~= new["Source"] then continue end
end
SendChat:FireClient(v, new)
end
end)
and then a local script for the button on screen, showing how to send a system message:
local chtmsg = require(game.ReplicatedStorage:WaitForChild("ChatMessage"))
local send = game.ReplicatedStorage:WaitForChild("SendChat")
script.Parent.Activated:Connect(function()
local msg = chtmsg.default()
:With("Message", "hello there!")
:With("IsRainBow", true)
send:FireServer(msg)
end)
now this is my hierarchy, useful information so you can see where every object resides:
alright and now here is a video:
it is a bit janky i know, it is because autosize on frames does not resize again when the children become smaller, only when they become bigger, but i’ll find a solution i swear!
what do you think?
i’ll extend it to include bubble chat soon, hold on a minute…
edit:
alright i have returned with bubble chat!
here is the modified client script, server script & the module stays the same.
local Rep = game.ReplicatedStorage
local core = game.StarterGui
local tweenss = game:GetService("TweenService")
local col = game:GetService("CollectionService")
local contextActions = game:GetService("ContextActionService")
local debris = game:GetService("Debris")
local SendChat = Rep:WaitForChild("SendChat")
local ChatMessage = require(Rep:WaitForChild("ChatMessage"))
local bubble = script:WaitForChild("ChatBubble")
local ChatUI = script.Parent
local Input = ChatUI.InputFrame.input
local Output = ChatUI.Output
local tweenInfo = TweenInfo.new(0.3, Enum.EasingStyle.Linear, Enum.EasingDirection.In, 0, false, 0)
local rainBows = {}
local hue = 0
local debounce = false
local function onSendRequest()
if debounce then return end
print("ran")
debounce = true
local msg = ChatMessage.default()
:With("Message", Input.Text)
:With("Source", game.Players.LocalPlayer.UserId)
SendChat:FireServer(msg)
Input.Text = ""
task.wait(1)
debounce = false
end
core:SetCoreGuiEnabled(Enum.CoreGuiType.Chat, false)
contextActions:BindAction("ChatSend", onSendRequest, false, Enum.KeyCode.Return)
SendChat.OnClientEvent:Connect(function(usermsg)
local sender = "System"
local posplr = game.Players:GetPlayerByUserId(usermsg["Source"])
if posplr then sender = posplr.Name end
local newDisplay = script.Message:Clone()
newDisplay.Internal.Text = sender..":".." \n"..usermsg["Message"]
newDisplay.Internal.Font = usermsg["Font"]
newDisplay.Internal.TextSize = usermsg["TextSize"]
newDisplay.Internal.TextColor3 = usermsg["TextColor"]
if usermsg["Source"] == game.Players.LocalPlayer.UserId then newDisplay.BackgroundColor3 = Color3.fromRGB(0, 170, 255) end
if usermsg["IsRainBow"] == true then newDisplay:AddTag("Rainbow") end
newDisplay.Parent = Output
if sender == "System" then return end
local newBubble = bubble:Clone()
debris:AddItem(newBubble, 20)--change lifetime of bubble here...
newBubble.MyFrame.Text.Text = usermsg["Message"]
newBubble.Parent = posplr.Character.Head
newBubble.Adornee = posplr.Character.Head
newBubble.MyFrame.Visible = true--show bubble
end)
spawn(function()
while task.wait(0.1) do
hue += 0.01
local color = Color3.fromHSV(hue, 1, 1)
for i,v in pairs(col:GetTagged("Rainbow")) do--for every rainbow, set its color
local int : TextLabel = v["Internal"]
if not int then return end
int.TextColor3 = color
end
if hue >= 1 then
hue = 0
end
end
end)
here is my new hierarchy:
sorry for my horrible ui design choices, but the scripting matters.
you can style the window however you like,
i’ll report back once i’ve finished fixing the ui auto size bug.
edit: my brother said: "blud made an entire new chat system just to color text rainbow "