· 6 years ago · Feb 27, 2020, 08:42 AM
1--Source code extracted from https://www.roblox.com/library/741785194/VisualPlugin-useful-building-plugin
2
3--'Proudly made in the UK' ... by an Anglophile living in the US.
4--Look around for NOTE comments all over the source code; thay contain helpful tips on using VisualPlugin.
5local version={shape='rbxasset://textures/ui/Lobby/Buttons/scroll_up.png',sub='C.U.L8r'}
6
7local defaults={
8 planeV={1,0,1},
9 planeS={.7,0,1},
10 planeTop={false},
11 planeTrans={.4,0,1},
12 selBoxSize={.05,0,1},
13 --1 for box, 2 for plane.
14 selMode={2,1,nil,1},
15 maxParts={187,0},
16
17 decRound={3},
18 debugP={false},
19 saveIncrs={true},
20 newPartDist={7,0},
21 guiScaling={1,.5},
22 handleIntensity={1},
23 selectsFirst={true},
24 selectLocked={false},
25 fromAllEdges={false},
26 screenTipDelay={.5,0},
27 maxUndo={-1,-1,nil,1},
28 resizeNegative={false},
29 blocking={false,'NO_TOUCH'},
30 --Maximum ancestry to search.
31 selectsSameCFrame={2,0,nil,1},
32 clearAllOnIncrementClear={false},
33
34 mods={{
35 --[[
36 game.workspace:
37 findFirstChild
38 'ModuleTest',
39 ]]
40 }},
41
42 --NOTE: Some settings are restricted from editing!
43 forceRestart={false,'NO_TOUCH'},
44 usedB4={true,'NO_TOUCH'},
45}
46
47local materialIds={
48 SmoothPlastic
49 =703387192,
50 CorrodedMetal
51 =843920084,
52 DiamondPlate
53 =844342054,
54 Cobblestone
55 =843920069,
56 WoodPlanks
57 =843920072,
58 Concrete
59 =843920069,
60 Granite
61 =843920074,
62 Plastic
63 =844342043,
64 Fabric
65 =843920050,
66 Marble
67 =843920026,
68 Pebble
69 =843920063,
70 Brick
71 =843920065,
72 Slate
73 =843920055,
74 Grass
75 =843920051,
76 Metal
77 =845896371,
78 Wood
79 =843920048,
80 Sand
81 =843920044,
82 Foil
83 =844342044,
84 Neon
85 =703387192,
86 Ice
87 =843920041,
88 Glass
89 =844342043,
90}
91
92local materialCount=0
93for i,g in pairs(materialIds) do
94 materialCount=materialCount+1
95end
96
97--------------------------------------------------------------------------------------------------------
98
99local plugin=getfenv().plugin
100print('The plugin object does'
101..(plugin and''or'n\'t')..' exist.')
102
103while not script.Parent:
104isA'Tool' and not plugin do
105 wait()
106end
107
108local player=game.
109Players.LocalPlayer
110local tool=not plugin
111and script.Parent or nil
112local mouse=(plugin and plugin or player):GetMouse()
113local core=plugin and game.CoreGui or player.PlayerGui
114local util=require(script
115:WaitForChild'Util')
116local active
117
118local permsF=require(script:
119WaitForChild'AdminPermissions')
120local partF=permsF.OnNewPartCreation
121local verify=permsF.VerifySelection
122
123local selFuncs
124local commands
125local canDoStuff
126
127local rs=game['Run Service']
128local gui=script.VisualPlugin:clone()
129local handlesF=script.Handles:clone()
130local mf=gui:WaitForChild'MainFrame'
131local cmds=mf:WaitForChild'Commands'
132local config=gui:WaitForChild'Config'
133
134local sm=mf:WaitForChild'Submodes'
135local smTemplate=sm['1']:clone()
136local incBox=sm.Increment.TextBox
137local clTemplate=script:
138WaitForChild'SpecialGui'.Colour
139clTemplate.Visible=false
140
141local id=plugin and plugin:
142GetStudioUserId()or player.userId
143local hue=1.07773e-8*id
144-3.1896e-17*id^2+.5
145--NOTE: There would be a VIP feature based on the client's user ID.
146local vip=id>0
147and id<1630229
148
149--NOTE: The hue of your plugin client varies by the client's user ID.
150function accColour()
151 return id<1 and Color3
152 .new(.7,.7,.7)or Color3
153 .fromHSV(hue%1,.75,.95)
154 --return BrickColor.new'Gold'.Color
155end
156
157--Indicies are numbers, values are part tables.
158--Index 0 of said table is optionally a group
159local selGroups={}
160--Indicies are numbers, values are selection tables.
161local selections={}
162--Indicies are numbers, values are parts.
163local selI
164
165--Stealing from DataBrain? Never mind?
166--[[
167local src=game.InsertService:LoadAsset(284294874):children()[1].Source:gsub('\n',' ')
168local matStr=select(3,src:find('local materials = ({.+}) local BasicMaterials'))
169local materials=loadstring('return '..matStr)()
170local basicMatStr=select(3,src:find('local BasicMaterials = ({.+}) local button'))
171local basicMaterials=loadstring('return '..basicMatStr)()
172]]
173
174if plugin then
175 defaults.maxUndo
176 [2]='NO_TOUCH'
177else
178 if not plugin then
179 defaults.selectLocked
180 [2]='NO_TOUCH'
181 end
182end
183
184local toolSettings={}
185local settings=setmetatable({},{
186 __index=function(t,i)
187 local v=plugin and
188 plugin:GetSetting(i)
189 or toolSettings[i]
190 if v=='true' then
191 return true
192 elseif v=='false' then
193 return false end
194 return v
195 end,
196 __newindex=function(t,i,v)
197 i=tostring(i)
198 if plugin then
199 if type(v)=='boolean' then
200 v=tostring(v)end
201 plugin:SetSetting(i,v)
202 else
203 toolSettings[i]=v
204 end
205 if reRender then
206 reRender()
207 end
208 end,
209})
210
211if defaults.forceRestart[1]
212or not settings.usedB4
213or not plugin then
214 print'Settings restored.'
215 for i,g in pairs(defaults) do
216 settings[i]=g[1]
217 end
218else
219 for i,g in pairs(defaults) do
220 if settings[i]==nil --THIS IS WHY THE BOOLEAN SETTINGS WERE SWITCHING TO TRUE;
221 --'not settings[i]' improperly returns true when the setting it set to false.
222 or g[2]=='NO_TOUCH' then
223 toolSettings[i]=g[1]
224 settings[i]=g[1]
225 end
226 end
227end
228
229local Print=print
230local doPrint=settings.debugP
231local print=function(...)
232 if doPrint then
233 return Print(unpack(0<#{...}
234 and{os.time(),'-',...}or{}))
235 end
236end
237
238local twIndex=0
239local toolWaypoints={}
240local origData,pData={},{}
241function createWaypoint(t)
242 if plugin then
243 game.ChangeHistoryService
244 :SetWaypoint'0x18E014'
245 else
246 if not t then
247 t={}
248 local proceed
249 for i2 in pairs(origData) do
250 if tonumber(i2) then
251 local n=pData[i2]
252 local o=origData[i2]
253 if not proceed and(o.cf~=n.CFrame
254 or o.size~=o.part.Size)then
255 proceed=true
256 end
257
258 t[i2]={undo=
259 {cf=o.cf,size=o
260 .size},redo={cf=
261 n.cf,size=n.size
262 },parts=o.parts}
263 end
264 end
265 if not proceed then
266 return
267 end
268 end
269 addToolWaypoint(t)
270 end
271end
272
273function addToolWaypoint(t)
274 print'Tool waypoint created.'
275 --Clears the stuff after the last state.
276 for i=1,#toolWaypoints do
277 toolWaypoints[i]
278 =toolWaypoints[i+twIndex]
279 end
280
281 --Adds a new position thingy.
282 twIndex=0
283 local max=settings.maxUndo
284 for i=max>-1 and max-1
285 or#toolWaypoints,0,-1 do
286 toolWaypoints[i+1]
287 =toolWaypoints[i]
288 or t
289 end
290end
291
292function undo()
293 if plugin and game.
294 ChangeHistoryService
295 :GetCanUndo() then
296 game.ChangeHistoryService:Undo()
297
298 elseif twIndex
299 <#toolWaypoints then
300 twIndex=twIndex+1
301 local w=toolWaypoints[twIndex]
302 print('The mode is '..(w.mode
303 or'null')..' ('..twIndex..')')
304 if w.mode=='del'then
305 for i,g in pairs(w[1]) do
306 g.part.Parent=g.par
307 end
308 elseif w.mode=='ins'then
309 for i,g in pairs(w[1]) do
310 removeSelection(g.part)
311 g.part.Parent=nil
312 end
313 elseif w.mode=='colour'then
314 local parents={}
315 for i,g in pairs(w[2]) do
316 removeSelection(g[1])
317 parents[i]=g[1].Parent
318 for i2,g2 in pairs(g) do
319 g2.Parent=nil
320 end
321 end
322
323 for i,g in pairs(w[1]) do
324 for i2,g2 in pairs(g) do
325 g2.Parent=parents[i]
326 end
327 addSelection(g[1])
328 end
329 else
330 for i,g in pairs(w) do
331 for i2,g2 in pairs(g.parts) do
332 setCFrame(g2,g.undo.cf)
333 setSize(g2,g.undo.size)
334 end
335 end
336 end
337 end
338 setSubmode()
339end
340
341function redo()
342 if plugin and game.
343 ChangeHistoryService
344 :GetCanRedo() then
345 game.ChangeHistoryService:Redo()
346 elseif twIndex>0 then
347 twIndex=twIndex-1
348 local w=toolWaypoints[twIndex+1]
349 if w.mode=='del'then
350 for i,g in pairs(w[1]) do
351 removeSelection(g.part)
352 g.part.Parent=nil
353 end
354 elseif w.mode=='ins'then
355 for i,g in pairs(w[1]) do
356 g.part.Parent=g.par
357 end
358 elseif w.mode=='colour'then
359 local parents={}
360 for i,g in pairs(w[1]) do
361 removeSelection(g[1])
362 parents[i]=g[1].Parent
363 for i2,g2 in pairs(g) do
364 g2.Parent=nil
365 end
366 end
367
368 for i,g in pairs(w[2]) do
369 for i2,g2 in pairs(g) do
370 g2.Parent=parents[i]
371 end
372 addSelection(g[1])
373 end
374 else
375 for i,g in pairs(w) do
376 for i2,g2 in pairs(g.parts) do
377 setCFrame(g2,g.redo.cf)
378 setSize(g2,g.redo.size)
379 end
380 end
381 end
382 end
383 setSubmode()
384end
385
386function duplicate()
387 local t,c={}
388 for i,g in pairs(selGroups) do
389 for i2,g2 in pairs(g) do
390 c=g2:clone()
391 c.Parent=g2.Parent
392 selGroups[i][i2]=c
393 t[#t+1]={part=c
394 ,par=g2.Parent}
395 end
396 selections[i].destroy()
397 selections[i]=selFuncs
398 [settings.selMode][2](c)
399 end
400 setMode(nil,1)
401 createWaypoint
402 {mode='ins',t}
403 upd8GameSel()
404end
405
406function delete()
407 local t=getSelection()
408 for i,g in pairs(t) do
409 removeSelection(g)
410 t[i]={part=g,
411 par=g.Parent}
412 g.Parent=nil
413 end
414
415 createWaypoint
416 {mode='del',t}
417 t={}
418end
419
420--------------------------------------------------------------------------------------------------------
421
422function selContains(p)
423 for i,g in pairs(selGroups) do
424 for i2,g2 in pairs(g) do
425 if g2==p then
426 return{g,i,i2}
427 end
428 end
429 end
430end
431
432function clearSelection()
433 for i,g in pairs(selections) do
434 g.destroy()end
435 selections={}
436 selGroups={}
437 setMode()
438end
439
440function getSelection(groups)
441 local t={}
442 for i,g in pairs(selGroups) do
443 if groups and g[0] then
444 t[#t+1]=g[0]
445 else
446 for i=1,#g do
447 t[#t+1]=g[i]
448 end
449 end
450 end
451 return t
452end
453
454--[[
455function setSelection(t)
456 clearSelection()
457 if active then
458 for i,g in pairs(t) do
459 if type(g)~='table' then
460 g={g}end
461 selGroups[i]=g
462 selections[i]=selFuncs
463 [settings.selMode][2](g[1])
464 end
465 setMode(nil,settings.
466 selectsFirst and 1 or#t)
467 end
468 return t
469end
470
471function setSel(t)
472 local t=t or{}
473 for i=1,math.max(#selGroups,#t) do
474 selGroups[i]=t[i]end
475end
476]]
477
478local addWhenUpd8=true
479function upd8GameSel()
480 if plugin then
481 addWhenUpd8=false
482 game.Selection:Set(
483 getSelection(true))
484 addWhenUpd8=true
485 end
486end
487
488--The part does not have to be a BasePart.
489function addSelection(p,solo)
490 if active then
491 local solo=solo
492 and not p:isA'Model'
493 local t=solo and{{p
494 }}or getSelParts(p)
495 local c=#selGroups
496 if #t>0 then
497 if t[1][1]:
498 isA'BasePart'then
499 for i=1,#t do
500 local doIt=true
501 if not solo then
502 for i2=1,#t[i] do
503 if selContains(t
504 [i][i2]) then
505 doIt=false
506 end
507 end
508 end
509
510 if doIt then
511 c=c+1
512 t[i][0]=t[0]
513 selGroups[c]=t[i]
514 selections[c]=selFuncs
515 [settings.selMode]
516 [2](t[i][1])
517 end
518 end
519 print'Selecktetto.'
520 setMode(nil,settings.selectsFirst and 1 or c)
521 end
522 --Returns the part if success.
523 return p
524 end
525 end
526end
527
528function removeSelection(p)
529 local parts=selContains(p)
530 if not parts then
531 return end
532
533 local t=getSelParts(p)
534 for i in pairs(selGroups) do
535 local sel=selGroups[i]
536 while sel and((t[0]and selGroups
537 [i][0]==t[0])or sel==parts[1])do
538 selections[i].destroy()
539 sel=selGroups[i+1]
540 for i2=i+1,#selGroups+1 do
541 selections[i2-1]
542 =selections[i2]
543 selGroups[i2-1]
544 =selGroups[i2]
545 end
546 end
547 end
548
549 local pl=#selGroups~=1
550 print('There '..(pl and'are'or'is')
551 ..' currently '..#selGroups..' part'
552 ..(pl and's'or'')..' selected after removal.')
553 setMode(nil,selI
554 >#selections and
555 (settings.
556 selectsFirst and 1
557 or#selections)
558 or nil)
559 return p
560end
561
562function toggleSelection(p,...)
563 if not removeSelection(p,...) then
564 addSelection(p,...)
565 end
566end
567
568local stInfo,snapped={}
569local submode,increment
570local handles
571local adornee
572local mode=1
573
574local secK=not plugin
575and'Control'or'Shift'
576local secondOn
577local binders={}
578
579game:service'UserInputService'
580.InputBegan:connect(function(inp)
581 if inp.UserInputType
582 .Name=='Keyboard'
583 and inp.KeyCode
584 .Name:find(secK)then
585 print'Special key pressed.'
586 secondOn=true
587 end
588end)
589game:service'UserInputService'
590.InputEnded:connect(function(inp)
591 if inp.UserInputType
592 .Name=='Keyboard'
593 and inp.KeyCode
594 .Name:find(secK)then
595 print'Special key released.'
596 secondOn=false
597 end
598end)
599
600function bindH(t)
601 if active then
602 game.ContextActionService
603 :BindAction(t.name,
604 function(nme,state)
605 if state.Name
606 =='Begin'then
607 if not secondOn
608 ==not t.second then
609 t.func()
610 else
611 for i,g in pairs(binders) do
612 if g~=t
613 and g.bind
614 ==t.bind then
615 g.func()
616 end
617 end
618 end
619 end
620 end,false
621 ,t.bind)
622 end
623end
624
625function bind(
626n,f,k,second)
627 binders
628 [n]={
629 bind=k,
630 func=f,
631 name=n,
632 second=
633 second}
634
635 local override=true
636 --[[
637 for i,g in pairs(binders) do
638 if g.bind==k and g.
639 second~=second then
640 override=false
641 end
642 end
643 ]]
644 if override then
645 bindH(binders[n])
646 end
647end
648
649function unbind(n)
650 game.ContextActionService
651 :UnbindAction(n)
652 binders[n]=nil
653end
654
655function rsBind(n,f)
656 rs:BindToRenderStep(n,666,f)
657end
658
659function rsUnbind(n,f)
660 rs:UnbindFromRenderStep(n)
661end
662
663local tempParts={}
664--Used for creating a bounding box.
665function forObjCentre(handles,a)
666 local p=Instance.new'Part'
667 p.Size=util.bounding(a)[2]
668 p.Position=a.Position
669 --p.Transparency=1
670 p.Anchored=true
671
672 --p.Parent=game.workspace
673 handles.Adornee=p
674 tempParts[1]=p
675 return p
676end
677
678function forGroup(handles,a)
679 if not plugin then
680 return forObjCentre(handles,a)
681 end
682
683 local p=Instance.new'Part'
684 p.Position,p.Size=unpack(util
685 .bounding(getSelection()))
686 --p.Transparency=1
687 p.Anchored=true
688
689 --p.Parent=game.workspace
690 handles.Adornee=p
691 tempParts[1]=p
692 return p
693end
694
695local edgeSaved
696function forEdge(handles,a,changed)
697 local p=Instance.new'Part'
698 handles.Adornee=nil
699 p.Anchored=true
700 tempParts[1]=p
701
702 if changed or not edgeSaved then
703 local sel=Instance.new'SelectionBox'
704 sel.SurfaceColor3=accColour()
705 sel.SurfaceTransparency=0
706 sel.Parent=core
707 sel.Transparency=1
708 sel.Adornee=p
709
710 local cft={}
711 local edge,relV3,mag
712 for i,g in pairs(settings.fromAllEdges
713 and(function()
714 local t={}
715 scanParts(game.
716 workspace,t,
717 function(p)
718 return p:
719 isA'BasePart'
720 end)
721 return t
722 end)()or selGroups) do
723 cft[i]=util.surroundingCF(type(g
724 )=='table' and g[1] or g)
725 end
726
727 local dunn
728 spawn(function()
729 while not dunn and wait() do
730 if mouse.Hit then
731 mag=nil
732 for i,g in pairs(cft) do
733 for i2,g2 in pairs(g) do
734 local cMag=(mouse.
735 Hit.p-g2[2].p).magnitude
736 if not mag or cMag<mag then
737 edge=g[i2][2]
738 relV3=g2[1]
739 mag=cMag
740 end
741 end
742 end
743
744 local dist=math.min(1/10*(
745 game.workspace.CurrentCamera.
746 CFrame.p-edge.p).magnitude,1)
747 p.Size=dist*Vector3.new(1,1,1)
748 p.CFrame=edge
749 end
750 end
751 end)
752
753 mouse.Button1Up:wait()
754 p.Size=Vector3.new()
755 edgeSaved=edge
756 sel:destroy()
757 dunn=true
758 else
759 p.CFrame=edgeSaved
760 end
761
762 p.Size=Vector3.new()
763 handles.Adornee=p
764 return p
765end
766
767function partDataTable(p)
768 local bm=p[1]:FindFirstChildOfClass'BlockMesh'
769 return{cf=p[1].CFrame,size=p[1].Size*(
770 bm and bm.Scale or Vector3.new(1,1,1)),
771 anchored=p[1].Anchored,parts=p}
772end
773
774function newPart()
775 local sG=selGroups[1]
776 local p=Instance.new'Part'
777 local dist=settings.newPartDist
778 local pos
779 if plugin then
780 local cf=game.workspace.CurrentCamera.CFrame
781 pos=cf.p+dist*cf.lookVector
782 else
783 local cf=player.Character:GetPrimaryPartCFrame()
784 pos=cf.p+dist*Vector3.new(cf.lookVector.x,0,cf.lookVector.z)
785 end
786 p.Position=pos
787 p.Size=Vector3.new(1,1,1)
788 p.Parent=sG and(sG[0]or sG[1
789 ].Parent)or game.workspace
790 p.BottomSurface='Smooth'
791 p.TopSurface='Smooth'
792 p.Anchored=true
793 addSelection(p)
794 upd8GameSel()
795
796 if not plugin then
797 partF(p,player)end
798 return p
799end
800
801local welds=game
802.JointsService
803:children()
804function weldSelection()
805 local main=selGroups[selI][1]
806 for i,g in pairs(selGroups) do
807 for i2,g2 in
808 pairs(g) do
809 if i2>=2
810 or i~=selI then
811 local w=Instance.new'Weld'
812 w.Parent=game.JointsService
813 w.Part0,w.Part1,w.C0
814 =main,g2,main.CFrame
815 :inverse()*g2.CFrame
816 welds[#welds+1]=w
817 end
818 end
819 end
820end
821
822function getWelded(p)
823 local t,T={}
824 if not p then
825 T=getSelection()
826 elseif type(p)
827 =='userdata'then
828 T={p
829 }end
830
831 for i,g in pairs(welds) do
832 for i2,g2 in pairs(T) do
833 local doIt=p~=g2
834 for i3,g3 in pairs(t) do
835 if( g3.Part0==g.Part0
836 and g3.Part1==g.Part1)
837 or( g3.Part1==g.Part0
838 and g3.Part0==g.Part1)then
839 doIt=false end end
840 if doIt and(g.Part0==g2 or g.Part1==g2)then
841 t[#t+1]=g
842 for i3,g3 in pairs(getWelded(g2)) do
843 t[#t+1]=g3
844 end
845 end
846 end
847 end
848 return t
849end
850
851function begin(p,a,t)
852 if t.groupI*t
853 .selI<2 then
854 stInfo={}
855 origData={}
856 createST()
857 snapped=0
858 upd8ST()
859 origData.temp={}
860 for i,g in pairs(tempParts) do
861 origData.temp[i]
862 =partDataTable{g}
863 end
864
865 for i,g in pairs(getWelded()) do
866 g.Parent=nil end
867 end
868
869 if t.groupI<2 then
870 local c={}
871 for i,g in pairs(
872 selGroups
873 [t.selI]) do
874 c[#c+1]=g
875 end
876 origData[t.selI]
877 =partDataTable(c)
878 end
879 p.Anchored=true
880end
881
882function release(p,a,t)
883 p.Anchored=origData
884 [t.selI].anchored
885 if t.index<2 then
886 pData={}
887 print'Released.'
888 removeST()
889 end
890
891 pData[t.selI]
892 =partDataTable{p}
893 if t.last then
894 createWaypoint()
895 for i,g in pairs(getWelded()) do
896 g.C0=g.Part0.
897 CFrame:inverse()
898 *g.Part1.CFrame
899 g.Parent=game.JointsService
900 end
901 end
902end
903
904--NOTE: This code makes it possible for the server to move parts for FE compatibility.
905local cp=require(script.CrossPeer)
906function callCPFunc(name,...)
907 cp[name](...)
908 if not plugin then
909 script.Move:FireServer(name,...)
910 end
911end
912
913function setSize(...)
914 callCPFunc('setSize',...)
915end
916
917function setCFrame(...)
918 callCPFunc('setCFrame',...)
919end
920
921local colourMode={
922 materialInfo=(function()
923 local t={}
924 for i,g in pairs(materialIds) do
925 t[i]={transparency
926 =i=='Plastic'and 0
927 or 01,dim={0,1,0}}
928 end
929 return t
930 end)(),
931}
932
933function colourFromCoords(v,brickColor)
934 local c=Color3
935 .fromHSV(v[1],1
936 -v[2],(1-v[3]))
937
938 if brickColor then
939 return BrickColor.
940 new(c).Color
941 end
942 return c
943end
944
945function lookGui()
946 local upd88s={}
947 local mat=script.
948 SpecialGui.Material:clone()
949 local fr,c=mat.Table.Frame,0
950 for i,g in pairs(materialIds) do
951 local sl=c==0 and
952 fr or fr:clone()
953 local il=sl
954 .ImageLabel
955
956 sl.ImageLabel.Image
957 ='rbxassetid://'..g
958 il.ImageRectSize
959 =140*Vector2.new(5
960 ,7/materialCount)
961 il.TextLabel.Text=i
962 sl.LayoutOrder=c
963 sl.Name=i
964 sl.Parent
965 =mat.Table
966 c=c+1
967
968 local function upd88()
969 local il=sl
970 .ImageLabel
971 local colour
972 =colourFromCoords(
973 colourMode.
974 materialInfo[
975 i].dim,true)
976 local trans
977 =colourMode.
978 materialInfo[i
979 ].transparency
980 il.TransDisplay.
981 BackgroundTransparency=trans
982 sl.ImageLabel.TransDisplay.
983 BackgroundColor3=colour
984 il.ImageColor3=colour
985 sl.ImageLabel
986 .TextBox.Text
987 =toNum(trans)
988
989 local eCol=.1313*colour
990 .r+.6781*colour.g+.1566
991 *colour.b>=.5 and Color3
992 .new()or Color3.new(1,1,1)
993 il.TextBox.TextColor3,il
994 .TextLabel.TextColor3,il
995 .TransDisplay.TextColor3
996 ,il.Frame.BackgroundColor3
997 =eCol,eCol,eCol,eCol
998 end
999 upd88s[i
1000 ]=upd88
1001
1002 il.TextBox.FocusLost
1003 :connect(function()
1004 local txt=il.
1005 TextBox.Text
1006 local n=tonumber(txt)
1007 if n and 0<=n
1008 and n<=1 then
1009 colourMode.materialInfo[i].
1010 transparency=tonumber(txt)
1011 end
1012 upd88()
1013 end)
1014
1015 il.TransDisplay.
1016 MouseButton1Down:
1017 connect(function()
1018 colourMode.materialInfo
1019 [i].dim=colourGui(mat.
1020 Colours,colourMode
1021 .materialInfo[i].
1022 dim,true,i)upd88()
1023 end)
1024
1025 upd88()
1026 end
1027
1028 mat.Colour.
1029 MouseButton1Down:
1030 connect(function()
1031 local dim=colourGui(mat.Colours
1032 ,colourMode.materialInfo.Plastic
1033 .colour,true,'All materials')
1034
1035 for i,g
1036 in pairs(colourMode
1037 .materialInfo) do
1038 g.dim=dim
1039 upd88s[i]()
1040 end
1041 end)
1042
1043 return mat,function()
1044 local doIt
1045 for i2,g2 in pairs(colourMode
1046 .materialInfo) do
1047 if g2.transparency<1 then
1048 doIt=true
1049 end
1050 end
1051
1052 if doIt then
1053 local oParts,nParts={},{}
1054 for i,g in pairs(selGroups) do
1055 selections[i]:destroy()
1056 local parent=g[1].Parent
1057 oParts[i]={}nParts[i]={}
1058 for i2,g2 in pairs(g) do
1059 g2.Parent=nil
1060 oParts[i][1
1061 +#oParts
1062 [i]]=g2
1063 end
1064
1065 local count=0
1066 selGroups[i]=g
1067 for i2,g2 in pairs(colourMode
1068 .materialInfo) do
1069 if g2.transparency<1 then
1070 local p=g[1]:clone()
1071
1072 p.Material=i2
1073 p.Parent=parent
1074 p.Transparency=g2.transparency
1075 p.Color=colourFromCoords(g2.dim)
1076 count=count+1
1077 g[count]=p
1078 nParts[i][1
1079 +#nParts
1080 [i]]=p
1081 end
1082 end
1083
1084 for i2=#g,count+1,-1 do
1085 g[i2]=nil
1086 end
1087
1088 selections[i]=selFuncs
1089 [settings.selMode][2](g[1])
1090 end
1091
1092 createWaypoint{oParts,
1093 nParts,mode='colour'}
1094 upd8GameSel()
1095 else
1096 warn'You can\'t do it when all materials are invisible!'
1097 end
1098 end
1099end
1100
1101function colourGui(parent,dim,brickColor,str)
1102 print'Colour grid creates.'
1103 local col=clTemplate:clone()
1104 local chuser=col.Choosers
1105 local p=chuser.ColourFrame
1106 local sl=chuser.SlideFrame
1107 local dim=dim or{0,1,0}
1108 local pC=p.Colour
1109 col.Parent=parent
1110 local colourT={}
1111 col.Visible=true
1112 col.Desc.Text
1113 =str or''
1114
1115 wait()
1116 local cSize=p.AbsoluteSize
1117 chuser.Size=chuser.Size-UDim2
1118 .new(0,cSize.x%8,0,cSize.y%8)
1119 local X,Y=p.AbsoluteSize
1120 .x/8,p.AbsoluteSize.y/8
1121 --print(X,Y)
1122
1123 local function
1124 updateGrid(pos)
1125 local colour=colourFromCoords(dim,brickColor)
1126 for x=0,X do
1127 for y=1,Y do
1128 local c=colourFromCoords(x>0
1129 and{(x-.5)/X,(y-.5)/Y,dim[3]
1130 }or{dim[1],dim[2],(y-.5)
1131 /Y},brickColor and x>0)
1132 colourT[x][y].BackgroundColor3=c
1133 end
1134 end
1135
1136 local b
1137 if colour then
1138 b=BrickColor.new(colour)
1139 col.BrickColorButton.Colour.
1140 BackgroundColor3=b.Color
1141 col.Color3Button.Colour.
1142 BackgroundColor3=colour
1143 end
1144
1145 col.Value.Text
1146 =colour and(brickColor
1147 and b.Number..' '..b.Name
1148
1149 or math.floor(255*colour.r)..' '
1150 ..math.floor(255*colour.g)..' '
1151 ..math.floor(255*colour.b))or''
1152 end
1153
1154 for x=0,X do
1155 colourT[x]={}
1156 for y=1,Y do
1157 local c=(x>0 or y>1)
1158 and pC:clone() or pC
1159 c.LayoutOrder=x+y*X
1160 colourT[x][y]=c
1161 c.Parent=x>0
1162 and p or sl
1163 end
1164 end
1165
1166 p.InputBegan:connect(function(input)
1167 if input.UserInputType.
1168 Name=='MouseButton1' then
1169 local pos=input.Position
1170 pos=(Vector2.new(pos.X,pos
1171 .Y)-p.AbsolutePosition
1172 )/Vector2.new(X*8,Y*8)
1173 colourMode.colour
1174 =colourFromCoords({pos.X,
1175 pos.Y,dim[3]},brickColor)
1176 dim[1],dim[2]
1177 =pos.X,pos.Y
1178 updateGrid(pos)
1179 end
1180 end)
1181 sl.InputBegan:connect(function(input)
1182 if input.UserInputType.
1183 Name=='MouseButton1' then
1184 local pos=(input.Position.Y
1185 -p.AbsolutePosition.Y)/Y/8
1186 colourMode.colour
1187 =colourFromCoords({dim[1
1188 ],dim[2],pos},brickColor)
1189 dim[3]=pos
1190 updateGrid(pos)
1191 end
1192 end)
1193
1194 col.BrickColorButton
1195 .MouseButton1Down
1196 :connect(function()
1197 brickColor
1198 =true
1199 updateGrid()
1200 end)
1201 col.Color3Button
1202 .MouseButton1Down
1203 :connect(function()
1204 brickColor
1205 =false
1206 updateGrid()
1207 end)
1208
1209 updateGrid()
1210 col.Button.MouseButton1Down:wait()
1211 print'Finally.'
1212 col:destroy()
1213 return dim
1214end
1215
1216commands={
1217 {name='move',
1218 key='z',
1219 colour=Color3.new(),
1220 handles=handlesF.MoveHandles,
1221 current={
1222 submode=1,
1223 incr={1},
1224 },
1225 submodes={
1226 'axis',
1227 'first',
1228 'object',
1229 'similar',
1230 'axis-oid',
1231 },
1232 submodeFunc={
1233 [1]=forGroup,
1234 [5]=forObjCentre,
1235 },
1236
1237 MouseDrag=function(part,main,i,face,dist)
1238 local new,isBloqued
1239 local initial=part.CFrame
1240 local orig=origData[i.selI].cf
1241 local inc=commands[mode].current.incr[1]
1242 snapped=math.floor(dist/inc+.5)*inc
1243
1244 local sm=commands[mode].current.submode-1
1245 if sm<1 then
1246 new=orig+snapped*Vector3.FromNormalId(face)
1247
1248 elseif sm<2 then
1249 new=orig+main.CFrame
1250 [({'right','up','look'})
1251 [util.axisIndexFromNormal(face)]..'Vector']*
1252 snapped*util.signForNormal(face)
1253
1254 elseif sm<3 then
1255 new=orig*CFrame.new(snapped*
1256 Vector3.FromNormalId(face))
1257
1258 elseif sm<4 then
1259 new=orig*CFrame.new(
1260 snapped*Vector3.FromNormalId(
1261 util.localNormal(
1262 part,util.globalNormal(main,face))))
1263
1264 elseif sm<5 then
1265 new=orig*CFrame.new(
1266 snapped*Vector3.FromNormalId(
1267 util.localNormal(part,face)))
1268 end
1269
1270 if i.groupI<2 and i.isAdorneer and tempParts[1] then
1271 tempParts[1].CFrame=(sm<2 and plugin)and new*main
1272 .CFrame:inverse()*tempParts[1].CFrame or CFrame.new(new.p)
1273 end
1274
1275 setCFrame(part,new)
1276 --if not isBloqued then
1277 stInfo[i.index]=snapped
1278 --end
1279
1280 if i.last then
1281 upd8ST()
1282 end
1283 end,
1284 MouseButton1Down=begin,
1285 MouseButton1Up=release,
1286 },
1287 {name='resize',
1288 key='x',
1289 colour=Color3.fromRGB(0,120,215),
1290 handles=handlesF.ResizeHandles,
1291 current={
1292 submode=1,
1293 incr={1},
1294 },
1295 submodes={
1296 'side',
1297 'centre',
1298 'uniform',
1299 'scale',
1300 'part scale',
1301 'edge scale',
1302 },
1303 submodeFunc={
1304 [4]=forGroup,
1305 [5]=forObjCentre,
1306 [6]=forEdge,
1307 },
1308 MouseDrag=function(part,main,i,face,dist)
1309 local initial={part.CFrame,part.Size}
1310 local orig=origData[i.selI]
1311 local size=orig.size
1312 local cf=orig.cf
1313 local sign=1
1314
1315 local a=util.axisFromNormal(face)
1316 local axis=Vector3.FromAxis(a)
1317 local sm=commands[mode].current.submode
1318 local inc=commands[mode].current.incr[1]
1319
1320 if sm<2 then
1321 snapped=math.floor(dist/inc+.5)*inc
1322 else
1323 snapped=math.floor(dist/inc*2+.5)*inc
1324 end
1325
1326 if sm<3 then
1327 size=size+snapped*
1328 Vector3.FromAxis(a)
1329 if size[a.Name]<0 then
1330 sign=-1
1331 size=size-
1332 size[a.Name]*
1333 axis*(settings.
1334 resizeNegative
1335 and 2 or 1)
1336 end
1337 else
1338 local m=snapped/(origData.temp[1]or
1339 origData[i.selI]).size[a.Name]+1
1340 local M=(settings.resizeNegative
1341 and math.abs(m)or math.max(m,0))
1342 size=M*size
1343 if sm>3 then
1344 cf=cf-(cf.p-origData
1345 .temp[1].cf.p)*(1-m)
1346 tempParts[1].Size=M*
1347 origData.temp[1].size
1348 tempParts[1].CFrame=
1349 origData.temp[1].cf
1350 end
1351 if m<0 then
1352 sign=-1
1353 end
1354 end
1355
1356
1357 if sm<2 then
1358 cf=cf*CFrame.new((
1359 sign*size-orig.size)
1360 [a.Name]*Vector3.
1361 FromNormalId(face)/2)
1362 end
1363
1364 local rSize=setSize(part,size)
1365 stInfo[i.index]=size[a.Name]
1366 setCFrame(part,cf)
1367 if i.last then
1368 upd8ST()
1369 end
1370 end,
1371 MouseButton1Down=begin,
1372 MouseButton1Up=release,
1373 },
1374 {name='rotate',
1375 key='c',
1376 colour=Color3.new(1,1,1),
1377 handles=handlesF.RotateHandles,
1378 current={
1379 submode=1,
1380 incr={30},
1381 },
1382 submodes={
1383 'part',
1384 'group',
1385 'first',
1386 'base',
1387 'edge',
1388 'face',
1389 },
1390 submodeFunc={
1391 [2]=forGroup,
1392 [4]=forObjCentre,
1393 [5]=forEdge,
1394 },
1395 MouseDrag=function(part,main,i,axis,rad,dist)
1396 local init=part.CFrame
1397 local orig=origData[i.selI]
1398 local sm=commands[mode].current.submode
1399 local inc=commands[mode].current.incr[1]
1400
1401 --Rotate face only.
1402 if sm>5 then
1403 local a=math.floor(rad/math.pi*2+.5)
1404 local size,sizeT,tempA,tempI=orig.size,{}
1405 for i,g in pairs(Enum.Axis:GetEnumItems()) do
1406 if g~=axis and a%2>0 then
1407 if tempA then
1408 local temp=
1409 size[tempA]
1410 sizeT[tempI]=
1411 size[g.Name]
1412 sizeT[i]=temp
1413 else
1414 tempA,tempI
1415 =g.Name,i
1416 end
1417 else
1418 sizeT[i]=
1419 size[g.Name]
1420 end
1421 end
1422
1423 setSize(part,Vector3.new(unpack(sizeT)))
1424 setCFrame(part,orig.cf*CFrame.fromAxisAngle(
1425 Vector3.FromAxis(axis),a*math.pi/2))
1426 return
1427 end
1428
1429 --Every other type of rotation.
1430 local cf
1431 local a=math.deg(rad)%360
1432 if a>180 then
1433 a=a-360 end
1434 snapped=math.floor(a/inc+.5)*inc
1435 local rot=CFrame.fromAxisAngle(
1436 Vector3.FromAxis(axis),math.rad(snapped))
1437 if sm<2 then
1438 cf=orig.cf*rot
1439 elseif tempParts[1] then
1440 local rotCF=origData.temp[1].cf
1441 cf=rotCF*(rot*(rotCF:inverse()*orig.cf))
1442 tempParts[1].CFrame=rotCF*rot
1443 else
1444 local ocf=origData[selI].cf
1445 cf=ocf*(rot*(ocf:inverse()*orig.cf))
1446 end
1447 setCFrame(part,cf)
1448
1449 --[[
1450 local isBloquing
1451 if settings.blocking then
1452 local p=part.Position
1453 part.Position=Vector3.new()
1454 part.Position=p
1455 if part.Position~=p then
1456 part.CFrame=init
1457 isBloquing=true
1458 end
1459 end
1460 ]]
1461
1462 --if not isBloquing then
1463 stInfo[i.index]=snapped
1464 --end
1465 if i.last then
1466 upd8ST()
1467 end
1468 end,
1469 MouseButton1Down=begin,
1470 --A bit broken, may fix later on Roblox's part.
1471 MouseButton1Up=release,
1472 },
1473 {name='utility',
1474 key='v',
1475 colour=Color3.new(1),
1476 current={
1477 submode=1,
1478 },
1479 submodes={
1480 'duplicate',
1481 'new part',
1482 'weld',
1483 },
1484 submodeFunc={
1485 duplicate,
1486 --[[ NOTE: 3D selection is actually very ineffective.
1487 function()
1488 local sel={}
1489 local bounds={}
1490 local iter,dunn=1
1491
1492 local evt=mouse.Button1Up:
1493 connect(function()
1494 iter=iter+1
1495 dunn=iter>3
1496 end)
1497
1498 local p=Instance.new'Part'
1499 local sel=Instance.new'SelectionBox'
1500 sel.SurfaceColor3=accColour()
1501 sel.SurfaceTransparency=0
1502 sel.Parent=core
1503 p.Size=Vector3.new()
1504 sel.Transparency=1
1505 sel.Adornee=p
1506
1507 rsBind('region',function()
1508 if iter<2 then
1509 bounds[1]=
1510 mouse.Hit.p
1511 elseif iter<3 then
1512 bounds[2]=
1513 mouse.Hit.p-
1514 Vector3.new(0,
1515 mouse.Hit.p.y-
1516 bounds[1].y)
1517 else
1518 bounds[2]=
1519 bounds[2]+
1520 Vector3.new(0,
1521 mouse.Hit.p.y-
1522 bounds[2].y)
1523 end
1524
1525 if #bounds>1 then
1526 sel.SurfaceTransparency
1527 =settings.planeTrans
1528 p.Position=Vector3.new(
1529 (bounds[1].x+
1530 bounds[2].x)/2,
1531 (bounds[1].y+
1532 bounds[2].y)/2,
1533 (bounds[1].z+
1534 bounds[2].z)/2
1535 )
1536 p.Size=Vector3.new(
1537 math.abs(bounds[
1538 1].x-bounds[2].x),
1539 math.abs(bounds[
1540 1].y-bounds[2].y),
1541 math.abs(bounds[
1542 1].z-bounds[2].z)
1543 )
1544 else
1545 p.Position
1546 =bounds[1]
1547 end
1548 end)
1549
1550 while not dunn do
1551 wait()end
1552 evt:disconnect()
1553 sel:destroy()
1554 unbind'enter'
1555
1556 for i,g in pairs(game.workspace
1557 :FindPartsInRegion3(Region3.new(
1558 Vector3.new(math.min(bounds[1].x,
1559 bounds[2].x),math.min(bounds[1].y,
1560 bounds[2].y),math.min(bounds[1].z,
1561 bounds[2].z)),Vector3.new(math.max(
1562 bounds[1].x,bounds[2].x),math.max(
1563 bounds[1].y,bounds[2].y),math.max(
1564 bounds[1].z,bounds[2].z))))) do
1565 addSelection(g)
1566 end
1567
1568 upd8GameSel()
1569 end,
1570 ]]
1571 newPart,
1572 weldSelection,
1573 }
1574 },
1575 {name='look',
1576 key='n',
1577 colour=Color3.new(0,1),
1578 current={
1579 submode=1,
1580 },
1581 submodes={
1582 lookGui,
1583 },
1584 submodeFunc={},
1585 },
1586}
1587
1588local modeIndicies={}
1589for i,g in pairs(commands) do
1590 modeIndicies[g.name]=i
1591end
1592
1593--NOTE: an unused modding API. No plans as of 2017-11-27 to do anything about it.
1594for i,g in pairs(defaults.mods[1]) do
1595 print(tostring(g.Parent))
1596 local m=require(g)
1597 for tI,t in pairs(m[1]and m or{m}) do
1598 local mode=tonumber(t.Mode)
1599 or modeIndicies[t.Mode
1600 or'utility']
1601
1602 local cmd=commands[mode]
1603 if cmd then
1604 local oldCount
1605 =#cmd.submodes
1606 cmd.submodes[oldCount
1607 +1]=t.Submode
1608
1609 for i2,g2 in pairs(t) do
1610 if type(g2)=='function' then
1611 if i2=='SelectionChange' then
1612 cmd.submodeFunc
1613 [oldCount+1]=g2
1614 else
1615 local f=cmd[i2]
1616 cmd[i2]=function(...)
1617 if cmd.current
1618 .submode>oldCount then
1619 g2(getfenv(),...)
1620 else
1621 f(...)
1622 end
1623 end
1624 end
1625 end
1626 end
1627 end
1628 end
1629end
1630
1631--[[
1632for i,g in pairs(commands) do
1633 for i2,g2 in pairs(g) do
1634 if type(g2)=='function' then
1635 end
1636 end
1637end
1638]]
1639
1640--Argument passed in is a part.
1641selFuncs={
1642 {'box',function(part)
1643 if plugin then
1644 return{
1645 part=part,
1646 destroy=function()end
1647 }
1648 end
1649
1650 local box=Instance.new'SelectionBox'
1651 box.LineThickness=settings.selBoxSize
1652 box.Color3=commands[mode].colour
1653 box.Parent=core
1654 box.Adornee=part
1655
1656 return{
1657 part=part,
1658 destroy=function()
1659 box:destroy()
1660 end,
1661 }
1662 end},
1663 {'plane',function(part)
1664 local hue=0
1665 local fol=Instance.new'Folder'
1666 fol.Name='Selection'
1667 fol.Parent=core
1668
1669 for i,g in pairs(Enum.NormalId:GetEnumItems()) do
1670 local sg=Instance.new'SurfaceGui'
1671 sg.AlwaysOnTop=settings.planeTop
1672 sg.CanvasSize=Vector2.new(1,1)
1673 sg.Adornee=part
1674 sg.Parent=fol
1675 sg.Face=g
1676
1677 local fr=Instance.new'Frame'
1678 fr.BackgroundColor3=Color3.fromHSV(hue
1679 %1,settings.planeS,settings.planeV)
1680 fr.BackgroundTransparency=settings.planeTrans
1681 fr.Size=UDim2.new(1,0,1,0)
1682 fr.BorderSizePixel=0
1683 fr.Parent=sg
1684
1685 if(i%2==1) then
1686 hue=hue+1/2
1687 else
1688 hue=hue-1/3
1689 end
1690 end
1691
1692 return{
1693 part=part,
1694 destroy=function()
1695 fol:destroy()
1696 end,
1697 }
1698 end},
1699}
1700
1701--rem is a table for reference purposes.
1702function scanParts(par,t,f,rem)
1703 t=t or{}
1704 for i,g in pairs(par:children()) do
1705 if scanParts(g,t
1706 ,f,rem)==0 then
1707 return end
1708
1709 if not f or f(g) then
1710 t[#t+1]=g
1711 if rem then
1712 if rem[1]<1 then
1713 for i in pairs(t) do
1714 t[i]=nil
1715 end
1716 t[1]='nope'
1717 return
1718 else
1719 rem[1]
1720 =rem[1]-1
1721 end
1722 end
1723 end
1724 end
1725end
1726
1727function isValidPart(p)
1728 return(plugin or(verify(p,player)and not(p.Parent:findFirstChild'Animate'and p.Parent
1729 :findFirstChild'Humanoid'))and(settings.selectLocked or not p.Locked))or not p:isA'BasePart'
1730end
1731
1732function getMousePart()
1733 return mouse.Target
1734end
1735
1736--Gets the parts to be selected from the target.
1737local groupLvl=0
1738function getSelParts(part)
1739 local orig=part
1740 if part:isA'BasePart' then
1741 if groupLvl<0 then
1742 local lvls=-1
1743 local par=part
1744 while par~=game do
1745 par=par.Parent
1746 lvls=lvls+1
1747 end
1748 for i=1,lvls+groupLvl do
1749 --if part.Parent.Parent~=game then
1750 part=part.Parent
1751 --end
1752 end
1753 else
1754 for i=1,groupLvl do
1755 if part.Parent.Parent~=game then
1756 part=part.Parent
1757 end
1758 end
1759 end
1760 end
1761
1762 if not part:isA'BasePart'then
1763 local t,count,max={}
1764 ,0,settings.maxParts
1765 scanParts(part
1766 ,t,function(p)
1767 return p:isA'BasePart'
1768 and isValidPart(p)
1769 end,{max})
1770 print(#t)
1771
1772 if t[1]=='nope'then
1773 return{}end
1774 print('This selection has '
1775 ..count..' parts.')
1776 for i,g in pairs(t) do
1777 t[i]={g}end
1778 t[0]=part
1779 return t
1780 end
1781
1782 local t=isValidPart(
1783 part)and{part}or{}
1784 local par=part
1785 for i=1,settings.
1786 selectsSameCFrame do
1787 if par.Parent
1788 ==game then
1789 break
1790 end
1791 par=par.Parent or par
1792 end
1793
1794 scanParts(par,t,function(p)
1795 return p~=part and
1796 p:isA'BasePart' and
1797 p.CFrame==part.CFrame and
1798 p.Size==part.Size and
1799 isValidPart(part)
1800 end)
1801
1802 if #t>0 then
1803 --[[
1804 print('We have got '..#t..' part'
1805 ..(#t>2 and's'or'')..' selected.')
1806 ]]
1807 return{t}
1808 else
1809 return{}
1810 end
1811end
1812
1813--[[ May allow for both box and plane selection?
1814setSelMode=function(s)
1815 settings.selMode=s
1816 for selI,sel in pairs(selGroups) do
1817 selections[selI]:destroy()
1818 selections[selI]=addSelection(sel)
1819 end
1820end
1821]]
1822
1823function reRender()
1824 mf.UIScale.Scale
1825 =settings.guiScaling
1826 config.UIScale.Scale
1827 =settings.guiScaling
1828 print'Rerenderring.'
1829 for i,g in pairs(selections) do
1830 g.destroy()
1831 selections[i]=selFuncs[settings.
1832 selMode][2](selGroups[i][1])
1833 end
1834end
1835
1836function fromNum(n)
1837 return (''..n):gsub('%.(.'
1838 ..string.rep('.?',settings
1839 .decRound-1)..').*',',%1')
1840end
1841function toNum(s,p)
1842 if p then
1843 local f=s
1844 :sub(1,1)
1845 local rem
1846 =toNum(s
1847 :sub(2))
1848 if'+'==
1849 f then
1850 return p+rem
1851 elseif f
1852 =='-'then
1853 return p-rem
1854 elseif f
1855 =='*'then
1856 return p*rem
1857 elseif f
1858 =='/'then
1859 return p/rem
1860 elseif f
1861 =='%'then
1862 return p%rem
1863 end
1864 end
1865
1866 if tonumber(s) then
1867 return s+0
1868 end
1869
1870 local ftTable={s:find'^(%d+)\'%-?([%d%.]*)"?$'}
1871 if #ftTable>0 then
1872 return ftTable[3]+(ftTable[4]or 0)/12
1873 end
1874
1875 for i,g in pairs{
1876 km=105e+3/32,
1877 m=105e+0/32,
1878 cm=105e-2/32,
1879 mm=105e-3/32,
1880
1881 ft=1,
1882 yd=3,mi=5280,
1883 ['in']=1/12,
1884 }do
1885 local m={s:find('^([%d%.]+)%s*'..i..'$')}
1886 if m[3] then
1887 return g*m[3]
1888 end
1889 end
1890end
1891
1892--Saves the increments stored in 'commands' for future use.
1893function updateConfigIncrs()
1894 local t={}
1895 for i,g in pairs(commands) do
1896 if g.current then
1897 t[i]=g.current.incr
1898 end
1899 end
1900 settings.incrs=t
1901 print'Saved.'
1902end
1903
1904local defaultIncrs={}
1905for i,g in pairs(commands) do
1906 local t={}
1907 for i2,g2 in pairs(g.
1908 current.incr or{}) do
1909 t[i2]=g2 end
1910 defaultIncrs[i]=t
1911end
1912
1913--Loads the increments from settings, if set to do so.
1914if settings.saveIncrs
1915and settings.incrs then
1916 local settingIncrs=settings.incrs
1917 for i,g in pairs(commands) do
1918 local incrs=settingIncrs[i]
1919 if g.current and incrs then
1920 g.current.incr=incrs
1921 end
1922 end
1923else
1924 updateConfigIncrs()
1925end
1926
1927bind('increment',function()
1928 incBox:CaptureFocus()
1929end,'k')
1930
1931incBox.Undo.MouseEnter:connect(function()
1932 incBox.TextColor3=accColour()
1933 local incr=commands[mode].current.incr
1934 incBox.Text=fromNum(incr[2]or incr[1])
1935end)
1936incBox.Undo.MouseLeave:connect(function()
1937 incBox.TextColor3=Color3.new(1,1,1)
1938 incBox.Text=fromNum(commands[mode].current.incr[1])
1939end)
1940incBox.Undo.MouseButton1Click:connect(function()
1941 incBox.TextColor3=Color3.new(1,1,1)
1942 local incr=commands[mode].current.incr
1943 incBox.Text=fromNum(incr[2])
1944 local first=incr[1]
1945 for i=1,#incr do
1946 incr[i]=incr[i+1] or first
1947 end
1948 updateConfigIncrs()
1949end)
1950
1951incBox.Redo.MouseEnter:connect(function()
1952 incBox.TextColor3=accColour()
1953 local incr=commands[mode].current.incr
1954 incBox.Text=fromNum(incr[#incr])
1955end)
1956incBox.Redo.MouseLeave:connect(function()
1957 incBox.TextColor3=Color3.new(1,1,1)
1958 incBox.Text=fromNum(commands[mode].current.incr[1])
1959end)
1960incBox.Redo.MouseButton1Click:connect(function()
1961 incBox.TextColor3=Color3.new(1,1,1)
1962 local incr=commands[mode].current.incr
1963 local last=incr[#incr]
1964 incBox.Text=fromNum(last)
1965 for i=#incr,1,-1 do
1966 incr[i]=incr[i-1] or last
1967 end
1968 updateConfigIncrs()
1969end)
1970
1971--Resets the increments
1972bind('clearIncs',function()
1973 print'Increments clearing.'
1974 if settings.clearAllOnIncrementClear then
1975 for i,g in pairs(commands) do
1976 g.current.incr={}
1977 --Not all modes need increments!
1978 for i2,g2 in pairs(
1979 defaultIncrs[i]) do
1980 g.current.incr[i2]=g2 end
1981 end else
1982 commands[mode].current.incr={}
1983 for i,g in pairs(defaultIncrs[mode]) do
1984 commands[mode].current.incr[i]=g end
1985 end
1986
1987 local incr=commands[mode].current.incr
1988 incBox.Text=fromNum(incr[1])
1989 updateConfigIncrs()
1990end,'m',true)
1991
1992incBox.FocusLost:connect(function()
1993 local incr=commands
1994 [mode].current.incr
1995 local v=toNum(incBox
1996 .Text,incr[1])
1997
1998 if v and v>0 then
1999 incBox.Text=v
2000 for i=#incr,0,-1 do
2001 incr[i+1]=incr[i] or v
2002 end
2003 updateConfigIncrs()
2004 else
2005 incBox.Text=incr[1]
2006 end
2007end)
2008
2009local lvlBox=sm.Level.TextBox
2010bind('level',function()
2011 lvlBox:CaptureFocus()
2012end,'l')
2013lvlBox.FocusLost:connect(function()
2014 local v=toNum(lvlBox.Text)
2015 if v then
2016 lvlBox.Text=v
2017 groupLvl=v
2018 else
2019 lvlBox.Text
2020 =groupLvl
2021 end
2022end)
2023
2024config.Visible=false
2025sm.Config.Button.MouseButton1Click:connect(function()
2026 config.Visible=not config.Visible
2027end)
2028bind('config',function()
2029 config.Visible=not config.Visible
2030end,Enum.KeyCode.RightBracket)
2031
2032local configTypes={}
2033for i,g in pairs(config:children()) do
2034 if g:isA'Frame' then
2035 configTypes[g.Name]=g:clone()
2036 g:destroy()
2037 end
2038end
2039
2040defaults.selMode[3]=#selFuncs
2041for i,g in pairs(defaults) do
2042 local ty=type(g[1])
2043 local typeFrame=configTypes[ty]
2044 if typeFrame and g[2]~='NO_TOUCH' then
2045 local tf=typeFrame:clone()
2046 tf.TextLabel.Text=i
2047
2048 if ty=='number' then
2049 local tb=tf.TextBox
2050 tb.Text=fromNum(settings[i])
2051 tb.FocusLost:connect(function()
2052 local n=toNum(tb.
2053 Text,settings[i])
2054 if n
2055 and (not g[2] or n>=g[2])
2056 and (not g[3] or n<=g[3])
2057 and (not g[4] or n%g[4]==0) then
2058 settings[i]=n
2059 print'Ja, the setting was changed.'
2060 end
2061 tb.Text=fromNum(settings[i])
2062 end)
2063
2064 elseif ty=='boolean' then
2065 local upd8=function()
2066 tf.TextLabel.TextColor3=settings[i] and accColour() or Color3.new(1,1,1)
2067 end
2068 upd8()
2069 tf.Button.MouseButton1Click:connect(function()
2070 settings[i]=not settings[i]
2071 upd8()
2072 end)
2073
2074 end
2075 tf.Name=i
2076 tf.Parent=config
2077 end
2078end
2079
2080--Set to the function keys!
2081local toolKeys={'Q','E','R','T','U','P'}
2082for i,g in pairs(toolKeys) do
2083 toolKeys[i]=g end
2084
2085function setSubmode(ii)
2086 if ii then
2087 print'Submode set.'
2088 end
2089 local m=commands[mode]
2090 local prev=m.current.submode
2091 local i=ii or prev
2092 if m.submodes[i] then
2093 if type(m.submodes[prev])=='string' then
2094 sm[prev].TextLabel.TextColor3=Color3.new(1,1,1)
2095 end
2096 if type(m.submodes[i])=='string' then
2097 sm[i].TextLabel.TextColor3=accColour()
2098 end
2099
2100 m.current.submode=i
2101 for i,g in pairs(tempParts) do
2102 g:destroy()
2103 tempParts[i]=nil
2104 end
2105
2106 --Calls any selection-changing methods if any.
2107 canDoStuff=false
2108 local func=m.submodeFunc[i]
2109 if handles and handles.Adornee then
2110 handles.Adornee=type(func)
2111 =='function'and func(handles
2112 ,selGroups[selI][1],ii~=nil)
2113 or adornee
2114 elseif ii and not m.handles then
2115 func()
2116 end
2117 canDoStuff=true
2118 end
2119end
2120
2121--Also used to change the main part, however not the submode.
2122function setMode(s,ii)
2123 reactiv8()
2124 local m=commands[mode]
2125 selI=ii or 1
2126
2127 --Clone the handle as to not overlap events.
2128 if m.handles then
2129 m.handles.Adornee=nil
2130 handles=m.handles:clone()
2131 m.handles:destroy()
2132 m.handles=handles
2133 end
2134
2135 mode=s or mode
2136 submode=1
2137
2138 cmds[m.name].Keybind.
2139 TextColor3=Color3.new(1,1,1)
2140 m=commands[mode]
2141 cmds[m.name].Keybind.
2142 TextColor3=accColour()
2143
2144 --Responsible for updating the handles and what not.
2145 if selections[selI] then
2146 if s then
2147 print('Mode '..s)
2148 reRender()
2149 end
2150
2151 adornee=selections[selI].part
2152 handles=m.handles
2153 if handles then
2154 handles.Parent=core
2155 handles.Adornee=adornee
2156
2157 print('The adornee is '..tostring(adornee)..'.')
2158 for i,g in pairs(m) do
2159 if type(g)=='function' then
2160 --Connects each command event to the handle so that it may function.
2161 --handles[i]:connect(g)
2162 handles[i]:connect(function(...)
2163 --Iterates over each object in the selection.
2164 if canDoStuff then
2165 local index=0
2166 local args={...}
2167 --[[NOTE: The handleIntensity setting determines the proportion
2168 between how far you pull the handle and how far the object moves.]]
2169 if i=='MouseDrag' then
2170 args[2]=args
2171 [2]*settings
2172 .handleIntensity
2173 end
2174
2175 for i2,g2 in pairs(selGroups) do
2176 for i3=1,#g2 do
2177 index=index+1
2178 g(g2[i3],adornee,{
2179 index=index,
2180 selI=i2,groupI=i3,
2181 isAdorneer=i2==selI and i3==1,
2182 last=i2==#selGroups and i3==#g2,
2183 },unpack(args))
2184 end
2185 end
2186 end
2187 end)
2188 end
2189 end
2190 end
2191
2192 --[[
2193 if m.Activated then
2194 local index=0
2195 for i,g in pairs(selGroups) do
2196 for i2,g2 in pairs(g) do
2197 index=index+1
2198 m.Activated(g2,adornee,{
2199 index=index,
2200 selI=i,groupI=i2,
2201 isAdorneer=i==selI and i2==1,
2202 last=i==#selGroups and i2==#g,
2203 })
2204 end
2205 end
2206 end
2207 ]]
2208 end
2209
2210 --Now for the GUI stuffs.
2211 if s then
2212 local
2213 i,c=1
2214 while sm:findFirstChild(i) do
2215 sm:findFirstChild(i):destroy()
2216 i=i+1
2217 end
2218
2219 for i,g in pairs(m.submodes) do
2220 if type(g)=='string' then
2221 c=smTemplate:clone()
2222 c.TextLabel.Text=g
2223
2224 elseif type(g)
2225 =='function'then
2226 local t={g(i)}
2227 c=t[1]
2228 if t[2
2229 ]then
2230 m.submodeFunc[i]=t[2]
2231 end
2232 end
2233
2234 local kl=c:findFirstChild('KeyLabel',true)
2235 if kl then
2236 kl.Text=(plugin
2237 and i<11)and i%10
2238 or toolKeys[i] or''
2239 end
2240
2241 local b=c:findFirstChild'Button'
2242 if b then
2243 b.MouseButton1Click
2244 :connect(function()
2245 setSubmode(i)
2246 end)
2247 end
2248 c.Name=i
2249 c.Parent=sm
2250 c.Visible=true
2251 end
2252
2253 if m.current.incr then
2254 sm.Increment.Visible=true
2255 sm.Increment.TextBox.Text
2256 =toNum(m.current.incr[1])
2257 else
2258 sm.Increment.Visible=false
2259 end
2260 end
2261 setSubmode()
2262end
2263
2264local button=plugin and plugin:CreateToolbar'VisualPlugin':
2265CreateButton(id,'Windowzhiuh.','rbxasset://textures/ui/TixIcon.png')
2266
2267--Activates the plugin.
2268function activ8(a)
2269 local willBeActive=a~=nil and a or not active
2270 print('Active was set to '..(willBeActive and 'on.' or 'off.'))
2271 gui.Parent=willBeActive and core or script
2272
2273 if willBeActive then
2274 reactiv8()
2275 for i,g in pairs(binders) do
2276 bindH(g)
2277 end
2278 else
2279 for i,g in pairs(binders) do
2280 game.ContextActionService
2281 :UnbindAction(i)
2282 end
2283
2284 if plugin then
2285 print'Lost.'
2286 button:SetActive(false)
2287 end
2288 end
2289
2290 active=willBeActive
2291 if active then
2292 if settings.setToSelection then
2293 for i,g in pairs(game.Selection:Get()) do
2294 addSelection(g)
2295 end
2296 end
2297 else
2298 clearSelection()
2299 end
2300end
2301
2302function reactiv8()
2303 if not active and plugin then
2304 plugin:Activate(true)
2305 button:SetActive(true)
2306 end
2307end
2308
2309if plugin then
2310 button.Click:connect(activ8)
2311else
2312 tool.Equipped:connect(activ8)
2313 tool.Unequipped:connect(activ8)
2314end
2315
2316sm.Re.Visible=plugin~=nil
2317sm.Re.Button.MouseButton1Click
2318:connect(function()
2319 reactiv8()
2320end)
2321
2322if plugin then
2323 plugin.Deactivation:connect(function()
2324 local rt=plugin:GetSelectedRibbonTool().Name
2325 if active and'None'~=rt
2326 and'Select'~=rt then
2327 print'Deactivated.'
2328 end
2329 end)
2330end
2331
2332local timeSC
2333local screentip=gui.ScreenTip
2334local delayTime=settings.screenTipDelay
2335gui.ScreenTip.Visible=false
2336function createST()
2337 if not screentip.Visible then
2338 screentip.Visible=true
2339 upd8ST('')
2340 end
2341end
2342
2343--If nothing is passed into s, we get the stInfo by default.
2344function upd8ST(s)
2345 timeSC=tick()
2346 screentip.Position=UDim2
2347 .new(0,mouse.X,0,mouse.Y)
2348 if s then
2349 screentip.Text=s
2350 else
2351 local t,b={}
2352 screentip.Text=fromNum(snapped)
2353 for i,g in pairs(stInfo) do
2354 b=true
2355 local g=fromNum(g)
2356 for i2,g2 in pairs(t) do
2357 if g==g2 then
2358 b=false
2359 break
2360 end
2361 end
2362 if b then
2363 t[#t+1]=g
2364 end
2365 end
2366 if #t>0 and t[1]~=snapped then
2367 screentip.Text=screentip.Text..' ('
2368 ..table.concat(t,', ')..')'
2369 end
2370 end
2371
2372 screentip.Visible=true
2373 local size=screentip.TextBounds
2374 screentip.Size=UDim2.new(0,size.X+5,0,size.Y)
2375
2376 delay(delayTime,function()
2377 if tick()-delayTime+.1>=timeSC then
2378 removeST()
2379 end
2380 end)
2381
2382 --[[The oh-so-requested 'undo rotation'.
2383 if mode==3 then
2384 delay(3,function()
2385 if tick()-3+.1>=timeSC then
2386 print'Luhh.'
2387 createWaypoint()
2388 end
2389 end)
2390 end
2391 ]]
2392end
2393
2394function removeST()
2395 screentip.Visible=false
2396end
2397
2398mouse.Button1Down:connect(function()
2399 local p=getMousePart()
2400 if canDoStuff and p then
2401 toggleSelection(p)
2402 upd8GameSel()
2403 end
2404end)
2405
2406--NOTE: Right click is for changing the main part.
2407mouse.Button2Down:connect(function()
2408 local p=getMousePart()
2409 if canDoStuff and p then
2410 local c=selContains(p)
2411 if c then
2412 setMode(nil,c[2])
2413 end
2414 end
2415end)
2416
2417--Only actually changed the selection if a setting is turnt up.
2418game.Selection.SelectionChanged:connect(function()
2419 if not addWhenUpd8 or not active then
2420 return
2421 end
2422
2423 print'Selection changed.'
2424 local prevSel=getSelection()
2425 local sel=game.Selection:Get()
2426 print('There were '..#prevSel..' selected.')
2427 print('There are now '..#sel..' selected.')
2428
2429 doPrint=false
2430 for i,g in pairs(prevSel) do
2431 local doIt=true
2432 for i2,g2 in pairs(sel) do
2433 if g2==g then
2434 doIt=false
2435 break
2436 end
2437 end
2438 if doIt then
2439 removeSelection(g)
2440 end
2441 end
2442
2443 local changed
2444 doPrint=settings.debugP
2445 for i,g in pairs(sel) do
2446 local doIt=true
2447 for i2,g2 in pairs(prevSel) do
2448 if g2==g then
2449 doIt=false
2450 break
2451 end
2452 end
2453 if doIt and not addSelection(g,true) then
2454 sel[i],changed=nil,true
2455 end
2456 end
2457 if changed then
2458 wait()
2459 game.Selection:Set(sel)
2460 end
2461
2462 --[[
2463 clearSelection()
2464 if active and settings.setToSelection then
2465 for i,g in pairs(game.Selection:Get()) do
2466 if g:isA'BasePart'then
2467 addSelection(g,true)
2468 elseif g:isA'Model'then
2469 addSelection(g)
2470 end
2471 end
2472 end
2473 ]]
2474end)
2475
2476--Just GUI stuff.
2477local c=0
2478local template=cmds.Template
2479for i,g in pairs(commands) do
2480 c=c+1
2481 local clone
2482 =template:clone()
2483 clone.Name=g.name
2484 clone.LayoutOrder=c
2485 clone.KeyName.Text=g.name
2486 clone.Parent=template.Parent
2487 clone.Keybind.Text=g.key:upper()
2488
2489 --[[
2490 clone.MouseEnter:
2491 connect(function(x,y)
2492 clone.Keybind.
2493 BackgroundColor3
2494 =accColour()
2495 end)
2496
2497 clone.MouseLeave:
2498 connect(function(x,y)
2499 clone.Keybind.
2500 BackgroundColor3
2501 =Color3.new(1,1,1)
2502 end)
2503 ]]
2504
2505 --Der maneras of changing mode.
2506 clone.MouseButton1Click
2507 :connect(function(x,y)
2508 print'Inputted.'
2509 setMode(i)
2510 end)
2511 bind(g.name,function(n)
2512 setMode(i)
2513 end,g.key)
2514end
2515cmds.Size=UDim2
2516.new(1,0,0,math.
2517ceil(c/2)*15+1)
2518template.
2519Visible
2520=false
2521
2522--Binds our submode keys.
2523--NOTE: If Ctrl+NUM is pressed whilst in tool mode, it switches to that mode number.
2524for i=1,10 do
2525 local sm=i
2526 bind('NKsubmode'
2527 ..i,function(n)
2528 setSubmode(sm)
2529 end,''..i%10
2530 ,not plugin)
2531end
2532
2533if not plugin then
2534 for i,g in pairs(toolKeys) do
2535 local sm=i
2536 bind('TKsubmode'
2537 ..i,function(n)
2538 setSubmode(sm)
2539 end,Enum.KeyCode[g])
2540 end
2541end
2542
2543local fr=sm.Version.Frame
2544fr.Image.Image=version.shape
2545fr.Version.Text=type(version.sub)=='number'
2546 and fromNum(version.sub)or version.sub
2547fr.Version.TextColor3=accColour()
2548
2549--NOTE: The coloured bar below the version identifier is used for tracking the client's user ID.
2550local dColours={[0]=
2551 Color3.new(1,0,0),
2552 Color3.new(1,1,0),
2553 Color3.new(0,1,0),
2554 Color3.new(0,1,1),
2555 Color3.new(0,0,1),
2556 Color3.new(1,0,1),
2557
2558 Color3.new(0,0,0),--6
2559 Color3.new(1,1,1),--7
2560 Color3.new(.3,.3,.3),--8
2561 Color3.new(.7,.7,.7),--9
2562}
2563local tId,i=id,0
2564local sq=fr.Line:WaitForChild'Square':clone()
2565sq.Name='IDer'
2566repeat
2567 local tSq=sq:clone()
2568 tSq.BackgroundColor3
2569 =dColours[tId%10]
2570 tId=math.floor(tId/10)
2571 tSq.LayoutOrder=-i
2572 tSq.Parent=fr.Line
2573 i=i+1
2574until tId<1
2575fr.Line.Square:destroy()
2576
2577script.ServerEvt.
2578Disabled=plugin~=nil
2579if not plugin then
2580 mf.Position=mf.Position
2581 +UDim2.new(0,0,0,250)
2582end
2583
2584--[[NOTE:
2585 The special key (SPC) is ctrl/cmd in tool mode or shift in plugin mode.
2586 SPC + B = deselect all objects.
2587 SPC + Z = undo.
2588 SPC + Y = redo.
2589 SPC + X = delete.
2590 SPC + C = duplicate.
2591 SPC + M = reset increments.
2592]]
2593bind('deselect',function()
2594 clearSelection()
2595 upd8GameSel()
2596end,'b',true)
2597
2598local udr=sm.UDR
2599udr.Delete.Button.MouseButton1Click:connect(delete)
2600udr.Undo.Button.MouseButton1Click:connect(undo)
2601udr.Redo.Button.MouseButton1Click:connect(redo)
2602bind('duplicate',duplicate,'c',true)
2603bind('delete',delete,'x',true)
2604bind('undo',undo,'z',true)
2605bind('redo',redo,'y',true)
2606
2607--------------------------------------------------------------------------------------------------------
2608
2609setMode(1)
2610reRender()
2611print'Dunn.'