From The Battle for Wesnoth Wiki
< LuaWML(Redirected from LuaWML:Display)

This page describes the LuaWML functions and helpers for interfacing with the user.


  • wesnoth.message([speaker,] 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 "<Big Brother> I'm watching you."

See also helper.wml_error for displaying error messages.


  • wesnoth.clear_messages()

Removes all messages from the chat window. No argument or returned values.


  • wesnoth.textdomain(domain)

Creates a function proxy for lazily translating strings from the given domain.

-- #textdomain "my-campaign"
-- the comment above ensures the subsequent strings will be extracted to the proper 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) appears as "translatable string".

The translatable strings can be appended to other strings/numbers with the standard .. operator. Translation can be forced with the standard tostring operator in order to get a plain string.

wesnoth.message(string.format(tostring(_ "You gain %d gold."), amount))

For details on the translation system, see GettextForWesnothDevelopers.


  • wesnoth.delay(milliseconds)

Delays the engine like the [delay] tag. one argument: time to delay in milliseconds



  • wesnoth.float_label(x, y, text)

Pops some text above a map tile.

wesnoth.float_label(unit.x, unit.y, "<span color='#ff0000'>Ouch</span>")


  • wesnoth.get_viewing_side()

returns two values 1) the number of the side of the current viewpoint, 2) a boolean specifying whether it has full vision (can only only true happen in replays or for observers)


Selects the given unit in the game map as if the player had clicked on it. Argument 3: boolean, whether to show the movement range of any unit on that location (def: true) Argument 4: boolean, whether to fire any select events (def: false). Note: currently (1.14.1), this argument has no effect (events are never fired).

wesnoth.select_unit(14,6, true, true)

The wesnoth.select_hex form is deprecated. If there is a unit on the location, it does the same thing as wesnoth.select_unit, but if show_movement is true it also calls wesnoth.highlight_hex for the location. If there is no unit on the location, wesnoth.select_hex does not do anything useful.

If called without arguments, wesnoth.select_unit deselects the current unit from the map as long as the mouse cursor is not on its hex. It will continue to be displayed on the UI sidebar in any case.


  • wesnoth.highlight_hex(x, y)

Draws an outline around the specified hex.


  • wesnoth.deselect_hex()

(Version 1.13.2 and later only)

Reverses any select_hex call, leaving all locations unhighlighted. Takes no arguments.


  • wesnoth.scroll_to_tile(x, y, [only_if_visible, [instant]])
  • (Version 1.13.7 and later only) wesnoth.scroll_to_tile(x, y, [only_if_visible, [instant, [only_if_needed]]])

Scrolls the map to the given location. If true is passed as the third parameter, scrolling is disabled if the tile is hidden under the fog. If true is passed as the fourth parameter, the view instantly warps to the location regardless of the scroll speed setting in Preferences. If true is passed as the fifth parameter, no scrolling occurs if the target location is already visible onscreen.

local u = wesnoth.get_units({ id = "hero" })[1]
wesnoth.scroll_to_tile(u.x, u.y)


  • wesnoth.lock_view(lock)

Locks or unlocks gamemap view scrolling for human players. If true is passed as the first parameter, the view is locked; pass false to unlock.

Human players cannot scroll the gamemap view as long as it is locked, but Lua or WML actions such as wesnoth.scroll_to_tile still can; the locked/unlocked state is preserved when saving the current game. This feature is generally intended to be used in cutscenes to prevent the player scrolling away from scripted actions.

wesnoth.scroll_to_tile(12, 14, false, true)


  • wesnoth.view_locked()

Returns a boolean indicating whether gamemap view scrolling is currently locked.


  • wesnoth.play_sound(sound, [repeat_count])

Plays the given sound file once, optionally repeating it one or more more times if an integer value is provided as a second argument (note that the sound is repeated the number of times specified in the second argument, i.e. a second argument of 4 will cause the sound to be played once and then repeated four more times for a total of 5 plays. See the example below).

wesnoth.play_sound "ambient/birds1.ogg"
wesnoth.play_sound("magic-holy-miss-3.ogg", 4) -- played 1 + 4 = 5 times


  • wesnoth.set_music(music_entry)

Sets the given table as an entry into the music list. See MusicListWML for the recognized attributes.

