is there any way to point table to another table? for example:
local a = {}
local b = {}
a.name = "Josh"
print(a.name) -- Prints 'Josh'
print(b.name) -- Prints 'Josh' aswell
a.name = "I don't have a name"
print(a.name) -- Print 'I don't have a name'
print(b.name) -- Prints 'I don't have a name' aswell
I hope you get my point.. thanks
EDIT:
Okay, so here is the idea:
I am making a dinamic function that is like this
local table = { 1, 2, "hey" }
function drawimage(name, posx, posy referencetable)
_tabledata[name] = { posx = posx, posy = posy, reference = {}}
setmetatable(_tabledata[name].reference, { __index = referencetable })
end
drawimage("Header", 0, 50, table)
All good and fine, values work and we are all happy.. the problem occurs when the reference table changes it's value in this way
local data = { 123123, 545454, "heyou" } -- Data is sent from another script via a trigger
table = data
Since I am not updating it by an index (ie: table[1] = 9999) reference variable is 'unsynced' with the real one, I hope you understand :)
EDIT2:
Okay here is a self working example of my main problem
local maintable = { "Stack", "Overflow" }
local maintablecopy = {}
maintablecopy = maintable
print("maintable[1] = " ..maintable[1]) -- Prints Stack
print("maintable[2] = " ..maintable[2]) -- Prints Overflow
print("")
print("maintablecopy[1] = " ..maintablecopy[1]) -- Prints Stack
print("maintablecopy[2] = " ..maintablecopy[2]) -- Prints Overflow
print("")
print("Changing values..")
local newdata = { "Hello", "World" }
maintable = newdata
print("")
print("maintable[1] = " ..maintable[1]) -- Prints Hello
print("maintable[2] = " ..maintable[2]) -- Prints World
print("")
print("maintablecopy[1] = " ..maintablecopy[1]) -- Prints Stack -- PROBLEM
print("maintablecopy[2] = " ..maintablecopy[2]) -- Prints Overflow -- PROBLEM
print("Using setmetatable..")
maintable = { "Stack", "Overflow" }
maintablecopy = {}
setmetatable(maintablecopy, { __index = maintable })
print("maintable[1] = " ..maintable[1]) -- Prints Stack
print("maintable[2] = " ..maintable[2]) -- Prints Overflow
print("")
print("maintablecopy[1] = " ..maintablecopy[1]) -- Prints Stack
print("maintablecopy[2] = " ..maintablecopy[2]) -- Prints Overflow
print("")
print("Changing values..")
local newdata = { "Hello", "World" }
maintable = newdata
print("")
print("maintable[1] = " ..maintable[1]) -- Prints Hello
print("maintable[2] = " ..maintable[2]) -- Prints World
print("")
print("maintablecopy[1] = " ..maintablecopy[1]) -- Prints Stack -- PROBLEM
print("maintablecopy[2] = " ..maintablecopy[2]) -- Prints Overflow -- PROBLEM
Why I cannot directly point it to the table when the variable updates? becouse I have 20 tables to update, it would be easier to do this
local _dynamics = {}
local tbl1 = { "Hey", 8787 }
local tbl2 = { 123, "There" }
local tbl3 = { "You", 1111 }
function dynamicFunction(name, posx, posy, textsize, reference)
_dynamics[name] = { posx = posx, posy = posy, textsize = textsize, reference = reference }
end
dynamicFunction("first", 0, 0, 5, tbl1)
dynamicFunction("second", 0, 0, 5, tbl2)
dynamicFunction("third", 0, 0, 5, tbl3)
for key in pairs(_dynamics) do
local inf = _dynamics[key]
for i = 1, #inf.reference do
print(inf.reference[i])
if i == #inf.reference then
print("")
end
end
end
print("")
print("")
tbl1 = { "aaaaa", "bbbbbbbbbb" }
tbl2 = { "ccccccccccc", "ttttttttttt" }
tbl3 = { "rrrrrrr", "yyyyyyyyyyy" }
for key in pairs(_dynamics) do
local inf = _dynamics[key]
for i = 1, #inf.reference do
print(inf.reference[i])
if i == #inf.reference then
print("")
end
end
end
print("Values should get updated on the reference variable, but it doesn't.. this would save me to do a check for every single variable")
You can run it on http://www.compileonline.com/execute_lua_online.php to see yourself what I mean.
Sorry if it's a mess but my english is not the best :D
You want the __index metamethod:
local a = { name="Josh" }
local b = {}
print(a.name) --> Josh
print(b.name) --> nil
setmetatable(b,{__index=a})
print(b.name) --> Josh
a.name = "Gilligan"
print(a.name) --> Gilligan
print(b.name) --> Gilligan
-- but note! the shadow
b.name = "overridden"
print(a.name) --> Gilligan
print(b.name) --> overridden
b.name = nil
print(a.name) --> Gilligan
print(b.name) --> Gilligan
For more details, I offer up this article of mine:
http://phrogz.net/lua/LearningLua_ValuesAndMetatables.html
Response to Edit2:
Let me summarize the problems with some of your code:
local maintablecopy = {}
maintablecopy = maintable
With the above code you create one table, set maintablecopy to reference that table, and then you completely abandon it when you set maintablecopy to instead reference a different table. This demonstrates a lack of understanding of how variables work.
local newdata = { "Hello", "World" }
maintable = newdata
Again, you are not "copying" newdata into maintable, you are changing the variable to reference the same table here.
maintable = { "Stack", "Overflow" }
maintablecopy = {}
setmetatable(maintablecopy, { __index = maintable })
-- …
local newdata = { "Hello", "World" }
maintable = newdata
Again, same problem. Here are some ways to change your code:
Replace a Table's Contents
Instead of maintable = newdata you could do this:
function copytable(from,to_table)
-- erase all old keys
for k,_ in pairs(to_table) do to_table[k] = nil end
-- copy the new ones over
for k,v in pairs(from) do to_table[k] = v end
end
local a = { name="Foo" }
local b = {}
copytable(a,b)
print(a.name == b.name) --> true
local c = { name="NEW" }
copytable(c,b)
print(c.name == b.name) --> true
However, doing this will not cause b to update if c changes.
c.name = "EVEN NEWER"
print(c.name == b.name) --> false
Update the __index
local a = { name="Foo" }
local b = setmetatable({},{__index=a})
print(a.name == b.name) --> true
-- cause b to follow c now instead of a
local c = { name="NEW" }
getmetatable(b).__index = c
print(c.name == b.name) --> true
c.name = "EVEN NEWER"
print(c.name == b.name) --> true
In general, you need to step back and describe the original problem you are trying to solve, instead of this XY problem.
Related
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
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.
I have a table called "inventory", initialized like so:
inventory = {}
inventory[1] = { qty = 0 }
I want to add more data to this table, at the index 1, eg:
val = { id = "example" }
inventory[1] = inventory[1], val
Is there a way I can do this while preserving the data that is already in this table at this index?
The final result should be something like:
inventory[1] = { qty = 0, id = "example" }
But if I try to print the id after trying this code I get:
print(inventory[1].id) == Nil
inventory[1].id = "example"
or
inventory[1]["id"] = "example"
or
this other SO answer with first_table being inventory[1] and second_table being val.
FWIW, you'd need 2 variables on the left side of the expression for inventory[1] = inventory[1], val to work: a, b = x, y.
You need to take the first key in the table and use it:
local inventory = {}
inventory[1] = { qty = 0 }
local val = { id = "example" }
--
local KeyName = next(val)
inventory[1][KeyName] = val[KeyName]
print(inventory[1][KeyName])
-- or
print(inventory[1].id)
i'm new to this and trying to create a NEW HERO from the subclass heroClass
code in CharacterClass.lua :
local CharacterClass =
{
name = " ",
hp = " ",
velocity = " ";
}
function CharacterClass:new(o, nameCharacter, hpCharacter, velocityCharacter)
local o = o or {}
setmetatable( o, self )
self.__index = self
self.name = nameCharacter
self.hp = hpCharacter
self.velocity = velocityCharacter
return o
end
-- CREATING HERO INHERIT
local heroClass = CharacterClass:new()
function heroClass:new (o, name, hp, velocity, playerId, color)
local o = o or CharacterClass:new(o, name, hp, velocity)
setmetatable(o, self)
self.__index = self
return o
end
Code in main.lua
local CharacterClass = require( "CharacterClass" )
local heroClassMain = heroClass:new()
And I am getting this error after i run the code
main.lua:45: attempt to index global 'heroClass' (a nil value) stack traceback: main.lua:45: in main chunk
PS : When I create a NEW CharacterClass in main, it works perfectly, but when I try to create a NEW heroClass (subclass) i receive the error !
Anyone know what's wrong ? Thank you !
Modify your CharacterClass.lua as follows:
local CharacterClass = {
name = " ",
hp = " ",
velocity = " ",
}
function CharacterClass:new(o, nameCharacter, hpCharacter, velocityCharacter)
-- same as before
end
-- CREATING HERO INHERIT
local heroClass = CharacterClass:new()
function heroClass:new (o, name, hp, velocity, playerId, color)
-- same as before
end
return {heroClass = heroClass, CharacterClass = CharacterClass }
Now, when you use require in main.lua, a bit more needs to be done:
local classModule = require "CharacterClass"
local CharacterClass, heroClass = classModule.CharacterClass, classModule.heroClass
local heroClassMian = heroClass:new()
Previously, your heroClass subclass was not provided to the main.lua file. This was what caused the error traceback.
In the code below it's my attempt to nest metatables on __index, but it's not working. What I want to do is if the value is t1 or t2 then return the associated value, otherwise call the function on the innermost __index. Is this possible?
so in the below x["hello"] can I return a value. I know I can just use a function on the outermost __index, but it seems I should be able to do this somehow using nested metatables.
tia.
x = { val = 3 } -- our object
mt = {
__index = {
t1 = 2,
t2 = 3,
__index = function (table, key)
print("here"..key)
return table.val
end
}
}
setmetatable(x, mt)
print(x["t1"]) -- prints 2
print(x.t2) -- prints 3
print(x["hello"]) -- prints nil???
This works but it seems like I could do it with metatables
x = { val = 3 } -- our object
mt1 = {
t1 = 2,
t2 = 3
}
mt = {
__index = function (table, key)
local a = mt1[key]
if a == nil then
print("here"..key)
a = "test"
end
return a
end
}
setmetatable(x, mt)
print(x["t1"])
print(x.t2)
print(x["hello"])
..and for anyone following along at home, here it is with nested metatables inline. Thanks for the tip Alexander that makes it a lot cleaner.
x = setmetatable(
{ val = 3 },
{
__index = setmetatable({
t1 = 2,
t2 = 3
},
{
__index = function (table, key)
print("here"..key)
return key
end
}
)
}
)
print(x["t1"])
print(x.t2)
print(x["hello"])
This works, but can I do it without declaring mt2?
x = { val = 3 } -- our object
mt2 = {
__index = function (table, key)
print("here"..key)
return key
end
}
mt = {
__index = {
t1 = 2,
t2 = 3
}
}
setmetatable(mt.__index, mt2)
setmetatable(x, mt)
print(x["t1"])
print(x.t2)
print(x["hello"])