Another basic lua query
Moderator: Forum Moderators
- Spannerbag
- Posts: 777
- Joined: December 18th, 2016, 6:14 pm
- Location: Yes
Re: Another basic lua query: location set
Ah, light shineth... people often credit me with more/less knowledge/stupidity than I possess...Celtic_Minstrel wrote: ↑June 10th, 2025, 12:34 pm ...you can shorten it a bit tlocal rand_x,rand_y = reach_locs:random()
.
...It's not all that complicated. It maps from locations to values, by converting the location to a numeric key in a Lua table.

Thanks for dumbing down stuff sufficiently for me to understand.
As ever your patience is much appreciated.
Thanks gnombat for the example, as it happens I only logged in to post a minor bug report on 1.19 so read Celtic_Minstrel's post before yours - but that was helpful because I had no idea an alternate syntax existed!gnombat wrote: ↑June 10th, 2025, 11:31 am There is an example in data/ai/micro_ais/cas/ca_herding_sheep_move.lua
Explains why my text search didn't find anything...

Thanks!
-- Spannerbag
- Spannerbag
- Posts: 777
- Joined: December 18th, 2016, 6:14 pm
- Location: Yes
Re: Another basic lua query: hopefully brief syntax check
Hi,
Found time to do a bit more on this recently and am making progress, albeit slooowly...
I'm not sure of some syntax and am just checking to see if I've got it right?
I'm stealing the "protective parents" ca logic from forest animals (
Specifically it's this snippet I'm unsure of how to rewrite:
Instead of
I also want to honour
So I have this code for the protect action (it's not finished, I just want to minimise syntax errors here
).
Questions:
I've kept my questions to a minimun (I have loads more but those I'll work on) but these would be quick wins if anyone could point out the more glaring errors!
Thanks in advance for your time and trouble, much appreciated as always.
Cheers!
-- Spannerbag
Found time to do a bit more on this recently and am making progress, albeit slooowly...

