LuaAPI/wesnoth/game events

From The Battle for Wesnoth Wiki
< LuaAPI‎ | wesnoth
Revision as of 06:03, 19 July 2022 by Celtic Minstrel (talk | contribs) (wesnoth.game_events.add_wml: Update based on latest master)

The game_events module contains functions for manipulating game event handlers and hooks that can be used for registering low-level game events.

Functions

(Version 1.17.x and later only)

NOTE: This is only a draft documentation section for an API that is not yet available in any released version. However, some parts of the API are already available under other names:

  • wesnoth.add_event_handler takes an [event] tag as the argument and is otherwise equivalent to the last form of wesnoth.game_events.add
  • wesnoth.remove_event_handler is equivalent to wesnoth.game_events.remove
  • wesnoth.fire_event is equivalent to wesnoth.game_events.fire
  • wesnoth.fire_event_by_id is equivalent to wesnoth.game_events.fire_by_id

wesnoth.game_events.add

Registers a new event handler, as EventWML. There are three ways to call this function. The first and most flexible is to use the options table. The second method is suitable for quick, recurring events, or for menu items. The third is the underlying mechanism of the event tag and mostly exists just for quick compatibility.

  • wesnoth.game_events.add(options table)

Registers an event handler in the scenario. The argument is a table containing several optional named arguments. The possible arguments are:

  • name: The name of the event to handle. This can be a string (a single event name, a comma-separated list, or an expression of WML variables that can expand to one or more event names) or a table of event names as individual strings (which can also possibly contain WML variables). Default is usually an empty string, but note that either a name or ID is required. If menu_item is true, the name defaults to "menu item id", where "id" is the same ID as below.
  • id: The event ID, used to remove it later; also, if an event with the same ID is already registered, then this event will be ignored and will not be registered. Default is an empty string. Required if menu_item is true. Note that either a name or ID is required for any event.
  • menu_item: True if this is a menu item (an ID is required); this means removing the menu item will automatically remove this event. Default false. Note that this does not, by itself, mean the event will be triggered when the menu item is selected. The name determines when the event is triggered.
  • first_time_only: Controls whether this event will fire more than once. Default true, meaning that it fires only once and is then removed.
  • filter: Filters that, if not passed, will prevent the event from firing. There are three possible formats for this:
    • A WML table with filter tags and attributes (like an [event] tag with no action content and no name or id). In this case the filters are parsed in the same way as they would be for an [event] tag.
    • A table of the form {filter_type = filter_contents}. The possible filter types are: formula, condition, side, unit, second_unit, attack, and second_attack. Each filter is a WML table, except for formula which is a string. A filter other than formula may also be set to a string, in which case it is taken as a WML variable that contains the filter. In this case the variable will be read at event handling time, not at registration time, so modifying the variable will dynamically alter the event's filter.
    • A function that optionally takes a WML table as argument and returns a boolean value.
  • filter_args: An optional WML table that will be passed to the filter function. This is used only if the filter is a function.
  • content: The content of the event. If action is specified, this is a WML table passed verbatim to that function when the event fires. If action is not specified, this will be interpreted as ActionWML to be executed when the event fires.
  • action: The function to call when the event triggers. It can take a WML table as argument (the content) but does not return anything.

wesnoth.game_events.add_repeating

  • wesnoth.game_events.add_repeating(event name, function)

Registers a recurring event (as [event]first_time_only=no) with the specified event name which will call the function when it triggers. The event name is the same as the name key in the options table.

The function takes no argument and returns nothing.

wesnoth.game_events.add_menu

  • wesnoth.game_events.add_menu(menu item id, function)

Registers a recurring event for a menu item. The first argument is the menu item ID, rather than an event name, so it cannot be an array or contain any commas. (TODO: Can it contain variables?) The event will be automatically linked to the menu item with the same ID and will be removed from the scenario if that menu item is removed. It will trigger when the menu item is selected.

The function takes no argument and returns nothing.

wesnoth.game_events.add_wml

  • wesnoth.game_events.add_wml(event config)

This is simply the underlying implementation of the event tag. The argument is the entire content of an event tag. It will parse name, id, delayed_variable_substitution, first_time_only, filters, and ActionWML from this config and use that to register an event.

wesnoth.game_events.remove

  • wesnoth.game_events.remove(id)

Removes the event handler with the given ID, if it exists. If it does not exist, this does nothing.

wesnoth.game_events.fire

  • wesnoth.game_events.fire(name, [first location, [second location]], [event data]) → processed?

Fires all events with the given name, passing any specified data into the event. If the locations passed in have a unit on them, that unit will become the primary or secondary unit of the event. The event data is a config that can contain arbitrary data.

The most common use of event data is to pass in the weapons for an attack event - to do this, add a first and second tag to the config. Other data used by built-in events includes the damage_inflicted value in an attack hit event, or the owner_side value in a village capture event. For custom events, you can put anything you want in here.

wesnoth.game_events.fire_by_id

  • wesnoth.game_events.fire(id, [first location, [second location]], [event data]) → processed?

