• There is NO official Otland's Discord server and NO official Otland's server list. The Otland's Staff does not manage any Discord server or server list. Moderators or administrator of any Discord server or server lists have NO connection to the Otland's Staff. Do not get scammed!

Lua About Metatables

The Matrix

Banned User
Joined
Dec 29, 2014
Messages
22
Reaction score
4
I wanted to construct metatables similar to that of which exists in 1.2 but instead use 8.6 functions. I guess an example would be more helpful, I'll use the heal friend spell from both versions as an example.

8.6 code, heal friend
Code:
local combat = createCombatObject()
setCombatParam(combat, COMBAT_PARAM_TYPE, COMBAT_HEALING)
setCombatParam(combat, COMBAT_PARAM_EFFECT, CONST_ME_MAGIC_BLUE)
setCombatParam(combat, COMBAT_PARAM_AGGRESSIVE, false)
setCombatParam(combat, COMBAT_PARAM_DISPEL, CONDITION_PARALYZE)
setHealingFormula(combat, COMBAT_FORMULA_LEVELMAGIC, 5, 5, 10, 14)

function onCastSpell(cid, var)
    return doCombat(cid, combat, var)
end

1.2 code heal friend
Code:
local combat = Combat()
combat:setParameter(COMBAT_PARAM_TYPE, COMBAT_HEALING)
combat:setParameter(COMBAT_PARAM_EFFECT, CONST_ME_MAGIC_GREEN)
combat:setParameter(COMBAT_PARAM_DISPEL, CONDITION_PARALYZE)
combat:setParameter(COMBAT_PARAM_AGGRESSIVE, false)

function onGetFormulaValues(player, level, maglevel)
    local min = (level / 5) + (maglevel * 6.3) + 45
    local max = (level / 5) + (maglevel * 14.4) + 90
    return min, max
end

combat:setCallback(CALLBACK_PARAM_LEVELMAGICVALUE, "onGetFormulaValues")

function onCastSpell(creature, variant)
    creature:getPosition():sendMagicEffect(CONST_ME_MAGIC_BLUE)
    return combat:execute(creature, variant)
end

Now my code
The Combat metatable, it isn't complete since I am still learning :)
Code:
-- this would have its own file and would be included globally
function Combat()
    local c = setmetatable({ __index = createCombatObject() },{
        __call = function(cls, ...)
            return cls.new(...)
        end,
    })
 
    function c.new()
        local self = setmetatable({}, c)
        return self
    end
 
    function c:setCombatParam(param, type)
        return setCombatParam(self, param, type)
    end
 
    function c:setHealingFormula(formula, ...)
        return setHealingFormula(self, formula, ...)
    end
 
    -- still working on what is a call back function...
    function c:setCallback(id, callback)
     
    end
 
    function c:execute(cid, var)
        return doCombat(cid, self, var)
    end
    return c
end

My interpretation of the script
Code:
local combat = Combat()
combat:setCombatParam(COMBAT_PARAM_TYPE, COMBAT_HEALING)
combat:setCombatParam(COMBAT_PARAM_EFFECT, CONST_ME_MAGIC_BLUE)
combat:setCombatParam(COMBAT_PARAM_AGGRESSIVE, false)
combat:setCombatParam(COMBAT_PARAM_DISPEL, CONDITION_PARALYZE)
combat:setHealingFormula(COMBAT_FORMULA_LEVELMAGIC, 5, 5, 10, 14)

function onCastSpell(cid, var)
    return combat:execute(cid, var)
end

The above is just of an example of what I am attempting to do, my real question is, if I were to pass cid which is of course userdata to a metatable would it return the user id or are there more steps involved?

example
Code:
function genericMetatable(cid)
    local g = {cid} or {}
    local m = setmetatable({ __index = g },{
        __call = function(cls, ...)
            return cls.new(...)
        end,
    })
 
    function m.new()
        local self = setmetatable({}, m)
        return self
    end
 
    function m:getId()
        return self.__index[1]
    end
 
    return m
end

Code:
Player = genericMetatable(cid)

print(Player:getId())
 
I'm pretty sure you are doing this wrong. Every time you make a new object you are going to define all the functions again for each object you create.

Code:
Combat = {}

setmetatable(Combat, {__call = function() return setmetatable({object = createCombatObject()}, {__index=Combat}) end})

function Combat:setCombatParam(param, type)
    return setCombatParam(self.object, param, type)
end

function Combat:setHealingFormula(formula, ...)
    return setHealingFormula(self.object, formula, ...)
end

function Combat:execute(cid, var)
    return doCombat(cid, self.object, var)
end

local combat = Combat()
combat:setCombatParam(COMBAT_PARAM_TYPE, COMBAT_HEALING)
combat:setCombatParam(COMBAT_PARAM_EFFECT, CONST_ME_MAGIC_BLUE)
combat:setCombatParam(COMBAT_PARAM_AGGRESSIVE, false)
combat:setCombatParam(COMBAT_PARAM_DISPEL, CONDITION_PARALYZE)
combat:setHealingFormula(COMBAT_FORMULA_LEVELMAGIC, 5, 5, 10, 14)

function onCastSpell(cid, var)
    return combat:execute(cid, var)
end

Thats how I would do it.

And for Player:

Code:
Player = {}

setmetatable(Player, {__call = function(_, cid) return setmetatable({cid=cid}, {__index=Player}) end})

function Player:getId()
    return self.cid
end

local player = Player(10)
print(player:getId())
 
Last edited:
You don't need to define the methods for every new object you create, you only need to do it once. I've made a helper function that you can use to implement the classes from TFS 1.2:

Code:
function newClass(constructor)
    local methods = { }
    local MT = {__index = methods}

    local call = function(self, ...)
        local obj = constructor(...)
        if not obj then
            return nil
        end

        return setmetatable(obj, MT)
    end

    return setmetatable(methods, {__call = call})
end

You call this function passing to it a constructor that will be called whenever the class table is called, e.g: Player("Name") or Player(cid). This is an example for the player class:

Code:
Player = newClass(function(param)
    if type(param) == 'string' then
        local cid = getPlayerByNameWildcard(param)
        if not cid then
            return nil
        end
        return {cid = cid}
    elseif type(param) == 'number' then
        if not getPlayerGUID(param) then
            return nil
        end
        return {cid = param}
    else
        return nil
    end
end)

function Player:getLevel()
    return getPlayerLevel(self.cid)
end

function Player:getItemCount(...)
    return getPlayerItemCount(self.cid, ...)
end

Just define your classes on a lib file and you will be able to use them anywhere.
 
Back
Top