-- Developer: sbx320 | |
-- License: MIT | |
-- Github Repos: https://github.com/sbx320/lua_utils | |
--// classlib | |
--|| A library providing several tools to enhance OOP with MTA and Lua | |
--\\ | |
SERVER = triggerServerEvent == nil | |
CLIENT = not SERVER | |
DEBUG = DEBUG or false | |
function new(class, ...) | |
assert(type(class) == "table", "first argument provided to new is not a table") | |
-- DEBUG: Validate that we are not instanciating a class with pure virtual methods | |
if DEBUG then | |
for k, v in pairs(class) do | |
assert(v ~= pure_virtual, "Attempted to instantiate a class with an unimplemented pure virtual method ("..tostring(k)..")") | |
end | |
end | |
local instance = setmetatable( { }, | |
{ | |
__index = class; | |
__super = { class }; | |
__newindex = class.__newindex; | |
__call = class.__call; | |
__len = class.__len; | |
__unm = class.__unm; | |
__add = class.__add; | |
__sub = class.__sub; | |
__mul = class.__mul; | |
__div = class.__div; | |
__pow = class.__pow; | |
__concat = class.__concat; }) | |
-- Call derived constructors | |
local callDerivedConstructor; | |
callDerivedConstructor = function(self, instance, ...) | |
for k, v in pairs(super(self)) do | |
if rawget(v, "virtual_constructor") then | |
rawget(v, "virtual_constructor")(instance, ...) | |
end | |
local s = super(v) | |
if s then callDerivedConstructor(s, instance, ...) end | |
end | |
end | |
callDerivedConstructor(class, instance, ...) | |
-- Call constructor | |
if rawget(class, "constructor") then | |
rawget(class, "constructor")(instance, ...) | |
end | |
instance.constructor = false | |
return instance | |
end | |
function delete(self, ...) | |
if self.destructor then --if rawget(self, "destructor") then | |
self:destructor(...) | |
end | |
-- Prevent the destructor to be called twice | |
self.destructor = false | |
local callDerivedDestructor; | |
callDerivedDestructor = function(parentClasses, instance, ...) | |
for k, v in pairs(parentClasses) do | |
if rawget(v, "virtual_destructor") then | |
rawget(v, "virtual_destructor")(instance, ...) | |
end | |
local s = super(v) | |
if s then callDerivedDestructor(s, instance, ...) end | |
end | |
end | |
callDerivedDestructor(super(self), self, ...) | |
end | |
function super(self) | |
assert(not isElement(self), "Cannot get the superclass of an element") -- at least: not yet | |
local metatable = getmetatable(self) | |
if metatable then return metatable.__super | |
else | |
return {} | |
end | |
end | |
function inherit(from, what) | |
assert(from, "Attempt to inherit a nil table value") | |
if not what then | |
local classt = setmetatable({}, { __index = _inheritIndex, __super = { from } }) | |
if from.onInherit then | |
from.onInherit(classt) | |
end | |
return classt | |
end | |
local metatable = getmetatable(what) or {} | |
local oldsuper = metatable and metatable.__super or {} | |
table.insert(oldsuper, 1, from) | |
metatable.__super = oldsuper | |
metatable.__index = _inheritIndex | |
return setmetatable(what, metatable) | |
end | |
function _inheritIndex(self, key) | |
for k, v in pairs(super(self) or {}) do | |
if v[key] then return v[key] end | |
end | |
return nil | |
end | |
function instanceof(self, class, direct) | |
for k, v in pairs(super(self)) do | |
if v == class then return true end | |
end | |
if direct then return false end | |
local check = false | |
-- Check if any of 'self's base classes is inheriting from 'class' | |
for k, v in pairs(super(self)) do | |
check = instanceof(v, class, false) | |
end | |
return check | |
end | |
function pure_virtual() | |
error("Function implementation missing") | |
end | |
-- Magic to allow MTA elements to be used as data storage | |
-- e.g. localPlayer.foo = 12 | |
oop = {} | |
oop.mta_metatable = {} | |
oop.elementInfo = setmetatable({}, { __mode = "k" }) | |
oop.getMTATypeMetatable = function(t) | |
local element = false | |
if t == "player" then return debug.getmetatable(localPlayer or getRandomPlayer()) | |
elseif t == "vehicle" then element = createVehicle(411, 0, 0, 0) | |
elseif t == "colshape" then element = createColCircle(0, 0, 1) | |
elseif t == "element" then element = createElement("oopelement") | |
elseif t == "marker" then element = createMarker(0, 0, 0, "ring") | |
elseif t == "object" then element = createObject(1337, 0, 0, 0) | |
elseif t == "ped" then element = createPed(0, 0, 0, 0) | |
elseif t == "pickup" then element = createPickup(0, 0, 0, 0, 0) | |
elseif t == "radarArea" then element = createRadarArea(0, 0, 1, 1) | |
elseif t == "water" then element = createWater(0, 0, 0, 2, 2, 2, 4, 4, 4) | |
elseif t == "weapon" then element = createWeapon("m4", 0, 0, 0) | |
elseif SERVER then | |
if t == "team" then element = createTeam("oopteam") end | |
elseif CLIENT then | |
if t == "sound" then element = playSFX("feet", 1, 1) | |
elseif t == "camera" then return debug.getmetatable(getCamera()) | |
elseif t == "effect" then element = createEffect("fire", 0, 0, 0) | |
-- todo: check if GUI elements have OOP | |
end | |
end | |
assert(element, t) | |
local mt = debug.getmetatable(element) | |
destroyElement(element) | |
return mt | |
end | |
oop.prepareClass = function(name) | |
local mt = oop.mta_metatable[name] | |
mt.__mtaindex = mt.__index | |
mt.__mtanewindex = mt.__newindex | |
mt.__index = function(self, key) | |
local value = debug.getmetatable(self).__mtaindex(self, key) | |
if value ~= nil then | |
return value | |
end | |
return (oop.elementInfo[self] or {})[key] | |
end | |
mt.__newindex = function(self, key, value) | |
local mt = debug.getmetatable(self) | |
if mt.__set[key] ~= nil then | |
debug.getmetatable(self).__mtanewindex(self, key, value) | |
return | |
end | |
if not oop.elementInfo[self] then | |
oop.elementInfo[self] = {} | |
end | |
oop.elementInfo[self][key] = value | |
end | |
end | |
oop.initClasses = function() | |
oop.mta_metatable["vehicle"] = oop.getMTATypeMetatable("vehicle") | |
oop.mta_metatable["colshape"] = oop.getMTATypeMetatable("colshape") | |
oop.mta_metatable["element"] = oop.getMTATypeMetatable("element") | |
oop.mta_metatable["marker"] = oop.getMTATypeMetatable("marker") | |
oop.mta_metatable["object"] = oop.getMTATypeMetatable("object") | |
oop.mta_metatable["ped"] = oop.getMTATypeMetatable("ped") | |
oop.mta_metatable["pickup"] = oop.getMTATypeMetatable("pickup") | |
oop.mta_metatable["radarArea"] = oop.getMTATypeMetatable("radarArea") | |
oop.mta_metatable["water"] = oop.getMTATypeMetatable("water") | |
oop.mta_metatable["weapon"] = oop.getMTATypeMetatable("weapon") | |
if SERVER then | |
oop.mta_metatable["team"] = oop.getMTATypeMetatable("team") | |
end | |
if CLIENT then | |
oop.mta_metatable["sound"] = oop.getMTATypeMetatable("sound") | |
oop.mta_metatable["camera"] = oop.getMTATypeMetatable("camera") | |
oop.mta_metatable["effect"] = oop.getMTATypeMetatable("effect") | |
end | |
for k, v in pairs(oop.mta_metatable) do | |
oop.prepareClass(k) | |
end | |
if SERVER then | |
if getPlayerCount() >= 1 then | |
oop.initPlayerClass() | |
else | |
addEventHandler("onPlayerConnect", root, oop.initPlayerClass) | |
end | |
else | |
oop.initPlayerClass() | |
end | |
end | |
oop.initPlayerClass = function() | |
oop.mta_metatable["player"] = oop.getMTATypeMetatable("player") | |
removeEventHandler("onPlayerConnect", root, oop.initPlayerClass) | |
oop.prepareClass("player") | |
end | |
oop.initClasses() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment