How to create an object metatable which is a number but it has member functions

I’m trying to make a stats table, however certain stat values themselves have member values.

For example

local stat = {}
stat.Speed = 10
stat.Speed.Stamina = 20
stat.Speed.Recovery = 3
stat.Health = 100
stat.Health.Healrate = 1

Such that

print(stat.Speed)

will print “10” and

print(stat.Speed.Stamine)

prints out “20”

I know metatable has the “.__call” method, but I want something where it returns, instead of the table, it returns the value assigned to it.

b…but why wouldnt you just do something like this? It’s just more simple (in my eyes) :roll_eyes:

local stat = {}
stat.Speed = {}
stat.Speed.Speed = 10
stat.Speed.Stamina = 20
stat.Speed.Recovery = 3

Because it feels redundant to call “stat.Speed.Speed”. Additionally I want to replicate how something like vector3 not only when called returns a vector3 value but has methods attached to the objects as well.

With what you are suggesting, it’s like having to get a vector3 value by calling “vector3.vector3” instead of just saying “vector3”.

In my case when I call “stat.Speed” it returns “10”, but it also has member methods and values I can call.

1 Like

You can do something like this to achieve the behavior you want.

local test = {}
test.speed = {}
test.speed.stamina = 20
test.speed.recovery = 3


setmetatable(test.speed, {
	__tostring = function() return 10 end,
	__mul = function(a, b) return tostring(a) * tostring(b) end,
	__div = function(a, b) return tostring(a) / tostring(b) end,
	__add = function(a, b) return tostring(a) + tostring(b) end,
	__sub = function(a, b) return tostring(a) - tostring(b) end,
})

print(5 + test.speed, 5 * test.speed, 5 / test.speed, 5 - test.speed)
-- outputs 15, 50, 0.5, -5

print(test.speed.stamina, test.speed.recovery)
--outputs 20, 3

Why does “tostring” return a number value? Also with this doesn’t this mean I have to do something like “test.speed+0” to get the value of speed?

Whenever you try to print out a table, it will try to invoke it’s __tostring method. If it exists, it will return whatever that function returns. If it does not, it will print out the table like normal.

By modifying that method, I can have it return a number and then use that method to modify the mathematical methods of the table in order to have it behave like a number, while still having the properties of a table.

There are probably better ways to go about this, but this is just a quick solution I found to the strange question that you asked.

And no, you do not have to do print(test.speed + 0) in order to retrieve the numerical value. You can simply do print(test.speed).

1 Like