The RPG Engine is responsible for the actual gameplay itself; moving actors around, running attacks, managing inventory, and so forth. I suppose some might call it the "Gameplay Engine", but for this blog entry, I will call it the "RPG Engine". As a side note, as far as the RPG Engine is concerned, all the code above it (see the picture in Part 1) is "the application", even though you and I know that the upper layers are also subdivided into various logical units.
In designing the RPG Engine, I had a couple of goals for how I wanted the code to work. The first of these was that the RPG Engine should be a "pure" implementation of the SENG RPG system. If you look at the online manual for "To The World Tree" here http://www.prankster.com/ttwt/manual/ttwt.htm, the "Game System" section describes the SENG RPG system. In a nutshell, though, the SENG RPG system can be described it in a manner independent of graphics or interface or anything; it could be the rules to a (extremely fiddly, math-centric) pencil-and-paper RPG system. The SENG RPG Engine is an implementation of that; and multiple interfaces or graphics engines could be written on top of it. In fact, early on, for testing, I had a crude text-based interface; no one in their right mind would play the game with that interface, but it was functional.
A related goal is that the RPG Engine is purely reactive to the upper layers. It doesn't have any threads, it never looks at the system clock. Instead, when the application wants it to run a tick (basically a turn, but turns in SENG are very short), it calls the method RunTick(), and the RPG engine runs the tick. Then, the application should look at the state of the game to determine what to display. The RPG Engine itself never makes the decision "Uhoh, we're running late, I'd better run a tick now".
The last goal is that the game description structures are view only, and the application manipulates these structures indirectly, by creating orders objects and pushing them on the appropriate game description object. For instance, there is a CActor object. The application is welcome to use the CActor object to determine (say) the position of the actor on screen. However, if the application decides to move the actor (say, because the player clicks somewhere on the screen), it doesn't update the position directly in the CActor object. Instead, it creates a CMoveOrder object, containing the position to move to, and pushes that CMoveOrder object onto the CActor. Then, when the engine runs a tick, the actor will perform appropriately.
OK, given those goals, here's a picture of the RPG engine organization:
The Game portion of the code is responsible for storing the current state of the game, and contains sub-objects like areas, actors, items, and so forth. The Engine portion of the code is responsible for manipulating the Game objects; moving actors around and the like.
The standard initialization of the RPG Engine is that the Engine uses the Game code to load a game from disk, loading the stored game state. This may also trigger the loading of module data, like the layout of the area, which is fixed and thus isn't stored in a save game file. From that point forward, the Game state is stored strictly in memory, until the game is exited for some reason (the player saves and quits, for instance). The exception is that the current implementation only keeps the full information for one area (area is the same as level, basically) in memory, and thus will purge this data and reload (from the module) new area data when the player moves between areas.
The Game structures are organized in a nice, object oriented manner. A CArea contains all the actors currently in that area through CActor objects, a CActor contains its inventory through CItem objects, and so forth. These objects contain data, and also some basic helper functions.
The Engine code, on the other hand, is basically a big monolithic class with a large number of methods for the various game manipulation functions. I'm sure the object-oriented purist would have a fit, but it works well enough. The majority of the methods are related to carrying out the various orders that a CActor might undertake. For instance, there is a ProcessMoveOrder() method, a ProcessAttackOrder() method, and so forth. When the application calls RunTick() on the Engine object, it goes through the Game structures and calls these appropriately.
The View/Order system that is central to the RPG Engine design was partly inspired by my background in play-by-email games; see the Atlantis Project homepage http://www.prankster.com/project/index.htm for a play-by-email game I implemented. Play-by-email games are the most extreme example; in that case, the player gets to issue orders something like once a week, and a "tick" is only run once per week! But the design scales well to something like SENG, where ticks are run tens of times a second, and I definitely recommend the View/Order design to other game engine implementors.
Okay, that covers the RPG Engine architecture. I think I'll move on to the Graphics Engine in the next RPG Anvil post.