How to index parent object in Lua? - lua

I'm trying to index a parent object by using its identifier, but it returns nil instead of the object, so it throws a error when executing the script.
local mapit = {
...
ground = function(x, y, w, h, data)
...
local id = 0
-- mapit is nil in this block
for i = 0, #mapit.data.ids do
if id ~= i then
id = id + 1
end
end
...
end,
data = {
ids = {}
}
...
}
local myRect = mapit.ground(400, 100, 600, 100)

In Lua, locals are not in scope on the right hand of their initializer, so your closure refers to a global named mapit instead.
Declare the local first, then assign to it.
local mapit
mapit = { ... }

Related

Spawning an Object with State TTS

I would like to combine two and more objects in the TabletopSimulator. Wenn I spawn the objects I can combine like this page https://kb.tabletopsimulator.com/host-guides/creating-states/. I would like this create with Lua. So I need help... I spawn the objects like here, but I didn`t get two objects with 2 states.
function SpawnLevel1(Obj1, ID)
CID = ID
spawnparamslvl = {
type = 'Custom_Assetbundle',
position = Obj1.getPosition(),
rotation = Obj1.getRotation(),
scale = {x=1, y=1, z=1},
}
paramslvl = {
assetbundle = data[CID].assetbundle,
type = 1,
material = 0,
}
Obj2 = spawnObject(spawnparamslvl)
obj_name = data[CID].display_name
Obj2.setDescription(obj_name)
Obj2.setName(obj_name)
Obj2.setCustomObject(paramslvl)
Obj1.addAttachment(Obj2).SetState(1)
end
function deploy(PID)
display_name = data[PID].display_name
spawnparams = {
type = 'Custom_Assetbundle',
position = self.getPosition(),
rotation = self.getRotation(),
scale = {x=1, y=1, z=1},
}
params = {
assetbundle = data[PID].assetbundle,
type = 0,
material = 0,
}
Spawning(spawnparams, params, display_name, PID)
end
function Spawning(spawnparams, params, display_name, PID)
Obj1 = spawnObject(spawnparamsmain)
ID = PID
Level1 = SpawnLevel1(Obj1, ID)
Obj1.setCustomObject(paramsmain)
Obj1.setName(display_name)
end
Thank you for your help
Radoan
You have to use spawnObjectData or spawnObjectJSON. The format of the "object data" follows the format of the save file.
Rather than hardcoding the large data structures needed to build an object, I'll use existing objects as templates. This is a common practice that's also reflected by the following common idiom for modifying an existing object:
local data = obj.getData()
-- Modify `data` here --
obj.destruct()
obj = spawnObjectData({ data = data })
The following are the relevant bits of "object data" for states:
{ -- Object data (current state)
-- ...
States = {
["1"] = { -- Object data (state 1)
--- ...
},
["3"] = { -- Object data (state 3)
-- ...
},
["4"] = { -- Object data (state 4)
-- ...
}
},
-- ...
}
So you could use this:
function combine_objects(base_obj, objs_to_add)
if not objs[1] then
return base_obj
end
local data = base_obj.getData()
if not data.States then
data.States = { }
end
local i = 1
while data.States[tostring(i)] do i = i + 1 end
i = i + 1 -- Skip current state.
while data.States[tostring(i)] do i = i + 1 end
for _, obj in ipairs(objs_to_add) do
data.States[tostring(i)] = obj.getData()
obj.destruct()
i = i + 1
end
base_obj.destruct()
return spawnObjectData({ data = data })
end

Full path to the required value

