import "macros" as { $ } $load $FILE --[[ //FileName: ChatScript.LUA //Written by: Sorcus //Description: Code for lua side chat on Mercury. Supports Scrolling. //NOTE: If you find any bugs or inaccuracies PM Sorcus on Roblox or @Canavus on Twitter ]] forceChatGUI = false -- Utility functions + Globals WaitForChild = (parent, childName) -> until parent\FindFirstChild(childName)? parent.ChildAdded\wait 0.03 parent[childName] IsPhone = -> cGui = Game\GetService "CoreGui" rGui = WaitForChild cGui, "RobloxGui" if rGui.AbsoluteSize.Y < 600 return true false -- Users can use enough white spaces to spoof chatting as other players -- This function removes trailing and leading white spaces -- AFAIK, there is no reason for spam white spaces StringTrim = (str) -> -- %S is whitespaces -- When we find the first non space character defined by ^%s -- we yank out anything in between that and the end of the string -- Everything else is replaced with %1 which is essentially nothing str\gsub "^%s*(.-)%s*$", "%1" until Game.Players.LocalPlayer? wait 0.03 Player = Game.Players.LocalPlayer until Player.Character? wait 0.03 Camera = Game.Workspace.CurrentCamera -- Heliodex's basic New function (basically a simplified version of melt) New = (className, name, props) -> if not props? -- no name was provided props = name name = nil obj = Instance.new className obj.Name = name if name local parent for k, v in pairs props if type(k) == "string" if k == "Parent" parent = v else obj[k] = v elseif type(k) == "number" and type(v) == "userdata" v.Parent = obj obj.Parent = parent obj -- -- Services CoreGuiService = Game\GetService "CoreGui" PlayersService = Game\GetService "Players" GuiService = Game\GetService "GuiService" -- Lua Enums Enums = {} EnumName = {} -- used as unique key for enum name CreateEnum = (enumName) -> (t) -> e = [EnumName]: enumName for i, name in pairs t item = Name: name Value: i Enum: e [EnumName]: enumName : (@, value) -> value == @ or value == @Name or value == @Value : (@) -> "Enum.#{@[EnumName]}.#{@Name}" e[i] = e[name] = e[item] = item Enums[enumName] = e setmetatable e, __call: (@, value) -> @[value] or @[tonumber value] __index: GetEnumItems: (@) -> t = {} for i, item in pairs @ if type(i) == "number" t[] = item table.sort t, (a, b) -> a.Value < b.Value t __tostring: (@) -> "Enum.#{@[EnumName]}" --------------------------------------------------- ------------------ Input class -------------------- Input = Mouse: Player\GetMouse! Speed: 0 Simulating: false Configuration: DefaultSpeed: 1 UserIsScrolling: false --------------------------------------------------- ------------------ Chat class -------------------- Chat = ChatColors: * 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" Gui: nil Frame: nil RenderFrame: nil TapToChatLabel: nil ClickToChatButton: nil ScrollingLock: false EventListener: nil -- This is actually a ring buffer -- Meaning at hitting the historyLength it wraps around -- Reuses the text objects, so chat atmost uses 100 text objects MessageQueue: {} -- Stores all the values for configuring chat Configuration: FontSize: Enum.FontSize.Size12, -- 10 is good -- Also change this when you are changing the above, this is suboptimal but so is our interface to find FontSize NumFontSize: 12 HistoryLength: 20 -- stores up to 50 of the last chat messages for you to scroll through, Size: UDim2.new 0.38, 0, 0.20, 0 MessageColor: Color3.new 1, 1, 1 AdminMessageColor: Color3.new 1, 215 / 255, 0 XScale: 0.025 LifeTime: 45 Position: UDim2.new 0, 2, 0.05, 0 DefaultTweenSpeed: 0.15 -- This could be redone by just using the previous and next fields of the Queue -- But the iterators cause issues, will be optimized later SlotPositions_List: {} -- To precompute and store all player null strings since its an expensive process CachedSpaceStrings_List: {} MouseOnFrame: false GotFocus: false Messages_List: {} MessageThread: nil --[[ Admins_List = {'Sorcus', 'Shedletsky', 'Telamon', 'Tarabyte', 'StickMasterLuke', 'OnlyTwentyCharacters', 'FusRoblox', 'SolarCrane', 'HotThoth', 'JediTkacheff', 'Builderman', 'Brighteyes', 'ReeseMcblox', 'GemLocker', 'GongfuTiger', 'Erik.Cassel', 'Matt Dusek', 'Keith', 'Totbl', 'LordRugDump', 'David.Baszucki', 'Dbapostle', 'DaveYorkRBX', 'nJay', 'OstrichSized', 'TobotRobot', 'twberg', 'Mercury', 'RBAdam', 'Doughtless', 'Anaminus', 'Stravant', 'Cr3470r', 'CodeWriter', 'Games', 'AcesWayUpHigh', 'Phil' }, --]] Admins_List: * "taskmanager" * "Heliodex" * "tako" SafeChat_List: "Use the Chat menu to talk to me.": * "/sc0" * true "I can only see menu chats.": * "/sc1" * true Hello: Hi: * "/sc2_0" * true "Hi there!": true "Hi everyone": true Howdy: * "/sc2_1" * true "Howdy partner!": true Greetings: * "/sc2_2" * true "Greetings everyone": true "Greetings Robloxians!": true "Seasons greetings!": true Welcome: * "/sc2_3" * true "Welcome to my place": true "Welcome to my barbeque": true "Welcome to our base": true "Hey there!": * "/sc2_4" * true "What's up?": * "/sc2_5" * true "How are you doing?": true "How's it going?": true "What's new?": true "Good day": * "/sc2_6" * true "Good morning": true "Good evening": true "Good afternoon": true "Good night": true Silly: * "/sc2_7" * true "Waaaaaaaz up?!": true "Hullo!": true "Behold greatness, mortals!": true "Pardon me, is this Sparta?": true "THIS IS SPARTAAAA!": true "Happy Holidays!": * "/sc2_8" * true "Happy New Year!": true "Happy Valentine's Day!": true "Beware the Ides of March!": true "Happy St. Patrick's Day!": true "Happy Easter!": true "Happy Earth Day!": true "Happy 4th of July!": true "Happy Thanksgiving!": true "Happy Halloween!": true "Happy Hanukkah!": true "Merry Christmas!": true "Happy May Day!": true "Happy Towel Day!": true "Happy Mercury Day!": true "Happy LOL Day!": true * "/sc2" Goodbye: "Good Night": * "/sc3_0" * true "Sweet dreams": true "Go to sleep!": true "Lights out!": true Bedtime: true "Going to bed now": true Later: * "/sc3_1" * true "See ya later": true "Later gator!": true "See you tomorrow": true Bye: * "/sc3_2" * true "Hasta la bye bye!": true "I'll be right back": * "/sc3_3" * true "I have to go": * "/sc3_4" * true Farewell: * "/sc3_5" * true "Take care": true "Have a nice day": true "Goodluck!": true "Ta-ta for now!": true Peace: * "/sc3_6" * true "Peace out!": true "Peace dudes!": true "Rest in pieces!": true Silly: * "/sc3_7" * true "To the batcave!": true "Over and out!": true "Happy trails!": true "I've got to book it!": true "Tootles!": true "Smell you later!": true "GG!": true "My house is on fire! gtg.": true * "/sc3" Friend: "Wanna be friends?": * "/sc4_0" * true "Follow me": * "/sc4_1" * true "Come to my place!": true "Come to my base!": true "Follow me, team!": true "Follow me": true "Your place is cool": * "/sc4_2" * true "Your place is fun": true "Your place is awesome": true "Your place looks good": true "This place is awesome!": true "Thank you": * "/sc4_3" * true "Thanks for playing": true "Thanks for visiting": true "Thanks for everything": true "No, thank you": true Thanx: true "No problem": * "/sc4_4" * true "Don't worry": true "That's ok": true np: true "You are ...": * "/sc4_5" * true "You are great!": true "You are good!": true "You are cool!": true "You are funny!": true "You are silly!": true "You are awesome!": true "You are doing something I don't like, please stop": true "I like ...": * "/sc4_6" * true "I like your name": true "I like your shirt": true "I like your place": true "I like your style": true "I like you": true "I like items": true "I like money": true Sorry: * "/sc4_7" * true "My bad!": true "I'm sorry": true "Whoops!": true "Please forgive me.": true "I forgive you.": true "I didn't mean to do that.": true "Sorry, I'll stop now.": true * "/sc4" Questions: "Who?": * "/sc5_0" * true "Who wants to be my friend?": true "Who wants to be on my team?": true "Who made this brilliant game?": true "What?": * "/sc5_1" * true "What is your favorite animal?": true "What is your favorite game?": true "What is your favorite movie?": true "What is your favorite TV show?": true "What is your favorite music?": true "What are your hobbies?": true "LOLWUT?": true "When?": * "/sc5_2" * true "When are you online?": true "When is the new version coming out?": true "When can we play again?": true "When will your place be done?": true "Where?": * "/sc5_3" * true "Where do you want to go?": true "Where are you going?": true "Where am I?!": true "Where did you go?": true "How?": * "/sc5_4" * true "How are you today?": true "How did you make this cool place?": true "LOLHOW?": true "Can I...": * "/sc5_5" * true "Can I have a tour?": true "Can I be on your team?": true "Can I be your friend?": true "Can I try something?": true "Can I have that please?": true "Can I have that back please?": true "Can I have borrow your hat?": true "Can I have borrow your gear?": true * "/sc5" Answers: "You need help?": * "/sc6_0" * true "Check out the news section": true "Check out the help section": true "Read the wiki!": true "All the answers are in the wiki!": true "I will help you with this.": true "Some people ...": * "/sc6_1" * true Me: true "Not me": true You: true "All of us": true "Everyone but you": true "Builderman!": true "Telamon!": true "My team": true "My group": true Mom: true Dad: true Sister: true Brother: true Cousin: true Grandparent: true Friend: true "Time ...": * "/sc6_2" * true "In the morning": true "In the afternoon": true "At night": true Tomorrow: true "This week": true "This month": true Sometime: true Sometimes: true "Whenever you want": true Never: true "After this": true "In 10 minutes": true "In a couple hours": true "In a couple days": true Animals: * "/sc6_3" * true Cats: Lion: true Tiger: true Leopard: true Cheetah: true Dogs: Wolves: true Beagle: true Collie: true Dalmatian: true Poodle: true Spaniel: true Shepherd: true Terrier: true Retriever: true Horses: Ponies: true Stallions: true Pwnyz: true Reptiles: Dinosaurs: true Lizards: true Snakes: true "Turtles!": true Hamster: true Monkey: true Bears: true Fish: Goldfish: true Sharks: true "Sea Bass": true Halibut: true "Tropical Fish": true Birds: Eagles: true Penguins: true Parakeets: true Owls: true Hawks: true Pidgeons: true Elephants: true "Mythical Beasts": Dragons: true Unicorns: true "Sea Serpents": true Sphinx: true Cyclops: true Minotaurs: true Goblins: true "Honest Politicians": true Ghosts: true "Scylla and Charybdis": true Games: * "/sc6_4" * true Action: true Puzzle: true Strategy: true Racing: true RPG: true "Obstacle Course": true Tycoon: true Roblox: BrickBattle: true "Community Building": true "Roblox Minigames": true "Contest Place": true "Board games": Chess: true Checkers: true "Settlers of Catan": true "Tigris and Euphrates": true "El Grande": true Stratego: true Carcassonne: true Sports: * "/sc6_5" * true Hockey: true Soccer: true Football: true Baseball: true Basketball: true Volleyball: true Tennis: true "Sports team practice": true Watersports: Surfing: true Swimming: true "Water Polo": true "Winter sports": Skiing: true Snowboarding: true Sledding: true Skating: true Adventure: "Rock climbing": true Hiking: true Fishing: true "Horseback riding": true Wacky: Foosball: true Calvinball: true Croquet: true Cricket: true Dodgeball: true Squash: true Trampoline: true "Movies/TV": * "/sc6_6" * true "Science Fiction": true Animated: Anime: true Comedy: true Romantic: true Action: true Fantasy: true Music: * "/sc6_7" * true Country: true Jazz: true Rap: true "Hip-hop": true Techno: true Classical: true Pop: true Rock: true Hobbies: * "/sc6_8" * true Computers: "Building computers": true Videogames: true Coding: true Hacking: true "The Internet": "lol. teh internets!": true "Watching vids": true Dance: true Gymnastics: true "Listening to music": true "Arts and crafts": true "Martial Arts": Karate: true Judo: true "Taikwon Do": true Wushu: true "Street fighting": true "Music lessons": "Playing in my band": true "Playing piano": true "Playing guitar": true "Playing violin": true "Playing drums": true "Playing a weird instrument": true Location: * "/sc6_9" * true USA: West: Alaska: true Arizona: true California: true Colorado: true Hawaii: true Idaho: true Montana: true Nevada: true "New Mexico": true Oregon: true Utah: true Washington: true Wyoming: true South: Alabama: true Arkansas: true Florida: true Georgia: true Kentucky: true Louisiana: true Mississippi: true "North Carolina": true Oklahoma: true "South Carolina": true Tennessee: true Texas: true Virginia: true "West Virginia": true Northeast: Connecticut: true Delaware: true Maine: true Maryland: true Massachusetts: true "New Hampshire": true "New Jersey": true "New York": true Pennsylvania: true "Rhode Island": true Vermont: true Midwest: Illinois: true Indiana: true Iowa: true Kansas: true Michigan: true Minnesota: true Missouri: true Nebraska: true "North Dakota": true Ohio: true "South Dakota": true Wisconsin: true Canada: Alberta: true "British Columbia": true Manitoba: true "New Brunswick": true Newfoundland: true "Northwest Territories": true "Nova Scotia": true Nunavut: true Ontario: true "Prince Edward Island": true Quebec: true Saskatchewan: true Yukon: true Mexico: true "Central America": true Europe: France: true Germany: true Spain: true Italy: true Poland: true Switzerland: true Greece: true Romania: true Netherlands: true "Great Britain": England: true Scotland: true Wales: true "Northern Ireland": true Asia: China: true India: true Japan: true Korea: true Russia: true Vietnam: true "South America": Argentina: true Brazil: true Africa: Eygpt: true Swaziland: true Australia: true "Middle East": true Antarctica: true "New Zealand": true Age: * "/sc6_10" * true Rugrat: true Kid: true Tween: true Teen: true Twenties: true Old: true Ancient: true Mesozoic: true "I don't want to say my age. Don't ask.": true Mood: * "/sc6_11" * true Good: true "Great!": true "Not bad": true Sad: true Hyper: true Chill: true Happy: true "Kind of mad": true Boy: * "/sc6_12" * true Girl: * "/sc6_13" * true "I don't want to say boy or girl. Don't ask.": * "/sc6_14" * true * "/sc6" Game: "Let's build": * "/sc7_0" * true "Let's battle": * "/sc7_1" * true "Nice one!": * "/sc7_2" * true "So far so good": * "/sc7_3" * true "Lucky shot!": * "/sc7_4" * true "Oh man!": * "/sc7_5" * true "I challenge you to a fight!": * "/sc7_6" * true "Help me with this": * "/sc7_7" * true "Let's go to your game": * "/sc7_8" * true "Can you show me how do to that?": * "/sc7_9" * true "Backflip!": * "/sc7_10" * true "Frontflip!": * "/sc7_11" * true "Dance!": * "/sc7_12" * true "I'm on your side!": * "/sc7_13" * true "Game Commands": * "/sc7_14" * true regen: true reset: true go: true fix: true respawn: true * "/sc7" Silly: "Muahahahaha!": true "all your base are belong to me!": true "GET OFF MAH LAWN": true "TEH EPIK DUCK IS COMING!!!": true ROFL: true "1337": * true "i r teh pwnz0r!": true "w00t!": true "z0mg h4x!": true "ub3rR0xXorzage!": true Yes: "Absolutely!": true "Rock on!": true "Totally!": true "Juice!": true "Yay!": true Yesh: true No: "Ummm. No.": true "...": true "Stop!": true "Go away!": true "Don't do that": true "Stop breaking the rules": true "I don't want to": true Ok: "Well... ok": true Sure: true Uncertain: Maybe: true "I don't know": true idk: true "I can't decide": true "Hmm...": true ":-)": ":-(": true ":D": true ":-O": true lol: true "=D": true "D=": true XD: true ";D": true ";)": true O_O: true "=)": true "@_@": true ">_<": true T_T: true "^_^": true "<(0_0<) <(0_0)> (>0_0)> KIRBY DANCE": true ")';": true ":3": true Ratings: "Rate it!": true "I give it a 1 out of 10": true "I give it a 2 out of 10": true "I give it a 3 out of 10": true "I give it a 4 out of 10": true "I give it a 5 out of 10": true "I give it a 6 out of 10": true "I give it a 7 out of 10": true "I give it a 8 out of 10": true "I give it a 9 out of 10": true "I give it a 10 out of 10!": true * CreateEnum"SafeChat" { "Level1", "Level2", "Level3" } SafeChatTree: {} TempSpaceLabel: nil --------------------------------------------------- --------------------------------------------------- GetNameValue = (pName) -> value = 0 for index = 1, #pName cValue = string.byte string.sub pName, index, index reverseIndex = #pName - index + 1 if #pName % 2 == 1 reverseIndex = reverseIndex - 1 if reverseIndex % 4 >= 2 cValue = -cValue value = value + cValue value % 8 Chat.ComputeChatColor = (pName) => @ChatColors[GetNameValue(pName) + 1].Color -- This is context based scrolling Chat.EnableScrolling = (toggle) => -- Genius idea gone to fail, if we switch the camera type we can effectively lock the -- camera and do no click scrolling @MouseOnFrame = false if @RenderFrame @RenderFrame.MouseEnter\connect -> character = Player.Character torso = WaitForChild character, "Torso" head = WaitForChild character, "Head" if toggle @MouseOnFrame = true Camera.CameraType = "Scriptable" -- Get relative position of camera and keep to it Spawn -> currentRelativePos = Camera.CoordinateFrame.p - torso.Position while Chat.MouseOnFrame Camera.CoordinateFrame = CFrame.new torso.Position + currentRelativePos, head.Position wait 0.015 @RenderFrame.MouseLeave\connect -> Camera.CameraType = "Custom" @MouseOnFrame = false -- TODO: Scrolling using Mouse wheel -- Chat.OnScroll = (speed) => -- if @MouseOnFrame -- -- -- end -- end -- Check if we are running on a touch device Chat.IsTouchDevice ==> touchEnabled = false try touchEnabled = Game\GetService"UserInputService".TouchEnabled touchEnabled -- Scrolling -- Chat.ScrollQueue = (value) => -- for i = 1, #@MessageQueue -- if @MessageQueue[i] -- for _, label in pairs(@MessageQueue[i]) -- next = @MessageQueue[i].Next -- previous = @MessageQueue[i].Previous -- if label and label\IsA('TextLabel') or label\IsA('TextButton') -- if value > 0 and previous and previous['Message'] -- label.Position = previous['Message'].Position -- elseif value < 1 and next['Message'] -- label.Position = previous['Message'].Position -- Handles the rendering of the text objects in their appropriate places Chat.UpdateQueue = (field, diff) => -- Have to do some sort of correction here for i = #@MessageQueue, 1, -1 if @MessageQueue[i] for _, label in pairs @MessageQueue[i] if label and type(label) ~= "table" and type(label) ~= "number" if label\IsA"TextLabel" or label\IsA "TextButton" if diff label.Position = label.Position - UDim2.new(0, 0, diff, 0) else if field == @MessageQueue[i] label.Position = UDim2.new( @Configuration.XScale, 0, label.Position.Y.Scale - field["Message"].Size.Y.Scale, 0 ) -- Just to show up popping effect for the latest message in chat Spawn -> wait 0.05 while label.TextTransparency >= 0 label.TextTransparency = label.TextTransparency - 0.2 wait 0.03 label.TextStrokeTransparency = if label == field["Message"] 0.8 else 1 else label.Position = UDim2.new( @Configuration.XScale, 0, label.Position.Y.Scale - field["Message"].Size.Y.Scale, 0 ) if label.Position.Y.Scale < -0.01 -- NOTE: Remove this fix when Textbounds is fixed label.Visible = false label\Destroy! Chat.CreateScrollBar ==> -- Code for scrolling is in here, partially, but scroll bar drawing isn't drawn -- TODO: Implement -- For scrolling, to see if we hit the bounds so that we can stop it from scrolling anymore Chat.CheckIfInBounds = (value) => if #Chat.MessageQueue < 3 return true if value > 0 and Chat.MessageQueue[1] and Chat.MessageQueue[1]["Player"] and Chat.MessageQueue[1]["Player"].Position.Y.Scale == 0 true elseif value < 0 and Chat.MessageQueue[1] and Chat.MessageQueue[1]["Player"] and Chat.MessageQueue[1]["Player"].Position.Y.Scale < 0 true else false -- This is to precompute all playerName space strings -- This is used to offset the message by exactly this + 2 spacestrings Chat.ComputeSpaceString = (pLabel) => nString = " " if not @TempSpaceLabel @TempSpaceLabel = New "TextButton", "SpaceButton" Size: UDim2.new 0, pLabel.AbsoluteSize.X, 0, pLabel.AbsoluteSize.Y FontSize: @Configuration.FontSize Parent: @RenderFrame BackgroundTransparency: 1 Text: nString else @TempSpaceLabel.Text = nString while @TempSpaceLabel.TextBounds.X < pLabel.TextBounds.X nString ..= " " @TempSpaceLabel.Text = nString nString ..= " " @CachedSpaceStrings_List[pLabel.Text] = nString @TempSpaceLabel.Text = "" nString -- When the playerChatted event fires -- The message is what the player chatted Chat.UpdateChat = (cPlayer, message) => messageField = Player: cPlayer Message: message if coroutine.status(Chat.MessageThread) == "dead" --Chat.Messages_List = {} table.insert Chat.Messages_List, messageField Chat.MessageThread = coroutine.create -> for i = 1, #Chat.Messages_List field = Chat.Messages_List[i] Chat\CreateMessage field["Player"], field["Message"] Chat.Messages_List = {} coroutine.resume Chat.MessageThread else table.insert Chat.Messages_List, messageField -- Chat.RecalculateSpacing ==> --[[for i = 1, #@MessageQueue pLabel = @MessageQueue[i]['Player'] mLabel = @MessageQueue[i]['Message'] prevYScale = mLabel.Size.Y.Scale prevText = mLabel.Text mLabel.Text = prevText heightField = mLabel.TextBounds.Y mLabel.Size = UDim2.new(1, 0, heightField/@RenderFrame.AbsoluteSize.Y, 0) pLabel.Size = mLabel.Size diff = mLabel.Size.Y.Scale - prevYScale Chat\UpdateQueue(@MessageQueue[i], diff) end ]] -- Chat.ApplyFilter = (str) => -- --[[for _, word in pair(@Filter_List) -- if string.find(str, word) -- str\gsub word, '@#$^' -- ]] -- NOTE: Temporarily disabled ring buffer to allow for chat to always wrap around Chat.CreateMessage = (cPlayer, message) => local pName if not cPlayer pName = "" else pName = cPlayer.Name message = StringTrim message local pLabel local mLabel -- Our history stores upto 50 messages that is 100 textlabels -- If we ever hit the mark, which would be in every popular game btw -- we wrap around and reuse the labels if #@MessageQueue > @Configuration.HistoryLength --[[pLabel = @MessageQueue[#@MessageQueue]['Player'] mLabel = @MessageQueue[#@MessageQueue]['Message'] pLabel.Text = pName .. ':' pLabel.Name = pName local pColor if cPlayer.Neutral pLabel.TextColor3 = Chat\ComputeChatColor(pName) else pLabel.TextColor3 = cPlayer.TeamColor.Color local nString if not @CachedSpaceStrings_List[pName] nString = Chat\ComputeSpaceString(pLabel) else nString = @CachedSpaceStrings_List[pName] end mLabel.Text = "" mLabel.Name = pName .. " - message" mLabel.Text = nString .. message; mLabel.Parent = nil mLabel.Parent = @RenderFrame mLabel.Position = UDim2.new 0, 0, 1, 0; pLabel.Position = UDim2.new 0, 0, 1, 0;]] -- Reinserted at the beginning, ring buffer @MessageQueue[#@MessageQueue] = nil --else -- Haven't hit the mark yet, so keep creating pLabel = New "TextLabel", pName, Text: pName .. ":" -- TextColor3 = pColor FontSize: Chat.Configuration.FontSize TextXAlignment: Enum.TextXAlignment.Left TextYAlignment: Enum.TextYAlignment.Top Parent: @RenderFrame TextWrapped: false Size: UDim2.new 1, 0, 0.1, 0 BackgroundTransparency: 1 TextTransparency: 1 Position: UDim2.new 0, 0, 1, 0 BorderSizePixel: 0 TextStrokeColor3: Color3.new 0.5, 0.5, 0.5 TextStrokeTransparency: 0.75 --Active = false if cPlayer.Neutral pLabel.TextColor3 = Chat\ComputeChatColor pName else pLabel.TextColor3 = cPlayer.TeamColor.Color local nString if not @CachedSpaceStrings_List[pName] nString = Chat\ComputeSpaceString(pLabel) else nString = @CachedSpaceStrings_List[pName] mLabel = New "TextLabel", "#{pName} - message" -- Max is 3 lines Size: UDim2.new 1, 0, 0.5, 0 TextColor3: Chat.Configuration.MessageColor FontSize: Chat.Configuration.FontSize TextXAlignment: Enum.TextXAlignment.Left TextYAlignment: Enum.TextYAlignment.Top Text: "" -- this is to stop when the engine reverts the swear words to default, which is button, ugh Parent: @RenderFrame TextWrapped: true BackgroundTransparency: 1 TextTransparency: 1 Position: UDim2.new 0, 0, 1, 0 BorderSizePixel: 0 TextStrokeColor3: Color3.new 0, 0, 0 --TextStrokeTransparency = 0.8; --Active = false; mLabel.Text = nString .. message if not pName pLabel.Text = "" mLabel.TextColor3 = Color3.new 0, 0.4, 1.0 --end for _, adminName in pairs @Admins_List if string.lower(adminName) == string.lower pName mLabel.TextColor3 = @Configuration.AdminMessageColor pLabel.Visible = true mLabel.Visible = true -- This will give beautiful multilines as well heightField = mLabel.TextBounds.Y mLabel.Size = UDim2.new 1, 0, heightField / @RenderFrame.AbsoluteSize.Y, 0 pLabel.Size = mLabel.Size queueField = {} queueField["Player"] = pLabel queueField["Message"] = mLabel queueField["SpawnTime"] = tick! -- Used for identifying when to make the message invisible table.insert @MessageQueue, 1, queueField Chat\UpdateQueue queueField Chat.ScreenSizeChanged ==> wait! while @Frame.AbsoluteSize.Y > 120 @Frame.Size = @Frame.Size - UDim2.new 0, 0, 0.005, 0 -- Chat\RecalculateSpacing! Chat.FindButtonTree = (scButton, rootList) => list = {} rootList = rootList or @SafeChatTree for button, _ in pairs rootList if button == scButton list = rootList[button] elseif type(rootList[button]) == "table" list = Chat\FindButtonTree scButton, rootList[button] return list Chat.ToggleSafeChatMenu = (scButton) => list = Chat\FindButtonTree scButton, @SafeChatTree if list for button, _ in pairs list if button\IsA"TextButton" or button\IsA "ImageButton" button.Visible = not button.Visible return true return false Chat.CreateSafeChatOptions = (list, rootButton) => text_List = {} count = 0 text_List[rootButton] = {} text_List[rootButton][1] = list[1] rootButton = rootButton or @SafeChatButton for msg, _ in pairs list if type(msg) == "string" chatText = New "TextButton", msg, Text: msg Size: UDim2.new 0, 100, 0, 20 TextXAlignment: Enum.TextXAlignment.Center TextColor3: Color3.new 0.2, 0.1, 0.1 BackgroundTransparency: 0.5 BackgroundColor3: Color3.new 1, 1, 1 Parent: @SafeChatFrame Visible: false Position: UDim2.new( 0, rootButton.Position.X.Scale + 105, 0, rootButton.Position.Y.Scale - (count - 3) * 100 ) count = count + 1 if type(list[msg]) == "table" text_List[rootButton][chatText] = Chat\CreateSafeChatOptions list[msg], chatText -- else -- --table.insert(text_List[chatText], true) chatText.MouseEnter\connect -> Chat\ToggleSafeChatMenu chatText chatText.MouseLeave\connect -> Chat\ToggleSafeChatMenu chatText chatText.MouseButton1Click\connect -> lList = Chat\FindButtonTree chatText -- if lList -- for i, v in pairs lList try PlayersService\Chat lList[1] return text_List Chat.CreateSafeChatGui ==> @SafeChatFrame = New "Frame", "SafeChatFrame" Size: UDim2.new 1, 0, 1, 0 Parent: @Gui BackgroundTransparency: 1 * New "ImageButton", "SafeChatButton" Size: UDim2.new 0, 44, 0, 31 Position: UDim2.new 0, 1, 0.35, 0 BackgroundTransparency: 1 Image: "http://www.roblox.com/asset/?id=97080365" @SafeChatButton = @SafeChatFrame.SafeChatButton -- safe chat button is the root of this tree @SafeChatTree[@SafeChatButton] = Chat\CreateSafeChatOptions @SafeChat_List, @SafeChatButton @SafeChatButton.MouseButton1Click\connect -> Chat\ToggleSafeChatMenu @SafeChatButton Chat.FocusOnChatBar ==> if @ClickToChatButton @ClickToChatButton.Visible = false @GotFocus = true if @Frame["Background"] @Frame.Background.Visible = false @ChatBar\CaptureFocus! -- For touch devices we create a button instead Chat.CreateTouchButton ==> @ChatTouchFrame = New "Frame", "ChatTouchFrame" Size: UDim2.new 0, 128, 0, 32 Position: UDim2.new 0, 88, 0, 0 BackgroundTransparency: 1 Parent: @Gui * New "ImageButton", "ChatLabel" Size: UDim2.new 0, 74, 0, 28 Position: UDim2.new 0, 0, 0, 0 BackgroundTransparency: 1 ZIndex: 2.0 * New "ImageLabel", "Background" Size: UDim2.new 1, 0, 1, 0 Position: UDim2.new 0, 0, 0, 0 BackgroundTransparency: 1 Image: "http://www.roblox.com/asset/?id=97078724" @TapToChatLabel = @ChatTouchFrame.ChatLabel @TouchLabelBackground = @ChatTouchFrame.Background @ChatBar = New "TextBox", "ChatBar" Size: UDim2.new 1, 0, 0.2, 0 Position: UDim2.new 0, 0, 0.8, 800 Text: "" ZIndex: 1 BackgroundTransparency: 1 Parent: @Frame TextXAlignment: Enum.TextXAlignment.Left TextColor3: Color3.new 1, 1, 1 ClearTextOnFocus: false @TapToChatLabel.MouseButton1Click\connect -> @TapToChatLabel.Visible = false --@ChatBar.Visible = true --@Frame.Background.Visible = true @ChatBar\CaptureFocus! @GotFocus = true if @TouchLabelBackground @TouchLabelBackground.Visible = false -- Non touch devices, create the bottom chat bar Chat.CreateChatBar ==> -- okay now we local status, result = try return GuiService.UseLuaChat if forceChatGUI or (status and result) @ClickToChatButton = New "TextButton", "ClickToChat" Size: UDim2.new 1, 0, 0, 20 BackgroundTransparency: 1 ZIndex: 2.0 Parent: @Gui Text: 'To chat click here or press "/" key' TextColor3: Color3.new 1, 1, 0.9 Position: UDim2.new 0, 0, 1, 0 TextXAlignment: Enum.TextXAlignment.Left FontSize: Enum.FontSize.Size12 @ChatBar = New "TextBox", "ChatBar" Size: UDim2.new 1, 0, 0, 20 Position: UDim2.new 0, 0, 1, 0 Text: "" ZIndex: 1 BackgroundColor3: Color3.new 0, 0, 0 BackgroundTransparency: 0.25 Parent: @Gui TextXAlignment: Enum.TextXAlignment.Left TextColor3: Color3.new 1, 1, 1 FontSize: Enum.FontSize.Size12 ClearTextOnFocus: false -- Engine has code to offset the entire world, so if we do it by -20 pixels nothing gets in our chat's way --GuiService\SetGlobalSizeOffsetPixel(0, -20) local success, error = try GuiService\SetGlobalGuiInset 0, 0, 0, 20 if not success GuiService\SetGlobalSizeOffsetPixel 0, -20 -- CHatHotKey is '/' GuiService\AddSpecialKey Enum.SpecialKey.ChatHotkey GuiService.SpecialKeyPressed\connect (key) -> if key == Enum.SpecialKey.ChatHotkey Chat\FocusOnChatBar! @ClickToChatButton.MouseButton1Click\connect -> Chat\FocusOnChatBar! -- Create the initial Chat stuff -- Done only once Chat.CreateGui ==> @Gui = WaitForChild CoreGuiService, "RobloxGui" @Frame = New "Frame", "ChatFrame" --Size: @Configuration.Size Size: UDim2.new 0, 500, 0, 120 Position: UDim2.new 0, 0, 0, 5 BackgroundTransparency: 1 --ClipsDescendants: true ZIndex: 0 Parent: @Gui Active: false * New "ImageLabel", "Background" Image: "http://www.roblox.com/asset/?id=97120937" --96551212' Size: UDim2.new 1.3, 0, 1.64, 0 Position: UDim2.new 0, 0, 0, 0 BackgroundTransparency: 1 ZIndex: 0 Visible: false * New "Frame", "Border" Size: UDim2.new 1, 0, 0, 1 Position: UDim2.new 0, 0, 0.8, 0 BackgroundTransparency: 0 BackgroundColor3: Color3.new 236 / 255, 236 / 255, 236 / 255 BorderSizePixel: 0 Visible: false * New "Frame", "ChatRenderFrame" Size: UDim2.new 1.02, 0, 1.01, 0 Position: UDim2.new 0, 0, 0, 0 BackgroundTransparency: 1 --ClipsDescendants: true ZIndex: 0 Active: false Spawn -> wait 0.5 if IsPhone! @Frame.Size = UDim2.new 0, 280, 0, 120 @RenderFrame = @Frame.ChatRenderFrame if Chat\IsTouchDevice! @Frame.Position = @Configuration.Position @RenderFrame.Size = UDim2.new 1, 0, 1, 0 elseif @Frame.AbsoluteSize.Y > 120 Chat\ScreenSizeChanged! @Gui.Changed\connect (property) -> if property == "AbsoluteSize" Chat\ScreenSizeChanged! if forceChatGUI or Player.ChatMode == Enum.ChatMode.TextAndMenu if Chat\IsTouchDevice! Chat\CreateTouchButton! else Chat\CreateChatBar! --Chat\CreateSafeChatGui! if @ChatBar @ChatBar.FocusLost\connect (enterPressed) -> Chat.GotFocus = false if Chat\IsTouchDevice! @ChatBar.Visible = false @TapToChatLabel.Visible = true if @TouchLabelBackground @TouchLabelBackground.Visible = true if enterPressed and @ChatBar.Text ~= "" cText = @ChatBar.Text if string.sub(@ChatBar.Text, 1, 1) == "%" cText = "(TEAM) #{string.sub cText, 2, #cText}" try PlayersService\TeamChat cText else try PlayersService\Chat cText if @ClickToChatButton @ClickToChatButton.Visible = true @ChatBar.Text = "" Spawn -> wait 5.0 if not Chat.GotFocus Chat.Frame.Background.Visible = false -- Scrolling function -- Applies a speed(velocity) to have nice scrolling effect Input.OnMouseScroll = => Spawn -> -- How long should the speed last? while Input.Speed ~= 0 if Input.Speed > 1 while Input.Speed > 0 Input.Speed = Input.Speed - 1 wait 0.25 elseif Input.Speed < 0 while Input.Speed < 0 Input.Speed = Input.Speed + 1 wait 0.25 wait 0.03 return if Chat\CheckIfInBounds Input.Speed Chat\ScrollQueue! Input.ApplySpeed = (value) => Input.Speed = Input.Speed + value if not @Simulating Input\OnMouseScroll! Input.Initialize = => @Mouse.WheelBackward\connect -> Input\ApplySpeed @Configuration.DefaultSpeed @Mouse.WheelForward\connect -> Input\ApplySpeed @Configuration.DefaultSpeed Chat.FindMessageInSafeChat = (message, list) => foundMessage = false for msg, _ in pairs list if msg == message return true if type(list[msg]) == "table" foundMessage = Chat\FindMessageInSafeChat message, list[msg] if foundMessage return true foundMessage -- Just a wrapper around our PlayerChatted event Chat.PlayerChatted = (...) => args = { ... } -- argCount = select("#", ...) local player, message -- This doesn't look very good, but what else to do? if args[2] player = args[2] if args[3] message = args[3] if string.sub(message, 1, 1) == "%" message = "(TEAM) #{string.sub message, 2, #message}" if PlayersService.ClassicChat if not (string.sub(message, 1, 3) == "/e " or string.sub(message, 1, 7) == "/emote ") and (forceChatGUI or Player.ChatMode == Enum.ChatMode.TextAndMenu) or (Player.ChatMode == Enum.ChatMode.Menu and string.sub(message, 1, 3) == "/sc") or Chat\FindMessageInSafeChat message, @SafeChat_List Chat\UpdateChat player, message -- After Chat.Configuration.Lifetime seconds of existence, the labels become invisible -- Runs only every 5 seconds and has to loop through 50 values -- Shouldn't be too expensive Chat.CullThread ==> while true if #@MessageQueue > 0 for _, field in pairs(@MessageQueue) if field["SpawnTime"] and field["Player"] and field["Message"] and tick! - field["SpawnTime"] > @Configuration.LifeTime field["Player"].Visible = false field["Message"].Visible = false wait 5.0 -- RobloxLock everything so users can't delete them(?) Chat.LockAllFields = (gui) => children = gui\GetChildren! for i = 1, #children children[i].RobloxLocked = true if #children[i]\GetChildren! > 0 Chat\LockAllFields children[i] Chat.CoreGuiChanged = (coreGuiType, enabled) => if coreGuiType == Enum.CoreGuiType.Chat or coreGuiType == Enum.CoreGuiType.All if @Frame @Frame.Visible = enabled if not Chat\IsTouchDevice! and @ChatBar @ChatBar.Visible = enabled GuiService\SetGlobalGuiInset 0, 0, 0, if enabled 20 else 0 -- Constructor -- This function initializes everything Chat.Initialize ==> Chat\CreateGui! try Chat\CoreGuiChanged Enum.CoreGuiType.Chat, Game.StarterGui\GetCoreGuiEnabled Enum.CoreGuiType.Chat Game.StarterGui.CoreGuiChangedSignal\connect (coreGuiType, enabled) -> Chat\CoreGuiChanged coreGuiType, enabled @EventListener = PlayersService.PlayerChatted\connect (...) -> -- This event has 4 callback arguments -- Enum.PlayerChatType.All, chatPlayer, message, targetPlayer Chat\PlayerChatted ... @MessageThread = coroutine.create -> coroutine.resume @MessageThread -- Initialize input for us Input\Initialize! -- Eww, everytime a player is added, you have to redo the connection -- Seems this is not automatic -- NOTE: PlayerAdded only fires on the server, hence ChildAdded is used here PlayersService.ChildAdded\connect -> Chat.EventListener\disconnect! @EventListener = PlayersService.PlayerChatted\connect (...) -> -- This event has 4 callback arguments -- Enum.PlayerChatType.All, chatPlayer, message, targetPlayer Chat\PlayerChatted ... Spawn -> Chat\CullThread! @Frame.RobloxLocked = true Chat\LockAllFields @Frame @Frame.DescendantAdded\connect (descendant) -> Chat\LockAllFields descendant Chat\Initialize!