I'm not sure of some syntax and am just checking to see if I've got it right?
I'm stealing the "protective parents" ca logic from forest animals (
ca_forest_animals_tusker_attack.lua
).Specifically it's this snippet I'm unsure of how to rewrite:
Code: Select all
-- The tusker moves as close to enemy as possible
-- Closeness to tusklets is secondary criterion
local adj_tusklets = wesnoth.units.find_on_map {
side = wesnoth.current.side,
type = cfg.tusklet_type,
{ "filter_adjacent", { id = target.id } }
}
cfg.tusklet_type
, the method used to identify the tusklet equivalent ("follower") in my version is a unit filter:
Code: Select all
[filter_follower]
type=Icemonax
[filter_wml]
[variables]
wmai=1
[/variables]
[/filter_wml]
[/filter_follower]
[avoid]
whilst moving the protecting unit ("wanderer").So I have this code for the protect action (it's not finished, I just want to minimise syntax errors here

Code: Select all
-- ca_wanderers_protect.lua
local AH = wesnoth.require "ai/lua/ai_helper.lua"
local M = wesnoth.map
local function get_wanderers(cfg)
local wanderers = AH.get_units_with_moves { side = wesnoth.current.side, { "and", filter_wanderer } }
return wanderers
end
-- Returns enemies adjacent to follower units
-- function ai_helper.get_attackable_enemies(filter, side, cfg)
-- ****************************************************
-- *** REFERENCED BY QUESTION 1 BELOW ***
-- ****************************************************
local function get_adjacent_enemies(cfg)
local adjacent_enemies = AH.get_attackable_enemies( { "filter_adjacent", { side = wesnoth.current.side, { "and", filter_follower } } }
side = wesnoth.current.side
avoid_map = avoid_locs )
return adjacent_enemies
end
local ca_wanderers_protect = {}
-- Check whether there is an enemy next to a follower and attack it (if disable_protect=false)
function ca_wanderers_protect:evaluation(cfg)
if cfg.disable_protect then return 0 end -- Protect followers disabled (not default)
if (not filter_wanderer) then return 0 end -- No filter_wanderer tag
if (not filter_follower) then return 0 end -- No filter_follower tag
if (not get_wanderers(cfg)[1]) then return 0 end -- No wanderers with moves, so nothing to move
if (not get_adjacent_enemies(cfg)[1]) then return 0 end -- No adjacent enemies
return cfg.ca_score
end
function ca_wanderers_protect:execution(cfg)
local wanderers = get_wanderers(cfg)
local adjacent_enemies = get_adjacent_enemies(cfg)
local avoid_locs = AH.get_avoid_map(ai, nil, true) -- [avoid]ed locations, if any
-- Find the closest enemy to any wanderer
---@type number, unit?, unit?
local min_dist, attacker, target = math.huge, nil, nil
for _,wanderer in ipairs(wanderers) do
for _,enemy in ipairs(adjacent_enemies) do
local dist = M.distance_between(wanderer.x, wanderer.y, enemy.x, enemy.y)
if (dist < min_dist) then min_dist, attacker, target = dist, wanderer, enemy end
end
end
-- ****************************************************
-- *** REFERENCED BY QUESTION 2 BELOW ***
-- ****************************************************
-- The wanderer moves as close to enemy as possible
-- Closeness to followers is secondary criterion
-- wesnoth.units.find_on_map(filter) → array of units
local adj_followers = wesnoth.units.find_on_map { side = wesnoth.current.side, { "and", filter_follower } { "and", "filter_adjacent", { id = target.id } } }
local best_hex = AH.find_best_move(attacker, function(x, y), { avoid_map = avoid_locs }
local rating = -M.distance_between(x, y, target.x, target.y)
for _,follower in ipairs(adj_followers) do
if (M.distance_between(x, y, follower.x, follower.y) == 1) then rating = rating + 0.1 end
end
return rating
end)
AH.robust_move_and_attack(ai, attacker, best_hex, target)
end
return ca_wanderers_protect
Questions:
- Follower filter is stored thus:
local filter_follower = wml.get_child(cfg,"filter_follower")
I'd like this to honourCode: Select all
local adjacent_enemies = AH.get_attackable_enemies( { "filter_adjacent", { side = wesnoth.current.side, { "and", filter_follower } } } side = wesnoth.current.side avoid_map = avoid_locs )
[avoid]
when moving attacking wanderers (more for consistency than anything else, if it's complicated I can omit it).
Is the best place to add that here (presumably removing some otherwise attackable enemies due to[avoid]
) or process avoided locations later?
Presently I specifyside=
so the function knows thatavoid_map
is indeed that and not a garbledside
- is there a more elegant way to do this?
Is enclosing parameters with "(...)" correct or should I use "{...}"?
Finally: any other syntax errors? - When using the existing logic I need to identify enemies adjacent to followers.
Original reference code usestype = cfg.tusklet_type
whereas I havefilter_follower
.Can I simply appendCode: Select all
local adj_followers = wesnoth.units.find_on_map { side = wesnoth.current.side, { "and", filter_follower } { "and", "filter_adjacent", { id = target.id } } }
filter_adjacent
and if so have I done it correctly?
Alternatively, is there a better way given what I have to work with?
E.g. maybe working with the enemy units adjacent to followers rather than vice-versa?
I've kept my questions to a minimun (I have loads more but those I'll work on) but these would be quick wins if anyone could point out the more glaring errors!

Thanks in advance for your time and trouble, much appreciated as always.
Cheers!
-- Spannerbag
- Celtic_Minstrel
- Developer
- Posts: 2376
- Joined: August 3rd, 2012, 11:26 pm
- Location: Canada
- Contact:
Re: Another basic lua query: hopefully brief syntax check
That code is absolutely not valid. I'm not sure what will happen, but you'll probably get a syntax error.Spannerbag wrote: ↑June 20th, 2025, 12:00 pm Follower filter is stored thus:local filter_follower = wml.get_child(cfg,"filter_follower")
Code: Select all
local adjacent_enemies = AH.get_attackable_enemies( { "filter_adjacent", { side = wesnoth.current.side, { "and", filter_follower } } } side = wesnoth.current.side avoid_map = avoid_locs )
- You need to separate multiple parameters with commas.
- There's no such thing as using equals to specify the name of a parameter.
Code: Select all
---Attackable enemies are defined as being being
--- - enemies of the specified side,
--- - not petrified
--- - and visible to the side as defined in cfg.viewing_side and cfg.ignore_visibility.
--- - have at least one adjacent hex that is not inside an area to avoid
---For speed reasons, this is done separately, rather than calling ai_helper.get_visible_units().
---@param filter? WMLTable Standard unit filter WML table for the enemies
---@param side? integer Side number, if side other than current side is to be considered
---@param cfg? get_enemies_opts
---@return table
That does absolutely nothing. It's not even valid syntax. If it were valid (because you're in "{...}" and not "(...)" then it would be required and fine, but that's not the case here.Spannerbag wrote: ↑June 20th, 2025, 12:00 pm Presently I specifyside=
so the function knows thatavoid_map
is indeed that and not a garbledside
- is there a more elegant way to do this?
Parameters are always enclosed in "(...)", not "{...}". If there is a single parameter which is enclosed in "{...}" then you can omit the "(...)", so it looks like your parameters are enclosed in "{...}", but the truth is you just have one parameter. But that doesn't apply here – you need 3 parameters. (You can also omit the parentheses if the single parameter is a string, which means it's enclosed in "double quotes", 'single quotes', or [[double brackets]].)Spannerbag wrote: ↑June 20th, 2025, 12:00 pm Is enclosing parameters with "(...)" correct or should I use "{...}"?
Now, to get back to the question… as stated previously, the function you're calling takes 3 parameters:
- A filter. You've got that one right:
{ "filter_adjacent", { side = wesnoth.current.side, { "and", filter_follower } } }
- A side number:
wesnoth.current.side
]*]An options table. From what you've said, you want to put the avoid map here:{avoid_map = avoid_locs}
Code: Select all
local adjacent_enemies = AH.get_attackable_enemies( { "filter_adjacent", { side = wesnoth.current.side, { "and", filter_follower } } },
wesnoth.current.side,
{avoid_map = avoid_locs} )
It looks like you have it right in terms of syntax, except for one error: you're missing a comma in there. Though I'm not sure if that filter is composed correctly.Spannerbag wrote: ↑June 20th, 2025, 12:00 pm When using the existing logic I need to identify enemies adjacent to followers.
Original reference code usestype = cfg.tusklet_type
whereas I havefilter_follower
.Can I simply appendCode: Select all
local adj_followers = wesnoth.units.find_on_map { side = wesnoth.current.side, { "and", filter_follower } { "and", "filter_adjacent", { id = target.id } } }
filter_adjacent
and if so have I done it correctly?
Alternatively, is there a better way given what I have to work with?
E.g. maybe working with the enemy units adjacent to followers rather than vice-versa?
- Spannerbag
- Posts: 777
- Joined: December 18th, 2016, 6:14 pm
- Location: Yes
Re: Another basic lua query
Thanks ever so much for your reply, really really helpful.
I think
this results in this filter (plugging in actual values):
The structure I'm trying to build is (assuming I'm right so far) is as above but with
My attempt (with added comma
):
or, embed filter_adjacent?:
Hope this makes sense.
Thanks again for your patience!
Cheers!
-- Spannerbag
Assuming I correctly deciphered the original code logic:Celtic_Minstrel wrote: ↑June 20th, 2025, 12:40 pm ...It looks like you have it right in terms of syntax, except for one error: you're missing a comma in there. Though I'm not sure if that filter is composed correctly.
Code: Select all
-- The tusker moves as close to enemy as possible
-- Closeness to tusklets is secondary criterion
local adj_tusklets = wesnoth.units.find_on_map {
side = wesnoth.current.side,
type = cfg.tusklet_type,
{ "filter_adjacent", { id = target.id } }
}
Code: Select all
side = 3
type = Icemonax
[filter_adjacent]
id=target.id
[/filter_adjacent]
type
replaced by a full filter:
Code: Select all
side = 3
type = Icemonax
[filter_wml]
[variables]
wmai=1
[/variables]
[/filter_wml]
[filter_adjacent]
id=target.id
[/filter_adjacent]

