LuaAPI/wesnoth/game events
NOTE: This is only a draft documentation page 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
- The hooks (on_load, on_save, on_mouse_action, on_mouse_move, on_event) have the same names.
The game_events module contains functions for manipulating game event handlers.
Contents
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 an empty string, but note that either a name or ID is required.
- 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.
- menu_item: True if this is a menu item (an ID is required); this means removing the menu item will automatically remove this event, and selecting the menu item will trigger this event. Default false.
- 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: condition, side, unit, second_unit, attack, and second_attack. Each filter is a WML table. - 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(event name, function, false)
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 item id, function, true)
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.
The function takes no argument and returns nothing.
- wesnoth.game_events.add(delayed variable substitution?, event config)
This is simply the underlying implementation of the event tag. The first argument is the value of the delayed_variable_substitution key, and the second argument is the entire content of an event tag. It will parse name, id, 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.
wesnoth.game_events.on_event
- wesnoth.game_events.on_event ↔ function(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_load ↔ function(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_save ↔ function() → 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_action ↔ function(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_move ↔ function(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.