· 8 years ago · Aug 17, 2018, 04:22 PM
1local ckRequire = require(script.Parent.ckRequire)
2
3_G.ckClassProvider = {}
4
5_G.ckClassProvider["com.codekingdoms.roblox.lib.Colors"] = function()
6-- AUTO-GENERATED by RobloxApiExporter
7local Colors = {
8 MAROON = Color3.new(0.5019607843137255, 0, 0),
9 RED = Color3.new(1, 0, 0),
10 ORANGE = Color3.new(1, 0.6470588235294118, 0),
11 YELLOW = Color3.new(1, 1, 0),
12 OLIVE = Color3.new(0.5019607843137255, 0.5019607843137255, 0),
13 GREEN = Color3.new(0, 0.5019607843137255, 0),
14 PURPLE = Color3.new(0.5019607843137255, 0, 0.5019607843137255),
15 FUCHSIA = Color3.new(1, 0, 1),
16 LIME = Color3.new(0, 1, 0),
17 TEAL = Color3.new(0, 0.5019607843137255, 0.5019607843137255),
18 AQUA = Color3.new(0, 1, 1),
19 BLUE = Color3.new(0, 0, 1),
20 NAVY = Color3.new(0, 0, 0.5019607843137255),
21 BLACK = Color3.new(0, 0, 0),
22 GRAY = Color3.new(0.5019607843137255, 0.5019607843137255, 0.5019607843137255),
23 SILVER = Color3.new(0.7529411764705882, 0.7529411764705882, 0.7529411764705882),
24 WHITE = Color3.new(1, 1, 1)
25}
26
27return Colors
28end
29
30_G.ckClassProvider["com.codekingdoms.roblox.base.BaseLocalObject"] = function()
31local BaseObject = ckRequire("com.codekingdoms.roblox.base.BaseObject")
32local BaseLocalObject = BaseObject:Extend(function( self, project, object )
33
34 BaseObject.init( self, project, object )
35 self._isLocal = true
36
37end)
38
39--- Make this object listen to the game's render loop
40-- @category Ui
41function BaseLocalObject:EnableRender()
42
43 local listener = function()
44
45 if self.OnRender and self.alive then
46
47 self:Run(self.OnRender)
48
49 end
50
51 end
52
53 game:GetService('RunService'):BindToRenderStep('Camera', Enum.RenderPriority.Camera.Value, listener)
54
55end
56
57-- @category Events
58-- @abstract
59function BaseLocalObject:OnRender()
60
61end
62
63--- Runs when a player hits the object
64-- @category Events
65-- @param {Local(com.codekingdoms.roblox.base.BaseLocalPlayer)} player
66-- @param {roblox.class.BasePart} [objectPart] The part of the object which is being touched by the player
67-- @abstract
68function BaseLocalObject:OnTouched( player, objectPart )
69
70end
71
72return BaseLocalObject
73end
74
75_G.ckClassProvider["space.codekingdoms.lovelyrat04.zombiesmash.PlayerCamera"] = function()
76local BaseLocalPlayer = ckRequire("com.codekingdoms.roblox.base.BaseLocalPlayer")
77local PlayerCamera = BaseLocalPlayer:Extend()
78
79
80
81
82return PlayerCamera
83end
84
85_G.ckClassProvider["com.codekingdoms.roblox.private.splashGui"] = function()
86local splashGui = {}
87
88function splashGui:remove( name )
89
90 local parent = splashGui:parent()
91
92 if ( parent ~= nil ) then
93
94 local child = parent:FindFirstChild(name)
95
96 if ( child ~= nil ) then
97
98 child:Destroy()
99 end
100 end
101
102end
103
104function splashGui:parent()
105
106 return game.StarterGui:FindFirstChild("ScreenGui")
107end
108
109function splashGui:create( name, text )
110
111 local splash = Instance.new("TextLabel")
112 splash.Parent = splashGui:parent()
113
114 local textColor = Color3.new(1,1,1)
115 local backgroundColor = Color3.new(0.4, 0.4, 0.7)
116
117 splash.Name = name
118 splash.Text = text
119 splash.TextColor3 = textColor
120 splash.BackgroundTransparency = 0
121 splash.BackgroundColor3 = backgroundColor
122 splash.Size = UDim2.new( 1, 0, 1, 0 )
123 splash.TextSize = 18.000
124
125 return splash
126
127end
128
129return splashGui
130
131end
132
133_G.ckClassProvider["com.codekingdoms.roblox.lib.Button"] = function()
134local class = ckRequire("com.codekingdoms.roblox.private.class")
135local baseLocalPlayer = ckRequire("com.codekingdoms.roblox.base.BaseLocalPlayer")
136local Button = class( function( self, name )
137
138 self.isPressed = false
139 self.name = name
140 self.localPlayerScripts = _G.projectInstance:findLocalPlayerScripts()
141
142 end)
143
144-- @category Input
145function Button:BeginPress()
146 self.isPressed = true
147
148 for _,script in pairs(self.localPlayerScripts) do
149
150 script:OnButtonPress(self.name)
151
152 end
153
154end
155
156-- @category Input
157function Button:EndPress()
158
159 self.isPressed = false
160
161end
162
163return Button
164end
165
166_G.ckClassProvider["com.codekingdoms.roblox.private.objectWalker"] = function()
167local objectWalker = {}
168
169function objectWalker:walk( obj, handler, path )
170
171 local output = {
172 type = obj.ClassName,
173 name = obj.Name,
174 path = path
175 }
176
177 self:walkChildren(obj, output, handler)
178
179 return handler(obj, output)
180
181end
182
183function objectWalker:walkChildren( obj, output, handler )
184
185 output.children = {}
186
187 for i,v in pairs( obj:GetChildren() ) do
188
189 local childPath
190 if ( output.path ~= nil ) then
191
192 childPath = output.path .. '.' .. v.Name
193
194 else
195
196 childPath = v.Name
197
198 end
199
200 output.children[i] = self:walk( v, handler, childPath )
201
202 end
203
204end
205
206return objectWalker
207
208end
209
210_G.ckClassProvider["com.codekingdoms.roblox.private.class"] = function()
211function class(base, init)
212 local c = {} -- a new class instance
213 if not init and type(base) == 'function' then
214 init = base
215 base = nil
216 elseif type(base) == 'table' then
217 -- our new class is a shallow copy of the base class!
218 for i,v in pairs(base) do
219 c[i] = v
220 end
221 c._base = base
222 end
223 -- the class will be the metatable for all its objects,
224 -- and they will look up their methods in it.
225 function c:__index( key )
226
227 if (string.sub(key, 0, 1) == '_') then
228
229 return rawget(self, key) or c[ key ]
230
231 end
232
233 if (rawget(self, '_properties') == nil) then
234
235 self._properties = {}
236
237 end
238
239 if ( self._properties[key] ) then
240
241 return self._properties[key].getter()
242
243 end
244
245 return rawget(self, key) or c[key]
246
247 end
248
249 function c:__newindex( key, value )
250
251 if (string.sub(key, 0, 1) == '_') then
252
253 return rawset(self, key, value)
254
255 end
256
257 if (rawget(self, '_properties') == nil) then
258
259 self._properties = {}
260
261 end
262
263 if ( self._properties[key] ) then
264
265 return self._properties[key].setter( value )
266
267 end
268
269 return rawset(self, key, value)
270
271 end
272
273 -- expose a constructor which can be called by <classname>(<args>)
274 local mt = {}
275 mt.__call = function(class_tbl, ...)
276 local obj = {}
277 setmetatable(obj,c)
278 if init then
279 init(obj,...)
280 else
281 -- make sure that any stuff from the base class is initialized!
282 if base and base.init then
283 base.init(obj, ...)
284 end
285 end
286 return obj
287 end
288 function c:Extend( extendInit )
289 return class(self, extendInit or self.init)
290 end
291 c.init = init
292 c.IsA = function(self, klass)
293 local m = getmetatable(self)
294 while m do
295 if m == klass then return true end
296 m = m._base
297 end
298 return false
299 end
300 setmetatable(c, mt)
301 return c
302end
303
304return class
305end
306
307_G.ckClassProvider["com.codekingdoms.roblox.private.CodeFactory"] = function()
308local objectWalker = ckRequire("com.codekingdoms.roblox.private.objectWalker")
309local logger = ckRequire("com.codekingdoms.roblox.private.logger")
310local Project = ckRequire("com.codekingdoms.roblox.private.Project")
311local Strings = ckRequire("com.codekingdoms.roblox.lib.Strings")
312
313local HttpService = game.HttpService
314local RunService = game:GetService('RunService')
315
316local CodeFactory = class(function( self )
317
318 local gameData = game.ReplicatedStorage:WaitForChild("CodeKingdoms"):WaitForChild("GameData").Value
319 self.ck = HttpService:JSONDecode( gameData )
320 self.projectInstance = Project.new( self.ck, self )
321 _G.projectInstance = self.projectInstance
322
323end)
324
325function CodeFactory:start()
326
327 if not self.ck.projectData then
328
329 logger:warn("Not running project as it hasn't synced")
330 return
331
332 end
333
334 logger:log("Running project " .. self.ck.projectData.name)
335
336 _G.inspect = ckRequire("com.codekingdoms.roblox.private.inspect")
337
338 local scriptInstances = self:instantiateChildren(game.Workspace)
339
340 -- Only run OnCreate (and OnSpawn) for workspace instances.
341 for _, instance in ipairs( scriptInstances ) do
342
343 instance:Run(instance.OnCreate)
344
345 end
346
347 for _, instance in ipairs( scriptInstances ) do
348
349 if instance._InitLifeCycle then
350
351 instance:Run(instance._InitLifeCycle)
352
353 end
354
355 end
356
357 if ( game:FindFirstChild("ServerStorage") ) then
358
359 self:instantiateChildren( game.ServerStorage )
360
361 end
362
363 local gameStorage = game.ReplicatedStorage:FindFirstChild('GameStorage')
364
365 if gameStorage then
366
367 self:instantiateChildren( gameStorage )
368
369 end
370
371 local needDefaultPlayer = true
372 local playerClasses = {}
373 local toolClasses = {}
374 local uiClasses = {}
375
376 for _, file in pairs(self.projectInstance._info.files) do
377
378 if ( RunService:IsServer() and file.superclass == "BasePlayer" ) or
379 ( RunService:IsClient() and file.superclass == "BaseLocalPlayer" ) then
380
381 playerClasses[file.path] = self:requireScriptForPath( file.path )
382
383 if ( #Strings.Split(file.path, '/' ) < 2 ) then
384
385 needDefaultPlayer = false
386
387 end
388
389 elseif ( RunService:IsServer() and file.superclass == "BaseTool" ) or
390 ( RunService:IsClient() and file.superclass == "BaseLocalTool" ) then
391
392 toolClasses[file.path] = self:requireScriptForPath( file.path )
393
394 elseif ( RunService:IsClient() and file.superclass == "BaseLocalUi" ) then
395
396 uiClasses[file.path] = self:requireScriptForPath( file.path )
397
398 end
399
400 end
401
402 if ( needDefaultPlayer ) then
403
404 if RunService:IsServer() then
405
406 -- Project paths which can refer to an imported API file do so without a forward slash
407 playerClasses["BasePlayer"] = ckRequire("com.codekingdoms.roblox.base.BasePlayer")
408
409 self.projectInstance:AddEvent("FireProjectileEvent")
410 self.projectInstance:ListenToEvent("FireProjectileEvent", function( client, classpath, speed, startPosition, position )
411
412 local lookVector = position - startPosition
413 lookVector = lookVector / lookVector.magnitude
414
415 local gameStorage = game.ReplicatedStorage:FindFirstChild("GameStorage") or game.ServerStorage
416
417 local allClassScripts = _G.projectInstance:findScriptsForClassPath( classpath, true )
418 local scripts = _G.projectInstance:filterScriptsForObject( allClassScripts, gameStorage, true, true )
419 local storedProjectile = scripts[1]
420
421 if ( storedProjectile == nil ) then
422
423 print("Can't fire a missing projectile!", classpath)
424 return
425
426 end
427
428 local offsetDistance = 5.0
429 local projectilePosition = startPosition + lookVector * offsetDistance
430 local projectile = storedProjectile:Clone(projectilePosition)
431 projectile:PushTowards( position, speed )
432
433 end)
434
435 end
436
437 if RunService:IsClient() then
438
439 playerClasses["BaseLocalPlayer"] = ckRequire("com.codekingdoms.roblox.base.BaseLocalPlayer")
440
441 end
442
443 end
444
445 self:instantiateScriptsForPlayers(playerClasses, toolClasses, uiClasses)
446
447end
448
449function CodeFactory:isFileLocal(files, file)
450
451 -- If the file extends a user class then we need to check recursively
452 if ( file.superclass:find('/', 1, true) ) then
453
454 -- Find the user file object from the superclass
455 for _,otherFile in pairs(files) do
456
457 if ( otherFile.path == file.superclass ) then
458
459 return self:isFileLocal(files, otherFile)
460
461 end
462
463 end
464
465 return false
466
467 else
468
469 return file.superclass:find('Local', 1, true) ~= nil
470
471 end
472
473end
474
475function CodeFactory:instantiateChildren( root )
476
477 local localScriptPaths = {}
478
479 for _, file in ipairs(self.ck.projectData.files) do
480
481 local isLocal = self:isFileLocal(self.ck.projectData.files, file)
482
483 if isLocal then
484
485 localScriptPaths[file.path] = true
486
487 end
488
489 end
490
491 local scriptInstances = {}
492
493 local visitor = function( node, _ )
494
495 for _, child in pairs( node:GetChildren() ) do
496
497 if (child.ClassName == "ModuleScript" and child:FindFirstChild("FilePath")) then
498
499 local path = child.FilePath.Value
500 local isLocal = localScriptPaths[ path ]
501
502 local include = (isLocal and RunService:IsClient()) or (not isLocal and RunService:IsServer() )
503
504 -- Only include scripts if they are in the correct client/server domain
505 -- Running in studio is both a client and server, so we need all the scripts
506 if ( include ) then
507
508 local scriptInstance = self:instantiateScript(self:requireScriptForPath(path), path, node)
509 table.insert( scriptInstances, scriptInstance )
510
511 else
512
513 print("CK - Skipping script", path, "as it doesn't run here")
514
515 end
516
517 end
518
519 end
520
521 end
522
523 objectWalker:walk( root, visitor )
524 return scriptInstances
525
526end
527
528function CodeFactory:instantiateScriptsForPlayers(playerClasses, toolClasses, uiClasses)
529
530 for _, player in pairs(game.Players:getPlayers()) do
531
532 self:instantiateScriptsForPlayer( player, uiClasses, toolClasses, playerClasses )
533
534 end
535
536 game.Players.PlayerAdded:connect(function(player)
537
538 self:instantiateScriptsForPlayer( player, uiClasses, toolClasses, playerClasses )
539
540 end)
541
542 game.Players.PlayerRemoving:connect(function(player)
543
544 self:destroyScriptInstancesOfPlayer(playerClasses, player)
545 self:destroyScriptInstancesOfPlayerTools(toolClasses, player)
546 self:destroyScriptInstancesOfPlayerUi(uiClasses, player)
547
548 end)
549
550end
551
552function CodeFactory:instantiateScriptsForPlayer( player, uiClasses, toolClasses, playerClasses )
553
554 -- Only run local player scripts for the LocalPlayer currently on the client
555 if RunService:IsClient() and player ~= game.Players.LocalPlayer then
556 return
557 end
558
559 self:instantiatePlayerScriptsForPlayer(playerClasses, player)
560
561 player.CharacterAdded:connect( function()
562
563 self:instantiateUiScriptsForPlayer(uiClasses, player)
564 self:instantiateToolScriptsForPlayer(toolClasses, player)
565
566 end )
567
568end
569
570function CodeFactory:instantiatePlayerScriptsForPlayer( playerClasses, player )
571
572 for path, Class in pairs(playerClasses) do
573
574 local scriptInstance = self:instantiateScript( Class, path, player )
575 scriptInstance:Run( scriptInstance.OnCreate )
576
577 end
578
579end
580
581function CodeFactory:instantiateUiScriptsForPlayer( uiClasses, player )
582
583 coroutine.wrap(function()
584
585 local visitor = function( node, _ )
586
587 if ( node == nil ) then
588
589 return
590
591 end
592
593 for _, child in pairs( node:GetChildren() ) do
594
595 if (child.ClassName == "ModuleScript" and child:FindFirstChild("FilePath")) then
596
597 local path = child.FilePath.Value
598 local scriptInstance = self:instantiateScript( uiClasses[ path ], path, node )
599 scriptInstance:Run( scriptInstance.OnCreate )
600
601 end
602
603 end
604
605 end
606
607 -- TODO [BLOX-906] Work out a better way to wait for the correct UI objects to become available
608 player:WaitForChild("PlayerGui", 5)
609 player.PlayerGui:WaitForChild("ScreenGui", 5)
610
611 objectWalker:walk( player.PlayerGui, visitor )
612
613 end)()
614
615end
616
617-- Wait until the player's backpack has been created and then create instances
618-- for each tool script in the player's starting items.
619function CodeFactory:instantiateToolScriptsForPlayer( toolClasses, player )
620
621 coroutine.wrap(function()
622
623 -- If the node is a tool with an attached module script that has a file path mapping to
624 -- a class, create an instance of the class for the tool.
625 local toolInstantiator = function (node, _)
626
627 if node.ClassName == "Tool" then
628
629 for _, child in pairs( node:GetChildren() ) do
630
631 if child.ClassName == "ModuleScript" and child:FindFirstChild("FilePath") then
632
633 local path = child.FilePath.Value
634 local Class = toolClasses[path]
635
636 if Class then
637
638 local scriptInstance = self:instantiateScript( Class, path, node )
639 scriptInstance:Run( scriptInstance.OnCreate )
640
641 end
642
643 end
644
645 end
646
647 end
648
649 end
650
651 local backpack = player:WaitForChild("Backpack")
652
653 objectWalker:walk( backpack, toolInstantiator )
654
655 end)()
656
657end
658
659function CodeFactory:instantiateScript( Class, path, object )
660
661 local scriptInstance = Class.new(self.projectInstance, object)
662 scriptInstance._path = path
663 scriptInstance._classpath = Class._classpath
664
665 self.projectInstance:addScript( scriptInstance )
666
667 return scriptInstance
668
669end
670
671function CodeFactory:destroyScriptInstancesOfPlayer( paths, player )
672
673 -- TODO
674
675 -- David: I'm commenting this out as Project doesn't have FindScript and the method
676 -- of that name in Base doesn't accept a path parameter.
677
678 -- for path, _ in pairs(paths) do
679
680 -- self.projectInstance:FindScript( path, player ):Destroy()
681
682 -- end
683
684end
685
686function CodeFactory:destroyScriptInstancesOfPlayerTools( paths, player )
687
688 -- TODO
689
690 -- local backpack = player.FindFirstChild("Backpack")
691
692 -- if backpack then
693
694 -- -- Find script instances of the passed in paths and run the following
695 -- self.projectInstance:removeScript( scriptInstance )
696 -- scriptInstance:Destroy()
697
698 -- end
699
700end
701
702function CodeFactory:destroyScriptInstancesOfPlayerUi( paths, player )
703
704 -- TODO
705
706end
707
708function CodeFactory:requireScriptForPath( path )
709
710 if ( path == nil ) then
711
712 logger:warn("Missing requires")
713 return {
714 missing = true
715 }
716
717 end
718
719 local qualifiedName = self.projectInstance:getQualifiedName( path )
720
721 return ckRequire( qualifiedName )
722
723end
724
725return CodeFactory
726end
727
728_G.ckClassProvider["com.codekingdoms.roblox.base.BaseLocalTool"] = function()
729local BaseTool = ckRequire("com.codekingdoms.roblox.base.BaseTool")
730local logger = ckRequire("com.codekingdoms.roblox.private.logger")
731
732local BaseLocalTool = BaseTool:Extend(function( self, project, object )
733
734 BaseTool.init( self, project, object )
735 self._isLocal = true
736
737end)
738
739-- @protected
740function BaseLocalTool:_addFields()
741 BaseTool._addFields(self)
742
743 self.target = nil
744
745 self:_defineProperty('target', function()
746
747 -- Assume that an equipped tool is always nested directly underneath
748 -- the player's model, which should have a Humanoid part.
749 local mouse = self.owner and self.owner:GetMouse()
750
751 if mouse then
752
753 return mouse.Target
754
755 end
756
757 logger:warn("target is undefined as the tool does not have an owner with a mouse")
758
759 end, function( _ )
760
761 logger:warn("BaseTool target is read-only")
762
763 end)
764
765
766end
767
768--- Fire a stored projectile in the direction this object is facing
769-- @category Actions
770-- @generic {com.codekingdoms.roblox.base.BaseObject} T
771-- @param {Type(T)} ProjectileClass The class of the stored projectile script to use
772-- @param {Number} speed The speed to fire the new projectile at
773-- @returns {T} The new projectile
774function BaseLocalTool:Fire(projectileClass, speed)
775
776 self._project:FireEvent("FireProjectileEvent", projectileClass._classpath, speed, self.position, self.targetPoint)
777
778end
779
780-- @protected
781function BaseLocalTool:_bindEvents()
782
783 self.object.Activated:connect(function()
784
785 self:Run( self.OnActivate, self.target )
786
787 end)
788 self.object.Deactivated:connect(function()
789
790 self:Run( self.OnDeactivate )
791
792 end)
793 self.object.Equipped:connect(function()
794
795 self:Run( self.OnEquip )
796
797 end)
798 self.object.Unequipped:connect(function()
799
800 self:Run( self.OnUnequip )
801
802 end)
803
804end
805
806-- @category Events
807-- @param {roblox.class.BasePart} [target] The target that the tool was pointing at when activated.
808-- @abstract
809function BaseLocalTool:OnActivate( target )
810
811end
812
813return BaseLocalTool
814
815end
816
817_G.ckClassProvider["space.codekingdoms.lovelyrat04.zombiesmash.Bullet"] = function()
818local BaseLocalObject = ckRequire("com.codekingdoms.roblox.base.BaseLocalObject")
819local Bullet = BaseLocalObject:Extend()
820
821
822
823
824return Bullet
825end
826
827_G.ckClassProvider["com.codekingdoms.roblox.base.BaseLocalPlayer"] = function()
828local BasePlayer = ckRequire("com.codekingdoms.roblox.base.BasePlayer")
829local BaseLocalCharacter = ckRequire("com.codekingdoms.roblox.base.BaseLocalCharacter")
830
831local BaseLocalPlayer = BasePlayer:Extend( function( self, project, object )
832
833 BasePlayer.init( self, project, object )
834
835 self._isLocal = true
836 self:_bindClicks()
837
838end)
839
840
841-- @category Actions
842function BaseLocalPlayer:Jump()
843
844 BaseLocalCharacter.Jump(self)
845
846end
847
848function BaseLocalPlayer:_bindClicks()
849
850 local Mouse = self.object:GetMouse()
851 Mouse.Button1Down:connect(function()
852
853 self:Run(self.OnLeftClick)
854
855 end)
856 Mouse.Button2Down:connect(function()
857
858 self:Run(self.OnRightClick)
859
860 end)
861
862end
863
864-- Detect the player
865-- @category Events
866function BaseLocalPlayer:OnLeftClick()
867
868end
869
870-- Detect the player
871-- @category Events
872function BaseLocalPlayer:OnRightClick()
873
874end
875
876-- Fires when a button is pressed
877-- @category Events
878-- @param {String} buttonName
879function BaseLocalPlayer:OnButtonPress(buttonName)
880
881end
882
883--- Make this object listen to the game's render loop
884-- @category Ui
885function BaseLocalPlayer:EnableRender()
886
887 BaseLocalCharacter.EnableRender(self)
888
889end
890
891-- @category Events
892-- @abstract
893function BaseLocalPlayer:OnRender()
894
895end
896
897--- Runs when a player hits the object
898-- @category Events
899-- @param {Local(com.codekingdoms.roblox.base.BaseLocalPlayer)} player
900-- @param {roblox.class.BasePart} [objectPart] The part of the object which is being touched by the player
901-- @abstract
902function BaseLocalPlayer:OnTouched( player, objectPart )
903
904end
905
906return BaseLocalPlayer
907end
908
909_G.ckClassProvider["com.codekingdoms.roblox.base.BaseLocalUi"] = function()
910local Base = ckRequire("com.codekingdoms.roblox.base.Base")
911local BaseObject = ckRequire("com.codekingdoms.roblox.base.BaseObject")
912
913-- @category Explorer
914-- @field {roblox.class.Instance} object The Roblox instance that this script controls
915-- @category Physics
916-- @field {roblox.class.UDim2} position The position of the ui
917
918local BaseLocalUi = Base:Extend( function( self, project, object )
919
920 Base.init(self, project, object)
921
922 self._isLocal = true
923 self:_addFields()
924 self:_bindClicked()
925
926end)
927
928function BaseLocalUi:_bindClicked()
929
930 if ( self.object.MouseButton1Down ) then
931
932 self.object.MouseButton1Down:connect(function()
933
934 local robloxPlayer = game.Players.LocalPlayer
935
936 if robloxPlayer ~= nil then
937
938 local playerClassPath = self._project:getLocalClassPath(self._path, "BaseLocalPlayer")
939
940 local playerInstance = self:FindScript( playerClassPath, robloxPlayer )
941
942 if ( playerInstance ~= nil and self.OnClick ~= BaseObject.OnClick) then
943
944 self:Run(self.OnClick, playerInstance)
945
946 end
947
948 end
949
950 end)
951
952 end
953
954end
955
956function BaseLocalUi:_addFields()
957
958 -- Force object to be saved as a field
959 self.object = self.object
960 self.position = nil
961
962 self:_defineProperty('position', function()
963
964 return self.object.Position
965
966 end, function( value )
967
968 self.object.Position = value
969
970 end)
971end
972
973--- Runs when a player clicks the object
974-- @category Events
975-- @param {Local(com.codekingdoms.roblox.base.BaseLocalPlayer)} player
976-- @abstract
977function BaseLocalUi:OnClick( player )
978
979end
980
981--- Clone the object
982-- @category Actions
983-- @param {roblox.class.UDim2} position The 2d position of the cloned object
984-- @param {com.codekingdoms.roblox.base.BaseLocalUi} [parent] The new parent of the cloned object, or the Workspace by default
985-- @returns {Local(com.codekingdoms.roblox.base.BaseLocalUi)}
986function BaseLocalUi:Clone( position, parent )
987
988 -- Same functionality, but with UDim2 instead of Vector3
989 return BaseObject.Clone(self, position, parent)
990
991end
992
993--- Set the visible text of this object to a particular value
994-- @category Ui
995-- @param {String} value
996function BaseLocalUi:SetText( value )
997
998 if ( self.object.Text ~= nil ) then
999
1000 self.object.Text.Text = value
1001
1002 end
1003
1004end
1005
1006return BaseLocalUi
1007
1008end
1009
1010_G.ckClassProvider["com.codekingdoms.roblox.base.BasePlayer"] = function()
1011local BaseCharacter = ckRequire("com.codekingdoms.roblox.base.BaseCharacter")
1012
1013-- @category Explorer
1014-- @field {roblox.class.Player} object The Roblox player that this script controls
1015
1016-- @category Player
1017-- @field {String} name The player's roblox username
1018
1019local BasePlayer = BaseCharacter:Extend( function( self, project, object )
1020
1021 BaseCharacter.init( self, project, object )
1022
1023 self.name = object.Name
1024
1025end)
1026
1027function BasePlayer:_InitLifeCycle()
1028
1029 self.name = self.name
1030
1031 -- Force object to be saved as a field
1032 self.object = self.object
1033
1034 self._hasDied = false
1035
1036 self.object.CharacterAdded:connect( function()
1037
1038 self:_BindDeath()
1039 wait(0)
1040 self:_OnSpawn()
1041
1042 end )
1043
1044end
1045
1046function BasePlayer:_bindTouched()
1047
1048 local handler = function()
1049
1050 if ( not self._joined ) then
1051
1052 self:Run(self.OnJoin)
1053 self._joined = true
1054
1055 end
1056
1057 BaseCharacter._bindTouched( self )
1058
1059 end
1060
1061 if ( self.object.Character ) then
1062
1063 handler()
1064
1065 end
1066
1067 self.object.CharacterAdded:connect( handler )
1068
1069end
1070
1071--- Get the current value of a stat
1072-- @category Ui
1073-- @param {String} name
1074-- @returns Mixed
1075function BasePlayer:GetStat( name )
1076
1077 -- Doesn't matter if the stat doesn't exist
1078 local ok, output = pcall(function()
1079
1080 return self.object.leaderstats[name].Value
1081
1082 end)
1083
1084 if ( ok ) then
1085
1086 return output
1087
1088 end
1089
1090end
1091
1092--- Set the current value of a stat
1093-- @category Ui
1094-- @param {String} name
1095-- @param {Mixed} value
1096function BasePlayer:SetStat( name, value )
1097
1098
1099 if ( self.object:FindFirstChild("leaderstats") == nil ) then
1100
1101 local leaderstats = self:Create("Model", self.position, self)
1102 leaderstats.Name = "leaderstats"
1103
1104 end
1105
1106 local stats = self.object.leaderstats
1107
1108 if ( self.object:FindFirstChild(name) == nil ) then
1109
1110 local valueType
1111
1112 if ( type(value) == "string" ) then
1113
1114 valueType = "StringValue"
1115
1116 elseif ( type(value) == "boolean" ) then
1117
1118 valueType = "BoolValue"
1119
1120 elseif ( type(value) == "number" ) then
1121
1122 valueType = "NumberValue"
1123
1124 else
1125
1126 valueType = "ObjectValue"
1127
1128 end
1129
1130 local stat = Instance.new(valueType)
1131 stat.Name = name
1132 stat.Parent = stats
1133
1134 end
1135
1136 self.object.leaderstats[name].Value = value
1137
1138end
1139
1140--- Hide stats
1141-- @category Ui
1142-- @param {String} [name] The individual stat to hide, or all of them by default
1143function BasePlayer:HideStats( name )
1144
1145 -- Doesn't matter if the stat doesn't exist
1146 pcall(function()
1147
1148 if ( name == nil ) then
1149
1150 self.object.leaderstats:Destroy()
1151
1152 else
1153
1154 self.object.leaderstats[name]:Destroy()
1155
1156 end
1157
1158 end)
1159
1160end
1161
1162-- @category Events
1163-- @abstract
1164function BasePlayer:OnJoin()
1165
1166end
1167
1168-- @category Events
1169-- @abstract
1170function BasePlayer:OnLeave()
1171
1172end
1173
1174--- Send a title to the player
1175-- @category Ui
1176-- @param {String} text The title to tell the player
1177function BasePlayer:SendTitle( text )
1178
1179 -- TODO Ben
1180 print("TITLE: " .. text)
1181
1182end
1183
1184--- Send a message to the player
1185-- @category Ui
1186-- @param {String} text The message to tell the player
1187function BasePlayer:SendMessage( text )
1188
1189 -- TODO Ben
1190 print("MESSAGE: " .. text)
1191
1192end
1193
1194--- Return a tool from your backpack
1195-- @category Player
1196-- @param {String} name The name of the item you want to find
1197-- @returns {roblox.class.Tool}
1198function BasePlayer:FindFirstTool(name)
1199
1200 local findToolInChildrenOfObject = function(object)
1201
1202 if object then
1203
1204 for _, child in pairs(object:GetChildren()) do
1205
1206 if (child.ClassName == "Tool" and child.Name == name) then
1207
1208 return child
1209
1210 end
1211
1212 end
1213
1214 end
1215
1216 end
1217
1218 local result = nil
1219
1220 if self.object then
1221
1222 result = findToolInChildrenOfObject(self.object.Character)
1223
1224 if not result then
1225
1226 result = findToolInChildrenOfObject(self.object.Backpack)
1227
1228 end
1229
1230 end
1231
1232 return result
1233
1234end
1235
1236-- @category Player
1237-- @returns {Model} The character's character part
1238function BasePlayer:GetCharacter()
1239
1240 return self.object.Character
1241
1242end
1243
1244return BasePlayer
1245end
1246
1247_G.ckClassProvider["space.codekingdoms.lovelyrat04.zombiesmash.ZombieSpawner"] = function()
1248local BaseLocalObject = ckRequire("com.codekingdoms.roblox.base.BaseLocalObject")
1249local Zombie = ckRequire("space.codekingdoms.lovelyrat04.zombiesmash.Zombie")
1250local Colors = ckRequire("com.codekingdoms.roblox.lib.Colors")
1251local Strings = ckRequire("com.codekingdoms.roblox.lib.Strings")
1252local Input = ckRequire("com.codekingdoms.roblox.lib.Input")
1253local ZombieSpawner = BaseLocalObject:Extend()
1254
1255-- @field {Number} level
1256-- @field {Number} zombiesLeft
1257-- @field {roblox.class.Part[]} spawnPoints
1258-- @field {space.codekingdoms.lovelyrat04.zombiesmash.Zombie} zombie
1259
1260function ZombieSpawner:OnCreate()
1261
1262 self.level = 0
1263 self.zombiesLeft = 0
1264 self.spawnPoints = self.object.SpawnPoints:GetChildren()
1265 self.zombie = self:FindStoredScript(Zombie)
1266 self:SpawnWave()
1267
1268end
1269
1270function ZombieSpawner:SpawnZombie()
1271
1272 self.zombiesLeft = self.zombiesLeft + 1
1273 local spawnPointIndex = math.random(1, # self.spawnPoints)
1274 local position = self.spawnPoints[spawnPointIndex].Position
1275 self.zombie:Clone(position)
1276
1277end
1278
1279function ZombieSpawner:SpawnWave()
1280
1281 self.level = self.level + 1
1282 for i = 1, self.level * 5 do
1283
1284 wait(1)
1285 self:SpawnZombie()
1286
1287 end
1288
1289end
1290
1291function ZombieSpawner:OnZombieKilled()
1292
1293 self.zombiesLeft = self.zombiesLeft - 1
1294 if (self.zombiesLeft == 0) then
1295
1296 self:SpawnWave()
1297
1298 end
1299
1300end
1301
1302return ZombieSpawner
1303end
1304
1305_G.ckClassProvider["com.codekingdoms.roblox.private.logger"] = function()
1306-- Adapted from https://github.com/rxi/log.lua/blob/master/log.lua
1307
1308local HttpService = game.HttpService
1309
1310local inspect
1311
1312if( script.Parent:FindFirstChild( "inspect" ) ~= nil ) then
1313 inspect = require(script.Parent.inspect)
1314else
1315 inspect = ckRequire("com.codekingdoms.roblox.private.inspect")
1316end
1317
1318local logger = {}
1319
1320logger.usecolor = false
1321logger.level = "trace"
1322
1323local modes = {
1324 { name = "trace", color = "\27[34m", },
1325 { name = "debug", color = "\27[36m", },
1326 { name = "log", color = "\27[32m", },
1327 { name = "info", color = "\27[32m", },
1328 { name = "warn", color = "\27[33m", },
1329 { name = "error", color = "\27[31m", },
1330 { name = "critical", color = "\27[35m", },
1331}
1332
1333local levels = {}
1334for i, v in ipairs(modes) do
1335 levels[v.name] = i
1336end
1337
1338local round = function(x, increment)
1339 increment = increment or 1
1340 x = x / increment
1341 return (x > 0 and math.floor(x + .5) or math.ceil(x - .5)) * increment
1342end
1343
1344
1345local _tostring = tostring
1346
1347local tostring = function(...)
1348 local t = {}
1349 for i = 1, select('#', ...) do
1350 local x = select(i, ...)
1351 if type(x) == "number" then
1352
1353 x = round(x, .01)
1354 elseif type(x) == "table" then
1355
1356 x = inspect(x)
1357 end
1358 t[#t + 1] = _tostring(x)
1359 end
1360 return table.concat(t, " ")
1361end
1362
1363-- Thanks to https://stackoverflow.com/a/24823383
1364function slice(tbl, first, last, step)
1365 local sliced = {}
1366
1367 for i = first or 1, last or #tbl, step or 1 do
1368 sliced[#sliced+1] = tbl[i]
1369 end
1370
1371 return sliced
1372end
1373
1374for i, x in ipairs(modes) do
1375 local nameupper = x.name:upper()
1376
1377 logger["ypcall_" .. x.name] = function(self, err, ...)
1378
1379 local file, line, message = string.match(err, "(.-):(%d+): (.+)")
1380
1381 -- Match the format from debug.traceback
1382 local customStack = "'" .. file .. "', Line " .. line
1383
1384 -- TODO BLOX-484: unpack(...) is not working for passing through additional args
1385 -- select(1, ...)
1386 return logger[x.name]( self, {
1387 customStack = customStack
1388 }, message )
1389 end
1390
1391 logger[x.name] = function(self, ...)
1392
1393 if i < levels[logger.level] then
1394 return
1395 end
1396
1397 local name = logger.name or "root"
1398 local message
1399
1400 local stack = debug.traceback()
1401 local lineinfo
1402 local simpleLineInfo
1403
1404 if ( select('#', ...) > 0 and type(select(1, ...)) == "table" and select(1, ...).customStack ~= nil )
1405 then
1406
1407 stack = select(1, ...).customStack
1408
1409 lineinfo = stack
1410 simpleLineInfo = lineinfo
1411
1412 -- TODO BLOX-484
1413 local remaining = select(2, ...)
1414
1415 message = tostring(remaining)
1416
1417 else
1418
1419 message = tostring(...)
1420
1421 lines = {}
1422 for s in stack:gmatch("[^\r\n]+") do
1423 table.insert(lines, s)
1424 end
1425
1426 lineinfo = lines[3]
1427
1428 local getLineInfo = function( pattern )
1429 local matches = {}
1430 for s in lineinfo:gmatch(pattern) do
1431 table.insert(matches, s)
1432 end
1433 return tostring(matches[1])
1434 end
1435
1436 local scriptName = getLineInfo( "Script '(.*)'" )
1437 local method = getLineInfo( "method (.*)" )
1438 local lineNo = getLineInfo( "Line ([0-9]*)" )
1439
1440 simpleLineInfo = scriptName .. ":" .. method .. ":" .. lineNo
1441
1442 end
1443
1444 local date = os.date("!*t")
1445 local formattedDate = string.format("%04d-%02d-%02dT%02d:%02d:%02dZ", date.year, date.month, date.day, date.hour, date.min, date.sec)
1446 local simpleDate = string.format("%02d:%02d:%02d", date.hour, date.min, date.sec)
1447
1448 -- TODO BLOX-680
1449 if i >= levels["warn"] then
1450 print(string.format("%s[%s - %s %s]: %s %s",
1451 logger.usecolor and x.color or "",
1452 simpleDate,
1453 logger.usecolor and "\27[0m" or "",
1454 simpleLineInfo,
1455 message,
1456 i >= levels["error"] and stack or ""))
1457 end
1458
1459 if( logger.serverRoot ) then
1460
1461 local activeProject = game.ReplicatedStorage.CodeKingdoms:FindFirstChild("ActiveProject")
1462 local projectId = activeProject and activeProject.Value
1463
1464 local ckUserIdString = game.ReplicatedStorage.CodeKingdoms:FindFirstChild("CkUserId")
1465 local ckUserId = ckUserIdString and ckUserIdString.Value
1466
1467 local robloxUserId = _G.plugin and _G.plugin:GetStudioUserId()
1468
1469 local request = {
1470 name = name,
1471 userId = ckUserId,
1472 robloxUserId = robloxUserId,
1473 level = i - 1,
1474 projectId = projectId,
1475 token = logger.token,
1476 privateToken = logger.privateToken,
1477 stack = stack,
1478 date = formattedDate,
1479 message = message
1480 }
1481
1482 if logger.context then
1483
1484 for k, v in pairs( logger.context ) do
1485
1486 request[k] = v
1487
1488 end
1489
1490 end
1491
1492 local ok, requestString = pcall(HttpService.JSONEncode, HttpService, request)
1493
1494 if ( not ok ) then
1495
1496 print("Error encoding log request", requestString)
1497 end
1498
1499 local ok, resultString = pcall(HttpService.PostAsync, HttpService, logger.serverRoot .. "log", requestString)
1500
1501 if ( not ok ) then
1502
1503 print("Error posting log request", resultString)
1504 end
1505
1506 end
1507
1508 end
1509end
1510
1511-- This is a bit ugly, but provides a means of associating a logger with static
1512-- context that can be attached before the log is sent to the server.
1513logger.getLoggerWithContext = function ( context )
1514
1515 local loggerWithContext = {}
1516
1517 for k, v in pairs( logger ) do
1518
1519 loggerWithContext[k] = v
1520
1521 end
1522
1523 for index, mode in ipairs( modes ) do
1524
1525 local fn = loggerWithContext[mode.name]
1526 loggerWithContext[mode.name] = function ( self, ... )
1527
1528 -- Temporarily set the context while the log is sent.
1529 logger.context = context
1530 fn( self, ... )
1531 logger.context = nil
1532
1533 end
1534
1535 end
1536
1537 return loggerWithContext
1538
1539end
1540
1541return logger
1542
1543end
1544
1545_G.ckClassProvider["com.codekingdoms.roblox.base.BaseLocalCharacter"] = function()
1546local BaseCharacter = ckRequire("com.codekingdoms.roblox.base.BaseCharacter")
1547local BaseLocalObject = ckRequire("com.codekingdoms.roblox.base.BaseLocalObject")
1548
1549local BaseLocalCharacter = BaseCharacter:Extend(function( self, project, object )
1550
1551 BaseCharacter.init( self, project, object )
1552 self._isLocal = true
1553
1554end)
1555
1556-- @category Actions
1557function BaseLocalCharacter:Jump()
1558
1559 self:GetHumanoid():ChangeState(Enum.HumanoidStateType.Jumping)
1560
1561end
1562
1563--- Make this object listen to the game's render loop
1564-- @category Ui
1565function BaseLocalCharacter:EnableRender()
1566
1567 BaseLocalObject.EnableRender(self)
1568
1569end
1570
1571--- Runs when a player hits the object
1572-- @category Events
1573-- @param {Local(com.codekingdoms.roblox.base.BaseLocalPlayer)} player
1574-- @param {roblox.class.BasePart} [objectPart] The part of the object which is being touched by the player
1575-- @abstract
1576function BaseLocalCharacter:OnTouched( player, objectPart )
1577
1578end
1579
1580return BaseLocalCharacter
1581
1582end
1583
1584_G.ckClassProvider["com.codekingdoms.roblox.lib.Input"] = function()
1585local class = ckRequire("com.codekingdoms.roblox.private.class")
1586local Button = ckRequire("com.codekingdoms.roblox.lib.Button")
1587local Strings = ckRequire("com.codekingdoms.roblox.lib.Strings")
1588local UserInputService = game:GetService("UserInputService")
1589local ContextActionService = game:GetService("ContextActionService")
1590
1591-- @category Input
1592-- @field {roblox.enum.KeyCode} Space
1593
1594local Input = {}
1595Input._buttonsByName = {}
1596
1597--- Add a virtual button bound to provided input keys.
1598-- @category Input
1599
1600-- @param {String} buttonName The name of the virtual button to create
1601-- @param {Input...} keys One or more keys to bind to the button
1602function Input.AddButton(buttonName, ...)
1603
1604 if Input._buttonsByName[buttonName] then
1605
1606 -- TODO [BLOX-529]
1607 error("Button " .. buttonName .. " has already been added. Call RemoveButton first.")
1608
1609 end
1610
1611 local button = Button.new(buttonName, {...})
1612 Input._buttonsByName[buttonName] = button
1613
1614 ContextActionService:BindAction(buttonName, Input._OnInput, true, ...)
1615
1616end
1617
1618-- @category Input
1619-- @param {String} buttonName The name of the virtual button to create
1620function Input.RemoveButton(buttonName)
1621
1622 if not Input._buttonsByName[buttonName] then
1623
1624 -- TODO [BLOX-529]
1625 error("Button " .. buttonName .. " does not exist, so cannot be removed.")
1626
1627 end
1628
1629 Input._buttonsByName[buttonName] = nil
1630
1631end
1632
1633-- @category Input
1634-- @param {String} buttonName The name of the virtual button to check
1635-- @returns {Boolean}
1636function Input.IsPressed( buttonName )
1637
1638 local button = Input._buttonsByName[buttonName]
1639 return button and button.isPressed
1640
1641end
1642
1643function Input._OnInput(buttonName, inputState)
1644
1645 local button = Input._buttonsByName[buttonName]
1646
1647 if inputState == Enum.UserInputState.Begin then
1648
1649 button:BeginPress()
1650
1651 elseif inputState == Enum.UserInputState.End then
1652
1653 button:EndPress()
1654
1655 else
1656
1657 error("Unexpected input state for button " .. buttonName .. ": " .. inputState)
1658
1659 end
1660
1661end
1662
1663--- Save Roblox keycodes to Input for easy reference.
1664--- E.g. Input.Up instead of Enum.KeyCode.Up.
1665function Input._InitializeInputMappings()
1666
1667 for i = 1, #Strings.ALPHABET do
1668
1669 local letter = string.sub(Strings.ALPHABET, i, i)
1670 Input[letter] = Enum.KeyCode[letter]
1671
1672 end
1673
1674 local robloxInputs = {
1675 "Backspace",
1676 "Tab",
1677 "Clear",
1678 "Return",
1679 "Pause",
1680 "Escape",
1681 "Space",
1682 "QuotedDouble",
1683 "Hash",
1684 "Dollar",
1685 "Percent",
1686 "Ampersand",
1687 "Quote",
1688 "LeftParenthesis",
1689 "RightParenthesis",
1690 "Asterisk",
1691 "Plus",
1692 "Comma",
1693 "Minus",
1694 "Period",
1695 "Slash",
1696 "Zero",
1697 "One",
1698 "Two",
1699 "Three",
1700 "Four",
1701 "Five",
1702 "Six",
1703 "Seven",
1704 "Eight",
1705 "Nine",
1706 "Colon",
1707 "Semicolon",
1708 "LessThan",
1709 "Equals",
1710 "GreaterThan",
1711 "Question",
1712 "At",
1713 "LeftBracket",
1714 "BackSlash",
1715 "RightBracket",
1716 "Caret",
1717 "Underscore",
1718 "Backquote",
1719 "A",
1720 "B",
1721 "C",
1722 "D",
1723 "E",
1724 "F",
1725 "G",
1726 "H",
1727 "I",
1728 "J",
1729 "K",
1730 "L",
1731 "M",
1732 "N",
1733 "O",
1734 "P",
1735 "Q",
1736 "R",
1737 "S",
1738 "T",
1739 "U",
1740 "V",
1741 "W",
1742 "X",
1743 "Y",
1744 "Z",
1745 "LeftCurly",
1746 "Pipe",
1747 "RightCurly",
1748 "Tilde",
1749 "Delete",
1750 "KeypadZero",
1751 "KeypadOne",
1752 "KeypadTwo",
1753 "KeypadThree",
1754 "KeypadFour",
1755 "KeypadFive",
1756 "KeypadSix",
1757 "KeypadSeven",
1758 "KeypadEight",
1759 "KeypadNine",
1760 "KeypadPeriod",
1761 "KeypadDivide",
1762 "KeypadMultiply",
1763 "KeypadMinus",
1764 "KeypadPlus",
1765 "KeypadEnter",
1766 "KeypadEquals",
1767 "Up" ,
1768 "Down",
1769 "Right",
1770 "Left",
1771 "Insert",
1772 "Home",
1773 "End",
1774 "PageUp",
1775 "PageDown",
1776 "F1",
1777 "F2",
1778 "F3",
1779 "F4",
1780 "F5",
1781 "F6",
1782 "F7",
1783 "F8",
1784 "F9",
1785 "F10",
1786 "F11",
1787 "F12",
1788 "F13",
1789 "F14",
1790 "F15",
1791 "NumLock",
1792 "CapsLock",
1793 "ScrollLock",
1794 "RightShift",
1795 "LeftShift",
1796 "RightControl",
1797 "LeftControl",
1798 "RightAlt",
1799 "LeftAlt",
1800 "RightMeta",
1801 "LeftMeta",
1802 "LeftSuper",
1803 "RightSuper",
1804 "Mode",
1805 "Compose",
1806 "Help",
1807 "Print",
1808 "SysReq",
1809 "Break",
1810 "Menu",
1811 "Power",
1812 "Euro",
1813 "Undo",
1814 "ButtonX",
1815 "ButtonY",
1816 "ButtonA",
1817 "ButtonB",
1818 "ButtonR1",
1819 "ButtonL1",
1820 "ButtonR2",
1821 "ButtonL2",
1822 "ButtonR3",
1823 "ButtonL3",
1824 "ButtonStart",
1825 "ButtonSelect",
1826 "DPadLeft",
1827 "DPadRight",
1828 "DPadUp",
1829 "DPadDown",
1830 "Thumbstick1",
1831 "Thumbstick2"
1832 }
1833
1834 for i = 1, #robloxInputs do
1835
1836 local input = robloxInputs[i]
1837 Input[input] = Enum.KeyCode[input]
1838
1839 end
1840
1841end
1842
1843Input._InitializeInputMappings()
1844
1845return Input
1846
1847end
1848
1849_G.ckClassProvider["com.codekingdoms.roblox.base.BaseTool"] = function()
1850local BaseObject = ckRequire("com.codekingdoms.roblox.base.BaseObject")
1851local logger = ckRequire("com.codekingdoms.roblox.private.logger")
1852
1853-- @field {roblox.class.Vector3} target
1854-- @field {roblox.class.Player} owner
1855local BaseTool = BaseObject:Extend( function( self, project, object )
1856
1857 BaseObject.init( self, project, object )
1858
1859 self:_bindEvents()
1860
1861end)
1862
1863-- @protected
1864function BaseTool:_addFields()
1865
1866 -- TODO: work out what properties from base object should apply to tool
1867 -- BaseObject._addFields(self)
1868
1869 self.owner = nil
1870
1871 self:_defineProperty("owner", function()
1872
1873 local playersService = game:GetService("Players")
1874 local parent = self.object.Parent
1875
1876 if ( parent.ClassName == "Model" ) then
1877
1878 return playersService:GetPlayerFromCharacter( parent )
1879
1880 elseif ( parent.Parent.ClassName == "Player" ) then
1881
1882 return parent.Parent
1883
1884 end
1885
1886 logger:warn("owner is undefined for a tool that is not either equipped or in a player's Backpack")
1887
1888 end, function(_)
1889
1890 logger:warn("BaseTool owner is read-only")
1891
1892 end)
1893
1894
1895 self:_defineProperty('position', function()
1896
1897 if (self.object.Parent.ClassName == "Model") then
1898
1899 return self.object.Parent.Head.position
1900
1901 end
1902
1903 logger:warn("Position is undefined for unequipped BaseTool")
1904
1905 end, function( _ )
1906
1907 logger:warn("BaseTool position is read-only")
1908
1909 end)
1910
1911 self:_defineProperty('targetPoint', function()
1912
1913 -- Assume that an equipped tool is always nested directly underneath
1914 -- the player's model, which should have a Humanoid part.
1915 if (self.object.Parent.ClassName == "Model") then
1916
1917 return self.object.Parent.Humanoid.TargetPoint
1918
1919 end
1920
1921 print("targetPoint is undefined for unequipped BaseTool")
1922
1923 end, function( _ )
1924
1925 -- TODO: Send error to Roblox code editor
1926 print("BaseTool targetPoint is read-only")
1927
1928 end)
1929
1930end
1931
1932-- @protected
1933function BaseTool:_bindEvents()
1934
1935 self.object.Activated:connect(function()
1936
1937 self:Run( self.OnActivate )
1938
1939 end)
1940 self.object.Deactivated:connect(function()
1941
1942 self:Run( self.OnDeactivate )
1943
1944 end)
1945 self.object.Equipped:connect(function()
1946
1947 self:Run( self.OnEquip )
1948
1949 end)
1950 self.object.Unequipped:connect(function()
1951
1952 self:Run( self.OnUnequip )
1953
1954 end)
1955
1956end
1957
1958--- Fire a stored projectile in the direction this object is facing
1959-- @category Actions
1960-- @generic {com.codekingdoms.roblox.base.BaseObject} T
1961-- @param {Type(T)} ProjectileClass The class of the stored projectile script to use
1962-- @param {Number} speed The speed to fire the new projectile at
1963-- @returns {T} The new projectile
1964function BaseTool:Fire(projectileClass, speed)
1965
1966 BaseObject.Fire(self, projectileClass, speed, self.targetPoint )
1967
1968end
1969
1970-- @category Events
1971-- @abstract
1972function BaseTool:OnActivate()
1973
1974end
1975
1976-- @category Events
1977-- @abstract
1978function BaseTool:OnDeactivate()
1979
1980end
1981
1982-- @category Events
1983-- @abstract
1984function BaseTool:OnEquip()
1985
1986end
1987
1988-- @category Events
1989-- @abstract
1990function BaseTool:OnUnequip()
1991
1992end
1993
1994return BaseTool
1995
1996end
1997
1998_G.ckClassProvider["com.codekingdoms.roblox.base.BaseCharacter"] = function()
1999local BaseObject = ckRequire("com.codekingdoms.roblox.base.BaseObject")
2000
2001-- @category Player
2002-- @field {Number} health
2003-- @category Script
2004-- @field {Boolean} alive Whether the Roblox object for this script has been destroyed or not
2005-- @category Player
2006-- @field {Number} respawnDelay
2007
2008local BaseCharacter = BaseObject:Extend( function( self, project, object )
2009
2010 BaseObject.init( self, project, object )
2011
2012 self:_InitLifeCycle()
2013
2014 self._lastPosition = nil
2015
2016end)
2017
2018function BaseCharacter:_bindTouched()
2019
2020 for _, obj in pairs(self:_WaitForCharacter():GetChildren()) do
2021
2022 if (obj.ClassName == "Part" and obj.Name ~= "HumanoidRootPart") then
2023
2024 self:_connectTouch( obj )
2025
2026 end
2027
2028 end
2029
2030end
2031
2032function BaseCharacter:_addFields()
2033
2034 BaseObject._addFields(self)
2035
2036 self.health = nil
2037
2038 self:_defineProperty('health', function()
2039
2040 return self:GetHumanoid().Health
2041
2042 end, function( value )
2043
2044 self:GetHumanoid().Health = value
2045 if ( value == 0 ) then
2046
2047 self.alive = false
2048
2049 end
2050
2051 end)
2052
2053 self:_defineProperty('position', function()
2054
2055 local torso = self:GetTorso()
2056
2057 if ( torso ) then
2058
2059 self._lastPosition = torso.Position
2060
2061 end
2062
2063 return self._lastPosition
2064
2065 end, function( value )
2066
2067 self._lastPosition = CFrame.new(value)
2068
2069 local torso = self:GetTorso()
2070 if ( torso ) then
2071
2072 torso.CFrame = self._lastPosition
2073
2074 end
2075
2076 end)
2077
2078end
2079
2080function BaseCharacter:_defineVisibility()
2081
2082 self:_defineProperty('visible', function()
2083
2084 local torso = self:GetTorso()
2085
2086 if not torso then
2087
2088 logger:warn("Cannot determine visibility as character has no torso")
2089
2090 end
2091
2092 return torso.Transparency < 1
2093
2094 end, function( _ )
2095
2096 -- TODO Roblox API
2097 logger:warn("Use Show and Hide to change the visibility of the object, setting visible directly has no effect!")
2098
2099 end)
2100
2101end
2102
2103-- @param {Number} delay
2104-- @category Player
2105function BaseCharacter:EnableRespawn( delay )
2106
2107 self._respawnCharacter = self:_WaitForCharacter():Clone();
2108 self.respawnDelay = delay
2109
2110end
2111
2112function BaseCharacter:_InitLifeCycle()
2113
2114 self._hasDied = false
2115 self:_BindDeath()
2116 self:_OnSpawn()
2117
2118end
2119
2120function BaseCharacter:_BindDeath()
2121
2122 self:GetHumanoid().Died:connect(function()
2123
2124 self:Run(self.OnDeath)
2125 self.alive = false
2126 self._hasDied = true
2127
2128 if self.respawnDelay and self._respawnCharacter then
2129
2130 wait(self.respawnDelay)
2131
2132 if self.object.Character then
2133
2134 self.object.Character:Destroy()
2135
2136 end
2137
2138 local newCharacter = self._respawnCharacter:Clone()
2139 newCharacter.Parent = self.object
2140
2141 self:_BindDeath()
2142 self:_OnSpawn()
2143
2144 end
2145
2146 end)
2147
2148end
2149
2150function BaseCharacter:_OnSpawn()
2151
2152 self:Run(self.OnSpawn)
2153 self.alive = true
2154
2155 if (self._hasDied) then
2156
2157 self:Run(self.OnRespawn)
2158
2159 end
2160
2161end
2162
2163function BaseCharacter:_WaitForCharacter()
2164
2165 local timeWaited = 0
2166 local delta = 0.01
2167 local timeout = 3
2168 local character = self:GetCharacter()
2169
2170 while not character and timeWaited < timeout do
2171
2172 wait(delta)
2173 timeWaited = timeWaited + delta
2174 character = self:GetCharacter()
2175
2176 end
2177
2178 return character
2179
2180end
2181
2182--- Get the character's Roblox humanoid part
2183-- @category Player
2184-- @returns {Humanoid} The character's humanoid part
2185function BaseCharacter:GetHumanoid()
2186
2187 -- When spawning, the humanoid doesn't always seem to be created
2188 -- before this method is called, so wait for it.
2189 local character = self:_WaitForCharacter()
2190
2191 if character then
2192
2193 return character:WaitForChild('Humanoid')
2194
2195 end
2196
2197 return nil
2198
2199end
2200
2201--- Get the character's Roblox character part
2202-- @category Player
2203-- @returns {Model} The character's character part
2204function BaseCharacter:GetCharacter()
2205
2206 return self.object:FindFirstChild("Character")
2207
2208end
2209
2210-- @category Player
2211-- @returns {Boolean} whether the character is on the ground
2212function BaseCharacter:IsOnGround()
2213
2214 local state = self:GetHumanoid():GetState().Value
2215
2216 -- See http://wiki.roblox.com/index.php?title=API:Enum/HumanoidStateType
2217 if state == 4 or state == 7 or state == 8 or state == 10 or state == 12 or state == 13 or state == 14 then
2218 return true
2219 else
2220 return false
2221 end
2222
2223end
2224
2225-- @category Player
2226function BaseCharacter:RemoveBody()
2227 --TODO: Confirm this works
2228 if not self.object.Character then
2229
2230 return nil
2231
2232 end
2233
2234 for _, child in pairs(self.object.Character:GetChildren()) do
2235
2236 if child.ClassName == "MeshPart" or child.ClassName == "Part" then
2237
2238 child:Destroy()
2239
2240 end
2241
2242 end
2243
2244end
2245
2246-- @param {roblox.class.Vector3} direction
2247-- @category Actions
2248function BaseCharacter:Move(direction)
2249
2250 self:GetHumanoid():Move(direction, false)
2251
2252end
2253
2254--- Get the character torso
2255-- @category Player
2256-- @returns {Part} The part representing the character's torso
2257function BaseCharacter:GetTorso ()
2258
2259 local character = self:GetCharacter()
2260
2261 if not character then
2262
2263 return nil
2264
2265 end
2266
2267 return character:FindFirstChild("Torso") or character:FindFirstChild("UpperTorso")
2268
2269end
2270
2271-- @param {BaseCharacter} target
2272-- @category Actions
2273function BaseCharacter:Follow( target )
2274
2275 if target then
2276
2277 local humanoid = self:GetHumanoid()
2278
2279 if humanoid then
2280
2281 humanoid:MoveTo(target.position, target.object)
2282
2283 end
2284
2285 else
2286
2287 -- TODO [BLOX-529]
2288 print("Target object not not found.")
2289
2290 end
2291
2292end
2293
2294--- Damage the character
2295-- @category Player
2296-- @param {Number} amount The number of hit points to damage the character by
2297function BaseCharacter:Damage( amount )
2298
2299 self.health = self.health - amount
2300
2301end
2302
2303--- Called when a character is first created.
2304-- @category Events
2305-- @abstract
2306function BaseCharacter:OnCreate()
2307
2308end
2309
2310--- Called each time that a character's model is added to the workspace
2311--- as part of the spawn/respawn/death cycle.
2312-- @category Events
2313-- @abstract
2314function BaseCharacter:OnSpawn()
2315
2316end
2317
2318--- Called each time after the first that a character's model is added to
2319--- the workspace.
2320-- @category Events
2321-- @abstract
2322function BaseCharacter:OnRespawn()
2323
2324end
2325
2326--- Called when a character's model is destroyed. If the character has
2327--- respawn enabled, OnSpawn will get called after the configured respawn
2328--- duration.
2329-- @category Events
2330-- @abstract
2331function BaseCharacter:OnDeath()
2332
2333end
2334
2335--- Make the character fall over
2336-- @category Actions
2337function BaseCharacter:FallOver()
2338
2339 local torso = self:GetTorso()
2340
2341 if torso:FindFirstChild("Knock") == nil then
2342 torso.CFrame = torso.CFrame * CFrame.Angles(math.rad(180),0,0)
2343 local knock = Instance.new("BodyThrust")
2344 knock.Name = "Knock"
2345 knock.force = Vector3.new(0,0,10000)
2346 knock.Parent = torso
2347 game:GetService("Debris"):AddItem(knock, 0.1)
2348 end
2349
2350end
2351
2352return BaseCharacter
2353
2354end
2355
2356_G.ckClassProvider["com.codekingdoms.roblox.base.Base"] = function()
2357local class = ckRequire("com.codekingdoms.roblox.private.class")
2358local logger = ckRequire("com.codekingdoms.roblox.private.logger")
2359
2360-- @category Script
2361-- @field {Boolean} alive Whether the Roblox object for this script has been destroyed or not
2362
2363local Base = class(function( self, project, object )
2364
2365 self._project = project
2366
2367 if ( not object ) then
2368
2369 -- TODO [BLOX-529]
2370 print("Object not found")
2371
2372 end
2373
2374 self.object = object
2375
2376 self.alive = true
2377
2378 self._destroyed = false
2379
2380end)
2381
2382--- Extend this class to make a new class
2383-- @category Tools
2384-- @returns {Type(com.codekingdoms.roblox.base.Base)}
2385function Base:Extend( fn )
2386
2387 return class( self, fn )
2388
2389end
2390
2391-- @category Events
2392-- @abstract
2393function Base:OnCreate()
2394
2395end
2396
2397--- Clone the script
2398-- @category Actions
2399-- @param {roblox.class.Vector3} position The position of the cloned object
2400-- @param {com.codekingdoms.roblox.base.Base} [parent] The parent of the cloned object, or the Workspace by default
2401-- @returns {Local}
2402-- @abstract
2403function Base:Clone( position, parent )
2404
2405
2406end
2407
2408-- @category Actions
2409function Base:Destroy()
2410
2411 self:Run(self.OnDestroy)
2412
2413 self._destroyed = true
2414
2415 -- TODO [BLOX-529]
2416 --print("Destroyed script", self._path)
2417
2418 if ( self.object ~= nil ) then
2419
2420 -- Doesn't matter if this fails
2421 local ok, output = pcall(self.object.Destroy, self.object)
2422
2423 end
2424
2425 if ( self._project ~= nil ) then
2426
2427 self._project:removeScript( self )
2428
2429 end
2430
2431 self.alive = false
2432
2433end
2434
2435--- Runs when an object is destroyed
2436-- @category Events
2437-- @abstract
2438function Base:OnDestroy()
2439
2440end
2441
2442--- Get a list of Players in the Game
2443-- @category Actions
2444-- @returns {Local(com.codekingdoms.roblox.base.BasePlayer)[]} The list of Player script objects
2445function Base:GetPlayers()
2446
2447 local playerClassName = self._isLocal and "BaseLocalPlayer" or "BasePlayer"
2448 local playerClassPath = self._project:getLocalClassPath(self._path, playerClassName)
2449 return self:FindScripts(playerClassPath)
2450
2451end
2452
2453--- Get an active script in the game
2454-- @category Script
2455-- @generic {com.codekingdoms.roblox.base.Base} T
2456-- @param {Type(T)} Class The class of the script to find
2457-- @param {roblox.class.Instance} [object] The roblox object who owns a script
2458-- @param {Boolean} [recursive] Whether to look through all the descendants of the object
2459-- @returns {T} The first script of type T that exists in the game
2460function Base:FindScript( Class, object, recursive )
2461
2462 return self._project:findScriptsForObject( Class, object, recursive, true )[1]
2463
2464end
2465
2466--- Find active scripts that are a descendant of a roblox object
2467-- @category Script
2468-- @generic {com.codekingdoms.roblox.base.Base} T
2469-- @param {Type(T)} Class The class of the scripts to find
2470-- @param {roblox.class.Instance} [object] The roblox object who owns a script
2471-- @returns {T[]} The scripts of type T that exists in the game
2472function Base:FindScripts( Class, object )
2473
2474 return self._project:findScriptsForObject( Class, object, true, false )
2475
2476end
2477
2478-- Find the first active script of a particular class which is an ancestor of this script
2479-- @category Script
2480-- @generic {com.codekingdoms.roblox.base.BaseObject} T
2481-- @param {Type(T)} Class The class of the scripts to find
2482-- @returns {T} The script of type T that exists in the game
2483function Base:FindAncestorScript( Class )
2484
2485 local parent = self.object.Parent
2486 while ( parent ) do
2487
2488 local ancestor = self:FindScript( Class, parent )
2489 if ( ancestor ) then
2490
2491 return ancestor
2492
2493 end
2494
2495 parent = parent.Parent
2496
2497 end
2498
2499end
2500
2501--- Find an active script in either ReplicatedStorage.GameStorage or ServerStorage.
2502-- @category Script
2503-- @generic {com.codekingdoms.roblox.base.Base} T
2504-- @param {Type(T)} Class The class of the script to find
2505-- @returns {T} The first script of type T that exists under parent
2506function Base:FindStoredScript( Class )
2507
2508 local gameStorage = game.ReplicatedStorage:FindFirstChild("GameStorage")
2509
2510 local script
2511
2512 if gameStorage then
2513
2514 script = self:FindScript( Class, gameStorage, true )
2515
2516 end
2517
2518 if not script then
2519
2520 script = self:FindScript( Class, game.ServerStorage, true )
2521
2522 end
2523
2524 return script
2525
2526end
2527
2528--- Find active scripts in either ReplicatedStorage.GameStorage or ServerStorage.
2529-- @category Script
2530-- @generic {Base} T
2531-- @param {Type(T)} class The class of the scripts to find
2532-- @returns {T[]} The scripts of type T that exist under parent
2533function Base:FindStoredScripts( Class )
2534
2535 local gameStorage = game.ReplicatedStorage:FindFirstChild("GameStorage")
2536
2537 local scripts = {}
2538
2539 if gameStorage then
2540
2541 for _, value in ipairs(self:FindScripts( Class, gameStorage )) do
2542
2543 table.insert(scripts, value)
2544
2545 end
2546
2547 end
2548
2549 for _, value in ipairs(self:FindScripts( Class, game.ServerStorage )) do
2550
2551 table.insert(scripts, value)
2552
2553 end
2554
2555 return scripts
2556
2557end
2558
2559-- @param {Mixed... -> Mixed} fn
2560-- @category Tools
2561function Base:Run( fn, ... )
2562
2563 local runArgs = {...}
2564
2565 local thread = coroutine.create(function()
2566
2567 wait()
2568
2569 local ok, err = ypcall(fn, self, unpack(runArgs))
2570
2571 if ( not ok ) then
2572
2573 -- TODO [BLOX-529]
2574 print(err)
2575 --logger:ypcall_error( err )
2576
2577 end
2578
2579 end)
2580
2581 coroutine.resume(thread)
2582
2583end
2584
2585--- Display a message to all players, for a set amount of time
2586-- @category Ui
2587-- @param {String} message The message to show
2588-- @param {Number} time The duration of time for which the message is shown
2589function Base:BroadcastMessage( text, time )
2590
2591 local m = Instance.new("Hint", Workspace)
2592 m.Text = text
2593 game:GetService("Debris"):AddItem(m, time)
2594
2595end
2596
2597-- @protected
2598function Base:_defineProperty( name, getter, setter )
2599
2600 self._properties[name] = {
2601 getter = getter,
2602 setter = setter
2603 }
2604
2605end
2606
2607-- Creates an event with specified name that client and server can call/listen to
2608-- @category Multiplayer
2609-- @param {String} name The name of the event to create
2610function Base:AddEvent(name)
2611
2612 -- As events go in ReplicatedStorage, it's server dictated.
2613 if(game:GetService("RunService"):IsServer()) then
2614
2615 local ReplicatedStorage = game:GetService("ReplicatedStorage")
2616 local newEvent = Instance.new("RemoteEvent", ReplicatedStorage)
2617 newEvent.Name = name
2618 else
2619 print("Base:AddEvent can only be called from a non-local script. Also make sure you are testing with a local server and clients!")
2620 end
2621
2622end
2623
2624-- Listens to a specific event - when it's fired, the method will run
2625-- @category Multiplayer
2626-- @param {String} name The name of the event to listen to
2627-- @param {function} method The function that should be called when the event is fired
2628function Base:ListenToEvent(name, method)
2629 local ReplicatedStorage = game:GetService("ReplicatedStorage")
2630 local event = ReplicatedStorage:WaitForChild(name)
2631
2632 local me = self
2633 local handler = function( ... )
2634 local args = {...}
2635 method( me, unpack(args))
2636 end
2637
2638 if(game:GetService("RunService"):IsClient()) then
2639 event.OnClientEvent:Connect(handler)
2640
2641 elseif(game:GetService("RunService"):IsServer()) then
2642 event.OnServerEvent:Connect(handler)
2643
2644 else
2645 -- Only servers and clients can listen to events
2646 print("Base:ListenToEvent cannot be called when running in Studio. Test with local server and clients instead!")
2647 end
2648
2649
2650end
2651
2652-- Listens to a specific event - when it's fired, the method will run
2653-- @category Multiplayer
2654-- @param {String} name The name of the event to listen to
2655-- @param {Mixed...} ... Optional objects to pass
2656function Base:FireEvent( name, ... )
2657
2658 if(RunService:IsServer()) then
2659 return
2660 end
2661
2662 local ReplicatedStorage = game:GetService("ReplicatedStorage")
2663 local event = ReplicatedStorage:WaitForChild(name)
2664
2665 -- This is an indeterminate amount of unknown parameters that get passed through
2666 local args = {...}
2667
2668 local sendArgs = {}
2669
2670 for i=1, #args do
2671 sendArgs[i] = args[i]
2672 end
2673 print("FireEvent with name " .. name .. " sent to server, along with " .. #sendArgs .. " arguments.")
2674 event:FireServer(unpack(sendArgs))
2675
2676end
2677
2678-- @category Multiplayer
2679function Base:FireEventTo( name, player, ... )
2680
2681 if(game:GetService("RunService"):IsClient()) then
2682
2683 if(game:GetService("RunService"):IsServer()) then
2684 print("Using FireEventTo can't be used in Roblox Studio so this didn't do anything. Please publish your game or test it with a Client and Server using the Test tab. Event:", name)
2685 return
2686 end
2687
2688 print("Using FireEventTo can't be used on the client so this didn't do anything. Event:", name)
2689 return
2690
2691 end
2692
2693 local ReplicatedStorage = game:GetService("ReplicatedStorage")
2694 local event = ReplicatedStorage:WaitForChild(name)
2695
2696 -- This is an indeterminate amount of unknown parameters that get passed through
2697 local args = {...}
2698
2699 local sendArgs = {}
2700
2701 for i=1, #args do
2702 sendArgs[i] = args[i]
2703 end
2704
2705 local ok, robloxPlayer = pcall(function() return player.object end)
2706 if ok then player = robloxPlayer end
2707
2708 if(not player:IsA("Player")) then
2709 print(name .. " event that is being sent from the server does not have a target player specified. Aborting!")
2710 return
2711 end
2712
2713 print("FireEvent with name " .. name .. " sent to " .. player.Name .. ", along with " .. #sendArgs .. " arguments.")
2714 event:FireClient( player, unpack(sendArgs))
2715
2716end
2717
2718-- This is a special event that can only be fired from the server - it sends to all clients
2719-- @category Multiplayer
2720-- @param {String} name The name of the event to listen to
2721-- @param {Mixed...} ... Optional variables
2722function Base:BroadcastEvent( name, ... )
2723 local ReplicatedStorage = game:GetService("ReplicatedStorage")
2724 local event = ReplicatedStorage:WaitForChild(name)
2725 local arg = {...}
2726
2727 if(game:GetService("RunService"):IsServer()) then
2728 local argumentsToSend = {}
2729
2730 -- The first entry in the table always seems to get eaten by FireAllClients, so add a buffer
2731 table.insert(argumentsToSend, "buffer")
2732 for child, data in pairs(arg) do
2733 table.insert(argumentsToSend, data)
2734 end
2735 local ok, output = pcall(function()
2736 event:FireAllClients(unpack(argumentsToSend))
2737 end)
2738 if not ok then
2739 print(output)
2740 end
2741 else
2742 -- This can only be fired from the server!
2743 print("Base:Broadcast can only be called from a non-local script. Also make sure you are testing with a local server and clients!")
2744 end
2745
2746end
2747
2748return Base
2749
2750end
2751
2752_G.ckClassProvider["com.codekingdoms.roblox.lib.Strings"] = function()
2753local Strings = {}
2754
2755-- From http://lua-users.org/wiki/StringRecipes
2756
2757-- @category Tools
2758-- @param {String} string
2759-- @param {Number} start
2760-- @returns {Boolean}
2761function Strings.StartsWith( str, start )
2762 return string.sub(str,1,string.len(start))==start
2763end
2764
2765-- @category Tools
2766-- @param {String} string
2767-- @param {Number} end
2768-- @returns {Boolean}
2769function Strings.EndsWith( str, ending )
2770 return string.sub(str,-string.len(ending))==ending
2771end
2772
2773-- @category Tools
2774-- @param {String} stringToSearch
2775-- @param {String} stringToFind
2776function Strings.Contains( stringToSearch, stringToFind )
2777 return string.find(stringToSearch, stringToFind, 1, true)
2778end
2779
2780-- @category Tools
2781-- @param {String} string
2782-- @param {String} separator
2783-- @returns {String[]}
2784function Strings.Split( str, separator )
2785
2786 if separator == nil then
2787 separator = "%s"
2788 end
2789 local output={} ; i=1
2790 for part in string.gmatch(str, "([^"..separator.."]+)") do
2791 output[i] = part
2792 i = i + 1
2793 end
2794 return output
2795
2796end
2797
2798-- @category Tools
2799-- @param {String} path
2800-- @returns {String}
2801function Strings.QualifiedNameFromPath( path )
2802
2803 return string.sub(string.gsub( path, "/", "." ), 1)
2804
2805end
2806
2807-- @category Tools
2808-- @param {String} path
2809-- @returns {String}
2810function Strings.PathFromQualifiedName( path )
2811
2812 return "/" .. string.gsub( path, "/", "." )
2813
2814end
2815
2816-- @category Tools
2817-- @param {String} path
2818-- @returns {String}
2819function Strings.SimpleNameFromPath( path )
2820
2821 local index = string.find(path, "/[^/]*$")
2822
2823 if ( index == nil ) then
2824
2825 return path
2826
2827 end
2828
2829 return string.sub(path,index+1)
2830
2831end
2832
2833Strings.ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
2834
2835return Strings
2836
2837end
2838
2839_G.ckClassProvider["com.codekingdoms.roblox.private.inspect"] = function()
2840local inspect ={
2841 _VERSION = 'inspect.lua 3.1.0',
2842 _URL = 'http://github.com/kikito/inspect.lua',
2843 _DESCRIPTION = 'human-readable representations of tables',
2844 _LICENSE = [[
2845 MIT LICENSE
2846 Copyright (c) 2013 Enrique GarcÃa Cota
2847 Permission is hereby granted, free of charge, to any person obtaining a
2848 copy of this software and associated documentation files (the
2849 "Software"), to deal in the Software without restriction, including
2850 without limitation the rights to use, copy, modify, merge, publish,
2851 distribute, sublicense, and/or sell copies of the Software, and to
2852 permit persons to whom the Software is furnished to do so, subject to
2853 the following conditions:
2854 The above copyright notice and this permission notice shall be included
2855 in all copies or substantial portions of the Software.
2856 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
2857 OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
2858 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
2859 IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
2860 CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
2861 TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
2862 SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
2863 ]]
2864}
2865
2866local tostring = tostring
2867
2868inspect.KEY = setmetatable({}, {__tostring = function() return 'inspect.KEY' end})
2869inspect.METATABLE = setmetatable({}, {__tostring = function() return 'inspect.METATABLE' end})
2870
2871-- Apostrophizes the string if it has quotes, but not aphostrophes
2872-- Otherwise, it returns a regular quoted string
2873local function smartQuote(str)
2874 if str:match('"') and not str:match("'") then
2875 return "'" .. str .. "'"
2876 end
2877 return '"' .. str:gsub('"', '\\"') .. '"'
2878end
2879
2880-- \a => '\\a', \0 => '\\0', 31 => '\31'
2881local shortControlCharEscapes = {
2882 ["\a"] = "\\a", ["\b"] = "\\b", ["\f"] = "\\f", ["\n"] = "\\n",
2883 ["\r"] = "\\r", ["\t"] = "\\t", ["\v"] = "\\v"
2884}
2885local longControlCharEscapes = {} -- \a => nil, \0 => \000, 31 => \031
2886for i=0, 31 do
2887 local ch = string.char(i)
2888 if not shortControlCharEscapes[ch] then
2889 shortControlCharEscapes[ch] = "\\"..i
2890 longControlCharEscapes[ch] = string.format("\\%03d", i)
2891 end
2892end
2893
2894local function escape(str)
2895 return (str:gsub("\\", "\\\\")
2896 :gsub("(%c)%f[0-9]", longControlCharEscapes)
2897 :gsub("%c", shortControlCharEscapes))
2898end
2899
2900local function isIdentifier(str)
2901 return type(str) == 'string' and str:match( "^[_%a][_%a%d]*$" )
2902end
2903
2904local function isSequenceKey(k, sequenceLength)
2905 return type(k) == 'number'
2906 and 1 <= k
2907 and k <= sequenceLength
2908 and math.floor(k) == k
2909end
2910
2911local defaultTypeOrders = {
2912 ['number'] = 1, ['boolean'] = 2, ['string'] = 3, ['table'] = 4,
2913 ['function'] = 5, ['userdata'] = 6, ['thread'] = 7
2914}
2915
2916local function sortKeys(a, b)
2917 local ta, tb = type(a), type(b)
2918
2919 -- strings and numbers are sorted numerically/alphabetically
2920 if ta == tb and (ta == 'string' or ta == 'number') then return a < b end
2921
2922 local dta, dtb = defaultTypeOrders[ta], defaultTypeOrders[tb]
2923 -- Two default types are compared according to the defaultTypeOrders table
2924 if dta and dtb then return defaultTypeOrders[ta] < defaultTypeOrders[tb]
2925 elseif dta then return true -- default types before custom ones
2926 elseif dtb then return false -- custom types after default ones
2927 end
2928
2929 -- custom types are sorted out alphabetically
2930 return ta < tb
2931end
2932
2933-- For implementation reasons, the behavior of rawlen & # is "undefined" when
2934-- tables aren't pure sequences. So we implement our own # operator.
2935local function getSequenceLength(t)
2936 local len = 1
2937 local v = rawget(t,len)
2938 while v ~= nil do
2939 len = len + 1
2940 v = rawget(t,len)
2941 end
2942 return len - 1
2943end
2944
2945local function getNonSequentialKeys(t)
2946 local keys = {}
2947 local sequenceLength = getSequenceLength(t)
2948 for k,_ in pairs(t) do
2949 if not isSequenceKey(k, sequenceLength) then table.insert(keys, k) end
2950 end
2951 table.sort(keys, sortKeys)
2952 return keys, sequenceLength
2953end
2954
2955local function getToStringResultSafely(t, mt)
2956 local __tostring = type(mt) == 'table' and rawget(mt, '__tostring')
2957 local str, ok
2958 if type(__tostring) == 'function' then
2959 ok, str = pcall(__tostring, t)
2960 str = ok and str or 'error: ' .. tostring(str)
2961 end
2962 if type(str) == 'string' and #str > 0 then return str end
2963end
2964
2965local function countTableAppearances(t, tableAppearances)
2966 tableAppearances = tableAppearances or {}
2967
2968 if type(t) == 'table' then
2969 if not tableAppearances[t] then
2970 tableAppearances[t] = 1
2971 for k,v in pairs(t) do
2972 countTableAppearances(k, tableAppearances)
2973 countTableAppearances(v, tableAppearances)
2974 end
2975 countTableAppearances(getmetatable(t), tableAppearances)
2976 else
2977 tableAppearances[t] = tableAppearances[t] + 1
2978 end
2979 end
2980
2981 return tableAppearances
2982end
2983
2984local copySequence = function(s)
2985 local copy, len = {}, #s
2986 for i=1, len do copy[i] = s[i] end
2987 return copy, len
2988end
2989
2990local function makePath(path, ...)
2991 local keys = {...}
2992 local newPath, len = copySequence(path)
2993 for i=1, #keys do
2994 newPath[len + i] = keys[i]
2995 end
2996 return newPath
2997end
2998
2999local function processRecursive(process, item, path, visited)
3000
3001 if item == nil then return nil end
3002 if visited[item] then return visited[item] end
3003
3004 local processed = process(item, path)
3005 if type(processed) == 'table' then
3006 local processedCopy = {}
3007 visited[item] = processedCopy
3008 local processedKey
3009
3010 for k,v in pairs(processed) do
3011 processedKey = processRecursive(process, k, makePath(path, k, inspect.KEY), visited)
3012 if processedKey ~= nil then
3013 processedCopy[processedKey] = processRecursive(process, v, makePath(path, processedKey), visited)
3014 end
3015 end
3016
3017 local mt = processRecursive(process, getmetatable(processed), makePath(path, inspect.METATABLE), visited)
3018 setmetatable(processedCopy, mt)
3019 processed = processedCopy
3020 end
3021 return processed
3022end
3023
3024
3025
3026-------------------------------------------------------------------
3027
3028local Inspector = {}
3029local Inspector_mt = {__index = Inspector}
3030
3031function Inspector:puts(...)
3032 local args = {...}
3033 local buffer = self.buffer
3034 local len = #buffer
3035 for i=1, #args do
3036 len = len + 1
3037 buffer[len] = args[i]
3038 end
3039end
3040
3041function Inspector:down(f)
3042 self.level = self.level + 1
3043 f()
3044 self.level = self.level - 1
3045end
3046
3047function Inspector:tabify()
3048 self:puts(self.newline, string.rep(self.indent, self.level))
3049end
3050
3051function Inspector:alreadyVisited(v)
3052 return self.ids[v] ~= nil
3053end
3054
3055function Inspector:getId(v)
3056 local id = self.ids[v]
3057 if not id then
3058 local tv = type(v)
3059 id = (self.maxIds[tv] or 0) + 1
3060 self.maxIds[tv] = id
3061 self.ids[v] = id
3062 end
3063 return tostring(id)
3064end
3065
3066function Inspector:putKey(k)
3067 if isIdentifier(k) then return self:puts(k) end
3068 self:puts("[")
3069 self:putValue(k)
3070 self:puts("]")
3071end
3072
3073function Inspector:putTable(t)
3074 if t == inspect.KEY or t == inspect.METATABLE then
3075 self:puts(tostring(t))
3076 elseif self:alreadyVisited(t) then
3077 self:puts('<table ', self:getId(t), '>')
3078 elseif self.level >= self.depth then
3079 self:puts('{...}')
3080 else
3081 if self.tableAppearances[t] > 1 then self:puts('<', self:getId(t), '>') end
3082
3083 local nonSequentialKeys, sequenceLength = getNonSequentialKeys(t)
3084 local mt = getmetatable(t)
3085 local toStringResult = getToStringResultSafely(t, mt)
3086
3087 self:puts('{')
3088 self:down(function()
3089 if toStringResult then
3090 self:puts(' -- ', escape(toStringResult))
3091 if sequenceLength >= 1 then self:tabify() end
3092 end
3093
3094 local count = 0
3095 for i=1, sequenceLength do
3096 if count > 0 then self:puts(',') end
3097 self:puts(' ')
3098 self:putValue(t[i])
3099 count = count + 1
3100 end
3101
3102 for _,k in ipairs(nonSequentialKeys) do
3103 if count > 0 then self:puts(',') end
3104 self:tabify()
3105 self:putKey(k)
3106 self:puts(' = ')
3107 self:putValue(t[k])
3108 count = count + 1
3109 end
3110
3111 if mt then
3112 if count > 0 then self:puts(',') end
3113 self:tabify()
3114 self:puts('<metatable> = ')
3115 self:putValue(mt)
3116 end
3117 end)
3118
3119 if #nonSequentialKeys > 0 or mt then -- result is multi-lined. Justify closing }
3120 self:tabify()
3121 elseif sequenceLength > 0 then -- array tables have one extra space before closing }
3122 self:puts(' ')
3123 end
3124
3125 self:puts('}')
3126 end
3127end
3128
3129function Inspector:putValue(v)
3130 local tv = type(v)
3131
3132 if tv == 'string' then
3133 self:puts(smartQuote(escape(v)))
3134 elseif tv == 'number' or tv == 'boolean' or tv == 'nil' then
3135 self:puts(tostring(v))
3136 elseif tv == 'table' then
3137 self:putTable(v)
3138 else
3139 self:puts('<',tv,' ',self:getId(v),'>')
3140 end
3141end
3142
3143-------------------------------------------------------------------
3144
3145function inspect.inspect(root, options)
3146 options = options or {}
3147
3148 local depth = options.depth or math.huge
3149 local newline = options.newline or '\n'
3150 local indent = options.indent or ' '
3151 local process = options.process
3152
3153 if process then
3154 root = processRecursive(process, root, {}, {})
3155 end
3156
3157 local inspector = setmetatable({
3158 depth = depth,
3159 level = 0,
3160 buffer = {},
3161 ids = {},
3162 maxIds = {},
3163 newline = newline,
3164 indent = indent,
3165 tableAppearances = countTableAppearances(root)
3166 }, Inspector_mt)
3167
3168 inspector:putValue(root)
3169
3170 return table.concat(inspector.buffer)
3171end
3172
3173setmetatable(inspect, { __call = function(_, ...) return inspect.inspect(...) end })
3174
3175return inspect
3176
3177end
3178
3179_G.ckClassProvider["com.codekingdoms.roblox.base.BaseObject"] = function()
3180local Base = ckRequire("com.codekingdoms.roblox.base.Base")
3181local logger = ckRequire("com.codekingdoms.roblox.private.logger")
3182local objectWalker = ckRequire("com.codekingdoms.roblox.private.objectWalker")
3183
3184-- @category Explorer
3185-- @field {roblox.class.Instance} object The Roblox instance that this script controls
3186-- @category Look
3187-- @field {Boolean} visible Whether the Roblox object is visible
3188-- @category Physics
3189-- @field {roblox.class.Vector3} position The position of the object
3190-- @category Physics
3191-- @field {roblox.class.Vector3} velocity The velocity of the object
3192-- @category Look
3193-- @field {roblox.class.Color3} color The current color of the object
3194-- @category Look
3195-- @field {Number} Transparency The current transparency of the object
3196-- @category Script
3197-- @field {Boolean} alive Whether the Roblox object for this script has been destroyed or not
3198
3199local BaseObject = Base:Extend(function( self, project, object )
3200
3201 Base.init( self, project, object )
3202
3203 self:_addFields()
3204 self:_bindTouched()
3205 self:_bindDestroyed()
3206 self:_addFieldValues( object )
3207
3208end)
3209
3210function BaseObject:_addFields()
3211
3212 -- Force object to be saved as a field
3213 self.object = self.object
3214 self.position = nil
3215 self.velocity = nil
3216 self.visible = nil
3217 self.color = nil
3218 self.Transparency = nil
3219
3220 self:_defineProperty('position', function()
3221
3222 if ( self.object.ClassName == "Model" ) then
3223
3224 if ( self.object.PrimaryPart ) then
3225
3226 return self.object.PrimaryPart.Position
3227
3228 else
3229
3230 return Vector3.new(0, 0, 0)
3231
3232 end
3233
3234 else
3235
3236 return self.object.Position
3237
3238 end
3239
3240 end, function( value )
3241
3242 if ( self.object.ClassName == "Model" ) then
3243
3244 if ( self.object.PrimaryPart ) then
3245
3246 self.object:MoveTo( value )
3247
3248 else
3249
3250 self.object:TranslateBy( value )
3251
3252 end
3253
3254 else
3255
3256 self.object.Position = value
3257
3258 end
3259
3260 end)
3261
3262 self:_defineProperty('velocity', function()
3263
3264 local source
3265
3266 if ( self.object.ClassName == "Model" ) then
3267
3268 source = self.object.PrimaryPart
3269
3270 else
3271
3272 source = self.object
3273
3274 end
3275
3276 local proxy = {}
3277 local accessor = {
3278 __index = function( table, key )
3279
3280 return source.Velocity[key]
3281
3282 end,
3283 __newindex = function( table, key, value )
3284
3285 local newVelocity = {
3286 X = source.Velocity.X,
3287 Y = source.Velocity.Y,
3288 Z = source.Velocity.Z
3289 }
3290 newVelocity[key] = value
3291
3292 source.Velocity = Vector3.new(newVelocity.X, newVelocity.Y, newVelocity.Z)
3293
3294 end
3295 }
3296 setmetatable(proxy, accessor)
3297
3298 return proxy
3299
3300 end, function( value )
3301
3302 if ( self.object.ClassName == "Model" ) then
3303
3304 self.object.PrimaryPart.Velocity = value
3305
3306 else
3307
3308 self.object.Velocity = value
3309
3310 end
3311
3312 end)
3313
3314
3315 self:_defineProperty('color', function()
3316
3317 return self.object.BrickColor.Color
3318
3319 end, function( value )
3320
3321 self.object.BrickColor = BrickColor.new( value )
3322
3323 end)
3324
3325 self:_defineProperty('Transparency', function()
3326
3327 return self.object.Transparency
3328
3329 end, function( value )
3330
3331 self.object.Transparency = value
3332
3333 end)
3334
3335 self:_defineVisibility()
3336
3337end
3338
3339function BaseObject:_defineVisibility()
3340
3341 self:_defineProperty('visible', function()
3342
3343 local part
3344
3345 if self.object.ClassName == "Model" then
3346
3347 part = self.object.PrimaryPart
3348
3349 else
3350
3351 part = self.object
3352
3353 end
3354
3355 if ( not part ) then
3356
3357 return true
3358
3359 end
3360
3361 if not part.Transparency then
3362
3363 return true
3364
3365 end
3366
3367 return part.Transparency < 1
3368
3369 end, function( _ )
3370
3371 -- TODO Roblox API
3372 logger:warn("Use Show and Hide to change the visibility of the object, setting visible directly has no effect!")
3373
3374 end)
3375
3376end
3377
3378function BaseObject:_bindDestroyed()
3379
3380 self.object.AncestryChanged:connect(function( obj )
3381
3382 if ( self.object.Parent == nil ) then
3383
3384 self:Destroy()
3385
3386 end
3387
3388 end)
3389
3390end
3391
3392function BaseObject:_addFieldValues( object )
3393
3394 for i, child in ipairs( object:GetChildren() ) do
3395
3396 if ( child.ClassName == "IntValue" or child.ClassName == "NumberValue" or child.ClassName == "StringValue" or child.ClassName == "BoolValue" or child.ClassName == "ObjectValue") then
3397
3398 self:_defineProperty(child.Name, function()
3399
3400 return self.object[ child.Name ].Value
3401
3402 end, function( value )
3403
3404 self.object[ child.Name ].Value = value
3405
3406 end)
3407
3408 end
3409
3410 end
3411
3412end
3413
3414--- Make this object listen to the game's heartbeat
3415-- @category Physics
3416function BaseObject:EnableUpdate()
3417
3418 local listener = function(step)
3419
3420 if self.OnUpdate and self.alive then
3421
3422 local onUpdate = function()
3423
3424 self:OnUpdate(step)
3425
3426 end
3427
3428 self:Run(onUpdate)
3429
3430 end
3431
3432 end
3433
3434 self:Run( function()
3435
3436 game:GetService('RunService').Heartbeat:connect( listener )
3437
3438 end )
3439
3440end
3441
3442function BaseObject:_bindTouched()
3443
3444 if ( self.object.ClassName == "Model" ) then
3445
3446 for i, obj in ipairs(self.object:GetChildren()) do
3447
3448 if (obj.ClassName == "Part") then
3449
3450 self:_connectTouch( obj )
3451
3452 end
3453
3454 end
3455
3456 elseif ( self.object.ClassName ~= "Tool" ) then
3457
3458 self:_connectTouch( self.object )
3459
3460 end
3461
3462end
3463
3464function BaseObject:_connectTouch( objectPart )
3465
3466 objectPart.Touched:connect(function( otherPart )
3467
3468 if self.visible and not self._destroyed then
3469
3470 local robloxPlayer = game.Players:GetPlayerFromCharacter( otherPart.Parent )
3471
3472 if robloxPlayer then
3473
3474 local classPathName = "BasePlayer"
3475
3476 if self._isLocal then
3477
3478 classPathName = "BaseLocalPlayer"
3479
3480 end
3481
3482 local playerClassPath = self._project:getLocalClassPath(self._path, classPathName)
3483
3484 local playerInstance = self:FindScript( playerClassPath, robloxPlayer )
3485
3486 if ( playerInstance and self.OnTouched ~= BaseObject.OnTouched) then
3487
3488 self:Run(function()
3489
3490 if ( self._destroyed or not self.visible ) then
3491
3492 return
3493
3494 end
3495
3496 self:OnTouched(playerInstance, objectPart)
3497
3498 end)
3499
3500 end
3501
3502 end
3503
3504 if ( self.OnCollide ~= BaseObject.OnCollide ) then
3505
3506 self:Run(self.OnCollide, otherPart , objectPart)
3507
3508 end
3509
3510 end
3511
3512 end)
3513
3514end
3515
3516-- @category Look
3517function BaseObject:Hide()
3518
3519 self.object.CanCollide = false
3520 self.object.Transparency = 1
3521
3522end
3523
3524-- @category Look
3525function BaseObject:Show()
3526
3527 self.object.CanCollide = true
3528 self.object.Transparency = 0
3529
3530end
3531
3532-- @category Player
3533-- @returns {Local(com.codekingdoms.roblox.base.BasePlayer)} The nearest Player's script object
3534function BaseObject:GetNearestPlayer( )
3535
3536 local pos = self.position
3537 local currentDistance = math.huge
3538 local nearestPlayer = nil
3539 for _, player in pairs(self:GetPlayers()) do
3540
3541 if player.alive and player.position then
3542
3543 local playerPosition = player.position
3544
3545 local dist = (playerPosition - pos).magnitude
3546 if dist <= currentDistance then
3547 nearestPlayer = player
3548 currentDistance = dist
3549 end
3550
3551 end
3552
3553 end
3554 return nearestPlayer
3555
3556end
3557
3558--- Clone the object
3559-- @category Actions
3560-- @param {roblox.class.Vector3} position The position of the cloned object
3561-- @param {com.codekingdoms.roblox.base.BaseObject} [parent] The parent of the cloned object, or the Workspace by default
3562-- @returns {Local}
3563function BaseObject:Clone( position, parent )
3564
3565 if ( parent == nil ) then
3566
3567 parent = game.Workspace
3568
3569 end
3570
3571 if ( position == nil ) then
3572
3573 position = self.position
3574
3575 end
3576
3577 local clonedObject = self.object:Clone()
3578 clonedObject.Parent = parent
3579
3580 local scriptInstances = self._project._codeFactory:instantiateChildren( clonedObject )
3581 for _, instance in ipairs( scriptInstances ) do
3582
3583 instance:Run(instance.OnCreate)
3584
3585 end
3586
3587 local objectWithCFrame = self.object
3588
3589 if objectWithCFrame.ClassName == "Model" then
3590
3591 objectWithCFrame = objectWithCFrame.PrimaryPart
3592
3593 end
3594
3595 local clonedScript = self:FindScript( self, clonedObject )
3596
3597 if objectWithCFrame then
3598
3599 local positionCFrame = CFrame.new(position)
3600 local angleCFrame = CFrame.Angles(self.object.CFrame:toEulerAnglesXYZ())
3601 clonedObject.CFrame = positionCFrame * angleCFrame
3602
3603 else
3604
3605 -- Fallback if model doesn't have a primary part
3606 clonedScript.position = position
3607
3608 end
3609
3610 return clonedScript
3611
3612end
3613
3614--- Runs when a player hits the object
3615-- @category Events
3616-- @param {Local(com.codekingdoms.roblox.base.BasePlayer)} player
3617-- @param {roblox.class.BasePart} [objectPart] The part of the object which is being touched by the player
3618-- @abstract
3619function BaseObject:OnTouched( player, objectPart )
3620
3621end
3622
3623--- Runs when another part hits the object
3624-- @category Events
3625-- @param {roblox.class.BasePart} otherPart The other part that collided with this object
3626-- @param {roblox.class.BasePart} objectPart The part of this object which collided
3627-- @abstract
3628function BaseObject:OnCollide( otherPart, objectPart )
3629
3630end
3631
3632--- Runs every frame the Roblox game updates
3633-- @category Events
3634-- @param {Number} step The amout of time passed since the last update
3635-- @abstract
3636function BaseObject:OnUpdate( step )
3637
3638end
3639
3640--- Check how far away another object is from this one
3641-- @category Physics
3642-- @param {com.codekingdoms.roblox.base.BaseObject} other The other object to check
3643-- @returns {Number}
3644function BaseObject:DistanceFrom( other )
3645
3646 return (self.position - other.position).magnitude
3647
3648end
3649
3650--- Create a new Roblox object
3651-- @category Actions
3652-- @param {roblox.enum.ClassName} type The type of the Roblox object to create
3653-- @param {roblox.class.Vector3} [position] The position of the new object, or the current position by default
3654-- @param {com.codekingdoms.roblox.base.BaseObject} [parent] The parent of the new object, or the Workspace by default
3655function BaseObject:Create( type, position, parent )
3656
3657 if ( position == nil ) then
3658
3659 position = self.position
3660
3661 end
3662
3663 local parentObject
3664
3665 if ( parent == nil ) then
3666
3667 parentObject = game.Workspace
3668
3669 else
3670
3671 parentObject = parent.object
3672
3673 end
3674
3675 local newObject = Instance.new(type)
3676
3677 -- Don't care if this fails as some roblox objects don't have a position
3678 pcall(function()
3679
3680 newObject.Position = position
3681
3682 end)
3683
3684 newObject.Parent = parentObject
3685
3686 return newObject
3687
3688end
3689
3690--- Push this object towards a particular position
3691-- @category Actions
3692-- @param {roblox.class.Vector3} position The position to push towards
3693-- @param {Number} speed The speed to push the object at
3694function BaseObject:PushTowards( position, speed )
3695
3696 local lookVector = position - self.position
3697 local velocity = lookVector / lookVector.magnitude * speed
3698 self.object.Velocity = self.object.Velocity + velocity
3699 self.object.CFrame = CFrame.new(self.position, lookVector)
3700
3701end
3702
3703--- Fire a stored projectile
3704-- @category Actions
3705-- @generic {com.codekingdoms.roblox.base.BaseObject} T
3706-- @param {Type(T)} ProjectileClass The class of the stored projectile script to use
3707-- @param {Number} speed The speed to fire the new projectile at
3708-- @param {roblox.class.Vector3} [position] The position to fire the projectile at
3709-- @returns {T} The new projectile
3710function BaseObject:Fire( ProjectileClass, speed, position )
3711
3712 if not position then
3713
3714 position = self.position + self.lookVector
3715
3716 end
3717
3718 local lookVector = position - self.position
3719 lookVector = lookVector / lookVector.magnitude
3720
3721 local storedProjectile = self:FindStoredScript(ProjectileClass)
3722
3723 if ( storedProjectile == nil ) then
3724
3725 print("Can't fire a missing projectile!", ProjectileClass._path)
3726 --BLOX-529
3727 --logger:warn("Can't fire a missing projectile!", ProjectileClass._path )
3728 return
3729
3730 end
3731
3732 local offsetDistance = 5.0
3733 local projectilePosition = self.position + lookVector * offsetDistance
3734 local projectile = storedProjectile:Clone(projectilePosition)
3735 projectile:PushTowards( position, speed )
3736
3737 return projectile
3738
3739end
3740
3741return BaseObject
3742
3743end
3744
3745_G.ckClassProvider["space.codekingdoms.lovelyrat04.zombiesmash.Gun"] = function()
3746local BaseLocalTool = ckRequire("com.codekingdoms.roblox.base.BaseLocalTool")
3747local Bullet = ckRequire("space.codekingdoms.lovelyrat04.zombiesmash.Bullet")
3748local Gun = BaseLocalTool:Extend()
3749
3750
3751
3752function Gun:OnActivate()
3753
3754 self:Fire(Bullet, 300)
3755
3756end
3757
3758return Gun
3759end
3760
3761_G.ckClassProvider["com.codekingdoms.roblox.private.Project"] = function()
3762local class = ckRequire("com.codekingdoms.roblox.private.class")
3763local Strings = ckRequire("com.codekingdoms.roblox.lib.Strings")
3764
3765local Project = class( function( self, gameData, codeFactory )
3766
3767 self.name = gameData.projectData.name
3768 self.ckUsername = gameData.ckUsername
3769 self.ckUrl = gameData.ckUrl
3770 self._info = gameData.projectData
3771 self._scripts = {}
3772 self._uid = 1
3773 self._codeFactory = codeFactory
3774 self:initClassHierarchy()
3775 _G.projectInstance = self
3776
3777end)
3778
3779function Project:initClassHierarchy()
3780
3781 local subclassPaths = {}
3782
3783 for _, file in pairs(self._info.files) do
3784
3785 local superclassPath = self:getQualifiedName( file.superclass )
3786 local classPath = self:getQualifiedName( file.path )
3787
3788 if not subclassPaths[ superclassPath ] then
3789
3790 subclassPaths[ superclassPath ] = {}
3791
3792 end
3793
3794 table.insert( subclassPaths[ superclassPath ], classPath )
3795
3796 end
3797
3798 self._subclassPaths = subclassPaths
3799
3800end
3801
3802function Project:AddEvent( name )
3803
3804
3805end
3806
3807-- @protected
3808function Project:addScript( instance )
3809
3810 -- TODO [BLOX-529]
3811 -- Though this shouldn't really print in normal case, only for debugging
3812 --print("CK - Adding Script", self.name, instance._classpath )
3813
3814 instance._id = self._uid
3815
3816 if ( self._scripts[instance._classpath] == nil ) then
3817
3818 self._scripts[instance._classpath] = {}
3819
3820 end
3821
3822 self._scripts[instance._classpath][instance._id] = instance
3823
3824 self._uid = self._uid + 1
3825
3826end
3827
3828-- @protected
3829function Project:removeScript( instance )
3830
3831 self._scripts[instance._classpath][instance._id] = nil
3832
3833end
3834
3835-- Returns the closest local class extending name to the path provided
3836-- e.g. For getLocalClassPath("/Checkpoint/CheckpointTile", "BasePlayer")
3837-- will return "/Checkpoint/CheckpointPlayer" for Ninja Obby
3838-- @protected
3839function Project:getLocalClassPath( path, name )
3840
3841 local bestMatchFile = nil
3842 local bestMatchProximity = 0
3843
3844 local pathParts = Strings.Split(path, '/')
3845
3846 -- Collect all files which extend name
3847 for i,file in pairs(self._info.files) do
3848
3849 if ( file.name == name or file.superclass == name ) then
3850
3851 local filePathParts = Strings.Split(file.path, '/')
3852 local fileProximity = 0
3853
3854 -- Don't compare names
3855 filePathParts[#filePathParts] = nil
3856
3857 for j, part in ipairs( filePathParts ) do
3858
3859 if ( part == pathParts[j] ) then
3860
3861 fileProximity = fileProximity + 1
3862
3863 else
3864
3865 fileProximity = -1
3866 break
3867
3868 end
3869
3870 end
3871
3872 --print("Found candidate in project", file.path, "with proximity", fileProximity )
3873
3874 if ( fileProximity >= bestMatchProximity ) then
3875
3876 bestMatchFile = file
3877 bestMatchProximity = fileProximity
3878
3879 end
3880
3881 end
3882
3883 end
3884
3885 if ( bestMatchFile ) then
3886
3887 return bestMatchFile.path
3888
3889 end
3890
3891 -- Return default player if none in the project
3892 if ( name == "BasePlayer" or name == "BaseLocalPlayer" ) then
3893
3894 return name
3895
3896 end
3897
3898end
3899
3900function Project:getExtendingClassName( className )
3901
3902 -- Classes in our API do not call each other recursively which
3903 -- means that they do not need to be monkey-patched with a
3904 -- fabricated Extend method as user class files may need to be.
3905 -- We also don't have the class hierarchy available as information
3906 -- in the plugin to do this!
3907 if ( not Strings.StartsWith(className, 'space')) then
3908 return
3909 end
3910
3911 local pathParts = Strings.Split(className, '.')
3912
3913 local filePath = ''
3914
3915 -- 1 .2 .3 .4 .5
3916 -- space.codekingdoms.userUrl.projectName.folderPath
3917 local folderPathStartIndex = 5
3918
3919 -- Get the user folder path for the className passed in
3920 for i = folderPathStartIndex, #pathParts do
3921
3922 filePath = filePath .. "/" .. pathParts[i]
3923
3924 end
3925
3926 -- Find the user file object from the folder path
3927 for _,file in pairs(self._info.files) do
3928
3929 if ( file.path == filePath ) then
3930 -- If the file extends a class the user has written then
3931 -- we build its class name from the userUrl and projectName
3932 -- and return that
3933 if ( Strings.StartsWith(file.superclass, '/')) then
3934 local userUrl = pathParts[3]
3935 local projectName = pathParts[4]
3936 return 'space.codekingdoms.' .. userUrl .. '.' .. projectName .. string.gsub( file.superclass, "/", "." )
3937 else
3938 -- Otherwise the superclass field refers to a base class
3939 -- in the CK Lua API
3940 return 'com.codekingdoms.roblox.base.' .. file.superclass
3941 end
3942
3943 end
3944
3945 end
3946
3947end
3948
3949function Project:getQualifiedName( path )
3950
3951 local qualifiedName
3952
3953 if ( Strings.StartsWith(path, "/") ) then
3954
3955 qualifiedName = "space.codekingdoms." .. self.ckUrl .. "." .. self.name .. string.gsub(path, "/", ".")
3956
3957 else
3958
3959 -- Project paths which can refer to an imported API file do so without a forward slash
3960 qualifiedName = "com.codekingdoms.roblox.base." .. path
3961
3962 end
3963
3964 return qualifiedName
3965
3966end
3967
3968function Project:findLocalPlayerScripts()
3969
3970 return self:findScriptsForClassPath("com.codekingdoms.roblox.base.BaseLocalPlayer", true)
3971
3972end
3973
3974
3975function Project:findScriptsForObject( Class, object, recursive, findOnlyOne )
3976
3977 -- There are still a couple of places where Class can be passed in as a string for the classpath,
3978 -- so handle that here.
3979 local classPath = (type(Class) == 'string' ) and self:getQualifiedName( Class ) or Class._classpath
3980 local allClassScripts = self:findScriptsForClassPath( classPath, true )
3981 return self:filterScriptsForObject( allClassScripts, object, recursive, findOnlyOne )
3982
3983end
3984
3985function Project:filterScriptsForObject( allClassScripts, object, recursive, findOnlyOne )
3986
3987 local scriptsForObject = {}
3988
3989 -- Find scripts attached to object.
3990 for _, script in ipairs( allClassScripts ) do
3991
3992 local scriptIsMatch = ( not object ) or ( script.object == object )
3993
3994 if scriptIsMatch then
3995
3996 table.insert( scriptsForObject, script )
3997
3998 if findOnlyOne then
3999
4000 return scriptsForObject
4001
4002 end
4003
4004 end
4005
4006 end
4007
4008 if recursive and object then
4009
4010 -- Recursively find the scripts attached to descendants of object.
4011 for _, child in pairs( object:GetChildren() ) do
4012
4013 local childScripts = self:filterScriptsForObject( allClassScripts, child, recursive, findOnlyOne )
4014
4015 if findOnlyOne and #childScripts > 0 then
4016
4017 return childScripts
4018
4019 end
4020 for _, script in ipairs( childScripts ) do
4021
4022 table.insert( scriptsForObject, script )
4023
4024 end
4025
4026 end
4027
4028 end
4029
4030 return scriptsForObject
4031
4032end
4033
4034function Project:findScriptsForClassPath( classpath, includeSubclasses )
4035
4036 local scripts = {}
4037 local scriptsForClassPath = self._scripts[ classpath ] or {}
4038
4039 for _, script in pairs( scriptsForClassPath ) do
4040
4041 table.insert( scripts, script )
4042
4043 end
4044
4045 local subclasses = self._subclassPaths[ classpath ] or {}
4046
4047 if ( includeSubclasses and #subclasses > 0 ) then
4048
4049 -- Find all scripts instances for each of the subclasses.
4050 for _, subclassPath in ipairs( subclasses ) do
4051
4052 local subclassScripts = self:findScriptsForClassPath( subclassPath, true )
4053
4054 for _, script in ipairs( subclassScripts ) do
4055
4056 table.insert( scripts, script )
4057
4058 end
4059
4060 end
4061
4062 end
4063
4064 return scripts
4065
4066end
4067
4068local eventHandlersByEventName = {}
4069local RunService = game:GetService("RunService")
4070
4071--- Add a particular event that can be used to communicate between the client and server
4072--- @ServerOnly
4073function Project:AddEvent( name ) --: string => void
4074 if(RunService:IsServer()) then
4075
4076 local newEvent = Instance.new("RemoteEvent", game.ReplicatedStorage)
4077 newEvent.Name = name
4078
4079 eventHandlersByEventName[name] = {}
4080 else
4081 print("Project:AddEvent can only be called from a non-local script. Also make sure you are testing with a local server and clients!")
4082 end
4083end
4084
4085--- Listens to a specific event and calls a handler function when it fires
4086function Project:ListenToEvent(name, handler) --: (string, ...any => void ) => void
4087
4088 local event = game.ReplicatedStorage:WaitForChild(name)
4089
4090 local eventHandlers = eventHandlersByEventName[name]
4091 table.insert(eventHandlers, handler)
4092
4093 if (RunService:IsClient()) then
4094 event.OnClientEvent:Connect(handler)
4095 else
4096 event.OnServerEvent:Connect(handler)
4097 end
4098end
4099
4100--- Fire an event by name and pass any number of args
4101function Project:FireEvent( name, ... ) --: string, ...any => void
4102 if(RunService:IsServer()) then
4103 for _, handler in pairs(eventHandlersByEventName[name]) do
4104 handler(game.Players.LocalPlayer, ... )
4105 end
4106 return
4107 end
4108 local event = game.ReplicatedStorage:WaitForChild(name)
4109 print("FireEvent with name " .. name .. " sent with arguments", ...)
4110 event:FireServer(...)
4111end
4112
4113return Project
4114end
4115
4116_G.ckClassProvider["space.codekingdoms.lovelyrat04.zombiesmash.Zombie"] = function()
4117local BaseLocalCharacter = ckRequire("com.codekingdoms.roblox.base.BaseLocalCharacter")
4118local ZombieSpawner = ckRequire("space.codekingdoms.lovelyrat04.zombiesmash.ZombieSpawner")
4119local PlayerCamera = ckRequire("space.codekingdoms.lovelyrat04.zombiesmash.PlayerCamera")
4120local Colors = ckRequire("com.codekingdoms.roblox.lib.Colors")
4121local Strings = ckRequire("com.codekingdoms.roblox.lib.Strings")
4122local Input = ckRequire("com.codekingdoms.roblox.lib.Input")
4123local Zombie = BaseLocalCharacter:Extend()
4124
4125-- @field {Number} health
4126-- @param {roblox.class.BasePart} otherPart
4127-- @param {roblox.class.BasePart} objectPart
4128function Zombie:OnCollide(otherPart, objectPart)
4129
4130 if (otherPart.Name == "Bumper" and otherPart.Velocity.magnitude >= 7) then
4131
4132 self:Kill()
4133
4134 elseif (otherPart.Name == "Bullet") then
4135
4136 self:Kill()
4137
4138 end
4139
4140end
4141
4142function Zombie:Kill()
4143
4144 self.health = 0
4145
4146end
4147
4148function Zombie:OnCreate()
4149
4150 while (true) do
4151
4152 wait(2)
4153 local target = self:GetNearestPlayer()
4154 if (target) then
4155
4156 local torso = target:GetTorso()
4157 self:GetHumanoid():MoveTo(torso.Position, torso)
4158
4159 end
4160
4161 end
4162
4163end
4164-- @param {space.codekingdoms.lovelyrat04.zombiesmash.PlayerCamera} player
4165function Zombie:OnTouched(player)
4166
4167 if (self.alive) then
4168
4169 player.health = 0
4170
4171 end
4172
4173end
4174
4175function Zombie:OnDeath()
4176
4177 local zombieSpawner = self:FindScript(ZombieSpawner).Work
4178
4179end
4180
4181return Zombie
4182end
4183
4184
4185local CodeFactory = ckRequire("com.codekingdoms.roblox.private.CodeFactory")
4186local factory = CodeFactory.new()
4187factory:start()
4188_G.codeFactory = factory
4189
4190return factory