Code: Select all
local adj_followers = wesnoth.units.find_on_map {
side = wesnoth.current.side,
{ "and", filter_follower }, { "and", "filter_adjacent", { id = target.id } }
}
or, embed filter_adjacent?:
Code: Select all
local adj_followers = wesnoth.units.find_on_map {
side = wesnoth.current.side,
{ "and", filter_follower, { "and", "filter_adjacent", { id = target.id }} }
}
Hope this makes sense.
Thanks again for your patience!
Cheers!
-- Spannerbag
Re: Another basic lua query
The
find_best_move
line is not right. It looks like you were trying to add a third argument?There are some examples of how to call
find_best_move
with a third argument in existing code, e.g., ca_swarm_move.luaI don't think either of those looks right... Before trying to fix those, a couple of questions:Spannerbag wrote: ↑June 20th, 2025, 3:17 pm The structure I'm trying to build is (assuming I'm right so far) is as above but withtype
replaced by a full filter:My attempt (with added commaCode: Select all
side = 3 type = Icemonax [filter_wml] [variables] wmai=1 [/variables] [/filter_wml] [filter_adjacent] id=target.id [/filter_adjacent]
):
Code: Select all
local adj_followers = wesnoth.units.find_on_map { side = wesnoth.current.side, { "and", filter_follower }, { "and", "filter_adjacent", { id = target.id } } }
or, embed filter_adjacent?:Code: Select all
local adj_followers = wesnoth.units.find_on_map { side = wesnoth.current.side, { "and", filter_follower, { "and", "filter_adjacent", { id = target.id }} } }
- Have you considered using wml.tag for representing WML? This would make the code simpler (it reduces the number of braces you need by 50%).
- Do you need to use
and
there? Usually you don't needand
(and there's no[and]
in the WML you posted). That would simplify the code further.
- Spannerbag
- Posts: 777
- Joined: December 18th, 2016, 6:14 pm
- Location: Yes
Re: Another basic lua query
First off, thanks for replying, your time and trouble are appreciated as always! 
... within which
... which in turn has:
... within which (finally) we drill down to:
So my hope/assumption is that by adding the avoid map to the original call it will pass through to
This is why I'm asking so many questions; I run out of stack space storing and combining so many nested queries in my head!
Thanks for the pointer but it doesn't really help me with the basics; the 3rd argument is composite (I guess) as many lines of code occupy where I expected to see a 3rd argument?
This extra detail rather gets in the way of seeing how things fit together - at least for me whilst I'm still getting used to lua.
Alas, don't have much quality thinking time just now so apologies if I ask dumb questions!
Would be willing to give it a go - but I might miss errors due to logic being presented differently!
2. No idea, copied from other code not a clue whether
However
I think I might have to revert to a much earlier and simpler (but essentially working) version that was (even more) just a simple tweak of forest animals.
Then gradually add/change things, I know more now than I did then so maybe starting over from scratch (even if it entails major surgery) might be quicker?
Thanks again for your help, really appreciated!
Cheers!
-- Spannerbag

I just looked at the function (in
data/ai/lua/ai_helper.lua
):Code: Select all
function ai_helper.find_best_move(units, rating_function, cfg)
cfg = cfg or {}
...
cfg
is referenced: local reach_map = ai_helper.get_reachable_unocc(unit, cfg)
... which in turn has:
ai_helper.get_reachmap(unit, cfg_GRU)
... within which (finally) we drill down to:
Code: Select all
function ai_helper.get_reachmap(unit, cfg)
-- plus all other parameters to wesnoth.paths.find_reach
local viewing_side = cfg and cfg.viewing_side or unit.side
ai_helper.check_viewing_side(viewing_side)
local ignore_visibility = cfg and cfg.ignore_visibility
local old_moves = unit.moves
if cfg and (cfg.moves == 'max') then unit.moves = unit.max_moves end
local reachmap = LS.create()
local initial_reach = wesnoth.paths.find_reach(unit, cfg)
for _,loc in ipairs(initial_reach) do
local is_available = true
if cfg and cfg.avoid_map and cfg.avoid_map:get(loc[1], loc[2]) then
is_available = false
...
function ai_helper.get_reachmap
thereby taking any avoided locations into consideration.This is why I'm asking so many questions; I run out of stack space storing and combining so many nested queries in my head!

gnombat wrote: ↑June 21st, 2025, 7:20 am There are some examples of how to callfind_best_move
with a third argument in existing code, e.g., ca_swarm_move.lua...
Thanks for the pointer but it doesn't really help me with the basics; the 3rd argument is composite (I guess) as many lines of code occupy where I expected to see a 3rd argument?
This extra detail rather gets in the way of seeing how things fit together - at least for me whilst I'm still getting used to lua.
Alas, don't have much quality thinking time just now so apologies if I ask dumb questions!
1. No, didn't think of that.gnombat wrote: ↑June 21st, 2025, 7:20 am ...I don't think either of those looks right... Before trying to fix those, a couple of questions:
- Have you considered using wml.tag for representing WML? This would make the code simpler (it reduces the number of braces you need by 50%).
- Do you need to use
and
there? Usually you don't needand
(and there's no[and]
in the WML you posted). That would simplify the code further.
Would be willing to give it a go - but I might miss errors due to logic being presented differently!

2. No idea, copied from other code not a clue whether
and
"glue" is mandatory when building filters and/or sub-filters within lua.However
wml.tag
might really help here! I think I might have to revert to a much earlier and simpler (but essentially working) version that was (even more) just a simple tweak of forest animals.
Then gradually add/change things, I know more now than I did then so maybe starting over from scratch (even if it entails major surgery) might be quicker?
Thanks again for your help, really appreciated!
Cheers!
-- Spannerbag
Re: Another basic lua query
No, it's actually the 2nd argument that spans many lines of code. The 2nd argument is a function that starts on line 45 and ends on line 74 (theSpannerbag wrote: ↑June 21st, 2025, 11:06 amThanks for the pointer but it doesn't really help me with the basics; the 3rd argument is composite (I guess) as many lines of code occupy where I expected to see a 3rd argument?gnombat wrote: ↑June 21st, 2025, 7:20 am There are some examples of how to callfind_best_move
with a third argument in existing code, e.g., ca_swarm_move.lua...
end
keyword is the end of that function). The 3rd argument { avoid_map = avoid_map }
is on line 74 after that.It's going to look weird if you're not used to programming languages like Lua with anonymous functions, but you'll get used to it.
Spannerbag wrote: ↑June 21st, 2025, 11:06 am 2. No idea, copied from other code not a clue whetherand
"glue" is mandatory when building filters and/or sub-filters within lua.
Howeverwml.tag
might really help here!![]()
and
here is just representing the WML [and]
tag. Usually you don't need the [and]
tag in WML (except maybe for very complex logic?), so I think you usually won't need it when representing WML in Lua.This is what you posted for WML:
Code: Select all
side = 3
type = Icemonax
[filter_wml]
[variables]
wmai=1
[/variables]
[/filter_wml]
[filter_adjacent]
id=target.id
[/filter_adjacent]
Code: Select all
{
side = 3,
type = 'Icemonax',
wml.tag.filter_wml {
wml.tag.variables { wmai = 1 }
},
wml.tag.filter_adjacent { id = target.id }
}
- Celtic_Minstrel
- Developer
- Posts: 2376
- Joined: August 3rd, 2012, 11:26 pm
- Location: Canada
- Contact:
Re: Another basic lua query
I think it's used quote often in the AI code, actually – likely to head off errors that might otherwise occur if someone passed in a filter containing
[or]
tags.- Spannerbag
- Posts: 777
- Joined: December 18th, 2016, 6:14 pm
- Location: Yes
Re: Another basic lua query
Ah-ha! That explains a lot and hugely clarifies a few (of several) things that puzzled me! Many thanks!gnombat wrote: ↑June 21st, 2025, 7:18 pm No, it's actually the 2nd argument that spans many lines of code. The 2nd argument is a function that starts on line 45 and ends on line 74 (theend
keyword is the end of that function). The 3rd argument{ avoid_map = avoid_map }
is on line 74 after that.
It's going to look weird if you're not used to programming languages like Lua with anonymous functions, but you'll get used to it.

