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