|Authors:||Ben Harrison and others|
|Operating System:||Nearly any imaginable system|
In the 1980s a new game genre was created. It featured a single-player dungeon adventure using character graphics. The game was called Rogue. Roguelike games continue to be a large development area of the open-source game community.
Angband is the current reigning king of roguelike games. Derived from Moria, Angband has numerous enhancements. You start as first level adventure. By exploring dungeon levels, your character increases in power until he can finally defeat Morgoth, Lord of Darkness.
Ben Harrison's code cleanup has resulted in an explosion of variants. The game and variants are available for nearly any platform. The design is very clean and extensively documented. No single column in Open Game Source can do justice to refinement that has gone into Angband. Before adding new features, I suggest any hackers take a look at the variants and the multitude of web sites devoted to the game.
When I downloaded Angband, it failed to compile initially under Linux. Normally I simply determine the correct options and recompile. Since it is a fairly popular game, I decided to download the SRPM package from RedHat's contrib page. The package included two patches for the game. The first patch has all the appropriate configuration for compiling under Linux. The second patch is far more interesting.
Under X windows, Angband opens eight windows on start. The first window is used for the main game display. The other windows can be adjusted to display various information such as inventory and character abilities. If you don't use these extra windows or only need one or two, the default eight can be annoying.
The second patch adds a command line argument to change the number of windows. It also allows one to specify the X window display so that it can be run on remote machines. These arguments would obviously be limited to X windows. Luckily Angband was specifically designed to allow extra arguments for the various platforms. All commands line arguments are interpreted by the default Angband handler until a '--' is encountered. All remaining arguments are passed to the platform specific handler.
Unfortunately the programmer misunderstood how this system functioned. When the two dashes aren't found, no command line options are sent to the platform specific handler. He removed this feature. So a command line of 'angband -n1' would be interpreted once by the standard handler and once by the platform handler. This might not be too bad except that the new X windows arguments use the same letters as regular Angband options. Here is the updated patch.
Game content requires many changes during development. Binary data files are the fastest to load but require a separate editor or conversion utility. Often the conversion utility converts a simple text file to the binary data. Angband follows this convention but the utility isn't separate from the game. A comment in the source code remarks on this fact, "The code could actually be removed and placed into a "stand-alone" program, but that feels a little silly, especially considering some of the platforms that we currently support." This is somewhat surprising to me since the 286 doesn't have a flat memory model and requires the use of overlays.
The binary data files are raw dumps of the game data structures. This is generally considered a bad idea for a couple of reasons. Many systems have alignment requirements for some types such as shorts and integers. To get the proper alignment the compiler may add extra bytes or worse yet the structure might be rearranged. With C++, virtual function pointers are stored in the class which makes the raw data reads and writes more trouble than they're worth. In practice no compiler to my knowledge rearranges structures. As for alignment, the structures have been carefully padded with extra bytes to prevent this. So the raw data files might be portable in this case.
Platform dependent code is localized to a single file. For example all Windows code is kept in main-win.c. A template, main-xxx.c, is included for those attempting to port to new architectures. Although a makefile is included to compile with svgalib, the accompanying main-lsl.c is not included. In fact the makefile has an error and thinks the program depends on main-ami.c, the Amiga code.
I've played an older version under svgalib. Porting to the latest version seemed like a reasonable project; the platform interface has changed only slightly since the svgalib version. The main improvement was to easily allow for tile graphics instead of the ASCII characters. Or at least that's how it appeared.
The first two hurdles didn't actually have much to do with Angband. The last svgalib patch for Angband had a corrupted bitmap file. Easily solved by downloading an older version but time consuming to discover. Secondly the console font files are compressed with gzip on RedHat 6.0. As a quick fix I copied and ungziped the file. For a permanent fix, I looked in zlib. Zlib has some simple functions similar to the standard io functions that can read and write gzip files. In case someone wants to run on an older Linux system I put in a check for the uncompressed file. Add a bug fix or two and Angband starts up with ASCII graphics.
Although better than a crash, the images should have been used.
This is when I learned the assumptions I'd made were wrong. Version 2.7.x
of Angband would set the high bit on the color attribute for the dungeon
view area of the screen. This allowed the svgalib patch to display a
tile based on the ASCII character and color. In the 2.8 series an
optional function was added to the platform dependent code called
term_pict_xxx. The main use of this function is to support tile based
higher_pict is set to true, this function will be called
instead of the standard function to display characters with the high bit
set on both the character and color.
Notice that the high bit on both the character and color need to be set. At this point I stopped and really took a good look at how the other systems handled this. The old svgalib would add the character and color times 128 to get the tile number. Divide this by the number of columns in the bitmap and you get the row number. Any remainder points to the column position. The latest Dos version uses the color attribute and ASCII character as the column and row positions respectively. The Dos bitmap contains 32 rows. There are only 16 colors so the mapping isn't as straight forwards as I'd thought.
The graphic preferences files in the lib/user sub directory determine the color and character values. Some of the unique creatures use the same letters and colors. Because of the limited selection that is the only option. The old svgalib method would require those creatures to use the same image. Instead the graphic preference files specify the color attribute and character by the monster, item, and terrain numbers. Since each monster has a unique number, you can create custom images for each unique.
At this point there are two choices: create a new preference file that keeps the color and character values the same but with the high bit set or create a new bitmap. Since there are over five hundred monsters the first option would take a lot of time. Creating a new bitmap requires a similar effort except that a bitmap already exists for Dos. The image was enlarged from the original 8x8 tiles to the 8x13 tiles used for svgalib. Some touching up is needed to remove the stretched look but that will have to wait.
The new graphics load perfectly and the game is almost playable now. Why only almost playable? The cursor keys don't function. Playing with the rogue-like key command would work but the standard Angband controls use the arrow keys for movement.
Keyboard controls for Angband are interesting. Most portable game
libraries translate key codes to some platform independent key codes.
Instead Angband makes use of macros. The macros are defined in the
files in lib/user. For example X windows translates all unusual keys into
a combination of control underscore, the modifier keys, and the key symbol.
The pref-x11.prf translates these macros into the appropriate key.
vga_getch return the same values as that the
Unix/Curses interface uses. After loading pref-gcu.prf, the Unix/Curses
general preference file, the character successfully navigated the town
level. Here's a complete svgalib patch.
In order to learn more about Angband, I decided to implement a new item. The arquebus is an early form of the musket. It had a tendency to be as dangerous to the user as the opponent. In Advanced Dungeons and Dragons 2nd Edition, the arquebus would do 1d10 damage but rolling a ten allowed the player to roll another d10. Unfortunately a one or two on the attack roll would cause the weapon to backfire. Implementing similar rules in Angband seemed reasonable.
Items and monsters in Angband have a variety of bit fields that flag whether or not the object or creature has a particular ability. For example the mace of destruction has the slay undead ability. Since the number of flags are fixed expanding capabilities without breaking backwards compatibility is difficult. Luckily adding the gun flags didn't require this as there are a number of unused fields.
For ammunition the iron shots used by the sling seemed ideal.
Unfortunately the bows and slings use an object type as ammunition. The
iron shot and rounded pebble have the same object type to allow them both
to be used as ammunition for the sling. The easiest solution would be to
create a new ammunition type. Another possibility would be to hard code
in an iron shot test. The
item_tester_okay function will call a supplied
function pointer which could easily test for iron shots or perhaps a
gun powder flag. At present, I haven't implemented any solution and
would welcome further comments.
Looking in ammunition revealed another flaw in my plan. Projectile launchers have damage multipliers while the damage dice are determined by the ammunition. The iron shot causes 1d3 damage. Good for a sling with a x2 multiplier but a d3 has a high chance of rolling a 3 meaning reroll for the arquebus. I thought of possibly making the damage multiplier be a die type multiplier. So a sling would roll 1d6 damage with an iron shot instead of 1d3 * 2. As a final solution I decided on making the gun flag mean that the launcher's damage dice are used instead of the ammunition's.
test_hit_fire function to cause the weapon to backfire seemed more
trouble than it would be worth. The fire command calculates damage
before determining if the missile hit. Since I have to check if the
weapon is a gun type to determine damage, backfire is handled as well.
Note that after decreasing the item count of the bow inventory slot you
need to call
inven_item_optimize. Otherwise when you view your equipment
you will have "no more Arquebus." Destroying the arquebus is probably a
bit extreme. It is unlikely anyone will spend the time enchanting the
arquebus since it may simply blow up.
In addition to the arquebus, a flint lock pistol could be added. If a similar feature were added to thrown weapons, you could add dynamite or grenades. Improvements or comments on the patch are always welcome.
Back to OGS