local function k(k,w,u,R)R=R or false if not TextSizeCache[k]then TextSizeCache[k]={}end if not TextSizeCache[k][w]then TextSizeCache[k][w]={}end if not TextSizeCache[k][w][R]then TextSizeCache[k][w][R]={}end if not TextSizeCache[k][w][R][u]then testLabel.Text=k testLabel.Font=w testLabel.TextSize=u if R then testLabel.TextWrapped=true testLabel.Size=UDim2.new(0,R.x,0,R.y)else testLabel.TextWrapped=false end TextSizeCache[k][w][R][u]=testLabel.TextBounds end return TextSizeCache[k][w][R][u]end local function w(k,w,u)return w+(u-w)*math.min(string.len(k)/75.0,1.0)end local function u()local k={}k.data={}local w=Instance.new("\066\105\110\100\097\098\108\101\069\118\101\110\116")k.Emptied=w.Event function k.Size(w)return#k.data end function k.Empty(w)return k:Size()<=0 end function k.PopFront(u)table.remove(k.data,1)if k:Empty()then w:Fire()end end function k.Front(w)return k.data[1]end function k.Get(u,w)return k.data[w]end function k.PushBack(u,w)table.insert(k.data,w)end function k.GetData(w)return k.data end return k end local function R()local k={}k.Fifo=u()k.BillboardGui=nil return k end local function y()local k={}k.data={}local w=0 function k.Size(k)return w end function k.Erase(R,u)if k.data[u]then w=w-1 end k.data[u]=nil end function k.Set(y,u,R)k.data[u]=R if R then w=w+1 end end function k.Get(u,w)if not w then return end if not k.data[w]then k.data[w]=R()local u=nil u=k.data[w].Fifo.Emptied:connect(function()u:disconnect()k:Erase(w)end)end return k.data[w]end function k.GetData(w)return k.data end return k end local function r(k,u,R)local y={}function y.ComputeBubbleLifetime(R,k,u)if u then return w(k,8,15)else return w(k,12,20)end end y.Origin=nil y.RenderBubble=nil y.Message=k y.BubbleDieDelay=y:ComputeBubbleLifetime(k,R)y.BubbleColor=u y.IsLocalPlayer=R return y end local function l(k,w,u)local R=r(w,BubbleColor.WHITE,u)if k then R.User=k.Name R.Origin=k.Character end return R end local function D(k,w,u,R)local y=r(w,R,u)y.Origin=k return y end function createChatBubbleMain(k,w)local u=Instance.new("\073\109\097\103\101\076\097\098\101\108")u.Name="\067\104\097\116\066\117\098\098\108\101"u.ScaleType=Enum.ScaleType.Slice u.SliceCenter=w u.Image="\114\098\120\097\115\115\101\116\058\047\047\116\101\120\116\117\114\101\115\047"..(tostring(k).."\046\112\110\103")u.BackgroundTransparency=1 u.BorderSizePixel=0 u.Size=UDim2.new(1.0,0,1.0,0)u.Position=UDim2.new(0,0,0,0)u.ImageColor3=CHAT_BUBBLE_COLOR return u end function createChatBubbleTail(k,w)local u=Instance.new("\073\109\097\103\101\076\097\098\101\108")u.Name="\067\104\097\116\066\117\098\098\108\101\084\097\105\108"u.Image="\114\098\120\097\115\115\101\116\058\047\047\116\101\120\116\117\114\101\115\047\117\105\047\100\105\097\108\111\103\095\116\097\105\108\046\112\110\103"u.BackgroundTransparency=1 u.BorderSizePixel=0 u.Position=k u.Size=w u.ImageColor3=CHAT_BUBBLE_COLOR return u end function createChatBubbleWithTail(k,w,u,R)local y=createChatBubbleMain(k,R)local r=createChatBubbleTail(w,u)r.Parent=y return y end function createScaledChatBubbleWithTail(k,w,u,R)local y=createChatBubbleMain(k,R)local r=Instance.new("\070\114\097\109\101")r.Name="\067\104\097\116\066\117\098\098\108\101\084\097\105\108\070\114\097\109\101"r.BackgroundTransparency=1 r.SizeConstraint=Enum.SizeConstraint.RelativeXX r.Position=UDim2.new(.5,0,1,0)r.Size=UDim2.new(w,0,w,0)r.Parent=y local l=createChatBubbleTail(u,UDim2.new(1,0,.5,0))l.Parent=r return y end function createChatImposter(k,w,u)local R=Instance.new("\073\109\097\103\101\076\097\098\101\108")R.Name="\068\105\097\108\111\103\080\108\097\099\101\104\111\108\100\101\114"R.Image="\114\098\120\097\115\115\101\116\058\047\047\116\101\120\116\117\114\101\115\047"..(tostring(k).."\046\112\110\103")R.BackgroundTransparency=1 R.BorderSizePixel=0 R.Position=UDim2.new(0,0,-1.25,0)R.Size=UDim2.new(1,0,1,0)local y=Instance.new("\073\109\097\103\101\076\097\098\101\108")y.Name="\068\111\116\068\111\116\068\111\116"y.Image="\114\098\120\097\115\115\101\116\058\047\047\116\101\120\116\117\114\101\115\047"..(tostring(w).."\046\112\110\103")y.BackgroundTransparency=1 y.BorderSizePixel=0 y.Position=UDim2.new(.001,0,u,0)y.Size=UDim2.new(1,0,.7,0)y.Parent=R return R end local P={}P.ChatBubble={}P.ChatBubbleWithTail={}P.ScalingChatBubbleWithTail={}P.CharacterSortedMsg=y()
--[[ FUNCTIONS ]]

