Scripted Events
Script events provide a mechanism for the game to notify running scripts when a noteworthy change in the game or UI state has occurred. Scripts can listen for events by adding lua functions to be called when a particular event occurs. When the event occurs the game calls each registered listener function in sequence, giving each an opportunity to respond to the change in the state of the game.
Examples of available events include FactionTurnStart
, PanelOpenedCampaign
, CharacterCreated
and ComponentLClickUp
. Listener functions registered for the FactionTurnStart
event would be called whenever a faction starts its turn, listeners for the ComponentLClickUp
event would be called whenever the user left-clicks on the interface, and so on.
Additionally, the game provides a context object to each listener function it calls. The context object encapsulates information about the state change that has occured, and may be queried in script to find out more information about the event.
A list of supported script events*, and the information that each provides via its context object, is given in the external model hierarchy documentation here: Model Hierarchy
*not all available events are listed
The script may register a listener for an event with the core:add_listener
function. Once registered, listeners may be later removed with core:remove_listener
if required.
The example below shows a FactionTurnEnd
listener. Two callbacks are provided as part of the listener - a conditional check as the third argument and a target function as the fourth. Each callback is provided a context object which is generated by the triggering code. The methods that can be called on this context object vary from event to event, but are listed in the external model hierarchy documentation: Model Hierarchy
If a conditional check is specified, it is called each time the FactionTurnEnd
event is triggered, and must return true
(or a value that equates to true
when evaluated as a boolean - i.e. any value other than nil
or false
) for the conditional check to pass. Alternatively, the boolean value true
may be supplied in place of a conditional function, in which case the condition always passes.
If the conditional check passes (or true
was supplied in place of a conditional check), then the target function is called.
The fifth argument, if set to false
, causes this listener to shut down and remove itself after the target function is called the first time. If set to true
, the listener will persist until it is removed with core:remove_listener
.
Example - FactionTurnEnd listener:
Listen for a FactionTurnEnd event for a specific faction.
core:add_listener(
"faction_turn_end_listener", -- Listener name, by which the listener may be later cancelled if necessary
"FactionTurnEnd", -- Script event to listen for.
function(context) -- Optional conditional check.
return context:faction():name() == "wh_main_emp_empire"
end,
function(context) -- Target function.
empire_ending_turn(context:faction())
end,
false -- Should the listener persist after the target function has been called the first time?
);
Example - Persistent left-click listener:
core:add_listener(
"example_click_listener",
"ComponentLClickUp",
true, -- This listener performs no conditional check, always calling the target function
function(context),
handle_left_click(context.string, UIComponent(context.component))
end,
true -- This listener persists and doesn't shut down after the target callback is called the first time
);
Script events are used extensively in campaign and by the user interface to notify script of state changes. They are not used so prominently in battle, though. Battle scripts rely more on polling and on bespoke handler mechanisms - see the Handlers
section of this documentation.
The events system is underpinned by a global lua table
called events
which is created each time the script loads. This table contains many subtables, one for each event which may be triggered by the game.
The events table and related subtables may be seen in the events.lua
file which lives in script folder.
A listener for an event is "registered" by adding a function to the subtable related to the event. When the game code triggers an event, it looks for the subtable in the events table that matches that event’s name, and calls each element within that subtable with the generated event context object as a single argument. Removal of a listener means removing the appropriate function entry from the event subtable.
The listener functionality provided by the core
object adds another layer on top of this functionality. See the functions listed in the Event Handling
section of this documentation. While listeners may be added directly to the events
table, it is strongly recommended to use the listener functionality provided by core
.
When a new event type is added, a subtable with the same name must also be added in events.lua
. Without it the game will not be able to trigger the event. In this case, an error message will be printed to the Lua
console spool.
The script may also generate events of its own with the functions core:trigger_event
and core:trigger_custom_event
. A script event being triggered doesn't need to be present in the events
table, the triggering functions will add it if not found.