The map module is responsible for presenting the game world to the player. It is separate from the actual state of most game world elements, which are part of the RPG Module.
Map Objects and Shapes
On the outside, Adonthell is a 2D game, where the world consists entirely of 2D sprites as provided by the Gfx Module. Internally, for the purpose of collision detection, the extend of a sprite is represented by a simple 3D model, called its shape. The combination of sprite and shape are hereafter referred to as placeables'. They are persisted using the placeable format. The coordinate system used by shapes is as follows:
The camera position is fixed and provides for a top-down view on the map. It follows that the X axis in 3D space runs from west to east, the Y axis from north to south and the Z axis from bottom to top. Due to the top-down view, there is no visual distinction between Y and Z axis. It is not possible to view the sides of an object, only its front and top part. (Note that the image above diverts from that view for clarity.)
The map itself is assembled from placeables, which are stored in an octree-like structure that keeps track of their position. It also allows fast object retrieval for the purpose of collision detection or rendering of a scene. Usually, one placeable instance will be placed on the map multiple times, meaning that all these objects will share the same sprite and thus have the exact same state. This is fine for general scenery and objects that serve no particular purpose in the game itself. But there are also objects that need to be referenced by game scripts or objects that really need to have an individual state (doors or characters for example). The following figure shows how this is made possible by the underlying data structures:
Anonymous objects can be place multiple times on the map, but will share the same underlying placeable. Shared objects are placed on the map only once, but all of them still share the same placeable. Finally, unique objects are placed on the map only once and every one is backed by a distinct placeable instance. A placeable itself can either be a static scenery object, a moving creature or an item that can be picked up.
The collision detection is implemented using the swept sphere algorithm described in . Details regarding the algorithm can be found there. What remains to be described here is the coordinate-transformation that needs to take place in order to map absolute (pixel-)coordinates of moveable map objects to the relative coordinates of the shapes we test for collision.
To be explained later ...
Maps and Views
One prominent component of the engine is the renderer. It produces the graphical representation of a scene, and that about 40 times per second. (A new scene â€“ or internal engine state â€“ is computed about 75 times per second, btw.)
More important than those numbers or the fact that drawing and update operations are disjunct is the implementation. As the heading suggests, a model-view-controller (MVC) architecture is used.
The part of the map being displayed is determined by so-called mapviews. A mapview can be of any size and can direct output to any surface. Multiple mapviews can be active at the same time, although at present they canâ€™t show different maps, just different areas of the same map.
The question that remains is how a mapview knows what part of a map to display. For that purpose, each mapview may have a schedule script assigned (the controller), which is executed before rendering takes place. The script in turn can use the methods provided by the mapview class for all kinds of effects. Usually, it will want to center the view on a certain character, but it is not limited to this.
Character schedules are basically the "AI" behind NPCs and creatures, as they dictate the actions performed by a character. As most actions occur in context of a specific location, schedules are part of the world module. Schedules themselves are implemented in two layers. A master schedule determines the action to take in a specific situation, whereas an actual worker schedule performs the action.
The idea behind this layered system is that the master schedule basically implements a characters personality. As such it is often unique for a character or (class of) creature. The actions themselves are implemented as generic worker schedules, so that they can be reused across different characters, customized only through their input parameters. As development of actual games for the engine progresses, a library of worker schedules should evolve that can be used to create varied and distinct character behavior.
Once a master schedule has been assigned to a character and activated, it will be automatically triggered when the character is updated for the next game cycle. It will determine the worker schedule to start and if started successfully, it will wait for the worker schedule to stop.
The worker schedule, on starting up will have to trigger some actions, then register callbacks that will notify the worker that the actions are completed. On completion, it can trigger new actions, register new callbacks and continue to remain active. At any time in this cycle, it can be paused (in which case it should suspend any activity unless woken up). Once a worker schedule has finished its task and stops, the manager schedule will switch to running mode again, where it will be triggered during the next game cycle. It can now determine the next worker schedule to run.
As an alternative (not present in the diagram), a worker schedule can queue its successor in the schedule system. Once it stops, the schedule system will automatically start the queued worker script instead of running the manager schedule.
The engine itself implements only the rendering of the map. Everything else, like transitions between map areas (characters entering buildings for example), interaction with items or characters on the map (picking up, initiating dialogue) and interaction with the map itself (traps, falling down) are handled by Python scripts. To trigger these scripts, so called map events are implemented and registered with the Event system.
The following events were implemented in the v0.3 engine and will probably appear in v0.4 as well:
- An enter event is triggered when a character enters a location (tile)
- A leave event is triggered when a character leaves a location (tile)
- An action event is triggered when the player interacts with characters or objects on the map