[solved] wml.get_child for unit

Discussion of Lua and LuaWML support, development, and ideas.

Moderator: Forum Moderators

User avatar
Celtic_Minstrel
Developer
Posts: 2371
Joined: August 3rd, 2012, 11:26 pm
Location: Canada
Contact:

Re: wml.get_child for unit

Post by Celtic_Minstrel »

white_haired_uncle wrote: August 22nd, 2024, 3:58 pm So does that mean any changes I make are "stored" immediately? I thought I had to "unstore" the unit (e.g to_map).
Nope, __cfg can be thought of as a WML copy of the unit.
white_haired_uncle wrote: August 22nd, 2024, 3:58 pm I've always thought of .__cfg as being akin to storing the unit.
It is exactly the same apart from not going into a WML variable.
white_haired_uncle wrote: August 22nd, 2024, 3:58 pm But in WML, I store a unit to a variable, which is then just a WML variable and I can do whatever I want with it (or not). And I suppose I could store it to multiple variables, each independent of the others. Short of doing a by-value copy, I don't think I can do this in lua (this is a rather silly example that can help me understand, not something I can see myself doing).
Sure you can. Access __cfg twice and now you have two independent WML copies of the unit.
white_haired_uncle wrote: August 22nd, 2024, 3:58 pm My immediate confusion likes with "It'll serialize every time you access it". Seems like that would mean if I made a change the next time I accessed .__cfg my change would be overwritten by the serialized data from the unit. Unless behind the scenes there are actually two copies of the unit, the "real" one and the ".__cfg" one and I'm getting a serialized version of the latter. Then "unstoring" would be syncing the real one from the "working copy".
I don't really get your confusion or how you drew any of these conclusions. Every time you access unit.__cfg, the game serializes that unit to WML and returns the result. If you access __cfg twice in a row, you'll end up with two identical but independent WML tables. If you access __cfg, make some change to the unit directly, and access __cfg again, the first WML table reflects the state of the unit before the change and the second WML table reflects the state of the unit after the change.
white_haired_uncle wrote: August 22nd, 2024, 4:28 pm Are you saying there is a difference in serialization cost between:

Code: Select all

local unit = wesnoth.units.find_on_map(cfg)[1]
unit.__cfg.hitpoints=23
unit.__cfg.language_name=_"Uncle Bob"
unit.__cfg.experience=100
and

Code: Select all

local unit = wesnoth.units.find_on_map(cfg)[1].__cfg
unit.hitpoints=23
unit.language_name=_"Uncle Bob"
unit.experience=100
There is, and also the first version results in no change to the unit, since you're only modifying the temporary WML table that was created when you accessed __cfg. The second version doesn't serialize anything. It directly modifies the unit.
Author of The Black Cross of Aleron campaign and Default++ era.
Former maintainer of Steelhive.
gnombat
Posts: 892
Joined: June 10th, 2010, 8:49 pm

Re: wml.get_child for unit

Post by gnombat »

Ravana wrote: August 22nd, 2024, 4:44 pm Yes. Different behaviour too if you wanted to do anything with the wml table.

Code: Select all

$ unit.__cfg == unit.__cfg
false
Celtic_Minstrel wrote: August 22nd, 2024, 5:38 pm I don't really get your confusion or how you drew any of these conclusions. Every time you access unit.__cfg, the game serializes that unit to WML and returns the result. If you access __cfg twice in a row, you'll end up with two identical but independent WML tables. If you access __cfg, make some change to the unit directly, and access __cfg again, the first WML table reflects the state of the unit before the change and the second WML table reflects the state of the unit after the change.
Personally I think it would be better if it were something like .cfg() instead of .__cfg. Magical properties are always confusing.
Celtic_Minstrel wrote: August 22nd, 2024, 5:38 pm The second version doesn't serialize anything.
It doesn't? (Did you see the .__cfg at the end of the first line?)
white_haired_uncle
Posts: 1456
Joined: August 26th, 2018, 11:46 pm
Location: A country place, far outside the Wire

Re: wml.get_child for unit

Post by white_haired_uncle »

Celtic_Minstrel wrote: August 22nd, 2024, 5:38 pm
I don't really get your confusion or how you drew any of these conclusions. Every time you access unit.__cfg, the game serializes that unit to WML and returns the result. If you access __cfg twice in a row, you'll end up with two identical but independent WML tables. If you access __cfg, make some change to the unit directly, and access __cfg again, the first WML table reflects the state of the unit before the change and the second WML table reflects the state of the unit after the change.

Code: Select all

local a = wesnoth.units.find_on_map({id = Bob})[1].__cfg
local b = wesnoth.units.find_on_map({id = Bob})[1].__cfg
Now, as I understand it, that's two copies of Bob serialized and stored in completely separate tables (and if this is true, I believe it confirms my suspicion that .__cfg is not a table, it RETURNS a table). No changes to a will affect b, and nothing I do to a or b will affect Bob until I "unstore" a or b.

Here's where it gets interesting:

Code: Select all

