· 5 years ago · Apr 19, 2020, 05:08 AM
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|.
758Method :disconnect()
759 Arguments: None
760 Returns: None
761 Description: Disconnects all of the functions connected to this signal.
762Method :fire( ... )
763 Arguments: Any arguments are accepted
764 Returns: None
765 Description: Calls all of the currently connected functions with the given arguments.
766Method :wait()
767 Arguments: None
768 Returns: The arguments given to fire
769 Description: This call blocks until
770]]
771
772function t.CreateSignal()
773 local this = {}
774
775 local mBindableEvent = Instance.new('BindableEvent')
776 local mAllCns = {} --all connection objects returned by mBindableEvent::connect
777
778 --main functions
779 function this:connect(func)
780 if self ~= this then error("connect must be called with `:`, not `.`", 2) end
781 if type(func) ~= 'function' then
782 error("Argument #1 of connect must be a function, got a "..type(func), 2)
783 end
784 local cn = mBindableEvent.Event:Connect(func)
785 mAllCns[cn] = true
786 local pubCn = {}
787 function pubCn:disconnect()
788 cn:Disconnect()
789 mAllCns[cn] = nil
790 end
791 pubCn.Disconnect = pubCn.disconnect
792
793 return pubCn
794 end
795
796 function this:disconnect()
797 if self ~= this then error("disconnect must be called with `:`, not `.`", 2) end
798 for cn, _ in pairs(mAllCns) do
799 cn:Disconnect()
800 mAllCns[cn] = nil
801 end
802 end
803
804 function this:wait()
805 if self ~= this then error("wait must be called with `:`, not `.`", 2) end
806 return mBindableEvent.Event:Wait()
807 end
808
809 function this:fire(...)
810 if self ~= this then error("fire must be called with `:`, not `.`", 2) end
811 mBindableEvent:Fire(...)
812 end
813
814 this.Connect = this.connect
815 this.Disconnect = this.disconnect
816 this.Wait = this.wait
817 this.Fire = this.fire
818
819 return this
820end
821
822------------------------------------------------- Sigal class End ------------------------------------------------------
823
824
825
826
827------------------------------------------------------------------------------------------------------------------------
828------------------------------------------------------------------------------------------------------------------------
829------------------------------------------------------------------------------------------------------------------------
830-----------------------------------------------Create Function Begins---------------------------------------------------
831------------------------------------------------------------------------------------------------------------------------
832------------------------------------------------------------------------------------------------------------------------
833------------------------------------------------------------------------------------------------------------------------
834--[[
835A "Create" function for easy creation of Roblox instances. The function accepts a string which is the classname of
836the object to be created. The function then returns another function which either accepts accepts no arguments, in
837which case it simply creates an object of the given type, or a table argument that may contain several types of data,
838in which case it mutates the object in varying ways depending on the nature of the aggregate data. These are the
839type of data and what operation each will perform:
8401) A string key mapping to some value:
841 Key-Value pairs in this form will be treated as properties of the object, and will be assigned in NO PARTICULAR
842 ORDER. If the order in which properties is assigned matter, then they must be assigned somewhere else than the
843 |Create| call's body.
8442) An integral key mapping to another Instance:
845 Normal numeric keys mapping to Instances will be treated as children if the object being created, and will be
846 parented to it. This allows nice recursive calls to Create to create a whole hierarchy of objects without a
847 need for temporary variables to store references to those objects.
8483) A key which is a value returned from Create.Event( eventname ), and a value which is a function function
849 The Create.E( string ) function provides a limited way to connect to signals inside of a Create hierarchy
850 for those who really want such a functionality. The name of the event whose name is passed to
851 Create.E( string )
8524) A key which is the Create function itself, and a value which is a function
853 The function will be run with the argument of the object itself after all other initialization of the object is
854 done by create. This provides a way to do arbitrary things involving the object from withing the create
855 hierarchy.
856 Note: This function is called SYNCHRONOUSLY, that means that you should only so initialization in
857 it, not stuff which requires waiting, as the Create call will block until it returns. While waiting in the
858 constructor callback function is possible, it is probably not a good design choice.
859 Note: Since the constructor function is called after all other initialization, a Create block cannot have two
860 constructor functions, as it would not be possible to call both of them last, also, this would be unnecessary.
861Some example usages:
862A simple example which uses the Create function to create a model object and assign two of it's properties.
863local model = Create'Model'{
864 Name = 'A New model',
865 Parent = game.Workspace,
866}
867An example where a larger hierarchy of object is made. After the call the hierarchy will look like this:
868Model_Container
869 |-ObjectValue
870 | |
871 | `-BoolValueChild
872 `-IntValue
873local model = Create'Model'{
874 Name = 'Model_Container',
875 Create'ObjectValue'{
876 Create'BoolValue'{
877 Name = 'BoolValueChild',
878 },
879 },
880 Create'IntValue'{},
881}
882An example using the event syntax:
883local part = Create'Part'{
884 [Create.E'Touched'] = function(part)
885 print("I was touched by "..part.Name)
886 end,
887}
888An example using the general constructor syntax:
889local model = Create'Part'{
890 [Create] = function(this)
891 print("Constructor running!")
892 this.Name = GetGlobalFoosAndBars(this)
893 end,
894}
895Note: It is also perfectly legal to save a reference to the function returned by a call Create, this will not cause
896 any unexpected behavior. EG:
897 local partCreatingFunction = Create'Part'
898 local part = partCreatingFunction()
899]]
900
901--the Create function need to be created as a functor, not a function, in order to support the Create.E syntax, so it
902--will be created in several steps rather than as a single function declaration.
903local function Create_PrivImpl(objectType)
904 if type(objectType) ~= 'string' then
905 error("Argument of Create must be a string", 2)
906 end
907 --return the proxy function that gives us the nice Create'string'{data} syntax
908 --The first function call is a function call using Lua's single-string-argument syntax
909 --The second function call is using Lua's single-table-argument syntax
910 --Both can be chained together for the nice effect.
911 return function(dat)
912 --default to nothing, to handle the no argument given case
913 dat = dat or {}
914
915 --make the object to mutate
916 local obj = Instance.new(objectType)
917 local parent = nil
918
919 --stored constructor function to be called after other initialization
920 local ctor = nil
921
922 for k, v in pairs(dat) do
923 --add property
924 if type(k) == 'string' then
925 if k == 'Parent' then
926 -- Parent should always be set last, setting the Parent of a new object
927 -- immediately makes performance worse for all subsequent property updates.
928 parent = v
929 else
930 obj[k] = v
931 end
932
933
934 --add child
935 elseif type(k) == 'number' then
936 if type(v) ~= 'userdata' then
937 error("Bad entry in Create body: Numeric keys must be paired with children, got a: "..type(v), 2)
938 end
939 v.Parent = obj
940
941
942 --event connect
943 elseif type(k) == 'table' and k.__eventname then
944 if type(v) ~= 'function' then
945 error("Bad entry in Create body: Key `[Create.E\'"..k.__eventname.."\']` must have a function value\
946 got: "..tostring(v), 2)
947 end
948 obj[k.__eventname]:connect(v)
949
950
951 --define constructor function
952 elseif k == t.Create then
953 if type(v) ~= 'function' then
954 error("Bad entry in Create body: Key `[Create]` should be paired with a constructor function, \
955 got: "..tostring(v), 2)
956 elseif ctor then
957 --ctor already exists, only one allowed
958 error("Bad entry in Create body: Only one constructor function is allowed", 2)
959 end
960 ctor = v
961
962
963 else
964 error("Bad entry ("..tostring(k).." => "..tostring(v)..") in Create body", 2)
965 end
966 end
967
968 --apply constructor function if it exists
969 if ctor then
970 ctor(obj)
971 end
972
973 if parent then
974 obj.Parent = parent
975 end
976
977 --return the completed object
978 return obj
979 end
980end
981
982--now, create the functor:
983t.Create = setmetatable({}, {__call = function(tb, ...) return Create_PrivImpl(...) end})
984
985--and create the "Event.E" syntax stub. Really it's just a stub to construct a table which our Create
986--function can recognize as special.
987t.Create.E = function(eventName)
988 return {__eventname = eventName}
989end
990
991-------------------------------------------------Create function End----------------------------------------------------
992
993
994
995
996------------------------------------------------------------------------------------------------------------------------
997------------------------------------------------------------------------------------------------------------------------
998------------------------------------------------------------------------------------------------------------------------
999------------------------------------------------Documentation Begin-----------------------------------------------------
1000------------------------------------------------------------------------------------------------------------------------
1001------------------------------------------------------------------------------------------------------------------------
1002------------------------------------------------------------------------------------------------------------------------
1003
1004t.Help =
1005 function(funcNameOrFunc)
1006 --input argument can be a string or a function. Should return a description (of arguments and expected side effects)
1007 if funcNameOrFunc == "DecodeJSON" or funcNameOrFunc == t.DecodeJSON then
1008 return "Function DecodeJSON. " ..
1009 "Arguments: (string). " ..
1010 "Side effect: returns a table with all parsed JSON values"
1011 end
1012 if funcNameOrFunc == "EncodeJSON" or funcNameOrFunc == t.EncodeJSON then
1013 return "Function EncodeJSON. " ..
1014 "Arguments: (table). " ..
1015 "Side effect: returns a string composed of argument table in JSON data format"
1016 end
1017 if funcNameOrFunc == "MakeWedge" or funcNameOrFunc == t.MakeWedge then
1018 return "Function MakeWedge. " ..
1019 "Arguments: (x, y, z, [default material]). " ..
1020 "Description: Makes a wedge at location x, y, z. Sets cell x, y, z to default material if "..
1021 "parameter is provided, if not sets cell x, y, z to be whatever material it previously was. "..
1022 "Returns true if made a wedge, false if the cell remains a block "
1023 end
1024 if funcNameOrFunc == "SelectTerrainRegion" or funcNameOrFunc == t.SelectTerrainRegion then
1025 return "Function SelectTerrainRegion. " ..
1026 "Arguments: (regionToSelect, color, selectEmptyCells, selectionParent). " ..
1027 "Description: Selects all terrain via a series of selection boxes within the regionToSelect " ..
1028 "(this should be a region3 value). The selection box color is detemined by the color argument " ..
1029 "(should be a brickcolor value). SelectionParent is the parent that the selection model gets placed to (optional)." ..
1030 "SelectEmptyCells is bool, when true will select all cells in the " ..
1031 "region, otherwise we only select non-empty cells. Returns a function that can update the selection," ..
1032 "arguments to said function are a new region3 to select, and the adornment color (color arg is optional). " ..
1033 "Also returns a second function that takes no arguments and destroys the selection"
1034 end
1035 if funcNameOrFunc == "CreateSignal" or funcNameOrFunc == t.CreateSignal then
1036 return "Function CreateSignal. "..
1037 "Arguments: None. "..
1038 "Returns: The newly created Signal object. This object is identical to the RBXScriptSignal class "..
1039 "used for events in Objects, but is a Lua-side object so it can be used to create custom events in"..
1040 "Lua code. "..
1041 "Methods of the Signal object: :connect, :wait, :fire, :disconnect. "..
1042 "For more info you can pass the method name to the Help function, or view the wiki page "..
1043 "for this library. EG: Help('Signal:connect')."
1044 end
1045 if funcNameOrFunc == "Signal:connect" then
1046 return "Method Signal:connect. "..
1047 "Arguments: (function handler). "..
1048 "Return: A connection object which can be used to disconnect the connection to this handler. "..
1049 "Description: Connectes a handler function to this Signal, so that when |fire| is called the "..
1050 "handler function will be called with the arguments passed to |fire|."
1051 end
1052 if funcNameOrFunc == "Signal:wait" then
1053 return "Method Signal:wait. "..
1054 "Arguments: None. "..
1055 "Returns: The arguments passed to the next call to |fire|. "..
1056 "Description: This call does not return until the next call to |fire| is made, at which point it "..
1057 "will return the values which were passed as arguments to that |fire| call."
1058 end
1059 if funcNameOrFunc == "Signal:fire" then
1060 return "Method Signal:fire. "..
1061 "Arguments: Any number of arguments of any type. "..
1062 "Returns: None. "..
1063 "Description: This call will invoke any connected handler functions, and notify any waiting code "..
1064 "attached to this Signal to continue, with the arguments passed to this function. Note: The calls "..
1065 "to handlers are made asynchronously, so this call will return immediately regardless of how long "..
1066 "it takes the connected handler functions to complete."
1067 end
1068 if funcNameOrFunc == "Signal:disconnect" then
1069 return "Method Signal:disconnect. "..
1070 "Arguments: None. "..
1071 "Returns: None. "..
1072 "Description: This call disconnects all handlers attacched to this function, note however, it "..
1073 "does NOT make waiting code continue, as is the behavior of normal Roblox events. This method "..
1074 "can also be called on the connection object which is returned from Signal:connect to only "..
1075 "disconnect a single handler, as opposed to this method, which will disconnect all handlers."
1076 end
1077 if funcNameOrFunc == "Create" then
1078 return "Function Create. "..
1079 "Arguments: A table containing information about how to construct a collection of objects. "..
1080 "Returns: The constructed objects. "..
1081 "Descrition: Create is a very powerfull function, whose description is too long to fit here, and "..
1082 "is best described via example, please see the wiki page for a description of how to use it."
1083 end
1084 end
1085
1086--------------------------------------------Documentation Ends----------------------------------------------------------
1087
1088return t