local function getStringTextBounds(text, font, textSize, sizeBounds)
	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][textSize] then
		testLabel.Text = text
		testLabel.Font = font
		testLabel.TextSize = textSize
		if sizeBounds then
			testLabel.TextWrapped = true
			testLabel.Size = UDim2.new(0, sizeBounds.x, 0, sizeBounds.y)
		else
			testLabel.TextWrapped = false
		end
		TextSizeCache[text][font][sizeBounds][textSize] = testLabel.TextBounds
	end
	return TextSizeCache[text][font][sizeBounds][textSize]
end

local function lerpLength(msg, min, max)
	return min + (max - min) * math.min(string.len(msg) / 75.0, 1.0)
end

local function createFifo()
	local this = {}
	this.data = {}

	local emptyEvent = Instance.new("BindableEvent")
	this.Emptied = emptyEvent.Event

	function this:Size()
		return #this.data
	end

	function this:Empty()
		return this:Size() <= 0
	end

	function this:PopFront()
		table.remove(this.data, 1)
		if this:Empty() then
			emptyEvent:Fire()
		end
	end

	function this:Front()
		return this.data[1]
	end

	function this:Get(index)
		return this.data[index]
	end

	function this:PushBack(value)
		table.insert(this.data, value)
	end

	function this:GetData()
		return this.data
	end

	return this
end

local function createCharacterChats()
	local this = {}

	this.Fifo = createFifo()
	this.BillboardGui = nil

	return this
end

local function createMap()
	local this = {}
	this.data = {}
	local count = 0

	function this:Size()
		return count
	end

	function this:Erase(key)
		if this.data[key] then
			count = count - 1
		end
		this.data[key] = nil
	end

	function this:Set(key, value)
		this.data[key] = value
		if value then
			count = count + 1
		end
	end

	function this:Get(key)
		if not key then
			return
		end
		if not this.data[key] then
			this.data[key] = createCharacterChats()
			local emptiedCon = nil
			emptiedCon = this.data[key].Fifo.Emptied:connect(function()
				emptiedCon:disconnect()
				this:Erase(key)
			end)
		end
		return this.data[key]
	end

	function this:GetData()
		return this.data
	end

	return this
end

local function createChatLine(message, bubbleColor, isLocalPlayer)
	local this = {}

	function this:ComputeBubbleLifetime(msg, isSelf)
		if isSelf then
			return lerpLength(msg, 8, 15)
		else
			return lerpLength(msg, 12, 20)
		end
	end

	this.Origin = nil
	this.RenderBubble = nil
	this.Message = message
	this.BubbleDieDelay = this:ComputeBubbleLifetime(message, isLocalPlayer)
	this.BubbleColor = bubbleColor
	this.IsLocalPlayer = isLocalPlayer

	return this