I did try reading the lua manual but it's rather abstract; I find it a much swifter process to work with actual code (even if it can be frustrating at times) as I have a knack of misunderstanding things manuals rarely seem to elaborate on... hmm, think I have my answer: I can find ambiguity in anything.

That ... actually makes sense!gnombat wrote: ↑June 21st, 2025, 7:18 pm This is what you posted for WML:
If you want to represent that literally in Lua, I think it would look like this:Code: Select all
side = 3 type = Icemonax [filter_wml] [variables] wmai=1 [/variables] [/filter_wml] [filter_adjacent] id=target.id [/filter_adjacent]
Code: Select all
{ side = 3, type = 'Icemonax', wml.tag.filter_wml { wml.tag.variables { wmai = 1 } }, wml.tag.filter_adjacent { id = target.id } }

type
is a string assignment hence quotes, others are numbers.- Use
{...]
construct because, to quote the wiki: Many functions in the Wesnoth Lua API expect a table representing a WML object... - Put comma after every parameter (except the last one of course) to tell lua where each one ends.

Many thanks for your assistance, as always.
Cheers!
-- Spannerbag
- Spannerbag
- Posts: 777
- Joined: December 18th, 2016, 6:14 pm
- Location: Yes
Re: Another basic lua query
Thanks for making that point, in a WML context I guess I can drop the "and" as (I presume) anything inside a wml.tag uses the sameCeltic_Minstrel wrote: ↑June 22nd, 2025, 1:30 amI think it's used quote often in the AI code, actually – likely to head off errors that might otherwise occur if someone passed in a filter containing[or]
tags.
[and]
rule as WML.So; to filter all leaders and all side 2 units:
Code: Select all
[filter]
canrecruit=yes
[or]
side=2
[/or]
[filter]
Code: Select all
{
canrecruit='yes',
{'or', side=2},
}
Whereas to filter leaders of side 2 and/or who are undead would be:
Code: Select all
[filter]
canrecruit=yes
[and]
side=2
[or]
race=undead
[/or]
[/and]
[filter]
Code: Select all
{
canrecruit='yes',
{'and',
{ side=2 },
{ 'or', race='undead' }
}
}
Code: Select all
{
canrecruit='yes',
wml.tag.and { side=2, wml.tag.or {race='undead'} }
}
Your continuing help and support is really useful and very enlightening!
Just the small task of recode and test left to do (shudder).

