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