Rowblox-V1/asset/backup/11

1757 lines
57 KiB
Plaintext

%11%
--[[
// FileName: ChatScript.lua
// Written by: SolarCrane
// Description: Code for lua side chat on ROBLOX.
]]
--[[ CONSTANTS ]]
local FORCE_CHAT_GUI = false
local USE_PLAYER_GUI_TESTING = false
local ADMIN_LIST = {
['68465808'] = true, -- IMightBeLying
['32345429'] = true, -- Rbadam
['48453004'] = true, -- Adamintygum
['57705391'] = true, -- androidtest
['58491921'] = true, -- RobloxFrenchie
['29341155'] = true, -- JacksSmirkingRevenge
['60629145'] = true, -- Mandaari
['6729993'] = true, -- vaiobot
['9804369'] = true, -- Goddessnoob
['67906358'] = true, -- Thr33pakShak3r
['41101356'] = true, -- effward
['29667843'] = true, -- Blockhaak
['26076267'] = true, -- Drewbda
['65171466'] = true, -- triptych999
['264635'] = true, -- Tone
['59257875'] = true, -- fasterbuilder19
['30068452'] = true, -- Zeuxcg
['53627251'] = true, -- concol2
['56449'] = true, -- ReeseMcBlox
['7210880'] = true, -- Jeditkacheff
['35187797'] = true, -- ChiefJustus
['21964644'] = true, -- Ellissar
['63971416'] = true, -- geekndestroy
['111627'] = true, -- Noob007
['482238'] = true, -- Limon
['39861325'] = true, -- hawkington
['32580099'] = true, -- Tabemono
['504316'] = true, -- BrightEyes
['35825637'] = true, -- Monsterinc3D
['55311255'] = true, -- IsolatedEvent
['48988254'] = true, -- CountOnConnor
['357346'] = true, -- Scubasomething
['28969907'] = true, -- OnlyTwentyCharacters
['17707636'] = true, -- LordRugdumph
['26714811'] = true, -- bellavour
['24941'] = true, -- david.baszucki
['13057592'] = true, -- ibanez2189
['66766775'] = true, -- ConvexHero
['13268404'] = true, -- Sorcus
['26065068'] = true, -- DeeAna00
['4108667'] = true, -- TheLorekt
['605367'] = true, -- MSE6
['28350010'] = true, -- CorgiParade
['1636196'] = true, -- Varia
['27861308'] = true, -- 4runningwolves
['26699190'] = true, -- pulmoesflor
['9733258'] = true, -- Olive71
['2284059'] = true, -- groundcontroll2
['28080592'] = true, -- GuruKrish
['28137935'] = true, -- Countvelcro
['25964666'] = true, -- IltaLumi
['31221099'] = true, -- juanjuan23
['13965343'] = true, -- OstrichSized
['541489'] = true, -- jackintheblox
['38324232'] = true, -- SlingshotJunkie
['146569'] = true, -- gordonrox24
['61666252'] = true, -- sharpnine
['25540124'] = true, -- Motornerve
['29044576'] = true, -- watchmedogood
['49626068'] = true, -- jmargh
['57352126'] = true, -- JayKorean
['25682044'] = true, -- Foyle
['1113299'] = true, -- MajorTom4321
['30598693'] = true, -- supernovacaine
['1311'] = true, -- FFJosh
['23603273'] = true, -- Sickenedmonkey
['28568151'] = true, -- Doughtless
['8298697'] = true, -- KBUX
['39871898'] = true, -- totallynothere
['57708043'] = true, -- ErzaStar
['22'] = true, -- Keith
['2430782'] = true, -- Chro
['29373363'] = true, -- SolarCrane
['29116915'] = true, -- GloriousSalt
['6783205'] = true, -- UristMcSparks
['62155769'] = true, -- ITOlaurEN
['24162607'] = true, -- Malcomso
['66692187'] = true, -- HeySeptember
['80254'] = true, -- Stickmasterluke
['66093461'] = true, -- chefdeletat
['62968051'] = true, -- windlight13
['80119'] = true, -- Stravant
['61890000'] = true, -- imaginationsensation
['916'] = true, -- Matt Dusek
['4550725'] = true, -- CrimmsonGhost
['59737068'] = true, -- Mcrtest
['5600283'] = true, -- Seranok
['48656806'] = true, -- maxvee
['48656806'] = true, -- Coatp0cketninja
['35231885'] = true, -- Screenme
['55958416'] = true, -- b1tsh1ft
['51763936'] = true, -- ConvexRumbler
['59638684'] = true, -- mpliner476
['44231609'] = true, -- Totbl
['16906083'] = true, -- Aquabot8
['23984372'] = true, -- grossinger
['13416513'] = true, -- Merely
['27416429'] = true, -- CDakkar
['46526337'] = true, -- logitek00
['21969613'] = true, -- Siekiera
['39488230'] = true, -- Robloxkidsaccount
['60425210'] = true, -- flotsamthespork
['16732061'] = true, -- Soggoth
['33904052'] = true, -- Phil
['39882028'] = true, -- OrcaSparkles
['62060462'] = true, -- skullgoblin
['29044708'] = true, -- RickROSStheB0SS
['15782010'] = true, -- ArgonPirate
['6949935'] = true, -- NobleDragon
['41256555'] = true, -- Squidcod
['16150324'] = true, -- Raeglyn
['24419770'] = true, -- Xerolayne
['20396599'] = true, -- Robloxsai
['55579189'] = true, -- Briarroze
['21641566'] = true, -- hawkeyebandit
['39871292'] = true, -- DapperBuffalo
['48725400'] = true, -- Vukota
['60880618'] = true, -- swiftstone
['8736269'] = true, -- Gemlocker
['17199995'] = true, -- Tarabyte
['6983145'] = true, -- Timobius
['20048521'] = true, -- Tobotrobot
['1644345'] = true, -- Foster008
['33067469'] = true, -- Twberg
['57741156'] = true, -- DarthVaden
['51164101'] = true, -- Khanovich
['61380399'] = true, -- oLEFTo
['39876101'] = true, -- CodeWriter
['23598967'] = true, -- VladTheFirst
['39299049'] = true, -- Phaedre
['39855185'] = true, -- gorroth
['61004766'] = true, -- jynj1984
['10261020'] = true, -- RoboYZ
['44564747'] = true, -- ZodiacZak
}
local CHAT_COLORS =
{
BrickColor.new("Bright red"),
BrickColor.new("Bright blue"),
BrickColor.new("Earth green"),
BrickColor.new("Bright violet"),
BrickColor.new("Bright orange"),
BrickColor.new("Bright yellow"),
BrickColor.new("Light reddish violet"),
BrickColor.new("Brick yellow"),
}
-- These emotes are copy-pastad from the humanoidLocalAnimateKeyframe script
local EMOTE_NAMES = {wave = true, point = true, dance = true, dance2 = true, dance3 = true, laugh = true, cheer = true}
local MESSAGES_FADE_OUT_TIME = 30
--[[ 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')
--[[ END OF SERVICES ]]
--[[ SCRIPT VARIABLES ]]
-- 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 = nil
if USE_PLAYER_GUI_TESTING then
GuiRoot = Instance.new("ScreenGui")
GuiRoot.Name = "RobloxGui"
GuiRoot.Parent = Player:WaitForChild('PlayerGui')
else
GuiRoot = CoreGuiService:WaitForChild('RobloxGui')
end
--[[ END OF SCRIPT VARIABLES ]]
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.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 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%8
end
function Util.ComputeChatColor(pName)
return CHAT_COLORS[GetNameValue(pName) + 1].Color
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
end
local SelectChatModeEvent = Util.Signal()
local SelectPlayerEvent = Util.Signal()
local function CreateChatMessage()
local this = {}
this.Settings =
{
Font = Enum.Font.SourceSansBold;
FontSize = Enum.FontSize.Size14;
}
function this:OnResize()
-- Nothing!
end
function this:FadeIn()
local gui = this:GetGui()
if gui then
--Util.PropertyTweener(this.ChatContainer, 'BackgroundTransparency', this.ChatContainer.BackgroundTransparency, 1, duration, Util.Linear)
gui.Visible = true
end
end
function this:FadeOut()
local gui = this:GetGui()
if gui then
--Util.PropertyTweener(this.ChatContainer, 'BackgroundTransparency', this.ChatContainer.BackgroundTransparency, 1, duration, Util.Linear)
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
end
return this
end
local function CreateSystemChatMessage(chattedMessage)
local this = CreateChatMessage()
this.chatMessage = chattedMessage
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
this.Container.Size = UDim2.new(1,0,0,textHeight)
return textHeight
end
end
local function CreateMessageGuiElement()
local systemMesasgeDisplayText = this.chatMessage or ""
local systemMessageSize = Util.GetStringTextBounds(systemMesasgeDisplayText, this.Settings.Font, this.Settings.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;
};
local chatMessage = Util.Create'TextLabel'
{
Name = 'SystemChatMessage';
Position = UDim2.new(0, 0, 0, 0);
Size = UDim2.new(1, 0, 1, 0);
Text = systemMesasgeDisplayText;
ZIndex = 1;
BackgroundColor3 = Color3.new(0, 0, 0);
BackgroundTransparency = 1;
TextXAlignment = Enum.TextXAlignment.Left;
TextYAlignment = Enum.TextYAlignment.Top;
TextWrapped = true;
TextColor3 = Color3.new(1, 1, 1);
FontSize = this.Settings.FontSize;
Font = this.Settings.Font;
TextStrokeColor3 = Color3.new(34/255, 34/255, 34/255);
TextStrokeTransparency = 0.3;
Parent = container;
};
container.Size = UDim2.new(1, 0, 0, systemMessageSize.Y);
this.Container = container
this.ChatMessage = chatMessage
end
CreateMessageGuiElement()
return this
end
local function CreatePlayerChatMessage(playerChatType, sendingPlayer, chattedMessage, receivingPlayer)
local this = CreateChatMessage()
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
this.Container.Size = UDim2.new(1,0,0,textHeight)
return textHeight
end
end
function this:FormatMessage()
local result = ""
if this.RawMessageContent then
local message = this.RawMessageContent
if string.sub(message, 1, 3) == '/e ' or string.sub(message, 1, 7) == '/emote ' then
if this.SendingPlayer then
result = this.SendingPlayer.Name .. " emotes."
end
else
result = message
end
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: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 toMesasgeDisplayText = "To: "
local toMessageSize = Util.GetStringTextBounds(toMesasgeDisplayText, this.Settings.Font, this.Settings.FontSize)
local fromMesasgeDisplayText = "From: "
local fromMessageSize = Util.GetStringTextBounds(fromMesasgeDisplayText, this.Settings.Font, this.Settings.FontSize)
local chatTypeDisplayText = this:FormatChatType()
local chatTypeSize = chatTypeDisplayText and Util.GetStringTextBounds(chatTypeDisplayText, this.Settings.Font, this.Settings.FontSize) or Vector2.new(0,0)
local playerNameDisplayText = this:FormatPlayerNameText()
local playerNameSize = Util.GetStringTextBounds(playerNameDisplayText, this.Settings.Font, this.Settings.FontSize)
local singleSpaceSize = Util.GetStringTextBounds(" ", this.Settings.Font, this.Settings.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, this.Settings.FontSize, UDim2.new(0, 400 - 5 - playerNameSize.X, 0, 1000))
local playerColor = Color3.new(1,1,1)
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;
};
local xOffset = 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 = Color3.new(1, 1, 1);
FontSize = this.Settings.FontSize;
Font = this.Settings.Font;
TextStrokeColor3 = Color3.new(34/255, 34/255, 34/255);
TextStrokeTransparency = 0.3;
Parent = container;
};
xOffset = xOffset + toMessageSize.X
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 = Color3.new(1, 1, 1);
FontSize = this.Settings.FontSize;
Font = this.Settings.Font;
TextStrokeColor3 = Color3.new(34/255, 34/255, 34/255);
TextStrokeTransparency = 0.3;
Parent = container;
};
xOffset = xOffset + fromMessageSize.X
else
local userNameDot = Util.Create'ImageLabel'
{
Name = "UserNameDot";
Size = UDim2.new(0, 14, 0, 14);
BackgroundTransparency = 1;
Position = UDim2.new(0, 0, 0, 2);
BorderSizePixel = 0;
Image = "rbxasset://textures/ui/chat_teamButton.png";
ImageColor3 = playerColor;
Parent = container;
}
xOffset = xOffset + 14 + 3
end
if chatTypeDisplayText then
local chatModeButton = Util.Create(Util.IsTouchDevice() and 'TextLabel' or 'TextButton')
{
Name = 'ChatMode';
BackgroundTransparency = 1;
ZIndex = 2;
Text = chatTypeDisplayText;
TextColor3 = Color3.new(255/255, 255/255, 243/255);
Position = UDim2.new(0, xOffset, 0, 0);
TextXAlignment = Enum.TextXAlignment.Left;
TextYAlignment = Enum.TextYAlignment.Top;
FontSize = this.Settings.FontSize;
Font = this.Settings.Font;
Size = UDim2.new(0, chatTypeSize.X, 0, chatTypeSize.Y);
TextStrokeColor3 = Color3.new(34/255, 34/255, 34/255);
TextStrokeTransparency = 0.3;
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
end
local userNameButton = Util.Create(Util.IsTouchDevice() and 'TextLabel' or 'TextButton')
{
Name = 'PlayerName';
BackgroundTransparency = 1;
ZIndex = 2;
Text = playerNameDisplayText;
TextColor3 = playerColor;
Position = UDim2.new(0, xOffset, 0, 0);
TextXAlignment = Enum.TextXAlignment.Left;
TextYAlignment = Enum.TextYAlignment.Top;
FontSize = this.Settings.FontSize;
Font = this.Settings.Font;
Size = UDim2.new(0, playerNameSize.X, 0, playerNameSize.Y);
TextStrokeColor3 = Color3.new(34/255, 34/255, 34/255);
TextStrokeTransparency = 0.3;
Parent = container
}
if userNameButton:IsA('TextButton') then
this.ClickedOnPlayerConn = userNameButton.MouseButton1Click:connect(function()
SelectPlayerEvent:fire(this.SendingPlayer)
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 = Color3.new(255/255, 255/255, 243/255);
FontSize = this.Settings.FontSize;
Font = this.Settings.Font;
TextStrokeColor3 = Color3.new(34/255, 34/255, 34/255);
TextStrokeTransparency = 0.3;
Parent = container;
};
-- 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 and ADMIN_LIST[tostring(this.SendingPlayer.userId)] then
chatMessage.TextColor3 = Color3.new(1, 215/255, 0)
end
chatMessage.Size = chatMessage.Size + UDim2.new(0, 0, 0, chatMessage.TextBounds.Y);
container.Size = UDim2.new(1, 0, 0, math.max(chatMessageSize.Y, userNameButton.Size.Y.Offset));
this.Container = container
this.ChatMessage = chatMessage
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.ChatBarGainedFocusEvent = Util.Signal()
this.ChatBarLostFocusEvent = Util.Signal()
this.ChatCommandEvent = Util.Signal() -- success, actionType, captures
this.ChatErrorEvent = Util.Signal() -- success, actionType, captures
this.ChatMatchingRegex =
{
[function(chatBarText) return string.find(string.lower(chatBarText), "^/w (%w+)") end] = "Whisper";
[function(chatBarText) return string.find(string.lower(chatBarText), "^/whisper (%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(string.lower(chatBarText), "^/t") end] = "Team";
[function(chatBarText) return string.find(string.lower(chatBarText), "^/team") end] = "Team";
[function(chatBarText) return string.find(string.lower(chatBarText), "^/a") end] = "All";
[function(chatBarText) return string.find(string.lower(chatBarText), "^/all") end] = "All";
[function(chatBarText) return string.find(string.lower(chatBarText), "^/s") end] = "All";
[function(chatBarText) return string.find(string.lower(chatBarText), "^/say") end] = "All";
[function(chatBarText) return string.find(string.lower(chatBarText), "^/e") end] = "Emote";
[function(chatBarText) return string.find(string.lower(chatBarText), "^/emote") end] = "Emote";
[function(chatBarText) return string.find(string.lower(chatBarText), "^/%?") end] = "Help";
}
local ChatModesDict =
{
['Whisper'] = 'Whisper';
['Team'] = 'Team';
['All'] = 'All';
[Enum.PlayerChatType.Whisper] = 'Whisper';
[Enum.PlayerChatType.Team] = 'Team';
[Enum.PlayerChatType.All] = 'All';
}
local function TearDownEvents()
-- Note: This is a new api so we need to pcall it
pcall(function() GuiService:RemoveSpecialKey(Enum.SpecialKey.ChatHotkey) end)
this.SpecialKeyPressedConn = Util.DisconnectEvent(this.SpecialKeyPressedConn)
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)
end
local function HookUpEvents()
TearDownEvents() -- Cleanup old events
-- ChatHotKey is '/'
GuiService:AddSpecialKey(Enum.SpecialKey.ChatHotkey)
this.SpecialKeyPressedConn = GuiService.SpecialKeyPressed:connect(function(key)
if key == Enum.SpecialKey.ChatHotkey then
this:FocusChatBar()
end
end)
if this.ClickToChatButton then this.ClickToChatButtonConn = this.ClickToChatButton.MouseButton1Click:connect(function() this:FocusChatBar() end) end
if this.ChatBar then this.ChatBarFocusLostConn = this.ChatBar.FocusLost:connect(function(...) this.ChatBarLostFocusEvent:fire(...) 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)
end
function this:CoreGuiChanged(coreGuiType, enabled)
if coreGuiType == Enum.CoreGuiType.Chat or coreGuiType == Enum.CoreGuiType.All then
if enabled then
HookUpEvents()
else
TearDownEvents()
end
if this.ChatBarContainer then
this.ChatBarContainer.Visible = enabled
end
end
end
function this:IsAChatMode(mode)
return ChatModesDict[mode] ~= nil
end
function this:IsAnEmoteMode(mode)
return mode == "Emote"
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:SetChatBarText("")
this.ChatCommandEvent:fire(false, actionType, capture)
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 newText = this:GetChatBarText()
if #newText > this.Settings.MaxCharactersInMessage then
local fixedText = ""
-- 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
this:SetChatBarText(fixedText)
previousText = fixedText
-- TODO: Flash Max Characters Feedback
else
previousText = newText
end
end
end
function this:GetChatBarText()
return this.ChatBar and this.ChatBar.Text or ""
end
function this:SetChatBarText(newText)
if this.ChatBar then
this.ChatBar.Text = newText
end
end
function this:GetMessageMode()
return this.MessageMode
end
function this:SetMessageMode(newMessageMode)
newMessageMode = ChatModesDict[newMessageMode]
if this.MessageMode ~= newMessageMode then
this.MessageMode = newMessageMode
if this.ChatModeText then
if newMessageMode == 'Whisper' then
-- TODO: also update this when they change players to whisper to
local chatRecipientText = "[" .. (this.TargetWhisperPlayer and this.TargetWhisperPlayer.Name or "") .. "]"
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, -offset - 5, 1, 0)
this.ChatBar.Position = UDim2.new(0, offset + 5, 0, 0)
end
end
end
end
function this:FocusChatBar()
if this.ChatBar then
this.ChatBar:CaptureFocus()
if self.ClickToChatButton then
self.ClickToChatButton.Visible = false
end
if this.ChatModeText then
this.ChatModeText.Visible = true
end
this.ChatBarChangedConn = Util.DisconnectEvent(this.ChatBarChangedConn)
this.ChatBarChangedConn = this.ChatBar.Changed:connect(function(prop)
if prop == "Text" then
this:OnChatBarTextChanged()
end
end)
if Util.IsTouchDevice() then
this.ChatBar.Visible = true
this:SetMessageMode('All') -- Don't remember message mode on mobile devices
else
-- Use a count to make sure you double backspace out of a chatmode; less likely to accidently do it.
local count = 0
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.ChatBarGainedFocusEvent:fire()
end
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
function this:OnChatBarFocusLost(enterPressed)
if self.ChatBar then
if Util.IsTouchDevice() then
self.ChatBar.Visible = false
end
if enterPressed then
local didMatchSlashCommand = this:ProcessChatBarModes(false)
local cText = this:SanitizeInput(this:GetChatBarText())
if cText ~= "" then
if not didMatchSlashCommand and string.sub(cText,1,1) == "/" then
this.ChatCommandEvent:fire(false, "Unknown", cText)
else
local currentMessageMode = this:GetMessageMode()
-- {All, Team, Whisper}
if currentMessageMode == 'Team' then
if Player and Player.Neutral == true then
this.ChatErrorEvent:fire("You are not in any team.")
else
pcall(function() PlayersService:TeamChat(cText) end)
end
elseif currentMessageMode == 'Whisper' then
if this.TargetWhisperPlayer then
if this.TargetWhisperPlayer == Player then
this.ChatErrorEvent:fire("You cannot send a whisper to yourself.")
else
pcall(function() PlayersService:WhisperChat(cText, this.TargetWhisperPlayer) end)
end
else
this.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
end
end
this:SetChatBarText("")
end
end
if self.ClickToChatButton then
self.ClickToChatButton.Visible = true
end
if this.ChatModeText then
this.ChatModeText.Visible = false
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;
};
local clickToChatButton = Util.Create'TextButton'
{
Name = 'ClickToChat';
Position = UDim2.new(0,9,0,0);
Size = UDim2.new(1, -9, 1, 0);
BackgroundTransparency = 1;
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;
}
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);
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;
}
local chatModeText = Util.Create'TextButton'
{
Name = 'ChatModeText';
Position = UDim2.new(0, 9, 0, 0);
Size = UDim2.new(1, -9, 1, 0);
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;
}
this.ChatBarContainer = chatBarContainer
this.ClickToChatButton = clickToChatButton
this.ChatBar = chatBar
this.ChatModeText = chatModeText
this.ChatBarContainer.Parent = GuiRoot
end
CreateChatBar()
return this
end
local function CreateChatWindowWidget(settings)
local this = {}
this.Settings = settings
this.Chats = {}
this.BackgroundVisible = false
this.ChatWindowPagingConn = nil
local lastMoveTime = tick()
local lastEnterTime = tick()
local lastLeaveTime = tick()
local lastFadeOutTime = 0
local lastFadeInTime = 0
local FadeLock = false
local function PointInChatWindow(pt)
local point0 = this.ChatContainer.AbsolutePosition
local point1 = point0 + this.ChatContainer.AbsoluteSize
return point0.X <= pt.X and point1.X >= pt.X and
point0.Y <= pt.Y and point1.Y >= pt.Y
end
function this:IsHovering()
--return lastEnterTime > lastLeaveTime
if this.ChatContainer and this.LastMousePosition then
return PointInChatWindow(this.LastMousePosition)
end
return false
end
function this:SetFadeLock(lock)
FadeLock = lock
end
function this:GetFadeLock()
return FadeLock
end
function this:FadeIn(duration, lockFade)
if not FadeLock then
duration = duration or 0.75
-- fade in
if this.BackgroundTweener then
this.BackgroundTweener:Cancel()
end
lastFadeInTime = tick()
this.ScrollingFrame.ScrollingEnabled = true
this.BackgroundTweener = Util.PropertyTweener(this.ChatContainer, 'BackgroundTransparency', this.ChatContainer.BackgroundTransparency, 0.7, 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.ScrollingFrame.CanvasPosition = Vector2.new(0, math.max(0, this.ScrollingFrame.CanvasPosition.Y - this.ScrollingFrame.AbsoluteSize.Y))
elseif key == Enum.KeyCode.PageDown then
this.ScrollingFrame.CanvasPosition =
Vector2.new(0, Util.Clamp(0, --min
this.ScrollingFrame.CanvasSize.Y.Offset - this.ScrollingFrame.AbsoluteSize.Y, --max
this.ScrollingFrame.CanvasPosition.Y + this.ScrollingFrame.AbsoluteSize.Y))
elseif key == Enum.KeyCode.Home then
this.ScrollingFrame.CanvasPosition = Vector2.new(0, 0)
elseif key == Enum.KeyCode.End then
this.ScrollingFrame.CanvasPosition = Vector2.new(0, this.ScrollingFrame.CanvasSize.Y.Offset - this.ScrollingFrame.AbsoluteSize.Y)
end
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()
this.ScrollingFrame.ScrollingEnabled = false
this.BackgroundTweener = Util.PropertyTweener(this.ChatContainer, 'BackgroundTransparency', this.ChatContainer.BackgroundTransparency, 1, duration, Util.Linear)
this.BackgroundVisible = false
this.ChatWindowPagingConn = Util.DisconnectEvent(this.ChatWindowPagingConn)
local now = lastFadeOutTime
delay(MESSAGES_FADE_OUT_TIME, function()
if lastFadeOutTime > lastFadeInTime and now == lastFadeOutTime then
this:FadeOutChats()
end
end)
end
end
function this:FadeInChats()
-- TODO: only bother with this loop if we know chats have been faded out, could be quicker than this
for index, message in pairs(this.Chats) do
message:FadeIn()
end
end
function this:FadeOutChats()
for index, message in pairs(this.Chats) do
message:FadeOut()
end
end
function this:OnResize()
local isScrolledDown = this:IsScrolledDown()
local ySize = 0
if this.ScrollingFrame then
for index, message in pairs(this.Chats) do
local newHeight = message:OnResize(this.ScrollingFrame.AbsoluteSize)
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(
1,
this.ScrollingFrame.Size.X.Offset - this.ScrollingFrame.ScrollBarThickness - this.ScrollingFrame.Position.X.Offset,
0,
ySize)
this.ScrollingFrame.CanvasSize = UDim2.new(this.ScrollingFrame.CanvasSize.X.Scale, this.ScrollingFrame.CanvasSize.X.Offset, this.ScrollingFrame.CanvasSize.Y.Scale, ySize)
if isScrolledDown then
local function UpdateScrollDown()
this.ScrollingFrame.CanvasPosition = Vector2.new(0, math.max(0, this.ScrollingFrame.CanvasSize.Y.Offset - math.max(0, this.ScrollingFrame.AbsoluteSize.Y)))
end
UpdateScrollDown()
-- Have to do it after a delay as well because when you resize not all of the absolutesize have been updated yet
delay(0, UpdateScrollDown)
end
end
end
function this:PushMessageIntoQueue(chatMessage)
table.insert(this.Chats, chatMessage)
local isScrolledDown = this:IsScrolledDown()
local chatMessageElement = chatMessage:GetGui()
chatMessageElement.Parent = this.MessageContainer
chatMessage:OnResize()
local ySize = this.MessageContainer.Size.Y.Offset
local chatMessageElementYSize = UDim2.new(0, 0, 0, chatMessageElement.Size.Y.Offset)
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.ScrollingFrame.CanvasPosition = Vector2.new(0, math.max(0, this.ScrollingFrame.CanvasSize.Y.Offset - this.ScrollingFrame.AbsoluteSize.Y))
else
-- Raise unread message alert!
end
this:FadeInChats()
end
function this:AddSystemChatMessage(chattedMessage)
local chatMessage = CreateSystemChatMessage(chattedMessage)
this:PushMessageIntoQueue(chatMessage)
end
function this:AddChatMessage(playerChatType, sendingPlayer, chattedMessage, receivingPlayer)
local chatMessage = CreatePlayerChatMessage(playerChatType, sendingPlayer, chattedMessage, receivingPlayer)
this:PushMessageIntoQueue(chatMessage)
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)
guiObj.Parent = nil
end
message:Destroy()
return table.remove(this.Chats, index)
end
end
end
end
function this:IsScrolledDown()
local yCanvasSize = this.ScrollingFrame.CanvasSize.Y.Offset
local yContainerSize = this.ScrollingFrame.AbsoluteSize.Y
local yScrolledPosition = this.ScrollingFrame.CanvasPosition.Y
-- Check if the messages are at the bottom
return yCanvasSize < yContainerSize or
yCanvasSize - yScrolledPosition >= yContainerSize - 2 -- Fuzzy equals here
end
function this:CoreGuiChanged(coreGuiType, enabled)
if coreGuiType == Enum.CoreGuiType.Chat or coreGuiType == Enum.CoreGuiType.All then
if this.ChatContainer then
this.ChatContainer.Visible = enabled
end
end
-- If we are using bubble-chat then do not show chat window
if this.ChatContainer and not PlayersService.ClassicChat then
this.ChatContainer.Visible = false
end
end
local function CreateChatWindow()
local container = Util.Create'Frame'
{
Name = 'ChatWindowContainer';
-- Height is a multiple of chat message height, maybe keep this value at 150 and move that padding into the messageContainer
Size = UDim2.new(0.33, 0, 0.25, 0);
Position = UDim2.new(0, 20, 0, 50);
ZIndex = 1;
BackgroundColor3 = Color3.new(0, 0, 0);
BackgroundTransparency = 1;
};
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 = 7;
BorderSizePixel = 0;
ScrollingEnabled = false;
Parent = container;
};
local messageContainer = Util.Create'Frame'
{
Name = 'MessageContainer';
Size = UDim2.new(1, 0, 0, 0);
Position = UDim2.new(0, 0, 1, 0);
ZIndex = 1;
BackgroundColor3 = Color3.new(0, 0, 0);
BackgroundTransparency = 1;
Parent = scrollingFrame
};
-- This is some trickery we are doing to make the first chat messages appear at the bottom and go towards the top.
local function OnChatWindowResize(prop)
if prop == 'AbsoluteSize' then
messageContainer.Position = UDim2.new(0, 0, 1, -messageContainer.Size.Y.Offset)
elseif prop == 'ScrollBarThickness' then
messageContainer.Size = UDim2.new(
messageContainer.Size.X.Scale,
scrollingFrame.Size.X.Offset - scrollingFrame.ScrollBarThickness - scrollingFrame.Position.X.Offset,
messageContainer.Size.Y.Scale,
messageContainer.Size.Y.Offset)
end
end
container.Changed:connect(function(prop) if prop == 'AbsoluteSize' then this:OnResize() end end)
local function RobloxClientScreenSizeChanged(newSize)
if container then
-- Phone
if 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.33,0,0.25,0) - container.Position
end
end
end
GuiRoot.Changed:connect(function(prop) if prop == "AbsoluteSize" then RobloxClientScreenSizeChanged(GuiRoot.AbsoluteSize) end end)
RobloxClientScreenSizeChanged(GuiRoot.AbsoluteSize)
messageContainer.Changed:connect(OnChatWindowResize)
scrollingFrame.Changed:connect(OnChatWindowResize)
this.ChatContainer = container
this.ScrollingFrame = scrollingFrame
this.MessageContainer = messageContainer
this.ChatContainer.Parent = GuiRoot
--- 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()
while true do
wait()
if this.BackgroundVisible then
if not dontFadeOutOnMouseLeave then
this:FadeOut(0.25)
end
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(2)
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()
if this:IsHovering() then
if tick() - lastMoveTime > 2 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
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(255/255, 255/255, 243/255);
WhisperTextColor = Color3.new(77/255, 139/255, 255/255);
TeamTextColor = Color3.new(230/255, 207/255, 0);
MaxWindowChatMessages = 100;
MaxCharactersInMessage = 140; -- Same as a tweet :D
}
function this:CoreGuiChanged(coreGuiType, enabled)
if coreGuiType == Enum.CoreGuiType.Chat or coreGuiType == Enum.CoreGuiType.All then
if Util:IsTouchDevice() then
Util.SetGUIInsetBounds(0, 0)
else
if enabled then
-- Reserve bottom 20 pixels for our chat bar
Util.SetGUIInsetBounds(0, 20)
else
Util.SetGUIInsetBounds(0, 0)
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
this.ChatWindowWidget:AddChatMessage(playerChatType, sendingPlayer, chattedMessage, receivingPlayer)
end
end
function this:OnPlayerAdded()
this.PlayerChattedConn = Util.DisconnectEvent(this.PlayerChattedConn)
this.PlayerChattedConn = PlayersService.PlayerChatted:connect(function(...)
this:OnPlayerChatted(...)
end)
end
function this:GetBlockedPlayersAsync()
local secureBaseUrl = Util.GetSecureApiBaseUrl()
local url = secureBaseUrl .. "userblock/getblockedusers" .. "?" .. "userId=" .. tostring(Player.userId) .. "&" .. "page=" .. "1"
local blockList = nil
local success, msg = ypcall(function()
local request = game:HttpGetAsync(url)
blockList = request and game:GetService('HttpService'):DecodeJSON(request)
end)
if blockList and blockList['success'] == true then
return blockList['userList']
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 = 'http://www.roblox.com/asset/?id=97078724';
};
end
function this:PrintWelcome()
this.ChatWindowWidget:AddSystemChatMessage("Welcome to Roblox")
this.ChatWindowWidget:AddSystemChatMessage("Please type /? for a list of commands")
end
function this:PrintHelp()
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")
end
function this:CreateGUI()
if Player.ChatMode == Enum.ChatMode.TextAndMenu or FORCE_CHAT_GUI then
-- NOTE: eventually we will make multiple chat window frames
-- Settings is a table, which makes it a pointing and is kosher to pass by reference
this.ChatWindowWidget = CreateChatWindowWidget(this.Settings)
this.ChatBarWidget = CreateChatBarWidget(this.Settings)
local focusCount = 0
this.ChatBarWidget.ChatBarGainedFocusEvent:connect(function()
focusCount = focusCount + 1
this.ChatWindowWidget:FadeIn(0.25)
this.ChatWindowWidget:SetFadeLock(true)
end)
this.ChatBarWidget.ChatBarLostFocusEvent:connect(function()
local focusNow = focusCount
if Util:IsTouchDevice() then
wait(2)
if focusNow == focusCount then
this.ChatWindowWidget:SetFadeLock(false)
end
else
this.ChatWindowWidget:SetFadeLock(false)
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 == "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 Util.IsTouchDevice() then
local mobileChatButton = this:CreateTouchDeviceChatButton()
mobileChatButton.Parent = GuiRoot
mobileChatButton.TouchTap:connect(function()
mobileChatButton.Visible = false
if this.ChatBarWidget then
this.ChatBarWidget:FocusChatBar()
end
end)
this.ChatBarWidget.ChatBarLostFocusEvent:connect(function()
mobileChatButton.Visible = true
end)
end
end
end
function this:Initialize()
--spawn(function()
-- this:GetBlockedPlayersAsync()
--end)
this:OnPlayerAdded()
-- 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()
this:OnPlayerAdded()
end)
this:CreateGUI()
pcall(function()
this:CoreGuiChanged(Enum.CoreGuiType.Chat, StarterGui:GetCoreGuiEnabled(Enum.CoreGuiType.Chat))
this.CoreGuiChangedConn = Util.DisconnectEvent(this.CoreGuiChangedConn)
this.CoreGuiChangedConn = StarterGui.CoreGuiChangedSignal:connect(
function(coreGuiType,enabled)
this:CoreGuiChanged(coreGuiType, enabled)
end)
end)
this:PrintWelcome()
end
return this
end
-- Run the script
do
local ChatInstance = CreateChat()
ChatInstance:Initialize()
end