· 7 years ago · Jan 23, 2019, 06:28 PM
1
2--Converted with ttyyuu12345's model to script plugin v4
3function sandbox(var,func)
4 local env = getfenv(func)
5 local newenv = setmetatable({},{
6 __index = function(self,k)
7 if k=="script" then
8 return var
9 else
10 return env[k]
11 end
12 end,
13 })
14 setfenv(func,newenv)
15 return func
16end
17cors = {}
18mas = Instance.new("Model",game:GetService("Lighting"))
19Tool0 = Instance.new("Tool")
20LocalScript1 = Instance.new("LocalScript")
21LocalScript2 = Instance.new("LocalScript")
22LocalScript3 = Instance.new("LocalScript")
23LocalScript4 = Instance.new("LocalScript")
24LocalScript5 = Instance.new("LocalScript")
25LocalScript6 = Instance.new("LocalScript")
26LocalScript7 = Instance.new("LocalScript")
27LocalScript8 = Instance.new("LocalScript")
28LocalScript9 = Instance.new("LocalScript")
29LocalScript10 = Instance.new("LocalScript")
30LocalScript11 = Instance.new("LocalScript")
31LocalScript12 = Instance.new("LocalScript")
32LocalScript13 = Instance.new("LocalScript")
33LocalScript14 = Instance.new("LocalScript")
34LocalScript15 = Instance.new("LocalScript")
35RemoteFunction16 = Instance.new("RemoteFunction")
36Script17 = Instance.new("Script")
37RemoteFunction18 = Instance.new("RemoteFunction")
38Script19 = Instance.new("Script")
39LocalScript20 = Instance.new("LocalScript")
40Part21 = Instance.new("Part")
41Decal22 = Instance.new("Decal")
42Decal23 = Instance.new("Decal")
43Decal24 = Instance.new("Decal")
44Decal25 = Instance.new("Decal")
45Decal26 = Instance.new("Decal")
46Decal27 = Instance.new("Decal")
47Camera28 = Instance.new("Camera")
48Frame29 = Instance.new("Frame")
49Frame30 = Instance.new("Frame")
50Frame31 = Instance.new("Frame")
51Frame32 = Instance.new("Frame")
52TextLabel33 = Instance.new("TextLabel")
53TextLabel34 = Instance.new("TextLabel")
54Frame35 = Instance.new("Frame")
55TextLabel36 = Instance.new("TextLabel")
56Frame37 = Instance.new("Frame")
57Frame38 = Instance.new("Frame")
58TextButton39 = Instance.new("TextButton")
59ImageLabel40 = Instance.new("ImageLabel")
60TextLabel41 = Instance.new("TextLabel")
61Frame42 = Instance.new("Frame")
62Frame43 = Instance.new("Frame")
63TextButton44 = Instance.new("TextButton")
64ImageLabel45 = Instance.new("ImageLabel")
65TextLabel46 = Instance.new("TextLabel")
66Frame47 = Instance.new("Frame")
67Frame48 = Instance.new("Frame")
68TextLabel49 = Instance.new("TextLabel")
69Frame50 = Instance.new("Frame")
70Frame51 = Instance.new("Frame")
71Frame52 = Instance.new("Frame")
72TextLabel53 = Instance.new("TextLabel")
73TextLabel54 = Instance.new("TextLabel")
74Frame55 = Instance.new("Frame")
75TextLabel56 = Instance.new("TextLabel")
76Frame57 = Instance.new("Frame")
77Frame58 = Instance.new("Frame")
78TextButton59 = Instance.new("TextButton")
79ImageLabel60 = Instance.new("ImageLabel")
80TextLabel61 = Instance.new("TextLabel")
81Frame62 = Instance.new("Frame")
82Frame63 = Instance.new("Frame")
83TextButton64 = Instance.new("TextButton")
84ImageLabel65 = Instance.new("ImageLabel")
85TextLabel66 = Instance.new("TextLabel")
86Frame67 = Instance.new("Frame")
87Frame68 = Instance.new("Frame")
88TextLabel69 = Instance.new("TextLabel")
89Frame70 = Instance.new("Frame")
90Frame71 = Instance.new("Frame")
91TextLabel72 = Instance.new("TextLabel")
92TextLabel73 = Instance.new("TextLabel")
93Frame74 = Instance.new("Frame")
94Frame75 = Instance.new("Frame")
95Frame76 = Instance.new("Frame")
96Frame77 = Instance.new("Frame")
97Frame78 = Instance.new("Frame")
98Frame79 = Instance.new("Frame")
99Frame80 = Instance.new("Frame")
100TextLabel81 = Instance.new("TextLabel")
101Frame82 = Instance.new("Frame")
102Frame83 = Instance.new("Frame")
103Frame84 = Instance.new("Frame")
104Frame85 = Instance.new("Frame")
105Frame86 = Instance.new("Frame")
106Frame87 = Instance.new("Frame")
107Frame88 = Instance.new("Frame")
108TextButton89 = Instance.new("TextButton")
109Frame90 = Instance.new("Frame")
110Frame91 = Instance.new("Frame")
111Frame92 = Instance.new("Frame")
112Frame93 = Instance.new("Frame")
113Frame94 = Instance.new("Frame")
114Frame95 = Instance.new("Frame")
115Frame96 = Instance.new("Frame")
116Frame97 = Instance.new("Frame")
117TextLabel98 = Instance.new("TextLabel")
118Frame99 = Instance.new("Frame")
119Frame100 = Instance.new("Frame")
120Frame101 = Instance.new("Frame")
121Frame102 = Instance.new("Frame")
122Frame103 = Instance.new("Frame")
123Frame104 = Instance.new("Frame")
124TextButton105 = Instance.new("TextButton")
125Frame106 = Instance.new("Frame")
126Frame107 = Instance.new("Frame")
127Frame108 = Instance.new("Frame")
128Frame109 = Instance.new("Frame")
129TextLabel110 = Instance.new("TextLabel")
130TextLabel111 = Instance.new("TextLabel")
131Frame112 = Instance.new("Frame")
132TextLabel113 = Instance.new("TextLabel")
133Frame114 = Instance.new("Frame")
134TextLabel115 = Instance.new("TextLabel")
135Frame116 = Instance.new("Frame")
136TextButton117 = Instance.new("TextButton")
137ImageLabel118 = Instance.new("ImageLabel")
138Frame119 = Instance.new("Frame")
139TextBox120 = Instance.new("TextBox")
140Frame121 = Instance.new("Frame")
141TextLabel122 = Instance.new("TextLabel")
142Frame123 = Instance.new("Frame")
143TextButton124 = Instance.new("TextButton")
144Frame125 = Instance.new("Frame")
145ImageLabel126 = Instance.new("ImageLabel")
146TextBox127 = Instance.new("TextBox")
147Frame128 = Instance.new("Frame")
148Frame129 = Instance.new("Frame")
149TextLabel130 = Instance.new("TextLabel")
150Frame131 = Instance.new("Frame")
151Frame132 = Instance.new("Frame")
152Frame133 = Instance.new("Frame")
153Frame134 = Instance.new("Frame")
154TextButton135 = Instance.new("TextButton")
155ImageLabel136 = Instance.new("ImageLabel")
156TextLabel137 = Instance.new("TextLabel")
157Frame138 = Instance.new("Frame")
158Frame139 = Instance.new("Frame")
159TextButton140 = Instance.new("TextButton")
160ImageLabel141 = Instance.new("ImageLabel")
161TextLabel142 = Instance.new("TextLabel")
162Frame143 = Instance.new("Frame")
163Frame144 = Instance.new("Frame")
164TextButton145 = Instance.new("TextButton")
165ImageLabel146 = Instance.new("ImageLabel")
166TextLabel147 = Instance.new("TextLabel")
167Frame148 = Instance.new("Frame")
168TextLabel149 = Instance.new("TextLabel")
169Frame150 = Instance.new("Frame")
170Frame151 = Instance.new("Frame")
171TextLabel152 = Instance.new("TextLabel")
172TextLabel153 = Instance.new("TextLabel")
173Frame154 = Instance.new("Frame")
174Frame155 = Instance.new("Frame")
175Frame156 = Instance.new("Frame")
176TextBox157 = Instance.new("TextBox")
177ImageLabel158 = Instance.new("ImageLabel")
178Frame159 = Instance.new("Frame")
179TextLabel160 = Instance.new("TextLabel")
180Frame161 = Instance.new("Frame")
181Frame162 = Instance.new("Frame")
182TextLabel163 = Instance.new("TextLabel")
183Frame164 = Instance.new("Frame")
184TextLabel165 = Instance.new("TextLabel")
185Frame166 = Instance.new("Frame")
186ImageLabel167 = Instance.new("ImageLabel")
187TextButton168 = Instance.new("TextButton")
188TextBox169 = Instance.new("TextBox")
189Frame170 = Instance.new("Frame")
190ImageLabel171 = Instance.new("ImageLabel")
191TextBox172 = Instance.new("TextBox")
192TextButton173 = Instance.new("TextButton")
193Frame174 = Instance.new("Frame")
194TextBox175 = Instance.new("TextBox")
195TextButton176 = Instance.new("TextButton")
196ImageLabel177 = Instance.new("ImageLabel")
197Frame178 = Instance.new("Frame")
198Frame179 = Instance.new("Frame")
199TextLabel180 = Instance.new("TextLabel")
200Frame181 = Instance.new("Frame")
201Frame182 = Instance.new("Frame")
202Frame183 = Instance.new("Frame")
203TextLabel184 = Instance.new("TextLabel")
204TextLabel185 = Instance.new("TextLabel")
205Frame186 = Instance.new("Frame")
206TextLabel187 = Instance.new("TextLabel")
207Frame188 = Instance.new("Frame")
208Frame189 = Instance.new("Frame")
209TextLabel190 = Instance.new("TextLabel")
210Frame191 = Instance.new("Frame")
211Frame192 = Instance.new("Frame")
212Frame193 = Instance.new("Frame")
213TextLabel194 = Instance.new("TextLabel")
214TextLabel195 = Instance.new("TextLabel")
215Frame196 = Instance.new("Frame")
216TextButton197 = Instance.new("TextButton")
217TextButton198 = Instance.new("TextButton")
218TextButton199 = Instance.new("TextButton")
219TextButton200 = Instance.new("TextButton")
220TextButton201 = Instance.new("TextButton")
221TextButton202 = Instance.new("TextButton")
222TextButton203 = Instance.new("TextButton")
223TextButton204 = Instance.new("TextButton")
224TextButton205 = Instance.new("TextButton")
225TextButton206 = Instance.new("TextButton")
226TextButton207 = Instance.new("TextButton")
227TextButton208 = Instance.new("TextButton")
228TextButton209 = Instance.new("TextButton")
229TextButton210 = Instance.new("TextButton")
230TextButton211 = Instance.new("TextButton")
231TextButton212 = Instance.new("TextButton")
232TextButton213 = Instance.new("TextButton")
233TextButton214 = Instance.new("TextButton")
234TextButton215 = Instance.new("TextButton")
235TextButton216 = Instance.new("TextButton")
236TextButton217 = Instance.new("TextButton")
237TextButton218 = Instance.new("TextButton")
238TextButton219 = Instance.new("TextButton")
239TextButton220 = Instance.new("TextButton")
240TextButton221 = Instance.new("TextButton")
241TextButton222 = Instance.new("TextButton")
242TextButton223 = Instance.new("TextButton")
243TextButton224 = Instance.new("TextButton")
244TextButton225 = Instance.new("TextButton")
245TextButton226 = Instance.new("TextButton")
246TextButton227 = Instance.new("TextButton")
247TextButton228 = Instance.new("TextButton")
248TextButton229 = Instance.new("TextButton")
249TextButton230 = Instance.new("TextButton")
250TextButton231 = Instance.new("TextButton")
251TextButton232 = Instance.new("TextButton")
252TextButton233 = Instance.new("TextButton")
253TextButton234 = Instance.new("TextButton")
254TextButton235 = Instance.new("TextButton")
255TextButton236 = Instance.new("TextButton")
256TextButton237 = Instance.new("TextButton")
257TextButton238 = Instance.new("TextButton")
258TextButton239 = Instance.new("TextButton")
259TextButton240 = Instance.new("TextButton")
260TextButton241 = Instance.new("TextButton")
261TextButton242 = Instance.new("TextButton")
262TextButton243 = Instance.new("TextButton")
263TextButton244 = Instance.new("TextButton")
264TextButton245 = Instance.new("TextButton")
265TextButton246 = Instance.new("TextButton")
266TextButton247 = Instance.new("TextButton")
267TextButton248 = Instance.new("TextButton")
268TextButton249 = Instance.new("TextButton")
269TextButton250 = Instance.new("TextButton")
270TextButton251 = Instance.new("TextButton")
271TextButton252 = Instance.new("TextButton")
272TextButton253 = Instance.new("TextButton")
273TextButton254 = Instance.new("TextButton")
274TextButton255 = Instance.new("TextButton")
275TextButton256 = Instance.new("TextButton")
276TextButton257 = Instance.new("TextButton")
277TextButton258 = Instance.new("TextButton")
278TextButton259 = Instance.new("TextButton")
279TextButton260 = Instance.new("TextButton")
280Frame261 = Instance.new("Frame")
281Frame262 = Instance.new("Frame")
282Frame263 = Instance.new("Frame")
283Frame264 = Instance.new("Frame")
284TextButton265 = Instance.new("TextButton")
285ImageLabel266 = Instance.new("ImageLabel")
286TextLabel267 = Instance.new("TextLabel")
287Frame268 = Instance.new("Frame")
288Frame269 = Instance.new("Frame")
289TextButton270 = Instance.new("TextButton")
290ImageLabel271 = Instance.new("ImageLabel")
291TextLabel272 = Instance.new("TextLabel")
292Frame273 = Instance.new("Frame")
293TextLabel274 = Instance.new("TextLabel")
294Frame275 = Instance.new("Frame")
295Frame276 = Instance.new("Frame")
296TextLabel277 = Instance.new("TextLabel")
297TextLabel278 = Instance.new("TextLabel")
298Frame279 = Instance.new("Frame")
299Frame280 = Instance.new("Frame")
300Frame281 = Instance.new("Frame")
301TextBox282 = Instance.new("TextBox")
302ImageLabel283 = Instance.new("ImageLabel")
303Frame284 = Instance.new("Frame")
304TextLabel285 = Instance.new("TextLabel")
305Frame286 = Instance.new("Frame")
306Frame287 = Instance.new("Frame")
307TextLabel288 = Instance.new("TextLabel")
308Frame289 = Instance.new("Frame")
309TextLabel290 = Instance.new("TextLabel")
310Frame291 = Instance.new("Frame")
311TextBox292 = Instance.new("TextBox")
312TextButton293 = Instance.new("TextButton")
313ImageLabel294 = Instance.new("ImageLabel")
314Frame295 = Instance.new("Frame")
315TextBox296 = Instance.new("TextBox")
316TextButton297 = Instance.new("TextButton")
317ImageLabel298 = Instance.new("ImageLabel")
318Frame299 = Instance.new("Frame")
319TextBox300 = Instance.new("TextBox")
320TextButton301 = Instance.new("TextButton")
321ImageLabel302 = Instance.new("ImageLabel")
322Frame303 = Instance.new("Frame")
323Frame304 = Instance.new("Frame")
324TextLabel305 = Instance.new("TextLabel")
325Frame306 = Instance.new("Frame")
326Frame307 = Instance.new("Frame")
327Frame308 = Instance.new("Frame")
328Frame309 = Instance.new("Frame")
329TextButton310 = Instance.new("TextButton")
330ImageLabel311 = Instance.new("ImageLabel")
331TextLabel312 = Instance.new("TextLabel")
332Frame313 = Instance.new("Frame")
333Frame314 = Instance.new("Frame")
334TextButton315 = Instance.new("TextButton")
335ImageLabel316 = Instance.new("ImageLabel")
336TextLabel317 = Instance.new("TextLabel")
337Frame318 = Instance.new("Frame")
338Frame319 = Instance.new("Frame")
339TextButton320 = Instance.new("TextButton")
340ImageLabel321 = Instance.new("ImageLabel")
341TextLabel322 = Instance.new("TextLabel")
342Frame323 = Instance.new("Frame")
343TextLabel324 = Instance.new("TextLabel")
344Frame325 = Instance.new("Frame")
345Frame326 = Instance.new("Frame")
346TextLabel327 = Instance.new("TextLabel")
347TextLabel328 = Instance.new("TextLabel")
348Frame329 = Instance.new("Frame")
349Frame330 = Instance.new("Frame")
350Frame331 = Instance.new("Frame")
351TextBox332 = Instance.new("TextBox")
352ImageLabel333 = Instance.new("ImageLabel")
353Frame334 = Instance.new("Frame")
354TextLabel335 = Instance.new("TextLabel")
355Frame336 = Instance.new("Frame")
356Frame337 = Instance.new("Frame")
357TextLabel338 = Instance.new("TextLabel")
358Frame339 = Instance.new("Frame")
359TextLabel340 = Instance.new("TextLabel")
360Frame341 = Instance.new("Frame")
361TextBox342 = Instance.new("TextBox")
362TextButton343 = Instance.new("TextButton")
363ImageLabel344 = Instance.new("ImageLabel")
364Frame345 = Instance.new("Frame")
365TextBox346 = Instance.new("TextBox")
366TextButton347 = Instance.new("TextButton")
367ImageLabel348 = Instance.new("ImageLabel")
368Frame349 = Instance.new("Frame")
369TextBox350 = Instance.new("TextBox")
370TextButton351 = Instance.new("TextButton")
371ImageLabel352 = Instance.new("ImageLabel")
372Frame353 = Instance.new("Frame")
373Frame354 = Instance.new("Frame")
374TextLabel355 = Instance.new("TextLabel")
375TextLabel356 = Instance.new("TextLabel")
376Frame357 = Instance.new("Frame")
377Frame358 = Instance.new("Frame")
378Frame359 = Instance.new("Frame")
379TextLabel360 = Instance.new("TextLabel")
380TextLabel361 = Instance.new("TextLabel")
381Frame362 = Instance.new("Frame")
382TextLabel363 = Instance.new("TextLabel")
383Frame364 = Instance.new("Frame")
384TextLabel365 = Instance.new("TextLabel")
385Frame366 = Instance.new("Frame")
386Frame367 = Instance.new("Frame")
387TextLabel368 = Instance.new("TextLabel")
388Frame369 = Instance.new("Frame")
389Frame370 = Instance.new("Frame")
390Frame371 = Instance.new("Frame")
391TextLabel372 = Instance.new("TextLabel")
392TextLabel373 = Instance.new("TextLabel")
393Frame374 = Instance.new("Frame")
394TextLabel375 = Instance.new("TextLabel")
395Frame376 = Instance.new("Frame")
396TextLabel377 = Instance.new("TextLabel")
397Frame378 = Instance.new("Frame")
398TextButton379 = Instance.new("TextButton")
399ImageLabel380 = Instance.new("ImageLabel")
400Frame381 = Instance.new("Frame")
401TextBox382 = Instance.new("TextBox")
402Frame383 = Instance.new("Frame")
403TextButton384 = Instance.new("TextButton")
404ImageLabel385 = Instance.new("ImageLabel")
405Frame386 = Instance.new("Frame")
406TextBox387 = Instance.new("TextBox")
407Frame388 = Instance.new("Frame")
408Frame389 = Instance.new("Frame")
409TextLabel390 = Instance.new("TextLabel")
410Frame391 = Instance.new("Frame")
411TextButton392 = Instance.new("TextButton")
412ImageLabel393 = Instance.new("ImageLabel")
413Frame394 = Instance.new("Frame")
414TextBox395 = Instance.new("TextBox")
415Frame396 = Instance.new("Frame")
416TextLabel397 = Instance.new("TextLabel")
417Frame398 = Instance.new("Frame")
418Frame399 = Instance.new("Frame")
419TextButton400 = Instance.new("TextButton")
420ImageLabel401 = Instance.new("ImageLabel")
421TextLabel402 = Instance.new("TextLabel")
422Frame403 = Instance.new("Frame")
423TextButton404 = Instance.new("TextButton")
424ImageLabel405 = Instance.new("ImageLabel")
425TextLabel406 = Instance.new("TextLabel")
426Frame407 = Instance.new("Frame")
427Frame408 = Instance.new("Frame")
428TextLabel409 = Instance.new("TextLabel")
429Frame410 = Instance.new("Frame")
430Frame411 = Instance.new("Frame")
431Frame412 = Instance.new("Frame")
432Frame413 = Instance.new("Frame")
433Frame414 = Instance.new("Frame")
434TextButton415 = Instance.new("TextButton")
435TextBox416 = Instance.new("TextBox")
436Frame417 = Instance.new("Frame")
437TextButton418 = Instance.new("TextButton")
438Frame419 = Instance.new("Frame")
439Frame420 = Instance.new("Frame")
440TextButton421 = Instance.new("TextButton")
441Frame422 = Instance.new("Frame")
442TextLabel423 = Instance.new("TextLabel")
443Frame424 = Instance.new("Frame")
444Frame425 = Instance.new("Frame")
445Frame426 = Instance.new("Frame")
446TextLabel427 = Instance.new("TextLabel")
447TextLabel428 = Instance.new("TextLabel")
448Frame429 = Instance.new("Frame")
449TextButton430 = Instance.new("TextButton")
450Frame431 = Instance.new("Frame")
451TextButton432 = Instance.new("TextButton")
452Frame433 = Instance.new("Frame")
453Frame434 = Instance.new("Frame")
454Frame435 = Instance.new("Frame")
455TextLabel436 = Instance.new("TextLabel")
456Frame437 = Instance.new("Frame")
457Frame438 = Instance.new("Frame")
458Frame439 = Instance.new("Frame")
459Frame440 = Instance.new("Frame")
460TextLabel441 = Instance.new("TextLabel")
461TextLabel442 = Instance.new("TextLabel")
462Frame443 = Instance.new("Frame")
463TextLabel444 = Instance.new("TextLabel")
464ImageButton445 = Instance.new("ImageButton")
465Frame446 = Instance.new("Frame")
466TextButton447 = Instance.new("TextButton")
467TextButton448 = Instance.new("TextButton")
468Frame449 = Instance.new("Frame")
469Frame450 = Instance.new("Frame")
470Frame451 = Instance.new("Frame")
471TextLabel452 = Instance.new("TextLabel")
472Frame453 = Instance.new("Frame")
473TextButton454 = Instance.new("TextButton")
474ImageLabel455 = Instance.new("ImageLabel")
475Frame456 = Instance.new("Frame")
476TextBox457 = Instance.new("TextBox")
477Frame458 = Instance.new("Frame")
478TextButton459 = Instance.new("TextButton")
479ImageLabel460 = Instance.new("ImageLabel")
480Frame461 = Instance.new("Frame")
481TextBox462 = Instance.new("TextBox")
482Frame463 = Instance.new("Frame")
483TextButton464 = Instance.new("TextButton")
484ImageLabel465 = Instance.new("ImageLabel")
485Frame466 = Instance.new("Frame")
486TextBox467 = Instance.new("TextBox")
487ImageButton468 = Instance.new("ImageButton")
488Frame469 = Instance.new("Frame")
489Frame470 = Instance.new("Frame")
490Frame471 = Instance.new("Frame")
491Frame472 = Instance.new("Frame")
492Frame473 = Instance.new("Frame")
493TextLabel474 = Instance.new("TextLabel")
494Frame475 = Instance.new("Frame")
495TextButton476 = Instance.new("TextButton")
496ImageLabel477 = Instance.new("ImageLabel")
497Frame478 = Instance.new("Frame")
498TextBox479 = Instance.new("TextBox")
499Frame480 = Instance.new("Frame")
500TextLabel481 = Instance.new("TextLabel")
501Frame482 = Instance.new("Frame")
502TextButton483 = Instance.new("TextButton")
503ImageLabel484 = Instance.new("ImageLabel")
504Frame485 = Instance.new("Frame")
505TextBox486 = Instance.new("TextBox")
506Frame487 = Instance.new("Frame")
507TextLabel488 = Instance.new("TextLabel")
508Frame489 = Instance.new("Frame")
509TextButton490 = Instance.new("TextButton")
510ImageLabel491 = Instance.new("ImageLabel")
511Frame492 = Instance.new("Frame")
512TextBox493 = Instance.new("TextBox")
513Frame494 = Instance.new("Frame")
514TextLabel495 = Instance.new("TextLabel")
515Frame496 = Instance.new("Frame")
516TextLabel497 = Instance.new("TextLabel")
517Frame498 = Instance.new("Frame")
518Frame499 = Instance.new("Frame")
519TextButton500 = Instance.new("TextButton")
520ImageLabel501 = Instance.new("ImageLabel")
521TextLabel502 = Instance.new("TextLabel")
522Frame503 = Instance.new("Frame")
523Frame504 = Instance.new("Frame")
524TextButton505 = Instance.new("TextButton")
525ImageLabel506 = Instance.new("ImageLabel")
526TextLabel507 = Instance.new("TextLabel")
527TextLabel508 = Instance.new("TextLabel")
528Frame509 = Instance.new("Frame")
529TextLabel510 = Instance.new("TextLabel")
530ImageButton511 = Instance.new("ImageButton")
531Frame512 = Instance.new("Frame")
532TextButton513 = Instance.new("TextButton")
533TextButton514 = Instance.new("TextButton")
534Frame515 = Instance.new("Frame")
535Frame516 = Instance.new("Frame")
536Frame517 = Instance.new("Frame")
537TextLabel518 = Instance.new("TextLabel")
538Frame519 = Instance.new("Frame")
539TextButton520 = Instance.new("TextButton")
540ImageLabel521 = Instance.new("ImageLabel")
541Frame522 = Instance.new("Frame")
542TextBox523 = Instance.new("TextBox")
543Frame524 = Instance.new("Frame")
544TextButton525 = Instance.new("TextButton")
545ImageLabel526 = Instance.new("ImageLabel")
546Frame527 = Instance.new("Frame")
547TextBox528 = Instance.new("TextBox")
548Frame529 = Instance.new("Frame")
549TextButton530 = Instance.new("TextButton")
550ImageLabel531 = Instance.new("ImageLabel")
551Frame532 = Instance.new("Frame")
552TextBox533 = Instance.new("TextBox")
553ImageButton534 = Instance.new("ImageButton")
554Frame535 = Instance.new("Frame")
555Frame536 = Instance.new("Frame")
556Frame537 = Instance.new("Frame")
557Frame538 = Instance.new("Frame")
558Frame539 = Instance.new("Frame")
559TextLabel540 = Instance.new("TextLabel")
560Frame541 = Instance.new("Frame")
561TextButton542 = Instance.new("TextButton")
562ImageLabel543 = Instance.new("ImageLabel")
563Frame544 = Instance.new("Frame")
564TextBox545 = Instance.new("TextBox")
565Frame546 = Instance.new("Frame")
566TextLabel547 = Instance.new("TextLabel")
567Frame548 = Instance.new("Frame")
568TextButton549 = Instance.new("TextButton")
569ImageLabel550 = Instance.new("ImageLabel")
570Frame551 = Instance.new("Frame")
571TextBox552 = Instance.new("TextBox")
572Frame553 = Instance.new("Frame")
573TextLabel554 = Instance.new("TextLabel")
574Frame555 = Instance.new("Frame")
575Frame556 = Instance.new("Frame")
576TextButton557 = Instance.new("TextButton")
577ImageLabel558 = Instance.new("ImageLabel")
578TextLabel559 = Instance.new("TextLabel")
579Frame560 = Instance.new("Frame")
580Frame561 = Instance.new("Frame")
581TextButton562 = Instance.new("TextButton")
582ImageLabel563 = Instance.new("ImageLabel")
583TextLabel564 = Instance.new("TextLabel")
584Frame565 = Instance.new("Frame")
585ImageButton566 = Instance.new("ImageButton")
586ImageLabel567 = Instance.new("ImageLabel")
587ImageButton568 = Instance.new("ImageButton")
588Frame569 = Instance.new("Frame")
589ImageLabel570 = Instance.new("ImageLabel")
590Frame571 = Instance.new("Frame")
591TextLabel572 = Instance.new("TextLabel")
592Frame573 = Instance.new("Frame")
593TextButton574 = Instance.new("TextButton")
594ImageLabel575 = Instance.new("ImageLabel")
595TextBox576 = Instance.new("TextBox")
596Frame577 = Instance.new("Frame")
597Frame578 = Instance.new("Frame")
598Frame579 = Instance.new("Frame")
599TextLabel580 = Instance.new("TextLabel")
600Frame581 = Instance.new("Frame")
601TextButton582 = Instance.new("TextButton")
602ImageLabel583 = Instance.new("ImageLabel")
603TextBox584 = Instance.new("TextBox")
604Frame585 = Instance.new("Frame")
605Frame586 = Instance.new("Frame")
606Frame587 = Instance.new("Frame")
607TextLabel588 = Instance.new("TextLabel")
608Frame589 = Instance.new("Frame")
609TextButton590 = Instance.new("TextButton")
610ImageLabel591 = Instance.new("ImageLabel")
611TextBox592 = Instance.new("TextBox")
612Frame593 = Instance.new("Frame")
613Frame594 = Instance.new("Frame")
614Frame595 = Instance.new("Frame")
615Frame596 = Instance.new("Frame")
616TextButton597 = Instance.new("TextButton")
617Frame598 = Instance.new("Frame")
618TextButton599 = Instance.new("TextButton")
619Frame600 = Instance.new("Frame")
620Frame601 = Instance.new("Frame")
621Frame602 = Instance.new("Frame")
622Frame603 = Instance.new("Frame")
623TextLabel604 = Instance.new("TextLabel")
624TextLabel605 = Instance.new("TextLabel")
625Frame606 = Instance.new("Frame")
626TextLabel607 = Instance.new("TextLabel")
627Frame608 = Instance.new("Frame")
628TextLabel609 = Instance.new("TextLabel")
629Frame610 = Instance.new("Frame")
630TextButton611 = Instance.new("TextButton")
631ImageLabel612 = Instance.new("ImageLabel")
632Frame613 = Instance.new("Frame")
633TextBox614 = Instance.new("TextBox")
634Frame615 = Instance.new("Frame")
635TextButton616 = Instance.new("TextButton")
636ImageLabel617 = Instance.new("ImageLabel")
637Frame618 = Instance.new("Frame")
638TextBox619 = Instance.new("TextBox")
639Frame620 = Instance.new("Frame")
640TextButton621 = Instance.new("TextButton")
641ImageLabel622 = Instance.new("ImageLabel")
642Frame623 = Instance.new("Frame")
643TextBox624 = Instance.new("TextBox")
644Frame625 = Instance.new("Frame")
645TextButton626 = Instance.new("TextButton")
646Frame627 = Instance.new("Frame")
647Frame628 = Instance.new("Frame")
648TextLabel629 = Instance.new("TextLabel")
649TextBox630 = Instance.new("TextBox")
650Frame631 = Instance.new("Frame")
651Frame632 = Instance.new("Frame")
652Frame633 = Instance.new("Frame")
653Frame634 = Instance.new("Frame")
654Frame635 = Instance.new("Frame")
655TextButton636 = Instance.new("TextButton")
656Frame637 = Instance.new("Frame")
657Frame638 = Instance.new("Frame")
658TextLabel639 = Instance.new("TextLabel")
659TextBox640 = Instance.new("TextBox")
660Frame641 = Instance.new("Frame")
661Frame642 = Instance.new("Frame")
662Frame643 = Instance.new("Frame")
663Frame644 = Instance.new("Frame")
664Frame645 = Instance.new("Frame")
665TextButton646 = Instance.new("TextButton")
666Frame647 = Instance.new("Frame")
667TextButton648 = Instance.new("TextButton")
668Frame649 = Instance.new("Frame")
669Frame650 = Instance.new("Frame")
670TextLabel651 = Instance.new("TextLabel")
671Frame652 = Instance.new("Frame")
672TextButton653 = Instance.new("TextButton")
673ImageLabel654 = Instance.new("ImageLabel")
674Frame655 = Instance.new("Frame")
675TextBox656 = Instance.new("TextBox")
676Frame657 = Instance.new("Frame")
677TextButton658 = Instance.new("TextButton")
678ImageLabel659 = Instance.new("ImageLabel")
679Frame660 = Instance.new("Frame")
680TextBox661 = Instance.new("TextBox")
681Frame662 = Instance.new("Frame")
682TextButton663 = Instance.new("TextButton")
683ImageLabel664 = Instance.new("ImageLabel")
684Frame665 = Instance.new("Frame")
685TextBox666 = Instance.new("TextBox")
686Frame667 = Instance.new("Frame")
687Frame668 = Instance.new("Frame")
688Frame669 = Instance.new("Frame")
689ImageButton670 = Instance.new("ImageButton")
690Frame671 = Instance.new("Frame")
691TextLabel672 = Instance.new("TextLabel")
692Frame673 = Instance.new("Frame")
693Frame674 = Instance.new("Frame")
694Frame675 = Instance.new("Frame")
695Frame676 = Instance.new("Frame")
696TextLabel677 = Instance.new("TextLabel")
697TextLabel678 = Instance.new("TextLabel")
698Frame679 = Instance.new("Frame")
699TextLabel680 = Instance.new("TextLabel")
700ImageButton681 = Instance.new("ImageButton")
701Frame682 = Instance.new("Frame")
702TextButton683 = Instance.new("TextButton")
703TextButton684 = Instance.new("TextButton")
704Frame685 = Instance.new("Frame")
705Frame686 = Instance.new("Frame")
706Frame687 = Instance.new("Frame")
707TextLabel688 = Instance.new("TextLabel")
708Frame689 = Instance.new("Frame")
709TextButton690 = Instance.new("TextButton")
710ImageLabel691 = Instance.new("ImageLabel")
711Frame692 = Instance.new("Frame")
712TextBox693 = Instance.new("TextBox")
713Frame694 = Instance.new("Frame")
714TextButton695 = Instance.new("TextButton")
715ImageLabel696 = Instance.new("ImageLabel")
716Frame697 = Instance.new("Frame")
717TextBox698 = Instance.new("TextBox")
718Frame699 = Instance.new("Frame")
719TextButton700 = Instance.new("TextButton")
720ImageLabel701 = Instance.new("ImageLabel")
721Frame702 = Instance.new("Frame")
722TextBox703 = Instance.new("TextBox")
723ImageButton704 = Instance.new("ImageButton")
724Frame705 = Instance.new("Frame")
725Frame706 = Instance.new("Frame")
726Frame707 = Instance.new("Frame")
727Frame708 = Instance.new("Frame")
728Frame709 = Instance.new("Frame")
729TextLabel710 = Instance.new("TextLabel")
730Frame711 = Instance.new("Frame")
731TextButton712 = Instance.new("TextButton")
732ImageLabel713 = Instance.new("ImageLabel")
733Frame714 = Instance.new("Frame")
734TextBox715 = Instance.new("TextBox")
735Frame716 = Instance.new("Frame")
736TextLabel717 = Instance.new("TextLabel")
737Frame718 = Instance.new("Frame")
738TextButton719 = Instance.new("TextButton")
739ImageLabel720 = Instance.new("ImageLabel")
740Frame721 = Instance.new("Frame")
741TextBox722 = Instance.new("TextBox")
742Frame723 = Instance.new("Frame")
743TextLabel724 = Instance.new("TextLabel")
744Frame725 = Instance.new("Frame")
745TextButton726 = Instance.new("TextButton")
746ImageLabel727 = Instance.new("ImageLabel")
747Frame728 = Instance.new("Frame")
748TextBox729 = Instance.new("TextBox")
749TextLabel730 = Instance.new("TextLabel")
750Frame731 = Instance.new("Frame")
751TextLabel732 = Instance.new("TextLabel")
752ImageButton733 = Instance.new("ImageButton")
753Frame734 = Instance.new("Frame")
754TextButton735 = Instance.new("TextButton")
755TextButton736 = Instance.new("TextButton")
756Frame737 = Instance.new("Frame")
757Frame738 = Instance.new("Frame")
758Frame739 = Instance.new("Frame")
759TextLabel740 = Instance.new("TextLabel")
760Frame741 = Instance.new("Frame")
761TextButton742 = Instance.new("TextButton")
762ImageLabel743 = Instance.new("ImageLabel")
763Frame744 = Instance.new("Frame")
764TextBox745 = Instance.new("TextBox")
765Frame746 = Instance.new("Frame")
766TextButton747 = Instance.new("TextButton")
767ImageLabel748 = Instance.new("ImageLabel")
768Frame749 = Instance.new("Frame")
769TextBox750 = Instance.new("TextBox")
770Frame751 = Instance.new("Frame")
771TextButton752 = Instance.new("TextButton")
772ImageLabel753 = Instance.new("ImageLabel")
773Frame754 = Instance.new("Frame")
774TextBox755 = Instance.new("TextBox")
775ImageButton756 = Instance.new("ImageButton")
776Frame757 = Instance.new("Frame")
777Frame758 = Instance.new("Frame")
778Frame759 = Instance.new("Frame")
779Frame760 = Instance.new("Frame")
780Frame761 = Instance.new("Frame")
781TextLabel762 = Instance.new("TextLabel")
782Frame763 = Instance.new("Frame")
783TextButton764 = Instance.new("TextButton")
784ImageLabel765 = Instance.new("ImageLabel")
785Frame766 = Instance.new("Frame")
786TextBox767 = Instance.new("TextBox")
787Frame768 = Instance.new("Frame")
788TextLabel769 = Instance.new("TextLabel")
789Frame770 = Instance.new("Frame")
790TextButton771 = Instance.new("TextButton")
791ImageLabel772 = Instance.new("ImageLabel")
792Frame773 = Instance.new("Frame")
793TextBox774 = Instance.new("TextBox")
794Frame775 = Instance.new("Frame")
795TextLabel776 = Instance.new("TextLabel")
796Frame777 = Instance.new("Frame")
797TextButton778 = Instance.new("TextButton")
798ImageLabel779 = Instance.new("ImageLabel")
799Frame780 = Instance.new("Frame")
800TextBox781 = Instance.new("TextBox")
801Frame782 = Instance.new("Frame")
802TextButton783 = Instance.new("TextButton")
803ImageLabel784 = Instance.new("ImageLabel")
804Frame785 = Instance.new("Frame")
805TextBox786 = Instance.new("TextBox")
806Frame787 = Instance.new("Frame")
807TextButton788 = Instance.new("TextButton")
808ImageLabel789 = Instance.new("ImageLabel")
809Frame790 = Instance.new("Frame")
810TextBox791 = Instance.new("TextBox")
811ImageButton792 = Instance.new("ImageButton")
812Frame793 = Instance.new("Frame")
813Frame794 = Instance.new("Frame")
814Frame795 = Instance.new("Frame")
815Frame796 = Instance.new("Frame")
816Frame797 = Instance.new("Frame")
817TextLabel798 = Instance.new("TextLabel")
818ImageButton799 = Instance.new("ImageButton")
819Frame800 = Instance.new("Frame")
820TextButton801 = Instance.new("TextButton")
821TextButton802 = Instance.new("TextButton")
822Frame803 = Instance.new("Frame")
823Frame804 = Instance.new("Frame")
824Frame805 = Instance.new("Frame")
825TextLabel806 = Instance.new("TextLabel")
826Frame807 = Instance.new("Frame")
827TextButton808 = Instance.new("TextButton")
828ImageLabel809 = Instance.new("ImageLabel")
829Frame810 = Instance.new("Frame")
830TextBox811 = Instance.new("TextBox")
831Frame812 = Instance.new("Frame")
832TextButton813 = Instance.new("TextButton")
833ImageLabel814 = Instance.new("ImageLabel")
834Frame815 = Instance.new("Frame")
835TextBox816 = Instance.new("TextBox")
836Frame817 = Instance.new("Frame")
837TextButton818 = Instance.new("TextButton")
838ImageLabel819 = Instance.new("ImageLabel")
839Frame820 = Instance.new("Frame")
840TextBox821 = Instance.new("TextBox")
841ImageButton822 = Instance.new("ImageButton")
842Frame823 = Instance.new("Frame")
843Frame824 = Instance.new("Frame")
844Frame825 = Instance.new("Frame")
845Frame826 = Instance.new("Frame")
846Frame827 = Instance.new("Frame")
847Frame828 = Instance.new("Frame")
848Frame829 = Instance.new("Frame")
849Frame830 = Instance.new("Frame")
850Frame831 = Instance.new("Frame")
851Frame832 = Instance.new("Frame")
852TextLabel833 = Instance.new("TextLabel")
853TextLabel834 = Instance.new("TextLabel")
854TextLabel835 = Instance.new("TextLabel")
855TextLabel836 = Instance.new("TextLabel")
856Frame837 = Instance.new("Frame")
857TextLabel838 = Instance.new("TextLabel")
858TextLabel839 = Instance.new("TextLabel")
859Frame840 = Instance.new("Frame")
860TextLabel841 = Instance.new("TextLabel")
861TextLabel842 = Instance.new("TextLabel")
862Frame843 = Instance.new("Frame")
863Frame844 = Instance.new("Frame")
864Frame845 = Instance.new("Frame")
865TextLabel846 = Instance.new("TextLabel")
866TextLabel847 = Instance.new("TextLabel")
867Frame848 = Instance.new("Frame")
868TextLabel849 = Instance.new("TextLabel")
869TextLabel850 = Instance.new("TextLabel")
870Frame851 = Instance.new("Frame")
871TextLabel852 = Instance.new("TextLabel")
872TextLabel853 = Instance.new("TextLabel")
873Frame854 = Instance.new("Frame")
874Frame855 = Instance.new("Frame")
875Frame856 = Instance.new("Frame")
876TextLabel857 = Instance.new("TextLabel")
877TextLabel858 = Instance.new("TextLabel")
878Frame859 = Instance.new("Frame")
879Frame860 = Instance.new("Frame")
880Frame861 = Instance.new("Frame")
881TextLabel862 = Instance.new("TextLabel")
882TextLabel863 = Instance.new("TextLabel")
883Frame864 = Instance.new("Frame")
884Frame865 = Instance.new("Frame")
885Frame866 = Instance.new("Frame")
886Frame867 = Instance.new("Frame")
887TextLabel868 = Instance.new("TextLabel")
888TextLabel869 = Instance.new("TextLabel")
889TextLabel870 = Instance.new("TextLabel")
890TextLabel871 = Instance.new("TextLabel")
891Frame872 = Instance.new("Frame")
892TextLabel873 = Instance.new("TextLabel")
893TextLabel874 = Instance.new("TextLabel")
894Frame875 = Instance.new("Frame")
895TextLabel876 = Instance.new("TextLabel")
896TextLabel877 = Instance.new("TextLabel")
897Frame878 = Instance.new("Frame")
898Frame879 = Instance.new("Frame")
899Frame880 = Instance.new("Frame")
900TextLabel881 = Instance.new("TextLabel")
901TextLabel882 = Instance.new("TextLabel")
902Frame883 = Instance.new("Frame")
903Frame884 = Instance.new("Frame")
904Frame885 = Instance.new("Frame")
905TextLabel886 = Instance.new("TextLabel")
906TextLabel887 = Instance.new("TextLabel")
907Frame888 = Instance.new("Frame")
908Frame889 = Instance.new("Frame")
909Frame890 = Instance.new("Frame")
910TextLabel891 = Instance.new("TextLabel")
911TextLabel892 = Instance.new("TextLabel")
912Frame893 = Instance.new("Frame")
913Frame894 = Instance.new("Frame")
914Frame895 = Instance.new("Frame")
915TextLabel896 = Instance.new("TextLabel")
916TextLabel897 = Instance.new("TextLabel")
917Frame898 = Instance.new("Frame")
918Frame899 = Instance.new("Frame")
919Frame900 = Instance.new("Frame")
920TextLabel901 = Instance.new("TextLabel")
921TextLabel902 = Instance.new("TextLabel")
922Frame903 = Instance.new("Frame")
923Frame904 = Instance.new("Frame")
924Frame905 = Instance.new("Frame")
925TextLabel906 = Instance.new("TextLabel")
926TextLabel907 = Instance.new("TextLabel")
927Frame908 = Instance.new("Frame")
928Frame909 = Instance.new("Frame")
929Frame910 = Instance.new("Frame")
930TextLabel911 = Instance.new("TextLabel")
931TextLabel912 = Instance.new("TextLabel")
932Frame913 = Instance.new("Frame")
933Frame914 = Instance.new("Frame")
934Frame915 = Instance.new("Frame")
935TextLabel916 = Instance.new("TextLabel")
936TextLabel917 = Instance.new("TextLabel")
937Frame918 = Instance.new("Frame")
938Frame919 = Instance.new("Frame")
939Frame920 = Instance.new("Frame")
940TextLabel921 = Instance.new("TextLabel")
941TextLabel922 = Instance.new("TextLabel")
942Frame923 = Instance.new("Frame")
943TextLabel924 = Instance.new("TextLabel")
944ImageButton925 = Instance.new("ImageButton")
945Frame926 = Instance.new("Frame")
946TextLabel927 = Instance.new("TextLabel")
947Frame928 = Instance.new("Frame")
948Frame929 = Instance.new("Frame")
949ImageButton930 = Instance.new("ImageButton")
950Frame931 = Instance.new("Frame")
951TextLabel932 = Instance.new("TextLabel")
952ImageButton933 = Instance.new("ImageButton")
953Frame934 = Instance.new("Frame")
954TextLabel935 = Instance.new("TextLabel")
955ImageButton936 = Instance.new("ImageButton")
956Frame937 = Instance.new("Frame")
957TextLabel938 = Instance.new("TextLabel")
958ImageButton939 = Instance.new("ImageButton")
959Frame940 = Instance.new("Frame")
960TextLabel941 = Instance.new("TextLabel")
961ImageButton942 = Instance.new("ImageButton")
962Frame943 = Instance.new("Frame")
963TextLabel944 = Instance.new("TextLabel")
964Frame945 = Instance.new("Frame")
965ImageButton946 = Instance.new("ImageButton")
966TextLabel947 = Instance.new("TextLabel")
967ImageButton948 = Instance.new("ImageButton")
968TextLabel949 = Instance.new("TextLabel")
969ImageButton950 = Instance.new("ImageButton")
970TextLabel951 = Instance.new("TextLabel")
971ImageButton952 = Instance.new("ImageButton")
972TextLabel953 = Instance.new("TextLabel")
973ImageButton954 = Instance.new("ImageButton")
974TextLabel955 = Instance.new("TextLabel")
975ImageButton956 = Instance.new("ImageButton")
976TextLabel957 = Instance.new("TextLabel")
977ImageButton958 = Instance.new("ImageButton")
978TextLabel959 = Instance.new("TextLabel")
979ImageButton960 = Instance.new("ImageButton")
980TextLabel961 = Instance.new("TextLabel")
981ImageButton962 = Instance.new("ImageButton")
982TextLabel963 = Instance.new("TextLabel")
983ImageButton964 = Instance.new("ImageButton")
984TextLabel965 = Instance.new("TextLabel")
985ImageButton966 = Instance.new("ImageButton")
986TextLabel967 = Instance.new("TextLabel")
987ImageButton968 = Instance.new("ImageButton")
988TextLabel969 = Instance.new("TextLabel")
989ImageButton970 = Instance.new("ImageButton")
990TextLabel971 = Instance.new("TextLabel")
991ImageButton972 = Instance.new("ImageButton")
992TextLabel973 = Instance.new("TextLabel")
993Frame974 = Instance.new("Frame")
994Frame975 = Instance.new("Frame")
995Frame976 = Instance.new("Frame")
996Frame977 = Instance.new("Frame")
997Frame978 = Instance.new("Frame")
998Frame979 = Instance.new("Frame")
999Frame980 = Instance.new("Frame")
1000Frame981 = Instance.new("Frame")
1001TextLabel982 = Instance.new("TextLabel")
1002Frame983 = Instance.new("Frame")
1003Frame984 = Instance.new("Frame")
1004Frame985 = Instance.new("Frame")
1005Frame986 = Instance.new("Frame")
1006Frame987 = Instance.new("Frame")
1007Frame988 = Instance.new("Frame")
1008TextLabel989 = Instance.new("TextLabel")
1009Frame990 = Instance.new("Frame")
1010TextLabel991 = Instance.new("TextLabel")
1011TextLabel992 = Instance.new("TextLabel")
1012Frame993 = Instance.new("Frame")
1013TextLabel994 = Instance.new("TextLabel")
1014TextLabel995 = Instance.new("TextLabel")
1015Tool996 = Instance.new("Tool")
1016Part997 = Instance.new("Part")
1017SpecialMesh998 = Instance.new("SpecialMesh")
1018Fire999 = Instance.new("Fire")
1019LocalScript1000 = Instance.new("LocalScript")
1020Script1001 = Instance.new("Script")
1021HopperBin1002 = Instance.new("HopperBin")
1022LocalScript1003 = Instance.new("LocalScript")
1023ScreenGui1004 = Instance.new("ScreenGui")
1024Frame1005 = Instance.new("Frame")
1025TextLabel1006 = Instance.new("TextLabel")
1026TextLabel1007 = Instance.new("TextLabel")
1027Frame1008 = Instance.new("Frame")
1028TextLabel1009 = Instance.new("TextLabel")
1029TextButton1010 = Instance.new("TextButton")
1030TextButton1011 = Instance.new("TextButton")
1031TextButton1012 = Instance.new("TextButton")
1032TextButton1013 = Instance.new("TextButton")
1033TextButton1014 = Instance.new("TextButton")
1034TextButton1015 = Instance.new("TextButton")
1035Frame1016 = Instance.new("Frame")
1036TextLabel1017 = Instance.new("TextLabel")
1037TextButton1018 = Instance.new("TextButton")
1038TextButton1019 = Instance.new("TextButton")
1039TextButton1020 = Instance.new("TextButton")
1040TextButton1021 = Instance.new("TextButton")
1041TextButton1022 = Instance.new("TextButton")
1042TextButton1023 = Instance.new("TextButton")
1043Frame1024 = Instance.new("Frame")
1044TextLabel1025 = Instance.new("TextLabel")
1045TextButton1026 = Instance.new("TextButton")
1046TextButton1027 = Instance.new("TextButton")
1047TextButton1028 = Instance.new("TextButton")
1048TextButton1029 = Instance.new("TextButton")
1049TextButton1030 = Instance.new("TextButton")
1050TextButton1031 = Instance.new("TextButton")
1051Frame1032 = Instance.new("Frame")
1052TextLabel1033 = Instance.new("TextLabel")
1053TextButton1034 = Instance.new("TextButton")
1054TextButton1035 = Instance.new("TextButton")
1055TextButton1036 = Instance.new("TextButton")
1056TextButton1037 = Instance.new("TextButton")
1057Frame1038 = Instance.new("Frame")
1058TextLabel1039 = Instance.new("TextLabel")
1059TextButton1040 = Instance.new("TextButton")
1060TextButton1041 = Instance.new("TextButton")
1061TextButton1042 = Instance.new("TextButton")
1062TextButton1043 = Instance.new("TextButton")
1063Frame1044 = Instance.new("Frame")
1064TextLabel1045 = Instance.new("TextLabel")
1065TextButton1046 = Instance.new("TextButton")
1066TextButton1047 = Instance.new("TextButton")
1067TextButton1048 = Instance.new("TextButton")
1068TextButton1049 = Instance.new("TextButton")
1069TextLabel1050 = Instance.new("TextLabel")
1070Frame1051 = Instance.new("Frame")
1071TextLabel1052 = Instance.new("TextLabel")
1072TextButton1053 = Instance.new("TextButton")
1073TextButton1054 = Instance.new("TextButton")
1074TextButton1055 = Instance.new("TextButton")
1075TextButton1056 = Instance.new("TextButton")
1076Script1057 = Instance.new("Script")
1077HopperBin1058 = Instance.new("HopperBin")
1078Handles1059 = Instance.new("Handles")
1079SelectionBox1060 = Instance.new("SelectionBox")
1080LocalScript1061 = Instance.new("LocalScript")
1081Script1062 = Instance.new("Script")
1082HopperBin1063 = Instance.new("HopperBin")
1083BoolValue1064 = Instance.new("BoolValue")
1084StringValue1065 = Instance.new("StringValue")
1085BoolValue1066 = Instance.new("BoolValue")
1086StringValue1067 = Instance.new("StringValue")
1087BoolValue1068 = Instance.new("BoolValue")
1088SelectionBox1069 = Instance.new("SelectionBox")
1089Script1070 = Instance.new("Script")
1090ScreenGui1071 = Instance.new("ScreenGui")
1091Frame1072 = Instance.new("Frame")
1092TextLabel1073 = Instance.new("TextLabel")
1093TextButton1074 = Instance.new("TextButton")
1094TextButton1075 = Instance.new("TextButton")
1095BoolValue1076 = Instance.new("BoolValue")
1096TextLabel1077 = Instance.new("TextLabel")
1097TextButton1078 = Instance.new("TextButton")
1098TextButton1079 = Instance.new("TextButton")
1099TextButton1080 = Instance.new("TextButton")
1100TextButton1081 = Instance.new("TextButton")
1101TextBox1082 = Instance.new("TextBox")
1102TextLabel1083 = Instance.new("TextLabel")
1103TextLabel1084 = Instance.new("TextLabel")
1104TextButton1085 = Instance.new("TextButton")
1105TextLabel1086 = Instance.new("TextLabel")
1106TextLabel1087 = Instance.new("TextLabel")
1107LocalScript1088 = Instance.new("LocalScript")
1108TextButton1089 = Instance.new("TextButton")
1109Frame1090 = Instance.new("Frame")
1110TextLabel1091 = Instance.new("TextLabel")
1111TextLabel1092 = Instance.new("TextLabel")
1112TextLabel1093 = Instance.new("TextLabel")
1113TextButton1094 = Instance.new("TextButton")
1114Script1095 = Instance.new("Script")
1115TextButton1096 = Instance.new("TextButton")
1116Script1097 = Instance.new("Script")
1117LocalScript1098 = Instance.new("LocalScript")
1118Frame1099 = Instance.new("Frame")
1119TextButton1100 = Instance.new("TextButton")
1120BoolValue1101 = Instance.new("BoolValue")
1121TextBox1102 = Instance.new("TextBox")
1122TextButton1103 = Instance.new("TextButton")
1123TextLabel1104 = Instance.new("TextLabel")
1124TextLabel1105 = Instance.new("TextLabel")
1125TextLabel1106 = Instance.new("TextLabel")
1126TextButton1107 = Instance.new("TextButton")
1127TextLabel1108 = Instance.new("TextLabel")
1128TextBox1109 = Instance.new("TextBox")
1129Frame1110 = Instance.new("Frame")
1130TextLabel1111 = Instance.new("TextLabel")
1131TextLabel1112 = Instance.new("TextLabel")
1132TextButton1113 = Instance.new("TextButton")
1133Script1114 = Instance.new("Script")
1134TextButton1115 = Instance.new("TextButton")
1135Script1116 = Instance.new("Script")
1136ObjectValue1117 = Instance.new("ObjectValue")
1137HopperBin1118 = Instance.new("HopperBin")
1138LocalScript1119 = Instance.new("LocalScript")
1139LocalScript1120 = Instance.new("LocalScript")
1140Model1121 = Instance.new("Model")
1141Model1122 = Instance.new("Model")
1142StringValue1123 = Instance.new("StringValue")
1143Color3Value1124 = Instance.new("Color3Value")
1144StringValue1125 = Instance.new("StringValue")
1145StringValue1126 = Instance.new("StringValue")
1146LocalScript1127 = Instance.new("LocalScript")
1147StringValue1128 = Instance.new("StringValue")
1148LocalScript1129 = Instance.new("LocalScript")
1149LocalScript1130 = Instance.new("LocalScript")
1150StringValue1131 = Instance.new("StringValue")
1151StringValue1132 = Instance.new("StringValue")
1152LocalScript1133 = Instance.new("LocalScript")
1153StringValue1134 = Instance.new("StringValue")
1154Color3Value1135 = Instance.new("Color3Value")
1155StringValue1136 = Instance.new("StringValue")
1156StringValue1137 = Instance.new("StringValue")
1157LocalScript1138 = Instance.new("LocalScript")
1158LocalScript1139 = Instance.new("LocalScript")
1159LocalScript1140 = Instance.new("LocalScript")
1160StringValue1141 = Instance.new("StringValue")
1161Color3Value1142 = Instance.new("Color3Value")
1162StringValue1143 = Instance.new("StringValue")
1163StringValue1144 = Instance.new("StringValue")
1164StringValue1145 = Instance.new("StringValue")
1165Color3Value1146 = Instance.new("Color3Value")
1166StringValue1147 = Instance.new("StringValue")
1167StringValue1148 = Instance.new("StringValue")
1168LocalScript1149 = Instance.new("LocalScript")
1169StringValue1150 = Instance.new("StringValue")
1170Color3Value1151 = Instance.new("Color3Value")
1171StringValue1152 = Instance.new("StringValue")
1172StringValue1153 = Instance.new("StringValue")
1173LocalScript1154 = Instance.new("LocalScript")
1174StringValue1155 = Instance.new("StringValue")
1175LocalScript1156 = Instance.new("LocalScript")
1176StringValue1157 = Instance.new("StringValue")
1177LocalScript1158 = Instance.new("LocalScript")
1178StringValue1159 = Instance.new("StringValue")
1179StringValue1160 = Instance.new("StringValue")
1180Color3Value1161 = Instance.new("Color3Value")
1181StringValue1162 = Instance.new("StringValue")
1182StringValue1163 = Instance.new("StringValue")
1183LocalScript1164 = Instance.new("LocalScript")
1184LocalScript1165 = Instance.new("LocalScript")
1185StringValue1166 = Instance.new("StringValue")
1186Color3Value1167 = Instance.new("Color3Value")
1187StringValue1168 = Instance.new("StringValue")
1188StringValue1169 = Instance.new("StringValue")
1189LocalScript1170 = Instance.new("LocalScript")
1190Model1171 = Instance.new("Model")
1191LocalScript1172 = Instance.new("LocalScript")
1192StringValue1173 = Instance.new("StringValue")
1193LocalScript1174 = Instance.new("LocalScript")
1194StringValue1175 = Instance.new("StringValue")
1195LocalScript1176 = Instance.new("LocalScript")
1196StringValue1177 = Instance.new("StringValue")
1197StringValue1178 = Instance.new("StringValue")
1198LocalScript1179 = Instance.new("LocalScript")
1199LocalScript1180 = Instance.new("LocalScript")
1200StringValue1181 = Instance.new("StringValue")
1201StringValue1182 = Instance.new("StringValue")
1202LocalScript1183 = Instance.new("LocalScript")
1203StringValue1184 = Instance.new("StringValue")
1204LocalScript1185 = Instance.new("LocalScript")
1205LocalScript1186 = Instance.new("LocalScript")
1206StringValue1187 = Instance.new("StringValue")
1207StringValue1188 = Instance.new("StringValue")
1208LocalScript1189 = Instance.new("LocalScript")
1209StringValue1190 = Instance.new("StringValue")
1210LocalScript1191 = Instance.new("LocalScript")
1211StringValue1192 = Instance.new("StringValue")
1212Color3Value1193 = Instance.new("Color3Value")
1213StringValue1194 = Instance.new("StringValue")
1214StringValue1195 = Instance.new("StringValue")
1215LocalScript1196 = Instance.new("LocalScript")
1216StringValue1197 = Instance.new("StringValue")
1217LocalScript1198 = Instance.new("LocalScript")
1218StringValue1199 = Instance.new("StringValue")
1219Color3Value1200 = Instance.new("Color3Value")
1220StringValue1201 = Instance.new("StringValue")
1221StringValue1202 = Instance.new("StringValue")
1222LocalScript1203 = Instance.new("LocalScript")
1223StringValue1204 = Instance.new("StringValue")
1224LocalScript1205 = Instance.new("LocalScript")
1225StringValue1206 = Instance.new("StringValue")
1226StringValue1207 = Instance.new("StringValue")
1227Color3Value1208 = Instance.new("Color3Value")
1228StringValue1209 = Instance.new("StringValue")
1229StringValue1210 = Instance.new("StringValue")
1230LocalScript1211 = Instance.new("LocalScript")
1231LocalScript1212 = Instance.new("LocalScript")
1232StringValue1213 = Instance.new("StringValue")
1233StringValue1214 = Instance.new("StringValue")
1234Color3Value1215 = Instance.new("Color3Value")
1235StringValue1216 = Instance.new("StringValue")
1236StringValue1217 = Instance.new("StringValue")
1237LocalScript1218 = Instance.new("LocalScript")
1238LocalScript1219 = Instance.new("LocalScript")
1239StringValue1220 = Instance.new("StringValue")
1240StringValue1221 = Instance.new("StringValue")
1241LocalScript1222 = Instance.new("LocalScript")
1242StringValue1223 = Instance.new("StringValue")
1243Color3Value1224 = Instance.new("Color3Value")
1244StringValue1225 = Instance.new("StringValue")
1245StringValue1226 = Instance.new("StringValue")
1246LocalScript1227 = Instance.new("LocalScript")
1247LocalScript1228 = Instance.new("LocalScript")
1248StringValue1229 = Instance.new("StringValue")
1249LocalScript1230 = Instance.new("LocalScript")
1250StringValue1231 = Instance.new("StringValue")
1251LocalScript1232 = Instance.new("LocalScript")
1252StringValue1233 = Instance.new("StringValue")
1253Color3Value1234 = Instance.new("Color3Value")
1254StringValue1235 = Instance.new("StringValue")
1255StringValue1236 = Instance.new("StringValue")
1256LocalScript1237 = Instance.new("LocalScript")
1257StringValue1238 = Instance.new("StringValue")
1258LocalScript1239 = Instance.new("LocalScript")
1259StringValue1240 = Instance.new("StringValue")
1260Color3Value1241 = Instance.new("Color3Value")
1261StringValue1242 = Instance.new("StringValue")
1262StringValue1243 = Instance.new("StringValue")
1263LocalScript1244 = Instance.new("LocalScript")
1264StringValue1245 = Instance.new("StringValue")
1265LocalScript1246 = Instance.new("LocalScript")
1266StringValue1247 = Instance.new("StringValue")
1267StringValue1248 = Instance.new("StringValue")
1268LocalScript1249 = Instance.new("LocalScript")
1269StringValue1250 = Instance.new("StringValue")
1270StringValue1251 = Instance.new("StringValue")
1271Color3Value1252 = Instance.new("Color3Value")
1272LocalScript1253 = Instance.new("LocalScript")
1273StringValue1254 = Instance.new("StringValue")
1274Color3Value1255 = Instance.new("Color3Value")
1275StringValue1256 = Instance.new("StringValue")
1276StringValue1257 = Instance.new("StringValue")
1277LocalScript1258 = Instance.new("LocalScript")
1278StringValue1259 = Instance.new("StringValue")
1279LocalScript1260 = Instance.new("LocalScript")
1280StringValue1261 = Instance.new("StringValue")
1281StringValue1262 = Instance.new("StringValue")
1282Color3Value1263 = Instance.new("Color3Value")
1283StringValue1264 = Instance.new("StringValue")
1284StringValue1265 = Instance.new("StringValue")
1285LocalScript1266 = Instance.new("LocalScript")
1286LocalScript1267 = Instance.new("LocalScript")
1287StringValue1268 = Instance.new("StringValue")
1288StringValue1269 = Instance.new("StringValue")
1289Color3Value1270 = Instance.new("Color3Value")
1290StringValue1271 = Instance.new("StringValue")
1291StringValue1272 = Instance.new("StringValue")
1292LocalScript1273 = Instance.new("LocalScript")
1293StringValue1274 = Instance.new("StringValue")
1294Color3Value1275 = Instance.new("Color3Value")
1295StringValue1276 = Instance.new("StringValue")
1296StringValue1277 = Instance.new("StringValue")
1297LocalScript1278 = Instance.new("LocalScript")
1298StringValue1279 = Instance.new("StringValue")
1299LocalScript1280 = Instance.new("LocalScript")
1300StringValue1281 = Instance.new("StringValue")
1301StringValue1282 = Instance.new("StringValue")
1302Color3Value1283 = Instance.new("Color3Value")
1303StringValue1284 = Instance.new("StringValue")
1304StringValue1285 = Instance.new("StringValue")
1305LocalScript1286 = Instance.new("LocalScript")
1306StringValue1287 = Instance.new("StringValue")
1307LocalScript1288 = Instance.new("LocalScript")
1308StringValue1289 = Instance.new("StringValue")
1309LocalScript1290 = Instance.new("LocalScript")
1310LocalScript1291 = Instance.new("LocalScript")
1311StringValue1292 = Instance.new("StringValue")
1312StringValue1293 = Instance.new("StringValue")
1313Color3Value1294 = Instance.new("Color3Value")
1314StringValue1295 = Instance.new("StringValue")
1315StringValue1296 = Instance.new("StringValue")
1316LocalScript1297 = Instance.new("LocalScript")
1317LocalScript1298 = Instance.new("LocalScript")
1318LocalScript1299 = Instance.new("LocalScript")
1319Model1300 = Instance.new("Model")
1320LocalScript1301 = Instance.new("LocalScript")
1321StringValue1302 = Instance.new("StringValue")
1322LocalScript1303 = Instance.new("LocalScript")
1323LocalScript1304 = Instance.new("LocalScript")
1324StringValue1305 = Instance.new("StringValue")
1325StringValue1306 = Instance.new("StringValue")
1326LocalScript1307 = Instance.new("LocalScript")
1327StringValue1308 = Instance.new("StringValue")
1328StringValue1309 = Instance.new("StringValue")
1329StringValue1310 = Instance.new("StringValue")
1330LocalScript1311 = Instance.new("LocalScript")
1331StringValue1312 = Instance.new("StringValue")
1332LocalScript1313 = Instance.new("LocalScript")
1333LocalScript1314 = Instance.new("LocalScript")
1334LocalScript1315 = Instance.new("LocalScript")
1335LocalScript1316 = Instance.new("LocalScript")
1336StringValue1317 = Instance.new("StringValue")
1337LocalScript1318 = Instance.new("LocalScript")
1338StringValue1319 = Instance.new("StringValue")
1339StringValue1320 = Instance.new("StringValue")
1340LocalScript1321 = Instance.new("LocalScript")
1341LocalScript1322 = Instance.new("LocalScript")
1342StringValue1323 = Instance.new("StringValue")
1343StringValue1324 = Instance.new("StringValue")
1344StringValue1325 = Instance.new("StringValue")
1345LocalScript1326 = Instance.new("LocalScript")
1346StringValue1327 = Instance.new("StringValue")
1347LocalScript1328 = Instance.new("LocalScript")
1348StringValue1329 = Instance.new("StringValue")
1349LocalScript1330 = Instance.new("LocalScript")
1350LocalScript1331 = Instance.new("LocalScript")
1351HopperBin1332 = Instance.new("HopperBin")
1352Script1333 = Instance.new("Script")
1353HopperBin1334 = Instance.new("HopperBin")
1354Script1335 = Instance.new("Script")
1355HopperBin1336 = Instance.new("HopperBin")
1356Script1337 = Instance.new("Script")
1357HopperBin1338 = Instance.new("HopperBin")
1358ScreenGui1339 = Instance.new("ScreenGui")
1359Frame1340 = Instance.new("Frame")
1360TextLabel1341 = Instance.new("TextLabel")
1361TextLabel1342 = Instance.new("TextLabel")
1362Frame1343 = Instance.new("Frame")
1363TextLabel1344 = Instance.new("TextLabel")
1364TextButton1345 = Instance.new("TextButton")
1365TextButton1346 = Instance.new("TextButton")
1366TextButton1347 = Instance.new("TextButton")
1367TextButton1348 = Instance.new("TextButton")
1368TextButton1349 = Instance.new("TextButton")
1369TextButton1350 = Instance.new("TextButton")
1370Frame1351 = Instance.new("Frame")
1371TextLabel1352 = Instance.new("TextLabel")
1372TextButton1353 = Instance.new("TextButton")
1373TextButton1354 = Instance.new("TextButton")
1374TextButton1355 = Instance.new("TextButton")
1375TextButton1356 = Instance.new("TextButton")
1376TextButton1357 = Instance.new("TextButton")
1377TextButton1358 = Instance.new("TextButton")
1378Frame1359 = Instance.new("Frame")
1379TextLabel1360 = Instance.new("TextLabel")
1380TextButton1361 = Instance.new("TextButton")
1381TextButton1362 = Instance.new("TextButton")
1382TextButton1363 = Instance.new("TextButton")
1383TextButton1364 = Instance.new("TextButton")
1384TextButton1365 = Instance.new("TextButton")
1385TextButton1366 = Instance.new("TextButton")
1386Frame1367 = Instance.new("Frame")
1387TextLabel1368 = Instance.new("TextLabel")
1388TextButton1369 = Instance.new("TextButton")
1389TextButton1370 = Instance.new("TextButton")
1390TextButton1371 = Instance.new("TextButton")
1391TextButton1372 = Instance.new("TextButton")
1392Frame1373 = Instance.new("Frame")
1393TextLabel1374 = Instance.new("TextLabel")
1394TextButton1375 = Instance.new("TextButton")
1395TextButton1376 = Instance.new("TextButton")
1396TextButton1377 = Instance.new("TextButton")
1397TextButton1378 = Instance.new("TextButton")
1398Frame1379 = Instance.new("Frame")
1399TextLabel1380 = Instance.new("TextLabel")
1400TextButton1381 = Instance.new("TextButton")
1401TextButton1382 = Instance.new("TextButton")
1402TextButton1383 = Instance.new("TextButton")
1403TextButton1384 = Instance.new("TextButton")
1404TextLabel1385 = Instance.new("TextLabel")
1405Frame1386 = Instance.new("Frame")
1406TextLabel1387 = Instance.new("TextLabel")
1407TextButton1388 = Instance.new("TextButton")
1408TextButton1389 = Instance.new("TextButton")
1409TextButton1390 = Instance.new("TextButton")
1410TextButton1391 = Instance.new("TextButton")
1411LocalScript1392 = Instance.new("LocalScript")
1412Script1393 = Instance.new("Script")
1413HopperBin1394 = Instance.new("HopperBin")
1414Script1395 = Instance.new("Script")
1415Script1396 = Instance.new("Script")
1416Tool0.Name = "Building Tools"
1417Tool0.Parent = mas
1418Tool0.ToolTip = "Building Tools by F3X"
1419LocalScript1.Name = "Building Tools by F3X"
1420LocalScript1.Parent = Tool0
1421table.insert(cors,sandbox(LocalScript1,function()
1422------------------------------------------
1423-- Create references to important objects
1424------------------------------------------
1425Services = {
1426 ["Workspace"] = Game:GetService( "Workspace" );
1427 ["Players"] = Game:GetService( "Players" );
1428 ["Lighting"] = Game:GetService( "Lighting" );
1429 ["Teams"] = Game:GetService( "Teams" );
1430 ["Debris"] = Game:GetService( "Debris" );
1431 ["MarketplaceService"] = Game:GetService( "MarketplaceService" );
1432 ["JointsService"] = Game.JointsService;
1433 ["BadgeService"] = Game:GetService( "BadgeService" );
1434 ["RunService"] = Game:GetService( "RunService" );
1435 ["ContentProvider"] = Game:GetService( "ContentProvider" );
1436 ["TeleportService"] = Game:GetService( "TeleportService" );
1437 ["SoundService"] = Game:GetService( "SoundService" );
1438 ["InsertService"] = Game:GetService( "InsertService" );
1439 ["CollectionService"] = Game:GetService( "CollectionService" );
1440 ["UserInputService"] = Game:GetService( "UserInputService" );
1441 ["GamePassService"] = Game:GetService( "GamePassService" );
1442 ["StarterPack"] = Game:GetService( "StarterPack" );
1443 ["StarterGui"] = Game:GetService( "StarterGui" );
1444 ["TestService"] = Game:GetService( "TestService" );
1445 ["ReplicatedStorage"] = Game:GetService( "ReplicatedStorage" );
1446 ["Selection"] = Game:GetService( "Selection" );
1447 ["CoreGui"] = Game:GetService( "CoreGui" );
1448};
1449
1450Tool = script.Parent;
1451Player = Services.Players.LocalPlayer;
1452Mouse = nil;
1453
1454-- Determine whether this is the plugin or tool
1455if plugin then
1456 ToolType = 'plugin';
1457elseif Tool:IsA( 'Tool' ) then
1458 ToolType = 'tool';
1459end;
1460
1461-- Get tool type-specific resources
1462if ToolType == 'tool' then
1463 GUIContainer = Player:WaitForChild( 'PlayerGui' );
1464 in_server = not not Game:FindFirstChild( 'NetworkClient' );
1465elseif ToolType == 'plugin' then
1466 GUIContainer = Services.CoreGui;
1467 in_server = not not Game:FindFirstChild( 'NetworkServer' );
1468end;
1469if in_server then
1470 Tool:WaitForChild( "GetAsync" );
1471 Tool:WaitForChild( "PostAsync" );
1472 GetAsync = function ( ... )
1473 return Tool.GetAsync:InvokeServer( ... );
1474 end;
1475 PostAsync = function ( ... )
1476 return Tool.PostAsync:InvokeServer( ... );
1477 end;
1478end;
1479
1480dark_slanted_rectangle = "http://www.roblox.com/asset/?id=127774197";
1481light_slanted_rectangle = "http://www.roblox.com/asset/?id=127772502";
1482action_completion_sound = "http://www.roblox.com/asset/?id=99666917";
1483expand_arrow = "http://www.roblox.com/asset/?id=134367382";
1484tool_decal = "http://www.roblox.com/asset/?id=129748355";
1485undo_active_decal = "http://www.roblox.com/asset/?id=141741408";
1486undo_inactive_decal = "http://www.roblox.com/asset/?id=142074557";
1487redo_active_decal = "http://www.roblox.com/asset/?id=141741327";
1488redo_inactive_decal = "http://www.roblox.com/asset/?id=142074553";
1489delete_active_decal = "http://www.roblox.com/asset/?id=141896298";
1490delete_inactive_decal = "http://www.roblox.com/asset/?id=142074644";
1491export_active_decal = "http://www.roblox.com/asset/?id=141741337";
1492export_inactive_decal = "http://www.roblox.com/asset/?id=142074569";
1493clone_active_decal = "http://www.roblox.com/asset/?id=142073926";
1494clone_inactive_decal = "http://www.roblox.com/asset/?id=142074563";
1495plugin_icon = "http://www.roblox.com/asset/?id=142287521";
1496
1497------------------------------------------
1498-- Load external dependencies
1499------------------------------------------
1500RbxUtility = LoadLibrary( "RbxUtility" );
1501Services.ContentProvider:Preload( dark_slanted_rectangle );
1502Services.ContentProvider:Preload( light_slanted_rectangle );
1503Services.ContentProvider:Preload( action_completion_sound );
1504Services.ContentProvider:Preload( expand_arrow );
1505Services.ContentProvider:Preload( tool_decal );
1506Services.ContentProvider:Preload( undo_active_decal );
1507Services.ContentProvider:Preload( undo_inactive_decal );
1508Services.ContentProvider:Preload( redo_inactive_decal );
1509Services.ContentProvider:Preload( redo_active_decal );
1510Services.ContentProvider:Preload( delete_active_decal );
1511Services.ContentProvider:Preload( delete_inactive_decal );
1512Services.ContentProvider:Preload( export_active_decal );
1513Services.ContentProvider:Preload( export_inactive_decal );
1514Services.ContentProvider:Preload( clone_active_decal );
1515Services.ContentProvider:Preload( clone_inactive_decal );
1516Services.ContentProvider:Preload( plugin_icon );
1517Tool:WaitForChild( "Interfaces" );
1518repeat wait( 0 ) until _G.gloo;
1519Gloo = _G.gloo;
1520
1521------------------------------------------
1522-- Define functions that are depended-upon
1523------------------------------------------
1524function _findTableOccurrences( haystack, needle )
1525 -- Returns the positions of instances of `needle` in table `haystack`
1526 local positions = {};
1527
1528 -- Add any indexes from `haystack` that have `needle`
1529 for index, value in pairs( haystack ) do
1530 if value == needle then
1531 table.insert( positions, index );
1532 end;
1533 end;
1534
1535 return positions;
1536end;
1537
1538function _getCollectionInfo( part_collection )
1539 -- Returns the size and position of collection of parts `part_collection`
1540
1541 -- Get the corners
1542 local corners = {};
1543
1544 -- Create shortcuts to certain things that are expensive to call constantly
1545 -- (note: otherwise it actually becomes an issue if the selection grows
1546 -- considerably large)
1547 local table_insert = table.insert;
1548 local newCFrame = CFrame.new;
1549
1550 for _, Part in pairs( part_collection ) do
1551
1552 local PartCFrame = Part.CFrame;
1553 local partCFrameOffset = PartCFrame.toWorldSpace;
1554 local PartSize = Part.Size / 2;
1555 local size_x, size_y, size_z = PartSize.x, PartSize.y, PartSize.z;
1556
1557 table_insert( corners, partCFrameOffset( PartCFrame, newCFrame( size_x, size_y, size_z ) ) );
1558 table_insert( corners, partCFrameOffset( PartCFrame, newCFrame( -size_x, size_y, size_z ) ) );
1559 table_insert( corners, partCFrameOffset( PartCFrame, newCFrame( size_x, -size_y, size_z ) ) );
1560 table_insert( corners, partCFrameOffset( PartCFrame, newCFrame( size_x, size_y, -size_z ) ) );
1561 table_insert( corners, partCFrameOffset( PartCFrame, newCFrame( -size_x, size_y, -size_z ) ) );
1562 table_insert( corners, partCFrameOffset( PartCFrame, newCFrame( -size_x, -size_y, size_z ) ) );
1563 table_insert( corners, partCFrameOffset( PartCFrame, newCFrame( size_x, -size_y, -size_z ) ) );
1564 table_insert( corners, partCFrameOffset( PartCFrame, newCFrame( -size_x, -size_y, -size_z ) ) );
1565
1566 end;
1567
1568 -- Get the extents
1569 local x, y, z = {}, {}, {};
1570
1571 for _, Corner in pairs( corners ) do
1572 table_insert( x, Corner.x );
1573 table_insert( y, Corner.y );
1574 table_insert( z, Corner.z );
1575 end;
1576
1577 local x_min, y_min, z_min = math.min( unpack( x ) ),
1578 math.min( unpack( y ) ),
1579 math.min( unpack( z ) );
1580
1581 local x_max, y_max, z_max = math.max( unpack( x ) ),
1582 math.max( unpack( y ) ),
1583 math.max( unpack( z ) );
1584
1585 -- Get the size between the extents
1586 local x_size, y_size, z_size = x_max - x_min,
1587 y_max - y_min,
1588 z_max - z_min;
1589
1590 local Size = Vector3.new( x_size, y_size, z_size );
1591
1592 -- Get the centroid of the collection of points
1593 local Position = CFrame.new( x_min + ( x_max - x_min ) / 2,
1594 y_min + ( y_max - y_min ) / 2,
1595 z_min + ( z_max - z_min ) / 2 );
1596
1597 -- Return the size of the collection of parts
1598 return Size, Position;
1599end;
1600
1601function _round( number, places )
1602 -- Returns `number` rounded to the number of decimal `places`
1603 -- (from lua-users)
1604
1605 local mult = 10 ^ ( places or 0 );
1606
1607 return math.floor( number * mult + 0.5 ) / mult;
1608
1609end
1610
1611function _cloneTable( source )
1612 -- Returns a deep copy of table `source`
1613
1614 -- Get a copy of `source`'s metatable, since the hacky method
1615 -- we're using to copy the table doesn't include its metatable
1616 local source_mt = getmetatable( source );
1617
1618 -- Return a copy of `source` including its metatable
1619 return setmetatable( { unpack( source ) }, source_mt );
1620end;
1621
1622function _getAllDescendants( Parent )
1623 -- Recursively gets all the descendants of `Parent` and returns them
1624
1625 local descendants = {};
1626
1627 for _, Child in pairs( Parent:GetChildren() ) do
1628
1629 -- Add the direct descendants of `Parent`
1630 table.insert( descendants, Child );
1631
1632 -- Add the descendants of each child
1633 for _, Subchild in pairs( _getAllDescendants( Child ) ) do
1634 table.insert( descendants, Subchild );
1635 end;
1636
1637 end;
1638
1639 return descendants;
1640
1641end;
1642
1643function _pointToScreenSpace( Point )
1644 -- Returns Vector3 `Point`'s position on the screen when rendered
1645 -- (kudos to stravant for this)
1646
1647 local point = Services.Workspace.CurrentCamera.CoordinateFrame:pointToObjectSpace( Point );
1648 local aspectRatio = Mouse.ViewSizeX / Mouse.ViewSizeY;
1649 local hfactor = math.tan( math.rad( Services.Workspace.CurrentCamera.FieldOfView ) / 2 )
1650 local wfactor = aspectRatio * hfactor;
1651
1652 local x = ( point.x / point.z ) / -wfactor;
1653 local y = ( point.y / point.z ) / hfactor;
1654
1655 local screen_pos = Vector2.new( Mouse.ViewSizeX * ( 0.5 + 0.5 * x ), Mouse.ViewSizeY * ( 0.5 + 0.5 * y ) );
1656 if ( screen_pos.x < 0 or screen_pos.x > Mouse.ViewSizeX ) or ( screen_pos.y < 0 or screen_pos.y > Mouse.ViewSizeY ) then
1657 return nil;
1658 end;
1659 if Services.Workspace.CurrentCamera.CoordinateFrame:toObjectSpace( CFrame.new( Point ) ).z > 0 then
1660 return nil;
1661 end;
1662
1663 return screen_pos;
1664
1665end;
1666
1667function _cloneParts( parts )
1668 -- Returns a table of cloned `parts`
1669
1670 local new_parts = {};
1671
1672 -- Copy the parts into `new_parts`
1673 for part_index, Part in pairs( parts ) do
1674 new_parts[part_index] = Part:Clone();
1675 end;
1676
1677 return new_parts;
1678end;
1679
1680function _replaceParts( old_parts, new_parts )
1681 -- Removes `old_parts` and inserts `new_parts`
1682
1683 -- Remove old parts
1684 for _, OldPart in pairs( old_parts ) do
1685 OldPart.Parent = nil;
1686 end;
1687
1688 -- Insert `new_parts
1689 for _, NewPart in pairs( new_parts ) do
1690 NewPart.Parent = Services.Workspace;
1691 NewPart:MakeJoints();
1692 end;
1693
1694end;
1695
1696function _splitString( str, delimiter )
1697 -- Returns a table of string `str` split by pattern `delimiter`
1698
1699 local parts = {};
1700 local pattern = ( "([^%s]+)" ):format( delimiter );
1701
1702 str:gsub( pattern, function ( part )
1703 table.insert( parts, part );
1704 end );
1705
1706 return parts;
1707end;
1708
1709function _generateSerializationID()
1710 -- Returns a random 5-character string
1711 -- with characters A-Z, a-z, and 0-9
1712 -- (there are 916,132,832 unique IDs)
1713
1714 local characters = {
1715 "0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
1716 "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z",
1717 "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z" };
1718
1719 local serialization_id = "";
1720
1721 -- Pick out 5 random characters
1722 for _ = 1, 5 do
1723 serialization_id = serialization_id .. ( characters[math.random( #characters )] );
1724 end;
1725
1726 return serialization_id;
1727end;
1728
1729function _splitNumberListString( str )
1730 -- Returns the contents of _splitString( str, ", " ), except
1731 -- each value in the table is turned into a number
1732
1733 -- Get the number strings
1734 local numbers = _splitString( str, ", " );
1735
1736 -- Turn them into numbers
1737 for number_index, number in pairs( numbers ) do
1738 numbers[number_index] = tonumber( number );
1739 end;
1740
1741 -- Return `numbers`
1742 return numbers;
1743end;
1744
1745function _getSerializationPartType( Part )
1746 -- Returns a special number that determines the type of
1747 -- part `Part` is
1748
1749 local Types = {
1750 Normal = 1,
1751 Truss = 2,
1752 Wedge = 3,
1753 Corner = 4,
1754 Cylinder = 5,
1755 Ball = 6,
1756 Seat = 7,
1757 VehicleSeat = 8,
1758 Spawn = 9
1759 };
1760
1761 -- Return the appropriate type number
1762 if Part.ClassName == "Part" then
1763 if Part.Shape == Enum.PartType.Block then
1764 return Types.Normal;
1765 elseif Part.Shape == Enum.PartType.Cylinder then
1766 return Types.Cylinder;
1767 elseif Part.Shape == Enum.PartType.Ball then
1768 return Types.Ball;
1769 end;
1770
1771 elseif Part.ClassName == "Seat" then
1772 return Types.Seat;
1773
1774 elseif Part.ClassName == "VehicleSeat" then
1775 return Types.VehicleSeat;
1776
1777 elseif Part.ClassName == "SpawnLocation" then
1778 return Types.Spawn;
1779
1780 elseif Part.ClassName == "WedgePart" then
1781 return Types.Wedge;
1782
1783 elseif Part.ClassName == "CornerWedgePart" then
1784 return Types.Corner;
1785
1786 elseif Part.ClassName == "TrussPart" then
1787 return Types.Truss;
1788
1789 end;
1790
1791end;
1792
1793function _serializeParts( parts )
1794 -- Returns JSON-encoded data about parts in
1795 -- table `parts` that can be used to recreate them
1796
1797 local data = {
1798 version = 1,
1799 parts = {}
1800 };
1801
1802 local objects = {};
1803
1804 -- Store part data
1805 for _, Part in pairs( parts ) do
1806 local part_id = _generateSerializationID();
1807 local PartData = {
1808 _getSerializationPartType( Part ),
1809 _splitNumberListString( tostring( Part.Size ) ),
1810 _splitNumberListString( tostring( Part.CFrame ) ),
1811 Part.BrickColor.Number,
1812 Part.Material.Value,
1813 Part.Anchored,
1814 Part.CanCollide,
1815 Part.Reflectance,
1816 Part.Transparency,
1817 Part.TopSurface.Value,
1818 Part.BottomSurface.Value,
1819 Part.LeftSurface.Value,
1820 Part.RightSurface.Value,
1821 Part.FrontSurface.Value,
1822 Part.BackSurface.Value
1823 };
1824 data.parts[part_id] = PartData;
1825 objects[part_id] = Part;
1826 end;
1827
1828 -- Get any welds in the selection
1829 local welds = {};
1830 for object_id, Object in pairs( objects ) do
1831 if Object:IsA( "BasePart" ) then
1832 for _, Joint in pairs( _getAllDescendants( Services.Workspace ) ) do
1833 if Joint:IsA( "Weld" ) and Joint.Name == "BTWeld" then
1834 if Joint.Part0 == Object and #_findTableOccurrences( objects, Joint.Part1 ) > 0 then
1835 table.insert( welds, Joint );
1836 end;
1837 end;
1838 end;
1839 end;
1840 end;
1841
1842 -- Serialize any welds
1843 if #welds > 0 then
1844 data.welds = {};
1845 for _, Weld in pairs( welds ) do
1846 local weld_id = _generateSerializationID();
1847 local WeldData = {
1848 _findTableOccurrences( objects, Weld.Part0 )[1],
1849 _findTableOccurrences( objects, Weld.Part1 )[1],
1850 _splitNumberListString( tostring( Weld.C1 ) )
1851 };
1852 data.welds[weld_id] = WeldData;
1853 objects[weld_id] = Weld;
1854 end;
1855 end;
1856
1857 -- Get any meshes in the selection
1858 local meshes = {};
1859 for _, Part in pairs( parts ) do
1860 local Mesh = _getChildOfClass( Part, "SpecialMesh" );
1861 if Mesh then
1862 table.insert( meshes, Mesh );
1863 end;
1864 end;
1865
1866 -- Serialize any meshes
1867 if #meshes > 0 then
1868 data.meshes = {};
1869 for _, Mesh in pairs( meshes ) do
1870 local mesh_id = _generateSerializationID();
1871 local MeshData = {
1872 _findTableOccurrences( objects, Mesh.Parent )[1],
1873 Mesh.MeshType.Value,
1874 _splitNumberListString( tostring( Mesh.Scale ) ),
1875 Mesh.MeshId,
1876 Mesh.TextureId,
1877 _splitNumberListString( tostring( Mesh.VertexColor ) )
1878 };
1879 data.meshes[mesh_id] = MeshData;
1880 objects[mesh_id] = Mesh;
1881 end;
1882 end;
1883
1884 -- Get any textures in the selection
1885 local textures = {};
1886 for _, Part in pairs( parts ) do
1887 local textures_found = _getChildrenOfClass( Part, "Texture" );
1888 for _, Texture in pairs( textures_found ) do
1889 table.insert( textures, Texture );
1890 end;
1891 local decals_found = _getChildrenOfClass( Part, "Decal" );
1892 for _, Decal in pairs( decals_found ) do
1893 table.insert( textures, Decal );
1894 end;
1895 end;
1896
1897 -- Serialize any textures
1898 if #textures > 0 then
1899 data.textures = {};
1900 for _, Texture in pairs( textures ) do
1901 local texture_type;
1902 if Texture.ClassName == "Decal" then
1903 texture_type = 1;
1904 elseif Texture.ClassName == "Texture" then
1905 texture_type = 2;
1906 end;
1907 local texture_id = _generateSerializationID();
1908 local TextureData = {
1909 _findTableOccurrences( objects, Texture.Parent )[1],
1910 texture_type,
1911 Texture.Face.Value,
1912 Texture.Texture,
1913 Texture.Transparency,
1914 texture_type == 2 and Texture.StudsPerTileU or nil,
1915 texture_type == 2 and Texture.StudsPerTileV or nil
1916 };
1917 data.textures[texture_id] = TextureData;
1918 objects[texture_id] = Texture;
1919 end;
1920 end;
1921
1922 -- Get any lights in the selection
1923 local lights = {};
1924 for _, Part in pairs( parts ) do
1925 local lights_found = _getChildrenOfClass( Part, "Light", true );
1926 for _, Light in pairs( lights_found ) do
1927 table.insert( lights, Light );
1928 end;
1929 end;
1930
1931 -- Serialize any lights
1932 if #lights > 0 then
1933 data.lights = {};
1934 for _, Light in pairs( lights ) do
1935 local light_type;
1936 if Light:IsA( "PointLight" ) then
1937 light_type = 1;
1938 elseif Light:IsA( "SpotLight" ) then
1939 light_type = 2;
1940 end;
1941 local light_id = _generateSerializationID();
1942 local LightData = {
1943 _findTableOccurrences( objects, Light.Parent )[1];
1944 light_type,
1945 _splitNumberListString( tostring( Light.Color ) ),
1946 Light.Brightness,
1947 Light.Range,
1948 Light.Shadows,
1949 light_type == 2 and Light.Angle or nil,
1950 light_type == 2 and Light.Face.Value or nil
1951 };
1952 data.lights[light_id] = LightData;
1953 objects[light_id] = Light;
1954 end;
1955 end;
1956
1957 -- Get any decorations in the selection
1958 local decorations = {};
1959 for _, Part in pairs( parts ) do
1960 table.insert( decorations, _getChildOfClass( Part, 'Smoke' ) )
1961 table.insert( decorations, _getChildOfClass( Part, 'Fire' ) );
1962 table.insert( decorations, _getChildOfClass( Part, 'Sparkles' ) );
1963 end;
1964
1965 -- Serialize any decorations
1966 if #decorations > 0 then
1967 data.decorations = {};
1968 for _, Decoration in pairs( decorations ) do
1969 local decoration_type;
1970 if Decoration:IsA( 'Smoke' ) then
1971 decoration_type = 1;
1972 elseif Decoration:IsA( 'Fire' ) then
1973 decoration_type = 2;
1974 elseif Decoration:IsA( 'Sparkles' ) then
1975 decoration_type = 3;
1976 end;
1977 local decoration_id = _generateSerializationID();
1978 local DecorationData = {
1979 _findTableOccurrences( objects, Decoration.Parent )[1],
1980 decoration_type
1981 };
1982 if decoration_type == 1 then
1983 DecorationData[3] = _splitNumberListString( tostring( Decoration.Color ) );
1984 DecorationData[4] = Decoration.Opacity;
1985 DecorationData[5] = Decoration.RiseVelocity;
1986 DecorationData[6] = Decoration.Size;
1987 elseif decoration_type == 2 then
1988 DecorationData[3] = _splitNumberListString( tostring( Decoration.Color ) );
1989 DecorationData[4] = _splitNumberListString( tostring( Decoration.SecondaryColor ) );
1990 DecorationData[5] = Decoration.Heat;
1991 DecorationData[6] = Decoration.Size;
1992 elseif decoration_type == 3 then
1993 DecorationData[3] = _splitNumberListString( tostring( Decoration.SparkleColor ) );
1994 end;
1995 data.decorations[decoration_id] = DecorationData;
1996 objects[decoration_id] = Decoration;
1997 end;
1998 end;
1999
2000 return RbxUtility.EncodeJSON( data );
2001
2002end;
2003
2004function _getChildOfClass( Parent, class_name, inherit )
2005 -- Returns the first child of `Parent` that is of class `class_name`
2006 -- or nil if it couldn't find any
2007
2008 -- Look for a child of `Parent` of class `class_name` and return it
2009 if not inherit then
2010 for _, Child in pairs( Parent:GetChildren() ) do
2011 if Child.ClassName == class_name then
2012 return Child;
2013 end;
2014 end;
2015 else
2016 for _, Child in pairs( Parent:GetChildren() ) do
2017 if Child:IsA( class_name ) then
2018 return Child;
2019 end;
2020 end;
2021 end;
2022
2023 return nil;
2024
2025end;
2026
2027function _getChildrenOfClass( Parent, class_name, inherit )
2028 -- Returns a table containing the children of `Parent` that are
2029 -- of class `class_name`
2030 local matches = {};
2031
2032
2033 if not inherit then
2034 for _, Child in pairs( Parent:GetChildren() ) do
2035 if Child.ClassName == class_name then
2036 table.insert( matches, Child );
2037 end;
2038 end;
2039 else
2040 for _, Child in pairs( Parent:GetChildren() ) do
2041 if Child:IsA( class_name ) then
2042 table.insert( matches, Child );
2043 end;
2044 end;
2045 end;
2046
2047 return matches;
2048end;
2049
2050function _HSVToRGB( hue, saturation, value )
2051 -- Returns the RGB equivalent of the given HSV-defined color
2052 -- (adapted from some code found around the web)
2053
2054 -- If it's achromatic, just return the value
2055 if saturation == 0 then
2056 return value;
2057 end;
2058
2059 -- Get the hue sector
2060 local hue_sector = math.floor( hue / 60 );
2061 local hue_sector_offset = ( hue / 60 ) - hue_sector;
2062
2063 local p = value * ( 1 - saturation );
2064 local q = value * ( 1 - saturation * hue_sector_offset );
2065 local t = value * ( 1 - saturation * ( 1 - hue_sector_offset ) );
2066
2067 if hue_sector == 0 then
2068 return value, t, p;
2069 elseif hue_sector == 1 then
2070 return q, value, p;
2071 elseif hue_sector == 2 then
2072 return p, value, t;
2073 elseif hue_sector == 3 then
2074 return p, q, value;
2075 elseif hue_sector == 4 then
2076 return t, p, value;
2077 elseif hue_sector == 5 then
2078 return value, p, q;
2079 end;
2080end;
2081
2082function _RGBToHSV( red, green, blue )
2083 -- Returns the HSV equivalent of the given RGB-defined color
2084 -- (adapted from some code found around the web)
2085
2086 local hue, saturation, value;
2087
2088 local min_value = math.min( red, green, blue );
2089 local max_value = math.max( red, green, blue );
2090
2091 value = max_value;
2092
2093 local value_delta = max_value - min_value;
2094
2095 -- If the color is not black
2096 if max_value ~= 0 then
2097 saturation = value_delta / max_value;
2098
2099 -- If the color is purely black
2100 else
2101 saturation = 0;
2102 hue = -1;
2103 return hue, saturation, value;
2104 end;
2105
2106 if red == max_value then
2107 hue = ( green - blue ) / value_delta;
2108 elseif green == max_value then
2109 hue = 2 + ( blue - red ) / value_delta;
2110 else
2111 hue = 4 + ( red - green ) / value_delta;
2112 end;
2113
2114 hue = hue * 60;
2115 if hue < 0 then
2116 hue = hue + 360;
2117 end;
2118
2119 return hue, saturation, value;
2120end;
2121
2122------------------------------------------
2123-- Create data containers
2124------------------------------------------
2125ActiveKeys = {};
2126
2127CurrentTool = nil;
2128
2129function equipTool( NewTool )
2130
2131 -- If it's a different tool than the current one
2132 if CurrentTool ~= NewTool then
2133
2134 -- Run (if existent) the old tool's `Unequipped` listener
2135 if CurrentTool and CurrentTool.Listeners.Unequipped then
2136 CurrentTool.Listeners.Unequipped();
2137 end;
2138
2139 CurrentTool = NewTool;
2140
2141 -- Recolor the handle
2142 if ToolType == 'tool' then
2143 Tool.Handle.BrickColor = NewTool.Color;
2144 end;
2145
2146 -- Highlight the right button on the dock
2147 for _, Button in pairs( Dock.ToolButtons:GetChildren() ) do
2148 Button.BackgroundTransparency = 1;
2149 end;
2150 local Button = Dock.ToolButtons:FindFirstChild( getToolName( NewTool ) .. "Button" );
2151 if Button then
2152 Button.BackgroundTransparency = 0;
2153 end;
2154
2155 -- Run (if existent) the new tool's `Equipped` listener
2156 if NewTool.Listeners.Equipped then
2157 NewTool.Listeners.Equipped();
2158 end;
2159
2160 end;
2161end;
2162
2163function cloneSelection()
2164 -- Clones the items in the selection
2165
2166 -- Make sure that there are items in the selection
2167 if #Selection.Items > 0 then
2168
2169 local item_copies = {};
2170
2171 -- Make a copy of every item in the selection and add it to table `item_copies`
2172 for _, Item in pairs( Selection.Items ) do
2173 local ItemCopy = Item:Clone();
2174 ItemCopy.Parent = Services.Workspace;
2175 table.insert( item_copies, ItemCopy );
2176 end;
2177
2178 -- Replace the selection with the copied items
2179 Selection:clear();
2180 for _, Item in pairs( item_copies ) do
2181 Selection:add( Item );
2182 end;
2183
2184 local HistoryRecord = {
2185 copies = item_copies;
2186 unapply = function ( self )
2187 for _, Copy in pairs( self.copies ) do
2188 if Copy then
2189 Copy.Parent = nil;
2190 end;
2191 end;
2192 end;
2193 apply = function ( self )
2194 Selection:clear();
2195 for _, Copy in pairs( self.copies ) do
2196 if Copy then
2197 Copy.Parent = Services.Workspace;
2198 Copy:MakeJoints();
2199 Selection:add( Copy );
2200 end;
2201 end;
2202 end;
2203 };
2204 History:add( HistoryRecord );
2205
2206 -- Play a confirmation sound
2207 local Sound = RbxUtility.Create "Sound" {
2208 Name = "BTActionCompletionSound";
2209 Pitch = 1.5;
2210 SoundId = action_completion_sound;
2211 Volume = 1;
2212 Parent = Player or Services.SoundService;
2213 };
2214 Sound:Play();
2215 Sound:Destroy();
2216
2217 -- Highlight the outlines of the new parts
2218 coroutine.wrap( function ()
2219 for transparency = 1, 0.5, -0.1 do
2220 for Item, SelectionBox in pairs( SelectionBoxes ) do
2221 SelectionBox.Transparency = transparency;
2222 end;
2223 wait( 0.1 );
2224 end;
2225 end )();
2226
2227 end;
2228
2229end;
2230
2231function deleteSelection()
2232 -- Deletes the items in the selection
2233
2234 if #Selection.Items == 0 then
2235 return;
2236 end;
2237
2238 local selection_items = _cloneTable( Selection.Items );
2239
2240 -- Create a history record
2241 local HistoryRecord = {
2242 targets = selection_items;
2243 parents = {};
2244 apply = function ( self )
2245 for _, Target in pairs( self.targets ) do
2246 if Target then
2247 Target.Parent = nil;
2248 end;
2249 end;
2250 end;
2251 unapply = function ( self )
2252 Selection:clear();
2253 for _, Target in pairs( self.targets ) do
2254 if Target then
2255 Target.Parent = self.parents[Target];
2256 Target:MakeJoints();
2257 Selection:add( Target );
2258 end;
2259 end;
2260 end;
2261 };
2262
2263 for _, Item in pairs( selection_items ) do
2264 HistoryRecord.parents[Item] = Item.Parent;
2265 Item.Parent = nil;
2266 end;
2267
2268 History:add( HistoryRecord );
2269
2270end;
2271
2272function prismSelect()
2273 -- Selects all the parts within the area of the selected parts
2274
2275 -- Make sure parts to define the area are present
2276 if #Selection.Items == 0 then
2277 return;
2278 end;
2279
2280 local parts = {};
2281
2282 -- Get all the parts in workspace
2283 local workspace_parts = {};
2284 local workspace_children = _getAllDescendants( Services.Workspace );
2285 for _, Child in pairs( workspace_children ) do
2286 if Child:IsA( 'BasePart' ) and not Selection:find( Child ) then
2287 table.insert( workspace_parts, Child );
2288 end;
2289 end;
2290
2291 -- Go through each part and perform area tests on each one
2292 local checks = {};
2293 for _, Item in pairs( workspace_parts ) do
2294 checks[Item] = 0;
2295 for _, SelectionItem in pairs( Selection.Items ) do
2296
2297 -- Calculate the position of the item in question in relation to the area-defining parts
2298 local offset = SelectionItem.CFrame:toObjectSpace( Item.CFrame );
2299 local extents = SelectionItem.Size / 2;
2300
2301 -- Check the item off if it passed this test (if it's within the range of the extents)
2302 if ( math.abs( offset.x ) <= extents.x ) and ( math.abs( offset.y ) <= extents.y ) and ( math.abs( offset.z ) <= extents.z ) then
2303 checks[Item] = checks[Item] + 1;
2304 end;
2305
2306 end;
2307 end;
2308
2309 -- Delete the parts that were used to select the area
2310 local selection_items = _cloneTable( Selection.Items );
2311 local selection_item_parents = {};
2312 for _, Item in pairs( selection_items ) do
2313 selection_item_parents[Item] = Item.Parent;
2314 Item.Parent = nil;
2315 end;
2316
2317 -- Select the parts that passed any area checks
2318 for _, Item in pairs( workspace_parts ) do
2319 if checks[Item] > 0 then
2320 Selection:add( Item );
2321 end;
2322 end;
2323
2324 -- Add a history record
2325 History:add( {
2326 selection_parts = selection_items;
2327 selection_part_parents = selection_item_parents;
2328 new_selection = _cloneTable( Selection.Items );
2329 apply = function ( self )
2330 Selection:clear();
2331 for _, Item in pairs( self.selection_parts ) do
2332 Item.Parent = nil;
2333 end;
2334 for _, Item in pairs( self.new_selection ) do
2335 Selection:add( Item );
2336 end;
2337 end;
2338 unapply = function ( self )
2339 Selection:clear();
2340 for _, Item in pairs( self.selection_parts ) do
2341 Item.Parent = self.selection_part_parents[Item];
2342 Selection:add( Item );
2343 end;
2344 end;
2345 } );
2346
2347end;
2348
2349function toggleHelp()
2350
2351 -- Make sure the dock is ready
2352 if not Dock then
2353 return;
2354 end;
2355
2356 -- Toggle the visibility of the help tooltip
2357 Dock.HelpInfo.Visible = not Dock.HelpInfo.Visible;
2358
2359end;
2360
2361function getToolName( tool )
2362 -- Returns the name of `tool` as registered in `Tools`
2363
2364 local name_search = _findTableOccurrences( Tools, tool );
2365 if #name_search > 0 then
2366 return name_search[1];
2367 end;
2368
2369end;
2370
2371function isSelectable( Object )
2372 -- Returns whether `Object` is selectable
2373
2374 if not Object or not Object.Parent or not Object:IsA( "BasePart" ) or Object.Locked or Selection:find( Object ) then
2375 return false;
2376 end;
2377
2378 -- If it passes all checks, return true
2379 return true;
2380end;
2381
2382-- Keep some state data
2383clicking = false;
2384selecting = false;
2385click_x, click_y = 0, 0;
2386override_selection = false;
2387
2388SelectionBoxes = {};
2389SelectionExistenceListeners = {};
2390SelectionBoxColor = BrickColor.new( "Cyan" );
2391TargetBox = nil;
2392
2393-- Keep a container for temporary connections
2394-- from the platform
2395Connections = {};
2396
2397-- Set the grip for the handle
2398if ToolType == 'tool' then
2399 Tool.Grip = CFrame.new( 0, 0, 0.4 );
2400end;
2401
2402-- Make sure the UI container gets placed
2403UI = RbxUtility.Create "ScreenGui" {
2404 Name = "Building Tools by F3X (UI)"
2405};
2406if ToolType == 'tool' then
2407 UI.Parent = GUIContainer;
2408elseif ToolType == 'plugin' then
2409 UI.Parent = Services.CoreGui;
2410end;
2411
2412Dragger = nil;
2413
2414function updateSelectionBoxColor()
2415 -- Updates the color of the selectionboxes
2416 for _, SelectionBox in pairs( SelectionBoxes ) do
2417 SelectionBox.Color = SelectionBoxColor;
2418 end;
2419end;
2420
2421Selection = {
2422
2423 ["Items"] = {};
2424
2425 -- Provide events to listen to changes in the selection
2426 ["Changed"] = RbxUtility.CreateSignal();
2427 ["ItemAdded"] = RbxUtility.CreateSignal();
2428 ["ItemRemoved"] = RbxUtility.CreateSignal();
2429
2430 -- Provide a method to get an item's index in the selection
2431 ["find"] = function ( self, Needle )
2432
2433 -- Look through all the selected items and return the matching item's index
2434 for item_index, Item in pairs( self.Items ) do
2435 if Item == Needle then
2436 return item_index;
2437 end;
2438 end;
2439
2440 -- Otherwise, return `nil`
2441
2442 end;
2443
2444 -- Provide a method to add items to the selection
2445 ["add"] = function ( self, NewPart )
2446
2447 -- Make sure `NewPart` is selectable
2448 if not isSelectable( NewPart ) then
2449 return false;
2450 end;
2451
2452 -- Make sure `NewPart` isn't already in the selection
2453 if #_findTableOccurrences( self.Items, NewPart ) > 0 then
2454 return false;
2455 end;
2456
2457 -- Insert it into the selection
2458 table.insert( self.Items, NewPart );
2459
2460 -- Add its SelectionBox
2461 SelectionBoxes[NewPart] = Instance.new( "SelectionBox", UI );
2462 SelectionBoxes[NewPart].Name = "BTSelectionBox";
2463 SelectionBoxes[NewPart].Color = SelectionBoxColor;
2464 SelectionBoxes[NewPart].Adornee = NewPart;
2465 SelectionBoxes[NewPart].Transparency = 0.5;
2466
2467 -- Remove any target selection box focus
2468 if NewPart == TargetBox.Adornee then
2469 TargetBox.Adornee = nil;
2470 end;
2471
2472 -- Make sure to remove the item from the selection when it's deleted
2473 SelectionExistenceListeners[NewPart] = NewPart.AncestryChanged:connect( function ( Object, NewParent )
2474 if NewParent == nil then
2475 Selection:remove( NewPart );
2476 end;
2477 end );
2478
2479 -- Provide a reference to the last item added to the selection (i.e. NewPart)
2480 self:focus( NewPart );
2481
2482 -- Fire events
2483 self.ItemAdded:fire( NewPart );
2484 self.Changed:fire();
2485
2486 end;
2487
2488 -- Provide a method to remove items from the selection
2489 ["remove"] = function ( self, Item )
2490
2491 -- Make sure selection item `Item` exists
2492 if not self:find( Item ) then
2493 return false;
2494 end;
2495
2496 -- Remove `Item`'s SelectionBox
2497 local SelectionBox = SelectionBoxes[Item];
2498 if SelectionBox then
2499 SelectionBox:Destroy();
2500 end;
2501 SelectionBoxes[Item] = nil;
2502
2503 -- Delete the item from the selection
2504 table.remove( self.Items, self:find( Item ) );
2505
2506 -- If it was logged as the last item, change it
2507 if self.Last == Item then
2508 self:focus( ( #self.Items > 0 ) and self.Items[#self.Items] or nil );
2509 end;
2510
2511 -- Delete the existence listeners of the item
2512 SelectionExistenceListeners[Item]:disconnect();
2513 SelectionExistenceListeners[Item] = nil;
2514
2515 -- Fire events
2516 self.ItemRemoved:fire( Item );
2517 self.Changed:fire();
2518
2519 end;
2520
2521 -- Provide a method to clear the selection
2522 ["clear"] = function ( self )
2523
2524 -- Go through all the items in the selection and call `self.remove` on them
2525 for _, Item in pairs( _cloneTable( self.Items ) ) do
2526 self:remove( Item );
2527 end;
2528
2529 end;
2530
2531 -- Provide a method to change the focus of the selection
2532 ["focus"] = function ( self, NewFocus )
2533
2534 -- Change the focus
2535 self.Last = NewFocus;
2536
2537 -- Fire events
2538 self.Changed:fire();
2539
2540 end;
2541
2542};
2543
2544-- Keep the Studio selection up-to-date (if applicable)
2545if ToolType == 'plugin' then
2546 Selection.Changed:connect( function ()
2547 Services.Selection:Set( Selection.Items );
2548 end );
2549end;
2550
2551Tools = {};
2552
2553------------------------------------------
2554-- Define other utilities needed by tools
2555------------------------------------------
2556
2557function createDropdown()
2558
2559 local Frame = RbxUtility.Create "Frame" {
2560 Name = "Dropdown";
2561 Size = UDim2.new( 0, 20, 0, 20 );
2562 BackgroundTransparency = 1;
2563 BorderSizePixel = 0;
2564 ClipsDescendants = true;
2565 };
2566
2567 RbxUtility.Create "ImageLabel" {
2568 Parent = Frame;
2569 Name = "Arrow";
2570 BackgroundTransparency = 1;
2571 BorderSizePixel = 0;
2572 Image = expand_arrow;
2573 Position = UDim2.new( 1, -21, 0, 3 );
2574 Size = UDim2.new( 0, 20, 0, 20 );
2575 ZIndex = 3;
2576 };
2577
2578 local DropdownObject = {
2579 -- Provide access to the actual frame
2580 Frame = Frame;
2581
2582 -- Keep a list of all the options in the dropdown
2583 _options = {};
2584
2585 -- Provide a function to add options to the dropdown
2586 addOption = function ( self, option )
2587
2588 -- Add the option to the list
2589 table.insert( self._options, option );
2590
2591 -- Create the GUI for the option
2592 local Button = RbxUtility.Create "TextButton" {
2593 Parent = self.Frame;
2594 BackgroundColor3 = Color3.new( 0, 0, 0 );
2595 BackgroundTransparency = 0.3;
2596 BorderColor3 = Color3.new( 27 / 255, 42 / 255, 53 / 255 );
2597 BorderSizePixel = 1;
2598 Name = option;
2599 Position = UDim2.new( math.ceil( #self._options / 9 ) - 1, 0, 0, 25 * ( ( #self._options % 9 == 0 ) and 9 or ( #self._options % 9 ) ) );
2600 Size = UDim2.new( 1, 0, 0, 25 );
2601 ZIndex = 3;
2602 Text = "";
2603 };
2604 local Label = RbxUtility.Create "TextLabel" {
2605 Parent = Button;
2606 BackgroundTransparency = 1;
2607 BorderSizePixel = 0;
2608 Position = UDim2.new( 0, 6, 0, 0 );
2609 Size = UDim2.new( 1, -30, 1, 0 );
2610 ZIndex = 3;
2611 Font = Enum.Font.ArialBold;
2612 FontSize = Enum.FontSize.Size12;
2613 Text = option;
2614 TextColor3 = Color3.new( 1, 1, 1 );
2615 TextXAlignment = Enum.TextXAlignment.Left;
2616 TextYAlignment = Enum.TextYAlignment.Center;
2617 };
2618
2619 -- Return the button object
2620 return Button;
2621
2622 end;
2623
2624 selectOption = function ( self, option )
2625 self.Frame.MainButton.CurrentOption.Text = option;
2626 end;
2627
2628 open = false;
2629
2630 toggle = function ( self )
2631
2632 -- If it's open, close it
2633 if self.open then
2634 self.Frame.MainButton.BackgroundTransparency = 0.3;
2635 self.Frame.ClipsDescendants = true;
2636 self.open = false;
2637
2638 -- If it's not open, open it
2639 else
2640 self.Frame.MainButton.BackgroundTransparency = 0;
2641 self.Frame.ClipsDescendants = false;
2642 self.open = true;
2643 end;
2644
2645 end;
2646
2647 };
2648
2649 -- Create the GUI for the option
2650 local MainButton = RbxUtility.Create "TextButton" {
2651 Parent = Frame;
2652 Name = "MainButton";
2653 BackgroundColor3 = Color3.new( 0, 0, 0 );
2654 BackgroundTransparency = 0.3;
2655 BorderColor3 = Color3.new( 27 / 255, 42 / 255, 53 / 255 );
2656 BorderSizePixel = 1;
2657 Position = UDim2.new( 0, 0, 0, 0 );
2658 Size = UDim2.new( 1, 0, 0, 25 );
2659 ZIndex = 2;
2660 Text = "";
2661
2662 -- Toggle the dropdown when pressed
2663 [RbxUtility.Create.E "MouseButton1Up"] = function ()
2664 DropdownObject:toggle();
2665 end;
2666 };
2667 RbxUtility.Create "TextLabel" {
2668 Parent = MainButton;
2669 Name = "CurrentOption";
2670 BackgroundTransparency = 1;
2671 BorderSizePixel = 0;
2672 Position = UDim2.new( 0, 6, 0, 0 );
2673 Size = UDim2.new( 1, -30, 1, 0 );
2674 ZIndex = 3;
2675 Font = Enum.Font.ArialBold;
2676 FontSize = Enum.FontSize.Size12;
2677 Text = "";
2678 TextColor3 = Color3.new( 1, 1, 1 );
2679 TextXAlignment = Enum.TextXAlignment.Left;
2680 TextYAlignment = Enum.TextYAlignment.Center;
2681 };
2682
2683 return DropdownObject;
2684
2685end;
2686
2687------------------------------------------
2688-- Provide an interface to the 2D
2689-- selection system
2690------------------------------------------
2691
2692Select2D = {
2693
2694 -- Keep state data
2695 ["enabled"] = false;
2696
2697 -- Keep objects
2698 ["GUI"] = nil;
2699
2700 -- Keep temporary, disposable connections
2701 ["Connections"] = {};
2702
2703 -- Provide an interface to the functions
2704 ["start"] = function ( self )
2705
2706 if enabled then
2707 return;
2708 end;
2709
2710 self.enabled = true;
2711
2712 -- Create the GUI
2713 self.GUI = RbxUtility.Create "ScreenGui" {
2714 Name = "BTSelectionRectangle";
2715 Parent = UI;
2716 };
2717
2718 local Rectangle = RbxUtility.Create "Frame" {
2719 Name = "Rectangle";
2720 Active = false;
2721 Parent = self.GUI;
2722 BackgroundColor3 = Color3.new( 0, 0, 0 );
2723 BackgroundTransparency = 0.5;
2724 BorderSizePixel = 0;
2725 Position = UDim2.new( 0, math.min( click_x, Mouse.X ), 0, math.min( click_y, Mouse.Y ) );
2726 Size = UDim2.new( 0, math.max( click_x, Mouse.X ) - math.min( click_x, Mouse.X ), 0, math.max( click_y, Mouse.Y ) - math.min( click_y, Mouse.Y ) );
2727 };
2728
2729 -- Listen for when to resize the selection
2730 self.Connections.SelectionResize = Mouse.Move:connect( function ()
2731 Rectangle.Position = UDim2.new( 0, math.min( click_x, Mouse.X ), 0, math.min( click_y, Mouse.Y ) );
2732 Rectangle.Size = UDim2.new( 0, math.max( click_x, Mouse.X ) - math.min( click_x, Mouse.X ), 0, math.max( click_y, Mouse.Y ) - math.min( click_y, Mouse.Y ) );
2733 end );
2734
2735 -- Listen for when the selection ends
2736 self.Connections.SelectionEnd = Mouse.Button1Up:connect( function ()
2737 self:select();
2738 self:finish();
2739 end );
2740
2741 end;
2742
2743 ["select"] = function ( self )
2744
2745 if not self.enabled then
2746 return;
2747 end;
2748
2749 for _, Object in pairs( _getAllDescendants( Services.Workspace ) ) do
2750
2751 -- Make sure we can select this part
2752 if isSelectable( Object ) then
2753
2754 -- Check if the part is rendered within the range of the selection area
2755 local PartPosition = _pointToScreenSpace( Object.Position );
2756 if PartPosition then
2757 local left_check = PartPosition.x >= self.GUI.Rectangle.AbsolutePosition.x;
2758 local right_check = PartPosition.x <= ( self.GUI.Rectangle.AbsolutePosition.x + self.GUI.Rectangle.AbsoluteSize.x );
2759 local top_check = PartPosition.y >= self.GUI.Rectangle.AbsolutePosition.y;
2760 local bottom_check = PartPosition.y <= ( self.GUI.Rectangle.AbsolutePosition.y + self.GUI.Rectangle.AbsoluteSize.y );
2761
2762 -- If the part is within the selection area, select it
2763 if left_check and right_check and top_check and bottom_check then
2764 Selection:add( Object );
2765 end;
2766 end;
2767
2768 end;
2769
2770 end;
2771
2772 end;
2773
2774 ["finish"] = function ( self )
2775
2776 if not self.enabled then
2777 return;
2778 end;
2779
2780 -- Disconnect temporary connections
2781 for connection_index, Connection in pairs( self.Connections ) do
2782 Connection:disconnect();
2783 self.Connections[connection_index] = nil;
2784 end;
2785
2786 -- Remove temporary objects
2787 self.GUI:Destroy();
2788 self.GUI = nil;
2789
2790 self.enabled = false;
2791
2792 end;
2793
2794};
2795
2796------------------------------------------
2797-- Provide an interface to the edge
2798-- selection system
2799------------------------------------------
2800SelectEdge = {
2801
2802 -- Keep state data
2803 ["enabled"] = false;
2804 ["started"] = false;
2805
2806 -- Keep objects
2807 ["Marker"] = nil;
2808 ["MarkerOutline"] = RbxUtility.Create "SelectionBox" {
2809 Color = BrickColor.new( "Institutional white" );
2810 Parent = UI;
2811 Name = "BTEdgeSelectionMarkerOutline";
2812 };
2813
2814 -- Keep temporary, disposable connections
2815 ["Connections"] = {};
2816
2817 -- Provide an interface to the functions
2818 ["start"] = function ( self, edgeSelectionCallback )
2819
2820 if self.started then
2821 return;
2822 end;
2823
2824 -- Listen for when to engage in selection
2825 self.Connections.KeyListener = Mouse.KeyDown:connect( function ( key )
2826
2827 local key = key:lower();
2828 local key_code = key:byte();
2829
2830 if key == "t" and #Selection.Items > 0 then
2831 self:enable( edgeSelectionCallback );
2832 end;
2833
2834 end );
2835
2836 self.started = true;
2837
2838 end;
2839
2840 ["enable"] = function ( self, edgeSelectionCallback )
2841
2842 if self.enabled then
2843 return;
2844 end;
2845
2846 self.Connections.MoveListener = Mouse.Move:connect( function ()
2847
2848 -- Make sure the target can be selected
2849 if not Selection:find( Mouse.Target ) then
2850 return;
2851 end;
2852
2853 -- Calculate the proximity to each edge
2854 local Proximity = {};
2855 local edges = {};
2856
2857 -- Create shortcuts to certain things that are expensive to call constantly
2858 local table_insert = table.insert;
2859 local newCFrame = CFrame.new;
2860 local PartCFrame = Mouse.Target.CFrame;
2861 local partCFrameOffset = PartCFrame.toWorldSpace;
2862 local PartSize = Mouse.Target.Size / 2;
2863 local size_x, size_y, size_z = PartSize.x, PartSize.y, PartSize.z;
2864
2865 table_insert( edges, partCFrameOffset( PartCFrame, newCFrame( size_x, size_y, size_z ) ) );
2866 table_insert( edges, partCFrameOffset( PartCFrame, newCFrame( -size_x, size_y, size_z ) ) );
2867 table_insert( edges, partCFrameOffset( PartCFrame, newCFrame( size_x, -size_y, size_z ) ) );
2868 table_insert( edges, partCFrameOffset( PartCFrame, newCFrame( size_x, size_y, -size_z ) ) );
2869 table_insert( edges, partCFrameOffset( PartCFrame, newCFrame( -size_x, size_y, -size_z ) ) );
2870 table_insert( edges, partCFrameOffset( PartCFrame, newCFrame( -size_x, -size_y, size_z ) ) );
2871 table_insert( edges, partCFrameOffset( PartCFrame, newCFrame( size_x, -size_y, -size_z ) ) );
2872 table_insert( edges, partCFrameOffset( PartCFrame, newCFrame( -size_x, -size_y, -size_z ) ) );
2873 table_insert( edges, partCFrameOffset( PartCFrame, newCFrame( size_x, size_y, 0 ) ) );
2874 table_insert( edges, partCFrameOffset( PartCFrame, newCFrame( size_x, 0, size_z ) ) );
2875 table_insert( edges, partCFrameOffset( PartCFrame, newCFrame( 0, size_y, size_z ) ) );
2876 table_insert( edges, partCFrameOffset( PartCFrame, newCFrame( size_x, 0, 0 ) ) );
2877 table_insert( edges, partCFrameOffset( PartCFrame, newCFrame( 0, size_y, 0 ) ) );
2878 table_insert( edges, partCFrameOffset( PartCFrame, newCFrame( 0, 0, size_z ) ) );
2879 table_insert( edges, partCFrameOffset( PartCFrame, newCFrame( -size_x, size_y, 0 ) ) );
2880 table_insert( edges, partCFrameOffset( PartCFrame, newCFrame( -size_x, 0, size_z ) ) );
2881 table_insert( edges, partCFrameOffset( PartCFrame, newCFrame( 0, -size_y, size_z ) ) );
2882 table_insert( edges, partCFrameOffset( PartCFrame, newCFrame( -size_x, 0, 0 ) ) );
2883 table_insert( edges, partCFrameOffset( PartCFrame, newCFrame( 0, -size_y, 0 ) ) );
2884 table_insert( edges, partCFrameOffset( PartCFrame, newCFrame( 0, 0, -size_z ) ) );
2885 table_insert( edges, partCFrameOffset( PartCFrame, newCFrame( size_x, -size_y, 0 ) ) );
2886 table_insert( edges, partCFrameOffset( PartCFrame, newCFrame( size_x, 0, -size_z ) ) );
2887 table_insert( edges, partCFrameOffset( PartCFrame, newCFrame( 0, size_y, -size_z ) ) );
2888 table_insert( edges, partCFrameOffset( PartCFrame, newCFrame( -size_x, -size_y, 0 ) ) );
2889 table_insert( edges, partCFrameOffset( PartCFrame, newCFrame( -size_x, 0, -size_z ) ) );
2890 table_insert( edges, partCFrameOffset( PartCFrame, newCFrame( 0, -size_y, -size_z ) ) );
2891
2892 -- Calculate the proximity of every edge to the mouse
2893 for edge_index, Edge in pairs( edges ) do
2894 Proximity[edge_index] = ( Mouse.Hit.p - Edge.p ).magnitude;
2895 end;
2896
2897 -- Get the closest edge to the mouse
2898 local highest_proximity = 1;
2899 for proximity_index, proximity in pairs( Proximity ) do
2900 if proximity < Proximity[highest_proximity] then
2901 highest_proximity = proximity_index;
2902 end;
2903 end;
2904
2905 -- Replace the current target edge (if any)
2906 local ClosestEdge = edges[highest_proximity];
2907
2908 if self.Marker then
2909 self.Marker:Destroy();
2910 end;
2911 self.Marker = RbxUtility.Create "Part" {
2912 Name = "BTEdgeSelectionMarker";
2913 Anchored = true;
2914 Locked = true;
2915 CanCollide = false;
2916 Transparency = 1;
2917 FormFactor = Enum.FormFactor.Custom;
2918 Size = Vector3.new( 0.2, 0.2, 0.2 );
2919 CFrame = ClosestEdge;
2920 };
2921
2922 self.MarkerOutline.Adornee = self.Marker;
2923
2924 end );
2925
2926 self.Connections.ClickListener = Mouse.Button1Up:connect( function ()
2927 override_selection = true;
2928 self:select( edgeSelectionCallback );
2929 end );
2930
2931 self.enabled = true;
2932
2933 end;
2934
2935 ["select"] = function ( self, callback )
2936
2937 if not self.enabled or not self.Marker then
2938 return;
2939 end;
2940
2941 self.MarkerOutline.Adornee = self.Marker;
2942
2943 callback( self.Marker );
2944
2945 -- Stop treating it like a marker
2946 self.Marker = nil;
2947
2948 self:disable();
2949
2950 end;
2951
2952 ["disable"] = function ( self )
2953
2954 if not self.enabled then
2955 return;
2956 end;
2957
2958 -- Disconnect unnecessary temporary connections
2959 if self.Connections.ClickListener then
2960 self.Connections.ClickListener:disconnect();
2961 self.Connections.ClickListener = nil;
2962 end;
2963 if self.Connections.MoveListener then
2964 self.Connections.MoveListener:disconnect();
2965 self.Connections.MoveListener = nil;
2966 end;
2967
2968 -- Remove temporary objects
2969 if self.Marker then
2970 self.Marker:Destroy();
2971 end;
2972 self.Marker = nil;
2973
2974 self.MarkerOutline.Adornee = nil;
2975 self.enabled = false;
2976
2977 end;
2978
2979 ["stop"] = function ( self )
2980
2981 if not self.started then
2982 return;
2983 end;
2984
2985 -- Disconnect & remove all temporary connections
2986 for connection_index, Connection in pairs( self.Connections ) do
2987 Connection:disconnect();
2988 self.Connections[connection_index] = nil;
2989 end;
2990
2991 -- Remove temporary objects
2992 if self.Marker then
2993 self.Marker:Destroy();
2994 end;
2995
2996 self.started = false;
2997
2998 end;
2999
3000};
3001
3002------------------------------------------
3003-- Provide an interface to the history
3004-- system
3005------------------------------------------
3006History = {
3007
3008 -- Keep a container for the actual history data
3009 ["Data"] = {};
3010
3011 -- Keep state data
3012 ["index"] = 0;
3013
3014 -- Provide events for the platform to listen for changes
3015 ["Changed"] = RbxUtility.CreateSignal();
3016
3017 -- Provide functions to control the system
3018 ["undo"] = function ( self )
3019
3020 -- Make sure we're not getting out of boundary
3021 if self.index - 1 < 0 then
3022 return;
3023 end;
3024
3025 -- Fetch the history record & unapply it
3026 local CurrentRecord = self.Data[self.index];
3027 CurrentRecord:unapply();
3028
3029 -- Go back in the history
3030 self.index = self.index - 1;
3031
3032 -- Fire the relevant events
3033 self.Changed:fire();
3034
3035 end;
3036
3037 ["redo"] = function ( self )
3038
3039 -- Make sure we're not getting out of boundary
3040 if self.index + 1 > #self.Data then
3041 return;
3042 end;
3043
3044 -- Go forward in the history
3045 self.index = self.index + 1;
3046
3047 -- Fetch the new history record & apply it
3048 local NewRecord = self.Data[self.index];
3049 NewRecord:apply();
3050
3051 -- Fire the relevant events
3052 self.Changed:fire();
3053
3054 end;
3055
3056 ["add"] = function ( self, Record )
3057
3058 -- Place the record in its right spot
3059 self.Data[self.index + 1] = Record;
3060
3061 -- Advance the history index
3062 self.index = self.index + 1;
3063
3064 -- Clear out the following history
3065 for index = self.index + 1, #self.Data do
3066 self.Data[index] = nil;
3067 end;
3068
3069 -- Fire the relevant events
3070 self.Changed:fire();
3071
3072 end;
3073
3074};
3075
3076
3077------------------------------------------
3078-- Provide an interface color picker
3079-- system
3080------------------------------------------
3081ColorPicker = {
3082
3083 -- Keep some state data
3084 ["enabled"] = false;
3085 ["callback"] = nil;
3086 ["track_mouse"] = nil;
3087 ["hue"] = 0;
3088 ["saturation"] = 1;
3089 ["value"] = 1;
3090
3091 -- Keep the current GUI here
3092 ["GUI"] = nil;
3093
3094 -- Keep temporary, disposable connections here
3095 ["Connections"] = {};
3096
3097 -- Provide an interface to the functions
3098 ["start"] = function ( self, callback, start_color )
3099
3100 -- Replace any existing color pickers
3101 if self.enabled then
3102 self:cancel();
3103 end;
3104 self.enabled = true;
3105
3106 -- Create the GUI
3107 self.GUI = Tool.Interfaces.BTHSVColorPicker:Clone();
3108 self.GUI.Parent = UI;
3109
3110 -- Register the callback function for when we're done here
3111 self.callback = callback;
3112
3113 -- Update the GUI
3114 local start_color = start_color or Color3.new( 1, 0, 0 );
3115 self:_changeColor( _RGBToHSV( start_color.r, start_color.g, start_color.b ) );
3116
3117 -- Add functionality to the GUI's interactive elements
3118 table.insert( self.Connections, self.GUI.HueSaturation.MouseButton1Down:connect( function ( x, y )
3119 self.track_mouse = 'hue-saturation';
3120 self:_onMouseMove( x, y );
3121 end ) );
3122
3123 table.insert( self.Connections, self.GUI.HueSaturation.MouseButton1Up:connect( function ()
3124 self.track_mouse = nil;
3125 end ) );
3126
3127 table.insert( self.Connections, self.GUI.MouseMoved:connect( function ( x, y )
3128 self:_onMouseMove( x, y );
3129 end ) );
3130
3131 table.insert( self.Connections, self.GUI.Value.MouseButton1Down:connect( function ( x, y )
3132 self.track_mouse = 'value';
3133 self:_onMouseMove( x, y );
3134 end ) );
3135
3136 table.insert( self.Connections, self.GUI.Value.MouseButton1Up:connect( function ()
3137 self.track_mouse = nil;
3138 end ) );
3139
3140 table.insert( self.Connections, self.GUI.OkButton.MouseButton1Up:connect( function ()
3141 self:finish();
3142 end ) );
3143
3144 table.insert( self.Connections, self.GUI.CancelButton.MouseButton1Up:connect( function ()
3145 self:cancel();
3146 end ) );
3147
3148 table.insert( self.Connections, self.GUI.HueOption.Input.TextButton.MouseButton1Down:connect( function ()
3149 self.GUI.HueOption.Input.TextBox:CaptureFocus();
3150 end ) );
3151 table.insert( self.Connections, self.GUI.HueOption.Input.TextBox.FocusLost:connect( function ( enter_pressed )
3152 local potential_new = tonumber( self.GUI.HueOption.Input.TextBox.Text );
3153 if potential_new then
3154 if potential_new > 360 then
3155 potential_new = 360;
3156 elseif potential_new < 0 then
3157 potential_new = 0;
3158 end;
3159 self:_changeColor( potential_new, self.saturation, self.value );
3160 else
3161 self:_updateGUI();
3162 end;
3163 end ) );
3164
3165 table.insert( self.Connections, self.GUI.SaturationOption.Input.TextButton.MouseButton1Down:connect( function ()
3166 self.GUI.SaturationOption.Input.TextBox:CaptureFocus();
3167 end ) );
3168 table.insert( self.Connections, self.GUI.SaturationOption.Input.TextBox.FocusLost:connect( function ( enter_pressed )
3169 local potential_new = tonumber( ( self.GUI.SaturationOption.Input.TextBox.Text:gsub( '%%', '' ) ) );
3170 if potential_new then
3171 if potential_new > 100 then
3172 potential_new = 100;
3173 elseif potential_new < 0 then
3174 potential_new = 0;
3175 end;
3176 self:_changeColor( self.hue, potential_new / 100, self.value );
3177 else
3178 self:_updateGUI();
3179 end;
3180 end ) );
3181
3182 table.insert( self.Connections, self.GUI.ValueOption.Input.TextButton.MouseButton1Down:connect( function ()
3183 self.GUI.ValueOption.Input.TextBox:CaptureFocus();
3184 end ) );
3185 table.insert( self.Connections, self.GUI.ValueOption.Input.TextBox.FocusLost:connect( function ( enter_pressed )
3186 local potential_new = tonumber( ( self.GUI.ValueOption.Input.TextBox.Text:gsub( '%%', '' ) ) );
3187 if potential_new then
3188 if potential_new < 0 then
3189 potential_new = 0;
3190 elseif potential_new > 100 then
3191 potential_new = 100;
3192 end;
3193 self:_changeColor( self.hue, self.saturation, potential_new / 100 );
3194 else
3195 self:_updateGUI();
3196 end;
3197 end ) );
3198
3199 end;
3200
3201 ["_onMouseMove"] = function ( self, x, y )
3202 if not self.track_mouse then
3203 return;
3204 end;
3205
3206 if self.track_mouse == 'hue-saturation' then
3207 -- Calculate the mouse position relative to the graph
3208 local graph_x, graph_y = x - self.GUI.HueSaturation.AbsolutePosition.x, y - self.GUI.HueSaturation.AbsolutePosition.y;
3209
3210 -- Make sure we're not going out of bounds
3211 if graph_x < 0 then
3212 graph_x = 0;
3213 elseif graph_x > self.GUI.HueSaturation.AbsoluteSize.x then
3214 graph_x = self.GUI.HueSaturation.AbsoluteSize.x;
3215 end;
3216 if graph_y < 0 then
3217 graph_y = 0;
3218 elseif graph_y > self.GUI.HueSaturation.AbsoluteSize.y then
3219 graph_y = self.GUI.HueSaturation.AbsoluteSize.y;
3220 end;
3221
3222 -- Calculate the new color and change it
3223 self:_changeColor( 359 * graph_x / 209, 1 - graph_y / 200, self.value );
3224
3225 elseif self.track_mouse == 'value' then
3226 -- Calculate the mouse position relative to the value bar
3227 local bar_y = y - self.GUI.Value.AbsolutePosition.y;
3228
3229 -- Make sure we're not going out of bounds
3230 if bar_y < 0 then
3231 bar_y = 0;
3232 elseif bar_y > self.GUI.Value.AbsoluteSize.y then
3233 bar_y = self.GUI.Value.AbsoluteSize.y;
3234 end;
3235
3236 -- Calculate the new color and change it
3237 self:_changeColor( self.hue, self.saturation, 1 - bar_y / 200 );
3238 end;
3239 end;
3240
3241 ["_changeColor"] = function ( self, hue, saturation, value )
3242 if hue ~= hue then
3243 hue = 359;
3244 end;
3245 self.hue = hue;
3246 self.saturation = saturation == 0 and 0.01 or saturation;
3247 self.value = value;
3248 self:_updateGUI();
3249 end;
3250
3251 ["_updateGUI"] = function ( self )
3252
3253 self.GUI.HueSaturation.Cursor.Position = UDim2.new( 0, 209 * self.hue / 360 - 8, 0, ( 1 - self.saturation ) * 200 - 8 );
3254 self.GUI.Value.Cursor.Position = UDim2.new( 0, -2, 0, ( 1 - self.value ) * 200 - 8 );
3255
3256 local color = Color3.new( _HSVToRGB( self.hue, self.saturation, self.value ) );
3257 self.GUI.ColorDisplay.BackgroundColor3 = color;
3258 self.GUI.Value.ColorBG.BackgroundColor3 = Color3.new( _HSVToRGB( self.hue, self.saturation, 1 ) );
3259
3260 self.GUI.HueOption.Bar.BackgroundColor3 = color;
3261 self.GUI.SaturationOption.Bar.BackgroundColor3 = color;
3262 self.GUI.ValueOption.Bar.BackgroundColor3 = color;
3263
3264 self.GUI.HueOption.Input.TextBox.Text = math.floor( self.hue );
3265 self.GUI.SaturationOption.Input.TextBox.Text = math.floor( self.saturation * 100 ) .. "%";
3266 self.GUI.ValueOption.Input.TextBox.Text = math.floor( self.value * 100 ) .. "%";
3267
3268 end;
3269
3270 ["finish"] = function ( self )
3271
3272 if not self.enabled then
3273 return;
3274 end;
3275
3276 -- Remove the GUI
3277 if self.GUI then
3278 self.GUI:Destroy();
3279 end;
3280 self.GUI = nil;
3281 self.track_mouse = nil;
3282
3283 -- Disconnect all temporary connections
3284 for connection_index, connection in pairs( self.Connections ) do
3285 connection:disconnect();
3286 self.Connections[connection_index] = nil;
3287 end;
3288
3289 -- Call the callback function that was provided to us
3290 self.callback( self.hue, self.saturation, self.value );
3291 self.callback = nil;
3292
3293 self.enabled = false;
3294
3295 end;
3296
3297 ["cancel"] = function ( self )
3298
3299 if not self.enabled then
3300 return;
3301 end;
3302
3303 -- Remove the GUI
3304 if self.GUI then
3305 self.GUI:Destroy();
3306 end;
3307 self.GUI = nil;
3308 self.track_mouse = nil;
3309
3310 -- Disconnect all temporary connections
3311 for connection_index, connection in pairs( self.Connections ) do
3312 connection:disconnect();
3313 self.Connections[connection_index] = nil;
3314 end;
3315
3316 -- Call the callback function that was provided to us
3317 self.callback();
3318 self.callback = nil;
3319
3320 self.enabled = false;
3321
3322 end;
3323
3324};
3325
3326------------------------------------------
3327-- Provide an interface to the
3328-- import/export system
3329------------------------------------------
3330IE = {
3331
3332 ["export"] = function ()
3333
3334 if #Selection.Items == 0 then
3335 return;
3336 end;
3337
3338 local serialized_selection = _serializeParts( Selection.Items );
3339
3340 -- Dump to logs
3341 -- Services.TestService:Warn( false, "[Building Tools by F3X] Exported Model: \n" .. serialized_selection );
3342
3343 -- Get ready to upload to the web for retrieval
3344 local upload_data;
3345 local cancelUpload;
3346
3347 -- Create the export dialog
3348 local Dialog = Tool.Interfaces.BTExportDialog:Clone();
3349 Dialog.Loading.Size = UDim2.new( 1, 0, 0, 0 );
3350 Dialog.Parent = UI;
3351 Dialog.Loading:TweenSize( UDim2.new( 1, 0, 0, 80 ), Enum.EasingDirection.Out, Enum.EasingStyle.Quad, 0.25 );
3352 Dialog.Loading.CloseButton.MouseButton1Up:connect( function ()
3353 cancelUpload();
3354 Dialog:Destroy();
3355 end );
3356
3357 -- Run the upload/post-upload/failure code in a coroutine
3358 -- so it can be cancelled
3359 coroutine.resume( coroutine.create( function ()
3360 cancelUpload = function ()
3361 coroutine.yield();
3362 end;
3363 local upload_attempt = ypcall( function ()
3364 upload_data = PostAsync( "http://www.f3xteam.com/bt/export", serialized_selection );
3365 end );
3366
3367 -- Make sure we're in a server
3368 if ToolType == 'plugin' and not in_server then
3369 Dialog.Loading.TextLabel.Text = "Use Tools > Test > Start Server to export from Studio";
3370 Dialog.Loading.TextLabel.TextWrapped = true;
3371 Dialog.Loading.CloseButton.Position = UDim2.new( 0, 0, 0, 50 );
3372 Dialog.Loading.CloseButton.Text = 'Got it';
3373 return;
3374 end;
3375
3376 -- Fail graciously
3377 if not upload_attempt then
3378 Dialog.Loading.TextLabel.Text = "Upload failed";
3379 Dialog.Loading.CloseButton.Text = 'Ok :(';
3380 return;
3381 end;
3382 if not ( upload_data and type( upload_data ) == 'string' and upload_data:len() > 0 ) then
3383 Dialog.Loading.TextLabel.Text = "Upload failed";
3384 Dialog.Loading.CloseButton.Text = 'Ok ;(';
3385 return;
3386 end;
3387 if not pcall( function () upload_data = RbxUtility.DecodeJSON( upload_data ); end ) or not upload_data then
3388 Dialog.Loading.TextLabel.Text = "Upload failed";
3389 Dialog.Loading.CloseButton.Text = "Ok :'(";
3390 return;
3391 end;
3392 if not upload_data.success then
3393 Dialog.Loading.TextLabel.Text = "Upload failed";
3394 Dialog.Loading.CloseButton.Text = "Ok :''(";
3395 end;
3396
3397 print( "[Building Tools by F3X] Uploaded Export: " .. upload_data.id );
3398
3399 Dialog.Loading.Visible = false;
3400 Dialog.Info.Size = UDim2.new( 1, 0, 0, 0 );
3401 Dialog.Info.CreationID.Text = upload_data.id;
3402 Dialog.Info.Visible = true;
3403 Dialog.Info:TweenSize( UDim2.new( 1, 0, 0, 75 ), Enum.EasingDirection.Out, Enum.EasingStyle.Quad, 0.25 );
3404 Dialog.Tip.Size = UDim2.new( 1, 0, 0, 0 );
3405 Dialog.Tip.Visible = true;
3406 Dialog.Tip:TweenSize( UDim2.new( 1, 0, 0, 30 ), Enum.EasingDirection.Out, Enum.EasingStyle.Quad, 0.25 );
3407 Dialog.Close.Size = UDim2.new( 1, 0, 0, 0 );
3408 Dialog.Close.Visible = true;
3409 Dialog.Close:TweenSize( UDim2.new( 1, 0, 0, 20 ), Enum.EasingDirection.Out, Enum.EasingStyle.Quad, 0.25 );
3410 Dialog.Close.Button.MouseButton1Up:connect( function ()
3411 Dialog:Destroy();
3412 end );
3413
3414 -- Play a confirmation sound
3415 local Sound = RbxUtility.Create "Sound" {
3416 Name = "BTActionCompletionSound";
3417 Pitch = 1.5;
3418 SoundId = action_completion_sound;
3419 Volume = 1;
3420 Parent = Player or Services.SoundService;
3421 };
3422 Sound:Play();
3423 Sound:Destroy();
3424 end ) );
3425
3426 end;
3427
3428};
3429
3430------------------------------------------
3431-- Prepare the dock UI
3432------------------------------------------
3433
3434Tooltips = {};
3435
3436Dock = Tool.Interfaces:WaitForChild( "BTDockGUI" ):Clone();
3437Dock.Parent = UI;
3438Dock.Visible = false;
3439
3440-- Add functionality to each tool button
3441for _, ToolButton in pairs( Dock.ToolButtons:GetChildren() ) do
3442
3443 -- Get the tool name and the tool
3444 local tool_name = ToolButton.Name:match( "(.+)Button" );
3445
3446 if tool_name then
3447
3448 -- Create the click connection
3449 ToolButton.MouseButton1Up:connect( function ()
3450 local Tool = Tools[tool_name];
3451 if Tool then
3452 equipTool( Tool );
3453 end;
3454 end );
3455
3456 ToolButton.MouseEnter:connect( function ()
3457 local Tooltip = Tooltips[tool_name];
3458 if Tooltip then
3459 Tooltip:focus( 'button' );
3460 end;
3461 end );
3462
3463 ToolButton.MouseLeave:connect( function ()
3464 local Tooltip = Tooltips[tool_name];
3465 if Tooltip then
3466 Tooltip:unfocus( 'button' );
3467 end;
3468 end );
3469
3470 end;
3471
3472end;
3473
3474-- Prepare the tooltips
3475for _, Tooltip in pairs( Dock.Tooltips:GetChildren() ) do
3476
3477 local tool_name = Tooltip.Name:match( "(.+)Info" );
3478
3479 Tooltips[tool_name] = {
3480
3481 GUI = Tooltip;
3482
3483 button_focus = false;
3484 tooltip_focus = false;
3485
3486 focus = function ( self, source )
3487 if Dock.HelpInfo.Visible then
3488 return;
3489 end;
3490 if source == 'button' then
3491 self.button_focus = true;
3492 elseif source == 'tooltip' then
3493 self.tooltip_focus = true;
3494 end;
3495 for _, Tooltip in pairs( Dock.Tooltips:GetChildren() ) do
3496 Tooltip.Visible = false;
3497 end;
3498 self.GUI.Visible = true;
3499 end;
3500
3501 unfocus = function ( self, source )
3502 if source == 'button' then
3503 self.button_focus = false;
3504 elseif source == 'tooltip' then
3505 self.tooltip_focus = false;
3506 end;
3507 if not self.button_focus and not self.tooltip_focus then
3508 self.GUI.Visible = false;
3509 end;
3510 end;
3511
3512 };
3513
3514 -- Make it disappear after it's out of mouse focus
3515 Tooltip.MouseEnter:connect( function ()
3516 Tooltips[tool_name]:focus( 'tooltip' );
3517 end );
3518 Tooltip.MouseLeave:connect( function ()
3519 Tooltips[tool_name]:unfocus( 'tooltip' );
3520 end );
3521
3522 -- Create the scrolling container
3523 local ScrollingContainer = Gloo.ScrollingContainer( true, false, 15 );
3524 ScrollingContainer.GUI.Parent = Tooltip;
3525
3526 -- Put the tooltip content in the container
3527 for _, Child in pairs( Tooltip.Content:GetChildren() ) do
3528 Child.Parent = ScrollingContainer.Container;
3529 end;
3530 ScrollingContainer.GUI.Size = Dock.Tooltips.Size;
3531 ScrollingContainer.Container.Size = Tooltip.Content.Size;
3532 ScrollingContainer.Boundary.Size = Dock.Tooltips.Size;
3533 ScrollingContainer.Boundary.BackgroundTransparency = 1;
3534 Tooltip.Content:Destroy();
3535
3536end;
3537
3538
3539-- Create the scrolling container for the help tooltip
3540local ScrollingContainer = Gloo.ScrollingContainer( true, false, 15 );
3541ScrollingContainer.GUI.Parent = Dock.HelpInfo;
3542
3543-- Put the help tooltip content in the container
3544for _, Child in pairs( Dock.HelpInfo.Content:GetChildren() ) do
3545 Child.Parent = ScrollingContainer.Container;
3546end;
3547ScrollingContainer.GUI.Size = Dock.HelpInfo.Size;
3548ScrollingContainer.Container.Size = Dock.HelpInfo.Content.Size;
3549ScrollingContainer.Boundary.Size = Dock.HelpInfo.Size;
3550ScrollingContainer.Boundary.BackgroundTransparency = 1;
3551Dock.HelpInfo.Content:Destroy();
3552
3553-- Add functionality to the other GUI buttons
3554Dock.SelectionButtons.UndoButton.MouseButton1Up:connect( function ()
3555 History:undo();
3556end );
3557Dock.SelectionButtons.RedoButton.MouseButton1Up:connect( function ()
3558 History:redo();
3559end );
3560Dock.SelectionButtons.DeleteButton.MouseButton1Up:connect( function ()
3561 deleteSelection();
3562end );
3563Dock.SelectionButtons.CloneButton.MouseButton1Up:connect( function ()
3564 cloneSelection();
3565end );
3566Dock.SelectionButtons.ExportButton.MouseButton1Up:connect( function ()
3567 IE:export();
3568end );
3569Dock.InfoButtons.HelpButton.MouseButton1Up:connect( function ()
3570 toggleHelp();
3571end );
3572
3573-- Shade the buttons according to whether they'll function or not
3574Selection.Changed:connect( function ()
3575
3576 -- If there are items, they should be active
3577 if #Selection.Items > 0 then
3578 Dock.SelectionButtons.DeleteButton.Image = delete_active_decal;
3579 Dock.SelectionButtons.CloneButton.Image = clone_active_decal;
3580 Dock.SelectionButtons.ExportButton.Image = export_active_decal;
3581
3582 -- If there aren't items, they shouldn't be active
3583 else
3584 Dock.SelectionButtons.DeleteButton.Image = delete_inactive_decal;
3585 Dock.SelectionButtons.CloneButton.Image = clone_inactive_decal;
3586 Dock.SelectionButtons.ExportButton.Image = export_inactive_decal;
3587 end;
3588
3589end );
3590
3591-- Make the selection/info buttons display tooltips upon hovering over them
3592for _, SelectionButton in pairs( Dock.SelectionButtons:GetChildren() ) do
3593 SelectionButton.MouseEnter:connect( function ()
3594 if SelectionButton:FindFirstChild( 'Tooltip' ) then
3595 SelectionButton.Tooltip.Visible = true;
3596 end;
3597 end );
3598 SelectionButton.MouseLeave:connect( function ()
3599 if SelectionButton:FindFirstChild( 'Tooltip' ) then
3600 SelectionButton.Tooltip.Visible = false;
3601 end;
3602 end );
3603end;
3604Dock.InfoButtons.HelpButton.MouseEnter:connect( function ()
3605 Dock.InfoButtons.HelpButton.Tooltip.Visible = true;
3606end );
3607Dock.InfoButtons.HelpButton.MouseLeave:connect( function ()
3608 Dock.InfoButtons.HelpButton.Tooltip.Visible = false;
3609end );
3610
3611History.Changed:connect( function ()
3612
3613 -- If there are any records
3614 if #History.Data > 0 then
3615
3616 -- If we're at the beginning
3617 if History.index == 0 then
3618 Dock.SelectionButtons.UndoButton.Image = undo_inactive_decal;
3619 Dock.SelectionButtons.RedoButton.Image = redo_active_decal;
3620
3621 -- If we're at the end
3622 elseif History.index == #History.Data then
3623 Dock.SelectionButtons.UndoButton.Image = undo_active_decal;
3624 Dock.SelectionButtons.RedoButton.Image = redo_inactive_decal;
3625
3626 -- If we're neither at the beginning or the end
3627 else
3628 Dock.SelectionButtons.UndoButton.Image = undo_active_decal;
3629 Dock.SelectionButtons.RedoButton.Image = redo_active_decal;
3630 end;
3631
3632 -- If there are no records
3633 else
3634 Dock.SelectionButtons.UndoButton.Image = undo_inactive_decal;
3635 Dock.SelectionButtons.RedoButton.Image = redo_inactive_decal;
3636 end;
3637
3638end );
3639
3640------------------------------------------
3641-- Attach tool event listeners
3642------------------------------------------
3643
3644function equipBT( CurrentMouse )
3645
3646 Mouse = CurrentMouse;
3647
3648 -- Enable the move tool if there's no tool currently enabled
3649 if not CurrentTool then
3650 equipTool( Tools.Move );
3651 end;
3652
3653 if not TargetBox then
3654 TargetBox = Instance.new( "SelectionBox", UI );
3655 TargetBox.Name = "BTTargetBox";
3656 TargetBox.Color = BrickColor.new( "Institutional white" );
3657 TargetBox.Transparency = 0.5;
3658 end;
3659
3660 -- Enable any temporarily-disabled selection boxes
3661 for _, SelectionBox in pairs( SelectionBoxes ) do
3662 SelectionBox.Parent = UI;
3663 end;
3664
3665 -- Update the internal selection if this is a plugin
3666 if ToolType == 'plugin' then
3667 for _, Item in pairs( Services.Selection:Get() ) do
3668 Selection:add( Item );
3669 end;
3670 end;
3671
3672 -- Call the `Equipped` listener of the current tool
3673 if CurrentTool and CurrentTool.Listeners.Equipped then
3674 CurrentTool.Listeners.Equipped();
3675 end;
3676
3677 -- Show the dock
3678 Dock.Visible = true;
3679
3680 table.insert( Connections, Mouse.KeyDown:connect( function ( key )
3681
3682 local key = key:lower();
3683 local key_code = key:byte();
3684
3685 -- Provide the abiltiy to delete via the shift + X key combination
3686 if ActiveKeys[47] or ActiveKeys[48] and key == "x" then
3687 deleteSelection();
3688 return;
3689 end;
3690
3691 -- Provide the ability to clone via the shift + C key combination
3692 if ActiveKeys[47] or ActiveKeys[48] and key == "c" then
3693 cloneSelection();
3694 return;
3695 end;
3696
3697 -- Undo if shift+z is pressed
3698 if key == "z" and ( ActiveKeys[47] or ActiveKeys[48] ) then
3699 History:undo();
3700 return;
3701
3702 -- Redo if shift+y is pressed
3703 elseif key == "y" and ( ActiveKeys[47] or ActiveKeys[48] ) then
3704 History:redo();
3705 return;
3706 end;
3707
3708 -- Serialize and dump selection to logs if shift+p is pressed
3709 if key == "p" and ( ActiveKeys[47] or ActiveKeys[48] ) then
3710 IE:export();
3711 return;
3712 end;
3713
3714 -- Perform a prism selection if shift + k is pressed
3715 if key == "k" and ( ActiveKeys[47] or ActiveKeys[48] ) then
3716 prismSelect();
3717 return;
3718 end;
3719
3720 -- Clear the selection if shift + r is pressed
3721 if key == "r" and ( ActiveKeys[47] or ActiveKeys[48] ) then
3722 Selection:clear();
3723 return;
3724 end;
3725
3726 if key == "z" then
3727 equipTool( Tools.Move );
3728
3729 elseif key == "x" then
3730 equipTool( Tools.Resize );
3731
3732 elseif key == "c" then
3733 equipTool( Tools.Rotate );
3734
3735 elseif key == "v" then
3736 equipTool( Tools.Paint );
3737
3738 elseif key == "b" then
3739 equipTool( Tools.Surface );
3740
3741 elseif key == "n" then
3742 equipTool( Tools.Material );
3743
3744 elseif key == "m" then
3745 equipTool( Tools.Anchor );
3746
3747 elseif key == "k" then
3748 equipTool( Tools.Collision );
3749
3750 elseif key == "j" then
3751 equipTool( Tools.NewPart );
3752
3753 elseif key == "h" then
3754 equipTool( Tools.Mesh );
3755
3756 elseif key == "g" then
3757 equipTool( Tools.Texture );
3758
3759 elseif key == "f" then
3760 equipTool( Tools.Weld );
3761
3762 elseif key == "u" then
3763 equipTool( Tools.Lighting );
3764
3765 elseif key == "p" then
3766 equipTool( Tools.Decorate );
3767
3768 end;
3769
3770 ActiveKeys[key_code] = key_code;
3771 ActiveKeys[key] = key;
3772
3773 -- If it's now in multiselection mode, update `selecting`
3774 -- (these are the left/right ctrl & shift keys)
3775 if ActiveKeys[47] or ActiveKeys[48] or ActiveKeys[49] or ActiveKeys[50] then
3776 selecting = ActiveKeys[47] or ActiveKeys[48] or ActiveKeys[49] or ActiveKeys[50];
3777 end;
3778
3779 end ) );
3780
3781 table.insert( Connections, Mouse.KeyUp:connect( function ( key )
3782
3783 local key = key:lower();
3784 local key_code = key:byte();
3785
3786 ActiveKeys[key_code] = nil;
3787 ActiveKeys[key] = nil;
3788
3789 -- If it's no longer in multiselection mode, update `selecting` & related values
3790 if selecting and not ActiveKeys[selecting] then
3791 selecting = false;
3792 if Select2D.enabled then
3793 Select2D:select();
3794 Select2D:finish();
3795 end;
3796 end;
3797
3798 -- Fire tool listeners
3799 if CurrentTool and CurrentTool.Listeners.KeyUp then
3800 CurrentTool.Listeners.KeyUp( key );
3801 end;
3802
3803 end ) );
3804
3805 table.insert( Connections, Mouse.Button1Down:connect( function ()
3806
3807 clicking = true;
3808 click_x, click_y = Mouse.X, Mouse.Y;
3809
3810 -- If multiselection is, just add to the selection
3811 if selecting then
3812 return;
3813 end;
3814
3815 -- Fire tool listeners
3816 if CurrentTool and CurrentTool.Listeners.Button1Down then
3817 CurrentTool.Listeners.Button1Down();
3818 end;
3819
3820 end ) );
3821
3822 table.insert( Connections, Mouse.Move:connect( function ()
3823
3824 -- If the mouse has moved since it was clicked, start 2D selection mode
3825 if not override_selection and not Select2D.enabled and clicking and selecting and ( click_x ~= Mouse.X or click_y ~= Mouse.Y ) then
3826 Select2D:start();
3827 end;
3828
3829 -- If the target has changed, update the selectionbox appropriately
3830 if not override_selection and isSelectable( Mouse.Target ) and TargetBox.Adornee ~= Mouse.Target then
3831 TargetBox.Adornee = Mouse.Target;
3832 end;
3833
3834 -- When aiming at something invalid, don't highlight any targets
3835 if not override_selection and not isSelectable( Mouse.Target ) then
3836 TargetBox.Adornee = nil;
3837 end;
3838
3839 -- Fire tool listeners
3840 if CurrentTool and CurrentTool.Listeners.Move then
3841 CurrentTool.Listeners.Move();
3842 end;
3843
3844 if override_selection then
3845 override_selection = false;
3846 end;
3847
3848 end ) );
3849
3850 table.insert( Connections, Mouse.Button1Up:connect( function ()
3851
3852 clicking = false;
3853
3854 -- Make sure the person didn't accidentally miss a handle or something
3855 if not Select2D.enabled and ( Mouse.X ~= click_x or Mouse.Y ~= click_y ) then
3856 override_selection = true;
3857 end;
3858
3859 -- If the target when clicking was invalid then clear the selection (unless we're multi-selecting)
3860 if not override_selection and not selecting and not isSelectable( Mouse.Target ) then
3861 Selection:clear();
3862 end;
3863
3864 -- If multi-selecting, add to/remove from the selection
3865 if not override_selection and selecting then
3866
3867 -- If the item isn't already selected, add it to the selection
3868 if not Selection:find( Mouse.Target ) then
3869 if isSelectable( Mouse.Target ) then
3870 Selection:add( Mouse.Target );
3871 end;
3872
3873 -- If the item _is_ already selected, remove it from the selection
3874 else
3875 if ( Mouse.X == click_x and Mouse.Y == click_y ) and Selection:find( Mouse.Target ) then
3876 Selection:remove( Mouse.Target );
3877 end;
3878 end;
3879
3880 -- If not multi-selecting, replace the selection
3881 else
3882 if not override_selection and isSelectable( Mouse.Target ) then
3883 Selection:clear();
3884 Selection:add( Mouse.Target );
3885 end;
3886 end;
3887
3888 -- Fire tool listeners
3889 if CurrentTool and CurrentTool.Listeners.Button1Up then
3890 CurrentTool.Listeners.Button1Up();
3891 end;
3892
3893 if override_selection then
3894 override_selection = false;
3895 end;
3896
3897 end ) );
3898
3899 table.insert( Connections, Mouse.Button2Down:connect( function ()
3900 -- Fire tool listeners
3901 if CurrentTool and CurrentTool.Listeners.Button2Down then
3902 CurrentTool.Listeners.Button2Down();
3903 end;
3904 end ) );
3905
3906 table.insert( Connections, Mouse.Button2Up:connect( function ()
3907 -- Fire tool listeners
3908 if CurrentTool and CurrentTool.Listeners.Button2Up then
3909 CurrentTool.Listeners.Button2Up();
3910 end;
3911 end ) );
3912
3913end;
3914
3915function unequipBT()
3916
3917 Mouse = nil;
3918
3919 -- Remove the mouse target SelectionBox from `Player`
3920 if TargetBox then
3921 TargetBox:Destroy();
3922 TargetBox = nil;
3923 end;
3924
3925 -- Disable all the selection boxes temporarily
3926 for _, SelectionBox in pairs( SelectionBoxes ) do
3927 SelectionBox.Parent = nil;
3928 end;
3929
3930 -- Hide the dock
3931 Dock.Visible = false;
3932
3933 -- Disconnect temporary platform-related connections
3934 for connection_index, Connection in pairs( Connections ) do
3935 Connection:disconnect();
3936 Connections[connection_index] = nil;
3937 end;
3938
3939 -- Call the `Unequipped` listener of the current tool
3940 if CurrentTool and CurrentTool.Listeners.Unequipped then
3941 CurrentTool.Listeners.Unequipped();
3942 end;
3943
3944end;
3945
3946
3947------------------------------------------
3948-- Provide the platform's environment for
3949-- other tool scripts to extend upon
3950------------------------------------------
3951
3952local tool_list = {
3953 "Anchor",
3954 "Collision",
3955 "Material",
3956 "Mesh",
3957 "Move",
3958 "NewPart",
3959 "Paint",
3960 "Resize",
3961 "Rotate",
3962 "Surface",
3963 "Texture",
3964 "Weld",
3965 "Lighting",
3966 "Decorate"
3967};
3968
3969-- Make sure all the tool scripts are in the tool & deactivate them
3970for _, tool_name in pairs( tool_list ) do
3971 local script_name = "BT" .. tool_name .. "Tool";
3972 repeat wait() until script:FindFirstChild( script_name );
3973 script[script_name].Disabled = true;
3974end;
3975
3976-- Load the platform
3977if not _G.BTCoreEnv then
3978 _G.BTCoreEnv = {};
3979end;
3980_G.BTCoreEnv[Tool] = getfenv( 0 );
3981CoreReady = true;
3982
3983-- Reload the tool scripts
3984for _, tool_name in pairs( tool_list ) do
3985 local script_name = "BT" .. tool_name .. "Tool";
3986 script[script_name].Disabled = false;
3987end;
3988
3989-- Wait for all the tools to load
3990for _, tool_name in pairs( tool_list ) do
3991 if not Tools[tool_name] then
3992 repeat wait() until Tools[tool_name];
3993 end;
3994 repeat wait() until Tools[tool_name].Loaded;
3995end;
3996
3997-- Activate the plugin and tool connections
3998if ToolType == 'plugin' then
3999 local ToolbarButton = plugin:CreateToolbar( 'Building Tools by F3X' ):CreateButton( '', 'Building Tools by F3X', plugin_icon );
4000 local plugin_active = false;
4001 ToolbarButton.Click:connect( function ()
4002 if plugin_active then
4003 plugin_active = false;
4004 unequipBT();
4005 else
4006 plugin_active = true;
4007 plugin:Activate( true );
4008 equipBT( plugin:GetMouse() );
4009 end;
4010 end );
4011 plugin.Deactivation:connect( unequipBT );
4012
4013elseif ToolType == 'tool' then
4014 Tool.Equipped:connect( equipBT );
4015 Tool.Unequipped:connect( unequipBT );
4016end;
4017end))
4018LocalScript2.Name = "BTMoveTool"
4019LocalScript2.Parent = LocalScript1
4020table.insert(cors,sandbox(LocalScript2,function()
4021-- Load the main tool's core environment when it's ready
4022repeat wait() until (
4023 _G.BTCoreEnv and
4024 _G.BTCoreEnv[script.Parent.Parent] and
4025 _G.BTCoreEnv[script.Parent.Parent].CoreReady
4026);
4027setfenv( 1, _G.BTCoreEnv[script.Parent.Parent] );
4028
4029------------------------------------------
4030-- Move tool
4031------------------------------------------
4032
4033-- Create the main container for this tool
4034Tools.Move = {};
4035
4036-- Define the color of the tool
4037Tools.Move.Color = BrickColor.new( "Deep orange" );
4038
4039-- Keep a container for temporary connections
4040Tools.Move.Connections = {};
4041
4042-- Keep options in a container too
4043Tools.Move.Options = {
4044 ["increment"] = 1;
4045 ["axes"] = "global";
4046};
4047
4048-- Keep internal state data in its own container
4049Tools.Move.State = {
4050 ["distance_moved"] = 0;
4051 ["moving"] = false;
4052 ["PreMove"] = {};
4053};
4054
4055-- Add listeners
4056Tools.Move.Listeners = {};
4057
4058Tools.Move.Listeners.Equipped = function ()
4059
4060 local self = Tools.Move;
4061
4062 -- Make sure the tool is actually being equipped (because this is the default tool)
4063 if not Mouse then
4064 return;
4065 end;
4066
4067 -- Change the color of selection boxes temporarily
4068 self.State.PreviousSelectionBoxColor = SelectionBoxColor;
4069 SelectionBoxColor = self.Color;
4070 updateSelectionBoxColor();
4071
4072 -- Reveal the GUI
4073 self:showGUI();
4074
4075 -- Create the boundingbox if it doesn't already exist
4076 if not self.BoundingBox then
4077 self.BoundingBox = RbxUtility.Create "Part" {
4078 Name = "BTBoundingBox";
4079 CanCollide = false;
4080 Transparency = 1;
4081 Anchored = true;
4082 };
4083 end;
4084 Mouse.TargetFilter = self.BoundingBox;
4085
4086 -- Refresh the axis type option
4087 self:changeAxes( self.Options.axes );
4088
4089 -- Listen for any keystrokes that might affect any dragging operation
4090 self.Connections.DraggerKeyListener = Mouse.KeyDown:connect( function ( key )
4091
4092 local key = key:lower();
4093
4094 -- Make sure a dragger exists
4095 if not self.Dragger then
4096 return;
4097 end;
4098
4099 -- Rotate along the Z axis if `r` is pressed
4100 if key == "r" then
4101 self.Dragger:AxisRotate( Enum.Axis.Z );
4102
4103 -- Rotate along the X axis if `t` is pressed
4104 elseif key == "t" then
4105 self.Dragger:AxisRotate( Enum.Axis.X );
4106
4107 -- Rotate along the Y axis if `y` is pressed
4108 elseif key == "y" then
4109 self.Dragger:AxisRotate( Enum.Axis.Y );
4110 end;
4111
4112 -- Simulate a mouse move so that it applies the changes
4113 self.Dragger:MouseMove( Mouse.UnitRay );
4114
4115 end );
4116
4117 -- Oh, and update the boundingbox and the GUI regularly
4118 coroutine.wrap( function ()
4119 updater_on = true;
4120
4121 -- Provide a function to stop the loop
4122 self.Updater = function ()
4123 updater_on = false;
4124 end;
4125
4126 while wait( 0.1 ) and updater_on do
4127
4128 -- Make sure the tool's equipped
4129 if CurrentTool == self then
4130
4131 -- Update the GUI if it's visible
4132 if self.GUI and self.GUI.Visible then
4133 self:updateGUI();
4134 end;
4135
4136 -- Update the boundingbox if it's visible
4137 if self.Options.axes == "global" then
4138 self:updateBoundingBox();
4139 end;
4140
4141 end;
4142
4143 end;
4144
4145 end )();
4146
4147end;
4148
4149Tools.Move.Listeners.Unequipped = function ()
4150
4151 local self = Tools.Move;
4152
4153 -- Stop the update loop
4154 if self.Updater then
4155 self.Updater();
4156 self.Updater = nil;
4157 end;
4158
4159 -- Hide the GUI
4160 self:hideGUI();
4161
4162 -- Hide the handles
4163 self:hideHandles();
4164
4165 -- Clear out any temporary connections
4166 for connection_index, Connection in pairs( self.Connections ) do
4167 Connection:disconnect();
4168 self.Connections[connection_index] = nil;
4169 end;
4170
4171 -- Restore the original color of the selection boxes
4172 SelectionBoxColor = self.State.PreviousSelectionBoxColor;
4173 updateSelectionBoxColor();
4174
4175end;
4176
4177Tools.Move.updateGUI = function ( self )
4178
4179 if self.GUI then
4180 local GUI = self.GUI;
4181
4182 if #Selection.Items > 0 then
4183
4184 -- Look for identical numbers in each axis
4185 local position_x, position_y, position_z = nil, nil, nil;
4186 for item_index, Item in pairs( Selection.Items ) do
4187
4188 -- Set the first values for the first item
4189 if item_index == 1 then
4190 position_x, position_y, position_z = _round( Item.Position.x, 2 ), _round( Item.Position.y, 2 ), _round( Item.Position.z, 2 );
4191
4192 -- Otherwise, compare them and set them to `nil` if they're not identical
4193 else
4194 if position_x ~= _round( Item.Position.x, 2 ) then
4195 position_x = nil;
4196 end;
4197 if position_y ~= _round( Item.Position.y, 2 ) then
4198 position_y = nil;
4199 end;
4200 if position_z ~= _round( Item.Position.z, 2 ) then
4201 position_z = nil;
4202 end;
4203 end;
4204
4205 end;
4206
4207 -- If each position along each axis is the same, display that number; otherwise, display "*"
4208 if not self.State.pos_x_focused then
4209 GUI.Info.Center.X.TextBox.Text = position_x and tostring( position_x ) or "*";
4210 end;
4211 if not self.State.pos_y_focused then
4212 GUI.Info.Center.Y.TextBox.Text = position_y and tostring( position_y ) or "*";
4213 end;
4214 if not self.State.pos_z_focused then
4215 GUI.Info.Center.Z.TextBox.Text = position_z and tostring( position_z ) or "*";
4216 end;
4217
4218 GUI.Info.Visible = true;
4219 else
4220 GUI.Info.Visible = false;
4221 end;
4222
4223 if self.State.distance_moved then
4224 GUI.Changes.Text.Text = "moved " .. tostring( self.State.distance_moved ) .. " studs";
4225 GUI.Changes.Position = GUI.Info.Visible and UDim2.new( 0, 5, 0, 165 ) or UDim2.new( 0, 5, 0, 100 );
4226 GUI.Changes.Visible = true;
4227 else
4228 GUI.Changes.Text.Text = "";
4229 GUI.Changes.Visible = false;
4230 end;
4231 end;
4232
4233end;
4234
4235Tools.Move.changePosition = function ( self, component, new_value )
4236
4237 self:startHistoryRecord();
4238
4239 -- Change the position of each item selected
4240 for _, Item in pairs( Selection.Items ) do
4241 Item.CFrame = CFrame.new(
4242 component == 'x' and new_value or Item.Position.x,
4243 component == 'y' and new_value or Item.Position.y,
4244 component == 'z' and new_value or Item.Position.z
4245 ) * CFrame.Angles( Item.CFrame:toEulerAnglesXYZ() );
4246 end;
4247
4248 self:finishHistoryRecord();
4249
4250end;
4251
4252Tools.Move.startHistoryRecord = function ( self )
4253
4254 if self.State.HistoryRecord then
4255 self.State.HistoryRecord = nil;
4256 end;
4257
4258 -- Create a history record
4259 self.State.HistoryRecord = {
4260 targets = _cloneTable( Selection.Items );
4261 initial_positions = {};
4262 terminal_positions = {};
4263 unapply = function ( self )
4264 Selection:clear();
4265 for _, Target in pairs( self.targets ) do
4266 if Target then
4267 Target.CFrame = self.initial_positions[Target];
4268 Target:MakeJoints();
4269 Selection:add( Target );
4270 end;
4271 end;
4272 end;
4273 apply = function ( self )
4274 Selection:clear();
4275 for _, Target in pairs( self.targets ) do
4276 if Target then
4277 Target.CFrame = self.terminal_positions[Target];
4278 Target:MakeJoints();
4279 Selection:add( Target );
4280 end;
4281 end;
4282 end;
4283 };
4284 for _, Item in pairs( self.State.HistoryRecord.targets ) do
4285 if Item then
4286 self.State.HistoryRecord.initial_positions[Item] = Item.CFrame;
4287 end;
4288 end;
4289
4290end;
4291
4292Tools.Move.finishHistoryRecord = function ( self )
4293
4294 if not self.State.HistoryRecord then
4295 return;
4296 end;
4297
4298 for _, Item in pairs( self.State.HistoryRecord.targets ) do
4299 if Item then
4300 self.State.HistoryRecord.terminal_positions[Item] = Item.CFrame;
4301 end;
4302 end;
4303 History:add( self.State.HistoryRecord );
4304 self.State.HistoryRecord = nil;
4305
4306end;
4307
4308Tools.Move.Listeners.Button1Down = function ()
4309
4310 local self = Tools.Move;
4311
4312 local Target = self.ManualTarget or Mouse.Target;
4313 self.ManualTarget = nil;
4314
4315 -- If an unselected part is being moved, switch to it
4316 if not Selection:find( Target ) and isSelectable( Target ) then
4317 Selection:clear();
4318 Selection:add( Target );
4319 end;
4320
4321 -- If the unselected target can't be selected at all, ignore the rest of the procedure
4322 if not Selection:find( Target ) then
4323 return;
4324 end;
4325
4326 for _, Item in pairs( Selection.Items ) do
4327 Item.RotVelocity = Vector3.new( 0, 0, 0 );
4328 Item.Velocity = Vector3.new( 0, 0, 0 );
4329 end;
4330
4331 self:startHistoryRecord();
4332
4333 self.State.dragging = true;
4334 override_selection = true;
4335
4336 self.Dragger = Instance.new( "Dragger" );
4337 self.Dragger:MouseDown( Target, Target.CFrame:toObjectSpace( CFrame.new( Mouse.Hit.p ) ).p, Selection.Items );
4338 self.Connections.DraggerConnection = Mouse.Button1Up:connect( function ()
4339
4340 override_selection = true;
4341
4342 -- Disable the dragger
4343 if self.Connections.DraggerConnection then
4344 self.Connections.DraggerConnection:disconnect();
4345 self.Connections.DraggerConnection = nil;
4346 end;
4347 if not self.Dragger then
4348 return;
4349 end;
4350 self.Dragger:MouseUp();
4351 self.State.dragging = false;
4352 self.Dragger:Destroy();
4353 self.Dragger = nil;
4354
4355 self:finishHistoryRecord();
4356
4357 end );
4358
4359end;
4360
4361Tools.Move.Listeners.Move = function ()
4362
4363 local self = Tools.Move;
4364
4365 if not self.Dragger then
4366 return;
4367 end;
4368
4369 override_selection = true;
4370
4371 self.Dragger:MouseMove( Mouse.UnitRay );
4372
4373end;
4374
4375Tools.Move.showGUI = function ( self )
4376
4377 -- Initialize the GUI if it's not ready yet
4378 if not self.GUI then
4379
4380 local Container = Tool.Interfaces:WaitForChild( "BTMoveToolGUI" ):Clone();
4381 Container.Parent = UI;
4382
4383 -- Change the axis type option when the button is clicked
4384 Container.AxesOption.Global.Button.MouseButton1Down:connect( function ()
4385 self:changeAxes( "global" );
4386 Container.AxesOption.Global.SelectedIndicator.BackgroundTransparency = 0;
4387 Container.AxesOption.Global.Background.Image = dark_slanted_rectangle;
4388 Container.AxesOption.Local.SelectedIndicator.BackgroundTransparency = 1;
4389 Container.AxesOption.Local.Background.Image = light_slanted_rectangle;
4390 Container.AxesOption.Last.SelectedIndicator.BackgroundTransparency = 1;
4391 Container.AxesOption.Last.Background.Image = light_slanted_rectangle;
4392 end );
4393
4394 Container.AxesOption.Local.Button.MouseButton1Down:connect( function ()
4395 self:changeAxes( "local" );
4396 Container.AxesOption.Global.SelectedIndicator.BackgroundTransparency = 1;
4397 Container.AxesOption.Global.Background.Image = light_slanted_rectangle;
4398 Container.AxesOption.Local.SelectedIndicator.BackgroundTransparency = 0;
4399 Container.AxesOption.Local.Background.Image = dark_slanted_rectangle;
4400 Container.AxesOption.Last.SelectedIndicator.BackgroundTransparency = 1;
4401 Container.AxesOption.Last.Background.Image = light_slanted_rectangle;
4402 end );
4403
4404 Container.AxesOption.Last.Button.MouseButton1Down:connect( function ()
4405 self:changeAxes( "last" );
4406 Container.AxesOption.Global.SelectedIndicator.BackgroundTransparency = 1;
4407 Container.AxesOption.Global.Background.Image = light_slanted_rectangle;
4408 Container.AxesOption.Local.SelectedIndicator.BackgroundTransparency = 1;
4409 Container.AxesOption.Local.Background.Image = light_slanted_rectangle;
4410 Container.AxesOption.Last.SelectedIndicator.BackgroundTransparency = 0;
4411 Container.AxesOption.Last.Background.Image = dark_slanted_rectangle;
4412 end );
4413
4414 -- Change the increment option when the value of the textbox is updated
4415 Container.IncrementOption.Increment.TextBox.FocusLost:connect( function ( enter_pressed )
4416 self.Options.increment = tonumber( Container.IncrementOption.Increment.TextBox.Text ) or self.Options.increment;
4417 Container.IncrementOption.Increment.TextBox.Text = tostring( self.Options.increment );
4418 end );
4419
4420 -- Add functionality to the position inputs
4421 Container.Info.Center.X.TextButton.MouseButton1Down:connect( function ()
4422 self.State.pos_x_focused = true;
4423 Container.Info.Center.X.TextBox:CaptureFocus();
4424 end );
4425 Container.Info.Center.X.TextBox.FocusLost:connect( function ( enter_pressed )
4426 local potential_new = tonumber( Container.Info.Center.X.TextBox.Text );
4427 if potential_new then
4428 self:changePosition( 'x', potential_new );
4429 end;
4430 self.State.pos_x_focused = false;
4431 end );
4432 Container.Info.Center.Y.TextButton.MouseButton1Down:connect( function ()
4433 self.State.pos_y_focused = true;
4434 Container.Info.Center.Y.TextBox:CaptureFocus();
4435 end );
4436 Container.Info.Center.Y.TextBox.FocusLost:connect( function ( enter_pressed )
4437 local potential_new = tonumber( Container.Info.Center.Y.TextBox.Text );
4438 if potential_new then
4439 self:changePosition( 'y', potential_new );
4440 end;
4441 self.State.pos_y_focused = false;
4442 end );
4443 Container.Info.Center.Z.TextButton.MouseButton1Down:connect( function ()
4444 self.State.pos_z_focused = true;
4445 Container.Info.Center.Z.TextBox:CaptureFocus();
4446 end );
4447 Container.Info.Center.Z.TextBox.FocusLost:connect( function ( enter_pressed )
4448 local potential_new = tonumber( Container.Info.Center.Z.TextBox.Text );
4449 if potential_new then
4450 self:changePosition( 'z', potential_new );
4451 end;
4452 self.State.pos_z_focused = false;
4453 end );
4454
4455 self.GUI = Container;
4456 end;
4457
4458 -- Reveal the GUI
4459 self.GUI.Visible = true;
4460
4461end;
4462
4463Tools.Move.hideGUI = function ( self )
4464
4465 -- Hide the GUI if it exists
4466 if self.GUI then
4467 self.GUI.Visible = false;
4468 end;
4469
4470end;
4471
4472Tools.Move.showHandles = function ( self, Part )
4473
4474 -- Create the handles if they don't exist yet
4475 if not self.Handles then
4476
4477 -- Create the object
4478 self.Handles = RbxUtility.Create "Handles" {
4479 Name = "BTMovementHandles";
4480 Color = self.Color;
4481 Parent = GUIContainer;
4482 };
4483
4484 -- Add functionality to the handles
4485
4486 self.Handles.MouseButton1Down:connect( function ()
4487
4488 -- Prevent the platform from thinking we're selecting
4489 override_selection = true;
4490 self.State.moving = true;
4491
4492 -- Clear the change stats
4493 self.State.distance_moved = 0;
4494
4495 self:startHistoryRecord();
4496
4497 -- Do a few things to the selection before manipulating it
4498 for _, Item in pairs( Selection.Items ) do
4499
4500 -- Keep a copy of the state of each item
4501 self.State.PreMove[Item] = Item:Clone();
4502
4503 -- Anchor each item
4504 Item.Anchored = true;
4505
4506 end;
4507
4508 -- Return stuff to normal once the mouse button is released
4509 self.Connections.HandleReleaseListener = Mouse.Button1Up:connect( function ()
4510
4511 -- Prevent the platform from thinking we're selecting
4512 override_selection = true;
4513 self.State.moving = false;
4514
4515 -- Stop this connection from firing again
4516 if self.Connections.HandleReleaseListener then
4517 self.Connections.HandleReleaseListener:disconnect();
4518 self.Connections.HandleReleaseListener = nil;
4519 end;
4520
4521 self:finishHistoryRecord();
4522
4523 -- Restore properties that may have been changed temporarily
4524 -- from the pre-movement state copies
4525 for Item, PreviousItemState in pairs( self.State.PreMove ) do
4526 Item.Anchored = PreviousItemState.Anchored;
4527 self.State.PreMove[Item] = nil;
4528 Item:MakeJoints();
4529 Item.Velocity = Vector3.new( 0, 0, 0 );
4530 Item.RotVelocity = Vector3.new( 0, 0, 0 );
4531 end;
4532
4533 end );
4534
4535 end );
4536
4537 self.Handles.MouseDrag:connect( function ( face, drag_distance )
4538
4539 -- Calculate which multiple of the increment to use based on the current drag distance's
4540 -- proximity to their nearest upper and lower multiples
4541
4542 local difference = drag_distance % self.Options.increment;
4543
4544 local lower_degree = drag_distance - difference;
4545 local upper_degree = drag_distance - difference + self.Options.increment;
4546
4547 local lower_degree_proximity = math.abs( drag_distance - lower_degree );
4548 local upper_degree_proximity = math.abs( drag_distance - upper_degree );
4549
4550 if lower_degree_proximity <= upper_degree_proximity then
4551 drag_distance = lower_degree;
4552 else
4553 drag_distance = upper_degree;
4554 end;
4555
4556 local increase = drag_distance;
4557
4558 self.State.distance_moved = drag_distance;
4559
4560 -- Increment the position of each selected item in the direction of `face`
4561 for _, Item in pairs( Selection.Items ) do
4562
4563 -- Remove any joints connected with `Item` so that it can freely move
4564 Item:BreakJoints();
4565
4566 -- Update the position of `Item` depending on the type of axes that is currently set
4567 if face == Enum.NormalId.Top then
4568 if self.Options.axes == "global" then
4569 Item.CFrame = CFrame.new( self.State.PreMove[Item].CFrame.p ):toWorldSpace( CFrame.new( 0, increase, 0 ) ) * CFrame.Angles( self.State.PreMove[Item].CFrame:toEulerAnglesXYZ() );
4570 elseif self.Options.axes == "local" then
4571 Item.CFrame = self.State.PreMove[Item].CFrame:toWorldSpace( CFrame.new( 0, increase, 0 ) );
4572 elseif self.Options.axes == "last" then
4573 Item.CFrame = self.State.PreMove[Selection.Last].CFrame:toWorldSpace( CFrame.new( 0, increase, 0 ) ):toWorldSpace( self.State.PreMove[Item].CFrame:toObjectSpace( self.State.PreMove[Selection.Last].CFrame ):inverse() );
4574 end;
4575
4576 elseif face == Enum.NormalId.Bottom then
4577 if self.Options.axes == "global" then
4578 Item.CFrame = CFrame.new( self.State.PreMove[Item].CFrame.p ):toWorldSpace( CFrame.new( 0, -increase, 0 ) ) * CFrame.Angles( self.State.PreMove[Item].CFrame:toEulerAnglesXYZ() );
4579 elseif self.Options.axes == "local" then
4580 Item.CFrame = self.State.PreMove[Item].CFrame:toWorldSpace( CFrame.new( 0, -increase, 0 ) );
4581 elseif self.Options.axes == "last" then
4582 Item.CFrame = self.State.PreMove[Selection.Last].CFrame:toWorldSpace( CFrame.new( 0, -increase, 0 ) ):toWorldSpace( self.State.PreMove[Item].CFrame:toObjectSpace( self.State.PreMove[Selection.Last].CFrame ):inverse() );
4583 end;
4584
4585 elseif face == Enum.NormalId.Front then
4586 if self.Options.axes == "global" then
4587 Item.CFrame = CFrame.new( self.State.PreMove[Item].CFrame.p ):toWorldSpace( CFrame.new( 0, 0, -increase ) ) * CFrame.Angles( self.State.PreMove[Item].CFrame:toEulerAnglesXYZ() );
4588 elseif self.Options.axes == "local" then
4589 Item.CFrame = self.State.PreMove[Item].CFrame:toWorldSpace( CFrame.new( 0, 0, -increase ) );
4590 elseif self.Options.axes == "last" then
4591 Item.CFrame = self.State.PreMove[Selection.Last].CFrame:toWorldSpace( CFrame.new( 0, 0, -increase ) ):toWorldSpace( self.State.PreMove[Item].CFrame:toObjectSpace( self.State.PreMove[Selection.Last].CFrame ):inverse() );
4592 end;
4593
4594 elseif face == Enum.NormalId.Back then
4595 if self.Options.axes == "global" then
4596 Item.CFrame = CFrame.new( self.State.PreMove[Item].CFrame.p ):toWorldSpace( CFrame.new( 0, 0, increase ) ) * CFrame.Angles( self.State.PreMove[Item].CFrame:toEulerAnglesXYZ() );
4597 elseif self.Options.axes == "local" then
4598 Item.CFrame = self.State.PreMove[Item].CFrame:toWorldSpace( CFrame.new( 0, 0, increase ) );
4599 elseif self.Options.axes == "last" then
4600 Item.CFrame = self.State.PreMove[Selection.Last].CFrame:toWorldSpace( CFrame.new( 0, 0, increase ) ):toWorldSpace( self.State.PreMove[Item].CFrame:toObjectSpace( self.State.PreMove[Selection.Last].CFrame ):inverse() );
4601 end;
4602
4603 elseif face == Enum.NormalId.Right then
4604 if self.Options.axes == "global" then
4605 Item.CFrame = CFrame.new( self.State.PreMove[Item].CFrame.p ):toWorldSpace( CFrame.new( increase, 0, 0 ) ) * CFrame.Angles( self.State.PreMove[Item].CFrame:toEulerAnglesXYZ() );
4606 elseif self.Options.axes == "local" then
4607 Item.CFrame = self.State.PreMove[Item].CFrame:toWorldSpace( CFrame.new( increase, 0, 0 ) );
4608 elseif self.Options.axes == "last" then
4609 Item.CFrame = self.State.PreMove[Selection.Last].CFrame:toWorldSpace( CFrame.new( increase, 0, 0 ) ):toWorldSpace( self.State.PreMove[Item].CFrame:toObjectSpace( self.State.PreMove[Selection.Last].CFrame ):inverse() );
4610 end;
4611
4612 elseif face == Enum.NormalId.Left then
4613 if self.Options.axes == "global" then
4614 Item.CFrame = CFrame.new( self.State.PreMove[Item].CFrame.p ):toWorldSpace( CFrame.new( -increase, 0, 0 ) ) * CFrame.Angles( self.State.PreMove[Item].CFrame:toEulerAnglesXYZ() );
4615 elseif self.Options.axes == "local" then
4616 Item.CFrame = self.State.PreMove[Item].CFrame:toWorldSpace( CFrame.new( -increase, 0, 0 ) );
4617 elseif self.Options.axes == "last" then
4618 Item.CFrame = self.State.PreMove[Selection.Last].CFrame:toWorldSpace( CFrame.new( -increase, 0, 0 ) ):toWorldSpace( self.State.PreMove[Item].CFrame:toObjectSpace( self.State.PreMove[Selection.Last].CFrame ):inverse() );
4619 end;
4620
4621 end;
4622
4623 end;
4624
4625 end );
4626
4627 end;
4628
4629 -- Stop listening for the existence of the previous adornee (if any)
4630 if self.Connections.AdorneeExistenceListener then
4631 self.Connections.AdorneeExistenceListener:disconnect();
4632 self.Connections.AdorneeExistenceListener = nil;
4633 end;
4634
4635 -- Attach the handles to `Part`
4636 self.Handles.Adornee = Part;
4637
4638 -- Make sure to hide the handles if `Part` suddenly stops existing
4639 self.Connections.AdorneeExistenceListener = Part.AncestryChanged:connect( function ( Object, NewParent )
4640
4641 -- Make sure this change in parent applies directly to `Part`
4642 if Object ~= Part then
4643 return;
4644 end;
4645
4646 -- Show the handles according to the existence of the part
4647 if NewParent == nil then
4648 self:hideHandles();
4649 else
4650 self:showHandles( Part );
4651 end;
4652
4653 end );
4654
4655end;
4656
4657Tools.Move.hideHandles = function ( self )
4658
4659 -- Hide the handles if they exist
4660 if self.Handles then
4661 self.Handles.Adornee = nil;
4662 end;
4663
4664end;
4665
4666Tools.Move.updateBoundingBox = function ( self )
4667
4668 if #Selection.Items > 0 and not self.State.dragging then
4669 local SelectionSize, SelectionPosition = _getCollectionInfo( Selection.Items );
4670 self.BoundingBox.Size = SelectionSize;
4671 self.BoundingBox.CFrame = SelectionPosition;
4672 self:showHandles( self.BoundingBox );
4673
4674 else
4675 self:hideHandles();
4676 end;
4677
4678end;
4679
4680Tools.Move.changeAxes = function ( self, new_axes )
4681
4682 -- Have a quick reference to the GUI (if any)
4683 local AxesOptionGUI = self.GUI and self.GUI.AxesOption or nil;
4684
4685 -- Disconnect any handle-related listeners that are specific to a certain axes option
4686
4687 if self.Connections.HandleFocusChangeListener then
4688 self.Connections.HandleFocusChangeListener:disconnect();
4689 self.Connections.HandleFocusChangeListener = nil;
4690 end;
4691
4692 if self.Connections.HandleSelectionChangeListener then
4693 self.Connections.HandleSelectionChangeListener:disconnect();
4694 self.Connections.HandleSelectionChangeListener = nil;
4695 end;
4696
4697 if new_axes == "global" then
4698
4699 -- Update the options
4700 self.Options.axes = "global";
4701
4702 -- Clear out any previous adornee
4703 self:hideHandles();
4704
4705 -- Focus the handles on the boundary box
4706 self:showHandles( self.BoundingBox );
4707
4708 -- Update the GUI's option panel
4709 if self.GUI then
4710 AxesOptionGUI.Global.SelectedIndicator.BackgroundTransparency = 0;
4711 AxesOptionGUI.Global.Background.Image = dark_slanted_rectangle;
4712 AxesOptionGUI.Local.SelectedIndicator.BackgroundTransparency = 1;
4713 AxesOptionGUI.Local.Background.Image = light_slanted_rectangle;
4714 AxesOptionGUI.Last.SelectedIndicator.BackgroundTransparency = 1;
4715 AxesOptionGUI.Last.Background.Image = light_slanted_rectangle;
4716 end;
4717
4718 end;
4719
4720 if new_axes == "local" then
4721
4722 -- Update the options
4723 self.Options.axes = "local";
4724
4725 -- Always have the handles on the most recent addition to the selection
4726 self.Connections.HandleSelectionChangeListener = Selection.Changed:connect( function ()
4727
4728 -- Clear out any previous adornee
4729 self:hideHandles();
4730
4731 -- If there /is/ a last item in the selection, attach the handles to it
4732 if Selection.Last then
4733 self:showHandles( Selection.Last );
4734 end;
4735
4736 end );
4737
4738 -- Switch the adornee of the handles if the second mouse button is pressed
4739 self.Connections.HandleFocusChangeListener = Mouse.Button2Up:connect( function ()
4740
4741 -- Make sure the platform doesn't think we're selecting
4742 override_selection = true;
4743
4744 -- If the target is in the selection, make it the new adornee
4745 if Selection:find( Mouse.Target ) then
4746 Selection:focus( Mouse.Target );
4747 self:showHandles( Mouse.Target );
4748 end;
4749
4750 end );
4751
4752 -- Finally, attach the handles to the last item added to the selection (if any)
4753 if Selection.Last then
4754 self:showHandles( Selection.Last );
4755 end;
4756
4757 -- Update the GUI's option panel
4758 if self.GUI then
4759 AxesOptionGUI.Global.SelectedIndicator.BackgroundTransparency = 1;
4760 AxesOptionGUI.Global.Background.Image = light_slanted_rectangle;
4761 AxesOptionGUI.Local.SelectedIndicator.BackgroundTransparency = 0;
4762 AxesOptionGUI.Local.Background.Image = dark_slanted_rectangle;
4763 AxesOptionGUI.Last.SelectedIndicator.BackgroundTransparency = 1;
4764 AxesOptionGUI.Last.Background.Image = light_slanted_rectangle;
4765 end;
4766
4767 end;
4768
4769 if new_axes == "last" then
4770
4771 -- Update the options
4772 self.Options.axes = "last";
4773
4774 -- Always have the handles on the most recent addition to the selection
4775 self.Connections.HandleSelectionChangeListener = Selection.Changed:connect( function ()
4776
4777 -- Clear out any previous adornee
4778 self:hideHandles();
4779
4780 -- If there /is/ a last item in the selection, attach the handles to it
4781 if Selection.Last then
4782 self:showHandles( Selection.Last );
4783 end;
4784
4785 end );
4786
4787 -- Switch the adornee of the handles if the second mouse button is pressed
4788 self.Connections.HandleFocusChangeListener = Mouse.Button2Up:connect( function ()
4789
4790 -- Make sure the platform doesn't think we're selecting
4791 override_selection = true;
4792
4793 -- If the target is in the selection, make it the new adornee
4794 if Selection:find( Mouse.Target ) then
4795 Selection:focus( Mouse.Target );
4796 self:showHandles( Mouse.Target );
4797 end;
4798
4799 end );
4800
4801 -- Finally, attach the handles to the last item added to the selection (if any)
4802 if Selection.Last then
4803 self:showHandles( Selection.Last );
4804 end;
4805
4806 -- Update the GUI's option panel
4807 if self.GUI then
4808 AxesOptionGUI.Global.SelectedIndicator.BackgroundTransparency = 1;
4809 AxesOptionGUI.Global.Background.Image = light_slanted_rectangle;
4810 AxesOptionGUI.Local.SelectedIndicator.BackgroundTransparency = 1;
4811 AxesOptionGUI.Local.Background.Image = light_slanted_rectangle;
4812 AxesOptionGUI.Last.SelectedIndicator.BackgroundTransparency = 0;
4813 AxesOptionGUI.Last.Background.Image = dark_slanted_rectangle;
4814 end;
4815
4816 end;
4817
4818end;
4819
4820Tools.Move.Loaded = true;
4821end))
4822LocalScript2.Disabled = true
4823LocalScript3.Name = "BTResizeTool"
4824LocalScript3.Parent = LocalScript1
4825table.insert(cors,sandbox(LocalScript3,function()
4826-- Load the main tool's core environment when it's ready
4827repeat wait() until (
4828 _G.BTCoreEnv and
4829 _G.BTCoreEnv[script.Parent.Parent] and
4830 _G.BTCoreEnv[script.Parent.Parent].CoreReady
4831);
4832setfenv( 1, _G.BTCoreEnv[script.Parent.Parent] );
4833
4834------------------------------------------
4835-- Resize tool
4836------------------------------------------
4837
4838-- Create the tool
4839Tools.Resize = {};
4840
4841-- Create structures that will be used within the tool
4842Tools.Resize.Connections = {};
4843
4844Tools.Resize.Options = {
4845 ["increment"] = 1;
4846 ["directions"] = "normal";
4847};
4848
4849Tools.Resize.State = {
4850 ["PreResize"] = {};
4851 ["previous_distance"] = 0;
4852 ["resizing"] = false;
4853 ["length_resized"] = 0;
4854};
4855
4856Tools.Resize.Listeners = {};
4857
4858-- Define the color of the tool
4859Tools.Resize.Color = BrickColor.new( "Cyan" );
4860
4861Tools.Resize.Listeners.Equipped = function ()
4862
4863 local self = Tools.Resize;
4864
4865 -- Change the color of selection boxes temporarily
4866 self.State.PreviousSelectionBoxColor = SelectionBoxColor;
4867 SelectionBoxColor = self.Color;
4868 updateSelectionBoxColor();
4869
4870 -- Reveal the GUI
4871 self:showGUI();
4872
4873 -- Always have the handles on the most recent addition to the selection
4874 table.insert( self.Connections, Selection.Changed:connect( function ()
4875
4876 -- Clear out any previous adornee
4877 self:hideHandles();
4878
4879 -- If there /is/ a last item in the selection, attach the handles to it
4880 if Selection.Last then
4881 self:showHandles( Selection.Last );
4882 end;
4883
4884 end ) );
4885
4886 -- Switch the adornee of the handles if the second mouse button is pressed
4887 table.insert( self.Connections, Mouse.Button2Up:connect( function ()
4888
4889 -- Make sure the platform doesn't think we're selecting
4890 override_selection = true;
4891
4892 -- If the target is in the selection, make it the new adornee
4893 if Selection:find( Mouse.Target ) then
4894 Selection:focus( Mouse.Target );
4895 end;
4896
4897 end ) );
4898
4899 -- Finally, attach the handles to the last item added to the selection (if any)
4900 if Selection.Last then
4901 self:showHandles( Selection.Last );
4902 end;
4903
4904 -- Update the GUI regularly
4905 coroutine.wrap( function ()
4906 updater_on = true;
4907
4908 -- Provide a function to stop the loop
4909 self.Updater = function ()
4910 updater_on = false;
4911 end;
4912
4913 while wait( 0.1 ) and updater_on do
4914
4915 -- Make sure the tool's equipped
4916 if CurrentTool == self then
4917
4918 -- Update the GUI if it's visible
4919 if self.GUI and self.GUI.Visible then
4920 self:updateGUI();
4921 end;
4922
4923 end;
4924
4925 end;
4926
4927 end )();
4928
4929end;
4930
4931Tools.Resize.Listeners.Unequipped = function ()
4932
4933 local self = Tools.Resize;
4934
4935 -- Stop the update loop
4936 if self.Updater then
4937 self.Updater();
4938 self.Updater = nil;
4939 end;
4940
4941 -- Hide the GUI
4942 self:hideGUI();
4943
4944 -- Hide the handles
4945 self:hideHandles();
4946
4947 -- Clear out any temporary connections
4948 for connection_index, Connection in pairs( self.Connections ) do
4949 Connection:disconnect();
4950 self.Connections[connection_index] = nil;
4951 end;
4952
4953 -- Restore the original color of the selection boxes
4954 SelectionBoxColor = self.State.PreviousSelectionBoxColor;
4955 updateSelectionBoxColor();
4956
4957end;
4958
4959Tools.Resize.showGUI = function ( self )
4960
4961 -- Initialize the GUI if it's not ready yet
4962 if not self.GUI then
4963
4964 local Container = Tool.Interfaces:WaitForChild( "BTResizeToolGUI" ):Clone();
4965 Container.Parent = UI;
4966
4967 -- Change the axis type option when the button is clicked
4968 Container.DirectionsOption.Normal.Button.MouseButton1Down:connect( function ()
4969 self.Options.directions = "normal";
4970 Container.DirectionsOption.Normal.SelectedIndicator.BackgroundTransparency = 0;
4971 Container.DirectionsOption.Normal.Background.Image = dark_slanted_rectangle;
4972 Container.DirectionsOption.Both.SelectedIndicator.BackgroundTransparency = 1;
4973 Container.DirectionsOption.Both.Background.Image = light_slanted_rectangle;
4974 end );
4975
4976 Container.DirectionsOption.Both.Button.MouseButton1Down:connect( function ()
4977 self.Options.directions = "both";
4978 Container.DirectionsOption.Normal.SelectedIndicator.BackgroundTransparency = 1;
4979 Container.DirectionsOption.Normal.Background.Image = light_slanted_rectangle;
4980 Container.DirectionsOption.Both.SelectedIndicator.BackgroundTransparency = 0;
4981 Container.DirectionsOption.Both.Background.Image = dark_slanted_rectangle;
4982 end );
4983
4984 -- Change the increment option when the value of the textbox is updated
4985 Container.IncrementOption.Increment.TextBox.FocusLost:connect( function ( enter_pressed )
4986 self.Options.increment = tonumber( Container.IncrementOption.Increment.TextBox.Text ) or self.Options.increment;
4987 Container.IncrementOption.Increment.TextBox.Text = tostring( self.Options.increment );
4988 end );
4989
4990 -- Add functionality to the size inputs
4991 Container.Info.SizeInfo.X.TextButton.MouseButton1Down:connect( function ()
4992 self.State.size_x_focused = true;
4993 Container.Info.SizeInfo.X.TextBox:CaptureFocus();
4994 end );
4995 Container.Info.SizeInfo.X.TextBox.FocusLost:connect( function ( enter_pressed )
4996 local potential_new = tonumber( Container.Info.SizeInfo.X.TextBox.Text );
4997 if potential_new then
4998 self:changeSize( 'x', potential_new );
4999 end;
5000 self.State.size_x_focused = false;
5001 end );
5002 Container.Info.SizeInfo.Y.TextButton.MouseButton1Down:connect( function ()
5003 self.State.size_y_focused = true;
5004 Container.Info.SizeInfo.Y.TextBox:CaptureFocus();
5005 end );
5006 Container.Info.SizeInfo.Y.TextBox.FocusLost:connect( function ( enter_pressed )
5007 local potential_new = tonumber( Container.Info.SizeInfo.Y.TextBox.Text );
5008 if potential_new then
5009 self:changeSize( 'y', potential_new );
5010 end;
5011 self.State.size_y_focused = false;
5012 end );
5013 Container.Info.SizeInfo.Z.TextButton.MouseButton1Down:connect( function ()
5014 self.State.size_z_focused = true;
5015 Container.Info.SizeInfo.Z.TextBox:CaptureFocus();
5016 end );
5017 Container.Info.SizeInfo.Z.TextBox.FocusLost:connect( function ( enter_pressed )
5018 local potential_new = tonumber( Container.Info.SizeInfo.Z.TextBox.Text );
5019 if potential_new then
5020 self:changeSize( 'z', potential_new );
5021 end;
5022 self.State.size_z_focused = false;
5023 end );
5024
5025 self.GUI = Container;
5026 end;
5027
5028 -- Reveal the GUI
5029 self.GUI.Visible = true;
5030
5031end;
5032
5033Tools.Resize.startHistoryRecord = function ( self )
5034
5035 if self.State.HistoryRecord then
5036 self.State.HistoryRecord = nil;
5037 end;
5038
5039 -- Create a history record
5040 self.State.HistoryRecord = {
5041 targets = _cloneTable( Selection.Items );
5042 initial_positions = {};
5043 terminal_positions = {};
5044 initial_sizes = {};
5045 terminal_sizes = {};
5046 unapply = function ( self )
5047 Selection:clear();
5048 for _, Target in pairs( self.targets ) do
5049 if Target then
5050 Target.Size = self.initial_sizes[Target];
5051 Target.CFrame = self.initial_positions[Target];
5052 Target:MakeJoints();
5053 Selection:add( Target );
5054 end;
5055 end;
5056 end;
5057 apply = function ( self )
5058 Selection:clear();
5059 for _, Target in pairs( self.targets ) do
5060 if Target then
5061 Target.Size = self.terminal_sizes[Target];
5062 Target.CFrame = self.terminal_positions[Target];
5063 Target:MakeJoints();
5064 Selection:add( Target );
5065 end;
5066 end;
5067 end;
5068 };
5069 for _, Item in pairs( self.State.HistoryRecord.targets ) do
5070 if Item then
5071 self.State.HistoryRecord.initial_sizes[Item] = Item.Size;
5072 self.State.HistoryRecord.initial_positions[Item] = Item.CFrame;
5073 end;
5074 end;
5075
5076end;
5077
5078Tools.Resize.finishHistoryRecord = function ( self )
5079
5080 if not self.State.HistoryRecord then
5081 return;
5082 end;
5083
5084 for _, Item in pairs( self.State.HistoryRecord.targets ) do
5085 if Item then
5086 self.State.HistoryRecord.terminal_sizes[Item] = Item.Size;
5087 self.State.HistoryRecord.terminal_positions[Item] = Item.CFrame;
5088 end;
5089 end;
5090 History:add( self.State.HistoryRecord );
5091 self.State.HistoryRecord = nil;
5092
5093end;
5094
5095Tools.Resize.changeSize = function ( self, component, new_value )
5096
5097 self:startHistoryRecord();
5098
5099 -- Change the size of each item selected
5100 for _, Item in pairs( Selection.Items ) do
5101 local OldCFrame = Item.CFrame;
5102 -- Make the item be able to be freely resized
5103 if ( pcall( function () local test = Item.FormFactor; end ) ) then
5104 Item.FormFactor = Enum.FormFactor.Custom;
5105 end;
5106 Item.Size = Vector3.new(
5107 component == 'x' and new_value or Item.Size.x,
5108 component == 'y' and new_value or Item.Size.y,
5109 component == 'z' and new_value or Item.Size.z
5110 );
5111 Item.CFrame = OldCFrame;
5112 end;
5113
5114 self:finishHistoryRecord();
5115
5116end;
5117
5118Tools.Resize.updateGUI = function ( self )
5119
5120 -- Make sure the GUI exists
5121 if not self.GUI then
5122 return;
5123 end;
5124
5125 local GUI = self.GUI;
5126
5127 if #Selection.Items > 0 then
5128
5129 -- Look for identical numbers in each axis
5130 local size_x, size_y, size_z = nil, nil, nil;
5131 for item_index, Item in pairs( Selection.Items ) do
5132
5133 -- Set the first values for the first item
5134 if item_index == 1 then
5135 size_x, size_y, size_z = _round( Item.Size.x, 2 ), _round( Item.Size.y, 2 ), _round( Item.Size.z, 2 );
5136
5137 -- Otherwise, compare them and set them to `nil` if they're not identical
5138 else
5139 if size_x ~= _round( Item.Size.x, 2 ) then
5140 size_x = nil;
5141 end;
5142 if size_y ~= _round( Item.Size.y, 2 ) then
5143 size_y = nil;
5144 end;
5145 if size_z ~= _round( Item.Size.z, 2 ) then
5146 size_z = nil;
5147 end;
5148 end;
5149
5150 end;
5151
5152 -- Update the size info on the GUI
5153 if not self.State.size_x_focused then
5154 GUI.Info.SizeInfo.X.TextBox.Text = size_x and tostring( size_x ) or "*";
5155 end;
5156 if not self.State.size_y_focused then
5157 GUI.Info.SizeInfo.Y.TextBox.Text = size_y and tostring( size_y ) or "*";
5158 end;
5159 if not self.State.size_z_focused then
5160 GUI.Info.SizeInfo.Z.TextBox.Text = size_z and tostring( size_z ) or "*";
5161 end;
5162
5163 GUI.Info.Visible = true;
5164 else
5165 GUI.Info.Visible = false;
5166 end;
5167
5168 if self.State.length_resized then
5169 GUI.Changes.Text.Text = "resized " .. tostring( self.State.length_resized ) .. " studs";
5170 GUI.Changes.Position = GUI.Info.Visible and UDim2.new( 0, 5, 0, 165 ) or UDim2.new( 0, 5, 0, 100 );
5171 GUI.Changes.Visible = true;
5172 else
5173 GUI.Changes.Text.Text = "";
5174 GUI.Changes.Visible = false;
5175 end;
5176
5177end;
5178
5179Tools.Resize.hideGUI = function ( self )
5180
5181 -- Hide the GUI if it exists
5182 if self.GUI then
5183 self.GUI.Visible = false;
5184 end;
5185
5186end;
5187
5188Tools.Resize.showHandles = function ( self, Part )
5189
5190 -- Create the handles if they don't exist yet
5191 if not self.Handles then
5192
5193 -- Create the object
5194 self.Handles = RbxUtility.Create "Handles" {
5195 Name = "BTResizeHandles";
5196 Style = Enum.HandlesStyle.Resize;
5197 Color = self.Color;
5198 Parent = GUIContainer;
5199 };
5200
5201 -- Add functionality to the handles
5202 self.Handles.MouseButton1Down:connect( function ()
5203
5204 -- Prevent the platform from thinking we're selecting
5205 override_selection = true;
5206 self.State.resizing = true;
5207
5208 -- Clear the change stats
5209 self.State.length_resized = 0;
5210
5211 self:startHistoryRecord();
5212
5213 -- Do a few things to the selection before manipulating it
5214 for _, Item in pairs( Selection.Items ) do
5215
5216 -- Keep a copy of the state of each item
5217 self.State.PreResize[Item] = Item:Clone();
5218
5219 -- Make the item be able to be freely resized
5220 if ( pcall( function () local test = Item.FormFactor; end ) ) then
5221 Item.FormFactor = Enum.FormFactor.Custom;
5222 end;
5223
5224 -- Anchor each item
5225 Item.Anchored = true;
5226
5227 end;
5228
5229 -- Return stuff to normal once the mouse button is released
5230 self.Connections.HandleReleaseListener = Mouse.Button1Up:connect( function ()
5231
5232 -- Prevent the platform from thinking we're selecting
5233 override_selection = true;
5234 self.State.resizing = false;
5235
5236 -- Stop this connection from firing again
5237 if self.Connections.HandleReleaseListener then
5238 self.Connections.HandleReleaseListener:disconnect();
5239 self.Connections.HandleReleaseListener = nil;
5240 end;
5241
5242 self:finishHistoryRecord();
5243
5244 -- Restore properties that may have been changed temporarily
5245 -- from the pre-resize state copies
5246 for Item, PreviousItemState in pairs( self.State.PreResize ) do
5247 Item.Anchored = PreviousItemState.Anchored;
5248 self.State.PreResize[Item] = nil;
5249 Item:MakeJoints();
5250 end;
5251
5252 end );
5253
5254 end );
5255
5256 self.Handles.MouseDrag:connect( function ( face, drag_distance )
5257
5258 -- Calculate which multiple of the increment to use based on the current drag distance's
5259 -- proximity to their nearest upper and lower multiples
5260
5261 local difference = drag_distance % self.Options.increment;
5262
5263 local lower_degree = drag_distance - difference;
5264 local upper_degree = drag_distance - difference + self.Options.increment;
5265
5266 local lower_degree_proximity = math.abs( drag_distance - lower_degree );
5267 local upper_degree_proximity = math.abs( drag_distance - upper_degree );
5268
5269 if lower_degree_proximity <= upper_degree_proximity then
5270 drag_distance = lower_degree;
5271 else
5272 drag_distance = upper_degree;
5273 end;
5274
5275 local increase = drag_distance;
5276
5277 -- Log the distance that the handle was dragged
5278 self.State.previous_distance = drag_distance;
5279
5280 -- Note the length by which the selection will be enlarged
5281 if self.Options.directions == "both" then
5282 increase = drag_distance * 2;
5283 end;
5284 self.State.length_resized = increase;
5285
5286 -- Go through the selection and make changes to it
5287 for _, Item in pairs( Selection.Items ) do
5288
5289 -- Keep a copy of `Item` in case we need to revert anything
5290 local PreviousItemState = Item:Clone();
5291
5292 -- Break any of `Item`'s joints so it can move freely
5293 Item:BreakJoints();
5294
5295 -- Position and resize `Item` according to the options and the handle that was used
5296
5297 if face == Enum.NormalId.Top then
5298
5299 -- Calculate the appropriate increment to the size based on the shape of `Item`
5300 local SizeIncrease;
5301 if ( pcall( function () local test = Item.Shape; end ) ) and ( Item.Shape == Enum.PartType.Ball or Item.Shape == Enum.PartType.Cylinder ) then
5302 SizeIncrease = Vector3.new( increase, increase, increase );
5303 elseif not ( pcall( function () local test = Item.Shape; end ) ) or ( Item.Shape and Item.Shape == Enum.PartType.Block ) then
5304 SizeIncrease = Vector3.new( 0, increase, 0 );
5305 end;
5306
5307 Item.Size = self.State.PreResize[Item].Size + SizeIncrease;
5308 if Item.Size == self.State.PreResize[Item].Size + SizeIncrease then
5309 Item.CFrame = ( self.Options.directions == "normal" and self.State.PreResize[Item].CFrame:toWorldSpace( CFrame.new( 0, increase / 2, 0 ) ) )
5310 or ( self.Options.directions == "both" and self.State.PreResize[Item].CFrame );
5311 -- If the resizing was not possible, revert `Item`'s state
5312 else
5313 Item.Size = PreviousItemState.Size;
5314 Item.CFrame = PreviousItemState.CFrame;
5315 end;
5316
5317 elseif face == Enum.NormalId.Bottom then
5318
5319 -- Calculate the appropriate increment to the size based on the shape of `Item`
5320 local SizeIncrease;
5321 if ( pcall( function () local test = Item.Shape; end ) ) and ( Item.Shape == Enum.PartType.Ball or Item.Shape == Enum.PartType.Cylinder ) then
5322 SizeIncrease = Vector3.new( increase, increase, increase );
5323 elseif not ( pcall( function () local test = Item.Shape; end ) ) or ( Item.Shape and Item.Shape == Enum.PartType.Block ) then
5324 SizeIncrease = Vector3.new( 0, increase, 0 );
5325 end;
5326
5327 Item.Size = self.State.PreResize[Item].Size + SizeIncrease;
5328 if Item.Size == self.State.PreResize[Item].Size + SizeIncrease then
5329 Item.CFrame = ( self.Options.directions == "normal" and self.State.PreResize[Item].CFrame:toWorldSpace( CFrame.new( 0, -increase / 2, 0 ) ) )
5330 or ( self.Options.directions == "both" and self.State.PreResize[Item].CFrame );
5331 -- If the resizing was not possible, revert `Item`'s state
5332 else
5333 Item.Size = PreviousItemState.Size;
5334 Item.CFrame = PreviousItemState.CFrame;
5335 end;
5336
5337 elseif face == Enum.NormalId.Front then
5338
5339 -- Calculate the appropriate increment to the size based on the shape of `Item`
5340 local SizeIncrease;
5341 if ( pcall( function () local test = Item.Shape; end ) ) and ( Item.Shape == Enum.PartType.Ball or Item.Shape == Enum.PartType.Cylinder ) then
5342 SizeIncrease = Vector3.new( increase, increase, increase );
5343 elseif not ( pcall( function () local test = Item.Shape; end ) ) or ( Item.Shape and Item.Shape == Enum.PartType.Block ) then
5344 SizeIncrease = Vector3.new( 0, 0, increase );
5345 end;
5346
5347 Item.Size = self.State.PreResize[Item].Size + SizeIncrease;
5348 if Item.Size == self.State.PreResize[Item].Size + SizeIncrease then
5349 Item.CFrame = ( self.Options.directions == "normal" and self.State.PreResize[Item].CFrame:toWorldSpace( CFrame.new( 0, 0, -increase / 2 ) ) )
5350 or ( self.Options.directions == "both" and self.State.PreResize[Item].CFrame );
5351 -- If the resizing was not possible, revert `Item`'s state
5352 else
5353 Item.Size = PreviousItemState.Size;
5354 Item.CFrame = PreviousItemState.CFrame;
5355 end;
5356
5357 elseif face == Enum.NormalId.Back then
5358
5359 -- Calculate the appropriate increment to the size based on the shape of `Item`
5360 local SizeIncrease;
5361 if ( pcall( function () local test = Item.Shape; end ) ) and ( Item.Shape == Enum.PartType.Ball or Item.Shape == Enum.PartType.Cylinder ) then
5362 SizeIncrease = Vector3.new( increase, increase, increase );
5363 elseif not ( pcall( function () local test = Item.Shape; end ) ) or ( Item.Shape and Item.Shape == Enum.PartType.Block ) then
5364 SizeIncrease = Vector3.new( 0, 0, increase );
5365 end;
5366
5367 Item.Size = self.State.PreResize[Item].Size + SizeIncrease;
5368 if Item.Size == self.State.PreResize[Item].Size + SizeIncrease then
5369 Item.CFrame = ( self.Options.directions == "normal" and self.State.PreResize[Item].CFrame:toWorldSpace( CFrame.new( 0, 0, increase / 2 ) ) )
5370 or ( self.Options.directions == "both" and self.State.PreResize[Item].CFrame );
5371 -- If the resizing was not possible, revert `Item`'s state
5372 else
5373 Item.Size = PreviousItemState.Size;
5374 Item.CFrame = PreviousItemState.CFrame;
5375 end;
5376
5377 elseif face == Enum.NormalId.Left then
5378
5379 -- Calculate the appropriate increment to the size based on the shape of `Item`
5380 local SizeIncrease;
5381 if ( pcall( function () local test = Item.Shape; end ) ) and ( Item.Shape == Enum.PartType.Ball or Item.Shape == Enum.PartType.Cylinder ) then
5382 SizeIncrease = Vector3.new( increase, increase, increase );
5383 elseif not ( pcall( function () local test = Item.Shape; end ) ) or ( Item.Shape and Item.Shape == Enum.PartType.Block ) then
5384 SizeIncrease = Vector3.new( increase, 0, 0 );
5385 end;
5386
5387 Item.Size = self.State.PreResize[Item].Size + SizeIncrease;
5388 if Item.Size == self.State.PreResize[Item].Size + SizeIncrease then
5389 Item.CFrame = ( self.Options.directions == "normal" and self.State.PreResize[Item].CFrame:toWorldSpace( CFrame.new( -increase / 2, 0, 0 ) ) )
5390 or ( self.Options.directions == "both" and self.State.PreResize[Item].CFrame );
5391 -- If the resizing was not possible, revert `Item`'s state
5392 else
5393 Item.Size = PreviousItemState.Size;
5394 Item.CFrame = PreviousItemState.CFrame;
5395 end;
5396
5397 elseif face == Enum.NormalId.Right then
5398
5399 -- Calculate the appropriate increment to the size based on the shape of `Item`
5400 local SizeIncrease;
5401 if ( pcall( function () local test = Item.Shape; end ) ) and ( Item.Shape == Enum.PartType.Ball or Item.Shape == Enum.PartType.Cylinder ) then
5402 SizeIncrease = Vector3.new( increase, increase, increase );
5403 elseif not ( pcall( function () local test = Item.Shape; end ) ) or ( Item.Shape and Item.Shape == Enum.PartType.Block ) then
5404 SizeIncrease = Vector3.new( increase, 0, 0 );
5405 end;
5406
5407 Item.Size = self.State.PreResize[Item].Size + SizeIncrease;
5408 if Item.Size == self.State.PreResize[Item].Size + SizeIncrease then
5409 Item.CFrame = ( self.Options.directions == "normal" and self.State.PreResize[Item].CFrame:toWorldSpace( CFrame.new( increase / 2, 0, 0 ) ) )
5410 or ( self.Options.directions == "both" and self.State.PreResize[Item].CFrame );
5411 -- If the resizing was not possible, revert `Item`'s state
5412 else
5413 Item.Size = PreviousItemState.Size;
5414 Item.CFrame = PreviousItemState.CFrame;
5415 end;
5416 end;
5417
5418 -- Make joints with surrounding parts again once the resizing is done
5419 Item:MakeJoints();
5420
5421 end;
5422
5423 end );
5424
5425 end;
5426
5427 -- Stop listening for the existence of the previous adornee (if any)
5428 if self.Connections.AdorneeExistenceListener then
5429 self.Connections.AdorneeExistenceListener:disconnect();
5430 self.Connections.AdorneeExistenceListener = nil;
5431 end;
5432
5433 -- Attach the handles to `Part`
5434 self.Handles.Adornee = Part;
5435
5436 -- Make sure to hide the handles if `Part` suddenly stops existing
5437 self.Connections.AdorneeExistenceListener = Part.AncestryChanged:connect( function ( Object, NewParent )
5438
5439 -- Make sure this change in parent applies directly to `Part`
5440 if Object ~= Part then
5441 return;
5442 end;
5443
5444 -- Show the handles according to the existence of the part
5445 if NewParent == nil then
5446 self:hideHandles();
5447 else
5448 self:showHandles( Part );
5449 end;
5450
5451 end );
5452
5453end;
5454
5455Tools.Resize.hideHandles = function ( self )
5456
5457 -- Hide the handles if they exist
5458 if self.Handles then
5459 self.Handles.Adornee = nil;
5460 end;
5461
5462end;
5463
5464Tools.Resize.Loaded = true;
5465end))
5466LocalScript3.Disabled = true
5467LocalScript4.Name = "BTRotateTool"
5468LocalScript4.Parent = LocalScript1
5469table.insert(cors,sandbox(LocalScript4,function()
5470-- Load the main tool's core environment when it's ready
5471repeat wait() until (
5472 _G.BTCoreEnv and
5473 _G.BTCoreEnv[script.Parent.Parent] and
5474 _G.BTCoreEnv[script.Parent.Parent].CoreReady
5475);
5476setfenv( 1, _G.BTCoreEnv[script.Parent.Parent] );
5477
5478------------------------------------------
5479-- Rotate tool
5480------------------------------------------
5481
5482-- Create the tool
5483Tools.Rotate = {};
5484
5485-- Create structures to hold data that the tool needs
5486Tools.Rotate.Connections = {};
5487
5488Tools.Rotate.Options = {
5489 ["increment"] = 15;
5490 ["pivot"] = "center"
5491};
5492
5493Tools.Rotate.State = {
5494 ["PreRotation"] = {};
5495 ["rotating"] = false;
5496 ["previous_distance"] = 0;
5497 ["degrees_rotated"] = 0;
5498 ["rotation_size"] = 0;
5499};
5500
5501Tools.Rotate.Listeners = {};
5502
5503-- Define the color of the tool
5504Tools.Rotate.Color = BrickColor.new( "Bright green" );
5505
5506-- Start adding functionality to the tool
5507Tools.Rotate.Listeners.Equipped = function ()
5508
5509 local self = Tools.Rotate;
5510
5511 -- Change the color of selection boxes temporarily
5512 self.State.PreviousSelectionBoxColor = SelectionBoxColor;
5513 SelectionBoxColor = self.Color;
5514 updateSelectionBoxColor();
5515
5516 -- Reveal the GUI
5517 self:showGUI();
5518
5519 -- Create the boundingbox if it doesn't already exist
5520 if not self.BoundingBox then
5521 self.BoundingBox = RbxUtility.Create "Part" {
5522 Name = "BTBoundingBox";
5523 CanCollide = false;
5524 Transparency = 1;
5525 Anchored = true;
5526 };
5527 end;
5528 Mouse.TargetFilter = self.BoundingBox;
5529
5530 -- Update the pivot option
5531 self:changePivot( self.Options.pivot );
5532
5533 -- Oh, and update the boundingbox and the GUI regularly
5534 coroutine.wrap( function ()
5535 updater_on = true;
5536
5537 -- Provide a function to stop the loop
5538 self.Updater = function ()
5539 updater_on = false;
5540 end;
5541
5542 while wait( 0.1 ) and updater_on do
5543
5544 -- Make sure the tool's equipped
5545 if CurrentTool == self then
5546
5547 -- Update the GUI if it's visible
5548 if self.GUI and self.GUI.Visible then
5549 self:updateGUI();
5550 end;
5551
5552 -- Update the boundingbox if it's visible
5553 if self.Options.pivot == "center" then
5554 self:updateBoundingBox();
5555 end;
5556
5557 end;
5558
5559 end;
5560
5561 end )();
5562
5563 -- Also enable the ability to select an edge as a pivot
5564 SelectEdge:start( function ( EdgeMarker )
5565 self:changePivot( "last" );
5566 self.Options.PivotPoint = EdgeMarker.CFrame;
5567 self.Connections.EdgeSelectionRemover = Selection.Changed:connect( function ()
5568 self.Options.PivotPoint = nil;
5569 if self.Connections.EdgeSelectionRemover then
5570 self.Connections.EdgeSelectionRemover:disconnect();
5571 self.Connections.EdgeSelectionRemover = nil;
5572 end;
5573 end );
5574 self:showHandles( EdgeMarker );
5575 end );
5576
5577end;
5578
5579Tools.Rotate.Listeners.Unequipped = function ()
5580
5581 local self = Tools.Rotate;
5582
5583 -- Stop the update loop
5584 if self.Updater then
5585 self.Updater();
5586 self.Updater = nil;
5587 end;
5588
5589 -- Disable the ability to select edges
5590 SelectEdge:stop();
5591 if self.Options.PivotPoint then
5592 self.Options.PivotPoint = nil;
5593 end;
5594
5595 -- Hide the GUI
5596 self:hideGUI();
5597
5598 -- Hide the handles
5599 self:hideHandles();
5600
5601 -- Clear out any temporary connections
5602 for connection_index, Connection in pairs( self.Connections ) do
5603 Connection:disconnect();
5604 self.Connections[connection_index] = nil;
5605 end;
5606
5607 -- Restore the original color of the selection boxes
5608 SelectionBoxColor = self.State.PreviousSelectionBoxColor;
5609 updateSelectionBoxColor();
5610
5611end;
5612
5613Tools.Rotate.Listeners.Button1Down = function ()
5614
5615 local self = Tools.Rotate;
5616
5617 if not self.State.rotating and self.Options.PivotPoint then
5618 self.Options.PivotPoint = nil;
5619 end;
5620
5621end;
5622
5623Tools.Rotate.showGUI = function ( self )
5624
5625 -- Initialize the GUI if it's not ready yet
5626 if not self.GUI then
5627
5628 local Container = Tool.Interfaces:WaitForChild( "BTRotateToolGUI" ):Clone();
5629 Container.Parent = UI;
5630
5631 -- Change the pivot type option when the button is clicked
5632 Container.PivotOption.Center.Button.MouseButton1Down:connect( function ()
5633 self:changePivot( "center" );
5634 end );
5635
5636 Container.PivotOption.Local.Button.MouseButton1Down:connect( function ()
5637 self:changePivot( "local" );
5638 end );
5639
5640 Container.PivotOption.Last.Button.MouseButton1Down:connect( function ()
5641 self:changePivot( "last" );
5642 end );
5643
5644 -- Change the increment option when the value of the textbox is updated
5645 Container.IncrementOption.Increment.TextBox.FocusLost:connect( function ( enter_pressed )
5646 self.Options.increment = tonumber( Container.IncrementOption.Increment.TextBox.Text ) or self.Options.increment;
5647 Container.IncrementOption.Increment.TextBox.Text = tostring( self.Options.increment );
5648 end );
5649
5650 -- Add functionality to the rotation inputs
5651 Container.Info.RotationInfo.X.TextButton.MouseButton1Down:connect( function ()
5652 self.State.rot_x_focused = true;
5653 Container.Info.RotationInfo.X.TextBox:CaptureFocus();
5654 end );
5655 Container.Info.RotationInfo.X.TextBox.FocusLost:connect( function ( enter_pressed )
5656 local potential_new = tonumber( Container.Info.RotationInfo.X.TextBox.Text );
5657 if potential_new then
5658 self:changeRotation( 'x', math.rad( potential_new ) );
5659 end;
5660 self.State.rot_x_focused = false;
5661 end );
5662 Container.Info.RotationInfo.Y.TextButton.MouseButton1Down:connect( function ()
5663 self.State.rot_y_focused = true;
5664 Container.Info.RotationInfo.Y.TextBox:CaptureFocus();
5665 end );
5666 Container.Info.RotationInfo.Y.TextBox.FocusLost:connect( function ( enter_pressed )
5667 local potential_new = tonumber( Container.Info.RotationInfo.Y.TextBox.Text );
5668 if potential_new then
5669 self:changeRotation( 'y', math.rad( potential_new ) );
5670 end;
5671 self.State.rot_y_focused = false;
5672 end );
5673 Container.Info.RotationInfo.Z.TextButton.MouseButton1Down:connect( function ()
5674 self.State.rot_z_focused = true;
5675 Container.Info.RotationInfo.Z.TextBox:CaptureFocus();
5676 end );
5677 Container.Info.RotationInfo.Z.TextBox.FocusLost:connect( function ( enter_pressed )
5678 local potential_new = tonumber( Container.Info.RotationInfo.Z.TextBox.Text );
5679 if potential_new then
5680 self:changeRotation( 'z', math.rad( potential_new ) );
5681 end;
5682 self.State.rot_z_focused = false;
5683 end );
5684
5685 self.GUI = Container;
5686 end;
5687
5688 -- Reveal the GUI
5689 self.GUI.Visible = true;
5690
5691end;
5692
5693Tools.Rotate.startHistoryRecord = function ( self )
5694
5695 if self.State.HistoryRecord then
5696 self.State.HistoryRecord = nil;
5697 end;
5698
5699 -- Create a history record
5700 self.State.HistoryRecord = {
5701 targets = _cloneTable( Selection.Items );
5702 initial_cframes = {};
5703 terminal_cframes = {};
5704 unapply = function ( self )
5705 Selection:clear();
5706 for _, Target in pairs( self.targets ) do
5707 if Target then
5708 Target.CFrame = self.initial_cframes[Target];
5709 Target:MakeJoints();
5710 Selection:add( Target );
5711 end;
5712 end;
5713 end;
5714 apply = function ( self )
5715 Selection:clear();
5716 for _, Target in pairs( self.targets ) do
5717 if Target then
5718 Target.CFrame = self.terminal_cframes[Target];
5719 Target:MakeJoints();
5720 Selection:add( Target );
5721 end;
5722 end;
5723 end;
5724 };
5725 for _, Item in pairs( self.State.HistoryRecord.targets ) do
5726 if Item then
5727 self.State.HistoryRecord.initial_cframes[Item] = Item.CFrame;
5728 end;
5729 end;
5730
5731end;
5732
5733Tools.Rotate.finishHistoryRecord = function ( self )
5734
5735 if not self.State.HistoryRecord then
5736 return;
5737 end;
5738
5739 for _, Item in pairs( self.State.HistoryRecord.targets ) do
5740 if Item then
5741 self.State.HistoryRecord.terminal_cframes[Item] = Item.CFrame;
5742 end;
5743 end;
5744 History:add( self.State.HistoryRecord );
5745 self.State.HistoryRecord = nil;
5746
5747end;
5748
5749Tools.Rotate.changeRotation = function ( self, component, new_value )
5750
5751 self:startHistoryRecord();
5752
5753 -- Change the rotation of each item selected
5754 for _, Item in pairs( Selection.Items ) do
5755 local old_x_rot, old_y_rot, old_z_rot = Item.CFrame:toEulerAnglesXYZ();
5756 Item.CFrame = CFrame.new( Item.Position ) * CFrame.Angles(
5757 component == 'x' and new_value or old_x_rot,
5758 component == 'y' and new_value or old_y_rot,
5759 component == 'z' and new_value or old_z_rot
5760 );
5761 end;
5762
5763 self:finishHistoryRecord();
5764
5765end;
5766
5767Tools.Rotate.updateGUI = function ( self )
5768
5769 -- Make sure the GUI exists
5770 if not self.GUI then
5771 return;
5772 end;
5773
5774 local GUI = self.GUI;
5775
5776 if #Selection.Items > 0 then
5777
5778 -- Look for identical numbers in each axis
5779 local rot_x, rot_y, rot_z = nil, nil, nil;
5780 for item_index, Item in pairs( Selection.Items ) do
5781
5782 local item_rot_x, item_rot_y, item_rot_z = Item.CFrame:toEulerAnglesXYZ();
5783
5784 -- Set the first values for the first item
5785 if item_index == 1 then
5786 rot_x, rot_y, rot_z = _round( math.deg( item_rot_x ), 2 ), _round( math.deg( item_rot_y ), 2 ), _round( math.deg( item_rot_z ), 2 );
5787
5788 -- Otherwise, compare them and set them to `nil` if they're not identical
5789 else
5790 if rot_x ~= _round( math.deg( item_rot_x ), 2 ) then
5791 rot_x = nil;
5792 end;
5793 if rot_y ~= _round( math.deg( item_rot_y ), 2 ) then
5794 rot_y = nil;
5795 end;
5796 if rot_z ~= _round( math.deg( item_rot_z ), 2 ) then
5797 rot_z = nil;
5798 end;
5799 end;
5800
5801 end;
5802
5803 -- Update the size info on the GUI
5804 if not self.State.rot_x_focused then
5805 GUI.Info.RotationInfo.X.TextBox.Text = rot_x and tostring( rot_x ) or "*";
5806 end;
5807 if not self.State.rot_y_focused then
5808 GUI.Info.RotationInfo.Y.TextBox.Text = rot_y and tostring( rot_y ) or "*";
5809 end;
5810 if not self.State.rot_z_focused then
5811 GUI.Info.RotationInfo.Z.TextBox.Text = rot_z and tostring( rot_z ) or "*";
5812 end;
5813
5814 GUI.Info.Visible = true;
5815 else
5816 GUI.Info.Visible = false;
5817 end;
5818
5819 if self.State.degrees_rotated then
5820 GUI.Changes.Text.Text = "rotated " .. tostring( self.State.degrees_rotated ) .. " degrees";
5821 GUI.Changes.Position = GUI.Info.Visible and UDim2.new( 0, 5, 0, 165 ) or UDim2.new( 0, 5, 0, 100 );
5822 GUI.Changes.Visible = true;
5823 else
5824 GUI.Changes.Text.Text = "";
5825 GUI.Changes.Visible = false;
5826 end;
5827
5828end;
5829
5830Tools.Rotate.hideGUI = function ( self )
5831
5832 -- Hide the GUI if it exists
5833 if self.GUI then
5834 self.GUI.Visible = false;
5835 end;
5836
5837end;
5838
5839Tools.Rotate.updateBoundingBox = function ( self )
5840
5841 if #Selection.Items > 0 then
5842 local SelectionSize, SelectionPosition = _getCollectionInfo( Selection.Items );
5843 self.BoundingBox.Size = SelectionSize;
5844 self.BoundingBox.CFrame = SelectionPosition;
5845 self:showHandles( self.BoundingBox );
5846
5847 else
5848 self:hideHandles();
5849 end;
5850
5851end;
5852
5853Tools.Rotate.changePivot = function ( self, new_pivot )
5854
5855 -- Have a quick reference to the GUI (if any)
5856 local PivotOptionGUI = self.GUI and self.GUI.PivotOption or nil;
5857
5858 -- Disconnect any handle-related listeners that are specific to a certain pivot option
5859 if self.Connections.HandleFocusChangeListener then
5860 self.Connections.HandleFocusChangeListener:disconnect();
5861 self.Connections.HandleFocusChangeListener = nil;
5862 end;
5863
5864 if self.Connections.HandleSelectionChangeListener then
5865 self.Connections.HandleSelectionChangeListener:disconnect();
5866 self.Connections.HandleSelectionChangeListener = nil;
5867 end;
5868
5869 -- Remove any temporary edge selection
5870 if self.Options.PivotPoint then
5871 self.Options.PivotPoint = nil;
5872 end;
5873
5874 if new_pivot == "center" then
5875
5876 -- Update the options
5877 self.Options.pivot = "center";
5878
5879 -- Focus the handles on the boundingbox
5880 self:showHandles( self.BoundingBox );
5881
5882 -- Update the GUI's option panel
5883 if self.GUI then
5884 PivotOptionGUI.Center.SelectedIndicator.BackgroundTransparency = 0;
5885 PivotOptionGUI.Center.Background.Image = dark_slanted_rectangle;
5886 PivotOptionGUI.Local.SelectedIndicator.BackgroundTransparency = 1;
5887 PivotOptionGUI.Local.Background.Image = light_slanted_rectangle;
5888 PivotOptionGUI.Last.SelectedIndicator.BackgroundTransparency = 1;
5889 PivotOptionGUI.Last.Background.Image = light_slanted_rectangle;
5890 end;
5891
5892 end;
5893
5894 if new_pivot == "local" then
5895
5896 -- Update the options
5897 self.Options.pivot = "local";
5898
5899 -- Always have the handles on the most recent addition to the selection
5900 self.Connections.HandleSelectionChangeListener = Selection.Changed:connect( function ()
5901
5902 -- Clear out any previous adornee
5903 self:hideHandles();
5904
5905 -- If there /is/ a last item in the selection, attach the handles to it
5906 if Selection.Last then
5907 self:showHandles( Selection.Last );
5908 end;
5909
5910 end );
5911
5912 -- Switch the adornee of the handles if the second mouse button is pressed
5913 self.Connections.HandleFocusChangeListener = Mouse.Button2Up:connect( function ()
5914
5915 -- Make sure the platform doesn't think we're selecting
5916 override_selection = true;
5917
5918 -- If the target is in the selection, make it the new adornee
5919 if Selection:find( Mouse.Target ) then
5920 Selection:focus( Mouse.Target );
5921 self:showHandles( Mouse.Target );
5922 end;
5923
5924 end );
5925
5926 -- Finally, attach the handles to the last item added to the selection (if any)
5927 if Selection.Last then
5928 self:showHandles( Selection.Last );
5929 end;
5930
5931 -- Update the GUI's option panel
5932 if self.GUI then
5933 PivotOptionGUI.Center.SelectedIndicator.BackgroundTransparency = 1;
5934 PivotOptionGUI.Center.Background.Image = light_slanted_rectangle;
5935 PivotOptionGUI.Local.SelectedIndicator.BackgroundTransparency = 0;
5936 PivotOptionGUI.Local.Background.Image = dark_slanted_rectangle;
5937 PivotOptionGUI.Last.SelectedIndicator.BackgroundTransparency = 1;
5938 PivotOptionGUI.Last.Background.Image = light_slanted_rectangle;
5939 end;
5940
5941 end;
5942
5943 if new_pivot == "last" then
5944
5945 -- Update the options
5946 self.Options.pivot = "last";
5947
5948 -- Always have the handles on the most recent addition to the selection
5949 self.Connections.HandleSelectionChangeListener = Selection.Changed:connect( function ()
5950
5951 -- Clear out any previous adornee
5952 if not self.Options.PivotPoint then
5953 self:hideHandles();
5954 end;
5955
5956 -- If there /is/ a last item in the selection, attach the handles to it
5957 if Selection.Last and not self.Options.PivotPoint then
5958 self:showHandles( Selection.Last );
5959 end;
5960
5961 end );
5962
5963 -- Switch the adornee of the handles if the second mouse button is pressed
5964 self.Connections.HandleFocusChangeListener = Mouse.Button2Up:connect( function ()
5965
5966 -- Make sure the platform doesn't think we're selecting
5967 override_selection = true;
5968
5969 -- If the target is in the selection, make it the new adornee
5970 if Selection:find( Mouse.Target ) then
5971 Selection:focus( Mouse.Target );
5972 self:showHandles( Mouse.Target );
5973 end;
5974
5975 end );
5976
5977 -- Finally, attach the handles to the last item added to the selection (if any)
5978 if Selection.Last then
5979 self:showHandles( Selection.Last );
5980 end;
5981
5982 -- Update the GUI's option panel
5983 if self.GUI then
5984 PivotOptionGUI.Center.SelectedIndicator.BackgroundTransparency = 1;
5985 PivotOptionGUI.Center.Background.Image = light_slanted_rectangle;
5986 PivotOptionGUI.Local.SelectedIndicator.BackgroundTransparency = 1;
5987 PivotOptionGUI.Local.Background.Image = light_slanted_rectangle;
5988 PivotOptionGUI.Last.SelectedIndicator.BackgroundTransparency = 0;
5989 PivotOptionGUI.Last.Background.Image = dark_slanted_rectangle;
5990 end;
5991
5992 end;
5993
5994end;
5995
5996
5997Tools.Rotate.showHandles = function ( self, Part )
5998
5999 -- Create the handles if they don't exist yet
6000 if not self.Handles then
6001
6002 -- Create the object
6003 self.Handles = RbxUtility.Create "ArcHandles" {
6004 Name = "BTRotationHandles";
6005 Color = self.Color;
6006 Parent = GUIContainer;
6007 };
6008
6009 -- Add functionality to the handles
6010
6011 self.Handles.MouseButton1Down:connect( function ()
6012
6013 -- Prevent the platform from thinking we're selecting
6014 override_selection = true;
6015 self.State.rotating = true;
6016
6017 -- Clear the change stats
6018 self.State.degrees_rotated = 0;
6019 self.State.rotation_size = 0;
6020
6021 self:startHistoryRecord();
6022
6023 -- Do a few things to the selection before manipulating it
6024 for _, Item in pairs( Selection.Items ) do
6025
6026 -- Keep a copy of the state of each item
6027 self.State.PreRotation[Item] = Item:Clone();
6028
6029 -- Anchor each item
6030 Item.Anchored = true;
6031
6032 end;
6033
6034 -- Also keep the position of the original selection
6035 local PreRotationSize, PreRotationPosition = _getCollectionInfo( self.State.PreRotation );
6036 self.State.PreRotationPosition = PreRotationPosition;
6037
6038 -- Return stuff to normal once the mouse button is released
6039 self.Connections.HandleReleaseListener = Mouse.Button1Up:connect( function ()
6040
6041 -- Prevent the platform from thinking we're selecting
6042 override_selection = true;
6043 self.State.rotating = false;
6044
6045 -- Stop this connection from firing again
6046 if self.Connections.HandleReleaseListener then
6047 self.Connections.HandleReleaseListener:disconnect();
6048 self.Connections.HandleReleaseListener = nil;
6049 end;
6050
6051 self:finishHistoryRecord();
6052
6053 -- Restore properties that may have been changed temporarily
6054 -- from the pre-rotation state copies
6055 for Item, PreviousItemState in pairs( self.State.PreRotation ) do
6056 Item.Anchored = PreviousItemState.Anchored;
6057 self.State.PreRotation[Item] = nil;
6058 Item:MakeJoints();
6059 end;
6060
6061 end );
6062
6063 end );
6064
6065 self.Handles.MouseDrag:connect( function ( axis, drag_distance )
6066
6067 -- Round down and convert the drag distance to degrees to make it easier to work with
6068 local drag_distance = math.floor( math.deg( drag_distance ) );
6069
6070 -- Calculate which multiple of the increment to use based on the current angle's
6071 -- proximity to their nearest upper and lower multiples
6072
6073 local difference = drag_distance % self.Options.increment;
6074
6075 local lower_degree = drag_distance - difference;
6076 local upper_degree = drag_distance - difference + self.Options.increment;
6077
6078 local lower_degree_proximity = math.abs( drag_distance - lower_degree );
6079 local upper_degree_proximity = math.abs( drag_distance - upper_degree );
6080
6081 if lower_degree_proximity <= upper_degree_proximity then
6082 drag_distance = lower_degree;
6083 else
6084 drag_distance = upper_degree;
6085 end;
6086
6087 local increase = self.Options.increment * math.floor( drag_distance / self.Options.increment );
6088
6089 self.State.degrees_rotated = drag_distance;
6090
6091 -- Go through the selection and make changes to it
6092 for _, Item in pairs( Selection.Items ) do
6093
6094 -- Keep a copy of `Item` in case we need to revert anything
6095 local PreviousItemState = Item:Clone();
6096
6097 -- Break any of `Item`'s joints so it can move freely
6098 Item:BreakJoints();
6099
6100 -- Rotate `Item` according to the options and the handle that was used
6101 if axis == Enum.Axis.Y then
6102 if self.Options.pivot == "center" then
6103 Item.CFrame = self.State.PreRotationPosition:toWorldSpace( CFrame.new( 0, 0, 0 ) * CFrame.Angles( 0, math.rad( increase ), 0 ) ):toWorldSpace( self.State.PreRotation[Item].CFrame:toObjectSpace( self.State.PreRotationPosition ):inverse() );
6104 elseif self.Options.pivot == "local" then
6105 Item.CFrame = self.State.PreRotation[Item].CFrame:toWorldSpace( CFrame.new( 0, 0, 0 ) * CFrame.Angles( 0, math.rad( increase ), 0 ) );
6106 elseif self.Options.pivot == "last" then
6107 Item.CFrame = ( self.Options.PivotPoint or self.State.PreRotation[Selection.Last].CFrame ):toWorldSpace( CFrame.new( 0, 0, 0 ) * CFrame.Angles( 0, math.rad( increase ), 0 ) ):toWorldSpace( self.State.PreRotation[Item].CFrame:toObjectSpace( self.Options.PivotPoint or self.State.PreRotation[Selection.Last].CFrame ):inverse() );
6108 end;
6109 elseif axis == Enum.Axis.X then
6110 if self.Options.pivot == "center" then
6111 Item.CFrame = self.State.PreRotationPosition:toWorldSpace( CFrame.new( 0, 0, 0 ) * CFrame.Angles( math.rad( increase ), 0, 0 ) ):toWorldSpace( self.State.PreRotation[Item].CFrame:toObjectSpace( self.State.PreRotationPosition ):inverse() );
6112 elseif self.Options.pivot == "local" then
6113 Item.CFrame = self.State.PreRotation[Item].CFrame:toWorldSpace( CFrame.new( 0, 0, 0 ) * CFrame.Angles( math.rad( increase ), 0, 0 ) );
6114 elseif self.Options.pivot == "last" then
6115 Item.CFrame = ( self.Options.PivotPoint or self.State.PreRotation[Selection.Last].CFrame ):toWorldSpace( CFrame.new( 0, 0, 0 ) * CFrame.Angles( math.rad( increase ), 0, 0 ) ):toWorldSpace( self.State.PreRotation[Item].CFrame:toObjectSpace( self.Options.PivotPoint or self.State.PreRotation[Selection.Last].CFrame ):inverse() );
6116 end;
6117 elseif axis == Enum.Axis.Z then
6118 if self.Options.pivot == "center" then
6119 Item.CFrame = self.State.PreRotationPosition:toWorldSpace( CFrame.new( 0, 0, 0 ) * CFrame.Angles( 0, 0, math.rad( increase ) ) ):toWorldSpace( self.State.PreRotation[Item].CFrame:toObjectSpace( self.State.PreRotationPosition ):inverse() );
6120 elseif self.Options.pivot == "local" then
6121 Item.CFrame = self.State.PreRotation[Item].CFrame:toWorldSpace( CFrame.new( 0, 0, 0 ) * CFrame.Angles( 0, 0, math.rad( increase ) ) );
6122 elseif self.Options.pivot == "last" then
6123 Item.CFrame = ( self.Options.PivotPoint or self.State.PreRotation[Selection.Last].CFrame ):toWorldSpace( CFrame.new( 0, 0, 0 ) * CFrame.Angles( 0, 0, math.rad( increase ) ) ):toWorldSpace( self.State.PreRotation[Item].CFrame:toObjectSpace( self.Options.PivotPoint or self.State.PreRotation[Selection.Last].CFrame ):inverse() );
6124 end;
6125 end;
6126
6127 -- Make joints with surrounding parts again once the resizing is done
6128 Item:MakeJoints();
6129
6130 end;
6131
6132 end );
6133
6134 end;
6135
6136 -- Stop listening for the existence of the previous adornee (if any)
6137 if self.Connections.AdorneeExistenceListener then
6138 self.Connections.AdorneeExistenceListener:disconnect();
6139 self.Connections.AdorneeExistenceListener = nil;
6140 end;
6141
6142 -- Attach the handles to `Part`
6143 self.Handles.Adornee = Part;
6144
6145 -- Make sure to hide the handles if `Part` suddenly stops existing
6146 self.Connections.AdorneeExistenceListener = Part.AncestryChanged:connect( function ( Object, NewParent )
6147
6148 -- Make sure this change in parent applies directly to `Part`
6149 if Object ~= Part then
6150 return;
6151 end;
6152
6153 -- Show the handles according to the existence of the part
6154 if NewParent == nil then
6155 self:hideHandles();
6156 else
6157 self:showHandles( Part );
6158 end;
6159
6160 end );
6161
6162end;
6163
6164Tools.Rotate.hideHandles = function ( self )
6165
6166 -- Hide the handles if they exist
6167 if self.Handles then
6168 self.Handles.Adornee = nil;
6169 end;
6170
6171end;
6172
6173Tools.Rotate.Loaded = true;
6174end))
6175LocalScript4.Disabled = true
6176LocalScript5.Name = "BTPaintTool"
6177LocalScript5.Parent = LocalScript1
6178table.insert(cors,sandbox(LocalScript5,function()
6179-- Load the main tool's core environment when it's ready
6180repeat wait() until (
6181 _G.BTCoreEnv and
6182 _G.BTCoreEnv[script.Parent.Parent] and
6183 _G.BTCoreEnv[script.Parent.Parent].CoreReady
6184);
6185setfenv( 1, _G.BTCoreEnv[script.Parent.Parent] );
6186
6187------------------------------------------
6188-- Paint tool
6189------------------------------------------
6190
6191-- Create the main container for this tool
6192Tools.Paint = {};
6193
6194-- Define the color of the tool
6195Tools.Paint.Color = BrickColor.new( "Really red" );
6196
6197-- Define options
6198Tools.Paint.Options = {
6199 ["Color"] = nil
6200};
6201
6202Tools.Paint.State = {};
6203
6204-- Add listeners
6205Tools.Paint.Listeners = {};
6206
6207Tools.Paint.Listeners.Equipped = function ()
6208
6209 local self = Tools.Paint;
6210
6211 -- Change the color of selection boxes temporarily
6212 self.State.PreviousSelectionBoxColor = SelectionBoxColor;
6213 SelectionBoxColor = self.Color;
6214 updateSelectionBoxColor();
6215
6216 -- Show the GUI
6217 self:showGUI();
6218
6219 -- Update the selected color
6220 self:changeColor( self.Options.Color );
6221
6222end;
6223
6224Tools.Paint.Listeners.Unequipped = function ()
6225
6226 local self = Tools.Paint;
6227
6228 -- Clear out the preferred color option
6229 self:changeColor( nil );
6230
6231 -- Hide the GUI
6232 self:hideGUI();
6233
6234 -- Restore the original color of the selection boxes
6235 SelectionBoxColor = self.State.PreviousSelectionBoxColor;
6236 updateSelectionBoxColor();
6237
6238end;
6239
6240Tools.Paint.startHistoryRecord = function ( self )
6241
6242 if self.State.HistoryRecord then
6243 self.State.HistoryRecord = nil;
6244 end;
6245
6246 -- Create a history record
6247 self.State.HistoryRecord = {
6248 targets = _cloneTable( Selection.Items );
6249 initial_colors = {};
6250 terminal_colors = {};
6251 unapply = function ( self )
6252 Selection:clear();
6253 for _, Target in pairs( self.targets ) do
6254 if Target then
6255 Target.BrickColor = self.initial_colors[Target];
6256 Selection:add( Target );
6257 end;
6258 end;
6259 end;
6260 apply = function ( self )
6261 Selection:clear();
6262 for _, Target in pairs( self.targets ) do
6263 if Target then
6264 Target.BrickColor = self.terminal_colors[Target];
6265 Selection:add( Target );
6266 end;
6267 end;
6268 end;
6269 };
6270 for _, Item in pairs( self.State.HistoryRecord.targets ) do
6271 if Item then
6272 self.State.HistoryRecord.initial_colors[Item] = Item.BrickColor;
6273 end;
6274 end;
6275
6276end;
6277
6278Tools.Paint.finishHistoryRecord = function ( self )
6279
6280 if not self.State.HistoryRecord then
6281 return;
6282 end;
6283
6284 for _, Item in pairs( self.State.HistoryRecord.targets ) do
6285 if Item then
6286 self.State.HistoryRecord.terminal_colors[Item] = Item.BrickColor;
6287 end;
6288 end;
6289 History:add( self.State.HistoryRecord );
6290 self.State.HistoryRecord = nil;
6291
6292end;
6293
6294Tools.Paint.Listeners.Button1Up = function ()
6295
6296 local self = Tools.Paint;
6297
6298 -- Make sure that they clicked on one of the items in their selection
6299 -- (and they weren't multi-selecting)
6300 if Selection:find( Mouse.Target ) and not selecting and not selecting then
6301
6302 override_selection = true;
6303
6304 self:startHistoryRecord();
6305
6306 -- Paint all of the selected items `Tools.Paint.Options.Color`
6307 if self.Options.Color then
6308 for _, Item in pairs( Selection.Items ) do
6309 Item.BrickColor = self.Options.Color;
6310 end;
6311 end;
6312
6313 self:finishHistoryRecord();
6314
6315 end;
6316
6317end;
6318
6319Tools.Paint.changeColor = function ( self, Color )
6320
6321 -- Alright so if `Color` is given, set that as the preferred color
6322 if Color then
6323
6324 -- First of all, change the color option itself
6325 self.Options.Color = Color;
6326
6327 self:startHistoryRecord();
6328
6329 -- Then, we want to update the color of any items in the selection
6330 for _, Item in pairs( Selection.Items ) do
6331 Item.BrickColor = Color;
6332 end;
6333
6334 self:finishHistoryRecord();
6335
6336 -- After that, we want to mark our new color in the palette
6337 if self.GUI then
6338
6339 -- First clear out any other marks
6340 for _, ColorSquare in pairs( self.GUI.Palette:GetChildren() ) do
6341 ColorSquare.Text = "";
6342 end;
6343
6344 -- Then mark the right square
6345 self.GUI.Palette[Color.Name].Text = "X";
6346
6347 end;
6348
6349 -- Otherwise, let's assume no color at all
6350 else
6351
6352 -- Set the preferred color to none
6353 self.Options.Color = nil;
6354
6355 -- Clear out any color option marks on any of the squares
6356 if self.GUI then
6357 for _, ColorSquare in pairs( self.GUI.Palette:GetChildren() ) do
6358 ColorSquare.Text = "";
6359 end;
6360 end;
6361
6362 end;
6363
6364end;
6365
6366Tools.Paint.showGUI = function ( self )
6367
6368 -- Initialize the GUI if it's not ready yet
6369 if not self.GUI then
6370
6371 local Container = Tool.Interfaces:WaitForChild( "BTPaintToolGUI" ):Clone();
6372 Container.Parent = UI;
6373
6374 for _, ColorButton in pairs( Container.Palette:GetChildren() ) do
6375 ColorButton.MouseButton1Click:connect( function ()
6376 self:changeColor( BrickColor.new( ColorButton.Name ) );
6377 end );
6378 end;
6379
6380 self.GUI = Container;
6381 end;
6382
6383 -- Reveal the GUI
6384 self.GUI.Visible = true;
6385
6386end;
6387
6388Tools.Paint.hideGUI = function ( self )
6389
6390 -- Hide the GUI if it exists
6391 if self.GUI then
6392 self.GUI.Visible = false;
6393 end;
6394
6395end;
6396
6397Tools.Paint.Loaded = true;
6398end))
6399LocalScript5.Disabled = true
6400LocalScript6.Name = "BTSurfaceTool"
6401LocalScript6.Parent = LocalScript1
6402table.insert(cors,sandbox(LocalScript6,function()
6403-- Load the main tool's core environment when it's ready
6404repeat wait() until (
6405 _G.BTCoreEnv and
6406 _G.BTCoreEnv[script.Parent.Parent] and
6407 _G.BTCoreEnv[script.Parent.Parent].CoreReady
6408);
6409setfenv( 1, _G.BTCoreEnv[script.Parent.Parent] );
6410
6411------------------------------------------
6412-- Surface tool
6413------------------------------------------
6414
6415-- Create the tool
6416Tools.Surface = {};
6417
6418-- Define the tool's color
6419Tools.Surface.Color = BrickColor.new( "Bright violet" );
6420
6421-- Keep a container for temporary connections
6422Tools.Surface.Connections = {};
6423
6424-- Keep a container for state data
6425Tools.Surface.State = {
6426 ["type"] = nil;
6427};
6428
6429-- Maintain a container for options
6430Tools.Surface.Options = {
6431 ["side"] = Enum.NormalId.Front;
6432};
6433
6434-- Keep a container for platform event connections
6435Tools.Surface.Listeners = {};
6436
6437-- Start adding functionality to the tool
6438Tools.Surface.Listeners.Equipped = function ()
6439
6440 local self = Tools.Surface;
6441
6442 -- Change the color of selection boxes temporarily
6443 self.State.PreviousSelectionBoxColor = SelectionBoxColor;
6444 SelectionBoxColor = self.Color;
6445 updateSelectionBoxColor();
6446
6447 -- Reveal the GUI
6448 self:showGUI();
6449
6450 -- Restore the side option
6451 self:changeSurface( self.Options.side );
6452
6453 -- Update the GUI regularly
6454 coroutine.wrap( function ()
6455 updater_on = true;
6456
6457 -- Provide a function to stop the loop
6458 self.Updater = function ()
6459 updater_on = false;
6460 end;
6461
6462 while wait( 0.1 ) and updater_on do
6463
6464 -- Make sure the tool's equipped
6465 if CurrentTool == self then
6466
6467 -- Update the surface type of every item in the selection
6468 local surface_type = nil;
6469 for item_index, Item in pairs( Selection.Items ) do
6470
6471 -- Set the first values for the first item
6472 if item_index == 1 then
6473 surface_type = Item[self.Options.side.Name .. "Surface"];
6474
6475 -- Otherwise, compare them and set them to `nil` if they're not identical
6476 else
6477 if surface_type ~= Item[self.Options.side.Name .. "Surface"] then
6478 surface_type = nil;
6479 end;
6480 end;
6481
6482 end;
6483
6484 self.State.type = surface_type;
6485
6486 -- Update the GUI if it's visible
6487 if self.GUI and self.GUI.Visible then
6488 self:updateGUI();
6489 end;
6490
6491 end;
6492
6493 end;
6494
6495 end )();
6496
6497end;
6498
6499Tools.Surface.Listeners.Unequipped = function ()
6500
6501 local self = Tools.Surface;
6502
6503 -- Stop the GUI updating loop
6504 self.Updater();
6505 self.Updater = nil;
6506
6507 -- Hide the GUI
6508 self:hideGUI();
6509
6510 -- Disconnect temporary connections
6511 for connection_index, Connection in pairs( self.Connections ) do
6512 Connection:disconnect();
6513 self.Connections[connection_index] = nil;
6514 end;
6515
6516 -- Restore the original color of selection boxes
6517 SelectionBoxColor = self.State.PreviousSelectionBoxColor;
6518 updateSelectionBoxColor();
6519
6520end;
6521
6522Tools.Surface.Listeners.Button2Down = function ()
6523
6524 local self = Tools.Surface;
6525
6526 -- Capture the camera rotation (for later use
6527 -- in determining whether a surface was being
6528 -- selected or the camera was being rotated
6529 -- with the right mouse button)
6530 local cr_x, cr_y, cr_z = Services.Workspace.CurrentCamera.CoordinateFrame:toEulerAnglesXYZ();
6531 self.State.PreB2DownCameraRotation = Vector3.new( cr_x, cr_y, cr_z );
6532
6533end;
6534
6535Tools.Surface.Listeners.Button2Up = function ()
6536
6537 local self = Tools.Surface;
6538
6539 local cr_x, cr_y, cr_z = Services.Workspace.CurrentCamera.CoordinateFrame:toEulerAnglesXYZ();
6540 local CameraRotation = Vector3.new( cr_x, cr_y, cr_z );
6541
6542 -- If a surface is selected
6543 if Selection:find( Mouse.Target ) and self.State.PreB2DownCameraRotation == CameraRotation then
6544 self:changeSurface( Mouse.TargetSurface );
6545 end;
6546
6547end;
6548
6549Tools.Surface.startHistoryRecord = function ( self )
6550
6551 if self.State.HistoryRecord then
6552 self.State.HistoryRecord = nil;
6553 end;
6554
6555 -- Create a history record
6556 self.State.HistoryRecord = {
6557 targets = _cloneTable( Selection.Items );
6558 target_surface = self.Options.side;
6559 initial_surfaces = {};
6560 terminal_surfaces = {};
6561 unapply = function ( self )
6562 Selection:clear();
6563 for _, Target in pairs( self.targets ) do
6564 if Target then
6565 Target[self.target_surface.Name .. "Surface"] = self.initial_surfaces[Target];
6566 Target:MakeJoints();
6567 Selection:add( Target );
6568 end;
6569 end;
6570 end;
6571 apply = function ( self )
6572 Selection:clear();
6573 for _, Target in pairs( self.targets ) do
6574 if Target then
6575 Target[self.target_surface.Name .. "Surface"] = self.terminal_surfaces[Target];
6576 Target:MakeJoints();
6577 Selection:add( Target );
6578 end;
6579 end;
6580 end;
6581 };
6582 for _, Item in pairs( self.State.HistoryRecord.targets ) do
6583 if Item then
6584 self.State.HistoryRecord.initial_surfaces[Item] = Item[self.Options.side.Name .. "Surface"];
6585 end;
6586 end;
6587
6588end;
6589
6590Tools.Surface.finishHistoryRecord = function ( self )
6591
6592 if not self.State.HistoryRecord then
6593 return;
6594 end;
6595
6596 for _, Item in pairs( self.State.HistoryRecord.targets ) do
6597 if Item then
6598 self.State.HistoryRecord.terminal_surfaces[Item] = Item[self.Options.side.Name .. "Surface"];
6599 end;
6600 end;
6601 History:add( self.State.HistoryRecord );
6602 self.State.HistoryRecord = nil;
6603
6604end;
6605
6606Tools.Surface.SpecialTypeNames = {
6607 SmoothNoOutlines = "NO OUTLINE",
6608 Inlet = "INLETS"
6609};
6610
6611Tools.Surface.changeType = function ( self, surface_type )
6612
6613 self:startHistoryRecord();
6614
6615 -- Apply `surface_type` to all items in the selection
6616 for _, Item in pairs( Selection.Items ) do
6617 Item[self.Options.side.Name .. "Surface"] = surface_type;
6618 Item:MakeJoints();
6619 end;
6620
6621 self:finishHistoryRecord();
6622
6623 self.TypeDropdown:selectOption( self.SpecialTypeNames[surface_type.Name] or surface_type.Name:upper() );
6624 if self.TypeDropdown.open then
6625 self.TypeDropdown:toggle();
6626 end;
6627end;
6628
6629Tools.Surface.changeSurface = function ( self, surface )
6630 self.Options.side = surface;
6631 self.SideDropdown:selectOption( surface.Name:upper() );
6632 if self.SideDropdown.open then
6633 self.SideDropdown:toggle();
6634 end;
6635end;
6636
6637Tools.Surface.updateGUI = function ( self )
6638
6639 -- Make sure the GUI exists
6640 if not self.GUI then
6641 return;
6642 end;
6643
6644 if #Selection.Items > 0 then
6645 self.TypeDropdown:selectOption( self.State.type and ( self.SpecialTypeNames[self.State.type.Name] or self.State.type.Name:upper() ) or "*" );
6646 else
6647 self.TypeDropdown:selectOption( "" );
6648 end;
6649
6650end;
6651
6652Tools.Surface.showGUI = function ( self )
6653
6654 -- Initialize the GUI if it's not ready yet
6655 if not self.GUI then
6656
6657 local Container = Tool.Interfaces:WaitForChild( "BTSurfaceToolGUI" ):Clone();
6658 Container.Parent = UI;
6659
6660 local SideDropdown = createDropdown();
6661 self.SideDropdown = SideDropdown;
6662 SideDropdown.Frame.Parent = Container.SideOption;
6663 SideDropdown.Frame.Position = UDim2.new( 0, 30, 0, 0 );
6664 SideDropdown.Frame.Size = UDim2.new( 0, 72, 0, 25 );
6665
6666 SideDropdown:addOption( "TOP" ).MouseButton1Up:connect( function ()
6667 self:changeSurface( Enum.NormalId.Top );
6668 end );
6669 SideDropdown:addOption( "BOTTOM" ).MouseButton1Up:connect( function ()
6670 self:changeSurface( Enum.NormalId.Bottom );
6671 end );
6672 SideDropdown:addOption( "FRONT" ).MouseButton1Up:connect( function ()
6673 self:changeSurface( Enum.NormalId.Front );
6674 end );
6675 SideDropdown:addOption( "BACK" ).MouseButton1Up:connect( function ()
6676 self:changeSurface( Enum.NormalId.Back );
6677 end );
6678 SideDropdown:addOption( "LEFT" ).MouseButton1Up:connect( function ()
6679 self:changeSurface( Enum.NormalId.Left );
6680 end );
6681 SideDropdown:addOption( "RIGHT" ).MouseButton1Up:connect( function ()
6682 self:changeSurface( Enum.NormalId.Right );
6683 end );
6684
6685 local TypeDropdown = createDropdown();
6686 self.TypeDropdown = TypeDropdown;
6687 TypeDropdown.Frame.Parent = Container.TypeOption;
6688 TypeDropdown.Frame.Position = UDim2.new( 0, 30, 0, 0 );
6689 TypeDropdown.Frame.Size = UDim2.new( 0, 87, 0, 25 );
6690
6691 TypeDropdown:addOption( "STUDS" ).MouseButton1Up:connect( function ()
6692 self:changeType( Enum.SurfaceType.Studs );
6693 end );
6694 TypeDropdown:addOption( "INLETS" ).MouseButton1Up:connect( function ()
6695 self:changeType( Enum.SurfaceType.Inlet );
6696 end );
6697 TypeDropdown:addOption( "SMOOTH" ).MouseButton1Up:connect( function ()
6698 self:changeType( Enum.SurfaceType.Smooth );
6699 end );
6700 TypeDropdown:addOption( "WELD" ).MouseButton1Up:connect( function ()
6701 self:changeType( Enum.SurfaceType.Weld );
6702 end );
6703 TypeDropdown:addOption( "GLUE" ).MouseButton1Up:connect( function ()
6704 self:changeType( Enum.SurfaceType.Glue );
6705 end );
6706 TypeDropdown:addOption( "UNIVERSAL" ).MouseButton1Up:connect( function ()
6707 self:changeType( Enum.SurfaceType.Universal );
6708 end );
6709 TypeDropdown:addOption( "HINGE" ).MouseButton1Up:connect( function ()
6710 self:changeType( Enum.SurfaceType.Hinge );
6711 end );
6712 TypeDropdown:addOption( "MOTOR" ).MouseButton1Up:connect( function ()
6713 self:changeType( Enum.SurfaceType.Motor );
6714 end );
6715 TypeDropdown:addOption( "NO OUTLINE" ).MouseButton1Up:connect( function ()
6716 self:changeType( Enum.SurfaceType.SmoothNoOutlines );
6717 end );
6718
6719 self.GUI = Container;
6720
6721 end;
6722
6723 -- Reveal the GUI
6724 self.GUI.Visible = true;
6725
6726end;
6727
6728Tools.Surface.hideGUI = function ( self )
6729
6730 -- Hide the GUI if it exists already
6731 if self.GUI then
6732 self.GUI.Visible = false;
6733 end;
6734
6735end;
6736
6737Tools.Surface.Loaded = true;
6738end))
6739LocalScript6.Disabled = true
6740LocalScript7.Name = "BTMaterialTool"
6741LocalScript7.Parent = LocalScript1
6742table.insert(cors,sandbox(LocalScript7,function()
6743-- Load the main tool's core environment when it's ready
6744repeat wait() until (
6745 _G.BTCoreEnv and
6746 _G.BTCoreEnv[script.Parent.Parent] and
6747 _G.BTCoreEnv[script.Parent.Parent].CoreReady
6748);
6749setfenv( 1, _G.BTCoreEnv[script.Parent.Parent] );
6750
6751------------------------------------------
6752-- Material tool
6753------------------------------------------
6754
6755-- Create the tool
6756Tools.Material = {};
6757Tools.Material.Color = BrickColor.new( "Bright violet" );
6758Tools.Material.Connections = {};
6759Tools.Material.State = {
6760 ["material"] = nil;
6761 ["reflectance_focused"] = false;
6762 ["transparency_focused"] = false;
6763};
6764Tools.Material.Listeners = {};
6765Tools.Material.SpecialMaterialNames = {
6766 CorrodedMetal = "CORRODED METAL",
6767 DiamondPlate = "DIAMOND PLATE",
6768 SmoothPlastic = "SMOOTH PLASTIC"
6769};
6770
6771-- Start adding functionality to the tool
6772Tools.Material.Listeners.Equipped = function ()
6773
6774 local self = Tools.Material;
6775
6776 -- Change the color of selection boxes temporarily
6777 self.State.PreviousSelectionBoxColor = SelectionBoxColor;
6778 SelectionBoxColor = self.Color;
6779 updateSelectionBoxColor();
6780
6781 -- Reveal the GUI
6782 self:showGUI();
6783
6784 -- Update the GUI regularly
6785 coroutine.wrap( function ()
6786 updater_on = true;
6787
6788 -- Provide a function to stop the loop
6789 self.Updater = function ()
6790 updater_on = false;
6791 end;
6792
6793 while wait( 0.1 ) and updater_on do
6794
6795 -- Make sure the tool's equipped
6796 if CurrentTool == self then
6797
6798 -- Update the material type of every item in the selection
6799 local material_type, transparency, reflectance = nil, nil, nil;
6800 for item_index, Item in pairs( Selection.Items ) do
6801
6802 -- Set the first values for the first item
6803 if item_index == 1 then
6804 material_type = Item.Material;
6805 transparency = Item.Transparency;
6806 reflectance = Item.Reflectance;
6807
6808 -- Otherwise, compare them and set them to `nil` if they're not identical
6809 else
6810 if material_type ~= Item.Material then
6811 material_type = nil;
6812 end;
6813 if reflectance ~= Item.Reflectance then
6814 reflectance = nil;
6815 end;
6816 if transparency ~= Item.Transparency then
6817 transparency = nil;
6818 end;
6819 end;
6820
6821 end;
6822
6823 self.State.material = material_type;
6824 self.State.transparency = transparency;
6825 self.State.reflectance = reflectance;
6826
6827 -- Update the GUI if it's visible
6828 if self.GUI and self.GUI.Visible then
6829 self:updateGUI();
6830 end;
6831
6832 end;
6833
6834 end;
6835
6836 end )();
6837
6838end;
6839
6840Tools.Material.Listeners.Unequipped = function ()
6841
6842 local self = Tools.Material;
6843
6844 -- Stop the GUI updating loop
6845 self.Updater();
6846 self.Updater = nil;
6847
6848 -- Hide the GUI
6849 self:hideGUI();
6850
6851 -- Disconnect temporary connections
6852 for connection_index, Connection in pairs( self.Connections ) do
6853 Connection:disconnect();
6854 self.Connections[connection_index] = nil;
6855 end;
6856
6857 -- Restore the original color of selection boxes
6858 SelectionBoxColor = self.State.PreviousSelectionBoxColor;
6859 updateSelectionBoxColor();
6860
6861end;
6862
6863Tools.Material.startHistoryRecord = function ( self )
6864
6865 if self.State.HistoryRecord then
6866 self.State.HistoryRecord = nil;
6867 end;
6868
6869 -- Create a history record
6870 self.State.HistoryRecord = {
6871 targets = _cloneTable( Selection.Items );
6872 initial_material = {};
6873 terminal_material = {};
6874 initial_transparency = {};
6875 terminal_transparency = {};
6876 initial_reflectance = {};
6877 terminal_reflectance = {};
6878 unapply = function ( self )
6879 Selection:clear();
6880 for _, Target in pairs( self.targets ) do
6881 if Target then
6882 Target.Material = self.initial_material[Target];
6883 Target.Transparency = self.initial_transparency[Target];
6884 Target.Reflectance = self.initial_reflectance[Target];
6885 Selection:add( Target );
6886 end;
6887 end;
6888 end;
6889 apply = function ( self )
6890 Selection:clear();
6891 for _, Target in pairs( self.targets ) do
6892 if Target then
6893 Target.Material = self.terminal_material[Target];
6894 Target.Transparency = self.terminal_transparency[Target];
6895 Target.Reflectance = self.terminal_reflectance[Target];
6896 Selection:add( Target );
6897 end;
6898 end;
6899 end;
6900 };
6901 for _, Item in pairs( self.State.HistoryRecord.targets ) do
6902 if Item then
6903 self.State.HistoryRecord.initial_material[Item] = Item.Material;
6904 self.State.HistoryRecord.initial_transparency[Item] = Item.Transparency;
6905 self.State.HistoryRecord.initial_reflectance[Item] = Item.Reflectance;
6906 end;
6907 end;
6908
6909end;
6910
6911Tools.Material.finishHistoryRecord = function ( self )
6912
6913 if not self.State.HistoryRecord then
6914 return;
6915 end;
6916
6917 for _, Item in pairs( self.State.HistoryRecord.targets ) do
6918 if Item then
6919 self.State.HistoryRecord.terminal_material[Item] = Item.Material;
6920 self.State.HistoryRecord.terminal_transparency[Item] = Item.Transparency;
6921 self.State.HistoryRecord.terminal_reflectance[Item] = Item.Reflectance;
6922 end;
6923 end;
6924 History:add( self.State.HistoryRecord );
6925 self.State.HistoryRecord = nil;
6926
6927end;
6928
6929Tools.Material.changeMaterial = function ( self, material_type )
6930
6931 self:startHistoryRecord();
6932
6933 -- Apply `material_type` to all items in the selection
6934 for _, Item in pairs( Selection.Items ) do
6935 Item.Material = material_type;
6936 end;
6937
6938 self:finishHistoryRecord();
6939
6940 if self.MaterialDropdown.open then
6941 self.MaterialDropdown:toggle();
6942 end;
6943end;
6944
6945Tools.Material.changeTransparency = function ( self, transparency )
6946
6947 self:startHistoryRecord();
6948
6949 -- Apply `transparency` to all items in the selection
6950 for _, Item in pairs( Selection.Items ) do
6951 Item.Transparency = transparency;
6952 end;
6953
6954 self:finishHistoryRecord();
6955
6956end;
6957
6958Tools.Material.changeReflectance = function ( self, reflectance )
6959
6960 self:startHistoryRecord();
6961
6962 -- Apply `reflectance` to all items in the selection
6963 for _, Item in pairs( Selection.Items ) do
6964 Item.Reflectance = reflectance;
6965 end;
6966
6967 self:finishHistoryRecord();
6968
6969end;
6970
6971Tools.Material.updateGUI = function ( self )
6972
6973 -- Make sure the GUI exists
6974 if not self.GUI then
6975 return;
6976 end;
6977
6978 if #Selection.Items > 0 then
6979 self.GUI.Size = UDim2.new( 0, 200, 0, 145 );
6980 self.GUI.MaterialOption.Visible = true;
6981 self.GUI.ReflectanceOption.Visible = true;
6982 self.GUI.TransparencyOption.Visible = true;
6983 self.GUI.SelectNote.Visible = false;
6984 self.MaterialDropdown:selectOption( self.State.material and ( self.SpecialMaterialNames[self.State.material.Name] or self.State.material.Name:upper() ) or "*" );
6985
6986 -- Update the text inputs without interrupting the user
6987 if not self.State.transparency_focused then
6988 self.GUI.TransparencyOption.TransparencyInput.TextBox.Text = self.State.transparency and tostring( _round( self.State.transparency, 2 ) ) or "*";
6989 end;
6990 if not self.State.reflectance_focused then
6991 self.GUI.ReflectanceOption.ReflectanceInput.TextBox.Text = self.State.reflectance and tostring( _round( self.State.reflectance, 2 ) ) or "*";
6992 end;
6993
6994 else
6995 self.GUI.Size = UDim2.new( 0, 200, 0, 62 );
6996 self.GUI.MaterialOption.Visible = false;
6997 self.GUI.ReflectanceOption.Visible = false;
6998 self.GUI.TransparencyOption.Visible = false;
6999 self.GUI.SelectNote.Visible = true;
7000 self.MaterialDropdown:selectOption( "" );
7001 self.GUI.TransparencyOption.TransparencyInput.TextBox.Text = "";
7002 self.GUI.ReflectanceOption.ReflectanceInput.TextBox.Text = "";
7003 end;
7004
7005end;
7006
7007
7008Tools.Material.showGUI = function ( self )
7009
7010 -- Initialize the GUI if it's not ready yet
7011 if not self.GUI then
7012
7013 local Container = Tool.Interfaces:WaitForChild( "BTMaterialToolGUI" ):Clone();
7014 Container.Parent = UI;
7015
7016 local MaterialDropdown = createDropdown();
7017 self.MaterialDropdown = MaterialDropdown;
7018 MaterialDropdown.Frame.Parent = Container.MaterialOption;
7019 MaterialDropdown.Frame.Position = UDim2.new( 0, 50, 0, 0 );
7020 MaterialDropdown.Frame.Size = UDim2.new( 0, 130, 0, 25 );
7021
7022 MaterialDropdown:addOption( "SMOOTH PLASTIC" ).MouseButton1Up:connect( function ()
7023 self:changeMaterial( Enum.Material.SmoothPlastic );
7024 end );
7025 MaterialDropdown:addOption( "PLASTIC" ).MouseButton1Up:connect( function ()
7026 self:changeMaterial( Enum.Material.Plastic );
7027 end );
7028 MaterialDropdown:addOption( "CONCRETE" ).MouseButton1Up:connect( function ()
7029 self:changeMaterial( Enum.Material.Concrete );
7030 end );
7031 MaterialDropdown:addOption( "DIAMOND PLATE" ).MouseButton1Up:connect( function ()
7032 self:changeMaterial( Enum.Material.DiamondPlate );
7033 end );
7034 MaterialDropdown:addOption( "CORRODED METAL" ).MouseButton1Up:connect( function ()
7035 self:changeMaterial( Enum.Material.CorrodedMetal );
7036 end );
7037 MaterialDropdown:addOption( "BRICK" ).MouseButton1Up:connect( function ()
7038 self:changeMaterial( Enum.Material.Brick );
7039 end );
7040 MaterialDropdown:addOption( "FABRIC" ).MouseButton1Up:connect( function ()
7041 self:changeMaterial( Enum.Material.Fabric );
7042 end );
7043 MaterialDropdown:addOption( "FOIL" ).MouseButton1Up:connect( function ()
7044 self:changeMaterial( Enum.Material.Foil );
7045 end );
7046 MaterialDropdown:addOption( "GRANITE" ).MouseButton1Up:connect( function ()
7047 self:changeMaterial( Enum.Material.Granite );
7048 end );
7049 MaterialDropdown:addOption( "GRASS" ).MouseButton1Up:connect( function ()
7050 self:changeMaterial( Enum.Material.Grass );
7051 end );
7052 MaterialDropdown:addOption( "ICE" ).MouseButton1Up:connect( function ()
7053 self:changeMaterial( Enum.Material.Ice );
7054 end );
7055 MaterialDropdown:addOption( "MARBLE" ).MouseButton1Up:connect( function ()
7056 self:changeMaterial( Enum.Material.Marble );
7057 end );
7058 MaterialDropdown:addOption( "PEBBLE" ).MouseButton1Up:connect( function ()
7059 self:changeMaterial( Enum.Material.Pebble );
7060 end );
7061 MaterialDropdown:addOption( "SAND" ).MouseButton1Up:connect( function ()
7062 self:changeMaterial( Enum.Material.Sand );
7063 end );
7064 MaterialDropdown:addOption( "SLATE" ).MouseButton1Up:connect( function ()
7065 self:changeMaterial( Enum.Material.Slate );
7066 end );
7067 MaterialDropdown:addOption( "WOOD" ).MouseButton1Up:connect( function ()
7068 self:changeMaterial( Enum.Material.Wood );
7069 end );
7070
7071 -- Capture focus of the input when clicked
7072 -- (so we can detect when it is focused-on)
7073 Container.TransparencyOption.TransparencyInput.TextButton.MouseButton1Down:connect( function ()
7074 self.State.transparency_focused = true;
7075 Container.TransparencyOption.TransparencyInput.TextBox:CaptureFocus();
7076 end );
7077
7078 -- Change the transparency when the value of the textbox is updated
7079 Container.TransparencyOption.TransparencyInput.TextBox.FocusLost:connect( function ( enter_pressed )
7080 local potential_new = tonumber( Container.TransparencyOption.TransparencyInput.TextBox.Text );
7081 if potential_new then
7082 if potential_new > 1 then
7083 potential_new = 1;
7084 elseif potential_new < 0 then
7085 potential_new = 0;
7086 end;
7087 self:changeTransparency( potential_new );
7088 end;
7089 self.State.transparency_focused = false;
7090 end );
7091
7092 -- Capture focus of the input when clicked
7093 -- (so we can detect when it is focused-on)
7094 Container.ReflectanceOption.ReflectanceInput.TextButton.MouseButton1Down:connect( function ()
7095 self.State.reflectance_focused = true;
7096 Container.ReflectanceOption.ReflectanceInput.TextBox:CaptureFocus();
7097 end );
7098
7099 -- Change the reflectance when the value of the textbox is updated
7100 Container.ReflectanceOption.ReflectanceInput.TextBox.FocusLost:connect( function ( enter_pressed )
7101 local potential_new = tonumber( Container.ReflectanceOption.ReflectanceInput.TextBox.Text );
7102 if potential_new then
7103 if potential_new > 1 then
7104 potential_new = 1;
7105 elseif potential_new < 0 then
7106 potential_new = 0;
7107 end;
7108 self:changeReflectance( potential_new );
7109 end;
7110 self.State.reflectance_focused = false;
7111 end );
7112
7113 self.GUI = Container;
7114
7115 end;
7116
7117 -- Reveal the GUI
7118 self.GUI.Visible = true;
7119
7120end;
7121
7122Tools.Material.hideGUI = function ( self )
7123
7124 -- Hide the GUI if it exists already
7125 if self.GUI then
7126 self.GUI.Visible = false;
7127 end;
7128
7129end;
7130
7131Tools.Material.Loaded = true;
7132end))
7133LocalScript7.Disabled = true
7134LocalScript8.Name = "BTAnchorTool"
7135LocalScript8.Parent = LocalScript1
7136table.insert(cors,sandbox(LocalScript8,function()
7137-- Load the main tool's core environment when it's ready
7138repeat wait() until (
7139 _G.BTCoreEnv and
7140 _G.BTCoreEnv[script.Parent.Parent] and
7141 _G.BTCoreEnv[script.Parent.Parent].CoreReady
7142);
7143setfenv( 1, _G.BTCoreEnv[script.Parent.Parent] );
7144
7145------------------------------------------
7146-- Anchor tool
7147------------------------------------------
7148
7149-- Create the tool
7150Tools.Anchor = {};
7151
7152-- Create structures to hold data that the tool needs
7153Tools.Anchor.Connections = {};
7154
7155Tools.Anchor.State = {
7156 ["anchored"] = nil;
7157};
7158
7159Tools.Anchor.Listeners = {};
7160
7161-- Define the color of the tool
7162Tools.Anchor.Color = BrickColor.new( "Really black" );
7163
7164-- Start adding functionality to the tool
7165Tools.Anchor.Listeners.Equipped = function ()
7166
7167 local self = Tools.Anchor;
7168
7169 -- Change the color of selection boxes temporarily
7170 self.State.PreviousSelectionBoxColor = SelectionBoxColor;
7171 SelectionBoxColor = self.Color;
7172 updateSelectionBoxColor();
7173
7174 -- Reveal the GUI
7175 self:showGUI();
7176
7177 -- Update the GUI regularly
7178 coroutine.wrap( function ()
7179 updater_on = true;
7180
7181 -- Provide a function to stop the loop
7182 self.Updater = function ()
7183 updater_on = false;
7184 end;
7185
7186 while wait( 0.1 ) and updater_on do
7187
7188 -- Make sure the tool's equipped
7189 if CurrentTool == self then
7190
7191 -- Update the anchor status of every item in the selection
7192 local anchor_status = nil;
7193 for item_index, Item in pairs( Selection.Items ) do
7194
7195 -- Set the first values for the first item
7196 if item_index == 1 then
7197 anchor_status = Item.Anchored;
7198
7199 -- Otherwise, compare them and set them to `nil` if they're not identical
7200 else
7201 if anchor_status ~= Item.Anchored then
7202 anchor_status = nil;
7203 end;
7204 end;
7205
7206 end;
7207
7208 self.State.anchored = anchor_status;
7209
7210 -- Update the GUI if it's visible
7211 if self.GUI and self.GUI.Visible then
7212 self:updateGUI();
7213 end;
7214
7215 end;
7216
7217 end;
7218
7219 end )();
7220
7221 -- Listen for the Enter button to be pressed to toggle the anchor
7222 self.Connections.EnterButtonListener = Mouse.KeyDown:connect( function ( key )
7223
7224 local key = key:lower();
7225 local key_code = key:byte();
7226
7227 -- If the Enter button is pressed
7228 if key_code == 13 then
7229
7230 if self.State.anchored == true then
7231 self:unanchor();
7232
7233 elseif self.State.anchored == false then
7234 self:anchor();
7235
7236 elseif self.State.anchored == nil then
7237 self:anchor();
7238
7239 end;
7240
7241 end;
7242
7243 end );
7244
7245end;
7246
7247
7248Tools.Anchor.startHistoryRecord = function ( self )
7249
7250 if self.State.HistoryRecord then
7251 self.State.HistoryRecord = nil;
7252 end;
7253
7254 -- Create a history record
7255 self.State.HistoryRecord = {
7256 targets = _cloneTable( Selection.Items );
7257 initial_positions = {};
7258 terminal_positions = {};
7259 initial_anchors = {};
7260 terminal_anchors = {};
7261 unapply = function ( self )
7262 Selection:clear();
7263 for _, Target in pairs( self.targets ) do
7264 if Target then
7265 Target.RotVelocity = Vector3.new( 0, 0, 0 );
7266 Target.Velocity = Vector3.new( 0, 0, 0 );
7267 Target.CFrame = self.initial_positions[Target];
7268 Target.Anchored = self.initial_anchors[Target];
7269 Target:MakeJoints();
7270 Selection:add( Target );
7271 end;
7272 end;
7273 end;
7274 apply = function ( self )
7275 Selection:clear();
7276 for _, Target in pairs( self.targets ) do
7277 if Target then
7278 Target.RotVelocity = Vector3.new( 0, 0, 0 );
7279 Target.Velocity = Vector3.new( 0, 0, 0 );
7280 Target.CFrame = self.terminal_positions[Target];
7281 Target.Anchored = self.terminal_anchors[Target];
7282 Target:MakeJoints();
7283 Selection:add( Target );
7284 end;
7285 end;
7286 end;
7287 };
7288 for _, Item in pairs( self.State.HistoryRecord.targets ) do
7289 if Item then
7290 self.State.HistoryRecord.initial_anchors[Item] = Item.Anchored;
7291 self.State.HistoryRecord.initial_positions[Item] = Item.CFrame;
7292 end;
7293 end;
7294
7295end;
7296
7297Tools.Anchor.finishHistoryRecord = function ( self )
7298
7299 if not self.State.HistoryRecord then
7300 return;
7301 end;
7302
7303 for _, Item in pairs( self.State.HistoryRecord.targets ) do
7304 if Item then
7305 self.State.HistoryRecord.terminal_anchors[Item] = Item.Anchored;
7306 self.State.HistoryRecord.terminal_positions[Item] = Item.CFrame;
7307 end;
7308 end;
7309 History:add( self.State.HistoryRecord );
7310 self.State.HistoryRecord = nil;
7311
7312end;
7313
7314Tools.Anchor.anchor = function ( self )
7315
7316 self:startHistoryRecord();
7317
7318 -- Anchor all the items in the selection
7319 for _, Item in pairs( Selection.Items ) do
7320 Item.Anchored = true;
7321 Item:MakeJoints();
7322 end;
7323
7324 self:finishHistoryRecord();
7325
7326end;
7327
7328Tools.Anchor.unanchor = function ( self )
7329
7330 self:startHistoryRecord();
7331
7332 -- Unanchor all the items in the selection
7333 for _, Item in pairs( Selection.Items ) do
7334 Item.Anchored = false;
7335 Item.Velocity = Vector3.new( 0, 0, 0 );
7336 Item.RotVelocity = Vector3.new( 0, 0, 0 );
7337 Item:MakeJoints();
7338 end;
7339
7340 self:finishHistoryRecord();
7341
7342end;
7343
7344Tools.Anchor.showGUI = function ( self )
7345
7346 -- Initialize the GUI if it's not ready yet
7347 if not self.GUI then
7348
7349 local Container = Tool.Interfaces:WaitForChild( "BTAnchorToolGUI" ):Clone();
7350 Container.Parent = UI;
7351
7352 -- Change the anchor status when the button is clicked
7353 Container.Status.Anchored.Button.MouseButton1Down:connect( function ()
7354 self:anchor();
7355 end );
7356
7357 Container.Status.Unanchored.Button.MouseButton1Down:connect( function ()
7358 self:unanchor();
7359 end );
7360
7361 self.GUI = Container;
7362 end;
7363
7364 -- Reveal the GUI
7365 self.GUI.Visible = true;
7366
7367end;
7368
7369Tools.Anchor.updateGUI = function ( self )
7370
7371 -- Make sure the GUI exists
7372 if not self.GUI then
7373 return;
7374 end;
7375
7376 local GUI = self.GUI;
7377
7378 if self.State.anchored == nil then
7379 GUI.Status.Anchored.Background.Image = light_slanted_rectangle;
7380 GUI.Status.Anchored.SelectedIndicator.BackgroundTransparency = 1;
7381 GUI.Status.Unanchored.Background.Image = light_slanted_rectangle;
7382 GUI.Status.Unanchored.SelectedIndicator.BackgroundTransparency = 1;
7383
7384 elseif self.State.anchored == true then
7385 GUI.Status.Anchored.Background.Image = dark_slanted_rectangle;
7386 GUI.Status.Anchored.SelectedIndicator.BackgroundTransparency = 0;
7387 GUI.Status.Unanchored.Background.Image = light_slanted_rectangle;
7388 GUI.Status.Unanchored.SelectedIndicator.BackgroundTransparency = 1;
7389
7390 elseif self.State.anchored == false then
7391 GUI.Status.Anchored.Background.Image = light_slanted_rectangle;
7392 GUI.Status.Anchored.SelectedIndicator.BackgroundTransparency = 1;
7393 GUI.Status.Unanchored.Background.Image = dark_slanted_rectangle;
7394 GUI.Status.Unanchored.SelectedIndicator.BackgroundTransparency = 0;
7395
7396 end;
7397
7398end;
7399
7400Tools.Anchor.hideGUI = function ( self )
7401
7402 -- Hide the GUI if it exists
7403 if self.GUI then
7404 self.GUI.Visible = false;
7405 end;
7406
7407end;
7408
7409Tools.Anchor.Listeners.Unequipped = function ()
7410
7411 local self = Tools.Anchor;
7412
7413 -- Stop the update loop
7414 if self.Updater then
7415 self.Updater();
7416 self.Updater = nil;
7417 end;
7418
7419 -- Hide the GUI
7420 self:hideGUI();
7421
7422 -- Clear out any temporary connections
7423 for connection_index, Connection in pairs( self.Connections ) do
7424 Connection:disconnect();
7425 self.Connections[connection_index] = nil;
7426 end;
7427
7428 -- Restore the original color of the selection boxes
7429 SelectionBoxColor = self.State.PreviousSelectionBoxColor;
7430 updateSelectionBoxColor();
7431
7432end;
7433
7434Tools.Anchor.Loaded = true;
7435end))
7436LocalScript8.Disabled = true
7437LocalScript9.Name = "BTCollisionTool"
7438LocalScript9.Parent = LocalScript1
7439table.insert(cors,sandbox(LocalScript9,function()
7440-- Load the main tool's core environment when it's ready
7441repeat wait() until (
7442 _G.BTCoreEnv and
7443 _G.BTCoreEnv[script.Parent.Parent] and
7444 _G.BTCoreEnv[script.Parent.Parent].CoreReady
7445);
7446setfenv( 1, _G.BTCoreEnv[script.Parent.Parent] );
7447
7448------------------------------------------
7449-- Collision tool
7450------------------------------------------
7451
7452-- Create the tool
7453Tools.Collision = {};
7454
7455-- Create structures to hold data that the tool needs
7456Tools.Collision.Connections = {};
7457
7458Tools.Collision.State = {
7459 ["colliding"] = nil;
7460};
7461
7462Tools.Collision.Listeners = {};
7463
7464-- Define the color of the tool
7465Tools.Collision.Color = BrickColor.new( "Really black" );
7466
7467-- Start adding functionality to the tool
7468Tools.Collision.Listeners.Equipped = function ()
7469
7470 local self = Tools.Collision;
7471
7472 -- Change the color of selection boxes temporarily
7473 self.State.PreviousSelectionBoxColor = SelectionBoxColor;
7474 SelectionBoxColor = self.Color;
7475 updateSelectionBoxColor();
7476
7477 -- Reveal the GUI
7478 self:showGUI();
7479
7480 -- Update the GUI regularly
7481 coroutine.wrap( function ()
7482 updater_on = true;
7483
7484 -- Provide a function to stop the loop
7485 self.Updater = function ()
7486 updater_on = false;
7487 end;
7488
7489 while wait( 0.1 ) and updater_on do
7490
7491 -- Make sure the tool's equipped
7492 if CurrentTool == self then
7493
7494 -- Update the collision status of every item in the selection
7495 local colliding = nil;
7496 for item_index, Item in pairs( Selection.Items ) do
7497
7498 -- Set the first values for the first item
7499 if item_index == 1 then
7500 colliding = Item.CanCollide;
7501
7502 -- Otherwise, compare them and set them to `nil` if they're not identical
7503 else
7504 if colliding ~= Item.CanCollide then
7505 colliding = nil;
7506 end;
7507 end;
7508
7509 end;
7510
7511 self.State.colliding = colliding;
7512
7513 -- Update the GUI if it's visible
7514 if self.GUI and self.GUI.Visible then
7515 self:updateGUI();
7516 end;
7517
7518 end;
7519
7520 end;
7521
7522 end )();
7523
7524 -- Listen for the Enter button to be pressed to toggle collision
7525 self.Connections.EnterButtonListener = Mouse.KeyDown:connect( function ( key )
7526
7527 local key = key:lower();
7528 local key_code = key:byte();
7529
7530 -- If the Enter button is pressed
7531 if key_code == 13 then
7532
7533 if self.State.colliding == true then
7534 self:disable();
7535
7536 elseif self.State.colliding == false then
7537 self:enable();
7538
7539 elseif self.State.colliding == nil then
7540 self:enable();
7541
7542 end;
7543
7544 end;
7545
7546 end );
7547
7548end;
7549
7550Tools.Collision.startHistoryRecord = function ( self )
7551
7552 if self.State.HistoryRecord then
7553 self.State.HistoryRecord = nil;
7554 end;
7555
7556 -- Create a history record
7557 self.State.HistoryRecord = {
7558 targets = _cloneTable( Selection.Items );
7559 initial_collide = {};
7560 terminal_collide = {};
7561 initial_cframe = {};
7562 terminal_cframe = {};
7563 unapply = function ( self )
7564 Selection:clear();
7565 for _, Target in pairs( self.targets ) do
7566 if Target then
7567 Target.CanCollide = self.initial_collide[Target];
7568 Target.CFrame = self.initial_cframe[Target];
7569 Target:MakeJoints();
7570 Selection:add( Target );
7571 end;
7572 end;
7573 end;
7574 apply = function ( self )
7575 Selection:clear();
7576 for _, Target in pairs( self.targets ) do
7577 if Target then
7578 Target.CanCollide = self.terminal_collide[Target];
7579 Target.CFrame = self.terminal_cframe[Target];
7580 Target:MakeJoints();
7581 Selection:add( Target );
7582 end;
7583 end;
7584 end;
7585 };
7586 for _, Item in pairs( self.State.HistoryRecord.targets ) do
7587 if Item then
7588 self.State.HistoryRecord.initial_collide[Item] = Item.CanCollide;
7589 self.State.HistoryRecord.initial_cframe[Item] = Item.CFrame;
7590 end;
7591 end;
7592
7593end;
7594
7595Tools.Collision.finishHistoryRecord = function ( self )
7596
7597 if not self.State.HistoryRecord then
7598 return;
7599 end;
7600
7601 for _, Item in pairs( self.State.HistoryRecord.targets ) do
7602 if Item then
7603 self.State.HistoryRecord.terminal_collide[Item] = Item.CanCollide;
7604 self.State.HistoryRecord.terminal_cframe[Item] = Item.CFrame;
7605 end;
7606 end;
7607 History:add( self.State.HistoryRecord );
7608 self.State.HistoryRecord = nil;
7609
7610end;
7611
7612Tools.Collision.enable = function ( self )
7613
7614 self:startHistoryRecord();
7615
7616 -- Enable collision for all the items in the selection
7617 for _, Item in pairs( Selection.Items ) do
7618 Item.CanCollide = true;
7619 Item:MakeJoints();
7620 end;
7621
7622 self:finishHistoryRecord();
7623
7624end;
7625
7626Tools.Collision.disable = function ( self )
7627
7628 self:startHistoryRecord();
7629
7630 -- Disable collision for all the items in the selection
7631 for _, Item in pairs( Selection.Items ) do
7632 Item.CanCollide = false;
7633 Item:MakeJoints();
7634 end;
7635
7636 self:finishHistoryRecord();
7637
7638end;
7639
7640Tools.Collision.showGUI = function ( self )
7641
7642 -- Initialize the GUI if it's not ready yet
7643 if not self.GUI then
7644
7645 local Container = Tool.Interfaces:WaitForChild( "BTCollisionToolGUI" ):Clone();
7646 Container.Parent = UI;
7647
7648 Container.Status.On.Button.MouseButton1Down:connect( function ()
7649 self:enable();
7650 end );
7651
7652 Container.Status.Off.Button.MouseButton1Down:connect( function ()
7653 self:disable();
7654 end );
7655
7656 self.GUI = Container;
7657 end;
7658
7659 -- Reveal the GUI
7660 self.GUI.Visible = true;
7661
7662end;
7663
7664Tools.Collision.updateGUI = function ( self )
7665
7666 -- Make sure the GUI exists
7667 if not self.GUI then
7668 return;
7669 end;
7670
7671 local GUI = self.GUI;
7672
7673 if self.State.colliding == nil then
7674 GUI.Status.On.Background.Image = light_slanted_rectangle;
7675 GUI.Status.On.SelectedIndicator.BackgroundTransparency = 1;
7676 GUI.Status.Off.Background.Image = light_slanted_rectangle;
7677 GUI.Status.Off.SelectedIndicator.BackgroundTransparency = 1;
7678
7679 elseif self.State.colliding == true then
7680 GUI.Status.On.Background.Image = dark_slanted_rectangle;
7681 GUI.Status.On.SelectedIndicator.BackgroundTransparency = 0;
7682 GUI.Status.Off.Background.Image = light_slanted_rectangle;
7683 GUI.Status.Off.SelectedIndicator.BackgroundTransparency = 1;
7684
7685 elseif self.State.colliding == false then
7686 GUI.Status.On.Background.Image = light_slanted_rectangle;
7687 GUI.Status.On.SelectedIndicator.BackgroundTransparency = 1;
7688 GUI.Status.Off.Background.Image = dark_slanted_rectangle;
7689 GUI.Status.Off.SelectedIndicator.BackgroundTransparency = 0;
7690
7691 end;
7692
7693end;
7694
7695Tools.Collision.hideGUI = function ( self )
7696
7697 -- Hide the GUI if it exists
7698 if self.GUI then
7699 self.GUI.Visible = false;
7700 end;
7701
7702end;
7703
7704Tools.Collision.Listeners.Unequipped = function ()
7705
7706 local self = Tools.Collision;
7707
7708 -- Stop the update loop
7709 if self.Updater then
7710 self.Updater();
7711 self.Updater = nil;
7712 end;
7713
7714 -- Hide the GUI
7715 self:hideGUI();
7716
7717 -- Clear out any temporary connections
7718 for connection_index, Connection in pairs( self.Connections ) do
7719 Connection:disconnect();
7720 self.Connections[connection_index] = nil;
7721 end;
7722
7723 -- Restore the original color of the selection boxes
7724 SelectionBoxColor = self.State.PreviousSelectionBoxColor;
7725 updateSelectionBoxColor();
7726
7727end;
7728
7729Tools.Collision.Loaded = true;
7730end))
7731LocalScript9.Disabled = true
7732LocalScript10.Name = "BTNewPartTool"
7733LocalScript10.Parent = LocalScript1
7734table.insert(cors,sandbox(LocalScript10,function()
7735-- Load the main tool's core environment when it's ready
7736repeat wait() until (
7737 _G.BTCoreEnv and
7738 _G.BTCoreEnv[script.Parent.Parent] and
7739 _G.BTCoreEnv[script.Parent.Parent].CoreReady
7740);
7741setfenv( 1, _G.BTCoreEnv[script.Parent.Parent] );
7742
7743------------------------------------------
7744-- New part tool
7745------------------------------------------
7746
7747-- Create the tool
7748Tools.NewPart = {};
7749
7750-- Define the tool's color
7751Tools.NewPart.Color = BrickColor.new( "Really black" );
7752
7753-- Keep a container for temporary connections
7754Tools.NewPart.Connections = {};
7755
7756-- Keep a container for state data
7757Tools.NewPart.State = {
7758 ["Part"] = nil;
7759};
7760
7761-- Maintain a container for options
7762Tools.NewPart.Options = {
7763 ["type"] = "normal"
7764};
7765
7766-- Keep a container for platform event connections
7767Tools.NewPart.Listeners = {};
7768
7769-- Start adding functionality to the tool
7770Tools.NewPart.Listeners.Equipped = function ()
7771
7772 local self = Tools.NewPart;
7773
7774 -- Change the color of selection boxes temporarily
7775 self.State.PreviousSelectionBoxColor = SelectionBoxColor;
7776 SelectionBoxColor = self.Color;
7777 updateSelectionBoxColor();
7778
7779 -- Reveal the GUI
7780 self:showGUI();
7781
7782 -- Restore the type option
7783 self:changeType( self.Options.type );
7784
7785end;
7786
7787Tools.NewPart.Listeners.Unequipped = function ()
7788
7789 local self = Tools.NewPart;
7790
7791 -- Hide the GUI
7792 self:hideGUI();
7793
7794 -- Disconnect temporary connections
7795 for connection_index, Connection in pairs( self.Connections ) do
7796 Connection:disconnect();
7797 self.Connections[connection_index] = nil;
7798 end;
7799
7800 -- Restore the original color of selection boxes
7801 SelectionBoxColor = self.State.PreviousSelectionBoxColor;
7802 updateSelectionBoxColor();
7803
7804end;
7805
7806Tools.NewPart.Listeners.Button1Down = function ()
7807
7808 local self = Tools.NewPart;
7809
7810 local NewPart;
7811
7812 -- Create the new part of type `self.Options.type`
7813 if self.Options.type == "normal" then
7814 NewPart = Instance.new( "Part", Services.Workspace );
7815 NewPart.FormFactor = "Custom";
7816 NewPart.Size = Vector3.new( 4, 1, 2 );
7817 elseif self.Options.type == "truss" then
7818 NewPart = Instance.new( "TrussPart", Services.Workspace );
7819 elseif self.Options.type == "wedge" then
7820 NewPart = Instance.new( "WedgePart", Services.Workspace );
7821 elseif self.Options.type == "corner" then
7822 NewPart = Instance.new( "CornerWedgePart", Services.Workspace );
7823 elseif self.Options.type == "cylinder" then
7824 NewPart = Instance.new( "Part", Services.Workspace );
7825 NewPart.Shape = "Cylinder";
7826 elseif self.Options.type == "ball" then
7827 NewPart = Instance.new( "Part", Services.Workspace );
7828 NewPart.Shape = "Ball";
7829 elseif self.Options.type == "seat" then
7830 NewPart = Instance.new( "Seat", Services.Workspace );
7831 elseif self.Options.type == "vehicle seat" then
7832 NewPart = Instance.new( "VehicleSeat", Services.Workspace );
7833 elseif self.Options.type == "spawn" then
7834 NewPart = Instance.new( "SpawnLocation", Services.Workspace );
7835 end;
7836 NewPart.Anchored = true;
7837
7838 -- Select the new part
7839 Selection:clear();
7840 Selection:add( NewPart );
7841
7842 local HistoryRecord = {
7843 target = NewPart;
7844 apply = function ( self )
7845 Selection:clear();
7846 if self.target then
7847 self.target.Parent = Services.Workspace;
7848 Selection:add( self.target );
7849 end;
7850 end;
7851 unapply = function ( self )
7852 if self.target then
7853 self.target.Parent = nil;
7854 end;
7855 end;
7856 };
7857 History:add( HistoryRecord );
7858
7859 -- Switch to the move tool and simulate clicking so
7860 -- that the user could easily position their new part
7861 equipTool( Tools.Move );
7862 Tools.Move.ManualTarget = NewPart;
7863 NewPart.CFrame = CFrame.new( Mouse.Hit.p );
7864 Tools.Move.Listeners.Button1Down();
7865 Tools.Move.Listeners.Move();
7866
7867end;
7868
7869Tools.NewPart.changeType = function ( self, new_type )
7870 self.Options.type = new_type;
7871 self.TypeDropdown:selectOption( new_type:upper() );
7872 if self.TypeDropdown.open then
7873 self.TypeDropdown:toggle();
7874 end;
7875end;
7876
7877Tools.NewPart.showGUI = function ( self )
7878
7879 -- Initialize the GUI if it's not ready yet
7880 if not self.GUI then
7881
7882 local Container = Tool.Interfaces:WaitForChild( "BTNewPartToolGUI" ):Clone();
7883 Container.Parent = UI;
7884
7885 local TypeDropdown = createDropdown();
7886 self.TypeDropdown = TypeDropdown;
7887 TypeDropdown.Frame.Parent = Container.TypeOption;
7888 TypeDropdown.Frame.Position = UDim2.new( 0, 70, 0, 0 );
7889 TypeDropdown.Frame.Size = UDim2.new( 0, 140, 0, 25 );
7890
7891 TypeDropdown:addOption( "NORMAL" ).MouseButton1Up:connect( function ()
7892 self:changeType( "normal" );
7893 end );
7894 TypeDropdown:addOption( "TRUSS" ).MouseButton1Up:connect( function ()
7895 self:changeType( "truss" );
7896 end );
7897 TypeDropdown:addOption( "WEDGE" ).MouseButton1Up:connect( function ()
7898 self:changeType( "wedge" );
7899 end );
7900 TypeDropdown:addOption( "CORNER" ).MouseButton1Up:connect( function ()
7901 self:changeType( "corner" );
7902 end );
7903 TypeDropdown:addOption( "CYLINDER" ).MouseButton1Up:connect( function ()
7904 self:changeType( "cylinder" );
7905 end );
7906 TypeDropdown:addOption( "BALL" ).MouseButton1Up:connect( function ()
7907 self:changeType( "ball" );
7908 end );
7909 TypeDropdown:addOption( "SEAT" ).MouseButton1Up:connect( function ()
7910 self:changeType( "seat" );
7911 end );
7912 TypeDropdown:addOption( "VEHICLE SEAT" ).MouseButton1Up:connect( function ()
7913 self:changeType( "vehicle seat" );
7914 end );
7915 TypeDropdown:addOption( "SPAWN" ).MouseButton1Up:connect( function ()
7916 self:changeType( "spawn" );
7917 end );
7918
7919 self.GUI = Container;
7920 end;
7921
7922 -- Reveal the GUI
7923 self.GUI.Visible = true;
7924
7925end;
7926
7927Tools.NewPart.hideGUI = function ( self )
7928
7929 -- Hide the GUI if it exists already
7930 if self.GUI then
7931 self.GUI.Visible = false;
7932 end;
7933
7934end;
7935
7936Tools.NewPart.Loaded = true;
7937end))
7938LocalScript10.Disabled = true
7939LocalScript11.Name = "BTMeshTool"
7940LocalScript11.Parent = LocalScript1
7941table.insert(cors,sandbox(LocalScript11,function()
7942-- Load the main tool's core environment when it's ready
7943repeat wait() until (
7944 _G.BTCoreEnv and
7945 _G.BTCoreEnv[script.Parent.Parent] and
7946 _G.BTCoreEnv[script.Parent.Parent].CoreReady
7947);
7948setfenv( 1, _G.BTCoreEnv[script.Parent.Parent] );
7949
7950------------------------------------------
7951-- Mesh tool
7952------------------------------------------
7953
7954-- Create the tool
7955Tools.Mesh = {};
7956
7957-- Define the tool's color
7958Tools.Mesh.Color = BrickColor.new( "Bright violet" );
7959
7960-- Keep a container for state data
7961Tools.Mesh.State = {};
7962
7963-- Keep a container for temporary connections
7964Tools.Mesh.Connections = {};
7965
7966-- Keep a container for platform event connections
7967Tools.Mesh.Listeners = {};
7968
7969-- Start adding functionality to the tool
7970Tools.Mesh.Listeners.Equipped = function ()
7971
7972 local self = Tools.Mesh;
7973
7974 -- Change the color of selection boxes temporarily
7975 self.State.PreviousSelectionBoxColor = SelectionBoxColor;
7976 SelectionBoxColor = self.Color;
7977 updateSelectionBoxColor();
7978
7979 -- Reveal the GUI
7980 self:showGUI();
7981
7982 -- Update the GUI regularly
7983 coroutine.wrap( function ()
7984 updater_on = true;
7985
7986 -- Provide a function to stop the loop
7987 self.stopGUIUpdater = function ( self )
7988 updater_on = false;
7989 end;
7990
7991 while wait( 0.1 ) and updater_on do
7992
7993 -- Make sure the tool's equipped
7994 if CurrentTool == self then
7995
7996 -- Update the GUI if it's visible
7997 if self.GUI and self.GUI.Visible then
7998 self:updateGUI();
7999 end;
8000
8001 end;
8002
8003 end;
8004
8005 end )();
8006
8007end;
8008
8009Tools.Mesh.Listeners.Unequipped = function ()
8010
8011 local self = Tools.Mesh;
8012
8013 -- Stop the GUI updater
8014 self:stopGUIUpdater();
8015
8016 -- Hide the GUI
8017 self:hideGUI();
8018
8019 -- Disconnect temporary connections
8020 for connection_index, Connection in pairs( self.Connections ) do
8021 Connection:disconnect();
8022 self.Connections[connection_index] = nil;
8023 end;
8024
8025 -- Restore the original color of selection boxes
8026 SelectionBoxColor = self.State.PreviousSelectionBoxColor;
8027 updateSelectionBoxColor();
8028
8029end;
8030
8031Tools.Mesh.TypeDropdownLabels = {
8032 [Enum.MeshType.Brick] = "BLOCK";
8033 [Enum.MeshType.Cylinder] = "CYLINDER";
8034 [Enum.MeshType.FileMesh] = "FILE";
8035 [Enum.MeshType.Head] = "HEAD";
8036 [Enum.MeshType.Sphere] = "SPHERE";
8037 [Enum.MeshType.Torso] = "TRAPEZOID";
8038 [Enum.MeshType.Wedge] = "WEDGE";
8039};
8040
8041Tools.Mesh.changeType = function ( self, new_type )
8042
8043 -- Apply type `new_type` to all the meshes in items from the selection
8044 local meshes = {};
8045 for _, Item in pairs( Selection.Items ) do
8046 local Mesh = _getChildOfClass( Item, "SpecialMesh" );
8047 if Mesh then
8048 table.insert( meshes, Mesh );
8049 end;
8050 end;
8051
8052 self:startHistoryRecord( meshes );
8053 for _, Mesh in pairs( meshes ) do
8054 Mesh.MeshType = new_type;
8055 end;
8056 self:finishHistoryRecord();
8057
8058 if self.TypeDropdown.open then
8059 self.TypeDropdown:toggle();
8060 end;
8061
8062 self:finishHistoryRecord();
8063
8064end;
8065
8066Tools.Mesh.updateGUI = function ( self )
8067
8068 -- Make sure the GUI exists
8069 if not self.GUI then
8070 return;
8071 end;
8072
8073 local GUI = self.GUI;
8074
8075 if #Selection.Items > 0 then
8076
8077 local meshes = {};
8078 for _, Item in pairs( Selection.Items ) do
8079 local Mesh = _getChildOfClass( Item, "SpecialMesh" );
8080 if Mesh then
8081 table.insert( meshes, Mesh );
8082 end;
8083 end;
8084
8085 local show_add, show_remove, show_mesh_id;
8086 local mesh_type, mesh_scale_x, mesh_scale_y, mesh_scale_z, mesh_id, mesh_texture, mesh_tint_r, mesh_tint_g, mesh_tint_b;
8087
8088 -- If every item has a mesh
8089 if #meshes == #Selection.Items then
8090 show_add = false;
8091 show_remove = true;
8092
8093 -- If no item has a mesh
8094 elseif #meshes == 0 then
8095 show_add = true;
8096 show_remove = false;
8097
8098 -- If some items have a mesh
8099 else
8100 show_add = true;
8101 show_remove = true;
8102 end;
8103
8104 -- If there are meshes
8105 if #meshes > 0 then
8106 show_type = true;
8107 for mesh_index, Mesh in pairs( meshes ) do
8108
8109 -- Set the start values for later comparison
8110 if mesh_index == 1 then
8111 mesh_type = Mesh.MeshType;
8112 mesh_scale_x, mesh_scale_y, mesh_scale_z = Mesh.Scale.x, Mesh.Scale.y, Mesh.Scale.z;
8113 mesh_id = Mesh.MeshId:lower();
8114 mesh_texture = Mesh.TextureId:lower();
8115 mesh_tint_r, mesh_tint_g, mesh_tint_b = Mesh.VertexColor.x, Mesh.VertexColor.y, Mesh.VertexColor.z;
8116
8117 -- Set the values to `nil` if they vary across the selection
8118 else
8119 if mesh_type ~= Mesh.MeshType then
8120 mesh_type = nil;
8121 end;
8122 if mesh_scale_x ~= Mesh.Scale.x then
8123 mesh_scale_x = nil;
8124 end;
8125 if mesh_scale_y ~= Mesh.Scale.y then
8126 mesh_scale_y = nil;
8127 end;
8128 if mesh_scale_z ~= Mesh.Scale.z then
8129 mesh_scale_z = nil;
8130 end;
8131 if mesh_id ~= Mesh.MeshId:lower() then
8132 mesh_id = nil;
8133 end;
8134 if mesh_texture ~= Mesh.TextureId:lower() then
8135 mesh_texture = nil;
8136 end;
8137 if mesh_tint_r ~= Mesh.VertexColor.x then
8138 mesh_tint_r = nil;
8139 end;
8140 if mesh_tint_g ~= Mesh.VertexColor.y then
8141 mesh_tint_g = nil;
8142 end;
8143 if mesh_tint_b ~= Mesh.VertexColor.z then
8144 mesh_tint_b = nil;
8145 end;
8146 end;
8147
8148 -- If there's a FileMesh around here, note that
8149 if Mesh.MeshType == Enum.MeshType.FileMesh then
8150 show_mesh_id = true;
8151 end;
8152
8153 end;
8154
8155 self.State.mesh_tint = ( mesh_tint_r and mesh_tint_g and mesh_tint_b ) and Color3.new( mesh_tint_r, mesh_tint_g, mesh_tint_b ) or nil;
8156
8157 if show_mesh_id and show_add and show_remove then
8158 self.GUI.AddButton.Visible = true;
8159 self.GUI.RemoveButton.Visible = true;
8160 self.GUI.MeshIDOption.Visible = true;
8161 self.GUI.TextureIDOption.Visible = true;
8162 self.GUI.ScaleOption.Visible = true;
8163 self.GUI.TintOption.Visible = true;
8164 self.GUI.TypeOption.Visible = true;
8165 self.GUI.TypeOption.Position = UDim2.new( 0, 14, 0, 65 );
8166 self.GUI.ScaleOption.Position = UDim2.new( 0, 0, 0, 100 );
8167 self.GUI.MeshIDOption.Position = UDim2.new( 0, 14, 0, 135 );
8168 self.GUI.TextureIDOption.Position = UDim2.new( 0, 14, 0, 165 );
8169 self.GUI.TintOption.Position = UDim2.new( 0, 0, 0, 200 );
8170 self.GUI.Size = UDim2.new( 0, 200, 0, 265 );
8171 elseif show_mesh_id and not show_add and show_remove then
8172 self.GUI.AddButton.Visible = false;
8173 self.GUI.RemoveButton.Visible = true;
8174 self.GUI.MeshIDOption.Visible = true;
8175 self.GUI.TextureIDOption.Visible = true;
8176 self.GUI.ScaleOption.Visible = true;
8177 self.GUI.TintOption.Visible = true;
8178 self.GUI.TypeOption.Visible = true;
8179 self.GUI.TypeOption.Position = UDim2.new( 0, 14, 0, 30 );
8180 self.GUI.ScaleOption.Position = UDim2.new( 0, 0, 0, 65 );
8181 self.GUI.MeshIDOption.Position = UDim2.new( 0, 14, 0, 100 );
8182 self.GUI.TextureIDOption.Position = UDim2.new( 0, 14, 0, 130 );
8183 self.GUI.TintOption.Position = UDim2.new( 0, 0, 0, 165 );
8184 self.GUI.Size = UDim2.new( 0, 200, 0, 230 );
8185
8186 elseif not show_mesh_id and show_add and show_remove then
8187 self.GUI.AddButton.Visible = true;
8188 self.GUI.RemoveButton.Visible = true;
8189 self.GUI.MeshIDOption.Visible = false;
8190 self.GUI.TextureIDOption.Visible = false;
8191 self.GUI.ScaleOption.Visible = true;
8192 self.GUI.TintOption.Visible = false;
8193 self.GUI.TypeOption.Visible = true;
8194 self.GUI.TypeOption.Position = UDim2.new( 0, 14, 0, 65 );
8195 self.GUI.ScaleOption.Position = UDim2.new( 0, 0, 0, 100 );
8196 self.GUI.Size = UDim2.new( 0, 200, 0, 165 );
8197 elseif not show_mesh_id and not show_add and show_remove then
8198 self.GUI.AddButton.Visible = false;
8199 self.GUI.RemoveButton.Visible = true;
8200 self.GUI.MeshIDOption.Visible = false;
8201 self.GUI.TextureIDOption.Visible = false;
8202 self.GUI.ScaleOption.Visible = true;
8203 self.GUI.TintOption.Visible = false;
8204 self.GUI.TypeOption.Visible = true;
8205 self.GUI.TypeOption.Position = UDim2.new( 0, 14, 0, 30 );
8206 self.GUI.ScaleOption.Position = UDim2.new( 0, 0, 0, 65 );
8207 self.GUI.Size = UDim2.new( 0, 200, 0, 130 );
8208 end;
8209
8210 -- Update the values shown on the GUI
8211 if not self.State.mesh_id_focused then
8212 self.GUI.MeshIDOption.TextBox.Text = mesh_id and ( mesh_id:match( "%?id=([0-9]+)" ) or "" ) or "*";
8213 end;
8214 if not self.State.texture_id_focused then
8215 self.GUI.TextureIDOption.TextBox.Text = mesh_texture and ( mesh_texture:match( "%?id=([0-9]+)" ) or "" ) or "*";
8216 end;
8217 self.TypeDropdown:selectOption( mesh_type and self.TypeDropdownLabels[mesh_type] or "*" );
8218 if not self.State.scale_x_focused then
8219 self.GUI.ScaleOption.XInput.TextBox.Text = mesh_scale_x and _round( mesh_scale_x, 2 ) or "*";
8220 end;
8221 if not self.State.scale_y_focused then
8222 self.GUI.ScaleOption.YInput.TextBox.Text = mesh_scale_y and _round( mesh_scale_y, 2 ) or "*";
8223 end;
8224 if not self.State.scale_z_focused then
8225 self.GUI.ScaleOption.ZInput.TextBox.Text = mesh_scale_z and _round( mesh_scale_z, 2 ) or "*";
8226 end;
8227 if not self.State.tint_r_focused then
8228 self.GUI.TintOption.RInput.TextBox.Text = mesh_tint_r and _round( mesh_tint_r * 255, 0 ) or "*";
8229 end;
8230 if not self.State.tint_g_focused then
8231 self.GUI.TintOption.GInput.TextBox.Text = mesh_tint_g and _round( mesh_tint_g * 255, 0 ) or "*";
8232 end;
8233 if not self.State.tint_b_focused then
8234 self.GUI.TintOption.BInput.TextBox.Text = mesh_tint_b and _round( mesh_tint_b * 255, 0 ) or "*";
8235 end;
8236
8237 -- If there are no meshes
8238 else
8239 self.GUI.AddButton.Visible = true;
8240 self.GUI.RemoveButton.Visible = false;
8241 self.GUI.MeshIDOption.Visible = false;
8242 self.GUI.TextureIDOption.Visible = false;
8243 self.GUI.ScaleOption.Visible = false;
8244 self.GUI.TintOption.Visible = false;
8245 self.GUI.TypeOption.Visible = false;
8246 self.GUI.Size = UDim2.new( 0, 200, 0, 62 );
8247 end;
8248 self.GUI.SelectNote.Visible = false;
8249
8250 -- Show a note that says to select something
8251 else
8252 self.GUI.AddButton.Visible = false;
8253 self.GUI.RemoveButton.Visible = false;
8254 self.GUI.MeshIDOption.Visible = false;
8255 self.GUI.TextureIDOption.Visible = false;
8256 self.GUI.ScaleOption.Visible = false;
8257 self.GUI.TintOption.Visible = false;
8258 self.GUI.TypeOption.Visible = false;
8259 self.GUI.SelectNote.Visible = true;
8260 self.GUI.Size = UDim2.new( 0, 200, 0, 55 );
8261 end;
8262
8263end;
8264
8265Tools.Mesh.showGUI = function ( self )
8266
8267 -- Initialize the GUI if it's not ready yet
8268 if not self.GUI then
8269 local Container = Tool.Interfaces:WaitForChild( "BTMeshToolGUI" ):Clone();
8270 Container.Parent = UI;
8271
8272 -- Add functionality to the add/remove buttons
8273 Container.AddButton.Button.MouseButton1Up:connect( function ()
8274 self:addMesh();
8275 end );
8276 Container.RemoveButton.Button.MouseButton1Up:connect( function ()
8277 self:removeMesh();
8278 end );
8279
8280 -- Add the type dropdown
8281 local TypeDropdown = createDropdown();
8282 self.TypeDropdown = TypeDropdown;
8283 TypeDropdown.Frame.Parent = Container.TypeOption;
8284 TypeDropdown.Frame.Position = UDim2.new( 0, 40, 0, 0 );
8285 TypeDropdown.Frame.Size = UDim2.new( 1, -40, 0, 25 );
8286 TypeDropdown:addOption( "BLOCK" ).MouseButton1Up:connect( function ()
8287 self:changeType( Enum.MeshType.Brick );
8288 end );
8289 TypeDropdown:addOption( "CYLINDER" ).MouseButton1Up:connect( function ()
8290 self:changeType( Enum.MeshType.Cylinder );
8291 end );
8292 TypeDropdown:addOption( "FILE" ).MouseButton1Up:connect( function ()
8293 self:changeType( Enum.MeshType.FileMesh );
8294 end );
8295 TypeDropdown:addOption( "HEAD" ).MouseButton1Up:connect( function ()
8296 self:changeType( Enum.MeshType.Head );
8297 end );
8298 TypeDropdown:addOption( "SPHERE" ).MouseButton1Up:connect( function ()
8299 self:changeType( Enum.MeshType.Sphere );
8300 end );
8301 TypeDropdown:addOption( "TRAPEZOID" ).MouseButton1Up:connect( function ()
8302 self:changeType( Enum.MeshType.Torso );
8303 end );
8304 TypeDropdown:addOption( "WEDGE" ).MouseButton1Up:connect( function ()
8305 self:changeType( Enum.MeshType.Wedge );
8306 end );
8307
8308 -- Add functionality to the scale inputs
8309 Container.ScaleOption.XInput.TextButton.MouseButton1Down:connect( function ()
8310 self.State.scale_x_focused = true;
8311 Container.ScaleOption.XInput.TextBox:CaptureFocus();
8312 end );
8313 Container.ScaleOption.XInput.TextBox.FocusLost:connect( function ( enter_pressed )
8314 local potential_new = tonumber( Container.ScaleOption.XInput.TextBox.Text );
8315 if potential_new then
8316 self:changeScale( 'x', potential_new );
8317 end;
8318 self.State.scale_x_focused = false;
8319 end );
8320
8321 Container.ScaleOption.YInput.TextButton.MouseButton1Down:connect( function ()
8322 self.State.scale_y_focused = true;
8323 Container.ScaleOption.YInput.TextBox:CaptureFocus();
8324 end );
8325 Container.ScaleOption.YInput.TextBox.FocusLost:connect( function ( enter_pressed )
8326 local potential_new = tonumber( Container.ScaleOption.YInput.TextBox.Text );
8327 if potential_new then
8328 self:changeScale( 'y', potential_new );
8329 end;
8330 self.State.scale_y_focused = false;
8331 end );
8332
8333 Container.ScaleOption.ZInput.TextButton.MouseButton1Down:connect( function ()
8334 self.State.scale_z_focused = true;
8335 Container.ScaleOption.ZInput.TextBox:CaptureFocus();
8336 end );
8337 Container.ScaleOption.ZInput.TextBox.FocusLost:connect( function ( enter_pressed )
8338 local potential_new = tonumber( Container.ScaleOption.ZInput.TextBox.Text );
8339 if potential_new then
8340 self:changeScale( 'z', potential_new );
8341 end;
8342 self.State.scale_z_focused = false;
8343 end );
8344
8345 -- Add functionality to the mesh/texture ID inputs
8346 Container.MeshIDOption.TextButton.MouseButton1Down:connect( function ()
8347 self.State.mesh_id_focused = true;
8348 Container.MeshIDOption.TextBox:CaptureFocus();
8349 end );
8350 Container.MeshIDOption.TextBox.FocusLost:connect( function ( enter_pressed )
8351 local input = Container.MeshIDOption.TextBox.Text;
8352 local potential_new = tonumber( input ) or input:lower():match( "%?id=([0-9]+)" );
8353 if potential_new then
8354 self:changeMesh( potential_new );
8355 end;
8356 self.State.mesh_id_focused = false;
8357 end );
8358
8359 Container.TextureIDOption.TextButton.MouseButton1Down:connect( function ()
8360 self.State.texture_id_focused = true;
8361 Container.TextureIDOption.TextBox:CaptureFocus();
8362 end );
8363 Container.TextureIDOption.TextBox.FocusLost:connect( function ( enter_pressed )
8364 local input = Container.TextureIDOption.TextBox.Text;
8365 local potential_new = tonumber( input ) or input:lower():match( "%?id=([0-9]+)" );
8366 if potential_new then
8367 self:changeTexture( potential_new );
8368 end;
8369 self.State.texture_id_focused = false;
8370 end );
8371
8372 -- Add functionality to the tint inputs
8373 Container.TintOption.RInput.TextButton.MouseButton1Down:connect( function ()
8374 self.State.tint_r_focused = true;
8375 Container.TintOption.RInput.TextBox:CaptureFocus();
8376 end );
8377 Container.TintOption.RInput.TextBox.FocusLost:connect( function ( enter_pressed )
8378 local potential_new = tonumber( Container.TintOption.RInput.TextBox.Text );
8379 if potential_new then
8380 if potential_new > 255 then
8381 potential_new = 255;
8382 elseif potential_new < 0 then
8383 potential_new = 0;
8384 end;
8385 self:changeTint( 'r', potential_new / 255 );
8386 end;
8387 self.State.tint_r_focused = false;
8388 end );
8389
8390 Container.TintOption.GInput.TextButton.MouseButton1Down:connect( function ()
8391 self.State.tint_g_focused = true;
8392 Container.TintOption.GInput.TextBox:CaptureFocus();
8393 end );
8394 Container.TintOption.GInput.TextBox.FocusLost:connect( function ( enter_pressed )
8395 local potential_new = tonumber( Container.TintOption.GInput.TextBox.Text );
8396 if potential_new then
8397 if potential_new > 255 then
8398 potential_new = 255;
8399 elseif potential_new < 0 then
8400 potential_new = 0;
8401 end;
8402 self:changeTint( 'g', potential_new / 255 );
8403 end;
8404 self.State.tint_g_focused = false;
8405 end );
8406
8407 Container.TintOption.BInput.TextButton.MouseButton1Down:connect( function ()
8408 self.State.tint_b_focused = true;
8409 Container.TintOption.BInput.TextBox:CaptureFocus();
8410 end );
8411 Container.TintOption.BInput.TextBox.FocusLost:connect( function ( enter_pressed )
8412 local potential_new = tonumber( Container.TintOption.BInput.TextBox.Text );
8413 if potential_new then
8414 if potential_new > 255 then
8415 potential_new = 255;
8416 elseif potential_new < 0 then
8417 potential_new = 0;
8418 end;
8419 self:changeTint( 'b', potential_new / 255 );
8420 end;
8421 self.State.tint_b_focused = false;
8422 end );
8423
8424 Container.TintOption.HSVPicker.MouseButton1Up:connect( function ()
8425 ColorPicker:start( function ( ... )
8426 local args = { ... };
8427 -- If a color was picked, change the spotlights' color
8428 -- to the selected color
8429 if #args == 3 then
8430 local meshes = {};
8431 for _, Item in pairs( Selection.Items ) do
8432 local Mesh = _getChildOfClass( Item, "SpecialMesh" );
8433 if Mesh then
8434 table.insert( meshes, Mesh );
8435 end;
8436 end;
8437 self:startHistoryRecord( meshes );
8438 for _, Mesh in pairs( meshes ) do
8439 Mesh.VertexColor = Vector3.new( _HSVToRGB( ... ) );
8440 end;
8441 self:finishHistoryRecord();
8442 end;
8443 end, self.State.mesh_tint );
8444 end );
8445
8446 self.GUI = Container;
8447 end;
8448
8449 -- Reveal the GUI
8450 self.GUI.Visible = true;
8451
8452end;
8453
8454Tools.Mesh.addMesh = function ( self )
8455
8456 local HistoryRecord = {
8457 apply = function ( self )
8458 Selection:clear();
8459 for _, Mesh in pairs( self.meshes ) do
8460 Mesh.Parent = self.mesh_parents[Mesh];
8461 Selection:add( Mesh.Parent );
8462 end;
8463 end;
8464 unapply = function ( self )
8465 Selection:clear();
8466 for _, Mesh in pairs( self.meshes ) do
8467 Selection:add( Mesh.Parent );
8468 Mesh.Parent = nil;
8469 end;
8470 end;
8471 };
8472
8473 -- Add meshes to all the items from the selection that
8474 -- don't already have one
8475 local meshes = {};
8476 local mesh_parents = {};
8477 for _, Item in pairs( Selection.Items ) do
8478 local Mesh = _getChildOfClass( Item, "SpecialMesh" );
8479 if not Mesh then
8480 local Mesh = RbxUtility.Create "SpecialMesh" {
8481 Parent = Item;
8482 MeshType = Enum.MeshType.Brick;
8483 };
8484 table.insert( meshes, Mesh );
8485 mesh_parents[Mesh] = Item;
8486 end;
8487 end;
8488
8489 HistoryRecord.meshes = meshes;
8490 HistoryRecord.mesh_parents = mesh_parents;
8491 History:add( HistoryRecord );
8492
8493end;
8494
8495Tools.Mesh.removeMesh = function ( self )
8496
8497 local HistoryRecord = {
8498 apply = function ( self )
8499 Selection:clear();
8500 for _, Mesh in pairs( self.meshes ) do
8501 Selection:add( Mesh.Parent );
8502 Mesh.Parent = nil;
8503 end;
8504 end;
8505 unapply = function ( self )
8506 Selection:clear();
8507 for _, Mesh in pairs( self.meshes ) do
8508 Mesh.Parent = self.mesh_parents[Mesh];
8509 Selection:add( Mesh.Parent );
8510 end;
8511 end;
8512 };
8513
8514 local meshes = {};
8515 local mesh_parents = {};
8516 -- Remove meshes from all the selected items
8517 for _, Item in pairs( Selection.Items ) do
8518 local meshes_found = _getChildrenOfClass( Item, "SpecialMesh" );
8519 for _, Mesh in pairs( meshes_found ) do
8520 table.insert( meshes, Mesh );
8521 mesh_parents[Mesh] = Mesh.Parent;
8522 Mesh.Parent = nil;
8523 end;
8524 end;
8525
8526 HistoryRecord.meshes = meshes;
8527 HistoryRecord.mesh_parents = mesh_parents;
8528 History:add( HistoryRecord );
8529
8530end;
8531
8532Tools.Mesh.startHistoryRecord = function ( self, meshes )
8533
8534 if self.State.HistoryRecord then
8535 self.State.HistoryRecord = nil;
8536 end;
8537
8538 -- Create a history record
8539 self.State.HistoryRecord = {
8540 targets = _cloneTable( meshes );
8541 initial_type = {};
8542 terminal_type = {};
8543 initial_mesh = {};
8544 terminal_mesh = {};
8545 initial_texture = {};
8546 terminal_texture = {};
8547 initial_scale = {};
8548 terminal_scale = {};
8549 initial_tint = {};
8550 terminal_tint = {};
8551 unapply = function ( self )
8552 Selection:clear();
8553 for _, Target in pairs( self.targets ) do
8554 if Target then
8555 Selection:add( Target.Parent );
8556 Target.MeshType = self.initial_type[Target];
8557 Target.MeshId = self.initial_mesh[Target];
8558 Target.TextureId = self.initial_texture[Target];
8559 Target.Scale = self.initial_scale[Target];
8560 Target.VertexColor = self.initial_tint[Target];
8561 end;
8562 end;
8563 end;
8564 apply = function ( self )
8565 Selection:clear();
8566 for _, Target in pairs( self.targets ) do
8567 if Target then
8568 Selection:add( Target.Parent );
8569 Target.MeshType = self.terminal_type[Target];
8570 Target.MeshId = self.terminal_mesh[Target];
8571 Target.TextureId = self.terminal_texture[Target];
8572 Target.Scale = self.terminal_scale[Target];
8573 Target.VertexColor = self.terminal_tint[Target];
8574 end;
8575 end;
8576 end;
8577 };
8578 for _, Item in pairs( self.State.HistoryRecord.targets ) do
8579 if Item then
8580 self.State.HistoryRecord.initial_type[Item] = Item.MeshType;
8581 self.State.HistoryRecord.initial_mesh[Item] = Item.MeshId;
8582 self.State.HistoryRecord.initial_texture[Item] = Item.TextureId;
8583 self.State.HistoryRecord.initial_scale[Item] = Item.Scale;
8584 self.State.HistoryRecord.initial_tint[Item] = Item.VertexColor;
8585 end;
8586 end;
8587
8588end;
8589
8590Tools.Mesh.finishHistoryRecord = function ( self )
8591
8592 if not self.State.HistoryRecord then
8593 return;
8594 end;
8595
8596 for _, Item in pairs( self.State.HistoryRecord.targets ) do
8597 if Item then
8598 self.State.HistoryRecord.terminal_type[Item] = Item.MeshType;
8599 self.State.HistoryRecord.terminal_mesh[Item] = Item.MeshId;
8600 self.State.HistoryRecord.terminal_texture[Item] = Item.TextureId;
8601 self.State.HistoryRecord.terminal_scale[Item] = Item.Scale;
8602 self.State.HistoryRecord.terminal_tint[Item] = Item.VertexColor;
8603 end;
8604 end;
8605 History:add( self.State.HistoryRecord );
8606 self.State.HistoryRecord = nil;
8607
8608end;
8609
8610Tools.Mesh.changeMesh = function ( self, mesh_id )
8611
8612 local meshes = {};
8613
8614 for _, Item in pairs( Selection.Items ) do
8615 local Mesh = _getChildOfClass( Item, "SpecialMesh" );
8616 if Mesh then
8617 table.insert( meshes, Mesh );
8618 end;
8619 end;
8620 self:startHistoryRecord( meshes );
8621 for _, Mesh in pairs( meshes ) do
8622 Mesh.MeshId = "http://www.roblox.com/asset/?id=" .. mesh_id;
8623 end;
8624 self:finishHistoryRecord();
8625
8626end;
8627
8628Tools.Mesh.changeTexture = function ( self, texture_id )
8629
8630 local meshes = {};
8631
8632 for _, Item in pairs( Selection.Items ) do
8633 local Mesh = _getChildOfClass( Item, "SpecialMesh" );
8634 if Mesh then
8635 table.insert( meshes, Mesh );
8636 end;
8637 end;
8638 self:startHistoryRecord( meshes );
8639 for _, Mesh in pairs( meshes ) do
8640 Mesh.TextureId = "http://www.roblox.com/asset/?id=" .. texture_id;
8641 end;
8642 self:finishHistoryRecord();
8643
8644end;
8645
8646Tools.Mesh.changeScale = function ( self, component, new_value )
8647
8648 local meshes = {};
8649
8650 for _, Item in pairs( Selection.Items ) do
8651 local Mesh = _getChildOfClass( Item, "SpecialMesh" );
8652 if Mesh then
8653 table.insert( meshes, Mesh );
8654 end;
8655 end;
8656
8657 self:startHistoryRecord( meshes );
8658 for _, Mesh in pairs( meshes ) do
8659 Mesh.Scale = Vector3.new(
8660 component == 'x' and new_value or Mesh.Scale.x,
8661 component == 'y' and new_value or Mesh.Scale.y,
8662 component == 'z' and new_value or Mesh.Scale.z
8663 );
8664 end;
8665 self:finishHistoryRecord();
8666
8667end;
8668
8669Tools.Mesh.changeTint = function ( self, component, new_value )
8670
8671 local meshes = {};
8672
8673 for _, Item in pairs( Selection.Items ) do
8674 local Mesh = _getChildOfClass( Item, "SpecialMesh" );
8675 if Mesh then
8676 table.insert( meshes, Mesh );
8677 end;
8678 end;
8679
8680 self:startHistoryRecord( meshes );
8681 for _, Mesh in pairs( meshes ) do
8682 Mesh.VertexColor = Vector3.new(
8683 component == 'r' and new_value or Mesh.VertexColor.x,
8684 component == 'g' and new_value or Mesh.VertexColor.y,
8685 component == 'b' and new_value or Mesh.VertexColor.z
8686 );
8687 end;
8688 self:finishHistoryRecord();
8689
8690end;
8691
8692Tools.Mesh.hideGUI = function ( self )
8693
8694 -- Hide the GUI if it exists already
8695 if self.GUI then
8696 self.GUI.Visible = false;
8697 end;
8698
8699end;
8700
8701Tools.Mesh.Loaded = true;
8702end))
8703LocalScript11.Disabled = true