wesnoth.set_music { name = "traveling_minstrels.ogg" }

Passing no argument forces the engine to take into account all the recent changes to the music list. (Note: this is done automatically when sequences of WML commands end, so it is useful only for long events.)

(Version 1.13.8 and later only) This function is now deprecated. Use the wesnoth.music_list table instead


(Version 1.13.8 and later only)

This is a table giving access to the current music playlist. It can be accessed as a normal array, including the Lua length operator. If you assign a music config to an entry, the track is replaced. It is not a normal array however and cannot be manipulated with the table library.

In addition, it has the following named fields:

  • wesnoth.music_list.current (read-write): The currently-playing track. This may sometimes be a track that's not on the playlist - "play once" tracks are not placed on the playlist.
  • wesnoth.music_list.current.__cfg (read-only): Returns a copy of the current track information.
  • wesnoth.music_list.previous (read-write): The track played before the current one. This may sometimes be a track that's not on the playlist - "play once" tracks are not placed on the playlist.
  • wesnoth.music_list.previous.__cfg (read-only): Returns a copy of the previous track information. NOTE: Wesnoth's playlist implementation effectively "plays" every song as it's added to the playlist, so when replacing one playlist with another, this will return information on the second-to-last track added to the new playlist, not the track you actually heard playing from the playlist that was replaced.
  • wesnoth.music_list.current_i (read-write): The index of the currently-playing track on the playlist, or nil if the currently-playing track is not on the playlist.
  • wesnoth.music_list.volume (read-write): The current music volume, as a percentage of the user's preferred volume set in preferences.
  • wesnoth.music_list.all (read-only): Returns a copy of the music list as an array of WML tables.

It also contains some functions:

  • wesnoth.music_list.add(track_name, [immediate,] [ms_before, [ms_after]]): Appends a track to the playlist. If true is passed, also start playing the new track.
  • wesnoth.music_list.remove(n1, ...): Removes one or more tracks by their index. You can pass as many indices as you wish. If one of the removed tracks is currently playing, it continues to play.
  • wesnoth.music_list.clear(): Clears the playlist. The currently-playing track continues to play.
  • Stop playing the current track and move on to the next one. This honours the shuffle settings.
  • Start playing a track without appending it to the playlist.

Each track contains the following fields:

  • shuffle (read-write)
  • once (read-write): generally only true for wesnoth.music_list.current
  • ms_before (read-write)
  • ms_after (read-write)
  • immediate (read-only)
  • name (read-only): the unresolved track filename
  • title (read-only): a user-friendly track title


(Version 1.13.8 and later only)

  • wesnoth.sound_volume(new_volume)

Sets the current sound volume, as a percentage of the user's preferred volume set in preferences. Returns the previous sound volume in the same format.


  • wesnoth.show_menu(items [, initial] [, markup])

Shows a popup menu onscreen at the current mouse location. This could be used for example to produce a sort of submenu in a [set_menu_item]. The items are specified as a Lua array of tables, each of which supports the following keys:

  • icon: An icon to display in the leftmost column of the menu.
  • image: An image to display in the main column of the menu. If this is present, label is ignored.
  • label: A label to display in the main column of the menu.
  • details: A secondary label to display in the right column of the menu.
  • tooltip: Text to display when mousing over this option.

The initial argument must be a valid index into the items array, or 0 to indicate that no element is initially selected (which is the default but typically not what you want).

The markup argument specifies whether Pango markup will be parsed in the menuitems. It defaults to false.

The initial and markup arguments can be passed in either order; the game will understand which is meant based on the type of the argument.


  • wesnoth.show_message_box(title, message [, button] [, markup])
  • wesnoth.confirm([title,] message)
  • wesnoth.alert([title], message)

Shows a standard message box onscreen (similar to the quit confirmation message, for example). The button can be any arbitrary potentially-translatable string (in which case, there will be one button with that label), or it can be one of the following special values:

  • ok or nil: A single OK button; this is the default
  • cancel: A single Cancel button
  • close: A single Close button
  • ok_cancel: Two buttons labelled OK and Cancel
  • yes_no: Two buttons labelled Yes and No
  • an empty string: No buttons; the dialog automatically closes after a few seconds

