As Adonthellâ€™s development progressed, a few fundamental design decisions have been made. These principles have to be taken into account, whenever a part of the engine is updated or new parts are added. So please read on carefully before you start coding; it may save you much trouble.
- Code and data have to be strictly separated
This is probably the most important rule of all. The engine is not intended for a single RPG, but should instead be able to drive multiple games. For that reason, the code must not contain any game data. Only functionality needed for a RPG may be provided by the engine, not the games themselves.
- The engine should be as flexible as possible
Creators of games should have the most possible freedom in designing their game. That is, the engine shall help people making their own RPGs without limiting their creativity. The game should control the engine, not the other way round. That means that the engine is no interpreter of game data, but rather a library the game may use for common and repetitive tasks.
- The engine should be easily extendable
To allow people to make quite different types of games, with different rules, different items, different AI or just a different user interface, the engine needs to be customisable. Therefore, not only game data, but also most of the game mechanics have to be separated from the core engine.
- The engine should be portable
Adonthell shall run on as many different operating systems and hardware architectures as possible. Right now the engine supports BeOS, BSD, Linux, Mac OS X, Solaris and Windows on hardware such as arm, alpha, ia64, ppc, sparc, x86 and possibly others. This has to be taken into account when writing code or utilising third-party libraries.
When considering all these four points, we can draw a rough picture of the engine and its surrounding layers: the engine core is the only component that has direct access to the operating system and underlying hardware. It is written in C++ and provides common functionalities. Game specific extensions â€“ written in Python â€“ make use of the interface provided by the engine core to implement functionality specific to a game. Avoiding direct access to the OS ensures that games will run on all platforms supported by the Adonthell engine.
Both game engine and game extensions may access game data: maps, graphics, dialogues and so on.
The engine core isnâ€™t a monolithic bloc, but split into a number of modules, each with its own namespace and each compiled into its own library. For each engine module, a corresponding extension module exists to allow access from Python.
The Base Module contains low level functionality used by most, if not all, the other modules. This includes I/O routines, i18n support, configuration file handling, a timer and more.
A number of modules are accompanied by a backend library, providing the glue between the engine core and third-party code responsible for capturing input and handling audio and video output. Currently, SDL is the only backend supported, but support for other libraries may be added at need. This backend mechanism serves two purposes: first, it should allow to use different, more suitable backends - like QT or GTK+ â€“ for our editors while still having full access to the functionality provided by the engine. Second, it limits the dependency upon a single media library. Should SDL 1.2 become obsolete, a port to a different backend would be fairly simple.
The Main Module provides the entry point for applications using the Adonthell engine, both Python scripts or C++ programs. It takes care of initializing other modules as requested, parses commandline arguments and a generic configuration file. It then passes control to a user supplied callback (Python) respectively main method (C++).
The Python Module provides the C++ <--> Python bindings. For one, it is a thin wrapper around the Python C API, allowing access to python scripts, classes and methods from within the engine. At the same time, it provides the functionality for accessing C++ objects from within Python scripts.
The Gfx Module provides graphic primitives, like surfaces to draw on and image loading/saving abilities. Any video output is routed through this module, meaning all other parts of the library that render something on the screen have no dependencies to the underlying graphic library.
The Input Module keeps track of keyboard, mouse and gamepad input. It also provides a configurable, virtual control device. As with gfx, all user interaction with the engine is routed through this module.
The Audio Module allows playback of sound effects and background music and provides other sound related functionality to parts of the engine that need them. Primary audio format is Ogg Vorbis.
A great deal of gameplay is accomplished through Python methods triggered by various events the engine generates. The Event Module provides the core event system based on a publish/subscribe mechanism. It also implements a date class to keep track of time in the game world and time events based upon that class.
The Rpg Module provides core functionality required by every RPG: characters, quests, items, inventories and so forth. However, it does not contain complete implementations where this would limit flexibility, only interfaces. Those have to be extended by Python scripts in order to implement the rules of a specific game.
The Gui Module provides a themeable widget library, suitable for modelling an unobtrusive game interface. It supports true type fonts through FreeType 2.
High and low level classes for displaying the state of the gameworld to users are kept in the Map Module.
Python on Board
Python is an easy to learn, interpreted, object oriented programming language â€“ and as such the perfect means to accomplish some of the design principles mentioned earlier. Adonthellâ€™s integration with Python works in two ways. In one direction, Adonthell acts as a Python interpreter. Therefore, it can delegate parts of the work to user-supplied scripts, allowing for more flexibility. This can be compared to a plug-in mechanism. In the other direction, all of Adonthellâ€™s modules can be accessed from within Python scripts. If a C++ class bar exists in namespace foo, a Python script can import bar from foo and manipulate it as if it was accessed from C++ code. That way, objects created in Python scripts can be passed to C++ methods or vice versa, with practically no limitations.
This is accomplished by SWIG, which provides the neccessary glue between C++ and Python. Given a C++ class, SWIG will generate a Python module with the same interface. Internally, all calls to methods of the Python object are routed to the underlying C++ implementation. The gory details of wrapping and unwrapping C++ objects in order to pass them from C++ to Python and back are covered by SWIG. The pass_instance and retrieve_instance template functions supplied by our Python module further simplify this task. They are described in the API.
As a result, the engine can be seen as a library providing bits and pieces of RPG related functionality that can be used to write the actual game in Python. The benefit of that concept is that quite different games can be created without having to touch the underlying engine. One day, a community might form around the engine, providing players with mods and completely new games.