2
votes

I am having trouble using a metatable to create new monsters for a game, I can create an exact copy but I can't generate a new rat or lizard for example with a new id.

local monsters = {
  rat = {
   name = "a rat",
   id = 1,
   health = 5,
   }
  lizard = {
   name = "a lizard",
   id = 1,
   health = 8,
   }
 }

local metamonsters = {__index = monsters}
setmetatable(monsters, metamonsters)

function monsters:new(o)
 setmetatable(o, metamonsters)
 return o
end 

local testrat = monsters:new({rat})         
print(testrat.name, testrat.id)

This creates a new rat under the variable testrat, and the console prints "a rat" and "1". I can't work out how to specify a new id number for the rat when its created. Any help would be appreciated, metatables are confusing me like mad!

1
You aren't using testrat anywhere in that code. You are printing out the singleton monsters.rat on that last line. That being said testrat is going to be a weird object. Not just the rat table but a table containing a rat table at index 1. Not that none of that has anything to do with metatables in the slightest.Etan Reisner
If you want to create new IDs each time you create a new monster then you need to assign the id in the new function and not hard-code it into the rat singleton (you could use the rat singleton as the current id counter if you wanted to keep a counter for each monster type individually though I suppose).Etan Reisner
Sorry that was a mistake in my copying of the code I changed the print line to testrat. As mentioned in the full version it does print the "default" details from the linked metatable. I will have a look at changing the new function and see if I can get it to work with assigning a new ID. Thanks.mixedfr00tjam
You know, local testrat = monsters:new({rat}) is equivalent to local testrat = monsters:new {}, because there is no global or local rat.Deduplicator
I am very confused then I thought using {rat} selected the rat from the monsters table so that testrat uses the rat's stats rather than the lizard's?mixedfr00tjam

1 Answers

1
votes

You need to begin at the basics of how classes in Lua work:

An instance object has a meta-table meta, which contains all the meta-method, specifically __index.

The __index meta-method is always called when a lookup in object fails to find the lookup-key.
Actually, it need not be a function, another table is acceptable too, and we have an ideal candidate: meta.

This game of looking whether __index in the meta-table has an entry for the key can be repeated:
Thus, a generic monster can be meta-table to rat, which can be the meta-table for all rats.

More and deeper inheritance can be done, if wanted.

protos = {}
monsters = {}
protos.monster = {
    name = 'generic monster',
    bp = 'monster',
    health = 1,
    __call = function(self, new, proto)
        if proto then
            proto = protos
            protos[new.bp] = new
            protos[new.name] = new
        else
            proto = monsters
        end
        table.insert(proto, new)
        new.id = #protos
        return setmetatable(new, self)
    end
}
protos.monster.__call(nil, protos.monster, true)

protos.monster({
    name = "a rat",
    short = 'rat',
    health = 5,
}, true)
protos.rat({
    name = "a black rat",
    short = 'blackrat',
    health = 7,
}, true)

Create a new monster with

protos[type] { --[[ some stats here ]] }

Create a new prototype with

protos[type]({ --[[ some stats here ]] }, true)

Lua manual: http://www.lua.org/manual/5.2/
Lua-users wiki (Sample Code): http://lua-users.org/wiki/SampleCode