Cheers!
-- Spannerbag
Re: Another basic lua query
Some of your examples are not valid Lua syntax. For example missing {} in wml table creation and using Lua keyword inappropriately. wml.tag["and"]
- Celtic_Minstrel
- Developer
- Posts: 2376
- Joined: August 3rd, 2012, 11:26 pm
- Location: Canada
- Contact:
Re: Another basic lua query
Sadly, though this is correct in spirit, it won't work in practice. That's because "and" and "or" are words that Lua considers special, so you can't use them there. You would need to either use the representation withoutSpannerbag wrote: ↑June 22nd, 2025, 11:31 amCode: Select all
{ canrecruit='yes', wml.tag.and { side=2, wml.tag.or {race='undead'} } }
wml.tag
here, or the alternate way of calling wml.tag
that Ravana mentioned:Code: Select all
{
canrecruit='yes',
wml.tag['and'] { side=2, wml.tag['or'] {race='undead'} }
}
This one is also wrong. There shouldn't be an extra set of braces aroungSpannerbag wrote: ↑June 22nd, 2025, 11:31 amCode: Select all
{ canrecruit='yes', {'and', { side=2 }, { 'or', race='undead' } } }
side=2
, but you do need braces around the full content of 'and'
:Code: Select all
{
canrecruit='yes',
{'and', {
side=2,
{ 'or', race='undead' }
}}
}
Re: Another basic lua query
Actually, you can put a comma after the last one if you want; Lua will allow a trailing comma in a table constructor.Spannerbag wrote: ↑June 22nd, 2025, 10:56 am Put comma after every parameter (except the last one of course) to tell lua where each one ends.
(This only works for tables - Lua doesn't allow a trailing comma in other places like function calls.)
- Spannerbag
- Posts: 777
- Joined: December 18th, 2016, 6:14 pm
- Location: Yes
Re: Another basic lua query
OK, can't use lua reserved words in wml.tag, got it (hope I remember it!).Celtic_Minstrel wrote: ↑June 22nd, 2025, 1:20 pmSadly, though this is correct in spirit, it won't work in practice. That's because "and" and "or" are words that Lua considers special, so you can't use them there...Spannerbag wrote: ↑June 22nd, 2025, 11:31 amCode: Select all
{ canrecruit='yes', wml.tag.and { side=2, wml.tag.or {race='undead'} } }

Thanks for pointing that out.
Aaarghh, forgot I was usingCeltic_Minstrel wrote: ↑June 22nd, 2025, 1:20 pm...You would need to either use the representation withoutwml.tag
here, or the alternate way of callingwml.tag
that Ravana mentioned:
Code: Select all
{ canrecruit='yes', wml.tag['and'] { side=2, wml.tag['or'] {race='undead'} } }
This one is also wrong. There shouldn't be an extra set of braces aroungSpannerbag wrote: ↑June 22nd, 2025, 11:31 amCode: Select all
{ canrecruit='yes', {'and', { side=2 }, { 'or', race='undead' } } }
side=2
, but you do need braces around the full content of'and'
:
Code: Select all
{ canrecruit='yes', {'and', { side=2, { 'or', race='undead' } }} }
and
in lua context (not wml.tag). 
Right... I think I've got that straight...
I was mulling over maybe having a hack at some text and examples for a wiki page: Lua introduction for WML coders for those who are interested - once I feel a bit more confident myself of course (anything I write now would be a great example of how not to do stuff in lua...).
Incorporating lua into a campaign is well documented and the existing wiki is a great reference once you know what you're doing.
However some grounding in basic syntax (with relevant examples) might provide people new to lua some signposts around the more common gotchas?
That is, it's not meant to be an exhaustive tutorial more a launch pad so readers aren't (quite as) overloaded when they refer to resources that assume knowledge/familiarity they may not have (and would maybe struggle to acquire through normal research).
Of course if there's such an introduction in the wiki already and I've missed it please let me know!
Thanks once again for your patience in guiding a slow learner out of the fog...

Cheers!
-- Spannerbag
- Spannerbag
- Posts: 777
- Joined: December 18th, 2016, 6:14 pm
- Location: Yes
Re: Another basic lua query
Ah, OK,another little wrinkle to remember!gnombat wrote: ↑June 22nd, 2025, 3:31 pmActually, you can put a comma after the last one if you want; Lua will allow a trailing comma in a table constructor.Spannerbag wrote: ↑June 22nd, 2025, 10:56 am Put comma after every parameter (except the last one of course) to tell lua where each one ends.
(This only works for tables - Lua doesn't allow a trailing comma in other places like function calls.)

Thanks for the heads-up.
Cheers!
-- Spannerbag