vultraz's lua questions
Moderator: Forum Moderators
- Elvish_Hunter
- Posts: 1575
- Joined: September 4th, 2009, 2:39 pm
- Location: Lintanir Forest...
Re: vultraz's lua questions
The helper.get_child() function returns data in form of a userdata object. This kind of objects cannot be handled by ordinary Lua means, and for example attempting to use pairs on it yields a nice error:
However, userdata objects have four special fields: __parsed, __literal, __shallow_parsed and __shallow_literal.
Their purpose is to allow accessing userdata stuff as if they were ordinary WML tables, and since I suppose that you still want to perform variable subtitution, then you need to use the __parsed field:If you want a faster code, and you don't need accessing sub-tags that may be contained in the filter, you can try using __shallow_parsed:
Code: Select all
bad argument #1 to 'pairs' (table expected, got userdata)
Their purpose is to allow accessing userdata stuff as if they were ordinary WML tables, and since I suppose that you still want to perform variable subtitution, then you need to use the __parsed field:
Code: Select all
local filter = helper.get_child(cfg, "filter").__parsed
Code: Select all
local filter = helper.get_child(cfg, "filter").__shallow_parsed
Current maintainer of these add-ons, all on 1.16:
The Sojournings of Grog, Children of Dragons, A Rough Life, Wesnoth Lua Pack, The White Troll (co-author)
The Sojournings of Grog, Children of Dragons, A Rough Life, Wesnoth Lua Pack, The White Troll (co-author)
Re: vultraz's lua questions
Hmm... Crendgrim gave me a different solution:
Or do they do the same thing?
Also, am I doing the second line there right? We (me and Crend) weren't sure.
Thanks
Code: Select all
local filter = helper.literal(helper.get_child(cfg, "filter"))
if not filter then filter = wesnoth.tovconfig({id = "$unit.id"}) end
filter.role = "hero"
local units = wesnoth.get_units(filter)
Also, am I doing the second line there right? We (me and Crend) weren't sure.
Thanks
Creator of Shadows of Deception (for 1.12) and co-creator of the Era of Chaos (for 1.12/1.13).
SurvivalXtreme rocks!!!
What happens when you get scared half to death...twice?
SurvivalXtreme rocks!!!
What happens when you get scared half to death...twice?
- Elvish_Hunter
- Posts: 1575
- Joined: September 4th, 2009, 2:39 pm
- Location: Lintanir Forest...
Re: vultraz's lua questions
The problem is that the __literal, __shallow_literal fields and helper.literal() function do not perform variable substitution, so you'll retrieve $unit.id exactly as a string stating "$unit.id". I'm not sure if get_units() will still work correctly also with __literal, as so far I ever needed to use the __parsed stuff. Since I think that you'll want to use $unit.id, I'd stay on the safe side and use __parsed.
So, the idea is to make a tag that requires a [filter] sub-tag, but if such sub-tag is missing it falls back on the auto-stored unit, instead of raising the usual error? That will be a non-standard behaviour, and as such a potential source of errors and bugs. Some days ago I insisted in typing "elif" (instead of "elseif") in Lua, then I remembered that "elif" is a Python command. It's so easy to do mistakes...
Code: Select all
if not filter then filter = wesnoth.tovconfig({id = "$unit.id"}) end
Current maintainer of these add-ons, all on 1.16:
The Sojournings of Grog, Children of Dragons, A Rough Life, Wesnoth Lua Pack, The White Troll (co-author)
The Sojournings of Grog, Children of Dragons, A Rough Life, Wesnoth Lua Pack, The White Troll (co-author)
-
- Inactive Developer
- Posts: 2461
- Joined: August 15th, 2008, 8:46 pm
- Location: Germany
Re: vultraz's lua questions
Note that I haven't read everything here carefully, but...
And yes, get_units substitutes variables fine. You can see that it converts the argument into a vconfig object in case that it isn't already a vconfig userdata:
vconfig filter = luaW_checkvconfig(L, 1, true);
(and that call path)
EDIT
Here's your code from above copy-pasted and corrected (untested):
The safer thing is .__literal, since .__parsed may substitute a variable at a time already when it is not yet defined. In particular, the filter passed may contain a $this_unit and this automatically stored variable is only set once .get_units is called.Elvish_Hunter wrote:The problem is that the __literal, __shallow_literal fields and helper.literal() function do not perform variable substitution, so you'll retrieve $unit.id exactly as a string stating "$unit.id". I'm not sure if get_units() will still work correctly also with __literal, as so far I ever needed to use the __parsed stuff. Since I think that you'll want to use $unit.id, I'd stay on the safe side and use __parsed.
And yes, get_units substitutes variables fine. You can see that it converts the argument into a vconfig object in case that it isn't already a vconfig userdata:
vconfig filter = luaW_checkvconfig(L, 1, true);
(and that call path)
Looking at this I would expect an error in case that that filter is nil, since writing into a vconfig userdata shouldn't work. helper.literal() probably returns always at least an empty table so it's never nil anyway.vultraz wrote:Code: Select all
local filter = helper.literal(helper.get_child(cfg, "filter")) if not filter then filter = wesnoth.tovconfig({id = "$unit.id"}) end filter.role = "hero" local units = wesnoth.get_units(filter)
EDIT
Here's your code from above copy-pasted and corrected (untested):
Code: Select all
function wml_actions.check_inventory(cfg)
local filter = helper.get_child(cfg, "filter") or
helper.wml_error "[check_inventory] missing required [filter] tag"
filter = helper.shallow_literal(filter)--just inserting this line here should be all that's needed
filter.role = "hero"
local units = wesnoth.get_units(filter)
for i, u in ipairs(units) do
local u_id = units[i].id
local items_carried = helper.get_variable_array(string.format("%s.%s", u_id, "inventory"))
for ii,k in ipairs(items_carried) do
if items_carried[ii].id == cfg.item then
local stuff_to_do = helper.get_child(cfg, "then") or
helper.wml_error "[check_inventory] missing required [then] tag"
wesnoth.fire(stuff_to_do.__literal)
return
end
end
end
end
projects (BfW 1.12):
A Simple Campaign: campaign draft for wml starters • Plan Your Advancements: mp mod
The Earth's Gut: sp campaign • Settlers of Wesnoth: mp scenario • Wesnoth Lua Pack: lua tags and utils
updated to 1.8 and handed over: A Gryphon's Tale: sp campaign
A Simple Campaign: campaign draft for wml starters • Plan Your Advancements: mp mod
The Earth's Gut: sp campaign • Settlers of Wesnoth: mp scenario • Wesnoth Lua Pack: lua tags and utils
updated to 1.8 and handed over: A Gryphon's Tale: sp campaign
Re: vultraz's lua questions
Haven't had to use this in a long time, but a quick question: How would you execute some WML, stored in a WML variable, from lua, in a callback function?
Creator of Shadows of Deception (for 1.12) and co-creator of the Era of Chaos (for 1.12/1.13).
SurvivalXtreme rocks!!!
What happens when you get scared half to death...twice?
SurvivalXtreme rocks!!!
What happens when you get scared half to death...twice?
Re: vultraz's lua questions
This is untested, but I think it's pretty simple once you understand it:
EDIT: Fixed the indexes.
Code: Select all
-- You need get_variable because you can't iterate over something you get
-- through set_wml_var_metatable.
wml = wesnoth.get_variable(var_name)
for _, tag in ipairs(wml) do
wesnoth.wml_actions[tag[1]](tag[2])
end
- Elvish_Hunter
- Posts: 1575
- Joined: September 4th, 2009, 2:39 pm
- Location: Lintanir Forest...
Re: vultraz's lua questions
I'd try using wml_actions.command, like I did here:vultraz wrote:How would you execute some WML, stored in a WML variable, from lua, in a callback function?
Code: Select all
local do_actions = wesnoth.get_variable( 'my_var' )
wesnoth.wml_actions.command( do_actions )
Current maintainer of these add-ons, all on 1.16:
The Sojournings of Grog, Children of Dragons, A Rough Life, Wesnoth Lua Pack, The White Troll (co-author)
The Sojournings of Grog, Children of Dragons, A Rough Life, Wesnoth Lua Pack, The White Troll (co-author)
Re: vultraz's lua questions
The structure could possible multiple tags, subtags, and conditionals ([if],etc), or maybe just [object].
Creator of Shadows of Deception (for 1.12) and co-creator of the Era of Chaos (for 1.12/1.13).
SurvivalXtreme rocks!!!
What happens when you get scared half to death...twice?
SurvivalXtreme rocks!!!
What happens when you get scared half to death...twice?
Re: vultraz's lua questions
Nextt question, for $100!
I'm trying to find a way to quickly insert a variable into a unit's [variables] node via Lua. Would this work?
I'm trying to find a way to quickly insert a variable into a unit's [variables] node via Lua. Would this work?
Code: Select all
local class_var = wesnoth.get_variable(string.format("character_development.class[%d]", wesnoth.get_variable("class_index")))
local unit_being_developed = wesnoth.get_unit(id = u_id)
rawset("unit_being_developed.variables", "class", class_var.id)
wesnoth.put_unit(unit_being_developed.x,unit_being_developed.y, unit_being_developed)
Creator of Shadows of Deception (for 1.12) and co-creator of the Era of Chaos (for 1.12/1.13).
SurvivalXtreme rocks!!!
What happens when you get scared half to death...twice?
SurvivalXtreme rocks!!!
What happens when you get scared half to death...twice?
Re: vultraz's lua questions
No it wouldn’t. Try this:
Code: Select all
local class_id = wesnoth.get_variable(("character_development.class[%d].id"):format(wesnoth.get_variable("class_index")))
local unit_being_developed = wesnoth.get_units {id = u_id} [1] -- get_unit takes either (underlying_id) or (x, y).
unit_being_developed.variables.class = class_id -- rawset would bypass the metamethod that tells the engine to set the variable.
wesnoth.put_unit(unit_being_developed)
Re: vultraz's lua questions
Been forever since I've had to post something here, but I'm really stuck.
So basically, it seem that variables get substituted in the cfg userdata in a custom tag. So if you have, say [tag] name=$foo [/tag], using cfg.name in wml_actions.tag will give it the value of foo. But in my case, I have a problem. It parses the WHOLE thing, even if you do cfg = helper.shallow_parsed(cfg) before doing anything. I want to toplevel keys parsed, so they're written as variable value in the config, BUT, the keys in any subtags to remain as "$foo" in the config, and substituted when accessed. This is because later in the tag, I write the whole cfg field to a a unit's [variables] table, to be accessed later. If the values of the subtags' keys contains a variable, and they get substituted when [tag] is encountered, and if the variable doesn't exist, they will end up with an empty string ("") as a value.
I COULD simply write the entire config object as literal, and then pass it through wesnoth.tovconfig when I access it, but that would rely on any external variables to remain in existence, so I'd rather not do that unless I have to.
So, to sum it up, is there a way to write variable substitutions for specific parts of a config object, to a config object, and leave the rest alone to be substituted by whatever accesses them?
EDIT: I seem to have explained myself badly. What I want to do is shallow_parse cfg and return the rest as literal.
EDIT 2: 8680 gave me a solution that involved looping through subtags using a function from his lua pack and passing each through helper.literal. It works.
So basically, it seem that variables get substituted in the cfg userdata in a custom tag. So if you have, say [tag] name=$foo [/tag], using cfg.name in wml_actions.tag will give it the value of foo. But in my case, I have a problem. It parses the WHOLE thing, even if you do cfg = helper.shallow_parsed(cfg) before doing anything. I want to toplevel keys parsed, so they're written as variable value in the config, BUT, the keys in any subtags to remain as "$foo" in the config, and substituted when accessed. This is because later in the tag, I write the whole cfg field to a a unit's [variables] table, to be accessed later. If the values of the subtags' keys contains a variable, and they get substituted when [tag] is encountered, and if the variable doesn't exist, they will end up with an empty string ("") as a value.
I COULD simply write the entire config object as literal, and then pass it through wesnoth.tovconfig when I access it, but that would rely on any external variables to remain in existence, so I'd rather not do that unless I have to.
So, to sum it up, is there a way to write variable substitutions for specific parts of a config object, to a config object, and leave the rest alone to be substituted by whatever accesses them?
EDIT: I seem to have explained myself badly. What I want to do is shallow_parse cfg and return the rest as literal.
EDIT 2: 8680 gave me a solution that involved looping through subtags using a function from his lua pack and passing each through helper.literal. It works.
Code: Select all
-- Parse the toplevel to substitute variables, then write the rest
-- of the config object as literal to preserve variables for later
-- access in the inventory
cfg = helper.shallow_parsed(cfg)
for subtag in lp8.subtags(cfg) do
subtag[2] = helper.literal(subtag[2])
end
Creator of Shadows of Deception (for 1.12) and co-creator of the Era of Chaos (for 1.12/1.13).
SurvivalXtreme rocks!!!
What happens when you get scared half to death...twice?
SurvivalXtreme rocks!!!
What happens when you get scared half to death...twice?