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