The alert and confirm functions are simpler wrappers for this function, using a single OK button and a pair of Yes/No buttons, respectively. Both confirm and show_message_box return false if No or Cancel was clicked, and true otherwise.


  • wesnoth.show_message_dialog(attributes, [options, [text_input_attributes]])

(Version 1.13.2 and later only)

Shows a message dialog, of the type used by the [message] ActionWML tag. Unlike the [message] tag, this is unsynced; if you need it synced, you must do it yourself. The first argument is a table describing the dialog with the following keys:

  • title - The title to show on the message. For example, the speaker's name.
  • message - The message content.
  • portrait - An image to show along with the message. By default, no image is shown.
  • left_side - The default is true; set to false to show the image on the right.
  • mirror - If true, the image will be flipped horizontally.

The second argument is a list of options as a Lua array. Each option is either a (possibly-translatable) string or a config with DescriptionWML keys. The array itself can also have an optional default key which if present should be the index of the initially selected option (useful if you don't need full DescriptionWML but want to set a default). If present it overrides any defaults set in individual options.

The third argument is a table describing the text input field with the following keys:

  • label - A label to show to the left of the text field.
  • text - Initial contents of the text field.
  • max_length - Maximum input length in characters (defaults to 256).

You need at least one key for the text input to be shown. Both the second arguments are option, but if you want text input with no options, you must pass nil for the second parameter.

This function returns two values. The first is the numeric result of the dialog. If there are no options and no text input, this is -2 if the user closed by pressing Escape, otherwise it's -1. If there are options, this is the index of the option chosen (starting from 1). If there is text input but no options, the first return value is 0. If there was text input, the second value contains the text entered.


     title = "Make your choice:",
     message = "Select an option and enter some text.",
     portrait = "wesnoth-icon.png",
 }, {
     "The first choice is always the best!",
     "Pick me! Second choices are better!",
     "You know you want the third option!",
 }, {
     label = "Text:",
     text = "?",
     max_length = 16

(You don't have to format it like that, of course.)


  • wesnoth.show_popup_dialog(title, message, [image])

(Version 1.13.2 and later only)

Shows a simple popup dialog in the centre of the screen. Takes three arguments, which in order are:

  1. A title string for the dialog
  2. The message content for the dialog.
  3. An image to show.

Both the title and the message support Pango markup. The image is optional.


  • wesnoth.show_story(story_config, default_title)

(Version 1.13.8 and later only)

Shows the storyscreen. The passed config is identical to the contents of [story]. The second parameter is the default title used if a part does not specify one — unlike intro storyscreens, a Lua-invoked one does not default to the scenario name.


  • wesnoth.show_dialog(wml_dialog_table, [pre_show_function, [post_show_function]])

Displays a dialog box described by a WML table and returns:

  • if the dialog was dismissed by a button click, the integer value associated to the button via the "return_value" keyword.
  • if the dialog was closed with the enter key, -1.
  • if the dialog was closed with the escape key, -2.

The dialog box is equivalent to the resolution section of a GUI window as described in GUIToolkit and must therefore contain at least the following children: [tooltip], [helptip], and [grid]. The [grid] must contain nested [row], [column] and [grid] tags which describe the layout of the window. (More information can be found in Layout; suffice to say that the basic structure is grid -> row -> column -> widget, where the widget is considered to be in a cell defined by the row and column of the grid. A list of widgets can be found at Widgets.)

Two optional functions can be passed as second and third arguments; the first one is called once the dialog is created and before it is shown; the second one is called once the dialog is closed. These functions are helpful in setting the initial values of the fields and in recovering the final user values. These functions can call the #wesnoth.set_dialog_value, #wesnoth.get_dialog_value, and #wesnoth.set_dialog_callback functions for this purpose.

This function should be called in conjunction with #wesnoth.synchronize_choice, in order to ensure that only one client displays the dialog and that the other ones recover the same input values from this single client. Even in single-player campaigns, synchronization is necessary otherwise the replay will be broken.

The example below defines a dialog with a list and two buttons on the left, and a big image on the right. The preshow function fills the list and defines a callback on it. This select callback changes the displayed image whenever a new list item is selected. The postshow function recovers the selected item before the dialog is destroyed.

local helper = wesnoth.require "lua/helper.lua"
local T = wml.tag
local _ = wesnoth.textdomain "wesnoth"

local dialog = {
  T.tooltip { id = "tooltip_large" },
  T.helptip { id = "tooltip_large" },
  T.grid { T.row {
    T.column { T.grid {
      T.row { T.column { horizontal_grow = true, T.listbox { id = "the_list",
        T.list_definition { T.row { T.column { horizontal_grow = true,
          T.toggle_panel { return_value = -1, T.grid { T.row {
            T.column { horizontal_alignment = "left", T.label { id = "the_label" } },
            T.column { T.image { id = "the_icon" } }
          } } }
        } } }
      } } },
      T.row { T.column { T.grid { T.row {
        T.column { T.button { id = "ok", label = _"OK" } },
        T.column { T.button { id = "cancel", label = _"Cancel" } }
      } } } }
    } },
    T.column { T.image { id = "the_image" } }
  } }

local function preshow()
    local t = { "Ancient Lich", "Ancient Wose", "Elvish Avenger" }
    local function select()
        local i = wesnoth.get_dialog_value "the_list"
        local ut = wesnoth.unit_types[t[i]].__cfg
        wesnoth.set_dialog_value(string.gsub(ut.profile, "([^/]+)$", "transparent/%1"), "the_image")
    wesnoth.set_dialog_callback(select, "the_list")
    for i,v in ipairs(t) do
        local ut = wesnoth.unit_types[v].__cfg
        wesnoth.set_dialog_value(, "the_list", i, "the_label")
        wesnoth.set_dialog_value(ut.image, "the_list", i, "the_icon")
    wesnoth.set_dialog_value(2, "the_list")

local li
local function postshow()
    li = wesnoth.get_dialog_value "the_list"

local r = wesnoth.show_dialog(dialog, preshow, postshow)
wesnoth.message(string.format("Button %d pressed. Item %d selected.", r, li))


  • wesnoth.set_dialog_value(value, path, to, widget, id)

Sets the value of a widget on the current dialog. The value is given by the first argument; its semantic depends on the type of widget it is applied to. The last argument is the id of the widget. If it does not point to a unique widget in the dialog, some discriminating parents should be given on its left, making a path that is read from left to right by the engine. The row of a list is specified by giving the id' of the list as a first argument and the 1-based row number as the next argument.

-- sets the value of a widget "bar" in the 7th row of the list "foo"
wesnoth.set_dialog_value(_"Hello world", "foo", 7, "bar")

Notes: When the row of a list does not exist, it is created. The value associated to a list is the selected row.


  • wesnoth.get_dialog_value(path, to, widget, id)

Gets the value of a widget on the current dialog. The arguments described the path for reaching the widget (see #wesnoth.set_dialog_value).

(Version 1.13.0 and later only) For treeviews this function returns a table descibing the currently selected node. If for example in this treeview


Item 9 is selcted the value will be {2,1,3}


  • wesnoth.set_dialog_active(active?, path, to, widget, id)

Enables or disables a widget. The first argument is a boolean specifying whether the widget should be active (true) or inactive (false). The remaining arguments are the path to locate the widget in question (see #wesnoth.set_dialog_value).


  • wesnoth.set_dialog_callback(callback_function, path, to, widget, id)

Sets the first argument as a callback function for the widget obtained by following the path of the other arguments (see #wesnoth.set_dialog_value). This function will be called whenever the user modifies something about the widget, so that the dialog can react to it.


  • wesnoth.set_dialog_markup(allowed?, path, to, widget, id)

Sets the flag associated to a widget to enable or disable Pango markup. The new flag value is passed as the first argument (boolean), and the widget to modify is obtained by following the path of the other arguments (see #wesnoth.set_dialog_value). Most widgets start with Pango markup disabled unless this function is used to set their flag to true.

wesnoth.set_dialog_markup(true, "notice_label")
wesnoth.set_dialog_value("<big>NOTICE!</big>", "notice_label")


  • wesnoth.set_dialog_focus(path, to, widget, id)

(Version 1.13.2 and later only)

Switches the keyboard focus to the widget found following the given path (see #wesnoth.set_dialog_value). This is often useful for dialogs containing a central listbox, so that it can be controlled with the keyboard as soon as it is displayed.



  • wesnoth.set_dialog_visible(visible?, path, to, widget, id)

(Version 1.13.2 and later only)

Sets a widget's visibility status. The new status is passed as the first argument, and the path to the widget is specified by the remaining arguments (see #wesnoth.set_dialog_value). The following visibility statuses are recognized:

String value Boolean shorthand Meaning
visible true The widget is visible and handles events.
hidden The widget is not visible, doesn't handle events, but still takes up space on the dialog grid.
invisible false The widget is not visible, doesn't handle events, and does not take up space on the dialog grid.
wesnoth.set_dialog_visible(false, "secret_button")


  • wesnoth.set_dialog_canvas(index, content, path, to, widget, id)

Sets the WML passed as the second argument as the canvas content (index given by the first argument) of the widget obtained by following the path of the other arguments (see #wesnoth.set_dialog_value). The content of the WML table is described at GUICanvasWML.

-- draw two rectangles in the upper-left corner of the window (empty path = window widget)
wesnoth.set_dialog_canvas(2, {
    T.rectangle { x = 20, y = 20, w = 20, h = 20, fill_color= "0,0,255,255" },
    T.rectangle { x = 30, y = 30, w = 20, h = 20, fill_color = "255,0,0,255" }

The meaning of the canvas index depends on the chosen widget. It may be the disabled / enabled states of the widget, or its background / foreground planes, or... For instance, overwriting canvas 1 of the window with an empty canvas causes the window to become transparent.


  • wesnoth.add_dialog_tree_node(type, index, path, to, widget, id)

(Version 1.13.0 and later only)

Adds a childnode to a treeview widget or a treeview node. The type (id of the node definition) of the node is passed in the first parameter. The second parameter (integer) spcifies where the node should be inserted in the parentnode. The other arguments describe the path of the parent treeview (-node)


  • wesnoth.remove_dialog_item(index, count, path, to, widget, id)

(Version 1.13.1 and later only)

Removes an item from a listbox, a multipage or a treeview. The first parameter is the index of the item to delete, the second parameter is the number of items to delete, with 0 standing for all items after the specified index; the remaining parameters describe the path to the listbox, the multipage or the parent treview node.


  • wesnoth.add_widget_definition(widget_type, definition_id, wml_content)

(Version 1.13.? and later only)

Creates a new wml widget definition see for examples wml_content


  • wesnoth.is_skipping_messages()

(Version 1.13.2 and later only)

Returns true if messages are currently being skipped, for example because the player has chosen to skip replay, or has pressed escape to dismiss a message.


  • wesnoth.skip_messages([skip?)

(Version 1.13.2 and later only)

Sets the skip messages flag. By default it sets it to true, but you can also pass false to unset the flag.


  • wesnoth.get_displayed_unit()

Returns a proxy to the unit currently displayed in the side pane of the user interface, if any.

local name = tostring(wesnoth.get_displayed_unit().name)


  • wesnoth.log([logger], message', in_chat)

(Version 1.13.5 and later only) Logs a message to the console. These messages are normally not visible unless Wesnoth is run from the command-line or (in Windows) with the --wconsole switch. The in_chat argument, however, can be set to true to also echo the message to the in-game chat area.

Possible loggers are info, debug, warning, error, and wml. The wml logger is special and is intended for WML errors; it always goes to chat, so the in_chat argument is ignored and can be omitted. The default logger is info.


This field is not a function but an associative table. It links item names to the functions that describe their content. These functions are called whenever the user interface is refreshed. The description of an item is a WML table containing [element] children. Each subtag shall contain either a text or an image field that is displayed to the user. It can also contain a tooltip field that is displayed to the user when moused over, and a "help" field that points to the help section that is displayed when the user clicks on the theme item.

Note that the wesnoth.theme_items table is originally empty and using pairs or next on it will not return the items from the current theme. Its metatable ensures that the drawing functions of existing items can be recovered though, as long as their name is known. The example below shows how to modify the unit_status item to display a custom status:

local old_unit_status = wesnoth.theme_items.unit_status
function wesnoth.theme_items.unit_status()
    local _ = wesnoth.textdomain "mydomain"
    local u = wesnoth.get_displayed_unit()
    if not u then return {} end
    local s = old_unit_status()
    if u.status.entangled then
        table.insert(s, wml.tag.element {
            image = "entangled.png",
            tooltip = _"entangled: This unit is entangled. It cannot move but it can still attack."
    return s

The things that would need to be to modified in the above code are:

  • the domain of your addon ("mydomain"), assuming that you are using translations. Otherwise just remove the underscore in the tooltip line.
  • the name of the status (u.status.entangled). Note that if the attribute happens to be inside [variables], so be it: u.variables.whatever.
  • the path to the image ("entangled.png").
  • the tooltip of the status ("entangled: This unit ...").

The following is a list of valid entries in wesnoth.theme_items which will have an effect in the game. Unfortunately when this feature was created the full range of capabilities of the feature was never properly documented. The following list is automatically generated. To find out what each entry will do, you will have to make guesses and experiment, or read the source code at src/reports.cpp. If you find out what an entry does, you are more than welcome to edit the wiki and give a proper description to any of these fields.

  • unit_name
  • selected_unit_name
  • unit_type
  • selected_unit_type
  • unit_race
  • selected_unit_race
  • unit_side
  • selected_unit_side
  • unit_level
  • selected_unit_level
  • unit_amla
  • unit_traits
  • selected_unit_traits
  • unit_status
  • selected_unit_status
  • unit_alignment
  • selected_unit_alignment
  • unit_abilities
  • selected_unit_abilities
  • unit_hp
  • selected_unit_hp
  • unit_xp
  • selected_unit_xp
  • unit_advancement_options
  • selected_unit_advancement_options
  • unit_defense
  • selected_unit_defense
  • unit_vision
  • selected_unit_vision
  • unit_moves
  • selected_unit_moves
  • unit_weapons

The default generator expresses each weapon line as a separate element

return { 
    wml.tag.element { text = "weapon text",
      tooltip = _"weapon tooltip"
    -- ...
  • highlighted_unit_weapons
  • selected_unit_weapons
  • unit_image
  • selected_unit_image
  • selected_unit_profile
  • unit_profile
  • tod_stats
  • time_of_day
  • unit_box
  • turn
  • gold
  • villages
  • num_units
  • upkeep
  • expenses
  • income
  • terrain_info
  • terrain
    • Text for terrain of the active hex. Same as the one displayed on the UI. It probably returns an empty table when no hex is active. Otherwise, it returns something like {{"element",{text="Grassland (Flat)"}}}.
  • zoom_level
  • position
  • side_playing
  • observers
    • When there is no observer, it returns an empty table. When there are observers, it gives {{"element", {tooltip="Observers\n<observer1>\n<observer2>\n", image="misc/eye.png"} }}
  • selected_terrain
  • edit_left_button_function
  • report_clock
    • wesnoth.theme_items.report_clock() gives a table {{"element", text=<time>}}, where <time> is HH:MM of local time. It probably return 12hr or 24hr formats depending on user preference.
  • report_countdown
    • wesnoth.theme_items.report_countdown() gives a table {{"element", text=<time>}}, where <time> is MM:SS of time limit for player's turn. If turn limit < 120 or 60, add <span foreground="#c8c800"> or <span foreground="#c80000"> html tags.


  • helper.get_user_choice(message_table, options)

Displays a WML message box querying a choice from the user. Attributes and options are taken from given tables (see [message]). The index of the selected option is returned.

local result = helper.get_user_choice({ speaker = "narrator" }, { "Choice 1", "Choice 2" })


(Version 1.13.8 and later only)

  • wesnoth.zoom(factor[, relative])

Changes the zoom level of the map. If relative is false, which is the default, it simply sets the zoom level. If relative is true, it zooms relative to the current zoom level. So, for example, if the zoom level is currently 0.5, then wesnoth.zoom(2) sets it to 2, while wesnoth.zoom(2, true) sets it to 1 (2 * 0.5).

This function also returns the resulting zoom level. Because of this, you can call wesnoth.zoom(1, true) to simply get the current zoom level.

Note that this function cannot zoom to a level that the user would not be able to reach from the UI. Attempting to do so will simply select the nearest allowed zoom level.

This page was last edited on 12 February 2021, at 13:28.