Working lua need advice deciding which method is best
Moderator: Forum Moderators
- Spannerbag
- Posts: 562
- Joined: December 18th, 2016, 6:14 pm
- Location: Yes
Re: Working lua need advice deciding which method is best
Ah... eyes glazed over and attention failed before I got that far into the lua manual...
Can't take too much manual-ploughing before brain packs in.
Thanks for the link and explanation, very helpful and enlightening.
Y'know, I think I actually understand this part of lua now.gnombat wrote: ↑April 10th, 2024, 8:52 pm_
is a function which should always take as its argument a single literal quoted string, which should generally be a complete sentence rather than a fragment of a sentence.
Suppose you have two variables you want to use in a sentence:
If you want to make that translatable, you should not use concatenation:Code: Select all
local color = "red" local species = "dragon" local sentence = "There is a " .. color .. " " .. species .. "."
...
Really the only way to do it is to avoid the concatenation operator (..
):
Code: Select all
# THIS WORKS: the argument to _() is a single literal quoted string which represents a complete sentence local sentence = stringx.vformat(_("There is a $color $species."), {color=color,species=species})
Many thanks as ever for your patience and taking the trouble to explain in detail, really appreciated.
Cheers!
-- Spannerbag
- Spannerbag
- Posts: 562
- Joined: December 18th, 2016, 6:14 pm
- Location: Yes
Re: Working lua need advice deciding which method is best
Thanks for taking the trouble to reply.Ravana wrote: ↑April 10th, 2024, 9:03 pm If you used global variables thenstringx.vformat(_("There is a $color $species."), _G)
something of this idea will work. _G is table of global variables.
With local you need to explicitly add them.
Or use local v={}, v.color = "red", v.species = "dragon",stringx.vformat(_("There is a $color $species."), v)
Amazingly, I'd actually stumbled across
_G
.I assume there is no relationship between
_G
and [set_global_variable]
?That is, manipulating one doesn't affect the other?
Cheers!
-- Spannerbag
Re: Working lua need advice deciding which method is best
_G is about Lua variables, [set_global_variable] is about WML variables which are stored in file.
- Spannerbag
- Posts: 562
- Joined: December 18th, 2016, 6:14 pm
- Location: Yes
Re: Working lua need advice deciding which method is best
Yep, got that - and I think you've answered my question but, to be crystal clear, if something is created by
[set_global_variable]
, that variable/data will not automagically appear in _G
?That is, they are totally separate data spaces?
Thanks again for your patience,
cheers!
-- Spannerbag
Re: Working lua need advice deciding which method is best
Yes, those are two entirely different things, despite the similarity in the name.Spannerbag wrote: ↑April 11th, 2024, 9:32 amYep, got that - and I think you've answered my question but, to be crystal clear, if something is created by[set_global_variable]
, that variable/data will not automagically appear in_G
?
That is, they are totally separate data spaces?
Actually even the name is not really the same, since the documentation for [set_global_variable] uses the term "Persistent Variables". It probably would have been better to name the tag [set_persistent_variable].
- Celtic_Minstrel
- Developer
- Posts: 2290
- Joined: August 3rd, 2012, 11:26 pm
- Location: Canada
- Contact:
Re: Working lua need advice deciding which method is best
Let me just repeat myself: passing
_G
to stringx.vformat
will not work.The stuff that isn't handled is things like gender and verb declension, which is what white_haired_uncle was talking about. There's nothing that automatically pluralizes the verb or automatically inserts the correct article or automatically makes adjectives agree with their noun in gender.gnombat wrote: ↑April 11th, 2024, 4:44 amI'm not sure what you mean by "none of that is handled" - the point I was trying to make with that example is that re-ordering the variables is handled, which is why using a formatting function may be preferable to simply using the concatenation operator. Of course, there may be other translation issues that are not so easy to solve.Celtic_Minstrel wrote: ↑April 11th, 2024, 4:11 amNone of that is handled, which is why you shouldn't construct translatable strings in this way. The only things that are safe to be substituted into a translatable sentence are numbers and proper nouns, and in the latter case you still need to have two copies of the string – one feminine and one masculine. (Substituting whole paragraphs into a string, or in some case full sentences, may also be fine.)white_haired_uncle wrote: ↑April 11th, 2024, 2:14 amI only speak American, not even English, but that looks like a pain for translators. Would they re-order $color and $species as necessary (in which case this appears to be a preferable alternative to string.format, which I find much more readable)? What about gender (a -> un or una depending on the unknown gender of $species). And even in American, a could be an for color=azure (I know I ran into a tool that handles that, though not in wesnoth, strings in Qt I think?).Code: Select all
_("There is a $color $species.")
- Spannerbag
- Posts: 562
- Joined: December 18th, 2016, 6:14 pm
- Location: Yes
Re: Working lua need advice deciding which method is best
Thanks for the clarification.gnombat wrote: ↑April 11th, 2024, 12:16 pmYes, those are two entirely different things, despite the similarity in the name.Spannerbag wrote: ↑April 11th, 2024, 9:32 am ...to be crystal clear, if something is created by[set_global_variable]
, that variable/data will not automagically appear in_G
?
That is, they are totally separate data spaces?
Actually even the name is not really the same, since the documentation for [set_global_variable] uses the term "Persistent Variables". It probably would have been better to name the tag [set_persistent_variable].
I got consufed about persistence because I got this muddled with (player) side "persistence" and used the wrong term.
Uh, while I've got you I've another (sort of) syntax question to ask if I may?
I'm probably trying to run before I can walk, but what I want to do is apply an externally specified (in WML) string repesenting a
[filter
to a specific unit which by the time the logic gets to this point will have confirmed that said unit exists.The parameters concerning this logic are passed to lua here:
Code: Select all
-- Pass values from WML to lua
function wesnoth.wml_actions.dbck_setup(cfg)
local event_name = cfg.event_name or wml.error "[dbck_setup] missing required event_name= attribute."
local side_candc = tonumber(cfg.side) or wml.error "[dbck_setup] missing required side= attribute."
local unit_filter = cfg.unit_filter or wml.error "[dbck_setup] missing required unit_filter= attribute."
end
Code: Select all
if not dbck_unit[1] then return end -- No unit at location
if not wml.eval_conditional{wml.variable{name = "side_number", numerical_not_equals = side_candc}} then return end -- Not correct side turn
I plan to use
have_unit
.The wiki gives this example:
Code: Select all
local result = wml.eval_conditional {
wml.have_unit { id = "hero" },
wml.variable { name = "counter", numerical_equals = "$old_counter" }
}
if wml.variables['boolean_wml_variable'] then ...
I.e. do something if variable is true?
Now here I can't simply
and
two have_unit
tests because:- The first test alone will always be true, and
- The second will test all units thereby likely producing false positives.
and
the conditions in a single have_unit
(unless there's a better way?).(If you're curious why this isn't all specified in the WML filter
unit_filter
it's because I want to cater for multiple concurrent uses of this logic which I don't need right now, but if I get it working this way, will be very handy in future applications.)It would save me a lot of frustration and keyboard bashing if I could narrow down my options here, so below are the two approaches I've thought of.
I'd just like to know if:
- There's a better way I've not thought of (highly likely), and
- If I have stumbled across the optimal strategy how to actually produce working code.
The problem is that both the ways I thought of doing this require lua to be able to "dig inside" variables and recognise "embedded" commands such as
and
or side=1
etc. which it probably can't do as it doesn't know WML...Anyway, my flawed ideas should describe what I'm trying to do.
First possible method I thought of
Prepend the unit's id (
dbck_unit[1].id
I presume) to the unit_filter
string (which is the contents of a [filter]
), i.e. something like:unit_filter=dbck_unit[1].id .. "and (" .. unit_filter ..")"
and then use something like: if not wml.eval_conditional{ wml.have_unit['unit_filter'] } then return end -- Unit failed filter specified
Second possible method I thought of
Explicitly
and
these conditions inside have_unit
... but I've no idea how to do this.I.e. something like:
wml.have_unit{ id = wml.variables.dbck_unit[1].id and wml.variables.unit_filter }
Hope this all makes sense and if you're bored answering basic questions, no worries.
Heh, once I have all this working then - even though it's for single player use - I'll probably have to make it replay safe by synchronising everything <shudder>...
Cheers!
-- Spannerbag
Re: Working lua need advice deciding which method is best
I'm not sure I understand what you have in
It might help if you posted the WML code.
unit_filter
.I'm still not clear on whatSpannerbag wrote: ↑April 11th, 2024, 2:04 pm theunit_filter
string (which is the contents of a[filter]
)
unit_filter
is expected to contain.It might help if you posted the WML code.
- Spannerbag
- Posts: 562
- Joined: December 18th, 2016, 6:14 pm
- Location: Yes
Re: Working lua need advice deciding which method is best
Sorry, wasn't clear.gnombat wrote: ↑April 11th, 2024, 3:02 pm I'm not sure I understand what you have inunit_filter
.
I'm still not clear on whatSpannerbag wrote: ↑April 11th, 2024, 2:04 pm theunit_filter
string (which is the contents of a[filter]
)unit_filter
is expected to contain.
It might help if you posted the WML code.
This lua code:
Code: Select all
-- Pass values from WML to lua
function wesnoth.wml_actions.dbck_setup(cfg)
local event_name = cfg.event_name or wml.error "[dbck_setup] missing required event_name= attribute."
local side_candc = tonumber(cfg.side) or wml.error "[dbck_setup] missing required side= attribute."
local unit_filter = cfg.unit_filter or wml.error "[dbck_setup] missing required unit_filter= attribute."
end
Code: Select all
[dbck_setup]
event_name = "dbck_test" # name of event to fire
side = 1 # Only enabled on this side's turn
unit_filter = "x,y=4,4"
[/dbck_setup]
unit_filter
contains the relevant filter (in this case x,y=4,4
).The unit to be filtered against is determined by a separate mechanism (double clicking a unit).
What I don't know how to do is how to get lua to parse or eval or otherwise treat the contents of
unit_filter
as a WML filter not a simple data string.Edit: forgot to add:
Somewhere else in the scenaerio is the event named above.
Here it's basically a placeholder as I need to get the lua working first.
Code: Select all
[event]
name=dbck_test
first_time_only=no
[message]
id=$unit.id
message=_"Well, did it work or what?"
[/message]
[/event]
If I can get this working (big if!) it'll function in a manner similar to the context menu.
[off_topic]
The reason why I'm doing this is that for me, at least, gameplay is disrupted by the context menu.
Whilst it's fine for information and start-of-game recruitment, in the "heat of battle" doubleclicking a unit to get it to do something feels more natural to me than right clicking it then selecting a menu option.
I know, I'm weird.
Besides, in my next campaign I already use the context menu for quite a few things so having an alternative for certain unit actions would be handy.
Heh, it was was about 70% complete but since I've started reworking chunks of Leafsea Burning - of which this is a part - I fear it's more like 40% complete.
[/off_topic]
Anyway, hope this all makes sense, but if I've still not been clear please let me know!
Thanks again,
cheers!
-- Spannerbag
Re: Working lua need advice deciding which method is best
Code: Select all
[dbck_setup]
event_name = "dbck_test" # name of event to fire
side = 1 # Only enabled on this side's turn
[unit_filter]
x,y=4,4
[/unit_filter]
[/dbck_setup]
- Spannerbag
- Posts: 562
- Joined: December 18th, 2016, 6:14 pm
- Location: Yes
Re: Working lua need advice deciding which method is best
Thanks!Ravana wrote: ↑April 11th, 2024, 3:40 pmwould make it much easier to parse.Code: Select all
[dbck_setup] event_name = "dbck_test" # name of event to fire side = 1 # Only enabled on this side's turn [unit_filter] x,y=4,4 [/unit_filter] [/dbck_setup]
I hadn't thought of doing it all in WML
[event][filter]
.I suppose it would be feasible to have something like:
Code: Select all
[filter]
id=$unit.id # Primary unit set in lua
[and]
... other conditions, using WML variables (also possibly set in lua) ...
[/and]
[/filter]
Great suggestion, will explore.
Thanks!
Really helpful... just wish I'd thought of that myself - too busy trying to learn lua to see alternatives!
Cheers!
-- Spannerbag
- Spannerbag
- Posts: 562
- Joined: December 18th, 2016, 6:14 pm
- Location: Yes
Re: Working lua need advice deciding which method is best
Just got stuck on another bit of logic where I want to test WML
Even if I replace
My code seems to be of the same form as the wiki...
... but I can't find any documentation for
(So maybe the error is actually about the
I've spent hours on this and am beginning to lose the will to live so will post this and leave it for awhile to calm down.
I hope someone can enlighten me - I've not even got to the tricky stuff yet!
Thanks in advance for your time and patience.
Cheers!
-- Spannerbag
side_number
against a lua variable and the syntax is wrong (I think).Code: Select all
wml.fire("message",
{ speaker = "narrator", message = stringx.vformat(_ "In game events dbck_setup values in lua:\nevent_name=$eventname\nside_candc=$sidecandc",{eventname=event_name,sidecandc=side_candc})})
wml.fire("message",
{ speaker = "narrator", message = stringx.vformat(_ "In game events side_number=$side_number",wml.all_variables)})
local dbck_unit = wesnoth.units.find_on_map{ x=x, y=y }
if wml.variables['dbck_disabled'] then return end -- Doubleclick detect disabled
if not dbck_unit[1] then return end -- No unit at doubleclicked location
if wml.eval_conditional{wml.variable{ name = "side_number", numerical_not_equals = 'side_candc'}} then return end -- Not correct side turn
side_number
is displayed correctly in the message
but the last line (#29 in the code) gives an error:Code: Select all
error scripting/lua: ~add-ons/stub18/lua/dbck.lua:29: attempt to call a nil value (field 'variable')
stack traceback:
~add-ons/stub18/lua/dbck.lua:29: in function <~add-ons/stub18/lua/dbck.lua:21>
'side_candc'
with the number 1 the error doesn't change.My code seems to be of the same form as the wiki...
Code: Select all
local result = wml.eval_conditional {
wml.have_unit { id = "hero" },
wml.variable { name = "counter", numerical_equals = "$old_counter" }
}
wml.variable
, though I haven't looked absolutely everywhere yet.(So maybe the error is actually about the
variable
statement itself, not the contents?)I've spent hours on this and am beginning to lose the will to live so will post this and leave it for awhile to calm down.
I hope someone can enlighten me - I've not even got to the tricky stuff yet!
Thanks in advance for your time and patience.
Cheers!
-- Spannerbag
Re: Working lua need advice deciding which method is best
Are you sure that's not supposed to be
wml.tag.variable
?Re: Working lua need advice deciding which method is best
wiki is wrong, wml.variable does not exist. It was supposed to be wml.tag.have_unit and wml.tag.variable.
Lua also knows some comparison operators, so you can use for example wesnoth.current.side ~= side_candc
Lua also knows some comparison operators, so you can use for example wesnoth.current.side ~= side_candc
- Spannerbag
- Posts: 562
- Joined: December 18th, 2016, 6:14 pm
- Location: Yes
Re: Working lua need advice deciding which method is best
@gnombat
Thanks for pointing this out!
@Ravana
Ditto, also... wow, wesnoth.current looks really useful and I totally missed it in the wiki. As I learn new stuff I'm realising how wrong-headed my original code design and approach was, am revising this on-the-fly.
At least I feel the various bits and pieces are coming together, many thanks for your time and effort, both of you, it's really appreciated.
I'm not there yet and am still blundering in the fog, but at least I now can see a few road signs...
Cheers!
-- Spannerbag
Heh, I trusted the wiki and didn't know about wml.tag.variable...
Thanks for pointing this out!
@Ravana
Ditto, also... wow, wesnoth.current looks really useful and I totally missed it in the wiki. As I learn new stuff I'm realising how wrong-headed my original code design and approach was, am revising this on-the-fly.
At least I feel the various bits and pieces are coming together, many thanks for your time and effort, both of you, it's really appreciated.
I'm not there yet and am still blundering in the fog, but at least I now can see a few road signs...
Cheers!
-- Spannerbag