Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Lua metatables: class fields vs instance fields

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!

like image 823
Aaron Avatar asked Apr 01 '26 05:04

Aaron


1 Answers

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
like image 115
Odoth Avatar answered Apr 02 '26 21:04

Odoth



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!