How do I get the full path to the required value in the table? I want to track changes in another table through a proxy table.
I understand that I need to use metatables and __index in it. But I haven't been able to come up with a tracker yet.
Sample table structure:
Objects = {
Panel = { layer = 1, x = 600, y = 328, w = 331, h = 491;
objects = {
label = { layer = 1, x = 0, y = 0, text = 'header' };
Window = { layer = 2, x = 400, y = 100, w = 100, h = 100;
objects = {
label = { layer = 1, x = 0, y = 0, text = 'lorem ipsum dorem' };
};
};
};
};
};
Path: Objects.Panel.objects.Window.objects.label.text
I tried to create a metatable for each of the tables and collect the result of each call to __index into a table in order to roughly understand which key and value were retrieved or changed in order to synchronize these values ​​with other tables.
This will prove itself to be horrendously slow and memory inefficient. Anyway, you were right on the track: proxy and handle __index and __newindex metamethods to your liking. This being said you also need to track the state of the proxy somehow.
You can try to hide it with some closures and upvalues but the easy way is to store the information directly in the proxy tables:
function make_tracker (o, name)
local mt = {}
mt.__index = function (proxy, key)
local path = {unpack(rawget(proxy, "__path"))} -- Stupid shallow copy
local object = rawget(proxy, "__to")
table.insert(path, key)
if type(object[key]) == "table" then
return setmetatable({__to = object[key], __path = path}, mt)
else
return table.concat(path, ".") .. " = " .. tostring(object[key])
end
end
return setmetatable({__to = o, __path = {name}}, mt)
end
__to fields indicates what proxy should point to and __path is there to cover fields we have trespassed so far. It does a shallow copy, so that one can use subproxies with local variables. name parameter is there to initialize the name of the first table, as you just simply can't know that. You use it like this:
local tObjects = make_tracker(Objects, "Objects")
local subproxy = tObjects.Panel.objects.Window
print(subproxy.objects.label.text)
print(tObjects.Panel.objects.label.text)
print(subproxy.x)
-- prints:
-- Objects.Panel.objects.Window.objects.label.text = lorem ipsum dorem
-- Objects.Panel.objects.label.text = header
-- Objects.Panel.objects.Window.x = 400
Of course, I doubt that appending the path to the original value is what you want. Modify insides of else block:
return table.concat(path, ".") .. " = " .. tostring(object[key])
according to your needs, e.g:
register_tracked_path(table.concat(path, "."))
return object[key]
If you want to handle modification of values you need to extend the metatable with similar __newindex.

Getting all data names from a table

