· 7 years ago · Dec 13, 2018, 09:06 PM
1local t = {}
2
3local rbxUtilitySetParentLastFlagSuccess, rbxUtilitySetParentLastFlagValue = pcall(function() return UserSettings():IsUserFeatureEnabled("UserRbxUtilityCreateSetParentLast") end)
4local rbxUtilitySetParentLast = (rbxUtilitySetParentLastFlagSuccess == true and rbxUtilitySetParentLastFlagValue == true)
5
6------------------------------------------------------------------------------------------------------------------------
7------------------------------------------------------------------------------------------------------------------------
8------------------------------------------------------------------------------------------------------------------------
9------------------------------------------------JSON Functions Begin----------------------------------------------------
10------------------------------------------------------------------------------------------------------------------------
11------------------------------------------------------------------------------------------------------------------------
12------------------------------------------------------------------------------------------------------------------------
13
14 --JSON Encoder and Parser for Lua 5.1
15 --
16 --Copyright 2007 Shaun Brown (http://www.chipmunkav.com)
17 --All Rights Reserved.
18
19 --Permission is hereby granted, free of charge, to any person
20 --obtaining a copy of this software to deal in the Software without
21 --restriction, including without limitation the rights to use,
22 --copy, modify, merge, publish, distribute, sublicense, and/or
23 --sell copies of the Software, and to permit persons to whom the
24 --Software is furnished to do so, subject to the following conditions:
25
26 --The above copyright notice and this permission notice shall be
27 --included in all copies or substantial portions of the Software.
28 --If you find this software useful please give www.chipmunkav.com a mention.
29
30 --THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
31 --EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
32 --OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
33 --IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
34 --ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
35 --CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
36 --CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
37
38local string = string
39local math = math
40local table = table
41local error = error
42local tonumber = tonumber
43local tostring = tostring
44local type = type
45local setmetatable = setmetatable
46local pairs = pairs
47local ipairs = ipairs
48local assert = assert
49
50
51local StringBuilder = {
52 buffer = {}
53}
54
55function StringBuilder:New()
56 local o = {}
57 setmetatable(o, self)
58 self.__index = self
59 o.buffer = {}
60 return o
61end
62
63function StringBuilder:Append(s)
64 self.buffer[#self.buffer+1] = s
65end
66
67function StringBuilder:ToString()
68 return table.concat(self.buffer)
69end
70
71local JsonWriter = {
72 backslashes = {
73 ['\b'] = "\\b",
74 ['\t'] = "\\t",
75 ['\n'] = "\\n",
76 ['\f'] = "\\f",
77 ['\r'] = "\\r",
78 ['"'] = "\\\"",
79 ['\\'] = "\\\\",
80 ['/'] = "\\/"
81 }
82}
83
84function JsonWriter:New()
85 local o = {}
86 o.writer = StringBuilder:New()
87 setmetatable(o, self)
88 self.__index = self
89 return o
90end
91
92function JsonWriter:Append(s)
93 self.writer:Append(s)
94end
95
96function JsonWriter:ToString()
97 return self.writer:ToString()
98end
99
100function JsonWriter:Write(o)
101 local t = type(o)
102 if t == "nil" then
103 self:WriteNil()
104 elseif t == "boolean" then
105 self:WriteString(o)
106 elseif t == "number" then
107 self:WriteString(o)
108 elseif t == "string" then
109 self:ParseString(o)
110 elseif t == "table" then
111 self:WriteTable(o)
112 elseif t == "function" then
113 self:WriteFunction(o)
114 elseif t == "thread" then
115 self:WriteError(o)
116 elseif t == "userdata" then
117 self:WriteError(o)
118 end
119end
120
121function JsonWriter:WriteNil()
122 self:Append("null")
123end
124
125function JsonWriter:WriteString(o)
126 self:Append(tostring(o))
127end
128
129function JsonWriter:ParseString(s)
130 self:Append('"')
131 self:Append(string.gsub(s, "[%z%c\\\"/]", function(n)
132 local c = self.backslashes[n]
133 if c then return c end
134 return string.format("\\u%.4X", string.byte(n))
135 end))
136 self:Append('"')
137end
138
139function JsonWriter:IsArray(t)
140 local count = 0
141 local isindex = function(k)
142 if type(k) == "number" and k > 0 then
143 if math.floor(k) == k then
144 return true
145 end
146 end
147 return false
148 end
149 for k,v in pairs(t) do
150 if not isindex(k) then
151 return false, '{', '}'
152 else
153 count = math.max(count, k)
154 end
155 end
156 return true, '[', ']', count
157end
158
159function JsonWriter:WriteTable(t)
160 local ba, st, et, n = self:IsArray(t)
161 self:Append(st)
162 if ba then
163 for i = 1, n do
164 self:Write(t[i])
165 if i < n then
166 self:Append(',')
167 end
168 end
169 else
170 local first = true;
171 for k, v in pairs(t) do
172 if not first then
173 self:Append(',')
174 end
175 first = false;
176 self:ParseString(k)
177 self:Append(':')
178 self:Write(v)
179 end
180 end
181 self:Append(et)
182end
183
184function JsonWriter:WriteError(o)
185 error(string.format(
186 "Encoding of %s unsupported",
187 tostring(o)))
188end
189
190function JsonWriter:WriteFunction(o)
191 if o == Null then
192 self:WriteNil()
193 else
194 self:WriteError(o)
195 end
196end
197
198local StringReader = {
199 s = "",
200 i = 0
201}
202
203function StringReader:New(s)
204 local o = {}
205 setmetatable(o, self)
206 self.__index = self
207 o.s = s or o.s
208 return o
209end
210
211function StringReader:Peek()
212 local i = self.i + 1
213 if i <= #self.s then
214 return string.sub(self.s, i, i)
215 end
216 return nil
217end
218
219function StringReader:Next()
220 self.i = self.i+1
221 if self.i <= #self.s then
222 return string.sub(self.s, self.i, self.i)
223 end
224 return nil
225end
226
227function StringReader:All()
228 return self.s
229end
230
231local JsonReader = {
232 escapes = {
233 ['t'] = '\t',
234 ['n'] = '\n',
235 ['f'] = '\f',
236 ['r'] = '\r',
237 ['b'] = '\b',
238 }
239}
240
241function JsonReader:New(s)
242 local o = {}
243 o.reader = StringReader:New(s)
244 setmetatable(o, self)
245 self.__index = self
246 return o;
247end
248
249function JsonReader:Read()
250 self:SkipWhiteSpace()
251 local peek = self:Peek()
252 if peek == nil then
253 error(string.format(
254 "Nil string: '%s'",
255 self:All()))
256 elseif peek == '{' then
257 return self:ReadObject()
258 elseif peek == '[' then
259 return self:ReadArray()
260 elseif peek == '"' then
261 return self:ReadString()
262 elseif string.find(peek, "[%+%-%d]") then
263 return self:ReadNumber()
264 elseif peek == 't' then
265 return self:ReadTrue()
266 elseif peek == 'f' then
267 return self:ReadFalse()
268 elseif peek == 'n' then
269 return self:ReadNull()
270 elseif peek == '/' then
271 self:ReadComment()
272 return self:Read()
273 else
274 return nil
275 end
276end
277
278function JsonReader:ReadTrue()
279 self:TestReservedWord{'t','r','u','e'}
280 return true
281end
282
283function JsonReader:ReadFalse()
284 self:TestReservedWord{'f','a','l','s','e'}
285 return false
286end
287
288function JsonReader:ReadNull()
289 self:TestReservedWord{'n','u','l','l'}
290 return nil
291end
292
293function JsonReader:TestReservedWord(t)
294 for i, v in ipairs(t) do
295 if self:Next() ~= v then
296 error(string.format(
297 "Error reading '%s': %s",
298 table.concat(t),
299 self:All()))
300 end
301 end
302end
303
304function JsonReader:ReadNumber()
305 local result = self:Next()
306 local peek = self:Peek()
307 while peek ~= nil and string.find(
308 peek,
309 "[%+%-%d%.eE]") do
310 result = result .. self:Next()
311 peek = self:Peek()
312 end
313 result = tonumber(result)
314 if result == nil then
315 error(string.format(
316 "Invalid number: '%s'",
317 result))
318 else
319 return result
320 end
321end
322
323function JsonReader:ReadString()
324 local result = ""
325 assert(self:Next() == '"')
326 while self:Peek() ~= '"' do
327 local ch = self:Next()
328 if ch == '\\' then
329 ch = self:Next()
330 if self.escapes[ch] then
331 ch = self.escapes[ch]
332 end
333 end
334 result = result .. ch
335 end
336 assert(self:Next() == '"')
337 local fromunicode = function(m)
338 return string.char(tonumber(m, 16))
339 end
340 return string.gsub(
341 result,
342 "u%x%x(%x%x)",
343 fromunicode)
344end
345
346function JsonReader:ReadComment()
347 assert(self:Next() == '/')
348 local second = self:Next()
349 if second == '/' then
350 self:ReadSingleLineComment()
351 elseif second == '*' then
352 self:ReadBlockComment()
353 else
354 error(string.format(
355 "Invalid comment: %s",
356 self:All()))
357 end
358end
359
360function JsonReader:ReadBlockComment()
361 local done = false
362 while not done do
363 local ch = self:Next()
364 if ch == '*' and self:Peek() == '/' then
365 done = true
366 end
367 if not done and
368 ch == '/' and
369 self:Peek() == "*" then
370 error(string.format(
371 "Invalid comment: %s, '/*' illegal.",
372 self:All()))
373 end
374 end
375 self:Next()
376end
377
378function JsonReader:ReadSingleLineComment()
379 local ch = self:Next()
380 while ch ~= '\r' and ch ~= '\n' do
381 ch = self:Next()
382 end
383end
384
385function JsonReader:ReadArray()
386 local result = {}
387 assert(self:Next() == '[')
388 local done = false
389 if self:Peek() == ']' then
390 done = true;
391 end
392 while not done do
393 local item = self:Read()
394 result[#result+1] = item
395 self:SkipWhiteSpace()
396 if self:Peek() == ']' then
397 done = true
398 end
399 if not done then
400 local ch = self:Next()
401 if ch ~= ',' then
402 error(string.format(
403 "Invalid array: '%s' due to: '%s'",
404 self:All(), ch))
405 end
406 end
407 end
408 assert(']' == self:Next())
409 return result
410end
411
412function JsonReader:ReadObject()
413 local result = {}
414 assert(self:Next() == '{')
415 local done = false
416 if self:Peek() == '}' then
417 done = true
418 end
419 while not done do
420 local key = self:Read()
421 if type(key) ~= "string" then
422 error(string.format(
423 "Invalid non-string object key: %s",
424 key))
425 end
426 self:SkipWhiteSpace()
427 local ch = self:Next()
428 if ch ~= ':' then
429 error(string.format(
430 "Invalid object: '%s' due to: '%s'",
431 self:All(),
432 ch))
433 end
434 self:SkipWhiteSpace()
435 local val = self:Read()
436 result[key] = val
437 self:SkipWhiteSpace()
438 if self:Peek() == '}' then
439 done = true
440 end
441 if not done then
442 ch = self:Next()
443 if ch ~= ',' then
444 error(string.format(
445 "Invalid array: '%s' near: '%s'",
446 self:All(),
447 ch))
448 end
449 end
450 end
451 assert(self:Next() == "}")
452 return result
453end
454
455function JsonReader:SkipWhiteSpace()
456 local p = self:Peek()
457 while p ~= nil and string.find(p, "[%s/]") do
458 if p == '/' then
459 self:ReadComment()
460 else
461 self:Next()
462 end
463 p = self:Peek()
464 end
465end
466
467function JsonReader:Peek()
468 return self.reader:Peek()
469end
470
471function JsonReader:Next()
472 return self.reader:Next()
473end
474
475function JsonReader:All()
476 return self.reader:All()
477end
478
479function Encode(o)
480 local writer = JsonWriter:New()
481 writer:Write(o)
482 return writer:ToString()
483end
484
485function Decode(s)
486 local reader = JsonReader:New(s)
487 return reader:Read()
488end
489
490function Null()
491 return Null
492end
493-------------------- End JSON Parser ------------------------
494
495t.DecodeJSON = function(jsonString)
496 pcall(function() warn("RbxUtility.DecodeJSON is deprecated, please use Game:GetService('HttpService'):JSONDecode() instead.") end)
497
498 if type(jsonString) == "string" then
499 return Decode(jsonString)
500 end
501 print("RbxUtil.DecodeJSON expects string argument!")
502 return nil
503end
504
505t.EncodeJSON = function(jsonTable)
506 pcall(function() warn("RbxUtility.EncodeJSON is deprecated, please use Game:GetService('HttpService'):JSONEncode() instead.") end)
507 return Encode(jsonTable)
508end
509
510
511
512
513
514
515
516
517------------------------------------------------------------------------------------------------------------------------
518------------------------------------------------------------------------------------------------------------------------
519------------------------------------------------------------------------------------------------------------------------
520--------------------------------------------Terrain Utilities Begin-----------------------------------------------------
521------------------------------------------------------------------------------------------------------------------------
522------------------------------------------------------------------------------------------------------------------------
523------------------------------------------------------------------------------------------------------------------------
524--makes a wedge at location x, y, z
525--sets cell x, y, z to default material if parameter is provided, if not sets cell x, y, z to be whatever material it previously w
526--returns true if made a wedge, false if the cell remains a block
527t.MakeWedge = function(x, y, z, defaultmaterial)
528 return game:GetService("Terrain"):AutoWedgeCell(x,y,z)
529end
530
531t.SelectTerrainRegion = function(regionToSelect, color, selectEmptyCells, selectionParent)
532 local terrain = game:GetService("Workspace"):FindFirstChild("Terrain")
533 if not terrain then return end
534
535 assert(regionToSelect)
536 assert(color)
537
538 if not type(regionToSelect) == "Region3" then
539 error("regionToSelect (first arg), should be of type Region3, but is type",type(regionToSelect))
540 end
541 if not type(color) == "BrickColor" then
542 error("color (second arg), should be of type BrickColor, but is type",type(color))
543 end
544
545 -- frequently used terrain calls (speeds up call, no lookup necessary)
546 local GetCell = terrain.GetCell
547 local WorldToCellPreferSolid = terrain.WorldToCellPreferSolid
548 local CellCenterToWorld = terrain.CellCenterToWorld
549 local emptyMaterial = Enum.CellMaterial.Empty
550
551 -- container for all adornments, passed back to user
552 local selectionContainer = Instance.new("Model")
553 selectionContainer.Name = "SelectionContainer"
554 selectionContainer.Archivable = false
555 if selectionParent then
556 selectionContainer.Parent = selectionParent
557 else
558 selectionContainer.Parent = game:GetService("Workspace")
559 end
560
561 local updateSelection = nil -- function we return to allow user to update selection
562 local currentKeepAliveTag = nil -- a tag that determines whether adorns should be destroyed
563 local aliveCounter = 0 -- helper for currentKeepAliveTag
564 local lastRegion = nil -- used to stop updates that do nothing
565 local adornments = {} -- contains all adornments
566 local reusableAdorns = {}
567
568 local selectionPart = Instance.new("Part")
569 selectionPart.Name = "SelectionPart"
570 selectionPart.Transparency = 1
571 selectionPart.Anchored = true
572 selectionPart.Locked = true
573 selectionPart.CanCollide = false
574 selectionPart.Size = Vector3.new(4.2,4.2,4.2)
575
576 local selectionBox = Instance.new("SelectionBox")
577
578 -- srs translation from region3 to region3int16
579 function Region3ToRegion3int16(region3)
580 local theLowVec = region3.CFrame.p - (region3.Size/2) + Vector3.new(2,2,2)
581 local lowCell = WorldToCellPreferSolid(terrain,theLowVec)
582
583 local theHighVec = region3.CFrame.p + (region3.Size/2) - Vector3.new(2,2,2)
584 local highCell = WorldToCellPreferSolid(terrain, theHighVec)
585
586 local highIntVec = Vector3int16.new(highCell.x,highCell.y,highCell.z)
587 local lowIntVec = Vector3int16.new(lowCell.x,lowCell.y,lowCell.z)
588
589 return Region3int16.new(lowIntVec,highIntVec)
590 end
591
592 -- helper function that creates the basis for a selection box
593 function createAdornment(theColor)
594 local selectionPartClone = nil
595 local selectionBoxClone = nil
596
597 if #reusableAdorns > 0 then
598 selectionPartClone = reusableAdorns[1]["part"]
599 selectionBoxClone = reusableAdorns[1]["box"]
600 table.remove(reusableAdorns,1)
601
602 selectionBoxClone.Visible = true
603 else
604 selectionPartClone = selectionPart:Clone()
605 selectionPartClone.Archivable = false
606
607 selectionBoxClone = selectionBox:Clone()
608 selectionBoxClone.Archivable = false
609
610 selectionBoxClone.Adornee = selectionPartClone
611 selectionBoxClone.Parent = selectionContainer
612
613 selectionBoxClone.Adornee = selectionPartClone
614
615 selectionBoxClone.Parent = selectionContainer
616 end
617
618 if theColor then
619 selectionBoxClone.Color = theColor
620 end
621
622 return selectionPartClone, selectionBoxClone
623 end
624
625 -- iterates through all current adornments and deletes any that don't have latest tag
626 function cleanUpAdornments()
627 for cellPos, adornTable in pairs(adornments) do
628
629 if adornTable.KeepAlive ~= currentKeepAliveTag then -- old news, we should get rid of this
630 adornTable.SelectionBox.Visible = false
631 table.insert(reusableAdorns,{part = adornTable.SelectionPart, box = adornTable.SelectionBox})
632 adornments[cellPos] = nil
633 end
634 end
635 end
636
637 -- helper function to update tag
638 function incrementAliveCounter()
639 aliveCounter = aliveCounter + 1
640 if aliveCounter > 1000000 then
641 aliveCounter = 0
642 end
643 return aliveCounter
644 end
645
646 -- finds full cells in region and adorns each cell with a box, with the argument color
647 function adornFullCellsInRegion(region, color)
648 local regionBegin = region.CFrame.p - (region.Size/2) + Vector3.new(2,2,2)
649 local regionEnd = region.CFrame.p + (region.Size/2) - Vector3.new(2,2,2)
650
651 local cellPosBegin = WorldToCellPreferSolid(terrain, regionBegin)
652 local cellPosEnd = WorldToCellPreferSolid(terrain, regionEnd)
653
654 currentKeepAliveTag = incrementAliveCounter()
655 for y = cellPosBegin.y, cellPosEnd.y do
656 for z = cellPosBegin.z, cellPosEnd.z do
657 for x = cellPosBegin.x, cellPosEnd.x do
658 local cellMaterial = GetCell(terrain, x, y, z)
659
660 if cellMaterial ~= emptyMaterial then
661 local cframePos = CellCenterToWorld(terrain, x, y, z)
662 local cellPos = Vector3int16.new(x,y,z)
663
664 local updated = false
665 for cellPosAdorn, adornTable in pairs(adornments) do
666 if cellPosAdorn == cellPos then
667 adornTable.KeepAlive = currentKeepAliveTag
668 if color then
669 adornTable.SelectionBox.Color = color
670 end
671 updated = true
672 break
673 end
674 end
675
676 if not updated then
677 local selectionPart, selectionBox = createAdornment(color)
678 selectionPart.Size = Vector3.new(4,4,4)
679 selectionPart.CFrame = CFrame.new(cframePos)
680 local adornTable = {SelectionPart = selectionPart, SelectionBox = selectionBox, KeepAlive = currentKeepAliveTag}
681 adornments[cellPos] = adornTable
682 end
683 end
684 end
685 end
686 end
687 cleanUpAdornments()
688 end
689
690
691 ------------------------------------- setup code ------------------------------
692 lastRegion = regionToSelect
693
694 if selectEmptyCells then -- use one big selection to represent the area selected
695 local selectionPart, selectionBox = createAdornment(color)
696
697 selectionPart.Size = regionToSelect.Size
698 selectionPart.CFrame = regionToSelect.CFrame
699
700 adornments.SelectionPart = selectionPart
701 adornments.SelectionBox = selectionBox
702
703 updateSelection =
704 function (newRegion, color)
705 if newRegion and newRegion ~= lastRegion then
706 lastRegion = newRegion
707 selectionPart.Size = newRegion.Size
708 selectionPart.CFrame = newRegion.CFrame
709 end
710 if color then
711 selectionBox.Color = color
712 end
713 end
714 else -- use individual cell adorns to represent the area selected
715 adornFullCellsInRegion(regionToSelect, color)
716 updateSelection =
717 function (newRegion, color)
718 if newRegion and newRegion ~= lastRegion then
719 lastRegion = newRegion
720 adornFullCellsInRegion(newRegion, color)
721 end
722 end
723
724 end
725
726 local destroyFunc = function()
727 updateSelection = nil
728 if selectionContainer then selectionContainer:Destroy() end
729 adornments = nil
730 end
731
732 return updateSelection, destroyFunc
733end
734
735-----------------------------Terrain Utilities End-----------------------------
736
737
738
739
740
741
742
743------------------------------------------------------------------------------------------------------------------------
744------------------------------------------------------------------------------------------------------------------------
745------------------------------------------------------------------------------------------------------------------------
746------------------------------------------------Signal class begin------------------------------------------------------
747------------------------------------------------------------------------------------------------------------------------
748------------------------------------------------------------------------------------------------------------------------
749------------------------------------------------------------------------------------------------------------------------
750--[[
751A 'Signal' object identical to the internal RBXScriptSignal object in it's public API and semantics. This function
752can be used to create "custom events" for user-made code.
753API:
754Method :connect( function handler )
755 Arguments: The function to connect to.
756 Returns: A new connection object which can be used to disconnect the connection
757 Description: Connects this signal to the function specified by |handler|. That is, when |fire( ... )| is called for
758 the signal the |handler| will be called with the arguments given to |fire( ... )|. Note, the functions
759 connected to a signal are called in NO PARTICULAR ORDER, so connecting one function after another does
760 NOT mean that the first will be called before the second as a result of a call to |fire|.
761
762Method :disconnect()
763 Arguments: None
764 Returns: None
765 Description: Disconnects all of the functions connected to this signal.
766
767Method :fire( ... )
768 Arguments: Any arguments are accepted
769 Returns: None
770 Description: Calls all of the currently connected functions with the given arguments.
771
772Method :wait()
773 Arguments: None
774 Returns: The arguments given to fire
775 Description: This call blocks until
776]]
777
778function t.CreateSignal()
779 local this = {}
780
781 local mBindableEvent = Instance.new('BindableEvent')
782 local mAllCns = {} --all connection objects returned by mBindableEvent::connect
783
784 --main functions
785 function this:connect(func)
786 if self ~= this then error("connect must be called with `:`, not `.`", 2) end
787 if type(func) ~= 'function' then
788 error("Argument #1 of connect must be a function, got a "..type(func), 2)
789 end
790 local cn = mBindableEvent.Event:connect(func)
791 mAllCns[cn] = true
792 local pubCn = {}
793 function pubCn:disconnect()
794 cn:disconnect()
795 mAllCns[cn] = nil
796 end
797 return pubCn
798 end
799 function this:disconnect()
800 if self ~= this then error("disconnect must be called with `:`, not `.`", 2) end
801 for cn, _ in pairs(mAllCns) do
802 cn:disconnect()
803 mAllCns[cn] = nil
804 end
805 end
806 function this:wait()
807 if self ~= this then error("wait must be called with `:`, not `.`", 2) end
808 return mBindableEvent.Event:wait()
809 end
810 function this:fire(...)
811 if self ~= this then error("fire must be called with `:`, not `.`", 2) end
812 mBindableEvent:Fire(...)
813 end
814
815 return this
816end
817
818------------------------------------------------- Sigal class End ------------------------------------------------------
819
820
821
822
823------------------------------------------------------------------------------------------------------------------------
824------------------------------------------------------------------------------------------------------------------------
825------------------------------------------------------------------------------------------------------------------------
826-----------------------------------------------Create Function Begins---------------------------------------------------
827------------------------------------------------------------------------------------------------------------------------
828------------------------------------------------------------------------------------------------------------------------
829------------------------------------------------------------------------------------------------------------------------
830--[[
831A "Create" function for easy creation of Roblox instances. The function accepts a string which is the classname of
832the object to be created. The function then returns another function which either accepts accepts no arguments, in
833which case it simply creates an object of the given type, or a table argument that may contain several types of data,
834in which case it mutates the object in varying ways depending on the nature of the aggregate data. These are the
835type of data and what operation each will perform:
8361) A string key mapping to some value:
837 Key-Value pairs in this form will be treated as properties of the object, and will be assigned in NO PARTICULAR
838 ORDER. If the order in which properties is assigned matter, then they must be assigned somewhere else than the
839 |Create| call's body.
840
8412) An integral key mapping to another Instance:
842 Normal numeric keys mapping to Instances will be treated as children if the object being created, and will be
843 parented to it. This allows nice recursive calls to Create to create a whole hierarchy of objects without a
844 need for temporary variables to store references to those objects.
845
8463) A key which is a value returned from Create.Event( eventname ), and a value which is a function function
847 The Create.E( string ) function provides a limited way to connect to signals inside of a Create hierarchy
848 for those who really want such a functionality. The name of the event whose name is passed to
849 Create.E( string )
850
8514) A key which is the Create function itself, and a value which is a function
852 The function will be run with the argument of the object itself after all other initialization of the object is
853 done by create. This provides a way to do arbitrary things involving the object from withing the create
854 hierarchy.
855 Note: This function is called SYNCHRONOUSLY, that means that you should only so initialization in
856 it, not stuff which requires waiting, as the Create call will block until it returns. While waiting in the
857 constructor callback function is possible, it is probably not a good design choice.
858 Note: Since the constructor function is called after all other initialization, a Create block cannot have two
859 constructor functions, as it would not be possible to call both of them last, also, this would be unnecessary.
860
861
862Some example usages:
863
864A simple example which uses the Create function to create a model object and assign two of it's properties.
865local model = Create'Model'{
866 Name = 'A New model',
867 Parent = game.Workspace,
868}
869
870
871An example where a larger hierarchy of object is made. After the call the hierarchy will look like this:
872Model_Container
873 |-ObjectValue
874 | |
875 | `-BoolValueChild
876 `-IntValue
877
878local model = Create'Model'{
879 Name = 'Model_Container',
880 Create'ObjectValue'{
881 Create'BoolValue'{
882 Name = 'BoolValueChild',
883 },
884 },
885 Create'IntValue'{},
886}
887
888
889An example using the event syntax:
890
891local part = Create'Part'{
892 [Create.E'Touched'] = function(part)
893 print("I was touched by "..part.Name)
894 end,
895}
896
897
898An example using the general constructor syntax:
899
900local model = Create'Part'{
901 [Create] = function(this)
902 print("Constructor running!")
903 this.Name = GetGlobalFoosAndBars(this)
904 end,
905}
906
907
908Note: It is also perfectly legal to save a reference to the function returned by a call Create, this will not cause
909 any unexpected behavior. EG:
910 local partCreatingFunction = Create'Part'
911 local part = partCreatingFunction()
912]]
913
914--the Create function need to be created as a functor, not a function, in order to support the Create.E syntax, so it
915--will be created in several steps rather than as a single function declaration.
916local function Create_PrivImpl(objectType)
917 if type(objectType) ~= 'string' then
918 error("Argument of Create must be a string", 2)
919 end
920 --return the proxy function that gives us the nice Create'string'{data} syntax
921 --The first function call is a function call using Lua's single-string-argument syntax
922 --The second function call is using Lua's single-table-argument syntax
923 --Both can be chained together for the nice effect.
924 return function(dat)
925 --default to nothing, to handle the no argument given case
926 dat = dat or {}
927
928 --make the object to mutate
929 local obj = Instance.new(objectType)
930 local parent = nil
931
932 --stored constructor function to be called after other initialization
933 local ctor = nil
934
935 for k, v in pairs(dat) do
936 --add property
937 if type(k) == 'string' then
938 if rbxUtilitySetParentLast and k == 'Parent' then
939 -- Parent should always be set last, setting the Parent of a new object
940 -- immediately makes performance worse for all subsequent property updates.
941 parent = v
942 else
943 obj[k] = v
944 end
945
946
947 --add child
948 elseif type(k) == 'number' then
949 if type(v) ~= 'userdata' then
950 error("Bad entry in Create body: Numeric keys must be paired with children, got a: "..type(v), 2)
951 end
952 v.Parent = obj
953
954
955 --event connect
956 elseif type(k) == 'table' and k.__eventname then
957 if type(v) ~= 'function' then
958 error("Bad entry in Create body: Key `[Create.E\'"..k.__eventname.."\']` must have a function value\
959 got: "..tostring(v), 2)
960 end
961 obj[k.__eventname]:connect(v)
962
963
964 --define constructor function
965 elseif k == t.Create then
966 if type(v) ~= 'function' then
967 error("Bad entry in Create body: Key `[Create]` should be paired with a constructor function, \
968 got: "..tostring(v), 2)
969 elseif ctor then
970 --ctor already exists, only one allowed
971 error("Bad entry in Create body: Only one constructor function is allowed", 2)
972 end
973 ctor = v
974
975
976 else
977 error("Bad entry ("..tostring(k).." => "..tostring(v)..") in Create body", 2)
978 end
979 end
980
981 --apply constructor function if it exists
982 if ctor then
983 ctor(obj)
984 end
985
986 if rbxUtilitySetParentLast and parent then
987 obj.Parent = parent
988 end
989
990 --return the completed object
991 return obj
992 end
993end
994
995--now, create the functor:
996t.Create = setmetatable({}, {__call = function(tb, ...) return Create_PrivImpl(...) end})
997
998--and create the "Event.E" syntax stub. Really it's just a stub to construct a table which our Create
999--function can recognize as special.
1000t.Create.E = function(eventName)
1001 return {__eventname = eventName}
1002end
1003
1004-------------------------------------------------Create function End----------------------------------------------------
1005
1006
1007
1008
1009------------------------------------------------------------------------------------------------------------------------
1010------------------------------------------------------------------------------------------------------------------------
1011------------------------------------------------------------------------------------------------------------------------
1012------------------------------------------------Documentation Begin-----------------------------------------------------
1013------------------------------------------------------------------------------------------------------------------------
1014------------------------------------------------------------------------------------------------------------------------
1015------------------------------------------------------------------------------------------------------------------------
1016
1017t.Help =
1018 function(funcNameOrFunc)
1019 --input argument can be a string or a function. Should return a description (of arguments and expected side effects)
1020 if funcNameOrFunc == "DecodeJSON" or funcNameOrFunc == t.DecodeJSON then
1021 return "Function DecodeJSON. " ..
1022 "Arguments: (string). " ..
1023 "Side effect: returns a table with all parsed JSON values"
1024 end
1025 if funcNameOrFunc == "EncodeJSON" or funcNameOrFunc == t.EncodeJSON then
1026 return "Function EncodeJSON. " ..
1027 "Arguments: (table). " ..
1028 "Side effect: returns a string composed of argument table in JSON data format"
1029 end
1030 if funcNameOrFunc == "MakeWedge" or funcNameOrFunc == t.MakeWedge then
1031 return "Function MakeWedge. " ..
1032 "Arguments: (x, y, z, [default material]). " ..
1033 "Description: Makes a wedge at location x, y, z. Sets cell x, y, z to default material if "..
1034 "parameter is provided, if not sets cell x, y, z to be whatever material it previously was. "..
1035 "Returns true if made a wedge, false if the cell remains a block "
1036 end
1037 if funcNameOrFunc == "SelectTerrainRegion" or funcNameOrFunc == t.SelectTerrainRegion then
1038 return "Function SelectTerrainRegion. " ..
1039 "Arguments: (regionToSelect, color, selectEmptyCells, selectionParent). " ..
1040 "Description: Selects all terrain via a series of selection boxes within the regionToSelect " ..
1041 "(this should be a region3 value). The selection box color is detemined by the color argument " ..
1042 "(should be a brickcolor value). SelectionParent is the parent that the selection model gets placed to (optional)." ..
1043 "SelectEmptyCells is bool, when true will select all cells in the " ..
1044 "region, otherwise we only select non-empty cells. Returns a function that can update the selection," ..
1045 "arguments to said function are a new region3 to select, and the adornment color (color arg is optional). " ..
1046 "Also returns a second function that takes no arguments and destroys the selection"
1047 end
1048 if funcNameOrFunc == "CreateSignal" or funcNameOrFunc == t.CreateSignal then
1049 return "Function CreateSignal. "..
1050 "Arguments: None. "..
1051 "Returns: The newly created Signal object. This object is identical to the RBXScriptSignal class "..
1052 "used for events in Objects, but is a Lua-side object so it can be used to create custom events in"..
1053 "Lua code. "..
1054 "Methods of the Signal object: :connect, :wait, :fire, :disconnect. "..
1055 "For more info you can pass the method name to the Help function, or view the wiki page "..
1056 "for this library. EG: Help('Signal:connect')."
1057 end
1058 if funcNameOrFunc == "Signal:connect" then
1059 return "Method Signal:connect. "..
1060 "Arguments: (function handler). "..
1061 "Return: A connection object which can be used to disconnect the connection to this handler. "..
1062 "Description: Connectes a handler function to this Signal, so that when |fire| is called the "..
1063 "handler function will be called with the arguments passed to |fire|."
1064 end
1065 if funcNameOrFunc == "Signal:wait" then
1066 return "Method Signal:wait. "..
1067 "Arguments: None. "..
1068 "Returns: The arguments passed to the next call to |fire|. "..
1069 "Description: This call does not return until the next call to |fire| is made, at which point it "..
1070 "will return the values which were passed as arguments to that |fire| call."
1071 end
1072 if funcNameOrFunc == "Signal:fire" then
1073 return "Method Signal:fire. "..
1074 "Arguments: Any number of arguments of any type. "..
1075 "Returns: None. "..
1076 "Description: This call will invoke any connected handler functions, and notify any waiting code "..
1077 "attached to this Signal to continue, with the arguments passed to this function. Note: The calls "..
1078 "to handlers are made asynchronously, so this call will return immediately regardless of how long "..
1079 "it takes the connected handler functions to complete."
1080 end
1081 if funcNameOrFunc == "Signal:disconnect" then
1082 return "Method Signal:disconnect. "..
1083 "Arguments: None. "..
1084 "Returns: None. "..
1085 "Description: This call disconnects all handlers attacched to this function, note however, it "..
1086 "does NOT make waiting code continue, as is the behavior of normal Roblox events. This method "..
1087 "can also be called on the connection object which is returned from Signal:connect to only "..
1088 "disconnect a single handler, as opposed to this method, which will disconnect all handlers."
1089 end
1090 if funcNameOrFunc == "Create" then
1091 return "Function Create. "..
1092 "Arguments: A table containing information about how to construct a collection of objects. "..
1093 "Returns: The constructed objects. "..
1094 "Descrition: Create is a very powerfull function, whose description is too long to fit here, and "..
1095 "is best described via example, please see the wiki page for a description of how to use it."
1096 end
1097 end
1098
1099--------------------------------------------Documentation Ends----------------------------------------------------------
1100
1101return t