Question

I'm working on a game in Corona/Lua, and implementing a class called "Item" which represents an in game weapon, armor, amulet, etc. And I'm new to object-oriented Lua.

After creating a new instance of the class, I'm finding that setting certain properties of the object seems to set them in the class metatable, not in the object itself.

Here is the class and constructor:

local Item = {
    name = nil,
    itemType = nil,
    scarcity = "basic",
    baseDamage = 0, -- Bow only
    bonuses = {
        damageBonus = 0,
        speedBonus = 0,
        critBonus = 0,
        dropBonus = 0,
        rechargeBonus = 0,
        xpBonus = 0
    }
}

-- Creates a simple blank instance of the Item class.
function Item:new(o)
    local item = o or {}
    setmetatable(item, self)
    self.__index = self
    return item
end

Now let's say I create two objects based on this prototype:

local bow = Item:new()
bow.itemType = "bow"
starterBow.baseDamage = 5

local ring = Item:new()
ring.itemType = "ring"
ring.bonuses.damageBonus = 0.25

To my dismay, the "bonuses.damageBonus" property seems to be getting set inside the metatable and therefore applying to every item (i.e. the bow also gets the damage bonus, and stacking with the ring). However the behavior seems limited to the "bonus" properties. If I set the "itemType" property, it is attached to the object, not the class, as expected.

The behavior I'd like to see is that the fields of the "bonuses" table can be set for individual items. Any idea what I'm doing wrong? Thanks!

Was it helpful?

Solution

The __index metafield is triggered when you try to 'get' a field from your table the doesn't exist. ring.bonuses.damageBonus = 0.25 is trying to 'get' bonuses from ring, and since it doesn't exist, goes to the metatable and returns your bonuses table, and then its damageBonus index is set to 0.25.

Understand that just declaring variables in the Item table doesn't mean your Item instances will inherit them as instance variables. For that, you would need to declare/initialize them in your Item:new() function:

function Item:new(o)
    local item = o or {}
    setmetatable(item, self)
    self.__index = self
    item.bonuses={} -- create instance bonuses table and give default values
    for k,v in pairs(self.bonuses) do 
        item.bonuses[k]=v
    end
    return item
end

OTHER TIPS

The bonuses table does not exist in the instance so the lookup for it looks in the metatable and finds it in the __index table. It then continues the lookup in that table. Similarly, assignments do the same thing. There is only, and this is the key, one bonuses table though because you never copy it and the only copy that exists is the one in the Item class which is then found via the __index metatable lookup.

You either need to add the __index metatable magic to a bonuses table you create for each instance (for defaults but local-to-instance assignment) or simply copy/create the bonuses table new for each instance.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top