Main      Site Guide    

The Making of

Murkon's Refuge

Coding


The Original Game

The original game of Murkon's Refuge was written in straight C, for UNIX operating systems, and using curses for screen output. It consists of 18004 lines of code in 34 source files.

As procedural C programs tend to be, the code was not particularly modularized, although each file mostly only contained functions with somewhat common purposes. Modification of data was done directly throughout the code, rather than through an object's public methods, so changing the way the data is stored would be an insurmountable task. Game data itself, however, such as the layout of the dungeon, the monsters, the items, etc, were, at least, isolated into their own files -- so it actually would not be such a difficult task to create a new game, with new dungeon with new monsters and items, that uses the same engine.

The other disadvantage to the structure of the code was that the game engine and the user interface were tightly integrated. The code for springing a trap in the dungeon, for example, contained code that reported on this to the user interleaved with the code that calculated what the trap was, the damage it inflicted, and inflicting the damage. This lack of separation between the functionality and the user interface breaks a basic rule of object-oriented design. I wasn't aware of the rule at the time, and this wasn't an object-oriented design anyway, but the point is that this made porting the game to the web without rewriting the game from scratch an impossibility. The curses library for the UNIX/C game involved putting whatever you needed on the screen, wherever you needed it, whenever you needed it. An HTML/CGI web interface involves responding to a button click by the user by regenerating an entire page and sending it to the browser in one chunk. Totally different paradigms of operation.

The Web Version

The web version of the game uses object-oriented C++. Every "noun" in the game is modeled as a separate class. (To avoid confusion with the word "class" as used in the game -- the profession of a character -- character classes will heretofore be referred to as "professions" and the word "classes" will only be used to refer to object-oriented programming classes.) A character is a class, encapsulating all data associated with a particular character: race, profession, stats, inventory, status, and so on. Methods in the character class include doing damage to the character, healing the character, changing the character's profession, setting the character's status, and so on. At every point in the game where damage must be inflicted on the character -- whether by monster, maze trap, loot trap, pool diving, whatever -- the character's dodamage() method must be called. The reason this is good is that if changes ever need to be made about the way the damage is done -- say I wanted to revamp the entire hit point system into something else entirely -- I only have to change that one method in that one place. Contrast this with the C version, where explicit manipulation of hit points is done wherever it is needed.

Other "nouns" that are modeled in their own classes are races, stats, statuses, items, item lists, inventories, spells, monsters, monster groups, monster parties, character parties, character rosters, traps, loot, squares in levels, levels in the dungeon, the dungeon as a whole, inns, clinics, shops, towns, and more. Less obvious things are environmental conditions -- groupings of data that describe where you are and under what conditions. This explicit division of function makes the code much cleaner, flexible, maintainable, modifiable, extensible, and less error-prone.

The game-specific data is isolated even further than it was in the original game. Now not just the data for the dungeon, spells, monsters, and items are isolated from the rest of the code, but professions, traps, and towns are, too. It is entirely conceivable to create a whole new game using the Murkon's Refuge engine -- totally new professions, races, dungeon, traps, monsters, items, spells, and spell sets -- in only a week or so, including testing. (Furthermore, the testing wouldn't be so much for bugs but to make sure the numbers -- monster strengths and the like -- are well balanced.)

The web version also separates the game engine from the user interface. Only a specialized subsystem of just a few different classes have anything to do with output to the user. Changing the HTML output in place now to something else -- text-based, or even something GUI-based, for a desktop version of the game -- would mean changing only the few output classes there are, rewriting each method to display the output in the proper way. The underlying game engine need not change. In the original UNIX/C version of the game, this kind of user interface replacement would have been impossible.

As a consequence of moving to an HTML/CGI interface, however, an additional functionality requirement was mandated. In desktop applications -- text-based or GUI-based -- or in java applets, the application is started, executes until you're finished with it, and only then exits. With a HTML/CGI interface, it's radically different. Every time you hit a button, starting with when you first log in, the game has to start up, execute, output the HTML page, and then exit. When you hit another button, it starts up, does its thing, and exits again. If it seems like you're running Murkon's Refuge a single time from the moment you start playing until the moment you stop, it's just an illusion. This mode of execution poses the following problem: how do you get the program to remember who you are and what situation you're in from one button click to the next? If you're dealing with a treasure chest and hit the button to disarm it, when the game process starts up, how is it supposed to know who you are, who your characters are, and what you're doing in the first place?

The answer is to save the game between every move -- that's why you can stop playing without notice, come back, and have the game start up again where you left off. Every time you hit a button, the game starts up, loads your game, makes the move, saves the game, and exits. Accounting for people reloading their browser window is even tricker. It's got to load the game, detect that it's already done the action you had just done, redisplay the correct page, and exit without modifying the saved game again.

The point of all this is that the problem faced by an HTML/CGI based game is that it needs to know how to save the game in every possible state. The original UNIX/C version did not do this. It saved the state of the characters whenever something happened to them, but if you killed the game prematurely (by hitting control-c or having the computer crash on you), you wouldn't be back where you left off when you restarted it. Changes to your characters would be preserved, but that's it -- you'd be back in town.

So this exhaustive save game requirement basically meant that practically every changeable class had to know how to write itself out to a file. The character roster class would know that it should write out every character that it had, and the character class would know how to write out the data it was responsible for, and so on. This save game requirement and the separation of function from interface required certain classes to exist that might otherwise not have had to. For example, when a trap goes off, the damage has to be calculated, stored in an object that knows how to write itself out to a file, and the user interface portion has to know how to read the data in that object and print out the proper messages that report on what damage was done. That way if the user left the game just then and logged back in the next day, the saved game file could be read, the game would remember that it had just inflicted trap damage, and the user interface portion could print out a report of the damage again.

In short, there are two basic steps to making a move in the game. There is the action step, in which whatever changes to the state of the game are necessary are made, and the view step, in which the information that needs to be displayed is displayed. If you make a move in the game, the action step executes first -- for example, damage from a trapped chest is inflicted on your characters -- and then the view step is executed -- which would be the part where the message, "The trap went off and blew everybody away," would be displayed.

Then, if instead of making another move, you reload the page (or log out and in again), the view step is executed again, but the action step is not. If the action step executed a second time, the trap would do double damage, and that wouldn't be very nice.


Back to The Making of Murkon's Refuge.