Difference between revisions of "LuaWML"

From The Battle for Wesnoth Wiki
(Updated documentation)
m (Skeleton of a lua tag)
 
(184 intermediate revisions by 26 users not shown)
Line 3: Line 3:
 
== The '''[lua]''' tag ==
 
== The '''[lua]''' tag ==
  
{{DevFeature}}
+
This tag is a part of [[ActionWML]], thus can be used inside [event] and at other places where [[ActionWML]] can be used. It makes it possible to write actions with the [http://www.lua.org Lua 5.3] language.
  
This tag is a subtag of the '''[event]''' tag. It makes it possible to write actions with the [http://www.lua.org Lua 5.1] language.
+
It is also possible to put this tag inside a [scenario] [[ScenarioWML]], those tags will be executed immediately when the lua engine loads which is even before the scenario preload event is fired.
 +
 
 +
[lua] is now also allowed in [era], [modification] and [resource], those [lua] tags are then copied into the [scenario]/[multiplayer] when it is played just like [event]s inside [era] or [modification]
  
 
The tag supports only the '''code''' key, which is a string containing the Lua scripts. Since Lua makes usage of the quotes and the { and } symbols, it is certainly wise to enclose the script between stronger quotes, as they prevent the preprocessor from performing macro expansion and tokenization.
 
The tag supports only the '''code''' key, which is a string containing the Lua scripts. Since Lua makes usage of the quotes and the { and } symbols, it is certainly wise to enclose the script between stronger quotes, as they prevent the preprocessor from performing macro expansion and tokenization.
  
 +
<syntaxhighlight lang='wml'>
 
  [lua]
 
  [lua]
 
     code = << wesnoth.message "Hello World!" >>
 
     code = << wesnoth.message "Hello World!" >>
 
  [/lua]
 
  [/lua]
 +
</syntaxhighlight>
  
 
The Lua kernel can also be accessed from the [[CommandMode|command mode]]:
 
The Lua kernel can also be accessed from the [[CommandMode|command mode]]:
Line 19: Line 23:
 
The '''[args]''' sub-tag can be used to pass a WML object to the script via its variadic local variable "'''...'''".
 
The '''[args]''' sub-tag can be used to pass a WML object to the script via its variadic local variable "'''...'''".
  
 +
<syntaxhighlight lang='wml'>
 
  [lua]
 
  [lua]
 
     code = << local t = ...; wesnoth.message(tostring(t.text)) >>
 
     code = << local t = ...; wesnoth.message(tostring(t.text)) >>
Line 25: Line 30:
 
     [/args]
 
     [/args]
 
  [/lua]
 
  [/lua]
 +
</syntaxhighlight>
  
 
== Global environment ==
 
== Global environment ==
  
All the Lua scripts of a scenario share the same global environment (aka Lua state). For instance, a function defined in an event can be used in all the events that happen after it.
+
All the Lua scripts of a scenario share the same global environment (aka Lua state). Unlike other parts of the configurable gamestate the Lua state is not stored in savefiles, thus [lua] tags in [scenario] are executed not only before the scenario starts but also each time the game is loaded. Funtions defined in [lua] tags in [scenario] can be used in all [lua] tags in [event]s.
  
  [event]
+
<syntaxhighlight lang='wml'>
    name = preload
+
  [scenario]
    first_time_only = no
 
 
     [lua]
 
     [lua]
 
         code = <<
 
         code = <<
Line 42: Line 47:
 
         >>
 
         >>
 
     [/lua]
 
     [/lua]
  [/event]
+
    [event]
 +
        name = turn 1
 +
        [lua]
 +
            code = << narrator(...) >>
 +
            [args]
 +
                sentence = _ "Hello world!"
 +
            [/args]
 +
        [/lua]
 +
        [lua]
 +
            code = << narrator(...) >>
 +
            [args]
 +
                sentence = _ "How are you today?"
 +
            [/args]
 +
        [/lua]
 +
    [/event]
 +
    ...
 +
  [/scenario]
 
   
 
   
[event]
+
</syntaxhighlight>
    name = turn 1
 
    [lua]
 
        code = << narrator(...) >>
 
        [args]
 
            sentence = _ "Hello world!"
 
        [/args]
 
    [/lua]
 
    [lua]
 
        code = << narrator(...) >>
 
        [args]
 
            sentence = _ "How are you today?"
 
        [/args]
 
    [/lua]
 
[/event]
 
  
 
In the example above, the redundant structure could be hidden behind macros. But it may be better to simply define a new WML tag.
 
In the example above, the redundant structure could be hidden behind macros. But it may be better to simply define a new WML tag.
  
  [event]
+
<syntaxhighlight lang='wml'>
    name = preload
+
  [scenario]
    first_time_only = no
 
 
     [lua]
 
     [lua]
 
         code = <<
 
         code = <<
             -- The function is now local, since its name does not have to be
+
             -- The function is now placed in the wesnoth.wml_actions table
             -- visible outside this Lua scripts.
+
             -- The tag is [narrator], same as the function name
             local function handler(t)
+
             function wesnoth.wml_actions.narrator(t)
 
                 -- Behave like the [message] tag.
 
                 -- Behave like the [message] tag.
 
                 wesnoth.fire("message",
 
                 wesnoth.fire("message",
 
                   { speaker = "narrator", message = t.sentence })
 
                   { speaker = "narrator", message = t.sentence })
 
             end
 
             end
            -- Create a new tag named [narrator].
 
            wesnoth.register_wml_action("narrator", handler)
 
 
         >>
 
         >>
 
     [/lua]
 
     [/lua]
[/event]
 
 
   
 
   
[event]
+
    [event]
    name = turn 1
+
        name = turn 1
    [narrator]
+
        [narrator]
        sentence = _ "Hello world!"
+
            sentence = _ "Hello world!"
    [/narrator]
+
        [/narrator]
    [narrator]
+
        [narrator]
        sentence = _ "How are you today?"
+
            sentence = _ "How are you today?"
     [/narrator]
+
        [/narrator]
  [/event]
+
     [/event]
 +
  [/scenario]
 +
</syntaxhighlight>
  
The global environment is not preserved over save/load cycles. Therefore, storing values in the global environment is generally a bad idea (unless it has been redirected to WML variables, see [[#set_wml_var_metatable|set_wml_var_metatable]]). The only time assigning global variables (including function definitions) makes sense is during a [[EventWML#Predefined 'name' Key Values|preload]] event, as this event is always run. Therefore, helper functions defined at that time will be available to all the later scripts.
+
The global environment is not preserved over save/load cycles. Therefore, storing values in the global environment is generally a bad idea. The only time assigning global variables (including function definitions) makes sense is in a [lua] block directly in [scenario] or during a [[EventWML#Predefined 'name' Key Values|preload]] event, as this event is always run. Therefore, helper functions defined at that time will be available to all the later scripts.
  
The global environment initially contains the following modules: [http://www.lua.org/manual/5.1/manual.html#5.1 basic] (no name), [http://www.lua.org/manual/5.1/manual.html#5.4 string], [http://www.lua.org/manual/5.1/manual.html#5.5 table], and [http://www.lua.org/manual/5.1/manual.html#5.6 math]. A '''wesnoth''' module is also available, it provides access to the [[#Interface to the C++ engine|C++ engine]].
+
The global environment initially contains the following modules: [http://www.lua.org/manual/5.3/manual.html#6.1 basic] (no name), [http://www.lua.org/manual/5.3/manual.html#6.4 string], [http://www.lua.org/manual/5.3/manual.html#6.6 table], and [http://www.lua.org/manual/5.3/manual.html#6.7 math]. A '''wesnoth''' module is also available, it provides access to the [[#Interface to the C++ engine|C++ engine]]. Additionally, the functions clock, date, time and difftime from the [http://www.lua.org/manual/5.3/manual.html#6.9 os] library (keep in mind that they aren't multiplayer- and replay-safe), as well as traceback from the [http://www.lua.org/manual/5.3/manual.html#6.10 debug] library are also available.
  
At the start of a script, the variadic local variable '''...''' (three dots) is a proxy table representing [[#Encoding WML objects into Lua tables|WML data]]. This table is the content of the '''[args]''' sub-tag of the '''[lua]''' tag, if any. The table also provides the fields '''x1''', '''y1''', '''x2''', and '''y2''', containing map locations, and the sub-tables '''weapon''' and '''second_weapon''' containing attacks, if relevant for the current event.
+
At the start of a script, the variadic local variable '''...''' (three dots) is a proxy table representing [[#Encoding WML objects into Lua tables|WML data]]. This table is the content of the '''[args]''' sub-tag of the '''[lua]''' tag, if any.
  
 
== Examples ==
 
== Examples ==
Line 100: Line 105:
 
The following WML event is taken from Wesnoth' tutorial. It will serve as an example to present how Lua scripts are embedded into Wesnoth. The event is fired whenever a unit from side 1 (that is, the hero controlled by the user) moves to a tile that is not the one set in the WML variable ''target_hex''.
 
The following WML event is taken from Wesnoth' tutorial. It will serve as an example to present how Lua scripts are embedded into Wesnoth. The event is fired whenever a unit from side 1 (that is, the hero controlled by the user) moves to a tile that is not the one set in the WML variable ''target_hex''.
  
 +
<syntaxhighlight lang='wml'>
 
  # General catch for them moving to the wrong place.
 
  # General catch for them moving to the wrong place.
 
  [event]
 
  [event]
Line 140: Line 146:
 
     [/if]
 
     [/if]
 
  [/event]
 
  [/event]
 +
</syntaxhighlight>
  
 
A Lua script that performs the same action is presented below.
 
A Lua script that performs the same action is presented below.
  
 +
<syntaxhighlight lang='wml'>
 
  [event]
 
  [event]
 
     name=moveto
 
     name=moveto
Line 153: Line 161:
 
     [lua]
 
     [lua]
 
         code = <<
 
         code = <<
             local args = ...
+
 
             if target_hex.is_set and
+
             local event_data = wesnoth.current.event_context
                 (args.x1 ~= target_hex.x or args.y1 ~= target_hex.y)
+
             if wml.variables["target_hex.is_set"] and
 +
                 (event_data.x1 ~= wml.variables["target_hex.x"] or event_data.y1 ~= wml.variables["target_hex.y"])
 
             then
 
             then
 
                 W.redraw()
 
                 W.redraw()
Line 163: Line 172:
 
     [/lua]
 
     [/lua]
 
  [/event]
 
  [/event]
 +
</syntaxhighlight>
  
 
Here is a more detailed explanation of the Lua code. Its first line
 
Here is a more detailed explanation of the Lua code. Its first line
  
local args = ...
+
<syntaxhighlight lang='lua'>
 +
local event_data = wesnoth.current.event_context
 +
</syntaxhighlight>
  
loads the parameter of the script into the ''args'' local variable. Since it is a ''moveto'' event, the ''args'' table contains the destination of the unit in the ''x1'' and ''y1'' fields.
+
puts the event data into the ''event_data'' local variable. Since it is a ''moveto'' event, the ''event_data'' table contains the destination of the unit in the ''x1'' and ''y1'' fields.
  
 
The next two lines then test
 
The next two lines then test
  
if target_hex.is_set and
+
<syntaxhighlight lang='lua'>
     (args.x1 ~= target_hex.x or args.y1 ~= target_hex.y)
+
if wml.variables["target_hex.is_set"] and
 +
     (event_data.x1 ~= wml.variables["target_hex.x"] or event_data.y1 ~= wml.variables["target_hex.y"])
 +
</syntaxhighlight>
  
whether the variable ''target_hex'' matches the event parameters. Since ''target_hex'' is not a local variable, it is taken from the global environment (a table implicitly named ''_G'', so it is actually ''_G.target_hex''). The global environment is not persistent, so it cannot be used to store data. In order to make it useful, it was mapped to the storage of WML variables by the following ''preload'' event.
+
whether the variable ''wml.variables["target_hex"]'' matches the event parameters. Since ''wml.variables'' is not a local variable, it is taken from the global environment. Usually, variables from the global environment are not persistent but the wesnoth engine maps the variable wml.variables to the storage of WML variables.
  
[event]
+
Without using wml.variables, the conditional would have been written
    name=preload
+
 
    first_time_only=no
+
<syntaxhighlight lang='lua'>
 +
if wesnoth.get_variable("target_hex.is_set") and
 +
    (event_data.x1 ~= wesnoth.get_variable("target_hex.x") or event_data.y1 ~= wesnoth.get_variable("target_hex.y")
 +
</syntaxhighlight>
 +
 
 +
The body of the conditional then performs the [[InterfaceActionsWML#Other interface tags|[redraw]]] action.
 +
 
 +
<syntaxhighlight lang='lua'>
 +
W.redraw()
 +
</syntaxhighlight>
 +
 
 +
This short syntax is made possible by a line of the prelude that makes ''W'' a proxy for performing WML actions.
 +
 
 +
<syntaxhighlight lang='lua'>
 +
[scenario]
 
     [lua]
 
     [lua]
 
         code = <<
 
         code = <<
 
             H = wesnoth.require "lua/helper.lua"
 
             H = wesnoth.require "lua/helper.lua"
             -- skipping some other initializations
+
             W = H.set_wml_action_metatable {}
            -- ...
 
            H.set_wml_var_metatable(_G)
 
 
         >>
 
         >>
 
     [/lua]
 
     [/lua]
[/event]
+
    ...
 
+
[/scenario]
Without a prelude redirecting ''_G'', the conditional would have been written
+
</syntaxhighlight>
 
 
if wesnoth.get_variable("target_hex.is_set") and
 
    (args.x1 ~= wesnoth.get_variable("target_hex.x") or args.y1 ~= wesnoth.get_variable("target_hex.y")
 
 
 
The body of the conditional then performs the [[InterfaceActionsWML#Other interface tags|redraw]] action.
 
 
 
W.redraw()
 
 
 
Again, this short syntax is made possible by a line of the prelude that makes ''W'' a proxy for performing WML actions.
 
 
 
W = H.set_wml_action_metatable {}
 
  
 
Without this shortcut, the first statement would have been written
 
Without this shortcut, the first statement would have been written
  
wesnoth.fire("redraw")
+
<syntaxhighlight lang='lua'>
 +
wesnoth.fire("redraw")
 +
</syntaxhighlight>
 +
or
 +
<syntaxhighlight lang='lua'>
 +
wesnoth.wml_actions.redraw {}
 +
</syntaxhighlight>
  
 
Finally the script displays a message by
 
Finally the script displays a message by
  
narrator_says(_ "*Oops!\nYou moved to the wrong place! After this message, you can press 'u' to undo, then try again.")
+
<syntaxhighlight lang='lua'>
 +
narrator_says(_ "*Oops!\nYou moved to the wrong place! After this message, you can press 'u' to undo, then try again.")
 +
</syntaxhighlight>
  
 
The ''narrator_says'' function is defined in the prelude too, since the construct behind it occurs several times in the tutorial. In plain WML, macros would have been used instead. The definition of the function is
 
The ''narrator_says'' function is defined in the prelude too, since the construct behind it occurs several times in the tutorial. In plain WML, macros would have been used instead. The definition of the function is
  
function narrator_says(m)
+
<syntaxhighlight lang='lua'>
    W.message { speaker="narrator",
+
function narrator_says(m)
                message = m .. _ "\n*Left click or press spacebar to continue..." }
+
    W.message { speaker="narrator",
end
+
                message = m .. _ "\n*Left click or press spacebar to continue..." }
 +
end
 +
</syntaxhighlight>
  
The function fires a [[InterfaceActionsWML#[message]|[message]]] action and passes a WML object containing the usual two fields to it. The second field is initialized by concatenating the function argument with another string. Both strings are prefixed by the ''_'' symbol to mark them as translatable. (Note that ''_'' is just a unary function, not a keyword.) Again, this is made possible by a specific line of the prelude:
+
The function fires a [[InterfaceActionsWML#.5Bmessage.5D|[message]]] action and passes a WML object containing the usual two fields to it. The second field is initialized by concatenating the function argument with another string. Both strings are prefixed by the ''_'' symbol to mark them as translatable. (Note that ''_'' is just a unary function, not a keyword.) Again, this is made possible by a specific line of the prelude:
  
_ = wesnoth.textdomain "wesnoth-tutorial"
+
<syntaxhighlight lang='lua'>
 +
_ = wesnoth.textdomain "wesnoth-tutorial"
 +
</syntaxhighlight>
  
 
A longer translation of the tutorial is available at [https://gna.org/patch/download.php?file_id=5483].
 
A longer translation of the tutorial is available at [https://gna.org/patch/download.php?file_id=5483].
  
== Interface to the C++ engine ==
+
== Interface to the engine and helper functions ==
 
 
Functionalities of the game engine are available through the functions of the '''wesnoth''' global table. Some of these functions return proxy tables. Writes to fields marked "read-only" are ignored. The '''__cfg''' fields return plain tables; in particular, writes do not modify the original object, and reads return the values from the time the dump was performed.
 
 
 
==== message ====
 
 
 
Displays a string in the chat window and dumps it to the lua/info log domain (''--log-info=scripting/lua'' on the command-line).
 
 
 
wesnoth.message "Hello World!"
 
 
 
The chat line header is "<Lua>" by default, but it can be changed by passing a string before the message.
 
 
 
wesnoth.message("Big Brother", "I'm watching you.") -- will result in "&lt;Big Brother&gt; I'm watching you."
 
 
 
==== fire ====
 
 
 
Fires a WML action. Argument 1 is the name of the action. Argument 2 is the WML table describing the action.
 
 
 
wesnoth.fire("message", { speaker="narrator", message=_ "Hello World!" })
 
 
 
==== get_variable ====
 
 
 
Loads a variable with the given WML name (argument 1) and converts it into a Lua object. Returns ''nil'' if the name does not point to anything, a scalar for a WML attribute, and a table for a WML object. Argument 2, if ''true'', prevents the recursive conversion if the name points to an object; an empty table is returned in this case.
 
 
 
wesnoth.fire("store_unit", { variable="my_unit", { "filter", { id="hero" } } })
 
local heros_hp = wesnoth.get_variable("my_unit[0].hitpoints")
 
 
 
==== set_variable ====
 
 
 
Converts and stores a Lua object (argument 2) to a WML variable (argument 1). A WML object is created for a table, an attribute otherwise.
 
 
 
wesnoth.set_variable("my_unit.hitpoints", heros_hp + 10)
 
 
 
==== textdomain ====
 
 
 
Creates a function proxy for lazily translating strings from the given domain.
 
 
 
_ = wesnoth.textdomain("my-campaign")
 
wesnoth.set_variable("my_unit.description", _ "the unit formerly known as Hero")
 
  
The metatable of the function proxy appears as '''"message domain"'''. The metatable of the translatable strings (results of the proxy) are '''"translatable string"'''.
+
<br><div class="navtemplate" style="width: auto; max-width: 75%; margin: 0 auto; border: 1px solid #c60; border-left-width: 8px"><div style="float:left;position:relative;top:-14px;left:-6px">https://raw.github.com/wesnoth/wesnoth/master/data/core/images/items/bones.png</div><p style="margin:0"><strong>This section is in the process of being moved.</strong> The information here remains accurate as of 1.14.0, but for 1.15.0 and later, [[LuaAPI]] is planned to be the definitive source.</p></div><br>
  
==== dofile ====
+
Functionalities of the game engine are available through the functions contained in the '''wesnoth''' global table. Some of these functions return proxy tables. Writes to fields marked "read-only" are ignored. The '''__cfg''' fields return plain tables; in particular, writes do not modify the original object, and reads return the values from the time the dump was performed.
  
Replaces [http://www.lua.org/manual/5.1/manual.html#5.1 basic.dofile] for loading files. Loads the given filename (relative to the content directory) and executes it in an unprotected environment (that is, exceptions propagate to the caller). Returns the values returned by the executed script.
+
Some helper functions are provided by the '''lua/helper.lua''' library. They are stored inside a table that is returned when loading the library with [[LuaWML:Files#wesnoth.require|wesnoth.require]].
  
wesnoth.dofile "~add-ons/MyCampaign/lua/scenario-utils.lua"
+
<syntaxhighlight lang='lua'>
 +
helper = wesnoth.require "lua/helper.lua"
 +
</syntaxhighlight>
  
It may be helpful to put as many Lua code as possible in specific files instead of embedding it into WML files, so as to not confuse text editors. Then a scenario only needs to contain the following event:
+
=== WML variables ===
 
 
[event]
 
    name = preload
 
    first_time_only = no
 
    [lua]
 
        code = << wesnoth.dofile "~add-ons/MyCampaign/lua/scenario-utils.lua" >>
 
    [/lua]
 
[/event]
 
  
If the same files need to be loaded for all the scenarios, the [lua] tag above can be directly put inside the '''_main.cfg''' file (or equivalent file). The Lua code will then be executed at the start of each scenario.
+
* [[LuaWML:Variables#wesnoth.get_variable|wesnoth.get_variable]]
 +
* [[LuaWML:Variables#wesnoth.set_variable|wesnoth.set_variable]]
 +
* [[LuaWML:Variables#wesnoth.get_all_vars|wesnoth.get_all_vars]] {{DevFeature1.13|0}}
 +
* [[LuaWML:Variables#wesnoth.wml_matches_filter|wesnoth.wml_matches_filter]] {{DevFeature1.13|8}}
 +
* [[LuaWML:Variables#helper.set_wml_var_metatable|helper.set_wml_var_metatable]]
 +
* [[LuaWML:Variables#helper.get_child|helper.get_child]]
 +
* [[LuaWML:Variables#helper.get_nth_child|helper.get_nth_child]] {{DevFeature1.13|2}}
 +
* [[LuaWML:Variables#helper.child_count|helper.child_count]] {{DevFeature1.13|2}}
 +
* [[LuaWML:Variables#helper.child_range|helper.child_range]]
 +
* [[LuaWML:Variables#helper.child_array|helper.child_array]] {{DevFeature1.13|2}}
 +
* [[LuaWML:Variables#helper.get_variable_array|helper.get_variable_array]]
 +
* [[LuaWML:Variables#helper.get_variable_proxy_array|helper.get_variable_proxy_array]]
 +
* [[LuaWML:Variables#helper.set_variable_array|helper.set_variable_array]]
  
==== require ====
+
=== Events and WML actions ===
  
Loads the given filename (relative to the content directory) and executes it in a protected environment. If the file has already been executed once, then loading, compilation, and execution, are skipped and the return value of its previous run is returned again.
+
* [[LuaWML:Events#wesnoth.fire|wesnoth.fire]]
 +
* [[LuaWML:Events#wesnoth.wml_actions|wesnoth.wml_actions]]
 +
* [[LuaWML:Events#wesnoth.wml_actions|wesnoth.wml_conditionals]] {{DevFeature1.13|0}}
 +
* [[LuaWML:Events#wesnoth.game_events|wesnoth.game_events]]
 +
* [[LuaWML:Events#wesnoth.persistent_tags|wesnoth.persistent_tags]] {{DevFeature1.13|12}}
 +
* [[LuaWML:Events#wesnoth.fire_event|wesnoth.fire_event]]
 +
* [[LuaWML:Events#wesnoth.fire_event_by_id|wesnoth.fire_event_by_id]] {{DevFeature1.13|6}}
 +
* [[LuaWML:Events#wesnoth.add_event_handler|wesnoth.add_event_handler]] {{DevFeature1.13|0}}
 +
* [[LuaWML:Events#wesnoth.remove_event_handler|wesnoth.remove_event_handler]] {{DevFeature1.13|0}}
 +
* [[LuaWML:Events#wesnoth.eval_conditional|wesnoth.eval_conditional]]
 +
* [[LuaWML:Events#wesnoth.tovconfig|wesnoth.tovconfig]]
 +
* [[LuaWML:Events#helper.set_wml_action_metatable|helper.set_wml_action_metatable]]
 +
* [[LuaWML:Events#helper.wml_error|helper.wml_error]]
 +
* [[LuaWML:Events#helper.literal|helper.literal]]
 +
* [[LuaWML:Events#helper.parsed|helper.parsed]]
 +
* [[LuaWML:Events#helper.shallow_literal|helper.shallow_literal]]
 +
* [[LuaWML:Events#helper.shallow_parsed|helper.shallow_parsed]]
 +
* [[LuaWML:Events#on_event.lua|on_event.on_event]] {{DevFeature1.13|6}}
  
helper = wesnoth.require "lua/helper.lua"
+
=== User interface ===
 +
* [[LuaWML:Display#wesnoth.get_viewing_side|wesnoth.get_viewing_side]] {{DevFeature1.13|?}}
 +
* [[LuaWML:Display#wesnoth.message|wesnoth.message]]
 +
* [[LuaWML:Display#wesnoth.clear_messages|wesnoth.clear_messages]]
 +
* [[LuaWML:Display#wesnoth.textdomain|wesnoth.textdomain]]
 +
* [[LuaWML:Display#wesnoth.delay|wesnoth.delay]]
 +
* [[LuaWML:Display#wesnoth.float_label|wesnoth.float_label]]
 +
* [[LuaWML:Display#wesnoth.select_unit|wesnoth.select_hex]]
 +
* [[LuaWML:Display#wesnoth.select_unit|wesnoth.select_unit]]
 +
* [[LuaWML:Display#wesnoth.highlight_hex|wesnoth.highlight_hex]]
 +
* [[LuaWML:Display#wesnoth.deselect_hex|wesnoth.deselect_hex]] {{DevFeature1.13|2}}
 +
* [[LuaWML:Display#wesnoth.scroll_to_tile|wesnoth.scroll_to_tile]]
 +
* [[LuaWML:Display#wesnoth.lock_view|wesnoth.lock_view]]
 +
* [[LuaWML:Display#wesnoth.view_locked|wesnoth.view_locked]]
 +
* [[LuaWML:Display#wesnoth.play_sound|wesnoth.play_sound]]
 +
* [[LuaWML:Display#wesnoth.set_music|wesnoth.set_music]]
 +
* [[LuaWML:Display#wesnoth.music_list|wesnoth.music_list]] {{DevFeature1.13|8}}
 +
* [[LuaWML:Display#wesnoth.sound_volume|wesnoth.sound_volume]] {{DevFeature1.13|8}}
 +
* [[LuaWML:Display#wesnoth.show_message_dialog|wesnoth.show_message_dialog]] {{DevFeature1.13|2}}
 +
* [[LuaWML:Display#wesnoth.show_popup_dialog|wesnoth.show_popup_dialog]] {{DevFeature1.13|2}}
 +
* [[LuaWML:Display#wesnoth.show_story|wesnoth.show_story]] {{DevFeature1.13|8}}
 +
* [[LuaWML:Display#wesnoth.show_message_box|wesnoth.show_message_box]] {{DevFeature1.13|8}}
 +
* [[LuaWML:Display#wesnoth.show_menu|wesnoth.show_menu]] {{DevFeature1.13|8}}
 +
* [[LuaWML:Display#wesnoth.show_message_box|wesnoth.alert]] {{DevFeature1.13|8}}
 +
* [[LuaWML:Display#wesnoth.show_message_box|wesnoth.confirm]] {{DevFeature1.13|8}}
 +
* [[LuaWML:Display#wesnoth.show_dialog|wesnoth.show_dialog]]
 +
* [[LuaWML:Display#wesnoth.set_dialog_value|wesnoth.set_dialog_value]]
 +
* [[LuaWML:Display#wesnoth.get_dialog_value|wesnoth.get_dialog_value]]
 +
* [[LuaWML:Display#wesnoth.set_dialog_active|wesnoth.set_dialog_active]]
 +
* [[LuaWML:Display#wesnoth.set_dialog_callback|wesnoth.set_dialog_callback]]
 +
* [[LuaWML:Display#wesnoth.set_dialog_markup|wesnoth.set_dialog_markup]]
 +
* [[LuaWML:Display#wesnoth.set_dialog_focus|wesnoth.set_dialog_focus]] {{DevFeature1.13|2}}
 +
* [[LuaWML:Display#wesnoth.set_dialog_visible|wesnoth.set_dialog_visible]] {{DevFeature1.13|2}}
 +
* [[LuaWML:Display#wesnoth.set_dialog_canvas|wesnoth.set_dialog_canvas]]
 +
* [[LuaWML:Display#wesnoth.add_dialog_tree_node|wesnoth.add_dialog_tree_node]] {{DevFeature1.13|0}}
 +
* [[LuaWML:Display#wesnoth.remove_dialog_item|wesnoth.remove_dialog_item]] {{DevFeature1.13|1}}
 +
* [[LuaWML:Display#wesnoth.get_displayed_unit|wesnoth.get_displayed_unit]]
 +
* [[LuaWML:Display#wesnoth.theme_items|wesnoth.theme_items]]
 +
* [[LuaWML:Display#helper.get_user_choice|helper.get_user_choice]]
 +
* [[LuaWML:Display#wesnoth.is_skipping_messages|wesnoth.is_skipping_messages]] {{DevFeature1.13|2}}
 +
* [[LuaWML:Display#wesnoth.skip_messages|wesnoth.skip_messages]] {{DevFeature1.13|2}}
 +
* [[LuaWML:Display#wesnoth.log|wesnoth.log]] {{DevFeature1.13|5}}
 +
* [[LuaWML:Display#wesnoth.zoom|wesnoth.zoom]] {{DevFeature1.13|8}}
  
This function is helpful in writing libraries of functions that can be accessed from various places. So the return value of the file is supposed to be a table containing the methods provided by the library. Such a library would look like:
+
=== Map and terrains ===
  
local library = {}
+
* [[LuaWML:Tiles#wesnoth.get_map_size|wesnoth.get_map_size]]
function library.do_something(a) ... end
+
* [[LuaWML:Tiles#wesnoth.get_terrain|wesnoth.get_terrain]]
function library.go_somewhere(x, y) ... end
+
* [[LuaWML:Tiles#wesnoth.set_terrain|wesnoth.set_terrain]]
return library
+
* [[LuaWML:Tiles#wesnoth.get_terrain_info|wesnoth.get_terrain_info]]
 +
* [[LuaWML:Tiles#wesnoth.get_selected_tile|wesnoth.get_selected_tile]]
 +
* [[LuaWML:Tiles#wesnoth.get_locations|wesnoth.get_locations]]
 +
* [[LuaWML:Tiles#wesnoth.get_villages|wesnoth.get_villages]]
 +
* [[LuaWML:Tiles#wesnoth.match_location|wesnoth.match_location]]
 +
* [[LuaWML:Tiles#wesnoth.add_tile_overlay|wesnoth.add_tile_overlay]]
 +
* [[LuaWML:Tiles#wesnoth.remove_tile_overlay|wesnoth.remove_tile_overlay]]
 +
* [[LuaWML:Tiles#wesnoth.add_fog|wesnoth.add_fog]] {{DevFeature1.13|5}}
 +
* [[LuaWML:Tiles#wesnoth.remove_fog|wesnoth.remove_fog]] {{DevFeature1.13|5}}
 +
* [[LuaWML:Tiles#wesnoth.add_sound_source|wesnoth.add_sound_source]] {{DevFeature1.13|5}}
 +
* [[LuaWML:Tiles#wesnoth.remove_sound_source|wesnoth.remove_sound_source]] {{DevFeature1.13|5}}
 +
* [[LuaWML:Tiles#wesnoth.get_sound_source|wesnoth.get_sound_source]] {{DevFeature1.13|5}}
 +
* [[LuaWML:Tiles#wesnoth.map.get_direction|wesnoth.map.get_direction]] {{DevFeature1.13|8}}
 +
* [[LuaWML:Tiles#wesnoth.map.get_relative_dir|wesnoth.map.get_relative_dir]] {{DevFeature1.13|8}}
 +
* [[LuaWML:Tiles#wesnoth.map.rotate_right_around_center|wesnoth.map.rotate_right_around_center]] {{DevFeature1.13|8}}
 +
* [[LuaWML:Tiles#wesnoth.map.get_adjacent_tiles|wesnoth.map.get_adjacent_tiles]] {{DevFeature1.13|8}}
 +
* [[LuaWML:Tiles#wesnoth.map.tiles_adjacent|wesnoth.map.tiles_adjacent]] {{DevFeature1.13|8}}
 +
* [[LuaWML:Tiles#wesnoth.map.distance_between|wesnoth.map.distance_between]] {{DevFeature1.13|8}}
 +
* [[LuaWML:Tiles#wesnoth.label|wesnoth.label]] {{DevFeature1.13|0}}
 +
* [[LuaWML:Tiles#wesnoth.special_locations|wesnoth.special_locations]] {{DevFeature1.13|12}}
 +
* [[LuaWML:Tiles#items.place_image|items.place_image]]
 +
* [[LuaWML:Tiles#items.place_halo|items.place_halo]]
 +
* [[LuaWML:Tiles#items.remove|items.remove]]
  
It can also be helpful when writing eras, since you may not have the opportunity to call [[#dofile|wesnoth.dofile]] during a ''preload'' event:
+
=== Time of day schedule ===
  
[unit_type]
+
* [[LuaWML:Time#wesnoth.get_time_of_day|wesnoth.get_time_of_day]]
    id = phoenix
+
* [[LuaWML:Time#wesnoth.add_time_area|wesnoth.add_time_area]] {{DevFeature1.13|0}}
    [event]
+
* [[LuaWML:Time#wesnoth.remove_time_area|wesnoth.remove_time_area]] {{DevFeature1.13|0}}
        name = last breath
+
* [[LuaWML:Time#wesnoth.get_max_liminal_bonus|wesnoth.get_max_liminal_bonus]] {{DevFeature1.15|4}}
        [lua]
 
            code = << wesnoth.require("~add-ons/MyEra/lua/unit-utils.lua").resurrect(...) >>
 
        [/lua]
 
    [/event]
 
[/unit_type]
 
  
==== register_wml_action ====
+
=== Units ===
  
Registers the second argument as a handler for the given action tag. When the game encounters this tag an event, it fires the action handler and passes the content of the WML object as the first argument. If the registered function raises an error with a message starting with ''~wml:'', it will not be displayed as a Lua error with a backtrace but as a standard WML error. The following script defines a [freeze_unit] tag that sets to 0 the move points of the unit with the given id.
+
* [[LuaWML:Units#wesnoth.get_units|wesnoth.get_units]]
 +
* [[LuaWML:Units#wesnoth.get_unit|wesnoth.get_unit]]
 +
* [[LuaWML:Units#wesnoth.match_unit|wesnoth.match_unit]]
 +
* [[LuaWML:Units#wesnoth.put_unit|wesnoth.put_unit]]
 +
* [[LuaWML:Units#wesnoth.erase_unit|wesnoth.erase_unit]] {{DevFeature1.13|2}}
 +
* [[LuaWML:Units#wesnoth.get_recall_units|wesnoth.get_recall_units]]
 +
* [[LuaWML:Units#wesnoth.put_recall_unit|wesnoth.put_recall_unit]]
 +
* [[LuaWML:Units#wesnoth.create_unit|wesnoth.create_unit]]
 +
* [[LuaWML:Units#wesnoth.copy_unit|wesnoth.copy_unit]]
 +
* [[LuaWML:Units#wesnoth.extract_unit|wesnoth.extract_unit]]
 +
* [[LuaWML:Units#wesnoth.add_modification|wesnoth.add_modification]]
 +
* [[LuaWML:Units#wesnoth.remove_modifications|wesnoth.remove_modifications]] {{DevFeature1.13|?}}
 +
* [[LuaWML:Units#wesnoth.unit_resistance|wesnoth.unit_resistance]]
 +
* [[LuaWML:Units#wesnoth.unit_defense|wesnoth.unit_defense]]
 +
* [[LuaWML:Units#wesnoth.unit_movement_cost|wesnoth.unit_movement_cost]]
 +
* [[LuaWML:Units#wesnoth.unit_vision_cost|wesnoth.unit_vision_cost]]
 +
* [[LuaWML:Units#wesnoth.unit_jamming_cost|wesnoth.unit_jamming_cost]]
 +
* [[LuaWML:Units#wesnoth.unit_ability|wesnoth.unit_ability]]
 +
* [[LuaWML:Units#wesnoth.unit_types|wesnoth.unit_types]]
 +
* [[LuaWML:Units#wesnoth.races|wesnoth.races]]
 +
* [[LuaWML:Units#wesnoth.get_traits|wesnoth.get_traits]]
 +
* [[LuaWML:Units#wesnoth.advance_unit|wesnoth.advance_unit]] {{DevFeature1.13|?}}
 +
* [[LuaWML:Units#wesnoth.add_known_unit|wesnoth.add_known_unit]] {{DevFeature1.13|?}}
 +
* [[LuaWML:Units#wesnoth.simulate_combat|wesnoth.simulate_combat]]
 +
* [[LuaWML:Units#wesnoth.transform_unit|wesnoth.transform_unit]]
 +
* [[LuaWML:Units#wesnoth.create_animator|wesnoth.create_animator]] {{DevFeature1.13|7}}
 +
* [[LuaWML:Units#wesnoth.effects|wesnoth.effects]] {{DevFeature1.13|2}}
  
wesnoth.register_wml_action("freeze_unit",
+
=== Sides ===
    function(cfg)
 
        local unit_id = cfg.id or error("~wml:[freeze_unit] expects an id= attribute.", 0)
 
        helper.modify_unit({ id = unit_id }, { moves = 0 })
 
    end
 
)
 
  
# The new tag can now be used in plain WML code.
+
* [[LuaWML:Sides#wesnoth.sides|wesnoth.sides]]
[freeze_unit]
+
* [[LuaWML:Sides#wesnoth.get_sides|wesnoth.get_sides]]
    id=Delfador
+
* [[LuaWML:Sides#wesnoth.get_village_owner|wesnoth.get_village_owner]]
[/freeze_unit]
+
* [[LuaWML:Sides#wesnoth.set_village_owner|wesnoth.set_village_owner]]
 +
* [[LuaWML:Sides#wesnoth.is_enemy|wesnoth.is_enemy]]
 +
* [[LuaWML:Sides#wesnoth.match_side|wesnoth.match_side]]
 +
* [[LuaWML:Sides#wesnoth.get_starting_location|wesnoth.get_starting_location]]
 +
* [[LuaWML:Sides#wesnoth.set_side_id|wesnoth.set_side_id]] {{DevFeature1.13|7}}
 +
* [[LuaWML:Sides#wesnoth.place_shroud|wesnoth.place_shroud]] {{DevFeature1.13|7}}
 +
* [[LuaWML:Sides#wesnoth.remove_shroud|wesnoth.remove_shroud]] {{DevFeature1.13|7}}
 +
* [[LuaWML:Sides#wesnoth.is_fogged|wesnoth.is_fogged]] {{DevFeature1.13|7}}
 +
* [[LuaWML:Sides#wesnoth.is_shrouded|wesnoth.is_shrouded]] {{DevFeature1.13|7}}
 +
* [[LuaWML:Sides#wesnoth.switch_ai|wesnoth.switch_ai]] {{DevFeature1.13|7}}
 +
* [[LuaWML:Sides#wesnoth.append_ai|wesnoth.append_ai]] {{DevFeature1.13|7}}
 +
* [[LuaWML:Sides#wesnoth.add_ai_component|wesnoth.add_ai_component]] {{DevFeature1.13|7}}
 +
* [[LuaWML:Sides#wesnoth.change_ai_component|wesnoth.change_ai_component]] {{DevFeature1.13|7}}
 +
* [[LuaWML:Sides#wesnoth.delete_ai_component|wesnoth.delete_ai_component]] {{DevFeature1.13|7}}
 +
* [[LuaWML:Sides#helper.all_teams|helper.all_teams]]
 +
* [[LuaWML:Sides#helper.get_side_variable|helper.get_side_variable]] {{DevFeature1.13|?}}
 +
* [[LuaWML:Sides#helper.set_side_variable|helper.set_side_variable]] {{DevFeature1.13|?}}
  
The function returns the previous action handler. Its metatable appears as '''"wml action handler"'''. This handler can be called to delegate part of the action, if needed. For instance, the following script modifies the [[InterfaceActionsWML#Other interface tags|[print]]] tag so that messages are displayed with a bigger font.
+
=== Pathfinder ===
  
local old_print_handler
+
* [[LuaWML:Pathfinder#wesnoth.find_path|wesnoth.find_path]]
old_print_handler = wesnoth.register_wml_action("print",
+
* [[LuaWML:Pathfinder#wesnoth.find_vacant_tile|wesnoth.find_vacant_tile]]
    function(cfg)
+
* [[LuaWML:Pathfinder#wesnoth.find_reach|wesnoth.find_reach]]
        -- Do not perform variable substitution.
+
* [[LuaWML:Pathfinder#wesnoth.find_cost_map|wesnoth.find_cost_map]]
        local new_cfg = cfg.__literal
+
* [[LuaWML:Pathfinder#helper.distance_between|helper.distance_between]]
        -- Modify the size field and call the previous handler.
+
* [[LuaWML:Pathfinder#helper.adjacent_tiles|helper.adjacent_tiles]]
        new_cfg.size = (cfg.size or 12) + 10
 
        old_print_handler(cfg)
 
    end
 
)
 
  
Note that the data coming from the WML tag are stored in a read-only object and variable substitution happens on the fly when accessing attributes and children. The metatable of this proxy object appears as '''"wml object"''' See [[#Encoding WML objects into Lua tables]] for more details.
+
=== Lua files ===
  
==== get_units ====
+
* [[LuaWML:Files#wesnoth.dofile|wesnoth.dofile]]
 +
* [[LuaWML:Files#wesnoth.require|wesnoth.require]]
 +
* [[LuaWML:Files#wesnoth.have_file|wesnoth.have_file]] {{DevFeature1.13|5}}
 +
* [[LuaWML:Files#wesnoth.read_file|wesnoth.read_file]] {{DevFeature1.13|5}}
  
Returns an array of all the units matching the WML filter passed as the first argument.
+
=== Location sets ===
  
local leaders_on_side_two = get_units({ side = 2, canrecruit = true })
+
* [[LuaWML:Location_set#location_set.create|location_set.create]]
local name_of_leader = leaders_on_side_two[1].name
+
* [[LuaWML:Location_set#location_set.of_pairs|location_set.of_pairs]]
 +
* [[LuaWML:Location_set#location_set.of_wml_var|location_set.of_wml_var]]
 +
* [[LuaWML:Location_set#location_set:empty|location_set:empty]]
 +
* [[LuaWML:Location_set#location_set:size|location_set:size]]
 +
* [[LuaWML:Location_set#location_set:clear|location_set:clear]]
 +
* [[LuaWML:Location_set#location_set:get|location_set:get]]
 +
* [[LuaWML:Location_set#location_set:insert|location_set:insert]]
 +
* [[LuaWML:Location_set#location_set:remove|location_set:remove]]
 +
* [[LuaWML:Location_set#location_set:of_pairs|location_set:of_pairs]]
 +
* [[LuaWML:Location_set#location_set:of_wml_var|location_set:of_wml_var]]
 +
* [[LuaWML:Location_set#location_set:to_pairs|location_set:to_pairs]]
 +
* [[LuaWML:Location_set#location_set:to_stable_pairs|location_set:to_stable_pairs]]
 +
* [[LuaWML:Location_set#location_set:to_wml_var|location_set:to_wml_var]]
 +
* [[LuaWML:Location_set#location_set:union|location_set:union]]
 +
* [[LuaWML:Location_set#location_set:inter|location_set:inter]]
 +
* [[LuaWML:Location_set#location_set:iter|location_set:iter]]
 +
* [[LuaWML:Location_set#location_set:stable_iter|location_set:stable_iter]]
 +
* [[LuaWML:Location_set#location_set:filter|location_set:filter]]
 +
* [[LuaWML:Location_set#location_set:union_merge|location_set:union_merge]]
 +
* [[LuaWML:Location_set#location_set:inter_merge|location_set:inter_merge]]
  
Units are proxy tables with the following fields:
+
=== Miscellaneous ===
* '''x''', '''y''': integers (read only)
 
* '''side''': integer (read/write)
 
* '''id''', '''side_id''': strings (read only)
 
* '''name''': translatable string (read only)
 
* '''hitpoints''', '''max_hitpoints''', '''experience''', '''max_experience''', '''max_moves''': integers (read only)
 
* '''moves''': integer (read/write)
 
* '''resting''': boolean (read/write)
 
* '''petrified''', '''canrecruit''': booleans (read only)
 
* '''role''', '''facing''': strings (read/write)
 
* '''__cfg''': WML table (dump)
 
  
The metatable of these proxy tables appears as '''"unit"'''.
+
* [[LuaWML:Misc#wesnoth.game_config|wesnoth.game_config]]
 +
* [[LuaWML:Misc#wesnoth.get_era|wesnoth.get_era]]
 +
* [[LuaWML:Misc#wesnoth.current|wesnoth.current]]
 +
* [[LuaWML:Misc#wesnoth.synchronize_choice|wesnoth.synchronize_choice]]
 +
* [[LuaWML:Misc#wesnoth.synchronize_choices|wesnoth.synchronize_choices]] {{DevFeature1.13|?}}
 +
* [[LuaWML:Misc#wesnoth.unsynced|wesnoth.unsynced]] {{DevFeature1.13|?}}
 +
* [[LuaWML:Misc#wesnoth.get_image_size|wesnoth.get_image_size]]
 +
* [[LuaWML:Misc#wesnoth.compare_versions|wesnoth.compare_versions]]
 +
* [[LuaWML:Misc#wesnoth.have_file|wesnoth.have_file]]
 +
* [[LuaWML:Misc#wesnoth.debug|wesnoth.debug]]
 +
* [[LuaWML:Misc#wesnoth.get_time_stamp|wesnoth.get_time_stamp]]
 +
* [[LuaWML:Misc#wesnoth.random|wesnoth.random]]  {{DevFeature1.13|2}}
 +
* [[LuaWML:Misc#wesnoth.eval_formula|wesnoth.eval_formula]] {{DevFeature1.13|5}}
 +
* [[LuaWML:Misc#wesnoth.compile_formula|wesnoth.compile_formula]] {{DevFeature1.13|5}}
 +
* [[LuaWML:Misc#wesnoth.name_generator|wesnoth.name_generator]] {{DevFeature1.13|5}}
 +
* [[LuaWML:Misc#wesnoth.set_next_scenario|wesnoth.set_next_scenario]] {{DevFeature1.13|5}}
 +
* [[LuaWML:Misc#wesnoth.format|wesnoth.format]] {{DevFeature1.13|8}}
 +
* [[LuaWML:Misc#wesnoth.format_conjunct_list|wesnoth.format_conjunct_list]] {{DevFeature1.13|8}}
 +
* [[LuaWML:Misc#wesnoth.format_disjunct_list|wesnoth.format_disjunct_list]] {{DevFeature1.13|8}}
 +
* [[LuaWML:Misc#wesnoth.wesnoth.end_turn|wesnoth.end_turn]] {{DevFeature1.13|?}}
 +
* [[LuaWML:Misc#wml.tag|wml.tag]]
 +
* [[LuaWML:Misc#helper.modify_unit|helper.modify_unit]]
 +
* [[LuaWML:Misc#helper.move_unit_fake|helper.move_unit_fake]]
 +
* [[LuaWML:Misc#helper.rand|helper.rand]]
 +
* [[LuaWML:Misc#helper.round|helper.round]]
 +
* [[LuaWML:Misc#helper.shuffle|helper.shuffle]]
  
==== put_unit ====
+
=== Functions that should not be used ===
  
Places a unit described by a table on the map. Coordinates can be passed as the first two arguments, otherwise the table is expected two fields '''x''' and '''y''', where the unit will be placed. If the function is called with coordinates but no table, the unit on the map at given coordinates is removed instead.
+
If you take a look at Wesnoth's own lua files, you might find some undocumented functions use in the implementations of WML tags. Where possible, you should avoid using them, as they might be changed or removed with no compatibility for further releases. Instead, use the corresponding wml tags (in lua you can use <tt>wesnoth.wml_actions.tag_name(cfg)</tt>, though keep in mind that this won't substitute variables in the config).
  
-- create a unit with random traits, then erase it
+
If uncertain whether an undocumented feature is safe to use, ask on the forums, Discord, or IRC. It may turn out that someone simply forgot to add it to the wiki.
wesnoth.put_unit(17, 42, { type = "Elvish Lady" }
 
wesnoth.put_unit(17, 42)
 
  
-- give back all its move points to a unit
+
* wesnoth.redraw
local u = wesnoth.get_units({ x = 16, y = 41 })[1].__cfg
+
* wesnoth.set_menu_item
u.moves = u.max_moves
+
* wesnoth.clear_menu_item
wesnoth.put_unit(u)
+
* wesnoth.modify_ai
 
+
* wesnoth.print
==== get_side ====
+
* wesnoth.end_level
 
 
Returns the team with given number.
 
 
 
local team = wesnoth.get_side(1)
 
team.gold = team.gold + 50
 
 
 
Teams are proxy tables with the following fields:
 
* '''gold''', '''village_gold''', '''base_income''': integers (read/write)
 
* '''total_income''': integer (read only)
 
* '''objectives''', '''user_team_name''': translatable strings (read/write)
 
* '''objectives_changed''': boolean (read/write)
 
* '''team_name''': string (read/write)
 
* '''__cfg''': WML table (dump)
 
 
 
The metatable of these proxy tables appears as '''"side"'''.
 
 
 
==== get_unit_type_ids ====
 
 
 
Returns an array containing all the unit type IDs the engine knows about.
 
 
 
local unit_types = wesnoth.get_unit_type_ids()
 
wesnoth.message(string.format("%d unit types registered. First one is %s.", #unit_types, unit_types[1]))
 
 
 
==== get_unit_type ====
 
 
 
Returns the unit type with the corresponding ID.
 
 
 
local lich_cost = wesnoth.get_unit_type("Ancient Lich").cost
 
 
 
Unit types are proxy tables with the following fields:
 
* '''id''': string
 
* '''name''': translatable string (read only)
 
* '''max_moves''', '''max_experience''', '''max_hitpoints''', '''level''', '''cost''': integers (read only)
 
* '''__cfg''': WML table (dump)
 
 
 
The metatable of these proxy tables appears as '''"unit type"'''.
 
 
 
==== get_terrain ====
 
 
 
Returns the terrain code for the given location.
 
 
 
local is_grassland = wesnoth.get_terrain(12, 15) == "Gg"
 
 
 
==== set_terrain ====
 
 
 
Modifies the terrain at the given location.
 
 
 
function create_village(x, y)
 
    wesnoth.set_terrain(x, y, "Gg^Vh")
 
end
 
 
 
==== get_terrain_info ====
 
 
 
Returns the terrain details for the given terrain code.
 
 
 
local is_keep = wesnoth.get_terrain_info(wesnoth.get_terrain(12, 15)).keep
 
 
 
Terrain info is a plain table with the following fields:
 
* '''id''': string
 
* '''name''', '''description''': translatable strings
 
* '''castle''', '''keep''', '''village''': booleans
 
* '''healing''': integer
 
 
 
==== get_village_owner ====
 
 
 
Returns the side that owns the village at the given location.
 
 
 
local owned_by_side_1 = wesnoth.get_village_owner(12, 15) == 1
 
 
 
==== set_village_owner ====
 
 
 
Gives ownership of the village at the given location to the given side (or remove ownership if none).
 
 
 
wesnoth.set_village_owner(12, 15, 1)
 
 
 
==== get_map_size ====
 
 
 
Returns the width, the height, and the border size of the map.
 
 
 
local w,h,b = wesnoth.get_map_size()
 
 
 
==== game_config ====
 
 
 
Contrarily to the other values of the ''wesnoth'' table, ''game_config'' is simply a proxy table. Its fields offer an interface to the global settings of Wesnoth:
 
 
 
* '''version''': string (read only)
 
* '''base_income''': integer (read/write)
 
* '''village_income''': integer (read/write)
 
* '''poison_amount''': integer (read/write)
 
* '''rest_heal_amount''': integer (read/write)
 
* '''recall_cost''': integer (read/write)
 
* '''kill_experience''': integer (read/write)
 
 
 
-- Healing, poison, and regeneration, are a bit weak? Let's boost them!
 
wesnoth.game_config.poison_amount = 15
 
 
 
==== find_path ====
 
 
 
Returns the shortest path from one location to another. The source location is given either by coordinates as two arguments x and y, or by a unit as a single argument (as returned by [[#get_units|get_units]]). There must be a unit at the source location. The second location is given by coordinates. The last argument is an optional table that can be used to parametrize the pathfinder. Its options are:
 
* '''max_cost''': if set, the pathfinder will ignore paths longer than its value
 
* '''ignore_units''': if set, the path will go through units and ignore zones of control
 
* '''ignore_teleport''': if set, the teleport ability of the unit is ignored
 
* '''viewing_side''': if set to a valid side number, fog and shroud for this side will be taken into account; if set to an invalid number (e.g. 0), fog and shroud will be ignored; if left unset, the viewing side will be the unit side
 
 
 
The path is returned as a table of coordinate pairs. It contains both the source and destination tile if a path was found. The total cost of the path is also available as a second return value, if needed.
 
 
 
-- Display some items along the path from (x1,y1) to (x2,y2).
 
local u = wesnoth.get_units({ x = x1, y = y1 })[1]
 
local path, cost = wesnoth.find_path(u, x2, y2, { ignore_units = true, viewing_side = 0 })
 
if cost > u.moves then
 
    wesnoth.message("That's too far!")
 
else
 
    for i, loc in ipairs(path) do
 
        wesnoth.fire("item", { x = loc[1], y = loc[2], image = "items/buckler.png" })
 
    end
 
end
 
 
 
Instead of a parameter table, a cost function can be passed to the pathfinder. It will be called for all the tiles the computed path may possibly go through. It receives three arguments. The first two are the coordinates of the tile, the last one is the current cost for reaching that tile. The function should return a floating-point value that is the cost for entering the given tile. This cost should be greater or equal to one.
 
 
 
-- Count how many turns it would take, assuming the worst case (3 movement points per tile)
 
local max_moves = wesnoth.get_units({ x = x1, y = y1 })[1].max_moves
 
local path, cost = wesnoth.find_path(x1, y2, x2, y2,
 
    function(x, y, current_cost)
 
        local remaining_moves = max_moves - (current_cost % max_moves)
 
        if remaining_moves < 3 then current_cost = current_cost + remaining_moves end
 
        return current_cost + 3
 
    end)
 
wesnoth.message(string.format("It would take %d turns.", math.ceil(cost / 3)))
 
 
 
==== find_vacant_tile ====
 
 
 
Returns the two coordinates of an empty tile the closest to the tile passed by coordinates. An optional unit can be passed as a third argument; if so, the returned tile is reachable by the unit from the original tile.
 
 
 
function teleport(src_x, src_y, dst_x, dst_y)
 
  local u = wesnoth.get_units({x = src_x, y = src_y })[1]
 
  local ut = u.__cfg
 
  dst_x, dst_y = wesnoth.find_vacant_tile(dst_x, dst_y, u)
 
  wesnoth.put_unit(src_x, src_y)
 
  wesnoth.put_unit(dst_x, dst_y, ut)
 
end
 
 
 
==== get_selected_tile ====
 
 
 
Returns the two coordinates of the currently selected tile. This is mostly useful for defining command-mode helpers.
 
 
 
function chg_unit(attr, val)
 
    local x, y = wesnoth.get_selected_tile()
 
    if not x then wesnoth.message("Error", "No unit selected."); return end
 
    helper.modify_unit({ x = x, y = y }, { [attr] = val })
 
end
 
-- Function chg_unit can be used in command mode to modify unit attributes on the fly:
 
--  :lua chg_unit("status.poisoned", true)
 
  
 
== Encoding WML objects into Lua tables ==
 
== Encoding WML objects into Lua tables ==
  
Function [[#fire|wesnoth.fire]] expects a table representing a WML object as its second argument (if needed). Function [[#set_variable|wesnoth.set_variable]] allows to modify whole WML objects, again by passing it a table. Function [[#get_variable|wesnoth.get_variable]] transforms a WML object into a table, if its second argument is not set to ''true''. All these tables have the same format.
+
Function [[LuaWML:Events#wesnoth.fire|wesnoth.fire]] expects a table representing a WML object as its second argument (if needed). Function [[LuaWML:Variables#wesnoth.set_variable|wesnoth.set_variable]] allows to modify whole WML objects, again by passing it a table. Function [[LuaWML:Variables#wesnoth.get_variable|wesnoth.get_variable]] transforms a WML object into a table, if its second argument is not set to ''true''. All these tables have the same format.
  
 
Scalar fields are transformed into WML attributes. For instance, the following Lua table
 
Scalar fields are transformed into WML attributes. For instance, the following Lua table
  
{
+
<syntaxhighlight lang='lua'>
    a_bool = true,
+
{
    an_int = 42,
+
    a_bool = true,
    a_float = 1.25,
+
    an_int = 42,
    a_string = "scout",
+
    a_float = 1.25,
    a_translation = _ "Hello World!"
+
    a_string = "scout",
}
+
    a_translation = _ "Hello World!"
 +
}
 +
</syntaxhighlight>
  
 
is equivalent to the content of the following WML object
 
is equivalent to the content of the following WML object
  
[dummy]
+
<syntaxhighlight lang='wml'>
    a_bool = "yes"
+
[dummy]
    an_int = "42"
+
    a_bool = "yes"
    a_float = "1.25"
+
    an_int = 42
    a_string = "scout"
+
    a_float = 1.25
    a_translation = _ "Hello World!"
+
    a_string = "scout"
[/dummy]
+
    a_translation = _ "Hello World!"
 +
[/dummy]
 +
</syntaxhighlight>
  
WML child objects are not stored as Lua named fields, since several of them can have the same tag. Moreover, their tags can conflict with the attribute keys. So child objects are stored as pairs string + table in the unnamed fields in definition order. For instance, the following Lua table
+
WML child objects are not stored as Lua named fields, since several of them can have the same tag. Moreover, their tags can conflict with the attribute keys. So child objects are stored as pairs string + table in the unnamed fields in definition order. This means that for every subtag appearing in the wml code there is an additional table "layer" in the corresponding WML table of the form <code>{[1] = "tag_name", [2] = {}}</code> which is equivalent to <code>{"tag_name", {}}</code>. [1] etc are the unnamed fields (as opposed to wml attributes). The table under [2] in this subtable then holds the wml attributes from inside the wml subtag. So every subtag other than the toplevel tag corresponds to two nested tables each. For instance, the following Lua table
  
{
+
<syntaxhighlight lang='lua'>
    foo = 42,
+
{
    { "bar", { v = 1, w = 2 } },
+
    foo = 42,
    { "foo", { x = false } },
+
    { "bar", { v = 1, w = 2 } },
    { "bar", { y = "foo" } },
+
    { "foo", { x = false } },
    { "foobar", { { "barfoo", {} } } }
+
    { "bar", { y = "foo" } },
}
+
    { "foobar", { z = 5, { "barfoo", {} } } }
 +
}
 +
</syntaxhighlight>
  
 
is equivalent to the content of the following WML object
 
is equivalent to the content of the following WML object
  
[dummy]
+
<syntaxhighlight lang='wml'>
    foo = 42
+
[dummy]
    [bar]
+
    foo = 42
        v = 1
+
    [bar]
        w = 2
+
        v = 1
    [/bar]
+
        w = 2
    [foo]
+
    [/bar]
        x = no
+
    [foo]
    [/foo]
+
        x = no
    [bar]
+
    [/foo]
        y = foo
+
    [bar]
    [bar]
+
        y = foo
    [foobar]
+
    [bar]
        [barfoo]
+
    [foobar]
        [/barfoo]
+
        z = 5
    [/foobar]
+
        [barfoo]
[/dummy]
+
        [/barfoo]
 +
    [/foobar]
 +
[/dummy]
 +
</syntaxhighlight>
  
So assuming ''cfg'' contains the above WML object, the following accesses are possible:
+
Both tables above are also equivalent to this WML table, where all unnamed fields are displayed:
  
a_int = cfg.foo       -- "dummy.foo", 42
+
<syntaxhighlight lang='lua'>
a_string = cfg[3][2].y -- "dummy.bar[1].y", "foo"
+
{
a_table = cfg[4][2]   -- "dummy.foobar", { { "barfoo", {} } }
+
    foo = 42,
 +
    [1] = { [1] = "bar", [2] = { v = 1, w = 2 } },
 +
    [2] = { [1] = "foo", [2] = { x = false } },
 +
    [3] = { [1] = "bar", [2] = { y = "foo" } },
 +
    [4] = { [1] = "foobar", [2] = { z = 5, [1] = { [1] = "barfoo", [2] = {} } } }
 +
}
 +
</syntaxhighlight>
  
Consider using the [[#get_child|helper.get_child]] and [[#child_range|helper.child_range]] to ease the access to subtags.
+
So assuming ''cfg'' contains the above WML object, the following accesses are possible:
  
Functions registered by [[#register_wml_action|wesnoth.register_wml_action]] receive their data in a userdata object which has the exact same structure as above. It is read-only however. Accessing fields or children performs variable substitution on the fly. Its '''__parsed''' and '''__literal''' fields provide translations to plain tables (therefore writable). '''__literal''' returns the original text of the data (including dollar symbols), while '''__parsed''' performs a last global variable substitution.
+
<syntaxhighlight lang=lua>
 +
a_int = cfg.foo        -- "dummy.foo", 42
 +
a_string = cfg[3][2].y -- "dummy.bar[1].y", "foo"
 +
a_table = cfg[4][2]    -- "dummy.foobar", { z = 5, { "barfoo", {} } }
 +
</syntaxhighlight>
  
For instance, if you cannot stand any longer the fact that '''first_time_only''' is set to yes by default for the '''[event]''' tag, you can redefine it. But we have to be careful not to cause variable substitution, since the engine would perform a second variable substitution afterwards.
+
For creating valid wml table in lua it is usully easier to use ''T = helper.set_wml_tag_metatable {}'' asuming you did that you can create the above wml document with
 +
<syntaxhighlight lang=lua>
 +
{
 +
    foo = 42,
 +
    T.bar {
 +
        v = 1,
 +
        w = 1,
 +
    },
 +
    T.foo {
 +
        x = false,
 +
    },
 +
    T.bar {
 +
        y = "foo",
 +
    },
 +
    T.foobar {
 +
        z = 5,
 +
        T.barfoo {
 +
        },
 +
    },
 +
}
 +
</syntaxhighlight>
  
local old_event_handler
+
Consider using the [[LuaAPI/wml#wml.get_child|wml.get_child]] and [[LuaAPI/wml#wml.child_range|wml.child_range]] helper functions to ease the access to subtags.
old_event_handler = register_wml_action("event",
 
    function(cfg)
 
        -- Get the plain text from the user.
 
        local new_cfg = cfg.__literal
 
        -- The expression below is equivalent to cfg.__parsed.first_time_only,
 
        -- only faster. It is needed, since the first_time_only attribute may
 
        -- reference variables.
 
        local first = cfg.first_time_only
 
        -- Modify the default behavior of first_time_only.
 
        if first == nil then first = false end
 
        new_cfg.first_time_only = first
 
        -- Call the engine handler.
 
        old_event_handler(new_cfg)
 
    end
 
)
 
  
Note that, since the object is a userdata and not a table, '''pairs''' and '''ipairs''' are unfortunately not usable on it. So scripts have to work at a lower level. For instance, the following function returns the first sub-tag with a given name and it works both on WML tables and WML userdata:
+
{{DevFeature1.13|5}} As a convenience, attributes with array values (tables with only integer keys) are concatenated into a string when converting a Lua table into WML. For example, the following Lua code:
  
function get_child(cfg, name)
+
<syntaxhighlight lang=lua>
    for i = 1, #cfg do
+
{
        local v = cfg[i]
+
    x = {1, 2, 3, 4},
        if v[1] == name then return v[2] end
+
    y = {7, 8, 9, 10}
    end
+
}
end
+
</syntaxhighlight>
  
Another approach for handling userdata and tables in the same way, would be to convert the former into the latter beforehand:
+
produces the following WML table:
  
if getmetatable(cfg) == "wml object" then cfg = cfg.__parsed end
+
<syntaxhighlight lang=wml>
 +
[dummy]
 +
    x=1,2,3,4
 +
    y=7,8,9,10
 +
[/dummy]
 +
</syntaxhighlight>
  
== Helper functions ==
+
Functions registered in [[LuaWML:Events#wesnoth.wml_actions|wesnoth.wml_actions]] and other similar tables that provide hooks that the engine calls will receive their data either in lua tables encoding a WML object or as a [[LuaAPI/wml#wml.tovconfig|WML vconfig userdata]], which has the same structure but is read-only. Accessing fields or children on a vconfig performs variable substitution on the fly. Its '''__parsed''' and '''__literal''' fields provide translations to plain, writable tables. '''__literal''' returns the original text of the data (including dollar symbols in attributes and '''[insert_tag]''' children), while '''__parsed''' performs a full variable substitution.
  
The following functions are provided by the '''lua/helper.lua''' library. They are stored inside a table that is returned when loading the library with [[#require|wesnoth.require]].
+
For instance, if you cannot stand any longer the fact that '''first_time_only''' is set to yes by default for the '''[event]''' tag, you can redefine it. But we have to be careful not to cause variable substitution, since the engine would perform a second variable substitution afterwards.
 
 
helper = wesnoth.require "lua/helper.lua"
 
 
 
==== set_wml_action_metatable ====
 
 
 
Sets the metable of a table so that it can be used to fire WML actions. Returns the table. The fields of the table are then simple wrappers around a call to [[#fire|wesnoth.fire]].
 
 
 
W = helper.set_wml_action_metatable {}
 
W.message { speaker = "narrator", message = "?" }
 
 
 
==== set_wml_var_metatable ====
 
 
 
Sets the metable of a table so that it can be used to access WML variables. Returns the table. The fields of the tables are then proxies to the WML objects with the same names; reading/writing to them will directly access the WML variables.
 
 
 
helper.set_wml_var_metatable(_G)
 
my_persistent_variable = 42
 
 
 
==== set_wml_tag_metatable ====
 
 
 
Sets the metable of a table so that it can be used to create subtags with less brackets. Returns the table. The fields of the table are simple wrappers around table constructors.
 
 
 
T = helper.set_wml_tag_metatable {}
 
W.event { name = "new turn", T.message { speaker = "narrator", message = "?" } }
 
 
 
==== wml_error ====
 
 
 
Interrupts the current execution and displays a chat message that looks like a WML error.
 
 
 
local names = cfg.name or helper.wml_error("[clear_variable] missing required name= attribute.")
 
 
 
==== get_child ====
 
 
 
Returns the first sub-tag of a WML object with the given name.
 
  
local u = wesnoth.get_units({ id = "Delfador" })[1]
+
<syntaxhighlight lang=lua>
local costs = helper.get_child(u.__cfg, "movement_costs")
+
local old_event_handler = wesnoth.wml_actions.event
wesnoth.message(string.format("Delfador needs %d points to move through a forest.", costs.forest))
+
function wesnoth.wml_actions.event(cfg)
 +
    -- Get the plain text from the user.
 +
    local new_cfg = helper.literal(cfg)
 +
    -- The expression below is equivalent to cfg.__parsed.first_time_only,
 +
    -- only faster. It is needed, since the first_time_only attribute may
 +
    -- reference variables.
 +
    local first = cfg.first_time_only
 +
    -- Modify the default behavior of first_time_only.
 +
    if first == nil then first = false end
 +
    new_cfg.first_time_only = first
 +
    -- Call the engine handler.
 +
    old_event_handler(new_cfg)
 +
end
 +
</syntaxhighlight>
  
==== child_range ====
+
(Note: The above example will only affect nested events. Toplevel events will still default to ''first_time_only=yes''.)
 +
(Note2: You should not do that since it will break other addons that rely on the first_time_only=no default value.)
 +
<!-- This should probably be replaced with a better example. -->
  
Returns an iterator over all the sub-tags of a WML object with the given name.
+
'''pairs''' and '''ipairs''' also work on vconfig objects (contrary to the above statement). However, '''pairs''' works a little differently than on plain configs (tables) - it returns only string keys (attributes in WML terms) and not integer keys (tags in WML terms).
  
local u = wesnoth.get_units({ id = "Delfador" })[1]
+
Another approach for handling userdata and tables in the same way, would be to convert the former into the latter beforehand. The [[LuaAPI/wml#wml.parsed|wml.parsed]] and [[LuaAPI/wml#wml.literal|wml.literal]] helpers take care of this for you.
for att in helper.child_range(u.__cfg, "attack") do
 
    wesnoth.message(tostring(att.description))
 
end
 
  
==== all_teams ====
+
The vconfig userdata provides two other special fields: '''__shallow_parsed''' and '''__shallow_literal'''. They return a table corresponding to the WML userdata with variable substitution performed on the attributes (or not). [insert_tag] tags have also been parsed, so the number of children is faithful. But contrarily to '''__parsed''' and '''__literal''', the process is not recursive: all the children are still WML userdata and variable substitution can still happen for them. These shallow translators are meant as optimized versions of the deep ones, when only the toplevel attributes need to be writable.
  
Returns an iterator over teams that can be used in a for-in loop.
+
== Skeleton of a lua tag ==
  
for team in helper.all_teams() do team.gold = 200 end
+
The following [lua] tag is a skeleton for a prelude enabling Lua in your WML events. It creates a table ''H'' containing the functions from helper.lua . It sets up a table ''T'' so be used for easier creation of valid WML tables. It also sets up a table ''V'' so that any access to it is redirected to the persistent WML storage. Finally, it loads a textdomain to be accessed through the ''_'' variable.  
  
==== modify_unit ====
+
<syntaxhighlight lang='wml'>
 +
[scenario]
 +
    [lua]
 +
        code = <<
 +
            H = wesnoth.require "lua/helper.lua"
 +
            T = wml.tag
 +
            V = wml.variables
 +
            local _ = wesnoth.textdomain "my-campaign"
  
Modifies all the units satisfying the given filter (argument 1) with some WML attributes/objects (argument 2). This is a Lua implementation of the [http://www.wesnoth.org/macro-reference.xhtml MODIFY_UNIT] macro.
+
            -- Define your global constants here.
 +
            -- ...
  
helper.modify_unit({ id="Delfador" }, { moves=0 })
 
  
==== move_unit_fake ====
+
            -- Define your global functions here.
 +
            -- ...
 +
        >>
 +
    [/lua]
 +
    ...
 +
[/scenario]
 +
</syntaxhighlight>
  
Fakes the move of a unit satisfying the given filter (argument 1) to the given position (argument 2). This is a Lua implementation of the [http://www.wesnoth.org/macro-reference.xhtml MOVE_UNIT] macro.
+
It may be worth putting the whole Lua script above inside a separate file and having the [lua] tag load it:
  
helper.move_unit_fake({ id="Delfador" }, 14, 8)
+
<syntaxhighlight lang='wml'>
 +
[scenario]
 +
    [lua]
 +
        code = << wesnoth.dofile "~add-ons/Whatever/file.lua" >>
 +
    [/lua]
 +
    ...
 +
[/scenario]
 +
</syntaxhighlight>
  
==== get_variable_array ====
+
'''Note''': When a multiplayer scenario is hosted on the multiplayer server, separate Lua files are not transmitted over the network from the host to the peers. Therefore, it may happen that the Lua code from the separate files is executed on the host but not on the peers. This may result in out of-sync type errors. Only the Lua code that is embedded in the scenario WML, that is without wesnoth.dofile, will be transmitted and executed. To prevent these issues, the Lua files that are to be loaded with wesnoth.dofile must be distributed to the end-user explicitly.
  
Fetches all the WML container variables with given name and returns a table containing them (starting at index 1).
+
== Remarks on Random Numbers ==
  
function get_recall_list(side)
+
The math.random function is not safe for replays and multiplayer games, since the random values will be different each time and on all the clients. Instead, the Lua code should use the [[LuaWML:Misc#wesnoth.random|wesnoth.random]] function to synchronize random values. It has the same interface as math.random but is multiplayer-safe.
    wesnoth.fire("store_unit", { x = "recall", variable = "LUA_recall_list })
 
    local l = get_variable_array "LUA_recall_list"
 
    wesnoth.set_variable "LUA_recall_list"
 
    return l
 
end
 
  
==== get_variable_proxy_array ====
+
Also available is [[LuaWML:Misc#helper.rand|helper.rand()]], which takes the same argument in the same format as [[InternalActionsWML#.5Bset_variable.5D|[set_variable]]] rand=.
  
Creates proxies for all the WML container variables with given name and returns a table containing them (starting at index 1). This function is similar to [[#get_variable_array|helper.get_variable_array]], except that the proxies can be used for modifying WML containers.
+
<syntaxhighlight lang='lua'>
 +
local random_variable = helper.rand("1,2,3")
 +
</syntaxhighlight>
  
==== get_user_choice ====
+
== Random Lua table iteration ==
  
Displays a WML message box querying a choice from the user. Attributes and options are taken from given tables (see [[InterfaceActionsWML|[message]]]). The index of the selected option is returned.
+
Table iteration order ('''pairs''') is not strictly defined in Lua. If your code depends on the order of iteration, different clients may have different data in a multiplayer game. For example:
  
local result = helper.get_user_choice({ speaker = "narrator" }, { "Choice 1", "Choice 2" })
+
<syntaxhighlight lang='lua'>
 +
  local table = { ["Mage"] = true, ["Wose"] = true }
 +
  local concat = ""
 +
  local bad_usage = next(table) -- wrong, leads to OOS
 +
  for k, _ in pairs(table) do -- wrong, leads to OOS
 +
    concat = concat .. k
 +
  end
 +
</syntaxhighlight>
  
== Skeleton of a preload event ==
+
To avoid the problem, sort the table keys before iterating. Or alternatively, use Lua "arrays" and the '''ipairs''' function, which have a strictly defined order and never lead to OOS. Example of correct code:
  
The following event is a skeleton for a prelude enabling Lua in your WML events. It creates a table ''H'' containing the functions from helper.lua and a table ''W'' that serves as a proxy for firing WML actions. It also sets up the global environment so that any access to an undefined global variable is redirected to the persistent WML storage.
+
<syntaxhighlight lang='lua'>
 +
  local array = { "Mage", "Wose" }
 +
  local good_usage = table[1] -- correct
 +
  local concat = ""
 +
  for _, v in ipairs(array) do -- correct
 +
    concat = concat .. v
 +
  end
 +
</syntaxhighlight>
  
[event]
+
[[Category: Lua Reference|*]]
    name=preload
+
[[Category: WML Reference]]
    first_time_only=no
 
    [lua]
 
        code = <<
 
            H = wesnoth.require "lua/helper.lua"
 
            W = H.set_wml_action_metatable {}
 
            _ = wesnoth.textdomain "my-campaign"
 
 
            -- Define your global constants here.
 
            -- ...
 
 
            H.set_wml_var_metatable(_G)
 
 
            -- Define your global functions here.
 
            -- ...
 
        >>
 
    [/lua]
 
[/event]
 
 
 
It may be worth putting all the Lua code above inside a separate file and having the ''preload'' event only load it:
 
 
 
  [event]
 
    name=preload
 
    first_time_only=no
 
    [lua]
 
        code = << wesnoth.dofile "~add-ons/Whatever/file.lua" >>
 
    [/lua]
 
[/event]
 

Latest revision as of 00:09, 16 September 2020

[edit]WML Tags

A:

abilities, about, add_ai_behavior, advance, advanced_preference, advancefrom, advancement, advances, affect_adjacent, ai, allied_with, allow_end_turn, allow_extra_recruit, allow_recruit, allow_undo, and, animate, animate_unit, animation, aspect, attack, attack_anim, attacks, avoid;

B:

base_unit, berserk, binary_path, break, brush;

C:

campaign, cancel_action, candidate_action, capture_village, case, chance_to_hit, change_theme, chat, choose, clear_global_variable, clear_menu_item, clear_variable, color_adjust, color_range, command (action, replay), continue, criteria;

D:

damage, death, deaths, default, defend, defends, defense, delay, deprecated_message, destination, difficulty, disable, disallow_end_turn, disallow_extra_recruit, disallow_recruit, do, do_command, drains, draw_weapon_anim;

E:

editor_group, editor_music, editor_times, effect, else (action, animation), elseif, endlevel, end_turn (action, replay), enemy_of, engine, entry, era, event, extra_anim;

F:

facet, facing, fake_unit, false, feedback, female, filter (concept, event), filter_adjacent, filter_adjacent_location, filter_attack, filter_attacker, filter_base_value, filter_condition, filter_defender, filter_enemy, filter_location, filter_opponent, filter_own, filter_owner, filter_radius, filter_recall, filter_second, filter_second_attack, filter_self, filter_side, filter_vision, filter_weapon, filter_wml, find_path, fire_event, firststrike, floating_text, for, foreach, frame;

G:

game_config, get_global_variable, goal, gold, gold_carryover;

H:

harm_unit, has_ally, has_attack, has_unit, have_location, have_unit, heal_on_hit, heal_unit, healed_anim, healing_anim, heals, hide_help, hide_unit, hides;

I:

idle_anim, if (action, animation), illuminates, image, init_side, insert_tag, inspect, item, item_group;

J:

jamming_costs, join;

K:

kill, killed;

L:

label, language, leader, leader_goal, leadership, leading_anim, levelin_anim, levelout_anim, lift_fog, limit, literal, load_resource, locale, lock_view, lua;

M:

male, menu_item, message, micro_ai, missile_frame, modification, modifications, modify_ai, modify_side, modify_turns, modify_unit, modify_unit_type, move, move_unit, move_unit_fake, move_units_fake, movement_anim, movement costs, movetype, multiplayer, multiplayer_side, music;

N:

not, note;

O:

object, objective, objectives, on_undo, open_help, option, options, or;

P:

part, petrifies, petrify, place_shroud, plague, poison, portrait, post_movement_anim, pre_movement_anim, primary_attack, primary_unit, print, put_to_recall_list;

R:

race, random_placement, recall (action, replay), recalls, recruit, recruit_anim, recruiting_anim, recruits, redraw, regenerate, remove_event, remove_item, remove_object, remove_shroud, remove_sound_source, remove_time_area, remove_unit_overlay, repeat, replace_map, replace_schedule, replay, replay_start, reset_fog, resistance (ability, unit), resistance_defaults, resource, return, role, rule;

S:

save, scenario, scroll, scroll_to, scroll_to_unit, secondary_attack, secondary_unit, section, select_unit, sequence, set_extra_recruit, set_global_variable, set_menu_item, set_recruit, set_specials, set_variable, set_variables, sheath_weapon_anim, show_if (message, set_menu_item), show_objectives, side, skirmisher, slow, snapshot, sound, sound_source, source (replay, teleport), special_note, specials, split, stage, standing_anim, statistics, status, store_gold, store_items, store_locations, store_map_dimensions, store_reachable_locations, store_relative_direction, store_side, store_starting_location, store_time_of_day, store_turns, store_unit, store_unit_defense, store_unit_type, store_unit_type_ids, store_villages, story, swarm, switch, sync_variable;

T:

target, team, teleport (ability, action), teleport_anim, terrain, terrain_defaults, terrain_graphics, terrain_mask, terrain_type, test, test_condition, text_input, textdomain, theme, then, tile, time, time_area, topic, toplevel, trait, transform_unit, traveler, true, tunnel, tutorial;

U:

unhide_unit, unit, unit_overlay, unit_type, unit_worth, units, unlock_view, unpetrify, unstore_unit, unsynced;

V:

value, variable, variables, variant, variation, victory_anim, village, vision_costs, volume;

W:

while, wml_message, wml_schema;

Z:

zoom;

The [lua] tag

This tag is a part of ActionWML, thus can be used inside [event] and at other places where ActionWML can be used. It makes it possible to write actions with the Lua 5.3 language.

It is also possible to put this tag inside a [scenario] ScenarioWML, those tags will be executed immediately when the lua engine loads which is even before the scenario preload event is fired.

[lua] is now also allowed in [era], [modification] and [resource], those [lua] tags are then copied into the [scenario]/[multiplayer] when it is played just like [event]s inside [era] or [modification]

The tag supports only the code key, which is a string containing the Lua scripts. Since Lua makes usage of the quotes and the { and } symbols, it is certainly wise to enclose the script between stronger quotes, as they prevent the preprocessor from performing macro expansion and tokenization.

 [lua]
     code = << wesnoth.message "Hello World!" >>
 [/lua]

The Lua kernel can also be accessed from the command mode:

:lua local u = wesnoth.get_units({ id = "Konrad" })[1]; u.moves = 5

The [args] sub-tag can be used to pass a WML object to the script via its variadic local variable "...".

 [lua]
     code = << local t = ...; wesnoth.message(tostring(t.text)) >>
     [args]
         text = _ "Hello world!"
     [/args]
 [/lua]

Global environment

All the Lua scripts of a scenario share the same global environment (aka Lua state). Unlike other parts of the configurable gamestate the Lua state is not stored in savefiles, thus [lua] tags in [scenario] are executed not only before the scenario starts but also each time the game is loaded. Funtions defined in [lua] tags in [scenario] can be used in all [lua] tags in [event]s.

 [scenario]
     [lua]
         code = <<
             function narrator(t)
                 -- Behave like the [message] tag.
                 wesnoth.fire("message",
                   { speaker = "narrator", message = t.sentence })
             end
         >>
     [/lua]
     [event]
         name = turn 1
         [lua]
             code = << narrator(...) >>
             [args]
                 sentence = _ "Hello world!"
             [/args]
         [/lua]
         [lua]
             code = << narrator(...) >>
             [args]
                 sentence = _ "How are you today?"
             [/args]
         [/lua]
     [/event]
     ...
 [/scenario]

In the example above, the redundant structure could be hidden behind macros. But it may be better to simply define a new WML tag.

 [scenario]
     [lua]
         code = <<
             -- The function is now placed in the wesnoth.wml_actions table
             -- The tag is [narrator], same as the function name
             function wesnoth.wml_actions.narrator(t)
                 -- Behave like the [message] tag.
                 wesnoth.fire("message",
                   { speaker = "narrator", message = t.sentence })
             end
         >>
     [/lua]
 
     [event]
         name = turn 1
         [narrator]
             sentence = _ "Hello world!"
         [/narrator]
         [narrator]
             sentence = _ "How are you today?"
         [/narrator]
     [/event]
 [/scenario]

The global environment is not preserved over save/load cycles. Therefore, storing values in the global environment is generally a bad idea. The only time assigning global variables (including function definitions) makes sense is in a [lua] block directly in [scenario] or during a preload event, as this event is always run. Therefore, helper functions defined at that time will be available to all the later scripts.

The global environment initially contains the following modules: basic (no name), string, table, and math. A wesnoth module is also available, it provides access to the C++ engine. Additionally, the functions clock, date, time and difftime from the os library (keep in mind that they aren't multiplayer- and replay-safe), as well as traceback from the debug library are also available.

At the start of a script, the variadic local variable ... (three dots) is a proxy table representing WML data. This table is the content of the [args] sub-tag of the [lua] tag, if any.

Examples

The following WML event is taken from Wesnoth' tutorial. It will serve as an example to present how Lua scripts are embedded into Wesnoth. The event is fired whenever a unit from side 1 (that is, the hero controlled by the user) moves to a tile that is not the one set in the WML variable target_hex.

 # General catch for them moving to the wrong place.
 [event]
     name=moveto
     first_time_only=no
     [allow_undo][/allow_undo]
     [filter]
         side=1
     [/filter]
 
     [if]
         [variable]
             name=target_hex.is_set
             equals=yes
         [/variable]
         [then]
             [if]
                 [variable]
                     name=x1
                     equals=$target_hex.x
                 [/variable]
                 [variable]
                     name=y1
                     equals=$target_hex.y
                 [/variable]
                 [then]
                 [/then]
                 [else]
                     [redraw][/redraw]
                     [message]
                         speaker=narrator
                         message=_ "*Oops!
 You moved to the wrong place! After this message, you can press 'u' to undo, then try again." +
                         _ "
 *Left click or press spacebar to continue..."
                     [/message]
                 [/else]
             [/if]
         [/then]
     [/if]
 [/event]

A Lua script that performs the same action is presented below.

 [event]
     name=moveto
     first_time_only=no
     [allow_undo][/allow_undo]
     [filter]
         side=1
     [/filter]
 
     [lua]
         code = <<

             local event_data = wesnoth.current.event_context
             if wml.variables["target_hex.is_set"] and
                (event_data.x1 ~= wml.variables["target_hex.x"] or event_data.y1 ~= wml.variables["target_hex.y"])
             then
                 W.redraw()
                 narrator_says(_ "*Oops!\nYou moved to the wrong place! After this message, you can press 'u' to undo, then try again.")
             end
         >>
     [/lua]
 [/event]

Here is a more detailed explanation of the Lua code. Its first line

local event_data = wesnoth.current.event_context

puts the event data into the event_data local variable. Since it is a moveto event, the event_data table contains the destination of the unit in the x1 and y1 fields.

The next two lines then test

if wml.variables["target_hex.is_set"] and
    (event_data.x1 ~= wml.variables["target_hex.x"] or event_data.y1 ~= wml.variables["target_hex.y"])

whether the variable wml.variables["target_hex"] matches the event parameters. Since wml.variables is not a local variable, it is taken from the global environment. Usually, variables from the global environment are not persistent but the wesnoth engine maps the variable wml.variables to the storage of WML variables.

Without using wml.variables, the conditional would have been written

if wesnoth.get_variable("target_hex.is_set") and
    (event_data.x1 ~= wesnoth.get_variable("target_hex.x") or event_data.y1 ~= wesnoth.get_variable("target_hex.y")

The body of the conditional then performs the [redraw] action.

W.redraw()

This short syntax is made possible by a line of the prelude that makes W a proxy for performing WML actions.

 [scenario]
     [lua]
         code = <<
             H = wesnoth.require "lua/helper.lua"
             W = H.set_wml_action_metatable {}
         >>
     [/lua]
     ...
 [/scenario]

Without this shortcut, the first statement would have been written

wesnoth.fire("redraw")

or

wesnoth.wml_actions.redraw {}

Finally the script displays a message by

narrator_says(_ "*Oops!\nYou moved to the wrong place! After this message, you can press 'u' to undo, then try again.")

The narrator_says function is defined in the prelude too, since the construct behind it occurs several times in the tutorial. In plain WML, macros would have been used instead. The definition of the function is

function narrator_says(m)
    W.message { speaker="narrator",
                message = m .. _ "\n*Left click or press spacebar to continue..." }
end

The function fires a [message] action and passes a WML object containing the usual two fields to it. The second field is initialized by concatenating the function argument with another string. Both strings are prefixed by the _ symbol to mark them as translatable. (Note that _ is just a unary function, not a keyword.) Again, this is made possible by a specific line of the prelude:

_ = wesnoth.textdomain "wesnoth-tutorial"

A longer translation of the tutorial is available at [1].

Interface to the engine and helper functions



Functionalities of the game engine are available through the functions contained in the wesnoth global table. Some of these functions return proxy tables. Writes to fields marked "read-only" are ignored. The __cfg fields return plain tables; in particular, writes do not modify the original object, and reads return the values from the time the dump was performed.

Some helper functions are provided by the lua/helper.lua library. They are stored inside a table that is returned when loading the library with wesnoth.require.

helper = wesnoth.require "lua/helper.lua"

WML variables

Events and WML actions

User interface

Map and terrains

Time of day schedule

Units

Sides

Pathfinder

Lua files

Location sets

Miscellaneous

Functions that should not be used

If you take a look at Wesnoth's own lua files, you might find some undocumented functions use in the implementations of WML tags. Where possible, you should avoid using them, as they might be changed or removed with no compatibility for further releases. Instead, use the corresponding wml tags (in lua you can use wesnoth.wml_actions.tag_name(cfg), though keep in mind that this won't substitute variables in the config).

If uncertain whether an undocumented feature is safe to use, ask on the forums, Discord, or IRC. It may turn out that someone simply forgot to add it to the wiki.

  • wesnoth.redraw
  • wesnoth.set_menu_item
  • wesnoth.clear_menu_item
  • wesnoth.modify_ai
  • wesnoth.print
  • wesnoth.end_level

Encoding WML objects into Lua tables

Function wesnoth.fire expects a table representing a WML object as its second argument (if needed). Function wesnoth.set_variable allows to modify whole WML objects, again by passing it a table. Function wesnoth.get_variable transforms a WML object into a table, if its second argument is not set to true. All these tables have the same format.

Scalar fields are transformed into WML attributes. For instance, the following Lua table

{
    a_bool = true,
    an_int = 42,
    a_float = 1.25,
    a_string = "scout",
    a_translation = _ "Hello World!"
}

is equivalent to the content of the following WML object

[dummy]
    a_bool = "yes"
    an_int = 42
    a_float = 1.25
    a_string = "scout"
    a_translation = _ "Hello World!"
[/dummy]

WML child objects are not stored as Lua named fields, since several of them can have the same tag. Moreover, their tags can conflict with the attribute keys. So child objects are stored as pairs string + table in the unnamed fields in definition order. This means that for every subtag appearing in the wml code there is an additional table "layer" in the corresponding WML table of the form {[1] = "tag_name", [2] = {}} which is equivalent to {"tag_name", {}}. [1] etc are the unnamed fields (as opposed to wml attributes). The table under [2] in this subtable then holds the wml attributes from inside the wml subtag. So every subtag other than the toplevel tag corresponds to two nested tables each. For instance, the following Lua table

{
    foo = 42,
    { "bar", { v = 1, w = 2 } },
    { "foo", { x = false } },
    { "bar", { y = "foo" } },
    { "foobar", { z = 5, { "barfoo", {} } } }
}

is equivalent to the content of the following WML object

[dummy]
    foo = 42
    [bar]
        v = 1
        w = 2
    [/bar]
    [foo]
        x = no
    [/foo]
    [bar]
        y = foo
    [bar]
    [foobar]
        z = 5
        [barfoo]
        [/barfoo]
    [/foobar]
[/dummy]

Both tables above are also equivalent to this WML table, where all unnamed fields are displayed:

{
    foo = 42,
    [1] = { [1] = "bar", [2] = { v = 1, w = 2 } },
    [2] = { [1] = "foo", [2] = { x = false } },
    [3] = { [1] = "bar", [2] = { y = "foo" } },
    [4] = { [1] = "foobar", [2] = { z = 5, [1] = { [1] = "barfoo", [2] = {} } } }
}

So assuming cfg contains the above WML object, the following accesses are possible:

a_int = cfg.foo        -- "dummy.foo", 42
a_string = cfg[3][2].y -- "dummy.bar[1].y", "foo"
a_table = cfg[4][2]    -- "dummy.foobar", { z = 5, { "barfoo", {} } }

For creating valid wml table in lua it is usully easier to use T = helper.set_wml_tag_metatable {} asuming you did that you can create the above wml document with

{
    foo = 42,
    T.bar {
         v = 1,
         w = 1,
    },
    T.foo {
        x = false,
    },
    T.bar {
         y = "foo",
    },
    T.foobar {
         z = 5,
         T.barfoo {
         },
    },
}

Consider using the wml.get_child and wml.child_range helper functions to ease the access to subtags.

(Version 1.13.5 and later only) As a convenience, attributes with array values (tables with only integer keys) are concatenated into a string when converting a Lua table into WML. For example, the following Lua code:

{
    x = {1, 2, 3, 4},
    y = {7, 8, 9, 10}
}

produces the following WML table:

[dummy]
    x=1,2,3,4
    y=7,8,9,10
[/dummy]

Functions registered in wesnoth.wml_actions and other similar tables that provide hooks that the engine calls will receive their data either in lua tables encoding a WML object or as a WML vconfig userdata, which has the same structure but is read-only. Accessing fields or children on a vconfig performs variable substitution on the fly. Its __parsed and __literal fields provide translations to plain, writable tables. __literal returns the original text of the data (including dollar symbols in attributes and [insert_tag] children), while __parsed performs a full variable substitution.

For instance, if you cannot stand any longer the fact that first_time_only is set to yes by default for the [event] tag, you can redefine it. But we have to be careful not to cause variable substitution, since the engine would perform a second variable substitution afterwards.

local old_event_handler = wesnoth.wml_actions.event
function wesnoth.wml_actions.event(cfg)
    -- Get the plain text from the user.
    local new_cfg = helper.literal(cfg)
    -- The expression below is equivalent to cfg.__parsed.first_time_only,
    -- only faster. It is needed, since the first_time_only attribute may
    -- reference variables.
    local first = cfg.first_time_only
    -- Modify the default behavior of first_time_only.
    if first == nil then first = false end
    new_cfg.first_time_only = first
    -- Call the engine handler.
    old_event_handler(new_cfg)
end

(Note: The above example will only affect nested events. Toplevel events will still default to first_time_only=yes.) (Note2: You should not do that since it will break other addons that rely on the first_time_only=no default value.)

pairs and ipairs also work on vconfig objects (contrary to the above statement). However, pairs works a little differently than on plain configs (tables) - it returns only string keys (attributes in WML terms) and not integer keys (tags in WML terms).

Another approach for handling userdata and tables in the same way, would be to convert the former into the latter beforehand. The wml.parsed and wml.literal helpers take care of this for you.

The vconfig userdata provides two other special fields: __shallow_parsed and __shallow_literal. They return a table corresponding to the WML userdata with variable substitution performed on the attributes (or not). [insert_tag] tags have also been parsed, so the number of children is faithful. But contrarily to __parsed and __literal, the process is not recursive: all the children are still WML userdata and variable substitution can still happen for them. These shallow translators are meant as optimized versions of the deep ones, when only the toplevel attributes need to be writable.

Skeleton of a lua tag

The following [lua] tag is a skeleton for a prelude enabling Lua in your WML events. It creates a table H containing the functions from helper.lua . It sets up a table T so be used for easier creation of valid WML tables. It also sets up a table V so that any access to it is redirected to the persistent WML storage. Finally, it loads a textdomain to be accessed through the _ variable.

[scenario]
    [lua]
        code = <<
            H = wesnoth.require "lua/helper.lua"
            T = wml.tag
            V = wml.variables
            local _ = wesnoth.textdomain "my-campaign"

            -- Define your global constants here.
            -- ...


            -- Define your global functions here.
            -- ...
        >>
    [/lua]
    ... 
[/scenario]

It may be worth putting the whole Lua script above inside a separate file and having the [lua] tag load it:

[scenario]
    [lua]
        code = << wesnoth.dofile "~add-ons/Whatever/file.lua" >>
    [/lua]
    ...
[/scenario]

Note: When a multiplayer scenario is hosted on the multiplayer server, separate Lua files are not transmitted over the network from the host to the peers. Therefore, it may happen that the Lua code from the separate files is executed on the host but not on the peers. This may result in out of-sync type errors. Only the Lua code that is embedded in the scenario WML, that is without wesnoth.dofile, will be transmitted and executed. To prevent these issues, the Lua files that are to be loaded with wesnoth.dofile must be distributed to the end-user explicitly.

Remarks on Random Numbers

The math.random function is not safe for replays and multiplayer games, since the random values will be different each time and on all the clients. Instead, the Lua code should use the wesnoth.random function to synchronize random values. It has the same interface as math.random but is multiplayer-safe.

Also available is helper.rand(), which takes the same argument in the same format as [set_variable] rand=.

 local random_variable = helper.rand("1,2,3")

Random Lua table iteration

Table iteration order (pairs) is not strictly defined in Lua. If your code depends on the order of iteration, different clients may have different data in a multiplayer game. For example:

  local table = { ["Mage"] = true, ["Wose"] = true }
  local concat = ""
  local bad_usage = next(table) -- wrong, leads to OOS
  for k, _ in pairs(table) do -- wrong, leads to OOS
    concat = concat .. k
  end

To avoid the problem, sort the table keys before iterating. Or alternatively, use Lua "arrays" and the ipairs function, which have a strictly defined order and never lead to OOS. Example of correct code:

  local array = { "Mage", "Wose" }
  local good_usage = table[1] -- correct
  local concat = ""
  for _, v in ipairs(array) do -- correct
    concat = concat .. v
  end
This page was last edited on 16 September 2020, at 00:09.