Same as #wesnoth.game_events.fire, but triggers the event with a specific ID instead of all events with the given name.

Hooks

wesnoth.game_events.on_event

  • wesnoth.game_events.on_eventfunction(event_name)

This is a lower-level method of handling events. It triggers on all events before any registered event handlers and is passed the name of the event as its sole parameter - the rest of the event info is available via wesnoth.current.event_context. This hook is called before any registered event handlers (eg [event] tags) for the event. The event names passed to on_event always use underscores instead of spaces.

Note: a on_event handler will not prevent undoing of that event, so if your callback intends to change the game state, you will need to take extra care to avoid out-of-sync errors. The easiest way is to have the callback register a new event with the same name, which will have the side-effect of disallowing undo. Since on_event is called before the event is fully processed, this new event will be triggered as soon as the callback exits. You could even wrap this functionality into a function:

function disallow_undo()
	wesnoth.wml_actions.event { name = wesnoth.current.event_context.name }
end

wesnoth.game_events.on_load

  • wesnoth.game_events.on_loadfunction(scenario wml)

This is a lower-level method of handling the load game event and the wesnoth.persistent_tags mechanism. It is called whenever the game is loaded, before the onload event is triggered. If overriding this, be sure to call the original version of the function, as wesnoth.persistent_tags also depends on this event.

The scenario WML passed to this function is not the complete WML of the scenario but only the tags that the core Wesnoth engine does not know how to handle. You will never find an [event] or [side] tag in this table, for example. Although some core tags are already handled using this mechanism, you should not rely on this - for example, reading [item] tags in on_load is not guaranteed to work in future versions. The following is a list of all such tags:

color_palette   color_range  display  end_level_data  era           event             generator
item            label        lua      menu_item       modification  modify_unit_type  music
next_item_name  objectives   options  side            sound_source  story             terrain_graphics
time            time_area    tunnel   undo_stack      used_item     variables

Note: since the on_load hook is called very early in the scenario, it cannot be set inside a [lua] tag in an [event], not even a preload one - it's called even before preload fires. It can only be set inside a [lua] tag outside an event, either at [scenario] level or global.

wesnoth.game_events.on_save

  • wesnoth.game_events.on_savefunction() → scenario wml

The counterpart to on_load, this function is called whenever the game is saved. Any returned WML is merged into the scenario WML in the saved game. Usually the higher-level wesnoth.persistent_tags mechanism is recommended instead of overriding this directly. If overriding this, be sure to call the original version of the function, as wesnoth.persistent_tags also depends on this event.

This callback can only be used to add custom data to the saved game. You can't use it to add extra data that the engine already knows about, such as events or sides, as such tags will be ignored when merging the result into the saved game WML. Although some core tags are already handled using this mechanism, you should not rely on this - for example, adding [item] tags in on_save is not guaranteed to work in future versions. See the on_load description above for a complete list of these tags.

Here's an example of how to use on_load and on_save to handle a custom tag:

-- some value that survives save/load cycles, but that is not forwarded to the next level
local level_local_data = 0

local old_on_load = wesnoth.game_event.on_load
function wesnoth.game_event.on_load(cfg)
    for i, tag in ipairs(cfg) do
        if tag[1] == "my_data" then
            -- recover the value stored in the savefile
            level_local_data = tag[2].value
            -- erase the child, since it has been handled
            table.remove(cfg, i)
            break
        end
    end
    -- call the previous hook, in case there are still some containers in the savefile
    old_on_load(cfg)
end

local old_on_save = wesnoth.game_events.on_save
function wesnoth.game_events.on_save()
    -- call the previous hook, in case it had some containers to store
    local cfg = old_on_save()
    -- add our own container to them
    table.insert(cfg, wml.tag.my_data{ value = level_local_data })
    -- tell the engine to store them in the savefile
    return cfg
end

wesnoth.game_events.on_mouse_action

  • wesnoth.game_events.on_mouse_actionfunction(x, y)

This function is called whenever the user left clicks on a hex, and is passed the coordinates of the clicked hex. This could be used for many purposes, such as a true long-ranged attack, but it has some problems.

The callback does not cancel the normal action that would have occurred as a result of the click, such as selecting or moving a unit. Furthermore, if the on_mouse_action callback takes some time to execute, it's possible the mouse moves during this time, and when the callback has completed, the action taken depends on the new position of the mouse, rather than the position that was originally clicked. Because of this bug, it is probably a bad idea to display any sort of animation in this callback or do anything that takes time.

In addition, this callback is unsynced, so it is not safe for use in multiplayer games unless you do manual sychronization.

Some discussion of the problems with this callback can be found in this enhancement request.

wesnoth.game_events.on_mouse_move

  • wesnoth.game_events.on_mouse_movefunction(x, y)

This function is called when the mouse moves. More specifically, it's called whenever the currently hovered hex changes. This callback does not share most of the shortcomings of the on_mouse_action callback, but it is still unsynced.