1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296
|
/*
$Id$
Copyright (C) 2001/2002 Kai Sterker <kaisterker@linuxgames.com>
Part of the Adonthell Project http://adonthell.linuxgames.com
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY.
See the COPYING file for more details.
*/
/*! \page page3 The Event System
The %event system is divided into three parts. The \ref event_sec1
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 \ref event_sec2
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
\ref event_sec3 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.
\section event_sec1 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:
- register_event() to pass a new %event to the handler. %Events
need to be registered with the handler before they can take
place
- remove_event() to remove a previously registered %event from
the handler
- raise_event() to send a message to the handler that will trigger
matching events.
\section event_sec2 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.
\code
NEW_EVENT (subclass)
REGISTER_EVENT (type, subclass)
\endcode
%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:
- event_list::add_event() adds an %event to the list and registers it
with the %event handler.
- event_list::put_state() saves the list with all events inside.
- event_list::get_state() restores the list with all events and registers
them with the %event handler.
\section event_sec3 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 \link py_object
python script \endlink, a \link py_callback python callback \endlink
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 \link schedule character schedules \endlink 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.
- equals() is used to compare two events for 'equality'. The message
sent to the handler is compared to all registered events to see if
they match.
- execute() is used once the %event is triggered and should executed
the %event's action.
\section event_sec4 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:
\code
// 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);
\endcode
For a list of available events with their corresponding parameters
see the \link event API documentation \endlink.
The %event script in that example could look as follows:
\verbatim
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)
\endverbatim
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:
\code
// 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);
\endcode
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 \link
enter_event enter event \endlink , you'll want to send a message to the
%event handler whenever a %character reaches a new position on the map.
\code
// 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);
\endcode
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.
\section event_sec5 The Different Event Types
Various types of events are defined by the %game engine, all
inheriting from the event class defined in event.h.
\subsection mapevents Map Events
There are 3 types of map events:
\li enter_event, which are triggered whenever a %character enters a
square,
\li leave_event, which are triggered whenever a %character leaves a
square,
\li action_event, which are triggered when the main %character "acts"
on a given square.
All these map events inherits from the map_event class, which
contains all the parameters they need:
\li x, the X coordinate of the square the %event happened on,
\li y, the Y coordinate of the square the %event happened on,
\li submap, the number of the submap where the %event happened on,
\li c, a pointer to the %mapcharacter that triggered the %event,
\li dir, the direction the %mapcharacter is facing when the %event
is triggered.
When a map %event is triggered, the run ()method of the Python script
is called, with the arguments \e submap, \e x, \e y and \e name, which
is the name of the mapcharacter that triggered the %event.
Map events are repeated forever by default.
\subsection timeevent 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.
\section event_sec6 Saving and Restoring Events
As described in the \ref event_sec2 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.
*/
|