end

local function createPlayerChatLine(player, message, isLocalPlayer)
	local this = createChatLine(message, BubbleColor.WHITE, isLocalPlayer)

	if player then
		this.User = player.Name
		this.Origin = player.Character
	end

	return this
end

local function createGameChatLine(origin, message, isLocalPlayer, bubbleColor)
	local this = createChatLine(message, bubbleColor, isLocalPlayer)
	this.Origin = origin

	return this
end

function createChatBubbleMain(filePrefix, sliceRect)
	local chatBubbleMain = Instance.new("ImageLabel")
	chatBubbleMain.Name = "ChatBubble"
	chatBubbleMain.ScaleType = Enum.ScaleType.Slice
	chatBubbleMain.SliceCenter = sliceRect
	chatBubbleMain.Image = "rbxasset://textures/" .. tostring(filePrefix) .. ".png"
	chatBubbleMain.BackgroundTransparency = 1
	chatBubbleMain.BorderSizePixel = 0
	chatBubbleMain.Size = UDim2.new(1.0, 0, 1.0, 0)
	chatBubbleMain.Position = UDim2.new(0, 0, 0, 0)
	chatBubbleMain.ImageColor3 = CHAT_BUBBLE_COLOR
	return chatBubbleMain
end

function createChatBubbleTail(position, size)
	local chatBubbleTail = Instance.new("ImageLabel")
	chatBubbleTail.Name = "ChatBubbleTail"
	chatBubbleTail.Image = "rbxasset://textures/ui/dialog_tail.png"
	chatBubbleTail.BackgroundTransparency = 1
	chatBubbleTail.BorderSizePixel = 0
	chatBubbleTail.Position = position
	chatBubbleTail.Size = size
	chatBubbleTail.ImageColor3 = CHAT_BUBBLE_COLOR
	return chatBubbleTail
end

function createChatBubbleWithTail(filePrefix, position, size, sliceRect)
	local chatBubbleMain = createChatBubbleMain(filePrefix, sliceRect)

	local chatBubbleTail = createChatBubbleTail(position, size)
	chatBubbleTail.Parent = chatBubbleMain

	return chatBubbleMain
end

function createScaledChatBubbleWithTail(filePrefix, frameScaleSize, position, sliceRect)
	local chatBubbleMain = createChatBubbleMain(filePrefix, sliceRect)

	local frame = Instance.new("Frame")
	frame.Name = "ChatBubbleTailFrame"
	frame.BackgroundTransparency = 1
	frame.SizeConstraint = Enum.SizeConstraint.RelativeXX
	frame.Position = UDim2.new(0.5, 0, 1, 0)
	frame.Size = UDim2.new(frameScaleSize, 0, frameScaleSize, 0)
	frame.Parent = chatBubbleMain

	local chatBubbleTail = createChatBubbleTail(position, UDim2.new(1, 0, 0.5, 0))
	chatBubbleTail.Parent = frame

	return chatBubbleMain
end

function createChatImposter(filePrefix, dotDotDot, yOffset)
	local result = Instance.new("ImageLabel")
	result.Name = "DialogPlaceholder"
	result.Image = "rbxasset://textures/" .. tostring(filePrefix) .. ".png"
	result.BackgroundTransparency = 1
	result.BorderSizePixel = 0
	result.Position = UDim2.new(0, 0, -1.25, 0)
	result.Size = UDim2.new(1, 0, 1, 0)

	local image = Instance.new("ImageLabel")
	image.Name = "DotDotDot"
	image.Image = "rbxasset://textures/" .. tostring(dotDotDot) .. ".png"
	image.BackgroundTransparency = 1
	image.BorderSizePixel = 0
	image.Position = UDim2.new(0.001, 0, yOffset, 0)
	image.Size = UDim2.new(1, 0, 0.7, 0)
	image.Parent = result

	return result
end

local this = {}
this.ChatBubble = {}
this.ChatBubbleWithTail = {}
this.ScalingChatBubbleWithTail = {}
this.CharacterSortedMsg = createMap()
