SHOW:
|
|
- or go back to the newest paste.
1 | --game:GetService("StarterGui"):SetCoreGuiEnabled("Chat", false) | |
2 | local NameColorDependsOnChatColor = true | |
3 | local StrokeTransparency = 0.5 -- Number 0 to 1. Lower stroke transparency is easier to read against colorful backgrounds. | |
4 | ||
5 | while not _G.PlayerListAPI do wait() end | |
6 | local PLAYERLIST_ADMINS, PLAYERLIST_CHATCOLORS, PLAYERLIST_ALIASES = _G.PlayerListAPI:GetChatData() | |
7 | ||
8 | local forceChatGUI = false | |
9 | ||
10 | -- Utility functions + Globals | |
11 | local function WaitForChild(parent, childName) | |
12 | while parent:FindFirstChild(childName) == nil do | |
13 | parent.ChildAdded:wait(0.03) | |
14 | end | |
15 | return parent[childName] | |
16 | end | |
17 | ||
18 | local function typedef(obj) | |
19 | return obj | |
20 | end | |
21 | ||
22 | local function IsPhone() | |
23 | local rGui = script.Parent | |
24 | if rGui.AbsoluteSize.Y < 600 then | |
25 | return true | |
26 | end | |
27 | return false | |
28 | end | |
29 | ||
30 | local export = {} | |
31 | export.NameColorDependsOnChatColor = NameColorDependsOnChatColor | |
32 | ||
33 | export.SetNameColorDependsOnChatColor = function(self, b) | |
34 | self.NameColorDependsOnChatColor = b | |
35 | end | |
36 | ||
37 | -- Users can use enough white spaces to spoof chatting as other players | |
38 | -- This function removes trailing and leading white spaces | |
39 | -- AFAIK, there is no reason for spam white spaces | |
40 | local function StringTrim(str) | |
41 | -- %S is whitespaces | |
42 | -- When we find the first non space character defined by ^%s | |
43 | -- we yank out anything in between that and the end of the string | |
44 | -- Everything else is replaced with %1 which is essentially nothing | |
45 | return (str:gsub("^%s*(.-)%s*$", "%1")) | |
46 | end | |
47 | ||
48 | while Game.Players.LocalPlayer == nil do wait(0.03) end | |
49 | ||
50 | local Player = Game.Players.LocalPlayer | |
51 | while Player.Character == nil do wait(0.03) end | |
52 | local RbxUtility = LoadLibrary('RbxUtility') | |
53 | local Gui = typedef(RbxUtility) | |
54 | local Camera = Game.Workspace.CurrentCamera | |
55 | ||
56 | -- Services | |
57 | local CoreGuiService = Game:GetService('CoreGui') | |
58 | local PlayersService = Game:GetService('Players') | |
59 | local DebrisService= Game:GetService('Debris') | |
60 | local GuiService = Game:GetService('GuiService') | |
61 | ||
62 | -- Lua Enums | |
63 | local Enums do | |
64 | Enums = {} | |
65 | local EnumName = {} -- used as unique key for enum name | |
66 | local enum_mt = { | |
67 | __call = function(self,value) | |
68 | return self[value] or self[tonumber(value)] | |
69 | end; | |
70 | __index = { | |
71 | GetEnumItems = function(self) | |
72 | local t = {} | |
73 | for i,item in pairs(self) do | |
74 | if type(i) == 'number' then | |
75 | t[#t+1] = item | |
76 | end | |
77 | end | |
78 | table.sort(t,function(a,b) return a.Value < b.Value end) | |
79 | return t | |
80 | end; | |
81 | }; | |
82 | __tostring = function(self) | |
83 | return "Enum." .. self[EnumName] | |
84 | end; | |
85 | } | |
86 | local item_mt = { | |
87 | __call = function(self,value) | |
88 | return value == self or value == self.Name or value == self.Value | |
89 | end; | |
90 | __tostring = function(self) | |
91 | return "Enum." .. self[EnumName] .. "." .. self.Name | |
92 | end; | |
93 | } | |
94 | function CreateEnum(enumName) | |
95 | return function(t) | |
96 | local e = {[EnumName] = enumName} | |
97 | for i,name in pairs(t) do | |
98 | local item = setmetatable({Name=name,Value=i,Enum=e,[EnumName]=enumName},item_mt) | |
99 | e[i] = item | |
100 | e[name] = item | |
101 | e[item] = item | |
102 | end | |
103 | Enums[enumName] = e | |
104 | return setmetatable(e, enum_mt) | |
105 | end | |
106 | end | |
107 | end | |
108 | --------------------------------------------------- | |
109 | ------------------ Input class -------------------- | |
110 | local Input = { | |
111 | Mouse = Player:GetMouse(), | |
112 | Speed = 0, | |
113 | Simulating = false, | |
114 | ||
115 | Configuration = { | |
116 | DefaultSpeed = 1 | |
117 | }, | |
118 | UserIsScrolling = false | |
119 | } | |
120 | ||
121 | --------------------------------------------------- | |
122 | ------------------ Chat class -------------------- | |
123 | local Chat = { | |
124 | ||
125 | ChatColors = { | |
126 | BrickColor.new("Bright red"), | |
127 | BrickColor.new("Bright blue"), | |
128 | BrickColor.new("Earth green"), | |
129 | BrickColor.new("Bright violet"), | |
130 | BrickColor.new("Bright orange"), | |
131 | BrickColor.new("Bright yellow"), | |
132 | BrickColor.new("Light reddish violet"), | |
133 | BrickColor.new("Brick yellow"), | |
134 | }, | |
135 | ||
136 | Gui = nil, | |
137 | Frame = nil, | |
138 | RenderFrame = nil, | |
139 | TapToChatLabel = nil, | |
140 | ClickToChatButton = nil, | |
141 | ||
142 | ScrollingLock = false, | |
143 | EventListener = nil, | |
144 | ||
145 | -- This is actually a ring buffer | |
146 | -- Meaning at hitting the historyLength it wraps around | |
147 | -- Reuses the text objects, so chat atmost uses 100 text objects | |
148 | MessageQueue = {}, | |
149 | ||
150 | -- Stores all the values for configuring chat | |
151 | Configuration = { | |
152 | FontSize = Enum.FontSize.Size12, -- 10 is good | |
153 | -- Also change this when you are changing the above, this is suboptimal but so is our interface to find FontSize | |
154 | NumFontSize = 12, | |
155 | HistoryLength = 20, -- stores up to 50 of the last chat messages for you to scroll through, | |
156 | Size = UDim2.new(0.38, 0, 0.20, 0), | |
157 | MessageColor = Color3.new(1, 1, 1), | |
158 | AdminMessageColor = Color3.new(1, 215/255, 0), | |
159 | XScale = 0.025, | |
160 | LifeTime = 45, | |
161 | Position = UDim2.new(0, 2, 0.05, 0), | |
162 | DefaultTweenSpeed = 0.15, | |
163 | }, | |
164 | ||
165 | -- This could be redone by just using the previous and next fields of the Queue | |
166 | -- But the iterators cause issues, will be optimized later | |
167 | SlotPositions_List = {}, | |
168 | -- To precompute and store all player null strings since its an expensive process | |
169 | CachedSpaceStrings_List = {}, | |
170 | MouseOnFrame = false, | |
171 | GotFocus = false, | |
172 | ||
173 | Messages_List = {}, | |
174 | MessageThread = nil, | |
175 | ||
176 | Admins_List = PLAYERLIST_ADMINS, | |
177 | ||
178 | SafeChat_List = { | |
179 | ['Use the Chat menu to talk to me.'] = {'/sc 0', true}, | |
180 | ['I can only see menu chats.'] = {'/sc 1', true}, | |
181 | ['Hello'] = { | |
182 | ['Hi'] = {'/sc 2_0', true, ['Hi there!'] = true, ['Hi everyone'] = true}, | |
183 | ['Howdy'] = {'/sc 2_1', true, ['Howdy partner!'] = true}, | |
184 | ['Greetings'] = {'/sc 2_2', true, ['Greetings everyone'] = true, ['Greetings Robloxians!'] = true, ['Seasons greetings!'] = true}, | |
185 | ['Welcome'] = {'/sc 2_3', true, ['Welcome to my place'] = true, ['Welcome to my barbeque'] = true, ['Welcome to our base'] = true}, | |
186 | ['Hey there!'] = {'/sc 2_4', true}, | |
187 | ['What\'s up?'] = {'/sc 2_5', true, ['How are you doing?'] = true, ['How\'s it going?'] = true, ['What\'s new?'] = true}, | |
188 | ['Good day'] = {'/sc 2_6', true, ['Good morning'] = true, ['Good evening'] = true, ['Good afternoon'] = true, ['Good night'] = true}, | |
189 | ['Silly'] = {'/sc 2_7', true, ['Waaaaaaaz up?!'] = true, ['Hullo!'] = true, ['Behold greatness, mortals!'] = true, ['Pardon me, is this Sparta?'] = true, ['THIS IS SPARTAAAA!'] = true}, | |
190 | ['Happy Holidays!'] = {'/sc 2_8', true, ['Happy New Year!'] = true, | |
191 | ['Happy Valentine\'s Day!'] = true, | |
192 | ['Beware the Ides of March!'] = true, | |
193 | ['Happy St. Patrick\'s Day!'] = true, | |
194 | ['Happy Easter!'] = true, | |
195 | ['Happy Earth Day!'] = true, | |
196 | ['Happy 4th of July!'] = true, | |
197 | ['Happy Thanksgiving!'] = true, | |
198 | ['Happy Halloween!'] = true, | |
199 | ['Happy Hanukkah!'] = true, | |
200 | ['Merry Christmas!'] = true, | |
201 | ['Happy Halloween!'] = true, | |
202 | ['Happy Earth Day!'] = true, | |
203 | ['Happy May Day!'] = true, | |
204 | ['Happy Towel Day!'] = true, | |
205 | ['Happy ROBLOX Day!'] = true, | |
206 | ['Happy LOL Day!'] = true }, | |
207 | ||
208 | [1] = '/sc 2' | |
209 | }, | |
210 | ['Goodbye'] = { | |
211 | ['Good Night']= {'/sc 3_0', true, | |
212 | ['Sweet dreams'] = true, | |
213 | ['Go to sleep!'] = true, | |
214 | ['Lights out!'] = true, | |
215 | ['Bedtime'] = true, | |
216 | ['Going to bed now'] = true}, | |
217 | ||
218 | ['Later']= {'/sc 3_1', true, | |
219 | ['See ya later'] = true, | |
220 | ['Later gator!'] = true, | |
221 | ['See you tomorrow'] = true}, | |
222 | ||
223 | ['Bye'] = {'/sc 3_2', true, ['Hasta la bye bye!'] = true}, | |
224 | ['I\'ll be right back'] = {'/sc 3_3', true}, | |
225 | ['I have to go'] = {'/sc 3_4', true}, | |
226 | ['Farewell'] = {'/sc 3_5', true, ['Take care'] = true, ['Have a nice day'] = true, ['Goodluck!'] = true, ['Ta-ta for now!'] = true}, | |
227 | ['Peace'] = {'/sc 3_6', true, ['Peace out!'] = true, ['Peace dudes!'] = true, ['Rest in pieces!'] = true}, | |
228 | ['Silly'] = {'/sc 3_7', true, | |
229 | ['To the batcave!'] = true, | |
230 | ['Over and out!'] = true, | |
231 | ['Happy trails!'] = true, | |
232 | ['I\'ve got to book it!'] = true, | |
233 | ['Tootles!'] = true, | |
234 | ['Smell you later!'] = true, | |
235 | ['GG!'] = true, | |
236 | ['My house is on fire! gtg.'] = true}, | |
237 | [1] = '/sc 3' | |
238 | }, | |
239 | ['Friend'] ={ | |
240 | ['Wanna be friends?'] = {'/sc 4_0', true}, | |
241 | ['Follow me'] = {'/sc 4_1', true, ['Come to my place!'] = true, ['Come to my base!'] = true, ['Follow me, team!'] = true, ['Follow me'] = true}, | |
242 | ['Your place is cool'] = {'/sc 4_2', true, ['Your place is fun'] = true, ['Your place is awesome'] = true, ['Your place looks good'] = true, ['This place is awesome!'] = true}, | |
243 | ['Thank you'] = {'/sc 4_3', true, ['Thanks for playing'] = true, ['Thanks for visiting'] = true, ['Thanks for everything'] = true, ['No, thank you'] = true, ['Thanx'] = true}, | |
244 | ['No problem'] = {'/sc 4_4', true, ['Don\'t worry'] = true, ['That\'s ok'] = true, ['np'] = true}, | |
245 | ['You are ...'] = {'/sc 4_5', true, | |
246 | ['You are great!'] = true, | |
247 | ['You are good!'] = true, | |
248 | ['You are cool!'] = true, | |
249 | ['You are funny!'] = true, | |
250 | ['You are silly!'] = true, | |
251 | ['You are awesome!'] = true, | |
252 | ['You are doing something I don\'t like, please stop'] = true | |
253 | }, | |
254 | ['I like ...'] = {'/sc 4_6', true, ['I like your name'] = true, ['I like your shirt'] = true, ['I like your place'] = true, ['I like your style'] = true, | |
255 | ['I like you'] = true, ['I like items'] = true, ['I like money'] = true}, | |
256 | ['Sorry'] = {'/sc 4_7', true, ['My bad!'] = true, ['I\'m sorry'] = true, ['Whoops!'] = true, ['Please forgive me.'] = true, ['I forgive you.'] = true, | |
257 | ['I didn\'t mean to do that.'] = true, ['Sorry, I\'ll stop now.'] = true}, | |
258 | [1] = '/sc 4' | |
259 | }, | |
260 | ['Questions'] = { | |
261 | ['Who?'] = {'/sc 5_0', true, ['Who wants to be my friend?'] = true, ['Who wants to be on my team?'] = true, ['Who made this brilliant game?'] = true}, | |
262 | ['What?'] = {'/sc 5_1', true, ['What is your favorite animal?'] = true, ['What is your favorite game?'] = true, ['What is your favorite movie?'] = true, | |
263 | ['What is your favorite TV show?'] = true, ['What is your favorite music?'] = true, ['What are your hobbies?'] = true, ['LOLWUT?'] = true}, | |
264 | ['When?'] = {'/sc 5_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}, | |
265 | ['Where?'] = {'/sc 5_3', true, ['Where do you want to go?'] = true, ['Where are you going?'] = true, ['Where am I?!'] = true, ['Where did you go?'] = true}, | |
266 | ['How?'] = {'/sc 5_4', true, ['How are you today?'] = true, ['How did you make this cool place?'] = true, ['LOLHOW?'] = true}, | |
267 | ['Can I...'] = {'/sc 5_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, | |
268 | ['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}, | |
269 | [1] = '/sc 5' | |
270 | }, | |
271 | ['Answers'] = { | |
272 | ['You need help?'] = {'/sc 6_0', true, ['Check out the news section'] = true, ['Check out the help section'] = true, ['Read the wiki!'] = true, | |
273 | ['All the answers are in the wiki!'] = true, ['I will help you with this.'] = true}, | |
274 | ['Some people ...'] = {'/sc 6_1', true, ['Me'] = true, ['Not me'] = true, ['You'] = true, ['All of us'] = true, ['Everyone but you'] = true, ['Builderman!'] = true, | |
275 | ['Telamon!'] = true, ['My team'] = true, ['My group'] = true, ['Mom'] = true, ['Dad'] = true, ['Sister'] = true, ['Brother'] = true, ['Cousin'] = true, | |
276 | ['Grandparent'] = true, ['Friend'] = true}, | |
277 | ['Time ...'] = {'/sc 6_2', true, ['In the morning'] = true, ['In the afternoon'] = true, ['At night'] = true, ['Tomorrow'] = true, ['This week'] = true, ['This month'] = true, | |
278 | ['Sometime'] = true, ['Sometimes'] = true, ['Whenever you want'] = true, ['Never'] = true, ['After this'] = true, ['In 10 minutes'] = true, ['In a couple hours'] = true, | |
279 | ['In a couple days'] = true}, | |
280 | ['Animals'] = {'/sc 6_3', true, | |
281 | ['Cats'] = {['Lion'] = true, ['Tiger'] = true, ['Leopard'] = true, ['Cheetah'] = true}, | |
282 | ['Dogs'] = {['Wolves'] = true, ['Beagle'] = true, ['Collie'] = true, ['Dalmatian'] = true, ['Poodle'] = true, ['Spaniel'] = true, | |
283 | ['Shepherd'] = true, ['Terrier'] = true, ['Retriever'] = true}, | |
284 | ['Horses'] = {['Ponies'] = true, ['Stallions'] = true, ['Pwnyz'] = true}, | |
285 | ['Reptiles'] = {['Dinosaurs'] = true, ['Lizards'] = true, ['Snakes'] = true, ['Turtles!'] = true}, | |
286 | ['Hamster'] = true, | |
287 | ['Monkey'] = true, | |
288 | ['Bears'] = true, | |
289 | ['Fish'] = {['Goldfish'] = true, ['Sharks'] = true, ['Sea Bass'] = true, ['Halibut'] = true, ['Tropical Fish'] = true}, | |
290 | ['Birds'] = {['Eagles'] = true, ['Penguins'] = true, ['Parakeets'] = true, ['Owls'] = true, ['Hawks'] = true, ['Pidgeons'] = true}, | |
291 | ['Elephants'] = true, | |
292 | ['Mythical Beasts'] = {['Dragons'] = true, ['Unicorns'] = true, ['Sea Serpents'] = true, ['Sphinx'] = true, ['Cyclops'] = true, | |
293 | ['Minotaurs'] = true, ['Goblins'] = true, ['Honest Politicians'] = true, ['Ghosts'] = true, ['Scylla and Charybdis'] = true} | |
294 | }, | |
295 | ['Games'] = {'/sc 6_4', true, | |
296 | ['Action'] = true, ['Puzzle'] = true, ['Strategy'] = true, ['Racing'] = true, ['RPG'] = true, ['Obstacle Course'] = true, ['Tycoon'] = true, | |
297 | ['Roblox'] = { ['BrickBattle'] = true, ['Community Building'] = true, ['Roblox Minigames'] = true, ['Contest Place'] = true}, | |
298 | ['Board games'] = { ['Chess'] = true, ['Checkers'] = true, ['Settlers of Catan'] = true, ['Tigris and Euphrates'] = true, ['El Grande'] = true, | |
299 | ['Stratego'] = true, ['Carcassonne'] = true} | |
300 | }, | |
301 | ['Sports'] = {'/sc 6_5', true, ['Hockey'] = true, ['Soccer'] = true, ['Football'] = true, ['Baseball'] = true, ['Basketball'] = true, | |
302 | ['Volleyball'] = true, ['Tennis'] = true, ['Sports team practice'] = true, | |
303 | ['Watersports'] = { ['Surfing'] = true,['Swimming'] = true, ['Water Polo'] = true}, | |
304 | ['Winter sports'] = { ['Skiing'] = true, ['Snowboarding'] = true, ['Sledding'] = true, ['Skating'] = true}, | |
305 | ['Adventure'] = {['Rock climbing'] = true, ['Hiking'] = true, ['Fishing'] = true, ['Horseback riding'] = true}, | |
306 | ['Wacky'] = {['Foosball'] = true, ['Calvinball'] = true, ['Croquet'] = true, ['Cricket'] = true, ['Dodgeball'] = true, | |
307 | ['Squash'] = true, ['Trampoline'] = true} | |
308 | }, | |
309 | ['Movies/TV'] = {'/sc 6_6', true, ['Science Fiction'] = true, ['Animated'] = {['Anime'] = true}, ['Comedy'] = true, ['Romantic'] = true, | |
310 | ['Action'] = true, ['Fantasy'] = true}, | |
311 | ['Music'] = {'/sc 6_7', true, ['Country'] = true, ['Jazz'] = true, ['Rap'] = true, ['Hip-hop'] = true, ['Techno'] = true, ['Classical'] = true, | |
312 | ['Pop'] = true, ['Rock'] = true}, | |
313 | ['Hobbies'] = {'/sc 6_8', true, | |
314 | ['Computers'] = { ['Building computers'] = true, ['Videogames'] = true, ['Coding'] = true, ['Hacking'] = true}, | |
315 | ['The Internet'] = { ['lol. teh internets!'] = true, ['Watching vids'] = true}, | |
316 | ['Dance'] = true, ['Gymnastics'] = true, ['Listening to music'] = true, ['Arts and crafts'] = true, | |
317 | ['Martial Arts'] = {['Karate'] = true, ['Judo'] = true, ['Taikwon Do'] = true, ['Wushu'] = true, ['Street fighting'] = true}, | |
318 | ['Music lessons'] = {['Playing in my band'] = true, ['Playing piano'] = true, ['Playing guitar'] = true, | |
319 | ['Playing violin'] = true, ['Playing drums'] = true, ['Playing a weird instrument'] = true} | |
320 | }, | |
321 | ['Location'] = {'/sc 6_9', true, | |
322 | ['USA'] = { | |
323 | ['West'] = { ['Alaska'] = true, ['Arizona'] = true, ['California'] = true, ['Colorado'] = true, ['Hawaii'] = true, | |
324 | ['Idaho'] = true, ['Montana'] = true, ['Nevada'] = true, ['New Mexico'] = true, ['Oregon'] = true, | |
325 | ['Utah'] = true, ['Washington'] = true, ['Wyoming'] = true | |
326 | }, | |
327 | ['South'] = { ['Alabama'] = true, ['Arkansas'] = true, ['Florida'] = true, ['Georgia'] = true, ['Kentucky'] = true, | |
328 | ['Louisiana'] = true, ['Mississippi'] = true, ['North Carolina'] = true, ['Oklahoma'] = true, | |
329 | ['South Carolina'] = true, ['Tennessee'] = true, ['Texas'] = true, ['Virginia'] = true, ['West Virginia'] = true | |
330 | }, | |
331 | ['Northeast'] = {['Connecticut'] = true, ['Delaware'] = true, ['Maine'] = true, ['Maryland'] = true, ['Massachusetts'] = true, | |
332 | ['New Hampshire'] = true, ['New Jersey'] = true, ['New York'] = true, ['Pennsylvania'] = true, ['Rhode Island'] = true, | |
333 | ['Vermont'] = true | |
334 | }, | |
335 | ['Midwest'] = {['Illinois'] = true, ['Indiana'] = true, ['Iowa'] = true, ['Kansas'] = true, ['Michigan'] = true, ['Minnesota'] = true, | |
336 | ['Missouri'] = true, ['Nebraska'] = true, ['North Dakota'] = true, ['Ohio'] = true, ['South Dakota'] = true, ['Wisconsin'] = true} | |
337 | }, | |
338 | ['Canada'] = {['Alberta'] = true, ['British Columbia'] = true, ['Manitoba'] = true, ['New Brunswick'] = true, ['Newfoundland'] = true, | |
339 | ['Northwest Territories'] = true, ['Nova Scotia'] = true, ['Nunavut'] = true, ['Ontario'] = true, ['Prince Edward Island'] = true, | |
340 | ['Quebec'] = true, ['Saskatchewan'] = true, ['Yukon'] = true}, | |
341 | ['Mexico'] = true, | |
342 | ['Central America'] = true, | |
343 | ['Europe'] = {['France'] = true, ['Germany'] = true, ['Spain'] = true, ['Italy'] = true, ['Poland'] = true, ['Switzerland'] = true, | |
344 | ['Greece'] = true, ['Romania'] = true, ['Netherlands'] = true, | |
345 | ['Great Britain'] = {['England'] = true, ['Scotland'] = true, ['Wales'] = true, ['Northern Ireland'] = true} | |
346 | }, | |
347 | ['Asia'] = { ['China'] = true, ['India'] = true, ['Japan'] = true, ['Korea'] = true, ['Russia'] = true, ['Vietnam'] = true}, | |
348 | ['South America'] = { ['Argentina'] = true, ['Brazil'] = true}, | |
349 | ['Africa'] = { ['Eygpt'] = true, ['Swaziland'] = true}, | |
350 | ['Australia'] = true, ['Middle East'] = true, ['Antarctica'] = true, ['New Zealand'] = true | |
351 | }, | |
352 | ['Age'] = {'/sc 6_10', true, ['Rugrat'] = true, ['Kid'] = true, ['Tween'] = true, ['Teen'] = true, ['Twenties'] = true, | |
353 | ['Old'] = true, ['Ancient'] = true, ['Mesozoic'] = true, ['I don\'t want to say my age. Don\'t ask.'] = true}, | |
354 | ['Mood'] = {'/sc 6_11', true, ['Good'] = true, ['Great!'] = true, ['Not bad'] = true, ['Sad'] = true, ['Hyper'] = true, | |
355 | ['Chill'] = true, ['Happy'] = true, ['Kind of mad'] = true}, | |
356 | ['Boy'] = {'/sc 6_12', true}, | |
357 | ['Girl'] = {'/sc 6_13', true}, | |
358 | ['I don\'t want to say boy or girl. Don\'t ask.'] = {'/sc 6_14', true}, | |
359 | [1] = '/sc 6' | |
360 | }, | |
361 | ['Game'] = { | |
362 | ['Let\'s build'] = {'/sc 7_0', true}, | |
363 | ['Let\'s battle'] = {'/sc 7_1', true}, | |
364 | ['Nice one!'] = {'/sc 7_2', true}, | |
365 | ['So far so good'] = {'/sc 7_3', true}, | |
366 | ['Lucky shot!'] = {'/sc 7_4', true}, | |
367 | ['Oh man!'] = {'/sc 7_5', true}, | |
368 | ['I challenge you to a fight!'] = {'/sc 7_6', true}, | |
369 | ['Help me with this'] = {'/sc 7_7', true}, | |
370 | ['Let\'s go to your game'] = {'/sc 7_8', true}, | |
371 | ['Can you show me how do to that?'] = {'/sc 7_9', true}, | |
372 | ['Backflip!'] = {'/sc 7_10', true}, | |
373 | ['Frontflip!'] = {'/sc 7_11', true}, | |
374 | ['Dance!'] = {'/sc 7_12', true}, | |
375 | ['I\'m on your side!'] = {'/sc 7_13', true}, | |
376 | ['Game Commands'] = {'/sc 7_14', true, ['regen'] = true, ['reset'] = true, ['go'] = true, ['fix'] = true, ['respawn'] = true}, | |
377 | [1] = '/sc 7' | |
378 | }; | |
379 | ['Silly'] = { | |
380 | ['Muahahahaha!'] = true, | |
381 | ['all your base are belong to me!'] = true, | |
382 | ['GET OFF MAH LAWN'] = true, | |
383 | ['TEH EPIK DUCK IS COMING!!!'] = true, | |
384 | ['ROFL'] = true, | |
385 | ['1337'] = {true, ['i r teh pwnz0r!'] = true, ['w00t!'] = true, ['z0mg h4x!'] = true, ['ub3rR0xXorzage!'] = true} | |
386 | }, | |
387 | ['Yes'] = { | |
388 | ['Absolutely!'] = true, | |
389 | ['Rock on!'] = true, | |
390 | ['Totally!'] = true, | |
391 | ['Juice!'] = true, | |
392 | ['Yay!'] = true, | |
393 | ['Yesh'] = true | |
394 | }, | |
395 | ['No'] = { | |
396 | ['Ummm. No.'] = true, | |
397 | ['...'] = true, | |
398 | ['Stop!'] = true, | |
399 | ['Go away!'] = true, | |
400 | ['Don\'t do that'] = true, | |
401 | ['Stop breaking the rules'] = true, | |
402 | ['I don\'t want to'] = true | |
403 | }, | |
404 | ['Ok'] = { | |
405 | ['Well... ok'] = true, | |
406 | ['Sure'] = true | |
407 | }, | |
408 | ['Uncertain'] = { | |
409 | ['Maybe'] = true, | |
410 | ['I don\'t know'] = true, | |
411 | ['idk'] = true, | |
412 | ['I can\'t decide'] = true, | |
413 | ['Hmm...'] = true | |
414 | }, | |
415 | [':-)'] = { | |
416 | [':-('] = true, | |
417 | [':D'] = true, | |
418 | [':-O'] = true, | |
419 | ['lol'] = true, | |
420 | ['=D'] = true, | |
421 | ['D='] = true, | |
422 | ['XD'] = true, | |
423 | [';D'] = true, | |
424 | [';)'] = true, | |
425 | ['O_O'] = true, | |
426 | ['=)'] = true, | |
427 | ['@_@'] = true, | |
428 | ['>_<'] = true, | |
429 | ['T_T'] = true, | |
430 | ['^_^'] = true, | |
431 | ['<(0_0<) <(0_0)> (>0_0)> KIRBY DANCE'] = true, | |
432 | [')\';'] = true, | |
433 | [':3'] = true | |
434 | }, | |
435 | ['Ratings'] = { | |
436 | ['Rate it!'] = true, | |
437 | ['I give it a 1 out of 10'] = true, | |
438 | ['I give it a 2 out of 10'] = true, | |
439 | ['I give it a 3 out of 10'] = true, | |
440 | ['I give it a 4 out of 10'] = true, | |
441 | ['I give it a 5 out of 10'] = true, | |
442 | ['I give it a 6 out of 10'] = true, | |
443 | ['I give it a 7 out of 10'] = true, | |
444 | ['I give it a 8 out of 10'] = true, | |
445 | ['I give it a 9 out of 10'] = true, | |
446 | ['I give it a 10 out of 10!'] = true, | |
447 | } | |
448 | }, | |
449 | CreateEnum('SafeChat'){'Level1', 'Level2', 'Level3'}, | |
450 | SafeChatTree = {}, | |
451 | TempSpaceLabel = nil | |
452 | } | |
453 | --------------------------------------------------- | |
454 | ||
455 | local function GetNameValue(pName) | |
456 | local value = 0 | |
457 | for index = 1, #pName do | |
458 | local cValue = string.byte(string.sub(pName, index, index)) | |
459 | local reverseIndex = #pName - index + 1 | |
460 | if #pName%2 == 1 then | |
461 | reverseIndex = reverseIndex - 1 | |
462 | end | |
463 | if reverseIndex%4 >= 2 then | |
464 | cValue = -cValue | |
465 | end | |
466 | value = value + cValue | |
467 | end | |
468 | return value%8 | |
469 | end | |
470 | ||
471 | function Chat:ComputeChatColor(pName) | |
472 | if export.NameColorDependsOnChatColor then | |
473 | return PLAYERLIST_CHATCOLORS[pName:lower()] or self.ChatColors[GetNameValue(pName) + 1].Color | |
474 | else | |
475 | return self.ChatColors[GetNameValue(pName) + 1].Color | |
476 | end | |
477 | end | |
478 | ||
479 | -- This is context based scrolling | |
480 | function Chat:EnableScrolling(toggle) | |
481 | -- Genius idea gone to fail, if we switch the camera type we can effectively lock the | |
482 | -- camera and do no click scrolling | |
483 | self.MouseOnFrame = false | |
484 | if self.RenderFrame then | |
485 | self.RenderFrame.MouseEnter:connect(function() | |
486 | local character = Player.Character | |
487 | local torso = WaitForChild(character, 'Torso') | |
488 | local humanoid = WaitForChild(character, 'Humanoid') | |
489 | local head = WaitForChild(character, 'Head') | |
490 | if toggle then | |
491 | self.MouseOnFrame = true | |
492 | Camera.CameraType = 'Scriptable' | |
493 | -- Get relative position of camera and keep to it | |
494 | Spawn(function() | |
495 | local currentRelativePos = Camera.CoordinateFrame.p - torso.Position | |
496 | while Chat.MouseOnFrame do | |
497 | Camera.CoordinateFrame = CFrame.new(torso.Position + currentRelativePos, head.Position) | |
498 | wait(0.015) | |
499 | end | |
500 | end) | |
501 | end | |
502 | end) | |
503 | ||
504 | self.RenderFrame.MouseLeave:connect(function() | |
505 | Camera.CameraType = 'Custom' | |
506 | self.MouseOnFrame = false | |
507 | end) | |
508 | end | |
509 | end | |
510 | ||
511 | -- TODO: Scrolling using Mouse wheel | |
512 | function Chat:OnScroll(speed) | |
513 | if self.MouseOnFrame then | |
514 | -- | |
515 | end | |
516 | end | |
517 | ||
518 | -- Check if we are running on a touch device | |
519 | function Chat:IsTouchDevice() | |
520 | local touchEnabled = false | |
521 | pcall(function() touchEnabled = Game:GetService('UserInputService').TouchEnabled end) | |
522 | return touchEnabled | |
523 | end | |
524 | ||
525 | -- Scrolling | |
526 | function Chat:ScrollQueue(value) | |
527 | --[[for i = 1, #self.MessageQueue do | |
528 | if self.MessageQueue[i] then | |
529 | for _, label in pairs(self.MessageQueue[i]) do | |
530 | local next = self.MessageQueue[i].Next | |
531 | local previous = self.MessageQueue[i].Previous | |
532 | if label and label:IsA('TextLabel') or label:IsA('TextButton') then | |
533 | if value > 0 and previous and previous['Message'] then | |
534 | label.Position = previous['Message'].Position | |
535 | elseif value < 1 and next['Message'] then | |
536 | label.Position = previous['Message'].Position | |
537 | end | |
538 | end | |
539 | end | |
540 | end | |
541 | end ]] | |
542 | end | |
543 | ||
544 | -- Handles the rendering of the text objects in their appropriate places | |
545 | function Chat:UpdateQueue(field, diff) | |
546 | -- Have to do some sort of correction here | |
547 | for i = #self.MessageQueue, 1, -1 do | |
548 | if self.MessageQueue[i] then | |
549 | for _, label in pairs(self.MessageQueue[i]) do | |
550 | if label and type(label) ~= 'table' and type(label) ~= 'number' then | |
551 | if label:IsA('TextLabel') or label:IsA('TextButton') then | |
552 | if diff then | |
553 | label.Position = label.Position - UDim2.new(0, 0, diff, 0) | |
554 | else | |
555 | if field == self.MessageQueue[i] then | |
556 | label.Position = UDim2.new(self.Configuration.XScale, 0, label.Position.Y.Scale - field['Message'].Size.Y.Scale , 0) | |
557 | -- Just to show up popping effect for the latest message in chat | |
558 | coroutine.resume(coroutine.create(function() | |
559 | wait(0.05) | |
560 | ||
561 | local t = 0.1 | |
562 | local start = tick() | |
563 | ||
564 | while tick() - start < t do | |
565 | local alpha = (tick() - start) / t | |
566 | ||
567 | label.TextTransparency = 1-alpha | |
568 | wait() | |
569 | end | |
570 | label.TextTransparency = 0 | |
571 | ||
572 | if label == field['Message'] then | |
573 | label.TextStrokeTransparency = StrokeTransparency | |
574 | else | |
575 | label.TextStrokeTransparency = StrokeTransparency | |
576 | end | |
577 | end)) | |
578 | else | |
579 | label.Position = UDim2.new(self.Configuration.XScale, 0, label.Position.Y.Scale - field['Message'].Size.Y.Scale, 0) | |
580 | end | |
581 | if label.Position.Y.Scale < -0.01 then | |
582 | -- NOTE: Remove this fix when Textbounds is fixed | |
583 | label.Visible = false | |
584 | label:Destroy() | |
585 | end | |
586 | end | |
587 | end | |
588 | end | |
589 | end | |
590 | end | |
591 | end | |
592 | end | |
593 | ||
594 | function Chat:CreateScrollBar() | |
595 | -- Code for scrolling is in here, partially, but scroll bar drawing isn't drawn | |
596 | -- TODO: Implement | |
597 | end | |
598 | ||
599 | -- For scrolling, to see if we hit the bounds so that we can stop it from scrolling anymore | |
600 | function Chat:CheckIfInBounds(value) | |
601 | if #Chat.MessageQueue < 3 then | |
602 | return true | |
603 | end | |
604 | ||
605 | if value > 0 and Chat.MessageQueue[1] and Chat.MessageQueue[1]['Player'] and Chat.MessageQueue[1]['Player'].Position.Y.Scale == 0 then | |
606 | return true | |
607 | elseif value < 0 and Chat.MessageQueue[1] and Chat.MessageQueue[1]['Player'] and Chat.MessageQueue[1]['Player'].Position.Y.Scale < 0 then | |
608 | return true | |
609 | else | |
610 | return false | |
611 | end | |
612 | return false | |
613 | end | |
614 | ||
615 | -- This is to precompute all playerName space strings | |
616 | -- This is used to offset the message by exactly this + 2 spacestrings | |
617 | function Chat:ComputeSpaceString(pLabel) | |
618 | local nString = " " | |
619 | if not self.TempSpaceLabel then | |
620 | self.TempSpaceLabel = Gui.Create'TextButton' | |
621 | { | |
622 | Size = UDim2.new(0, pLabel.AbsoluteSize.X, 0, pLabel.AbsoluteSize.Y); | |
623 | FontSize = self.Configuration.FontSize; | |
624 | Parent = self.RenderFrame; | |
625 | BackgroundTransparency = 1.0; | |
626 | Text = nString; | |
627 | Name = 'SpaceButton' | |
628 | }; | |
629 | else | |
630 | self.TempSpaceLabel.Text = nString | |
631 | end | |
632 | ||
633 | while self.TempSpaceLabel.TextBounds.X < pLabel.TextBounds.X do | |
634 | nString = nString .. " " | |
635 | self.TempSpaceLabel.Text = nString | |
636 | end | |
637 | nString = nString .. " " | |
638 | self.CachedSpaceStrings_List[pLabel.Text] = nString | |
639 | self.TempSpaceLabel.Text = "" | |
640 | return nString | |
641 | end | |
642 | ||
643 | -- When the playerChatted event fires | |
644 | -- The message is what the player chatted | |
645 | function Chat:UpdateChat(cPlayer, message) | |
646 | local messageField = { | |
647 | ['Player'] = cPlayer, | |
648 | ['Message'] = message | |
649 | } | |
650 | if Chat.MessageThread == nil or coroutine.status(Chat.MessageThread) == 'dead' then | |
651 | --Chat.Messages_List = {} | |
652 | table.insert(Chat.Messages_List, messageField) | |
653 | Chat.MessageThread = coroutine.create(function() | |
654 | for i = 1, #Chat.Messages_List do | |
655 | local field = Chat.Messages_List[i] | |
656 | Chat:CreateMessage(field['Player'], field['Message']) | |
657 | end | |
658 | Chat.Messages_List = {} | |
659 | end) | |
660 | coroutine.resume(Chat.MessageThread) | |
661 | else | |
662 | table.insert(Chat.Messages_List, messageField) | |
663 | end | |
664 | end | |
665 | ||
666 | function Chat:RecalculateSpacing() | |
667 | --[[for i = 1, #self.MessageQueue do | |
668 | local pLabel = self.MessageQueue[i]['Player'] | |
669 | local mLabel = self.MessageQueue[i]['Message'] | |
670 | ||
671 | local prevYScale = mLabel.Size.Y.Scale | |
672 | local prevText = mLabel.Text | |
673 | mLabel.Text = prevText | |
674 | ||
675 | local heightField = mLabel.TextBounds.Y | |
676 | ||
677 | mLabel.Size = UDim2.new(1, 0, heightField/self.RenderFrame.AbsoluteSize.Y, 0) | |
678 | pLabel.Size = mLabel.Size | |
679 | ||
680 | local diff = mLabel.Size.Y.Scale - prevYScale | |
681 | ||
682 | Chat:UpdateQueue(self.MessageQueue[i], diff) | |
683 | end ]] | |
684 | end | |
685 | ||
686 | function Chat:ApplyFilter(str) | |
687 | --[[for _, word in pair(self.Filter_List) do | |
688 | if string.find(str, word) then | |
689 | str:gsub(word, '@#$^') | |
690 | end | |
691 | end ]] | |
692 | end | |
693 | ||
694 | export.SendChat = function(self, name, message, custom_color, do_not_add_to_history) | |
695 | local old_color = PLAYERLIST_CHATCOLORS[name:lower()] | |
696 | if custom_color ~= nil then | |
697 | PLAYERLIST_CHATCOLORS[name:lower()] = custom_color | |
698 | end | |
699 | print(old_color, PLAYERLIST_CHATCOLORS[name:lower()]) | |
700 | ||
701 | local player = game.Players:FindFirstChild(name) or { | |
702 | Name = name, | |
703 | Neutral = true, | |
704 | TeamColor = Color3.new(1,1,1) | |
705 | } | |
706 | ||
707 | Chat:CreateMessage(player, message, do_not_add_to_history) | |
708 | -- if do_not_add_to_history ~= true then | |
709 | -- AddToChatHistory(player, message) | |
710 | -- end | |
711 | ||
712 | wait() | |
713 | PLAYERLIST_CHATCOLORS[name:lower()] = old_color or nil | |
714 | end | |
715 | ||
716 | -- NOTE: Temporarily disabled ring buffer to allow for chat to always wrap around | |
717 | function Chat:CreateMessage(cPlayer, message, do_not_add_to_history) | |
718 | if do_not_add_to_history ~= true then | |
719 | AddToChatHistory(cPlayer, message) | |
720 | end | |
721 | ||
722 | local pName | |
723 | if not cPlayer then | |
724 | pName = '' | |
725 | else | |
726 | pName = cPlayer.Name | |
727 | end | |
728 | message = StringTrim(message) | |
729 | local pLabel | |
730 | local mLabel | |
731 | -- Our history stores upto 50 messages that is 100 textlabels | |
732 | -- If we ever hit the mark, which would be in every popular game btw | |
733 | -- we wrap around and reuse the labels | |
734 | if #self.MessageQueue > self.Configuration.HistoryLength then | |
735 | --[[pLabel = self.MessageQueue[#self.MessageQueue]['Player'] | |
736 | mLabel = self.MessageQueue[#self.MessageQueue]['Message'] | |
737 | ||
738 | pLabel.Text = pName .. ':' | |
739 | pLabel.Name = pName | |
740 | ||
741 | local pColor | |
742 | if cPlayer.Neutral then | |
743 | pLabel.TextColor3 = Chat:ComputeChatColor(pName) | |
744 | else | |
745 | pLabel.TextColor3 = cPlayer.TeamColor.Color | |
746 | end | |
747 | ||
748 | local nString | |
749 | ||
750 | if not self.CachedSpaceStrings_List[pName] then | |
751 | nString = Chat:ComputeSpaceString(pLabel) | |
752 | else | |
753 | nString = self.CachedSpaceStrings_List[pName] | |
754 | end | |
755 | ||
756 | mLabel.Text = "" | |
757 | mLabel.Name = pName .. " - message" | |
758 | mLabel.Text = nString .. message; | |
759 | ||
760 | mLabel.Parent = nil | |
761 | mLabel.Parent = self.RenderFrame | |
762 | ||
763 | mLabel.Position = UDim2.new(0, 0, 1, 0); | |
764 | pLabel.Position = UDim2.new(0, 0, 1, 0);]] | |
765 | ||
766 | -- Reinserted at the beginning, ring buffer | |
767 | self.MessageQueue[#self.MessageQueue] = nil | |
768 | end | |
769 | --else | |
770 | -- Haven't hit the mark yet, so keep creating | |
771 | pLabel = Gui.Create'TextLabel' | |
772 | { | |
773 | Name = pName; | |
774 | Text = (PLAYERLIST_ALIASES[pName:lower()] or pName) .. ":"; | |
775 | TextColor3 = PLAYERLIST_CHATCOLORS[pName:lower()] or pColor; | |
776 | FontSize = Chat.Configuration.FontSize; | |
777 | TextXAlignment = Enum.TextXAlignment.Left; | |
778 | TextYAlignment = Enum.TextYAlignment.Top; | |
779 | Parent = self.RenderFrame; | |
780 | TextWrapped = false; | |
781 | Size = UDim2.new(1, 0, 0.1, 0); | |
782 | BackgroundTransparency = 1.0; | |
783 | TextTransparency = 1.0; | |
784 | Position = UDim2.new(0, 0, 1, 0); | |
785 | BorderSizePixel = 0.0; | |
786 | TextStrokeColor3 = Color3.new(0, 0, 0); | |
787 | TextStrokeTransparency = StrokeTransparency; | |
788 | --Active = false; | |
789 | }; | |
790 | local pColor | |
791 | if cPlayer.Neutral then | |
792 | pLabel.TextColor3 = Chat:ComputeChatColor(pName) | |
793 | else | |
794 | pLabel.TextColor3 = cPlayer.TeamColor.Color | |
795 | end | |
796 | ||
797 | local nString | |
798 | ||
799 | if not self.CachedSpaceStrings_List[pName] then | |
800 | nString = Chat:ComputeSpaceString(pLabel) | |
801 | else | |
802 | nString = self.CachedSpaceStrings_List[pName] | |
803 | end | |
804 | ||
805 | mLabel = Gui.Create'TextLabel' | |
806 | { | |
807 | Name = pName .. ' - message'; | |
808 | -- Max is 3 lines | |
809 | Size = UDim2.new(1, 0, 0.5, 0); | |
810 | TextColor3 = Chat.Configuration.MessageColor; | |
811 | FontSize = Chat.Configuration.FontSize; | |
812 | TextXAlignment = Enum.TextXAlignment.Left; | |
813 | TextYAlignment = Enum.TextYAlignment.Top; | |
814 | Text = ""; -- this is to stop when the engine reverts the swear words to default, which is button, ugh | |
815 | Parent = self.RenderFrame; | |
816 | TextWrapped = true; | |
817 | BackgroundTransparency = 1.0; | |
818 | TextTransparency = 1.0; | |
819 | Position = UDim2.new(0, 0, 1, 0); | |
820 | BorderSizePixel = 0.0; | |
821 | TextStrokeColor3 = Color3.new(0, 0, 0); | |
822 | --TextStrokeTransparency = 0.8; | |
823 | --Active = false; | |
824 | }; | |
825 | mLabel.Text = nString .. message; | |
826 | ||
827 | if not pName then | |
828 | pLabel.Text = '' | |
829 | mLabel.TextColor3 = Color3.new(0, 0.4, 1.0) | |
830 | end | |
831 | --end | |
832 | ||
833 | if self.Admins_List[pName:lower()] then | |
834 | mLabel.TextColor3 = PLAYERLIST_CHATCOLORS[pName:lower()] or self.Configuration.AdminMessageColor | |
835 | end | |
836 | ||
837 | pLabel.Visible = true | |
838 | mLabel.Visible = true | |
839 | ||
840 | -- This will give beautiful multilines as well | |
841 | local heightField = mLabel.TextBounds.Y | |
842 | ||
843 | mLabel.Size = UDim2.new(1, 0, heightField/self.RenderFrame.AbsoluteSize.Y, 0) | |
844 | pLabel.Size = mLabel.Size | |
845 | ||
846 | local yPixels = self.RenderFrame.AbsoluteSize.Y | |
847 | local yFieldSize = mLabel.TextBounds.Y | |
848 | ||
849 | local queueField = {} | |
850 | queueField['Player'] = pLabel | |
851 | queueField['Message'] = mLabel | |
852 | queueField['SpawnTime'] = tick() -- Used for identifying when to make the message invisible | |
853 | ||
854 | table.insert(self.MessageQueue, 1, queueField) | |
855 | Chat:UpdateQueue(queueField) | |
856 | end | |
857 | ||
858 | function Chat:ScreenSizeChanged() | |
859 | wait() | |
860 | while self.Frame.AbsoluteSize.Y > 120 do | |
861 | self.Frame.Size = self.Frame.Size - UDim2.new(0, 0, 0.005, 0) | |
862 | end | |
863 | Chat:RecalculateSpacing() | |
864 | end | |
865 | ||
866 | ||
867 | function Chat:FindButtonTree(scButton, rootList) | |
868 | local list = {} | |
869 | rootList = rootList or self.SafeChatTree | |
870 | for button, _ in pairs(rootList) do | |
871 | if button == scButton then | |
872 | list = rootList[button] | |
873 | elseif type(rootList[button]) == 'table' then | |
874 | list = Chat:FindButtonTree(scButton, rootList[button]) | |
875 | end | |
876 | end | |
877 | return list | |
878 | end | |
879 | ||
880 | function Chat:ToggleSafeChatMenu(scButton) | |
881 | local list = Chat:FindButtonTree(scButton, self.SafeChatTree) | |
882 | if list then | |
883 | for button, _ in pairs(list) do | |
884 | if button:IsA('TextButton') or button:IsA('ImageButton') then | |
885 | button.Visible = not button.Visible | |
886 | end | |
887 | end | |
888 | return true | |
889 | end | |
890 | return false | |
891 | end | |
892 | ||
893 | function Chat:CreateSafeChatOptions(list, rootButton) | |
894 | local text_List = {} | |
895 | level = level or 0 | |
896 | local count = 0 | |
897 | text_List[rootButton] = {} | |
898 | text_List[rootButton][1] = list[1] | |
899 | rootButton = rootButton or self.SafeChatButton | |
900 | for msg, _ in pairs(list) do | |
901 | if type(msg) == 'string' then | |
902 | local chatText = Gui.Create'TextButton' | |
903 | { | |
904 | Name = msg; | |
905 | Text = msg; | |
906 | Size = UDim2.new(0, 100, 0, 20); | |
907 | TextXAlignment = Enum.TextXAlignment.Center; | |
908 | TextColor3 = Color3.new(0.2, 0.1, 0.1); | |
909 | BackgroundTransparency = 0.5; | |
910 | BackgroundColor3 = Color3.new(1, 1, 1); | |
911 | Parent = self.SafeChatFrame; | |
912 | Visible = false; | |
913 | Position = UDim2.new(0, rootButton.Position.X.Scale + 105, 0, rootButton.Position.Y.Scale - ((count - 3) * 100)); | |
914 | }; | |
915 | ||
916 | count = count + 1 | |
917 | ||
918 | if type(list[msg]) == 'table' then | |
919 | text_List[rootButton][chatText] = Chat:CreateSafeChatOptions(list[msg], chatText) | |
920 | else | |
921 | --table.insert(text_List[chatText], true) | |
922 | end | |
923 | chatText.MouseEnter:connect(function() | |
924 | Chat:ToggleSafeChatMenu(chatText) | |
925 | end) | |
926 | ||
927 | chatText.MouseLeave:connect(function() | |
928 | Chat:ToggleSafeChatMenu(chatText) | |
929 | end) | |
930 | ||
931 | chatText.MouseButton1Click:connect(function() | |
932 | local lList = Chat:FindButtonTree(chatText) | |
933 | if lList then | |
934 | for i, v in pairs(lList) do | |
935 | end | |
936 | else | |
937 | end | |
938 | pcall(function() PlayersService:Chat(lList[1]) end) | |
939 | end) | |
940 | end | |
941 | end | |
942 | return text_List | |
943 | end | |
944 | ||
945 | function Chat:CreateSafeChatGui() | |
946 | self.SafeChatFrame = Gui.Create'Frame' | |
947 | { | |
948 | Name = 'SafeChatFrame'; | |
949 | Size = UDim2.new(1, 0, 1, 0); | |
950 | Parent = self.Gui; | |
951 | BackgroundTransparency = 1.0; | |
952 | ||
953 | Gui.Create'ImageButton' | |
954 | { | |
955 | Name = 'SafeChatButton'; | |
956 | Size = UDim2.new(0, 44, 0, 31); | |
957 | Position = UDim2.new(0, 1, 0.35, 0); | |
958 | BackgroundTransparency = 1.0; | |
959 | Image = 'http://www.roblox.com/asset/?id=97080365'; | |
960 | }; | |
961 | } | |
962 | ||
963 | self.SafeChatButton = self.SafeChatFrame.SafeChatButton | |
964 | -- safe chat button is the root of this tree | |
965 | self.SafeChatTree[self.SafeChatButton] = Chat:CreateSafeChatOptions(self.SafeChat_List, self.SafeChatButton) | |
966 | ||
967 | self.SafeChatButton.MouseButton1Click:connect(function() | |
968 | Chat:ToggleSafeChatMenu(self.SafeChatButton) | |
969 | end) | |
970 | end | |
971 | ||
972 | ||
973 | function Chat:FocusOnChatBar() | |
974 | if self.ClickToChatButton then | |
975 | self.ClickToChatButton.Visible = false | |
976 | end | |
977 | ||
978 | self.GotFocus = true | |
979 | if self.Frame['Background'] then | |
980 | self.Frame.Background.Visible = false | |
981 | end | |
982 | self.ChatBar:CaptureFocus() | |
983 | end | |
984 | ||
985 | -- For touch devices we create a button instead | |
986 | function Chat:CreateTouchButton() | |
987 | self.ChatTouchFrame = Gui.Create'Frame' | |
988 | { | |
989 | Name = 'ChatTouchFrame'; | |
990 | Size = UDim2.new(0, 128, 0, 32); | |
991 | Position = UDim2.new(0, 88, 0, 0); | |
992 | BackgroundTransparency = 1.0; | |
993 | Parent = self.Gui; | |
994 | ||
995 | Gui.Create'ImageButton' | |
996 | { | |
997 | Name = 'ChatLabel'; | |
998 | Size = UDim2.new(0, 74, 0, 28); | |
999 | Position = UDim2.new(0, 0, 0, 0); | |
1000 | BackgroundTransparency = 1.0; | |
1001 | ZIndex = 2.0; | |
1002 | }; | |
1003 | Gui.Create'ImageLabel' | |
1004 | { | |
1005 | Name = 'Background'; | |
1006 | Size = UDim2.new(1, 0, 1, 0); | |
1007 | Position = UDim2.new(0, 0, 0, 0); | |
1008 | BackgroundTransparency = 1.0; | |
1009 | Image = 'http://www.roblox.com/asset/?id=97078724' | |
1010 | }; | |
1011 | ||
1012 | } | |
1013 | self.TapToChatLabel = self.ChatTouchFrame.ChatLabel | |
1014 | self.TouchLabelBackground = self.ChatTouchFrame.Background | |
1015 | ||
1016 | self.ChatBar = Gui.Create'TextBox' | |
1017 | { | |
1018 | Name = 'ChatBar'; | |
1019 | Size = UDim2.new(1, 0, 0.2, 0); | |
1020 | Position = UDim2.new(0, 0, 0.8, 800); | |
1021 | Text = ""; | |
1022 | ZIndex = 1.0; | |
1023 | BackgroundTransparency = 1.0; | |
1024 | Parent = self.Frame; | |
1025 | TextXAlignment = Enum.TextXAlignment.Left; | |
1026 | TextColor3 = Color3.new(1, 1, 1); | |
1027 | ClearTextOnFocus = false; | |
1028 | }; | |
1029 | ||
1030 | self.TapToChatLabel.MouseButton1Click:connect(function() | |
1031 | self.TapToChatLabel.Visible = false | |
1032 | --self.ChatBar.Visible = true | |
1033 | --self.Frame.Background.Visible = true | |
1034 | self.ChatBar:CaptureFocus() | |
1035 | self.GotFocus = true | |
1036 | if self.TouchLabelBackground then | |
1037 | self.TouchLabelBackground.Visible = false | |
1038 | end | |
1039 | end) | |
1040 | end | |
1041 | ||
1042 | -- Non touch devices, create the bottom chat bar | |
1043 | function Chat:CreateChatBar() | |
1044 | -- okay now we do | |
1045 | -- local status, result = pcall(function() return GuiService.UseLuaChat end) | |
1046 | -- if forceChatGUI or (status and result) then | |
1047 | -- self.ClickToChatButton = Gui.Create'TextButton' | |
1048 | -- { | |
1049 | -- Name = 'ClickToChat'; | |
1050 | -- Size = UDim2.new(1, 0, 0, 20); | |
1051 | -- BackgroundTransparency = 1.0; | |
1052 | -- ZIndex = 2.0; | |
1053 | -- Parent = self.Gui; | |
1054 | -- Text = "To chat click here or press \"/\" key"; | |
1055 | -- TextColor3 = Color3.new(1, 1, 0.9); | |
1056 | -- Position = UDim2.new(0, 0, 1, 0); | |
1057 | -- TextXAlignment = Enum.TextXAlignment.Left; | |
1058 | -- FontSize = Enum.FontSize.Size12; | |
1059 | -- } | |
1060 | -- | |
1061 | -- self.ChatBar = Gui.Create'TextBox' | |
1062 | -- { | |
1063 | -- Name = 'ChatBar'; | |
1064 | -- Size = UDim2.new(1, 0, 0, 20); | |
1065 | -- Position = UDim2.new(0, 0, 1, 0); | |
1066 | -- Text = ""; | |
1067 | -- ZIndex = 1.0; | |
1068 | -- BackgroundColor3 = Color3.new(0, 0, 0); | |
1069 | -- BackgroundTransparency = 0.25; | |
1070 | -- Parent = self.Gui; | |
1071 | -- TextXAlignment = Enum.TextXAlignment.Left; | |
1072 | -- TextColor3 = Color3.new(1, 1, 1); | |
1073 | -- FontSize = Enum.FontSize.Size12; | |
1074 | -- ClearTextOnFocus = false; | |
1075 | -- Text = ''; | |
1076 | -- }; | |
1077 | -- | |
1078 | -- -- Engine has code to offset the entire world, so if we do it by -20 pixels nothing gets in our chat's way | |
1079 | -- --GuiService:SetGlobalSizeOffsetPixel(0, -20) | |
1080 | -- local success, error = pcall(function() GuiService:SetGlobalGuiInset(0, 0, 0, 20) end) | |
1081 | -- if not success then | |
1082 | -- GuiService:SetGlobalSizeOffsetPixel(0, -20) | |
1083 | -- end | |
1084 | -- -- CHatHotKey is '/' | |
1085 | -- GuiService:AddSpecialKey(Enum.SpecialKey.ChatHotkey) | |
1086 | -- GuiService.SpecialKeyPressed:connect(function(key) | |
1087 | -- if key == Enum.SpecialKey.ChatHotkey then | |
1088 | -- Chat:FocusOnChatBar() | |
1089 | -- end | |
1090 | -- end) | |
1091 | -- | |
1092 | -- self.ClickToChatButton.MouseButton1Click:connect(function() | |
1093 | -- Chat:FocusOnChatBar() | |
1094 | -- end) | |
1095 | -- end | |
1096 | end | |
1097 | ||
1098 | -- Create the initial Chat stuff | |
1099 | -- Done only once | |
1100 | function Chat:CreateGui() | |
1101 | self.Gui = script.Parent | |
1102 | self.Frame = Gui.Create'Frame' | |
1103 | { | |
1104 | Name = 'ChatFrame'; | |
1105 | --Size = self.Configuration.Size; | |
1106 | Size = UDim2.new(0, 500, 0, 120); | |
1107 | Position = UDim2.new(0, 0, 0, 5); | |
1108 | BackgroundTransparency = 1.0; | |
1109 | --ClipsDescendants = true; | |
1110 | ZIndex = 0.0; | |
1111 | Parent = self.Gui; | |
1112 | Active = false; | |
1113 | ||
1114 | Gui.Create'ImageLabel' | |
1115 | { | |
1116 | Name = 'Background'; | |
1117 | Image = 'http://www.roblox.com/asset/?id=97120937'; --96551212'; | |
1118 | Size = UDim2.new(1.3, 0, 1.64, 0); | |
1119 | Position = UDim2.new(0, 0, 0, 0); | |
1120 | BackgroundTransparency = 1.0; | |
1121 | ZIndex = 0.0; | |
1122 | Visible = false | |
1123 | }; | |
1124 | ||
1125 | Gui.Create'Frame' | |
1126 | { | |
1127 | Name = 'Border'; | |
1128 | Size = UDim2.new(1, 0, 0, 1); | |
1129 | Position = UDim2.new(0, 0, 0.8, 0); | |
1130 | BackgroundTransparency = 0.0; | |
1131 | BackgroundColor3 = Color3.new(236/255, 236/255, 236/255); | |
1132 | BorderSizePixel = 0.0; | |
1133 | Visible = false; | |
1134 | }; | |
1135 | ||
1136 | Gui.Create'Frame' | |
1137 | { | |
1138 | Name = 'ChatRenderFrame'; | |
1139 | Size = UDim2.new(1.02, 0, 1.01, 0); | |
1140 | Position = UDim2.new(0, 0, 0, 0); | |
1141 | BackgroundTransparency = 1.0; | |
1142 | --ClipsDescendants = true; | |
1143 | ZIndex = 0.0; | |
1144 | Active = false; | |
1145 | ||
1146 | }; | |
1147 | }; | |
1148 | ||
1149 | Spawn(function() | |
1150 | wait(0.5) | |
1151 | if IsPhone() then | |
1152 | self.Frame.Size = UDim2.new(0, 280, 0, 120) | |
1153 | end | |
1154 | end) | |
1155 | ||
1156 | self.RenderFrame = self.Frame.ChatRenderFrame | |
1157 | if Chat:IsTouchDevice() then | |
1158 | self.Frame.Position = self.Configuration.Position; | |
1159 | self.RenderFrame.Size = UDim2.new(1, 0, 1, 0) | |
1160 | elseif self.Frame.AbsoluteSize.Y > 120 then | |
1161 | Chat:ScreenSizeChanged() | |
1162 | self.Gui.Changed:connect(function(property) | |
1163 | if property == 'AbsoluteSize' then | |
1164 | Chat:ScreenSizeChanged() | |
1165 | end | |
1166 | end) | |
1167 | end | |
1168 | ||
1169 | if forceChatGUI or true then | |
1170 | if Chat:IsTouchDevice() then | |
1171 | Chat:CreateTouchButton() | |
1172 | else | |
1173 | Chat:CreateChatBar() | |
1174 | --Chat:CreateSafeChatGui() | |
1175 | end | |
1176 | ||
1177 | if self.ChatBar then | |
1178 | self.ChatBar.FocusLost:connect(function(enterPressed) | |
1179 | Chat.GotFocus = false | |
1180 | if Chat:IsTouchDevice() then | |
1181 | self.ChatBar.Visible = false | |
1182 | self.TapToChatLabel.Visible = true | |
1183 | ||
1184 | if self.TouchLabelBackground then | |
1185 | self.TouchLabelBackground.Visible = true | |
1186 | end | |
1187 | end | |
1188 | if enterPressed and self.ChatBar.Text ~= "" then | |
1189 | ||
1190 | local cText = self.ChatBar.Text | |
1191 | if string.sub(self.ChatBar.Text, 1, 1) == '%' then | |
1192 | cText = '(TEAM) ' .. string.sub(cText, 2, #cText) | |
1193 | pcall(function() PlayersService:TeamChat(cText) end) | |
1194 | else | |
1195 | pcall(function() PlayersService:Chat(cText) end) | |
1196 | end | |
1197 | ||
1198 | if self.ClickToChatButton then | |
1199 | self.ClickToChatButton.Visible = true | |
1200 | end | |
1201 | self.ChatBar.Text = "" | |
1202 | end | |
1203 | Spawn(function() | |
1204 | wait(5.0) | |
1205 | if not Chat.GotFocus then | |
1206 | Chat.Frame.Background.Visible = false | |
1207 | end | |
1208 | end) | |
1209 | end) | |
1210 | end | |
1211 | end | |
1212 | end | |
1213 | ||
1214 | -- Scrolling function | |
1215 | -- Applies a speed(velocity) to have nice scrolling effect | |
1216 | function Input:OnMouseScroll() | |
1217 | Spawn(function() | |
1218 | -- How long should the speed last? | |
1219 | while Input.Speed ~=0 do | |
1220 | if Input.Speed > 1 then | |
1221 | while Input.Speed > 0 do | |
1222 | Input.Speed = Input.Speed - 1 | |
1223 | wait(0.25) | |
1224 | end | |
1225 | elseif Input.Speed < 0 then | |
1226 | while Input.Speed < 0 do | |
1227 | Input.Speed = Input.Speed + 1 | |
1228 | wait(0.25) | |
1229 | end | |
1230 | end | |
1231 | wait(0.03) | |
1232 | end | |
1233 | end) | |
1234 | if Chat:CheckIfInBounds(Input.Speed) then | |
1235 | return | |
1236 | end | |
1237 | Chat:ScrollQueue() | |
1238 | end | |
1239 | ||
1240 | function Input:ApplySpeed(value) | |
1241 | Input.Speed = Input.Speed + value | |
1242 | if not self.Simulating then | |
1243 | Input:OnMouseScroll() | |
1244 | end | |
1245 | end | |
1246 | ||
1247 | function Input:Initialize() | |
1248 | self.Mouse.WheelBackward:connect(function() | |
1249 | Input:ApplySpeed(self.Configuration.DefaultSpeed) | |
1250 | end) | |
1251 | ||
1252 | self.Mouse.WheelForward:connect(function() | |
1253 | Input:ApplySpeed(self.Configuration.DefaultSpeed) | |
1254 | end) | |
1255 | end | |
1256 | ||
1257 | function Chat:FindMessageInSafeChat(message, list) | |
1258 | local foundMessage = false | |
1259 | for msg, _ in pairs(list) do | |
1260 | if msg == message then | |
1261 | return true | |
1262 | end | |
1263 | if type(list[msg]) == 'table' then | |
1264 | foundMessage = Chat:FindMessageInSafeChat(message, list[msg]) | |
1265 | if foundMessage then | |
1266 | return true | |
1267 | end | |
1268 | end | |
1269 | end | |
1270 | return foundMessage | |
1271 | end | |
1272 | ||
1273 | -- Just a wrapper around our PlayerChatted event | |
1274 | function Chat:PlayerChatted(...) | |
1275 | local args = {...} | |
1276 | local argCount = select('#', ...) | |
1277 | local player | |
1278 | local message | |
1279 | -- This doesn't look very good, but what else to do? | |
1280 | if args[2] then | |
1281 | player = args[2] | |
1282 | end | |
1283 | if args[3] then | |
1284 | message = args[3] | |
1285 | if string.sub(message, 1, 1) == '%' then | |
1286 | message = '(TEAM) ' .. string.sub(message, 2, #message) | |
1287 | end | |
1288 | end | |
1289 | ||
1290 | -- if PlayersService.ClassicChat then | |
1291 | if string.sub(message, 1, 3) == '/e ' or string.sub(message, 1, 7) == '/emote ' then | |
1292 | -- don't do anything right now | |
1293 | elseif forceChatGUI or true then | |
1294 | Chat:UpdateChat(player, message) | |
1295 | elseif true and string.sub(message, 1, 3) == '/sc' then | |
1296 | Chat:UpdateChat(player, message) | |
1297 | else | |
1298 | if Chat:FindMessageInSafeChat(message, self.SafeChat_List) then | |
1299 | Chat:UpdateChat(player, message) | |
1300 | end | |
1301 | end | |
1302 | -- end | |
1303 | end | |
1304 | ||
1305 | -- After Chat.Configuration.Lifetime seconds of existence, the labels become invisible | |
1306 | -- Runs only every 5 seconds and has to loop through 50 values | |
1307 | -- Shouldn't be too expensive | |
1308 | function Chat:CullThread() | |
1309 | while true do | |
1310 | if #self.MessageQueue > 0 then | |
1311 | for _, field in pairs(self.MessageQueue) do | |
1312 | if field['SpawnTime'] and field['Player'] and field['Message'] and tick() - field['SpawnTime'] > self.Configuration.LifeTime then | |
1313 | field['Player'].Visible = false | |
1314 | field['Message'].Visible = false | |
1315 | end | |
1316 | end | |
1317 | end | |
1318 | wait(5.0) | |
1319 | end | |
1320 | end | |
1321 | ||
1322 | -- RobloxLock everything so users can't delete them(?) | |
1323 | function Chat:LockAllFields(gui) | |
1324 | local children = gui:GetChildren() | |
1325 | for i = 1, #children do | |
1326 | children[i].RobloxLocked = true | |
1327 | if #children[i]:GetChildren() > 0 then | |
1328 | Chat:LockAllFields(children[i]) | |
1329 | end | |
1330 | end | |
1331 | end | |
1332 | ||
1333 | function Chat:CoreGuiChanged(coreGuiType,enabled) | |
1334 | if coreGuiType == Enum.CoreGuiType.Chat or coreGuiType == Enum.CoreGuiType.All then | |
1335 | if self.Frame then self.Frame.Visible = enabled end | |
1336 | ||
1337 | if not Chat:IsTouchDevice() and self.ChatBar then | |
1338 | self.ChatBar.Visible = enabled | |
1339 | if enabled then | |
1340 | GuiService:SetGlobalGuiInset(0, 0, 0, 20) | |
1341 | else | |
1342 | GuiService:SetGlobalGuiInset(0, 0, 0, 0) | |
1343 | end | |
1344 | end | |
1345 | end | |
1346 | end | |
1347 | ||
1348 | _G.ChatHistory = _G.ChatHistory or {} | |
1349 | function RunChatHistory(max) | |
1350 | for i=#_G.ChatHistory-max, #_G.ChatHistory do | |
1351 | local player, message = unpack(_G.ChatHistory[i] or {}) | |
1352 | ||
1353 | if player ~= nil and message ~= nil then | |
1354 | export:SendChat(player.Name, message, nil, true) | |
1355 | end | |
1356 | ||
1357 | end | |
1358 | end | |
1359 | ||
1360 | function AddToChatHistory(player, message) | |
1361 | table.insert(_G.ChatHistory, {player, message}) | |
1362 | end | |
1363 | ||
1364 | -- Constructor | |
1365 | -- This function initializes everything | |
1366 | function Chat:Initialize() | |
1367 | ||
1368 | Chat:CreateGui() | |
1369 | ||
1370 | function PlayerChatted(...) | |
1371 | Chat:PlayerChatted(...) | |
1372 | end | |
1373 | ||
1374 | function OnPlayer(p) | |
1375 | if p:IsA 'Player' then | |
1376 | p.Chatted:connect(function(chat) | |
1377 | PlayerChatted( | |
1378 | Enum.PlayerChatType.All, | |
1379 | p, | |
1380 | chat, | |
1381 | nil | |
1382 | ) | |
1383 | end) | |
1384 | end | |
1385 | end | |
1386 | ||
1387 | for _,p in pairs(game.Players:GetPlayers()) do | |
1388 | OnPlayer(p) | |
1389 | end | |
1390 | ||
1391 | game.Players.ChildAdded:connect(OnPlayer) | |
1392 | ||
1393 | self.EventListener = script.ChildAdded:connect(function(...) | |
1394 | -- This event has 4 callback arguments | |
1395 | -- Enum.PlayerChatType.All, chatPlayer, message, targetPlayer | |
1396 | -- Chat:PlayerChatted(...) | |
1397 | ||
1398 | end) | |
1399 | ||
1400 | -- Initialize input for us | |
1401 | -- Input:Initialize() | |
1402 | -- Eww, everytime a player is added, you have to redo the connection | |
1403 | -- Seems this is not automatic | |
1404 | -- -- NOTE: PlayerAdded only fires on the server, hence ChildAdded is used here | |
1405 | -- PlayersService.ChildAdded:connect(function() | |
1406 | -- Chat.EventListener:disconnect() | |
1407 | -- self.EventListener = PlayersService.PlayerChatted:connect(function(...) | |
1408 | -- -- This event has 4 callback arguments | |
1409 | -- -- Enum.PlayerChatType.All, chatPlayer, message, targetPlayer | |
1410 | -- Chat:PlayerChatted(...) | |
1411 | -- end) | |
1412 | -- end) | |
1413 | ||
1414 | Spawn(function() | |
1415 | Chat:CullThread() | |
1416 | end) | |
1417 | ||
1418 | -- Chat:LockAllFields(self.Frame) | |
1419 | -- self.Frame.DescendantAdded:connect(function(descendant) | |
1420 | ---- Chat:LockAllFields(descendant) | |
1421 | -- end) | |
1422 | end | |
1423 | ||
1424 | Chat:Initialize() | |
1425 | ||
1426 | RunChatHistory(10) | |
1427 | ||
1428 | _G.ChatAPI = export |