The Event System

The event system is divided into three parts. The Event Handler keeps track of all registered event scripts. Whenever an event occurs, the event handler is notified and executes all scripts registered for that particular event. The Event List keeps track of the events registered by a certain object, (e.g. a NPC, a maptile or item) and automatically unregisters these events when this object is deleted. Finally, there are the Events themself, used both as message sent to the event handler whenever an event occurs and to register an event script. Each event has its own data structure with parameters corresponding to its type. These parameters are passed to the event script, so all infomation regarding an event is available from within the script as well. The parameters can further be used to specialize the script so it reacts to a smaller range of events.

Event Handler

The event handler is the core component of the event system. It provides a method to initialize the event system and allows global access to the specialized handlers for individual events. For that purpose, it stores a list of event_handler_base objects, the virtual base class for the specialized handlers, and passes any event it receives to the right handler.

The event_handler_base class provides three pure virtual methods that need to be implemented by each specialized handler:

Event List

The event_list is a convenience class for objects that register events with the handler. As it is up to each object to save the events it registers, and to load and re-register them, the event list has been written to take care of that.

To make the event list independent from specific event types, it only works on pointers to the event base class. That works fine in all cases, except one. When loading events, the event list needs to instanciate the proper event subclass. For that purpose, the event list stores a list of callbacks that return a newly instanciated event subclass of a given type. Two macros have been written that take care of most of the work.

NEW_EVENT (subclass)
REGISTER_EVENT (type, subclass)

NEW_EVENT provides the function that will return a newly allocated event. REGISTER_EVENT will pass this function to the event list. For each event type, these two macros should be added to event_handler::init(), the event system's init method.

The event list provides the following methods:

Events

Events have two main purposes. Registered with the event handler, they allow to execute either a python script, a python or a C/C++ callback in case that event takes place. But they are also sent as messages to the handler, to trigger matching events.

Each specific event may have its own data fields and methods, depending on its purpose. However, all events share a number of common features, which are implemented by the event base class.

The first feature is the event's action, i.e. what happens if that event is triggered. The event class allows to attach a python script , a python callback or a C/C++ callback.

C/C++ callbacks are only useful when the game engine wants to make use of the event system internally. Python callbacks allow scripts like character schedules to use the event system. Event scripts usually define the action of a certain class of events. For example an event script might implement a trap that is activated whenever a character steps on it. This 'trap' script can then be used by traps all over the place.

Apart from the event's action, the base event class also provides means to repeat events or prevent them from repeating. For example, a trap might only work once. event::set_repeat() allows to specify how often an event can be executed. Once that repeat count reaches zero, executing the event will automatically delete it. This will also remove it from the event_handler and event_list. That way, events that are used up vanish from the game, just as one would expect.

Further, the event class has two pure virtual methods that need to be implemented by the specific events. They are provided so that (not so) specialized event handlers may take care of different event types.

Using the Event System

The event handler is implemented in the event_handler class. It totally consists of static members and methods, to make it easily accessible from any part of the code. Just include the event_handler.h file. To register a script with the handler that is executed whenever the player arrives at the coordinates (20, 20) of a map, you'd write:

// Create the filter and set it's parameters
event *filter = new enter_event;
 
filter->x = 20;
filter->y = 20;
filter->c = data::the_player;

// Set the script to be executed when the event occurs
filter->set_script ("a_script");

// Finally add the filter to the event list. This will register it with the event handler
add_event (filter);

For a list of available events with their corresponding parameters see the API documentation .

The event script in that example could look as follows:

class a_script:
    # -- constructor
    def __init__ (self, event, <additional arguments>):
        # -- the event the script belongs to
        self.myself = event
        ...

    # -- method called when the event occurs, the parameters
    #    depend on the type of the event
    def run (self, submap, x, y, direction, character):
        print "%s arrived at %i, %i" % (character, x, y)

As you can see, you have the possibility to pass extra parameters to the script constructor. This is limited to strings and integers though. When you set the argument list from C++, you have to manually create a Python tuple, and you should not forget to decrement its reference count when you are done with it. The following code could be used for the example above:

// Create our argument tuple that will be passed to the script constructor
PyObject * args = PyTuple_New (2);
PyTuple_SetItem (args, 0, PyInt_FromLong (10));
PyTuple_SetItem (args, 0, PyString_FromString ("2nd argument"));

// Set the script to be executed when the event occurs
filter->set_script ("a_script", args);

// We don't need our reference to the tuple anymore
Py_DECREF (args);

The script constructor would then receive two additional arguments. This is useful to create generic scripts that can be customized at runtime. To return to our old trap example, the amount of damage the trap does could be specified for each trap in this way.

Now we have registered an event with the event handler. But that alone won't get the event triggered. So depending on its type, you'll have to notify the event handler of an event's occurance. In case of the enter event , you'll want to send a message to the event handler whenever a character reaches a new position on the map.

// Event structures are also used to pass messages to the event_handler
enter_event message;

// Fill in the parameters
message.x = mapcharacter::posx ();
message.y = mapcharacter::posy ();
message.submap = mapcharacter::submap ();
message.c = this;

// Notify the event handler
event_handler::raise_event (&message);

The event handler will then compare all events of the given type with the message it received and execute the event action of events that match.

The Different Event Types

Various types of events are defined by the game engine, all inheriting from the event class defined in event.h.

Map Events

There are 3 types of map events: All these map events inherits from the map_event class, which contains all the parameters they need: When a map event is triggered, the run ()method of the Python script is called, with the arguments submap, x, y and name, which is the name of the mapcharacter that triggered the event.

Map events are repeated forever by default.

Time Event

The time_event can be used to track relative or absolute game time. Relative means, the event will be triggered after a given amount of time has passed, starting at the moment it is created. Absolute means a fixed point in time, i.e. a certain hour at a certain day.

Time events are not repeated by default. They can be told to repeat though, and it is also possible to specify the delay between two occurances.

Time events pass no arguments to the run() method of the Python script. The script can easily get the current game time from the gamedate class.

Saving and Restoring Events

As described in the Event List section, events can be saved to disk and restored at a later point. For this to work, a few restrictions have to be made, especially regarding any python arguments that get passed to script or callback. Events that contain a C/C++ callback cannot be saved at all, as it is not possible to restore that callback.

For events with an attached python script, the only restriction is that the argument needs to be a tuple, containing only string and/or integer objects. On saving, the name of the script and this argument tuple are saved to disk. On loading, the arguments are loaded, and the script is instanciated with those arguments.

The same restriction concerning arguments applies to events with an attached python callback. However, when saving the callback, only the name of the function or method is saved, but not the instance it belongs to. To restore the callback, it is neccessary to restore that instance first. Then a pointer to it needs to be assigned to py_callback::instance, which is a static member of the py_callback class. Only then can the callback be restored properly.


Generated on Wed Jun 18 16:53:41 2008 for Adonthell by  doxygen 1.5.6