I have a nested table of global variables in my lua file like
GLOBAL.ADVANTAGES =
{
CLASS =
{
WARRIOR =
{
ATTACK = 0,
DEFEND = 0,
CRIT_CHANCE = 0,
CRIT_DAMAGE = 0
},
PALADIN =
{
ATTACK = 0,
DEFEND = 0,
CRIT_CHANCE = 0,
CRIT_DAMAGE = 0
},
PRIEST =
{
ATTACK = 0,
DEFEND = 0,
CRIT_CHANCE = 0,
CRIT_DAMAGE = 0
},
...
...
I want to traverse table to return all variable names in strings like "GLOBAL.CLASS.PRIEST.ATTACK", "GLOBAL.CLASS.PRIEST.DEFENSE", and so on. I already can traverse whole table but It returns values solely as "ATTACK", "DEFENSE" and such...
How can I get full "address" of each value?
More efficient and easier way is to use table.concat. For example, to show all fields:
local myprint
myprint = function(tab, prefixtab) -- Tab = tab to print, prefix = inital tab ({'GLOBAL'} in your case) or nil
prefixtab = prefixtab or {}
local idx = #prefixtab+1
for k,v in pairs(tab) do
prefixtab[idx] = k
print(table.concat(prefixtab, '.')) -- Comment to don't print for every path
if type(v) == 'table' then
myprint(v, prefixtab)
--else -- To print only complete pathes
--print(table.concat(prefixtab, '.'))
end
end
prefixtab[idx] = nil
end
Example (based on your table):
> myprint(GLOBAL, {'GLOBAL'})
GLOBAL.ADVANTAGES
GLOBAL.ADVANTAGES.CLASS
GLOBAL.ADVANTAGES.CLASS.WARRIOR
GLOBAL.ADVANTAGES.CLASS.WARRIOR.CRIT_DAMAGE
GLOBAL.ADVANTAGES.CLASS.WARRIOR.ATTACK
GLOBAL.ADVANTAGES.CLASS.WARRIOR.CRIT_CHANCE
GLOBAL.ADVANTAGES.CLASS.WARRIOR.DEFEND
GLOBAL.ADVANTAGES.CLASS.PRIEST
GLOBAL.ADVANTAGES.CLASS.PRIEST.CRIT_DAMAGE
GLOBAL.ADVANTAGES.CLASS.PRIEST.ATTACK
GLOBAL.ADVANTAGES.CLASS.PRIEST.CRIT_CHANCE
GLOBAL.ADVANTAGES.CLASS.PRIEST.DEFEND
GLOBAL.ADVANTAGES.CLASS.PALADIN
GLOBAL.ADVANTAGES.CLASS.PALADIN.CRIT_DAMAGE
GLOBAL.ADVANTAGES.CLASS.PALADIN.ATTACK
GLOBAL.ADVANTAGES.CLASS.PALADIN.CRIT_CHANCE
GLOBAL.ADVANTAGES.CLASS.PALADIN.DEFEND
Or complete-only path version:
> myprint(GLOBAL, {'GLOBAL'})
GLOBAL.ADVANTAGES.CLASS.PRIEST.CRIT_CHANCE
GLOBAL.ADVANTAGES.CLASS.PRIEST.ATTACK
GLOBAL.ADVANTAGES.CLASS.PRIEST.CRIT_DAMAGE
GLOBAL.ADVANTAGES.CLASS.PRIEST.DEFEND
GLOBAL.ADVANTAGES.CLASS.WARRIOR.CRIT_CHANCE
GLOBAL.ADVANTAGES.CLASS.WARRIOR.ATTACK
GLOBAL.ADVANTAGES.CLASS.WARRIOR.CRIT_DAMAGE
GLOBAL.ADVANTAGES.CLASS.WARRIOR.DEFEND
GLOBAL.ADVANTAGES.CLASS.PALADIN.CRIT_CHANCE
GLOBAL.ADVANTAGES.CLASS.PALADIN.ATTACK
GLOBAL.ADVANTAGES.CLASS.PALADIN.CRIT_DAMAGE
GLOBAL.ADVANTAGES.CLASS.PALADIN.DEFEND
Assuming you are doing a depth-first traversal (aka in-order), as you descend the hierarchy, pass down the path for the parent node. At the point where you capture the leaf's name, prepend it with the parent node's path. If the node is not a leaf, prepend the node's name with the parent node's path and pass it down.
local node = path .. "." .. tostring(key) -- tostring used in case key is not a string
Use "GLOBAL" as the initial path.

Division of metatable

got some problem with metatable. This is my simple metatable:
local mt = {}
function mt:add(n)
return setmetatable({n = n}, {__index = mt})
end
function mt:get() return self.n end
Now I want to add some division like:
mt.math
mt.effect
Which each one has some own methods like:
mt.math:floor() return math.floor(self:get()) end
mt.effect:show(args) onMapShowEffect(self:get(), {x = x + (args[1] ~= nil or 0), ...) end
mt.effect:get() return getCurrentPos() end
Any ideas?
OK, trying make all details to share my problem.
Player = {}
function Player:add(this)
return setmetatable({this = this}, {__index = Player})
end
Player:get() return self.this end
Above code works perfectly on this example
function enterToGame(player1, player2)
local p1 = Player:add(player1)
local p2 = Player:add(player2)
print(p1:get()) -- ID1
print(p2:get()) -- ID2
Now I want to create some helpfully methods(functions) for table Player. I want to make it more flexible, so I want divide it for classes. Example:
Player.info = {
id = function() return Player:get() end,
}
Player.pos = {
get = function() return getPosition(Player:get()) end,
set = function(args) setPosition(Player:get(), args) end,
}
Player.speed = {
get = function() return getSpeed(Player:get()) end,
set = function(value) setSpeed(value) end,
improve = function(value) setSpeed(Player.speed.get() + value) end,
}
But its not work exactly what I want:
function enterToGame(player1, player2)
local p1 = Player:add(player1)
local p2 = Player:add(player2)
print(p1:get()) -- ID1
print(p2:get()) -- ID2
print(p1.info.id()) -- ID2 instead of ID1
print(p2.info.id()) -- ID2
When I put Player:get() in my methods its return last object declaration.
Based on what you state, if you do
mt.math = mt:add(123)
You don't need themt:get() because mt is the metatable for mt.math. Then
mt.math.floor = function(self) return math.floor(self.n) end
will work as expected. For example,
print(mt.math:floor())
prints 123.
EDIT 1: So now that I have a better understanding of what you are trying to do: normally you would do
p1:id()
p1:getPos()
p1:setPos()
p1:getSpeed()
p1:improveSpeed()
Note the colon, this is important, so that each method gets a "self" as first parameter, thereby given them the table instance to operate on (p1, in the above example). Instead you want to group methods so
p1.info:id()
p1.pos:get()
p1.pos:set()
p1.speed:improve()
p1.speed:get()
These methods will get a self that points to p1.info, p1.pos, etc. But those sub-tables have no knowledge of the container table (p1). The info and pos tables are in the Player class: they are shared by all instances of Player (p1, p2 etc). You have to make the info and pos tables non-shared:
function Player:add(player)
local pN= setmetatable( {n = player, info={}, pos={}}, {__index = Player})
pN.info.id = function() return pN.n end
pN.pos.set = function(x) return setPosition(pN, x) end
return pN
end
Then you get
> p1=mt:add(player1)
> p2=mt:add(player2)
> print(player1)
table: 0024D390
> print(p1.info.id())
table: 0024D390
> print(player2)
table: 0024D250
> print(p2.info.id())
table: 0024D250
All that said, I don't really like the idea of having to use closures like this, perhaps there are gotchas since not everything will be in Player.

Lua table variables the same even for new objects

I'm trying to create a Lua table that represents a matrix, however I keep running into a problem where if I create two Matrices, and initialize some values they both have the same values.
--Test.Lua
require"Matrix"
M1 = Matrix.Matrix:New()
M2 = Matrix.Matrix:New()
M1._11 = 2
print(M1._11) --Prints 2
print(M2._11) --Prints 2
--Matrix.lua
module("Matrix", package.seeall)
Matrix = {}
Matrix = { _11 = 0, _12 = 0, _13 = 0,
_21 = 0, _22 = 0, _23 = 0,
_31 = 0, _32 = 0, _33 = 0
}
function Matrix:New()
object = object or {}
setmetatable(object, self)
self.__index = self
return object
end
object = object or {}
This is why that happens. You only ever create one Matrix object. There is only every one object table which you return, and there is only ever one self table that you use as a metatable.
So how can you expect different instances when Matrix:New will always return the exact same value on every call?
You need to return a new table for each New call; that's why we use that name ;) Because of the way you're using a metatable, you also have to return a new metatable; you can't return the same metatable attached to new tables and expect it to work.
As nicol is explaining, on one hand you are trying to "reuse the same object over and over" (probably to "make it faster") and on the other you want to have different objects.
The solution is - don't reuse object on New call.
local Matrix = {} -- don't use the module function. Make Matrix local ...
Matrix.__index = Matrix
function Matrix:New()
local object = { -- create one local variable on every call to New
_11 = 0, _12 = 0, _13 = 0,
_21 = 0, _22 = 0, _23 = 0,
_31 = 0, _32 = 0, _33 = 0
}
setmetatable(object, self)
return object
end
return Matrix -- ... and return the Matrix local var at the end
A couple notes:
You really must learn how to use local
Usage of the module function is not recommended. Return a local table instead, as in my example.
Usage: assuming that that file is called "Matrix.lua":
local Matrix = require 'Matrix'
local M1 = Matrix:New()
local M2 = Matrix:New()
-- etc
As a sidenote, the Matrix:New() function can be made shorter (and faster). The following implementation works exactly as the one above, but it's slightly more efficient:
function Matrix:New()
return setmetatable({
_11 = 0, _12 = 0, _13 = 0,
_21 = 0, _22 = 0, _23 = 0,
_31 = 0, _32 = 0, _33 = 0
},
self)
end
This works because setmetatable(t,m) returns t with m already set as its metatable.

Resources