u = wesnoth.units.find_on_map({id = Bob})[1]
print(u.__cfg.hitpoints)  ==> 24
u.__cfg.hitpoints=12
print(u.__cfg.hitpoints)  ==> ???
I would have thought the second print would print 12. But if every access to .__cfg serializes from the unit (Bob), and I've done nothing here to change Bob, then I guess it would print 24.

I probably just need to play with it a bit. My gut is telling me the second code block is either just stupid, or making a change this way will only work if you "unstore" immediately after the change.
Speak softly, and carry Doombringer.
User avatar
Celtic_Minstrel
Developer
Posts: 2371
Joined: August 3rd, 2012, 11:26 pm
Location: Canada
Contact:

Re: wml.get_child for unit

Post by Celtic_Minstrel »

gnombat wrote: August 22nd, 2024, 6:05 pm It doesn't? (Did you see the .__cfg at the end of the first line?)
Ah, you're right, I did indeed miss that… which means both versions result in no actual change to the unit.
white_haired_uncle wrote: August 22nd, 2024, 7:44 pm Now, as I understand it, that's two copies of Bob serialized and stored in completely separate tables (and if this is true, I believe it confirms my suspicion that .__cfg is not a table, it RETURNS a table). No changes to a will affect b, and nothing I do to a or b will affect Bob until I "unstore" a or b.
Correct.
white_haired_uncle wrote: August 22nd, 2024, 7:44 pm Here's where it gets interesting:

Code: Select all

u = wesnoth.units.find_on_map({id = Bob})[1]
print(u.__cfg.hitpoints)  ==> 24
u.__cfg.hitpoints=12
print(u.__cfg.hitpoints)  ==> ???
I would have thought the second print would print 12. But if every access to .__cfg serializes from the unit (Bob), and I've done nothing here to change Bob, then I guess it would print 24.
__cfg is read-only. It's a little unfortunate that Lua allows you to modify its members anyway, but that won't do anything, as what you're modifying is just a temporary copy of the table.
white_haired_uncle wrote: August 22nd, 2024, 7:44 pm My gut is telling me the second code block is either just stupid, or making a change this way will only work if you "unstore" immediately after the change.
Making a change this way will never work. You need to store __cfg in a variable (which includes passing it to a function), otherwise any changes to it will be immediately lost.
Author of The Black Cross of Aleron campaign and Default++ era.
Former maintainer of Steelhive.
User avatar
hermestrismi
Posts: 730
Joined: February 6th, 2016, 11:28 pm
Location: Tunisia
Contact:

Re: wml.get_child for unit

Post by hermestrismi »

hi,
a quick stupid question. I want to build a wml array (like append variables) like that:

Code: Select all

[set_variables]
mode=append 
name=unit_items_pair
[literal]
id=Blood Bat-20
item=sword2
[/literal]
...
[/variables]
what's the best way to do that? the ways I tried (

Code: Select all

wml.array_access_set

and

Code: Select all

wml.variables
didn't build an append array
white_haired_uncle
Posts: 1456
Joined: August 26th, 2018, 11:46 pm
Location: A country place, far outside the Wire

Re: wml.get_child for unit

Post by white_haired_uncle »

wml.array_access.get and wml.array_access.set are used to convert WML arrays to/from lua tables.

Generally, you'll use table.insert to append to a lua table. https://www.lua.org/pil/19.2.html

WML and I don't get along well, but I think this is close to what you want:

Code: Select all

local a=wml.array_access.get("unit_items_pair")
b["id]="Blood Bat-20"
b["item"]="sword"
table.insert(a,b)
print(a[#a].item) ==> "Blood Bat-20"
wml.array_access.set("unit_items_pair",a)
Speak softly, and carry Doombringer.
User avatar
hermestrismi
Posts: 730
Joined: February 6th, 2016, 11:28 pm
Location: Tunisia
Contact:

Re: wml.get_child for unit

Post by hermestrismi »

white_haired_uncle wrote: August 23rd, 2024, 9:52 am wml.array_access.get and wml.array_access.set are used to convert WML arrays to/from lua tables.

Generally, you'll use table.insert to append to a lua table. https://www.lua.org/pil/19.2.html

WML and I don't get along well, but I think this is close to what you want:

Code: Select all

local a=wml.array_access.get("unit_items_pair")
b["id]="Blood Bat-20"
b["item"]="sword"
table.insert(a,b)
print(a[#a].item) ==> "Blood Bat-20"
wml.array_access.set("unit_items_pair",a)
thanks. I'll try it. may I ask if this is the best way to serialize a table (so that it became surely persistent upon reload)?
white_haired_uncle
Posts: 1456
Joined: August 26th, 2018, 11:46 pm
Location: A country place, far outside the Wire

Re: wml.get_child for unit

Post by white_haired_uncle »

hermestrismi wrote: August 23rd, 2024, 9:56 am
thanks. I'll try it. may I ask if this is the best way to serialize a table (so that it became surely persistent upon reload)?
Best? I don't know. It's how I do it.

There's also

https://wiki.wesnoth.org/LuaAPI/wesnoth ... stent_tags

but I've never used it.
Speak softly, and carry Doombringer.
Post Reply