Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
MTA 1.4 classlib-rc
-- 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 instanciate 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