Plugin quad class local module = {}; -- local exportItemMeta = { -- __index = function (self,key) -- local methods = self.__methods; -- local events = self.__events; -- return (methods and methods[key]) or (events and events[key]) or (self.getters[key](self.__this)); -- end; -- __newindex = function (self,key,value) -- self.__setters[key](self.__this,value); -- end; -- }; function module.init(shared) local new = {}; local bind = shared.event.bind; local addObject = shared.store.addObject; local storeNew = shared.store.new; function new.getHolder(item) return (type(item) == "table") and (item._holder or item.holder or item.__holder) or item; end local getHolder = new.getHolder; -- make object that from instance, class and more function new.make(ClassName,...) -- render object -- make thing local item; local classOfClassName = type(ClassName); if classOfClassName == "string" then -- if classname is a string value, cdall instance.new for making new roblox instance item = Instance.new(ClassName); elseif classOfClassName == "function" then -- if classname is a function value, call it for making new object (OOP object) item = ClassName(); elseif classOfClassName == "table" then -- if classname is a calss what is included new function, call it for making new object (object) local func = ClassName.new or ClassName.New or ClassName.__new; if ClassName.__noSetup then -- if is support initing props local childs,props = {},{...}; local parsed; for iprop = #props,1,-1 do local prop = props[iprop]; for i,v in ipairs(prop) do childs[i] = ((iprop == 1) and v or v:Clone()); prop[i] = nil; end if next(prop) then -- there are (key/value)s if parsed then for i,v in pairs(prop) do parsed[i] = v; end else parsed = prop; end end end item = func(parsed); local holder = getHolder(item); for _,v in ipairs(childs) do v.Parent = holder; end return item; end item = func(); end if not item then -- if cannot make new object, ignore this call local str = tostring(ClassName); print(("fail to make item '%s', did you forget to checking the class '%s' is exist?"):format(str,str)); end -- set property and adding child and binding functions local holder = getHolder(item); -- to __holder or holder or it self (for tabled) -- for iprop,prop in ipairs({...}) do for iprop = select("#",...),1,-1 do local prop = select(iprop,...); for index,value in pairs(prop) do local valueType = typeof(value); local indexType = typeof(index); -- child if indexType == "string" and valueType == "table" and value.t == "reg" then -- register (bind to store event) -- store binding local with = value.wfunc; local set = value.store[value.key]; if set then if with then set = with(set); end item[index] = set; else local dset = value.dvalue; if dset then item[index] = dset; end end value.register(function (newValue,store) if with then newValue = with(newValue,item,store); end item[index] = newValue; end); elseif (valueType == "function" or valueType == "table") and bind(item,index,value,valueType) then -- connect event -- event binding elseif indexType == "string" then -- prop set item[index] = value; -- set property elseif indexType == "number" then -- object -- child object ((iprop == 1) and value or value:Clone()).Parent = holder; end end end return item; end local make = new.make; -- import quad object function new.import(ClassName,defaultProperties) -- make new quad class object local this = defaultProperties or {}; setmetatable(this,{ __call = function (self,prop) if type(prop) == "string" then local lastName = prop; return function (nprop) nprop.Name = lastName; local item = make(ClassName,nprop); addObject(lastName,item); return item; end; end return make(ClassName,prop,this); end; }); return this; end -- function new.export(newFn,setters,getters,methods,events) -- make quad importable class -- return function () -- return setmetatable({ -- __this = newFn(); -- __setters = setters; -- __getters = getters; -- __methods = methods; -- __events = events; -- },exportItemMeta); -- end; -- end -- set module calling function setmetatable(new,{ __call = function (self,...) return new.import(...); end; }); -- make class function new.extend() local this = {}; --- make new object function this.new(prop) -- make metatable prop = storeNew(prop); local parent = prop and (prop.Parent or prop.parent); local self = {__prop = prop; __parent = parent}; local init = this.init; if init then init(self,prop); end setmetatable(self,this); -- render that local object = self:render(prop); rawset(self,"__object",object); rawset(self,"__holder",object); if parent then rawset(self,"__parent",parent); object.Parent = getHolder(parent); end return self; end --- re-render object (if some props chaged, call this to render again) function this:update() -- swap object local object = rawget(self,"__object"); -- get last instance local parent = rawget(self,"__parent") or (object and object.Parent); -- date parent local lastObject = object; if lastObject then lastObject.Parent = nil; end object = self:render(self.__prop); -- new instance rawset(self,"__holder",object); rawset(self,"__object",object); object.Parent = getHolder(parent); -- update parent if lastObject then -- remove old instance self:Destroy(lastObject); end end -- define destroy method function this:Destroy(object) local unload = rawget(self,"unload"); object = object or rawget(self,"__object"); if object then if unload then unload(self,object); else local destroy = (object.Destroy or object.destroy); if destroy then pcall(destroy,object); end end end end --- link to new function this.__call(self,...) return (self.new or self.New or self.__new)(...); end; -- indexer (getter) function this:__index(k) if k == "destroy" then return self.Destroy; end local getter = this.getter[k]; if getter then return getter(self,rawget(self,"__object")); end -- from this (have more priority) local fromThis = this[k]; if fromThis then return fromThis; end -- from prop, not start with '_' (private value) if k:sub(1,1) ~= "_" then return self.__prop[k]; end end --- new indexer (setter) function this:__newindex(k,v) -- from setter local setter = this.setter[k]; if setter then return setter(self,v,rawget(self,"__object")); end -- is private (self value) if k == "holder" or k:sub(1,1) == "_" then return rawset(self,k,v); end -- prop update self.__prop[k] = v; if this.updateTriggers[k] then self:update(); end end this.getter = {}; this.setter = { Parent = function(self,parent,object) rawset(self,"__parent",parent); if object then object.Parent = getHolder(parent); end end; }; this.updateTriggers = {}; this.__noSetup = true; return this; end return new; end return module; event local module = {}; function module.init(shared) local new = {}; local unpack = table.unpack; local wrap = coroutine.wrap; local prefix = "Event::"; local special = { ["Property::(.+)"] = function (this,func,property) this.GetPropertyChangedSignal(property):Connect(function() func(this,this[property]); end); end; ["CreatedSync::"] = function (this,func) func(this); end; ["Created::"] = function (this,func) wrap(func)(this); end; }; local prefixLen = #prefix; setmetatable(new,{ __call = function(self,eventName) return prefix .. eventName; end; }); -- try binding, if key dose match with anything, ignore call function new.bind(this,key,func,typefunc) -- if is advanced binding (self setted) typefunc = typefunc or type(func); local self; if typefunc == "table" then self = func.self; func = func.func; end -- check prefix if key:sub(1,prefixLen) ~= prefix then return; elseif not func then return true; end key = key:sub(prefixLen + 1,-1); -- find special bindings for specKey,specFunc in pairs(special) do local find = {key:match(specKey)}; if #find ~= 0 then specFunc(this,func,unpack(find)); return true; end end -- binding normal events local event = this[key]; if event then event:Connect(function(...) func(self or this,...); end); return true; end end -- property changed binding local prefixProperty = prefix .. "Property::"; function new.prop(name) return prefixProperty .. name; end -- when created binding new.created = prefix .. "Created::"; new.createdSync = prefix .. "CreatedSync::"; return new; end return module; mount local module = {}; function module.init(shared) local new = {}; local mount = {}; mount.__index = mount; function mount:unmount() local this = self.this; local typeThis = type(this); if typeThis == "userdata" then this:Destroy(); elseif typeThis == "table" then pcall(function() this.Parent = nil; end); local destroyThis = this.Destroy; if destroyThis then pcall(destroyThis); end end end -- we should add plugin support function new.mount(to,this) this.Parent = to; return setmetatable({to = to,this = this},mount); end setmetatable(new,{ __call = function (self,...) return new.mount(...); end; }); return new; end return module; require -- remove the roblox's require system, -- now, we can call modules with string path if (not workspace) and (not game) and (not script) then return; end return function (query) local typeQuery = type(query); if typeQuery == "string" then local object = script.Parent; query = query:gsub("/","."); -- local index = 0; query:gsub("[^%.]+",function (this) -- index = index + 1; -- if index == 1 and this == "src" then -- return; -- end local lastObject = object; object = object[this]; if not object then object = lastObject.libs error(("[require] : object %s was not found from this worktree, require failed"):format(query)); end end); return require(object); elseif typeQuery == "userdata" then return require(query); end end; store