The event_handler_base class provides three pure virtual methods that need to be implemented by each specialized handler:
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:
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.
// 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.
Map events are repeated forever by default.
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.
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.